How Chromium Negotiates Post-Quantum Keys in TLS 1.3
This article provides a technical overview of how the Chromium web browser negotiates post-quantum key encapsulation mechanisms (KEMs) during a TLS 1.3 handshake. It covers Chromium's implementation of a hybrid cryptographic approach via its BoringSSL library, the specific extension data transmitted in the client and server hello messages, how the classical and post-quantum shared secrets are combined, and how the browser seamlessly falls back to classical cryptography when communicating with legacy servers.
The Hybrid Cryptographic Approach
Chromium does not rely solely on post-quantum algorithms for securing TLS 1.3 connections. Instead, it uses a hybrid key exchange mechanism that pairs a classical elliptic curve algorithm (X25519) with a NIST-standardized post-quantum lattice-based KEM (ML-KEM-768, formerly known as Kyber-768).
This hybrid approach ensures that the connection remains secure as long as at least one of the two algorithms remains unbroken. It protects data transmitted today against future "harvest now, decrypt later" attacks by quantum computers, without sacrificing the proven security of existing elliptic curve cryptography.
Step 1: The ClientHello Message
The negotiation begins when Chromium initiates a TLS 1.3 handshake by
sending a ClientHello message to the server. To negotiate
the post-quantum hybrid key exchange, Chromium populates two specific
TLS extensions:
supported_groupsExtension: Chromium lists the cryptographic groups it supports in order of preference. It places the hybrid identifier (such asX25519MLKEM768using code point0x11EC, or the earlier draftX25519Kyber768Draft00using code point0x6399) at the top of the list, followed by classical groups like standardX25519(0x001D) andsecp256r1.key_shareExtension: TLS 1.3 optimizes handshake speed by having the client guess which algorithms the server supports and sending the corresponding public keys immediately. Chromium generates and sends two key shares in the initialClientHello: the hybrid public key (which consists of a 32-byte X25519 public key concatenated with a 1,184-byte ML-KEM-768 public key) and a standalone 32-byte X25519 public key.
By sending both key shares upfront, Chromium avoids an extra network round-trip if the server does not support the post-quantum hybrid algorithm.
Step 2: Server Processing and Fallback
When the server receives the ClientHello, it evaluates
the supported_groups and key_share extensions.
There are two primary outcomes:
- Post-Quantum Negotiation Succeeds: If the server
supports the hybrid KEM, it selects the hybrid code point. The server
generates its own ephemeral X25519 key pair to compute the classical
shared secret. Simultaneously, it uses the client's ML-KEM-768 public
key to encapsulate a randomly generated post-quantum shared secret,
producing a ciphertext. The server responds with a
ServerHellomessage containing akey_shareextension that holds its 32-byte X25519 public key concatenated with the 1,088-byte ML-KEM-768 ciphertext. - Fallback to Classical Cryptography: If the server
does not recognize the hybrid code point, standard TLS 1.3 protocol
rules dictate that the server simply ignore the unknown identifier. The
server moves down the client's preference list, sees the classical
X25519group, and uses the standalone X25519 key share that Chromium already provided in theClientHello. The handshake completes using classical cryptography without any connection failures or delays.
Step 3: Secret Combination and Key Derivation
When the post-quantum negotiation succeeds and Chromium receives the
ServerHello, it extracts the server's hybrid
key_share data and splits it into its two components: the
classical public key and the post-quantum ciphertext.
Chromium's BoringSSL library then performs two mathematical operations:
- It executes a standard Diffie-Hellman operation using its private X25519 key and the server's public X25519 key, yielding a 32-byte classical shared secret.
- It decapsulates the server's ML-KEM-768 ciphertext using its private ML-KEM-768 key, yielding a 32-byte post-quantum shared secret.
Chromium concatenates these two outputs together to form a single 64-byte combined shared secret. This 64-byte value is then fed directly into the standard TLS 1.3 HMAC-based Extract-and-Expand Key Derivation Function (HKDF) as the key exchange input. From this point onward, the rest of the TLS 1.3 handshake—including certificate verification and the generation of symmetric encryption keys (such as AES-GCM or ChaCha20-Poly1305) for bulk data transfer—proceeds identically to a standard TLS 1.3 connection.