Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

crypto: drbg - reseed 'nopr' drbgs periodically from get_random_bytes()

In contrast to the fully prediction resistant 'pr' DRBGs, the 'nopr'
variants get seeded once at boot and reseeded only rarely thereafter,
namely only after 2^20 requests have been served each. AFAICT, this
reseeding based on the number of requests served is primarily motivated
by information theoretic considerations, c.f. NIST SP800-90Ar1,
sec. 8.6.8 ("Reseeding").

However, given the relatively large seed lifetime of 2^20 requests, the
'nopr' DRBGs can hardly be considered to provide any prediction resistance
whatsoever, i.e. to protect against threats like side channel leaks of the
internal DRBG state (think e.g. leaked VM snapshots). This is expected and
completely in line with the 'nopr' naming, but as e.g. the
"drbg_nopr_hmac_sha512" implementation is potentially being used for
providing the "stdrng" and thus, the crypto_default_rng serving the
in-kernel crypto, it would certainly be desirable to achieve at least the
same level of prediction resistance as get_random_bytes() does.

Note that the chacha20 rngs underlying get_random_bytes() get reseeded
every CRNG_RESEED_INTERVAL == 5min: the secondary, per-NUMA node rngs from
the primary one and the primary rng in turn from the entropy pool, provided
sufficient entropy is available.

The 'nopr' DRBGs do draw randomness from get_random_bytes() for their
initial seed already, so making them to reseed themselves periodically from
get_random_bytes() in order to let them benefit from the latter's
prediction resistance is not such a big change conceptually.

In principle, it would have been also possible to make the 'nopr' DRBGs to
periodically invoke a full reseeding operation, i.e. to also consider the
jitterentropy source (if enabled) in addition to get_random_bytes() for the
seed value. However, get_random_bytes() is relatively lightweight as
compared to the jitterentropy generation process and thus, even though the
'nopr' reseeding is supposed to get invoked infrequently, it's IMO still
worthwhile to avoid occasional latency spikes for drbg_generate() and
stick to get_random_bytes() only. As an additional remark, note that
drawing randomness from the non-SP800-90B-conforming get_random_bytes()
only won't adversely affect SP800-90A conformance either: the very same is
being done during boot via drbg_seed_from_random() already once
rng_is_initialized() flips to true and it follows that if the DRBG
implementation does conform to SP800-90A now, it will continue to do so.

Make the 'nopr' DRBGs to reseed themselves periodically from
get_random_bytes() every CRNG_RESEED_INTERVAL == 5min.

More specifically, introduce a new member ->last_seed_time to struct
drbg_state for recording in units of jiffies when the last seeding
operation had taken place. Make __drbg_seed() maintain it and let
drbg_generate() invoke a reseed from get_random_bytes() via
drbg_seed_from_random() if more than 5min have passed by since the last
seeding operation. Be careful to not to reseed if in testing mode though,
or otherwise the drbg related tests in crypto/testmgr.c would fail to
reproduce the expected output.

In order to keep the formatting clean in drbg_generate() wrap the logic
for deciding whether or not a reseed is due in a new helper,
drbg_nopr_reseed_interval_elapsed().

Signed-off-by: Nicolai Stange <nstange@suse.de>
Reviewed-by: Stephan Müller <smueller@chronox.de>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Nicolai Stange and committed by
Herbert Xu
8ea5ee00 559edd47

+26 -1
+25 -1
crypto/drbg.c
··· 100 100 #include <crypto/drbg.h> 101 101 #include <crypto/internal/cipher.h> 102 102 #include <linux/kernel.h> 103 + #include <linux/jiffies.h> 103 104 104 105 /*************************************************************** 105 106 * Backend cipher definitions available to DRBG ··· 1045 1044 return ret; 1046 1045 1047 1046 drbg->seeded = new_seed_state; 1047 + drbg->last_seed_time = jiffies; 1048 1048 /* 10.1.1.2 / 10.1.1.3 step 5 */ 1049 1049 drbg->reseed_ctr = 1; 1050 1050 ··· 1112 1110 out: 1113 1111 memzero_explicit(entropy, entropylen); 1114 1112 return ret; 1113 + } 1114 + 1115 + static bool drbg_nopr_reseed_interval_elapsed(struct drbg_state *drbg) 1116 + { 1117 + unsigned long next_reseed; 1118 + 1119 + /* Don't ever reseed from get_random_bytes() in test mode. */ 1120 + if (list_empty(&drbg->test_data.list)) 1121 + return false; 1122 + 1123 + /* 1124 + * Obtain fresh entropy for the nopr DRBGs after 300s have 1125 + * elapsed in order to still achieve sort of partial 1126 + * prediction resistance over the time domain at least. Note 1127 + * that the period of 300s has been chosen to match the 1128 + * CRNG_RESEED_INTERVAL of the get_random_bytes()' chacha 1129 + * rngs. 1130 + */ 1131 + next_reseed = drbg->last_seed_time + 300 * HZ; 1132 + return time_after(jiffies, next_reseed); 1115 1133 } 1116 1134 1117 1135 /* ··· 1435 1413 /* 9.3.1 step 7.4 */ 1436 1414 addtl = NULL; 1437 1415 } else if (rng_is_initialized() && 1438 - drbg->seeded == DRBG_SEED_STATE_PARTIAL) { 1416 + (drbg->seeded == DRBG_SEED_STATE_PARTIAL || 1417 + drbg_nopr_reseed_interval_elapsed(drbg))) { 1439 1418 len = drbg_seed_from_random(drbg); 1440 1419 if (len) 1441 1420 goto err; ··· 1592 1569 drbg->core = &drbg_cores[coreref]; 1593 1570 drbg->pr = pr; 1594 1571 drbg->seeded = DRBG_SEED_STATE_UNSEEDED; 1572 + drbg->last_seed_time = 0; 1595 1573 drbg->reseed_threshold = drbg_max_requests(drbg); 1596 1574 1597 1575 ret = drbg_alloc_state(drbg);
+1
include/crypto/drbg.h
··· 134 134 struct scatterlist sg_in, sg_out; /* CTR mode SGLs */ 135 135 136 136 enum drbg_seed_state seeded; /* DRBG fully seeded? */ 137 + unsigned long last_seed_time; 137 138 bool pr; /* Prediction resistance enabled? */ 138 139 bool fips_primed; /* Continuous test primed? */ 139 140 unsigned char *prev; /* FIPS 140-2 continuous test value */