7. Cryptographic Computations (密码学计算)
TLS handshake 建立一个或多个 input secret, 这些 secret 被组合后创建实际使用的 keying material. key derivation process 同时纳入 input secret 和 handshake transcript. 由于 handshake transcript 包含 Hello 消息中的随机值, 即使使用相同 input secret, 任何给定 handshake 都会拥有不同 traffic secret. 同一 PSK 用于多个 connection 时也是如此.
7.1. Key Schedule (密钥调度)
key derivation process 使用 HKDF [RFC5869] 定义的 HKDF-Extract 和 HKDF-Expand, 以及如下函数:
HKDF-Expand-Label(Secret, Label, Context, Length) =
HKDF-Expand(Secret, HkdfLabel, Length)
Where HkdfLabel is specified as:
struct {
uint16 length = Length;
opaque label<7..255> = "tls13 " + Label;
opaque context<0..255> = Context;
} HkdfLabel;
Derive-Secret(Secret, Label, Messages) =
HKDF-Expand-Label(Secret, Label,
Transcript-Hash(Messages), Hash.length)
Transcript-Hash 和 HKDF 使用的 Hash function 是 cipher suite hash algorithm. Messages 是指定 handshake message 的串接, 包含 handshake message type 和 length 字段, 但不包含 record layer header. 本文档中指定的 label 都是 ASCII string, 且不包含尾随 NUL byte.
TLS 1.3 本版本中有两个 input secret:
- PSK: 外部建立的 pre-shared key, 或从先前 connection 的 resumption_master_secret 派生.
- (EC)DHE shared secret (第 7.4 节).
key schedule 通过 HKDF-Extract 加入新的 secret. 一般模式是 Salt 使用当前 secret state, Input Keying Material (IKM) 使用要加入的新 secret. 如果某个 secret 不可用, 则使用由 Hash.length 个零 byte 构成的 0 值, 这并不表示跳过某一轮.
生成 binder_key 时, external PSK 使用 label "ext binder", resumption PSK 使用 label "res binder". 不同 label 防止一种 PSK 类型被替换为另一种.
可能存在多个 Early Secret 值, 取决于 server 最终选择哪个 PSK. client 需要为每个可能 PSK 计算一个 Early Secret. 如果没有选择 PSK, 则计算对应 zero PSK 的 Early Secret.
从给定 secret 派生的所有值计算完成后, 该 secret SHOULD 被擦除.
7.2. Updating Traffic Secrets (更新流量 Secret)
handshake 完成后, 任一方都可以使用第 4.6.3 节定义的 KeyUpdate handshake message 更新其发送 traffic key. 下一代 traffic key 通过从 client_/server_application_traffic_secret_N 生成 client_/server_application_traffic_secret_N+1, 再按第 7.3 节重新派生 traffic key 得到.
application_traffic_secret_N+1 =
HKDF-Expand-Label(application_traffic_secret_N,
"traffic upd", "", Hash.length)
计算出 client_/server_application_traffic_secret_N+1 及其关联 traffic key 后, 实现 SHOULD 删除 client_/server_application_traffic_secret_N 及其关联 traffic key.
7.3. Traffic Key Calculation (流量密钥计算)
traffic keying material 由以下输入值生成:
- secret value.
- purpose value, 指示正在生成的具体值.
- 正在生成的 key 的长度.
从 input traffic secret 生成 traffic keying material 的方式如下:
[sender]_write_key = HKDF-Expand-Label(Secret, "key", "", key_length)
[sender]_write_iv = HKDF-Expand-Label(Secret, "iv", "", iv_length)
[sender] 表示发送方. 不同 record type 使用的 Secret 如下:
| Record Type | Secret |
|---|---|
| 0-RTT Application | client_early_traffic_secret |
| Handshake | [sender]_handshake_traffic_secret |
| Application Data | [sender]_application_traffic_secret_N |
每当底层 Secret 改变时, 所有 traffic keying material 都会重新计算, 例如从 handshake key 切换到 application data key, 或执行 key update 时.
7.4. (EC)DHE Shared Secret Calculation ((EC)DHE 共享 Secret 计算)
7.4.1. Finite Field Diffie-Hellman
对于 finite field group, 执行常规 Diffie-Hellman [DH76] 计算. 协商出的 key (Z) 通过 big-endian 编码转换为 byte string, 并在左侧补零到 prime 的大小. 该 byte string 用作上文 key schedule 中的 shared secret.
7.4.2. Elliptic Curve Diffie-Hellman
对于 secp256r1, secp384r1 和 secp521r1, ECDH 计算按 [IEEE1363] 使用 ECKAS-DH1 scheme 和 identity map 作为 KDF 执行, shared secret 是 ECDH shared secret elliptic curve point 的 x-coordinate, 表示为 octet string. 该 octet string 对给定 field 具有固定长度, 其中的 leading zero MUST NOT 被截断.
对于 X25519 和 X448, public key 是将 ECDH scalar multiplication function 应用于 secret key 和标准 public basepoint 的结果. ECDH shared secret 是将同一 scalar multiplication function 应用于 secret key 和 peer public key 的结果, 输出原样使用而不处理.
这些曲线的实现 SHOULD 使用 [RFC7748] 规定的方法计算 Diffie-Hellman shared secret. 实现 MUST 检查计算出的 Diffie-Hellman shared secret 是否为全零值, 如果是则中止.
7.5. Exporters
[RFC5705] 根据 TLS pseudorandom function (PRF) 定义 TLS keying material exporter. 本文档用 HKDF 替代 PRF, 因而需要新的构造. exporter interface 保持不变.
TLS-Exporter(label, context_value, key_length) =
HKDF-Expand-Label(Derive-Secret(Secret, label, ""),
"exporter", Hash(context_value), key_length)
Secret 是 early_exporter_master_secret 或 exporter_master_secret. 除非应用明确指定, 实现 MUST 使用 exporter_master_secret. early_exporter_master_secret 用于需要为 0-RTT data 提供 exporter 的场景. RECOMMENDED 提供单独的 early exporter interface, 以避免 exporter 用户误用 early exporter 或 regular exporter.
如果未提供 context, context_value 为零长度. 因此, 不提供 context 与提供空 context 会计算出相同值. 这是相对早期 TLS 版本的变化. 新的 exporter 用法 SHOULD 在所有 exporter 计算中提供 context, 即使该值可以为空.