no-mlkem
TLS 1.3 works, but the server doesn’t negotiate ML-KEM. Either its TLS library is too old to have it, or the group list in its config leaves it out.
This was 61.3% of the non-post-quantum origins in the study: 2,108 of 3,439.
How pqdoctor sees it
Section titled “How pqdoctor sees it”- The classical TLS 1.3 hello (probe 1) gets a ServerHello.
- Offered only an
X25519MLKEM768key share (probe 2), the server asks the client to retry with a classical group, usually X25519. - It doesn’t steer clients to NIST curves. That would be
pinned-nist.
The fix
Section titled “The fix”- Upgrade the TLS library. Hybrid PQ (
X25519MLKEM768) ships in OpenSSL 3.5+, current BoringSSL and AWS-LC, Go 1.24+, and rustls with aws-lc-rs. - If the groups are set by hand, put
X25519MLKEM768first:
# nginx, on OpenSSL 3.5+ssl_protocols TLSv1.2 TLSv1.3;ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1;// Go 1.24+: the default includes X25519MLKEM768cfg.CurvePreferences = nilpqdoctor prints the version for the stack it detects. Every server’s lines are on Fixes by server. If you can’t upgrade, there’s the Cloudflare Tunnel fallback.
Shown in the fix lab
Section titled “Shown in the fix lab”The fix lab ran real servers with this fault. pqdoctor reported each one not post-quantum, and the fix it printed made each one post-quantum:
| Server | Fix applied | After |
|---|---|---|
OpenSSL 3.5, groups X25519:P-256 |
ssl_ecdh_curve X25519MLKEM768:X25519:prime256v1 |
PQ |
Node, ecdhCurve set to X25519 |
ecdhCurve: 'X25519MLKEM768:X25519:P-256' |
PQ |
Go without ML-KEM (GODEBUG=tlsmlkem=0, pre-1.24 behaviour) |
Go 1.24+ defaults (CurvePreferences = nil) |
PQ |
Go with CurvePreferences X25519, P-256 |
Remove CurvePreferences |
PQ |
| SMTP, IMAP STARTTLS with pinned groups | The Postfix / Dovecot group list | PQ |
In the study
Section titled “In the study”Server software named in the Server header for no-mlkem origins: unknown 1,030, nginx 600, apache 225, aws 151, envoy 30. The header is often missing, so “unknown” is large.
Verify
Section titled “Verify”openssl s_client -connect example.com:443 -servername example.com -groups X25519MLKEM768 </dev/null 2>&1 | grep -i 'group'