The Runtime Theory
Networking

TLS 1.3 Handshake Round Trips: 1-RTT, PSK Resumption, and 0-RTT

The TLS 1.3 handshake in one round trip — key shares, the transcript signature, session resumption with PSKs, and the replay risks of 0-RTT data.

The Runtime Theory Team3 min read#tls#https#handshake#performance#security
On this page

Every HTTPS connection pays a latency tax before the first byte of your request moves: the TCP handshake, then the TLS handshake. TLS 1.3 cut the TLS half from two round trips to one — a change worth more than any CDN — and added a resumption mechanism that can cut it to zero. Here is exactly which bytes go where, what each proves, and why the fastest mode is also the riskiest.

The old bill: TLS 1.2 cost two RTTs

TLS 1.2's handshake was a conversation: ClientHello, then ServerHello and the certificate, then a client premaster secret (the key material), then a server Finished — and only then could the client send its first request. Total: two round trips beyond TCP's one. On a 50 ms RTT, that's 150 ms before your GET leaves the machine. Worse, the client's key contribution arrived in the second flight, so nothing was forward-secret until then.

TLS 1.3: one flight, both sides' keys, encrypted

TLS 1.3 (RFC 8446) collapsed the negotiation. The client guesses — it sends its ephemeral key share (ECDHE) inside the ClientHello, along with the list of cipher suites it speaks. The server picks one, sends its own key share back, and the two sides compute the same session key immediately:

text
Client → Server:  ClientHello
                  cipher suites, key_share (client's ephemeral X25519 key)
 
Server → Client:  ServerHello
                  chosen suite, key_share (server's ephemeral key)
                  EncryptedExtensions     ← now everything is encrypted
                  Certificate             ← leaf + intermediates
                  CertificateVerify       ← signs the whole transcript
                  Finished                ← first MAC under the session key
 
Client → Server:  Finished + application data  ← 1 RTT total

The critical property: the server's signature arrives under encryption, proving the transcript — every byte of both hellos — is exactly what the client sent. No attacker can swap a cipher suite or inject a certificate without breaking CertificateVerify, and because both key shares are ephemeral, the session key is forward-secret from minute zero. The first request rides in the same flight as the client's Finished: 1 RTT, always.

bash
$ openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null |
    grep -E "New|Protocol|Cipher"
New, TLSv1.3, Cipher is TLS_AES_128_GCM_SHA256

Session resumption: the PSK shortcut

Doing a full handshake on every connection is still expensive — the certificate chain alone can be several kilobytes of signatures to verify. So TLS 1.3 offers resumption: after the first handshake, the server hands the client a ticket — an opaque blob containing a session key, encrypted so only the server can read it. The client stores it and, on the next connection, offers it back as a PSK (pre-shared key):

text
Client → Server:  ClientHello + pre_shared_key (ticket from last time)
                  + key_share (fresh ephemeral — forward secrecy preserved)
Server → Client:  ServerHello + Finished (ticket accepted, session key derived)
Client → Server:  Finished + application data

The handshake is still one flight each way, but the server sends no certificate and does no signature — it just derives the key from the ticket. This is what your browser does when it reconnects minutes later: same 1 RTT, vastly less server work. The client still sends a fresh key share: the new session key mixes the PSK with fresh ephemeral material, so resumption never abandons forward secrecy.

0-RTT: the request goes before the handshake

The ticket contains more than a key — it carries an early data allowance. A resumed client may attach encrypted application data to the very first flight. The server can process it the instant the ticket validates, with no round trip at all:

text
Client → Server:  ClientHello + PSK + 0-RTT data (encrypted, replayable)
Server → Client:  ServerHello + Finished + response to the 0-RTT data

0-RTT is the difference between "new tab feels instant" and "new tab waits 50 ms". But the cost is structural:

The full cost table

SetupRound tripsNotes
TCP + TLS 1.231 TCP + 2 TLS; no forward secrecy until flight 2
TCP + TLS 1.321 TCP + 1 TLS; forward-secret immediately
TCP + TLS 1.3 + PSK21 TLS, but no certificate/signature work
QUIC (TLS 1.3 built in)1transport + crypto handshakes merged
QUIC + 0-RTT0first request encrypted with the ticket, replayable

On a 40 ms RTT, TLS 1.3 over 1.2 saves 40 ms per cold connection; 0-RTT saves another 40 ms.

The mental model

  • TLS 1.3's one RTT comes from guessing the key share — negotiation became a confirmation, not a discovery.
  • PSK resumption trades certificate verification for a server-side ticket check — same RTTs, less work, same forward secrecy.
  • 0-RTT is a replay by design. Treat early data like a cacheable GET: fine to process, never safe to execute.