Sophie

Sophie

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

kernel-2.6.18-238.el5.src.rpm

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Sun, 6 Jan 2008 16:09:27 +1100
Subject: [crypto] hmac: add crypto template implementation
Message-id: E1JBNlH-0001BJ-00@gondolin.me.apana.org.au
O-Subject: [PATCH 13/32] [CRYPTO] hmac: Add crypto template implementation
Bugzilla: 253051

[CRYPTO] hmac: Add crypto template implementation

This patch rewrites HMAC as a crypto template.  This means that HMAC is no
longer a hard-coded part of the API.  It's now a template that generates
standard digest algorithms like any other.

The old HMAC is preserved until all current users are converted.

The same structure can be used by other MACs such as AES-XCBC-MAC.

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 638bc17..0ed73b3 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -62,6 +62,15 @@ config CRYPTO_HMAC
 	  HMAC: Keyed-Hashing for Message Authentication (RFC2104).
 	  This is required for IPSec.
 
+config CRYPTO_NHMAC
+	tristate "HMAC support (new)"
+	select CRYPTO_HASH
+	select CRYPTO_MANAGER
+	depends on CRYPTO
+	help
+	  HMAC: Keyed-Hashing for Message Authentication (RFC2104).
+	  This is required for IPSec.
+
 config CRYPTO_NULL
 	tristate "Null algorithms"
 	depends on CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
index 552ee39..cc33257 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -24,7 +24,8 @@ obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
 
 obj-$(CONFIG_CRYPTO_API) += crypto_api.o
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
-obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
+obj-$(CONFIG_CRYPTO_HMAC) += hmac_old.o
+obj-$(CONFIG_CRYPTO_NHMAC) += hmac.o
 obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o
 obj-$(CONFIG_CRYPTO_MD4) += md4.o
 obj-$(CONFIG_CRYPTO_MD5) += md5.o
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 46120de..28a713b 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -4,121 +4,285 @@
  * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
  *
  * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
  *
  * The HMAC implementation is derived from USAGI.
  * Copyright (c) 2002 Kazunori Miyazawa <miyazawa@linux-ipv6.org> / USAGI
  *
  * 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) 
+ * Software Foundation; either version 2 of the License, or (at your option)
  * any later version.
  *
  */
-#include <linux/crypto.h>
-#include <linux/mm.h>
-#include <linux/highmem.h>
-#include <linux/slab.h>
+
+#include <crypto/algapi.h>
+#include <crypto/nscatterwalk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/scatterlist.h>
-#include "internal.h"
+#include <linux/slab.h>
+#include <linux/string.h>
 
-static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen)
-{
-	struct scatterlist tmp;
-	
-	sg_set_buf(&tmp, key, keylen);
-	crypto_digest_digest(tfm, &tmp, 1, key);
-}
+struct hmac_ctx {
+	struct crypto_tfm *child;
+};
 
-int crypto_alloc_hmac_block(struct crypto_tfm *tfm)
+static inline void *align_ptr(void *p, unsigned int align)
 {
-	int ret = 0;
-
-	BUG_ON(!crypto_tfm_alg_blocksize(tfm));
-	
-	tfm->crt_digest.dit_hmac_block = kmalloc(crypto_tfm_alg_blocksize(tfm),
-	                                         GFP_KERNEL);
-	if (tfm->crt_digest.dit_hmac_block == NULL)
-		ret = -ENOMEM;
-
-	return ret;
-		
+	return (void *)ALIGN((unsigned long)p, align);
 }
 
-void crypto_free_hmac_block(struct crypto_tfm *tfm)
+static inline struct hmac_ctx *hmac_ctx(struct crypto_hash *tfm)
 {
-	kfree(tfm->crt_digest.dit_hmac_block);
+	return align_ptr(crypto_hash_ctx_aligned(tfm) +
+			 crypto_hash_blocksize(tfm) * 2 +
+			 crypto_hash_digestsize(tfm), sizeof(void *));
 }
 
-void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen)
+static int hmac_setkey(struct crypto_hash *parent,
+		       const u8 *inkey, unsigned int keylen)
 {
+	int bs = crypto_hash_blocksize(parent);
+	int ds = crypto_hash_digestsize(parent);
+	char *ipad = crypto_hash_ctx_aligned(parent);
+	char *opad = ipad + bs;
+	char *digest = opad + bs;
+	struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
+	struct crypto_tfm *tfm = ctx->child;
 	unsigned int i;
-	struct scatterlist tmp;
-	char *ipad = tfm->crt_digest.dit_hmac_block;
-	
-	if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
-		hash_key(tfm, key, *keylen);
-		*keylen = crypto_tfm_alg_digestsize(tfm);
+
+	if (keylen > bs) {
+		struct scatterlist tmp;
+
+		sg_init_one(&tmp, (u8 *)inkey, keylen);
+		crypto_digest_digest(tfm, &tmp, 1, digest);
+
+		inkey = digest;
+		keylen = ds;
 	}
 
-	memset(ipad, 0, crypto_tfm_alg_blocksize(tfm));
-	memcpy(ipad, key, *keylen);
+	memcpy(ipad, inkey, keylen);
+	memset(ipad + keylen, 0, bs - keylen);
+	memcpy(opad, ipad, bs);
 
-	for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
+	for (i = 0; i < bs; i++) {
 		ipad[i] ^= 0x36;
+		opad[i] ^= 0x5c;
+	}
+
+	return 0;
+}
+
+static int hmac_init(struct hash_desc *pdesc)
+{
+	struct crypto_hash *parent = pdesc->tfm;
+	int bs = crypto_hash_blocksize(parent);
+	int ds = crypto_hash_digestsize(parent);
+	char *ipad = crypto_hash_ctx_aligned(parent);
+	struct hmac_ctx *ctx = align_ptr(ipad + bs * 2 + ds, sizeof(void *));
+	struct crypto_tfm *tfm = ctx->child;
+	struct scatterlist tmp;
 
-	sg_set_buf(&tmp, ipad, crypto_tfm_alg_blocksize(tfm));
-	
+	sg_init_one(&tmp, ipad, bs);
 	crypto_digest_init(tfm);
+
 	crypto_digest_update(tfm, &tmp, 1);
+	return 0;
 }
 
-void crypto_hmac_update(struct crypto_tfm *tfm,
-                        struct scatterlist *sg, unsigned int nsg)
+static int hmac_update(struct hash_desc *desc,
+		       struct scatterlist *sg, unsigned int nbytes)
 {
-	crypto_digest_update(tfm, sg, nsg);
+	struct hmac_ctx *ctx = hmac_ctx(desc->tfm);
+	struct crypto_tfm *tfm = ctx->child;
+	unsigned int alignmask = crypto_tfm_alg_alignmask(tfm);
+
+	if (!nbytes)
+		return 0;
+
+	for (;;) {
+		struct page *pg = sg_page(sg);
+		unsigned int offset = sg->offset;
+		unsigned int l = sg->length;
+
+		if (unlikely(l > nbytes))
+			l = nbytes;
+		nbytes -= l;
+
+		do {
+			unsigned int bytes_from_page = min(l, ((unsigned int)
+							   (PAGE_SIZE)) - 
+							   offset);
+			char *src = ncrypto_kmap(pg, 0);
+			char *p = src + offset;
+
+			if (unlikely(offset & alignmask)) {
+				unsigned int bytes =
+					alignmask + 1 - (offset & alignmask);
+				bytes = min(bytes, bytes_from_page);
+				tfm->__crt_alg->cra_digest.dia_update(tfm, p,
+								      bytes);
+				p += bytes;
+				bytes_from_page -= bytes;
+				l -= bytes;
+			}
+			tfm->__crt_alg->cra_digest.dia_update(tfm, p,
+							      bytes_from_page);
+			ncrypto_kunmap(src, 0);
+			ncrypto_yield(desc->flags);
+			offset = 0;
+			pg++;
+			l -= bytes_from_page;
+		} while (l > 0);
+
+		if (!nbytes)
+			break;
+		sg = scatterwalk_sg_next(sg);
+	}
+
+	return 0;
 }
 
-void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key,
-                       unsigned int *keylen, u8 *out)
+static int hmac_final(struct hash_desc *pdesc, u8 *out)
 {
-	unsigned int i;
+	struct crypto_hash *parent = pdesc->tfm;
+	int bs = crypto_hash_blocksize(parent);
+	int ds = crypto_hash_digestsize(parent);
+	char *opad = crypto_hash_ctx_aligned(parent) + bs;
+	char *digest = opad + bs;
+	struct hmac_ctx *ctx = align_ptr(digest + ds, sizeof(void *));
+	struct crypto_tfm *tfm = ctx->child;
 	struct scatterlist tmp;
-	char *opad = tfm->crt_digest.dit_hmac_block;
-	
-	if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
-		hash_key(tfm, key, *keylen);
-		*keylen = crypto_tfm_alg_digestsize(tfm);
-	}
 
-	crypto_digest_final(tfm, out);
+	sg_init_one(&tmp, opad, bs + ds);
+	crypto_digest_final(tfm, digest);
 
-	memset(opad, 0, crypto_tfm_alg_blocksize(tfm));
-	memcpy(opad, key, *keylen);
-		
-	for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
-		opad[i] ^= 0x5c;
+	crypto_digest_digest(tfm, &tmp, 1, out);
+	return 0;
+}
 
-	sg_set_buf(&tmp, opad, crypto_tfm_alg_blocksize(tfm));
+static int hmac_digest(struct hash_desc *pdesc, struct scatterlist *sg,
+		       unsigned int nbytes, u8 *out)
+{
+	int err;
 
-	crypto_digest_init(tfm);
-	crypto_digest_update(tfm, &tmp, 1);
-	
-	sg_set_buf(&tmp, out, crypto_tfm_alg_digestsize(tfm));
-	
-	crypto_digest_update(tfm, &tmp, 1);
-	crypto_digest_final(tfm, out);
+	err = hmac_init(pdesc);
+	if (err)
+		return err;
+
+	err = hmac_update(pdesc, sg, nbytes);
+	if (err)
+		return err;
+
+	return hmac_final(pdesc, out);
+}
+
+static int hmac_init_tfm(struct ncrypto_tfm *tfm)
+{
+	struct crypto_tfm *hash;
+	struct crypto_instance *inst = (void *)tfm->__crt_alg;
+	const char *spawn = crypto_instance_ctx(inst);
+	struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm));
+
+	hash = crypto_spawn_digest(spawn);
+	if (IS_ERR(hash))
+		return PTR_ERR(hash);
+
+	ctx->child = hash;
+	return 0;
+}
+
+static void hmac_exit_tfm(struct ncrypto_tfm *tfm)
+{
+	struct hmac_ctx *ctx = hmac_ctx(__crypto_hash_cast(tfm));
+	crypto_free_tfm(ctx->child);
+}
+
+static void hmac_free(struct crypto_instance *inst)
+{
+	kfree(inst);
+}
+
+static struct crypto_instance *hmac_alloc(struct rtattr **tb)
+{
+	struct crypto_instance *inst;
+	const char *name;
+	struct crypto_tfm *tfm;
+	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);
+
+	tfm = crypto_alloc_tfm(name, 0);
+	if (!tfm)
+		return ERR_PTR(-ENOENT);
+
+	inst = ERR_PTR(-EINVAL);
+	if (crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_DIGEST)
+		goto out_put_alg;
+
+	alg = tfm->__crt_alg;
+
+	inst = ocrypto_alloc_instance("hmac", 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_digest.dia_digestsize;
+
+	inst->alg.cra_ctxsize = sizeof(struct hmac_ctx) +
+				ALIGN(inst->alg.cra_blocksize * 2 +
+				      inst->alg.cra_hash.digestsize,
+				      sizeof(void *));
+
+	inst->alg.cra_init = hmac_init_tfm;
+	inst->alg.cra_exit = hmac_exit_tfm;
+
+	inst->alg.cra_hash.init = hmac_init;
+	inst->alg.cra_hash.update = hmac_update;
+	inst->alg.cra_hash.final = hmac_final;
+	inst->alg.cra_hash.digest = hmac_digest;
+	inst->alg.cra_hash.setkey = hmac_setkey;
+
+out_put_alg:
+	crypto_free_tfm(tfm);
+	return inst;
+}
+
+static struct crypto_template hmac_tmpl = {
+	.name = "hmac",
+	.alloc = hmac_alloc,
+	.free = hmac_free,
+	.module = THIS_MODULE,
+};
+
+static int __init hmac_module_init(void)
+{
+	return crypto_register_template(&hmac_tmpl);
 }
 
-void crypto_hmac(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen,
-                 struct scatterlist *sg, unsigned int nsg, u8 *out)
+static void __exit hmac_module_exit(void)
 {
-	crypto_hmac_init(tfm, key, keylen);
-	crypto_hmac_update(tfm, sg, nsg);
-	crypto_hmac_final(tfm, key, keylen, out);
+	crypto_unregister_template(&hmac_tmpl);
 }
 
-EXPORT_SYMBOL_GPL(crypto_hmac_init);
-EXPORT_SYMBOL_GPL(crypto_hmac_update);
-EXPORT_SYMBOL_GPL(crypto_hmac_final);
-EXPORT_SYMBOL_GPL(crypto_hmac);
+module_init(hmac_module_init);
+module_exit(hmac_module_exit);
 
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HMAC hash algorithm");
diff --git a/crypto/hmac_old.c b/crypto/hmac_old.c
new file mode 100644
index 0000000..46120de
--- /dev/null
+++ b/crypto/hmac_old.c
@@ -0,0 +1,124 @@
+/*
+ * Cryptographic API.
+ *
+ * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
+ *
+ * Copyright (c) 2002 James Morris <jmorris@intercode.com.au>
+ *
+ * The HMAC implementation is derived from USAGI.
+ * Copyright (c) 2002 Kazunori Miyazawa <miyazawa@linux-ipv6.org> / USAGI
+ *
+ * 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.
+ *
+ */
+#include <linux/crypto.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include "internal.h"
+
+static void hash_key(struct crypto_tfm *tfm, u8 *key, unsigned int keylen)
+{
+	struct scatterlist tmp;
+	
+	sg_set_buf(&tmp, key, keylen);
+	crypto_digest_digest(tfm, &tmp, 1, key);
+}
+
+int crypto_alloc_hmac_block(struct crypto_tfm *tfm)
+{
+	int ret = 0;
+
+	BUG_ON(!crypto_tfm_alg_blocksize(tfm));
+	
+	tfm->crt_digest.dit_hmac_block = kmalloc(crypto_tfm_alg_blocksize(tfm),
+	                                         GFP_KERNEL);
+	if (tfm->crt_digest.dit_hmac_block == NULL)
+		ret = -ENOMEM;
+
+	return ret;
+		
+}
+
+void crypto_free_hmac_block(struct crypto_tfm *tfm)
+{
+	kfree(tfm->crt_digest.dit_hmac_block);
+}
+
+void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen)
+{
+	unsigned int i;
+	struct scatterlist tmp;
+	char *ipad = tfm->crt_digest.dit_hmac_block;
+	
+	if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
+		hash_key(tfm, key, *keylen);
+		*keylen = crypto_tfm_alg_digestsize(tfm);
+	}
+
+	memset(ipad, 0, crypto_tfm_alg_blocksize(tfm));
+	memcpy(ipad, key, *keylen);
+
+	for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
+		ipad[i] ^= 0x36;
+
+	sg_set_buf(&tmp, ipad, crypto_tfm_alg_blocksize(tfm));
+	
+	crypto_digest_init(tfm);
+	crypto_digest_update(tfm, &tmp, 1);
+}
+
+void crypto_hmac_update(struct crypto_tfm *tfm,
+                        struct scatterlist *sg, unsigned int nsg)
+{
+	crypto_digest_update(tfm, sg, nsg);
+}
+
+void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key,
+                       unsigned int *keylen, u8 *out)
+{
+	unsigned int i;
+	struct scatterlist tmp;
+	char *opad = tfm->crt_digest.dit_hmac_block;
+	
+	if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
+		hash_key(tfm, key, *keylen);
+		*keylen = crypto_tfm_alg_digestsize(tfm);
+	}
+
+	crypto_digest_final(tfm, out);
+
+	memset(opad, 0, crypto_tfm_alg_blocksize(tfm));
+	memcpy(opad, key, *keylen);
+		
+	for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
+		opad[i] ^= 0x5c;
+
+	sg_set_buf(&tmp, opad, crypto_tfm_alg_blocksize(tfm));
+
+	crypto_digest_init(tfm);
+	crypto_digest_update(tfm, &tmp, 1);
+	
+	sg_set_buf(&tmp, out, crypto_tfm_alg_digestsize(tfm));
+	
+	crypto_digest_update(tfm, &tmp, 1);
+	crypto_digest_final(tfm, out);
+}
+
+void crypto_hmac(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen,
+                 struct scatterlist *sg, unsigned int nsg, u8 *out)
+{
+	crypto_hmac_init(tfm, key, keylen);
+	crypto_hmac_update(tfm, sg, nsg);
+	crypto_hmac_final(tfm, key, keylen, out);
+}
+
+EXPORT_SYMBOL_GPL(crypto_hmac_init);
+EXPORT_SYMBOL_GPL(crypto_hmac_update);
+EXPORT_SYMBOL_GPL(crypto_hmac_final);
+EXPORT_SYMBOL_GPL(crypto_hmac);
+