Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024// 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
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 = 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);}
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)) { walk->data = req->svirt; walk->total = 0; return req->nbytes; }
walk->sg = req->src;
return hash_walk_new_entry(walk);}EXPORT_SYMBOL_GPL(crypto_hash_walk_first);
int crypto_hash_walk_done(struct crypto_hash_walk *walk, int err){ if ((walk->flags & CRYPTO_AHASH_REQ_VIRT)) return err;
walk->data -= walk->offset;
kunmap_local(walk->data); crypto_yield(walk->flags);
if (err) return err;
if (walk->entrylen) { walk->offset = 0; walk->pg++; return hash_walk_next(walk); }
if (!walk->total) return 0;
walk->sg = sg_next(walk->sg);
return hash_walk_new_entry(walk);}EXPORT_SYMBOL_GPL(crypto_hash_walk_done);
/* * For an ahash tfm that is using an shash algorithm (instead of an ahash * algorithm), this returns the underlying shash tfm. */static inline struct crypto_shash *ahash_to_shash(struct crypto_ahash *tfm){ return *(struct crypto_shash **)crypto_ahash_ctx(tfm);}
static inline struct shash_desc *prepare_shash_desc(struct ahash_request *req, struct crypto_ahash *tfm){ struct shash_desc *desc = ahash_request_ctx(req);
desc->tfm = ahash_to_shash(tfm); return desc;}
int shash_ahash_update(struct ahash_request *req, struct shash_desc *desc){ struct crypto_hash_walk walk; int nbytes;
for (nbytes = crypto_hash_walk_first(req, &walk); nbytes > 0; nbytes = crypto_hash_walk_done(&walk, nbytes)) nbytes = crypto_shash_update(desc, walk.data, nbytes);
return nbytes;}EXPORT_SYMBOL_GPL(shash_ahash_update);
int shash_ahash_finup(struct ahash_request *req, struct shash_desc *desc){ struct crypto_hash_walk walk; int nbytes;
nbytes = crypto_hash_walk_first(req, &walk); if (!nbytes) return crypto_shash_final(desc, req->result);
do { nbytes = crypto_hash_walk_last(&walk) ? crypto_shash_finup(desc, walk.data, nbytes, req->result) : crypto_shash_update(desc, walk.data, nbytes); nbytes = crypto_hash_walk_done(&walk, nbytes); } while (nbytes > 0);
return nbytes;}EXPORT_SYMBOL_GPL(shash_ahash_finup);
int shash_ahash_digest(struct ahash_request *req, struct shash_desc *desc){ unsigned int nbytes = req->nbytes; struct scatterlist *sg; unsigned int offset; struct page *page; const u8 *data; int err;
data = req->svirt; if (!nbytes || ahash_request_isvirt(req)) return crypto_shash_digest(desc, data, nbytes, req->result);
sg = req->src; if (nbytes > sg->length) return crypto_shash_init(desc) ?: shash_ahash_finup(req, desc);
page = sg_page(sg); offset = sg->offset; data = lowmem_page_address(page) + offset; if (!IS_ENABLED(CONFIG_HIGHMEM)) return crypto_shash_digest(desc, data, nbytes, req->result);
page += offset >> PAGE_SHIFT; offset = offset_in_page(offset);
if (nbytes > (unsigned int)PAGE_SIZE - offset) return crypto_shash_init(desc) ?: shash_ahash_finup(req, desc);
data = kmap_local_page(page); err = crypto_shash_digest(desc, data + offset, nbytes, req->result); kunmap_local(data); return err;}EXPORT_SYMBOL_GPL(shash_ahash_digest);
static void crypto_exit_ahash_using_shash(struct crypto_tfm *tfm){ struct crypto_shash **ctx = crypto_tfm_ctx(tfm);
crypto_free_shash(*ctx);}
static int crypto_init_ahash_using_shash(struct crypto_tfm *tfm){ struct crypto_alg *calg = tfm->__crt_alg; struct crypto_ahash *crt = __crypto_ahash_cast(tfm); struct crypto_shash **ctx = crypto_tfm_ctx(tfm); struct crypto_shash *shash;
if (!crypto_mod_get(calg)) return -EAGAIN;
shash = crypto_create_tfm(calg, &crypto_shash_type); if (IS_ERR(shash)) { crypto_mod_put(calg); return PTR_ERR(shash); }
crt->using_shash = true; *ctx = shash; tfm->exit = crypto_exit_ahash_using_shash;
crypto_ahash_set_flags(crt, crypto_shash_get_flags(shash) & CRYPTO_TFM_NEED_KEY);
return 0;}
static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen){ return -ENOSYS;}
static void ahash_set_needkey(struct crypto_ahash *tfm, struct ahash_alg *alg){ if (alg->setkey != ahash_nosetkey && !(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY)) crypto_ahash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);}
int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen){ if (likely(tfm->using_shash)) { struct crypto_shash *shash = ahash_to_shash(tfm); int err;
err = crypto_shash_setkey(shash, key, keylen); if (unlikely(err)) { crypto_ahash_set_flags(tfm, crypto_shash_get_flags(shash) & CRYPTO_TFM_NEED_KEY); return err; } } else { struct ahash_alg *alg = crypto_ahash_alg(tfm); int err;
err = alg->setkey(tfm, key, keylen); if (!err && crypto_ahash_need_fallback(tfm)) err = crypto_ahash_setkey(crypto_ahash_fb(tfm), key, keylen); if (unlikely(err)) { ahash_set_needkey(tfm, alg); return err; } } crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY); return 0;}EXPORT_SYMBOL_GPL(crypto_ahash_setkey);
static int ahash_do_req_chain(struct ahash_request *req, int (*const *op)(struct ahash_request *req)){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); int err;
if (crypto_ahash_req_virt(tfm) || !ahash_request_isvirt(req)) return (*op)(req);
if (crypto_ahash_statesize(tfm) > HASH_MAX_STATESIZE) return -ENOSYS;
if (!crypto_ahash_need_fallback(tfm)) return -ENOSYS;
if (crypto_hash_no_export_core(tfm)) return -ENOSYS;
{ u8 state[HASH_MAX_STATESIZE];
if (op == &crypto_ahash_alg(tfm)->digest) { ahash_request_set_tfm(req, crypto_ahash_fb(tfm)); err = crypto_ahash_digest(req); goto out_no_state; }
err = crypto_ahash_export(req, state); ahash_request_set_tfm(req, crypto_ahash_fb(tfm)); err = err ?: crypto_ahash_import(req, state);
if (op == &crypto_ahash_alg(tfm)->finup) { err = err ?: crypto_ahash_finup(req); goto out_no_state; }
err = err ?: crypto_ahash_update(req) ?: crypto_ahash_export(req, state);
ahash_request_set_tfm(req, tfm); return err ?: crypto_ahash_import(req, state);
out_no_state: ahash_request_set_tfm(req, tfm); return err; }}
int crypto_ahash_init(struct ahash_request *req){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return crypto_shash_init(prepare_shash_desc(req, tfm)); if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) return -ENOKEY; if (ahash_req_on_stack(req) && ahash_is_async(tfm)) return -EAGAIN; if (crypto_ahash_block_only(tfm)) { u8 *buf = ahash_request_ctx(req);
buf += crypto_ahash_reqsize(tfm) - 1; *buf = 0; } return crypto_ahash_alg(tfm)->init(req);}EXPORT_SYMBOL_GPL(crypto_ahash_init);
static void ahash_save_req(struct ahash_request *req, crypto_completion_t cplt){ req->saved_complete = req->base.complete; req->saved_data = req->base.data; req->base.complete = cplt; req->base.data = req;}
static void ahash_restore_req(struct ahash_request *req){ req->base.complete = req->saved_complete; req->base.data = req->saved_data;}
static int ahash_update_finish(struct ahash_request *req, int err){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); bool nonzero = crypto_ahash_final_nonzero(tfm); int bs = crypto_ahash_blocksize(tfm); u8 *blenp = ahash_request_ctx(req); int blen; u8 *buf;
blenp += crypto_ahash_reqsize(tfm) - 1; blen = *blenp; buf = blenp - bs;
if (blen) { req->src = req->sg_head + 1; if (sg_is_chain(req->src)) req->src = sg_chain_ptr(req->src); }
req->nbytes += nonzero - blen;
blen = 0; if (err >= 0) { blen = err + nonzero; err = 0; } if (ahash_request_isvirt(req)) memcpy(buf, req->svirt + req->nbytes - blen, blen); else memcpy_from_sglist(buf, req->src, req->nbytes - blen, blen); *blenp = blen;
ahash_restore_req(req);
return err;}
static void ahash_update_done(void *data, int err){ ahash_op_done(data, err, ahash_update_finish);}
int crypto_ahash_update(struct ahash_request *req){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); bool nonzero = crypto_ahash_final_nonzero(tfm); int bs = crypto_ahash_blocksize(tfm); u8 *blenp = ahash_request_ctx(req); int blen, err; u8 *buf;
if (likely(tfm->using_shash)) return shash_ahash_update(req, ahash_request_ctx(req)); if (ahash_req_on_stack(req) && ahash_is_async(tfm)) return -EAGAIN; if (!crypto_ahash_block_only(tfm)) return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->update);
blenp += crypto_ahash_reqsize(tfm) - 1; blen = *blenp; buf = blenp - bs;
if (blen + req->nbytes < bs + nonzero) { if (ahash_request_isvirt(req)) memcpy(buf + blen, req->svirt, req->nbytes); else memcpy_from_sglist(buf + blen, req->src, 0, req->nbytes);
*blenp += req->nbytes; return 0; }
if (blen) { memset(req->sg_head, 0, sizeof(req->sg_head[0])); sg_set_buf(req->sg_head, buf, blen); if (req->src != req->sg_head + 1) sg_chain(req->sg_head, 2, req->src); req->src = req->sg_head; req->nbytes += blen; } req->nbytes -= nonzero;
ahash_save_req(req, ahash_update_done);
err = ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->update); if (err == -EINPROGRESS || err == -EBUSY) return err;
return ahash_update_finish(req, err);}EXPORT_SYMBOL_GPL(crypto_ahash_update);
static int ahash_finup_finish(struct ahash_request *req, int err){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); u8 *blenp = ahash_request_ctx(req); int blen;
blenp += crypto_ahash_reqsize(tfm) - 1; blen = *blenp;
if (blen) { if (sg_is_last(req->src)) req->src = NULL; else { req->src = req->sg_head + 1; if (sg_is_chain(req->src)) req->src = sg_chain_ptr(req->src); } req->nbytes -= blen; }
ahash_restore_req(req);
return err;}
static void ahash_finup_done(void *data, int err){ ahash_op_done(data, err, ahash_finup_finish);}
int crypto_ahash_finup(struct ahash_request *req){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); int bs = crypto_ahash_blocksize(tfm); u8 *blenp = ahash_request_ctx(req); int blen, err; u8 *buf;
if (likely(tfm->using_shash)) return shash_ahash_finup(req, ahash_request_ctx(req)); if (ahash_req_on_stack(req) && ahash_is_async(tfm)) return -EAGAIN; if (!crypto_ahash_alg(tfm)->finup) return ahash_def_finup(req); if (!crypto_ahash_block_only(tfm)) return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->finup);
blenp += crypto_ahash_reqsize(tfm) - 1; blen = *blenp; buf = blenp - bs;
if (blen) { memset(req->sg_head, 0, sizeof(req->sg_head[0])); sg_set_buf(req->sg_head, buf, blen); if (!req->src) sg_mark_end(req->sg_head); else if (req->src != req->sg_head + 1) sg_chain(req->sg_head, 2, req->src); req->src = req->sg_head; req->nbytes += blen; }
ahash_save_req(req, ahash_finup_done);
err = ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->finup); if (err == -EINPROGRESS || err == -EBUSY) return err;
return ahash_finup_finish(req, err);}EXPORT_SYMBOL_GPL(crypto_ahash_finup);
int crypto_ahash_digest(struct ahash_request *req){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return shash_ahash_digest(req, prepare_shash_desc(req, tfm)); if (ahash_req_on_stack(req) && ahash_is_async(tfm)) return -EAGAIN; if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) return -ENOKEY; return ahash_do_req_chain(req, &crypto_ahash_alg(tfm)->digest);}EXPORT_SYMBOL_GPL(crypto_ahash_digest);
static void ahash_def_finup_done2(void *data, int err){ struct ahash_request *areq = data;
if (err == -EINPROGRESS) return;
ahash_restore_req(areq); ahash_request_complete(areq, err);}
static int ahash_def_finup_finish1(struct ahash_request *req, int err){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (err) goto out;
req->base.complete = ahash_def_finup_done2;
err = crypto_ahash_alg(tfm)->final(req); if (err == -EINPROGRESS || err == -EBUSY) return err;
out: ahash_restore_req(req); return err;}
static void ahash_def_finup_done1(void *data, int err){ ahash_op_done(data, err, ahash_def_finup_finish1);}
static int ahash_def_finup(struct ahash_request *req){ int err;
ahash_save_req(req, ahash_def_finup_done1);
err = crypto_ahash_update(req); if (err == -EINPROGRESS || err == -EBUSY) return err;
return ahash_def_finup_finish1(req, err);}
int crypto_ahash_export_core(struct ahash_request *req, void *out){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return crypto_shash_export_core(ahash_request_ctx(req), out); return crypto_ahash_alg(tfm)->export_core(req, out);}EXPORT_SYMBOL_GPL(crypto_ahash_export_core);
int crypto_ahash_export(struct ahash_request *req, void *out){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return crypto_shash_export(ahash_request_ctx(req), out); if (crypto_ahash_block_only(tfm)) { unsigned int plen = crypto_ahash_blocksize(tfm) + 1; unsigned int reqsize = crypto_ahash_reqsize(tfm); unsigned int ss = crypto_ahash_statesize(tfm); u8 *buf = ahash_request_ctx(req);
memcpy(out + ss - plen, buf + reqsize - plen, plen); } return crypto_ahash_alg(tfm)->export(req, out);}EXPORT_SYMBOL_GPL(crypto_ahash_export);
int crypto_ahash_import_core(struct ahash_request *req, const void *in){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return crypto_shash_import_core(prepare_shash_desc(req, tfm), in); if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) return -ENOKEY; if (crypto_ahash_block_only(tfm)) { unsigned int reqsize = crypto_ahash_reqsize(tfm); u8 *buf = ahash_request_ctx(req);
buf[reqsize - 1] = 0; } return crypto_ahash_alg(tfm)->import_core(req, in);}EXPORT_SYMBOL_GPL(crypto_ahash_import_core);
int crypto_ahash_import(struct ahash_request *req, const void *in){ struct crypto_ahash *tfm = crypto_ahash_reqtfm(req);
if (likely(tfm->using_shash)) return crypto_shash_import(prepare_shash_desc(req, tfm), in); if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) return -ENOKEY; if (crypto_ahash_block_only(tfm)) { unsigned int plen = crypto_ahash_blocksize(tfm) + 1; unsigned int reqsize = crypto_ahash_reqsize(tfm); unsigned int ss = crypto_ahash_statesize(tfm); u8 *buf = ahash_request_ctx(req);
memcpy(buf + reqsize - plen, in + ss - plen, plen); if (buf[reqsize - 1] >= plen) return -EOVERFLOW; } return crypto_ahash_alg(tfm)->import(req, in);}EXPORT_SYMBOL_GPL(crypto_ahash_import);
static void crypto_ahash_exit_tfm(struct crypto_tfm *tfm){ struct crypto_ahash *hash = __crypto_ahash_cast(tfm); struct ahash_alg *alg = crypto_ahash_alg(hash);
if (alg->exit_tfm) alg->exit_tfm(hash); else if (tfm->__crt_alg->cra_exit) tfm->__crt_alg->cra_exit(tfm);
if (crypto_ahash_need_fallback(hash)) crypto_free_ahash(crypto_ahash_fb(hash));}
static int crypto_ahash_init_tfm(struct crypto_tfm *tfm){ struct crypto_ahash *hash = __crypto_ahash_cast(tfm); struct ahash_alg *alg = crypto_ahash_alg(hash); struct crypto_ahash *fb = NULL; int err;
crypto_ahash_set_statesize(hash, alg->halg.statesize); crypto_ahash_set_reqsize(hash, crypto_tfm_alg_reqsize(tfm));
if (tfm->__crt_alg->cra_type == &crypto_shash_type) return crypto_init_ahash_using_shash(tfm);
if (crypto_ahash_need_fallback(hash)) { fb = crypto_alloc_ahash(crypto_ahash_alg_name(hash), CRYPTO_ALG_REQ_VIRT, CRYPTO_ALG_ASYNC | CRYPTO_ALG_REQ_VIRT | CRYPTO_AHASH_ALG_NO_EXPORT_CORE); if (IS_ERR(fb)) return PTR_ERR(fb);
tfm->fb = crypto_ahash_tfm(fb); }
ahash_set_needkey(hash, alg);
tfm->exit = crypto_ahash_exit_tfm;
if (alg->init_tfm) err = alg->init_tfm(hash); else if (tfm->__crt_alg->cra_init) err = tfm->__crt_alg->cra_init(tfm); else return 0;
if (err) goto out_free_sync_hash;
if (!ahash_is_async(hash) && crypto_ahash_reqsize(hash) > MAX_SYNC_HASH_REQSIZE) goto out_exit_tfm;
BUILD_BUG_ON(HASH_MAX_DESCSIZE > MAX_SYNC_HASH_REQSIZE); if (crypto_ahash_reqsize(hash) < HASH_MAX_DESCSIZE) crypto_ahash_set_reqsize(hash, HASH_MAX_DESCSIZE);
return 0;
out_exit_tfm: if (alg->exit_tfm) alg->exit_tfm(hash); else if (tfm->__crt_alg->cra_exit) tfm->__crt_alg->cra_exit(tfm); err = -EINVAL;out_free_sync_hash: crypto_free_ahash(fb); return err;}
static unsigned int crypto_ahash_extsize(struct crypto_alg *alg){ if (alg->cra_type == &crypto_shash_type) return sizeof(struct crypto_shash *);
return crypto_alg_extsize(alg);}
static void crypto_ahash_free_instance(struct crypto_instance *inst){ struct ahash_instance *ahash = ahash_instance(inst);
ahash->free(ahash);}
static int __maybe_unused crypto_ahash_report( struct sk_buff *skb, struct crypto_alg *alg){ struct crypto_report_hash rhash = { .type = "ahash", };
rhash.blocksize = alg->cra_blocksize; rhash.digestsize = __crypto_hash_alg_common(alg)->digestsize;
return nla_put(skb, CRYPTOCFGA_REPORT_HASH, sizeof(rhash), &rhash);}
static void __maybe_unused crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg){ seq_printf(m, "type : ahash\n"); seq_printf(m, "async : %s\n", str_yes_no(alg->cra_flags & CRYPTO_ALG_ASYNC)); seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); seq_printf(m, "digestsize : %u\n", __crypto_hash_alg_common(alg)->digestsize);}
static const struct crypto_type crypto_ahash_type = { .extsize = crypto_ahash_extsize, .init_tfm = crypto_ahash_init_tfm, .free = crypto_ahash_free_instance,#ifdef CONFIG_PROC_FS .show = crypto_ahash_show,#endif#if IS_ENABLED(CONFIG_CRYPTO_USER) .report = crypto_ahash_report,#endif .maskclear = ~CRYPTO_ALG_TYPE_MASK, .maskset = CRYPTO_ALG_TYPE_AHASH_MASK, .type = CRYPTO_ALG_TYPE_AHASH, .tfmsize = offsetof(struct crypto_ahash, base), .algsize = offsetof(struct ahash_alg, halg.base),};
int crypto_grab_ahash(struct crypto_ahash_spawn *spawn, struct crypto_instance *inst, const char *name, u32 type, u32 mask){ spawn->base.frontend = &crypto_ahash_type; return crypto_grab_spawn(&spawn->base, inst, name, type, mask);}EXPORT_SYMBOL_GPL(crypto_grab_ahash);
struct crypto_ahash *crypto_alloc_ahash(const char *alg_name, u32 type, u32 mask){ return crypto_alloc_tfm(alg_name, &crypto_ahash_type, type, mask);}EXPORT_SYMBOL_GPL(crypto_alloc_ahash);
int crypto_has_ahash(const char *alg_name, u32 type, u32 mask){ return crypto_type_has_alg(alg_name, &crypto_ahash_type, type, mask);}EXPORT_SYMBOL_GPL(crypto_has_ahash);
bool crypto_hash_alg_has_setkey(struct hash_alg_common *halg){ struct crypto_alg *alg = &halg->base;
if (alg->cra_type == &crypto_shash_type) return crypto_shash_alg_has_setkey(__crypto_shash_alg(alg));
return __crypto_ahash_alg(alg)->setkey != ahash_nosetkey;}EXPORT_SYMBOL_GPL(crypto_hash_alg_has_setkey);
static int ahash_default_export_core(struct ahash_request *req, void *out){ return -ENOSYS;}
static int ahash_default_import_core(struct ahash_request *req, const void *in){ return -ENOSYS;}
static int ahash_prepare_alg(struct ahash_alg *alg){ struct crypto_alg *base = &alg->halg.base; int err;
if (alg->halg.statesize == 0) return -EINVAL;
if (base->cra_reqsize && base->cra_reqsize < alg->halg.statesize) return -EINVAL;
if (!(base->cra_flags & CRYPTO_ALG_ASYNC) && base->cra_reqsize > MAX_SYNC_HASH_REQSIZE) return -EINVAL;
if (base->cra_flags & CRYPTO_ALG_NEED_FALLBACK && base->cra_flags & CRYPTO_ALG_NO_FALLBACK) return -EINVAL;
err = hash_prepare_alg(&alg->halg); if (err) return err;
base->cra_type = &crypto_ahash_type; base->cra_flags |= CRYPTO_ALG_TYPE_AHASH;
if ((base->cra_flags ^ CRYPTO_ALG_REQ_VIRT) & (CRYPTO_ALG_ASYNC | CRYPTO_ALG_REQ_VIRT) && !(base->cra_flags & CRYPTO_ALG_NO_FALLBACK)) base->cra_flags |= CRYPTO_ALG_NEED_FALLBACK;
if (!alg->setkey) alg->setkey = ahash_nosetkey;
if (base->cra_flags & CRYPTO_AHASH_ALG_BLOCK_ONLY) { BUILD_BUG_ON(MAX_ALGAPI_BLOCKSIZE >= 256); if (!alg->finup) return -EINVAL;
base->cra_reqsize += base->cra_blocksize + 1; alg->halg.statesize += base->cra_blocksize + 1; alg->export_core = alg->export; alg->import_core = alg->import; } else if (!alg->export_core || !alg->import_core) { alg->export_core = ahash_default_export_core; alg->import_core = ahash_default_import_core; base->cra_flags |= CRYPTO_AHASH_ALG_NO_EXPORT_CORE; }
return 0;}
int crypto_register_ahash(struct ahash_alg *alg){ struct crypto_alg *base = &alg->halg.base; int err;
err = ahash_prepare_alg(alg); if (err) return err;
return crypto_register_alg(base);}EXPORT_SYMBOL_GPL(crypto_register_ahash);
void crypto_unregister_ahash(struct ahash_alg *alg){ crypto_unregister_alg(&alg->halg.base);}EXPORT_SYMBOL_GPL(crypto_unregister_ahash);
int crypto_register_ahashes(struct ahash_alg *algs, int count){ int i, ret;
for (i = 0; i < count; i++) { ret = crypto_register_ahash(&algs[i]); if (ret) { crypto_unregister_ahashes(algs, i); return ret; } }
return 0;}EXPORT_SYMBOL_GPL(crypto_register_ahashes);
void crypto_unregister_ahashes(struct ahash_alg *algs, int count){ int i;
for (i = count - 1; i >= 0; --i) crypto_unregister_ahash(&algs[i]);}EXPORT_SYMBOL_GPL(crypto_unregister_ahashes);
int ahash_register_instance(struct crypto_template *tmpl, struct ahash_instance *inst){ int err;
if (WARN_ON(!inst->free)) return -EINVAL;
err = ahash_prepare_alg(&inst->alg); if (err) return err;
return crypto_register_instance(tmpl, ahash_crypto_instance(inst));}EXPORT_SYMBOL_GPL(ahash_register_instance);
void ahash_request_free(struct ahash_request *req){ if (unlikely(!req)) return;
if (!ahash_req_on_stack(req)) { kfree(req); return; }
ahash_request_zero(req);}EXPORT_SYMBOL_GPL(ahash_request_free);
int crypto_hash_digest(struct crypto_ahash *tfm, const u8 *data, unsigned int len, u8 *out){ HASH_REQUEST_ON_STACK(req, crypto_ahash_fb(tfm)); int err;
ahash_request_set_callback(req, 0, NULL, NULL); ahash_request_set_virt(req, data, out, len); err = crypto_ahash_digest(req);
ahash_request_zero(req);
return err;}EXPORT_SYMBOL_GPL(crypto_hash_digest);
void ahash_free_singlespawn_instance(struct ahash_instance *inst){ crypto_drop_spawn(ahash_instance_ctx(inst)); kfree(inst);}EXPORT_SYMBOL_GPL(ahash_free_singlespawn_instance);
MODULE_LICENSE("GPL");MODULE_DESCRIPTION("Asynchronous cryptographic hash type");