开发密码算法

注册和注销转换

密码 API 中有三种不同的注册函数类型。一种用于注册通用的密码转换,而另外两种专门用于 HASH 转换和 COMPRESS 压缩。我们将在单独的章节中讨论后两种,这里我们只看通用的注册函数。

在讨论注册函数之前,必须考虑要填充每个函数的数据结构,struct crypto_alg,—— 请参阅下面的此数据结构的描述。

通用的注册函数可以在 include/linux/crypto.h 中找到,它们的定义如下所示。前一个函数注册单个转换,而后一个函数则处理转换描述的数组。后者在批量注册转换时很有用,例如当驱动程序实现多个转换时。

int crypto_register_alg(struct crypto_alg *alg);
int crypto_register_algs(struct crypto_alg *algs, int count);

这些函数的对应注销函数如下所示。

void crypto_unregister_alg(struct crypto_alg *alg);
void crypto_unregister_algs(struct crypto_alg *algs, int count);

注册函数在成功时返回 0,失败时返回负的 errno 值。 crypto_register_algs() 仅在成功注册所有给定算法时才成功;如果中途失败,则任何更改都将回滚。

注销函数总是成功,因此它们没有返回值。请不要尝试注销当前未注册的算法。

单块对称密码 [CIPHER]

转换示例:aes、serpent,...

本节介绍所有转换实现中最简单的一种,即用于对称密码的 CIPHER 类型。 CIPHER 类型用于每次只操作一个块的转换,并且块之间没有任何依赖关系。

注册细节

[CIPHER] 算法的注册是特殊的,因为 struct crypto_alg 字段 .cra_type 为空。.cra_u.cipher 必须使用适当的回调来填充以实现此转换。

请参阅下面的 struct cipher_alg

使用 struct cipher_alg 定义密码

Struct cipher_alg 定义了单个块密码。

以下是当从内核的其他部分操作时如何调用这些函数的示意图。请注意,.cia_setkey() 调用可能发生在任何这些示意图发生之前或之后,但绝不能在任何这些调用正在进行时发生。

KEY ---.    PLAINTEXT ---.
       v                 v
 .cia_setkey() -> .cia_encrypt()
                         |
                         '-----> CIPHERTEXT

请注意,多次调用 .cia_setkey() 的模式也是有效的

KEY1 --.    PLAINTEXT1 --.         KEY2 --.    PLAINTEXT2 --.
       v                 v                v                 v
 .cia_setkey() -> .cia_encrypt() -> .cia_setkey() -> .cia_encrypt()
                         |                                  |
                         '---> CIPHERTEXT1                  '---> CIPHERTEXT2

多块密码

转换示例:cbc(aes)、chacha20,...

本节介绍多块密码转换的实现。多块密码用于操作提供给转换函数的数据散列表的转换。它们还将结果输出到数据散列表中。

注册细节

多块密码算法的注册是整个密码 API 中最标准的程序之一。

注意,如果密码实现需要正确的数据对齐,则调用者应使用 crypto_skcipher_alignmask() 函数来标识内存对齐掩码。内核密码 API 能够处理未对齐的请求。然而,这意味着额外的开销,因为内核密码 API 需要执行数据的重新对齐,这可能意味着移动数据。

使用 struct skcipher_alg 定义密码

Struct skcipher_alg 定义了多块密码,或者更广义地说,定义了长度保留的对称密码算法。

散列表处理

如果硬件需要馈送包含明文并将包含密文的散列表的单独块,则某些驱动程序将希望使用通用 ScatterWalk。请参阅 Linux 内核散布/收集列表实现提供的 ScatterWalk 接口。

哈希 [HASH]

转换示例:crc32、md5、sha1、sha256,...

注册和注销转换

根据转换是同步 [SHASH] 还是异步 [AHASH] 以及我们正在注册的 HASH 转换的数量,有多种方法可以注册 HASH 转换。您可以在 include/crypto/internal/hash.h 中找到定义的原型。

int crypto_register_ahash(struct ahash_alg *alg);

int crypto_register_shash(struct shash_alg *alg);
int crypto_register_shashes(struct shash_alg *algs, int count);

用于注销 HASH 转换的相应对应函数如下

void crypto_unregister_ahash(struct ahash_alg *alg);

void crypto_unregister_shash(struct shash_alg *alg);
void crypto_unregister_shashes(struct shash_alg *algs, int count);

使用 struct shash_alg 和 ahash_alg 定义密码

以下是当从内核的其他部分操作时如何调用这些函数的示意图。请注意,.setkey() 调用可能发生在任何这些示意图发生之前或之后,但绝不能在任何这些调用正在进行时发生。请注意,调用 .init() 后立即调用 .final() 也是一个完全有效的转换。

I)   DATA -----------.
                     v
      .init() -> .update() -> .final()      ! .update() might not be called
                  ^    |         |            at all in this scenario.
                  '----'         '---> HASH

II)  DATA -----------.-----------.
                     v           v
      .init() -> .update() -> .finup()      ! .update() may not be called
                  ^    |         |            at all in this scenario.
                  '----'         '---> HASH

III) DATA -----------.
                     v
                 .digest()                  ! The entire process is handled
                     |                        by the .digest() call.
                     '---------------> HASH

以下是当从内核的另一部分使用时如何调用 .export()/.import() 函数的示意图。

KEY--.                 DATA--.
     v                       v                  ! .update() may not be called
 .setkey() -> .init() -> .update() -> .export()   at all in this scenario.
                          ^     |         |
                          '-----'         '--> PARTIAL_HASH

----------- other transformations happen here -----------

PARTIAL_HASH--.   DATA1--.
              v          v
          .import -> .update() -> .final()     ! .update() may not be called
                      ^    |         |           at all in this scenario.
                      '----'         '--> HASH1

PARTIAL_HASH--.   DATA2-.
              v         v
          .import -> .finup()
                        |
                        '---------------> HASH2

请注意,完全可以“放弃”请求对象:- 调用 .init(),然后(多次)调用 .update() - 在未来的任何时候都_不_ 调用 .final()、.finup() 或 .export()

换句话说,实现应注意资源分配和清理。在调用 .init() 或 .update() 之后,不应保留与请求对象相关的任何资源,因为可能没有机会释放它们。

异步 HASH 转换的细节

如果实现需要馈送包含输入数据的散列表的单独块,则某些驱动程序将希望使用通用 ScatterWalk。