Hi:

[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 <[EMAIL PROTECTED]>

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <[EMAIL PROTECTED]>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
diff --git a/crypto/Kconfig b/crypto/Kconfig
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -18,12 +18,16 @@ config CRYPTO_MANAGER
          cbc(aes).
 
 config CRYPTO_HMAC
-       bool "HMAC support"
+       tristate "HMAC support"
        depends on CRYPTO
+       select CRYPTO_HMAC_OLD
        help
          HMAC: Keyed-Hashing for Message Authentication (RFC2104).
          This is required for IPSec.
 
+config CRYPTO_HMAC_OLD
+       bool
+
 config CRYPTO_NULL
        tristate "Null algorithms"
        depends on CRYPTO
diff --git a/crypto/Makefile b/crypto/Makefile
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_CRYPTO) += api.o scatterwal
 
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
 obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
+obj-$(CONFIG_CRYPTO_HMAC_OLD) += hmac-old.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-old.c b/crypto/hmac-old.c
new file mode 100644
--- /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 <[EMAIL PROTECTED]>
+ *
+ * The HMAC implementation is derived from USAGI.
+ * Copyright (c) 2002 Kazunori Miyazawa <[EMAIL PROTECTED]> / 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);
+
diff --git a/crypto/hmac.c b/crypto/hmac.c
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -4,121 +4,227 @@
  * HMAC: Keyed-Hashing for Message Authentication (RFC2104).
  *
  * Copyright (c) 2002 James Morris <[EMAIL PROTECTED]>
+ * Copyright (c) 2006 Herbert Xu <[EMAIL PROTECTED]>
  *
  * The HMAC implementation is derived from USAGI.
  * Copyright (c) 2002 Kazunori Miyazawa <[EMAIL PROTECTED]> / 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/err.h>
+#include <linux/kernel.h>
 #include <linux/mm.h>
-#include <linux/highmem.h>
+#include <linux/rtnetlink.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 crypto_hmac_ctx {
+       struct crypto_tfm *child;
+};
+
+static inline struct crypto_hmac_ctx *crypto_hmac_ctx(struct crypto_tfm *tfm)
 {
-       struct scatterlist tmp;
-       
-       sg_set_buf(&tmp, key, keylen);
-       crypto_digest_digest(tfm, &tmp, 1, key);
+       return (void *)((char *)crypto_tfm_ctx_aligned(tfm) +
+                       crypto_tfm_alg_blocksize(tfm) * 2);
 }
 
-int crypto_alloc_hmac_block(struct crypto_tfm *tfm)
+static int crypto_hmac_digest_setkey(struct crypto_tfm *parent,
+                                    const u8 *inkey, unsigned int keylen,
+                                    u32 *flags)
 {
-       int ret = 0;
+       int bs = crypto_tfm_alg_blocksize(parent);
+       char *key = crypto_tfm_ctx_aligned(parent);
+       char *pad = key + bs;
+       struct crypto_hmac_ctx *ctx = (void *)(pad + bs);
+       struct crypto_tfm *child = ctx->child;
 
-       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;
+       if (keylen > bs) {
+               struct scatterlist tmp;
 
-       return ret;
-               
-}
+               sg_set_buf(&tmp, (void *)inkey, keylen);
+               crypto_digest_digest(child, &tmp, 1, pad);
+               inkey = pad;
+               keylen = crypto_tfm_alg_digestsize(parent);
+       }
 
-void crypto_free_hmac_block(struct crypto_tfm *tfm)
-{
-       kfree(tfm->crt_digest.dit_hmac_block);
+       memcpy(key, inkey, keylen);
+       memset(key + keylen, 0, bs - keylen);
+       return 0;
 }
 
-void crypto_hmac_init(struct crypto_tfm *tfm, u8 *key, unsigned int *keylen)
-{
+static void crypto_hmac_digest_init(struct crypto_tfm *parent)
+{
+       int bs = crypto_tfm_alg_blocksize(parent);
+       char *key = crypto_tfm_ctx_aligned(parent);
+       char *ipad = key + bs;
+       struct crypto_hmac_ctx *ctx = (void *)(ipad + bs);
+       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);
-       }
 
-       memset(ipad, 0, crypto_tfm_alg_blocksize(tfm));
-       memcpy(ipad, key, *keylen);
+       memcpy(ipad, key, bs);
 
-       for (i = 0; i < crypto_tfm_alg_blocksize(tfm); i++)
+       for (i = 0; i < bs; i++)
                ipad[i] ^= 0x36;
 
-       sg_set_buf(&tmp, ipad, crypto_tfm_alg_blocksize(tfm));
-       
+       sg_set_buf(&tmp, ipad, bs);
+
        crypto_digest_init(tfm);
        crypto_digest_update(tfm, &tmp, 1);
 }
 
-void crypto_hmac_update(struct crypto_tfm *tfm,
-                        struct scatterlist *sg, unsigned int nsg)
+static void crypto_hmac_digest_update(struct crypto_tfm *parent,
+                                     struct scatterlist *sg, unsigned int nsg)
 {
-       crypto_digest_update(tfm, sg, nsg);
+       struct crypto_hmac_ctx *ctx = crypto_hmac_ctx(parent);
+       crypto_digest_update(ctx->child, sg, nsg);
 }
 
-void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key,
-                       unsigned int *keylen, u8 *out)
+static void crypto_hmac_digest_final(struct crypto_tfm *parent, u8 *out)
 {
+       int bs = crypto_tfm_alg_blocksize(parent);
+       char *key = crypto_tfm_ctx(parent);
+       char *opad = key + bs;
+       struct crypto_hmac_ctx *ctx = (void *)(opad + bs);
+       struct crypto_tfm *tfm = ctx->child;
        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++)
+       memcpy(opad, key, bs);
+
+       for (i = 0; i < bs; i++)
                opad[i] ^= 0x5c;
 
-       sg_set_buf(&tmp, opad, crypto_tfm_alg_blocksize(tfm));
+       sg_set_buf(&tmp, opad, bs);
 
        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)
+static int crypto_hmac_init_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_instance *inst = (void *)tfm->__crt_alg;
+       struct crypto_spawn *spawn = crypto_instance_ctx(inst);
+       struct crypto_hmac_ctx *ctx = crypto_hmac_ctx(tfm);
+
+       ctx->child = crypto_spawn_tfm(spawn);
+       if (IS_ERR(ctx->child))
+               return PTR_ERR(ctx->child);
+
+       return 0;
+}
+
+static void crypto_hmac_exit_tfm(struct crypto_tfm *tfm)
+{
+       crypto_free_tfm(crypto_hmac_ctx(tfm)->child);
+}
+
+static struct crypto_instance *crypto_hmac_alloc(void *param, unsigned int len)
+{
+       struct rtattr *rta = param;
+       struct crypto_instance *inst;
+       struct crypto_attr_alg *alga;
+       struct crypto_alg *alg;
+       struct crypto_spawn *spawn;
+       int err;
+
+       if (!RTA_OK(rta, len))
+               return ERR_PTR(-EBADR);
+       if (rta->rta_type != CRYPTOA_ALG || RTA_PAYLOAD(rta) < sizeof(*alga))
+               return ERR_PTR(-EINVAL);
+
+       alga = RTA_DATA(rta);
+
+       inst = kzalloc(sizeof(*inst) + sizeof(*spawn), GFP_KERNEL);
+       if (!inst)
+               return ERR_PTR(-ENOMEM);
+
+       inst->alg.cra_flags = CRYPTO_ALG_TYPE_DIGEST;
+
+       err = -ENOENT;
+       alg = crypto_alg_mod_lookup(alga->name, CRYPTO_ALG_TYPE_DIGEST,
+                                   CRYPTO_ALG_TYPE_MASK);
+       if (!alg)
+               goto err_free_inst;
+
+       err = -ENAMETOOLONG;
+       if (snprintf(inst->alg.cra_name, CRYPTO_MAX_ALG_NAME,
+                    "hmac(%s)", alg->cra_name) >= CRYPTO_MAX_ALG_NAME)
+               goto put_alg;
+
+       if (snprintf(inst->alg.cra_driver_name, CRYPTO_MAX_ALG_NAME,
+                    "hmac(%s)", alg->cra_driver_name) >= CRYPTO_MAX_ALG_NAME)
+               goto put_alg;
+
+       spawn = crypto_instance_ctx(inst);
+       err = crypto_init_spawn(spawn, alg, inst);
+
+put_alg:
+       crypto_mod_put(alg);
+       if (err)
+               goto err_free_inst;
+
+       inst->alg.cra_priority = alg->cra_priority;
+       inst->alg.cra_blocksize = alg->cra_blocksize;
+       inst->alg.cra_alignmask = alg->cra_alignmask;
+       inst->alg.cra_digest.dia_digestsize = alg->cra_digest.dia_digestsize;
+
+       inst->alg.cra_ctxsize = sizeof(struct crypto_hmac_ctx) +
+                               inst->alg.cra_blocksize * 2;
+
+       inst->alg.cra_init = crypto_hmac_init_tfm;
+       inst->alg.cra_exit = crypto_hmac_exit_tfm;
+
+       inst->alg.cra_digest.dia_init = crypto_hmac_digest_init;
+       inst->alg.cra_digest.dia_update_sg = crypto_hmac_digest_update;
+       inst->alg.cra_digest.dia_final = crypto_hmac_digest_final;
+       inst->alg.cra_digest.dia_setkey = crypto_hmac_digest_setkey;
+
+       return inst;
+
+err_free_inst:
+       kfree(inst);
+       return ERR_PTR(err);
+}
+
+static void crypto_hmac_free(struct crypto_instance *inst)
+{
+       crypto_drop_spawn(crypto_instance_ctx(inst));
+       kfree(inst);
+}
+
+static struct crypto_template crypto_hmac_tmpl = {
+       .name = "hmac",
+       .alloc = crypto_hmac_alloc,
+       .free = crypto_hmac_free,
+       .module = THIS_MODULE,
+};
+
+static int __init crypto_hmac_module_init(void)
+{
+       return crypto_register_template(&crypto_hmac_tmpl);
+}
+
+static void __exit crypto_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(&crypto_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(crypto_hmac_module_init);
+module_exit(crypto_hmac_module_exit);
 
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("HMAC digest algorithm");
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -334,6 +334,16 @@ static inline unsigned int crypto_tfm_ct
        return __alignof__(tfm->__crt_ctx);
 }
 
+static inline void *crypto_tfm_ctx_aligned(struct crypto_tfm *tfm)
+{
+       unsigned long addr = (unsigned long)crypto_tfm_ctx(tfm);
+       unsigned long align = crypto_tfm_alg_alignmask(tfm);
+
+       if (align <= crypto_tfm_ctx_alignment())
+               align = 1;
+       return (void *)ALIGN(addr, align);
+}
+
 /*
  * API wrappers.
  */
-
To unsubscribe from this list: send the line "unsubscribe linux-crypto" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to