内核加密 API 架构

密码算法类型

内核加密 API 为以下密码类型提供了不同的 API 调用

  • 对称密码

  • AEAD 密码

  • 消息摘要,包括密钥消息摘要

  • 随机数生成

  • 用户空间接口

密码和模板

内核加密 API 提供了单块密码和消息摘要的实现。此外,内核加密 API 提供了许多“模板”,这些模板可以与单块密码和消息摘要结合使用。模板包括所有类型的块链接模式、HMAC 机制等。

单块密码和消息摘要可以由调用者直接使用,也可以与模板一起调用以形成多块密码或密钥消息摘要。

一个单块密码甚至可以用多个模板调用。但是,没有单块密码就不能使用模板。

请参阅 /proc/crypto 并搜索“name”。例如

  • aes

  • ecb(aes)

  • cmac(aes)

  • ccm(aes)

  • rfc4106(gcm(aes))

  • sha1

  • hmac(sha1)

  • authenc(hmac(sha1),cbc(aes))

在这些示例中,“aes”和“sha1”是密码,所有其他都是模板。

同步和异步操作

内核加密 API 提供同步和异步 API 操作。

使用同步 API 操作时,调用者调用一个密码操作,该操作由内核加密 API 同步执行。这意味着,调用者会等待直到密码操作完成。因此,内核加密 API 调用就像常规函数调用一样工作。对于同步操作,API 调用集很小,概念上类似于任何其他加密库。

内核加密 API 提供了异步操作,这意味着密码操作的调用将几乎立即完成。该调用会触发密码操作,但不会发出其完成的信号。在调用密码操作之前,调用者必须提供一个回调函数,内核加密 API 可以调用该回调函数来发出密码操作完成的信号。此外,调用者必须确保它可以通过在其数据周围应用适当的锁定来处理此类异步事件。内核加密 API 不会执行任何特殊的序列化操作来保护调用者的数据完整性。

加密 API 密码引用和优先级

调用者使用字符串引用密码。该字符串具有以下语义

template(single block cipher)

其中“模板”和“单块密码”分别是上述模板和单块密码。如果适用,其他模板可以包含其他模板,例如

template1(template2(single block cipher)))

内核加密 API 可以提供模板或单块密码的多个实现。例如,较新的英特尔硬件上的 AES 具有以下实现:AES-NI、汇编程序实现或直接 C。现在,当在内核加密 API 中使用字符串“aes”时,会使用哪个密码实现?该问题的答案是内核加密 API 为每个密码实现分配的优先级编号。当调用者在初始化密码句柄期间使用该字符串引用密码时,内核加密 API 会查找提供具有该名称的实现的所有实现,并选择优先级最高的实现。

现在,调用者可能需要引用特定的密码实现,因此不想依赖基于优先级的选择。为了适应这种情况,内核加密 API 允许密码实现除了常用名称之外还注册一个唯一名称。因此,当使用该唯一名称时,调用者始终可以确保引用的是预期的密码实现。

可用密码的列表在 /proc/crypto 中给出。但是,该列表并未指定模板和密码的所有可能排列。/proc/crypto 中列出的每个块可能包含以下信息——如果以下列出的组件之一不适用于密码,则不会显示

  • name:密码的通用名称,该名称受基于优先级的选择约束——此名称可以由密码分配 API 调用使用(上面列出的所有名称都是此类通用名称的示例)

  • driver:密码的唯一名称——此名称可以由密码分配 API 调用使用

  • module:提供密码实现的内核模块(对于静态链接的密码,则为“kernel”)

  • priority:密码实现的优先级值

  • refcnt:相应密码的引用计数(即,此密码的当前使用者数量)

  • selftest:指定密码的自检是否通过

  • type

    • skcipher 用于对称密钥密码

    • cipher 用于可以与附加模板一起使用的单块密码

    • shash 用于同步消息摘要

    • ahash 用于异步消息摘要

    • aead 用于 AEAD 密码类型

    • compression 用于压缩类型转换

    • rng 用于随机数生成器

    • kpp 用于密钥协商协议原语 (KPP) 密码,例如 ECDH 或 DH 实现

  • blocksize:密码的块大小(以字节为单位)

  • keysize:密钥大小(以字节为单位)

  • ivsize:IV 大小(以字节为单位)

  • seedsize:随机数生成器所需的种子数据大小

  • digestsize:消息摘要的输出大小

  • geniv:IV 生成器(已过时)

密钥大小

在分配密码句柄时,调用者仅指定密码类型。但是,对称密码通常支持多种密钥大小(例如,AES-128 与 AES-192 与 AES-256)。这些密钥大小由提供的密钥的长度确定。因此,内核加密 API 不提供单独的方法来选择特定的对称密码密钥大小。

密码分配类型和掩码

不同的密码句柄分配函数允许指定类型和掩码标志。这两个参数具有以下含义(因此在后续章节中不再赘述)。

type 标志指定密码算法的类型。当调用者想要默认处理时,通常提供 0。否则,调用者可以提供以下与上述密码类型匹配的选择

  • CRYPTO_ALG_TYPE_CIPHER 单块密码

  • CRYPTO_ALG_TYPE_COMPRESS 压缩

  • CRYPTO_ALG_TYPE_AEAD 带有关联数据 (MAC) 的身份验证加密

  • CRYPTO_ALG_TYPE_KPP 密钥协商协议原语 (KPP),例如 ECDH 或 DH 实现

  • CRYPTO_ALG_TYPE_HASH 原始消息摘要

  • CRYPTO_ALG_TYPE_SHASH 同步多块哈希

  • CRYPTO_ALG_TYPE_AHASH 异步多块哈希

  • CRYPTO_ALG_TYPE_RNG 随机数生成

  • CRYPTO_ALG_TYPE_AKCIPHER 非对称密码

  • CRYPTO_ALG_TYPE_SIG 非对称签名

  • CRYPTO_ALG_TYPE_PCOMPRESS CRYPTO_ALG_TYPE_COMPRESS 的增强版本,允许进行分段压缩/解压缩,而不是仅对一个段执行操作。CRYPTO_ALG_TYPE_PCOMPRESS 旨在在现有使用者转换后替换 CRYPTO_ALG_TYPE_COMPRESS。

掩码标志限制密码类型。唯一允许的标志是 CRYPTO_ALG_ASYNC,用于将密码查找函数限制为异步密码。通常,调用者为掩码标志提供 0。

当调用者提供掩码和类型规范时,调用者限制了内核加密 API 可以为给定密码名称执行的合适密码实现的搜索。这意味着,即使调用者在其初始化调用期间使用了一个存在的密码名称,内核加密 API 也可能由于使用的类型和掩码字段而不会选择它。

内核加密 API 的内部结构

内核加密 API 具有内部结构,其中密码实现可以使用许多层和间接。本节应有助于阐明内核加密 API 如何使用各种组件来实现完整的密码。

以下小节基于现有密码实现解释了内部结构。第一节讨论最复杂的情况,其中所有其他情况都形成一个逻辑子集。

通用 AEAD 密码结构

以下 ASCII 图分解了使用带有自动 IV 生成的 AEAD 密码时的内核加密 API 层。IPSEC 层使用了所示示例。

对于 AEAD 密码的其他用例,ASCII 图也适用,但调用者可能不会使用带有单独 IV 生成器的 AEAD 密码。在这种情况下,调用者必须生成 IV。

所描述的示例基于通用的 C 实现(gcm.c、aes-generic.c、ctr.c、ghash-generic.c、seqiv.c)分解了 GCM(AES) 的 AEAD 密码。通用实现作为一个示例,展示了内核密码 API 的完整逻辑。

某些精简的密码实现(如 AES-NI)可能会提供将内核密码 API 视角下无法分解为多层的方面合并的实现。在 AES-NI 实现的情况下,CTR 模式、GHASH 实现和 AES 密码都被合并到一个在内核密码 API 中注册的密码实现中。在这种情况下,以下 ASCII 图描述的概念也适用。但是,内核密码 API 不再将 GCM 分解为各个子组件。

以下 ASCII 图中的每个块都是从内核密码 API 获取的独立密码实例。调用者或其他块使用内核密码 API 为密码实现类型定义的 API 函数访问每个块。

下面的块指示密码类型以及密码中实现的特定逻辑。

ASCII 图还指示了调用结构,即谁调用哪个组件。箭头指向被调用的块,其中调用者使用适用于该块指定的密码类型的 API。

kernel crypto API                                |   IPSEC Layer
                                                 |
+-----------+                                    |
|           |            (1)
|   aead    | <-----------------------------------  esp_output
|  (seqiv)  | ---+
+-----------+    |
                 | (2)
+-----------+    |
|           | <--+                (2)
|   aead    | <-----------------------------------  esp_input
|   (gcm)   | ------------+
+-----------+             |
      | (3)               | (5)
      v                   v
+-----------+       +-----------+
|           |       |           |
|  skcipher |       |   ahash   |
|   (ctr)   | ---+  |  (ghash)  |
+-----------+    |  +-----------+
                 |
+-----------+    | (4)
|           | <--+
|   cipher  |
|   (aes)   |
+-----------+

当 IPSEC 层使用 esp_output 函数触发加密操作时,以下调用顺序适用。在配置期间,管理员设置使用 seqiv(rfc4106(gcm(aes))) 作为 ESP 的密码。以下调用顺序现在在上面的 ASCII 图中描述。

  1. esp_output() 调用 crypto_aead_encrypt() 以触发带有 IV 生成器的 AEAD 密码的加密操作。

    SEQIV 生成 IV。

  2. 现在,SEQIV 使用 AEAD API 函数调用来调用相关的 AEAD 密码。在我们的例子中,在 SEQIV 实例化期间,GCM 的密码句柄被提供给 SEQIV。这意味着 SEQIV 使用 GCM 密码句柄调用 AEAD 密码操作。

    在 GCM 句柄实例化期间,CTR(AES) 和 GHASH 密码被实例化。CTR(AES) 和 GHASH 的密码句柄被保留供以后使用。

    GCM 实现负责以正确的方式调用 CTR 模式 AES 和 GHASH 密码以实现 GCM 规范。

  3. GCM AEAD 密码类型实现现在使用实例化的 CTR(AES) 密码句柄调用 SKCIPHER API。

    在 CTR(AES) 密码实例化期间,AES 的 CIPHER 类型实现被实例化。AES 的密码句柄被保留。

    这意味着 CTR(AES) 的 SKCIPHER 实现仅实现 CTR 块链接模式。在执行块链接操作后,将调用 AES 的 CIPHER 实现。

  4. CTR(AES) 的 SKCIPHER 现在使用 AES 密码句柄调用 CIPHER API 来加密一个块。

  5. GCM AEAD 实现还通过 AHASH API 调用 GHASH 密码实现。

当 IPSEC 层触发 esp_input() 函数时,遵循相同的调用顺序,唯一的区别是操作从步骤 (2) 开始。

通用块密码结构

通用块密码遵循与上面 ASCII 图描述的概念相同的概念。

例如,CBC(AES) 是使用 cbc.c 和 aes-generic.c 实现的。上面的 ASCII 图也适用,区别仅在于只使用了步骤 (4),并且 SKCIPHER 块链接模式是 CBC。

通用键控消息摘要结构

键控消息摘要实现也遵循与上面 ASCII 图中描述的概念相同的概念。

例如,HMAC(SHA256) 是使用 hmac.c 和 sha256_generic.c 实现的。以下 ASCII 图说明了该实现

kernel crypto API            |       Caller
                             |
+-----------+         (1)    |
|           | <------------------  some_function
|   ahash   |
|   (hmac)  | ---+
+-----------+    |
                 | (2)
+-----------+    |
|           | <--+
|   shash   |
|  (sha256) |
+-----------+

当调用者触发 HMAC 操作时,以下调用顺序适用

  1. 调用者调用 AHASH API 函数。HMAC 实现根据需要执行其操作。

    在初始化 HMAC 密码期间,SHA256 的 SHASH 密码类型被实例化。SHA256 实例的密码句柄被保留。

    在某个时候,HMAC 实现需要 SHA256 操作,其中使用了 SHA256 密码句柄。

  2. HMAC 实例现在使用 SHA256 密码句柄调用 SHASH API 来计算消息摘要。