xref: /freebsd/lib/libc/gen/arc4random.c (revision c1e80940f3b4030df0aaed73028053af057e476d)
1*c1e80940SXin LI /*	$OpenBSD: arc4random.c,v 1.54 2015/09/13 08:31:47 guenther Exp $	*/
2c0b48470SDavid Schultz 
383a03b38SAndrey A. Chernov /*
4860c4e58SAndrey A. Chernov  * Copyright (c) 1996, David Mazieres <dm@uun.org>
5860c4e58SAndrey A. Chernov  * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
6*c1e80940SXin LI  * Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
7*c1e80940SXin LI  * Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
883a03b38SAndrey A. Chernov  *
9860c4e58SAndrey A. Chernov  * Permission to use, copy, modify, and distribute this software for any
10860c4e58SAndrey A. Chernov  * purpose with or without fee is hereby granted, provided that the above
11860c4e58SAndrey A. Chernov  * copyright notice and this permission notice appear in all copies.
12860c4e58SAndrey A. Chernov  *
13860c4e58SAndrey A. Chernov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14860c4e58SAndrey A. Chernov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15860c4e58SAndrey A. Chernov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16860c4e58SAndrey A. Chernov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17860c4e58SAndrey A. Chernov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18860c4e58SAndrey A. Chernov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19860c4e58SAndrey A. Chernov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2083a03b38SAndrey A. Chernov  */
2183a03b38SAndrey A. Chernov 
2283a03b38SAndrey A. Chernov /*
23*c1e80940SXin LI  * ChaCha based random number generator for OpenBSD.
2483a03b38SAndrey A. Chernov  */
2583a03b38SAndrey A. Chernov 
26333fc21eSDavid E. O'Brien #include <sys/cdefs.h>
27333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
28333fc21eSDavid E. O'Brien 
29d201fe46SDaniel Eischen #include "namespace.h"
3083a03b38SAndrey A. Chernov #include <fcntl.h>
317a0789b4SDavid Schultz #include <limits.h>
325295209eSBrian Feldman #include <pthread.h>
33*c1e80940SXin LI #include <signal.h>
34*c1e80940SXin LI #include <stdint.h>
35*c1e80940SXin LI #include <stdlib.h>
36*c1e80940SXin LI #include <string.h>
37*c1e80940SXin LI #include <unistd.h>
38*c1e80940SXin LI #include <sys/types.h>
39*c1e80940SXin LI #include <sys/time.h>
405295209eSBrian Feldman 
415295209eSBrian Feldman #include "libc_private.h"
42d201fe46SDaniel Eischen #include "un-namespace.h"
4383a03b38SAndrey A. Chernov 
44*c1e80940SXin LI #define KEYSTREAM_ONLY
45*c1e80940SXin LI #include "chacha.c"
46*c1e80940SXin LI 
47*c1e80940SXin LI #define minimum(a, b) ((a) < (b) ? (a) : (b))
48*c1e80940SXin LI 
49*c1e80940SXin LI #if defined(__GNUC__) || defined(_MSC_VER)
50c0b48470SDavid Schultz #define inline __inline
51*c1e80940SXin LI #else				/* __GNUC__ || _MSC_VER */
52c0b48470SDavid Schultz #define inline
53*c1e80940SXin LI #endif				/* !__GNUC__ && !_MSC_VER */
54c0b48470SDavid Schultz 
55*c1e80940SXin LI #define KEYSZ	32
56*c1e80940SXin LI #define IVSZ	8
57*c1e80940SXin LI #define BLOCKSZ	64
58*c1e80940SXin LI #define RSBUFSZ	(16*BLOCKSZ)
5983a03b38SAndrey A. Chernov 
60*c1e80940SXin LI /* Marked INHERIT_ZERO, so zero'd out in fork children. */
61*c1e80940SXin LI static struct _rs {
62*c1e80940SXin LI 	size_t		rs_have;	/* valid bytes at end of rs_buf */
63*c1e80940SXin LI 	size_t		rs_count;	/* bytes till reseed */
64*c1e80940SXin LI } *rs;
655295209eSBrian Feldman 
66*c1e80940SXin LI /* Maybe be preserved in fork children, if _rs_allocate() decides. */
67*c1e80940SXin LI static struct _rsx {
68*c1e80940SXin LI 	chacha_ctx	rs_chacha;	/* chacha context for random keystream */
69*c1e80940SXin LI 	u_char		rs_buf[RSBUFSZ];	/* keystream blocks */
70*c1e80940SXin LI } *rsx;
715295209eSBrian Feldman 
72*c1e80940SXin LI static inline int _rs_allocate(struct _rs **, struct _rsx **);
73*c1e80940SXin LI static inline void _rs_forkdetect(void);
74*c1e80940SXin LI #include "arc4random.h"
755295209eSBrian Feldman 
76*c1e80940SXin LI static inline void _rs_rekey(u_char *dat, size_t datlen);
7760ce8b0eSDavid Schultz 
7883a03b38SAndrey A. Chernov static inline void
79*c1e80940SXin LI _rs_init(u_char *buf, size_t n)
8083a03b38SAndrey A. Chernov {
81*c1e80940SXin LI 	if (n < KEYSZ + IVSZ)
82*c1e80940SXin LI 		return;
8383a03b38SAndrey A. Chernov 
84*c1e80940SXin LI 	if (rs == NULL) {
85*c1e80940SXin LI 		if (_rs_allocate(&rs, &rsx) == -1)
8649a6e1baSEd Maste 			abort();
8749a6e1baSEd Maste 	}
8883a03b38SAndrey A. Chernov 
89*c1e80940SXin LI 	chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
90*c1e80940SXin LI 	chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ, NULL);
9183a03b38SAndrey A. Chernov }
9283a03b38SAndrey A. Chernov 
937a0789b4SDavid Schultz static void
94*c1e80940SXin LI _rs_stir(void)
957a0789b4SDavid Schultz {
96*c1e80940SXin LI 	u_char rnd[KEYSZ + IVSZ];
977a0789b4SDavid Schultz 
98*c1e80940SXin LI 	if (getentropy(rnd, sizeof rnd) == -1)
99*c1e80940SXin LI 		_getentropy_fail();
100*c1e80940SXin LI 
101*c1e80940SXin LI 	if (!rs)
102*c1e80940SXin LI 		_rs_init(rnd, sizeof(rnd));
103*c1e80940SXin LI 	else
104*c1e80940SXin LI 		_rs_rekey(rnd, sizeof(rnd));
105*c1e80940SXin LI 	explicit_bzero(rnd, sizeof(rnd));	/* discard source seed */
106*c1e80940SXin LI 
107*c1e80940SXin LI 	/* invalidate rs_buf */
108*c1e80940SXin LI 	rs->rs_have = 0;
109*c1e80940SXin LI 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
110*c1e80940SXin LI 
111*c1e80940SXin LI 	rs->rs_count = 1600000;
1127a0789b4SDavid Schultz }
1137a0789b4SDavid Schultz 
114*c1e80940SXin LI static inline void
115*c1e80940SXin LI _rs_stir_if_needed(size_t len)
11683a03b38SAndrey A. Chernov {
117*c1e80940SXin LI 	_rs_forkdetect();
118*c1e80940SXin LI 	if (!rs || rs->rs_count <= len)
119*c1e80940SXin LI 		_rs_stir();
120*c1e80940SXin LI 	if (rs->rs_count <= len)
121*c1e80940SXin LI 		rs->rs_count = 0;
122*c1e80940SXin LI 	else
123*c1e80940SXin LI 		rs->rs_count -= len;
12483a03b38SAndrey A. Chernov }
12583a03b38SAndrey A. Chernov 
126*c1e80940SXin LI static inline void
127*c1e80940SXin LI _rs_rekey(u_char *dat, size_t datlen)
12883a03b38SAndrey A. Chernov {
129*c1e80940SXin LI #ifndef KEYSTREAM_ONLY
130*c1e80940SXin LI 	memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
131*c1e80940SXin LI #endif
132*c1e80940SXin LI 	/* fill rs_buf with the keystream */
133*c1e80940SXin LI 	chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
134*c1e80940SXin LI 	    rsx->rs_buf, sizeof(rsx->rs_buf));
135*c1e80940SXin LI 	/* mix in optional user provided data */
136*c1e80940SXin LI 	if (dat) {
137*c1e80940SXin LI 		size_t i, m;
138*c1e80940SXin LI 
139*c1e80940SXin LI 		m = minimum(datlen, KEYSZ + IVSZ);
140*c1e80940SXin LI 		for (i = 0; i < m; i++)
141*c1e80940SXin LI 			rsx->rs_buf[i] ^= dat[i];
142*c1e80940SXin LI 	}
143*c1e80940SXin LI 	/* immediately reinit for backtracking resistance */
144*c1e80940SXin LI 	_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
145*c1e80940SXin LI 	memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
146*c1e80940SXin LI 	rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
14783a03b38SAndrey A. Chernov }
14883a03b38SAndrey A. Chernov 
149*c1e80940SXin LI static inline void
150*c1e80940SXin LI _rs_random_buf(void *_buf, size_t n)
151bc6847e2SAndrey A. Chernov {
152bc6847e2SAndrey A. Chernov 	u_char *buf = (u_char *)_buf;
153*c1e80940SXin LI 	u_char *keystream;
154*c1e80940SXin LI 	size_t m;
155*c1e80940SXin LI 
156*c1e80940SXin LI 	_rs_stir_if_needed(n);
157*c1e80940SXin LI 	while (n > 0) {
158*c1e80940SXin LI 		if (rs->rs_have > 0) {
159*c1e80940SXin LI 			m = minimum(n, rs->rs_have);
160*c1e80940SXin LI 			keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
161*c1e80940SXin LI 			    - rs->rs_have;
162*c1e80940SXin LI 			memcpy(buf, keystream, m);
163*c1e80940SXin LI 			memset(keystream, 0, m);
164*c1e80940SXin LI 			buf += m;
165*c1e80940SXin LI 			n -= m;
166*c1e80940SXin LI 			rs->rs_have -= m;
167bc6847e2SAndrey A. Chernov 		}
168*c1e80940SXin LI 		if (rs->rs_have == 0)
169*c1e80940SXin LI 			_rs_rekey(NULL, 0);
170*c1e80940SXin LI 	}
171*c1e80940SXin LI }
172*c1e80940SXin LI 
173*c1e80940SXin LI static inline void
174*c1e80940SXin LI _rs_random_u32(uint32_t *val)
175*c1e80940SXin LI {
176*c1e80940SXin LI 	u_char *keystream;
177*c1e80940SXin LI 
178*c1e80940SXin LI 	_rs_stir_if_needed(sizeof(*val));
179*c1e80940SXin LI 	if (rs->rs_have < sizeof(*val))
180*c1e80940SXin LI 		_rs_rekey(NULL, 0);
181*c1e80940SXin LI 	keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
182*c1e80940SXin LI 	memcpy(val, keystream, sizeof(*val));
183*c1e80940SXin LI 	memset(keystream, 0, sizeof(*val));
184*c1e80940SXin LI 	rs->rs_have -= sizeof(*val);
185*c1e80940SXin LI }
186*c1e80940SXin LI 
187*c1e80940SXin LI uint32_t
188*c1e80940SXin LI arc4random(void)
189*c1e80940SXin LI {
190*c1e80940SXin LI 	uint32_t val;
191*c1e80940SXin LI 
192*c1e80940SXin LI 	_ARC4_LOCK();
193*c1e80940SXin LI 	_rs_random_u32(&val);
194*c1e80940SXin LI 	_ARC4_UNLOCK();
195*c1e80940SXin LI 	return val;
196*c1e80940SXin LI }
197*c1e80940SXin LI 
198*c1e80940SXin LI void
199*c1e80940SXin LI arc4random_buf(void *buf, size_t n)
200*c1e80940SXin LI {
201*c1e80940SXin LI 	_ARC4_LOCK();
202*c1e80940SXin LI 	_rs_random_buf(buf, n);
203c0b48470SDavid Schultz 	_ARC4_UNLOCK();
204bc6847e2SAndrey A. Chernov }
205