Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2137,7 +2137,6 @@ dev/random/dummy_rng.c standard dev/random/live_entropy_sources.c standard dev/random/random_harvestq.c standard -dev/random/randomdev_soft.c optional random dev/random/yarrow.c optional random dev/random/fortuna.c optional random dev/random/hash.c optional random Index: sys/conf/kern.opts.mk =================================================================== --- sys/conf/kern.opts.mk +++ sys/conf/kern.opts.mk @@ -38,6 +38,7 @@ KERNEL_SYMBOLS \ NETGRAPH \ PF \ + RANDOM_YARROW \ SOURCELESS_HOST \ SOURCELESS_UCODE \ USB_GADGET_EXAMPLES \ @@ -46,7 +47,8 @@ __DEFAULT_NO_OPTIONS = \ EISA \ NAND \ - OFED + OFED \ + RANDOM_FORTUNA # Some options are totally broken on some architectures. We disable # them. If you need to enable them on an experimental basis, you Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -932,6 +932,4 @@ RCTL opt_global.h # Random number generator(s) -RANDOM_YARROW opt_random.h -RANDOM_FORTUNA opt_random.h -RANDOM_DEBUG opt_random.h +RANDOM_DEBUG opt_global.h Index: sys/dev/random/build.sh =================================================================== --- sys/dev/random/build.sh +++ sys/dev/random/build.sh @@ -1,3 +1,29 @@ +#!/bin/sh +#- +# Copyright (c) 2013-2015 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. +# # $FreeBSD$ # # Basic script to build crude unit tests. @@ -11,6 +37,7 @@ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ ../../crypto/sha2/sha256c.c \ + -lz \ -o yunit_test cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \ -I../.. -lstdthreads -Wall \ @@ -21,4 +48,5 @@ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ ../../crypto/sha2/sha256c.c \ + -lz \ -o funit_test Index: sys/dev/random/dummy_rng.c =================================================================== --- sys/dev/random/dummy_rng.c +++ sys/dev/random/dummy_rng.c @@ -28,8 +28,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include #include @@ -60,43 +58,7 @@ dummy_random_init(void) { -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif - - randomdev_init_reader(dummy_random_read_phony); -} - -/* This is used only by the internal read_random(9) call, and then only - * if no entropy processor is loaded. - * - * Make a token effort to provide _some_ kind of output. No warranty of - * the quality of this output is made, mainly because its lousy. - * - * This is only used by the internal read_random(9) call when no other - * adaptor is active. - * - * It has external scope due to the way things work in - * randomdev_[de]init_reader() that the rest of the world doesn't need to - * know about. - * - * Caveat Emptor. - */ -void -dummy_random_read_phony(uint8_t *buf, u_int count) -{ - /* If no entropy device is loaded, don't spam the console with warnings */ - u_long randval; - size_t size, i; - - /* srandom() is called in kern/init_main.c:proc0_post() */ - - /* Fill buf[] with random(9) output */ - for (i = 0; i < count; i += sizeof(randval)) { - randval = random(); - size = MIN(count - i, sizeof(randval)); - memcpy(buf + i, &randval, (size_t)size); - } + randomdev_init_reader(NULL); } struct random_adaptor randomdev_dummy = { Index: sys/dev/random/fortuna.h =================================================================== --- sys/dev/random/fortuna.h +++ sys/dev/random/fortuna.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,27 @@ #ifdef _KERNEL typedef struct mtx mtx_t; +#define RANDOM_RESEED_INIT_LOCK mtx_init(&random_fortuna_reseed_mtx, "reseed mutex", NULL, MTX_DEF) +#define RANDOM_RESEED_DEINIT_LOCK mtx_destroy(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_LOCK mtx_lock(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_UNLOCK mtx_unlock(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED mtx_assert(&random_fortuna_reseed_mtx, MA_OWNED) +#else +#define RANDOM_RESEED_INIT_LOCK mtx_init(&random_fortuna_reseed_mtx, mtx_plain) +#define RANDOM_RESEED_DEINIT_LOCK mtx_destroy(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_LOCK mtx_lock(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_UNLOCK mtx_unlock(&random_fortuna_reseed_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED #endif void random_fortuna_init_alg(void); void random_fortuna_deinit_alg(void); +void random_fortuna_pre_read(void); void random_fortuna_read(uint8_t *, u_int); +void random_fortuna_post_read(void); void random_fortuna_write(uint8_t *, u_int); void random_fortuna_reseed(void); int random_fortuna_seeded(void); void random_fortuna_process_event(struct harvest_event *event); -#endif +#endif /* SYS_DEV_RANDOM_FORTUNA_H_INCLUDED */ Index: sys/dev/random/fortuna.c =================================================================== --- sys/dev/random/fortuna.c +++ sys/dev/random/fortuna.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013-2014 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,25 +25,28 @@ * */ -/* This implementation of Fortuna is based on the descriptions found in +/* + * This implementation of Fortuna is based on the descriptions found in * ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier * ("F&S"). * * The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography - * Engineering" by Ferguson, Schneier and Kohno ("FS&K"). The code has + * Engineering" by Ferguson, Schneier and Kohno ("FS&K"). This code has * not yet fully caught up with FS&K. */ -#include +#include __FBSDID("$FreeBSD$"); -#ifdef _KERNEL -#include "opt_random.h" +#include +#ifdef _KERNEL #include #include +#include #include #include +#include #include #include #include @@ -61,8 +64,6 @@ #include #include #else /* !_KERNEL */ -#include -#include #include #include #include @@ -79,22 +80,22 @@ #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_FORTUNA) +/* Defined in FS&K */ +#define NPOOLS 32 /* The number of accumulation pools */ +#define DEFPOOLSIZE 256 /* The default pool size/length */ +#define RANDOM_MAX_READ (1 << 20) /* Max bytes in a single read */ -#define NPOOLS 32 +/* + * The allowable range of DEFPOOLSIZE. The default value is above. + * Making DEFPOOLSIZE too large will mean a long time between reseeds, + * and too small may compromise initial security but get faster reseeds. + */ #define MINPOOLSIZE 64 -#define DEFPOOLSIZE 256 -#define MAXPOOLSIZE 65536 +#define MAXPOOLSIZE UINT_MAX -/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */ -CTASSERT(BLOCKSIZE == sizeof(uint128_t)); -CTASSERT(KEYSIZE == 2*BLOCKSIZE); +/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t)); +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); /* This is the beastie that needs protecting. It contains all of the * state that we are excited about. @@ -102,46 +103,38 @@ */ static struct fortuna_state { /* P_i */ - struct pool { - u_int length; - struct randomdev_hash hash; - } pool[NPOOLS]; + struct fs_pool { + u_int fsp_length; + struct randomdev_hash fsp_hash; + } fs_pool[NPOOLS]; /* ReseedCnt */ - u_int reseedcount; + u_int fs_reseedcount; - /* C - 128 bits */ - union { - uint8_t byte[BLOCKSIZE]; - uint128_t whole; - } counter; + /* C */ + uint128_t fs_counter; /* K */ - struct randomdev_key key; + struct randomdev_key fs_key; /* Extras */ - u_int minpoolsize; + u_int fs_minpoolsize; /* Extras for the OS */ - #ifdef _KERNEL /* For use when 'pacing' the reseeds */ - sbintime_t lasttime; + sbintime_t fs_lasttime; #endif } fortuna_state; -/* The random_reseed_mtx mutex protects seeding and polling/blocking. */ -static mtx_t random_reseed_mtx; - -static struct fortuna_start_cache { - uint8_t junk[PAGE_SIZE]; - size_t length; - struct randomdev_hash hash; -} fortuna_start_cache; +/* The random_fortuna_reseed_mtx mutex protects seeding and polling/blocking. */ +static mtx_t random_fortuna_reseed_mtx; #ifdef _KERNEL static struct sysctl_ctx_list random_clist; -RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE); +RANDOM_CHECK_UINT(fs_minpoolsize, MINPOOLSIZE, MAXPOOLSIZE); +#else +static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE]; #endif void @@ -152,16 +145,7 @@ struct sysctl_oid *random_fortuna_o; #endif - memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); - fortuna_start_cache.length = 0U; - randomdev_hash_init(&fortuna_start_cache.hash); - - /* Set up a lock for the reseed process */ -#ifdef _KERNEL - mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF); -#else /* !_KERNEL */ - mtx_init(&random_reseed_mtx, mtx_plain); -#endif /* _KERNEL */ + RANDOM_RESEED_INIT_LOCK; #ifdef _KERNEL /* Fortuna parameters. Do not adjust these unless you have @@ -175,41 +159,42 @@ SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO, "minpoolsize", CTLTYPE_UINT|CTLFLAG_RW, - &fortuna_state.minpoolsize, DEFPOOLSIZE, - random_check_uint_minpoolsize, "IU", + &fortuna_state.fs_minpoolsize, DEFPOOLSIZE, + random_check_uint_fs_minpoolsize, "IU", "Minimum pool size necessary to cause a reseed automatically"); - fortuna_state.lasttime = 0U; + fortuna_state.fs_lasttime = 0U; #endif - fortuna_state.minpoolsize = DEFPOOLSIZE; + fortuna_state.fs_minpoolsize = DEFPOOLSIZE; /* F&S - InitializePRNG() */ /* F&S - P_i = \epsilon */ for (i = 0; i < NPOOLS; i++) { - randomdev_hash_init(&fortuna_state.pool[i].hash); - fortuna_state.pool[i].length = 0U; + randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash); + fortuna_state.fs_pool[i].fsp_length = 0U; } /* F&S - ReseedCNT = 0 */ - fortuna_state.reseedcount = 0U; + fortuna_state.fs_reseedcount = 0U; /* F&S - InitializeGenerator() */ /* F&S - C = 0 */ - uint128_clear(&fortuna_state.counter.whole); + fortuna_state.fs_counter = UINT128_ZERO; /* F&S - K = 0 */ - memset(&fortuna_state.key, 0, sizeof(fortuna_state.key)); + bzero(&fortuna_state.fs_key, sizeof(fortuna_state.fs_key)); } void random_fortuna_deinit_alg(void) { - mtx_destroy(&random_reseed_mtx); - memset(&fortuna_state, 0, sizeof(fortuna_state)); + RANDOM_RESEED_DEINIT_LOCK; + + bzero(&fortuna_state, sizeof(fortuna_state)); } /* F&S - AddRandomEvent() */ @@ -220,224 +205,361 @@ u_int pl; /* We must be locked for all this as plenty of state gets messed with */ - mtx_lock(&random_reseed_mtx); + RANDOM_RESEED_LOCK; + + /* F&S - P_i = P_i| + * Accumulate the event into the appropriate pool + * where each event carries the destination information. + * + * The hash_init() and hash_finish() calls are done in + * random_fortuna_pre_read(). + * + * We must be locked against pool state modification which can happen + * during accumulation/reseeding and reading/regating. + */ + pl = event->he_destination % NPOOLS; + randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, event, sizeof(*event)); + /* Don't wrap the length. Doing the the hard way so as not to wrap at MAXUINT. + * This is a "saturating" add. + * XXX: FIX!!: We don't actually need lengths for anything but fs_pool[0], + * but it's been useful debugging to see them all. + */ + if (MAXPOOLSIZE - fortuna_state.fs_pool[pl].fsp_length > event->he_size) + fortuna_state.fs_pool[pl].fsp_length += event->he_size; + else + fortuna_state.fs_pool[pl].fsp_length = MAXPOOLSIZE; + + RANDOM_RESEED_UNLOCK; +} - /* Accumulate the event into the appropriate pool - * where each event carries the destination information +/* + * Process a block of data suspected to be slightly stochastic + * The length is RANDOM_KEYSIZE bytes, but data is passed in words. + */ +static void +random_fortuna_process_buffer(uint32_t *buf) +{ + static struct harvest_event event; + u_int i, pl; + static u_int dest = 0; + + /* F&S - P_i = P_i| + * Accumulate the data into the appropriate pools + * where each chunk will carry the destination information. + * + * The hash_init() and hash_finish() calls are done in + * random_fortuna_pre_read(). + * + * We must be locked against pool state modification which can happen + * during accumulation/reseeding and reading/regating. */ - /* F&S - P_i = P_i| */ + + bzero(&event, sizeof(event)); + + RANDOM_RESEED_LOCK; + + /* event.he_source = RANDOM_CACHED; */ /* The hash_init and hash_finish are done in random_fortuna_read() below */ - pl = event->he_destination % NPOOLS; - randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event)); - /* No point in counting above the outside maximum */ - fortuna_state.pool[pl].length += event->he_size; - fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE); + for (i = 0; i < RANDOM_KEYSIZE_WORDS; i++) { + event.he_somecounter = get_cyclecount(); + event.he_size = sizeof(uint32_t); + event.he_entropy[0] = buf[i]; + /* Do the actual entropy insertion */ + event.he_destination = dest++; /* Harmless cheating */ + pl = event.he_destination % NPOOLS; + randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, &event, sizeof(event)); + /* Don't wrap the length. Doing the the hard way so as not to wrap at MAXPOOLSIZE. + * This is a "saturating add". + * XXX: FIX!!: We don't actually need lengths for anything but fs_pool[0], + * but it's been useful debugging to see them all. + */ + if ((MAXPOOLSIZE - fortuna_state.fs_pool[pl].fsp_length) > event.he_size) + fortuna_state.fs_pool[pl].fsp_length += event.he_size; + else + fortuna_state.fs_pool[pl].fsp_length = MAXPOOLSIZE; + } - /* Done with state-messing */ - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK; } -/* F&S - Reseed() */ -/* Reseed Mutex is held */ +/* + * F&S - Reseed() + * This introduces new key material into the output generator. + * Additionaly it increments the output generator's counter + * variable C. When C > 0, the output generator is seeded and + * will deliver output. + * The entropy_data buffer passed is a very specific size; the + * product of NPOOLS and RANDOM_KEYSIZE. + */ static void -reseed(uint8_t *junk, u_int length) +random_fortuna_reseed_internal(uint32_t *entropy_data) { struct randomdev_hash context; - uint8_t hash[KEYSIZE]; + uint8_t hash[RANDOM_KEYSIZE]; - KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0")); -#ifdef _KERNEL - mtx_assert(&random_reseed_mtx, MA_OWNED); -#endif + KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold = 0")); + RANDOM_RESEED_ASSERT_LOCK_OWNED; /* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */ randomdev_hash_init(&context); - randomdev_hash_iterate(&context, zero_region, 512/8); - randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key)); - randomdev_hash_iterate(&context, junk, length); + randomdev_hash_iterate(&context, zero_region, RANDOM_ZERO_BLOCKSIZE); + randomdev_hash_iterate(&context, &fortuna_state.fs_key, sizeof(fortuna_state.fs_key)); + randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*NPOOLS); randomdev_hash_finish(&context, hash); randomdev_hash_init(&context); - randomdev_hash_iterate(&context, hash, KEYSIZE); + randomdev_hash_iterate(&context, hash, RANDOM_KEYSIZE); randomdev_hash_finish(&context, hash); - randomdev_encrypt_init(&fortuna_state.key, hash); - memset(hash, 0, sizeof(hash)); + randomdev_encrypt_init(&fortuna_state.fs_key, hash); + bzero(hash, sizeof(hash)); - /* Unblock the device if it was blocked due to being unseeded */ - if (uint128_is_zero(fortuna_state.counter.whole)) + /* Unblock the device if this is the first time we are reseeding. */ + if (uint128_is_zero(fortuna_state.fs_counter)) random_adaptor_unblock(); /* FS&K - C = C + 1 */ - uint128_increment(&fortuna_state.counter.whole); + uint128_increment(&fortuna_state.fs_counter); } -/* F&S - GenerateBlocks() */ -/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +/* + * F&S - GenerateBlocks() + * Generate a number of complete blocks of random output. + */ static __inline void random_fortuna_genblocks(uint8_t *buf, u_int blockcount) { u_int i; - for (i = 0u; i < blockcount; i++) { + RANDOM_RESEED_ASSERT_LOCK_OWNED; + + for (i = 0; i < blockcount; i++) { /* F&S - r = r|E(K,C) */ - randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE); - buf += BLOCKSIZE; + randomdev_encrypt(&fortuna_state.fs_key, &fortuna_state.fs_counter, buf, RANDOM_BLOCKSIZE); + buf += RANDOM_BLOCKSIZE; /* F&S - C = C + 1 */ - uint128_increment(&fortuna_state.counter.whole); + uint128_increment(&fortuna_state.fs_counter); } } -/* F&S - PseudoRandomData() */ -/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +/* + * F&S - PseudoRandomData() + * This generates no more than 2^20 bytes of data, and cleans up its + * internal state when finished. It is assumed that a whole number of + * blocks are available for writing; any excess generated will be + * ignored. + */ static __inline void random_fortuna_genrandom(uint8_t *buf, u_int bytecount) { - static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)]; + static uint8_t temp[RANDOM_BLOCKSIZE*(RANDOM_KEYS_PER_BLOCK)]; u_int blockcount; - /* F&S - assert(n < 2^20) */ - KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount)); + /* F&S - assert(n < 2^20 (== 1 MB) */ + KASSERT((bytecount <= RANDOM_MAX_READ), ("invalid single read request to fortuna of %d bytes", bytecount)); + RANDOM_RESEED_ASSERT_LOCK_OWNED; /* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */ - blockcount = bytecount / BLOCKSIZE; + blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE; random_fortuna_genblocks(buf, blockcount); - /* TODO: FIX! remove memcpy()! */ - if (bytecount % BLOCKSIZE > 0) { - random_fortuna_genblocks(temp, 1); - memcpy(buf + (blockcount * BLOCKSIZE), temp, bytecount % BLOCKSIZE); - } /* F&S - K = GenerateBlocks(2) */ - random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE); - randomdev_encrypt_init(&fortuna_state.key, temp); - memset(temp, 0, sizeof(temp)); + random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK); + randomdev_encrypt_init(&fortuna_state.fs_key, temp); + bzero(temp, sizeof(temp)); } -/* F&S - RandomData() */ -/* Used to return processed entropy from the PRNG */ -/* The argument buf points to a whole number of blocks. */ +/* + * F&S - RandomData() + * Used to return processed entropy from the PRNG + * There is a pre_read and a post_read required to be present + * (but they can be null functions) in order to allow specific + * actions at the begin or the end of a read. Fortuna does its + * reseeding in the _pre_read() part, and _post_read() is not + * used. + */ void -random_fortuna_read(uint8_t *buf, u_int bytecount) +random_fortuna_pre_read(void) { #ifdef _KERNEL - sbintime_t thistime; + sbintime_t now; #endif struct randomdev_hash context; - uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE]; + uint32_t s[NPOOLS*RANDOM_KEYSIZE_WORDS]; + uint8_t temp[RANDOM_KEYSIZE]; int i; - u_int seedlength; - /* We must be locked for all this as plenty of state gets messed with */ - mtx_lock(&random_reseed_mtx); +#ifdef _KERNEL + now = getsbinuptime(); +#endif + RANDOM_RESEED_LOCK; - /* if buf == NULL and bytecount == 0 then this is the pre-read. */ - /* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */ - if (buf == NULL) { - if (bytecount == 0) { - if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize + if (fortuna_state.fs_pool[0].fsp_length >= fortuna_state.fs_minpoolsize #ifdef _KERNEL - /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */ - && ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10) + /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */ + && (now - fortuna_state.fs_lasttime > hz/10) #endif - ) { + ) { #ifdef _KERNEL - fortuna_state.lasttime = thistime; + fortuna_state.fs_lasttime = now; #endif - seedlength = 0U; - /* F&S - ReseedCNT = ReseedCNT + 1 */ - fortuna_state.reseedcount++; - /* s = \epsilon by default */ - for (i = 0; i < NPOOLS; i++) { - /* F&S - if Divides(ReseedCnt, 2^i) ... */ - if ((fortuna_state.reseedcount % (1 << i)) == 0U) { - seedlength += KEYSIZE; - /* F&S - temp = (P_i) */ - randomdev_hash_finish(&fortuna_state.pool[i].hash, temp); - /* F&S - P_i = \epsilon */ - randomdev_hash_init(&fortuna_state.pool[i].hash); - fortuna_state.pool[i].length = 0U; - /* F&S - s = s|H(temp) */ - randomdev_hash_init(&context); - randomdev_hash_iterate(&context, temp, KEYSIZE); - randomdev_hash_finish(&context, s + i*KEYSIZE); - } - else - break; - } + /* F&S - ReseedCNT = ReseedCNT + 1 */ + fortuna_state.fs_reseedcount++; + /* s = \epsilon by default */ + for (i = 0; i < NPOOLS; i++) { + /* F&S - if Divides(ReseedCnt, 2^i) ... */ + if ((fortuna_state.fs_reseedcount % (1 << i)) == 0U) { + /* F&S - temp = (P_i) */ + randomdev_hash_finish(&fortuna_state.fs_pool[i].fsp_hash, temp); + /* F&S - P_i = \epsilon */ + randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash); + fortuna_state.fs_pool[i].fsp_length = 0U; + /* F&S - s = s|H(temp) */ + randomdev_hash_init(&context); + randomdev_hash_iterate(&context, temp, RANDOM_KEYSIZE); + randomdev_hash_finish(&context, s + i*RANDOM_KEYSIZE_WORDS); + } else + break; + } #ifdef RANDOM_DEBUG - printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount); - for (i = 0; i < NPOOLS; i++) - printf(" %d", fortuna_state.pool[i].length); - printf("\n"); + /* XXX: FIX!! This is a dangerously tedious thing to do with mutexes held */ + printf("random: reseedcount [%d]", fortuna_state.fs_reseedcount); + for (i = 0; i < NPOOLS; i++) + printf(" %X", fortuna_state.fs_pool[i].fsp_length); + printf("\n"); #endif - /* F&S */ - reseed(s, seedlength); - - /* Clean up */ - memset(s, 0, seedlength); - seedlength = 0U; - memset(temp, 0, sizeof(temp)); - memset(&context, 0, sizeof(context)); - } - } + /* F&S */ + random_fortuna_reseed_internal(s); + + /* Clean up and secure */ + bzero(s, sizeof(s)); + bzero(temp, sizeof(temp)); + bzero(&context, sizeof(context)); } - /* if buf != NULL do a regular read. */ - else - random_fortuna_genrandom(buf, bytecount); - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK; +} + +void +random_fortuna_read(uint8_t *buf, u_int bytecount) +{ + + RANDOM_RESEED_LOCK; + + /* The argument buf points to a whole number of blocks. */ + random_fortuna_genrandom(buf, bytecount); + + RANDOM_RESEED_UNLOCK; +} + +void +random_fortuna_post_read(void) +{ + + /* CWOT */ } /* Internal function to hand external entropy to the PRNG */ void random_fortuna_write(uint8_t *buf, u_int count) { - uint8_t temp[KEYSIZE]; - int i; + struct randomdev_hash hash; + uint32_t entropy_data[RANDOM_KEYSIZE_WORDS]; uintmax_t timestamp; + randomdev_hash_init(&hash); timestamp = get_cyclecount(); - randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp)); - randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_iterate(&hash, buf, count); timestamp = get_cyclecount(); - randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp)); - randomdev_hash_finish(&fortuna_start_cache.hash, temp); - for (i = 0; i < KEYSIZE; i++) - fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i]; - fortuna_start_cache.length += KEYSIZE; + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_finish(&hash, entropy_data); + bzero(&hash, sizeof(hash)); -#ifdef RANDOM_DEBUG - printf("random: %s - ", __func__); - for (i = 0; i < KEYSIZE; i++) - printf("%02X", temp[i]); - printf("\n"); -#endif + random_fortuna_process_buffer(entropy_data); + bzero(entropy_data, sizeof(entropy_data)); +} - memset(temp, 0, KEYSIZE); +void +random_fortuna_reseed(void) +{ - /* We must be locked for all this as plenty of state gets messed with */ - mtx_lock(&random_reseed_mtx); + /* CWOT */ +} + +int +random_fortuna_seeded(void) +{ + + return (!uint128_is_zero(fortuna_state.fs_counter)); +} + +/* MODULE */ + +#ifdef _KERNEL - randomdev_hash_init(&fortuna_start_cache.hash); +static void randomdev_init(void); +static void randomdev_deinit(void); + +static struct random_adaptor random_soft_processor = { + .ra_ident = "Fortuna", + .ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */ + .ra_pre_read = random_fortuna_pre_read, + .ra_read = random_fortuna_read, + .ra_post_read = random_fortuna_post_read, + .ra_write = random_fortuna_write, + .ra_reseed = random_fortuna_reseed, + .ra_seeded = random_fortuna_seeded, + .ra_init = randomdev_init, + .ra_deinit = randomdev_deinit, +}; - reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length)); - memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); +void +randomdev_init(void) +{ - mtx_unlock(&random_reseed_mtx); + random_fortuna_init_alg(); + random_harvestq_init(random_fortuna_process_event, 32); + randomdev_init_harvester(random_harvestq_internal); } void -random_fortuna_reseed(void) +randomdev_deinit(void) { - /* CWOT */ + randomdev_deinit_harvester(); + random_harvestq_deinit(); + random_fortuna_deinit_alg(); } -int -random_fortuna_seeded(void) +/* ARGSUSED */ +static int +randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused) { + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("random: SOFT: fortuna init()\n"); + random_adaptor_register("fortuna", &random_soft_processor); + break; + + case MOD_UNLOAD: + random_adaptor_deregister("fortuna"); + break; - return (!uint128_is_zero(fortuna_state.counter.whole)); + case MOD_SHUTDOWN: + break; + + default: + error = EOPNOTSUPP; + break; + + } + return (error); } -#endif /* RANDOM_FORTUNA */ +DEV_MODULE(fortuna, randomdev_soft_modevent, NULL); +MODULE_VERSION(fortuna, 1); +MODULE_DEPEND(fortuna, randomdev, 1, 1, 1); + +#endif /* _KERNEL */ Index: sys/dev/random/hash.h =================================================================== --- sys/dev/random/hash.h +++ sys/dev/random/hash.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,8 +29,12 @@ #ifndef SYS_DEV_RANDOM_HASH_H_INCLUDED #define SYS_DEV_RANDOM_HASH_H_INCLUDED -#define KEYSIZE 32 /* (in bytes) == 256 bits */ -#define BLOCKSIZE 16 /* (in bytes) == 128 bits */ +#define RANDOM_KEYSIZE 32 /* (in bytes) == 256 bits */ +#define RANDOM_KEYSIZE_WORDS (RANDOM_KEYSIZE/sizeof(uint32_t)) +#define RANDOM_BLOCKSIZE 16 /* (in bytes) == 128 bits */ +#define RANDOM_BLOCKSIZE_WORDS (RANDOM_BLOCKSIZE/sizeof(uint32_t)) +#define RANDOM_KEYS_PER_BLOCK (RANDOM_KEYSIZE/RANDOM_BLOCKSIZE) +#define RANDOM_ZERO_BLOCKSIZE 64 /* (in bytes) == 512 zero bits */ struct randomdev_hash { /* Big! Make static! */ SHA256_CTX sha; @@ -47,4 +51,4 @@ void randomdev_encrypt_init(struct randomdev_key *, const void *); void randomdev_encrypt(struct randomdev_key *context, const void *, void *, u_int); -#endif +#endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */ Index: sys/dev/random/hash.c =================================================================== --- sys/dev/random/hash.c +++ sys/dev/random/hash.c @@ -47,8 +47,8 @@ #include -/* This code presumes that KEYSIZE is twice as large as BLOCKSIZE */ -CTASSERT(KEYSIZE == 2*BLOCKSIZE); +/* This code presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); /* Initialise the hash */ void @@ -67,7 +67,7 @@ } /* Conclude by returning the hash in the supplied <*buf> which must be - * KEYSIZE bytes long. + * RANDOM_KEYSIZE bytes long. */ void randomdev_hash_finish(struct randomdev_hash *context, void *buf) @@ -77,7 +77,7 @@ } /* Initialise the encryption routine by setting up the key schedule - * from the supplied <*data> which must be KEYSIZE bytes of binary + * from the supplied <*data> which must be RANDOM_KEYSIZE bytes of binary * data. Use CBC mode for better avalanche. */ void @@ -85,12 +85,12 @@ { rijndael_cipherInit(&context->cipher, MODE_CBC, NULL); - rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data); + rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); } /* Encrypt the supplied data using the key schedule preset in the context. * bytes are encrypted from <*d_in> to <*d_out>. must be - * a multiple of BLOCKSIZE. + * a multiple of RANDOM_BLOCKSIZE. */ void randomdev_encrypt(struct randomdev_key *context, const void *d_in, void *d_out, u_int length) Index: sys/dev/random/ivy.c =================================================================== --- sys/dev/random/ivy.c +++ sys/dev/random/ivy.c @@ -46,7 +46,6 @@ #include #include -#include #include #include Index: sys/dev/random/live_entropy_sources.h =================================================================== --- sys/dev/random/live_entropy_sources.h +++ sys/dev/random/live_entropy_sources.h @@ -1,6 +1,6 @@ /*- * Copyright (c) 2013 Arthur Mesh - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,6 +54,6 @@ void live_entropy_sources_deinit(void); void live_entropy_source_register(struct live_entropy_source *); void live_entropy_source_deregister(struct live_entropy_source *); -void live_entropy_sources_feed(void); +void live_entropy_sources_feed(int); #endif /* SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED */ Index: sys/dev/random/live_entropy_sources.c =================================================================== --- sys/dev/random/live_entropy_sources.c +++ sys/dev/random/live_entropy_sources.c @@ -1,6 +1,6 @@ /*- * Copyright (c) 2013 Arthur Mesh - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,8 +28,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include #include @@ -46,7 +44,6 @@ #include #include -#include #include #include @@ -133,7 +130,7 @@ * read which can be quite expensive. */ void -live_entropy_sources_feed(void) +live_entropy_sources_feed(int pool_count) { static struct harvest_event event; struct live_entropy_sources *lles; @@ -143,26 +140,25 @@ sx_slock(&les_lock); /* - * Walk over all of live entropy sources, and feed their output + * Step over all of live entropy sources, and feed their output * to the system-wide RNG. */ read_rate = random_adaptor_read_rate(); LIST_FOREACH(lles, &les_sources, lles_entries) { - for (i = 0; i < harvest_pool_count*read_rate; i++) { + for (i = 0; i < pool_count*read_rate; i++) { /* This *must* be quick, since it's a live entropy source. */ - n = lles->lles_rsource->les_read(event.he_entropy, HARVESTSIZE); - KASSERT((n > 0 && n <= HARVESTSIZE), ("very bad return from les_read (= %d) in %s", n, __func__)); - memset(event.he_entropy + n, 0, HARVESTSIZE - n); + n = lles->lles_rsource->les_read(event.he_entropy, sizeof(event.he_entropy)); + KASSERT((n > 0 && n <= sizeof(event.he_entropy)), ("very bad return from les_read (= %d) in %s", n, __func__)); + memset(event.he_entropy + n, 0, sizeof(event.he_entropy) - n); event.he_somecounter = get_cyclecount(); event.he_size = n; event.he_bits = (n*8)/2; event.he_source = lles->lles_rsource->les_source; - event.he_destination = harvest_destination[event.he_source]++; /* Do the actual entropy insertion */ - harvest_process_event(&event); + harvest_fast_process_event(&event); } } Index: sys/dev/random/nehemiah.c =================================================================== --- sys/dev/random/nehemiah.c +++ sys/dev/random/nehemiah.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * Copyright (c) 2013 David E. O'Brien * All rights reserved. * @@ -44,7 +44,6 @@ #include #include -#include #include #include Index: sys/dev/random/random_adaptors.h =================================================================== --- sys/dev/random/random_adaptors.h +++ sys/dev/random/random_adaptors.h @@ -1,5 +1,6 @@ /*- * Copyright (c) 2013 Arthur Mesh + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,7 +34,9 @@ typedef void random_adaptor_init_func_t(void); typedef void random_adaptor_deinit_func_t(void); +typedef void random_adaptor_pre_read_func_t(void); typedef void random_adaptor_read_func_t(uint8_t *, u_int); +typedef void random_adaptor_post_read_func_t(void); typedef void random_adaptor_write_func_t(uint8_t *, u_int); typedef int random_adaptor_seeded_func_t(void); typedef void random_adaptor_reseed_func_t(void); @@ -43,7 +46,9 @@ int ra_priority; random_adaptor_init_func_t *ra_init; random_adaptor_deinit_func_t *ra_deinit; + random_adaptor_pre_read_func_t *ra_pre_read; random_adaptor_read_func_t *ra_read; + random_adaptor_post_read_func_t *ra_post_read; random_adaptor_write_func_t *ra_write; random_adaptor_reseed_func_t *ra_reseed; random_adaptor_seeded_func_t *ra_seeded; Index: sys/dev/random/random_adaptors.c =================================================================== --- sys/dev/random/random_adaptors.c +++ sys/dev/random/random_adaptors.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2013 David E. O'Brien * All rights reserved. @@ -29,8 +29,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include #include @@ -218,7 +216,7 @@ KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); /* Let the entropy source do any pre-read setup. */ - (random_adaptor->ra_read)(NULL, 0); + (random_adaptor->ra_pre_read)(); /* (Un)Blocking logic */ error = 0; @@ -235,7 +233,7 @@ __func__)); /* keep tapping away at the pre-read until we seed/unblock. */ - (random_adaptor->ra_read)(NULL, 0); + (random_adaptor->ra_pre_read)(); } mtx_lock(&random_read_rate_mtx); @@ -260,7 +258,7 @@ } /* Let the entropy source do any post-read cleanup. */ - (random_adaptor->ra_read)(NULL, 1); + (random_adaptor->ra_post_read)(); if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR) ) Index: sys/dev/random/random_harvestq.h =================================================================== --- sys/dev/random/random_harvestq.h +++ sys/dev/random/random_harvestq.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013-2014 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * All rights reserved. * @@ -30,14 +30,14 @@ #ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED #define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED -#define HARVESTSIZE 16 /* max size of each harvested entropy unit */ +#define HARVESTSIZE 4 /* Max length in words of each harvested entropy unit */ /* These are used to queue harvested packets of entropy. The entropy * buffer size is pretty arbitrary. */ struct harvest_event { uintmax_t he_somecounter; /* fast counter for clock jitter */ - uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */ + uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */ u_int he_size; /* harvested entropy byte count */ u_int he_bits; /* stats about the entropy */ u_int he_destination; /* destination pool of this entropy */ @@ -48,23 +48,6 @@ void random_harvestq_deinit(void); void random_harvestq_internal(const void *, u_int, u_int, enum random_entropy_source); -/* Pool count is used by anything needing to know how many entropy - * pools are currently being maintained. - * This is of use to (e.g.) the live source feed where we need to give - * all the pools a top-up. - */ -extern int harvest_pool_count; - -/* This is in randomdev.c as it needs to be permanently in the kernel */ -void randomdev_set_wakeup_exit(void *); - -/* Force all currently pending queue contents to clear, and kick the software processor */ -void random_harvestq_flush(void); - -/* Function called to process one harvested stochastic event */ -extern void (*harvest_process_event)(struct harvest_event *); - -/* Round-robin destination cache. */ -extern u_int harvest_destination[ENTROPYSOURCE]; +void harvest_fast_process_event(struct harvest_event *); #endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */ Index: sys/dev/random/random_harvestq.c =================================================================== --- sys/dev/random/random_harvestq.c +++ sys/dev/random/random_harvestq.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2014 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. @@ -30,8 +30,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include #include @@ -64,11 +62,40 @@ */ #define RANDOM_FIFO_MAX 1024 +/* 1 to let the kernel thread run, 0 to terminate */ +volatile int random_kthread_control; + /* - * The harvest mutex protects the consistency of the entropy Fifos and - * empty fifo and other associated structures. + * Put all the harvest queue context stuff in one place. + * this make is a bit easier to lock and protect. */ -static struct mtx harvest_mtx; +static struct harvest_context { + + /* Make multiple inits possible */ + int harvest_initialised; + + /* + * The harvest mutex protects the consistency of the entropy Fifos and + * empty fifo and other associated structures. + */ + struct mtx harvest_mtx; + + /* Round-robin destination cache. */ + u_int harvest_destination[ENTROPYSOURCE]; + + /* Function called to process one harvested stochastic event */ + void (*harvest_process_event)(struct harvest_event *); + + /* Pool count is used by anything needing to know how many entropy + * pools are currently being maintained. + * This is of use to (e.g.) the live source feed where we need to give + * all the pools a top-up. + */ + int harvest_pool_count; + + struct proc *harvest_kthread_proc; + +} harvest_context; /* * Lockable FIFO ring buffer holding entropy events @@ -91,28 +118,18 @@ volatile u_int ring_out; } entropyfifo; -/* Round-robin destination cache. */ -u_int harvest_destination[ENTROPYSOURCE]; - -/* Function called to process one harvested stochastic event */ -void (*harvest_process_event)(struct harvest_event *); - -/* Allow the sysadmin to select the broad category of - * entropy types to harvest. - */ -static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1); - -/* Pool count is used by anything needing to know how many entropy - * pools are currently being maintained. - * This is of use to (e.g.) the live source feed where we need to give - * all the pools a top-up. +/* + * Pass the given event straight through to Fortuna/Yarrow/Whatever + * This is for high-rate, efficient sources only. */ -int harvest_pool_count; - -/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ -static int random_kthread_control = 0; +void +harvest_fast_process_event(struct harvest_event *event) +{ -static struct proc *random_kthread_proc; + /* sx_assert(&les_lock, SA_LOCKED); */ + event->he_destination = harvest_context.harvest_destination[event->he_source%harvest_context.harvest_pool_count]++; + harvest_context.harvest_process_event(event); +} static void random_kthread(void *arg __unused) @@ -120,20 +137,20 @@ u_int maxloop, ring_out; /* - * Process until told to stop. - * * Locking is not needed as this is the only place we modify ring_out, and * we only examine ring_in without changing it. Both of these are volatile, * and this is a unique thread. + * XXX: FIX!! Is there a race when terminating? */ - while (random_kthread_control >= 0) { + for (random_kthread_control = 1; random_kthread_control;) { /* Deal with events, if any. Restrict the number we do in one go. */ maxloop = RANDOM_FIFO_MAX; while (entropyfifo.ring_out != entropyfifo.ring_in) { ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX; - harvest_process_event(entropyfifo.ring + ring_out); + /* This is a direct call into Yarrow/Fortuna/Whatever */ + harvest_context.harvest_process_event(entropyfifo.ring + ring_out); /* Modifying ring_out here ONLY. Sufficient for atomicity? */ entropyfifo.ring_out = ring_out; @@ -144,37 +161,26 @@ } /* - * Give the fast hardware sources a go + * Give the fast hardware sources a go. These will ultimately + * call back through the harvest_fast_process_event() function + * above. */ - live_entropy_sources_feed(); - - /* - * If a queue flush was commanded, it has now happened, - * and we can mark this by resetting the command. - * A negative value, however, terminates the thread. - */ - - if (random_kthread_control == 1) - random_kthread_control = 0; + live_entropy_sources_feed(harvest_context.harvest_pool_count); /* Some work is done, so give the rest of the OS a chance. */ - tsleep_sbt(&random_kthread_control, 0, "-", SBT_1S/10, 0, C_PREL(1)); + tsleep_sbt(&harvest_context.harvest_kthread_proc, 0, "-", SBT_1S/10, 0, C_PREL(1)); } - randomdev_set_wakeup_exit(&random_kthread_control); + wakeup(&harvest_context.harvest_kthread_proc); + kproc_exit(0); /* NOTREACHED */ } -void -random_harvestq_flush(void) -{ - - /* Command a entropy queue flush and wait for it to finish */ - random_kthread_control = 1; - while (random_kthread_control) - pause("-", hz/10); -} +/* Allow the sysadmin to select the broad category of + * entropy types to harvest. + */ +static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1); /* ARGSUSED */ RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1)); @@ -250,48 +256,54 @@ size_t size, j; struct sysctl_oid *random_sys_o; -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif + if (!harvest_context.harvest_initialised) { - random_sys_o = SYSCTL_ADD_NODE(&random_clist, - SYSCTL_STATIC_CHILDREN(_kern_random), - OID_AUTO, "harvest", CTLFLAG_RW, 0, - "Entropy Device Parameters"); + random_sys_o = SYSCTL_ADD_NODE(&random_clist, + SYSCTL_STATIC_CHILDREN(_kern_random), + OID_AUTO, "harvest", CTLFLAG_RW, 0, + "Entropy Device Parameters"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_o), - OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW, - &harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1), - random_check_uint_harvestmask, "IU", - "Entropy harvesting mask"); + SYSCTL_ADD_PROC(&random_clist, + SYSCTL_CHILDREN(random_sys_o), + OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW, + &harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1), + random_check_uint_harvestmask, "IU", + "Entropy harvesting mask"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_o), - OID_AUTO, "mask_bin", CTLTYPE_STRING | CTLFLAG_RD, - NULL, 0, random_print_harvestmask, "A", "Entropy harvesting mask (printable)"); + SYSCTL_ADD_PROC(&random_clist, + SYSCTL_CHILDREN(random_sys_o), + OID_AUTO, "mask_bin", CTLTYPE_STRING | CTLFLAG_RD, + NULL, 0, random_print_harvestmask, "A", "Entropy harvesting mask (printable)"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_o), - OID_AUTO, "mask_symbolic", CTLTYPE_STRING | CTLFLAG_RD, - NULL, 0, random_print_harvestmask_symbolic, "A", "Entropy harvesting mask (symbolic)"); + SYSCTL_ADD_PROC(&random_clist, + SYSCTL_CHILDREN(random_sys_o), + OID_AUTO, "mask_symbolic", CTLTYPE_STRING | CTLFLAG_RD, + NULL, 0, random_print_harvestmask_symbolic, "A", "Entropy harvesting mask (symbolic)"); - /* Point to the correct event_processing function */ - harvest_process_event = event_processor; + /* Initialise the harvesting mutex and in/out indexes. */ + mtx_init(&harvest_context.harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN); - /* Store the pool count (used by live source feed) */ - harvest_pool_count = poolcount; + /* Start the hash/reseed thread */ + entropyfifo.ring_in = entropyfifo.ring_out = 0U; + error = kproc_create(random_kthread, NULL, + &harvest_context.harvest_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); + + if (error != 0) + panic("Cannot create entropy maintenance thread."); - /* Initialise the harvesting mutex and in/out indexes. */ - mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN); - entropyfifo.ring_in = entropyfifo.ring_out = 0U; + harvest_context.harvest_initialised = 1; - /* Start the hash/reseed thread */ - error = kproc_create(random_kthread, NULL, - &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); + } + + mtx_lock_spin(&harvest_context.harvest_mtx); - if (error != 0) - panic("Cannot create entropy maintenance thread."); + /* Store the pool count (used by live source feed) */ + harvest_context.harvest_pool_count = poolcount; + + /* Point to the correct event_processing function */ + harvest_context.harvest_process_event = event_processor; + + mtx_unlock_spin(&harvest_context.harvest_mtx); /* Get entropy that may have been preloaded by loader(8) * and use it to pre-charge the entropy harvest queue. @@ -315,18 +327,15 @@ void random_harvestq_deinit(void) { - -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif + /* Deregister the randomness harvesting routine */ + randomdev_deinit_harvester(); /* * Command the hash/reseed thread to end and wait for it to finish + * XXX: FIX!! Is there a race when terminating the thread? */ - random_kthread_control = -1; - tsleep(&random_kthread_control, 0, "term", 0); - - mtx_destroy(&harvest_mtx); + random_kthread_control = 0; + tsleep(&harvest_context.harvest_kthread_proc, 0, "term", 0); sysctl_ctx_free(&random_clist); } @@ -360,23 +369,23 @@ return; /* Lock ring_in against multi-thread contention */ - mtx_lock_spin(&harvest_mtx); + mtx_lock_spin(&harvest_context.harvest_mtx); ring_in = (entropyfifo.ring_in + 1)%RANDOM_FIFO_MAX; if (ring_in != entropyfifo.ring_out) { /* The ring is not full */ event = entropyfifo.ring + ring_in; /* Stash the harvested stuff in the *event buffer */ - count = MIN(count, HARVESTSIZE); + count = MIN(count, sizeof(event->he_entropy)); event->he_somecounter = get_cyclecount(); event->he_size = count; event->he_bits = bits; event->he_source = origin; - event->he_destination = harvest_destination[origin]++; + event->he_destination = harvest_context.harvest_destination[origin]++; memcpy(event->he_entropy, entropy, count); - memset(event->he_entropy + count, 0, HARVESTSIZE - count); + memset(event->he_entropy + count, 0, sizeof(event->he_entropy) - count); entropyfifo.ring_in = ring_in; } - mtx_unlock_spin(&harvest_mtx); + mtx_unlock_spin(&harvest_context.harvest_mtx); } Index: sys/dev/random/randomdev.h =================================================================== --- sys/dev/random/randomdev.h +++ sys/dev/random/randomdev.h @@ -41,9 +41,6 @@ void randomdev_deinit_harvester(void); void randomdev_deinit_reader(void); -/* Stub/fake routines for when no entropy processor is loaded */ -extern void dummy_random_read_phony(uint8_t *, u_int); - /* kern.random sysctls */ #ifdef SYSCTL_DECL /* from sysctl.h */ SYSCTL_DECL(_kern_random); @@ -64,4 +61,4 @@ } #endif /* SYSCTL_DECL */ -#endif +#endif /* SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED */ Index: sys/dev/random/randomdev.c =================================================================== --- sys/dev/random/randomdev.c +++ sys/dev/random/randomdev.c @@ -27,16 +27,8 @@ */ /* - * NOTE NOTE NOTE - * - * This file is compiled into the kernel unconditionally. Any random(4) - * infrastructure that needs to be in the kernel by default goes here! - * - * Except ... - * - * The adaptor code all goes into random_adaptor.c, which is also compiled - * the kernel by default. The module in that file is initialised before - * this one. + * The adaptor code all goes into random_adaptor.c. The module in + * that file is initialised before this one. * * Other modules must be initialised after the above two, and are * software random processors which plug into random_adaptor.c. @@ -46,8 +38,6 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include #include @@ -66,6 +56,10 @@ #include #include +#include +#include + +#include #include #include #include @@ -112,21 +106,6 @@ return (error); } -/* Helper routine to enable kproc_exit() to work while the module is - * being (or has been) unloaded. - * This routine is in this file because it is always linked into the kernel, - * and will thus never be unloaded. This is critical for unloadable modules - * that have threads. - */ -void -randomdev_set_wakeup_exit(void *control) -{ - - wakeup(control); - kproc_exit(0); - /* NOTREACHED */ -} - /* ARGSUSED */ static int randomdev_modevent(module_t mod __unused, int type, void *data __unused) @@ -214,7 +193,7 @@ */ /* Hold the address of the routine which is actually called */ -static void (*read_func)(uint8_t *, u_int) = dummy_random_read_phony; +static void (*read_func)(uint8_t *, u_int); /* Initialise the reader when/if it is loaded */ void @@ -229,7 +208,7 @@ randomdev_deinit_reader(void) { - read_func = dummy_random_read_phony; + read_func = NULL; } /* Kernel API version of read_random(). @@ -239,11 +218,13 @@ int read_random(void *buf, int count) { + uint8_t local_buf[count + RANDOM_BLOCKSIZE]; - if (count < 0) + if (count <= 0 || read_func == NULL) return 0; - read_func(buf, count); + read_func(local_buf, count); + memcpy(buf, local_buf, count); return count; } Index: sys/dev/random/randomdev_soft.h =================================================================== --- sys/dev/random/randomdev_soft.h +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * 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. - * - * $FreeBSD$ - */ - -#ifndef SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED -#define SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED - -/* This header contains only those definitions that are - * specific to the entropy processor - */ - -void randomdev_init(void); -void randomdev_deinit(void); - -#endif Index: sys/dev/random/randomdev_soft.c =================================================================== --- sys/dev/random/randomdev_soft.c +++ /dev/null @@ -1,165 +0,0 @@ -/*- - * Copyright (c) 2000-2014 Mark R V Murray - * Copyright (c) 2004 Robert N. M. Watson - * 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. - * - */ - -/* - * This is the loadable infrastructure base file for software CSPRNG - * drivers such as Yarrow or Fortuna. - * - * It is anticipated that one instance of this file will be used - * for _each_ invocation of a CSPRNG, but with different #defines - * set. See below. - * - */ - -#include "opt_random.h" - -#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 - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#if defined(RANDOM_YARROW) -#include -#endif -#if defined(RANDOM_FORTUNA) -#include -#endif - -static struct random_adaptor random_soft_processor = { -#if defined(RANDOM_YARROW) -#define RANDOM_CSPRNG_NAME "yarrow" - .ra_ident = "Yarrow", - .ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */ - .ra_read = random_yarrow_read, - .ra_write = random_yarrow_write, - .ra_reseed = random_yarrow_reseed, - .ra_seeded = random_yarrow_seeded, -#endif -#if defined(RANDOM_FORTUNA) -#define RANDOM_CSPRNG_NAME "fortuna" - .ra_ident = "Fortuna", - .ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */ - .ra_read = random_fortuna_read, - .ra_write = random_fortuna_write, - .ra_reseed = random_fortuna_reseed, - .ra_seeded = random_fortuna_seeded, -#endif - .ra_init = randomdev_init, - .ra_deinit = randomdev_deinit, -}; - -void -randomdev_init(void) -{ - -#if defined(RANDOM_YARROW) - random_yarrow_init_alg(); - random_harvestq_init(random_yarrow_process_event, 2); -#endif -#if defined(RANDOM_FORTUNA) - random_fortuna_init_alg(); - random_harvestq_init(random_fortuna_process_event, 32); -#endif - - /* Register the randomness harvesting routine */ - randomdev_init_harvester(random_harvestq_internal); -} - -void -randomdev_deinit(void) -{ - /* Deregister the randomness harvesting routine */ - randomdev_deinit_harvester(); - -#if defined(RANDOM_YARROW) - random_yarrow_deinit_alg(); -#endif -#if defined(RANDOM_FORTUNA) - random_fortuna_deinit_alg(); -#endif -} - -/* ARGSUSED */ -static int -randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused) -{ - int error = 0; - - switch (type) { - case MOD_LOAD: - printf("random: SOFT: %s init()\n", RANDOM_CSPRNG_NAME); - random_adaptor_register(RANDOM_CSPRNG_NAME, &random_soft_processor); - break; - - case MOD_UNLOAD: - random_adaptor_deregister(RANDOM_CSPRNG_NAME); - break; - - case MOD_SHUTDOWN: - break; - - default: - error = EOPNOTSUPP; - break; - - } - return (error); -} - -#if defined(RANDOM_YARROW) -DEV_MODULE(yarrow, randomdev_soft_modevent, NULL); -MODULE_VERSION(yarrow, 1); -MODULE_DEPEND(yarrow, randomdev, 1, 1, 1); -#endif -#if defined(RANDOM_FORTUNA) -DEV_MODULE(fortuna, randomdev_soft_modevent, NULL); -MODULE_VERSION(fortuna, 1); -MODULE_DEPEND(fortuna, randomdev, 1, 1, 1); -#endif Index: sys/dev/random/uint128.h =================================================================== --- sys/dev/random/uint128.h +++ sys/dev/random/uint128.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2014 Mark R V Murray + * Copyright (c) 2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,40 +35,41 @@ */ #ifdef __SIZEOF_INT128__ -typedef __uint128_t uint128_t; -#else -typedef uint64_t uint128_t[2]; +#define USE_REAL_UINT128_T #endif -static __inline void -uint128_clear(uint128_t *big_uint) -{ -#ifdef __SIZEOF_INT128__ - (*big_uint) = 0ULL; +#ifdef USE_REAL_UINT128_T +typedef __uint128_t uint128_t; +#define UINT128_ZERO 0ULL #else - (*big_uint)[0] = (*big_uint)[1] = 0UL; +typedef struct { + /* Ignore endianness */ + uint64_t u128t_word0; + uint64_t u128t_word1; +} uint128_t; +static const uint128_t very_long_zero = {0UL,0UL}; +#define UINT128_ZERO very_long_zero #endif -} static __inline void -uint128_increment(uint128_t *big_uint) +uint128_increment(uint128_t *big_uintp) { -#ifdef __SIZEOF_INT128__ - (*big_uint)++; +#ifdef USE_REAL_UINT128_T + (*big_uintp)++; #else - (*big_uint)[0]++; - if ((*big_uint)[0] == 0UL) - (*big_uint)[1]++; + big_uintp->word0++; + if (big_uintp->u128t_word0 == 0UL) + big_uintp->u128t_word1++; #endif } static __inline int uint128_is_zero(uint128_t big_uint) { -#ifdef __SIZEOF_INT128__ - return (big_uint == 0ULL); +#ifdef USE_REAL_UINT128_T + return (big_uint == UINT128_ZERO); #else - return (big_uint[0] == 0UL && big_uint[1] == 0UL); + return (big_uint.u128t_word0 == 0UL && big_uint.u128t_word1 == 0UL); #endif } Index: sys/dev/random/unit_test.h =================================================================== --- sys/dev/random/unit_test.h +++ sys/dev/random/unit_test.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,18 +30,38 @@ #ifndef UNIT_TEST_H_INCLUDED #define UNIT_TEST_H_INCLUDED +#ifdef _KERNEL +#error "Random unit tests cannot be compiled into the kernel." +#endif + void random_adaptor_unblock(void); +#if defined(clang) && __has_builtin(__builtin_readcyclecounter) +#define rdtsc __builtin_readcyclecounter +#else /* !clang */ +#if defined(__amd64__) || defined(__i386__) +static __inline uint64_t +rdtsc(void) +{ + uint32_t low, high; + + __asm __volatile("rdtsc" : "=a" (low), "=d" (high)); + return (low | ((uint64_t)high << 32)); +} +#else /* __amd64__ || __i386__ */ +#error "No rdtsc() implementation available." +#endif /* __amd64__ || __i386__ */ +#endif /* !clang */ + static __inline uint64_t get_cyclecount(void) { - /* Shaddup! */ - return (4ULL); + return (rdtsc()); } // #define PAGE_SIZE 4096 -#define HARVESTSIZE 16 +#define HARVESTSIZE 4 enum random_entropy_source { RANDOM_START = 0, @@ -51,7 +71,7 @@ struct harvest_event { uintmax_t he_somecounter; /* fast counter for clock jitter */ - uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */ + uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */ u_int he_size; /* harvested entropy byte count */ u_int he_bits; /* stats about the entropy */ u_int he_destination; /* destination pool of this entropy */ Index: sys/dev/random/unit_test.c =================================================================== --- sys/dev/random/unit_test.c +++ sys/dev/random/unit_test.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,7 @@ ../../crypto/rijndael/rijndael-api-fst.c \ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ + -lz \ -o unit_test ./unit_test @@ -49,6 +50,7 @@ #include #include #include +#include #include "unit_test.h" @@ -60,8 +62,75 @@ #endif #define NUM_THREADS 3 +#define DEBUG static volatile int stopseeding = 0; + +static __inline void +check_err(int err, const char *func) +{ + if (err != Z_OK) { + fprintf(stderr, "Compress error in %s: %d\n", func, err); + exit(0); + } +} + +void * +myalloc(void *q, unsigned n, unsigned m) +{ + q = Z_NULL; + return (calloc(n, m)); +} + +void myfree(void *q, void *p) +{ + q = Z_NULL; + free(p); +} + +size_t +block_deflate(uint8_t *uncompr, uint8_t *compr, const size_t len) +{ + z_stream c_stream; + int err; + + if (len == 0U) + return (0U); + + c_stream.zalloc = myalloc; + c_stream.zfree = myfree; + c_stream.opaque = NULL; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + check_err(err, "deflateInit"); + + c_stream.next_in = uncompr; + c_stream.next_out = compr; + c_stream.avail_in = len; + c_stream.avail_out = len*2u +512u; + + while (c_stream.total_in != len && c_stream.total_out < (len*2u + 512u)) { + err = deflate(&c_stream, Z_NO_FLUSH); +#ifdef DEBUG + printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out); +#endif + check_err(err, "deflate(..., Z_NO_FLUSH)"); + } + + for (;;) { + err = deflate(&c_stream, Z_FINISH); +#ifdef DEBUG + printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out); +#endif + if (err == Z_STREAM_END) break; + check_err(err, "deflate(..., Z_STREAM_END)"); + } + + err = deflateEnd(&c_stream); + check_err(err, "deflateEnd"); + + return ((size_t)c_stream.total_out); +} void random_adaptor_unblock(void) @@ -128,6 +197,7 @@ if (i % 1000 == 0) printf("Thread write 1 - %d\n", i); if (buf != NULL) { + printf("Thread 1 writing.\n"); #ifdef RANDOM_YARROW random_yarrow_write(buf, i); #endif @@ -149,9 +219,12 @@ static int ReadCSPRNG(void *threadid) { - size_t tid; - uint8_t *buf; + size_t tid, zsize; + uint8_t *buf, *zbuf; int i; +#ifdef DEBUG + int j; +#endif tid = (size_t)threadid; printf("Thread #%zd starts\n", tid); @@ -164,42 +237,50 @@ #endif { #ifdef RANDOM_YARROW - random_yarrow_read(NULL, 0); - random_yarrow_read(NULL, 1); + random_yarrow_pre_read(); + random_yarrow_post_read(); #endif #ifdef RANDOM_FORTUNA - random_fortuna_read(NULL, 0); - random_fortuna_read(NULL, 1); + random_fortuna_pre_read(); + random_fortuna_post_read(); #endif usleep(100); } for (i = 0; i < 100000; i++) { buf = malloc(i); + zbuf = malloc(2*i + 1024); if (i % 1000 == 0) - printf("Thread read %zd - %d %p\n", tid, i, buf); - if (buf != NULL) { + printf("Thread read %zd - %d\n", tid, i); + if (buf != NULL && zbuf != NULL) { #ifdef RANDOM_YARROW - random_yarrow_read(NULL, 0); + random_yarrow_pre_read(); random_yarrow_read(buf, i); - random_yarrow_read(NULL, 1); + random_yarrow_post_read(); #endif #ifdef RANDOM_FORTUNA - random_fortuna_read(NULL, 0); + random_fortuna_pre_read(); random_fortuna_read(buf, i); - random_fortuna_read(NULL, 1); + random_fortuna_post_read(); #endif -#if 0 - { - int j; - + zsize = block_deflate(buf, zbuf, i); + if (zsize < i) + printf("ERROR!! Compressible RNG output!\n"); +#ifdef DEBUG + printf("RNG output:\n"); for (j = 0; j < i; j++) { printf(" %02X", buf[j]); if (j % 32 == 31 || j == i - 1) printf("\n"); } + printf("Compressed output:\n"); + for (j = 0; j < zsize; j++) { + printf(" %02X", zbuf[j]); + if (j % 32 == 31 || j == zsize - 1) + printf("\n"); } #endif + free(zbuf); free(buf); } usleep(100); @@ -228,7 +309,7 @@ for (t = 0; t < NUM_THREADS; t++) { printf("In main: creating thread %ld\n", t); - rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), t); + rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), NULL); if (rc != thrd_success) { printf("ERROR; return code from thrd_create() is %d\n", rc); exit(-1); Index: sys/dev/random/yarrow.h =================================================================== --- sys/dev/random/yarrow.h +++ sys/dev/random/yarrow.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,14 +31,27 @@ #ifdef _KERNEL typedef struct mtx mtx_t; +#define RANDOM_RESEED_INIT_LOCK mtx_init(&random_yarrow_reseed_mtx, "reseed mutex", NULL, MTX_DEF) +#define RANDOM_RESEED_DEINIT_LOCK mtx_destroy(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_LOCK mtx_lock(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_UNLOCK mtx_unlock(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED mtx_assert(&random_yarrow_reseed_mtx, MA_OWNED) +#else +#define RANDOM_RESEED_INIT_LOCK mtx_init(&random_yarrow_reseed_mtx, mtx_plain) +#define RANDOM_RESEED_DEINIT_LOCK mtx_destroy(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_LOCK mtx_lock(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_UNLOCK mtx_unlock(&random_yarrow_reseed_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED #endif void random_yarrow_init_alg(void); void random_yarrow_deinit_alg(void); +void random_yarrow_pre_read(void); void random_yarrow_read(uint8_t *, u_int); +void random_yarrow_post_read(void); void random_yarrow_write(uint8_t *, u_int); void random_yarrow_reseed(void); int random_yarrow_seeded(void); void random_yarrow_process_event(struct harvest_event *event); -#endif +#endif /* SYS_DEV_RANDOM_YARROW_H_INCLUDED */ Index: sys/dev/random/yarrow.c =================================================================== --- sys/dev/random/yarrow.c +++ sys/dev/random/yarrow.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,12 @@ __FBSDID("$FreeBSD$"); #ifdef _KERNEL -#include "opt_random.h" - #include #include +#include #include #include +#include #include #include #include @@ -71,68 +71,48 @@ #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 +#define RANDOM_YARROW_FAST 0 +#define RANDOM_YARROW_SLOW 1 +#define RANDOM_YARROW_NPOOLS 2 -/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */ -CTASSERT(BLOCKSIZE == sizeof(uint128_t)); -CTASSERT(KEYSIZE == 2*BLOCKSIZE); +/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t)); +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); -/* This is the beastie that needs protecting. It contains all of the +/* + * 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; + uint128_t ys_counter; /* C */ + struct randomdev_key ys_key; /* K */ + u_int ys_gengateinterval; /* Pg */ + u_int ys_bins; /* Pt/t */ + u_int ys_outputblocks; /* count output blocks for gates */ + u_int ys_slowoverthresh; /* slow pool overthreshhold reseed count */ + struct ys_pool { + u_int ysp_source_bits[ENTROPYSOURCE]; /* estimated bits of entropy per source */ + u_int ysp_thresh; /* pool reseed threshhold */ + struct randomdev_hash ysp_hash; /* accumulated entropy */ + } ys_pool[RANDOM_YARROW_NPOOLS];/* pool[0] is fast, pool[1] is slow */ + int ys_seeded; } yarrow_state; -/* The random_reseed_mtx mutex protects seeding and polling/blocking. */ -static mtx_t random_reseed_mtx; +static mtx_t random_yarrow_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(bins, RANDOM_YARROW_NPOOLS, 16); +RANDOM_CHECK_UINT(fastthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_BLOCKSIZE*8)); /* Bit counts */ +RANDOM_CHECK_UINT(slowthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_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); +static void random_yarrow_reseed_internal(u_int); void random_yarrow_init_alg(void) @@ -142,21 +122,14 @@ 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 */ + RANDOM_RESEED_INIT_LOCK; /* Start unseeded, therefore blocked. */ - yarrow_state.seeded = 0; + yarrow_state.ys_seeded = 0; #ifdef _KERNEL - /* Yarrow parameters. Do not adjust these unless you have + /* + * 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, @@ -167,65 +140,66 @@ SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "gengateinterval", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.gengateinterval, 10, + &yarrow_state.ys_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, + &yarrow_state.ys_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, + &yarrow_state.ys_pool[0].ysp_thresh, (3*(RANDOM_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), + &yarrow_state.ys_pool[1].ysp_thresh, (RANDOM_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, + &yarrow_state.ys_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; + yarrow_state.ys_gengateinterval = 10; + yarrow_state.ys_bins = 10; + yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh = (3*(RANDOM_BLOCKSIZE*8))/4; + yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh = (RANDOM_BLOCKSIZE*8); + yarrow_state.ys_slowoverthresh = 2; /* Ensure that the first time we read, we are gated. */ - yarrow_state.outputblocks = yarrow_state.gengateinterval; + yarrow_state.ys_outputblocks = yarrow_state.ys_gengateinterval; /* Initialise the fast and slow entropy pools */ - for (i = FAST; i <= SLOW; i++) { - randomdev_hash_init(&yarrow_state.pool[i].hash); + for (i = RANDOM_YARROW_FAST; i <= RANDOM_YARROW_SLOW; i++) { + randomdev_hash_init(&yarrow_state.ys_pool[i].ysp_hash); for (j = RANDOM_START; j < ENTROPYSOURCE; j++) - yarrow_state.pool[i].source[j].bits = 0U; + yarrow_state.ys_pool[i].ysp_source_bits[j] = 0U; } /* Clear the counter */ - uint128_clear(&yarrow_state.counter.whole); + yarrow_state.ys_counter = UINT128_ZERO; } void random_yarrow_deinit_alg(void) { - mtx_destroy(&random_reseed_mtx); - memset(&yarrow_state, 0, sizeof(yarrow_state)); + RANDOM_RESEED_DEINIT_LOCK; + + bzero(&yarrow_state, sizeof(yarrow_state)); #ifdef _KERNEL sysctl_ctx_free(&random_clist); @@ -235,28 +209,28 @@ static __inline void random_yarrow_post_insert(void) { - u_int pl, overthreshhold[2]; + u_int pl, overthreshhold[RANDOM_YARROW_NPOOLS]; enum random_entropy_source src; -#ifdef _KERNEL - mtx_assert(&random_reseed_mtx, MA_OWNED); -#endif + RANDOM_RESEED_ASSERT_LOCK_OWNED; + /* Count the over-threshold sources in each pool */ - for (pl = 0; pl < 2; pl++) { + for (pl = RANDOM_YARROW_FAST; pl <= RANDOM_YARROW_SLOW; pl++) { overthreshhold[pl] = 0; for (src = RANDOM_START; src < ENTROPYSOURCE; src++) { - if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh) + if (yarrow_state.ys_pool[pl].ysp_source_bits[src] > yarrow_state.ys_pool[pl].ysp_thresh) overthreshhold[pl]++; } } - /* If enough slow sources are over threshhold, then slow reseed + /* + * 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); + if (overthreshhold[RANDOM_YARROW_SLOW] >= yarrow_state.ys_slowoverthresh) + random_yarrow_reseed_internal(RANDOM_YARROW_SLOW); + else if (overthreshhold[RANDOM_YARROW_FAST] > 0 && yarrow_state.ys_seeded) + random_yarrow_reseed_internal(RANDOM_YARROW_FAST); } /* Process a single stochastic event off the harvest queue */ @@ -265,116 +239,116 @@ { u_int pl; - mtx_lock(&random_reseed_mtx); + RANDOM_RESEED_LOCK; - /* Accumulate the event into the appropriate pool + /* + * 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; + pl = event->he_destination % RANDOM_YARROW_NPOOLS; + randomdev_hash_iterate(&yarrow_state.ys_pool[pl].ysp_hash, event, sizeof(*event)); + yarrow_state.ys_pool[pl].ysp_source_bits[event->he_source] += event->he_bits; random_yarrow_post_insert(); - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK; } -/* Process a block of data suspected to be slightly stochastic */ +/* + * Process a block of data suspected to be slightly stochastic. + * The length is RANDOM_KEYSIZE bytes, but data is passed in words. + */ static void -random_yarrow_process_buffer(uint8_t *buf, u_int length) +random_yarrow_process_buffer(uint32_t *buf) { static struct harvest_event event; u_int i, pl; + static u_int dest = 0; - /* Accumulate the data into the appropriate pools + /* + * 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++) { + + RANDOM_RESEED_LOCK; + + bzero(event.he_entropy, sizeof(event.he_entropy)); + for (i = 0; i < RANDOM_KEYSIZE_WORDS; 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_destination = dest++; /* Harmless cheating */ event.he_size = sizeof(uint32_t); - *((uint32_t *)event.he_entropy) = *((uint32_t *)buf + i); - + event.he_entropy[0] = 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 + pl = event.he_destination % RANDOM_YARROW_NPOOLS; + randomdev_hash_iterate(&yarrow_state.ys_pool[pl].ysp_hash, &event, sizeof(event)); } - for (pl = FAST; pl <= SLOW; pl++) - yarrow_state.pool[pl].source[RANDOM_CACHED].bits += (length >> 4); + /* The entropy is estimated as the buffer length divided by 16. This is very conservative. */ + for (pl = RANDOM_YARROW_FAST; pl <= RANDOM_YARROW_SLOW; pl++) + yarrow_state.ys_pool[pl].ysp_source_bits[RANDOM_CACHED] += (RANDOM_KEYSIZE >> 4); random_yarrow_post_insert(); + + RANDOM_RESEED_UNLOCK; } static void -reseed(u_int fastslow) +random_yarrow_reseed_internal(u_int fastslow) { - /* Interrupt-context stack is a limited resource; make large + /* + * 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 uint8_t v[TIMEBIN][RANDOM_KEYSIZE]; /* v[i] */ + static uint128_t temp; 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")); + KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh > 0, ("random: Yarrow fast threshold = 0")); + KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh > 0, ("random: Yarrow slow threshold = 0")); + RANDOM_RESEED_ASSERT_LOCK_OWNED; #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); + /* WARNING! This is dangerously tedious to do with mutexes held! */ + printf("random: %s %s seeded = %d\n", __func__, (fastslow == RANDOM_YARROW_FAST ? "RANDOM_YARROW_FAST" : "RANDOM_YARROW_SLOW"), yarrow_state.ys_seeded); + printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh); + for (i = RANDOM_START; i < ENTROPYSOURCE; i++) + printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_source_bits[i]); + printf("\n"); + printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh, yarrow_state.ys_slowoverthresh); + for (i = RANDOM_START; i < ENTROPYSOURCE; i++) + printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_source_bits[i]); + printf("\n"); #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)); + if (fastslow == RANDOM_YARROW_SLOW) { + randomdev_hash_finish(&yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_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(&yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_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++) { + if (yarrow_state.ys_bins > TIMEBIN) + yarrow_state.ys_bins = TIMEBIN; + for (i = 1; i < yarrow_state.ys_bins; i++) { randomdev_hash_init(&context); /* v[i] #= h(v[i - 1]) */ - randomdev_hash_iterate(&context, v[i - 1], KEYSIZE); + randomdev_hash_iterate(&context, v[i - 1], RANDOM_KEYSIZE); /* v[i] #= h(v[0]) */ - randomdev_hash_iterate(&context, v[0], KEYSIZE); + randomdev_hash_iterate(&context, v[0], RANDOM_KEYSIZE); /* v[i] #= h(i) */ randomdev_hash_iterate(&context, &i, sizeof(i)); /* Return the hashval */ @@ -386,30 +360,29 @@ */ 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); + randomdev_hash_iterate(&context, &yarrow_state.ys_key, RANDOM_KEYSIZE); + for (i = 1; i < yarrow_state.ys_bins; i++) + randomdev_hash_iterate(&context, v[i], RANDOM_KEYSIZE); + randomdev_hash_finish(&context, &temp); + randomdev_encrypt_init(&yarrow_state.ys_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); + yarrow_state.ys_counter = UINT128_ZERO; + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, &temp, RANDOM_BLOCKSIZE); + yarrow_state.ys_counter = temp; /* 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; + yarrow_state.ys_pool[i].ysp_source_bits[j] = 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)); + bzero(v, sizeof(v)); + bzero(&temp, sizeof(temp)); + bzero(&context, sizeof(context)); #ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */ /* 7. Dump to seed file */ @@ -422,111 +395,180 @@ #endif /* Unblock the device if it was blocked due to being unseeded */ - if (!yarrow_state.seeded) { - yarrow_state.seeded = 1; + if (!yarrow_state.ys_seeded) { + yarrow_state.ys_seeded = 1; random_adaptor_unblock(); } } -/* Internal function to return processed entropy from the PRNG */ +static __inline void +random_yarrow_generator_gate(void) +{ + u_int i; + uint8_t temp[RANDOM_KEYSIZE]; + + RANDOM_RESEED_ASSERT_LOCK_OWNED; + + uint128_increment(&yarrow_state.ys_counter); + for (i = 0; i < RANDOM_KEYSIZE; i += RANDOM_BLOCKSIZE) + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, temp + i, RANDOM_BLOCKSIZE); + + randomdev_encrypt_init(&yarrow_state.ys_key, temp); + bzero(temp, sizeof(temp)); +} + +/* + * Used to return processed entropy from the PRNG + * There is a pre_read and a post_read required to be present + * (but they can be null functions) in order to allow specific + * actions at the begin or the end of a read. Yarrow does its + * reseeding in its own thread. The _pre_read() and _post_read() + * are not used. + */ +void +random_yarrow_pre_read(void) +{ +} + +/* + * Main read from Yarrow. + * The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size. + * Lots of code presumes this for efficiency, both here and in other + * routines. You are NOT allowed to break this! + */ void random_yarrow_read(uint8_t *buf, u_int bytecount) { - uint8_t tbuf[BLOCKSIZE]; u_int blockcount, i; - /* Check for initial/final read requests */ - if (buf == NULL) - return; + RANDOM_RESEED_LOCK; - /* The reseed task must not be jumped on */ - mtx_lock(&random_reseed_mtx); - - blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE; + blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_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); - if ((i + 1) * BLOCKSIZE > bytecount) { - /* TODO: FIX! remove memcpy()! */ - randomdev_encrypt(&yarrow_state.key, - yarrow_state.counter.byte, tbuf, BLOCKSIZE); - memcpy(buf, tbuf, bytecount - i * BLOCKSIZE); - } else { - randomdev_encrypt(&yarrow_state.key, - yarrow_state.counter.byte, buf, BLOCKSIZE); - buf += BLOCKSIZE; + if (yarrow_state.ys_outputblocks++ >= yarrow_state.ys_gengateinterval) { + random_yarrow_generator_gate(); + yarrow_state.ys_outputblocks = 0; } + uint128_increment(&yarrow_state.ys_counter); + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, buf, RANDOM_BLOCKSIZE); + buf += RANDOM_BLOCKSIZE; } - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK; +} + +void +random_yarrow_post_read(void) +{ } /* Internal function to hand external entropy to the PRNG */ void random_yarrow_write(uint8_t *buf, u_int count) { + struct randomdev_hash hash; uintmax_t timestamp; + uint32_t random_data[RANDOM_KEYSIZE_WORDS]; - /* We must be locked for all this as plenty of state gets messed with */ - mtx_lock(&random_reseed_mtx); - + /* Extra timing here is helpful to scrape scheduler timing entropy */ + randomdev_hash_init(&hash); timestamp = get_cyclecount(); - randomdev_hash_iterate(&yarrow_state.start_cache.hash, ×tamp, sizeof(timestamp)); - randomdev_hash_iterate(&yarrow_state.start_cache.hash, buf, count); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_iterate(&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 + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_finish(&hash, random_data); + + random_yarrow_process_buffer(random_data); + bzero(random_data, sizeof(random_data)); +} + +void +random_yarrow_reseed(void) +{ - random_yarrow_process_buffer(yarrow_state.start_cache.junk, KEYSIZE); - memset(yarrow_state.start_cache.junk, 0, KEYSIZE); + RANDOM_RESEED_LOCK; - mtx_unlock(&random_reseed_mtx); + random_yarrow_reseed_internal(RANDOM_YARROW_SLOW); + + RANDOM_RESEED_UNLOCK; } -static void -generator_gate(void) +int +random_yarrow_seeded(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); - } + return (yarrow_state.ys_seeded); +} + +/* MODULE */ + +#ifdef _KERNEL - randomdev_encrypt_init(&yarrow_state.key, temp); - memset(temp, 0, KEYSIZE); +static void randomdev_init(void); +static void randomdev_deinit(void); + +static struct random_adaptor random_soft_processor = { +#define RANDOM_CSPRNG_NAME "yarrow" + .ra_ident = "Yarrow", + .ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */ + .ra_pre_read = random_yarrow_pre_read, + .ra_read = random_yarrow_read, + .ra_post_read = random_yarrow_post_read, + .ra_write = random_yarrow_write, + .ra_reseed = random_yarrow_reseed, + .ra_seeded = random_yarrow_seeded, + .ra_init = randomdev_init, + .ra_deinit = randomdev_deinit, +}; + +void +randomdev_init(void) +{ + + random_yarrow_init_alg(); + random_harvestq_init(random_yarrow_process_event, 2); + randomdev_init_harvester(random_harvestq_internal); } void -random_yarrow_reseed(void) +randomdev_deinit(void) { - mtx_lock(&random_reseed_mtx); - reseed(SLOW); - mtx_unlock(&random_reseed_mtx); + randomdev_deinit_harvester(); + random_harvestq_deinit(); + random_yarrow_deinit_alg(); } -int -random_yarrow_seeded(void) +/* ARGSUSED */ +static int +randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused) { + int error = 0; + + switch (type) { + case MOD_LOAD: + printf("random: SOFT: yarrow init()\n"); + random_adaptor_register("yarrow", &random_soft_processor); + break; + + case MOD_UNLOAD: + random_adaptor_deregister("yarrow"); + break; + + case MOD_SHUTDOWN: + break; - return (yarrow_state.seeded); + default: + error = EOPNOTSUPP; + break; + + } + return (error); } -#endif /* RANDOM_YARROW */ +DEV_MODULE(yarrow, randomdev_soft_modevent, NULL); +MODULE_VERSION(yarrow, 1); +MODULE_DEPEND(yarrow, randomdev, 1, 1, 1); + +#endif /* _KERNEL */ Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -28,7 +28,6 @@ __FBSDID("$FreeBSD$"); #include "opt_bus.h" -#include "opt_random.h" #include #include Index: sys/modules/random/Makefile =================================================================== --- sys/modules/random/Makefile +++ sys/modules/random/Makefile @@ -5,10 +5,9 @@ .PATH: ${.CURDIR}/../../crypto/sha2 KMOD= random -SRCS= randomdev_soft.c SRCS+= yarrow.c fortuna.c hash.c SRCS+= rijndael-alg-fst.c rijndael-api-fst.c sha2.c sha256c.c -SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h opt_random.h +SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h CFLAGS+= -I${.CURDIR}/../..