- PVSM.RU - https://www.pvsm.ru -
Всё началось с простой задачи: нужно было безопасно передавать файлы на обычных USB-флешках. Существующие решения либо создавали контейнеры (VeraCrypt), что неудобно для быстрого доступа к отдельным файлам на разных ОС, либо работали слишком сложно для конечного пользователя.
Мне нужно было решение уровня «вставил флешку -> ввел пароль -> файлы зашифрованы». Но главное требование — безопасность данных даже при сбое питания. Если выдернуть флешку посередине шифрования, данные не должны превратиться в кашу.
Так появился crypto_engine. Это не попытка изобрести свою криптографию (мы используем стандартные AES-GCM и ChaCha20), а инженерная работа над тем, как безопасно управлять ключами в памяти, обрабатывать гигабайтные файлы без переполнения RAM и гарантировать целостность данных.
Самая большая уязвимость криптографических утилит на управляемых языках (Python, Java) — это работа с памятью. Когда вы храните пароль или ключ в переменной bytes, сборщик мусора (GC) может скопировать эти данные в другое место памяти при сборке мусора, оставив исходные копии «висеть» в RAM до неопределенного времени.
В моем движке я реализовал класс SecureBytes, который решает эту проблему:
class SecureBytes:
def __init__(self, data: Union[bytes, bytearray, int]):
if isinstance(data, int):
self._buffer = bytearray(data)
else:
self._buffer = bytearray(data)
self._finalized = False
# Регистрируем слабый финализатор
self._weak_ref = weakref.ref(self, self._cleanup_callback)
def wipe(self, passes: int = 3):
if self._finalized or len(self._buffer) == 0:
return
# Проход 1: случайные данные
self._buffer[:] = secrets.token_bytes(len(self._buffer))
# Проход 2: нули
self._buffer[:] = b'x00' * len(self._buffer)
self._finalized = True
gc.collect()
def __del__(self):
if not self._finalized:
self.wipe()
Что здесь важно:
Использование bytearray: В отличие от неизменяемых bytes, bytearray позволяет перезаписывать данные по тому же адресу памяти.
Многопроходная очистка: Перед освобождением памяти буфер перезаписывается случайными данными, затем нулями (согласно рекомендациям NIST SP 800-88).
Контекстный менеджер: Ключи используются только внутри блока with secure_key(...):, что гарантирует очистку даже при возникновении исключений.
Шифрование файла размером 10 ГБ на флешке с 4 ГБ оперативной памяти — нетривиальная задача. Загружать файл целиком в RAM нельзя.
Я реализовал MemorySensitiveReader, который автоматически переключается между режимами работы в зависимости от размера файла и доступной памяти:
class MemorySensitiveReader:
def __init__(self, file_path: str, memory_threshold: int = 100 * 1024 * 1024):
self.file_size = os.path.getsize(file_path)
# Порог переключения на потоковый режим
self.use_streaming = self.file_size > memory_threshold
def iter_chunks(self, chunk_size: int = 8192):
# Чтение и шифрование блоками
...
Проблема Nonce при потоковом шифровании:
В режимах AES-GCM и ChaCha20 нельзя использовать один и тот же nonce (номер однократного использования) для разных блоков с одним ключом. Это критическая уязвимость. Решение в моем коде — деривация уникального nonce для каждого блока на основе базового nonce и индекса блока:
def _derive_block_nonce_12bit(base_nonce: bytes, block_index: int) -> bytes:
# Первые 8 байт — префикс, последние 4 — счётчик блока
prefix = base_nonce[:8]
block_counter = block_index.to_bytes(4, byteorder='big')
return prefix + block_counter
Это позволяет шифровать файлы любого размера, не нарушая криптографические стандарты.
Самый страшный сценарий для пользователя — потеря данных из-за сбоя во время шифрования. Стандартный подход «зашифровать -> удалить оригинал» здесь не работает.
Я внедрил систему блокировок и отката (rollback):
Lock-файл (.encryption_lock.json): Перед началом операции создается файл, где записывается статус in_progress и список уже обработанных файлов.
Временные файлы: Шифрование идет в .tmp файл. Только после успешной проверки целостности оригинал удаляется, а временный файл переименовывается.
Проверка целостности: Перед удалением оригинала я расшифровываю блок данных обратно и сравниваю HMAC и SHA-256 хеши. Если не совпадает — оригинал не трогается.
Восстановление: Если процесс прервался, утилита видит lock-файл и предлагает откатить операцию (rollback_operation), расшифровав уже обработанные файлы обратно.
Движок поддерживает три алгоритма:
AES-256-GCM: Стандарт индустрии, аппаратное ускорение на большинстве CPU.
ChaCha20-Poly1305: Быстрее на устройствах без AES-NI (например, некоторые ARM-процессоры).
XChaCha20-Poly1305: Увеличенный nonce (24 байта), что снижает риск коллизий при очень больших объемах данных.
Для ускорения работы с множеством мелких файлов реализована параллельная обработка через ThreadPoolExecutor. Однако из-за GIL в Python прирост заметен скорее на операциях I/O, чем на чистом шифровании.
Хотя ядро написано на Python, для пользователей доступен GUI, чтобы не запускать скрипты через консоль.
Важно понимать, для чего этот инструмент подходит, а для чего — нет.
Метаданные не скрыты: Имена файлов и структура папок сохраняются в .usb_crypt_meta.json. Злоумышленник с доступом к флешке увидит список файлов, но не сможет их открыть. Скрыть имена файлов без создания контейнера технически сложно и неудобно для навигации.
Защита от физической потери: Инструмент защищает данные, если вы потеряли флешку. Он не защищает от кейлоггеров на компьютере, где вы вводите пароль.
Парольная политика: Встроенная валидация требует минимум 12 символов, цифры и спецсимволы. Слабые пароли блокируются на уровне кода.



Написание собственного крипто-движка — это всегда баланс между безопасностью и удобством. Я постарался сделать акцент на безопасности управления памятью (что редко встречается в Python-скриптах) и отказоустойчивости операций.
Проект открыт, код доступен для аудита. Если вы найдете уязвимости или способы оптимизировать SecureBytes — добро пожаловать в Issues.
Автор: slimeopus
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/448010
Ссылки в тексте:
[1] https://github.com/slimeopus/SparkLock/releases: https://github.com/slimeopus/SparkLock/releases
[2] Источник: https://habr.com/ru/articles/1016268/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1016268
Нажмите здесь для печати.