Tink 线格式

本页介绍了 Tink 的密钥和基元输出的线格格式。本文档面向希望向 Tink 添加其他语言的密码学家,以及希望使用线路兼容模式的其他高级加密库的维护者。不适合一般观众。

密钥集序列化

Tink 使用 Google protobuf 来序列化其键集。

  • 二进制序列化密钥集是 tink.proto 中定义的序列化 Keyset proto。键的 KeyData 值属性是相应键类型的序列化 proto。
  • JSON 序列化密钥集是采用 JSON 格式序列化的 Keyset proto。请注意,KeyData 值仍然是二进制序列化 Proto。
  • 加密密钥集是 tink.proto 中定义的序列化 EncryptedKeyset proto。它包含一个加密的二进制序列化密钥集,以及一些(可选)未加密的 KeysetInfo 元数据。

Tink 输出前缀

大多数 Tink 基元都支持 5 字节的输出前缀,其中包括:

  • 1 字节版本:0x01
  • 4 字节的键提示:这是所用密钥的键 ID。

某些旧版密钥可能也支持版本字节 0x00

请注意,此前缀未经过身份验证,不能用于安全目的。Tink 会将其用作提示,以加快解密或验证速度。

AEAD

一般来说,Tink 将 AEAD 密文格式化为:

prefix || IV || ciphertext || tag

除非相应的 RFC 中另有规定。prefix 为空或 5 字节的 Tink 输出前缀。

AES-CTR-HMAC

对于 AES-CTR-HMAC,Tink 会按如下方式计算 MAC 和关联数据 (AD):

AD || IV || ciphertext || bitlen(AD)

其中 bitlen(AD) 是 AD 的长度(以位为单位),表示为 64 位大端序无符号整数。此 HMAC 方案遵循 Mcgrew 的 AES-CBC-HMAC 草稿

确定性 AEAD

Tink 针对 AES-SIV 实现了 RFC 5297,将合成初始化矢量 (SIV) 放在密文开头。基元可能会添加一个 5 字节的 Tink 输出前缀。

虽然 RFC 5297 支持关联数据列表,但 Tink 仅支持确切一个关联数据,这在 RFC 5297 中对应于一个元素的列表。空关联数据是包含一个空元素的列表,而不是空列表。

流式 AEAD

请参阅 AES-CTR HMACAES-GCM-HKDF

信封加密

信封加密使用 Tink 的 AEAD 基元,使用数据加密密钥 DEK 对数据进行加密。加密的工作原理如下:

  • 使用给定密钥模板(或密钥参数)生成新的 DEK
  • DEK 会序列化为字节字符串。序列化格式是键类型 proto 的协议缓冲区序列化。例如,以下是 aes_gcm.proto 中针对密钥类型为 AES GCM 的 DEK 定义的序列化 AesGcmKey 协议缓冲区消息。如需了解如何序列化协议缓冲区,请参阅协议缓冲区序列化
  • 序列化的 DEK 会由外部提供商(例如 GCP)加密为 encrypted DEK
  • DEK 用于将明文与关联数据加密为 ciphertext。因此,ciphertext 的格式与与 DEK 对应的 AEAD 基元完全相同。

信封加密的输出格式如下:

encrypted DEK length || encrypted DEK || ciphertext

encrypted DEK length 为 4 个字节,将 encrypted DEK 的长度存储为 32 位大端整数。

MAC

Tink 遵循相应的 RFC。基元可能会向标记添加 5 字节的 Tink 输出前缀。

PRF 集

Tink 遵循相应的 RFC。请注意,对于 PRF Set,密钥类型不同于同一算法的 MAC 密钥类型,因为它不包含输出长度。PRF 集键永远不会添加 Tink 输出前缀。这可确保输出实际上是 PRF。

混合加密

Tink 混合加密的一般线格格式如下:

prefix || encapsulated_key || encrypted_data

prefix 为空或 5 字节的 Tink 输出前缀。每种键类型都包含有关要解析的字节数以及如何从 encapsulated_key 解析这些字节的信息。

HPKE(混合公钥加密)

Tink 遵循 RFC 9180 中定义的 HPKE 标准。HPKE 密码套件包含以下三个基元。

  • 密钥封装机制 (KEM)
  • 密钥派生函数 (KDF)
  • 关联数据加密的身份验证 (AEAD)

HPKE 标准未在 RFC 9180 第 10 节中定义通用线格式。Tink 的 HPKE 实现使用以下 encapsulated_keyencrypted_data 值。

  • encapsulated_key
    • 发件人的序列化公钥
    • RFC 9180 的第 4.1 节中定义为 enc
    • 格式由所使用的特定 HPKE KEM 决定
  • encrypted_data
    • 密文和标签(即ciphertext || tag(不含 IV)
    • RFC 9180 第 4 节中定义为 ct
    • 格式由所使用的特定 HPKE AEAD 决定
X25519 基于 Diffie-Hellman 的 KEM

对于 X25519 DHKEM,值 enc 是发件人的 32 字节 Diffie-Hellman 公钥。

ECIES-AEAD-HKDF

对于 Tink 的 ECIES-AEAD-HKDF 实现,encapsulated_key 是密钥封装机制 (KEM) 的输出,encrypted_data 是数据封装机制 (DEM) 的输出。

KEM

根据密钥类型,Tink 会使用压缩和未压缩的椭圆曲线点,遵循 RFC 8422/ANSI.X9-62.2005 编码标准。对于未压缩的点,字节 0x04 后跟 xy 坐标(以固定大小的整数表示)。对于压缩坐标,系统会使用字节 0x020x03 以及作为固定大小整数的 x 坐标。对于 X25519,使用 RFC 7748 定义(x 坐标为固定大小的整数)。

DEM

对于 encrypted_data,Tink 使用与 AEAD 相同的格式。这包括指定 IV。

密钥派生

首先计算共享点的 x 坐标 x_ss。然后,将 AEAD 的键设置为:

HKDF(ikm = encapsulated_key || x_ss, salt = salt_of_key, info = context_info, length = dem_key_size)

其中,encapsulated_key 是字节形式的完整 KEM 输出。

数字签名

Tink 遵循相应的 RFC。基元可能会向生成的代码添加一个 5 字节的 Tink 输出前缀。

ECDSA

ECDSA 签名的格式为 IEEE P1363ASN.1 DER,具体取决于密钥中的 EcdsaSignatureEncoding 字段

IEEE P1363 签名的格式为 r || s,其中 rs 采用零填充,并且以字节为单位的大小与曲线的顺序相同。例如,对于 NIST P-256 曲线,rs 会填充 0 以达到 32 字节。

DER 签名使用 ASN.1 进行编码:

ECDSA-Sig-Value :: = SEQUENCE { r INTEGER, s INTEGER }

具体而言,编码如下:

0x30 || totalLength || 0x02 || r's length || r || 0x02 || s's length || s

Tink 遵循签名验证最佳实践,仅接受 DER 编码的 ECDSA 签名(替代 BER 编码的签名无效)。

这有助于防止签名可变性攻击,这类攻击通常会影响加密货币系统