Двойное дно: реализуем свой формат шифрования .CHA на Python и прячем его в стеганографии

от автора

Привет, Хабр! Когда речь заходит о защите конфиденциального файла, на ум приходят два пути: шифрование и стеганография. Первый делает файл нечитаемым для посторонних. Второй — делает сам факт существования файла незаметным. А что, если объединить эти два подхода, создав по-настояшему надежное «двойное дно» для ваших данных?

В этой статье мы не просто обсудим теорию, а пошагово, с подробным разбором кода, создадим собственный простой и надежный формат шифрования .cha (сокращение от Chameleon) на Python. А затем покажем, как именно работает механизм LSB-встраивания в картинку и как наша программа «ChameleonLab» автоматически находит эти скрытые данные.

Часть 1: Пошаговая реализация .CHA на Python

Цель — создать утилиту, которая:

  • Надежно шифрует файл с помощью пароля.

  • Сохраняет оригинальное имя файла внутри зашифрованного контейнера.

  • Проверяет целостность данных при расшифровке.

Для этого мы будем использовать отличную библиотеку cryptography.

Шаг 1: Генерация ключа из пароля (KDF)

Никогда не используйте пароль напрямую в качестве ключа шифрования! Его нужно «растянуть» до криптографически стойкого ключа с помощью функции деривации ключей (KDF). Мы используем PBKDF2 со 100 000 итераций.

# Из crypto_utils.py from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC import base64 import os  def generate_key_from_password(password: str, salt: bytes) -> bytes:     """     Генерирует 32-байтный ключ из пароля и соли с помощью PBKDF2-HMAC-SHA256.     """     kdf = PBKDF2HMAC(         algorithm=hashes.SHA256(),         length=32,  # Длина ключа в байтах (256 бит для AES)         salt=salt,         iterations=100000, # Рекомендованное количество итераций     )     # Кодируем ключ в URL-safe Base64 для совместимости с Fernet     return base64.urlsafe_b64encode(kdf.derive(password.encode())) 

Шаг 2: Упаковка и шифрование файла

Структура нашего .cha файла: [Магическое число] + [Соль] + [Зашифрованные данные].

  • Магическое число (b'CHAMELEON'): Уникальная сигнатура для опознания формата.

  • Соль: 16 случайных байт для PBKDF2.

  • Зашифрованные данные: Результат шифрования по стандарту Fernet (AES-128-CBC + HMAC-SHA256), который гарантирует конфиденциальность и целостность данных.

# Из crypto_utils.py from cryptography.fernet import Fernet from pathlib import Path  MAGIC_NUMBER = b'CHAMELEON'  def encrypt_cha_file(input_path: str, output_path: str, password: str):     """Шифрует файл в формат .cha, сохраняя имя файла."""     input_path = Path(input_path)     with open(input_path, 'rb') as f:         plaintext = f.read()      # Упаковываем имя файла и его содержимое через разделитель '|'     packaged_data = input_path.name.encode('utf-8') + b'|' + plaintext     salt = os.urandom(16)     key = generate_key_from_password(password, salt)     f = Fernet(key)     encrypted_data = f.encrypt(packaged_data)      with open(output_path, 'wb') as f_out:         f_out.write(MAGIC_NUMBER)         f_out.write(salt)         f_out.write(encrypted_data) 

Эта функция реализована в UI на вкладке «Шифрование файлов».

Шаг 3: Расшифровка и проверка

Процесс расшифровки обратный, но с ключевым моментом: fernet.decrypt() автоматически проверит HMAC-подпись. Если данные повреждены или пароль неверен, функция выбросит исключение InvalidToken, защищая от работы с некорректными данными.

# Из crypto_utils.py def decrypt_cha_file(input_path: str, output_path: str, password: str) -> str:     """Расшифровывает .cha файл, возвращает оригинальное имя файла."""     with open(input_path, 'rb') as f:         if f.read(len(MAGIC_NUMBER)) != MAGIC_NUMBER:             raise ValueError("Это не файл формата .cha или он поврежден.")         salt = f.read(16)         encrypted_data = f.read()      key = generate_key_from_password(password, salt)     f = Fernet(key)     decrypted_packaged_data = f.decrypt(encrypted_data)      filename_bytes, file_content = decrypted_packaged_data.split(b'|', 1)     original_filename = filename_bytes.decode('utf-8')      with open(output_path, 'wb') as f_out:         f_out.write(file_content)     return original_filename 

Часть 2: Прячем зашифрованный файл в картинке

Итак, у нас есть надежно зашифрованный файл. Теперь задача — сделать его невидимым. Как именно происходит встраивание в картинку и, что еще важнее, как потом найти эти данные автоматически?

Шаг 2a: Создание «Стего-Пакета»

Чтобы утилита могла автоматически находить данные, мы «заворачиваем» наш .cha файл в еще один контейнер со служебным заголовком. Структура этого стего-пакета:

[Длина данных (4б)] + [Число бит (1б)] + [СИГНАТУРА (4б)] + [Флаг шифрования (1б)] + [Сам файл]

Ключевой элемент здесь — СИГНАТУРА (b'S6KB'). Это уникальный «маячок», который наша программа ищет в файлах для автоматического обнаружения.

Шаг 2b: Алгоритм встраивания (hide)

Функция hide берет этот пакет и последовательно записывает его биты в младшие биты (LSB) пикселей изображения.

# steganography_core.py def hide(carrier_bytes: np.ndarray, final_payload: bytes, n_bits: int, is_encrypted: bool) -> np.ndarray:     encryption_flag = b'\x01' if is_encrypted else b'\x00'     data_to_hide = MAGIC_NUMBER + encryption_flag + final_payload     metadata_len = len(data_to_hide).to_bytes(4, 'big')     n_bits_val = int(n_bits).to_bytes(1, 'big')     full_data = metadata_len + n_bits_val + data_to_hide     full_bits = data_to_binary(full_data)      carrier_flat = carrier_bytes.flatten().copy().astype(np.uint8)     mask = ~((1 << n_bits) - 1) & 0xFF     carrier_flat &= mask      bit_index = 0     for i in range(math.ceil(len(full_bits) / n_bits)):         chunk = full_bits[bit_index : bit_index + n_bits].ljust(n_bits, '0')         carrier_flat[i] |= int(chunk, 2)         bit_index += n_bits      return carrier_flat.reshape(carrier_bytes.shape) 

Шаг 2c: Автоматическое извлечение (reveal)

Программа для извлечения не знает, сколько LSB было использовано, поэтому она перебирает все варианты от 1 до 8. Для каждого варианта она пытается прочитать заголовок и найти сигнатуру.

# steganography_core.py def reveal(carrier_bytes: np.ndarray) -> Tuple[bytes, bool, bool]:     flat = carrier_bytes.flatten().astype(np.uint8)     METADATA_BITS = 40      for candidate_n in range(1, 9):         bytes_for_metadata = math.ceil(METADATA_BITS / candidate_n)         if flat.size < bytes_for_metadata: continue         bitstream = _get_bitstream(flat, candidate_n, bytes_for_metadata)         if len(bitstream) < METADATA_BITS: continue          data_len = int(bitstream[:32], 2)         declared_n = int(bitstream[32:40], 2)          if declared_n != candidate_n: continue          total_bytes_to_read = math.ceil((5 + data_len) * 8 / declared_n)         full_bitstream = _get_bitstream(flat, declared_n, total_bytes_to_read)         full_bytes = binary_to_data(full_bitstream)         data_to_hide = full_bytes[5 : 5 + data_len]                  if data_to_hide.startswith(MAGIC_NUMBER):             header_len = len(MAGIC_NUMBER)             is_encrypted = (data_to_hide[header_len] == 1)             packaged_data = data_to_hide[header_len + 1:]             return packaged_data, is_encrypted, True      return b'', False, False 

Часть 3: Синергия: Шифрование + Стеганография = ❤️

Использование связки .cha и стеганографии дает два независимых уровня защиты:

  • Уровень 1: Шифрование (Конфиденциальность и целостность): Вы берете ваш my_plan.docx и превращаете его в my_plan.docx.cha. Даже если злоумышленник его найдет, он не сможет его прочитать без пароля или незаметно изменить.

    Программа "ChameleonLab". Шифрование

    Программа «ChameleonLab». Шифрование
  • Уровень 2: Стеганография (Скрытность и правдоподобное отрицание): Вы берете полученный my_plan.docx.cha и встраиваете его в фотографию kitten.png. Теперь у постороннего наблюдателя нет даже оснований подозревать, что какой-то секретный файл вообще существует. Файл не просто защищен — он невидим.

Программа "ChameleonLab". Встраивание

Программа «ChameleonLab». Встраивание

Эта двухступенчатая защита — и есть философия ChameleonLab. Сначала мы делаем данные нечитаемыми, а затем делаем их невидимыми. Это обеспечивает правдоподобное отрицание: вы всегда можете утверждать, что это просто картинка, и доказать обратное будет практически невозможно.

Последнюю версию программы «Steganographia» от ChameleonLab для Windows и macOS можно скачать на нашем официальном сайте. https://chalab.ru

А чтобы быть в курсе обновлений, обсуждать новые функции и общаться с единомышленниками, присоединяйтесь к нашему Telegram-каналу: https://t.me/ChameleonLab

Спасибо за ваше внимание и интерес к проекту!


ссылка на оригинал статьи https://habr.com/ru/articles/943402/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *