Перейти к содержимому
ecdsa.com

AES-GCM или ChaCha20-Poly1305: какой AEAD выбрать

Оба — стандартные AEAD с 128-битным тегом и 96-битным nonce. AES-GCM выигрывает на процессорах с инструкциями AES и в средах с требованиями FIPS, ChaCha20-Poly1305 — во всех остальных случаях.
Что сравниваетсяAES-GCMChaCha20-Poly1305

AES-GCM или ChaCha20-Poly1305 — это выбор между двумя алгоритмами аутентифицированного шифрования, на которых держится почти весь зашифрованный трафик: TLS 1.3, QUIC, SSH, WireGuard, age и большая часть шифрования на уровне приложений. При правильном использовании оба безопасны, при повторе nonce защита обоих серьёзно нарушается, а их скорость сильно зависит от оборудования.

Коротко

AES-GCMChaCha20-Poly1305
СпецификацияNIST SP 800-38D (2007)RFC 8439 (2018, заменяет RFC 7539)
Длина ключа128, 192, 256 бит256 бит
Nonceрекомендуется 96 бит96 бит (192 бита в XChaCha20)
Тег128 бит (короче допускается, но не рекомендуется)128 бит
Максимум данных в одном сообщенииоколо 64 ГиБ (2³² − 2 блока)256 ГиБ (2³² блоков по 64 байта)
Лимит случайных nonce на один ключ2³² сообщений (SP 800-38D)Тот же порядок при 96-битных nonce; с XChaCha20 практически без ограничений
Повторное использование nonceРаскрывает XOR открытых текстов и ключ GHASH, что позволяет подделывать сообщенияРаскрывает XOR открытых текстов и позволяет подделывать сообщения
Пропускная способность, 16 КиБ, OpenSSL 3.6.510,4 ГБ/с (AES-128), 8,8 ГБ/с (AES-256)2,1 ГБ/с
Пропускная способность, сообщения по 16 байт0,14 ГБ/с (AES-256)0,52 ГБ/с
Постоянное время выполнения без аппаратной поддержкиТрудно достижимо (табличная реализация AES даёт утечки через кеш)Достигается естественным образом (сложение, циклический сдвиг, XOR)
Одобрен по FIPS 140-3ДаНет
TLS 1.3TLS_AES_128_GCM_SHA256 (обязателен), TLS_AES_256_GCM_SHA384 (рекомендуется)TLS_CHACHA20_POLY1305_SHA256 (рекомендуется)
Другие примененияJWE A128GCM и A256GCM, IPsec, SSH aes256-gcm@openssh.com, шифрование дисков и облачных данныхWireGuard, SSH chacha20-poly1305@openssh.com, age, libsodium

Пропускная способность измерена командой openssl speed -evp на одном ядре Apple M4 Max, у которого есть инструкции AES и полиномиального умножения.

Производительность зависит от процессора

AES-GCM рассчитан на аппаратную поддержку: каждый раунд AES выполняется одной инструкцией (AES-NI на x86, криптографические расширения ARMv8), а GHASH использует умножение без переноса (carry-less multiplication). Когда доступно и то и другое, в этом замере он выдаёт около 10 ГБ/с на ядро — примерно в пять раз больше, чем ChaCha20-Poly1305. Без них реализации AES приходится выбирать между таблицами поиска, которые работают медленно и раскрывают биты ключа через время обращения к кешу, и bitslice-кодом, который работает за постоянное время, но тоже медленно. Для ChaCha20 нужны только 32-битные сложения, циклические сдвиги и XOR, поэтому он работает быстро и за постоянное время на любом процессоре, включая бюджетные телефоны и микроконтроллеры.

На маленьких сообщениях разрыв сокращается, а то и меняется на обратный: на входных данных по 16 байт ChaCha20-Poly1305 показал 0,52 ГБ/с против 0,14 ГБ/с у AES-256-GCM, потому что в GCM основное время уходит на подготовку. Для типичных TLS-записей (до 16 КиБ) на серверах быстрее AES-GCM; для множества крошечных пакетов разница в любом случае невелика.

Nonce и ограничения

Оба алгоритма — шифры в режиме счётчика с одноразовыми аутентификаторами, поэтому пара (ключ, nonce) никогда не должна повторяться. Повтор раскрывает XOR двух открытых текстов; в GCM он, кроме того, позволяет атакующему восстановить подключ аутентификации и подделывать сообщения для этого ключа, а в Poly1305 раскрывает одноразовый ключ и позволяет подделать сообщение с этим nonce. NIST SP 800-38D ограничивает использование случайно сгенерированных 96-битных IV величиной 2³² вызовов на ключ, чтобы вероятность коллизии оставалась пренебрежимо малой; TLS обходит эту проблему: nonce выводятся из порядковых номеров записей, а ключи периодически обновляются.

Если ваша схема генерирует случайные nonce для множества сообщений под одним ключом, используйте конструкцию с более широким пространством nonce или устойчивостью к неправильному использованию (misuse resistance):

  • XChaCha20-Poly1305 (192-битные nonce, описан в черновике IRTF и реализован в libsodium и других библиотеках): с ним случайные nonce безопасны практически для неограниченного числа сообщений;
  • AES-GCM-SIV (RFC 8452): повторное использование nonce раскрывает лишь то, были ли два сообщения одинаковыми;
  • или вывод отдельного ключа для каждого сообщения или сеанса, как это делают TLS, age и HPKE.

Ни один из алгоритмов не обеспечивает привязку к ключу (key commitment): можно подобрать шифртекст, который корректно расшифровывается двумя разными ключами. Это важно для схем с несколькими получателями или на основе пароля (проблема «невидимых саламандр», invisible salamanders). Если атакующий может выбирать ключи, добавьте привязку к ключу.

Node.js 26: один и тот же интерфейс AEAD для обоих алгоритмов
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";

const plaintext = Buffer.from("card ending 4242, amount 19.99");
const aad = Buffer.from("order=7781");

for (const algorithm of ["aes-256-gcm", "chacha20-poly1305"]) {
  const key = randomBytes(32);
  const nonce = randomBytes(12); // 96-bit nonce for both; never reuse with the same key
  const cipher = createCipheriv(algorithm, key, nonce, { authTagLength: 16 });
  cipher.setAAD(aad);
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
  const tag = cipher.getAuthTag();

  const decipher = createDecipheriv(algorithm, key, nonce, { authTagLength: 16 });
  decipher.setAAD(aad);
  decipher.setAuthTag(tag);
  const roundTrip = Buffer.concat([decipher.update(ciphertext), decipher.final()]);

  console.log(algorithm.padEnd(18), "ciphertext", ciphertext.length, "bytes + tag", tag.length, "bytes, decrypts:", roundTrip.equals(plaintext));
}
Результат
aes-256-gcm        ciphertext 30 bytes + tag 16 bytes, decrypts: true
chacha20-poly1305  ciphertext 30 bytes + tag 16 bytes, decrypts: true

Как TLS выбирает между ними

TLS 1.3 требует, чтобы каждая реализация поддерживала TLS_AES_128_GCM_SHA256, и рекомендует AES-256-GCM и ChaCha20-Poly1305. Клиенты без аппаратной поддержки AES ставят ChaCha20 первым в списке; серверы, которые учитывают это предпочтение, отдают каждому клиенту более быстрый для него шифр. В OpenSSL для этого есть параметр PrioritizeChaCha (SSL_OP_PRIORITIZE_CHACHA): он выбирает ChaCha20, только если клиент указал его первым. BoringSSL для той же цели использует группы с равным приоритетом. Генератор конфигурации TLS создаёт настройки сервера, в которых сохранены оба набора шифров.

Что когда выбирать

  • Серверы TLS и QUIC: включите оба; отдавайте предпочтение AES-GCM, но позвольте клиентам, которые ставят ChaCha20 первым, получать его.
  • Среды FIPS 140-3: AES-GCM; ChaCha20-Poly1305 не входит в число одобренных алгоритмов.
  • Мобильные устройства, IoT и микроконтроллеры без аппаратного ускорения AES: ChaCha20-Poly1305.
  • Шифрование на уровне приложений со случайными nonce и долгоживущими ключами: XChaCha20-Poly1305 или AES-GCM-SIV.
  • JWE: A256GCM (или A128GCM); ChaCha20 нет в основном реестре JWA.
  • Очень большие отдельные сообщения (больше 64 ГиБ): разбивайте их на фрагменты с отдельным nonce для каждого (так age шифрует данные фрагментами ChaCha20-Poly1305 по 64 КиБ); не полагайтесь на предельный объём одного сообщения.

Заметки о миграции

В хорошо спроектированной системе смена AEAD меняет формат шифртекста и больше ничего.

  1. Версионируйте формат: храните с каждым шифртекстом идентификатор алгоритма и идентификатор ключа, чтобы старые данные оставались читаемыми.
  2. Используйте для каждого алгоритма свой ключ: выводите разные ключи (например, с помощью HKDF с разными строками info), а не применяйте один ключ для обоих.
  3. Перепроверьте генерацию nonce: счётчики должны сохраняться между перезапусками; случайные nonce должны укладываться в указанные выше ограничения, иначе переходите на XChaCha20.
  4. Перешифровывайте по мере обращения к данным: когда запись затрагивается, расшифровывайте её старым алгоритмом и записывайте новым, если только нормативный срок не требует массовой миграции.
  5. Измеряйте производительность на реальном оборудовании: на серверных процессорах и на маленьких устройствах лидеры по скорости разные.

Источники

Все сравнения