November 13, 2026ยท6 min read

Finding the Network Gear That Cannot Handle a Post-Quantum Handshake

EN | DE

An application built against OpenSSL 3.5 sends a post-quantum key share in its first handshake message without being asked to. The library’s default group list “selects X25519MLKEM768 as one of the predicted keyshares”, and the same documentation page says what can follow: “In rare cases this can lead to failures or timeouts because the resulting larger TLS Client Hello message may no longer fit in a single TCP segment and firewall software may erroneously disrupt the TLS handshake” (OpenSSL, SSL_CTX_set1_groups_list). A routine library upgrade can therefore make a connection that worked for years time out, with the cause in a device between the two ends. This post describes how to find that equipment in your own network ahead of the upgrade.

Why a post-quantum ClientHello is in a different size class

A classical ClientHello with an X25519 key share is a few hundred bytes. The X25519MLKEM768 key share alone is 1,216 bytes; the sizes for every key exchange are in Handshake Bytes on the Wire. One TCP segment on an ordinary Ethernet path holds 1,460 bytes, so with the rest of the message a ClientHello that carries the key share arrives in two segments.

That is valid TLS, and it used to be rare. Cloudflare described the consequence when it began offering the hybrid to origin servers: “it used to be so exceedingly rare to see a split ClientHello that there is plenty of software and hardware out there that falsely assumes it never happens” (Cloudflare, “Cloudflare now uses post-quantum cryptography to talk to your origin server”, 29 September 2023). The same post notes where the fault can sit: “Rarely it’s just the server: there could be a load-balancer, and even a humble router could be at fault.” tldr.fail documents the server side of the defect. To the client it looks the same in every case: the connection gets no answer, or it is reset.

Why internal networks meet the problem late

Public sites have been receiving hybrid ClientHellos from browsers for some time, and the paths to them are well exercised. In October 2026, 186 of 351 answering hosts among the 500 busiest domains negotiated X25519MLKEM768 (Post-Quantum Key Exchange on the 500 Busiest Domains, October 2026). Traffic inside a company has a different history. Service-to-service calls and appliances with embedded TLS stacks have mostly carried small hellos, so nothing has tested the equipment between them with a large one.

The first large hello tends to arrive with an upgrade that has nothing to do with cryptography on its face. OpenSSL 3.5 is one source. Go is another: since Go 1.24, the X25519MLKEM768 key exchange “is now supported and is enabled by default when Config.CurvePreferences is nil” (Go, Go 1.24 Release Notes). A service rebuilt on either starts sending a ClientHello that spans two segments where it sent a few hundred bytes, to every internal endpoint it talks to.

A size test that separates the path from the server

The question to put to each internal endpoint is how large a ClientHello gets through to it and is answered. The test sends a small control ClientHello first, then one ClientHello at each client-flight size the handshake size calculator gives for a key exchange at its default fixed part. These are the calculator’s sizes, and a given client’s own ClientHello will differ from them by its other extensions:

Key exchangeClientHello size tested
X25519550 B
secp256r1583 B
ML-KEM-5121,318 B
ML-KEM-7681,702 B
X25519MLKEM7681,734 B
P256MLKEM7681,767 B
ML-KEM-10242,086 B

Each ClientHello carries the key share for its row and a second one for X25519. A server that does not have the row’s group answers on X25519, so the result is about the size and holds whether or not the server supports post-quantum key exchange. A hello longer than the connection’s segment size is written as two segments with a one-second pause between them, the reproduction tldr.fail uses.

A size counts as failed only when the control completed and the hello of that size went unanswered twice, the second time sent ahead of a control that completed again. The reversed order keeps rate limiting from being read as a size limit. Anything short of that is recorded as unknown. The result per host is the largest ClientHello answered and the size at which answers stop.

Scanning a network from the inside

pqprobe netcheck runs this test from a machine inside the network, against named hosts or whole CIDR ranges:

pqprobe netcheck 10.20.0.0/24 app.example.internal

For each host that answers, it reports the size test together with whether the endpoint offers post-quantum key exchange. The two belong together, because the remedy differs:

Offers post-quantum key exchangeSize testReading
yesfailsClients that send the post-quantum key share cannot reach a server that would accept it. This is the case to fix first.
nofailsEnabling post-quantum key exchange on the server will not help until the path carries the hello.
noevery size answeredThe path is ready. The server configuration is what remains.
yesevery size answeredNo action needed.

The summary opens with the counts and lists the failing hosts first, grouped by shared failure: hosts that stop answering at the same size in the same way. The grouping is the useful part. The scan sees hosts, and a device is visible only as what failing hosts have in common. Twenty hosts behind one address range that all stop at 1,318 bytes point to whatever serves that range. A single host that stops there while its neighbours answer every size points to the host itself.

Trying it on one public host

The calculator lists the same sizes in its client flight, and now has a host field and a Test button. Press it and our server runs the size test against that host, then marks each row with its result. The page makes no network call until the button is pressed. The test runs under the same limits as the public probe, including its opt-out list.

The button reaches what our servers can reach. It tests the path from the internet to a public host, which is a fair first look at an edge firewall or a load balancer. The rows of the server flight stay untested, since a client cannot make someone else’s server send a different certificate chain. Hosts on an internal network, and the paths your own clients take to them, need the command-line scan run from inside.

What to do with a failing host

The durable fix is in the device that fails the hello: a firmware update, or a configuration change on an inspection feature that parses the handshake. Until that is in place, OpenSSL documents a client-side stopgap on the page quoted above, a group list prefixed with ?X25519MLKEM768:, which keeps the hybrid on offer and leaves its key share out of the first ClientHello. The client then reaches the hybrid only if the server asks for it in a second round trip, and the scan reports for each host whether the server does. Where it does not, the stopgap connects on classical key exchange without an error, so a host on that list still needs the path fixed before its sessions are post-quantum.