前言 上周讲了使用tcrypt对内核加密框架linux kernel crypto中算法的测试。今天具体看一下几个示例。
算法类型分为4种: hash、对称、非对称、随机数。
hash算法调用 对hash算法的调用主要有几个步骤:
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 struct scatterlist sg [TVMEMSIZE ]; struct crypto_wait wait ;struct ahash_request *req ;struct crypto_ahash *tfm ;char *output;int i, ret;tfm = crypto_alloc_ahash(algo, 0 , mask); ... test_hash_sg_init(sg); req = ahash_request_alloc(tfm, GFP_KERNEL); crypto_init_wait(&wait); ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); ... output = kmalloc(MAX_DIGEST_SIZE, GFP_KERNEL); ... if (klen) crypto_ahash_setkey(tfm, tvmem[0 ], klen); ret = crypto_wait_req(crypto_ahash_init(req), &wait); ahash_request_set_crypt(req, sg, output, speed[i].plen); ret = crypto_wait_req(crypto_ahash_update(req), &wait); ahash_request_set_crypt(req, &sg, out, 0 ); ret = crypto_wait_req(crypto_ahash_final(req), &wait);
这个是典型的init, update, final的流程。 有时候,数据没有那么长, 可以直接调用digest接口 一次运算就得到结果。 还有一个就是 init, update , finup的流程, 最后一次的upate和final结合起来, 中间可能就没有update的调用。
主要根据用户对数据的长度判断来决定。总之最后的结果肯定都是一样的。
对称算法调用 大的总体流程都是差不多的,首先根据算法名创建tfm,然后设置key, iv,最后再调用加解密的接口。
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 tfm = crypto_alloc_skcipher(algo, 0 , async ? 0 : CRYPTO_ALG_ASYNC); ... req = skcipher_request_alloc(tfm, GFP_KERNEL); skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); ret = crypto_skcipher_setkey(tfm, key, *keysize); if (ret) { pr_err("setkey() failed flags=%x\n" , crypto_skcipher_get_flags(tfm)); goto out_free_req; } sg_init_table(sg, DIV_ROUND_UP(k, PAGE_SIZE)); ... sg_set_buf(sg, tvmem[0 ] + *keysize, bs); skcipher_request_set_crypt(req, sg, sg, bs, iv); ret = crypto_wait_req(crypto_skcipher_encrypt(req), wait); skcipher_request_free(req); crypto_free_skcipher(tfm);
非对称算法调用 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 tfm = crypto_alloc_akcipher(driver, type, mask); req = akcipher_request_alloc(tfm, GFP_KERNEL); crypto_init_wait(&wait); if (vecs->public_key_vec) err = crypto_akcipher_set_pub_key(tfm, key, vecs->key_len); else err = crypto_akcipher_set_priv_key(tfm, key, vecs->key_len); ... out_len_max = crypto_akcipher_maxsize(tfm); outbuf_enc = kzalloc(out_len_max, GFP_KERNEL); if (!outbuf_enc) goto free_key; if (!vecs->siggen_sigver_test) { m = vecs->m; m_size = vecs->m_size; c = vecs->c; c_size = vecs->c_size; op = "encrypt" ; } else { m = vecs->c; m_size = vecs->c_size; c = vecs->m; c_size = vecs->m_size; op = "verify" ; } ... sg_init_table(src_tab, 3 ); sg_set_buf(&src_tab[0 ], xbuf[0 ], 8 ); sg_set_buf(&src_tab[1 ], xbuf[0 ] + 8 , m_size - 8 ); if (vecs->siggen_sigver_test) { if (WARN_ON(c_size > PAGE_SIZE)) goto free_all; memcpy (xbuf[1 ], c, c_size); sg_set_buf(&src_tab[2 ], xbuf[1 ], c_size); akcipher_request_set_crypt(req, src_tab, NULL , m_size, c_size); } else { sg_init_one(&dst, outbuf_enc, out_len_max); akcipher_request_set_crypt(req, src_tab, &dst, m_size, out_len_max); } akcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait); err = crypto_wait_req(vecs->siggen_sigver_test ? crypto_akcipher_verify(req) : crypto_akcipher_encrypt(req), &wait); ... ...
随机数算法调用 // 参考testmgr.c alg_test_cprng函数
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 rng = crypto_alloc_rng(driver, type, mask); ... seedsize = crypto_rng_seedsize(tfm); ... memset (result, 0 , 32 ); memcpy (seed, template[i].v, template[i].vlen);memcpy (seed + template[i].vlen, template[i].key, template[i].klen); memcpy (seed + template[i].vlen + template[i].klen, template[i].dt, template[i].dtlen); err = crypto_rng_reset(tfm, seed, seedsize); if (err) { printk(KERN_ERR "alg: cprng: Failed to reset rng " "for %s\n" , algo); goto out; } for (j = 0 ; j < template[i].loops; j++) { err = crypto_rng_get_bytes(tfm, result, template[i].rlen); if (err < 0 ) { printk(KERN_ERR "alg: cprng: Failed to obtain " "the correct amount of random data for " "%s (requested %d)\n" , algo, template[i].rlen); goto out; } }
scatterlist 在前面的代码中看到,在设置数据的时候,很多地方使用了sg_init_table、 sg_set_buf函数,使用的就是scatterlist这个结构体。
1 2 3 4 5 6 7 8 9 10 struct scatterlist { unsigned long page_link; unsigned int offset; unsigned int length; dma_addr_t dma_address; #ifdef CONFIG_NEED_SG_DMA_LENGTH unsigned int dma_length; #endif };
对于一个给定的数据块,在内存中可能是在一些离散的区域。scatterlist就是把这些区域信息汇聚在一起的结构,一般是以数组的形式出现。
sg_init_table用来初始化这一个数组,参数是数组指针和元素的个数。内部实现就是清空内存,然后在最后一个元素中page_link成员中设置结束标识。
sg_set_buf就是把缓冲区的地址和长度设置到每个scatterlist元素中。
然后,算法调用的时候再通过xxxkcipher_request_set_crypt函数把scatterlist形式的数据设置到request中的src/dst。
另一边,在算法实现接口中,取出src/dst。获取到对应的虚拟地址,也就得到了数据,然后进行算法计算。
参考代码: https://gitee.com/fishmwei/blog_code/blob/master/linux-kernel/crypto_example/crypto.c
执行效果:
1 2 3 4 5 6 7 cd crypto_example make journalctl -f & insmod crypto.ko alg=aes len=16
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。
博客地址: https://fishmwei.github.io
掘金主页: https://juejin.cn/user/2084329776486919