Module Name: src Committed By: tls Date: Mon Apr 7 02:00:00 UTC 2014
Modified Files: src/sys/kern [tls-earlyentropy]: kern_rndpool.c kern_rndq.c src/sys/sys [tls-earlyentropy]: rnd.h Log Message: Entropy estimation changes: 1) Avoid wraparound problems with delta estimator by making estimation framework 64-bit. 2) Adjust rnd_counter to always return a 64-bit value, accordingly. 3) Make delta estimator generic and create two instances: delta-time and delta-value. 4) Add LZF estimator -- used mostly to protect us against injection of bulk data we think is random but is really constant. 5) Allow value and time estimation/collection to be controlled separately. 6) Expose estimator performance to userspace. To generate a diff of this commit: cvs rdiff -u -r1.5 -r1.5.2.1 src/sys/kern/kern_rndpool.c cvs rdiff -u -r1.23 -r1.23.2.1 src/sys/kern/kern_rndq.c cvs rdiff -u -r1.40 -r1.40.2.1 src/sys/sys/rnd.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/kern_rndpool.c diff -u src/sys/kern/kern_rndpool.c:1.5 src/sys/kern/kern_rndpool.c:1.5.2.1 --- src/sys/kern/kern_rndpool.c:1.5 Thu Aug 29 01:04:49 2013 +++ src/sys/kern/kern_rndpool.c Mon Apr 7 02:00:00 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_rndpool.c,v 1.5 2013/08/29 01:04:49 tls Exp $ */ +/* $NetBSD: kern_rndpool.c,v 1.5.2.1 2014/04/07 02:00:00 tls Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -31,7 +31,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_rndpool.c,v 1.5 2013/08/29 01:04:49 tls Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_rndpool.c,v 1.5.2.1 2014/04/07 02:00:00 tls Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -85,6 +85,12 @@ rndpool_get_entropy_count(rndpool_t *rp) return (rp->stats.curentropy); } +void +rndpool_set_entropy_count(rndpool_t *rp, u_int32_t count) +{ + rp->stats.curentropy = count; +} + void rndpool_get_stats(rndpool_t *rp, void *rsp, int size) { Index: src/sys/kern/kern_rndq.c diff -u src/sys/kern/kern_rndq.c:1.23 src/sys/kern/kern_rndq.c:1.23.2.1 --- src/sys/kern/kern_rndq.c:1.23 Tue Mar 11 20:26:08 2014 +++ src/sys/kern/kern_rndq.c Mon Apr 7 02:00:00 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_rndq.c,v 1.23 2014/03/11 20:26:08 pooka Exp $ */ +/* $NetBSD: kern_rndq.c,v 1.23.2.1 2014/04/07 02:00:00 tls Exp $ */ /*- * Copyright (c) 1997-2013 The NetBSD Foundation, Inc. @@ -32,7 +32,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_rndq.c,v 1.23 2014/03/11 20:26:08 pooka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_rndq.c,v 1.23.2.1 2014/04/07 02:00:00 tls Exp $"); #include <sys/param.h> #include <sys/ioctl.h> @@ -63,7 +63,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_rndq.c, #endif #ifdef RND_DEBUG -#define DPRINTF(l,x) if (rnd_debug & (l)) printf x +#define DPRINTF(l,x) if (rnd_debug & (l)) rnd_printf x int rnd_debug = 0; #else #define DPRINTF(l,x) @@ -81,6 +81,10 @@ int rnd_debug = 0; #define RND_VERBOSE #endif +#ifdef RND_VERBOSE +static unsigned int deltacnt; +#endif + /* * This is a little bit of state information attached to each device that we * collect entropy from. This is simply a collection buffer, and when it @@ -93,7 +97,7 @@ typedef struct _rnd_sample_t { krndsource_t *source; int cursor; int entropy; - u_int32_t ts[RND_SAMPLE_COUNT]; + uint64_t ts[RND_SAMPLE_COUNT]; u_int32_t values[RND_SAMPLE_COUNT]; } rnd_sample_t; @@ -129,31 +133,45 @@ static krndsource_t rnd_source_no_collec /* LIST_ENTRY list */ .name = { 'N', 'o', 'C', 'o', 'l', 'l', 'e', 'c', 't', 0, 0, 0, 0, 0, 0, 0 }, - .last_time = 0, .last_delta = 0, .last_delta2 = 0, .total = 0, + .total = 0, .type = RND_TYPE_UNKNOWN, .flags = (RND_FLAG_NO_COLLECT | - RND_FLAG_NO_ESTIMATE | - RND_TYPE_UNKNOWN), + RND_FLAG_NO_ESTIMATE), + .state = NULL, + .test_cnt = 0, + .test = NULL +}; + +static krndsource_t rnd_source_anonymous = { + /* LIST_ENTRY list */ + .name = { 'A', 'n', 'o', 'n', 'y', 'm', 'o', 'u', 's', + 0, 0, 0, 0, 0, 0, 0 }, + .total = 0, + .type = RND_TYPE_UNKNOWN, + .flags = (RND_FLAG_COLLECT_TIME| + RND_FLAG_COLLECT_VALUE| + RND_FLAG_ESTIMATE_TIME), .state = NULL, .test_cnt = 0, .test = NULL }; + void *rnd_process, *rnd_wakeup; struct callout skew_callout; -void rnd_wakeup_readers(void); -static inline u_int32_t rnd_estimate_entropy(krndsource_t *, u_int32_t); -static inline u_int32_t rnd_counter(void); +void rnd_wakeup_readers(void); +static inline uint64_t rnd_counter(void); static void rnd_intr(void *); static void rnd_wake(void *); static void rnd_process_events(void); u_int32_t rnd_extract_data_locked(void *, u_int32_t, u_int32_t); /* XXX */ static void rnd_add_data_ts(krndsource_t *, const void *const, - uint32_t, uint32_t, uint32_t); + uint32_t, uint32_t, uint64_t); static inline void rnd_schedule_process(void); int rnd_ready = 0; int rnd_initial_entropy = 0; +int rnd_printing = 0; #ifdef DIAGNOSTIC static int rnd_tested = 0; @@ -165,6 +183,23 @@ LIST_HEAD(, krndsource) rnd_sources; rndsave_t *boot_rsp; +static inline void +rnd_printf(const char *fmt, ...) +{ + va_list ap; + + membar_consumer(); + if (rnd_printing) { + return; + } + rnd_printing = 1; + membar_producer(); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + rnd_printing = 0; +} + void rnd_init_softint(void) { rnd_process = softint_establish(SOFTINT_SERIAL|SOFTINT_MPSAFE, @@ -175,21 +210,25 @@ rnd_init_softint(void) { } /* - * Generate a 32-bit counter. This should be more machine dependent, - * using cycle counters and the like when possible. + * Generate a 64-bit counter. */ -static inline u_int32_t +static inline uint64_t rnd_counter(void) { - struct timeval tv; + struct timespec ts; + uint64_t ret; #if defined(__HAVE_CPU_COUNTER) - if (cpu_hascounter()) - return (cpu_counter32()); + if (cpu_hascounter() && sizeof(cpu_counter() == sizeof(uint64_t))) { + return (cpu_counter()); + } #endif if (rnd_ready) { - microtime(&tv); - return (tv.tv_sec * 1000000 + tv.tv_usec); + nanouptime(&ts); + ret = ts.tv_sec; + ret *= (uint64_t)1000000000; + ret += ts.tv_nsec; + return ret; } /* when called from rnd_init, its too early to call microtime safely */ return (0); @@ -243,7 +282,7 @@ rnd_getmore(size_t byteswanted) KASSERT(rs->getarg != NULL); rs->get(byteswanted, rs->getarg); #ifdef RND_VERBOSE - printf("rnd: asking source %s for %zu bytes\n", + rnd_printf("rnd: asking source %s for %zu bytes\n", rs->name, byteswanted); #endif } @@ -271,7 +310,7 @@ rnd_wakeup_readers(void) } else { #ifdef RND_VERBOSE if (__predict_false(!rnd_initial_entropy)) - printf("rnd: have initial entropy (%zu)\n", + rnd_printf("rnd: have initial entropy (%zu)\n", entropy_count); #endif rnd_empty = 0; @@ -282,42 +321,85 @@ rnd_wakeup_readers(void) rndsinks_distribute(); } +static uint32_t +rnd_lz_estimate(krndsource_t *rs, rnd_lz_t *const est, + const void *const data, uint32_t len) +{ + const uint8_t *const cdata = data; + size_t c = 0, wherein = 0, cursor = est->cursor; + size_t isz = sizeof(est->in), osz = sizeof(est->out); + LZF_STATE *state = &est->state; + uint8_t *in = est->in, *out = est->out; + uint32_t total = 0; + + KASSERT(rs != NULL); + KASSERT(cursor < isz); + KASSERT(cursor < osz); + + /* We don't loop, so the maximum estimate we will + ever return is one internal-buffer-size worth of bits. */ + + if (cursor + len - wherein >= isz) { + c = lzf_compress_r(in, cursor, out, + cursor, *state); + memset(out, 0, osz); + memset(in, 0, isz); + if (c == 0) { + c = cursor; + } + total += c; + wherein += cursor; + cursor = 0; + } else { + memcpy(in + cursor, cdata + wherein, len - wherein); + cursor += len - wherein; + wherein += len - wherein; + } + + total *= NBBY; + + /* + * Compressing a stream of zeroes gives us 144 output + * bits per input kilobyte -- pure overhead, not entropy. + */ + total = total > 144 ? total - 144 : 0; + + /* LZF is not a very good LZ compressor! */ + total /= 2; + + est->cursor = cursor; + est->inbytes += len; + est->outbits += total; + + return est->outbits > rs->total ? est->outbits - rs->total : 0; +} + /* - * Use the timing of the event to estimate the entropy gathered. + * Use the timing/value of the event to estimate the entropy gathered. * If all the differentials (first, second, and third) are non-zero, return * non-zero. If any of these are zero, return zero. */ -static inline u_int32_t -rnd_estimate_entropy(krndsource_t *rs, u_int32_t t) +static inline uint32_t +rnd_delta_estimate(rnd_delta_t *d, uint64_t v, int64_t delta) { - int32_t delta, delta2, delta3; + int64_t delta2, delta3; - /* - * If the time counter has overflowed, calculate the real difference. - * If it has not, it is simplier. - */ - if (t < rs->last_time) - delta = UINT_MAX - rs->last_time + t; - else - delta = rs->last_time - t; - - if (delta < 0) - delta = -delta; + d->insamples++; /* * Calculate the second and third order differentials */ - delta2 = rs->last_delta - delta; + delta2 = d->dx - delta; if (delta2 < 0) delta2 = -delta2; - delta3 = rs->last_delta2 - delta2; + delta3 = d->d2x - delta2; if (delta3 < 0) delta3 = -delta3; - rs->last_time = t; - rs->last_delta = delta; - rs->last_delta2 = delta2; + d->x = v; + d->dx = delta; + d->d2x = delta2; /* * If any delta is 0, we got no entropy. If all are non-zero, we @@ -326,10 +408,77 @@ rnd_estimate_entropy(krndsource_t *rs, u if (delta == 0 || delta2 == 0 || delta3 == 0) return (0); + d->outbits++; return (1); } -#if defined(__HAVE_CPU_COUNTER) +/* + * Delta estimator for 64-bit timeestamps. Must handle wrap. + */ +static inline uint32_t +rnd_dt_estimate(krndsource_t *rs, uint32_t t) +{ + int64_t delta; + uint64_t ret; + rnd_delta_t *d = &rs->time_delta; + + if (t < d->x) { + delta = UINT64_MAX - d->x + t; + } else { + delta = d->x - t; + } + + if (delta < 0) { + delta = -delta; + } + + ret = rnd_delta_estimate(d, t, delta); + + KASSERT(d->x == t); + KASSERT(d->dx == delta); +#ifdef RND_VERBOSE + if (deltacnt++ % 1151 == 0) { + rnd_printf("rnd_dt_estimate: %s x = %lld, dx = %lld, " + "d2x = %lld\n", rs->name, + (long long int)d->x, + (long long int)d->dx, + (long long int)d->d2x); + } +#endif + return ret; +} + +/* + * Delta estimator for 32 or 64 bit values. "Wrap" isn't. + */ +static inline uint32_t +rnd_dv_estimate(krndsource_t *rs, uint64_t v) +{ + int64_t delta; + uint32_t ret; + rnd_delta_t *d = &rs->value_delta; + + delta = d->x - v; + + if (delta < 0) { + delta = -delta; + } + ret = rnd_delta_estimate(d, v, (uint64_t)delta); + + KASSERT(d->x == v); + KASSERT(d->dx == delta); +#ifdef RND_VERBOSE + if (deltacnt++ % 1151 == 0) { + rnd_printf("rnd_dv_estimate: %s x = %lld, dx = %lld, " + " d2x = %lld\n", rs->name, + (long long int)d->x, + (long long int)d->dx, + (long long int)d->d2x); + } +#endif + return ret; +} + static void rnd_skew(void *arg) { @@ -344,27 +493,28 @@ rnd_skew(void *arg) /* * Even on systems with seemingly stable clocks, the - * entropy estimator seems to think we get 1 bit here - * about every 2 calls. That seems like too much. Set - * NO_ESTIMATE on this source until we can better analyze - * the entropy of its output. + * delta-time entropy estimator seems to think we get 1 bit here + * about every 2 calls. That seems like too much. Instead, + * we feed the rnd_counter() value to the value estimator as well, + * to take advantage of the additional LZ test on estimated values. + * */ if (__predict_false(!live)) { rnd_attach_source(&skewsrc, "callout", RND_TYPE_SKEW, - RND_FLAG_NO_ESTIMATE); + RND_FLAG_COLLECT_VALUE| + RND_FLAG_ESTIMATE_VALUE); live = 1; } flipflop = !flipflop; if (flipflop) { - rnd_add_uint32(&skewsrc, rnd_counter()); - callout_schedule(&skew_callout, hz); + rnd_add_uint64(&skewsrc, rnd_counter()); + callout_schedule(&skew_callout, hz / 10); } else { callout_schedule(&skew_callout, 1); } } -#endif /* * initialize the global random pool for our use. @@ -374,7 +524,7 @@ rnd_skew(void *arg) void rnd_init(void) { - u_int32_t c; + uint64_t c; if (rnd_ready) return; @@ -421,16 +571,11 @@ rnd_init(void) mutex_spin_exit(&rndpool_mtx); } - rnd_ready = 1; - /* * If we have a cycle counter, take its error with respect * to the callout mechanism as a source of entropy, ala * TrueRand. * - * XXX This will do little when the cycle counter *is* what's - * XXX clocking the callout mechanism. How to get this right - * XXX without unsightly spelunking in the timecounter code? */ #if defined(__HAVE_CPU_COUNTER) callout_init(&skew_callout, CALLOUT_MPSAFE); @@ -439,7 +584,7 @@ rnd_init(void) #endif #ifdef RND_VERBOSE - printf("rnd: initialised (%u)%s", RND_POOLBITS, + rnd_printf("rnd: initialised (%u)%s", RND_POOLBITS, c ? " with counter\n" : "\n"); #endif if (boot_rsp != NULL) { @@ -454,11 +599,16 @@ rnd_init(void) } mutex_spin_exit(&rndpool_mtx); #ifdef RND_VERBOSE - printf("rnd: seeded with %d bits\n", + rnd_printf("rnd: seeded with %d bits\n", MIN(boot_rsp->entropy, RND_POOLBITS / 2)); #endif memset(boot_rsp, 0, sizeof(*boot_rsp)); } + rnd_attach_source(&rnd_source_anonymous, "Anonymous", + RND_TYPE_UNKNOWN, + RND_FLAG_COLLECT_TIME|RND_FLAG_COLLECT_VALUE| + RND_FLAG_ESTIMATE_TIME); + rnd_ready = 1; } static rnd_sample_t * @@ -507,17 +657,18 @@ rnd_sample_free(rnd_sample_t *c) * Add a source to our list of sources. */ void -rnd_attach_source(krndsource_t *rs, const char *name, u_int32_t type, - u_int32_t flags) +rnd_attach_source(krndsource_t *rs, const char *name, uint32_t type, + uint32_t flags) { - u_int32_t ts; + uint64_t ts; ts = rnd_counter(); strlcpy(rs->name, name, sizeof(rs->name)); - rs->last_time = ts; - rs->last_delta = 0; - rs->last_delta2 = 0; + memset(&rs->time_delta, 0, sizeof(rs->time_delta)); + rs->time_delta.x = ts; + memset(&rs->value_delta, 0, sizeof(rs->value_delta)); + memset(&rs->lz_v, 0, sizeof(rs->lz_v)); rs->total = 0; /* @@ -526,6 +677,10 @@ rnd_attach_source(krndsource_t *rs, cons rs->test = NULL; rs->test_cnt = -1; + if (flags == 0) { + flags = RND_FLAG_DEFAULT; + } + switch (type) { case RND_TYPE_NET: /* Don't collect by default */ flags |= (RND_FLAG_NO_COLLECT | RND_FLAG_NO_ESTIMATE); @@ -550,22 +705,24 @@ rnd_attach_source(krndsource_t *rs, cons LIST_INSERT_HEAD(&rnd_sources, rs, list); #ifdef RND_VERBOSE - printf("rnd: %s attached as an entropy source (", rs->name); + rnd_printf("rnd: %s attached as an entropy source (", rs->name); if (!(flags & RND_FLAG_NO_COLLECT)) { - printf("collecting"); + rnd_printf("collecting"); if (flags & RND_FLAG_NO_ESTIMATE) - printf(" without estimation"); + rnd_printf(" without estimation"); } else - printf("off"); - printf(")\n"); + rnd_printf("off"); + rnd_printf(")\n"); #endif /* * Again, put some more initial junk in the pool. - * XXX Bogus, but harder to guess than zeros. + * FreeBSD claim to have an analysis that show 4 bits of + * entropy per source-attach timestamp. I am skeptical, + * but we count 1 bit per source here. */ - rndpool_add_data(&rnd_pool, &ts, sizeof(u_int32_t), 1); + rndpool_add_data(&rnd_pool, &ts, sizeof(ts), 1); mutex_spin_exit(&rndpool_mtx); } @@ -607,19 +764,41 @@ rnd_detach_source(krndsource_t *source) } #ifdef RND_VERBOSE - printf("rnd: %s detached as an entropy source\n", source->name); + rnd_printf("rnd: %s detached as an entropy source\n", source->name); #endif } +static inline uint32_t +rnd_estimate(krndsource_t *rs, uint64_t ts, uint64_t val) +{ + uint32_t entropy = 0, dt_est, dv_est, lz_est; + + dt_est = rnd_dt_estimate(rs, ts); + dv_est = rnd_dv_estimate(rs, val); + lz_est = rnd_lz_estimate(rs, &rs->lz_v, &val, sizeof(val)); + + if (!(rs->flags & RND_FLAG_NO_ESTIMATE)) { + if (rs->flags & RND_FLAG_ESTIMATE_TIME) { + entropy += dt_est; + } + + if (rs->flags & RND_FLAG_ESTIMATE_VALUE) { + entropy += MIN(lz_est, dv_est); + } + + } + return entropy; +} + /* * Add a 32-bit value to the entropy pool. The rs parameter should point to * the source-specific source structure. */ void -_rnd_add_uint32(krndsource_t *rs, u_int32_t val) +_rnd_add_uint32(krndsource_t *rs, uint32_t val) { - u_int32_t ts; - u_int32_t entropy = 0; + uint64_t ts; + uint32_t entropy = 0; if (rs->flags & RND_FLAG_NO_COLLECT) return; @@ -631,13 +810,34 @@ _rnd_add_uint32(krndsource_t *rs, u_int3 ts = rnd_counter(); /* - * If we are estimating entropy on this source, - * calculate differentials. + * Calculate estimates - we may not use them, but if we do + * not calculate them, the estimators' history becomes invalid. */ + entropy = rnd_estimate(rs, ts, (uint64_t)val); - if ((rs->flags & RND_FLAG_NO_ESTIMATE) == 0) { - entropy = rnd_estimate_entropy(rs, ts); - } + rnd_add_data_ts(rs, &val, sizeof(val), entropy, ts); +} + +void +_rnd_add_uint64(krndsource_t *rs, uint64_t val) +{ + uint64_t ts; + uint32_t entropy = 0; + + if (rs->flags & RND_FLAG_NO_COLLECT) + return; + + /* + * Sample the counter as soon as possible to avoid + * entropy overestimation. + */ + ts = rnd_counter(); + + /* + * Calculate estimates - we may not use them, but if we do + * not calculate them, the estimators' history becomes invalid. + */ + entropy = rnd_estimate(rs, ts, val); rnd_add_data_ts(rs, &val, sizeof(val), entropy, ts); } @@ -651,18 +851,18 @@ rnd_add_data(krndsource_t *rs, const voi * itself, random. Don't estimate entropy based on * timestamp, just directly add the data. */ + mutex_spin_enter(&rndpool_mtx); if (__predict_false(rs == NULL)) { - mutex_spin_enter(&rndpool_mtx); - rndpool_add_data(&rnd_pool, data, len, entropy); - mutex_spin_exit(&rndpool_mtx); - } else { - rnd_add_data_ts(rs, data, len, entropy, rnd_counter()); + rs = &rnd_source_anonymous; } + entropy = MIN(entropy, rnd_lz_estimate(rs, &rs->lz_v, data, len)); + rndpool_add_data(&rnd_pool, data, len, entropy); + mutex_spin_exit(&rndpool_mtx); } static void rnd_add_data_ts(krndsource_t *rs, const void *const data, u_int32_t len, - u_int32_t entropy, uint32_t ts) + u_int32_t entropy, uint64_t ts) { rnd_sample_t *state = NULL; const uint32_t *dint = data; @@ -671,10 +871,12 @@ rnd_add_data_ts(krndsource_t *rs, const SIMPLEQ_HEAD(, _rnd_sample_t) tmp_samples = SIMPLEQ_HEAD_INITIALIZER(tmp_samples); - if (rs->flags & RND_FLAG_NO_COLLECT) { + if (rs && (rs->flags & RND_FLAG_NO_COLLECT || + __predict_false(!(rs->flags & + (RND_FLAG_COLLECT_TIME| + RND_FLAG_COLLECT_VALUE))))) { return; } - todo = len / sizeof(*dint); /* * Let's try to be efficient: if we are warm, and a source @@ -689,12 +891,12 @@ rnd_add_data_ts(krndsource_t *rs, const getmicrouptime(&upt); if ((todo >= RND_SAMPLE_COUNT) || - (rs->total > upt.tv_sec * 10) || + (upt.tv_sec > 0 && rs->total > upt.tv_sec * 10) || (upt.tv_sec > 10 && rs->total > upt.tv_sec) || (upt.tv_sec > 100 && rs->total > upt.tv_sec / 10)) { #ifdef RND_VERBOSE - printf("rnd: source %s is fast (%d samples " + rnd_printf("rnd: source %s is fast (%d samples " "at once, %d bits in %lld seconds), " "processing samples in bulk.\n", rs->name, todo, rs->total, @@ -790,7 +992,7 @@ rnd_hwrng_test(rnd_sample_t *sample) v2 = (uint8_t *)sample->values + cmplen; if (__predict_false(!memcmp(v1, v2, cmplen))) { - printf("rnd: source \"%s\" failed continuous-output test.\n", + rnd_printf("rnd: source \"%s\" failed continuous-output test.\n", source->name); return 1; } @@ -811,7 +1013,7 @@ rnd_hwrng_test(rnd_sample_t *sample) strlcpy(source->test->rt_name, source->name, sizeof(source->test->rt_name)); if (rngtest(source->test)) { - printf("rnd: source \"%s\" failed statistical test.", + rnd_printf("rnd: source \"%s\" failed statistical test.", source->name); return 1; } @@ -855,8 +1057,9 @@ rnd_process_events(void) * the source was disabled before we were called, but * after the entry was queued. */ - if (__predict_false(sample->source->flags - & RND_FLAG_NO_COLLECT)) { + if (__predict_false(!(sample->source->flags & + (RND_FLAG_COLLECT_TIME| + RND_FLAG_COLLECT_VALUE)))) { SIMPLEQ_INSERT_TAIL(&df_samples, sample, next); } else { SIMPLEQ_INSERT_TAIL(&dq_samples, sample, next); @@ -868,21 +1071,14 @@ rnd_process_events(void) mutex_spin_enter(&rndpool_mtx); pool_entropy = rndpool_get_entropy_count(&rnd_pool); - if (pool_entropy > RND_ENTROPY_THRESHOLD * NBBY) { - wake++; - } else { - rnd_empty = 1; - rnd_getmore(howmany((RND_POOLBITS - pool_entropy), NBBY)); -#ifdef RND_VERBOSE - printf("rnd: empty, asking for %zu bytes\n", - howmany((RND_POOLBITS - pool_entropy), NBBY)); -#endif - } while ((sample = SIMPLEQ_FIRST(&dq_samples))) { + int sample_count; + SIMPLEQ_REMOVE_HEAD(&dq_samples, next); source = sample->source; entropy = sample->entropy; + sample_count = sample->cursor + 1; /* * Don't provide a side channel for timing attacks on @@ -906,20 +1102,40 @@ rnd_process_events(void) * Detach the bad source. See below. */ badsource = source; - printf("rnd: detaching source \"%s\".", + rnd_printf("rnd: detaching source \"%s\".", badsource->name); break; } } - rndpool_add_data(&rnd_pool, sample->values, - RND_SAMPLE_COUNT * 4, 0); - rndpool_add_data(&rnd_pool, sample->ts, - RND_SAMPLE_COUNT * 4, entropy); + if (source->flags & RND_FLAG_COLLECT_VALUE) { + rndpool_add_data(&rnd_pool, sample->values, + sample_count * + sizeof(sample->values[1]), + 0); + } + if (source->flags & RND_FLAG_COLLECT_TIME) { + rndpool_add_data(&rnd_pool, sample->ts, + sample_count * + sizeof(sample->ts[1]), + 0); + } + pool_entropy += entropy; source->total += sample->entropy; SIMPLEQ_INSERT_TAIL(&df_samples, sample, next); } + rndpool_set_entropy_count(&rnd_pool, pool_entropy); + if (pool_entropy > RND_ENTROPY_THRESHOLD * 8) { + wake++; + } else { + rnd_empty = 1; + rnd_getmore((RND_POOLBITS - pool_entropy) / 8); +#ifdef RND_VERBOSE + rnd_printf("rnd: empty, asking for %d bits\n", + (int)((RND_POOLBITS - pool_entropy) / 8)); +#endif + } mutex_spin_exit(&rndpool_mtx); /* Now we hold no locks: clean up. */ @@ -941,7 +1157,6 @@ rnd_process_events(void) rnd_sample_free(sample); } - /* * Wake up any potential readers waiting. */ @@ -977,22 +1192,22 @@ rnd_extract_data_locked(void *p, u_int32 timed_in++; } if (__predict_false(!rnd_initial_entropy)) { - u_int32_t c; + uint64_t c; #ifdef RND_VERBOSE - printf("rnd: WARNING! initial entropy low (%u).\n", + rnd_printf("rnd: WARNING! initial entropy low (%u).\n", rndpool_get_entropy_count(&rnd_pool)); #endif /* Try once again to put something in the pool */ c = rnd_counter(); - rndpool_add_data(&rnd_pool, &c, sizeof(u_int32_t), 1); + rndpool_add_data(&rnd_pool, &c, sizeof(c), 1); } #ifdef DIAGNOSTIC while (!rnd_tested) { entropy_count = rndpool_get_entropy_count(&rnd_pool); #ifdef RND_VERBOSE - printf("rnd: starting statistical RNG test, entropy = %d.\n", + rnd_printf("rnd: starting statistical RNG test, entropy = %d.\n", entropy_count); #endif if (rndpool_extract_data(&rnd_pool, rnd_rt.rt_b, @@ -1016,7 +1231,7 @@ rnd_extract_data_locked(void *p, u_int32 * The relevant standard says to reset the module, * but developers objected... */ - printf("rnd: WARNING, ENTROPY POOL FAILED " + rnd_printf("rnd: WARNING, ENTROPY POOL FAILED " "STATISTICAL TEST!\n"); continue; } @@ -1025,7 +1240,7 @@ rnd_extract_data_locked(void *p, u_int32 entropy_count); memset(rnd_testbits, 0, sizeof(rnd_testbits)); #ifdef RND_VERBOSE - printf("rnd: statistical RNG test done, entropy = %d.\n", + rnd_printf("rnd: statistical RNG test done, entropy = %d.\n", rndpool_get_entropy_count(&rnd_pool)); #endif rnd_tested++; @@ -1056,7 +1271,7 @@ rnd_seed(void *base, size_t len) uint8_t digest[SHA1_DIGEST_LENGTH]; if (len != sizeof(*boot_rsp)) { - aprint_error("rnd: bad seed length %d\n", (int)len); + rnd_printf("rnd: bad seed length %d\n", (int)len); return; } @@ -1068,7 +1283,7 @@ rnd_seed(void *base, size_t len) SHA1Final(digest, &s); if (memcmp(digest, boot_rsp->digest, sizeof(digest))) { - aprint_error("rnd: bad seed checksum\n"); + rnd_printf("rnd: bad seed checksum\n"); return; } @@ -1078,7 +1293,7 @@ rnd_seed(void *base, size_t len) */ if (rnd_ready) { #ifdef RND_VERBOSE - printf("rnd: ready, feeding in seed data directly.\n"); + rnd_printf("rnd: ready, feeding in seed data directly.\n"); #endif mutex_spin_enter(&rndpool_mtx); rndpool_add_data(&rnd_pool, boot_rsp->data, @@ -1088,7 +1303,7 @@ rnd_seed(void *base, size_t len) mutex_spin_exit(&rndpool_mtx); } else { #ifdef RND_VERBOSE - printf("rnd: not ready, deferring seed feed.\n"); + rnd_printf("rnd: not ready, deferring seed feed.\n"); #endif } } Index: src/sys/sys/rnd.h diff -u src/sys/sys/rnd.h:1.40 src/sys/sys/rnd.h:1.40.2.1 --- src/sys/sys/rnd.h:1.40 Thu Aug 29 01:04:49 2013 +++ src/sys/sys/rnd.h Mon Apr 7 02:00:00 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: rnd.h,v 1.40 2013/08/29 01:04:49 tls Exp $ */ +/* $NetBSD: rnd.h,v 1.40.2.1 2014/04/07 02:00:00 tls Exp $ */ /*- * Copyright (c) 1997 The NetBSD Foundation, Inc. @@ -42,6 +42,7 @@ #ifdef _KERNEL #include <sys/queue.h> +#include <sys/systm.h> #endif #ifdef _KERNEL @@ -85,13 +86,29 @@ typedef struct { uint32_t flags; /* flags */ } rndsource_t; +typedef struct { + rndsource_t rt; + uint32_t dt_samples; /* time-delta samples input */ + uint32_t dt_total; /* time-delta entropy estimate */ + uint32_t dv_samples; /* value-delta samples input */ + uint32_t dv_total; /* value-delta entropy estimate */ + uint32_t lzv_bytes; /* LZF bytes in */ + uint32_t lzv_total; /* LZF entropy estimate (bits) */ +} rndsource_est_t; + /* * Flags to control the source. Low byte is type, upper bits are flags. */ -#define RND_FLAG_NO_ESTIMATE 0x00000100 /* don't estimate entropy */ -#define RND_FLAG_NO_COLLECT 0x00000200 /* don't collect entropy */ +#define RND_FLAG_NO_ESTIMATE 0x00000100 +#define RND_FLAG_NO_COLLECT 0x00000200 #define RND_FLAG_FAST 0x00000400 /* process samples in bulk */ #define RND_FLAG_HASCB 0x00000800 /* has get callback */ +#define RND_FLAG_COLLECT_TIME 0x00001000 /* use timestamp as input */ +#define RND_FLAG_COLLECT_VALUE 0x00002000 /* use value as input */ +#define RND_FLAG_ESTIMATE_TIME 0x00004000 /* estimate entropy on time */ +#define RND_FLAG_ESTIMATE_VALUE 0x00008000 /* estimate entropy on value */ +#define RND_FLAG_DEFAULT (RND_FLAG_COLLECT_VALUE|RND_FLAG_COLLECT_TIME|\ + RND_FLAG_ESTIMATE_TIME) #define RND_TYPE_UNKNOWN 0 /* unknown source */ #define RND_TYPE_DISK 1 /* source is physical disk */ @@ -115,20 +132,37 @@ typedef struct { #endif #define RND_POOLBITS (RND_POOLWORDS * 32) +typedef struct rnd_lz_estimator { + LZF_STATE state; + size_t cursor; + uint8_t in[1024]; + uint8_t out[1024]; + uint64_t inbytes; + uint64_t outbits; +} rnd_lz_t; + +typedef struct rnd_delta_estimator { + uint64_t x; + uint64_t dx; + uint64_t d2x; + uint64_t insamples; + uint64_t outbits; +} rnd_delta_t; + typedef struct krndsource { LIST_ENTRY(krndsource) list; /* the linked list */ char name[16]; /* device name */ - uint32_t last_time; /* last time recorded */ - uint32_t last_delta; /* last delta value */ - uint32_t last_delta2; /* last delta2 value */ + rnd_delta_t time_delta; /* time delta estimator */ + rnd_delta_t value_delta; /* value delta estimator */ uint32_t total; /* entropy from this source */ uint32_t type; /* type */ uint32_t flags; /* flags */ void *state; /* state information */ size_t test_cnt; /* how much test data accumulated? */ - rngtest_t *test; /* test data for RNG type sources */ void (*get)(size_t, void *); /* pool wants N bytes (badly) */ void *getarg; /* argument to get-function */ + rnd_lz_t lz_v; /* LZF context as entropy estimator */ + rngtest_t *test; /* test data for RNG type sources */ } krndsource_t; static inline void @@ -151,6 +185,7 @@ typedef struct { void rndpool_init(rndpool_t *); void rndpool_init_global(void); uint32_t rndpool_get_entropy_count(rndpool_t *); +void rndpool_set_entropy_count(rndpool_t *, uint32_t); void rndpool_get_stats(rndpool_t *, void *, int); void rndpool_increment_entropy_count(rndpool_t *, uint32_t); uint32_t *rndpool_get_pool(rndpool_t *); @@ -161,6 +196,7 @@ uint32_t rndpool_extract_data(rndpool_t void rnd_init(void); void rnd_init_softint(void); void _rnd_add_uint32(krndsource_t *, uint32_t); +void _rnd_add_uint64(krndsource_t *, uint64_t); void rnd_add_data(krndsource_t *, const void *const, uint32_t, uint32_t); void rnd_attach_source(krndsource_t *, const char *, @@ -174,8 +210,22 @@ void rnd_seed(void *, size_t); static inline void rnd_add_uint32(krndsource_t *kr, uint32_t val) { - if (__predict_true(kr) && RND_ENABLED(kr)) { - _rnd_add_uint32(kr, val); + if (__predict_true(kr)) { + if (RND_ENABLED(kr)) { + _rnd_add_uint32(kr, val); + } + } else { + rnd_add_data(NULL, &val, sizeof(val), 0); + } +} + +static inline void +rnd_add_uint64(krndsource_t *kr, uint64_t val) +{ + if (__predict_true(kr)) { + if (RND_ENABLED(kr)) { + _rnd_add_uint64(kr, val); + } } else { rnd_add_data(NULL, &val, sizeof(val), 0); } @@ -186,6 +236,9 @@ extern int rnd_full; extern int rnd_filled; extern int rnd_initial_entropy; +extern int rnd_ready; +extern int rnd_printing; /* XXX recursion through printf */ + #endif /* _KERNEL */ #define RND_MAXSTATCOUNT 10 /* 10 sources at once max */ @@ -200,6 +253,16 @@ typedef struct { } rndstat_t; /* + * return "count" random entries with estimates, starting at "start" + */ +typedef struct { + uint32_t start; + uint32_t count; + rndsource_est_t source[RND_MAXSTATCOUNT]; +} rndstat_est_t; + + +/* * return information on a specific source by name */ typedef struct { @@ -207,6 +270,12 @@ typedef struct { rndsource_t source; } rndstat_name_t; +typedef struct { + char name[16]; + rndsource_est_t source; +} rndstat_est_name_t; + + /* * set/clear device flags. If type is set to 0xff, the name is used * instead. Otherwise, the flags set/cleared apply to all devices of @@ -235,5 +304,7 @@ typedef struct { #define RNDCTL _IOW('R', 104, rndctl_t) /* set/clear source flags */ #define RNDADDDATA _IOW('R', 105, rnddata_t) /* add data to the pool */ #define RNDGETPOOLSTAT _IOR('R', 106, rndpoolstat_t) /* get statistics */ +#define RNDGETESTNUM _IOWR('R', 107, rndstat_est_t) /* get srcest */ +#define RNDGETESTNAME _IOWR('R', 108, rndstat_est_name_t) /* " by name */ #endif /* !_SYS_RND_H_ */