This patch introduces AES-XCBC-MAC using the "keyed hash" interface.
It accordingly depends on the previous patch.

Signed-off-by: Kazunori MIYAZAWA <[EMAIL PROTECTED]>

---
commit f23d1b52e77f339a9eaa89a1b913d6373fe60fb6
tree bab6efc5808414f13dcefb4bb164c3e111000fa3
parent 9c6b9affbacf12fc1e32868eaa14fef279b2696c
author Kazunori MIYAZAWA <[EMAIL PROTECTED]> Tue, 13 Jun 2006 10:05:50 +0900
committer Kazunori MIYAZAWA <[EMAIL PROTECTED]> Tue, 13 Jun 2006 10:05:50 +0900

 crypto/Kconfig         |    9 ++
 crypto/Makefile        |    1 
 crypto/cipher.c        |   20 ++++
 crypto/internal.h      |   16 +++
 crypto/tcrypt.c        |   99 +++++++++++++++++++++
 crypto/tcrypt.h        |   80 +++++++++++++++++
 crypto/xcbc.c          |  230 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/crypto.h |   30 ++++++
 8 files changed, 482 insertions(+), 3 deletions(-)

diff --git a/crypto/Kconfig b/crypto/Kconfig
index c442f2e..8dc28e9 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -16,6 +16,15 @@ config CRYPTO_HMAC
          HMAC: Keyed-Hashing for Message Authentication (RFC2104).
          This is required for IPSec.
 
+config CRYPTO_XCBC
+       bool "XCBC support"
+       depends on CRYPTO && EXPERIMENTAL
+       help
+         XCBC: Keyed-Hashing with encryption algorithm
+               http://www.ietf.org/rfc/rfc3566.txt
+               http://csrc.nist.gov/encryption/modes/proposedmodes/
+                 xcbc-mac/xcbc-mac-spec.pdf
+
 config CRYPTO_NULL
        tristate "Null algorithms"
        depends on CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index d287b9e..781712d 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_CRYPTO) += api.o scatterwal
                        $(proc-crypto-y)
 
 obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
+obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
 obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o
 obj-$(CONFIG_CRYPTO_MD4) += md4.o
 obj-$(CONFIG_CRYPTO_MD5) += md5.o
diff --git a/crypto/cipher.c b/crypto/cipher.c
index 65bcea0..5605216 100644
--- a/crypto/cipher.c
+++ b/crypto/cipher.c
@@ -402,6 +402,7 @@ int crypto_init_cipher_ops(struct crypto
                break;
                
        case CRYPTO_TFM_MODE_CBC:
+       case CRYPTO_TFM_MODE_XCBC:
                ops->cit_encrypt = cbc_encrypt;
                ops->cit_decrypt = cbc_decrypt;
                ops->cit_encrypt_iv = cbc_encrypt_iv;
@@ -426,7 +427,7 @@ int crypto_init_cipher_ops(struct crypto
                BUG();
        }
        
-       if (ops->cit_mode == CRYPTO_TFM_MODE_CBC) {
+       if (ops->cit_mode & (CRYPTO_TFM_MODE_CBC | CRYPTO_TFM_MODE_XCBC)) {
                unsigned long align;
                unsigned long addr;
                
@@ -453,6 +454,16 @@ int crypto_init_cipher_ops(struct crypto
                addr = ALIGN(addr, align);
                addr += ALIGN(tfm->__crt_alg->cra_ctxsize, align);
                ops->cit_iv = (void *)addr;
+#ifdef CONFIG_CRYPTO_XCBC
+               if (ops->cit_mode & CRYPTO_TFM_MODE_XCBC) {
+                       tfm->crt_keyedhash.kht_init = crypto_xcbc_init;
+                       tfm->crt_keyedhash.kht_update = crypto_xcbc_update;
+                       tfm->crt_keyedhash.kht_final = crypto_xcbc_final;
+                       tfm->crt_keyedhash.keyed_hash = crypto_xcbc;
+                       tfm->crt_keyedhash.kht_digestsize = 
crypto_tfm_alg_blocksize;
+                       ret = crypto_alloc_xcbc_block(tfm);
+               }
+#endif
        }
 
 out:   
@@ -461,4 +472,11 @@ out:       
 
 void crypto_exit_cipher_ops(struct crypto_tfm *tfm)
 {
+#ifdef CONFIG_CRYPTO_XCBC
+       struct cipher_tfm *ops = &tfm->crt_cipher;
+       if (ops->cit_mode & CRYPTO_TFM_MODE_CBC &&
+               ops->cit_mode & CRYPTO_TFM_MODE_XCBC) {
+               crypto_free_xcbc_block(tfm);
+       }
+#endif
 }
diff --git a/crypto/internal.h b/crypto/internal.h
index 959e602..cc4244f 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -62,6 +62,22 @@ static inline void crypto_free_hmac_bloc
 { }
 #endif
 
+#ifdef CONFIG_CRYPTO_XCBC
+int crypto_alloc_xcbc_block(struct crypto_tfm *tfm);
+static inline void crypto_free_xcbc_block(struct crypto_tfm *tfm)
+{
+       kfree(tfm->crt_keyedhash.kht_xcbc_block);
+}
+#else
+static inline int crypto_alloc_xcbc_block(struct crypto_tfm *tfm)
+{
+       return 0;
+}
+
+static inline void crypto_free_xcbc_block(struct crypto_tfm *tfm)
+{ }
+#endif
+
 #ifdef CONFIG_PROC_FS
 void __init crypto_init_proc(void);
 #else
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 37aeced..0b4117a 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -253,6 +253,102 @@ out:
 
 #endif /* CONFIG_CRYPTO_HMAC */
 
+#ifdef CONFIG_CRYPTO_XCBC
+
+static void
+test_xcbc(char *algo, struct xcbc_testvec * template, unsigned int tcount)
+{
+       char *p;
+       unsigned int i, j, k, temp;
+       struct scatterlist sg[8];
+       char result[64];
+       struct crypto_tfm *tfm;
+       struct xcbc_testvec *xcbc_tv;
+       unsigned int tsize, klen;
+       u_int32_t ks[12] = {0x01010101, 0x01010101, 0x01010101, 0x01010101,
+                           0x02020202, 0x02020202, 0x02020202, 0x02020202,
+                           0x03030303, 0x03030303, 0x03030303, 0x03030303}; 
+
+       tfm = crypto_alloc_tfm(algo, CRYPTO_TFM_MODE_XCBC);
+       if (tfm == NULL) {
+               printk("failed to load transform for %s\n", algo);
+               return;
+       }
+
+       tfm->crt_keyedhash.kht_xcbc_const = (u8*)ks;
+
+       printk("\ntesting xcbc_%s\n", algo);
+       
+       tsize = sizeof (struct xcbc_testvec);
+       tsize *= tcount;
+       if (tsize > TVMEMSIZE) {
+               printk("template (%u) too big for tvmem (%u)\n", tsize,
+                      TVMEMSIZE);
+               goto out;
+       }
+
+       memcpy(tvmem, template, tsize);
+       xcbc_tv = (void *) tvmem;
+
+       for (i = 0; i < tcount; i++) {
+               printk("test %u:\n", i + 1);
+               memset(result, 0, sizeof (result));
+
+               p = xcbc_tv[i].plaintext;
+               klen = xcbc_tv[i].ksize;
+               sg[0].page = virt_to_page(p);
+               sg[0].offset = offset_in_page(p);
+               sg[0].length = xcbc_tv[i].psize;
+
+               crypto_xcbc(tfm, xcbc_tv[i].key, &klen, sg, 1, result);
+
+               hexdump(result, crypto_tfm_alg_blocksize(tfm));
+               printk("%s\n",
+                      memcmp(result, xcbc_tv[i].digest,
+                             crypto_tfm_alg_blocksize(tfm)) ? "fail" :
+                      "pass");
+       }
+
+       printk("\ntesting xcbc_%s across pages\n", algo);
+
+       memset(xbuf, 0, XBUFSIZE);
+       
+       j = 0;
+       for (i = 0; i < tcount; i++) {
+               if (xcbc_tv[i].np) {
+                       j++;
+                       printk ("test %u:\n",j);
+                       memset (result, 0, 64);
+
+                       temp = 0;
+                       klen = xcbc_tv[i].ksize;
+                       for (k = 0; k < xcbc_tv[i].np; k++) {
+                               memcpy (&xbuf[IDX[k]], xcbc_tv[i].plaintext + 
temp, 
+                                               xcbc_tv[i].tap[k]);     
+                               temp += xcbc_tv[i].tap[k];
+                               p = &xbuf[IDX[k]];
+                               sg[k].page = virt_to_page (p);
+                               sg[k].offset = offset_in_page (p);
+                               sg[k].length = xcbc_tv[i].tap[k];
+                       }
+
+                       crypto_xcbc(tfm, xcbc_tv[i].key, &klen, sg, 
xcbc_tv[i].np, 
+                                       result);
+                       hexdump(result, crypto_tfm_alg_blocksize(tfm));
+                       
+                       printk("%s\n",
+                               memcmp(result, xcbc_tv[i].digest,
+                                       crypto_tfm_alg_blocksize(tfm)) ? "fail" 
: 
+                               "pass");
+               }
+       }
+out:
+       crypto_free_tfm(tfm);
+}
+
+#endif /* CONFIG_CRYPTO_XCBC */
+
+
 static void test_cipher(char *algo, int mode, int enc,
                        struct cipher_testvec *template, unsigned int tcount)
 {
@@ -857,6 +953,9 @@ static void do_test(void)
                test_hmac("md5", hmac_md5_tv_template, HMAC_MD5_TEST_VECTORS);
                test_hmac("sha1", hmac_sha1_tv_template, 
HMAC_SHA1_TEST_VECTORS);
                test_hmac("sha256", hmac_sha256_tv_template, 
HMAC_SHA256_TEST_VECTORS);
+#endif         
+#ifdef CONFIG_CRYPTO_XCBC
+               test_xcbc("aes", aes_xcbc_tv_template, XCBC_AES_TEST_VECTORS);
 #endif
 
                test_hash("michael_mic", michael_mic_tv_template, 
MICHAEL_MIC_TEST_VECTORS);
diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h
index 1f683ba..6c1361f 100644
--- a/crypto/tcrypt.h
+++ b/crypto/tcrypt.h
@@ -46,6 +46,16 @@ struct hmac_testvec {
        unsigned char np;
 };
 
+struct xcbc_testvec {  
+       char key[128];
+       unsigned char ksize;
+       char plaintext[128];
+       unsigned char psize;
+       char digest[MAX_DIGEST_SIZE];
+       unsigned char np;
+       unsigned char tap[MAX_TAP];             
+};
+
 struct cipher_testvec {
        char key[MAX_KEYLEN] __attribute__ ((__aligned__(4)));
        char iv[MAX_IVLEN];
@@ -941,6 +951,76 @@ static struct hmac_testvec hmac_sha256_t
 
 #endif /* CONFIG_CRYPTO_HMAC */
 
+#define XCBC_AES_TEST_VECTORS 6
+
+#ifdef CONFIG_CRYPTO_XCBC
+static struct xcbc_testvec aes_xcbc_tv_template[] = {
+       {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { [0 ... 127] = 0 },
+               .psize  = 0,
+               .digest = { 0x75, 0xf0, 0x25, 0x1d, 0x52, 0x8a, 0xc0, 0x1c,
+                           0x45, 0x73, 0xdf, 0xd5, 0x84, 0xd7, 0x9f, 0x29 },
+       }, {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { 0x00, 0x01, 0x02 },
+               .psize  = 3,
+               .digest = { 0x5b, 0x37, 0x65, 0x80, 0xae, 0x2f, 0x19, 0xaf,
+                           0xe7, 0x21, 0x9c, 0xee, 0xf1, 0x72, 0x75, 0x6f },
+       } , {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .psize  = 16,
+               .digest = { 0xd2, 0xa2, 0x46, 0xfa, 0x34, 0x9b, 0x68, 0xa7,
+                           0x99, 0x98, 0xa4, 0x39, 0x4f, 0xf7, 0xa2, 0x63 },
+       }, {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                           0x10, 0x11, 0x12, 0x13 },
+               .psize  = 20,
+               .digest = { 0x47, 0xf5, 0x1b, 0x45, 0x64, 0x96, 0x62, 0x15,
+                           0xb8, 0x98, 0x5c, 0x63, 0x05, 0x5e, 0xd3, 0x08 },
+               .np     = 2,
+               .tap    = {10, 10},
+       }, {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                           0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                           0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
+               .psize  = 32,
+               .digest = { 0xf5, 0x4f, 0x0e, 0xc8, 0xd2, 0xb9, 0xf3, 0xd3,
+                           0x68, 0x07, 0x73, 0x4b, 0xd5, 0x28, 0x3f, 0xd4 },
+       }, {
+               .key    = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+               .ksize  = 16,
+               .plaintext      = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
0x07,
+                           0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                           0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                           0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+                           0x20, 0x21 },
+               .psize  = 34,
+               .digest = { 0xbe, 0xcb, 0xb3, 0xbc, 0xcd, 0xb5, 0x18, 0xa3,
+                           0x06, 0x77, 0xd5, 0x48, 0x1f, 0xb6, 0xb4, 0xd8 },
+               .np     = 2,
+               .tap    = {17,17},
+       }
+};
+#endif
+
 /*
  * DES test vectors.
  */
diff --git a/crypto/xcbc.c b/crypto/xcbc.c
new file mode 100644
index 0000000..0cc23f0
--- /dev/null
+++ b/crypto/xcbc.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C)2005,2006 USAGI/WIDE Project
+ * 
+ * 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.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author:
+ *     Kazunori Miyazawa <[EMAIL PROTECTED]>
+ */
+
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <asm/scatterlist.h>
+#include "internal.h"
+
+struct xcbc_ops {
+       unsigned int len;
+       u8 *prev;
+};
+
+int crypto_alloc_xcbc_block(struct crypto_tfm *tfm)
+{
+       struct xcbc_ops *ops;
+
+       BUG_ON(!crypto_tfm_alg_blocksize(tfm));
+
+       ops = kmalloc(sizeof(*ops) + crypto_tfm_alg_blocksize(tfm), GFP_KERNEL);
+       if (ops == NULL)
+               return -ENOMEM;
+       ops->len = 0;
+       ops->prev = (u8*)(ops + 1);
+
+       tfm->crt_keyedhash.kht_xcbc_block = ops;
+       tfm->crt_keyedhash.kht_xcbc_const = NULL;
+
+       return 0;
+}
+
+static int _crypto_xcbc_init(struct crypto_tfm *tfm, u8 *key, unsigned int 
keylen)
+{
+       const unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+       u8 key1[bsize];
+       int err;
+
+       if (!(tfm->crt_cipher.cit_mode & CRYPTO_TFM_MODE_XCBC))
+               return -EINVAL;
+
+       if (keylen != crypto_tfm_alg_blocksize(tfm))
+               return -EINVAL;
+
+       if ((err = crypto_cipher_setkey(tfm, key, keylen)))
+           return err;
+
+       tfm->__crt_alg->cra_cipher.cia_encrypt(crypto_tfm_ctx(tfm), key1,
+                       (const u8*)tfm->crt_keyedhash.kht_xcbc_const);
+
+       return crypto_cipher_setkey(tfm, key1, bsize);
+
+}
+
+int crypto_xcbc_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen)
+{
+       struct xcbc_ops *ops = (struct 
xcbc_ops*)tfm->crt_keyedhash.kht_xcbc_block;
+
+       if (tfm->crt_keyedhash.kht_xcbc_const == NULL) 
+               return -EINVAL;
+
+       ops->len = 0;
+       memset(ops->prev, 0, crypto_tfm_alg_blocksize(tfm));
+       memset(tfm->crt_cipher.cit_iv, 0, crypto_tfm_alg_blocksize(tfm));
+       return _crypto_xcbc_init(tfm, key, *keylen);
+}
+
+void crypto_xcbc_update(struct crypto_tfm *tfm, struct scatterlist *sg, 
unsigned int nsg)
+{
+       struct xcbc_ops *ops = (struct 
xcbc_ops*)tfm->crt_keyedhash.kht_xcbc_block;
+       const unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+       unsigned int i;
+
+       if (!(tfm->crt_cipher.cit_mode & CRYPTO_TFM_MODE_XCBC))
+               return;
+ 
+       for(i = 0; i < nsg; i++) {
+
+               struct page *pg = sg[i].page;
+               unsigned int offset = sg[i].offset;
+               unsigned int slen = sg[i].length;
+
+               while (slen > 0) {
+                       unsigned int len = min(slen, ((unsigned 
int)(PAGE_SIZE)) - offset);
+                       char *p = crypto_kmap(pg, 0) + offset;
+
+                       /* checking the data can fill the block */
+                       if ((ops->len + len) <= bsize) {
+                               memcpy(ops->prev + ops->len, p, len);
+                               ops->len += len;
+                               slen -= len;
+
+                               /* checking the rest of the page */
+                               if (len + offset >= PAGE_SIZE) {
+                                       offset = 0;
+                                       pg++;
+                               } else
+                                       offset += len;
+
+                               crypto_kunmap(p, 0);
+                               crypto_yield(tfm);
+                               continue;
+                       }
+
+                       /* filling ops->prev with new data and encrypting it */
+                       memcpy(ops->prev + ops->len, p, bsize - ops->len);
+                       len -= bsize - ops->len;
+                       p += bsize - ops->len;
+                       tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv,
+                                                       ops->prev);
+                       tfm->__crt_alg->cra_cipher.cia_encrypt(
+                               crypto_tfm_ctx(tfm), tfm->crt_cipher.cit_iv,
+                               tfm->crt_cipher.cit_iv);
+
+                       /* clearing the length */
+                       ops->len = 0;
+
+                       /* encrypting the rest of data */
+                       while (len > bsize) {
+                               
tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv, p);
+                               tfm->__crt_alg->cra_cipher.cia_encrypt(
+                                       crypto_tfm_ctx(tfm), 
tfm->crt_cipher.cit_iv,
+                                       tfm->crt_cipher.cit_iv);
+                               p += bsize;
+                               len -= bsize;
+                       }
+
+                       /* keeping the surplus of blocksize */
+                       if (len) {
+                               memcpy(ops->prev, p, len);
+                               ops->len = len;
+                       }
+                       crypto_kunmap(p, 0);
+                       crypto_yield(tfm);
+                       slen -= min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
+                       offset = 0;
+                       pg++;
+               }
+       }
+}
+
+int crypto_xcbc_final(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen, 
u8 *out)
+{
+       struct xcbc_ops *ops = (struct 
xcbc_ops*)tfm->crt_keyedhash.kht_xcbc_block;
+       const unsigned int bsize = crypto_tfm_alg_blocksize(tfm);
+       int ret = 0;
+
+       if (!(tfm->crt_cipher.cit_mode & CRYPTO_TFM_MODE_XCBC))
+               return -EINVAL;
+
+       if (*keylen != bsize)
+               return -EINVAL;
+
+       if (ops->len == bsize) {
+               u8 key2[bsize];
+
+               if ((ret = crypto_cipher_setkey(tfm, key, *keylen)))
+                       return ret;
+
+               tfm->__crt_alg->cra_cipher.cia_encrypt(crypto_tfm_ctx(tfm), 
key2,
+                               (const 
u8*)(tfm->crt_keyedhash.kht_xcbc_const+bsize));
+               tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv, 
ops->prev);
+               tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv, key2);
+
+               _crypto_xcbc_init(tfm, key, *keylen);
+
+               tfm->__crt_alg->cra_cipher.cia_encrypt(crypto_tfm_ctx(tfm), 
out, tfm->crt_cipher.cit_iv);
+       } else {
+               u8 key3[bsize];
+               unsigned int rlen;
+               u8 *p = ops->prev + ops->len;
+               *p = 0x80;
+               p++;
+
+               rlen = bsize - ops->len -1;
+               if (rlen)
+                       memset(p, 0, rlen);
+
+               if ((ret = crypto_cipher_setkey(tfm, key, *keylen)))
+                       return ret;
+
+               tfm->__crt_alg->cra_cipher.cia_encrypt(crypto_tfm_ctx(tfm), 
key3,
+                               (const 
u8*)(tfm->crt_keyedhash.kht_xcbc_const+bsize*2));
+
+               tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv, 
ops->prev);
+               tfm->crt_u.cipher.cit_xor_block(tfm->crt_cipher.cit_iv, key3);
+               _crypto_xcbc_init(tfm, key, *keylen);
+               tfm->__crt_alg->cra_cipher.cia_encrypt(crypto_tfm_ctx(tfm), 
out, tfm->crt_cipher.cit_iv);
+       }
+
+       return ret;
+}
+
+int crypto_xcbc(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen,
+               struct scatterlist *sg, unsigned int nsg, u8 *out)
+{
+       int ret = 0;
+
+       ret = crypto_xcbc_init(tfm, key, keylen);
+       if (ret)
+               return ret;
+       crypto_xcbc_update(tfm, sg, nsg);
+       ret = crypto_xcbc_final(tfm, key, keylen, out);
+
+       return ret;
+}
+
+EXPORT_SYMBOL_GPL(crypto_xcbc_init);
+EXPORT_SYMBOL_GPL(crypto_xcbc_update);
+EXPORT_SYMBOL_GPL(crypto_xcbc_final);
+EXPORT_SYMBOL_GPL(crypto_xcbc);
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 1ade651..c4bc3c2 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -45,6 +45,7 @@
 #define CRYPTO_TFM_MODE_CFB            0x00000004
 #define CRYPTO_TFM_MODE_CTR            0x00000008
 #define CRYPTO_TFM_MODE_HMAC           0x00000080
+#define CRYPTO_TFM_MODE_XCBC           0x00000080
 
 #define CRYPTO_TFM_REQ_WEAK_KEY                0x00000100
 #define CRYPTO_TFM_REQ_MAY_SLEEP       0x00000200
@@ -217,9 +218,22 @@ struct compress_tfm {
 #define crt_digest     crt_u.digest
 #define crt_compress   crt_u.compress
 
-#if defined(CONFIG_CRYPTO_HMAC)
+#if defined(CONFIG_CRYPTO_HMAC) || defined(CONFIG_CRYPTO_XCBC)
+struct cit_xcbc_ctx {
+       /*
+        * XCBC needs 3 constants for the derived keys.
+        * Each lengh of those must equal the cipher
+        * block size so that cit_xcbc_const's length is
+        * 3 times of the blocksize and it contains
+        * concantinated constants.
+        */
+       void *_block;
+       u8 *_const;
+};
 
 #define kht_hmac_block kht_u.hmac_block
+#define kht_xcbc_block kht_u.xcbc_ctx._block
+#define kht_xcbc_const kht_u.xcbc_ctx._const
 
 struct keyed_hash_tfm {
        int (*kht_init)(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen);
@@ -230,6 +244,7 @@ struct keyed_hash_tfm {
        unsigned int (*kht_digestsize)(struct crypto_tfm *tfm);
        union {
                void *hmac_block;
+               struct cit_xcbc_ctx xcbc_ctx;
        } kht_u;
 };
 #endif
@@ -459,5 +474,16 @@ int crypto_hmac(struct crypto_tfm *tfm, 
                  struct scatterlist *sg, unsigned int nsg, u8 *out);
 #endif /* CONFIG_CRYPTO_HMAC */
 
-#endif /* _LINUX_CRYPTO_H */
+/*
+ *  * XCBC support
+ *   */
+#ifdef CONFIG_CRYPTO_XCBC
+int crypto_xcbc_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen);
+void crypto_xcbc_update(struct crypto_tfm *tfm, struct scatterlist *sg, 
unsigned int nsg);
+int crypto_xcbc_final(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen, u8
+               *out);
+int crypto_xcbc(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen,
+                               struct scatterlist *sg, unsigned int nsg, u8 
*out);
+#endif /* CONFIG_CRYPTO_XCBC */
+#endif  /* _LINUX_CRYPTO_H */
 


-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to