patch-2.3.16 linux/drivers/char/random.c
Next file: linux/drivers/char/riscom8.c
Previous file: linux/drivers/char/radio-terratec.c
Back to the patch index
Back to the overall index
- Lines: 1622
- Date:
Tue Aug 31 12:37:39 1999
- Orig file:
v2.3.15/linux/drivers/char/random.c
- Orig date:
Thu Aug 26 13:05:35 1999
diff -u --recursive --new-file v2.3.15/linux/drivers/char/random.c linux/drivers/char/random.c
@@ -1,10 +1,10 @@
/*
* random.c -- A strong random number generator
*
- * Version 1.04, last modified 26-Apr-98
+ * Version 1.88, last modified 30-Aug-99
*
- * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998. All rights
- * reserved.
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997, 1998, 1999. All
+ * rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,15 +27,16 @@
*
* 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
- * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
+ * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
*/
/*
@@ -223,8 +224,8 @@
* The code for SHA transform was taken from Peter Gutmann's
* implementation, which has been placed in the public domain.
* 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
+ * 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
@@ -232,11 +233,6 @@
* Eastlake, Steve Crocker, and Jeff Schiller.
*/
-/*
- * Added a check for signal pending in the extract_entropy() loop to allow
- * the read(2) syscall to be interrupted. Copyright (C) 1998 Andrea Arcangeli
- */
-
#include <linux/utsname.h>
#include <linux/config.h>
#include <linux/kernel.h>
@@ -256,72 +252,77 @@
/*
* Configuration information
*/
-#undef RANDOM_BENCHMARK
-#undef BENCHMARK_NOINT
-#define ROTATE_PARANOIA
-
-#define POOLWORDS 128 /* Power of 2 - note that this is 32-bit words */
-#define POOLBITS (POOLWORDS*32)
-/*
- * The pool is stirred with a primitive polynomial of degree POOLWORDS
- * over GF(2). The taps for various sizes are defined below. They are
- * chosen to be evenly spaced (minimum RMS distance from evenly spaced;
- * the numbers in the comments are a scaled squared error sum) except
- * for the last tap, which is 1 to get the twisting happening as fast
- * as possible.
- */
-#if POOLWORDS == 2048 /* 115 x^2048+x^1638+x^1231+x^819+x^411+x^1+1 */
-#define TAP1 1638
-#define TAP2 1231
-#define TAP3 819
-#define TAP4 411
-#define TAP5 1
-#elif POOLWORDS == 1024 /* 290 x^1024+x^817+x^615+x^412+x^204+x^1+1 */
-/* Alt: 115 x^1024+x^819+x^616+x^410+x^207+x^2+1 */
-#define TAP1 817
-#define TAP2 615
-#define TAP3 412
-#define TAP4 204
-#define TAP5 1
-#elif POOLWORDS == 512 /* 225 x^512+x^411+x^308+x^208+x^104+x+1 */
-/* Alt: 95 x^512+x^409+x^307+x^206+x^102+x^2+1
- * 95 x^512+x^409+x^309+x^205+x^103+x^2+1 */
-#define TAP1 411
-#define TAP2 308
-#define TAP3 208
-#define TAP4 104
-#define TAP5 1
-#elif POOLWORDS == 256 /* 125 x^256+x^205+x^155+x^101+x^52+x+1 */
-#define TAP1 205
-#define TAP2 155
-#define TAP3 101
-#define TAP4 52
-#define TAP5 1
-#elif POOLWORDS == 128 /* 105 x^128+x^103+x^76+x^51+x^25+x+1 */
-/* Alt: 70 x^128+x^103+x^78+x^51+x^27+x^2+1 */
-#define TAP1 103
-#define TAP2 76
-#define TAP3 51
-#define TAP4 25
-#define TAP5 1
-#elif POOLWORDS == 64 /* 15 x^64+x^52+x^39+x^26+x^14+x+1 */
-#define TAP1 52
-#define TAP2 39
-#define TAP3 26
-#define TAP4 14
-#define TAP5 1
-#elif POOLWORDS == 32 /* 15 x^32+x^26+x^20+x^14+x^7+x^1+1 */
-#define TAP1 26
-#define TAP2 20
-#define TAP3 14
-#define TAP4 7
-#define TAP5 1
-#elif POOLWORDS & (POOLWORDS-1)
-#error POOLWORDS must be a power of 2
-#else
-#error No primitive polynomial available for chosen POOLWORDS
+#define DEFAULT_POOL_SIZE 512
+#define SECONDARY_POOL_SIZE 128
+#define BATCH_ENTROPY_SIZE 256
+#define USE_SHA
+
+/*
+ * The minimum number of bits of entropy before we wake up a read on
+ * /dev/random. Should always be at least 8, or at least 1 byte.
+ */
+static int random_read_wakeup_thresh = 8;
+
+/*
+ * If the entropy count falls under this number of bits, then we
+ * should wake up processes which are selecting or polling on write
+ * access to /dev/random.
+ */
+static int random_write_wakeup_thresh = 128;
+
+/*
+ * A pool of size POOLWORDS is stirred with a primitive polynomial
+ * of degree POOLWORDS over GF(2). The taps for various sizes are
+ * defined below. They are chosen to be evenly spaced (minimum RMS
+ * distance from evenly spaced; the numbers in the comments are a
+ * scaled squared error sum) except for the last tap, which is 1 to
+ * get the twisting happening as fast as possible.
+ */
+static struct poolinfo {
+ int poolwords;
+ int tap1, tap2, tap3, tap4, tap5;
+} poolinfo_table[] = {
+ /* x^2048 + x^1638 + x^1231 + x^819 + x^411 + x + 1 -- 115 */
+ { 2048, 1638, 1231, 819, 411, 1 },
+
+ /* x^1024 + x^817 + x^615 + x^412 + x^204 + x + 1 -- 290 */
+ { 1024, 817, 615, 412, 204, 1 },
+
+#if 0 /* Alternate polynomial */
+ /* x^1024 + x^819 + x^616 + x^410 + x^207 + x^2 + 1 -- 115 */
+ { 1024, 819, 616, 410, 207, 2 },
+#endif
+
+ /* x^512 + x^411 + x^308 + x^208 + x^104 + x + 1 -- 225 */
+ { 512, 411, 308, 208, 104, 1 },
+
+#if 0 /* Alternates */
+ /* x^512 + x^409 + x^307 + x^206 + x^102 + x^2 + 1 -- 95 */
+ { 512, 409, 307, 206, 102, 2 },
+ /* x^512 + x^409 + x^309 + x^205 + x^103 + x^2 + 1 -- 95 */
+ { 512, 409, 309, 205, 103, 2 },
+#endif
+
+ /* x^256 + x^205 + x^155 + x^101 + x^52 + x + 1 -- 125 */
+ { 256, 205, 155, 101, 52, 1 },
+
+ /* x^128 + x^103 + x^76 + x^51 +x^25 + x + 1 -- 105 */
+ { 128, 103, 76, 51, 25, 1 },
+
+#if 0 /* Alternate polynomial */
+ /* x^128 + x^103 + x^78 + x^51 + x^27 + x^2 + 1 -- 70 */
+ { 128, 103, 78, 51, 27, 2 },
#endif
+ /* x^64 + x^52 + x^39 + x^26 + x^14 + x + 1 -- 15 */
+ { 64, 52, 39, 26, 14, 1 },
+
+ /* x^32 + x^26 + x^20 + x^14 + x^7 + x + 1 -- 15 */
+ { 32, 26, 20, 14, 7, 1 },
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
/*
* For the purposes of better mixing, we use the CRC-32 polynomial as
* well to make a twisted Generalized Feedback Shift Reigster
@@ -332,25 +333,26 @@
* II. ACM Transactions on Mdeling and Computer Simulation 4:254-266)
*
* Thanks to Colin Plumb for suggesting this.
+ *
* We have not analyzed the resultant polynomial to prove it primitive;
* in fact it almost certainly isn't. Nonetheless, the irreducible factors
* of a random large-degree polynomial over GF(2) are more than large enough
* that periodicity is not a concern.
- *
- * The input hash is much less sensitive than the output hash. All that
- * we want of it is that it be a good non-cryptographic hash; i.e. it
- * not produce collisions when fed "random" data of the sort we expect
- * to see. As long as the pool state differs for different inputs, we
- * have preserved the input entropy and done a good job. The fact that an
- * intelligent attacker can construct inputs that will produce controlled
- * alterations to the pool's state is not important because we don't
- * consider such inputs to contribute any randomness.
- * The only property we need with respect to them is
- * that the attacker can't increase his/her knowledge of the pool's state.
+ *
+ * The input hash is much less sensitive than the output hash. All
+ * that we want of it is that it be a good non-cryptographic hash;
+ * i.e. it not produce collisions when fed "random" data of the sort
+ * we expect to see. As long as the pool state differs for different
+ * inputs, we have preserved the input entropy and done a good job.
+ * The fact that an intelligent attacker can construct inputs that
+ * will produce controlled alterations to the pool's state is not
+ * important because we don't consider such inputs to contribute any
+ * randomness. The only property we need with respect to them is that
+ * the attacker can't increase his/her knowledge of the pool's state.
* Since all additions are reversible (knowing the final state and the
* input, you can reconstruct the initial state), if an attacker has
- * any uncertainty about the initial state, he/she can only shuffle that
- * uncertainty about, but never cause any collisions (which would
+ * any uncertainty about the initial state, he/she can only shuffle
+ * that uncertainty about, but never cause any collisions (which would
* decrease the uncertainty).
*
* The chosen system lets the state of the pool be (essentially) the input
@@ -365,82 +367,36 @@
*/
/*
- * The minimum number of bits to release a "wait on input". Should
- * probably always be 8, since a /dev/random read can return a single
- * byte.
- */
-#define WAIT_INPUT_BITS 8
-/*
- * The limit number of bits under which to release a "wait on
- * output". Should probably always be the same as WAIT_INPUT_BITS, so
- * that an output wait releases when and only when a wait on input
- * would block.
- */
-#define WAIT_OUTPUT_BITS WAIT_INPUT_BITS
-
-/* There is actually only one of these, globally. */
-struct random_bucket {
- unsigned add_ptr;
- unsigned entropy_count;
-#ifdef ROTATE_PARANOIA
- int input_rotate;
+ * Linux 2.2 compatibility
+ */
+#ifndef DECLARE_WAITQUEUE
+#define DECLARE_WAITQUEUE(WAIT, PTR) struct wait_queue WAIT = { PTR, NULL }
#endif
- __u32 pool[POOLWORDS];
-};
-
-#ifdef RANDOM_BENCHMARK
-/* For benchmarking only */
-struct random_benchmark {
- unsigned long long start_time;
- int times; /* # of samples */
- unsigned long min;
- unsigned long max;
- unsigned long accum; /* accumulator for average */
- const char *descr;
- int unit;
- unsigned long flags;
-};
-
-#define BENCHMARK_INTERVAL 500
-
-static void initialize_benchmark(struct random_benchmark *bench,
- const char *descr, int unit);
-static void begin_benchmark(struct random_benchmark *bench);
-static void end_benchmark(struct random_benchmark *bench);
-
-struct random_benchmark timer_benchmark;
+#ifndef DECLARE_WAIT_QUEUE_HEAD
+#define DECLARE_WAIT_QUEUE_HEAD(WAIT) struct wait_queue *WAIT
#endif
-/* There is one of these per entropy source */
-struct timer_rand_state {
- __u32 last_time;
- __s32 last_delta,last_delta2;
- int dont_count_entropy:1;
-};
-
-static struct random_bucket random_state;
-static struct timer_rand_state keyboard_timer_state;
-static struct timer_rand_state mouse_timer_state;
-static struct timer_rand_state extract_timer_state;
-static struct timer_rand_state *irq_timer_state[NR_IRQS];
-static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV];
+/*
+ * Static global variables
+ */
+static struct entropy_store *random_state; /* The default global store */
+static struct entropy_store *sec_random_state; /* secondary store */
static DECLARE_WAIT_QUEUE_HEAD(random_read_wait);
static DECLARE_WAIT_QUEUE_HEAD(random_write_wait);
-static ssize_t random_read(struct file * file, char * buf,
- size_t nbytes, loff_t *ppos);
-static ssize_t random_read_unlimited(struct file * file, char * buf,
- size_t nbytes, loff_t *ppos);
-static unsigned int random_poll(struct file *file, poll_table * wait);
-static ssize_t random_write(struct file * file, const char * buffer,
- size_t count, loff_t *ppos);
-static int random_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg);
-
-static inline void fast_add_entropy_words(struct random_bucket *r,
- __u32 x, __u32 y);
+/*
+ * Forward procedure declarations
+ */
+#ifdef CONFIG_SYSCTL
+static void sysctl_init_random(struct entropy_store *random_state);
+#endif
-static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y);
+/*****************************************************************
+ *
+ * Utility functions, with some ASM defined functions for speed
+ * purposes
+ *
+ *****************************************************************/
#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
@@ -504,185 +460,236 @@
}
#endif
-
-/*
- * Initialize the random pool with standard stuff.
+/**********************************************************************
*
- * NOTE: This is an OS-dependent function.
+ * OS independent entropy store. Here are the functions which handle
+ * storing entropy in an entropy pool.
+ *
+ **********************************************************************/
+
+struct entropy_store {
+ unsigned add_ptr;
+ int entropy_count;
+ int input_rotate;
+ int extract_count;
+ struct poolinfo poolinfo;
+ __u32 *pool;
+};
+
+/*
+ * Initialize the entropy store. The input argument is the size of
+ * the random pool.
+ *
+ * Returns an negative error if there is a problem.
*/
-static void init_std_data(struct random_bucket *r)
+static int create_entropy_store(int size, struct entropy_store **ret_bucket)
{
- __u32 words[2], *p;
- int i;
- struct timeval tv;
+ struct entropy_store *r;
+ struct poolinfo *p;
+ int poolwords;
- do_gettimeofday(&tv);
- add_entropy_words(r, tv.tv_sec, tv.tv_usec);
+ poolwords = (size + 3) / 4; /* Convert bytes->words */
+ /* The pool size must be a multiple of 16 32-bit words */
+ poolwords = ((poolwords + 15) / 16) * 16;
- /*
- * This doesnt lock system.utsname. Howeve we are generating
- * entropy so a race with a name set here is fine.
- */
- p = (__u32 *)&system_utsname;
- for (i = sizeof(system_utsname) / sizeof(words); i; i--) {
- memcpy(words, p, sizeof(words));
- add_entropy_words(r, words[0], words[1]);
- p += sizeof(words)/sizeof(*words);
+ for (p = poolinfo_table; p->poolwords; p++) {
+ if (poolwords == p->poolwords)
+ break;
}
-
+ if (p->poolwords == 0)
+ return -EINVAL;
+
+ r = kmalloc(sizeof(struct entropy_store), GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ memset (r, 0, sizeof(struct entropy_store));
+ r->poolinfo = *p;
+
+ r->pool = kmalloc(poolwords*4, GFP_KERNEL);
+ if (!r->pool) {
+ kfree_s(r, sizeof(struct entropy_store));
+ return -ENOMEM;
+ }
+ memset(r->pool, 0, poolwords*4);
+ *ret_bucket = r;
+ return 0;
}
/* Clear the entropy pool and associated counters. */
-static void rand_clear_pool(void)
+static void clear_entropy_store(struct entropy_store *r)
{
- memset(&random_state, 0, sizeof(random_state));
- init_std_data(&random_state);
+ r->add_ptr = 0;
+ r->entropy_count = 0;
+ r->input_rotate = 0;
+ r->extract_count = 0;
+ memset(r->pool, 0, r->poolinfo.poolwords*4);
}
-void __init rand_initialize(void)
+static void free_entropy_store(struct entropy_store *r)
{
- int i;
-
- rand_clear_pool();
- for (i = 0; i < NR_IRQS; i++)
- irq_timer_state[i] = NULL;
- for (i = 0; i < MAX_BLKDEV; i++)
- blkdev_timer_state[i] = NULL;
- memset(&keyboard_timer_state, 0, sizeof(struct timer_rand_state));
- memset(&mouse_timer_state, 0, sizeof(struct timer_rand_state));
- memset(&extract_timer_state, 0, sizeof(struct timer_rand_state));
-#ifdef RANDOM_BENCHMARK
- initialize_benchmark(&timer_benchmark, "timer", 0);
-#endif
- extract_timer_state.dont_count_entropy = 1;
+ if (r->pool)
+ kfree(r->pool);
+ kfree_s(r, sizeof(struct entropy_store));
}
-void rand_initialize_irq(int irq)
+/*
+ * This function adds a byte into the entropy "pool". It does not
+ * update the entropy estimate. The caller should call
+ * credit_entropy_store if this is appropriate.
+ *
+ * The pool is stirred with a primitive polynomial of the appropriate
+ * degree, and then twisted. We twist by three bits at a time because
+ * it's cheap to do so and helps slightly in the expected case where
+ * the entropy is concentrated in the low-order bits.
+ */
+static void add_entropy_words(struct entropy_store *r, const __u32 *in,
+ int num)
{
- struct timer_rand_state *state;
-
- if (irq >= NR_IRQS || irq_timer_state[irq])
- return;
-
- /*
- * If kmalloc returns null, we just won't use that entropy
- * source.
- */
- state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
- if (state) {
- irq_timer_state[irq] = state;
- memset(state, 0, sizeof(struct timer_rand_state));
+ static __u32 const twist_table[8] = {
+ 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
+ 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
+ unsigned i;
+ int new_rotate;
+ __u32 w;
+
+ while (num--) {
+ w = rotate_left(r->input_rotate, *in);
+ i = r->add_ptr = (r->add_ptr - 1) & (r->poolinfo.poolwords-1);
+ /*
+ * Normally, we add 7 bits of rotation to the pool.
+ * At the beginning of the pool, add an extra 7 bits
+ * rotation, so that successive passes spread the
+ * input bits across the pool evenly.
+ */
+ new_rotate = r->input_rotate + 14;
+ if (i)
+ new_rotate = r->input_rotate + 7;
+ r->input_rotate = new_rotate & 31;
+
+ /* XOR in the various taps */
+ w ^= r->pool[(i+r->poolinfo.tap1)&(r->poolinfo.poolwords-1)];
+ w ^= r->pool[(i+r->poolinfo.tap2)&(r->poolinfo.poolwords-1)];
+ w ^= r->pool[(i+r->poolinfo.tap3)&(r->poolinfo.poolwords-1)];
+ w ^= r->pool[(i+r->poolinfo.tap4)&(r->poolinfo.poolwords-1)];
+ w ^= r->pool[(i+r->poolinfo.tap5)&(r->poolinfo.poolwords-1)];
+ w ^= r->pool[i];
+ r->pool[i] = (w >> 3) ^ twist_table[w & 7];
}
}
-void rand_initialize_blkdev(int major, int mode)
+/*
+ * Credit (or debit) the entropy store with n bits of entropy
+ */
+static void credit_entropy_store(struct entropy_store *r, int num)
{
- struct timer_rand_state *state;
-
- if (major >= MAX_BLKDEV || blkdev_timer_state[major])
- return;
+ int max_entropy = r->poolinfo.poolwords*32;
- /*
- * If kmalloc returns null, we just won't use that entropy
- * source.
- */
- state = kmalloc(sizeof(struct timer_rand_state), mode);
- if (state) {
- blkdev_timer_state[major] = state;
- memset(state, 0, sizeof(struct timer_rand_state));
- }
+ if (r->entropy_count + num < 0)
+ r->entropy_count = 0;
+ else if (r->entropy_count + num > max_entropy)
+ r->entropy_count = max_entropy;
+ else
+ r->entropy_count = r->entropy_count + num;
}
-/*
- * This function adds a byte into the entropy "pool". It does not
- * update the entropy estimate. The caller must do this if appropriate.
+/**********************************************************************
*
- * This function is tuned for speed above most other considerations.
+ * Entropy batch input management
*
- * The pool is stirred with a primitive polynomial of the appropriate degree,
- * and then twisted. We twist by three bits at a time because it's
- * cheap to do so and helps slightly in the expected case where the
- * entropy is concentrated in the low-order bits.
- */
-#define MASK(x) ((x) & (POOLWORDS-1)) /* Convenient abreviation */
-static inline void fast_add_entropy_words(struct random_bucket *r,
- __u32 x, __u32 y)
-{
- static __u32 const twist_table[8] = {
- 0, 0x3b6e20c8, 0x76dc4190, 0x4db26158,
- 0xedb88320, 0xd6d6a3e8, 0x9b64c2b0, 0xa00ae278 };
- unsigned i, j;
+ * We batch entropy to be added to avoid increasing interrupt latency
+ *
+ **********************************************************************/
- i = MASK(r->add_ptr - 2); /* i is always even */
- r->add_ptr = i;
+static __u32 *batch_entropy_pool;
+static int *batch_entropy_credit;
+static int batch_max;
+static int batch_head, batch_tail;
+static struct tq_struct batch_tqueue;
+static void batch_entropy_process(void *private_);
-#ifdef ROTATE_PARANOIA
- j = r->input_rotate + 14;
- if (i)
- j -= 7;
- r->input_rotate = j & 31;
+/* note: the size must be a power of 2 */
+static int batch_entropy_init(int size, struct entropy_store *r)
+{
+ batch_entropy_pool = kmalloc(2*size*sizeof(__u32), GFP_KERNEL);
+ if (!batch_entropy_pool)
+ return -1;
+ batch_entropy_credit =kmalloc(size*sizeof(int), GFP_KERNEL);
+ if (!batch_entropy_credit) {
+ kfree(batch_entropy_pool);
+ return -1;
+ }
+ batch_head = batch_tail = 0;
+ batch_max = size;
+ batch_tqueue.routine = batch_entropy_process;
+ batch_tqueue.data = r;
+ return 0;
+}
- x = rotate_left(r->input_rotate, x);
- y = rotate_left(r->input_rotate, y);
-#endif
+static void batch_entropy_store(__u32 a, __u32 b, int num)
+{
+ int new;
- /*
- * XOR in the various taps. Even though logically, we compute
- * x and then compute y, we read in y then x order because most
- * caches work slightly better with increasing read addresses.
- * If a tap is even then we can use the fact that i is even to
- * avoid a masking operation. Every polynomial has at least one
- * even tap, so j is always used.
- * (Is there a nicer way to arrange this code?)
- */
-#if TAP1 & 1
- y ^= r->pool[MASK(i+TAP1)]; x ^= r->pool[MASK(i+TAP1+1)];
-#else
- j = MASK(i+TAP1); y ^= r->pool[j]; x ^= r->pool[j+1];
-#endif
-#if TAP2 & 1
- y ^= r->pool[MASK(i+TAP2)]; x ^= r->pool[MASK(i+TAP2+1)];
-#else
- j = MASK(i+TAP2); y ^= r->pool[j]; x ^= r->pool[j+1];
-#endif
-#if TAP3 & 1
- y ^= r->pool[MASK(i+TAP3)]; x ^= r->pool[MASK(i+TAP3+1)];
-#else
- j = MASK(i+TAP3); y ^= r->pool[j]; x ^= r->pool[j+1];
-#endif
-#if TAP4 & 1
- y ^= r->pool[MASK(i+TAP4)]; x ^= r->pool[MASK(i+TAP4+1)];
-#else
- j = MASK(i+TAP4); y ^= r->pool[j]; x ^= r->pool[j+1];
-#endif
-#if TAP5 == 1
- /* We need to pretend to write pool[i+1] before computing y */
- y ^= r->pool[i];
- x ^= r->pool[i+1];
- x ^= r->pool[MASK(i+TAP5+1)];
- y ^= r->pool[i+1] = x = (x >> 3) ^ twist_table[x & 7];
- r->pool[i] = (y >> 3) ^ twist_table[y & 7];
-#else
-# if TAP5 & 1
- y ^= r->pool[MASK(i+TAP5)]; x ^= r->pool[MASK(i+TAP5+1)];
-# else
- j = MASK(i+TAP5); y ^= r->pool[j]; x ^= r->pool[j+1];
-# endif
- y ^= r->pool[i];
- x ^= r->pool[i+1];
- r->pool[i] = (y >> 3) ^ twist_table[y & 7];
- r->pool[i+1] = (x >> 3) ^ twist_table[x & 7];
+ if (!batch_max)
+ return;
+
+ batch_entropy_pool[2*batch_head] = a;
+ batch_entropy_pool[(2*batch_head) + 1] = b;
+ batch_entropy_credit[batch_head] = num;
+
+ new = (batch_head+1) & (batch_max-1);
+ if (new != batch_tail) {
+ queue_task(&batch_tqueue, &tq_timer);
+ batch_head = new;
+ } else {
+#if 0
+ printk(KERN_NOTICE "random: batch entropy buffer full\n");
#endif
+ }
}
-/*
- * For places where we don't need the inlined version
- */
-static void add_entropy_words(struct random_bucket *r, __u32 x, __u32 y)
+static void batch_entropy_process(void *private_)
{
- fast_add_entropy_words(r, x, y);
+ int num = 0;
+ int max_entropy;
+ struct entropy_store *r = (struct entropy_store *) private_, *p;
+
+ if (!batch_max)
+ return;
+
+ max_entropy = r->poolinfo.poolwords*32;
+ while (batch_head != batch_tail) {
+ add_entropy_words(r, batch_entropy_pool + 2*batch_tail, 2);
+ p = r;
+ if (r->entropy_count > max_entropy && (num & 1))
+ r = sec_random_state;
+ credit_entropy_store(r, batch_entropy_credit[batch_tail]);
+ batch_tail = (batch_tail+1) & (batch_max-1);
+ num++;
+ }
+ if (r->entropy_count >= random_read_wakeup_thresh)
+ wake_up_interruptible(&random_read_wait);
}
+/*********************************************************************
+ *
+ * Entropy input management
+ *
+ *********************************************************************/
+
+/* There is one of these per entropy source */
+struct timer_rand_state {
+ __u32 last_time;
+ __s32 last_delta,last_delta2;
+ int dont_count_entropy:1;
+};
+
+static struct timer_rand_state keyboard_timer_state;
+static struct timer_rand_state mouse_timer_state;
+static struct timer_rand_state extract_timer_state;
+static struct timer_rand_state *irq_timer_state[NR_IRQS];
+static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV];
+
/*
* This function adds entropy to the entropy "pool" by using timing
* delays. It uses the timer_rand_state structure to make an estimate
@@ -695,15 +702,12 @@
* are used for a high-resolution timer.
*
*/
-static void add_timer_randomness(struct random_bucket *r,
- struct timer_rand_state *state, unsigned num)
+static void add_timer_randomness(struct timer_rand_state *state, unsigned num)
{
__u32 time;
__s32 delta, delta2, delta3;
+ int entropy = 0;
-#ifdef RANDOM_BENCHMARK
- begin_benchmark(&timer_benchmark);
-#endif
#if defined (__i386__)
if (boot_cpu_data.x86_capability & X86_FEATURE_TSC) {
__u32 high;
@@ -717,14 +721,12 @@
time = jiffies;
#endif
- fast_add_entropy_words(r, (__u32)num, time);
-
/*
* Calculate number of bits of randomness we probably added.
* We take into account the first, second and third-order deltas
* in order to make our estimate.
*/
- if ((r->entropy_count < POOLBITS) && !state->dont_count_entropy) {
+ if (!state->dont_count_entropy) {
delta = time - state->last_time;
state->last_time = time;
@@ -753,30 +755,19 @@
delta >>= 1;
delta &= (1 << 12) - 1;
- r->entropy_count += int_ln_12bits(delta);
-
- /* Prevent overflow */
- if (r->entropy_count > POOLBITS)
- r->entropy_count = POOLBITS;
-
- /* Wake up waiting processes, if we have enough entropy. */
- if (r->entropy_count >= WAIT_INPUT_BITS)
- wake_up_interruptible(&random_read_wait);
+ entropy = int_ln_12bits(delta);
}
-
-#ifdef RANDOM_BENCHMARK
- end_benchmark(&timer_benchmark);
-#endif
+ batch_entropy_store(num, time, entropy);
}
void add_keyboard_randomness(unsigned char scancode)
{
- add_timer_randomness(&random_state, &keyboard_timer_state, scancode);
+ add_timer_randomness(&keyboard_timer_state, scancode);
}
void add_mouse_randomness(__u32 mouse_data)
{
- add_timer_randomness(&random_state, &mouse_timer_state, mouse_data);
+ add_timer_randomness(&mouse_timer_state, mouse_data);
}
void add_interrupt_randomness(int irq)
@@ -784,7 +775,7 @@
if (irq >= NR_IRQS || irq_timer_state[irq] == 0)
return;
- add_timer_randomness(&random_state, irq_timer_state[irq], 0x100+irq);
+ add_timer_randomness(irq_timer_state[irq], 0x100+irq);
}
void add_blkdev_randomness(int major)
@@ -798,10 +789,15 @@
return;
}
- add_timer_randomness(&random_state, blkdev_timer_state[major],
- 0x200+major);
+ add_timer_randomness(blkdev_timer_state[major], 0x200+major);
}
+/******************************************************************
+ *
+ * Hash function definition
+ *
+ *******************************************************************/
+
/*
* This chunk of code defines a function
* void HASH_TRANSFORM(__u32 digest[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE],
@@ -821,12 +817,11 @@
* 3) 0x98badcfe
* 4) 0x10325476
* 5) 0xc3d2e1f0 (SHA only)
- *
+ *
* For /dev/random purposes, the length of the data being hashed is
- * fixed in length (at POOLWORDS words), so appending a bit count in
- * the usual way is not cryptographically necessary.
+ * fixed in length, so appending a bit count in the usual way is not
+ * cryptographically necessary.
*/
-#define USE_SHA
#ifdef USE_SHA
@@ -1074,9 +1069,6 @@
/*
* MD5 transform algorithm, taken from code written by Colin Plumb,
* and put into the public domain
- *
- * QUESTION: Replace this with SHA, which as generally received better
- * reviews from the cryptographic community?
*/
/* The four core functions - F1 is optimized somewhat */
@@ -1187,39 +1179,94 @@
#endif /* !USE_SHA */
+/*********************************************************************
+ *
+ * Entropy extraction routines
+ *
+ *********************************************************************/
+
+#define EXTRACT_ENTROPY_USER 1
+#define EXTRACT_ENTROPY_SECONDARY 2
+#define TMP_BUF_SIZE (HASH_BUFFER_SIZE + HASH_EXTRA_SIZE)
+#define SEC_XFER_SIZE (TMP_BUF_SIZE*4)
+
+static ssize_t extract_entropy(struct entropy_store *r, void * buf,
+ size_t nbytes, int flags);
+
+/*
+ * This utility inline function is responsible for transfering entropy
+ * from the primary pool to the secondary extraction pool. We pull
+ * randomness under two conditions; one is if there isn't enough entropy
+ * in the secondary pool. The other is after we have extract 1024 bytes,
+ * at which point we do a "catastrophic reseeding".
+ */
+static inline void xfer_secondary_pool(struct entropy_store *r,
+ size_t nbytes)
+{
+ __u32 tmp[TMP_BUF_SIZE];
+
+ if (r->entropy_count < nbytes*8) {
+ extract_entropy(random_state, tmp, sizeof(tmp), 0);
+ add_entropy_words(r, tmp, TMP_BUF_SIZE);
+ credit_entropy_store(r, TMP_BUF_SIZE*8);
+ }
+ if (r->extract_count > 1024) {
+ extract_entropy(random_state, tmp, sizeof(tmp), 0);
+ add_entropy_words(r, tmp, TMP_BUF_SIZE);
+ r->extract_count = 0;
+ }
+}
-#if POOLWORDS % 16 != 0
-#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
* bits of entropy are left in the pool, but it does not restrict the
- * number of bytes that are actually obtained.
+ * number of bytes that are actually obtained. If the EXTRACT_ENTROPY_USER
+ * flag is given, then the buf pointer is assumed to be in user space.
+ * If the EXTRACT_ENTROPY_SECONDARY flag is given, then this function will
+ *
+ * Note: extract_entropy() assumes that POOLWORDS is a multiple of 16 words.
*/
-static ssize_t extract_entropy(struct random_bucket *r, char * buf,
- size_t nbytes, int to_user)
+static ssize_t extract_entropy(struct entropy_store *r, void * buf,
+ size_t nbytes, int flags)
{
ssize_t ret, i;
- __u32 tmp[HASH_BUFFER_SIZE + HASH_EXTRA_SIZE];
+ __u32 tmp[TMP_BUF_SIZE];
__u32 x;
- add_timer_randomness(r, &extract_timer_state, nbytes);
+ add_timer_randomness(&extract_timer_state, nbytes);
/* Redundant, but just in case... */
- if (r->entropy_count > POOLBITS)
- r->entropy_count = POOLBITS;
+ if (r->entropy_count > r->poolinfo.poolwords)
+ r->entropy_count = r->poolinfo.poolwords;
+
+ if (flags & EXTRACT_ENTROPY_SECONDARY)
+ xfer_secondary_pool(r, nbytes);
- ret = nbytes;
if (r->entropy_count / 8 >= nbytes)
r->entropy_count -= nbytes*8;
else
r->entropy_count = 0;
- if (r->entropy_count < WAIT_OUTPUT_BITS)
+ if (r->entropy_count < random_write_wakeup_thresh)
wake_up_interruptible(&random_write_wait);
+
+ r->extract_count += nbytes;
+ ret = 0;
while (nbytes) {
+ /*
+ * Check if we need to break out or reschedule....
+ */
+ if ((flags & EXTRACT_ENTROPY_USER) && current->need_resched) {
+ if (signal_pending(current)) {
+ if (ret == 0)
+ ret = -ERESTARTSYS;
+ break;
+ }
+ schedule();
+ }
+
/* Hash the pool to get the output */
tmp[0] = 0x67452301;
tmp[1] = 0xefcdab89;
@@ -1228,39 +1275,34 @@
#ifdef USE_SHA
tmp[4] = 0xc3d2e1f0;
#endif
- for (i = 0; i < POOLWORDS; i += 16)
- HASH_TRANSFORM(tmp, r->pool+i);
-
/*
- * The following code does two separate things that happen
- * to both work two words at a time, so are convenient
- * to do together.
- *
- * First, this feeds the output back into the pool so
- * that the next call will return different results.
- * Any perturbation of the pool's state would do, even
- * changing one bit, but this mixes the pool nicely.
- *
- * Second, this folds the output in half to hide the data
- * fed back into the pool from the user and further mask
- * any patterns in the hash output. (The exact folding
- * pattern is not important; the one used here is quick.)
+ * As we hash the pool, we mix intermediate values of
+ * the hash back into the pool. This eliminates
+ * backtracking attacks (where the attacker knows
+ * the state of the pool plus the current outputs, and
+ * attempts to find previous ouputs), unless the hash
+ * function can be inverted.
*/
- for (i = 0; i < HASH_BUFFER_SIZE/2; i++) {
- x = tmp[i + (HASH_BUFFER_SIZE+1)/2];
- add_entropy_words(r, tmp[i], x);
- tmp[i] ^= x;
+ for (i = 0, x = 0; i < r->poolinfo.poolwords; i += 16, x+=2) {
+ HASH_TRANSFORM(tmp, r->pool+i);
+ add_entropy_words(r, &tmp[x%HASH_BUFFER_SIZE], 1);
}
+
+ /*
+ * In case the hash function has some recognizable
+ * output pattern, we fold it in half.
+ */
+ for (i = 0; i < HASH_BUFFER_SIZE/2; i++)
+ tmp[i] ^= tmp[i + (HASH_BUFFER_SIZE+1)/2];
#if HASH_BUFFER_SIZE & 1 /* There's a middle word to deal with */
x = tmp[HASH_BUFFER_SIZE/2];
- add_entropy_words(r, x, (__u32)((unsigned long)buf));
x ^= (x >> 16); /* Fold it in half */
((__u16 *)tmp)[HASH_BUFFER_SIZE-1] = (__u16)x;
#endif
/* Copy data to destination buffer */
i = MIN(nbytes, HASH_BUFFER_SIZE*sizeof(__u32)/2);
- if (to_user) {
+ if (flags & EXTRACT_ENTROPY_USER) {
i -= copy_to_user(buf, (__u8 const *)tmp, i);
if (!i) {
ret = -EFAULT;
@@ -1270,16 +1312,8 @@
memcpy(buf, (__u8 const *)tmp, i);
nbytes -= i;
buf += i;
- add_timer_randomness(r, &extract_timer_state, nbytes);
- if (to_user && current->need_resched)
- {
- if (signal_pending(current))
- {
- ret = -EINTR;
- break;
- }
- schedule();
- }
+ ret += i;
+ add_timer_randomness(&extract_timer_state, nbytes);
}
/* Wipe data just returned from memory */
@@ -1295,9 +1329,108 @@
*/
void get_random_bytes(void *buf, int nbytes)
{
- extract_entropy(&random_state, (char *) buf, nbytes, 0);
+ extract_entropy(sec_random_state, (char *) buf, nbytes,
+ EXTRACT_ENTROPY_SECONDARY);
+}
+
+/*********************************************************************
+ *
+ * Functions to interface with Linux
+ *
+ *********************************************************************/
+
+/*
+ * Initialize the random pool with standard stuff.
+ *
+ * NOTE: This is an OS-dependent function.
+ */
+static void init_std_data(struct entropy_store *r)
+{
+ struct timeval tv;
+ __u32 words[2];
+ char *p;
+ int i;
+
+ do_gettimeofday(&tv);
+ words[0] = tv.tv_sec;
+ words[1] = tv.tv_usec;
+ add_entropy_words(r, words, 2);
+
+ /*
+ * This doesn't lock system.utsname. However, we are generating
+ * entropy so a race with a name set here is fine.
+ */
+ p = (char *) &system_utsname;
+ for (i = sizeof(system_utsname) / sizeof(words); i; i--) {
+ memcpy(words, p, sizeof(words));
+ add_entropy_words(r, words, sizeof(words)/4);
+ p += sizeof(words);
+ }
+}
+
+void __init rand_initialize(void)
+{
+ int i;
+
+ if (create_entropy_store(DEFAULT_POOL_SIZE, &random_state))
+ return; /* Error, return */
+ if (batch_entropy_init(BATCH_ENTROPY_SIZE, random_state))
+ return; /* Error, return */
+ if (create_entropy_store(SECONDARY_POOL_SIZE, &sec_random_state))
+ return; /* Error, return */
+ clear_entropy_store(random_state);
+ clear_entropy_store(sec_random_state);
+ init_std_data(random_state);
+#ifdef CONFIG_SYSCTL
+ sysctl_init_random(random_state);
+#endif
+ for (i = 0; i < NR_IRQS; i++)
+ irq_timer_state[i] = NULL;
+ for (i = 0; i < MAX_BLKDEV; i++)
+ blkdev_timer_state[i] = NULL;
+ memset(&keyboard_timer_state, 0, sizeof(struct timer_rand_state));
+ memset(&mouse_timer_state, 0, sizeof(struct timer_rand_state));
+ memset(&extract_timer_state, 0, sizeof(struct timer_rand_state));
+ extract_timer_state.dont_count_entropy = 1;
+}
+
+void rand_initialize_irq(int irq)
+{
+ struct timer_rand_state *state;
+
+ if (irq >= NR_IRQS || irq_timer_state[irq])
+ return;
+
+ /*
+ * If kmalloc returns null, we just won't use that entropy
+ * source.
+ */
+ state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL);
+ if (state) {
+ memset(state, 0, sizeof(struct timer_rand_state));
+ irq_timer_state[irq] = state;
+ }
+}
+
+void rand_initialize_blkdev(int major, int mode)
+{
+ struct timer_rand_state *state;
+
+ if (major >= MAX_BLKDEV || blkdev_timer_state[major])
+ return;
+
+ /*
+ * If kmalloc returns null, we just won't use that entropy
+ * source.
+ */
+ state = kmalloc(sizeof(struct timer_rand_state), mode);
+ if (state) {
+ memset(state, 0, sizeof(struct timer_rand_state));
+ blkdev_timer_state[major] = state;
+ }
}
+
static ssize_t
random_read(struct file * file, char * buf, size_t nbytes, loff_t *ppos)
{
@@ -1309,11 +1442,13 @@
add_wait_queue(&random_read_wait, &wait);
while (nbytes > 0) {
- current->state = TASK_INTERRUPTIBLE;
+ set_current_state(TASK_INTERRUPTIBLE);
n = nbytes;
- if (n > random_state.entropy_count / 8)
- n = random_state.entropy_count / 8;
+ if (n > SEC_XFER_SIZE)
+ n = SEC_XFER_SIZE;
+ if (n > random_state->entropy_count / 8)
+ n = random_state->entropy_count / 8;
if (n == 0) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
@@ -1326,7 +1461,9 @@
schedule();
continue;
}
- n = extract_entropy(&random_state, buf, n, 1);
+ n = extract_entropy(sec_random_state, buf, n,
+ EXTRACT_ENTROPY_USER |
+ EXTRACT_ENTROPY_SECONDARY);
if (n < 0) {
retval = n;
break;
@@ -1351,10 +1488,12 @@
}
static ssize_t
-random_read_unlimited(struct file * file, char * buf,
+urandom_read(struct file * file, char * buf,
size_t nbytes, loff_t *ppos)
{
- return extract_entropy(&random_state, buf, nbytes, 1);
+ return extract_entropy(sec_random_state, buf, nbytes,
+ EXTRACT_ENTROPY_USER |
+ EXTRACT_ENTROPY_SECONDARY);
}
static unsigned int
@@ -1365,9 +1504,9 @@
poll_wait(file, &random_read_wait, wait);
poll_wait(file, &random_write_wait, wait);
mask = 0;
- if (random_state.entropy_count >= WAIT_INPUT_BITS)
+ if (random_state->entropy_count >= random_read_wakeup_thresh)
mask |= POLLIN | POLLRDNORM;
- if (random_state.entropy_count < WAIT_OUTPUT_BITS)
+ if (random_state->entropy_count < random_write_wakeup_thresh)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
@@ -1378,7 +1517,6 @@
{
int ret = 0;
size_t bytes;
- unsigned i;
__u32 buf[16];
const char *p = buffer;
size_t c = count;
@@ -1393,11 +1531,10 @@
}
c -= bytes;
p += bytes;
-
- i = (unsigned)((bytes-1) / (2 * sizeof(__u32)));
- do {
- add_entropy_words(&random_state, buf[2*i], buf[2*i+1]);
- } while (i--);
+
+ /* Convert bytes to words */
+ bytes = (bytes + 3) / sizeof(__u32);
+ add_entropy_words(random_state, buf, bytes);
}
if (p == buffer) {
return (ssize_t)ret;
@@ -1417,7 +1554,7 @@
switch (cmd) {
case RNDGETENTCNT:
- ent_count = random_state.entropy_count;
+ ent_count = random_state->entropy_count;
if (put_user(ent_count, (int *) arg))
return -EFAULT;
return 0;
@@ -1426,45 +1563,31 @@
return -EPERM;
if (get_user(ent_count, (int *) arg))
return -EFAULT;
- /*
- * Add i to entropy_count, limiting the result to be
- * between 0 and POOLBITS.
- */
- if (ent_count < -random_state.entropy_count)
- random_state.entropy_count = 0;
- else if (ent_count > POOLBITS)
- random_state.entropy_count = POOLBITS;
- else {
- random_state.entropy_count += ent_count;
- if (random_state.entropy_count > POOLBITS)
- random_state.entropy_count = POOLBITS;
- if (random_state.entropy_count < 0)
- random_state.entropy_count = 0;
- }
+ credit_entropy_store(random_state, ent_count);
/*
* Wake up waiting processes if we have enough
* entropy.
*/
- if (random_state.entropy_count >= WAIT_INPUT_BITS)
+ if (random_state->entropy_count >= random_read_wakeup_thresh)
wake_up_interruptible(&random_read_wait);
return 0;
case RNDGETPOOL:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
p = (int *) arg;
- ent_count = random_state.entropy_count;
+ ent_count = random_state->entropy_count;
if (put_user(ent_count, p++))
return -EFAULT;
if (get_user(size, p))
return -EFAULT;
- if (put_user(POOLWORDS, p++))
+ if (put_user(random_state->poolinfo.poolwords, p++))
return -EFAULT;
if (size < 0)
return -EINVAL;
- if (size > POOLWORDS)
- size = POOLWORDS;
- if (copy_to_user(p, random_state.pool, size*sizeof(__u32)))
+ if (size > random_state->poolinfo.poolwords)
+ size = random_state->poolinfo.poolwords;
+ if (copy_to_user(p, random_state->pool, size*sizeof(__u32)))
return -EFAULT;
return 0;
case RNDADDENTROPY:
@@ -1481,36 +1604,25 @@
size, &file->f_pos);
if (retval < 0)
return retval;
- /*
- * Add ent_count to entropy_count, limiting the result to be
- * between 0 and POOLBITS.
- */
- if (ent_count > POOLBITS)
- random_state.entropy_count = POOLBITS;
- else {
- random_state.entropy_count += ent_count;
- if (random_state.entropy_count > POOLBITS)
- random_state.entropy_count = POOLBITS;
- if (random_state.entropy_count < 0)
- random_state.entropy_count = 0;
- }
+ credit_entropy_store(random_state, ent_count);
/*
* Wake up waiting processes if we have enough
* entropy.
*/
- if (random_state.entropy_count >= WAIT_INPUT_BITS)
+ if (random_state->entropy_count >= random_read_wakeup_thresh)
wake_up_interruptible(&random_read_wait);
return 0;
case RNDZAPENTCNT:
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- random_state.entropy_count = 0;
+ random_state->entropy_count = 0;
return 0;
case RNDCLEARPOOL:
/* Clear the entropy pool and associated counters. */
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- rand_clear_pool();
+ clear_entropy_store(random_state);
+ init_std_data(random_state);
return 0;
default:
return -EINVAL;
@@ -1532,7 +1644,7 @@
struct file_operations urandom_fops = {
NULL, /* unrandom_lseek */
- random_read_unlimited,
+ urandom_read,
random_write,
NULL, /* urandom_readdir */
NULL, /* urandom_poll */
@@ -1543,6 +1655,210 @@
NULL /* no special release code */
};
+/***************************************************************
+ * Random UUID interface
+ *
+ * Used here for a Boot ID, but can be useful for other kernel
+ * drivers.
+ ***************************************************************/
+
+/*
+ * Generate random UUID
+ */
+void generate_random_uuid(unsigned char uuid_out[16])
+{
+ get_random_bytes(uuid_out, 16);
+ /* Set UUID version to 4 --- truely random generation */
+ uuid_out[6] = (uuid_out[6] & 0x0F) | 0x40;
+ /* Set the UUID variant to DCE */
+ uuid_out[8] = (uuid_out[8] & 0x3F) | 0x80;
+}
+
+/********************************************************************
+ *
+ * Sysctl interface
+ *
+ ********************************************************************/
+
+#ifdef CONFIG_SYSCTL
+
+#include <linux/sysctl.h>
+
+static int sysctl_poolsize;
+static int min_read_thresh, max_read_thresh;
+static int min_write_thresh, max_write_thresh;
+static char sysctl_bootid[16];
+
+/*
+ * This function handles a request from the user to change the pool size
+ * of the primary entropy store.
+ */
+static int change_poolsize(int poolsize)
+{
+ struct entropy_store *new_store, *old_store;
+ int ret;
+
+ if ((ret = create_entropy_store(poolsize, &new_store)))
+ return ret;
+
+ add_entropy_words(new_store, random_state->pool,
+ random_state->poolinfo.poolwords);
+ credit_entropy_store(new_store, random_state->entropy_count);
+
+ sysctl_init_random(new_store);
+ old_store = random_state;
+ random_state = batch_tqueue.data = new_store;
+ free_entropy_store(old_store);
+ return 0;
+}
+
+static int proc_do_poolsize(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ int ret;
+
+ sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+
+ ret = proc_dointvec(table, write, filp, buffer, lenp);
+ if (ret || !write ||
+ (sysctl_poolsize == random_state->poolinfo.poolwords * 4))
+ return ret;
+
+ return change_poolsize(sysctl_poolsize);
+}
+
+static int poolsize_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen, void **context)
+{
+ int len;
+
+ sysctl_poolsize = random_state->poolinfo.poolwords * 4;
+
+ /*
+ * We only handle the write case, since the read case gets
+ * handled by the default handler (and we don't care if the
+ * write case happens twice; it's harmless).
+ */
+ if (newval && newlen) {
+ len = newlen;
+ if (len > table->maxlen)
+ len = table->maxlen;
+ if (copy_from_user(table->data, newval, len))
+ return -EFAULT;
+ }
+
+ if (sysctl_poolsize != random_state->poolinfo.poolwords * 4)
+ return change_poolsize(sysctl_poolsize);
+
+ return 0;
+}
+
+/*
+ * These functions is used to return both the bootid UUID, and random
+ * UUID. The difference is in whether table->data is NULL; if it is,
+ * then a new UUID is generated and returned to the user.
+ *
+ * If the user accesses this via the proc interface, it will be returned
+ * as an ASCII string in the standard UUID format. If accesses via the
+ * sysctl system call, it is returned as 16 bytes of binary data.
+ */
+static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
+{
+ ctl_table fake_table;
+ unsigned char buf[64], tmp_uuid[16], *uuid;
+
+ uuid = table->data;
+ if (!uuid) {
+ uuid = tmp_uuid;
+ uuid[8] = 0;
+ }
+ if (uuid[8] == 0)
+ generate_random_uuid(uuid);
+
+ sprintf(buf, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
+ "%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3],
+ uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11],
+ uuid[12], uuid[13], uuid[14], uuid[15]);
+ fake_table.data = buf;
+ fake_table.maxlen = sizeof(buf);
+
+ return proc_dostring(&fake_table, write, filp, buffer, lenp);
+}
+
+static int uuid_strategy(ctl_table *table, int *name, int nlen,
+ void *oldval, size_t *oldlenp,
+ void *newval, size_t newlen, void **context)
+{
+ unsigned char tmp_uuid[16], *uuid;
+ int len;
+
+ if (!oldval || !oldlenp)
+ return 1;
+
+ uuid = table->data;
+ if (!uuid) {
+ uuid = tmp_uuid;
+ uuid[8] = 0;
+ }
+ if (uuid[8] == 0)
+ generate_random_uuid(uuid);
+
+ get_user(len, oldlenp);
+ if (len) {
+ if (len > 16)
+ len = 16;
+ if (copy_to_user(oldval, table->data, len))
+ return -EFAULT;
+ if (put_user(len, oldlenp))
+ return -EFAULT;
+ }
+ return 1;
+}
+
+ctl_table random_table[] = {
+ {RANDOM_POOLSIZE, "poolsize",
+ &sysctl_poolsize, sizeof(int), 0644, NULL,
+ &proc_do_poolsize, &poolsize_strategy},
+ {RANDOM_ENTROPY_COUNT, "entropy_avail",
+ NULL, sizeof(int), 0444, NULL,
+ &proc_dointvec},
+ {RANDOM_READ_THRESH, "read_wakeup_threshold",
+ &random_read_wakeup_thresh, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, 0,
+ &min_read_thresh, &max_read_thresh},
+ {RANDOM_WRITE_THRESH, "write_wakeup_threshold",
+ &random_write_wakeup_thresh, sizeof(int), 0644, NULL,
+ &proc_dointvec_minmax, &sysctl_intvec, 0,
+ &min_write_thresh, &max_write_thresh},
+ {RANDOM_BOOT_ID, "boot_id",
+ &sysctl_bootid, 16, 0444, NULL,
+ &proc_do_uuid, &uuid_strategy},
+ {RANDOM_UUID, "uuid",
+ NULL, 16, 0444, NULL,
+ &proc_do_uuid, &uuid_strategy},
+ {0}
+};
+
+static void sysctl_init_random(struct entropy_store *random_state)
+{
+ min_read_thresh = 8;
+ min_write_thresh = 0;
+ max_read_thresh = max_write_thresh =
+ random_state->poolinfo.poolwords * 32;
+ random_table[1].data = &random_state->entropy_count;
+}
+#endif /* CONFIG_SYSCTL */
+
+/********************************************************************
+ *
+ * Random funtions for networking
+ *
+ ********************************************************************/
+
/*
* TCP initial sequence number picking. This uses the random number
* generator to pick an initial secret value. This value is hashed
@@ -1831,71 +2147,3 @@
return (cookie - tmp[17]) & COOKIEMASK; /* Leaving the data behind */
}
#endif
-
-
-#ifdef RANDOM_BENCHMARK
-/*
- * This is so we can do some benchmarking of the random driver, to see
- * how much overhead add_timer_randomness really takes. This only
- * works on a Pentium, since it depends on the timer clock...
- *
- * Note: the results of this benchmark as of this writing (5/27/96)
- *
- * On a Pentium, add_timer_randomness() takes between 150 and 1000
- * clock cycles, with an average of around 600 clock cycles. On a 75
- * MHz Pentium, this translates to 2 to 13 microseconds, with an
- * average time of 8 microseconds. This should be fast enough so we
- * can use add_timer_randomness() even with the fastest of interrupts...
- */
-static inline unsigned long long get_clock_cnt(void)
-{
- unsigned long low, high;
- __asm__(".byte 0x0f,0x31" :"=a" (low), "=d" (high));
- return (((unsigned long long) high << 32) | low);
-}
-
-static void __init
-initialize_benchmark(struct random_benchmark *bench,
- const char *descr, int unit)
-{
- bench->times = 0;
- bench->accum = 0;
- bench->max = 0;
- bench->min = 1 << 31;
- bench->descr = descr;
- bench->unit = unit;
-}
-
-static void begin_benchmark(struct random_benchmark *bench)
-{
-#ifdef BENCHMARK_NOINT
- save_flags(bench->flags); cli();
-#endif
- bench->start_time = get_clock_cnt();
-}
-
-static void end_benchmark(struct random_benchmark *bench)
-{
- unsigned long ticks;
-
- ticks = (unsigned long) (get_clock_cnt() - bench->start_time);
-#ifdef BENCHMARK_NOINT
- restore_flags(bench->flags);
-#endif
- if (ticks < bench->min)
- bench->min = ticks;
- if (ticks > bench->max)
- bench->max = ticks;
- bench->accum += ticks;
- bench->times++;
- if (bench->times == BENCHMARK_INTERVAL) {
- printk("Random benchmark: %s %d: %lu min, %lu avg, "
- "%lu max\n", bench->descr, bench->unit, bench->min,
- bench->accum / BENCHMARK_INTERVAL, bench->max);
- bench->times = 0;
- bench->accum = 0;
- bench->max = 0;
- bench->min = 1 << 31;
- }
-}
-#endif /* RANDOM_BENCHMARK */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)