Since Go 1.24, which key exchange does crypto/tls prefer by default, and why is it a hybrid?
answer
- a TLS default that changed in Go 1.24
- belt and braces: two algorithms, one group
- one classical curve, one lattice KEM
- the group name concatenates both halves
basics
~10 sGo 1.24 made X25519MLKEM768 the preferred TLS 1.3 key exchange whenever tls.Config.CurvePreferences is left nil. It is hybrid: classical X25519 runs alongside post-quantum ML-KEM-768, so the session key holds if either half survives.
solid answer
~40 sSince Go 1.24, a `crypto/tls` client offers `X25519MLKEM768` first and a server prefers it, on any TLS 1.3 handshake where `tls.Config.CurvePreferences` is nil — you get it by doing nothing. The group is hybrid: it performs an ordinary X25519 exchange and an ML-KEM-768 encapsulation, then combines both secrets, so an attacker must break *both* to recover the session key. That hedges two ways at once: against a future quantum computer breaking X25519, and against a flaw being found in the much newer lattice scheme. The motivation is store-now-decrypt-later — traffic captured today can be decrypted years later once the classical exchange falls. The cost is size: the client's key share goes from 32 bytes to about 1216, so a full handshake carries roughly 1.5 KB more. Authentication is unaffected; certificates are still signed classically.
go deeper
Be ready to name X25519MLKEM768, say that Go 1.24 turned it on by default, and explain that hybrid means classical plus post-quantum together rather than one replacing the other.
Explain the mechanics: it is a TLS 1.3 group, it applies only when tls.Config.CurvePreferences is nil, both secrets are combined, and the key shares grow to about 1216 and 1120 bytes.
Show you know what the default does not buy you. Authentication is still classical, TLS 1.2 gets nothing, and a peer without the group silently falls back, so verify the negotiated group rather than assuming coverage.
Own the framing: which of your traffic has a secrecy horizon long enough for store-now-decrypt-later to matter, and what adopting a new protocol default commits the organisation to in toolchain and partner compatibility.
## The one-line version In Go 1.24, `crypto/tls` started offering and preferring the hybrid key-exchange group **`X25519MLKEM768`** on TLS 1.3 handshakes, for both the client and the server, whenever `tls.Config.CurvePreferences` is left `nil`. No code change is required to get it — that is the whole point of shipping it as a default. ## What "key exchange group" means here In TLS 1.3, the two sides agree on a *group* (Go calls it a `tls.CurveID`) and each sends a key share for it in the handshake. From those shares they derive the symmetric keys that encrypt everything afterwards. Historically the group was an elliptic curve: `X25519`, `CurveP256`, `CurveP384`. `X25519MLKEM768` is not a curve at all — it is two key agreements bolted together and exposed to TLS under one group name. ## What "hybrid" means A hybrid group runs both halves on every handshake and mixes both results into the secret: - **X25519** — the classical elliptic-curve Diffie-Hellman exchange that has protected TLS for a decade. Well understood, fast, 32-byte shares. - **ML-KEM-768** — a *key encapsulation mechanism* standardised by NIST as FIPS 203 (the scheme formerly known as Kyber). Its security rests on lattice problems that no known quantum algorithm solves. Its encapsulation key is 1184 bytes and its ciphertext 1088 bytes. Because both secrets feed the derivation, an attacker needs to break **both** to learn the session key. That is deliberately conservative in two directions. If a large quantum computer eventually breaks X25519, ML-KEM still holds. If a cryptanalytic break is found in ML-KEM — it is a young scheme by comparison — X25519 still holds. A pure post-quantum group would give up the second guarantee, which is why every mainstream TLS deployment is hybrid first. ## Why now: store-now-decrypt-later The threat that justifies paying for this today is that an adversary who can record encrypted traffic now can hold it and decrypt it later, once the classical exchange is breakable. Nothing about that attack requires the quantum computer to exist yet — only the recording does. So any traffic whose confidentiality must outlive the arrival of such a machine has to be protected *at the time it is sent*, which is why the key exchange was the first part of TLS to move. Note the asymmetry with authentication. Certificates are still signed with classical algorithms (ECDSA, RSA, Ed25519), and Go's TLS stack still verifies them that way. That is not an oversight: a signature cannot be forged *retroactively*. An attacker who breaks ECDSA in 2035 cannot use it to impersonate a server in a session that already happened in 2026. Confidentiality is the half with the retroactive exposure, so confidentiality moved first. ## What it costs The client's key share grows from 32 bytes to roughly 1216 (the 1184-byte ML-KEM encapsulation key plus the 32-byte X25519 public key). The server's reply grows from 32 bytes to roughly 1120 (a 1088-byte ciphertext plus 32 bytes). So a full handshake carries about 1.5 KB more than it used to, and the `ClientHello` typically no longer fits in a single network packet. The CPU cost of ML-KEM is small — lattice operations are fast — and on a real service the extra bytes are usually invisible next to the round-trip time. If you want a number for your own environment rather than a guess, a small benchmark harness that dials the same endpoint with and without the group and records per-handshake latency will produce one; resumed connections skip the exchange entirely and pay nothing. ## Scope and history The group only applies to TLS 1.3; a TLS 1.2 handshake negotiates classical ECDHE and is untouched. Go 1.23 had shipped an earlier experiment based on a pre-standard Kyber draft, enabled by default in that release; Go 1.24 removed it and replaced it with the standardised `X25519MLKEM768`. A consequence worth knowing is that a Go 1.23 peer and a Go 1.24 peer share no post-quantum group at all, so they quietly fall back to plain X25519 — the handshake still succeeds, it just gets no post-quantum protection. ## What to remember Default, not opt-in. Hybrid, not a replacement. TLS 1.3 only. Confidentiality, not authentication. And it is on precisely as long as nobody sets `CurvePreferences` by hand.
- Does this default apply to TLS 1.2 connections too?No. `X25519MLKEM768` is a TLS 1.3 key-exchange group. A handshake that negotiates TLS 1.2 uses classical ECDHE and gets no post-quantum protection at all, which is a reason to make sure TLS 1.3 is actually being negotiated before claiming the property.
- Are certificates post-quantum in Go 1.24 as well?No. Go's TLS handshake still authenticates with classical signatures — ECDSA, RSA, Ed25519. That is the deliberate order of work: recorded ciphertext can be attacked years later, but a signature cannot be forged retroactively for a session that has already completed. Confidentiality was the urgent half.
- What does the hybrid group cost on the wire?The client's key share grows from 32 bytes to roughly 1216 and the server's from 32 to roughly 1120, so a full handshake carries about 1.5 KB more. CPU cost is small. Resumed sessions skip the exchange and pay nothing. Measure it with a benchmark harness rather than assuming.
- How would you confirm a given connection actually used it?Read the negotiated group off the connection state after the handshake — recent Go exposes it as `ConnectionState.CurveID`. Counting handshakes by group across a fleet is far more useful than assuming the default applied, because any peer that lacks the group silently drops back to a classical curve.
Two locks on the same door, from two different manufacturers. A burglar who has a trick for one still has to defeat the other.
saying these in an interview costs you the question
- Claims Go 1.24 made TLS fully post-quantum, certificates included
- Thinks ML-KEM-768 replaces X25519 rather than running alongside it
- Assumes you must set CurvePreferences to enable the hybrid group
- Says the hybrid is weaker because it depends on a new algorithm
- Believes the change also covers TLS 1.2 handshakes