xref: /freebsd/contrib/unbound/compat/arc4random.c (revision 193d9e768ba63fcfb187cfd17f461f7d41345048)
1 /*      $OpenBSD: arc4random.c,v 1.41 2014/07/12 13:24:54 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 1996, David Mazieres <dm@uun.org>
5  * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
6  * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 #include "config.h"
21 
22 /*
23  * ChaCha based random number generator for OpenBSD.
24  */
25 
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <signal.h>
29 #ifdef HAVE_STDINT_H
30 #include <stdint.h>
31 #endif
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/time.h>
38 #ifndef UB_ON_WINDOWS
39 #include <sys/mman.h>
40 #endif
41 
42 #define KEYSTREAM_ONLY
43 #include "chacha_private.h"
44 
45 #define arc4_min(a, b) ((a) < (b) ? (a) : (b))
46 #ifdef __GNUC__
47 #define inline __inline
48 #else				/* !__GNUC__ */
49 #define inline
50 #endif				/* !__GNUC__ */
51 #ifndef MAP_ANON
52 #define MAP_ANON MAP_ANONYMOUS
53 #endif
54 
55 #define KEYSZ	32
56 #define IVSZ	8
57 #define BLOCKSZ	64
58 #define RSBUFSZ	(16*BLOCKSZ)
59 
60 /* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
61 static struct {
62 	size_t		rs_have;	/* valid bytes at end of rs_buf */
63 	size_t		rs_count;	/* bytes till reseed */
64 } *rs;
65 
66 /* Preserved in fork children. */
67 static struct {
68 	chacha_ctx	rs_chacha;	/* chacha context for random keystream */
69 	u_char		rs_buf[RSBUFSZ];	/* keystream blocks */
70 } *rsx;
71 
72 static inline void _rs_rekey(u_char *dat, size_t datlen);
73 
74 static inline void
75 _rs_init(u_char *buf, size_t n)
76 {
77 	if (n < KEYSZ + IVSZ)
78 		return;
79 
80 	if (rs == NULL) {
81 #ifndef UB_ON_WINDOWS
82 		if ((rs = mmap(NULL, sizeof(*rs), PROT_READ|PROT_WRITE,
83 		    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
84 			abort();
85 #ifdef MAP_INHERIT_ZERO
86 		if (minherit(rs, sizeof(*rs), MAP_INHERIT_ZERO) == -1)
87 			abort();
88 #endif
89 #else /* WINDOWS */
90 		rs = malloc(sizeof(*rs));
91 		if(!rs)
92 			abort();
93 #endif
94 	}
95 	if (rsx == NULL) {
96 #ifndef UB_ON_WINDOWS
97 		if ((rsx = mmap(NULL, sizeof(*rsx), PROT_READ|PROT_WRITE,
98 		    MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
99 			abort();
100 #else /* WINDOWS */
101 		rsx = malloc(sizeof(*rsx));
102 		if(!rsx)
103 			abort();
104 #endif
105 	}
106 
107 	chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8, 0);
108 	chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
109 }
110 
111 static void
112 _rs_stir(void)
113 {
114 	u_char rnd[KEYSZ + IVSZ];
115 
116 	if (getentropy(rnd, sizeof rnd) == -1) {
117 #ifdef SIGKILL
118 		raise(SIGKILL);
119 #else
120 		exit(9); /* windows */
121 #endif
122 	}
123 
124 	if (!rs)
125 		_rs_init(rnd, sizeof(rnd));
126 	else
127 		_rs_rekey(rnd, sizeof(rnd));
128 	explicit_bzero(rnd, sizeof(rnd));	/* discard source seed */
129 
130 	/* invalidate rs_buf */
131 	rs->rs_have = 0;
132 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
133 
134 	rs->rs_count = 1600000;
135 }
136 
137 static inline void
138 _rs_stir_if_needed(size_t len)
139 {
140 #ifndef MAP_INHERIT_ZERO
141 	static pid_t _rs_pid = 0;
142 	pid_t pid = getpid();
143 
144 	/* If a system lacks MAP_INHERIT_ZERO, resort to getpid() */
145 	if (_rs_pid == 0 || _rs_pid != pid) {
146 		_rs_pid = pid;
147 		if (rs)
148 			rs->rs_count = 0;
149 	}
150 #endif
151 	if (!rs || rs->rs_count <= len)
152 		_rs_stir();
153 	if (rs->rs_count <= len)
154 		rs->rs_count = 0;
155 	else
156 		rs->rs_count -= len;
157 }
158 
159 static inline void
160 _rs_rekey(u_char *dat, size_t datlen)
161 {
162 #ifndef KEYSTREAM_ONLY
163 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
164 #endif
165 	/* fill rs_buf with the keystream */
166 	chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
167 	    rsx->rs_buf, sizeof(rsx->rs_buf));
168 	/* mix in optional user provided data */
169 	if (dat) {
170 		size_t i, m;
171 
172 		m = arc4_min(datlen, KEYSZ + IVSZ);
173 		for (i = 0; i < m; i++)
174 			rsx->rs_buf[i] ^= dat[i];
175 	}
176 	/* immediately reinit for backtracking resistance */
177 	_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
178 	memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
179 	rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
180 }
181 
182 static inline void
183 _rs_random_buf(void *_buf, size_t n)
184 {
185 	u_char *buf = (u_char *)_buf;
186 	u_char *keystream;
187 	size_t m;
188 
189 	_rs_stir_if_needed(n);
190 	while (n > 0) {
191 		if (rs->rs_have > 0) {
192 			m = arc4_min(n, rs->rs_have);
193 			keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
194 			    - rs->rs_have;
195 			memcpy(buf, keystream, m);
196 			memset(keystream, 0, m);
197 			buf += m;
198 			n -= m;
199 			rs->rs_have -= m;
200 		}
201 		if (rs->rs_have == 0)
202 			_rs_rekey(NULL, 0);
203 	}
204 }
205 
206 static inline void
207 _rs_random_u32(uint32_t *val)
208 {
209 	u_char *keystream;
210 	_rs_stir_if_needed(sizeof(*val));
211 	if (rs->rs_have < sizeof(*val))
212 		_rs_rekey(NULL, 0);
213 	keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
214 	memcpy(val, keystream, sizeof(*val));
215 	memset(keystream, 0, sizeof(*val));
216 	rs->rs_have -= sizeof(*val);
217 }
218 
219 uint32_t
220 arc4random(void)
221 {
222 	uint32_t val;
223 
224 	_ARC4_LOCK();
225 	_rs_random_u32(&val);
226 	_ARC4_UNLOCK();
227 	return val;
228 }
229 
230 void
231 arc4random_buf(void *buf, size_t n)
232 {
233 	_ARC4_LOCK();
234 	_rs_random_buf(buf, n);
235 	_ARC4_UNLOCK();
236 }
237