cregit-Linux how code gets into the kernel

Release 4.16 crypto/ahash.c

Directory: crypto
/*
 * Asynchronous Cryptographic Hash operations.
 *
 * This is the asynchronous version of hash.c with notification of
 * completion via a callback.
 *
 * Copyright (c) 2008 Loc Ho <lho@amcc.com>
 *
 * 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 <crypto/internal/hash.h>
#include <crypto/scatterwalk.h>
#include <linux/bug.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/cryptouser.h>
#include <linux/compiler.h>
#include <net/netlink.h>

#include "internal.h"


struct ahash_request_priv {
	
crypto_completion_t complete;
	
void *data;
	
u8 *result;
	
u32 flags;
	
void *ubuf[] CRYPTO_MINALIGN_ATTR;
};


static inline struct ahash_alg *crypto_ahash_alg(struct crypto_ahash *hash) { return container_of(crypto_hash_alg_common(hash), struct ahash_alg, halg); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu28100.00%1100.00%
Total28100.00%1100.00%


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); if (walk->flags & CRYPTO_ALG_ASYNC) walk->data = kmap(walk->pg); else walk->data = kmap_atomic(walk->pg); walk->data += offset; if (offset & alignmask) { unsigned int unaligned = alignmask + 1 - (offset & alignmask); if (nbytes > unaligned) nbytes = unaligned; } walk->entrylen -= nbytes; return nbytes; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu11186.72%250.00%
Szilveszter Ördög1612.50%125.00%
Américo Wang10.78%125.00%
Total128100.00%4100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu100100.00%2100.00%
Total100100.00%2100.00%


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 = ALIGN(walk->offset, alignmask + 1); walk->data += walk->offset; nbytes = min(nbytes, ((unsigned int)(PAGE_SIZE)) - walk->offset); walk->entrylen -= nbytes; return nbytes; } if (walk->flags & CRYPTO_ALG_ASYNC) kunmap(walk->pg); else { kunmap_atomic(walk->data); /* * The may sleep test only makes sense for sync users. * Async users don't need to sleep here anyway. */ crypto_yield(walk->flags); } if (err) return err; if (nbytes) { 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); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu19398.97%360.00%
Américo Wang10.51%120.00%
Cristian Stoica10.51%120.00%
Total195100.00%5100.00%

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) { walk->entrylen = 0; return 0; } walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req)); walk->sg = req->src; walk->flags = req->base.flags & CRYPTO_TFM_REQ_MASK; return hash_walk_new_entry(walk); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu7189.87%266.67%
Tim Chen810.13%133.33%
Total79100.00%3100.00%

EXPORT_SYMBOL_GPL(crypto_hash_walk_first);
int crypto_ahash_walk_first(struct ahash_request *req, struct crypto_hash_walk *walk) { walk->total = req->nbytes; if (!walk->total) { walk->entrylen = 0; return 0; } walk->alignmask = crypto_ahash_alignmask(crypto_ahash_reqtfm(req)); walk->sg = req->src; walk->flags = req->base.flags & CRYPTO_TFM_REQ_MASK; walk->flags |= CRYPTO_ALG_ASYNC; BUILD_BUG_ON(CRYPTO_TFM_REQ_MASK & CRYPTO_ALG_ASYNC); return hash_walk_new_entry(walk); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu8491.30%150.00%
Tim Chen88.70%150.00%
Total92100.00%2100.00%

EXPORT_SYMBOL_GPL(crypto_ahash_walk_first);
static int ahash_setkey_unaligned(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { unsigned long alignmask = crypto_ahash_alignmask(tfm); int ret; u8 *buffer, *alignbuffer; unsigned long absize; absize = keylen + alignmask; buffer = kmalloc(absize, GFP_KERNEL); if (!buffer) return -ENOMEM; alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1); memcpy(alignbuffer, key, keylen); ret = tfm->setkey(tfm, alignbuffer, keylen); kzfree(buffer); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Loc Ho11397.41%125.00%
Herbert Xu32.59%375.00%
Total116100.00%4100.00%


int crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { unsigned long alignmask = crypto_ahash_alignmask(tfm); int err; if ((unsigned long)key & alignmask) err = ahash_setkey_unaligned(tfm, key, keylen); else err = tfm->setkey(tfm, key, keylen); if (err) return err; crypto_ahash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Loc Ho5566.27%125.00%
Eric Biggers2631.33%125.00%
Herbert Xu22.41%250.00%
Total83100.00%4100.00%

EXPORT_SYMBOL_GPL(crypto_ahash_setkey);
static int ahash_nosetkey(struct crypto_ahash *tfm, const u8 *key, unsigned int keylen) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu24100.00%1100.00%
Total24100.00%1100.00%


static inline unsigned int ahash_align_buffer_size(unsigned len, unsigned long mask) { return len + (mask & ~(crypto_tfm_ctx_alignment() - 1)); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu2893.33%150.00%
Loc Ho26.67%150.00%
Total30100.00%2100.00%


static int ahash_save_req(struct ahash_request *req, crypto_completion_t cplt) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); unsigned long alignmask = crypto_ahash_alignmask(tfm); unsigned int ds = crypto_ahash_digestsize(tfm); struct ahash_request_priv *priv; priv = kmalloc(sizeof(*priv) + ahash_align_buffer_size(ds, alignmask), (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ? GFP_KERNEL : GFP_ATOMIC); if (!priv) return -ENOMEM; /* * WARNING: Voodoo programming below! * * The code below is obscure and hard to understand, thus explanation * is necessary. See include/crypto/hash.h and include/linux/crypto.h * to understand the layout of structures used here! * * The code here will replace portions of the ORIGINAL request with * pointers to new code and buffers so the hashing operation can store * the result in aligned buffer. We will call the modified request * an ADJUSTED request. * * The newly mangled request will look as such: * * req { * .result = ADJUSTED[new aligned buffer] * .base.complete = ADJUSTED[pointer to completion function] * .base.data = ADJUSTED[*req (pointer to self)] * .priv = ADJUSTED[new priv] { * .result = ORIGINAL(result) * .complete = ORIGINAL(base.complete) * .data = ORIGINAL(base.data) * } */ priv->result = req->result; priv->complete = req->base.complete; priv->data = req->base.data; priv->flags = req->base.flags; /* * WARNING: We do not backup req->priv here! The req->priv * is for internal use of the Crypto API and the * user must _NOT_ _EVER_ depend on it's content! */ req->result = PTR_ALIGN((u8 *)priv->ubuf, alignmask + 1); req->base.complete = cplt; req->base.data = req; req->priv = priv; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut13477.91%120.00%
Herbert Xu3218.60%360.00%
Loc Ho63.49%120.00%
Total172100.00%5100.00%


static void ahash_restore_req(struct ahash_request *req, int err) { struct ahash_request_priv *priv = req->priv; if (!err) memcpy(priv->result, req->result, crypto_ahash_digestsize(crypto_ahash_reqtfm(req))); /* Restore the original crypto request. */ req->result = priv->result; ahash_request_set_callback(req, priv->flags, priv->complete, priv->data); req->priv = NULL; /* Free the req->priv.priv from the ADJUSTED request. */ kzfree(priv); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut4451.76%240.00%
Herbert Xu4148.24%360.00%
Total85100.00%5100.00%


static void ahash_notify_einprogress(struct ahash_request *req) { struct ahash_request_priv *priv = req->priv; struct crypto_async_request oreq; oreq.data = priv->data; priv->complete(&oreq, -EINPROGRESS); }

Contributors

PersonTokensPropCommitsCommitProp
Marek Vašut2558.14%150.00%
Herbert Xu1841.86%150.00%
Total43100.00%2100.00%


static void ahash_op_unaligned_done(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; if (err == -EINPROGRESS) { ahash_notify_einprogress(areq); return; } /* * Restore the original request, see ahash_op_unaligned() for what * goes where. * * The "struct ahash_request *req" here is in fact the "req.base" * from the ADJUSTED request from ahash_op_unaligned(), thus as it * is a pointer to self, it is also the ADJUSTED "req" . */ /* First copy req->result into req->priv.result */ ahash_restore_req(areq, err); /* Complete the ORIGINAL request. */ areq->base.complete(&areq->base, err); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu5080.65%350.00%
Marek Vašut1219.35%350.00%
Total62100.00%6100.00%


static int ahash_op_unaligned(struct ahash_request *req, int (*op)(struct ahash_request *)) { int err; err = ahash_save_req(req, ahash_op_unaligned_done); if (err) return err; err = op(req); if (err == -EINPROGRESS || err == -EBUSY) return err; ahash_restore_req(req, err); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu6589.04%250.00%
Marek Vašut68.22%125.00%
Loc Ho22.74%125.00%
Total73100.00%4100.00%


static int crypto_ahash_op(struct ahash_request *req, int (*op)(struct ahash_request *)) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); unsigned long alignmask = crypto_ahash_alignmask(tfm); if ((unsigned long)req->result & alignmask) return ahash_op_unaligned(req, op); return op(req); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu67100.00%1100.00%
Total67100.00%1100.00%


int crypto_ahash_final(struct ahash_request *req) { return crypto_ahash_op(req, crypto_ahash_reqtfm(req)->final); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu23100.00%1100.00%
Total23100.00%1100.00%

EXPORT_SYMBOL_GPL(crypto_ahash_final);
int crypto_ahash_finup(struct ahash_request *req) { return crypto_ahash_op(req, crypto_ahash_reqtfm(req)->finup); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu23100.00%1100.00%
Total23100.00%1100.00%

EXPORT_SYMBOL_GPL(crypto_ahash_finup);
int crypto_ahash_digest(struct ahash_request *req) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); if (crypto_ahash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY) return -ENOKEY; return crypto_ahash_op(req, tfm->digest); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Biggers2455.81%150.00%
Herbert Xu1944.19%150.00%
Total43100.00%2100.00%

EXPORT_SYMBOL_GPL(crypto_ahash_digest);
static void ahash_def_finup_done2(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; if (err == -EINPROGRESS) return; ahash_restore_req(areq, err); areq->base.complete(&areq->base, err); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu4382.69%266.67%
Marek Vašut917.31%133.33%
Total52100.00%3100.00%


static int ahash_def_finup_finish1(struct ahash_request *req, int err) { if (err) goto out; req->base.complete = ahash_def_finup_done2; err = crypto_ahash_reqtfm(req)->final(req); if (err == -EINPROGRESS || err == -EBUSY) return err; out: ahash_restore_req(req, err); return err; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu68100.00%2100.00%
Total68100.00%2100.00%


static void ahash_def_finup_done1(struct crypto_async_request *req, int err) { struct ahash_request *areq = req->data; if (err == -EINPROGRESS) { ahash_notify_einprogress(areq); return; } areq->base.flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; err = ahash_def_finup_finish1(areq, err); if (areq->priv) return; areq->base.complete(&areq->base, err); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu6989.61%266.67%
Marek Vašut810.39%133.33%
Total77100.00%3100.00%


static int ahash_def_finup(struct ahash_request *req) { struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); int err; err = ahash_save_req(req, ahash_def_finup_done1); if (err) return err; err = tfm->update(req); if (err == -EINPROGRESS || err == -EBUSY) return err; return ahash_def_finup_finish1(req, err); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu6083.33%266.67%
Marek Vašut1216.67%133.33%
Total72100.00%3100.00%


static int ahash_no_export(struct ahash_request *req, void *out) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu19100.00%1100.00%
Total19100.00%1100.00%


static int ahash_no_import(struct ahash_request *req, const void *in) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu20100.00%1100.00%
Total20100.00%1100.00%


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); hash->setkey = ahash_nosetkey; hash->export = ahash_no_export; hash->import = ahash_no_import; if (tfm->__crt_alg->cra_type != &crypto_ahash_type) return crypto_init_shash_ops_async(tfm); hash->init = alg->init; hash->update = alg->update; hash->final = alg->final; hash->finup = alg->finup ?: ahash_def_finup; hash->digest = alg->digest; if (alg->setkey) { hash->setkey = alg->setkey; if (!(alg->halg.base.cra_flags & CRYPTO_ALG_OPTIONAL_KEY)) crypto_ahash_set_flags(hash, CRYPTO_TFM_NEED_KEY); } if (alg->export) hash->export = alg->export; if (alg->import) hash->import = alg->import; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu15385.96%360.00%
Eric Biggers2011.24%120.00%
Loc Ho52.81%120.00%
Total178100.00%5100.00%


static unsigned int crypto_ahash_extsize(struct crypto_alg *alg) { if (alg->cra_type != &crypto_ahash_type) return sizeof(struct crypto_shash *); return crypto_alg_extsize(alg); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu35100.00%2100.00%
Total35100.00%2100.00%

#ifdef CONFIG_NET
static int crypto_ahash_report(struct sk_buff *skb, struct crypto_alg *alg) { struct crypto_report_hash rhash; strncpy(rhash.type, "ahash", sizeof(rhash.type)); rhash.blocksize = alg->cra_blocksize; rhash.digestsize = __crypto_hash_alg_common(alg)->digestsize; if (nla_put(skb, CRYPTOCFGA_REPORT_HASH, sizeof(struct crypto_report_hash), &rhash)) goto nla_put_failure; return 0; nla_put_failure: return -EMSGSIZE; }

Contributors

PersonTokensPropCommitsCommitProp
Steffen Klassert7082.35%133.33%
Mathias Krause89.41%133.33%
David S. Miller78.24%133.33%
Total85100.00%3100.00%

#else
static int crypto_ahash_report(struct sk_buff *skb, struct crypto_alg *alg) { return -ENOSYS; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu20100.00%1100.00%
Total20100.00%1100.00%

#endif static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg) __maybe_unused;
static void crypto_ahash_show(struct seq_file *m, struct crypto_alg *alg) { seq_printf(m, "type : ahash\n"); seq_printf(m, "async : %s\n", alg->cra_flags & CRYPTO_ALG_ASYNC ? "yes" : "no"); seq_printf(m, "blocksize : %u\n", alg->cra_blocksize); seq_printf(m, "digestsize : %u\n", __crypto_hash_alg_common(alg)->digestsize); }

Contributors

PersonTokensPropCommitsCommitProp
Loc Ho6295.38%150.00%
Herbert Xu34.62%150.00%
Total65100.00%2100.00%

const struct crypto_type crypto_ahash_type = { .extsize = crypto_ahash_extsize, .init_tfm = crypto_ahash_init_tfm, #ifdef CONFIG_PROC_FS .show = crypto_ahash_show, #endif .report = crypto_ahash_report, .maskclear = ~CRYPTO_ALG_TYPE_MASK, .maskset = CRYPTO_ALG_TYPE_AHASH_MASK, .type = CRYPTO_ALG_TYPE_AHASH, .tfmsize = offsetof(struct crypto_ahash, base), }; EXPORT_SYMBOL_GPL(crypto_ahash_type);
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); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu31100.00%1100.00%
Total31100.00%1100.00%

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); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu29100.00%1100.00%
Total29100.00%1100.00%

EXPORT_SYMBOL_GPL(crypto_has_ahash);
static int ahash_prepare_alg(struct ahash_alg *alg) { struct crypto_alg *base = &alg->halg.base; if (alg->halg.digestsize > PAGE_SIZE / 8 || alg->halg.statesize > PAGE_SIZE / 8 || alg->halg.statesize == 0) return -EINVAL; base->cra_type = &crypto_ahash_type; base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK; base->cra_flags |= CRYPTO_ALG_TYPE_AHASH; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu7290.00%150.00%
Russell King810.00%150.00%
Total80100.00%2100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu45100.00%1100.00%
Total45100.00%1100.00%

EXPORT_SYMBOL_GPL(crypto_register_ahash);
int crypto_unregister_ahash(struct ahash_alg *alg) { return crypto_unregister_alg(&alg->halg.base); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu21100.00%1100.00%
Total21100.00%1100.00%

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) goto err; } return 0; err: for (--i; i >= 0; --i) crypto_unregister_ahash(&algs[i]); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Rabin Vincent80100.00%1100.00%
Total80100.00%1100.00%

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]); }

Contributors

PersonTokensPropCommitsCommitProp
Rabin Vincent40100.00%1100.00%
Total40100.00%1100.00%

EXPORT_SYMBOL_GPL(crypto_unregister_ahashes);
int ahash_register_instance(struct crypto_template *tmpl, struct ahash_instance *inst) { int err; err = ahash_prepare_alg(&inst->alg); if (err) return err; return crypto_register_instance(tmpl, ahash_crypto_instance(inst)); }

Contributors

PersonTokensPropCommitsCommitProp
Herbert Xu46100.00%1100.00%
Total46100.00%1100.00%

EXPORT_SYMBOL_GPL(ahash_register_instance)