November 16, 2015

Please note that this post is about draft-11 of the TLS v1.3 standard.

TLS must be fast. Adoption will greatly benefit from speeding up the initial handshake that authenticates and secures the connection. You want to get the protocol out of the way and start delivering data to visitors as soon as possible. This is crucial if we want the web to succeed at deprecating non-secure HTTP.

Let’s start by looking at full handshakes as standardized in TLS v1.2, and then continue to abbreviated handshakes that decrease connection times for resumed sessions. Once we understand the current protocol we can proceed to proposals made in the latest TLS v1.3 draft to achieve full 1-RTT and even 0-RTT handshakes.

It helps if you already have a rough idea of how TLS and Diffie-Hellman work as I can’t go into every detail. The focus of this post is on comparing current and future handshakes and I might omit a few technicalities to get basic ideas across more easily.

Full TLS 1.2 Handshake (static RSA)

Static RSA is a straightforward key exchange method, available since SSLv2. After sharing basic protocol information via the ClientHello and ServerHello messages the server sends its certificate to the client. ServerHelloDone signals that for now there will be no further messages until the client responds.

The client then encrypts the so-called premaster secret with the server’s public key found in the certificate and wraps it in a ClientKeyExchange message. ChangeCipherSpec signals that from now on messages will be encrypted. Finished , the first message to be encrypted and the client’s last message of the handshake, contains a MAC of all handshake messages exchanged thus far to prove that both parties saw the same messages, without interference from a MITM.

The server decrypts the premaster secret found in the ClientKeyExchange message using its certificate’s private key, and derives the master secret and communication keys. It then too signals a switch to encrypted communication and completes the handshake. It takes two round-trips to establish a connection.

Authentication: With static RSA key exchanges, the connection is authenticated by encrypting the premaster secret with the server certificate’s public key. Only the server in possession of the private key can decrypt, correctly derive the master secret, and send an encrypted Finished message with the right MAC.

The simplicity of static RSA has a serious drawback: it does not offer forward secrecy. If a passive adversary records all traffic to a server then every recorded TLS session can be broken later by obtaining the certificate’s private key.

This key exchange method will be removed in TLS v1.3.

Full TLS 1.2 Handshake (ephemeral DH)

A full handshake using (Elliptic Curve) Diffie-Hellman to exchange ephemeral keys is very similar to the flow of static RSA. The main difference is that after sending the certificate the server will also send a ServerKeyExchange message. This message contains either the parameters of a DH group or of an elliptic curve, paired with an ephemeral public key computed by the server.

The client too computes an ephemeral public key compatible with the given parameters and sends it to the server. Knowing their private keys and the other party’s public key both sides should now share the same premaster secret and can derive a shared master secret.

Authentication: With (EC)DH key exchanges it’s still the certificate that must be signed by a CA listed in the client’s trust store. To authenticate the connection the server will sign the parameters contained in ServerKeyExchange with the certificate’s private key. The client verifies the signature with the certificate’s public key and only then proceeds with the handshake.

Abbreviated Handshakes in TLS 1.2

Since SSLv2 clients have been able to use session identifiers as a way to resume previously established TLS/SSL sessions. Session resumption is important because a full handshake can take time: it has a high latency as it needs two round-trips and might involve expensive computation to exchange keys, or sign and verify certificates.

Session IDs, assigned by the server, are unique identifiers under which both parties store the master secret and other details of the connection they established. The client may include this ID in the ClientHello message of the next handshake to short-circuit the negotiation and reuse previous connection parameters.

If the server is willing and able to resume the session it responds with a ServerHello message including the Session ID given by the client. This handshake is effectively 1-RTT as the client can send application data immediately after the Finished message.

Sites with lots of visitors will have to manage and secure big session caches, or risk pushing out saved sessions too quickly. A setup involving multiple load-balanced servers will need to securely synchronize session caches across machines. The forward secrecy of a connection is bounded by how long session information is retained on servers.

Session tickets, created by the server and stored by the client, are blobs containing all necessary information about a connection, encrypted by a key only known to the server. If the client presents this tickets with the ClientHello message, and proves that it knows the master secret stored in the ticket, the session will be resumed.

A server willing and able to decrypt the given ticket responds with a ServerHello message including an empty SessionTicket extension, otherwise the extension would be omitted completely. As with session IDs, the client will start sending application data immediately after the Finished message to achieve 1-RTT.

To not affect the forward secrecy provided by (EC)DHE suites session ticket keys should be rotated periodically, otherwise stealing the ticket key would allow recovering recorded sessions later. In a setup with multiple load-balanced servers the main challenge here is to securely generate, rotate, and synchronize keys across machines.

Authentication: Both session resumption mechanisms retain the client’s and server’s authentication states as established in the session’s initial handshake. Neither the server nor the client have to send and verify certificates a second time, and thus can reduce connection times significantly, especially when dealing with RSA certificates.

Full Handshakes in TLS 1.3

The first good news about handshakes in TLS v1.3 is that static RSA key exchanges are no longer supported. Great! That means we can start with full handshakes using forward-secure Diffie-Hellman.

Another important change is the removal of the ChangeCipherSpec protocol (yes, it’s actually a protocol, not a message). With TLS v1.3 every message sent after ServerHello is encrypted with the so-called ephemeral secret to lock out passive adversaries very early in the game. EncryptedExtensions carries Hello extension data that must be encrypted because it’s not needed to set up secure communication.

The probably most important change with regard to 1-RTT is the removal of the ServerKeyExchange and ClientKeyExchange messages. The DH parameters and public keys are now sent in special KeyShare extensions, a new type of extension to be included in the ServerHello and ClientHello messages. Moving this data into Hello extensions keeps the handshake compatible with TLS v1.2 as it doesn’t change the order of messages.

The client sends a list of KeyShareEntry values, each consisting of a named (EC)DH group and an ephemeral public key. If the server accepts it must respond with one of the proposed groups and its own public key. If the server does not support any of the given key shares the server will request retrying the handshake or abort the connection with a fatal handshake_failure alert.

Authentication: The Diffie-Hellman parameters itself aren’t signed anymore, authentication will be a tad more explicit in TLS v1.3. The server sends a CertificateVerify message that contains a hash of all handshake message exchanged so far, signed with the certificate’s private key. The client then simply verifies the signature with the certificate’s public key.

Session Resumption in TLS 1.3 (PSK)

Session resumption via identifiers and tickets is obsolete in TLS v1.3. Both methods are replaced by a pre-shared key (PSK) mode. A PSK is established on a previous connection after the handshake is completed, and can then be presented by the client on the next visit.

The client sends one or more PSK identities as opaque blobs of data. They can be database lookup keys (similar to Session IDs), or self-encrypted and self-authenticated values (similar to Session Tickets). If the server accepts one of the given PSK identities it replies with the one it selected. The KeyShare extension is sent to allow servers to ignore PSKs and fall back to a full handshake.

Forward secrecy can be maintained by limiting the lifetime of PSK identities sensibly. Clients and servers may also choose an (EC)DHE cipher suite for PSK handshakes to provide forward secrecy for every connection, not just the whole session.

Authentication: As in TLS v1.2, the client’s and server’s authentication states are retained and both parties don’t need to exchange and verify certificates again. A regular PSK handshake initiating a new session, instead of resuming, omits certificates completely.

Session resumption still allows significantly faster handshakes when using RSA certificates and can prevent user-facing client authentication dialogs on subsequent connections. However, the fact that it requires a single round-trip just like a full handshake might make it less appealing, especially if you have an ECDSA or EdDSA certificate and do not require client authentication.

Zero-RTT Handshakes in TLS 1.3

The latest draft of the specification contains a proposal to let clients encrypt application data and include it in their first flights. On a previous connection, after the handshake completes, the server would send a ServerConfiguration message that the client can use for 0-RTT handshakes on subsequent connections. The configuration includes a configuration identifier, the server’s semi-static (EC)DH parameters, an expiration date, and other details.

With the very first TLS record the client sends its ClientHello and, changing the order of messages, directly appends application data (e.g. GET / HTTP/1.1 ). Everything after the ClientHello will be encrypted with the static secret, derived from the client’s ephemeral KeyShareEntry and the semi-static DH parameters given in the server’s configuration. The end_of_early_data alert indicates the end of the flight.

The server, if able and willing to decrypt, responds with its default set of messages and immediately appends the contents of the requested resource. That’s the same round-trip time as for an unencrypted HTTP request. All communication following the ServerHello will again be encrypted with the ephemeral secret, derived from the client’s and server’s ephemeral key shares. After exchanging Finished messages the server will be re-authenticated, and traffic encrypted with keys derived from the master secret.

Security of 0-RTT Handshakes

At first glance, 0-RTT mode seems similar to session resumption or PSK, and you might wonder why one wouldn’t merge these mechanisms. The differences however are subtle but important, and the security properties of 0-RTT handshakes are weaker than those for other kinds of TLS data:

1. To protect against replay attacks the server must incorporate a server random into the master secret. That is unfortunately not possible before the first round-trip and so the poor server can’t easily tell whether it’s a valid request or an attacker replaying a recorded conversation. Replay protection will be in place again after the ServerHello message is sent.

2. The semi-static DH share given in the server configuration, used to derive the static secret and encrypt first flight data, defies forward secrecy. We need at least one round-trip to establish the ephemeral secret. As configurations are shared between clients, and recovering the server’s DH share becomes more attractive, expiration dates should be limited sensibly. The maximum allowed validity is 7 days.

3. If the server’s DH share is compromised a MITM can tamper with the 0-RTT data sent by the client, without being detected. This does not extend to the full session as the client can retrospectively authenticate the server via the remaining handshake messages.

Defending against Replay Attacks

Thwarting replay attacks without input from the server is fundamentally very expensive. It’s important to understand that this is a generic problem, not an issue with TLS in particular, so alas one can’t just borrow another protocol’s 0-RTT model and put that into TLS.

It is possible to have servers keep a list of every ClientRandom they have received in a given time window. Upon receiving a ClientHello the server checks its list and rejects replays if necessary. This list must be globally and temporally consistent as there are possible attack vectors due to TLS’ reliable delivery guarantee if an attacker can force a server to lose its state, as well as with multiple servers in loosely-synchronized data centers.

Maintaining a consistent global state is possible, but only in some limited circumstances, namely for very sophisticated operators or situations where there is a single server with good state management. We will need something better.

Removing Anti-Replay Guarantee

A possible solution might be a TLS stack API to let applications designate certain data as replay-safe, for example GET / HTTP/1.1 assuming that GET requests against a given resource are idempotent.

let c = new TLSConnection (...); c . setReplayable0RTTData ( "GET / ..." ); c . connect ();

Applications can, before opening the connection, specify replayable 0-RTT data to send on the first flight. If the server ignores the given 0-RTT data, the TLS stack automatically replays it after the first round-trip.

Removing Reliable Delivery Guarantee

Another way of achieving the same outcome would be a TLS stack API that again lets applications designate certain data as replay-safe, but does not automatically replay if the server ignores it. The application can decide to do this manually if necessary.

let c = new TLSConnection (...); c . setUnreliable0RTTData ( "GET / ..." ); c . connect (); if ( c . delivered0RTTData ()) { // Things are cool. } else { // Try to figure out whether to replay or not. }

Both of these APIs are early proposals and the final version of the specification might look very different from what we can see above. Though, as 0-RTT handshakes are a charter goal, the working group will very likely find a way to make them work.

Summing up

TLS v1.3 will bring major improvements to handshakes, how exactly will be finalized in the coming months. They will be more private by default as all information not needed to set up a secure channel will be encrypted as early as possible. Clients will need only a single round-trip to establish secure and authenticated connections to servers they never spoke to before.

Static RSA mode will no longer be available, forward secrecy will be the default. The two session resumption standards, session identifiers and session tickets, are merged into a single PSK mode which will allow streamlining implementations.