Vault can negotiate a post-quantum-secure TLS 1.3 handshake with the
Bitwarden / Vaultwarden server using the hybrid X25519MLKEM768 key-exchange
group. It is off by default and enabled with the pqc cargo feature.
# build the agent with PQC preferred on its HTTPS client
cargo build -p vault-agent --features pqc
# (vault-api exposes the underlying feature: `--features vault-api/pqc`)When enabled, the client offers X25519MLKEM768 first and the classical groups (X25519, P-256, P-384) as fallback. If the server doesn't support PQC — most don't yet — the handshake silently falls back to a classical group, so enabling the feature is safe. PQC is TLS 1.3 only.
rustls only ships X25519MLKEM768 through its aws-lc-rs provider. aws-lc-rs
bundles AWS-LC, whose license tree includes OpenSSL-licensed code — which is
GPL-incompatible. Vault is GPL-3.0-or-later (Standard §4), so aws-lc-rs is
not an option, and our deny.toml allow-list would reject it.
Instead, crates/vault-api/src/pqc.rs builds the hybrid group from
GPL-compatible parts:
- classical half — ring's audited X25519 (
rustls::crypto::ring::kx_group::X25519), reused as-is; - post-quantum half — RustCrypto's
ml-kem(MlKem768), Apache-2.0/MIT.
The two are composed into a rustls::crypto::SupportedKxGroup and injected via
reqwest's use_preconfigured_tls. Only the client role is implemented.
Per draft-ietf-tls-ecdhe-mlkem (X25519MLKEM768), the post-quantum element
comes first in every share and in the derived secret:
| bytes | layout | |
|---|---|---|
| client share | 1216 | ek_ML-KEM-768 (1184) ‖ x25519_pub (32) |
| server share | 1120 | ct (1088) ‖ x25519_pub (32) |
| shared secret | 64 | ss_ML-KEM (32) ‖ ss_X25519 (32) |
The client generates an ML-KEM keypair (keeping the decapsulation key) plus an
X25519 ephemeral, sends ek ‖ x25519_pub, then on the server's reply
X25519-DHs the classical part, decapsulates the ML-KEM ciphertext, and
concatenates the two secrets (PQ first). This mirrors rustls's own
(crate-private) aws_lc_rs::pq::hybrid layout.
This satisfies the M7 "PQC transport feature flag" item. Still pending for the
v0.1 tag: making PQC a tested live handshake against a PQC-enabled server, the
≥ 24 h EncString fuzz soak (docs/fuzzing.md), the broader hardening pass, and
the §11.2 two-week daily-driver attestation.