Linux内核密码学API--对称密码

Linux内核密码学API–对称密码

  当我们做Linux内核或者内核模块开发需要使用密码算法时,通常使用Linux内核集成的密码学套件是比较方便的。常用的密码学算法可以分为三类:对称密码算法,非对称密码算法和消息摘要(单向哈希)算法,这篇博客将主要介绍如何使用Linux内核的对称密码算法API。

1. 头文件

  使用Linux内核的对称密码API时,需要以下头文件

// 包含对称密码(symmetric key)算法API(该文件内部包含了linux/crypto.h所以无需再次引入linux/crypto.h)
#include <crypto/skcipher.h> 
// 对称密码API需要使用的struct scatterlist结构(用来保存输入/输出缓冲区)
#include <linux/scatterlist.h> 

2. 需要使用的重要函数/数据结构

// 用来保存加/解密缓冲区的结构
struct scatterlist sg;
// 加密算法对象(上下文)
struct crypto_skcipher *tfm;
// 异步操作请求对象
struct skcipher_request *req;
// 异步操作等待对象
struct crypto_wait wait;


// 该函数用于等待异步加密操作完成,通常需要将crypto_skcipher_decrypt的返回值作为err参数传入
int crypto_wait_req(int err, struct crypto_wait *wait);

// 该函数作为异步加密操作的回调函数传入req对象中,在加密完成后被调用
void crypto_req_done(struct crypto_async_request *req, int err);


// 该函数根据密码算法名称分配密码算法对象,内核支持的密码算法可以在/proc/crypto文件中查看
struct crypto_skcipher *crypto_alloc_skcipher(const char *alg_name,
					      u32 type, u32 mask);

struct skcipher_request *skcipher_request_alloc(
	                    struct crypto_skcipher *tfm, gfp_t gfp);

// init function
// 初始化scatterlist时需要使用kmalloc分配的内存,如果使用vmalloc分配的内存会导致内存页分配错误,目前还不知道具体原因
void sg_init_one(struct scatterlist *sg,
		    const void *buf, unsigned int buflen);

// 设置异步调用的回调函数,这里data是一个自定数据结构,其会被传给回调函数。
void skcipher_request_set_callback(struct skcipher_request *req,
                                    u32 flags,
                                    crypto_completion_t compl,
                                    void *data);

// 内核的对称加密API可以“原地”加密,即加解密共用相同的缓冲区,因此这里的src和dst可以设置为同一个
void skcipher_request_set_crypt(
	struct skcipher_request *req,
	struct scatterlist *src, struct scatterlist *dst,
	unsigned int cryptlen, void *iv);

// 设置密钥和密钥长度,密码长度单位为字节
int crypto_skcipher_setkey(struct crypto_skcipher *tfm,
					 const u8 *key, unsigned int keylen);

// 解密,将返回值传入crypto_wait_req函数来等待可能的异步操作完成
int crypto_skcipher_decrypt(struct skcipher_request *req);
// 加密
int crypto_skcipher_encrypt(struct skcipher_request *req)

// 释放资源
void crypto_free_skcipher(struct crypto_skcipher *tfm);
void skcipher_request_free(struct skcipher_request *req);

3. 基本流程

  对称密码算法的加/解密流程同上一节给出的API函数顺序相同,可以分为4个大的步骤。

  1. 获取密码算法对象和异步操作请求对象
  2. 初始化这些对象,设置所需参数,即给scatterlist设置缓冲区,给异步请求对象设置回调函数/初始化向量等,给密码算法对象设置密钥
  3. 执行加/解密操作
  4. 释放动态分配的资源

4. 示例

int linux_kernel_crypto_decrypt(void* data_in_out, int data_len, void* key, int key_len, void* iv, int iv_len) {
	struct crypto_skcipher* cipher;
	struct skcipher_request* req;
    struct crypto_wait wait;
	struct scatterlist sg;
    size_t block_size;
	int ret;

    // 分配算法对象,支持的算法可以在/proc/crypto文件中查看
	cipher = crypto_alloc_skcipher("cbc(aes)", 0, 0);
	if (IS_ERR(cipher)) {
		printk("fail to allocate cipher\n");
		return -1;
	}

    // skcipher api不支持填充,所以加/解密数据需要为加密块的整数倍
    block_size = crypto_skcipher_blocksize(cipher);
    if (data_len % block_size != 0) {
		printk("data len not aligned");
		return -1;
	}

    // 分配req对象
	req = skcipher_request_alloc(cipher, GFP_KERNEL);
	if (IS_ERR(req)) {
		printk("fail to allocate req\n");
		return -1;
	}
	sg_init_one(&sg, data_in_out, data_len);
	skcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, crypto_req_done, &wait);
	skcipher_request_set_crypt(req, &sg, &sg, data_len, iv);
	ret = crypto_skcipher_setkey(cipher, key, key_len);
    if ( 0 != ret) {
        printk("fail to set key, error %d\n", ret);
        return -1;
    }
    // 执行解密操作
	ret = crypto_wait_req(crypto_skcipher_decrypt(req), &wait); 
	if (0 != ret) {
        printk("decryption error %d\n", ret);
        return -1}
	// 释放资源
	crypto_free_skcipher(cipher);
	skcipher_request_free(req);
	printk("decryption finished");
	return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值