summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2006-08-19 22:24:23 +1000
committerHerbert Xu <herbert@gondor.apana.org.au>2006-09-21 11:46:17 +1000
commit055bcee3102dc35f019b69df9c2618e9d6dd1c09 (patch)
tree3f7c68abbbb5041d570e4cb8588f3943530bc0b7 /crypto
parent7226bc877a22244e8003924031435a4bffd52654 (diff)
downloadlinux-3.10-055bcee3102dc35f019b69df9c2618e9d6dd1c09.tar.gz
linux-3.10-055bcee3102dc35f019b69df9c2618e9d6dd1c09.tar.bz2
linux-3.10-055bcee3102dc35f019b69df9c2618e9d6dd1c09.zip
[CRYPTO] digest: Added user API for new hash type
The existing digest user interface is inadequate for support asynchronous operations. For one it doesn't return a value to indicate success or failure, nor does it take a per-operation descriptor which is essential for the issuing of requests while other requests are still outstanding. This patch is the first in a series of steps to remodel the interface for asynchronous operations. For the ease of transition the new interface will be known as "hash" while the old one will remain as "digest". This patch also changes sg_next to allow chaining. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto')
-rw-r--r--crypto/Kconfig4
-rw-r--r--crypto/Makefile3
-rw-r--r--crypto/digest.c129
-rw-r--r--crypto/hash.c61
-rw-r--r--crypto/hmac.c12
-rw-r--r--crypto/scatterwalk.h4
6 files changed, 177 insertions, 36 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig
index be5eb0cb7c3..69c5f992bcd 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -20,6 +20,10 @@ config CRYPTO_BLKCIPHER
tristate
select CRYPTO_ALGAPI
+config CRYPTO_HASH
+ tristate
+ select CRYPTO_ALGAPI
+
config CRYPTO_MANAGER
tristate "Cryptographic algorithm manager"
select CRYPTO_ALGAPI
diff --git a/crypto/Makefile b/crypto/Makefile
index 5e1ff4e0b1f..72366208e29 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -10,6 +10,9 @@ obj-$(CONFIG_CRYPTO_ALGAPI) += crypto_algapi.o
obj-$(CONFIG_CRYPTO_BLKCIPHER) += blkcipher.o
+crypto_hash-objs := hash.o
+obj-$(CONFIG_CRYPTO_HASH) += crypto_hash.o
+
obj-$(CONFIG_CRYPTO_MANAGER) += cryptomgr.o
obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
obj-$(CONFIG_CRYPTO_NULL) += crypto_null.o
diff --git a/crypto/digest.c b/crypto/digest.c
index 96244a52884..5873063db84 100644
--- a/crypto/digest.c
+++ b/crypto/digest.c
@@ -11,29 +11,89 @@
* any later version.
*
*/
-#include <linux/crypto.h>
+
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/highmem.h>
-#include <asm/scatterlist.h>
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+
#include "internal.h"
+#include "scatterwalk.h"
-static void init(struct crypto_tfm *tfm)
+void crypto_digest_init(struct crypto_tfm *tfm)
{
- tfm->__crt_alg->cra_digest.dia_init(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);
-static void update(struct crypto_tfm *tfm,
- struct scatterlist *sg, unsigned int nsg)
+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)
+{
+ struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
+
+ tfm->__crt_alg->cra_digest.dia_init(tfm);
+ return 0;
+}
+
+static int update(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);
- for (i = 0; i < nsg; i++) {
+ if (!nbytes)
+ return 0;
+
+ for (;;) {
+ struct page *pg = sg->page;
+ unsigned int offset = sg->offset;
+ unsigned int l = sg->length;
- struct page *pg = sg[i].page;
- unsigned int offset = sg[i].offset;
- unsigned int l = sg[i].length;
+ if (unlikely(l > nbytes))
+ l = nbytes;
+ nbytes -= l;
do {
unsigned int bytes_from_page = min(l, ((unsigned int)
@@ -55,16 +115,23 @@ static void update(struct crypto_tfm *tfm,
tfm->__crt_alg->cra_digest.dia_update(tfm, p,
bytes_from_page);
crypto_kunmap(src, 0);
- crypto_yield(tfm->crt_flags);
+ crypto_yield(desc->flags);
offset = 0;
pg++;
l -= bytes_from_page;
} while (l > 0);
+
+ if (!nbytes)
+ break;
+ sg = sg_next(sg);
}
+
+ return 0;
}
-static void final(struct crypto_tfm *tfm, u8 *out)
+static int final(struct hash_desc *desc, u8 *out)
{
+ struct crypto_tfm *tfm = crypto_hash_tfm(desc->tfm);
unsigned long alignmask = crypto_tfm_alg_alignmask(tfm);
struct digest_alg *digest = &tfm->__crt_alg->cra_digest;
@@ -78,26 +145,30 @@ static void final(struct crypto_tfm *tfm, u8 *out)
memcpy(out, dst, digest->dia_digestsize);
} else
digest->dia_final(tfm, out);
+
+ return 0;
}
-static int nosetkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen)
+static int nosetkey(struct crypto_hash *tfm, const u8 *key, unsigned int keylen)
{
- tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ crypto_hash_clear_flags(tfm, CRYPTO_TFM_RES_MASK);
return -ENOSYS;
}
-static int setkey(struct crypto_tfm *tfm, const u8 *key, unsigned int keylen)
+static int setkey(struct crypto_hash *hash, const u8 *key, unsigned int keylen)
{
- tfm->crt_flags &= ~CRYPTO_TFM_RES_MASK;
+ struct crypto_tfm *tfm = crypto_hash_tfm(hash);
+
+ crypto_hash_clear_flags(hash, CRYPTO_TFM_RES_MASK);
return tfm->__crt_alg->cra_digest.dia_setkey(tfm, key, keylen);
}
-static void digest(struct crypto_tfm *tfm,
- struct scatterlist *sg, unsigned int nsg, u8 *out)
+static int digest(struct hash_desc *desc,
+ struct scatterlist *sg, unsigned int nbytes, u8 *out)
{
- init(tfm);
- update(tfm, sg, nsg);
- final(tfm, out);
+ init(desc);
+ update(desc, sg, nbytes);
+ return final(desc, out);
}
int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags)
@@ -107,14 +178,18 @@ int crypto_init_digest_flags(struct crypto_tfm *tfm, u32 flags)
int crypto_init_digest_ops(struct crypto_tfm *tfm)
{
- struct digest_tfm *ops = &tfm->crt_digest;
+ 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))
+ return -EINVAL;
- ops->dit_init = init;
- ops->dit_update = update;
- ops->dit_final = final;
- ops->dit_digest = digest;
- ops->dit_setkey = dalg->dia_setkey ? setkey : nosetkey;
+ 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 crypto_alloc_hmac_block(tfm);
}
diff --git a/crypto/hash.c b/crypto/hash.c
new file mode 100644
index 00000000000..cdec23d885f
--- /dev/null
+++ b/crypto/hash.c
@@ -0,0 +1,61 @@
+/*
+ * Cryptographic Hash operations.
+ *
+ * Copyright (c) 2006 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * 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/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/seq_file.h>
+
+#include "internal.h"
+
+static unsigned int crypto_hash_ctxsize(struct crypto_alg *alg)
+{
+ return alg->cra_ctxsize;
+}
+
+static int crypto_init_hash_ops(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 = alg->setkey;
+ crt->digestsize = alg->digestsize;
+
+ return 0;
+}
+
+static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
+ __attribute_used__;
+static void crypto_hash_show(struct seq_file *m, struct crypto_alg *alg)
+{
+ seq_printf(m, "type : hash\n");
+ seq_printf(m, "blocksize : %u\n", alg->cra_blocksize);
+ seq_printf(m, "digestsize : %u\n", alg->cra_hash.digestsize);
+}
+
+const struct crypto_type crypto_hash_type = {
+ .ctxsize = crypto_hash_ctxsize,
+ .init = crypto_init_hash_ops,
+#ifdef CONFIG_PROC_FS
+ .show = crypto_hash_show,
+#endif
+};
+EXPORT_SYMBOL_GPL(crypto_hash_type);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Generic cryptographic hash type");
diff --git a/crypto/hmac.c b/crypto/hmac.c
index 46120dee5ad..ecf7b0a95b5 100644
--- a/crypto/hmac.c
+++ b/crypto/hmac.c
@@ -35,9 +35,9 @@ int crypto_alloc_hmac_block(struct crypto_tfm *tfm)
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)
+ tfm->crt_hash.hmac_block = kmalloc(crypto_tfm_alg_blocksize(tfm),
+ GFP_KERNEL);
+ if (tfm->crt_hash.hmac_block == NULL)
ret = -ENOMEM;
return ret;
@@ -46,14 +46,14 @@ int crypto_alloc_hmac_block(struct crypto_tfm *tfm)
void crypto_free_hmac_block(struct crypto_tfm *tfm)
{
- kfree(tfm->crt_digest.dit_hmac_block);
+ kfree(tfm->crt_hash.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;
+ char *ipad = tfm->crt_hash.hmac_block;
if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
hash_key(tfm, key, *keylen);
@@ -83,7 +83,7 @@ void crypto_hmac_final(struct crypto_tfm *tfm, u8 *key,
{
unsigned int i;
struct scatterlist tmp;
- char *opad = tfm->crt_digest.dit_hmac_block;
+ char *opad = tfm->crt_hash.hmac_block;
if (*keylen > crypto_tfm_alg_blocksize(tfm)) {
hash_key(tfm, key, *keylen);
diff --git a/crypto/scatterwalk.h b/crypto/scatterwalk.h
index ace595a2e11..f1592cc2d0f 100644
--- a/crypto/scatterwalk.h
+++ b/crypto/scatterwalk.h
@@ -20,11 +20,9 @@
#include "internal.h"
-/* Define sg_next is an inline routine now in case we want to change
- scatterlist to a linked list later. */
static inline struct scatterlist *sg_next(struct scatterlist *sg)
{
- return sg + 1;
+ return (++sg)->length ? sg : (void *)sg->page;
}
static inline unsigned long scatterwalk_samebuf(struct scatter_walk *walk_in,