开发密码算法¶
注册和注销转换¶
密码 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。