mirror of
https://github.com/torvalds/linux.git
synced 2025-12-01 07:26:02 +07:00
crypto: shash - Handle partial blocks in API
Provide an option to handle the partial blocks in the shash API. Almost every hash algorithm has a block size and are only able to hash partial blocks on finalisation. Rather than duplicating the partial block handling many times, add this functionality to the shash API. It is optional (e.g., hmac would never need this by relying on the partial block handling of the underlying hash), and to enable it set the bit CRYPTO_AHASH_ALG_BLOCK_ONLY. The export format is always that of the underlying hash export, plus the partial block buffer, followed by a single-byte for the partial block length. Set the bit CRYPTO_AHASH_ALG_FINAL_NONZERO to withhold an extra byte in the partial block. This will come in handy when this is extended to ahash where hardware often can't deal with a zero-length final. It will also be used for algorithms requiring an extra block for finalisation (e.g., cmac). As an optimisation, set the bit CRYPTO_AHASH_ALG_FINUP_MAX if the algorithm wishes to get as much data as possible instead of just the last partial block. The descriptor will be zeroed after finalisation. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
229
crypto/shash.c
229
crypto/shash.c
@@ -16,6 +16,24 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -46,18 +64,27 @@ int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_setkey);
|
||||
|
||||
int crypto_shash_update(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len)
|
||||
static int __crypto_shash_init(struct shash_desc *desc)
|
||||
{
|
||||
return crypto_shash_alg(desc->tfm)->update(desc, data, len);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_update);
|
||||
struct crypto_shash *tfm = desc->tfm;
|
||||
|
||||
int crypto_shash_final(struct shash_desc *desc, u8 *out)
|
||||
{
|
||||
return crypto_shash_alg(desc->tfm)->final(desc, out);
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_final);
|
||||
|
||||
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)
|
||||
@@ -68,20 +95,89 @@ static int shash_default_finup(struct shash_desc *desc, const u8 *data,
|
||||
shash->final(desc, out);
|
||||
}
|
||||
|
||||
int crypto_shash_finup(struct shash_desc *desc, const u8 *data,
|
||||
unsigned int len, u8 *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)
|
||||
{
|
||||
return crypto_shash_alg(desc->tfm)->finup(desc, data, len, 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)
|
||||
{
|
||||
struct shash_alg *shash = crypto_shash_alg(desc->tfm);
|
||||
|
||||
return shash->init(desc) ?:
|
||||
shash->finup(desc, data, len, out);
|
||||
return __crypto_shash_init(desc) ?:
|
||||
crypto_shash_finup(desc, data, len, out);
|
||||
}
|
||||
|
||||
int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
|
||||
@@ -92,7 +188,8 @@ int crypto_shash_digest(struct shash_desc *desc, const u8 *data,
|
||||
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
|
||||
return -ENOKEY;
|
||||
|
||||
return crypto_shash_alg(tfm)->digest(desc, data, len, out);
|
||||
return crypto_shash_op_and_zero(crypto_shash_alg(tfm)->digest, desc,
|
||||
data, len, out);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_digest);
|
||||
|
||||
@@ -100,44 +197,92 @@ int crypto_shash_tfm_digest(struct crypto_shash *tfm, const u8 *data,
|
||||
unsigned int len, u8 *out)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(desc, tfm);
|
||||
int err;
|
||||
|
||||
desc->tfm = tfm;
|
||||
|
||||
err = crypto_shash_digest(desc, data, len, out);
|
||||
|
||||
shash_desc_zero(desc);
|
||||
|
||||
return err;
|
||||
return crypto_shash_digest(desc, data, len, out);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_tfm_digest);
|
||||
|
||||
int crypto_shash_export_core(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;
|
||||
export = crypto_shash_alg(tfm)->export;
|
||||
if (!export) {
|
||||
memcpy(out, buf, ss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return export(desc, out);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_export_core);
|
||||
|
||||
int crypto_shash_export(struct shash_desc *desc, void *out)
|
||||
{
|
||||
struct crypto_shash *tfm = desc->tfm;
|
||||
struct shash_alg *shash = crypto_shash_alg(tfm);
|
||||
|
||||
if (shash->export)
|
||||
return shash->export(desc, out);
|
||||
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, shash_desc_ctx(desc), crypto_shash_descsize(tfm));
|
||||
return 0;
|
||||
memcpy(out + ss - plen, buf + descsize - plen, plen);
|
||||
}
|
||||
return crypto_shash_export_core(desc, out);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crypto_shash_export);
|
||||
|
||||
int crypto_shash_import(struct shash_desc *desc, const void *in)
|
||||
int crypto_shash_import_core(struct shash_desc *desc, const void *in)
|
||||
{
|
||||
int (*import)(struct shash_desc *desc, const void *in);
|
||||
struct crypto_shash *tfm = desc->tfm;
|
||||
struct shash_alg *shash = crypto_shash_alg(tfm);
|
||||
unsigned int descsize, plen, ss;
|
||||
u8 *buf = shash_desc_ctx(desc);
|
||||
|
||||
if (crypto_shash_get_flags(tfm) & CRYPTO_TFM_NEED_KEY)
|
||||
return -ENOKEY;
|
||||
|
||||
if (shash->import)
|
||||
return shash->import(desc, in);
|
||||
plen = crypto_shash_blocksize(tfm) + 1;
|
||||
descsize = crypto_shash_descsize(tfm);
|
||||
ss = crypto_shash_statesize(tfm);
|
||||
buf[descsize - 1] = 0;
|
||||
if (crypto_shash_block_only(tfm))
|
||||
ss -= plen;
|
||||
import = crypto_shash_alg(tfm)->import;
|
||||
if (!import) {
|
||||
memcpy(buf, in, ss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(shash_desc_ctx(desc), in, crypto_shash_descsize(tfm));
|
||||
return 0;
|
||||
return import(desc, in);
|
||||
}
|
||||
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_core(desc, in);
|
||||
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);
|
||||
|
||||
@@ -293,9 +438,6 @@ static int shash_prepare_alg(struct shash_alg *alg)
|
||||
struct crypto_alg *base = &alg->halg.base;
|
||||
int err;
|
||||
|
||||
if (alg->descsize > HASH_MAX_DESCSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if ((alg->export && !alg->import) || (alg->import && !alg->export))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -321,11 +463,20 @@ static int shash_prepare_alg(struct shash_alg *alg)
|
||||
alg->finup = shash_default_finup;
|
||||
if (!alg->digest)
|
||||
alg->digest = shash_default_digest;
|
||||
if (!alg->export)
|
||||
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;
|
||||
}
|
||||
|
||||
if (alg->descsize > HASH_MAX_DESCSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user