Esta traducción fue generada mediante aprendizaje automático y puede no ser 100% precisa. Ver versión en inglés

PQ Hybrid NTCP2

Variante híbrida post-cuántica del protocolo de transporte NTCP2 usando ML-KEM

Estado

Beta Q1 2026, lanzamiento Q2 2026

Descripción general

Esta es la variante híbrida post-cuántica del protocolo de transporte NTCP2, tal como se diseñó en la Propuesta 169. Consulte dicha propuesta para obtener información adicional de contexto.

PQ Hybrid NTCP2 solo se define en la misma dirección y puerto que el NTCP2 estándar. No está permitido operar en un puerto diferente ni sin soporte de NTCP2 estándar, y esto no cambiará durante varios años, hasta que el NTCP2 estándar sea obsoleto.

Esta especificación documenta únicamente los cambios necesarios en el NTCP2 estándar para soportar PQ Hybrid. Consulte la especificación de NTCP2 para los detalles de implementación de referencia.

Diseño

Admitimos los estándares NIST FIPS 203 y 204 FIPS 203 FIPS 204 , los cuales están basados en CRYSTALS-Kyber y CRYSTALS-Dilithium (versiones 3.1, 3 y anteriores), pero NO son compatibles con ellos.

Intercambio de claves

PQ KEM proporciona únicamente claves efímeras y no admite directamente handshakes de clave estática como Noise XK e IK. Los tipos de cifrado son los mismos que se utilizan en PQ Hybrid Ratchet y están definidos en el documento de estructuras comunes /docs/specs/common-structures/ ; al igual que en FIPS 203 , los tipos híbridos solo se definen en combinación con X25519.

Los tipos de cifrado son:

TypeCodeNTCP2 Version
MLKEM512_X2551953
MLKEM768_X2551964
MLKEM1024_X2551975
### Combinaciones Legales

Los nuevos tipos de cifrado se indican en los RouterAddresses. El tipo de cifrado en el certificado de clave continuará siendo el tipo 4.

Especificación

Patrones de protocolo de enlace

Los handshakes (intercambios de autenticación) utilizan patrones de handshake del Protocolo Noise .

Se utiliza la siguiente correspondencia de letras:

  • e = clave efímera de un solo uso
  • s = clave estática
  • p = carga útil del mensaje
  • e1 = clave PQ efímera de un solo uso, enviada de Alice a Bob
  • ekem1 = el texto cifrado KEM, enviado de Bob a Alice

Las siguientes modificaciones a XK e IK para la confidencialidad directa híbrida (hfs) son las especificadas en la sección 5 de Noise HFS spec :

XK:                       XKhfs:
  <- s                      <- s
  ...                       ...
  -> e, es, p               -> e, es, e1, p
  <- e, ee, p               <- e, ee, ekem1, p
  -> s, se                  -> s, se
  <- p                      <- p
  p ->                      p ->


  e1 and ekem1 are encrypted. See pattern definitions below.
  NOTE: e1 and ekem1 are different sizes (unlike X25519)

El patrón e1 se define de la siguiente manera, según lo especificado en la sección 4 de la especificación Noise HFS :

For Alice:
  (encap_key, decap_key) = PQ_KEYGEN()

  // EncryptAndHash(encap_key)
  ciphertext = ENCRYPT(k, n, encap_key, ad)
  n++
  MixHash(ciphertext)

  For Bob:

  // DecryptAndHash(ciphertext)
  encap_key = DECRYPT(k, n, ciphertext, ad)
  n++
  MixHash(ciphertext)

El patrón ekem1 se define de la siguiente manera, tal como se especifica en la sección 4 de Noise HFS spec :

For Bob:

  (kem_ciphertext, kem_shared_key) = ENCAPS(encap_key)

  // EncryptAndHash(kem_ciphertext)
  ciphertext = ENCRYPT(k, n, kem_ciphertext, ad)
  MixHash(ciphertext)

  // MixKey
  MixKey(kem_shared_key)


  For Alice:

  // DecryptAndHash(ciphertext)
  kem_ciphertext = DECRYPT(k, n, ciphertext, ad)
  MixHash(ciphertext)

  // MixKey
  kem_shared_key = DECAPS(kem_ciphertext, decap_key)
  MixKey(kem_shared_key)

KDF del protocolo de handshake Noise

Descripción general

El handshake híbrido se define en la especificación Noise HFS . El primer mensaje, de Alice a Bob, contiene e1, la clave de encapsulación, antes del payload del mensaje. Esto se trata como una clave estática adicional; se llama a EncryptAndHash() (como Alice) o DecryptAndHash() (como Bob). Luego se procesa el payload del mensaje de forma habitual.

El segundo mensaje, de Bob a Alice, contiene ekem1, el texto cifrado, antes del payload del mensaje. Este se trata como una clave estática adicional; se llama a EncryptAndHash() sobre él (como Bob) o DecryptAndHash() (como Alice). Luego, se calcula el kem_shared_key y se llama a MixKey(kem_shared_key). A continuación, se procesa el payload del mensaje de la manera habitual.

Operaciones ML-KEM Definidas

Definimos las siguientes funciones correspondientes a los bloques de construcción criptográficos utilizados según lo definido en FIPS 203 .

(encap_key, decap_key) = PQ_KEYGEN()

Alice creates the encapsulation and decapsulation keys
The encapsulation key is sent in message 1.
encap_key and decap_key sizes vary based on ML-KEM variant.

(ciphertext, kem_shared_key) = ENCAPS(encap_key)

Bob calculates the ciphertext and shared key,
using the ciphertext received in message 1.
The ciphertext is sent in message 2.
ciphertext size varies based on ML-KEM variant.
The kem_shared_key is always 32 bytes.

kem_shared_key = DECAPS(ciphertext, decap_key)

Alice calculates the shared key,
using the ciphertext received in message 2.
The kem_shared_key is always 32 bytes.

Tenga en cuenta que tanto la encap_key como el texto cifrado están cifrados dentro de bloques ChaCha/Poly en los mensajes 1 y 2 del protocolo de enlace Noise. Serán descifrados como parte del proceso de protocolo de enlace.

El kem_shared_key se mezcla en la clave de encadenamiento con MixHash(). Consulte a continuación para obtener más detalles.

KDF de Alice para el Mensaje 1

Después del patrón de mensaje ’es’ y antes del payload, añadir:

This is the "e1" message pattern:
  (encap_key, decap_key) = PQ_KEYGEN()

  // EncryptAndHash(encap_key)
  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, encap_key, ad)
  n++

  // MixHash(ciphertext)
  h = SHA256(h || ciphertext)


  End of "e1" message pattern.

  NOTE: For the next section (payload for XK or static key for IK),
  the keydata and chain key remain the same,
  and n now equals 1 (instead of 0 for non-hybrid).

KDF de Bob para el Mensaje 1

Después del patrón de mensaje ’es’ y antes del payload, añadir:

This is the "e1" message pattern:

  // DecryptAndHash(encap_key_section)
  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  encap_key = DECRYPT(k, n, encap_key_section, ad)
  n++

  // MixHash(encap_key_section)
  h = SHA256(h || encap_key_section)

  End of "e1" message pattern.

  NOTE: For the next section (payload for XK or static key for IK),
  the keydata and chain key remain the same,
  and n now equals 1 (instead of 0 for non-hybrid).

KDF de Bob para el Mensaje 2

Para XK: Después del patrón de mensaje ’ee’ y antes del payload, agregar:

This is the "ekem1" message pattern:

  (kem_ciphertext, kem_shared_key) = ENCAPS(encap_key)

  // EncryptAndHash(kem_ciphertext)
  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  ciphertext = ENCRYPT(k, n, kem_ciphertext, ad)

  // MixHash(ciphertext)
  h = SHA256(h || ciphertext)

  // MixKey(kem_shared_key)
  keydata = HKDF(chainKey, kem_shared_key, "", 64)
  chainKey = keydata[0:31]

  End of "ekem1" message pattern.

KDF de Alice para el Mensaje 2

Después del patrón de mensaje ’ee’, añadir:

This is the "ekem1" message pattern:

  // DecryptAndHash(kem_ciphertext_section)
  // AEAD parameters
  k = keydata[32:63]
  n = 0
  ad = h
  kem_ciphertext = DECRYPT(k, n, kem_ciphertext_section, ad)

  // MixHash(kem_ciphertext_section)
  h = SHA256(h || kem_ciphertext_section)

  // MixKey(kem_shared_key)
  kem_shared_key = DECAPS(kem_ciphertext, decap_key)
  keydata = HKDF(chainKey, kem_shared_key, "", 64)
  chainKey = keydata[0:31]

  End of "ekem1" message pattern.

KDF para el Mensaje 3 (solo XK)

sin cambios

KDF para split()

sin cambios

Detalles del protocolo de enlace

Identificadores de Noise

  • “Noise_XKhfsaesobfse+hs2+hs3_25519+MLKEM512_ChaChaPoly_SHA256”
  • “Noise_XKhfsaesobfse+hs2+hs3_25519+MLKEM768_ChaChaPoly_SHA256”
  • “Noise_XKhfsaesobfse+hs2+hs3_25519+MLKEM1024_ChaChaPoly_SHA256”

1) SessionRequest

Cambios: El NTCP2 actual contiene solo las opciones en la sección ChaCha. Con ML-KEM, la sección ChaCha también contendrá la clave pública PQ cifrada.

Para que PQ y no-PQ NTCP2 puedan ser compatibles en la misma dirección y puerto del router, utilizamos el bit más significativo del valor X (clave pública efímera X25519) para indicar que se trata de una conexión PQ. Este bit siempre está sin establecer en las conexiones no-PQ.

Para Alice, después de que el mensaje sea cifrado por Noise, pero antes de la ofuscación AES de X, establezca X[31] |= 0x7f.

Para Bob, después de la des-ofuscación AES de X, comprobar X[31] & 0x80. Si el bit está establecido, borrarlo con X[31] &= 0x7f, y descifrar mediante Noise como una conexión PQ. Si el bit está vacío, descifrar mediante Noise como una conexión no PQ de la forma habitual.

Para PQ NTCP2 anunciado en una dirección de router y puerto diferente, esto no es necesario.

Para obtener información adicional, consulte la sección Direcciones publicadas a continuación.

Contenido sin procesar:

  +----+----+----+----+----+----+----+----+
  |        MS bit set to 1 and then       |
  +        obfuscated with RH_B           +
  |       AES-CBC-256 encrypted X         |
  +             (32 bytes)                +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |   ChaChaPoly frame (MLKEM)            |
  +      (see table below for length)     +
  |   k defined in KDF for message 1      |
  +   n = 0                               +
  |   see KDF for associated data         |
  ~   n = 0                               ~
  +----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |   ChaChaPoly frame (options)          |
  +         32 bytes                      +
  |   k defined in KDF for message 1      |
  +   n = 0                               +
  |   see KDF for associated data         |
  +----+----+----+----+----+----+----+----+
  |     unencrypted authenticated         |
  ~         padding (optional)            ~
  |     length defined in options block   |
  +----+----+----+----+----+----+----+----+

  Same as current specification except add a second ChaChaPoly frame

Datos sin cifrar (etiqueta de autenticación Poly1305 no mostrada):

  +----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |                   X                   |
  +              (32 bytes)               +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |           ML-KEM encap_key            |
  +      (see table below for length)     +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |               options                 |
  +              (16 bytes)               +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |     unencrypted authenticated         |
  +         padding (optional)            +
  |     length defined in options block   |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

Nota: el campo de versión en el bloque de opciones del mensaje 1 debe establecerse en 2, incluso para conexiones PQ.

Tamaños:

TypeType CodeX lenMsg 1 lenMsg 1 Enc lenMsg 1 Dec lenPQ key lenopt len
X2551943264+pad3216--16
MLKEM512_X25519532880+pad84881680016
MLKEM768_X255196321264+pad12321200118416
MLKEM1024_X255197321648+pad16161584156816
Nota: Los códigos de tipo son únicamente para uso interno. Los routers permanecerán como tipo 4, y el soporte se indicará en las direcciones del router.

2) SessionCreated

Contenido sin procesar:

  +----+----+----+----+----+----+----+----+
  |                                       |
  +        obfuscated with RH_B           +
  |       AES-CBC-256 encrypted Y         |
  +              (32 bytes)               +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |   ChaChaPoly frame (MLKEM)            |
  +   Encrypted and authenticated data    +
  -      (see table below for length)     -
  +   k defined in KDF for message 2      +
  |   n = 0; see KDF for associated data  |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |   ChaChaPoly frame (options)          |
  +   Encrypted and authenticated data    +
  -           32 bytes                    -
  +   k defined in KDF for message 2      +
  |   n = 0; see KDF for associated data  |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |     unencrypted authenticated         |
  +         padding (optional)            +
  |     length defined in options block   |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

  Same as current specification except add a second ChaChaPoly frame

Datos sin cifrar (etiqueta de autenticación Poly1305 no mostrada):

  +----+----+----+----+----+----+----+----+
  |                                       |
  +                                       +
  |                  Y                    |
  +              (32 bytes)               +
  |                                       |
  +                                       +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |           ML-KEM Ciphertext           |
  +      (see table below for length)     +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |               options                 |
  +              (16 bytes)               +
  |                                       |
  +----+----+----+----+----+----+----+----+
  |     unencrypted authenticated         |
  +         padding (optional)            +
  |     length defined in options block   |
  ~               .   .   .               ~
  |                                       |
  +----+----+----+----+----+----+----+----+

Tamaños:

TypeType CodeY lenMsg 2 lenMsg 2 Enc lenMsg 2 Dec lenPQ CT lenopt len
X2551943264+pad3216--16
MLKEM512_X25519532848+pad81678476816
MLKEM768_X255196321136+pad11041104108816
MLKEM1024_X255197321616+pad15841584156816
Nota: Los códigos de tipo son únicamente para uso interno. Los routers permanecerán como tipo 4, y el soporte se indicará en las direcciones del router.

3) SessionConfirmed

Sin cambios

Función de Derivación de Claves (KDF) (para la fase de datos)

Sin cambios

Direcciones publicadas

En todos los casos, utilice el nombre del transporte NTCP2 como de costumbre.

Utiliza la misma dirección/puerto que la variante sin PQ y sin firewall. Solo se admite una variante PQ. En la dirección del router, publica v=2 (como de costumbre) y el nuevo parámetro pq=[3|4|5] para indicar MLKEM 512/768/1024. Alice establece el MSB de la clave efímera (key[31] & 0x80) en la solicitud de sesión para indicar que se trata de una conexión híbrida. Ver más arriba. Los routers más antiguos ignorarán el parámetro pq y se conectarán sin PQ como de costumbre.

No está soportado tener una dirección/puerto diferente como no-PQ, o solo-PQ sin firewall. Esto no se implementará hasta que NTCP2 no-PQ sea deshabilitado, dentro de varios años. Cuando no-PQ sea deshabilitado, podrán soportarse múltiples variantes PQ, pero solo una por dirección. Cuando sea soportado, en la dirección del router se publicará v=[3|4|5] para indicar MLKEM 512/768/1024. Alice no establece el MSB de la clave efímera. Los routers más antiguos verificarán el parámetro v y omitirán esta dirección como no soportada.

Direcciones con firewall (sin IP publicada): En la dirección del router, publicar v=2 (como de costumbre). No es necesario publicar un parámetro pq.

Alice puede conectarse a un Bob PQ utilizando la variante PQ que Bob publica, independientemente de si Alice anuncia soporte PQ en su router info, o si anuncia la misma variante.

Relleno máximo

En la especificación actual, los mensajes 1 y 2 están definidos para tener una cantidad “razonable” de relleno, con un rango recomendado de 0 a 31 bytes y sin un máximo especificado.

A través de la API 0.9.68 (versión 2.11.0), Java I2P implementó un máximo de 256 bytes de relleno para conexiones no-PQ, aunque esto no estaba documentado anteriormente. A partir de la API 0.9.69 (versión 2.12.0), Java I2P implementa el mismo relleno máximo para conexiones no-PQ que para MLKEM-512. Véase la tabla a continuación.

Utilice el tamaño de mensaje definido como el relleno máximo, es decir, el relleno máximo duplicará el tamaño del mensaje para las conexiones PQ, de la siguiente manera:

Message Max Paddingnon-PQ (thru 0.9.68)non-PQ (as of 0.9.69)MLKEM-512MLKEM-768MLKEM-1024
Session Request25688088012641648
Session Created25684884811361616
## Análisis de sobrecarga

Intercambio de claves

Aumento de tamaño (bytes):

TypePubkey (Msg 1)Ciphertext (Msg 2)
MLKEM512_X25519+816+784
MLKEM768_X25519+1200+1104
MLKEM1024_X25519+1584+1584
## Análisis de Seguridad

Las categorías de seguridad NIST se resumen en la presentación NIST , diapositiva 10. Criterios preliminares: nuestra categoría de seguridad NIST mínima debería ser 2 para protocolos híbridos y 3 para protocolos exclusivamente poscuánticos (PQ-only).

CategoryAs Secure As
1AES128
2SHA256
3AES192
4SHA384
5AES256
### Handshakes

Todos estos son protocolos híbridos. Las implementaciones deben preferir MLKEM768; MLKEM512 no es suficientemente seguro.

Categorías de seguridad NIST FIPS 203 :

AlgorithmSecurity Category
MLKEM5121
MLKEM7683
MLKEM10245
## Notas de implementación

Soporte de biblioteca

Las bibliotecas Bouncycastle, BoringSSL y WolfSSL ya admiten MLKEM y MLDSA. El soporte de OpenSSL estará disponible en su versión 3.5 del 8 de abril de 2025 OpenSSL .

Identificación del tráfico entrante

Establecemos el MSB (bit más significativo) de la clave efímera (key[31] & 0x80) en la solicitud de sesión para indicar que se trata de una conexión híbrida. Esto nos permite ejecutar tanto NTCP estándar como NTCP híbrido en el mismo puerto. Solo se admite una variante híbrida para conexiones entrantes, la cual se anuncia en la dirección del router. Por ejemplo, pq=3 o pq=4.

Ofuscación

Como Alice, para una conexión PQ, antes de la ofuscación, establece X[31] |= 0x80. Esto convierte X en una clave pública X25519 no válida. Después de la ofuscación, AES-CBC la aleatorizará. El MSB de X será aleatorio después de la ofuscación.

Como Bob, verificar si (X[31] & 0x80) != 0 después de la de-ofuscación. Si es así, se trata de una conexión PQ.

La versión mínima del router requerida para NTCP2-PQ está por determinarse.

Nota: Los códigos de tipo son únicamente para uso interno. Los routers permanecerán como tipo 4, y el soporte se indicará en las direcciones del router.

Compatibilidad del Router

Nombres de Transporte

En todos los casos, utilice el nombre de transporte NTCP2 de la manera habitual. Los routers más antiguos ignorarán el parámetro pq y se conectarán con el NTCP2 estándar como de costumbre.

Referencias

Was this page helpful?