Skip to content
ecdsa.com

X25519MLKEM768 vs X25519: post-quantum TLS key exchange

The hybrid group adds about 1.2 KB to the ClientHello and protects recorded traffic against future quantum computers. It is already the default in browsers, OpenSSL and Go.

X25519MLKEM768 vs X25519 is the key-exchange decision in every TLS 1.3 handshake today. X25519 alone is fast and tiny but falls to a quantum computer, which means traffic recorded now could be decrypted later. X25519MLKEM768 runs X25519 and ML-KEM-768 together, so the session stays confidential unless both are broken.

At a glance

X25519X25519MLKEM768
TLS supported groupx25519, 29 (0x001D)X25519MLKEM768, 4588 (0x11EC), IANA Recommended
SpecificationRFC 7748, RFC 9846RFC 10024 (2026), framework RFC 9954
ComponentsX25519ML-KEM-768 (FIPS 203) and X25519
Client key share32 bytes1,216 bytes (1,184 ML-KEM encapsulation key + 32)
Server key share32 bytes1,120 bytes (1,088 ML-KEM ciphertext + 32)
Shared secret into the key schedule32 bytes64 bytes (ML-KEM secret first, then X25519)
Quantum resistanceNoYes, as long as ML-KEM-768 holds
Classical securityX25519At least X25519, even if ML-KEM were broken
CPU per handshake, OpenSSL 3.6.5about 52,000 X25519 operations per secondabout 19,000–24,000 keygen, encapsulation or decapsulation per second
ClientHello measured below306 bytes1,484 bytes
ServerHello measured below122 bytes1,210 bytes

Throughput from openssl speed on one Apple M4 Max core; handshake sizes from the capture below.

Why hybrid now

Key exchange is exposed to harvest-now-decrypt-later: an adversary records encrypted sessions today and decrypts them once a cryptographically relevant quantum computer exists, recovering the session keys from the recorded X25519 exchange. Forward secrecy does not help, because each recorded exchange can be broken individually. Anything whose confidentiality must outlast the arrival of such a computer is already at risk, which is why key exchange moved first while signatures can wait longer.

The hybrid design hedges the other way too. ML-KEM was standardized in August 2024 and is young compared with X25519, so a mathematical or implementation break is not unthinkable. X25519MLKEM768 concatenates both shared secrets into the TLS key schedule, so an attacker must break both. RFC 10024 orders the ML-KEM share and secret first for this group, unlike the NIST-curve hybrids (SecP256r1MLKEM768, 0x11EB, and SecP384r1MLKEM1024, 0x11ED), which put ECDH first.

What changes on the wire

The client sends a 1,216-byte key share instead of 32 bytes, so a typical ClientHello grows from about 300 bytes to about 1.5 KB and no longer fits in one network packet with all the other extensions of a browser. The server answers with a 1,120-byte share. A live handshake with Cloudflare, captured with OpenSSL 3.6.5, shows the difference:

OpenSSL 3.6.5: TLS 1.3 handshake with each group
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519MLKEM768 -brief 2>&1 | grep -E "Protocol|group"
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519MLKEM768 -msg 2>&1 | grep -E "ClientHello|ServerHello"
echo | openssl s_client -connect cloudflare.com:443 -servername cloudflare.com -groups X25519 -msg 2>&1 | grep -E "ClientHello|ServerHello"
Output on 2026-10-20 (lengths in hex)
Protocol version: TLSv1.3
Negotiated TLS1.3 group: X25519MLKEM768
>>> TLS 1.3, Handshake [length 05cc], ClientHello
<<< TLS 1.3, Handshake [length 04ba], ServerHello
>>> TLS 1.3, Handshake [length 0132], ClientHello
<<< TLS 1.3, Handshake [length 007a], ServerHello

That is 1,484 against 306 bytes for the ClientHello and 1,210 against 122 for the ServerHello. Larger ClientHellos exposed bugs in servers and middleboxes that assumed the message arrives in one TCP read or one packet; when Chrome enabled hybrid Kyber shares by default in version 124 (2024), several firewall and proxy products had to be fixed, a problem class documented at tldr.fail. Cloudflare and Google reported a few percent of median handshake latency from the extra bytes, which matters far less than the round trips saved by TLS 1.3 itself.

Deployment status

  • Browsers: Chrome and Edge offer X25519MLKEM768 by default since version 131 (November 2024), Firefox since 132 on desktop, Safari and Apple's system TLS since iOS 26 and macOS 26 (2025). The pre-standard X25519Kyber768Draft00 (0x6399) they used earlier is deprecated.
  • Libraries: OpenSSL 3.5 (April 2025) sends X25519MLKEM768 and X25519 key shares by default; Go 1.24 enables it by default in crypto/tls; JDK 27 (September 2026, JEP 527) offers it first by default; BoringSSL, rustls and NSS support it.
  • Servers and CDNs: Cloudflare, Google and other large providers negotiate it with supporting clients; nginx, Apache and HAProxy pick it up from the linked OpenSSL.
  • SSH: the equivalent mlkem768x25519-sha256 is the OpenSSH default since 10.0 (RFC 10042).

The browser PQ check shows what your own browser negotiates.

When to choose which

  • Public HTTPS and APIs: offer X25519MLKEM768 first and X25519 second; every modern client negotiates the hybrid and old ones fall back.
  • Internal service mesh and mTLS: hybrid by default, especially for data with long confidentiality requirements.
  • FIPS-oriented deployments: SecP256r1MLKEM768 or SecP384r1MLKEM1024 if your module's approved list requires NIST curves; X25519MLKEM768 qualifies where ML-KEM is the approved component.
  • Constrained links where every packet counts: measure first; the hybrid adds one packet per direction, which is usually acceptable.
  • X25519 alone: only for peers that cannot do the hybrid, never as a deliberate choice for new deployments.

Migration notes

  1. Upgrade the TLS library (OpenSSL 3.5 or later, Go 1.24 or later, current BoringSSL or rustls); with OpenSSL 3.5 the default groups already include the hybrid.
  2. If you set groups explicitly, add it: for example ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1; in nginx or Groups = X25519MLKEM768:X25519:P-256 in an OpenSSL configuration file.
  3. Test the path, not just the server: load balancers, WAFs and TLS-inspecting proxies must handle a ClientHello split across packets and reads; retest after their upgrades.
  4. Verify what is negotiated with openssl s_client -groups X25519MLKEM768 or browser developer tools, and monitor the share of hybrid handshakes.
  5. Plan the signature side separately: the hybrid protects confidentiality only; certificates and handshake signatures remain classical until post-quantum signatures arrive in the WebPKI.

References

All comparisons