开发密码算法¶
注册与注销变换¶
加密 API 中有三种不同类型的注册函数。一种用于注册通用密码变换,而另外两种则特定于 HASH 变换和压缩。我们将在单独的章节中讨论后两者,此处仅介绍通用类型。
在讨论注册函数之前,必须考虑用于填充每个注册函数的数据结构,即 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, ...
本节描述了多块密码变换的实现。多块密码用于对提供给变换函数的数据 scatterlist 进行操作的变换。它们也将结果输出到数据 scatterlist 中。
注册细节¶
多块密码算法的注册是整个加密 API 中最标准的程序之一。
请注意,如果密码实现需要适当的数据对齐,调用者应使用 crypto_skcipher_alignmask() 函数来识别内存对齐掩码。内核加密 API 能够处理未对齐的请求。然而,这会带来额外的开销,因为内核加密 API 需要执行数据重新对齐,这可能意味着数据移动。
使用 struct skcipher_alg 定义密码¶
Struct skcipher_alg 定义了一个多块密码,或者更一般地说,一个保持长度的对称密码算法。
Scatterlist 处理¶
某些驱动程序会希望使用通用 ScatterWalk,以防硬件需要分别处理包含明文和将包含密文的 scatterlist 块。请参考 Linux 内核 scatter / gather 列表实现提供的 ScatterWalk 接口。
哈希 [HASH]¶
变换示例:crc32, md5, sha1, sha256,...
注册与注销变换¶
注册 HASH 变换有多种方式,取决于变换是同步 [SHASH] 还是异步 [AHASH],以及我们正在注册的 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,以防实现需要分别处理包含输入数据的 scatterlist 块。