TLS1.3---密钥的计算续

New Session Ticket Message

首先我还是想说一下这个扩展:

  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;

关于每个字段的含义我就不解释了,在TLS1.3概述这篇博客中有详细的解释。
有人可能会问:为什么要说这个呢?
在之前的一篇博客中一直在说TLS1.3中的密钥计算相关的问题,其中我说到了PreSharedKeyExtension这个扩展里面的binder_key是由PSK计算出的Early Secret得来,

 struct {
          opaque identity<1..2^16-1>;
          uint32 obfuscated_ticket_age;
      } PskIdentity;
  • identity:其中存储的就是NewSessionTicket中的ticket
    我么来看一下是如何计算的:
HKDF-Expand-Label(resumption_master_secret,
                "resumption", ticket_nonce, Hash.length) =PskIdentity.identity = ticket 

PreShareKeyPskIdentityPskBinderEntry 结合成 PSK
我们可以看到使用了resumption_master_secret这个密钥,这就和之前的内容联系起来了,我们知道这个密钥是由Master SecretDerive-Secret函数计算得来:

0 -> HKDF-Extract = Master Secret
             |
   			 |
             +-----> Derive-Secret(., "res master",
                                   ClientHello...client Finished)
                                   = resumption_master_secret

ticket_nonce就是NewSessionTicket扩展中对应的字段,因为 ticket_nonce 值对于每个 NewSessionTicket 消息都是不同的,所以每个 ticket 会派生出不同的 PSK。这样的话,整个PreSharedKey扩展就都理解清楚了。

我还想说一下NewSessionTicket中的extensions,它里面涉及的是early_data扩展,表示该 ticket 可用于发送 0-RTT 数据,正是因为有这个扩展,才会使得TLS1.3具有0-RTT这样的握手时间,极大提高了握手效率。在这里,扩展对应的名称是max_early_data_size这个字段表示使用 ticket 时允许 Client 发送的最大 0-RTT 数据量(以字节为单位)。Server 如果接收的数据大小超过了 max_early_data_size 字节的 0-RTT 数据,应该立即使用 "unexpected_message" alert消息终止连接。

0-RTT(Round-trip time)

前面的时候也说过这个0-RTT,先来看一下RTT的定义:

  • 在双方通信中,发讯方的信号(Signal)传播(Propagation)到收讯方的时间(意即:传播延迟(Propagation delay)),加上收讯方回传消息到发讯方的时间。

之前理解的不是特别到位,现在再来梳理一下:

如果要想达到0-RTT的时间需要一些条件:

  • 首先是clientserver之前有过一次握手,并且结束之后server发送的NewSessionTicket中携带max_early_data_size扩展,表明server愿意接受EarlyData
  • 其次是第二次握手的时候,client发送了PreSharedKey扩展,这里面有一个规定,其中的identities字段中的第一个标识被用来标识 0-RTT 的,并且server正确相应成功恢复会话。
  • 同时client也需要发送EarlyData扩展,并且在clienthello后面紧跟Application Data
  • 其次server端需要在它的EncryptedExtensions中返回自己的 EarlyData 扩展,表明它准备处理 early data

其中的EncryptedExtensions是由server发送出的,在所有的握手中,Server 必须在 ServerHello 消息之后立即发送 EncryptedExtensions 消息。这是在从 server_handshake_traffic_secret 派生的密钥下加密的第一条消息。其中包含着应该被保护的扩展:

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

有关前面的密钥计算,只有选择使用PSK握手模式时,也就是说之前握手过,这是第二次握手,这时密钥的计算是从头开始的,也就是:

 			 0
             |
             v
   PSK ->  HKDF-Extract = Early Secret

这一部分开始,如果没有使用PSK回复回话,那么这一步也是不能省略的,只不过就是hash.length长度的0用于计算HKDF-Extract(0,0)。下面是我画的0-RTT密钥计算的流程图:
tls_0_RTT

补充

Transcript-Hash

server发现客户端没有提供合适的key_share组时,会发送HelloRetryRequest消息,此消息的结构和serverhello是基本一致的,里面也带有key_share扩展通过它来告知client需要作出那些更改,client会根据它作出相应的更改,如:更换key_share组,然后会再次发送修改后的clienthello消息。

  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]

在这种情况下,对握手信息的计算就需要作出一些改变,主旨就是HelloRetryRequest消息是包含在握手消息内的,也就是说 握手记录 包括最初的ClientHello / HelloRetryRequest交换; 它不会重新设置新的ClientHello

我们来看一下计算公式:

 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)

猜你喜欢

转载自blog.csdn.net/qq_35324057/article/details/105894606
今日推荐