From: mickey Date: Mon, 10 Apr 2000 19:44:38 +0000 (+0000) Subject: add support for more random sources. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=452ba1bcddee35c1fe52f4e34ec0cc61fd3425c2;p=openbsd add support for more random sources. make it easy to add more in the future. make extract_entropy() faster. more accurate stats. --- diff --git a/sys/dev/rnd.c b/sys/dev/rnd.c index 40999f7cb82..f1ea0df2ebd 100644 --- a/sys/dev/rnd.c +++ b/sys/dev/rnd.c @@ -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 @@ -21,13 +21,13 @@ * 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 @@ -96,25 +96,25 @@ * 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, @@ -123,33 +123,49 @@ * * 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. @@ -158,27 +174,27 @@ * 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 @@ -196,9 +212,9 @@ * /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: * ================= * @@ -211,15 +227,15 @@ * 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. @@ -237,6 +253,7 @@ #include #include #include +#include #include #include @@ -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; } diff --git a/sys/dev/rndioctl.h b/sys/dev/rndioctl.h index 97f358452c4..addbdad7ca3 100644 --- a/sys/dev/rndioctl.h +++ b/sys/dev/rndioctl.h @@ -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__ */ diff --git a/sys/dev/rndvar.h b/sys/dev/rndvar.h index 0d209a84a65..0ba2e64d5f4 100644 --- a/sys/dev/rndvar.h +++ b/sys/dev/rndvar.h @@ -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 */ @@ -46,42 +46,51 @@ #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 */