Проект ЦИТадель
Прикладному разработчику обычно не требуется реализовывать криптографические алгоритмы, но одного вызова библиотечной функции тоже недостаточно. Сначала нужно определить требуемое свойство и модель угроз, затем выбрать стандартный протокол или высокоуровневую конструкцию и соблюсти правила её применения: правильно обращаться с ключами и одноразовыми значениями, проверять ошибки, хранить версию формата и планировать замену алгоритма. Криптография для разработчика — это выбор готового средства вместе с безопасным способом его применения.
«С полки» здесь означает не первый найденный пакет и не низкоуровневый примитив, а реализацию опубликованной конструкции или протокола в сопровождаемой библиотеке с понятным интерфейсом и безопасными настройками по умолчанию. Раздел 1 объясняет границу между использованием и изобретением. Раздел 2 сопоставляет задачи и свойства. Разделы 3–6 разбирают хэши и MAC, симметричное шифрование, установление ключей и подписи. Раздел 7 посвящён жизненному циклу ключей, раздел 8 — постквантовой миграции, раздел 9 — практикуму на учебных данных.
Основное правило: не проектируйте собственный алгоритм или протокол и не реализуйте криптографический примитив. Если задачу уже решает TLS, формат подписи, высокоуровневое шифрование сообщений или штатный API хранения паролей во фреймворке, следует использовать этот уровень. При необходимости более низкого уровня выбирают стандартизованную конструкцию в распространённой библиотеке и точно соблюдают её документацию.
Причина не в том, что криптографический код нельзя тестировать. Для него существуют тестовые векторы, анализ побочных каналов, аудит и формальные методы. Проблема в другом: функциональные тесты подтверждают ожидаемое поведение и совместимость, но не доказывают стойкость. Схема может успешно шифровать и расшифровывать, одновременно повторяя одноразовое значение, принимая подменённый шифртекст или оставляя ключ в памяти и журналах.
Один из примеров — сравнение тега MAC или секретного токена. Сравнение с досрочным выходом может связать время работы с совпавшим префиксом; возможность практической атаки зависит от шума и окружения, но писать и проверять такую реализацию самостоятельно незачем. Библиотеки дают функции вроде hmac.compare_digest, разработанные без зависимого от содержимого досрочного выхода. При этом длина, тип данных и остальная ветвящаяся логика всё ещё могут раскрывать информацию.
Готовый алгоритм также не исправит ошибочный протокол вокруг него. Поэтому порядок выбора идёт сверху вниз: стандартный протокол, высокоуровневый формат или API, стандартная конструкция и лишь затем отдельный примитив. Выбранную библиотеку нужно обновлять, а формат данных — версионировать, чтобы алгоритм можно было заменить без потери старых данных.
Криптографические средства дают разные свойства, и название алгоритма ничего не говорит о том, подходит ли он к поставленной задаче.
Конфиденциальность и целостность — разные свойства. Некоторые режимы позволяют предсказуемо изменить открытый текст, не зная ключа, поэтому для прикладных сообщений используют аутентифицированное шифрование (раздел 4), которое проверяет шифртекст до выдачи результата. Но даже правильно выбранная криптография не решает, кому разрешена операция, безопасна ли конечная точка и можно ли доверять ключу, которым создана подпись: эти вопросы остаются частью модели угроз.
Криптографическая хэш-функция детерминированно отображает данные произвольной длины в дайджест фиксированной длины. От стойкой функции ожидают стойкости к поиску прообраза, второго прообраза и коллизий. Речь идёт о вычислительной трудности, а не о невозможности: если вход берётся из маленького словаря, его можно перебрать.
Хэш применяют для адресации содержимого, внутри подписей и для сверки файла с дайджестом, полученным по доверенному каналу. При стойкой функции совпадение доверенного дайджеста с вычисленным служит проверкой того, что данные не изменились. Дайджест нужно защитить от подмены.
hash(key || message) могут иметь свойства, которых разработчик не учитывает. Используют определённый стандартом MAC, например HMAC с разрешённой хэш-функцией, и сравнивают теги функцией без досрочного выхода. MAC не скрывает сообщение и подтверждает лишь то, что тег создал кто-то из обладателей общего ключа.Симметричное шифрование использует общий секретный ключ и эффективно обрабатывает прикладные данные. Для нового формата сообщений обычно выбирают готовую схему AEAD — аутентифицированное шифрование с ассоциированными данными, например AES-GCM или ChaCha20-Poly1305, — либо протокол более высокого уровня, который уже задаёт схему и формат.
ECB раскрывает равенство одинаковых блоков и не применяется для защиты сообщений. Режимы без аутентификации допустимы лишь там, где их требует уже спроектированный и проанализированный формат. Для дисков, больших потоков и архивов нужны отдельные схемы с разбиением на записи, порядком блоков и восстановлением после обрыва: единичный вызов AEAD не заменяет такой протокол.
Асимметричная криптография использует пару связанных ключей — открытый и закрытый, но дальнейшая операция зависит от конкретной схемы. При согласовании ключа, например на основе X25519, стороны объединяют собственный закрытый материал с открытым материалом собеседника. В механизме инкапсуляции, например ML-KEM, отправитель по открытому ключу получает шифртекст и общий секрет, а обладатель закрытого ключа восстанавливает тот же секрет. Цифровая подпись использует другую схему. Объяснение «зашифровать одним ключом и расшифровать другим» описывает упрощённую картину RSA и не является общей моделью асимметричной криптографии.
Открытый ключ не обязан быть секретным, но нужно доказать, чей он. Неаутентифицированное согласование ключа защищает от пассивного наблюдения, но допускает человека посередине, который установит отдельный секрет с каждой стороной. Привязку ключа к имени или участнику обеспечивают сертификаты, заранее известный ключ, доверенный каталог либо другой механизм протокола.
Прикладные системы работают гибридно: асимметричная часть устанавливает или передаёт небольшой общий секрет, KDF получает из него раздельные сеансовые ключи, а основной поток защищает AEAD. Так устроено типичное соединение TLS 1.3 с сертификатом: эфемерное установление секрета и подпись рукопожатия решают разные задачи. Самостоятельно применять низкоуровневое асимметричное шифрование к произвольным данным обычно не требуется; следует выбрать готовый протокол, который определяет аутентификацию, формат, обработку ошибок и обновление алгоритмов.
Схема цифровой подписи создаёт пару ключей с разными полномочиями: закрытый ключ подписывает, открытый проверяет. Это не шифрование «в обратную сторону»; многие алгоритмы подписи вообще не имеют операции шифрования, а ключи разных назначений не следует переиспользовать. Успешная проверка показывает, что подпись для точной последовательности байтов мог создать обладатель закрытого ключа. Поэтому формат подписываемых данных и разделение контекстов должны быть однозначными.
Подпись сама по себе не называет владельца ключа и не доказывает его намерение. Для аутентичности нужен доверенный способ связать открытый ключ с субъектом и проверить срок, назначение и статус ключа. Безусловной неотрекаемости подпись тоже не обеспечивает: ключ мог быть украден, разделён между операторами или использован автоматикой. Юридически или организационно значимое доказательство требует правил выпуска и хранения ключа, журналов, времени подписи, отзыва и процедуры разрешения спора.
Разница с MAC состоит в распределении полномочий. Каждый обладатель общего ключа MAC может и проверять, и создавать допустимые теги, поэтому третьей стороне нельзя доказать, кто именно из них создал сообщение. Обладатель открытого ключа подписи может проверять, но не подписывать. MAC удобен при небольшом числе участников с безопасно распределённым общим секретом; подпись — когда проверяющих много или им нельзя давать право создавать сообщения. На подписях строятся сертификаты и проверка выпусков ПО, но подпись подтверждает происхождение и неизменность артефакта, а не его безопасность.
Стандартный алгоритм не обеспечивает защиту без управления ключевым материалом. Для каждого ключа должны быть определены назначение, владелец, место хранения, полномочия, срок использования, способ замены и действия при компрометации.
secrets, а не генератор для моделирования. Одноразовое значение шифрования не всегда обязано быть случайным или секретным: для многих AEAD важна уникальность при данном ключе. Состояние счётчика нельзя терять при перезапуске или восстановлении снимка, а параллельным копиям нужны непересекающиеся диапазоны или другая схема обеспечения уникальности.Таким образом, выбор AEAD или схемы подписи — только начало. Эксплуатационная безопасность определяется тем, можно ли ограничить доступ к ключу, пережить его замену, восстановиться после сбоя и установить масштаб компрометации.
Криптографически значимого квантового компьютера пока нет, и срок его появления неизвестен. Однако достаточно мощная машина с алгоритмом Шора сможет нарушить стойкость широко применяемых RSA, Диффи — Хеллмана и схем на эллиптических кривых. Это не означает «слом всей асимметричной криптографии»: постквантовые схемы тоже асимметричны, но основаны на других задачах. Симметричные шифры и хэш-функции не подвержены той же атаке; их параметры выбирают по актуальному профилю, а не по универсальному правилу «удвоить длину».
В 2024 году NIST утвердил первые стандарты: ML-KEM для установления ключей, ML-DSA и SLH-DSA для подписей. Переход уже относится к эксплуатации, а не к отдалённой теории. Его начинают с криптографической описи: где находятся уязвимые для квантовой атаки алгоритмы, как долго должны оставаться секретными данные, кто обновляет библиотеку, протокол, сертификаты и устройства. Долгоживущие секреты требуют раннего внимания из-за сценария «собрать сейчас — расшифровать потом».
Разработчику следует включать поддержанные поставщиком постквантовые или гибридные режимы в стандартизованных протоколах и проверять совместимость, а не добавлять новый примитив в собственный формат. Статья о TLS разбирает гибрид X25519MLKEM768, а квантовый курс — алгоритмы и план миграции. Важнейшее архитектурное свойство здесь — криптографическая гибкость: возможность заменить алгоритм и ключ без полной переделки системы.
Все задания выполняйте локально, на одноразовых ключах и вымышленных данных. Не проверяйте временные атаки на чужом сервисе и не сохраняйте результаты заведомо небезопасных режимов. Для Python понадобятся стандартные модули hashlib, hmac, random, secrets и сопровождаемые пакеты с AEAD, Ed25519 и Argon2id, например cryptography и argon2-cffi.
Задание 1. Функциональный тест и время. Напишите только для лаборатории уязвимое сравнение двух байтовых строк: проходите их слева направо, завершайте работу при несовпадении и после каждого совпавшего байта добавляйте небольшую задержку. Для догадок с префиксами разной длины соберите по нескольку десятков измерений и сравните медианы. Затем замените функцию на hmac.compare_digest. Объясните, почему обычный тест на равенство проходил в обоих вариантах, а чистого временного сигнала от встроенного оператора == конкретная версия среды показывать не обязана.
Задание 2. ECB и повторяющиеся блоки. В изолированном примере зашифруйте строку из восьми одинаковых 16-байтовых блоков с AES-ECB. Разбейте шифртекст на блоки и посчитайте одинаковые. Обработайте те же данные высокоуровневым AES-GCM с новым одноразовым значением и повторите подсчёт. Наблюдение демонстрирует утечку структуры ECB, но случайный вид второго шифртекста сам по себе не служит доказательством стойкости.
Задание 3. Подмена и AEAD. В учебном скрипте зашифруйте сообщение AES-CTR, измените один бит шифртекста и сравните открытые тексты после расшифрования. Затем используйте AES-GCM или ChaCha20-Poly1305: передайте версию учебного формата, алгоритм и идентификатор ключа как AAD и по очереди измените шифртекст, AAD и одноразовое значение. Во всех случаях AEAD должен вернуть ошибку проверки, а программа — не использовать частичный открытый текст. Выпишите поля получившегося конверта: версия, алгоритм, идентификатор ключа, одноразовое значение, шифртекст и тег; секретный ключ в конверт не входит.
Задание 4. Хэш, пароль и MAC. Для тестового файла вычислите SHA-256 и проверьте его по заранее сохранённому доверенному значению. Тестовый пароль обработайте Argon2id дважды с автоматически созданными уникальными солями и проверьте оба результата штатной функцией. Для сообщения создайте HMAC-SHA-256 со случайным ключом и проверьте его через compare_digest. Для каждого результата укажите, кто способен его пересчитать или подделать и какая часть данных должна быть секретной.
Задание 5. Источник случайности. Создайте два экземпляра random.Random с одинаковым известным зерном и убедитесь, что последовательности совпадают. Отдельно получите токены через secrets. По внешнему виду коротких выборок нельзя определить их криптографическую стойкость: вывод делают из назначения API и модели генератора. Проведите поиск вызовов обычного генератора в учебном проекте и разделите их на моделирование, где он уместен, и ключи, токены или пароли, где требуется криптографический источник.
Задание 6. Подпись против MAC. Создайте учебную пару Ed25519, подпишите сообщение и проверьте открытым ключом. Затем создайте HMAC того же сообщения. Передайте воображаемой третьей стороне сначала открытый ключ, затем ключ HMAC: в первом случае она может проверять, но не подписывать, во втором получает возможность создавать любые допустимые теги. Сформулируйте критерий выбора через распределение полномочий, а не через скорость или принадлежность сервисов одной организации.
Задание 7. Жизненный цикл ключа. Для одного учебного случая заполните карточку: назначение и алгоритм, владелец, идентификатор, создание, хранилище, получатели доступа, одноразовые значения, криптопериод и предел данных, ротация, резервирование, если оно требуется, действия при компрометации и судьба старых данных. Не записывайте в карточку значение ключа. Проверьте сценарии: новый ключ уже используется для записи, старый ещё читает архив; активный ключ отозван; хранилище временно недоступно.
Задание 8*. Криптографическая опись. Для небольшого сервиса перечислите TLS, сертификаты, подписи токенов и выпусков, шифрование базы и резервных копий, SSH и встроенную криптографию зависимостей. Рядом укажите алгоритм, библиотеку, владельца обновления и требуемый срок конфиденциальности или проверки подписи. Отметьте применения RSA, Диффи — Хеллмана и эллиптических кривых, но не меняйте их в рабочей системе: результат задания — приоритетный план миграции и проверок совместимости.