FIPS 203, 204 y 205: ML-KEM, ML-DSA y SLH-DSA explicados y probados con OpenSSL
Índice de contenidos
- Puntos clave
- Qué son FIPS 203, 204 y 205 y qué sustituye cada uno
- Qué significan las categorías de seguridad 1, 2, 3 y 5
- ML-KEM (FIPS 203): parámetros y tamaños
- ML-DSA (FIPS 204): parámetros y tamaños
- SLH-DSA (FIPS 205): firmas grandes con supuestos mínimos
- Qué falta por llegar: FN-DSA (FIPS 206), HQC y más firmas
- Cuándo dejan de valer RSA y ECC según NIST IR 8547
- Práctica: ML-KEM, ML-DSA y SLH-DSA con OpenSSL 3.5 en Docker
- Preparar el contenedor y listar los algoritmos
- Encapsular y decapsular un secreto con ML-KEM
- Firmar y verificar con ML-DSA y SLH-DSA
- Tamaños medidos frente a la especificación
- El grupo híbrido X25519MLKEM768 en un handshake TLS 1.3
- mlkem768x25519-sha256 y firmas ML-DSA en OpenSSH
- Preguntas frecuentes
- ¿Kyber y ML-KEM son lo mismo?
- ¿Qué parámetros elijo para empezar?
- ¿Tengo que migrar ya si el ordenador cuántico no existe?
- Conclusión
- Fuentes
FIPS 203 (ML-KEM) define cómo acordar una clave secreta, y FIPS 204 (ML-DSA) y FIPS 205 (SLH-DSA) cómo firmar, los tres resistentes a un ordenador cuántico y definitivos desde agosto de 2024. Con OpenSSL 3.5.9 medí sus tamaños: una clave pública ML-KEM-768 ocupa 1184 bytes y una firma SLH-DSA-128s, 7856.
FIPS 203, FIPS 204 y FIPS 205 son los tres estándares de criptografía post-cuántica que NIST aprobó el 13 de agosto de 2024. FIPS 203 define ML-KEM, un mecanismo para acordar una clave secreta; FIPS 204 y FIPS 205 definen ML-DSA y SLH-DSA, dos esquemas de firma digital. Aquí tienes para qué sirve cada uno, qué significan sus conjuntos de parámetros, cuánto ocupan sus claves y firmas y qué calendario propone NIST para retirar RSA y las curvas elípticas. Después lo pruebo todo con OpenSSL 3.5.9 en Docker: claves, firmas, encapsulado, un handshake TLS 1.3 híbrido y OpenSSH. Tienes también la versión en inglés de esta guía.
Puntos clave
- FIPS 203 (ML-KEM, antes CRYSTALS-Kyber) sustituye al intercambio de claves con RSA o ECDH; FIPS 204 (ML-DSA, antes Dilithium) y FIPS 205 (SLH-DSA, antes SPHINCS+) sustituyen a las firmas RSA, ECDSA y EdDSA.
- Cada estándar tiene conjuntos de parámetros ligados a categorías de seguridad de NIST: la 1 equivale a romper AES-128, la 3 a AES-192 y la 5 a AES-256.
- Los tamaños crecen mucho: una clave pública ML-KEM-768 ocupa 1184 bytes frente a 32 de X25519, y una firma SLH-DSA va de 7856 a 49 856 bytes.
- El borrador NIST IR 8547 propone declarar obsoletos RSA y ECC de 112 bits después de 2030 y prohibir todo RSA y ECC después de 2035.
- OpenSSL 3.5 trae los tres algoritmos y ofrece por defecto el grupo híbrido X25519MLKEM768 en TLS 1.3; en mi prueba el ClientHello pasó de 281 a 1515 bytes.
- A 30 de septiembre de 2026, FN-DSA (FIPS 206) y HQC siguen sin borrador publicado en la página del proyecto PQC de NIST.
Qué son FIPS 203, 204 y 205 y qué sustituye cada uno
Los tres estándares cubren las dos operaciones de clave pública que un ordenador cuántico grande rompería: acordar un secreto y firmar. El algoritmo de Shor factoriza enteros y resuelve logaritmos discretos en tiempo polinómico, y eso tumba RSA, Diffie-Hellman y las curvas elípticas. Los algoritmos nuevos se basan en problemas para los que no se conoce ataque cuántico eficiente. El contexto histórico del concurso está en el artículo sobre los estándares NIST PQC.
La distinción que más confunde es la de KEM frente a firma. Un mecanismo de encapsulado de claves (KEM) permite que dos partes compartan un secreto de 32 bytes. Una publica su clave de encapsulado; la otra genera el secreto y lo envía dentro de un texto cifrado que solo la clave de decapsulado abre. Una firma digital hace otra cosa: demuestra quién emitió un mensaje y que nadie lo alteró.
| Estándar | Algoritmo | Basado en | Función | Sustituye a |
|---|---|---|---|---|
| FIPS 203 | ML-KEM | CRYSTALS-Kyber | Encapsulado de claves | RSA-KEM, DH, ECDH |
| FIPS 204 | ML-DSA | CRYSTALS-Dilithium | Firma digital | RSA, ECDSA, EdDSA |
| FIPS 205 | SLH-DSA | SPHINCS+ | Firma digital (hash) | RSA, ECDSA, EdDSA |
| FIPS 206 (borrador pendiente) | FN-DSA | Falcon | Firma digital | RSA, ECDSA, EdDSA |
ML-KEM y ML-DSA se apoyan en retículos modulares (el problema module learning with errors). SLH-DSA solo depende de la seguridad de una función hash, y por eso NIST lo mantiene como alternativa si un día se rompen los retículos.
Qué significan las categorías de seguridad 1, 2, 3 y 5
NIST no describe la seguridad post-cuántica con un número de bits, sino con cinco categorías definidas por un ataque de referencia. El borrador NIST IR 8547 las resume así:
| Categoría | Ataque de referencia | Ejemplo |
|---|---|---|
| 1 | Búsqueda de clave en un cifrado de bloque de 128 bits | AES-128 |
| 2 | Búsqueda de colisiones en un hash de 256 bits | SHA-256 |
| 3 | Búsqueda de clave en un cifrado de bloque de 192 bits | AES-192 |
| 4 | Búsqueda de colisiones en un hash de 384 bits | SHA3-384 |
| 5 | Búsqueda de clave en un cifrado de bloque de 256 bits | AES-256 |
Un parámetro de categoría 3 promete que romperlo cuesta al menos tanto como buscar por fuerza bruta una clave AES-192, con cualquier modelo de computación realista. El sufijo de cada nombre sigue esta lógica: ML-KEM-512 es categoría 1, ML-KEM-768 categoría 3 y ML-KEM-1024 categoría 5.
ML-KEM (FIPS 203): parámetros y tamaños
ML-KEM tiene tres conjuntos de parámetros, y los tres producen el mismo secreto compartido de 32 bytes. Lo que cambia es el tamaño de la clave y del texto cifrado que viaja por la red, según la tabla 3 de FIPS 203:
| Parámetros | Categoría | Clave de encapsulado | Clave de decapsulado | Texto cifrado |
|---|---|---|---|---|
| ML-KEM-512 | 1 | 800 bytes | 1632 bytes | 768 bytes |
| ML-KEM-768 | 3 | 1184 bytes | 2400 bytes | 1088 bytes |
| ML-KEM-1024 | 5 | 1568 bytes | 3168 bytes | 1568 bytes |
ML-KEM-768 es el que usan TLS y OpenSSH en sus grupos híbridos. FIPS 203 da un criterio general para elegir: "When initially establishing cryptographic protections for data, the strongest possible parameter set should be used". El mismo párrafo matiza que algunos parámetros pueden penalizar el rendimiento, así que la elección real mezcla seguridad y ancho de banda.
Un detalle que conviene conocer antes de programar con ML-KEM es el rechazo implícito. Si el texto cifrado llega alterado, la decapsulación no devuelve un error: devuelve un secreto distinto, calculado a partir del texto cifrado y de un valor aleatorio z guardado en la clave privada. El fallo aparece después, cuando las dos partes no consiguen descifrar nada con claves diferentes. Lo compruebo en la parte práctica.
ML-DSA (FIPS 204): parámetros y tamaños
ML-DSA es la firma de uso general de la familia: claves y firmas de pocos kilobytes y una implementación que NIST describe como sencilla. El nombre de cada conjunto indica las dimensiones de la matriz interna, no los bits de seguridad, y por eso ML-DSA-44 es categoría 2 y no categoría 1:
| Parámetros | Categoría | Clave privada | Clave pública | Firma |
|---|---|---|---|---|
| ML-DSA-44 | 2 | 2560 bytes | 1312 bytes | 2420 bytes |
| ML-DSA-65 | 3 | 4032 bytes | 1952 bytes | 3309 bytes |
| ML-DSA-87 | 5 | 4896 bytes | 2592 bytes | 4627 bytes |
Como comparación, en el mismo contenedor una firma Ed25519 ocupó 64 bytes y su clave pública 32. Una cadena de certificados X.509 con ML-DSA multiplica ese peso en cada handshake. FIPS 204 también permite guardar solo una semilla de 32 bytes como clave privada y regenerar el resto al firmar.
SLH-DSA (FIPS 205): firmas grandes con supuestos mínimos
SLH-DSA cambia tamaño de firma por confianza en los supuestos: su seguridad depende solo de SHA-2 o SHAKE. Sus claves públicas son diminutas (32, 48 o 64 bytes) y sus firmas, enormes. FIPS 205 define 12 conjuntos, combinando tres niveles, dos familias de hash (SHA2 y SHAKE) y dos variantes: s, de firma pequeña, y f, de firma rápida.
| Parámetros | Categoría | Clave pública | Firma |
|---|---|---|---|
| SLH-DSA-*-128s | 1 | 32 bytes | 7856 bytes |
| SLH-DSA-*-128f | 1 | 32 bytes | 17 088 bytes |
| SLH-DSA-*-192s | 3 | 48 bytes | 16 224 bytes |
| SLH-DSA-*-192f | 3 | 48 bytes | 35 664 bytes |
| SLH-DSA-*-256s | 5 | 64 bytes | 29 792 bytes |
| SLH-DSA-*-256f | 5 | 64 bytes | 49 856 bytes |
El asterisco vale SHA2 o SHAKE, con los mismos tamaños. Cada clave admite hasta 264 firmas; la nota de FIPS 205 calcula que a 10 000 millones de firmas por segundo harían falta más de 58 años para agotarla. SLH-DSA encaja en firmas de firmware, raíces de confianza o documentos de larga vida, donde se firma poco y se verifica durante décadas. En abril de 2026 NIST publicó el borrador SP 800-230 con conjuntos extra de SLH-DSA para usos que firman un número limitado de mensajes.
Qué falta por llegar: FN-DSA (FIPS 206), HQC y más firmas
La familia no está cerrada, y a 30 de septiembre de 2026 hay tres piezas en camino:
- FN-DSA (FIPS 206): la firma basada en Falcon, con firmas más cortas y verificación rápida, pero una implementación más delicada. La presentación de NIST en el taller MPTS 2026 la describe como "Under development (hopefully soon)" y la página de noticias del proyecto PQC no lista todavía un borrador publicado
- HQC: un segundo KEM basado en códigos correctores, no en retículos, elegido el 11 de marzo de 2025 (NIST IR 8545) como respaldo de ML-KEM. Su borrador de estándar figura como "underway" en la misma presentación
- Firmas adicionales: el 14 de mayo de 2026, NIST IR 8610 pasó nueve candidatos a la tercera ronda del proceso de firmas adicionales, buscando esquemas que no dependan de retículos estructurados
Nada de esto cambia lo que puedes desplegar hoy. ML-KEM, ML-DSA y SLH-DSA son estándares finales, tienen validación FIPS 140 en marcha y ya están en OpenSSL y OpenSSH.
Cuándo dejan de valer RSA y ECC según NIST IR 8547
NIST IR 8547, publicado como borrador en noviembre de 2024, propone el calendario para retirar los algoritmos vulnerables. Hoy sigue siendo un borrador (la página de CSRC lo lista como Initial Public Draft), así que las fechas pueden moverse, pero es el único calendario oficial de NIST sobre la mesa:
| Algoritmos | Seguridad | Propuesta |
|---|---|---|
| RSA, ECDSA, EdDSA (firmas) | 112 bits | Obsoletos después de 2030, prohibidos después de 2035 |
| RSA, ECDSA, EdDSA (firmas) | 128 bits o más | Prohibidos después de 2035 |
| DH, ECDH, MQV, RSA (claves) | 112 bits | Obsoletos después de 2030, prohibidos después de 2035 |
| DH, ECDH, MQV, RSA (claves) | 128 bits o más | Prohibidos después de 2035 |
Según la tabla 2 de SP 800-57, RSA de 2048 bits y las curvas de 224 a 255 bits dan 112 bits de seguridad; RSA de 3072 bits y P-256 dan 128. El objetivo de 2035 viene del memorando de seguridad nacional NSM-10 de Estados Unidos. Los algoritmos simétricos aprobados con al menos 128 bits, como AES-128 o SHA-256, siguen valiendo. Cómo priorizar la migración está en el artículo sobre qué hacer con los estándares finales de NIST.
Práctica: ML-KEM, ML-DSA y SLH-DSA con OpenSSL 3.5 en Docker
OpenSSL 3.5 fue la primera versión con los tres algoritmos, y es la versión de soporte largo hasta el 8 de abril de 2030. Probé todo el 30 de septiembre de 2026 en un contenedor Alpine 3.23.6 sobre Linux arm64, con OpenSSL 3.5.9 (publicada el 29 de septiembre) y OpenSSH 10.2p1.
Preparar el contenedor y listar los algoritmos
Arranca un contenedor Alpine, instala OpenSSL y OpenSSH y comprueba la versión. Luego lista los KEM disponibles:
docker run --rm -it mirror.gcr.io/library/alpine:3.23 sh
apk add --no-cache openssl openssh
openssl version
openssl list -kem-algorithms | grep -i mlkem
La salida confirma la versión y muestra los tres ML-KEM puros y cuatro grupos híbridos:
OpenSSL 3.5.9 29 Sep 2026 (Library: OpenSSL 3.5.9 29 Sep 2026)
{ 2.16.840.1.101.3.4.4.1, id-alg-ml-kem-512, ML-KEM-512, MLKEM512 } @ default
{ 2.16.840.1.101.3.4.4.2, id-alg-ml-kem-768, ML-KEM-768, MLKEM768 } @ default
{ 2.16.840.1.101.3.4.4.3, id-alg-ml-kem-1024, ML-KEM-1024, MLKEM1024 } @ default
X25519MLKEM768 @ default
X448MLKEM1024 @ default
SecP256r1MLKEM768 @ default
SecP384r1MLKEM1024 @ default
openssl list -signature-algorithms muestra de la misma forma los tres ML-DSA y los doce SLH-DSA, todos en el proveedor default.
Encapsular y decapsular un secreto con ML-KEM
El flujo de un KEM son cuatro pasos: generar la clave, publicar la parte pública, encapsular con ella y decapsular con la privada. Con openssl pkeyutl queda así:
openssl genpkey -algorithm ML-KEM-768 -out kem.key
openssl pkey -in kem.key -pubout -out kem.pub
openssl pkeyutl -encap -pubin -inkey kem.pub \
-out kem.ct -secret emisor.ss
openssl pkeyutl -decap -inkey kem.key -in kem.ct -secret receptor.ss
cmp emisor.ss receptor.ss && echo "mismo secreto"
wc -c kem.ct emisor.ss
El emisor obtiene emisor.ss y un texto cifrado de 1088 bytes; el receptor recupera el mismo secreto de 32 bytes. Luego cambié un solo byte del texto cifrado y repetí la decapsulación: openssl terminó con código 0 y devolvió un secreto de 32 bytes distinto (1666f3ae… frente a 5863fcc4…). Es el rechazo implícito de FIPS 203 en acción, y la razón por la que tu protocolo debe confirmar la clave antes de fiarse de ella.
Firmar y verificar con ML-DSA y SLH-DSA
Las firmas post-cuánticas se usan con -rawin, porque el algoritmo procesa el mensaje completo en lugar de un resumen calculado aparte:
openssl genpkey -algorithm ML-DSA-65 -out firma.key
openssl pkey -in firma.key -pubout -out firma.pub
openssl pkeyutl -sign -rawin -inkey firma.key \
-in msg.txt -out msg.sig
openssl pkeyutl -verify -rawin -pubin -inkey firma.pub \
-in msg.txt -sigfile msg.sig
wc -c msg.sig
La verificación imprime Signature Verified Successfully y la firma ocupa 3309 bytes. Con un mensaje alterado, openssl sale con código 1 y el error Signature Verification Failure. Para SLH-DSA basta cambiar el nombre del algoritmo, por ejemplo SLH-DSA-SHA2-128s.
Tamaños medidos frente a la especificación
Repetí el flujo con los tres ML-KEM, los tres ML-DSA y cuatro SLH-DSA, y medí la clave pública dentro de su estructura SPKI, el texto cifrado y la firma. Todos los valores coinciden byte a byte con las tablas de FIPS 203, 204 y 205:
La clave privada en PKCS#8 pesa más de lo que dice la tabla, y tiene explicación. OpenSSL 3.5 guarda por defecto la semilla y la clave expandida juntas (el formato seed-priv de su documentación). Una clave ML-KEM-768 ocupó 2498 bytes en DER: la semilla (d, z) de 64 bytes, la clave de decapsulado de 2400 y la cabecera ASN.1. En SLH-DSA-128s la clave privada entera ocupa 84 bytes.
El grupo híbrido X25519MLKEM768 en un handshake TLS 1.3
OpenSSL 3.5 ofrece por defecto dos key shares en TLS 1.3, X25519MLKEM768 y X25519, así que un cliente y un servidor actualizados negocian el híbrido sin configurar nada. El grupo combina ML-KEM-768 con X25519: el secreto es seguro mientras resista cualquiera de los dos. Para verlo, levanté un servidor con un certificado ML-DSA-65 autofirmado y me conecté con s_client:
openssl req -x509 -newkey ML-DSA-65 -keyout srv.key -out srv.crt \
-days 30 -nodes -subj "/CN=pqc.lab"
openssl s_server -accept 4433 -cert srv.crt -key srv.key -www -quiet &
echo | openssl s_client -connect 127.0.0.1:4433 -brief
Estas son las líneas relevantes del resumen de -brief, con las dos piezas post-cuánticas del handshake, el acuerdo de claves y la firma del servidor:
Protocol version: TLSv1.3
Ciphersuite: TLS_AES_256_GCM_SHA384
Signature type: mldsa65
Negotiated TLS1.3 group: X25519MLKEM768
Con -trace en lugar de -brief se ven los tamaños de cada mensaje. Estos son los que medí, contra un servidor con certificado P-256 para aislar el efecto del grupo, y contra el de ML-DSA-65 para ver el del certificado:
El key share del cliente son 1184 bytes de clave ML-KEM más 32 de X25519, y el del servidor, 1088 de texto cifrado más 32. Con los grupos por defecto, el cliente además envía un key share X25519 aparte, por si el servidor no conoce el híbrido. Si fuerzas en el cliente -groups MLKEM768 o SecP256r1MLKEM768, un servidor OpenSSL 3.5 con su configuración por defecto corta con alert handshake failure (alerta 40), porque ninguno de los dos está en su lista por defecto. La adopción real de estos grupos en la web está en el artículo sobre criptografía post-cuántica en TLS.
mlkem768x25519-sha256 y firmas ML-DSA en OpenSSH
OpenSSH usa el mismo KEM con otro nombre, mlkem768x25519-sha256, y desde OpenSSH 10.0 (9 de abril de 2025) es el intercambio de claves por defecto. Con OpenSSH 10.2p1 en el contenedor, una conexión a localhost lo confirma:
ssh-keygen -A
/usr/sbin/sshd
ssh -v -o BatchMode=yes localhost true 2>&1 | grep "kex: algorithm"
La línea de depuración muestra kex: algorithm: mlkem768x25519-sha256. Las firmas son otro asunto: la clave de host siguió siendo ssh-ed25519.
OpenSSH 10.3 añadió soporte experimental para una firma compuesta ML-DSA-44 más Ed25519, desactivada por defecto. La probé en Alpine edge con OpenSSH 10.5p1: ssh-keygen -t mldsa44-ed25519 generó una clave pública de 1895 bytes (99 en Ed25519), y con ella como clave de host y de usuario la sesión autenticó con kex: algorithm: mlkem768x25519-sha256 y host key algorithm: ssh-mldsa44-ed25519@openssh.com. La configuración paso a paso para servidores está en la guía para migrar SSH a criptografía post-cuántica.
Preguntas frecuentes
¿Kyber y ML-KEM son lo mismo?
No exactamente. ML-KEM es la versión estandarizada de CRYSTALS-Kyber, y el apéndice C de FIPS 203 enumera los cambios que hacen que, con las mismas entradas, produzca salidas distintas a las de Kyber. Por eso TLS pasó del grupo X25519Kyber768 al X25519MLKEM768. Lo mismo vale para Dilithium frente a ML-DSA y SPHINCS+ frente a SLH-DSA.
¿Qué parámetros elijo para empezar?
ML-KEM-768 para acuerdo de claves, porque es el que usan los grupos híbridos de TLS y OpenSSH. Para firmas, ML-DSA-65 cubre la categoría 3 con firmas de 3309 bytes; SLH-DSA encaja donde firmas poco y quieres depender solo de un hash. FIPS 203 recomienda el parámetro más fuerte que tu sistema admita.
¿Tengo que migrar ya si el ordenador cuántico no existe?
El acuerdo de claves, sí. Un atacante puede guardar hoy tráfico cifrado y descifrarlo cuando exista la máquina, así que el KEM híbrido protege datos que ya estás enviando. Las firmas solo hay que cambiarlas antes de que el ataque sea posible, y el borrador IR 8547 marca 2035 como límite.
Conclusión
FIPS 203 resuelve el acuerdo de claves con ML-KEM, y FIPS 204 y FIPS 205 resuelven la firma con ML-DSA y SLH-DSA. Los tres son definitivos desde agosto de 2024 y ya funcionan en software de uso diario. En mi prueba con OpenSSL 3.5.9 todos los tamaños coincidieron con los documentos de NIST. El coste real está en el ancho de banda: un ClientHello de 1515 bytes y un certificado ML-DSA-65 de 5506.
El plan que sale de aquí tiene tres pasos:
- comprueba que tus servidores TLS y SSH usan OpenSSL 3.5 u OpenSSH 10 o posteriores, que negocian el híbrido por defecto
- verifica el grupo negociado con
openssl s_client -briefy el intercambio SSH conssh -v - haz inventario de dónde firmas con RSA o ECDSA, porque esa migración es la lenta y el borrador de NIST la cierra en 2035
El estado de la adopción está en el artículo sobre la migración post-cuántica en la práctica.
Fuentes: [1] FIPS 203, Module-Lattice-Based Key-Encapsulation Mechanism Standard[1], [2] FIPS 204, Module-Lattice-Based Digital Signature Standard[2], [3] FIPS 205, Stateless Hash-Based Digital Signature Standard[3], [4] NIST IR 8547 ipd, Transition to Post-Quantum Cryptography Standards[4], [5] Proyecto Post-Quantum Cryptography de NIST CSRC[5], [6] The NIST PQC Project, MPTS 2026[6], [7] NEWS de OpenSSL 3.5[7], [8] Estrategia de versiones de OpenSSL[8], [9] Notas de versión de OpenSSH[9], [10] NIST SP 800-57 Part 1 Rev. 5[10].
Fuentes
- FIPS 203, Module-Lattice-Based Key-Encapsulation Mechanism Standard
- FIPS 204, Module-Lattice-Based Digital Signature Standard
- FIPS 205, Stateless Hash-Based Digital Signature Standard
- NIST IR 8547 ipd, Transition to Post-Quantum Cryptography Standards
- Proyecto Post-Quantum Cryptography de NIST CSRC
- The NIST PQC Project, MPTS 2026
- NEWS de OpenSSL 3.5
- Estrategia de versiones de OpenSSL
- Notas de versión de OpenSSH
- NIST SP 800-57 Part 1 Rev. 5