一致性

Tink 在所有编程语言中的行为都“相同”,这一点非常重要。这个概念并不简单,但最关键的是:

为了保持一致性,Tink 使用跨语言测试,这些测试可在跨语言 GitHub 代码库中找到。这些测试可验证不同语言的一致性和互操作性。

然而,定义一致性并不像人们预期的那样简单。 因此,本页面提供了我们的工作定义。基本上,Tink 提供两种类型的一致性:

上下文

概括来讲,Tink 提供以下 API:

  • 密钥集操作:Tink 提供了用于向密钥集添加新密钥、从密钥集中移除密钥以及更改密钥集中的主密钥的 API。

  • 密钥集序列化:Tink 提供了 API,用于将密钥集序列化为字节序列,反之亦然,可从字节序列解析密钥集。

  • 原语创建:Tink 提供了一个 API,用于从密钥集创建原语接口。例如,如需在 Java 中通过密钥集创建 Aead 对象,用户需要调用 keysetHandle.getPrimitive(config, Aead.class)。

  • 原语用法:在原语创建步骤中生成的接口提供了一个用于执行加密操作的 API。

关于这些 API,有两个重要问题需要考虑:

  • 创建:对于给定的序列化密钥集、语言和原语,是否可以使用该语言从此密钥集创建原语?

  • 评估:如果可以从给定密钥集以某种语言创建原语,则原语对象的行为如何?

请务必注意,Tink 在解析密钥集时无法保证一致性。例如,Tink C++

  • 成功解析包含 CHACHA20-POLY1305 密钥的密钥集,即使 Tink 中未实现 CHACHA20-POLY1305 AEAD 操作;
  • 成功解析密钥长度为 1 字节的密钥集,这将在所有加密操作中失败。

这些行为可能会在次要版本中发生变化。

评估一致性

评估的一致性比创建过程中的任何一致性都更重要:如果 Java 中的 AEAD 无法解密 C++ 中 AEAD 的加密,用户就会遇到严重问题。

一般来说,Tink 的目标是以显而易见的方式保持原语的一致性。例如,如果 C++ 二进制文件使用 public_key_sign->Sign(data) 计算签名,则相应的 Java 验证调用 publicKeyVerify.verify(signature, data) 应该会成功。

不过,即使在这种情况下,也有一些注意事项。例如,Java 中 aead.Encrypt 的返回值类型为 byte[]。根据 Java 语言规范 (JLS) §10.7,数组的长度为 int 类型,根据 §JLS 4.2.1,其最大值为 2147483647。因此,在 Java 中,长度为 2147483647 的数组加密会失败:加密会产生一些开销,这意味着输出会过长。不过,在其他语言中,此类输入可以成功加密。

因此,Tink 可提供评估一致性:对于给定的密钥集,如果以两种语言成功创建原语,则它们的行为相同。

例外情况是,某些操作在某些特殊情况下可能会失败。

创作一致性

并非所有密钥集都能成功创建原语。例如,如果密钥材料的长度为 128 位,Tink 不允许用户创建 AesSivKey。

不过,Tink 提供了以下一致性:如果两种语言都支持某种密钥类型,则可以为其创建原语的密钥集是一致的。当然,如果某种语言不支持某个密钥类型,则无法创建任何原语对象。

创建一致性不如评估一致性重要,并且此规则的例外情况比评估一致性更多。这些限制记录在我们支持的密钥类型列表中。例如,对于密钥类型 ECIES,Tink 提供了用于密钥协商的椭圆曲线选项,但 Java 不支持 X25519。因此,在 Java 中使用 X25519 创建密钥会失败。


  1. 在本文档中,我们使用术语 Keyset 来表示在大多数语言中称为 KeysetHandle 的对象。 ↩