Index: projects/random_number_generator/sys/dev/random/dummy_rng.c =================================================================== --- projects/random_number_generator/sys/dev/random/dummy_rng.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/dummy_rng.c (revision 258288) @@ -1,144 +1,115 @@ /*- * Copyright (c) 2013 Arthur Mesh * 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 __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include +#include #include #include #include #include -#include #include #include #include -#include #include #include -static struct mtx dummy_random_mtx; - -/* If no entropy device is loaded, don't spam the console with warnings */ -static int warned = 0; - -/* Used to fake out unused random calls in random_adaptor */ -static void -random_null_func(void) -{ -} - 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) -{ - 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); -} - static void -dummy_random_init(void) +dummy_random(void) { - - mtx_init(&dummy_random_mtx, "sleep mtx for dummy_random", - NULL, MTX_DEF); } +/* ARGSUSED */ static void -dummy_random_deinit(void) +dummy_random_init(struct mtx *mtx __unused) { - mtx_destroy(&dummy_random_mtx); + randomdev_init_reader(dummy_random_read_phony); } /* This is used only by the internal read_random(9) call, and then only * if no entropy processor is loaded. * - * DO NOT, REPEAT, DO NOT add this to the "struct random_adaptor" below! - * * 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. */ -int -dummy_random_read_phony(void *buf, int count) +u_int +dummy_random_read_phony(void *buf, u_int count) { + /* If no entropy device is loaded, don't spam the console with warnings */ + static int warned = 0; u_long randval; int size, i; if (!warned) { - log(LOG_WARNING, "random device not loaded; using insecure pseudo-random number generator\n"); + log(LOG_WARNING, "random device not loaded/active; using insecure pseudo-random number generator\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); } struct random_adaptor randomdev_dummy = { - .ra_ident = "Dummy entropy device", - .ra_init = dummy_random_init, - .ra_deinit = dummy_random_deinit, - .ra_block = dummy_random_block, - .ra_poll = dummy_random_poll, - .ra_read = (random_adaptor_read_func_t *)random_null_func, - .ra_reseed = (random_adaptor_reseed_func_t *)random_null_func, - .ra_seeded = 0, /* This device can never be seeded */ + .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, }; Index: projects/random_number_generator/sys/dev/random/ivy.c =================================================================== --- projects/random_number_generator/sys/dev/random/ivy.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/ivy.c (revision 258288) @@ -1,134 +1,136 @@ /*- * Copyright (c) 2013 The FreeBSD Foundation * Copyright (c) 2013 David E. O'Brien * Copyright (c) 2012 Konstantin Belousov * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RETRY_COUNT 10 -static int random_ivy_read(void *, int); +static u_int random_ivy_read(void *, u_int); static struct live_entropy_source random_ivy = { - .les_ident = "Hardware, Intel IvyBridge+ RNG", + .les_ident = "Intel IvyBridge+ RNG", .les_source = RANDOM_PURE_RDRAND, .les_read = random_ivy_read }; static inline int ivy_rng_store(long *buf) { #ifdef __GNUCLIKE_ASM long tmp; int retry; retry = RETRY_COUNT; __asm __volatile( "1:\n\t" "rdrand %2\n\t" /* read randomness into tmp */ "jb 2f\n\t" /* CF is set on success, exit retry loop */ "dec %0\n\t" /* otherwise, retry-- */ "jne 1b\n\t" /* and loop if retries are not exhausted */ "jmp 3f\n" /* failure, retry is 0, used as return value */ "2:\n\t" "mov %2,%1\n\t" /* *buf = tmp */ "3:" : "+q" (retry), "=m" (*buf), "=q" (tmp) : : "cc"); return (retry); #else /* __GNUCLIKE_ASM */ return (0); #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) + b = buf; + for (count = c; count > 0; count -= sizeof(long)) { + if (ivy_rng_store(b++) == 0) break; } return (c - count); } static int rdrand_modevent(module_t mod, int type, void *unused) { int error = 0; switch (type) { case MOD_LOAD: if (cpu_feature2 & CPUID2_RDRAND) { live_entropy_source_register(&random_ivy); printf("random: live provider: \"%s\"\n", random_ivy.les_ident); } break; case MOD_UNLOAD: if (cpu_feature2 & CPUID2_RDRAND) live_entropy_source_deregister(&random_ivy); break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } DEV_MODULE(rdrand, rdrand_modevent, NULL); MODULE_VERSION(rdrand, 1); MODULE_DEPEND(rdrand, randomdev, 1, 1, 1); Index: projects/random_number_generator/sys/dev/random/live_entropy_sources.c =================================================================== --- projects/random_number_generator/sys/dev/random/live_entropy_sources.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/live_entropy_sources.c (revision 258288) @@ -1,192 +1,194 @@ /*- * Copyright (c) 2013 Arthur Mesh * 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 __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "live_entropy_sources.h" /* * 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 live_entropy_source *rsource) { struct live_entropy_sources *lles; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); lles = malloc(sizeof(struct live_entropy_sources), M_ENTROPY, M_WAITOK); lles->lles_rsource = rsource; sx_xlock(&les_lock); LIST_INSERT_HEAD(&les_sources, lles, lles_entries); sx_xunlock(&les_lock); } void live_entropy_source_deregister(struct live_entropy_source *rsource) { struct live_entropy_sources *lles = NULL; KASSERT(rsource != NULL, ("invalid input to %s", __func__)); sx_xlock(&les_lock); LIST_FOREACH(lles, &les_sources, lles_entries) if (lles->lles_rsource == rsource) { LIST_REMOVE(lles, lles_entries); break; } sx_xunlock(&les_lock); if (lles != NULL) free(lles, M_ENTROPY); } static int live_entropy_source_handler(SYSCTL_HANDLER_ARGS) { struct live_entropy_sources *lles; struct sbuf sbuf; int error, count; sx_slock(&les_lock); sbuf_new_for_sysctl(&sbuf, NULL, 64, req); 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); } /* * Run through all "live" sources reading entropy for the given * number of rounds, which should be a multiple of the number * of entropy accumulation pools in use; 2 for Yarrow and 32 * for Fortuna. * * 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(void) { static struct harvest_event event; struct live_entropy_sources *lles; - int i, n, read_rate; + int i, read_rate; + u_int n; sx_slock(&les_lock); /* * Walk over all of live entropy sources, and feed their output * to the system-wide RNG. */ read_rate = random_adaptor_read_rate(); LIST_FOREACH(lles, &les_sources, lles_entries) { for (i = 0; i < harvest_pool_count*read_rate; i++) { 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]++; /* 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 - (u_int)n); + memset(event.he_entropy + n, 0, HARVESTSIZE - n); /* Do the actual entropy insertion */ harvest_process_event(&event); } } sx_sunlock(&les_lock); } 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"); } static void live_entropy_sources_deinit(void *unused) { sx_destroy(&les_lock); } 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); Index: projects/random_number_generator/sys/dev/random/live_entropy_sources.h =================================================================== --- projects/random_number_generator/sys/dev/random/live_entropy_sources.h (revision 258287) +++ projects/random_number_generator/sys/dev/random/live_entropy_sources.h (revision 258288) @@ -1,55 +1,57 @@ /*- * Copyright (c) 2013 Arthur Mesh * 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 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_adaptor_read_func_t *les_read; + random_live_read_func_t *les_read; }; struct live_entropy_sources { 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 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 */ Index: projects/random_number_generator/sys/dev/random/nehemiah.c =================================================================== --- projects/random_number_generator/sys/dev/random/nehemiah.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/nehemiah.c (revision 258288) @@ -1,159 +1,160 @@ /*- * Copyright (c) 2013 Mark R V Murray * Copyright (c) 2013 David E. O'Brien * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 live_entropy_source random_nehemiah = { - .les_ident = "Hardware, VIA Nehemiah Padlock RNG", + .les_ident = "VIA Nehemiah Padlock RNG", .les_source = RANDOM_PURE_NEHEMIAH, .les_read = random_nehemiah_read }; /* 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)() ? */ static struct fpu_kern_ctx *fpu_ctx_save; /* This H/W source never stores more than 8 bytes in one go */ /* ARGSUSED */ static __inline size_t VIA_RNG_store(void *buf) { uint32_t retval = 0; uint32_t rate = 0; #ifdef __GNUCLIKE_ASM __asm __volatile( "movl $0,%%edx\n\t" "xstore" : "=a" (retval), "+d" (rate), "+D" (buf) : : "memory" ); #endif if (rate == 0) return (retval&0x1f); return (0); } static void random_nehemiah_init(void) { fpu_ctx_save = fpu_kern_alloc_ctx(FPU_KERN_NORMAL); } static void 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; uint64_t tmp; if ((fpu_kern_enter(curthread, fpu_ctx_save, FPU_KERN_NORMAL) == 0)) { b = buf; for (count = c; count > 0; count -= ret) { ret = MIN(VIA_RNG_store(&tmp), count); memcpy(b, &tmp, ret); b += ret; } fpu_kern_leave(curthread, fpu_ctx_save); } else c = 0; return (c); } static int nehemiah_modevent(module_t mod, int type, void *unused) { int error = 0; switch (type) { 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(); } break; case MOD_UNLOAD: if (via_feature_rng & VIA_HAS_RNG) random_nehemiah_deinit(); live_entropy_source_deregister(&random_nehemiah); break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } DEV_MODULE(nehemiah, nehemiah_modevent, NULL); MODULE_VERSION(nehemiah, 1); MODULE_DEPEND(nehemiah, randomdev, 1, 1, 1); Index: projects/random_number_generator/sys/dev/random/random_adaptors.c =================================================================== --- projects/random_number_generator/sys/dev/random/random_adaptors.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/random_adaptors.c (revision 258288) @@ -1,411 +1,525 @@ /*- * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2013 David E. O'Brien * 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 __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include +#include +#include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include -/* These are the data structures and associated items that need to be locked against - * "under-the-feet" changes. +/* 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; /* need a sleepable lock */ - +static struct sx random_adaptors_lock; LIST_HEAD(adaptors_head, random_adaptors); static struct adaptors_head random_adaptors_list = LIST_HEAD_INITIALIZER(random_adaptors_list); static struct random_adaptor *random_adaptor = NULL; /* Currently active adaptor */ -/* End of data items requiring adaptor lock protection */ +/* End of data items requiring random_adaptors_lock protection */ -/* The rate mutex protects the consistency of the read-rate logic. */ -struct mtx rate_mtx; +/* The random_rate_mtx mutex protects the consistency of the read-rate logic. */ +struct mtx random_rate_mtx; int random_adaptor_read_rate_cache; -/* End of data items requiring rate mutex protection */ +/* End of data items requiring random_rate_mtx mutex protection */ -MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures"); +/* The random_reseed_mtx mutex protects seeding and polling/blocking. + * This is passed into the software entropy hasher/processor. + */ +struct mtx random_reseed_mtx; +/* End of data items requiring random_reseed_mtx mutex protection */ -static void random_adaptor_choose(void); +static struct selinfo rsel; +/* Utility routine to change active adaptor when the random_adaptors_list + * gets modified. + * + * Walk a list of registered random(4) adaptors and pick either a requested + * one or the highest priority one, whichever comes first. Panic on failure + * as the fallback must always be the "dummy" adaptor. + */ +static void +random_adaptor_choose(void) +{ + char rngs[128], *token, *cp; + struct random_adaptors *rra, *rrai; + struct random_adaptor *random_adaptor_previous; + int primax; + + /* We are going to be messing with random_adaptor. + * Exclusive lock is mandatory. + */ + sx_assert(&random_adaptors_lock, SA_XLOCKED); + + random_adaptor_previous = random_adaptor; + + random_adaptor = NULL; + if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { + cp = rngs; + + 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)(&random_reseed_mtx); + } +} + + +/* XXX: FIX!! Make sure we are not inserting a duplicate */ void random_adaptor_register(const char *name, struct random_adaptor *ra) { struct random_adaptors *rra; KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__)); rra = malloc(sizeof(struct random_adaptors), M_ENTROPY, M_WAITOK); rra->rra_name = name; rra->rra_ra = ra; sx_xlock(&random_adaptors_lock); - - /* XXX: FIX!! Make sure we are not inserting a duplicate */ LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries); - random_adaptor_choose(); - sx_xunlock(&random_adaptors_lock); KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); } void random_adaptor_deregister(const char *name) { struct random_adaptors *rra; KASSERT(name != NULL, ("invalid input to %s", __func__)); KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_xlock(&random_adaptors_lock); - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) if (strcmp(rra->rra_name, name) == 0) { LIST_REMOVE(rra, rra_entries); break; } - random_adaptor_choose(); - /* It is conceivable that there is no active random adaptor here, - * e.g. at shutdown. - */ - sx_xunlock(&random_adaptors_lock); - if (rra != NULL) - free(rra, M_ENTROPY); + free(rra, M_ENTROPY); } +/* + * Per-instance structure. + * + * List of locks + * XXX: FIX!! + */ +struct random_adaptor_softc { + int oink; + int tweet; +}; + +static void +random_adaptor_dtor(void *data) +{ + struct random_adaptor_softc *ras = data; + + free(ras, M_ENTROPY); +} + +/* ARGSUSED */ int -random_adaptor_block(int flag) +random_adaptor_open(struct cdev *dev __unused, int flags, int mode __unused, struct thread *td __unused) { - int ret; + struct random_adaptor_softc *ras; KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - sx_slock(&random_adaptors_lock); + ras = malloc(sizeof(struct random_adaptor_softc), M_ENTROPY, M_WAITOK|M_ZERO); + /* XXX: FIX!! Set up softc here */ - ret = random_adaptor->ra_block(flag); + devfs_set_cdevpriv(ras, random_adaptor_dtor); - sx_sunlock(&random_adaptors_lock); + /* Give the source a chance to do some pre-read/write startup */ + if (flags & FREAD) { + sx_slock(&random_adaptors_lock); + (random_adaptor->ra_read)(NULL, 0); + sx_sunlock(&random_adaptors_lock); + } else if (flags & FWRITE) { + sx_slock(&random_adaptors_lock); + (random_adaptor->ra_write)(NULL, 0); + sx_sunlock(&random_adaptors_lock); + } - return ret; + return (0); } +/* ARGSUSED */ int -random_adaptor_read(struct uio *uio, int flag) +random_adaptor_close(struct cdev *dev __unused, int flags, int fmt __unused, struct thread *td __unused) { - int c, error = 0; - void *random_buf; KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - /* The read-rate stuff is a *VERY* crude measure of the instantaneous read rate, designed - * to increase the use of 'live' entropy sources when lots of reads are done. - */ - mtx_lock(&rate_mtx); - random_adaptor_read_rate_cache += (int)((uio->uio_resid + PAGE_SIZE + 1)/PAGE_SIZE); - mtx_unlock(&rate_mtx); + /* Give the source a chance to do some post-read/write shutdown */ + if (flags & FREAD) { + sx_slock(&random_adaptors_lock); + (random_adaptor->ra_read)(NULL, 1); + sx_sunlock(&random_adaptors_lock); + } else if (flags & FWRITE) { + sx_slock(&random_adaptors_lock); + (random_adaptor->ra_write)(NULL, 1); + sx_sunlock(&random_adaptors_lock); + } - sx_slock(&random_adaptors_lock); + return (0); +} - /* Blocking logic */ - if (random_adaptor->ra_seeded) - error = (random_adaptor->ra_block)(flag); +/* ARGSUSED */ +int +random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags __unused) +{ + void *random_buf; + int c, error; + u_int npages; + struct random_adaptor_softc *ras; - /* The actual read */ - if (!error) { + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - random_buf = (void *)malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); + error = devfs_get_cdevpriv((void **)&ras); + if (error == 0) { - while (uio->uio_resid > 0 && !error) { - c = MIN(uio->uio_resid, PAGE_SIZE); - c = (random_adaptor->ra_read)(random_buf, c); - error = uiomove(random_buf, c, uio); + sx_slock(&random_adaptors_lock); + + /* Blocking logic */ + mtx_lock(&random_reseed_mtx); + while (!random_adaptor->ra_seeded() && !error) { + if (flags & O_NONBLOCK) + error = EWOULDBLOCK; + else + error = msleep(&random_adaptor, &random_reseed_mtx, PUSER | PCATCH, "block", 0); } - /* Finished reading; let the source know so it can do some - * optional housekeeping */ - (random_adaptor->ra_read)(NULL, 0); + mtx_unlock(&random_reseed_mtx); - free(random_buf, M_ENTROPY); + /* The actual read */ + if (!error) { + /* The read-rate stuff is a *VERY* crude indication of the instantaneous read rate, + * designed to increase the use of 'live' entropy sources when lots of reads are done. + */ + mtx_lock(&random_rate_mtx); + npages = (uio->uio_resid + PAGE_SIZE - 1)/PAGE_SIZE; + random_adaptor_read_rate_cache += npages; + random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32); + mtx_unlock(&random_rate_mtx); - } + random_buf = (void *)malloc(npages*PAGE_SIZE, M_ENTROPY, M_WAITOK); + while (uio->uio_resid > 0 && !error) { + c = MIN(uio->uio_resid, npages*PAGE_SIZE); + (random_adaptor->ra_read)(random_buf, c); + error = uiomove(random_buf, c, uio); + } + free(random_buf, M_ENTROPY); + } - sx_sunlock(&random_adaptors_lock); + sx_sunlock(&random_adaptors_lock); + } + return (error); } int random_adaptor_read_rate(void) { int ret; - mtx_lock(&rate_mtx); - ret = random_adaptor_read_rate_cache = random_adaptor_read_rate_cache ? random_adaptor_read_rate_cache%32 + 1 : 1; - mtx_unlock(&rate_mtx); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); + mtx_lock(&random_rate_mtx); + ret = random_adaptor_read_rate_cache; + mtx_unlock(&random_rate_mtx); + return (ret); } +/* ARGSUSED */ int -random_adaptor_poll(int events, struct thread *td) +random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { - int revents = 0; + int error; + struct random_adaptor_softc *ras; - sx_slock(&random_adaptors_lock); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - if (events & (POLLIN | POLLRDNORM)) { - if (random_adaptor->ra_seeded) - revents = events & (POLLIN | POLLRDNORM); - else - revents = (random_adaptor->ra_poll)(events, td); + sx_slock(&random_adaptors_lock); + error = devfs_get_cdevpriv((void **)&ras); + if (error == 0) { + /* 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. + */ + /* XXX: FIX!! Now that RWFILE is gone, we need to get this back. + * ALSO: See devfs_get_cdevpriv(9) and friends for ways to build per-session nodes. + */ + uio->uio_resid = 0; + /* c = (random_adaptor->ra_write)(random_buf, c); */ } - sx_sunlock(&random_adaptors_lock); - return (revents); + return (error); } -/* - * 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 be the "dummy" adaptor. - */ -static void -random_adaptor_choose(void) +/* ARGSUSED */ +int +random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused) { - char rngs[128], *token, *cp; - struct random_adaptors *rra, *rrai; - struct random_adaptor *random_adaptor_previous; - int primax; + struct random_adaptor_softc *ras; - /* We are going to be messing with random_adaptor. - * Exclusive lock is mandatory. - */ - sx_assert(&random_adaptors_lock, SA_XLOCKED); + KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - random_adaptor_previous = random_adaptor; + if (devfs_get_cdevpriv((void **)&ras) != 0) + return (events & + (POLLHUP|POLLIN|POLLRDNORM|POLLOUT|POLLWRNORM)); - random_adaptor = NULL; - if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { - cp = rngs; - - 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); - } + sx_slock(&random_adaptors_lock); + mtx_lock(&random_reseed_mtx); + if (events & (POLLIN | POLLRDNORM)) { + if (random_adaptor->ra_seeded()) + events &= (POLLIN | POLLRDNORM); + else + selrecord(td, &rsel); } + mtx_unlock(&random_reseed_mtx); + sx_sunlock(&random_adaptors_lock); - 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); - } + return (events); +} - KASSERT(random_adaptor != NULL, ("adaptor not found")); +/* This will be called by the entropy processor when it seeds itself and becomes secure */ +void +random_adaptor_unblock(void) +{ - /* If we are changing adaptors, deinit the old and init the new. */ - if (random_adaptor != random_adaptor_previous) { - if (random_adaptor_previous != NULL) - (random_adaptor_previous->ra_deinit)(); - (random_adaptor->ra_init)(); - } + mtx_assert(&random_reseed_mtx, MA_OWNED); + + selwakeuppri(&rsel, PUSER); + wakeup(&random_adaptor); + printf("random: unblocking device.\n"); + + /* Do arc4random(9) a favour while we are about it. */ + (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); } static int random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS) { struct random_adaptors *rra; struct sbuf sbuf; int error, count; sx_slock(&random_adaptors_lock); - sbuf_new_for_sysctl(&sbuf, NULL, 64, req); - count = 0; LIST_FOREACH(rra, &random_adaptors_list, rra_entries) sbuf_printf(&sbuf, "%s%s(%d)", (count++ ? "," : ""), rra->rra_name, rra->rra_ra->ra_priority); error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); - sx_sunlock(&random_adaptors_lock); return (error); } static int random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS) { struct random_adaptors *rra; struct sbuf sbuf; int error; KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); - sbuf_new_for_sysctl(&sbuf, NULL, 16, req); - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) if (rra->rra_ra == random_adaptor) { sbuf_cat(&sbuf, rra->rra_name); break; } - - error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); - sx_sunlock(&random_adaptors_lock); return (error); } /* ARGSUSED */ static void random_adaptors_init(void *unused __unused) { SYSCTL_PROC(_kern_random, OID_AUTO, adaptors, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, random_sysctl_adaptors_handler, "", "Random Number Generator adaptors"); SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, random_sysctl_active_adaptor_handler, "", "Active Random Number Generator Adaptor"); sx_init(&random_adaptors_lock, "random_adaptors"); - mtx_init(&rate_mtx, "read rate mutex", NULL, MTX_DEF); - /* This dummy "thing" is not a module by itself, but part of the + mtx_init(&random_rate_mtx, "read rate mutex", NULL, MTX_DEF); + mtx_init(&random_reseed_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); } +SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, + random_adaptors_init, NULL); /* ARGSUSED */ static void random_adaptors_deinit(void *unused __unused) { - /* Don't do this! Panic will follow. */ + /* Don't do this! Panic will surely follow! */ /* random_adaptor_deregister("dummy"); */ - mtx_destroy(&rate_mtx); + mtx_destroy(&random_reseed_mtx); + mtx_destroy(&random_rate_mtx); + sx_destroy(&random_adaptors_lock); } +SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, + random_adaptors_deinit, NULL); /* * 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_seed(void *unused __unused) { KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); sx_slock(&random_adaptors_lock); - random_adaptor->ra_reseed(); - sx_sunlock(&random_adaptors_lock); arc4rand(NULL, 0, 1); } SYSINIT(random_seed, SI_SUB_INTRINSIC_POST, SI_ORDER_LAST, random_adaptors_reseed, NULL); #endif /* RANDOM_AUTOSEED */ - -SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_SECOND, - random_adaptors_init, NULL); -SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_SECOND, - random_adaptors_deinit, NULL); Index: projects/random_number_generator/sys/dev/random/random_adaptors.h =================================================================== --- projects/random_number_generator/sys/dev/random/random_adaptors.h (revision 258287) +++ projects/random_number_generator/sys/dev/random/random_adaptors.h (revision 258288) @@ -1,73 +1,73 @@ /*- * Copyright (c) 2013 Arthur Mesh * 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_RANDOM_ADAPTORS_H_INCLUDED #define SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED MALLOC_DECLARE(M_ENTROPY); -typedef void random_adaptor_init_func_t(void); +typedef void random_adaptor_init_func_t(struct mtx *); typedef void random_adaptor_deinit_func_t(void); - -typedef int random_adaptor_block_func_t(int); -typedef int random_adaptor_read_func_t(void *, int); -typedef int random_adaptor_poll_func_t(int, struct thread *); - +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_seeded; - int ra_priority; + int ra_priority; random_adaptor_init_func_t *ra_init; random_adaptor_deinit_func_t *ra_deinit; - random_adaptor_block_func_t *ra_block; random_adaptor_read_func_t *ra_read; - random_adaptor_poll_func_t *ra_poll; + 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) rra_entries; /* list of providers */ const char *rra_name; /* name of random adaptor */ struct random_adaptor *rra_ra; }; /* Dummy "always-block" pseudo-device */ extern struct random_adaptor randomdev_dummy; void random_adaptor_register(const char *, struct random_adaptor *); void random_adaptor_deregister(const char *); -int random_adaptor_block(int); -int random_adaptor_read(struct uio *, int); -int random_adaptor_poll(int, struct thread *); +int random_adaptor_open(struct cdev *, int, int, struct thread *); +int random_adaptor_read(struct cdev *, struct uio *, int); +int random_adaptor_write(struct cdev *, struct uio *, int); +int random_adaptor_close(struct cdev *, int, int, struct thread *); +int random_adaptor_poll(struct cdev *, int, struct thread *); int random_adaptor_read_rate(void); +void random_adaptor_unblock(void); #endif /* SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED */ Index: projects/random_number_generator/sys/dev/random/random_harvestq.c =================================================================== --- projects/random_number_generator/sys/dev/random/random_harvestq.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/random_harvestq.c (revision 258288) @@ -1,277 +1,365 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_random.h" #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include +/* List for the dynamic sysctls */ +static struct sysctl_ctx_list random_clist; + /* * 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 { STAILQ_HEAD(harvestlist, harvest_event) head; }; /* Empty entropy buffers */ static struct entropyfifo emptyfifo; /* Harvested entropy */ static struct entropyfifo harvestfifo; /* Round-robin destination cache. */ u_int harvest_destination[ENTROPYSOURCE]; /* Function called to process one harvested stochastic event */ void (*harvest_process_event)(struct harvest_event *); +/* Allow the sysadmin to select the broad category of + * entropy types to harvest. + */ +static u_int harvest_source_mask = ((1<= 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.head, &harvestfifo.head); /* * Deal with events, if any. * Then transfer the used events back into the empty fifo. */ if (!STAILQ_EMPTY(&local_queue.head)) { mtx_unlock_spin(&harvest_mtx); STAILQ_FOREACH(event, &local_queue.head, he_next) harvest_process_event(event); mtx_lock_spin(&harvest_mtx); STAILQ_CONCAT(&emptyfifo.head, &local_queue.head); } /* * Give the fast hardware sources a go */ mtx_unlock_spin(&harvest_mtx); live_entropy_sources_feed(); mtx_lock_spin(&harvest_mtx); /* * If a queue flush was commanded, it has now happened, * and we can mark this by resetting the command. */ 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)); } mtx_unlock_spin(&harvest_mtx); randomdev_set_wakeup_exit(&random_kthread_control); /* NOTREACHED */ } void +random_harvestq_flush(void) +{ + + /* Command a entropy queue flush and wait for it to finish */ + random_kthread_control = 1; + while (random_kthread_control) + pause("-", hz/10); + +#if 0 +#if defined(RANDOM_YARROW) + random_yarrow_reseed(); +#endif +#if defined(RANDOM_FORTUNA) + random_fortuna_reseed(); +#endif +#endif +} + +/* ARGSUSED */ +RANDOM_CHECK_UINT(harvestmask, 0, ((1<= 0; i--) + sbuf_cat(&sbuf, (harvest_source_mask & (1<= RANDOM_START && origin < ENTROPYSOURCE, ("random_harvest_internal: origin %d invalid\n", origin)); + + /* Mask out unwanted sources */ + if (!(harvest_source_mask & (1<he_somecounter = get_cyclecount(); event->he_size = count; event->he_bits = bits; event->he_source = origin; event->he_destination = harvest_destination[origin]++; c = MIN(count, HARVESTSIZE); memcpy(event->he_entropy, entropy, c); memset(event->he_entropy + c, 0, HARVESTSIZE - c); STAILQ_INSERT_TAIL(&harvestfifo.head, event, he_next); } mtx_unlock_spin(&harvest_mtx); } Index: projects/random_number_generator/sys/dev/random/random_harvestq.h =================================================================== --- projects/random_number_generator/sys/dev/random/random_harvestq.h (revision 258287) +++ projects/random_number_generator/sys/dev/random/random_harvestq.h (revision 258288) @@ -1,71 +1,71 @@ /*- * Copyright (c) 2013 Arthur Mesh * 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 SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED #define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED #define HARVESTSIZE 16 /* max size of each harvested entropy unit */ /* 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 */ STAILQ_ENTRY(harvest_event) he_next; /* next item on the list */ }; void random_harvestq_init(void (*)(struct harvest_event *), int); void random_harvestq_deinit(void); void random_harvestq_internal(const void *, u_int, u_int, enum random_entropy_source); /* Pool count is used by anything needing to know how many entropy * pools are currently being maintained. * This is of use to (e.g.) the live source feed where we need to give * all the pools a top-up. */ extern int harvest_pool_count; /* This is in randomdev.c as it needs to be permanently in the kernel */ void randomdev_set_wakeup_exit(void *); -/* Round-robin destination cache. */ -extern u_int harvest_destination[ENTROPYSOURCE]; +/* 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 */ Index: projects/random_number_generator/sys/dev/random/randomdev.c =================================================================== --- projects/random_number_generator/sys/dev/random/randomdev.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/randomdev.c (revision 258288) @@ -1,247 +1,244 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * 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. * */ /* * 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 __FBSDID("$FreeBSD$"); +#include "opt_random.h" + #include #include #include #include #include #include #include #include #include -#include #include -#include +#include #include #include #include #include #include #include -#include - #include #include #include #define RANDOM_MINOR 0 -static d_read_t randomdev_read; -static d_write_t randomdev_write; static d_ioctl_t randomdev_ioctl; -static d_poll_t randomdev_poll; static struct cdevsw random_cdevsw = { + .d_name = "random", .d_version = D_VERSION, - .d_read = randomdev_read, - .d_write = randomdev_write, + .d_open = random_adaptor_open, + .d_read = random_adaptor_read, + .d_write = random_adaptor_write, + .d_close = random_adaptor_close, + .d_poll = random_adaptor_poll, .d_ioctl = randomdev_ioctl, - .d_poll = randomdev_poll, - .d_name = "random", }; /* For use with make_dev(9)/destroy_dev(9). */ static struct cdev *random_dev; -/* Allow the sysadmin to select the broad category of - * entropy types to harvest. - */ -u_int randomdev_harvest_source_mask = ((1<uio_resid = 0; - - return (0); -} - -/* ARGSUSED */ -static int randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused, int flags __unused, struct thread *td __unused) { int error = 0; switch (cmd) { /* Really handled in upper layer */ case FIOASYNC: case FIONBIO: break; + default: error = ENOTTY; + } + return (error); } -/* ARGSUSED */ -static int -randomdev_poll(struct cdev *dev __unused, int events, struct thread *td) +/* 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) { - return (random_adaptor_poll(events, td)); + wakeup(control); + kproc_exit(0); + /* NOTREACHED */ } /* ARGSUSED */ static int randomdev_modevent(module_t mod __unused, int type, void *data __unused) { int error = 0; switch (type) { case MOD_LOAD: 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 */ break; case MOD_UNLOAD: destroy_dev(random_dev); break; case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } + return (error); } /* Order is SI_ORDER_MIDDLE */ DEV_MODULE(randomdev, randomdev_modevent, NULL); MODULE_VERSION(randomdev, 1); -/* Internal stub/fake routine for when no entropy processor is loaded */ -static void random_harvest_phony(const void *, u_int, u_int, enum random_entropy_source); +/* ================ + * Harvesting stubs + * ================ + */ -/* hold the addresses of the routines which are actually called if - * the random device is loaded. +/* 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_harvest_phony(const void *entropy __unused, u_int count __unused, + u_int bits __unused, enum random_entropy_source origin __unused) +{ +} + +/* 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; -static int (*read_func)(void *, int) = dummy_random_read_phony; /* Initialise the harvester when/if it is loaded */ void -randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source), - int (*reader)(void *, int)) +randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source)) { + reap_func = reaper; - read_func = reader; } /* Deinitialise the harvester when/if it is unloaded */ void randomdev_deinit_harvester(void) { + reap_func = random_harvest_phony; - read_func = dummy_random_read_phony; } -/* Entropy harvesting routine. This is supposed to be fast; do - * not do anything slow in here! +/* 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) { - if (randomdev_harvest_source_mask & (1<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 Index: projects/random_number_generator/sys/dev/random/randomdev_soft.c =================================================================== --- projects/random_number_generator/sys/dev/random/randomdev_soft.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/randomdev_soft.c (revision 258288) @@ -1,328 +1,204 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ /* * This is the loadable infrastructure base file for software CSPRNG * drivers such as Yarrow or Fortuna. * * It is anticipated that one instance of this file will be used * for _each_ invocation of a CSPRNG, but with different #defines * set. See below. * */ #include "opt_random.h" #if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) #define RANDOM_YARROW #elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) #error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" #endif -#if defined(RANDOM_FORTUNA) -#error "Fortuna is not yet implemented" -#endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include -#include -#include #include -#include #include #include #include #include #include #if defined(RANDOM_YARROW) #include #endif #if defined(RANDOM_FORTUNA) #include #endif -static int randomdev_poll(int event, struct thread *td); -static int randomdev_block(int flag); -static void randomdev_flush_reseed(void); - -#if defined(RANDOM_YARROW) static struct random_adaptor random_soft_processor = { - .ra_ident = "Software, Yarrow", - .ra_init = randomdev_init, - .ra_deinit = randomdev_deinit, - .ra_block = randomdev_block, - .ra_read = random_yarrow_read, - .ra_poll = randomdev_poll, - .ra_reseed = randomdev_flush_reseed, - .ra_seeded = 0, /* This will be seeded during entropy processing */ - .ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */ -}; -#define RANDOM_MODULE_NAME yarrow +#if defined(RANDOM_YARROW) #define RANDOM_CSPRNG_NAME "yarrow" + .ra_ident = "Yarrow", + .ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */ + .ra_read = random_yarrow_read, + .ra_write = random_yarrow_write, + .ra_reseed = random_yarrow_reseed, + .ra_seeded = random_yarrow_seeded, #endif - #if defined(RANDOM_FORTUNA) -static struct random_adaptor random_soft_processor = { - .ra_ident = "Software, Fortuna", +#define RANDOM_CSPRNG_NAME "fortuna" + .ra_ident = "Fortuna", + .ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */ + .ra_read = random_fortuna_read, + .ra_write = random_fortuna_write, + .ra_reseed = random_fortuna_reseed, + .ra_seeded = random_fortuna_seeded, +#endif .ra_init = randomdev_init, .ra_deinit = randomdev_deinit, - .ra_block = randomdev_block, - .ra_read = random_fortuna_read, - .ra_poll = randomdev_poll, - .ra_reseed = randomdev_flush_reseed, - .ra_seeded = 0, /* This will be excplicitly seeded at startup when secured */ - .ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */ }; -#define RANDOM_MODULE_NAME fortuna -#define RANDOM_CSPRNG_NAME "fortuna" -#endif -TUNABLE_INT("kern.random.sys.seeded", &random_soft_processor.ra_seeded); - /* List for the dynamic sysctls */ static struct sysctl_ctx_list random_clist; -static struct selinfo rsel; - /* 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)); } -/* ARGSUSED */ -RANDOM_CHECK_UINT(harvestmask, 0, ((1<= 0; i--) - sbuf_cat(&sbuf, (randomdev_harvest_source_mask & (1<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 Index: projects/random_number_generator/sys/dev/random/yarrow.c =================================================================== --- projects/random_number_generator/sys/dev/random/yarrow.c (revision 258287) +++ projects/random_number_generator/sys/dev/random/yarrow.c (revision 258288) @@ -1,402 +1,409 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include "opt_random.h" #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #define TIMEBIN 16 /* max value for Pt/t */ #define FAST 0 #define SLOW 1 /* 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 */ struct pool { struct source { u_int bits; /* estimated bits of entropy */ } source[ENTROPYSOURCE]; u_int thresh; /* pool reseed threshhold */ struct randomdev_hash hash; /* accumulated entropy */ } pool[2]; /* pool[0] is fast, pool[1] is slow */ -} random_state; + /* The reseed thread mutex */ + int seeded; + struct mtx *reseed_mtx; +} yarrow_state; + 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); 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) +yarrow_clear_counter(void) { - random_state.counter.qword[0] = 0UL; - random_state.counter.qword[1] = 0UL; + yarrow_state.counter.qword[0] = 0UL; + yarrow_state.counter.qword[1] = 0UL; } /* 128-bit C = C + 1 */ /* Nothing to see here, folks, just an ugly mess. */ /* XXX: TODO? Make a Galois counter instead? */ static void -increment_counter(void) +yarrow_increment_counter(void) { - random_state.counter.qword[0]++; - if (!random_state.counter.qword[0]) - random_state.counter.qword[1]++; + yarrow_state.counter.qword[0]++; + if (!yarrow_state.counter.qword[0]) + yarrow_state.counter.qword[1]++; } /* Process a single stochastic event off the harvest queue */ void random_yarrow_process_event(struct harvest_event *event) { u_int pl, overthreshhold[2]; - struct source *source; enum random_entropy_source src; /* Accumulate the event into the appropriate pool * where each event carries the destination information */ pl = event->he_destination % 2; - source = &random_state.pool[pl].source[event->he_source]; - randomdev_hash_iterate(&random_state.pool[pl].hash, event, - sizeof(*event)); - source->bits += event->he_bits; + randomdev_hash_iterate(&yarrow_state.pool[pl].hash, event, sizeof(*event)); + yarrow_state.pool[pl].source[event->he_source].bits += event->he_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) + if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh) overthreshhold[pl]++; } } /* if any fast source over threshhold, reseed */ - if (overthreshhold[FAST]) + if (overthreshhold[FAST] > 0) reseed(FAST); /* if enough slow sources are over threshhold, reseed */ - if (overthreshhold[SLOW] >= random_state.slowoverthresh) + if (overthreshhold[SLOW] >= yarrow_state.slowoverthresh) reseed(SLOW); } void -random_yarrow_init_alg(struct sysctl_ctx_list *clist) +random_yarrow_init_alg(struct sysctl_ctx_list *clist, struct mtx *mtx) { - int i; + int i, j; struct sysctl_oid *random_yarrow_o; +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif + + /* Set up a lock for the reseed process */ + yarrow_state.reseed_mtx = mtx; + + /* Start unseeded, therefore blocked. */ + yarrow_state.seeded = 0; + /* 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, SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "yarrow", CTLFLAG_RW, 0, "Yarrow Parameters"); SYSCTL_ADD_PROC(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_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_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_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_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"); - 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; + 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; /* 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); + yarrow_clear_counter(); } void random_yarrow_deinit_alg(void) { - mtx_destroy(&random_reseed_mtx); + +#ifdef RANDOM_DEBUG + printf("random: %s\n", __func__); +#endif + memset((void *)(&yarrow_state), 0, sizeof(struct yarrow_state)); } static void reseed(u_int fastslow) { /* Interrupt-context stack is a limited resource; make large * structures static. */ static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */ + static uint8_t hash[KEYSIZE]; /* h' */ + static uint8_t temp[KEYSIZE]; static struct randomdev_hash context; - uint8_t hash[KEYSIZE]; /* h' */ - uint8_t temp[KEYSIZE]; u_int i; enum random_entropy_source j; + KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0")); + KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0")); + +#ifdef RANDOM_DEBUG + if (!yarrow_state.seeded) { + printf("random: %s - %s reseed complete\n", __func__, (fastslow == FAST ? "FAST" : "SLOW")); + 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 + /* The reseed task must not be jumped on */ - mtx_lock(&random_reseed_mtx); + mtx_lock(yarrow_state.reseed_mtx); /* 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, + &yarrow_state.pool[SLOW].hash, sizeof(struct randomdev_hash)); randomdev_hash_iterate(&context, - &random_state.pool[FAST].hash, sizeof(struct randomdev_hash)); + &yarrow_state.pool[FAST].hash, sizeof(struct randomdev_hash)); 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)); /* Return the hashval */ randomdev_hash_finish(&context, v[i]); } /* 3. Compute a new key; h' is the identity function here; * it is not being ignored! */ randomdev_hash_init(&context); - randomdev_hash_iterate(&context, &random_state.key, KEYSIZE); - for (i = 1; i < random_state.bins; i++) + 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); + yarrow_clear_counter(); + 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((void *)&context, 0, sizeof(context)); /* 7. Dump to seed file */ #ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */ /* This pseudo-code is documentation. Please leave it alone. */ seed_file = ""; error = randomdev_write_file(seed_file, , PAGE_SIZE); if (error == 0) printf("random: entropy seed file '%s' successfully written\n", seed_file); #endif /* Unblock the device if it was blocked due to being unseeded */ - randomdev_unblock(); + if (!yarrow_state.seeded) { + yarrow_state.seeded = 1; + random_adaptor_unblock(); + } /* Release the reseed mutex */ - mtx_unlock(&random_reseed_mtx); + mtx_unlock(yarrow_state.reseed_mtx); } /* 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); + mtx_lock(yarrow_state.reseed_mtx); + /* Ensure that the first time this is ever run, we are gated. */ if (gate) { generator_gate(); - random_state.outputblocks = 0; + yarrow_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++) { + yarrow_increment_counter(); + randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, buf, BLOCKSIZE); + buf += BLOCKSIZE; + if (++yarrow_state.outputblocks >= yarrow_state.gengateinterval) { + generator_gate(); + yarrow_state.outputblocks = 0; } } - 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); - return (retval); + mtx_unlock(yarrow_state.reseed_mtx); } +/* Internal function to hand external entropy to the PRNG */ +/* ARGSUSED */ +void +random_yarrow_write(uint8_t *buf __unused, u_int count __unused) +{ +} + static void generator_gate(void) { u_int i; 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); + yarrow_increment_counter(); + randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE); } - randomdev_encrypt_init(&random_state.key, temp); + randomdev_encrypt_init(&yarrow_state.key, temp); memset((void *)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); } Index: projects/random_number_generator/sys/dev/random/yarrow.h =================================================================== --- projects/random_number_generator/sys/dev/random/yarrow.h (revision 258287) +++ projects/random_number_generator/sys/dev/random/yarrow.h (revision 258288) @@ -1,38 +1,40 @@ /*- * Copyright (c) 2000-2013 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef SYS_DEV_RANDOM_YARROW_H_INCLUDED #define SYS_DEV_RANDOM_YARROW_H_INCLUDED -void random_yarrow_init_alg(struct sysctl_ctx_list *); +void random_yarrow_init_alg(struct sysctl_ctx_list *, struct mtx *); 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