Subject: [PATCH] randread: use /dev/urandom to seed, not just getpid etc

From: Paul Eggert <[email-masked]>
Date: Tue, 20 Jul 2010 09:59:41 -0700

OK, I installed part (2) of the patch: it uses /dev/urandom
rather than the getpid()/gettimeofday() yucky stuff,
when /dev/urandom is available.

One downside to this patch is that on hosts where mkstemp
doesn't work and we're using our substitute, each invocation
of mkstemp opens /dev/urandom, reads a few bytes, and closes it.
I don't view this as a big problem, because (a) it's only for
hosts with broken mkstemp and they should fix their mkstemp and
(b) creating a temp file is a big-deal operation anyway and
the extra overhead of opening /dev/urandom doesn't add much.

I considered working around the problem anyway, but couldn't
come up with a simple, clean and fast workaround under the
constraint that mkstemp must be thread-safe (the current randread
stuff does not allow competing threads to read from the same
random source).  Perhaps later, if there's interest.

>From 3403f96997bc6ff441696e11a892f2389891aaa7 Mon Sep 17 00:00:00 2001
From: Paul R. Eggert <[email-masked]>
Date: Tue, 20 Jul 2010 09:50:37 -0700
Subject: [PATCH] randread: use /dev/urandom to seed, not just getpid etc

* gl/lib/rand-isaac.c (isaac_seed_start): New arg SEEDED.
(isaac_seed): New args FD and BYTES_BOUND.  Read from FD if possible.
Don't bother with low-quality sources if FD has enough bytes.
* gl/lib/rand-isaac.h: New size_t arg for isaac_seed.
* gl/lib/randread.c: Include fcntl.h, unistd.h.
(NAME_OF_NONCE_DEVICE): New #define.
(nonce_device): New static var.
(randread_new): Use nonce device if available.
---
 gl/lib/rand-isaac.c |   49 +++++++++++++++++++++++++++++++++----------------
 gl/lib/rand-isaac.h |    3 ++-
 gl/lib/randread.c   |   11 ++++++++++-
 3 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/gl/lib/rand-isaac.c b/gl/lib/rand-isaac.c
index 377caa6..f73144a 100644
--- a/gl/lib/rand-isaac.c
+++ b/gl/lib/rand-isaac.c
@@ -209,9 +209,10 @@ isaac_init (struct isaac_state *s, uint32_t const *seed, size_t seedsize)
 }
 #endif
 
-/* Initialize *S to a somewhat-random value.  */
+/* Initialize *S to a somewhat-random value.  The first SEEDED bytes
+   in S->mm are already seeded with random data.  */
 static void
-isaac_seed_start (struct isaac_state *s)
+isaac_seed_start (struct isaac_state *s, size_t seeded)
 {
   static uint32_t const iv[8] =
     {
@@ -228,11 +229,12 @@ isaac_seed_start (struct isaac_state *s)
     mix (iv[0], iv[1], iv[2], iv[3], iv[4], iv[5], iv[6], iv[7]);
 #endif
 
-  memset (s->mm, 0, sizeof s->mm);
+  memset ((char *) s->mm + seeded, 0, sizeof s->mm - seeded);
   memcpy (s->iv, iv, sizeof s->iv);
 
   /* s->c gets used for a data pointer during the seeding phase */
-  s->a = s->b = s->c = 0;
+  s->a = s->b = 0;
+  s->c = seeded;
 }
 
 /* Add a buffer of seed material.  */
@@ -279,22 +281,37 @@ isaac_seed_finish (struct isaac_state *s)
 #define ISAAC_SEED(s,x) isaac_seed_data (s, &(x), sizeof (x))
 
 /* Initialize *S to a somewhat-random value; this starts seeding,
-   seeds with somewhat-random data, and finishes seeding.  */
+   seeds with somewhat-random data, and finishes seeding.  If FD is
+   nonnegative, seed by reading at most BYTES_BOUNDS bytes from it.  */
 void
-isaac_seed (struct isaac_state *s)
+isaac_seed (struct isaac_state *s, int fd, size_t bytes_bound)
 {
-  isaac_seed_start (s);
+  /* Get some data from FD if available.  */
+  ssize_t seeded = 0;
+  if (0 <= fd)
+    {
+      if (sizeof s->mm < bytes_bound)
+        bytes_bound = sizeof s->mm;
+      seeded = read (fd, s->mm, bytes_bound);
+      if (seeded < 0)
+        seeded = 0;
+    }
 
-  { pid_t t = getpid ();   ISAAC_SEED (s, t); }
-  { pid_t t = getppid ();  ISAAC_SEED (s, t); }
-  { uid_t t = getuid ();   ISAAC_SEED (s, t); }
-  { gid_t t = getgid ();   ISAAC_SEED (s, t); }
+  isaac_seed_start (s, seeded);
 
-  {
-    struct timeval t;
-    gettimeofday (&t, NULL);
-    ISAAC_SEED (s, t);
-  }
+  if (seeded < sizeof s->mm)
+    {
+      { pid_t t = getpid ();   ISAAC_SEED (s, t); }
+      { pid_t t = getppid ();  ISAAC_SEED (s, t); }
+      { uid_t t = getuid ();   ISAAC_SEED (s, t); }
+      { gid_t t = getgid ();   ISAAC_SEED (s, t); }
+
+      {
+        struct timeval t;
+        gettimeofday (&t, NULL);
+        ISAAC_SEED (s, t);
+      }
+    }
 
   isaac_seed_finish (s);
 }
diff --git a/gl/lib/rand-isaac.h b/gl/lib/rand-isaac.h
index 6559dc8..052dc9f 100644
--- a/gl/lib/rand-isaac.h
+++ b/gl/lib/rand-isaac.h
@@ -21,6 +21,7 @@
 #ifndef RAND_ISAAC_H
 # define RAND_ISAAC_H
 
+# include <stddef.h>
 # include <stdint.h>
 
 /* Size of the state tables to use.  ISAAC_LOG should be at least 3,
@@ -37,7 +38,7 @@ struct isaac_state
     uint32_t a, b, c;		/* Extra index variables */
   };
 
-void isaac_seed (struct isaac_state *);
+void isaac_seed (struct isaac_state *, int, size_t);
 void isaac_refill (struct isaac_state *, uint32_t[ISAAC_WORDS]);
 
 #endif
diff --git a/gl/lib/randread.c b/gl/lib/randread.c
index 94b9928..a681c8d 100644
--- a/gl/lib/randread.c
+++ b/gl/lib/randread.c
@@ -24,12 +24,14 @@
 #include <errno.h>
 #include <error.h>
 #include <exitfail.h>
+#include <fcntl.h>
 #include <quotearg.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -60,6 +62,10 @@
 # define ALIGNED_POINTER(ptr, type) ((size_t) (ptr) % alignof (type) == 0)
 #endif
 
+#ifndef NAME_OF_NONCE_DEVICE
+#define NAME_OF_NONCE_DEVICE "/dev/urandom"
+#endif
+
 /* The maximum buffer size used for reads of random data.  Using the
    value 2 * ISAAC_BYTES makes this the largest power of two that
    would not otherwise cause struct randread_source to grow.  */
@@ -164,8 +170,11 @@ randread_new (char const *name, size_t bytes_bound)
         setvbuf (source, s->buf.c, _IOFBF, MIN (sizeof s->buf.c, bytes_bound));
       else
         {
+          int nonce_device = open (NAME_OF_NONCE_DEVICE, O_RDONLY | O_BINARY);
           s->buf.isaac.buffered = 0;
-          isaac_seed (&s->buf.isaac.state);
+          isaac_seed (&s->buf.isaac.state, nonce_device, bytes_bound);
+          if (0 <= nonce_device)
+            close (nonce_device);
         }
 
       return s;
-- 
1.7.1