qrtc quantum realtime communication GitHub

Post-quantum key agreement

qrtc agrees the keys of DTLS 1.3 (media and data channels) and of TURN over TLS with hybrid X25519MLKEM768, the group browsers and TLS servers negotiate. A recording of today's traffic stays confidential even against a future quantum computer, as long as either half of the hybrid holds.

Only the key exchange is post-quantum. Peers still authenticate with ECDSA certificates whose fingerprints travel in the signalling, as in every WebRTC stack today.

How a hybrid group works

Every group here is a key encapsulation mechanism (KEM) in the TLS 1.3 sense. The client sends an encapsulation key, the server answers with a ciphertext, and both derive the same secret. A hybrid group runs ML-KEM (FIPS 203) and X25519 side by side, following draft-ietf-tls-ecdhe-mlkem:

Groups

GroupCodepointFeatureNotes
X25519MLKEM7680x11ecbothWhat browsers and TLS servers negotiate. Offered first.
X25519MLKEM5120xfe5c (private use)pq-modulettoNIST category 1 instead of 3. Only qrtc knows it; offered after 768, so only a peer without 768 picks it. Never used for TURN.
MLKEM7680x0201bothML-KEM-768 alone.
MLKEM5120x0200pq-modulettoML-KEM-512 alone. DTLS only.

Two ML-KEM backends

Both are pure Rust and differ only in where ML-KEM comes from:

With both features on, moduletto wins.

Policies

Each connection picks a policy with RtcConfiguration::post_quantum (postQuantum in JSON). Without one, the engine uses prefer when built with a post-quantum feature and off otherwise.

PolicyDTLSTURN over TLS
offDTLS 1.2, classical groupsClassical groups
preferAuto-senses the version: DTLS 1.3 with X25519MLKEM768 when the peer supports it, DTLS 1.2 otherwiseX25519MLKEM768 and MLKEM768 first, classical groups after
requireDTLS 1.3 with post-quantum groups onlyTLS 1.3 with post-quantum groups only

Under prefer, the ClientHello offers DTLS 1.2 and 1.3 with the hybrid share, and the peer's answer decides the version. When qrtc is the DTLS server, the peer's ClientHello decides. Setting a policy other than off without a post-quantum feature is a NotSupportedError.

What had to change in DTLS

str0m's DTLS library, dimpl, knew only Diffie-Hellman groups. qrtc vendors it with six patches, all meant for upstream (details):

  1. Named groups. ML-KEM and hybrid codepoints, and a supported-groups list that grows from 4 to 10 entries.
  2. Server-side key exchange in one step. A KEM server cannot produce its share before it has the client's, because its share is a ciphertext for the client's key.
  3. KEM groups kept out of DTLS 1.2, where the server sends its share first.
  4. Provider validation that exercises both kinds of group.
  5. Fragmented ClientHello. A hybrid ClientHello is about 1.4 kB, above the DTLS MTU, so it is split into MTU-sized records (RFC 9147 section 5.5).
  6. Fragmented ServerHello detection. A ServerHello split over several records carries a large key share, so it is DTLS 1.3.

Seeing the result

stats() reports the negotiated group as the transport's tlsGroup, for example "X25519MLKEM768". It is absent after a classical DTLS 1.2 handshake.

Interoperability

PeerResult
qrtc, every policy pair, both backendsUnit tests: require fails against off, every other pair connects, with a hybrid group whenever both sides have one
Chromium 141 with WebRTC-ForceDtls13 and WebRTC-EnableDtlsPqcDTLS 1.3 with X25519MLKEM768, both offer directions, under prefer and require
Chromium 141 without flagsDTLS 1.2 under prefer; no connection under require
OpenSSL 3.5 TLS server accepting only X25519MLKEM768TURN over TLS connects with both backends and both rustls providers

CI runs the Chromium and OpenSSL checks on every push; see Testing and CI.