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.
Когда использовать: на мобильных устройствах, в чисто программных реализациях, когда нет AES-ускорения.
XChaCha20-Poly1305
Расширенная версия ChaCha20-Poly1305.
Когда использовать: когда хочется генерировать nonce случайно без страха коллизий. Отличный выбор для прикладных задач.
AES-GCM-SIV
Главная фишка — устойчивость к повтору nonce.
Напомню, что происходит при повторе nonce в обычном AES-GCM: злоумышленник может расшифровать сообщения, подделать аутентификационный тег, полностью скомпрометировать шифрование. Это катастрофа.
В AES-GCM-SIV при повторе nonce ничего такого не случится. Аутентификация останется надёжной — подделать сообщение не получится. Единственная «утечка»: если вы зашифруете два одинаковых сообщения с одинаковым nonce, шифртексты тоже совпадут. Наблюдатель увидит: «эти два сообщения одинаковые». Но что именно в них — не узнает. И подделать ничего не сможет.
Разница огромная: в AES-GCM повтор nonce — пробоина в корпусе. В AES-GCM-SIV — царапина на краске.
Когда использовать: когда сложно гарантировать уникальность 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 — одна из тех редких технологий, которая не усложняет жизнь, а делает её честнее. Либо всё в порядке, либо нет. Никаких «ну вроде похоже». Никаких «наверное, это правильные данные». Никакой полуправды.