RFC 8446

The Transport Layer Security (TLS) Protocol Version 1.3

๐Ÿ“… published August 2018, from ietf

RFC 8446 โœจ The Transport Layer Security (TLS) Protocol Version 1.3 ๐Ÿ’–

okay bestie, let's start with the ID card ๐Ÿชช of this doc. it's an IETF document written by E. Rescorla at Mozilla, and it's dated August 2018 ๐ŸŽ€

  • Request for Comments: 8446
  • Author: E. Rescorla (Mozilla)
  • Date: August 2018
  • Category: Standards Track
  • Obsoletes: 5077, 5246, 6961
  • Updates: 5705, 6066
  • ISSN: 2070-1721
  • Stream: Internet Engineering Task Force (IETF)

Abstract ๐ŸŒธ

this doc specifies version 1.3 of the Transport Layer Security (TLS) protocol ๐Ÿ”. TLS lets client and server apps talk over the Internet in a way that's built to stop eavesdropping, tampering, and message forgery. no sneaky stuff allowed ๐Ÿ™…โ€โ™€๏ธ

it updates RFCs 5705 and 6066, and it obsoletes RFCs 5077, 5246, and 6961. it also adds new requirements for TLS 1.2 implementations, so TLS 1.2 folks, pay attention ๐Ÿ‘€โœจ

Status of This Memo ๐Ÿชง

this is an Internet Standards Track document, so it's official official ๐Ÿ‘‘

it's a product of the Internet Engineering Task Force (IETF), and it represents the consensus of the IETF community. it got public review and was approved for publication by the Internet Engineering Steering Group (IESG). if you want more on Internet Standards, check Section 2 of RFC 7841 ๐Ÿ“š

for the current status, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc8446 ๐Ÿฆ‹

Copyright (c) 2018 IETF Trust and the persons identified as the document authors. All rights reserved ๐Ÿ’œ

this doc is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, which you can find at https://trustee.ietf.org/license-info as of the publication date. please read those carefully, because they spell out your rights and restrictions for this doc ๐Ÿ“œ any Code Components pulled out of this document have to include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, same as the Simplified BSD License says ๐Ÿ’…

some of this doc may contain material from IETF Documents or IETF Contributions published or made publicly available before November 10, 2008. the people who control copyright in some of that material may not have given the IETF Trust permission to allow changes outside the IETF Standards Process ๐Ÿ™ˆ so unless you get an adequate license from those copyright holders, you're not allowed to modify this doc outside the IETF Standards Process, and you can't make derivative works of it outside the IETF Standards Process either. the exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐ŸŒˆ

Table of Contents ๐Ÿ“š

   1. Introduction ....................................................6
      1.1. Conventions and Terminology ................................7
      1.2. Major Differences from TLS 1.2 .............................8
      1.3. Updates Affecting TLS 1.2 ..................................9
   2. Protocol Overview ..............................................10
      2.1. Incorrect DHE Share .......................................14
      2.2. Resumption and Pre-Shared Key (PSK) .......................15
      2.3. 0-RTT Data ................................................17
   3. Presentation Language ..........................................19
      3.1. Basic Block Size ..........................................19
      3.2. Miscellaneous .............................................20
      3.3. Numbers ...................................................20
      3.4. Vectors ...................................................20
      3.5. Enumerateds ...............................................21
      3.6. Constructed Types .........................................22
      3.7. Constants .................................................23
      3.8. Variants ..................................................23
   4. Handshake Protocol .............................................24
      4.1. Key Exchange Messages .....................................25
           4.1.1. Cryptographic Negotiation ..........................26
           4.1.2. Client Hello .......................................27
           4.1.3. Server Hello .......................................31
           4.1.4. Hello Retry Request ................................33
      4.2. Extensions ................................................35
           4.2.1. Supported Versions .................................39
           4.2.2. Cookie .............................................40
           4.2.3. Signature Algorithms ...............................41
           4.2.4. Certificate Authorities ............................45
           4.2.5. OID Filters ........................................45
           4.2.6. Post-Handshake Client Authentication ...............47
           4.2.7. Supported Groups ...................................47
           4.2.8. Key Share ..........................................48
           4.2.9. Pre-Shared Key Exchange Modes ......................51
           4.2.10. Early Data Indication .............................52
           4.2.11. Pre-Shared Key Extension ..........................55
      4.3. Server Parameters .........................................59
           4.3.1. Encrypted Extensions ...............................60
           4.3.2. Certificate Request ................................60
      4.4. Authentication Messages ...................................61
           4.4.1. The Transcript Hash ................................63
           4.4.2. Certificate ........................................64
           4.4.3. Certificate Verify .................................69
           4.4.4. Finished ...........................................71
      4.5. End of Early Data .........................................72
      4.6. Post-Handshake Messages ...................................73
           4.6.1. New Session Ticket Message .........................73
           4.6.2. Post-Handshake Authentication ......................75
           4.6.3. Key and Initialization Vector Update ...............76
   5. Record Protocol ................................................77
      5.1. Record Layer ..............................................78
      5.2. Record Payload Protection .................................80
      5.3. Per-Record Nonce ..........................................82
      5.4. Record Padding ............................................83
      5.5. Limits on Key Usage .......................................84
   6. Alert Protocol .................................................85
      6.1. Closure Alerts ............................................87
      6.2. Error Alerts ..............................................88
   7. Cryptographic Computations .....................................90
      7.1. Key Schedule ..............................................91
      7.2. Updating Traffic Secrets ..................................94
      7.3. Traffic Key Calculation ...................................95
      7.4. (EC)DHE Shared Secret Calculation .........................95
           7.4.1. Finite Field Diffie-Hellman ........................95
           7.4.2. Elliptic Curve Diffie-Hellman ......................96
      7.5. Exporters .................................................97
   8. 0-RTT and Anti-Replay ..........................................98
      8.1. Single-Use Tickets ........................................99
      8.2. Client Hello Recording ....................................99
      8.3. Freshness Checks .........................................101
   9. Compliance Requirements .......................................102
      9.1. Mandatory-to-Implement Cipher Suites .....................102
      9.2. Mandatory-to-Implement Extensions ........................103
      9.3. Protocol Invariants ......................................104
   10. Security Considerations ......................................106
   11. IANA Considerations ..........................................106
   12. References ...................................................109
      12.1. Normative References ....................................109
      12.2. Informative References ..................................112
   Appendix A. State Machine ........................................120
     A.1. Client ....................................................120
     A.2. Server ....................................................121
   Appendix B. Protocol Data Structures and Constant Values .........122
     B.1. Record Layer ..............................................122
     B.2. Alert Messages ............................................123
     B.3. Handshake Protocol ........................................124
       B.3.1. Key Exchange Messages .................................125
       B.3.2. Server Parameters Messages ............................131
       B.3.3. Authentication Messages ...............................132
       B.3.4. Ticket Establishment ..................................132
       B.3.5. Updating Keys .........................................133
     B.4. Cipher Suites .............................................133

   Appendix C. Implementation Notes .................................134
     C.1. Random Number Generation and Seeding ......................134
     C.2. Certificates and Authentication ...........................135
     C.3. Implementation Pitfalls ...................................135
     C.4. Client Tracking Prevention ................................137
     C.5. Unauthenticated Operation .................................137
   Appendix D. Backward Compatibility ...............................138
     D.1. Negotiating with an Older Server ..........................139
     D.2. Negotiating with an Older Client ..........................139
     D.3. 0-RTT Backward Compatibility ..............................140
     D.4. Middlebox Compatibility Mode ..............................140
     D.5. Security Restrictions Related to Backward Compatibility ...141
   Appendix E. Overview of Security Properties ......................142
     E.1. Handshake .................................................142
       E.1.1. Key Derivation and HKDF ...............................145
       E.1.2. Client Authentication .................................146
       E.1.3. 0-RTT .................................................146
       E.1.4. Exporter Independence .................................146
       E.1.5. Post-Compromise Security ..............................146
       E.1.6. External References ...................................147
     E.2. Record Layer ..............................................147
       E.2.1. External References ...................................148
     E.3. Traffic Analysis ..........................................148
     E.4. Side-Channel Attacks ......................................149
     E.5. Replay Attacks on 0-RTT ...................................150
       E.5.1. Replay and Exporters ..................................151
     E.6. PSK Identity Exposure .....................................152
     E.7. Sharing PSKs ..............................................152
     E.8. Attacks on Static RSA .....................................152
   Contributors .....................................................153
   Author's Address .................................................160

1. Introduction ๐Ÿ’–

okay so the main goal of TLS is giving two communicating peers a secure channel ๐Ÿ”. the only thing it needs from the transport underneath is a reliable, in-order data stream ๐Ÿงš. specifically, the channel should have these vibes:

  • Authentication โœจ: The server side of the channel is always authenticated; the client side is optionally authenticated. Authentication can happen via asymmetric cryptography (e.g., RSA [RSA], the Elliptic Curve Digital Signature Algorithm (ECDSA) [ECDSA], or the Edwards-Curve Digital Signature Algorithm (EdDSA) [RFC8032]) or a symmetric pre-shared key (PSK).
  • Confidentiality ๐Ÿ™ˆ: Data sent over the channel after establishment is only visible to the endpoints. TLS does not hide the length of the data it transmits, though endpoints are able to pad TLS records in order to obscure lengths and improve protection against traffic analysis techniques.
  • Integrity ๐Ÿ›ก๏ธ: Data sent over the channel after establishment cannot be modified by attackers without detection.

these properties should hold even when an attacker has complete control of the network, as described in [RFC3552] ๐Ÿ’…. see Appendix E for a more complete statement of the relevant security properties, because it's a whole vibe and you deserve the full picture.

TLS has two main components:

  • A handshake protocol (Section 4) that authenticates the communicating parties, negotiates cryptographic modes and parameters, and establishes shared keying material. The handshake protocol is designed to resist tampering; an active attacker should not be able to force the peers to negotiate different parameters than they would if the connection were not under attack. ๐Ÿซถ
  • A record protocol (Section 5) that uses the parameters established by the handshake protocol to protect traffic between the communicating peers. The record protocol divides traffic up into a series of records, each of which is independently protected using the traffic keys. ๐ŸŽ€

TLS is application protocol independent, so higher-level protocols can layer on top of it transparently ๐Ÿฆ‹. the TLS standard, however, does not specify how protocols add security with TLS. how to initiate TLS handshaking and how to interpret the authentication certificates exchanged are left to the judgment of the designers and implementors of protocols that run on top of TLS. not its problem, bestie ๐Ÿ’โ€โ™€๏ธ

This document defines TLS version 1.3. While TLS 1.3 is not directly compatible with previous versions, all versions of TLS incorporate a versioning mechanism which allows clients and servers to interoperably negotiate a common version if one is supported by both peers. ๐ŸŒˆ

This document supersedes and obsoletes previous versions of TLS, including version 1.2 [RFC5246]. It also obsoletes the TLS ticket mechanism defined in [RFC5077] and replaces it with the mechanism defined in Section 2.2. Because TLS 1.3 changes the way keys are derived, it updates [RFC5705] as described in Section 7.5. It also changes how Online Certificate Status Protocol (OCSP) messages are carried and therefore updates [RFC6066] and obsoletes [RFC6961] as described in Section 4.4.2.1. so much cleanup, so many receipts ๐Ÿงพโœจ

1.1. Conventions and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. ๐Ÿ“–

here's the vocab list you need ๐Ÿ“

  • client: The endpoint initiating the TLS connection.
  • connection: A transport-layer connection between two endpoints.
  • endpoint: Either the client or server of the connection.
  • handshake: An initial negotiation between client and server that establishes the parameters of their subsequent interactions within TLS.
  • peer: An endpoint. When discussing a particular endpoint, "peer" refers to the endpoint that is not the primary subject of discussion.
  • receiver: An endpoint that is receiving records.
  • sender: An endpoint that is transmitting records.
  • server: The endpoint that did not initiate the TLS connection.

1.2. Major Differences from TLS 1.2

okay, this is the glow-up list ๐Ÿ’… it's not exhaustive and there are many minor differences, but these are the big moves:

  • The list of supported symmetric encryption algorithms has been pruned of all algorithms that are considered legacy. Those that remain are all Authenticated Encryption with Associated Data (AEAD) algorithms. The cipher suite concept has been changed to separate the authentication and key exchange mechanisms from the record protection algorithm (including secret key length) and a hash to be used with both the key derivation function and handshake message authentication code (MAC). ๐Ÿงน
  • A zero round-trip time (0-RTT) mode was added, saving a round trip at connection setup for some application data, at the cost of certain security properties. โฉ
  • Static RSA and Diffie-Hellman cipher suites have been removed; all public-key based key exchange mechanisms now provide forward secrecy. ๐Ÿ”ฅ
  • All handshake messages after the ServerHello are now encrypted. The newly introduced EncryptedExtensions message allows various extensions previously sent in the clear in the ServerHello to also enjoy confidentiality protection. ๐Ÿคซ
  • The key derivation functions have been redesigned. The new design allows easier analysis by cryptographers due to their improved key separation properties. The HMAC-based Extract-and-Expand Key Derivation Function (HKDF) is used as an underlying primitive. ๐Ÿง
  • The handshake state machine has been significantly restructured to be more consistent and to remove superfluous messages such as ChangeCipherSpec (except when needed for middlebox compatibility). ๐Ÿง 
  • Elliptic curve algorithms are now in the base spec, and new signature algorithms, such as EdDSA, are included. TLS 1.3 removed point format negotiation in favor of a single point format for each curve. ๐ŸŒธ
  • Other cryptographic improvements were made, including changing the RSA padding to use the RSA Probabilistic Signature Scheme (RSASSA-PSS), and the removal of compression, the Digital Signature Algorithm (DSA), and custom Ephemeral Diffie-Hellman (DHE) groups. ๐Ÿ™Œ
  • The TLS 1.2 version negotiation mechanism has been deprecated in favor of a version list in an extension. This increases compatibility with existing servers that incorrectly implemented version negotiation. ๐Ÿคญ
  • Session resumption with and without server-side state as well as the PSK-based cipher suites of earlier TLS versions have been replaced by a single new PSK exchange. ๐Ÿ”
  • References have been updated to point to the updated versions of RFCs, as appropriate (e.g., RFC 5280 rather than RFC 3280). ๐Ÿ“š

1.3. Updates Affecting TLS 1.2

this section is for the TLS 1.2 folks who aren't ready to jump ship yet ๐Ÿฅน this document defines several changes that optionally affect implementations of TLS 1.2, including those which do not also support TLS 1.3:

  • A version downgrade protection mechanism is described in Section 4.1.3. ๐Ÿ›ก๏ธ
  • RSASSA-PSS signature schemes are defined in Section 4.2.3. ๐Ÿ’Ž
  • The "supported_versions" ClientHello extension can be used to negotiate the version of TLS to use, in preference to the legacy_version field of the ClientHello. ๐ŸŽฏ
  • The "signature_algorithms_cert" extension allows a client to indicate which signature algorithms it can validate in X.509 certificates. ๐Ÿชช

Additionally, this document clarifies some compliance requirements for earlier versions of TLS; see Section 9.3. โœ…

2. Protocol Overview ๐Ÿฆ‹

the cryptographic parameters used by the secure channel are produced by the TLS handshake protocol. this sub-protocol of TLS is used by the client and server when first communicating with each other ๐Ÿค the handshake protocol lets peers negotiate a protocol version, select cryptographic algorithms, optionally authenticate each other, and establish shared secret keying material. once the handshake is complete, the peers use the established keys to protect the application-layer traffic. โœจ

a failure of the handshake or other protocol error triggers the termination of the connection, optionally preceded by an alert message (Section 6). ๐Ÿ’€

TLS supports three basic key exchange modes:

  • (EC)DHE (Diffie-Hellman over either finite fields or elliptic curves)
  • PSK-only
  • PSK with (EC)DHE

Figure 1 below shows the basic full TLS handshake, and honestly it's giving main character energy ๐Ÿ‘‘

    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.

               Figure 1: Message Flow for Full TLS Handshake

Okay so the handshake is basically three phases, and the diagram above shows them ๐ŸŽ€ โœจ

  • Key Exchange ๐Ÿ”‘: Lock in the shared keying material and pick the cryptographic parameters. Everything after this phase is encrypted ๐Ÿ”’
  • Server Parameters ๐Ÿงฉ: Sort out the other handshake stuff, like whether the client is getting authenticated and which application-layer protocols are supported, and so on.
  • Authentication ๐Ÿชช: The server gets authenticated (and the client can be too, if that's a thing you want), and everyone gets key confirmation and handshake integrity. Iconic ๐Ÿ‘‘

So in the Key Exchange phase, the client sends the ClientHello (Section 4.1.2) message ๐Ÿ’Œ It carries a random nonce (ClientHello.random), the protocol versions it's willing to speak, a list of symmetric cipher/HKDF hash pairs, and either a set of Diffie-Hellman key shares (in the key_share (Section 4.2.8) extension), a set of pre-shared key labels (in the pre_shared_key (Section 4.2.11) extension), or both. It can also bring along extra extensions. Some extra fields and/or messages might show up too, just for middlebox compatibility ๐Ÿงš

The server reads the ClientHello and works out the right cryptographic parameters for the connection. Then it replies with its own ServerHello (Section 4.1.3), which says which connection parameters got negotiated. The ClientHello and ServerHello together determine the shared keys ๐Ÿ—๏ธ If (EC)DHE key establishment is happening, the ServerHello has a key_share extension holding the server's ephemeral Diffie-Hellman share, and the server's share MUST be in the same group as one of the client's shares. If PSK key establishment is happening, the ServerHello has a pre_shared_key extension that says which of the client's offered PSKs got picked. Also, implementations can mix (EC)DHE and PSK together, in which case both extensions get sent ๐Ÿ’…

After that, the server sends two messages to set up the Server Parameters:

  • EncryptedExtensions: Responses to ClientHello extensions that aren't needed to figure out the cryptographic parameters, except the ones specific to individual certificates. [Section 4.3.1]
  • CertificateRequest: If certificate-based client authentication is wanted, this carries the desired parameters for that certificate. This message gets left out if client authentication isn't wanted. [Section 4.3.2]

Finally, the client and server swap Authentication messages ๐Ÿซถ TLS uses the same set of messages every single time certificate-based authentication is needed. (PSK-based authentication just happens as a side effect of key exchange, so it's giving low-key effortless.) Specifically:

  • Certificate: The endpoint's certificate and any per-certificate extensions. The server leaves this out if it's not authenticating with a certificate, and the client leaves it out if the server didn't send CertificateRequest (which tells the client it shouldn't authenticate with a certificate). Heads up: if raw public keys [RFC7250] or the cached information extension [RFC7924] are in use, this message won't hold a certificate. It'll hold some other value that matches the server's long-term key instead. [Section 4.4.2]
  • CertificateVerify: A signature over the entire handshake, made with the private key that matches the public key in the Certificate message. This message gets left out if the endpoint isn't authenticating via a certificate. [Section 4.4.3]
  • Finished: A MAC (Message Authentication Code) over the entire handshake. It gives key confirmation, binds the endpoint's identity to the exchanged keys, and, in PSK mode, also authenticates the handshake. [Section 4.4.4]

Once the client gets the server's messages, it answers with its own Authentication messages: Certificate and CertificateVerify (if requested), and Finished ๐Ÿ’–

At this point the handshake is complete โœจ The client and server derive the keying material the record layer needs, so they can swap application-layer data that's protected with authenticated encryption. Application Data MUST NOT be sent before the Finished message goes out, except as specified in Section 2.3. Also, the server might start sending Application Data before it gets the client's Authentication messages, but anything sent at that point is, of course, going to an unauthenticated peer ๐Ÿ™ˆ

2.1. Incorrect DHE Share ๐Ÿ™ƒ

If the client hasn't sent a good enough key_share extension (for example, it only includes DHE or ECDHE groups that the server doesn't accept or doesn't support), the server fixes the mismatch with a HelloRetryRequest. The client then has to restart the handshake with a proper key_share extension, as shown in Figure 2 ๐Ÿ” If no common cryptographic parameters can be negotiated, the server MUST abort the handshake with an appropriate alert.

     Client                                               Server

     ClientHello
     + key_share             -------->
                                               HelloRetryRequest
                             <--------               + key_share
     ClientHello
     + key_share             -------->
                                                     ServerHello
                                                     + key_share
                                           {EncryptedExtensions}
                                           {CertificateRequest*}
                                                  {Certificate*}
                                            {CertificateVerify*}
                                                      {Finished}
                             <--------       [Application Data*]
     {Certificate*}
     {CertificateVerify*}
     {Finished}              -------->
     [Application Data]      <------->        [Application Data]

          Figure 2: Message Flow for a Full Handshake with
                        Mismatched Parameters
Note: The handshake transcript keeps the initial ClientHello/HelloRetryRequest exchange. It doesn't get reset when the new ClientHello shows up ๐Ÿงพ

TLS also has a few optimized versions of the basic handshake, which the following sections cover ๐ŸŒธ

2.2. Resumption and Pre-Shared Key (PSK) ๐ŸŒท

PSKs can be set up out of band, but they can also be established in a previous connection and then reused to start a new one. That's what "session resumption" (or "resuming" with a PSK) is all about ๐Ÿ’œ Once a handshake finishes, the server can send the client a PSK identity that matches a unique key derived from that first handshake (see Section 4.6.1). The client can then use that PSK identity in later handshakes to negotiate the associated PSK. If the server accepts the PSK, the new connection's security context is cryptographically tied to the original one, and the key from the initial handshake bootstraps the crypto state, so there's no full handshake needed ๐Ÿฆ‹ In TLS 1.2 and below, this job was done by "session IDs" and "session tickets" [RFC5077]. Both of those mechanisms are obsoleted in TLS 1.3 ๐Ÿ’€

PSKs can be combined with (EC)DHE key exchange to get forward secrecy alongside shared keys. Or they can be used alone, but then you lose forward secrecy for the application data ๐Ÿ™…โ€โ™€๏ธ

Figure 3 shows two handshakes: the first one establishes a PSK, and the second one uses it ๐Ÿงš

       Client                                               Server

Initial Handshake:
       ClientHello
       + key_share               -------->
                                                       ServerHello
                                                       + key_share
                                             {EncryptedExtensions}
                                             {CertificateRequest*}
                                                    {Certificate*}
                                              {CertificateVerify*}
                                                        {Finished}
                                 <--------     [Application Data*]
       {Certificate*}
       {CertificateVerify*}
       {Finished}                -------->
                                 <--------      [NewSessionTicket]
       [Application Data]        <------->      [Application Data]


Subsequent Handshake:
       ClientHello
       + key_share*
       + pre_shared_key          -------->
                                                       ServerHello
                                                  + pre_shared_key
                                                      + key_share*
                                             {EncryptedExtensions}
                                                        {Finished}
                                 <--------     [Application Data*]
       {Finished}                -------->
       [Application Data]        <------->      [Application Data]

            Figure 3: Message Flow for Resumption and PSK

Since the server is authenticating with a PSK, it doesn't send a Certificate or a CertificateVerify message ๐Ÿ™Œ When a client offers resumption via a PSK, it SHOULD also send a key_share extension to the server. That way the server can turn down resumption and fall back to a full handshake if it needs to. The server answers with a pre_shared_key extension to agree on PSK key establishment, and it can (as shown here) respond with a key_share extension to do (EC)DHE key establishment too, which brings forward secrecy along for the ride ๐Ÿ’–

When PSKs are provisioned out of band, the PSK identity and the KDF hash algorithm that goes with the PSK MUST also be provisioned ๐Ÿ—‚๏ธ

Note: When you're using an out-of-band provisioned pre-shared secret, the big thing is using enough entropy when generating the key, as discussed in [RFC4086]. Deriving a shared secret from a password or other low-entropy source is not secure ๐Ÿšจ A low-entropy secret (aka a password) is open to dictionary attacks based on the PSK binder. The specified PSK authentication isn't a strong password-based authenticated key exchange, even when it's paired with Diffie-Hellman key establishment. Specifically, it doesn't stop an attacker who can watch the handshake from running a brute-force attack on the password/pre-shared key. Be careful, bestie ๐Ÿ˜ญ๐Ÿซถ

2.3. 0-RTT Data ๐Ÿš€

okay bestie, so when a client and server already share a PSK (either you got it out of band or from a previous handshake), TLS 1.3 lets the client send data on the very first flight. that's called "early data" ๐Ÿ’– the client uses the PSK to authenticate the server AND to encrypt that early data. iconic ๐Ÿฆ‹

Figure 4 shows the vibe. the 0-RTT data just gets tacked onto the 1-RTT handshake in the first flight, and the rest of the handshake is the same messages you'd get in a 1-RTT handshake with PSK resumption โœจ

     Client                                               Server

     ClientHello
     + early_data
     + key_share*
     + psk_key_exchange_modes
     + pre_shared_key
     (Application Data*)     -------->
                                                     ServerHello
                                                + pre_shared_key
                                                    + key_share*
                                           {EncryptedExtensions}
                                                   + early_data*
                                                      {Finished}
                             <--------       [Application Data*]
     (EndOfEarlyData)
     {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 client_early_traffic_secret.

           {} Indicates messages protected using keys
              derived from a [sender]_handshake_traffic_secret.

           [] Indicates messages protected using keys
              derived from [sender]_application_traffic_secret_N.

           Figure 4: Message Flow for a 0-RTT Handshake
IMPORTANT NOTE: the security properties for 0-RTT data are weaker than those for other kinds of TLS data ๐Ÿ˜ญ specifically:
  1. this data is not forward secret, because it's encrypted solely under keys derived using the offered PSK.
  2. there are no guarantees of non-replay between connections. ordinary TLS 1.3 1-RTT data gets replay protection via the server's Random value, but 0-RTT data doesn't depend on the ServerHello, so its guarantees are weaker. this is especially relevant if the data is authenticated either with TLS client authentication or inside the application protocol. the same warnings apply to any use of the early_exporter_master_secret.

0-RTT data can't be duplicated within a connection (aka the server will not process the same data twice for the same connection), and an attacker can't make 0-RTT data appear to be 1-RTT data because it's protected with different keys ๐Ÿ” Appendix E.5 has a description of the potential attacks, and Section 8 describes the mechanisms the server can use to limit the impact of replay.


3. Presentation Language โœจ

this document is all about formatting data in an external representation. we're using a very basic, kinda casually defined presentation syntax for it, so let's go ๐Ÿ’…

3.1. Basic Block Size

the representation of all data items is explicitly specified ๐ŸŽ€ the basic data block size is one byte (i.e., 8 bits). multiple-byte data items are concatenations of bytes, from left to right, from top to bottom. from the byte stream, a multi-byte item (a numeric in the following example) is formed (using C notation) by:

   value = (byte[0] << 8*(n-1)) | (byte[1] << 8*(n-2)) |
           ... | byte[n-1];

this byte ordering for multi-byte values is the commonplace network byte order, aka big-endian format ๐ŸŒˆ

3.2. Miscellaneous

comments begin with /* and end with */ ๐Ÿ’œ

optional components are denoted by enclosing them in [[ ]] (double brackets) ๐ŸŒธ

single-byte entities containing uninterpreted data are of type opaque ๐Ÿ“

a type alias T' for an existing type T is defined by:

   T T';

3.3. Numbers

the basic numeric data type is an unsigned byte (uint8). all larger numeric data types are constructed from a fixed-length series of bytes concatenated as described in Section 3.1, and they're also unsigned ๐Ÿ”ฅ the following numeric types are predefined:

   uint8 uint16[2];
   uint8 uint24[3];
   uint8 uint32[4];
   uint8 uint64[8];

all values, here and elsewhere in the specification, are transmitted in network byte (big-endian) order. so the uint32 represented by the hex bytes 01 02 03 04 is equivalent to the decimal value 16909060 ๐Ÿงš

3.4. Vectors

a vector (single-dimensioned array) is a stream of homogeneous data elements ๐Ÿฆ‹ the size of the vector may be specified at documentation time or left unspecified until runtime. either way, the length declares the number of bytes, not the number of elements, in the vector. the syntax for specifying a new type, T', that is a fixed-length vector of type T is:

   T T'[n];

here, T' occupies n bytes in the data stream, where n is a multiple of the size of T. the length of the vector is not included in the encoded stream โœจ

in the following example, Datum is defined to be three consecutive bytes that the protocol does not interpret, while Data is three consecutive Datum, consuming a total of nine bytes:

   opaque Datum[3];      /* three uninterpreted bytes */
   Datum Data[9];        /* three consecutive 3-byte vectors */

variable-length vectors are defined by specifying a subrange of legal lengths, inclusively, using the notation <floor..ceiling>. when these are encoded, the actual length precedes the vector's contents in the byte stream. the length will be in the form of a number consuming as many bytes as required to hold the vector's specified maximum (ceiling) length. a variable-length vector with an actual length field of zero is referred to as an empty vector ๐Ÿฅน

   T T'<floor..ceiling>;

in the following example, "mandatory" is a vector that must contain between 300 and 400 bytes of type opaque. it can never be empty. the actual length field consumes two bytes, a uint16, which is sufficient to represent the value 400 (see Section 3.3). similarly, "longer" can represent up to 800 bytes of data, or 400 uint16 elements, and it may be empty. its encoding will include a two-byte actual length field prepended to the vector. the length of an encoded vector must be an exact multiple of the length of a single element (e.g., a 17-byte vector of uint16 would be illegal) ๐Ÿ™ˆ

      opaque mandatory<300..400>;
            /* length field is two bytes, cannot be empty */
      uint16 longer<0..800>;
            /* zero to 400 16-bit unsigned integers */

3.5. Enumerateds

an additional sparse data type, called "enum" or "enumerated", is available ๐Ÿ’… each definition is a different type. only enumerateds of the same type may be assigned or compared. every element of an enumerated must be assigned a value, as demonstrated in the following example. since the elements of the enumerated are not ordered, they can be assigned any unique value, in any order.

   enum { e1(v1), e2(v2), ... , en(vn) [[, (n)]] } Te;

future extensions or additions to the protocol may define new values. implementations need to be able to parse and ignore unknown values unless the definition of the field states otherwise ๐Ÿ‘‘

an enumerated occupies as much space in the byte stream as would its maximal defined ordinal value. the following definition would cause one byte to be used to carry fields of type Color:

   enum { red(3), blue(5), white(7) } Color;

one may optionally specify a value without its associated tag to force the width definition without defining a superfluous element ๐ŸŒธ

in the following example, Taste will consume two bytes in the data stream but can only assume the values 1, 2, or 4 in the current version of the protocol:

   enum { sweet(1), sour(2), bitter(4), (32000) } Taste;

the names of the elements of an enumeration are scoped within the defined type. in the first example, a fully qualified reference to the second element of the enumeration would be Color.blue. such qualification is not required if the target of the assignment is well specified ๐Ÿ’–

   Color color = Color.blue;     /* overspecified, legal */
   Color color = blue;           /* correct, type implicit */

the names assigned to enumerateds do not need to be unique. the numerical value can describe a range over which the same name applies. the value includes the minimum and maximum inclusive values in that range, separated by two period characters. this is principally useful for reserving regions of the space ๐ŸŒˆ

   enum { sad(0), meh(1..254), happy(255) } Mood;

3.6. Constructed Types

structure types may be constructed from primitive types for convenience. each specification declares a new, unique type. the syntax used for definitions is much like that of C ๐ŸŽ€

   struct {
       T1 f1;
       T2 f2;
       ...
       Tn fn;
   } T;

fixed- and variable-length vector fields are allowed using the standard vector syntax. Structures V1 and V2 in the variants example (Section 3.8) demonstrate this ๐Ÿงš the fields within a structure may be qualified using the type's name, with a syntax much like that available for enumerateds. for example, T.f2 refers to the second field of the previous declaration.

3.7. Constants

fields and variables may be assigned a fixed value using "=", as in:

   struct {
       T1 f1 = 8;  /* T.f1 must always be 8 */
       T2 f2;
   } T;

3.8. Variants

defined structures may have variants based on some knowledge that is available within the environment. the selector must be an enumerated type that defines the possible variants the structure defines. each arm of the select (below) specifies the type of that variant's field and an optional field label. the mechanism by which the variant is selected at runtime is not prescribed by the presentation language ๐Ÿฆ‹

      struct {
          T1 f1;
          T2 f2;
          ....
          Tn fn;
          select (E) {
              case e1: Te1 [[fe1]];
              case e2: Te2 [[fe2]];
              ....
              case en: Ten [[fen]];
          };
      } Tv;

for example:

      enum { apple(0), orange(1) } VariantTag;

      struct {
          uint16 number;
          opaque string<0..10>; /* variable length */
      } V1;

      struct {
          uint32 number;
          opaque string[10];    /* fixed length */
      } V2;

      struct {
          VariantTag type;
          select (VariantRecord.type) {
              case apple:  V1;
              case orange: V2;
          };
      } VariantRecord;

4. Handshake Protocol ๐Ÿคโœจ

okay so the handshake protocol is where the two peers sit down and figure out their security settings together ๐Ÿ’… Handshake messages get handed off to the TLS record layer, where they're wrapped up inside one or more TLSPlaintext or TLSCiphertext structures. Those get processed and sent according to the current active connection state ๐ŸŽ€

   enum {
       client_hello(1),
       server_hello(2),
       new_session_ticket(4),
       end_of_early_data(5),
       encrypted_extensions(8),
       certificate(11),
       certificate_request(13),
       certificate_verify(15),
       finished(20),
       key_update(24),
       message_hash(254),
       (255)
   } HandshakeType;

   struct {
       HandshakeType msg_type;    /* handshake type */
       uint24 length;             /* remaining bytes in message */
       select (Handshake.msg_type) {
           case client_hello:          ClientHello;
           case server_hello:          ServerHello;
           case end_of_early_data:     EndOfEarlyData;
           case encrypted_extensions:  EncryptedExtensions;
           case certificate_request:   CertificateRequest;
           case certificate:           Certificate;
           case certificate_verify:    CertificateVerify;
           case finished:              Finished;
           case new_session_ticket:    NewSessionTicket;
           case key_update:            KeyUpdate;
       };
   } Handshake;

Protocol messages MUST be sent in the order defined in Section 4.4.1 and shown in the diagrams in Section 2 ๐Ÿฆ‹ If a peer gets a handshake message in an unexpected order, it MUST abort the handshake with an "unexpected_message" alert ๐Ÿ™ˆ no vibes-based message ordering allowed, bestie ๐Ÿ’€

New handshake message types are assigned by IANA as described in Section 11 ๐Ÿงš

4.1. Key Exchange Messages ๐Ÿ”‘

The key exchange messages are the main event ๐ŸŒˆ They're how the client and server figure out each other's security capabilities and set up shared secrets, including the traffic keys that protect the rest of the handshake and all the data ๐Ÿ’œ

4.1.1. Cryptographic Negotiation


okay so in TLS, the negotiation goes like this: the client throws four sets of options into its ClientHello ๐Ÿ’–

  • A list of cipher suites that shows the AEAD algorithm/HKDF hash pairs the client supports ๐Ÿ“
  • A "supported_groups" (Section 4.2.7) extension showing the (EC)DHE groups the client supports, plus a "key_share" (Section 4.2.8) extension carrying (EC)DHE shares for some or all of those groups โœจ
  • A "signature_algorithms" (Section 4.2.3) extension showing the signature algorithms the client can accept. A "signature_algorithms_cert" extension (Section 4.2.3) may also be added to show certificate-specific signature algorithms ๐Ÿซถ
  • A "pre_shared_key" (Section 4.2.11) extension holding a list of symmetric key identities the client knows, plus a "psk_key_exchange_modes" (Section 4.2.9) extension showing which key exchange modes may be used with PSKs ๐ŸŒธ

If the server does not select a PSK, then the first three of these options are totally independent of each other ๐Ÿ‘‘ The server picks a cipher suite, an (EC)DHE group and key share for key establishment, and a signature algorithm/certificate pair to prove who it is to the client. If there's no overlap between the received "supported_groups" and what the server supports, then the server MUST abort the handshake with a "handshake_failure" or an "insufficient_security" alert ๐Ÿ˜ญ

If the server selects a PSK, then it MUST also select a key establishment mode from the set in the client's "psk_key_exchange_modes" extension (right now that's PSK alone, or PSK with (EC)DHE) ๐Ÿงš Note that if the PSK can be used without (EC)DHE, then a mismatch in "supported_groups" doesn't have to be fatal, unlike the non-PSK case from the paragraph above โœจ

If the server selects an (EC)DHE group and the client didn't offer a compatible "key_share" extension in the initial ClientHello, the server MUST respond with a HelloRetryRequest (Section 4.1.4) message ๐Ÿ”

If the server successfully picks parameters and doesn't need a HelloRetryRequest, it shows the chosen parameters in the ServerHello like this:

  • If PSK is being used, the server sends a "pre_shared_key" extension indicating the selected key ๐ŸŽ€
  • When (EC)DHE is in use, the server also provides a "key_share" extension. If PSK isn't being used, then (EC)DHE and certificate-based authentication are always used ๐ŸŒˆ
  • When authenticating with a certificate, the server sends the Certificate (Section 4.4.2) and CertificateVerify (Section 4.4.3) messages. In TLS 1.3 as defined by this document, either a PSK or a certificate is always used, but never both ๐Ÿ™…โ€โ™€๏ธ Future documents may define how to use them together ๐Ÿ”ฎ

If the server can't negotiate a supported set of parameters (aka there's no overlap between client and server parameters), it MUST abort the handshake with either a "handshake_failure" or "insufficient_security" fatal alert (see Section 6) ๐Ÿ’€ no overlap, no handshake, periodt ๐Ÿ’…

4.1.2. Client Hello ๐ŸŽ€

okay so bestie, when a client first connects to a server, it is REQUIRED to send the ClientHello as its first TLS message. no skipping this, it's the main character intro ๐Ÿ’… The client will also send a ClientHello again when the server has responded to its ClientHello with a HelloRetryRequest. ๐Ÿฆ‹

When that happens, the client MUST send the same ClientHello without modification, except for these glow-ups โœจ:

  • If a "key_share" extension was supplied in the HelloRetryRequest, the client replaces the list of shares with a list containing a single KeyShareEntry from the indicated group. ๐Ÿซถ
  • The client removes the "early_data" extension (Section 4.2.10) if one was present. Early data is not permitted after a HelloRetryRequest. ๐Ÿ˜ญ
  • The client includes a "cookie" extension if one was provided in the HelloRetryRequest. ๐Ÿงš
  • The client updates the "pre_shared_key" extension if present by recomputing the "obfuscated_ticket_age" and binder values and (optionally) removing any PSKs which are incompatible with the server's indicated cipher suite. ๐ŸŒธ
  • Optionally, the client adds, removes, or changes the length of the "padding" extension [RFC7685]. ๐Ÿ“
  • Other modifications that may be allowed by an extension defined in the future and present in the HelloRetryRequest. ๐ŸŒˆ

Because TLS 1.3 forbids renegotiation, if a server has negotiated TLS 1.3 and receives a ClientHello at any other time, it MUST terminate the connection with an "unexpected_message" alert. no drama, no second chances ๐Ÿ’€

If a server established a TLS connection with a previous version of TLS and receives a TLS 1.3 ClientHello in a renegotiation, it MUST retain the previous protocol version. In particular, it MUST NOT negotiate TLS 1.3. ngl this is giving "stay in your lane" energy ๐Ÿ™ˆ

Structure of this message:

uint16 ProtocolVersion;
opaque Random[32];

uint8 CipherSuite[2];    /* Cryptographic suite selector */

struct {
    ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
    Random random;
    opaque legacy_session_id<0..32>;
    CipherSuite cipher_suites<2..2^16-2>;
    opaque legacy_compression_methods<1..2^8-1>;
    Extension extensions<8..2^16-1>;
} ClientHello;

legacy_version: okay so in previous versions of TLS, this field was used for version negotiation and held the highest version number the client supported. but experience has shown that many servers don't implement version negotiation properly, which leads to "version intolerance" ๐Ÿ˜ฉ where the server rejects an otherwise totally fine ClientHello just because the version number is higher than it knows. so in TLS 1.3, the client shares its version preferences in the "supported_versions" extension (Section 4.2.1) and the legacy_version field MUST be set to 0x0303, which is the version number for TLS 1.2. TLS 1.3 ClientHellos are identified as having a legacy_version of 0x0303 and a supported_versions extension present with 0x0304 as the highest version indicated therein. (See Appendix D for details about backward compatibility.) ๐Ÿ’–

random: 32 bytes generated by a secure random number generator. See Appendix C for additional information. ๐ŸŽฒโœจ

legacy_session_id: earlier TLS versions (before 1.3) had a "session resumption" feature, which has been merged with pre-shared keys in this version (see Section 2.2). ๐Ÿซง A client that has a cached session ID set by a pre-TLS 1.3 server SHOULD set this field to that value. In compatibility mode (see Appendix D.4), this field MUST be non-empty, so a client not offering a pre-TLS 1.3 session MUST generate a new 32-byte value. This value need not be random but SHOULD be unpredictable, so implementations don't fixate on one specific value (also known as ossification) ๐Ÿ™ƒ. Otherwise, it MUST be set as a zero-length vector (i.e., a zero-valued single byte length field).

cipher_suites: a list of the symmetric cipher options the client supports, specifically the record protection algorithm (including secret key length) and a hash to be used with HKDF, in descending order of client preference. Values are defined in Appendix B.4. If the list contains cipher suites that the server does not recognize, support, or wish to use, the server MUST ignore those cipher suites and process the remaining ones as usual. If the client is attempting a PSK key establishment, it SHOULD advertise at least one cipher suite indicating a Hash associated with the PSK. ๐Ÿฐ

legacy_compression_methods: before TLS 1.3, compression was a thing, and the list of supported compression methods got sent in this field. ๐Ÿ™Š For every TLS 1.3 ClientHello, this vector MUST contain exactly one byte, set to zero, which corresponds to the "null" compression method in prior versions of TLS. If a TLS 1.3 ClientHello is received with any other value in this field, the server MUST abort the handshake with an "illegal_parameter" alert. Note that TLS 1.3 servers might receive TLS 1.2 or prior ClientHellos which contain other compression methods and (if negotiating such a prior version) MUST follow the procedures for the appropriate prior version of TLS. ๐Ÿง

extensions: clients ask servers for extra features by sending data in the extensions field. The actual "Extension" format is defined in Section 4.2. In TLS 1.3, certain extensions are mandatory, because functionality moved into extensions to keep ClientHello compatible with previous versions of TLS. Servers MUST ignore unrecognized extensions. ๐ŸŒŸ

All versions of TLS allow an extensions field to optionally follow the compression_methods field. TLS 1.3 ClientHello messages always contain extensions (minimally "supported_versions", otherwise, they will be interpreted as TLS 1.2 ClientHello messages). However, TLS 1.3 servers might receive ClientHello messages without an extensions field from prior versions of TLS. The presence of extensions can be detected by determining whether there are bytes following the compression_methods field at the end of the ClientHello. Note that this method of detecting optional data differs from the normal TLS method of having a variable-length field, but it is used for compatibility with TLS before extensions were defined. TLS 1.3 servers will need to perform this check first and only attempt to negotiate TLS 1.3 if the "supported_versions" extension is present. If negotiating a version of TLS prior to 1.3, a server MUST check that the message either contains no data after legacy_compression_methods or that it contains a valid extensions block with no data following. If not, then it MUST abort the handshake with a "decode_error" alert. ๐Ÿ•ต๏ธโ€โ™€๏ธ

In the event that a client requests additional functionality using extensions and this functionality is not supplied by the server, the client MAY abort the handshake. ๐Ÿคทโ€โ™€๏ธ

After sending the ClientHello message, the client waits for a ServerHello or HelloRetryRequest message. If early data is in use, the client MAY transmit early Application Data (Section 2.3) while waiting for the next handshake message. ๐Ÿš€

4.1.3. Server Hello โœจ

okay so the server sends this message as its reply to a ClientHello ๐Ÿ’Œ but only if it can agree on a set of handshake parameters that works for both of you ๐Ÿซถ no compromise, no ServerHello ๐Ÿ’€

here's the structure ๐Ÿ‘€

struct {
    ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
    Random random;
    opaque legacy_session_id_echo<0..32>;
    CipherSuite cipher_suite;
    uint8 legacy_compression_method = 0;
    Extension extensions<6..2^16-1>;
} ServerHello;

legacy_version: back in older TLS versions, this field handled version negotiation and held the version picked for the connection ๐Ÿ•ฐ๏ธ but some middleboxes freak out when they see new values ๐Ÿ˜ค so in TLS 1.3 the server shows its version through the supported_versions extension (Section 4.2.1) instead, and the legacy_version field MUST be set to 0x0303, which is the TLS 1.2 number ๐ŸŽ€ (See Appendix D for details about backward compatibility.)

random: 32 bytes from a secure random number generator ๐ŸŽฒ (see Appendix C for more). The last 8 bytes MUST be overwritten as described below if negotiating TLS 1.2 or TLS 1.1, but the remaining bytes MUST be random ๐Ÿคž This structure is made by the server and MUST be generated independently of the ClientHello.random ๐Ÿ™…โ€โ™€๏ธ

legacy_session_id_echo: this is just the contents of the client's legacy_session_id field ๐Ÿชž and it gets echoed even if the client's value pointed at a cached pre-TLS 1.3 session that the server chose not to resume ๐Ÿ” A client that receives a legacy_session_id_echo that doesn't match what it sent in the ClientHello MUST abort the handshake with an "illegal_parameter" alert ๐Ÿšจ

cipher_suite: the single cipher suite the server picked from the list in ClientHello.cipher_suites ๐Ÿ’Ž A client that receives a cipher suite it never offered MUST abort the handshake with an "illegal_parameter" alert.

legacy_compression_method: a single byte that MUST have the value 0 ๐Ÿ™…

extensions: a list of extensions ๐Ÿงฉ The ServerHello MUST only include extensions that are required to establish the cryptographic context and negotiate the protocol version. All TLS 1.3 ServerHello messages MUST contain the "supported_versions" extension. Current ServerHello messages also contain either the "pre_shared_key" extension or the "key_share" extension, or both (when using a PSK with (EC)DHE key establishment) ๐Ÿ”‘ Other extensions (see Section 4.2) get sent separately in the EncryptedExtensions message ๐Ÿ“ฌ

For backward compatibility with middleboxes (see Appendix D.4), the HelloRetryRequest message uses the same structure as the ServerHello, but with Random set to the special value of the SHA-256 of "HelloRetryRequest" ๐Ÿช„

CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91
C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C

Upon receiving a message with type server_hello, implementations MUST first examine the Random value and, if it matches this value, process it as described in Section 4.1.4) ๐Ÿ”

TLS 1.3 has a downgrade protection mechanism tucked inside the server's random value ๐Ÿ›ก๏ธ TLS 1.3 servers which negotiate TLS 1.2 or below in response to a ClientHello MUST set the last 8 bytes of their Random value specially in their ServerHello.

If negotiating TLS 1.2, TLS 1.3 servers MUST set the last 8 bytes of their Random value to the bytes:

44 4F 57 4E 47 52 44 01

If negotiating TLS 1.1 or below, TLS 1.3 servers MUST, and TLS 1.2 servers SHOULD, set the last 8 bytes of their ServerHello.Random value to the bytes:

44 4F 57 4E 47 52 44 00

TLS 1.3 clients receiving a ServerHello indicating TLS 1.2 or below MUST check that the last 8 bytes are not equal to either of these values. TLS 1.2 clients SHOULD also check that the last 8 bytes are not equal to the second value if the ServerHello indicates TLS 1.1 or below. If a match is found, the client MUST abort the handshake with an "illegal_parameter" alert ๐Ÿšซ This mechanism gives limited protection against downgrade attacks, on top of what the Finished exchange already does ๐Ÿง Because the ServerKeyExchange, a message present in TLS 1.2 and below, includes a signature over both random values, an active attacker can't change the random values without getting caught, as long as ephemeral ciphers are used. It does not provide downgrade protection when static RSA is used ๐Ÿ’”

Note: This is a change from [RFC5246], so in practice many TLS 1.2 clients and servers will not behave as specified above. ๐Ÿ™ˆ

A legacy TLS client that performs renegotiation with TLS 1.2 or prior, and which receives a TLS 1.3 ServerHello during renegotiation, MUST abort the handshake with a "protocol_version" alert โš ๏ธ Note that renegotiation is not possible when TLS 1.3 has been negotiated ๐Ÿšซ๐Ÿ”


4.1.4. Hello Retry Request โœจ

the server sends this one in response to a ClientHello when it can find an acceptable set of parameters, but the ClientHello doesn't have enough info for the handshake to keep going ๐Ÿงฉ As discussed in Section 4.1.3, the HelloRetryRequest has the same format as a ServerHello message, and the legacy_version, legacy_session_id_echo, cipher_suite, and legacy_compression_method fields mean the same thing ๐Ÿ’… For convenience, though, we talk about "HelloRetryRequest" throughout this document as if it were its own distinct message ๐Ÿฆ‹

The server's extensions MUST contain "supported_versions". Additionally, it SHOULD contain the minimal set of extensions necessary for the client to generate a correct ClientHello pair. As with the ServerHello, a HelloRetryRequest MUST NOT contain any extensions that were not first offered by the client in its ClientHello, with the exception of optionally the "cookie" (see Section 4.2.2) extension ๐Ÿช

Upon receipt of a HelloRetryRequest, the client MUST check the legacy_version, legacy_session_id_echo, cipher_suite, and legacy_compression_method as specified in Section 4.1.3, and then process the extensions, starting with determining the version using "supported_versions". Clients MUST abort the handshake with an "illegal_parameter" alert if the HelloRetryRequest would not result in any change in the ClientHello ๐Ÿ™… If a client receives a second HelloRetryRequest in the same connection (i.e., where the ClientHello was itself in response to a HelloRetryRequest), it MUST abort the handshake with an "unexpected_message" alert ๐Ÿ˜ญ

Otherwise, the client MUST process all extensions in the HelloRetryRequest and send a second updated ClientHello. The HelloRetryRequest extensions defined in this specification are:

  • supported_versions (see Section 4.2.1) ๐ŸŒˆ
  • cookie (see Section 4.2.2) ๐Ÿช
  • key_share (see Section 4.2.8) ๐Ÿ”‘

A client which receives a cipher suite that was not offered MUST abort the handshake. Servers MUST ensure that they negotiate the same cipher suite when receiving a conformant updated ClientHello (if the server selects the cipher suite as the first step in the negotiation, then this will happen automatically) โœจ Upon receiving the ServerHello, clients MUST check that the cipher suite supplied in the ServerHello is the same as the one in the HelloRetryRequest, and otherwise abort the handshake with an "illegal_parameter" alert ๐Ÿšจ

In addition, in its updated ClientHello, the client SHOULD NOT offer any pre-shared keys associated with a hash other than that of the selected cipher suite ๐Ÿง  This lets the client avoid computing partial hash transcripts for multiple hashes in the second ClientHello ๐Ÿ’œ

The value of selected_version in the HelloRetryRequest "supported_versions" extension MUST be retained in the ServerHello, and a client MUST abort the handshake with an "illegal_parameter" alert if the value changes ๐Ÿ‘‘

4.2. Extensions ๐ŸŽ€

okay bestie, TLS messages are basically stuffed with little tag-length-value extension blobs โœจ and every single one is shaped like this:

    struct {
        ExtensionType extension_type;
        opaque extension_data<0..2^16-1>;
    } Extension;

    enum {
        server_name(0),                             /* RFC 6066 */
        max_fragment_length(1),                     /* RFC 6066 */
        status_request(5),                          /* RFC 6066 */
        supported_groups(10),                       /* RFC 8422, 7919 */
        signature_algorithms(13),                   /* RFC 8446 */
        use_srtp(14),                               /* RFC 5764 */
        heartbeat(15),                              /* RFC 6520 */
        application_layer_protocol_negotiation(16), /* RFC 7301 */
        signed_certificate_timestamp(18),           /* RFC 6962 */
        client_certificate_type(19),                /* RFC 7250 */
        server_certificate_type(20),                /* RFC 7250 */
        padding(21),                                /* RFC 7685 */
        pre_shared_key(41),                         /* RFC 8446 */
        early_data(42),                             /* RFC 8446 */
        supported_versions(43),                     /* RFC 8446 */
        cookie(44),                                 /* RFC 8446 */
        psk_key_exchange_modes(45),                 /* RFC 8446 */
        certificate_authorities(47),                /* RFC 8446 */
        oid_filters(48),                            /* RFC 8446 */
        post_handshake_auth(49),                    /* RFC 8446 */
        signature_algorithms_cert(50),              /* RFC 8446 */
        key_share(51),                              /* RFC 8446 */
        (65535)
    } ExtensionType;

Here's the breakdown:

  • extension_type tells you which extension you're looking at ๐Ÿ’…
  • extension_data holds the stuff that's specific to that extension type ๐Ÿงš

The list of extension types is maintained by IANA, as described in Section 11. Obsessed with the paperwork, I know ๐Ÿ™ˆ

Extensions are mostly a little ask-and-answer situation, but some are just a heads-up with no reply expected ๐Ÿฆ‹ The client sends its extension requests in the ClientHello message, and the server sends its extension responses in the ServerHello, EncryptedExtensions, HelloRetryRequest, and Certificate messages. The server sends extension requests in the CertificateRequest message, and a client MAY respond to that with a Certificate message. The server MAY also send unsolicited extensions in the NewSessionTicket, though the client doesn't respond directly to these.

Implementations MUST NOT send extension responses if the remote endpoint didn't send the matching extension requests. The one exception is the cookie extension in the HelloRetryRequest. If an endpoint gets an extension like that, it MUST abort the handshake with an "unsupported_extension" alert. No cap, that's a hard rule ๐Ÿ’€

The table below shows which messages each extension is allowed to appear in. The notation is: CH (ClientHello), SH (ServerHello), EE (EncryptedExtensions), CT (Certificate), CR (CertificateRequest), NST (NewSessionTicket), and HRR (HelloRetryRequest). If an implementation gets an extension it recognizes, but that extension isn't specified for the message it showed up in, it MUST abort the handshake with an "illegal_parameter" alert ๐Ÿšจ

   +--------------------------------------------------+-------------+
   | Extension                                        |     TLS 1.3 |
   +--------------------------------------------------+-------------+
   | server_name [RFC6066]                            |      CH, EE |
   |                                                  |             |
   | max_fragment_length [RFC6066]                    |      CH, EE |
   |                                                  |             |
   | status_request [RFC6066]                         |  CH, CR, CT |
   |                                                  |             |
   | supported_groups [RFC7919]                       |      CH, EE |
   |                                                  |             |
   | signature_algorithms (RFC 8446)                  |      CH, CR |
   |                                                  |             |
   | use_srtp [RFC5764]                               |      CH, EE |
   |                                                  |             |
   | heartbeat [RFC6520]                              |      CH, EE |
   |                                                  |             |
   | application_layer_protocol_negotiation [RFC7301] |      CH, EE |
   |                                                  |             |
   | signed_certificate_timestamp [RFC6962]           |  CH, CR, CT |
   |                                                  |             |
   | client_certificate_type [RFC7250]                |      CH, EE |
   |                                                  |             |
   | server_certificate_type [RFC7250]                |      CH, EE |
   |                                                  |             |
   | padding [RFC7685]                                |          CH |
   |                                                  |             |
   | key_share (RFC 8446)                             | CH, SH, HRR |
   |                                                  |             |
   | pre_shared_key (RFC 8446)                        |      CH, SH |
   |                                                  |             |
   | psk_key_exchange_modes (RFC 8446)                |          CH |
   |                                                  |             |
   | early_data (RFC 8446)                            | CH, EE, NST |
   |                                                  |             |
   | cookie (RFC 8446)                                |     CH, HRR |
   |                                                  |             |
   | supported_versions (RFC 8446)                    | CH, SH, HRR |
   |                                                  |             |
   | certificate_authorities (RFC 8446)               |      CH, CR |
   |                                                  |             |
   | oid_filters (RFC 8446)                           |          CR |
   |                                                  |             |
   | post_handshake_auth (RFC 8446)                   |          CH |
   |                                                  |             |
   | signature_algorithms_cert (RFC 8446)             |      CH, CR |
   +--------------------------------------------------+-------------+

When extensions of different types show up together, they MAY appear in any order, with one exception: "pre_shared_key" (Section 4.2.11) MUST be the last extension in the ClientHello. (It can sit anywhere in the ServerHello extensions block, though ๐Ÿงฉ) There MUST NOT be more than one extension of the same type in a given extension block.

In TLS 1.3, unlike TLS 1.2, extensions get negotiated on every handshake, even in resumption-PSK mode. 0-RTT parameters, however, are the ones negotiated in the previous handshake. If they don't match up, you might have to reject 0-RTT (see Section 4.2.10) ๐Ÿ˜ญ

There are subtle (and not so subtle) interactions between new features and existing ones in this protocol, and they can seriously drop the overall security. Keep these in mind when you design a new extension:

  • Some cases where a server doesn't agree to an extension are error conditions (for example, the handshake can't continue). Others are just refusals to support a particular feature. In general, error alerts should be used for the first kind, and a field in the server's extension response for the second.
  • Extensions should, as far as possible, be designed to prevent any attack that forces use (or non-use) of a particular feature by manipulating handshake messages. This principle should be followed regardless of whether the feature is believed to cause a security problem. Often, the fact that the extension fields are included in the inputs to the Finished message hashes will be sufficient, but extreme care is needed when the extension changes the meaning of messages sent in the handshake phase. Designers and implementors should be aware that, until the handshake has been authenticated, active attackers can modify messages and insert, remove, or replace extensions. This is a genuinely serious one, so please don't skip it ๐Ÿ”ฅ

4.2.1. Supported Versions

      struct {
          select (Handshake.msg_type) {
              case client_hello:
                   ProtocolVersion versions<2..254>;

              case server_hello: /* and HelloRetryRequest */
                   ProtocolVersion selected_version;
          };
      } SupportedVersions;

The supported_versions extension is how the client says which versions of TLS it speaks, and how the server says which one it's actually using ๐Ÿ‘‘ It's a list in preference order, with the favourite first. Implementations of this specification MUST send this extension in the ClientHello, containing all the versions of TLS they're prepared to negotiate. For this specification, that means at minimum 0x0304. If earlier versions of TLS are allowed to be negotiated, they MUST be included too.

If this extension isn't present, servers that comply with this specification and also support TLS 1.2 MUST negotiate TLS 1.2 or earlier as specified in [RFC5246], even if ClientHello.legacy_version is 0x0304 or later. Servers MAY abort the handshake upon receiving a ClientHello with legacy_version of 0x0304 or later.

If this extension is present in the ClientHello, servers MUST NOT use the ClientHello.legacy_version value for version negotiation. They MUST use only the "supported_versions" extension to figure out client preferences. Servers MUST only select a version of TLS that's present in that extension, and MUST ignore any unknown versions in it. Note that this mechanism makes it possible to negotiate a version prior to TLS 1.2 if one side supports a sparse range ๐Ÿฆ‹ Implementations of TLS 1.3 that choose to support prior versions of TLS SHOULD support TLS 1.2. Servers MUST be prepared to receive ClientHellos that include this extension but don't include 0x0304 in the list of versions.

A server that negotiates a version of TLS prior to TLS 1.3 MUST set ServerHello.version and MUST NOT send the "supported_versions" extension. A server that negotiates TLS 1.3 MUST respond by sending a "supported_versions" extension containing the selected version value (0x0304). It MUST set the ServerHello.legacy_version field to 0x0303 (TLS 1.2). Clients MUST check for this extension before processing the rest of the ServerHello. (They'll still have to parse the ServerHello in order to read the extension, though ๐Ÿ™ˆ) If this extension is present, clients MUST ignore the ServerHello.legacy_version value and MUST use only the "supported_versions" extension to determine the selected version. If the "supported_versions" extension in the ServerHello contains a version the client didn't offer, or contains a version prior to TLS 1.3, the client MUST abort the handshake with an "illegal_parameter" alert ๐Ÿšจ

Okay so bestie, cookies! ๐Ÿช Here's the struct, and it's giving very simple energy:

      struct {
          opaque cookie<1..2^16-1>;
      } Cookie;

Cookies have two main jobs, and they're both lowkey genius ๐Ÿง :

  • Allowing the server to force the client to demonstrate reachability at their apparent network address (thus providing a measure of DoS protection). This is primarily useful for non-connection-oriented transports (see [RFC6347] for an example of this). ๐Ÿ›ก๏ธ The server basically says "prove you're really at this address, bestie" before it does anything else. Protective, not extra!
  • Allowing the server to offload state to the client, thus allowing it to send a HelloRetryRequest without storing any state. The server can do this by storing the hash of the ClientHello in the HelloRetryRequest cookie (protected with some suitable integrity protection algorithm). ๐Ÿงš So the server doesn't have to hold onto anything. It tucks the hash of the ClientHello inside the cookie instead, and the cookie gets protected with a suitable integrity protection algorithm. No memory hoarding, main character server behavior ๐Ÿ’…

When sending a HelloRetryRequest, the server MAY provide a "cookie" extension to the client (this is an exception to the usual rule that the only extensions that may be sent are those that appear in the ClientHello). ๐ŸŽ€ Okay, so usually servers can only send extensions the client already asked for, but the cookie is the special exception, so it's allowed to show up uninvited. Iconic.

When sending the new ClientHello, the client MUST copy the contents of the extension received in the HelloRetryRequest into a "cookie" extension in the new ClientHello. Clients MUST NOT use cookies in their initial ClientHello in subsequent connections. ๐Ÿซถ So the client has to copy that cookie over into its new ClientHello, no skipping! And it must never bring a cookie into its initial ClientHello on later connections. Don't be that friend ๐Ÿ™ˆ

When a server is operating statelessly, it may receive an unprotected record of type change_cipher_spec between the first and second ClientHello (see Section 5). Since the server is not storing any state, this will appear as if it were the first message to be received. Servers operating statelessly MUST ignore these records. ๐Ÿ’€ So a stateless server might see a change_cipher_spec record sneaking in between the first and second ClientHello, and because it has no memory, it'll look like the very first message. Stateless servers MUST ignore these records, no exceptions, periodt ๐Ÿ˜ค


4.2.3. Signature Algorithms โœ๏ธ

Okay, the signature algorithms era is giving a lot of detail, so let's go! ๐Ÿฆ‹

TLS 1.3 provides two extensions for indicating which signature algorithms may be used in digital signatures. The "signature_algorithms_cert" extension applies to signatures in certificates, and the "signature_algorithms" extension, which originally appeared in TLS 1.2, applies to signatures in CertificateVerify messages. The keys found in certificates MUST also be of appropriate type for the signature algorithms they are used with. This is a particular issue for RSA keys and PSS signatures, as described below. If no "signature_algorithms_cert" extension is present, then the "signature_algorithms" extension also applies to signatures appearing in certificates. Clients which desire the server to authenticate itself via a certificate MUST send the "signature_algorithms" extension. If a server is authenticating via a certificate and the client has not sent a "signature_algorithms" extension, then the server MUST abort the handshake with a "missing_extension" alert (see Section 9.2). ๐Ÿšจ So the vibe check: keys in certs need to match the algorithms they're used with, especially RSA and PSS. Clients who want the server to prove itself with a cert MUST send "signature_algorithms", or the server has to bail with a "missing_extension" alert. No ghosting allowed ๐Ÿ‘ป

The "signature_algorithms_cert" extension was added to allow implementations which supported different sets of algorithms for certificates and in TLS itself to clearly signal their capabilities. TLS 1.2 implementations SHOULD also process this extension. Implementations which have the same policy in both cases MAY omit the "signature_algorithms_cert" extension. ๐ŸŒธ It exists so that implementations with different algorithm sets for certs versus TLS itself can be clear about what they support. TLS 1.2 implementations SHOULD process it too, and if you have the same policy everywhere, you MAY skip it. Simple! โœจ

The "extension_data" field of these extensions contains a SignatureSchemeList value:

      enum {
          /* RSASSA-PKCS1-v1_5 algorithms */
          rsa_pkcs1_sha256(0x0401),
          rsa_pkcs1_sha384(0x0501),
          rsa_pkcs1_sha512(0x0601),

          /* ECDSA algorithms */
          ecdsa_secp256r1_sha256(0x0403),
          ecdsa_secp384r1_sha384(0x0503),
          ecdsa_secp521r1_sha512(0x0603),

          /* RSASSA-PSS algorithms with public key OID rsaEncryption */
          rsa_pss_rsae_sha256(0x0804),
          rsa_pss_rsae_sha384(0x0805),
          rsa_pss_rsae_sha512(0x0806),

          /* EdDSA algorithms */
          ed25519(0x0807),
          ed448(0x0808),

          /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
          rsa_pss_pss_sha256(0x0809),
          rsa_pss_pss_sha384(0x080a),
          rsa_pss_pss_sha512(0x080b),

          /* Legacy algorithms */
          rsa_pkcs1_sha1(0x0201),
          ecdsa_sha1(0x0203),

          /* Reserved Code Points */
          private_use(0xFE00..0xFFFF),
          (0xFFFF)
      } SignatureScheme;

      struct {
          SignatureScheme supported_signature_algorithms<2..2^16-2>;
      } SignatureSchemeList;

Note: This enum is named "SignatureScheme" because there is already a "SignatureAlgorithm" type in TLS 1.2, which this replaces. We use the term "signature algorithm" throughout the text. ๐Ÿค“ Quick note, the enum is called "SignatureScheme" because TLS 1.2 already had a "SignatureAlgorithm" type, and this one replaces it. We still say "signature algorithm" in the text, though, so don't get confused ๐Ÿ’œ

Each SignatureScheme value lists a single signature algorithm that the client is willing to verify. The values are indicated in descending order of preference. Note that a signature algorithm takes as input an arbitrary-length message, rather than a digest. Algorithms which traditionally act on a digest should be defined in TLS to first hash the input with a specified hash algorithm and then proceed as usual. The code point groups listed above have the following meanings: ๐Ÿฆ„ Each SignatureScheme value is one signature algorithm the client is willing to verify, listed from most to least preferred. A signature algorithm takes an arbitrary-length message, not a digest. If something normally works on a digest, TLS should define it to hash the input first with a specified hash algorithm, then carry on as usual. Here's what each code point group means:

RSASSA-PKCS1-v1_5 algorithms: Indicates a signature algorithm using RSASSA-PKCS1-v1_5 [RFC8017] with the corresponding hash algorithm as defined in [SHS]. These values refer solely to signatures which appear in certificates (see Section 4.4.2.2) and are not defined for use in signed TLS handshake messages, although they MAY appear in "signature_algorithms" and "signature_algorithms_cert" for backward compatibility with TLS 1.2. ๐Ÿงฑ Basically the classic RSA PKCS1 v1.5 flavor, but only for certificate signatures. They MAY show up in those two extensions just so TLS 1.2 folks feel seen.

ECDSA algorithms: Indicates a signature algorithm using ECDSA [ECDSA], the corresponding curve as defined in ANSI X9.62 [ECDSA] and FIPS 186-4 [DSS], and the corresponding hash algorithm as defined in [SHS]. The signature is represented as a DER-encoded [X690] ECDSA-Sig-Value structure. ๐ŸŒŠ ECDSA with the matching curve and hash, and the signature gets written as a DER-encoded ECDSA-Sig-Value structure.

RSASSA-PSS RSAE algorithms: Indicates a signature algorithm using RSASSA-PSS [RFC8017] with mask generation function 1. The digest used in the mask generation function and the digest being signed are both the corresponding hash algorithm as defined in [SHS]. The length of the Salt MUST be equal to the length of the output of the digest algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID [RFC5280]. ๐ŸŽ€ RSASSA-PSS with mask generation function 1, where both the MGF digest and the signed digest use the matching hash. The Salt length MUST equal the digest output length. And if the key lives in an X.509 cert, it MUST use the rsaEncryption OID. Strict, but cute ๐Ÿ’…

EdDSA algorithms: Indicates a signature algorithm using EdDSA as defined in [RFC8032] or its successors. Note that these correspond to the "PureEdDSA" algorithms and not the "prehash" variants. ๐ŸŒˆ EdDSA as defined in [RFC8032] or whatever comes after it. These are the PureEdDSA ones, not the prehash variants, so no mixing them up!

RSASSA-PSS PSS algorithms: Indicates a signature algorithm using RSASSA-PSS [RFC8017] with mask generation function 1. The digest used in the mask generation function and the digest being signed are both the corresponding hash algorithm as defined in [SHS]. The length of the Salt MUST be equal to the length of the digest algorithm. If the public key is carried in an X.509 certificate, it MUST use the RSASSA-PSS OID [RFC5756]. When used in certificate signatures, the algorithm parameters MUST be DER encoded. If the corresponding public key's parameters are present, then the parameters in the signature MUST be identical to those in the public key. ๐Ÿ” Same RSASSA-PSS vibe with MGF1 and matching hashes, but this one uses the RSASSA-PSS OID [RFC5756] for X.509 keys. The Salt length still MUST match the digest length. For certificate signatures, the algorithm parameters MUST be DER encoded, and if the public key has parameters, the signature's parameters MUST match them exactly. Very particular, very iconic ๐Ÿ’–

Legacy algorithms: Indicates algorithms which are being deprecated because they use algorithms with known weaknesses, specifically SHA-1 which is used in this context with either (1) RSA using RSASSA-PKCS1-v1_5 or (2) ECDSA. These values refer solely to signatures which appear in certificates (see Section 4.4.2.2) and are not defined for use in signed TLS handshake messages, although they MAY appear in "signature_algorithms" and "signature_algorithms_cert" for backward compatibility with TLS 1.2. Endpoints SHOULD NOT negotiate these algorithms but are permitted to do so solely for backward compatibility. Clients offering these values MUST list them as the lowest priority (listed after all other algorithms in SignatureSchemeList). TLS 1.3 servers MUST NOT offer a SHA-1 signed certificate unless no valid certificate chain can be produced without it (see Section 4.4.2.2). ๐Ÿชฆ These are the retired-ish ones that use SHA-1, either with RSA PKCS1 v1.5 or ECDSA. They're only for certificate signatures and MAY appear in those extensions for TLS 1.2 backward compatibility. Endpoints SHOULD NOT negotiate them, and only do it for backward compatibility. If a client offers them, they MUST go at the very bottom of the list. And TLS 1.3 servers MUST NOT offer a SHA-1 signed cert unless there's truly no valid chain without it. Last resort only ๐Ÿ˜ญ

The signatures on certificates that are self-signed or certificates that are trust anchors are not validated, since they begin a certification path (see [RFC5280], Section 3.2). A certificate that begins a certification path MAY use a signature algorithm that is not advertised as being supported in the "signature_algorithms" extension. ๐ŸŒŸ Self-signed certs and trust anchors don't get their signatures validated, since they start the certification path (see [RFC5280], Section 3.2). A cert that starts a path MAY use a signature algorithm that wasn't advertised in "signature_algorithms". Plot twist!

Note that TLS 1.2 defines this extension differently. TLS 1.3 implementations willing to negotiate TLS 1.2 MUST behave in accordance with the requirements of [RFC5246] when negotiating that version. In particular: ๐Ÿ‘‘ Heads up, TLS 1.2 defines this extension differently! If a TLS 1.3 implementation is willing to negotiate TLS 1.2, it MUST follow [RFC5246] for that version. Specifically:

  • TLS 1.2 ClientHellos MAY omit this extension.
  • In TLS 1.2, the extension contained hash/signature pairs. The pairs are encoded in two octets, so SignatureScheme values have been allocated to align with TLS 1.2's encoding. Some legacy pairs are left unallocated. These algorithms are deprecated as of TLS 1.3. They MUST NOT be offered or negotiated by any implementation. In particular, MD5 [SLOTH], SHA-224, and DSA MUST NOT be used. ๐Ÿ™…โ€โ™€๏ธ In 1.2 the extension held hash/signature pairs, two octets each, and the SignatureScheme values were lined up to match. Some legacy pairs were never allocated, and they're deprecated now. No implementation MUST offer or negotiate them, and MD5 [SLOTH], SHA-224, and DSA MUST NOT be used. Absolutely not.
  • ECDSA signature schemes align with TLS 1.2's ECDSA hash/signature pairs. However, the old semantics did not constrain the signing curve. If TLS 1.2 is negotiated, implementations MUST be prepared to accept a signature that uses any curve that they advertised in the "supported_groups" extension. ๐ŸŒŠ The ECDSA schemes match TLS 1.2's ECDSA pairs, but back then the signing curve wasn't constrained. So if TLS 1.2 gets negotiated, implementations MUST be ready to accept a signature on any curve they advertised in "supported_groups".
  • Implementations that advertise support for RSASSA-PSS (which is mandatory in TLS 1.3) MUST be prepared to accept a signature using that scheme even when TLS 1.2 is negotiated. In TLS 1.2, RSASSA-PSS is used with RSA cipher suites. ๐Ÿงš If you advertise RSASSA-PSS (which is mandatory in TLS 1.3), you MUST be ready to accept a signature with it even when TLS 1.2 is negotiated. In TLS 1.2, RSASSA-PSS goes with RSA cipher suites.

4.2.4. Certificate Authorities ๐Ÿ“œ

The "certificate_authorities" extension is used to indicate the certificate authorities (CAs) which an endpoint supports and which SHOULD be used by the receiving endpoint to guide certificate selection. ๐Ÿ’ซ This extension tells the other side which certificate authorities (CAs) you support, and the receiver SHOULD use them to help pick the right certificate.

The body of the "certificate_authorities" extension consists of a CertificateAuthoritiesExtension structure.

      opaque DistinguishedName<1..2^16-1>;

      struct {
          DistinguishedName authorities<3..2^16-1>;
      } CertificateAuthoritiesExtension;

authorities: A list of the distinguished names [X501] of acceptable certificate authorities, represented in DER-encoded [X690] format. These distinguished names specify a desired distinguished name for a trust anchor or subordinate CA; thus, this message can be used to describe known trust anchors as well as a desired authorization space. ๐Ÿ“š "authorities" is the list of distinguished names [X501] for acceptable CAs, DER-encoded [X690]. They can name a trust anchor or a subordinate CA, so the message can describe known trust anchors and also the authorization space you'd like.

The client MAY send the "certificate_authorities" extension in the ClientHello message. The server MAY send it in the CertificateRequest message. ๐ŸŽŸ๏ธ The client MAY send this extension in its ClientHello, and the server MAY send it in its CertificateRequest.

The "trusted_ca_keys" extension [RFC6066], which serves a similar purpose but is more complicated, is not used in TLS 1.3 (although it may appear in ClientHello messages from clients which are offering prior versions of TLS). ๐Ÿ™ƒ "trusted_ca_keys" [RFC6066] does a similar job but is way more complicated, so TLS 1.3 doesn't use it. It can still show up in ClientHellos from clients offering older versions of TLS, though. Old friends, still around ๐ŸŒธ

4.2.5. OID Filters

Okay so the oid_filters extension is basically the server going, "hey, here's a list of OID/value pairs I'd love your certificate to match" ๐ŸŽ€โœจ. If the server sends it, it MUST only be sent in the CertificateRequest message ๐Ÿ’….

      struct {
          opaque certificate_extension_oid<1..2^8-1>;
          opaque certificate_extension_values<0..2^16-1>;
      } OIDFilter;

      struct {
          OIDFilter filters<0..2^16-1>;
      } OIDFilterExtension;
  • filters: This is a list of certificate extension OIDs [RFC5280], each with its allowed value(s), written in DER-encoded [X690] format. Some extension OIDs allow multiple values (Extended Key Usage, I'm looking at you ๐Ÿ‘€). If the server put in a non-empty filters list, the client certificate in the response MUST contain all of the specified extension OIDs that the client recognizes. For every extension OID the client recognizes, all of the specified values MUST be present in the client certificate, but the certificate MAY have other values too ๐ŸŒธ. The client MUST ignore and skip any extension OIDs it doesn't recognize, though ๐Ÿ™ˆ. If the client skipped some of the required extension OIDs and sent a certificate that doesn't satisfy the request, the server MAY, at its discretion, either keep the connection going without client authentication or abort the handshake with an "unsupported_certificate" alert ๐Ÿ˜ญ. And any given OID MUST NOT appear more than once in the filters list โœ‹.

Honestly the vibe here is "be precise, bestie." PKIX RFCs define a whole bunch of certificate extension OIDs and their value types, and depending on the type, matching values aren't necessarily bitwise-equal. So it's expected that TLS implementations will lean on their PKI libraries to do the certificate selection using those extension OIDs ๐Ÿงš.

This document defines matching rules for two standard certificate extensions from [RFC5280]:

  • The Key Usage extension in a certificate matches the request when all key usage bits asserted in the request are also asserted in the Key Usage certificate extension ๐Ÿ”‘.
  • The Extended Key Usage extension in a certificate matches the request when all key purpose OIDs present in the request are also found in the Extended Key Usage certificate extension. The special anyExtendedKeyUsage OID MUST NOT be used in the request ๐Ÿšซ.

Separate specifications may define matching rules for other certificate extensions, so keep an eye out ๐Ÿ‘‘.


4.2.6. Post-Handshake Client Authentication

The post_handshake_auth extension is how a client says it's willing to do post-handshake authentication (Section 4.6.2) ๐Ÿฆ‹. Servers MUST NOT send a post-handshake CertificateRequest to clients that don't offer this extension, and servers MUST NOT send this extension themselves ๐Ÿ™…โ€โ™€๏ธ.

      struct {} PostHandshakeAuth;

The "extension_data" field of the post_handshake_auth extension is zero length ๐Ÿ“.


4.2.7. Supported Groups

When the client sends it, the supported_groups extension tells the server which named groups the client supports for key exchange, ordered from most preferred to least preferred ๐Ÿ’–.

Note: Back in the pre-TLS 1.3 days this extension was called "elliptic_curves" and only held elliptic curve groups. See [RFC8422] and [RFC7919]. It was also used to negotiate ECDSA curves, but signature algorithms are now negotiated on their own (see Section 4.2.3) ๐Ÿงš.

The "extension_data" field of this extension holds a "NamedGroupList" value:

      enum {

          /* Elliptic Curve Groups (ECDHE) */
          secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
          x25519(0x001D), x448(0x001E),

          /* Finite Field Groups (DHE) */
          ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
          ffdhe6144(0x0103), ffdhe8192(0x0104),

          /* Reserved Code Points */
          ffdhe_private_use(0x01FC..0x01FF),
          ecdhe_private_use(0xFE00..0xFEFF),
          (0xFFFF)
      } NamedGroup;

      struct {
          NamedGroup named_group_list<2..2^16-1>;
      } NamedGroupList;
  • Elliptic Curve Groups (ECDHE): This indicates support for the matching named curve, which is defined in either FIPS 186-4 [DSS] or [RFC7748]. Values 0xFE00 through 0xFEFF are reserved for Private Use [RFC8126] ๐ŸŒˆ.
  • Finite Field Groups (DHE): This indicates support for the matching finite field group, which is defined in [RFC7919]. Values 0x01FC through 0x01FF are reserved for Private Use ๐Ÿ”ฅ.

Items in named_group_list are ordered by the sender's preferences, most preferred first. Main character energy ๐Ÿ‘‘.

As of TLS 1.3, servers are allowed to send the supported_groups extension to the client ๐Ÿ’…. Clients MUST NOT act upon any information found in supported_groups before the handshake has successfully completed. BUT they MAY use what they learned from a successfully completed handshake to change which groups they use in their key_share extension in later connections ๐Ÿง . If the server has a group it prefers over the ones in the key_share extension but is still happy to accept the ClientHello, it SHOULD send supported_groups to update the client's view of its preferences. This extension SHOULD contain all groups the server supports, whether or not they're currently supported by the client ๐Ÿซถ.


4.2.8. Key Share

The key_share extension carries the endpoint's cryptographic parameters ๐Ÿ”.

Clients MAY send an empty client_shares vector to ask the server to pick the group, but that costs an extra round trip (see Section 4.1.4) ๐Ÿฆ‹.

      struct {
          NamedGroup group;
          opaque key_exchange<1..2^16-1>;
      } KeyShareEntry;
  • group: The named group for the key being exchanged ๐ŸŽ€.
  • key_exchange: The key exchange information. What goes in here is determined by the specified group and its matching definition. Finite Field Diffie-Hellman [DH76] parameters are covered in Section 4.2.8.1, and Elliptic Curve Diffie-Hellman parameters are covered in Section 4.2.8.2 โœจ.

In the ClientHello message, the "extension_data" field of this extension holds a "KeyShareClientHello" value:

      struct {
          KeyShareEntry client_shares<0..2^16-1>;
      } KeyShareClientHello;
  • client_shares: A list of offered KeyShareEntry values, in descending order of client preference ๐Ÿ’œ.

This vector MAY be empty if the client is requesting a HelloRetryRequest. Each KeyShareEntry value MUST correspond to a group offered in the supported_groups extension, and MUST appear in the same order. The values, however, MAY be a non-contiguous subset of the supported_groups extension, and they MAY leave out the most preferred groups. That could happen if the most preferred groups are brand new and not supported in enough places to make pregenerating key shares for them worth it ๐Ÿ™ˆ.

Clients can offer as many KeyShareEntry values as the number of supported groups they're offering, with each one representing a single set of key exchange parameters. For instance, a client might offer shares for several elliptic curves or multiple FFDHE groups ๐ŸŒธ. The key_exchange values for each KeyShareEntry MUST be generated independently. Clients MUST NOT offer multiple KeyShareEntry values for the same group. Clients MUST NOT offer any KeyShareEntry values for groups that aren't listed in the client's supported_groups extension. Servers MAY check for violations of these rules and abort the handshake with an "illegal_parameter" alert if one happens ๐Ÿšจ.

In a HelloRetryRequest message, the "extension_data" field of this extension holds a KeyShareHelloRetryRequest value:

      struct {
          NamedGroup selected_group;
      } KeyShareHelloRetryRequest;
  • selected_group: The mutually supported group the server plans to negotiate, and the group it's asking a retried ClientHello/KeyShare for ๐Ÿ“.

When the client gets this extension in a HelloRetryRequest, it MUST verify two things. One, the selected_group field has to correspond to a group that was provided in the supported_groups extension of the original ClientHello. Two, the selected_group field must NOT correspond to a group that was provided in the key_share extension of the original ClientHello. If either of these checks fails, the client MUST abort the handshake with an "illegal_parameter" alert ๐Ÿ˜ฌ. Otherwise, when sending the new ClientHello, the client MUST replace the original key_share extension with one that contains only a new KeyShareEntry for the group named in the selected_group field of the triggering HelloRetryRequest ๐Ÿ”.

In a ServerHello message, the "extension_data" field of this extension holds a KeyShareServerHello value:

      struct {
          KeyShareEntry server_share;
      } KeyShareServerHello;
  • server_share: A single KeyShareEntry value that's in the same group as one of the client's shares ๐Ÿ’–.

If the connection uses (EC)DHE key establishment, servers offer exactly one KeyShareEntry in the ServerHello. This value MUST be in the same group as the KeyShareEntry the client offered and the server selected for the negotiated key exchange. Servers MUST NOT send a KeyShareEntry for any group that isn't indicated in the client's supported_groups extension, and MUST NOT send a KeyShareEntry when using the "psk_ke" PskKeyExchangeMode ๐Ÿ™…โ€โ™€๏ธ. If the connection uses (EC)DHE key establishment and the client received a HelloRetryRequest containing a key_share extension, the client MUST verify that the selected NamedGroup in the ServerHello is the same as the one in the HelloRetryRequest. If this check fails, the client MUST abort the handshake with an "illegal_parameter" alert ๐Ÿ’€.


4.2.8.1. Diffie-Hellman Parameters

Diffie-Hellman [DH76] parameters for both clients and servers are encoded in the opaque key_exchange field of a KeyShareEntry inside a KeyShare structure ๐Ÿง. The opaque value holds the Diffie-Hellman public value (Y = g^X mod p) for the specified group (see [RFC7919] for group definitions). It's encoded as a big-endian integer and padded on the left with zeros to the size of p in bytes ๐ŸŒŠ.

Note: For a given Diffie-Hellman group, the padding means every public key ends up the same length ๐Ÿ“.

Peers MUST validate each other's public key Y by making sure that 1 < Y < p-1. This check makes sure the remote peer is behaving properly and isn't trying to push the local system into a small subgroup ๐Ÿ›ก๏ธ๐Ÿ˜ค.

4.2.8.2. ECDHE Parameters

Okay so ECDHE parameters for both clients AND servers get packed into the opaque key_exchange field of a KeyShareEntry inside a KeyShare structure ๐Ÿซถโœจ

For secp256r1, secp384r1, and secp521r1, the contents are the serialized value of this struct:

   struct {
       uint8 legacy_form = 4;
       opaque X[coordinate_length];
       opaque Y[coordinate_length];
   } UncompressedPointRepresentation;

X and Y are the binary representations of the x and y values, in network byte order ๐Ÿ’…. There are no internal length markers, so each number just takes up as many octets as the curve parameters say it should. For P-256, X and Y each use 32 octets, padded on the left with zeros if needed ๐ŸŒธ. For P-384 they take 48 octets each, and for P-521 they take 66 octets each ๐Ÿฆ‹.

For the curves secp256r1, secp384r1, and secp521r1, peers MUST validate each other's public value Q by making sure the point is a valid point on the elliptic curve ๐Ÿ˜ญ. The validation procedures are defined in Section 4.3.7 of [ECDSA] and also in Section 5.6.2.3 of [KEYAGREEMENT]. The process has three steps: (1) check that Q is not the point at infinity (O), (2) check that for Q = (x, y) both integers x and y are in the correct interval, and (3) make sure (x, y) is a correct solution to the elliptic curve equation ๐Ÿ”ฅ. For these curves, implementors do NOT need to verify membership in the correct subgroup, so no extra work there ๐Ÿ™ˆ.

For X25519 and X448, the contents of the public value are the byte string inputs and outputs of the corresponding functions defined in [RFC7748]: 32 bytes for X25519 and 56 bytes for X448 ๐Ÿงš.

Note: Versions of TLS prior to 1.3 permitted point format negotiation. TLS 1.3 removes this feature in favor of a single point format for each curve ๐Ÿ’€.

4.2.9. Pre-Shared Key Exchange Modes

Okay bestie, so in order to use PSKs, clients MUST also send a psk_key_exchange_modes extension ๐ŸŽ€. The semantics are that the client only supports using PSKs with these modes, and that restricts both the PSKs offered in this ClientHello AND the ones the server might hand out via NewSessionTicket ๐Ÿ‘‘.

A client MUST provide a psk_key_exchange_modes extension if it offers a pre_shared_key extension. If clients offer pre_shared_key WITHOUT a psk_key_exchange_modes extension, servers MUST abort the handshake ๐Ÿ˜ญ. Servers MUST NOT select a key exchange mode that the client didn't list. This extension also restricts the modes used for PSK resumption. Servers SHOULD NOT send NewSessionTicket with tickets that aren't compatible with the advertised modes; however, if a server does anyway, the only impact is that the client's resumption attempts fail ๐Ÿ’….

The server MUST NOT send a psk_key_exchange_modes extension ๐Ÿ™….

   enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;

   struct {
       PskKeyExchangeMode ke_modes<1..255>;
   } PskKeyExchangeModes;
  • psk_ke: PSK-only key establishment. In this mode, the server MUST NOT supply a key_share value ๐Ÿซ .
  • psk_dhe_ke: PSK with (EC)DHE key establishment. In this mode, the client and server MUST supply key_share values as described in Section 4.2.8 ๐ŸŒˆ.

Any future values that get allocated must make sure the transmitted protocol messages unambiguously identify which mode the server picked. Right now, that's indicated by the presence of the key_share in the ServerHello ๐Ÿฆ‹.

4.2.10. Early Data Indication

Okay so when a PSK is used AND early data is allowed for that PSK, the client can send Application Data in its first flight of messages โœจ. If the client chooses to do this, it MUST supply both the pre_shared_key and early_data extensions ๐ŸŽ€.

The extension_data field of this extension contains an EarlyDataIndication value ๐Ÿงš.

   struct {} Empty;

   struct {
       select (Handshake.msg_type) {
           case new_session_ticket:   uint32 max_early_data_size;
           case client_hello:         Empty;
           case encrypted_extensions: Empty;
       };
   } EarlyDataIndication;

See Section 4.6.1 for details on how the max_early_data_size field is used ๐ŸŒธ.

The parameters for the 0-RTT data (version, symmetric cipher suite, Application-Layer Protocol Negotiation (ALPN) [RFC7301] protocol, and so on) are the ones associated with the PSK in use ๐Ÿ’œ. For externally provisioned PSKs, the associated values are the ones provisioned along with the key. For PSKs established via a NewSessionTicket message, the associated values are the ones negotiated in the connection that established the PSK. The PSK used to encrypt the early data MUST be the first PSK listed in the client's pre_shared_key extension ๐Ÿ”.

For PSKs provisioned via NewSessionTicket, a server MUST validate that the ticket age for the selected PSK identity (computed by subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age modulo 2^32) is within a small tolerance of the time since the ticket was issued (see Section 8) โฐ. If it isn't, the server SHOULD proceed with the handshake but reject 0-RTT, and SHOULD NOT take any other action that assumes this ClientHello is fresh ๐Ÿ™ˆ.

0-RTT messages sent in the first flight have the same (encrypted) content types as messages of the same type sent in other flights (handshake and application_data), but they're protected under different keys ๐Ÿ—๏ธ. After receiving the server's Finished message, if the server has accepted early data, an EndOfEarlyData message will be sent to indicate the key change ๐Ÿ’ซ. This message is encrypted with the 0-RTT traffic keys.

A server that receives an early_data extension MUST behave in one of three ways ๐Ÿ‘‘:

  • Ignore the extension and return a regular 1-RTT response. The server then skips past early data by attempting to deprotect received records using the handshake traffic key, discarding records that fail deprotection (up to the configured max_early_data_size). Once a record is deprotected successfully, it's treated as the start of the client's second flight, and the server proceeds as with an ordinary 1-RTT handshake ๐Ÿฆ‹.
  • Request that the client send another ClientHello by responding with a HelloRetryRequest. A client MUST NOT include the early_data extension in its followup ClientHello. The server then ignores early data by skipping all records with an external content type of "application_data" (indicating that they are encrypted), up to the configured max_early_data_size ๐Ÿ™ƒ.
  • Return its own early_data extension in EncryptedExtensions, indicating that it intends to process the early data. It is NOT possible for the server to accept only a subset of the early data messages. Even though the server sends a message accepting early data, the actual early data may already be in flight by the time the server generates this message ๐Ÿ˜ณ.

In order to accept early data, the server MUST have accepted a PSK cipher suite and selected the first key offered in the client's pre_shared_key extension ๐Ÿ”‘. In addition, it MUST verify that the following values are the same as those associated with the selected PSK ๐Ÿง:

  • The TLS version number
  • The selected cipher suite
  • The selected ALPN [RFC7301] protocol, if any

These requirements are a superset of those needed to perform a 1-RTT handshake using the PSK in question ๐Ÿ’–. For externally established PSKs, the associated values are the ones provisioned along with the key. For PSKs established via a NewSessionTicket message, the associated values are the ones negotiated in the connection during which the ticket was established.

Future extensions MUST define their interaction with 0-RTT ๐Ÿงฉ.

If any of these checks fail, the server MUST NOT respond with the extension and MUST discard all the first-flight data using one of the first two mechanisms listed above (thus falling back to 1-RTT or 2-RTT) ๐Ÿ™…โ€โ™€๏ธ. If the client attempts a 0-RTT handshake but the server rejects it, the server generally won't have the 0-RTT record protection keys and must instead use trial decryption (either with the 1-RTT handshake keys or by looking for a cleartext ClientHello in the case of a HelloRetryRequest) to find the first non-0-RTT message ๐Ÿ•ต๏ธโ€โ™€๏ธ.

If the server chooses to accept the early_data extension, then it MUST comply with the same error-handling requirements specified for all records when processing early data records. Specifically, if the server fails to decrypt a 0-RTT record following an accepted early_data extension, it MUST terminate the connection with a bad_record_mac alert as per Section 5.2 ๐Ÿšจ.

If the server rejects the early_data extension, the client application MAY opt to retransmit the Application Data previously sent in early data once the handshake has been completed ๐Ÿ”. Note that automatic retransmission of early data could result in incorrect assumptions about the status of the connection. For instance, when the negotiated connection selects a different ALPN protocol from the one used for early data, an application might need to construct different messages. Similarly, if early data assumes anything about the connection state, it might be sent in error after the handshake completes ๐Ÿซฃ.

A TLS implementation SHOULD NOT automatically resend early data, because applications are in a better position to decide when retransmission is appropriate ๐Ÿ’…. A TLS implementation MUST NOT automatically resend early data unless the negotiated connection selects the same ALPN protocol ๐ŸŒˆ.

4.2.11. Pre-Shared Key Extension ๐Ÿ”‘

okay bestie, the pre_shared_key extension is giving "I already know exactly who you are" ๐Ÿ’…. It's used to negotiate the identity of the pre-shared key that gets used for a given handshake, in association with PSK key establishment ๐Ÿฆ‹.

The extension_data field of this extension contains a PreSharedKeyExtension value โœจ:

struct {
    opaque identity<1..2^16-1>;
    uint32 obfuscated_ticket_age;
} PskIdentity;

opaque PskBinderEntry<32..255>;

struct {
    PskIdentity identities<7..2^16-1>;
    PskBinderEntry binders<33..2^16-1>;
} OfferedPsks;

struct {
    select (Handshake.msg_type) {
        case client_hello: OfferedPsks;
        case server_hello: uint16 selected_identity;
    };
} PreSharedKeyExtension;

Here's what each piece does ๐ŸŒธ:

  • identity: a label for a key. For instance, a ticket (as defined in Appendix B.3.4) or a label for a pre-shared key established externally.
  • obfuscated_ticket_age: an obfuscated version of the age of the key. Section 4.2.11.1 describes how to form this value for identities established via the NewSessionTicket message. For identities established externally, an obfuscated_ticket_age of 0 SHOULD be used, and servers MUST ignore the value.
  • identities: a list of the identities that the client is willing to negotiate with the server. If sent alongside the early_data extension (see Section 4.2.10), the first identity is the one used for 0-RTT data.
  • binders: a series of HMAC values, one for each value in the identities list and in the same order, computed as described below.
  • selected_identity: the server's chosen identity, expressed as a (0-based) index into the identities in the client's list.

Each PSK is associated with a single Hash algorithm ๐Ÿง. For PSKs established via the ticket mechanism (Section 4.6.1), this is the KDF Hash algorithm on the connection where the ticket was established. For externally established PSKs, the Hash algorithm MUST be set when the PSK is established or default to SHA-256 if no such algorithm is defined. The server MUST ensure that it selects a compatible PSK (if any) and cipher suite. Pick your matches carefully, periodt ๐Ÿ’–.

The SNI drama is real ๐Ÿ™ˆ. In TLS versions prior to TLS 1.3, the Server Name Identification (SNI) value was intended to be associated with the session (Section 3 of [RFC6066]), and the server was required to enforce that the SNI value associated with the session matches the one specified in the resumption handshake. In reality, though, implementations weren't consistent about which of two supplied SNI values they would use, so clients ended up de facto enforcing the consistency requirement themselves. In TLS 1.3, the SNI value is always explicitly specified in the resumption handshake, so the server doesn't need to associate an SNI value with the ticket at all. Clients, however, SHOULD store the SNI with the PSK to fulfill the requirements of Section 4.6.1 ๐Ÿซถ.

Implementor's note ๐Ÿ“: when session resumption is the primary use case of PSKs, the most straightforward way to implement the PSK/cipher suite matching requirements is to negotiate the cipher suite first and then exclude any incompatible PSKs. Any unknown PSKs (e.g., ones not in the PSK database or encrypted with an unknown key) SHOULD simply be ignored. If no acceptable PSKs are found, the server SHOULD perform a non-PSK handshake if possible. If backward compatibility is important, client-provided, externally established PSKs SHOULD influence cipher suite selection.

Prior to accepting PSK key establishment, the server MUST validate the corresponding binder value (see Section 4.2.11.2 below). If this value is not present or does not validate, the server MUST abort the handshake ๐Ÿšซ. Servers SHOULD NOT attempt to validate multiple binders. Instead, they SHOULD select a single PSK and validate solely the binder that corresponds to that PSK. See Section 8.2 and Appendix E.6 for the security rationale behind this requirement ๐Ÿ”’. To accept PSK key establishment, the server sends a pre_shared_key extension indicating the selected identity.

Clients MUST verify that the server's selected_identity is within the range supplied by the client, that the server selected a cipher suite indicating a Hash associated with the PSK, and that a server key_share extension is present if required by the ClientHello psk_key_exchange_modes extension. If these values are not consistent, the client MUST abort the handshake with an illegal_parameter alert ๐Ÿ˜ญ.

If the server supplies an early_data extension, the client MUST verify that the server's selected_identity is 0. If any other value is returned, the client MUST abort the handshake with an illegal_parameter alert ๐Ÿ’€.

The pre_shared_key extension MUST be the last extension in the ClientHello (this facilitates implementation as described below) ๐Ÿงš. Servers MUST check that it is the last extension and otherwise fail the handshake with an illegal_parameter alert.


4.2.11.1. Ticket Age โฐ

The client's view of the age of a ticket is the time since the receipt of the NewSessionTicket message. Clients MUST NOT attempt to use tickets which have ages greater than the ticket_lifetime value which was provided with the ticket. The obfuscated_ticket_age field of each PskIdentity contains an obfuscated version of the ticket age, formed by taking the age in milliseconds and adding the ticket_age_add value that was included with the ticket (see Section 4.6.1), modulo 2^32. This addition prevents passive observers from correlating connections unless tickets are reused ๐Ÿ•ต๏ธโ€โ™€๏ธ.

Note that the ticket_lifetime field in the NewSessionTicket message is in seconds, but the obfuscated_ticket_age is in milliseconds. Because ticket lifetimes are restricted to a week, 32 bits is enough to represent any plausible age, even in milliseconds ๐Ÿ“.

4.2.11.2. PSK Binder ๐ŸŽ€

The PSK binder value forms a binding between a PSK and the current handshake, as well as a binding between the handshake in which the PSK was generated (if via a NewSessionTicket message) and the current handshake. Each entry in the binders list is computed as an HMAC over a transcript hash (see Section 4.4.1) containing a partial ClientHello up to and including the PreSharedKeyExtension.identities field. That is, it includes all of the ClientHello but not the binders list itself. The length fields for the message (including the overall length, the length of the extensions block, and the length of the pre_shared_key extension) are all set as if binders of the correct lengths were present.

The PskBinderEntry is computed in the same way as the Finished message (Section 4.4.4), but with the BaseKey being the binder_key derived via the key schedule from the corresponding PSK which is being offered (see Section 7.1).

If the handshake includes a HelloRetryRequest, the initial ClientHello and HelloRetryRequest are included in the transcript along with the new ClientHello. For instance, if the client sends ClientHello1, its binder will be computed over:

Transcript-Hash(Truncate(ClientHello1))

Where Truncate() removes the binders list from the ClientHello.

If the server responds with a HelloRetryRequest and the client then sends ClientHello2, its binder will be computed over:

Transcript-Hash(ClientHello1,
                HelloRetryRequest,
                Truncate(ClientHello2))

The full ClientHello1/ClientHello2 is included in all other handshake hash computations. Note that in the first flight, Truncate(ClientHello1) is hashed directly, but in the second flight, ClientHello1 is hashed and then reinjected as a message_hash message, as described in Section 4.4.1 ๐ŸŒˆ.

4.2.11.3. Processing Order ๐Ÿƒโ€โ™€๏ธ

Clients are permitted to "stream" 0-RTT data until they receive the server's Finished, only then sending the EndOfEarlyData message, followed by the rest of the handshake. In order to avoid deadlocks, when accepting early_data, servers MUST process the client's ClientHello and then immediately send their flight of messages, rather than waiting for the client's EndOfEarlyData message before sending its ServerHello ๐Ÿš€.


4.3. Server Parameters ๐Ÿ‘‘

The next two messages from the server, EncryptedExtensions and CertificateRequest, contain information from the server that determines the rest of the handshake. These messages are encrypted with keys derived from the server_handshake_traffic_secret ๐Ÿ”.

4.3.1. Encrypted Extensions ๐Ÿซง

In all handshakes, the server MUST send the EncryptedExtensions message immediately after the ServerHello message. This is the first message that is encrypted under keys derived from the server_handshake_traffic_secret.

The EncryptedExtensions message contains extensions that can be protected, i.e., any which are not needed to establish the cryptographic context but which are not associated with individual certificates. The client MUST check EncryptedExtensions for the presence of any forbidden extensions, and if any are found, MUST abort the handshake with an illegal_parameter alert ๐Ÿšจ.

Structure of this message:

struct {
    Extension extensions<0..2^16-1>;
} EncryptedExtensions;
  • extensions: a list of extensions. For more information, see the table in Section 4.2.

4.3.2. Certificate Request ๐Ÿ“œ

A server which is authenticating with a certificate MAY optionally request a certificate from the client ๐ŸŽŸ๏ธ. This message, if sent, MUST follow EncryptedExtensions.

Structure of this message:

struct {
    opaque certificate_request_context<0..2^8-1>;
    Extension extensions<2..2^16-1>;
} CertificateRequest;
  • certificate_request_context: an opaque string which identifies the certificate request and which will be echoed in the client's Certificate message. The certificate_request_context MUST be unique within the scope of this connection (thus preventing replay of client CertificateVerify messages). This field SHALL be zero length unless used for the post-handshake authentication exchanges described in Section 4.6.2. When requesting post-handshake authentication, the server SHOULD make the context unpredictable to the client (e.g., by randomly generating it) in order to prevent an attacker who has temporary access to the client's private key from pre-computing valid CertificateVerify messages ๐Ÿ•ต๏ธ.
  • extensions: a set of extensions describing the parameters of the certificate being requested. The signature_algorithms extension MUST be specified, and other extensions MAY optionally be included if defined for this message. Clients MUST ignore unrecognized extensions ๐Ÿ™ˆ.

In prior versions of TLS, the CertificateRequest message carried a list of signature algorithms and certificate authorities which the server would accept. In TLS 1.3, the former is expressed by sending the signature_algorithms and optionally signature_algorithms_cert extensions. The latter is expressed by sending the certificate_authorities extension (see Section 4.2.4) ๐Ÿ’–.

Servers which are authenticating with a PSK MUST NOT send the CertificateRequest message in the main handshake, though they MAY send it in post-handshake authentication (see Section 4.6.2) provided that the client has sent the post_handshake_auth extension (see Section 4.2.6). No PSK-only shortcuts here, bestie โœจ.

4.4. Authentication Messages โœจ

okay so back in Section 2 we met the main squad: TLS mostly uses the same three messages for authentication, key confirmation, and handshake integrity, which are Certificate, CertificateVerify, and Finished ๐Ÿ”. (The PSK binders do key confirmation too, just in a similar vibe ๐ŸŽ€.) These three are always the last messages in their handshake flight. Certificate and CertificateVerify only show up in certain situations, which we'll get into below, and Finished always rides along in the Authentication Block ๐Ÿ’…. All three are encrypted under keys derived from the [sender]_handshake_traffic_secret ๐Ÿฆ‹.

the computations for the Authentication messages all take the same three inputs:

  • The certificate and signing key to be used.
  • A Handshake Context, which is the set of messages included in the transcript hash.
  • A Base Key, used to compute a MAC key.

from those inputs, the messages end up containing:

Certificate: the certificate used for authentication, plus any supporting certificates in the chain. ngl, certificate-based client authentication is not available in PSK handshake flows (including 0-RTT) ๐Ÿ™ˆ.

CertificateVerify: a signature over the value Transcript-Hash(Handshake Context, Certificate) โœ๏ธ.

Finished: a MAC over the value Transcript-Hash(Handshake Context, Certificate, CertificateVerify), using a MAC key derived from the Base Key ๐Ÿ”’.

here's the lineup for each scenario, so you can see which Handshake Context and Base Key go with which mode ๐Ÿ“‹:

+-----------+-------------------------+-----------------------------+
| Mode      | Handshake Context       | Base Key                    |
+-----------+-------------------------+-----------------------------+
| Server    | ClientHello ... later   | server_handshake_traffic_   |
|           | of EncryptedExtensions/ | secret                      |
|           | CertificateRequest      |                             |
|           |                         |                             |
| Client    | ClientHello ... later   | client_handshake_traffic_   |
|           | of server               | secret                      |
|           | Finished/EndOfEarlyData |                             |
|           |                         |                             |
| Post-     | ClientHello ... client  | client_application_traffic_ |
| Handshake | Finished +              | secret_N                    |
|           | CertificateRequest      |                             |
+-----------+-------------------------+-----------------------------+

4.4.1. The Transcript Hash ๐Ÿงพ

lots of TLS's cryptography leans on a transcript hash โœจ. The value is made by hashing the concatenation of each included handshake message. That includes the handshake message header with its type and length fields, but NOT the record layer headers. In other words:

Transcript-Hash(M1, M2, ... Mn) = Hash(M1 || M2 || ... || Mn)

there's one exception to the general rule. When the server answers a ClientHello with a HelloRetryRequest, the value of ClientHello1 gets swapped out for a special synthetic handshake message of handshake type "message_hash" that contains Hash(ClientHello1) ๐Ÿช„:

Transcript-Hash(ClientHello1, HelloRetryRequest, ... Mn) =
    Hash(message_hash ||        /* Handshake type */
         00 00 Hash.length  ||  /* Handshake message length (bytes) */
         Hash(ClientHello1) ||  /* Hash of ClientHello1 */
         HelloRetryRequest  || ... || Mn)

why bother? It lets the server do a stateless HelloRetryRequest. It only has to stash the hash of ClientHello1 in the cookie, instead of exporting the entire intermediate hash state (see Section 4.2.2) ๐Ÿ“.

for concreteness, the transcript hash is always taken from the following sequence of handshake messages, starting at the first ClientHello and including only the ones that were actually sent ๐Ÿ“œ: ClientHello, HelloRetryRequest, ClientHello, ServerHello, EncryptedExtensions, server CertificateRequest, server Certificate, server CertificateVerify, server Finished, EndOfEarlyData, client Certificate, client CertificateVerify, client Finished.

implementations can generally keep a running transcript hash value based on the negotiated hash ๐Ÿƒโ€โ™€๏ธ. Just note that subsequent post-handshake authentications do NOT include each other. They only cover the messages through the end of the main handshake ๐Ÿ™…โ€โ™€๏ธ.

4.4.2. Certificate ๐ŸŽ€

this message hands the endpoint's certificate chain over to the peer ๐ŸŽ.

The server MUST send a Certificate message whenever the agreed-upon key exchange method uses certificates for authentication. This includes every key exchange method defined in this document except PSK ๐Ÿ‘‘.

The client MUST send a Certificate message if and only if the server has requested client authentication via a CertificateRequest message (Section 4.3.2). If the server requests client authentication but no suitable certificate is available, the client MUST send a Certificate message containing no certificates (i.e., with the certificate_list field having length 0). A Finished message MUST be sent regardless of whether the Certificate message is empty ๐Ÿซถ.

here's the structure of this message ๐Ÿงฑ:

   enum {
       X509(0),
       RawPublicKey(2),
       (255)
   } CertificateType;

   struct {
       select (certificate_type) {
           case RawPublicKey:
             /* From RFC 7250 ASN.1_subjectPublicKeyInfo */
             opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;

           case X509:
             opaque cert_data<1..2^24-1>;
       };
       Extension extensions<0..2^16-1>;
   } CertificateEntry;

   struct {
       opaque certificate_request_context<0..2^8-1>;
       CertificateEntry certificate_list<0..2^24-1>;
   } Certificate;

the fields, explained in my own words ๐Ÿ’–:

  • certificate_request_context: if this message is answering a CertificateRequest, this is the value of certificate_request_context from that message. Otherwise (server authentication), this field SHALL be zero length ๐Ÿซง.
  • certificate_list: a sequence (chain) of CertificateEntry structures, each holding one certificate and a set of extensions ๐Ÿ”—.
  • extensions: a set of extension values for the CertificateEntry. The "Extension" format is defined in Section 4.2. Right now, the valid extensions for server certificates are the OCSP Status extension [RFC6066] and the SignedCertificateTimestamp extension [RFC6962], though future extensions might get added here too ๐ŸŒˆ. Extensions in the Certificate message from the server MUST correspond to ones from the ClientHello message. Extensions in the Certificate message from the client MUST correspond to extensions in the CertificateRequest message from the server. If an extension applies to the entire chain, it SHOULD be included in the first CertificateEntry ๐Ÿง.

if the matching certificate type extension ("server_certificate_type" or "client_certificate_type") wasn't negotiated in EncryptedExtensions, or the X.509 certificate type was negotiated, then each CertificateEntry holds a DER-encoded X.509 certificate ๐Ÿ“„. The sender's certificate MUST come first in the list, as the first CertificateEntry ๐Ÿฅ‡. Each following certificate SHOULD directly certify the one right before it. Trust anchors have to be distributed independently, so a certificate that specifies a trust anchor MAY be left out of the chain, as long as supported peers are known to already have any omitted certificates ๐Ÿค.

Note: Prior to TLS 1.3, "certificate_list" ordering required each certificate to certify the one immediately preceding it. Some implementations allowed some flexibility, though. Servers sometimes send both a current and a deprecated intermediate for transitional purposes, and others are just misconfigured, but these cases can still validate properly. For maximum compatibility, all implementations SHOULD be prepared to handle potentially extraneous certificates and arbitrary orderings from any TLS version, with the exception of the end-entity certificate, which MUST be first ๐ŸŒธ.

If the RawPublicKey certificate type was negotiated, then the certificate_list MUST contain no more than one CertificateEntry. That entry holds an ASN1_subjectPublicKeyInfo value, as defined in [RFC7250], Section 3 ๐Ÿ”‘.

The OpenPGP certificate type [RFC6091] MUST NOT be used with TLS 1.3 ๐Ÿšซ.

The server's certificate_list MUST always be non-empty. A client will send an empty certificate_list if it doesn't have an appropriate certificate to send in response to the server's authentication request ๐Ÿ™ˆ.

4.4.2.1. OCSP Status and SCT Extensions ๐Ÿฆ‹

[RFC6066] and [RFC6961] provide extensions for negotiating the server sending OCSP responses to the client. In TLS 1.2 and below, the server replies with an empty extension to show it's negotiated this, and the OCSP information rides in a CertificateStatus message. In TLS 1.3, the server's OCSP information is carried in an extension inside the CertificateEntry that holds the associated certificate ๐Ÿฌ. Specifically, the body of the "status_request" extension from the server MUST be a CertificateStatus structure as defined in [RFC6066], which is interpreted as defined in [RFC6960].

Note: The status_request_v2 extension [RFC6961] is deprecated. TLS 1.3 servers MUST NOT act upon its presence or the information in it when processing ClientHello messages. In particular, they MUST NOT send the status_request_v2 extension in the EncryptedExtensions, CertificateRequest, or Certificate messages. TLS 1.3 servers MUST be able to process ClientHello messages that include it, since it MAY be sent by clients that want to use it in earlier protocol versions ๐Ÿงƒ.

A server MAY request that a client present an OCSP response with its certificate by sending an empty "status_request" extension in its CertificateRequest message. If the client opts to send an OCSP response, the body of its "status_request" extension MUST be a CertificateStatus structure as defined in [RFC6066] ๐Ÿ“ฌ.

Similarly, [RFC6962] gives a server a way to send a Signed Certificate Timestamp (SCT) as an extension in the ServerHello in TLS 1.2 and below. In TLS 1.3, the server's SCT information is carried in an extension in the CertificateEntry instead โฑ๏ธโœจ.

4.4.2.2. Server Certificate Selection

okay bestie, so here's how the server picks which certificates it sends ๐ŸŽ€โœจ

  • The certificate type MUST be X.509v3 [RFC5280], unless explicitly negotiated otherwise (e.g., [RFC7250]). ๐Ÿ“œ
  • The server's end-entity certificate's public key (and associated restrictions) MUST be compatible with the selected authentication algorithm from the client's "signature_algorithms" extension (currently RSA, ECDSA, or EdDSA). ๐Ÿ”‘
  • The certificate MUST allow the key to be used for signing (i.e., the digitalSignature bit MUST be set if the Key Usage extension is present) with a signature scheme indicated in the client's "signature_algorithms"/"signature_algorithms_cert" extensions (see Section 4.2.3). ๐Ÿ’…
  • The "server_name" [RFC6066] and "certificate_authorities" extensions are used to guide certificate selection. As servers MAY require the presence of the "server_name" extension, clients SHOULD send this extension, when applicable. ๐Ÿซถ

All certificates provided by the server MUST be signed by a signature algorithm advertised by the client if it is able to provide such a chain (see Section 4.2.3). Certificates that are self-signed or certificates that are expected to be trust anchors are not validated as part of the chain and therefore MAY be signed with any algorithm. ๐ŸŒธ

If the server cannot produce a certificate chain that is signed only via the indicated supported algorithms, then it SHOULD continue the handshake by sending the client a certificate chain of its choice that may include algorithms that are not known to be supported by the client. This fallback chain SHOULD NOT use the deprecated SHA-1 hash algorithm in general, but MAY do so if the client's advertisement permits it, and MUST NOT do so otherwise. ๐Ÿ™ˆ

If the client cannot construct an acceptable chain using the provided certificates and decides to abort the handshake, then it MUST abort the handshake with an appropriate certificate-related alert (by default, "unsupported_certificate"; see Section 6.2 for more information). ๐Ÿ’€

If the server has multiple certificates, it chooses one of them based on the above-mentioned criteria (in addition to other criteria, such as transport-layer endpoint, local configuration, and preferences). ๐Ÿฆ‹

4.4.2.3. Client Certificate Selection

same energy, but for the client side ๐Ÿ’œ

  • The certificate type MUST be X.509v3 [RFC5280], unless explicitly negotiated otherwise (e.g., [RFC7250]). ๐Ÿ“œ
  • If the "certificate_authorities" extension in the CertificateRequest message was present, at least one of the certificates in the certificate chain SHOULD be issued by one of the listed CAs. ๐Ÿงš
  • The certificates MUST be signed using an acceptable signature algorithm, as described in Section 4.3.2. Note that this relaxes the constraints on certificate-signing algorithms found in prior versions of TLS. โœจ
  • If the CertificateRequest message contained a non-empty "oid_filters" extension, the end-entity certificate MUST match the extension OIDs that are recognized by the client, as described in Section 4.2.5. ๐Ÿ‘‘

4.4.2.4. Receiving a Certificate Message

ngl, detailed certificate validation is its own whole thing and it's out of scope for TLS (see [RFC5280]). This section just covers the TLS-specific rules ๐ŸŒˆ

If the server supplies an empty Certificate message, the client MUST abort the handshake with a "decode_error" alert. ๐Ÿ˜ญ

If the client does not send any certificates (i.e., it sends an empty Certificate message), the server MAY at its discretion either continue the handshake without client authentication or abort the handshake with a "certificate_required" alert. Also, if some aspect of the certificate chain was unacceptable (e.g., it was not signed by a known, trusted CA), the server MAY at its discretion either continue the handshake (considering the client unauthenticated) or abort the handshake. ๐Ÿ“

Any endpoint receiving any certificate which it would need to validate using any signature algorithm using an MD5 hash MUST abort the handshake with a "bad_certificate" alert. SHA-1 is deprecated, and it is RECOMMENDED that any endpoint receiving any certificate which it would need to validate using any signature algorithm using a SHA-1 hash abort the handshake with a "bad_certificate" alert. For clarity, this means that endpoints can accept these algorithms for certificates that are self-signed or are trust anchors. ๐Ÿ”ฅ

All endpoints are RECOMMENDED to transition to SHA-256 or better as soon as possible to maintain interoperability with implementations currently in the process of phasing out SHA-1 support. ๐Ÿ’–

Note that a certificate containing a key for one signature algorithm MAY be signed using a different signature algorithm (for instance, an RSA key signed with an ECDSA key). ๐Ÿฆ‹

4.4.3. Certificate Verify

okay this is the proof-of-possession moment ๐Ÿ” This message shows that an endpoint actually holds the private key for its certificate. It also gives integrity to the handshake up to this point. Servers MUST send this message when authenticating via a certificate. Clients MUST send this message whenever authenticating via a certificate (i.e., when the Certificate message is non-empty). When sent, this message MUST appear immediately after the Certificate message and immediately prior to the Finished message. ๐Ÿ’…

Structure of this message:

   struct {
       SignatureScheme algorithm;
       opaque signature<0..2^16-1>;
   } CertificateVerify;

The algorithm field specifies the signature algorithm used (see Section 4.2.3 for the definition of this type). The signature is a digital signature using that algorithm. The content that is covered under the signature is the hash output as described in Section 4.4.1, namely:

   Transcript-Hash(Handshake Context, Certificate)

The digital signature is then computed over the concatenation of:

  • A string that consists of octet 32 (0x20) repeated 64 times
  • The context string
  • A single 0 byte which serves as the separator
  • The content to be signed

This structure is intended to prevent an attack on previous versions of TLS in which the ServerKeyExchange format meant that attackers could obtain a signature of a message with a chosen 32-byte prefix (ClientHello.random). The initial 64-byte pad clears that prefix along with the server-controlled ServerHello.random. ๐Ÿ˜ฑ

The context string for a server signature is "TLS 1.3, server CertificateVerify". The context string for a client signature is "TLS 1.3, client CertificateVerify". It is used to provide separation between signatures made in different contexts, helping against potential cross-protocol attacks. ๐Ÿง

For example, if the transcript hash was 32 bytes of 01 (this length would make sense for SHA-256), the content covered by the digital signature for a server CertificateVerify would be:

   2020202020202020202020202020202020202020202020202020202020202020
   2020202020202020202020202020202020202020202020202020202020202020
   544c5320312e332c207365727665722043657274696669636174655665726966
   79
   00
   0101010101010101010101010101010101010101010101010101010101010101

On the sender side, the process for computing the signature field of the CertificateVerify message takes as input:

  • The content covered by the digital signature
  • The private signing key corresponding to the certificate sent in the previous message

If the CertificateVerify message is sent by a server, the signature algorithm MUST be one offered in the client's "signature_algorithms" extension unless no valid certificate chain can be produced without unsupported algorithms (see Section 4.2.3). ๐ŸŒธ

If sent by a client, the signature algorithm used in the signature MUST be one of those present in the supported_signature_algorithms field of the "signature_algorithms" extension in the CertificateRequest message. ๐Ÿซถ

In addition, the signature algorithm MUST be compatible with the key in the sender's end-entity certificate. RSA signatures MUST use an RSASSA-PSS algorithm, regardless of whether RSASSA-PKCS1-v1_5 algorithms appear in "signature_algorithms". The SHA-1 algorithm MUST NOT be used in any signatures of CertificateVerify messages. ๐Ÿšซ

All SHA-1 signature algorithms in this specification are defined solely for use in legacy certificates and are not valid for CertificateVerify signatures. ๐Ÿ™…

The receiver of a CertificateVerify message MUST verify the signature field. The verification process takes as input:

  • The content covered by the digital signature
  • The public key contained in the end-entity certificate found in the associated Certificate message
  • The digital signature received in the signature field of the CertificateVerify message

If the verification fails, the receiver MUST terminate the handshake with a "decrypt_error" alert. ๐Ÿ’€

4.4.4. Finished

the Finished message is the last one in the Authentication Block ๐ŸŽ‰ It's super essential because it proves the handshake and the computed keys are legit.

Recipients of Finished messages MUST verify that the contents are correct and if incorrect MUST terminate the connection with a "decrypt_error" alert. ๐Ÿ˜ค

Once a side has sent its Finished message and has received and validated the Finished message from its peer, it may begin to send and receive Application Data over the connection. There are two settings in which it is permitted to send data prior to receiving the peer's Finished:

  1. Clients sending 0-RTT data as described in Section 4.2.10.
  2. Servers MAY send data after sending their first flight, but because the handshake is not yet complete, they have no assurance of either the peer's identity or its liveness (i.e., the ClientHello might have been replayed). ๐Ÿ‘€

The key used to compute the Finished message is computed from the Base Key defined in Section 4.4 using HKDF (see Section 7.1). Specifically:

   finished_key =
       HKDF-Expand-Label(BaseKey, "finished", "", Hash.length)

Structure of this message:

   struct {
       opaque verify_data[Hash.length];
   } Finished;

The verify_data value is computed as follows:

   verify_data =
       HMAC(finished_key,
            Transcript-Hash(Handshake Context,
                            Certificate*, CertificateVerify*))

   * Only included if present.

HMAC [RFC2104] uses the Hash algorithm for the handshake. As noted above, the HMAC input can generally be implemented by a running hash, i.e., just the handshake hash at this point. ๐Ÿงฎ

In previous versions of TLS, the verify_data was always 12 octets long. In TLS 1.3, it is the size of the HMAC output for the Hash used for the handshake. โœจ

Note: Alerts and any other non-handshake record types are not handshake messages and are not included in the hash computations. ๐Ÿ’…

Any records following a Finished message MUST be encrypted under the appropriate application traffic key as described in Section 7.2. In particular, this includes any alerts sent by the server in response to client Certificate and CertificateVerify messages. ๐Ÿ”

4.5. End of Early Data ๐Ÿ’Œ

Okay bestie, this is the "I'm done sending my 0-RTT stuff" message ๐Ÿซถ

struct {} EndOfEarlyData;

If the server sent an early_data extension in EncryptedExtensions, the client MUST send an EndOfEarlyData message after receiving the server Finished. If the server does not send an early_data extension in EncryptedExtensions, then the client MUST NOT send an EndOfEarlyData message. This message tells the server that all 0-RTT application_data messages, if any, have been sent, and that the records after it are protected under handshake traffic keys. โœจ

Servers MUST NOT send this message, and clients that receive it MUST terminate the connection with an unexpected_message alert. ๐Ÿšซ This message is encrypted under keys derived from the client_early_traffic_secret. ๐Ÿ”


4.6. Post-Handshake Messages ๐ŸŽ€

TLS also lets other messages get sent after the main handshake is done, like a little afterparty ๐Ÿฅณ These messages use a handshake content type and are encrypted under the appropriate application traffic key.

4.6.1. New Session Ticket Message ๐ŸŽŸ๏ธ

At any time after the server has received the client Finished message, it MAY send a NewSessionTicket message. This message creates a unique association between the ticket value and a secret PSK derived from the resumption master secret (see Section 7). It's giving "here's your VIP pass for next time" ๐Ÿ’–

The client MAY use this PSK for future handshakes by including the ticket value in the pre_shared_key extension in its ClientHello (Section 4.2.11). Servers MAY send multiple tickets on a single connection, either right after each other or after specific events (see Appendix C.4). For instance, the server might send a new ticket after post-handshake authentication so it can wrap up the extra client authentication state ๐Ÿงš Multiple tickets are really useful for clients for a bunch of reasons, including:

  • Opening multiple parallel HTTP connections. ๐ŸŒˆ
  • Performing connection racing across interfaces and address families via (for example) Happy Eyeballs [RFC8305] or related techniques. ๐ŸŽ๏ธ

Any ticket MUST only be resumed with a cipher suite that has the same KDF hash algorithm as the one used to establish the original connection. ๐Ÿ”‘

Clients MUST only resume if the new SNI value is valid for the server certificate presented in the original session, and SHOULD only resume if the SNI value matches the one used in the original session. The latter is a performance optimization ๐Ÿ™ˆ Normally there's no reason to expect that different servers covered by a single certificate would be able to accept each other's tickets, so trying to resume in that case would waste a single-use ticket. ๐Ÿ—‘๏ธ If such an indication is provided (externally or by any other means), clients MAY resume with a different SNI value. ๐Ÿฆ‹

On resumption, if reporting an SNI value to the calling application, implementations MUST use the value sent in the resumption ClientHello rather than the value sent in the previous session. Note that if a server implementation declines all PSK identities with different SNI values, these two values are always the same. ๐Ÿ’…

Note: Although the resumption master secret depends on the client's second flight, a server which does not request client authentication MAY compute the remainder of the transcript independently and then send a NewSessionTicket immediately upon sending its Finished, rather than waiting for the client Finished. This might be appropriate in cases where the client is expected to open multiple TLS connections in parallel and would benefit from the reduced overhead of a resumption handshake, for example. โญ

Here's the struct, bestie ๐Ÿ‘€

struct {
    uint32 ticket_lifetime;
    uint32 ticket_age_add;
    opaque ticket_nonce<0..255>;
    opaque ticket<1..2^16-1>;
    Extension extensions<0..2^16-2>;
} NewSessionTicket;

Now let's go through the fields one by one ๐Ÿง

  • ticket_lifetime: Indicates the lifetime in seconds, as a 32-bit unsigned integer in network byte order, counted from the time of ticket issuance. Servers MUST NOT use any value greater than 604800 seconds (7 days). โณ The value of zero means the ticket should be discarded immediately. Clients MUST NOT cache tickets for longer than 7 days, regardless of the ticket_lifetime, and MAY delete tickets earlier based on local policy. A server MAY treat a ticket as valid for a shorter period of time than what is stated in the ticket_lifetime. ๐Ÿซ 
  • ticket_age_add: A securely generated, random 32-bit value that is used to obscure the age of the ticket that the client includes in the pre_shared_key extension. ๐Ÿ•ต๏ธโ€โ™€๏ธ The client-side ticket age is added to this value modulo 2^32 to get the value that the client actually transmits. The server MUST generate a fresh value for each ticket it sends. โœจ
  • ticket_nonce: A per-ticket value that is unique across all tickets issued on this connection. ๐Ÿชช
  • ticket: The value of the ticket to be used as the PSK identity. The ticket itself is an opaque label. It MAY be either a database lookup key or a self-encrypted and self-authenticated value. ๐Ÿ”
  • extensions: A set of extension values for the ticket. The "Extension" format is defined in Section 4.2. Clients MUST ignore unrecognized extensions. ๐Ÿ™ˆ

The only extension currently defined for NewSessionTicket is early_data, which says the ticket may be used to send 0-RTT data (Section 4.2.10). It contains the following value:

  • max_early_data_size: The maximum amount of 0-RTT data that the client is allowed to send when using this ticket, in bytes. Only the Application Data payload (i.e., plaintext but not padding or the inner content type byte) is counted. ๐Ÿ“ A server that receives more than max_early_data_size bytes of 0-RTT data SHOULD terminate the connection with an unexpected_message alert. Also, servers that reject early data due to lack of cryptographic material won't be able to tell padding from content, so clients SHOULD NOT depend on being able to send large quantities of padding in early data records. ๐Ÿซฃ

The PSK associated with the ticket is computed as:

HKDF-Expand-Label(resumption_master_secret,
                 "resumption", ticket_nonce, Hash.length)

Because the ticket_nonce value is distinct for each NewSessionTicket message, a different PSK will be derived for each ticket. ๐ŸŽ€

Note that, in principle, it's possible to keep issuing new tickets that indefinitely extend the lifetime of the keying material originally derived from an initial non-PSK handshake (which was most likely tied to the peer's certificate). ๐Ÿ˜ณ It is RECOMMENDED that implementations place limits on the total lifetime of such keying material. These limits should take into account the lifetime of the peer's certificate, the likelihood of intervening revocation, and the time since the peer's online CertificateVerify signature. ๐Ÿ›ก๏ธ

4.6.2. Post-Handshake Authentication ๐Ÿชช

When the client has sent the post_handshake_auth extension (see Section 4.2.6), a server MAY request client authentication at any time after the handshake has completed, by sending a CertificateRequest message. The client MUST respond with the appropriate Authentication messages (see Section 4.4). If the client chooses to authenticate, it MUST send Certificate, CertificateVerify, and Finished. If it declines, it MUST send a Certificate message containing no certificates, followed by Finished. All of the client's messages for a given response MUST appear consecutively on the wire, with no intervening messages of other types. ๐Ÿงต

A client that receives a CertificateRequest message without having sent the post_handshake_auth extension MUST send an unexpected_message fatal alert. ๐Ÿšจ

Note: Because client authentication could involve prompting the user, servers MUST be prepared for some delay, including receiving an arbitrary number of other messages between sending the CertificateRequest and receiving a response. In addition, clients which receive multiple CertificateRequests in close succession MAY respond to them in a different order than they were received (the certificate_request_context value lets the server tell the responses apart). ๐Ÿคญ

4.6.3. Key and Initialization Vector Update ๐Ÿ”„

The KeyUpdate handshake message is used to indicate that the sender is updating its sending cryptographic keys. ๐Ÿ—๏ธ Either peer can send this message after it has sent a Finished message. Implementations that receive a KeyUpdate message before receiving a Finished message MUST terminate the connection with an unexpected_message alert. After sending a KeyUpdate message, the sender SHALL send all its traffic using the next generation of keys, computed as described in Section 7.2. Once a receiver gets a KeyUpdate, it MUST update its receiving keys. ๐Ÿ’ซ

Here's the wire format:

enum {
    update_not_requested(0), update_requested(1), (255)
} KeyUpdateRequest;

struct {
    KeyUpdateRequest request_update;
} KeyUpdate;
  • request_update: Indicates whether the recipient of the KeyUpdate should respond with its own KeyUpdate. If an implementation receives any other value, it MUST terminate the connection with an illegal_parameter alert. ๐Ÿšซ

If the request_update field is set to update_requested, then the receiver MUST send a KeyUpdate of its own, with request_update set to update_not_requested, before sending its next Application Data record. ๐Ÿค This mechanism lets either side force an update to the entire connection, but it means an implementation that receives multiple KeyUpdates while it is silent will respond with a single update. Note that implementations may receive an arbitrary number of messages between sending a KeyUpdate with request_update set to update_requested and receiving the peer's KeyUpdate, because those messages may already be in flight. โœˆ๏ธ However, because send and receive keys are derived from independent traffic secrets, keeping the receive traffic secret does not threaten the forward secrecy of data sent before the sender changed keys. ๐Ÿ”’

If implementations independently send their own KeyUpdates with request_update set to update_requested, and they cross in flight, then each side will also send a response. The result is that each side increments by two generations. ๐Ÿ‘ฏ

Both sender and receiver MUST encrypt their KeyUpdate messages with the old keys. Additionally, both sides MUST enforce that a KeyUpdate with the old key is received before accepting any messages encrypted with the new key. Failure to do so may allow message truncation attacks. ๐Ÿ’€

5. Record Protocol โœจ

okay so the TLS record protocol is the part that takes your messages, chops them into bite-sized blocks, protects them, and sends them off ๐Ÿฆ‹. On the receiving end it checks, decrypts, and reassembles everything, then hands it up to the higher-level clients ๐Ÿ’…

Records are typed, which is what lets a bunch of higher-level protocols share one record layer. It's multiplexing, bestie ๐Ÿงš This document specifies four content types: handshake, application_data, alert, and change_cipher_spec. The change_cipher_spec one only exists for compatibility purposes (see Appendix D.4) ๐Ÿ™ˆ

Now for the change_cipher_spec drama. An implementation may receive an unencrypted record of type change_cipher_spec consisting of the single byte value 0x01 at any time after the first ClientHello message has been sent or received and before the peer's Finished message has been received. When that happens, it MUST simply drop it without further processing ๐Ÿซถ Watch out, though: this record can show up at a point in the handshake where you're expecting protected records, so you have to detect the situation before you try to deprotect the record ๐Ÿ˜ญ

If an implementation gets any other change_cipher_spec value, or a protected change_cipher_spec record, it MUST abort the handshake with an "unexpected_message" alert ๐Ÿ’€ And if it spots a change_cipher_spec record before the first ClientHello message or after the peer's Finished message, that MUST be treated as an unexpected record type. (Stateless servers might not be able to tell those cases apart from the allowed ones, so it's a little messy ๐ŸŒธ)

Implementations MUST NOT send record types that aren't defined in this document unless some extension negotiated them. If a TLS implementation receives an unexpected record type, it MUST terminate the connection with an "unexpected_message" alert ๐Ÿ”ฅ New record content type values get assigned by IANA in the TLS ContentType registry, as described in Section 11 ๐ŸŒˆ

5.1. Record Layer

The record layer fragments information blocks into TLSPlaintext records, carrying data in chunks of 2^14 bytes or less. Message boundaries get handled differently depending on the underlying ContentType. Any future content types MUST specify appropriate rules. Also, these rules are stricter than what got enforced in TLS 1.2, so don't be surprised ๐Ÿ‘‘

Handshake messages MAY be coalesced into a single TLSPlaintext record, or fragmented across several records, provided that:

  • Handshake messages MUST NOT be interleaved with other record types. Basically, if a handshake message is split over two or more records, there MUST NOT be any other records between them.
  • Handshake messages MUST NOT span key changes. Implementations MUST verify that all messages immediately preceding a key change align with a record boundary. If they don't, they MUST terminate the connection with an "unexpected_message" alert. Because the ClientHello, EndOfEarlyData, ServerHello, Finished, and KeyUpdate messages can immediately precede a key change, implementations MUST send these messages in alignment with a record boundary ๐ŸŽ€

Implementations MUST NOT send zero-length fragments of Handshake types, even if those fragments contain padding. No empty handshake fragments, periodt ๐Ÿ’…

Alert messages (Section 6) MUST NOT be fragmented across records, and multiple alert messages MUST NOT be coalesced into a single TLSPlaintext record. In other words, a record with an Alert type MUST contain exactly one message โœจ

Application Data messages contain data that is opaque to TLS. They are always protected. Zero-length fragments of Application Data MAY be sent, since they're potentially useful as a traffic analysis countermeasure ๐Ÿ•ต๏ธโ€โ™€๏ธ Application Data fragments MAY be split across multiple records or coalesced into a single record.

   enum {
       invalid(0),
       change_cipher_spec(20),
       alert(21),
       handshake(22),
       application_data(23),
       (255)
   } ContentType;

   struct {
       ContentType type;
       ProtocolVersion legacy_record_version;
       uint16 length;
       opaque fragment[TLSPlaintext.length];
   } TLSPlaintext;

Here's what each field does:

  • type: The higher-level protocol used to process the enclosed fragment.
  • legacy_record_version: MUST be set to 0x0303 for all records generated by a TLS 1.3 implementation other than an initial ClientHello (i.e., one not generated after a HelloRetryRequest), where it MAY also be 0x0301 for compatibility purposes. This field is deprecated and MUST be ignored for all purposes. Previous versions of TLS would use other values in this field under some circumstances ๐Ÿ™ƒ
  • length: The length (in bytes) of the following TLSPlaintext.fragment. The length MUST NOT exceed 2^14 bytes. An endpoint that receives a record that exceeds this length MUST terminate the connection with a "record_overflow" alert ๐Ÿšจ
  • fragment: The data being transmitted. This value is transparent and is treated as an independent block to be dealt with by the higher-level protocol specified by the type field.

This document describes TLS 1.3, which uses the version 0x0304. That version value is historical, coming from the use of 0x0301 for TLS 1.0 and 0x0300 for SSL 3.0 ๐Ÿ•ฐ๏ธ To maximize backward compatibility, a record containing an initial ClientHello SHOULD have version 0x0301 (reflecting TLS 1.0), and a record containing a second ClientHello or a ServerHello MUST have version 0x0303 (reflecting TLS 1.2). When negotiating prior versions of TLS, endpoints follow the procedure and requirements provided in Appendix D.

When record protection hasn't been engaged yet, TLSPlaintext structures get written directly onto the wire. Once record protection has started, TLSPlaintext records are protected and sent as described in the following section. Note that Application Data records MUST NOT be written to the wire unprotected (see Section 2 for details) ๐Ÿ”

5.2. Record Payload Protection

The record protection functions translate a TLSPlaintext structure into a TLSCiphertext structure, and the deprotection functions reverse the process. In TLS 1.3, unlike previous versions of TLS, all ciphers are modeled as "Authenticated Encryption with Associated Data" (AEAD) [RFC5116]. AEAD functions provide a unified encryption and authentication operation that turns plaintext into authenticated ciphertext and back again ๐Ÿง Each encrypted record is a plaintext header followed by an encrypted body, which itself contains a type and optional padding.

   struct {
       opaque content[TLSPlaintext.length];
       ContentType type;
       uint8 zeros[length_of_padding];
   } TLSInnerPlaintext;

   struct {
       ContentType opaque_type = application_data; /* 23 */
       ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
       uint16 length;
       opaque encrypted_record[TLSCiphertext.length];
   } TLSCiphertext;

Here's the breakdown:

  • content: The TLSPlaintext.fragment value, containing the byte encoding of a handshake or an alert message, or the raw bytes of the application's data to send.
  • type: The TLSPlaintext.type value containing the content type of the record.
  • zeros: An arbitrary-length run of zero-valued bytes may appear in the cleartext after the type field. This gives senders an opportunity to pad any TLS record by a chosen amount, as long as the total stays within record size limits. See Section 5.4 for more details ๐ŸŒท
  • opaque_type: The outer opaque_type field of a TLSCiphertext record is always set to the value 23 (application_data), for outward compatibility with middleboxes that are used to parsing previous versions of TLS. The actual content type of the record is found in TLSInnerPlaintext.type after decryption ๐Ÿ‘€
  • legacy_record_version: The legacy_record_version field is always 0x0303. TLS 1.3 TLSCiphertexts aren't generated until after TLS 1.3 has been negotiated, so there are no historical compatibility concerns where other values might be received. Note that the handshake protocol, including the ClientHello and ServerHello messages, authenticates the protocol version, so this value is redundant ๐Ÿซ 
  • length: The length (in bytes) of the following TLSCiphertext.encrypted_record, which is the sum of the lengths of the content and the padding, plus one for the inner content type, plus any expansion added by the AEAD algorithm. The length MUST NOT exceed 2^14 + 256 bytes. An endpoint that receives a record that exceeds this length MUST terminate the connection with a "record_overflow" alert ๐Ÿšจ
  • encrypted_record: The AEAD-encrypted form of the serialized TLSInnerPlaintext structure.

AEAD algorithms take as input a single key, a nonce, a plaintext, and "additional data" to be included in the authentication check, as described in Section 2.1 of [RFC5116]. The key is either the client_write_key or the server_write_key. The nonce is derived from the sequence number and the client_write_iv or server_write_iv (see Section 5.3), and the additional data input is the record header. I.e.,

   additional_data = TLSCiphertext.opaque_type ||
                     TLSCiphertext.legacy_record_version ||
                     TLSCiphertext.length

The plaintext input to the AEAD algorithm is the encoded TLSInnerPlaintext structure. Derivation of traffic keys is defined in Section 7.3 ๐Ÿ”‘

The AEAD output is the ciphertext output from the AEAD encryption operation. The length of the plaintext is greater than the corresponding TLSPlaintext.length, thanks to the inclusion of TLSInnerPlaintext.type and any padding supplied by the sender. The length of the AEAD output will generally be larger than the plaintext, but by an amount that varies with the AEAD algorithm ๐Ÿงฎ

Since the ciphers might incorporate padding, the amount of overhead could vary with different lengths of plaintext. Symbolically:

   AEADEncrypted =
       AEAD-Encrypt(write_key, nonce, additional_data, plaintext)

The encrypted_record field of TLSCiphertext is set to AEADEncrypted.

To decrypt and verify, the cipher takes as input the key, nonce, additional data, and the AEADEncrypted value. The output is either the plaintext or an error indicating that the decryption failed. There's no separate integrity check. Symbolically:

   plaintext of encrypted_record =
       AEAD-Decrypt(peer_write_key, nonce,
                    additional_data, AEADEncrypted)
If the decryption fails, the receiver MUST terminate the connection with a "bad_record_mac" alert. No second chances, bestie ๐Ÿ’€

An AEAD algorithm used in TLS 1.3 MUST NOT produce an expansion greater than 255 octets. An endpoint that receives a record from its peer with TLSCiphertext.length larger than 2^14 + 256 octets MUST terminate the connection with a "record_overflow" alert ๐Ÿ“ This limit comes from the maximum TLSInnerPlaintext length of 2^14 octets, plus 1 octet for ContentType, plus the maximum AEAD expansion of 255 octets. Math ate, no crumbs ๐Ÿฐ

5.3. Per-Record Nonce โœจ

okay so every record gets its own nonce, and the trick is a 64-bit sequence number ๐Ÿฆ‹ each side keeps one for reading and one for writing, bumps it by one after every record, and resets it to zero at the start of a connection and whenever the key changes. the very first record sent under any particular traffic key MUST use sequence number 0 ๐Ÿ’–

sequence numbers are 64 bits, so they should not wrap ๐Ÿ™ˆ if an implementation would have to wrap one anyway, it MUST either rekey (Section 4.6.3) or terminate the connection. no wrapping allowed, bestie ๐Ÿ’…

each AEAD algorithm gives a range of nonce lengths, from N_MIN bytes up to N_MAX bytes of input [RFC5116]. the TLS per-record nonce length (iv_length) is the larger of 8 bytes and the algorithm's N_MIN (see [RFC5116], Section 4). an AEAD algorithm whose N_MAX is less than 8 bytes MUST NOT be used with TLS ๐Ÿšซ the per-record nonce is built like this:

  1. the 64-bit record sequence number is encoded in network byte order and padded on the left with zeros to iv_length
  2. the padded sequence number gets XORed with either the static client_write_iv or server_write_iv, depending on your role

the result (exactly iv_length long) is the per-record nonce ๐ŸŽ€

Note: this is a different construction from TLS 1.2, which used a partially explicit nonce. so don't mix up your old notes, iykyk ๐ŸŒˆ

5.4. Record Padding ๐ŸŒธ

all encrypted TLS records can be padded so the TLSCiphertext gets bigger ๐Ÿงš this lets the sender hide how big their traffic is from anyone watching ๐Ÿ‘€

when generating a TLSCiphertext record, implementations MAY choose to pad ๐Ÿ’œ an unpadded record is just a record with a padding length of zero. padding is a string of zero-valued bytes tacked onto the ContentType field before encryption. implementations MUST set every padding octet to all zeros before encrypting ๐Ÿซถ

Application Data records MAY contain a zero-length TLSInnerPlaintext.content if the sender wants to. this makes plausibly sized cover traffic possible when even whether something is happening can be sensitive ๐Ÿคซ but Handshake and Alert records MUST NOT be sent with a zero-length TLSInnerPlaintext.content ๐Ÿ˜ญ if one arrives anyway, the receiver MUST terminate the connection with an "unexpected_message" alert ๐Ÿ’€

the padding gets checked automatically by the record protection mechanism. after a TLSCiphertext.encrypted_record decrypts successfully, the receiver scans from the end toward the beginning until it hits a non-zero octet. that octet is the message's content type โœจ this scheme was picked because it lets you pad any encrypted record by any size (from zero up to the TLS record size limits) without inventing new content types, and forcing all-zero padding makes padding errors easy to spot ๐Ÿ”ฅ

implementations MUST limit their scanning to the cleartext returned from the AEAD decryption. if the receiver doesn't find a non-zero octet in that cleartext, it MUST terminate the connection with an "unexpected_message" alert ๐Ÿ™…โ€โ™€๏ธ

padding doesn't change the overall record size limits: the full encoded TLSInnerPlaintext MUST NOT exceed 2^14 + 1 octets. if the maximum fragment length gets reduced (for example by the record_size_limit extension from [RFC8449]), then the reduced limit applies to the full plaintext, content type and padding included ๐Ÿ“

picking a padding policy (when to pad and how much) is a whole complicated topic, and it's out of scope for this spec ๐Ÿง if the application protocol on top of TLS has its own padding, it may be better to pad Application Data records up at that layer. padding for encrypted Handshake or Alert records still has to be handled at the TLS layer though ๐Ÿ’… later documents may define padding selection algorithms or a padding policy request mechanism through TLS extensions or some other means ๐ŸŒˆ


5.5. Limits on Key Usage ๐Ÿ”

there are cryptographic limits on how much plaintext you can safely encrypt under one set of keys โš ๏ธ [AEAD-LIMITS] analyzes these limits on the assumption that the underlying primitive (AES or ChaCha20) has no weaknesses. implementations SHOULD do a key update as described in Section 4.6.3 before they reach these limits ๐Ÿ”„

for AES-GCM, up to 2^24.5 full-size records (about 24 million) may be encrypted on a given connection while keeping a safety margin of approximately 2^-57 for Authenticated Encryption (AE) security ๐Ÿฆ‹ for ChaCha20/Poly1305, the record sequence number would wrap before the safety limit is reached ๐Ÿ˜ณ


6. Alert Protocol ๐Ÿšจ

TLS has an Alert content type for closure info and errors ๐Ÿ’Œ like other messages, alerts are encrypted as specified by the current connection state ๐Ÿ”’

alert messages carry a description, plus a legacy field that held the severity level in older versions of TLS. alerts come in two classes: closure alerts and error alerts ๐ŸŽ€ in TLS 1.3 the severity is implicit in the type of alert, so the "level" field can safely be ignored ๐Ÿ’โ€โ™€๏ธ the "close_notify" alert says one direction of the connection is closing in an orderly way. when a TLS implementation receives one, it SHOULD indicate end-of-data to the application ๐Ÿ“

error alerts mean the connection is closing abortively (see Section 6.2) ๐Ÿ’ฅ when one arrives, the TLS implementation SHOULD indicate an error to the application, and it MUST NOT allow any further data to be sent or received on the connection ๐Ÿ›‘ servers and clients MUST forget the secret values and keys established in failed connections, except the PSKs tied to session tickets, which SHOULD be discarded if possible ๐Ÿงน

all the alerts listed in Section 6.2 MUST be sent with AlertLevel=fatal, and they MUST be treated as error alerts when received, no matter what AlertLevel the message says ๐Ÿ‘‘ unknown Alert types MUST be treated as error alerts too ๐Ÿ™Œ

Note: TLS defines two generic alerts (see Section 6) to use when a message fails to parse. peers that get a message which can't be parsed according to the syntax (for example, a length running past the message boundary or an out-of-range length) MUST terminate the connection with a "decode_error" alert. peers that get a message which is syntactically correct but semantically invalid (for example, a DHE share of p - 1, or an invalid enum) MUST terminate the connection with an "illegal_parameter" alert ๐Ÿซฃ
enum { warning(1), fatal(2), (255) } AlertLevel;

enum {
    close_notify(0),
    unexpected_message(10),
    bad_record_mac(20),
    record_overflow(22),
    handshake_failure(40),
    bad_certificate(42),
    unsupported_certificate(43),
    certificate_revoked(44),
    certificate_expired(45),
    certificate_unknown(46),
    illegal_parameter(47),
    unknown_ca(48),
    access_denied(49),
    decode_error(50),
    decrypt_error(51),
    protocol_version(70),
    insufficient_security(71),
    internal_error(80),
    inappropriate_fallback(86),
    user_canceled(90),
    missing_extension(109),
    unsupported_extension(110),
    unrecognized_name(112),
    bad_certificate_status_response(113),
    unknown_psk_identity(115),
    certificate_required(116),
    no_application_protocol(120),
    (255)
} AlertDescription;

struct {
    AlertLevel level;
    AlertDescription description;
} Alert;

6.1. Closure Alerts ๐Ÿค

the client and the server must share the knowledge that the connection is ending, or else you get a truncation attack ๐Ÿ˜ฑ

close_notify: this alert tells the recipient that the sender will not send any more messages on this connection. any data received after a closure alert has been received MUST be ignored ๐Ÿ™ˆ

user_canceled: this alert tells the recipient that the sender is canceling the handshake for some reason that isn't a protocol failure. if a user cancels an operation after the handshake is complete, just closing the connection by sending a "close_notify" is more appropriate ๐Ÿ’ซ this alert SHOULD be followed by a "close_notify". it generally has AlertLevel=warning ๐ŸŒท

either party MAY start closing its write side of the connection by sending a "close_notify" alert. any data received after a closure alert has been received MUST be ignored. if a transport-level close arrives before a "close_notify", the receiver can't know that all the data that was sent has been received ๐Ÿ˜ฌ

each party MUST send a "close_notify" alert before closing its write side of the connection, unless it has already sent some error alert ๐Ÿ“จ this doesn't affect its read side of the connection. note that this is a change from TLS versions before 1.3, where implementations were required to react to a "close_notify" by discarding pending writes and sending an immediate "close_notify" alert of their own. that older requirement could cause truncation on the read side ๐Ÿฅบ both parties don't need to wait to receive a "close_notify" alert before closing their read side of the connection, though doing so would open the door to truncation ๐Ÿšช

if the application protocol using TLS says any data may be carried over the underlying transport after the TLS connection is closed, the TLS implementation MUST receive a "close_notify" alert before indicating end-of-data to the application layer ๐ŸŽฏ nothing in this standard should be read as dictating how a usage profile for TLS manages its data transport, including when connections are opened or closed ๐Ÿงธ

Note: it is assumed that closing the write side of a connection reliably delivers pending data before destroying the transport ๐Ÿ“ฆ

6.2. Error Alerts ๐Ÿšจ

Okay bestie, error handling in TLS is actually so simple it's giving clean. When something goes wrong, the detecting party just sends a message to its peer ๐Ÿ“ฃ. But if it's a fatal alert, sent or received, both parties MUST immediately close the connection ๐Ÿšช๐Ÿ’€.

Whenever an implementation runs into a fatal error condition, it SHOULD send an appropriate fatal alert, and then it MUST close the connection without sending or receiving any more data ๐Ÿ™…โ€โ™€๏ธ. Later in the spec, when you see "terminate the connection" or "abort the handshake" with no specific alert, it means the implementation SHOULD send the alert described below ๐ŸŽ€. But "terminate the connection with an X alert" or "abort the handshake with an X alert" is stricter: the implementation MUST send alert X if it sends any alert at all ๐Ÿ’…. All the alerts defined in this section, plus any unknown alerts, are universally considered fatal as of TLS 1.3 (see Section 6) ๐Ÿ’€. And the implementation SHOULD provide a way to log the sending and receiving of alerts, because future you will thank present you ๐Ÿ“โœจ.

Here are the error alerts that are defined ๐Ÿ‘‡

  • unexpected_message: An inappropriate message (like the wrong handshake message, premature Application Data, etc.) was received. This alert should never be observed in communication between proper implementations ๐Ÿ™ˆ.
  • bad_record_mac: This alert comes back if a record is received that can't be deprotected. AEAD algorithms combine decryption and verification, and to avoid side-channel attacks, this alert is used for ALL deprotection failures ๐Ÿ”. It should never be observed between proper implementations, except when messages were corrupted in the network ๐ŸŒ.
  • record_overflow: A TLSCiphertext record was received with a length more than 2^14 + 256 bytes, or a record decrypted to a TLSPlaintext record with more than 2^14 bytes (or some other negotiated limit) ๐Ÿ“ฆ๐Ÿ’ฅ. It should never be observed between proper implementations, except when messages were corrupted in the network ๐ŸŒ.
  • handshake_failure: Receiving a "handshake_failure" alert message means the sender couldn't negotiate an acceptable set of security parameters from the options available ๐Ÿคทโ€โ™€๏ธ.
  • bad_certificate: A certificate was corrupt, had signatures that didn't verify correctly, etc. ๐Ÿ“œโŒ
  • unsupported_certificate: A certificate was of an unsupported type ๐Ÿ™…โ€โ™€๏ธ.
  • certificate_revoked: A certificate was revoked by its signer ๐Ÿšซ.
  • certificate_expired: A certificate has expired or is not currently valid โฐ.
  • certificate_unknown: Some other (unspecified) issue came up while processing the certificate, making it unacceptable ๐Ÿซ .
  • illegal_parameter: A field in the handshake was incorrect or didn't match other fields. This is used for errors that follow the formal protocol syntax but are otherwise incorrect ๐Ÿง.
  • unknown_ca: A valid certificate chain or partial chain was received, but it wasn't accepted because the CA certificate couldn't be located or matched with a known trust anchor ๐Ÿ”.
  • access_denied: A valid certificate or PSK was received, but when access control was applied, the sender decided not to proceed with negotiation ๐Ÿšช๐Ÿ™…โ€โ™€๏ธ.
  • decode_error: A message couldn't be decoded because some field was out of the specified range or the message length was incorrect. This is for errors where the message doesn't follow the formal protocol syntax ๐Ÿงฉ. It should never be observed between proper implementations, except when messages were corrupted in the network ๐ŸŒ.
  • decrypt_error: A handshake (not record layer) cryptographic operation failed, including being unable to verify a signature or validate a Finished message or a PSK binder ๐Ÿ”‘๐Ÿ’”.
  • protocol_version: The protocol version the peer tried to negotiate is recognized but not supported (see Appendix D) ๐Ÿ•ฐ๏ธ.
  • insufficient_security: Sent instead of "handshake_failure" when a negotiation failed specifically because the server needs parameters more secure than the client supports ๐Ÿ›ก๏ธ.
  • internal_error: An internal error unrelated to the peer or the correctness of the protocol (like a memory allocation failure) makes it impossible to continue ๐Ÿ”ฅ.
  • inappropriate_fallback: Sent by a server in response to an invalid connection retry attempt from a client (see [RFC7507]) ๐Ÿ”.
  • missing_extension: Sent by endpoints that receive a handshake message without an extension that must be included for the offered TLS version or other negotiated parameters ๐Ÿงฉ.
  • unsupported_extension: Sent by endpoints that receive any handshake message containing an extension known to be prohibited in that handshake message, or that include any extensions in a ServerHello or Certificate that weren't first offered in the corresponding ClientHello or CertificateRequest ๐Ÿ™…โ€โ™€๏ธ.
  • unrecognized_name: Sent by servers when no server exists for the name the client provided via the "server_name" extension (see [RFC6066]) ๐Ÿ—บ๏ธ.
  • bad_certificate_status_response: Sent by clients when an invalid or unacceptable OCSP response is provided by the server via the "status_request" extension (see [RFC6066]) ๐Ÿ“‘.
  • unknown_psk_identity: Sent by servers when PSK key establishment is desired but no acceptable PSK identity is provided by the client. Sending this alert is OPTIONAL; servers MAY instead choose to send a "decrypt_error" alert to just indicate an invalid PSK identity ๐ŸŽŸ๏ธ.
  • certificate_required: Sent by servers when a client certificate is wanted but the client didn't provide one ๐Ÿชช.
  • no_application_protocol: Sent by servers when the client's "application_layer_protocol_negotiation" extension advertises only protocols the server doesn't support (see [RFC7301]) ๐Ÿ—ฃ๏ธ.

New Alert values are assigned by IANA as described in Section 11 ๐Ÿ“‹๐ŸŒธ.


7. Cryptographic Computations ๐Ÿ”โœจ

Okay so the TLS handshake sets up one or more input secrets, which get combined to create the actual working keying material ๐Ÿ—๏ธ. The key derivation process uses both the input secrets and the handshake transcript ๐Ÿง. Here's the lowkey iconic part: because the handshake transcript includes the random values from the Hello messages, any given handshake gets different traffic secrets, even when the same input secrets are used. That happens when the same PSK is reused across multiple connections ๐Ÿฆ‹๐Ÿ’œ.

7.1. Key Schedule ๐Ÿ”‘

okay so the key derivation process is where the magic happens โœจ it uses HKDF-Extract and HKDF-Expand, as defined in [RFC5869], plus a few helper functions we define right here:

HKDF-Expand-Label(Secret, Label, Context, Length) =
     HKDF-Expand(Secret, HkdfLabel, Length)

Where HkdfLabel is specified as:

struct {
    uint16 length = Length;
    opaque label<7..255> = "tls13 " + Label;
    opaque context<0..255> = Context;
} HkdfLabel;

Derive-Secret(Secret, Label, Messages) =
     HKDF-Expand-Label(Secret, Label,
                       Transcript-Hash(Messages), Hash.length)

The Hash function used by Transcript-Hash and HKDF is the cipher suite hash algorithm, and Hash.length is its output length in bytes ๐Ÿ’… Messages is the concatenation of the indicated handshake messages. That includes the handshake message type and length fields, but NOT the record layer headers. Heads up: sometimes a zero-length Context (written as "") gets passed to HKDF-Expand-Label ๐Ÿ™ˆ Also, all the labels in this document are ASCII strings with no trailing NUL byte.

Quick note from me: with common hash functions, any label longer than 12 characters needs an extra round of the hash function to compute. Every label in this spec was picked to fit under that limit, so we're good ๐Ÿซถ

Keys come from two input secrets, using HKDF-Extract and Derive-Secret. The general move for adding a new secret is to run HKDF-Extract with the Salt set to the current secret state and the Input Keying Material (IKM) set to the new secret. In this version of TLS 1.3, the two input secrets are:

  • a PSK (a pre-shared key set up externally, or derived from the resumption_master_secret of a previous connection) ๐ŸŽ€
  • the (EC)DHE shared secret (see Section 7.4)

Put it all together and you get the full key derivation schedule below ๐ŸŒธ Here's how to read the diagram: HKDF-Extract takes its Salt from the top and its IKM from the left, and the output goes down, with the output's name on the right. Derive-Secret's Secret argument comes in through the incoming arrow, so for example the Early Secret is the Secret used to generate the client_early_traffic_secret. And "0" means a string of Hash.length bytes set to zero.

          0
          |
          v
PSK ->  HKDF-Extract = Early Secret
          |
          +-----> Derive-Secret(., "ext binder" | "res binder", "")
          |                     = binder_key
          |
          +-----> Derive-Secret(., "c e traffic", ClientHello)
          |                     = client_early_traffic_secret
          |
          +-----> Derive-Secret(., "e exp master", ClientHello)
          |                     = early_exporter_master_secret
          v
    Derive-Secret(., "derived", "")
          |
          v
(EC)DHE -> HKDF-Extract = Handshake Secret
          |
          +-----> Derive-Secret(., "c hs traffic",
          |                     ClientHello...ServerHello)
          |                     = client_handshake_traffic_secret
          |
          +-----> Derive-Secret(., "s hs traffic",
          |                     ClientHello...ServerHello)
          |                     = server_handshake_traffic_secret
          v
    Derive-Secret(., "derived", "")
          |
          v
0 -> HKDF-Extract = Master Secret
          |
          +-----> Derive-Secret(., "c ap traffic",
          |                     ClientHello...server Finished)
          |                     = client_application_traffic_secret_0
          |
          +-----> Derive-Secret(., "s ap traffic",
          |                     ClientHello...server Finished)
          |                     = server_application_traffic_secret_0
          |
          +-----> Derive-Secret(., "exp master",
          |                     ClientHello...server Finished)
          |                     = exporter_master_secret
          |
          +-----> Derive-Secret(., "res master",
                                ClientHello...client Finished)
                                = resumption_master_secret

The vibe here: the secrets down the LEFT side are just raw entropy with no context ๐Ÿฆ‹ The ones down the RIGHT side include Handshake Context, so they can be used to derive working keys with no extra context needed. Also, different calls to Derive-Secret can take different Messages arguments, even when they use the same secret. In a 0-RTT exchange, Derive-Secret gets called with four distinct transcripts ๐Ÿ’œ In a 1-RTT-only exchange, it gets called with three.

If a given secret isn't available, the 0-value (a string of Hash.length bytes set to zeros) gets used instead. And this does NOT mean skipping rounds, so if no PSK is in use, the Early Secret is still HKDF-Extract(0, 0) โœจ For computing the binder_key, the label is "ext binder" for external PSKs (the ones provisioned outside of TLS) and "res binder" for resumption PSKs (the ones provisioned as the resumption master secret of a previous handshake). Those different labels stop one type of PSK from being swapped in for the other ๐Ÿ›ก๏ธ

There can be multiple potential Early Secret values, depending on which PSK the server ends up selecting. The client has to compute one for each potential PSK. If no PSK gets selected, it then has to compute the Early Secret for the zero PSK ๐Ÿ“

Once every value that's going to be derived from a given secret has been computed, that secret SHOULD be erased ๐Ÿงผ

7.2. Updating Traffic Secrets ๐Ÿ”„

Once the handshake is done, either side can update its sending traffic keys by using the KeyUpdate handshake message from Section 4.6.3 ๐Ÿ’– The next generation of traffic keys comes from generating client_/server_application_traffic_secret_N+1 out of client_/server_application_traffic_secret_N (as described in this section), and then re-deriving the traffic keys as described in Section 7.3.

The next-generation application_traffic_secret is computed like this:

    application_traffic_secret_N+1 =
        HKDF-Expand-Label(application_traffic_secret_N,
                          "traffic upd", "", Hash.length)

Once client_/server_application_traffic_secret_N+1 and its traffic keys have been computed, implementations SHOULD delete client_/server_application_traffic_secret_N and its traffic keys ๐Ÿ—‘๏ธโœจ

7.3. Traffic Key Calculation ๐Ÿ—๏ธ

The traffic keying material is generated from these three input values:

  • a secret value ๐Ÿคซ
  • a purpose value that says which specific value is being generated
  • the length of the key being generated

Concretely, the traffic keying material comes from an input traffic secret using:

[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv  = HKDF-Expand-Label(Secret, "iv", "", iv_length)

[sender] just means the sending side ๐Ÿ’… The value of Secret for each record type is in the table below:

    +-------------------+---------------------------------------+
    | Record Type       | Secret                                |
    +-------------------+---------------------------------------+
    | 0-RTT Application | client_early_traffic_secret           |
    |                   |                                       |
    | Handshake         | [sender]_handshake_traffic_secret     |
    |                   |                                       |
    | Application Data  | [sender]_application_traffic_secret_N |
    +-------------------+---------------------------------------+

All the traffic keying material gets recomputed whenever the underlying Secret changes. For example, that happens when you move from the handshake keys to the Application Data keys, or when there's a key update ๐Ÿ”

7.4. (EC)DHE Shared Secret Calculation ๐Ÿค

7.4.1. Finite Field Diffie-Hellman ๐Ÿ”ข

For finite field groups, a conventional Diffie-Hellman [DH76] computation gets performed ๐Ÿ’ซ The negotiated key (Z) is turned into a byte string by encoding it in big-endian form and left-padding it with zeros up to the size of the prime. That byte string is then used as the shared secret in the key schedule, as specified above.

Heads up, this construction is different from earlier versions of TLS, which removed leading zeros ๐Ÿ‘€

7.4.2. Elliptic Curve Diffie-Hellman ๐ŸŒˆ

For secp256r1, secp384r1, and secp521r1, ECDH calculations (including parameter and key generation, as well as the shared secret calculation) are done according to [IEEE1363]. They use the ECKAS-DH1 scheme with the identity map as the key derivation function (KDF), so the shared secret is the x-coordinate of the ECDH shared secret elliptic curve point, represented as an octet string. This octet string ("Z" in IEEE 1363 terminology), as output by FE2OSP (the Field Element to Octet String Conversion Primitive), has a constant length for any given field. Leading zeros found in this octet string MUST NOT be truncated ๐Ÿ™…โ€โ™€๏ธ

(Okay, this use of the identity KDF is kind of a technicality. The full picture is that ECDH is used with a non-trivial KDF, because TLS doesn't directly use this secret for anything except computing other secrets ๐Ÿค“)

For X25519 and X448, the ECDH calculations go like this:

  • The public key that goes into the KeyShareEntry.key_exchange structure is the result of applying the ECDH scalar multiplication function to the secret key (as the scalar input) and the standard public basepoint (as the u-coordinate point input). ๐ŸŽฏ
  • The ECDH shared secret is the result of applying the ECDH scalar multiplication function to the secret key (as the scalar input) and the peer's public key (as the u-coordinate point input). The output gets used raw, with no processing ๐Ÿงƒ

For these curves, implementations SHOULD use the approach specified in [RFC7748] to calculate the Diffie-Hellman shared secret. Implementations MUST check whether the computed Diffie-Hellman shared secret is the all-zero value, and abort if it is, as described in Section 6 of [RFC7748] ๐Ÿšจ If implementors use an alternative implementation of these elliptic curves, they SHOULD perform the additional checks specified in Section 7 of [RFC7748] ๐Ÿ’œ

7.5. Exporters โœจ

okay so [RFC5705] is the spec that defines keying material exporters for TLS, and it's built on the TLS PRF ๐Ÿ”‘. This document swaps the PRF out for HKDF, which means we need a brand new construction, BUT the exporter interface stays exactly the same ๐Ÿ’… no drama for the people using it.

Here's how the exporter value gets computed ๐Ÿฆ‹:

TLS-Exporter(label, context_value, key_length) =
    HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
                      "exporter", Hash(context_value), key_length)

Where Secret is either the early_exporter_master_secret or the exporter_master_secret. Implementations MUST use the exporter_master_secret unless explicitly specified by the application. The early_exporter_master_secret is defined for use in settings where an exporter is needed for 0-RTT data. A separate interface for the early exporter is RECOMMENDED, because this avoids the exporter user accidentally grabbing an early exporter when a regular one is desired, or vice versa ๐Ÿ™ˆ that's a whole mix-up we do not need.

If no context is provided, the context_value is zero length. Consequently, providing no context computes the same value as providing an empty context. This is a change from previous versions of TLS, where an empty context produced a different output than an absent context ๐Ÿ‘€ plot twist. As of this document's publication, no allocated exporter label is used both with and without a context. Future specifications MUST NOT define a use of exporters that permit both an empty context and no context with the same label. New uses of exporters SHOULD provide a context in all exporter computations, though the value could be empty ๐ŸŒธ

Requirements for the format of exporter labels are defined in Section 4 of [RFC5705].


8. 0-RTT and Anti-Replay ๐ŸŒ™

As noted in Section 2.3 and Appendix E.5, TLS does not provide inherent replay protections for 0-RTT data. There are two potential threats to be concerned with, and honestly both are a big deal ๐Ÿ˜ญ

  • Network attackers who mount a replay attack by simply duplicating a flight of 0-RTT data.
  • Network attackers who take advantage of client retry behavior to arrange for the server to receive multiple copies of an application message. This threat already exists to some extent, because clients that value robustness respond to network errors by attempting to retry requests. However, 0-RTT adds an additional dimension for any server system which does not maintain globally consistent server state. Specifically, if a server system has multiple zones where tickets from zone A will not be accepted in zone B, then an attacker can duplicate a ClientHello and early data intended for A to both A and B. At A, the data will be accepted in 0-RTT, but at B the server will reject 0-RTT data and instead force a full handshake. If the attacker blocks the ServerHello from A, then the client will complete the handshake with B and probably retry the request, leading to duplication on the server system as a whole ๐Ÿ’€

The first class of attack can be prevented by sharing state to guarantee that the 0-RTT data is accepted at most once. Servers SHOULD provide that level of replay safety by implementing one of the methods described in this section or by equivalent means. It is understood, however, that due to operational concerns, not all deployments will maintain state at that level. Therefore, in normal operation, clients will not know which, if any, of these mechanisms servers actually implement, and hence clients MUST only send early data which they deem safe to be replayed ๐Ÿซถ

In addition to the direct effects of replays, there is a class of attacks where even operations normally considered idempotent could be exploited by a large number of replays (timing attacks, resource limit exhaustion and others, as described in Appendix E.5). Those can be mitigated by ensuring that every 0-RTT payload can be replayed only a limited number of times. The server MUST ensure that any instance of it (be it a machine, a thread, or any other entity within the relevant serving infrastructure) would accept 0-RTT for the same 0-RTT handshake at most once. This limits the number of replays to the number of server instances in the deployment โœจ Such a guarantee can be accomplished by locally recording data from recently received ClientHellos and rejecting repeats, or by any other method that provides the same or a stronger guarantee. The "at most once per server instance" guarantee is a minimum requirement; servers SHOULD limit 0-RTT replays further when feasible.

The second class of attack cannot be prevented at the TLS layer and MUST be dealt with by any application. Note that any application whose clients implement any kind of retry behavior already needs to implement some sort of anti-replay defense ๐Ÿงš


8.1. Single-Use Tickets ๐ŸŽŸ๏ธ

The simplest form of anti-replay defense is for the server to only allow each session ticket to be used once. For instance, the server can maintain a database of all outstanding valid tickets, deleting each ticket from the database as it is used. If an unknown ticket is provided, the server would then fall back to a full handshake.

If the tickets are not self-contained but rather are database keys, and the corresponding PSKs are deleted upon use, then connections established using PSKs enjoy forward secrecy. This improves security for all 0-RTT data and PSK usage when PSK is used without (EC)DHE ๐Ÿ”

Because this mechanism requires sharing the session database between server nodes in environments with multiple distributed servers, it may be hard to achieve high rates of successful PSK 0-RTT connections when compared to self-encrypted tickets. Unlike session databases, session tickets can successfully do PSK-based session establishment even without consistent storage. However, when 0-RTT is allowed, they still require consistent storage for anti-replay of 0-RTT data, as detailed in the following section ๐Ÿ“


8.2. Client Hello Recording ๐Ÿ•ต๏ธ

An alternative form of anti-replay is to record a unique value derived from the ClientHello (generally either the random value or the PSK binder) and reject duplicates. Recording all ClientHellos causes state to grow without bound, but a server can instead record ClientHellos within a given time window and use the obfuscated_ticket_age to ensure that tickets aren't reused outside that window. big brain move ๐Ÿง 

In order to implement this, when a ClientHello is received, the server first verifies the PSK binder as described in Section 4.2.11. It then computes the expected_arrival_time as described in the next section and rejects 0-RTT if it is outside the recording window, falling back to the 1-RTT handshake.

If the expected_arrival_time is in the window, then the server checks to see if it has recorded a matching ClientHello. If one is found, it either aborts the handshake with an illegal_parameter alert or accepts the PSK but rejects 0-RTT. If no matching ClientHello is found, then it accepts 0-RTT and then stores the ClientHello for as long as the expected_arrival_time is inside the window. Servers MAY also implement data stores with false positives, such as Bloom filters, in which case they MUST respond to apparent replay by rejecting 0-RTT but MUST NOT abort the handshake ๐Ÿฆ‹

The server MUST derive the storage key only from validated sections of the ClientHello. If the ClientHello contains multiple PSK identities, then an attacker can create multiple ClientHellos with different binder values for the less-preferred identity, on the assumption that the server will not verify it (as recommended by Section 4.2.11). I.e., if the client sends PSKs A and B but the server prefers A, then the attacker can change the binder for B without affecting the binder for A. If the binder for B is part of the storage key, then this ClientHello will not appear as a duplicate, which will cause the ClientHello to be accepted, and may cause side effects such as replay cache pollution. Any 0-RTT data will not be decryptable, though, because it will use different keys. If the validated binder or the ClientHello.random is used as the storage key, then this attack is not possible ๐Ÿ”ฅ

Because this mechanism does not require storing all outstanding tickets, it may be easier to implement in distributed systems with high rates of resumption and 0-RTT, at the cost of potentially weaker anti-replay defense because of the difficulty of reliably storing and retrieving the received ClientHello messages. In many such systems, it is impractical to have globally consistent storage of all the received ClientHellos. In this case, the best anti-replay protection is provided by having a single storage zone be authoritative for a given ticket and refusing 0-RTT for that ticket in any other zone. This approach prevents simple replay by the attacker, because only one zone will accept 0-RTT data. A weaker design is to implement separate storage for each zone but allow 0-RTT in any zone. This approach limits the number of replays to once per zone. Application message duplication of course remains possible with either design ๐ŸŒˆ

When implementations are freshly started, they SHOULD reject 0-RTT as long as any portion of their recording window overlaps the startup time. Otherwise, they run the risk of accepting replays which were originally sent during that period โญ

Note: If the client's clock is running much faster than the server's, then a ClientHello may be received that is outside the window in the future, in which case it might be accepted for 1-RTT, causing a client retry, and then acceptable later for 0-RTT. This is another variant of the second form of attack described in Section 8.

8.3. Freshness Checks โฐ

okay so the ClientHello tells the server when the client sent it, which means the server can efficiently check whether it was sent pretty recently ๐Ÿ•ฐ๏ธ. If it was, the server accepts 0-RTT, and if not, it falls back to a 1-RTT handshake. This is needed for the ClientHello storage mechanism in Section 8.2, because otherwise the server would have to store an unlimited number of ClientHellos ๐Ÿ˜ญ. It's also a super useful optimization for self-contained single-use tickets, since it lets the server quickly reject ClientHellos that can't be used for 0-RTT ๐Ÿ’….

To make this work, a server needs to store the time it generated the session ticket, offset by an estimate of the round-trip time between client and server. Like this:

adjusted_creation_time = creation_time + estimated_RTT

This value can be encoded in the ticket itself, so the server doesn't need to keep state for every outstanding ticket ๐Ÿงš. The server can figure out the client's view of the ticket's age by subtracting the ticket's "ticket_age_add" value from the "obfuscated_ticket_age" parameter in the client's "pre_shared_key" extension. Then it can work out the expected_arrival_time of the ClientHello:

expected_arrival_time = adjusted_creation_time + clients_ticket_age

When a new ClientHello comes in, the expected_arrival_time gets compared against the server's current wall clock time. If they differ by more than a certain amount, 0-RTT gets rejected, but the 1-RTT handshake can still be allowed to finish ๐Ÿฆ‹.

There are a few potential sources of error that could cause mismatches between the expected_arrival_time and the measured time. Variations in client and server clock rates are likely to be pretty minimal, though the absolute times could be off by big values ๐Ÿ™ˆ. Network propagation delays are the most likely culprits for mismatches in legit elapsed time. Both the NewSessionTicket and ClientHello messages might get retransmitted and delayed, which TCP might hide ๐Ÿซ . For clients on the Internet, this works out to windows on the order of ten seconds to cover clock errors and measurement wobbles. Other deployment scenarios might need different windows. Also, clock skew distributions aren't symmetric, so the optimal tradeoff might involve an asymmetric range of acceptable mismatch values โœจ.

โš ๏ธ okay bestie, real talk: freshness checking alone is not enough to stop replays, because it can't catch them inside the error window. Depending on bandwidth and system capacity, that window could let through billions of replays in real-world settings ๐Ÿ˜ฑ. Also, the freshness check only happens when the ClientHello shows up, not when the later early Application Data records arrive. Once early data is accepted, records can keep streaming to the server for a much longer time ๐Ÿ’€.

9. Compliance Requirements ๐Ÿ’…

9.1. Mandatory-to-Implement Cipher Suites ๐Ÿ”

Unless an application profile standard says otherwise, here's the deal:

A TLS-compliant application MUST implement the TLS_AES_128_GCM_SHA256 [GCM] cipher suite, and SHOULD implement the TLS_AES_256_GCM_SHA384 [GCM] and TLS_CHACHA20_POLY1305_SHA256 [RFC8439] cipher suites (see Appendix B.4).

A TLS-compliant application MUST support digital signatures with rsa_pkcs1_sha256 (for certificates), rsa_pss_rsae_sha256 (for CertificateVerify and certificates), and ecdsa_secp256r1_sha256. A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256), and SHOULD support key exchange with X25519 [RFC7748] ๐ŸŒธ.

9.2. Mandatory-to-Implement Extensions ๐ŸŽ€

Unless an application profile standard says otherwise, a TLS-compliant application MUST implement the following TLS extensions:

  • Supported Versions ("supported_versions"; Section 4.2.1)
  • Cookie ("cookie"; Section 4.2.2)
  • Signature Algorithms ("signature_algorithms"; Section 4.2.3)
  • Signature Algorithms Certificate ("signature_algorithms_cert"; Section 4.2.3)
  • Negotiated Groups ("supported_groups"; Section 4.2.7)
  • Key Share ("key_share"; Section 4.2.8)
  • Server Name Indication ("server_name"; Section 3 of [RFC6066])

All implementations MUST send and use these extensions when offering applicable features:

  • "supported_versions" is REQUIRED for all ClientHello, ServerHello, and HelloRetryRequest messages.
  • "signature_algorithms" is REQUIRED for certificate authentication.
  • "supported_groups" is REQUIRED for ClientHello messages using DHE or ECDHE key exchange.
  • "key_share" is REQUIRED for DHE or ECDHE key exchange.
  • "pre_shared_key" is REQUIRED for PSK key agreement.
  • "psk_key_exchange_modes" is REQUIRED for PSK key agreement.

A client is considered to be attempting to negotiate using this specification if the ClientHello contains a "supported_versions" extension with 0x0304 in its body. Such a ClientHello message MUST meet the following requirements:

  • If it doesn't contain a "pre_shared_key" extension, it MUST contain both a "signature_algorithms" extension and a "supported_groups" extension.
  • If it contains a "supported_groups" extension, it MUST also contain a "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is permitted.

Servers receiving a ClientHello that doesn't meet these requirements MUST abort the handshake with a "missing_extension" alert.

Additionally, all implementations MUST support the use of the "server_name" extension with applications capable of using it. Servers MAY require clients to send a valid "server_name" extension. Servers requiring this extension SHOULD respond to a ClientHello lacking a "server_name" extension by terminating the connection with a "missing_extension" alert ๐Ÿ™ˆ.

9.3. Protocol Invariants ๐Ÿฆ‹

This section describes invariants that TLS endpoints and middleboxes MUST follow. It also applies to earlier versions of TLS.

TLS is designed to be securely and compatibly extensible. Newer clients or servers, when talking to newer peers, should ideally negotiate the most preferred common parameters. The TLS handshake provides downgrade protection: middleboxes passing traffic between a newer client and a newer server without terminating TLS should be unable to influence the handshake (see Appendix E.1). At the same time, deployments update at different rates, so a newer client or server MAY continue to support older parameters, which would let it interoperate with older endpoints ๐ŸŒˆ.

For this to work, implementations MUST correctly handle extensible fields:

  • A client sending a ClientHello MUST support all parameters advertised in it. Otherwise, the server might fail to interoperate by selecting one of those parameters.
  • A server receiving a ClientHello MUST correctly ignore all unrecognized cipher suites, extensions, and other parameters. Otherwise, it might fail to interoperate with newer clients. In TLS 1.3, a client receiving a CertificateRequest or NewSessionTicket MUST also ignore all unrecognized extensions.
  • A middlebox which terminates a TLS connection MUST behave as a compliant TLS server (to the original client), including having a certificate which the client is willing to accept, and also as a compliant TLS client (to the original server), including verifying the original server's certificate. In particular, it MUST generate its own ClientHello containing only parameters it understands, and it MUST generate a fresh ServerHello random value, rather than forwarding the endpoint's value.

Note that TLS's protocol requirements and security analysis only apply to the two connections separately. Safely deploying a TLS terminator requires additional security considerations which are beyond the scope of this document ๐Ÿ”.

  • A middlebox which forwards ClientHello parameters it does not understand MUST NOT process any messages beyond that ClientHello. It MUST forward all subsequent traffic unmodified. Otherwise, it might fail to interoperate with newer clients and servers.

Forwarded ClientHellos might contain advertisements for features the middlebox doesn't support, so the response might include future TLS additions the middlebox doesn't recognize. These additions MAY change any message beyond the ClientHello arbitrarily. In particular, the values sent in the ServerHello might change, the ServerHello format might change, and the TLSCiphertext format might change ๐Ÿ˜ต.

The design of TLS 1.3 was constrained by widely deployed non-compliant TLS middleboxes (see Appendix D.4). However, it does not relax the invariants. Those middleboxes continue to be non-compliant ๐Ÿ™…โ€โ™€๏ธ.


10. Security Considerations ๐Ÿ”ฅ

Security issues are discussed throughout this memo, especially in Appendices C, D, and E. Read those appendices, bestie, because security is literally the whole vibe ๐Ÿ”๐Ÿ’œ.

11. IANA Considerations โœจ

okay bestie, this section is basically the registry paperwork ๐Ÿ“‹๐Ÿ’…. the IANA folks look after the big lists of protocol values, and this doc tells them how to update those lists for TLS 1.3. the registries were first made in [RFC4346] and then tweaked by [RFC8447], and now IANA has pointed them at this document too ๐Ÿฆ‹.

Here's the vibe for each registry ๐Ÿ’œ

  • TLS Cipher Suites registry: values whose first byte is 0-254 (decimal) get handed out via Specification Required [RFC8126]. Values with first byte 255 (decimal) are reserved for Private Use [RFC8126]. ๐Ÿ”’

IANA added the cipher suites from Appendix B.4 to the registry, taking the "Value" and "Description" columns straight from that table ๐ŸŒธ. Every new cipher suite is marked "Y" in both the "DTLS-OK" and "Recommended" columns, so they're all giving main character energy ๐Ÿ‘‘.

  • TLS ContentType registry: future values are allocated via Standards Action [RFC8126] ๐ŸŽ€.
  • TLS Alerts registry: future values are allocated via Standards Action [RFC8126] ๐Ÿ’€. IANA filled this one in with the values from Appendix B.2, and the "DTLS-OK" column is "Y" for all of them. Anything marked "_RESERVED" has a comment explaining what it used to do ๐Ÿ™ˆ.
  • TLS HandshakeType registry: future values are allocated via Standards Action [RFC8126] โœจ. IANA renamed item 4 from "NewSessionTicket" to "new_session_ticket" ๐Ÿงš and filled in the values from Appendix B.3. "DTLS-OK" is "Y" for all of those too. Anything marked "_RESERVED" has comments about its old or temporary use ๐Ÿซถ.

This document also uses the TLS ExtensionType Values registry, which was first created in [RFC4366] ๐Ÿ’–. IANA updated it to point at this document, and here's what changed ๐ŸŒˆ.

  • IANA changed the registration policy like so: values whose first byte is 0-254 (decimal) are assigned via Specification Required [RFC8126], and values whose first byte is 255 (decimal) are reserved for Private Use [RFC8126] ๐Ÿ”ฅ.
  • IANA added these extensions with the values defined here and a "Recommended" value of "Y": "key_share", "pre_shared_key", "psk_key_exchange_modes", "early_data", "cookie", "supported_versions", "certificate_authorities", "oid_filters", "post_handshake_auth", and "signature_algorithms_cert" โœจ๐Ÿ’….
  • IANA added a "TLS 1.3" column that lists which messages each extension may show up in ๐Ÿฆ‹. It's filled in from the table in Section 4.2, and any extension that isn't listed there gets a "-" to show it isn't used by TLS 1.3 ๐Ÿ™ˆ.

Two other registries got little updates too ๐ŸŽ€.

This document updates an entry in the TLS Certificate Types registry, which was first made in [RFC6091] and then updated in [RFC8447] ๐ŸŒธ. Value 1 is now named "OpenPGP_RESERVED", its "Recommended" value is "N", and its comment says "Used in TLS versions prior to 1.3." ๐Ÿ”’

This document also updates an entry in the TLS Certificate Status Types registry, first made in [RFC6961] ๐Ÿ’œ. Value 2 is now named "ocsp_multi_RESERVED", with the comment "Used in TLS versions prior to 1.3" ๐Ÿ’€.

This document updates two entries in the TLS Supported Groups registry ๐Ÿงš. That one was first created under a different name by [RFC4492], is now maintained by [RFC8422], and was updated by [RFC7919] and [RFC8447]. The entries for values 29 and 30 (x25519 and x448) now also refer to this document โœจ.

And then there are two brand new registries that IANA maintains ๐Ÿ‘‘.

  • TLS SignatureScheme registry: values whose first byte is 0-253 (decimal) are assigned via Specification Required [RFC8126]. Values whose first byte is 254 or 255 (decimal) are reserved for Private Use [RFC8126]. Values whose first byte is in the range 0-6, or whose second byte is in the range 0-3, that aren't currently allocated are reserved for backward compatibility ๐Ÿ”ฅ. This registry has a "Recommended" column and was initially populated with the values in Section 4.2.3 ๐ŸŒˆ.

These values are marked "Recommended": ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, and ed25519 ๐Ÿ’–.

The "Recommended" column is assigned "N" unless someone explicitly asks for something else. Adding a value with "Recommended" set to "Y" needs Standards Action [RFC8126]. IESG Approval is REQUIRED for a Y->N transition ๐Ÿ˜ญ.

  • TLS PskKeyExchangeMode registry: values in the range 0-253 (decimal) are assigned via Specification Required [RFC8126]. The values 254 and 255 (decimal) are reserved for Private Use [RFC8126] ๐Ÿซถ. This registry also has a "Recommended" column, and it was initially populated with psk_ke (0) and psk_dhe_ke (1), both marked "Recommended" ๐Ÿฆ‹. The "Recommended" column is "N" unless explicitly requested, and adding a value with "Recommended" set to "Y" requires Standards Action [RFC8126]. IESG Approval is REQUIRED for a Y->N transition ๐Ÿ’….

12. References ๐Ÿ“š

12.1. Normative References

Here's the reading list the spec needs to make sense ๐Ÿง .

12.2. Informative References ๐Ÿ“š

okay bestie, this is the extra reading list ๐Ÿงš papers, mailing list threads, slides, and older RFCs that back up the spec. the cryptography nerds in your life are going to be obsessed ๐Ÿ’œ

  • [AEAD-LIMITS] Luykx, A. and K. Paterson, "Limits on Authenticated Encryption Use in TLS", August 2017, http://www.isg.rhul.ac.uk/~kp/TLS-AEbounds.pdf.
  • [BBFGKZ16] Bhargavan, K., Brzuska, C., Fournet, C., Green, M., Kohlweiss, M., and S. Zanella-Beguelin, "Downgrade Resilience in Key-Exchange Protocols", Proceedings of IEEE Symposium on Security and Privacy (San Jose), DOI 10.1109/SP.2016.37, May 2016.
  • [BBK17] Bhargavan, K., Blanchet, B., and N. Kobeissi, "Verified Models and Reference Implementations for the TLS 1.3 Standard Candidate", Proceedings of IEEE Symposium on Security and Privacy (San Jose), DOI 10.1109/SP.2017.26, May 2017.
  • [BDFKPPRSZZ16] Bhargavan, K., Delignat-Lavaud, A., Fournet, C., Kohlweiss, M., Pan, J., Protzenko, J., Rastogi, A., Swamy, N., Zanella-Beguelin, S., and J. Zinzindohoue, "Implementing and Proving the TLS 1.3 Record Layer", Proceedings of IEEE Symposium on Security and Privacy (San Jose), May 2017, https://eprint.iacr.org/2016/1178.
  • [Ben17a] Benjamin, D., "Presentation before the TLS WG at IETF 100", November 2017, https://datatracker.ietf.org/meeting/100/materials/slides-100-tls-sessa-tls13/.
  • [Ben17b] Benjamin, D., "Additional TLS 1.3 results from Chrome", message to the TLS mailing list, 18 December 2017, https://www.ietf.org/mail-archive/web/tls/current/msg25168.html.
  • [Blei98] Bleichenbacher, D., "Chosen Ciphertext Attacks against Protocols Based on RSA Encryption Standard PKCS #1", Proceedings of CRYPTO '98, 1998.
  • [BMMRT15] Badertscher, C., Matt, C., Maurer, U., Rogaway, P., and B. Tackmann, "Augmented Secure Channels and the Goal of the TLS 1.3 Record Layer", ProvSec 2015, September 2015, https://eprint.iacr.org/2015/394.
  • [BT16] Bellare, M. and B. Tackmann, "The Multi-User Security of Authenticated Encryption: AES-GCM in TLS 1.3", Proceedings of CRYPTO 2016, July 2016, https://eprint.iacr.org/2016/564.
  • [CCG16] Cohn-Gordon, K., Cremers, C., and L. Garratt, "On Post-compromise Security", IEEE Computer Security Foundations Symposium, DOI 10.1109/CSF.2016.19, July 2015.
  • [CHECKOWAY] Checkoway, S., Maskiewicz, J., Garman, C., Fried, J., Cohney, S., Green, M., Heninger, N., Weinmann, R., Rescorla, E., and H. Shacham, "A Systematic Analysis of the Juniper Dual EC Incident", Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security - CCS '16, DOI 10.1145/2976749.2978395, October 2016.
  • [CHHSV17] Cremers, C., Horvat, M., Hoyland, J., Scott, S., and T. van der Merwe, "Awkward Handshake: Possible mismatch of client/server view on client authentication in post-handshake mode in Revision 18", message to the TLS mailing list, 10 February 2017, https://www.ietf.org/mail-archive/web/tls/current/msg22382.html.
  • [CHSV16] Cremers, C., Horvat, M., Scott, S., and T. van der Merwe, "Automated Analysis and Verification of TLS 1.3: 0-RTT, Resumption and Delayed Authentication", Proceedings of IEEE Symposium on Security and Privacy (San Jose), DOI 10.1109/SP.2016.35, May 2016, https://ieeexplore.ieee.org/document/7546518/.
  • [CK01] Canetti, R. and H. Krawczyk, "Analysis of Key-Exchange Protocols and Their Use for Building Secure Channels", Proceedings of Eurocrypt 2001, DOI 10.1007/3-540-44987-6_28, April 2001.
  • [CLINIC] Miller, B., Huang, L., Joseph, A., and J. Tygar, "I Know Why You Went to the Clinic: Risks and Realization of HTTPS Traffic Analysis", Privacy Enhancing Technologies, pp. 143-163, DOI 10.1007/978-3-319-08506-7_8, 2014.
  • [DFGS15] Dowling, B., Fischlin, M., Guenther, F., and D. Stebila, "A Cryptographic Analysis of the TLS 1.3 Handshake Protocol Candidates", Proceedings of ACM CCS 2015, October 2015, https://eprint.iacr.org/2015/914.
  • [DFGS16] Dowling, B., Fischlin, M., Guenther, F., and D. Stebila, "A Cryptographic Analysis of the TLS 1.3 Full and Pre-shared Key Handshake Protocol", TRON 2016, February 2016, https://eprint.iacr.org/2016/081.
  • [DOW92] Diffie, W., van Oorschot, P., and M. Wiener, "Authentication and authenticated key exchanges", Designs, Codes and Cryptography, DOI 10.1007/BF00124891, June 1992.
  • [DSS] National Institute of Standards and Technology, U.S. Department of Commerce, "Digital Signature Standard (DSS)", NIST FIPS PUB 186-4, DOI 10.6028/NIST.FIPS.186-4, July 2013.
  • [FG17] Fischlin, M. and F. Guenther, "Replay Attacks on Zero Round-Trip Time: The Case of the TLS 1.3 Handshake Candidates", Proceedings of EuroS&P 2017, April 2017, https://eprint.iacr.org/2017/082.
  • [FGSW16] Fischlin, M., Guenther, F., Schmidt, B., and B. Warinschi, "Key Confirmation in Key Exchange: A Formal Treatment and Implications for TLS 1.3", Proceedings of IEEE Symposium on Security and Privacy (San Jose), DOI 10.1109/SP.2016.34, May 2016, https://ieeexplore.ieee.org/document/7546517/.
  • [FW15] Weimer, F., "Factoring RSA Keys With TLS Perfect Forward Secrecy", September 2015.
  • [HCJC16] Husak, M., Cermak, M., Jirsik, T., and P. Celeda, "HTTPS traffic analysis and client identification using passive SSL/TLS fingerprinting", EURASIP Journal on Information Security, Vol. 2016, DOI 10.1186/s13635-016-0030-7, February 2016.
  • [HGFS15] Hlauschek, C., Gruber, M., Fankhauser, F., and C. Schanes, "Prying Open Pandora's Box: KCI Attacks against TLS", Proceedings of USENIX Workshop on Offensive Technologies, August 2015.
  • [IEEE1363] IEEE, "IEEE Standard Specifications for Public Key Cryptography", IEEE Std. 1363-2000, DOI 10.1109/IEEESTD.2000.92292.
  • [JSS15] Jager, T., Schwenk, J., and J. Somorovsky, "On the Security of TLS 1.3 and QUIC Against Weaknesses in PKCS#1 v1.5 Encryption", Proceedings of ACM CCS 2015, DOI 10.1145/2810103.2813657, October 2015, https://www.nds.rub.de/media/nds/veroeffentlichungen/2015/08/21/Tls13QuicAttacks.pdf.
  • [KEYAGREEMENT] Barker, E., Chen, L., Roginsky, A., Vassilev, A., and R. Davis, "Recommendation for Pair-Wise Key Establishment Schemes Using Discrete Logarithm Cryptography", National Institute of Standards and Technology, DOI 10.6028/NIST.SP.800-56Ar3, April 2018.
  • [Kraw10] Krawczyk, H., "Cryptographic Extraction and Key Derivation: The HKDF Scheme", Proceedings of CRYPTO 2010, August 2010, https://eprint.iacr.org/2010/264.
  • [Kraw16] Krawczyk, H., "A Unilateral-to-Mutual Authentication Compiler for Key Exchange (with Applications to Client Authentication in TLS 1.3", Proceedings of ACM CCS 2016, October 2016, https://eprint.iacr.org/2016/711.
  • [KW16] Krawczyk, H. and H. Wee, "The OPTLS Protocol and TLS 1.3", Proceedings of EuroS&P 2016, March 2016, https://eprint.iacr.org/2015/978.
  • [LXZFH16] Li, X., Xu, J., Zhang, Z., Feng, D., and H. Hu, "Multiple Handshakes Security of TLS 1.3 Candidates", Proceedings of IEEE Symposium on Security and Privacy (San Jose), DOI 10.1109/SP.2016.36, May 2016, https://ieeexplore.ieee.org/document/7546519/.
  • [Mac17] MacCarthaigh, C., "Security Review of TLS1.3 0-RTT", March 2017, https://github.com/tlswg/tls13-spec/issues/1001.
  • [PS18] Patton, C. and T. Shrimpton, "Partially specified channels: The TLS 1.3 record layer without elision", 2018, https://eprint.iacr.org/2018/634.
  • [PSK-FINISHED] Scott, S., Cremers, C., Horvat, M., and T. van der Merwe, "Revision 10: possible attack if client authentication is allowed during PSK", message to the TLS mailing list, 31 October 2015, https://www.ietf.org/mail-archive/web/tls/current/msg18215.html.
  • [REKEY] Abdalla, M. and M. Bellare, "Increasing the Lifetime of a Key: A Comparative Analysis of the Security of Re-keying Techniques", ASIACRYPT 2000, DOI 10.1007/3-540-44448-3_42, October 2000.
  • [Res17a] Rescorla, E., "Preliminary data on Firefox TLS 1.3 Middlebox experiment", message to the TLS mailing list, 5 December 2017, https://www.ietf.org/mail-archive/web/tls/current/msg25091.html.
  • [Res17b] Rescorla, E., "More compatibility measurement results", message to the TLS mailing list, 22 December 2017, https://www.ietf.org/mail-archive/web/tls/current/msg25179.html.
  • [RFC3552] Rescorla, E. and B. Korver, "Guidelines for Writing RFC Text on Security Considerations", BCP 72, RFC 3552, DOI 10.17487/RFC3552, July 2003, https://www.rfc-editor.org/info/rfc3552.
  • [RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, https://www.rfc-editor.org/info/rfc4086.
  • [RFC4346] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.1", RFC 4346, DOI 10.17487/RFC4346, April 2006, https://www.rfc-editor.org/info/rfc4346.
  • [RFC4366] Blake-Wilson, S., Nystrom, M., Hopwood, D., Mikkelsen, J., and T. Wright, "Transport Layer Security (TLS) Extensions", RFC 4366, DOI 10.17487/RFC4366, April 2006, https://www.rfc-editor.org/info/rfc4366.
  • [RFC4492] Blake-Wilson, S., Bolyard, N., Gupta, V., Hawk, C., and B. Moeller, "Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)", RFC 4492, DOI 10.17487/RFC4492, May 2006, https://www.rfc-editor.org/info/rfc4492.
  • [RFC5077] Salowey, J., Zhou, H., Eronen, P., and H. Tschofenig, "Transport Layer Security (TLS) Session Resumption without Server-Side State", RFC 5077, DOI 10.17487/RFC5077, January 2008, https://www.rfc-editor.org/info/rfc5077.
  • [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer Security (TLS) Protocol Version 1.2", RFC 5246, DOI 10.17487/RFC5246, August 2008, https://www.rfc-editor.org/info/rfc5246.
  • [RFC5764] McGrew, D. and E. Rescorla, "Datagram Transport Layer Security (DTLS) Extension to Establish Keys for the Secure Real-time Transport Protocol (SRTP)", RFC 5764, DOI 10.17487/RFC5764, May 2010, https://www.rfc-editor.org/info/rfc5764.

Okay so the reference list keeps going, bestie ๐Ÿ“š

[RFC5929] Altman, J., Williams, N., and L. Zhu, "Channel Bindings for TLS", RFC 5929, DOI 10.17487/RFC5929, July 2010, <https://www.rfc-editor.org/info/rfc5929>.

[RFC6091] Mavrogiannopoulos, N. and D. Gillmor, "Using OpenPGP Keys for Transport Layer Security (TLS) Authentication", RFC 6091, DOI 10.17487/RFC6091, February 2011, <https://www.rfc-editor.org/info/rfc6091>.

[RFC6101] Freier, A., Karlton, P., and P. Kocher, "The Secure Sockets Layer (SSL) Protocol Version 3.0", RFC 6101, DOI 10.17487/RFC6101, August 2011, <https://www.rfc-editor.org/info/rfc6101>.

[RFC6176] Turner, S. and T. Polk, "Prohibiting Secure Sockets Layer (SSL) Version 2.0", RFC 6176, DOI 10.17487/RFC6176, March 2011, <https://www.rfc-editor.org/info/rfc6176>.

[RFC6347] Rescorla, E. and N. Modadugu, "Datagram Transport Layer Security Version 1.2", RFC 6347, DOI 10.17487/RFC6347, January 2012, <https://www.rfc-editor.org/info/rfc6347>.

[RFC6520] Seggelmann, R., Tuexen, M., and M. Williams, "Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) Heartbeat Extension", RFC 6520, DOI 10.17487/RFC6520, February 2012, <https://www.rfc-editor.org/info/rfc6520>.

[RFC7230] Fielding, R., Ed. and J. Reschke, Ed., "Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing", RFC 7230, DOI 10.17487/RFC7230, June 2014, <https://www.rfc-editor.org/info/rfc7230>.

[RFC7250] Wouters, P., Ed., Tschofenig, H., Ed., Gilmore, J., Weiler, S., and T. Kivinen, "Using Raw Public Keys in Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS)", RFC 7250, DOI 10.17487/RFC7250, June 2014, <https://www.rfc-editor.org/info/rfc7250>.

[RFC7465] Popov, A., "Prohibiting RC4 Cipher Suites", RFC 7465, DOI 10.17487/RFC7465, February 2015, <https://www.rfc-editor.org/info/rfc7465>.

[RFC7568] Barnes, R., Thomson, M., Pironti, A., and A. Langley, "Deprecating Secure Sockets Layer Version 3.0", RFC 7568, DOI 10.17487/RFC7568, June 2015, <https://www.rfc-editor.org/info/rfc7568>.

[RFC7627] Bhargavan, K., Ed., Delignat-Lavaud, A., Pironti, A., Langley, A., and M. Ray, "Transport Layer Security (TLS) Session Hash and Extended Master Secret Extension", RFC 7627, DOI 10.17487/RFC7627, September 2015, <https://www.rfc-editor.org/info/rfc7627>.

[RFC7685] Langley, A., "A Transport Layer Security (TLS) ClientHello Padding Extension", RFC 7685, DOI 10.17487/RFC7685, October 2015, <https://www.rfc-editor.org/info/rfc7685>.

[RFC7924] Santesson, S. and H. Tschofenig, "Transport Layer Security (TLS) Cached Information Extension", RFC 7924, DOI 10.17487/RFC7924, July 2016, <https://www.rfc-editor.org/info/rfc7924>.

[RFC8305] Schinazi, D. and T. Pauly, "Happy Eyeballs Version 2: Better Connectivity Using Concurrency", RFC 8305, DOI 10.17487/RFC8305, December 2017, <https://www.rfc-editor.org/info/rfc8305>.

[RFC8422] Nir, Y., Josefsson, S., and M. Pegourie-Gonnard, "Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier", RFC 8422, DOI 10.17487/RFC8422, August 2018, <https://www.rfc-editor.org/info/rfc8422>.

[RFC8447] Salowey, J. and S. Turner, "IANA Registry Updates for TLS and DTLS", RFC 8447, DOI 10.17487/RFC8447, August 2018, <https://www.rfc-editor.org/info/rfc8447>.

[RFC8449] Thomson, M., "Record Size Limit Extension for TLS", RFC 8449, DOI 10.17487/RFC8449, August 2018, <https://www.rfc-editor.org/info/rfc8449>.

[RSA] Rivest, R., Shamir, A., and L. Adleman, "A Method for Obtaining Digital Signatures and Public-Key Cryptosystems", Communications of the ACM, Vol. 21 No. 2, pp. 120-126, DOI 10.1145/359340.359342, February 1978.

[SIGMA] Krawczyk, H., "SIGMA: The 'SIGn-and-MAc' Approach to Authenticated Diffie-Hellman and its Use in the IKE Protocols", Proceedings of CRYPTO 2003, DOI 10.1007/978-3-540-45146-4_24, August 2003.

[SLOTH] Bhargavan, K. and G. Leurent, "Transcript Collision Attacks: Breaking Authentication in TLS, IKE, and SSH", Network and Distributed System Security Symposium (NDSS 2016), DOI 10.14722/ndss.2016.23418, February 2016.

[SSL2] Hickman, K., "The SSL Protocol", February 1995.

[TIMING] Boneh, D. and D. Brumley, "Remote Timing Attacks Are Practical", USENIX Security Symposium, August 2003.

[TLS13-TRACES] Thomson, M., "Example Handshake Traces for TLS 1.3", Work in Progress, draft-ietf-tls-tls13-vectors-06, July 2018.

[X501] ITU-T, "Information Technology - Open Systems Interconnection - The Directory: Models", ITU-T X.501, October 2016, <https://www.itu.int/rec/T-REC-X.501/en>.


Appendix A. State Machine โœจ

Appendix A is basically the state machine cheat sheet ๐Ÿง  It summarises the legal state transitions for the client and server handshakes. State names (all capitals, like START) have no formal meaning, they're just there to make things easier to follow ๐Ÿ’… Actions that only happen in certain circumstances are shown in []. And the notation K_{send,recv} = foo means "set the send/recv key to the given key" ๐Ÿ”‘

A.1. Client

                              START <----+
               Send ClientHello |        | Recv HelloRetryRequest
          [K_send = early data] |        |
                                v        |
           /                 WAIT_SH ----+
           |                    | Recv ServerHello
           |                    | K_recv = handshake
       Can |                    V
      send |                 WAIT_EE
     early |                    | Recv EncryptedExtensions
      data |           +--------+--------+
           |     Using |                 | Using certificate
           |       PSK |                 v
           |           |            WAIT_CERT_CR
           |           |        Recv |       | Recv CertificateRequest
           |           | Certificate |       v
           |           |             |    WAIT_CERT
           |           |             |       | Recv Certificate
           |           |             v       v
           |           |              WAIT_CV
           |           |                 | Recv CertificateVerify
           |           +> WAIT_FINISHED <+
           |                  | Recv Finished
           \                  | [Send EndOfEarlyData]
                              | K_send = handshake
                              | [Send Certificate [+ CertificateVerify]]
    Can send                  | Send Finished
    app data   -->            | K_send = K_recv = application
    after here                v
                          CONNECTED

Quick heads up on this one ๐Ÿ‘€ Clients can end up sending alerts that come from messages after the ServerHello either in the clear or with the early data keys, and that's just how these transitions shake out. If clients need to send those alerts, they SHOULD first rekey to the handshake keys if possible ๐Ÿ”

A.2. Server

                              START <-----+
               Recv ClientHello |         | Send HelloRetryRequest
                                v         |
                             RECVD_CH ----+
                                | Select parameters
                                v
                             NEGOTIATED
                                | Send ServerHello
                                | K_send = handshake
                                | Send EncryptedExtensions
                                | [Send CertificateRequest]
 Can send                       | [Send Certificate + CertificateVerify]
 app data                       | Send Finished
 after   -->                    | K_send = application
 here                  +--------+--------+
              No 0-RTT |                 | 0-RTT
                       |                 |
   K_recv = handshake  |                 | K_recv = early data
 [Skip decrypt errors] |    +------> WAIT_EOED -+
                       |    |       Recv |      | Recv EndOfEarlyData
                       |    | early data |      | K_recv = handshake
                       |    +------------+      |
                       |                        |
                       +> WAIT_FLIGHT2 <--------+
                                |
                       +--------+--------+
               No auth |                 | Client auth
                       |                 |
                       |                 v
                       |             WAIT_CERT
                       |        Recv |       | Recv Certificate
                       |       empty |       v
                       | Certificate |    WAIT_CV
                       |             |       | Recv
                       |             v       | CertificateVerify
                       +-> WAIT_FINISHED <---+
                                | Recv Finished
                                | K_recv = application
                                v
                            CONNECTED

Appendix B. Protocol Data Structures and Constant Values ๐Ÿ’œ

This appendix is the official dictionary ๐Ÿ“– It has the normative protocol types and the definitions for constants. Values listed as _RESERVED were used in previous versions of TLS and are only listed here so nothing is missing, bestie ๐Ÿซถ TLS 1.3 implementations MUST NOT send them but might receive them from older TLS implementations ๐Ÿ™ˆ

B.1. Record Layer

Okay, first up is the ContentType enum ๐ŸŽ€ Notice the heartbeat(24) entry, which is marked with /* RFC 6520 */:

      enum {
          invalid(0),
          change_cipher_spec(20),
          alert(21),
          handshake(22),
          application_data(23),
          heartbeat(24),  /* RFC 6520 */
          (255)
      } ContentType;

Then there's the TLSPlaintext struct, which is the plaintext record shape โœจ

      struct {
          ContentType type;
          ProtocolVersion legacy_record_version;
          uint16 length;
          opaque fragment[TLSPlaintext.length];
      } TLSPlaintext;

Next is TLSInnerPlaintext, which has the content, then the type, then the zeros for padding ๐ŸŒธ

      struct {
          opaque content[TLSPlaintext.length];
          ContentType type;
          uint8 zeros[length_of_padding];
      } TLSInnerPlaintext;

And last but definitely not least, TLSCiphertext. Its opaque_type is always application_data (23), and the legacy_record_version is pinned to 0x0303, which is TLS v1.2 ๐Ÿฆ‹

      struct {
          ContentType opaque_type = application_data; /* 23 */
          ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
          uint16 length;
          opaque encrypted_record[TLSCiphertext.length];
      } TLSCiphertext;

B.2. Alert Messages โœจ

enum { warning(1), fatal(2), (255) } AlertLevel;

enum {
    close_notify(0),
    unexpected_message(10),
    bad_record_mac(20),
    decryption_failed_RESERVED(21),
    record_overflow(22),
    decompression_failure_RESERVED(30),
    handshake_failure(40),
    no_certificate_RESERVED(41),
    bad_certificate(42),
    unsupported_certificate(43),
    certificate_revoked(44),
    certificate_expired(45),
    certificate_unknown(46),
    illegal_parameter(47),
    unknown_ca(48),
    access_denied(49),
    decode_error(50),
    decrypt_error(51),
    export_restriction_RESERVED(60),
    protocol_version(70),
    insufficient_security(71),
    internal_error(80),
    inappropriate_fallback(86),
    user_canceled(90),
    no_renegotiation_RESERVED(100),
    missing_extension(109),
    unsupported_extension(110),
    certificate_unobtainable_RESERVED(111),
    unrecognized_name(112),
    bad_certificate_status_response(113),
    bad_certificate_hash_value_RESERVED(114),
    unknown_psk_identity(115),
    certificate_required(116),
    no_application_protocol(120),
    (255)
} AlertDescription;

struct {
    AlertLevel level;
    AlertDescription description;
} Alert;

B.3. Handshake Protocol ๐Ÿค

enum {
    hello_request_RESERVED(0),
    client_hello(1),
    server_hello(2),
    hello_verify_request_RESERVED(3),
    new_session_ticket(4),
    end_of_early_data(5),
    hello_retry_request_RESERVED(6),
    encrypted_extensions(8),
    certificate(11),
    server_key_exchange_RESERVED(12),
    certificate_request(13),
    server_hello_done_RESERVED(14),
    certificate_verify(15),
    client_key_exchange_RESERVED(16),
    finished(20),
    certificate_url_RESERVED(21),
    certificate_status_RESERVED(22),
    supplemental_data_RESERVED(23),
    key_update(24),
    message_hash(254),
    (255)
} HandshakeType;

struct {
    HandshakeType msg_type;    /* handshake type */
    uint24 length;             /* bytes in message */
    select (Handshake.msg_type) {
        case client_hello:          ClientHello;
        case server_hello:          ServerHello;
        case end_of_early_data:     EndOfEarlyData;
        case encrypted_extensions:  EncryptedExtensions;
        case certificate_request:   CertificateRequest;
        case certificate:           Certificate;
        case certificate_verify:    CertificateVerify;
        case finished:              Finished;
        case new_session_ticket:    NewSessionTicket;
        case key_update:            KeyUpdate;
    };
} Handshake;

B.3.1. Key Exchange Messages ๐Ÿ”‘

uint16 ProtocolVersion;
opaque Random[32];

uint8 CipherSuite[2];    /* Cryptographic suite selector */

struct {
    ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
    Random random;
    opaque legacy_session_id<0..32>;
    CipherSuite cipher_suites<2..2^16-2>;
    opaque legacy_compression_methods<1..2^8-1>;
    Extension extensions<8..2^16-1>;
} ClientHello;

struct {
    ProtocolVersion legacy_version = 0x0303;    /* TLS v1.2 */
    Random random;
    opaque legacy_session_id_echo<0..32>;
    CipherSuite cipher_suite;
    uint8 legacy_compression_method = 0;
    Extension extensions<6..2^16-1>;
} ServerHello;
struct {
    ExtensionType extension_type;
    opaque extension_data<0..2^16-1>;
} Extension;

enum {
    server_name(0),                             /* RFC 6066 */
    max_fragment_length(1),                     /* RFC 6066 */
    status_request(5),                          /* RFC 6066 */
    supported_groups(10),                       /* RFC 8422, 7919 */
    signature_algorithms(13),                   /* RFC 8446 */
    use_srtp(14),                               /* RFC 5764 */
    heartbeat(15),                              /* RFC 6520 */
    application_layer_protocol_negotiation(16), /* RFC 7301 */
    signed_certificate_timestamp(18),           /* RFC 6962 */
    client_certificate_type(19),                /* RFC 7250 */
    server_certificate_type(20),                /* RFC 7250 */
    padding(21),                                /* RFC 7685 */
    RESERVED(40),                               /* Used but never
                                                   assigned */
    pre_shared_key(41),                         /* RFC 8446 */
    early_data(42),                             /* RFC 8446 */
    supported_versions(43),                     /* RFC 8446 */
    cookie(44),                                 /* RFC 8446 */
    psk_key_exchange_modes(45),                 /* RFC 8446 */
    RESERVED(46),                               /* Used but never
                                                   assigned */
    certificate_authorities(47),                /* RFC 8446 */
    oid_filters(48),                            /* RFC 8446 */
    post_handshake_auth(49),                    /* RFC 8446 */
    signature_algorithms_cert(50),              /* RFC 8446 */
    key_share(51),                              /* RFC 8446 */
    (65535)
} ExtensionType;

struct {
    NamedGroup group;
    opaque key_exchange<1..2^16-1>;
} KeyShareEntry;

struct {
    KeyShareEntry client_shares<0..2^16-1>;
} KeyShareClientHello;

struct {
    NamedGroup selected_group;
} KeyShareHelloRetryRequest;
struct {
    KeyShareEntry server_share;
} KeyShareServerHello;

struct {
    uint8 legacy_form = 4;
    opaque X[coordinate_length];
    opaque Y[coordinate_length];
} UncompressedPointRepresentation;

enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;

struct {
    PskKeyExchangeMode ke_modes<1..255>;
} PskKeyExchangeModes;

struct {} Empty;

struct {
    select (Handshake.msg_type) {
        case new_session_ticket:   uint32 max_early_data_size;
        case client_hello:         Empty;
        case encrypted_extensions: Empty;
    };
} EarlyDataIndication;

struct {
    opaque identity<1..2^16-1>;
    uint32 obfuscated_ticket_age;
} PskIdentity;

opaque PskBinderEntry<32..255>;

struct {
    PskIdentity identities<7..2^16-1>;
    PskBinderEntry binders<33..2^16-1>;
} OfferedPsks;

struct {
    select (Handshake.msg_type) {
        case client_hello: OfferedPsks;
        case server_hello: uint16 selected_identity;
    };
} PreSharedKeyExtension;

B.3.1.1. Version Extension ๐ŸŽ€

struct {
    select (Handshake.msg_type) {
        case client_hello:
             ProtocolVersion versions<2..254>;

        case server_hello: /* and HelloRetryRequest */
             ProtocolVersion selected_version;
    };
} SupportedVersions;
struct {
    opaque cookie<1..2^16-1>;
} Cookie;

B.3.1.3. Signature Algorithm Extension โœ๏ธ

enum {
    /* RSASSA-PKCS1-v1_5 algorithms */
    rsa_pkcs1_sha256(0x0401),
    rsa_pkcs1_sha384(0x0501),
    rsa_pkcs1_sha512(0x0601),

    /* ECDSA algorithms */
    ecdsa_secp256r1_sha256(0x0403),
    ecdsa_secp384r1_sha384(0x0503),
    ecdsa_secp521r1_sha512(0x0603),

    /* RSASSA-PSS algorithms with public key OID rsaEncryption */
    rsa_pss_rsae_sha256(0x0804),
    rsa_pss_rsae_sha384(0x0805),
    rsa_pss_rsae_sha512(0x0806),

    /* EdDSA algorithms */
    ed25519(0x0807),
    ed448(0x0808),

    /* RSASSA-PSS algorithms with public key OID RSASSA-PSS */
    rsa_pss_pss_sha256(0x0809),
    rsa_pss_pss_sha384(0x080a),
    rsa_pss_pss_sha512(0x080b),

    /* Legacy algorithms */
    rsa_pkcs1_sha1(0x0201),
    ecdsa_sha1(0x0203),

    /* Reserved Code Points */
    obsolete_RESERVED(0x0000..0x0200),
    dsa_sha1_RESERVED(0x0202),
    obsolete_RESERVED(0x0204..0x0400),
    dsa_sha256_RESERVED(0x0402),
    obsolete_RESERVED(0x0404..0x0500),
    dsa_sha384_RESERVED(0x0502),
    obsolete_RESERVED(0x0504..0x0600),
    dsa_sha512_RESERVED(0x0602),
    obsolete_RESERVED(0x0604..0x06FF),
    private_use(0xFE00..0xFFFF),
    (0xFFFF)
} SignatureScheme;

struct {
    SignatureScheme supported_signature_algorithms<2..2^16-2>;
} SignatureSchemeList;

B.3.1.4. Supported Groups Extension ๐ŸŒˆ

enum {
    unallocated_RESERVED(0x0000),

    /* Elliptic Curve Groups (ECDHE) */
    obsolete_RESERVED(0x0001..0x0016),
    secp256r1(0x0017), secp384r1(0x0018), secp521r1(0x0019),
    obsolete_RESERVED(0x001A..0x001C),
    x25519(0x001D), x448(0x001E),

    /* Finite Field Groups (DHE) */
    ffdhe2048(0x0100), ffdhe3072(0x0101), ffdhe4096(0x0102),
    ffdhe6144(0x0103), ffdhe8192(0x0104),

    /* Reserved Code Points */
    ffdhe_private_use(0x01FC..0x01FF),
    ecdhe_private_use(0xFE00..0xFEFF),
    obsolete_RESERVED(0xFF01..0xFF02),
    (0xFFFF)
} NamedGroup;

struct {
    NamedGroup named_group_list<2..2^16-1>;
} NamedGroupList;

Okay bestie, heads up about the old curves ๐Ÿซฃ Values inside the "obsolete_RESERVED" ranges come from previous versions of TLS, and TLS 1.3 implementations MUST NOT offer or negotiate them ๐Ÿšซ. The obsolete curves have various known or theoretical weaknesses, or they barely got any usage at all (sometimes only because a server was accidentally misconfigured) ๐Ÿ’€. They aren't considered appropriate for general use anymore, so you should assume they're potentially unsafe ๐Ÿ™ˆ. The curves listed here are enough to interoperate with every currently deployed and properly configured TLS implementation โœจ.

B.3.2. Server Parameters Messages ๐Ÿ‘‘

opaque DistinguishedName<1..2^16-1>;

struct {
    DistinguishedName authorities<3..2^16-1>;
} CertificateAuthoritiesExtension;

struct {
    opaque certificate_extension_oid<1..2^8-1>;
    opaque certificate_extension_values<0..2^16-1>;
} OIDFilter;

struct {
    OIDFilter filters<0..2^16-1>;
} OIDFilterExtension;

struct {} PostHandshakeAuth;

struct {
    Extension extensions<0..2^16-1>;
} EncryptedExtensions;

struct {
    opaque certificate_request_context<0..2^8-1>;
    Extension extensions<2..2^16-1>;
} CertificateRequest;

B.3.3. Authentication Messages ๐Ÿฆ‹

enum {
    X509(0),
    OpenPGP_RESERVED(1),
    RawPublicKey(2),
    (255)
} CertificateType;

struct {
    select (certificate_type) {
        case RawPublicKey:
          /* From RFC 7250 ASN.1_subjectPublicKeyInfo */
          opaque ASN1_subjectPublicKeyInfo<1..2^24-1>;

        case X509:
          opaque cert_data<1..2^24-1>;
    };
    Extension extensions<0..2^16-1>;
} CertificateEntry;

struct {
    opaque certificate_request_context<0..2^8-1>;
    CertificateEntry certificate_list<0..2^24-1>;
} Certificate;

struct {
    SignatureScheme algorithm;
    opaque signature<0..2^16-1>;
} CertificateVerify;

struct {
    opaque verify_data[Hash.length];
} Finished;

B.3.4. Ticket Establishment ๐ŸŽ€

okay so here's the NewSessionTicket struct, bestie ๐Ÿ’…

struct {
    uint32 ticket_lifetime;
    uint32 ticket_age_add;
    opaque ticket_nonce<0..255>;
    opaque ticket<1..2^16-1>;
    Extension extensions<0..2^16-2>;
} NewSessionTicket;

B.3.5. Updating Keys ๐Ÿฆ‹

the EndOfEarlyData struct is literally empty ๐Ÿ’€ it's just struct {} EndOfEarlyData; no fields, no cap, pure vibes

then we get the KeyUpdateRequest enum, which has two real values: update_not_requested(0) and update_requested(1), plus a reserved (255) slot ๐Ÿซถ

struct {} EndOfEarlyData;

enum {
    update_not_requested(0), update_requested(1), (255)
} KeyUpdateRequest;

struct {
    KeyUpdateRequest request_update;
} KeyUpdate;

B.4. Cipher Suites ๐ŸŒˆ

okay so a symmetric cipher suite is basically a pairing of an AEAD algorithm and a hash algorithm, the hash being the one used with HKDF โœจ the names follow a super predictable pattern, so once you get it you're set:

CipherSuite TLS_AEAD_HASH = VALUE;
+-----------+------------------------------------------------+
| Component | Contents                                       |
+-----------+------------------------------------------------+
| TLS       | The string "TLS"                               |
|           |                                                |
| AEAD      | The AEAD algorithm used for record protection  |
|           |                                                |
| HASH      | The hash algorithm used with HKDF              |
|           |                                                |
| VALUE     | The two-byte ID assigned for this cipher suite |
+-----------+------------------------------------------------+

this spec defines the following cipher suites for TLS 1.3, and honestly the list is giving main character energy ๐Ÿ‘‘

+------------------------------+-------------+
| Description                  | Value       |
+------------------------------+-------------+
| TLS_AES_128_GCM_SHA256       | {0x13,0x01} |
|                              |             |
| TLS_AES_256_GCM_SHA384       | {0x13,0x02} |
|                              |             |
| TLS_CHACHA20_POLY1305_SHA256 | {0x13,0x03} |
|                              |             |
| TLS_AES_128_CCM_SHA256       | {0x13,0x04} |
|                              |             |
| TLS_AES_128_CCM_8_SHA256     | {0x13,0x05} |
+------------------------------+-------------+

the AEAD algorithms AEAD_AES_128_GCM, AEAD_AES_256_GCM, and AEAD_AES_128_CCM are defined in [RFC5116]. AEAD_CHACHA20_POLY1305 is defined in [RFC8439]. AEAD_AES_128_CCM_8 is defined in [RFC6655]. the hash algorithms come from [SHS] ๐Ÿ“

โš ๏ธ tea: TLS 1.3 shares the same cipher suite space as older TLS versions, BUT its suites are defined differently. they only specify the symmetric ciphers, so they can't be used for TLS 1.2. and the other way around: TLS 1.2 and lower suites can't be used with TLS 1.3 ๐Ÿ™…โ€โ™€๏ธ

new cipher suite values get assigned by IANA, as described in Section 11 ๐Ÿงš

Appendix C. Implementation Notes โœจ

okay so TLS literally can't stop every common security mistake on its own ๐Ÿ’€ this appendix is a bunch of tips to help implementors not mess up. [TLS13-TRACES] has test vectors for TLS 1.3 handshakes, so go look ๐Ÿ‘€

C.1. Random Number Generation and Seeding ๐ŸŒธ

TLS needs a cryptographically secure pseudorandom number generator (CSPRNG) ๐Ÿ” usually your OS has one already, like /dev/urandom, and you should use it unless performance or something else is a real concern. it is RECOMMENDED to use an existing CSPRNG implementation instead of crafting a new one, because lots of solid crypto libraries already exist under favorable license terms. if those don't work out for you, [RFC4086] has guidance on generating random values.

TLS uses random values in two ways: in public protocol fields, like the public Random values in the ClientHello and ServerHello, and to generate keying material. with a properly working CSPRNG this is fine, since you can't feasibly work out the CSPRNG state from its output. but with a broken CSPRNG, an attacker might use the public output to figure out the internal state and predict the keying material, as documented in [CHECKOWAY] ๐Ÿ˜ญ extra protection against this kind of attack is to use separate CSPRNGs for public and private values.

C.2. Certificates and Authentication ๐Ÿ”ฅ

implementations are responsible for verifying certificate integrity, and they should generally support certificate revocation messages. unless an application profile says otherwise, certificates should always be verified to make sure a trusted certificate authority (CA) actually signed them. pick your trust anchors very carefully, and users should be able to see info about the certificate and trust anchor ๐Ÿ‘€ applications SHOULD also enforce minimum and maximum key sizes. for example, certification paths with keys or signatures weaker than 2048-bit RSA or 224-bit ECDSA are not appropriate for secure applications ๐Ÿšซ

C.3. Implementation Pitfalls ๐Ÿ’€

ngl, implementation experience has shown that some parts of earlier TLS specs are confusing, and they've caused interoperability and security problems. a lot of that has been cleared up in this document, but this appendix has a short list of the biggest things implementors need to watch out for ๐Ÿซถ

TLS protocol issues:

  • Do you correctly handle handshake messages that are fragmented across multiple TLS records (see Section 5.1)? Do you correctly handle corner cases like a ClientHello that is split into several small fragments? Do you fragment handshake messages that exceed the maximum fragment size? In particular, the Certificate and CertificateRequest handshake messages can be large enough to require fragmentation.
  • Do you ignore the TLS record layer version number in all unencrypted TLS records (see Appendix D)?
  • Have you made sure all support for SSL, RC4, EXPORT ciphers, and MD5 (via the signature_algorithms extension) is completely removed from every configuration that supports TLS 1.3 or later, and that attempts to use these obsolete capabilities fail correctly (see Appendix D)?
  • Do you handle TLS extensions in ClientHellos correctly, including unknown extensions?
  • When the server has requested a client certificate but no suitable certificate is available, do you correctly send an empty Certificate message instead of omitting the whole message (see Section 4.4.2)?
  • When processing the plaintext fragment produced by AEAD-Decrypt and scanning from the end for the ContentType, do you avoid scanning past the start of the cleartext in case the peer has sent a malformed plaintext of all zeros?
  • Do you properly ignore unrecognized cipher suites (Section 4.1.2), hello extensions (Section 4.2), named groups (Section 4.2.7), key shares (Section 4.2.8), supported versions (Section 4.2.1), and signature algorithms (Section 4.2.3) in the ClientHello?
  • As a server, do you send a HelloRetryRequest to clients that support a compatible (EC)DHE group but don't predict it in the key_share extension? As a client, do you correctly handle a HelloRetryRequest from the server?

Cryptographic details:

  • What countermeasures do you use to prevent timing attacks [TIMING]?
  • When using Diffie-Hellman key exchange, do you correctly preserve leading zero bytes in the negotiated key (see Section 7.4.1)?
  • Does your TLS client check that the Diffie-Hellman parameters sent by the server are acceptable (see Section 4.2.8.1)?
  • Do you use a strong and, most importantly, properly seeded random number generator (see Appendix C.1) when generating Diffie-Hellman private values, the ECDSA "k" parameter, and other security-critical values? It is RECOMMENDED that implementations implement "deterministic ECDSA" as specified in [RFC6979].
  • Do you zero-pad Diffie-Hellman public key values and shared secrets to the group size (see Section 4.2.8.1 and Section 7.4.1)?
  • Do you verify signatures after making them, to protect against RSA-CRT key leaks [FW15]?

C.4. Client Tracking Prevention ๐Ÿ•ต๏ธโ€โ™€๏ธ

big rule: clients SHOULD NOT reuse a ticket for multiple connections, because reusing one lets passive observers link different connections together ๐Ÿ‘€ servers that issue tickets SHOULD offer at least as many tickets as the number of connections a client might use. for example, a web browser using HTTP/1.1 [RFC7230] might open six connections to a server. servers SHOULD issue new tickets with every connection, so clients can always grab a fresh ticket when they make a new connection โœจ

C.5. Unauthenticated Operation ๐Ÿ™ˆ

previous TLS versions offered explicitly unauthenticated cipher suites based on anonymous Diffie-Hellman, but those modes are deprecated in TLS 1.3 ๐Ÿชฆ it's still possible to negotiate parameters without verifiable server authentication, though, a few ways:

  • Raw public keys [RFC7250].
  • Using a public key from a certificate, but without validating the certificate chain or any of its contents.

either one used alone is vulnerable to man-in-the-middle attacks, so it's unsafe for general use ๐Ÿ˜ฑ you can bind these connections to an external authentication mechanism, like out-of-band validation of the server's public key, trust on first use, or a channel binding mechanism (though the channel bindings described in [RFC5929] aren't defined for TLS 1.3). if none of that is used, the connection has no protection against active man-in-the-middle attacks, and applications MUST NOT use TLS in such a way absent explicit configuration or a specific application profile. ๐Ÿšจ

Appendix D. Backward Compatibility โœจ

okay bestie, this is the glow-up section ๐Ÿฆ‹ TLS has a built-in way for two endpoints to agree on a version, even when they're running different ones. Like, it's giving diplomacy ๐Ÿ’…

TLS 1.x and SSL 3.0 use compatible ClientHello messages. Servers can also handle clients trying to use future versions of TLS ๐Ÿ’– as long as the ClientHello format stays compatible AND there's at least one protocol version that both the client and the server support ๐Ÿซถ

Older versions of TLS used the record layer version number (TLSPlaintext.legacy_record_version and TLSCiphertext.legacy_record_version) for various things ๐ŸŒธ but as of TLS 1.3, that field is deprecated ๐Ÿ˜ญ The value of TLSPlaintext.legacy_record_version MUST be ignored by all implementations ๐Ÿ™ˆ The value of TLSCiphertext.legacy_record_version gets included in the additional data for deprotection, but it MAY otherwise be ignored, OR it MAY be validated to match the fixed constant value โœจ Version negotiation is done using only the handshake versions (ClientHello.legacy_version and ServerHello.legacy_version, plus the "supported_versions" extensions on the ClientHello, HelloRetryRequest, and ServerHello) ๐ŸŽ€ For max interoperability with older endpoints, implementations that negotiate TLS 1.0-1.2 SHOULD set the record layer version number to the negotiated version for the ServerHello and all records after that ๐Ÿงš

For maximum compatibility with previously non-standard behavior and misconfigured deployments ๐Ÿ™ˆ, all implementations SHOULD support validation of certification paths based on the expectations in this document, even when handling prior TLS versions' handshakes (see Section 4.4.2.2) ๐Ÿ”ฅ

TLS 1.2 and earlier supported an "Extended Master Secret" [RFC7627] extension ๐Ÿ’œ which digested big chunks of the handshake transcript into the master secret. Because TLS 1.3 always hashes in the transcript up to the server Finished, implementations that support both TLS 1.3 and earlier versions SHOULD indicate the use of the Extended Master Secret extension in their APIs whenever TLS 1.3 is used ๐Ÿ‘‘

D.1. Negotiating with an Older Server ๐ŸŒˆ

A TLS 1.3 client that wants to negotiate with servers that don't support TLS 1.3 will send a normal TLS 1.3 ClientHello containing 0x0303 (TLS 1.2) in ClientHello.legacy_version, but with the correct version(s) in the "supported_versions" extension ๐Ÿฆ‹ If the server doesn't support TLS 1.3, it'll respond with a ServerHello containing an older version number ๐Ÿ˜ญ If the client agrees to use that version, the negotiation proceeds as appropriate for the negotiated protocol โœจ A client using a ticket for resumption SHOULD initiate the connection using the version that was previously negotiated ๐Ÿซถ

Note that 0-RTT data is not compatible with older servers and SHOULD NOT be sent unless the client knows the server supports TLS 1.3 ๐Ÿ™ˆ See Appendix D.3 ๐Ÿ’…

If the version the server chose isn't supported by the client (or isn't acceptable), the client MUST abort the handshake with a "protocol_version" alert ๐Ÿ’€

Some legacy server implementations are known to not implement TLS properly and might abort connections when they run into TLS extensions or versions they don't recognize ๐Ÿ˜ญ Interoperating with buggy servers is a whole complex topic that's beyond the scope of this document ๐Ÿ“ Multiple connection attempts may be needed to negotiate a backward-compatible connection, but this practice is vulnerable to downgrade attacks, so it is NOT RECOMMENDED ๐Ÿšซ

D.2. Negotiating with an Older Client ๐Ÿงš

A TLS server can also receive a ClientHello indicating a version number smaller than its highest supported version โœจ If the "supported_versions" extension is present, the server MUST negotiate using that extension, as described in Section 4.2.1 ๐Ÿ‘‘ If the "supported_versions" extension is not present, the server MUST negotiate the minimum of ClientHello.legacy_version and TLS 1.2 ๐ŸŒธ For example, if the server supports TLS 1.0, 1.1, and 1.2, and legacy_version is TLS 1.0, the server will proceed with a TLS 1.0 ServerHello ๐ŸŽ€ If the "supported_versions" extension is absent and the server only supports versions greater than ClientHello.legacy_version, the server MUST abort the handshake with a "protocol_version" alert ๐Ÿ’€

Note that earlier versions of TLS didn't clearly specify the record layer version number value in every case (TLSPlaintext.legacy_record_version) ๐Ÿ™ˆ Servers will receive various TLS 1.x versions in this field, but its value MUST always be ignored ๐Ÿ’–

D.3. 0-RTT Backward Compatibility ๐Ÿฆ‹

0-RTT data is not compatible with older servers ๐Ÿ˜ญ An older server will respond to the ClientHello with an older ServerHello, but it won't correctly skip the 0-RTT data and will fail to finish the handshake ๐Ÿ’€ This can cause issues when a client tries to use 0-RTT, especially against multi-server deployments ๐Ÿซ  For example, a deployment could roll out TLS 1.3 gradually, with some servers on TLS 1.3 and some still on TLS 1.2, or a TLS 1.3 deployment could get downgraded to TLS 1.2 ๐Ÿ“

A client that attempts to send 0-RTT data MUST fail the connection if it receives a ServerHello with TLS 1.2 or older ๐Ÿšจ It can then retry the connection with 0-RTT disabled ๐ŸŒธ To avoid a downgrade attack, the client SHOULD NOT disable TLS 1.3, only 0-RTT ๐Ÿ›ก๏ธ

To avoid this error condition, multi-server deployments SHOULD ensure a uniform and stable deployment of TLS 1.3 without 0-RTT before enabling 0-RTT ๐Ÿซถ

D.4. Middlebox Compatibility Mode ๐ŸŽ€

Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found that a significant number of middleboxes misbehave when a TLS client/server pair negotiates TLS 1.3 ๐Ÿ˜ญ Implementations can improve their chances of getting connections through those middleboxes by making the TLS 1.3 handshake look more like a TLS 1.2 handshake ๐Ÿงš

  • The client always provides a non-empty session ID in the ClientHello, as described in the legacy_session_id section of Section 4.1.2 ๐Ÿ’–
  • If not offering early data, the client sends a dummy change_cipher_spec record (see the third paragraph of Section 5) immediately before its second flight ๐Ÿฆ‹ This may either be before its second ClientHello or before its encrypted handshake flight ๐ŸŒธ If offering early data, the record is placed immediately after the first ClientHello โœจ
  • The server sends a dummy change_cipher_spec record immediately after its first handshake message ๐Ÿ‘‘ This may either be after a ServerHello or a HelloRetryRequest ๐Ÿซถ

When you put these together, the TLS 1.3 handshake ends up resembling TLS 1.2 session resumption, which improves the chance of connecting successfully through middleboxes ๐ŸŒˆ This "compatibility mode" is partially negotiated: the client can choose whether to provide a session ID, and the server has to echo it ๐Ÿ’… Either side can send change_cipher_spec at any time during the handshake, since it must be ignored by the peer ๐Ÿ™ˆ But if the client sends a non-empty session ID, the server MUST send the change_cipher_spec as described in this appendix ๐Ÿšจ

Implementations negotiating the use of older versions of TLS SHOULD prefer forward secret and AEAD cipher suites, when they're available ๐Ÿ›ก๏ธ

The security of RC4 cipher suites is considered insufficient for the reasons cited in [RFC7465] ๐Ÿ’€ Implementations MUST NOT offer or negotiate RC4 cipher suites for any version of TLS, for any reason ๐Ÿšซ

Old versions of TLS allowed very low strength ciphers ๐Ÿ˜ญ Ciphers with a strength less than 112 bits MUST NOT be offered or negotiated for any version of TLS, for any reason ๐Ÿšซ

The security of SSL 3.0 [RFC6101] is considered insufficient for the reasons enumerated in [RFC7568] ๐Ÿ’€ and it MUST NOT be negotiated for any reason ๐Ÿšซ

The security of SSL 2.0 [SSL2] is considered insufficient for the reasons enumerated in [RFC6176] ๐Ÿ’€ and it MUST NOT be negotiated for any reason ๐Ÿšซ

Implementations MUST NOT send an SSL version 2.0 compatible CLIENT-HELLO ๐Ÿ™… Implementations MUST NOT negotiate TLS 1.3 or later using an SSL version 2.0 compatible CLIENT-HELLO ๐Ÿ™… Implementations are NOT RECOMMENDED to accept an SSL version 2.0 compatible CLIENT-HELLO in order to negotiate older versions of TLS ๐Ÿšซ

Implementations MUST NOT send a ClientHello.legacy_version or ServerHello.legacy_version set to 0x0300 or less ๐Ÿ™… Any endpoint receiving a Hello message with ClientHello.legacy_version or ServerHello.legacy_version set to 0x0300 MUST abort the handshake with a "protocol_version" alert ๐Ÿ’€

Implementations MUST NOT send any records with a version less than 0x0300 ๐Ÿ™… Implementations SHOULD NOT accept any records with a version less than 0x0300 (but they may accidentally do so if the record version number is ignored completely) ๐Ÿ™ˆ

Implementations MUST NOT use the Truncated HMAC extension, defined in Section 7 of [RFC6066] ๐Ÿšซ as it doesn't apply to AEAD algorithms and has been shown to be insecure in some scenarios ๐Ÿ’€

Appendix E. Overview of Security Properties ๐Ÿ”ฎ

A complete security analysis of TLS is outside the scope of this document ๐Ÿซถ In this appendix, we give an informal description of the properties we want, plus references to more detailed work in the research literature that gives more formal definitions ๐Ÿ“š

We cover properties of the handshake separately from those of the record layer ๐Ÿ’–

E.1. Handshake ๐Ÿ’–

okay so the TLS handshake is an Authenticated Key Exchange (AKE) protocol, and it's meant to give you both one-way authenticated (server-only) AND mutually authenticated (client and server) functionality ๐Ÿซถ at the end of it, each side outputs its own view of:

  • A set of "session keys" (all the secrets derived from the master secret), from which a set of working keys can be derived โœจ
  • A set of cryptographic parameters (algorithms, etc.) ๐ŸŽ€
  • The identities of the communicating parties ๐Ÿฆ‹

we assume the attacker is an active network attacker, which means they have complete control over the network between the parties [RFC3552] ๐Ÿ˜ญ even in that scary situation, the handshake should still give you the properties below. these aren't necessarily independent, they just reflect what protocol consumers actually need ๐Ÿ’…

Establishing the same session keys: both sides need to output the same set of session keys, as long as the handshake completes successfully on each endpoint (see [CK01], Definition 1, part 1) ๐ŸŒธ

Secrecy of the session keys: the shared session keys should be known only to the communicating parties and NOT the attacker (see [CK01], Definition 1, part 2) ๐Ÿ”ฅ note that in a unilaterally authenticated connection, the attacker can set up its own session keys with the server, but those are distinct from the ones the client gets ๐Ÿ™ˆ

Peer authentication: the client's view of the peer identity should reflect the server's identity. if the client is authenticated, the server's view of the peer identity should match the client's identity ๐Ÿ“

Uniqueness of the session keys: any two distinct handshakes should produce distinct, unrelated session keys. individual session keys from one handshake should also be distinct and independent ๐Ÿงš

Downgrade protection: the cryptographic parameters should be the same on both sides, and the same as if the peers had been talking with no attack happening (see [BBFGKZ16], Definitions 8 and 9) ๐Ÿ‘‘

Forward secret with respect to long-term keys: if the long-term keying material (the signature keys in certificate-based modes, or the external/resumption PSK in PSK with (EC)DHE modes) gets compromised after the handshake is done, that does NOT compromise the session key (see [DOW92]), as long as the session key itself has been erased ๐Ÿ’œ the forward secrecy property is NOT satisfied when PSK is used in the "psk_ke" PskKeyExchangeMode ๐Ÿ’€

Key Compromise Impersonation (KCI) resistance: in a mutually authenticated connection with certificates, compromising one actor's long-term secret should NOT break that actor's authentication of their peer in the given connection (see [HGFS15]). for example, if a client's signature key leaks, nobody should be able to impersonate arbitrary servers to that client in later handshakes ๐ŸŒˆ

Protection of endpoint identities: the server's identity (certificate) should be protected against passive attackers. the client's identity should be protected against both passive AND active attackers ๐Ÿฆ‹

informally, the signature-based modes of TLS 1.3 give you a unique, secret, shared key from an (EC)DHE key exchange. it's authenticated by the server's signature over the handshake transcript, and tied to the server's identity by a MAC โœจ if the client is authenticated by a certificate, it also signs over the transcript and provides a MAC tied to both identities ๐Ÿซถ [SIGMA] describes the design and analysis of this kind of key exchange protocol. and if fresh (EC)DHE keys are used for each connection, the output keys are forward secret ๐Ÿ”ฅ

the external PSK and resumption PSK bootstrap from a long-term shared secret into a unique per-connection set of short-term session keys ๐ŸŒธ that secret may have been set up in a previous handshake. if PSK with (EC)DHE key establishment is used, these session keys are forward secret too ๐Ÿ‘‘ the resumption PSK is designed so that the resumption master secret computed by connection N (the one needed to form connection N+1) is separate from the traffic keys used by connection N, which gives you forward secrecy between connections ๐ŸŽ€ also, if multiple tickets are established on the same connection, they're tied to different keys, so compromising the PSK for one ticket doesn't compromise connections made with the PSKs for other tickets ๐Ÿงš this property matters most if tickets are stored in a database (so they can be deleted), rather than self-encrypted ๐Ÿ’…

the PSK binder value forms a binding between a PSK and the current handshake, and also between the session where the PSK was established and the current session ๐Ÿ’– this binding transitively includes the original handshake transcript, because that transcript gets digested into the values that produce the resumption master secret ๐ŸŒˆ this needs both the KDF used for the resumption master secret and the MAC used to compute the binder to be collision resistant ๐Ÿค“ see Appendix E.1.1 for more on this. note: the binder does NOT cover the binder values from other PSKs, though they ARE included in the Finished MAC ๐Ÿ™ˆ

TLS does not currently let the server send a certificate_request message in non-certificate-based handshakes (e.g., PSK) ๐Ÿ˜ญ if that restriction were ever relaxed, the client's signature would NOT cover the server's certificate directly ๐Ÿ’€ however, if the PSK was established through a NewSessionTicket, the client's signature would transitively cover the server's certificate through the PSK binder ๐Ÿซถ [PSK-FINISHED] describes a concrete attack on constructions that don't bind to the server's certificate (see also [Kraw16]) ๐Ÿ”ฅ it is unsafe to use certificate-based client authentication when the client might share the same PSK/key-id pair with two different endpoints ๐Ÿšจ implementations MUST NOT combine external PSKs with certificate-based authentication of either the client or the server, unless some extension negotiates it ๐Ÿšจ

if an exporter is used, it produces values that are unique and secret (because they're generated from a unique session key) โœจ exporters computed with different labels and contexts are computationally independent, so it's NOT feasible to compute one from another, or the session secret from the exported value ๐Ÿฆ‹ note: exporters can produce arbitrary-length values. if exporters are going to be used as channel bindings, the exported value MUST be large enough to provide collision resistance ๐ŸŒธ the exporters in TLS 1.3 are derived from the same Handshake Contexts as the early traffic keys and the application traffic keys, respectively, so they have similar security properties ๐Ÿ’œ note that they do NOT include the client's certificate. future applications that want to bind to the client's certificate may need to define a new exporter that includes the full handshake transcript ๐Ÿ“

for all handshake modes, the Finished MAC (and, where present, the signature) prevents downgrade attacks ๐Ÿ›ก๏ธ on top of that, the use of certain bytes in the random nonces described in Section 4.1.3 lets you detect a downgrade to previous TLS versions ๐ŸŒˆ see [BBFGKZ16] for more on TLS 1.3 and downgrade ๐ŸŽ€

as soon as the client and server have swapped enough info to establish shared keys, the rest of the handshake is encrypted, which protects against passive attackers even if the computed shared key isn't authenticated ๐Ÿซง because the server authenticates BEFORE the client, the client can make sure that if it authenticates to the server, it only reveals its identity to an authenticated server ๐Ÿฅน note that implementations MUST use the provided record-padding mechanism during the handshake to avoid leaking identity info through length ๐Ÿšจ the client's proposed PSK identities are NOT encrypted, and neither is the one the server selects ๐Ÿ‘€

E.1.1. Key Derivation and HKDF

key derivation in TLS 1.3 uses HKDF as defined in [RFC5869] and its two components, HKDF-Extract and HKDF-Expand ๐Ÿง the full rationale for the HKDF construction is in [Kraw10], and the rationale for how TLS 1.3 uses it is in [KW16] ๐Ÿ“š throughout this document, every application of HKDF-Extract is followed by one or more invocations of HKDF-Expand. this ordering should ALWAYS be followed (including in future revisions of this document) ๐Ÿ”’ in particular, one SHOULD NOT use an output of HKDF-Extract as an input to another application of HKDF-Extract without an HKDF-Expand in between ๐Ÿ™…โ€โ™€๏ธ multiple applications of HKDF-Expand to some of the same inputs are fine, as long as they're differentiated via the key and/or the labels โœจ

note that HKDF-Expand implements a pseudorandom function (PRF) with both inputs and outputs of variable length ๐ŸŒˆ in some uses of HKDF in this document (e.g., generating exporters and the resumption_master_secret), the HKDF-Expand application needs to be collision resistant. that means it should be infeasible to find two different inputs to HKDF-Expand that output the same value ๐Ÿ’… this requires the underlying hash function to be collision resistant, and the output length from HKDF-Expand to be at least 256 bits (or as much as needed for the hash function to prevent finding collisions) ๐Ÿซถ

E.1.2. Client Authentication

a client that has sent authentication data to a server, either during the handshake or in post-handshake authentication, can't be sure whether the server afterwards considers it authenticated or not ๐Ÿฅบ if the client needs to figure out whether the server sees the connection as unilaterally or mutually authenticated, the application layer has to provide that ๐ŸŽ€ see [CHHSV17] for details. also, the analysis of post-handshake authentication from [Kraw16] shows that the client identified by the certificate sent in the post-handshake phase possesses the traffic key ๐Ÿ”‘ so that party is either the client that took part in the original handshake, or someone the original client delegated the traffic key to (assuming the traffic key hasn't been compromised) ๐Ÿ‘‘

E.1.3. 0-RTT

the 0-RTT mode generally gives you security properties similar to 1-RTT data, with two exceptions ๐Ÿซ  first, the 0-RTT encryption keys do NOT provide full forward secrecy. second, the server can't guarantee uniqueness of the handshake (non-replayability) without keeping potentially undue amounts of state ๐Ÿ™ƒ see Section 8 for mechanisms to limit the exposure to replay ๐Ÿ›ก๏ธโœจ

(Continuing from E.1.3 without repeating the heading.)

Right, let's keep the vibe going, bestie ๐Ÿ’…

E.1.4. Exporter Independence โœจ

Okay so the exporter_master_secret and early_exporter_master_secret are derived to be independent of the traffic keys ๐Ÿฆ‹, which means they are NOT a threat to the security of traffic encrypted with those keys ๐Ÿ”ฅ. Iconic, right? But here's the tea: because these secrets can compute any exporter value, they SHOULD be erased as soon as possible ๐Ÿซถ. If you already know the full set of exporter labels, then implementations SHOULD pre-compute the inner Derive-Secret stage for all of those labels ๐Ÿงš, then erase the [early_]exporter_master_secret, followed by each inner value as soon as it is clear it won't be needed again ๐Ÿ’€. Main character cleanup energy, periodt ๐Ÿงนโœจ.

E.1.5. Post-Compromise Security ๐ŸŒธ

TLS does not provide security for handshakes that happen after the peer's long-term secret (signature key or external PSK) is compromised ๐Ÿ˜ญ. So it does not provide post-compromise security [CCG16], which people also call backward or future secrecy ๐ŸŒˆ. KCI resistance is kind of the opposite ๐Ÿ’œ: it describes the guarantees a party still has after its own long-term secret has been compromised ๐Ÿ™ˆ.

E.1.6. External References ๐Ÿ“š

The reader should check out the following references for analysis of the TLS handshake ๐ŸŽ€: [DFGS15], [CHSV16], [DFGS16], [KW16], [Kraw16], [FGSW16], [LXZFH16], [FG17], and [BBK17]. Iykyk ๐Ÿ˜Œ.

E.2. Record Layer ๐Ÿ“

The record layer depends on the handshake producing strong traffic secrets, which can be used to derive bidirectional encryption keys and nonces ๐Ÿ”‘. Assuming that's true, and the keys are used for no more data than indicated in Section 5.5, then the record layer should provide the following guarantees โœจ:

  • Confidentiality ๐Ÿซฃ: An attacker should not be able to determine the plaintext contents of a given record.
  • Integrity ๐Ÿ›ก๏ธ: An attacker should not be able to craft a new record that is different from an existing record and will be accepted by the receiver.
  • Order protection/non-replayability ๐Ÿ”: An attacker should not be able to cause the receiver to accept a record it has already accepted, or cause the receiver to accept record N+1 without having first processed record N.
  • Length concealment ๐Ÿ™ˆ: Given a record with a given external length, the attacker should not be able to determine how much of the record is content versus padding.
  • Forward secrecy after key change ๐Ÿฆ‹: If the traffic key update mechanism described in Section 4.6.3 has been used and the previous generation key is deleted, an attacker who compromises the endpoint should not be able to decrypt traffic encrypted with the old key.

Informally, TLS 1.3 gets these properties by AEAD-protecting the plaintext with a strong key ๐Ÿ’–. AEAD encryption [RFC5116] provides confidentiality and integrity for the data. Non-replayability comes from using a separate nonce for each record, derived from the record sequence number (Section 5.3), with the sequence number maintained independently at both sides ๐Ÿงฎ. So records delivered out of order cause AEAD deprotection failures ๐Ÿ™…โ€โ™€๏ธ. To prevent mass cryptanalysis when the same plaintext is repeatedly encrypted by different users under the same key (as is commonly the case for HTTP) ๐Ÿ˜ฑ, the nonce is formed by mixing the sequence number with a secret per-connection initialization vector derived along with the traffic keys ๐ŸŽ€. See [BT16] for analysis of this construction.

The rekeying technique in TLS 1.3 (see Section 7.2) follows the construction of the serial generator discussed in [REKEY] ๐Ÿ”„. That shows rekeying can allow keys to be used for a larger number of encryptions than without rekeying โœจ. This relies on the security of the HKDF-Expand-Label function as a pseudorandom function (PRF) ๐Ÿง . Also, as long as this function is truly one way, it is not possible to compute traffic keys from before a key change (forward secrecy) ๐Ÿ”.

TLS does not provide security for data communicated on a connection after a traffic secret of that connection is compromised ๐Ÿ’€. That means TLS does not provide post-compromise security/future secrecy/backward secrecy with respect to the traffic secret ๐Ÿ˜ญ. Honestly, an attacker who learns a traffic secret can compute all future traffic secrets on that connection ๐Ÿšจ. Systems that want such guarantees need to do a fresh handshake and establish a new connection with an (EC)DHE exchange ๐Ÿ’….

E.2.1. External References ๐Ÿ“–

The reader should check out the following references for analysis of the TLS record layer ๐ŸŽ€: [BMMRT15], [BT16], [BDFKPPRSZZ16], [BBK17], and [PS18].

E.3. Traffic Analysis ๐Ÿ•ต๏ธโ€โ™€๏ธ

TLS is vulnerable to a bunch of traffic analysis attacks that work by watching the length and timing of encrypted packets [CLINIC] [HCJC16] ๐Ÿ‘€. This is especially easy when there's a small set of possible messages to tell apart, like a video server hosting a fixed set of content ๐ŸŽฌ, but it still leaks useful info even in more complicated setups ๐Ÿ˜ฌ.

TLS doesn't have any specific defenses against this kind of attack ๐Ÿ™…โ€โ™€๏ธ, but it does include a padding mechanism for applications to use ๐ŸŽ. The plaintext protected by the AEAD function is content plus variable-length padding, which lets the application produce arbitrary-length encrypted records, as well as padding-only cover traffic that hides the difference between periods of transmission and periods of silence ๐Ÿซฅ. Because the padding is encrypted alongside the actual content, an attacker can't directly see the padding length ๐Ÿ™ˆ, but they may be able to measure it indirectly through timing channels exposed during record processing ๐Ÿ•ฐ๏ธ (like seeing how long it takes to process a record, or trickling in records to see which ones get a response from the server ๐Ÿ‘ป). In general, it isn't known how to remove all of these channels, because even a constant-time padding removal function will likely feed the content into data-dependent functions ๐Ÿซ . At minimum, a fully constant-time server or client would need close cooperation with the application-layer protocol implementation, including making that higher-level protocol constant time ๐Ÿง˜โ€โ™€๏ธ.

Note: Robust traffic analysis defenses will likely lead to inferior performance due to delays in transmitting packets and increased traffic volume ๐ŸŒ.

E.4. Side-Channel Attacks ๐Ÿ•ถ๏ธ

In general, TLS does not have specific defenses against side-channel attacks (the ones that attack communications through secondary channels, like timing) โฑ๏ธ, and leaves those to the implementation of the relevant cryptographic primitives ๐Ÿ› ๏ธ. That said, certain features of TLS are designed to make it easier to write side-channel resistant code ๐Ÿ’ช:

  • Unlike previous versions of TLS, which used a composite MAC-then-encrypt structure, TLS 1.3 only uses AEAD algorithms, which lets implementations use self-contained constant-time implementations of those primitives ๐ŸŽ‰.
  • TLS uses a uniform bad_record_mac alert for all decryption errors ๐Ÿค. This is meant to stop an attacker from gaining piecewise insight into portions of the message ๐Ÿงฉ. Extra resistance comes from terminating the connection on such errors, since a new connection will have different cryptographic material, which prevents attacks on cryptographic primitives that need multiple trials ๐Ÿ™ƒ.

Information leakage through side channels can happen above TLS, in application protocols and the applications that use them ๐Ÿšช. Resistance to side-channel attacks depends on applications and application protocols each making sure confidential information isn't accidentally leaked ๐Ÿ”’๐Ÿ’–.

E.5. Replay Attacks on 0-RTT โœจ

okay bestie, replayable 0-RTT data is a whole mood ๐Ÿ˜ญ TLS-using apps can get hit with real threats here unless they're built to be safe under replay. The bare minimum is being idempotent, and some apps need even stronger stuff, like constant-time responses ๐Ÿ’… Here's what can go wrong:

  • Side effects getting doubled up, like buying something or sending money twice. That totally hurts the site or the user ๐Ÿ’ธ
  • Attackers storing and replaying 0-RTT messages to shuffle them around other messages, like sneaking a delete in after a create ๐Ÿ™ˆ
  • Cache timing sneakiness: replay a 0-RTT message to a different cache node, then use a separate connection to measure latency and figure out if both requests hit the same resource ๐Ÿ•ต๏ธโ€โ™€๏ธ

If data can get replayed a bunch of times, even more attacks open up, like repeatedly timing how fast crypto operations run ๐Ÿซฃ They might also be able to overwhelm rate-limiting systems ๐Ÿšจ For a deeper dive on all this, check out [Mac17] ๐Ÿ“š

Okay, real talk: servers are the ones responsible for protecting themselves from 0-RTT replication attacks ๐Ÿ›ก๏ธ The mechanisms in Section 8 are meant to stop replay at the TLS layer, but they don't fully protect you from getting multiple copies of client data. TLS 1.3 falls back to the 1-RTT handshake when the server has no info about the client. That can happen if it's in a different cluster that doesn't share state, or if the ticket got deleted as described in Section 8.1 ๐ŸŽซ Now, if the application-layer protocol retransmits data in that situation, an attacker can cause duplication. They send the ClientHello to the original cluster, which processes the data right away, and to another cluster that falls back to 1-RTT and processes the data when the app replays it ๐Ÿ˜ฑ How much damage this can do depends on how willing the client is to retry transactions, so it only allows a limited amount of duplication. Each copy shows up at the server as a brand new connection ๐ŸŒ€

If it's implemented correctly, the mechanisms in Sections 8.1 and 8.2 stop a replayed ClientHello and its 0-RTT data from being accepted multiple times by any cluster with consistent state ๐Ÿ™Œ If a server only lets 0-RTT go through one cluster for a single ticket, then a given ClientHello and its 0-RTT data will only get accepted once. BUT if the state isn't totally consistent, an attacker might get multiple copies accepted during the replication window ๐Ÿ˜ฌ Clients don't know the exact details of server behavior, so they MUST NOT send messages in early data that aren't safe to replay and that they wouldn't be okay retrying across multiple 1-RTT connections ๐Ÿšซ

Application protocols MUST NOT use 0-RTT data without a profile that defines how it gets used ๐Ÿ“ That profile needs to say which messages or interactions are safe for 0-RTT, and how to handle it when the server rejects 0-RTT and falls back to 1-RTT ๐Ÿ”„

To avoid accidental misuse, TLS implementations MUST NOT enable 0-RTT (whether sending or accepting) unless the application specifically asks for it ๐Ÿ™…โ€โ™€๏ธ They also MUST NOT automatically resend 0-RTT data that the server rejected, unless the application tells them to ๐Ÿ” Server-side apps might want special handling for 0-RTT data for certain kinds of traffic, like aborting the connection, asking for the data to be resent at the application layer, or delaying processing until the handshake finishes โณ So that apps can do this kind of processing, TLS implementations MUST provide a way for the application to tell whether the handshake has completed โœ…

E.5.1. Replay and Exporters ๐Ÿ”‘

Replays of the ClientHello produce the same early exporter, so apps that use these exporters need to be extra careful ๐Ÿ‘€ If these exporters are used as an authentication channel binding (for example, by signing the exporter's output), an attacker who compromises the PSK can transplant authenticators between connections without ever touching the authentication key ๐Ÿ’€

Also, the early exporter SHOULD NOT be used to generate server-to-client encryption keys, because that would mean reusing those keys ๐Ÿ” This mirrors how the early application traffic keys are only used in the client-to-server direction ๐Ÿชž

E.6. PSK Identity Exposure ๐Ÿ•ต๏ธโ€โ™€๏ธ

Okay, so implementations respond to an invalid PSK binder by aborting the handshake ๐Ÿ™ƒ That means an attacker might be able to check whether a given PSK identity is valid. If a server accepts both external-PSK handshakes and certificate-based handshakes, a valid PSK identity makes the handshake fail, while an invalid identity just gets skipped and the certificate handshake succeeds ๐Ÿ˜ถ Servers that only support PSK handshakes may be able to block this by treating "no valid PSK identity" and "an identity with an invalid binder" the exact same way ๐Ÿงฑ

E.7. Sharing PSKs ๐Ÿค

TLS 1.3 is super conservative with PSKs and binds them to a specific KDF ๐Ÿง TLS 1.2, on the other hand, lets PSKs be used with any hash function and the TLS 1.2 PRF. So any PSK used with both TLS 1.2 and TLS 1.3 has to be used with only one hash in TLS 1.3, which is less than ideal if users want to set up a single PSK ๐Ÿ˜ฉ The constructions in TLS 1.2 and TLS 1.3 are different, even though they're both based on HMAC ๐Ÿ”ง There's no known way for the same PSK to produce related output in both versions, but only limited analysis has been done so far ๐Ÿ” To stay safe from cross-protocol related output, implementations can avoid reusing PSKs between TLS 1.3 and TLS 1.2 ๐Ÿซถ

E.8. Attacks on Static RSA ๐Ÿฆ‹

TLS 1.3 doesn't use RSA key transport, so it isn't directly vulnerable to Bleichenbacher-type attacks [Blei98] ๐ŸŽ‰ BUT if TLS 1.3 servers also support static RSA for older versions of TLS, it may be possible to impersonate the server for TLS 1.3 connections [JSS15] ๐Ÿ˜ญ TLS 1.3 implementations can stop this by disabling static RSA support across all versions of TLS ๐Ÿ›‘ In principle, implementations might also be able to separate certificates with different keyUsage bits for static RSA decryption and RSA signature. The catch is that this trick relies on clients refusing to accept signatures from keys in certificates that don't have the digitalSignature bit set, and lots of clients don't enforce that restriction ๐Ÿ™ˆ

Contributors โœจ

Okay bestie, this is the squad that helped bring this spec to life ๐Ÿซถ Every name, affiliation, and email is listed below exactly as it appears in the original, so you can reach out if you want to talk protocols ๐Ÿ’Œ

  • Martin Abadi ยท University of California, Santa Cruz ยท abadi@cs.ucsc.edu
  • Christopher Allen (co-editor of TLS 1.0) ยท Alacrity Ventures ยท ChristopherA@AlacrityManagement.com
  • Richard Barnes ยท Cisco ยท rlb@ipv.sx
  • Steven M. Bellovin ยท Columbia University ยท smb@cs.columbia.edu
  • David Benjamin ยท Google ยท davidben@google.com
  • Benjamin Beurdouche ยท INRIA & Microsoft Research ยท benjamin.beurdouche@ens.fr
  • Karthikeyan Bhargavan (editor of [RFC7627]) ยท INRIA ยท karthikeyan.bhargavan@inria.fr
  • Simon Blake-Wilson (co-author of [RFC4492]) ยท BCI ยท sblakewilson@bcisse.com
  • Nelson Bolyard (co-author of [RFC4492]) ยท Sun Microsystems, Inc. ยท nelson@bolyard.com
  • Ran Canetti ยท IBM ยท canetti@watson.ibm.com
  • Matt Caswell ยท OpenSSL ยท matt@openssl.org
  • Stephen Checkoway ยท University of Illinois at Chicago ยท sfc@uic.edu
  • Pete Chown ยท Skygate Technology Ltd ยท pc@skygate.co.uk
  • Katriel Cohn-Gordon ยท University of Oxford ยท me@katriel.co.uk
  • Cas Cremers ยท University of Oxford ยท cas.cremers@cs.ox.ac.uk
  • Antoine Delignat-Lavaud (co-author of [RFC7627]) ยท INRIA ยท antdl@microsoft.com
  • Tim Dierks (co-author of TLS 1.0, co-editor of TLS 1.1 and 1.2) ยท Independent ยท tim@dierks.org
  • Roelof DuToit ยท Symantec Corporation ยท roelof_dutoit@symantec.com
  • Taher Elgamal ยท Securify ยท taher@securify.com
  • Pasi Eronen ยท Nokia ยท pasi.eronen@nokia.com
  • Cedric Fournet ยท Microsoft ยท fournet@microsoft.com
  • Anil Gangolli ยท anil@busybuddha.org
  • David M. Garrett ยท dave@nulldereference.com
  • Illya Gerasymchuk ยท Independent ยท illya@iluxonchik.me
  • Alessandro Ghedini ยท Cloudflare Inc. ยท alessandro@cloudflare.com
  • Daniel Kahn Gillmor ยท ACLU ยท dkg@fifthhorseman.net
  • Matthew Green ยท Johns Hopkins University ยท mgreen@cs.jhu.edu
  • Jens Guballa ยท ETAS ยท jens.guballa@etas.com
  • Felix Guenther ยท TU Darmstadt ยท mail@felixguenther.info
  • Vipul Gupta (co-author of [RFC4492]) ยท Sun Microsystems Laboratories ยท vipul.gupta@sun.com
  • Chris Hawk (co-author of [RFC4492]) ยท Corriente Networks LLC ยท chris@corriente.net
  • Kipp Hickman
  • Alfred Hoenes
  • David Hopwood ยท Independent Consultant ยท david.hopwood@blueyonder.co.uk
  • Marko Horvat ยท MPI-SWS ยท mhorvat@mpi-sws.org
  • Jonathan Hoyland ยท Royal Holloway, University of London ยท jonathan.hoyland@gmail.com
  • Subodh Iyengar ยท Facebook ยท subodh@fb.com
  • Benjamin Kaduk ยท Akamai Technologies ยท kaduk@mit.edu
  • Hubert Kario ยท Red Hat Inc. ยท hkario@redhat.com
  • Phil Karlton (co-author of SSL 3.0)
  • Leon Klingele ยท Independent ยท mail@leonklingele.de
  • Paul Kocher (co-author of SSL 3.0) ยท Cryptography Research ยท paul@cryptography.com
  • Hugo Krawczyk ยท IBM ยท hugokraw@us.ibm.com
  • Adam Langley (co-author of [RFC7627]) ยท Google ยท agl@google.com
  • Olivier Levillain ยท ANSSI ยท olivier.levillain@ssi.gouv.fr
  • Xiaoyin Liu ยท University of North Carolina at Chapel Hill ยท xiaoyin.l@outlook.com
  • Ilari Liusvaara ยท Independent ยท ilariliusvaara@welho.com
  • Atul Luykx ยท K.U. Leuven ยท atul.luykx@kuleuven.be
  • Colm MacCarthaigh ยท Amazon Web Services ยท colm@allcosts.net
  • Carl Mehner ยท USAA ยท carl.mehner@usaa.com
  • Jan Mikkelsen ยท Transactionware ยท janm@transactionware.com
  • Bodo Moeller (co-author of [RFC4492]) ยท Google ยท bodo@acm.org
  • Kyle Nekritz ยท Facebook ยท knekritz@fb.com
  • Erik Nygren ยท Akamai Technologies ยท erik+ietf@nygren.org
  • Magnus Nystrom ยท Microsoft ยท mnystrom@microsoft.com
  • Kazuho Oku ยท DeNA Co., Ltd. ยท kazuhooku@gmail.com
  • Kenny Paterson ยท Royal Holloway, University of London ยท kenny.paterson@rhul.ac.uk
  • Christopher Patton ยท University of Florida ยท cjpatton@ufl.edu
  • Alfredo Pironti (co-author of [RFC7627]) ยท INRIA ยท alfredo.pironti@inria.fr
  • Andrei Popov ยท Microsoft ยท andrei.popov@microsoft.com
  • Marsh Ray (co-author of [RFC7627]) ยท Microsoft ยท maray@microsoft.com
  • Robert Relyea ยท Netscape Communications ยท relyea@netscape.com
  • Kyle Rose ยท Akamai Technologies ยท krose@krose.org
  • Jim Roskind ยท Amazon ยท jroskind@amazon.com
  • Michael Sabin
  • Joe Salowey ยท Tableau Software ยท joe@salowey.net
  • Rich Salz ยท Akamai ยท rsalz@akamai.com
  • David Schinazi ยท Apple Inc. ยท dschinazi@apple.com
  • Sam Scott ยท Royal Holloway, University of London ยท me@samjs.co.uk
  • Thomas Shrimpton ยท University of Florida ยท teshrim@ufl.edu
  • Dan Simon ยท Microsoft, Inc. ยท dansimon@microsoft.com
  • Brian Smith ยท Independent ยท brian@briansmith.org
  • Brian Sniffen ยท Akamai Technologies ยท ietf@bts.evenmere.org
  • Nick Sullivan ยท Cloudflare Inc. ยท nick@cloudflare.com
  • Bjoern Tackmann ยท University of California, San Diego ยท btackmann@eng.ucsd.edu
  • Tim Taubert ยท Mozilla ยท ttaubert@mozilla.com
  • Martin Thomson ยท Mozilla ยท mt@mozilla.com
  • Hannes Tschofenig ยท Arm Limited ยท Hannes.Tschofenig@arm.com
  • Sean Turner ยท sn3rd ยท sean@sn3rd.com
  • Steven Valdez ยท Google ยท svaldez@google.com
  • Filippo Valsorda ยท Cloudflare Inc. ยท filippo@cloudflare.com
  • Thyla van der Merwe ยท Royal Holloway, University of London ยท tjvdmerwe@gmail.com
  • Victor Vasiliev ยท Google ยท vasilvv@google.com
  • Hoeteck Wee ยท Ecole Normale Superieure, Paris ยท hoeteck@alum.mit.edu
  • Tom Weinstein
  • David Wong ยท NCC Group ยท david.wong@nccgroup.trust
  • Christopher A. Wood ยท Apple Inc. ยท cawood@apple.com
  • Tim Wright ยท Vodafone ยท timothy.wright@vodafone.com
  • Peter Wu ยท Independent ยท peter@lekensteyn.nl
  • Kazu Yamamoto ยท Internet Initiative Japan Inc. ยท kazu@iij.ad.jp

Author's Address ๐Ÿ’Œ

Eric Rescorla ๐ŸŽ€ Mozilla

Email: ekr@rtfm.com

๐Ÿ“ฆ where this page comes from