AES-CTR HMAC 流式传输 AEAD

本文档正式定义了由 AES-CTR HMAC 流式密钥(以 proto 格式编码为 type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey)表示的数学函数。

此加密方式大致基于 [HRRV15]1。如需分析安全性,请参阅 [HS20]2。另请注意,Tink 跨语言测试包含一个测试 aes_ctr_hmac_streaming_key_test.py,其中包含 test_manually_created_test_vector,其中包含有关如何获取密文的完整演示。

键和参数

键由以下部分描述(本文档中的所有大小均以字节为单位):

  • InitialKeyMaterial,字节字符串:初始密钥材料。
  • CiphertextSegmentSize{1,2,,2311}
  • DerivedKeySize{16,32}
  • HkdfHashType{SHA1,SHA256,SHA512}
  • HmacHashType{SHA1,SHA256,SHA512}
  • HmacTagSizeN

有效键还满足以下属性:

  • len(InitialKeyMaterial)DerivedKeySize
  • 如果 HmacHashType=SHA1 ,则 HmacTagSize{10,,20}
  • 如果 HmacHashType=SHA256 ,则 HmacTagSize{10,,32}
  • 如果 HmacHashType=SHA512 ,则 HmacTagSize{10,,64}
  • CiphertextSegmentSize>DerivedKeySize+HmacTagSize+8 (如后所述,此值等于len(Header)+HmacTagSize )。

Tink 会拒绝不满足上述任何属性的键(在解析键时或创建相应基元时)。

加密函数

如需 Msg 对包含关联数据的消息进行加密AssociatedData,我们需要创建一个标头,将消息拆分为多个分段,对每个分段进行加密,然后将这些分段串联起来。我们将在下文中介绍这些步骤。

创建标头

如需创建标头,我们首先选择一个长度为 DerivedKeySize的均匀随机字符串 Salt。接下来,我们选择一个长度为 7 的均匀随机字符串NoncePrefix

然后,我们设置Header:=len(Header)SaltNoncePrefix,其中标头的长度编码为单个字节。我们注意到len(Header){24,40}

接下来,我们将 HKDF3 与哈希函数 HkdfHashType结合使用,为此消息计算长度为DerivedKeySize+32 的密钥材料:k:=HKDF(InitialKeyMaterial,Salt,AssociatedData)。这些输入会用于HKDF的相应输入: InitialKeyMaterialikmSalt 是盐,AssociatedData 用作 info

然后,字符串 k 会拆分为两个部分 k1k2:=k,即len(k1)=DerivedKeySizelen(k2)=32

拆分消息

接下来,消息 M 会拆分为以下部分: M=M0M1Mn1

其长度应满足以下条件:

  • len(M0){0,,CiphertextSegmentSizelen(Header)HmacTagSize}
  • 如果 n>1,则 len(M1),,len(Mn1){1,,CiphertextSegmentSizeHmacTagSize}
  • 如果为 n>1,则 M0,,Mn2 必须根据上述约束条件具有最大长度。

在此拆分中, n 不得超过 232。否则,加密将失败。

加密分块

如需对分段 Mi进行加密,我们首先计算IVi:=NoncePrefixib0x00000000,其中我们使用大端字节编码将 i 编码为 4 个字节,并将字节 b 设置为 i<n1 时为 0x00,否则为 0x01

然后,我们 Mi 使用 AES CTR 密钥 k1和初始化矢量IVi进行加密。换句话说,AES 调用的输入为IVi,IVi+1,IVi+2,,其中 IVi 被解释为大端整数。这会产生 Ci

我们使用 HMAC 和 HmacHashType 提供的哈希函数以及对串联IViCi的密钥 k2 计算标记。

然后,我们将密文与标记串联起来,以获取 Ci

串联这些细分

最后,将所有分段串联为HeaderC0Cn1,即最终的密文。

解密函数

解密只是对加密进行反转。我们使用标头获取 Nonce,并单独解密每个密文段。

API 可能会(通常也确实会)允许随机访问,或者允许访问文件开头,而无需检查文件结尾。这是有意为之,因为可以从 Ci解密 Mi ,而无需解密所有之前和剩余的密文块。

不过,API 应注意不要让用户混淆文件结束错误和解密错误:在上述两种情况下,API 都可能必须返回错误,而忽略这种差异可能会导致攻击者能够有效地截断文件。

键的序列化和解析

如需以“Tink Proto”格式序列化密钥,我们首先以显而易见的方式将参数映射到 aes_ctr_hmac_streaming.proto 中给出的 proto。version 字段需要设置为 0。然后,我们使用常规 Proto 序列化功能对其进行序列化,并将生成的字符串嵌入 KeyData Proto 字段的值中。我们将 type_url 字段设置为 type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey。然后,我们将 key_material_type 设置为 SYMMETRIC,并将其嵌入到密钥集中。我们通常将 output_prefix_type 设置为 RAW。唯一的例外情况是,如果为 output_prefix_type 设置了其他值来解析键,Tink 可能会写入 RAW 或之前的值。

如需解析键,我们会反向执行上述过程(以解析 proto 的常规方式)。系统会忽略 key_material_type 字段。您可以忽略 output_prefix_type 的值,也可以拒绝 output_prefix_typeRAW 不同的键。系统会拒绝 version 不同于 0 的键。

参考


  1. [HRRV15] Hoang、Reyhanitabar、Rogaway、Vizar。在线身份验证加密及其 Nonce 重复使用滥用行为防范功能。CRYPTO 2015。 https://eprint.iacr.org/2015/189 

  2. [HS20] Google Tink 库中的流式加密安全性。 Hoang, Shen, 2020. https://eprint.iacr.org/2020/1019 

  3. [HKDF] 基于 HMAC 的提取和展开密钥派生函数 (HKDF),RFC 5869。https://www.rfc-editor.org/rfc/rfc5869