天天看點

TLS 1.3協定分析

2年多以前學習總結TLS1.3文檔的筆記,希望裡面的一些分析對有困惑的同行有所幫助

截止目前2017/04/07 已知實作TLS1.3的廠商有ngix 且在firefox 中49版本以經支援但沒有預設打開,在52版本中已經預設打開; OpenSSL 将在1.1.1版本中支援;

要使用支援TLS1.3的原因:

There are a couple of reasons why TLS 1.3 is important. First, TLS 1.3 always uses Forward Secrecy (PFS). Consequently, if traffic is intercepted, or if long-term TLS certificate private keys are exposed, there is no common key so each session is separately protected. Second, it only uses strong ciphers (AES-GCM and ChaCha-Poly) so it will be easy to configure applications that are secure by default. Finally, it has a way for clients to reconnect to a server to which they have previously connected, so that an entire network round-trip can be avoided.

要快速建立安全連接配接,而不是像現在這樣要互動3,4個包

When coupled with other speed-ups like TCP FastOpen, it can mean that users can connect to their favorite website, and start seeing the page display with a single packet exchange, rather than the current three or more

各大廠商已經着手開發TLS1.3

Mozilla’s NSS (used in Firefox), Facebook server (fizz), Google’s BoringSSL (which powers their servers and Chrome), a Go implementation from CloudFlare, and other commercial offerings.

TLS 1.3 removes support for known insecure ciphers such as RC4, DES, 3DES and export grade ciphers as well older hashing algorithms e.g. SHA-1 and MD5.

OpenSSL 1.3 的支援必須要在其1.1.0的基礎上

At a face-to-face team meeting in October 2016, the OpenSSL development team decided that TLS 1.3 would be the focus of the next release and that it would be 1.1.1 to maintain API/ABI compatibility with the then-current 1.1.0 release. The effort in 1.1.0 to make data structures opaque is an important factor in being able to ensure this compatibility.

OpenSSL 号稱在4.5号釋出1.3的支援:

Because of our sponsorship, OpenSSL will deliver TLS 1.3 on April 5, in a version compatible with 1.1.0. Akamai will begin incorporating this update into its systems soon thereafter.

This reduces the attack surface (defined within the second paragraph of this blog post) of TLS 1.3 but the improvements don’t stop there. Cipher suites such as NIST P-256 and AES-GCM are being removed as primitives with only x25519, ChaCha20 and Poly1305 remaining developed by Dan Bernstein (who uses the handle djb).

X25519 is a key exchange protocol (with a similar purpose to Diffie Hellman), ChaCha20 is a stream cipher (a more secure alternative to the older RC4) and Poly1305 is used as a message authentication code (defined) with a view to replacing GCM.

Wireshark 對TLS1.3 的狀态跟蹤:

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=12779

SSL: RFC 7250 format for encoding raw public keys in certificate message

https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=11480

wireshark build下載下傳:

https://www.wireshark.org/download/automated/

TLS 協定的分析和對TLS1.3的改動描述:

https://blog.helong.info/blog/2015/09/07/tls-protocol-analysis-and-crypto-protocol-design/

https://blog.cloudflare.com/tls-1-3-overview-and-q-and-a/

https://github.com/WeMobileDev/article/blob/master/基于TLS1.3的微信安全通信協定mmtls介紹.md

根據TLS1.3的描述,實際上有2種1-RTT的密鑰協商方式(1-RTT ECDHE、 1-RTT PSK)和4種0-RTT的密鑰協商方式(0-RTT PSK, 0-RTT ECDH, 0-RTT PSK-ECDHE, 0-RTT ECDH-ECDHE)

哪來的這麼多種 ?

1-RTT ECDHE 是一個完整的握手

0-RTT 隻是說資料的0-RTT性質,其餘的握手其實還是要協商的;

0-RTT PSK 就是 1-RTT PSK 上加了應用資料

0-RTT ECDH 是基于Client 端一直server 端靜态 pub_key的 同時攜帶應用資料

0-RTT PSK-ECDHE 是用PSK秘鑰加密攜帶應用資料,然後再重新用ECDHE 協商新的的共享秘鑰

0-RTT ECDH-ECDHE 是用靜态ECDH秘鑰加密攜帶應用資料,然後再重新用ECDHE 協商新的的共享秘鑰

在draft 13中去掉了 0-RTT ECDH

是以 ServerConfiguration 這握手消息隻能在12 以前的draft上能看到,且12的draft 畫了一個表格的key schedule 在13以後就沒有這個表格了。

RFC : draft 19

Because TLS 1.3 forbids renegotiation, if a server receives a

ClientHello at any other time, it MUST terminate the connection.

TLS 1.3 servers will need to perform this check first and

only attempt to negotiate TLS 1.3 if a “supported_version” extension

is present.

legacy_session_id Versions of TLS before TLS 1.3 supported a

“session resumption” feature which has been merged with Pre-Shared

Keys in this version (see Section 2.2).

看着意思 session reuse 來個更直接的 pre-master-key 給替換了

  1. Protocol Overview

    1).裡面有提到過 0-RTT data的實作要在有需求的時候才打開,且打開了以後要有能力處理重放的問題;

    2). PSK client 端可以帶多個PSKs,且同時也帶着key_share 以防server端不想用PSK, server端在回hello消息時可以同時回標明的PSK 和 key_share (用以采取PSK+ECDHE的協商方式)

PSK+ECDHE的協商方式 精華簡介:

Server 端處理 消息(包括擴充)的先後順序:

  1. 在tls_process_client_hello裡面 調用 tls_collect_extensions 收集擴充
  2. tls_early_post_process_client_hello  -----> ssl_get_prev_session()  {先處理 TLSEXT_IDX_psk_kex_modes 後處理 TLSEXT_IDX_psk 也就是說server收到client-hello 最先處理的兩個擴充是
               

PSK_mode (記錄 PSK 的模式) 和 PSK(條件沒問題後 設定 s->hit = 1),在處理key_share擴充時,如果s->hit && (s->ext.psk_kex_mode & TLSEXT_KEX_MODE_FLAG_KE_DHE) == 0 代表client端隻支援PSK only模式 是以就沒有必要再處理key_share了 隻要用PSK就好了

不然得話 就算s->hit 為真也要處理key_share 擴充,因為這時server 會選擇PSK and ECDHE模式, 在處理key_share中會把s->s3->peer_tmp 設定成client 的公鑰, 如果client端隻支援 PSK模式, 那麼就不會處理 key_share, 是以s->s3->peer_tmp 就是空的

在 tls_construct_stoc_key_share 中 s->s3->peer_tmp 是否為空決定server是否要發送key_share 當 peer_tmp為空時 就把ECDHE秘鑰置成空調用tls13_generate_handshake_secret(s, NULL, 0) 生成handshake secret

而如果 peer_tmp不空代表PSK and DHE模式, 則調用 ssl_derive 來生成 PSM 且生成ECDHE 和handshake secret}

如圖上标注所示, 當server端傳回PSK的同時 也 傳回了key_share, 說明是server端希望用PSK + ECDHE的模式握手.

tls_collect_extensions 如果收到了相應的extension 則會把相應的 present 設定為1; tls_parse_all_extensions 會調用(1) tls_parse_extension 處理擴充但是當該項不存在的時候該函數立即傳回 調用完(1) 周遊處理所有擴充後調用(2) ext_defs 表裡面所有 final 不為空的處理

如: thisexd->final(s, context, exts[i].present, al) 其中present 代表是否收到該擴充; 其中final_key_share 就會針對server端沒有發送 key_share 而進行的處理:

Client 端 在 tls_process_server_hello的時候 先去 檢查 TLSEXT_IDX_psk 擴充, 如果存在則設定 s->hit =1 ; 然後在 tls_parse_all_extensions 處理到key_share的時候 完全是對PSK 透明的(key_share處理在有無PSK的處理都是一樣的), 如果server端發送了 key_share則在 tls_parse_stoc_key_share

處理并調用 ssl_derive 生成handshake serect, 如果沒有key_share 擴充, 則相應的parse函數 肯定在 tls_parse_extension 調用時發現 present 為0而不去處理,但是final函數是要處理的, 是以在 final_key_share 函數裡面如下, 即 以ECDHE 為0 的input key 生成handshake secret. 這和server端的處理是一樣的;

if (!sent && !s->server && !tls13_generate_handshake_secret(s, NULL, 0)) {

error process…

}

4.1. Key Exchange Messages

The key exchange messages are used to exchange security capabilities between the client and server and to establish the traffic keys used to protect the handshake and data.

4.1.1. Cryptographic Negotiation

TLS cryptographic negotiation proceeds by the client offering the following four sets of options in its ClientHello:

* A list of cipher suites which indicates the AEAD algorithm/HKDF hash pairs which the client supports.
* A “supported_groups” (Section 4.2.6) extension which indicates the (EC)DHE groups which the client supports and a “key_share” (Section 4.2.7) extension which contains (EC)DHE shares for some or all of these groups.
* A “signature_algorithms” (Section 4.2.3) extension which indicates the signature algorithms which the client can accept.
* A “pre_shared_key” (Section 4.2.10) extension which contains a list of symmetric key identities known to the client and a “psk_key_exchange_modes” (Section 4.2.8) extension which indicates the key exchange modes that may be used with PSKs.
           

If the server does not select a PSK, then the first three of these options are entirely orthogonal: the server independently selects a cipher suite, an (EC)DHE group and key share for key establishment, and a signature algorithm/certificate pair to authenticate itself to the client. If there is no overlap between the received “supported_groups” and the groups supported by the server then the server MUST abort the handshake.

If the server selects a PSK, then it MUST also select a key establishment mode from the set indicated by client’s “psk_key_exchange_modes” extension (at present, PSK alone or with (EC)DHE). Note that if the PSK can be used without (EC)DHE then non-overlap in the “supported_groups” parameters need not be fatal, as it is in the non-PSK case discussed in the previous paragraph. /如果PSK 可以單純使用,這時就不要太在意DHE group的問題/

If the server selects an (EC)DHE group and the client did not offer a compatible “key_share” extension in the initial ClientHello, the server MUST respond with a HelloRetryRequest (Section 4.1.4) message. /假如client說他支援p-256,但是隻給出了25519的key_share,但server隻支援256 這時候要發送HRR/

4.1.2. Client Hello

兩種情況發送此消息,1是client 自發的,2是client發送的hello server覺得不妥 發送HRR響應後發送的,這種情況需要client再次發送的client-hello消息符合一定規矩;

已經協商了TLS1.3以後的任何時間收到了client-hello都是非法的;
  如果在1.3之前的版本上建立的連接配接上收到了client端 是TLS1.3的 重協商的hello 則一定要維持在以前的版本上建立連接配接;
           

TLS 1.3 ClientHello messages always contain extensions (minimally, “supported_versions”, or they will be interpreted as TLS 1.2 ClientHello messages) 想要協商成TLS1.3 則必須要包含support_version 擴充,不然就被當成TLS1.2協定;

After sending the ClientHello message, the client waits for a ServerHello or HelloRetryRequest message. The client may transmit early application data Section 2.3 while waiting for the next handshake message, if early data is in use. 用戶端發送了hello後在狀态上要等待server端的hello

但是在此期間可以發送early_data;

4.1.3. Server Hello

TLS1.3的 server在協商出了低版本的時候要 在hello的random字段的最後8個位元組做改動, TLS1.2的server should 加上最後8個自己的改動

TLS1.3的用戶端在收到了serve低版本協商時必須要檢查最後8個位元組, TLS1.2用戶端should 檢查;

該機制隻能在有limit的保護降級攻擊,因為對RSA沒意義, ECDHE 是 因為server對 kexch + 兩個random有簽名機制是以能防止降級

4.1.4. Hello Retry Request

As with ServerHello, a HelloRetryRequest MUST NOT contain any extensions that were not first offered by the client in its ClientHello, with the exception of optionally the “cookie” (see Section 4.2.2) extension

此消息裡面隻可發送cookie 和 key_share兩種擴充;

in its updated ClientHello, the client SHOULD NOT offer any pre-shared keys associated with a hash other than that of the selected cipher suite

client 第二次的hello消息隻能包含一個和select ciphersuit 相符的PSK

Upon receiving the ServerHello, clients MUST check that the cipher suite supplied in the ServerHello is the same as that in the HelloRetryRequest and otherwise abort the handshake with an “illegal_parameter” alert.

HRR 決定了一個固定的cipher 如果一方不遵守則連接配接關閉;

4.2.Extensions

enum {

server_name(0),

max_fragment_length(1),

status_request(5),

supported_groups(10),

signature_algorithms(13),

use_srtp(14),

heartbeat(15),

application_layer_protocol_negotiation(16),

signed_certificate_timestamp(18),

client_certificate_type(19),

server_certificate_type(20)

padding(21),

key_share(40),

pre_shared_key(41),

early_data(42),

supported_versions(43),

cookie(44),

psk_key_exchange_modes(45),

certificate_authorities(47),

oid_filters(48),

post_handshake_auth(49),

(65535)

} ExtensionType;

擴充:

client hello 裡面的支援版本擴充在draf 期間是 0x7f | draf version

Implementations MUST NOT send extension responses if the remote

endpoint did not send the corresponding extension requests, with the

exception of the “cookie” extension in HelloRetryRequest. Upon

receiving such an extension, an endpoint MUST abort the handshake

with an “unsupported_extension” alert.

除了cookie 對端未請求,本端不能給任何響應

Extensions are generally structured in a request/response fashion, though some extensions are just indications with no corresponding response. 都是有問有答的方式的,即使答案可能是不支援;

The server MAY also send unsolicited extensions in the NewSessionTicket, though the client does not respond directly to these.

“pre_shared_key” Section 4.2.10 which MUST be the last extension in the ClientHello. 最後一項的擴充必須為 pre_shared_key;

In TLS 1.3, unlike TLS 1.2, extensions are renegotiated with each handshake even when in resumption-PSK mode. However, 0-RTT parameters are those negotiated in the previous handshake; mismatches may require rejecting 0-RTT (see Section 4.2.9).

不想1.2那樣即使是連接配接複用也可以自行協商新的擴充, TLS1.3 0-RTT的協商方式攜帶的擴充必須同上次建立的時候才用的,不然0-RTT 協商請求被拒;

4.2.1. Supported Versions

support version 裡面可以不包含0304,但是隻要client hello裡面攜帶了 support version 擴充,則 legc_version 就不要去看了 隻看該擴充就好了。

The server MUST NOT send the “supported_versions” extension. The server’s selected version is contained in the ServerHello.version field as in previous versions of TLS.

server端標明的cipher就在普通的version 字段裡面;

4.2.2. Cookie

When sending a HelloRetryRequest, the server MAY provide a “cookie” extension to the client (this is an exception to the usual rule that the only extensions that may be sent are those that appear in the ClientHello). When sending the new ClientHello, the client MUST copy the contents of the extension received in the HelloRetryRequest into a “cookie” extension in the new ClientHello. Clients MUST NOT use cookies in subsequent connections.

cookie 僅僅是server端的一個小伎倆,我發送一個cookie 在HRR裡面,你回來的時候一定給我帶回來,不然我就認為你是非法的client通路;

4.2.3. Signature Algorithms

enum {

rsa_pkcs1_sha1(0x0201),

rsa_pkcs1_sha256(0x0401),

rsa_pkcs1_sha384(0x0501),

rsa_pkcs1_sha512(0x0601),

/* ECDSA algorithms */
   ecdsa_secp256r1_sha256(0x0403),
   ecdsa_secp384r1_sha384(0x0503),
   ecdsa_secp521r1_sha512(0x0603),

   /* RSASSA-PSS algorithms */
   rsa_pss_sha256(0x0804),
   rsa_pss_sha384(0x0805),
   rsa_pss_sha512(0x0806),

   /* EdDSA algorithms */
   ed25519(0x0807),
   ed448(0x0808),

   /* Reserved Code Points */
   private_use(0xFE00..0xFFFF),
   (0xFFFF)
           

} SignatureScheme;

4.2.4. Certificate Authorities

The “certificate_authorities” extension is used to indicate the

certificate authorities which an endpoint supports and which SHOULD

be used by the receiving endpoint to guide certificate selection.

這個就是告訴對方選擇證書的條件的

4.2.5. Post-Handshake Client Authentication 隻能出現在client-hello 裡面,用以表面client 可以接受 post-handshake client auth , 如果client端從未标明支援秋後算賬,則server端不能發送 post-hs certificateReq

The “post_handshake_auth” extension is used to indicate that a client is willing to perform post-handshake authentication Section 4.6.2. Servers MUST not send a post-handshake CertificateRequest to clients which do not offer this extension.

The “extension_data” field of the “post_handshake_auth” extension is zero length.

4.2.6. Negotiated Groups — 用于指定簽名曲線的

Note: In versions of TLS prior to TLS 1.3, this extension was named

“elliptic_curves” and only contained elliptic curve groups. See

[RFC4492] and [RFC7919]. This extension was also used to negotiate

ECDSA curves

4.2.7. Key Share

The “key_share” extension contains the endpoint’s cryptographic

parameters.

struct {

NamedGroup group;

opaque key_exchange<1…2^16-1>;

} KeyShareEntry;

group The named group for the key being exchanged. Finite Field

Diffie-Hellman [DH] parameters are described in Section 4.2.5.1;

Elliptic Curve Diffie-Hellman parameters are described in

Section 4.2.5.2.

key_exchange Key exchange information. The contents of this field

are determined by the specified group and its corresponding

definition.

The “extension_data” field of this extension contains a “KeyShare”

value:

struct {

select (Handshake.msg_type) {

case client_hello:

KeyShareEntry client_shares<0…2^16-1>; 《Client 端主動的第一次就發送了相關資訊》

case hello_retry_request: 《client 沒有發送足夠的資訊,server 發送 hello_retry_request 讓client端一定要給出相關資訊》

NamedGroup selected_group;

case server_hello:

KeyShareEntry server_share;

};

} KeyShare;

The table below indicates the messages where a given extension may

appear, using the following notation: CH (ClientHello), SH

(ServerHello), EE (EncryptedExtensions), CT (Certificate), CR

(CertificateRequest), NST (NewSessionTicket) and HRR

(HelloRetryRequest). If an implementation receives an extension

which it recognizes and which is not specified for the message in

which it appears it MUST abort the handshake with an

“illegal_parameter” alert.

±-------------------------------------------------±------------+

| Extension | TLS 1.3 |

±-------------------------------------------------±------------+

| server_name [RFC6066] | CH, EE |

| | |

| max_fragment_length [RFC6066] | CH, EE |

| | |

| client_certificate_url [RFC6066] | CH, EE |

| | |

| status_request [RFC6066] | CH, CR, CT |

| | |

| user_mapping [RFC4681] | CH, EE |

| | |

| cert_type [RFC6091] | CH, EE |

| | |

| supported_groups [RFC7919] | CH, EE |

| | |

| signature_algorithms [RFC5246] | CH, CR |

| | |

| use_srtp [RFC5764] | CH, EE |

| | |

| heartbeat [RFC6520] | CH, EE |

| | |

| application_layer_protocol_negotiation [RFC7301] | CH, EE |

| | |

| signed_certificate_timestamp [RFC6962] | CH, CR, CT |

| | |

| client_certificate_type [RFC7250] | CH, EE |

| | |

| server_certificate_type [RFC7250] | CH, CT |

| | |

| padding [RFC7685] | CH |

| | |

| key_share [[this document]] | CH, SH, HRR |

| | |

| pre_shared_key [[this document]] | CH, SH |

| | |

| psk_key_exchange_modes [[this document]] | CH |

| | |

| early_data [[this document]] | CH, EE, NST |

| | |

| cookie [[this document]] | CH, HRR |

| | |

| supported_versions [[this document]] | CH |

| | |

| certificate_authorities [[this document]] | CH, CR |

| | |

| oid_filters [[this document]] | CR |

±-------------------------------------------------±------------+

Latest draft:

Extension

TLS 1.3

server_name [RFC6066]

CH, EE

max_fragment_length [RFC6066]

CH, EE

status_request [RFC6066]

CH, CR, CT

supported_groups [RFC7919]

CH, EE

signature_algorithms [RFC5246]

CH, CR

use_srtp [RFC5764]

CH, EE

heartbeat [RFC6520]

CH, EE

application_layer_protocol_negotiation [RFC7301]

CH, EE

signed_certificate_timestamp [RFC6962]

CH, CR, CT

client_certificate_type [RFC7250]

CH, EE

server_certificate_type [RFC7250]

CH, CT

padding [RFC7685]

CH

key_share [[this document]]

CH, SH, HRR

pre_shared_key [[this document]]

CH, SH

psk_key_exchange_modes [[this document]]

CH

early_data [[this document]]

CH, EE, NST

cookie [[this document]]

CH, HRR

supported_versions [[this document]]

CH

certificate_authorities [[this document]]

CH, CR

oid_filters [[this document]]

CR

post_handshake_auth [[this document]]

CH

4.2.8. Pre-Shared Key Exchange Modes

If clients offer “pre_shared_key” without a “psk_key_exchange_modes” extension, servers MUST abort the handshake.

4.2.9. Early Data Indication

client 端在PSK的模式下可以在第一個包裡面就傳遞應用資料給server, 這個應用資料使用建立PSK時的共享秘鑰加密的

When multiple extensions of different types are present, the

extensions MAY appear in any order, with the exception of=======================================>除了最後一個擴充必須是PSK以外, 其他所有的擴充不限定順序;

“pre_shared_key” Section 4.2.8 which MUST be the last extension in

the ClientHello.

draft-19說的很隐晦, 不如draft-12說的明了

draft-19 :

struct {} Empty;

struct {

select (Handshake.msg_type) {

case new_session_ticket: uint32 max_early_data_size;

case client_hello: Empty;

case encrypted_extensions: Empty; <=========此處就是下面 server 三種處理方法中最後一種接受 early_data時發送的

};

} EarlyDataIndication;

draft-12:

The “extension_data” field of this extension contains an

“EarlyDataIndication” value:

struct {
      select (Role) {
          case client:
              opaque configuration_id<1..2^16-1>;
              CipherSuite cipher_suite;
              Extension extensions<0..2^16-1>;
              opaque context<0..255>;

          case server:
             struct {};                 <=======
      }
  } EarlyDataIndication;
           

首先要明确一點,early_data是個擴充,而該擴充的資料部分是 EarlyDataIndication, 而在RFC上好多的握手消息流圖中提到early_data 指的是client-hello 或者 server-hello裡面的 擴充,而根據以上EarlyDataIndication 定義來看,client or server 隻是發送一個Empty 值的 early_data擴充來标明

本端能否支援0-RTT 應用資料的功能;eary_data 擴充還可以出現在 new session tick消息裡,而且new session tick目前隻定義了這麼一個擴充,用max_early_data_size 來标明可以發送的最大資料;

The parameters for the 0-RTT data (symmetric cipher suite, ALPN protocol, etc.) are the same as those which were negotiated in the connection which established the PSK. The PSK used to encrypt the early data MUST be the first PSK listed in the client’s “pre_shared_key” extension.

After receiving the server’s Finished message, if the server has accepted early data, an EndOfEarlyData message will be sent to indicate the key change. This message will be encrypted with the 0-RTT traffic keys.

client端發送的earyly data 和 endofealydata 都是用 client_early_traffic_secre 加密的,但是在endofearlydata之後的資料都是用此次協商出來的秘鑰來加密的;

A server which receives an “early_data” extension MUST behave in one of three ways: 第一種是欲拒還迎(就算不能使用 PSK也要為client節省RTT), 第二種是忠貞不屈的(拒絕走PSK而且要求client重連且别再玩這一套),第三種 則是你來我往,情投意合的

* Ignore the extension and return a regular 1-RTT response. The server then ignores early data by attempting to decrypt received records in the handshake traffic keys until it is able to receive the client’s second flight and complete an ordinary 1-RTT handshake, skipping records that fail to decrypt, up to the configured max_early_data_size.

	* 這個有點難辦啊,在正常的1-RTT握手完成之前都是不去解密record,即使1-RTT握手完成之後 如果early_data 還在不斷到達 隻是server 解密失敗的最大容忍程度是max_earyly_data_size  ?
	* 上面的了解可能不對,不是不去解密record,而是用 handshake traffic key 去解密, 這樣解密固然是失敗的(因為early_data是用 client_early_traffic_key加密的),當收到了client 第二批握手消息時 就不會出現解密失敗了(因為client finish 是能被正常解密的),是以一切照舊走下去,就當沒有early_data這回事,但是如果client 第二批握手消息遲遲不來,不能任由對端發送大量無法解密的record,當收到數量超過max_early_data_size的時候 應該果斷 斷開連接配接;
* Request that the client send another ClientHello by responding with a HelloRetryRequest. A client MUST NOT include the “early_data” extension in its followup ClientHello. The server then ignores early data by skipping all records with external content type of “application_data” (indicating that they are encrypted).   直接告訴client 重連且重連的hello裡面 不可再有 early_data extension;
* Return its own extension in EncryptedExtensions, indicating that it intends to process the early data. It is not possible for the server to accept only a subset of the early data messages. Even though the server sends a message accepting early data, the actual early data itself may already be in flight by the time the server generates this message
           

當兩邊協商好可以接受eary_data 資料後 當client 收到了對端server端傳來的Finish消息後 必須馬上發送 EndOfEarlyData 用以告訴server 後續資料用traffic key 加密而不是用PSK

4.2.10. Pre-Shared Key Extension

The “pre_shared_key” extension is used to indicate the identity of

the pre-shared key to be used with a given handshake in association

with PSK key establishment.

Client 端可能發送多個可用的PSK,但是early_data 是被第一個PSK加密的, server端如果接受PSK 則回複only 一個PSK 代表此次0-RTT 協商的PSK

If the server supplies an “early_data” extension, the client MUST verify that the server’s selected_identity is 0. If any other value is returned, the client MUST abort the handshake with an “illegal_parameter” alert.

server 也可發送early_data ?

The “pre_shared_key” extension is used to indicate the identity of the pre-shared key to be used with a given handshake in association with PSK key establishment.

The “extension_data” field of this extension contains a “PreSharedKeyExtension” value:

struct {

opaque identity<1…2^16-1>;

uint32 obfuscated_ticket_age;

} PskIdentity;

opaque PskBinderEntry<32…255>;

struct {

select (Handshake.msg_type) {

case client_hello:

PskIdentity identities<7…2^16-1>;

PskBinderEntry binders<33…2^16-1>;

case server_hello:

uint16 selected_identity;

};

} PreSharedKeyExtension;

identity

A label for a key. For instance, a ticket defined in Appendix B.3.4, or a label for a pre-shared key established externally. ------->client發回來的PSK的 ID 就是 server 通過new session tick 發過去的tick.

obfuscated_ticket_age

An obfuscated version of the age of the key. Section 4.2.10.1 describes how to form this value for identities established via the NewSessionTicket message. For identities established externally an obfuscated_ticket_age of 0 SHOULD be used, and servers MUST ignore the value.

identities

A list of the identities that the client is willing to negotiate with the server. If sent alongside the “early_data” extension (see Section 4.2.9), the first identity is the one used for 0-RTT data.

binders

A series of HMAC values, one for each PSK offered in the “pre_shared_keys” extension and in the same order, computed as described below.

selected_identity

The server’s chosen identity expressed as a (0-based) index into the identities in the client’s list.

Appendix B.3.4 的内容如下:

struct {

uint32 ticket_lifetime;

uint32 ticket_age_add;

opaque ticket<1…2^16-1>;

Extension extensions<0…2^16-2>;

} NewSessionTicket;

This extension MUST be the last extension in the ClientHello (this facilitates implementation as described below). Servers MUST check that it is the last extension and otherwise fail the handshake with an “illegal_parameter” al

因為 裡面涉及到psk_binder 加密副本的問題,binder 會加密到所有的client-hello消息 除了最後一項binder(s) (ClientHello up to and including the PreSharedKeyExtension.identities field, 這也是為什麼RFC擴充的規定最後一項是PSK)

4.2.10.2. PSK Binder

The PSK binder value forms a binding between a PSK and the current handshake, as well as between the handshake in which the PSK was generated (if via a NewSessionTicket message) and the handshake where it was used. Each entry in the binders list is computed as an HMAC over a transcript hash (see Section 4.4.1) containing a partial ClientHello up to and including the PreSharedKeyExtension.identities field. That is, it includes all of the ClientHello but not the binders list itself. The length fields for the message (including the overall length, the length of the extensions block, and the length of the “pre_shared_key” extension) are all set as if binders of the correct lengths were present.

The PskBinderEntry is computed in the same way as the Finished message (Section 4.4.4) but with the BaseKey being the binder_key derived via the key schedule from the corresponding PSK which is being offered (see Section 7.1).

If the handshake includes a HelloRetryRequest, the initial ClientHello and HelloRetryRequest are included in the transcript along with the new ClientHello. For instance, if the client sends ClientHello1, its binder will be computed over:

Transcript-Hash(ClientHello1[truncated])

If the server responds with HelloRetryRequest, and the client then sends ClientHello2, its binder will be computed over:

Transcript-Hash(ClientHello1,

HelloRetryRequest,

ClientHello2[truncated])

The full ClientHello1 is included in all other handshake hash computations. Note that in the first flight, ClientHello1[truncated] is hashed directly, but in the second flight, ClientHello1 is hashed and then reinjected as a “handshake_hash” message, as described in Section 4.4.1.

Section 4.4.4:

finished_key =

HKDF-Expand-Label(BaseKey, “finished”, “”, Hash.length)

Structure of this message:

struct {

opaque verify_data[Hash.length];

} Finished;

The verify_data value is computed as follows:

verify_data =

HMAC(finished_key,

Transcript-Hash(Handshake Context,

Certificate*, CertificateVerify*))

是以 PSK binder 的MAC應該是這樣計算的: 和計算finish的MAC是一樣的隻不過 用binder_key 而不是 Base Key 在4.4中提到的 三種類型 作為finish key的 派生

finished_key =

HKDF-Expand-Label(binder_key, “res binder”, “”, Hash.length)

Structure of this message:

struct {

opaque verify_data[Hash.length];

} PSK binder;

The verify_data value is computed as follows:

verify_data = HMAC( finished_key, Transcript-Hash(ClientHello1[truncated]))

4.3. Server Parameters

The next two messages from the server, EncryptedExtensions and

CertificateRequest, contain information from the server that

determines the rest of the handshake. These messages are encrypted

with keys derived from the server_handshake_traffic_secret.

4.3.1. Encrypted Extensions

In all handshakes, the server MUST send the EncryptedExtensions

message immediately after the ServerHello message. This is the first

message that is encrypted under keys derived from the

server_handshake_traffic_secret.

4.4. Authentication Messages

As discussed in Section 2, TLS generally uses a common set of

messages for authentication, key confirmation, and handshake

integrity: Certificate, CertificateVerify, and Finished.

These three messages are always sent as the last messages

in their handshake flight. The Certificate and CertificateVerify

messages are only sent under certain circumstances, as defined below.

The Finished message is always sent as part of the Authentication

block.

These messages are encrypted under keys derived from

[sender]_handshake_traffic_secret.

The computations for the Authentication messages all uniformly take the following inputs: 擦得,說的不是很明白,剛開始以為是所有三個驗證消息都有如下步驟,原來隻是統一羅列到前面,每個都對應一個消息而已:

* The certificate and signing key to be used.   ============> Certificate
* A Handshake Context consisting of the set of messages to be included in the transcript hash.  ====> CertificateVerify
* A base key to be used to compute a MAC key.   ====>Finish
           

Based on these inputs, the messages then contain:

CertificateThe certificate to be used for authentication, and any supporting certificates in the chain. Note that certificate-based client authentication is not available in the 0-RTT case.CertificateVerifyA signature over the value Transcript-Hash(Handshake Context, Certificate)FinishedA MAC over the value Transcript-Hash(Handshake Context, Certificate, CertificateVerify) using a MAC key derived from the base key.

The following table defines the Handshake Context and MAC Base Key for each scenario: ====>Finish 的時候用到了 tls_construct_finished —> tls13_final_finish_mac (函數中将會用server_finished_secret or client_finished_secret, 這個值是在tls13_change_cipher_state 裡面生成的)

Mode

Handshake Context

Base Key

Server

ClientHello … later of EncryptedExtensions/CertificateRequest

server_handshake_traffic_secret

Client

ClientHello … later of server Finished/EndOfEarlyData

client_handshake_traffic_secret

Post-Handshake

ClientHello … client Finished + CertificateRequest

client_application_traffic_secret_N

從上面可以看出 計算hash的 内容也是到發送本個finish之前所能見到的所有資訊 都包含在内;

* 4.4.1. The Transcript Hash
* 4.4.2. Certificate
* 4.4.3. Certificate Verify
* 4.4.4. Finished
           

4.4.4. Finished

The key used to compute the finished message is computed from the Base key defined in Section 4.4 using HKDF (see Section 7.1). Specifically:

finished_key =

HKDF-Expand-Label(BaseKey, “finished”, “”, Hash.length)

Structure of this message:

struct {

opaque verify_data[Hash.length];

} Finished;

The verify_data value is computed as follows:

verify_data =

HMAC(finished_key, =====> HMAC 可以用僞代碼描述為: hash(opadkey || hash (ipadkey || message)) ipadkey <>ipad ^ key opadkey <=> opad ^ key ipad 是0x36循環到hash長度, opad是0x5c 循環到hash長度;

Transcript-Hash(Handshake Context,

Certificate*, CertificateVerify*))

  • Only included if present.

    4.5. End of Early Data

    If the server sent an “early_data” extension, the client MUST send an EndOfEarlyData after receiving the server Finished. This indicates that all 0-RTT application_data messages, if any, have been transmitted and that the following records are protected under handshake traffic keys. Servers MUST NOT send this message and clients receiving it MUST terminate the connection with an “unexpected_message” alert. This message is encrypted under keys derived from the client_early_traffic_secret.

    可以了解為client都收到了對端的finish資訊了 代表 兩邊可以協商出新的app traffic key了 就沒有必要使用PSK建立的通道了(因為沒有前向安全性,是以不宜長用,一經有新的秘鑰立刻 告訴server端 early 通道從此不用)

    4.6. Post-Handshake Messages post 有點秋後算賬的意思 :)

TLS also allows other messages to be sent after the main handshake.

These messages use a handshake content type and are encrypted under

the appropriate application traffic key.

4.6.1. New Session Ticket Message 該消息是5077 位before 1.3 TLS 協定提出的 new session ticket

At any time after the server has received the client Finished

message, it MAY send a NewSessionTicket message. This message

creates a pre-shared key (PSK) binding between the ticket value and

the resumption master secret.

pre-shared key 是之前的連接配接已經共享的key, 不是目前連接配接的pre master 的pre

struct {

uint32 ticket_lifetime;

uint32 ticket_age_add;

opaque ticket<1…2^16-1>;

Extension extensions<0…2^16-2>; 《============隻能說TLS1.3 挺進階,擴充不僅僅使用在hello消息裡面-----在文檔裡面搜尋 “”Extension extensions“”能找到有7個消息用到了擴充》

} NewSessionTicket;

The sole extension currently defined for NewSessionTicket is “early_data”, indicating that the ticket may be used to send 0-RTT data (Section 4.2.9)). It contains the following value:

其中ticket 是有以下指定規範構造的: Section 4 of [RFC5077] describes a recommended ticket construction mechanism as following:

The ticket is structured as follows:

struct {
      opaque key_name[16];      《======明文    發送的時候 用 ext.tick_key_name 填充,收到的時候用 ext.tick_key_name 來比對是否相當(因為在tick中是以明文純在的是以可以實作快速比較)
      opaque iv[16];                    <======明文
      opaque encrypted_state<0..2^16-1>;    --------->使用 StatePlaintext in the above(在下面)。使用ext.tick_aes_key 加密 
      opaque mac[32];-------------->tick_hmac_key 計算mac;
  } ticket;
           

/插播Openssl 關于session tick的 key的資訊的生成:

(RAND_bytes(ret->ext.tick_key_name,

sizeof(ret->ext.tick_key_name)) <= 0)

|| (RAND_bytes(ret->ext.tick_hmac_key,

sizeof(ret->ext.tick_hmac_key)) <= 0)

|| (RAND_bytes(ret->ext.tick_aes_key,

sizeof(ret->ext.tick_aes_key)) <= 0)

因這些key 隻有server 自己知道, client 拿到tick時對client來講就是一團漿糊,但是server就能識别這個資訊,這時有個問題,如果長期使用這個key,可能會破壞前向安全性,是以在openssl裡面實作是在

SSL_CTX_new 的時候使用了上述代碼來随機生成(如果在沒有明确指定的時候);

/

Here, key_name serves to identify a particular set of keys used to

protect the ticket. It enables the server to easily recognize

tickets it has issued. The key_name should be randomly generated to

avoid collisions between servers. One possibility is to generate new

random keys and key_name every time the server is started.

The actual state information in encrypted_state is encrypted using

128-bit AES in CBC mode with the given IV. The Message

Authentication Code (MAC) is calculated using HMAC-SHA-256 over

key_name (16 octets) and IV (16 octets), followed by the length of

the encrypted_state field (2 octets) and its contents (variable

length).

struct {

ProtocolVersion protocol_version;

CipherSuite cipher_suite;

CompressionMethod compression_method;

opaque master_secret[48];

ClientIdentity client_identity;

uint32 timestamp;

} StatePlaintext;

max_early_data_size

The maximum amount of 0-RTT data that the client is allowed to send when using this ticket, in bytes. Only Application Data payload (i.e., plaintext but not padding or the inner content type byte) is counted. A server receiving more than max_early_data_size bytes of 0-RTT data SHOULD terminate the connection with an “unexpected_message” alert.

4.6.2. Post-Handshake Authentication

The server is permitted to request client authentication at any time

after the handshake has completed by sending a CertificateRequest

message. The client SHOULD respond with the appropriate

Authentication messages. If the client chooses to authenticate, it

MUST send Certificate, CertificateVerify, and Finished.

該消息依賴于4.2.5 如下:

The “post_handshake_auth” extension is used to indicate that a client is willing to perform post-handshake authentication Section 4.6.2. Servers MUST not send a post-handshake CertificateRequest to clients which do not offer this extension.

也就是說作為client 完全有權利拒絕提供證書給server證書如果隻收到了 post-handshake authentication, 而server沒有在hello擴充裡面發送post_handshake_auth ;

If the client chooses to authenticate, it MUST send Certificate, CertificateVerify, and Finished. If it declines, it MUST send a Certificate message containing no certificates followed by Finished. All of the client’s messages for a given response MUST appear consecutively on the wire with no intervening messages of other types.

這個說如果server端要求秋後算賬 則client端必須發送 或者為空的證書,或者連續發送 cert certverify, finish 三個消息 中間不得有任何的其他的消息; 但是問題來了 這些驗證消息 怎樣計算mac 加密 , hash 資料是什麼 ?

4.6.3. Key and IV Update

The KeyUpdate handshake message is used to indicate that the sender

is updating its sending cryptographic keys. This message can be sent

by either peer after it has sent a Finished message. Implementations

that receive a KeyUpdate message prior to receiving a Finished

message MUST terminate the connection with an “unexpected_message”

alert. After sending a KeyUpdate message, the sender SHALL send all

its traffic using the next generation of keys, computed as described

in Section 7.2. Upon receiving a KeyUpdate, the receiver MUST update

its receiving keys.

任意一端都有權要求更換秘鑰(這可能是出于 更新使用時間較長的秘鑰的目的),而更換秘鑰需要生成下一代的秘鑰,下一代的秘鑰的生成則是簡單的由上一代traffic_secret 派生出(不需要從early_secret,到handshake_secret, master_secret 一步一步的生成中間key)

Once the handshake is complete, it is possible for either side to update its sending traffic keys using the KeyUpdate handshake message defined in Section 4.6.3. The next generation of traffic keys is computed by generating client_/server_traffic_secret_N+1 from client_/server_traffic_secret_N as described in this section then re-deriving the traffic keys as described in Section 7.3.

The next-generation traffic_secret is computed as:

traffic_secret_N+1 = HKDF-Expand-Label(

traffic_secret_N,

“application traffic secret”, “”, Hash.length)

Once client/server_traffic_secret_N+1 and its associated traffic keys have been computed, implementations SHOULD delete client_/server_traffic_secret_N and its associated traffic keys.

5.1. Record Layer

The record layer fragments information blocks into TLSPlaintext records carrying data in chunks of 2^14 bytes or less. Message boundaries are handled differently depending on the underlying ContentType. Any future content types MUST specify appropriate rules. Note that these rules are stricter than what was enforced in TLS 1.2.

Handshake messages MAY be coalesced into a single TLSPlaintext record or fragmented across several records, provided that: 握手消息 如果能分段則在,分段之間不能有其他record資訊,

* Handshake messages MUST NOT be interleaved with other record types. That is, if a handshake message is split over two or more records, there MUST NOT be any other records between them.
* Handshake messages MUST NOT span key changes. Implementations MUST verify that all messages immediately preceding a key change align with a record boundary; if not, then they MUST terminate the connection with an “unexpected_message” alert. Because the ClientHello, EndOfEarlyData, ServerHello, Finished, and KeyUpdate messages can immediately precede a key change, implementations MUST send these messages in alignment with a record boundary.    在可以觸發更改key的消息裡面 必然不能放到兩個record裡面;
           

enum {

invalid(0),

alert(21),

handshake(22),

application_data(23),

(255)

} ContentType;

struct {

ContentType type;

ProtocolVersion legacy_record_version;

uint16 length;

opaque fragment[TLSPlaintext.length];

} TLSPlaintext;

5.2. Record Payload Protection TLS1.3 在相容老版本的考慮下加入了inner 概念,最外層的lengh是 cipher 加密的所有長度,而内層先是純明文的長度(算法決定與密文長度相同) 然後接着一個type

The record protection functions translate a TLSPlaintext structure into a TLSCiphertext. The deprotection functions reverse the process. In TLS 1.3, as opposed to previous versions of TLS, all ciphers are modeled as “Authenticated Encryption with Additional Data” (AEAD) [RFC5116]. AEAD functions provide an unified encryption and authentication operation which turns plaintext into authenticated ciphertext and back again. Each encrypted record consists of a plaintext header followed by an encrypted body, which itself contains a type and optional padding.

以上這段文字大概說的是 record 保護程式在加密的時候負責把 上面的 TLSPlaintext 轉化成下面的 格式, 而在解密的時候 還要把 從密文裡面 抽取資料 組裝成 TLSPlaintext的格式 來傳給 握手狀态機(這樣做的的目的就是為了讓上面的狀态機 不感覺下面的加解密 ?)

在解密的時候 要想知道明文的長度 需要 配合 5.4. Record Padding 裡面講到的怎樣從最後一直向前查到非零為止 這樣從ciphertext.len - 零長度 - 1 才能知道實際的資料長度;

struct {

opaque content[TLSPlaintext.length];

ContentType type;

uint8 zeros[length_of_padding];

} TLSInnerPlaintext;

struct {

ContentType opaque_type = 23;

uint16 length;

opaque encrypted_record[length];

} TLSCiphertext;

content

The byte encoding of a handshake or an alert message, or the raw bytes of the application’s data to send.

type

The content type of the record.

zeros

An arbitrary-length run of zero-valued bytes may appear in the cleartext after the type field. This provides an opportunity for senders to pad any TLS record by a chosen amount as long as the total stays within record size limits. See Section 5.4 for more details.

opaque_type

The outer opaque_type field of a TLSCiphertext record is always set to the value 23 (application_data) for outward compatibility with middleboxes accustomed to parsing previous versions of TLS. The actual content type of the record is found in TLSInnerPlaintext.type after decryption.

legacy_record_version

The legacy_record_version field is always 0x0301. TLS 1.3 TLSCiphertexts are not generated until after TLS 1.3 has been negotiated, so there are no historical compatibility concerns where other values might be received. Implementations MAY verify that the legacy_record_version field is 0x0301 and abort the connection if it is not. Note that the handshake protocol including the ClientHello and ServerHello messages authenticates the protocol version, so this value is redundant.

length

The length (in bytes) of the following TLSCiphertext.encrypted_record, which is the sum of the lengths of the content and the padding, plus one for the inner content type, plus any expansion added by the AEAD algorithm. The length MUST NOT exceed 2^14 + 256 bytes. An endpoint that receives a record that exceeds this length MUST terminate the connection with a “record_overflow” alert.

encrypted_record

The AEAD-encrypted form of the serialized TLSInnerPlaintext structure.

  1. Cryptographic Computations

    Note that because the handshake transcript includes the random values in the Hello messages, any given handshake will have different traffic secrets, even if the same input secrets are used, as is the case when the same PSK is used for multiple connections

    從此可以看出PSK 丫的頂多就是個TLS1.2裡面的master key, 1.2裡面 reuse的時候就是拿到master key + 兩個random 就會重新生成對稱秘鑰;

    For the computation of the binder_secret, the label is “external psk binder key” for external PSKs (those provisioned outside of TLS) and “resumption psk binder key” for resumption PSKs (those provisioned as the resumption master secret of a previous handshake)

    8.2. Mandatory-to-Implement Extensions

In the absence of an application profile standard specifying otherwise, a TLS-compliant application MUST implement the following TLS extensions:

* Supported Versions (“supported_versions”; Section 4.2.1)
* Cookie (“cookie”; Section 4.2.2)
* Signature Algorithms (“signature_algorithms”; Section 4.2.3)
* Negotiated Groups (“supported_groups”; Section 4.2.5)
* Key Share (“key_share”; Section 4.2.6)
* Server Name Indication (“server_name”; Section 3 of [RFC6066])
           

All implementations MUST send and use these extensions when offering applicable features:

* “supported_versions” is REQUIRED for all ClientHello messages.
* “signature_algorithms” is REQUIRED for certificate authentication.
* “supported_groups” and “key_share” are REQUIRED for DHE or ECDHE key exchange.
* “pre_shared_key” is REQUIRED for PSK key agreement.
           

A client is considered to be attempting to negotiate using this specification if the ClientHello contains a “supported_versions” extension with a version indicating TLS 1.3. Such a ClientHello message MUST meet the following requirements:

* If not containing a “pre_shared_key” extension, it MUST contain both a “signature_algorithms” extension and a “supported_groups” extension.
* If containing a “supported_groups” extension, it MUST also contain a “key_share” extension, and vice versa. An empty KeyShare.client_shares vector is permitted.
           

Servers receiving a ClientHello which does not conform to these requirements MUST abort the handshake with a “missing_extension” alert.

Additionally, all implementations MUST support use of the “server_name” extension with applications capable of using it. Servers MAY require clients to send a valid “server_name” extension. Servers requiring this extension SHOULD respond to a ClientHello lacking a “server_name” extension by terminating the connection with a “missing_extension” alert.

繼續閱讀