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 Server
ClientHello
+ 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 Security
TLSv1.3 Record Layer: Handshake Protocol: New Session Ticket
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 82
Handshake Protocol: New Session Ticket
Handshake Type: New Session Ticket (4)
Length: 61
TLS Session Ticket
Session Ticket Lifetime Hint: 7200 seconds (2 hours)
Session Ticket Age Add: 953680565
Session Ticket Nonce Length: 8
Session Ticket Nonce: 0000000000000001
Session Ticket Length: 32
Session Ticket: ....af85e4152440
Extensions Length: 8
Extension: early_data (len=4)
Type: early_data (42)
Length: 4
Maximum 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 Security
TLSv1.3 Record Layer: Handshake Protocol: Client Hello
Content Type: Handshake (22)
Version: TLS 1.0 (0x0301)
Length: 309
Handshake Protocol: Client Hello
Handshake Type: Client Hello (1)
Length: 305
Version: TLS 1.2 (0x0303)
Random: ...1081762b162
Session ID Length: 32
Session ID: ,,,8218132ba7
Cipher Suites Length: 8
Cipher Suites (4 suites)
Compression Methods Length: 1
Compression Methods (1 method)
Extensions Length: 224
....
Extension: early_data (len=0)
Type: early_data (42)
Length: 0
Extension: pre_shared_key (len=91)
Type: pre_shared_key (41)
Length: 91
Pre-Shared Key extension
Identities Length: 38
PSK Identity (length: 32)
PSK Binders length: 49
PSK Binders
early_data
扩展:这个扩展没有实际数据,就是一个标识符,表明当前握手过程中,客户端会发送 0-RTT 数据。
这里同时还要携带 pre_shared_key
(这是前提), 并且这是使用的 PSK
必须是从带有 early_data
扩展的 New Session Ticket
中计算得来的。
[7] EncryptedExtensions.early_data
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: Encrypted Extensions
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 27
[Content Type: Handshake (22)]
Handshake Protocol: Encrypted Extensions
Handshake Type: Encrypted Extensions (8)
Length: 6
Extensions Length: 4
Extension: early_data (len=0)
Type: early_data (42)
Length: 0
early_data
扩展: 用于向客户端表明服务器端接受了客户端的 0-RTT 数据。
[8] EndOfEarlyData
Transport Layer Security
TLSv1.3 Record Layer: Handshake Protocol: End of Early Data
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 21
[Content Type: Handshake (22)]
Handshake Protocol: End of Early Data
Handshake 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 Security
TLSv1.3 Record Layer: Handshake Protocol: Encrypted Extensions
Opaque Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 23
[Content Type: Handshake (22)]
Handshake Protocol: Encrypted Extensions
Handshake Type: Encrypted Extensions (8)
Length: 2
Extensions Length: 0
因为,客户端的 0-RTT 已经被明确的拒绝接受了,因此,客户端也不需要再发送 EndOfEarlyData
消息了.