Interactive step-by-step TLS 1.3 handshake demonstration plus TLS 1.2 downgrade and legacy attack scenarios. See how key exchange, authentication, encryption, and integrity work together, and trace how different failures or protocol regressions break the channel.
Every HTTPS connection starts with a handshake. This demo walks through the TLS 1.3 protocol step by step, mapping each step to the four cryptographic pillars:
- ClientHello — Key Exchange begins (ECDHE key share sent)
- ServerHello — Key exchange complete; handshake traffic keys derived. Authentication not yet established.
- Certificate — Server authenticates: encrypted Certificate + CertificateVerify flight verified. Encryption already active.
- Client Finished — Integrity established: Finished authenticates the handshake transcript; both sides derive complementary client/server application traffic secrets via HKDF
- Encrypted Data — AEAD-authenticated application records flow (auth tag is integral to the cipher, not a separate HMAC)
- Done — All four pillars active. Connection secure.
Toggle scenarios to see how the handshake changes or breaks:
TLS 1.3 — Baseline & Variations
| Scenario | Effect |
|---|---|
| HelloRetryRequest | Step 2 — server rejects the client's key_share group; client retries with secp256r1, adding one RTT |
| PSK Session Resumption | Step 2 — server accepts a session ticket; ECDHE still used for forward secrecy |
| 0-RTT Early Data | Step 5 — early data sent before server Finished; replayable, bound to resumption key (not fresh ECDHE share) |
| Mutual TLS | Step 4 — client presents its own certificate after server CertificateRequest |
TLS 1.3 — Certificate & Auth Failures
| Scenario | Effect |
|---|---|
| Expired Certificate | Step 3 fails — certificate validity period has ended |
| Hostname Mismatch | Step 3 fails — certificate CN/SAN does not match the requested hostname |
| Weak Signature (SHA-1) | Step 3 fails — SHA-1 is cryptographically broken; TLS 1.3 forbids it in CertificateVerify |
| Revoked Certificate (OCSP staple) | Step 3 fails — OCSP staple embedded in Certificate message shows certStatus: revoked |
| Certificate Pinning Failure | Step 3 fails — cert is valid but SPKI hash doesn't match the pinned value |
| MITM Attempt | Step 3 fails — rogue certificate signed by an untrusted CA, or CertificateVerify invalid |
| Client Authentication Failure | Step 4 fails — server required a client certificate; client sent an empty Certificate message |
TLS 1.3 — Privacy & Extensions
| Scenario | Effect |
|---|---|
| SNI Exposed (no ECH) | Step 1 — server name visible in plaintext ClientHello extension |
| ECH Success | Step 1 — inner ClientHello encrypted with server's published public key; supersedes ESNI |
| ALPN Mismatch | Step 3 fails — client offered only h2; server supports only http/1.1; fatal no_application_protocol alert (RFC 7301 §3.3.2) |
| HSTS | Step 6 — server delivers Strict-Transport-Security in the HTTP response; browser enforces HTTPS-only for future visits |
| QUIC / HTTP/3 contrast | Step 1 — educational comparison; QUIC carries the TLS 1.3 handshake inside CRYPTO frames over UDP instead of a TLS record layer |
TLS 1.3 — Active Attacks
| Scenario | Effect |
|---|---|
| Session Ticket Theft | Step 2 — attacker replays a stolen PSK ticket to hijack the session |
| Record Tampering | Step 5 — attacker modifies an application data record; AES-256-GCM detects it via authentication tag mismatch; connection aborted |
TLS 1.2 Downgrade path
| Scenario | Effect |
|---|---|
| TLS 1.2 downgrade (RSA) | Full downgrade — RSA key exchange, no forward secrecy |
| TLS 1.2 + CBC cipher | Warning from step 3 — Lucky13/POODLE exploit CBC padding oracles; BEAST exploits predictable TLS 1.0 CBC IVs |
| TLS 1.2 + expired certificate | Step 2 fails — certificate validity check |
| Export RSA Downgrade (FREAK) | Step 2 — illustrative TLS 1.2 server silently accepts an export-grade RSA suite; downgrade succeeds with no client alert |
| Export DHE Downgrade (Logjam) | Step 2 — illustrative TLS 1.2 server sends weak 512-bit DHE parameters in ServerKeyExchange; shared secret becomes recoverable |
| Renegotiation Injection | Step 5 — pre-RFC 5746 TLS 1.2 allows attacker prefix injection via unauthenticated renegotiation |
npm install
npx playwright install chromium
npm run captures:build
python3 -m http.server 3009 --directory web
# Open http://localhost:3009The repo now includes real local capture fixtures for reproducible TLS 1.3 scenarios in:
fixtures/captures/sni.pcapfixtures/captures/hostname.pcapfixtures/captures/mitm.pcapfixtures/captures/client-auth-fail.pcapfixtures/captures/mtls.pcapfixtures/captures/hrr.pcapfixtures/captures/psk-resumption.pcapfixtures/captures/zero-rtt.pcapfixtures/captures/alpn-mismatch.pcap
The repo also keeps illustrative or synthetic fixtures for scenarios that are not straightforward to reproduce honestly on the wire with local tooling:
fixtures/captures/freak.pcapngfixtures/captures/logjam.pcapngfixtures/captures/ocsp-revoked.pcapngfixtures/captures/quic-http3.pcapng
Most are generated from source hexdumps in fixtures/capture-src/ using:
npm run captures:buildThe real local TLS 1.3 captures are generated with:
npm run captures:realThis command uses openssl s_server, openssl s_client, and tcpdump on lo0, so it requires local packet-capture privileges. These fixtures intentionally preserve loopback addresses (127.0.0.1) because they are genuine captures, not rewritten demos. The manifest at fixtures/captures/manifest.json is refreshed with tshark so both real and illustrative capture assets can be inspected reproducibly rather than edited ad hoc inside the UI.
npm test174 Playwright E2E tests covering security headers, happy path, scenario toggles, protocol trees, capture fixtures, and reset.
- Vanilla HTML/CSS/JS (single file, no framework)
- Playwright for E2E testing
- Netlify for static deployment
This demo accompanies The Codebreakers Part II: The Algorithms — an introductory information security course by Albert Hui (Security Ronin).
MIT