HIP-0088: Session KEM (ML-KEM-768/1024 for P2P)
Abstract
HIP-0088 locks the post-quantum session KEM under the strict-PQ
profile. The ZAP wire handshake (HIP-0077) negotiates an ML-KEM-768
(FIPS 203, NIST PQ Cat 3) shared secret by default and ML-KEM-1024
(Cat 5) for high-value sessions. One KEM family is admitted and it is
ML-KEM; there is no hybrid mode to fall back to. The derived shared
secret is fed to an HKDF-Extract
keyed by KMAC256 (SP 800-185) and produces the AEAD key for
AES-256-GCM or ChaCha20-Poly1305 transport.
Motivation
Path 12 of the LUX_STRICT_E2E_PQ coverage matrix is partial: LP-022 (ZAP) is the wire protocol and LP-072 specifies ML-KEM as a primitive, but no spec mandates ML-KEM under strict-PQ at the handshake boundary. HIP-0077 references ML-KEM-768 informationally but does not lock the KEM-derived transport key path. Without HIP-0088, P2P session encryption can silently fall back to classical X25519, defeating the end-to-end PQ posture.
Specification
Canonical references:
luxfi/consensus/config/pq_mode.go—KeyExchangeIDenum (canonical wire bytes).luxfi/node/network/kem/scheme.go— ML-KEM session primitives (FIPS 203).luxfi/node/network/peer/handshake.go— handshake state machine.
The KEM auth/session_kem.go path named in earlier drafts does not exist;
session-KEM lives in node/network/kem/ (encapsulate/decapsulate +
shared-secret derivation) and node/network/peer/handshake.go (mutual
authentication transcript). Canonical wire bytes are sourced from
config.KeyExchangeID and shared with protocol/auth via type alias.
SessionInit (client → server):
profile_id uint8
kem_scheme_id uint8 // 0x01 ML-KEM-768 | 0x02 ML-KEM-1024
// 0x90 X25519Unsafe (forbidden under strict-PQ)
client_kem_pubkey []byte // 1184 B for 768, 1568 B for 1024
client_nonce [32]byte
client_mldsa_sig []byte // ML-DSA.Sign over (init_bytes)
SessionAccept (server → client):
server_id [48]byte // AccountID
kem_ciphertext []byte // 1088 B for 768, 1568 B for 1024
server_nonce [32]byte
server_mldsa_sig []byte // ML-DSA.Sign over (init_bytes || accept_bytes_no_sig)
shared_secret = ML-KEM.Decap(client_kem_secret, kem_ciphertext)
= ML-KEM.Encap(client_kem_pubkey).ss // identity
transcript = TupleHash256(
"SESSION-KEM-V1",
[ profile_id, kem_scheme_id, client_kem_pubkey, kem_ciphertext,
client_nonce, server_nonce, server_id ],
384
)
session_key = KMAC256(shared_secret,
transcript,
256, // 256-bit AEAD key
"LUX-SESSION-KDF-V1")
Acceptance under strict-PQ:
profile_idmatches chain pin.kem_scheme_id ∈ {0x01, 0x02};0x90(X25519Unsafe) rejected.- Both ML-DSA-65 signatures verify under unmodified FIPS 204 against the respective party's pubkey.
kem_scheme_id = 0x01(ML-KEM-768) is the default;0x02(ML-KEM-1024) is required when either side advertisesrole=high-value.- Session key is rotated every 1 hour or 2^28 records, whichever comes first.
Rationale
ML-KEM-768 is NIST PQ Cat 3, matching ML-DSA-65's identity scheme. ML-KEM-1024 is Cat 5 for high-value (governance, bridge, treasury) flows. A hybrid X25519 leg would add zero cryptanalytic surface while silently weakening the posture, so it is refused rather than offered as a way in. KMAC256 over the transcript matches the SP 800-185 key-derivation pattern used elsewhere in the strict-PQ stack.
Backwards compatibility
None. Classical key exchange (X25519, ECDH) is refused at the handshake boundary. A peer that cannot speak ML-KEM does not connect.
Reference implementation
ML-KEM primitive + session derivation: luxfi/node/network/kem/.
Handshake state machine: luxfi/node/network/peer/handshake.go.
Canonical wire byte registry: luxfi/consensus/config/pq_mode.go
(KeyExchangeID).
Security considerations
ML-KEM-768 provides IND-CCA2 security at NIST Cat 3 per FIPS 203.
Mutual ML-DSA-65 signatures over the handshake transcript bind
identity to the KEM key exchange, preventing unknown-key-share attacks
and identity misbinding. Session key rotation bounds the volume of
data protected by any single derived key. The KMAC256 KDF cust string
LUX-SESSION-KDF-V1 provides domain separation against other KDF
paths in the strict-PQ stack.
References
- NIST FIPS 203 — ML-KEM primitive.
- NIST FIPS 204 — ML-DSA mutual auth.
- NIST FIPS 202 + SP 800-185 — KMAC256, TupleHash256.
- NIST SP 800-57 — key management.
luxfi/consensus/config/pq_mode.go— canonicalKeyExchangeID.luxfi/node/network/kem/scheme.go,luxfi/node/network/peer/handshake.go.
Copyright
CC0.