]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/unbound/util/random.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / unbound / util / random.c
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 }