AEAD: шифрование, которое не верит на слово

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, аутентифицированное шифрование с присоединёнными данными.

Это не один алгоритм, а целое семейство алгоритмов, которые решают обе задачи сразу:

  1. Шифруют — чтобы никто не прочитал.
  2. Проверяют подлинность — чтобы никто не подменил.

И делают это так, что ошибиться гораздо сложнее. Не нужно думать, в каком порядке что вычислять, что куда включать, как сравнивать. Всё продумано и встроено внутрь.

Если кто-то изменит шифртекст — расшифровка не произойдёт. Вы не получите «слегка испорченный текст». Вы получите ошибку: «Подлинность не подтверждена».

Аналогия: посылка с защитой

Обычная почта — это просто коробка. Можно вскрыть, посмотреть, положить обратно, заклеить. Если делать аккуратно — никто не заметит.

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 — одна из тех редких технологий, которая не усложняет жизнь, а делает её честнее. Либо всё в порядке, либо нет. Никаких «ну вроде похоже». Никаких «наверное, это правильные данные». Никакой полуправды.

Коробка с пломбой надёжнее, чем просто закрытая коробка.

Предыдущий пост Следующий пост
Наверх