2 min readtlssecuritynetworking

Understanding TLS 1.3 handshakes

TLS 1.3 (RFC 8446) is not just TLS 1.2 with a new version number. The handshake was redesigned to take one round trip instead of two, and almost everything after the first message is encrypted. Here is what happens, step by step.

The client guesses

In TLS 1.2 the client and server first agreed on a key exchange and only then exchanged key material. TLS 1.3 skips the negotiation: the client guesses which groups the server supports and sends public keys for them right away in the key_share extension of its ClientHello.

ClientHello
  supported_versions: TLS 1.3, TLS 1.2
  supported_groups:   X25519MLKEM768, X25519, P-256, P-384
  key_share:          X25519MLKEM768 (1216 bytes), X25519 (32 bytes)
  signature_algorithms, server_name, ALPN: h2, http/1.1 ...

The version field in the record header still says TLS 1.2 for compatibility with old middleboxes; the real version lives in supported_versions.

The server answers — and starts encrypting

The server picks one of the offered key shares, sends its own share in ServerHello, and both sides can now derive handshake keys. Everything after ServerHello is encrypted:

  1. EncryptedExtensions — ALPN result and other extensions that do not need to be public.
  2. Certificate — the server's certificate chain.
  3. CertificateVerify — a signature over the handshake so far, proving possession of the private key.
  4. Finished — a MAC over the whole transcript.

The client checks the certificate, sends its own Finished, and can send application data in the same flight. One round trip in total.

If none of the client's guesses was acceptable, the server sends a HelloRetryRequest naming the group it wants, and the client tries again. That costs an extra round trip, which is why clients send shares for the most common groups up front.

Session resumption and 0-RTT

After a handshake the server can send a session ticket. On the next connection the client presents it as a pre-shared key and may send early data before the handshake finishes. 0-RTT saves a round trip but early data can be replayed by an attacker, so servers should only accept it for idempotent requests like GETs — or not at all.

Hybrid post-quantum key exchange

Since late 2024 browsers offer X25519MLKEM768 by default: a hybrid that combines classical X25519 with ML-KEM-768 (the standardized form of Kyber). The shared secret is safe as long as either part holds, which protects today's traffic against "record now, decrypt later" attacks.

The cost is size. The ML-KEM public key makes the client's key share about 1.2 KB, so a ClientHello now often exceeds 1,500 bytes and spans two TCP segments. Poorly written middleboxes that expected the whole ClientHello in one packet broke when this rolled out; most have since been fixed.

Watching a handshake

openssl s_client -connect example.com:443 -servername example.com -tls1_3 -msg </dev/null

The -msg flag prints each handshake message. For a full picture, capture with tcpdump and open the file in Wireshark; set SSLKEYLOGFILE in a browser or curl to decrypt your own sessions.

Quick reference

TLS 1.2TLS 1.3
Full handshake2 RTT1 RTT
Certificate visible on the wireYesNo
Static RSA key exchangeAllowedRemoved
Forward secrecyOptionalAlways
ResumptionSession IDs / ticketsPSK, optional 0-RTT