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 messagemthat the caller derives from the initiator’s Ed25519 secret (seecrypto::dm). This lets the initiator reproduce the exact ciphertext + shared secret with no per-DM secret state, while staying PQ-secure:mis 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 eitherm, 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).