Вступление

Статья частично является продолжениям идеи создания своего алгоритма шифрования на основе хеш функций из первой части. И так, продолжим. Большинство практических схем шифрования строятся вокруг проверенных стандартов вроде AES или RSA. В этом материале мы разберем альтернативный, исследовательский подход: шифрование, где для каждого байта сообщения подбирается собственное значение salt на основе SHA-256. Главная особенность алгоритма в том, что для шифрования используется хеш функция. Цель статьи — интуитивно и пошагово объяснить механику новой версии алгоритма, чтобы вы могли оценить его сильные и слабые стороны.

Идея алгоритма

Алгоритм обрабатывает сообщение как последовательность байтов. Для каждого байта используется текущее состояние ключа. Из ключа вычисляется хэш, а его первый байт играет роль маски XOR. Дальше происходит ключевой шаг, алгоритм подбирает число salt. Перебор продолжается, пока первый байт полученного хэша не совпадет с целевым значением, которое было рассчитано из исходного байта и маски XOR. Найденный salt и сохраняется в шифротекст. После этого ключ обновляется (снова через SHA-256), и следующий байт обрабатывается уже с новым ключом. Каждый шаг зависит от предыдущего состояния.

Шифрования

Шаг 1. Из секретной фразы получаем стартовый ключ

Берем любую секретную фразу произвольной длины (лучше конечно не меньше 16 символов) и хэшируем ее через SHA-256. Результат всегда 32 байта — это и есть стартовый ключ алгоритма.

import hashlib

secret_phrase = "my very secret passphrase"
secret_seed = secret_phrase.encode("utf-8")
initial_key = hashlib.sha256(secret_seed).digest()

Шаг 2. Готовим данные для шифрования

Алгоритм работает с байтами, поэтому сообщение тоже переводим в bytes.

message = "Hello!"
message_bytes = message.encode("utf-8")

Шаг 3. Шифруем один байт

Для каждого байта:

  • берем маску из текущего ключа (первый байт SHA-256 от ключа);

  • считаем целевое значение через XOR;

  • подбираем salt перебором, пока первый байт SHA-256 от key + salt не совпадет с целью, другими словами, не будет равно xor_target (single_byte ^ mask_byte).

def encrypt_byte(single_byte: int, current_key: bytes) -> tuple[int, bytes]:
    mask = hashlib.sha256(current_key).digest()
    mask_byte = mask[0]

    xor_target = single_byte ^ mask_byte

    salt = 0
    while True:
        salt_bytes = salt.to_bytes(4, byteorder="big")
        attempt_hash = hashlib.sha256(current_key + salt_bytes).digest()
        if attempt_hash[0] == xor_target:
            break
        salt += 1

    next_key = hashlib.sha256(current_key).digest()
    return salt, next_key

Шаг 4. Шифруем все сообщение

Повторяем шаг для каждого байта. В результате получаем массив salt — это и есть шифротекст.

salts = []
current_key = initial_key

for b in message_bytes:
    salt, current_key = encrypt_byte(b, current_key)
    salts.append(salt)

print("Ciphertext (salts):", salts)

Расшифрования

Шаг 1. Расшифровываем один байт

На расшифровке перебора уже нет. Мы знаем salt, поэтому сразу восстанавливаем байт.

def decrypt_byte(salt: int, current_key: bytes) -> tuple[int, bytes]:
    mask = hashlib.sha256(current_key).digest()
    mask_byte = mask[0]

    salt_bytes = salt.to_bytes(4, byteorder="big")
    attempt_hash = hashlib.sha256(current_key + salt_bytes).digest()
    xor_target = attempt_hash[0]

    single_byte = xor_target ^ mask_byte
    next_key = hashlib.sha256(current_key).digest()
    return single_byte, next_key

Шаг 2. Расшифровываем все сообщение

Идем по всему массиву salts, восстанавливаем байты, собираем обратно строку.

decrypted_bytes = bytearray()
current_key = initial_key

for salt in salts:
    single_byte, current_key = decrypt_byte(salt, current_key)
    decrypted_bytes.append(single_byte)

decrypted_message = decrypted_bytes.decode("utf-8")
print("Decrypted:", decrypted_message)

Итоги

Этот алгоритм — интересный пример того, как можно построить обратимое преобразование вокруг SHA-256 или другой хеш функции и использовать это для шифрования данных. Алгоритм конечно можно модифицировать и улучшать, здесь показана только идея и принцип работы. Полный код можно найти на GitHub. Спасибо за внимание и комментарии.