From: Nicolai Stange <nstange@suse.de>
To: Herbert Xu <herbert@gondor.apana.org.au>,
"David S. Miller" <davem@davemloft.net>
Cc: "Stephan Müller" <smueller@chronox.de>,
"Hannes Reinecke" <hare@suse.de>, "Torsten Duwe" <duwe@suse.de>,
"David Howells" <dhowells@redhat.com>,
"Jarkko Sakkinen" <jarkko@kernel.org>,
linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org,
keyrings@vger.kernel.org, "Nicolai Stange" <nstange@suse.de>
Subject: [PATCH v3 09/15] crypto: dh - implement private key generation primitive for ffdheXYZ(dh)
Date: Wed, 2 Feb 2022 11:40:06 +0100 [thread overview]
Message-ID: <20220202104012.4193-10-nstange@suse.de> (raw)
In-Reply-To: <20220202104012.4193-1-nstange@suse.de>
The support for NVME in-band authentication currently in the works ([1])
needs to generate ephemeral DH keys for use with the RFC 7919 safe-prime
FFDHE groups.
In analogy to ECDH and its ecc_gen_privkey(), implement a
dh_safe_prime_gen_privkey() and invoke it from the ffdheXYZ(dh) templates'
common ->set_secret(), i.e. dh_safe_prime_set_secret(), in case the input
->key_size is zero.
As the RFC 7919 FFDHE groups are classified as approved safe-prime groups
by SP800-56Arev3, it's worthwhile to make the new
dh_safe_prime_gen_privkey() to follow the approach specified in
SP800-56Arev3, sec. 5.6.1.1.3 ("Key-Pair Generation Using Extra Random
Bits") in order to achieve conformance.
SP800-56Arev3 specifies a lower as well as an upper bound on the generated
key's length:
- it must be >= two times the maximum supported security strength of
the group in question and
- it must be <= the length of the domain parameter Q.
For any safe-prime group Q = (P - 1)/2 by definition and the individual
maximum supported security strengths as specified by SP800-56Arev3 have
been made available as part of the FFDHE dh_safe_prime definitions
introduced with a previous patch. Make dh_safe_prime_gen_privkey() pick
twice the maximum supported strength rounded up to the next power of two
for the output key size. This choice respects both, the lower and upper
bounds given by SP800-90Arev3 for any of the approved safe-prime groups and
is also in line with the NVME base spec 2.0, which requires the key size to
be >= 256bits.
[1] https://lore.kernel.org/r/20211202152358.60116-1-hare@suse.de
Signed-off-by: Nicolai Stange <nstange@suse.de>
---
crypto/Kconfig | 1 +
crypto/dh.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 138 insertions(+), 3 deletions(-)
diff --git a/crypto/Kconfig b/crypto/Kconfig
index e041cabe93da..c47fe5da1440 100644
--- a/crypto/Kconfig
+++ b/crypto/Kconfig
@@ -235,6 +235,7 @@ config CRYPTO_DH_RFC7919_GROUPS
bool "Support for RFC 7919 FFDHE group parameters"
default n
depends on CRYPTO_DH
+ select CRYPTO_RNG_DEFAULT
help
Provide support for RFC 7919 FFDHE group parameters. If unsure, say N.
diff --git a/crypto/dh.c b/crypto/dh.c
index d0adb1705fe7..869a0476e5e2 100644
--- a/crypto/dh.c
+++ b/crypto/dh.c
@@ -10,6 +10,7 @@
#include <crypto/internal/kpp.h>
#include <crypto/kpp.h>
#include <crypto/dh.h>
+#include <crypto/rng.h>
#include <linux/mpi.h>
struct dh_ctx {
@@ -315,6 +316,128 @@ static void dh_safe_prime_exit_tfm(struct crypto_kpp *tfm)
crypto_free_kpp(tfm_ctx->dh_tfm);
}
+static u64 __add_u64_to_be(__be64 *dst, unsigned int n, u64 val)
+{
+ unsigned int i;
+
+ for (i = n; val && i > 0; --i) {
+ u64 tmp = be64_to_cpu(dst[i - 1]);
+
+ tmp += val;
+ val = tmp >= val ? 0 : 1;
+ dst[i - 1] = cpu_to_be64(tmp);
+ }
+
+ return val;
+}
+
+static void *dh_safe_prime_gen_privkey(const struct dh_safe_prime *safe_prime,
+ unsigned int *key_size)
+{
+ unsigned int n, oversampling_size;
+ __be64 *key;
+ int err;
+ u64 h, o;
+
+ /*
+ * Generate a private key following NIST SP800-56Ar3,
+ * sec. 5.6.1.1.1 and 5.6.1.1.3 resp..
+ *
+ * 5.6.1.1.1: choose key length N such that
+ * 2 * ->max_strength <= N <= log2(q) + 1 = ->p_size * 8 - 1
+ * with q = (p - 1) / 2 for the safe-prime groups.
+ * Choose the lower bound's next power of two for N in order to
+ * avoid excessively large private keys while still
+ * maintaining some extra reserve beyond the bare minimum in
+ * most cases. Note that for each entry in safe_prime_groups[],
+ * the following holds for such N:
+ * - N >= 256, in particular it is a multiple of 2^6 = 64
+ * bits and
+ * - N < log2(q) + 1, i.e. N respects the upper bound.
+ */
+ n = roundup_pow_of_two(2 * safe_prime->max_strength);
+ WARN_ON_ONCE(n & ((1u << 6) - 1));
+ n >>= 6; /* Convert N into units of u64. */
+
+ /*
+ * Reserve one extra u64 to hold the extra random bits
+ * required as per 5.6.1.1.3.
+ */
+ oversampling_size = (n + 1) * sizeof(__be64);
+ key = kmalloc(oversampling_size, GFP_KERNEL);
+ if (!key)
+ return ERR_PTR(-ENOMEM);
+
+ /*
+ * 5.6.1.1.3, step 3 (and implicitly step 4): obtain N + 64
+ * random bits and interpret them as a big endian integer.
+ */
+ err = -EFAULT;
+ if (crypto_get_default_rng())
+ goto out_err;
+
+ err = crypto_rng_get_bytes(crypto_default_rng, (u8 *)key,
+ oversampling_size);
+ crypto_put_default_rng();
+ if (err)
+ goto out_err;
+
+ /*
+ * 5.6.1.1.3, step 5 is implicit: 2^N < q and thus,
+ * M = min(2^N, q) = 2^N.
+ *
+ * For step 6, calculate
+ * key = (key[] mod (M - 1)) + 1 = (key[] mod (2^N - 1)) + 1.
+ *
+ * In order to avoid expensive divisions, note that
+ * 2^N mod (2^N - 1) = 1 and thus, for any integer h,
+ * 2^N * h mod (2^N - 1) = h mod (2^N - 1) always holds.
+ * The big endian integer key[] composed of n + 1 64bit words
+ * may be written as key[] = h * 2^N + l, with h = key[0]
+ * representing the 64 most significant bits and l
+ * corresponding to the remaining 2^N bits. With the remark
+ * from above,
+ * h * 2^N + l mod (2^N - 1) = l + h mod (2^N - 1).
+ * As both, l and h are less than 2^N, their sum after
+ * this first reduction is guaranteed to be <= 2^(N + 1) - 2.
+ * Or equivalently, that their sum can again be written as
+ * h' * 2^N + l' with h' now either zero or one and if one,
+ * then l' <= 2^N - 2. Thus, all bits at positions >= N will
+ * be zero after a second reduction:
+ * h' * 2^N + l' mod (2^N - 1) = l' + h' mod (2^N - 1).
+ * At this point, it is still possible that
+ * l' + h' = 2^N - 1, i.e. that l' + h' mod (2^N - 1)
+ * is zero. This condition will be detected below by means of
+ * the final increment overflowing in this case.
+ */
+ h = be64_to_cpu(key[0]);
+ h = __add_u64_to_be(key + 1, n, h);
+ h = __add_u64_to_be(key + 1, n, h);
+ WARN_ON_ONCE(h);
+
+ /* Increment to obtain the final result. */
+ o = __add_u64_to_be(key + 1, n, 1);
+ /*
+ * The overflow bit o from the increment is either zero or
+ * one. If zero, key[1:n] holds the final result in big-endian
+ * order. If one, key[1:n] is zero now, but needs to be set to
+ * one, c.f. above.
+ */
+ if (o)
+ key[n] = cpu_to_be64(1);
+
+ /* n is in units of u64, convert to bytes. */
+ *key_size = n << 3;
+ /* Strip the leading extra __be64, which is (virtually) zero by now. */
+ memmove(key, &key[1], *key_size);
+
+ return key;
+
+out_err:
+ kfree_sensitive(key);
+ return ERR_PTR(err);
+}
+
static int dh_safe_prime_set_secret(struct crypto_kpp *tfm, const void *buffer,
unsigned int len)
{
@@ -322,7 +445,7 @@ static int dh_safe_prime_set_secret(struct crypto_kpp *tfm, const void *buffer,
dh_safe_prime_instance_ctx(tfm);
struct dh_safe_prime_tfm_ctx *tfm_ctx = kpp_tfm_ctx(tfm);
struct dh params;
- void *buf;
+ void *buf = NULL, *key = NULL;
unsigned int buf_size;
int err;
@@ -338,10 +461,20 @@ static int dh_safe_prime_set_secret(struct crypto_kpp *tfm, const void *buffer,
params.g = safe_prime_g;
params.g_size = sizeof(safe_prime_g);
+ if (!params.key_size) {
+ key = dh_safe_prime_gen_privkey(inst_ctx->safe_prime,
+ ¶ms.key_size);
+ if (IS_ERR(key))
+ return PTR_ERR(key);
+ params.key = key;
+ }
+
buf_size = crypto_dh_key_len(¶ms);
buf = kmalloc(buf_size, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
+ if (!buf) {
+ err = -ENOMEM;
+ goto out;
+ }
err = crypto_dh_encode_key(buf, buf_size, ¶ms);
if (err)
@@ -350,6 +483,7 @@ static int dh_safe_prime_set_secret(struct crypto_kpp *tfm, const void *buffer,
err = crypto_kpp_set_secret(tfm_ctx->dh_tfm, buf, buf_size);
out:
kfree_sensitive(buf);
+ kfree_sensitive(key);
return err;
}
--
2.26.2
next prev parent reply other threads:[~2022-02-02 10:41 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-02-02 10:39 [PATCH v3 00/15] crypto: dh - infrastructure for NVM in-band auth and FIPS conformance Nicolai Stange
2022-02-02 10:39 ` [PATCH v3 01/15] crypto: kpp - provide support for KPP template instances Nicolai Stange
2022-02-02 10:39 ` [PATCH v3 02/15] crypto: kpp - provide support for KPP spawns Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 03/15] crypto: dh - remove struct dh's ->q member Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 04/15] crypto: dh - constify struct dh's pointer members Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 05/15] crypto: dh - split out deserialization code from crypto_dh_decode() Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 06/15] crypto: dh - introduce common code for built-in safe-prime group support Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 07/15] crypto: dh - implement ffdheXYZ(dh) templates Nicolai Stange
2022-02-11 9:09 ` Herbert Xu
2022-02-14 8:38 ` Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 08/15] crypto: testmgr - add known answer tests for " Nicolai Stange
2022-02-02 10:40 ` Nicolai Stange [this message]
2022-02-02 10:40 ` [PATCH v3 10/15] crypto: testmgr - add keygen " Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 11/15] crypto: dh - allow for passing NULL to the ffdheXYZ(dh)s' ->set_secret() Nicolai Stange
2022-02-11 9:09 ` Herbert Xu
2022-02-14 8:53 ` Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 12/15] crypto: api - allow algs only in specific constructions in FIPS mode Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 13/15] crypto: dh - disallow plain "dh" usage " Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 14/15] lib/mpi: export mpi_rshift Nicolai Stange
2022-02-02 10:40 ` [PATCH v3 15/15] crypto: dh - calculate Q from P for the full public key verification Nicolai Stange
2022-02-03 17:11 ` [PATCH v3 00/15] crypto: dh - infrastructure for NVM in-band auth and FIPS conformance Stephan Mueller
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=20220202104012.4193-10-nstange@suse.de \
--to=nstange@suse.de \
--cc=davem@davemloft.net \
--cc=dhowells@redhat.com \
--cc=duwe@suse.de \
--cc=hare@suse.de \
--cc=herbert@gondor.apana.org.au \
--cc=jarkko@kernel.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--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 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).