What does the key_share extension carry in a TLS 1.3 ClientHello, and what comes back in the ServerHello?
answer
- Two lists, one of them speculative
- Willingness versus an actual public value
- supported_groups names, key_share carries
- ServerHello returns one entry only
- Private values never leave either host
basics
~10 sThe key_share extension carries ephemeral Diffie-Hellman public values the client generated, one per named group it guessed the server would accept. The ServerHello returns exactly one such value, for the group the server chose.
solid answer
~40 sA TLS 1.3 client lists every named group it is willing to use in `supported_groups(10)` — for example `x25519(0x001D)` and `secp256r1(0x0017)` — and then, in `key_share(51)`, speculatively includes a real ephemeral public value for one or two of them. Each entry is a `KeyShareEntry` pairing a `NamedGroup` with the raw `key_exchange` bytes. The server selects one group it also supports, and its ServerHello carries a single `KeyShareEntry` for that group holding the server's own ephemeral public value. Each side then combines its own private value with the peer's public value and arrives at the same shared secret, which is never transmitted. TLS feeds that secret into its key schedule, naming each derived output with `Derive-Secret` and `HKDF-Expand-Label`.
code
pseudocode · 14 linesClientHello
extension supported_groups(10):
x25519(0x001D), secp256r1(0x0017), secp384r1(0x0018)
extension key_share(51):
KeyShareEntry { group = x25519(0x001D),
key_exchange = <32-byte client public value> }
ServerHello
extension key_share(51):
KeyShareEntry { group = x25519(0x001D),
key_exchange = <32-byte server public value> }
// both sides now compute the same shared secret locally;
// neither private value, and not the secret, is ever sentgo deeper
Recall the split: one extension names the named groups you accept, the other carries an actual public value for one of them, and the ServerHello answers with exactly one.
Explain why the share is a guess and what the guess buys: the exchange finishes in the first two flights, at the cost of a key pair generated before anyone agreed to use it.
Show what a captured upload actually contains — both public values and both group lists in the clear — and what it still does not determine, without drifting into what that property is called.
Frame the trade being made in the first flight: bytes and key-generation work spent up front against a round trip saved on every new connection an estate opens.
A TLS 1.3 handshake has to agree a secret that both ends know and no observer can compute, and it has to do it in the first pair of flights so that application data can follow immediately. Two extensions carry that work: `supported_groups(10)`, which says what the client is willing to use, and `key_share(51)`, which puts real key material on the wire. ## Named groups: the vocabulary A **named group** is a registered identifier for one Diffie-Hellman parameter set — an elliptic curve, or a finite-field group. The ones that actually appear are few: - `x25519(0x001D)` and `x448(0x001E)` — the two Montgomery curves; - `secp256r1(0x0017)`, `secp384r1(0x0018)`, `secp521r1(0x0019)` — the NIST prime curves, `secp256r1(0x0017)` being the one usually written P-256; - `ffdhe2048(0x0100)` and `ffdhe3072(0x0101)` — fixed finite-field groups, rarely negotiated in practice. `supported_groups(10)` is a preference-ordered list of those identifiers. It carries **no key material at all** — it is a statement of willingness, nothing more. ## key_share: the speculative part `key_share(51)` in a ClientHello carries a list of `KeyShareEntry` structures. Each one pairs a `NamedGroup` with a `key_exchange` field holding the raw public value the client generated for that group: 32 bytes for `x25519(0x001D)`, an encoded curve point for the prime curves. The client has already generated the matching **private** value and is holding it in memory for the rest of the handshake. Generating a key pair is not free, and every entry adds bytes to the first flight, so a client typically offers one entry, sometimes two — its best guess at what the server will pick. The list in `key_share(51)` is therefore normally a strict subset of `supported_groups(10)`. A client must not offer an entry for a group missing from `supported_groups(10)`, and must not offer two entries for the same group. | | `supported_groups(10)` | `key_share(51)` in the ClientHello | |---|---|---| | Sent by | client | client | | Contents | named group identifiers, in preference order | `KeyShareEntry` values: group plus a public value | | Key material | none | one ephemeral public value per entry | | Cost of one more | a couple of bytes | a generated key pair and tens of bytes | ## What the server sends back The server picks one group that appears in both its own configuration and the client's offer, and its ServerHello carries `key_share(51)` with exactly **one** `KeyShareEntry` — the group it chose and the server's own ephemeral public value. There is no separate "negotiated group" field anywhere in the handshake; the group is simply whichever one that entry names. Two outcomes are worth separating: 1. The server wants a group the client sent a share for — it answers immediately, and the exchange is complete in one round trip. 2. The server shares **no** group with the client at all — no retry can help, and it aborts with `handshake_failure(40)` or `insufficient_security(71)`. (The middle case — the server wants a group the client listed but sent no share for — is what `HelloRetryRequest` exists for.) ## From the shared secret to traffic keys Each side combines its own private value with the peer's public value. Both arrive at the identical shared secret, and that secret is never put on the wire in any form. TLS does not use it directly as a key: it is extracted into the key schedule, and every subsequent value is named through `Derive-Secret` and `HKDF-Expand-Label`, whose label strings keep the handshake traffic secrets separate from the application traffic secrets. That is why the server's certificate messages are already encrypted in TLS 1.3 — the handshake keys exist from the moment the ServerHello lands. ## What an observer actually captures Picture a vote-tally transmitter uploading precinct results. Anyone recording that upload captures, in the clear: - the named group identifiers in `supported_groups(10)`; - the client's ephemeral public value in `key_share(51)`; - the server's ephemeral public value in the ServerHello. What the recording does **not** contain is either private value or the shared secret, and neither side ever sends them. The recording is complete and still does not determine the keys — which is precisely the point of putting an agreement, rather than a transported secret, in the first flight.
- Why does a TLS 1.3 client send key_share(51) speculatively instead of waiting to be told which named group to use?Because waiting costs a round trip. If the client sent only `supported_groups(10)`, the server would have to name a group and the client would then have to send its public value, pushing application data a full exchange later. Guessing right — which a sensible ordering usually does — completes the key exchange inside the first two flights.
- What does TLS do with the shared secret once both sides have computed it?It never becomes a key directly. The secret is extracted into the key schedule, and each output is derived and labelled through `Derive-Secret` and `HKDF-Expand-Label`, producing separate handshake traffic secrets — which immediately encrypt the rest of the handshake — and application traffic secrets for the data that follows.
- May a client offer more than one KeyShareEntry, and what limits that?Yes. A client may include several entries, but each one costs a generated key pair and bytes in the first flight, and it must not send two entries for the same named group or any entry for a group absent from `supported_groups(10)`. In practice one or two entries is the norm.
supported_groups(10) is the menu you say you can eat from; key_share(51) is the dish you have already started cooking on the guess that it will be chosen.
saying these in an interview costs you the question
- Says the client sends the shared secret itself inside key_share(51).
- Treats supported_groups(10) and key_share(51) as always listing the same groups.
- Believes the ServerHello returns a share for every group the client offered.
- Claims the server's private Diffie-Hellman value travels in the ServerHello.
- Assumes a client must generate a key pair for every group it supports.