VOLVER A NOTICIAS
defensa #grover #aes #simetrico #migracion #tamano-clave

Grover muerde a AES: por qué 128 ha muerto y 256 es tu nueva base

El algoritmo de Grover da un speedup cuadrático sobre la búsqueda de clave por fuerza bruta. AES-128 baja a ~64 bits efectivos. AES-256 se queda en ~128 bits. El arreglo es una línea de config — pero hay que encontrar cada sitio donde vive estado de cifrado.

por · · 3 min de lectura

TL;DR

Un adversario cuántico corriendo el algoritmo de Grover contra un cifrado simétrico de n bits necesita unas 2^(n/2) operaciones cuánticas para recuperar la clave. AES-128 cae a ~2⁶⁴ trabajo efectivo — al alcance de un adversario serio en los 2030s. AES-256 cae a ~2¹²⁸ efectivo — más allá de cualquier presupuesto proyectado. Mueve cada clave AES-128 a AES-256 ya. Esta es la actualización PQ más barata que jamás harás.

1. La matemática, comprimida

Grover da un speedup de raíz cuadrada sobre búsqueda no estructurada. Para un espacio de claves de tamaño 2^n:

  • Fuerza bruta clásica: 2^n intentos, ~2^n puertas cuánticas.
  • Cuántico (Grover): ~2^(n/2) iteraciones, cada una costando ~O(n) puertas reversibles más una evaluación de oráculo.

Concretamente, para AES:

Cifrado Esfuerzo clásico Esfuerzo Grover Veredicto
AES-128 2¹²⁸ 2⁶⁴ Cae en los 2030s
AES-192 2¹⁹² 2⁹⁶ Límite
AES-256 2²⁵⁶ 2¹²⁸ Seguro

El número 2⁶⁴ es aproximadamente el coste de minar la cadena de Bitcoin dos veces. No trivial — pero predecible, paralelizable, y al alcance de un adversario a nivel estatal hacia finales de los 2020s.

El aviso CNSA 2.0 de la NSA (2022) deja explícitamente AES-128 fuera de las suites aprobadas para "confidencialidad a largo plazo de información de seguridad nacional". El objetivo de migración es AES-256-GCM.

2. Por qué los hashes están mayormente bien

El mismo speedup aplica a preimágenes de hash. El coste de preimagen de SHA-256 baja de 2²⁵⁶ a ~2¹²⁸ bajo Grover — sigue seguro. Los ataques de colisión (cumpleaños) son más duros cuánticamente: Brassard-Høyer-Tapp da raíz cúbica, así que las colisiones de SHA-256 bajan de 2¹²⁸ a ~2⁸⁵.

Ese número de colisión importa para compromisos de larga duración — raíces Merkle en logs append-only, entradas de certificate-transparency, storage deduplicado. Para esos casos, migra a SHA-384 o SHA3-384. SHA-256 está bien para integridad de corta duración (MAC de record TLS, HMAC de tokens de sesión).

3. Migración: cada sitio que escribiste AES-128, séde'lo

La mayoría de equipos tienen AES-128 esparcido en tres sitios:

  1. Suites de cipher TLS (config del servidor, config del load balancer).
  2. Cifrado de envelope a nivel de aplicación (llamadas KMS, cifrado de columnas de base de datos, archivos en reposo).
  3. Cifrado de RPC interno (Wireguard, Tailscale, HMAC + AES custom).

3a. Python (cryptography)

PYTHON
1# cryptography ≥ 41
2from cryptography.hazmat.primitives.ciphers.aead import AESGCM
3import os
4 
5# ANTES: AES-128 — vulnerable a Grover
6key128 = AESGCM.generate_key(bit_length=128)
7aead128 = AESGCM(key128)
8 
9# DESPUÉS: AES-256 — seguro frente a Grover
10key256 = AESGCM.generate_key(bit_length=256)
11aead256 = AESGCM(key256)
12 
13nonce = os.urandom(12)
14ct = aead256.encrypt(nonce, b'plaintext', b'associated-data')
15pt = aead256.decrypt(nonce, ct, b'associated-data')

3b. Node.js

JAVASCRIPT
1const crypto = require('node:crypto');
2 
3// ANTES: aes-128-gcm
4function encryptOld(key, plaintext) {
5 const iv = crypto.randomBytes(12);
6 const cipher = crypto.createCipheriv('aes-128-gcm', key, iv);
7 const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
8 return { iv, tag: cipher.getAuthTag(), ct };
9}
10 
11// DESPUÉS: aes-256-gcm (solo cambian el algoritmo y la longitud de la clave)
12function encryptNew(key, plaintext) {
13 // key debe ser de 32 bytes
14 const iv = crypto.randomBytes(12);
15 const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
16 const ct = Buffer.concat([cipher.update(plaintext), cipher.final()]);
17 return { iv, tag: cipher.getAuthTag(), ct };
18}
19 
20// Genera una clave fresca de 256 bits
21const key256 = crypto.randomBytes(32);

3c. PHP (libsodium)

PHP
1<?php
2// libsodium viene con PHP 7.2+ — incorporado, sin extensión que instalar.
3// XChaCha20-Poly1305 es un reemplazo casi directo de AES-256-GCM y es la
4// primitiva simétrica cuántica-segura recomendada en CNSA 2.0.
5 
6// ANTES: openssl AES-128-GCM
7$key128 = random_bytes(16);
8$iv = random_bytes(12);
9$tag = '';
10$ct128 = openssl_encrypt('hola', 'aes-128-gcm', $key128, OPENSSL_RAW_DATA, $iv, $tag);
11 
12// DESPUÉS: libsodium XChaCha20-Poly1305 (efectivamente 256 bits, simétrico cuántica-seguro)
13$key256 = sodium_crypto_aead_xchacha20poly1305_ietf_keygen(); // 32 bytes
14$nonce = random_bytes(SODIUM_CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
15$ct256 = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt('hola', '', $nonce, $key256);

3d. TLS — Apache / nginx

NGINX
1# nginx: quita AES-128, deja solo AEADs AES-256
2ssl_protocols TLSv1.2 TLSv1.3;
3ssl_ciphers "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384";
4ssl_prefer_server_ciphers off;
APACHE
1# Apache mod_ssl
2SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1
3SSLCipherSuite TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384
4SSLHonorCipherOrder off

4. Barrido one-shot por un repo

BASH
1# aes256-migrate.sh — marca cada referencia AES-128 en un árbol.
2# Conservador: nunca auto-edita, solo lista. Requiere revisión manual.
3set -eu
4 
5ROOT="${1:-.}"
6 
7echo "=== Referencias AES-128 en $ROOT ==="
8grep -rnE 'AES[-_]?128[-_]?(GCM|CBC|CTR|ECB)?|aes-128-(gcm|cbc|ctr|ecb)|aes_128'
9 --include='*.{py,js,ts,php,go,rb,java,kt,rs,c,cpp,sh,yaml,yml,conf,nginx,htaccess}'
10 "$ROOT" 2>/dev/null
11 
12echo
13echo "=== Generadores con bit_length=128 ==="
14grep -rnE 'bit_lengths*=s*128|key.*=.*16s*[);]|randomBytes(s*16s*)'
15 --include='*.{py,js,ts,php,go,rb,rs}' "$ROOT" 2>/dev/null
16 
17echo
18echo "=== Configs TLS referenciando suites CBC ==="
19grep -rnE 'AES.*CBC|CBC-SHA'
20 --include='*.{conf,nginx,htaccess,yaml,yml}' "$ROOT" 2>/dev/null

Hazlo ejecutable y córrelo contra tu repo: chmod +x aes256-migrate.sh && ./aes256-migrate.sh /srv/app. Tría por archivo.

5. Coste de esperar

Un documento cifrado con AES-128 en 2026 será abrible por un adversario a nivel estatal con capacidad cuántica hacia 2032-2034. Si tu política de retención dice "cifrado durante 7 años", la matemática dice que ya llegas tarde.

La migración a AES-256 no rompe ningún protocolo. Sin nuevas dependencias. Sin regresión de rendimiento que valga la pena medir (AES-NI en Intel/AMD hace 256 casi indistinguible de 128 en el throughput que te importa). Esta es la única actualización post-cuántica más barata de toda la pila.

6. Referencias

  • Bernstein, Cost analysis of hash collisions: will quantum computers make SHARCS obsolete? (2009)
  • Grassl, Langenberg, Roetteler y Steinwandt, Applying Grover's algorithm to AES: quantum resource estimates (2016)
  • NSA CNSA 2.0 (2022, actualizado 2024) — exige AES-256-GCM en builds nuevos
  • NIST SP 800-131A Rev. 2 — retira AES-128 para casos de uso "de transición"

Migra simétrico primero. No cuesta nada. El trabajo PQ caro (asimétrico, firmas, KEMs) merece tu atención después.

// related ops

OPS RELACIONADAS