diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-07-07 22:19:53 +0800 |
---|---|---|
committer | Herbert Xu <herbert@gondor.apana.org.au> | 2008-07-10 20:35:18 +0800 |
commit | 20036252fc61c624a49770fb89684ea5cfdfa05e (patch) | |
tree | 29db0f146bab13b399d0df9e80041fc35a64fc91 /crypto | |
parent | b8454eebe380677789735fd6bad368af2e6b2d1e (diff) | |
download | lwn-20036252fc61c624a49770fb89684ea5cfdfa05e.tar.gz lwn-20036252fc61c624a49770fb89684ea5cfdfa05e.zip |
crypto: hash - Added scatter list walking helper
This patch adds the walking helpers for hash algorithms akin to
those of block ciphers. This is a necessary step before we can
reimplement existing hash algorithms using the new ahash interface.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/ahash.c | 91 |
1 files changed, 90 insertions, 1 deletions
diff --git a/crypto/ahash.c b/crypto/ahash.c index e6e5906ca80a..27128f2c687a 100644 --- a/crypto/ahash.c +++ b/crypto/ahash.c @@ -13,7 +13,8 @@ * */ -#include <crypto/algapi.h> +#include <crypto/internal/hash.h> +#include <crypto/scatterwalk.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> @@ -23,6 +24,94 @@ #include "internal.h" +static int hash_walk_next(struct crypto_hash_walk *walk) +{ + unsigned int alignmask = walk->alignmask; + unsigned int offset = walk->offset; + unsigned int nbytes = min(walk->entrylen, + ((unsigned int)(PAGE_SIZE)) - offset); + + walk->data = crypto_kmap(walk->pg, 0); + walk->data += offset; + + if (offset & alignmask) + nbytes = alignmask + 1 - (offset & alignmask); + + walk->entrylen -= nbytes; + return nbytes; +} + +static int hash_walk_new_entry(struct crypto_hash_walk *walk) +{ + struct scatterlist *sg; + + sg = walk->sg; + walk->pg = sg_page(sg); + walk->offset = sg->offset; + walk->entrylen = sg->length; + + if (walk->entrylen > walk->total) + walk->entrylen = walk->total; + walk->total -= walk->entrylen; + + return hash_walk_next(walk); +} + +int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err) +{ + unsigned int alignmask = walk->alignmask; + unsigned int nbytes = walk->entrylen; + + walk->data -= walk->offset; + + if (nbytes && walk->offset & alignmask && !err) { + walk->offset += alignmask - 1; + walk->offset = ALIGN(walk->offset, alignmask + 1); + walk->data += walk->offset; + + nbytes = min(nbytes, + ((unsigned int)(PAGE_SIZE)) - walk->offset); + walk->entrylen -= nbytes; + + return nbytes; + } + + crypto_kunmap(walk->data, 0); + crypto_yield(walk->flags); + + if (err) + return err; + + walk->offset = 0; + + if (nbytes) + return hash_walk_next(walk); + + if (!walk->total) + return 0; + + walk->sg = scatterwalk_sg_next(walk->sg); + + return hash_walk_new_entry(walk); +} +EXPORT_SYMBOL_GPL(crypto_hash_walk_done); + +int crypto_hash_walk_first(struct ahash_request *req, + struct crypto_hash_walk *walk) +{ + walk->total = req->nbytes; + + if (!walk->total) + return 0; + + walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req)); + walk->sg = req->src; + walk->flags = req->base.flags; + + return hash_walk_new_entry(walk); +} +EXPORT_SYMBOL_GPL(crypto_hash_walk_first); + static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { |