Files
hips/HIPs/hip-0088-session-kem.md
zeekayandHanzo Dev 7a9dcac628 What each runtime implements of the Hanzo corpus
An audit read the code and found the proposal that specifies it. Go reaches
all 122 capability proposals and 64 of the 121 other Standards Track ones;
Rust has 35, mostly the operator and the CLI; C++ has three, because the
complete original C++ in the estate is a ZAP handler, a bot nothing specifies,
and authored kernels inside forked CUDA and Metal crates.

    | | shipped | partial | none | not assessed |
    | Go   | 146 | 33 | 1 |  80 |
    | C++  |   1 |  2 | 0 | 257 |
    | Rust |  10 | 25 | 1 | 224 |

The large "not assessed" columns are the point rather than a gap. The model and
training specs are Python, the frontend and design-system proposals are
TypeScript, and recording `none` against those would read as a search that
came up empty when the honest statement is that the runtime was never the
question. Governance, Meta and Process are not graded at all.

Six proposals are Final with nothing found in Go and are left unwritten rather
than demoted: 0065, 0089, 0098, 0103, 0521 and 0902. HIP-0521 answers itself,
its own section reading "Not yet implemented". Three of the others name a
canonical Go file in consensus/protocol/auth that nobody wrote, in a package
where the four neighbouring proposals' files all exist with tests beside them.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-05 19:18:13 -07:00

5.0 KiB

hip, title, type, category, status, implementation-rust, implementation-go, author, created, requires
hip title type category status implementation-rust implementation-go author created requires
0088 Session KEM (ML-KEM-768/1024 for P2P) Standards Track Cryptography Final none shipped Hanzo AI 2026-05-11 HIP-0005, HIP-0077, HIP-0084

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 — KeyExchangeID enum (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:

  1. profile_id matches chain pin.
  2. kem_scheme_id ∈ {0x01, 0x02}; 0x90 (X25519Unsafe) rejected.
  3. Both ML-DSA-65 signatures verify under unmodified FIPS 204 against the respective party's pubkey.
  4. kem_scheme_id = 0x01 (ML-KEM-768) is the default; 0x02 (ML-KEM-1024) is required when either side advertises role=high-value.
  5. 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 — canonical KeyExchangeID.
  • luxfi/node/network/kem/scheme.go, luxfi/node/network/peer/handshake.go.

CC0.