-/* $OpenBSD: rnd.c,v 1.34 2000/03/19 17:38:03 mickey Exp $ */
+/* $OpenBSD: rnd.c,v 1.35 2000/04/10 19:44:38 mickey Exp $ */
/*
* random.c -- A strong random number generator
* Copyright (c) 1996, 1997 Michael Shalayeff.
*
* Version 1.00, last modified 26-May-96
- *
+ *
* Copyright Theodore Ts'o, 1994, 1995, 1996. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
- *
+ *
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
- *
+ *
* THIS SOFTWARE IS PROVIDED ``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
*/
/*
- * (now, with legal B.S. out of the way.....)
- *
+ * (now, with legal B.S. out of the way.....)
+ *
* This routine gathers environmental noise from device drivers, etc.,
* and returns good random numbers, suitable for cryptographic use.
* Besides the obvious cryptographic uses, these numbers are also good
*
* Theory of operation
* ===================
- *
+ *
* Computers are very predictable devices. Hence it is extremely hard
* to produce truly random numbers on a computer --- as opposed to
* pseudo-random numbers, which can easily generated by using a
* must be hard for outside attackers to observe, and use that to
* generate random numbers. In a Unix environment, this is best done
* from inside the kernel.
- *
+ *
* Sources of randomness from the environment include inter-keyboard
* timings, inter-interrupt timings from some interrupts, and other
* events which are both (a) non-deterministic and (b) hard for an
* As random bytes are mixed into the entropy pool, the routines keep
* an *estimate* of how many bits of randomness have been stored into
* the random number generator's internal state.
- *
+ *
* When random bytes are desired, they are obtained by taking the MD5
* hash of the contents of the "entropy pool". The MD5 hash avoids
* exposing the internal state of the entropy pool. It is believed to
* reason, the routine decreases its internal estimate of how many
* bits of "true randomness" are contained in the entropy pool as it
* outputs random numbers.
- *
+ *
* If this estimate goes to zero, the routine can still generate
* random numbers; however, an attacker may (at least in theory) be
* able to infer the future output of the generator from prior
* not believed to be feasible, but there is a remote possibility.
* Nonetheless, these numbers should be useful for the vast majority
* of purposes.
- *
+ *
* Exported interfaces ---- output
* ===============================
- *
+ *
* There are three exported interfaces; the first is one designed to
* be used from within the kernel:
*
- * void get_random_bytes(void *buf, int nbytes);
+ * void get_random_bytes(void *buf, int nbytes);
*
* This interface will return the requested number of random bytes,
* and place it in the requested buffer.
- *
+ *
* The two other interfaces are two character devices /dev/random and
* /dev/urandom. /dev/random is suitable for use when very high
* quality randomness is desired (for example, for key generation or
* one-time pads), as it will only return a maximum of the number of
* bits of randomness (as estimated by the random number generator)
* contained in the entropy pool.
- *
+ *
* The /dev/urandom device does not have this limit, and will return
* as many bytes as are requested. As more and more random bytes are
* requested without giving time for the entropy pool to recharge,
*
* Exported interfaces ---- input
* ==============================
- *
+ *
* The current exported interfaces for gathering environmental noise
* from the devices are:
- *
- * void add_mouse_randomness(u_int32_t mouse_data);
- * void add_net_randomness(int isr);
+ *
+ * void add_true_randomness(int data);
+ * void add_timer_randomness(int data);
+ * void add_mouse_randomness(int mouse_data);
+ * void add_net_randomness(int isr);
* void add_tty_randomness(int c);
- * void add_disk_randomness(u_int32_t n);
- *
+ * void add_disk_randomness(int n);
+ * void add_audio_randomness(int n);
+ *
+ * add_true_randomness() uses true random number generators present
+ * on some cryptographic and system chipsets. entropy accounting
+ * is not quitable, no timing is done, supplied 32 bits of pure entropy
+ * are hashed into the pool plain and blindly, increasing the counter.
+ *
+ * add_timer_randomness() uses the random driver itselves timing,
+ * measuring extract_entropy() and rndioctl() execution times.
+ *
* add_mouse_randomness() uses the mouse interrupt timing, as well as
* the reported position of the mouse from the hardware.
*
* add_net_randomness() times the finishing time of net input.
- *
+ *
* add_tty_randomness() uses the inter-keypress timing, as well as the
* character as random inputs into the "entropy pool".
- *
+ *
* add_disk_randomness() times the finishing time of disk requests as well
* as feeding both xfer size & time into the entropy pool.
- *
- * All of these routines try to estimate how many bits of randomness a
- * particular randomness source. They do this by keeping track of the
- * first and second order deltas of the event timings.
+ *
+ * add_audio_randomness() times the finishing of audio codec dma
+ * requests for both recording and playback, apparently supplies quite
+ * a lot of entropy, i'd blame on low resolution audio clock generators.
+ *
+ * All of these routines (except for add_true_randomness() of course)
+ * try to estimate how many bits of randomness a particular randomness
+ * source. They do this by keeping track of the first and second order
+ * deltas of the event timings.
*
* Ensuring unpredictability at system startup
* ============================================
- *
+ *
* When any operating system starts up, it will go through a sequence
* of actions that are fairly predictable by an adversary, especially
* if the start-up does not involve interaction with a human operator.
* counteract this effect, it helps to carry information in the
* entropy pool across shut-downs and start-ups. To do this, put the
* following lines an appropriate script which is run during the boot
- * sequence:
+ * sequence:
*
* echo "Initializing random number generator..."
* # Carry a random seed from start-up to start-up
* # Load and then save 512 bytes, which is the size of the entropy pool
- * if [ -f /etc/random-seed ]; then
+ * if [ -f /etc/random-seed ]; then
* cat /etc/random-seed >/dev/urandom
- * fi
+ * fi
* dd if=/dev/urandom of=/etc/random-seed count=1
*
* and the following lines in an appropriate script which is run as
* the system is shutdown:
- *
+ *
* # Carry a random seed from shut-down to start-up
* # Save 512 bytes, which is the size of the entropy pool
* echo "Saving random seed..."
* dd if=/dev/urandom of=/etc/random-seed count=1
- *
+ *
* For example, on many Linux systems, the appropriate scripts are
* usually /etc/rc.d/rc.local and /etc/rc.d/rc.0, respectively.
- *
+ *
* Effectively, these commands cause the contents of the entropy pool
* to be saved at shut-down time and reloaded into the entropy pool at
* start-up. (The 'dd' in the addition to the bootup script is to
* /dev/random and /dev/urandom created already, they can be created
* by using the commands:
*
- * mknod /dev/random c 1 8
- * mknod /dev/urandom c 1 9
- *
+ * mknod /dev/random c 1 8
+ * mknod /dev/urandom c 1 9
+ *
* Acknowledgements:
* =================
*
* the input values to add_entropy_word() are mostly small numbers.
* Dale Worley has also contributed many useful ideas and suggestions
* to improve this driver.
- *
+ *
* Any flaws in the design are solely my responsibility, and should
* not be attributed to the Phil, Colin, or any of authors of PGP.
- *
+ *
* The code for MD5 transform was taken from Colin Plumb's
* implementation, which has been placed in the public domain. The
* MD5 cryptographic checksum was devised by Ronald Rivest, and is
* documented in RFC 1321, "The MD5 Message Digest Algorithm".
- *
+ *
* Further background information on this topic may be obtained from
* RFC 1750, "Randomness Recommendations for Security", by Donald
* Eastlake, Steve Crocker, and Jeff Schiller.
#include <sys/vnode.h>
#include <sys/md5k.h>
#include <sys/sysctl.h>
+#include <sys/timeout.h>
#include <dev/rndvar.h>
#include <dev/rndioctl.h>
#error No primitive polynomial available for chosen POOLWORDS
#endif
-/* p60/256kL2 reported to have some drops w/ these numbers */
-#define QEVLEN 40
-#define QEVSLOW 32 /* yet another 0.75 for 60-minutes hour /-; */
-#define QEVSBITS 6
+/* pIII/333 reported to have some drops w/ these numbers */
+#define QEVLEN 96
+#define QEVSLOW 64 /* yet another 0.75 for 60-minutes hour /-; */
+#define QEVSBITS 12
/* There is actually only one of these, globally. */
struct random_bucket {
u_int entropy_count;
u_char input_rotate;
u_int32_t pool[POOLWORDS];
+ u_int asleep;
+ u_int queued;
+ u_int tmo;
};
/* There is one of these per entropy source */
struct timer_rand_state {
u_int last_time;
u_int last_delta;
- u_char dont_count_entropy:1;
+ u_char dont_count_entropy : 1;
+ u_char max_entropy : 1;
};
struct arc4_stream {
u_int re_val;
};
-/* tags for different random sources */
-#define ENT_NET 0x100
-#define ENT_DISK 0x200
-#define ENT_TTY 0x300
+struct timeout rnd_timeout, arc4_timeout;
+struct random_bucket random_state;
+struct arc4_stream arc4random_state;
+struct timer_rand_state rnd_states[RND_SRC_NUM];
+struct rand_event rnd_event_space[QEVLEN];
+struct rand_event *rnd_event_q;
+struct rand_event *rnd_event_free;
+int rnd_attached;
+int arc4random_initialized;
struct rndstats rndstats;
-static struct random_bucket random_state;
-static int arc4random_uninitialized = 2;
-static struct arc4_stream arc4_state;
-static struct timer_rand_state mouse_timer_state;
-static struct timer_rand_state extract_timer_state;
-static struct timer_rand_state disk_timer_state;
-static struct timer_rand_state net_timer_state;
-static struct timer_rand_state tty_timer_state;
-static struct rand_event event_space[QEVLEN];
-static int rnd_attached = 0;
-static struct rand_event *event_q = NULL;
-static struct rand_event *event_free;
-
-#ifndef MIN
-#define MIN(a,b) (((a) < (b)) ? (a) : (b))
-#endif
-static __inline void add_entropy_word __P((const u_int32_t));
-static void enqueue_randomness __P((register struct timer_rand_state*, u_int));
-void dequeue_randomness __P((void *));
+void dequeue_randomness __P((void *v));
+
+static __inline void add_entropy_words __P((const u_int32_t *, u_int));
static __inline void extract_entropy __P((register u_int8_t *, int));
-void arc4_init __P((u_int8_t *, int));
-static __inline void arc4_stir __P((void));
+
static __inline u_int8_t arc4_getbyte __P((void));
+void arc4_stir __P((void));
+void arc4_reinit __P((void *v));
/* Arcfour random stream generator. This code is derived from section
* 17.1 of Applied Cryptography, second edition, which describes a
*/
void
-arc4_init (register u_int8_t *data, int len)
+arc4_stir(void)
{
+ u_int8_t buf[256];
register u_int8_t si;
- register int n;
+ register int n, s;
+ int len;
+
+ microtime((struct timeval *) buf);
+ len = random_state.entropy_count / 8; /* XXX maybe a half? */
+ if (len > sizeof(buf) - sizeof(struct timeval))
+ len = sizeof(buf) - sizeof(struct timeval);
+ get_random_bytes(buf + sizeof (struct timeval), len);
+ len += sizeof(struct timeval);
- arc4_state.i--;
+ s = splhigh();
+ arc4random_state.i--;
for (n = 0; n < 256; n++) {
- arc4_state.i = (arc4_state.i + 1) & 0xff;
- si = arc4_state.s[arc4_state.i];
- arc4_state.j = (arc4_state.j + si + data[n % len]) & 0xff;
- arc4_state.s[arc4_state.i] = arc4_state.s[arc4_state.j];
- arc4_state.s[arc4_state.j] = si;
+ arc4random_state.i++;
+ si = arc4random_state.s[arc4random_state.i];
+ arc4random_state.j += si + buf[n % len];
+ arc4random_state.s[arc4random_state.i] =
+ arc4random_state.s[arc4random_state.j];
+ arc4random_state.s[arc4random_state.j] = si;
}
- arc4_state.j = arc4_state.i;
- arc4_state.cnt = 0;
+ arc4random_state.j = arc4random_state.i;
+ arc4random_state.cnt = 0;
+ rndstats.arc4_stirs += len;
+ rndstats.arc4_nstirs++;
+ splx(s);
}
static __inline u_int8_t
-arc4_getbyte (void)
+arc4_getbyte(void)
{
register u_int8_t si, sj;
rndstats.arc4_reads++;
- arc4_state.cnt++;
- arc4_state.i = (arc4_state.i + 1) & 0xff;
- si = arc4_state.s[arc4_state.i];
- arc4_state.j = (arc4_state.j + si) & 0xff;
- sj = arc4_state.s[arc4_state.j];
- arc4_state.s[arc4_state.i] = sj;
- arc4_state.s[arc4_state.j] = si;
- return (arc4_state.s[(si + sj) & 0xff]);
+ arc4random_state.cnt++;
+ arc4random_state.i++;
+ si = arc4random_state.s[arc4random_state.i];
+ arc4random_state.j += si;
+ sj = arc4random_state.s[arc4random_state.j];
+ arc4random_state.s[arc4random_state.i] = sj;
+ arc4random_state.s[arc4random_state.j] = si;
+ return arc4random_state.s[(si + sj) & 0xff];
}
static __inline void
-arc4maybeinit (void)
+arc4maybeinit(void)
{
- if (arc4random_uninitialized) {
- if (arc4random_uninitialized > 1
- || random_state.entropy_count >= 128) {
- arc4random_uninitialized--;
- arc4_stir ();
- }
+ if (!arc4random_initialized) {
+ arc4random_initialized++;
+ arc4_stir();
}
}
+void
+arc4_reinit(v)
+ void *v;
+{
+ extern int hz;
+
+ arc4random_initialized = 0;
+ timeout_add(&arc4_timeout, 10*60*hz);
+}
+
int
arc4random_8(void)
{
u_int32_t
arc4random(void)
{
- arc4maybeinit ();
- return ((arc4_getbyte () << 24) | (arc4_getbyte () << 16)
- | (arc4_getbyte () << 8) | arc4_getbyte ());
-}
-
-static __inline void
-arc4_stir (void)
-{
- u_int8_t buf[256];
-
- microtime ((struct timeval *) buf);
- get_random_bytes (buf + sizeof (struct timeval),
- sizeof (buf) - sizeof (struct timeval));
- arc4_init (buf, sizeof (buf));
- rndstats.arc4_stirs++;
+ arc4maybeinit();
+ return ((arc4_getbyte() << 24) | (arc4_getbyte() << 16)
+ | (arc4_getbyte() << 8) | arc4_getbyte());
}
void
random_state.add_ptr = 0;
random_state.entropy_count = 0;
- extract_timer_state.dont_count_entropy = 1;
+ rnd_states[RND_SRC_TIMER].dont_count_entropy = 1;
+ rnd_states[RND_SRC_TRUE].dont_count_entropy = 1;
+ rnd_states[RND_SRC_TRUE].max_entropy = 1;
bzero(&rndstats, sizeof(rndstats));
-
- bzero(&event_space, sizeof(event_space));
- event_free = event_space;
- for (rep = event_space; rep < &event_space[QEVLEN-1]; rep++)
+ bzero(&rnd_event_space, sizeof(rnd_event_space));
+ rnd_event_free = rnd_event_space;
+ for (rep = rnd_event_space; rep < &rnd_event_space[QEVLEN-1]; rep++)
rep->re_next = rep + 1;
for (i = 0; i < 256; i++)
- arc4_state.s[i] = i;
- microtime (&tv);
- arc4_init ((u_int8_t *) &tv, sizeof (tv));
+ arc4random_state.s[i] = i;
+ microtime(&tv);
+ timeout_set(&rnd_timeout, dequeue_randomness, NULL);
+ timeout_set(&arc4_timeout, arc4_reinit, NULL);
+ arc4_reinit(NULL);
rnd_attached = 1;
}
* The pool is stirred with a primitive polynomial of degree 128
* over GF(2), namely x^128 + x^99 + x^59 + x^31 + x^9 + x^7 + 1.
* For a pool of size 64, try x^64+x^62+x^38+x^10+x^6+x+1.
- *
+ *
* We rotate the input word by a changing number of bits, to help
* assure that all bits in the entropy get toggled. Otherwise, if we
* consistently feed the entropy pool small numbers (like jiffies and
* get affected. --- TYT, 10/11/95
*/
static __inline void
-add_entropy_word(input)
- const u_int32_t input;
+add_entropy_words(buf, n)
+ const u_int32_t *buf;
+ u_int n;
{
- u_int i;
- u_int32_t w;
-
- w = (input << random_state.input_rotate) |
- (input >> (32 - random_state.input_rotate));
- i = random_state.add_ptr = (random_state.add_ptr - 1) & (POOLWORDS-1);
- if (i)
- random_state.input_rotate =
- (random_state.input_rotate + 7) & 31;
- else
- /*
- * At the beginning of the pool, add an extra 7 bits
- * rotation, so that successive passes spread the
- * input bits across the pool evenly.
- */
- random_state.input_rotate =
- (random_state.input_rotate + 14) & 31;
-
- /* XOR in the various taps */
- w ^= random_state.pool[(i+TAP1)&(POOLWORDS-1)];
- w ^= random_state.pool[(i+TAP2)&(POOLWORDS-1)];
- w ^= random_state.pool[(i+TAP3)&(POOLWORDS-1)];
- w ^= random_state.pool[(i+TAP4)&(POOLWORDS-1)];
- w ^= random_state.pool[(i+TAP5)&(POOLWORDS-1)];
- w ^= random_state.pool[i];
- /* Rotate w left 1 bit (stolen from SHA) and store */
- random_state.pool[i] = (w << 1) | (w >> 31);
+ for (; n--; buf++) {
+
+ register u_int32_t w = (*buf << random_state.input_rotate) |
+ (*buf >> (32 - random_state.input_rotate));
+ register u_int i = random_state.add_ptr =
+ (random_state.add_ptr - 1) & (POOLWORDS-1);
+
+ if (i)
+ random_state.input_rotate =
+ (random_state.input_rotate + 7) & 31;
+ else
+ /*
+ * At the beginning of the pool, add an extra 7 bits
+ * rotation, so that successive passes spread the
+ * input bits across the pool evenly.
+ */
+ random_state.input_rotate =
+ (random_state.input_rotate + 14) & 31;
+
+ /* XOR in the various taps */
+ w ^= random_state.pool[(i+TAP1)&(POOLWORDS-1)];
+ w ^= random_state.pool[(i+TAP2)&(POOLWORDS-1)];
+ w ^= random_state.pool[(i+TAP3)&(POOLWORDS-1)];
+ w ^= random_state.pool[(i+TAP4)&(POOLWORDS-1)];
+ w ^= random_state.pool[(i+TAP5)&(POOLWORDS-1)];
+ w ^= random_state.pool[i];
+ /* Rotate w left 1 bit (stolen from SHA) and store */
+ random_state.pool[i] = (w << 1) | (w >> 31);
+ }
}
/*
* are used for a high-resolution timer.
*
*/
-static void
+void
enqueue_randomness(state, val)
- register struct timer_rand_state *state;
- u_int val;
+ int state, val;
{
- struct timeval tv;
+ register struct timer_rand_state *p;
register struct rand_event *rep;
+ struct timeval tv;
int s;
u_int time, nbits;
- rndstats.rnd_enqs++;
+#ifdef DIAGNOSTIC
+ if (state < 0 || RND_SRC_NUM <= state)
+ return;
+#endif
+
+ p = &rnd_states[state];
+ val += state << 13;
microtime(&tv);
time = tv.tv_usec ^ tv.tv_sec;
* added. We take into account the first and second order
* deltas in order to make our estimate.
*/
- if (!state->dont_count_entropy) {
+ if (!p->dont_count_entropy) {
register int delta, delta2;
- delta = time - state->last_time;
- delta2 = delta - state->last_delta;
+ delta = time - p->last_time;
+ delta2 = delta - p->last_delta;
if (delta < 0) delta = -delta;
if (delta2 < 0) delta2 = -delta2;
- delta2 = delta = MIN(delta, delta2) >> 1;
+ if (delta > delta2) delta = delta2;
+ delta2 = delta >>= 1;
if (delta & 0xffff0000) {
nbits = 16;
if (delta & 1)
nbits++;
- rndstats.rnd_ed[nbits]++;
-
- if (rndstats.rnd_queued > QEVSLOW && nbits > QEVSBITS) {
+ /*
+ * the logic is to drop low-entropy entries,
+ * in hope for dequeuing to be more sourcefull
+ */
+ if (random_state.queued > QEVSLOW && nbits < QEVSBITS) {
rndstats.rnd_drople++;
return;
}
- state->last_time = time;
- state->last_delta = delta2;
- }
+ p->last_time = time;
+ p->last_delta = delta2;
+ } else if (p->max_entropy)
+ nbits = 8 * sizeof(val) - 1;
s = splhigh();
- if ((rep = event_free) == NULL) {
- splx(s);
+ if ((rep = rnd_event_free) == NULL) {
rndstats.rnd_drops++;
+ splx(s);
return;
}
- event_free = rep->re_next;
+ rnd_event_free = rep->re_next;
- rep->re_state = state;
+ rep->re_state = p;
rep->re_nbits = nbits;
rep->re_time = time;
rep->re_val = val;
- rep->re_next = event_q;
- event_q = rep;
- rep = rep->re_next;
- splx(s);
- rndstats.rnd_timer++;
- rndstats.rnd_queued++;
+ rep->re_next = rnd_event_q;
+ rnd_event_q = rep;
- if (rep == NULL)
- timeout(dequeue_randomness, NULL, 1);
+ rndstats.rnd_enqs++;
+ rndstats.rnd_ed[nbits]++;
+ rndstats.rnd_sc[state]++;
+ rndstats.rnd_sb[state] += nbits;
+ if (++random_state.queued > QEVSLOW/2 && !random_state.tmo) {
+ timeout_add(&rnd_timeout, 1);
+ random_state.tmo++;
+ }
+ splx(s);
}
void
void *v;
{
register struct rand_event *rep;
- register u_int32_t val, time;
+ u_int32_t val, time;
u_int nbits;
int s;
+ timeout_del(&rnd_timeout); /* XXX just in case */
rndstats.rnd_deqs++;
do {
s = splhigh();
- if (event_q == NULL) {
+ if (rnd_event_q == NULL) {
splx(s);
- return;
+ break;
}
- rep = event_q;
- event_q = rep->re_next;
+ rep = rnd_event_q;
+ rnd_event_q = rep->re_next;
+ random_state.queued--;
+
val = rep->re_val;
time = rep->re_time;
nbits = rep->re_nbits;
- rep->re_next = event_free;
- event_free = rep;
+
+ rep->re_next = rnd_event_free;
+ rnd_event_free = rep;
splx(s);
/* Prevent overflow */
if ((random_state.entropy_count + nbits) > POOLBITS &&
- arc4_state.cnt > 253)
+ arc4random_state.cnt > 253)
arc4_stir();
- add_entropy_word(val);
- add_entropy_word(time);
+ add_entropy_words(&val, 1);
+ add_entropy_words(&time, 1);
random_state.entropy_count += nbits;
rndstats.rnd_total += nbits;
if (random_state.entropy_count > POOLBITS)
random_state.entropy_count = POOLBITS;
- rndstats.rnd_queued--;
if (random_state.entropy_count > 8 &&
- rndstats.rnd_asleep != 0) {
+ random_state.asleep != 0) {
#ifdef RNDEBUG
if (rnd_debug & RD_WAIT)
- printf("rnd: wakeup[%d]{%u}\n",
- rndstats.rnd_asleep,
- random_state.entropy_count);
+ printf("rnd: wakeup[%u]{%u}\n",
+ random_state.asleep,
+ random_state.entropy_count);
#endif
- rndstats.rnd_asleep--;
- wakeup(&rndstats.rnd_asleep);
+ random_state.asleep--;
+ wakeup(&random_state.asleep);
}
} while(1);
-}
-
-void
-add_mouse_randomness(mouse_data)
- u_int32_t mouse_data;
-{
- /* Has randomattach run yet? */
- if (!rnd_attached)
- return;
-
- rndstats.rnd_mouse++;
- enqueue_randomness(&mouse_timer_state, mouse_data);
-}
-
-void
-add_net_randomness(isr)
- int isr;
-{
- /* Has randomattach run yet? */
- if (!rnd_attached)
- return;
-
- rndstats.rnd_net++;
- enqueue_randomness(&net_timer_state, ENT_NET + isr);
-}
-
-void
-add_disk_randomness(n)
- u_int32_t n;
-{
- u_int8_t c;
-
- /* Has randomattach run yet? */
- if (!rnd_attached)
- return;
-
- rndstats.rnd_disk++;
- c = n & 0xff;
- n >>= 8;
- c ^= n & 0xff;
- n >>= 8;
- c ^= n & 0xff;
- n >>= 8;
- c ^= n & 0xff;
- enqueue_randomness(&disk_timer_state, ENT_DISK + c);
-}
-
-void
-add_tty_randomness(c)
- int c;
-{
- /* Has randomattach run yet? */
- if (!rnd_attached)
- return;
-
- rndstats.rnd_tty++;
- enqueue_randomness(&tty_timer_state, ENT_TTY + c);
+ random_state.tmo = 0;
}
#if POOLWORDS % 16
#error extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
#endif
+
/*
* This function extracts randomness from the "entropy pool", and
* returns it in a buffer. This function computes how many remaining
register u_int8_t *buf;
int nbytes;
{
- int i;
MD5_CTX tmp;
+ u_char buffer[16];
- enqueue_randomness(&extract_timer_state, nbytes);
+ add_timer_randomness(nbytes);
/* Redundant, but just in case... */
- if (random_state.entropy_count > POOLBITS)
+ if (random_state.entropy_count > POOLBITS)
random_state.entropy_count = POOLBITS;
- if (random_state.entropy_count / 8 >= nbytes)
+ if (random_state.entropy_count / 8 > nbytes)
random_state.entropy_count -= nbytes*8;
else
random_state.entropy_count = 0;
while (nbytes) {
- /* Hash the pool to get the output */
- MD5Init(&tmp);
+ register u_char *p = buf;
+ register int i = sizeof(buffer);
- for (i = 0; i < POOLWORDS; i += 16)
- MD5Update(&tmp, (u_int8_t*)random_state.pool+i, 16);
+ if (i > nbytes) {
+ i = nbytes;
+ p = buffer;
+ }
- /* Modify pool so next hash will produce different results */
- for (i = 0; i < sizeof(tmp.buffer)/sizeof(tmp.buffer[0]); i++)
- add_entropy_word(tmp.buffer[i]);
- /*
- * Run the MD5 Transform one more time, since we want
- * to add at least minimal obscuring of the inputs to
- * add_entropy_word(). --- TYT
- */
- MD5Update(&tmp, (u_int8_t*)random_state.pool, 16);
+ /* Hash the pool to get the output */
+ MD5Init(&tmp);
+ MD5Update(&tmp, (u_int8_t*)random_state.pool,
+ sizeof(random_state.pool));
+ MD5Final(p, &tmp);
/*
* In case the hash function has some recognizable
* output pattern, we fold it in half.
*/
- {
- register u_int8_t *cp, *dp;
- cp = (u_int8_t *) &tmp.buffer;
- dp = cp + sizeof(tmp.buffer) - 1;
- while (cp < dp)
- *cp++ ^= *dp--;
- }
+ p[0] ^= p[15];
+ p[1] ^= p[14];
+ p[2] ^= p[13];
+ p[3] ^= p[12];
+ p[4] ^= p[11];
+ p[5] ^= p[10];
+ p[6] ^= p[ 9];
+ p[7] ^= p[ 8];
+
+ /* Modify pool so next hash will produce different results */
+ add_entropy_words((u_int32_t *)p, sizeof(buffer)/4);
/* Copy data to destination buffer */
- i = MIN(nbytes, sizeof(tmp.buffer));
- bcopy((caddr_t)&tmp.buffer, buf, i);
+ if (i < sizeof(buffer))
+ bcopy(buffer, buf, i);
nbytes -= i;
buf += i;
- enqueue_randomness(&extract_timer_state, nbytes);
+ add_timer_randomness(nbytes);
}
/* Wipe data from memory */
bzero(&tmp, sizeof(tmp));
+ bzero(&buffer, sizeof(buffer));
}
/*
{
int ret = 0;
int s, i;
-
+
if (uio->uio_resid == 0)
return 0;
ret = EIO; /* no chip -- error */
break;
case RND_SRND:
- if (random_state.entropy_count < 8) {
+ if (random_state.entropy_count < 16 * 8) {
if (ioflag & IO_NDELAY) {
ret = EWOULDBLOCK;
break;
}
#ifdef RNDEBUG
if (rnd_debug & RD_WAIT)
- printf("rnd: sleep[%d]\n",
- rndstats.rnd_asleep);
+ printf("rnd: sleep[%u]\n",
+ random_state.asleep);
#endif
- rndstats.rnd_asleep++;
+ random_state.asleep++;
rndstats.rnd_waits++;
- ret = tsleep(&rndstats.rnd_asleep,
- PWAIT | PCATCH, "rndrd", 0);
+ ret = tsleep(&random_state.asleep,
+ PWAIT | PCATCH, "rndrd", 0);
#ifdef RNDEBUG
if (rnd_debug & RD_WAIT)
printf("rnd: awakened(%d)\n", ret);
if (ret)
break;
}
- n = min(n, random_state.entropy_count / 8);
+ if (n > random_state.entropy_count / 8)
+ n = random_state.entropy_count / 8;
rndstats.rnd_reads++;
#ifdef RNDEBUG
if (rnd_debug & RD_OUTPUT)
printf("rnd: %u bytes for output\n", n);
#endif
break;
- case RND_PRND:
+ case RND_PRND:
i = (n + 3) / 4;
while (i--)
buf[i] = random();
break;
case RND_ARND:
- {
+ {
u_int8_t *cp = (u_int8_t *) buf;
u_int8_t *end = cp + n;
while (cp < end)
*cp++ = arc4random_8();
break;
- }
+ }
+ default:
+ ret = ENXIO;
}
splx(s);
if (n != 0 && ret == 0)
ret = uiomove((caddr_t)buf, n, uio);
if (!ret) {
- int i;
while (n % sizeof(u_int32_t))
((u_int8_t *) buf)[n++] = 0;
- n >>= 2;
- for (i = 0; i < n; i++)
- add_entropy_word(buf[i]);
+ add_entropy_words(buf, n / 4);
}
}
if (minor(dev) == RND_ARND && !ret)
- arc4random_uninitialized = 2;
+ arc4random_initialized = 0;
return ret;
}
int flag;
struct proc *p;
{
- int ret;
+ int ret = 0;
u_int cnt;
+ add_timer_randomness((int)p);
+
switch (cmd) {
case RNDGETENTCNT:
ret = copyout(&random_state.entropy_count, data,
break;
case RNDADDTOENTCNT:
if (suser(p->p_ucred, &p->p_acflag) != 0)
- return EPERM;
- copyin(&cnt, data, sizeof(cnt));
- random_state.entropy_count += cnt;
- if (random_state.entropy_count > POOLBITS)
- random_state.entropy_count = POOLBITS;
- ret = 0;
+ ret = EPERM;
+ else {
+ copyin(&cnt, data, sizeof(cnt));
+ random_state.entropy_count += cnt;
+ if (random_state.entropy_count > POOLBITS)
+ random_state.entropy_count = POOLBITS;
+ }
break;
case RNDZAPENTCNT:
if (suser(p->p_ucred, &p->p_acflag) != 0)
- return EPERM;
- random_state.entropy_count = 0;
- ret = 0;
+ ret = EPERM;
+ else
+ random_state.entropy_count = 0;
break;
case RNDSTIRARC4:
if (suser(p->p_ucred, &p->p_acflag) != 0)
- return EPERM;
- if (random_state.entropy_count < 64)
- return EAGAIN;
- arc4random_uninitialized = 2;
- ret = 0;
+ ret = EPERM;
+ else if (random_state.entropy_count < 64)
+ ret = EAGAIN;
+ else
+ arc4random_initialized = 0;
+ break;
+ case RNDCLRSTATS:
+ if (suser(p->p_ucred, &p->p_acflag) != 0)
+ ret = EPERM;
+ else
+ bzero(&rndstats, sizeof(rndstats));
break;
default:
ret = EINVAL;
}
+ add_timer_randomness((int)p);
return ret;
}