1 /* 2 * util/random.c - thread safe random generator, which is reasonably secure. 3 * 4 * Copyright (c) 2007, NLnet Labs. All rights reserved. 5 * 6 * This software is open source. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * Redistributions of source code must retain the above copyright notice, 13 * this list of conditions and the following disclaimer. 14 * 15 * Redistributions in binary form must reproduce the above copyright notice, 16 * this list of conditions and the following disclaimer in the documentation 17 * and/or other materials provided with the distribution. 18 * 19 * Neither the name of the NLNET LABS nor the names of its contributors may 20 * be used to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 /** 37 * \file 38 * Thread safe random functions. Similar to arc4random() with an explicit 39 * initialisation routine. 40 * 41 * The code in this file is based on arc4random from 42 * openssh-4.0p1/openbsd-compat/bsd-arc4random.c 43 * That code is also BSD licensed. Here is their statement: 44 * 45 * Copyright (c) 1996, David Mazieres <dm@uun.org> 46 * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 47 * 48 * Permission to use, copy, modify, and distribute this software for any 49 * purpose with or without fee is hereby granted, provided that the above 50 * copyright notice and this permission notice appear in all copies. 51 * 52 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 53 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 54 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 55 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 56 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 57 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 58 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 59 */ 60 #include "config.h" 61 #include "util/random.h" 62 #include "util/log.h" 63 #ifdef HAVE_SSL 64 #include <openssl/rand.h> 65 #include <openssl/rc4.h> 66 #include <openssl/err.h> 67 #elif defined(HAVE_NSS) 68 /* nspr4 */ 69 #include "prerror.h" 70 /* nss3 */ 71 #include "secport.h" 72 #include "pk11pub.h" 73 #endif 74 75 /** 76 * Max random value. Similar to RAND_MAX, but more portable 77 * (mingw uses only 15 bits random). 78 */ 79 #define MAX_VALUE 0x7fffffff 80 81 #ifdef HAVE_SSL 82 /** 83 * Struct with per-thread random state. 84 * Keeps SSL types away from the header file. 85 */ 86 struct ub_randstate { 87 /** key used for arc4random generation */ 88 RC4_KEY rc4; 89 /** keeps track of key usage */ 90 int rc4_ready; 91 }; 92 93 /** Size of key to use (must be multiple of 8) */ 94 #define SEED_SIZE 24 95 96 /** Number of bytes to reseed after */ 97 #define REKEY_BYTES (1 << 24) 98 99 /* (re)setup system seed */ 100 void 101 ub_systemseed(unsigned int seed) 102 { 103 /* RAND_ is threadsafe, by the way */ 104 if(!RAND_status()) { 105 /* try to seed it */ 106 unsigned char buf[256]; 107 unsigned int v = seed; 108 size_t i; 109 for(i=0; i<256/sizeof(seed); i++) { 110 memmove(buf+i*sizeof(seed), &v, sizeof(seed)); 111 v = v*seed + (unsigned int)i; 112 } 113 RAND_seed(buf, 256); 114 if(!RAND_status()) { 115 log_err("Random generator has no entropy " 116 "(error %ld)", ERR_get_error()); 117 } else { 118 verbose(VERB_OPS, "openssl has no entropy, " 119 "seeding with time and pid"); 120 } 121 } 122 } 123 124 /** reseed random generator */ 125 static void 126 ub_arc4random_stir(struct ub_randstate* s, struct ub_randstate* from) 127 { 128 /* not as unsigned char, but longerint so that it is 129 aligned properly on alignment sensitive platforms */ 130 uint64_t rand_buf[SEED_SIZE/sizeof(uint64_t)]; 131 int i; 132 133 memset(&s->rc4, 0, sizeof(s->rc4)); 134 memset(rand_buf, 0xc, sizeof(rand_buf)); 135 if (from) { 136 uint8_t* rbuf = (uint8_t*)rand_buf; 137 for(i=0; i<SEED_SIZE; i++) 138 rbuf[i] = (uint8_t)ub_random(from); 139 } else { 140 if(!RAND_status()) 141 ub_systemseed((unsigned)getpid()^(unsigned)time(NULL)); 142 if (RAND_bytes((unsigned char*)rand_buf, 143 (int)sizeof(rand_buf)) <= 0) { 144 /* very unlikely that this happens, since we seeded 145 * above, if it does; complain and keep going */ 146 log_err("Couldn't obtain random bytes (error %ld)", 147 ERR_get_error()); 148 s->rc4_ready = 256; 149 return; 150 } 151 } 152 #ifdef HAVE_FIPS_MODE 153 if(FIPS_mode()) { 154 /* RC4 is not allowed, get some trustworthy randomness */ 155 /* double certainty here, this routine should not be 156 * called in FIPS_mode */ 157 memset(rand_buf, 0, sizeof(rand_buf)); 158 s->rc4_ready = REKEY_BYTES; 159 return; 160 } 161 #endif /* FIPS_MODE */ 162 RC4_set_key(&s->rc4, SEED_SIZE, (unsigned char*)rand_buf); 163 164 /* 165 * Discard early keystream, as per recommendations in: 166 * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps 167 */ 168 for(i = 0; i <= 256; i += sizeof(rand_buf)) 169 RC4(&s->rc4, sizeof(rand_buf), (unsigned char*)rand_buf, 170 (unsigned char*)rand_buf); 171 172 memset(rand_buf, 0, sizeof(rand_buf)); 173 174 s->rc4_ready = REKEY_BYTES; 175 } 176 177 struct ub_randstate* 178 ub_initstate(unsigned int seed, struct ub_randstate* from) 179 { 180 struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s)); 181 if(!s) { 182 log_err("malloc failure in random init"); 183 return NULL; 184 } 185 ub_systemseed(seed); 186 #ifdef HAVE_FIPS_MODE 187 if(!FIPS_mode()) 188 #endif 189 ub_arc4random_stir(s, from); 190 return s; 191 } 192 193 long int 194 ub_random(struct ub_randstate* s) 195 { 196 unsigned int r = 0; 197 #ifdef HAVE_FIPS_MODE 198 if(FIPS_mode()) { 199 /* RC4 is not allowed, get some trustworthy randomness */ 200 /* we use pseudo bytes: it tries to return secure randomness 201 * but returns 'something' if that fails. We need something 202 * else if it fails, because we cannot block here */ 203 if(RAND_pseudo_bytes((unsigned char*)&r, (int)sizeof(r)) 204 == -1) { 205 log_err("FIPSmode, no arc4random but RAND failed " 206 "(error %ld)", ERR_get_error()); 207 } 208 return (long int)((r) % (((unsigned)MAX_VALUE + 1))); 209 } 210 #endif /* FIPS_MODE */ 211 if (s->rc4_ready <= 0) { 212 ub_arc4random_stir(s, NULL); 213 } 214 215 RC4(&s->rc4, sizeof(r), 216 (unsigned char *)&r, (unsigned char *)&r); 217 s->rc4_ready -= sizeof(r); 218 return (long int)((r) % (((unsigned)MAX_VALUE + 1))); 219 } 220 221 #elif defined(HAVE_NSS) 222 223 /* not much to remember for NSS since we use its pk11_random, placeholder */ 224 struct ub_randstate { 225 int ready; 226 }; 227 228 void ub_systemseed(unsigned int ATTR_UNUSED(seed)) 229 { 230 } 231 232 struct ub_randstate* ub_initstate(unsigned int ATTR_UNUSED(seed), 233 struct ub_randstate* ATTR_UNUSED(from)) 234 { 235 struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s)); 236 if(!s) { 237 log_err("malloc failure in random init"); 238 return NULL; 239 } 240 return s; 241 } 242 243 long int ub_random(struct ub_randstate* ATTR_UNUSED(state)) 244 { 245 long int x; 246 /* random 31 bit value. */ 247 SECStatus s = PK11_GenerateRandom((unsigned char*)&x, (int)sizeof(x)); 248 if(s != SECSuccess) { 249 log_err("PK11_GenerateRandom error: %s", 250 PORT_ErrorToString(PORT_GetError())); 251 } 252 return x & MAX_VALUE; 253 } 254 255 #endif /* HAVE_SSL or HAVE_NSS */ 256 257 long int 258 ub_random_max(struct ub_randstate* state, long int x) 259 { 260 /* make sure we fetch in a range that is divisible by x. ignore 261 * values from d .. MAX_VALUE, instead draw a new number */ 262 long int d = MAX_VALUE - (MAX_VALUE % x); /* d is divisible by x */ 263 long int v = ub_random(state); 264 while(d <= v) 265 v = ub_random(state); 266 return (v % x); 267 } 268 269 void 270 ub_randfree(struct ub_randstate* s) 271 { 272 if(s) 273 free(s); 274 /* user app must do RAND_cleanup(); */ 275 } 276