- testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin
- 上述代码是内核内部即crypto子系统对外提供密码服务的测试程序
- 调用流程:crypto API <—> crypto core <—> crypto_register_alg
- 处于用户态的程序想要调用处于内核态的密码算法,需要使用crypto_register_alg函数提交对应的相关信息,进行注册,将一个内核支持的算法注册到crypto系统里。
- crypto_alg参考链接如下
- crypto_alg 结构
struct crypto_alg {
struct list_head cra_list;
struct list_head cra_users;
u32 cra_flags;
unsigned int cra_blocksize;
unsigned int cra_ctxsize;
unsigned int cra_alignmask;
int cra_priority;
refcount_t cra_refcnt;
char cra_name[CRYPTO_MAX_ALG_NAME];
char cra_driver_name[CRYPTO_MAX_ALG_NAME];
const struct crypto_type *cra_type;
union {
struct cipher_alg cipher;
struct compress_alg compress;
} cra_u;
int (*cra_init)(struct crypto_tfm *tfm);
void (*cra_exit)(struct crypto_tfm *tfm);
void (*cra_destroy)(struct crypto_alg *alg);
struct module *cra_module;
#ifdef CONFIG_CRYPTO_STATS
union {
struct crypto_istat_aead aead;
struct crypto_istat_akcipher akcipher;
struct crypto_istat_cipher cipher;
struct crypto_istat_compress compress;
struct crypto_istat_hash hash;
struct crypto_istat_rng rng;
struct crypto_istat_kpp kpp;
} stats;
#endif /* CONFIG_CRYPTO_STATS */
} CRYPTO_MINALIGN_ATTR;
- 执行一个请求的时候,还有维护一组上下文的信息, 这些信息记录在结构体: struct crypto_tfm
- crypto_tfm类型指针tfm可以理解为指代了一个算法对象
- 参考链接 Linux内核 crypto文件夹 密码学知识学习_CHYabc123456hh的博客-CSDN博客
- cra_init是准备上下文的函数,比如,你用一个硬件设备压缩数据,实际的物理操作发生在这个硬件的一个队列上,那么就需要准备这个队列,准备必要的缓存等等。
- cra_exit 是退出上下文。
- cra_u里是具体执行算法的函数,比如可以压缩和解压缩的函数。
struct crypto_tfm {
u32 crt_flags;
int node;
void (*exit)(struct crypto_tfm *tfm);
struct crypto_alg *__crt_alg;
void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
};
- crypto_tfm中最后一个元素__crt_ctx是这个上下文的私有数据。
- 上面的crypto_alg中cra_ctxsize参数就是这个私有数据的__crt_ctx的大小
例子 同步接口
/* 分配一个压缩解压缩的上下文, 可以看到这里的压缩解压缩的上下文完全就是crypto_tfm */
struct crypto_comp = crypto_alloc_comp(driver, type, mask);
--> crypto_alloc_base(alg_name, type, mask)
/* find algrithm: use alg_name, driver name */
--> alg = crypto_alg_mod_lookup(alg_name, type, mask);
/* 上下文是依据具体的算法去分配的 */
--> tfm = __crypto_alloc_tfm(alg, type, mask);
/* 上下文中指定相关的算法 */
--> tfm->__crt_alg = alg;
--> crypto_init_ops
/* 把相应的算法中的压缩解压缩函数传递给上下文 */
--> crypto_init_compress_ops(tfm)
/* ops is struct compress_tfm */
--> ops->cot_compress = crypto_compress;
--> tfm->__crt_alg->cra_compress.coa_compress
--> ops->cot_decompress = crypto_decompress;
/*
* 在创建上下文的最后调用下,算法里的初始化函数,如果是和一个硬件
* 的驱动适配,那么这里就可以执行相应硬件初始化的内容。
*/
--> if (!tfm->exit && alg->cra_init && (err = alg->cra_init(tfm)))
第二,就是执行压缩的操作:
crypto_comp_compress(tfm, input, ilen, result, &dlen)
crypto_comp_decompress(crypto_comp, src, slen, dst, dlen)
/* so hardware can do compress here! */
--> compress_tfm->cot_compress;
第三,就是释放这个压缩的上下文
crypto_free_comp(comp)
-
从设备驱动的角度讲, 设备驱动只是看到了crypto_alg这个结构。
-
这个结构里的crypt_tfm 即一个操作执行的上下文是从哪里知道的呢?毕竟crypto_alg这个结构里的.cra_init, .cra_exit, .cra_u里的.coa_compress都需要这个执行上下文。
-
知道这些内部的数据结构对我们理解外部的API有帮助。现在假设crypto的设备驱动已经有了,那么,其他的内核模块怎么用呢? 其实一开头我们已经讲到crypto/testmgr.c测试程序。
-
测试的代码里有异步的测试和同步的测试流程,我们这里先看同步的测试:
-
主要的逻辑就三个函数, 首先需要分配一个压缩的上下文(本文用压缩的例子), 其实它就是crypto_tfm的包装,和cryto_tfm是一样的: 紫色的删去 ,我没找到这个压缩的例子
struct crypto_comp {
struct crypto_tfm base;
};
- 使用testmgr.c文件中的函数进行分析
第一步 创建对象 进行准备操作
- crypto.h - include/linux/crypto.h - Linux source code (v5.15.11) - Bootlin crypto_alloc_comp
- 创建对象
- api.c - crypto/api.c - Linux source code (v5.15.11) - Bootlin crypto_alloc_base
- 调用 crypto_alg_mod_lookup寻找 用户输入的函数的名字是否是内核支持的算法类型
- crypto_alloc_base通过crypro_alg_mod_lookup判断这个算法类型是存在的
- 才会使用函数__crypto_alloc_tfm为其创建对象
- api.c - crypto/api.c - Linux source code (v5.15.11) - Bootlin
- 使用上层输入的 参数 初始化对象
- api.c - crypto/api.c - Linux source code (v5.15.11) - Bootlin crypto_init_ops
- 调用crypto_type的init
- algapi.h - include/crypto/algapi.h - Linux source code (v5.15.11) - Bootlin
第二步 执行具体的函数操作
- 本例子具体执行的函数操作是crypto_comp_compress和crypto_comp_decompress
- testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin
第三步 释放压缩的上下文
例子 异步接口
- testmgr.c - crypto/testmgr.c - Linux source code (v5.15.11) - Bootlin
- 异步接口和同步接口不一样的是,这里又创建一个acomp_req的上下文, 后续的操作都围绕着这个req结构展开。
- 可以看到req里面包含了异步接口需要的回调函数
static int test_acomp(struct crypto_acomp *tfm,
const struct comp_testvec *ctemplate,
const struct comp_testvec *dtemplate,
int ctcount, int dtcount)
{
const char *algo = crypto_tfm_alg_driver_name(crypto_acomp_tfm(tfm));
unsigned int i;
char *output, *decomp_out;
int ret;
struct scatterlist src, dst;
struct acomp_req *req;
struct crypto_wait wait;
output = kmalloc(COMP_BUF_SIZE, GFP_KERNEL);
if (!output)
return -ENOMEM;
decomp_out = kmalloc(COMP_BUF_SIZE, GFP_KERNEL);
if (!decomp_out) {
kfree(output);
return -ENOMEM;
}
for (i = 0; i < ctcount; i++) {
unsigned int dlen = COMP_BUF_SIZE;
int ilen = ctemplate[i].inlen;
void *input_vec;
input_vec = kmemdup(ctemplate[i].input, ilen, GFP_KERNEL);
if (!input_vec) {
ret = -ENOMEM;
goto out;
}
memset(output, 0, dlen);
crypto_init_wait(&wait);
sg_init_one(&src, input_vec, ilen);
sg_init_one(&dst, output, dlen);
req = acomp_request_alloc(tfm);
if (!req) {
pr_err("alg: acomp: request alloc failed for %s\n",
algo);
kfree(input_vec);
ret = -ENOMEM;
goto out;
}
acomp_request_set_params(req, &src, &dst, ilen, dlen);
acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
ret = crypto_wait_req(crypto_acomp_compress(req), &wait);
if (ret) {
pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req);
goto out;
}
ilen = req->dlen;
dlen = COMP_BUF_SIZE;
sg_init_one(&src, output, ilen);
sg_init_one(&dst, decomp_out, dlen);
crypto_init_wait(&wait);
acomp_request_set_params(req, &src, &dst, ilen, dlen);
ret = crypto_wait_req(crypto_acomp_decompress(req), &wait);
if (ret) {
pr_err("alg: acomp: compression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req);
goto out;
}
if (req->dlen != ctemplate[i].inlen) {
pr_err("alg: acomp: Compression test %d failed for %s: output len = %d\n",
i + 1, algo, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req);
goto out;
}
if (memcmp(input_vec, decomp_out, req->dlen)) {
pr_err("alg: acomp: Compression test %d failed for %s\n",
i + 1, algo);
hexdump(output, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req);
goto out;
}
kfree(input_vec);
acomp_request_free(req);
}
for (i = 0; i < dtcount; i++) {
unsigned int dlen = COMP_BUF_SIZE;
int ilen = dtemplate[i].inlen;
void *input_vec;
input_vec = kmemdup(dtemplate[i].input, ilen, GFP_KERNEL);
if (!input_vec) {
ret = -ENOMEM;
goto out;
}
memset(output, 0, dlen);
crypto_init_wait(&wait);
sg_init_one(&src, input_vec, ilen);
sg_init_one(&dst, output, dlen);
req = acomp_request_alloc(tfm);
if (!req) {
pr_err("alg: acomp: request alloc failed for %s\n",
algo);
kfree(input_vec);
ret = -ENOMEM;
goto out;
}
acomp_request_set_params(req, &src, &dst, ilen, dlen);
acomp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
crypto_req_done, &wait);
ret = crypto_wait_req(crypto_acomp_decompress(req), &wait);
if (ret) {
pr_err("alg: acomp: decompression failed on test %d for %s: ret=%d\n",
i + 1, algo, -ret);
kfree(input_vec);
acomp_request_free(req);
goto out;
}
if (req->dlen != dtemplate[i].outlen) {
pr_err("alg: acomp: Decompression test %d failed for %s: output len = %d\n",
i + 1, algo, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req);
goto out;
}
if (memcmp(output, dtemplate[i].output, req->dlen)) {
pr_err("alg: acomp: Decompression test %d failed for %s\n",
i + 1, algo);
hexdump(output, req->dlen);
ret = -EINVAL;
kfree(input_vec);
acomp_request_free(req);
goto out;
}
kfree(input_vec);
acomp_request_free(req);
}
ret = 0;
out:
kfree(decomp_out);
kfree(output);
return ret;
}
- 这里需要说明的是,testmsg.c里的这个acomp的测试程序里加了wait的相关内容。这里应该是为了测试方便而加的,一般的异步接口里, 当硬件完成操作的时候,在中断函数里直接调用异步接口的回调函数就可以了。
- request是为了异步而创建的,但是wait对象的存在的主要目的目前还不清楚
例外一个例子
总的来说,在内核态使用加密算法的过程分为以下几步:
- 分配tranform对象 也就是具体的算法
- 分配request对象 异步操作等待对象
- 设置上下文 如加密密钥/验签公钥,填充数据源,给scatterlist设置缓冲区,给异步请求对象设置回调函数/初始化向量等,给密码算法对象设置密钥
- 完成加密/解密/摘要/验签
- 释放transform,request等对象
- 如果是同步调用的方式,不需要创建request对象