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 给替换了
PSK+ECDHE的协商方式 精华简介:
Server 端处理 消息(包括扩展)的先后顺序:
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 {
/* RSASSA-PKCS1-v1_5 algorithms */
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;
/*The mutually supported group the server intends to
negotiate and is requesting a retried ClientHello/KeyShare for.*/
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*))
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; /* application_data /
ProtocolVersion legacy_record_version = 0x0301; / TLS v1.x */
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.
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.