// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Asynchronous Cryptographic Hash operations.
*
* This is the implementation of the ahash (asynchronous hash) API. It differs
* from shash (synchronous hash) in that ahash supports asynchronous operations,
* and it hashes data from scatterlists instead of virtually addressed buffers.
*
* The ahash API provides access to both ahash and shash algorithms. The shash
* API only provides access to shash algorithms.
*
* Copyright (c) 2008 Loc Ho <lho@amcc.com>
*/
#include <crypto/scatterwalk.h>
#include <linux/cryptouser.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/string_choices.h>
#include <net/netlink.h>
#include "hash.h"
#define CRYPTO_ALG_TYPE_AHASH_MASK 0x0000000e
struct crypto_hash_walk {
const char *data;
unsigned int offset;
unsigned int flags;
struct page *pg;
unsigned int entrylen;
unsigned int total;
struct scatterlist *sg;
};
static int ahash_def_finup(struct ahash_request *req);
static inline bool crypto_ahash_block_only(struct crypto_ahash *tfm)
{
return crypto_ahash_alg(tfm)->halg.base.cra_flags &
CRYPTO_AHASH_ALG_BLOCK_ONLY;
}
static inline bool crypto_ahash_final_nonzero(struct crypto_ahash *tfm)
{
return crypto_ahash_alg(tfm)->halg.base.cra_flags &
CRYPTO_AHASH_ALG_FINAL_NONZERO;
}
static inline bool crypto_ahash_need_fallback(struct crypto_ahash *tfm)
{
return crypto_ahash_alg(tfm)->halg.base.cra_flags &
CRYPTO_ALG_NEED_FALLBACK;
}
static inline void ahash_op_done(void *data, int err,
int (*finish)(struct ahash_request *, int))
{
struct ahash_request *areq = data;
crypto_completion_t compl;
compl = areq->saved_complete;
data = areq->saved_data;
if (err == -EINPROGRESS)
goto out;
areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP;
err = finish(areq, err);
if (err == -EINPROGRESS || err == -EBUSY)
return;
out:
compl(data, err);
}
static int hash_walk_next(struct crypto_hash_walk *walk)
{
unsigned int offset = walk->offset;
unsigned int nbytes = min(walk->entrylen,
((unsigned int)(PAGE_SIZE)) - offset);
walk->data = kmap_local_page(walk->pg);
walk->data += offset;
walk->entrylen -= nbytes;
return nbytes;
}
static int hash_walk_new_entry(struct crypto_hash_walk *walk)
{
struct scatterlist *sg;
sg = walk->sg;
walk->offset = sg->offset;
walk->pg = nth_page(sg_page(walk->sg), (walk->offset >> PAGE_SHIFT));
walk->offset = offset_in_page(walk->offset);
walk->entrylen = sg->length;
if (walk->entrylen > walk->total)
walk->entrylen = walk->total;
walk->total -= walk->entrylen;
return hash_walk_next(walk);
}
static int crypto_hash_walk_first(struct ahash_request *req,
struct crypto_hash_walk *walk)
{
walk->total = req->nbytes;
walk->entrylen = 0;
if (!walk->total)
return 0;
walk->flags = req->base.flags;
if (ahash_request_isvirt(req)) {