xref: /freebsd/contrib/unbound/util/random.c (revision aeaed508982227551b2748339033bb2483382b4d)
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 }
277