Linux内核crypto子系统的调用逻辑

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_alloc_base通过crypro_alg_mod_lookup判断这个算法类型是存在的
  • 才会使用函数__crypto_alloc_tfm为其创建对象

第二步 执行具体的函数操作

 第三步 释放压缩的上下文

例子 异步接口 

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对象

参考链接

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值