一致性

Tink 在所有编程语言中的行为都保持一致非常重要。这个概念并不简单,但最重要的是:

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

但是,定义一致性并不像您想象的那样简单。因此,本页提供了我们的有效定义。基本上,Tink 提供了两种类型的一致性:

上下文

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

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

  • 密钥集序列化:Tink 提供了用于将密钥集序列化为一系列字节的 API,并从一系列字节序列中相反地解析密钥集。

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

  • 原初使用:原初创建步骤中生成的接口提供了一个用于执行加密操作的 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,Tink 提供了用于密钥协议的椭圆曲线选择,但 Java 不支持 X25519。因此,在 Java 中使用 X25519 创建密钥会失败。


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