xref: /freebsd/lib/libc/gen/arc4random.c (revision 49a6e1ba328e7d0d9c3743c3d0a9306c7f188edd)
1dad64c97SPedro F. Giffuni /*	$OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt 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>
683a03b38SAndrey A. Chernov  *
7860c4e58SAndrey A. Chernov  * Permission to use, copy, modify, and distribute this software for any
8860c4e58SAndrey A. Chernov  * purpose with or without fee is hereby granted, provided that the above
9860c4e58SAndrey A. Chernov  * copyright notice and this permission notice appear in all copies.
10860c4e58SAndrey A. Chernov  *
11860c4e58SAndrey A. Chernov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12860c4e58SAndrey A. Chernov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13860c4e58SAndrey A. Chernov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14860c4e58SAndrey A. Chernov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15860c4e58SAndrey A. Chernov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16860c4e58SAndrey A. Chernov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17860c4e58SAndrey A. Chernov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1883a03b38SAndrey A. Chernov  */
1983a03b38SAndrey A. Chernov 
2083a03b38SAndrey A. Chernov /*
21860c4e58SAndrey A. Chernov  * Arc4 random number generator for OpenBSD.
22860c4e58SAndrey A. Chernov  *
2383a03b38SAndrey A. Chernov  * This code is derived from section 17.1 of Applied Cryptography,
2483a03b38SAndrey A. Chernov  * second edition, which describes a stream cipher allegedly
2583a03b38SAndrey A. Chernov  * compatible with RSA Labs "RC4" cipher (the actual description of
2683a03b38SAndrey A. Chernov  * which is a trade secret).  The same algorithm is used as a stream
2783a03b38SAndrey A. Chernov  * cipher called "arcfour" in Tatu Ylonen's ssh package.
2883a03b38SAndrey A. Chernov  *
2983a03b38SAndrey A. Chernov  * RC4 is a registered trademark of RSA Laboratories.
3083a03b38SAndrey A. Chernov  */
3183a03b38SAndrey A. Chernov 
32333fc21eSDavid E. O'Brien #include <sys/cdefs.h>
33333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
34333fc21eSDavid E. O'Brien 
35d201fe46SDaniel Eischen #include "namespace.h"
3683a03b38SAndrey A. Chernov #include <fcntl.h>
377a0789b4SDavid Schultz #include <limits.h>
387a0789b4SDavid Schultz #include <stdlib.h>
3983a03b38SAndrey A. Chernov #include <unistd.h>
407a0789b4SDavid Schultz #include <sys/param.h>
4172de35d0SPawel Jakub Dawidek #include <sys/sysctl.h>
427a0789b4SDavid Schultz #include <sys/time.h>
435295209eSBrian Feldman #include <pthread.h>
445295209eSBrian Feldman 
455295209eSBrian Feldman #include "libc_private.h"
46d201fe46SDaniel Eischen #include "un-namespace.h"
4783a03b38SAndrey A. Chernov 
48c0b48470SDavid Schultz #ifdef __GNUC__
49c0b48470SDavid Schultz #define inline __inline
50c0b48470SDavid Schultz #else				/* !__GNUC__ */
51c0b48470SDavid Schultz #define inline
52c0b48470SDavid Schultz #endif				/* !__GNUC__ */
53c0b48470SDavid Schultz 
5483a03b38SAndrey A. Chernov struct arc4_stream {
5583a03b38SAndrey A. Chernov 	u_int8_t i;
5683a03b38SAndrey A. Chernov 	u_int8_t j;
5783a03b38SAndrey A. Chernov 	u_int8_t s[256];
5883a03b38SAndrey A. Chernov };
5983a03b38SAndrey A. Chernov 
605295209eSBrian Feldman static pthread_mutex_t	arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
615295209eSBrian Feldman 
624220467eSAndrey A. Chernov #define	RANDOMDEV	"/dev/random"
6389538e75SAndrey A. Chernov #define	KEYSIZE		128
64c0b48470SDavid Schultz #define	_ARC4_LOCK()						\
655295209eSBrian Feldman 	do {							\
665295209eSBrian Feldman 		if (__isthreaded)				\
675295209eSBrian Feldman 			_pthread_mutex_lock(&arc4random_mtx);	\
685295209eSBrian Feldman 	} while (0)
695295209eSBrian Feldman 
70c0b48470SDavid Schultz #define	_ARC4_UNLOCK()						\
715295209eSBrian Feldman 	do {							\
725295209eSBrian Feldman 		if (__isthreaded)				\
735295209eSBrian Feldman 			_pthread_mutex_unlock(&arc4random_mtx);	\
745295209eSBrian Feldman 	} while (0)
755295209eSBrian Feldman 
765295209eSBrian Feldman static int rs_initialized;
777a0789b4SDavid Schultz static struct arc4_stream rs;
787a0789b4SDavid Schultz static pid_t arc4_stir_pid;
79f68412f9SAndrey A. Chernov static int arc4_count;
8083a03b38SAndrey A. Chernov 
8172de35d0SPawel Jakub Dawidek extern int __sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
8272de35d0SPawel Jakub Dawidek     void *newp, size_t newlen);
8372de35d0SPawel Jakub Dawidek 
84860c4e58SAndrey A. Chernov static inline u_int8_t arc4_getbyte(void);
85860c4e58SAndrey A. Chernov static void arc4_stir(void);
8660ce8b0eSDavid Schultz 
8783a03b38SAndrey A. Chernov static inline void
88860c4e58SAndrey A. Chernov arc4_init(void)
8983a03b38SAndrey A. Chernov {
9083a03b38SAndrey A. Chernov 	int     n;
9183a03b38SAndrey A. Chernov 
9283a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++)
93860c4e58SAndrey A. Chernov 		rs.s[n] = n;
94860c4e58SAndrey A. Chernov 	rs.i = 0;
95860c4e58SAndrey A. Chernov 	rs.j = 0;
9683a03b38SAndrey A. Chernov }
9783a03b38SAndrey A. Chernov 
9883a03b38SAndrey A. Chernov static inline void
99860c4e58SAndrey A. Chernov arc4_addrandom(u_char *dat, int datlen)
10083a03b38SAndrey A. Chernov {
10183a03b38SAndrey A. Chernov 	int     n;
10283a03b38SAndrey A. Chernov 	u_int8_t si;
10383a03b38SAndrey A. Chernov 
104860c4e58SAndrey A. Chernov 	rs.i--;
10583a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++) {
106860c4e58SAndrey A. Chernov 		rs.i = (rs.i + 1);
107860c4e58SAndrey A. Chernov 		si = rs.s[rs.i];
108860c4e58SAndrey A. Chernov 		rs.j = (rs.j + si + dat[n % datlen]);
109860c4e58SAndrey A. Chernov 		rs.s[rs.i] = rs.s[rs.j];
110860c4e58SAndrey A. Chernov 		rs.s[rs.j] = si;
11183a03b38SAndrey A. Chernov 	}
112860c4e58SAndrey A. Chernov 	rs.j = rs.i;
11383a03b38SAndrey A. Chernov }
11483a03b38SAndrey A. Chernov 
11572de35d0SPawel Jakub Dawidek static size_t
11672de35d0SPawel Jakub Dawidek arc4_sysctl(u_char *buf, size_t size)
11772de35d0SPawel Jakub Dawidek {
11872de35d0SPawel Jakub Dawidek 	int mib[2];
11972de35d0SPawel Jakub Dawidek 	size_t len, done;
12072de35d0SPawel Jakub Dawidek 
12172de35d0SPawel Jakub Dawidek 	mib[0] = CTL_KERN;
12272de35d0SPawel Jakub Dawidek 	mib[1] = KERN_ARND;
12372de35d0SPawel Jakub Dawidek 	done = 0;
12472de35d0SPawel Jakub Dawidek 
12572de35d0SPawel Jakub Dawidek 	do {
12672de35d0SPawel Jakub Dawidek 		len = size;
12772de35d0SPawel Jakub Dawidek 		if (__sysctl(mib, 2, buf, &len, NULL, 0) == -1)
12872de35d0SPawel Jakub Dawidek 			return (done);
12972de35d0SPawel Jakub Dawidek 		done += len;
13072de35d0SPawel Jakub Dawidek 		buf += len;
13172de35d0SPawel Jakub Dawidek 		size -= len;
13272de35d0SPawel Jakub Dawidek 	} while (size > 0);
13372de35d0SPawel Jakub Dawidek 
13472de35d0SPawel Jakub Dawidek 	return (done);
13572de35d0SPawel Jakub Dawidek }
13672de35d0SPawel Jakub Dawidek 
13783a03b38SAndrey A. Chernov static void
138860c4e58SAndrey A. Chernov arc4_stir(void)
13983a03b38SAndrey A. Chernov {
1405c1ea1fcSEd Maste 	u_char rdat[KEYSIZE];
1415c1ea1fcSEd Maste 	int i;
14283a03b38SAndrey A. Chernov 
1437a0789b4SDavid Schultz 	if (!rs_initialized) {
1447a0789b4SDavid Schultz 		arc4_init();
1457a0789b4SDavid Schultz 		rs_initialized = 1;
1467a0789b4SDavid Schultz 	}
147*49a6e1baSEd Maste 	if (arc4_sysctl(rdat, KEYSIZE) != KEYSIZE) {
148*49a6e1baSEd Maste 		/*
149*49a6e1baSEd Maste 		 * The sysctl cannot fail. If it does fail on some FreeBSD
150*49a6e1baSEd Maste 		 * derivative or after some future change, just abort so that
151*49a6e1baSEd Maste 		 * the problem will be found and fixed. abort is not normally
152*49a6e1baSEd Maste 		 * suitable for a library but makes sense here.
153*49a6e1baSEd Maste 		 */
154*49a6e1baSEd Maste 		abort();
155*49a6e1baSEd Maste 	}
15683a03b38SAndrey A. Chernov 
1575c1ea1fcSEd Maste 	arc4_addrandom(rdat, KEYSIZE);
15860ce8b0eSDavid Schultz 
15960ce8b0eSDavid Schultz 	/*
160c0b48470SDavid Schultz 	 * Discard early keystream, as per recommendations in:
161c0b48470SDavid Schultz 	 * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
16260ce8b0eSDavid Schultz 	 */
163c0b48470SDavid Schultz 	for (i = 0; i < 1024; i++)
164860c4e58SAndrey A. Chernov 		(void)arc4_getbyte();
1650761bd1fSAndrey A. Chernov 	arc4_count = 1600000;
16683a03b38SAndrey A. Chernov }
16783a03b38SAndrey A. Chernov 
1687a0789b4SDavid Schultz static void
1697a0789b4SDavid Schultz arc4_stir_if_needed(void)
1707a0789b4SDavid Schultz {
1717a0789b4SDavid Schultz 	pid_t pid = getpid();
1727a0789b4SDavid Schultz 
173dad64c97SPedro F. Giffuni 	if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {
1747a0789b4SDavid Schultz 		arc4_stir_pid = pid;
1757a0789b4SDavid Schultz 		arc4_stir();
1767a0789b4SDavid Schultz 	}
1777a0789b4SDavid Schultz }
1787a0789b4SDavid Schultz 
17983a03b38SAndrey A. Chernov static inline u_int8_t
180860c4e58SAndrey A. Chernov arc4_getbyte(void)
18183a03b38SAndrey A. Chernov {
18283a03b38SAndrey A. Chernov 	u_int8_t si, sj;
18383a03b38SAndrey A. Chernov 
184860c4e58SAndrey A. Chernov 	rs.i = (rs.i + 1);
185860c4e58SAndrey A. Chernov 	si = rs.s[rs.i];
186860c4e58SAndrey A. Chernov 	rs.j = (rs.j + si);
187860c4e58SAndrey A. Chernov 	sj = rs.s[rs.j];
188860c4e58SAndrey A. Chernov 	rs.s[rs.i] = sj;
189860c4e58SAndrey A. Chernov 	rs.s[rs.j] = si;
190860c4e58SAndrey A. Chernov 	return (rs.s[(si + sj) & 0xff]);
19183a03b38SAndrey A. Chernov }
19283a03b38SAndrey A. Chernov 
19383a03b38SAndrey A. Chernov static inline u_int32_t
194860c4e58SAndrey A. Chernov arc4_getword(void)
19583a03b38SAndrey A. Chernov {
19683a03b38SAndrey A. Chernov 	u_int32_t val;
197860c4e58SAndrey A. Chernov 	val = arc4_getbyte() << 24;
198860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 16;
199860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 8;
200860c4e58SAndrey A. Chernov 	val |= arc4_getbyte();
201c0b48470SDavid Schultz 	return val;
20283a03b38SAndrey A. Chernov }
20383a03b38SAndrey A. Chernov 
2045295209eSBrian Feldman void
205cb4e06ebSXin LI arc4random_stir(void)
2065295209eSBrian Feldman {
207c0b48470SDavid Schultz 	_ARC4_LOCK();
208860c4e58SAndrey A. Chernov 	arc4_stir();
209c0b48470SDavid Schultz 	_ARC4_UNLOCK();
21083a03b38SAndrey A. Chernov }
21183a03b38SAndrey A. Chernov 
21283a03b38SAndrey A. Chernov void
213cb4e06ebSXin LI arc4random_addrandom(u_char *dat, int datlen)
21483a03b38SAndrey A. Chernov {
215c0b48470SDavid Schultz 	_ARC4_LOCK();
2167a0789b4SDavid Schultz 	if (!rs_initialized)
2177a0789b4SDavid Schultz 		arc4_stir();
218860c4e58SAndrey A. Chernov 	arc4_addrandom(dat, datlen);
219c0b48470SDavid Schultz 	_ARC4_UNLOCK();
22083a03b38SAndrey A. Chernov }
22183a03b38SAndrey A. Chernov 
22283a03b38SAndrey A. Chernov u_int32_t
223cb4e06ebSXin LI arc4random(void)
22483a03b38SAndrey A. Chernov {
225c0b48470SDavid Schultz 	u_int32_t val;
226c0b48470SDavid Schultz 	_ARC4_LOCK();
227b6634bf8SAndrey A. Chernov 	arc4_count -= 4;
2287a0789b4SDavid Schultz 	arc4_stir_if_needed();
2297a0789b4SDavid Schultz 	val = arc4_getword();
230c0b48470SDavid Schultz 	_ARC4_UNLOCK();
231c0b48470SDavid Schultz 	return val;
23283a03b38SAndrey A. Chernov }
23383a03b38SAndrey A. Chernov 
234bc6847e2SAndrey A. Chernov void
235bc6847e2SAndrey A. Chernov arc4random_buf(void *_buf, size_t n)
236bc6847e2SAndrey A. Chernov {
237bc6847e2SAndrey A. Chernov 	u_char *buf = (u_char *)_buf;
238c0b48470SDavid Schultz 	_ARC4_LOCK();
2397a0789b4SDavid Schultz 	arc4_stir_if_needed();
240bc6847e2SAndrey A. Chernov 	while (n--) {
2417a0789b4SDavid Schultz 		if (--arc4_count <= 0)
2427a0789b4SDavid Schultz 			arc4_stir();
243860c4e58SAndrey A. Chernov 		buf[n] = arc4_getbyte();
244bc6847e2SAndrey A. Chernov 	}
245c0b48470SDavid Schultz 	_ARC4_UNLOCK();
246bc6847e2SAndrey A. Chernov }
247bc6847e2SAndrey A. Chernov 
2486e4fe40aSAndrey A. Chernov /*
2496e4fe40aSAndrey A. Chernov  * Calculate a uniformly distributed random number less than upper_bound
2506e4fe40aSAndrey A. Chernov  * avoiding "modulo bias".
2516e4fe40aSAndrey A. Chernov  *
2526e4fe40aSAndrey A. Chernov  * Uniformity is achieved by generating new random numbers until the one
2536e4fe40aSAndrey A. Chernov  * returned is outside the range [0, 2**32 % upper_bound).  This
2546e4fe40aSAndrey A. Chernov  * guarantees the selected random number will be inside
2556e4fe40aSAndrey A. Chernov  * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
2566e4fe40aSAndrey A. Chernov  * after reduction modulo upper_bound.
2576e4fe40aSAndrey A. Chernov  */
2586e4fe40aSAndrey A. Chernov u_int32_t
2596e4fe40aSAndrey A. Chernov arc4random_uniform(u_int32_t upper_bound)
2606e4fe40aSAndrey A. Chernov {
2616e4fe40aSAndrey A. Chernov 	u_int32_t r, min;
2626e4fe40aSAndrey A. Chernov 
2636e4fe40aSAndrey A. Chernov 	if (upper_bound < 2)
264c0b48470SDavid Schultz 		return 0;
26561d35b63SAndrey A. Chernov 
266dad64c97SPedro F. Giffuni 	/* 2**32 % x == (2**32 - x) % x */
267dad64c97SPedro F. Giffuni 	min = -upper_bound % upper_bound;
2686e4fe40aSAndrey A. Chernov 	/*
2696e4fe40aSAndrey A. Chernov 	 * This could theoretically loop forever but each retry has
2706e4fe40aSAndrey A. Chernov 	 * p > 0.5 (worst case, usually far better) of selecting a
2716e4fe40aSAndrey A. Chernov 	 * number inside the range we need, so it should rarely need
2726e4fe40aSAndrey A. Chernov 	 * to re-roll.
2736e4fe40aSAndrey A. Chernov 	 */
2746e4fe40aSAndrey A. Chernov 	for (;;) {
2756e4fe40aSAndrey A. Chernov 		r = arc4random();
2766e4fe40aSAndrey A. Chernov 		if (r >= min)
2776e4fe40aSAndrey A. Chernov 			break;
2786e4fe40aSAndrey A. Chernov 	}
2796e4fe40aSAndrey A. Chernov 
280c0b48470SDavid Schultz 	return r % upper_bound;
2816e4fe40aSAndrey A. Chernov }
2826e4fe40aSAndrey A. Chernov 
28383a03b38SAndrey A. Chernov #if 0
28483a03b38SAndrey A. Chernov /*-------- Test code for i386 --------*/
28583a03b38SAndrey A. Chernov #include <stdio.h>
28683a03b38SAndrey A. Chernov #include <machine/pctr.h>
28783a03b38SAndrey A. Chernov int
28883a03b38SAndrey A. Chernov main(int argc, char **argv)
28983a03b38SAndrey A. Chernov {
29083a03b38SAndrey A. Chernov 	const int iter = 1000000;
29183a03b38SAndrey A. Chernov 	int     i;
29283a03b38SAndrey A. Chernov 	pctrval v;
29383a03b38SAndrey A. Chernov 
29483a03b38SAndrey A. Chernov 	v = rdtsc();
29583a03b38SAndrey A. Chernov 	for (i = 0; i < iter; i++)
29683a03b38SAndrey A. Chernov 		arc4random();
29783a03b38SAndrey A. Chernov 	v = rdtsc() - v;
29883a03b38SAndrey A. Chernov 	v /= iter;
29983a03b38SAndrey A. Chernov 
30083a03b38SAndrey A. Chernov 	printf("%qd cycles\n", v);
30183a03b38SAndrey A. Chernov }
30283a03b38SAndrey A. Chernov #endif
303