一、简述
主要编写了一个测试验证PadLock AES内核接口的验证模块。应用PadLock AES内核接口实现加密解密。本文提供了应用该模块的方法、步骤、原理及源码。
操作系统 |
mint17 |
内核 |
3.8.0 |
编译器 |
gcc4.7.3 |
CPU |
VIA Nano X2 L4530 @ 1.6+ GHz |
内存 |
4G |
多核 |
2个 |
Linux内核有关于加密的函数的接口,主要在cryto文件夹目录中。有AES、SHA等多种算法的实现接口。网上有很多介绍Linux内核关于加密算法框架的文章,通过阅读Linux内核源码,编写了一个使用AES内核函数库的内核模块。
使用AES加密算法主要有4个函数:
crypto_blkcipher_setkey
crypto_blkcipher_set_iv
crypto_blkcipher_encrypt
crypto_blkcipher_decrypt
只有这几个函数配置正确,那么就可以正常使用Linux内核的AES加密库了。下面介绍这几个函数的用法。
static inline int crypto_blkcipher_setkey(struct crypto_blkcipher *tfm,
const u8 *key, unsigned int keylen)
该函数主要是用于设置加密关键字key。这里需要说明下AES算法的如何应用,对于算法层级,AES通过key(关键字)、iv(初始化向量)、加密模式(CBC/ECB/CFB/OFB等)、key-length(128/192/256)来确定加密的模式,依据不同的模式同样的明文加密出的密文是不一样的。crypto_blkcipher_setkey()函数就是设置其key。*key是秘钥,keylen是秘钥长度(单位字节可以选择16、24、32)
下面说说crypto_blkcipher *tfm,结构体这个是加密上下文,它是贯穿加密的始终,crypto_blkcipher_setkey、crypto_blkcipher_set_iv都是用来设置tfm这个结构体的,它是加密配置的载体。
struct crypto_blkcipher { struct crypto_tfm base; }; struct crypto_tfm { u32 crt_flags; union { struct ablkcipher_tfm ablkcipher; struct aead_tfm aead; struct blkcipher_tfm blkcipher; struct cipher_tfm cipher; struct hash_tfm hash; struct compress_tfm compress; struct rng_tfm rng; } crt_u; //确定是算法模式 void (*exit)(struct crypto_tfm *tfm); //退出函数 struct crypto_alg *__crt_alg; void *__crt_ctx[] CRYPTO_MINALIGN_ATTR; };
从中间那个union和上面的一堆#define可以看出,从这个结构又可以分散出一组xxx_tfm。crypto_alg对应cipher_tfm。最后那个参数__crt_ctx[]就是上面说到的算法上下文。也就是说,算法上下文是跟随tfm一起分配的。从tfm,我们就可以得到ctx。Linux提供了一个函数inline void *crypto_tfm_ctx(struct crypto_tfm *tfm);来进行转换,该函数也在<srcdir>/include/linux/crypto.h中。
下面介绍crypto_blkcipher_set_iv函数
static inline void crypto_blkcipher_set_iv(struct crypto_blkcipher *tfm,
const u8 *src, unsigned int len)
它的功能是设置iv(初始化向量)。src是iv值,len是iv的长度,tfm是和前面讲述的一样。
下面介绍核心加密函数crypto_blkcipher_encrypt_iv:
static inline int crypto_blkcipher_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes)
它的功能就是实现加密。返回0表示正确,1表示错误。*desc是加密上下文结构体,上面set_iv和set_key就是用来设置这个blkcipher_desc结构体的,
原型为:
struct blkcipher_desc {
struct crypto_blkcipher *tfm;
void *info;
u32 flags;
};
其中tfm就是之前我们所说的加密上下文,info通常用来存放iv,因为块加密的散集序列工具(scatterwalk)在初始化时直接将info当作iv来使用。
散集序列(scatterlist)是什么呢?【1】
在Linux内核中,跟外设打交道有三种方式:IO、端口和DMA,这个教科书上都讲了。其中DMA方式是由DMA控制器来控制内存、外设间的数据传输。我们知道,Linux的地址空间有三种:虚拟地址、物理地址和总线地址。DMA要求每次传输的一整块数据分布在连续的总线地址空间上。而DMA是为传输大块数据设计的,但是大块的连续总线地址空间通常是稀缺的。因此当没有那么多连续空间时,只能将大块数据分散到尽可能少的小块连续地址上,然后让DMA控制器一块接着一块地把数据全部传完。因此Linux内核中专门设计了一种叫做散集序列(scatterlist)的数据结构将小块的连续总线地址串起来,交给DMA驱动自动地一个接着一个地传输。
说白了,scatterlist就是一个线性表(scatterlist可以是链表,也可以是数组),每个元素包含一个指针指向一块总线地址连续的内存块,这是为DMA量身定做的数据结构。
scatterlist的结构体 struct scatterlist { #ifdef CONFIG_DEBUG_SG unsigned long sg_magic; #endif 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 };
在本文中给scatterwalk分配空间及设置数据利用函数:
sg_init_one()。
scatterwalk是对物理内存的操作,它便于DMA的传输。但是我们在Linux内核中的操作很多是对虚拟内存的处理(显示数据printk),因此还需要在应用scatterwalk的同时,要还需要它映射到虚拟内存中来利用(kmap),在后面实现程序中会介绍。
dst是目的地址,src是原数据,nbytes是处理的字节。
crypto_blkcipher_decrypt函数的原型是:
static inline int crypto_blkcipher_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, struct scatterlist *src, unsigned int nbytes)
该函数的作用是解密,和加密算法参数类似,不再累述。
主要关键的函数说完了,下面结合具体的源码进一步分析:
我通过编译Linux内核模块实现,两个文件:tcrypt.c和Makefile。直接通过make、make install就可以实现。
tcrypt.c: #include <linux/module.h> #include <linux/crypto.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/highmem.h> static void my_test_cipher(void) //实现函数 { unsigned int ret; int i; /*必须要用数组,如果用*code这种方式就会输出不正确的数据,还请大神们帮忙解答*/ char code[17] = "1234567887654321"; char *key = "0123456789abcdef";//关键key char *iv = "1234567887654321";//设置iv值 struct scatterlist sgd;//散集序列,输出 struct scatterlist sgs;//散集序列,输入 struct scatterlist sgl;//散集序列,解密后输出 char last_mem[17]; char dst_mem[17]; char *out = NULL; char *result = NULL; char *src = NULL; struct crypto_blkcipher *tfm; struct blkcipher_desc desc; memset(last_mem,0,17); memset(dst_mem,0,17); /*分配块加密上下文 cbc(aes)表示模式,也可以ofb(aes)等,通常第一个参数为0,第三个参数表示加密模式, */ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); desc.tfm = tfm; desc.flags = 0; /*设置散集序列,将Linux内核中虚拟内存的数据传送到散集序列供dma应用,其中dst_mem,code,last_mem是我们自己设定的值,后面的16表示数据长度大小*/ sg_init_one(&sgd,dst_mem,16); sg_init_one(&sgs,code,16); sg_init_one(&sgl,last_mem,16); crypto_blkcipher_setkey(tfm,key,16);//设置key crypto_blkcipher_set_iv(tfm,iv,16);//设置iv /*将sgs(散集序列,物理内存)映射到Linux内核的虚拟内存中,目的是我们可以显示其数据*/ src = kmap(sg_page(&sgs))+sgs.offset; printk("the orgin is\n"); for(i = 0 ;i < sgs.length;i++){ printk("%c\n",src[i]); } //加密 ret = crypto_blkcipher_encrypt(&desc,&sgd,&sgs,sgs.length); if (!ret) { printk("AES encrypt success*************************\n"); out = kmap(sg_page(&sgd))+sgd.offset; printk("the encrypt is:\n"); for(i = 0;i<sgd.length;i++){ printk("%c\n",out[i]); } } else{ printk("the encrypt is wrong\n"); return ; } crypto_blkcipher_setkey(tfm,key,16); crypto_blkcipher_set_iv(tfm,iv,16); //解密 ret = crypto_blkcipher_decrypt(&desc,&sgl,&sgd,sgd.length); if(!ret){ printk(KERN_INFO"hw aes decrypt is success\n"); result = kmap(sg_page(&sgl))+sgl.offset; for(i = 0; i < sgl.length;i++){ printk("%c\n",result[i]); } } else{ printk("the decrypt is wrong\n"); return; } crypto_free_blkcipher(tfm); } static int __init tcrypt_mod_init(void) { my_test_cipher(); return 0; } /* * If an init function is provided, an exit function must also be provided * to allow module unload. */ static void __exit tcrypt_mod_fini(void) { } module_init(tcrypt_mod_init); module_exit(tcrypt_mod_fini); //MODULE_PARM_DESC(sec, "Length in seconds of speed tests " // "(defaults to zero which uses CPU cycles instead)"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Quick & dirty crypto testing module"); MODULE_AUTHOR("dachuan"); Makefile: KONAME = tcrypt.o IFTEST=$(shell lsmod | grep tcrypt | wc -l) KERNELDIR := /lib/modules/$(shell uname -r)/build PWD :=$(shell pwd) obj-m:=$(KONAME) module: make -C $(KERNELDIR) M=$(PWD) test_install : ifeq ($(IFTEST),1) rmmod tcrypt insmod tcrypt.ko else insmod tcrypt.ko endif install: test_install .PHONY:install clean: rm *.o *.mod.c modules.order Module.symvers .*.cmd *.ko rm -rf .tmp_versions
首先进入带有tcrypt.c makefile的文件夹:
输入命令:
make
make install
利用dmesg查看Linux内核信息:
[1]http://blog.csdn.net/sonicling/article/details/6275898 Linux内核块设备加密分析
原文地址:http://blog.csdn.net/fengjingge/article/details/42192151