Re: [PATCH v3] crypto: add blake2b generic implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi David, thanks for working on this.  Comments below.

On Thu, Oct 10, 2019 at 04:10:05PM +0200, David Sterba wrote:
> The patch brings support of several BLAKE2 variants (2b with various
> digest lengths).  The keyed digest is supported, using tfm->setkey call.
> The in-tree user will be btrfs (for checksumming), we're going to use
> the BLAKE2b-256 variant.
> 
> The code is reference implementation taken from the official sources and
> modified only in terms of kernel coding style (whitespace, comments,
> uintXX_t -> uXX types, removed unused prototypes and #ifdefs, removed
> testing code, changed secure_zero_memory -> memzero_explicit, used own
> helpers for unaligned reads/writes and rotations).
> 
> Signed-off-by: David Sterba <dsterba@xxxxxxxx>
> ---
> 
> V3:
> 
> - added 'static' to blake2b_* and removed .h declarations
> - updated Kconfig help text
> - replaced custom build bug check with BUILD_BUG_ON
> 
> - added .setkey to TFM, optional key, the length validation is same as
>   what blake2b_init_key accepts, ie. 1..BLAKE2B_KEYBYTES
> 
> - fixed a serious bug: digestsize in all callbacks must be obtained from
>   TFM, as the same functions are used for all variants but the default
>   output size was used (in digest_init, digest_final, digest_finup),
> 
> I'm going to do the selftests next so the above can't happen again.

The test vectors should be included in this patch.

> +
> +	  - blake2b     - the default 512b digest
> +	  - blake2b-160
> +	  - blake2b-256
> +	  - blake2b-384
> +	  - blake2b-512
> +

Why have the "blake2b" algorithm at all, when it's already available under the
name "blake2b-512"?  It's confusing to have two different names for the same
algorithm because then people will need to decide which one to use, and both
will need to be tested.

> diff --git a/crypto/blake2b_generic.c b/crypto/blake2b_generic.c
> new file mode 100644
> index 000000000000..588f2c5daa2d
> --- /dev/null
> +++ b/crypto/blake2b_generic.c
> @@ -0,0 +1,504 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR Apache-2.0)
> +/*
> + * BLAKE2b reference source code package - reference C implementations
> + *
> + * Copyright 2012, Samuel Neves <sneves@xxxxxxxxx>.  You may use this under the
> + * terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at
> + * your option.  The terms of these licenses can be found at:
> + *
> + * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0
> + * - OpenSSL license   : https://www.openssl.org/source/license.html
> + * - Apache 2.0        : http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * More information about the BLAKE2 hash function can be found at
> + * https://blake2.net.
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/module.h>
> +#include <linux/string.h>
> +#include <linux/kernel.h>
> +#include <linux/bitops.h>
> +#include <crypto/internal/hash.h>
> +#include <crypto/blake2b.h>
> +
> +struct blake2b_param
> +{
> +	u8  digest_length; /* 1 */
> +	u8  key_length;    /* 2 */
> +	u8  fanout;        /* 3 */
> +	u8  depth;         /* 4 */
> +	u32 leaf_length;   /* 8 */
> +	u32 node_offset;   /* 12 */
> +	u32 xof_length;    /* 16 */

The u32 fields need to be __le32, since this struct is interpreted as an array
of bytes.

> +static int blake2b_init(struct blake2b_state *S, size_t outlen)
> +{
> +	struct blake2b_param P[1];

This shouldn't be an array.

> +
> +	if ((!outlen) || (outlen > BLAKE2B_OUTBYTES))
> +		return -1;

No need for these checks, since this patch doesn't provide any way for the user
to set an arbitrary outlen.  They should either be removed, or replaced with a
WARN_ON().  As-is, it looks like a valid error, which is bad because some
callers of the crypto_shash API don't handle errors.

> +
> +	P->digest_length = (u8)outlen;
> +	P->key_length    = 0;
> +	P->fanout        = 1;
> +	P->depth         = 1;
> +	put_unaligned_le32(0, &P->leaf_length);
> +	put_unaligned_le32(0, &P->node_offset);
> +	put_unaligned_le32(0, &P->xof_length);

struct blake2b_param is already a packed structure, so these should be direct
assignments.  No need for put_unaligned_le32().

> +	P->node_depth    = 0;
> +	P->inner_length  = 0;
> +	memset(P->reserved, 0, sizeof(P->reserved));
> +	memset(P->salt,     0, sizeof(P->salt));
> +	memset(P->personal, 0, sizeof(P->personal));
> +	return blake2b_init_param(S, P);
> +}
> +
> +static int blake2b_init_key(struct blake2b_state *S, size_t outlen,
> +			 const void *key, size_t keylen)
> +{
> +	struct blake2b_param P[1];
> +
> +	if ((!outlen) || (outlen > BLAKE2B_OUTBYTES))
> +		return -1;
> +
> +	if (!key || !keylen || keylen > BLAKE2B_KEYBYTES)
> +		return -1;

More unclear error checks here.  Which are actually valid reachable errors, and
which are assertions that should never trigger?  See comment above.

> +
> +	P->digest_length = (u8)outlen;
> +	P->key_length    = (u8)keylen;
> +	P->fanout        = 1;
> +	P->depth         = 1;
> +	put_unaligned_le32(0, &P->leaf_length);
> +	put_unaligned_le32(0, &P->node_offset);
> +	put_unaligned_le32(0, &P->xof_length);

Same problem with the unnecessary put_unaligned_le32().

> +static int blake2b_final(struct blake2b_state *S, void *out, size_t outlen)
> +{
> +	u8 buffer[BLAKE2B_OUTBYTES] = {0};
> +	size_t i;
> +
> +	if (out == NULL || outlen < S->outlen)
> +		return -1;

More unnecessary error checks.  None of the other hash algorithms check for a
NULL output buffer, and some users don't check for errors.  So returning -1
instead of just crashing could hide bugs.

> +
> +	if (blake2b_is_lastblock(S))
> +		return -1;

This can't be the case because lastblock is only set by final().

> +static int digest_setkey(struct crypto_shash *tfm, const u8 *key,
> +			 unsigned int keylen)
> +{
> +	struct digest_tfm_ctx *mctx = crypto_shash_ctx(tfm);
> +
> +	if (keylen == 0 || keylen > BLAKE2B_KEYBYTES) {
> +		crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN);
> +		return -EINVAL;
> +	}
> +
> +	memcpy(mctx->key, key, BLAKE2B_KEYBYTES);
> +	mctx->keylen = keylen;
> +
> +	return 0;
> +}

This reads past the end of the key buffer if keylen < BLAKE2B_KEYBYTES.

Please add tests and run with CONFIG_KASAN=y.

> +static int digest_update(struct shash_desc *desc, const u8 *data,
> +			 unsigned int length)
> +{
> +	struct digest_desc_ctx *ctx = shash_desc_ctx(desc);
> +	int ret;
> +
> +	ret = blake2b_update(ctx->S, data, length);
> +	if (ret)
> +		return -EINVAL;
> +	return 0;
> +}

Why does update() need to fail?  Not all shash API users check for errors.

> +
> +static int digest_final(struct shash_desc *desc, u8 *out)
> +{
> +	struct digest_desc_ctx *ctx = shash_desc_ctx(desc);
> +	const int digestsize = crypto_shash_digestsize(desc->tfm);
> +	int ret;
> +
> +	ret = blake2b_final(ctx->S, out, digestsize);
> +	if (ret)
> +		return -EINVAL;
> +	return 0;
> +}

Likewise.  Why does final() need to fail?

> +
> +static int digest_finup(struct shash_desc *desc, const u8 *data,
> +			unsigned int len, u8 *out)
> +{
> +	struct digest_desc_ctx *ctx = shash_desc_ctx(desc);
> +	const int digestsize = crypto_shash_digestsize(desc->tfm);
> +	int ret;
> +
> +	ret = blake2b_update(ctx->S, data, len);
> +	if (ret)
> +		return -EINVAL;
> +	ret = blake2b_final(ctx->S, out, digestsize);
> +	if (ret)
> +		return -EINVAL;
> +	return 0;
> +}

finup() shouldn't be implemented if it can't be made more efficient than
update() and final() separately.

> +static int blake2b_cra_init(struct crypto_tfm *tfm)
> +{
> +	struct digest_tfm_ctx *mctx = crypto_tfm_ctx(tfm);
> +
> +	/* Use the unkeyed version by default */
> +	memset(mctx->key, 0, BLAKE2B_KEYBYTES);
> +	mctx->keylen = 0;
> +
> +	return 0;
> +}

No need for this function, since the tfm_ctx starts out zeroed by default.

> +static struct shash_alg blake2b_algs[] = {
> +	{
> +		.digestsize		= BLAKE2B_512_DIGEST_SIZE,
> +		.setkey			= digest_setkey,
> +		.init			= digest_init,
> +		.update			= digest_update,
> +		.final			= digest_final,
> +		.finup			= digest_finup,
> +		.descsize		= sizeof(struct digest_desc_ctx),
> +		.base.cra_name		= "blake2b",
> +		.base.cra_driver_name	= "blake2b-generic",
> +		.base.cra_priority	= 100,
> +		.base.cra_flags		= CRYPTO_ALG_OPTIONAL_KEY,
> +		.base.cra_blocksize	= BLAKE2B_BLOCKBYTES,
> +		.base.cra_ctxsize	= 0,

Need to set cra_ctxsize to sizeof(struct digest_tfm_ctx), otherwise the code is
using an area beyond the end of the buffer for the tfm_ctx.  This would have
been caught if there were self tests and they were run with CONFIG_KASAN=y.

Thanks!

- Eric



[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]

  Powered by Linux