Skip to main content

Module pqc

Module pqc 

Source
Expand description

huddle 1.3: post-quantum hybrid key-agreement primitives (ML-KEM-768).

This module is the quantum-resistant half of huddle’s hybrid DM key agreement. It wraps the FIPS 203 ML-KEM-768 KEM (RustCrypto ml-kem, pure Rust) and a transcript-binding HKDF combiner that mixes a classical X25519 shared secret with the ML-KEM shared secret. The result is at least as strong as the stronger of the two:

  • a future quantum computer that breaks X25519 (via Shor) still cannot recover the key without also breaking ML-KEM, and
  • a (hypothetical) cryptanalytic break of ML-KEM still leaves the classical X25519 secret protecting the key.

That is exactly the “harvest-now, decrypt-later” defence: an adversary who records DM ciphertext today and builds a quantum computer tomorrow gains nothing, because the X25519 secret they can eventually recover is only one of the two HKDF inputs.

§Design choices specific to huddle’s non-interactive, static DM model

huddle’s classical DM key (crypto::dm::derive_dm_key) is non-interactive and reproducible: both peers derive the same [u8; 32] from long-term keys with no stored per-DM state. ML-KEM is a KEM (directional: one side encapsulates, the other decapsulates), so a hybrid path cannot be perfectly symmetric — a ciphertext must travel from the initiator to the responder. We keep everything else stateless by being deterministic:

  • The ML-KEM keypair is derived from the peer’s long-term Ed25519 identity seed (PqKeypair::from_identity_seed, HKDF domain- separated), so it needs no extra storage and is stable across restarts. The public encapsulation key is still published to peers — they cannot compute it from the Ed25519 public key alone.
  • Encapsulation is deterministic (encapsulate_deterministic), seeded by a message m that the caller derives from the initiator’s Ed25519 secret (see crypto::dm). This lets the initiator reproduce the exact ciphertext + shared secret with no per-DM secret state, while staying PQ-secure: m is unknown to anyone without the initiator’s seed, so a Shor attacker who recovers the X25519 secret still cannot reconstruct the ML-KEM shared secret (that needs either m, which is seed-derived, or the responder’s decapsulation key).

See crypto::dm::derive_dm_key_hybrid_initiator / _responder for the protocol wiring.

Structs§

PqKeypair
A deterministically-derived ML-KEM-768 keypair bound to a huddle identity.

Constants§

MLKEM_CT_LEN
Serialized length of an ML-KEM-768 ciphertext.
MLKEM_EK_LEN
Serialized length of an ML-KEM-768 encapsulation (public) key.
SS_LEN
Shared-secret length (ML-KEM, X25519, and our combined output all 32B).

Functions§

combine_hybrid
Combine a classical X25519 shared secret and an ML-KEM shared secret into a single 32-byte hybrid key, binding the KEM ciphertext and a context label into the transcript.
encapsulate_deterministic
Encapsulate to a peer’s ML-KEM-768 encapsulation key using a caller-supplied deterministic 32-byte message m. Returns (ciphertext, shared_secret).