rand/: Secure `rand' generator against fork problems.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 26 May 2016 08:26:09 +0000 (09:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 4 Jun 2016 12:52:58 +0000 (13:52 +0100)
This is fiddlier than it really ought to be.

  * Make the `i' and `irot' members be `unsigned short' to make space
    for a new member.  These members have well-constrained ranges, so
    this is safe.

  * Insert a new `gen' member to keep track of the pool's `generation
    number'.  Arrange that the global generator's generation number is
    initially zero.

  * Invent a new system-specific function `rand_generation' which
    returns a nonzero `generation number', which changes across forks
    and such things.

  * Have the output functions `rand_get' and `rand_getgood' check the
    generation number and force a `rand_gate' if it changes.

  * Arrange for `rand_gate' and `rand_stretch' to feed the generation
    number into the hashing, so that generators with different
    generations behave computationally independently.

rand/Makefile.am
rand/rand.c
rand/rand.h
rand/randgen.c [new file with mode: 0644]

index a60125b..d97749e 100644 (file)
@@ -67,6 +67,7 @@ librand_la_SOURCES    += noise.c
 ## Cryptographic laundering for true random data generation.
 pkginclude_HEADERS     += rand.h
 librand_la_SOURCES     += rand.c
+librand_la_SOURCES     += randgen.c
 
 ## The SSL v3 pseudorandom function.
 pkginclude_HEADERS     += sslprf.h
index 6fcdf43..8de6607 100644 (file)
@@ -68,7 +68,7 @@ typedef struct rand__gctx {
 
 gctx rand_global = {
   { &gops },
-  { { 0 }, 0, 0, 0,
+  { { 0 }, 0, 0, 0, 0,
     { 0 }, RAND_SECSZ, 0,
     { "Catacomb global random byte pool" },
     &noise_source }
@@ -79,6 +79,11 @@ gctx rand_global = {
 #define RAND_RESOLVE(r)                                                        \
   do { if ((r) == RAND_GLOBAL) r = &rand_global.p; } while (0)
 
+#define GENCHECK(r) do {                                               \
+  unsigned gen = rand_generation();                                    \
+  if (r->gen != gen) { r->gen = gen; rand_gate(r); }                   \
+} while (0)
+
 #define TIMER(r) do {                                                  \
   if ((r)->s && (r)->s->timer)                                         \
     (r)->s->timer(r);                                                  \
@@ -103,6 +108,7 @@ void rand_init(rand_pool *r)
   RAND_RESOLVE(r);
   memset(r->pool, 0, sizeof(r->pool));
   memset(r->buf, 0, sizeof(r->buf));
+  r->gen = rand_generation();
   r->i = 0;
   r->irot = 0;
   r->ibits = r->obits = 0;
@@ -261,7 +267,7 @@ unsigned rand_goodbits(rand_pool *r)
 
 void rand_gate(rand_pool *r)
 {
-  octet h[HASH_SZ];
+  octet h[HASH_SZ], g[4];
   HASH_CTX hc;
   CIPHER_CTX cc;
 
@@ -271,6 +277,7 @@ void rand_gate(rand_pool *r)
   /* --- Hash up all the data in the pool --- */
 
   HASH_INIT(&hc);
+  STORE32(g, r->gen); HASH(&hc, g, sizeof(g));
   HASH(&hc, r->pool, RAND_POOLSZ);
   HASH(&hc, r->buf, RAND_BUFSZ);
   HASH_DONE(&hc, h);
@@ -310,7 +317,7 @@ void rand_gate(rand_pool *r)
 
 void rand_stretch(rand_pool *r)
 {
-  octet h[HASH_SZ];
+  octet h[HASH_SZ], g[4];
   HASH_CTX hc;
   CIPHER_CTX cc;
 
@@ -320,6 +327,7 @@ void rand_stretch(rand_pool *r)
   /* --- Hash up all the data in the buffer --- */
 
   HASH_INIT(&hc);
+  STORE32(g, r->gen); HASH(&hc, g, sizeof(g));
   HASH(&hc, r->pool, RAND_POOLSZ);
   HASH(&hc, r->buf, RAND_BUFSZ);
   HASH_DONE(&hc, h);
@@ -359,6 +367,7 @@ void rand_get(rand_pool *r, void *p, size_t sz)
   octet *o = p;
 
   RAND_RESOLVE(r);
+  GENCHECK(r);
   TIMER(r);
 
   if (!sz)
@@ -412,6 +421,7 @@ void rand_getgood(rand_pool *r, void *p, size_t sz)
     rand_get(r, p, sz);
     return;
   }
+  GENCHECK(r);
   TIMER(r);
 
   while (sz) {
index 3c2fa73..ab1c4a8 100644 (file)
@@ -99,8 +99,9 @@
 
 typedef struct rand_pool {
   octet pool[RAND_POOLSZ];             /* Actual contents of the pool */
-  unsigned i;                          /* Current index into pool */
-  unsigned irot;                       /* Current rotation applied */
+  unsigned gen;                                /* Generation number */
+  unsigned short i;                    /* Current index into pool */
+  unsigned short irot;                 /* Current rotation applied */
   unsigned ibits;                      /* Number of good bits in pool */
   octet buf[RAND_BUFSZ];               /* Random octet output buffer */
   unsigned o;                          /* Current index into buffer */
@@ -132,6 +133,20 @@ typedef struct rand_source {
 
 extern void rand_init(rand_pool */*r*/);
 
+/* --- @rand_generation@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A nonzero generation number.
+ *
+ * Use:                Returns a generation number for the current process.  Each
+ *             pool has its own number.  If this matches the process number
+ *             then all is well.  If it doesn't match, then the pool needs
+ *             to be cleaned before its next use.
+ */
+
+extern unsigned rand_generation(void);
+
 /* --- @rand_noisesrc@ --- *
  *
  * Arguments:  @rand_pool *r@ = pointer to a randomness pool
diff --git a/rand/randgen.c b/rand/randgen.c
new file mode 100644 (file)
index 0000000..7cd2f35
--- /dev/null
@@ -0,0 +1,50 @@
+/* -*-c-*-
+ *
+ * Pool-generation boundary machinery (Unix-specific)
+ *
+ * (c) 2016 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <unistd.h>
+
+#include "rand.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @rand_generation@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A nonzero generation number.
+ *
+ * Use:                Returns a generation number for the current process.  Each
+ *             pool has its own number.  If this matches the process number
+ *             then all is well.  If it doesn't match, then the pool needs
+ *             to be cleaned before its next use.
+ */
+
+unsigned rand_generation(void) { return (getpid()); }
+
+/*----- That's all, folks -------------------------------------------------*/