原语和接口
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
接下来,我们要定义(非正式,但之后更加正式)的两个重要部分,
Tink、Primitive 和 Interface 中使用的语言。
原初
基元是与所有算法对应的数学对象
安全地执行某项任务。例如,AEAD 基元包含所有
一种加密算法,这些算法满足Tink 所要求的安全属性
Aead 的组件。
我们强调,原语并不受限于某种编程语言,
方式
访问它们。相反,我们应该将基元视为
数学对象。例如,如果我们考虑 AEAD,它从根本上
由函数对组成,一个用于执行加密,另一个用于
执行解密。
接口
接口是为用户提供对基元的访问权限的一种方式。
例如,我们预计 Tink 将来将提供一个 Mac
接口,
还有一个 StreamingMac
接口,用于计算
不会直接加载到内存中的数据
请注意,我们在此处明确区分了接口和基元。这应该
以明确这两个接口所提供的数学对象
访问方式是一样的
对于大多数读者来说,以上直观的解释可能就足够了。
尽管如此,我们认为有时有必要提供正式
这些概念的定义。
加密函数
加密函数的概念不如
但我们需要引入它才能正式定义基元。
- 加密函数
加密函数是映射,
\[ f: {\bf K} \times {\bf R} \times {\bf I} \to {\bf O}\]
从集合 \({\bf K}\) (键空间)、集合 \({\bf R} = \{0,1\}^{\infty}\)
(随机性,我们假定这是无限位串的集合),以及
set \({\bf I}\) (输入空间)设为 set \({\bf O}\) (输出空间)。
稍后我们将清楚我们添加特定随机性参数的原因。
例如,我们展示了一种可能性,即如何将这些概念映射到
AES-GCM。对于每个有效的密钥大小 \(s_k\)、Nonce 大小 \(s_n\)和标记大小
\(s_t\),AES-GCM 包含两个加密函数,一个用于
一个用于加密,另一个用于解密两者将具有相同的键空间 \({\bf
K} = \{0,1\}^{s_k}\)。
对于加密函数 \(\mathrm{Enc}\), \(s_n\)
将使用随机性来选择 Nonce。
让我们用 \({\bf B} = \{0,1\}^8\) 表示一个字节。
加密函数的输入空间是任意长度的 \({\bf I} = {\bf B}^{*}
\times {\bf B}^{*}\) 字节字符串对。
键值对中的第一个元素是消息,第二个元素是
相关数据AES-GCM 标准对
但我们希望允许任意长度,
输出空间中的错误符号 \(\bot\) 。然后,输出空间变为 \({\bf
O} = {\bf B}^* \cup \{\bot\}\),我们可以在其中任意定义
以标准中给出的串联的形式 \((\mathrm{IV} \|
\mathrm{ciphertext} \| \mathrm{tag})\) 成功的计算,然后输出
\(\bot\),以防某些输入过长。因此,对于固定键,
加密函数将变为 \(\mathrm{Enc}_k : {\bf R} \times {\bf B}^*
\times {\bf B}^* \rightarrow {\bf B}^* \cup \{\bot\}\)类型。
对于解密函数, \(\mathrm{Dec}\) 密钥空间也是如此。通过
恰好,输入空间是相同的: \({\bf I} ={\bf B}^* \times {\bf B}^*\),
但现在,第一个元素本着加密函数的输出,
而第二个仍然是关联的数据。
输出空间也恰好是相同的 \({\bf O} = {\bf B}^* \cup
\{\bot\}\) (再次强调)。解读方式有点不同
as \(\bot\) 通常表示身份验证错误(但也会是
(以防某些输入内容过长)。
我们强调,上述规范化不是将
标准。例如,您可以将 Nonce 视为输入的一部分,
(这会产生截然不同的基元)。
或者,您也可以将输出定义为包含 Nonce 的三元组,
密文和标记(而不是串联)。或者,您也可以
将键空间(随意地)限制为
\({\bf K} = \{0,1\}^{128} \cup \{0,1\}^{256}\)。
- 加密算法:
(对称)加密算法是一个元组
\[(f_1, ... f_k)\]
一组加密函数,其中所有函数具有相同的密钥空间。通过
加密算法的类型是元组 \((({\bf I}_1, {\bf
O}_1), \ldots, ({\bf I}_k, {\bf O}_k))\)。
例如,对于密钥、Nonce 和标记的每个有效的 \((s_k, s_n, s_t)\) 三元组
AES-GCM\({}_{s_k, s_n, s_t}\) 是一种采用
上述两个函数 \(\mathrm{Enc}\) 和 \(\mathrm{Dec}\) 。
基元和接口
接下来定义加密基元。
- 原初
- 基元基元是一组加密算法,其所有算法
具有相同类型 \((({\bf I}_1, {\bf O}_1), \ldots, ({\bf I}_k, {\bf
O}_k))\),并且算法的关键空间是两两不相交的。
以 Tink 中的 \(\mathrm{AEAD}\) 基元为例。它有多个
算法,其中包括适用于 128 位和 256 位密钥大小的 AES-GCM 和 Nonce
大小为 96 位、具有某些密钥大小的 AES-EAX 以及 XChaCha20Poly1305。它们具有
不相交的密钥空间,但都提供相同的加密函数
\(\mathrm{Enc}\) 和 \(\mathrm{Dec}\)。(我们看不出
折叠了 AES-GCM 的不同密钥大小,但是
当然也可以这样做)。
定义基元
基元的常用思路是先定义
然后简单地将原语视为全部
此类算法。
例如,对于 AEAD,我们会说 \(\mathrm{Dec}_k(\mathrm{Enc}_k(m, a),
a) = m\) 为“always”(除非明文 \(m\)
长)。此外,我们还提供安全属性:例如,对于
那么加密在语义上是安全的。
AEAD 基元就是所有加密算法的集合,
这两种属性换言之,在实践中,
我们根据属性来定义它。我们不提供
如定义所暗示的那样。
接口
Tink 中的接口提供了对基元的访问权限,因为它允许
从输入空间计算输出空间的一个元素。例如:
以 Java 中的 AEAD 接口为例:
public interface Aead {
byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;
byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;
}
请注意,我们不提供对随机性的访问权限。而是允许用户
用于提供输入空间的元素。禁止获取随机性内容
课程。1
Tink 有时会为单个基元提供多个接口。
此功能非常有用,因为要求有时会不同。不过,这样做
是有代价的:一般来说,提供的接口越多,越低越好。
互操作性。例如,假设
有人基于 Tink 编写了一个库,并且该库要求用户传入
Aead
对象(用于在内部加密内容)。如果 Tink 提供的
\(\mathrm{AEAD}\) 基元的不同接口,概率很高
确保用户
没有适用于用户选取的键和
库。因此,您需要做出权衡,添加更多接口。
如未另行说明,那么本页面中的内容已根据知识共享署名 4.0 许可获得了许可,并且代码示例已根据 Apache 2.0 许可获得了许可。有关详情,请参阅 Google 开发者网站政策。Java 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-07-25。
[null,null,["最后更新时间 (UTC):2025-07-25。"],[[["\u003cp\u003eTink uses Primitives, which are mathematical representations of secure algorithms, and Interfaces, which provide user access to these Primitives.\u003c/p\u003e\n"],["\u003cp\u003ePrimitives are defined by the security properties they satisfy, for example, AEAD requires semantic security and correct decryption.\u003c/p\u003e\n"],["\u003cp\u003eInterfaces in Tink provide controlled access to Primitives, often abstracting away internal details like randomness generation for security reasons.\u003c/p\u003e\n"],["\u003cp\u003eOffering multiple Interfaces for the same Primitive can improve flexibility but may reduce interoperability between different Tink users or libraries.\u003c/p\u003e\n"],["\u003cp\u003eWhile Primitives are abstract and language-independent, Interfaces are specific to a programming language and provide a way for users to interact with the underlying algorithms.\u003c/p\u003e\n"]]],["Tink defines *Primitives* as mathematical objects representing secure algorithms, like AEAD, which consists of encryption/decryption functions. *Interfaces* are the means to access these primitives. A *Cryptographic Function* maps keys, randomness, and input to output. A *Cryptographic Algorithm* is a tuple of such functions with the same key space. A *Primitive* is a set of Cryptographic Algorithms that have the same type and disjoint key spaces. Interfaces grant users access to the primitive's output, without providing access to randomness.\n"],null,["# Primitives and Interfaces\n\nWe next define (informally, but then more formally), two important pieces of\nthe language used in Tink, the *Primitive* and the *Interface*.\n\nPrimitive\n---------\n\nA primitive is a *mathematical* object corresponding to all algorithms\nperforming some task securely. For example, the AEAD primitive consists of all\nencryption algorithms which satisfy [the security properties which Tink requires\nof an Aead](https://developers.google.com/tink/aead#minimal_security_guarantees).\n\nWe stress that primitives are not bound to a programming language, or a specific\nway of\naccessing them. Instead, one should think of the primitive as the purely\nmathematical object. For example, if we consider AEAD, fundamentally it will\nconsist of pairs of functions, one which performs encryption, and one which\nperforms decryption.\n\nInterfaces\n----------\n\nAn interface is a way in which we provide users access to a primitive.\nFor example, we expect that in the future Tink will provides a `Mac` interface,\nbut also a `StreamingMac` interface, which allows to compute the mac of\ndata which is not directly loaded into memory.\n\nNote that we explicitly distinguish interfaces and primitives here. This should\nmake clear that the mathematical object to which these two interfaces give\naccess are the same.\n\nFormal definitions\n------------------\n\nFor most readers, the above intuitive explanations are probably enough.\nNevertheless, we feel that it can be important sometimes to provide formal\ndefinitions of these concepts.\n\n### Cryptographic functions\n\nThe concept of a cryptographic function is not as important as the concept of a\nprimitive, but we need to introduce it to formally define primitive.\n\nCryptographic Function\n\n: A *cryptographic function* is a map\n\n \\\\\\[ f: {\\\\bf K} \\\\times {\\\\bf R} \\\\times {\\\\bf I} \\\\to {\\\\bf O}\\\\\\]\n\n from a set \\\\({\\\\bf K}\\\\) (the key space), a set \\\\({\\\\bf R} = \\\\{0,1\\\\}\\^{\\\\infty}\\\\)\n (randomness, which we assume to be the set of infinite bitstrings), and a\n set \\\\({\\\\bf I}\\\\) (the input space), to a set \\\\({\\\\bf O}\\\\) (the output space).\n\nIt will become clear later why we added a specific randomness parameter.\n\nAs an example, we show one possibility how these concepts can be mapped to\nAES-GCM. For each valid key size \\\\(s_k\\\\), nonce size \\\\(s_n\\\\), and tag size\n\\\\(s_t\\\\), AES-GCM consists of two cryptographic functions, one for\nencryption, and one for decryption. Both will have the same key space \\\\({\\\\bf\nK} = \\\\{0,1\\\\}\\^{s_k}\\\\).\n\nFor the encryption function \\\\(\\\\mathrm{Enc}\\\\), the first \\\\(s_n\\\\) bits of\nrandomness will be used to select the nonce.\n\nLet \\\\({\\\\bf B} = \\\\{0,1\\\\}\\^8\\\\) denote a byte.\nThe input space of the encryption function is the pairs \\\\({\\\\bf I} = {\\\\bf B}\\^{\\*}\n\\\\times {\\\\bf B}\\^{\\*}\\\\) of pairs of byte strings of arbitrary length.\nThe first element of the pair is meant to be the message, the second element is\nthe associated data. The AES-GCM standard has an upper limit on the lengths of\nthe inputs, but we prefer to allow arbitrary lengths, and instead add a special\nerror symbol \\\\(\\\\bot\\\\) to the output space. The output space then becomes \\\\({\\\\bf\nO} = {\\\\bf B}\\^\\* \\\\cup \\\\{\\\\bot\\\\}\\\\), where we arbitrarily define the result of\nsuccessful computations as the concatenation \\\\((\\\\mathrm{IV} \\\\\\|\n\\\\mathrm{ciphertext} \\\\\\| \\\\mathrm{tag})\\\\) as given in the standard, and output\n\\\\(\\\\bot\\\\), in case some input is too long. Hence, for a fixed key, the\nencryption function becomes of type \\\\(\\\\mathrm{Enc}_k : {\\\\bf R} \\\\times {\\\\bf B}\\^\\*\n\\\\times {\\\\bf B}\\^\\* \\\\rightarrow {\\\\bf B}\\^\\* \\\\cup \\\\{\\\\bot\\\\}\\\\).\n\nFor the decryption function \\\\(\\\\mathrm{Dec}\\\\) the key space is the same. The\ninput space coincidentally is the same: \\\\({\\\\bf I} ={\\\\bf B}\\^\\* \\\\times {\\\\bf B}\\^\\*\\\\),\nbut now the first element is meant to be the output of the encryption function,\nwhile the second one is still the associated data.\n\nThe output space also happens to be the same \\\\({\\\\bf O} = {\\\\bf B}\\^\\* \\\\cup\n\\\\{\\\\bot\\\\}\\\\) (again a coincidence). The interpretation is somewhat different,\nas \\\\(\\\\bot\\\\) usually denotes an authentication error (though it will also be the\noutput in case some input is too long).\n\nWe stress that the above formalization is *not* the only option to formalize the\nstandard. For example, one could consider the nonce a part of the input, instead\nof reading it from the randomness (which results in a very different primitive).\nAlternatively, one could define the output as a triple containing the nonce,\nthe ciphertext, and the tag (instead of the concatenation). Or one could\nrestrict the key space (somewhat arbitrarily) to\n\\\\({\\\\bf K} = \\\\{0,1\\\\}\\^{128} \\\\cup \\\\{0,1\\\\}\\^{256}\\\\).\n\nCryptographic Algorithm:\n\n: A (symmetric) *cryptographic algorithm* is a tuple\n\n \\\\\\[(f_1, ... f_k)\\\\\\]\n\n of cryptographic functions, where all functions have the same key space. The\n *type* of the cryptographic algorithm is the tuple \\\\((({\\\\bf I}_1, {\\\\bf\n O}_1), \\\\ldots, ({\\\\bf I}_k, {\\\\bf O}_k))\\\\).\n\nFor example, for each valid triple \\\\((s_k, s_n, s_t)\\\\) of key, nonce, and tag\nsize, AES-GCM\\\\({}_{s_k, s_n, s_t}\\\\) is a cryptographic algorithm with the\ntwo functions \\\\(\\\\mathrm{Enc}\\\\) and \\\\(\\\\mathrm{Dec}\\\\) described above.\n\n### Primitives and interfaces\n\nWe next define a cryptographic primitive.\n\nPrimitive\n: A *primitive* is a set of cryptographic algorithms, where all the algorithms\n have the same type \\\\((({\\\\bf I}_1, {\\\\bf O}_1), \\\\ldots, ({\\\\bf I}_k, {\\\\bf\n O}_k))\\\\), and the key spaces of the algorithms are pairwise disjoint.\n\nAs an example, consider the \\\\(\\\\mathrm{AEAD}\\\\) primitive in Tink. It has multiple\nalgorithms, among those are AES-GCM for key sizes 128 and 256 bits, with nonce\nsize 96 bits, AES-EAX with some key sizes, and XChaCha20Poly1305. They have\ndisjoint key spaces, but all provide the same cryptographic functions\n\\\\(\\\\mathrm{Enc}\\\\) and \\\\(\\\\mathrm{Dec}\\\\). (We do not see a purpose in somehow\ncollapsing different key sizes of AES-GCM in this formal discussion, but of\ncourse one could do so).\n\n#### Defining primitives\n\nThe usual way of thinking of primitives is to first define properties of the\ncryptographic functions, and then simply considering the primitive to be all\nsuch algorithms.\n\nFor example, for AEAD we would say that \\\\(\\\\mathrm{Dec}_k(\\\\mathrm{Enc}_k(m, a),\na) = m\\\\) is 'always' satisfied (except e.g. if the plaintext \\\\(m\\\\) is too\nlong). In addition, we have security properties; for example, for\na random key, the encryption is semantially secure.\n\nThe AEAD primitive is then simply the set of all cryptographic algorithms which\nsatisfy these properties. In other words, in practice, when we define a specific\nprimitive, we define it based on properties. We do not give a list of\nalgorithms, as the definition suggests.\n\n#### Interfaces\n\nAn *interface* in Tink gives access to a primitive, in the sense that it allows\nto compute an element of the output space from the input space. For example,\nconsider the AEAD interface in Java: \n\n public interface Aead {\n byte[] encrypt(byte[] plaintext, byte[] associated_data) throws GeneralSecurityException;\n byte[] decrypt(byte[] ciphertext, byte[] associated_data) throws GeneralSecurityException;\n }\n\nNote that we do not give access to the randomness. Instead, we allow the user to\nprovide elements of the input space. Disallowing access to the randomness is of\ncourse on purpose.^[1](#fn1)^\n\nTink sometimes offers multiple interfaces for a single primitive.\nThis can be very useful, as requirements sometimes differ. Still, doing this\ncomes at a price: in general, the more interfaces one offers, the lower\ninteroperability is. For example, imagine\nthat someone writes a library based on Tink that requires the user to pass in an\n`Aead` object (to encrypt something internally). If Tink offers too many\ndifferent interfaces to the \\\\(\\\\mathrm{AEAD}\\\\) primitive, chances are high\nthat the user\ndoes not have an instance ready which works for the key the user picked and the\nlibrary at the same time. Hence, adding more interfaces is a trade-off. \n\n*** ** * ** ***\n\n1. AEAD ciphers have the property that they are secure\n against chosen ciphertext attacks, which is guaranteed only if there is no\n reuse of the nonce. The Aead interface in Tink is designed such that it\n prevents nonce reuse: the user cannot provide a nonce as input for encryption,\n instead, a new nonce is randomly generated for each encrypt operation. [↩](#fnref1)"]]