diff mbox

[RFC] crypto: drbg - lower reseed threshold if seed source is degraded

Message ID 1546948.ChzZZVGUza@tachyon.chronox.de (mailing list archive)
State Changes Requested
Delegated to: Herbert Xu
Headers show

Commit Message

Stephan Mueller June 6, 2015, 10:04 p.m. UTC
Hi,

may I ask for review of the following patch. I would like particular feedback
to the initial threshold value, which the patch currently sets to 50 (requests
by a caller to the DRBG for random numbers). Thank you.

I am not sure about this value because there are two conflicting issues
revolving around this value:

1. We have to ensure that the DRBG has a sufficiently entropic internal state.
That would mean, I should set that value as low as possible.

2. This is the more problematic one: when considering information theory, if
you draw from a DRNG (which the nonblocking pool is in the worst case -- and
the boot time discussed below is our worst case) that is not fully seeded, you
reduce the entropy in that DRNG (contrary to conventional wisdom). For the
discussion, let us assume the worst case that there is coming in one bit of
entropy at a time into the discussed DRNG. In between the addition of each bit
of entropy, an attacker can access the DRNG (i.e. the SHA1 output of the
nonblocking_pool). When only one bit of entropy is added to the
nonblocking_pool, the attack complexity would be 1 bit. When an attacker would
access the nonblocking_pool after each received bit, in the worst case, the
attack complexity is not 2**128 but rather 256 (i.e. 1 bit for each individual
attack between the addition of one new bit of entropy). So, the total attack
complexity is the sum of the individual attack complexities (i.e. the
complexity added after the previous attack is performed). This issue is
aggravated by the nonblocking pool as the entropy counter used for declaring
that the threshold defining the that nonblocking pool is initialized is never
decreased by requests, but only increased. So, with that issue in mind, we
want to set the reseed threshold of the DRBG to rather a higher level.

Thus I am struggling to find the right(TM) initial value for the reseeding
threshold.

Or maybe you can tell me that there is no need for the patch to begin with as
the initial seed plus the async request to the nonblocking pool is good as is.
:-)

Note, this patch is on top of the patch updating the async reseeding sent out 
yesterday.

---8<---
As required by SP800-90A, the DRBG implements are reseeding threshold.
This threshold is at 2**48 (64 bit) and 2**32 bit (32 bit) as
implemented in drbg_max_requests.

With the recently introduced changes, the DRBG is now always used as a
stdrng which is initialized very early in the boot cycle. To ensure that
sufficient entropy is present, the Jitter RNG is added to even provide
entropy at early boot time.

However, the 2nd seed source, the nonblocking pool, is usually
degraded at that time. Therefore, the DRBG is seeded with the Jitter RNG
(which I believe contains good entropy, which however is questioned by
others) and is seeded with a degradded nonblocking pool. This seed is
now used for quasi the lifetime of the system (2**48 requests is a lot).

The patch now changes the reseed threshold as follows: up until the time
the DRBG obtains a seed from a fully iniitialized nonblocking pool, the
reseeding threshold is lowered such that the DRBG is forced to reseed
itself resonably often. Once it obtains the seed from a fully
initialized nonblocking pool, the reseed threshold is set to the value
required by SP800-90A.

Signed-off-by: Stephan Mueller <smueller@chronox.de>
---
 crypto/drbg.c         | 35 ++++++++++++++++++++++++++---------
 include/crypto/drbg.h |  1 +
 2 files changed, 27 insertions(+), 9 deletions(-)

Comments

Herbert Xu June 9, 2015, 2:25 p.m. UTC | #1
On Sun, Jun 07, 2015 at 12:04:17AM +0200, Stephan Mueller wrote:
> Hi,
> 
> may I ask for review of the following patch. I would like particular feedback
> to the initial threshold value, which the patch currently sets to 50 (requests
> by a caller to the DRBG for random numbers). Thank you.
> 
> I am not sure about this value because there are two conflicting issues
> revolving around this value:
> 
> 1. We have to ensure that the DRBG has a sufficiently entropic internal state.
> That would mean, I should set that value as low as possible.
> 
> 2. This is the more problematic one: when considering information theory, if
> you draw from a DRNG (which the nonblocking pool is in the worst case -- and
> the boot time discussed below is our worst case) that is not fully seeded, you
> reduce the entropy in that DRNG (contrary to conventional wisdom). For the
> discussion, let us assume the worst case that there is coming in one bit of
> entropy at a time into the discussed DRNG. In between the addition of each bit
> of entropy, an attacker can access the DRNG (i.e. the SHA1 output of the
> nonblocking_pool). When only one bit of entropy is added to the
> nonblocking_pool, the attack complexity would be 1 bit. When an attacker would
> access the nonblocking_pool after each received bit, in the worst case, the
> attack complexity is not 2**128 but rather 256 (i.e. 1 bit for each individual
> attack between the addition of one new bit of entropy). So, the total attack
> complexity is the sum of the individual attack complexities (i.e. the
> complexity added after the previous attack is performed). This issue is
> aggravated by the nonblocking pool as the entropy counter used for declaring
> that the threshold defining the that nonblocking pool is initialized is never
> decreased by requests, but only increased. So, with that issue in mind, we
> want to set the reseed threshold of the DRBG to rather a higher level.
> 
> Thus I am struggling to find the right(TM) initial value for the reseeding
> threshold.
> 
> Or maybe you can tell me that there is no need for the patch to begin with as
> the initial seed plus the async request to the nonblocking pool is good as is.
> :-)
> 
> Note, this patch is on top of the patch updating the async reseeding sent out 
> yesterday.

Well it makes perfect sense if you don't trust Jitter RNG to return
the amount of entropy it claims to return :)

Anyway, I'm happy to apply this.  However, the patch is corrupted
so please resend it without the white-space damage/line wrapping.

Thanks,
Stephan Mueller June 9, 2015, 3:22 p.m. UTC | #2
Am Dienstag, 9. Juni 2015, 22:25:25 schrieb Herbert Xu:

Hi Herbert,

> 
> Anyway, I'm happy to apply this.  However, the patch is corrupted
> so please resend it without the white-space damage/line wrapping.

As this patch would clash with the async seeding patch, shall I develop this 
patch on top of your async patch set?
> 
> Thanks,
Herbert Xu June 9, 2015, 3:26 p.m. UTC | #3
On Tue, Jun 09, 2015 at 05:22:32PM +0200, Stephan Mueller wrote:
> Am Dienstag, 9. Juni 2015, 22:25:25 schrieb Herbert Xu:
> 
> Hi Herbert,
> 
> > 
> > Anyway, I'm happy to apply this.  However, the patch is corrupted
> > so please resend it without the white-space damage/line wrapping.
> 
> As this patch would clash with the async seeding patch, shall I develop this 
> patch on top of your async patch set?

Either way is fine.  I can fix it up when applying as they don't
conceptually conflict.

Thanks,
Stephan Mueller June 9, 2015, 3:27 p.m. UTC | #4
Am Dienstag, 9. Juni 2015, 23:26:05 schrieb Herbert Xu:

Hi Herbert,

> On Tue, Jun 09, 2015 at 05:22:32PM +0200, Stephan Mueller wrote:
> > Am Dienstag, 9. Juni 2015, 22:25:25 schrieb Herbert Xu:
> > 
> > Hi Herbert,
> > 
> > > Anyway, I'm happy to apply this.  However, the patch is corrupted
> > > so please resend it without the white-space damage/line wrapping.
> > 
> > As this patch would clash with the async seeding patch, shall I develop
> > this patch on top of your async patch set?
> 
> Either way is fine.  I can fix it up when applying as they don't
> conceptually conflict.

I see one: the reset of the threshold to the "normal" value must happen at two 
places: the one place is in the async handler. The 2nd place is when the 
request to random.c returns 0 (i.e. fully initialized). The 2nd location is 
different between both code bases.
> 
> Thanks,
Herbert Xu June 9, 2015, 3:33 p.m. UTC | #5
On Tue, Jun 09, 2015 at 05:27:46PM +0200, Stephan Mueller wrote:
>
> I see one: the reset of the threshold to the "normal" value must happen at two 
> places: the one place is in the async handler. The 2nd place is when the 
> request to random.c returns 0 (i.e. fully initialized). The 2nd location is 
> different between both code bases.

Right but after the callback patch there should be only one location
where you reset it to normal, namely in the async handler.  The
other place would only set it to the lower value if the pool is
not ready yet.

Cheers,
diff mbox

Patch

diff --git a/crypto/drbg.c b/crypto/drbg.c
index 3fed67e..0ea4d3c 100644
--- a/crypto/drbg.c
+++ b/crypto/drbg.c
@@ -1075,10 +1075,16 @@  static void drbg_async_seed(void *private)
 	mutex_lock(&drbg->drbg_mutex);
 	ret = __drbg_seed(drbg, &seedlist, true);
 
-	/* If nonblocking pool is initialized, deactivate Jitter RNG */
-	if (!ret && drbg->jent) {
-		crypto_free_rng(drbg->jent);
-		drbg->jent = NULL;
+	/*
+	 * If nonblocking pool is initialized: set reseeding threshold to
+	 * regular threshold and deactivate Jitter RNG.
+	 */
+	if (!ret) {
+		drbg->reseed_threshold = drbg_max_requests(drbg);
+		if (drbg->jent) {
+			crypto_free_rng(drbg->jent);
+			drbg->jent = NULL;
+		}
 	}
 	memzero_explicit(entropy, entropylen);
 	mutex_unlock(&drbg->drbg_mutex);
@@ -1157,10 +1163,16 @@  static int drbg_seed(struct drbg_state *drbg, struct 
drbg_string *pers,
 				 entropylen * 2);
 		}
 
-		/* If nonblocking pool is initialized, deactivate Jitter RNG 
*/
-		if (!ret && drbg->jent) {
-			crypto_free_rng(drbg->jent);
-			drbg->jent = NULL;
+		/*
+		 * If nonblocking pool is initialized: set reseeding threshold
+		 * to regular threshold and deactivate Jitter RNG.
+		 */
+		if (!ret) {
+			drbg->reseed_threshold = drbg_max_requests(drbg);
+			if (drbg->jent) {
+				crypto_free_rng(drbg->jent);
+				drbg->jent = NULL;
+			}
 		}
 	}
 	list_add_tail(&data1.list, &seedlist);
@@ -1357,7 +1369,7 @@  static int drbg_generate(struct drbg_state *drbg,
 	 * 9.3.1 step 6 and 9 supplemented by 9.3.2 step c is implemented
 	 * here. The spec is a bit convoluted here, we make it simpler.
 	 */
-	if ((drbg_max_requests(drbg)) < drbg->reseed_ctr)
+	if (drbg->reseed_threshold < drbg->reseed_ctr)
 		drbg->seeded = false;
 
 	if (drbg->pr || !drbg->seeded) {
@@ -1504,6 +1516,11 @@  static int drbg_instantiate(struct drbg_state *drbg, 
struct drbg_string *pers,
 		drbg->core = &drbg_cores[coreref];
 		drbg->pr = pr;
 		drbg->seeded = false;
+		/*
+		 * Require frequent reseeds until the seed source is fully
+		 * initialized.
+		 */
+		drbg->reseed_threshold = 50;
 
 		ret = drbg_alloc_state(drbg);
 		if (ret)
diff --git a/include/crypto/drbg.h b/include/crypto/drbg.h
index e2c9530..6d83fff 100644
--- a/include/crypto/drbg.h
+++ b/include/crypto/drbg.h
@@ -110,6 +110,7 @@  struct drbg_state {
 	unsigned char *C;
 	/* Number of RNG requests since last reseed -- 10.1.1.1 1c) */
 	size_t reseed_ctr;
+	size_t reseed_threshold;
 	 /* some memory the DRBG can use for its operation */
 	unsigned char *scratchpad;
 	void *priv_data;	/* Cipher handle */