RFC 9001 - 使用 TLS 保护 QUIC
- 状态: Proposed Standard
- 发布日期: May 2021
- Stream: IETF
- 勘误: 无勘误
摘要
本文档定义了如何使用 TLS 1.3 保护 QUIC 连接, 描述了 QUIC 与 TLS 的集成方式.
相关资源
- 官方文本:
https://www.rfc-editor.org/rfc/rfc9001.txt - 官方页面:
https://datatracker.ietf.org/doc/html/rfc9001
1. 简介
本文档说明 QUIC 如何使用 TLS [TLS13] 提供安全性. TLS 1.3 相比早期版本显著降低了连接建立时延: 在没有丢包的情况下, 大多数新连接可以在 1 个往返时间内完成建立并获得保护; 对于客户端和服务器此前已经通信过的连接, 客户端通常还能立即发送应用数据, 即使用 0-RTT 建立.
在 QUIC 中, TLS 不是以传统 "TLS over TCP" 的记录层形式承载应用数据, 而是作为 QUIC 的安全组件使用. TLS 负责认证、密钥协商和握手状态推进; QUIC 负责传输 TLS 握手数据, 并使用 TLS 产生的密钥保护 QUIC 数据包.
2. 符号约定
本文档中的 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", "OPTIONAL" 等关键词, 当且仅当以全大写形式出现时, 按 BCP 14 [RFC2119] [RFC8174] 解释.
本文档沿用 [QUIC-TRANSPORT] 建立的术语. 为简洁起见, 文中的 TLS 指 TLS 1.3, 但在满足第 4.2 节要求的情况下, 也可以使用更新版本的 TLS.
TLS 为两个端点在不可信介质上建立通信提供机制. 它支持对等方认证, 并为端点交换的消息提供机密性和完整性保护. TLS 的认证密钥交换在客户端和服务器之间进行; 交换成功后, 双方会得到共享秘密. QUIC 关注的握手模式包括完整的 1-RTT 握手和可发送早期数据的 0-RTT 握手. 0-RTT 数据可能被攻击者重放, 因此不得用于会因重放而产生不希望效果的操作.
3. 协议概述
QUIC 负责对数据包提供机密性和完整性保护. 它使用从 TLS 握手派生出的密钥, 但不会像 TCP 上的 TLS 那样在 QUIC 中承载 TLS 记录. TLS 握手消息和告警消息直接由 QUIC 传输承载, QUIC 则承担可靠交付、排序和记录层保护相关职责.
QUIC 还依赖 TLS 执行认证, 并协商对安全性和性能至关重要的参数. 两者不是严格分层关系, 而是协作关系: QUIC 使用 TLS 握手; TLS 使用 QUIC 提供的可靠、有序传输以及 QUIC 的数据包保护.
二者的主要交互有两类: TLS 组件通过 QUIC 组件发送和接收握手消息; TLS 组件向 QUIC 组件提供状态更新, 包括要安装的新数据包保护密钥、握手完成状态、服务器证书等. QUIC 应用发送数据时不使用 TLS Application Data 记录, 而是将数据放入 QUIC STREAM frame 或其他 frame, 再封装进 QUIC packet.
4. 承载 TLS 消息
QUIC 在 CRYPTO frame 中承载 TLS 握手数据. 每个 CRYPTO frame 包含一段连续的握手数据, 由 offset 和 length 标识. 这些 frame 被封装进 QUIC packet, 并使用当前加密级别进行保护. 与 TCP 上的 TLS 类似, 一旦 TLS 握手数据交给 QUIC, QUIC 就负责可靠交付.
TLS 产生的每段数据都关联到 TLS 当前使用的一组密钥. 如果 QUIC 需要重传该数据, 即使 TLS 已经更新到更新的密钥, 也必须使用原来的密钥. 每个加密级别对应一个 packet number space, 而 packet number space 会影响 frame 的语义; 某些 frame 在不同 packet number space 中被禁止.
QUIC 和 TLS 的接口主要包括: 发送和接收握手消息; 处理恢复会话中保存的传输和应用状态, 并判断是否可以产生或接受 0-RTT 数据; 重新生成发送和接收密钥; 更新握手状态. QUIC 与 TLS 还需要约定由哪一方负责验证对等方凭据, 例如证书验证.
本文档中, 当 TLS 栈报告握手完成时, 认为 TLS handshake complete. 在服务器侧, 握手完成也表示 handshake confirmed, 服务器必须尽快发送 HANDSHAKE_DONE frame. 在客户端侧, 收到 HANDSHAKE_DONE frame 时认为握手已确认; 客户端也可以在收到 1-RTT packet 的确认后认为握手已确认.
TLS 1.3 是本文档定义的基线. 客户端禁止提供早于 TLS 1.3 的版本; 如果协商出 TLS 1.2 或更早版本, 端点必须终止连接. 服务器在处理 ClientHello 时应注意首个 Initial packet 的大小和地址验证状态; 如果 ClientHello 被拆分到多个 Initial packet 中, 未验证地址的客户端可能造成服务器缓冲压力, 此时服务器可以使用 Retry.
QUIC 支持 TLS 1.3 的 session resumption 和 0-RTT. 0-RTT 依赖客户端保存此前连接的关键参数, 并向服务器提供可恢复相同信息的 TLS session ticket. 端点必须一致使用建立 0-RTT 所需的全部信息, 不能选择性忽略会改变 0-RTT 发送或处理方式的信息.
TLS alert 会映射为 QUIC CONNECTION_CLOSE 错误码. 当某个加密级别不再需要时, QUIC 会丢弃对应密钥: Initial key 会更积极地丢弃, Handshake key 在握手确认后丢弃, 0-RTT key 在不再需要发送或接收 0-RTT packet 后丢弃.
5. 数据包保护
QUIC 与 TCP 上的 TLS 一样, 使用从 TLS 握手派生出的密钥保护数据包, 并使用 TLS 协商出的 AEAD 算法. 不同类型的 QUIC packet 具有不同保护属性: Version Negotiation packet 没有密码保护; Retry packet 使用固定的 AEAD_AES_128_GCM 保护以防意外修改并限制可生成有效 Retry 的实体; Initial packet 使用从客户端首个 Initial packet 的 Destination Connection ID 派生出的密钥; 其他 packet 使用 TLS 协商出的强机密性和完整性保护.
每个加密级别在两个方向上都有独立的 traffic secret. 除 Initial 级别外, 这些 secret 由 TLS 派生, QUIC 再通过 TLS 提供的 KDF 计算 packet protection key、IV 和 header protection key. TLS 1.3 中使用 HKDF-Expand-Label, QUIC 标签包括 "quic key", "quic iv" 和 "quic hp".
Initial secret 使用固定 salt 和客户端首个 Initial packet 的 Destination Connection ID 通过 HKDF-Extract 计算, 再分别派生客户端和服务器方向的 Initial secret. 由于 Initial key 容易由观察者计算, Initial packet 不被视为提供真正的机密性或完整性.
构造 packet 时, 先应用 AEAD, 再应用 header protection. 未保护的 packet header 作为 associated data 参与认证. 接收 packet 时, 端点先移除 header protection, 恢复 packet number, 再用 packet protection key 和 IV 验证并解密负载.
Header protection 使用单独派生的 key 保护首字节的低位以及 Packet Number 字段, 使路径上的设备无法观察这些字段. AES-GCM、AES-CCM 和 ChaCha20-Poly1305 分别定义了相应的 header protection 算法. Retry 和 Version Negotiation packet 不包含受此过程保护的字段.
端点在接收受保护 packet 时, 如果某个 packet 看起来触发 key update 但无法成功解保护, 必须丢弃该 packet. 0-RTT key 只能用于 0-RTT packet, 且必须考虑早期数据可能被重放的风险. 对乱序到达的受保护 packet, 实现需要能够在允许范围内保留或生成合适的接收密钥.
6. 密钥更新
QUIC 使用自己的 key update 机制, 不使用 TLS KeyUpdate 消息. 端点为发送和接收分别维护 packet protection secret. 发起 key update 时, 端点从当前写 secret 派生新的写 secret, 并用它保护新的 packet. 新密钥通过 TLS 提供的 KDF 和 "quic ku" 标签生成; header protection key 不随 key update 改变.
Key Phase bit 用于让接收方识别对端是否切换到了下一组 packet protection key. 端点在成功使用下一组 key 解保护 packet 后, 认为对端发起了 key update, 并必须相应更新自己的发送 key.
实现必须避免在处理疑似 key update packet 时泄露 timing side channel. 端点通常应准备当前和下一组接收 key; 在刚完成 key update 后, 可以短时间推迟生成再下一组 key, 以减少同时保留的密钥数量.
端点切换到新 key 后不会再用旧 key 发送 packet. 更高 packet number 的 packet 必须使用与较低 packet number 相同或更新的 packet protection key. 如果端点发现较低 packet number 使用新 key 而较高 packet number 又能用旧 key 成功解保护, 必须视为 KEY_UPDATE_ERROR.
为保护 AEAD 安全边界, 端点必须限制每组 key 下发送的加密 packet 数和接收的认证失败 packet 数. 达到 AEAD 使用限制前, 推荐立即以 AEAD_LIMIT_REACHED 连接错误关闭连接.
7. 初始消息的安全性
Initial packet 使用的密钥可以由任何观察到客户端首个 Initial packet 的实体计算, 因此 Initial 保护只提供防止偶然修改的能力, 不提供真正的机密性或认证. 攻击者可以伪造或修改 Initial packet, 试图干扰连接建立.
QUIC 通过尽快丢弃 Initial key、验证后续 Handshake packet、以及使用 TLS Finished 消息认证握手 transcript 来限制此类攻击. 一旦端点能够使用 Handshake key, 就表明它已经接收到了 Initial packet 中必要的 CRYPTO frame, 因而不再需要继续处理新的 Initial 数据.
实现必须谨慎处理来自较低加密级别的数据. 如果 TLS 在进入更高接收加密级别时仍有未消费的较低级别数据, 端点必须将其视为 PROTOCOL_VIOLATION. 这防止攻击者通过旧级别数据扰乱握手状态.
8. QUIC 特定调整
QUIC 对 TLS 握手做了若干特定调整. 应用层协议通过 TLS 的 ALPN 机制协商, 并且协商结果影响 QUIC 连接上承载的应用语义. 如果 ALPN 协商失败或应用协议不被接受, 连接必须失败.
QUIC 定义了 quic_transport_parameters TLS extension, 用于在 ClientHello 和 EncryptedExtensions 中携带 QUIC transport parameters. 这些参数在握手完成后被 TLS transcript 认证, 因而可以被 QUIC 安全使用. 端点必须验证接收到的 transport parameters 是否符合 [QUIC-TRANSPORT] 的要求.
QUIC 不使用 TLS 的 EndOfEarlyData 消息. 当客户端停止发送 0-RTT packet 并开始发送 Handshake 或 1-RTT packet 时, 其语义已经足以表示 early data 结束.
QUIC 也禁止 TLS middlebox compatibility mode. TLS 1.3 中的 ChangeCipherSpec 对 QUIC 没有意义, 且 QUIC 没有 TLS 记录层. 因此端点不得发送或依赖这些兼容性消息.
9. 安全考虑
QUIC 使用 TLS 的安全属性, 但也引入了若干需要独立分析的问题. Session resumption 可能使服务器把原连接和恢复连接关联起来, 从而影响隐私; 客户端可以选择不启用恢复, 并且不应重复使用 ticket.
0-RTT 数据可能被重放. 应用协议必须定义哪些请求可以安全地在 0-RTT 中发送, 并避免让重放触发不可接受的副作用. 服务器也需要根据保存的参数判断是否接受 0-RTT.
在地址验证前, QUIC 服务器受到放大攻击限制. TLS certificate chain、transport parameters 和握手消息大小都会影响服务器在未验证客户端地址前可发送的数据量. 控制证书链大小和 ClientHello 大小对性能和抗滥用都很重要.
Header protection 提供对 Packet Number 和 Key Phase 等字段的机密性保护, 但这些字段最终通过 packet protection 的 associated data 获得认证. 实现必须避免在移除 header protection、恢复 packet number、解保护 packet、处理 key update 时产生 timing side channel.
QUIC 使用与 TLS 记录层不同的标签派生 packet protection key、IV 和 header protection key, 以维持跨协议密钥分离. 新 QUIC 版本应定义新的派生标签和 Initial secret salt. QUIC 还依赖端点生成安全随机数, 包括 connection ID 以及 TLS 间接使用的随机值.
10. IANA 考虑
IANA 已在 "TLS ExtensionType Values" 注册表中为本文档定义的 quic_transport_parameters extension 分配代码点 57 (0x39). 该扩展的 Recommended 列标记为 Yes.
该 TLS 1.3 extension 可出现在 ClientHello (CH) 和 EncryptedExtensions (EE) 中. 注册项引用本文档, 用于标识 QUIC 端点在 TLS 握手期间交换 QUIC transport parameters 的机制.
11. 参考文献
本节包含规范性参考文献和资料性参考文献. 规范性参考文献包括 TLS 1.3、HKDF、AEAD、QUIC transport、QUIC recovery、RFC 2119/8174 规范性关键词、证书验证相关规范以及 TLS 注册表等.
资料性参考文献包括 0-RTT 重放风险、AEAD 使用界限分析、GCM/CCM 安全分析、随机数生成指南、证书压缩、HTTP/2 与 TLS 1.3 交互等背景材料. 阅读实现细节时, 应同时参考 [QUIC-TRANSPORT], [QUIC-RECOVERY] 和 [TLS13].
附录 A. 数据包保护示例
本附录给出 packet protection 的样例, 用于展示如何从 TLS secret 派生 QUIC 使用的 key、IV、header protection key 和 key update secret, 以及如何对 Initial packet、Retry packet 和 short header packet 应用保护.
示例首先列出从 secret 通过 HKDF-Expand-Label 派生出的值, 然后展示客户端 Initial packet 和服务器 Initial packet 的构造过程. 这些示例包括 packet number、nonce、未保护 header、payload 明文、payload 密文、header protection sample、mask 以及最终受保护 packet.
Retry 示例展示了 Retry integrity tag 的计算方式, 其中完整性检查覆盖客户端选择的原始 Destination Connection ID, 但该值不直接出现在最终 Retry packet 中.
ChaCha20-Poly1305 short header 示例展示了使用 AEAD_CHACHA20_POLY1305 时的最小 packet 保护流程. 示例说明如何用 application write secret 派生 key、IV、header protection key 和后续 key update secret, 并展示 payload 加密和 header protection 的结果.
附录 B. AEAD 算法分析
本附录记录用于推导 AEAD_AES_128_GCM、AEAD_AES_256_GCM 和 AEAD_AES_128_CCM 使用限制的分析. 分析使用乘法 *、除法 /、指数 ^ 和括号表示优先级, 并使用若干符号: t 表示 authentication tag 的 bit 数, 这些算法中为 128; n 表示 block function 的 bit 数, 这些算法中为 128; k 表示 key bit 数; l 表示每个 packet 中的 block 数; q 表示端点真实创建并保护的 packet 数; v 表示端点会接受处理的伪造 packet 数; o 表示攻击者进行的离线 ideal cipher query 数.
分析分别考虑最大 2^11 bytes 的常见部署 packet 大小和最大 2^16 bytes 的 QUIC packet 大小. 只有严格限制 packet 大小的端点, 才能使用基于较小 packet 推导出的更高机密性和完整性界限.
对 AEAD_AES_128_GCM 和 AEAD_AES_256_GCM, 分析基于 [GCM-MU]. 对机密性, 目标是限制攻击者区分真实 AEAD 与随机 AEAD 的优势; 对完整性, 目标是限制攻击者成功伪造 packet 的优势. 本文档建议对 AES-128-GCM 和 AES-256-GCM 应用相同的实用界限, 因为这些界限已经足够大.
对 AEAD_AES_128_CCM, 分析基于 [CCM-ANALYSIS]. QUIC 要求任何被使用的 AEAD 都必须具有保护机密性和完整性的使用限制. 对 CCM, 完整性界限给出的优势约束不弱于机密性界限, 因此可以用它来推导统一限制.