0%

周谈(27)-内核tcrypt加密算法测试模块代码解读

前言

Linux内核里有一个密码学框架Linux kernel crypto。框架内部提供了常用的标准算法,比如:AES, DES, MD5, SHA1, RSA,DSA等。框架也是支持开发者自定义算法的,通过框架可以注册我们自己的算法,比如国标的SM2, SM3, SM4等, 或者某个公司内部的私有算法。当然了, 也支持对已有的算法重新注册,可以通过优先级设置让用户调用到我们注册的算法。框架代码在内核源码下的crypto目录。

框架提供了一个算法的自检的实现,也就是tcrypt。在我们注册算法的时候,框架会自动使用我们提供的数据执行用例。

tcrypt

tcrypt其实分为两个部分,一个是编译进内核的testmgr.c, 另外一个就是编译成模块的tcrypt.c。

testmgr

在我们往框架注册算法的时候,框架默认就会调用testmgr.c里的alg_test函数,查找对应的测试用例函数,找到之后就会执行用例,检测算法实现是否符合预期。如果没有对应的用例或者用例执行成功的话,则算法直接注册成功,否则注册失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

// 注册算法
int crypto_register_alg(struct crypto_alg *alg)
{
struct crypto_larval *larval;
bool test_started;
int err;

alg->cra_flags &= ~CRYPTO_ALG_DEAD;
err = crypto_check_alg(alg);
if (err)
return err;

down_write(&crypto_alg_sem);
larval = __crypto_register_alg(alg);
test_started = static_key_enabled(&crypto_boot_test_finished);
if (!IS_ERR_OR_NULL(larval))
larval->test_started = test_started;
up_write(&crypto_alg_sem);

if (IS_ERR_OR_NULL(larval))
return PTR_ERR(larval);

if (test_started)
crypto_wait_for_test(larval); // 执行测试
return 0;
}

void crypto_wait_for_test(struct crypto_larval *larval)
{
int err;

err = crypto_probing_notify(CRYPTO_MSG_ALG_REGISTER, larval->adult);
if (WARN_ON_ONCE(err != NOTIFY_STOP))
goto out;

err = wait_for_completion_killable(&larval->completion);
WARN_ON(err);
if (!err)
crypto_notify(CRYPTO_MSG_ALG_LOADED, larval);

out:
crypto_larval_kill(&larval->alg);
}

// 最终调用到这边
static int cryptomgr_test(void *data)
{
struct crypto_test_param *param = data;
u32 type = param->type;
int err = 0;

#ifdef CONFIG_CRYPTO_MANAGER_DISABLE_TESTS
goto skiptest;
#endif

if (type & CRYPTO_ALG_TESTED)
goto skiptest;

err = alg_test(param->driver, param->alg, type, CRYPTO_ALG_TESTED);

skiptest:
crypto_alg_tested(param->driver, err);

kfree(param);
module_put_and_exit(0);
}

我们需要在testmgr.h里面定义测试的几组数据。如下 MD5算法的测试数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/*
* MD5 test vectors from RFC1321
*/
static const struct hash_testvec md5_tv_template[] = {
{
.digest = "\xd4\x1d\x8c\xd9\x8f\x00\xb2\x04"
"\xe9\x80\x09\x98\xec\xf8\x42\x7e",
}, {
.plaintext = "a",
.psize = 1,
.digest = "\x0c\xc1\x75\xb9\xc0\xf1\xb6\xa8"
"\x31\xc3\x99\xe2\x69\x77\x26\x61",
}, {
.plaintext = "abc",
.psize = 3,
.digest = "\x90\x01\x50\x98\x3c\xd2\x4f\xb0"
"\xd6\x96\x3f\x7d\x28\xe1\x7f\x72",
}, {
.plaintext = "message digest",
.psize = 14,
.digest = "\xf9\x6b\x69\x7d\x7c\xb7\x93\x8d"
"\x52\x5a\x2f\x31\xaa\xf1\x61\xd0",
}, {
.plaintext = "abcdefghijklmnopqrstuvwxyz",
.psize = 26,
.digest = "\xc3\xfc\xd3\xd7\x61\x92\xe4\x00"
"\x7d\xfb\x49\x6c\xca\x67\xe1\x3b",
}, {
.plaintext = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
.psize = 62,
.digest = "\xd1\x74\xab\x98\xd2\x77\xd9\xf5"
"\xa5\x61\x1c\x2c\x9f\x41\x9d\x9f",
}, {
.plaintext = "12345678901234567890123456789012345678901234567890123456789012"
"345678901234567890",
.psize = 80,
.digest = "\x57\xed\xf4\xa2\x2b\xe3\xc9\x55"
"\xac\x49\xda\x2e\x21\x07\xb6\x7a",
}

}

然后再在alg_test_descs里面添加映射关系, 注意这里有个巧妙的实现, 映射关系需要按照算法名升序加入。 因为在查找算法用例的时候,它其实是一个二分查找的过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

{
.alg = "md4",
.test = alg_test_hash,
.suite = {
.hash = __VECS(md4_tv_template)
}
}, {
.alg = "md5",
.test = alg_test_hash,
.suite = {
.hash = __VECS(md5_tv_template)
}
}, {
.alg = "michael_mic",
.test = alg_test_hash,
.suite = {
.hash = __VECS(michael_mic_tv_template)
}
}

// 二分法查找算法用例
static int alg_find_test(const char *alg)
{
int start = 0;
int end = ARRAY_SIZE(alg_test_descs);

while (start < end) {
int i = (start + end) / 2;
int diff = strcmp(alg_test_descs[i].alg, alg);

if (diff > 0) {
end = i;
continue;
}

if (diff < 0) {
start = i + 1;
continue;
}

return i;
}

return -1;
}

alg_test_hash是内核提供的一个测试hash算法的用例模板,里面的流程就是调用Linux kernel crypto框架提供的接口来运行算法,然后拿结果跟提供的数据作对比。关于这个接口的使用,在后面出文章讲一下。
不同的算法类型都有对应的测试函数,没有的话, 得研究一下怎么自己加一下。

tcrypt模块

tcrypt.c则编译成一个内核模块,允许我们通过insmod/modprobe 动态加载来测试不同的算法。它提供了好几个模块参数,加载模块的时候传递不同参数来控制调用不同的算法用例。

在我们编译完内核之后, 可以在/lib/modules/5.17.1/kernel/crypto (5.17.1 是内核版本号)找到tcrypt.ko

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

root@keep-VirtualBox:/lib/modules/5.17.1/kernel/crypto# ls
842.ko authencesn.ko chacha20poly1305.ko ecdsa_generic.ko ofb.ko twofish_common.ko
adiantum.ko authenc.ko chacha_generic.ko echainiv.ko pcbc.ko twofish_generic.ko
aegis128.ko blake2b_generic.ko cmac.ko ecrdsa_generic.ko pcrypt.ko vmac.ko
aes_ti.ko blake2s_generic.ko crc32_generic.ko essiv.ko poly1305_generic.ko wp512.ko
af_alg.ko blowfish_common.ko cryptd.ko fcrypt.ko rmd160.ko xcbc.ko
algif_aead.ko blowfish_generic.ko crypto_engine.ko keywrap.ko serpent_generic.ko xor.ko
algif_hash.ko camellia_generic.ko crypto_simd.ko lrw.ko sha3_generic.ko xxhash_generic.ko
algif_rng.ko cast5_generic.ko crypto_user.ko lz4hc.ko sm2_generic.ko zstd.ko
algif_skcipher.ko cast6_generic.ko curve25519-generic.ko lz4.ko sm3_generic.ko
ansi_cprng.ko cast_common.ko des_generic.ko md4.ko sm4_generic.ko
asymmetric_keys ccm.ko ecc.ko michael_mic.ko streebog_generic.ko
async_tx cfb.ko ecdh_generic.ko nhpoly1305.ko tcrypt.ko


root@keep-VirtualBox:/lib/modules/5.17.1/kernel/crypto# insmod tcrypt.ko mode=1000
insmod: ERROR: could not insert module tcrypt.ko: Resource temporarily unavailable
root@keep-VirtualBox:/lib/modules/5.17.1/kernel/crypto# 7月 02 20:16:02 keep-VirtualBox kernel: alg des
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg md5
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg des3_ede
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg rot13
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha1
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha224
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha256
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sm3
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg blowfish
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg twofish
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg serpent
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha384
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha512
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg md4
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg aes
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg cast6
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg arc4
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg michael_mic
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg deflate
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg crc32c
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg tea
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg xtea
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg khazad
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg wp512
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg wp384
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg wp256
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg xeta
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg fcrypt
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg camellia
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg seed
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg rmd160
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg lzo
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg lzo-rle
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg cts
7月 02 20:16:02 keep-VirtualBox kernel: not found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha3-224
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha3-256
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha3-384
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg sha3-512
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg streebog256
7月 02 20:16:02 keep-VirtualBox kernel: found
7月 02 20:16:02 keep-VirtualBox kernel: alg streebog512
7月 02 20:16:02 keep-VirtualBox kernel: found

传入mode=1000表示输出内核中注册的算法, 这里不包含组合的算法。具体的参数可以去看tcrypt.c的do_test函数。如果我们要自己添加一个测试算法的入口,那么只需要增加一个case, 然后调用我们自己指定的算法就可以了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static int do_test(const char *alg, u32 type, u32 mask, int m, u32 num_mb)
{
int i;
int ret = 0;

switch (m) {
...
case 159:
ret += tcrypt_test("cmac(sm4)");
break;
case 160:
ret += tcrypt_test("zuc"); // 加一个祖冲之算法测试用例调用入口
break;

编译

要能tcrypt测试, 还需要在内核编译make menuconfig的时候选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

--- Cryptographic API │ │
│ │ *** Crypto core or helper *** │ │
│ │ -*- Cryptographic algorithm manager │ │
│ │ <M> Userspace cryptographic algorithm configuration │ │
│ │ [ ] Disable run-time self tests │ │ # 不选择
│ │ -*- Null algorithms │ │
│ │ <M> Parallel crypto engine │ │
│ │ {M} Software async crypto daemon │ │
│ │ {M} Authenc support │ │
│ │ <M> Testing module │ │ # 选择
│ │ *** Public-key cryptography *** │ │
│ │ -*- RSA algorithm │ │
│ │ -*- Diffie-Hellman algorithm │ │
│ │ {M} ECDH algorithm │ │
│ │ <M> ECDSA (NIST P192, P256 etc.) algorithm │ │
│ │ <M> EC-RDSA (GOST 34.10) algorithm │ │
│ │ <M> SM2 algorithm │ │
│ │ <M> Curve25519 algorithm │ │
│ │ {M} x86_64 accelerated Curve25519 scalar multiplication library │ │
│ │ *** Authenticated Encryption with Associated Data *** │ │
│ │ {M} CCM support │ │
│ │ {*} GCM/GMAC support │ │
│ │ {M} ChaCha20-Poly1305 AEAD support │ │
│ │ <M> AEGIS-128 AEAD algorithm │ │
│ │ <M> AEGIS-128 AEAD algorithm (x86_64 AESNI+SSE2 implementation)

更多

最近股市行情不错,账户也已经回血了许多。但是搞了大半年,发现也就是几千块钱的收益(哈哈,主要是本金少),还不如好好工作,拿个好绩效,做出成绩来得更实在一些。

另外,六月份的时候工作也转正了,工作上的事情还是蛮多的,有很多事情可以做,单单把目前看到的事情做好就得有个一两年吧。好好学习,天天向上吧。

现在开始做驱动开发,每天都会花个半小时到1小时的时间看驱动的书《Linux驱动开发详解:基于最新Linux4.0内核》,也是个大块头啊,发现内核里面做的事情还是很多的,有的学了。工作的时候,看到很多新的概念名词,还有知识点,由于实在太多,很多没有深入,但是基本上都记录下来了,需要多花写时间梳理小结一下,下半年出文章的数量估计会多一些, 当然保底依旧是每周一篇的。

周末和晚上的时间可以好好利用,做好规划,不断地在知识的海洋中遨游!


行动,才不会被动!

欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。

博客地址: https://fishmwei.github.io

掘金主页: https://juejin.cn/user/2084329776486919