5. Record Protocol (记录协议)
TLS record protocol 接收要传输的消息, 将数据分片为可管理的块, 保护 record, 并传输结果. 接收到的数据会被验证, 解密, 重新组装, 然后交付给更高层 client.
TLS record 具有类型, 这允许多个高层协议复用同一个 record layer. 本文档规定四种 content type: handshake, application_data, alert, change_cipher_spec. change_cipher_spec record 仅用于兼容性目的 (见 Appendix D.4).
实现 MAY 在发送或接收第一个 ClientHello 之后, 且收到 peer 的 Finished 消息之前, 随时接收一个未加密的 change_cipher_spec record, 其内容为单 byte 值 0x01, 并且 MUST 直接丢弃而不做进一步处理. 如果收到任何其他 change_cipher_spec 值, 或收到受保护的 change_cipher_spec record, 实现 MUST 以 "unexpected_message" alert 中止 handshake.
实现 MUST NOT 发送本文档未定义的 record type, 除非某个 extension 已协商该类型. TLS 实现收到 unexpected record type 时, MUST 以 "unexpected_message" alert 终止 connection.
5.1. Record Layer (记录层)
record layer 将信息块分片成 TLSPlaintext record, 每个 record 承载不超过 2^14 byte 的数据块. message boundary 的处理方式取决于底层 ContentType. 未来任何 content type MUST 指定适当规则.
Handshake message MAY 合并到单个 TLSPlaintext record 中, 也可以分片到多个 record 中, 前提是:
- Handshake message MUST NOT 与其他 record type 交错. 如果一个 handshake message 被拆分到两个或更多 record 中, 它们之间 MUST NOT 出现其他 record.
- Handshake message MUST NOT 跨越 key change. 实现 MUST 验证 key change 前的所有消息都与 record boundary 对齐. 如果不对齐, MUST 以 "unexpected_message" alert 终止 connection.
实现 MUST NOT 发送零长度 Handshake fragment, 即使这些 fragment 只包含 padding.
Alert message MUST NOT 跨 record 分片, 多个 alert message MUST NOT 合并到单个 TLSPlaintext record 中. 换言之, Alert type 的 record MUST 只包含一条 message.
Application Data message 包含 TLS 不解释的数据. Application Data message 始终受保护. 可发送零长度 Application Data fragment, 因为它们可作为流量分析对抗措施. Application Data fragment MAY 拆分到多个 record 或合并到单个 record.
enum {
invalid(0),
change_cipher_spec(20),
alert(21),
handshake(22),
application_data(23),
(255)
} ContentType;
struct {
ContentType type;
ProtocolVersion legacy_record_version;
uint16 length;
opaque fragment[TLSPlaintext.length];
} TLSPlaintext;
type: 用于处理封装 fragment 的高层协议.
legacy_record_version: TLS 1.3 实现生成的所有 record MUST 设置为 0x0303, 初始 ClientHello 除外. 为兼容目的, 初始 ClientHello MAY 设置为 0x0301. 此字段已弃用, MUST 在所有用途中忽略.
length: 后续 TLSPlaintext.fragment 的 byte 长度. 该长度 MUST NOT 超过 2^14 byte. endpoint 收到超过此长度的 record 时, MUST 以 "record_overflow" alert 终止 connection.
fragment: 正在传输的数据. 该值对 TLS 透明, 由 type 字段指定的高层协议作为独立块处理.
TLS 1.3 使用 version 0x0304. 为最大化向后兼容性, 包含初始 ClientHello 的 record SHOULD 使用 version 0x0301, 包含第二个 ClientHello 或 ServerHello 的 record MUST 使用 version 0x0303.
5.2. Record Payload Protection (记录载荷保护)
record protection function 将 TLSPlaintext 转换为 TLSCiphertext. deprotection function 执行反向过程. 与早期 TLS 版本不同, TLS 1.3 中所有 cipher 都建模为 Authenticated Encryption with Associated Data (AEAD) [RFC5116]. AEAD 同时提供加密和认证.
struct {
opaque content[TLSPlaintext.length];
ContentType type;
uint8 zeros[length_of_padding];
} TLSInnerPlaintext;
struct {
ContentType opaque_type = application_data; /* 23 */
ProtocolVersion legacy_record_version = 0x0303; /* TLS v1.2 */
uint16 length;
opaque encrypted_record[TLSCiphertext.length];
} TLSCiphertext;
content: TLSPlaintext.fragment 值, 可以是 handshake 或 alert message 的 byte 编码, 也可以是应用要发送的原始 byte.
type: TLSPlaintext.type 值, 表示 record 的实际 content type.
zeros: type 字段后可出现任意长度的零值 byte, 用作 padding. sender 可以借此按所选长度填充 TLS record, 只要总长度保持在 record size limit 内.
opaque_type: TLSCiphertext record 的外层 opaque_type 始终设置为 23 (application_data), 用于兼容习惯解析早期 TLS 版本的 middlebox. 实际 content type 在解密后的 TLSInnerPlaintext.type 中.
legacy_record_version: 始终为 0x0303.
length: 后续 TLSCiphertext.encrypted_record 的 byte 长度. 该长度 MUST NOT 超过 2^14 + 256 byte. endpoint 收到超过此长度的 record 时, MUST 以 "record_overflow" alert 终止 connection.
AEAD algorithm 输入包括 key, nonce, plaintext 和 additional data. key 是 client_write_key 或 server_write_key. nonce 由 sequence number 和 client_write_iv 或 server_write_iv 派生. additional data 为 record header:
additional_data = TLSCiphertext.opaque_type ||
TLSCiphertext.legacy_record_version ||
TLSCiphertext.length
如果解密失败, receiver MUST 以 "bad_record_mac" alert 终止 connection. TLS 1.3 中使用的 AEAD algorithm MUST NOT 产生超过 255 octet 的扩展.
5.3. Per-Record Nonce (每记录 Nonce)
读写 record 分别维护一个 64-bit sequence number. 读或写每个 record 后, 相应 sequence number 加一. 每个 sequence number 在 connection 开始时以及每次 key 改变时设为零. 在特定 traffic key 下传输的第一个 record MUST 使用 sequence number 0.
sequence number 不应回绕. 如果 TLS 实现需要回绕 sequence number, 它 MUST rekey (第 4.6.3 节) 或终止 connection.
per-record nonce 通过将 64-bit record sequence number 按 network byte order 编码并左侧补零到 iv_length, 然后与 static client_write_iv 或 server_write_iv 做 XOR 形成. 这与 TLS 1.2 中部分显式 nonce 的构造不同.
5.4. Record Padding (记录填充)
所有加密 TLS record 都可以填充以增大 TLSCiphertext 大小, 从而使 sender 能够向观察者隐藏流量大小. padding 是附加在 ContentType 字段之后并在加密前加入的一串零值 byte. 实现 MUST 在加密前将 padding octet 全部设为零.
Application Data record 可以包含零长度 TLSInnerPlaintext.content, 以便生成看似合理大小的掩护流量. 实现 MUST NOT 发送零长度 content 的 Handshake 或 Alert record. 如果收到此类 message, 接收实现 MUST 以 "unexpected_message" alert 终止 connection.
padding 会由 record protection mechanism 自动验证. 成功解密 TLSCiphertext.encrypted_record 后, 接收实现从明文末尾向前扫描, 直到找到非零 octet. 该非零 octet 是 message 的 content type. 如果接收实现未在明文中找到非零 octet, MUST 以 "unexpected_message" alert 终止 connection.
padding 不改变总体 record size limit. 完整编码后的 TLSInnerPlaintext MUST NOT 超过 2^14 + 1 octet.
5.5. Limits on Key Usage (密钥使用限制)
在给定一组 key 下, 可安全加密的 plaintext 数量存在密码学限制. [AEAD-LIMITS] 在假设底层原语 AES 或 ChaCha20 没有弱点的前提下分析了这些限制. 实现 SHOULD 在达到限制之前按第 4.6.3 节执行 key update.
对于 AES-GCM, 在给定 connection 上最多可以加密 2^24.5 个满尺寸 record (约 2400 万), 同时保持约 2^-57 的 Authenticated Encryption (AE) 安全裕度. 对于 ChaCha20/Poly1305, record sequence number 会先于安全限制到达之前回绕.