This is the much-discussed major upgrade to the random(4) device, known to you all as /dev/random.
This code has had an extensive rewrite and a good series of reviews, both by the author and other parties. This means a lot of code has been simplified. Pluggable structures for high-rate entropy generators are available, and it is most definitely not the case that /dev/random can be driven by only a hardware souce any more. This has been designed out of the device. Hardware sources are stirred into the CSPRNG (Yarrow, Fortuna) like any other entropy source. Pluggable modules may be written by third parties for additional sources. The harvesting structures and consequently the locking have been simplified. Entropy harvesting is done in a more general way (the documentation for this will follow). There is some GREAT entropy to be had in the UMA allocator, but it is disabled for now as messing with that is likely to annoy many people. The venerable (but effective) Yarrow algorithm, which is no longer supported by its authors now has an alternative, Fortuna. For now, Yarrow is retained as the default algorithm, but this may be changed using a kernel option. It is intended to make Fortuna the default algorithm for 11.0. Interested parties are encouraged to read ISBN 978-0-470-47424-2 "Cryptography Engineering" By Ferguson, Schneier and Kohno for Fortuna's gory details. Heck, read it anyway. Many thanks to Arthur Mesh who did early grunt work, and who got caught in the crossfire rather more than he deserved to. My thanks also to folks who helped me thresh this out on whiteboards and in the odd "Hallway track", or otherwise. My Nomex pants are on. Let the feedback commence! Reviewed by: trasz,des(partial),imp(partial?),rwatson(partial?) Approved by: so(des)
This commit is contained in:
parent
39d22d86ab
commit
10cb24248a
@ -645,7 +645,7 @@ update_motd="YES" # update version info in /etc/motd (or NO)
|
||||
entropy_file="/entropy" # Set to NO to disable caching entropy through reboots.
|
||||
# /var/db/entropy-file is preferred if / is not avail.
|
||||
entropy_dir="/var/db/entropy" # Set to NO to disable caching entropy via cron.
|
||||
entropy_save_sz="2048" # Size of the entropy cache files.
|
||||
entropy_save_sz="4096" # Size of the entropy cache files.
|
||||
entropy_save_num="8" # Number of entropy cache files to save.
|
||||
harvest_interrupt="YES" # Entropy device harvests interrupt randomness
|
||||
harvest_ethernet="YES" # Entropy device harvests ethernet randomness
|
||||
|
@ -57,7 +57,6 @@ FILES= DAEMON \
|
||||
hostid_save \
|
||||
hostname \
|
||||
inetd \
|
||||
initrandom \
|
||||
ip6addrctl \
|
||||
ipfilter \
|
||||
ipfs \
|
||||
|
@ -28,7 +28,7 @@
|
||||
#
|
||||
|
||||
# PROVIDE: disks
|
||||
# REQUIRE: initrandom
|
||||
# REQUIRE: random
|
||||
# KEYWORD: nojail
|
||||
|
||||
. /etc/rc.subr
|
||||
|
@ -1,61 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# $FreeBSD$
|
||||
#
|
||||
|
||||
# PROVIDE: initrandom
|
||||
# REQUIRE: dumpon ddb
|
||||
# BEFORE: disks
|
||||
# KEYWORD: nojail
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name="initrandom"
|
||||
start_cmd="initrandom_start"
|
||||
stop_cmd=":"
|
||||
|
||||
initrandom_start()
|
||||
{
|
||||
soft_random_generator=`sysctl kern.random 2>/dev/null`
|
||||
|
||||
echo -n 'Entropy harvesting:'
|
||||
|
||||
if [ \! -z "${soft_random_generator}" ] ; then
|
||||
|
||||
if [ -w /dev/random ]; then
|
||||
if checkyesno harvest_interrupt; then
|
||||
${SYSCTL} kern.random.sys.harvest.interrupt=1 >/dev/null
|
||||
echo -n ' interrupts'
|
||||
else
|
||||
${SYSCTL} kern.random.sys.harvest.interrupt=0 >/dev/null
|
||||
fi
|
||||
|
||||
if checkyesno harvest_ethernet; then
|
||||
${SYSCTL} kern.random.sys.harvest.ethernet=1 >/dev/null
|
||||
echo -n ' ethernet'
|
||||
else
|
||||
${SYSCTL} kern.random.sys.harvest.ethernet=0 >/dev/null
|
||||
fi
|
||||
|
||||
if checkyesno harvest_p_to_p; then
|
||||
${SYSCTL} kern.random.sys.harvest.point_to_point=1 >/dev/null
|
||||
echo -n ' point_to_point'
|
||||
else
|
||||
${SYSCTL} kern.random.sys.harvest.point_to_point=0 >/dev/null
|
||||
fi
|
||||
|
||||
if checkyesno harvest_swi; then
|
||||
${SYSCTL} kern.random.sys.harvest.swi=1 >/dev/null
|
||||
echo -n ' swi'
|
||||
else
|
||||
${SYSCTL} kern.random.sys.harvest.swi=0 >/dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
echo '.'
|
||||
}
|
||||
|
||||
load_rc_config random
|
||||
run_rc_command "$1"
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
# PROVIDE: postrandom
|
||||
# REQUIRE: initrandom random FILESYSTEMS
|
||||
# REQUIRE: random FILESYSTEMS
|
||||
# BEFORE: LOGIN
|
||||
# KEYWORD: nojail
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
#
|
||||
|
||||
# PROVIDE: random
|
||||
# REQUIRE: initrandom FILESYSTEMS
|
||||
# REQUIRE: FILESYSTEMS
|
||||
# BEFORE: netif
|
||||
# KEYWORD: nojail shutdown
|
||||
|
||||
|
@ -53,7 +53,7 @@ case ${entropy_dir} in
|
||||
;;
|
||||
esac
|
||||
|
||||
entropy_save_sz=${entropy_save_sz:-2048}
|
||||
entropy_save_sz=${entropy_save_sz:-4096}
|
||||
entropy_save_num=${entropy_save_num:-8}
|
||||
|
||||
if [ ! -d "${entropy_dir}" ]; then
|
||||
|
@ -35,17 +35,20 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/random.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
|
||||
static int random_example_read(void *, int);
|
||||
static void live_random_example_init(void);
|
||||
static void live_random_example_deinit(void);
|
||||
static u_int live_random_example_read(void *, u_int);
|
||||
|
||||
struct random_adaptor random_example = {
|
||||
.ident = "Example RNG",
|
||||
.source = RANDOM_PURE_BOGUS, /* Make sure this is in
|
||||
* sys/random.h and is unique */
|
||||
.read = random_example_read,
|
||||
struct random_adaptor live_random_example = {
|
||||
.les_ident = "Example RNG",
|
||||
.les_source = RANDOM_PURE_BOGUS, /* Make sure this is in
|
||||
* sys/random.h and is unique */
|
||||
.les_read = live_random_example_read,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -58,8 +61,26 @@ getRandomNumber(void)
|
||||
return 4; /* chosen by fair dice roll, guaranteed to be random */
|
||||
}
|
||||
|
||||
static int
|
||||
random_example_read(void *buf, int c)
|
||||
static void
|
||||
live_random_example_init(void)
|
||||
{
|
||||
|
||||
/* Do initialisation stuff here */
|
||||
}
|
||||
|
||||
static void
|
||||
live_random_example_deinit(void)
|
||||
{
|
||||
|
||||
/* Do de-initialisation stuff here */
|
||||
}
|
||||
|
||||
/* get <c> bytes of random stuff into <buf>. You may presume
|
||||
* that <c> is a multiple of 2^n, with n>=3. A typical value
|
||||
* is c=16.
|
||||
*/
|
||||
static u_int
|
||||
live_random_example_read(void *buf, u_int c)
|
||||
{
|
||||
uint8_t *b;
|
||||
int count;
|
||||
@ -69,22 +90,23 @@ random_example_read(void *buf, int c)
|
||||
for (count = 0; count < c; count++)
|
||||
b[count] = getRandomNumber();
|
||||
|
||||
printf("returning %d bytes of pure randomness\n", c);
|
||||
/* printf("returning %d bytes of pure randomness\n", c); */
|
||||
return (c);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_example_modevent(module_t mod, int type, void *unused)
|
||||
live_random_example_modevent(module_t mod __unused, int type, void *unused __unused)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
live_entropy_source_register(&random_example);
|
||||
live_entropy_source_register(&live_random_example);
|
||||
break;
|
||||
|
||||
case MOD_UNLOAD:
|
||||
live_entropy_source_deregister(&random_example);
|
||||
live_entropy_source_deregister(&live_random_example);
|
||||
break;
|
||||
|
||||
case MOD_SHUTDOWN:
|
||||
@ -98,4 +120,6 @@ random_example_modevent(module_t mod, int type, void *unused)
|
||||
return (error);
|
||||
}
|
||||
|
||||
LIVE_ENTROPY_SRC_MODULE(live_entropy_source_example, random_example_modevent, 1);
|
||||
DEV_MODULE(live_random_example, live_random_example_modevent, NULL);
|
||||
MODULE_VERSION(live_random_example, 1);
|
||||
MODULE_DEPEND(live_random_example, randomdev, 1, 1, 1);
|
||||
|
@ -2993,10 +2993,10 @@ options BROOKTREE_ALLOC_PAGES=(217*4+1)
|
||||
options MAXFILES=999
|
||||
|
||||
# Random number generator
|
||||
options RANDOM_YARROW # Yarrow RNG
|
||||
##options RANDOM_FORTUNA # Fortuna RNG - not yet implemented
|
||||
# Only ONE of the below two may be used; they are mutually exclusive.
|
||||
options RANDOM_YARROW # Yarrow CSPRNG (Default)
|
||||
#options RANDOM_FORTUNA # Fortuna CSPRNG
|
||||
options RANDOM_DEBUG # Debugging messages
|
||||
options RANDOM_RWFILE # Read and write entropy cache
|
||||
|
||||
# Module to enable execution of application via emulators like QEMU
|
||||
options IMAGACT_BINMISC
|
||||
|
@ -2132,16 +2132,15 @@ rt2860.fw optional rt2860fw | ralfw \
|
||||
compile-with "${NORMAL_FW}" \
|
||||
no-obj no-implicit-rule \
|
||||
clean "rt2860.fw"
|
||||
dev/random/harvest.c standard
|
||||
dev/random/dummy_rng.c standard
|
||||
dev/random/randomdev.c standard
|
||||
dev/random/random_adaptors.c standard
|
||||
dev/random/live_entropy_sources.c optional random
|
||||
dev/random/random_harvestq.c optional random
|
||||
dev/random/randomdev.c optional random
|
||||
dev/random/dummy_rng.c standard
|
||||
dev/random/live_entropy_sources.c standard
|
||||
dev/random/random_harvestq.c standard
|
||||
dev/random/randomdev_soft.c optional random
|
||||
dev/random/yarrow.c optional random
|
||||
dev/random/fortuna.c optional random
|
||||
dev/random/hash.c optional random
|
||||
dev/random/rwfile.c optional random
|
||||
dev/rc/rc.c optional rc
|
||||
dev/re/if_re.c optional re
|
||||
dev/rl/if_rl.c optional rl pci
|
||||
|
@ -930,4 +930,3 @@ RCTL opt_global.h
|
||||
RANDOM_YARROW opt_random.h
|
||||
RANDOM_FORTUNA opt_random.h
|
||||
RANDOM_DEBUG opt_random.h
|
||||
RANDOM_RWFILE opt_random.h
|
||||
|
@ -476,7 +476,7 @@ glxsb_rnd(void *v)
|
||||
if (status & SB_RNS_TRNG_VALID) {
|
||||
value = bus_read_4(sc->sc_sr, SB_RANDOM_NUM);
|
||||
/* feed with one uint32 */
|
||||
random_harvest(&value, 4, 32/2, RANDOM_PURE_GLXSB);
|
||||
random_harvest(&value, sizeof(value), 32/2, RANDOM_PURE_GLXSB);
|
||||
}
|
||||
|
||||
callout_reset(&sc->sc_rngco, sc->sc_rnghz, glxsb_rnd, sc);
|
||||
|
24
sys/dev/random/build.sh
Executable file
24
sys/dev/random/build.sh
Executable file
@ -0,0 +1,24 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Basic script to build crude unit tests.
|
||||
#
|
||||
cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_YARROW \
|
||||
-I../.. -lstdthreads -Wall \
|
||||
unit_test.c \
|
||||
yarrow.c \
|
||||
hash.c \
|
||||
../../crypto/rijndael/rijndael-api-fst.c \
|
||||
../../crypto/rijndael/rijndael-alg-fst.c \
|
||||
../../crypto/sha2/sha2.c \
|
||||
../../crypto/sha2/sha256c.c \
|
||||
-o yunit_test
|
||||
cc -g -O0 -pthread -DRANDOM_DEBUG -DRANDOM_FORTUNA \
|
||||
-I../.. -lstdthreads -Wall \
|
||||
unit_test.c \
|
||||
fortuna.c \
|
||||
hash.c \
|
||||
../../crypto/rijndael/rijndael-api-fst.c \
|
||||
../../crypto/rijndael/rijndael-alg-fst.c \
|
||||
../../crypto/sha2/sha2.c \
|
||||
../../crypto/sha2/sha256c.c \
|
||||
-o funit_test
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -27,98 +28,92 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/randomdev.h>
|
||||
|
||||
static struct mtx dummy_random_mtx;
|
||||
|
||||
/* Used to fake out unused random calls in random_adaptor */
|
||||
static void
|
||||
random_null_func(void)
|
||||
{
|
||||
}
|
||||
#include <dev/random/random_adaptors.h>
|
||||
|
||||
static int
|
||||
dummy_random_poll(int events __unused, struct thread *td __unused)
|
||||
dummy_random_zero(void)
|
||||
{
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
dummy_random_block(int flag)
|
||||
static void
|
||||
dummy_random(void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
mtx_lock(&dummy_random_mtx);
|
||||
|
||||
/* Blocking logic */
|
||||
while (!error) {
|
||||
if (flag & O_NONBLOCK)
|
||||
error = EWOULDBLOCK;
|
||||
else {
|
||||
printf("random: dummy device blocking on read.\n");
|
||||
error = msleep(&dummy_random_block,
|
||||
&dummy_random_mtx,
|
||||
PUSER | PCATCH, "block", 0);
|
||||
}
|
||||
}
|
||||
mtx_unlock(&dummy_random_mtx);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
dummy_random_init(void)
|
||||
{
|
||||
|
||||
mtx_init(&dummy_random_mtx, "sleep mtx for dummy_random",
|
||||
NULL, MTX_DEF);
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
randomdev_init_reader(dummy_random_read_phony);
|
||||
}
|
||||
|
||||
static void
|
||||
dummy_random_deinit(void)
|
||||
/* This is used only by the internal read_random(9) call, and then only
|
||||
* if no entropy processor is loaded.
|
||||
*
|
||||
* Make a token effort to provide _some_ kind of output. No warranty of
|
||||
* the quality of this output is made, mainly because its lousy.
|
||||
*
|
||||
* This is only used by the internal read_random(9) call when no other
|
||||
* adaptor is active.
|
||||
*
|
||||
* It has external scope due to the way things work in
|
||||
* randomdev_[de]init_reader() that the rest of the world doesn't need to
|
||||
* know about.
|
||||
*
|
||||
* Caveat Emptor.
|
||||
*/
|
||||
u_int
|
||||
dummy_random_read_phony(uint8_t *buf, u_int count)
|
||||
{
|
||||
/* If no entropy device is loaded, don't spam the console with warnings */
|
||||
static int warned = 0;
|
||||
u_long randval;
|
||||
size_t size, i;
|
||||
|
||||
mtx_destroy(&dummy_random_mtx);
|
||||
}
|
||||
|
||||
struct random_adaptor dummy_random = {
|
||||
.ident = "Dummy entropy device that always blocks",
|
||||
.init = dummy_random_init,
|
||||
.deinit = dummy_random_deinit,
|
||||
.block = dummy_random_block,
|
||||
.poll = dummy_random_poll,
|
||||
.read = (random_read_func_t *)random_null_func,
|
||||
.reseed = (random_reseed_func_t *)random_null_func,
|
||||
.seeded = 0, /* This device can never be seeded */
|
||||
.priority = 1, /* Bottom priority, so goes to last position */
|
||||
};
|
||||
|
||||
static int
|
||||
dummy_random_modevent(module_t mod __unused, int type, void *unused __unused)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
random_adaptor_register("dummy", &dummy_random);
|
||||
EVENTHANDLER_INVOKE(random_adaptor_attach,
|
||||
&dummy_random);
|
||||
|
||||
return (0);
|
||||
if (!warned) {
|
||||
log(LOG_WARNING, "random device not loaded/active; using insecure pseudo-random number generator\n");
|
||||
warned = 1;
|
||||
}
|
||||
|
||||
return (EINVAL);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
RANDOM_ADAPTOR_MODULE(dummy, dummy_random_modevent, 1);
|
||||
struct random_adaptor randomdev_dummy = {
|
||||
.ra_ident = "Dummy",
|
||||
.ra_priority = 1, /* Bottom priority, so goes to last position */
|
||||
.ra_reseed = dummy_random,
|
||||
.ra_seeded = (random_adaptor_seeded_func_t *)dummy_random_zero,
|
||||
.ra_read = (random_adaptor_read_func_t *)dummy_random_zero,
|
||||
.ra_write = (random_adaptor_write_func_t *)dummy_random_zero,
|
||||
.ra_init = dummy_random_init,
|
||||
.ra_deinit = dummy_random,
|
||||
};
|
||||
|
433
sys/dev/random/fortuna.c
Normal file
433
sys/dev/random/fortuna.c
Normal file
@ -0,0 +1,433 @@
|
||||
/*-
|
||||
* Copyright (c) 2013-2014 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#include <crypto/rijndael/rijndael-api-fst.h>
|
||||
#include <crypto/sha2/sha2.h>
|
||||
|
||||
#include <dev/random/hash.h>
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/uint128.h>
|
||||
#include <dev/random/fortuna.h>
|
||||
#else /* !_KERNEL */
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include "unit_test.h"
|
||||
|
||||
#include <crypto/rijndael/rijndael-api-fst.h>
|
||||
#include <crypto/sha2/sha2.h>
|
||||
|
||||
#include <dev/random/hash.h>
|
||||
#include <dev/random/uint128.h>
|
||||
#include <dev/random/fortuna.h>
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
|
||||
#define RANDOM_YARROW
|
||||
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
|
||||
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
|
||||
#endif
|
||||
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
|
||||
#define NPOOLS 32
|
||||
#define MINPOOLSIZE 64
|
||||
#define DEFPOOLSIZE 256
|
||||
#define MAXPOOLSIZE 65536
|
||||
|
||||
/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
|
||||
CTASSERT(BLOCKSIZE == sizeof(uint128_t));
|
||||
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
|
||||
|
||||
/* This is the beastie that needs protecting. It contains all of the
|
||||
* state that we are excited about.
|
||||
* Exactly one is instantiated.
|
||||
*/
|
||||
static struct fortuna_state {
|
||||
/* P_i */
|
||||
struct pool {
|
||||
u_int length;
|
||||
struct randomdev_hash hash;
|
||||
} pool[NPOOLS];
|
||||
|
||||
/* ReseedCnt */
|
||||
u_int reseedcount;
|
||||
|
||||
/* C - 128 bits */
|
||||
union {
|
||||
uint8_t byte[BLOCKSIZE];
|
||||
uint128_t whole;
|
||||
} counter;
|
||||
|
||||
/* K */
|
||||
struct randomdev_key key;
|
||||
|
||||
/* Extras */
|
||||
u_int minpoolsize;
|
||||
|
||||
/* Extras for the OS */
|
||||
|
||||
#ifdef _KERNEL
|
||||
/* For use when 'pacing' the reseeds */
|
||||
sbintime_t lasttime;
|
||||
#endif
|
||||
} fortuna_state;
|
||||
|
||||
/* The random_reseed_mtx mutex protects seeding and polling/blocking. */
|
||||
static mtx_t random_reseed_mtx;
|
||||
|
||||
static struct fortuna_start_cache {
|
||||
uint8_t junk[PAGE_SIZE];
|
||||
size_t length;
|
||||
struct randomdev_hash hash;
|
||||
} fortuna_start_cache;
|
||||
|
||||
#ifdef _KERNEL
|
||||
static struct sysctl_ctx_list random_clist;
|
||||
RANDOM_CHECK_UINT(minpoolsize, MINPOOLSIZE, MAXPOOLSIZE);
|
||||
#endif
|
||||
|
||||
void
|
||||
random_fortuna_init_alg(void)
|
||||
{
|
||||
int i;
|
||||
#ifdef _KERNEL
|
||||
struct sysctl_oid *random_fortuna_o;
|
||||
#endif
|
||||
|
||||
memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
|
||||
fortuna_start_cache.length = 0U;
|
||||
randomdev_hash_init(&fortuna_start_cache.hash);
|
||||
|
||||
/* Set up a lock for the reseed process */
|
||||
#ifdef _KERNEL
|
||||
mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
|
||||
#else /* !_KERNEL */
|
||||
mtx_init(&random_reseed_mtx, mtx_plain);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#ifdef _KERNEL
|
||||
/* Fortuna parameters. Do not adjust these unless you have
|
||||
* have a very good clue about what they do!
|
||||
*/
|
||||
random_fortuna_o = SYSCTL_ADD_NODE(&random_clist,
|
||||
SYSCTL_STATIC_CHILDREN(_kern_random),
|
||||
OID_AUTO, "fortuna", CTLFLAG_RW, 0,
|
||||
"Fortuna Parameters");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_fortuna_o), OID_AUTO,
|
||||
"minpoolsize", CTLTYPE_UINT|CTLFLAG_RW,
|
||||
&fortuna_state.minpoolsize, DEFPOOLSIZE,
|
||||
random_check_uint_minpoolsize, "IU",
|
||||
"Minimum pool size necessary to cause a reseed automatically");
|
||||
|
||||
fortuna_state.lasttime = 0U;
|
||||
#endif
|
||||
|
||||
fortuna_state.minpoolsize = DEFPOOLSIZE;
|
||||
|
||||
/* F&S - InitializePRNG() */
|
||||
|
||||
/* F&S - P_i = \epsilon */
|
||||
for (i = 0; i < NPOOLS; i++) {
|
||||
randomdev_hash_init(&fortuna_state.pool[i].hash);
|
||||
fortuna_state.pool[i].length = 0U;
|
||||
}
|
||||
|
||||
/* F&S - ReseedCNT = 0 */
|
||||
fortuna_state.reseedcount = 0U;
|
||||
|
||||
/* F&S - InitializeGenerator() */
|
||||
|
||||
/* F&S - C = 0 */
|
||||
uint128_clear(&fortuna_state.counter.whole);
|
||||
|
||||
/* F&S - K = 0 */
|
||||
memset(&fortuna_state.key, 0, sizeof(fortuna_state.key));
|
||||
}
|
||||
|
||||
void
|
||||
random_fortuna_deinit_alg(void)
|
||||
{
|
||||
|
||||
mtx_destroy(&random_reseed_mtx);
|
||||
memset(&fortuna_state, 0, sizeof(fortuna_state));
|
||||
}
|
||||
|
||||
/* F&S - AddRandomEvent() */
|
||||
/* Process a single stochastic event off the harvest queue */
|
||||
void
|
||||
random_fortuna_process_event(struct harvest_event *event)
|
||||
{
|
||||
u_int pl;
|
||||
|
||||
/* We must be locked for all this as plenty of state gets messed with */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
/* Accumulate the event into the appropriate pool
|
||||
* where each event carries the destination information
|
||||
*/
|
||||
/* F&S - P_i = P_i|<harvested stuff> */
|
||||
/* The hash_init and hash_finish are done in random_fortuna_read() below */
|
||||
pl = event->he_destination % NPOOLS;
|
||||
randomdev_hash_iterate(&fortuna_state.pool[pl].hash, event, sizeof(*event));
|
||||
/* No point in counting above the outside maximum */
|
||||
fortuna_state.pool[pl].length += event->he_size;
|
||||
fortuna_state.pool[pl].length = MIN(fortuna_state.pool[pl].length, MAXPOOLSIZE);
|
||||
|
||||
/* Done with state-messing */
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
}
|
||||
|
||||
/* F&S - Reseed() */
|
||||
/* Reseed Mutex is held */
|
||||
static void
|
||||
reseed(uint8_t *junk, u_int length)
|
||||
{
|
||||
struct randomdev_hash context;
|
||||
uint8_t hash[KEYSIZE], temp[KEYSIZE];
|
||||
|
||||
KASSERT(fortuna_state.minpoolsize > 0, ("random: Fortuna threshold = 0"));
|
||||
#ifdef _KERNEL
|
||||
mtx_assert(&random_reseed_mtx, MA_OWNED);
|
||||
#endif
|
||||
|
||||
/* F&S - temp = H(K|s) */
|
||||
randomdev_hash_init(&context);
|
||||
randomdev_hash_iterate(&context, &fortuna_state.key, sizeof(fortuna_state.key));
|
||||
randomdev_hash_iterate(&context, junk, length);
|
||||
randomdev_hash_finish(&context, temp);
|
||||
|
||||
/* F&S - hash = H(temp) */
|
||||
randomdev_hash_init(&context);
|
||||
randomdev_hash_iterate(&context, temp, KEYSIZE);
|
||||
randomdev_hash_finish(&context, hash);
|
||||
|
||||
/* F&S - K = hash */
|
||||
randomdev_encrypt_init(&fortuna_state.key, temp);
|
||||
memset(temp, 0, sizeof(temp));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
|
||||
/* Unblock the device if it was blocked due to being unseeded */
|
||||
if (uint128_is_zero(fortuna_state.counter.whole))
|
||||
random_adaptor_unblock();
|
||||
/* F&S - C = C + 1 */
|
||||
uint128_increment(&fortuna_state.counter.whole);
|
||||
}
|
||||
|
||||
/* F&S - GenerateBlocks() */
|
||||
/* Reseed Mutex is held, and buf points to a whole number of blocks. */
|
||||
static __inline void
|
||||
random_fortuna_genblocks(uint8_t *buf, u_int blockcount)
|
||||
{
|
||||
u_int i;
|
||||
|
||||
for (i = 0u; i < blockcount; i++) {
|
||||
/* F&S - r = r|E(K,C) */
|
||||
randomdev_encrypt(&fortuna_state.key, fortuna_state.counter.byte, buf, BLOCKSIZE);
|
||||
buf += BLOCKSIZE;
|
||||
|
||||
/* F&S - C = C + 1 */
|
||||
uint128_increment(&fortuna_state.counter.whole);
|
||||
}
|
||||
}
|
||||
|
||||
/* F&S - PseudoRandomData() */
|
||||
/* Reseed Mutex is held, and buf points to a whole number of blocks. */
|
||||
static __inline void
|
||||
random_fortuna_genrandom(uint8_t *buf, u_int bytecount)
|
||||
{
|
||||
static uint8_t temp[BLOCKSIZE*(KEYSIZE/BLOCKSIZE)];
|
||||
u_int blockcount;
|
||||
|
||||
/* F&S - assert(n < 2^20) */
|
||||
KASSERT((bytecount <= (1 << 20)), ("invalid single read request to fortuna of %d bytes", bytecount));
|
||||
|
||||
/* F&S - r = first-n-bytes(GenerateBlocks(ceil(n/16))) */
|
||||
blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE;
|
||||
random_fortuna_genblocks(buf, blockcount);
|
||||
|
||||
/* F&S - K = GenerateBlocks(2) */
|
||||
random_fortuna_genblocks(temp, KEYSIZE/BLOCKSIZE);
|
||||
randomdev_encrypt_init(&fortuna_state.key, temp);
|
||||
memset(temp, 0, sizeof(temp));
|
||||
}
|
||||
|
||||
/* F&S - RandomData() */
|
||||
/* Used to return processed entropy from the PRNG */
|
||||
/* The argument buf points to a whole number of blocks. */
|
||||
void
|
||||
random_fortuna_read(uint8_t *buf, u_int bytecount)
|
||||
{
|
||||
#ifdef _KERNEL
|
||||
sbintime_t thistime;
|
||||
#endif
|
||||
struct randomdev_hash context;
|
||||
uint8_t s[NPOOLS*KEYSIZE], temp[KEYSIZE];
|
||||
int i;
|
||||
u_int seedlength;
|
||||
|
||||
/* We must be locked for all this as plenty of state gets messed with */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
/* if buf == NULL and bytecount == 0 then this is the pre-read. */
|
||||
/* if buf == NULL and bytecount != 0 then this is the post-read; ignore. */
|
||||
if (buf == NULL) {
|
||||
if (bytecount == 0) {
|
||||
if (fortuna_state.pool[0].length >= fortuna_state.minpoolsize
|
||||
#ifdef _KERNEL
|
||||
/* F&S - Use 'getsbinuptime()' to prevent reseed-spamming. */
|
||||
&& ((thistime = getsbinuptime()) - fortuna_state.lasttime > hz/10)
|
||||
#endif
|
||||
) {
|
||||
#ifdef _KERNEL
|
||||
fortuna_state.lasttime = thistime;
|
||||
#endif
|
||||
|
||||
seedlength = 0U;
|
||||
/* F&S - ReseedCNT = ReseedCNT + 1 */
|
||||
fortuna_state.reseedcount++;
|
||||
/* s = \epsilon by default */
|
||||
for (i = 0; i < NPOOLS; i++) {
|
||||
/* F&S - if Divides(ReseedCnt, 2^i) ... */
|
||||
if ((fortuna_state.reseedcount % (1 << i)) == 0U) {
|
||||
seedlength += KEYSIZE;
|
||||
/* F&S - temp = (P_i) */
|
||||
randomdev_hash_finish(&fortuna_state.pool[i].hash, temp);
|
||||
/* F&S - P_i = \epsilon */
|
||||
randomdev_hash_init(&fortuna_state.pool[i].hash);
|
||||
fortuna_state.pool[i].length = 0U;
|
||||
/* F&S - s = s|H(temp) */
|
||||
randomdev_hash_init(&context);
|
||||
randomdev_hash_iterate(&context, temp, KEYSIZE);
|
||||
randomdev_hash_finish(&context, s + i*KEYSIZE);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: active reseed: reseedcount [%d] ", fortuna_state.reseedcount);
|
||||
for (i = 0; i < NPOOLS; i++)
|
||||
printf(" %d", fortuna_state.pool[i].length);
|
||||
printf("\n");
|
||||
#endif
|
||||
/* F&S */
|
||||
reseed(s, seedlength);
|
||||
|
||||
/* Clean up */
|
||||
memset(s, 0, seedlength);
|
||||
seedlength = 0U;
|
||||
memset(temp, 0, sizeof(temp));
|
||||
memset(&context, 0, sizeof(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* if buf != NULL do a regular read. */
|
||||
else
|
||||
random_fortuna_genrandom(buf, bytecount);
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
}
|
||||
|
||||
/* Internal function to hand external entropy to the PRNG */
|
||||
void
|
||||
random_fortuna_write(uint8_t *buf, u_int count)
|
||||
{
|
||||
uint8_t temp[KEYSIZE];
|
||||
int i;
|
||||
uintmax_t timestamp;
|
||||
|
||||
timestamp = get_cyclecount();
|
||||
randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp));
|
||||
randomdev_hash_iterate(&fortuna_start_cache.hash, buf, count);
|
||||
timestamp = get_cyclecount();
|
||||
randomdev_hash_iterate(&fortuna_start_cache.hash, ×tamp, sizeof(timestamp));
|
||||
randomdev_hash_finish(&fortuna_start_cache.hash, temp);
|
||||
for (i = 0; i < KEYSIZE; i++)
|
||||
fortuna_start_cache.junk[(fortuna_start_cache.length + i)%PAGE_SIZE] ^= temp[i];
|
||||
fortuna_start_cache.length += KEYSIZE;
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s - ", __func__);
|
||||
for (i = 0; i < KEYSIZE; i++)
|
||||
printf("%02X", temp[i]);
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
memset(temp, 0, KEYSIZE);
|
||||
|
||||
/* We must be locked for all this as plenty of state gets messed with */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
randomdev_hash_init(&fortuna_start_cache.hash);
|
||||
|
||||
reseed(fortuna_start_cache.junk, MIN(PAGE_SIZE, fortuna_start_cache.length));
|
||||
memset(fortuna_start_cache.junk, 0, sizeof(fortuna_start_cache.junk));
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
random_fortuna_reseed(void)
|
||||
{
|
||||
|
||||
/* CWOT */
|
||||
}
|
||||
|
||||
int
|
||||
random_fortuna_seeded(void)
|
||||
{
|
||||
|
||||
return (!uint128_is_zero(fortuna_state.counter.whole));
|
||||
}
|
||||
|
||||
#endif /* RANDOM_FORTUNA */
|
@ -26,14 +26,19 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef SYS_DEV_RANDOM_RWFILE_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_RWFILE_H_INCLUDED
|
||||
|
||||
#ifdef RANDOM_RWFILE
|
||||
|
||||
int randomdev_read_file(const char *filename, void *buf, size_t);
|
||||
int randomdev_write_file(const char *filename, void *buf, size_t);
|
||||
#ifndef SYS_DEV_RANDOM_FORTUNA_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_FORTUNA_H_INCLUDED
|
||||
|
||||
#ifdef _KERNEL
|
||||
typedef struct mtx mtx_t;
|
||||
#endif
|
||||
|
||||
void random_fortuna_init_alg(void);
|
||||
void random_fortuna_deinit_alg(void);
|
||||
void random_fortuna_read(uint8_t *, u_int);
|
||||
void random_fortuna_write(uint8_t *, u_int);
|
||||
void random_fortuna_reseed(void);
|
||||
int random_fortuna_seeded(void);
|
||||
void random_fortuna_process_event(struct harvest_event *event);
|
||||
|
||||
#endif
|
@ -1,141 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2000-2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
|
||||
static int read_random_phony(void *, int);
|
||||
|
||||
/* Structure holding the desired entropy sources */
|
||||
struct harvest_select harvest = { 1, 1, 1, 1 };
|
||||
static int warned = 0;
|
||||
|
||||
/* hold the address of the routine which is actually called if
|
||||
* the randomdev is loaded
|
||||
*/
|
||||
static void (*reap_func)(u_int64_t, const void *, u_int, u_int,
|
||||
enum esource) = NULL;
|
||||
static int (*read_func)(void *, int) = read_random_phony;
|
||||
|
||||
/* Initialise the harvester at load time */
|
||||
void
|
||||
randomdev_init_harvester(void (*reaper)(u_int64_t, const void *, u_int,
|
||||
u_int, enum esource), int (*reader)(void *, int))
|
||||
{
|
||||
reap_func = reaper;
|
||||
read_func = reader;
|
||||
}
|
||||
|
||||
/* Deinitialise the harvester at unload time */
|
||||
void
|
||||
randomdev_deinit_harvester(void)
|
||||
{
|
||||
reap_func = NULL;
|
||||
read_func = read_random_phony;
|
||||
warned = 0;
|
||||
}
|
||||
|
||||
/* Entropy harvesting routine. This is supposed to be fast; do
|
||||
* not do anything slow in here!
|
||||
* Implemented as in indirect call to allow non-inclusion of
|
||||
* the entropy device.
|
||||
*
|
||||
* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
|
||||
* counters are built in, but on older hardware it will do a real time clock
|
||||
* read which can be quite expensive.
|
||||
*/
|
||||
void
|
||||
random_harvest(const void *entropy, u_int count, u_int bits, enum esource origin)
|
||||
{
|
||||
if (reap_func)
|
||||
(*reap_func)(get_cyclecount(), entropy, count, bits, origin);
|
||||
}
|
||||
|
||||
/* Userland-visible version of read_random */
|
||||
int
|
||||
read_random(void *buf, int count)
|
||||
{
|
||||
return ((*read_func)(buf, count));
|
||||
}
|
||||
|
||||
/* If the entropy device is not loaded, make a token effort to
|
||||
* provide _some_ kind of randomness. This should only be used
|
||||
* inside other RNG's, like arc4random(9).
|
||||
*/
|
||||
static int
|
||||
read_random_phony(void *buf, int count)
|
||||
{
|
||||
u_long randval;
|
||||
int size, i;
|
||||
|
||||
if (!warned) {
|
||||
log(LOG_WARNING, "random device not loaded; using insecure entropy\n");
|
||||
warned = 1;
|
||||
}
|
||||
|
||||
/* srandom() is called in kern/init_main.c:proc0_post() */
|
||||
|
||||
/* Fill buf[] with random(9) output */
|
||||
for (i = 0; i < count; i+= (int)sizeof(u_long)) {
|
||||
randval = random();
|
||||
size = MIN(count - i, sizeof(u_long));
|
||||
memcpy(&((char *)buf)[i], &randval, (size_t)size);
|
||||
}
|
||||
|
||||
return (count);
|
||||
}
|
||||
|
||||
/* Helper routine to enable kproc_exit() to work while the module is
|
||||
* being (or has been) unloaded.
|
||||
* This routine is in this file because it is always linked into the kernel,
|
||||
* and will thus never be unloaded. This is critical for unloadable modules
|
||||
* that have threads.
|
||||
*/
|
||||
void
|
||||
random_set_wakeup_exit(void *control)
|
||||
{
|
||||
wakeup(control);
|
||||
kproc_exit(0);
|
||||
/* NOTREACHED */
|
||||
}
|
@ -28,18 +28,33 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#else /* !_KERNEL */
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <threads.h>
|
||||
#include "unit_test.h"
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#include <crypto/rijndael/rijndael-api-fst.h>
|
||||
#include <crypto/sha2/sha2.h>
|
||||
|
||||
#include <dev/random/hash.h>
|
||||
|
||||
/* This code presumes that KEYSIZE is twice as large as BLOCKSIZE */
|
||||
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
|
||||
|
||||
/* Initialise the hash */
|
||||
void
|
||||
randomdev_hash_init(struct randomdev_hash *context)
|
||||
{
|
||||
|
||||
SHA256_Init(&context->sha);
|
||||
}
|
||||
|
||||
@ -47,6 +62,7 @@ randomdev_hash_init(struct randomdev_hash *context)
|
||||
void
|
||||
randomdev_hash_iterate(struct randomdev_hash *context, void *data, size_t size)
|
||||
{
|
||||
|
||||
SHA256_Update(&context->sha, data, size);
|
||||
}
|
||||
|
||||
@ -56,6 +72,7 @@ randomdev_hash_iterate(struct randomdev_hash *context, void *data, size_t size)
|
||||
void
|
||||
randomdev_hash_finish(struct randomdev_hash *context, void *buf)
|
||||
{
|
||||
|
||||
SHA256_Final(buf, &context->sha);
|
||||
}
|
||||
|
||||
@ -66,6 +83,7 @@ randomdev_hash_finish(struct randomdev_hash *context, void *buf)
|
||||
void
|
||||
randomdev_encrypt_init(struct randomdev_key *context, void *data)
|
||||
{
|
||||
|
||||
rijndael_cipherInit(&context->cipher, MODE_CBC, NULL);
|
||||
rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data);
|
||||
}
|
||||
@ -75,7 +93,8 @@ randomdev_encrypt_init(struct randomdev_key *context, void *data)
|
||||
* a multiple of BLOCKSIZE.
|
||||
*/
|
||||
void
|
||||
randomdev_encrypt(struct randomdev_key *context, void *d_in, void *d_out, unsigned length)
|
||||
randomdev_encrypt(struct randomdev_key *context, void *d_in, void *d_out, u_int length)
|
||||
{
|
||||
|
||||
rijndael_blockEncrypt(&context->cipher, &context->key, d_in, length*8, d_out);
|
||||
}
|
||||
|
@ -45,6 +45,6 @@ void randomdev_hash_init(struct randomdev_hash *);
|
||||
void randomdev_hash_iterate(struct randomdev_hash *, void *, size_t);
|
||||
void randomdev_hash_finish(struct randomdev_hash *, void *);
|
||||
void randomdev_encrypt_init(struct randomdev_key *, void *);
|
||||
void randomdev_encrypt(struct randomdev_key *context, void *, void *, unsigned);
|
||||
void randomdev_encrypt(struct randomdev_key *context, void *, void *, u_int);
|
||||
|
||||
#endif
|
||||
|
@ -35,11 +35,11 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/md_var.h>
|
||||
@ -47,18 +47,17 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
|
||||
#define RETRY_COUNT 10
|
||||
|
||||
static int random_ivy_read(void *, int);
|
||||
static u_int random_ivy_read(void *, u_int);
|
||||
|
||||
static struct random_hardware_source random_ivy = {
|
||||
.ident = "Hardware, Intel Secure Key RNG",
|
||||
.source = RANDOM_PURE_RDRAND,
|
||||
.read = random_ivy_read
|
||||
static struct live_entropy_source random_ivy = {
|
||||
.les_ident = "Intel Secure Key RNG",
|
||||
.les_source = RANDOM_PURE_RDRAND,
|
||||
.les_read = random_ivy_read
|
||||
};
|
||||
|
||||
static inline int
|
||||
@ -86,15 +85,17 @@ ivy_rng_store(long *buf)
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
random_ivy_read(void *buf, int c)
|
||||
/* It is specifically allowed that buf is a multiple of sizeof(long) */
|
||||
static u_int
|
||||
random_ivy_read(void *buf, u_int c)
|
||||
{
|
||||
long *b;
|
||||
int count;
|
||||
u_int count;
|
||||
|
||||
KASSERT(c % sizeof(long) == 0, ("partial read %d", c));
|
||||
for (b = buf, count = c; count > 0; count -= sizeof(long), b++) {
|
||||
if (ivy_rng_store(b) == 0)
|
||||
KASSERT(c % sizeof(*b) == 0, ("partial read %d", c));
|
||||
b = buf;
|
||||
for (count = c; count > 0; count -= sizeof(*b)) {
|
||||
if (ivy_rng_store(b++) == 0)
|
||||
break;
|
||||
}
|
||||
return (c - count);
|
||||
@ -107,14 +108,10 @@ rdrand_modevent(module_t mod, int type, void *unused)
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
if (cpu_feature2 & CPUID2_RDRAND)
|
||||
if (cpu_feature2 & CPUID2_RDRAND) {
|
||||
live_entropy_source_register(&random_ivy);
|
||||
else
|
||||
#ifndef KLD_MODULE
|
||||
if (bootverbose)
|
||||
#endif
|
||||
printf("%s: RDRAND is not present\n",
|
||||
random_ivy.ident);
|
||||
printf("random: live provider: \"%s\"\n", random_ivy.les_ident);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOD_UNLOAD:
|
||||
@ -134,4 +131,6 @@ rdrand_modevent(module_t mod, int type, void *unused)
|
||||
return (error);
|
||||
}
|
||||
|
||||
LIVE_ENTROPY_SRC_MODULE(random_rdrand, rdrand_modevent, 1);
|
||||
DEV_MODULE(rdrand, rdrand_modevent, NULL);
|
||||
MODULE_VERSION(rdrand, 1);
|
||||
MODULE_DEPEND(rdrand, random_adaptors, 1, 1, 1);
|
||||
|
@ -28,13 +28,16 @@
|
||||
#include <sys/param.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
@ -49,89 +52,73 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include "live_entropy_sources.h"
|
||||
|
||||
LIST_HEAD(les_head, live_entropy_sources);
|
||||
static struct les_head sources = LIST_HEAD_INITIALIZER(sources);
|
||||
|
||||
/*
|
||||
* The live_lock protects the consistency of the "struct les_head sources"
|
||||
* The les_lock protects the consistency of the "struct les_head les_sources"
|
||||
*/
|
||||
static struct sx les_lock; /* need a sleepable lock */
|
||||
static struct sx les_lock; /* Need a sleepable lock for the sbuf/sysctl stuff. */
|
||||
|
||||
LIST_HEAD(les_head, live_entropy_sources);
|
||||
static struct les_head les_sources = LIST_HEAD_INITIALIZER(les_sources);
|
||||
|
||||
void
|
||||
live_entropy_source_register(struct random_hardware_source *rsource)
|
||||
live_entropy_source_register(struct live_entropy_source *rsource)
|
||||
{
|
||||
struct live_entropy_sources *les;
|
||||
struct live_entropy_sources *lles;
|
||||
|
||||
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
|
||||
|
||||
les = malloc(sizeof(struct live_entropy_sources), M_ENTROPY, M_WAITOK);
|
||||
les->rsource = rsource;
|
||||
lles = malloc(sizeof(*lles), M_ENTROPY, M_WAITOK);
|
||||
lles->lles_rsource = rsource;
|
||||
|
||||
sx_xlock(&les_lock);
|
||||
LIST_INSERT_HEAD(&sources, les, entries);
|
||||
LIST_INSERT_HEAD(&les_sources, lles, lles_entries);
|
||||
sx_xunlock(&les_lock);
|
||||
}
|
||||
|
||||
void
|
||||
live_entropy_source_deregister(struct random_hardware_source *rsource)
|
||||
live_entropy_source_deregister(struct live_entropy_source *rsource)
|
||||
{
|
||||
struct live_entropy_sources *les = NULL;
|
||||
struct live_entropy_sources *lles = NULL;
|
||||
|
||||
KASSERT(rsource != NULL, ("invalid input to %s", __func__));
|
||||
|
||||
sx_xlock(&les_lock);
|
||||
LIST_FOREACH(les, &sources, entries)
|
||||
if (les->rsource == rsource) {
|
||||
LIST_REMOVE(les, entries);
|
||||
LIST_FOREACH(lles, &les_sources, lles_entries)
|
||||
if (lles->lles_rsource == rsource) {
|
||||
LIST_REMOVE(lles, lles_entries);
|
||||
break;
|
||||
}
|
||||
sx_xunlock(&les_lock);
|
||||
if (les != NULL)
|
||||
free(les, M_ENTROPY);
|
||||
if (lles != NULL)
|
||||
free(lles, M_ENTROPY);
|
||||
}
|
||||
|
||||
static int
|
||||
live_entropy_source_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct live_entropy_sources *les;
|
||||
struct live_entropy_sources *lles;
|
||||
struct sbuf sbuf;
|
||||
int error, count;
|
||||
|
||||
count = error = 0;
|
||||
|
||||
sx_slock(&les_lock);
|
||||
|
||||
if (LIST_EMPTY(&sources))
|
||||
error = SYSCTL_OUT(req, "", 0);
|
||||
else {
|
||||
LIST_FOREACH(les, &sources, entries) {
|
||||
sbuf_new_for_sysctl(&sbuf, NULL, 64, req);
|
||||
|
||||
error = SYSCTL_OUT(req, ",", count++ ? 1 : 0);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
error = SYSCTL_OUT(req, les->rsource->ident, strlen(les->rsource->ident));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
count = 0;
|
||||
LIST_FOREACH(lles, &les_sources, lles_entries) {
|
||||
sbuf_cat(&sbuf, (count++ ? ",'" : "'"));
|
||||
sbuf_cat(&sbuf, lles->lles_rsource->les_ident);
|
||||
sbuf_cat(&sbuf, "'");
|
||||
}
|
||||
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
|
||||
sx_sunlock(&les_lock);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
live_entropy_sources_init(void *unused)
|
||||
{
|
||||
|
||||
SYSCTL_PROC(_kern_random, OID_AUTO, live_entropy_sources,
|
||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, live_entropy_source_handler, "",
|
||||
"List of Active Live Entropy Sources");
|
||||
|
||||
sx_init(&les_lock, "live_entropy_sources");
|
||||
}
|
||||
|
||||
/*
|
||||
* Run through all "live" sources reading entropy for the given
|
||||
* number of rounds, which should be a multiple of the number
|
||||
@ -140,15 +127,18 @@ live_entropy_sources_init(void *unused)
|
||||
*
|
||||
* BEWARE!!!
|
||||
* This function runs inside the RNG thread! Don't do anything silly!
|
||||
* Remember that we are NOT holding harvest_mtx on entry!
|
||||
*/
|
||||
/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
|
||||
* counters are built in, but on older hardware it will do a real time clock
|
||||
* read which can be quite expensive.
|
||||
*/
|
||||
void
|
||||
live_entropy_sources_feed(int rounds, event_proc_f entropy_processor)
|
||||
live_entropy_sources_feed(void)
|
||||
{
|
||||
static struct harvest event;
|
||||
static uint8_t buf[HARVESTSIZE];
|
||||
struct live_entropy_sources *les;
|
||||
int i, n;
|
||||
static struct harvest_event event;
|
||||
struct live_entropy_sources *lles;
|
||||
int i, read_rate;
|
||||
u_int n;
|
||||
|
||||
sx_slock(&les_lock);
|
||||
|
||||
@ -156,25 +146,23 @@ live_entropy_sources_feed(int rounds, event_proc_f entropy_processor)
|
||||
* Walk over all of live entropy sources, and feed their output
|
||||
* to the system-wide RNG.
|
||||
*/
|
||||
LIST_FOREACH(les, &sources, entries) {
|
||||
read_rate = random_adaptor_read_rate();
|
||||
LIST_FOREACH(lles, &les_sources, lles_entries) {
|
||||
|
||||
for (i = 0; i < rounds; i++) {
|
||||
/*
|
||||
* This should be quick, since it's a live entropy
|
||||
* source.
|
||||
*/
|
||||
/* FIXME: Whine loudly if this didn't work. */
|
||||
n = les->rsource->read(buf, sizeof(buf));
|
||||
n = MIN(n, HARVESTSIZE);
|
||||
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.somecounter = get_cyclecount();
|
||||
event.size = n;
|
||||
event.bits = (n*8)/2;
|
||||
event.source = les->rsource->source;
|
||||
memcpy(event.entropy, buf, n);
|
||||
event.he_somecounter = get_cyclecount();
|
||||
event.he_size = n;
|
||||
event.he_bits = (n*8)/2;
|
||||
event.he_source = lles->lles_rsource->les_source;
|
||||
event.he_destination = harvest_destination[event.he_source]++;
|
||||
|
||||
/* Do the actual entropy insertion */
|
||||
entropy_processor(&event);
|
||||
harvest_process_event(&event);
|
||||
}
|
||||
|
||||
}
|
||||
@ -182,14 +170,21 @@ live_entropy_sources_feed(int rounds, event_proc_f entropy_processor)
|
||||
sx_sunlock(&les_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
live_entropy_sources_deinit(void *unused)
|
||||
void
|
||||
live_entropy_sources_init(void)
|
||||
{
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST,
|
||||
live_entropy_sources_init, NULL);
|
||||
SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST,
|
||||
live_entropy_sources_deinit, NULL);
|
||||
|
@ -30,31 +30,30 @@
|
||||
#ifndef SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED
|
||||
|
||||
typedef u_int random_live_read_func_t(void *, u_int);
|
||||
|
||||
/*
|
||||
* Live entropy source is a source of entropy that can provide
|
||||
* specified or approximate amount of entropy immediately upon request or within
|
||||
* an acceptable amount of time.
|
||||
*/
|
||||
struct live_entropy_source {
|
||||
const char *les_ident;
|
||||
enum random_entropy_source les_source;
|
||||
random_live_read_func_t *les_read;
|
||||
};
|
||||
|
||||
struct live_entropy_sources {
|
||||
LIST_ENTRY(live_entropy_sources) entries; /* list of providers */
|
||||
struct random_hardware_source *rsource; /* associated random adaptor */
|
||||
LIST_ENTRY(live_entropy_sources) lles_entries; /* list of providers */
|
||||
struct live_entropy_source *lles_rsource; /* associated random adaptor */
|
||||
};
|
||||
|
||||
extern struct mtx live_mtx;
|
||||
|
||||
void live_entropy_source_register(struct random_hardware_source *);
|
||||
void live_entropy_source_deregister(struct random_hardware_source *);
|
||||
void live_entropy_sources_feed(int, event_proc_f);
|
||||
|
||||
#define LIVE_ENTROPY_SRC_MODULE(name, modevent, ver) \
|
||||
static moduledata_t name##_mod = { \
|
||||
#name, \
|
||||
modevent, \
|
||||
0 \
|
||||
}; \
|
||||
DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, \
|
||||
SI_ORDER_SECOND); \
|
||||
MODULE_VERSION(name, ver); \
|
||||
MODULE_DEPEND(name, random, 1, 1, 1);
|
||||
void live_entropy_sources_init(void);
|
||||
void live_entropy_sources_deinit(void);
|
||||
void live_entropy_source_register(struct live_entropy_source *);
|
||||
void live_entropy_source_deregister(struct live_entropy_source *);
|
||||
void live_entropy_sources_feed(void);
|
||||
|
||||
#endif /* SYS_DEV_RANDOM_LIVE_ENTROPY_SOURCES_H_INCLUDED */
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -30,11 +31,11 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/segments.h>
|
||||
@ -44,21 +45,20 @@ __FBSDID("$FreeBSD$");
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
|
||||
static void random_nehemiah_init(void);
|
||||
static void random_nehemiah_deinit(void);
|
||||
static int random_nehemiah_read(void *, int);
|
||||
static u_int random_nehemiah_read(void *, u_int);
|
||||
|
||||
static struct random_hardware_source random_nehemiah = {
|
||||
.ident = "Hardware, VIA Nehemiah Padlock RNG",
|
||||
.source = RANDOM_PURE_NEHEMIAH,
|
||||
.read = random_nehemiah_read
|
||||
static struct live_entropy_source random_nehemiah = {
|
||||
.les_ident = "VIA Nehemiah Padlock RNG",
|
||||
.les_source = RANDOM_PURE_NEHEMIAH,
|
||||
.les_read = random_nehemiah_read
|
||||
};
|
||||
|
||||
/* TODO: now that the Davies-Meyer hash is gone and we only use
|
||||
/* XXX: FIX? Now that the Davies-Meyer hash is gone and we only use
|
||||
* the 'xstore' instruction, do we still need to preserve the
|
||||
* FPU state with fpu_kern_(enter|leave)() ?
|
||||
*/
|
||||
@ -75,7 +75,7 @@ VIA_RNG_store(void *buf)
|
||||
#ifdef __GNUCLIKE_ASM
|
||||
__asm __volatile(
|
||||
"movl $0,%%edx\n\t"
|
||||
".byte 0x0f, 0xa7, 0xc0" /* xstore */
|
||||
"xstore"
|
||||
: "=a" (retval), "+d" (rate), "+D" (buf)
|
||||
:
|
||||
: "memory"
|
||||
@ -100,8 +100,9 @@ random_nehemiah_deinit(void)
|
||||
fpu_kern_free_ctx(fpu_ctx_save);
|
||||
}
|
||||
|
||||
static int
|
||||
random_nehemiah_read(void *buf, int c)
|
||||
/* It is specifically allowed that buf is a multiple of sizeof(long) */
|
||||
static u_int
|
||||
random_nehemiah_read(void *buf, u_int c)
|
||||
{
|
||||
uint8_t *b;
|
||||
size_t count, ret;
|
||||
@ -131,13 +132,9 @@ nehemiah_modevent(module_t mod, int type, void *unused)
|
||||
case MOD_LOAD:
|
||||
if (via_feature_rng & VIA_HAS_RNG) {
|
||||
live_entropy_source_register(&random_nehemiah);
|
||||
printf("random: live provider: \"%s\"\n", random_nehemiah.les_ident);
|
||||
random_nehemiah_init();
|
||||
} else
|
||||
#ifndef KLD_MODULE
|
||||
if (bootverbose)
|
||||
#endif
|
||||
printf("%s: VIA Padlock RNG not present\n",
|
||||
random_nehemiah.ident);
|
||||
}
|
||||
break;
|
||||
|
||||
case MOD_UNLOAD:
|
||||
@ -158,4 +155,6 @@ nehemiah_modevent(module_t mod, int type, void *unused)
|
||||
return (error);
|
||||
}
|
||||
|
||||
LIVE_ENTROPY_SRC_MODULE(nehemiah, nehemiah_modevent, 1);
|
||||
DEV_MODULE(nehemiah, nehemiah_modevent, NULL);
|
||||
MODULE_VERSION(nehemiah, 1);
|
||||
MODULE_DEPEND(nehemiah, random_adaptors, 1, 1, 1);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
|
||||
* Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org>
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -29,158 +29,348 @@
|
||||
#include <sys/param.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/libkern.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sx.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
|
||||
LIST_HEAD(adaptors_head, random_adaptors);
|
||||
static struct adaptors_head adaptors = LIST_HEAD_INITIALIZER(adaptors);
|
||||
static struct sx adaptors_lock; /* need a sleepable lock */
|
||||
|
||||
/* List for the dynamic sysctls */
|
||||
static struct sysctl_ctx_list random_clist;
|
||||
|
||||
struct random_adaptor *random_adaptor;
|
||||
|
||||
MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures");
|
||||
|
||||
int
|
||||
random_adaptor_register(const char *name, struct random_adaptor *rsp)
|
||||
{
|
||||
struct random_adaptors *rpp;
|
||||
|
||||
KASSERT(name != NULL && rsp != NULL, ("invalid input to %s", __func__));
|
||||
|
||||
rpp = malloc(sizeof(struct random_adaptors), M_ENTROPY, M_WAITOK);
|
||||
rpp->name = name;
|
||||
rpp->rsp = rsp;
|
||||
|
||||
sx_xlock(&adaptors_lock);
|
||||
LIST_INSERT_HEAD(&adaptors, rpp, entries);
|
||||
sx_xunlock(&adaptors_lock);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct random_adaptor *
|
||||
random_adaptor_get(const char *name)
|
||||
{
|
||||
struct random_adaptors *rpp;
|
||||
struct random_adaptor *rsp;
|
||||
|
||||
rsp = NULL;
|
||||
|
||||
sx_slock(&adaptors_lock);
|
||||
|
||||
LIST_FOREACH(rpp, &adaptors, entries)
|
||||
if (strcmp(rpp->name, name) == 0)
|
||||
rsp = rpp->rsp;
|
||||
|
||||
sx_sunlock(&adaptors_lock);
|
||||
|
||||
return (rsp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Walk a list of registered random(4) adaptors and pick the last non-selected
|
||||
* one.
|
||||
*
|
||||
* If none are selected, use yarrow if available.
|
||||
/* The random_adaptors_lock protects random_adaptors_list and friends and random_adaptor.
|
||||
* We need a sleepable lock for uiomove/block/poll/sbuf/sysctl.
|
||||
*/
|
||||
void
|
||||
random_adaptor_choose(struct random_adaptor **adaptor)
|
||||
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 *rppi, *ramax;
|
||||
unsigned primax;
|
||||
struct random_adaptors *rra, *rrai;
|
||||
struct random_adaptor *random_adaptor_previous;
|
||||
int primax;
|
||||
|
||||
KASSERT(adaptor != NULL, ("pre-conditions failed"));
|
||||
/* We are going to be messing with random_adaptor.
|
||||
* Exclusive lock is mandatory.
|
||||
*/
|
||||
sx_assert(&random_adaptors_lock, SA_XLOCKED);
|
||||
|
||||
*adaptor = NULL;
|
||||
random_adaptor_previous = random_adaptor;
|
||||
|
||||
random_adaptor = NULL;
|
||||
if (TUNABLE_STR_FETCH("kern.random.active_adaptor", rngs, sizeof(rngs))) {
|
||||
cp = rngs;
|
||||
|
||||
while ((token = strsep(&cp, ",")) != NULL)
|
||||
if ((*adaptor = random_adaptor_get(token)) != NULL)
|
||||
/* 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 if (bootverbose)
|
||||
printf("%s random adaptor is not available,"
|
||||
" skipping\n", token);
|
||||
}
|
||||
else
|
||||
printf("random: requested adaptor <%s> not available\n",
|
||||
token);
|
||||
}
|
||||
}
|
||||
|
||||
primax = 0U;
|
||||
if (*adaptor == NULL) {
|
||||
primax = 0;
|
||||
if (random_adaptor == NULL) {
|
||||
/*
|
||||
* Fall back to the highest priority item on the available
|
||||
* RNG list.
|
||||
*/
|
||||
sx_slock(&adaptors_lock);
|
||||
|
||||
ramax = NULL;
|
||||
LIST_FOREACH(rppi, &adaptors, entries) {
|
||||
if (rppi->rsp->priority >= primax) {
|
||||
ramax = rppi;
|
||||
primax = rppi->rsp->priority;
|
||||
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 (ramax != NULL)
|
||||
*adaptor = ramax->rsp;
|
||||
if (random_adaptor != NULL)
|
||||
printf("random: selecting highest priority adaptor <%s>\n",
|
||||
random_adaptor->ra_ident);
|
||||
}
|
||||
|
||||
sx_sunlock(&adaptors_lock);
|
||||
KASSERT(random_adaptor != NULL, ("adaptor not found"));
|
||||
|
||||
if (bootverbose && *adaptor)
|
||||
printf("Falling back to <%s> random adaptor\n",
|
||||
(*adaptor)->ident);
|
||||
/* If we are changing adaptors, deinit the old and init the new. */
|
||||
if (random_adaptor != random_adaptor_previous) {
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s - changing from %s to %s\n", __func__,
|
||||
(random_adaptor_previous == NULL ? "NULL" : random_adaptor_previous->ra_ident),
|
||||
random_adaptor->ra_ident);
|
||||
#endif
|
||||
if (random_adaptor_previous != NULL)
|
||||
(random_adaptor_previous->ra_deinit)();
|
||||
(random_adaptor->ra_init)();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
random_adaptors_deinit(void *unused)
|
||||
|
||||
/* XXX: FIX!! Make sure we are not inserting a duplicate */
|
||||
void
|
||||
random_adaptor_register(const char *name, struct random_adaptor *ra)
|
||||
{
|
||||
struct random_adaptors *rra;
|
||||
|
||||
KASSERT(name != NULL && ra != NULL, ("invalid input to %s", __func__));
|
||||
|
||||
rra = malloc(sizeof(*rra), M_ENTROPY, M_WAITOK);
|
||||
rra->rra_name = name;
|
||||
rra->rra_ra = ra;
|
||||
|
||||
sx_xlock(&random_adaptors_lock);
|
||||
LIST_INSERT_HEAD(&random_adaptors_list, rra, rra_entries);
|
||||
random_adaptor_choose();
|
||||
sx_xunlock(&random_adaptors_lock);
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
}
|
||||
|
||||
void
|
||||
random_adaptor_deregister(const char *name)
|
||||
{
|
||||
struct random_adaptors *rra;
|
||||
|
||||
KASSERT(name != NULL, ("invalid input to %s", __func__));
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
sx_xlock(&random_adaptors_lock);
|
||||
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
||||
if (strcmp(rra->rra_name, name) == 0) {
|
||||
LIST_REMOVE(rra, rra_entries);
|
||||
break;
|
||||
}
|
||||
random_adaptor_choose();
|
||||
sx_xunlock(&random_adaptors_lock);
|
||||
|
||||
free(rra, M_ENTROPY);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
random_adaptor_read(struct cdev *dev __unused, struct uio *uio, int flags)
|
||||
{
|
||||
void *random_buf;
|
||||
int c, error;
|
||||
ssize_t nbytes;
|
||||
|
||||
#ifdef RANDOM_DEBUG_VERBOSE
|
||||
printf("random: %s %ld\n", __func__, uio->uio_resid);
|
||||
#endif
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
sx_slock(&random_adaptors_lock);
|
||||
|
||||
/* Let the entropy source do any pre-read setup. */
|
||||
(random_adaptor->ra_read)(NULL, 0);
|
||||
|
||||
/* (Un)Blocking logic */
|
||||
error = 0;
|
||||
while (!random_adaptor->ra_seeded()) {
|
||||
if (flags & O_NONBLOCK) {
|
||||
error = EWOULDBLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Sleep instead of going into a spin-frenzy */
|
||||
tsleep(&random_adaptor, PUSER | PCATCH, "block", hz/10);
|
||||
|
||||
/* keep tapping away at the pre-read until we seed/unblock. */
|
||||
(random_adaptor->ra_read)(NULL, 0);
|
||||
}
|
||||
|
||||
mtx_lock(&random_read_rate_mtx);
|
||||
|
||||
/* The read-rate stuff is a rough indication of the instantaneous read rate,
|
||||
* used to increase the use of 'live' entropy sources when lots of reads are done.
|
||||
*/
|
||||
nbytes = (uio->uio_resid + 32 - 1)/32; /* Round up to units of 32 */
|
||||
random_adaptor_read_rate_cache += nbytes*32;
|
||||
random_adaptor_read_rate_cache = MIN(random_adaptor_read_rate_cache, 32);
|
||||
|
||||
mtx_unlock(&random_read_rate_mtx);
|
||||
|
||||
if (!error) {
|
||||
|
||||
/* The actual read */
|
||||
|
||||
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
||||
|
||||
while (uio->uio_resid && !error) {
|
||||
c = MIN(uio->uio_resid, PAGE_SIZE);
|
||||
(random_adaptor->ra_read)(random_buf, c);
|
||||
error = uiomove(random_buf, c, uio);
|
||||
}
|
||||
|
||||
/* Let the entropy source do any post-read cleanup. */
|
||||
(random_adaptor->ra_read)(NULL, 1);
|
||||
|
||||
free(random_buf, M_ENTROPY);
|
||||
}
|
||||
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
random_adaptor_read_rate(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
mtx_lock(&random_read_rate_mtx);
|
||||
|
||||
ret = random_adaptor_read_rate_cache;
|
||||
random_adaptor_read_rate_cache = 1;
|
||||
|
||||
mtx_unlock(&random_read_rate_mtx);
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
random_adaptor_write(struct cdev *dev __unused, struct uio *uio, int flags __unused)
|
||||
{
|
||||
int c, error = 0;
|
||||
void *random_buf;
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s %zd\n", __func__, uio->uio_resid);
|
||||
#endif
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
sx_slock(&random_adaptors_lock);
|
||||
|
||||
random_buf = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
||||
|
||||
while (uio->uio_resid > 0) {
|
||||
c = MIN(uio->uio_resid, PAGE_SIZE);
|
||||
error = uiomove(random_buf, c, uio);
|
||||
if (error)
|
||||
break;
|
||||
(random_adaptor->ra_write)(random_buf, c);
|
||||
|
||||
/* Introduce an annoying delay to stop swamping */
|
||||
tsleep(&random_adaptor, PUSER | PCATCH, "block", hz/10);
|
||||
}
|
||||
|
||||
free(random_buf, M_ENTROPY);
|
||||
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
int
|
||||
random_adaptor_poll(struct cdev *dev __unused, int events, struct thread *td __unused)
|
||||
{
|
||||
|
||||
sx_destroy(&adaptors_lock);
|
||||
sysctl_ctx_free(&random_clist);
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
sx_slock(&random_adaptors_lock);
|
||||
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (random_adaptor->ra_seeded())
|
||||
events &= (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(td, &rsel);
|
||||
}
|
||||
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
return (events);
|
||||
}
|
||||
|
||||
/* This will be called by the entropy processor when it seeds itself and becomes secure */
|
||||
void
|
||||
random_adaptor_unblock(void)
|
||||
{
|
||||
|
||||
selwakeuppri(&rsel, PUSER);
|
||||
wakeup(&random_adaptor);
|
||||
printf("random: unblocking device.\n");
|
||||
|
||||
/* Do arc4random(9) a favour while we are about it. */
|
||||
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE, ARC4_ENTR_HAVE);
|
||||
}
|
||||
|
||||
static int
|
||||
random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct random_adaptors *rpp;
|
||||
struct random_adaptors *rra;
|
||||
struct sbuf sbuf;
|
||||
int error, count;
|
||||
|
||||
count = error = 0;
|
||||
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);
|
||||
|
||||
sx_slock(&adaptors_lock);
|
||||
|
||||
if (LIST_EMPTY(&adaptors))
|
||||
error = SYSCTL_OUT(req, "", 0);
|
||||
else {
|
||||
LIST_FOREACH(rpp, &adaptors, entries) {
|
||||
|
||||
error = SYSCTL_OUT(req, ",", count++ ? 1 : 0);
|
||||
if (error)
|
||||
break;
|
||||
|
||||
error = SYSCTL_OUT(req, rpp->name, strlen(rpp->name));
|
||||
if (error)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sx_sunlock(&adaptors_lock);
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
return (error);
|
||||
}
|
||||
@ -188,36 +378,34 @@ random_sysctl_adaptors_handler(SYSCTL_HANDLER_ARGS)
|
||||
static int
|
||||
random_sysctl_active_adaptor_handler(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct random_adaptor *rsp;
|
||||
struct random_adaptors *rpp;
|
||||
const char *name;
|
||||
struct random_adaptors *rra;
|
||||
struct sbuf sbuf;
|
||||
int error;
|
||||
|
||||
name = NULL;
|
||||
rsp = random_adaptor;
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
if (rsp != NULL) {
|
||||
sx_slock(&adaptors_lock);
|
||||
|
||||
LIST_FOREACH(rpp, &adaptors, entries)
|
||||
if (rpp->rsp == rsp)
|
||||
name = rpp->name;
|
||||
|
||||
sx_sunlock(&adaptors_lock);
|
||||
}
|
||||
|
||||
if (rsp == NULL || name == NULL)
|
||||
error = SYSCTL_OUT(req, "", 0);
|
||||
else
|
||||
error = SYSCTL_OUT(req, name, strlen(name));
|
||||
sx_slock(&random_adaptors_lock);
|
||||
sbuf_new_for_sysctl(&sbuf, NULL, 16, req);
|
||||
LIST_FOREACH(rra, &random_adaptors_list, rra_entries)
|
||||
if (rra->rra_ra == random_adaptor) {
|
||||
sbuf_cat(&sbuf, rra->rra_name);
|
||||
break;
|
||||
}
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
static void
|
||||
random_adaptors_init(void *unused)
|
||||
void
|
||||
random_adaptors_init(void)
|
||||
{
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
SYSCTL_PROC(_kern_random, OID_AUTO, adaptors,
|
||||
CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE,
|
||||
NULL, 0, random_sysctl_adaptors_handler, "A",
|
||||
@ -228,24 +416,74 @@ random_adaptors_init(void *unused)
|
||||
NULL, 0, random_sysctl_active_adaptor_handler, "A",
|
||||
"Active Random Number Generator Adaptor");
|
||||
|
||||
sx_init(&adaptors_lock, "random_adaptors");
|
||||
sx_init(&random_adaptors_lock, "random_adaptors");
|
||||
|
||||
mtx_init(&random_read_rate_mtx, "read rate mutex", NULL, MTX_DEF);
|
||||
|
||||
/* The dummy adaptor is not a module by itself, but part of the
|
||||
* randomdev module.
|
||||
*/
|
||||
random_adaptor_register("dummy", &randomdev_dummy);
|
||||
|
||||
live_entropy_sources_init();
|
||||
}
|
||||
|
||||
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator");
|
||||
|
||||
SYSINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST, random_adaptors_init,
|
||||
NULL);
|
||||
SYSUNINIT(random_adaptors, SI_SUB_DRIVERS, SI_ORDER_FIRST,
|
||||
random_adaptors_deinit, NULL);
|
||||
|
||||
static void
|
||||
random_adaptors_reseed(void *unused)
|
||||
void
|
||||
random_adaptors_deinit(void)
|
||||
{
|
||||
|
||||
(void)unused;
|
||||
if (random_adaptor != NULL)
|
||||
(*random_adaptor->reseed)();
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
live_entropy_sources_deinit();
|
||||
|
||||
/* Don't do this! Panic will surely follow! */
|
||||
/* random_adaptor_deregister("dummy"); */
|
||||
|
||||
mtx_destroy(&random_read_rate_mtx);
|
||||
|
||||
sx_destroy(&random_adaptors_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* First seed.
|
||||
*
|
||||
* NB! NB! NB!
|
||||
* NB! NB! NB!
|
||||
*
|
||||
* It turns out this is bloody dangerous. I was fiddling with code elsewhere
|
||||
* and managed to get conditions where a safe (i.e. seeded) entropy device should
|
||||
* not have been possible. This managed to hide that by unblocking the device anyway.
|
||||
* As crap randomness is not directly distinguishable from good randomness, this
|
||||
* could have gone unnoticed for quite a while.
|
||||
*
|
||||
* NB! NB! NB!
|
||||
* NB! NB! NB!
|
||||
*
|
||||
* Very luckily, the probe-time entropy is very nearly good enough to cause a
|
||||
* first seed all of the time, and the default settings for other entropy
|
||||
* harvesting causes a proper, safe, first seed (unblock) in short order after that.
|
||||
*
|
||||
* That said, the below would be useful where folks are more concerned with
|
||||
* a quick start than with extra paranoia in a low-entropy environment.
|
||||
*
|
||||
* markm - October 2013.
|
||||
*/
|
||||
#ifdef RANDOM_AUTOSEED
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
random_adaptors_seed(void *unused __unused)
|
||||
{
|
||||
|
||||
KASSERT(random_adaptor != NULL, ("No active random adaptor in %s", __func__));
|
||||
|
||||
sx_slock(&random_adaptors_lock);
|
||||
random_adaptor->ra_reseed();
|
||||
sx_sunlock(&random_adaptors_lock);
|
||||
|
||||
arc4rand(NULL, 0, 1);
|
||||
}
|
||||
SYSINIT(random_reseed, SI_SUB_INTRINSIC_POST, SI_ORDER_SECOND,
|
||||
SYSINIT(random_seed, SI_SUB_INTRINSIC_POST, SI_ORDER_LAST,
|
||||
random_adaptors_reseed, NULL);
|
||||
#endif /* RANDOM_AUTOSEED */
|
||||
|
@ -29,43 +29,46 @@
|
||||
#ifndef SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_RANDOM_ADAPTORS_H_INCLUDED
|
||||
|
||||
#include <sys/eventhandler.h>
|
||||
|
||||
MALLOC_DECLARE(M_ENTROPY);
|
||||
|
||||
struct random_adaptors {
|
||||
LIST_ENTRY(random_adaptors) entries; /* list of providers */
|
||||
const char *name; /* name of random adaptor */
|
||||
struct random_adaptor *rsp;
|
||||
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_adaptor *random_adaptor_get(const char *);
|
||||
int random_adaptor_register(const char *, struct random_adaptor *);
|
||||
void random_adaptor_choose(struct random_adaptor **);
|
||||
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;
|
||||
};
|
||||
|
||||
extern struct random_adaptor *random_adaptor;
|
||||
/* Dummy "always-block" pseudo-device */
|
||||
extern struct random_adaptor randomdev_dummy;
|
||||
|
||||
/*
|
||||
* random_adaptor's should be registered prior to
|
||||
* random module (SI_SUB_DRIVERS/SI_ORDER_MIDDLE)
|
||||
*/
|
||||
#define RANDOM_ADAPTOR_MODULE(name, modevent, ver) \
|
||||
static moduledata_t name##_mod = { \
|
||||
#name, \
|
||||
modevent, \
|
||||
0 \
|
||||
}; \
|
||||
DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, \
|
||||
SI_ORDER_SECOND); \
|
||||
MODULE_VERSION(name, ver); \
|
||||
MODULE_DEPEND(name, random, 1, 1, 1);
|
||||
void random_adaptors_init(void);
|
||||
void random_adaptors_deinit(void);
|
||||
|
||||
typedef void (*random_adaptor_attach_hook)(void *, struct random_adaptor *);
|
||||
EVENTHANDLER_DECLARE(random_adaptor_attach, random_adaptor_attach_hook);
|
||||
void random_adaptor_register(const char *, struct random_adaptor *);
|
||||
void random_adaptor_deregister(const char *);
|
||||
|
||||
/* kern.random sysctls */
|
||||
#ifdef SYSCTL_DECL /* from sysctl.h */
|
||||
SYSCTL_DECL(_kern_random);
|
||||
#endif /* SYSCTL_DECL */
|
||||
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 */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2000-2013 Mark R V Murray
|
||||
* Copyright (c) 2000-2014 Mark R V Murray
|
||||
* Copyright (c) 2013 Arthur Mesh
|
||||
* Copyright (c) 2004 Robert N. M. Watson
|
||||
* All rights reserved.
|
||||
@ -42,230 +42,293 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
#include <machine/vmparam.h>
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
#include <dev/random/rwfile.h>
|
||||
|
||||
#define RANDOM_FIFO_MAX 1024 /* How many events to queue up */
|
||||
/* List for the dynamic sysctls */
|
||||
static struct sysctl_ctx_list random_clist;
|
||||
|
||||
/*
|
||||
* The harvest mutex protects the consistency of the entropy fifos and
|
||||
* How many events to queue up. We create this many items in
|
||||
* an 'empty' queue, then transfer them to the 'harvest' queue with
|
||||
* supplied junk. When used, they are transferred back to the
|
||||
* 'empty' queue.
|
||||
*/
|
||||
#define RANDOM_FIFO_MAX 1024
|
||||
|
||||
/*
|
||||
* The harvest mutex protects the consistency of the entropy Fifos and
|
||||
* empty fifo and other associated structures.
|
||||
*/
|
||||
struct mtx harvest_mtx;
|
||||
static struct mtx harvest_mtx;
|
||||
|
||||
/* Lockable FIFO queue holding entropy buffers */
|
||||
struct entropyfifo {
|
||||
int count;
|
||||
STAILQ_HEAD(harvestlist, harvest) head;
|
||||
};
|
||||
/*
|
||||
* Lockable FIFO ring buffer holding entropy events
|
||||
* If ring_in == ring_out,
|
||||
* the buffer is empty.
|
||||
* If (ring_in + 1) == ring_out (MOD RANDOM_FIFO_MAX),
|
||||
* the buffer is full.
|
||||
*
|
||||
* The ring_in variable needs locking as there are multiple
|
||||
* sources to the ring. Only the sources may change ring_in,
|
||||
* but the consumer may examine it.
|
||||
*
|
||||
* The ring_out variable does not need locking as there is
|
||||
* only one consumer. Only the consumer may change ring_out,
|
||||
* but the sources may examine it.
|
||||
*/
|
||||
static struct entropyfifo {
|
||||
struct harvest_event ring[RANDOM_FIFO_MAX];
|
||||
volatile u_int ring_in;
|
||||
volatile u_int ring_out;
|
||||
} entropyfifo;
|
||||
|
||||
/* Empty entropy buffers */
|
||||
static struct entropyfifo emptyfifo;
|
||||
/* Round-robin destination cache. */
|
||||
u_int harvest_destination[ENTROPYSOURCE];
|
||||
|
||||
/* Harvested entropy */
|
||||
static struct entropyfifo harvestfifo;
|
||||
/* 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;
|
||||
|
||||
/* <0 to end the kthread, 0 to let it run, 1 to flush the harvest queues */
|
||||
int random_kthread_control = 0;
|
||||
static int random_kthread_control = 0;
|
||||
|
||||
static struct proc *random_kthread_proc;
|
||||
|
||||
#ifdef RANDOM_RWFILE
|
||||
static const char *entropy_files[] = {
|
||||
"/entropy",
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
|
||||
/* Deal with entropy cached externally if this is present.
|
||||
* Lots of policy may eventually arrive in this function.
|
||||
* Called after / is mounted.
|
||||
*/
|
||||
static void
|
||||
random_harvestq_cache(void *arg __unused)
|
||||
random_kthread(void *arg __unused)
|
||||
{
|
||||
uint8_t *keyfile, *data;
|
||||
size_t size, i;
|
||||
#ifdef RANDOM_RWFILE
|
||||
const char **entropy_file;
|
||||
uint8_t *zbuf;
|
||||
int error;
|
||||
#endif
|
||||
u_int maxloop, ring_out;
|
||||
|
||||
/* Get stuff that may have been preloaded by loader(8) */
|
||||
keyfile = preload_search_by_type("/boot/entropy");
|
||||
if (keyfile != NULL) {
|
||||
data = preload_fetch_addr(keyfile);
|
||||
size = preload_fetch_size(keyfile);
|
||||
if (data != NULL && size != 0) {
|
||||
for (i = 0; i < size; i += 16)
|
||||
random_harvestq_internal(get_cyclecount(), data + i, 16, 16, RANDOM_CACHED);
|
||||
printf("random: read %zu bytes from preloaded cache\n", size);
|
||||
bzero(data, size);
|
||||
}
|
||||
else
|
||||
printf("random: no preloaded entropy cache available\n");
|
||||
}
|
||||
|
||||
#ifdef RANDOM_RWFILE
|
||||
/* Read and attempt to overwrite the entropy cache files.
|
||||
* If the file exists, can be read and then overwritten,
|
||||
* then use it. Ignore it otherwise, but print out what is
|
||||
* going on.
|
||||
/*
|
||||
* Process until told to stop.
|
||||
*
|
||||
* Locking is not needed as this is the only place we modify ring_out, and
|
||||
* we only examine ring_in without changing it. Both of these are volatile,
|
||||
* and this is a unique thread.
|
||||
*/
|
||||
data = malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
||||
zbuf = __DECONST(void *, zero_region);
|
||||
for (entropy_file = entropy_files; *entropy_file; entropy_file++) {
|
||||
error = randomdev_read_file(*entropy_file, data, PAGE_SIZE);
|
||||
if (error == 0) {
|
||||
printf("random: entropy cache '%s' provides %ld bytes\n", *entropy_file, (long)PAGE_SIZE);
|
||||
error = randomdev_write_file(*entropy_file, zbuf, PAGE_SIZE);
|
||||
if (error == 0) {
|
||||
printf("random: entropy cache '%s' contents used and successfully overwritten\n", *entropy_file);
|
||||
for (i = 0; i < PAGE_SIZE; i += 16)
|
||||
random_harvestq_internal(get_cyclecount(), data + i, 16, 16, RANDOM_CACHED);
|
||||
}
|
||||
else
|
||||
printf("random: entropy cache '%s' not overwritten and therefore not used; error = %d\n", *entropy_file, error);
|
||||
}
|
||||
else
|
||||
printf("random: entropy cache '%s' not present or unreadable; error = %d\n", *entropy_file, error);
|
||||
}
|
||||
bzero(data, PAGE_SIZE);
|
||||
free(data, M_ENTROPY);
|
||||
#endif
|
||||
}
|
||||
EVENTHANDLER_DEFINE(mountroot, random_harvestq_cache, NULL, 0);
|
||||
while (random_kthread_control >= 0) {
|
||||
|
||||
static void
|
||||
random_kthread(void *arg)
|
||||
{
|
||||
STAILQ_HEAD(, harvest) local_queue;
|
||||
struct harvest *event = NULL;
|
||||
int local_count;
|
||||
event_proc_f entropy_processor = arg;
|
||||
/* Deal with events, if any. Restrict the number we do in one go. */
|
||||
maxloop = RANDOM_FIFO_MAX;
|
||||
while (entropyfifo.ring_out != entropyfifo.ring_in) {
|
||||
|
||||
STAILQ_INIT(&local_queue);
|
||||
local_count = 0;
|
||||
ring_out = (entropyfifo.ring_out + 1)%RANDOM_FIFO_MAX;
|
||||
harvest_process_event(entropyfifo.ring + ring_out);
|
||||
/* Modifying ring_out here ONLY. Sufficient for atomicity? */
|
||||
entropyfifo.ring_out = ring_out;
|
||||
|
||||
/* Process until told to stop */
|
||||
mtx_lock_spin(&harvest_mtx);
|
||||
for (; random_kthread_control >= 0;) {
|
||||
/* The ring may be filled quickly so don't loop forever. */
|
||||
if (--maxloop)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Grab all the entropy events.
|
||||
* Drain entropy source records into a thread-local
|
||||
* queue for processing while not holding the mutex.
|
||||
*/
|
||||
STAILQ_CONCAT(&local_queue, &harvestfifo.head);
|
||||
local_count += harvestfifo.count;
|
||||
harvestfifo.count = 0;
|
||||
|
||||
/*
|
||||
* Deal with events, if any.
|
||||
* Then transfer the used events back into the empty fifo.
|
||||
*/
|
||||
if (!STAILQ_EMPTY(&local_queue)) {
|
||||
mtx_unlock_spin(&harvest_mtx);
|
||||
STAILQ_FOREACH(event, &local_queue, next)
|
||||
entropy_processor(event);
|
||||
mtx_lock_spin(&harvest_mtx);
|
||||
STAILQ_CONCAT(&emptyfifo.head, &local_queue);
|
||||
emptyfifo.count += local_count;
|
||||
local_count = 0;
|
||||
}
|
||||
|
||||
KASSERT(local_count == 0, ("random_kthread: local_count %d",
|
||||
local_count));
|
||||
|
||||
/*
|
||||
* Do only one round of the hardware sources for now.
|
||||
* Later we'll need to make it rate-adaptive.
|
||||
* Give the fast hardware sources a go
|
||||
*/
|
||||
mtx_unlock_spin(&harvest_mtx);
|
||||
live_entropy_sources_feed(1, entropy_processor);
|
||||
mtx_lock_spin(&harvest_mtx);
|
||||
live_entropy_sources_feed();
|
||||
|
||||
/*
|
||||
* If a queue flush was commanded, it has now happened,
|
||||
* and we can mark this by resetting the command.
|
||||
* A negative value, however, terminates the thread.
|
||||
*/
|
||||
|
||||
if (random_kthread_control == 1)
|
||||
random_kthread_control = 0;
|
||||
|
||||
/* Work done, so don't belabour the issue */
|
||||
msleep_spin_sbt(&random_kthread_control, &harvest_mtx,
|
||||
"-", SBT_1S/10, 0, C_PREL(1));
|
||||
/* Some work is done, so give the rest of the OS a chance. */
|
||||
tsleep_sbt(&random_kthread_control, 0, "-", SBT_1S/10, 0, C_PREL(1));
|
||||
|
||||
}
|
||||
mtx_unlock_spin(&harvest_mtx);
|
||||
|
||||
random_set_wakeup_exit(&random_kthread_control);
|
||||
randomdev_set_wakeup_exit(&random_kthread_control);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
void
|
||||
random_harvestq_init(event_proc_f cb)
|
||||
random_harvestq_flush(void)
|
||||
{
|
||||
|
||||
/* Command a entropy queue flush and wait for it to finish */
|
||||
random_kthread_control = 1;
|
||||
while (random_kthread_control)
|
||||
pause("-", hz/10);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
RANDOM_CHECK_UINT(harvestmask, 0, ((1U << RANDOM_ENVIRONMENTAL_END) - 1));
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_print_harvestmask(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct sbuf sbuf;
|
||||
int error, i;
|
||||
struct harvest *np;
|
||||
|
||||
/* Initialise the harvest fifos */
|
||||
|
||||
/* Contains the currently unused event structs. */
|
||||
STAILQ_INIT(&emptyfifo.head);
|
||||
for (i = 0; i < RANDOM_FIFO_MAX; i++) {
|
||||
np = malloc(sizeof(struct harvest), M_ENTROPY, M_WAITOK);
|
||||
STAILQ_INSERT_TAIL(&emptyfifo.head, np, next);
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error == 0) {
|
||||
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
|
||||
for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--)
|
||||
sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? "1" : "0");
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
}
|
||||
emptyfifo.count = RANDOM_FIFO_MAX;
|
||||
|
||||
/* Will contain the queued-up events. */
|
||||
STAILQ_INIT(&harvestfifo.head);
|
||||
harvestfifo.count = 0;
|
||||
return (error);
|
||||
}
|
||||
|
||||
static const char *(random_source_descr[]) = {
|
||||
"CACHED",
|
||||
"ATTACH",
|
||||
"KEYBOARD",
|
||||
"MOUSE",
|
||||
"NET_TUN",
|
||||
"NET_ETHER",
|
||||
"NET_NG",
|
||||
"INTERRUPT",
|
||||
"SWI",
|
||||
"UMA_ALLOC",
|
||||
"", /* "ENVIRONMENTAL_END" */
|
||||
"PURE_OCTEON",
|
||||
"PURE_SAFE",
|
||||
"PURE_GLXSB",
|
||||
"PURE_UBSEC",
|
||||
"PURE_HIFN",
|
||||
"PURE_RDRAND",
|
||||
"PURE_NEHEMIAH",
|
||||
"PURE_RNDTEST",
|
||||
/* "ENTROPYSOURCE" */
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_print_harvestmask_symbolic(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
struct sbuf sbuf;
|
||||
int error, i;
|
||||
|
||||
error = sysctl_wire_old_buffer(req, 0);
|
||||
if (error == 0) {
|
||||
sbuf_new_for_sysctl(&sbuf, NULL, 128, req);
|
||||
for (i = RANDOM_ENVIRONMENTAL_END - 1; i >= 0; i--) {
|
||||
sbuf_cat(&sbuf, (i == RANDOM_ENVIRONMENTAL_END - 1) ? "" : ",");
|
||||
sbuf_cat(&sbuf, (harvest_source_mask & (1U << i)) ? random_source_descr[i] : "");
|
||||
}
|
||||
error = sbuf_finish(&sbuf);
|
||||
sbuf_delete(&sbuf);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
void
|
||||
random_harvestq_init(void (*event_processor)(struct harvest_event *), int poolcount)
|
||||
{
|
||||
uint8_t *keyfile, *data;
|
||||
int error;
|
||||
size_t size, j;
|
||||
struct sysctl_oid *random_sys_o;
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
random_sys_o = SYSCTL_ADD_NODE(&random_clist,
|
||||
SYSCTL_STATIC_CHILDREN(_kern_random),
|
||||
OID_AUTO, "harvest", CTLFLAG_RW, 0,
|
||||
"Entropy Device Parameters");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_o),
|
||||
OID_AUTO, "mask", CTLTYPE_UINT | CTLFLAG_RW,
|
||||
&harvest_source_mask, ((1U << RANDOM_ENVIRONMENTAL_END) - 1),
|
||||
random_check_uint_harvestmask, "IU",
|
||||
"Entropy harvesting mask");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_o),
|
||||
OID_AUTO, "mask_bin", CTLTYPE_STRING | CTLFLAG_RD,
|
||||
NULL, 0, random_print_harvestmask, "A", "Entropy harvesting mask (printable)");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_o),
|
||||
OID_AUTO, "mask_symbolic", CTLTYPE_STRING | CTLFLAG_RD,
|
||||
NULL, 0, random_print_harvestmask_symbolic, "A", "Entropy harvesting mask (symbolic)");
|
||||
|
||||
/* Point to the correct event_processing function */
|
||||
harvest_process_event = event_processor;
|
||||
|
||||
/* Store the pool count (used by live source feed) */
|
||||
harvest_pool_count = poolcount;
|
||||
|
||||
/* Initialise the harvesting mutex and in/out indexes. */
|
||||
mtx_init(&harvest_mtx, "entropy harvest mutex", NULL, MTX_SPIN);
|
||||
entropyfifo.ring_in = entropyfifo.ring_out = 0U;
|
||||
|
||||
/* Start the hash/reseed thread */
|
||||
error = kproc_create(random_kthread, cb,
|
||||
&random_kthread_proc, RFHIGHPID, 0, "rand_harvestq"); /* RANDOM_CSPRNG_NAME */
|
||||
error = kproc_create(random_kthread, NULL,
|
||||
&random_kthread_proc, RFHIGHPID, 0, "rand_harvestq");
|
||||
|
||||
if (error != 0)
|
||||
panic("Cannot create entropy maintenance thread.");
|
||||
|
||||
/* Get entropy that may have been preloaded by loader(8)
|
||||
* and use it to pre-charge the entropy harvest queue.
|
||||
*/
|
||||
keyfile = preload_search_by_type("/boot/entropy");
|
||||
if (keyfile != NULL) {
|
||||
data = preload_fetch_addr(keyfile);
|
||||
size = preload_fetch_size(keyfile);
|
||||
if (data != NULL && size != 0) {
|
||||
for (j = 0; j < size; j += 16)
|
||||
random_harvestq_internal(data + j, 16, 16, RANDOM_CACHED);
|
||||
printf("random: read %zu bytes from preloaded cache\n", size);
|
||||
bzero(data, size);
|
||||
}
|
||||
else
|
||||
printf("random: no preloaded entropy cache\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
random_harvestq_deinit(void)
|
||||
{
|
||||
struct harvest *np;
|
||||
|
||||
/* Destroy the harvest fifos */
|
||||
while (!STAILQ_EMPTY(&emptyfifo.head)) {
|
||||
np = STAILQ_FIRST(&emptyfifo.head);
|
||||
STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
|
||||
free(np, M_ENTROPY);
|
||||
}
|
||||
emptyfifo.count = 0;
|
||||
while (!STAILQ_EMPTY(&harvestfifo.head)) {
|
||||
np = STAILQ_FIRST(&harvestfifo.head);
|
||||
STAILQ_REMOVE_HEAD(&harvestfifo.head, next);
|
||||
free(np, M_ENTROPY);
|
||||
}
|
||||
harvestfifo.count = 0;
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("random: %s\n", __func__);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Command the hash/reseed thread to end and wait for it to finish
|
||||
*/
|
||||
random_kthread_control = -1;
|
||||
tsleep(&random_kthread_control, 0, "term", 0);
|
||||
|
||||
mtx_destroy(&harvest_mtx);
|
||||
|
||||
sysctl_ctx_free(&random_clist);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -278,45 +341,42 @@ random_harvestq_deinit(void)
|
||||
* check a few lines below. This includes the "always-on" sources
|
||||
* like the Intel "rdrand" or the VIA Nehamiah "xstore" sources.
|
||||
*/
|
||||
/* XXXRW: get_cyclecount() is cheap on most modern hardware, where cycle
|
||||
* counters are built in, but on older hardware it will do a real time clock
|
||||
* read which can be quite expensive.
|
||||
*/
|
||||
void
|
||||
random_harvestq_internal(u_int64_t somecounter, const void *entropy,
|
||||
u_int count, u_int bits, enum esource origin)
|
||||
random_harvestq_internal(const void *entropy, u_int count, u_int bits,
|
||||
enum random_entropy_source origin)
|
||||
{
|
||||
struct harvest *event;
|
||||
struct harvest_event *event;
|
||||
u_int ring_in;
|
||||
|
||||
KASSERT(origin >= RANDOM_START && origin < ENTROPYSOURCE,
|
||||
("random_harvest_internal: origin %d invalid\n", origin));
|
||||
|
||||
/* Lockless read to avoid lock operations if fifo is full. */
|
||||
if (harvestfifo.count >= RANDOM_FIFO_MAX)
|
||||
/* Mask out unwanted sources */
|
||||
if (!(harvest_source_mask & (1U << origin)))
|
||||
return;
|
||||
|
||||
/* Lock ring_in against multi-thread contention */
|
||||
mtx_lock_spin(&harvest_mtx);
|
||||
ring_in = (entropyfifo.ring_in + 1)%RANDOM_FIFO_MAX;
|
||||
if (ring_in != entropyfifo.ring_out) {
|
||||
/* The ring is not full */
|
||||
event = entropyfifo.ring + ring_in;
|
||||
|
||||
/*
|
||||
* On't overfill the harvest queue; this could steal all
|
||||
* our memory.
|
||||
*/
|
||||
if (harvestfifo.count < RANDOM_FIFO_MAX) {
|
||||
event = STAILQ_FIRST(&emptyfifo.head);
|
||||
if (event != NULL) {
|
||||
/* Add the harvested data to the fifo */
|
||||
STAILQ_REMOVE_HEAD(&emptyfifo.head, next);
|
||||
emptyfifo.count--;
|
||||
event->somecounter = somecounter;
|
||||
event->size = count;
|
||||
event->bits = bits;
|
||||
event->source = origin;
|
||||
/* Stash the harvested stuff in the *event buffer */
|
||||
count = MIN(count, HARVESTSIZE);
|
||||
event->he_somecounter = get_cyclecount();
|
||||
event->he_size = count;
|
||||
event->he_bits = bits;
|
||||
event->he_source = origin;
|
||||
event->he_destination = harvest_destination[origin]++;
|
||||
memcpy(event->he_entropy, entropy, count);
|
||||
memset(event->he_entropy + count, 0, HARVESTSIZE - count);
|
||||
|
||||
/* XXXX Come back and make this dynamic! */
|
||||
count = MIN(count, HARVESTSIZE);
|
||||
memcpy(event->entropy, entropy, count);
|
||||
|
||||
STAILQ_INSERT_TAIL(&harvestfifo.head,
|
||||
event, next);
|
||||
harvestfifo.count++;
|
||||
}
|
||||
entropyfifo.ring_in = ring_in;
|
||||
}
|
||||
|
||||
mtx_unlock_spin(&harvest_mtx);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2013-2014 Mark R V Murray
|
||||
* Copyright (c) 2013 Arthur Mesh <arthurmesh@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -29,14 +30,41 @@
|
||||
#ifndef SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED
|
||||
|
||||
typedef void (*event_proc_f)(struct harvest *event);
|
||||
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
|
||||
|
||||
void random_harvestq_init(event_proc_f);
|
||||
/* These are used to queue harvested packets of entropy. The entropy
|
||||
* buffer size is pretty arbitrary.
|
||||
*/
|
||||
struct harvest_event {
|
||||
uintmax_t he_somecounter; /* fast counter for clock jitter */
|
||||
uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */
|
||||
u_int he_size; /* harvested entropy byte count */
|
||||
u_int he_bits; /* stats about the entropy */
|
||||
u_int he_destination; /* destination pool of this entropy */
|
||||
enum random_entropy_source he_source; /* origin of the entropy */
|
||||
};
|
||||
|
||||
void random_harvestq_init(void (*)(struct harvest_event *), int);
|
||||
void random_harvestq_deinit(void);
|
||||
void random_harvestq_internal(u_int64_t, const void *,
|
||||
u_int, u_int, enum esource);
|
||||
void random_harvestq_internal(const void *, u_int, u_int, enum random_entropy_source);
|
||||
|
||||
extern int random_kthread_control;
|
||||
extern struct mtx harvest_mtx;
|
||||
/* 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];
|
||||
|
||||
#endif /* SYS_DEV_RANDOM_RANDOM_HARVESTQ_H_INCLUDED */
|
||||
|
@ -26,9 +26,28 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* NOTE NOTE NOTE
|
||||
*
|
||||
* This file is compiled into the kernel unconditionally. Any random(4)
|
||||
* infrastructure that needs to be in the kernel by default goes here!
|
||||
*
|
||||
* Except ...
|
||||
*
|
||||
* The adaptor code all goes into random_adaptor.c, which is also compiled
|
||||
* the kernel by default. The module in that file is initialised before
|
||||
* this one.
|
||||
*
|
||||
* Other modules must be initialised after the above two, and are
|
||||
* software random processors which plug into random_adaptor.c.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
@ -38,96 +57,43 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/kthread.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/priv.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/live_entropy_sources.h>
|
||||
|
||||
#define RANDOM_MINOR 0
|
||||
|
||||
static d_read_t random_read;
|
||||
static d_write_t random_write;
|
||||
static d_ioctl_t random_ioctl;
|
||||
static d_poll_t random_poll;
|
||||
static d_ioctl_t randomdev_ioctl;
|
||||
|
||||
static struct cdevsw random_cdevsw = {
|
||||
.d_version = D_VERSION,
|
||||
.d_read = random_read,
|
||||
.d_write = random_write,
|
||||
.d_ioctl = random_ioctl,
|
||||
.d_poll = random_poll,
|
||||
.d_name = "random",
|
||||
.d_version = D_VERSION,
|
||||
.d_read = random_adaptor_read,
|
||||
.d_write = random_adaptor_write,
|
||||
.d_poll = random_adaptor_poll,
|
||||
.d_ioctl = randomdev_ioctl,
|
||||
};
|
||||
|
||||
/* For use with make_dev(9)/destroy_dev(9). */
|
||||
static struct cdev *random_dev;
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_read(struct cdev *dev __unused, struct uio *uio, int flag)
|
||||
{
|
||||
int c, error = 0;
|
||||
void *random_buf;
|
||||
/* Set up the sysctl root node for the entropy device */
|
||||
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW, 0, "Random Number Generator");
|
||||
|
||||
/* Blocking logic */
|
||||
if (!random_adaptor->seeded)
|
||||
error = (*random_adaptor->block)(flag);
|
||||
|
||||
/* The actual read */
|
||||
if (!error) {
|
||||
|
||||
random_buf = (void *)malloc(PAGE_SIZE, M_ENTROPY, M_WAITOK);
|
||||
|
||||
while (uio->uio_resid > 0 && !error) {
|
||||
c = MIN(uio->uio_resid, PAGE_SIZE);
|
||||
c = (*random_adaptor->read)(random_buf, c);
|
||||
error = uiomove(random_buf, c, uio);
|
||||
}
|
||||
/* Finished reading; let the source know so it can do some
|
||||
* optional housekeeping */
|
||||
(*random_adaptor->read)(NULL, 0);
|
||||
|
||||
free(random_buf, M_ENTROPY);
|
||||
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
MALLOC_DEFINE(M_ENTROPY, "entropy", "Entropy harvesting buffers and data structures");
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_write(struct cdev *dev __unused, struct uio *uio, int flag __unused)
|
||||
{
|
||||
|
||||
/* We used to allow this to insert userland entropy.
|
||||
* We don't any more because (1) this so-called entropy
|
||||
* is usually lousy and (b) its vaguely possible to
|
||||
* mess with entropy harvesting by overdoing a write.
|
||||
* Now we just ignore input like /dev/null does.
|
||||
*/
|
||||
uio->uio_resid = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
|
||||
randomdev_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
|
||||
int flags __unused, struct thread *td __unused)
|
||||
{
|
||||
int error = 0;
|
||||
@ -137,85 +103,48 @@ random_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t addr __unused,
|
||||
case FIOASYNC:
|
||||
case FIONBIO:
|
||||
break;
|
||||
|
||||
default:
|
||||
error = ENOTTY;
|
||||
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_poll(struct cdev *dev __unused, int events, struct thread *td)
|
||||
/* Helper routine to enable kproc_exit() to work while the module is
|
||||
* being (or has been) unloaded.
|
||||
* This routine is in this file because it is always linked into the kernel,
|
||||
* and will thus never be unloaded. This is critical for unloadable modules
|
||||
* that have threads.
|
||||
*/
|
||||
void
|
||||
randomdev_set_wakeup_exit(void *control)
|
||||
{
|
||||
int revents = 0;
|
||||
|
||||
if (events & (POLLIN | POLLRDNORM)) {
|
||||
if (random_adaptor->seeded)
|
||||
revents = events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
revents = (*random_adaptor->poll)(events, td);
|
||||
}
|
||||
return (revents);
|
||||
}
|
||||
|
||||
static void
|
||||
random_initialize(void *p, struct random_adaptor *s)
|
||||
{
|
||||
static int random_inited = 0;
|
||||
|
||||
if (random_inited) {
|
||||
printf("random: <%s> already initialized\n",
|
||||
random_adaptor->ident);
|
||||
return;
|
||||
}
|
||||
|
||||
random_adaptor = s;
|
||||
|
||||
(s->init)();
|
||||
|
||||
printf("random: <%s> initialized\n", s->ident);
|
||||
|
||||
/* Use an appropriately evil mode for those who are concerned
|
||||
* with daemons */
|
||||
random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
|
||||
RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0666, "random");
|
||||
make_dev_alias(random_dev, "urandom"); /* compatibility */
|
||||
|
||||
/* mark random(4) as initialized, to avoid being called again */
|
||||
random_inited = 1;
|
||||
wakeup(control);
|
||||
kproc_exit(0);
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_modevent(module_t mod __unused, int type, void *data __unused)
|
||||
randomdev_modevent(module_t mod __unused, int type, void *data __unused)
|
||||
{
|
||||
static eventhandler_tag attach_tag = NULL;
|
||||
int error = 0;
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
random_adaptor_choose(&random_adaptor);
|
||||
|
||||
if (random_adaptor == NULL) {
|
||||
printf("random: No random adaptor attached, "
|
||||
"postponing initialization\n");
|
||||
attach_tag = EVENTHANDLER_REGISTER(random_adaptor_attach,
|
||||
random_initialize, NULL, EVENTHANDLER_PRI_ANY);
|
||||
} else
|
||||
random_initialize(NULL, random_adaptor);
|
||||
|
||||
printf("random: entropy device infrastructure driver\n");
|
||||
random_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD, &random_cdevsw,
|
||||
RANDOM_MINOR, NULL, UID_ROOT, GID_WHEEL, 0644, "random");
|
||||
make_dev_alias(random_dev, "urandom"); /* compatibility */
|
||||
random_adaptors_init();
|
||||
break;
|
||||
|
||||
case MOD_UNLOAD:
|
||||
if (random_adaptor != NULL) {
|
||||
(*random_adaptor->deinit)();
|
||||
destroy_dev(random_dev);
|
||||
}
|
||||
/* Unregister the event handler */
|
||||
if (attach_tag != NULL)
|
||||
EVENTHANDLER_DEREGISTER(random_adaptor_attach,
|
||||
attach_tag);
|
||||
|
||||
random_adaptors_deinit();
|
||||
destroy_dev(random_dev);
|
||||
break;
|
||||
|
||||
case MOD_SHUTDOWN:
|
||||
@ -226,8 +155,98 @@ random_modevent(module_t mod __unused, int type, void *data __unused)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
DEV_MODULE(random, random_modevent, NULL);
|
||||
MODULE_VERSION(random, 1);
|
||||
#define EARLY_2_DEV_MODULE(name, evh, arg) \
|
||||
static moduledata_t name##_mod = { \
|
||||
#name, \
|
||||
evh, \
|
||||
arg \
|
||||
}; \
|
||||
DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND)
|
||||
|
||||
EARLY_2_DEV_MODULE(randomdev, randomdev_modevent, NULL);
|
||||
MODULE_VERSION(randomdev, 1);
|
||||
|
||||
/* ================
|
||||
* Harvesting stubs
|
||||
* ================
|
||||
*/
|
||||
|
||||
/* Internal stub/fake routine for when no entropy processor is loaded.
|
||||
* If the entropy device is not loaded, don't act on harvesting calls
|
||||
* and just return.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
random_harvest_phony(const void *entropy __unused, u_int count __unused,
|
||||
u_int bits __unused, enum random_entropy_source origin __unused)
|
||||
{
|
||||
}
|
||||
|
||||
/* Hold the address of the routine which is actually called */
|
||||
static void (*reap_func)(const void *, u_int, u_int, enum random_entropy_source) = random_harvest_phony;
|
||||
|
||||
/* Initialise the harvester when/if it is loaded */
|
||||
void
|
||||
randomdev_init_harvester(void (*reaper)(const void *, u_int, u_int, enum random_entropy_source))
|
||||
{
|
||||
|
||||
reap_func = reaper;
|
||||
}
|
||||
|
||||
/* Deinitialise the harvester when/if it is unloaded */
|
||||
void
|
||||
randomdev_deinit_harvester(void)
|
||||
{
|
||||
|
||||
reap_func = random_harvest_phony;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
||||
(*reap_func)(entropy, count, bits, origin);
|
||||
}
|
||||
|
||||
/* ================================
|
||||
* Internal reading stubs and fakes
|
||||
* ================================
|
||||
*/
|
||||
|
||||
/* Hold the address of the routine which is actually called */
|
||||
static u_int (*read_func)(uint8_t *, u_int) = dummy_random_read_phony;
|
||||
|
||||
/* Initialise the reader when/if it is loaded */
|
||||
void
|
||||
randomdev_init_reader(u_int (*reader)(uint8_t *, u_int))
|
||||
{
|
||||
|
||||
read_func = reader;
|
||||
}
|
||||
|
||||
/* Deinitialise the reader when/if it is unloaded */
|
||||
void
|
||||
randomdev_deinit_reader(void)
|
||||
{
|
||||
|
||||
read_func = dummy_random_read_phony;
|
||||
}
|
||||
|
||||
/* Kernel API version of read_random().
|
||||
* Implemented as in indirect call to allow non-inclusion of
|
||||
* the entropy device.
|
||||
*/
|
||||
int
|
||||
read_random(void *buf, int count)
|
||||
{
|
||||
|
||||
return ((int)(*read_func)(buf, (u_int)count));
|
||||
}
|
||||
|
@ -35,28 +35,33 @@
|
||||
|
||||
typedef void random_init_func_t(void);
|
||||
typedef void random_deinit_func_t(void);
|
||||
typedef int random_block_func_t(int);
|
||||
typedef int random_read_func_t(void *, int);
|
||||
typedef int random_poll_func_t(int, struct thread *);
|
||||
typedef void random_reseed_func_t(void);
|
||||
|
||||
struct random_adaptor {
|
||||
struct selinfo rsel;
|
||||
const char *ident;
|
||||
int seeded;
|
||||
unsigned priority;
|
||||
random_init_func_t *init;
|
||||
random_deinit_func_t *deinit;
|
||||
random_block_func_t *block;
|
||||
random_read_func_t *read;
|
||||
random_poll_func_t *poll;
|
||||
random_reseed_func_t *reseed;
|
||||
};
|
||||
void randomdev_init_harvester(void (*)(const void *, u_int, u_int, enum random_entropy_source));
|
||||
void randomdev_init_reader(u_int (*)(uint8_t *, u_int));
|
||||
void randomdev_deinit_harvester(void);
|
||||
void randomdev_deinit_reader(void);
|
||||
|
||||
struct random_hardware_source {
|
||||
const char *ident;
|
||||
enum esource source;
|
||||
random_read_func_t *read;
|
||||
};
|
||||
/* Stub/fake routines for when no entropy processor is loaded */
|
||||
extern u_int dummy_random_read_phony(uint8_t *, u_int);
|
||||
|
||||
/* kern.random sysctls */
|
||||
#ifdef SYSCTL_DECL /* from sysctl.h */
|
||||
SYSCTL_DECL(_kern_random);
|
||||
|
||||
/* If this was C++, the macro below would be a template */
|
||||
#define RANDOM_CHECK_UINT(name, min, max) \
|
||||
static int \
|
||||
random_check_uint_##name(SYSCTL_HANDLER_ARGS) \
|
||||
{ \
|
||||
if (oidp->oid_arg1 != NULL) { \
|
||||
if (*(u_int *)(oidp->oid_arg1) <= (min)) \
|
||||
*(u_int *)(oidp->oid_arg1) = (min); \
|
||||
else if (*(u_int *)(oidp->oid_arg1) > (max)) \
|
||||
*(u_int *)(oidp->oid_arg1) = (max); \
|
||||
} \
|
||||
return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \
|
||||
req)); \
|
||||
}
|
||||
#endif /* SYSCTL_DECL */
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*-
|
||||
* Copyright (c) 2000-2013 Mark R V Murray
|
||||
* Copyright (c) 2000-2014 Mark R V Murray
|
||||
* Copyright (c) 2004 Robert N. M. Watson
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -26,6 +26,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is the loadable infrastructure base file for software CSPRNG
|
||||
* drivers such as Yarrow or Fortuna.
|
||||
*
|
||||
* It is anticipated that one instance of this file will be used
|
||||
* for _each_ invocation of a CSPRNG, but with different #defines
|
||||
* set. See below.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
|
||||
@ -33,15 +43,13 @@
|
||||
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
|
||||
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
|
||||
#endif
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
#error "Fortuna is not yet implemented"
|
||||
#endif
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
@ -50,14 +58,9 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/selinfo.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/unistd.h>
|
||||
|
||||
#include <machine/bus.h>
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
@ -69,111 +72,44 @@ __FBSDID("$FreeBSD$");
|
||||
#include <dev/random/fortuna.h>
|
||||
#endif
|
||||
|
||||
|
||||
static int randomdev_poll(int event, struct thread *td);
|
||||
static int randomdev_block(int flag);
|
||||
static void randomdev_flush_reseed(void);
|
||||
|
||||
static struct random_adaptor random_soft_processor = {
|
||||
#if defined(RANDOM_YARROW)
|
||||
static struct random_adaptor random_context = {
|
||||
.ident = "Software, Yarrow",
|
||||
.init = randomdev_init,
|
||||
.deinit = randomdev_deinit,
|
||||
.block = randomdev_block,
|
||||
.read = random_yarrow_read,
|
||||
.poll = randomdev_poll,
|
||||
.reseed = randomdev_flush_reseed,
|
||||
.seeded = 0, /* This will be seeded during entropy processing */
|
||||
.priority = 90, /* High priority, so top of the list. Fortuna may still win. */
|
||||
};
|
||||
#define RANDOM_MODULE_NAME yarrow
|
||||
#define RANDOM_CSPRNG_NAME "yarrow"
|
||||
.ra_ident = "Yarrow",
|
||||
.ra_priority = 90, /* High priority, so top of the list. Fortuna may still win. */
|
||||
.ra_read = random_yarrow_read,
|
||||
.ra_write = random_yarrow_write,
|
||||
.ra_reseed = random_yarrow_reseed,
|
||||
.ra_seeded = random_yarrow_seeded,
|
||||
#endif
|
||||
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
static struct random_adaptor random_context = {
|
||||
.ident = "Software, Fortuna",
|
||||
.init = randomdev_init,
|
||||
.deinit = randomdev_deinit,
|
||||
.block = randomdev_block,
|
||||
.read = random_fortuna_read,
|
||||
.poll = randomdev_poll,
|
||||
.reseed = randomdev_flush_reseed,
|
||||
.seeded = 0, /* This will be excplicitly seeded at startup when secured */
|
||||
.priority = 100, /* High priority, so top of the list. Beat Yarrow. */
|
||||
};
|
||||
#define RANDOM_MODULE_NAME fortuna
|
||||
#define RANDOM_CSPRNG_NAME "fortuna"
|
||||
.ra_ident = "Fortuna",
|
||||
.ra_priority = 100, /* High priority, so top of the list. Beat Yarrow. */
|
||||
.ra_read = random_fortuna_read,
|
||||
.ra_write = random_fortuna_write,
|
||||
.ra_reseed = random_fortuna_reseed,
|
||||
.ra_seeded = random_fortuna_seeded,
|
||||
#endif
|
||||
|
||||
TUNABLE_INT("kern.random.sys.seeded", &random_context.seeded);
|
||||
|
||||
/* List for the dynamic sysctls */
|
||||
static struct sysctl_ctx_list random_clist;
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
random_check_boolean(SYSCTL_HANDLER_ARGS)
|
||||
{
|
||||
if (oidp->oid_arg1 != NULL && *(u_int *)(oidp->oid_arg1) != 0)
|
||||
*(u_int *)(oidp->oid_arg1) = 1;
|
||||
return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req));
|
||||
}
|
||||
.ra_init = randomdev_init,
|
||||
.ra_deinit = randomdev_deinit,
|
||||
};
|
||||
|
||||
void
|
||||
randomdev_init(void)
|
||||
{
|
||||
struct sysctl_oid *random_sys_o, *random_sys_harvest_o;
|
||||
|
||||
#if defined(RANDOM_YARROW)
|
||||
random_yarrow_init_alg(&random_clist);
|
||||
random_yarrow_init_alg();
|
||||
random_harvestq_init(random_yarrow_process_event, 2);
|
||||
#endif
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
random_fortuna_init_alg(&random_clist);
|
||||
random_fortuna_init_alg();
|
||||
random_harvestq_init(random_fortuna_process_event, 32);
|
||||
#endif
|
||||
|
||||
random_sys_o = SYSCTL_ADD_NODE(&random_clist,
|
||||
SYSCTL_STATIC_CHILDREN(_kern_random),
|
||||
OID_AUTO, "sys", CTLFLAG_RW, 0,
|
||||
"Entropy Device Parameters");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_o),
|
||||
OID_AUTO, "seeded", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&random_context.seeded, 0, random_check_boolean, "I",
|
||||
"Seeded State");
|
||||
|
||||
random_sys_harvest_o = SYSCTL_ADD_NODE(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_o),
|
||||
OID_AUTO, "harvest", CTLFLAG_RW, 0,
|
||||
"Entropy Sources");
|
||||
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_harvest_o),
|
||||
OID_AUTO, "ethernet", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&harvest.ethernet, 1, random_check_boolean, "I",
|
||||
"Harvest NIC entropy");
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_harvest_o),
|
||||
OID_AUTO, "point_to_point", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&harvest.point_to_point, 1, random_check_boolean, "I",
|
||||
"Harvest serial net entropy");
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_harvest_o),
|
||||
OID_AUTO, "interrupt", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&harvest.interrupt, 1, random_check_boolean, "I",
|
||||
"Harvest IRQ entropy");
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_sys_harvest_o),
|
||||
OID_AUTO, "swi", CTLTYPE_INT | CTLFLAG_RW,
|
||||
&harvest.swi, 1, random_check_boolean, "I",
|
||||
"Harvest SWI entropy");
|
||||
|
||||
random_harvestq_init(random_process_event);
|
||||
|
||||
/* Register the randomness harvesting routine */
|
||||
randomdev_init_harvester(random_harvestq_internal,
|
||||
random_context.read);
|
||||
randomdev_init_harvester(random_harvestq_internal);
|
||||
}
|
||||
|
||||
void
|
||||
@ -182,118 +118,56 @@ randomdev_deinit(void)
|
||||
/* Deregister the randomness harvesting routine */
|
||||
randomdev_deinit_harvester();
|
||||
|
||||
/*
|
||||
* Command the hash/reseed thread to end and wait for it to finish
|
||||
*/
|
||||
random_kthread_control = -1;
|
||||
tsleep((void *)&random_kthread_control, 0, "term", 0);
|
||||
|
||||
#if defined(RANDOM_YARROW)
|
||||
random_yarrow_deinit_alg();
|
||||
#endif
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
random_fortuna_deinit_alg();
|
||||
#endif
|
||||
|
||||
sysctl_ctx_free(&random_clist);
|
||||
}
|
||||
|
||||
void
|
||||
randomdev_unblock(void)
|
||||
{
|
||||
if (!random_context.seeded) {
|
||||
selwakeuppri(&random_context.rsel, PUSER);
|
||||
wakeup(&random_context);
|
||||
printf("random: unblocking device.\n");
|
||||
random_context.seeded = 1;
|
||||
}
|
||||
/* Do arc4random(9) a favour while we are about it. */
|
||||
(void)atomic_cmpset_int(&arc4rand_iniseed_state, ARC4_ENTR_NONE,
|
||||
ARC4_ENTR_HAVE);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
randomdev_poll(int events, struct thread *td)
|
||||
{
|
||||
int revents = 0;
|
||||
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
if (random_context.seeded)
|
||||
revents = events & (POLLIN | POLLRDNORM);
|
||||
else
|
||||
selrecord(td, &random_context.rsel);
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
return (revents);
|
||||
}
|
||||
|
||||
static int
|
||||
randomdev_block(int flag)
|
||||
randomdev_soft_modevent(module_t mod __unused, int type, void *unused __unused)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
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;
|
||||
|
||||
/* Blocking logic */
|
||||
while (!random_context.seeded && !error) {
|
||||
if (flag & O_NONBLOCK)
|
||||
error = EWOULDBLOCK;
|
||||
else {
|
||||
printf("random: blocking on read.\n");
|
||||
error = msleep(&random_context,
|
||||
&random_reseed_mtx,
|
||||
PUSER | PCATCH, "block", 0);
|
||||
}
|
||||
}
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/* Helper routine to perform explicit reseeds */
|
||||
static void
|
||||
randomdev_flush_reseed(void)
|
||||
{
|
||||
/* Command a entropy queue flush and wait for it to finish */
|
||||
random_kthread_control = 1;
|
||||
while (random_kthread_control)
|
||||
pause("-", hz / 10);
|
||||
#define MID_DEV_MODULE(name, evh, arg) \
|
||||
static moduledata_t name##_mod = { \
|
||||
#name, \
|
||||
evh, \
|
||||
arg \
|
||||
}; \
|
||||
DECLARE_MODULE(name, name##_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE)
|
||||
|
||||
#if defined(RANDOM_YARROW)
|
||||
/* This ultimately calls randomdev_unblock() */
|
||||
random_yarrow_reseed();
|
||||
MID_DEV_MODULE(yarrow, randomdev_soft_modevent, NULL);
|
||||
MODULE_VERSION(yarrow, 1);
|
||||
MODULE_DEPEND(yarrow, random_adaptors, 1, 1, 1);
|
||||
#endif
|
||||
#if defined(RANDOM_FORTUNA)
|
||||
/* This ultimately calls randomdev_unblock() */
|
||||
random_fortuna_reseed();
|
||||
MID_DEV_MODULE(fortuna, randomdev_soft_modevent, NULL);
|
||||
MODULE_VERSION(fortuna, 1);
|
||||
MODULE_DEPEND(fortuna, random_adaptors, 1, 1, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int
|
||||
randomdev_modevent(module_t mod __unused, int type, void *unused __unused)
|
||||
{
|
||||
|
||||
switch (type) {
|
||||
case MOD_LOAD:
|
||||
random_adaptor_register(RANDOM_CSPRNG_NAME, &random_context);
|
||||
/*
|
||||
* For statically built kernels that contain both device
|
||||
* random and options PADLOCK_RNG/RDRAND_RNG/etc..,
|
||||
* this event handler will do nothing, since the random
|
||||
* driver-specific handlers are loaded after these HW
|
||||
* consumers, and hence hasn't yet registered for this event.
|
||||
*
|
||||
* In case where both the random driver and RNG's are built
|
||||
* as seperate modules, random.ko is loaded prior to *_rng.ko's
|
||||
* (by dependency). This event handler is there to delay
|
||||
* creation of /dev/{u,}random and attachment of this *_rng.ko.
|
||||
*/
|
||||
EVENTHANDLER_INVOKE(random_adaptor_attach, &random_context);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
RANDOM_ADAPTOR_MODULE(RANDOM_MODULE_NAME, randomdev_modevent, 1);
|
||||
|
@ -29,58 +29,11 @@
|
||||
#ifndef SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_RANDOMDEV_SOFT_H_INCLUDED
|
||||
|
||||
/* This header contains only those definitions that are global
|
||||
* and harvester-specific for the entropy processor
|
||||
/* This header contains only those definitions that are
|
||||
* specific to the entropy processor
|
||||
*/
|
||||
|
||||
/* #define ENTROPYSOURCE nn entropy sources (actually classes)
|
||||
* This is properly defined in
|
||||
* an enum in sys/random.h
|
||||
*/
|
||||
|
||||
/* The ring size _MUST_ be a power of 2 */
|
||||
#define HARVEST_RING_SIZE 1024 /* harvest ring buffer size */
|
||||
#define HARVEST_RING_MASK (HARVEST_RING_SIZE - 1)
|
||||
|
||||
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
|
||||
|
||||
/* These are used to queue harvested packets of entropy. The entropy
|
||||
* buffer size is pretty arbitrary.
|
||||
*/
|
||||
struct harvest {
|
||||
uintmax_t somecounter; /* fast counter for clock jitter */
|
||||
uint8_t entropy[HARVESTSIZE]; /* the harvested entropy */
|
||||
u_int size, bits; /* stats about the entropy */
|
||||
enum esource source; /* origin of the entropy */
|
||||
STAILQ_ENTRY(harvest) next; /* next item on the list */
|
||||
};
|
||||
|
||||
void randomdev_init(void);
|
||||
void randomdev_deinit(void);
|
||||
|
||||
void randomdev_init_harvester(void (*)(u_int64_t, const void *, u_int,
|
||||
u_int, enum esource), int (*)(void *, int));
|
||||
void randomdev_deinit_harvester(void);
|
||||
|
||||
void random_set_wakeup_exit(void *);
|
||||
void random_process_event(struct harvest *event);
|
||||
void randomdev_unblock(void);
|
||||
|
||||
extern struct mtx random_reseed_mtx;
|
||||
|
||||
/* If this was C++, the macro below would be a template */
|
||||
#define RANDOM_CHECK_UINT(name, min, max) \
|
||||
static int \
|
||||
random_check_uint_##name(SYSCTL_HANDLER_ARGS) \
|
||||
{ \
|
||||
if (oidp->oid_arg1 != NULL) { \
|
||||
if (*(u_int *)(oidp->oid_arg1) <= (min)) \
|
||||
*(u_int *)(oidp->oid_arg1) = (min); \
|
||||
else if (*(u_int *)(oidp->oid_arg1) > (max)) \
|
||||
*(u_int *)(oidp->oid_arg1) = (max); \
|
||||
} \
|
||||
return (sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, \
|
||||
req)); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,96 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_random.h"
|
||||
|
||||
#ifdef RANDOM_RWFILE
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/namei.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/vnode.h>
|
||||
|
||||
#include <dev/random/rwfile.h>
|
||||
|
||||
int
|
||||
randomdev_read_file(const char *filename, void *buf, size_t length)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct thread* td = curthread;
|
||||
int error;
|
||||
ssize_t resid;
|
||||
int flags;
|
||||
|
||||
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
|
||||
flags = FREAD;
|
||||
error = vn_open(&nd, &flags, 0, NULL);
|
||||
if (error == 0) {
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
if (nd.ni_vp->v_type != VREG)
|
||||
error = ENOEXEC;
|
||||
else
|
||||
error = vn_rdwr(UIO_READ, nd.ni_vp, buf, length, 0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td);
|
||||
VOP_UNLOCK(nd.ni_vp, 0);
|
||||
vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
randomdev_write_file(const char *filename, void *buf, size_t length)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct thread* td = curthread;
|
||||
int error;
|
||||
ssize_t resid;
|
||||
int flags;
|
||||
|
||||
NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
|
||||
flags = FWRITE | O_CREAT | O_TRUNC;
|
||||
error = vn_open(&nd, &flags, 0, NULL);
|
||||
if (error == 0) {
|
||||
NDFREE(&nd, NDF_ONLY_PNBUF);
|
||||
if (nd.ni_vp->v_type != VREG)
|
||||
error = ENOEXEC;
|
||||
else
|
||||
error = vn_rdwr(UIO_WRITE, nd.ni_vp, buf, length, 0, UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td);
|
||||
|
||||
VOP_UNLOCK(nd.ni_vp, 0);
|
||||
vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
|
||||
}
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
#endif
|
75
sys/dev/random/uint128.h
Normal file
75
sys/dev/random/uint128.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef SYS_DEV_RANDOM_UINT128_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_UINT128_H_INCLUDED
|
||||
|
||||
/* This whole thing is a crock :-(
|
||||
*
|
||||
* Everyone knows you always need the __uint128_t types!
|
||||
*/
|
||||
|
||||
#ifdef __SIZEOF_INT128__
|
||||
typedef __uint128_t uint128_t;
|
||||
#else
|
||||
typedef uint64_t uint128_t[2];
|
||||
#endif
|
||||
|
||||
static __inline void
|
||||
uint128_clear(uint128_t *big_uint)
|
||||
{
|
||||
#ifdef __SIZEOF_INT128__
|
||||
(*big_uint) = 0ULL;
|
||||
#else
|
||||
(*big_uint)[0] = (*big_uint)[1] = 0UL;
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline void
|
||||
uint128_increment(uint128_t *big_uint)
|
||||
{
|
||||
#ifdef __SIZEOF_INT128__
|
||||
(*big_uint)++;
|
||||
#else
|
||||
(*big_uint)[0]++;
|
||||
if ((*big_uint)[0] == 0UL)
|
||||
(*big_uint)[1]++;
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline int
|
||||
uint128_is_zero(uint128_t big_uint)
|
||||
{
|
||||
#ifdef __SIZEOF_INT128__
|
||||
return (big_uint == 0ULL);
|
||||
#else
|
||||
return (big_uint[0] == 0UL && big_uint[1] == 0UL);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* SYS_DEV_RANDOM_UINT128_H_INCLUDED */
|
257
sys/dev/random/unit_test.c
Normal file
257
sys/dev/random/unit_test.c
Normal file
@ -0,0 +1,257 @@
|
||||
/*-
|
||||
* Copyright (c) 2000-2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
Build this by going:
|
||||
|
||||
cc -g -O0 -pthread -DRANDOM_<alg> -DRANDOM_DEBUG -I../.. -lstdthreads -Wall \
|
||||
unit_test.c \
|
||||
yarrow.c \
|
||||
fortuna.c \
|
||||
hash.c \
|
||||
../../crypto/rijndael/rijndael-api-fst.c \
|
||||
../../crypto/rijndael/rijndael-alg-fst.c \
|
||||
../../crypto/sha2/sha2.c \
|
||||
-o unit_test
|
||||
./unit_test
|
||||
|
||||
Where <alg> is YARROW or FORTUNA.
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "unit_test.h"
|
||||
|
||||
#ifdef RANDOM_YARROW
|
||||
#include "dev/random/yarrow.h"
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
#include "dev/random/fortuna.h"
|
||||
#endif
|
||||
|
||||
#define NUM_THREADS 3
|
||||
|
||||
static volatile int stopseeding = 0;
|
||||
|
||||
void
|
||||
random_adaptor_unblock(void)
|
||||
{
|
||||
|
||||
#if 0
|
||||
if (mtx_trylock(&random_reseed_mtx) == thrd_busy)
|
||||
printf("Mutex held. Good.\n");
|
||||
else {
|
||||
printf("Mutex not held. PANIC!!\n");
|
||||
thrd_exit(0);
|
||||
}
|
||||
#endif
|
||||
printf("random: unblocking device.\n");
|
||||
}
|
||||
|
||||
static int
|
||||
RunHarvester(void *arg __unused)
|
||||
{
|
||||
int i, r;
|
||||
struct harvest_event e;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (stopseeding)
|
||||
break;
|
||||
if (i % 1000 == 0)
|
||||
printf("Harvest: %d\n", i);
|
||||
r = random()%10;
|
||||
e.he_somecounter = i;
|
||||
*((uint64_t *)e.he_entropy) = random();
|
||||
e.he_size = 8;
|
||||
e.he_bits = random()%4;
|
||||
e.he_destination = i;
|
||||
e.he_source = (i + 3)%7;
|
||||
e.he_next = NULL;
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_process_event(&e);
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_process_event(&e);
|
||||
#endif
|
||||
usleep(r);
|
||||
}
|
||||
|
||||
printf("Thread #0 ends\n");
|
||||
|
||||
thrd_exit(0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
WriteCSPRNG(void *threadid)
|
||||
{
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
|
||||
printf("Thread #1 starts\n");
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
if (stopseeding)
|
||||
break;
|
||||
buf = malloc(4096);
|
||||
if (i % 1000 == 0)
|
||||
printf("Thread write 1 - %d\n", i);
|
||||
if (buf != NULL) {
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_write(buf, i);
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_write(buf, i);
|
||||
#endif
|
||||
free(buf);
|
||||
}
|
||||
usleep(1000000);
|
||||
}
|
||||
|
||||
printf("Thread #1 ends\n");
|
||||
|
||||
thrd_exit(0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
ReadCSPRNG(void *threadid)
|
||||
{
|
||||
size_t tid;
|
||||
uint8_t *buf;
|
||||
int i;
|
||||
|
||||
tid = (size_t)threadid;
|
||||
printf("Thread #%zd starts\n", tid);
|
||||
|
||||
#ifdef RANDOM_YARROW
|
||||
while (!random_yarrow_seeded())
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
while (!random_fortuna_seeded())
|
||||
#endif
|
||||
{
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_read(NULL, 0);
|
||||
random_yarrow_read(NULL, 1);
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_read(NULL, 0);
|
||||
random_fortuna_read(NULL, 1);
|
||||
#endif
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
for (i = 0; i < 100000; i++) {
|
||||
buf = malloc(i);
|
||||
if (i % 1000 == 0)
|
||||
printf("Thread read %zd - %d %p\n", tid, i, buf);
|
||||
if (buf != NULL) {
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_read(NULL, 0);
|
||||
random_yarrow_read(buf, i);
|
||||
random_yarrow_read(NULL, 1);
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_read(NULL, 0);
|
||||
random_fortuna_read(buf, i);
|
||||
random_fortuna_read(NULL, 1);
|
||||
#endif
|
||||
#if 0
|
||||
{
|
||||
int j;
|
||||
|
||||
for (j = 0; j < i; j++) {
|
||||
printf(" %02X", buf[j]);
|
||||
if (j % 32 == 31 || j == i - 1)
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
free(buf);
|
||||
}
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
printf("Thread #%zd ends\n", tid);
|
||||
|
||||
thrd_exit(0);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
thrd_t threads[NUM_THREADS];
|
||||
int rc;
|
||||
long t;
|
||||
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_init_alg();
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_init_alg();
|
||||
#endif
|
||||
|
||||
for (t = 0; t < NUM_THREADS; t++) {
|
||||
printf("In main: creating thread %ld\n", t);
|
||||
rc = thrd_create(&threads[t], (t == 0 ? RunHarvester : (t == 1 ? WriteCSPRNG : ReadCSPRNG)), t);
|
||||
if (rc != thrd_success) {
|
||||
printf("ERROR; return code from thrd_create() is %d\n", rc);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
for (t = 2; t < NUM_THREADS; t++)
|
||||
thrd_join(threads[t], &rc);
|
||||
|
||||
stopseeding = 1;
|
||||
|
||||
thrd_join(threads[1], &rc);
|
||||
thrd_join(threads[0], &rc);
|
||||
|
||||
#ifdef RANDOM_YARROW
|
||||
random_yarrow_deinit_alg();
|
||||
#endif
|
||||
#ifdef RANDOM_FORTUNA
|
||||
random_fortuna_deinit_alg();
|
||||
#endif
|
||||
|
||||
/* Last thing that main() should do */
|
||||
thrd_exit(0);
|
||||
|
||||
return (0);
|
||||
}
|
72
sys/dev/random/unit_test.h
Normal file
72
sys/dev/random/unit_test.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Mark R V Murray
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UNIT_TEST_H_INCLUDED
|
||||
#define UNIT_TEST_H_INCLUDED
|
||||
|
||||
void random_adaptor_unblock(void);
|
||||
|
||||
static __inline uint64_t
|
||||
get_cyclecount(void)
|
||||
{
|
||||
|
||||
/* Shaddup! */
|
||||
return (4ULL);
|
||||
}
|
||||
|
||||
// #define PAGE_SIZE 4096
|
||||
#define HARVESTSIZE 16
|
||||
|
||||
enum random_entropy_source {
|
||||
RANDOM_START = 0,
|
||||
RANDOM_CACHED = 0,
|
||||
ENTROPYSOURCE = 32
|
||||
};
|
||||
|
||||
struct harvest_event {
|
||||
uintmax_t he_somecounter; /* fast counter for clock jitter */
|
||||
uint8_t he_entropy[HARVESTSIZE];/* some harvested entropy */
|
||||
u_int he_size; /* harvested entropy byte count */
|
||||
u_int he_bits; /* stats about the entropy */
|
||||
u_int he_destination; /* destination pool of this entropy */
|
||||
enum random_entropy_source he_source; /* origin of the entropy */
|
||||
void * he_next; /* next item on the list */
|
||||
};
|
||||
|
||||
struct sysctl_ctx_list;
|
||||
|
||||
#define CTASSERT(x) _Static_assert(x, "compile-time assertion failed")
|
||||
#define KASSERT(exp,msg) do { \
|
||||
if (!(exp)) { \
|
||||
printf msg; \
|
||||
exit(0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* UNIT_TEST_H_INCLUDED */
|
@ -28,6 +28,7 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef _KERNEL
|
||||
#include "opt_random.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -39,197 +40,280 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#include <crypto/rijndael/rijndael-api-fst.h>
|
||||
#include <crypto/sha2/sha2.h>
|
||||
|
||||
#include <dev/random/hash.h>
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/random_adaptors.h>
|
||||
#include <dev/random/randomdev_soft.h>
|
||||
#include <dev/random/random_harvestq.h>
|
||||
#include <dev/random/uint128.h>
|
||||
#include <dev/random/yarrow.h>
|
||||
#else /* !_KERNEL */
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include "unit_test.h"
|
||||
|
||||
#include <crypto/rijndael/rijndael-api-fst.h>
|
||||
#include <crypto/sha2/sha2.h>
|
||||
|
||||
#include <dev/random/hash.h>
|
||||
#include <dev/random/randomdev.h>
|
||||
#include <dev/random/uint128.h>
|
||||
#include <dev/random/yarrow.h>
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#if !defined(RANDOM_YARROW) && !defined(RANDOM_FORTUNA)
|
||||
#define RANDOM_YARROW
|
||||
#elif defined(RANDOM_YARROW) && defined(RANDOM_FORTUNA)
|
||||
#error "Must define either RANDOM_YARROW or RANDOM_FORTUNA"
|
||||
#endif
|
||||
|
||||
#if defined(RANDOM_YARROW)
|
||||
|
||||
#define TIMEBIN 16 /* max value for Pt/t */
|
||||
|
||||
#define FAST 0
|
||||
#define SLOW 1
|
||||
|
||||
/* This algorithm (and code) presumes that KEYSIZE is twice as large as BLOCKSIZE */
|
||||
CTASSERT(BLOCKSIZE == sizeof(uint128_t));
|
||||
CTASSERT(KEYSIZE == 2*BLOCKSIZE);
|
||||
|
||||
/* This is the beastie that needs protecting. It contains all of the
|
||||
* state that we are excited about.
|
||||
* Exactly one is instantiated.
|
||||
*/
|
||||
static struct random_state {
|
||||
static struct yarrow_state {
|
||||
union {
|
||||
uint8_t byte[BLOCKSIZE];
|
||||
uint64_t qword[BLOCKSIZE/sizeof(uint64_t)];
|
||||
} counter; /* C */
|
||||
struct randomdev_key key; /* K */
|
||||
u_int gengateinterval; /* Pg */
|
||||
u_int bins; /* Pt/t */
|
||||
u_int outputblocks; /* count output blocks for gates */
|
||||
u_int slowoverthresh; /* slow pool overthreshhold reseed count */
|
||||
uint128_t whole;
|
||||
} counter; /* C */
|
||||
struct randomdev_key key; /* K */
|
||||
u_int gengateinterval; /* Pg */
|
||||
u_int bins; /* Pt/t */
|
||||
u_int outputblocks; /* count output blocks for gates */
|
||||
u_int slowoverthresh; /* slow pool overthreshhold reseed count */
|
||||
struct pool {
|
||||
struct source {
|
||||
u_int bits; /* estimated bits of entropy */
|
||||
} source[ENTROPYSOURCE];
|
||||
u_int thresh; /* pool reseed threshhold */
|
||||
} source[ENTROPYSOURCE];/* ... per source */
|
||||
u_int thresh; /* pool reseed threshhold */
|
||||
struct randomdev_hash hash; /* accumulated entropy */
|
||||
} pool[2]; /* pool[0] is fast, pool[1] is slow */
|
||||
u_int which; /* toggle - sets the current insertion pool */
|
||||
} random_state;
|
||||
} pool[2]; /* pool[0] is fast, pool[1] is slow */
|
||||
int seeded;
|
||||
|
||||
struct start_cache {
|
||||
uint8_t junk[KEYSIZE];
|
||||
struct randomdev_hash hash;
|
||||
} start_cache;
|
||||
} yarrow_state;
|
||||
|
||||
/* The random_reseed_mtx mutex protects seeding and polling/blocking. */
|
||||
static mtx_t random_reseed_mtx;
|
||||
|
||||
#ifdef _KERNEL
|
||||
static struct sysctl_ctx_list random_clist;
|
||||
RANDOM_CHECK_UINT(gengateinterval, 4, 64);
|
||||
RANDOM_CHECK_UINT(bins, 2, 16);
|
||||
RANDOM_CHECK_UINT(fastthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */
|
||||
RANDOM_CHECK_UINT(slowthresh, (BLOCKSIZE*8)/4, (BLOCKSIZE*8)); /* Bit counts */
|
||||
RANDOM_CHECK_UINT(slowoverthresh, 1, 5);
|
||||
#else /* !_KERNEL */
|
||||
static u_int harvest_destination[ENTROPYSOURCE];
|
||||
#endif /* _KERNEL */
|
||||
|
||||
static void generator_gate(void);
|
||||
static void reseed(u_int);
|
||||
|
||||
/* The reseed thread mutex */
|
||||
struct mtx random_reseed_mtx;
|
||||
|
||||
/* 128-bit C = 0 */
|
||||
/* Nothing to see here, folks, just an ugly mess. */
|
||||
static void
|
||||
clear_counter(void)
|
||||
{
|
||||
random_state.counter.qword[0] = 0UL;
|
||||
random_state.counter.qword[1] = 0UL;
|
||||
}
|
||||
|
||||
/* 128-bit C = C + 1 */
|
||||
/* Nothing to see here, folks, just an ugly mess. */
|
||||
/* TODO: Make a Galois counter instead? */
|
||||
static void
|
||||
increment_counter(void)
|
||||
{
|
||||
random_state.counter.qword[0]++;
|
||||
if (!random_state.counter.qword[0])
|
||||
random_state.counter.qword[1]++;
|
||||
}
|
||||
|
||||
/* Process a single stochastic event off the harvest queue */
|
||||
void
|
||||
random_process_event(struct harvest *event)
|
||||
random_yarrow_init_alg(void)
|
||||
{
|
||||
u_int pl, overthreshhold[2];
|
||||
struct source *source;
|
||||
enum esource src;
|
||||
|
||||
#if 0
|
||||
/* Do this better with DTrace */
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Harvest:%16jX ", event->somecounter);
|
||||
for (i = 0; i < event->size; i++)
|
||||
printf("%02X", event->entropy[i]);
|
||||
for (; i < 16; i++)
|
||||
printf(" ");
|
||||
printf(" %2d %2d %02X\n", event->size, event->bits, event->source);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Accumulate the event into the appropriate pool */
|
||||
pl = random_state.which;
|
||||
source = &random_state.pool[pl].source[event->source];
|
||||
randomdev_hash_iterate(&random_state.pool[pl].hash, event,
|
||||
sizeof(*event));
|
||||
source->bits += event->bits;
|
||||
|
||||
/* Count the over-threshold sources in each pool */
|
||||
for (pl = 0; pl < 2; pl++) {
|
||||
overthreshhold[pl] = 0;
|
||||
for (src = RANDOM_START; src < ENTROPYSOURCE; src++) {
|
||||
if (random_state.pool[pl].source[src].bits
|
||||
> random_state.pool[pl].thresh)
|
||||
overthreshhold[pl]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* if any fast source over threshhold, reseed */
|
||||
if (overthreshhold[FAST])
|
||||
reseed(FAST);
|
||||
|
||||
/* if enough slow sources are over threshhold, reseed */
|
||||
if (overthreshhold[SLOW] >= random_state.slowoverthresh)
|
||||
reseed(SLOW);
|
||||
|
||||
/* Invert the fast/slow pool selector bit */
|
||||
random_state.which = !random_state.which;
|
||||
}
|
||||
|
||||
void
|
||||
random_yarrow_init_alg(struct sysctl_ctx_list *clist)
|
||||
{
|
||||
int i;
|
||||
int i, j;
|
||||
#ifdef _KERNEL
|
||||
struct sysctl_oid *random_yarrow_o;
|
||||
#endif /* _KERNEL */
|
||||
|
||||
memset(yarrow_state.start_cache.junk, 0, KEYSIZE);
|
||||
randomdev_hash_init(&yarrow_state.start_cache.hash);
|
||||
|
||||
/* Set up the lock for the reseed/gate state */
|
||||
#ifdef _KERNEL
|
||||
mtx_init(&random_reseed_mtx, "reseed mutex", NULL, MTX_DEF);
|
||||
#else /* !_KERNEL */
|
||||
mtx_init(&random_reseed_mtx, mtx_plain);
|
||||
#endif /* _KERNEL */
|
||||
|
||||
/* Start unseeded, therefore blocked. */
|
||||
yarrow_state.seeded = 0;
|
||||
|
||||
#ifdef _KERNEL
|
||||
/* Yarrow parameters. Do not adjust these unless you have
|
||||
* have a very good clue about what they do!
|
||||
*/
|
||||
random_yarrow_o = SYSCTL_ADD_NODE(clist,
|
||||
random_yarrow_o = SYSCTL_ADD_NODE(&random_clist,
|
||||
SYSCTL_STATIC_CHILDREN(_kern_random),
|
||||
OID_AUTO, "yarrow", CTLFLAG_RW, 0,
|
||||
"Yarrow Parameters");
|
||||
|
||||
SYSCTL_ADD_PROC(clist,
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
|
||||
"gengateinterval", CTLTYPE_INT|CTLFLAG_RW,
|
||||
&random_state.gengateinterval, 10,
|
||||
&yarrow_state.gengateinterval, 10,
|
||||
random_check_uint_gengateinterval, "I",
|
||||
"Generation gate interval");
|
||||
|
||||
SYSCTL_ADD_PROC(clist,
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
|
||||
"bins", CTLTYPE_INT|CTLFLAG_RW,
|
||||
&random_state.bins, 10,
|
||||
&yarrow_state.bins, 10,
|
||||
random_check_uint_bins, "I",
|
||||
"Execution time tuner");
|
||||
|
||||
SYSCTL_ADD_PROC(clist,
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
|
||||
"fastthresh", CTLTYPE_INT|CTLFLAG_RW,
|
||||
&random_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4,
|
||||
&yarrow_state.pool[0].thresh, (3*(BLOCKSIZE*8))/4,
|
||||
random_check_uint_fastthresh, "I",
|
||||
"Fast reseed threshold");
|
||||
|
||||
SYSCTL_ADD_PROC(clist,
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
|
||||
"slowthresh", CTLTYPE_INT|CTLFLAG_RW,
|
||||
&random_state.pool[1].thresh, (BLOCKSIZE*8),
|
||||
&yarrow_state.pool[1].thresh, (BLOCKSIZE*8),
|
||||
random_check_uint_slowthresh, "I",
|
||||
"Slow reseed threshold");
|
||||
|
||||
SYSCTL_ADD_PROC(clist,
|
||||
SYSCTL_ADD_PROC(&random_clist,
|
||||
SYSCTL_CHILDREN(random_yarrow_o), OID_AUTO,
|
||||
"slowoverthresh", CTLTYPE_INT|CTLFLAG_RW,
|
||||
&random_state.slowoverthresh, 2,
|
||||
&yarrow_state.slowoverthresh, 2,
|
||||
random_check_uint_slowoverthresh, "I",
|
||||
"Slow over-threshold reseed");
|
||||
#endif /* _KERNEL */
|
||||
|
||||
random_state.gengateinterval = 10;
|
||||
random_state.bins = 10;
|
||||
random_state.pool[0].thresh = (3*(BLOCKSIZE*8))/4;
|
||||
random_state.pool[1].thresh = (BLOCKSIZE*8);
|
||||
random_state.slowoverthresh = 2;
|
||||
random_state.which = FAST;
|
||||
yarrow_state.gengateinterval = 10;
|
||||
yarrow_state.bins = 10;
|
||||
yarrow_state.pool[FAST].thresh = (3*(BLOCKSIZE*8))/4;
|
||||
yarrow_state.pool[SLOW].thresh = (BLOCKSIZE*8);
|
||||
yarrow_state.slowoverthresh = 2;
|
||||
|
||||
/* Ensure that the first time we read, we are gated. */
|
||||
yarrow_state.outputblocks = yarrow_state.gengateinterval;
|
||||
|
||||
/* Initialise the fast and slow entropy pools */
|
||||
for (i = 0; i < 2; i++)
|
||||
randomdev_hash_init(&random_state.pool[i].hash);
|
||||
for (i = FAST; i <= SLOW; i++) {
|
||||
randomdev_hash_init(&yarrow_state.pool[i].hash);
|
||||
for (j = RANDOM_START; j < ENTROPYSOURCE; j++)
|
||||
yarrow_state.pool[i].source[j].bits = 0U;
|
||||
}
|
||||
|
||||
/* Clear the counter */
|
||||
clear_counter();
|
||||
|
||||
/* Set up a lock for the reseed process */
|
||||
mtx_init(&random_reseed_mtx, "Yarrow reseed", NULL, MTX_DEF);
|
||||
uint128_clear(&yarrow_state.counter.whole);
|
||||
}
|
||||
|
||||
void
|
||||
random_yarrow_deinit_alg(void)
|
||||
{
|
||||
|
||||
mtx_destroy(&random_reseed_mtx);
|
||||
memset(&yarrow_state, 0, sizeof(yarrow_state));
|
||||
|
||||
#ifdef _KERNEL
|
||||
sysctl_ctx_free(&random_clist);
|
||||
#endif
|
||||
}
|
||||
|
||||
static __inline void
|
||||
random_yarrow_post_insert(void)
|
||||
{
|
||||
u_int pl, overthreshhold[2];
|
||||
enum random_entropy_source src;
|
||||
|
||||
#ifdef _KERNEL
|
||||
mtx_assert(&random_reseed_mtx, MA_OWNED);
|
||||
#endif
|
||||
/* Count the over-threshold sources in each pool */
|
||||
for (pl = 0; pl < 2; pl++) {
|
||||
overthreshhold[pl] = 0;
|
||||
for (src = RANDOM_START; src < ENTROPYSOURCE; src++) {
|
||||
if (yarrow_state.pool[pl].source[src].bits > yarrow_state.pool[pl].thresh)
|
||||
overthreshhold[pl]++;
|
||||
}
|
||||
}
|
||||
|
||||
/* If enough slow sources are over threshhold, then slow reseed
|
||||
* else if any fast source over threshhold, then fast reseed.
|
||||
*/
|
||||
if (overthreshhold[SLOW] >= yarrow_state.slowoverthresh)
|
||||
reseed(SLOW);
|
||||
else if (overthreshhold[FAST] > 0 && yarrow_state.seeded)
|
||||
reseed(FAST);
|
||||
}
|
||||
|
||||
/* Process a single stochastic event off the harvest queue */
|
||||
void
|
||||
random_yarrow_process_event(struct harvest_event *event)
|
||||
{
|
||||
u_int pl;
|
||||
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
/* Accumulate the event into the appropriate pool
|
||||
* where each event carries the destination information.
|
||||
* We lock against pool state modification which can happen
|
||||
* during accumulation/reseeding and reading/regating
|
||||
*/
|
||||
pl = event->he_destination % 2;
|
||||
randomdev_hash_iterate(&yarrow_state.pool[pl].hash, event, sizeof(*event));
|
||||
yarrow_state.pool[pl].source[event->he_source].bits += event->he_bits;
|
||||
|
||||
random_yarrow_post_insert();
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
}
|
||||
|
||||
/* Process a block of data suspected to be slightly stochastic */
|
||||
static void
|
||||
random_yarrow_process_buffer(uint8_t *buf, u_int length)
|
||||
{
|
||||
static struct harvest_event event;
|
||||
u_int i, pl;
|
||||
|
||||
/* Accumulate the data into the appropriate pools
|
||||
* where each event carries the destination information.
|
||||
* We lock against pool state modification which can happen
|
||||
* during accumulation/reseeding and reading/regating
|
||||
*/
|
||||
memset(event.he_entropy + sizeof(uint32_t), 0, HARVESTSIZE - sizeof(uint32_t));
|
||||
for (i = 0; i < length/sizeof(uint32_t); i++) {
|
||||
event.he_somecounter = get_cyclecount();
|
||||
event.he_bits = 0; /* Fake */
|
||||
event.he_source = RANDOM_CACHED;
|
||||
event.he_destination = harvest_destination[RANDOM_CACHED]++;
|
||||
event.he_size = sizeof(uint32_t);
|
||||
*((uint32_t *)event.he_entropy) = *((uint32_t *)buf + i);
|
||||
|
||||
/* Do the actual entropy insertion */
|
||||
pl = event.he_destination % 2;
|
||||
randomdev_hash_iterate(&yarrow_state.pool[pl].hash, &event, sizeof(event));
|
||||
#ifdef DONT_DO_THIS_HERE
|
||||
/* Don't do this here - do it in bulk at the end */
|
||||
yarrow_state.pool[pl].source[RANDOM_CACHED].bits += bits;
|
||||
#endif
|
||||
}
|
||||
for (pl = FAST; pl <= SLOW; pl++)
|
||||
yarrow_state.pool[pl].source[RANDOM_CACHED].bits += (length >> 4);
|
||||
|
||||
random_yarrow_post_insert();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -239,45 +323,60 @@ reseed(u_int fastslow)
|
||||
* structures static.
|
||||
*/
|
||||
static uint8_t v[TIMEBIN][KEYSIZE]; /* v[i] */
|
||||
static uint8_t hash[KEYSIZE]; /* h' */
|
||||
static uint8_t temp[KEYSIZE];
|
||||
static struct randomdev_hash context;
|
||||
uint8_t hash[KEYSIZE]; /* h' */
|
||||
uint8_t temp[KEYSIZE];
|
||||
u_int i;
|
||||
enum esource j;
|
||||
enum random_entropy_source j;
|
||||
|
||||
#if 0
|
||||
printf("Yarrow: %s reseed\n", fastslow == FAST ? "fast" : "slow");
|
||||
KASSERT(yarrow_state.pool[FAST].thresh > 0, ("random: Yarrow fast threshold = 0"));
|
||||
KASSERT(yarrow_state.pool[SLOW].thresh > 0, ("random: Yarrow slow threshold = 0"));
|
||||
|
||||
#ifdef RANDOM_DEBUG
|
||||
#ifdef RANDOM_DEBUG_VERBOSE
|
||||
printf("random: %s %s\n", __func__, (fastslow == FAST ? "FAST" : "SLOW"));
|
||||
#endif
|
||||
if (!yarrow_state.seeded) {
|
||||
printf("random: %s - fast - thresh %d,1 - ", __func__, yarrow_state.pool[FAST].thresh);
|
||||
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
|
||||
printf(" %d", yarrow_state.pool[FAST].source[i].bits);
|
||||
printf("\n");
|
||||
printf("random: %s - slow - thresh %d,%d - ", __func__, yarrow_state.pool[SLOW].thresh, yarrow_state.slowoverthresh);
|
||||
for (i = RANDOM_START; i < ENTROPYSOURCE; i++)
|
||||
printf(" %d", yarrow_state.pool[SLOW].source[i].bits);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
#ifdef _KERNEL
|
||||
mtx_assert(&random_reseed_mtx, MA_OWNED);
|
||||
#endif
|
||||
|
||||
/* The reseed task must not be jumped on */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
/* 1. Hash the accumulated entropy into v[0] */
|
||||
|
||||
randomdev_hash_init(&context);
|
||||
/* Feed the slow pool hash in if slow */
|
||||
if (fastslow == SLOW)
|
||||
randomdev_hash_iterate(&context,
|
||||
&random_state.pool[SLOW].hash,
|
||||
sizeof(struct randomdev_hash));
|
||||
randomdev_hash_iterate(&context,
|
||||
&random_state.pool[FAST].hash, sizeof(struct randomdev_hash));
|
||||
if (fastslow == SLOW) {
|
||||
randomdev_hash_finish(&yarrow_state.pool[SLOW].hash, temp);
|
||||
randomdev_hash_iterate(&context, temp, sizeof(temp));
|
||||
}
|
||||
randomdev_hash_finish(&yarrow_state.pool[FAST].hash, temp);
|
||||
randomdev_hash_iterate(&context, temp, sizeof(temp));
|
||||
randomdev_hash_finish(&context, v[0]);
|
||||
|
||||
/* 2. Compute hash values for all v. _Supposed_ to be computationally
|
||||
* intensive.
|
||||
*/
|
||||
|
||||
if (random_state.bins > TIMEBIN)
|
||||
random_state.bins = TIMEBIN;
|
||||
for (i = 1; i < random_state.bins; i++) {
|
||||
if (yarrow_state.bins > TIMEBIN)
|
||||
yarrow_state.bins = TIMEBIN;
|
||||
for (i = 1; i < yarrow_state.bins; i++) {
|
||||
randomdev_hash_init(&context);
|
||||
/* v[i] #= h(v[i - 1]) */
|
||||
randomdev_hash_iterate(&context, v[i - 1], KEYSIZE);
|
||||
/* v[i] #= h(v[0]) */
|
||||
randomdev_hash_iterate(&context, v[0], KEYSIZE);
|
||||
/* v[i] #= h(i) */
|
||||
randomdev_hash_iterate(&context, &i, sizeof(u_int));
|
||||
randomdev_hash_iterate(&context, &i, sizeof(i));
|
||||
/* Return the hashval */
|
||||
randomdev_hash_finish(&context, v[i]);
|
||||
}
|
||||
@ -287,98 +386,107 @@ reseed(u_int fastslow)
|
||||
*/
|
||||
|
||||
randomdev_hash_init(&context);
|
||||
randomdev_hash_iterate(&context, &random_state.key, KEYSIZE);
|
||||
for (i = 1; i < random_state.bins; i++)
|
||||
randomdev_hash_iterate(&context, &v[i], KEYSIZE);
|
||||
randomdev_hash_iterate(&context, &yarrow_state.key, KEYSIZE);
|
||||
for (i = 1; i < yarrow_state.bins; i++)
|
||||
randomdev_hash_iterate(&context, v[i], KEYSIZE);
|
||||
randomdev_hash_finish(&context, temp);
|
||||
randomdev_encrypt_init(&random_state.key, temp);
|
||||
randomdev_encrypt_init(&yarrow_state.key, temp);
|
||||
|
||||
/* 4. Recompute the counter */
|
||||
|
||||
clear_counter();
|
||||
randomdev_encrypt(&random_state.key, random_state.counter.byte, temp, BLOCKSIZE);
|
||||
memcpy(random_state.counter.byte, temp, BLOCKSIZE);
|
||||
uint128_clear(&yarrow_state.counter.whole);
|
||||
randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp, BLOCKSIZE);
|
||||
memcpy(yarrow_state.counter.byte, temp, BLOCKSIZE);
|
||||
|
||||
/* 5. Reset entropy estimate accumulators to zero */
|
||||
|
||||
for (i = 0; i <= fastslow; i++)
|
||||
for (j = RANDOM_START; j < ENTROPYSOURCE; j++)
|
||||
random_state.pool[i].source[j].bits = 0;
|
||||
yarrow_state.pool[i].source[j].bits = 0;
|
||||
|
||||
/* 6. Wipe memory of intermediate values */
|
||||
|
||||
memset((void *)v, 0, sizeof(v));
|
||||
memset((void *)temp, 0, sizeof(temp));
|
||||
memset((void *)hash, 0, sizeof(hash));
|
||||
memset(v, 0, sizeof(v));
|
||||
memset(temp, 0, sizeof(temp));
|
||||
memset(hash, 0, sizeof(hash));
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
#ifdef RANDOM_RWFILE_WRITE_IS_OK /* Not defined so writes ain't gonna happen */
|
||||
/* 7. Dump to seed file */
|
||||
/* XXX Not done here yet */
|
||||
|
||||
/* This pseudo-code is documentation. Please leave it alone. */
|
||||
seed_file = "<some file>";
|
||||
error = randomdev_write_file(seed_file, <generated entropy>, PAGE_SIZE);
|
||||
if (error == 0)
|
||||
printf("random: entropy seed file '%s' successfully written\n", seed_file);
|
||||
#endif
|
||||
|
||||
/* Unblock the device if it was blocked due to being unseeded */
|
||||
randomdev_unblock();
|
||||
|
||||
/* Release the reseed mutex */
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
if (!yarrow_state.seeded) {
|
||||
yarrow_state.seeded = 1;
|
||||
random_adaptor_unblock();
|
||||
}
|
||||
}
|
||||
|
||||
/* Internal function to return processed entropy from the PRNG */
|
||||
int
|
||||
random_yarrow_read(void *buf, int count)
|
||||
void
|
||||
random_yarrow_read(uint8_t *buf, u_int bytecount)
|
||||
{
|
||||
static int cur = 0;
|
||||
static int gate = 1;
|
||||
static uint8_t genval[KEYSIZE];
|
||||
size_t tomove;
|
||||
int i;
|
||||
int retval;
|
||||
u_int blockcount, i;
|
||||
|
||||
/* Check for final read request */
|
||||
if (buf == NULL && count == 0)
|
||||
return (0);
|
||||
/* Check for initial/final read requests */
|
||||
if (buf == NULL)
|
||||
return;
|
||||
|
||||
/* The reseed task must not be jumped on */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
if (gate) {
|
||||
generator_gate();
|
||||
random_state.outputblocks = 0;
|
||||
gate = 0;
|
||||
}
|
||||
if (count > 0 && (size_t)count >= BLOCKSIZE) {
|
||||
retval = 0;
|
||||
for (i = 0; i < count; i += BLOCKSIZE) {
|
||||
increment_counter();
|
||||
randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE);
|
||||
tomove = MIN(count - i, BLOCKSIZE);
|
||||
memcpy((char *)buf + i, genval, tomove);
|
||||
if (++random_state.outputblocks >= random_state.gengateinterval) {
|
||||
generator_gate();
|
||||
random_state.outputblocks = 0;
|
||||
}
|
||||
retval += (int)tomove;
|
||||
cur = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!cur) {
|
||||
increment_counter();
|
||||
randomdev_encrypt(&random_state.key, random_state.counter.byte, genval, BLOCKSIZE);
|
||||
memcpy(buf, genval, (size_t)count);
|
||||
cur = BLOCKSIZE - count;
|
||||
if (++random_state.outputblocks >= random_state.gengateinterval) {
|
||||
generator_gate();
|
||||
random_state.outputblocks = 0;
|
||||
}
|
||||
retval = count;
|
||||
}
|
||||
else {
|
||||
retval = MIN(cur, count);
|
||||
memcpy(buf, &genval[BLOCKSIZE - cur], (size_t)retval);
|
||||
cur -= retval;
|
||||
blockcount = (bytecount + BLOCKSIZE - 1)/BLOCKSIZE;
|
||||
for (i = 0; i < blockcount; i++) {
|
||||
if (yarrow_state.outputblocks++ >= yarrow_state.gengateinterval) {
|
||||
generator_gate();
|
||||
yarrow_state.outputblocks = 0;
|
||||
}
|
||||
uint128_increment(&yarrow_state.counter.whole);
|
||||
randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, buf, BLOCKSIZE);
|
||||
buf += BLOCKSIZE;
|
||||
}
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
}
|
||||
|
||||
/* Internal function to hand external entropy to the PRNG */
|
||||
void
|
||||
random_yarrow_write(uint8_t *buf, u_int count)
|
||||
{
|
||||
uintmax_t timestamp;
|
||||
|
||||
/* We must be locked for all this as plenty of state gets messed with */
|
||||
mtx_lock(&random_reseed_mtx);
|
||||
|
||||
timestamp = get_cyclecount();
|
||||
randomdev_hash_iterate(&yarrow_state.start_cache.hash, ×tamp, sizeof(timestamp));
|
||||
randomdev_hash_iterate(&yarrow_state.start_cache.hash, buf, count);
|
||||
timestamp = get_cyclecount();
|
||||
randomdev_hash_iterate(&yarrow_state.start_cache.hash, ×tamp, sizeof(timestamp));
|
||||
randomdev_hash_finish(&yarrow_state.start_cache.hash, yarrow_state.start_cache.junk);
|
||||
randomdev_hash_init(&yarrow_state.start_cache.hash);
|
||||
|
||||
#ifdef RANDOM_DEBUG_VERBOSE
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("random: %s - ", __func__);
|
||||
for (i = 0; i < KEYSIZE; i++)
|
||||
printf("%02X", yarrow_state.start_cache.junk[i]);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
random_yarrow_process_buffer(yarrow_state.start_cache.junk, KEYSIZE);
|
||||
memset(yarrow_state.start_cache.junk, 0, KEYSIZE);
|
||||
|
||||
mtx_unlock(&random_reseed_mtx);
|
||||
return (retval);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -388,29 +496,26 @@ generator_gate(void)
|
||||
uint8_t temp[KEYSIZE];
|
||||
|
||||
for (i = 0; i < KEYSIZE; i += BLOCKSIZE) {
|
||||
increment_counter();
|
||||
randomdev_encrypt(&random_state.key, random_state.counter.byte, temp + i, BLOCKSIZE);
|
||||
uint128_increment(&yarrow_state.counter.whole);
|
||||
randomdev_encrypt(&yarrow_state.key, yarrow_state.counter.byte, temp + i, BLOCKSIZE);
|
||||
}
|
||||
|
||||
randomdev_encrypt_init(&random_state.key, temp);
|
||||
memset((void *)temp, 0, KEYSIZE);
|
||||
randomdev_encrypt_init(&yarrow_state.key, temp);
|
||||
memset(temp, 0, KEYSIZE);
|
||||
}
|
||||
|
||||
/* Helper routine to perform explicit reseeds */
|
||||
void
|
||||
random_yarrow_reseed(void)
|
||||
{
|
||||
#ifdef RANDOM_DEBUG
|
||||
int i;
|
||||
|
||||
printf("%s(): fast:", __func__);
|
||||
for (i = RANDOM_START; i < ENTROPYSOURCE; ++i)
|
||||
printf(" %d", random_state.pool[FAST].source[i].bits);
|
||||
printf("\n");
|
||||
printf("%s(): slow:", __func__);
|
||||
for (i = RANDOM_START; i < ENTROPYSOURCE; ++i)
|
||||
printf(" %d", random_state.pool[SLOW].source[i].bits);
|
||||
printf("\n");
|
||||
#endif
|
||||
reseed(SLOW);
|
||||
}
|
||||
|
||||
int
|
||||
random_yarrow_seeded(void)
|
||||
{
|
||||
|
||||
return (yarrow_state.seeded);
|
||||
}
|
||||
|
||||
#endif /* RANDOM_YARROW */
|
||||
|
@ -29,9 +29,16 @@
|
||||
#ifndef SYS_DEV_RANDOM_YARROW_H_INCLUDED
|
||||
#define SYS_DEV_RANDOM_YARROW_H_INCLUDED
|
||||
|
||||
void random_yarrow_init_alg(struct sysctl_ctx_list *);
|
||||
#ifdef _KERNEL
|
||||
typedef struct mtx mtx_t;
|
||||
#endif
|
||||
|
||||
void random_yarrow_init_alg(void);
|
||||
void random_yarrow_deinit_alg(void);
|
||||
int random_yarrow_read(void *, int);
|
||||
void random_yarrow_read(uint8_t *, u_int);
|
||||
void random_yarrow_write(uint8_t *, u_int);
|
||||
void random_yarrow_reseed(void);
|
||||
int random_yarrow_seeded(void);
|
||||
void random_yarrow_process_event(struct harvest_event *event);
|
||||
|
||||
#endif
|
||||
|
@ -853,4 +853,4 @@ kick_init(const void *udata __unused)
|
||||
sched_add(td, SRQ_BORING);
|
||||
thread_unlock(td);
|
||||
}
|
||||
SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_FIRST, kick_init, NULL);
|
||||
SYSINIT(kickinit, SI_SUB_KTHREAD_INIT, SI_ORDER_MIDDLE, kick_init, NULL);
|
||||
|
@ -885,13 +885,10 @@ intr_event_schedule_thread(struct intr_event *ie)
|
||||
* If any of the handlers for this ithread claim to be good
|
||||
* sources of entropy, then gather some.
|
||||
*/
|
||||
if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) {
|
||||
CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__,
|
||||
p->p_pid, td->td_name);
|
||||
if (ie->ie_flags & IE_ENTROPY) {
|
||||
entropy.event = (uintptr_t)ie;
|
||||
entropy.td = ctd;
|
||||
random_harvest(&entropy, sizeof(entropy), 2,
|
||||
RANDOM_INTERRUPT);
|
||||
random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
|
||||
}
|
||||
|
||||
KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
|
||||
@ -1039,13 +1036,10 @@ intr_event_schedule_thread(struct intr_event *ie, struct intr_thread *it)
|
||||
* If any of the handlers for this ithread claim to be good
|
||||
* sources of entropy, then gather some.
|
||||
*/
|
||||
if (harvest.interrupt && ie->ie_flags & IE_ENTROPY) {
|
||||
CTR3(KTR_INTR, "%s: pid %d (%s) gathering entropy", __func__,
|
||||
p->p_pid, td->td_name);
|
||||
if (ie->ie_flags & IE_ENTROPY) {
|
||||
entropy.event = (uintptr_t)ie;
|
||||
entropy.td = ctd;
|
||||
random_harvest(&entropy, sizeof(entropy), 2,
|
||||
RANDOM_INTERRUPT);
|
||||
random_harvest(&entropy, sizeof(entropy), 2, RANDOM_INTERRUPT);
|
||||
}
|
||||
|
||||
KASSERT(p != NULL, ("ithread %s has no process", ie->ie_name));
|
||||
@ -1130,14 +1124,9 @@ swi_sched(void *cookie, int flags)
|
||||
CTR3(KTR_INTR, "swi_sched: %s %s need=%d", ie->ie_name, ih->ih_name,
|
||||
ih->ih_need);
|
||||
|
||||
if (harvest.swi) {
|
||||
CTR2(KTR_INTR, "swi_sched: pid %d (%s) gathering entropy",
|
||||
curproc->p_pid, curthread->td_name);
|
||||
entropy.event = (uintptr_t)ih;
|
||||
entropy.td = curthread;
|
||||
random_harvest(&entropy, sizeof(entropy), 1,
|
||||
RANDOM_SWI);
|
||||
}
|
||||
entropy.event = (uintptr_t)ih;
|
||||
entropy.td = curthread;
|
||||
random_harvest(&entropy, sizeof(entropy), 1, RANDOM_SWI);
|
||||
|
||||
/*
|
||||
* Set ih_need for this handler so that if the ithread is already
|
||||
|
@ -2851,7 +2851,7 @@ device_attach(device_t dev)
|
||||
* need to be adjusted on other platforms.
|
||||
*/
|
||||
#ifdef RANDOM_DEBUG
|
||||
printf("%s(): feeding %d bit(s) of entropy from %s%d\n",
|
||||
printf("random: %s(): feeding %d bit(s) of entropy from %s%d\n",
|
||||
__func__, 4, dev->driver->name, dev->unit);
|
||||
#endif
|
||||
random_harvest(&attachtime, sizeof(attachtime), 4, RANDOM_ATTACH);
|
||||
|
@ -270,6 +270,7 @@ SUBDIR= \
|
||||
${_opensolaris} \
|
||||
oce \
|
||||
${_padlock} \
|
||||
${_padlock_rng} \
|
||||
patm \
|
||||
${_pccard} \
|
||||
${_pcfclock} \
|
||||
@ -297,6 +298,7 @@ SUBDIR= \
|
||||
${_random} \
|
||||
rc4 \
|
||||
${_rdma} \
|
||||
${_rdrand_rng} \
|
||||
re \
|
||||
reiserfs \
|
||||
rl \
|
||||
@ -583,6 +585,8 @@ _nvram= nvram
|
||||
_nxge= nxge
|
||||
.if ${MK_CRYPT} != "no" || defined(ALL_MODULES)
|
||||
_padlock= padlock
|
||||
_padlock_rng= padlock_rng
|
||||
_rdrand_rng= rdrand_rng
|
||||
.endif
|
||||
_s3= s3
|
||||
_tpm= tpm
|
||||
@ -602,6 +606,17 @@ _x86bios= x86bios
|
||||
_ixl= ixl
|
||||
_ixlv= ixlv
|
||||
_ntb= ntb
|
||||
_nvd= nvd
|
||||
_nvme= nvme
|
||||
_nvram= nvram
|
||||
_nxge= nxge
|
||||
.if ${MK_CDDL} != "no" || defined(ALL_MODULES)
|
||||
_opensolaris= opensolaris
|
||||
.endif
|
||||
.if ${MK_CRYPT} != "no" || defined(ALL_MODULES)
|
||||
_padlock= padlock
|
||||
.endif
|
||||
_pccard= pccard
|
||||
_qlxge= qlxge
|
||||
_qlxgb= qlxgb
|
||||
_qlxgbe= qlxgbe
|
||||
|
11
sys/modules/padlock_rng/Makefile
Normal file
11
sys/modules/padlock_rng/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../dev/random
|
||||
|
||||
KMOD= padlock_rng
|
||||
SRCS= nehemiah.c
|
||||
SRCS+= bus_if.h device_if.h
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../..
|
||||
|
||||
.include <bsd.kmod.mk>
|
@ -5,13 +5,9 @@
|
||||
.PATH: ${.CURDIR}/../../crypto/sha2
|
||||
|
||||
KMOD= random
|
||||
SRCS= randomdev.c
|
||||
.if ${MACHINE} == "amd64" || ${MACHINE} == "i386"
|
||||
SRCS+= nehemiah.c
|
||||
SRCS+= ivy.c
|
||||
.endif
|
||||
SRCS+= randomdev_soft.c yarrow.c hash.c
|
||||
SRCS+= random_harvestq.c live_entropy_sources.c rwfile.c
|
||||
SRCS= randomdev_soft.c
|
||||
SRCS+= yarrow.c hash.c
|
||||
SRCS+= random_harvestq.c live_entropy_sources.c
|
||||
SRCS+= rijndael-alg-fst.c rijndael-api-fst.c sha2.c sha256c.c
|
||||
SRCS+= bus_if.h device_if.h vnode_if.h opt_cpu.h opt_random.h
|
||||
|
||||
|
11
sys/modules/rdrand_rng/Makefile
Normal file
11
sys/modules/rdrand_rng/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.PATH: ${.CURDIR}/../../dev/random
|
||||
|
||||
KMOD= rdrand_rng
|
||||
SRCS= ivy.c
|
||||
SRCS+= bus_if.h device_if.h
|
||||
|
||||
CFLAGS+= -I${.CURDIR}/../..
|
||||
|
||||
.include <bsd.kmod.mk>
|
@ -576,8 +576,7 @@ ether_input_internal(struct ifnet *ifp, struct mbuf *m)
|
||||
m->m_flags |= M_PROMISC;
|
||||
}
|
||||
|
||||
if (harvest.ethernet)
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER);
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_ETHER);
|
||||
|
||||
ether_demux(ifp, m);
|
||||
CURVNET_RESTORE();
|
||||
|
@ -906,8 +906,7 @@ tunwrite(struct cdev *dev, struct uio *uio, int flag)
|
||||
m_freem(m);
|
||||
return (EAFNOSUPPORT);
|
||||
}
|
||||
if (harvest.point_to_point)
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN);
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_TUN);
|
||||
if_inc_counter(ifp, IFCOUNTER_IBYTES, m->m_pkthdr.len);
|
||||
if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
|
||||
CURVNET_SET(ifp->if_vnet);
|
||||
|
@ -758,8 +758,7 @@ ng_iface_rcvdata(hook_p hook, item_p item)
|
||||
m_freem(m);
|
||||
return (EAFNOSUPPORT);
|
||||
}
|
||||
if (harvest.point_to_point)
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG);
|
||||
random_harvest(&(m->m_data), 12, 2, RANDOM_NET_NG);
|
||||
M_SETFIB(m, ifp->if_fib);
|
||||
netisr_dispatch(isr, m);
|
||||
return (0);
|
||||
|
@ -34,12 +34,17 @@
|
||||
int read_random(void *, int);
|
||||
|
||||
/*
|
||||
* Note: if you add or remove members of esource, remember to also update the
|
||||
* KASSERT regarding what valid members are in random_harvest_internal().
|
||||
* Note: if you add or remove members of random_entropy_source, remember to also update the
|
||||
* KASSERT regarding what valid members are in random_harvest_internal(), and remember the
|
||||
* strings in the static array random_source_descr[] in random_harvestq.c.
|
||||
*
|
||||
* NOTE: complain loudly to markm@ or on the lists if this enum gets more than 32
|
||||
* distinct values (0-31)! ENTROPYSOURCE may be == 32, but not > 32.
|
||||
*/
|
||||
enum esource {
|
||||
enum random_entropy_source {
|
||||
RANDOM_START = 0,
|
||||
RANDOM_CACHED = 0,
|
||||
/* Environmental sources */
|
||||
RANDOM_ATTACH,
|
||||
RANDOM_KEYBOARD,
|
||||
RANDOM_MOUSE,
|
||||
@ -48,6 +53,9 @@ enum esource {
|
||||
RANDOM_NET_NG,
|
||||
RANDOM_INTERRUPT,
|
||||
RANDOM_SWI,
|
||||
RANDOM_UMA_ALLOC,
|
||||
RANDOM_ENVIRONMENTAL_END, /* This one is wasted */
|
||||
/* High-quality HW RNGs from here on. */
|
||||
RANDOM_PURE_OCTEON,
|
||||
RANDOM_PURE_SAFE,
|
||||
RANDOM_PURE_GLXSB,
|
||||
@ -59,20 +67,7 @@ enum esource {
|
||||
RANDOM_PURE_VIRTIO,
|
||||
ENTROPYSOURCE
|
||||
};
|
||||
void random_harvest(const void *, u_int, u_int, enum esource);
|
||||
|
||||
/* Allow the sysadmin to select the broad category of
|
||||
* entropy types to harvest
|
||||
*/
|
||||
struct harvest_select {
|
||||
int ethernet;
|
||||
int point_to_point;
|
||||
int interrupt;
|
||||
int swi;
|
||||
int namei;
|
||||
};
|
||||
|
||||
extern struct harvest_select harvest;
|
||||
void random_harvest(const void *, u_int, u_int, enum random_entropy_source);
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
|
@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/proc.h>
|
||||
#include <sys/random.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sched.h>
|
||||
@ -2097,6 +2098,12 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
|
||||
int lockfail;
|
||||
int cpu;
|
||||
|
||||
#if 0
|
||||
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
|
||||
/* The entropy here is desirable, but the harvesting is expensive */
|
||||
random_harvest(&(zone->uz_name), sizeof(void *), 1, RANDOM_UMA_ALLOC);
|
||||
#endif
|
||||
|
||||
/* This is the fast path allocation */
|
||||
#ifdef UMA_DEBUG_ALLOC_1
|
||||
printf("Allocating one item from %s(%p)\n", zone->uz_name, zone);
|
||||
@ -2127,6 +2134,11 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
|
||||
zone->uz_fini(item, zone->uz_size);
|
||||
return (NULL);
|
||||
}
|
||||
#if 0
|
||||
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
|
||||
/* The entropy here is desirable, but the harvesting is expensive */
|
||||
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
|
||||
#endif
|
||||
return (item);
|
||||
}
|
||||
/* This is unfortunate but should not be fatal. */
|
||||
@ -2169,6 +2181,11 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
|
||||
#endif
|
||||
if (flags & M_ZERO)
|
||||
uma_zero_item(item, zone);
|
||||
#if 0
|
||||
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
|
||||
/* The entropy here is desirable, but the harvesting is expensive */
|
||||
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
|
||||
#endif
|
||||
return (item);
|
||||
}
|
||||
|
||||
@ -2289,6 +2306,11 @@ uma_zalloc_arg(uma_zone_t zone, void *udata, int flags)
|
||||
zalloc_item:
|
||||
item = zone_alloc_item(zone, udata, flags);
|
||||
|
||||
#if 0
|
||||
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
|
||||
/* The entropy here is desirable, but the harvesting is expensive */
|
||||
random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC);
|
||||
#endif
|
||||
return (item);
|
||||
}
|
||||
|
||||
@ -2636,6 +2658,19 @@ uma_zfree_arg(uma_zone_t zone, void *item, void *udata)
|
||||
int lockfail;
|
||||
int cpu;
|
||||
|
||||
#if 0
|
||||
/* XXX: FIX!! Do not enable this in CURRENT!! MarkM */
|
||||
/* The entropy here is desirable, but the harvesting is expensive */
|
||||
struct entropy {
|
||||
const void *uz_name;
|
||||
const void *item;
|
||||
} entropy;
|
||||
|
||||
entropy.uz_name = zone->uz_name;
|
||||
entropy.item = item;
|
||||
random_harvest(&entropy, sizeof(struct entropy), 2, RANDOM_UMA_ALLOC);
|
||||
#endif
|
||||
|
||||
#ifdef UMA_DEBUG_ALLOC_1
|
||||
printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user