Developing Cipher Algorithms
注册和注销转换
加密 API 中有三种不同类型的注册函数。一个用于注册通用加密转换,而另外两个特定于 HASH 转换和 COMPRESSion。我们将在单独的一章中讨论后两者,这里我们只看通用的。
在讨论注册函数之前,需要考虑要填充每个注册函数的数据结构 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,失败时为负数错误码。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、…
本节描述了多块密码转换的实现方式。多块密码用于对提供给转换函数的散列列表中的数据(scatterlists of data)进行操作的转换。它们还将结果输出到一个散列列表中。
注册细节
多块密码算法的注册是整个加密 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)的各个块时使用。
ref: https://www.kernel.org/doc/html/latest/crypto/devel-algos.html