AES-GCM vs ChaCha20-Poly1305 é a escolha entre os dois algoritmos de criptografia autenticada que transportam quase todo o tráfego criptografado: TLS 1.3, QUIC, SSH, WireGuard, age e a maior parte da criptografia na camada de aplicação. Os dois são seguros quando usados corretamente, os dois falham de forma grave quando um nonce se repete, e a velocidade de ambos depende muito do hardware.
Visão geral
| AES-GCM | ChaCha20-Poly1305 | |
|---|---|---|
| Especificação | NIST SP 800-38D (2007) | RFC 8439 (2018, substitui a RFC 7539) |
| Tamanhos de chave | 128, 192, 256 bits | 256 bits |
| Nonce | 96 bits (recomendado) | 96 bits (192 bits no XChaCha20) |
| Tag | 128 bits (tags mais curtas são permitidas, mas não recomendadas) | 128 bits |
| Máximo de dados por mensagem | cerca de 64 GiB (2³² − 2 blocos) | 256 GiB (2³² blocos de 64 bytes) |
| Limite de nonces aleatórios por chave | 2³² mensagens (SP 800-38D) | Mesma ordem de grandeza com nonces de 96 bits; praticamente ilimitado com XChaCha20 |
| Reutilização de nonce | Vaza o XOR dos textos claros e a chave do GHASH, o que permite falsificações | Vaza o XOR dos textos claros e permite falsificações |
| Vazão, 16 KiB, OpenSSL 3.6.5 | 10,4 GB/s (AES-128), 8,8 GB/s (AES-256) | 2,1 GB/s |
| Vazão, mensagens de 16 bytes | 0,14 GB/s (AES-256) | 0,52 GB/s |
| Tempo constante sem hardware | Difícil (o AES baseado em tabelas vaza informação pelo cache) | Natural (adição, rotação, XOR) |
| Aprovado no FIPS 140-3 | Sim | Não |
| TLS 1.3 | TLS_AES_128_GCM_SHA256 (obrigatória), TLS_AES_256_GCM_SHA384 (recomendada) | TLS_CHACHA20_POLY1305_SHA256 (recomendada) |
| Outros usos | JWE A128GCM e A256GCM, IPsec, SSH aes256-gcm@openssh.com, criptografia de disco e na nuvem | WireGuard, SSH chacha20-poly1305@openssh.com, age, libsodium |
Vazão medida com openssl speed -evp em um núcleo de um Apple M4 Max, que tem instruções de AES e de multiplicação polinomial.
O desempenho depende da CPU
O AES-GCM foi feito para o hardware: as rodadas do AES são executadas como instruções únicas (AES-NI no x86, as extensões de criptografia do ARMv8) e o GHASH usa multiplicação sem carry. Com os dois recursos disponíveis, ele chega a uns 10 GB/s por núcleo nesta medição, cerca de cinco vezes a vazão do ChaCha20-Poly1305. Sem eles, o AES precisa usar tabelas de consulta, que são lentas e vazam bits da chave pelo tempo de acesso ao cache, ou código bitsliced, que é de tempo constante, mas lento. O ChaCha20 só precisa de adições, rotações e XORs de 32 bits, por isso roda rápido e em tempo constante em qualquer CPU, inclusive em celulares de entrada e microcontroladores.
Mensagens pequenas reduzem ou invertem a diferença: com entradas de 16 bytes, o ChaCha20-Poly1305 chegou a 0,52 GB/s, contra 0,14 GB/s do AES-256-GCM, porque o custo de preparação do GCM domina. Para registros TLS típicos (até 16 KiB) em servidores, o AES-GCM é mais rápido; com muitos pacotes minúsculos, a diferença é pequena, seja qual for o mais rápido.
Nonces e limites
Os dois são cifras em modo contador com autenticadores de uso único, por isso o par (chave, nonce) nunca deve se repetir. Uma repetição revela o XOR dos dois textos claros; no GCM, ela também permite que um atacante recupere a subchave de autenticação e falsifique mensagens com essa chave, e no Poly1305 ela expõe a chave de uso único e permite falsificação para aquele nonce. A NIST SP 800-38D limita os IVs de 96 bits gerados aleatoriamente a 2³² invocações por chave, para manter a probabilidade de colisão desprezível; o TLS contorna o problema derivando os nonces dos números de sequência dos registros e renovando as chaves.
Se seu projeto gera nonces aleatórios para muitas mensagens com a mesma chave, use uma construção com mais espaço de nonce ou com resistência a uso indevido (misuse resistance):
- XChaCha20-Poly1305 (nonces de 192 bits, descrito em um Internet-Draft do IRTF e implementado pela libsodium e por outras bibliotecas), em que nonces aleatórios são seguros para uma quantidade praticamente ilimitada de mensagens;
- AES-GCM-SIV (RFC 8452), em que a reutilização de nonce revela apenas se duas mensagens eram idênticas;
- ou derivação de chave por mensagem ou por sessão, como fazem o TLS, o age e o HPKE.
Nenhum dos dois algoritmos vincula o texto cifrado à chave (não há key commitment): é possível montar um texto cifrado que se descriptografa de forma válida com duas chaves diferentes, o que importa em projetos com vários destinatários ou baseados em senha (o problema das “invisible salamanders”). Acrescente um mecanismo de key commitment se atacantes puderem escolher as chaves.
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
Como o TLS escolhe entre os dois
O TLS 1.3 exige que toda implementação ofereça suporte a TLS_AES_128_GCM_SHA256 e recomenda o AES-256-GCM e o ChaCha20-Poly1305. Clientes sem hardware de AES colocam o ChaCha20 em primeiro lugar na lista; servidores que respeitam essa preferência entregam a cada cliente a cifra mais rápida para ele. O OpenSSL implementa isso com a opção PrioritizeChaCha (SSL_OP_PRIORITIZE_CHACHA), que prefere o ChaCha20 apenas quando o cliente o listou primeiro, e o BoringSSL usa grupos de preferência igual com o mesmo objetivo. O gerador de configurações TLS produz configurações de servidor que mantêm as duas cipher suites.
Quando escolher cada um
- Servidores TLS e QUIC: habilite os dois; prefira o AES-GCM, mas deixe que os clientes que colocam o ChaCha20 em primeiro lugar o recebam.
- Ambientes FIPS 140-3: AES-GCM; o ChaCha20-Poly1305 não é um algoritmo aprovado.
- Dispositivos móveis, IoT e microcontroladores sem aceleração de AES: ChaCha20-Poly1305.
- Criptografia na camada de aplicação com nonces aleatórios e chaves de longa duração: XChaCha20-Poly1305 ou AES-GCM-SIV.
- JWE:
A256GCM(ouA128GCM); o ChaCha20 não está no registro principal do JWA. - Mensagens únicas muito grandes (acima de 64 GiB): divida-as em segmentos com um nonce por segmento (como o age faz com segmentos de 64 KiB de ChaCha20-Poly1305); não conte com os limites por mensagem.
Notas de migração
Em um sistema bem projetado, trocar de AEAD muda o formato do texto cifrado e nada mais.
- Versione o formato: armazene um identificador do algoritmo e um identificador da chave com cada texto cifrado, para que os dados antigos continuem legíveis.
- Use uma chave separada por algoritmo: derive chaves distintas (por exemplo, com HKDF e strings info diferentes) em vez de reutilizar a mesma chave nos dois.
- Revise a geração de nonces: os contadores precisam ser gravados de forma persistente, para sobreviver a reinicializações; com nonces aleatórios, é preciso ficar dentro dos limites acima ou migrar para o XChaCha20.
- Recriptografe sob demanda: descriptografe com o algoritmo antigo e grave com o novo quando os registros forem acessados, a menos que um prazo de conformidade exija uma migração em massa.
- Meça o desempenho no hardware real: a ordem de velocidade se inverte entre CPUs de servidor e dispositivos pequenos.