crypto: shash - Export/import hash state only
[safe/jmp/linux-2.6] / crypto / digest.c
index 0155a94..5d3f130 100644 (file)
  *
  */
 
+#include <crypto/internal/hash.h>
+#include <crypto/scatterwalk.h>
 #include <linux/mm.h>
 #include <linux/errno.h>
+#include <linux/hardirq.h>
 #include <linux/highmem.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/scatterlist.h>
 
 #include "internal.h"
-#include "scatterwalk.h"
-
-void crypto_digest_init(struct crypto_tfm *tfm)
-{
-       struct crypto_hash *hash = crypto_hash_cast(tfm);
-       struct hash_desc desc = { .tfm = hash, .flags = tfm->crt_flags };
-
-       crypto_hash_init(&desc);
-}
-EXPORT_SYMBOL_GPL(crypto_digest_init);
-
-void crypto_digest_update(struct crypto_tfm *tfm,
-                         struct scatterlist *sg, unsigned int nsg)
-{
-       struct crypto_hash *hash = crypto_hash_cast(tfm);
-       struct hash_desc desc = { .tfm = hash, .flags = tfm->crt_flags };
-       unsigned int nbytes = 0;
-       unsigned int i;
-
-       for (i = 0; i < nsg; i++)
-               nbytes += sg[i].length;
-
-       crypto_hash_update(&desc, sg, nbytes);
-}
-EXPORT_SYMBOL_GPL(crypto_digest_update);
-
-void crypto_digest_final(struct crypto_tfm *tfm, u8 *out)
-{
-       struct crypto_hash *hash = crypto_hash_cast(tfm);
-       struct hash_desc desc = { .tfm = hash, .flags = tfm->crt_flags };
-
-       crypto_hash_final(&desc, out);
-}
-EXPORT_SYMBOL_GPL(crypto_digest_final);
-
-void crypto_digest_digest(struct crypto_tfm *tfm,
-                         struct scatterlist *sg, unsigned int nsg, u8 *out)
-{
-       struct crypto_hash *hash = crypto_hash_cast(tfm);
-       struct hash_desc desc = { .tfm = hash, .flags = tfm->crt_flags };
-       unsigned int nbytes = 0;
-       unsigned int i;
-
-       for (i = 0; i < nsg; i++)
-               nbytes += sg[i].length;
-
-       crypto_hash_digest(&desc, sg, nbytes, out);
-}
-EXPORT_SYMBOL_GPL(crypto_digest_digest);
 
 static int init(struct hash_desc *desc)
 {
@@ -77,8 +32,8 @@ static int init(struct hash_desc *desc)
        return 0;
 }
 
-static int update(struct hash_desc *desc,
-                 struct scatterlist *sg, unsigned int nbytes)
+static int update2(struct hash_desc *desc,
+                  struct scatterlist *sg, unsigned int nbytes)
 {
        struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
        unsigned int alignmask = crypto_tfm_alg_alignmask(tfm);
@@ -87,7 +42,7 @@ static int update(struct hash_desc *desc,
                return 0;
 
        for (;;) {
-               struct page *pg = sg->page;
+               struct page *pg = sg_page(sg);
                unsigned int offset = sg->offset;
                unsigned int l = sg->length;
 
@@ -123,12 +78,20 @@ static int update(struct hash_desc *desc,
 
                if (!nbytes)
                        break;
-               sg = sg_next(sg);
+               sg = scatterwalk_sg_next(sg);
        }
 
        return 0;
 }
 
+static int update(struct hash_desc *desc,
+                 struct scatterlist *sg, unsigned int nbytes)
+{
+       if (WARN_ON_ONCE(in_irq()))
+               return -EDEADLK;
+       return update2(desc, sg, nbytes);
+}
+
 static int final(struct hash_desc *desc, u8 *out)
 {
        struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
@@ -166,22 +129,20 @@ static int setkey(struct crypto_hash *hash, const u8 *key, unsigned int keylen)
 static int digest(struct hash_desc *desc,
                  struct scatterlist *sg, unsigned int nbytes, u8 *out)
 {
+       if (WARN_ON_ONCE(in_irq()))
+               return -EDEADLK;
+
        init(desc);
-       update(desc, sg, nbytes);
+       update2(desc, sg, nbytes);
        return final(desc, out);
 }
 
-int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags)
-{
-       return flags ? -EINVAL : 0;
-}
-
 int crypto_init_digest_ops(struct crypto_tfm *tfm)
 {
        struct hash_tfm *ops = &tfm->crt_hash;
        struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
 
-       if (dalg->dia_digestsize > crypto_tfm_alg_blocksize(tfm))
+       if (dalg->dia_digestsize > PAGE_SIZE / 8)
                return -EINVAL;
        
        ops->init       = init;
@@ -197,3 +158,83 @@ int crypto_init_digest_ops(struct crypto_tfm *tfm)
 void crypto_exit_digest_ops(struct crypto_tfm *tfm)
 {
 }
+
+static int digest_async_nosetkey(struct crypto_ahash *tfm_async, const u8 *key,
+                       unsigned int keylen)
+{
+       crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK);
+       return -ENOSYS;
+}
+
+static int digest_async_setkey(struct crypto_ahash *tfm_async, const u8 *key,
+                       unsigned int keylen)
+{
+       struct crypto_tfm    *tfm        = crypto_ahash_tfm(tfm_async);
+       struct digest_alg    *dalg       = &tfm->__crt_alg->cra_digest;
+
+       crypto_ahash_clear_flags(tfm_async, CRYPTO_TFM_RES_MASK);
+       return dalg->dia_setkey(tfm, key, keylen);
+}
+
+static int digest_async_init(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm  = req->base.tfm;
+       struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
+
+       dalg->dia_init(tfm);
+       return 0;
+}
+
+static int digest_async_update(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm = req->base.tfm;
+       struct hash_desc  desc = {
+               .tfm   = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       update(&desc, req->src, req->nbytes);
+       return 0;
+}
+
+static int digest_async_final(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm  = req->base.tfm;
+       struct hash_desc  desc = {
+               .tfm   = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       final(&desc, req->result);
+       return 0;
+}
+
+static int digest_async_digest(struct ahash_request *req)
+{
+       struct crypto_tfm *tfm  = req->base.tfm;
+       struct hash_desc  desc = {
+               .tfm   = __crypto_hash_cast(tfm),
+               .flags = req->base.flags,
+       };
+
+       return digest(&desc, req->src, req->nbytes, req->result);
+}
+
+int crypto_init_digest_ops_async(struct crypto_tfm *tfm)
+{
+       struct ahash_tfm  *crt  = &tfm->crt_ahash;
+       struct digest_alg *dalg = &tfm->__crt_alg->cra_digest;
+
+       if (dalg->dia_digestsize > PAGE_SIZE / 8)
+               return -EINVAL;
+
+       crt->init       = digest_async_init;
+       crt->update     = digest_async_update;
+       crt->final      = digest_async_final;
+       crt->digest     = digest_async_digest;
+       crt->setkey     = dalg->dia_setkey ? digest_async_setkey :
+                                               digest_async_nosetkey;
+       crt->digestsize = dalg->dia_digestsize;
+
+       return 0;
+}