From patchwork Wed Jan 16 11:08:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Stephan Mueller X-Patchwork-Id: 10765745 X-Patchwork-Delegate: herbert@gondor.apana.org.au Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D9414186E for ; Wed, 16 Jan 2019 11:12:01 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C6C5C2D344 for ; Wed, 16 Jan 2019 11:12:01 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C3BB02D166; Wed, 16 Jan 2019 11:12:01 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 21F5D2D929 for ; Wed, 16 Jan 2019 11:12:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2392331AbfAPLLs (ORCPT ); Wed, 16 Jan 2019 06:11:48 -0500 Received: from mo4-p02-ob.smtp.rzone.de ([85.215.255.82]:18160 "EHLO mo4-p02-ob.smtp.rzone.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2392367AbfAPLLs (ORCPT ); Wed, 16 Jan 2019 06:11:48 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; t=1547637103; s=strato-dkim-0002; d=chronox.de; h=References:In-Reply-To:Message-ID:Date:Subject:Cc:To:From: X-RZG-CLASS-ID:X-RZG-AUTH:From:Subject:Sender; bh=1Sd5yDNPiSJSxhy4ySS6exTR+m+Hq7U1h1jNBS7Nqmo=; b=kQ88Ul4U4RzOmbAvqIH8r/0nd47iDScwtod6Cxul0x01v+bx2flDcWS7r7Ux5C7nVG dbDJVDJ6jsJtV9ICyfzMX4ctLyLLPyK8aa/Nej5rwFCIkqb6IxlYvRb9vbMIwJGVYiY4 KrzcVBiq4DAcr7t0sPYzjgpVgZt/K3X/a+xRry/mQu0ojIxhPue1juelGR/iR221qmiB IUEBzmPS2B6oESSMTaVa1y2fPRdERhjLdbw4Zp2pLmKJJBkZlF+gVOvMp9obOYvOc3VX iJU+St+MaJdqzCUHn8yZEBhIbVRL+pY2Ug2qFMZ0nskcFuiTkSOVTXhWDS9xdpSCfHsd VQqQ== X-RZG-AUTH: ":P2ERcEykfu11Y98lp/T7+hdri+uKZK8TKWEqNyiHySGSa9k9yWgdNs16dfA/c7fW145n" X-RZG-CLASS-ID: mo00 Received: from positron.chronox.de by smtp.strato.de (RZmta 44.9 AUTH) with ESMTPSA id 309bcfv0GBBQ4NV (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (curve secp521r1 with 521 ECDH bits, eq. 15360 bits RSA)) (Client did not present a certificate); Wed, 16 Jan 2019 12:11:26 +0100 (CET) From: Stephan =?iso-8859-1?q?M=FCller?= To: Herbert Xu Cc: Eric Biggers , James Bottomley , Andy Lutomirski , "Lee, Chun-Yi" , "Rafael J . Wysocki" , Pavel Machek , linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, keyrings@vger.kernel.org, "Rafael J. Wysocki" , Chen Yu , Oliver Neukum , Ryan Chen , David Howells , Giovanni Gherdovich , Randy Dunlap , Jann Horn , Andy Lutomirski , linux-crypto@vger.kernel.org Subject: [PATCH v2 4/6] crypto: hkdf - HMAC-based Extract-and-Expand KDF Date: Wed, 16 Jan 2019 12:08:49 +0100 Message-ID: <3767574.eZANq8FNrB@positron.chronox.de> In-Reply-To: <2082192.jPI8ve1O8G@positron.chronox.de> References: <20190103143227.9138-1-jlee@suse.com> <9733066.Vrs4h5eWcW@positron.chronox.de> <2082192.jPI8ve1O8G@positron.chronox.de> MIME-Version: 1.0 Sender: linux-crypto-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-crypto@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The HMAC-based Extract-and-Expand Key Derivation Function is conformant to RFC5869. The extraction phase can be invoked separately from the expansion phase. This implies that the once a key is set and thus the extraction phase was applied, the expansion phase can be invoked multiple times. The HKDF implementation does not restrict the type of keyed message digest it is instantiated with. RFC5869 specifies HMAC-SHA as the only keyed message digest, though. From a cryptographic point of view, using other keyed message digests would result in an equally strong KDF. Signed-off-by: Stephan Mueller --- crypto/Kconfig | 6 ++ crypto/Makefile | 1 + crypto/hkdf.c | 272 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 279 insertions(+) create mode 100644 crypto/hkdf.c diff --git a/crypto/Kconfig b/crypto/Kconfig index 7c0336c9ba9c..c25b2033321a 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -568,6 +568,12 @@ config CRYPTO_KDF_SP800108 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 (HKDF)" + select CRYPTO_RNG + help + Support for KDF conformant to RFC5869. + config CRYPTO_XCBC tristate "XCBC support" select CRYPTO_HASH diff --git a/crypto/Makefile b/crypto/Makefile index eead7ec9fd8e..80363b8cbf6c 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_SP800108) += kdf_sp800108.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..2f392c71c85b --- /dev/null +++ b/crypto/hkdf.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * HMAC-based Extract-and-Expand Key Derivation Function (conformant to RFC5869) + * + * Copyright (C) 2019, Stephan Mueller + */ + +/* + * The HKDF extract phase is applied with crypto_rng_reset(). + * The HKDF expand phase is applied with crypto_rng_generate(). + */ + +#include +#include +#include +#include +#include +#include + +struct crypto_hkdf_ctx { + struct crypto_shash *kmd; +}; + +#define CRYPTO_HKDF_MAX_DIGESTSIZE 64 + +static inline void crypto_kdf_init_desc(struct shash_desc *desc, + struct crypto_shash *kmd) +{ + desc->tfm = kmd; + desc->flags = 0; +} + +/* + * HKDF expand phase + */ +static int crypto_hkdf_generate(struct crypto_rng *rng, + const u8 *info, unsigned int infolen, + u8 *dst, unsigned int dlen) +{ + const struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng)); + struct crypto_shash *kmd = ctx->kmd; + SHASH_DESC_ON_STACK(desc, kmd); + const unsigned int h = crypto_shash_digestsize(kmd); + int err = 0; + u8 *dst_orig = dst; + const u8 *prev = NULL; + u8 ctr = 0x01; + + if (dlen > h * 255) + return -EINVAL; + + crypto_kdf_init_desc(desc, 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; + } + + 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; + } + + 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 a word 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) +{ + const struct crypto_hkdf_ctx *ctx = crypto_tfm_ctx(crypto_rng_tfm(rng)); + struct crypto_shash *kmd = ctx->kmd; + SHASH_DESC_ON_STACK(desc, kmd); + u32 saltlen; + unsigned int h = crypto_shash_digestsize(kmd); + int err; + const u8 null_salt[CRYPTO_HKDF_MAX_DIGESTSIZE] = { 0 }; + u8 prk[CRYPTO_HKDF_MAX_DIGESTSIZE]; + + if (slen < sizeof(saltlen)) + return -EINVAL; + + saltlen = get_unaligned((u32 *)seed); + + seed += sizeof(saltlen); + slen -= sizeof(saltlen); + + if (slen < saltlen) + return -EINVAL; + + crypto_kdf_init_desc(desc, kmd); + + /* Set the salt as HMAC key */ + if (saltlen) + err = crypto_shash_setkey(kmd, seed, saltlen); + else + err = crypto_shash_setkey(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(kmd, prk, h); + +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 *kmd; + + kmd = crypto_spawn_shash(spawn); + if (IS_ERR(kmd)) + return PTR_ERR(kmd); + + ctx->kmd = 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->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); + + /* Require a keyed message digest */ + if (!salg->setkey) + return -EOPNOTSUPP; + + ds = salg->digestsize; + /* Hashes with no digest size are not allowed for KDFs. */ + if (!ds || WARN_ON(ds > CRYPTO_HKDF_MAX_DIGESTSIZE)) + return -EOPNOTSUPP; + + 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 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_generate; + inst->alg.seed = crypto_hkdf_seed; + + inst->alg.base.cra_init = crypto_hkdf_init_tfm; + inst->alg.base.cra_exit = crypto_hkdf_exit_tfm; + inst->alg.base.cra_ctxsize = sizeof(struct crypto_hkdf_ctx); + + inst->free = crypto_kdf_free; + + err = rng_register_instance(tmpl, inst); + + if (err) { + crypto_drop_spawn(rng_instance_ctx(inst)); +free_inst: + kfree(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 v2"); +MODULE_AUTHOR("Stephan Mueller "); +MODULE_DESCRIPTION("HKDF HMAC-based Extract-and-Expand Key Derivation Function (conformant to RFC5869)"); +MODULE_ALIAS_CRYPTO("hkdf");