0-RTT
0-RTT: Zero round-trip time
这是 TLS1.3 新加的一个特性.
用途: 可以用于快速的发送一部分数据
“快速” 的含义:通常的 TLS 连接, 只有 TLS 握手成功之后, 才能开始发送应用层数据。但是当使用 0-RTT 特性发送消息时, 不需要等待握手结束,便可以提交发送一小部分数据。 因为不需要等待 TLS 握手流程进行完毕,因此比较快。
代价是牺牲部分 TLS 的安全性。
如何工作的?
并不是任何情况下都能直接发送 0-RTT 数据包。 客户端需要提前有一个和服务器端共享的 PSK.
这个 PSK 的获取方式可以是: 从之前的链接上获取,或者通过其他外部方式。
这里的 PSK 主要用于 TLS 1.3 会话恢复(session resumption),
也就是意味着客户端和服务器已经有一组共享的密钥,可以用来生成加解密 0-RTT 数据的密钥(0-RTT traffic keys)
如下流程, 0-RTT 数据直接跟在客户端的 ClientHello 之后或者 Change Cipher Spec 之后.()
(Change Cipher Spec 在 TLS1.3 中不是必须的,但是为了兼容旧的版本,允许发送 Change Cipher Spec)
Client                                               ServerClientHello+ early_data+ key_share*+ psk_key_exchange_modes+ pre_shared_key(Application Data*)(0-RTT Data) -------->                                                         ServerHello                                                    + pre_shared_key                                                        + key_share*                                               {EncryptedExtensions}                                                       + early_data*                                                          {Finished}                                 <--------       [Application Data*](EndOfEarlyData){Finished}              -------->[Application Data]      <------->        [Application Data]               Figure 4: Message Flow for a 0-RTT Handshake
代价
0-RTT 数据的安全性是明显弱于正常握手后发送的 TLS 数据的。
尤其是以下几点:
- 这些数据的不是前向保密的(forward secret)。 因为加密这些数据的密钥是从 
PSK直接生成的。 - 不能保证这些数据在不同链接上不会被重播(replay)。 1-RTT 数据的防止重播 的机制是依赖于 
ServerHello.Random的值,而0-RTT数据的加密不依赖于这个值,因此没有这样的保护。 
实例

1-5 是前一个连接, 主要是为了获取 PSK, 这里的 PSK 由服务器端发送的 New Session Ticket 得到。
6-9 是当前连接(一条新的连接)。 我们注意到红框圈起来的部分便是 0-RTT 数据。 它是直接跟在 Change Cipher Spec 之后的, 也就是我们当前连接的 TCP 连接建立起来之后, 直接便发送 Client Hello, Change Cipher Spec 和 0-RTT 数据, 而不需要等待 TLS 握手完成。
这里我们在第 6 步便把数据发送出去了, 第 8 步才是 TLS 握手成功的节点。
这里依然有一些细节需要注意,我们下面逐一解析:
[5] NewSessionTicket.early_data
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: New Session TicketOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 82Handshake Protocol: New Session TicketHandshake Type: New Session Ticket (4)Length: 61TLS Session TicketSession Ticket Lifetime Hint: 7200 seconds (2 hours)Session Ticket Age Add: 953680565Session Ticket Nonce Length: 8Session Ticket Nonce: 0000000000000001Session Ticket Length: 32Session Ticket: ....af85e4152440Extensions Length: 8Extension: early_data (len=4)Type: early_data (42)Length: 4Maximum Early Data Size: 16384
第 5 步中的 New Session Ticket 携带了一个扩展 early_data, 用于表明当前 Session ticket 可以用于发送 0-RTT 数据。
并且这个 early_data 中携带一个 Maximum Early Data Size 表明服务器端愿意接收 0-RTT 数据的最大值,超过这个值会被拒绝。 (这也就是我们开始说可以发送“一部分数据”的原因了, 不能像 1-RTT 那样发送任意数量的数据)
[6] ClientHello.early_data
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: Client HelloContent Type: Handshake (22)Version: TLS 1.0 (0x0301)Length: 309Handshake Protocol: Client HelloHandshake Type: Client Hello (1)Length: 305Version: TLS 1.2 (0x0303)Random: ...1081762b162Session ID Length: 32Session ID: ,,,8218132ba7Cipher Suites Length: 8Cipher Suites (4 suites)Compression Methods Length: 1Compression Methods (1 method)Extensions Length: 224....Extension: early_data (len=0)Type: early_data (42)Length: 0Extension: pre_shared_key (len=91)Type: pre_shared_key (41)Length: 91Pre-Shared Key extensionIdentities Length: 38PSK Identity (length: 32)PSK Binders length: 49PSK Binders
early_data 扩展:这个扩展没有实际数据,就是一个标识符,表明当前握手过程中,客户端会发送 0-RTT 数据。
这里同时还要携带 pre_shared_key(这是前提), 并且这是使用的 PSK 必须是从带有 early_data 扩展的 New Session Ticket 中计算得来的。
[7] EncryptedExtensions.early_data
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: Encrypted ExtensionsOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 27[Content Type: Handshake (22)]Handshake Protocol: Encrypted ExtensionsHandshake Type: Encrypted Extensions (8)Length: 6Extensions Length: 4Extension: early_data (len=0)Type: early_data (42)Length: 0
early_data 扩展: 用于向客户端表明服务器端接受了客户端的 0-RTT 数据。
[8] EndOfEarlyData
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: End of Early DataOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 21[Content Type: Handshake (22)]Handshake Protocol: End of Early DataHandshake Type: End of Early Data (5)Length: 0
EndOfEarlyData 是客户端发送给服务器的一个消息, 用于表明自己的 0-RTT 数据已经发送完毕,解析来的数据将是平常的 TLS 加密的数据了。
这个消息也是空的,只是用于表明自己 0-RTT发送完毕了。
实例: 0-RTT数据被拒绝
也存在一些场景,客户端发送的 0-RTT 数据不被服务器端接受.这里我们来看看响应的流程.
(这里我是用一个过期的 PSK 来模拟这种场景的)

在步骤 1 中, 我们依然是发送 ClientHello 以及相应的 0-RTT 数据(Application Data 中)
ClientHello 中依然携带 pre_shared_key 和 early_data 扩展. 但是这里的 pre_shared_key 是已经过期了. 因此,此次 TLS 会话恢复(Session resumption) 会失败,因此服务器端无法正常解密我们的 0-RTT 数据,因此拒绝接受它.
在步骤 2中, 服务器回复的 Encrypted Extensions 中便没有携带 early_data 扩展, 以表示不接受我们的 0-RTT 数据.
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: Encrypted ExtensionsOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 23[Content Type: Handshake (22)]Handshake Protocol: Encrypted ExtensionsHandshake Type: Encrypted Extensions (8)Length: 2Extensions Length: 0
因为,客户端的 0-RTT 已经被明确的拒绝接受了,因此,客户端也不需要再发送 EndOfEarlyData 消息了.