Transport Layer Security
TLS 1.2
Main reference: RFC5246
Also see TLS Cypher Suites for more information on standard algorithms used in TLS for various cryptographic purposes (hashing, encryption, signing)
The overall flow of TLS 1.2 Handshake is given below:
Client Server
ClientHello -------->
ServerHello
Certificate*
ServerKeyExchange*
CertificateRequest*
<-------- ServerHelloDone
Certificate*
ClientKeyExchange
CertificateVerify*
[ChangeCipherSpec]
{Finished} -------->
[ChangeCipherSpec]
<-------- {Finished}
{Application Data} <-------> {Application Data}
Figure 1. Message flow for a full handshake
* Indicates optional or situation-dependent messages that are not
always sent.
{} Indicates messages protected using the master key derived after
a successful handshake
Note: To help avoid pipeline stalls, ChangeCipherSpec is an
independent TLS protocol content type, and is not actually a TLS
handshake message.
Reference slightly modified
Hello messages
ClientHello and ServerHello contain a 28-byte long random
(generated using PRNG) which adds randomness to each handshake. This random is later used in
- Deriving digital signature using server’s private key corresponding to public key in the certificate
- Deriving the master key
- Finished message
Server side
Server shares its x_509 certificate which contains a public key and metadata about the server (website name, org name etc). Since a certificate is signed using the Public Key Infrastructure, client can be sure that the public key really belongs to the server that it is connecting to.
In case of ephemeral diffie-hellman (DHE or ECDHE) KeyExchangeAlgorithm
, server needs to send its temporary DH params or ServerDHParams
. This is done using ServerKeyExchange
message.
The structure of ServerKeyExchange
is as follow:
struct {
select (KeyExchangeAlgorithm) {
// anonymous key exchange, no auhentication from any side
case dh_anon:
case ecdh_anon:
ServerDHParams params;
// ephemeral key-exchange algorithms
case dhe_dss:
case dhe_rsa:
case ecdhe_rsa:
case ecdhe_ecdsa:
ServerDHParams params;
digitally-signed struct {
opaque client_random[32];
opaque server_random[32];
ServerDHParams params;
} signed_params;
// static key-exchange algorithms
case rsa:
case dh_dss:
case dh_rsa:
case ecdh_rsa:
case ecdh_ecdsa:
struct {} ; /* message is omitted */
};
} ServerKeyExchange;
Reference slightly modified based on TLSECC
To prevent MITM attacks, a digital signature signed_params
is sent in the ServerKeyExchange
message. In signed_params
, the 2 random
s sent in Hello messages along with server’s DH params are signed using the private key corresponding to the public key present in the server’s certificate.
The signed_params
signature is omitted when performing an Anonymous TLS connection.
The entire ServerKeyExchange
message is omitted when KeyExchangeAlgorithm
is static in nature i.e. uses long stored public-private keys, since (ideally) only the server has access to the “private key” / “private params” corresponding to “public key” / “public params” in the certificate. Without the private key, MITM attacks are difficult.
Typically in TLS only server authentication takes place. If server also wants client to authenticate itself (given that client holds a x_509 certificate of its own), the server can send CertificateRequest
message.
Finally, ServerHelloDone
message is sent to denote end of server side messages.
Client side
If server sends CertificateRequest
, client sends its own x_509 certificate to the server.
If the client sends a certificate containing a static diffie-hellman exponent (i.e., it is doing fixed_dh client authentication), then both client and server generate the same pre-master secret at every handshake. (refer). This is fast, but really not recommended as we completely lose Forward Secrecy. In fixed_dh, ClientKeyExchange
message is empty and CertificateVerify
message is not sent.
If the client sends a certificate containing RSA public key, then client uses the corresponding RSA private key to sign the CertificateVerify
message.
If the agreed upon KeyExchangeAlgorithm
is RSA
, then client generates a random 46-byte PreMasterSecret, and encrypts it using server's RSA public key present in the server’s certificate (refer). The ClientKeyExchange
message contains this RSA EncryptedPreMasterSecret
.
If the agreed upon KeyExchangeAlgorithm
is one of the “Diffie-Hellman”s (excluding fixed_dh), then ClientKeyExchange
contains the client side public parameters of the diffie-hellman. (refer)
The CertificateVerify
is sent to provide explicit verification of a client certificate (if it is sent in the first place). It contains a digital signature over all the handshake messages sent till now (including this message). The digital signature is signed using the private key corresponding to the public key present in the client certificate.
Derivation of master key
The pre_master_secret
is either
- A PRNG generated by client in case of RSA key exchange mode
- The shared secret generated after (any of the) Diffie-Hellman key exchanges
For all key exchange methods, the same algorithm is used to convert
the pre_master_secret into the master_secret.
master_secret = PRF(pre_master_secret, "master secret",
ClientHello.random + ServerHello.random)[0..47];
The master_secret
is always exactly 48 bytes in length.
The psuedorandom function (PRF) is defined here
Start of encryption
The encryption starts only after ChangeCipherSpec
is called. Till then none of the data is encrypted by any means.
The Finished
is the first message that is encrypted using the decided encryption algorithm and the master_secret
The structure of finished message is like this (refer)
struct {
opaque verify_data[verify_data_length];
} Finished;
verify_data
PRF(master_secret, finished_label, Hash(handshake_messages))
[0..verify_data_length-1];
finished_label
For Finished messages sent by the client, the string
"client finished". For Finished messages sent by the server,
the string "server finished".
Since Finished
message’s input contains an hash over the entire handshake messages, this guarantees that the handshake messages seen by both client and server are exactly same.
After Finished messages, both client and server can talk to each other using encrypted medium.
TLS 1.3
Main reference: RFC8446
The TLS 1.3 Handshake is given below:
Client Server
Key ^ ClientHello
Exch | + key_share*
| + signature_algorithms*
| + psk_key_exchange_modes*
v + pre_shared_key* -------->
ServerHello ^ Key
+ key_share* | Exch
+ pre_shared_key* v
{EncryptedExtensions} ^ Server
{CertificateRequest*} v Params
{Certificate*} ^
{CertificateVerify*} | Auth
{Finished} v
<-------- [Application Data*]
^ {Certificate*}
Auth | {CertificateVerify*}
v {Finished} -------->
[Application Data] <-------> [Application Data]
+ Indicates noteworthy extensions sent in the
previously noted message.
* Indicates optional or situation-dependent
messages/extensions that are not always sent.
{} Indicates messages protected using keys
derived from a [sender]_handshake_traffic_secret.
[] Indicates messages protected using keys
derived from [sender]_application_traffic_secret_N.
Above diagram pretty much summarises everything we need to know about how handshake takes place.
TLS 1.3 heavily relies on Extensions, as they are present in ClientHello, ServerHello and other messages.
Since client sends either a key_share or pre_shared_key (or both) along with the ClientHello message (1st message), server has all the data necessary to generate shared secrets and start encrypting its messages. Once server sends its key_share
or pre_shared_key
, client has all the data to generated shared secrets.
Thus TLS 1.3 is done within 1 round-trip (1-RTT) instead of 2-RTT in TLS 1.2
TLS 1.3 added an improved Pre-shared Key (PSK) mechanism, which can be used for session resumptions. Addition of PSK also enables a zero-RTT mode.
TLS 1.3 has removed static RSA and static DH based key-exchange mechanisms (see Support in TLS). All handshakes provide Forward Secrecy. All key exchanges (except when using PSK) take place using Ephemeral Finite/Elliptical Diffie-Hellman.
In Ephemeral key-exchanges cases, server must authenticate itself using Certificate and CertificateVerify messages.
The Certificate
message contains servers x_509 certificate, which contains the public key using one of the Digital Signature Algorithm like RSA, ECDSA or EdDSA.
The CertificateVerify
message contains a signature, over the hash of entire handshake message upto that point, generated using the private key corresponding to the public key in the certificate..
Unlike TLS 1.2, the handshake messages after server key exchange are encrypted with keys derived from a set of 11 secrets. Refer to section 7.1 to see list of all the secrets (with how they are derived) and also section 7.3 to see how the key and iv (required for AES or ChaCha20) are derived from those secrets.
All key derivation processes use new HKDF over the existing PRF.