I only dealt with encryption and decryption, compression and hash seem similar but still need relative hack work if you try to use them.
Firstly you need allocate a cipher, which is identified by both the cipher mode(say ECB) and the cipher algorithm(say AES). The API is struct crypto_blkcipher crypto_alloc_blkcipher(char* name, type, mask). I have no idea about the type and mask so simply set them 0. The name is in such format "CipherMode(CipherAlgorithm)", e.g. "ecb(aes)". You can find examples from /proc/crypto and by loading all modules from /lib/module/kernel-verion/kernel/crypto and then check /proc/crypto. An algorithm has two IDs: name and driver. Name may be shared by other implementations like a HW-accelerated one, but driver is unique. So you can specify the driver to alloc exact what you want, like "ecb(aes-generic)" to avoid loading "ecb(aes-asm)" in which the 'aes-asm' has higher priority than 'aes-generic' when allocating a cipher.
Data are represented in a scatterlist which you can think to be a triple tuple (page_address, offset, len). It is a struct scatterlist. You can initialize a list of scatterlist objects by sg_init_table(scatterlist*, n). Don't be confused by the name scatterlist, it is actually an element because it just has three important fields as described in the triple tuple. To set the tuple, use sg_set_buf(scatterlist*, void* buf, len) which means this scatterlist tuple represent a buffer from buf and has a length of len.
Before encryption/decryption, set the key with crypto_blkcipher_setkey(struct crypto_blkcipher*, char* key, key_len) to set a key with length of key_len for the cipher. Then save the cipher in a struct blkcipher_desc. (See the code)
Then you can do real things with crypto_blkcipher_encrypt(struct blkcipher_desc*, scatterlist *dst, scatterlist *src, len) and crypto_blkcipher_decrypt(struct blkcipher_desc*, scatterlist *dst, scatterlist *src, len).
Finally free the cipher with crypto_free_blkcipher(struct *crypto_blkcipher).
Here is an example test of AES128-ECB:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/gfp.h>
#include <linux/err.h>
#include <linux/timex.h>
#define MAX_BLK_SIZE (64*1024*1024)
#define MIN_BLK_SIZE (16)
#define TEST_TIMES 100
/* Test ECB(AES-128) */
void test_aes(void)
{
struct crypto_blkcipher *tfm;
struct blkcipher_desc desc;
u32 bs;
int i,j;
u32 npages;
struct scatterlist *src;
struct scatterlist *dst;
char *buf;
char **ins, **outs;
unsigned int ret;
u8 key[] = {0x00, 0x01, 0x02, 0x03, 0x05, 0x06, 0x07,
0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x12};
npages = MAX_BLK_SIZE/PAGE_SIZE;
src = kmalloc(npages*sizeof(struct scatterlist), __GFP_ZERO|GFP_KERNEL);
if (!src) {
printk("taes ERROR: failed to alloc src\n");
return;
}
dst = kmalloc(npages*sizeof(struct scatterlist), __GFP_ZERO|GFP_KERNEL);
if (!dst) {
printk("taes ERROR: failed to alloc dst\n");
kfree(src);
return;
}
ins = kmalloc(npages*sizeof(char*), __GFP_ZERO|GFP_KERNEL);
if (!ins) {
printk("taes ERROR: failed to alloc ins\n");
kfree(src);
kfree(dst);
return;
}
outs = kmalloc(npages*sizeof(char*), __GFP_ZERO|GFP_KERNEL);
if (!outs) {
printk("taes ERROR: failed to alloc outs\n");
kfree(src);
kfree(dst);
kfree(ins);
return;
}
tfm = crypto_alloc_blkcipher("ecb(aes-generic)", 0, 0);
if (IS_ERR(tfm)) {
printk("failed to load transform for %s: %ld\n", AES_GENERIC,
PTR_ERR(tfm));
goto out;
}
desc.tfm = tfm;
desc.flags = 0;
ret = crypto_blkcipher_setkey(tfm, key, sizeof(key));
if (ret) {
printk("setkey() failed flags=%x\n",
crypto_blkcipher_get_flags(tfm));
goto out;
}
sg_init_table(src, npages);
for (i=0; i<npages; i++) {
buf = (void *)__get_free_page(GFP_KERNEL);
if (!buf) {
printk("taes ERROR: alloc free page error\n");
goto free_err_pages;
}
ins[i] = buf;
sg_set_buf(src+i, buf, PAGE_SIZE);
buf = (void *)__get_free_page(GFP_KERNEL);
if (!buf) {
printk("taes ERROR: alloc free page error\n");
goto free_err_pages;
}
outs[i] = buf;
sg_set_buf(dst+i, buf, PAGE_SIZE);
}
for (bs = MIN_BLK_SIZE; bs <= MAX_BLK_SIZE; bs*=2) {
struct timeval t0, t1;
long int enc, dec;
do_gettimeofday(&t0);
for (j=0; j<TEST_TIMES; j++) {
if (j%2==0)
ret = crypto_blkcipher_encrypt(&desc, dst, src, bs);
else
ret = crypto_blkcipher_encrypt(&desc, src, dst, bs);
if (ret) {
printk("taes ERROR: enc error\n");
goto free_err_pages;
}
}
do_gettimeofday(&t1);
enc = 1000000*(t1.tv_sec-t0.tv_sec) +
((int)(t1.tv_usec) - (int)(t0.tv_usec));
do_gettimeofday(&t0);
for (j=0; j<TEST_TIMES; j++) {
if (j%2==0)
ret = crypto_blkcipher_decrypt(&desc, src, dst, bs);
else
ret = crypto_blkcipher_decrypt(&desc, dst, src, bs);
if (ret) {
printk("taes ERROR: dec error\n");
goto free_err_pages;
}
}
do_gettimeofday(&t1);
dec = 1000000*(t1.tv_sec-t0.tv_sec) +
((int)(t1.tv_usec) - (int)(t0.tv_usec));
printk("Size %u, enc %ld, dec %ld\n",
bs, enc, dec);
}
free_err_pages:
for (i=0; i<npages && ins[i]; i++){
free_page((unsigned long)ins[i]);
}
for (i=0; i<npages && outs[i]; i++){
free_page((unsigned long)outs[i]);
}
out:
kfree(src);
kfree(dst);
kfree(ins);
kfree(outs);
crypto_free_blkcipher(tfm);
}
3 comments:
Thanks for good example - I've tried and tired for googling some simple snippet, which shows how to use Linux CryptoAPI - you're the best so far :-)
But your snippet, just like as in crypto/tcrypt.c from kernel - a test, not demo for encryption/decryption. So, I will be very appreciate, if you could explaining real demo snippet, probably, by updating post.
This information will be useful not for me, I guess (and hope), because as far as I can see and understand, CryptoAPI has really poor documentation, especially for newbies in kernel internals. So, my main question about your code - how exactly plain/cipher text should be setting up? I guess, that this can be something like
strcpy(src, "secret plain text");
for plain text and
strcpy(dst, ciphertext);
for cipher text, where ciphertext - pointer (to, probably, char array).
But how and when exactly we must to call these functions? Before sg_set_buf(...), or after? And how easily get access for data after decryption/encryption? Do we get plaintext in dst just right after crypto_blkcipher_decrypt(...)? Simple demo without any tests and unnecessary cycles would be very great - just snippet for, let's say, aes-cbc with block size 512. I will be very appreciate for such demo, because there is so much confuse in all this sg_* routine
But thanks a lot anyway.
static void test_cipher(int enc, char *buf,const char *key,unsigned int keylen)
{
unsigned int ret, i, j, iv_len;
char iv[128];
struct crypto_blkcipher *tfm;
struct blkcipher_desc desc;
struct scatterlist sg[4];
tfm = crypto_alloc_base("ecb(aes)", 0, 0);
if (IS_ERR(tfm)) {
printk("failed to load transform for %s: %ld\n","aes",
PTR_ERR(tfm));
//return;
}
desc.tfm = tfm;
desc.flags = 0;
/* set key, plain text and IV */
ret = crypto_blkcipher_setkey(tfm, key,32);
if (ret) {
printk("setkey() failed flags=%x\n",
crypto_blkcipher_get_flags(tfm));
//goto out;
}
sg_init_table(sg,1);
sg_set_buf(sg,buf,16);
ret=sg_copy_from_buffer(sg,1,buf,16);
iv_len = crypto_blkcipher_ivsize(tfm);
if (iv_len) {
memset(&iv, 0xff, iv_len);
crypto_blkcipher_set_iv(tfm, iv, iv_len);
}
if(enc==ENCRYPT)
ret=crypto_blkcipher_encrypt(&desc,sg,sg,16);
else
ret=crypto_blkcipher_decrypt(&desc,sg,sg,16);
ret=sg_copy_to_buffer(sg,1,buf,16);
out:
crypto_free_blkcipher(tfm);
}
this is my code but tfm is retured null can you please help me.
.
Post a Comment