diff options
49 files changed, 2460 insertions, 1524 deletions
diff --git a/etc/defaults/rc.conf b/etc/defaults/rc.conf index 18989fa..79799bf 100644 --- a/etc/defaults/rc.conf +++ b/etc/defaults/rc.conf @@ -645,7 +645,7 @@ update_motd="YES" # update version info in /etc/motd (or NO) entropy_file="/entropy" # Set to NO to disable caching entropy through reboots. # /var/db/entropy-file is preferred if / is not avail. entropy_dir="/var/db/entropy" # Set to NO to disable caching entropy via cron. -entropy_save_sz="2048" # Size of the entropy cache files. +entropy_save_sz="4096" # Size of the entropy cache files. entropy_save_num="8" # Number of entropy cache files to save. harvest_interrupt="YES" # Entropy device harvests interrupt randomness harvest_ethernet="YES" # Entropy device harvests ethernet randomness diff --git a/etc/rc.d/Makefile b/etc/rc.d/Makefile index 2a3057f..72b5247 100644 --- a/etc/rc.d/Makefile +++ b/etc/rc.d/Makefile @@ -57,7 +57,6 @@ FILES= DAEMON \ hostid_save \ hostname \ inetd \ - initrandom \ ip6addrctl \ ipfilter \ ipfs \ diff --git a/etc/rc.d/geli b/etc/rc.d/geli index 8b867b3..4551f71 100755 --- a/etc/rc.d/geli +++ b/etc/rc.d/geli @@ -28,7 +28,7 @@ # # PROVIDE: disks -# REQUIRE: initrandom +# REQUIRE: random # KEYWORD: nojail . /etc/rc.subr diff --git a/etc/rc.d/initrandom b/etc/rc.d/initrandom deleted file mode 100755 index 907668b..0000000 --- a/etc/rc.d/initrandom +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/sh -# -# $FreeBSD$ -# - -# PROVIDE: initrandom -# REQUIRE: dumpon ddb -# BEFORE: disks -# KEYWORD: nojail - -. /etc/rc.subr - -name="initrandom" -start_cmd="initrandom_start" -stop_cmd=":" - -initrandom_start() -{ - soft_random_generator=`sysctl kern.random 2>/dev/null` - - echo -n 'Entropy harvesting:' - - if [ \! -z "${soft_random_generator}" ] ; then - - if [ -w /dev/random ]; then - if checkyesno harvest_interrupt; then - ${SYSCTL} kern.random.sys.harvest.interrupt=1 >/dev/null - echo -n ' interrupts' - else - ${SYSCTL} kern.random.sys.harvest.interrupt=0 >/dev/null - fi - - if checkyesno harvest_ethernet; then - ${SYSCTL} kern.random.sys.harvest.ethernet=1 >/dev/null - echo -n ' ethernet' - else - ${SYSCTL} kern.random.sys.harvest.ethernet=0 >/dev/null - fi - - if checkyesno harvest_p_to_p; then - ${SYSCTL} kern.random.sys.harvest.point_to_point=1 >/dev/null - echo -n ' point_to_point' - else - ${SYSCTL} kern.random.sys.harvest.point_to_point=0 >/dev/null - fi - - if checkyesno harvest_swi; then - ${SYSCTL} kern.random.sys.harvest.swi=1 >/dev/null - echo -n ' swi' - else - ${SYSCTL} kern.random.sys.harvest.swi=0 >/dev/null - fi - fi - - fi - - echo '.' -} - -load_rc_config random -run_rc_command "$1" diff --git a/etc/rc.d/postrandom b/etc/rc.d/postrandom index 006d563..3a60830 100755 --- a/etc/rc.d/postrandom +++ b/etc/rc.d/postrandom @@ -4,7 +4,7 @@ # # PROVIDE: postrandom -# REQUIRE: initrandom random FILESYSTEMS +# REQUIRE: random FILESYSTEMS # BEFORE: LOGIN # KEYWORD: nojail diff --git a/etc/rc.d/random b/etc/rc.d/random index 8499522..c7da932 100755 --- a/etc/rc.d/random +++ b/etc/rc.d/random @@ -4,7 +4,7 @@ # # PROVIDE: random -# REQUIRE: initrandom FILESYSTEMS +# REQUIRE: FILESYSTEMS # BEFORE: netif # KEYWORD: nojail shutdown diff --git a/libexec/save-entropy/save-entropy.sh b/libexec/save-entropy/save-entropy.sh index 880a988..06319d5 100755 --- a/libexec/save-entropy/save-entropy.sh +++ b/libexec/save-entropy/save-entropy.sh @@ -53,7 +53,7 @@ case ${entropy_dir} in ;; esac -entropy_save_sz=${entropy_save_sz:-2048} +entropy_save_sz=${entropy_save_sz:-4096} entropy_save_num=${entropy_save_num:-8} if [ ! -d "${entropy_dir}" ]; then diff --git a/share/examples/kld/random_adaptor/random_adaptor_example.c b/share/examples/kld/random_adaptor/random_adaptor_example.c index da588a8..34993c1 100644 --- a/share/examples/kld/random_adaptor/random_adaptor_example.c +++ b/share/examples/kld/random_adaptor/random_adaptor_example.c @@ -35,17 +35,20 @@ __FBSDID("$FreeBSD$"); #include <sys/random.h> #include <sys/systm.h> -#include <dev/random/live_entropy_sources.h> -#include <dev/random/random_adaptors.h> #include <dev/random/randomdev.h> +#include <dev/random/randomdev_soft.h> +#include <dev/random/random_adaptors.h> +#include <dev/random/live_entropy_sources.h> -static int random_example_read(void *, int); +static void live_random_example_init(void); +static void live_random_example_deinit(void); +static u_int live_random_example_read(void *, u_int); -struct random_adaptor random_example = { - .ident = "Example RNG", - .source = RANDOM_PURE_BOGUS, /* Make sure this is in - * sys/random.h and is unique */ - .read = random_example_read, +struct random_adaptor live_random_example = { + .les_ident = "Example RNG", + .les_source = RANDOM_PURE_BOGUS, /* Make sure this is in + * sys/random.h and is unique */ + .les_read = live_random_example_read, }; /* @@ -58,8 +61,26 @@ getRandomNumber(void) return 4; /* chosen by fair dice roll, guaranteed to be random */ } -static int -random_example_read(void *buf, int c) +static void +live_random_example_init(void) +{ + + /* Do initialisation stuff here */ +} + +static void +live_random_example_deinit(void) +{ + + /* Do de-initialisation stuff here */ +} + +/* get <c> bytes of random stuff into <buf>. You may presume + * that <c> is a multiple of 2^n, with n>=3. A typical value + * is c=16. + */ +static u_int +live_random_example_read(void *buf, u_int c) { uint8_t *b; int count; @@ -69,22 +90,23 @@ random_example_read(void *buf, int c) for (count = 0; count < c; count++) b[count] = getRandomNumber(); - printf("returning %d bytes of pure randomness\n", c); + /* printf("returning %d bytes of pure randomness\n", c); */ return (c); } +/* ARGSUSED */ static int -random_example_modevent(module_t mod, int type, void *unused) +live_random_example_modevent(module_t mod __unused, int type, void *unused __unused) { int error = 0; switch (type) { case MOD_LOAD: - live_entropy_source_register(&random_example); + live_entropy_source_register(&live_random_example); break; case MOD_UNLOAD: - live_entropy_source_deregister(&random_example); + live_entropy_source_deregister(&live_random_example); break; case MOD_SHUTDOWN: @@ -98,4 +120,6 @@ random_example_modevent(module_t mod, int type, void *unused) return (error); } -LIVE_ENTROPY_SRC_MODULE(live_entropy_source_example, random_example_modevent, 1); +DEV_MODULE(live_random_example, live_random_example_modevent, NULL); +MODULE_VERSION(live_random_example, 1); +MODULE_DEPEND(live_random_example, randomdev, 1, 1, 1); diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 4eb25d5..513300c 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -2993,10 +2993,10 @@ options BROOKTREE_ALLOC_PAGES=(217*4+1) options MAXFILES=999 # Random number generator -options RANDOM_YARROW # Yarrow RNG -##options RANDOM_FORTUNA # Fortuna RNG - not yet implemented +# Only ONE of the below two may be used; they are mutually exclusive. +options RANDOM_YARROW # Yarrow CSPRNG (Default) +#options RANDOM_FORTUNA # Fortuna CSPRNG options RANDOM_DEBUG # Debugging messages -options RANDOM_RWFILE # Read and write entropy cache # Module to enable execution of application via emulators like QEMU options IMAGACT_BINMISC diff --git a/sys/conf/files b/sys/conf/files index 3e89dbb..7abcc66 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -2132,16 +2132,15 @@ rt2860.fw optional rt2860fw | ralfw \ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2860.fw" -dev/random/harvest.c standard -dev/random/dummy_rng.c standard +dev/random/randomdev.c standard dev/random/random_adaptors.c standard -dev/random/live_entropy_sources.c optional random -dev/random/random_harvestq.c optional random -dev/random/randomdev.c optional random +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 -dev/random/rwfile.c optional random dev/rc/rc.c optional rc dev/re/if_re.c optional re dev/rl/if_rl.c optional rl pci diff --git a/sys/conf/options b/sys/conf/options index f6ad35a..ae0f852 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -930,4 +930,3 @@ RCTL opt_global.h RANDOM_YARROW opt_random.h RANDOM_FORTUNA opt_random.h RANDOM_DEBUG opt_random.h -RANDOM_RWFILE opt_random.h diff --git a/sys/dev/glxsb/glxsb.c b/sys/dev/glxsb/glxsb.c index 646fe3f..9a467ed 100644 --- a/sys/dev/glxsb/glxsb.c +++ b/sys/dev/glxsb/glxsb.c @@ -476,7 +476,7 @@ glxsb_rnd(void *v) if (status & SB_RNS_TRNG_VALID) { value = bus_read_4(sc->sc_sr, SB_RANDOM_NUM); /* feed with one uint32 */ - random_harvest(&value, 4, 32/2, RANDOM_PURE_GLXSB); + random_harvest(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB); } callout_reset(&sc->sc_rngco, sc->sc_rnghz, glxsb_rnd, sc); diff --git a/sys/dev/random/build.sh b/sys/dev/random/build.sh new file mode 100755 index 0000000..e573dc1 --- /dev/null +++ b/sys/dev/random/build.sh @@ -0,0 +1,24 @@ +# $FreeBSD$ +# +# Basic script to build crude unit tests. +# +cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_YARROW \ + -I../.. -lstdthreads -Wall \ + unit_test.c \ + yarrow.c \ + hash.c \ + ../../crypto/rijndael/rijndael-api-fst.c \ + ../../crypto/rijndael/rijndael-alg-fst.c \ + ../../crypto/sha2/sha2.c \ + ../../crypto/sha2/sha256c.c \ + -o yunit_test +cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \ + -I../.. -lstdthreads -Wall \ + unit_test.c \ + fortuna.c \ + hash.c \ + ../../crypto/rijndael/rijndael-api-fst.c \ + ../../crypto/rijndael/rijndael-alg-fst.c \ + ../../crypto/sha2/sha2.c \ + ../../crypto/sha2/sha256c.c \ + -o funit_test diff --git a/sys/dev/random/dummy_rng.c b/sys/dev/random/dummy_rng.c index 810a784..a7ca4b3 100644 --- a/sys/dev/random/dummy_rng.c +++ b/sys/dev/random/dummy_rng.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com> + * Copyright (c) 2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,98 +28,92 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include <sys/param.h> +#include <sys/conf.h> #include <sys/fcntl.h> #include <sys/kernel.h> +#include <sys/lock.h> #include <sys/malloc.h> -#include <sys/module.h> #include <sys/random.h> -#include <sys/selinfo.h> +#include <sys/syslog.h> #include <sys/systm.h> -#include <sys/time.h> -#include <dev/random/random_adaptors.h> #include <dev/random/randomdev.h> - -static struct mtx dummy_random_mtx; - -/* Used to fake out unused random calls in random_adaptor */ -static void -random_null_func(void) -{ -} +#include <dev/random/random_adaptors.h> static int -dummy_random_poll(int events __unused, struct thread *td __unused) +dummy_random_zero(void) { return (0); } -static int -dummy_random_block(int flag) +static void +dummy_random(void) { - int error = 0; - - mtx_lock(&dummy_random_mtx); - - /* Blocking logic */ - while (!error) { - if (flag & O_NONBLOCK) - error = EWOULDBLOCK; - else { - printf("random: dummy device blocking on read.\n"); - error = msleep(&dummy_random_block, - &dummy_random_mtx, - PUSER | PCATCH, "block", 0); - } - } - mtx_unlock(&dummy_random_mtx); - - return (error); } +/* ARGSUSED */ static void dummy_random_init(void) { - mtx_init(&dummy_random_mtx, "sleep mtx for dummy_random", - NULL, MTX_DEF); -} - -static void -dummy_random_deinit(void) -{ +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif - mtx_destroy(&dummy_random_mtx); + randomdev_init_reader(dummy_random_read_phony); } -struct random_adaptor dummy_random = { - .ident = "Dummy entropy device that always blocks", - .init = dummy_random_init, - .deinit = dummy_random_deinit, - .block = dummy_random_block, - .poll = dummy_random_poll, - .read = (random_read_func_t *)random_null_func, - .reseed = (random_reseed_func_t *)random_null_func, - .seeded = 0, /* This device can never be seeded */ - .priority = 1, /* Bottom priority, so goes to last position */ -}; - -static int -dummy_random_modevent(module_t mod __unused, int type, void *unused __unused) +/* 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. + */ +u_int +dummy_random_read_phony(uint8_t *buf, u_int count) { + /* If no entropy device is loaded, don't spam the console with warnings */ + static int warned = 0; + u_long randval; + size_t size, i; + + if (!warned) { + log(LOG_WARNING, "random device not loaded/active; using insecure pseudo-random number generator\n"); + warned = 1; + } - switch (type) { - case MOD_LOAD: - random_adaptor_register("dummy", &dummy_random); - EVENTHANDLER_INVOKE(random_adaptor_attach, - &dummy_random); + /* srandom() is called in kern/init_main.c:proc0_post() */ - return (0); + /* 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); } - return (EINVAL); + return (count); } -RANDOM_ADAPTOR_MODULE(dummy, dummy_random_modevent, 1); +struct random_adaptor randomdev_dummy = { + .ra_ident = "Dummy", + .ra_priority = 1, /* Bottom priority, so goes to last position */ + .ra_reseed = dummy_random, + .ra_seeded = (random_adaptor_seeded_func_t *)dummy_random_zero, + .ra_read = (random_adaptor_read_func_t *)dummy_random_zero, + .ra_write = (random_adaptor_write_func_t *)dummy_random_zero, + .ra_init = dummy_random_init, + .ra_deinit = dummy_random, +}; diff --git a/sys/dev/random/fortuna.c b/sys/dev/random/fortuna.c new file mode 100644 index 0000000..46ec88d --- /dev/null +++ b/sys/dev/random/fortuna.c @@ -0,0 +1,433 @@ +/*- + * Copyright (c) 2013-2014 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#ifdef _KERNEL +#include "opt_random.h" + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/malloc.h> +#include <sys/mutex.h> +#include <sys/random.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <machine/cpu.h> + +#include <crypto/rijndael/rijndael-api-fst.h> +#include <crypto/sha2/sha2.h> + +#include <dev/random/hash.h> +#include <dev/random/randomdev.h> +#include <dev/random/random_adaptors.h> +#include <dev/random/random_harvestq.h> +#include <dev/random/uint128.h> +#include <dev/random/fortuna.h> +#else /* !_KERNEL */ +#include <sys/param.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <threads.h> + +#include "unit_test.h" + +#include <crypto/rijndael/rijndael-api-fst.h> +#include <crypto/sha2/sha2.h> + +#include <dev/random/hash.h> +#include <dev/random/uint128.h> +#include <dev/random/fortuna.h> +#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) + +#define NPOOLS 32 +#define MINPOOLSIZE 64 +#define DEFPOOLSIZE 256 +#define MAXPOOLSIZE 65536 + +/* 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 fortuna_state { + /* P_i */ + struct pool { + u_int length; + struct randomdev_hash hash; + } pool[NPOOLS]; + + /* ReseedCnt */ + u_int reseedcount; + + /* C - 128 bits */ + union { + uint8_t byte[BLOCKSIZE]; + uint128_t whole; + } counter; + + /* K */ + struct randomdev_key key; + + /* Extras */ + u_int minpoolsize; + + /* Extras for the OS */ + +#ifdef _KERNEL + /* For use when 'pacing' the reseeds */ + sbintime_t 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; + +#ifdef _KERNEL +static struct sysctl_ctx_list random_clist; +RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE); +#endif + +void +random_fortuna_init_alg(void) +{ + int i; +#ifdef _KERNEL + 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 */ + +#ifdef _KERNEL + /* Fortuna parameters. Do not adjust these unless you have + * have a very good clue about what they do! + */ + random_fortuna_o = SYSCTL_ADD_NODE(&random_clist, + SYSCTL_STATIC_CHILDREN(_kern_random), + OID_AUTO, "fortuna", CTLFLAG_RW, 0, + "Fortuna Parameters"); + + 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", + "Minimum pool size necessary to cause a reseed automatically"); + + fortuna_state.lasttime = 0U; +#endif + + fortuna_state.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; + } + + /* F&S - ReseedCNT = 0 */ + fortuna_state.reseedcount = 0U; + + /* F&S - InitializeGenerator() */ + + /* F&S - C = 0 */ + uint128_clear(&fortuna_state.counter.whole); + + /* F&S - K = 0 */ + memset(&fortuna_state.key, 0, sizeof(fortuna_state.key)); +} + +void +random_fortuna_deinit_alg(void) +{ + + mtx_destroy(&random_reseed_mtx); + memset(&fortuna_state, 0, sizeof(fortuna_state)); +} + +/* F&S - AddRandomEvent() */ +/* Process a single stochastic event off the harvest queue */ +void +random_fortuna_process_event(struct harvest_event *event) +{ + u_int pl; + + /* We must be locked for all this as plenty of state gets messed with */ + mtx_lock(&random_reseed_mtx); + + /* Accumulate the event into the appropriate pool + * where each event carries the destination information + */ + /* F&S - P_i = P_i|<harvested stuff> */ + /* 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); + + /* Done with state-messing */ + mtx_unlock(&random_reseed_mtx); +} + +/* F&S - Reseed() */ +/* Reseed Mutex is held */ +static void +reseed(uint8_t *junk, u_int length) +{ + struct randomdev_hash context; + uint8_t hash[KEYSIZE], temp[KEYSIZE]; + + KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0")); +#ifdef _KERNEL + mtx_assert(&random_reseed_mtx, MA_OWNED); +#endif + + /* F&S - temp = H(K|s) */ + randomdev_hash_init(&context); + randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key)); + randomdev_hash_iterate(&context, junk, length); + randomdev_hash_finish(&context, temp); + + /* F&S - hash = H(temp) */ + randomdev_hash_init(&context); + randomdev_hash_iterate(&context, temp, KEYSIZE); + randomdev_hash_finish(&context, hash); + + /* F&S - K = hash */ + randomdev_encrypt_init(&fortuna_state.key, temp); + memset(temp, 0, sizeof(temp)); + memset(hash, 0, sizeof(hash)); + + /* Unblock the device if it was blocked due to being unseeded */ + if (uint128_is_zero(fortuna_state.counter.whole)) + random_adaptor_unblock(); + /* F&S - C = C + 1 */ + uint128_increment(&fortuna_state.counter.whole); +} + +/* F&S - GenerateBlocks() */ +/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +static __inline void +random_fortuna_genblocks(uint8_t *buf, u_int blockcount) +{ + u_int i; + + for (i = 0u; i < blockcount; i++) { + /* F&S - r = r|E(K,C) */ + randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE); + buf += BLOCKSIZE; + + /* F&S - C = C + 1 */ + uint128_increment(&fortuna_state.counter.whole); + } +} + +/* F&S - PseudoRandomData() */ +/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +static __inline void +random_fortuna_genrandom(uint8_t *buf, u_int bytecount) +{ + static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)]; + 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 - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */ + blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE; + random_fortuna_genblocks(buf, blockcount); + + /* F&S - K = GenerateBlocks(2) */ + random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE); + randomdev_encrypt_init(&fortuna_state.key, temp); + memset(temp, 0, sizeof(temp)); +} + +/* F&S - RandomData() */ +/* Used to return processed entropy from the PRNG */ +/* The argument buf points to a whole number of blocks. */ +void +random_fortuna_read(uint8_t *buf, u_int bytecount) +{ +#ifdef _KERNEL + sbintime_t thistime; +#endif + struct randomdev_hash context; + uint8_t s[NPOOLS*KEYSIZE], temp[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); + + /* 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 +#ifdef _KERNEL + /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */ + && ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10) +#endif + ) { +#ifdef _KERNEL + fortuna_state.lasttime = thistime; +#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; + } +#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"); +#endif + /* F&S */ + reseed(s, seedlength); + + /* Clean up */ + memset(s, 0, seedlength); + seedlength = 0U; + memset(temp, 0, sizeof(temp)); + memset(&context, 0, sizeof(context)); + } + } + } + /* if buf != NULL do a regular read. */ + else + random_fortuna_genrandom(buf, bytecount); + + mtx_unlock(&random_reseed_mtx); +} + +/* 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; + uintmax_t timestamp; + + timestamp = get_cyclecount(); + randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp)); + randomdev_hash_iterate(&fortuna_start_cache.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; + +#ifdef RANDOM_DEBUG + printf("random: %s - ", __func__); + for (i = 0; i < KEYSIZE; i++) + printf("%02X", temp[i]); + printf("\n"); +#endif + + memset(temp, 0, KEYSIZE); + + /* We must be locked for all this as plenty of state gets messed with */ + mtx_lock(&random_reseed_mtx); + + randomdev_hash_init(&fortuna_start_cache.hash); + + reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length)); + memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); + + mtx_unlock(&random_reseed_mtx); +} + +void +random_fortuna_reseed(void) +{ + + /* CWOT */ +} + +int +random_fortuna_seeded(void) +{ + + return (!uint128_is_zero(fortuna_state.counter.whole)); +} + +#endif /* RANDOM_FORTUNA */ diff --git a/sys/dev/random/rwfile.h b/sys/dev/random/fortuna.h index f14fd7b..81fcac3 100644 --- a/sys/dev/random/rwfile.h +++ b/sys/dev/random/fortuna.h @@ -26,14 +26,19 @@ * $FreeBSD$ */ -#ifndef SYS_DEV_RANDOM_RWFILE_H_INCLUDED -#define SYS_DEV_RANDOM_RWFILE_H_INCLUDED - -#ifdef RANDOM_RWFILE - -int randomdev_read_file(const char *filename, void *buf, size_t); -int randomdev_write_file(const char *filename, void *buf, size_t); +#ifndef SYS_DEV_RANDOM_FORTUNA_H_INCLUDED +#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED +#ifdef _KERNEL +typedef struct mtx mtx_t; #endif +void random_fortuna_init_alg(void); +void random_fortuna_deinit_alg(void); +void random_fortuna_read(uint8_t *, u_int); +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 diff --git a/sys/dev/random/harvest.c b/sys/dev/random/harvest.c deleted file mode 100644 index 9dbae84..0000000 --- a/sys/dev/random/harvest.c +++ /dev/null @@ -1,141 +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. - * - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include <sys/param.h> -#include <sys/kthread.h> -#include <sys/lock.h> -#include <sys/malloc.h> -#include <sys/mutex.h> -#include <sys/poll.h> -#include <sys/queue.h> -#include <sys/random.h> -#include <sys/selinfo.h> -#include <sys/syslog.h> -#include <sys/systm.h> -#include <sys/sysctl.h> - -#include <machine/cpu.h> - -#include <dev/random/randomdev_soft.h> - -static int read_random_phony(void *, int); - -/* Structure holding the desired entropy sources */ -struct harvest_select harvest = { 1, 1, 1, 1 }; -static int warned = 0; - -/* hold the address of the routine which is actually called if - * the randomdev is loaded - */ -static void (*reap_func)(u_int64_t, const void *, u_int, u_int, - enum esource) = NULL; -static int (*read_func)(void *, int) = read_random_phony; - -/* Initialise the harvester at load time */ -void -randomdev_init_harvester(void (*reaper)(u_int64_t, const void *, u_int, - u_int, enum esource), int (*reader)(void *, int)) -{ - reap_func = reaper; - read_func = reader; -} - -/* Deinitialise the harvester at unload time */ -void -randomdev_deinit_harvester(void) -{ - reap_func = NULL; - read_func = read_random_phony; - warned = 0; -} - -/* Entropy harvesting routine. This is supposed to be fast; do - * not do anything slow in here! - * Implemented as in indirect call to allow non-inclusion of - * the entropy device. - * - * XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle - * counters are built in, but on older hardware it will do a real time clock - * read which can be quite expensive. - */ -void -random_harvest(const void *entropy, u_int count, u_int bits, enum esource origin) -{ - if (reap_func) - (*reap_func)(get_cyclecount(), entropy, count, bits, origin); -} - -/* Userland-visible version of read_random */ -int -read_random(void *buf, int count) -{ - return ((*read_func)(buf, count)); -} - -/* If the entropy device is not loaded, make a token effort to - * provide _some_ kind of randomness. This should only be used - * inside other RNG's, like arc4random(9). - */ -static int -read_random_phony(void *buf, int count) -{ - u_long randval; - int size, i; - - if (!warned) { - log(LOG_WARNING, "random device not loaded; using insecure entropy\n"); - warned = 1; - } - - /* srandom() is called in kern/init_main.c:proc0_post() */ - - /* Fill buf[] with random(9) output */ - for (i = 0; i < count; i+= (int)sizeof(u_long)) { - randval = random(); - size = MIN(count - i, sizeof(u_long)); - memcpy(&((char *)buf)[i], &randval, (size_t)size); - } - - return (count); -} - -/* 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 -random_set_wakeup_exit(void *control) -{ - wakeup(control); - kproc_exit(0); - /* NOTREACHED */ -} diff --git a/sys/dev/random/hash.c b/sys/dev/random/hash.c index cf0feaa..7deee87 100644 --- a/sys/dev/random/hash.c +++ b/sys/dev/random/hash.c @@ -28,18 +28,33 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#ifdef _KERNEL #include <sys/param.h> #include <sys/systm.h> +#else /* !_KERNEL */ +#include <sys/param.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <threads.h> +#include "unit_test.h" +#endif /* _KERNEL */ #include <crypto/rijndael/rijndael-api-fst.h> #include <crypto/sha2/sha2.h> #include <dev/random/hash.h> +/* This code presumes that KEYSIZE is twice as large as BLOCKSIZE */ +CTASSERT(KEYSIZE == 2*BLOCKSIZE); + /* Initialise the hash */ void randomdev_hash_init(struct randomdev_hash *context) { + SHA256_Init(&context->sha); } @@ -47,6 +62,7 @@ randomdev_hash_init(struct randomdev_hash *context) void randomdev_hash_iterate(struct randomdev_hash *context, void *data, size_t size) { + SHA256_Update(&context->sha, data, size); } @@ -56,6 +72,7 @@ randomdev_hash_iterate(struct randomdev_hash *context, void *data, size_t size) void randomdev_hash_finish(struct randomdev_hash *context, void *buf) { + SHA256_Final(buf, &context->sha); } @@ -66,6 +83,7 @@ randomdev_hash_finish(struct randomdev_hash *context, void *buf) void randomdev_encrypt_init(struct randomdev_key *context, void *data) { + rijndael_cipherInit(&context->cipher, MODE_CBC, NULL); rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data); } @@ -75,7 +93,8 @@ randomdev_encrypt_init(struct randomdev_key *context, void *data) * a multiple of BLOCKSIZE. */ void -randomdev_encrypt(struct randomdev_key *context, void *d_in, void *d_out, unsigned length) +randomdev_encrypt(struct randomdev_key *context, void *d_in, void *d_out, u_int length) { + rijndael_blockEncrypt(&context->cipher, &context->key, d_in, length*8, d_out); } diff --git a/sys/dev/random/hash.h b/sys/dev/random/hash.h index 4e6a4a0..57c0c6d 100644 --- a/sys/dev/random/hash.h +++ b/sys/dev/random/hash.h @@ -45,6 +45,6 @@ void randomdev_hash_init(struct randomdev_hash *); void randomdev_hash_iterate(struct randomdev_hash *, void *, size_t); void randomdev_hash_finish(struct randomdev_hash *, void *); void randomdev_encrypt_init(struct randomdev_key *, void *); -void randomdev_encrypt(struct randomdev_key *context, void *, void *, unsigned); +void randomdev_encrypt(struct randomdev_key *context, void *, void *, u_int); #endif diff --git a/sys/dev/random/ivy.c b/sys/dev/random/ivy.c index 23fd542..724e7aa 100644 --- a/sys/dev/random/ivy.c +++ b/sys/dev/random/ivy.c @@ -35,11 +35,11 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> +#include <sys/conf.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/module.h> #include <sys/random.h> -#include <sys/selinfo.h> #include <sys/systm.h> #include <machine/md_var.h> @@ -47,18 +47,17 @@ __FBSDID("$FreeBSD$"); #include <dev/random/randomdev.h> #include <dev/random/randomdev_soft.h> -#include <dev/random/random_harvestq.h> -#include <dev/random/live_entropy_sources.h> #include <dev/random/random_adaptors.h> +#include <dev/random/live_entropy_sources.h> #define RETRY_COUNT 10 -static int random_ivy_read(void *, int); +static u_int random_ivy_read(void *, u_int); -static struct random_hardware_source random_ivy = { - .ident = "Hardware, Intel Secure Key RNG", - .source = RANDOM_PURE_RDRAND, - .read = random_ivy_read +static struct live_entropy_source random_ivy = { + .les_ident = "Intel Secure Key RNG", + .les_source = RANDOM_PURE_RDRAND, + .les_read = random_ivy_read }; static inline int @@ -86,15 +85,17 @@ ivy_rng_store(long *buf) #endif } -static int -random_ivy_read(void *buf, int c) +/* It is specifically allowed that buf is a multiple of sizeof(long) */ +static u_int +random_ivy_read(void *buf, u_int c) { long *b; - int count; + u_int count; - KASSERT(c % sizeof(long) == 0, ("partial read %d", c)); - for (b = buf, count = c; count > 0; count -= sizeof(long), b++) { - if (ivy_rng_store(b) == 0) + KASSERT(c % sizeof(*b) == 0, ("partial read %d", c)); + b = buf; + for (count = c; count > 0; count -= sizeof(*b)) { + if (ivy_rng_store(b++) == 0) break; } return (c - count); @@ -107,14 +108,10 @@ rdrand_modevent(module_t mod, int type, void *unused) switch (type) { case MOD_LOAD: - if (cpu_feature2 & CPUID2_RDRAND) + if (cpu_feature2 & CPUID2_RDRAND) { live_entropy_source_register(&random_ivy); - else -#ifndef KLD_MODULE - if (bootverbose) -#endif - printf("%s: RDRAND is not present\n", - random_ivy.ident); + printf("random: live provider: \"%s\"\n", random_ivy.les_ident); + } break; case MOD_UNLOAD: @@ -134,4 +131,6 @@ rdrand_modevent(module_t mod, int type, void *unused) return (error); } -LIVE_ENTROPY_SRC_MODULE(random_rdrand, rdrand_modevent, 1); +DEV_MODULE(rdrand, rdrand_modevent, NULL); +MODULE_VERSION(rdrand, 1); +MODULE_DEPEND(rdrand, random_adaptors, 1, 1, 1); diff --git a/sys/dev/random/live_entropy_sources.c b/sys/dev/random/live_entropy_sources.c index d406ebd..9899cb4 100644 --- a/sys/dev/random/live_entropy_sources.c +++ b/sys/dev/random/live_entropy_sources.c @@ -28,13 +28,16 @@ #include <sys/param.h> __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include <sys/kernel.h> #include <sys/libkern.h> #include <sys/lock.h> #include <sys/malloc.h> +#include <sys/module.h> #include <sys/queue.h> #include <sys/random.h> -#include <sys/selinfo.h> +#include <sys/sbuf.h> #include <sys/sx.h> #include <sys/sysctl.h> #include <sys/systm.h> @@ -49,89 +52,73 @@ __FBSDID("$FreeBSD$"); #include "live_entropy_sources.h" -LIST_HEAD(les_head, live_entropy_sources); -static struct les_head sources = LIST_HEAD_INITIALIZER(sources); - /* - * The live_lock protects the consistency of the "struct les_head sources" + * The les_lock protects the consistency of the "struct les_head les_sources" */ -static struct sx les_lock; /* need a sleepable lock */ +static struct sx les_lock; /* Need a sleepable lock for the sbuf/sysctl stuff. */ + +LIST_HEAD(les_head, live_entropy_sources); +static struct les_head les_sources = LIST_HEAD_INITIALIZER(les_sources); void -live_entropy_source_register(struct random_hardware_source *rsource) +live_entropy_source_register(struct live_entropy_source *rsource) { - struct live_entropy_sources *les; + struct live_entropy_sources *lles; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); - les = malloc(sizeof(struct live_entropy_sources), M_ENTROPY, M_WAITOK); - les->rsource = rsource; + lles = malloc(sizeof(*lles), M_ENTROPY, M_WAITOK); + lles->lles_rsource = rsource; sx_xlock(&les_lock); - LIST_INSERT_HEAD(&sources, les, entries); + LIST_INSERT_HEAD(&les_sources, lles, lles_entries); sx_xunlock(&les_lock); } void -live_entropy_source_deregister(struct random_hardware_source *rsource) +live_entropy_source_deregister(struct live_entropy_source *rsource) { - struct live_entropy_sources *les = NULL; + struct live_entropy_sources *lles = NULL; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); sx_xlock(&les_lock); - LIST_FOREACH(les, &sources, entries) - if (les->rsource == rsource) { - LIST_REMOVE(les, entries); + LIST_FOREACH(lles, &les_sources, lles_entries) + if (lles->lles_rsource == rsource) { + LIST_REMOVE(lles, lles_entries); break; } sx_xunlock(&les_lock); - if (les != NULL) - free(les, M_ENTROPY); + if (lles != NULL) + free(lles, M_ENTROPY); } static int live_entropy_source_handler(SYSCTL_HANDLER_ARGS) { - struct live_entropy_sources *les; + struct live_entropy_sources *lles; + struct sbuf sbuf; int error, count; - count = error = 0; - sx_slock(&les_lock); - if (LIST_EMPTY(&sources)) - error = SYSCTL_OUT(req, "", 0); - else { - LIST_FOREACH(les, &sources, entries) { - - error = SYSCTL_OUT(req, ",", count++ ? 1 : 0); - if (error) - break; + sbuf_new_for_sysctl(&sbuf, NULL, 64, req); - error = SYSCTL_OUT(req, les->rsource->ident, strlen(les->rsource->ident)); - if (error) - break; - } + count = 0; + LIST_FOREACH(lles, &les_sources, lles_entries) { + sbuf_cat(&sbuf, (count++ ? ",'" : "'")); + sbuf_cat(&sbuf, lles->lles_rsource->les_ident); + sbuf_cat(&sbuf, "'"); } + error = sbuf_finish(&sbuf); + sbuf_delete(&sbuf); + sx_sunlock(&les_lock); return (error); } -static void -live_entropy_sources_init(void *unused) -{ - - SYSCTL_PROC(_kern_random, OID_AUTO, live_entropy_sources, - CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, - NULL, 0, live_entropy_source_handler, "", - "List of Active Live Entropy Sources"); - - sx_init(&les_lock, "live_entropy_sources"); -} - /* * Run through all "live" sources reading entropy for the given * number of rounds, which should be a multiple of the number @@ -140,15 +127,18 @@ live_entropy_sources_init(void *unused) * * BEWARE!!! * This function runs inside the RNG thread! Don't do anything silly! - * Remember that we are NOT holding harvest_mtx on entry! + */ +/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle + * counters are built in, but on older hardware it will do a real time clock + * read which can be quite expensive. */ void -live_entropy_sources_feed(int rounds, event_proc_f entropy_processor) +live_entropy_sources_feed(void) { - static struct harvest event; - static uint8_t buf[HARVESTSIZE]; - struct live_entropy_sources *les; - int i, n; + static struct harvest_event event; + struct live_entropy_sources *lles; + int i, read_rate; + u_int n; sx_slock(&les_lock); @@ -156,25 +146,23 @@ live_entropy_sources_feed(int rounds, event_proc_f entropy_processor) * Walk over all of live entropy sources, and feed their output * to the system-wide RNG. */ - LIST_FOREACH(les, &sources, entries) { - - for (i = 0; i < rounds; i++) { - /* - * This should be quick, since it's a live entropy - * source. - */ - /* FIXME: Whine loudly if this didn't work. */ - n = les->rsource->read(buf, sizeof(buf)); - n = MIN(n, HARVESTSIZE); - - event.somecounter = get_cyclecount(); - event.size = n; - event.bits = (n*8)/2; - event.source = les->rsource->source; - memcpy(event.entropy, buf, n); + read_rate = random_adaptor_read_rate(); + LIST_FOREACH(lles, &les_sources, lles_entries) { + + for (i = 0; i < harvest_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); + + 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 */ - entropy_processor(&event); + harvest_process_event(&event); } } @@ -182,14 +170,21 @@ live_entropy_sources_feed(int rounds, event_proc_f entropy_processor) sx_sunlock(&les_lock); } -static void -live_entropy_sources_deinit(void *unused) +void +live_entropy_sources_init(void) { - sx_destroy(&les_lock); + SYSCTL_PROC(_kern_random, OID_AUTO, live_entropy_sources, + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, live_entropy_source_handler, "", + "List of Active Live Entropy Sources"); + + sx_init(&les_lock, "live_entropy_sources"); } -SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, - live_entropy_sources_init, NULL); -SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, - live_entropy_sources_deinit, NULL); +void +live_entropy_sources_deinit(void) +{ + + sx_destroy(&les_lock); +} diff --git a/sys/dev/random/live_entropy_sources.h b/sys/dev/random/live_entropy_sources.h index 9a23070..95e2e4b 100644 --- a/sys/dev/random/live_entropy_sources.h +++ b/sys/dev/random/live_entropy_sources.h @@ -30,31 +30,30 @@ #ifndef SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED #define SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED +typedef u_int random_live_read_func_t(void *, u_int); + /* * Live entropy source is a source of entropy that can provide * specified or approximate amount of entropy immediately upon request or within * an acceptable amount of time. */ +struct live_entropy_source { + const char *les_ident; + enum random_entropy_source les_source; + random_live_read_func_t *les_read; +}; + struct live_entropy_sources { - LIST_ENTRY(live_entropy_sources) entries; /* list of providers */ - struct random_hardware_source *rsource; /* associated random adaptor */ + LIST_ENTRY(live_entropy_sources) lles_entries; /* list of providers */ + struct live_entropy_source *lles_rsource; /* associated random adaptor */ }; extern struct mtx live_mtx; -void live_entropy_source_register(struct random_hardware_source *); -void live_entropy_source_deregister(struct random_hardware_source *); -void live_entropy_sources_feed(int, event_proc_f); - -#define LIVE_ENTROPY_SRC_MODULE(name, modevent, ver) \ - static moduledata_t name##_mod = { \ - #name, \ - modevent, \ - 0 \ - }; \ - DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, \ - SI_ORDER_SECOND); \ - MODULE_VERSION(name, ver); \ - MODULE_DEPEND(name, random, 1, 1, 1); +void live_entropy_sources_init(void); +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); #endif /* SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED */ diff --git a/sys/dev/random/nehemiah.c b/sys/dev/random/nehemiah.c index b60689e..4740250 100644 --- a/sys/dev/random/nehemiah.c +++ b/sys/dev/random/nehemiah.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,11 +31,11 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> +#include <sys/conf.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/module.h> #include <sys/random.h> -#include <sys/selinfo.h> #include <sys/systm.h> #include <machine/segments.h> @@ -44,21 +45,20 @@ __FBSDID("$FreeBSD$"); #include <dev/random/randomdev.h> #include <dev/random/randomdev_soft.h> -#include <dev/random/random_harvestq.h> -#include <dev/random/live_entropy_sources.h> #include <dev/random/random_adaptors.h> +#include <dev/random/live_entropy_sources.h> static void random_nehemiah_init(void); static void random_nehemiah_deinit(void); -static int random_nehemiah_read(void *, int); +static u_int random_nehemiah_read(void *, u_int); -static struct random_hardware_source random_nehemiah = { - .ident = "Hardware, VIA Nehemiah Padlock RNG", - .source = RANDOM_PURE_NEHEMIAH, - .read = random_nehemiah_read +static struct live_entropy_source random_nehemiah = { + .les_ident = "VIA Nehemiah Padlock RNG", + .les_source = RANDOM_PURE_NEHEMIAH, + .les_read = random_nehemiah_read }; -/* TODO: now that the Davies-Meyer hash is gone and we only use +/* XXX: FIX? Now that the Davies-Meyer hash is gone and we only use * the 'xstore' instruction, do we still need to preserve the * FPU state with fpu_kern_(enter|leave)() ? */ @@ -75,7 +75,7 @@ VIA_RNG_store(void *buf) #ifdef __GNUCLIKE_ASM __asm __volatile( "movl $0,%%edx\n\t" - ".byte 0x0f, 0xa7, 0xc0" /* xstore */ + "xstore" : "=a" (retval), "+d" (rate), "+D" (buf) : : "memory" @@ -100,8 +100,9 @@ random_nehemiah_deinit(void) fpu_kern_free_ctx(fpu_ctx_save); } -static int -random_nehemiah_read(void *buf, int c) +/* It is specifically allowed that buf is a multiple of sizeof(long) */ +static u_int +random_nehemiah_read(void *buf, u_int c) { uint8_t *b; size_t count, ret; @@ -131,13 +132,9 @@ nehemiah_modevent(module_t mod, int type, void *unused) case MOD_LOAD: if (via_feature_rng & VIA_HAS_RNG) { live_entropy_source_register(&random_nehemiah); + printf("random: live provider: \"%s\"\n", random_nehemiah.les_ident); random_nehemiah_init(); - } else -#ifndef KLD_MODULE - if (bootverbose) -#endif - printf("%s: VIA Padlock RNG not present\n", - random_nehemiah.ident); + } break; case MOD_UNLOAD: @@ -158,4 +155,6 @@ nehemiah_modevent(module_t mod, int type, void *unused) return (error); } -LIVE_ENTROPY_SRC_MODULE(nehemiah, nehemiah_modevent, 1); +DEV_MODULE(nehemiah, nehemiah_modevent, NULL); +MODULE_VERSION(nehemiah, 1); +MODULE_DEPEND(nehemiah, random_adaptors, 1, 1, 1); diff --git a/sys/dev/random/random_adaptors.c b/sys/dev/random/random_adaptors.c index aa6db7b..c58f929 100644 --- a/sys/dev/random/random_adaptors.c +++ b/sys/dev/random/random_adaptors.c @@ -1,7 +1,7 @@ /*- + * Copyright (c) 2013 Mark R V Murray * Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com> * Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org> - * Copyright (c) 2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,195 +29,383 @@ #include <sys/param.h> __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include <sys/systm.h> +#include <sys/conf.h> +#include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/kthread.h> #include <sys/libkern.h> #include <sys/lock.h> #include <sys/malloc.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/poll.h> #include <sys/queue.h> #include <sys/random.h> +#include <sys/sbuf.h> #include <sys/selinfo.h> #include <sys/sx.h> #include <sys/sysctl.h> +#include <sys/uio.h> #include <sys/unistd.h> #include <dev/random/randomdev.h> -#include <dev/random/randomdev_soft.h> #include <dev/random/random_adaptors.h> +#include <dev/random/live_entropy_sources.h> +/* 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 adaptors = LIST_HEAD_INITIALIZER(adaptors); -static struct sx adaptors_lock; /* need a sleepable lock */ - -/* List for the dynamic sysctls */ -static struct sysctl_ctx_list random_clist; +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 */ -struct random_adaptor *random_adaptor; +/* 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 */ -MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures"); +static struct selinfo rsel; -int -random_adaptor_register(const char *name, struct random_adaptor *rsp) +/* 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) { - struct random_adaptors *rpp; + char rngs[128], *token, *cp; + struct random_adaptors *rra, *rrai; + struct random_adaptor *random_adaptor_previous; + int primax; - KASSERT(name != NULL && rsp != NULL, ("invalid input to %s", __func__)); + /* We are going to be messing with random_adaptor. + * Exclusive lock is mandatory. + */ + sx_assert(&random_adaptors_lock, SA_XLOCKED); - rpp = malloc(sizeof(struct random_adaptors), M_ENTROPY, M_WAITOK); - rpp->name = name; - rpp->rsp = rsp; + random_adaptor_previous = random_adaptor; - sx_xlock(&adaptors_lock); - LIST_INSERT_HEAD(&adaptors, rpp, entries); - sx_xunlock(&adaptors_lock); + random_adaptor = NULL; + if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { + cp = rngs; - return (0); + /* 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)(); + } } -struct random_adaptor * -random_adaptor_get(const char *name) -{ - struct random_adaptors *rpp; - struct random_adaptor *rsp; - rsp = NULL; +/* 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; - sx_slock(&adaptors_lock); + KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__)); - LIST_FOREACH(rpp, &adaptors, entries) - if (strcmp(rpp->name, name) == 0) - rsp = rpp->rsp; + rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK); + rra->rra_name = name; + rra->rra_ra = ra; - sx_sunlock(&adaptors_lock); + sx_xlock(&random_adaptors_lock); + LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries); + random_adaptor_choose(); + sx_xunlock(&random_adaptors_lock); - return (rsp); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); } -/* - * Walk a list of registered random(4) adaptors and pick the last non-selected - * one. - * - * If none are selected, use yarrow if available. - */ void -random_adaptor_choose(struct random_adaptor **adaptor) +random_adaptor_deregister(const char *name) { - char rngs[128], *token, *cp; - struct random_adaptors *rppi, *ramax; - unsigned primax; + struct random_adaptors *rra; - KASSERT(adaptor != NULL, ("pre-conditions failed")); + KASSERT(name != NULL, ("invalid input to %s", __func__)); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - *adaptor = NULL; - if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { - cp = rngs; + 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); - while ((token = strsep(&cp, ",")) != NULL) - if ((*adaptor = random_adaptor_get(token)) != NULL) - break; - else if (bootverbose) - printf("%s random adaptor is not available," - " skipping\n", token); + 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); } - primax = 0U; - if (*adaptor == NULL) { - /* - * Fall back to the highest priority item on the available - * RNG list. - */ - sx_slock(&adaptors_lock); + mtx_lock(&random_read_rate_mtx); - ramax = NULL; - LIST_FOREACH(rppi, &adaptors, entries) { - if (rppi->rsp->priority >= primax) { - ramax = rppi; - primax = rppi->rsp->priority; - } + /* 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); } - if (ramax != NULL) - *adaptor = ramax->rsp; - sx_sunlock(&adaptors_lock); + /* Let the entropy source do any post-read cleanup. */ + (random_adaptor->ra_read)(NULL, 1); - if (bootverbose && *adaptor) - printf("Falling back to <%s> random adaptor\n", - (*adaptor)->ident); + free(random_buf, M_ENTROPY); } + + sx_sunlock(&random_adaptors_lock); + + return (error); } -static void -random_adaptors_deinit(void *unused) +int +random_adaptor_read_rate(void) { + int ret; - sx_destroy(&adaptors_lock); - sysctl_ctx_free(&random_clist); + 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); } -static int -random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS) +/* ARGSUSED */ +int +random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { - struct random_adaptors *rpp; - int error, count; + int c, error = 0; + void *random_buf; - count = error = 0; +#ifdef RANDOM_DEBUG + printf("random: %s %zd\n", __func__, uio->uio_resid); +#endif - sx_slock(&adaptors_lock); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - if (LIST_EMPTY(&adaptors)) - error = SYSCTL_OUT(req, "", 0); - else { - LIST_FOREACH(rpp, &adaptors, entries) { + sx_slock(&random_adaptors_lock); - error = SYSCTL_OUT(req, ",", count++ ? 1 : 0); - if (error) - break; + random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); - error = SYSCTL_OUT(req, rpp->name, strlen(rpp->name)); - if (error) - break; - } + 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); } - sx_sunlock(&adaptors_lock); + free(random_buf, M_ENTROPY); + + sx_sunlock(&random_adaptors_lock); return (error); } -static int -random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS) +/* ARGSUSED */ +int +random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { - struct random_adaptor *rsp; - struct random_adaptors *rpp; - const char *name; - int error; - name = NULL; - rsp = random_adaptor; +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif - if (rsp != NULL) { - sx_slock(&adaptors_lock); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - LIST_FOREACH(rpp, &adaptors, entries) - if (rpp->rsp == rsp) - name = rpp->name; + sx_slock(&random_adaptors_lock); - sx_sunlock(&adaptors_lock); + if (events & (POLLIN | POLLRDNORM)) { + if (random_adaptor->ra_seeded()) + events &= (POLLIN | POLLRDNORM); + else + selrecord(td, &rsel); } - if (rsp == NULL || name == NULL) - error = SYSCTL_OUT(req, "", 0); - else - error = SYSCTL_OUT(req, name, strlen(name)); + 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 void -random_adaptors_init(void *unused) +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", @@ -228,24 +416,74 @@ random_adaptors_init(void *unused) NULL, 0, random_sysctl_active_adaptor_handler, "A", "Active Random Number Generator Adaptor"); - sx_init(&adaptors_lock, "random_adaptors"); + 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(); } -SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator"); +void +random_adaptors_deinit(void) +{ + +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif + + live_entropy_sources_deinit(); -SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, random_adaptors_init, - NULL); -SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, - random_adaptors_deinit, NULL); + /* 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. + */ +#ifdef RANDOM_AUTOSEED +/* ARGSUSED */ static void -random_adaptors_reseed(void *unused) +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); - (void)unused; - if (random_adaptor != NULL) - (*random_adaptor->reseed)(); arc4rand(NULL, 0, 1); } -SYSINIT(random_reseed, SI_SUB_INTRINSIC_POST, SI_ORDER_SECOND, +SYSINIT(random_seed, SI_SUB_INTRINSIC_POST, SI_ORDER_LAST, random_adaptors_reseed, NULL); +#endif /* RANDOM_AUTOSEED */ diff --git a/sys/dev/random/random_adaptors.h b/sys/dev/random/random_adaptors.h index 4765694..08076ce 100644 --- a/sys/dev/random/random_adaptors.h +++ b/sys/dev/random/random_adaptors.h @@ -29,43 +29,46 @@ #ifndef SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED #define SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED -#include <sys/eventhandler.h> - MALLOC_DECLARE(M_ENTROPY); +typedef void random_adaptor_init_func_t(void); +typedef void random_adaptor_deinit_func_t(void); +typedef void random_adaptor_read_func_t(uint8_t *, u_int); +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); + +struct random_adaptor { + const char *ra_ident; + int ra_priority; + random_adaptor_init_func_t *ra_init; + random_adaptor_deinit_func_t *ra_deinit; + random_adaptor_read_func_t *ra_read; + random_adaptor_write_func_t *ra_write; + random_adaptor_reseed_func_t *ra_reseed; + random_adaptor_seeded_func_t *ra_seeded; +}; + struct random_adaptors { - LIST_ENTRY(random_adaptors) entries; /* list of providers */ - const char *name; /* name of random adaptor */ - struct random_adaptor *rsp; + LIST_ENTRY(random_adaptors) rra_entries; /* list of providers */ + const char *rra_name; /* name of random adaptor */ + struct random_adaptor *rra_ra; }; -struct random_adaptor *random_adaptor_get(const char *); -int random_adaptor_register(const char *, struct random_adaptor *); -void random_adaptor_choose(struct random_adaptor **); +/* Dummy "always-block" pseudo-device */ +extern struct random_adaptor randomdev_dummy; -extern struct random_adaptor *random_adaptor; +void random_adaptors_init(void); +void random_adaptors_deinit(void); -/* - * random_adaptor's should be registered prior to - * random module (SI_SUB_DRIVERS/SI_ORDER_MIDDLE) - */ -#define RANDOM_ADAPTOR_MODULE(name, modevent, ver) \ - static moduledata_t name##_mod = { \ - #name, \ - modevent, \ - 0 \ - }; \ - DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, \ - SI_ORDER_SECOND); \ - MODULE_VERSION(name, ver); \ - MODULE_DEPEND(name, random, 1, 1, 1); +void random_adaptor_register(const char *, struct random_adaptor *); +void random_adaptor_deregister(const char *); -typedef void (*random_adaptor_attach_hook)(void *, struct random_adaptor *); -EVENTHANDLER_DECLARE(random_adaptor_attach, random_adaptor_attach_hook); +int random_adaptor_read(struct cdev *, struct uio *, int); +int random_adaptor_write(struct cdev *, struct uio *, int); +int random_adaptor_poll(struct cdev *, int, struct thread *); -/* kern.random sysctls */ -#ifdef SYSCTL_DECL /* from sysctl.h */ -SYSCTL_DECL(_kern_random); -#endif /* SYSCTL_DECL */ +int random_adaptor_read_rate(void); +void random_adaptor_unblock(void); #endif /* SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED */ diff --git a/sys/dev/random/random_harvestq.c b/sys/dev/random/random_harvestq.c index b7b8381..df6a853 100644 --- a/sys/dev/random/random_harvestq.c +++ b/sys/dev/random/random_harvestq.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2014 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. @@ -42,230 +42,293 @@ __FBSDID("$FreeBSD$"); #include <sys/malloc.h> #include <sys/mutex.h> #include <sys/random.h> -#include <sys/selinfo.h> +#include <sys/sbuf.h> #include <sys/sysctl.h> #include <sys/unistd.h> #include <machine/cpu.h> -#include <machine/vmparam.h> #include <dev/random/randomdev.h> -#include <dev/random/randomdev_soft.h> #include <dev/random/random_adaptors.h> #include <dev/random/random_harvestq.h> #include <dev/random/live_entropy_sources.h> -#include <dev/random/rwfile.h> -#define RANDOM_FIFO_MAX 1024 /* How many events to queue up */ +/* List for the dynamic sysctls */ +static struct sysctl_ctx_list random_clist; /* - * The harvest mutex protects the consistency of the entropy fifos and + * How many events to queue up. We create this many items in + * an 'empty' queue, then transfer them to the 'harvest' queue with + * supplied junk. When used, they are transferred back to the + * 'empty' queue. + */ +#define RANDOM_FIFO_MAX 1024 + +/* + * The harvest mutex protects the consistency of the entropy Fifos and * empty fifo and other associated structures. */ -struct mtx harvest_mtx; +static struct mtx harvest_mtx; -/* Lockable FIFO queue holding entropy buffers */ -struct entropyfifo { - int count; - STAILQ_HEAD(harvestlist, harvest) head; -}; +/* + * Lockable FIFO ring buffer holding entropy events + * If ring_in == ring_out, + * the buffer is empty. + * If (ring_in + 1) == ring_out (MOD RANDOM_FIFO_MAX), + * the buffer is full. + * + * The ring_in variable needs locking as there are multiple + * sources to the ring. Only the sources may change ring_in, + * but the consumer may examine it. + * + * The ring_out variable does not need locking as there is + * only one consumer. Only the consumer may change ring_out, + * but the sources may examine it. + */ +static struct entropyfifo { + struct harvest_event ring[RANDOM_FIFO_MAX]; + volatile u_int ring_in; + 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 *); -/* Empty entropy buffers */ -static struct entropyfifo emptyfifo; +/* Allow the sysadmin to select the broad category of + * entropy types to harvest. + */ +static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1); -/* Harvested entropy */ -static struct entropyfifo harvestfifo; +/* 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; /* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ -int random_kthread_control = 0; +static int random_kthread_control = 0; static struct proc *random_kthread_proc; -#ifdef RANDOM_RWFILE -static const char *entropy_files[] = { - "/entropy", - NULL -}; -#endif - -/* Deal with entropy cached externally if this is present. - * Lots of policy may eventually arrive in this function. - * Called after / is mounted. - */ static void -random_harvestq_cache(void *arg __unused) +random_kthread(void *arg __unused) { - uint8_t *keyfile, *data; - size_t size, i; -#ifdef RANDOM_RWFILE - const char **entropy_file; - uint8_t *zbuf; - int error; -#endif - - /* Get stuff that may have been preloaded by loader(8) */ - keyfile = preload_search_by_type("/boot/entropy"); - if (keyfile != NULL) { - data = preload_fetch_addr(keyfile); - size = preload_fetch_size(keyfile); - if (data != NULL && size != 0) { - for (i = 0; i < size; i += 16) - random_harvestq_internal(get_cyclecount(), data + i, 16, 16, RANDOM_CACHED); - printf("random: read %zu bytes from preloaded cache\n", size); - bzero(data, size); - } - else - printf("random: no preloaded entropy cache available\n"); - } + u_int maxloop, ring_out; -#ifdef RANDOM_RWFILE - /* Read and attempt to overwrite the entropy cache files. - * If the file exists, can be read and then overwritten, - * then use it. Ignore it otherwise, but print out what is - * going on. + /* + * 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. */ - data = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); - zbuf = __DECONST(void *, zero_region); - for (entropy_file = entropy_files; *entropy_file; entropy_file++) { - error = randomdev_read_file(*entropy_file, data, PAGE_SIZE); - if (error == 0) { - printf("random: entropy cache '%s' provides %ld bytes\n", *entropy_file, (long)PAGE_SIZE); - error = randomdev_write_file(*entropy_file, zbuf, PAGE_SIZE); - if (error == 0) { - printf("random: entropy cache '%s' contents used and successfully overwritten\n", *entropy_file); - for (i = 0; i < PAGE_SIZE; i += 16) - random_harvestq_internal(get_cyclecount(), data + i, 16, 16, RANDOM_CACHED); - } - else - printf("random: entropy cache '%s' not overwritten and therefore not used; error = %d\n", *entropy_file, error); - } - else - printf("random: entropy cache '%s' not present or unreadable; error = %d\n", *entropy_file, error); - } - bzero(data, PAGE_SIZE); - free(data, M_ENTROPY); -#endif -} -EVENTHANDLER_DEFINE(mountroot, random_harvestq_cache, NULL, 0); + while (random_kthread_control >= 0) { -static void -random_kthread(void *arg) -{ - STAILQ_HEAD(, harvest) local_queue; - struct harvest *event = NULL; - int local_count; - event_proc_f entropy_processor = arg; + /* Deal with events, if any. Restrict the number we do in one go. */ + maxloop = RANDOM_FIFO_MAX; + while (entropyfifo.ring_out != entropyfifo.ring_in) { - STAILQ_INIT(&local_queue); - local_count = 0; + ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX; + harvest_process_event(entropyfifo.ring + ring_out); + /* Modifying ring_out here ONLY. Sufficient for atomicity? */ + entropyfifo.ring_out = ring_out; - /* Process until told to stop */ - mtx_lock_spin(&harvest_mtx); - for (; random_kthread_control >= 0;) { - - /* - * Grab all the entropy events. - * Drain entropy source records into a thread-local - * queue for processing while not holding the mutex. - */ - STAILQ_CONCAT(&local_queue, &harvestfifo.head); - local_count += harvestfifo.count; - harvestfifo.count = 0; + /* The ring may be filled quickly so don't loop forever. */ + if (--maxloop) + break; - /* - * Deal with events, if any. - * Then transfer the used events back into the empty fifo. - */ - if (!STAILQ_EMPTY(&local_queue)) { - mtx_unlock_spin(&harvest_mtx); - STAILQ_FOREACH(event, &local_queue, next) - entropy_processor(event); - mtx_lock_spin(&harvest_mtx); - STAILQ_CONCAT(&emptyfifo.head, &local_queue); - emptyfifo.count += local_count; - local_count = 0; } - KASSERT(local_count == 0, ("random_kthread: local_count %d", - local_count)); - /* - * Do only one round of the hardware sources for now. - * Later we'll need to make it rate-adaptive. + * Give the fast hardware sources a go */ - mtx_unlock_spin(&harvest_mtx); - live_entropy_sources_feed(1, entropy_processor); - mtx_lock_spin(&harvest_mtx); + 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; - /* Work done, so don't belabour the issue */ - msleep_spin_sbt(&random_kthread_control, &harvest_mtx, - "-", SBT_1S/10, 0, C_PREL(1)); + /* 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)); } - mtx_unlock_spin(&harvest_mtx); - random_set_wakeup_exit(&random_kthread_control); + randomdev_set_wakeup_exit(&random_kthread_control); /* NOTREACHED */ } void -random_harvestq_init(event_proc_f cb) +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); +} + +/* ARGSUSED */ +RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1)); + +/* ARGSUSED */ +static int +random_print_harvestmask(SYSCTL_HANDLER_ARGS) { + struct sbuf sbuf; int error, i; - struct harvest *np; - /* Initialise the harvest fifos */ + error = sysctl_wire_old_buffer(req, 0); + if (error == 0) { + sbuf_new_for_sysctl(&sbuf, NULL, 128, req); + for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--) + sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? "1" : "0"); + error = sbuf_finish(&sbuf); + sbuf_delete(&sbuf); + } + + return (error); +} - /* Contains the currently unused event structs. */ - STAILQ_INIT(&emptyfifo.head); - for (i = 0; i < RANDOM_FIFO_MAX; i++) { - np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK); - STAILQ_INSERT_TAIL(&emptyfifo.head, np, next); +static const char *(random_source_descr[]) = { + "CACHED", + "ATTACH", + "KEYBOARD", + "MOUSE", + "NET_TUN", + "NET_ETHER", + "NET_NG", + "INTERRUPT", + "SWI", + "UMA_ALLOC", + "", /* "ENVIRONMENTAL_END" */ + "PURE_OCTEON", + "PURE_SAFE", + "PURE_GLXSB", + "PURE_UBSEC", + "PURE_HIFN", + "PURE_RDRAND", + "PURE_NEHEMIAH", + "PURE_RNDTEST", + /* "ENTROPYSOURCE" */ +}; + +/* ARGSUSED */ +static int +random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS) +{ + struct sbuf sbuf; + int error, i; + + error = sysctl_wire_old_buffer(req, 0); + if (error == 0) { + sbuf_new_for_sysctl(&sbuf, NULL, 128, req); + for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--) { + sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END - 1) ? "" : ","); + sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? random_source_descr[i] : ""); + } + error = sbuf_finish(&sbuf); + sbuf_delete(&sbuf); } - emptyfifo.count = RANDOM_FIFO_MAX; - /* Will contain the queued-up events. */ - STAILQ_INIT(&harvestfifo.head); - harvestfifo.count = 0; + return (error); +} +void +random_harvestq_init(void (*event_processor)(struct harvest_event *), int poolcount) +{ + uint8_t *keyfile, *data; + int error; + size_t size, j; + struct sysctl_oid *random_sys_o; + +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif + + 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_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)"); + + /* Point to the correct event_processing function */ + harvest_process_event = event_processor; + + /* Store the pool count (used by live source feed) */ + harvest_pool_count = poolcount; + + /* 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; /* Start the hash/reseed thread */ - error = kproc_create(random_kthread, cb, - &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); /* RANDOM_CSPRNG_NAME */ + error = kproc_create(random_kthread, NULL, + &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); if (error != 0) panic("Cannot create entropy maintenance thread."); + + /* Get entropy that may have been preloaded by loader(8) + * and use it to pre-charge the entropy harvest queue. + */ + keyfile = preload_search_by_type("/boot/entropy"); + if (keyfile != NULL) { + data = preload_fetch_addr(keyfile); + size = preload_fetch_size(keyfile); + if (data != NULL && size != 0) { + for (j = 0; j < size; j += 16) + random_harvestq_internal(data + j, 16, 16, RANDOM_CACHED); + printf("random: read %zu bytes from preloaded cache\n", size); + bzero(data, size); + } + else + printf("random: no preloaded entropy cache\n"); + } + } void random_harvestq_deinit(void) { - struct harvest *np; - /* Destroy the harvest fifos */ - while (!STAILQ_EMPTY(&emptyfifo.head)) { - np = STAILQ_FIRST(&emptyfifo.head); - STAILQ_REMOVE_HEAD(&emptyfifo.head, next); - free(np, M_ENTROPY); - } - emptyfifo.count = 0; - while (!STAILQ_EMPTY(&harvestfifo.head)) { - np = STAILQ_FIRST(&harvestfifo.head); - STAILQ_REMOVE_HEAD(&harvestfifo.head, next); - free(np, M_ENTROPY); - } - harvestfifo.count = 0; +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif + + /* + * Command the hash/reseed thread to end and wait for it to finish + */ + random_kthread_control = -1; + tsleep(&random_kthread_control, 0, "term", 0); mtx_destroy(&harvest_mtx); + + sysctl_ctx_free(&random_clist); } /* @@ -278,45 +341,42 @@ random_harvestq_deinit(void) * check a few lines below. This includes the "always-on" sources * like the Intel "rdrand" or the VIA Nehamiah "xstore" sources. */ +/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle + * counters are built in, but on older hardware it will do a real time clock + * read which can be quite expensive. + */ void -random_harvestq_internal(u_int64_t somecounter, const void *entropy, - u_int count, u_int bits, enum esource origin) +random_harvestq_internal(const void *entropy, u_int count, u_int bits, + enum random_entropy_source origin) { - struct harvest *event; + struct harvest_event *event; + u_int ring_in; KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("random_harvest_internal: origin %d invalid\n", origin)); - /* Lockless read to avoid lock operations if fifo is full. */ - if (harvestfifo.count >= RANDOM_FIFO_MAX) + /* Mask out unwanted sources */ + if (!(harvest_source_mask & (1U << origin))) return; + /* Lock ring_in against multi-thread contention */ mtx_lock_spin(&harvest_mtx); - - /* - * On't overfill the harvest queue; this could steal all - * our memory. - */ - if (harvestfifo.count < RANDOM_FIFO_MAX) { - event = STAILQ_FIRST(&emptyfifo.head); - if (event != NULL) { - /* Add the harvested data to the fifo */ - STAILQ_REMOVE_HEAD(&emptyfifo.head, next); - emptyfifo.count--; - event->somecounter = somecounter; - event->size = count; - event->bits = bits; - event->source = origin; - - /* XXXX Come back and make this dynamic! */ - count = MIN(count, HARVESTSIZE); - memcpy(event->entropy, entropy, count); - - STAILQ_INSERT_TAIL(&harvestfifo.head, - event, next); - harvestfifo.count++; - } + 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); + event->he_somecounter = get_cyclecount(); + event->he_size = count; + event->he_bits = bits; + event->he_source = origin; + event->he_destination = harvest_destination[origin]++; + memcpy(event->he_entropy, entropy, count); + memset(event->he_entropy + count, 0, HARVESTSIZE - count); + + entropyfifo.ring_in = ring_in; } - mtx_unlock_spin(&harvest_mtx); } diff --git a/sys/dev/random/random_harvestq.h b/sys/dev/random/random_harvestq.h index a2ac3d1..ab09a3f 100644 --- a/sys/dev/random/random_harvestq.h +++ b/sys/dev/random/random_harvestq.h @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2013-2014 Mark R V Murray * Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com> * All rights reserved. * @@ -29,14 +30,41 @@ #ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED #define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED -typedef void (*event_proc_f)(struct harvest *event); +#define HARVESTSIZE 16 /* max size of each harvested entropy unit */ -void random_harvestq_init(event_proc_f); +/* 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 */ + 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 */ + enum random_entropy_source he_source; /* origin of the entropy */ +}; + +void random_harvestq_init(void (*)(struct harvest_event *), int); void random_harvestq_deinit(void); -void random_harvestq_internal(u_int64_t, const void *, - u_int, u_int, enum esource); +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 *); -extern int random_kthread_control; -extern struct mtx harvest_mtx; +/* Round-robin destination cache. */ +extern u_int harvest_destination[ENTROPYSOURCE]; #endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */ diff --git a/sys/dev/random/randomdev.c b/sys/dev/random/randomdev.c index b76cb83..bc41d51 100644 --- a/sys/dev/random/randomdev.c +++ b/sys/dev/random/randomdev.c @@ -26,9 +26,28 @@ * */ +/* + * 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. + * + * Other modules must be initialised after the above two, and are + * software random processors which plug into random_adaptor.c. + * + */ + #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include <sys/param.h> #include <sys/systm.h> #include <sys/bus.h> @@ -38,196 +57,196 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/kthread.h> #include <sys/lock.h> -#include <sys/malloc.h> #include <sys/module.h> -#include <sys/mutex.h> -#include <sys/poll.h> -#include <sys/priv.h> +#include <sys/malloc.h> #include <sys/proc.h> #include <sys/random.h> -#include <sys/selinfo.h> +#include <sys/sysctl.h> +#include <sys/systm.h> #include <sys/uio.h> #include <sys/unistd.h> -#include <machine/bus.h> -#include <machine/cpu.h> - #include <dev/random/randomdev.h> -#include <dev/random/randomdev_soft.h> #include <dev/random/random_adaptors.h> #include <dev/random/random_harvestq.h> -#include <dev/random/live_entropy_sources.h> #define RANDOM_MINOR 0 -static d_read_t random_read; -static d_write_t random_write; -static d_ioctl_t random_ioctl; -static d_poll_t random_poll; +static d_ioctl_t randomdev_ioctl; static struct cdevsw random_cdevsw = { - .d_version = D_VERSION, - .d_read = random_read, - .d_write = random_write, - .d_ioctl = random_ioctl, - .d_poll = random_poll, .d_name = "random", + .d_version = D_VERSION, + .d_read = random_adaptor_read, + .d_write = random_adaptor_write, + .d_poll = random_adaptor_poll, + .d_ioctl = randomdev_ioctl, }; /* For use with make_dev(9)/destroy_dev(9). */ static struct cdev *random_dev; +/* Set up the sysctl root node for the entropy device */ +SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator"); + +MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures"); + /* ARGSUSED */ static int -random_read(struct cdev *dev __unused, struct uio *uio, int flag) +randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, + int flags __unused, struct thread *td __unused) { - int c, error = 0; - void *random_buf; - - /* Blocking logic */ - if (!random_adaptor->seeded) - error = (*random_adaptor->block)(flag); - - /* The actual read */ - if (!error) { - - random_buf = (void *)malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); + int error = 0; - while (uio->uio_resid > 0 && !error) { - c = MIN(uio->uio_resid, PAGE_SIZE); - c = (*random_adaptor->read)(random_buf, c); - error = uiomove(random_buf, c, uio); - } - /* Finished reading; let the source know so it can do some - * optional housekeeping */ - (*random_adaptor->read)(NULL, 0); + switch (cmd) { + /* Really handled in upper layer */ + case FIOASYNC: + case FIONBIO: + break; - free(random_buf, M_ENTROPY); + default: + error = ENOTTY; } return (error); } -/* ARGSUSED */ -static int -random_write(struct cdev *dev __unused, struct uio *uio, int flag __unused) +/* 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) { - /* We used to allow this to insert userland entropy. - * We don't any more because (1) this so-called entropy - * is usually lousy and (b) its vaguely possible to - * mess with entropy harvesting by overdoing a write. - * Now we just ignore input like /dev/null does. - */ - uio->uio_resid = 0; - - return (0); + wakeup(control); + kproc_exit(0); + /* NOTREACHED */ } /* ARGSUSED */ static int -random_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, - int flags __unused, struct thread *td __unused) +randomdev_modevent(module_t mod __unused, int type, void *data __unused) { int error = 0; - switch (cmd) { - /* Really handled in upper layer */ - case FIOASYNC: - case FIONBIO: + switch (type) { + case MOD_LOAD: + printf("random: entropy device infrastructure driver\n"); + random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, + RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0644, "random"); + make_dev_alias(random_dev, "urandom"); /* compatibility */ + random_adaptors_init(); break; + + case MOD_UNLOAD: + random_adaptors_deinit(); + destroy_dev(random_dev); + break; + + case MOD_SHUTDOWN: + break; + default: - error = ENOTTY; + error = EOPNOTSUPP; + break; + } + return (error); } -/* ARGSUSED */ -static int -random_poll(struct cdev *dev __unused, int events, struct thread *td) -{ - int revents = 0; +#define EARLY_2_DEV_MODULE(name, evh, arg) \ +static moduledata_t name##_mod = { \ + #name, \ + evh, \ + arg \ +}; \ +DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND) - if (events & (POLLIN | POLLRDNORM)) { - if (random_adaptor->seeded) - revents = events & (POLLIN | POLLRDNORM); - else - revents = (*random_adaptor->poll)(events, td); - } - return (revents); -} +EARLY_2_DEV_MODULE(randomdev, randomdev_modevent, NULL); +MODULE_VERSION(randomdev, 1); + +/* ================ + * Harvesting stubs + * ================ + */ +/* Internal stub/fake routine for when no entropy processor is loaded. + * If the entropy device is not loaded, don't act on harvesting calls + * and just return. + */ +/* ARGSUSED */ static void -random_initialize(void *p, struct random_adaptor *s) +random_harvest_phony(const void *entropy __unused, u_int count __unused, + u_int bits __unused, enum random_entropy_source origin __unused) { - static int random_inited = 0; - - if (random_inited) { - printf("random: <%s> already initialized\n", - random_adaptor->ident); - return; - } +} - random_adaptor = s; +/* Hold the address of the routine which is actually called */ +static void (*reap_func)(const void *, u_int, u_int, enum random_entropy_source) = random_harvest_phony; - (s->init)(); +/* Initialise the harvester when/if it is loaded */ +void +randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source)) +{ - printf("random: <%s> initialized\n", s->ident); + reap_func = reaper; +} - /* Use an appropriately evil mode for those who are concerned - * with daemons */ - random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw, - RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random"); - make_dev_alias(random_dev, "urandom"); /* compatibility */ +/* Deinitialise the harvester when/if it is unloaded */ +void +randomdev_deinit_harvester(void) +{ - /* mark random(4) as initialized, to avoid being called again */ - random_inited = 1; + reap_func = random_harvest_phony; } -/* ARGSUSED */ -static int -random_modevent(module_t mod __unused, int type, void *data __unused) +/* Entropy harvesting routine. + * Implemented as in indirect call to allow non-inclusion of + * the entropy device. + */ +void +random_harvest(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin) { - static eventhandler_tag attach_tag = NULL; - int error = 0; - - switch (type) { - case MOD_LOAD: - random_adaptor_choose(&random_adaptor); - if (random_adaptor == NULL) { - printf("random: No random adaptor attached, " - "postponing initialization\n"); - attach_tag = EVENTHANDLER_REGISTER(random_adaptor_attach, - random_initialize, NULL, EVENTHANDLER_PRI_ANY); - } else - random_initialize(NULL, random_adaptor); + (*reap_func)(entropy, count, bits, origin); +} - break; +/* ================================ + * Internal reading stubs and fakes + * ================================ + */ - case MOD_UNLOAD: - if (random_adaptor != NULL) { - (*random_adaptor->deinit)(); - destroy_dev(random_dev); - } - /* Unregister the event handler */ - if (attach_tag != NULL) - EVENTHANDLER_DEREGISTER(random_adaptor_attach, - attach_tag); +/* Hold the address of the routine which is actually called */ +static u_int (*read_func)(uint8_t *, u_int) = dummy_random_read_phony; - break; +/* Initialise the reader when/if it is loaded */ +void +randomdev_init_reader(u_int (*reader)(uint8_t *, u_int)) +{ - case MOD_SHUTDOWN: - break; + read_func = reader; +} - default: - error = EOPNOTSUPP; - break; +/* Deinitialise the reader when/if it is unloaded */ +void +randomdev_deinit_reader(void) +{ - } - return (error); + read_func = dummy_random_read_phony; } -DEV_MODULE(random, random_modevent, NULL); -MODULE_VERSION(random, 1); +/* Kernel API version of read_random(). + * Implemented as in indirect call to allow non-inclusion of + * the entropy device. + */ +int +read_random(void *buf, int count) +{ + + return ((int)(*read_func)(buf, (u_int)count)); +} diff --git a/sys/dev/random/randomdev.h b/sys/dev/random/randomdev.h index b87789f..4daf735 100644 --- a/sys/dev/random/randomdev.h +++ b/sys/dev/random/randomdev.h @@ -35,28 +35,33 @@ typedef void random_init_func_t(void); typedef void random_deinit_func_t(void); -typedef int random_block_func_t(int); -typedef int random_read_func_t(void *, int); -typedef int random_poll_func_t(int, struct thread *); -typedef void random_reseed_func_t(void); - -struct random_adaptor { - struct selinfo rsel; - const char *ident; - int seeded; - unsigned priority; - random_init_func_t *init; - random_deinit_func_t *deinit; - random_block_func_t *block; - random_read_func_t *read; - random_poll_func_t *poll; - random_reseed_func_t *reseed; -}; - -struct random_hardware_source { - const char *ident; - enum esource source; - random_read_func_t *read; -}; + +void randomdev_init_harvester(void (*)(const void *, u_int, u_int, enum random_entropy_source)); +void randomdev_init_reader(u_int (*)(uint8_t *, u_int)); +void randomdev_deinit_harvester(void); +void randomdev_deinit_reader(void); + +/* Stub/fake routines for when no entropy processor is loaded */ +extern u_int dummy_random_read_phony(uint8_t *, u_int); + +/* kern.random sysctls */ +#ifdef SYSCTL_DECL /* from sysctl.h */ +SYSCTL_DECL(_kern_random); + +/* If this was C++, the macro below would be a template */ +#define RANDOM_CHECK_UINT(name, min, max) \ +static int \ +random_check_uint_##name(SYSCTL_HANDLER_ARGS) \ +{ \ + if (oidp->oid_arg1 != NULL) { \ + if (*(u_int *)(oidp->oid_arg1) <= (min)) \ + *(u_int *)(oidp->oid_arg1) = (min); \ + else if (*(u_int *)(oidp->oid_arg1) > (max)) \ + *(u_int *)(oidp->oid_arg1) = (max); \ + } \ + return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \ + req)); \ +} +#endif /* SYSCTL_DECL */ #endif diff --git a/sys/dev/random/randomdev_soft.c b/sys/dev/random/randomdev_soft.c index 0929704..61cdf35 100644 --- a/sys/dev/random/randomdev_soft.c +++ b/sys/dev/random/randomdev_soft.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2014 Mark R V Murray * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * @@ -26,6 +26,16 @@ * */ +/* + * 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) @@ -33,15 +43,13 @@ #elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) #error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" #endif -#if defined(RANDOM_FORTUNA) -#error "Fortuna is not yet implemented" -#endif #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/conf.h> #include <sys/fcntl.h> #include <sys/kernel.h> #include <sys/lock.h> @@ -50,14 +58,9 @@ __FBSDID("$FreeBSD$"); #include <sys/mutex.h> #include <sys/poll.h> #include <sys/random.h> -#include <sys/selinfo.h> #include <sys/sysctl.h> -#include <sys/uio.h> #include <sys/unistd.h> -#include <machine/bus.h> -#include <machine/cpu.h> - #include <dev/random/randomdev.h> #include <dev/random/randomdev_soft.h> #include <dev/random/random_harvestq.h> @@ -69,111 +72,44 @@ __FBSDID("$FreeBSD$"); #include <dev/random/fortuna.h> #endif - -static int randomdev_poll(int event, struct thread *td); -static int randomdev_block(int flag); -static void randomdev_flush_reseed(void); - +static struct random_adaptor random_soft_processor = { #if defined(RANDOM_YARROW) -static struct random_adaptor random_context = { - .ident = "Software, Yarrow", - .init = randomdev_init, - .deinit = randomdev_deinit, - .block = randomdev_block, - .read = random_yarrow_read, - .poll = randomdev_poll, - .reseed = randomdev_flush_reseed, - .seeded = 0, /* This will be seeded during entropy processing */ - .priority = 90, /* High priority, so top of the list. Fortuna may still win. */ -}; -#define RANDOM_MODULE_NAME 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) -static struct random_adaptor random_context = { - .ident = "Software, Fortuna", - .init = randomdev_init, - .deinit = randomdev_deinit, - .block = randomdev_block, - .read = random_fortuna_read, - .poll = randomdev_poll, - .reseed = randomdev_flush_reseed, - .seeded = 0, /* This will be excplicitly seeded at startup when secured */ - .priority = 100, /* High priority, so top of the list. Beat Yarrow. */ -}; -#define RANDOM_MODULE_NAME 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 - -TUNABLE_INT("kern.random.sys.seeded", &random_context.seeded); - -/* List for the dynamic sysctls */ -static struct sysctl_ctx_list random_clist; - -/* ARGSUSED */ -static int -random_check_boolean(SYSCTL_HANDLER_ARGS) -{ - if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0) - *(u_int *)(oidp->oid_arg1) = 1; - return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req)); -} + .ra_init = randomdev_init, + .ra_deinit = randomdev_deinit, +}; void randomdev_init(void) { - struct sysctl_oid *random_sys_o, *random_sys_harvest_o; #if defined(RANDOM_YARROW) - random_yarrow_init_alg(&random_clist); + random_yarrow_init_alg(); + random_harvestq_init(random_yarrow_process_event, 2); #endif #if defined(RANDOM_FORTUNA) - random_fortuna_init_alg(&random_clist); + random_fortuna_init_alg(); + random_harvestq_init(random_fortuna_process_event, 32); #endif - random_sys_o = SYSCTL_ADD_NODE(&random_clist, - SYSCTL_STATIC_CHILDREN(_kern_random), - OID_AUTO, "sys", CTLFLAG_RW, 0, - "Entropy Device Parameters"); - - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_o), - OID_AUTO, "seeded", CTLTYPE_INT | CTLFLAG_RW, - &random_context.seeded, 0, random_check_boolean, "I", - "Seeded State"); - - random_sys_harvest_o = SYSCTL_ADD_NODE(&random_clist, - SYSCTL_CHILDREN(random_sys_o), - OID_AUTO, "harvest", CTLFLAG_RW, 0, - "Entropy Sources"); - - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_harvest_o), - OID_AUTO, "ethernet", CTLTYPE_INT | CTLFLAG_RW, - &harvest.ethernet, 1, random_check_boolean, "I", - "Harvest NIC entropy"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_harvest_o), - OID_AUTO, "point_to_point", CTLTYPE_INT | CTLFLAG_RW, - &harvest.point_to_point, 1, random_check_boolean, "I", - "Harvest serial net entropy"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_harvest_o), - OID_AUTO, "interrupt", CTLTYPE_INT | CTLFLAG_RW, - &harvest.interrupt, 1, random_check_boolean, "I", - "Harvest IRQ entropy"); - SYSCTL_ADD_PROC(&random_clist, - SYSCTL_CHILDREN(random_sys_harvest_o), - OID_AUTO, "swi", CTLTYPE_INT | CTLFLAG_RW, - &harvest.swi, 1, random_check_boolean, "I", - "Harvest SWI entropy"); - - random_harvestq_init(random_process_event); - /* Register the randomness harvesting routine */ - randomdev_init_harvester(random_harvestq_internal, - random_context.read); + randomdev_init_harvester(random_harvestq_internal); } void @@ -182,118 +118,56 @@ randomdev_deinit(void) /* Deregister the randomness harvesting routine */ randomdev_deinit_harvester(); - /* - * Command the hash/reseed thread to end and wait for it to finish - */ - random_kthread_control = -1; - tsleep((void *)&random_kthread_control, 0, "term", 0); - #if defined(RANDOM_YARROW) random_yarrow_deinit_alg(); #endif #if defined(RANDOM_FORTUNA) random_fortuna_deinit_alg(); #endif - - sysctl_ctx_free(&random_clist); -} - -void -randomdev_unblock(void) -{ - if (!random_context.seeded) { - selwakeuppri(&random_context.rsel, PUSER); - wakeup(&random_context); - printf("random: unblocking device.\n"); - random_context.seeded = 1; - } - /* Do arc4random(9) a favour while we are about it. */ - (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, - ARC4_ENTR_HAVE); } +/* ARGSUSED */ static int -randomdev_poll(int events, struct thread *td) +randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused) { - int revents = 0; - - mtx_lock(&random_reseed_mtx); + int error = 0; - if (random_context.seeded) - revents = events & (POLLIN | POLLRDNORM); - else - selrecord(td, &random_context.rsel); + switch (type) { + case MOD_LOAD: + printf("random: SOFT: %s init()\n", RANDOM_CSPRNG_NAME); + random_adaptor_register(RANDOM_CSPRNG_NAME, &random_soft_processor); + break; - mtx_unlock(&random_reseed_mtx); - return (revents); -} + case MOD_UNLOAD: + random_adaptor_deregister(RANDOM_CSPRNG_NAME); + break; -static int -randomdev_block(int flag) -{ - int error = 0; + case MOD_SHUTDOWN: + break; - mtx_lock(&random_reseed_mtx); + default: + error = EOPNOTSUPP; + break; - /* Blocking logic */ - while (!random_context.seeded && !error) { - if (flag & O_NONBLOCK) - error = EWOULDBLOCK; - else { - printf("random: blocking on read.\n"); - error = msleep(&random_context, - &random_reseed_mtx, - PUSER | PCATCH, "block", 0); - } } - mtx_unlock(&random_reseed_mtx); - return (error); } -/* Helper routine to perform explicit reseeds */ -static void -randomdev_flush_reseed(void) -{ - /* Command a entropy queue flush and wait for it to finish */ - random_kthread_control = 1; - while (random_kthread_control) - pause("-", hz / 10); +#define MID_DEV_MODULE(name, evh, arg) \ +static moduledata_t name##_mod = { \ + #name, \ + evh, \ + arg \ +}; \ +DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE) #if defined(RANDOM_YARROW) - /* This ultimately calls randomdev_unblock() */ - random_yarrow_reseed(); +MID_DEV_MODULE(yarrow, randomdev_soft_modevent, NULL); +MODULE_VERSION(yarrow, 1); +MODULE_DEPEND(yarrow, random_adaptors, 1, 1, 1); #endif #if defined(RANDOM_FORTUNA) - /* This ultimately calls randomdev_unblock() */ - random_fortuna_reseed(); +MID_DEV_MODULE(fortuna, randomdev_soft_modevent, NULL); +MODULE_VERSION(fortuna, 1); +MODULE_DEPEND(fortuna, random_adaptors, 1, 1, 1); #endif -} - -static int -randomdev_modevent(module_t mod __unused, int type, void *unused __unused) -{ - - switch (type) { - case MOD_LOAD: - random_adaptor_register(RANDOM_CSPRNG_NAME, &random_context); - /* - * For statically built kernels that contain both device - * random and options PADLOCK_RNG/RDRAND_RNG/etc.., - * this event handler will do nothing, since the random - * driver-specific handlers are loaded after these HW - * consumers, and hence hasn't yet registered for this event. - * - * In case where both the random driver and RNG's are built - * as seperate modules, random.ko is loaded prior to *_rng.ko's - * (by dependency). This event handler is there to delay - * creation of /dev/{u,}random and attachment of this *_rng.ko. - */ - EVENTHANDLER_INVOKE(random_adaptor_attach, &random_context); - return (0); - } - - return (EINVAL); -} - -RANDOM_ADAPTOR_MODULE(RANDOM_MODULE_NAME, randomdev_modevent, 1); diff --git a/sys/dev/random/randomdev_soft.h b/sys/dev/random/randomdev_soft.h index cbee779..e814de6 100644 --- a/sys/dev/random/randomdev_soft.h +++ b/sys/dev/random/randomdev_soft.h @@ -29,58 +29,11 @@ #ifndef SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED #define SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED -/* This header contains only those definitions that are global - * and harvester-specific for the entropy processor +/* This header contains only those definitions that are + * specific to the entropy processor */ -/* #define ENTROPYSOURCE nn entropy sources (actually classes) - * This is properly defined in - * an enum in sys/random.h - */ - -/* The ring size _MUST_ be a power of 2 */ -#define HARVEST_RING_SIZE 1024 /* harvest ring buffer size */ -#define HARVEST_RING_MASK (HARVEST_RING_SIZE - 1) - -#define HARVESTSIZE 16 /* max size of each harvested entropy unit */ - -/* These are used to queue harvested packets of entropy. The entropy - * buffer size is pretty arbitrary. - */ -struct harvest { - uintmax_t somecounter; /* fast counter for clock jitter */ - uint8_t entropy[HARVESTSIZE]; /* the harvested entropy */ - u_int size, bits; /* stats about the entropy */ - enum esource source; /* origin of the entropy */ - STAILQ_ENTRY(harvest) next; /* next item on the list */ -}; - void randomdev_init(void); void randomdev_deinit(void); -void randomdev_init_harvester(void (*)(u_int64_t, const void *, u_int, - u_int, enum esource), int (*)(void *, int)); -void randomdev_deinit_harvester(void); - -void random_set_wakeup_exit(void *); -void random_process_event(struct harvest *event); -void randomdev_unblock(void); - -extern struct mtx random_reseed_mtx; - -/* If this was C++, the macro below would be a template */ -#define RANDOM_CHECK_UINT(name, min, max) \ -static int \ -random_check_uint_##name(SYSCTL_HANDLER_ARGS) \ -{ \ - if (oidp->oid_arg1 != NULL) { \ - if (*(u_int *)(oidp->oid_arg1) <= (min)) \ - *(u_int *)(oidp->oid_arg1) = (min); \ - else if (*(u_int *)(oidp->oid_arg1) > (max)) \ - *(u_int *)(oidp->oid_arg1) = (max); \ - } \ - return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \ - req)); \ -} - #endif diff --git a/sys/dev/random/rwfile.c b/sys/dev/random/rwfile.c deleted file mode 100644 index 9b38957..0000000 --- a/sys/dev/random/rwfile.c +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * Copyright (c) 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 <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "opt_random.h" - -#ifdef RANDOM_RWFILE - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/proc.h> -#include <sys/namei.h> -#include <sys/fcntl.h> -#include <sys/vnode.h> - -#include <dev/random/rwfile.h> - -int -randomdev_read_file(const char *filename, void *buf, size_t length) -{ - struct nameidata nd; - struct thread* td = curthread; - int error; - ssize_t resid; - int flags; - - NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); - flags = FREAD; - error = vn_open(&nd, &flags, 0, NULL); - if (error == 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_vp->v_type != VREG) - error = ENOEXEC; - else - error = vn_rdwr(UIO_READ, nd.ni_vp, buf, length, 0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); - VOP_UNLOCK(nd.ni_vp, 0); - vn_close(nd.ni_vp, FREAD, td->td_ucred, td); - } - - return (error); -} - -int -randomdev_write_file(const char *filename, void *buf, size_t length) -{ - struct nameidata nd; - struct thread* td = curthread; - int error; - ssize_t resid; - int flags; - - NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td); - flags = FWRITE | O_CREAT | O_TRUNC; - error = vn_open(&nd, &flags, 0, NULL); - if (error == 0) { - NDFREE(&nd, NDF_ONLY_PNBUF); - if (nd.ni_vp->v_type != VREG) - error = ENOEXEC; - else - error = vn_rdwr(UIO_WRITE, nd.ni_vp, buf, length, 0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td); - - VOP_UNLOCK(nd.ni_vp, 0); - vn_close(nd.ni_vp, FREAD, td->td_ucred, td); - } - - return (error); -} - -#endif diff --git a/sys/dev/random/uint128.h b/sys/dev/random/uint128.h new file mode 100644 index 0000000..b2cc1e0 --- /dev/null +++ b/sys/dev/random/uint128.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 2014 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_UINT128_H_INCLUDED +#define SYS_DEV_RANDOM_UINT128_H_INCLUDED + +/* This whole thing is a crock :-( + * + * Everyone knows you always need the __uint128_t types! + */ + +#ifdef __SIZEOF_INT128__ +typedef __uint128_t uint128_t; +#else +typedef uint64_t uint128_t[2]; +#endif + +static __inline void +uint128_clear(uint128_t *big_uint) +{ +#ifdef __SIZEOF_INT128__ + (*big_uint) = 0ULL; +#else + (*big_uint)[0] = (*big_uint)[1] = 0UL; +#endif +} + +static __inline void +uint128_increment(uint128_t *big_uint) +{ +#ifdef __SIZEOF_INT128__ + (*big_uint)++; +#else + (*big_uint)[0]++; + if ((*big_uint)[0] == 0UL) + (*big_uint)[1]++; +#endif +} + +static __inline int +uint128_is_zero(uint128_t big_uint) +{ +#ifdef __SIZEOF_INT128__ + return (big_uint == 0ULL); +#else + return (big_uint[0] == 0UL && big_uint[1] == 0UL); +#endif +} + +#endif /* SYS_DEV_RANDOM_UINT128_H_INCLUDED */ diff --git a/sys/dev/random/unit_test.c b/sys/dev/random/unit_test.c new file mode 100644 index 0000000..32b3363 --- /dev/null +++ b/sys/dev/random/unit_test.c @@ -0,0 +1,257 @@ +/*- + * 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$ + */ + +/* + Build this by going: + +cc -g -O0 -pthread -DRANDOM_<alg> -DRANDOM_DEBUG -I../.. -lstdthreads -Wall \ + unit_test.c \ + yarrow.c \ + fortuna.c \ + hash.c \ + ../../crypto/rijndael/rijndael-api-fst.c \ + ../../crypto/rijndael/rijndael-alg-fst.c \ + ../../crypto/sha2/sha2.c \ + -o unit_test +./unit_test + +Where <alg> is YARROW or FORTUNA. +*/ + +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <threads.h> +#include <unistd.h> + +#include "unit_test.h" + +#ifdef RANDOM_YARROW +#include "dev/random/yarrow.h" +#endif +#ifdef RANDOM_FORTUNA +#include "dev/random/fortuna.h" +#endif + +#define NUM_THREADS 3 + +static volatile int stopseeding = 0; + +void +random_adaptor_unblock(void) +{ + +#if 0 + if (mtx_trylock(&random_reseed_mtx) == thrd_busy) + printf("Mutex held. Good.\n"); + else { + printf("Mutex not held. PANIC!!\n"); + thrd_exit(0); + } +#endif + printf("random: unblocking device.\n"); +} + +static int +RunHarvester(void *arg __unused) +{ + int i, r; + struct harvest_event e; + + for (i = 0; ; i++) { + if (stopseeding) + break; + if (i % 1000 == 0) + printf("Harvest: %d\n", i); + r = random()%10; + e.he_somecounter = i; + *((uint64_t *)e.he_entropy) = random(); + e.he_size = 8; + e.he_bits = random()%4; + e.he_destination = i; + e.he_source = (i + 3)%7; + e.he_next = NULL; +#ifdef RANDOM_YARROW + random_yarrow_process_event(&e); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_process_event(&e); +#endif + usleep(r); + } + + printf("Thread #0 ends\n"); + + thrd_exit(0); + + return (0); +} + +static int +WriteCSPRNG(void *threadid) +{ + uint8_t *buf; + int i; + + printf("Thread #1 starts\n"); + + for (i = 0; ; i++) { + if (stopseeding) + break; + buf = malloc(4096); + if (i % 1000 == 0) + printf("Thread write 1 - %d\n", i); + if (buf != NULL) { +#ifdef RANDOM_YARROW + random_yarrow_write(buf, i); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_write(buf, i); +#endif + free(buf); + } + usleep(1000000); + } + + printf("Thread #1 ends\n"); + + thrd_exit(0); + + return (0); +} + +static int +ReadCSPRNG(void *threadid) +{ + size_t tid; + uint8_t *buf; + int i; + + tid = (size_t)threadid; + printf("Thread #%zd starts\n", tid); + +#ifdef RANDOM_YARROW + while (!random_yarrow_seeded()) +#endif +#ifdef RANDOM_FORTUNA + while (!random_fortuna_seeded()) +#endif + { +#ifdef RANDOM_YARROW + random_yarrow_read(NULL, 0); + random_yarrow_read(NULL, 1); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_read(NULL, 0); + random_fortuna_read(NULL, 1); +#endif + usleep(100); + } + + for (i = 0; i < 100000; i++) { + buf = malloc(i); + if (i % 1000 == 0) + printf("Thread read %zd - %d %p\n", tid, i, buf); + if (buf != NULL) { +#ifdef RANDOM_YARROW + random_yarrow_read(NULL, 0); + random_yarrow_read(buf, i); + random_yarrow_read(NULL, 1); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_read(NULL, 0); + random_fortuna_read(buf, i); + random_fortuna_read(NULL, 1); +#endif +#if 0 + { + int j; + + for (j = 0; j < i; j++) { + printf(" %02X", buf[j]); + if (j % 32 == 31 || j == i - 1) + printf("\n"); + } + } +#endif + free(buf); + } + usleep(100); + } + + printf("Thread #%zd ends\n", tid); + + thrd_exit(0); + + return (0); +} + +int +main(int argc, char *argv[]) +{ + thrd_t threads[NUM_THREADS]; + int rc; + long t; + +#ifdef RANDOM_YARROW + random_yarrow_init_alg(); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_init_alg(); +#endif + + 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); + if (rc != thrd_success) { + printf("ERROR; return code from thrd_create() is %d\n", rc); + exit(-1); + } + } + + for (t = 2; t < NUM_THREADS; t++) + thrd_join(threads[t], &rc); + + stopseeding = 1; + + thrd_join(threads[1], &rc); + thrd_join(threads[0], &rc); + +#ifdef RANDOM_YARROW + random_yarrow_deinit_alg(); +#endif +#ifdef RANDOM_FORTUNA + random_fortuna_deinit_alg(); +#endif + + /* Last thing that main() should do */ + thrd_exit(0); + + return (0); +} diff --git a/sys/dev/random/unit_test.h b/sys/dev/random/unit_test.h new file mode 100644 index 0000000..9fc0931 --- /dev/null +++ b/sys/dev/random/unit_test.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 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 UNIT_TEST_H_INCLUDED +#define UNIT_TEST_H_INCLUDED + +void random_adaptor_unblock(void); + +static __inline uint64_t +get_cyclecount(void) +{ + + /* Shaddup! */ + return (4ULL); +} + +// #define PAGE_SIZE 4096 +#define HARVESTSIZE 16 + +enum random_entropy_source { + RANDOM_START = 0, + RANDOM_CACHED = 0, + ENTROPYSOURCE = 32 +}; + +struct harvest_event { + uintmax_t he_somecounter; /* fast counter for clock jitter */ + uint8_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 */ + enum random_entropy_source he_source; /* origin of the entropy */ + void * he_next; /* next item on the list */ +}; + +struct sysctl_ctx_list; + +#define CTASSERT(x) _Static_assert(x, "compile-time assertion failed") +#define KASSERT(exp,msg) do { \ + if (!(exp)) { \ + printf msg; \ + exit(0); \ + } \ +} while (0) + +#endif /* UNIT_TEST_H_INCLUDED */ diff --git a/sys/dev/random/yarrow.c b/sys/dev/random/yarrow.c index 1cfa373..a8da20a 100644 --- a/sys/dev/random/yarrow.c +++ b/sys/dev/random/yarrow.c @@ -28,6 +28,7 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#ifdef _KERNEL #include "opt_random.h" #include <sys/param.h> @@ -39,197 +40,280 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/systm.h> +#include <machine/cpu.h> + #include <crypto/rijndael/rijndael-api-fst.h> #include <crypto/sha2/sha2.h> #include <dev/random/hash.h> +#include <dev/random/randomdev.h> #include <dev/random/random_adaptors.h> -#include <dev/random/randomdev_soft.h> +#include <dev/random/random_harvestq.h> +#include <dev/random/uint128.h> +#include <dev/random/yarrow.h> +#else /* !_KERNEL */ +#include <sys/param.h> +#include <sys/types.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <threads.h> + +#include "unit_test.h" + +#include <crypto/rijndael/rijndael-api-fst.h> +#include <crypto/sha2/sha2.h> + +#include <dev/random/hash.h> +#include <dev/random/randomdev.h> +#include <dev/random/uint128.h> #include <dev/random/yarrow.h> +#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 random_state { +static struct yarrow_state { union { uint8_t byte[BLOCKSIZE]; - uint64_t qword[BLOCKSIZE/sizeof(uint64_t)]; - } 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 */ + 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]; - u_int thresh; /* pool reseed threshhold */ + } 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 */ - u_int which; /* toggle - sets the current insertion pool */ -} random_state; + } 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); -/* The reseed thread mutex */ -struct mtx random_reseed_mtx; - -/* 128-bit C = 0 */ -/* Nothing to see here, folks, just an ugly mess. */ -static void -clear_counter(void) -{ - random_state.counter.qword[0] = 0UL; - random_state.counter.qword[1] = 0UL; -} - -/* 128-bit C = C + 1 */ -/* Nothing to see here, folks, just an ugly mess. */ -/* TODO: Make a Galois counter instead? */ -static void -increment_counter(void) -{ - random_state.counter.qword[0]++; - if (!random_state.counter.qword[0]) - random_state.counter.qword[1]++; -} - -/* Process a single stochastic event off the harvest queue */ void -random_process_event(struct harvest *event) +random_yarrow_init_alg(void) { - u_int pl, overthreshhold[2]; - struct source *source; - enum esource src; - -#if 0 - /* Do this better with DTrace */ - { - int i; - - printf("Harvest:%16jX ", event->somecounter); - for (i = 0; i < event->size; i++) - printf("%02X", event->entropy[i]); - for (; i < 16; i++) - printf(" "); - printf(" %2d %2d %02X\n", event->size, event->bits, event->source); - } -#endif - - /* Accumulate the event into the appropriate pool */ - pl = random_state.which; - source = &random_state.pool[pl].source[event->source]; - randomdev_hash_iterate(&random_state.pool[pl].hash, event, - sizeof(*event)); - source->bits += event->bits; - - /* 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 (random_state.pool[pl].source[src].bits - > random_state.pool[pl].thresh) - overthreshhold[pl]++; - } - } - - /* if any fast source over threshhold, reseed */ - if (overthreshhold[FAST]) - reseed(FAST); + int i, j; +#ifdef _KERNEL + struct sysctl_oid *random_yarrow_o; +#endif /* _KERNEL */ - /* if enough slow sources are over threshhold, reseed */ - if (overthreshhold[SLOW] >= random_state.slowoverthresh) - reseed(SLOW); + memset(yarrow_state.start_cache.junk, 0, KEYSIZE); + randomdev_hash_init(&yarrow_state.start_cache.hash); - /* Invert the fast/slow pool selector bit */ - random_state.which = !random_state.which; -} + /* 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 */ -void -random_yarrow_init_alg(struct sysctl_ctx_list *clist) -{ - int i; - struct sysctl_oid *random_yarrow_o; + /* 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(clist, + random_yarrow_o = SYSCTL_ADD_NODE(&random_clist, SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "yarrow", CTLFLAG_RW, 0, "Yarrow Parameters"); - SYSCTL_ADD_PROC(clist, + SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "gengateinterval", CTLTYPE_INT|CTLFLAG_RW, - &random_state.gengateinterval, 10, + &yarrow_state.gengateinterval, 10, random_check_uint_gengateinterval, "I", "Generation gate interval"); - SYSCTL_ADD_PROC(clist, + SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "bins", CTLTYPE_INT|CTLFLAG_RW, - &random_state.bins, 10, + &yarrow_state.bins, 10, random_check_uint_bins, "I", "Execution time tuner"); - SYSCTL_ADD_PROC(clist, + SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "fastthresh", CTLTYPE_INT|CTLFLAG_RW, - &random_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4, + &yarrow_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4, random_check_uint_fastthresh, "I", "Fast reseed threshold"); - SYSCTL_ADD_PROC(clist, + SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "slowthresh", CTLTYPE_INT|CTLFLAG_RW, - &random_state.pool[1].thresh, (BLOCKSIZE*8), + &yarrow_state.pool[1].thresh, (BLOCKSIZE*8), random_check_uint_slowthresh, "I", "Slow reseed threshold"); - SYSCTL_ADD_PROC(clist, + SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, "slowoverthresh", CTLTYPE_INT|CTLFLAG_RW, - &random_state.slowoverthresh, 2, + &yarrow_state.slowoverthresh, 2, random_check_uint_slowoverthresh, "I", "Slow over-threshold reseed"); +#endif /* _KERNEL */ - random_state.gengateinterval = 10; - random_state.bins = 10; - random_state.pool[0].thresh = (3*(BLOCKSIZE*8))/4; - random_state.pool[1].thresh = (BLOCKSIZE*8); - random_state.slowoverthresh = 2; - random_state.which = FAST; + 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 = 0; i < 2; i++) - randomdev_hash_init(&random_state.pool[i].hash); + 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 */ - clear_counter(); - - /* Set up a lock for the reseed process */ - mtx_init(&random_reseed_mtx, "Yarrow reseed", NULL, MTX_DEF); + 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 @@ -239,45 +323,60 @@ reseed(u_int fastslow) * 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; - uint8_t hash[KEYSIZE]; /* h' */ - uint8_t temp[KEYSIZE]; u_int i; - enum esource j; + enum random_entropy_source j; -#if 0 - printf("Yarrow: %s reseed\n", fastslow == FAST ? "fast" : "slow"); -#endif + KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0")); + KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0")); - /* The reseed task must not be jumped on */ - mtx_lock(&random_reseed_mtx); +#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_iterate(&context, - &random_state.pool[SLOW].hash, - sizeof(struct randomdev_hash)); - randomdev_hash_iterate(&context, - &random_state.pool[FAST].hash, sizeof(struct randomdev_hash)); + 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 (random_state.bins > TIMEBIN) - random_state.bins = TIMEBIN; - for (i = 1; i < random_state.bins; i++) { + 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(u_int)); + randomdev_hash_iterate(&context, &i, sizeof(i)); /* Return the hashval */ randomdev_hash_finish(&context, v[i]); } @@ -287,98 +386,107 @@ reseed(u_int fastslow) */ randomdev_hash_init(&context); - randomdev_hash_iterate(&context, &random_state.key, KEYSIZE); - for (i = 1; i < random_state.bins; i++) - randomdev_hash_iterate(&context, &v[i], KEYSIZE); + 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(&random_state.key, temp); + randomdev_encrypt_init(&yarrow_state.key, temp); /* 4. Recompute the counter */ - clear_counter(); - randomdev_encrypt(&random_state.key, random_state.counter.byte, temp, BLOCKSIZE); - memcpy(random_state.counter.byte, temp, BLOCKSIZE); + 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++) - random_state.pool[i].source[j].bits = 0; + yarrow_state.pool[i].source[j].bits = 0; /* 6. Wipe memory of intermediate values */ - memset((void *)v, 0, sizeof(v)); - memset((void *)temp, 0, sizeof(temp)); - memset((void *)hash, 0, sizeof(hash)); + 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 */ - /* XXX Not done here yet */ - /* Unblock the device if it was blocked due to being unseeded */ - randomdev_unblock(); + /* This pseudo-code is documentation. Please leave it alone. */ + seed_file = "<some file>"; + error = randomdev_write_file(seed_file, <generated entropy>, PAGE_SIZE); + if (error == 0) + printf("random: entropy seed file '%s' successfully written\n", seed_file); +#endif - /* Release the reseed mutex */ - mtx_unlock(&random_reseed_mtx); + /* 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 */ -int -random_yarrow_read(void *buf, int count) +void +random_yarrow_read(uint8_t *buf, u_int bytecount) { - static int cur = 0; - static int gate = 1; - static uint8_t genval[KEYSIZE]; - size_t tomove; - int i; - int retval; + u_int blockcount, i; - /* Check for final read request */ - if (buf == NULL && count == 0) - return (0); + /* Check for initial/final read requests */ + if (buf == NULL) + return; /* The reseed task must not be jumped on */ mtx_lock(&random_reseed_mtx); - if (gate) { - generator_gate(); - random_state.outputblocks = 0; - gate = 0; - } - if (count > 0 && (size_t)count >= BLOCKSIZE) { - retval = 0; - for (i = 0; i < count; i += BLOCKSIZE) { - increment_counter(); - randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE); - tomove = MIN(count - i, BLOCKSIZE); - memcpy((char *)buf + i, genval, tomove); - if (++random_state.outputblocks >= random_state.gengateinterval) { - generator_gate(); - random_state.outputblocks = 0; - } - retval += (int)tomove; - cur = 0; + 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; } - else { - if (!cur) { - increment_counter(); - randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE); - memcpy(buf, genval, (size_t)count); - cur = BLOCKSIZE - count; - if (++random_state.outputblocks >= random_state.gengateinterval) { - generator_gate(); - random_state.outputblocks = 0; - } - retval = count; - } - else { - retval = MIN(cur, count); - memcpy(buf, &genval[BLOCKSIZE - cur], (size_t)retval); - cur -= retval; - } + + 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); - return (retval); } static void @@ -388,29 +496,26 @@ generator_gate(void) uint8_t temp[KEYSIZE]; for (i = 0; i < KEYSIZE; i += BLOCKSIZE) { - increment_counter(); - randomdev_encrypt(&random_state.key, random_state.counter.byte, temp + i, BLOCKSIZE); + uint128_increment(&yarrow_state.counter.whole); + randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE); } - randomdev_encrypt_init(&random_state.key, temp); - memset((void *)temp, 0, KEYSIZE); + randomdev_encrypt_init(&yarrow_state.key, temp); + memset(temp, 0, KEYSIZE); } -/* Helper routine to perform explicit reseeds */ void random_yarrow_reseed(void) { -#ifdef RANDOM_DEBUG - int i; - - printf("%s(): fast:", __func__); - for (i = RANDOM_START; i < ENTROPYSOURCE; ++i) - printf(" %d", random_state.pool[FAST].source[i].bits); - printf("\n"); - printf("%s(): slow:", __func__); - for (i = RANDOM_START; i < ENTROPYSOURCE; ++i) - printf(" %d", random_state.pool[SLOW].source[i].bits); - printf("\n"); -#endif + reseed(SLOW); } + +int +random_yarrow_seeded(void) +{ + + return (yarrow_state.seeded); +} + +#endif /* RANDOM_YARROW */ diff --git a/sys/dev/random/yarrow.h b/sys/dev/random/yarrow.h index f32313e..9ba0b4e 100644 --- a/sys/dev/random/yarrow.h +++ b/sys/dev/random/yarrow.h @@ -29,9 +29,16 @@ #ifndef SYS_DEV_RANDOM_YARROW_H_INCLUDED #define SYS_DEV_RANDOM_YARROW_H_INCLUDED -void random_yarrow_init_alg(struct sysctl_ctx_list *); +#ifdef _KERNEL +typedef struct mtx mtx_t; +#endif + +void random_yarrow_init_alg(void); void random_yarrow_deinit_alg(void); -int random_yarrow_read(void *, int); +void random_yarrow_read(uint8_t *, u_int); +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 diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index 9faea00..9246904 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -853,4 +853,4 @@ kick_init(const void *udata __unused) sched_add(td, SRQ_BORING); thread_unlock(td); } -SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST, kick_init, NULL); +SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_MIDDLE, kick_init, NULL); diff --git a/sys/kern/kern_intr.c b/sys/kern/kern_intr.c index a8d9f1a..7a5d936 100644 --- a/sys/kern/kern_intr.c +++ b/sys/kern/kern_intr.c @@ -885,13 +885,10 @@ intr_event_schedule_thread(struct intr_event *ie) * If any of the handlers for this ithread claim to be good * sources of entropy, then gather some. */ - if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) { - CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__, - p->p_pid, td->td_name); + if (ie->ie_flags & IE_ENTROPY) { entropy.event = (uintptr_t)ie; entropy.td = ctd; - random_harvest(&entropy, sizeof(entropy), 2, - RANDOM_INTERRUPT); + random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); } KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name)); @@ -1039,13 +1036,10 @@ intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it) * If any of the handlers for this ithread claim to be good * sources of entropy, then gather some. */ - if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) { - CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__, - p->p_pid, td->td_name); + if (ie->ie_flags & IE_ENTROPY) { entropy.event = (uintptr_t)ie; entropy.td = ctd; - random_harvest(&entropy, sizeof(entropy), 2, - RANDOM_INTERRUPT); + random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); } KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name)); @@ -1130,14 +1124,9 @@ swi_sched(void *cookie, int flags) CTR3(KTR_INTR, "swi_sched: %s %s need=%d", ie->ie_name, ih->ih_name, ih->ih_need); - if (harvest.swi) { - CTR2(KTR_INTR, "swi_sched: pid %d (%s) gathering entropy", - curproc->p_pid, curthread->td_name); - entropy.event = (uintptr_t)ih; - entropy.td = curthread; - random_harvest(&entropy, sizeof(entropy), 1, - RANDOM_SWI); - } + entropy.event = (uintptr_t)ih; + entropy.td = curthread; + random_harvest(&entropy, sizeof(entropy), 1, RANDOM_SWI); /* * Set ih_need for this handler so that if the ithread is already diff --git a/sys/kern/subr_bus.c b/sys/kern/subr_bus.c index 31ad45e..51d7ca6 100644 --- a/sys/kern/subr_bus.c +++ b/sys/kern/subr_bus.c @@ -2851,7 +2851,7 @@ device_attach(device_t dev) * need to be adjusted on other platforms. */ #ifdef RANDOM_DEBUG - printf("%s(): feeding %d bit(s) of entropy from %s%d\n", + printf("random: %s(): feeding %d bit(s) of entropy from %s%d\n", __func__, 4, dev->driver->name, dev->unit); #endif random_harvest(&attachtime, sizeof(attachtime), 4, RANDOM_ATTACH); diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 23e4753..fa956df 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -270,6 +270,7 @@ SUBDIR= \ ${_opensolaris} \ oce \ ${_padlock} \ + ${_padlock_rng} \ patm \ ${_pccard} \ ${_pcfclock} \ @@ -297,6 +298,7 @@ SUBDIR= \ ${_random} \ rc4 \ ${_rdma} \ + ${_rdrand_rng} \ re \ reiserfs \ rl \ @@ -583,6 +585,8 @@ _nvram= nvram _nxge= nxge .if ${MK_CRYPT} != "no" || defined(ALL_MODULES) _padlock= padlock +_padlock_rng= padlock_rng +_rdrand_rng= rdrand_rng .endif _s3= s3 _tpm= tpm @@ -602,6 +606,17 @@ _x86bios= x86bios _ixl= ixl _ixlv= ixlv _ntb= ntb +_nvd= nvd +_nvme= nvme +_nvram= nvram +_nxge= nxge +.if ${MK_CDDL} != "no" || defined(ALL_MODULES) +_opensolaris= opensolaris +.endif +.if ${MK_CRYPT} != "no" || defined(ALL_MODULES) +_padlock= padlock +.endif +_pccard= pccard _qlxge= qlxge _qlxgb= qlxgb _qlxgbe= qlxgbe diff --git a/sys/modules/padlock_rng/Makefile b/sys/modules/padlock_rng/Makefile new file mode 100644 index 0000000..25bf24f --- /dev/null +++ b/sys/modules/padlock_rng/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/random + +KMOD= padlock_rng +SRCS= nehemiah.c +SRCS+= bus_if.h device_if.h + +CFLAGS+= -I${.CURDIR}/../.. + +.include <bsd.kmod.mk> diff --git a/sys/modules/random/Makefile b/sys/modules/random/Makefile index 6bf47f2..7d0370f 100644 --- a/sys/modules/random/Makefile +++ b/sys/modules/random/Makefile @@ -5,13 +5,9 @@ .PATH: ${.CURDIR}/../../crypto/sha2 KMOD= random -SRCS= randomdev.c -.if ${MACHINE} == "amd64" || ${MACHINE} == "i386" -SRCS+= nehemiah.c -SRCS+= ivy.c -.endif -SRCS+= randomdev_soft.c yarrow.c hash.c -SRCS+= random_harvestq.c live_entropy_sources.c rwfile.c +SRCS= randomdev_soft.c +SRCS+= yarrow.c hash.c +SRCS+= random_harvestq.c live_entropy_sources.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 diff --git a/sys/modules/rdrand_rng/Makefile b/sys/modules/rdrand_rng/Makefile new file mode 100644 index 0000000..9d5adc3 --- /dev/null +++ b/sys/modules/rdrand_rng/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/random + +KMOD= rdrand_rng +SRCS= ivy.c +SRCS+= bus_if.h device_if.h + +CFLAGS+= -I${.CURDIR}/../.. + +.include <bsd.kmod.mk> diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index 5f315ec..87e71b6 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -576,8 +576,7 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m) m->m_flags |= M_PROMISC; } - if (harvest.ethernet) - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER); + random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER); ether_demux(ifp, m); CURVNET_RESTORE(); diff --git a/sys/net/if_tun.c b/sys/net/if_tun.c index 89af288..bb9de44 100644 --- a/sys/net/if_tun.c +++ b/sys/net/if_tun.c @@ -906,8 +906,7 @@ tunwrite(struct cdev *dev, struct uio *uio, int flag) m_freem(m); return (EAFNOSUPPORT); } - if (harvest.point_to_point) - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN); + random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN); if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len); if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); CURVNET_SET(ifp->if_vnet); diff --git a/sys/netgraph/ng_iface.c b/sys/netgraph/ng_iface.c index 7326d3e..5aec8a3 100644 --- a/sys/netgraph/ng_iface.c +++ b/sys/netgraph/ng_iface.c @@ -758,8 +758,7 @@ ng_iface_rcvdata(hook_p hook, item_p item) m_freem(m); return (EAFNOSUPPORT); } - if (harvest.point_to_point) - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG); + random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG); M_SETFIB(m, ifp->if_fib); netisr_dispatch(isr, m); return (0); diff --git a/sys/sys/random.h b/sys/sys/random.h index 8bb262a..2714625 100644 --- a/sys/sys/random.h +++ b/sys/sys/random.h @@ -34,12 +34,17 @@ int read_random(void *, int); /* - * Note: if you add or remove members of esource, remember to also update the - * KASSERT regarding what valid members are in random_harvest_internal(). + * Note: if you add or remove members of random_entropy_source, remember to also update the + * KASSERT regarding what valid members are in random_harvest_internal(), and remember the + * strings in the static array random_source_descr[] in random_harvestq.c. + * + * NOTE: complain loudly to markm@ or on the lists if this enum gets more than 32 + * distinct values (0-31)! ENTROPYSOURCE may be == 32, but not > 32. */ -enum esource { +enum random_entropy_source { RANDOM_START = 0, RANDOM_CACHED = 0, + /* Environmental sources */ RANDOM_ATTACH, RANDOM_KEYBOARD, RANDOM_MOUSE, @@ -48,6 +53,9 @@ enum esource { RANDOM_NET_NG, RANDOM_INTERRUPT, RANDOM_SWI, + RANDOM_UMA_ALLOC, + RANDOM_ENVIRONMENTAL_END, /* This one is wasted */ + /* High-quality HW RNGs from here on. */ RANDOM_PURE_OCTEON, RANDOM_PURE_SAFE, RANDOM_PURE_GLXSB, @@ -59,20 +67,7 @@ enum esource { RANDOM_PURE_VIRTIO, ENTROPYSOURCE }; -void random_harvest(const void *, u_int, u_int, enum esource); - -/* Allow the sysadmin to select the broad category of - * entropy types to harvest - */ -struct harvest_select { - int ethernet; - int point_to_point; - int interrupt; - int swi; - int namei; -}; - -extern struct harvest_select harvest; +void random_harvest(const void *, u_int, u_int, enum random_entropy_source); #endif /* _KERNEL */ diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c index 8527b09..50e66c0 100644 --- a/sys/vm/uma_core.c +++ b/sys/vm/uma_core.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/mutex.h> #include <sys/proc.h> +#include <sys/random.h> #include <sys/rwlock.h> #include <sys/sbuf.h> #include <sys/sched.h> @@ -2097,6 +2098,12 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) int lockfail; int cpu; +#if 0 + /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ + /* The entropy here is desirable, but the harvesting is expensive */ + random_harvest(&(zone->uz_name), sizeof(void *), 1, RANDOM_UMA_ALLOC); +#endif + /* This is the fast path allocation */ #ifdef UMA_DEBUG_ALLOC_1 printf("Allocating one item from %s(%p)\n", zone->uz_name, zone); @@ -2127,6 +2134,11 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) zone->uz_fini(item, zone->uz_size); return (NULL); } +#if 0 + /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ + /* The entropy here is desirable, but the harvesting is expensive */ + random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); +#endif return (item); } /* This is unfortunate but should not be fatal. */ @@ -2169,6 +2181,11 @@ zalloc_start: #endif if (flags & M_ZERO) uma_zero_item(item, zone); +#if 0 + /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ + /* The entropy here is desirable, but the harvesting is expensive */ + random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); +#endif return (item); } @@ -2289,6 +2306,11 @@ zalloc_start: zalloc_item: item = zone_alloc_item(zone, udata, flags); +#if 0 + /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ + /* The entropy here is desirable, but the harvesting is expensive */ + random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); +#endif return (item); } @@ -2636,6 +2658,19 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata) int lockfail; int cpu; +#if 0 + /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ + /* The entropy here is desirable, but the harvesting is expensive */ + struct entropy { + const void *uz_name; + const void *item; + } entropy; + + entropy.uz_name = zone->uz_name; + entropy.item = item; + random_harvest(&entropy, sizeof(struct entropy), 2, RANDOM_UMA_ALLOC); +#endif + #ifdef UMA_DEBUG_ALLOC_1 printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone); #endif |