Saltar al contenido
ecdsa.com

AES-GCM frente a ChaCha20-Poly1305: qué AEAD usar

Ambos son AEAD estándar con etiquetas de 128 bits y nonces de 96 bits. AES-GCM gana en CPU con instrucciones AES y en entornos FIPS; ChaCha20-Poly1305 gana en todos los demás casos.

Elegir entre AES-GCM y ChaCha20-Poly1305 es elegir entre los dos algoritmos de cifrado autenticado que transportan casi todo el tráfico cifrado: TLS 1.3, QUIC, SSH, WireGuard, age y la mayor parte del cifrado a nivel de aplicación. Ambos son seguros si se usan correctamente, ambos fallan de forma grave cuando se repite un nonce, y su velocidad depende mucho del hardware.

De un vistazo

AES-GCMChaCha20-Poly1305
EspecificaciónNIST SP 800-38D (2007)RFC 8439 (2018, sustituye al RFC 7539)
Tamaños de clave128, 192, 256 bits256 bits
Nonce96 bits recomendados96 bits (192 bits en XChaCha20)
Etiqueta128 bits (se admiten etiquetas más cortas, pero no se recomiendan)128 bits
Máximo de datos por mensajeunos 64 GiB (2³² − 2 bloques)256 GiB (2³² bloques de 64 bytes)
Presupuesto de nonces aleatorios por clave2³² mensajes (SP 800-38D)Del mismo orden con nonces de 96 bits; prácticamente ilimitado con XChaCha20
Reutilización del nonceRevela el XOR de los textos en claro y la clave de GHASH, lo que permite falsificacionesRevela el XOR de los textos en claro y permite falsificaciones
Rendimiento, 16 KiB, OpenSSL 3.6.510,4 GB/s (AES-128), 8,8 GB/s (AES-256)2,1 GB/s
Rendimiento, mensajes de 16 bytes0,14 GB/s (AES-256)0,52 GB/s
Tiempo constante sin hardwareDifícil (el AES basado en tablas filtra información a través de la caché)Natural (sumas, rotaciones y XOR)
Aprobado en FIPS 140-3SíNo
TLS 1.3TLS_AES_128_GCM_SHA256 (MUST), TLS_AES_256_GCM_SHA384 (SHOULD)TLS_CHACHA20_POLY1305_SHA256 (SHOULD)
Otros usosJWE A128GCM y A256GCM, IPsec, SSH aes256-gcm@openssh.com, cifrado de discos y en la nubeWireGuard, SSH chacha20-poly1305@openssh.com, age, libsodium

Rendimiento medido con openssl speed -evp en un núcleo de un Apple M4 Max, que tiene instrucciones de AES y de multiplicación polinómica.

El rendimiento depende de la CPU

AES-GCM está pensado para el hardware: cada ronda de AES se ejecuta como una sola instrucción (AES-NI en x86, las extensiones criptográficas de ARMv8) y GHASH usa multiplicación sin acarreo. Con ambas disponibles, aquí alcanza unos 10 GB/s por núcleo, unas cinco veces más que ChaCha20-Poly1305. Sin ellas, AES tiene que recurrir a tablas de consulta, que son lentas y filtran bits de la clave a través de los tiempos de la caché, o a código bitsliced, que es de tiempo constante pero lento. ChaCha20 solo necesita sumas, rotaciones y XOR de 32 bits, así que se ejecuta rápido y en tiempo constante en cualquier CPU, incluidos los teléfonos de gama baja y los microcontroladores.

Con mensajes pequeños, la diferencia se reduce o se invierte: con entradas de 16 bytes, ChaCha20-Poly1305 alcanzó 0,52 GB/s frente a 0,14 GB/s de AES-256-GCM, porque domina el coste de preparación de GCM. Con los registros TLS habituales (de hasta 16 KiB) en servidores, AES-GCM es más rápido; con muchos paquetes diminutos, la diferencia es pequeña en cualquier caso.

Nonces y límites

Ambos son algoritmos de cifrado en modo contador con autenticadores de un solo uso, así que el par (clave, nonce) nunca debe repetirse. Una repetición revela el XOR de los dos textos en claro; con GCM, además, permite a un atacante recuperar la subclave de autenticación y falsificar mensajes con esa clave, y con Poly1305 expone la clave de un solo uso y permite falsificar mensajes para ese nonce. NIST SP 800-38D limita los IV de 96 bits generados aleatoriamente a 2³² invocaciones por clave para que la probabilidad de colisión sea despreciable; TLS esquiva el problema derivando los nonces de los números de secuencia de los registros y renovando las claves.

Si tu diseño genera nonces aleatorios para muchos mensajes con una misma clave, usa una construcción con más espacio de nonces o con resistencia al mal uso:

  • XChaCha20-Poly1305 (nonces de 192 bits, descrito en un Internet-Draft del IRTF e implementado por libsodium, entre otros), con el que los nonces aleatorios son seguros para una cantidad prácticamente ilimitada de mensajes;
  • AES-GCM-SIV (RFC 8452), con el que la reutilización del nonce solo revela si dos mensajes eran idénticos;
  • o la derivación de claves por mensaje o por sesión, como hacen TLS, age y HPKE.

Ninguno de los dos algoritmos ofrece compromiso con la clave: se puede construir un texto cifrado que se descifre de forma válida con dos claves distintas, algo que importa en diseños con varios destinatarios o basados en contraseñas (el problema conocido como «invisible salamanders»). Si los atacantes pueden elegir las claves, añade un compromiso con la clave (key commitment).

Node.js 26: la misma interfaz AEAD para ambos algoritmos
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));
}
Salida
aes-256-gcm        ciphertext 30 bytes + tag 16 bytes, decrypts: true
chacha20-poly1305  ciphertext 30 bytes + tag 16 bytes, decrypts: true

Cómo elige TLS entre ellos

TLS 1.3 exige que toda implementación admita TLS_AES_128_GCM_SHA256 y recomienda AES-256-GCM y ChaCha20-Poly1305. Los clientes sin hardware AES ponen ChaCha20 en primer lugar; los servidores que respetan esa preferencia sirven a cada cliente el cifrado que le resulta más rápido. OpenSSL lo implementa con la opción PrioritizeChaCha (SSL_OP_PRIORITIZE_CHACHA), que prefiere ChaCha20 solo cuando el cliente lo puso en primer lugar, y BoringSSL usa grupos de igual preferencia con el mismo fin. El generador de configuración TLS produce una configuración de servidor que mantiene ambos conjuntos de cifrado.

Cuándo elegir cada uno

  • Servidores TLS y QUIC: activa ambos; prefiere AES-GCM, pero deja que los clientes que ponen ChaCha20 en primer lugar lo obtengan.
  • Entornos FIPS 140-3: AES-GCM; ChaCha20-Poly1305 no es un algoritmo aprobado.
  • Dispositivos móviles, IoT y microcontroladores sin aceleración AES: ChaCha20-Poly1305.
  • Cifrado a nivel de aplicación con nonces aleatorios y claves de larga duración: XChaCha20-Poly1305 o AES-GCM-SIV.
  • JWE: A256GCM (o A128GCM); ChaCha20 no figura en el registro principal de JWA.
  • Mensajes individuales muy grandes (más de 64 GiB): divídelos en fragmentos con un nonce por fragmento (como hace age con fragmentos de ChaCha20-Poly1305 de 64 KiB); no te apoyes en los límites por mensaje.

Notas de migración

Cambiar de AEAD modifica el formato del texto cifrado, pero nada más en un sistema bien diseñado.

  1. Versiona el formato: guarda un identificador de algoritmo y un identificador de clave con cada texto cifrado para que los datos antiguos sigan siendo legibles.
  2. Usa una clave distinta por algoritmo: deriva claves diferentes (por ejemplo, con HKDF y cadenas de info distintas) en lugar de reutilizar una misma clave para ambos.
  3. Revisa la generación de nonces: los contadores deben persistir entre reinicios; los nonces aleatorios deben mantenerse dentro de los límites anteriores o pasar a XChaCha20.
  4. Vuelve a cifrar de forma diferida: descifra con el algoritmo antiguo y escribe con el nuevo a medida que se accede a los registros, salvo que un plazo de cumplimiento normativo exija una migración masiva.
  5. Mide el rendimiento en el hardware real: el orden de velocidad se invierte entre las CPU de servidor y los dispositivos pequeños.

Referencias

Todas las comparaciones