TLS13 Session resumption
TLS13 的 Session resumption 和之前版本不同
什么是 Session resumption: 在之前的 TLS 连接之上,基于已经交换过的信息, 快速的建立另外一个TLS 连接的机制。
为什么需要 Session resumption: 加速TLS握手, 更快的准备好安全的数据通道.
常见的使用场景:
- FTP 协议。 在 FTP 协议中,当 control channel 使用 TLS 协议进行通信,通常会要求 Data channel 也进行 TLS 加密通信,并且强制要求 Data channel 使用 Session resumption 机制建立 TLS 连接。
 - HTTP 协议中,当我们需要和同一个服务器建立多个 TLS 连接时,我们可以选择在成功的建立一条TLS连接之后,后续连接使用 Session resumption 来加速 TLS 连接建立的速度。
 - 其他场景。
 
概述
正如前面所说, Session resumption 可以加速握手流程,这里我们先看看具体是如何做到加速的?
完整握手流程:
Client Server---------------------------------------------------------------------ClientHello+ key_share -------->ServerHello+ key_share{EncryptedExtensions}{CertificateRequest*}{Certificate*}{CertificateVerify*}{Finished}<-------- [Application Data*]{Certificate*}{CertificateVerify*}{Finished} --------><-------- [NewSessionTicket][Application Data] <-------> [Application Data]
Session resumption 的握手流程:
Client Server---------------------------------------------------------------------ClientHello+ key_share*+ pre_shared_key -------->ServerHello+ pre_shared_key+ key_share*{EncryptedExtensions}{Finished}<-------- [Application Data*]{Finished} -------->[Application Data] <-------> [Application Data]
我们可以看到,在 Session Resumption 的握手流程中,CertificateRequest, Certificate, CertrificateVerify都不用再次发送了。这就很明显了,更少的数据交换,那就需要更少的时间啦。
详细流程
这里,我们需要说明的一点儿是, Session resumption 的前提是必须已经和同一个服务器建立过TLS连接。那么接下来的连接中可以选择是否使用 Session resumption。
Session Resumption 在协议上的实现如下:
- 客户端A和服务器先建立一条 TLS13 的连接,并进行后续的通信。
 - 通常情况下, 如果服务器支持 Session Resumption, 它便会给你发送一个 
New Session Ticket数据包。(这个包我们后面详细解析, 这里只需要知道它包含了一个 Ticket, 这个 Ticket 用于Session Resumption) - 接着,客户端B(可以是另外一个客户端B或者同一个客户端A)需要和服务器建立 TLS13 连接进行通信。这里,第一条 TLS 连接是否依然存活已经不再重要, 仅仅需要第一次的 TLS连接成功的被建立,并且收到了来自服务器的  
New Session Ticket数据包即可。 - 客户端B发送 
ClientHello. 这个包的不同点在于: 它需要包含也给pre_shared_key扩展。 这个扩展中包含了前面从服务器端收到的New Session Ticket中的 Ticket 信息, 和基于这个 Ticket 计算的一个哈希值。 - 服务器端收到这个 
ClientHello之后,校验pre_shared_key扩展中的值是否合法。 如果合法,并且服务器选择和当前客户端进行 Session Resumption, 那么它会回复一个ServerHello, 这个包中包含一个pre_shared_key扩展,这个扩展中包含它选中的客户端提供的 ticket。 - 客户端在收到服务器端的
ServerHello之后,发现其中携带pre_shared_key扩展,对它进行校验,如果检验合法,那么双方便能根据之前TLS连接的信息生成用户当前TLS连接的安全参数(cipher suite, master key 等等)。 - 后续的部分 TLS 握手数据包便可以省略不发,快进到交换 
Finished来表明握手已经成功,接下来可以交换 Application data 了。至此 Session Resumption 成功。 
如果在第五步中,服务器收到带有 pre_shared_key 扩展的 ClientHello 之后,不想进行 Session Resumption, 那么回复的 ServerHello 中便不会携带 pre_shared_key 扩展。 那么客户端和服务器便需要想通常那样,进行完整的 TLS 握手流程。
重要的实现细节
在第一次 TLS 握手成功,并且收到来自服务器端的 New Session Ticket , 客户端需要做如下事情:
- 记录这个数据包中的信息,后续需要使用。
 - 在此, 需要计算一个叫做 
resumption master secret的密钥。这个密钥用户计算在Session Resumption 成功之后的 TLS 相关加密参数。 
对于想要使用 Session Resumption 方式进行TLS握手的客户端,需要将上面的信息设置到当前客户端上。 在当前客户中,这些信息用于计算附带在 ClientHello 中的 pre_shared_key扩展。
客户端中的 pre_shared_key 中主要包含一个或多个 OfferedPsks , 每个 OfferedPsks  包含两个信息:
- 一个叫做 PskIdentity  的信息。 这里包含  
New Session Ticket中的 Ticket。服务器端可以根据这个 ticket 来获取上一个 TLS session 的信息,包括上一个 Tls session 中所用到的加密参数等等。还包括一个 ticket 过期时间的信息。 用户判断当前 ticket 是否已经过期。 - 一个叫做 PskBinderEntry  的信息。这里包含 HMAC 的输出,HMAC输入为当前 ClientHello 序列化后的字节数组(排除掉 PskBinderEntry 自己), 使用特定的 binder key 作为 key。 这个 binder key 由 
resumption master secret通过一系列算法生成而来。 
服务器端收到来自客户端的 pre_shared_key 扩展之后,检查当前 ticket 的合法性, 从 Ticket 中获取想要恢复的 TLS session 信息。 当决定接受这个扩展之后,回复一个pre_shared_key 扩展给客户端,表明它接受 session resumption.
来自服务器端中的 pre_shared_key 中一个信息: 那就是它所选中的客户端中 OfferedPsks 的索引值。
在客户端和服务器交换了 ClientHello 和 ServerHello 之后,并且得到对方都决定进行 Session resumption 之后,双方都根据自己上一次 TLS session 中计算的 resumption master secret 通过一系列算法来生成用于当前 TLS session 进行加密的 key。这个过程中,不需要额外的TLS数据包的交换。
New Session Ticket
在TLS握手成功后,服务器端可以在任何时刻发送 New Session Ticket 消息给客户端。
这个数据包的作用是: 将当前 ticket 和当前 TLS session 中的 由 resumption master secret 生成的 PSK 关联起来。
它的定义如下:
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;
- 
ticket_lifetime: 代表当前 Ticket 的生命周期,单位为秒。最大值为 7天。 如果为零,表示这个 ticket 立即过期。
 - 
ticket_age_add: 一个随机值,用户混淆客户端发送给服务器端的 'Obfucated Ticket Age' 的值。
 - 
ticket_nonce: 在计算 PskBinderEntry 的 HMAC 值时,作为 salt。
 - 
ticket:一个服务器生成的字节数组,内容的含义是实现相关的。 客户端不需要关注这些自己的含义,只需要原样的把它传回给服务器端即可。
 - 
extension: 略
 
实践中, 它就像下面这样:
Transport Layer SecurityTLSv1.3 Record Layer: Handshake Protocol: New Session TicketOpaque Type: Application Data (23)Version: TLS 1.2 (0x0303)Length: 252[Content Type: Handshake (22)]Handshake Protocol: New Session TicketHandshake Type: New Session Ticket (4)Length: 231TLS Session TicketSession Ticket Lifetime Hint: 604800 seconds (7 days)Session Ticket Age Add: 2010878192Session Ticket Nonce Length: 4Session Ticket Nonce: bae70ebbSession Ticket Length: 214Session Ticket: ee7ed0879c8cd275fdce0b5c48…Extensions Length: 0
Extension: pre_shared_key
这个扩展用于协商使用的 Ticket Id。
就像前文中描述的一样,客户端可以在 ClientHello 中的 pre_shared_key 中包含多个 OfferedPsks, 客户端从中选择一个,然后把它的 index 值通过pre_shared_key 扩展发回客户端,以此来达到协商的目的。
定义如下:
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;
这里,我们不过多解释了,通过一个例子来看看它的样子。
注意,这个扩展出现在 ClientHello 和 ServerHello 中时,它的定义是不同的!
ClientHello 中的 pre_shared_key 扩展
Extension: pre_shared_key (len=257)Type: pre_shared_key (41)Length: 257Pre-Shared Key extensionIdentities Length: 220PSK Identity (length: 214)Identity Length: 214Identity: ee7ed0879…Obfuscated Ticket Age: 2010878193PSK Binders length: 33PSK Binders:....(HMAC value)
ServerHello 中的 pre_shared_key 扩展:
Extension: pre_shared_key (len=2)Type: pre_shared_key (41)Length: 2Pre-Shared Key extensionSelected Identity: 0
完整的 ClientHello 和 ServerHello
最后,我们包含一个完成的 ClientHello 和 ServerHello 在这里,以供参考:
ClientHello:
TLSv1.3 Record Layer: Handshake Protocol: Client Hello    Content Type: Handshake (22)    Version: TLS 1.0 (0x0301)    Length: 503    Handshake Protocol: Client Hello        Handshake Type: Client Hello (1)        Length: 499        Version: TLS 1.2 (0x0303)        Random: b4d4d566da09c6.....ea        Session ID Length: 32        Session ID: 28ef0f99cb.....5b7653a        Cipher Suites Length: 38        Cipher Suites (19 suites)        Compression Methods Length: 1        Compression Methods (1 method)        Extensions Length: 388        Extension: status_request (len=5)            Type: status_request (5)            Length: 5            Certificate Status Type: OCSP (1)            Responder ID list Length: 0            Request Extensions Length: 0        Extension: supported_groups (len=10)            Type: supported_groups (10)            Length: 10            Supported Groups List Length: 8            Supported Groups (4 groups)        Extension: ec_point_formats (len=2)            Type: ec_point_formats (11)            Length: 2            EC point formats Length: 1            Elliptic curves point formats (1)        Extension: session_ticket (len=0)            Type: session_ticket (35)            Length: 0            Data (0 bytes)        Extension: signature_algorithms (len=26)            Type: signature_algorithms (13)            Length: 26            Signature Hash Algorithms Length: 24            Signature Hash Algorithms (12 algorithms)        Extension: renegotiation_info (len=1)            Type: renegotiation_info (65281)            Length: 1            Renegotiation Info extension        Extension: signed_certificate_timestamp (len=0)            Type: signed_certificate_timestamp (18)            Length: 0        Extension: supported_versions (len=3)            Type: supported_versions (43)            Length: 3            Supported Versions length: 2            Supported Version: TLS 1.3 (0x0304)        Extension: key_share (len=38)            Type: key_share (51)            Length: 38            Key Share extension        Extension: psk_key_exchange_modes (len=2)            Type: psk_key_exchange_modes (45)            Length: 2            PSK Key Exchange Modes Length: 1            PSK Key Exchange Mode: PSK with (EC)DHE key establishment (psk_dhe_ke) (1)        Extension: pre_shared_key (len=257)            Type: pre_shared_key (41)            Length: 257            Pre-Shared Key extension                Identities Length: 220                PSK Identity (length: 214)                    Identity Length: 214                    Identity: ee7ed087...…                    Obfuscated Ticket Age: 2010878193                PSK Binders length: 33                PSK Binders
ServerHello:
TLSv1.3 Record Layer: Handshake Protocol: Server HelloContent Type: Handshake (22)Version: TLS 1.2 (0x0303)Length: 128Handshake Protocol: Server HelloHandshake Type: Server Hello (2)Length: 124Version: TLS 1.2 (0x0303)Random: b624.....1Session ID Length: 32Session ID: 28ef0f99.....3aCipher Suite: TLS_AES_128_GCM_SHA256 (0x1301)Compression Method: null (0)Extensions Length: 52Extension: key_share (len=36)Type: key_share (51)Length: 36Key Share extensionExtension: supported_versions (len=2)Type: supported_versions (43)Length: 2Supported Version: TLS 1.3 (0x0304)Extension: pre_shared_key (len=2)Type: pre_shared_key (41)Length: 2Pre-Shared Key extensionSelected Identity: 0