From 9e732da50bba44fcfe4ea6d6a2e36d8357694bee Mon Sep 17 00:00:00 2001 From: Wouter Wijngaards Date: Wed, 17 Oct 2007 12:08:34 +0000 Subject: [PATCH] Arc4random. git-svn-id: file:///svn/unbound/trunk@683 be551aaa-1e26-0410-a405-d3ace91eadb9 --- Makefile.in | 2 +- doc/Changelog | 3 + testcode/unitmain.c | 22 +++ util/random.c | 437 +++++++++++--------------------------------- util/random.h | 23 +-- 5 files changed, 138 insertions(+), 349 deletions(-) diff --git a/Makefile.in b/Makefile.in index 49e30e7da..5eff514ac 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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 diff --git a/doc/Changelog b/doc/Changelog index 16a062d7d..5caeed211 100644 --- a/doc/Changelog +++ b/doc/Changelog @@ -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. diff --git a/testcode/unitmain.c b/testcode/unitmain.c index 1e2e9e971..72822ab6d 100644 --- a/testcode/unitmain.c +++ b/testcode/unitmain.c @@ -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= 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(); diff --git a/util/random.c b/util/random.c index 976b6a0e2..19a72834b 100644 --- a/util/random.c +++ b/util/random.c @@ -1,363 +1,134 @@ /* - * 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 +#include +#include -#include - -#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(); } diff --git a/util/random.h b/util/random.h index b0b3d1e3b..98b8883d0 100644 --- a/util/random.h +++ b/util/random.h @@ -38,34 +38,27 @@ /** * \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, -- 2.47.2