>From e5d67c3670f1ec15339a92cc291027c0a059aaed Mon Sep 17 00:00:00 2001
From: Loc Ho <[EMAIL PROTECTED]>
Date: Thu, 24 Jan 2008 18:13:28 -0800
Subject: [PATCH] Add Async Hash Support

---
 crypto/Makefile               |    1 +
 crypto/ahash.c                |  151 +++++++++++++++++
 crypto/algapi.c               |    2 +-
 crypto/api.c                  |    2 +-
 crypto/cryptd.c               |  220 +++++++++++++++++++++++++
 crypto/digest.c               |    4 +-
 crypto/hash.c                 |  102 +++++++++++-
 crypto/tcrypt.c               |  142 ++++++++++++++++-
 drivers/crypto/Kconfig        |    8 +-
 drivers/crypto/Makefile       |    1 +
 drivers/crypto/ahash_sample.c |  354
+++++++++++++++++++++++++++++++++++++++++
 include/crypto/algapi.h       |   36 ++++
 include/linux/crypto.h        |  183 ++++++++++++++++++++-
 13 files changed, 1183 insertions(+), 23 deletions(-)
 create mode 100644 crypto/ahash.c
 create mode 100644 drivers/crypto/ahash_sample.c

diff --git a/crypto/Makefile b/crypto/Makefile
index 48c7583..a9c3d09 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_CRYPTO_BLKCIPHER) += eseqiv.o
 obj-$(CONFIG_CRYPTO_SEQIV) += seqiv.o
 
 crypto_hash-objs := hash.o
+crypto_hash-objs += ahash.o
 obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
 
 obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
diff --git a/crypto/ahash.c b/crypto/ahash.c
new file mode 100644
index 0000000..e9bf72f
--- /dev/null
+++ b/crypto/ahash.c
@@ -0,0 +1,151 @@
+/*
+ * Asynchronous Cryptographic Hash operations.
+ *
+ * This is the asynchronous version of hash.c with notification of
+ * completion via a callback.
+ *
+ * Copyright (c) 2008 Loc Ho <[EMAIL PROTECTED]>
+ *
+ * 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 <crypto/algapi.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+
+#include "internal.h"
+
+static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key,
+                               unsigned int keylen)
+{
+       struct ahash_alg *ahash = crypto_ahash_alg(tfm);
+       unsigned long alignmask = crypto_ahash_alignmask(tfm);
+       int ret;
+       u8 *buffer, *alignbuffer;
+       unsigned long absize;
+
+       absize = keylen + alignmask;
+       buffer = kmalloc(absize, GFP_ATOMIC);
+       if (!buffer)
+               return -ENOMEM;
+
+       alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
+       memcpy(alignbuffer, key, keylen);
+       ret = ahash->setkey(tfm, alignbuffer, keylen);
+       memset(alignbuffer, 0, keylen);
+       kfree(buffer);
+       return ret;
+}
+
+static int ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
+                       unsigned int keylen)
+{
+       struct ahash_alg *ahash = crypto_ahash_alg(tfm);
+       unsigned long alignmask = crypto_ahash_alignmask(tfm);
+
+       if ((unsigned long)key & alignmask)
+               return ahash_setkey_unaligned(tfm, key, keylen);
+
+       return ahash->setkey(tfm, key, keylen);
+}
+
+static unsigned int crypto_ahash_ctxsize(struct crypto_alg *alg, u32 type,
+                                       u32 mask)
+{
+       return alg->cra_ctxsize;
+}
+
+static int crypto_init_ahash_ops(struct crypto_tfm *tfm, u32 type, u32
mask)
+{
+       struct ahash_alg *alg = &tfm->__crt_alg->cra_ahash;
+       struct ahash_tfm *crt   = &tfm->crt_ahash;
+
+       if (alg->digestsize > crypto_tfm_alg_blocksize(tfm))
+               return -EINVAL;
+
+       crt->init = alg->init;
+       crt->update = alg->update;
+       crt->final  = alg->final;
+       crt->digest = alg->digest;
+       crt->setkey = ahash_setkey;
+       crt->base   = __crypto_ahash_cast(tfm);
+       crt->digestsize = alg->digestsize;
+
+       return 0;
+}
+
+static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
+       __attribute__ ((unused));
+static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg)
+{
+       seq_printf(m, "type         : hash\n");
+       seq_printf(m, "async        : %s\n", alg->cra_flags &
CRYPTO_ALG_ASYNC ?
+                                            "yes" : "no");
+       seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);
+       seq_printf(m, "digestsize   : %u\n", alg->cra_hash.digestsize);
+}
+
+const struct crypto_type crypto_ahash_type = {
+       .ctxsize = crypto_ahash_ctxsize,
+       .init = crypto_init_ahash_ops,
+#ifdef CONFIG_PROC_FS
+       .show = crypto_ahash_show,
+#endif
+};
+EXPORT_SYMBOL_GPL(crypto_ahash_type);
+
+struct crypto_ahash *crypto_alloc_ahash(const char *alg_name,
+                                       u32 type, u32 mask)
+{
+       struct crypto_tfm *tfm;
+       int err;
+
+       mask &= ~CRYPTO_ALG_TYPE_MASK;
+       mask |= CRYPTO_ALG_TYPE_HASH_MASK;
+
+       for (;;) {
+               struct crypto_alg *alg;
+
+               type &= ~CRYPTO_ALG_TYPE_MASK;
+               type |= CRYPTO_ALG_TYPE_AHASH;
+               alg = crypto_alg_mod_lookup(alg_name, type, mask);
+               if (IS_ERR(alg)) {
+                       type &= ~CRYPTO_ALG_TYPE_MASK;
+                       type |= CRYPTO_ALG_TYPE_HASH;
+                       alg = crypto_alg_mod_lookup(alg_name, type, mask);
+                       if (IS_ERR(alg)) {
+                               err = PTR_ERR(alg);
+                               goto err;
+                       }
+               }
+
+               tfm = __crypto_alloc_tfm(alg, type, mask |
CRYPTO_ALG_ASYNC);
+               if (!IS_ERR(tfm))
+                       return __crypto_ahash_cast(tfm);
+
+               crypto_mod_put(alg);
+               err = PTR_ERR(tfm);
+
+err:
+               if (err != -EAGAIN)
+                       break;
+               if (signal_pending(current)) {
+                       err = -EINTR;
+                       break;
+               }
+       }
+
+       return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(crypto_alloc_ahash);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Asynchronous cryptographic hash type");
diff --git a/crypto/algapi.c b/crypto/algapi.c
index e65cb50..5fdb974 100644
--- a/crypto/algapi.c
+++ b/crypto/algapi.c
@@ -182,7 +182,7 @@ static int __crypto_register_alg(struct crypto_alg *alg,
 
                crypto_remove_spawns(&q->cra_users, list, alg->cra_flags);
        }
-       
+
        list_add(&alg->cra_list, &crypto_alg_list);
 
        crypto_notify(CRYPTO_MSG_ALG_REGISTER, alg);
diff --git a/crypto/api.c b/crypto/api.c
index a2496d1..c3213f4 100644
--- a/crypto/api.c
+++ b/crypto/api.c
@@ -10,7 +10,7 @@
  *
  * 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.
  *
  */
diff --git a/crypto/cryptd.c b/crypto/cryptd.c
index 074298f..cdf57c8 100644
--- a/crypto/cryptd.c
+++ b/crypto/cryptd.c
@@ -45,6 +45,14 @@ struct cryptd_blkcipher_request_ctx {
        crypto_completion_t complete;
 };
 
+struct cryptd_hash_ctx {
+       struct crypto_hash *child;
+};
+
+struct cryptd_hash_request_ctx {
+       crypto_completion_t complete;
+       struct hash_desc    desc;
+};
 
 static inline struct cryptd_state *cryptd_get_state(struct crypto_tfm *tfm)
 {
@@ -259,6 +267,216 @@ out_put_alg:
        return inst;
 }
 
+static int cryptd_hash_init_tfm(struct crypto_tfm *tfm)
+{
+       struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+       struct cryptd_instance_ctx *ictx = crypto_instance_ctx(inst);
+       struct crypto_spawn *spawn = &ictx->spawn;
+       struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct crypto_hash *cipher;
+
+       cipher = crypto_spawn_hash(spawn);
+       if (IS_ERR(cipher))
+               return PTR_ERR(cipher);
+
+       ctx->child = cipher;
+       tfm->crt_ahash.reqsize =
+               sizeof(struct cryptd_hash_request_ctx);
+       return 0;
+}
+
+static void cryptd_hash_exit_tfm(struct crypto_tfm *tfm)
+{
+       struct cryptd_hash_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct cryptd_state *state = cryptd_get_state(tfm);
+       int active;
+
+       mutex_lock(&state->mutex);
+       active = ahash_tfm_in_queue(&state->queue,
+                               __crypto_ahash_cast(tfm));
+       mutex_unlock(&state->mutex);
+
+       BUG_ON(active);
+
+       crypto_free_hash(ctx->child);
+}
+
+static int cryptd_hash_setkey(struct crypto_ahash *parent,
+                                  const u8 *key, unsigned int keylen)
+{
+       struct cryptd_hash_ctx *ctx   = crypto_ahash_ctx(parent);
+       struct crypto_hash     *child = ctx->child;
+       int err;
+
+       crypto_hash_clear_flags(child, CRYPTO_TFM_REQ_MASK);
+       crypto_hash_set_flags(child, crypto_ahash_get_flags(parent) &
+                                         CRYPTO_TFM_REQ_MASK);
+       err = crypto_hash_setkey(child, key, keylen);
+       crypto_ahash_set_flags(parent, crypto_hash_get_flags(child) &
+                                           CRYPTO_TFM_RES_MASK);
+       return err;
+}
+
+static int cryptd_hash_init(struct ahash_request *req)
+{
+       struct cryptd_hash_ctx *ctx   = ahash_request_ctx(req);
+       struct crypto_hash     *child = ctx->child;
+       struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
+       int err;
+
+       err = crypto_hash_crt(child)->init(&rctx->desc);
+       rctx->desc.flags |= CRYPTO_TFM_REQ_MAY_SLEEP;
+       return err;
+}
+
+static int cryptd_hash_enqueue(struct ahash_request *req,
+                               crypto_completion_t complete)
+{
+       struct cryptd_hash_request_ctx *rctx = ahash_request_ctx(req);
+       struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
+       struct cryptd_state *state =
+               cryptd_get_state(crypto_ahash_tfm(tfm));
+       int err;
+
+       rctx->complete = req->base.complete;
+       req->base.complete = complete;
+
+       spin_lock_bh(&state->lock);
+       err = ahash_enqueue_request(&state->queue, req);
+       spin_unlock_bh(&state->lock);
+
+       wake_up_process(state->task);
+       return err;
+}
+
+static void cryptd_hash_update(struct crypto_async_request *req_async, int
err)
+{
+       struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+       struct crypto_hash     *child = ctx->child;
+       struct ahash_request    *req = ahash_request_cast(req_async);
+       struct cryptd_hash_request_ctx *rctx;
+
+       rctx = ahash_request_ctx(req);
+
+       if (unlikely(err == -EINPROGRESS)) {
+               rctx->complete(&req->base, err);
+               return;
+       }
+
+       err = crypto_hash_crt(child)->update(&rctx->desc,
+                                               req->src,
+                                               req->nbytes);
+
+       req->base.complete = rctx->complete;
+
+       local_bh_disable();
+       req->base.complete(&req->base, err);
+       local_bh_enable();
+}
+
+static int cryptd_hash_update_enqueue(struct ahash_request *req)
+{
+       return cryptd_hash_enqueue(req, cryptd_hash_update);
+}
+
+static void cryptd_hash_final(struct crypto_async_request *req_async, int
err)
+{
+       struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+       struct crypto_hash     *child = ctx->child;
+       struct ahash_request    *req = ahash_request_cast(req_async);
+       struct cryptd_hash_request_ctx *rctx;
+
+       rctx = ahash_request_ctx(req);
+
+       if (unlikely(err == -EINPROGRESS)) {
+               rctx->complete(&req->base, err);
+               return;
+       }
+
+       err = crypto_hash_crt(child)->final(&rctx->desc, req->result);
+
+       req->base.complete = rctx->complete;
+
+       local_bh_disable();
+       req->base.complete(&req->base, err);
+       local_bh_enable();
+}
+
+static int cryptd_hash_final_enqueue(struct ahash_request *req)
+{
+       return cryptd_hash_enqueue(req, cryptd_hash_final);
+}
+
+static void cryptd_hash_digest(struct crypto_async_request *req_async, int
err)
+{
+       struct cryptd_hash_ctx *ctx   = crypto_tfm_ctx(req_async->tfm);
+       struct crypto_hash     *child = ctx->child;
+       struct ahash_request    *req = ahash_request_cast(req_async);
+       struct cryptd_hash_request_ctx *rctx;
+       struct hash_desc desc;
+
+       rctx = ahash_request_ctx(req);
+
+       if (unlikely(err == -EINPROGRESS)) {
+               rctx->complete(&req->base, err);
+               return;
+       }
+
+       desc.tfm = child;
+       desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+
+       err = crypto_hash_crt(child)->digest(&desc,
+                                               req->src,
+                                               req->nbytes,
+                                               req->result);
+
+       req->base.complete = rctx->complete;
+
+       local_bh_disable();
+       req->base.complete(&req->base, err);
+       local_bh_enable();
+}
+
+static int cryptd_hash_digest_enqueue(struct ahash_request *req)
+{
+       return cryptd_hash_enqueue(req, cryptd_hash_digest);
+}
+
+static struct crypto_instance *cryptd_alloc_hash(
+       struct rtattr **tb, struct cryptd_state *state)
+{
+       struct crypto_instance *inst;
+       struct crypto_alg *alg;
+
+       alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_HASH,
+                                 CRYPTO_ALG_TYPE_MASK);
+       if (IS_ERR(alg))
+               return ERR_PTR(PTR_ERR(alg));
+
+       inst = cryptd_alloc_instance(alg, state);
+       if (IS_ERR(inst))
+               goto out_put_alg;
+
+       inst->alg.cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC;
+       inst->alg.cra_type = &crypto_ahash_type;
+
+       inst->alg.cra_ahash.digestsize = alg->cra_hash.digestsize;
+       inst->alg.cra_ctxsize = sizeof(struct cryptd_hash_ctx);
+
+       inst->alg.cra_init = cryptd_hash_init_tfm;
+       inst->alg.cra_exit = cryptd_hash_exit_tfm;
+
+       inst->alg.cra_ahash.init   = cryptd_hash_init;
+       inst->alg.cra_ahash.update = cryptd_hash_update_enqueue;
+       inst->alg.cra_ahash.final  = cryptd_hash_final_enqueue;
+       inst->alg.cra_ahash.setkey = cryptd_hash_setkey;
+       inst->alg.cra_ahash.digest = cryptd_hash_digest_enqueue;
+
+out_put_alg:
+       crypto_mod_put(alg);
+       return inst;
+}
+
 static struct cryptd_state state;
 
 static struct crypto_instance *cryptd_alloc(struct rtattr **tb)
@@ -272,6 +490,8 @@ static struct crypto_instance *cryptd_alloc(struct
rtattr **tb)
        switch (algt->type & algt->mask & CRYPTO_ALG_TYPE_MASK) {
        case CRYPTO_ALG_TYPE_BLKCIPHER:
                return cryptd_alloc_blkcipher(tb, &state);
+       case CRYPTO_ALG_TYPE_HASH:
+               return cryptd_alloc_hash(tb, &state);
        }
 
        return ERR_PTR(-EINVAL);
diff --git a/crypto/digest.c b/crypto/digest.c
index 6fd43bd..19b7ade 100644
--- a/crypto/digest.c
+++ b/crypto/digest.c
@@ -141,14 +141,14 @@ int crypto_init_digest_ops(struct crypto_tfm *tfm)
 
        if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm))
                return -EINVAL;
-       
+
        ops->init       = init;
        ops->update     = update;
        ops->final      = final;
        ops->digest     = digest;
        ops->setkey     = dalg->dia_setkey ? setkey : nosetkey;
        ops->digestsize = dalg->dia_digestsize;
-       
+
        return 0;
 }
 
diff --git a/crypto/hash.c b/crypto/hash.c
index 7dcff67..6df8a8c 100644
--- a/crypto/hash.c
+++ b/crypto/hash.c
@@ -59,24 +59,108 @@ static int hash_setkey(struct crypto_hash *crt, const
u8 *key,
        return alg->setkey(crt, key, keylen);
 }
 
-static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+static int hash_async_setkey(struct crypto_ahash *tfm_async, const u8 *key,
+                       unsigned int keylen)
+{
+       struct crypto_tfm  *tfm      = crypto_ahash_tfm(tfm_async);
+       struct crypto_hash *tfm_hash = __crypto_hash_cast(tfm);
+       struct hash_alg    *alg      = &tfm->__crt_alg->cra_hash;
+
+       return alg->setkey(tfm_hash, key, keylen);
+}
+
+static int hash_async_init(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm = req->base.tfm;
+       struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+       struct hash_desc  desc = {
+               .tfm = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       return alg->init(&desc);
+}
+
+static int hash_async_update(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm = req->base.tfm;
+       struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+       struct hash_desc  desc = {
+               .tfm = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       return alg->update(&desc, req->src, req->nbytes);
+}
+
+static int hash_async_final(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm = req->base.tfm;
+       struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+       struct hash_desc  desc = {
+               .tfm = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       return alg->final(&desc, req->result);
+}
+
+static int hash_async_digest(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm = req->base.tfm;
+       struct hash_alg   *alg = &tfm->__crt_alg->cra_hash;
+       struct hash_desc  desc = {
+               .tfm = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       return alg->digest(&desc, req->src, req->nbytes, req->result);
+}
+
+static int crypto_init_hash_ops_async(struct crypto_tfm *tfm)
+{
+       struct ahash_tfm *crt = &tfm->crt_ahash;
+       struct hash_alg  *alg = &tfm->__crt_alg->cra_hash;
+
+       crt->init       = hash_async_init;
+       crt->update     = hash_async_update;
+       crt->final      = hash_async_final;
+       crt->digest     = hash_async_digest;
+       crt->setkey     = hash_async_setkey;
+       crt->digestsize = alg->digestsize;
+       crt->base       = __crypto_ahash_cast(tfm);
+
+       return 0;
+}
+
+static int crypto_init_hash_ops_sync(struct crypto_tfm *tfm)
 {
        struct hash_tfm *crt = &tfm->crt_hash;
        struct hash_alg *alg = &tfm->__crt_alg->cra_hash;
 
-       if (alg->digestsize > crypto_tfm_alg_blocksize(tfm))
-               return -EINVAL;
-
-       crt->init = alg->init;
-       crt->update = alg->update;
-       crt->final = alg->final;
-       crt->digest = alg->digest;
-       crt->setkey = hash_setkey;
+       crt->init       = alg->init;
+       crt->update     = alg->update;
+       crt->final      = alg->final;
+       crt->digest     = alg->digest;
+       crt->setkey     = hash_setkey;
        crt->digestsize = alg->digestsize;
 
        return 0;
 }
 
+static int crypto_init_hash_ops(struct crypto_tfm *tfm, u32 type, u32 mask)
+{
+       struct hash_alg *alg = &tfm->__crt_alg->cra_hash;
+
+       if (alg->digestsize > crypto_tfm_alg_blocksize(tfm))
+               return -EINVAL;
+
+       if (mask & CRYPTO_ALG_ASYNC)
+               return crypto_init_hash_ops_async(tfm);
+       else
+               return crypto_init_hash_ops_sync(tfm);
+}
+
 static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
        __attribute__ ((unused));
 static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c
index 1ab8c01..784f0b5 100644
--- a/crypto/tcrypt.c
+++ b/crypto/tcrypt.c
@@ -35,6 +35,7 @@
 #include <linux/jiffies.h>
 #include <linux/timex.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include "tcrypt.h"
 
 /*
@@ -220,6 +221,98 @@ out:
        crypto_free_hash(tfm);
 }
 
+static void test_ahash(char *algo, struct hash_testvec *template,
+                     unsigned int tcount)
+{
+       struct hash_testvec  *hash_tv;
+       struct crypto_ahash  *tfm = NULL;
+       struct ahash_request *req = NULL;
+       struct tcrypt_result result;
+       struct scatterlist   sg[8];
+       char            digest_result[tcount][4*16];
+       unsigned int    tsize;
+       unsigned int    i;
+       int             ret;
+
+       printk(KERN_INFO "\ntesting %s\n", algo);
+
+       tsize = sizeof(struct hash_testvec);
+       tsize *= tcount;
+       if (tsize > TVMEMSIZE) {
+               printk(KERN_ERR "template (%u) too big for tvmem (%u)\n",
+                       tsize, TVMEMSIZE);
+               return;
+       }
+       memcpy(tvmem, template, tsize);
+       hash_tv = (void *)tvmem;
+
+       init_completion(&result.completion);
+
+       tfm = crypto_alloc_ahash(algo, 0, 0);
+       if (IS_ERR(tfm)) {
+               printk(KERN_ERR "failed to load transform for %s: %ld\n",
algo,
+                      PTR_ERR(tfm));
+               return;
+       }
+       req = ahash_request_alloc(tfm, GFP_KERNEL);
+       if (!req) {
+               printk(KERN_ERR "failed to allocate request for %s\n",
algo);
+               goto out;
+       }
+       ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+                               tcrypt_complete, &result);
+
+       for (i = 0; i < tcount; i++) {
+               printk(KERN_INFO "test %u:\n", i + 1);
+               memset(&digest_result[i], 0, 4*16);
+               crypto_ahash_clear_flags(tfm, ~0);
+               if (hash_tv[i].ksize) {
+                       ret = crypto_ahash_setkey(tfm, hash_tv[i].key,
+                                                hash_tv[i].ksize);
+                       if (ret) {
+                               printk(KERN_ERR "setkey() failed error
%d\n",
+                                       ret);
+                               goto out;
+                       }
+               }
+
+               sg_init_one(&sg[0], hash_tv[i].plaintext, hash_tv[i].psize);
+
+               ahash_request_set_crypt(req, sg, digest_result[i],
+                                       hash_tv[i].psize);
+
+               ret = crypto_ahash_digest(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_ERR "hash() failed error %d\n", ret);
+                       goto out;
+               }
+
+               hexdump(digest_result[i], crypto_ahash_digestsize(tfm));
+               printk(KERN_INFO "%s\n",
+                      memcmp(digest_result[i], hash_tv[i].digest,
+                             crypto_ahash_digestsize(tfm)) ?
+                      "fail" : "pass");
+       }
+
+out:
+       if (req)
+               ahash_request_free(req);
+
+       crypto_free_ahash(tfm);
+}
+
 static void test_aead(char *algo, int enc, struct aead_testvec *template,
                      unsigned int tcount)
 {
@@ -471,7 +564,7 @@ static void test_cipher(char *algo, int enc,
        else
                e = "decryption";
 
-       printk("\ntesting %s %s\n", algo, e);
+       printk(KERN_INFO "\ntesting cipher %s %s\n", algo, e);
 
        tsize = sizeof (struct cipher_testvec);
        if (tsize > TVMEMSIZE) {
@@ -1619,6 +1712,51 @@ static void do_test(void)
                          XCBC_AES_TEST_VECTORS);
                break;
 
+       case 110:
+               test_ahash("hmac(md5)", hmac_md5_tv_template,
+                         HMAC_MD5_TEST_VECTORS);
+               break;
+
+       case 111:
+               test_ahash("hmac(sha1)", hmac_sha1_tv_template,
+                         HMAC_SHA1_TEST_VECTORS);
+               break;
+
+       case 112:
+               test_ahash("hmac(sha256)", hmac_sha256_tv_template,
+                         HMAC_SHA256_TEST_VECTORS);
+               break;
+
+       case 113:
+               test_ahash("hmac(sha384)", hmac_sha384_tv_template,
+                         HMAC_SHA384_TEST_VECTORS);
+               break;
+
+       case 114:
+               test_ahash("hmac(sha512)", hmac_sha512_tv_template,
+                         HMAC_SHA512_TEST_VECTORS);
+               break;
+
+       case 115:
+               test_ahash("hmac(sha224)", hmac_sha224_tv_template,
+                         HMAC_SHA224_TEST_VECTORS);
+               break;
+
+       case 120:
+               test_ahash("hmac(md5)", hmac_md5_tv_template,
+                         HMAC_MD5_TEST_VECTORS);
+               test_ahash("hmac(sha1)", hmac_sha1_tv_template,
+                         HMAC_SHA1_TEST_VECTORS);
+               test_ahash("hmac(sha224)", hmac_sha224_tv_template,
+                         HMAC_SHA224_TEST_VECTORS);
+               test_ahash("hmac(sha256)", hmac_sha256_tv_template,
+                         HMAC_SHA256_TEST_VECTORS);
+               test_ahash("hmac(sha384)", hmac_sha384_tv_template,
+                         HMAC_SHA384_TEST_VECTORS);
+               test_ahash("hmac(sha512)", hmac_sha512_tv_template,
+                         HMAC_SHA512_TEST_VECTORS);
+               break;
+
        case 200:
                test_cipher_speed("ecb(aes)", ENCRYPT, sec, NULL, 0,
                                  aes_speed_template);
@@ -1795,7 +1933,7 @@ static int __init init(void)
 
        /* We intentionaly return -EAGAIN to prevent keeping
         * the module. It does all its work from init()
-        * and doesn't offer any runtime functionality 
+        * and doesn't offer any runtime functionality
         * => we don't need it in the memory, do we?
         *                                        -- mludvig
         */
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index d8c7040..21e4234 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -92,6 +92,12 @@ config CRYPTO_DEV_HIFN_795X
        help
          This option allows you to have support for HIFN 795x crypto
adapters.
 
-
+config CRYPTO_DEV_AHASH_SAMPLE
+       tristate "Asynchronous HASH sample driver over software synchronous
HASH"
+       select CRYPTO_HASH
+       select CRYPTO_ALGAPI
+       help
+         This is a sample asynchronous HASH device driver over synchronous
software
+          HASH.
 
 endif # CRYPTO_HW
diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
index c0327f0..0b1cc2f 100644
--- a/drivers/crypto/Makefile
+++ b/drivers/crypto/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
 obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
+obj-$(CONFIG_CRYPTO_DEV_AHASH_SAMPLE) += ahash_sample.o
diff --git a/drivers/crypto/ahash_sample.c b/drivers/crypto/ahash_sample.c
new file mode 100644
index 0000000..0c1ad60
--- /dev/null
+++ b/drivers/crypto/ahash_sample.c
@@ -0,0 +1,354 @@
+/*
+ * Sample Asynchronous device driver that wraps around software sync HASH
+ *
+ * 2008 Copyright (c) Loc Ho <[EMAIL PROTECTED]>
+ * All rights reserved.
+ *
+ * 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mod_devicetable.h>
+#include <linux/interrupt.h>
+#include <linux/highmem.h>
+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+#include <crypto/algapi.h>
+
+struct ahash_sample_device {
+       char                    name[64];
+       struct tasklet_struct   tasklet;
+       struct crypto_queue     queue;
+       spinlock_t              lock;           /**< Queue lock protection
*/
+       struct list_head        alg_list;
+};
+
+#define AHASH_SAMPLE_OP_DIGEST 0
+#define AHASH_SAMPLE_OP_UPDATE 1
+#define AHASH_SAMPLE_OP_FINAL  2
+
+struct ahash_sample_context {
+       struct ahash_sample_device *dev;
+       u8                      key[16];
+       unsigned int            keysize;
+       struct crypto_hash      *sync_tfm;
+       struct hash_desc        desc;
+       u8      ops;
+};
+
+struct ahash_sample_alg {
+       struct list_head        entry;
+       struct crypto_alg       alg;
+       struct ahash_sample_device *dev;
+};
+
+static struct ahash_sample_device *ahash_sample_dev;
+
+#define crypto_alg_to_ahash_sample_alg(a)      container_of(a, \
+                                               struct ahash_sample_alg, \
+                                               alg)
+
+static int ahash_sample_alg_init(struct crypto_tfm *tfm)
+{
+       struct crypto_alg    *alg          = tfm->__crt_alg;
+       struct ahash_sample_alg *ahash_alg = crypto_alg_to_ahash_sample_alg(
+
alg);
+       struct ahash_sample_context *ctx   = crypto_tfm_ctx(tfm);
+
+       ctx->dev      = ahash_alg->dev;
+       ctx->sync_tfm = crypto_alloc_hash(alg->cra_name, 0,
CRYPTO_ALG_ASYNC);
+       if (IS_ERR(ctx->sync_tfm)) {
+               printk(KERN_ERR
+                       "AHASH_SAMPLE: failed to load transform for %s:
%ld\n",
+                       alg->cra_name, PTR_ERR(ctx->sync_tfm));
+               return -ENOMEM;
+       }
+       printk(KERN_INFO "AHASH_SAMPLE: initialize alg %s\n",
alg->cra_name);
+       return 0;
+}
+
+static void ahash_sample_alg_exit(struct crypto_tfm *tfm)
+{
+       struct crypto_alg           *alg = tfm->__crt_alg;
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(tfm);
+
+       printk(KERN_INFO "AHASH_SAMPLE: exit alg %s\n", alg->cra_name);
+
+       if (ctx->sync_tfm) {
+               crypto_free_hash(ctx->sync_tfm);
+               ctx->sync_tfm = NULL;
+               ctx->dev      = NULL;
+       }
+}
+
+static int ahash_sample_ops_setkey(struct crypto_ahash *cipher, const u8
*key,
+                       unsigned int keylen)
+{
+       struct crypto_tfm           *tfm = crypto_ahash_tfm(cipher);
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(tfm);
+       int    ret;
+
+       printk(KERN_INFO "AHASH_SAMPLE: setkey\n");
+
+       ret = crypto_hash_setkey(ctx->sync_tfm, key, keylen);
+       if (ret) {
+               printk(KERN_ERR
+                       "aynchronous hash generic setkey failed error %d\n",
+                       ret);
+               return -1;
+       }
+       return ret;
+}
+
+static inline int ahash_sample_ops_init(struct ahash_request *req)
+{
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(req->base.tfm);
+
+       printk(KERN_INFO "AHASH_SAMPLE: init\n");
+
+       ctx->desc.tfm   = ctx->sync_tfm;
+       ctx->desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
+       return crypto_hash_init(&ctx->desc);
+}
+
+static inline int ahash_sample_ops_update(struct ahash_request *req)
+{
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       unsigned long flags;
+       int ret;
+
+       printk(KERN_INFO "AHASH_SAMPLE: update\n");
+
+       ctx->ops = AHASH_SAMPLE_OP_UPDATE;
+       spin_lock_irqsave(&ctx->dev->lock, flags);
+       ret = ahash_enqueue_request(&ctx->dev->queue, req);
+       spin_unlock_irqrestore(&ctx->dev->lock, flags);
+
+       tasklet_schedule(&ctx->dev->tasklet);
+       return ret;
+}
+
+static inline int ahash_sample_ops_final(struct ahash_request *req)
+{
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       unsigned long flags;
+       int ret;
+
+       printk(KERN_INFO "AHASH_SAMPLE: final\n");
+
+       ctx->ops = AHASH_SAMPLE_OP_FINAL;
+       spin_lock_irqsave(&ctx->dev->lock, flags);
+       ret = ahash_enqueue_request(&ctx->dev->queue, req);
+       spin_unlock_irqrestore(&ctx->dev->lock, flags);
+
+       tasklet_schedule(&ctx->dev->tasklet);
+       return ret;
+}
+
+static inline int ahash_sample_ops_digest(struct ahash_request *req)
+{
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       unsigned long flags;
+       int ret;
+
+       printk(KERN_INFO "AHASH_SAMPLE: digest\n");
+
+       ctx->ops = AHASH_SAMPLE_OP_DIGEST;
+       spin_lock_irqsave(&ctx->dev->lock, flags);
+       ret = ahash_enqueue_request(&ctx->dev->queue, req);
+       spin_unlock_irqrestore(&ctx->dev->lock, flags);
+
+       tasklet_schedule(&ctx->dev->tasklet);
+       return ret;
+}
+
+static int ahash_sample_handle_req(struct ahash_request *req)
+{
+       struct ahash_sample_context *ctx = crypto_tfm_ctx(req->base.tfm);
+       struct hash_desc desc;
+       int    ret;
+
+       desc.tfm   = ctx->sync_tfm;
+       desc.flags = 0;
+       switch (ctx->ops) {
+       case AHASH_SAMPLE_OP_UPDATE:
+               ret = crypto_hash_update(&desc, req->src, req->nbytes);
+               break;
+       case AHASH_SAMPLE_OP_FINAL:
+               ret = crypto_hash_final(&desc, req->result);
+               break;
+       case AHASH_SAMPLE_OP_DIGEST:
+       default:
+               ret = crypto_hash_digest(&desc, req->src,
+                                       req->nbytes, req->result);
+               break;
+       }
+       if (ret) {
+               printk(KERN_ERR "AHASH_SAMPLE: "
+                       "asynchronous hash generic digest failed error
%d\n",
+                       ret);
+               return ret;
+       }
+       return 0;
+}
+
+static void ahash_sample_bh_tasklet_cb(unsigned long data)
+{
+       struct ahash_sample_device  *dev = (struct ahash_sample_device *)
data;
+       struct crypto_async_request *async_req;
+       struct ahash_sample_context *ctx;
+       struct ahash_request        *req;
+       unsigned long   flags;
+       int             err;
+
+       while (1) {
+               spin_lock_irqsave(&dev->lock, flags);
+               async_req = crypto_dequeue_request(&dev->queue);
+               spin_unlock_irqrestore(&dev->lock, flags);
+
+               if (!async_req)
+                       break;
+
+               ctx = crypto_tfm_ctx(async_req->tfm);
+               req = container_of(async_req, struct ahash_request, base);
+
+               /* Process the request */
+               err = ahash_sample_handle_req(req);
+
+               /* Notify packet completed */
+               req->base.complete(&req->base, err);
+       }
+}
+
+static struct crypto_alg ahash_sample_alg_tbl[] =
+{
+       { .cra_name             = "hmac(md5)",
+         .cra_driver_name      = "ahash-md5",
+         .cra_priority         = 300,
+         .cra_flags            = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC,
+         .cra_blocksize        = 64, /* MD5-HMAC block size is 512-bits */
+         .cra_ctxsize          = sizeof(struct ahash_sample_context),
+         .cra_alignmask        = 0,
+         .cra_type             = &crypto_ahash_type,
+         .cra_module           = THIS_MODULE,
+         .cra_u                = { .ahash = {
+         .digestsize           = 16, /* Disgest is 128-bits */
+         .init                 = ahash_sample_ops_init,
+         .update               = ahash_sample_ops_update,
+         .final                = ahash_sample_ops_final,
+         .digest               = ahash_sample_ops_digest,
+         .setkey               = ahash_sample_ops_setkey,
+         } },
+       },
+       { .cra_name             = "" }
+};
+
+static void ahash_sample_unregister_alg(struct ahash_sample_device *dev)
+{
+       struct ahash_sample_alg *alg, *tmp;
+
+       list_for_each_entry_safe(alg, tmp, &dev->alg_list, entry) {
+               list_del(&alg->entry);
+               crypto_unregister_alg(&alg->alg);
+               kfree(alg);
+       }
+}
+
+static int ahash_sample_register_alg(struct ahash_sample_device *dev)
+{
+       struct ahash_sample_alg *alg;
+       int i;
+       int rc = 0;
+
+       for (i = 0; ahash_sample_alg_tbl[i].cra_name[0] != '\0'; i++) {
+               alg = kzalloc(sizeof(struct ahash_sample_alg), GFP_KERNEL);
+               if (!alg)
+                       return -ENOMEM;
+
+               alg->alg                = ahash_sample_alg_tbl[i];
+               INIT_LIST_HEAD(&alg->alg.cra_list);
+               alg->dev                = dev;
+               alg->alg.cra_init       = ahash_sample_alg_init;
+               alg->alg.cra_exit       = ahash_sample_alg_exit;
+               list_add_tail(&alg->entry, &dev->alg_list);
+               rc = crypto_register_alg(&alg->alg);
+               if (rc) {
+                       printk(KERN_ERR
+                               "AHASH_SAMPLE: failed to register alg
%s.%s",
+                               ahash_sample_alg_tbl[i].cra_driver_name,
+                               ahash_sample_alg_tbl[i].cra_name);
+
+                       list_del(&alg->entry);
+                       kfree(alg);
+                       return rc;
+               }
+       }
+       return rc;
+}
+
+static int __devinit ahash_sample_init(void)
+{
+       int err;
+
+       ahash_sample_dev = kzalloc(sizeof(struct ahash_sample_device) +
+                               sizeof(struct crypto_alg),
+                               GFP_KERNEL);
+       if (!ahash_sample_dev) {
+               err = -ENOMEM;
+               goto err_nomem;
+       }
+
+       INIT_LIST_HEAD(&ahash_sample_dev->alg_list);
+       strncpy(ahash_sample_dev->name, "AHASH_generic",
+               sizeof(ahash_sample_dev->name));
+
+       err = ahash_sample_register_alg(ahash_sample_dev);
+       if (err)
+               goto err_register_alg;
+
+       /* Init tasklet for asynchronous processing */
+       tasklet_init(&ahash_sample_dev->tasklet, ahash_sample_bh_tasklet_cb,
+               (unsigned long) ahash_sample_dev);
+       crypto_init_queue(&ahash_sample_dev->queue, 64*1024);
+
+       printk(KERN_INFO "AHASH_SAMPLE: Asynchronous "
+               "hashing sample driver successfully registered.\n");
+       return 0;
+
+err_register_alg:
+       kfree(ahash_sample_dev);
+       ahash_sample_dev = NULL;
+
+err_nomem:
+       return err;
+}
+
+static void __devexit ahash_sample_fini(void)
+{
+       ahash_sample_unregister_alg(ahash_sample_dev);
+       kfree(ahash_sample_dev);
+       ahash_sample_dev = NULL;
+       printk(KERN_INFO
+               "AHASH_SAMPLE: Driver for testing asynchronous hash support
"
+               "framework has been successfully unregistered.\n");
+}
+
+module_init(ahash_sample_init);
+module_exit(ahash_sample_fini);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Loc Ho <[EMAIL PROTECTED]>");
+MODULE_DESCRIPTION("Sample asynchronous hash driver");
diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h
index 60d06e7..fef272a 100644
--- a/include/crypto/algapi.h
+++ b/include/crypto/algapi.h
@@ -98,6 +98,7 @@ extern const struct crypto_type crypto_ablkcipher_type;
 extern const struct crypto_type crypto_aead_type;
 extern const struct crypto_type crypto_blkcipher_type;
 extern const struct crypto_type crypto_hash_type;
+extern const struct crypto_type crypto_ahash_type;
 
 void crypto_mod_put(struct crypto_alg *alg);
 
@@ -314,5 +315,40 @@ static inline int crypto_requires_sync(u32 type, u32
mask)
        return (type ^ CRYPTO_ALG_ASYNC) & mask & CRYPTO_ALG_ASYNC;
 }
 
+static inline void *crypto_ahash_ctx(struct crypto_ahash *tfm)
+{
+       return crypto_tfm_ctx(&tfm->base);
+}
+
+static inline struct ahash_alg *crypto_ahash_alg(
+       struct crypto_ahash *tfm)
+{
+       return &crypto_ahash_tfm(tfm)->__crt_alg->cra_ahash;
+}
+
+static inline int ahash_enqueue_request(struct crypto_queue *queue,
+                                            struct ahash_request *request)
+{
+       return crypto_enqueue_request(queue, &request->base);
+}
+
+static inline struct ahash_request *ahash_dequeue_request(
+       struct crypto_queue *queue)
+{
+       return ahash_request_cast(crypto_dequeue_request(queue));
+}
+
+static inline void *ahash_request_ctx(struct ahash_request *req)
+{
+       return req->__ctx;
+}
+
+static inline int ahash_tfm_in_queue(struct crypto_queue *queue,
+                                         struct crypto_ahash *tfm)
+{
+       return crypto_tfm_in_queue(queue, crypto_ahash_tfm(tfm));
+}
+
+
 #endif /* _CRYPTO_ALGAPI_H */
 
diff --git a/include/linux/crypto.h b/include/linux/crypto.h
index 5e02d1b..fe9a5c2 100644
--- a/include/linux/crypto.h
+++ b/include/linux/crypto.h
@@ -7,10 +7,10 @@
  *
  * Portions derived from Cryptoapi, by Alexander Kjeldaas <[EMAIL PROTECTED]>
  * and Nettle, by Niels Möller.
- * 
+ *
  * 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.
  *
  */
@@ -37,6 +37,7 @@
 #define CRYPTO_ALG_TYPE_GIVCIPHER      0x00000006
 #define CRYPTO_ALG_TYPE_COMPRESS       0x00000008
 #define CRYPTO_ALG_TYPE_AEAD           0x00000009
+#define CRYPTO_ALG_TYPE_AHASH          0x0000000A
 
 #define CRYPTO_ALG_TYPE_HASH_MASK      0x0000000e
 #define CRYPTO_ALG_TYPE_BLKCIPHER_MASK 0x0000000c
@@ -102,6 +103,7 @@ struct crypto_async_request;
 struct crypto_aead;
 struct crypto_blkcipher;
 struct crypto_hash;
+struct crypto_ahash;
 struct crypto_tfm;
 struct crypto_type;
 struct aead_givcrypt_request;
@@ -131,6 +133,16 @@ struct ablkcipher_request {
        void *__ctx[] CRYPTO_MINALIGN_ATTR;
 };
 
+struct ahash_request {
+       struct crypto_async_request base;
+
+       unsigned int nbytes;
+       struct scatterlist *src;
+       u8                 *result;
+
+       void *__ctx[] CRYPTO_MINALIGN_ATTR;
+};
+
 /**
  *     struct aead_request - AEAD request
  *     @base: Common attributes for async crypto requests
@@ -195,6 +207,17 @@ struct ablkcipher_alg {
        unsigned int ivsize;
 };
 
+struct ahash_alg {
+       int (*init)(struct ahash_request *req);
+       int (*update)(struct ahash_request *req);
+       int (*final)(struct ahash_request *req);
+       int (*digest)(struct ahash_request *req);
+       int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
+                       unsigned int keylen);
+
+       unsigned int digestsize;
+};
+
 struct aead_alg {
        int (*setkey)(struct crypto_aead *tfm, const u8 *key,
                      unsigned int keylen);
@@ -272,6 +295,7 @@ struct compress_alg {
 #define cra_cipher     cra_u.cipher
 #define cra_digest     cra_u.digest
 #define cra_hash       cra_u.hash
+#define cra_ahash      cra_u.ahash
 #define cra_compress   cra_u.compress
 
 struct crypto_alg {
@@ -298,13 +322,14 @@ struct crypto_alg {
                struct cipher_alg cipher;
                struct digest_alg digest;
                struct hash_alg hash;
+               struct ahash_alg ahash;
                struct compress_alg compress;
        } cra_u;
 
        int (*cra_init)(struct crypto_tfm *tfm);
        void (*cra_exit)(struct crypto_tfm *tfm);
        void (*cra_destroy)(struct crypto_alg *alg);
-       
+
        struct module *cra_module;
 };
 
@@ -390,6 +415,19 @@ struct hash_tfm {
        unsigned int digestsize;
 };
 
+struct ahash_tfm {
+       int (*init)(struct ahash_request *req);
+       int (*update)(struct ahash_request *req);
+       int (*final)(struct ahash_request *req);
+       int (*digest)(struct ahash_request *req);
+       int (*setkey)(struct crypto_ahash *tfm, const u8 *key,
+                       unsigned int keylen);
+
+       unsigned int digestsize;
+       struct crypto_ahash *base;
+       unsigned int reqsize;
+};
+
 struct compress_tfm {
        int (*cot_compress)(struct crypto_tfm *tfm,
                            const u8 *src, unsigned int slen,
@@ -404,21 +442,23 @@ struct compress_tfm {
 #define crt_blkcipher  crt_u.blkcipher
 #define crt_cipher     crt_u.cipher
 #define crt_hash       crt_u.hash
+#define crt_ahash      crt_u.ahash
 #define crt_compress   crt_u.compress
 
 struct crypto_tfm {
 
        u32 crt_flags;
-       
+
        union {
                struct ablkcipher_tfm ablkcipher;
                struct aead_tfm aead;
                struct blkcipher_tfm blkcipher;
                struct cipher_tfm cipher;
                struct hash_tfm hash;
+               struct ahash_tfm ahash;
                struct compress_tfm compress;
        } crt_u;
-       
+
        struct crypto_alg *__crt_alg;
 
        void *__crt_ctx[] CRYPTO_MINALIGN_ATTR;
@@ -448,6 +488,10 @@ struct crypto_hash {
        struct crypto_tfm base;
 };
 
+struct crypto_ahash {
+       struct crypto_tfm base;
+};
+
 enum {
        CRYPTOA_UNSPEC,
        CRYPTOA_ALG,
@@ -477,7 +521,7 @@ struct crypto_attr_u32 {
 /* 
  * Transform user interface.
  */
- 
+
 struct crypto_tfm *crypto_alloc_tfm(const char *alg_name, u32 tfm_flags);
 struct crypto_tfm *crypto_alloc_base(const char *alg_name, u32 type, u32
mask);
 void crypto_free_tfm(struct crypto_tfm *tfm);
@@ -1112,7 +1156,7 @@ static inline struct crypto_hash
*crypto_alloc_hash(const char *alg_name,
                                                    u32 type, u32 mask)
 {
        type &= ~CRYPTO_ALG_TYPE_MASK;
-       mask &= ~CRYPTO_ALG_TYPE_MASK;
+       mask &= ~(CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC);
        type |= CRYPTO_ALG_TYPE_HASH;
        mask |= CRYPTO_ALG_TYPE_HASH_MASK;
 
@@ -1271,5 +1315,130 @@ static inline int crypto_comp_decompress(struct
crypto_comp *tfm,
                                                    src, slen, dst, dlen);
 }
 
+static inline struct crypto_tfm *crypto_ahash_tfm(
+       struct crypto_ahash *tfm)
+{
+       return &tfm->base;
+}
+
+struct crypto_ahash *crypto_alloc_ahash(const char *alg_name,
+                                       u32 type, u32 mask);
+
+static inline void crypto_free_ahash(struct crypto_ahash *tfm)
+{
+       crypto_free_tfm(crypto_ahash_tfm(tfm));
+}
+
+static inline struct crypto_ahash *__crypto_ahash_cast(struct crypto_tfm
*tfm)
+{
+       return (struct crypto_ahash *) tfm;
+}
+
+static inline unsigned int crypto_ahash_alignmask(
+       struct crypto_ahash *tfm)
+{
+       return crypto_tfm_alg_alignmask(crypto_ahash_tfm(tfm));
+}
+
+static inline struct ahash_tfm *crypto_ahash_crt(struct crypto_ahash *tfm)
+{
+       return &crypto_ahash_tfm(tfm)->crt_ahash;
+}
+
+static inline unsigned int crypto_ahash_digestsize(struct crypto_ahash
*tfm)
+{
+       return crypto_ahash_crt(tfm)->digestsize;
+}
+
+static inline u32 crypto_ahash_get_flags(struct crypto_ahash *tfm)
+{
+       return crypto_tfm_get_flags(crypto_ahash_tfm(tfm));
+}
+
+static inline void crypto_ahash_set_flags(struct crypto_ahash *tfm, u32
flags)
+{
+       crypto_tfm_set_flags(crypto_ahash_tfm(tfm), flags);
+}
+
+static inline void crypto_ahash_clear_flags(struct crypto_ahash *tfm, u32
flags)
+{
+       crypto_tfm_clear_flags(crypto_ahash_tfm(tfm), flags);
+}
+
+static inline struct crypto_ahash *crypto_ahash_reqtfm(
+       struct ahash_request *req)
+{
+       return __crypto_ahash_cast(req->base.tfm);
+}
+
+static inline unsigned int crypto_ahash_reqsize(struct crypto_ahash *tfm)
+{
+       return crypto_ahash_crt(tfm)->reqsize;
+}
+
+static inline int crypto_ahash_setkey(struct crypto_ahash *tfm,
+                                       const u8 *key, unsigned int keylen)
+{
+       struct ahash_tfm *crt = crypto_ahash_crt(tfm);
+
+       return crt->setkey(crt->base, key, keylen);
+}
+
+static inline int crypto_ahash_digest(struct ahash_request *req)
+{
+       struct ahash_tfm *crt = crypto_ahash_crt(crypto_ahash_reqtfm(req));
+       return crt->digest(req);
+}
+
+static inline void ahash_request_set_tfm(
+       struct ahash_request *req, struct crypto_ahash *tfm)
+{
+       req->base.tfm = crypto_ahash_tfm(crypto_ahash_crt(tfm)->base);
+}
+
+static inline struct ahash_request *ahash_request_alloc(
+       struct crypto_ahash *tfm, gfp_t gfp)
+{
+       struct ahash_request *req;
+
+       req = kmalloc(sizeof(struct ahash_request) +
+                     crypto_ahash_reqsize(tfm), gfp);
+
+       if (likely(req))
+               ahash_request_set_tfm(req, tfm);
+
+       return req;
+}
+
+static inline void ahash_request_free(struct ahash_request *req)
+{
+       kfree(req);
+}
+
+static inline struct ahash_request *ahash_request_cast(
+       struct crypto_async_request *req)
+{
+       return container_of(req, struct ahash_request, base);
+}
+
+static inline void ahash_request_set_callback(
+       struct ahash_request *req,
+       u32 flags, crypto_completion_t complete, void *data)
+{
+       req->base.complete = complete;
+       req->base.data = data;
+       req->base.flags = flags;
+}
+
+static inline void ahash_request_set_crypt(
+       struct ahash_request *req,
+       struct scatterlist *src, u8 *result,
+       unsigned int nbytes)
+{
+       req->src = src;
+       req->nbytes = nbytes;
+       req->result = result;
+}
+
 #endif /* _LINUX_CRYPTO_H */
 
-- 
1.5.3

-
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