Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 574

kernel-2.6.18-238.el5.src.rpm

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Sun, 6 Jan 2008 16:09:34 +1100
Subject: [crypto] xcbc: new algorithm
Message-id: E1JBNlO-0001Bz-00@gondolin.me.apana.org.au
O-Subject: [PATCH 19/32] [CRYPTO] xcbc: New algorithm
Bugzilla: 253051

[CRYPTO] xcbc: New algorithm

This is core code of XCBC.

XCBC is an algorithm that forms a MAC algorithm out of a cipher algorithm.
For example, AES-XCBC-MAC is a MAC algorithm based on the AES cipher
algorithm.

Signed-off-by: Kazunori MIYAZAWA <miyazawa@linux-ipv6.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Acked-by: "David S. Miller" <davem@redhat.com>

diff --git a/crypto/Kconfig b/crypto/Kconfig
index 14c841f..5fa1fb5 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -71,6 +71,17 @@ config CRYPTO_NHMAC
 	  HMAC: Keyed-Hashing for Message Authentication (RFC2104).
 	  This is required for IPSec.
 
+config CRYPTO_XCBC
+	tristate "XCBC support"
+	depends on EXPERIMENTAL
+	select CRYPTO_HASH
+	select CRYPTO_MANAGER
+	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 eab5d3f..8e81b7d 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_CRYPTO_API) += crypto_api.o
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
 obj-$(CONFIG_CRYPTO_HMAC) += hmac_old.o
 obj-$(CONFIG_CRYPTO_NHMAC) += 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/tcrypt.c b/crypto/tcrypt.c
index 59518b7..d0e91ae 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -1333,6 +1333,9 @@ static void do_test(void)
 		test_nhash("hmac(sha256)", hmac_sha256_tv_template,
 			   HMAC_SHA256_TEST_VECTORS);
 
+		test_hash("xcbc(aes)", aes_xcbc128_tv_template,
+			  XCBC_AES_TEST_VECTORS);
+
 		test_hash("michael_mic", michael_mic_tv_template, MICHAEL_MIC_TEST_VECTORS);
 		break;
 
@@ -1548,6 +1551,11 @@ static void do_test(void)
 			   HMAC_SHA256_TEST_VECTORS);
 		break;
 
+	case 106:
+		test_hash("xcbc(aes)", aes_xcbc128_tv_template,
+			  XCBC_AES_TEST_VECTORS);
+		break;
+
 	case 200:
 		test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
 				  aes_speed_template);
diff --git a/crypto/tcrypt.h b/crypto/tcrypt.h
index bba71f0..ce2210a 100644
--- a/crypto/tcrypt.h
+++ b/crypto/tcrypt.h
@@ -217,6 +217,74 @@ static struct hash_testvec sha256_tv_template[] = {
 	},
 };
 
+#define XCBC_AES_TEST_VECTORS 6
+
+static struct hash_testvec aes_xcbc128_tv_template[] = {
+	{
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.plaintext = { [0 ... 15] = 0 },
+		.digest = { 0x75, 0xf0, 0x25, 0x1d, 0x52, 0x8a, 0xc0, 0x1c,
+			    0x45, 0x73, 0xdf, 0xd5, 0x84, 0xd7, 0x9f, 0x29 },
+		.psize	= 0,
+		.ksize	= 16,
+	}, {
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.plaintext = { 0x00, 0x01, 0x02 },
+		.digest	= { 0x5b, 0x37, 0x65, 0x80, 0xae, 0x2f, 0x19, 0xaf,
+			    0xe7, 0x21, 0x9c, 0xee, 0xf1, 0x72, 0x75, 0x6f },
+		.psize	= 3,
+		.ksize	= 16,
+	} , {
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.digest = { 0xd2, 0xa2, 0x46, 0xfa, 0x34, 0x9b, 0x68, 0xa7,
+			    0x99, 0x98, 0xa4, 0x39, 0x4f, 0xf7, 0xa2, 0x63 },
+		.psize	= 16,
+		.ksize	= 16,
+	}, {
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.plaintext = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+			       0x10, 0x11, 0x12, 0x13 },
+		.digest = { 0x47, 0xf5, 0x1b, 0x45, 0x64, 0x96, 0x62, 0x15,
+			    0xb8, 0x98, 0x5c, 0x63, 0x05, 0x5e, 0xd3, 0x08 },
+		.tap	= { 10, 10 },
+		.psize	= 20,
+		.np	= 2,
+		.ksize	= 16,
+	}, {
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.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 },
+		.digest = { 0xf5, 0x4f, 0x0e, 0xc8, 0xd2, 0xb9, 0xf3, 0xd3,
+			    0x68, 0x07, 0x73, 0x4b, 0xd5, 0x28, 0x3f, 0xd4 },
+		.psize	= 32,
+		.ksize	= 16,
+	}, {
+		.key	= { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+			    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
+		.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 },
+		.digest = { 0xbe, 0xcb, 0xb3, 0xbc, 0xcd, 0xb5, 0x18, 0xa3,
+			    0x06, 0x77, 0xd5, 0x48, 0x1f, 0xb6, 0xb4, 0xd8 },
+		.tap	= { 17, 17 },
+		.psize	= 34,
+		.np	= 2,
+		.ksize	= 16,
+	}
+};
+
 /*
  * SHA384 test vectors from from NIST and kerneli
  */
diff --git a/crypto/xcbc.c b/crypto/xcbc.c
new file mode 100644
index 0000000..b730c6f
--- /dev/null
+++ b/crypto/xcbc.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C)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 <miyazawa@linux-ipv6.org>
+ */
+
+#include <crypto/nscatterwalk.h>
+#include <linux/ncrypto.h>
+#include <linux/err.h>
+#include <linux/hardirq.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/rtnetlink.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+
+static u_int32_t ks[12] = {0x01010101, 0x01010101, 0x01010101, 0x01010101,
+			   0x02020202, 0x02020202, 0x02020202, 0x02020202,
+			   0x03030303, 0x03030303, 0x03030303, 0x03030303};
+/*
+ * +------------------------
+ * | <parent tfm>
+ * +------------------------
+ * | crypto_xcbc_ctx
+ * +------------------------
+ * | odds (block size)
+ * +------------------------
+ * | prev (block size)
+ * +------------------------
+ * | key (block size)
+ * +------------------------
+ * | consts (block size * 3)
+ * +------------------------
+ */
+struct crypto_xcbc_ctx {
+	struct crypto_cipher *child;
+	u8 *odds;
+	u8 *prev;
+	u8 *key;
+	u8 *consts;
+	void (*xor)(u8 *a, const u8 *b, unsigned int bs);
+	unsigned int keylen;
+	unsigned int len;
+};
+
+static void xor_128(u8 *a, const u8 *b, unsigned int bs)
+{
+	((u32 *)a)[0] ^= ((u32 *)b)[0];
+	((u32 *)a)[1] ^= ((u32 *)b)[1];
+	((u32 *)a)[2] ^= ((u32 *)b)[2];
+	((u32 *)a)[3] ^= ((u32 *)b)[3];
+}
+
+static int _crypto_xcbc_digest_setkey(struct crypto_hash *parent,
+				      struct crypto_xcbc_ctx *ctx)
+{
+	int bs = crypto_hash_blocksize(parent);
+	int err = 0;
+	u8 key1[bs];
+
+	if ((err = crypto_cipher_setkey(ctx->child, ctx->key, ctx->keylen)))
+	    return err;
+
+	crypto_cipher_encrypt_one(ctx->child, key1, ctx->consts);
+
+	return crypto_cipher_setkey(ctx->child, key1, bs);
+}
+
+static int crypto_xcbc_digest_setkey(struct crypto_hash *parent,
+				     const u8 *inkey, unsigned int keylen)
+{
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
+
+	if (keylen != crypto_cipher_blocksize(ctx->child))
+		return -EINVAL;
+
+	ctx->keylen = keylen;
+	memcpy(ctx->key, inkey, keylen);
+	ctx->consts = (u8*)ks;
+
+	return _crypto_xcbc_digest_setkey(parent, ctx);
+}
+
+static int crypto_xcbc_digest_init(struct hash_desc *pdesc)
+{
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(pdesc->tfm);
+	int bs = crypto_hash_blocksize(pdesc->tfm);
+
+	ctx->len = 0;
+	memset(ctx->odds, 0, bs);
+	memset(ctx->prev, 0, bs);
+
+	return 0;
+}
+
+static int crypto_xcbc_digest_update2(struct hash_desc *pdesc,
+				      struct scatterlist *sg,
+				      unsigned int nbytes)
+{
+	struct crypto_hash *parent = pdesc->tfm;
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
+	struct crypto_cipher *tfm = ctx->child;
+	int bs = crypto_hash_blocksize(parent);
+	unsigned int i = 0;
+
+	do {
+
+		struct page *pg = sg_page(&sg[i]);
+		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 = ncrypto_kmap(pg, 0) + offset;
+
+			/* checking the data can fill the block */
+			if ((ctx->len + len) <= bs) {
+				memcpy(ctx->odds + ctx->len, p, len);
+				ctx->len += len;
+				slen -= len;
+
+				/* checking the rest of the page */
+				if (len + offset >= PAGE_SIZE) {
+					offset = 0;
+					pg++;
+				} else
+					offset += len;
+
+				ncrypto_kunmap(p, 0);
+				ncrypto_yield(pdesc->flags);
+				continue;
+			}
+
+			/* filling odds with new data and encrypting it */
+			memcpy(ctx->odds + ctx->len, p, bs - ctx->len);
+			len -= bs - ctx->len;
+			p += bs - ctx->len;
+
+			ctx->xor(ctx->prev, ctx->odds, bs);
+			crypto_cipher_encrypt_one(tfm, ctx->prev, ctx->prev);
+
+			/* clearing the length */
+			ctx->len = 0;
+
+			/* encrypting the rest of data */
+			while (len > bs) {
+				ctx->xor(ctx->prev, p, bs);
+				crypto_cipher_encrypt_one(tfm, ctx->prev,
+							  ctx->prev);
+				p += bs;
+				len -= bs;
+			}
+
+			/* keeping the surplus of blocksize */
+			if (len) {
+				memcpy(ctx->odds, p, len);
+				ctx->len = len;
+			}
+			ncrypto_kunmap(p, 0);
+			ncrypto_yield(pdesc->flags);
+			slen -= min(slen, ((unsigned int)(PAGE_SIZE)) - offset);
+			offset = 0;
+			pg++;
+		}
+		nbytes-=sg[i].length;
+		i++;
+	} while (nbytes>0);
+
+	return 0;
+}
+
+static int crypto_xcbc_digest_update(struct hash_desc *pdesc,
+				     struct scatterlist *sg,
+				     unsigned int nbytes)
+{
+	if (WARN_ON_ONCE(in_irq()))
+		return -EDEADLK;
+	return crypto_xcbc_digest_update2(pdesc, sg, nbytes);
+}
+
+static int crypto_xcbc_digest_final(struct hash_desc *pdesc, u8 *out)
+{
+	struct crypto_hash *parent = pdesc->tfm;
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(parent);
+	struct crypto_cipher *tfm = ctx->child;
+	int bs = crypto_hash_blocksize(parent);
+	int err = 0;
+
+	if (ctx->len == bs) {
+		u8 key2[bs];
+
+		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
+			return err;
+
+		crypto_cipher_encrypt_one(tfm, key2,
+					  (u8 *)(ctx->consts + bs));
+
+		ctx->xor(ctx->prev, ctx->odds, bs);
+		ctx->xor(ctx->prev, key2, bs);
+		_crypto_xcbc_digest_setkey(parent, ctx);
+
+		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
+	} else {
+		u8 key3[bs];
+		unsigned int rlen;
+		u8 *p = ctx->odds + ctx->len;
+		*p = 0x80;
+		p++;
+
+		rlen = bs - ctx->len -1;
+		if (rlen)
+			memset(p, 0, rlen);
+
+		if ((err = crypto_cipher_setkey(tfm, ctx->key, ctx->keylen)) != 0)
+			return err;
+
+		crypto_cipher_encrypt_one(tfm, key3,
+					  (u8 *)(ctx->consts + bs * 2));
+
+		ctx->xor(ctx->prev, ctx->odds, bs);
+		ctx->xor(ctx->prev, key3, bs);
+
+		_crypto_xcbc_digest_setkey(parent, ctx);
+
+		crypto_cipher_encrypt_one(tfm, out, ctx->prev);
+	}
+
+	return 0;
+}
+
+static int crypto_xcbc_digest(struct hash_desc *pdesc,
+		  struct scatterlist *sg, unsigned int nbytes, u8 *out)
+{
+	if (WARN_ON_ONCE(in_irq()))
+		return -EDEADLK;
+
+	crypto_xcbc_digest_init(pdesc);
+	crypto_xcbc_digest_update2(pdesc, sg, nbytes);
+	return crypto_xcbc_digest_final(pdesc, out);
+}
+
+static int xcbc_init_tfm(struct ncrypto_tfm *tfm)
+{
+	struct crypto_cipher *cipher;
+	struct crypto_instance *inst = (void *)tfm->__crt_alg;
+	const char *spawn = crypto_instance_ctx(inst);
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
+	int bs = crypto_hash_blocksize(__crypto_hash_cast(tfm));
+
+	cipher = crypto_spawn_cipher(spawn);
+	if (IS_ERR(cipher))
+		return PTR_ERR(cipher);
+
+	switch(bs) {
+	case 16:
+		ctx->xor = xor_128;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ctx->child = cipher;
+	ctx->odds = (u8*)(ctx+1);
+	ctx->prev = ctx->odds + bs;
+	ctx->key = ctx->prev + bs;
+
+	return 0;
+};
+
+static void xcbc_exit_tfm(struct ncrypto_tfm *tfm)
+{
+	struct crypto_xcbc_ctx *ctx = crypto_hash_ctx_aligned(__crypto_hash_cast(tfm));
+	crypto_free_cipher(ctx->child);
+}
+
+static struct crypto_instance *xcbc_alloc(struct rtattr **tb)
+{
+	struct crypto_instance *inst;
+	const char *name;
+	struct crypto_cipher *cipher;
+	struct crypto_alg *alg;
+	int err;
+
+	err = crypto_check_attr_type(tb, NCRYPTO_ALG_TYPE_HASH);
+	if (err)
+		return ERR_PTR(err);
+
+	name = crypto_attr_alg_name(tb[1]);
+	err = PTR_ERR(name);
+	if (IS_ERR(name))
+		return ERR_PTR(err);
+
+	cipher = crypto_alloc_cipher(name, 0, 0);
+	err = PTR_ERR(cipher);
+	if (IS_ERR(cipher))
+		return ERR_PTR(err);
+
+	alg = crypto_cipher_tfm(cipher)->__crt_alg;
+
+	switch(alg->cra_blocksize) {
+	case 16:
+		break;
+	default:
+		inst = ERR_PTR(-EINVAL);
+		goto out_put_alg;
+	}
+
+	inst = ocrypto_alloc_instance("xcbc", alg);
+	if (IS_ERR(inst))
+		goto out_put_alg;
+
+	inst->alg.cra_flags = NCRYPTO_ALG_TYPE_HASH;
+	inst->alg.cra_priority = alg->cra_priority;
+	inst->alg.cra_blocksize = alg->cra_blocksize;
+	inst->alg.cra_alignmask = alg->cra_alignmask;
+	inst->alg.cra_type = &crypto_hash_type;
+
+	inst->alg.cra_hash.digestsize = alg->cra_blocksize;
+	inst->alg.cra_ctxsize = sizeof(struct crypto_xcbc_ctx) +
+				ALIGN(inst->alg.cra_blocksize * 3, sizeof(void *));
+	inst->alg.cra_init = xcbc_init_tfm;
+	inst->alg.cra_exit = xcbc_exit_tfm;
+
+	inst->alg.cra_hash.init = crypto_xcbc_digest_init;
+	inst->alg.cra_hash.update = crypto_xcbc_digest_update;
+	inst->alg.cra_hash.final = crypto_xcbc_digest_final;
+	inst->alg.cra_hash.digest = crypto_xcbc_digest;
+	inst->alg.cra_hash.setkey = crypto_xcbc_digest_setkey;
+
+out_put_alg:
+	crypto_free_cipher(cipher);
+	return inst;
+}
+
+static void xcbc_free(struct crypto_instance *inst)
+{
+	kfree(inst);
+}
+
+static struct crypto_template crypto_xcbc_tmpl = {
+	.name = "xcbc",
+	.alloc = xcbc_alloc,
+	.free = xcbc_free,
+	.module = THIS_MODULE,
+};
+
+static int __init crypto_xcbc_module_init(void)
+{
+	return crypto_register_template(&crypto_xcbc_tmpl);
+}
+
+static void __exit crypto_xcbc_module_exit(void)
+{
+	crypto_unregister_template(&crypto_xcbc_tmpl);
+}
+
+module_init(crypto_xcbc_module_init);
+module_exit(crypto_xcbc_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("XCBC keyed hash algorithm");