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 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
next prev parent reply index Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <20190103143227.9138-1-jlee@suse.com> [not found] ` <1894062.aDvIuj92vB@tauon.chronox.de> [not found] ` <20190109082103.GA8586@sol.localdomain> 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
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
Linux-Crypto Archive on lore.kernel.org Archives are clonable: git clone --mirror https://lore.kernel.org/linux-crypto/0 linux-crypto/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 linux-crypto linux-crypto/ https://lore.kernel.org/linux-crypto \ linux-crypto@vger.kernel.org public-inbox-index linux-crypto Example config snippet for mirrors Newsgroup available over NNTP: nntp://nntp.lore.kernel.org/org.kernel.vger.linux-crypto AGPL code for this site: git clone https://public-inbox.org/public-inbox.git