Skip to content

tls: enable hybrid post-quantum key exchange (X25519MLKEM768) - #421

Open
ramnbt wants to merge 1 commit into
haproxytech:masterfrom
ramnbt:crypt/pqc-hybrid-kex
Open

ramnbt wants to merge 1 commit into
haproxytech:masterfrom
ramnbt:crypt/pqc-hybrid-kex

Conversation

@ramnbt

@ramnbt ramnbt commented Oct 24, 2026

Copy link
Copy Markdown

tls: enable hybrid post-quantum key exchange (X25519MLKEM768)

The Data Plane API's HTTPS listener pins CurvePreferences to P-256 only (server.go:227). The comment there — "only use curves which have assembly implementations" — predates Go's post-quantum support: since Go 1.24, an explicit curve list without X25519MLKEM768 opts the server out of the hybrid post-quantum key exchange it would otherwise have by default. Since the Data Plane API often runs long-lived on infrastructure hosts, its admin traffic is the kind of thing "harvest now, decrypt later" collection targets. This change re-enables the hybrid group, additively — P-256 stays, and the classical-fallback handshake below demonstrates compatibility with classical-only peers.

This PR was generated by crypt 0.0.1-dev and verified before it was opened — every gate below ran against this branch's code. Machine-checked: this change touches exactly what it claims. An independent checker (sharing no code with the rewriter) compared the pre- and post-change syntax trees and confirmed the delta is precisely the edits listed below — no other declaration differs. An explicit CurvePreferences list without X25519MLKEM768 silently disables post-quantum key exchange in Go ≥ 1.24; this change re-enables it, preferred first. The change is additive: all previously accepted curves remain (crypt hard rule: rewrites never remove a working path), and compatibility is demonstrated against the tested classical-only peer below. One known ecosystem caveat: a larger ClientHello can trip fragmentation-buggy middleboxes on some network paths — Go documents GODEBUG=tlsmlkem=0 as the operator escape if that occurs.

Changes

  • server.go:227 — []tls.CurveID{tls.CurveP256} → []tls.CurveID{tls.X25519MLKEM768, tls.CurveP256}

Evidence

gate result detail
compile PASS (7292ms) go build ./... && go vet ./... clean
proof-diff-isolation PASS (24ms) machine-checked: AST delta across 1 file(s) is exactly the 1 claimed edit(s) [insert-hybrid-group ×1]; all other declarations identical (position/comment-insensitive)
kat-mlkem768 PASS (368ms) ML-KEM-768: 125 keyGen KATs (ACVP 25 + Wycheproof 100) + roundtrip + implicit-rejection passed in target build
interop-openssl35 PASS (4387ms) TLS 1.3 handshake vs OpenSSL 3.5 completed; negotiated group == X25519MLKEM768
interop-fallback PASS (2677ms) handshake vs classical-only OpenSSL peer completed with a classical group — hybrid preference does not break non-PQ peers
rescan PASS (8894ms) template dry runs find no remaining rewritable sites
proof-diff-isolation artifacts
  • claim: full
  • scope: syntax-tree level; identical ASTs under the same compiler imply identical behavior for unchanged code
kat-mlkem768 artifacts
  • vector_sources: NIST ACVP ML-KEM-keyGen-FIPS203 (final spec) + C2SP/wycheproof mlkem_768_keygen_seed — two independent vector authors
  • checks: keygen ek match (public seed API); encaps/decaps roundtrip; corrupted-ct implicit rejection
  • vector_count: 125
interop-openssl35 artifacts
  • server_groups: X25519MLKEM768:X25519:P-256 (classical offered; client chose hybrid)
  • entrypoint: synthesized config from rewritten CurvePreferences literal
  • negotiated_group: X25519MLKEM768
  • tls_version: 0x0304
  • peer: OpenSSL 3.5.9 29 Sep 2026 (Library: OpenSSL 3.5.9 29 Sep 2026)
interop-fallback artifacts
  • peer: OpenSSL 3.5.9 29 Sep 2026 (Library: OpenSSL 3.5.9 29 Sep 2026)
  • server_groups: X25519:P-256 (no hybrid offered; fallback must complete)
  • entrypoint: synthesized config from rewritten CurvePreferences literal
  • negotiated_group: CurveP256
  • tls_version: 0x0304

evidence bundle: crypt-evidence.json · templates T1-tls-curvepreferences-x25519mlkem768 · generated 2026-10-24T15:54:00Z

@mjuraga

mjuraga commented Nov 6, 2026

Copy link
Copy Markdown
Collaborator

WDYT if we just change this to leave CurvePreferences as nil, so a default golang stdlib preference is used? In this case X25519MLKEM768 would be enabled by default also.

Remove the CurvePreferences pin (dating from the era when only
assembly-backed curves were recommended) so the server rides the Go
stdlib defaults: on go >= 1.24 these prefer the hybrid post-quantum
X25519MLKEM768 key exchange and automatically adopt new groups as the
toolchain updates (go 1.26 added SecP256r1MLKEM768 and
SecP384r1MLKEM1024).

Note: hybrid key exchange exists only in TLS 1.3; connections
negotiating TLS 1.2 (permitted by the current MinVersion) remain
classical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ramnbt
ramnbt force-pushed the crypt/pqc-hybrid-kex branch from e584e68 to c1ecc87 Compare November 7, 2026 15:00
@ramnbt

ramnbt commented Nov 7, 2026

Copy link
Copy Markdown
Author

Agreed — done in c1ecc87: CurvePreferences removed entirely, so the config rides the stdlib defaults. On go ≥ 1.24 that negotiates X25519MLKEM768 and will auto-adopt future groups as the toolchain updates (1.26 already added SecP256r1MLKEM768 / SecP384r1MLKEM1024). This also matches what Decred's maintainers converged on for the same change in decred/vspd#536.

One scope note for transparency: the hybrid key exchange only exists in TLS 1.3, so connections that negotiate TLS 1.2 (allowed by the current MinVersion) stay fully classical. The Mozilla "modern" profile the config comment cites is TLS 1.3-only these days — raising MinVersion to 1.3 would close that gap and also make the CipherSuites list unnecessary (1.3 suites aren't configurable). Happy to include that here or leave it for a follow-up — client-compat call is yours; this commit keeps the change minimal as you suggested.

@mjuraga

mjuraga commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

I don't see the change in the PR unfortunately.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants