内核tcrypt加密算法测试模块代码解读

1,648 阅读4分钟

前言

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函数,查找对应的测试用例函数,找到之后就会执行用例,检测算法实现是否符合预期。如果没有对应的用例或者用例执行成功的话,则算法直接注册成功,否则注册失败。


// 注册算法
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算法的测试数据:

/*
 * 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里面添加映射关系, 注意这里有个巧妙的实现, 映射关系需要按照算法名升序加入。 因为在查找算法用例的时候,它其实是一个二分查找的过程:


{
		.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


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, 然后调用我们自己指定的算法就可以了。

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的时候选择


 --- 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,沟通交流。

博客地址: fishmwei.github.io

掘金主页: juejin.cn/user/208432…