All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: David Sterba <dsterba@suse.com>
Cc: linux-crypto@vger.kernel.org, ard.biesheuvel@linaro.org
Subject: Re: [PATCH v3] crypto: add blake2b generic implementation
Date: Thu, 10 Oct 2019 15:12:02 -0700	[thread overview]
Message-ID: <20191010221200.GB143518@gmail.com> (raw)
In-Reply-To: <e7f46def436c2c705c0b2cac3324f817efa4717d.1570715842.git.dsterba@suse.com>

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@suse.com>
> ---
> 
> 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@dei.uc.pt>.  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

  parent reply	other threads:[~2019-10-10 22:12 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-10-10 14:10 [PATCH v3] crypto: add blake2b generic implementation David Sterba
2019-10-10 14:45 ` Ard Biesheuvel
2019-10-10 22:12 ` Eric Biggers [this message]
2019-10-11 12:04   ` David Sterba
2019-10-10 22:29 ` Eric Biggers

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20191010221200.GB143518@gmail.com \
    --to=ebiggers@kernel.org \
    --cc=ard.biesheuvel@linaro.org \
    --cc=dsterba@suse.com \
    --cc=linux-crypto@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.