diff options
author | Mikko Herranen <mh1@iki.fi> | 2007-11-26 15:12:07 +0100 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2008-01-10 22:16:23 +0100 |
commit | e3a4ea4fd2e5f154ae9233f1ce30e7564e5cbcfc (patch) | |
tree | 8564aa42c333db6170cdc5dfc93e7a82bc315b27 /crypto/tcrypt.c | |
parent | [CRYPTO] camellia: Move more common code into camellia_setup_tail (diff) | |
download | linux-e3a4ea4fd2e5f154ae9233f1ce30e7564e5cbcfc.tar.xz linux-e3a4ea4fd2e5f154ae9233f1ce30e7564e5cbcfc.zip |
[CRYPTO] tcrypt: Add aead support
Add AEAD support to tcrypt, needed by GCM.
Signed-off-by: Mikko Herranen <mh1@iki.fi>
Reviewed-by: Mika Kukkonen <mika.kukkonen@nsn.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto/tcrypt.c')
-rw-r--r-- | crypto/tcrypt.c | 258 |
1 files changed, 249 insertions, 9 deletions
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index b8cb1d1420ae..b343d81d20c9 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -6,12 +6,14 @@ * * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> * Copyright (c) 2002 Jean-Francois Dive <jef@linuxbe.org> + * Copyright (c) 2007 Nokia Siemens Networks * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * + * 2007-11-13 Added AEAD support * 2007-11-06 Added SHA-224 and SHA-224-HMAC tests * 2006-12-07 Added SHA384 HMAC and SHA512 HMAC tests * 2004-08-09 Added cipher speed tests (Reyk Floeter <reyk@vantronix.net>) @@ -72,6 +74,7 @@ static unsigned int sec; static int mode; static char *xbuf; +static char *axbuf; static char *tvmem; static char *check[] = { @@ -169,6 +172,7 @@ static void test_hash(char *algo, struct hash_testvec *template, /* setup the dummy buffer first */ memset(xbuf, 0, XBUFSIZE); + memset(axbuf, 0, XBUFSIZE); j = 0; for (i = 0; i < tcount; i++) { @@ -217,6 +221,233 @@ out: crypto_free_hash(tfm); } +static void test_aead(char *algo, int enc, struct aead_testvec *template, + unsigned int tcount) +{ + unsigned int ret, i, j, k, temp; + unsigned int tsize; + char *q; + struct crypto_aead *tfm; + char *key; + struct aead_testvec *aead_tv; + struct aead_request *req; + struct scatterlist sg[8]; + struct scatterlist asg[8]; + const char *e; + struct tcrypt_result result; + + if (enc == ENCRYPT) + e = "encryption"; + else + e = "decryption"; + + printk(KERN_INFO "\ntesting %s %s\n", algo, e); + + tsize = sizeof(struct aead_testvec); + tsize *= tcount; + + if (tsize > TVMEMSIZE) { + printk(KERN_INFO "template (%u) too big for tvmem (%u)\n", + tsize, TVMEMSIZE); + return; + } + + memcpy(tvmem, template, tsize); + aead_tv = (void *)tvmem; + + init_completion(&result.completion); + + tfm = crypto_alloc_aead(algo, 0, 0); + + if (IS_ERR(tfm)) { + printk(KERN_INFO "failed to load transform for %s: %ld\n", + algo, PTR_ERR(tfm)); + return; + } + + req = aead_request_alloc(tfm, GFP_KERNEL); + if (!req) { + printk(KERN_INFO "failed to allocate request for %s\n", algo); + goto out; + } + + aead_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + tcrypt_complete, &result); + + for (i = 0, j = 0; i < tcount; i++) { + if (!aead_tv[i].np) { + printk(KERN_INFO "test %u (%d bit key):\n", + ++j, aead_tv[i].klen * 8); + + crypto_aead_clear_flags(tfm, ~0); + if (aead_tv[i].wk) + crypto_aead_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + key = aead_tv[i].key; + + ret = crypto_aead_setkey(tfm, key, + aead_tv[i].klen); + if (ret) { + printk(KERN_INFO "setkey() failed flags=%x\n", + crypto_aead_get_flags(tfm)); + + if (!aead_tv[i].fail) + goto out; + } + + sg_init_one(&sg[0], aead_tv[i].input, + aead_tv[i].ilen); + + sg_init_one(&asg[0], aead_tv[i].assoc, + aead_tv[i].alen); + + aead_request_set_crypt(req, sg, sg, + aead_tv[i].ilen, + aead_tv[i].iv); + + aead_request_set_assoc(req, asg, aead_tv[i].alen); + + if (enc) { + ret = crypto_aead_encrypt(req); + } else { + memcpy(req->__ctx, aead_tv[i].tag, + aead_tv[i].tlen); + ret = crypto_aead_decrypt(req); + } + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !(ret = result.err)) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_INFO "%s () failed err=%d\n", + e, -ret); + goto out; + } + + q = kmap(sg_page(&sg[0])) + sg[0].offset; + hexdump(q, aead_tv[i].rlen); + printk(KERN_INFO "auth tag: "); + hexdump((unsigned char *)req->__ctx, aead_tv[i].tlen); + + printk(KERN_INFO "enc/dec: %s\n", + memcmp(q, aead_tv[i].result, + aead_tv[i].rlen) ? "fail" : "pass"); + + printk(KERN_INFO "auth tag: %s\n", + memcmp(req->__ctx, aead_tv[i].tag, + aead_tv[i].tlen) ? "fail" : "pass"); + } + } + + printk(KERN_INFO "\ntesting %s %s across pages (chunking)\n", algo, e); + memset(xbuf, 0, XBUFSIZE); + + for (i = 0, j = 0; i < tcount; i++) { + if (aead_tv[i].np) { + printk(KERN_INFO "test %u (%d bit key):\n", + ++j, aead_tv[i].klen * 8); + + crypto_aead_clear_flags(tfm, ~0); + if (aead_tv[i].wk) + crypto_aead_set_flags( + tfm, CRYPTO_TFM_REQ_WEAK_KEY); + key = aead_tv[i].key; + + ret = crypto_aead_setkey(tfm, key, aead_tv[i].klen); + if (ret) { + printk(KERN_INFO "setkey() failed flags=%x\n", + crypto_aead_get_flags(tfm)); + + if (!aead_tv[i].fail) + goto out; + } + + sg_init_table(sg, aead_tv[i].np); + for (k = 0, temp = 0; k < aead_tv[i].np; k++) { + memcpy(&xbuf[IDX[k]], + aead_tv[i].input + temp, + aead_tv[i].tap[k]); + temp += aead_tv[i].tap[k]; + sg_set_buf(&sg[k], &xbuf[IDX[k]], + aead_tv[i].tap[k]); + } + + sg_init_table(asg, aead_tv[i].anp); + for (k = 0, temp = 0; k < aead_tv[i].anp; k++) { + memcpy(&axbuf[IDX[k]], + aead_tv[i].assoc + temp, + aead_tv[i].atap[k]); + temp += aead_tv[i].atap[k]; + sg_set_buf(&asg[k], &axbuf[IDX[k]], + aead_tv[i].atap[k]); + } + + aead_request_set_crypt(req, sg, sg, + aead_tv[i].ilen, + aead_tv[i].iv); + + aead_request_set_assoc(req, asg, aead_tv[i].alen); + + if (enc) { + ret = crypto_aead_encrypt(req); + } else { + memcpy(req->__ctx, aead_tv[i].tag, + aead_tv[i].tlen); + ret = crypto_aead_decrypt(req); + } + + switch (ret) { + case 0: + break; + case -EINPROGRESS: + case -EBUSY: + ret = wait_for_completion_interruptible( + &result.completion); + if (!ret && !(ret = result.err)) { + INIT_COMPLETION(result.completion); + break; + } + /* fall through */ + default: + printk(KERN_INFO "%s () failed err=%d\n", + e, -ret); + goto out; + } + + for (k = 0, temp = 0; k < aead_tv[i].np; k++) { + printk(KERN_INFO "page %u\n", k); + q = kmap(sg_page(&sg[k])) + sg[k].offset; + hexdump(q, aead_tv[i].tap[k]); + printk(KERN_INFO "%s\n", + memcmp(q, aead_tv[i].result + temp, + aead_tv[i].tap[k]) ? + "fail" : "pass"); + + temp += aead_tv[i].tap[k]; + } + printk(KERN_INFO "auth tag: "); + hexdump((unsigned char *)req->__ctx, aead_tv[i].tlen); + + printk(KERN_INFO "auth tag: %s\n", + memcmp(req->__ctx, aead_tv[i].tag, + aead_tv[i].tlen) ? "fail" : "pass"); + } + } + +out: + crypto_free_aead(tfm); + aead_request_free(req); +} + static void test_cipher(char *algo, int enc, struct cipher_testvec *template, unsigned int tcount) { @@ -1497,20 +1728,21 @@ static void do_test(void) static int __init init(void) { + int err = -ENOMEM; + tvmem = kmalloc(TVMEMSIZE, GFP_KERNEL); if (tvmem == NULL) - return -ENOMEM; + return err; xbuf = kmalloc(XBUFSIZE, GFP_KERNEL); - if (xbuf == NULL) { - kfree(tvmem); - return -ENOMEM; - } + if (xbuf == NULL) + goto err_free_tv; - do_test(); + axbuf = kmalloc(XBUFSIZE, GFP_KERNEL); + if (axbuf == NULL) + goto err_free_xbuf; - kfree(xbuf); - kfree(tvmem); + do_test(); /* We intentionaly return -EAGAIN to prevent keeping * the module. It does all its work from init() @@ -1518,7 +1750,15 @@ static int __init init(void) * => we don't need it in the memory, do we? * -- mludvig */ - return -EAGAIN; + err = -EAGAIN; + + kfree(axbuf); + err_free_xbuf: + kfree(xbuf); + err_free_tv: + kfree(tvmem); + + return err; } /* |