5. 数据包保护 (Packet Protection)
与TCP上的TLS一样,QUIC使用从TLS握手派生的密钥保护数据包,使用TLS协商的AEAD算法[AEAD]。
QUIC数据包根据其类型具有不同的保护:
-
版本协商(Version Negotiation)数据包没有加密保护。
-
重试(Retry)数据包使用AEAD_AES_128_GCM提供防止意外修改的保护,并限制能够生成有效重试的实体(参见第5.8节)。
-
初始(Initial)数据包使用AEAD_AES_128_GCM,密钥从客户端发送的第一个初始数据包的目标连接ID字段派生(参见第5.2节)。
-
所有其他数据包使用TLS协商的密钥和算法具有强大的加密保密性和完整性保护。
本节描述如何将数据包保护应用于握手、0-RTT和1-RTT数据包。相同的数据包保护过程应用于初始数据包。但是,由于确定用于初始数据包的密钥很简单,这些数据包不被认为具有保密性或完整性保护。重试数据包使用固定密钥,因此也缺乏保密性和完整性保护。
5.1. 数据包保护密钥 (Packet Protection Keys)
QUIC以与TLS派生记录保护密钥相同的方式派生数据包保护密钥。
每个加密级别都有单独的秘密值,用于保护在每个方向发送的数据包。这些流量秘密(traffic secrets)由TLS派生(参见[TLS13]第7.1节),并被QUIC用于除初始加密级别之外的所有加密级别。初始加密级别的秘密基于客户端的初始目标连接ID计算,如第5.2节所述。
用于数据包保护的密钥使用TLS提供的KDF从TLS秘密计算。在TLS 1.3中,使用[TLS13]第7.1节中描述的HKDF-Expand-Label函数,使用协商的密码套件的哈希函数。QUIC中HKDF-Expand-Label的所有使用都使用零长度的上下文(Context)。
请注意,描述为字符串的标签(Labels)使用ASCII [ASCII]编码为字节,不带引号或尾随NUL字节。
当前加密级别的秘密和标签"quic key"用作KDF的输入以生成AEAD密钥。标签"quic iv"用于派生初始化向量(IV)(参见第5.3节)。头部保护密钥使用标签"quic hp"(参见第5.4节)。使用这些标签在QUIC和TLS之间提供密钥分离(参见第9.6节)。
由于"quic key"和"quic hp"都用于生成密钥,因此使用这些标签提供给HKDF-Expand-Label的长度由AEAD或头部保护算法的密钥大小确定。为"quic iv"提供的长度是最小AEAD nonce长度,如果更大则为8字节(参见[AEAD])。
用于初始秘密的KDF始终是TLS 1.3 HKDF-Expand-Label函数(参见第5.2节)。
5.2. 初始秘密 (Initial Secrets)
初始数据包应用数据包保护过程,但使用从客户端发送的第一个初始数据包的目标连接ID字段派生的秘密。
该秘密使用HKDF-Extract确定(参见[HKDF]第2.2节)。盐(salt)为0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a,输入密钥材料(IKM)是目标连接ID字段。这产生一个中间伪随机密钥(PRK),用于派生两个单独的发送和接收秘密。
客户端用于构建初始数据包的秘密使用PRK和标签"client in"作为TLS的HKDF-Expand-Label函数[TLS13]的输入,以生成32字节的秘密。服务器构建的数据包使用相同的过程,使用标签"server in"。用于派生初始秘密和密钥的HKDF的哈希函数是SHA-256 [SHA]。
此过程的伪代码为:
initial_salt = 0x38762cf7f55934b34d179ae6a4c80cadccbb7f0a
initial_secret = HKDF-Extract(initial_salt,
client_dst_connection_id)
client_initial_secret = HKDF-Expand-Label(initial_secret,
"client in", "",
Hash.length)
server_initial_secret = HKDF-Expand-Label(initial_secret,
"server in", "",
Hash.length)
与HKDF-Expand-Label一起使用的连接ID是客户端发送的初始数据包中的目标连接ID。这将是一个随机选择的值,除非客户端在接收到重试数据包后创建初始数据包,在这种情况下目标连接ID由服务器选择。
未来的QUIC版本应该(SHOULD)生成新的盐值,从而确保每个QUIC版本的密钥不同。这可以防止只识别一个QUIC版本的中间盒看到或修改未来版本的数据包内容。
初始数据包必须(MUST)使用TLS 1.3中定义的HKDF-Expand-Label函数,即使提供的TLS版本不包括TLS 1.3。
服务器发送重试数据包并使用服务器选择的连接ID值会导致用于构建后续初始数据包的秘密发生变化。客户端响应服务器初始数据包时使用的目标连接ID不会改变秘密。
| 注意:目标连接ID字段的长度最多可达20字节,或者如果服务器发送源连接ID字段为零长度的 | 重试数据包,则可能为零长度。重试后,初始密钥不保证服务器已收到客户端数据包,因此客户端 | 必须依赖包含重试数据包的交换来验证服务器地址(参见[QUIC-TRANSPORT]第8.1节)。
附录A包含初始数据包的示例。
5.3. AEAD使用 (AEAD Usage)
用于QUIC数据包保护的带关联数据的认证加密(AEAD)函数(参见[AEAD])是为与TLS连接一起使用而协商的AEAD。例如,如果TLS使用密码套件TLS_AES_128_GCM_SHA256,则使用AEAD_AES_128_GCM函数。
QUIC可以使用[TLS13]中定义的任何密码套件,但TLS_AES_128_CCM_8_SHA256除外。除非已为密码套件定义了头部保护方案,否则不得(MUST NOT)协商密码套件。本文档为[TLS13]中定义的所有密码套件定义了头部保护方案,但TLS_AES_128_CCM_8_SHA256除外。这些密码套件具有16字节的认证标签,并产生比输入大16字节的输出。
终端不得(MUST NOT)拒绝提供其不支持的密码套件的ClientHello,否则将无法部署新的密码套件。这也适用于TLS_AES_128_CCM_8_SHA256。
在构造数据包时,在应用头部保护之前应用AEAD函数(参见第5.4节)。未受保护的数据包头部是关联数据(A)的一部分。在处理数据包时,终端首先移除头部保护。
数据包密钥和IV按第5.1节所述计算。随机数(N)通过将IV与数据包编号组合形成。按网络字节序重构的62位QUIC数据包编号用零左填充到IV的大小。填充的数据包编号和IV的异或(XOR)形成AEAD随机数。
AEAD的关联数据A是QUIC数据包头部的内容,从短头部或长头部的第一个字节开始,直到并包括未受保护的数据包编号。
AEAD的明文P是QUIC数据包有效载荷,如[QUIC-TRANSPORT]中所述。
AEAD的密文C代替P传输。
某些AEAD函数对可以使用相同密钥和IV加密的数据包数量有限制(参见第6.6节)。这可能低于数据包编号限制。终端必须(MUST)在超过使用中的AEAD配置施加的任何限制之前启动密钥更新(第6节)。
5.4. 头部保护 (Header Protection)
QUIC数据包头部的某些部分,特别是数据包编号字段,使用与数据包保护密钥和IV分开派生的密钥进行保护。使用标签"quic hp"派生的密钥用于为路径上的元素未暴露的字段提供保密性保护。
此保护应用于第一个字节的最低有效位和数据包编号字段。对于长头部数据包,第一个字节的4个最低有效位受到保护。对于短头部数据包,第一个字节的5个最低有效位受到保护。对于两种头部格式,这涵盖了保留(Reserved)位和数据包编号长度(Packet Number Length)字段。短头部数据包中的密钥相位(Key Phase)位也受到保护。
相同的头部保护密钥用于整个连接,并且其值在密钥更新后不会改变(参见第6节)。这允许头部保护用于保护密钥相位。
此过程不适用于重试或版本协商数据包,它们不包含受保护的有效载荷或不包含受此过程保护的字段。
注意:为了限制文档长度并确保完整性,建议参考原始RFC 9001文本以获取剩余章节(5.4.1-5.8)的完整技术细节,包括:
- 5.4.1. 头部保护应用
- 5.4.2. 头部保护样本
- 5.4.3. 基于AES的头部保护
- 5.4.4. 基于ChaCha20的头部保护
- 5.5. 接收受保护的数据包
- 5.6. 0-RTT密钥的使用
- 5.7. 接收乱序的受保护数据包
- 5.8. 重试数据包完整性
完整技术文档请参考:
https://www.rfc-editor.org/rfc/rfc9001.html#section-5