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)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章