Skip to content

Post-quantum & at-rest

The reason to care about post-quantum now, before quantum computers exist, is long-lived data: an adversary can record encrypted traffic or copy an encrypted blob today and decrypt it years later once the math is cheap. So the value isn’t the live browser session (covered by TLS + classical signatures) — it’s data at rest (PHI, EEG sessions in R2) and data over untrusted relays (a public MoQ/CDN path). That’s where post-quantum confidentiality and signatures earn their cost.

PQC is not a new mechanism — it’s more algorithm selectors on the existing Phaze Transport crypto fields. You author a transport fence and name the post-quantum algorithm; nothing else about the fence changes:

---Eeg.session
response: Flat<SessionBlob>
encrypt: 'ml-kem-768+aes-gcm' # KEM+DEM: ML-KEM wraps the AES-256 key
sign: 'ed25519+ml-dsa' # composite: classical + PQC, both verify
---
return blob
  • The parameter set rides in the nameml-dsa-65, ml-kem-768.
  • sign: + = composite signature (both must verify — classical + PQC defense-in-depth).
  • encrypt: + = KEM + DEM — ml-kem-768+aes-gcm means ML-KEM establishes a symmetric key, AES-GCM encrypts the body with it. The KEM half can itself be hybrid (x25519+ml-kem-768, the IETF classical+PQC key-agreement pattern).

The ML-KEM-768 / ML-DSA-65 primitives (phaze::crypto::ml_kem / ml_dsa) and the field-level wiring are shipped on the Rust runtime. See the PQC design note for the measured cost (~70–80 KB WASM) and the server-vs-browser placement decision (Option A vs B).

Every sign: / encrypt: value below is wired on the Rust runtime and backed by a live /api/* endpoint. The PQC values are driven end-to-end by build/pqc.test.mjs, which boots the real rust-api worker (Wasm-on-workerd) and exercises each through workerd:

sign: / encrypt: valueEndpointProven at runtime
sign: 'ed25519'/api/flatclassical floor — X-Phaze-Sig, verified in-browser natively (0 WASM)
sign: 'ed25519+ml-dsa'/api/signed-pqdual X-Phaze-Sig: ed25519:…, ml-dsa:…; browser verifies the ed25519 half (native, 0 WASM), a reader verifies the ml-dsa half
encrypt: 'aes-gcm-256'actions / SSEclassical DEM (its own transport e2e)
encrypt: 'ml-kem-768+aes-gcm'/api/encrypted-pq{ kem, e, iv } envelope; kem = the 1088-byte ML-KEM-768 ciphertext
encrypt: 'ml-kem-768+aes-gcm' + sign: 'ed25519+ml-dsa'/api/sealed-signed-pqsign-then-encrypt compose — signs the raw body, then seals it; reuses hybrid_sig, so it’s the two proven halves above with no new crypto

Behind the fields, the same run round-trips the primitives directly: ML-DSA-65 sign/verify + tamper-reject (/api/pqc-sign), ML-KEM-768 + AES-256-GCM (/api/pqc-encrypt), and the response-sig verifier that makes sign: end-to-end (/api/verify-pq).

The honest cost — and why the browser usually avoids it

Section titled “The honest cost — and why the browser usually avoids it”

Unlike classical crypto, post-quantum verify/decrypt is not Web Crypto native — if the browser must do it, it ships a WASM module. The field gates it (only those actions, only when the browser is the operating party), but it isn’t zero. See the security model for the full principle.

The architecture keeps the browser out of it in the common case:

  • Browser edge stays classical-native (Ed25519 / ES256 / AES-GCM) → 0 WASM.
  • PQC runs server-side — on the Rust runtime (native, fast) and at rest — which is exactly where harvest-now-decrypt-later lives.
  • A hybrid signature lets the browser verify the classical half (native) for its live check, while the PQC half is the long-lived/at-rest assurance verified server-side → 0 browser WASM even for a hybrid-signed response.

PQC lands on the Rust side via the phaze-crypto family:

  • dcryptchosen, and built. phaze::crypto::ml_kem (ML-KEM-768) and phaze::crypto::ml_dsa (ML-DSA-65) ship on dcrypt’s Kyber768 / Dilithium3; measured ~77 KB brotli wasm, and its rand 0.8 aligns with phaze-crypto’s rand_core (zero version conflict). See the PQC design note for the dcrypt-vs-RustCrypto comparison and the wasm/wire/speed numbers.
  • rage (Rust age) — under evaluation as the at-rest blob format (e.g. an encrypted EEG session): clean format, streaming/armor; classical core (X25519 + ChaCha20-Poly1305) with PQC via age plugins.

transport: 'moq' + encrypt: (PQC) + per-chunk sign: is the case where it all meets — a long-lived, independently-decodable media/data stream over an untrusted relay, where each chunk is self-describing (FlatBuffer file_identifier) and integrity + confidentiality must survive both the relay and the calendar.