548 lines · 13.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Synchronous Cryptographic Hash operations. * * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au> */ #include <crypto/scatterwalk.h>#include <linux/cryptouser.h>#include <linux/err.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/seq_file.h>#include <linux/string.h>#include <net/netlink.h> #include "hash.h" static inline bool crypto_shash_block_only(struct crypto_shash *tfm){	return crypto_shash_alg(tfm)->base.cra_flags &	       CRYPTO_AHASH_ALG_BLOCK_ONLY;} static inline bool crypto_shash_final_nonzero(struct crypto_shash *tfm){	return crypto_shash_alg(tfm)->base.cra_flags &	       CRYPTO_AHASH_ALG_FINAL_NONZERO;} static inline bool crypto_shash_finup_max(struct crypto_shash *tfm){	return crypto_shash_alg(tfm)->base.cra_flags &	       CRYPTO_AHASH_ALG_FINUP_MAX;} int shash_no_setkey(struct crypto_shash *tfm, const u8 *key,		    unsigned int keylen){	return -ENOSYS;}EXPORT_SYMBOL_GPL(shash_no_setkey); static void shash_set_needkey(struct crypto_shash *tfm, struct shash_alg *alg){	if (crypto_shash_alg_needs_key(alg))		crypto_shash_set_flags(tfm, CRYPTO_TFM_NEED_KEY);} int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,			unsigned int keylen){	struct shash_alg *shash = crypto_shash_alg(tfm);	int err; 	err = shash->setkey(tfm, key, keylen);	if (unlikely(err)) {		shash_set_needkey(tfm, shash);		return err;	} 	crypto_shash_clear_flags(tfm, CRYPTO_TFM_NEED_KEY);	return 0;}EXPORT_SYMBOL_GPL(crypto_shash_setkey); static int __crypto_shash_init(struct shash_desc *desc){	struct crypto_shash *tfm = desc->tfm; 	if (crypto_shash_block_only(tfm)) {		u8 *buf = shash_desc_ctx(desc); 		buf += crypto_shash_descsize(tfm) - 1;		*buf = 0;	} 	return crypto_shash_alg(tfm)->init(desc);} int crypto_shash_init(struct shash_desc *desc){	if (crypto_shash_get_flags(desc->tfm) & CRYPTO_TFM_NEED_KEY)		return -ENOKEY;	return __crypto_shash_init(desc);}EXPORT_SYMBOL_GPL(crypto_shash_init); static int shash_default_finup(struct shash_desc *desc, const u8 *data,			       unsigned int len, u8 *out){	struct shash_alg *shash = crypto_shash_alg(desc->tfm); 	return shash->update(desc, data, len) ?:	       shash->final(desc, out);} static int crypto_shash_op_and_zero(	int (*op)(struct shash_desc *desc, const u8 *data,		  unsigned int len, u8 *out),	struct shash_desc *desc, const u8 *data, unsigned int len, u8 *out){	int err; 	err = op(desc, data, len, out);	memset(shash_desc_ctx(desc), 0, crypto_shash_descsize(desc->tfm));	return err;} int crypto_shash_finup(struct shash_desc *restrict desc, const u8 *data,		       unsigned int len, u8 *restrict out){	struct crypto_shash *tfm = desc->tfm;	u8 *blenp = shash_desc_ctx(desc);	bool finup_max, nonzero;	unsigned int bs;	int err;	u8 *buf; 	if (!crypto_shash_block_only(tfm)) {		if (out)			goto finup;		return crypto_shash_alg(tfm)->update(desc, data, len);	} 	finup_max = out && crypto_shash_finup_max(tfm); 	/* Retain extra block for final nonzero algorithms. */	nonzero = crypto_shash_final_nonzero(tfm); 	/*	 * The partial block buffer follows the algorithm desc context.	 * The byte following that contains the length.	 */	blenp += crypto_shash_descsize(tfm) - 1;	bs = crypto_shash_blocksize(tfm);	buf = blenp - bs; 	if (likely(!*blenp && finup_max))		goto finup; 	while ((*blenp + len) >= bs + nonzero) {		unsigned int nbytes = len - nonzero;		const u8 *src = data; 		if (*blenp) {			memcpy(buf + *blenp, data, bs - *blenp);			nbytes = bs;			src = buf;		} 		err = crypto_shash_alg(tfm)->update(desc, src, nbytes);		if (err < 0)			return err; 		data += nbytes - err - *blenp;		len -= nbytes - err - *blenp;		*blenp = 0;	} 	if (*blenp || !out) {		memcpy(buf + *blenp, data, len);		*blenp += len;		if (!out)			return 0;		data = buf;		len = *blenp;	} finup:	return crypto_shash_op_and_zero(crypto_shash_alg(tfm)->finup, desc,					data, len, out);}EXPORT_SYMBOL_GPL(crypto_shash_finup); static int shash_default_digest(struct shash_desc *desc, const u8 *data,				unsigned int len, u8 *out){	return __crypto_shash_init(desc) ?:	       crypto_shash_finup(desc, data, len, out);} int crypto_shash_digest(struct shash_desc *desc, const u8 *data,			unsigned int len, u8 *out){	struct crypto_shash *tfm = desc->tfm; 	if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)		return -ENOKEY; 	return crypto_shash_op_and_zero(crypto_shash_alg(tfm)->digest, desc,					data, len, out);}EXPORT_SYMBOL_GPL(crypto_shash_digest); int crypto_shash_tfm_digest(struct crypto_shash *tfm, const u8 *data,			    unsigned int len, u8 *out){	SHASH_DESC_ON_STACK(desc, tfm); 	desc->tfm = tfm;	return crypto_shash_digest(desc, data, len, out);}EXPORT_SYMBOL_GPL(crypto_shash_tfm_digest); static int __crypto_shash_export(struct shash_desc *desc, void *out,				 int (*export)(struct shash_desc *desc,					       void *out)){	struct crypto_shash *tfm = desc->tfm;	u8 *buf = shash_desc_ctx(desc);	unsigned int plen, ss; 	plen = crypto_shash_blocksize(tfm) + 1;	ss = crypto_shash_statesize(tfm);	if (crypto_shash_block_only(tfm))		ss -= plen;	if (!export) {		memcpy(out, buf, ss);		return 0;	} 	return export(desc, out);} int crypto_shash_export_core(struct shash_desc *desc, void *out){	return __crypto_shash_export(desc, out,				     crypto_shash_alg(desc->tfm)->export_core);}EXPORT_SYMBOL_GPL(crypto_shash_export_core); int crypto_shash_export(struct shash_desc *desc, void *out){	struct crypto_shash *tfm = desc->tfm; 	if (crypto_shash_block_only(tfm)) {		unsigned int plen = crypto_shash_blocksize(tfm) + 1;		unsigned int descsize = crypto_shash_descsize(tfm);		unsigned int ss = crypto_shash_statesize(tfm);		u8 *buf = shash_desc_ctx(desc); 		memcpy(out + ss - plen, buf + descsize - plen, plen);	}	return __crypto_shash_export(desc, out, crypto_shash_alg(tfm)->export);}EXPORT_SYMBOL_GPL(crypto_shash_export); static int __crypto_shash_import(struct shash_desc *desc, const void *in,				 int (*import)(struct shash_desc *desc,					       const void *in)){	struct crypto_shash *tfm = desc->tfm;	unsigned int descsize, plen, ss;	u8 *buf = shash_desc_ctx(desc); 	if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)		return -ENOKEY; 	ss = crypto_shash_statesize(tfm);	if (crypto_shash_block_only(tfm)) {		plen = crypto_shash_blocksize(tfm) + 1;		ss -= plen;		descsize = crypto_shash_descsize(tfm);		buf[descsize - 1] = 0;	}	if (!import) {		memcpy(buf, in, ss);		return 0;	} 	return import(desc, in);} int crypto_shash_import_core(struct shash_desc *desc, const void *in){	return __crypto_shash_import(desc, in,				     crypto_shash_alg(desc->tfm)->import_core);}EXPORT_SYMBOL_GPL(crypto_shash_import_core); int crypto_shash_import(struct shash_desc *desc, const void *in){	struct crypto_shash *tfm = desc->tfm;	int err; 	err = __crypto_shash_import(desc, in, crypto_shash_alg(tfm)->import);	if (crypto_shash_block_only(tfm)) {		unsigned int plen = crypto_shash_blocksize(tfm) + 1;		unsigned int descsize = crypto_shash_descsize(tfm);		unsigned int ss = crypto_shash_statesize(tfm);		u8 *buf = shash_desc_ctx(desc); 		memcpy(buf + descsize - plen, in + ss - plen, plen);		if (buf[descsize - 1] >= plen)			err = -EOVERFLOW;	}	return err;}EXPORT_SYMBOL_GPL(crypto_shash_import); static void crypto_shash_exit_tfm(struct crypto_tfm *tfm){	struct crypto_shash *hash = __crypto_shash_cast(tfm);	struct shash_alg *alg = crypto_shash_alg(hash); 	alg->exit_tfm(hash);} static int crypto_shash_init_tfm(struct crypto_tfm *tfm){	struct crypto_shash *hash = __crypto_shash_cast(tfm);	struct shash_alg *alg = crypto_shash_alg(hash); 	shash_set_needkey(hash, alg); 	if (alg->exit_tfm)		tfm->exit = crypto_shash_exit_tfm; 	if (!alg->init_tfm)		return 0; 	return alg->init_tfm(hash);} static void crypto_shash_free_instance(struct crypto_instance *inst){	struct shash_instance *shash = shash_instance(inst); 	shash->free(shash);} static int __maybe_unused crypto_shash_report(	struct sk_buff *skb, struct crypto_alg *alg){	struct shash_alg *salg = __crypto_shash_alg(alg);	struct crypto_report_hash rhash = {		.type = "shash",	}; 	rhash.blocksize = alg->cra_blocksize;	rhash.digestsize = salg->digestsize; 	return nla_put(skb, CRYPTOCFGA_REPORT_HASH, sizeof(rhash), &rhash);} static void __maybe_unused crypto_shash_show(struct seq_file *m,					     struct crypto_alg *alg){	struct shash_alg *salg = __crypto_shash_alg(alg); 	seq_printf(m, "type         : shash\n");	seq_printf(m, "blocksize    : %u\n", alg->cra_blocksize);	seq_printf(m, "digestsize   : %u\n", salg->digestsize);} const struct crypto_type crypto_shash_type = {	.extsize = crypto_alg_extsize,	.init_tfm = crypto_shash_init_tfm,	.free = crypto_shash_free_instance,#ifdef CONFIG_PROC_FS	.show = crypto_shash_show,#endif#if IS_ENABLED(CONFIG_CRYPTO_USER)	.report = crypto_shash_report,#endif	.maskclear = ~CRYPTO_ALG_TYPE_MASK,	.maskset = CRYPTO_ALG_TYPE_MASK,	.type = CRYPTO_ALG_TYPE_SHASH,	.tfmsize = offsetof(struct crypto_shash, base),	.algsize = offsetof(struct shash_alg, base),}; int crypto_grab_shash(struct crypto_shash_spawn *spawn,		      struct crypto_instance *inst,		      const char *name, u32 type, u32 mask){	spawn->base.frontend = &crypto_shash_type;	return crypto_grab_spawn(&spawn->base, inst, name, type, mask);}EXPORT_SYMBOL_GPL(crypto_grab_shash); struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type,					u32 mask){	return crypto_alloc_tfm(alg_name, &crypto_shash_type, type, mask);}EXPORT_SYMBOL_GPL(crypto_alloc_shash); int crypto_has_shash(const char *alg_name, u32 type, u32 mask){	return crypto_type_has_alg(alg_name, &crypto_shash_type, type, mask);}EXPORT_SYMBOL_GPL(crypto_has_shash); int hash_prepare_alg(struct hash_alg_common *alg){	struct crypto_alg *base = &alg->base; 	if (alg->digestsize > HASH_MAX_DIGESTSIZE)		return -EINVAL; 	/* alignmask is not useful for hashes, so it is not supported. */	if (base->cra_alignmask)		return -EINVAL; 	base->cra_flags &= ~CRYPTO_ALG_TYPE_MASK; 	return 0;} static int shash_default_export_core(struct shash_desc *desc, void *out){	return -ENOSYS;} static int shash_default_import_core(struct shash_desc *desc, const void *in){	return -ENOSYS;} static int shash_prepare_alg(struct shash_alg *alg){	struct crypto_alg *base = &alg->halg.base;	int err; 	if ((alg->export && !alg->import) || (alg->import && !alg->export))		return -EINVAL; 	err = hash_prepare_alg(&alg->halg);	if (err)		return err; 	base->cra_type = &crypto_shash_type;	base->cra_flags |= CRYPTO_ALG_TYPE_SHASH;	base->cra_flags |= CRYPTO_ALG_REQ_VIRT; 	/*	 * Handle missing optional functions.  For each one we can either	 * install a default here, or we can leave the pointer as NULL and check	 * the pointer for NULL in crypto_shash_*(), avoiding an indirect call	 * when the default behavior is desired.  For ->finup and ->digest we	 * install defaults, since for optimal performance algorithms should	 * implement these anyway.  On the other hand, for ->import and	 * ->export the common case and best performance comes from the simple	 * memcpy of the shash_desc_ctx, so when those pointers are NULL we	 * leave them NULL and provide the memcpy with no indirect call.	 */	if (!alg->finup)		alg->finup = shash_default_finup;	if (!alg->digest)		alg->digest = shash_default_digest;	if (!alg->export && !alg->halg.statesize)		alg->halg.statesize = alg->descsize;	if (!alg->setkey)		alg->setkey = shash_no_setkey; 	if (base->cra_flags & CRYPTO_AHASH_ALG_BLOCK_ONLY) {		BUILD_BUG_ON(MAX_ALGAPI_BLOCKSIZE >= 256);		alg->descsize += base->cra_blocksize + 1;		alg->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 = shash_default_export_core;		alg->import_core = shash_default_import_core;		base->cra_flags |= CRYPTO_AHASH_ALG_NO_EXPORT_CORE;	} 	if (alg->descsize > HASH_MAX_DESCSIZE)		return -EINVAL;	if (alg->statesize > HASH_MAX_STATESIZE)		return -EINVAL; 	base->cra_reqsize = sizeof(struct shash_desc) + alg->descsize; 	return 0;} int crypto_register_shash(struct shash_alg *alg){	struct crypto_alg *base = &alg->base;	int err; 	err = shash_prepare_alg(alg);	if (err)		return err; 	return crypto_register_alg(base);}EXPORT_SYMBOL_GPL(crypto_register_shash); void crypto_unregister_shash(struct shash_alg *alg){	crypto_unregister_alg(&alg->base);}EXPORT_SYMBOL_GPL(crypto_unregister_shash); int crypto_register_shashes(struct shash_alg *algs, int count){

Lines 1–500 of 548.