]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Arc4random.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 17 Oct 2007 12:08:34 +0000 (12:08 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Wed, 17 Oct 2007 12:08:34 +0000 (12:08 +0000)
git-svn-id: file:///svn/unbound/trunk@683 be551aaa-1e26-0410-a405-d3ace91eadb9

Makefile.in
doc/Changelog
testcode/unitmain.c
util/random.c
util/random.h

index 49e30e7da1a5a9720104cefb9e6c2b6ef533682e..5eff514ac6eb7365725f143f9fe36b6472319e03 100644 (file)
@@ -48,7 +48,7 @@ BUILD=build/
 LINT=splint
 LINTFLAGS=+quiet -weak -warnposix -unrecog -Din_addr_t=uint32_t -Du_int=unsigned -Du_char=uint8_t -preproc -Drlimit=rlimit64 -D__gnuc_va_list=va_list
 # compat with openssl linux edition.
-LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t
+LINTFLAGS+="-DBN_ULONG=unsigned long" -Dkrb5_int32=int "-Dkrb5_ui_4=unsigned int" -DPQ_64BIT=uint64_t -DRC4_INT=unsigned
 
 INSTALL=$(srcdir)/install-sh
 
index 16a062d7d476ab1022b674aac10678c5cb96e16f..5caeed211be6783f7c7aadc635fadd4a8e0ffdab 100644 (file)
@@ -5,6 +5,9 @@
          We're going to have to ask a TLD server anyway; might as well be
          the TLD server for this name. And this resolves a lot of cases where
          the other nameserver names lead to cycles or are not available.
+       - changed random generator from random(3) clone to arc4random wrapped
+         for thread safety. The random generator is initialised with
+         entropy from the system.
 
 16 October 2007: Wouter
        - no malloc in log_hex.
index 1e2e9e9710d88597c39e1f0ec923928e7743e881..72822ab6dbc97d267a3794b79fca993710df9e88 100644 (file)
@@ -197,6 +197,27 @@ infra_test()
        config_delete(cfg);
 }
 
+#include "util/random.h"
+/** test randomness */
+static void
+rnd_test()
+{
+       struct ub_randstate r;
+       int num = 100, i;
+       long int a[100];
+       unit_assert( ub_initstate((unsigned)time(NULL), &r, 256) );
+       for(i=0; i<num; i++) {
+               a[i] = ub_random(&r);
+               unit_assert(a[i] >= 0);
+               unit_assert((size_t)a[i] <= (size_t)RAND_MAX);
+               if(i > 5)
+                       unit_assert(a[i] != a[i-1] || a[i] != a[i-2] ||
+                               a[i] != a[i-3] || a[i] != a[i-4] ||
+                               a[i] != a[i-5] || a[i] != a[i-6]);
+       }
+       ub_randfree(&r);
+}
+
 /**
  * Main unit test program. Setup, teardown and report errors.
  * @param argc: arg count.
@@ -213,6 +234,7 @@ main(int argc, char* argv[])
        }
        printf("Start of %s unit test.\n", PACKAGE_STRING);
        checklock_start();
+       rnd_test();
        verify_test();
        net_test();
        dname_test();
index 976b6a0e24527847f50a1d3629e8bb0ff6985edd..19a72834bc3ac13173a2337dfa77b71dad92084f 100644 (file)
 /*
- * util/random.c - random numbers thread safe.
- * BSD licensed, taken from binutils 2.17.
- */
-
-#include "config.h"
-#include "util/random.h"
-
-/*
- * Copyright (c) 1983 Regents of the University of California.
- * All rights reserved.
- *
+ * util/random.c - thread safe random generator, which is reasonably secure.
+ * 
+ * Copyright (c) 2007, NLnet Labs. All rights reserved.
+ * 
+ * This software is open source.
+ * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. [rescinded 22 July 1999]
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
- */
-
-/*
- * This is derived from the Berkeley source:
- *     @(#)random.c    5.5 (Berkeley) 7/6/88
- * It was reworked for the GNU C Library by Roland McGrath.
+ * 
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 
+ * Neither the name of the NLNET LABS nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 REGENTS OR CONTRIBUTORS 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.
  */
 
 /**
  * \file
- * Thread safe random functions. Similar to random(3) and initstate(3).
+ * Thread safe random functions. Similar to arc4random() with an explicit
+ * initialisation routine.
+ *
+ * The code in this file is based on arc4random from
+ * openssh-4.0p1/openbsd-compat/bsd-arc4random.c
+ * That code is also BSD licensed.
  */
+#include "config.h"
+#include "util/random.h"
+#include "util/log.h"
+#include <openssl/rand.h>
+#include <openssl/rc4.h>
+#include <openssl/err.h>
 
-#include <errno.h>
-
-#ifndef ULONG_MAX
-/** in case its not defined */
-#define        ULONG_MAX  ((unsigned long)(~0L))     /* 0xFFFFFFFF for 32-bits */
-#endif
-#ifndef LONG_MAX
-/** in case its not defined */
-#define        LONG_MAX   ((long)(ULONG_MAX >> 1))   /* 0x7FFFFFFF for 32-bits*/
-#endif
-
-
-/* An improved random number generation package.  In addition to the standard
-   rand()/srand() like interface, this package also has a special state info
-   interface.  The initstate() routine is called with a seed, an array of
-   bytes, and a count of how many bytes are being passed in; this array is
-   then initialized to contain information for random number generation with
-   that much state information.  Good sizes for the amount of state
-   information are 32, 64, 128, and 256 bytes.  The state can be switched by
-   calling the setstate() function with the same array as was initiallized
-   with initstate().  By default, the package runs with 128 bytes of state
-   information and generates far better random numbers than a linear
-   congruential generator.  If the amount of state information is less than
-   32 bytes, a simple linear congruential R.N.G. is used.  Internally, the
-   state information is treated as an array of longs; the zeroeth element of
-   the array is the type of R.N.G. being used (small integer); the remainder
-   of the array is the state information for the R.N.G.  Thus, 32 bytes of
-   state information will give 7 longs worth of state information, which will
-   allow a degree seven polynomial.  (Note: The zeroeth word of state
-   information also has some other information stored in it; see setstate
-   for details).  The random number generation technique is a linear feedback
-   shift register approach, employing trinomials (since there are fewer terms
-   to sum up that way).  In this approach, the least significant bit of all
-   the numbers in the state table will act as a linear feedback shift register,
-   and will have period 2^deg - 1 (where deg is the degree of the polynomial
-   being used, assuming that the polynomial is irreducible and primitive).
-   The higher order bits will have longer periods, since their values are
-   also influenced by pseudo-random carries out of the lower bits.  The
-   total period of the generator is approximately deg*(2**deg - 1); thus
-   doubling the amount of state information has a vast influence on the
-   period of the generator.  Note: The deg*(2**deg - 1) is an approximation
-   only good for large deg, when the period of the shift register is the
-   dominant factor.  With deg equal to seven, the period is actually much
-   longer than the 7*(2**7 - 1) predicted by this formula.  */
-
-
-
-/* For each of the currently supported random number generators, we have a
-   break value on the amount of state information (you need at least thi
-   bytes of state info to support this random number generator), a degree for
-   the polynomial (actually a trinomial) that the R.N.G. is based on, and
-   separation between the two lower order coefficients of the trinomial.  */
-
-/** Linear congruential.  */
-#define        TYPE_0          0
-/** the break */
-#define        BREAK_0         8
-/** the degree */
-#define        DEG_0           0
-/** the sep */
-#define        SEP_0           0
-
-/** x**7 + x**3 + 1.  */
-#define        TYPE_1          1
-/** the break */
-#define        BREAK_1         32
-/** the degree */
-#define        DEG_1           7
-/** the sep */
-#define        SEP_1           3
-
-/** x**15 + x + 1.  */
-#define        TYPE_2          2
-/** the break */
-#define        BREAK_2         64
-/** the degree */
-#define        DEG_2           15
-/** the sep */
-#define        SEP_2           1
-
-/** x**31 + x**3 + 1.  */
-#define        TYPE_3          3
-/** the break */
-#define        BREAK_3         128
-/** the degree */
-#define        DEG_3           31
-/** the sep */
-#define        SEP_3           3
-
-/** x**63 + x + 1.  */
-#define        TYPE_4          4
-/** the break */
-#define        BREAK_4         256
-/** the degree */
-#define        DEG_4           63
-/** the sep */
-#define        SEP_4           1
-
-
-/* Array versions of the above information to make code run faster.
-   Relies on fact that TYPE_i == i.  */
-
-/** Max number of types above.  */
-#define        MAX_TYPES       5       
-
-/*
-static int degrees[MAX_TYPES] = { DEG_0, DEG_1, DEG_2, DEG_3, DEG_4 };
-static int seps[MAX_TYPES] = { SEP_0, SEP_1, SEP_2, SEP_3, SEP_4 };
-*/
-
-
-/* Initially, everything is set up as if from:
-       initstate(1, randtbl, 128);
-   Note that this initialization takes advantage of the fact that srandom
-   advances the front and rear pointers 10*rand_deg times, and hence the
-   rear pointer which starts at 0 will also end up at zero; thus the zeroeth
-   element of the state information, which contains info about the current
-   position of the rear pointer is just
-       (MAX_TYPES * (rptr - state)) + TYPE_3 == TYPE_3.  */
-
-/*
-static long int randtbl[DEG_3 + 1] =
-  { TYPE_3,
-      0x9a319039, 0x32d9c024, 0x9b663182, 0x5da1f342,
-      0xde3b81e0, 0xdf0a6fb5, 0xf103bc02, 0x48f340fb,
-      0x7449e56b, 0xbeb1dbb0, 0xab5c5918, 0x946554fd,
-      0x8c2e680f, 0xeb3d799f, 0xb11ee0b7, 0x2d436b86,
-      0xda672e2a, 0x1588ca88, 0xe369735d, 0x904f35f7,
-      0xd7158fd6, 0x6fa6f051, 0x616e6b96, 0xac94efdc,
-      0x36413f93, 0xc622c298, 0xf5a42ab8, 0x8a88d77b,
-      0xf5ad9d0e, 0x8999220b, 0x27fb47b9
-    };
-*/
-
-/* FPTR and RPTR are two pointers into the state info, a front and a rear
-   pointer.  These two pointers are always rand_sep places aparts, as they
-   cycle through the state information.  (Yes, this does mean we could get
-   away with just one pointer, but the code for random is more efficient
-   this way).  The pointers are left positioned as they would be from the call:
-       initstate(1, randtbl, 128);
-   (The position of the rear pointer, rptr, is really 0 (as explained above
-   in the initialization of randtbl) because the state table pointer is set
-   to point to randtbl[1] (as explained below).)  */
+/**
+ * Struct with per-thread random state.
+ * Keeps SSL types away from the header file.
+ */
+struct ub_hiddenstate {
+       /** key used for arc4random generation */
+       RC4_KEY rc4;
+       /** keeps track of key usage */
+       int rc4_ready;
+};
 
-/*
-static long int *fptr = &randtbl[SEP_3 + 1];
-static long int *rptr = &randtbl[1];
-*/
+/** Size of key to use */
+#define SEED_SIZE 20
 
+/** Number of bytes to reseed after */
+#define REKEY_BYTES    (1 << 24)
 
-/* The following things are the pointer to the state information table,
-   the type of the current generator, the degree of the current polynomial
-   being used, and the separation between the two pointers.
-   Note that for efficiency of random, we remember the first location of
-   the state information, not the zeroeth.  Hence it is valid to access
-   state[-1], which is used to store the type of the R.N.G.
-   Also, we remember the last location, since this is more efficient than
-   indexing every time to find the address of the last element to see if
-   the front and rear pointers have wrapped.  */
+/** reseed random generator */
+static void
+ub_arc4random_stir(struct ub_hiddenstate* s)
+{
+       unsigned char rand_buf[SEED_SIZE];
+       int i;
 
-/*
-static long int *state = &randtbl[1];
+       memset(&s->rc4, 0, sizeof(s->rc4));
+       if (RAND_bytes(rand_buf, (int)sizeof(rand_buf)) <= 0)
+               fatal_exit("Couldn't obtain random bytes (error %ld)",
+                           ERR_get_error());
+       RC4_set_key(&s->rc4, (int)sizeof(rand_buf), rand_buf);
 
-static int rand_type = TYPE_3;
-static int rand_deg = DEG_3;
-static int rand_sep = SEP_3;
+       /*
+        * Discard early keystream, as per recommendations in:
+        * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
+        */
+       for(i = 0; i <= 256; i += sizeof(rand_buf))
+               RC4(&s->rc4, sizeof(rand_buf), rand_buf, rand_buf);
 
-static long int *end_ptr = &randtbl[sizeof(randtbl) / sizeof(randtbl[0])];
-*/
+       memset(rand_buf, 0, sizeof(rand_buf));
 
-/* Initialize the random number generator based on the given seed.  If the
-   type is the trivial no-state-information type, just remember the seed.
-   Otherwise, initializes state[] based on the given "seed" via a linear
-   congruential generator.  Then, the pointers are set to known locations
-   that are exactly rand_sep places apart.  Lastly, it cycles the state
-   information a given number of times to get rid of any initial dependencies
-   introduced by the L.C.R.N.G.  Note that the initialization of randtbl[]
-   for default usage relies on values produced by this routine.  */
-/** init state.
- * @param s: state to init.
- * @param x: seed.
- */
-static void
-ub_srandom (struct ub_randstate* s, unsigned int x)
-{
-  s->state[0] = (long int)x;
-  if (s->rand_type != TYPE_0)
-    {
-      register long int i;
-      for (i = 1; i < s->rand_deg; ++i)
-       s->state[i] = (1103515145 * s->state[i - 1]) + 12345;
-      s->fptr = &s->state[s->rand_sep];
-      s->rptr = &s->state[0];
-      for (i = 0; i < 10 * s->rand_deg; ++i)
-       (void)ub_random(s);
-    }
+       s->rc4_ready = REKEY_BYTES;
 }
 
-/* Initialize the state information in the given array of N bytes for
-   future random number generation.  Based on the number of bytes we
-   are given, and the break values for the different R.N.G.'s, we choose
-   the best (largest) one we can and set things up for it.  srandom is
-   then called to initialize the state information.  Note that on return
-   from srandom, we set state[-1] to be the type multiplexed with the current
-   value of the rear pointer; this is so successive calls to initstate won't
-   lose this information and will be able to restart with setstate.
-   Note: The first thing we do is save the current state, if any, just like
-   setstate so that it doesn't matter when initstate is called.
-   Returns a pointer to the old state.  */
-int
-ub_initstate (unsigned int seed, struct ub_randstate* state, unsigned long n)
+int 
+ub_initstate(unsigned int ATTR_UNUSED(seed), struct ub_randstate* state, 
+       unsigned long ATTR_UNUSED(n))
 {
-  memset(state, 0, sizeof(state));
-  state->state = calloc(1, n);
-  if(!state->state)
-         return 0;
-
-  if (n < BREAK_1)
-    {
-      if (n < BREAK_0)
-       {
-         errno = EINVAL;
-         return 0;
+       state->s = calloc(1, sizeof(*state->s));
+       if(!state->s) {
+               log_err("malloc failure in random init");
+               return 0;
        }
-      state->rand_type = TYPE_0;
-      state->rand_deg = DEG_0;
-      state->rand_sep = SEP_0;
-    }
-  else if (n < BREAK_2)
-    {
-      state->rand_type = TYPE_1;
-      state->rand_deg = DEG_1;
-      state->rand_sep = SEP_1;
-    }
-  else if (n < BREAK_3)
-    {
-      state->rand_type = TYPE_2;
-      state->rand_deg = DEG_2;
-      state->rand_sep = SEP_2;
-    }
-  else if (n < BREAK_4)
-    {
-      state->rand_type = TYPE_3;
-      state->rand_deg = DEG_3;
-      state->rand_sep = SEP_3;
-    }
-  else
-    {
-      state->rand_type = TYPE_4;
-      state->rand_deg = DEG_4;
-      state->rand_sep = SEP_4;
-    }
 
-  /* Must set END_PTR before srandom.  */
-  state->end_ptr = &state->state[state->rand_deg];
-  ub_srandom(state, seed);
-  /*
-  if (state->rand_type == TYPE_0)
-    state->state[-1] = state->rand_type;
-  else
-    state->state[-1] = (MAX_TYPES * (state->rptr - state->state)) + state->rand_type;
-    */
-
-  return 1;
+       /* RAND_ is threadsafe, by the way */
+       if(!RAND_status()) {
+               log_err("Random generator has no entropy (error %ld)",
+                   ERR_get_error());
+               return 0;
+       }
+       ub_arc4random_stir(state->s);
+       return 1;
 }
 
-/* If we are using the trivial TYPE_0 R.N.G., just do the old linear
-   congruential bit.  Otherwise, we do our fancy trinomial stuff, which is the
-   same in all ther other cases due to all the global variables that have been
-   set up.  The basic operation is to add the number at the rear pointer into
-   the one at the front pointer.  Then both pointers are advanced to the next
-   location cyclically in the table.  The value returned is the sum generated,
-   reduced to 31 bits by throwing away the "least random" low bit.
-   Note: The code takes advantage of the fact that both the front and
-   rear pointers can't wrap on the same call by not testing the rear
-   pointer if the front one has wrapped.  Returns a 31-bit random number.  */
-
-long int
-ub_random (struct ub_randstate* s)
+long int 
+ub_random(struct ub_randstate* state)
 {
-  if (s->rand_type == TYPE_0)
-    {
-      s->state[0] = ((s->state[0] * 1103515245) + 12345) & LONG_MAX;
-      return s->state[0];
-    }
-  else
-    {
-      long int i;
-      *s->fptr += *s->rptr;
-      /* Chucking least random bit.  */
-      i = (*s->fptr >> 1) & LONG_MAX;
-      ++s->fptr;
-      if (s->fptr >= s->end_ptr)
-       {
-         s->fptr = s->state;
-         ++s->rptr;
+       unsigned int r = 0;
+       if (state->s->rc4_ready <= 0) {
+               ub_arc4random_stir(state->s);
        }
-      else
-       {
-         ++s->rptr;
-         if (s->rptr >= s->end_ptr)
-           s->rptr = s->state;
-       }
-      return i;
-    }
-}
 
+       RC4(&state->s->rc4, sizeof(r), 
+               (unsigned char *)&r, (unsigned char *)&r);
+       state->s->rc4_ready -= sizeof(r);
+       return (long int)((r) % (((unsigned)RAND_MAX + 1)));
+}
 
-void ub_randfree(struct ub_randstate* state)
+void 
+ub_randfree(struct ub_randstate* state)
 {
-       if(!state)
-               return;
-       free(state->state);
+       if(state)
+               free(state->s);
+       RAND_cleanup();
 }
index b0b3d1e3b001219c212b829e0d037c6e7c4e61fd..98b8883d0a828bfef88328874e9efab53a341d03 100644 (file)
 
 /**
  * \file
- * Thread safe random functions. Similar to random(3) and initstate(3).
+ * Thread safe random functions. Similar to arc4random() with an explicit
+ * initialisation routine.
  */
 
+struct ub_hiddenstate;
+
 /**
  * random state structure.
  */
 struct ub_randstate {
-       /** state array, malloced */
-       long int* state;
-       /** front ptr */
-       long int* fptr;
-       /** rear ptr */
-       long int* rptr;
-       /** rng type */
-       int rand_type;
-       /** rng degree */
-       int rand_deg;
-       /** rng sep */
-       int rand_sep;
-       /** rng end ptr */
-       long int* end_ptr;
+       /** state hidden type. */
+       struct ub_hiddenstate* s;
 };
 
 /**
  * Initialize a random generator state for use 
  * @param seed: seed value to create state contents.
+ *     (ignored for arc4random).
  * @param state: struct allocated by caller.
  * @param n: size of state->state. 8, 32, 64, 128, or 256 bytes.
+ *     (ignored for arc4random).
  * @return false alloc failure.
  */
 int ub_initstate(unsigned int seed, struct ub_randstate* state,