All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eric Biggers <ebiggers@kernel.org>
To: "Stephan Müller" <smueller@chronox.de>
Cc: herbert@gondor.apana.org.au, Jarkko Sakkinen <jarkko@kernel.org>,
	mathew.j.martineau@linux.intel.com, dhowells@redhat.com,
	linux-crypto@vger.kernel.org, linux-fscrypt@vger.kernel.org,
	linux-kernel@vger.kernel.org, keyrings@vger.kernel.org,
	simo@redhat.com
Subject: Re: [PATCH v2 3/7] crypto: add RFC5869 HKDF
Date: Thu, 28 Jan 2021 12:08:41 -0800	[thread overview]
Message-ID: <YBMZyU8Befg84iru@sol.localdomain> (raw)
In-Reply-To: <7864824.T7Z3S40VBb@positron.chronox.de>

On Sun, Jan 24, 2021 at 03:03:28PM +0100, Stephan Müller wrote:
> RFC5869 specifies an extract and expand two-step key derivation
> function. The HKDF implementation is provided as a service function that
> operates on a caller-provided HMAC handle. The caller has to allocate
> the HMAC shash handle and then can invoke the HKDF service functions.
> The HKDF implementation ensures that the entire state is kept with the
> HMAC shash handle which implies that no additional state is required to
> be maintained by the HKDF implementation.
> 
> The extract function is invoked via the crypto_hkdf_extract call. RFC5869
> allows two optional parameters to be provided to the extract operation:
> the salt and input key material (IKM). Both are to be provided with the
> seed parameter where the salt is the first entry of the seed parameter
> and all subsequent entries are handled as IKM. If the caller intends to
> invoke the HKDF without salt, it has to provide a NULL/0 entry as first
> entry in seed.

The part about the "seed parameter" is outdated.

> The expand function is invoked via crypto_hkdf_expand and can be
> invoked multiple times. This function allows the caller to provide a
> context for the key derivation operation. As specified in RFC5869, it is
> optional. In case such context is not provided, the caller must provide
> NULL / 0 for the info / info_nvec parameters.

This commit message doesn't actually mention of *why* this patch is useful.  Is
it because there are going to be more uses of HKDF in the kernel besides the one
in fs/crypto/?  If so, what are those planned uses?

> diff --git a/crypto/Kconfig b/crypto/Kconfig
> index 9f375c2350f5..661287d7283b 100644
> --- a/crypto/Kconfig
> +++ b/crypto/Kconfig
> @@ -1862,6 +1862,13 @@ config CRYPTO_JITTERENTROPY
>  	  random numbers. This Jitterentropy RNG registers with
>  	  the kernel crypto API and can be used by any caller.
>  
> +config CRYPTO_HKDF
> +	tristate "Extract and Expand HKDF (RFC 5869)"
> +	select CRYPTO_HASH
> +	help
> +	  Enable the extract and expand key derivation function compliant
> +	  to RFC 5869.

This is just a library function, so it shouldn't be user-selectable.
It should just be selected by the kconfig options that need it.

> diff --git a/crypto/hkdf.c b/crypto/hkdf.c
> new file mode 100644
> index 000000000000..8e80eca202e7
> --- /dev/null
> +++ b/crypto/hkdf.c
> @@ -0,0 +1,199 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/*
> + * HMAC-based Extract-and-Expand Key Derivation Function (conformant to RFC5869)
> + *
> + * Copyright (C) 2020, Stephan Mueller <smueller@chronox.de>
> + */
> +
> +#include <linux/module.h>
> +#include <crypto/hkdf.h>
> +#include <crypto/internal/kdf_selftest.h>
> +
> +/*
> + * HKDF expand phase
> + */
> +int crypto_hkdf_expand(struct crypto_shash *kmd,
> +		       const struct kvec *info, unsigned int info_nvec,
> +		       u8 *dst, unsigned int dlen)
> +{
> +	SHASH_DESC_ON_STACK(desc, kmd);
> +	const unsigned int h = crypto_shash_digestsize(kmd), dlen_orig = dlen;
> +	unsigned int i;
> +	int err = 0;
> +	u8 *dst_orig = dst;
> +	const u8 *prev = NULL;
> +	u8 ctr = 0x01;
> +
> +	if (dlen > h * 255)
> +		return -EINVAL;
> +
> +	desc->tfm = kmd;
> +
> +	/* T(1) and following */
> +	while (dlen) {
> +		err = crypto_shash_init(desc);
> +		if (err)
> +			goto out;
> +
> +		if (prev) {
> +			err = crypto_shash_update(desc, prev, h);
> +			if (err)
> +				goto out;
> +		}
> +
> +		for (i = 0; i < info_nvec; i++) {
> +			err = crypto_shash_update(desc, info[i].iov_base,
> +						  info[i].iov_len);
> +			if (err)
> +				goto out;
> +		}
> +
> +		if (dlen < h) {
> +			u8 tmpbuffer[HASH_MAX_DIGESTSIZE];
> +
> +			err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
> +			if (err)
> +				goto out;
> +			memcpy(dst, tmpbuffer, dlen);
> +			memzero_explicit(tmpbuffer, h);
> +			goto out;
> +		}
> +
> +		err = crypto_shash_finup(desc, &ctr, 1, dst);
> +		if (err)
> +			goto out;
> +
> +		prev = dst;
> +		dst += h;
> +		dlen -= h;
> +		ctr++;
> +	}
> +
> +out:
> +	if (err)
> +		memzero_explicit(dst_orig, dlen_orig);
> +	shash_desc_zero(desc);
> +	return err;
> +}
> +EXPORT_SYMBOL(crypto_hkdf_expand);

EXPORT_SYMBOL_GPL?

> +
> +/*
> + * HKDF extract phase.
> + */
> +int crypto_hkdf_extract(struct crypto_shash *kmd,
> +			const u8 *salt, size_t saltlen,
> +			const u8 *ikm, size_t ikmlen)
> +{
> +	SHASH_DESC_ON_STACK(desc, kmd);
> +	unsigned int h = crypto_shash_digestsize(kmd);
> +	int err;
> +	static const u8 null_salt[HASH_MAX_DIGESTSIZE] = { 0 };
> +	u8 prk[HASH_MAX_DIGESTSIZE];
> +
> +	desc->tfm = kmd;
> +
> +	if (salt && saltlen) {

Checking 'salt && saltlen' like this is poor practice, as then if people
accidentally use salt == NULL && saltlen != 0 when they intended to use a salt,
the bug won't be detected.  Just doing 'if (saltlen)' would be better.

> +
> +	/* Extract the PRK */
> +	err = crypto_shash_init(desc);
> +	if (err)
> +		goto err;
> +
> +	err = crypto_shash_finup(desc, ikm, ikmlen, prk);
> +	if (err)
> +		goto err;

This should use crypto_shash_digest() instead of crypto_shash_init() +
crypto_shash_finup().

> +/* Test vectors from RFC 5869 appendix A */
> +static const struct kdf_testvec hkdf_hmac_sha256_tv_template[] = {
> +	{
> +		/* salt */
> +		.key = "\x00\x01\x02\x03\x04\x05\x06\x07"
> +		       "\x08\x09\x0a\x0b\x0c",
> +		.keylen  = 13,
> +		.ikm = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> +		       "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> +		       "\x0b\x0b\x0b\x0b\x0b\x0b",
> +		.ikmlen = 22,
> +		.info = {
> +			.iov_base = "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7"
> +				    "\xf8\xf9",
> +			.iov_len  = 10
> +		},
> +		.expected	  = "\x3c\xb2\x5f\x25\xfa\xac\xd5\x7a"
> +				    "\x90\x43\x4f\x64\xd0\x36\x2f\x2a"
> +				    "\x2d\x2d\x0a\x90\xcf\x1a\x5a\x4c"
> +				    "\x5d\xb0\x2d\x56\xec\xc4\xc5\xbf"
> +				    "\x34\x00\x72\x08\xd5\xb8\x87\x18"
> +				    "\x58\x65",
> +		.expectedlen	  = 42
> +	}, {
> +		/* salt */
> +		.key = NULL,
> +		.keylen  = 0,
> +		.ikm = "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> +		       "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
> +		       "\x0b\x0b\x0b\x0b\x0b\x0b",
> +		.ikmlen  = 22,
> +		.info = {
> +			.iov_base = NULL,
> +			.iov_len  = 0
> +		},
> +		.expected	  = "\x8d\xa4\xe7\x75\xa5\x63\xc1\x8f"
> +				    "\x71\x5f\x80\x2a\x06\x3c\x5a\x31"
> +				    "\xb8\xa1\x1f\x5c\x5e\xe1\x87\x9e"
> +				    "\xc3\x45\x4e\x5f\x3c\x73\x8d\x2d"
> +				    "\x9d\x20\x13\x95\xfa\xa4\xb6\x1a"
> +				    "\x96\xc8",
> +		.expectedlen	  = 42
> +	}
> +};

The case of multiple entries in the 'info' kvec isn't being tested.

Also, it is confusing having both 'key' and 'ikm'.  'key' really should be
'salt'.

> diff --git a/include/crypto/hkdf.h b/include/crypto/hkdf.h
> new file mode 100644
> index 000000000000..c6989f786860
> --- /dev/null
> +++ b/include/crypto/hkdf.h
> @@ -0,0 +1,48 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/*
> + * Copyright (C) 2020, Stephan Mueller <smueller@chronox.de>
> + */
> +
> +#ifndef _CRYPTO_HKDF_H
> +#define _CRYPTO_HKDF_H
> +
> +#include <crypto/hash.h>
> +#include <linux/uio.h>
> +
> +/**
> + * RFC 5869 HKDF expand operation
> + *
> + * @kmd Keyed message digest whose key was set with crypto_hkdf_extract
> + * @info optional context and application specific information - this may be
> + *	 NULL
> + * @info_vec number of optional context/application specific information entries
> + * @dst destination buffer that the caller already allocated
> + * @dlen length of the destination buffer - the KDF derives that amount of
> + *	 bytes.
> + *
> + * @return 0 on success, < 0 on error
> + */
> +int crypto_hkdf_expand(struct crypto_shash *kmd,
> +		       const struct kvec *info, unsigned int info_nvec,
> +		       u8 *dst, unsigned int dlen);
> +
> +/**
> + * RFC 5869 HKDF extract operation
> + *
> + * @kmd Keyed message digest allocated by the caller. The key should not have
> + *	been set.
> + * @salt The salt used for the KDF. It is permissible to provide NULL as salt
> + *	 which implies that the default salt is used.
> + * @saltlen Length of the salt buffer.
> + * @ikm The input key material (IKM). It is permissible to provide NULL as IKM.
> + * @ikmlen Length of the IKM buffer
> + * @seed_nvec number of seed entries (must be at least 1)

seed_nvec no longer exists.

- Eric

  reply	other threads:[~2021-01-28 20:11 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-01-24 14:01 [PATCH v2 0/7] Add KDF implementations to crypto API Stephan Müller
2021-01-24 14:01 ` [PATCH v2 1/7] crypto: Add key derivation self-test support code Stephan Müller
2021-01-24 14:02 ` [PATCH v2 2/7] crypto: add SP800-108 counter key derivation function Stephan Müller
2021-01-24 14:03 ` [PATCH v2 3/7] crypto: add RFC5869 HKDF Stephan Müller
2021-01-28 20:08   ` Eric Biggers [this message]
2021-01-24 14:03 ` [PATCH v2 4/7] security: DH - remove dead code for zero padding Stephan Müller
2021-01-24 14:04 ` [PATCH v2 5/7] security: DH - use KDF implementation from crypto API Stephan Müller
2021-01-24 14:04 ` [PATCH v2 6/7] fs: use HKDF implementation from kernel " Stephan Müller
2021-01-28 20:16   ` Eric Biggers
2021-01-28 20:18   ` Eric Biggers
2021-01-24 14:04 ` [PATCH v2 7/7] fs: HKDF - remove duplicate memory clearing Stephan Müller
2021-01-28 20:21   ` Eric Biggers
2021-01-24 14:23 ` [PATCH v2 0/7] Add KDF implementations to crypto API Ard Biesheuvel
2021-01-24 14:32   ` Ard Biesheuvel
2021-01-24 14:36     ` Stephan Müller
2021-01-24 14:34   ` Stephan Müller

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=YBMZyU8Befg84iru@sol.localdomain \
    --to=ebiggers@kernel.org \
    --cc=dhowells@redhat.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=jarkko@kernel.org \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-fscrypt@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mathew.j.martineau@linux.intel.com \
    --cc=simo@redhat.com \
    --cc=smueller@chronox.de \
    /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.