diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/random.c | 122 |
1 files changed, 70 insertions, 52 deletions
diff --git a/drivers/char/random.c b/drivers/char/random.c index e83f645..a93ea7e 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -840,51 +840,64 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, size_t nbytes, int min, int rsvd); /* - * This utility inline function is responsible for transferring entropy - * from the primary pool to the secondary extraction pool. We make - * sure we pull enough for a 'catastrophic reseed'. + * This function is responsible for filling this pool (r) + * to an appropriate level, by pulling from the upstream + * pool (r->pull), if any, if necessary. + * + * We enforce (probably redundantly) the requirement that + * any pull be large enough to be "significant" to any attacker. + * + * In principle, this would cascade, pulling from other pools + * even farther upstream, but as it stands there are only two + * pools, so no cascade. */ -static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) -{ +static void fill_pool( + struct entropy_store *r, + size_t want_level /* measured in bytes */ +){ __u32 tmp[OUTPUT_POOL_WORDS]; - - if (!r->pull) return; - if (r->entropy_count < nbytes * 8 && r->entropy_count < r->poolinfo->POOLBITS) { - int rsvd = 0; - int bytes = nbytes; - if (!r->limit) { + int rsvd = 0; /* measured in bits */ + int actual; /* measured in BYTES */ + int txbits = BYTE2BIT(want_level) - r->entropy_count; + if (txbits < 0) return; /* already have enough */ + if (r->entropy_count >= r->poolinfo->POOLBITS) return; /* already full */ + if (!r->pull) return; /* no upstream pool to pull from */ + if (!r->limit) { /* - * Here if we are non-limited i.e. non-blocking i.e. urandom. - * Reserve a suitable amount of entropy in the input pool, - * based on how desperate we are to be reseeded. - * - * The dilution factor ranges from 819 to 1 (at appetite=0) - * to 858,993,459 to 1 (at appetite=20) - */ - int appetite = fls64(r->extracted_subttl) - 18; - if (appetite < 0) return; /* 128k bits maps to appetite 0 */ -/* Max possible rsvd: */ - rsvd = W2BIT(r->pull->poolinfo->poolwords) - RESEED_BATCH; +* Here if we are non-limited i.e. non-blocking i.e. urandom. +* Reserve a suitable amount of entropy in the input pool, a +* sliding scale based on how desperately we need to be reseeded. +* +* The dilution factor ranges from 819 to 1 (at appetite=0) +* to 858,993,459 to 1 (at appetite=20) +*/ + int appetite = fls64(r->extracted_subttl) - 18; + if (appetite < 0) return; /* 128k bits maps to appetite 0 */ +/* The largest rsvd that makes any sense: */ + rsvd = W2BIT(r->pull->poolinfo->poolwords) - RESEED_BATCH; /* When the appetite gets to 20, rsvd goes to zero: */ - rsvd = rsvd - appetite*rsvd/20; - if (rsvd < 0) rsvd = 0; - } - - /* Make the request big enough to be "significant" to any attacker: */ - bytes = max_t(int, bytes, BIT2BYTE(RESEED_BATCH)); - /* but never more than our buffer size */ - bytes = min_t(int, bytes, sizeof(tmp)); - - DEBUG_ENT("Going to reseed %s with %d bits; " - "caller requested: %zu prev level: %d\n", - r->name, bytes * 8, nbytes * 8, r->entropy_count); - - bytes = extract_entropy(r->pull, tmp, bytes, - BIT2BYTE(RESEED_BATCH), BIT2BYTE(rsvd)); - mix_pool_bytes(r, tmp, bytes, NULL); - credit_entropy_bits(r, bytes*8); - } -// FIXME: return bytes; + rsvd = rsvd - appetite*rsvd/20; + if (rsvd < 0) rsvd = 0; + } + + /* Make the request big enough to be "significant" to any attacker: */ + txbits = max_t(int, txbits, RESEED_BATCH); + /* but never more than our buffer size */ + txbits = min_t(int, txbits, 8*sizeof(tmp)); + + DEBUG_ENT("Going to reseed %s with %d bits; " + "caller want_level: %zu prev level: %d\n", + r->name, txbits, + want_level * 8, r->entropy_count); + + actual = extract_entropy(r->pull, tmp, BIT2BYTE(txbits), + BIT2BYTE(RESEED_BATCH), BIT2BYTE(rsvd)); + mix_pool_bytes(r, tmp, actual, NULL); + credit_entropy_bits(r, actual*8); + if (BYTE2BIT(actual) > RESEED_BATCH) { + r->extracted_subttl = 0; + } +// FIXME: return actual; } /* @@ -1012,8 +1025,9 @@ static void extract_buf(struct entropy_store *r, __u8 *out) memset(&hash, 0, sizeof(hash)); } +/* returns actual number of bytes extracted */ static ssize_t extract_entropy(struct entropy_store *r, void *buf, - size_t nbytes, int min, int reserved) + size_t txbytes, int min, int reserved) { ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; @@ -1027,7 +1041,7 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, spin_unlock_irqrestore(&r->lock, flags); trace_extract_entropy(r->name, EXTRACT_SIZE, r->entropy_count, _RET_IP_); - xfer_secondary_pool(r, EXTRACT_SIZE); + fill_pool(r, EXTRACT_SIZE); extract_buf(r, tmp); spin_lock_irqsave(&r->lock, flags); memcpy(r->last_data, tmp, EXTRACT_SIZE); @@ -1035,11 +1049,15 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, spin_unlock_irqrestore(&r->lock, flags); } - trace_extract_entropy(r->name, nbytes, r->entropy_count, _RET_IP_); - xfer_secondary_pool(r, nbytes); - nbytes = account(r, nbytes, min, reserved); + trace_extract_entropy(r->name, txbytes, r->entropy_count, _RET_IP_); +/* We want our pool (r) to have enough, if possible. + * So pull it up to a level (txbits) that will cover the + * extraction we are about to do. + */ + fill_pool(r, txbytes); + txbytes = account(r, txbytes, min, reserved); - while (nbytes) { + while (txbytes) { extract_buf(r, tmp); if (fips_enabled) { @@ -1049,9 +1067,9 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, memcpy(r->last_data, tmp, EXTRACT_SIZE); spin_unlock_irqrestore(&r->lock, flags); } - i = min_t(int, nbytes, EXTRACT_SIZE); + i = min_t(int, txbytes, EXTRACT_SIZE); memcpy(buf, tmp, i); - nbytes -= i; + txbytes -= i; buf += i; ret += i; } @@ -1060,11 +1078,11 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, memset(tmp, 0, sizeof(tmp)); if (ret > 0) { -/* Prevent overflow; saturate at a user-friendly round number: */ +/* Subttl does not overflow; it saturates at a user-friendly round number: */ r->extracted_subttl += BYTE2BIT(ret); if (r->extracted_subttl > 1000000000000000000LL) r->extracted_subttl = 1000000000000000000LL; +/* Total does not saturate; it just overflows and wraps around. */ r->extracted_total += BYTE2BIT(ret); - if (r->extracted_total > 1000000000000000000LL) r->extracted_total = 1000000000000000000LL; } return ret; } @@ -1076,7 +1094,7 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, __u8 tmp[EXTRACT_SIZE]; trace_extract_entropy_user(r->name, nbytes, r->entropy_count, _RET_IP_); - xfer_secondary_pool(r, nbytes); + fill_pool(r, nbytes); nbytes = account(r, nbytes, 0, 0); while (nbytes) { |