Addition of /dev/random and in-kernel random value generation.

Over the next few days (thank goodness for long weekends) I'll be hunting
down device drivers and adding hooks to gather entropy from many devices,
and adding the conf.c changes to the various port's device structs to
define major numbers for /dev/random and /dev/urandom.
This commit is contained in:
explorer 1997-10-09 23:13:12 +00:00
parent ff14dd5a30
commit 2021c11247
3 changed files with 1221 additions and 0 deletions

755
sys/dev/rnd.c Normal file
View File

@ -0,0 +1,755 @@
/* $NetBSD: rnd.c,v 1.1 1997/10/09 23:13:12 explorer Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Michael Graff <explorer@flame.org>.
*
* 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.
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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/param.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/select.h>
#include <sys/poll.h>
#include <sys/malloc.h>
#include <sys/md5.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/systm.h>
#include <sys/rnd.h>
#include <sys/vnode.h>
#ifdef RND_DEBUG
#define DPRINTF(l,x) if (rnd_debug & (l)) printf x
int rnd_debug = 0;
#else
#define DPRINTF(l,x)
#endif
#define RND_DEBUG_WRITE 0x0001
#define RND_DEBUG_READ 0x0002
#define RND_DEBUG_IOCTL 0x0004
#define RND_DEBUG_SNOOZE 0x0008
/*
* list devices attached
*/
#define RND_VERBOSE
/*
* Use the extraction time as a somewhat-random source
*/
#ifndef RND_USE_EXTRACT_TIME
#define RND_USE_EXTRACT_TIME 1
#endif
/*
* The size of a temporary buffer, malloc()ed when needed, and used for
* reading and writing data.
*/
#define RND_TEMP_BUFFER_SIZE 128
/*
* This is the minimum amount of random data we can have and be
* considered "readable"
*/
#define RND_ENTROPY_THRESHOLD 64
/*
* our select/poll queue
*/
struct selinfo rnd_selq;
/*
* Set when there are readers blocking on data from us
*/
#define RND_READWAITING 0x00000001
u_int32_t rnd_status;
void rndattach __P((int));
int rndopen __P((dev_t, int, int, struct proc *));
int rndclose __P((dev_t, int, int, struct proc *));
int rndread __P((dev_t, struct uio *, int));
int rndwrite __P((dev_t, struct uio *, int));
int rndioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
int rndpoll __P((dev_t, int, struct proc *));
static inline u_int32_t rnd_estimate_entropy(rndsource_t *, u_int32_t *);
/*
* Use the timing of the event to estimate the entropy gathered.
* Note that right now we will return either one or two, depending on
* if all the differentials (first, second, and third) are non-zero.
*/
static inline u_int32_t
rnd_estimate_entropy(rs, t)
rndsource_t *rs;
u_int32_t *t;
{
struct timeval tv;
int32_t delta;
int32_t delta2;
int32_t delta3;
microtime(&tv);
*t = tv.tv_sec * 1000000 + tv.tv_usec;
if (*t < rs->last_time)
delta = UINT_MAX - rs->last_time + *t;
else
delta = rs->last_time - *t;
if (delta < 0)
delta = -delta;
delta2 = rs->last_delta - delta;
if (delta2 < 0)
delta2 = -delta2;
delta3 = rs->last_delta2 - delta2;
if (delta3 < 0)
delta3 = -delta3;
rs->last_time = *t;
rs->last_delta = delta;
rs->last_delta2 = delta2;
if (delta == 0 || delta2 == 0 || delta3 == 0)
return 0;
return 1;
}
static int rnd_ready;
LIST_HEAD(, __rndsource_element) rnd_sources;
/*
* attach the random device, and initialize the global random pool
* for our use.
*/
void
rndattach(num)
int num;
{
rnd_init();
}
int
rndopen(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
if (rnd_ready == 0)
return (ENXIO);
if (minor(dev) == RND_DEV_RANDOM || minor(dev) == RND_DEV_URANDOM)
return 0;
return (ENXIO);
}
int
rndclose(dev, flags, ifmt, p)
dev_t dev;
int flags, ifmt;
struct proc *p;
{
return (0);
}
int
rndread(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
int ret;
u_int32_t nread;
int n;
int s;
u_int8_t *buf;
u_int32_t mode;
u_int32_t entcnt;
DPRINTF(RND_DEBUG_READ,
("Random: Read of %d requested, flags 0x%08x\n",
uio->uio_resid, ioflag));
if (uio->uio_resid == 0)
return (0);
switch (minor(dev)) {
case RND_DEV_RANDOM:
mode = RND_EXTRACT_GOOD;
break;
case RND_DEV_URANDOM:
mode = RND_EXTRACT_ANY;
break;
default:
/* Can't happen, but this is cheap */
return (ENXIO);
}
ret = 0;
buf = malloc(RND_TEMP_BUFFER_SIZE, M_TEMP, M_WAITOK);
while (uio->uio_resid > 0) {
n = min(RND_TEMP_BUFFER_SIZE, uio->uio_resid);
/*
* Make certain there is data available. If there
* is, do the I/O even if it is partial. If not,
* sleep unless the user has requested non-blocking
* I/O.
*/
for (;;) {
/*
* If not requesting strong randomness, we
* can always read.
*/
if (mode == RND_EXTRACT_ANY)
break;
s = splhigh();
entcnt = rndpool_get_entropy_count(NULL);
splx(s);
if (entcnt >= RND_ENTROPY_THRESHOLD)
break;
/*
* Data is not available.
*/
if (ioflag & IO_NDELAY) {
ret = EWOULDBLOCK;
goto out;
}
rnd_status |= RND_READWAITING;
ret = tsleep(&rnd_status, PRIBIO|PCATCH,
"rndread", 0);
if (ret)
goto out;
}
nread = rnd_extract_data(buf, n, mode);
/*
* copy (possibly partial) data to the user.
* If an error occurs, or this is a partial
* read, bail out.
*/
ret = uiomove((caddr_t)buf, nread, uio);
if (ret != 0 || nread != n)
goto out;
}
out:
free(buf, M_TEMP);
return (ret);
}
int
rndwrite(dev, uio, ioflag)
dev_t dev;
struct uio *uio;
int ioflag;
{
u_int8_t *buf;
int ret;
int n;
DPRINTF(RND_DEBUG_WRITE,
("Random: Write of %d requested\n", uio->uio_resid));
if (uio->uio_resid == 0)
return (0);
ret = 0;
buf = malloc(RND_TEMP_BUFFER_SIZE, M_TEMP, M_WAITOK);
while (uio->uio_resid > 0) {
n = min(RND_TEMP_BUFFER_SIZE, uio->uio_resid);
ret = uiomove((caddr_t)buf, n, uio);
if (ret != 0)
break;
/*
* Mix in the bytes.
*/
rnd_add_data(NULL, buf, n, 0);
DPRINTF(RND_DEBUG_WRITE, ("Random: Copied in %d bytes\n", n));
}
free(buf, M_TEMP);
return (ret);
}
int
rndioctl(dev, cmd, addr, flag, p)
dev_t dev;
u_long cmd;
caddr_t addr;
int flag;
struct proc *p;
{
int ret;
rndsource_element_t *rse;
rndstat_t *rst;
rndstat_name_t *rstnm;
rndctl_t *rctl;
rnddata_t *rnddata;
u_int32_t count;
u_int32_t start;
ret = 0;
switch (cmd) {
/*
* Handled in upper layer really, but we have to return zero
* for it to be accepted by the upper layer.
*/
case FIONBIO:
case FIOASYNC:
break;
case RNDGETENTCNT:
*(u_int32_t *)addr = rndpool_get_entropy_count(NULL);
break;
case RNDGETPOOL:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
bcopy(rndpool_get_pool(NULL), addr, rndpool_get_poolsize());
break;
case RNDADDTOENTCNT:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
rndpool_increment_entropy_count(NULL, *(u_int32_t *)addr);
break;
case RNDSETENTCNT:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
rndpool_set_entropy_count(NULL, *(u_int32_t *)addr);
break;
case RNDGETSRCNUM:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
rst = (rndstat_t *)addr;
if (rst->count == 0)
break;
if (rst->count > RND_MAXSTATCOUNT)
return (EINVAL);
/*
* find the starting source by running through the
* list of sources.
*/
rse = rnd_sources.lh_first;
start = rst->start;
while (rse != NULL && start >= 1) {
rse = rse->list.le_next;
start--;
}
/*
* Return up to as many structures as the user asked
* for. If we run out of sources, a count of zero
* will be returned, without an error.
*/
for (count = 0 ; count < rst->count && rse != NULL ; count++) {
bcopy(&rse->data, &rst->source[count],
sizeof(rndsource_t));
rse = rse->list.le_next;
}
rst->count = count;
break;
case RNDGETSRCNAME:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
/*
* scan through the list, trying to find the name
*/
rstnm = (rndstat_name_t *)addr;
rse = rnd_sources.lh_first;
while (rse != NULL) {
if (strncmp(rse->data.name, rstnm->name, 16) == 0) {
bcopy(&rse->data, &rstnm->source,
sizeof(rndsource_t));
return 0;
}
rse = rse->list.le_next;
}
ret = ENOENT; /* name not found */
break;
case RNDCTL:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
/*
* set flags to enable/disable entropy counting and/or
* collection
*/
rctl = (rndctl_t *)addr;
rse = rnd_sources.lh_first;
/*
* flags set apply to all sources of this type
*/
if (rctl->type != 0xff) {
while (rse != NULL) {
if ((rse->data.tyfl & 0xff) == rctl->type) {
rse->data.tyfl &= ~rctl->mask;
rse->data.tyfl |= (rctl->flags
& rctl->mask);
}
rse = rse->list.le_next;
}
return 0;
}
/*
* scan through the list, trying to find the name
*/
while (rse != NULL) {
if (strncmp(rse->data.name, rctl->name, 16) == 0) {
rse->data.tyfl &= ~rctl->mask;
rse->data.tyfl |= (rctl->flags & rctl->mask);
return 0;
}
rse = rse->list.le_next;
}
ret = ENOENT; /* name not found */
break;
case RNDADDDATA:
if ((ret = suser(p->p_ucred, &p->p_acflag)) != 0)
return (ret);
rnddata = (rnddata_t *)addr;
rnd_add_data(NULL, rnddata->data, rnddata->len,
rnddata->entropy);
break;
default:
return (EINVAL);
}
return (ret);
}
int
rndpoll(dev, events, p)
dev_t dev;
int events;
struct proc *p;
{
int revents;
int s;
u_int32_t entcnt;
/*
* we are always writable
*/
revents = events & (POLLOUT | POLLWRNORM);
/*
* Save some work if not checking for reads
*/
if ((events & (POLLIN | POLLRDNORM)) == 0)
return (revents);
/*
* If the minor device is not /dev/random, we are always readable.
*/
if (minor(dev) != RND_DEV_RANDOM) {
revents |= events & (POLLIN | POLLRDNORM);
return (revents);
}
/*
* make certain we have enough entropy to be readable
*/
s = splhigh();
entcnt = rndpool_get_entropy_count(NULL);
splx(s);
if (entcnt >= RND_ENTROPY_THRESHOLD)
revents |= events & (POLLIN | POLLRDNORM);
else
selrecord(p, &rnd_selq);
return (revents);
}
/*
* always called at splhigh() or something else safe
*/
void
rnd_init(void)
{
if (rnd_ready)
return;
LIST_INIT(&rnd_sources);
rndpool_init_global();
rnd_ready = 1;
#ifdef RND_VERBOSE
printf("Random device ready\n");
#endif
}
/*
* add a source to our list of sources
*/
void
rnd_attach_source(rs, name, tyfl)
rndsource_element_t *rs;
char *name;
u_int32_t tyfl;
{
strcpy(rs->data.name, name);
rs->data.tyfl = tyfl;
LIST_INSERT_HEAD(&rnd_sources, rs, list);
#ifdef RND_VERBOSE
printf("%s: attached as an entropy source\n", rs->data.name);
#endif
}
/*
* Add a value to the entropy pool. If rs is NULL no entropy estimation
* will be performed, otherwise it should point to the source-specific
* source structure.
*/
void
rnd_add_uint32(rs, val)
rndsource_element_t *rs;
u_int32_t val;
{
int s;
u_int32_t entropy;
u_int32_t t;
s = splhigh();
/*
* if the source is NULL, add the byte given and return.
*/
if (rs == NULL) {
rndpool_add_uint32(NULL, val, 0);
splx(s);
return;
}
/*
* If we are not collecting any data at all, just return.
*/
if (rs->data.tyfl & RND_FLAG_NO_COLLECT) {
splx(s);
return;
}
rndpool_add_uint32(NULL, val, 0);
/*
* If we are not estimating entropy from this source, we are done.
*/
if (rs->data.tyfl & RND_FLAG_NO_ESTIMATE) {
splx(s);
return;
}
entropy = rnd_estimate_entropy(&rs->data, &t);
if (entropy) {
rndpool_add_uint32(NULL, t, entropy);
rs->data.total += entropy;
/*
* if we have enough bits to do something useful,
* wake up sleeping readers.
*/
if (rndpool_get_entropy_count(NULL) > RND_ENTROPY_THRESHOLD) {
if (rnd_status & RND_READWAITING) {
DPRINTF(RND_DEBUG_SNOOZE,
("waking up pending readers.\n"));
rnd_status &= ~RND_READWAITING;
wakeup(&rnd_status);
}
selwakeup(&rnd_selq);
}
}
splx(s);
}
void
rnd_add_data(rs, p, len, entropy)
rndsource_element_t *rs;
void *p;
u_int32_t len;
u_int32_t entropy;
{
int s;
u_int32_t t;
/*
* if the caller is trying to add more entropy than can possibly
* be in the buffer we are passed, ignore the whole thing.
*/
if (entropy > len * 8)
return;
s = splhigh();
/*
* if the source is NULL, add the data and return.
*/
if (rs == NULL) {
rndpool_add_data(NULL, p, len, entropy);
splx(s);
return;
}
/*
* If we are not collecting any data at all, just return.
*/
if (rs->data.tyfl & RND_FLAG_NO_COLLECT) {
splx(s);
return;
}
rndpool_add_data(NULL, p, len, entropy);
/*
* If we are not estimating entropy from this source, we are done.
*/
if (rs->data.tyfl & RND_FLAG_NO_ESTIMATE) {
splx(s);
return;
}
entropy = rnd_estimate_entropy(&rs->data, &t);
if (entropy) {
rndpool_add_uint32(NULL, t, entropy);
rs->data.total += entropy;
/*
* if we have enough bits to do something useful,
* wake up sleeping readers.
*/
if (rndpool_get_entropy_count(NULL) > RND_ENTROPY_THRESHOLD) {
if (rnd_status & RND_READWAITING) {
DPRINTF(RND_DEBUG_SNOOZE,
("waking up pending readers.\n"));
rnd_status &= ~RND_READWAITING;
wakeup(&rnd_status);
}
selwakeup(&rnd_selq);
}
}
splx(s);
}
int
rnd_extract_data(p, len, flags)
void *p;
u_int32_t len;
u_int32_t flags;
{
struct timeval tv;
int s;
int retval;
s = splhigh();
#if RND_USE_EXTRACT_TIME
microtime(&tv);
rndpool_add_uint32(NULL, tv.tv_usec, 0);
#endif
retval = rndpool_extract_data(NULL, p, len, flags);
splx(s);
return retval;
}

329
sys/dev/rndpool.c Normal file
View File

@ -0,0 +1,329 @@
/* $NetBSD: rndpool.c,v 1.1 1997/10/09 23:13:13 explorer Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Michael Graff <explorer@flame.org>.
*
* 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.
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/md5.h>
#include <sys/rnd.h>
/*
* The random pool "taps"
*/
#define TAP1 13
#define TAP2 113
#define TAP3 230
#define TAP4 412
static rndpool_t _global_rndpool;
static inline void rndpool_add_one_word(rndpool_t *, u_int32_t);
void
rndpool_init(rp)
rndpool_t *rp;
{
rp->cursor = RND_POOLWORDS - 1;
rp->entropy = 0;
rp->rotate = 0;
}
void
rndpool_init_global(void)
{
rndpool_init(&_global_rndpool);
}
u_int32_t
rndpool_get_entropy_count(rp)
rndpool_t *rp;
{
if (rp == NULL)
rp = &_global_rndpool;
return rp->entropy;
}
void
rndpool_set_entropy_count(rp, entropy)
rndpool_t *rp;
u_int32_t entropy;
{
if (rp == NULL)
rp = &_global_rndpool;
rp->entropy = entropy;
if (rp->entropy > RND_POOLBITS)
rp->entropy = RND_POOLBITS;
}
void
rndpool_increment_entropy_count(rp, entropy)
rndpool_t *rp;
u_int32_t entropy;
{
if (rp == NULL)
rp = &_global_rndpool;
rp->entropy += entropy;
if (rp->entropy > RND_POOLBITS)
rp->entropy = RND_POOLBITS;
}
u_int32_t *
rndpool_get_pool(rp)
rndpool_t *rp;
{
if (rp == NULL)
rp = &_global_rndpool;
return (rp->pool);
}
u_int32_t
rndpool_get_poolsize(void)
{
return (RND_POOLWORDS);
}
/*
* Add one word to the pool, rotating the input as needed.
*/
static inline void
rndpool_add_one_word(rp, val)
rndpool_t *rp;
u_int32_t val;
{
if (rp == NULL)
rp = &_global_rndpool;
/*
* Steal some values out of the pool, and xor them into the
* word we were given.
*
* Store the new value into the pool using xor. This will
* prevent the actual values from being known to the caller
* since the previous values are assumed to be unknown as well.
*/
val ^= rp->pool[(rp->cursor + TAP1) & (RND_POOLWORDS - 1)];
val ^= rp->pool[(rp->cursor + TAP2) & (RND_POOLWORDS - 1)];
val ^= rp->pool[(rp->cursor + TAP3) & (RND_POOLWORDS - 1)];
val ^= rp->pool[(rp->cursor + TAP4) & (RND_POOLWORDS - 1)];
rp->pool[rp->cursor++] ^=
((val << rp->rotate) | (val >> (31 - rp->rotate)));
/*
* If we have looped around the pool, increment the rotate
* variable so the next value will get xored in slightly
* rotated. Increment by a value that is relativly prime to
* the word size to try to spread the bits throughout the pool
* quickly when the pool is empty.
*/
if (rp->cursor == RND_POOLWORDS) {
rp->cursor = 0;
rp->rotate = (rp->rotate + 7) & 31;
}
}
/*
* Add one byte to the pool. Update entropy estimate if an estimate
* was given.
*/
void
rndpool_add_uint32(rp, val, entropy)
rndpool_t *rp;
u_int32_t val;
u_int32_t entropy;
{
if (rp == NULL)
rp = &_global_rndpool;
val = (val << rp->rotate) | (val >> rp->rotate);
rp->rotate = (rp->rotate + 1) & 0x07;
rndpool_add_one_word(rp, val);
if (entropy) {
rp->entropy += entropy;
if (rp->entropy > RND_POOLBITS)
rp->entropy = RND_POOLBITS;
}
}
/*
* add a buffer's worth of data to the pool.
*/
void
rndpool_add_data(rp, p, len, entropy)
rndpool_t *rp;
void *p;
u_int32_t len;
u_int32_t entropy;
{
u_int32_t val;
u_int8_t *buf;
if (rp == NULL)
rp = &_global_rndpool;
buf = p;
for (; len > 3 ; len -= 4) {
val = *((u_int32_t *)buf);
rndpool_add_one_word(rp, val);
buf += 4;
}
val = 0;
if (len != 0) {
switch (len) {
case 3:
val = *buf++;
case 2:
val = val << 8 | *buf++;
case 1:
val = val << 8 | *buf++;
}
rndpool_add_one_word(rp, val);
}
if (entropy) {
rp->entropy += entropy;
if (rp->entropy > RND_POOLBITS)
rp->entropy = RND_POOLBITS;
}
}
/*
* Extract some number of bytes from the random pool, decreasing the
* estimate of randomness as each byte is extracted.
*
* Do this by stiring the pool and returning a part of hash as randomness.
* Note that no secrets are given away here since parts of the hash are
* xored together before returned.
*
* Honor the request from the caller to only return good data, any data,
* etc. Note that we must have at least 64 bits of entropy in the pool
* before we return anything in the high-quality modes.
*/
int
rndpool_extract_data(rp, p, len, mode)
rndpool_t *rp;
void *p;
u_int32_t len;
u_int32_t mode;
{
u_int i;
MD5_CTX md5;
u_int32_t digest[4];
u_int32_t remain;
u_int8_t *buf;
int good;
if (rp == NULL)
rp = &_global_rndpool;
buf = p;
remain = len;
if (mode == RND_EXTRACT_ANY)
good = 1;
else
good = (rp->entropy >= 64); /* size of hash returned */
/*
* While bytes are requested, stir the pool with a hash function and
* copy some of the bytes from that hash out, preserving the secret
* hash value itself.
*/
while (good && (remain != 0)) {
MD5Init(&md5);
MD5Update(&md5, (u_int8_t *)rp->pool, RND_POOLWORDS * 4);
MD5Final((u_int8_t *)digest, &md5);
/*
* Add the hash into the pool. This helps stir the pool a
* bit, and also guarantees that the next hash will generate
* a different value if no new values were added to the
* pool.
*/
for (i = 0 ; i < 4 ; i++)
rndpool_add_one_word(rp, digest[i]);
/*
* copy out the bytes, but xor two bytes from the hash
* together before returning them. This allows us (with
* MD5 as the hash function) to return 8 bytes per hash
* call and not give out any information to the caller.
*/
digest[0] ^= digest[2];
digest[1] ^= digest[3];
if (remain < 8) {
bcopy(digest, buf, remain);
remain = 0;
buf += remain;
if (rp->entropy >= remain * 8)
rp->entropy -= remain * 8;
else
rp->entropy = 0;
} else {
bcopy(digest, buf, 8);
buf += 8;
remain -= 8;
if (rp->entropy >= 64)
rp->entropy -= 64;
else
rp->entropy = 0;
}
if (mode == RND_EXTRACT_GOOD)
good = (rp->entropy >= 64);
}
bzero(&md5, sizeof(MD5_CTX));
bzero(digest, sizeof(digest));
return (len - remain);
}

137
sys/sys/rnd.h Normal file
View File

@ -0,0 +1,137 @@
/* $NetBSD: rnd.h,v 1.1 1997/10/09 23:13:16 explorer Exp $ */
#ifndef _SYS_RND_H_
#define _SYS_RND_H_
#ifndef _KERNEL
#include <sys/cdefs.h>
#endif /* !_KERNEL */
#include <sys/types.h>
#ifdef _KERNEL
#include <sys/queue.h>
#endif
#define RND_DEV_RANDOM 0 /* minor devices for random and kinda random */
#define RND_DEV_URANDOM 1
#ifndef RND_POOLWORDS
#define RND_POOLWORDS 128 /* size of entropy pool in 32-bit words */
#endif
#define RND_POOLBITS (RND_POOLWORDS * 32)
typedef struct {
u_int32_t cursor; /* current add point in the pool */
u_int32_t entropy; /* current entropy estimate in bits */
u_int32_t rotate; /* how many bits to rotate by */
u_int32_t pool[RND_POOLWORDS]; /* random pool data */
} rndpool_t;
typedef struct {
char name[16]; /* device name */
u_int32_t last_time; /* last time recorded */
u_int32_t last_delta; /* last delta value */
u_int32_t last_delta2; /* last delta2 value */
u_int32_t total; /* entropy from this source */
u_int32_t tyfl; /* type and flags */
} rndsource_t;
/*
* Flags to control the source. Low byte is type, upper bits are flags.
*/
#define RND_FLAG_NO_ESTIMATE 0x00000100 /* don't estimate entropy */
#define RND_FLAG_NO_COLLECT 0x00000200 /* don't collect entropy */
#define RND_TYPE_UNKNOWN 0 /* unknown source */
#define RND_TYPE_DISK 1 /* source is physical disk */
#define RND_TYPE_NET 2 /* source is a network device */
#define RND_TYPE_TAPE 3 /* source is a tape drive */
#define RND_TYPE_MAX 3 /* last type id used */
#ifdef _KERNEL
typedef struct __rndsource_element rndsource_element_t;
struct __rndsource_element {
LIST_ENTRY(__rndsource_element) list; /* the linked list */
rndsource_t data; /* the actual data */
};
/*
* Used by rnd_extract_data() and rndpool_extract_data() to describe how
* "good" the data has to be.
*/
#define RND_EXTRACT_ANY 0 /* extract anything, even if no entropy */
#define RND_EXTRACT_GOOD 1 /* return as many good bytes (short read ok) */
void rndpool_init __P((rndpool_t *));
void rndpool_init_global __P((void));
u_int32_t rndpool_get_entropy_count __P((rndpool_t *));
void rndpool_set_entropy_count __P((rndpool_t *, u_int32_t));
void rndpool_increment_entropy_count __P((rndpool_t *, u_int32_t));
u_int32_t *rndpool_get_pool __P((rndpool_t *));
u_int32_t rndpool_get_poolsize __P((void));
void rndpool_add_uint32 __P((rndpool_t *, u_int32_t, u_int32_t));
void rndpool_add_data __P((rndpool_t *, void *, u_int32_t,
u_int32_t));
int rndpool_extract_data __P((rndpool_t *, void *, u_int32_t,
u_int32_t));
void rnd_init __P((void));
void rnd_add_uint32 __P((rndsource_element_t *, u_int32_t));
void rnd_add_data __P((rndsource_element_t *, void *, u_int32_t,
u_int32_t));
int rnd_extract_data __P((void *, u_int32_t, u_int32_t));
void rnd_attach_source __P((rndsource_element_t *, char *,
u_int32_t));
#endif /* _KERNEL */
#define RND_MAXSTATCOUNT 10 /* 10 sources at once max */
/*
* return "count" random entries, starting at "start"
*/
typedef struct {
u_int32_t start;
u_int32_t count;
rndsource_t source[RND_MAXSTATCOUNT];
} rndstat_t;
/*
* return information on a specific source by name
*/
typedef struct {
char name[16];
rndsource_t source;
} rndstat_name_t;
/*
* set/clear device flags. If type is set to 0xff, the name is used
* instead. Otherwise, the flags set/cleared apply to all devices of
* the specified type, and the name is ignored.
*/
typedef struct {
char name[16]; /* the name we are adjusting */
u_int32_t type; /* the type of device we want */
u_int32_t flags; /* flags to set or clear */
u_int32_t mask; /* mask for the flags we are setting */
} rndctl_t;
typedef struct {
u_int32_t len;
u_int32_t entropy;
u_char data[RND_POOLWORDS * 4];
} rnddata_t;
#define RNDGETENTCNT _IOR('R', 101, u_int32_t) /* get entropy count */
#define RNDSETENTCNT _IOW('R', 102, u_int32_t) /* set the entropy count */
#define RNDGETPOOL _IOR('R', 103, u_int32_t) /* get whole pool */
#define RNDADDTOENTCNT _IOW('R', 104, u_int32_t) /* add to entropy count */
#define RNDGETSRCNUM _IOWR('R', 105, rndstat_t) /* get rnd source info */
#define RNDGETSRCNAME _IOWR('R', 106, rndstat_t) /* get src by name */
#define RNDCTL _IOW('R', 107, rndctl_t) /* set/clear source flags */
#define RNDADDDATA _IOW('R', 108, rnddata_t) /* add data to the pool */
#endif /* !_SYS_RND_H_ */