Index: UPDATING =================================================================== --- UPDATING +++ UPDATING @@ -31,6 +31,41 @@ disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +2015
: + The default kernel entropy-processing algorithm is now + Fortuna, replacing Yarrow. + + Assuming you have 'device random' in your kernel config + file, the configurations allow a kernel option to override + this default. You may choose *ONE* of: + + options RANDOM_YARROW # Legacy /dev/random algorithm. + options RANDOM_DUMMY # Blocking-only driver. + + If you have neither, you get Fortuna. For most people, + read no further, Fortuna will give a /dev/random that works + like it always used to, and the difference will be irrelevant. + + If you remove 'device random', you get *NO* kernel-processed + entopy at all. This may be acceptable to folks building + embedded systems, but has complications. Carry on reading, + and it is assumed you know what you need. + + *PLEASE* read random(4) and random(9) if you are in the + habit of tweeking kernel configs, and/or if you are a member + of the embedded community, wanting specific and not-usual + behaviour from your security subsystems. + + NOTE!! If you use RANDOM_DUMMY and/or have no 'device + random', you will NOT have a functioning /dev/random, and + many cryptographic features will not work, including SSH. + You may also find strange behaviour from the random(3) set + of library functions, in particular sranddev(3), srandomdev(3) + and arc4random(3). The reason for this is that the KERN_ARND + sysctl only returns entropy if it thinks it has some to + share, and with RANDOM_DUMMY or no 'device random' this + will never happen. + 20150616: FreeBSD's old make (fmake) has been removed from the system. It is available as the devel/fmake port or via pkg install fmake. Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf +++ etc/defaults/rc.conf @@ -630,15 +630,16 @@ # in the system booting with securelevel set to 1, as # init(8) will raise the level when rc(8) completes. update_motd="YES" # update version info in /etc/motd (or NO) -entropy_file="/entropy" # Set to NO to disable caching entropy through reboots. +entropy_boot_file="/boot/entropy" # Set to NO to disable very early + # (used at early boot time) entropy caching through reboots. +entropy_file="/entropy" # Set to NO to disable late (used when going multi-user) + # 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="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 -harvest_p_to_p="YES" # Entropy device harvests point-to-point randomness -harvest_swi="YES" # Entropy device harvests internal SWI randomness +harvest_mask="511" # Entropy device harvests all but the very invasive sources. + # (See 'sysctl kern.random.harvest' and random(4)) dmesg_enable="YES" # Save dmesg(8) to /var/run/dmesg.boot watchdogd_enable="NO" # Start the software watchdog daemon watchdogd_flags="" # Flags to watchdogd (if enabled) Index: etc/rc.d/random =================================================================== --- etc/rc.d/random +++ etc/rc.d/random @@ -41,6 +41,13 @@ random_start() { + + if [ ${harvest_mask} -gt 0 ]; then + echo -n 'Setting up harvesting:' + ${SYSCTL} kern.random.harvest.mask=${harvest_mask} > /dev/null + ${SYSCTL_N} sysctl -n kern.random.harvest.mask_symbolic + fi + echo -n 'Feeding entropy:' if [ ! -w /dev/random ] ; then @@ -68,6 +75,14 @@ ;; esac + case ${entropy_boot_file:=/boot/entropy} in + [Nn][Oo] | '') + ;; + *) + save_dev_random "${entropy_boot_file}" + ;; + esac + echo '.' } @@ -100,7 +115,33 @@ ;; *) dd if=/dev/random of=${entropy_file_confirmed} \ - bs=4096 count=1 2> /dev/null + bs=4096 count=1 2> /dev/null || + warn 'write failed (unwriteable file or full fs?)' + echo '.' + ;; + esac + umask ${oumask} + ;; + esac + case ${entropy_boot_file:=/boot/entropy} in + [Nn][Oo] | '') + ;; + *) + echo -n 'Writing early boot entropy file:' + rm -f ${entropy_boot_file} 2> /dev/null + oumask=`umask` + umask 077 + if touch ${entropy_boot_file} 2> /dev/null; then + entropy_boot_file_confirmed="${entropy_boot_file}" + fi + case ${entropy_boot_file_confirmed} in + '') + warn 'write failed (read-only fs?)' + ;; + *) + dd if=/dev/random of=${entropy_boot_file_confirmed} \ + bs=4096 count=1 2> /dev/null || + warn 'write failed (unwriteable file or full fs?)' echo '.' ;; esac Index: share/man/man4/random.4 =================================================================== --- share/man/man4/random.4 +++ share/man/man4/random.4 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2001-2013 Mark R V Murray. All rights reserved. +.\" Copyright (c) 2001-2015 Mark R V Murray. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -23,7 +23,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 12, 2013 +.Dd June 21, 2015 .Dt RANDOM 4 .Os .Sh NAME @@ -37,31 +37,18 @@ device returns an endless supply of random bytes when read. It also accepts and reads data -as any ordinary (and willing) file, -but discards data written to it. -The device will probe for -certain hardware entropy sources, -and use these in preference to the fallback, -which is a generator implemented in software. +as any ordinary file. .Pp -The software generator will start in an +The generator will start in an .Em unseeded state, and will block reads until -it is (re)seeded. +it is seeded for the first time. This may cause trouble at system boot when keys and the like are generated from /dev/random so steps should be taken to ensure a -reseed as soon as possible. -The -.Xr sysctl 8 -controlling the -.Em seeded -status (see below) may be used -if security is not an issue -or for convenience -during setup or development. +seedng as soon as possible. .Pp This initial seeding of random number generators @@ -94,92 +81,40 @@ .Pp which results in something like: .Bd -literal -offset indent -kern.random.adaptors: yarrow,dummy -kern.random.active_adaptor: yarrow -kern.random.yarrow.gengateinterval: 10 -kern.random.yarrow.bins: 10 -kern.random.yarrow.fastthresh: 96 -kern.random.yarrow.slowthresh: 128 -kern.random.yarrow.slowoverthresh: 2 -kern.random.sys.seeded: 1 -kern.random.sys.harvest.ethernet: 1 -kern.random.sys.harvest.point_to_point: 1 -kern.random.sys.harvest.interrupt: 1 -kern.random.sys.harvest.swi: 1 +kern.random.fortuna.minpoolsize: 64 +kern.random.harvest.mask_symbolic: [HIGH_PERFORMANCE], ... ,CACHED +kern.random.harvest.mask_bin: 00111111111 +kern.random.harvest.mask: 511 +kern.random.random_sources: 'Intel Secure Key RNG' .Ed .Pp Other than -.Dl kern.random.adaptors -all settings are read/write. -.Pp -The -.Va kern.random.sys.seeded -variable indicates whether or not the -.Nm -device is in an acceptably secure state -as a result of reseeding. -If set to 0, -the device will block (on read) -until the next reseed -as a result of entropy harvesting. -A reseed will set the value to 1 (non-blocking). -.Pp -The -.Va kern.random.sys.harvest.ethernet -variable is used to select LAN traffic as an entropy source. -A 0 (zero) value means that LAN traffic -is not considered as an entropy source. -Set the variable to 1 (one) -if you wish to use LAN traffic for entropy harvesting. -.Pp -The -.Va kern.random.sys.harvest.point_to_point -variable is used to select serial line traffic as an entropy source. -(Serial line traffic includes PPP, SLIP and all tun0 traffic.) -A 0 (zero) value means such traffic -is not considered as an entropy source. -Set the variable to 1 (one) -if you wish to use it for entropy harvesting. +.Dl kern.random.fortuna.minpoolsize +and +.Dl kern.random.harvest.mask +all settings are read-only. .Pp The -.Va kern.random.sys.harvest.interrupt -variable is used to select hardware interrupts +.Va kern.random.harvest.mask +bitmask is used to select +the possible entropy sources. +A 0 (zero) value means +the corresponding source +is not considered as an entropy source. -A 0 (zero) value means hardware interrupts -are not considered as an entropy source. -Set the variable to 1 (one) -if you wish to use them for entropy harvesting. -All hardware interrupt harvesting is set up by the -individual device drivers. -.Pp +Set the bit to 1 (one) +if you wish to use +that source. The -.Va kern.random.sys.harvest.swi -variable is used to select software interrupts -as an entropy source. -A 0 (zero) value means software interrupts -are not considered as an entropy source. -Set the variable to 1 (one) -if you wish to use them for entropy harvesting. -.Pp -The other variables are explained in the paper describing the -.Em Yarrow -algorithm at -.Pa http://www.schneier.com/yarrow.html . -.Pp -These variables are all limited -in terms of the values they may contain: -.Bl -tag -width "kern.random.yarrow.gengateinterval" -compact -offset indent -.It Va kern.random.yarrow.gengateinterval -.Bq 4..64 -.It Va kern.random.yarrow.bins -.Bq 2..16 -.It Va kern.random.yarrow.fastthresh -.Bq 64..256 -.It Va kern.random.yarrow.slowthresh -.Bq 64..256 -.It Va kern.random.yarrow.slowoverthresh -.Bq 1..5 -.El +.Va kern.random.harvest.mask_bin +and +.Va kern.random.harvest.mask_symbolic +sysctl +can be used confirm +that your choices are correct. +Note that disabled items +in the latter item +are listed in square brackets. .Pp Internal .Xr sysctl 3 @@ -308,23 +243,36 @@ .Xr RAND_add 3 , .Xr RAND_bytes 3 , .Xr random 3 , +.Xr random 9 , .Xr sysctl 8 +.Rs +.%A Ferguson +.%A Schneier +.%A Kohno +.%B Cryptography Engineering +.%I Wiley +.%O ISBN 978-0-470-47424-2 +.Re .Sh HISTORY A .Nm device appeared in .Fx 2.2 . -The early version was taken from Theodore Ts'o's entropy driver for Linux. The current software implementation, introduced in -.Fx 5.0 , -is a complete rewrite by +.Fx 11.0 , +is by .An Mark R V Murray , and is an implementation of the -.Em Yarrow -algorithm by Bruce Schneier, +.Em Fortuna +algorithm by Ferguson .Em et al . -Significant infrastructure work was done by Arthur Mesh. -.Pp -The author gratefully acknowledges -significant assistance from VIA Technologies, Inc. +It replaces the previous +.Em Yarrow +implementation, +introduced in +.Fx 5.0 . +The older +.Em Yarrow +algorithm remains available +as a compile-time fallback. Index: share/man/man5/rc.conf.5 =================================================================== --- share/man/man5/rc.conf.5 +++ share/man/man5/rc.conf.5 @@ -3995,27 +3995,11 @@ .Va jail_ Ns Ao Ar jname Ac Ns Va _sysvipc_allow .El .\" ----------------------------------------------------- -.It Va harvest_interrupt -.Pq Vt bool -Set to -.Dq Li YES -to use hardware interrupts as an entropy source. -Refer to -.Xr random 4 -for more information. -.It Va harvest_ethernet -.Pq Vt bool -Set to -.Dq Li YES -to use LAN traffic as an entropy source. -Refer to -.Xr random 4 -for more information. -.It Va harvest_p_to_p -.Pq Vt bool -Set to -.Dq Li YES -to use serial line traffic as an entropy source. +.It Va harvest_mask +.Pq Vt int +Set to a bit-mask +representing the entropy sources +you wish to harvest. Refer to .Xr random 4 for more information. @@ -4036,6 +4020,19 @@ This file should be located on the root file system to seed the .Xr random 4 device as early as possible in the boot process. +.It Va entropy_boot_file +.Pq Vt str +Set to +.Dq Li NO +to disable +very early caching entropy +through reboots. +Otherwise set to the filename +used to read +early reboot cached entropy. +This file should be located where +.Xr loader 8 +can read it. .It Va entropy_save_sz .Pq Vt int Size of the entropy cache files saved by Index: share/man/man9/random.9 =================================================================== --- share/man/man9/random.9 +++ share/man/man9/random.9 @@ -53,11 +53,12 @@ .Sh DESCRIPTION The .Fn random -function will by default produce a sequence of numbers that can be duplicated +function will by default produce +a sequence of numbers +that can be duplicated by calling .Fn srandom -with -.Ql 1 +with some constant as the .Fa seed . The @@ -67,19 +68,28 @@ value to get slightly more unpredictable numbers. It is important to remember that the .Fn random -function is entirely predictable, and is therefore not of use where -knowledge of the sequence of numbers may be of benefit to an attacker. +function is entirely predictable, +and is therefore not of use where +knowledge of the sequence of numbers +may be of benefit to an attacker. .Pp The .Fn arc4rand -function will return very good quality random numbers, slightly better -suited for security-related purposes. +function will return very good quality random numbers, +slightly better suited +for security-related purposes. The random numbers from .Fn arc4rand -are seeded from the entropy device if it is available. -Automatic reseeds happen after a certain timeinterval and after a -certain number of bytes have been delivered. -A forced reseed can be forced by passing a non-zero value in the +are seeded from the entropy device +if it is available. +Automatic reseeds happen +after a certain timeinterval +and after a certain number of bytes +have been delivered. +A forced reseed +can be forced +by passing a non-zero +value in the .Fa reseed argument. .Pp @@ -90,19 +100,19 @@ If the entropy device is not loaded, then the .Fa buffer -is filled with output generated by -.Fn random . +is ignored. The .Fa buffer is filled with no more than .Fa count bytes. -It is advised that +It is strongly advised that .Fn read_random -is not used; instead use +is not used; +instead use .Fn arc4rand .Pp -All the bits generated by +All the bits returned by .Fn random , .Fn arc4rand and @@ -120,32 +130,38 @@ .Sh RETURN VALUES The .Fn random -function -uses a non-linear additive feedback random number generator employing a -default table of size 31 long integers to return successive pseudo-random +function uses +a non-linear additive feedback random number generator +employing a default table +of size 31 +containing long integers +to return successive pseudo-random numbers in the range from 0 to .if t 2\u\s731\s10\d\(mi1. .if n (2**31)\(mi1. -The period of this random number generator is very large, approximately +The period of this random number generator +is very large, +approximately .if t 16\(mu(2\u\s731\s10\d\(mi1). .if n 16*((2**31)\(mi1). .Pp The .Fn arc4rand -function uses the RC4 algorithm to generate successive pseudo-random -bytes. +function uses the RC4 algorithm +to generate successive pseudo-random bytes. The .Fn arc4random -function -uses +function uses .Fn arc4rand -to generate pseudo-random numbers in the range from 0 to +to generate pseudo-random numbers +in the range from 0 to .if t 2\u\s732\s10\d\(mi1. .if n (2**32)\(mi1. .Pp The .Fn read_random -function returns the number of bytes placed in +function returns +the number of bytes placed in .Fa buffer . .Sh AUTHORS .An Dan Moschuk Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -528,14 +528,14 @@ crypto/des/des_setkey.c optional crypto | ipsec | netsmb crypto/rc4/rc4.c optional netgraph_mppc_encryption | kgssapi crypto/rijndael/rijndael-alg-fst.c optional crypto | geom_bde | \ - ipsec | random | wlan_ccmp -crypto/rijndael/rijndael-api-fst.c optional geom_bde | random + ipsec | random random_yarrow | random !random_yarrow !random_dummy | wlan_ccmp +crypto/rijndael/rijndael-api-fst.c optional geom_bde | random random_yarrow | random !random_yarrow !random_dummy crypto/rijndael/rijndael-api.c optional crypto | ipsec | wlan_ccmp crypto/sha1.c optional carp | crypto | ipsec | \ netgraph_mppc_encryption | sctp -crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random | \ +crypto/sha2/sha2.c optional crypto | geom_bde | ipsec | random random_yarrow | random !random_yarrow !random_dummy | \ sctp | zfs -crypto/sha2/sha256c.c optional crypto | geom_bde | ipsec | random | \ +crypto/sha2/sha256c.c optional crypto | geom_bde | ipsec | random random_yarrow | random !random_yarrow !random_dummy | \ sctp | zfs crypto/siphash/siphash.c optional inet | inet6 crypto/siphash/siphash_test.c optional inet | inet6 @@ -2139,15 +2139,12 @@ compile-with "${NORMAL_FW}" \ no-obj no-implicit-rule \ clean "rt2860.fw" -dev/random/randomdev.c standard -dev/random/random_adaptors.c standard -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/randomdev_none.c optional !random +dev/random/randomdev.c optional random +dev/random/random_harvestq.c optional random random_yarrow | random !random_dummy +dev/random/yarrow.c optional random random_yarrow +dev/random/fortuna.c optional random !random_yarrow !random_dummy +dev/random/hash.c optional random random_yarrow | random !random_dummy dev/rc/rc.c optional rc dev/re/if_re.c optional re dev/rl/if_rl.c optional rl pci Index: sys/conf/options =================================================================== --- sys/conf/options +++ sys/conf/options @@ -938,9 +938,16 @@ RCTL opt_global.h # Random number generator(s) +# The DEBUG option is in global.h as the random harvesting +# puts probes all over the place, and it makes little sense +# to pollute these headers with an extra include. +# the DUMMY option is in global.h because it is used to +# turn off harvesting all over the kernel. +RANDOM_DEBUG opt_global.h +# Which CSPRNG hashes we get. +# These are mutually exclusive. With neither, Fortuna is selected. +RANDOM_DUMMY opt_global.h RANDOM_YARROW opt_random.h -RANDOM_FORTUNA opt_random.h -RANDOM_DEBUG opt_random.h # Intel em(4) driver EM_MULTIQUEUE opt_em.h Index: sys/dev/glxsb/glxsb.c =================================================================== --- sys/dev/glxsb/glxsb.c +++ sys/dev/glxsb/glxsb.c @@ -476,7 +476,8 @@ if (status & SB_RNS_TRNG_VALID) { value = bus_read_4(sc->sc_sr, SB_RANDOM_NUM); /* feed with one uint32 */ - random_harvest(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB); + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB); } callout_reset(&sc->sc_rngco, sc->sc_rnghz, glxsb_rnd, sc); Index: sys/dev/hifn/hifn7751.c =================================================================== --- sys/dev/hifn/hifn7751.c +++ sys/dev/hifn/hifn7751.c @@ -258,7 +258,8 @@ static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_HIFN); + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_HIFN); } static u_int Index: sys/dev/random/build.sh =================================================================== --- sys/dev/random/build.sh +++ sys/dev/random/build.sh @@ -1,3 +1,29 @@ +#!/bin/sh +#- +# Copyright (c) 2013-2015 Mark R V Murray +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer +# in this position and unchanged. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# # $FreeBSD$ # # Basic script to build crude unit tests. @@ -11,6 +37,7 @@ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ ../../crypto/sha2/sha256c.c \ + -lz \ -o yunit_test cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \ -I../.. -lstdthreads -Wall \ @@ -21,4 +48,5 @@ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ ../../crypto/sha2/sha256c.c \ + -lz \ -o funit_test Index: sys/dev/random/dummy_rng.c =================================================================== --- sys/dev/random/dummy_rng.c +++ /dev/null @@ -1,111 +0,0 @@ -/*- - * 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 - -static int -dummy_random_zero(void) -{ - - return (0); -} - -static void -dummy_random(void) -{ -} - -/* ARGSUSED */ -static void -dummy_random_init(void) -{ - -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif - - randomdev_init_reader(dummy_random_read_phony); -} - -/* This is used only by the internal read_random(9) call, and then only - * if no entropy processor is loaded. - * - * Make a token effort to provide _some_ kind of output. No warranty of - * the quality of this output is made, mainly because its lousy. - * - * This is only used by the internal read_random(9) call when no other - * adaptor is active. - * - * It has external scope due to the way things work in - * randomdev_[de]init_reader() that the rest of the world doesn't need to - * know about. - * - * Caveat Emptor. - */ -void -dummy_random_read_phony(uint8_t *buf, u_int count) -{ - /* If no entropy device is loaded, don't spam the console with warnings */ - u_long randval; - size_t size, i; - - /* srandom() is called in kern/init_main.c:proc0_post() */ - - /* Fill buf[] with random(9) output */ - for (i = 0; i < count; i += sizeof(randval)) { - randval = random(); - size = MIN(count - i, sizeof(randval)); - memcpy(buf + i, &randval, (size_t)size); - } -} - -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, -}; Index: sys/dev/random/fortuna.h =================================================================== --- sys/dev/random/fortuna.h +++ sys/dev/random/fortuna.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,18 +27,21 @@ */ #ifndef SYS_DEV_RANDOM_FORTUNA_H_INCLUDED -#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED +#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED #ifdef _KERNEL typedef struct mtx mtx_t; +#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&fortuna_state.fs_mtx, "reseed mutex", NULL, MTX_DEF) +#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_LOCK(x) mtx_lock(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) mtx_assert(&fortuna_state.fs_mtx, MA_OWNED) +#else +#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&fortuna_state.fs_mtx, mtx_plain) +#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_LOCK(x) mtx_lock(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&fortuna_state.fs_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) #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 +#endif /* SYS_DEV_RANDOM_FORTUNA_H_INCLUDED */ Index: sys/dev/random/fortuna.c =================================================================== --- sys/dev/random/fortuna.c +++ sys/dev/random/fortuna.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013-2014 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,25 +25,24 @@ * */ -/* This implementation of Fortuna is based on the descriptions found in - * ISBN 0-471-22357-3 "Practical Cryptography" by Ferguson and Schneier - * ("F&S"). - * - * The above book is superseded by ISBN 978-0-470-47424-2 "Cryptography - * Engineering" by Ferguson, Schneier and Kohno ("FS&K"). The code has - * not yet fully caught up with FS&K. +/* + * This implementation of Fortuna is based on the descriptions found in + * ISBN 978-0-470-47424-2 "Cryptography Engineering" by Ferguson, Schneier + * and Kohno ("FS&K"). */ #include __FBSDID("$FreeBSD$"); -#ifdef _KERNEL -#include "opt_random.h" +#include +#ifdef _KERNEL #include #include +#include #include #include +#include #include #include #include @@ -56,13 +55,10 @@ #include #include -#include #include #include #include #else /* !_KERNEL */ -#include -#include #include #include #include @@ -79,351 +75,405 @@ #include #endif /* _KERNEL */ -#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) -#define RANDOM_YARROW -#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) -#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" -#endif - -#if defined(RANDOM_FORTUNA) - -#define NPOOLS 32 -#define MINPOOLSIZE 64 -#define DEFPOOLSIZE 256 -#define MAXPOOLSIZE 65536 +/* Defined in FS&K */ +#define RANDOM_FORTUNA_NPOOLS 32 /* The number of accumulation pools */ +#define RANDOM_FORTUNA_DEFPOOLSIZE 64 /* The default pool size/length for a (re)seed */ +#define RANDOM_FORTUNA_MAX_READ (1 << 20) /* Max bytes in a single read */ -/* 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. +/* + * The allowable range of RANDOM_FORTUNA_DEFPOOLSIZE. The default value is above. + * Making RANDOM_FORTUNA_DEFPOOLSIZE too large will mean a long time between reseeds, + * and too small may compromise initial security but get faster reseeds. + */ +#define RANDOM_FORTUNA_MINPOOLSIZE 16 +#define RANDOM_FORTUNA_MAXPOOLSIZE UINT_MAX +CTASSERT(RANDOM_FORTUNA_MINPOOLSIZE <= RANDOM_FORTUNA_DEFPOOLSIZE); +CTASSERT(RANDOM_FORTUNA_DEFPOOLSIZE <= RANDOM_FORTUNA_MAXPOOLSIZE); + +/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t)); +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); + +/* + * This is the beastie that needs protecting. It contains all of the + * state that we are excited about. 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; - + struct fs_pool { /* P_i */ + u_int fsp_length; /* Only the first one is used by Fortuna */ + struct randomdev_hash fsp_hash; + } fs_pool[RANDOM_FORTUNA_NPOOLS]; + u_int fs_reseedcount; /* ReseedCnt */ + uint128_t fs_counter; /* C */ + struct randomdev_key fs_key; /* K */ + u_int fs_minpoolsize; /* Extras */ /* Extras for the OS */ - #ifdef _KERNEL /* For use when 'pacing' the reseeds */ - sbintime_t lasttime; + sbintime_t fs_lasttime; #endif + /* Reseed lock */ + mtx_t fs_mtx; } fortuna_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(fs_minpoolsize, RANDOM_FORTUNA_MINPOOLSIZE, RANDOM_FORTUNA_MAXPOOLSIZE); +#else +static uint8_t zero_region[RANDOM_ZERO_BLOCKSIZE]; +#endif -static struct fortuna_start_cache { - uint8_t junk[PAGE_SIZE]; - size_t length; - struct randomdev_hash hash; -} fortuna_start_cache; +static void random_fortuna_pre_read(void); +static void random_fortuna_read(uint8_t *, u_int); +static void random_fortuna_post_read(void); +static void random_fortuna_write(uint8_t *, u_int); +static void random_fortuna_reseed(void); +static int random_fortuna_seeded(void); +static void random_fortuna_process_event(struct harvest_event *); #ifdef _KERNEL -static struct sysctl_ctx_list random_clist; -RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE); +/* Interface to Adaptors system */ +struct random_algorithm random_alg_context = { + .ra_ident = "Fortuna", + .ra_pre_read = random_fortuna_pre_read, + .ra_read = random_fortuna_read, + .ra_post_read = random_fortuna_post_read, + .ra_write = random_fortuna_write, + .ra_reseed = random_fortuna_reseed, + .ra_seeded = random_fortuna_seeded, + .ra_event_processor = random_fortuna_process_event, + .ra_poolcount = RANDOM_FORTUNA_NPOOLS, +}; #endif -void -random_fortuna_init_alg(void) +/* ARGSUSED */ +static void +random_fortuna_init_alg(void *unused __unused) { 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 + RANDOM_RESEED_INIT_LOCK(); + /* + * Fortuna parameters. Do not adjust these unless you have * have a very good clue about what they do! */ + fortuna_state.fs_minpoolsize = RANDOM_FORTUNA_DEFPOOLSIZE; +#ifdef _KERNEL + fortuna_state.fs_lasttime = 0; 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; + "minpoolsize", CTLTYPE_UINT | CTLFLAG_RWTUN, + &fortuna_state.fs_minpoolsize, RANDOM_FORTUNA_DEFPOOLSIZE, + random_check_uint_fs_minpoolsize, "IU", + "Minimum pool size necessary to cause a reseed"); + KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0 at startup")); #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; + /*- + * FS&K - InitializePRNG() + * - P_i = \epsilon + * - ReseedCNT = 0 + */ + for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) { + randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash); + fortuna_state.fs_pool[i].fsp_length = 0; } - - /* 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)); + fortuna_state.fs_reseedcount = 0; + /*- + * FS&K - InitializeGenerator() + * - C = 0 + * - K = 0 + */ + fortuna_state.fs_counter = UINT128_ZERO; + explicit_bzero(&fortuna_state.fs_key, sizeof(fortuna_state.fs_key)); } +#ifdef _KERNEL +SYSINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_init_alg, NULL); +#endif -void -random_fortuna_deinit_alg(void) +/* ARGSUSED */ +static void +random_fortuna_deinit_alg(void *unused __unused) { - mtx_destroy(&random_reseed_mtx); - memset(&fortuna_state, 0, sizeof(fortuna_state)); + RANDOM_RESEED_DEINIT_LOCK(); + explicit_bzero(&fortuna_state, sizeof(fortuna_state)); +#ifdef _KERNEL + sysctl_ctx_free(&random_clist); +#endif } +#ifdef _KERNEL +SYSUNINIT(random_fortuna, SI_SUB_RANDOM, SI_ORDER_THIRD, random_fortuna_deinit_alg, NULL); +#endif -/* F&S - AddRandomEvent() */ -/* Process a single stochastic event off the harvest queue */ +/*- + * FS&K - 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 + RANDOM_RESEED_LOCK(); + /*- + * FS&K - P_i = P_i| + * Accumulate the event into the appropriate pool + * where each event carries the destination information. + * + * The hash_init() and hash_finish() calls are done in + * random_fortuna_pre_read(). + * + * We must be locked against pool state modification which can happen + * during accumulation/reseeding and reading/regating. + */ + pl = event->he_destination % RANDOM_FORTUNA_NPOOLS; + randomdev_hash_iterate(&fortuna_state.fs_pool[pl].fsp_hash, event, sizeof(*event)); + /*- + * Don't wrap the length. Doing the the hard way so as not to wrap at MAXUINT. + * This is a "saturating" add. + * XXX: FIX!!: We don't actually need lengths for anything but fs_pool[0], + * but it's been useful debugging to see them all. */ - /* F&S - P_i = P_i| */ - /* 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); + if (RANDOM_FORTUNA_MAXPOOLSIZE - fortuna_state.fs_pool[pl].fsp_length > event->he_size) + fortuna_state.fs_pool[pl].fsp_length += event->he_size; + else + fortuna_state.fs_pool[pl].fsp_length = RANDOM_FORTUNA_MAXPOOLSIZE; + explicit_bzero(event, sizeof(*event)); + RANDOM_RESEED_UNLOCK(); +} + +/*- + * Process a block of data suspected to be slightly stochastic. + * Do this by breaking it up and inserting the pieces as if + * they were separate events. + */ +static void +random_fortuna_process_buffer(uint32_t *buf, u_int wordcount) +{ + static struct harvest_event event; + static u_int destination = 0; + int i; + + for (i = 0; i < wordcount; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) { + event.he_somecounter = (uint32_t)get_cyclecount(); + event.he_size = sizeof(event.he_entropy); + event.he_bits = event.he_size/8; + event.he_source = RANDOM_CACHED; + event.he_destination = destination++; /* Harmless cheating */ + memcpy(event.he_entropy, buf + i, sizeof(event.he_entropy)); + random_fortuna_process_event(&event); + } } -/* F&S - Reseed() */ -/* Reseed Mutex is held */ +/*- + * FS&K - Reseed() + * This introduces new key material into the output generator. + * Additionaly it increments the output generator's counter + * variable C. When C > 0, the output generator is seeded and + * will deliver output. + * The entropy_data buffer passed is a very specific size; the + * product of RANDOM_FORTUNA_NPOOLS and RANDOM_KEYSIZE. + */ static void -reseed(uint8_t *junk, u_int length) +random_fortuna_reseed_internal(uint32_t *entropy_data, u_int blockcount) { struct randomdev_hash context; - uint8_t hash[KEYSIZE]; + uint8_t hash[RANDOM_KEYSIZE]; - KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0")); -#ifdef _KERNEL - mtx_assert(&random_reseed_mtx, MA_OWNED); -#endif - - /* FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) */ + RANDOM_RESEED_ASSERT_LOCK_OWNED(); + /*- + * FS&K - K = Hd(K|s) where Hd(m) is H(H(0^512|m)) + * - C = C + 1 + */ randomdev_hash_init(&context); - randomdev_hash_iterate(&context, zero_region, 512/8); - randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key)); - randomdev_hash_iterate(&context, junk, length); + randomdev_hash_iterate(&context, zero_region, RANDOM_ZERO_BLOCKSIZE); + randomdev_hash_iterate(&context, &fortuna_state.fs_key, sizeof(fortuna_state.fs_key)); + randomdev_hash_iterate(&context, entropy_data, RANDOM_KEYSIZE*blockcount); randomdev_hash_finish(&context, hash); randomdev_hash_init(&context); - randomdev_hash_iterate(&context, hash, KEYSIZE); + randomdev_hash_iterate(&context, hash, RANDOM_KEYSIZE); randomdev_hash_finish(&context, hash); - randomdev_encrypt_init(&fortuna_state.key, hash); - memset(hash, 0, sizeof(hash)); - - /* Unblock the device if it was blocked due to being unseeded */ - if (uint128_is_zero(fortuna_state.counter.whole)) - random_adaptor_unblock(); - /* FS&K - C = C + 1 */ - uint128_increment(&fortuna_state.counter.whole); + randomdev_encrypt_init(&fortuna_state.fs_key, hash); + explicit_bzero(hash, sizeof(hash)); + /* Unblock the device if this is the first time we are reseeding. */ + if (uint128_is_zero(fortuna_state.fs_counter)) + randomdev_unblock(); + uint128_increment(&fortuna_state.fs_counter); } -/* F&S - GenerateBlocks() */ -/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +/*- + * FS&K - GenerateBlocks() + * Generate a number of complete blocks of random output. + */ static __inline void random_fortuna_genblocks(uint8_t *buf, u_int blockcount) { u_int i; - for (i = 0u; i < blockcount; i++) { - /* 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); + RANDOM_RESEED_ASSERT_LOCK_OWNED(); + for (i = 0; i < blockcount; i++) { + /*- + * FS&K - r = r|E(K,C) + * - C = C + 1 + */ + randomdev_encrypt(&fortuna_state.fs_key, &fortuna_state.fs_counter, buf, RANDOM_BLOCKSIZE); + buf += RANDOM_BLOCKSIZE; + uint128_increment(&fortuna_state.fs_counter); } } -/* F&S - PseudoRandomData() */ -/* Reseed Mutex is held, and buf points to a whole number of blocks. */ +/*- + * FS&K - PseudoRandomData() + * This generates no more than 2^20 bytes of data, and cleans up its + * internal state when finished. It is assumed that a whole number of + * blocks are available for writing; any excess generated will be + * ignored. + */ static __inline void random_fortuna_genrandom(uint8_t *buf, u_int bytecount) { - static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)]; + static uint8_t temp[RANDOM_BLOCKSIZE*(RANDOM_KEYS_PER_BLOCK)]; u_int blockcount; - /* F&S - assert(n < 2^20) */ - KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount)); - - /* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */ - blockcount = bytecount / BLOCKSIZE; + RANDOM_RESEED_ASSERT_LOCK_OWNED(); + /*- + * FS&K - assert(n < 2^20 (== 1 MB) + * - r = first-n-bytes(GenerateBlocks(ceil(n/16))) + * - K = GenerateBlocks(2) + */ + KASSERT((bytecount <= RANDOM_FORTUNA_MAX_READ), ("invalid single read request to Fortuna of %d bytes", bytecount)); + blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE; random_fortuna_genblocks(buf, blockcount); - /* TODO: FIX! remove memcpy()! */ - if (bytecount % BLOCKSIZE > 0) { - random_fortuna_genblocks(temp, 1); - memcpy(buf + (blockcount * BLOCKSIZE), temp, bytecount % BLOCKSIZE); - } - - /* F&S - K = GenerateBlocks(2) */ - random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE); - randomdev_encrypt_init(&fortuna_state.key, temp); - memset(temp, 0, sizeof(temp)); + random_fortuna_genblocks(temp, RANDOM_KEYS_PER_BLOCK); + randomdev_encrypt_init(&fortuna_state.fs_key, temp); + explicit_bzero(temp, sizeof(temp)); } -/* F&S - RandomData() */ -/* Used to return processed entropy from the PRNG */ -/* The argument buf points to a whole number of blocks. */ +/*- + * FS&K - RandomData() + * Used to return processed entropy from the PRNG. + * There is a pre_read and a post_read required to be present + * (but they can be null functions) in order to allow specific + * actions at the begin or the end of a read. Fortuna does its + * reseeding in the _pre_read() part, and _post_read() is not + * used. + */ void -random_fortuna_read(uint8_t *buf, u_int bytecount) +random_fortuna_pre_read(void) { #ifdef _KERNEL - sbintime_t thistime; + sbintime_t now; #endif struct randomdev_hash context; - uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE]; - int i; - u_int seedlength; + uint32_t s[RANDOM_FORTUNA_NPOOLS*RANDOM_KEYSIZE_WORDS]; + uint8_t temp[RANDOM_KEYSIZE]; + u_int i; - /* We must be locked for all this as plenty of state gets messed with */ - mtx_lock(&random_reseed_mtx); + KASSERT(fortuna_state.fs_minpoolsize > 0, ("random: Fortuna threshold must be > 0")); +#ifdef _KERNEL + /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */ + now = getsbinuptime(); +#endif + RANDOM_RESEED_LOCK(); - /* if buf == NULL and bytecount == 0 then this is the pre-read. */ - /* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */ - if (buf == NULL) { - if (bytecount == 0) { - if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize + if (fortuna_state.fs_pool[0].fsp_length >= fortuna_state.fs_minpoolsize #ifdef _KERNEL - /* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */ - && ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10) + /* FS&K - Use 'getsbinuptime()' to prevent reseed-spamming. */ + && (now - fortuna_state.fs_lasttime > hz/10) #endif - ) { + ) { #ifdef _KERNEL - fortuna_state.lasttime = thistime; + fortuna_state.fs_lasttime = now; #endif - seedlength = 0U; - /* F&S - ReseedCNT = ReseedCNT + 1 */ - fortuna_state.reseedcount++; - /* s = \epsilon by default */ - for (i = 0; i < NPOOLS; i++) { - /* F&S - if Divides(ReseedCnt, 2^i) ... */ - if ((fortuna_state.reseedcount % (1 << i)) == 0U) { - seedlength += KEYSIZE; - /* F&S - temp = (P_i) */ - randomdev_hash_finish(&fortuna_state.pool[i].hash, temp); - /* F&S - P_i = \epsilon */ - randomdev_hash_init(&fortuna_state.pool[i].hash); - fortuna_state.pool[i].length = 0U; - /* F&S - s = s|H(temp) */ - randomdev_hash_init(&context); - randomdev_hash_iterate(&context, temp, KEYSIZE); - randomdev_hash_finish(&context, s + i*KEYSIZE); - } - else - break; - } + /* FS&K - ReseedCNT = ReseedCNT + 1 */ + fortuna_state.fs_reseedcount++; + /* s = \epsilon at start */ + for (i = 0; i < RANDOM_FORTUNA_NPOOLS; i++) { + /* FS&K - if Divides(ReseedCnt, 2^i) ... */ + if ((fortuna_state.fs_reseedcount % (1 << i)) == 0) { + /*- + * FS&K - temp = (P_i) + * - P_i = \epsilon + * - s = s|H(temp) + */ + randomdev_hash_finish(&fortuna_state.fs_pool[i].fsp_hash, temp); + randomdev_hash_init(&fortuna_state.fs_pool[i].fsp_hash); + fortuna_state.fs_pool[i].fsp_length = 0; + randomdev_hash_init(&context); + randomdev_hash_iterate(&context, temp, RANDOM_KEYSIZE); + randomdev_hash_finish(&context, s + i*RANDOM_KEYSIZE_WORDS); + } else + break; + } #ifdef RANDOM_DEBUG - printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount); - for (i = 0; i < NPOOLS; i++) - printf(" %d", fortuna_state.pool[i].length); - printf("\n"); -#endif - /* F&S */ - reseed(s, seedlength); - - /* Clean up */ - memset(s, 0, seedlength); - seedlength = 0U; - memset(temp, 0, sizeof(temp)); - memset(&context, 0, sizeof(context)); - } + { + u_int j; + + printf("random: reseedcount [%d]", fortuna_state.fs_reseedcount); + for (j = 0; j < RANDOM_FORTUNA_NPOOLS; j++) + printf(" %X", fortuna_state.fs_pool[j].fsp_length); + printf("\n"); } +#endif + /* FS&K */ + random_fortuna_reseed_internal(s, i < RANDOM_FORTUNA_NPOOLS ? i + 1 : RANDOM_FORTUNA_NPOOLS); + /* Clean up and secure */ + explicit_bzero(s, sizeof(s)); + explicit_bzero(temp, sizeof(temp)); + explicit_bzero(&context, sizeof(context)); } - /* if buf != NULL do a regular read. */ - else - random_fortuna_genrandom(buf, bytecount); - - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK(); } -/* Internal function to hand external entropy to the PRNG */ +/*- + * Main read from Fortuna. + * The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size. + * Lots of code presumes this for efficiency, both here and in other + * routines. You are NOT allowed to break this! + */ void -random_fortuna_write(uint8_t *buf, u_int count) +random_fortuna_read(uint8_t *buf, u_int bytecount) { - 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); + RANDOM_RESEED_LOCK(); + random_fortuna_genrandom(buf, bytecount); + RANDOM_RESEED_UNLOCK(); +} - randomdev_hash_init(&fortuna_start_cache.hash); +void +random_fortuna_post_read(void) +{ - reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length)); - memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk)); + /* CWOT */ +} - mtx_unlock(&random_reseed_mtx); +/* Internal function to hand external entropy to the PRNG. */ +void +random_fortuna_write(uint8_t *buf, u_int count) +{ + struct randomdev_hash hash; + uint32_t entropy_data[RANDOM_KEYSIZE_WORDS], timestamp; + + /* Extra timing here is helpful to scrape scheduler timing entropy */ + randomdev_hash_init(&hash); + timestamp = (uint32_t)get_cyclecount(); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_iterate(&hash, buf, count); + timestamp = (uint32_t)get_cyclecount(); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_finish(&hash, entropy_data); + explicit_bzero(&hash, sizeof(hash)); + random_fortuna_process_buffer(entropy_data, sizeof(entropy_data)/sizeof(entropy_data[0])); + explicit_bzero(entropy_data, sizeof(entropy_data)); } void @@ -437,7 +487,5 @@ random_fortuna_seeded(void) { - return (!uint128_is_zero(fortuna_state.counter.whole)); + return (!uint128_is_zero(fortuna_state.fs_counter)); } - -#endif /* RANDOM_FORTUNA */ Index: sys/dev/random/hash.h =================================================================== --- sys/dev/random/hash.h +++ sys/dev/random/hash.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,10 +27,14 @@ */ #ifndef SYS_DEV_RANDOM_HASH_H_INCLUDED -#define SYS_DEV_RANDOM_HASH_H_INCLUDED +#define SYS_DEV_RANDOM_HASH_H_INCLUDED -#define KEYSIZE 32 /* (in bytes) == 256 bits */ -#define BLOCKSIZE 16 /* (in bytes) == 128 bits */ +#define RANDOM_KEYSIZE 32 /* (in bytes) == 256 bits */ +#define RANDOM_KEYSIZE_WORDS (RANDOM_KEYSIZE/sizeof(uint32_t)) +#define RANDOM_BLOCKSIZE 16 /* (in bytes) == 128 bits */ +#define RANDOM_BLOCKSIZE_WORDS (RANDOM_BLOCKSIZE/sizeof(uint32_t)) +#define RANDOM_KEYS_PER_BLOCK (RANDOM_KEYSIZE/RANDOM_BLOCKSIZE) +#define RANDOM_ZERO_BLOCKSIZE 64 /* (in bytes) == 512 zero bits */ struct randomdev_hash { /* Big! Make static! */ SHA256_CTX sha; @@ -47,4 +51,4 @@ void randomdev_encrypt_init(struct randomdev_key *, const void *); void randomdev_encrypt(struct randomdev_key *context, const void *, void *, u_int); -#endif +#endif /* SYS_DEV_RANDOM_HASH_H_INCLUDED */ Index: sys/dev/random/hash.c =================================================================== --- sys/dev/random/hash.c +++ sys/dev/random/hash.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,8 +47,8 @@ #include -/* This code presumes that KEYSIZE is twice as large as BLOCKSIZE */ -CTASSERT(KEYSIZE == 2*BLOCKSIZE); +/* This code presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); /* Initialise the hash */ void @@ -67,7 +67,7 @@ } /* Conclude by returning the hash in the supplied <*buf> which must be - * KEYSIZE bytes long. + * RANDOM_KEYSIZE bytes long. */ void randomdev_hash_finish(struct randomdev_hash *context, void *buf) @@ -77,20 +77,20 @@ } /* Initialise the encryption routine by setting up the key schedule - * from the supplied <*data> which must be KEYSIZE bytes of binary - * data. Use CBC mode for better avalanche. + * from the supplied <*data> which must be RANDOM_KEYSIZE bytes of binary + * data. */ void randomdev_encrypt_init(struct randomdev_key *context, const void *data) { - rijndael_cipherInit(&context->cipher, MODE_CBC, NULL); - rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data); + rijndael_cipherInit(&context->cipher, MODE_ECB, NULL); + rijndael_makeKey(&context->key, DIR_ENCRYPT, RANDOM_KEYSIZE*8, data); } /* Encrypt the supplied data using the key schedule preset in the context. * bytes are encrypted from <*d_in> to <*d_out>. must be - * a multiple of BLOCKSIZE. + * a multiple of RANDOM_BLOCKSIZE. */ void randomdev_encrypt(struct randomdev_key *context, const void *d_in, void *d_out, u_int length) Index: sys/dev/random/ivy.c =================================================================== --- sys/dev/random/ivy.c +++ sys/dev/random/ivy.c @@ -46,18 +46,15 @@ #include #include -#include -#include -#include #define RETRY_COUNT 10 static u_int random_ivy_read(void *, u_int); -static struct live_entropy_source random_ivy = { - .les_ident = "Intel Secure Key RNG", - .les_source = RANDOM_PURE_RDRAND, - .les_read = random_ivy_read +static struct random_source random_ivy = { + .rs_ident = "Intel Secure Key RNG", + .rs_source = RANDOM_PURE_RDRAND, + .rs_read = random_ivy_read }; static inline int @@ -108,14 +105,14 @@ 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); + random_source_register(&random_ivy); + printf("random: fast provider: \"%s\"\n", random_ivy.rs_ident); } break; case MOD_UNLOAD: if (cpu_feature2 & CPUID2_RDRAND) - live_entropy_source_deregister(&random_ivy); + random_source_deregister(&random_ivy); break; case MOD_SHUTDOWN: Index: sys/dev/random/live_entropy_sources.h =================================================================== --- sys/dev/random/live_entropy_sources.h +++ /dev/null @@ -1,59 +0,0 @@ -/*- - * 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_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_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 */ Index: sys/dev/random/live_entropy_sources.c =================================================================== --- sys/dev/random/live_entropy_sources.c +++ /dev/null @@ -1,190 +0,0 @@ -/*- - * 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 - -#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 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(*lles), 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! - */ -/* 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, 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++) { - /* 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 */ - harvest_process_event(&event); - } - - } - - sx_sunlock(&les_lock); -} - -void -live_entropy_sources_init(void) -{ - - 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"); -} - -void -live_entropy_sources_deinit(void) -{ - - sx_destroy(&les_lock); -} Index: sys/dev/random/nehemiah.c =================================================================== --- sys/dev/random/nehemiah.c +++ sys/dev/random/nehemiah.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * Copyright (c) 2013 David E. O'Brien * All rights reserved. * @@ -44,24 +44,17 @@ #include #include -#include -#include -#include static void random_nehemiah_init(void); static void random_nehemiah_deinit(void); static u_int random_nehemiah_read(void *, u_int); -static struct live_entropy_source random_nehemiah = { - .les_ident = "VIA Nehemiah Padlock RNG", - .les_source = RANDOM_PURE_NEHEMIAH, - .les_read = random_nehemiah_read +static struct random_source random_nehemiah = { + .rs_ident = "VIA Nehemiah Padlock RNG", + .rs_source = RANDOM_PURE_NEHEMIAH, + .rs_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 */ @@ -131,8 +124,8 @@ 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_source_register(&random_nehemiah); + printf("random: fast provider: \"%s\"\n", random_nehemiah.rs_ident); random_nehemiah_init(); } break; @@ -140,7 +133,7 @@ case MOD_UNLOAD: if (via_feature_rng & VIA_HAS_RNG) random_nehemiah_deinit(); - live_entropy_source_deregister(&random_nehemiah); + random_source_deregister(&random_nehemiah); break; case MOD_SHUTDOWN: Index: sys/dev/random/random_adaptors.h =================================================================== --- sys/dev/random/random_adaptors.h +++ /dev/null @@ -1,74 +0,0 @@ -/*- - * 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_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) 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_adaptors_init(void); -void random_adaptors_deinit(void); - -void random_adaptor_register(const char *, struct random_adaptor *); -void random_adaptor_deregister(const char *); - -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 *); - -int random_adaptor_read_rate(void); -void random_adaptor_unblock(void); - -#endif /* SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED */ Index: sys/dev/random/random_adaptors.c =================================================================== --- sys/dev/random/random_adaptors.c +++ /dev/null @@ -1,483 +0,0 @@ -/*- - * Copyright (c) 2013 Mark R V Murray - * Copyright (c) 2013 Arthur Mesh - * Copyright (c) 2013 David E. O'Brien - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer - * in this position and unchanged. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include "opt_random.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -/* The random_adaptors_lock protects random_adaptors_list and friends and random_adaptor. - * We need a sleepable lock for uiomove/block/poll/sbuf/sysctl. - */ -static struct sx random_adaptors_lock; -LIST_HEAD(adaptors_head, random_adaptors); -static struct adaptors_head random_adaptors_list = LIST_HEAD_INITIALIZER(random_adaptors_list); -static struct random_adaptor *random_adaptor = NULL; /* Currently active adaptor */ -/* End of data items requiring random_adaptors_lock protection */ - -/* The random_readrate_mtx mutex protects the read-rate estimator. - */ -static struct mtx random_read_rate_mtx; -static int random_adaptor_read_rate_cache; -/* End of data items requiring random_readrate_mtx mutex protection */ - -static struct selinfo rsel; - -/* Utility routine to change active adaptor when the random_adaptors_list - * gets modified. - * - * Walk a list of registered random(4) adaptors and pick either a requested - * one or the highest priority one, whichever comes first. Panic on failure - * as the fallback must always be the "dummy" adaptor. - */ -static void -random_adaptor_choose(void) -{ - char rngs[128], *token, *cp; - struct random_adaptors *rra, *rrai; - struct random_adaptor *random_adaptor_previous; - int primax; - - /* We are going to be messing with random_adaptor. - * Exclusive lock is mandatory. - */ - sx_assert(&random_adaptors_lock, SA_XLOCKED); - - random_adaptor_previous = random_adaptor; - - random_adaptor = NULL; - if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) { - cp = rngs; - - /* XXX: FIX!! (DES): - * - fetch tunable once, at boot - * - make sysctl r/w - * - when fetching tunable or processing a sysctl - * write, parse into list of strings so we don't - * have to do it here again and again - * - sysctl read should return a reconstructed string - */ - while ((token = strsep(&cp, ",")) != NULL) { - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) - if (strcmp(rra->rra_name, token) == 0) { - random_adaptor = rra->rra_ra; - break; - } - if (random_adaptor != NULL) { - printf("random: selecting requested adaptor <%s>\n", - random_adaptor->ra_ident); - break; - } - else - printf("random: requested adaptor <%s> not available\n", - token); - } - } - - primax = 0; - if (random_adaptor == NULL) { - /* - * Fall back to the highest priority item on the available - * RNG list. - */ - LIST_FOREACH(rrai, &random_adaptors_list, rra_entries) { - if (rrai->rra_ra->ra_priority >= primax) { - random_adaptor = rrai->rra_ra; - primax = rrai->rra_ra->ra_priority; - } - } - if (random_adaptor != NULL) - printf("random: selecting highest priority adaptor <%s>\n", - random_adaptor->ra_ident); - } - - KASSERT(random_adaptor != NULL, ("adaptor not found")); - - /* If we are changing adaptors, deinit the old and init the new. */ - if (random_adaptor != random_adaptor_previous) { -#ifdef RANDOM_DEBUG - printf("random: %s - changing from %s to %s\n", __func__, - (random_adaptor_previous == NULL ? "NULL" : random_adaptor_previous->ra_ident), - random_adaptor->ra_ident); -#endif - if (random_adaptor_previous != NULL) { - randomdev_deinit_reader(); - (random_adaptor_previous->ra_deinit)(); - } - (random_adaptor->ra_init)(); - } - - randomdev_init_reader(random_adaptor->ra_read); -} - - -/* XXX: FIX!! Make sure we are not inserting a duplicate */ -void -random_adaptor_register(const char *name, struct random_adaptor *ra) -{ - struct random_adaptors *rra; - - KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__)); - - rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK); - rra->rra_name = name; - rra->rra_ra = ra; - - sx_xlock(&random_adaptors_lock); - LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries); - random_adaptor_choose(); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - sx_xunlock(&random_adaptors_lock); -} - -void -random_adaptor_deregister(const char *name) -{ - struct random_adaptors *rra; - - KASSERT(name != NULL, ("invalid input to %s", __func__)); - - sx_xlock(&random_adaptors_lock); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) - if (strcmp(rra->rra_name, name) == 0) { - LIST_REMOVE(rra, rra_entries); - break; - } - random_adaptor_choose(); - sx_xunlock(&random_adaptors_lock); - - free(rra, M_ENTROPY); -} - -/* ARGSUSED */ -int -random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags) -{ - void *random_buf; - int c, error; - ssize_t nbytes; - -#ifdef RANDOM_DEBUG_VERBOSE - printf("random: %s %ld\n", __func__, uio->uio_resid); -#endif - - random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); - - sx_slock(&random_adaptors_lock); - - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - - /* Let the entropy source do any pre-read setup. */ - (random_adaptor->ra_read)(NULL, 0); - - /* (Un)Blocking logic */ - error = 0; - while (!random_adaptor->ra_seeded() && error == 0) { - if (flags & O_NONBLOCK) { - error = EWOULDBLOCK; - break; - } - - /* Sleep instead of going into a spin-frenzy */ - error = sx_sleep(&random_adaptor, &random_adaptors_lock, - PUSER | PCATCH, "randrd", hz/10); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", - __func__)); - - /* keep tapping away at the pre-read until we seed/unblock. */ - (random_adaptor->ra_read)(NULL, 0); - } - - mtx_lock(&random_read_rate_mtx); - - /* The read-rate stuff is a rough indication of the instantaneous read rate, - * used to increase the use of 'live' entropy sources when lots of reads are done. - */ - nbytes = (uio->uio_resid + 32 - 1)/32; /* Round up to units of 32 */ - random_adaptor_read_rate_cache += nbytes*32; - random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32); - - mtx_unlock(&random_read_rate_mtx); - - if (error == 0) { - nbytes = uio->uio_resid; - - /* The actual read */ - while (uio->uio_resid && !error) { - c = MIN(uio->uio_resid, PAGE_SIZE); - (random_adaptor->ra_read)(random_buf, c); - error = uiomove(random_buf, c, uio); - } - - /* Let the entropy source do any post-read cleanup. */ - (random_adaptor->ra_read)(NULL, 1); - - if (nbytes != uio->uio_resid && (error == ERESTART || - error == EINTR) ) - error = 0; /* Return partial read, not error. */ - - } - sx_sunlock(&random_adaptors_lock); - - free(random_buf, M_ENTROPY); - - return (error); -} - -int -random_adaptor_read_rate(void) -{ - int ret; - - mtx_lock(&random_read_rate_mtx); - - ret = random_adaptor_read_rate_cache; - random_adaptor_read_rate_cache = 1; - - mtx_unlock(&random_read_rate_mtx); - - return (ret); -} - -/* ARGSUSED */ -int -random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) -{ - int c, error = 0; - void *random_buf; - ssize_t nbytes; - -#ifdef RANDOM_DEBUG - printf("random: %s %zd\n", __func__, uio->uio_resid); -#endif - - random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); - - sx_slock(&random_adaptors_lock); - - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - - nbytes = uio->uio_resid; - while (uio->uio_resid > 0 && error == 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 */ - error = sx_sleep(&random_adaptor, &random_adaptors_lock, - PUSER | PCATCH, "randwr", hz/10); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", - __func__)); - } - - sx_sunlock(&random_adaptors_lock); - - if (nbytes != uio->uio_resid && (error == ERESTART || - error == EINTR) ) - error = 0; /* Partial write, not error. */ - - free(random_buf, M_ENTROPY); - - return (error); -} - -/* ARGSUSED */ -int -random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused) -{ - -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif - - 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()) - events &= (POLLIN | POLLRDNORM); - else - selrecord(td, &rsel); - } - - sx_sunlock(&random_adaptors_lock); - - return (events); -} - -/* This will be called by the entropy processor when it seeds itself and becomes secure */ -void -random_adaptor_unblock(void) -{ - - selwakeuppri(&rsel, PUSER); - wakeup(&random_adaptor); - printf("random: unblocking device.\n"); - - /* Do arc4random(9) a favour while we are about it. */ - (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); -} - -static int -random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS) -{ - struct random_adaptors *rra; - struct sbuf sbuf; - int error, count; - - sx_slock(&random_adaptors_lock); - sbuf_new_for_sysctl(&sbuf, NULL, 64, req); - count = 0; - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) - sbuf_printf(&sbuf, "%s%s(%d)", - (count++ ? "," : ""), rra->rra_name, rra->rra_ra->ra_priority); - - error = sbuf_finish(&sbuf); - sbuf_delete(&sbuf); - sx_sunlock(&random_adaptors_lock); - - return (error); -} - -static int -random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS) -{ - struct random_adaptors *rra; - struct sbuf sbuf; - int error; - - sx_slock(&random_adaptors_lock); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - - sbuf_new_for_sysctl(&sbuf, NULL, 16, req); - LIST_FOREACH(rra, &random_adaptors_list, rra_entries) - if (rra->rra_ra == random_adaptor) { - sbuf_cat(&sbuf, rra->rra_name); - break; - } - error = sbuf_finish(&sbuf); - sbuf_delete(&sbuf); - sx_sunlock(&random_adaptors_lock); - - return (error); -} - -void -random_adaptors_init(void) -{ - -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif - - SYSCTL_PROC(_kern_random, OID_AUTO, adaptors, - CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, - NULL, 0, random_sysctl_adaptors_handler, "A", - "Random Number Generator adaptors"); - - SYSCTL_PROC(_kern_random, OID_AUTO, active_adaptor, - CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, - NULL, 0, random_sysctl_active_adaptor_handler, "A", - "Active Random Number Generator Adaptor"); - - sx_init(&random_adaptors_lock, "random_adaptors"); - - mtx_init(&random_read_rate_mtx, "read rate mutex", NULL, MTX_DEF); - - /* The dummy adaptor is not a module by itself, but part of the - * randomdev module. - */ - random_adaptor_register("dummy", &randomdev_dummy); - - live_entropy_sources_init(); -} - -void -random_adaptors_deinit(void) -{ - -#ifdef RANDOM_DEBUG - printf("random: %s\n", __func__); -#endif - - live_entropy_sources_deinit(); - - /* Don't do this! Panic will surely follow! */ - /* random_adaptor_deregister("dummy"); */ - - mtx_destroy(&random_read_rate_mtx); - - sx_destroy(&random_adaptors_lock); -} - -/* - * Reseed the active adaptor shortly before starting init(8). - */ -/* ARGSUSED */ -static void -random_adaptors_seed(void *unused __unused) -{ - - sx_slock(&random_adaptors_lock); - KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__)); - - random_adaptor->ra_reseed(); - sx_sunlock(&random_adaptors_lock); - - arc4rand(NULL, 0, 1); -} -SYSINIT(random_seed, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST, - random_adaptors_seed, NULL); Index: sys/dev/random/random_harvestq.h =================================================================== --- sys/dev/random/random_harvestq.h +++ sys/dev/random/random_harvestq.h @@ -1,6 +1,5 @@ /*- - * Copyright (c) 2013-2014 Mark R V Murray - * Copyright (c) 2013 Arthur Mesh + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,43 +27,26 @@ */ #ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED -#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED +#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED -#define HARVESTSIZE 16 /* max size of each harvested entropy unit */ +#define HARVESTSIZE 2 /* Max length in words of each harvested entropy unit */ /* These are used to queue harvested packets of entropy. The entropy * buffer size is pretty arbitrary. */ struct harvest_event { - uintmax_t he_somecounter; /* fast counter for clock jitter */ - uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */ - 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(const void *, u_int, u_int, enum random_entropy_source); - -/* Pool count is used by anything needing to know how many entropy - * pools are currently being maintained. - * This is of use to (e.g.) the live source feed where we need to give - * all the pools a top-up. - */ -extern int harvest_pool_count; - -/* This is in randomdev.c as it needs to be permanently in the kernel */ -void randomdev_set_wakeup_exit(void *); - -/* Force all currently pending queue contents to clear, and kick the software processor */ -void random_harvestq_flush(void); - -/* Function called to process one harvested stochastic event */ -extern void (*harvest_process_event)(struct harvest_event *); - -/* Round-robin destination cache. */ -extern u_int harvest_destination[ENTROPYSOURCE]; + uint32_t he_somecounter; /* fast counter for clock jitter */ + uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */ + uint8_t he_size; /* harvested entropy byte count */ + uint8_t he_bits; /* stats about the entropy */ + uint8_t he_destination; /* destination pool of this entropy */ + uint8_t he_source; /* origin of the entropy */ +} __packed; + +#define RANDOM_HARVESTQ_BOOT_ENTROPY_FILE "/boot/entropy" + +#define RANDOM_HARVEST_INIT_LOCK(x) mtx_init(&harvest_context.hc_mtx, "entropy harvest mutex", NULL, MTX_SPIN) +#define RANDOM_HARVEST_LOCK(x) mtx_lock_spin(&harvest_context.hc_mtx) +#define RANDOM_HARVEST_UNLOCK(x) mtx_unlock_spin(&harvest_context.hc_mtx) #endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */ Index: sys/dev/random/random_harvestq.c =================================================================== --- sys/dev/random/random_harvestq.c +++ sys/dev/random/random_harvestq.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2014 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * Copyright (c) 2013 Arthur Mesh * Copyright (c) 2004 Robert N. M. Watson * All rights reserved. @@ -30,16 +30,17 @@ #include __FBSDID("$FreeBSD$"); -#include "opt_random.h" - #include #include +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -49,9 +50,9 @@ #include #include -#include #include -#include + +static void random_kthread(void); /* List for the dynamic sysctls */ static struct sysctl_ctx_list random_clist; @@ -62,122 +63,111 @@ * supplied junk. When used, they are transferred back to the * 'empty' queue. */ -#define RANDOM_FIFO_MAX 1024 +#define RANDOM_RING_MAX 1024 +#define RANDOM_ACCUM_MAX 8 -/* - * The harvest mutex protects the consistency of the entropy Fifos and - * empty fifo and other associated structures. - */ -static struct mtx harvest_mtx; +/* 1 to let the kernel thread run, 0 to terminate */ +volatile int random_kthread_control; /* - * 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. + * Put all the harvest queue context stuff in one place. + * this make is a bit easier to lock and protect. */ -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 *); - -/* Allow the sysadmin to select the broad category of - * entropy types to harvest. - */ -static u_int harvest_source_mask = ((1U << RANDOM_ENVIRONMENTAL_END) - 1); - -/* Pool count is used by anything needing to know how many entropy - * pools are currently being maintained. - * This is of use to (e.g.) the live source feed where we need to give - * all the pools a top-up. - */ -int harvest_pool_count; +static struct harvest_context { + /* The harvest mutex protects the consistency of the entropy Fifos and + * empty fifo and other associated structures. + */ + struct mtx hc_mtx; + /* Round-robin destination cache. */ + u_int hc_destination[ENTROPYSOURCE]; + /* The context of the kernel thread processing harvested entropy */ + struct proc *hc_kthread_proc; + /* Allow the sysadmin to select the broad category of + * entropy types to harvest. + */ + u_int hc_source_mask; + /* + * Lockless ring buffer holding entropy events + * If ring.in == ring.out, + * the buffer is empty. + * If ring.in != ring.out, + * the buffer contains harvested entropy. + * If (ring.in + 1) == ring.out (mod RANDOM_RING_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. + */ + struct entropy_ring { + struct harvest_event ring[RANDOM_RING_MAX]; + volatile u_int in; + volatile u_int out; + } hc_entropy_ring; + struct fast_entropy_accumulator { + volatile u_int pos; + uint32_t buf[8]; + } hc_entropy_fast_accumulator; +} harvest_context; + +static struct kproc_desc random_proc_kp = { + "rand_harvestq", + random_kthread, + &harvest_context.hc_kthread_proc, +}; -/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */ -static int random_kthread_control = 0; -static struct proc *random_kthread_proc; +/* Pass the given event straight through to Fortuna/Yarrow/Whatever. */ +static __inline void +random_harvestq_fast_process_event(struct harvest_event *event) +{ + if (random_alg_context.ra_event_processor) + random_alg_context.ra_event_processor(event); +} static void -random_kthread(void *arg __unused) +random_kthread(void) { - u_int maxloop, ring_out; + u_int maxloop, ring_out, i; /* - * 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, + * 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. */ - while (random_kthread_control >= 0) { - + for (random_kthread_control = 1; random_kthread_control;) { /* Deal with events, if any. Restrict the number we do in one go. */ - maxloop = RANDOM_FIFO_MAX; - while (entropyfifo.ring_out != entropyfifo.ring_in) { - - ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX; - harvest_process_event(entropyfifo.ring + ring_out); - /* Modifying ring_out here ONLY. Sufficient for atomicity? */ - entropyfifo.ring_out = ring_out; - - /* The ring may be filled quickly so don't loop forever. */ - if (--maxloop) + maxloop = RANDOM_RING_MAX; + while (harvest_context.hc_entropy_ring.out != harvest_context.hc_entropy_ring.in) { + ring_out = (harvest_context.hc_entropy_ring.out + 1)%RANDOM_RING_MAX; + random_harvestq_fast_process_event(harvest_context.hc_entropy_ring.ring + ring_out); + harvest_context.hc_entropy_ring.out = ring_out; + if (!--maxloop) break; - } - - /* - * Give the fast hardware sources a go - */ - 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; - - /* 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)); - + random_sources_feed(); + /* XXX: FIX!! This This seems a little slow; 8 items every 0.1s from UMA? */ + for (i = 0; i < RANDOM_ACCUM_MAX; i++) { + if (harvest_context.hc_entropy_fast_accumulator.buf[i]) { + random_harvest_direct(harvest_context.hc_entropy_fast_accumulator.buf + i, sizeof(harvest_context.hc_entropy_fast_accumulator.buf[0]), 4, RANDOM_FAST); + harvest_context.hc_entropy_fast_accumulator.buf[i] = 0; + } + } + /* XXX: FIX!! This is a *great* place to pass hardware/live entropy to random(9) */ + tsleep_sbt(&harvest_context.hc_kthread_proc, 0, "-", SBT_1S/10, 0, C_PREL(1)); } - - randomdev_set_wakeup_exit(&random_kthread_control); + wakeup(&harvest_context.hc_kthread_proc); + kproc_exit(0); /* NOTREACHED */ } - -void -random_harvestq_flush(void) -{ - - /* Command a entropy queue flush and wait for it to finish */ - random_kthread_control = 1; - while (random_kthread_control) - pause("-", hz/10); -} +SYSINIT(random_device_h_proc, SI_SUB_CREATE_INIT, SI_ORDER_ANY, kproc_start, &random_proc_kp); /* ARGSUSED */ -RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1)); +RANDOM_CHECK_UINT(harvestmask, 0, RANDOM_HARVEST_EVERYTHING_MASK); /* ARGSUSED */ static int @@ -189,12 +179,11 @@ 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"); + for (i = RANDOM_ENVIRONMENTAL_END; i >= 0; i--) + sbuf_cat(&sbuf, (harvest_context.hc_source_mask & (1 << i)) ? "1" : "0"); error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); } - return (error); } @@ -208,8 +197,8 @@ "NET_NG", "INTERRUPT", "SWI", - "UMA_ALLOC", - "", /* "ENVIRONMENTAL_END" */ + "FS_ATIME", + "HIGH_PERFORMANCE", /* ENVIRONMENTAL_END */ "PURE_OCTEON", "PURE_SAFE", "PURE_GLXSB", @@ -231,112 +220,111 @@ 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] : ""); + for (i = RANDOM_ENVIRONMENTAL_END; i >= 0; i--) { + sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END) ? "" : ","); + sbuf_cat(&sbuf, !(harvest_context.hc_source_mask & (1 << i)) ? "[" : ""); + sbuf_cat(&sbuf, random_source_descr[i]); + sbuf_cat(&sbuf, !(harvest_context.hc_source_mask & (1 << i)) ? "]" : ""); } error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); } - return (error); } -void -random_harvestq_init(void (*event_processor)(struct harvest_event *), int poolcount) +/* ARGSUSED */ +static void +random_harvestq_init(void *unused __unused) { - 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 - + if (bootverbose) + printf("random: %s\n", __func__); random_sys_o = SYSCTL_ADD_NODE(&random_clist, SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "harvest", CTLFLAG_RW, 0, "Entropy Device Parameters"); - + harvest_context.hc_source_mask = RANDOM_HARVEST_EVERYTHING_MASK; SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_sys_o), OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW, - &harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1), + &harvest_context.hc_source_mask, 0, 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)"); + RANDOM_HARVEST_INIT_LOCK(); + harvest_context.hc_entropy_ring.in = harvest_context.hc_entropy_ring.out = 0; +} +SYSINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_SECOND, random_harvestq_init, NULL); - /* 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, NULL, - &random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); - - if (error != 0) - panic("Cannot create entropy maintenance thread."); +/* + * This is used to prime the RNG by grabbing any early random stuff + * known to the kernel, and inserting it directly into the hashing + * module, e.g. Fortuna or Yarrow. + */ +/* ARGSUSED */ +static void +random_harvestq_prime(void *unused __unused) +{ + struct harvest_event event; + size_t count, size, i; + uint8_t *keyfile, *data; - /* Get entropy that may have been preloaded by loader(8) + /* + * 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"); + keyfile = preload_search_by_type(RANDOM_HARVESTQ_BOOT_ENTROPY_FILE); 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"); + for (i = 0; i < size; i += sizeof(event.he_entropy)) { + count = sizeof(event.he_entropy); + event.he_somecounter = (uint32_t)get_cyclecount(); + event.he_size = count; + event.he_bits = count/4; /* Underestimate the size for Yarrow */ + event.he_source = RANDOM_CACHED; + event.he_destination = harvest_context.hc_destination[0]++; + memcpy(event.he_entropy, data + i, sizeof(event.he_entropy)); + random_harvestq_fast_process_event(&event); + explicit_bzero(&event, sizeof(event)); + } + explicit_bzero(data, size); + if (bootverbose) + printf("random: read %zu bytes from preloaded cache\n", size); + } else + if (bootverbose) + printf("random: no preloaded entropy cache\n"); } - } +SYSINIT(random_device_prime, SI_SUB_RANDOM, SI_ORDER_FOURTH, random_harvestq_prime, NULL); -void -random_harvestq_deinit(void) +/* ARGSUSED */ +static void +random_harvestq_deinit(void *unused __unused) { -#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); - + /* Command the hash/reseed thread to end and wait for it to finish */ + random_kthread_control = 0; + tsleep(&harvest_context.hc_kthread_proc, 0, "term", 0); sysctl_ctx_free(&random_clist); } +SYSUNINIT(random_device_h_init, SI_SUB_RANDOM, SI_ORDER_SECOND, random_harvestq_deinit, NULL); -/* - * Entropy harvesting routine. - * This is supposed to be fast; do not do anything slow in here! +/*- + * Entropy harvesting queue routine. * + * This is supposed to be fast; do not do anything slow in here! * It is also illegal (and morally reprehensible) to insert any - * high-rate data here. "High-rate" is define as a data source + * high-rate data here. "High-rate" is defined as a data source * that will usually cause lots of failures of the "Lockless read" * check a few lines below. This includes the "always-on" sources * like the Intel "rdrand" or the VIA Nehamiah "xstore" sources. @@ -346,37 +334,78 @@ * read which can be quite expensive. */ void -random_harvestq_internal(const void *entropy, u_int count, u_int bits, - enum random_entropy_source origin) +random_harvest_queue(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin) { struct harvest_event *event; u_int ring_in; - KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, - ("random_harvest_internal: origin %d invalid\n", origin)); - - /* Mask out unwanted sources */ - if (!(harvest_source_mask & (1U << origin))) + KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin)); + if (!(harvest_context.hc_source_mask & (1 << origin))) return; - - /* Lock ring_in against multi-thread contention */ - mtx_lock_spin(&harvest_mtx); - ring_in = (entropyfifo.ring_in + 1)%RANDOM_FIFO_MAX; - if (ring_in != entropyfifo.ring_out) { + RANDOM_HARVEST_LOCK(); + ring_in = (harvest_context.hc_entropy_ring.in + 1)%RANDOM_RING_MAX; + if (ring_in != harvest_context.hc_entropy_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 = harvest_context.hc_entropy_ring.ring + ring_in; + event->he_somecounter = (uint32_t)get_cyclecount(); 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; + event->he_destination = harvest_context.hc_destination[origin]++; + event->he_bits = bits; + if (count <= sizeof(event->he_entropy)) { + event->he_size = count; + memcpy(event->he_entropy, entropy, count); + } + else { + /* Big event, so squash it */ + event->he_size = sizeof(event->he_entropy[0]); + event->he_entropy[0] = jenkins_hash(entropy, count, (uint32_t)(uintptr_t)event); + } + harvest_context.hc_entropy_ring.in = ring_in; } - mtx_unlock_spin(&harvest_mtx); + RANDOM_HARVEST_UNLOCK(); +} + +/*- + * Entropy harvesting fast routine. + * + * This is supposed to be very fast; do not do anything slow in here! + * This is the right place for high-rate harvested data. + */ +void +random_harvest_fast(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin) +{ + u_int pos; + + KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin)); + /* XXX: FIX!! The above KASSERT is BS. Right now we ignore most structure and just accumulate the supplied data */ + if (!(harvest_context.hc_source_mask & (1 << origin))) + return; + pos = harvest_context.hc_entropy_fast_accumulator.pos; + harvest_context.hc_entropy_fast_accumulator.buf[pos] ^= jenkins_hash(entropy, count, (uint32_t)get_cyclecount()); + harvest_context.hc_entropy_fast_accumulator.pos = (pos + 1)%RANDOM_ACCUM_MAX; +} + +/*- + * Entropy harvesting direct routine. + * + * This is not supposed to be fast, but will only be used during + * (e.g.) booting when initial entropy is being gathered. + */ +void +random_harvest_direct(const void *entropy, u_int count, u_int bits, enum random_entropy_source origin) +{ + struct harvest_event event; + + KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE, ("%s: origin %d invalid\n", __func__, origin)); + if (!(harvest_context.hc_source_mask & (1 << origin))) + return; + count = MIN(count, sizeof(event.he_entropy)); + event.he_somecounter = (uint32_t)get_cyclecount(); + event.he_size = count; + event.he_bits = bits; + event.he_source = origin; + event.he_destination = harvest_context.hc_destination[origin]++; + memcpy(event.he_entropy, entropy, count); + random_harvestq_fast_process_event(&event); + explicit_bzero(&event, sizeof(event)); } Index: sys/dev/random/randomdev.h =================================================================== --- sys/dev/random/randomdev.h +++ sys/dev/random/randomdev.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,41 +27,87 @@ */ #ifndef SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED -#define SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED +#define SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED /* This header contains only those definitions that are global * and non algorithm-specific for the entropy processor */ -typedef void random_init_func_t(void); -typedef void random_deinit_func_t(void); - -void randomdev_init_harvester(void (*)(const void *, u_int, u_int, enum random_entropy_source)); -void randomdev_init_reader(void (*)(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 void 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) \ +#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)) \ + if (*(u_int *)(oidp->oid_arg1) <= (min)) \ *(u_int *)(oidp->oid_arg1) = (min); \ - else if (*(u_int *)(oidp->oid_arg1) > (max)) \ + 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, \ + return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \ req)); \ } #endif /* SYSCTL_DECL */ -#endif +MALLOC_DECLARE(M_ENTROPY); + +#define RANDOM_ALG_READ_RATE_MINIMUM 32 + +struct harvest_event; + +typedef void random_alg_pre_read_t(void); +typedef void random_alg_read_t(uint8_t *, u_int); +typedef void random_alg_post_read_t(void); +typedef void random_alg_write_t(uint8_t *, u_int); +typedef int random_alg_seeded_t(void); +typedef void random_alg_reseed_t(void); +typedef void random_alg_eventprocessor_t(struct harvest_event *); + +typedef u_int random_source_read_t(void *, u_int); + +/* + * Random Algorithm is a processor of randomness for the kernel + * and for userland. + */ +struct random_algorithm { + const char *ra_ident; + u_int ra_poolcount; + random_alg_pre_read_t *ra_pre_read; + random_alg_read_t *ra_read; + random_alg_post_read_t *ra_post_read; + random_alg_write_t *ra_write; + random_alg_reseed_t *ra_reseed; + random_alg_seeded_t *ra_seeded; + random_alg_eventprocessor_t *ra_event_processor; +}; + +extern struct random_algorithm random_alg_context; + +/* + * Random Source is a source of entropy that can provide + * specified or approximate amount of entropy immediately + * upon request. + */ +struct random_source { + const char *rs_ident; + enum random_entropy_source rs_source; + random_source_read_t *rs_read; +}; + +#if !defined(RANDOM_DUMMY) +struct random_sources { + LIST_ENTRY(random_sources) rrs_entries; + struct random_source *rrs_source; +}; +#endif /* !defined(RANDOM_DUMMY) */ + +void random_source_register(struct random_source *); +void random_source_deregister(struct random_source *); + +void random_sources_feed(void); + +void randomdev_unblock(void); + +#endif /* SYS_DEV_RANDOM_RANDOMDEV_H_INCLUDED */ Index: sys/dev/random/randomdev.c =================================================================== --- sys/dev/random/randomdev.c +++ sys/dev/random/randomdev.c @@ -1,6 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray - * Copyright (c) 2013 Arthur Mesh + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,28 +25,9 @@ * */ -/* - * 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 @@ -59,27 +39,42 @@ #include #include #include +#include #include #include +#include +#include #include #include #include #include +#include +#include + +#include #include -#include #include -#define RANDOM_MINOR 0 +#include "opt_random.h" + +#if defined(RANDOM_DUMMY) && defined(RANDOM_YARROW) +#error "Cannot define both RANDOM_DUMMY and RANDOM_YARROW" +#endif +#define RANDOM_MINOR 0 + +static d_read_t randomdev_read; +static d_write_t randomdev_write; +static d_poll_t randomdev_poll; static d_ioctl_t randomdev_ioctl; static struct cdevsw random_cdevsw = { .d_name = "random", .d_version = D_VERSION, - .d_read = random_adaptor_read, - .d_write = random_adaptor_write, - .d_poll = random_adaptor_poll, + .d_read = randomdev_read, + .d_write = randomdev_write, + .d_poll = randomdev_poll, .d_ioctl = randomdev_ioctl, }; @@ -87,163 +82,326 @@ 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"); +SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Cryptographically Secure Random Number Generator"); MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures"); -/* 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; +#if defined(RANDOM_DUMMY) - default: - error = ENOTTY; +/*- + * Dummy "always block" pseudo algorithm, used when there is no real + * random(4) driver to provide a CSPRNG. + */ - } +static u_int +dummy_random_zero(void) +{ - return (error); + return (0); } -/* 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) +static void +dummy_random(void) { - - wakeup(control); - kproc_exit(0); - /* NOTREACHED */ } -/* ARGSUSED */ -static int -randomdev_modevent(module_t mod __unused, int type, void *data __unused) -{ - int error = 0; +struct random_algorithm random_alg_context = { + .ra_ident = "Dummy", + .ra_reseed = dummy_random, + .ra_seeded = (random_alg_seeded_t *)dummy_random_zero, + .ra_pre_read = dummy_random, + .ra_read = (random_alg_read_t *)dummy_random_zero, + .ra_post_read = dummy_random, + .ra_write = (random_alg_write_t *)dummy_random_zero, + .ra_event_processor = NULL, + .ra_poolcount = 0, +}; - 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; +#else /* !defined(RANDOM_DUMMY) */ - case MOD_UNLOAD: - random_adaptors_deinit(); - destroy_dev(random_dev); - break; +LIST_HEAD(sources_head, random_sources); +static struct sources_head source_list = LIST_HEAD_INITIALIZER(source_list); +static u_int read_rate; - case MOD_SHUTDOWN: - break; +#endif /* defined(RANDOM_DUMMY) */ - default: - error = EOPNOTSUPP; - break; +static struct selinfo rsel; +/* + * This is the read uio(9) interface for random(4). + */ +/* ARGSUSED */ +static int +randomdev_read(struct cdev *dev __unused, struct uio *uio, int flags) +{ + uint8_t *random_buf; + int c, error; + ssize_t nbytes; + + random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); + random_alg_context.ra_pre_read(); + /* (Un)Blocking logic */ + error = 0; + while (!random_alg_context.ra_seeded() && error == 0) { + if (flags & O_NONBLOCK) { + error = EWOULDBLOCK; + break; + } + tsleep(&random_alg_context, 0, "randrd", hz/10); + /* keep tapping away at the pre-read until we seed/unblock. */ + random_alg_context.ra_pre_read(); + printf("random: %s unblock (error = %d)\n", __func__, error); } - + if (error == 0) { +#if !defined(RANDOM_DUMMY) + /* XXX: FIX!! Next line as an atomic operation? */ + read_rate += (uio->uio_resid + sizeof(uint32_t))/sizeof(uint32_t); +#endif + nbytes = uio->uio_resid; + while (uio->uio_resid && !error) { + c = MIN(uio->uio_resid, PAGE_SIZE); + random_alg_context.ra_read(random_buf, c); + error = uiomove(random_buf, c, uio); + } + random_alg_context.ra_post_read(); + if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR) ) + /* Return partial read, not error. */ + error = 0; + } + free(random_buf, M_ENTROPY); return (error); } -DEV_MODULE_ORDERED(randomdev, randomdev_modevent, NULL, SI_ORDER_SECOND); -MODULE_VERSION(randomdev, 1); - -/* ================ - * Harvesting stubs - * ================ +/*- + * Kernel API version of read_random(). + * This is similar to random_alg_read(), + * except it doesn't interface with uio(9). + * It cannot assumed that random_buf is a multiple of + * RANDOM_BLOCKSIZE bytes. */ +u_int +read_random(void *random_buf, u_int len) +{ + u_int read_len, total_read, c; + uint8_t local_buf[len + RANDOM_BLOCKSIZE]; + + KASSERT(random_buf != NULL, ("No suitable random buffer in %s", __func__)); + random_alg_context.ra_pre_read(); + /* (Un)Blocking logic; if not seeded, return nothing. */ + if (random_alg_context.ra_seeded()) { +#if !defined(RANDOM_DUMMY) + /* XXX: FIX!! Next line as an atomic operation? */ + read_rate += (len + sizeof(uint32_t))/sizeof(uint32_t); +#endif + read_len = len; + total_read = 0; + while (read_len) { + c = MIN(read_len, PAGE_SIZE); + random_alg_context.ra_read(&local_buf[total_read], c); + read_len -= c; + total_read += c; + } + memcpy(random_buf, local_buf, len); + } else + len = 0; + random_alg_context.ra_post_read(); + return (len); +} -/* 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) +static int +randomdev_write(struct cdev *dev __unused, struct uio *uio, int flags __unused) { + uint8_t *random_buf; + int c, error = 0; + ssize_t nbytes; + + random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK); + nbytes = uio->uio_resid; + while (uio->uio_resid > 0 && error == 0) { + c = MIN(uio->uio_resid, PAGE_SIZE); + error = uiomove(random_buf, c, uio); + if (error) + break; + random_alg_context.ra_write(random_buf, c); + tsleep(&random_alg_context, 0, "randwr", hz/10); + } + if (nbytes != uio->uio_resid && (error == ERESTART || error == EINTR)) + /* Partial write, not error. */ + error = 0; + free(random_buf, M_ENTROPY); + return (error); } -/* 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; +/* ARGSUSED */ +static int +randomdev_poll(struct cdev *dev __unused, int events, struct thread *td __unused) +{ -/* Initialise the harvester when/if it is loaded */ + if (events & (POLLIN | POLLRDNORM)) { + if (random_alg_context.ra_seeded()) + events &= (POLLIN | POLLRDNORM); + else + selrecord(td, &rsel); + } + return (events); +} + +/* This will be called by the entropy processor when it seeds itself and becomes secure */ void -randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source)) +randomdev_unblock(void) { - reap_func = reaper; + selwakeuppri(&rsel, PUSER); + wakeup(&random_alg_context); + printf("random: unblocking device.\n"); + /* Do random(9) a favour while we are about it. */ + (void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE); } -/* Deinitialise the harvester when/if it is unloaded */ -void -randomdev_deinit_harvester(void) +/* 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; + } - reap_func = random_harvest_phony; + return (error); } -/* 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) +random_source_register(struct random_source *rsource) { +#if defined(RANDOM_DUMMY) + (void)rsource; +#else /* !defined(RANDOM_DUMMY) */ + struct random_sources *rrs; - (*reap_func)(entropy, count, bits, origin); -} + KASSERT(rsource != NULL, ("invalid input to %s", __func__)); -/* ================================ - * Internal reading stubs and fakes - * ================================ - */ + rrs = malloc(sizeof(*rrs), M_ENTROPY, M_WAITOK); + rrs->rrs_source = rsource; -/* Hold the address of the routine which is actually called */ -static void (*read_func)(uint8_t *, u_int) = dummy_random_read_phony; + printf("random: registering fast source %s\n", rsource->rs_ident); + LIST_INSERT_HEAD(&source_list, rrs, rrs_entries); +#endif /* defined(RANDOM_DUMMY) */ +} -/* Initialise the reader when/if it is loaded */ void -randomdev_init_reader(void (*reader)(uint8_t *, u_int)) +random_source_deregister(struct random_source *rsource) { - - read_func = reader; +#if defined(RANDOM_DUMMY) + (void)rsource; +#else /* !defined(RANDOM_DUMMY) */ + struct random_sources *rrs = NULL; + + KASSERT(rsource != NULL, ("invalid input to %s", __func__)); + LIST_FOREACH(rrs, &source_list, rrs_entries) + if (rrs->rrs_source == rsource) { + LIST_REMOVE(rrs, rrs_entries); + break; + } + if (rrs != NULL) + free(rrs, M_ENTROPY); +#endif /* defined(RANDOM_DUMMY) */ } -/* Deinitialise the reader when/if it is unloaded */ +#if !defined(RANDOM_DUMMY) +/* + * Run through all fast 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! + */ void -randomdev_deinit_reader(void) +random_sources_feed(void) { + uint32_t entropy[HARVESTSIZE]; + struct random_sources *rrs; + u_int i, n, local_read_rate; + + /* + * Step over all of live entropy sources, and feed their output + * to the system-wide RNG. + */ + /* XXX: FIX!! Next lines as an atomic operation? */ + local_read_rate = read_rate; + read_rate = RANDOM_ALG_READ_RATE_MINIMUM; + LIST_FOREACH(rrs, &source_list, rrs_entries) { + for (i = 0; i < random_alg_context.ra_poolcount*local_read_rate; i++) { + n = rrs->rrs_source->rs_read(entropy, sizeof(entropy)); + KASSERT((n > 0 && n <= sizeof(entropy)), ("very bad return from rs_read (= %d) in %s", n, __func__)); + random_harvest_direct(entropy, n, (n*8)/2, rrs->rrs_source->rs_source); + } + } + explicit_bzero(entropy, sizeof(entropy)); +} - read_func = dummy_random_read_phony; +static int +random_source_handler(SYSCTL_HANDLER_ARGS) +{ + struct random_sources *rrs; + struct sbuf sbuf; + int error, count; + + sbuf_new_for_sysctl(&sbuf, NULL, 64, req); + count = 0; + LIST_FOREACH(rrs, &source_list, rrs_entries) { + sbuf_cat(&sbuf, (count++ ? ",'" : "'")); + sbuf_cat(&sbuf, rrs->rrs_source->rs_ident); + sbuf_cat(&sbuf, "'"); + } + error = sbuf_finish(&sbuf); + sbuf_delete(&sbuf); + return (error); } +SYSCTL_PROC(_kern_random, OID_AUTO, random_sources, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + NULL, 0, random_source_handler, "A", + "List of active fast entropy sources."); +#endif /* !defined(RANDOM_DUMMY) */ -/* 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) +/* ARGSUSED */ +static int +randomdev_modevent(module_t mod __unused, int type, void *data __unused) { + int error = 0; - if (count < 0) - return 0; + switch (type) { + case MOD_LOAD: + printf("random: entropy device external interface\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 */ + break; + case MOD_UNLOAD: + destroy_dev(random_dev); + break; + case MOD_SHUTDOWN: + break; + default: + error = EOPNOTSUPP; + break; + } + return (error); +} - read_func(buf, count); +static moduledata_t randomdev_mod = { + "random_device", + randomdev_modevent, + 0 +}; - return count; -} +DECLARE_MODULE(random_device, randomdev_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(random_device, 1); Index: sys/dev/random/randomdev_none.c =================================================================== --- /dev/null +++ sys/dev/random/randomdev_none.c @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2015 Mark R V Murray + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer + * in this position and unchanged. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include "opt_random.h" + +#if defined(RANDOM_DUMMY) || defined(RANDOM_YARROW) +#error "Cannot define any of RANDOM_DUMMY and RANDOM_YARROW without 'device random'" +#endif + +/*- + * Dummy "not even here" device. Stub out all routines that the kernel would need. + */ + +/* ARGSUSED */ +u_int +read_random(void *random_buf __unused, u_int len __unused) +{ + + return (0); +} + +/* ARGSUSED */ +void +random_harvest_direct(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused) +{ +} + +/* ARGSUSED */ +void +random_harvest_queue(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused) +{ +} + +/* ARGSUSED */ +void +random_harvest_fast(const void *entropy __unused, u_int count __unused, u_int bits __unused, enum random_entropy_source origin __unused) +{ +} Index: sys/dev/random/randomdev_soft.h =================================================================== --- sys/dev/random/randomdev_soft.h +++ /dev/null @@ -1,39 +0,0 @@ -/*- - * Copyright (c) 2000-2013 Mark R V Murray - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer - * in this position and unchanged. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED -#define SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED - -/* This header contains only those definitions that are - * specific to the entropy processor - */ - -void randomdev_init(void); -void randomdev_deinit(void); - -#endif Index: sys/dev/random/randomdev_soft.c =================================================================== --- sys/dev/random/randomdev_soft.c +++ /dev/null @@ -1,165 +0,0 @@ -/*- - * Copyright (c) 2000-2014 Mark R V Murray - * Copyright (c) 2004 Robert N. M. Watson - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer - * in this position and unchanged. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This is the loadable infrastructure base file for software CSPRNG - * drivers such as Yarrow or Fortuna. - * - * It is anticipated that one instance of this file will be used - * for _each_ invocation of a CSPRNG, but with different #defines - * set. See below. - * - */ - -#include "opt_random.h" - -#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) -#define RANDOM_YARROW -#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) -#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" -#endif - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#if defined(RANDOM_YARROW) -#include -#endif -#if defined(RANDOM_FORTUNA) -#include -#endif - -static struct random_adaptor random_soft_processor = { -#if defined(RANDOM_YARROW) -#define RANDOM_CSPRNG_NAME "yarrow" - .ra_ident = "Yarrow", - .ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */ - .ra_read = random_yarrow_read, - .ra_write = random_yarrow_write, - .ra_reseed = random_yarrow_reseed, - .ra_seeded = random_yarrow_seeded, -#endif -#if defined(RANDOM_FORTUNA) -#define RANDOM_CSPRNG_NAME "fortuna" - .ra_ident = "Fortuna", - .ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */ - .ra_read = random_fortuna_read, - .ra_write = random_fortuna_write, - .ra_reseed = random_fortuna_reseed, - .ra_seeded = random_fortuna_seeded, -#endif - .ra_init = randomdev_init, - .ra_deinit = randomdev_deinit, -}; - -void -randomdev_init(void) -{ - -#if defined(RANDOM_YARROW) - random_yarrow_init_alg(); - random_harvestq_init(random_yarrow_process_event, 2); -#endif -#if defined(RANDOM_FORTUNA) - random_fortuna_init_alg(); - random_harvestq_init(random_fortuna_process_event, 32); -#endif - - /* Register the randomness harvesting routine */ - randomdev_init_harvester(random_harvestq_internal); -} - -void -randomdev_deinit(void) -{ - /* Deregister the randomness harvesting routine */ - randomdev_deinit_harvester(); - -#if defined(RANDOM_YARROW) - random_yarrow_deinit_alg(); -#endif -#if defined(RANDOM_FORTUNA) - random_fortuna_deinit_alg(); -#endif -} - -/* ARGSUSED */ -static int -randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused) -{ - int error = 0; - - switch (type) { - case MOD_LOAD: - printf("random: SOFT: %s init()\n", RANDOM_CSPRNG_NAME); - random_adaptor_register(RANDOM_CSPRNG_NAME, &random_soft_processor); - break; - - case MOD_UNLOAD: - random_adaptor_deregister(RANDOM_CSPRNG_NAME); - break; - - case MOD_SHUTDOWN: - break; - - default: - error = EOPNOTSUPP; - break; - - } - return (error); -} - -#if defined(RANDOM_YARROW) -DEV_MODULE(yarrow, randomdev_soft_modevent, NULL); -MODULE_VERSION(yarrow, 1); -MODULE_DEPEND(yarrow, randomdev, 1, 1, 1); -#endif -#if defined(RANDOM_FORTUNA) -DEV_MODULE(fortuna, randomdev_soft_modevent, NULL); -MODULE_VERSION(fortuna, 1); -MODULE_DEPEND(fortuna, randomdev, 1, 1, 1); -#endif Index: sys/dev/random/uint128.h =================================================================== --- sys/dev/random/uint128.h +++ sys/dev/random/uint128.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2014 Mark R V Murray + * Copyright (c) 2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,7 +27,7 @@ */ #ifndef SYS_DEV_RANDOM_UINT128_H_INCLUDED -#define SYS_DEV_RANDOM_UINT128_H_INCLUDED +#define SYS_DEV_RANDOM_UINT128_H_INCLUDED /* This whole thing is a crock :-( * @@ -35,40 +35,41 @@ */ #ifdef __SIZEOF_INT128__ -typedef __uint128_t uint128_t; -#else -typedef uint64_t uint128_t[2]; +#define USE_REAL_UINT128_T #endif -static __inline void -uint128_clear(uint128_t *big_uint) -{ -#ifdef __SIZEOF_INT128__ - (*big_uint) = 0ULL; +#ifdef USE_REAL_UINT128_T +typedef __uint128_t uint128_t; +#define UINT128_ZERO 0ULL #else - (*big_uint)[0] = (*big_uint)[1] = 0UL; +typedef struct { + /* Ignore endianness */ + uint64_t u128t_word0; + uint64_t u128t_word1; +} uint128_t; +static const uint128_t very_long_zero = {0UL,0UL}; +#define UINT128_ZERO very_long_zero #endif -} static __inline void -uint128_increment(uint128_t *big_uint) +uint128_increment(uint128_t *big_uintp) { -#ifdef __SIZEOF_INT128__ - (*big_uint)++; +#ifdef USE_REAL_UINT128_T + (*big_uintp)++; #else - (*big_uint)[0]++; - if ((*big_uint)[0] == 0UL) - (*big_uint)[1]++; + big_uintp->u128t_word0++; + if (big_uintp->u128t_word0 == 0UL) + big_uintp->u128t_word1++; #endif } static __inline int uint128_is_zero(uint128_t big_uint) { -#ifdef __SIZEOF_INT128__ - return (big_uint == 0ULL); +#ifdef USE_REAL_UINT128_T + return (big_uint == UINT128_ZERO); #else - return (big_uint[0] == 0UL && big_uint[1] == 0UL); + return (big_uint.u128t_word0 == 0UL && big_uint.u128t_word1 == 0UL); #endif } Index: sys/dev/random/unit_test.h =================================================================== --- sys/dev/random/unit_test.h +++ sys/dev/random/unit_test.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2013 Mark R V Murray + * Copyright (c) 2013-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,20 +28,39 @@ #ifndef UNIT_TEST_H_INCLUDED -#define UNIT_TEST_H_INCLUDED +#define UNIT_TEST_H_INCLUDED + +#ifdef _KERNEL +#error "Random unit tests cannot be compiled into the kernel." +#endif void random_adaptor_unblock(void); +#if defined(clang) && __has_builtin(__builtin_readcyclecounter) +#define rdtsc __builtin_readcyclecounter +#else /* !clang */ +#if defined(__amd64__) || defined(__i386__) +static __inline uint64_t +rdtsc(void) +{ + uint32_t low, high; + + __asm __volatile("rdtsc" : "=a" (low), "=d" (high)); + return (low | ((uint64_t)high << 32)); +} +#else /* __amd64__ || __i386__ */ +#error "No rdtsc() implementation available." +#endif /* __amd64__ || __i386__ */ +#endif /* !clang */ + static __inline uint64_t get_cyclecount(void) { - /* Shaddup! */ - return (4ULL); + return (rdtsc()); } -// #define PAGE_SIZE 4096 -#define HARVESTSIZE 16 +#define HARVESTSIZE 2 enum random_entropy_source { RANDOM_START = 0, @@ -51,7 +70,7 @@ struct harvest_event { uintmax_t he_somecounter; /* fast counter for clock jitter */ - uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */ + uint32_t he_entropy[HARVESTSIZE];/* some harvested entropy */ u_int he_size; /* harvested entropy byte count */ u_int he_bits; /* stats about the entropy */ u_int he_destination; /* destination pool of this entropy */ Index: sys/dev/random/unit_test.c =================================================================== --- sys/dev/random/unit_test.c +++ sys/dev/random/unit_test.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +37,7 @@ ../../crypto/rijndael/rijndael-api-fst.c \ ../../crypto/rijndael/rijndael-alg-fst.c \ ../../crypto/sha2/sha2.c \ + -lz \ -o unit_test ./unit_test @@ -49,6 +50,7 @@ #include #include #include +#include #include "unit_test.h" @@ -59,10 +61,77 @@ #include "dev/random/fortuna.h" #endif -#define NUM_THREADS 3 +#define NUM_THREADS 3 +#define DEBUG static volatile int stopseeding = 0; +static __inline void +check_err(int err, const char *func) +{ + if (err != Z_OK) { + fprintf(stderr, "Compress error in %s: %d\n", func, err); + exit(0); + } +} + +void * +myalloc(void *q, unsigned n, unsigned m) +{ + q = Z_NULL; + return (calloc(n, m)); +} + +void myfree(void *q, void *p) +{ + q = Z_NULL; + free(p); +} + +size_t +block_deflate(uint8_t *uncompr, uint8_t *compr, const size_t len) +{ + z_stream c_stream; + int err; + + if (len == 0) + return (0); + + c_stream.zalloc = myalloc; + c_stream.zfree = myfree; + c_stream.opaque = NULL; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + check_err(err, "deflateInit"); + + c_stream.next_in = uncompr; + c_stream.next_out = compr; + c_stream.avail_in = len; + c_stream.avail_out = len*2u +512u; + + while (c_stream.total_in != len && c_stream.total_out < (len*2u + 512u)) { + err = deflate(&c_stream, Z_NO_FLUSH); +#ifdef DEBUG + printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out); +#endif + check_err(err, "deflate(..., Z_NO_FLUSH)"); + } + + for (;;) { + err = deflate(&c_stream, Z_FINISH); +#ifdef DEBUG + printf("deflate: len = %zd total_in = %lu total_out = %lu\n", len, c_stream.total_in, c_stream.total_out); +#endif + if (err == Z_STREAM_END) break; + check_err(err, "deflate(..., Z_STREAM_END)"); + } + + err = deflateEnd(&c_stream); + check_err(err, "deflateEnd"); + + return ((size_t)c_stream.total_out); +} + void random_adaptor_unblock(void) { @@ -128,6 +197,7 @@ if (i % 1000 == 0) printf("Thread write 1 - %d\n", i); if (buf != NULL) { + printf("Thread 1 writing.\n"); #ifdef RANDOM_YARROW random_yarrow_write(buf, i); #endif @@ -149,9 +219,12 @@ static int ReadCSPRNG(void *threadid) { - size_t tid; - uint8_t *buf; + size_t tid, zsize; + uint8_t *buf, *zbuf; int i; +#ifdef DEBUG + int j; +#endif tid = (size_t)threadid; printf("Thread #%zd starts\n", tid); @@ -164,42 +237,50 @@ #endif { #ifdef RANDOM_YARROW - random_yarrow_read(NULL, 0); - random_yarrow_read(NULL, 1); + random_yarrow_pre_read(); + random_yarrow_post_read(); #endif #ifdef RANDOM_FORTUNA - random_fortuna_read(NULL, 0); - random_fortuna_read(NULL, 1); + random_fortuna_pre_read(); + random_fortuna_post_read(); #endif usleep(100); } for (i = 0; i < 100000; i++) { buf = malloc(i); + zbuf = malloc(2*i + 1024); if (i % 1000 == 0) - printf("Thread read %zd - %d %p\n", tid, i, buf); - if (buf != NULL) { + printf("Thread read %zd - %d\n", tid, i); + if (buf != NULL && zbuf != NULL) { #ifdef RANDOM_YARROW - random_yarrow_read(NULL, 0); + random_yarrow_pre_read(); random_yarrow_read(buf, i); - random_yarrow_read(NULL, 1); + random_yarrow_post_read(); #endif #ifdef RANDOM_FORTUNA - random_fortuna_read(NULL, 0); + random_fortuna_pre_read(); random_fortuna_read(buf, i); - random_fortuna_read(NULL, 1); + random_fortuna_post_read(); #endif -#if 0 - { - int j; - + zsize = block_deflate(buf, zbuf, i); + if (zsize < i) + printf("ERROR!! Compressible RNG output!\n"); +#ifdef DEBUG + printf("RNG output:\n"); for (j = 0; j < i; j++) { printf(" %02X", buf[j]); if (j % 32 == 31 || j == i - 1) printf("\n"); } + printf("Compressed output:\n"); + for (j = 0; j < zsize; j++) { + printf(" %02X", zbuf[j]); + if (j % 32 == 31 || j == zsize - 1) + printf("\n"); } #endif + free(zbuf); free(buf); } usleep(100); @@ -228,7 +309,7 @@ for (t = 0; t < NUM_THREADS; t++) { printf("In main: creating thread %ld\n", t); - rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), t); + rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), NULL); if (rc != thrd_success) { printf("ERROR; return code from thrd_create() is %d\n", rc); exit(-1); Index: sys/dev/random/yarrow.h =================================================================== --- sys/dev/random/yarrow.h +++ sys/dev/random/yarrow.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,18 +27,21 @@ */ #ifndef SYS_DEV_RANDOM_YARROW_H_INCLUDED -#define SYS_DEV_RANDOM_YARROW_H_INCLUDED +#define SYS_DEV_RANDOM_YARROW_H_INCLUDED #ifdef _KERNEL typedef struct mtx mtx_t; +#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&yarrow_state.ys_mtx, "reseed mutex", NULL, MTX_DEF) +#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_LOCK(x) mtx_lock(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) mtx_assert(&yarrow_state.ys_mtx, MA_OWNED) +#else +#define RANDOM_RESEED_INIT_LOCK(x) mtx_init(&yarrow_state.ys_mtx, mtx_plain) +#define RANDOM_RESEED_DEINIT_LOCK(x) mtx_destroy(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_LOCK(x) mtx_lock(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_UNLOCK(x) mtx_unlock(&yarrow_state.ys_mtx) +#define RANDOM_RESEED_ASSERT_LOCK_OWNED(x) #endif -void random_yarrow_init_alg(void); -void random_yarrow_deinit_alg(void); -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 +#endif /* SYS_DEV_RANDOM_YARROW_H_INCLUDED */ Index: sys/dev/random/yarrow.c =================================================================== --- sys/dev/random/yarrow.c +++ sys/dev/random/yarrow.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000-2013 Mark R V Murray + * Copyright (c) 2000-2015 Mark R V Murray * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,12 +29,12 @@ __FBSDID("$FreeBSD$"); #ifdef _KERNEL -#include "opt_random.h" - #include #include +#include #include #include +#include #include #include #include @@ -47,13 +47,10 @@ #include #include -#include #include #include #include #else /* !_KERNEL */ -#include -#include #include #include #include @@ -66,467 +63,403 @@ #include #include -#include #include #include #endif /* _KERNEL */ -#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA) -#define RANDOM_YARROW -#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA) -#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA" -#endif - -#if defined(RANDOM_YARROW) - -#define TIMEBIN 16 /* max value for Pt/t */ +#define RANDOM_YARROW_TIMEBIN 16 /* max value for Pt/t */ -#define FAST 0 -#define SLOW 1 +#define RANDOM_YARROW_FAST 0 +#define RANDOM_YARROW_SLOW 1 +#define RANDOM_YARROW_NPOOLS 2 -/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */ -CTASSERT(BLOCKSIZE == sizeof(uint128_t)); -CTASSERT(KEYSIZE == 2*BLOCKSIZE); +/* This algorithm (and code) presumes that RANDOM_KEYSIZE is twice as large as RANDOM_BLOCKSIZE */ +CTASSERT(RANDOM_BLOCKSIZE == sizeof(uint128_t)); +CTASSERT(RANDOM_KEYSIZE == 2*RANDOM_BLOCKSIZE); -/* This is the beastie that needs protecting. It contains all of the - * state that we are excited about. - * Exactly one is instantiated. +/* + * This is the beastie that needs protecting. It contains all of the + * state that we are excited about. Exactly one is instantiated. */ static struct yarrow_state { - union { - uint8_t byte[BLOCKSIZE]; - uint128_t whole; - } counter; /* C */ - struct randomdev_key key; /* K */ - u_int gengateinterval; /* Pg */ - u_int bins; /* Pt/t */ - u_int outputblocks; /* count output blocks for gates */ - u_int slowoverthresh; /* slow pool overthreshhold reseed count */ - struct pool { - struct source { - u_int bits; /* estimated bits of entropy */ - } source[ENTROPYSOURCE];/* ... per source */ - u_int thresh; /* pool reseed threshhold */ - struct randomdev_hash hash; /* accumulated entropy */ - } pool[2]; /* pool[0] is fast, pool[1] is slow */ - int seeded; - - struct start_cache { - uint8_t junk[KEYSIZE]; - struct randomdev_hash hash; - } start_cache; + uint128_t ys_counter; /* C */ + struct randomdev_key ys_key; /* K */ + u_int ys_gengateinterval; /* Pg */ + u_int ys_bins; /* Pt/t */ + u_int ys_outputblocks; /* count output blocks for gates */ + u_int ys_slowoverthresh; /* slow pool overthreshhold reseed count */ + struct ys_pool { + u_int ysp_source_bits[ENTROPYSOURCE]; /* estimated bits of entropy per source */ + u_int ysp_thresh; /* pool reseed threshhold */ + struct randomdev_hash ysp_hash; /* accumulated entropy */ + } ys_pool[RANDOM_YARROW_NPOOLS];/* pool[0] is fast, pool[1] is slow */ + int ys_seeded; + /* Reseed lock */ + mtx_t ys_mtx; } 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(bins, RANDOM_YARROW_NPOOLS, 16); +RANDOM_CHECK_UINT(fastthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_BLOCKSIZE*8)); /* Bit counts */ +RANDOM_CHECK_UINT(slowthresh, (RANDOM_BLOCKSIZE*8)/4, (RANDOM_BLOCKSIZE*8)); /* Bit counts */ RANDOM_CHECK_UINT(slowoverthresh, 1, 5); -#else /* !_KERNEL */ -static u_int harvest_destination[ENTROPYSOURCE]; #endif /* _KERNEL */ -static void generator_gate(void); -static void reseed(u_int); +static void random_yarrow_pre_read(void); +static void random_yarrow_read(uint8_t *, u_int); +static void random_yarrow_post_read(void); +static void random_yarrow_write(uint8_t *, u_int); +static void random_yarrow_reseed(void); +static int random_yarrow_seeded(void); +static void random_yarrow_reseed_internal(u_int); +static void random_yarrow_process_event(struct harvest_event *); -void -random_yarrow_init_alg(void) +#ifdef _KERNEL +/* Interface to Adaptors system */ +struct random_algorithm random_alg_context = { + .ra_ident = "Yarrow", + .ra_pre_read = random_yarrow_pre_read, + .ra_read = random_yarrow_read, + .ra_post_read = random_yarrow_post_read, + .ra_write = random_yarrow_write, + .ra_reseed = random_yarrow_reseed, + .ra_seeded = random_yarrow_seeded, + .ra_event_processor = random_yarrow_process_event, + .ra_poolcount = RANDOM_YARROW_NPOOLS, +}; +#endif + +/* ARGSUSED */ +static void +random_yarrow_init_alg(void *unused __unused) { int i, j; #ifdef _KERNEL struct sysctl_oid *random_yarrow_o; -#endif /* _KERNEL */ - - memset(yarrow_state.start_cache.junk, 0, KEYSIZE); - randomdev_hash_init(&yarrow_state.start_cache.hash); - - /* Set up the lock for the reseed/gate state */ -#ifdef _KERNEL - mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF); -#else /* !_KERNEL */ - mtx_init(&random_reseed_mtx, mtx_plain); -#endif /* _KERNEL */ +#endif + RANDOM_RESEED_INIT_LOCK(); /* Start unseeded, therefore blocked. */ - yarrow_state.seeded = 0; - + yarrow_state.ys_seeded = 0; #ifdef _KERNEL - /* Yarrow parameters. Do not adjust these unless you have + /* + * Yarrow parameters. Do not adjust these unless you have * have a very good clue about what they do! */ random_yarrow_o = SYSCTL_ADD_NODE(&random_clist, SYSCTL_STATIC_CHILDREN(_kern_random), OID_AUTO, "yarrow", CTLFLAG_RW, 0, "Yarrow Parameters"); - SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, - "gengateinterval", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.gengateinterval, 10, - random_check_uint_gengateinterval, "I", + "gengateinterval", CTLTYPE_UINT | CTLFLAG_RWTUN, + &yarrow_state.ys_gengateinterval, 0, + random_check_uint_gengateinterval, "UI", "Generation gate interval"); - SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, - "bins", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.bins, 10, - random_check_uint_bins, "I", + "bins", CTLTYPE_UINT | CTLFLAG_RWTUN, + &yarrow_state.ys_bins, 0, + random_check_uint_bins, "UI", "Execution time tuner"); - SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, - "fastthresh", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4, - random_check_uint_fastthresh, "I", + "fastthresh", CTLTYPE_UINT | CTLFLAG_RWTUN, + &yarrow_state.ys_pool[0].ysp_thresh, 0, + random_check_uint_fastthresh, "UI", "Fast reseed threshold"); - SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, - "slowthresh", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.pool[1].thresh, (BLOCKSIZE*8), - random_check_uint_slowthresh, "I", + "slowthresh", CTLTYPE_UINT | CTLFLAG_RWTUN, + &yarrow_state.ys_pool[1].ysp_thresh, 0, + random_check_uint_slowthresh, "UI", "Slow reseed threshold"); - SYSCTL_ADD_PROC(&random_clist, SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO, - "slowoverthresh", CTLTYPE_INT|CTLFLAG_RW, - &yarrow_state.slowoverthresh, 2, - random_check_uint_slowoverthresh, "I", + "slowoverthresh", CTLTYPE_UINT | CTLFLAG_RWTUN, + &yarrow_state.ys_slowoverthresh, 0, + random_check_uint_slowoverthresh, "UI", "Slow over-threshold reseed"); #endif /* _KERNEL */ - - yarrow_state.gengateinterval = 10; - yarrow_state.bins = 10; - yarrow_state.pool[FAST].thresh = (3*(BLOCKSIZE*8))/4; - yarrow_state.pool[SLOW].thresh = (BLOCKSIZE*8); - yarrow_state.slowoverthresh = 2; - + yarrow_state.ys_gengateinterval = 10; + yarrow_state.ys_bins = 10; + yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh = (3*(RANDOM_BLOCKSIZE*8))/4; + yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh = (RANDOM_BLOCKSIZE*8); + yarrow_state.ys_slowoverthresh = 2; /* Ensure that the first time we read, we are gated. */ - yarrow_state.outputblocks = yarrow_state.gengateinterval; - + yarrow_state.ys_outputblocks = yarrow_state.ys_gengateinterval; /* Initialise the fast and slow entropy pools */ - for (i = FAST; i <= SLOW; i++) { - randomdev_hash_init(&yarrow_state.pool[i].hash); + for (i = RANDOM_YARROW_FAST; i <= RANDOM_YARROW_SLOW; i++) { + randomdev_hash_init(&yarrow_state.ys_pool[i].ysp_hash); for (j = RANDOM_START; j < ENTROPYSOURCE; j++) - yarrow_state.pool[i].source[j].bits = 0U; + yarrow_state.ys_pool[i].ysp_source_bits[j] = 0; } - /* Clear the counter */ - uint128_clear(&yarrow_state.counter.whole); + yarrow_state.ys_counter = UINT128_ZERO; } +#ifdef _KERNEL +SYSINIT(random_yarrow, SI_SUB_RANDOM, SI_ORDER_THIRD, random_yarrow_init_alg, NULL); +#endif -void -random_yarrow_deinit_alg(void) +/* ARGSUSED */ +static void +random_yarrow_deinit_alg(void *unused __unused) { - mtx_destroy(&random_reseed_mtx); - memset(&yarrow_state, 0, sizeof(yarrow_state)); - + RANDOM_RESEED_DEINIT_LOCK(); + explicit_bzero(&yarrow_state, sizeof(yarrow_state)); #ifdef _KERNEL sysctl_ctx_free(&random_clist); #endif } +#ifdef _KERNEL +SYSUNINIT(random_yarrow, SI_SUB_RANDOM, SI_ORDER_THIRD, random_yarrow_deinit_alg, NULL); +#endif -static __inline void -random_yarrow_post_insert(void) +/* Process a single stochastic event off the harvest queue */ +static void +random_yarrow_process_event(struct harvest_event *event) { - u_int pl, overthreshhold[2]; + u_int pl, overthreshhold[RANDOM_YARROW_NPOOLS]; enum random_entropy_source src; -#ifdef _KERNEL - mtx_assert(&random_reseed_mtx, MA_OWNED); -#endif + RANDOM_RESEED_LOCK(); + /* + * 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 % RANDOM_YARROW_NPOOLS; + randomdev_hash_iterate(&yarrow_state.ys_pool[pl].ysp_hash, event, sizeof(*event)); + yarrow_state.ys_pool[pl].ysp_source_bits[event->he_source] += event->he_bits; /* Count the over-threshold sources in each pool */ - for (pl = 0; pl < 2; pl++) { + for (pl = RANDOM_YARROW_FAST; pl <= RANDOM_YARROW_SLOW; pl++) { overthreshhold[pl] = 0; for (src = RANDOM_START; src < ENTROPYSOURCE; src++) { - if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh) + if (yarrow_state.ys_pool[pl].ysp_source_bits[src] > yarrow_state.ys_pool[pl].ysp_thresh) overthreshhold[pl]++; } } - - /* If enough slow sources are over threshhold, then slow reseed + /* + * If enough slow sources are over threshhold, then slow reseed * else if any fast source over threshhold, then fast reseed. */ - if (overthreshhold[SLOW] >= yarrow_state.slowoverthresh) - reseed(SLOW); - else if (overthreshhold[FAST] > 0 && yarrow_state.seeded) - reseed(FAST); -} - -/* 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); + if (overthreshhold[RANDOM_YARROW_SLOW] >= yarrow_state.ys_slowoverthresh) + random_yarrow_reseed_internal(RANDOM_YARROW_SLOW); + else if (overthreshhold[RANDOM_YARROW_FAST] > 0 && yarrow_state.ys_seeded) + random_yarrow_reseed_internal(RANDOM_YARROW_FAST); + explicit_bzero(event, sizeof(*event)); + RANDOM_RESEED_UNLOCK(); } -/* Process a block of data suspected to be slightly stochastic */ +/* Process a block of data suspected to be slightly stochastic. */ static void -random_yarrow_process_buffer(uint8_t *buf, u_int length) +random_yarrow_process_buffer(uint32_t *buf, u_int wordcount) { static struct harvest_event event; - u_int i, pl; + static u_int destination = 0; + int i; - /* 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 */ + for (i = 0; i < wordcount; i += sizeof(event.he_entropy)/sizeof(event.he_entropy[0])) { + event.he_somecounter = (uint32_t)get_cyclecount(); + event.he_size = sizeof(event.he_entropy); + event.he_bits = event.he_size/8; 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 + event.he_destination = destination++; /* Harmless cheating */ + memcpy(event.he_entropy, buf + i, sizeof(event.he_entropy)); + random_yarrow_process_event(&event); } - for (pl = FAST; pl <= SLOW; pl++) - yarrow_state.pool[pl].source[RANDOM_CACHED].bits += (length >> 4); - - random_yarrow_post_insert(); } static void -reseed(u_int fastslow) +random_yarrow_reseed_internal(u_int fastslow) { - /* Interrupt-context stack is a limited resource; make large + /* + * Interrupt-context stack is a limited resource; make large * structures static. */ - static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */ - static uint8_t hash[KEYSIZE]; /* h' */ - static uint8_t temp[KEYSIZE]; + static uint8_t v[RANDOM_YARROW_TIMEBIN][RANDOM_KEYSIZE]; /* v[i] */ + static uint128_t temp; static struct randomdev_hash context; u_int i; enum random_entropy_source j; - KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0")); - KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0")); - + KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh > 0, ("random: Yarrow fast threshold = 0")); + KASSERT(yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh > 0, ("random: Yarrow slow threshold = 0")); + RANDOM_RESEED_ASSERT_LOCK_OWNED(); #ifdef RANDOM_DEBUG -#ifdef RANDOM_DEBUG_VERBOSE - printf("random: %s %s\n", __func__, (fastslow == FAST ? "FAST" : "SLOW")); -#endif - if (!yarrow_state.seeded) { - printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.pool[FAST].thresh); - for (i = RANDOM_START; i < ENTROPYSOURCE; i++) - printf(" %d", yarrow_state.pool[FAST].source[i].bits); - printf("\n"); - printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.pool[SLOW].thresh, yarrow_state.slowoverthresh); - for (i = RANDOM_START; i < ENTROPYSOURCE; i++) - printf(" %d", yarrow_state.pool[SLOW].source[i].bits); - printf("\n"); - } + /* WARNING! This is dangerously tedious to do with mutexes held! */ + printf("random: %s %s seeded = %d\n", __func__, (fastslow == RANDOM_YARROW_FAST ? "RANDOM_YARROW_FAST" : "RANDOM_YARROW_SLOW"), yarrow_state.ys_seeded); + printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_thresh); + for (i = RANDOM_START; i < ENTROPYSOURCE; i++) + printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_source_bits[i]); + printf("\n"); + printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_thresh, yarrow_state.ys_slowoverthresh); + for (i = RANDOM_START; i < ENTROPYSOURCE; i++) + printf(" %d", yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_source_bits[i]); + printf("\n"); #endif -#ifdef _KERNEL - mtx_assert(&random_reseed_mtx, MA_OWNED); -#endif - /* 1. Hash the accumulated entropy into v[0] */ - randomdev_hash_init(&context); /* Feed the slow pool hash in if slow */ - if (fastslow == SLOW) { - randomdev_hash_finish(&yarrow_state.pool[SLOW].hash, temp); - randomdev_hash_iterate(&context, temp, sizeof(temp)); + if (fastslow == RANDOM_YARROW_SLOW) { + randomdev_hash_finish(&yarrow_state.ys_pool[RANDOM_YARROW_SLOW].ysp_hash, &temp); + randomdev_hash_iterate(&context, &temp, sizeof(temp)); } - randomdev_hash_finish(&yarrow_state.pool[FAST].hash, temp); - randomdev_hash_iterate(&context, temp, sizeof(temp)); + randomdev_hash_finish(&yarrow_state.ys_pool[RANDOM_YARROW_FAST].ysp_hash, &temp); + randomdev_hash_iterate(&context, &temp, sizeof(temp)); randomdev_hash_finish(&context, v[0]); - - /* 2. Compute hash values for all v. _Supposed_ to be computationally + /*- + * 2. Compute hash values for all v. _Supposed_ to be computationally * intensive. */ - - if (yarrow_state.bins > TIMEBIN) - yarrow_state.bins = TIMEBIN; - for (i = 1; i < yarrow_state.bins; i++) { + if (yarrow_state.ys_bins > RANDOM_YARROW_TIMEBIN) + yarrow_state.ys_bins = RANDOM_YARROW_TIMEBIN; + for (i = 1; i < yarrow_state.ys_bins; i++) { randomdev_hash_init(&context); /* v[i] #= h(v[i - 1]) */ - randomdev_hash_iterate(&context, v[i - 1], KEYSIZE); + randomdev_hash_iterate(&context, v[i - 1], RANDOM_KEYSIZE); /* v[i] #= h(v[0]) */ - randomdev_hash_iterate(&context, v[0], KEYSIZE); + randomdev_hash_iterate(&context, v[0], RANDOM_KEYSIZE); /* v[i] #= h(i) */ randomdev_hash_iterate(&context, &i, sizeof(i)); /* Return the hashval */ randomdev_hash_finish(&context, v[i]); } - - /* 3. Compute a new key; h' is the identity function here; + /*- + * 3. Compute a new key; h' is the identity function here; * it is not being ignored! */ - randomdev_hash_init(&context); - randomdev_hash_iterate(&context, &yarrow_state.key, KEYSIZE); - for (i = 1; i < yarrow_state.bins; i++) - randomdev_hash_iterate(&context, v[i], KEYSIZE); - randomdev_hash_finish(&context, temp); - randomdev_encrypt_init(&yarrow_state.key, temp); - + randomdev_hash_iterate(&context, &yarrow_state.ys_key, RANDOM_KEYSIZE); + for (i = 1; i < yarrow_state.ys_bins; i++) + randomdev_hash_iterate(&context, v[i], RANDOM_KEYSIZE); + randomdev_hash_finish(&context, &temp); + randomdev_encrypt_init(&yarrow_state.ys_key, &temp); /* 4. Recompute the counter */ - - uint128_clear(&yarrow_state.counter.whole); - randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp, BLOCKSIZE); - memcpy(yarrow_state.counter.byte, temp, BLOCKSIZE); - + yarrow_state.ys_counter = UINT128_ZERO; + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, &temp, RANDOM_BLOCKSIZE); + yarrow_state.ys_counter = temp; /* 5. Reset entropy estimate accumulators to zero */ - for (i = 0; i <= fastslow; i++) for (j = RANDOM_START; j < ENTROPYSOURCE; j++) - yarrow_state.pool[i].source[j].bits = 0; - + yarrow_state.ys_pool[i].ysp_source_bits[j] = 0; /* 6. Wipe memory of intermediate values */ - - memset(v, 0, sizeof(v)); - memset(temp, 0, sizeof(temp)); - memset(hash, 0, sizeof(hash)); - memset(&context, 0, sizeof(context)); - -#ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */ - /* 7. Dump to seed file */ - - /* This pseudo-code is documentation. Please leave it alone. */ + explicit_bzero(v, sizeof(v)); + explicit_bzero(&temp, sizeof(temp)); + explicit_bzero(&context, sizeof(context)); +/* Not defined so writes ain't gonna happen. Kept for documenting. */ +#ifdef RANDOM_RWFILE_WRITE_IS_OK + /*- + * 7. Dump to seed file. + * This pseudo-code is documentation. Please leave it alone. + */ seed_file = ""; error = randomdev_write_file(seed_file, , PAGE_SIZE); if (error == 0) printf("random: entropy seed file '%s' successfully written\n", seed_file); #endif - /* Unblock the device if it was blocked due to being unseeded */ - if (!yarrow_state.seeded) { - yarrow_state.seeded = 1; - random_adaptor_unblock(); + if (!yarrow_state.ys_seeded) { + yarrow_state.ys_seeded = 1; + randomdev_unblock(); } } -/* Internal function to return processed entropy from the PRNG */ +static __inline void +random_yarrow_generator_gate(void) +{ + u_int i; + uint8_t temp[RANDOM_KEYSIZE]; + + RANDOM_RESEED_ASSERT_LOCK_OWNED(); + uint128_increment(&yarrow_state.ys_counter); + for (i = 0; i < RANDOM_KEYSIZE; i += RANDOM_BLOCKSIZE) + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, temp + i, RANDOM_BLOCKSIZE); + randomdev_encrypt_init(&yarrow_state.ys_key, temp); + explicit_bzero(temp, sizeof(temp)); +} + +/*- + * Used to return processed entropy from the PRNG. + * There is a pre_read and a post_read required to be present + * (but they can be null functions) in order to allow specific + * actions at the begin or the end of a read. Yarrow does its + * reseeding in its own thread. The _pre_read() and _post_read() + * are not used here, and must be kept for completeness. + */ +void +random_yarrow_pre_read(void) +{ +} + +/*- + * Main read from Yarrow. + * The supplied buf MUST be a multiple (>=0) of RANDOM_BLOCKSIZE in size. + * Lots of code presumes this for efficiency, both here and in other + * routines. You are NOT allowed to break this! + */ void random_yarrow_read(uint8_t *buf, u_int bytecount) { - uint8_t tbuf[BLOCKSIZE]; u_int blockcount, i; - /* Check for initial/final read requests */ - if (buf == NULL) - return; - - /* The reseed task must not be jumped on */ - mtx_lock(&random_reseed_mtx); - - blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE; + RANDOM_RESEED_LOCK(); + blockcount = (bytecount + RANDOM_BLOCKSIZE - 1)/RANDOM_BLOCKSIZE; for (i = 0; i < blockcount; i++) { - if (yarrow_state.outputblocks++ >= yarrow_state.gengateinterval) { - generator_gate(); - yarrow_state.outputblocks = 0; - } - uint128_increment(&yarrow_state.counter.whole); - if ((i + 1) * BLOCKSIZE > bytecount) { - /* TODO: FIX! remove memcpy()! */ - randomdev_encrypt(&yarrow_state.key, - yarrow_state.counter.byte, tbuf, BLOCKSIZE); - memcpy(buf, tbuf, bytecount - i * BLOCKSIZE); - } else { - randomdev_encrypt(&yarrow_state.key, - yarrow_state.counter.byte, buf, BLOCKSIZE); - buf += BLOCKSIZE; + if (yarrow_state.ys_outputblocks++ >= yarrow_state.ys_gengateinterval) { + random_yarrow_generator_gate(); + yarrow_state.ys_outputblocks = 0; } + uint128_increment(&yarrow_state.ys_counter); + randomdev_encrypt(&yarrow_state.ys_key, &yarrow_state.ys_counter, buf, RANDOM_BLOCKSIZE); + buf += RANDOM_BLOCKSIZE; } - - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_UNLOCK(); } -/* Internal function to hand external entropy to the PRNG */ void -random_yarrow_write(uint8_t *buf, u_int count) +random_yarrow_post_read(void) { - 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); + /* CWOT */ } -static void -generator_gate(void) +/* Internal function to hand external entropy to the PRNG. */ +void +random_yarrow_write(uint8_t *buf, u_int count) { - u_int i; - uint8_t temp[KEYSIZE]; - - for (i = 0; i < KEYSIZE; i += BLOCKSIZE) { - uint128_increment(&yarrow_state.counter.whole); - randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE); - } - - randomdev_encrypt_init(&yarrow_state.key, temp); - memset(temp, 0, KEYSIZE); + struct randomdev_hash hash; + uint32_t entropy_data[RANDOM_KEYSIZE_WORDS], timestamp; + + /* Extra timing here is helpful to scrape scheduler timing entropy */ + randomdev_hash_init(&hash); + timestamp = (uint32_t)get_cyclecount(); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_iterate(&hash, buf, count); + timestamp = (uint32_t)get_cyclecount(); + randomdev_hash_iterate(&hash, ×tamp, sizeof(timestamp)); + randomdev_hash_finish(&hash, entropy_data); + explicit_bzero(&hash, sizeof(hash)); + random_yarrow_process_buffer(entropy_data, sizeof(entropy_data)/sizeof(entropy_data[0])); + explicit_bzero(entropy_data, sizeof(entropy_data)); } void random_yarrow_reseed(void) { - mtx_lock(&random_reseed_mtx); - reseed(SLOW); - mtx_unlock(&random_reseed_mtx); + RANDOM_RESEED_LOCK(); + random_yarrow_reseed_internal(RANDOM_YARROW_SLOW); + RANDOM_RESEED_UNLOCK(); } int random_yarrow_seeded(void) { - return (yarrow_state.seeded); + return (yarrow_state.ys_seeded); } - -#endif /* RANDOM_YARROW */ Index: sys/dev/rndtest/rndtest.c =================================================================== --- sys/dev/rndtest/rndtest.c +++ sys/dev/rndtest/rndtest.c @@ -145,16 +145,9 @@ */ if (rsp->rs_discard) rndstats.rst_discard += len; - else { -#if __FreeBSD_version < 500000 - /* XXX verify buffer is word aligned */ - u_int32_t *p = buf; - for (len /= sizeof (u_int32_t); len; len--) - add_true_randomness(*p++); -#else - random_harvest(buf, len, len*NBBY/2, RANDOM_PURE_RNDTEST); -#endif - } + else + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(buf, len, len*NBBY/2, RANDOM_PURE_RNDTEST); } static void Index: sys/dev/safe/safe.c =================================================================== --- sys/dev/safe/safe.c +++ sys/dev/safe/safe.c @@ -211,7 +211,8 @@ static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_SAFE); + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_SAFE); } #endif /* SAFE_NO_RNG */ Index: sys/dev/syscons/scmouse.c =================================================================== --- sys/dev/syscons/scmouse.c +++ sys/dev/syscons/scmouse.c @@ -666,7 +666,7 @@ mouse = (mouse_info_t*)data; - random_harvest(mouse, sizeof(mouse_info_t), 2, RANDOM_MOUSE); + random_harvest_queue(mouse, sizeof(mouse_info_t), 2, RANDOM_MOUSE); if (cmd == OLD_CONS_MOUSECTL) { static u_char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; Index: sys/dev/syscons/syscons.c =================================================================== --- sys/dev/syscons/syscons.c +++ sys/dev/syscons/syscons.c @@ -3411,7 +3411,7 @@ sc_touch_scrn_saver(); if (!(flags & SCGETC_CN)) - random_harvest(&c, sizeof(c), 1, RANDOM_KEYBOARD); + random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD); if (scp->kbd_mode != K_XLATE) return KEYCHAR(c); Index: sys/dev/ubsec/ubsec.c =================================================================== --- sys/dev/ubsec/ubsec.c +++ sys/dev/ubsec/ubsec.c @@ -259,7 +259,8 @@ static void default_harvest(struct rndtest_state *rsp, void *buf, u_int count) { - random_harvest(buf, count, count*NBBY/2, RANDOM_PURE_UBSEC); + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(buf, count, count*NBBY/2, RANDOM_PURE_UBSEC); } static int Index: sys/dev/virtio/random/virtio_random.c =================================================================== --- sys/dev/virtio/random/virtio_random.c +++ sys/dev/virtio/random/virtio_random.c @@ -215,7 +215,7 @@ virtqueue_notify(vq); virtqueue_poll(vq, NULL); - random_harvest(&value, sizeof(value), sizeof(value) * NBBY / 2, + random_harvest_queue(&value, sizeof(value), sizeof(value) * NBBY / 2, RANDOM_PURE_VIRTIO); } Index: sys/dev/vt/vt_core.c =================================================================== --- sys/dev/vt/vt_core.c +++ sys/dev/vt/vt_core.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -732,6 +733,7 @@ { struct vt_window *vw = vd->vd_curwindow; + random_harvest_queue(&c, sizeof(c), 1, RANDOM_KEYBOARD); #if VT_ALT_TO_ESC_HACK if (c & RELKEY) { switch (c & ~RELKEY) { Index: sys/dev/vt/vt_sysmouse.c =================================================================== --- sys/dev/vt/vt_sysmouse.c +++ sys/dev/vt/vt_sysmouse.c @@ -139,7 +139,7 @@ unsigned char buf[MOUSE_SYS_PACKETSIZE]; int x, y, iy, z; - random_harvest(mi, sizeof *mi, 2, RANDOM_MOUSE); + random_harvest_queue(mi, sizeof *mi, 2, RANDOM_MOUSE); mtx_lock(&sysmouse_lock); switch (mi->operation) { Index: sys/fs/tmpfs/tmpfs_subr.c =================================================================== --- sys/fs/tmpfs/tmpfs_subr.c +++ sys/fs/tmpfs/tmpfs_subr.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -1758,6 +1759,8 @@ } node->tn_status &= ~(TMPFS_NODE_ACCESSED | TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED); + /* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */ + random_harvest_queue(node, sizeof(*node), 1, RANDOM_FS_ATIME); } void Index: sys/kern/kern_intr.c =================================================================== --- sys/kern/kern_intr.c +++ sys/kern/kern_intr.c @@ -888,7 +888,7 @@ if (ie->ie_flags & IE_ENTROPY) { entropy.event = (uintptr_t)ie; entropy.td = ctd; - random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); + random_harvest_queue(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); } KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name)); @@ -1039,7 +1039,7 @@ if (ie->ie_flags & IE_ENTROPY) { entropy.event = (uintptr_t)ie; entropy.td = ctd; - random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); + random_harvest_queue(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT); } KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name)); @@ -1126,7 +1126,7 @@ entropy.event = (uintptr_t)ih; entropy.td = curthread; - random_harvest(&entropy, sizeof(entropy), 1, RANDOM_SWI); + random_harvest_queue(&entropy, sizeof(entropy), 1, RANDOM_SWI); /* * Set ih_need for this handler so that if the ithread is already Index: sys/kern/kern_mib.c =================================================================== --- sys/kern/kern_mib.c +++ sys/kern/kern_mib.c @@ -43,16 +43,17 @@ #include "opt_config.h" #include +#include #include -#include -#include -#include -#include #include #include -#include +#include +#include +#include #include #include +#include +#include #include SYSCTL_ROOT_NODE(0, sysctl, CTLFLAG_RW, 0, @@ -152,10 +153,15 @@ char buf[256]; size_t len; - len = req->oldlen; - if (len > sizeof(buf)) - len = sizeof(buf); - arc4rand(buf, len, 0); + /*- + * This is one of the very few legitimate uses of read_random(9). + * Use of arc4random(9) is not recommended as that will ignore + * an unsafe (i.e. unseeded) random(4). + * + * If random(4) is not seeded, then this returns 0, so the + * sysctl will return a zero-length buffer. + */ + len = read_random(buf, MIN(req->oldlen, sizeof(buf))); return (SYSCTL_OUT(req, buf, len)); } Index: sys/kern/subr_bus.c =================================================================== --- sys/kern/subr_bus.c +++ sys/kern/subr_bus.c @@ -28,7 +28,6 @@ __FBSDID("$FreeBSD$"); #include "opt_bus.h" -#include "opt_random.h" #include #include @@ -2877,14 +2876,16 @@ attachtime = get_cyclecount() - attachtime; /* * 4 bits per device is a reasonable value for desktop and server - * hardware with good get_cyclecount() implementations, but may + * hardware with good get_cyclecount() implementations, but WILL * need to be adjusted on other platforms. */ -#ifdef RANDOM_DEBUG - 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); +#define RANDOM_PROBE_BIT_GUESS 4 + if (bootverbose) + printf("random: harvesting attach, %zu bytes (%d bits) from %s%d\n", + sizeof(attachtime), RANDOM_PROBE_BIT_GUESS, + dev->driver->name, dev->unit); + random_harvest_direct(&attachtime, sizeof(attachtime), + RANDOM_PROBE_BIT_GUESS, RANDOM_ATTACH); device_sysctl_update(dev); if (dev->busy) dev->state = DS_BUSY; Index: sys/libkern/arc4random.c =================================================================== --- sys/libkern/arc4random.c +++ sys/libkern/arc4random.c @@ -22,7 +22,7 @@ #define ARC4_RESEED_BYTES 65536 #define ARC4_RESEED_SECONDS 300 -#define ARC4_KEYBYTES (256 / 8) +#define ARC4_KEYBYTES 256 int arc4rand_iniseed_state = ARC4_ENTR_NONE; @@ -48,39 +48,33 @@ * Stir our S-box. */ static void -arc4_randomstir (void) +arc4_randomstir(void) { - u_int8_t key[256]; - int r, n; + u_int8_t key[ARC4_KEYBYTES]; + int n; struct timeval tv_now; /* - * XXX read_random() returns unsafe numbers if the entropy - * device is not loaded -- MarkM. + * XXX: FIX!! This isn't brilliant. Need more confidence. + * This returns zero entropy before random(4) is seeded. */ - r = read_random(key, ARC4_KEYBYTES); + (void)read_random(key, ARC4_KEYBYTES); getmicrouptime(&tv_now); mtx_lock(&arc4_mtx); - /* If r == 0 || -1, just use what was on the stack. */ - if (r > 0) { - for (n = r; n < sizeof(key); n++) - key[n] = key[n % r]; - } - for (n = 0; n < 256; n++) { arc4_j = (arc4_j + arc4_sbox[n] + key[n]) % 256; arc4_swap(&arc4_sbox[n], &arc4_sbox[arc4_j]); } arc4_i = arc4_j = 0; - /* Reset for next reseed cycle. */ arc4_t_reseed = tv_now.tv_sec + ARC4_RESEED_SECONDS; arc4_numruns = 0; - /* * Throw away the first N words of output, as suggested in the * paper "Weaknesses in the Key Scheduling Algorithm of RC4" * by Fluher, Mantin, and Shamir. (N = 256 in our case.) + * + * http://dl.acm.org/citation.cfm?id=646557.694759 */ for (n = 0; n < 256*4; n++) arc4_randbyte(); Index: sys/libkern/random.c =================================================================== --- sys/libkern/random.c +++ sys/libkern/random.c @@ -50,7 +50,7 @@ } /* - * Pseudo-random number generator for randomizing the profiling clock, + * Pseudo-random number generator for perturbing the profiling clock, * and whatever else we might use it for. The result is uniform on * [0, 2^31 - 1]. */ Index: sys/mips/cavium/octeon_rnd.c =================================================================== --- sys/mips/cavium/octeon_rnd.c +++ sys/mips/cavium/octeon_rnd.c @@ -125,7 +125,8 @@ for (i = 0; i < OCTEON_RND_WORDS; i++) sc->sc_entropy[i] = cvmx_rng_get_random64(); - random_harvest(sc->sc_entropy, sizeof sc->sc_entropy, + /* MarkM: FIX!! Check that this does not swamp the harvester! */ + random_harvest_queue(sc->sc_entropy, sizeof sc->sc_entropy, (sizeof(sc->sc_entropy)*8)/2, RANDOM_PURE_OCTEON); callout_reset(&sc->sc_callout, hz * 5, octeon_rnd_harvest, sc); Index: sys/mips/conf/AR71XX_BASE =================================================================== --- sys/mips/conf/AR71XX_BASE +++ sys/mips/conf/AR71XX_BASE @@ -24,7 +24,7 @@ # Build these as modules so small platform builds will have the # modules already built. -makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci" +makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci" # For small memory footprints options VM_KMEM_SIZE_SCALE=1 Index: sys/mips/conf/AR724X_BASE =================================================================== --- sys/mips/conf/AR724X_BASE +++ sys/mips/conf/AR724X_BASE @@ -25,7 +25,7 @@ # Build these as modules so small platform builds will have the # modules already built. -makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci hwpmc cam" +makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_pci hwpmc cam" # For small memory footprints options VM_KMEM_SIZE_SCALE=1 Index: sys/mips/conf/AR91XX_BASE =================================================================== --- sys/mips/conf/AR91XX_BASE +++ sys/mips/conf/AR91XX_BASE @@ -20,7 +20,7 @@ hints "AR91XX_BASE.hints" makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc" +makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc" options DDB options KDB Index: sys/mips/conf/AR933X_BASE =================================================================== --- sys/mips/conf/AR933X_BASE +++ sys/mips/conf/AR933X_BASE @@ -20,7 +20,7 @@ hints "AR933X_BASE.hints" makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_vlan if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr hwpmc ipfw" +makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_vlan if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr hwpmc ipfw" options DDB options KDB Index: sys/mips/conf/AR934X_BASE =================================================================== --- sys/mips/conf/AR934X_BASE +++ sys/mips/conf/AR934X_BASE @@ -20,7 +20,7 @@ hints "AR934X_BASE.hints" makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_vlan if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc ipfw ipfw_nat libalias" +makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_vlan if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc ipfw ipfw_nat libalias" # makeoptions MODULES_OVERRIDE="" options DDB Index: sys/mips/conf/PB92 =================================================================== --- sys/mips/conf/PB92 +++ sys/mips/conf/PB92 @@ -22,7 +22,7 @@ # who already are using it without modifying the default flash layout) # we need to cut down on a lot of things. -makeoptions MODULES_OVERRIDE="ath ath_pci ath_ahb bridgestp if_bridge if_gif if_gre random wlan wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt wlan_tkip wlan_wep wlan_xauth usb ar71xx" +makeoptions MODULES_OVERRIDE="ath ath_pci ath_ahb bridgestp if_bridge if_gif if_gre wlan wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt wlan_tkip wlan_wep wlan_xauth usb ar71xx" hints "PB92.hints" include "../atheros/std.ar71xx" Index: sys/mips/conf/QCA955X_BASE =================================================================== --- sys/mips/conf/QCA955X_BASE +++ sys/mips/conf/QCA955X_BASE @@ -26,7 +26,7 @@ hints "QCA955X_BASE.hints" makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols -# makeoptions MODULES_OVERRIDE="random gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc" +# makeoptions MODULES_OVERRIDE="gpio ar71xx if_gif if_gre if_bridge bridgestp usb wlan wlan_xauth wlan_acl wlan_wep wlan_tkip wlan_ccmp wlan_rssadapt wlan_amrr ath ath_ahb hwpmc" makeoptions MODULES_OVERRIDE="if_vlan ipfw if_gre if_gif if_bridge bridgestp" options DDB Index: sys/mips/conf/RT305X =================================================================== --- sys/mips/conf/RT305X +++ sys/mips/conf/RT305X @@ -24,7 +24,7 @@ makeoptions KERNLOADADDR=0x80001000 # Don't build any modules yet. -makeoptions MODULES_OVERRIDE="wlan_xauth wlan_wep wlan_tkip wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt random if_bridge bridgestp msdosfs md ipfw dummynet libalias geom/geom_label ufs usb/uplcom usb/u3g usb/umodem usb/umass usb/ucom cam zlib" +makeoptions MODULES_OVERRIDE="wlan_xauth wlan_wep wlan_tkip wlan_acl wlan_amrr wlan_ccmp wlan_rssadapt if_bridge bridgestp msdosfs md ipfw dummynet libalias geom/geom_label ufs usb/uplcom usb/u3g usb/umodem usb/umass usb/ucom cam zlib" makeoptions RT3052F include "../rt305x/std.rt305x" Index: sys/modules/Makefile =================================================================== --- sys/modules/Makefile +++ sys/modules/Makefile @@ -291,7 +291,6 @@ ${_qlxgbe} \ ral \ ${_ralfw} \ - ${_random} \ rc4 \ ${_rdma} \ ${_rdrand_rng} \ @@ -395,9 +394,6 @@ _crypto= crypto _cryptodev= cryptodev .endif -.if exists(${.CURDIR}/../crypto) -_random= random -.endif .endif .if ${MK_CUSE} != "no" || defined(ALL_MODULES) Index: sys/modules/crypto/Makefile =================================================================== --- sys/modules/crypto/Makefile +++ sys/modules/crypto/Makefile @@ -12,7 +12,7 @@ KMOD = crypto SRCS = crypto.c cryptodev_if.c SRCS += criov.c cryptosoft.c xform.c -SRCS += cast.c cryptodeflate.c rmd160.c rijndael-alg-fst.c rijndael-api.c +SRCS += cast.c cryptodeflate.c rmd160.c rijndael-alg-fst.c rijndael-api.c rijndael-api-fst.c SRCS += skipjack.c bf_enc.c bf_ecb.c bf_skey.c SRCS += camellia.c camellia-api.c SRCS += des_ecb.c des_enc.c des_setkey.c Index: sys/modules/random/Makefile =================================================================== --- sys/modules/random/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# $FreeBSD$ - -.PATH: ${.CURDIR}/../../dev/random -.PATH: ${.CURDIR}/../../crypto/rijndael -.PATH: ${.CURDIR}/../../crypto/sha2 - -KMOD= random -SRCS= randomdev_soft.c -SRCS+= yarrow.c fortuna.c hash.c -SRCS+= rijndael-alg-fst.c rijndael-api-fst.c sha2.c sha256c.c -SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h opt_random.h - -CFLAGS+= -I${.CURDIR}/../.. - -.include Index: sys/modules/random_fortuna/Makefile =================================================================== --- /dev/null +++ sys/modules/random_fortuna/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/random + +KMOD= random_fortuna +SRCS= fortuna.c hash.c +SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h + +CFLAGS+= -I${.CURDIR}/../.. + +.include Index: sys/modules/random_yarrow/Makefile =================================================================== --- /dev/null +++ sys/modules/random_yarrow/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/random + +KMOD= random_yarrow +SRCS= yarrow.c hash.c +SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h + +CFLAGS+= -I${.CURDIR}/../.. + +.include Index: sys/net/if_ethersubr.c =================================================================== --- sys/net/if_ethersubr.c +++ sys/net/if_ethersubr.c @@ -426,6 +426,8 @@ } #endif + random_harvest_queue(m, sizeof(*m), 2, RANDOM_NET_ETHER); + CURVNET_SET_QUIET(ifp->if_vnet); if (ETHER_IS_MULTICAST(eh->ether_dhost)) { @@ -570,8 +572,6 @@ m->m_flags |= M_PROMISC; } - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER); - ether_demux(ifp, m); CURVNET_RESTORE(); } Index: sys/net/if_tun.c =================================================================== --- sys/net/if_tun.c +++ sys/net/if_tun.c @@ -906,7 +906,7 @@ m_freem(m); return (EAFNOSUPPORT); } - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN); + random_harvest_queue(&(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); Index: sys/netgraph/ng_iface.c =================================================================== --- sys/netgraph/ng_iface.c +++ sys/netgraph/ng_iface.c @@ -705,7 +705,7 @@ m_freem(m); return (EAFNOSUPPORT); } - random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG); + random_harvest_queue(&(m->m_data), 12, 2, RANDOM_NET_NG); M_SETFIB(m, ifp->if_fib); netisr_dispatch(isr, m); return (0); Index: sys/sys/kernel.h =================================================================== --- sys/sys/kernel.h +++ sys/sys/kernel.h @@ -107,8 +107,8 @@ SI_SUB_KLD = 0x2000000, /* KLD and module setup */ SI_SUB_CPU = 0x2100000, /* CPU resource(s)*/ SI_SUB_RACCT = 0x2110000, /* resource accounting */ - SI_SUB_RANDOM = 0x2120000, /* random number generator */ SI_SUB_KDTRACE = 0x2140000, /* Kernel dtrace hooks */ + SI_SUB_RANDOM = 0x2160000, /* random number generator */ SI_SUB_MAC = 0x2180000, /* TrustedBSD MAC subsystem */ SI_SUB_MAC_POLICY = 0x21C0000, /* TrustedBSD MAC policies */ SI_SUB_MAC_LATE = 0x21D0000, /* TrustedBSD MAC subsystem */ Index: sys/sys/random.h =================================================================== --- sys/sys/random.h +++ sys/sys/random.h @@ -31,7 +31,7 @@ #ifdef _KERNEL -int read_random(void *, int); +u_int read_random(void *, u_int); /* * Note: if you add or remove members of random_entropy_source, remember to also update the @@ -53,9 +53,10 @@ 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_FS_ATIME, + RANDOM_FAST, /* Special!! Miscellaneous high performance stuff, like UMA/SLAB Allocator */ + RANDOM_ENVIRONMENTAL_END = RANDOM_FAST, + /* Fast hardware random-number sources from here on. */ RANDOM_PURE_OCTEON, RANDOM_PURE_SAFE, RANDOM_PURE_GLXSB, @@ -67,7 +68,18 @@ RANDOM_PURE_VIRTIO, ENTROPYSOURCE }; -void random_harvest(const void *, u_int, u_int, enum random_entropy_source); + +#define RANDOM_HARVEST_EVERYTHING_MASK ((1 << (RANDOM_ENVIRONMENTAL_END + 1)) - 1) + +#if defined(RANDOM_DUMMY) +#define random_harvest_queue(a, b, c, d) do {} while (0) +#define random_harvest_fast(a, b, c, d) do {} while (0) +#define random_harvest_direct(a, b, c, d) do {} while (0) +#else /* !defined(RANDOM_DUMMY) */ +void random_harvest_queue(const void *, u_int, u_int, enum random_entropy_source); +void random_harvest_fast(const void *, u_int, u_int, enum random_entropy_source); +void random_harvest_direct(const void *, u_int, u_int, enum random_entropy_source); +#endif /* defined(RANDOM_DUMMY) */ #endif /* _KERNEL */ Index: sys/ufs/ffs/ffs_inode.c =================================================================== --- sys/ufs/ffs/ffs_inode.c +++ sys/ufs/ffs/ffs_inode.c @@ -36,16 +36,17 @@ #include #include -#include -#include #include #include -#include #include +#include +#include +#include #include #include -#include #include +#include +#include #include #include @@ -143,12 +144,17 @@ softdep_update_inodeblock(ip, bp, waitfor); else if (ip->i_effnlink != ip->i_nlink) panic("ffs_update: bad link cnt"); - if (ip->i_ump->um_fstype == UFS1) + if (ip->i_ump->um_fstype == UFS1) { *((struct ufs1_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = *ip->i_din1; - else + /* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */ + random_harvest_queue(&(ip->i_din1), sizeof(ip->i_din1), 1, RANDOM_FS_ATIME); + } else { *((struct ufs2_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)) = *ip->i_din2; + /* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */ + random_harvest_queue(&(ip->i_din2), sizeof(ip->i_din2), 1, RANDOM_FS_ATIME); + } if (waitfor && !DOINGASYNC(vp)) error = bwrite(bp); else if (vm_page_count_severe() || buf_dirty_count_severe()) { Index: sys/vm/uma_core.c =================================================================== --- sys/vm/uma_core.c +++ sys/vm/uma_core.c @@ -2121,11 +2121,8 @@ 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 + /* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */ + random_harvest_fast(&zone, sizeof(zone), 1, RANDOM_FAST); /* This is the fast path allocation */ #ifdef UMA_DEBUG_ALLOC_1 @@ -2157,11 +2154,6 @@ 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. */ @@ -2204,11 +2196,6 @@ #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); } @@ -2329,11 +2316,6 @@ 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); } @@ -2681,18 +2663,8 @@ 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 + /* XXX: FIX? The entropy here is desirable, but the harvesting may be expensive */ + random_harvest_fast(&zone, sizeof(zone), 1, RANDOM_FAST); #ifdef UMA_DEBUG_ALLOC_1 printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone);