On Sat, Feb 21, 2026 at 02:51:39PM -0800, Eric Rescorla wrote:
> S 1.1.
>
> FIPS 203 (ML-KEM) [FIPS203] is a FIPS standard for post-quantum
> [RFC9794] key establishment via a lattice-based key encapsulation
> mechanism (KEM). This document defines key establishment options for
> TLS 1.3 that use solely post-quantum algorithms, without a hybrid
> construction that also includes a traditional cryptographic
> algorithm. Use cases include regulatory frameworks that require
> standalone post-quantum key establishment, constrained environments
> where smaller key sizes or less computation are needed, and
> deployments where legacy middleboxes reject larger hybrid key shares.
>
> I don't think this middlebox text is really on point.
>
> If we look at John Schauman's helpful breakdown of a hybrid CH that
> offers both X25519 and X25519/Kyber768, we see that the total CH is
> 1815 octets. Swapping out the hybrid for MLKEM-768 would buy you 23
> octets, which doesn't change things materially.
While I agree that switching from an X25519MLKEM768 predicted keyshare
to a pure ML-KEM768 predicted keyshare will generally not make a
material difference, that 1815 is sometimes an over-estimate.
- A version-flexible OpenSSL 4.0-dev client hello supporting both TLS
1.2 and TLS 1.3 is ~1540 bytes (the SNI was "some.host.example"):
Sent TLS Record
Header:
Version = TLS 1.0 (0x301)
Content Type = Handshake (22)
Length = 1543
ClientHello, Length=1539
client_version=0x303 (TLS 1.2)
...
Admittedly, this does not include a resumption PSK, the same client
attempting resumption sends:
Sent TLS Record
Header:
Version = TLS 1.0 (0x301)
Content Type = Handshake (22)
Length = 1814
ClientHello, Length=1810
client_version=0x303 (TLS 1.2)
which closely matches the the larger number cited by Eric.
- Replacing the hybrid keyshare with pure MLKEM768 and dropping the
second classical-only X25519 keyshare, the non-resumption case
becomes:
Sent TLS Record
Header:
Version = TLS 1.0 (0x301)
Content Type = Handshake (22)
Length = 1475
ClientHello, Length=1471
client_version=0x303 (TLS 1.2)
...
But given TCP/IP encapsulation this is still liable to require
multiple TCP segments, and to trigger middlebox issues.
- Setting the protocol floor at TLS 1.3 trims a bunch of TLS 1.2 cipher
codepoints, and a non-resumption CH is finally plausibly small enough
to fit into a single TCP segment:
Sent TLS Record
Header:
Version = TLS 1.0 (0x301)
Content Type = Handshake (22)
Length = 1396
ClientHello, Length=1392
client_version=0x303 (TLS 1.2)
But with a resumption PSK this is again too large:
Sent TLS Record
Header:
Version = TLS 1.0 (0x301)
Content Type = Handshake (22)
Length = 1667
ClientHello, Length=1663
client_version=0x303 (TLS 1.2)
...
extension_type=key_share(51), length=1190
NamedGroup: MLKEM768 (513)
key_exchange: (len=1184): ...
extension_type=psk(41), length=267
...
So the winning scenario for pure ML-KEM is perhaps a non-resumption TLS
1.3-only CH. Notably specialised, but perhaps that's a use-case that
just happens to fit someone's situation.
--
Viktor. 🇺🇦 Слава Україні!
_______________________________________________
TLS mailing list -- [email protected]
To unsubscribe send an email to [email protected]