]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/unbound/util/random.c
Merge from head
[FreeBSD/FreeBSD.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
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE 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 #include <time.h>
64 #ifdef HAVE_SSL
65 #include <openssl/rand.h>
66 #include <openssl/rc4.h>
67 #include <openssl/err.h>
68 #elif defined(HAVE_NSS)
69 /* nspr4 */
70 #include "prerror.h"
71 /* nss3 */
72 #include "secport.h"
73 #include "pk11pub.h"
74 #endif
75
76 /** 
77  * Max random value.  Similar to RAND_MAX, but more portable
78  * (mingw uses only 15 bits random).
79  */
80 #define MAX_VALUE 0x7fffffff
81
82 #ifdef HAVE_SSL
83 /**
84  * Struct with per-thread random state.
85  * Keeps SSL types away from the header file.
86  */
87 struct ub_randstate {
88         /** key used for arc4random generation */
89         RC4_KEY rc4;
90         /** keeps track of key usage */
91         int rc4_ready;
92 };
93
94 /** Size of key to use (must be multiple of 8) */
95 #define SEED_SIZE 24
96
97 /** Number of bytes to reseed after */
98 #define REKEY_BYTES     (1 << 24)
99
100 /* (re)setup system seed */
101 void
102 ub_systemseed(unsigned int seed)
103 {
104         /* RAND_ is threadsafe, by the way */
105         if(!RAND_status()) {
106                 /* try to seed it */
107                 unsigned char buf[256];
108                 unsigned int v = seed;
109                 size_t i;
110                 for(i=0; i<256/sizeof(seed); i++) {
111                         memmove(buf+i*sizeof(seed), &v, sizeof(seed));
112                         v = v*seed + (unsigned int)i;
113                 }
114                 RAND_seed(buf, 256);
115                 if(!RAND_status()) {
116                         log_err("Random generator has no entropy "
117                                 "(error %ld)", ERR_get_error());
118                 } else {
119                         verbose(VERB_OPS, "openssl has no entropy, "
120                                 "seeding with time and pid");
121                 }
122         }
123 }
124
125 /** reseed random generator */
126 static void
127 ub_arc4random_stir(struct ub_randstate* s, struct ub_randstate* from)
128 {
129         /* not as unsigned char, but longerint so that it is
130            aligned properly on alignment sensitive platforms */
131         uint64_t rand_buf[SEED_SIZE/sizeof(uint64_t)];
132         int i;
133
134         memset(&s->rc4, 0, sizeof(s->rc4));
135         memset(rand_buf, 0xc, sizeof(rand_buf));
136         if (from) {
137                 uint8_t* rbuf = (uint8_t*)rand_buf;
138                 for(i=0; i<SEED_SIZE; i++)
139                         rbuf[i] = (uint8_t)ub_random(from);
140         } else {
141                 if(!RAND_status())
142                         ub_systemseed((unsigned)getpid()^(unsigned)time(NULL));
143                 if (RAND_bytes((unsigned char*)rand_buf,
144                         (int)sizeof(rand_buf)) <= 0) {
145                         /* very unlikely that this happens, since we seeded
146                          * above, if it does; complain and keep going */
147                         log_err("Couldn't obtain random bytes (error %ld)",
148                                     ERR_get_error());
149                         s->rc4_ready = 256;
150                         return;
151                 }
152         }
153 #ifdef HAVE_FIPS_MODE
154         if(FIPS_mode()) {
155                 /* RC4 is not allowed, get some trustworthy randomness */
156                 /* double certainty here, this routine should not be
157                  * called in FIPS_mode */
158                 memset(rand_buf, 0, sizeof(rand_buf));
159                 s->rc4_ready = REKEY_BYTES;
160                 return;
161         }
162 #endif /* FIPS_MODE */
163         RC4_set_key(&s->rc4, SEED_SIZE, (unsigned char*)rand_buf);
164
165         /*
166          * Discard early keystream, as per recommendations in:
167          * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
168          */
169         for(i = 0; i <= 256; i += sizeof(rand_buf))
170                 RC4(&s->rc4, sizeof(rand_buf), (unsigned char*)rand_buf,
171                         (unsigned char*)rand_buf);
172
173         memset(rand_buf, 0, sizeof(rand_buf));
174
175         s->rc4_ready = REKEY_BYTES;
176 }
177
178 struct ub_randstate* 
179 ub_initstate(unsigned int seed, struct ub_randstate* from)
180 {
181         struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s));
182         if(!s) {
183                 log_err("malloc failure in random init");
184                 return NULL;
185         }
186         ub_systemseed(seed);
187 #ifdef HAVE_FIPS_MODE
188         if(!FIPS_mode())
189 #endif
190         ub_arc4random_stir(s, from);
191         return s;
192 }
193
194 long int 
195 ub_random(struct ub_randstate* s)
196 {
197         unsigned int r = 0;
198 #ifdef HAVE_FIPS_MODE
199         if(FIPS_mode()) {
200                 /* RC4 is not allowed, get some trustworthy randomness */
201                 /* we use pseudo bytes: it tries to return secure randomness
202                  * but returns 'something' if that fails.  We need something
203                  * else if it fails, because we cannot block here */
204                 if(RAND_pseudo_bytes((unsigned char*)&r, (int)sizeof(r))
205                         == -1) {
206                         log_err("FIPSmode, no arc4random but RAND failed "
207                                 "(error %ld)", ERR_get_error());
208                 }
209                 return (long int)((r) % (((unsigned)MAX_VALUE + 1)));
210         }
211 #endif /* FIPS_MODE */
212         if (s->rc4_ready <= 0) {
213                 ub_arc4random_stir(s, NULL);
214         }
215
216         RC4(&s->rc4, sizeof(r), 
217                 (unsigned char *)&r, (unsigned char *)&r);
218         s->rc4_ready -= sizeof(r);
219         return (long int)((r) % (((unsigned)MAX_VALUE + 1)));
220 }
221
222 #elif defined(HAVE_NSS)
223
224 /* not much to remember for NSS since we use its pk11_random, placeholder */
225 struct ub_randstate {
226         int ready;
227 };
228
229 void ub_systemseed(unsigned int ATTR_UNUSED(seed))
230 {
231 }
232
233 struct ub_randstate* ub_initstate(unsigned int ATTR_UNUSED(seed), 
234         struct ub_randstate* ATTR_UNUSED(from))
235 {
236         struct ub_randstate* s = (struct ub_randstate*)calloc(1, sizeof(*s));
237         if(!s) {
238                 log_err("malloc failure in random init");
239                 return NULL;
240         }
241         return s;
242 }
243
244 long int ub_random(struct ub_randstate* ATTR_UNUSED(state))
245 {
246         long int x;
247         /* random 31 bit value. */
248         SECStatus s = PK11_GenerateRandom((unsigned char*)&x, (int)sizeof(x));
249         if(s != SECSuccess) {
250                 log_err("PK11_GenerateRandom error: %s",
251                         PORT_ErrorToString(PORT_GetError()));
252         }
253         return x & MAX_VALUE;
254 }
255
256 #endif /* HAVE_SSL or HAVE_NSS */
257
258 long int
259 ub_random_max(struct ub_randstate* state, long int x)
260 {
261         /* make sure we fetch in a range that is divisible by x. ignore
262          * values from d .. MAX_VALUE, instead draw a new number */
263         long int d = MAX_VALUE - (MAX_VALUE % x); /* d is divisible by x */
264         long int v = ub_random(state);
265         while(d <= v)
266                 v = ub_random(state);
267         return (v % x);
268 }
269
270 void 
271 ub_randfree(struct ub_randstate* s)
272 {
273         if(s)
274                 free(s);
275         /* user app must do RAND_cleanup(); */
276 }