Problema
RSA y ECC caducan ante Shor. NIST IR 8547 sitúa la retirada en 2035; FIDO2 sigue anclado a ES256.
Caso de estudio · TFG 2026
Arquitectura de microservicios cuyo núcleo es un servidor Relying Party FIDO2 en Go que verifica firmas ML-DSA (FIPS 204) en las ceremonias WebAuthn.
$ fido2 verify --alg ML-DSA-65 --cose -49authData ‖ SHA-256(clientDataJSON) → σ 3 309 B✓ firma válida · verify 0.42 ms$ ▊
El proyecto
Los algoritmos de firma y establecimiento de claves vigentes —RSA, ECDSA y ECDH— resultan vulnerables frente a un ordenador cuántico de gran escala, como consecuencia del algoritmo de Shor [20]. Ante esta amenaza, el NIST publicó en agosto de 2024 los primeros estándares de criptografía post-cuántica: ML-KEM (FIPS 203) [1], ML-DSA (FIPS 204) [2] y SLH-DSA (FIPS 205) [3]. Esta web documenta la Fase 1: estándares, protocolo FIDO2/WebAuthn y COSE, arquitectura Zero Trust (NIST SP 800-207) [4], plan de trabajo y estructura de la memoria. El objetivo es implementar y evaluar un servidor Relying Party que verifique firmas ML-DSA en registro y autenticación WebAuthn, frente a criptografía tradicional.
Problema
RSA y ECC caducan ante Shor. NIST IR 8547 sitúa la retirada en 2035; FIDO2 sigue anclado a ES256.
Enfoque
RP FIDO2 en Go que verifica ML-DSA-65 (COSE −49) sobre microservicios con mTLS y privilegio mínimo.
Evidencia
Benchmark reproducible: ML-DSA-44/65/87 frente a ES256 — latencia p95, ops/s, bytes y CPU.
El 13 de agosto de 2024 el NIST publicó los tres primeros estándares finales de criptografía post-cuántica, culminando el proceso de estandarización abierto iniciado en 2016 [6]. Constituyen la base normativa de este proyecto.
ML-KEM — Module-Lattice-Based Key-Encapsulation Mechanism, derivado de CRYSTALS-Kyber [22]. Su seguridad se apoya en la dificultad del problema Module Learning With Errors. Sustituye a ECDH/DH en el establecimiento de claves simétricas y es el esquema recomendado por defecto [1].
ML-DSA — Module-Lattice-Based Digital Signature Algorithm, derivado de CRYSTALS-Dilithium [21], con construcción Fiat-Shamir with Aborts. Sustituye a RSA/ECDSA. Es el algoritmo central de este TFG: valida la firma de las aserciones WebAuthn [2].
SLH-DSA — Stateless Hash-Based Digital Signature Standard, derivado de SPHINCS+. Seguridad basada exclusivamente en funciones hash; firmas de gran tamaño (~7–8 KB). Se contempla como alternativa conservadora y para el firmado de firmware en la fase extendida [3]. El estándar FN-DSA (Falcon) permanece en desarrollo.
| Fecha | Hito |
|---|---|
| 2016 | Lanzamiento del proyecto de estandarización PQC del NIST (69 candidaturas). |
| 2022 | Selección de los algoritmos: Kyber, Dilithium, Falcon y SPHINCS+. |
| 13-08-2024 | Publicación final de FIPS 203, 204 y 205 (efectivos desde la misma fecha). |
| 2024–2030 | Periodo de transición; NIST IR 8547 declara RSA/ECC/DSA deprecated hacia 2030 [7]. |
| 2035 | Retirada completa de los algoritmos vulnerables a lo cuántico. CNSA 2.0 exige firmas PQC hacia 2030–2033. |
Un KEM permite que dos partes establezcan un secreto compartido de 256 bits sobre un canal público; dicho secreto alimenta primitivas simétricas (AES-GCM, ChaCha20) o un HKDF [1]. Interfaz: KeyGen → (ek, dk), Encaps(ek) → (K, c), Decaps(dk, c) → K.
q = 3329 y n = 256.| Conjunto | Cat. NIST | ek | dk | ciphertext | secreto |
|---|---|---|---|---|---|
| ML-KEM-512 | 1 (≈AES-128) | 800 | 1 632 | 768 | 32 |
| ML-KEM-768 | 3 (≈AES-192) | 1 184 | 2 400 | 1 088 | 32 |
| ML-KEM-1024 | 5 (≈AES-256) | 1 568 | 3 168 | 1 568 | 32 |
Parámetros: k = 2/3/4, η₁ = 2–3, η₂ = 2, du = 10–11, dv = 4–5. Referencia comparativa: una clave pública ECDH P-256 ocupa 65 bytes, de modo que ML-KEM-768 resulta ~18× mayor; su coste computacional, en cambio, es comparable o inferior al de X25519.
La amenaza «harvest now, decrypt later» aconseja hibridar desde el primer momento: se combina un intercambio clásico (X25519) con ML-KEM-768, de forma que la sesión permanece segura si al menos uno de los dos esquemas resiste [23]. Grupos TLS: X25519Kyber768Draft00 (0x6399, Go 1.23+) y el estándar X25519MLKEM768 (0x11EC, por defecto desde Go 1.24). Coste: el ClientHello crece ~1 216 bytes (clave pública de 1 184 B) y el ServerHello ~1 120 B (ciphertext de 1 088 B). OpenSSL ≥ 3.5 lo incorpora por omisión.
// Go: servidor con negociación híbrida post-cuántica
srv := &http.Server{
Addr: ":8443",
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
CurvePreferences: nil, // vacío ⇒ habilita X25519MLKEM768 por defecto
},
}Esquema de firma de retículos con seguridad SUF-CMA (strong existential unforgeability under chosen-message attack), basado en los problemas M-LWE y SelfTargetMSIS. Constituye el reemplazo de ECDSA/RSA y el núcleo criptográfico de este proyecto [2].
ML-DSA.KeyGen() — genera (pk, sk) a partir de la semilla ξ (32 B); la matriz  se expande con SHAKE-128.ML-DSA.Sign(sk, M, ctx, rnd) — firma σ. Rejillas y compresión (hint) para reducir tamaño; aleatoriedad externa opcional rnd (32 B) o modo determinista; contexto ctx ≤ 255 bytes para separación de dominios.ML-DSA.Verify(pk, M, σ, ctx) — verificación booleana de la ecuación Az − c·t̂ ≈ w₁·2ᵈ comprimida.HashML-DSA: variante pre-hash con dominio separado, prevista para módulos que ya calculan el hash del mensaje (HSM/TPM) con mensajes de gran tamaño.
Las claves admiten representación por semilla ξ; el precálculo de  acelera firma y verificación.
| Conjunto | Cat. | sk | pk | firma σ | COSE |
|---|---|---|---|---|---|
| ML-DSA-44 | 2 | 2 560 | 1 312 | 2 420 | −48 |
| ML-DSA-65 | 3 | 4 032 | 1 952 | 3 309 | −49 |
| ML-DSA-87 | 5 | 4 896 | 2 592 | 4 627 | −50 |
Comparativa: una firma ECDSA P-256 (ES256) ocupa ≈64 B y su clave pública 65 B; la firma ML-DSA-65 es, por tanto, ~52× mayor. Este sobrecoste es el principal obstáculo de integración en FIDO2 (tamaños CBOR, almacenamiento de credenciales, latencia).
El servidor RP recibirá authenticatorData ‖ SHA-256(clientDataJSON) como mensaje M y la firma σ del autenticador; la verificación se realizará con la biblioteca CIRCL [15]:
import "github.com/cloudflare/circl/sign/mldsa/mldsa65"
pk, _ := mldsa65.Scheme().UnmarshalBinaryPublicKey(pubKeyBytes)
ok := mldsa65.Scheme().Verify(pk, msg, sig, nil) // ctx opcional
if !ok { /* rechazar la aserción */ }Estado del ecosistema (septiembre de 2026): CIRCL implementa el estándar final FIPS 204 desde v1.5.0 [15]; crypto/mlkem figura en la biblioteca estándar de Go (1.24) y crypto/mldsa público está propuesto para Go 1.27; OpenSSL 3.5+, Node.js 24.5+ y Java 24 (JEP 497) ya operan con ML-DSA [23].
FIDO2 se compone de WebAuthn (W3C) y CTAP2 (FIDO Alliance) [8]. Proporciona autenticación sin contraseñas, resistente a phishing, con credenciales de clave pública generadas dentro del autenticador y que nunca abandonan el dispositivo.
+-----------+ WebAuthn JS (HTTPS) +---------+ CTAP2 (CBOR) +---------------+
| Relying | --------------------->| Cliente | -------------->| Autenticador |
| Party RP | <---------------------| (naveg.)| <--------------| (plataf./roam)|
+-----------+ attestation/assertion +---------+ USB·NFC·BLE +---------------+navigator.credentials.create() (registro) y .get() (acceso). El nivel 3 es ya Recomendación W3C (2026) y el nivel 4 está en elaboración [8].authenticatorMakeCredential y authenticatorGetAssertion.{fmt, authData, attStmt}. El RP verifica firma, origin, challenge y, opcionalmente, la cadena de atestación (FIDO MDS, AAGUID) [9].authData ‖ SHA-256(clientDataJSON) con la clave privada del credencial. El RP comprueba el contador (anti-clonación), los indicadores UP/UV, el origen y el challenge de un solo uso.clientDataJSON: {type, challenge, origin, crossOrigin}. authData: rpIdHash (32 B) ‖ flags (UP 0x01, UV 0x04, AT 0x40, ED 0x80) ‖ contador (4 B) ‖ [AAGUID 16 B ‖ L 2 B ‖ credId ‖ COSE_Key].
COSE (CBOR Object Signing and Encryption, RFC 9052/9053 [11]) es el formato binario compacto con el que WebAuthn transporta claves públicas y designa algoritmos; los identificadores empleados por WebAuthn se registran en RFC 8812 [10]. Un COSE_Key es un mapa CBOR: 1: kty, 3: alg, -1: crv, -2/-3: coordenadas.
| Nombre | Valor COSE | Descripción | Situación en WebAuthn |
|---|---|---|---|
| ES256 | −7 | ECDSA con SHA-256, curva P-256 | Requerido — estándar de facto actual |
| EdDSA | −8 | Ed25519 / Ed448 (OKP) | Recomendado |
| RS256 | −257 | RSASSA-PKCS1-v1_5 con SHA-256 | Requerido (legado) |
| PS256 | −37 | RSASSA-PSS con SHA-256 | Recomendado |
| ML-DSA-44 | −48 | FIPS 204, categoría 2 | Pendiente en IANA — borrador IETF [12] |
| ML-DSA-65 | −49 | FIPS 204, categoría 3 | Pendiente en IANA (valor solicitado −49) [12] |
| ML-DSA-87 | −50 | FIPS 204, categoría 5 | Pendiente en IANA (valor solicitado −50) [12] |
El Internet-Draft ML-DSA for Web Authentication [13] define la incorporación de ML-DSA (FIPS 204) a WebAuthn: únicamente la clave pública se codifica en COSE dentro de authData, y la firma ML-DSA sustituye a ECDSA tanto en el attStmt como en la aserción. El documento Server Requirements de FIDO2 v2.3 (review draft, 2026) ya referencia estos algoritmos [9].
Marco normativo de referencia: NIST SP 800-207 (Zero Trust Architecture, 2020) [4] y SP 800-207A (modelo ZTA para aplicaciones cloud-native y microservicios, 2023) [5], con la serie SP 800-204 para la seguridad en service meshes.
MVP: mTLS clásico interno con certificados de servicio de vida corta y CA propia. Fase extendida: hibridación X25519MLKEM768 en los mismos túneles.
| Servicio | Puerto (interno) | Responsabilidad | Dependencias |
|---|---|---|---|
| api-gateway | 443 → 8080 | Ingress TLS 1.3, PEP Zero Trust, limitación de tasa | todos |
| rp-fido2 | 8081 | Ceremonias WebAuthn: challenge, attestation, assertion | crypto-svc, users-svc, Redis |
| crypto-svc | 8082 | Verificación ML-DSA (CIRCL) y ES256 de control | — |
| users-svc | 8083 | Cuentas y almacén de credenciales (pk COSE, AAGUID, contador) | PostgreSQL |
| session-svc | 8084 | Challenges de un solo uso y sesiones con TTL | Redis |
| benchmark-svc | 8085 | Suite de pruebas de rendimiento PQC (p95, ops/s, bytes) | Grafana / OTLP |
| postgres | 5432 | Estado persistente | — |
| redis | 6379 | Estado efímero | — |
Principio de exposición: únicamente la pasarela publica puerto al host (443). Los demás servicios solo son alcanzables dentro de las redes Docker internas (net-edge, net-domain, net-data), cada una con su política de segmentación.
1. GET /login/begin → session-svc crea challenge (Redis, TTL 60 s)
2. Emulador: navigator.credentials.get({challenge, rpId})
→ CTAP2 getAssertion: firma ML-DSA-65 de
authData ‖ SHA256(clientDataJSON)
3. POST /login/finish → api-gateway (PEP, mTLS)
4. rp-fido2: valida origin, rpIdHash, flags (UP/UV), contador
5. crypto-svc: ML-DSA.Verify(pk_COSE(−49), M, σ) vía CIRCL
6. Si es correcto → sesión emitida (PA) · métricas → OTLP| Variable | ML-DSA-44 | ML-DSA-65 | ML-DSA-87 | ES256 |
|---|---|---|---|---|
| Firmas/s (sign) | experimental | experimental | experimental | control |
| Verificaciones/s | experimental | experimental | experimental | control |
| Latencia p50/p95 (ms) | medida | medida | medida | medida |
| Tamaño de aserción (B) | ≈2 700 | ≈3 600 | ≈4 900 | ≈180 |
| CPU/mem (Go pprof) | medida | medida | medida | medida |
Metodología: go test -bench junto a carga HTTP con k6/vegeta; tres ejecuciones por configuración, proceso repetible en CI. Los resultados conformarán el capítulo de evaluación de la memoria.
Planificación por fases con gestión de riesgos: el MVP es independiente de las extensiones, de modo que el fallo de una extensión no compromete ni el núcleo ni la memoria.
Esqueleto preparado para incorporar el marco teórico y la bibliografía desde el primer día. La versión detallada, con listas de comprobación por sección, se mantiene en el repositorio (memoria/esqueleto-memoria.md).
Fuentes primarias consultadas durante la Fase 1. Las referencias están numeradas y enlazadas desde el texto; la versión BibTeX se mantiene en el repositorio (memoria/bibliografia.bib).