当前位置: 首页 > 工具软件 > TCrypto > 使用案例 >

linux crypto API 学习

解宏扬
2023-12-01

linux 内核里实现了crypto模块,其用法介绍的很少:
参考:linux-3.18.21/Documentation/crypto/api-intro.txt

Here’s an example of how to use the API:

#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/scatterlist.h>

struct scatterlist sg[2];
char result[128];
struct crypto_hash *tfm;
struct hash_desc desc;

tfm = crypto_alloc_hash(“md5”, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(tfm))
fail();

/* … set up the scatterlists … */

desc.tfm = tfm;
desc.flags = 0;

if (crypto_hash_digest(&desc, sg, 2, result))
fail();

crypto_free_hash(tfm);

Many real examples are available in the regression test module (tcrypt.c).

这提到 Many real examples are available in the regression test module (tcrypt.c).
这里就来看一下 linux-3.18.21/crypto/tcrypt.c

tcrypt.c

tcrypt_test("cbc(aes");
  alg_test(alg, alg, 0, 0);
    i = alg_find_test(alg);
      alg_test_descs[i].test(alg_test_descs + i, driver,type, mask);
      //alg_test_descs 是一个全局结构体数组,部分数据如下:
       {
		.alg = "cbc(aes)",
		.test = alg_test_skcipher,   //测试函数
		.fips_allowed = 1,
		.suite = {
			.cipher = {
				.enc = {
					.vecs = aes_cbc_enc_tv_template,  //测试数据
					.count = AES_CBC_ENC_TEST_VECTORS
				},
				.dec = {
					.vecs = aes_cbc_dec_tv_template,
					.count = AES_CBC_DEC_TEST_VECTORS
				}
			}
		}
		static struct cipher_testvec aes_cbc_enc_tv_template[] = {
		{ /* From RFC 3602 */
			.key    = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
				  "\x51\x2e\x03\xd5\x34\x12\x00\x06",
			.klen   = 16,
			.iv	= "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30"
				  "\xb4\x22\xda\x80\x2c\x9f\xac\x41",
			.input	= "Single block msg",
			.ilen   = 16,
			.result = "\xe3\x53\x77\x9c\x10\x79\xae\xb8"
				  "\x27\x08\x94\x2d\xbe\x77\x18\x1a",
			.rlen   = 16,
		},
		...
		}
		static struct cipher_testvec aes_cbc_dec_tv_template[] = {
		{ /* From RFC 3602 */
			.key    = "\x06\xa9\x21\x40\x36\xb8\xa1\x5b"
				  "\x51\x2e\x03\xd5\x34\x12\x00\x06",
			.klen   = 16,
			.iv     = "\x3d\xaf\xba\x42\x9d\x9e\xb4\x30"
				  "\xb4\x22\xda\x80\x2c\x9f\xac\x41",
			.input  = "\xe3\x53\x77\x9c\x10\x79\xae\xb8"
				  "\x27\x08\x94\x2d\xbe\x77\x18\x1a",
			.ilen   = 16,
			.result = "Single block msg",
			.rlen   = 16,
		},					    

继续跟踪 alg_test_skcipher

static int alg_test_skcipher(const struct alg_test_desc *desc, const char *driver, u32 type, u32 mask)
  struct crypto_ablkcipher *tfm;
  tfm = crypto_alloc_ablkcipher(driver, type, mask);
  test_skcipher(tfm, ENCRYPT, desc->suite.cipher.enc.vecs,desc->suite.cipher.enc.count);
    ret = __test_skcipher(tfm, enc, template, tcount, false, 0);    
      init_completion(&result.completion);
	  req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
	  ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
					tcrypt_complete, &result);
	  for (i = 0; i < tcount; i++) {		
		memcpy(iv, template[i].iv, MAX_IVLEN);
		j++;
		ret = -EINVAL;
		data = xbuf[0];
		data += align_offset;
		memcpy(data, template[i].input, template[i].ilen);

		crypto_ablkcipher_clear_flags(tfm, ~0);		

		ret = crypto_ablkcipher_setkey(tfm, template[i].key,
					       template[i].klen);
		sg_init_one(&sg[0], data, template[i].ilen);
		if (diff_dst) {
			data = xoutbuf[0];
			data += align_offset;
			sg_init_one(&sgout[0], data, template[i].ilen);
		}
		ablkcipher_request_set_crypt(req, sg, (diff_dst) ? sgout : sg,
					     template[i].ilen, iv);
					     
		ret = enc ? crypto_ablkcipher_encrypt(req) :
			    crypto_ablkcipher_decrypt(req);

		switch (ret) {
		case 0:
			break;
		case -EINPROGRESS:
		case -EBUSY:
			ret = wait_for_completion_interruptible(
				&result.completion);
			if (!ret && !((ret = result.err))) {
				reinit_completion(&result.completion);
				break;
			}
			/* fall through */
		default:
			pr_err("alg: skcipher%s: %s failed on test %d for %s: ret=%d\n",
			       d, e, j, algo, -ret);
			goto out;
		}

		q = data;
		if (memcmp(q, template[i].result, template[i].rlen)) {
			pr_err("alg: skcipher%s: Test %d failed on %s for %s\n",
			       d, j, e, algo);
			hexdump(q, template[i].rlen);
			ret = -EINVAL;
			goto out;
		}
	}

自己写一个测试代码:

#include <crypto/hash.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <crypto/rng.h>
#include <crypto/drbg.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <linux/scatterlist.h>
#include <linux/moduleparam.h>
#include <linux/jiffies.h>
#include <linux/timex.h>
#include <linux/interrupt.h>

static char *alg = NULL;

struct tcrypt_result {
	struct completion completion;
	int err;
};

static void hexdump(unsigned char *buf, unsigned int len)
{
	print_hex_dump(KERN_CONT, "", DUMP_PREFIX_OFFSET,
			16, 1,
			buf, len, false);
}

static void tcrypt_complete(struct crypto_async_request *req, int err)
{
	struct tcrypt_result *res = req->data;

	if (err == -EINPROGRESS)
		return;

	res->err = err;
	complete(&res->completion);
}
/*
static struct cipher_testvec aes_enc_tv_template[] = {
	{ 
		.key	= "\x00\x01\x02\x03\x04\x05\x06\x07"
			  "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
		.klen	= 16,
		.input	= "\x00\x11\x22\x33\x44\x55\x66\x77"
			  "\x88\x99\xaa\xbb\xcc\xdd\xee\xff",
		.ilen	= 16,
		.result	= "\x69\xc4\xe0\xd8\x6a\x7b\x04\x30"
			  "\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a",
		.rlen	= 16,
	},

*/
int test(void)
{
	char *key = "\x00\x01\x02\x03\x04\x05\x06\x07"
			  "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
	int keylen = 16;	
	char *str_input = "\x00\x11\x22\x33\x44\x55\x66\x77"
			  "\x88\x99\xaa\xbb\xcc\xdd\xee\xff";
	char *str_iv = "\x00\x11\x22\x33\x44\x55\x66\x77"
			  "\x88\x99\xaa\xbb\xcc\xdd\xee\xff";
	int inputlen = 16;
	
	char iv[32];
	
	struct scatterlist sg[2];
	struct scatterlist sg2[2];
	char *input;
	char *output;
	char *denc;
	struct ablkcipher_request *req;	
	struct crypto_ablkcipher *tfm;
	struct tcrypt_result result;
	int ret ;

	input = (void *)__get_free_page(GFP_KERNEL);

	//memcpy( iv,"test iv",strlen("test iv"));
	memset(iv,0,sizeof(iv));

	memcpy(input,str_input,inputlen);
	
	output = (void *)__get_free_page(GFP_KERNEL);
	denc = (void *)__get_free_page(GFP_KERNEL);

	char *driver = "ecb(aes)";
	if(alg)
	{
		driver = alg;
	}
	printk("alg : %s\n",driver);

	if(strcmp(driver,"ecb(aes)")) // !="ecb(aes)"
	{
		memcpy(iv,str_iv,16);
	}
	
	//1.加密
  	tfm = crypto_alloc_ablkcipher(driver, 0, 0);
	if (IS_ERR(tfm)) {
		printk(KERN_ERR "alg: skcipher: Failed to load transform for "
		       "%s: %ld\n", driver, PTR_ERR(tfm));
		return PTR_ERR(tfm);
	}
  	
  	init_completion(&result.completion);
	req = ablkcipher_request_alloc(tfm, GFP_KERNEL);
	if(!req)
	{
		printk("error req = null\n");
		crypto_free_ablkcipher(tfm);
		return -1;	
	}
	ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
					tcrypt_complete, &result);

	crypto_ablkcipher_clear_flags(tfm, ~0);
      //set key
	crypto_ablkcipher_setkey(tfm, key,keylen );
	  //sg init
	sg_init_one(&sg[0], input, inputlen);
	sg_init_one(&sg[1], output, inputlen);
      //set iv 
	ablkcipher_request_set_crypt(req, &sg[0], &sg[1],inputlen , iv);
	  //开始加密
	ret = crypto_ablkcipher_encrypt(req);
	if(ret == -EINPROGRESS || ret == -EBUSY)
	{
		ret = wait_for_completion_interruptible(
			&result.completion);
		if (!ret && !((ret = result.err)))
		{
			reinit_completion(&result.completion);
		}
	}
	else if(ret  == 0)
	{
		printk("succ\n");
	}
	else
	{
		printk("error ret = %d\n",ret);
		goto _err;
	}

	hexdump(output,16);
	
	//2.解密
	sg_init_one(&sg2[0], output,inputlen);
	sg_init_one(&sg2[1], denc,inputlen );
	crypto_ablkcipher_clear_flags(tfm, ~0);
	
	if(strcmp(driver,"ecb(aes)")) // !="ecb(aes)"
	{
		memcpy(iv,str_iv,16);
	}
	
	ablkcipher_request_set_crypt(req, &sg2[0], &sg2[1],inputlen , iv);
	
	ret = crypto_ablkcipher_decrypt(req);
	if(ret == -EINPROGRESS || ret == -EBUSY)
	{
		ret = wait_for_completion_interruptible(
			&result.completion);
		if (!ret && !((ret = result.err)))
		{
			reinit_completion(&result.completion);
		}
	}
	else if(ret  == 0)
	{
		printk("succ\n");
		//printk("%s \n",denc);
	}
	else
	{
		printk("error ret = %d\n",ret);
		goto _err;
	}//*/
	hexdump(denc,16);	
_err:
	ablkcipher_request_free(req);
	crypto_free_ablkcipher(tfm);
	return -1;	//恒定返回-1 ,免得insmod 后每次需要先rmmod 
}

static int __init test_init(void)
{
	test();
}
 
static void __exit test_exit(void)
{
 
}
 
module_init(test_init);
module_exit(test_exit);

module_param(alg, charp, 0); 

MODULE_LICENSE("GPL");
MODULE_AUTHOR("lql@lql.com");


测试

root@100:/lib/modules/3.18.21# insmod /tmp/test_kernal_crypto.ko alg="cbc(aes)"
[ 9332.350000] alg : cbc(aes)
[ 9332.350000] succ
[ 9332.360000] 00000000: c6 a1 3b 37 87 8f 5b 82 6f 4f 81 62 a1 c8 d8 79
[ 9332.370000] succ
[ 9332.370000] retire_capture_urb: 11 callbacks suppressed
[ 9332.390000] 00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff
failed to insert /tmp/test_kernal_crypto.ko
root@100:/lib/modules/3.18.21# insmod /tmp/test_kernal_crypto.ko alg="ctr(aes)"
[ 9335.680000] alg : ctr(aes)
[ 9335.680000] succ
[ 9335.690000] 00000000: 69 d5 c2 eb 2e 2e 62 47 50 54 1d 3b bc 69 2b a5
[ 9335.700000] succ
[ 9335.700000] 00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff
failed to insert /tmp/test_kernal_crypto.ko
root@100:/lib/modules/3.18.21# insmod /tmp/test_kernal_crypto.ko 
[ 9340.790000] alg : ecb(aes)
[ 9340.790000] succ
[ 9340.800000] 00000000: 69 c4 e0 d8 6a 7b 04 30 d8 cd b7 80 70 b4 c5 5a
[ 9340.810000] succ
[ 9340.810000] retire_capture_urb: 20 callbacks suppressed
[ 9340.820000] 00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff
failed to insert /tmp/test_kernal_crypto.ko
root@G100:/lib/modules/3.18.21# 

总结:
要学习 linux crypto API ,可以直接参考 linux-3.18.21/crypto/tcrypt.c

 类似资料: