PQ Crypta Logo

Post-quantum certificates are the half nobody is ready for

9 of 12 implementations complete a hybrid key exchange. 3 can receive a post-quantum certificate.

PQ CRYPTA PLATFORM

🏠 Main

📰 News

👤 Account

⟨ QUANTUM ERROR PORTAL ⟩

Navigate the Error Dimensions

Jump to an anomaly59

Every one of these is a page: what the server emits, the clause it is judged against, and how each client answered. The full list carries the verdict tallies too.

Post-quantum migration is usually discussed as one thing. It is two, and the measurements below put them a long way apart. Key exchange is nearly done. Certificates have barely started, and no amount of server configuration moves them.

Measured on 2026-10-27T02:22:42Z by driving each client's own binary against ports that serve exactly one thing. Every count on this page is read from that run rather than written into it.

The two halves, side by side

What the port demandsCan completeCannotNot established
X25519MLKEM768 key exchange only 9 of 12 aioquic, xquic, msquic —
ML-DSA-87 certificate chain 3 of 12 aioquic, chromium, quic-go, quiche, picoquic, lsquic, xquic, msquic curl

“Not established” is this run’s reach, not a property of the client: the connection ended without saying how far it got, so neither column above can claim it. It is listed rather than folded into “Cannot”, because a client that was never measured is not a client that failed.

Both ports are legal configurations. Neither emits a malformed byte: one negotiates a single key exchange group, the other serves a certificate chain signed with a post-quantum algorithm. What is measured is what a client does when the thing it expected is not on offer.

Why the certificate half is harder

A hybrid key exchange is a negotiation. Both ends offer what they have, and a client that supports X25519MLKEM768 uses it while one that does not falls back — so a server can enable it unilaterally and lose almost nobody. That is why 9 of 12 implementations complete a handshake against a port that will speak nothing else.

A certificate is not a negotiation in the same sense. The client sends signature_algorithms listing what it can verify, and a server holding only an ML-DSA-87 chain against a client that does not name ML-DSA-87 has nothing to send. The handshake fails on the server side, before the certificate goes out, with no useful signal to the operator beyond a failed connection.

That asymmetry is the finding. Hybrid key exchange can be turned on today and almost everything still connects. A post-quantum certificate cannot be turned on at all for most of these clients, and the failure will look like the server is broken.

What each client did with the chain

The chain is issued by a private CA on purpose, and being untrusted is what makes the measurement work. A client that rejects it over trust has already decompressed the chain — 2 certificates, 15,430 bytes of DER before RFC 8879 compression, against 3,435 for the 4-certificate ECDSA chain every other port serves — parsed ML-DSA-87 structures it may never have seen, and got as far as chain building. Rejecting an unknown CA after all that is correct behaviour, and it demonstrates exactly the capability under test.

Every figure on this page is over the independent implementations only. quinn is our own fork of the stack this suite is built on, so counting it would be counting ourselves — it would have made the headline “10 of 13” where the measurement of anyone else's implementation is 9 of 12. Its row is still in the table below, marked, because leaving it out entirely would be the other way of being misleading.

ClientVerdictWhat happened
curl Inconclusive The handshake ended (aborted by peer: the endpoint encountered an internal error and cannot continue with the connection) without a CONNECTION_CLOSE carrying a TLS alert, so nothing shows how far into the chain the client got. The client process had already exited 100ms in, before this test's reaction point at never observed, so the silence after that is not evidence either way.
quinnself-test Pass Handled it: decompressed and parsed the 2-certificate, 15,430-byte ML-DSA-87 chain, then rejected it on trust (alert 42). That is the right answer to a private CA, and reaching it means the certificate message itself was handled.
aioquic Unsupported The client was refused before a handshake existed (the cryptographic handshake failed: error 40: peer is incompatible: NoSignatureSchemesInCommon). The refusal is itself the measurement on this tier, but what the client lacked is not recorded in it: this endpoint constrains both the key exchange group and the certificate's signature algorithm, and the error names neither.
chromium Unsupported The client's signature_algorithms named nothing that can verify an ML-DSA-87 chain, so this endpoint refused before sending one. Post-quantum certificates are not reachable for this client at all.
quic-go Unsupported The client's signature_algorithms named nothing that can verify an ML-DSA-87 chain, so this endpoint refused before sending one. Post-quantum certificates are not reachable for this client at all.
quiche Unsupported The client's signature_algorithms named nothing that can verify an ML-DSA-87 chain, so this endpoint refused before sending one. Post-quantum certificates are not reachable for this client at all.
neqo Pass Handled it: completed the handshake against the 2-certificate, 15,430-byte ML-DSA-87 chain: the compressed certificate message was decompressed, the chain parsed, and a post-quantum signature verified. Note that a client run with certificate verification disabled reaches this point without trusting anything, so what this shows is that the chain was processed, not that it was trusted.
picoquic Unsupported The client was refused before a handshake existed (detected an error with protocol compliance that was not covered by more specific error codes: authentication failed). The refusal is itself the measurement on this tier, but what the client lacked is not recorded in it: this endpoint constrains both the key exchange group and the certificate's signature algorithm, and the error names neither.
ngtcp2 Pass Handled it: completed the handshake against the 2-certificate, 15,430-byte ML-DSA-87 chain: the compressed certificate message was decompressed, the chain parsed, and a post-quantum signature verified. Note that a client run with certificate verification disabled reaches this point without trusting anything, so what this shows is that the chain was processed, not that it was trusted.
lsquic Unsupported The client was refused before a handshake existed (detected an error with protocol compliance that was not covered by more specific error codes: authentication failed). The refusal is itself the measurement on this tier, but what the client lacked is not recorded in it: this endpoint constrains both the key exchange group and the certificate's signature algorithm, and the error names neither.
xquic Unsupported The client aborted with handshake_failure (alert 40), which it sends when the parameters cannot be agreed at all — on this port, most often the key exchange group. Nothing shows the certificate was reached, so this is not evidence either way about its handling of an ML-DSA-87 chain.
msquic Unsupported The client was refused before a handshake existed (detected an error with protocol compliance that was not covered by more specific error codes: authentication failed). The refusal is itself the measurement on this tier, but what the client lacked is not recorded in it: this endpoint constrains both the key exchange group and the certificate's signature algorithm, and the error names neither.
quico Pass Handled it: decompressed and parsed the 2-certificate, 15,430-byte ML-DSA-87 chain, then rejected it on trust (alert 42). That is the right answer to a private CA, and reaching it means the certificate message itself was handled.

What this does not say

Nothing here is a defect report. No RFC requires support for ML-DSA certificates, RFC 8879 certificate compression is optional, and its §4 expressly lets a receiver cap the decompressed size and abort. A client that cannot receive this chain is not violating anything, and the test is classed discretionary so that it is never scored as though it were.

What the page claims is narrower and more useful: at the date of this run, an operator who turns on post-quantum certificates will be unreachable to most of these stacks, and will get no clearer diagnosis than a handshake that failed. That is worth knowing before the deadlines arrive, not after.

The verdict behind every row above, with the detail string the suite recorded: this test across every client. Citations: RFC 8879 §4, RFC 8446 §4.4.2.