linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Herbert Xu <herbert@gondor.apana.org.au>
To: Stephan Mueller <smueller@chronox.de>, Ted Tso <tytso@mit.edu>,
	andreas.steffen@strongswan.org, sandyinchina@gmail.com,
	linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 2/3] crypto: drbg - Use callback API for random readiness
Date: Tue, 09 Jun 2015 21:55:38 +0800	[thread overview]
Message-ID: <E1Z2K0A-0003YB-5u@gondolin.me.apana.org.au> (raw)
In-Reply-To: 20150609101830.GA10521@gondor.apana.org.au

From: Stephan Mueller <smueller@chronox.de>

The get_blocking_random_bytes API is broken because the wait can
be arbitrarily long (potentially forever) so there is no safe way
of calling it from within the kernel.

This patch replaces it with the new callback API which does not
have this problem.

The patch also removes the entropy buffer registered with the DRBG
handle in favor of stack variables to hold the seed data.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
---

 crypto/drbg.c         |  217 +++++++++++++++++++++++++++++---------------------
 include/crypto/drbg.h |    3 
 2 files changed, 127 insertions(+), 93 deletions(-)

diff --git a/crypto/drbg.c b/crypto/drbg.c
index 04836b4..c6cbf13 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -98,6 +98,7 @@
  */
 
 #include <crypto/drbg.h>
+#include <linux/kernel.h>
 
 /***************************************************************
  * Backend cipher definitions available to DRBG
@@ -190,6 +191,8 @@ static const struct drbg_core drbg_cores[] = {
 #endif /* CONFIG_CRYPTO_DRBG_HMAC */
 };
 
+static int drbg_uninstantiate(struct drbg_state *drbg);
+
 /******************************************************************
  * Generic helper functions
  ******************************************************************/
@@ -1062,20 +1065,32 @@ static void drbg_async_seed(struct work_struct *work)
 	LIST_HEAD(seedlist);
 	struct drbg_state *drbg = container_of(work, struct drbg_state,
 					       seed_work);
-	int ret;
+	unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
+	unsigned char entropy[32];
 
-	get_blocking_random_bytes(drbg->seed_buf, drbg->seed_buf_len);
+	BUG_ON(!entropylen);
+	BUG_ON(entropylen > sizeof(entropy));
+	get_random_bytes(entropy, entropylen);
 
-	drbg_string_fill(&data, drbg->seed_buf, drbg->seed_buf_len);
+	drbg_string_fill(&data, entropy, entropylen);
 	list_add_tail(&data.list, &seedlist);
+
 	mutex_lock(&drbg->drbg_mutex);
-	ret = __drbg_seed(drbg, &seedlist, true);
-	if (!ret && drbg->jent) {
-		crypto_free_rng(drbg->jent);
-		drbg->jent = NULL;
-	}
-	memzero_explicit(drbg->seed_buf, drbg->seed_buf_len);
+
+	/* If nonblocking pool is initialized, deactivate Jitter RNG */
+	crypto_free_rng(drbg->jent);
+	drbg->jent = NULL;
+
+	/* Set seeded to false so that if __drbg_seed fails the
+	 * next generate call will trigger a reseed.
+	 */
+	drbg->seeded = false;
+
+	__drbg_seed(drbg, &seedlist, true);
+
 	mutex_unlock(&drbg->drbg_mutex);
+
+	memzero_explicit(entropy, entropylen);
 }
 
 /*
@@ -1092,7 +1107,9 @@ static void drbg_async_seed(struct work_struct *work)
 static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
 		     bool reseed)
 {
-	int ret = 0;
+	int ret;
+	unsigned char entropy[((32 + 16) * 2)];
+	unsigned int entropylen = drbg_sec_strength(drbg->core->flags);
 	struct drbg_string data1;
 	LIST_HEAD(seedlist);
 
@@ -1108,23 +1125,39 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
 				 drbg->test_data.len);
 		pr_devel("DRBG: using test entropy\n");
 	} else {
+		/*
+		 * Gather entropy equal to the security strength of the DRBG.
+		 * With a derivation function, a nonce is required in addition
+		 * to the entropy. A nonce must be at least 1/2 of the security
+		 * strength of the DRBG in size. Thus, entropy + nonce is 3/2
+		 * of the strength. The consideration of a nonce is only
+		 * applicable during initial seeding.
+		 */
+		BUG_ON(!entropylen);
+		if (!reseed)
+			entropylen = ((entropylen + 1) / 2) * 3;
+		BUG_ON((entropylen * 2) > sizeof(entropy));
+
 		/* Get seed from in-kernel /dev/urandom */
-		get_random_bytes(drbg->seed_buf, drbg->seed_buf_len);
-
-		/* Get seed from Jitter RNG */
-		if (!drbg->jent ||
-		    crypto_rng_get_bytes(drbg->jent,
-					 drbg->seed_buf + drbg->seed_buf_len,
-					 drbg->seed_buf_len)) {
-			drbg_string_fill(&data1, drbg->seed_buf,
-					 drbg->seed_buf_len);
-			pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
-				 drbg->seed_buf_len);
+		get_random_bytes(entropy, entropylen);
+
+		if (!drbg->jent) {
+			drbg_string_fill(&data1, entropy, entropylen);
+			pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+				 entropylen);
 		} else {
-			drbg_string_fill(&data1, drbg->seed_buf,
-					 drbg->seed_buf_len * 2);
-			pr_devel("DRBG: (re)seeding with %zu bytes of entropy\n",
-				 drbg->seed_buf_len * 2);
+			/* Get seed from Jitter RNG */
+			ret = crypto_rng_get_bytes(drbg->jent,
+						   entropy + entropylen,
+						   entropylen);
+			if (ret) {
+				pr_devel("DRBG: jent failed with %d\n", ret);
+				return ret;
+			}
+
+			drbg_string_fill(&data1, entropy, entropylen * 2);
+			pr_devel("DRBG: (re)seeding with %u bytes of entropy\n",
+				 entropylen * 2);
 		}
 	}
 	list_add_tail(&data1.list, &seedlist);
@@ -1146,26 +1179,8 @@ static int drbg_seed(struct drbg_state *drbg, struct drbg_string *pers,
 
 	ret = __drbg_seed(drbg, &seedlist, reseed);
 
-	/*
-	 * Clear the initial entropy buffer as the async call may not overwrite
-	 * that buffer for quite some time.
-	 */
-	memzero_explicit(drbg->seed_buf, drbg->seed_buf_len * 2);
-	if (ret)
-		goto out;
-	/*
-	 * For all subsequent seeding calls, we only need the seed buffer
-	 * equal to the security strength of the DRBG. We undo the calculation
-	 * in drbg_alloc_state.
-	 */
-	if (!reseed)
-		drbg->seed_buf_len = drbg->seed_buf_len / 3 * 2;
-
-	/* Invoke asynchronous seeding unless DRBG is in test mode. */
-	if (!list_empty(&drbg->test_data.list) && !reseed)
-		schedule_work(&drbg->seed_work);
+	memzero_explicit(entropy, entropylen * 2);
 
-out:
 	return ret;
 }
 
@@ -1188,12 +1203,6 @@ static inline void drbg_dealloc_state(struct drbg_state *drbg)
 	drbg->prev = NULL;
 	drbg->fips_primed = false;
 #endif
-	kzfree(drbg->seed_buf);
-	drbg->seed_buf = NULL;
-	if (drbg->jent) {
-		crypto_free_rng(drbg->jent);
-		drbg->jent = NULL;
-	}
 }
 
 /*
@@ -1256,42 +1265,6 @@ static inline int drbg_alloc_state(struct drbg_state *drbg)
 			goto err;
 	}
 
-	/*
-	 * Gather entropy equal to the security strength of the DRBG.
-	 * With a derivation function, a nonce is required in addition
-	 * to the entropy. A nonce must be at least 1/2 of the security
-	 * strength of the DRBG in size. Thus, entropy * nonce is 3/2
-	 * of the strength. The consideration of a nonce is only
-	 * applicable during initial seeding.
-	 */
-	drbg->seed_buf_len = drbg_sec_strength(drbg->core->flags);
-	if (!drbg->seed_buf_len) {
-		ret = -EFAULT;
-		goto err;
-	}
-	/*
-	 * Ensure we have sufficient buffer space for initial seed which
-	 * consists of the seed from get_random_bytes and the Jitter RNG.
-	 */
-	drbg->seed_buf_len = ((drbg->seed_buf_len + 1) / 2) * 3;
-	drbg->seed_buf = kzalloc(drbg->seed_buf_len * 2, GFP_KERNEL);
-	if (!drbg->seed_buf)
-		goto err;
-
-	INIT_WORK(&drbg->seed_work, drbg_async_seed);
-
-	drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
-	if(IS_ERR(drbg->jent))
-	{
-		pr_info("DRBG: could not allocate Jitter RNG handle for seeding\n");
-		/*
-		 * As the Jitter RNG is a module that may not be present, we
-		 * continue with the operation and do not fully tie the DRBG
-		 * to the Jitter RNG.
-		 */
-		drbg->jent = NULL;
-	}
-
 	return 0;
 
 err:
@@ -1467,6 +1440,47 @@ static int drbg_generate_long(struct drbg_state *drbg,
 	return 0;
 }
 
+static void drbg_schedule_async_seed(struct random_ready_callback *rdy)
+{
+	struct drbg_state *drbg = container_of(rdy, struct drbg_state,
+					       random_ready);
+
+	schedule_work(&drbg->seed_work);
+}
+
+static int drbg_prepare_hrng(struct drbg_state *drbg)
+{
+	int err;
+
+	/* We do not need an HRNG in test mode. */
+	if (list_empty(&drbg->test_data.list))
+		return 0;
+
+	INIT_WORK(&drbg->seed_work, drbg_async_seed);
+
+	drbg->random_ready.owner = THIS_MODULE;
+	drbg->random_ready.func = drbg_schedule_async_seed;
+
+	err = add_random_ready_callback(&drbg->random_ready);
+
+	switch (err) {
+	case 0:
+		break;
+
+	case -EALREADY:
+		err = 0;
+		/* fall through */
+
+	default:
+		drbg->random_ready.func = NULL;
+		return err;
+	}
+
+	drbg->jent = crypto_alloc_rng("jitterentropy_rng", 0, 0);
+
+	return err;
+}
+
 /*
  * DRBG instantiation function as required by SP800-90A - this function
  * sets up the DRBG handle, performs the initial seeding and all sanity
@@ -1517,15 +1531,25 @@ static int drbg_instantiate(struct drbg_state *drbg, struct drbg_string *pers,
 		if (drbg->d_ops->crypto_init(drbg))
 			goto err;
 
+		ret = drbg_prepare_hrng(drbg);
+		if (ret)
+			goto free_everything;
+
+		if (IS_ERR(drbg->jent)) {
+			ret = PTR_ERR(drbg->jent);
+			drbg->jent = NULL;
+			if (fips_enabled || ret != -ENOENT)
+				goto free_everything;
+			pr_info("DRBG: Continuing without Jitter RNG\n");
+		}
+
 		reseed = false;
 	}
 
 	ret = drbg_seed(drbg, pers, reseed);
 
-	if (ret && !reseed) {
-		drbg->d_ops->crypto_fini(drbg);
-		goto err;
-	}
+	if (ret && !reseed)
+		goto free_everything;
 
 	mutex_unlock(&drbg->drbg_mutex);
 	return ret;
@@ -1535,6 +1559,11 @@ err:
 unlock:
 	mutex_unlock(&drbg->drbg_mutex);
 	return ret;
+
+free_everything:
+	mutex_unlock(&drbg->drbg_mutex);
+	drbg_uninstantiate(drbg);
+	return ret;
 }
 
 /*
@@ -1548,7 +1577,13 @@ unlock:
  */
 static int drbg_uninstantiate(struct drbg_state *drbg)
 {
-	cancel_work_sync(&drbg->seed_work);
+	if (drbg->random_ready.func) {
+		del_random_ready_callback(&drbg->random_ready);
+		cancel_work_sync(&drbg->seed_work);
+		crypto_free_rng(drbg->jent);
+		drbg->jent = NULL;
+	}
+
 	if (drbg->d_ops)
 		drbg->d_ops->crypto_fini(drbg);
 	drbg_dealloc_state(drbg);
diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h
index c3f208d..fad6450 100644
--- a/include/crypto/drbg.h
+++ b/include/crypto/drbg.h
@@ -121,12 +121,11 @@ struct drbg_state {
 	unsigned char *prev;	/* FIPS 140-2 continuous test value */
 #endif
 	struct work_struct seed_work;	/* asynchronous seeding support */
-	u8 *seed_buf;			/* buffer holding the seed */
-	size_t seed_buf_len;
 	struct crypto_rng *jent;
 	const struct drbg_state_ops *d_ops;
 	const struct drbg_core *core;
 	struct drbg_string test_data;
+	struct random_ready_callback random_ready;
 };
 
 static inline __u8 drbg_statelen(struct drbg_state *drbg)

      parent reply	other threads:[~2015-06-09 13:55 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-06  2:16 [PATCH] crypto: use list to stage async seeding requests Stephan Mueller
2015-06-09  6:49 ` Herbert Xu
2015-06-09 10:18   ` [PATCH 0/3] random: Replace kernel blocking API with callback API Herbert Xu
2015-06-09 10:19     ` [PATCH 1/3] random: Add callback API for random pool readiness Herbert Xu
2015-06-09 10:19     ` [PATCH 2/3] crypto: drbg - Use callback API for random readiness Herbert Xu
2015-06-09 12:23       ` Stephan Mueller
2015-06-09 13:24         ` Herbert Xu
2015-06-09 10:19     ` [PATCH 3/3] random: Remove kernel blocking API Herbert Xu
2015-06-09 10:21     ` [PATCH 2/3] crypto: drbg - Use callback API for random readiness Herbert Xu
2015-06-09 13:55     ` Herbert Xu [this message]

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=E1Z2K0A-0003YB-5u@gondolin.me.apana.org.au \
    --to=herbert@gondor.apana.org.au \
    --cc=andreas.steffen@strongswan.org \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sandyinchina@gmail.com \
    --cc=smueller@chronox.de \
    --cc=tytso@mit.edu \
    /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).