Very large makeover of the /dev/random driver.

o Separate the kernel stuff from the Yarrow algorithm. Yarrow is now
  well contained in one source file and one header.

o Replace the Blowfish-based crypto routines with Rijndael-based ones.
  (Rijndael is the new AES algorithm). The huge improvement in
  Rijndael's key-agility over Blowfish means that this is an
  extremely dramatic improvement in speed, and makes a heck of
  a difference in its (lack of) CPU load.

o Clean up the sysctl's. At BDE's prompting, I have gone back to
  static sysctls.

o Bug fixes. The streamlining of the crypto stuff enabled me to
  find and fix some bugs. DES also found a bug in the reseed routine
  which is fixed.

o Change the way reseeds clear "used" entropy. Previously, only the
  source(s) that caused a reseed were cleared. Now all sources in the
  relevant pool(s) are cleared.

o Code tidy-up. Mostly to make it (nearly) 80-column compliant.
This commit is contained in:
markm 2001-03-10 12:51:55 +00:00
parent a9b8e8be5d
commit ad4dd3b5a2
7 changed files with 513 additions and 467 deletions

View File

@ -38,10 +38,7 @@
#include <machine/cpu.h>
#include <crypto/blowfish/blowfish.h>
#include <dev/random/hash.h>
#include <dev/random/yarrow.h>
#include <dev/random/randomdev.h>
static u_int read_random_phony(void *, u_int);

View File

@ -33,19 +33,17 @@
#include <sys/random.h>
#include <sys/types.h>
#include <crypto/blowfish/blowfish.h>
#include <crypto/rijndael/rijndael.h>
#include <dev/random/hash.h>
/* initialise the hash by copying in some supplied data */
/* initialise the hash by zeroing it */
void
yarrow_hash_init(struct yarrowhash *context, void *data, size_t size)
yarrow_hash_init(struct yarrowhash *context)
{
size_t count;
count = size > KEYSIZE ? KEYSIZE : size;
memset(context->hash, 0xff, KEYSIZE);
memcpy(context->hash, data, count);
rijndael_cipherInit(&context->cipher, MODE_CBC, NULL);
bzero(context->hash, KEYSIZE);
context->partial = 0;
}
/* Do a Davies-Meyer hash using a block cipher.
@ -55,65 +53,65 @@ yarrow_hash_init(struct yarrowhash *context, void *data, size_t size)
void
yarrow_hash_iterate(struct yarrowhash *context, void *data, size_t size)
{
u_char keybuffer[KEYSIZE], temp[KEYSIZE];
size_t count;
int iteration, last, i;
u_char temp[KEYSIZE];
u_int i, j;
iteration = 0;
last = 0;
for (;;) {
if (size <= KEYSIZE)
last = 1;
count = size > KEYSIZE ? KEYSIZE : size;
memcpy(keybuffer, &((u_char *)data)[iteration], count);
memset(&keybuffer[KEYSIZE - count], 0xff, count);
BF_set_key(&context->hashkey, count,
&((u_char *)data)[iteration]);
BF_cbc_encrypt(context->hash, temp, KEYSIZE, &context->hashkey,
context->ivec, BF_ENCRYPT);
for (i = 0; i < KEYSIZE; i++)
context->hash[i] ^= temp[i];
if (last)
break;
iteration += KEYSIZE;
size -= KEYSIZE;
for (i = 0; i < size; i++) {
context->accum[context->partial++] = ((u_char *)(data))[i];
if (context->partial == (KEYSIZE - 1)) {
rijndael_makeKey(&context->hashkey, DIR_ENCRYPT,
KEYSIZE*8, context->accum);
rijndael_blockEncrypt(&context->cipher,
&context->hashkey, context->hash,
KEYSIZE*8, temp);
for (j = 0; j < KEYSIZE; j++)
context->hash[j] ^= temp[j];
bzero(context->accum, KEYSIZE);
context->partial = 0;
}
}
}
/* Conclude by returning a pointer to the data */
/* Conclude by returning the hash in the supplied /buf/ which must be
* KEYSIZE bytes long. Trailing data (less than KEYSIZE bytes) are
* not forgotten.
*/
void
yarrow_hash_finish(struct yarrowhash *context, void *buf)
{
memcpy(buf, context->hash, sizeof(context->hash));
}
u_char temp[KEYSIZE];
int i;
/* Initialise the encryption routine by setting up the key schedule */
void
yarrow_encrypt_init(struct yarrowkey *context, void *data, size_t size)
{
size_t count;
count = size > KEYSIZE ? KEYSIZE : size;
BF_set_key(&context->key, count, data);
}
/* Encrypt the supplied data using the key schedule preset in the context */
void
yarrow_encrypt(struct yarrowkey *context, void *d_in, void *d_out, size_t size)
{
size_t count;
int iteration, last;
last = 0;
for (iteration = 0;; iteration += KEYSIZE) {
if (size <= KEYSIZE)
last = 1;
count = size > KEYSIZE ? KEYSIZE : size;
BF_cbc_encrypt(&((u_char *)d_in)[iteration],
&((u_char *)d_out)[iteration], count, &context->key,
context->ivec, BF_ENCRYPT);
if (last)
break;
size -= KEYSIZE;
if (context->partial) {
rijndael_makeKey(&context->hashkey, DIR_ENCRYPT,
KEYSIZE*8, context->accum);
rijndael_blockEncrypt(&context->cipher,
&context->hashkey, context->hash,
KEYSIZE*8, temp);
for (i = 0; i < KEYSIZE; i++)
context->hash[i] ^= temp[i];
}
memcpy(buf, context->hash, KEYSIZE);
bzero(context->hash, KEYSIZE);
}
/* Initialise the encryption routine by setting up the key schedule
* from the supplied /key/ which must be KEYSIZE bytes of binary
* data.
*/
void
yarrow_encrypt_init(struct yarrowkey *context, void *data)
{
rijndael_cipherInit(&context->cipher, MODE_CBC, NULL);
rijndael_makeKey(&context->key, DIR_ENCRYPT, KEYSIZE*8, data);
}
/* Encrypt the supplied data using the key schedule preset in the context.
* KEYSIZE bytes are encrypted from /d_in/ to /d_out/.
*/
void
yarrow_encrypt(struct yarrowkey *context, void *d_in, void *d_out)
{
rijndael_blockEncrypt(&context->cipher, &context->key, d_in,
KEYSIZE*8, d_out);
}

View File

@ -26,21 +26,23 @@
* $FreeBSD$
*/
#define KEYSIZE 32 /* 32 bytes == 256 bits */
#define KEYSIZE 32 /* in bytes - 32 bytes == 256 bits */
struct yarrowhash { /* Big! Make static! */
BF_KEY hashkey; /* Data cycles through here */
u_char ivec[8]; /* Blowfish Internal */
keyInstance hashkey; /* Data cycles through here */
cipherInstance cipher; /* Rijndael internal */
u_char hash[KEYSIZE]; /* Repeatedly encrypted */
u_char accum[KEYSIZE]; /* Accumulate partial chunks */
u_int partial; /* Keep track of < KEYSIZE chunks */
};
struct yarrowkey { /* Big! Make static! */
BF_KEY key; /* Key schedule */
u_char ivec[8]; /* Blowfish Internal */
keyInstance key; /* Key schedule */
cipherInstance cipher; /* Rijndael internal */
};
void yarrow_hash_init(struct yarrowhash *, void *, size_t);
void yarrow_hash_init(struct yarrowhash *);
void yarrow_hash_iterate(struct yarrowhash *, void *, size_t);
void yarrow_hash_finish(struct yarrowhash *, void *);
void yarrow_encrypt_init(struct yarrowkey *, void *, size_t);
void yarrow_encrypt(struct yarrowkey *context, void *, void *, size_t);
void yarrow_encrypt_init(struct yarrowkey *, void *);
void yarrow_encrypt(struct yarrowkey *context, void *, void *);

View File

@ -33,6 +33,7 @@
#include <sys/fcntl.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/bus.h>
@ -41,23 +42,23 @@
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/vnode.h>
#include <sys/unistd.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <machine/resource.h>
#include <crypto/blowfish/blowfish.h>
#include <dev/random/hash.h>
#include <dev/random/yarrow.h>
#include <dev/random/randomdev.h>
static d_open_t random_open;
static d_close_t random_close;
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_open_t random_open;
static d_close_t random_close;
static d_read_t random_read;
static d_write_t random_write;
static d_ioctl_t random_ioctl;
static d_poll_t random_poll;
#define CDEV_MAJOR 2
#define RANDOM_MINOR 3
#define URANDOM_MINOR 4
static struct cdevsw random_cdevsw = {
/* open */ random_open,
@ -76,13 +77,63 @@ static struct cdevsw random_cdevsw = {
/* bmaj */ -1
};
/* For use with make_dev(9)/destroy_dev(9). */
static dev_t random_dev;
static dev_t urandom_dev; /* XXX Temporary */
static void random_kthread(void *);
static void random_harvest_internal(u_int64_t, void *, u_int, u_int, u_int, enum esource);
static void random_write_internal(void *, u_int);
/* To stash the sysctl's until they are removed */
static struct sysctl_oid *random_sysctl[12]; /* magic # is sysctl count */
static int sysctlcount = 0;
/* Ring buffer holding harvested entropy */
static struct harvestring {
volatile u_int head;
volatile u_int tail;
struct harvest data[HARVEST_RING_SIZE];
} harvestring;
static struct random_systat {
u_int seeded; /* 0 causes blocking 1 allows normal output */
u_int burst; /* number of events to do before sleeping */
struct selinfo rsel; /* For poll(2) */
} random_systat;
/* <0 to end the kthread, 0 to let it run */
static int random_kthread_control = 0;
static struct proc *random_kthread_proc;
/* For use with make_dev(9)/destroy_dev(9). */
static dev_t random_dev;
static dev_t urandom_dev;
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);
}
RANDOM_CHECK_UINT(burst, 0, 20);
SYSCTL_NODE(_kern, OID_AUTO, random, CTLFLAG_RW,
0, "Random Number Generator");
SYSCTL_NODE(_kern_random, OID_AUTO, sys, CTLFLAG_RW,
0, "Entropy Device Parameters");
SYSCTL_PROC(_kern_random_sys, OID_AUTO, seeded,
CTLTYPE_INT|CTLFLAG_RW, &random_systat.seeded, 1,
random_check_boolean, "I", "Seeded State");
SYSCTL_PROC(_kern_random_sys, OID_AUTO, burst,
CTLTYPE_INT|CTLFLAG_RW, &random_systat.burst, 20,
random_check_uint_burst, "I", "Harvest Burst Size");
SYSCTL_NODE(_kern_random_sys, OID_AUTO, harvest, CTLFLAG_RW,
0, "Entropy Sources");
SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, ethernet,
CTLTYPE_INT|CTLFLAG_RW, &harvest.ethernet, 0,
random_check_boolean, "I", "Harvest NIC entropy");
SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, point_to_point,
CTLTYPE_INT|CTLFLAG_RW, &harvest.point_to_point, 0,
random_check_boolean, "I", "Harvest serial net entropy");
SYSCTL_PROC(_kern_random_sys_harvest, OID_AUTO, interrupt,
CTLTYPE_INT|CTLFLAG_RW, &harvest.interrupt, 0,
random_check_boolean, "I", "Harvest IRQ entropy");
static int
random_open(dev_t dev, int flags, int fmt, struct proc *p)
@ -104,15 +155,16 @@ random_close(dev_t dev, int flags, int fmt, struct proc *p)
static int
random_read(dev_t dev, struct uio *uio, int flag)
{
u_int c, ret;
int error = 0;
void *random_buf;
u_int c, ret;
int error = 0;
void *random_buf;
while (!random_state.seeded) {
while (!random_systat.seeded) {
if (flag & IO_NDELAY)
error = EWOULDBLOCK;
else
error = tsleep(&random_state, PUSER|PCATCH, "rndblk", 0);
error = tsleep(&random_systat, PUSER|PCATCH,
"block", 0);
if (error != 0)
return error;
}
@ -129,17 +181,18 @@ random_read(dev_t dev, struct uio *uio, int flag)
static int
random_write(dev_t dev, struct uio *uio, int flag)
{
u_int c;
int error = 0;
void *random_buf;
u_int c;
int error;
void *random_buf;
error = 0;
random_buf = (void *)malloc(PAGE_SIZE, M_TEMP, M_WAITOK);
while (uio->uio_resid > 0) {
c = min(uio->uio_resid, PAGE_SIZE);
error = uiomove(random_buf, c, uio);
if (error)
break;
write_random(random_buf, c);
random_write_internal(random_buf, c);
}
free(random_buf, M_TEMP);
return error;
@ -154,14 +207,14 @@ random_ioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
static int
random_poll(dev_t dev, int events, struct proc *p)
{
int revents;
int revents;
revents = 0;
if (events & (POLLIN | POLLRDNORM)) {
if (random_state.seeded)
if (random_systat.seeded)
revents = events & (POLLIN | POLLRDNORM);
else
selrecord(p, &random_state.rsel);
selrecord(p, &random_systat.rsel);
}
return revents;
}
@ -169,84 +222,58 @@ random_poll(dev_t dev, int events, struct proc *p)
static int
random_modevent(module_t mod, int type, void *data)
{
struct sysctl_oid *node_base, *node1, *node2;
int error, i;
int error;
switch(type) {
case MOD_LOAD:
error = random_init();
if (error != 0)
return error;
random_init();
random_sysctl[sysctlcount++] = node_base =
SYSCTL_ADD_NODE(NULL, SYSCTL_STATIC_CHILDREN(_kern),
OID_AUTO, "random", CTLFLAG_RW, 0,
"Random Number Generator");
random_sysctl[sysctlcount++] = node1 =
SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(node_base),
OID_AUTO, "sys", CTLFLAG_RW, 0,
"Entropy Device Parameters");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node1),
OID_AUTO, "seeded", CTLFLAG_RW,
&random_state.seeded, 0, "Seeded State");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node1),
OID_AUTO, "harvest_ethernet", CTLFLAG_RW,
&harvest.ethernet, 0, "Harvest NIC entropy");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node1),
OID_AUTO, "harvest_point_to_point", CTLFLAG_RW,
&harvest.point_to_point, 0, "Harvest serial net entropy");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node1),
OID_AUTO, "harvest_interrupt", CTLFLAG_RW,
&harvest.interrupt, 0, "Harvest IRQ entropy");
random_sysctl[sysctlcount++] = node2 =
SYSCTL_ADD_NODE(NULL, SYSCTL_CHILDREN(node_base),
OID_AUTO, "yarrow", CTLFLAG_RW, 0,
"Yarrow Parameters");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
OID_AUTO, "gengateinterval", CTLFLAG_RW,
&random_state.gengateinterval, 0,
"Generator Gate Interval");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
OID_AUTO, "bins", CTLFLAG_RW,
&random_state.bins, 0,
"Execution time tuner");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
OID_AUTO, "fastthresh", CTLFLAG_RW,
&random_state.pool[0].thresh, 0,
"Fast pool reseed threshhold");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
OID_AUTO, "slowthresh", CTLFLAG_RW,
&random_state.pool[1].thresh, 0,
"Slow pool reseed threshhold");
random_sysctl[sysctlcount++] =
SYSCTL_ADD_INT(NULL, SYSCTL_CHILDREN(node2),
OID_AUTO, "slowoverthresh", CTLFLAG_RW,
&random_state.slowoverthresh, 0,
"Slow pool over-threshhold reseed");
/* This can be turned off by the very paranoid
* a reseed will turn it back on.
*/
random_systat.seeded = 1;
/* Number of envents to process off the harvest
* queue before giving it a break and sleeping
*/
random_systat.burst = 20;
/* Initialise the harvest ringbuffer */
harvestring.head = 0;
harvestring.tail = 0;
if (bootverbose)
printf("random: <entropy source>\n");
random_dev = make_dev(&random_cdevsw, RANDOM_MINOR, UID_ROOT,
GID_WHEEL, 0666, "random");
urandom_dev = make_dev(&random_cdevsw, URANDOM_MINOR, UID_ROOT,
GID_WHEEL, 0666, "urandom"); /* XXX Temporary */
urandom_dev = make_dev_alias(random_dev, "urandom");
/* Start the hash/reseed thread */
error = kthread_create(random_kthread, NULL,
&random_kthread_proc, RFHIGHPID, "random");
if (error != 0)
return error;
/* Register the randomness harvesting routine */
random_init_harvester(random_harvest_internal,
read_random_real);
return 0;
case MOD_UNLOAD:
/* Deregister the randomness harvesting routine */
random_deinit_harvester();
/* Command the hash/reseed thread to end and
* wait for it to finish
*/
random_kthread_control = -1;
tsleep((void *)&random_kthread_control, PUSER, "term", 0);
random_deinit();
destroy_dev(random_dev);
destroy_dev(urandom_dev); /* XXX Temporary */
for (i = sysctlcount - 1; i >= 0; i--)
if (sysctl_remove_oid(random_sysctl[i], 1, 0) == EINVAL)
panic("random: removing sysctl");
destroy_dev(urandom_dev);
return 0;
case MOD_SHUTDOWN:
@ -258,3 +285,129 @@ random_modevent(module_t mod, int type, void *data)
}
DEV_MODULE(random, random_modevent, NULL);
static void
random_kthread(void *arg /* NOTUSED */)
{
struct harvest *event;
int newtail, burst;
/* Drain the harvest queue (in 'burst' size chunks,
* if 'burst' > 0. If 'burst' == 0, then completely
* drain the queue.
*/
for (burst = 0; ; burst++) {
if ((harvestring.tail == harvestring.head) ||
(random_systat.burst && burst == random_systat.burst)) {
tsleep(&harvestring, PUSER, "sleep", hz/10);
burst = 0;
}
else {
/* Suck a harvested entropy event out of the queue and
* hand it to the event processor
*/
newtail = (harvestring.tail + 1) & HARVEST_RING_MASK;
event = &harvestring.data[harvestring.tail];
/* Bump the ring counter. This action is assumed
* to be atomic.
*/
harvestring.tail = newtail;
random_process_event(event);
}
/* Is the thread scheduled for a shutdown? */
if (random_kthread_control != 0) {
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random kthread setting terminate\n");
mtx_unlock(&Giant);
#endif
random_set_wakeup_exit(&random_kthread_control);
/* NOTREACHED */
break;
}
}
}
/* Entropy harvesting routine. This is supposed to be fast; do
* not do anything slow in here!
*/
static void
random_harvest_internal(u_int64_t somecounter, void *entropy, u_int count,
u_int bits, u_int frac, enum esource origin)
{
struct harvest *harvest;
int newhead;
newhead = (harvestring.head + 1) & HARVEST_RING_MASK;
if (newhead != harvestring.tail) {
/* Add the harvested data to the ring buffer */
harvest = &harvestring.data[harvestring.head];
/* Stuff the harvested data into the ring */
harvest->somecounter = somecounter;
count = count > HARVESTSIZE ? HARVESTSIZE : count;
memcpy(harvest->entropy, entropy, count);
harvest->size = count;
harvest->bits = bits;
harvest->frac = frac;
harvest->source = origin < ENTROPYSOURCE ? origin : 0;
/* Bump the ring counter. This action is assumed
* to be atomic.
*/
harvestring.head = newhead;
}
}
static void
random_write_internal(void *buf, u_int count)
{
u_int i;
/* Break the input up into HARVESTSIZE chunks.
* The writer has too much control here, so "estimate" the
* the entropy as zero.
*/
for (i = 0; i < count; i += HARVESTSIZE) {
random_harvest_internal(get_cyclecount(), (char *)buf + i,
HARVESTSIZE, 0, 0, RANDOM_WRITE);
}
/* Maybe the loop iterated at least once */
if (i > count)
i -= HARVESTSIZE;
/* Get the last bytes even if the input length is not
* a multiple of HARVESTSIZE.
*/
count %= HARVESTSIZE;
if (count) {
random_harvest_internal(get_cyclecount(), (char *)buf + i,
count, 0, 0, RANDOM_WRITE);
}
}
void
random_unblock(void)
{
if (!random_systat.seeded) {
random_systat.seeded = 1;
selwakeup(&random_systat.rsel);
wakeup(&random_systat);
}
}

View File

@ -0,0 +1,83 @@
/*-
* Copyright (c) 2001 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$
*/
/* This header contains only those definitions that are global
* and non algorithm-specific for the entropy processor
*/
/* #define ENTROPYSOURCE nn entropy sources (actually classes)
* This is properly defined in
* an enum in sys/random.h
*/
/* Cryptographic block size in bits */
#define BLOCKSIZE 256
/* 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 */
SYSCTL_DECL(_kern_random);
/* These are used to queue harvested packets of entropy. The entropy
* buffer size is pretty arbitrary.
*/
struct harvest {
u_int64_t somecounter; /* fast counter for clock jitter */
u_char entropy[HARVESTSIZE]; /* the harvested entropy */
u_int size, bits, frac; /* stats about the entropy */
enum esource source; /* stats about the entropy */
};
void random_init(void);
void random_deinit(void);
void random_init_harvester(void (*)(u_int64_t, void *, u_int, u_int, u_int, enum esource), u_int (*)(void *, u_int));
void random_deinit_harvester(void);
void random_set_wakeup_exit(void *);
void random_process_event(struct harvest *event);
void random_reseed(void);
void random_unblock(void);
u_int read_random_real(void *, u_int);
/* If this was c++, this 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); \
}

View File

@ -29,229 +29,128 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/libkern.h>
#include <sys/mutex.h>
#include <sys/selinfo.h>
#include <sys/random.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/unistd.h>
#include <machine/atomic.h>
#include <machine/cpu.h>
#include <crypto/blowfish/blowfish.h>
#include <crypto/rijndael/rijndael.h>
#include <dev/random/hash.h>
#include <dev/random/randomdev.h>
#include <dev/random/yarrow.h>
/* #define DEBUG */
static void generator_gate(void);
static void reseed(int);
static void random_harvest_internal(u_int64_t, void *, u_int, u_int, u_int, enum esource);
RANDOM_CHECK_UINT(gengateinterval, 4, 64);
RANDOM_CHECK_UINT(bins, 2, 16);
RANDOM_CHECK_UINT(fastthresh, BLOCKSIZE/4, BLOCKSIZE);
RANDOM_CHECK_UINT(slowthresh, BLOCKSIZE/4, BLOCKSIZE);
RANDOM_CHECK_UINT(slowoverthresh, 1, 5);
static void random_kthread(void *);
SYSCTL_NODE(_kern_random, OID_AUTO, yarrow, CTLFLAG_RW, 0, "Yarrow Parameters");
SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, gengateinterval,
CTLTYPE_INT|CTLFLAG_RW, &random_state.gengateinterval, 10,
random_check_uint_gengateinterval, "I", "Generator Gate Interval");
SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, bins,
CTLTYPE_INT|CTLFLAG_RW, &random_state.bins, 10,
random_check_uint_bins, "I", "Execution time tuner");
SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, fastthresh,
CTLTYPE_INT|CTLFLAG_RW, &random_state.pool[0].thresh, (3*BLOCKSIZE)/4,
random_check_uint_fastthresh, "I", "Fast reseed threshold");
SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, slowthresh,
CTLTYPE_INT|CTLFLAG_RW, &random_state.pool[1].thresh, BLOCKSIZE,
random_check_uint_slowthresh, "I", "Slow reseed threshold");
SYSCTL_PROC(_kern_random_yarrow, OID_AUTO, slowoverthresh,
CTLTYPE_INT|CTLFLAG_RW, &random_state.slowoverthresh, 2,
random_check_uint_slowoverthresh, "I", "Slow over-threshold reseed");
static void generator_gate(void);
static void reseed(u_int);
/* Structure holding the entropy state */
struct random_state random_state;
/* These are used to queue harvested packets of entropy. The entropy
* buffer size is pretty arbitrary.
*/
struct harvest {
u_int64_t somecounter; /* fast counter for clock jitter */
u_char entropy[HARVESTSIZE]; /* the harvested entropy */
u_int size, bits, frac; /* stats about the entropy */
enum esource source; /* stats about the entropy */
};
/* Ring buffer holding harvested entropy */
static struct harvestring {
volatile int head;
volatile int tail;
struct harvest data[HARVEST_RING_SIZE];
} harvestring;
/* The reseed thread mutex */
static struct mtx random_reseed_mtx;
/* <0 to end the kthread, 0 to let it run */
static int random_kthread_control = 0;
static struct proc *random_kthread_proc;
static void
random_kthread(void *arg /* NOTUSED */)
/* Process a single stochastic event off the harvest queue */
void
random_process_event(struct harvest *event)
{
int pl, src, overthreshhold[2], newtail;
struct harvest *event;
u_int pl, src, overthreshhold[2];
struct source *source;
#ifdef DEBUG
mtx_lock(&Giant);
printf("OWNERSHIP Giant == %d sched_lock == %d\n",
mtx_owned(&Giant), mtx_owned(&sched_lock));
mtx_unlock(&Giant);
#endif
for (pl = 0; pl < 2; pl++)
yarrow_hash_init(&random_state.pool[pl].hash, NULL, 0);
for (;;) {
if (harvestring.tail == harvestring.head)
tsleep(&harvestring, PUSER, "rndslp", hz/10);
else {
/* Suck the harvested entropy out of the queue and hash
* it into the appropriate pool.
*/
newtail = (harvestring.tail + 1) & HARVEST_RING_MASK;
event = &harvestring.data[harvestring.tail];
/* Bump the ring counter. This action is assumed
* to be atomic.
*/
harvestring.tail = newtail;
pl = random_state.which = !random_state.which;
source = &random_state.pool[pl].source[event->source];
yarrow_hash_iterate(&random_state.pool[pl].hash,
event->entropy, sizeof(event->entropy));
yarrow_hash_iterate(&random_state.pool[pl].hash,
&event->somecounter, sizeof(event->somecounter));
source->frac += event->frac;
source->bits += event->bits + source->frac/1024;
source->frac %= 1024;
/* Count the over-threshold sources in each pool */
for (pl = 0; pl < 2; pl++) {
overthreshhold[pl] = 0;
for (src = 0; 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);
/* Unpack the event into the appropriate source accumulator */
pl = random_state.which;
source = &random_state.pool[pl].source[event->source];
yarrow_hash_iterate(&random_state.pool[pl].hash, event->entropy,
sizeof(event->entropy));
yarrow_hash_iterate(&random_state.pool[pl].hash, &event->somecounter,
sizeof(event->somecounter));
source->frac += event->frac;
source->bits += event->bits + source->frac/1024;
source->frac %= 1024;
/* Count the over-threshold sources in each pool */
for (pl = 0; pl < 2; pl++) {
overthreshhold[pl] = 0;
for (src = 0; src < ENTROPYSOURCE; src++) {
if (random_state.pool[pl].source[src].bits
> random_state.pool[pl].thresh)
overthreshhold[pl]++;
}
/* Is the thread scheduled for a shutdown? */
if (random_kthread_control != 0) {
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random kthread setting terminate\n");
mtx_unlock(&Giant);
#endif
random_set_wakeup_exit(&random_kthread_control);
/* NOTREACHED */
break;
}
}
/* 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;
}
int
void
random_init(void)
{
int error;
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random initialise\n");
mtx_unlock(&Giant);
#endif
/* This can be turned off by the very paranoid
* a reseed will turn it back on.
*/
random_state.seeded = 1;
int i;
/* Yarrow parameters. Do not adjust these unless you have
* have a very good clue about what they do!
*/
random_state.gengateinterval = 10;
random_state.bins = 10;
random_state.pool[0].thresh = 100;
random_state.pool[1].thresh = 160;
random_state.pool[0].thresh = (3*BLOCKSIZE)/4;
random_state.pool[1].thresh = BLOCKSIZE;
random_state.slowoverthresh = 2;
random_state.which = FAST;
/* Initialise the fast and slow entropy pools */
for (i = 0; i < 2; i++)
yarrow_hash_init(&random_state.pool[i].hash);
/* Clear the counter */
for (i = 0; i < 4; i++)
random_state.counter[i] = 0;
/* Set up a lock for the reseed process */
mtx_init(&random_reseed_mtx, "random reseed", MTX_DEF);
harvestring.head = 0;
harvestring.tail = 0;
/* Start the hash/reseed thread */
error = kthread_create(random_kthread, NULL,
&random_kthread_proc, RFHIGHPID, "random");
if (error != 0)
return error;
/* Register the randomness harvesting routine */
random_init_harvester(random_harvest_internal, read_random_real);
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random initialise finish\n");
mtx_unlock(&Giant);
#endif
return 0;
}
void
random_deinit(void)
{
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random deinitialise\n");
mtx_unlock(&Giant);
#endif
/* Deregister the randomness harvesting routine */
random_deinit_harvester();
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random deinitialise waiting for thread to terminate\n");
mtx_unlock(&Giant);
#endif
/* Command the hash/reseed thread to end and wait for it to finish */
random_kthread_control = -1;
tsleep((void *)&random_kthread_control, PUSER, "rndend", 0);
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random deinitialise removing mutexes\n");
mtx_unlock(&Giant);
#endif
mtx_destroy(&random_reseed_mtx);
#ifdef DEBUG
mtx_lock(&Giant);
printf("Random deinitialise finish\n");
mtx_unlock(&Giant);
#endif
}
static void
reseed(int fastslow)
reseed(u_int fastslow)
{
/* Interrupt-context stack is a limited resource; make large
* structures static.
@ -260,7 +159,7 @@ reseed(int fastslow)
static struct yarrowhash context;
u_char hash[KEYSIZE]; /* h' */
u_char temp[KEYSIZE];
int i, j;
u_int i, j;
#ifdef DEBUG
mtx_lock(&Giant);
@ -273,14 +172,15 @@ reseed(int fastslow)
/* 1. Hash the accumulated entropy into v[0] */
yarrow_hash_init(&context, NULL, 0);
yarrow_hash_init(&context);
/* Feed the slow pool hash in if slow */
if (fastslow == SLOW)
yarrow_hash_iterate(&context,
&random_state.pool[SLOW].hash, sizeof(struct yarrowhash));
&random_state.pool[SLOW].hash,
sizeof(struct yarrowhash));
yarrow_hash_iterate(&context,
&random_state.pool[FAST].hash, sizeof(struct yarrowhash));
yarrow_hash_finish(&context, v[0]);
/* 2. Compute hash values for all v. _Supposed_ to be computationally
* intensive.
@ -289,13 +189,13 @@ reseed(int fastslow)
if (random_state.bins > TIMEBIN)
random_state.bins = TIMEBIN;
for (i = 1; i < random_state.bins; i++) {
yarrow_hash_init(&context, NULL, 0);
/* v[i] #= h(v[i-1]) */
yarrow_hash_init(&context);
/* v[i] #= h(v[i - 1]) */
yarrow_hash_iterate(&context, v[i - 1], KEYSIZE);
/* v[i] #= h(v[0]) */
yarrow_hash_iterate(&context, v[0], KEYSIZE);
/* v[i] #= h(i) */
yarrow_hash_iterate(&context, &i, sizeof(int));
yarrow_hash_iterate(&context, &i, sizeof(u_int));
/* Return the hashval */
yarrow_hash_finish(&context, v[i]);
}
@ -304,29 +204,26 @@ reseed(int fastslow)
* it is not being ignored!
*/
yarrow_hash_init(&context, NULL, 0);
yarrow_hash_init(&context);
yarrow_hash_iterate(&context, &random_state.key, KEYSIZE);
for (i = 1; i < random_state.bins; i++)
yarrow_hash_iterate(&context, &v[i], KEYSIZE);
yarrow_hash_finish(&context, temp);
yarrow_encrypt_init(&random_state.key, temp, KEYSIZE);
yarrow_encrypt_init(&random_state.key, temp);
/* 4. Recompute the counter */
random_state.counter = 0;
yarrow_encrypt(&random_state.key, &random_state.counter, temp,
sizeof(random_state.counter));
memcpy(&random_state.counter, temp, random_state.counter);
for (i = 0; i < 4; i++)
random_state.counter[i] = 0;
yarrow_encrypt(&random_state.key, random_state.counter, temp);
memcpy(random_state.counter, temp, sizeof(random_state.counter));
/* 5. Reset entropy estimate accumulators to zero */
for (i = 0; i <= fastslow; i++) {
for (j = 0; j < ENTROPYSOURCE; j++) {
if (random_state.pool[i].source[j].bits >
random_state.pool[i].thresh) {
random_state.pool[i].source[j].bits = 0;
random_state.pool[i].source[j].frac = 0;
}
random_state.pool[i].source[j].bits = 0;
random_state.pool[i].source[j].frac = 0;
}
}
@ -348,14 +245,13 @@ reseed(int fastslow)
mtx_unlock(&Giant);
#endif
if (!random_state.seeded) {
random_state.seeded = 1;
selwakeup(&random_state.rsel);
wakeup(&random_state);
}
/* Unblock the device if it was blocked due to being unseeded */
random_unblock();
}
/* Internal function to do return processed entropy from the
* Yarrow PRNG
*/
u_int
read_random_real(void *buf, u_int count)
{
@ -376,12 +272,13 @@ read_random_real(void *buf, u_int count)
if (count >= sizeof(random_state.counter)) {
retval = 0;
for (i = 0; i < count; i += sizeof(random_state.counter)) {
random_state.counter++;
yarrow_encrypt(&random_state.key, &random_state.counter,
&genval, sizeof(random_state.counter));
random_state.counter[0]++;
yarrow_encrypt(&random_state.key, random_state.counter,
&genval);
memcpy((char *)buf + i, &genval,
sizeof(random_state.counter));
if (++random_state.outputblocks >= random_state.gengateinterval) {
if (++random_state.outputblocks >=
random_state.gengateinterval) {
generator_gate();
random_state.outputblocks = 0;
}
@ -390,12 +287,13 @@ read_random_real(void *buf, u_int count)
}
else {
if (!cur) {
random_state.counter++;
yarrow_encrypt(&random_state.key, &random_state.counter,
&genval, sizeof(random_state.counter));
random_state.counter[0]++;
yarrow_encrypt(&random_state.key, random_state.counter,
&genval);
memcpy(buf, &genval, count);
cur = sizeof(random_state.counter) - count;
if (++random_state.outputblocks >= random_state.gengateinterval) {
if (++random_state.outputblocks >=
random_state.gengateinterval) {
generator_gate();
random_state.outputblocks = 0;
}
@ -414,38 +312,10 @@ read_random_real(void *buf, u_int count)
return retval;
}
void
write_random(void *buf, u_int count)
{
u_int i;
/* Break the input up into HARVESTSIZE chunks.
* The writer has too much control here, so "estimate" the
* the entropy as zero.
*/
for (i = 0; i < count; i += HARVESTSIZE) {
random_harvest_internal(get_cyclecount(), (char *)buf + i,
HARVESTSIZE, 0, 0, RANDOM_WRITE);
}
/* Maybe the loop iterated at least once */
if (i > count)
i -= HARVESTSIZE;
/* Get the last bytes even if the input length is not
* a multiple of HARVESTSIZE.
*/
count %= HARVESTSIZE;
if (count) {
random_harvest_internal(get_cyclecount(), (char *)buf + i,
count, 0, 0, RANDOM_WRITE);
}
}
static void
generator_gate(void)
{
int i;
u_int i;
u_char temp[KEYSIZE];
#ifdef DEBUG
@ -455,12 +325,12 @@ generator_gate(void)
#endif
for (i = 0; i < KEYSIZE; i += sizeof(random_state.counter)) {
random_state.counter++;
yarrow_encrypt(&random_state.key, &random_state.counter,
&(temp[i]), sizeof(random_state.counter));
random_state.counter[0]++;
yarrow_encrypt(&random_state.key, random_state.counter,
&(temp[i]));
}
yarrow_encrypt_init(&random_state.key, temp, KEYSIZE);
yarrow_encrypt_init(&random_state.key, temp);
memset((void *)temp, 0, KEYSIZE);
#ifdef DEBUG
@ -470,43 +340,6 @@ generator_gate(void)
#endif
}
/* Entropy harvesting routine. This is supposed to be fast; do
* not do anything slow in here!
*/
static void
random_harvest_internal(u_int64_t somecounter, void *entropy, u_int count,
u_int bits, u_int frac, enum esource origin)
{
struct harvest *harvest;
int newhead;
newhead = (harvestring.head + 1) & HARVEST_RING_MASK;
if (newhead != harvestring.tail) {
/* Add the harvested data to the ring buffer */
harvest = &harvestring.data[harvestring.head];
/* Stuff the harvested data into the ring */
harvest->somecounter = somecounter;
count = count > HARVESTSIZE ? HARVESTSIZE : count;
memcpy(harvest->entropy, entropy, count);
harvest->size = count;
harvest->bits = bits;
harvest->frac = frac;
harvest->source = origin < ENTROPYSOURCE ? origin : 0;
/* Bump the ring counter. This action is assumed
* to be atomic.
*/
harvestring.head = newhead;
}
}
/* Helper routine to perform explicit reseeds */
void
random_reseed(void)

View File

@ -26,42 +26,25 @@
* $FreeBSD$
*/
/* #define ENTROPYSOURCE nn entropy sources (actually classes)
* This is properly defined in
* an enum in sys/random.h
/* This contains Yarrow-specific declarations.
* See http://www.counterpane.com/yarrow.html
*/
/* 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 TIMEBIN 16 /* max value for Pt/t */
#define HARVESTSIZE 16 /* max size of each harvested entropy unit */
#define FAST 0
#define SLOW 1
int random_init(void);
void random_deinit(void);
void random_init_harvester(void (*)(u_int64_t, void *, u_int, u_int, u_int, enum esource), u_int (*)(void *, u_int));
void random_deinit_harvester(void);
void random_set_wakeup_exit(void *);
void random_reseed(void);
u_int read_random_real(void *, u_int);
void write_random(void *, u_int);
/* This is the beastie that needs protecting. It contains all of the
* state that we are excited about.
* Exactly one will be instantiated.
*/
struct random_state {
u_int64_t counter; /* C */
u_int64_t counter[4]; /* C - 256 bits */
struct yarrowkey key; /* K */
int gengateinterval; /* Pg */
int bins; /* Pt/t */
int outputblocks; /* count output blocks for gates */
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 {
@ -72,10 +55,7 @@ struct random_state {
u_int thresh; /* pool reseed threshhold */
struct yarrowhash hash; /* accumulated entropy */
} pool[2]; /* pool[0] is fast, pool[1] is slow */
int which; /* toggle - shows the current insertion pool */
int seeded; /* 0 causes blocking 1 allows normal output */
struct selinfo rsel; /* For poll(2) */
u_char raw[HARVESTSIZE];/* Raw buffer for checking */
u_int which; /* toggle - sets the current insertion pool */
};
extern struct random_state random_state;