add support for more random sources.
authormickey <mickey@openbsd.org>
Mon, 10 Apr 2000 19:44:38 +0000 (19:44 +0000)
committermickey <mickey@openbsd.org>
Mon, 10 Apr 2000 19:44:38 +0000 (19:44 +0000)
make it easy to add more in the future.
make extract_entropy() faster.
more accurate stats.

sys/dev/rnd.c
sys/dev/rndioctl.h
sys/dev/rndvar.h

index 40999f7..f1ea0df 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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
@@ -6,7 +6,7 @@
  * 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
@@ -42,8 +42,8 @@
  */
 
 /*
- * (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
@@ -53,7 +53,7 @@
  *
  * 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
@@ -64,7 +64,7 @@
  * 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
@@ -76,7 +76,7 @@
  * 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
@@ -88,7 +88,7 @@
  * 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>
@@ -270,10 +287,10 @@ int       rnd_debug = 0x0000;
 #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 {
@@ -281,13 +298,17 @@ 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 {
@@ -305,36 +326,26 @@ struct rand_event {
        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
@@ -355,51 +366,72 @@ static __inline u_int8_t arc4_getbyte __P((void));
  */
 
 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)
 {
@@ -410,21 +442,9 @@ 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
@@ -443,18 +463,21 @@ randomattach(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;
 }
 
@@ -485,7 +508,7 @@ randomclose(dev, flag, mode, p)
  * 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
@@ -493,36 +516,39 @@ randomclose(dev, flag, mode, p)
  * 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);
+       }
 }
 
 /*
@@ -537,17 +563,23 @@ add_entropy_word(input)
  * 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;
@@ -558,14 +590,15 @@ enqueue_randomness(state, val)
         * 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;
@@ -590,39 +623,45 @@ enqueue_randomness(state, val)
                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
@@ -630,116 +669,64 @@ dequeue_randomness(v)
        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
@@ -751,59 +738,62 @@ extract_entropy(buf, nbytes)
        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));
 }
 
 /*
@@ -828,7 +818,7 @@ randomread(dev, uio, ioflag)
 {
        int     ret = 0;
        int     s, i;
-       
+
        if (uio->uio_resid == 0)
                return 0;
 
@@ -842,20 +832,20 @@ randomread(dev, uio, ioflag)
                        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);
@@ -863,7 +853,8 @@ randomread(dev, uio, ioflag)
                                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)
@@ -876,19 +867,21 @@ randomread(dev, uio, ioflag)
                                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)
@@ -932,17 +925,14 @@ randomwrite(dev, uio, flags)
 
                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;
 }
@@ -955,9 +945,11 @@ randomioctl(dev, cmd, data, flag, p)
        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,
@@ -965,29 +957,37 @@ randomioctl(dev, cmd, data, flag, p)
                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;
 }
index 97f3584..addbdad 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rndioctl.h,v 1.6 1997/06/24 02:45:01 mickey Exp $     */
+/*     $OpenBSD: rndioctl.h,v 1.7 2000/04/10 19:44:39 mickey Exp $     */
 
 /*
  * Copyright (c) 1996 Michael Shalayeff.
@@ -52,6 +52,7 @@ struct rnd_pool_info {
 #define RNDADDENTROPY  _IOW('R', 3, sizeof(u_int))
 #define RNDZAPENTCNT   _IO( 'R', 4)
 #define RNDSTIRARC4    _IO( 'R', 5)
+#define RNDCLRSTATS    _IO( 'R', 6)
 
 
 #endif /* __RNDIOCTL_H__ */
index 0d209a8..0ba2e64 100644 (file)
@@ -1,8 +1,8 @@
-/*     $OpenBSD: rndvar.h,v 1.12 2000/03/19 17:38:03 mickey Exp $      */
+/*     $OpenBSD: rndvar.h,v 1.13 2000/04/10 19:44:39 mickey Exp $      */
 
 /*
  * Copyright (c) 1996 Michael Shalayeff.
- * 
+ *
  * This software derived from one contributed by Theodore Ts'o.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -37,7 +37,7 @@
 #ifndef __RNDVAR_H__
 #define __RNDVAR_H__
 
-#define POOLWORDS 128    /* Power of 2 - note that this is 32-bit words */
+#define POOLWORDS 64   /* Power of 2 - note that this is 32-bit words */
 
 #define        RND_RND         0       /* real randomness like nuclear chips */
 #define        RND_SRND        1       /* strong random source */
 #define RND_ARND       4       /* aRC4 based random number generator */
 #define RND_NODEV      5       /* First invalid minor device number */
 
-struct rndstats {
-       u_long rnd_total; /* total bits of entropy generated */
-       u_long rnd_used;  /* strong data bits read so far */
-       u_long arc4_reads;/* aRC4 data bytes read so far */
+#define        RND_SRC_TRUE    0
+#define        RND_SRC_TIMER   1
+#define        RND_SRC_MOUSE   2
+#define        RND_SRC_TTY     3
+#define        RND_SRC_DISK    4
+#define        RND_SRC_NET     5
+#define        RND_SRC_AUDIO   6
+#define        RND_SRC_NUM     8
 
-       u_long rnd_timer; /* timer calls */
-       u_long rnd_mouse; /* mouse calls */
-       u_long rnd_tty;   /* tty calls */
-       u_long rnd_disk;  /* block devices calls */
-       u_long rnd_net;   /* net calls */
+struct rndstats {
+       quad_t rnd_total;       /* total bits of entropy generated */
+       quad_t rnd_used;        /* strong data bits read so far */
+       quad_t rnd_reads;       /* strong read calls */
+       quad_t arc4_reads;      /* aRC4 data bytes read so far */
+       quad_t arc4_nstirs;     /* arc4 pool stirs */
+       quad_t arc4_stirs;      /* arc4 pool stirs (bits used) */
 
-       u_long rnd_reads; /* strong read calls */
-       u_long rnd_waits; /* sleep for data */
-       u_long rnd_enqs;  /* enqueue calls */
-       u_long rnd_deqs;  /* dequeue calls */
-       u_long rnd_drops; /* queue-full drops */
-       u_long rnd_drople;/* queue low watermark low entropy drops */
+       quad_t rnd_pad[5];
 
-       u_long rnd_asleep;/* sleeping for the data */
-       u_long rnd_queued;/* queued for processing */
-       u_long arc4_stirs;/* arc4 pool stirs */
+       quad_t rnd_waits;       /* sleeps for data */
+       quad_t rnd_enqs;        /* enqueue calls */
+       quad_t rnd_deqs;        /* dequeue calls */
+       quad_t rnd_drops;       /* queue-full drops */
+       quad_t rnd_drople;      /* queue low watermark low entropy drops */
 
-       u_long rnd_ed[32];/* entropy feed distribution */
+       quad_t rnd_ed[32];      /* entropy feed distribution */
+       quad_t rnd_sc[RND_SRC_NUM]; /* add* calls */
+       quad_t rnd_sb[RND_SRC_NUM]; /* add* bits */
 };
 
 #ifdef _KERNEL
 extern struct rndstats rndstats;
 
-extern void add_mouse_randomness __P((u_int32_t));
-extern void add_net_randomness __P((int));
-extern void add_disk_randomness __P((u_int32_t));
-extern void add_tty_randomness __P((int));
+#define        add_true_randomness(d)  enqueue_randomness(RND_SRC_TRUE,  (int)(d))
+#define        add_timer_randomness(d) enqueue_randomness(RND_SRC_TIMER, (int)(d))
+#define        add_mouse_randomness(d) enqueue_randomness(RND_SRC_MOUSE, (int)(d))
+#define        add_tty_randomness(d)   enqueue_randomness(RND_SRC_TTY,   (int)(d))
+#define        add_disk_randomness(d)  enqueue_randomness(RND_SRC_DISK,  (int)(d))
+#define        add_net_randomness(d)   enqueue_randomness(RND_SRC_NET,   (int)(d))
+#define        add_audio_randomness(d) enqueue_randomness(RND_SRC_AUDIO, (int)(d))
 
-extern void get_random_bytes __P((void *, size_t));
-extern u_int32_t arc4random __P((void));
-extern int arc4random_8 __P((void));
+void enqueue_randomness __P((int, int));
+void get_random_bytes __P((void *, size_t));
+u_int32_t arc4random __P((void));
+int arc4random_8 __P((void));
 
 #endif /* _KERNEL */