linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Stephan Müller" <smueller@chronox.de>
To: Eric Biggers <ebiggers@kernel.org>
Cc: Herbert Xu <herbert@gondor.apana.org.au>,
	James Bottomley <James.Bottomley@hansenpartnership.com>,
	Andy Lutomirski <luto@amacapital.net>,
	"Lee, Chun-Yi" <joeyli.kernel@gmail.com>,
	"Rafael J . Wysocki" <rjw@rjwysocki.net>,
	Pavel Machek <pavel@ucw.cz>,
	linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org,
	keyrings@vger.kernel.org,
	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>,
	Chen Yu <yu.c.chen@intel.com>, Oliver Neukum <oneukum@suse.com>,
	Ryan Chen <yu.chen.surf@gmail.com>,
	David Howells <dhowells@redhat.com>,
	Giovanni Gherdovich <ggherdovich@suse.cz>,
	Randy Dunlap <rdunlap@infradead.org>,
	Jann Horn <jannh@google.com>, Andy Lutomirski <luto@kernel.org>,
	linux-crypto@vger.kernel.org
Subject: [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function
Date: Fri, 11 Jan 2019 20:10:39 +0100	[thread overview]
Message-ID: <2423373.Zd5ThvQH5g@positron.chronox.de> (raw)
In-Reply-To: <9733066.Vrs4h5eWcW@positron.chronox.de>

The RFC5869 compliant Key Derivation Function is implemented as a
random number generator considering that it behaves like a deterministic
RNG.

The extract and expand phases use different instances of the underlying
keyed message digest cipher to ensure that while the extraction phase
generates a new key for the expansion phase, the cipher for the
expansion phase can still be used. This approach is intended to aid
multi-threaded uses cases.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
 crypto/Kconfig  |   6 +
 crypto/Makefile |   1 +
 crypto/hkdf.c   | 290 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 297 insertions(+)
 create mode 100644 crypto/hkdf.c

diff --git a/crypto/Kconfig b/crypto/Kconfig
index cc80d89e0cf5..0eee5e129fa3 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -568,6 +568,12 @@ config CRYPTO_KDF
 	  Support for KDF compliant to SP800-108. All three types of
 	  KDF specified in SP800-108 are implemented.
 
+config CRYPTO_HKDF
+	tristate "HMAC-based Extract-and expand Key Derivation Function"
+	select CRYPTO_RNG
+	help
+	  Support for KDF compliant to RFC5869.
+
 config CRYPTO_XCBC
 	tristate "XCBC support"
 	select CRYPTO_HASH
diff --git a/crypto/Makefile b/crypto/Makefile
index 69a0bb64b0ac..6bbb0a4dea13 100644
--- a/crypto/Makefile
+++ b/crypto/Makefile
@@ -59,6 +59,7 @@ crypto_user-$(CONFIG_CRYPTO_STATS) += crypto_user_stat.o
 obj-$(CONFIG_CRYPTO_CMAC) += cmac.o
 obj-$(CONFIG_CRYPTO_HMAC) += hmac.o
 obj-$(CONFIG_CRYPTO_KDF) += kdf.o
+obj-$(CONFIG_CRYPTO_HKDF) += hkdf.o
 obj-$(CONFIG_CRYPTO_VMAC) += vmac.o
 obj-$(CONFIG_CRYPTO_XCBC) += xcbc.o
 obj-$(CONFIG_CRYPTO_NULL2) += crypto_null.o
diff --git a/crypto/hkdf.c b/crypto/hkdf.c
new file mode 100644
index 000000000000..35a975ed64a8
--- /dev/null
+++ b/crypto/hkdf.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * RFC 5869 Key-derivation function
+ *
+ * Copyright (C) 2019, Stephan Mueller <smueller@chronox.de>
+ */
+
+/*
+ * The HKDF extract phase is applied with crypto_rng_reset().
+ * The HKDF expand phase is applied with crypto_rng_generate().
+ *
+ * NOTE: In-place cipher operations are not supported.
+ */
+
+#include <linux/module.h>
+#include <crypto/rng.h>
+#include <crypto/internal/rng.h>
+#include <crypto/hash.h>
+#include <crypto/internal/hash.h>
+#include <linux/rtnetlink.h>
+
+struct crypto_hkdf_ctx {
+	struct crypto_shash *extract_kmd;
+	struct crypto_shash *expand_kmd;
+};
+
+#define CRYPTO_HKDF_MAX_DIGESTSIZE	64
+
+/*
+ * HKDF expand phase
+ */
+static int crypto_hkdf_random(struct crypto_rng *rng,
+			      const u8 *info, unsigned int infolen,
+			      u8 *dst, unsigned int dlen)
+{
+	struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+	struct crypto_shash *expand_kmd = ctx->expand_kmd;
+	SHASH_DESC_ON_STACK(desc, expand_kmd);
+	unsigned int h = crypto_shash_digestsize(expand_kmd);
+	int err = 0;
+	u8 *dst_orig = dst;
+	const u8 *prev = NULL;
+	uint8_t ctr = 0x01;
+
+	if (dlen > h * 255)
+		return -EINVAL;
+
+	desc->tfm = expand_kmd;
+	desc->flags = crypto_shash_get_flags(expand_kmd) &
+		      CRYPTO_TFM_REQ_MAY_SLEEP;
+
+	/* 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;
+		}
+
+		if (info) {
+			err = crypto_shash_update(desc, info, infolen);
+			if (err)
+				goto out;
+		}
+
+		if (dlen < h) {
+			u8 tmpbuffer[CRYPTO_HKDF_MAX_DIGESTSIZE];
+
+			err = crypto_shash_finup(desc, &ctr, 1, tmpbuffer);
+			if (err)
+				goto out;
+			memcpy(dst, tmpbuffer, dlen);
+			memzero_explicit(tmpbuffer, h);
+			goto out;
+		} else {
+			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);
+	shash_desc_zero(desc);
+	return err;
+}
+
+/*
+ * HKDF extract phase.
+ *
+ * The seed is defined to be a concatenation of the salt and the IKM.
+ * The data buffer is pre-pended by an rtattr which provides an u32 value
+ * with the length of the salt. Thus, the buffer length - salt length is the
+ * IKM length.
+ */
+static int crypto_hkdf_seed(struct crypto_rng *rng,
+			    const u8 *seed, unsigned int slen)
+{
+	struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng));
+	struct crypto_shash *extract_kmd = ctx->extract_kmd;
+	struct crypto_shash *expand_kmd = ctx->expand_kmd;
+	struct rtattr *rta = (struct rtattr *)seed;
+	SHASH_DESC_ON_STACK(desc, extract_kmd);
+	u32 saltlen;
+	unsigned int h = crypto_shash_digestsize(extract_kmd);
+	int err;
+	const uint8_t null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
+	u8 prk[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 };
+
+	/* Require aligned buffer to directly read out saltlen below */
+	if (WARN_ON((unsigned long)seed & (sizeof(saltlen) - 1)))
+		return -EINVAL;
+
+	if (!RTA_OK(rta, slen))
+		return -EINVAL;
+	if (rta->rta_type != 1)
+		return -EINVAL;
+	if (RTA_PAYLOAD(rta) < sizeof(saltlen))
+		return -EINVAL;
+	saltlen = *((u32 *)RTA_DATA(rta));
+
+	seed += RTA_ALIGN(rta->rta_len);
+	slen -= RTA_ALIGN(rta->rta_len);
+
+	if (slen < saltlen)
+		return -EINVAL;
+
+	desc->tfm = extract_kmd;
+
+	/* Set the salt as HMAC key */
+	if (saltlen)
+		err = crypto_shash_setkey(extract_kmd, seed, saltlen);
+	else
+		err = crypto_shash_setkey(extract_kmd, null_salt, h);
+	if (err)
+		return err;
+
+	/* Extract the PRK */
+	err = crypto_shash_digest(desc, seed + saltlen, slen - saltlen, prk);
+	if (err)
+		goto err;
+
+	/* Set the PRK for the expand phase */
+	err = crypto_shash_setkey(expand_kmd, prk, h);
+	if (err)
+		goto err;
+
+err:
+	shash_desc_zero(desc);
+	memzero_explicit(prk, h);
+	return err;
+}
+
+static int crypto_hkdf_init_tfm(struct crypto_tfm *tfm)
+{
+	struct crypto_instance *inst = crypto_tfm_alg_instance(tfm);
+	struct crypto_shash_spawn *spawn = crypto_instance_ctx(inst);
+	struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+	struct crypto_shash *extract_kmd = NULL, *expand_kmd = NULL;
+	unsigned int ds;
+
+	extract_kmd = crypto_spawn_shash(spawn);
+	if (IS_ERR(extract_kmd))
+		return PTR_ERR(extract_kmd);
+
+	expand_kmd = crypto_spawn_shash(spawn);
+	if (IS_ERR(expand_kmd)) {
+		crypto_free_shash(extract_kmd);
+		return PTR_ERR(expand_kmd);
+	}
+
+	ds = crypto_shash_digestsize(extract_kmd);
+	/* Hashes with no digest size are not allowed for KDFs. */
+	if (!ds || ds > CRYPTO_HKDF_MAX_DIGESTSIZE) {
+		crypto_free_shash(extract_kmd);
+		crypto_free_shash(expand_kmd);
+		return -EOPNOTSUPP;
+	}
+
+	ctx->extract_kmd = extract_kmd;
+	ctx->expand_kmd = expand_kmd;
+
+	return 0;
+}
+
+static void crypto_hkdf_exit_tfm(struct crypto_tfm *tfm)
+{
+	struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(tfm);
+
+	crypto_free_shash(ctx->extract_kmd);
+	crypto_free_shash(ctx->expand_kmd);
+}
+
+static void crypto_kdf_free(struct rng_instance *inst)
+{
+	crypto_drop_spawn(rng_instance_ctx(inst));
+	kfree(inst);
+}
+
+static int crypto_hkdf_create(struct crypto_template *tmpl, struct rtattr **tb)
+{
+	struct rng_instance *inst;
+	struct crypto_alg *alg;
+	struct shash_alg *salg;
+	int err;
+	unsigned int ds, ss;
+
+	err = crypto_check_attr_type(tb, CRYPTO_ALG_TYPE_RNG);
+	if (err)
+		return err;
+
+	salg = shash_attr_alg(tb[1], 0, 0);
+	if (IS_ERR(salg))
+		return PTR_ERR(salg);
+
+	ds = salg->digestsize;
+	ss = salg->statesize;
+	alg = &salg->base;
+
+	inst = rng_alloc_instance("hkdf", alg);
+	err = PTR_ERR(inst);
+	if (IS_ERR(inst))
+		goto out_put_alg;
+
+	err = crypto_init_shash_spawn(rng_instance_ctx(inst), salg,
+				      rng_crypto_instance(inst));
+	if (err)
+		goto out_free_inst;
+
+	inst->alg.base.cra_priority	= alg->cra_priority;
+	inst->alg.base.cra_blocksize	= alg->cra_blocksize;
+	inst->alg.base.cra_alignmask	= alg->cra_alignmask;
+
+	inst->alg.generate		= crypto_hkdf_random;
+	inst->alg.seed			= crypto_hkdf_seed;
+	inst->alg.seedsize		= ds;
+
+	inst->alg.base.cra_init		= crypto_hkdf_init_tfm;
+	inst->alg.base.cra_exit		= crypto_hkdf_exit_tfm;
+	inst->alg.base.cra_ctxsize	= ALIGN(sizeof(struct crypto_hkdf_ctx) +
+					  ss * 2, crypto_tfm_ctx_alignment());
+
+	inst->free			= crypto_kdf_free;
+
+	err = rng_register_instance(tmpl, inst);
+
+	if (err) {
+out_free_inst:
+		crypto_kdf_free(inst);
+	}
+
+out_put_alg:
+	crypto_mod_put(alg);
+	return err;
+}
+
+static struct crypto_template crypto_hkdf_tmpl = {
+	.name = "hkdf",
+	.create = crypto_hkdf_create,
+	.module = THIS_MODULE,
+};
+
+static int __init crypto_hkdf_init(void)
+{
+	return crypto_register_template(&crypto_hkdf_tmpl);
+}
+
+static void __exit crypto_hkdf_exit(void)
+{
+	crypto_unregister_template(&crypto_hkdf_tmpl);
+}
+
+module_init(crypto_hkdf_init);
+module_exit(crypto_hkdf_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>");
+MODULE_DESCRIPTION("Key Derivation Function according to RFC 5869");
+MODULE_ALIAS_CRYPTO("hkdf");
-- 
2.20.1





  parent reply	other threads:[~2019-01-11 19:14 UTC|newest]

Thread overview: 90+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-01-03 14:32 [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image Lee, Chun-Yi
2019-01-03 14:32 ` [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler Lee, Chun-Yi
2019-01-06  8:01   ` Stephan Mueller
2019-01-06  8:25     ` Stephan Mueller
2019-01-07 15:33     ` joeyli
2019-01-07 15:52       ` Stephan Mueller
2019-01-08  5:03         ` Herbert Xu
2019-01-08  7:09           ` Stephan Mueller
2019-01-08 23:54             ` Andy Lutomirski
2019-01-09  0:44               ` James Bottomley
2019-01-09  1:43                 ` Andy Lutomirski
2019-01-09  6:49                   ` James Bottomley
2019-01-09 18:11                     ` joeyli
2019-01-11 15:53                       ` Jarkko Sakkinen
2019-01-09 18:34                     ` Andy Lutomirski
2019-01-09 19:46                       ` James Bottomley
2019-01-09 20:12                         ` Andy Lutomirski
2019-01-09 21:43                           ` James Bottomley
2019-01-09 22:19                             ` Pavel Machek
2019-01-11 16:04                       ` Jarkko Sakkinen
2019-01-11 14:02                   ` Jarkko Sakkinen
2019-01-11 15:28                     ` James Bottomley
2019-01-18 14:33                       ` Jarkko Sakkinen
2019-01-18 20:59                         ` James Bottomley
2019-01-20 16:02                           ` Jarkko Sakkinen
2019-01-09  6:45                 ` Stephan Mueller
2019-01-09  6:58                   ` James Bottomley
2019-01-09  7:05                     ` Stephan Mueller
2019-01-09  8:21                       ` Eric Biggers
2019-01-09 10:17                         ` Stephan Mueller
2019-01-09 17:34                           ` Eric Biggers
2019-01-09 18:18                             ` Stephan Mueller
2019-01-11 19:08                         ` [PATCH 0/6] General Key Derivation Function Support Stephan Müller
2019-01-11 19:09                           ` [PATCH 1/6] crypto: add template handling for RNGs Stephan Müller
2019-01-11 19:10                           ` [PATCH 2/6] crypto: kdf - SP800-108 Key Derivation Function Stephan Müller
2019-01-12  5:27                             ` Eric Biggers
2019-01-14  9:31                               ` Stephan Müller
2019-01-11 19:10                           ` [PATCH 3/6] crypto: kdf - add known answer tests Stephan Müller
2019-01-12  5:26                             ` Eric Biggers
2019-01-14  9:26                               ` Stephan Müller
2019-01-11 19:10                           ` Stephan Müller [this message]
2019-01-12  5:12                             ` [PATCH 4/6] crypto: hkdf - RFC5869 Key Derivation Function Eric Biggers
2019-01-12  9:55                               ` Herbert Xu
2019-01-13  7:56                                 ` Stephan Müller
2019-01-13 16:52                                   ` James Bottomley
2019-01-14  9:30                               ` Stephan Müller
2019-01-14 17:53                                 ` Eric Biggers
2019-01-14 18:44                                   ` Stephan Mueller
2019-01-11 19:10                           ` [PATCH 5/6] crypto: hkdf - add known answer tests Stephan Müller
2019-01-12  5:19                             ` Eric Biggers
2019-01-14  9:25                               ` Stephan Müller
2019-01-14 17:44                                 ` Eric Biggers
2019-01-11 19:11                           ` [PATCH 6/6] crypto: tcrypt - add KDF test invocation Stephan Müller
2019-01-16 11:06                           ` [PATCH v2 0/6] General Key Derivation Function Support Stephan Müller
2019-01-16 11:07                             ` [PATCH v2 1/6] crypto: add template handling for RNGs Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 2/6] crypto: kdf - SP800-108 Key Derivation Function Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 3/6] crypto: kdf - add known answer tests Stephan Müller
2019-01-16 11:08                             ` [PATCH v2 4/6] crypto: hkdf - HMAC-based Extract-and-Expand KDF Stephan Müller
2019-01-16 11:09                             ` [PATCH v2 5/6] crypto: hkdf - add known answer tests Stephan Müller
2019-01-16 11:09                             ` [PATCH v2 6/6] crypto: tcrypt - add KDF test invocation Stephan Müller
2019-01-28 10:07                             ` [PATCH v2 0/6] General Key Derivation Function Support Stephan Mueller
2019-01-30 10:08                               ` Herbert Xu
2019-01-30 14:39                                 ` Stephan Mueller
2019-02-08  7:45                                   ` Herbert Xu
2019-02-08  8:00                                     ` Stephan Mueller
2019-02-08  8:05                                       ` Herbert Xu
2019-02-08  8:17                                         ` Stephan Mueller
2019-02-19  5:44                                           ` Herbert Xu
2019-01-09 15:34                       ` [PATCH 1/5 v2] PM / hibernate: Create snapshot keys handler James Bottomley
2019-01-09  6:27               ` Stephan Mueller
2019-01-03 14:32 ` [PATCH 2/5] PM / hibernate: Generate and verify signature for snapshot image Lee, Chun-Yi
2019-01-06  8:09   ` Stephan Mueller
2019-01-07 18:58   ` Dan Carpenter
2019-01-03 14:32 ` [PATCH 3/5] PM / hibernate: Encrypt " Lee, Chun-Yi
2019-01-06  8:23   ` Stephan Mueller
2019-01-03 14:32 ` [PATCH 4/5 v2] PM / hibernate: Erase the snapshot master key in snapshot pages Lee, Chun-Yi
2019-01-03 14:32 ` [PATCH 5/5 v2] PM / hibernate: An option to request that snapshot image must be authenticated Lee, Chun-Yi
2019-01-06 18:10 ` [PATCH 0/5 v2][RFC] Encryption and authentication for hibernate snapshot image Pavel Machek
2019-01-07 17:37   ` joeyli
2019-01-07 18:07     ` Pavel Machek
2019-01-08 21:41     ` Andy Lutomirski
2019-01-08 23:42       ` Pavel Machek
2019-01-09 16:39       ` joeyli
2019-01-09 16:47         ` Stephan Mueller
2019-01-11 14:29           ` joeyli
2019-01-09 16:51         ` joeyli
2019-01-09 18:47         ` Andy Lutomirski
2019-01-10 15:12           ` joeyli
2019-01-11  1:09             ` Andy Lutomirski
2019-01-11 14:59               ` joeyli

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=2423373.Zd5ThvQH5g@positron.chronox.de \
    --to=smueller@chronox.de \
    --cc=James.Bottomley@hansenpartnership.com \
    --cc=dhowells@redhat.com \
    --cc=ebiggers@kernel.org \
    --cc=ggherdovich@suse.cz \
    --cc=herbert@gondor.apana.org.au \
    --cc=jannh@google.com \
    --cc=joeyli.kernel@gmail.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=luto@amacapital.net \
    --cc=luto@kernel.org \
    --cc=oneukum@suse.com \
    --cc=pavel@ucw.cz \
    --cc=rafael.j.wysocki@intel.com \
    --cc=rdunlap@infradead.org \
    --cc=rjw@rjwysocki.net \
    --cc=yu.c.chen@intel.com \
    --cc=yu.chen.surf@gmail.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).