
AEAD: шифрование, которое не верит на слово
Я обожаю криптографию. И одна из вещей, от которых я в восторге, — это аутентифицированное шифрование. Звучит занудно, но на самом деле это красивая идея, которая решает проблему, о которой многие даже не задумываются.
Вот смотрите. Есть очень живучее заблуждение: «Если я зашифровал — значит, я в безопасности». Никто не прочитает — никто не узнает. А раз никто не узнает — всё хорошо.
Но «не прочитать» — это только половина дела. Вторая половина — «не подменить». И вот тут начинается самое интересное.
Часть первая. Коробка без пломбы
Секретность — это ещё не безопасность
Вы отправляете другу подарок по почте. Берёте коробку, заворачиваете, заклеиваете скотчем. Думаете: «Ну вот, снаружи ничего не видно — значит, безопасно».
Но почта — не музей. Коробки кидают, мнут, иногда вскрывают «для проверки». И вот друг получает посылку. Она закрыта. Скотч на месте. Но внутри вместо подарка — камень. Или подарок на месте, но сломан. Или вообще что-то левое.
Коробка скрывала содержимое. Но она не гарантировала, что содержимое никто не трогал.
Это и есть разница между секретностью и целостностью:
- секретность — никто не видит, что внутри;
- целостность — никто не менял то, что внутри.
Шифрование без проверки целостности — это коробка без пломбы. Закрытая, но не защищённая.
Почему «зашифровано» не значит «защищено»
Вы отправляете зашифрованное сообщение: «Переведи Маше 1000 рублей». Получатель расшифрует и выполнит.
Кто-то перехватывает сообщение по дороге. Прочитать не может — всё зашифровано. Но вот штука: при некоторых способах шифрования можно «вслепую» поменять отдельные символы. Злоумышленник не знает, что написано. Но он знает: если изменить вот этот байт — изменится соответствующая буква в тексте.
Это как тыкать иголкой в запечатанный конверт и менять буквы, не вскрывая его.
Иногда результат — мусор. А иногда — «Переведи Маше 9000 рублей». Или «Переведи Васе 1000 рублей».
Получатель честно расшифрует. Увидит осмысленный текст. И выполнит.
У этой проблемы есть название — malleability, «податливость» шифртекста. Это когда зашифрованные данные можно изменить, не зная ключа, и изменения «проявятся» после расшифровки.
Коробка закрыта. Но пломбы нет.
Часть вторая. Добавляем пломбу
MAC — криптографическая печать
Логичный следующий шаг: «Окей, давайте проверять целостность».
Для этого придумали MAC — Message Authentication Code, код аутентификации сообщения. Самый известный вариант — HMAC.
Идея простая. Берём данные, берём секретный ключ, вычисляем специальный «отпечаток». Прикладываем его к сообщению. Теперь, если кто-то изменит хоть один бит — отпечаток не сойдётся.
Это уже похоже на пломбу. Коробку можно не только закрыть, но и запечатать так, чтобы вскрытие сразу стало заметно.
Ловушка: порядок имеет значение
И тут начинаются тонкости.
Вы решили использовать шифрование вместе с MAC. Вопрос: что делать сначала?
Вариант 1: MAC-then-Encrypt. Сначала вычисляем MAC от открытого текста, потом шифруем всё вместе.
Вариант 2: Encrypt-then-MAC. Сначала шифруем, потом вычисляем MAC от шифртекста.
Вариант 3: Encrypt-and-MAC. Шифруем текст и отдельно вычисляем MAC от открытого текста.
Кажется, разница косметическая. Но нет.
Правильный ответ — Encrypt-then-MAC. Почему?
При получении сообщения нужно сначала проверить MAC, и только потом расшифровывать. Если MAC не сошёлся — данные не трогаем. Не пытаемся расшифровать «просто посмотреть». Не передаём дальше.
Дело в том, что сам процесс расшифровки может выдавать информацию. Через время выполнения. Через сообщения об ошибках. Через поведение системы. Это называется oracle attacks — атаки через оракул.
Классический пример — padding oracle attack. Если система по-разному реагирует на «неправильный MAC» и «неправильные данные после расшифровки», злоумышленник может по одному байту восстановить весь текст. Не зная ключа. Просто отправляя запросы и наблюдая за ответами.
Звучит как фокус, но это реальная атака. Она ломала реализации SSL/TLS, ASP.NET, Java Server Faces и десятки других систем.
Поэтому золотое правило: сначала проверяй, потом расшифровывай. А для этого MAC должен быть от шифртекста, а не от открытого текста.
Люди ошибаются
Даже зная правильный подход, легко споткнуться:
- забыть включить в MAC что-то важное (например, длину данных);
- перепутать ключи;
- сравнить MAC так, что время проверки зависит от количества совпавших байтов — злоумышленник замеряет задержки и подбирает правильное значение;
- неправильно обработать ошибку и всё равно попытаться что-то расшифровать.
История криптографии полна случаев, когда разработчики «вроде всё сделали правильно», но упустили мелочь — и система оказывалась дырявой.
Из этого родилась идея: а что если объединить шифрование и проверку целостности в одну неразрывную операцию? Чтобы нельзя было забыть, перепутать, сделать в неправильном порядке. Чтобы криптографы один раз собрали всё как надо, а разработчики просто пользовались готовым алгоритмом.
Часть третья. AEAD — шифрование, которое всё делает за вас
Что такое AEAD
AEAD — это Authenticated Encryption with Associated Data, аутентифицированное шифрование с присоединёнными данными.
Это не один алгоритм, а целое семейство алгоритмов, которые решают обе задачи сразу:
- Шифруют — чтобы никто не прочитал.
- Проверяют подлинность — чтобы никто не подменил.
И делают это так, что ошибиться гораздо сложнее. Не нужно думать, в каком порядке что вычислять, что куда включать, как сравнивать. Всё продумано и встроено внутрь.
Если кто-то изменит шифртекст — расшифровка не произойдёт. Вы не получите «слегка испорченный текст». Вы получите ошибку: «Подлинность не подтверждена».
Аналогия: посылка с защитой
Обычная почта — это просто коробка. Можно вскрыть, посмотреть, положить обратно, заклеить. Если делать аккуратно — никто не заметит.
AEAD — это коробка с несколькими уровнями защиты:
- непрозрачная (содержимое не видно) — это шифрование;
- с пломбой, которая рвётся при вскрытии — это проверка подлинности;
- пломба охватывает и коробку, и накладную — это ассоциированные данные (associated data, о них ниже).
Вы либо получаете груз целым и нетронутым, либо сразу видите: что-то не так.
Почему использовать AEAD лучше, чем собирать проверки самому
Сложнее накосячить. Всё собрано в одну операцию. Не нужно гадать с порядком, ключами, тем, что куда включать.
Быстрее работает. AEAD-алгоритмы часто делают шифрование и проверку за один проход — это эффективнее, чем две отдельные операции.
Проверено временем. Популярные AEAD-схемы изучены криптографами со всего мира, реализованы в надёжных библиотеках, обкатаны годами использования.
Чёткий ответ. Данные либо подлинные, либо нет. Никакой серой зоны, никаких «ну вроде похоже».
Часть четвёртая. Associated Data — неожиданно красивая идея
Данные, которые видны, но защищены
В названии AEAD есть «Associated Data» — присоединённые или ассоциированные данные. Это, пожалуй, самая изящная часть концепции.
Присоединённые данные — это информация, которая не шифруется, но защищается от подмены.
Странно звучит: зачем защищать то, что и так видно?
Вернёмся к посылке. На коробке есть наклейка с адресом. Адрес не спрятан — иначе как почта доставит посылку? Но если кто-то переклеит наклейку и напишет другой адрес — посылка уедет не туда. Содержимое в порядке. Пломба на месте. Но адрес подменён.
Пример из жизни
Мессенджер с шифрованием. Вы отправляете сообщение другу. Само сообщение зашифровано — прочитать может только друг. Но у сообщения есть «обёртка»: кому адресовано, от кого, порядковый номер.
Эту обёртку нельзя шифровать — иначе сервер не будет знать, кому доставить. Но если её можно незаметно изменить, открываются интересные возможности:
- переслать сообщение другому человеку (вы писали Маше, а получит Петя);
- поменять порядок сообщений (ответ придёт раньше вопроса);
- отправить старое сообщение повторно (вы один раз попросили перевести деньги, а команда выполнится дважды).
AEAD позволяет сказать: «Вот эти поля пусть будут открыты — но если в них изменить хоть один символ, вся расшифровка провалится».
Что обычно включают в присоединённые данные
- кому предназначено сообщение;
- от кого оно пришло;
- порядковый номер (чтобы нельзя было переставлять или повторять);
- тип сообщения («запрос», «ответ», «ошибка»);
- временну́ю метку;
- идентификатор сессии или соединения;
- версию протокола.
Общее правило: в присоединённые данные нужно включать всё, что влияет на смысл сообщения. Всё, что при изменении могло бы изменить его назначение.
Часть пятая. Nonce — маленькая деталь, от которой всё зависит
Зачем нужен nonce
Большинство AEAD-алгоритмов требуют при шифровании указывать nonce (от «number used once» — число, используемое однократно). Иногда его называют IV (initialization vector).
Зачем? Потому что без nonce одинаковые сообщения дадут одинаковый шифртекст. А это плохо.
Электронное голосование. Избиратели отправляют зашифрованные голоса. Если два человека проголосовали одинаково — их шифртексты тоже одинаковые. Прочитать нельзя, но видно, что голоса совпадают. Это утечка.
Nonce добавляет уникальность. Даже если сообщения одинаковые, шифртексты будут разными.
Главное правило: не повторять
У nonce одно железное правило: нельзя использовать один и тот же nonce дважды с одним и тем же ключом.
Повтор nonce — катастрофа. Последствия зависят от алгоритма, но всегда плохие.
Для популярного AES-GCM повтор nonce позволяет:
- узнать, чем отличаются два сообщения, даже не зная их содержимого (а если угадать одно — расшифровать второе);
- подделать аутентификационный тег для произвольных сообщений;
- полностью сломать защиту.
Это не теоретическая страшилка. Это реальная уязвимость, которую умеют эксплуатировать.
Как генерировать nonce
Два основных способа:
Счётчик. Увеличиваете число на единицу при каждом шифровании. Гарантированно не повторится (пока не переполнится). Но нужно надёжно хранить состояние — где остановились.
Случайное число. Генерируете криптографически случайное значение. Не нужно ничего хранить. Но при очень большом количестве сообщений есть шанс случайного совпадения (парадокс дней рождения).
Для стандартного AES-GCM nonce — 96 бит. После примерно 2³² (~4 миллиардов) сообщений вероятность коллизии становится ощутимой. Для большинства применений это много, но для высоконагруженных систем — не так уж.
Алгоритмы, которые прощают ошибки
Криптографы понимают, что люди ошибаются. Поэтому разработаны алгоритмы, более терпимые к проблемам с nonce:
AES-GCM-SIV. Synthetic IV — nonce выводится из самого сообщения. Если nonce повторится, единственная «утечка» — можно заметить, что два сообщения одинаковые. Но подделать ничего не получится.
XChaCha20-Poly1305. Использует 192-битный nonce. При случайной генерации шанс совпадения практически нулевой даже для астрономического количества сообщений.
Эти алгоритмы не панацея, но они прощают ошибки, которые AES-GCM не прощает.
Часть шестая. Какие AEAD-алгоритмы бывают
AES-GCM
Galois/Counter Mode — самый распространённый вариант.
Плюсы:
- аппаратное ускорение на большинстве современных процессоров (Intel AES-NI, ARM Cryptography Extensions);
- очень быстрый на железе с ускорением;
- стандартизован, используется повсюду: TLS 1.2/1.3, IPsec, SSH.
Минусы:
- катастрофически чувствителен к повтору nonce;
- на железе без ускорения заметно медленнее;
- сложная математика (поля Галуа), легко ошибиться в реализации.
Когда использовать: когда есть аппаратное ускорение и вы уверены в правильной генерации nonce.
ChaCha20-Poly1305
Альтернатива от Дэниела Бернштейна.
Плюсы:
- быстрый на любом железе, не требует специальных инструкций;
- проще устроен, меньше шансов ошибиться в реализации;
- не подвержен атакам через измерение времени (время работы не зависит от данных);
- стандартизован, используется в TLS 1.3, WireGuard, SSH.
Минусы:
- стандартный nonce — 96 бит (те же ограничения, что у GCM).
Когда использовать: на мобильных устройствах, в чисто программных реализациях, когда нет AES-ускорения.
XChaCha20-Poly1305
Расширенная версия ChaCha20-Poly1305.
Плюсы:
- всё то же, что ChaCha20-Poly1305;
- 192-битный nonce — можно безопасно генерировать случайно.
Минусы:
- не стандартизован IETF (но широко используется);
- чуть медленнее из-за дополнительного шага.
Когда использовать: когда хочется генерировать nonce случайно без страха коллизий. Отличный выбор для прикладных задач.
AES-GCM-SIV
Главная фишка — устойчивость к повтору nonce.
Напомню, что происходит при повторе nonce в обычном AES-GCM: злоумышленник может расшифровать сообщения, подделать аутентификационный тег, полностью скомпрометировать шифрование. Это катастрофа.
В AES-GCM-SIV при повторе nonce ничего такого не случится. Аутентификация останется надёжной — подделать сообщение не получится. Единственная «утечка»: если вы зашифруете два одинаковых сообщения с одинаковым nonce, шифртексты тоже совпадут. Наблюдатель увидит: «эти два сообщения одинаковые». Но что именно в них — не узнает. И подделать ничего не сможет.
Разница огромная: в AES-GCM повтор nonce — пробоина в корпусе. В AES-GCM-SIV — царапина на краске.
Плюсы:
- прощает случайный повтор nonce;
- быстрый при наличии AES-ускорения.
Минусы:
- сложнее в реализации;
- менее распространён.
Когда использовать: когда сложно гарантировать уникальность nonce (например, в распределённых системах без общего состояния), но есть AES-ускорение.
Часть седьмая. AEAD вокруг нас
Вы уже пользуетесь AEAD каждый день — просто не знаете об этом.
TLS: каждый HTTPS-запрос
Когда вы открываете сайт, браузер и сервер шифруют трафик. В современном TLS 1.3 это всегда AEAD — либо AES-GCM, либо ChaCha20-Poly1305.
Каждый пакет данных защищён от чтения и от подмены. Заголовки, определяющие тип записи, включены в присоединённые данные.
WireGuard: современный VPN
WireGuard — пожалуй, самый элегантный VPN-протокол — использует ChaCha20-Poly1305 для всего шифрования.
Минимум кода, максимум безопасности. Никаких опций «выберите алгоритм» — всё зашито правильно.
Signal: защищённый мессенджер
Signal Protocol использует AEAD для шифрования каждого сообщения. Даже если атакующий получит доступ к сообщению, он не сможет его изменить или подделать.
SSH: удалённый доступ
Современные версии SSH поддерживают ChaCha20-Poly1305 и AES-GCM. Каждая команда, каждый вывод защищены от чтения и подмены.
Шифрование дисков и файлов
- age (современная замена PGP) использует ChaCha20-Poly1305;
- LUKS2 поддерживает AES-GCM для шифрования дисков;
- Android использует AES-GCM для шифрования хранилища.
Часть восьмая. Практические советы
Пользуйтесь готовым
Не реализуйте AEAD самостоятельно. Никогда. Используйте проверенные библиотеки:
- libsodium — отличный выбор для большинства языков, API спроектирован так, что ошибиться сложно;
- OpenSSL / BoringSSL — если нужна совместимость с TLS-экосистемой;
- стандартные библиотеки языков — Go crypto, Python cryptography, Rust ring/RustCrypto.
Не изобретайте протоколы
Нужно защитить данные при передаче — используйте TLS. Не придумывайте своё «шифрование поверх TCP».
Нужно хранить секреты — используйте готовые решения вроде Vault или sealed secrets.
Нужно зашифровать файл — age проще и надёжнее самодельного решения.
Не забывайте про присоединённые данные
Частая ошибка — использовать AEAD с пустыми присоединёнными данными. Технически работает, но вы теряете защиту контекста.
Всегда думайте: что ещё определяет смысл этого сообщения? Что могло бы быть подменено?
Храните ключи отдельно от данных
AEAD защищает данные, только пока ключ в секрете. Если ключ лежит рядом с шифртекстом — защиты нет.
Думайте о ротации ключей
Ключи не вечны. Планируйте, как будете их менять без потери доступа к старым данным.
Заключение
Аутентифицированное шифрование стало стандартом не потому, что все вдруг стали параноиками.
Просто мир несовершенен. Не обязательно враждебен — иногда просто хаотичен. Сетевые сбои, повторы пакетов, ошибки в коде, кэширующие прокси, человеческий фактор.
«Скрыть» данные недостаточно. Нужно ещё гарантировать, что вы читаете именно то, что было отправлено. Именно вам. Именно в том контексте, который ожидался.
AEAD — одна из тех редких технологий, которая не усложняет жизнь, а делает её честнее. Либо всё в порядке, либо нет. Никаких «ну вроде похоже». Никаких «наверное, это правильные данные». Никакой полуправды.
Коробка с пломбой надёжнее, чем просто закрытая коробка.