Two post-quantum hybrids reach endpoints today. Both combine X25519 and ML-KEM-768. The wire sizes are identical: 1216 byte client share, 1120 byte server share or ciphertext. The names sound interchangeable. The constructions are not.
Swapping one for the other breaks. Decapsulating an X25519MLKEM768 ciphertext with an X-Wing decapsulator produces the wrong shared secret, because the combiners differ. Vendor documentation routinely treats them as synonyms. Treating them that way in a migration plan is a defect.
X25519MLKEM768 is a TLS 1.3 named group
Specified in draft-kwiatkowski-tls-ecdhe-mlkem. IANA codepoint 0x11EC. The ClientHello advertises the group in supported_groups and sends a key_share of ML-KEM-768.encaps_key || X25519.ephemeral_share, 1216 bytes. The ServerHello returns X25519.ephemeral_share || ML-KEM-768.ciphertext, 1120 bytes. The shared secret is ss_MLKEM || ss_X25519, 64 bytes, fed into the existing TLS 1.3 key schedule as the (EC)DHE input to HKDF-Extract.
ML-KEM goes first in the concatenation for a specific reason: NIST SP 800-56Cr2 approves HKDF with two shared secrets only if the first is produced by a FIPS-approved scheme. ML-KEM-768 is FIPS 203. X25519 is not FIPS-approved. The ordering preserves the FIPS derivation path.
The combiner is simple concatenation. The non-malleability of the resulting key schedule is not a property of the combiner on its own. It comes from the TLS 1.3 transcript hash, which binds the full handshake into every derived secret. Remove the transcript and the combiner alone does not carry the binding properties a general-purpose KEM needs.
X-Wing is a general-purpose KEM
Specified in draft-connolly-cfrg-xwing-kem, currently at revision 10 (March 2026, expires September 2026). Encapsulation key 1216 bytes, ciphertext 1120 bytes, shared secret 32 bytes.
The combiner is a single SHA3-256 hash:
Combiner(ss_M, ss_X, ct_X, pk_X) =
SHA3-256(ss_M || ss_X || ct_X || pk_X || XWingLabel)
XWingLabel = 0x5c2e2f2f5e5c // ASCII "\./" + "/^\"
Hashing the X25519 ciphertext and the X25519 public key into the combiner gives X-Wing MAL-BIND-K-PK and MAL-BIND-K-CT binding properties. Bare ML-KEM-768 has neither. The binding is what makes X-Wing safe to use outside a surrounding transcript, which is the entire point of calling it general-purpose.
Security proof in IACR ePrint 2024/039: X-Wing is IND-CCA secure in the QROM if either X25519 or ML-KEM-768 is secure. 128-bit security target, NIST PQC level 1.
Side by side
| X25519MLKEM768 | X-Wing | |
|---|---|---|
| Spec | draft-kwiatkowski-tls-ecdhe-mlkem | draft-connolly-cfrg-xwing-kem-10 |
| Surface | TLS 1.3 named group | HPKE, X.509, general-purpose |
| Codepoint | TLS 0x11EC | HPKE 0x647a (requested, draft-10) |
| Combiner | Concatenation, then TLS key schedule | SHA3-256 over ss_M, ss_X, ct_X, pk_X, label |
| Client share | 1216 bytes | 1216 bytes (encaps key) |
| Server share | 1120 bytes | 1120 bytes (ciphertext) |
| Shared secret | 64 bytes (concatenated) | 32 bytes (hashed) |
| Binding | Via TLS transcript, not combiner | MAL-BIND-K-PK, MAL-BIND-K-CT in combiner |
| FIPS path | Via SP 800-56Cr2 HKDF derivation | Not a FIPS construction |
They are not interchangeable
From the X-Wing draft, section 1.5.1:
There is also a different KEM called X25519Kyber768Draft00 which is used in TLS. This one should not be used outside of TLS, as it assumes the presence of the TLS transcript to ensure non malleability.
The same constraint applies to X25519MLKEM768. It is a TLS construction. Lift the 64-byte shared secret out of the TLS key schedule and use it elsewhere and the binding properties do not travel with it.
In the other direction, X-Wing is not a TLS named group. It has no 0x11EC or equivalent TLS codepoint. TLS 1.3 implementations cannot negotiate it. Attempting to wire X-Wing into a TLS stack requires either a new TLS extension or abuse of an unassigned codepoint, and the interop story is nonexistent.
Historical transition
X25519Kyber768Draft00, codepoint 0x6399, was the hybrid Chrome, Firefox, and Cloudflare deployed in 2023 and 2024. It used the Kyber round-3 specification, which was superseded by FIPS 203 ML-KEM in August 2024. The codepoint and the wire format changed. X25519MLKEM768 at 0x11EC is the FIPS 203 version. Packet captures and vendor documentation from 2023 and 2024 that reference Kyber or codepoint 0x6399 describe the deprecated construction.
Probing any endpoint today and seeing X25519Kyber768Draft00 still advertised is a signal. The vendor has not updated to ML-KEM and is running obsolete code.
NIST position
SP 800-227 finalized September 2025. Section 4.6 on multi-algorithm KEMs and PQ/T hybrids names X-Wing as the concrete example of a composite KEM. Not a FIPS mandate. Not a TLS recommendation. It is the only named construction in the document, which is as close to endorsement as NIST offers outside FIPS 203.
TLS hybrid codepoints are governed by the IETF TLS working group through IANA. X25519MLKEM768 sits in that track. NIST SP 800-227 does not name it.
Deployment status
X25519MLKEM768 in TLS: default in Chrome 131 (November 2024, when Chrome switched its default from X25519Kyber768Draft00 at 0x6399 to X25519MLKEM768 at 0x11EC), default in Edge 131, Firefox 132, Cloudflare edge since March 2024, OpenSSL 3.5 and later, BoringSSL, wolfSSL. Safari Technology Preview has it; the stable release lags.
X-Wing in KEM contexts: Go via filippo.io/mlkem768/xwing, Rust via the x-wing crate, Google Cloud KMS under the kem-xwing algorithm identifier, HPKE implementations tracking the draft. OpenSSL provider work is in progress. No serious TLS implementation ships X-Wing, because X-Wing is not a TLS named group.
Scope
This is key exchange. Signatures are a separate migration with separate algorithms: ML-DSA (FIPS 204), SLH-DSA (FIPS 205), Falcon (FIPS 206 draft). A vendor with X25519MLKEM768 on the wire may still be signing with classical RSA or ECDSA. Confidentiality posture and authentication posture are independent and need to be measured separately.
Testing it yourself
TLS side, OpenSSL 3.5 or later:
openssl s_client -connect example.com:443 -groups X25519MLKEM768 -tls1_3
If the handshake completes and the ServerHello returns key_share group 0x11EC, the endpoint negotiates the hybrid. If the server falls back to X25519 or secp256r1, it does not.
KEM side, Go:
import "filippo.io/mlkem768/xwing"
dk, ek, _ := xwing.GenerateKeyPair(rand.Reader)
ct, ss1, _ := xwing.Encapsulate(rand.Reader, ek)
ss2, _ := xwing.Decapsulate(dk, ct)
// ss1 == ss2, 32 bytes
The two operations produce different shared secrets from the same X25519 and ML-KEM-768 primitives. That is the demonstration: same inputs, different outputs, because the combiners differ.
What to ask vendors
- Which TLS named groups does your endpoint advertise, and what is the current codepoint?
- Is the advertised group X25519MLKEM768 at 0x11EC, or the deprecated X25519Kyber768Draft00 at 0x6399?
- If you expose a KEM or HPKE API, is it X-Wing, the generic combiner of draft-ounsworth-cfrg-kem-combiners, or something proprietary?
- Are your signatures post-quantum? ML-DSA, SLH-DSA, hybrid with ECDSA or RSA, or still classical only?
- If your TLS advertises X25519MLKEM768 and your KMS advertises X-Wing, can you confirm they are two different constructions using the same underlying primitives, not the same construction under two names?
Question five is the one vendors most often get wrong. [PQ]probe records the TLS named group the endpoint actually negotiates and surfaces the codepoint directly; the KEM and HPKE surfaces require separate review of product documentation, SDK code, and API responses.