Index: head/sys/dev/random/random_adaptors.c =================================================================== --- head/sys/dev/random/random_adaptors.c (revision 273957) +++ head/sys/dev/random/random_adaptors.c (revision 273958) @@ -1,489 +1,466 @@ /*- * Copyright (c) 2013 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2013 David E. O'Brien * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_random.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* The random_adaptors_lock protects random_adaptors_list and friends and random_adaptor. * We need a sleepable lock for uiomove/block/poll/sbuf/sysctl. */ static struct sx random_adaptors_lock; LIST_HEAD(adaptors_head, random_adaptors); static struct adaptors_head random_adaptors_list = LIST_HEAD_INITIALIZER(random_adaptors_list); static struct random_adaptor *random_adaptor = NULL; /* Currently active adaptor */ /* End of data items requiring random_adaptors_lock protection */ /* The random_readrate_mtx mutex protects the read-rate estimator. */ static struct mtx random_read_rate_mtx; static int random_adaptor_read_rate_cache; /* End of data items requiring random_readrate_mtx mutex protection */ static struct selinfo rsel; /* Utility routine to change active adaptor when the random_adaptors_list * gets modified. * * Walk a list of registered random(4) adaptors and pick either a requested * one or the highest priority one, whichever comes first. Panic on failure * as the fallback must always be the "dummy" adaptor. */ static void random_adaptor_choose(void) { char rngs[128], *token, *cp; struct random_adaptors *rra, *rrai; struct random_adaptor *random_adaptor_previous; int primax; /* We are going to be messing with random_adaptor. * Exclusive lock is mandatory. */ sx_assert(&random_adaptors_lock, SA_XLOCKED); random_adaptor_previous = random_adaptor; random_adaptor = NULL; if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { cp = rngs; /* XXX: FIX!! (DES): * - fetch tunable once, at boot * - make sysctl r/w * - when fetching tunable or processing a sysctl * write, parse into list of strings so we don't * have to do it here again and again * - sysctl read should return a reconstructed string */ while ((token = strsep(&cp, ",")) != NULL) { LIST_FOREACH(rra, &random_adaptors_list, rra_entries) if (strcmp(rra->rra_name, token) == 0) { random_adaptor = rra->rra_ra; break; } if (random_adaptor != NULL) { printf("random: selecting requested adaptor <%s>\n", random_adaptor->ra_ident); break; } else printf("random: requested adaptor <%s> not available\n", token); } } primax = 0; if (random_adaptor == NULL) { /* * Fall back to the highest priority item on the available * RNG list. */ LIST_FOREACH(rrai, &random_adaptors_list, rra_entries) { if (rrai->rra_ra->ra_priority >= primax) { random_adaptor = rrai->rra_ra; primax = rrai->rra_ra->ra_priority; } } if (random_adaptor != NULL) printf("random: selecting highest priority adaptor <%s>\n", random_adaptor->ra_ident); } KASSERT(random_adaptor != NULL, ("adaptor not found")); /* If we are changing adaptors, deinit the old and init the new. */ if (random_adaptor != random_adaptor_previous) { #ifdef RANDOM_DEBUG printf("random: %s - changing from %s to %s\n", __func__, (random_adaptor_previous == NULL ? "NULL" : random_adaptor_previous->ra_ident), random_adaptor->ra_ident); #endif if (random_adaptor_previous != NULL) (random_adaptor_previous->ra_deinit)(); (random_adaptor->ra_init)(); } } /* XXX: FIX!! Make sure we are not inserting a duplicate */ void random_adaptor_register(const char *name, struct random_adaptor *ra) { struct random_adaptors *rra; KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__)); rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK); rra->rra_name = name; rra->rra_ra = ra; sx_xlock(&random_adaptors_lock); LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries); random_adaptor_choose(); sx_xunlock(&random_adaptors_lock); KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); } void random_adaptor_deregister(const char *name) { struct random_adaptors *rra; KASSERT(name != NULL, ("invalid input to %s", __func__)); KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_xlock(&random_adaptors_lock); LIST_FOREACH(rra, &random_adaptors_list, rra_entries) if (strcmp(rra->rra_name, name) == 0) { LIST_REMOVE(rra, rra_entries); break; } random_adaptor_choose(); sx_xunlock(&random_adaptors_lock); free(rra, M_ENTROPY); } /* ARGSUSED */ int random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags) { void *random_buf; int c, error; ssize_t nbytes; #ifdef RANDOM_DEBUG_VERBOSE printf("random: %s %ld\n", __func__, uio->uio_resid); #endif KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); /* Let the entropy source do any pre-read setup. */ (random_adaptor->ra_read)(NULL, 0); /* (Un)Blocking logic */ error = 0; while (!random_adaptor->ra_seeded()) { if (flags & O_NONBLOCK) { error = EWOULDBLOCK; break; } /* Sleep instead of going into a spin-frenzy */ tsleep(&random_adaptor, PUSER | PCATCH, "block", hz/10); /* keep tapping away at the pre-read until we seed/unblock. */ (random_adaptor->ra_read)(NULL, 0); } mtx_lock(&random_read_rate_mtx); /* The read-rate stuff is a rough indication of the instantaneous read rate, * used to increase the use of 'live' entropy sources when lots of reads are done. */ nbytes = (uio->uio_resid + 32 - 1)/32; /* Round up to units of 32 */ random_adaptor_read_rate_cache += nbytes*32; random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32); mtx_unlock(&random_read_rate_mtx); if (!error) { /* The actual read */ random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); while (uio->uio_resid && !error) { c = MIN(uio->uio_resid, PAGE_SIZE); (random_adaptor->ra_read)(random_buf, c); error = uiomove(random_buf, c, uio); } /* Let the entropy source do any post-read cleanup. */ (random_adaptor->ra_read)(NULL, 1); free(random_buf, M_ENTROPY); } sx_sunlock(&random_adaptors_lock); return (error); } int random_adaptor_read_rate(void) { int ret; KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); mtx_lock(&random_read_rate_mtx); ret = random_adaptor_read_rate_cache; random_adaptor_read_rate_cache = 1; mtx_unlock(&random_read_rate_mtx); return (ret); } /* ARGSUSED */ int random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { int c, error = 0; void *random_buf; #ifdef RANDOM_DEBUG printf("random: %s %zd\n", __func__, uio->uio_resid); #endif KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); while (uio->uio_resid > 0) { c = MIN(uio->uio_resid, PAGE_SIZE); error = uiomove(random_buf, c, uio); if (error) break; (random_adaptor->ra_write)(random_buf, c); /* Introduce an annoying delay to stop swamping */ tsleep(&random_adaptor, PUSER | PCATCH, "block", hz/10); } free(random_buf, M_ENTROPY); sx_sunlock(&random_adaptors_lock); return (error); } /* ARGSUSED */ int random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { #ifdef RANDOM_DEBUG printf("random: %s\n", __func__); #endif KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); if (events & (POLLIN | POLLRDNORM)) { if (random_adaptor->ra_seeded()) events &= (POLLIN | POLLRDNORM); else selrecord(td, &rsel); } sx_sunlock(&random_adaptors_lock); return (events); } /* This will be called by the entropy processor when it seeds itself and becomes secure */ void random_adaptor_unblock(void) { selwakeuppri(&rsel, PUSER); wakeup(&random_adaptor); printf("random: unblocking device.\n"); /* Do arc4random(9) a favour while we are about it. */ (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); } static int random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS) { struct random_adaptors *rra; struct sbuf sbuf; int error, count; sx_slock(&random_adaptors_lock); sbuf_new_for_sysctl(&sbuf, NULL, 64, req); count = 0; LIST_FOREACH(rra, &random_adaptors_list, rra_entries) sbuf_printf(&sbuf, "%s%s(%d)", (count++ ? "," : ""), rra->rra_name, rra->rra_ra->ra_priority); error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); sx_sunlock(&random_adaptors_lock); return (error); } static int random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS) { struct random_adaptors *rra; struct sbuf sbuf; int error; KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); sbuf_new_for_sysctl(&sbuf, NULL, 16, req); LIST_FOREACH(rra, &random_adaptors_list, rra_entries) if (rra->rra_ra == random_adaptor) { sbuf_cat(&sbuf, rra->rra_name); break; } error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); sx_sunlock(&random_adaptors_lock); return (error); } void random_adaptors_init(void) { #ifdef RANDOM_DEBUG printf("random: %s\n", __func__); #endif SYSCTL_PROC(_kern_random, OID_AUTO, adaptors, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, random_sysctl_adaptors_handler, "A", "Random Number Generator adaptors"); SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, random_sysctl_active_adaptor_handler, "A", "Active Random Number Generator Adaptor"); sx_init(&random_adaptors_lock, "random_adaptors"); mtx_init(&random_read_rate_mtx, "read rate mutex", NULL, MTX_DEF); /* The dummy adaptor is not a module by itself, but part of the * randomdev module. */ random_adaptor_register("dummy", &randomdev_dummy); live_entropy_sources_init(); } void random_adaptors_deinit(void) { #ifdef RANDOM_DEBUG printf("random: %s\n", __func__); #endif live_entropy_sources_deinit(); /* Don't do this! Panic will surely follow! */ /* random_adaptor_deregister("dummy"); */ mtx_destroy(&random_read_rate_mtx); sx_destroy(&random_adaptors_lock); } /* - * First seed. - * - * NB! NB! NB! - * NB! NB! NB! - * - * It turns out this is bloody dangerous. I was fiddling with code elsewhere - * and managed to get conditions where a safe (i.e. seeded) entropy device should - * not have been possible. This managed to hide that by unblocking the device anyway. - * As crap randomness is not directly distinguishable from good randomness, this - * could have gone unnoticed for quite a while. - * - * NB! NB! NB! - * NB! NB! NB! - * - * Very luckily, the probe-time entropy is very nearly good enough to cause a - * first seed all of the time, and the default settings for other entropy - * harvesting causes a proper, safe, first seed (unblock) in short order after that. - * - * That said, the below would be useful where folks are more concerned with - * a quick start than with extra paranoia in a low-entropy environment. - * - * markm - October 2013. + * Reseed the active adaptor shortly before starting init(8). */ -#ifdef RANDOM_AUTOSEED /* ARGSUSED */ static void random_adaptors_seed(void *unused __unused) { KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); random_adaptor->ra_reseed(); sx_sunlock(&random_adaptors_lock); arc4rand(NULL, 0, 1); } -SYSINIT(random_seed, SI_SUB_INTRINSIC_POST, SI_ORDER_LAST, - random_adaptors_reseed, NULL); -#endif /* RANDOM_AUTOSEED */ +SYSINIT(random_seed, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST, + random_adaptors_seed, NULL); Index: head/sys/dev/random/yarrow.c =================================================================== --- head/sys/dev/random/yarrow.c (revision 273957) +++ head/sys/dev/random/yarrow.c (revision 273958) @@ -1,521 +1,523 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #ifdef _KERNEL #include "opt_random.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #else /* !_KERNEL */ #include #include #include #include #include #include #include #include "unit_test.h" #include #include #include #include #include #include #endif /* _KERNEL */ #if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) #define RANDOM_YARROW #elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) #error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" #endif #if defined(RANDOM_YARROW) #define TIMEBIN 16 /* max value for Pt/t */ #define FAST 0 #define SLOW 1 /* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */ CTASSERT(BLOCKSIZE == sizeof(uint128_t)); CTASSERT(KEYSIZE == 2*BLOCKSIZE); /* This is the beastie that needs protecting. It contains all of the * state that we are excited about. * Exactly one is instantiated. */ static struct yarrow_state { union { uint8_t byte[BLOCKSIZE]; uint128_t whole; } counter; /* C */ struct randomdev_key key; /* K */ u_int gengateinterval; /* Pg */ u_int bins; /* Pt/t */ u_int outputblocks; /* count output blocks for gates */ u_int slowoverthresh; /* slow pool overthreshhold reseed count */ struct pool { struct source { u_int bits; /* estimated bits of entropy */ } source[ENTROPYSOURCE];/* ... per source */ u_int thresh; /* pool reseed threshhold */ struct randomdev_hash hash; /* accumulated entropy */ } pool[2]; /* pool[0] is fast, pool[1] is slow */ int seeded; struct start_cache { uint8_t junk[KEYSIZE]; struct randomdev_hash hash; } start_cache; } yarrow_state; /* The random_reseed_mtx mutex protects seeding and polling/blocking. */ static mtx_t random_reseed_mtx; #ifdef _KERNEL static struct sysctl_ctx_list random_clist; RANDOM_CHECK_UINT(gengateinterval, 4, 64); RANDOM_CHECK_UINT(bins, 2, 16); RANDOM_CHECK_UINT(fastthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */ RANDOM_CHECK_UINT(slowthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */ RANDOM_CHECK_UINT(slowoverthresh, 1, 5); #else /* !_KERNEL */ static u_int harvest_destination[ENTROPYSOURCE]; #endif /* _KERNEL */ static void generator_gate(void); static void reseed(u_int); void random_yarrow_init_alg(void) { int i, j; #ifdef _KERNEL struct sysctl_oid *random_yarrow_o; #endif /* _KERNEL */ memset(yarrow_state.start_cache.junk, 0, KEYSIZE); randomdev_hash_init(&yarrow_state.start_cache.hash); /* Set up the lock for the reseed/gate state */ #ifdef _KERNEL mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF); #else /* !_KERNEL */ mtx_init(&random_reseed_mtx, mtx_plain); #endif /* _KERNEL */ /* Start unseeded, therefore blocked. */ yarrow_state.seeded = 0; #ifdef _KERNEL /* Yarrow parameters. Do not adjust these unless you have * have a very good clue about what they do! */ random_yarrow_o = SYSCTL_ADD_NODE(&random_clist, SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "yarrow", CTLFLAG_RW, 0, "Yarrow Parameters"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "gengateinterval", CTLTYPE_INT|CTLFLAG_RW, &yarrow_state.gengateinterval, 10, random_check_uint_gengateinterval, "I", "Generation gate interval"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "bins", CTLTYPE_INT|CTLFLAG_RW, &yarrow_state.bins, 10, random_check_uint_bins, "I", "Execution time tuner"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "fastthresh", CTLTYPE_INT|CTLFLAG_RW, &yarrow_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4, random_check_uint_fastthresh, "I", "Fast reseed threshold"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "slowthresh", CTLTYPE_INT|CTLFLAG_RW, &yarrow_state.pool[1].thresh, (BLOCKSIZE*8), random_check_uint_slowthresh, "I", "Slow reseed threshold"); SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "slowoverthresh", CTLTYPE_INT|CTLFLAG_RW, &yarrow_state.slowoverthresh, 2, random_check_uint_slowoverthresh, "I", "Slow over-threshold reseed"); #endif /* _KERNEL */ yarrow_state.gengateinterval = 10; yarrow_state.bins = 10; yarrow_state.pool[FAST].thresh = (3*(BLOCKSIZE*8))/4; yarrow_state.pool[SLOW].thresh = (BLOCKSIZE*8); yarrow_state.slowoverthresh = 2; /* Ensure that the first time we read, we are gated. */ yarrow_state.outputblocks = yarrow_state.gengateinterval; /* Initialise the fast and slow entropy pools */ for (i = FAST; i <= SLOW; i++) { randomdev_hash_init(&yarrow_state.pool[i].hash); for (j = RANDOM_START; j < ENTROPYSOURCE; j++) yarrow_state.pool[i].source[j].bits = 0U; } /* Clear the counter */ uint128_clear(&yarrow_state.counter.whole); } void random_yarrow_deinit_alg(void) { mtx_destroy(&random_reseed_mtx); memset(&yarrow_state, 0, sizeof(yarrow_state)); #ifdef _KERNEL sysctl_ctx_free(&random_clist); #endif } static __inline void random_yarrow_post_insert(void) { u_int pl, overthreshhold[2]; enum random_entropy_source src; #ifdef _KERNEL mtx_assert(&random_reseed_mtx, MA_OWNED); #endif /* Count the over-threshold sources in each pool */ for (pl = 0; pl < 2; pl++) { overthreshhold[pl] = 0; for (src = RANDOM_START; src < ENTROPYSOURCE; src++) { if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh) overthreshhold[pl]++; } } /* If enough slow sources are over threshhold, then slow reseed * else if any fast source over threshhold, then fast reseed. */ if (overthreshhold[SLOW] >= yarrow_state.slowoverthresh) reseed(SLOW); else if (overthreshhold[FAST] > 0 && yarrow_state.seeded) reseed(FAST); } /* Process a single stochastic event off the harvest queue */ void random_yarrow_process_event(struct harvest_event *event) { u_int pl; mtx_lock(&random_reseed_mtx); /* Accumulate the event into the appropriate pool * where each event carries the destination information. * We lock against pool state modification which can happen * during accumulation/reseeding and reading/regating */ pl = event->he_destination % 2; randomdev_hash_iterate(&yarrow_state.pool[pl].hash, event, sizeof(*event)); yarrow_state.pool[pl].source[event->he_source].bits += event->he_bits; random_yarrow_post_insert(); mtx_unlock(&random_reseed_mtx); } /* Process a block of data suspected to be slightly stochastic */ static void random_yarrow_process_buffer(uint8_t *buf, u_int length) { static struct harvest_event event; u_int i, pl; /* Accumulate the data into the appropriate pools * where each event carries the destination information. * We lock against pool state modification which can happen * during accumulation/reseeding and reading/regating */ memset(event.he_entropy + sizeof(uint32_t), 0, HARVESTSIZE - sizeof(uint32_t)); for (i = 0; i < length/sizeof(uint32_t); i++) { event.he_somecounter = get_cyclecount(); event.he_bits = 0; /* Fake */ event.he_source = RANDOM_CACHED; event.he_destination = harvest_destination[RANDOM_CACHED]++; event.he_size = sizeof(uint32_t); *((uint32_t *)event.he_entropy) = *((uint32_t *)buf + i); /* Do the actual entropy insertion */ pl = event.he_destination % 2; randomdev_hash_iterate(&yarrow_state.pool[pl].hash, &event, sizeof(event)); #ifdef DONT_DO_THIS_HERE /* Don't do this here - do it in bulk at the end */ yarrow_state.pool[pl].source[RANDOM_CACHED].bits += bits; #endif } for (pl = FAST; pl <= SLOW; pl++) yarrow_state.pool[pl].source[RANDOM_CACHED].bits += (length >> 4); random_yarrow_post_insert(); } static void reseed(u_int fastslow) { /* Interrupt-context stack is a limited resource; make large * structures static. */ static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */ static uint8_t hash[KEYSIZE]; /* h' */ static uint8_t temp[KEYSIZE]; static struct randomdev_hash context; u_int i; enum random_entropy_source j; KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0")); KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0")); #ifdef RANDOM_DEBUG #ifdef RANDOM_DEBUG_VERBOSE printf("random: %s %s\n", __func__, (fastslow == FAST ? "FAST" : "SLOW")); #endif if (!yarrow_state.seeded) { printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.pool[FAST].thresh); for (i = RANDOM_START; i < ENTROPYSOURCE; i++) printf(" %d", yarrow_state.pool[FAST].source[i].bits); printf("\n"); printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.pool[SLOW].thresh, yarrow_state.slowoverthresh); for (i = RANDOM_START; i < ENTROPYSOURCE; i++) printf(" %d", yarrow_state.pool[SLOW].source[i].bits); printf("\n"); } #endif #ifdef _KERNEL mtx_assert(&random_reseed_mtx, MA_OWNED); #endif /* 1. Hash the accumulated entropy into v[0] */ randomdev_hash_init(&context); /* Feed the slow pool hash in if slow */ if (fastslow == SLOW) { randomdev_hash_finish(&yarrow_state.pool[SLOW].hash, temp); randomdev_hash_iterate(&context, temp, sizeof(temp)); } randomdev_hash_finish(&yarrow_state.pool[FAST].hash, temp); randomdev_hash_iterate(&context, temp, sizeof(temp)); randomdev_hash_finish(&context, v[0]); /* 2. Compute hash values for all v. _Supposed_ to be computationally * intensive. */ if (yarrow_state.bins > TIMEBIN) yarrow_state.bins = TIMEBIN; for (i = 1; i < yarrow_state.bins; i++) { randomdev_hash_init(&context); /* v[i] #= h(v[i - 1]) */ randomdev_hash_iterate(&context, v[i - 1], KEYSIZE); /* v[i] #= h(v[0]) */ randomdev_hash_iterate(&context, v[0], KEYSIZE); /* v[i] #= h(i) */ randomdev_hash_iterate(&context, &i, sizeof(i)); /* Return the hashval */ randomdev_hash_finish(&context, v[i]); } /* 3. Compute a new key; h' is the identity function here; * it is not being ignored! */ randomdev_hash_init(&context); randomdev_hash_iterate(&context, &yarrow_state.key, KEYSIZE); for (i = 1; i < yarrow_state.bins; i++) randomdev_hash_iterate(&context, v[i], KEYSIZE); randomdev_hash_finish(&context, temp); randomdev_encrypt_init(&yarrow_state.key, temp); /* 4. Recompute the counter */ uint128_clear(&yarrow_state.counter.whole); randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp, BLOCKSIZE); memcpy(yarrow_state.counter.byte, temp, BLOCKSIZE); /* 5. Reset entropy estimate accumulators to zero */ for (i = 0; i <= fastslow; i++) for (j = RANDOM_START; j < ENTROPYSOURCE; j++) yarrow_state.pool[i].source[j].bits = 0; /* 6. Wipe memory of intermediate values */ memset(v, 0, sizeof(v)); memset(temp, 0, sizeof(temp)); memset(hash, 0, sizeof(hash)); memset(&context, 0, sizeof(context)); #ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */ /* 7. Dump to seed file */ /* This pseudo-code is documentation. Please leave it alone. */ seed_file = ""; error = randomdev_write_file(seed_file, , PAGE_SIZE); if (error == 0) printf("random: entropy seed file '%s' successfully written\n", seed_file); #endif /* Unblock the device if it was blocked due to being unseeded */ if (!yarrow_state.seeded) { yarrow_state.seeded = 1; random_adaptor_unblock(); } } /* Internal function to return processed entropy from the PRNG */ void random_yarrow_read(uint8_t *buf, u_int bytecount) { u_int blockcount, i; /* Check for initial/final read requests */ if (buf == NULL) return; /* The reseed task must not be jumped on */ mtx_lock(&random_reseed_mtx); blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE; for (i = 0; i < blockcount; i++) { if (yarrow_state.outputblocks++ >= yarrow_state.gengateinterval) { generator_gate(); yarrow_state.outputblocks = 0; } uint128_increment(&yarrow_state.counter.whole); randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, buf, BLOCKSIZE); buf += BLOCKSIZE; } mtx_unlock(&random_reseed_mtx); } /* Internal function to hand external entropy to the PRNG */ void random_yarrow_write(uint8_t *buf, u_int count) { uintmax_t timestamp; /* We must be locked for all this as plenty of state gets messed with */ mtx_lock(&random_reseed_mtx); timestamp = get_cyclecount(); randomdev_hash_iterate(&yarrow_state.start_cache.hash, ×tamp, sizeof(timestamp)); randomdev_hash_iterate(&yarrow_state.start_cache.hash, buf, count); timestamp = get_cyclecount(); randomdev_hash_iterate(&yarrow_state.start_cache.hash, ×tamp, sizeof(timestamp)); randomdev_hash_finish(&yarrow_state.start_cache.hash, yarrow_state.start_cache.junk); randomdev_hash_init(&yarrow_state.start_cache.hash); #ifdef RANDOM_DEBUG_VERBOSE { int i; printf("random: %s - ", __func__); for (i = 0; i < KEYSIZE; i++) printf("%02X", yarrow_state.start_cache.junk[i]); printf("\n"); } #endif random_yarrow_process_buffer(yarrow_state.start_cache.junk, KEYSIZE); memset(yarrow_state.start_cache.junk, 0, KEYSIZE); mtx_unlock(&random_reseed_mtx); } static void generator_gate(void) { u_int i; uint8_t temp[KEYSIZE]; for (i = 0; i < KEYSIZE; i += BLOCKSIZE) { uint128_increment(&yarrow_state.counter.whole); randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE); } randomdev_encrypt_init(&yarrow_state.key, temp); memset(temp, 0, KEYSIZE); } void random_yarrow_reseed(void) { + mtx_lock(&random_reseed_mtx); reseed(SLOW); + mtx_unlock(&random_reseed_mtx); } int random_yarrow_seeded(void) { return (yarrow_state.seeded); } #endif /* RANDOM_YARROW */