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]

Reply via email to