mlkem-tls
X25519MLKEM768 and X25519MLKEM1024 hybrid post-quantum kems, per draft-ietf-tls-ecdhe-mlkem. wire-format compatible with the TLS 1.3 codepoint 0x11EC, which Cloudflare, Chrome, Firefox and rustls >= 0.23.27 ship today. stand-alone, decoupled from any specific TLS library, RustCrypto-trait friendly, no_std compatible.
why this exists
rustls already ships X25519MLKEM768 inside its TLS stack. but the hybrid combiner is wired into rustls and not exposed as a reusable kem. anyone outside rustls, custom QUIC stacks, Noise variants, MLS PQ ciphersuites, embedded TLS, HPKE PQ extensions, currently has to re-implement the byte concat and shared-secret combiner by hand. x-wing exists but is a different CFRG construction with a different byte layout, not wire-compatible with the TLS hybrid that browsers actually ship.
mlkem-tls is the TLS-WG hybrid combiner as a stand-alone crate, with the byte order and shared-secret layout pinned to the IETF draft.
construction
- classical half: x25519-dalek (audited, constant-time)
- post-quantum half: mlkem-rs (FIPS 203 ML-KEM, pure rust)
- combiner: concatenation, ML-KEM bytes first. matches §1.5 of the draft. no kdf wrapper.
| variant | encaps key | ciphertext | shared secret | TLS codepoint |
|---|---|---|---|---|
| X25519MLKEM768 | 1216 B | 1120 B | 64 B | 0x11EC |
| X25519MLKEM1024 | 1600 B | 1600 B | 64 B | (non-standard) |
byte order on the wire (matches the draft):
encaps_key = mlkem_pk || x25519_pk
ciphertext = mlkem_ct || x25519_eph_pk
shared_secret = mlkem_ss || x25519_ss
security falls back to the stronger of the two halves: a quantum adversary that breaks X25519 still cannot read traffic protected by the resulting key, and a classical adversary that breaks ML-KEM still cannot read traffic protected by it.
install
usage
use X25519MlKem768;
use thread_rng;
let mut rng = thread_rng;
// bob: generate the long-term hybrid keypair, send ek over the wire.
let = keygen;
// alice: encapsulate against bob's encaps key.
let = encapsulate;
// bob: decapsulate to recover the same 64-byte shared secret.
let bob_ss = decapsulate;
assert_eq!;
features
std(default): standard-library hooks on the dependencies.
disable defaults to compile against core + alloc:
testing
tests/round_trip.rs covers honest handshake round-trip on both 768 and 1024, exact byte-size assertions per the draft, wire-byte serialization round-trip, and TryFrom length errors.
audit readiness
documents at the repo root for anyone commissioning a third-party audit:
SECURITY.mdpublic threat model, scope, what is and is not under auditSUPPLY_CHAIN.mdevery runtime dep with role + maintainer + audit history
the post-quantum half mlkem-rs ships its own audit-readiness pack (SECURITY.md, SIDE_CHANNELS.md, AUDIT_SCOPE.md, FORMAL_VERIFICATION.md, SUPPLY_CHAIN.md) plus 10 kani-verified formal proofs and a dudect-style timing harness.
not audited
the post-quantum half delegates to mlkem-rs, which is unaudited. for production cryptography please use rustls's built-in PQ provider, which ships rustcrypto's audited ml-kem plus the same X25519 hybrid combiner. this crate exists for stacks that don't use rustls and need the hybrid combiner as a stand-alone reusable kem.
links
related crates
other small rust pieces shipped alongside this one:
mlkem-rsFIPS 203 ML-KEM in pure rust (the post-quantum half this crate composes with x25519-dalek)bashwardcheckpoint and rewind for bash side-effects in claude codeskill-scanlocal prompt-injection scanner for claude skills, MCP, AGENTS.mdpluvgofast neovim plugin manager, single rust binary, no neovim required to install
license
dual-licensed under MIT or Apache-2.0, at your option.