On the internet, messages from your device travel through many routers, ISPs, and international cables. With HTTP, data travels unprotected. Itâs like sending a transparent and unsealed envelope through the mail.
When you send sensitive information online (like credit card details or login credentials), you donât want anyone on the route to read your data, or tamper with it (for example, by injecting malware), or pretend to be the website youâre visiting (phishing). While HTTP doesnât prevent any of these from happening, HTTPS uses a security protocol that protects communication between your browser and servers worldwide. This security protocol is called TLS (Transport Layer Security), and it protects your data when it moves across the internet.
HTTPS is essentially HTTP over TLS, that is, the same HTTP request but encrypted, protected, and verified using TLS. HTTPS uses port 443 instead of the HTTP port 80.
In the OSI model, TLS sits between the application layer and the transport layer. Think of TLS as a security guard standing between your app and the network. It secures HTTP data sent by applications (in the application layer) before passing it to the web server working at the transport layer. TLS effectively provides three key guarantees:
- Confidentiality: TLS scrambles (encrypts) your data in a way that only the intended recipient can unscramble (decrypt) it. In a real world analogy, TLS locks your letter in a box and only the intended recipient has the key.
- Integrity: TLS ensures that your data canât be changed in transit and if someone tries, it would be detected. Itâs like sealing your letter with wax - if itâs broken, you know it was tampered with.
- Authentication: To make sure that youâre not sending sensitive information to an imposter, TLS verifies the websiteâs digital certificate (like an ID badge).
There are two commonly used types of encryption:
- Symmetric Encryption: Imagine locking your letter in a box that can be locked and unlocked with the same key. Both the sender and the recipient own a copy of this key.
- Asymmetric Encryption: Imagine locking a box with one key, but needing a different key to unlock it. The sender uses a locking key, and only the recipient has the correct key to open it.
In this post, weâll focus on TLS 1.3 - the latest and most secure version of the protocol. While TLS 1.3 offers the most security, its adoption is still ongoing, and older versions of TLS, like TLS 1.2, are still widely used.
How TLS 1.3 works:
Before any encrypted data gets exchanged, thereâs a TLS handshake - itâs like a conversation starter between a client (like your browser) and a server (like https://example.com
). The handshakeâs job is to:
- Help both sides prove their identity
- Let them agree on a shared secret key for encryption
- Ensure no one can eavesdrop, tamper, or replay the conversation
TLS 1.3 simplifies the handshake process to just three steps:
1. ClientHello (Client -> Server)
The client kicks things off by generating a temporary key pair (a private key + public key) using elliptic curve cryptography. It then sends the server:
- The generated public key
- A list of supported cipher suites (encryption methods)
- The highest TLS version it supports
- A random number (used later during key derivation)
All of this is unencrypted.
2. ServerHello + Encrypted Handshake (Server -> Client)
Now itâs the serverâs turn to respond.
2.1 ServerHello
The server also generates its own temporary key pair and sends back:
- The server generated public key
- The chosen cipher suite (picked from the clientâs list)
- Another random number
This part is still unencrypted.
2.2. Deriving the Handshake Keys
Both sides now have what they need to compute a shared secret. Using the Ephemeral Diffie-Hellman algorithm (usually ECDHE), each side combines:
- Its own private key
- The other partyâs public key
This results in a shared secret seed (also referred to as shared secret). Thanks to the magic of ECDHE, both sides arrive at the same secret without ever sending it over the network. Even if someone were watching, they couldnât derive the secret because private keys never leave the respective devices. Itâs like two people using a secret recipe that creates the same dish but never sharing the ingredients.
Next, both sides feed this seed into HKDF (a Key Derivation Function). HKDF uses:
- The shared secret seed
- A transcript hash (a digest of the handshake so far)
- A context label like
tls13 finished
ortls13 c hs traffic
From this, they generate encryption keys:
client_handshake_traffic_key
: Generated using the context labeltls13 c hs traffic
. Used for encrypting and decrypting client -> server messagesserver_handshake_traffic_key
: Generated using the context labeltls13 s hs traffic
. Used for encrypting and decrypting server -> client messages
These keys are computed independently on both sides but are identical due to the shared inputs.
đ Note: While the random numbers sent in ClientHello and ServerHello arenât direct inputs to computation of the raw seed, they do influence all derived secrets through the transcript hash. Thus, they ensure that each session has a unique context, even if the same ECDHE keys were (hypothetically) reused.
2.3. Encrypted Handshake Begins
The server now uses the server_handshake_traffic_key
to encrypt the next set of messages:
- Digital certificate: This is like a passport issued by a trusted authority (a Certificate Authority or CA). It includes the domain, the serverâs public key, the issuing CA, validity dates, and a digital signature (encrypted using the CAâs private key and can be unencrypted using its public key). The client browser can verify the signature using the CAâs public key, which is built into the browser.
- CertificateVerify: A digital signature encrypted using the serverâs private key. This can be decrypted using the public key of the server in the certificate.
Finished
message: This is a MAC (Message Authentication Code), generated using a specialFinished
key (derived via HKDF from the serverâs secretserver_handshake_traffic_secret
+ the handshake transcript). It proves that the server has the correct secrets and hasnât tampered with anything.
3. Finish (Client â Server)
3.1 Client Verifies Server Identity
The client decrypts the serverâs messages using the server_handshake_traffic_key
, then performs several checks:
- Is the TLS certificate still valid?
- Was it issued by a trusted CA?
- Does it match the domain name of the server?
- Is the CAâs digital signature valid?
- Can it verify the serverâs signature (CertificateVerify) using the public key from the certificate?
It also verifies the serverâs Finished MAC by generating its own version of the Finished
key and comparing the MACs.
If anythingâs off, the client aborts the handshake and shows an error like âConnection not secureâ.
3.2 Client Sends Finished
If everything checks out, the client sends its own Finished message, encrypted with the client_handshake_traffic_key
. This message:
- Proves to the server that the client has derived the correct keys
- Confirms that the client trusts the serverâs identity
Only once this is done can encrypted communication begin.
Post Handshake
With the handshake complete, both the client and server derive a new set of encryption keys, called the application traffic keys. Theyâre based on the same shared secret seed from the handshake, but with different context labels:
client_application_traffic_key
: Encrypts and decrypts messages from client -> serverserver_application_traffic_key
: Encrypts and decrypts messages from server -> client
From this point forward, all communication is encrypted using these symmetric keys. That means the same key is used to encrypt and decrypt a given message (as opposed to asymmetric cryptography, where encryption and decryption keys are different).
So, in TLS 1.3:
- Asymmetric encryption is used only during the handshake, to establish trust (certificate verification) and derive a shared secret seed.
- Symmetric encryption is used afterward for fast, secure communication.
TLS 1.3 = 1-RTT Handshake
While a TCP handshake technically involves 1.5 round trips, TLS 1.3 is known as a 1-RTT protocol, because:
- By the end of the second flight (when the server responds), both parties already have all they need to derive the application keys.
- The clientâs first actual request can be sent together with its Finished message, piggybacking on that final handshake step. So, thereâs no need to wait for another round trip before starting secure data transfer.
Other Keys Used in TLS 1.3
TLS 1.3 also defines additional types of keys beyond the main handshake and application keys:
- Resumption Key: TLS 1.3 allows the browser to resume a session without repeating the full handshake, by sending a resumption master secret (a temporary one-time use only pass that only works if the browser comes back soon). From that, the server generates a resumption key that lets the browser connect quickly and securely.
- Key Update Keys: For long-lived sessions, TLS 1.3 supports key updates. Both parties can periodically generate new application traffic keys, reducing the risk of long-term key compromise.
Whatâs the Point of So Much Randomness?
Randomness is at the heart of TLS 1.3âs security. Hereâs why it matters:
1. Unique Keys for Every Session
Each handshake generates a fresh ephemeral key pair, leading to a new shared secret seed and new encryption keys. This ensures:
- Every session is isolated.
- A hacker canât use data from one session to crack another.
2. No Replay Attacks
A replay attack is when someone reuses past messages to impersonate a client. For example, a hacker might try to log into your Gmail account by resending yesterdayâs login messages. That wonât work in TLS 1.3 because:
- Each session uses new ephemeral keys.
- That results in a different shared secret seed and different encryption keys.
- The attackerâs replayed messages wonât produce the correct Finished MAC, so the server aborts the connection immediately.
3. Forward Secrecy
TLS 1.3 ensures forward secrecy, which means:
- Encryption keys are never stored.
- If someone steals your serverâs long-term private key later, they still canât decrypt past sessions, because session specific secrets are gone forever.
Itâs like burning both the lock and the key after each session - no one can ever reconstruct them.
4. Freshness in Resumed Sessions
Even during session resumption, TLS 1.3 introduces fresh randomness. So while the client and server can skip parts of the handshake, they donât reuse the same keys. That keeps resumed sessions just as secure as new ones.
Session Resumption in TLS 1.3
When you visit a website over HTTPS, your browser and the server perform a full TLS 1.3 handshake - exchanging keys, verifying identities, and setting up encryption. This ensures privacy and trust but takes a bit of time and computing effort. If you connect soon after, TLS session resumption lets you skip parts of that handshake, saving time, computing power, and bandwidth, without compromising security.
1. Issuing the Session Ticket
Once a full handshake is complete, both client and server derive a shared secret called the resumption master secret
using HKDF and their handshake secrets. The server then derives a resumption_psk
PSK (Pre-Shared Key) from this master secret, again using HKDF.
It then sends the client a NewSessionTicket message, which includes:
ticket
: A blob that encodes the PSK (opaque to the client)ticket_lifetime
: How long the ticket is validticket_age_add
: A random offset added to the clientâs clock for securityticket_nonce
: A unique value used when deriving the PSKticket_id
: A unique identifier for this session ticket
This message is sent after the handshake and is encrypted using the application traffic keys.
The client receives and decrypts the message but it doesnât decrypt the ticket
. Instead it stores ticket_id
, ticket
and associated metadata for future use.
2. Reconnecting with PSK and Binder
When the client reconnects to the server, it sends a ClientHello message, just like it would in a full handshake (including a new public key derived using elliptic curve math). But this time, it includes a pre_shared_key
extension, containing:
- One or more PSK identities (ticket IDs from earlier sessions)
- Corresponding binders, one for each PSK
For every PSK identity, the client computes a binder using the current ClientHello transcript hash (transcript_hash
):
early_secret_seed = HKDF-Extract(salt=0, resumption_psk)
binder_key = HKDF-Expand(early_secret_seed, "resumption binder", "", num_bytes_output)
binder = HMAC(binder_key, transcript_hash)
The binder proves that the client possesses the secret resumption_psk
associated with the ticket.
3. Server Verifies the Binder
The server receives the ClientHello and checks for the pre_shared_key
extension. It examines each PSK identity in order of client preference. For each one:
- It checks if it still has the corresponding session ticket
- Verifies the ticketâs validity (authenticity, expiration, etc.)
- Recomputes the binder using its own copy of the
resumption_psk
- Compares the computed binder with the one sent by the client
If the binder matches, it means the client possesses the correct PSK, and the server selects this PSK for resumption.
The server then responds with a ServerHello, containing:
- The index of the accepted PSK identity (from the clientâs list)
- A new ephemeral public key (generated using ECDHE)
No certificate is sent during resumption as it is assumed that the client already trusts the server based on the earlier full handshake.
4. PSK + ECDHE Key Derivation and Completion
At this point, both sides have:
- Their own ephemeral key pair (freshly generated)
- The other sideâs ephemeral public key
- A shared PSK (from the session ticket)
Using these, the client and server perform a fresh ECDHE exchange, and combine it with the PSK (in multiple stages, not as a single blob) to derive:
- A new shared secret seed
- Fresh handshake traffic keys
- New application traffic keys
- Finished keys, MAC keys, etc.
The server sends a Finished message encrypted using the new server_handshake_traffic_key
, and the client replies with its own Finished. Once thatâs done, they can immediately begin encrypted application data exchange, just like in a full handshake.
TLS 1.3 may seem intimidating at first glance, but once you break it down into its handshake steps, key derivation process, and session resumption mechanics, it becomes a clear example of elegant security by design. Whether youâre building APIs, debugging HTTPS issues, or just trying to understand whatâs going on beneath the padlock icon in your browser, TLS 1.3 is a foundational piece of modern internet security.
Happy (secure) building!