> TLS HANDSHAKE
The idea in plain English: When you visit a website with https:// in
the URL, your browser and the server perform an elaborate cryptographic dance called the
TLS handshake before a single byte of your data is sent. The goal is deceptively
simple: agree on a shared secret key, verify the server's identity, and
set up a secure tunnel — all within about 100–300 milliseconds.
Think of it like meeting a stranger in a dark room. You need to: (1) confirm they are who they say they are, (2) agree on a secret code word that only you two know, and (3) make sure nobody else can listen in. All of this happens before you say anything private. That's the handshake.
Why this really exists: Before TLS, HTTP traffic was sent in plain text over the network. Anyone on the same Wi-Fi network, any ISP, any router in between could read every password, credit card number, and private message. TLS solved this by adding a cryptographic handshake layer between HTTP and TCP. Today, over 95% of all web traffic uses HTTPS — the lock icon in your browser is powered by TLS. Without it, e-commerce, online banking, and private communication would be impossible at scale.
▸ Step-by-Step Handshake (TLS 1.2 — Full)
1. ClientHello — The browser sends its supported cipher suites, a random number (client_random), and the highest TLS version it can handle. This is the opening move.
2. ServerHello — The server picks one cipher suite from the client's list, sends its own random number (server_random), and confirms the TLS version. The two sides now agree on what crypto they'll use.
3. Certificate — The server sends its X.509 certificate chain, which includes its public key (often an ECDSA or RSA key). The client verifies the certificate against a trusted CA (Certificate Authority) — this proves the server is who it claims to be.
4. ServerKeyExchange — For key exchange algorithms like ECDHE or DHE, the server sends its ephemeral Diffie–Hellman parameters (public value, curve, signature). This is what enables forward secrecy: even if the server's long-term key is later stolen, past sessions stay secure.
5. ServerHelloDone — The server signals: "I'm done with my part — your turn."
6. ClientKeyExchange — The client sends its ephemeral DH public value. Both sides now compute the pre-master secret using ECDH: shared_secret = ECDH(client_priv, server_pub) = ECDH(server_priv, client_pub).
7. ChangeCipherSpec + Finished — The client announces it's switching to encrypted mode, then sends an encrypted "Finished" message (a hash of the entire handshake). The server responds with its own ChangeCipherSpec + Finished. Both sides verify the hashes match — if they do, the handshake is complete.
Total messages: ~7 round trips (TLS 1.2). TLS 1.3 reduces this to 1 round trip (or 0 with session resumption) by combining steps.
▸ Key Derivation — From Pre-Master Secret to Session Keys
The pre-master secret (from ECDH) is never used directly to encrypt data. Instead, both sides feed it into a Pseudo-Random Function (PRF) along with the client_random and server_random to derive multiple session keys:
key_block = PRF(master_secret, "key expansion", server_random + client_random)
# The key_block is split into:
client_write_key — encrypts data from browser → server
server_write_key — encrypts data from server → browser
client_write_MAC_key — authenticates browser → server messages
server_write_MAC_key — authenticates server → browser messages
client_write_IV — initialization vector (for CBC/GCM modes)
server_write_IV — initialization vector (for CBC/GCM modes)
The PRF ensures that every session gets unique keys, even if two sessions happen to produce the same pre-master secret (astronomically unlikely, but defense-in-depth).
▸ How It Appears in Puzzles
In this puzzle website, you'll be given a partial TLS handshake transcript. Some value in the handshake is missing or obfuscated — you must reconstruct it from the other values, using the same key derivation logic that real TLS implementations use.
and the PRF output (key_block).
# Reconstruct the master secret:
master_secret = PRF(pre_master_secret, "master secret",
client_random + server_random)
# Then derive the same key_block:
key_block = PRF(master_secret, "key expansion",
server_random + client_random)
# Parse key_block into individual keys → extract the hidden message byte(s)
The puzzle may use a simplified PRF (like repeated HMAC-SHA256) rather than the full TLS PRF. The core logic — seeded derivation from shared secret + randomness — is the same.
▸ History — From SSL to TLS
- 1995 (SSL 2.0): Netscape invents the Secure Sockets Layer for early e-commerce on the web. Flawed — didn't protect message integrity.
- 1996 (SSL 3.0): Major redesign by Netscape's Taher Elgamal. Added MAC-based authentication. The standard that defined modern secure web.
- 1999 (TLS 1.0): IETF standardizes SSL 3.0 as TLS 1.0 (minor changes). The name changes from "SSL" to "TLS" for legal/standards reasons.
- 2006 (TLS 1.1): Protection against CBC attacks. Still widely compatible.
- 2008 (TLS 1.2): SHA-256 replaces MD5/SHA-1 combos. AEAD ciphers (AES-GCM) introduced. The most widely deployed TLS version for over a decade.
- 2018 (TLS 1.3): Major overhaul. Eliminated insecure options (static RSA, RC4, CBC). Reduced handshake from 2 round trips to 1. Mandated forward secrecy with ECDHE.
- Today: TLS 1.3 is the gold standard. SSL 2.0/3.0 and TLS 1.0/1.1 are deprecated by all major browsers and the IETF (RFC 8996).
▸ Real-World Applications
- Every HTTPS website: The lock icon in your browser — literally every time you see "https://", a TLS handshake just happened
- Email encryption: STARTTLS for SMTP, IMAP, and POP3 uses TLS to secure email in transit between servers and clients
- VPNs: OpenVPN uses TLS for its control channel (authentication and key exchange)
- IoT: MQTT over TLS secures communication between smart devices and their cloud backends
- API calls: Every REST API that uses HTTPS (virtually all of them) relies on a TLS handshake for every connection
- Container registries: Docker pulls images over TLS. Your docker pull command runs a TLS handshake