xref: /freebsd/lib/libc/gen/arc4random.c (revision c0b48470301d33cd14c7f50b2853fef221136285)
1*c0b48470SDavid Schultz /*	$OpenBSD: arc4random.c,v 1.22 2010/12/22 08:23:42 otto Exp $	*/
2*c0b48470SDavid 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"
36333fc21eSDavid E. O'Brien #include <sys/types.h>
37333fc21eSDavid E. O'Brien #include <sys/time.h>
3883a03b38SAndrey A. Chernov #include <stdlib.h>
3983a03b38SAndrey A. Chernov #include <fcntl.h>
4083a03b38SAndrey A. Chernov #include <unistd.h>
415295209eSBrian Feldman #include <pthread.h>
425295209eSBrian Feldman 
435295209eSBrian Feldman #include "libc_private.h"
44d201fe46SDaniel Eischen #include "un-namespace.h"
4583a03b38SAndrey A. Chernov 
46*c0b48470SDavid Schultz #ifdef __GNUC__
47*c0b48470SDavid Schultz #define inline __inline
48*c0b48470SDavid Schultz #else				/* !__GNUC__ */
49*c0b48470SDavid Schultz #define inline
50*c0b48470SDavid Schultz #endif				/* !__GNUC__ */
51*c0b48470SDavid Schultz 
5283a03b38SAndrey A. Chernov struct arc4_stream {
5383a03b38SAndrey A. Chernov 	u_int8_t i;
5483a03b38SAndrey A. Chernov 	u_int8_t j;
5583a03b38SAndrey A. Chernov 	u_int8_t s[256];
5683a03b38SAndrey A. Chernov };
5783a03b38SAndrey A. Chernov 
585295209eSBrian Feldman static pthread_mutex_t	arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
595295209eSBrian Feldman 
604220467eSAndrey A. Chernov #define	RANDOMDEV	"/dev/random"
6189538e75SAndrey A. Chernov #define	KEYSIZE		128
62*c0b48470SDavid Schultz #define	_ARC4_LOCK()						\
635295209eSBrian Feldman 	do {							\
645295209eSBrian Feldman 		if (__isthreaded)				\
655295209eSBrian Feldman 			_pthread_mutex_lock(&arc4random_mtx);	\
665295209eSBrian Feldman 	} while (0)
675295209eSBrian Feldman 
68*c0b48470SDavid Schultz #define	_ARC4_UNLOCK()						\
695295209eSBrian Feldman 	do {							\
705295209eSBrian Feldman 		if (__isthreaded)				\
715295209eSBrian Feldman 			_pthread_mutex_unlock(&arc4random_mtx);	\
725295209eSBrian Feldman 	} while (0)
735295209eSBrian Feldman 
7483a03b38SAndrey A. Chernov static struct arc4_stream rs;
755295209eSBrian Feldman static int rs_initialized;
765295209eSBrian Feldman static int rs_stired;
77f68412f9SAndrey A. Chernov static int arc4_count;
7883a03b38SAndrey A. Chernov 
79860c4e58SAndrey A. Chernov static inline u_int8_t arc4_getbyte(void);
80860c4e58SAndrey A. Chernov static void arc4_stir(void);
8160ce8b0eSDavid Schultz 
8283a03b38SAndrey A. Chernov static inline void
83860c4e58SAndrey A. Chernov arc4_init(void)
8483a03b38SAndrey A. Chernov {
8583a03b38SAndrey A. Chernov 	int     n;
8683a03b38SAndrey A. Chernov 
8783a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++)
88860c4e58SAndrey A. Chernov 		rs.s[n] = n;
89860c4e58SAndrey A. Chernov 	rs.i = 0;
90860c4e58SAndrey A. Chernov 	rs.j = 0;
9183a03b38SAndrey A. Chernov }
9283a03b38SAndrey A. Chernov 
9383a03b38SAndrey A. Chernov static inline void
94860c4e58SAndrey A. Chernov arc4_addrandom(u_char *dat, int datlen)
9583a03b38SAndrey A. Chernov {
9683a03b38SAndrey A. Chernov 	int     n;
9783a03b38SAndrey A. Chernov 	u_int8_t si;
9883a03b38SAndrey A. Chernov 
99860c4e58SAndrey A. Chernov 	rs.i--;
10083a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++) {
101860c4e58SAndrey A. Chernov 		rs.i = (rs.i + 1);
102860c4e58SAndrey A. Chernov 		si = rs.s[rs.i];
103860c4e58SAndrey A. Chernov 		rs.j = (rs.j + si + dat[n % datlen]);
104860c4e58SAndrey A. Chernov 		rs.s[rs.i] = rs.s[rs.j];
105860c4e58SAndrey A. Chernov 		rs.s[rs.j] = si;
10683a03b38SAndrey A. Chernov 	}
107860c4e58SAndrey A. Chernov 	rs.j = rs.i;
10883a03b38SAndrey A. Chernov }
10983a03b38SAndrey A. Chernov 
11083a03b38SAndrey A. Chernov static void
111860c4e58SAndrey A. Chernov arc4_stir(void)
11283a03b38SAndrey A. Chernov {
113*c0b48470SDavid Schultz 	int done, fd, i;
11483a03b38SAndrey A. Chernov 	struct {
11583a03b38SAndrey A. Chernov 		struct timeval	tv;
11683a03b38SAndrey A. Chernov 		pid_t		pid;
117*c0b48470SDavid Schultz 		u_char	 	rnd[KEYSIZE];
118913e28a4SAndrey A. Chernov 	} rdat;
11983a03b38SAndrey A. Chernov 
1206a05bf3aSAndrey A. Chernov 	fd = _open(RANDOMDEV, O_RDONLY, 0);
12189538e75SAndrey A. Chernov 	done = 0;
1226a05bf3aSAndrey A. Chernov 	if (fd >= 0) {
12389538e75SAndrey A. Chernov 		if (_read(fd, &rdat, KEYSIZE) == KEYSIZE)
12489538e75SAndrey A. Chernov 			done = 1;
12589538e75SAndrey A. Chernov 		(void)_close(fd);
126a08f5d95SAndrey A. Chernov 	}
12789538e75SAndrey A. Chernov 	if (!done) {
12889538e75SAndrey A. Chernov 		(void)gettimeofday(&rdat.tv, NULL);
12989538e75SAndrey A. Chernov 		rdat.pid = getpid();
13089538e75SAndrey A. Chernov 		/* We'll just take whatever was on the stack too... */
13189538e75SAndrey A. Chernov 	}
13283a03b38SAndrey A. Chernov 
13389538e75SAndrey A. Chernov 	arc4_addrandom((u_char *)&rdat, KEYSIZE);
13460ce8b0eSDavid Schultz 
13560ce8b0eSDavid Schultz 	/*
136*c0b48470SDavid Schultz 	 * Discard early keystream, as per recommendations in:
137*c0b48470SDavid Schultz 	 * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
13860ce8b0eSDavid Schultz 	 */
139*c0b48470SDavid Schultz 	for (i = 0; i < 1024; i++)
140860c4e58SAndrey A. Chernov 		(void)arc4_getbyte();
1410761bd1fSAndrey A. Chernov 	arc4_count = 1600000;
14283a03b38SAndrey A. Chernov }
14383a03b38SAndrey A. Chernov 
14483a03b38SAndrey A. Chernov static inline u_int8_t
145860c4e58SAndrey A. Chernov arc4_getbyte(void)
14683a03b38SAndrey A. Chernov {
14783a03b38SAndrey A. Chernov 	u_int8_t si, sj;
14883a03b38SAndrey A. Chernov 
149860c4e58SAndrey A. Chernov 	rs.i = (rs.i + 1);
150860c4e58SAndrey A. Chernov 	si = rs.s[rs.i];
151860c4e58SAndrey A. Chernov 	rs.j = (rs.j + si);
152860c4e58SAndrey A. Chernov 	sj = rs.s[rs.j];
153860c4e58SAndrey A. Chernov 	rs.s[rs.i] = sj;
154860c4e58SAndrey A. Chernov 	rs.s[rs.j] = si;
155860c4e58SAndrey A. Chernov 	return (rs.s[(si + sj) & 0xff]);
15683a03b38SAndrey A. Chernov }
15783a03b38SAndrey A. Chernov 
15883a03b38SAndrey A. Chernov static inline u_int32_t
159860c4e58SAndrey A. Chernov arc4_getword(void)
16083a03b38SAndrey A. Chernov {
16183a03b38SAndrey A. Chernov 	u_int32_t val;
162860c4e58SAndrey A. Chernov 	val = arc4_getbyte() << 24;
163860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 16;
164860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 8;
165860c4e58SAndrey A. Chernov 	val |= arc4_getbyte();
166*c0b48470SDavid Schultz 	return val;
16783a03b38SAndrey A. Chernov }
16883a03b38SAndrey A. Chernov 
1695295209eSBrian Feldman static void
1705295209eSBrian Feldman arc4_check_init(void)
17183a03b38SAndrey A. Chernov {
17283a03b38SAndrey A. Chernov 	if (!rs_initialized) {
173860c4e58SAndrey A. Chernov 		arc4_init();
1746a05bf3aSAndrey A. Chernov 		rs_initialized = 1;
17583a03b38SAndrey A. Chernov 	}
1765295209eSBrian Feldman }
1775295209eSBrian Feldman 
178bc6847e2SAndrey A. Chernov static inline void
1795295209eSBrian Feldman arc4_check_stir(void)
1805295209eSBrian Feldman {
181b6634bf8SAndrey A. Chernov 	if (!rs_stired || arc4_count <= 0) {
182860c4e58SAndrey A. Chernov 		arc4_stir();
1835295209eSBrian Feldman 		rs_stired = 1;
1845295209eSBrian Feldman 	}
1855295209eSBrian Feldman }
1865295209eSBrian Feldman 
1875295209eSBrian Feldman void
188cb4e06ebSXin LI arc4random_stir(void)
1895295209eSBrian Feldman {
190*c0b48470SDavid Schultz 	_ARC4_LOCK();
1915295209eSBrian Feldman 	arc4_check_init();
192860c4e58SAndrey A. Chernov 	arc4_stir();
1934220467eSAndrey A. Chernov 	rs_stired = 1;
194*c0b48470SDavid Schultz 	_ARC4_UNLOCK();
19583a03b38SAndrey A. Chernov }
19683a03b38SAndrey A. Chernov 
19783a03b38SAndrey A. Chernov void
198cb4e06ebSXin LI arc4random_addrandom(u_char *dat, int datlen)
19983a03b38SAndrey A. Chernov {
200*c0b48470SDavid Schultz 	_ARC4_LOCK();
2015295209eSBrian Feldman 	arc4_check_init();
2025295209eSBrian Feldman 	arc4_check_stir();
203860c4e58SAndrey A. Chernov 	arc4_addrandom(dat, datlen);
204*c0b48470SDavid Schultz 	_ARC4_UNLOCK();
20583a03b38SAndrey A. Chernov }
20683a03b38SAndrey A. Chernov 
20783a03b38SAndrey A. Chernov u_int32_t
208cb4e06ebSXin LI arc4random(void)
20983a03b38SAndrey A. Chernov {
210*c0b48470SDavid Schultz 	u_int32_t val;
211*c0b48470SDavid Schultz 	_ARC4_LOCK();
2125295209eSBrian Feldman 	arc4_check_init();
2135295209eSBrian Feldman 	arc4_check_stir();
214*c0b48470SDavid Schultz 	val = arc4_getword();
215b6634bf8SAndrey A. Chernov 	arc4_count -= 4;
216*c0b48470SDavid Schultz 	_ARC4_UNLOCK();
217*c0b48470SDavid Schultz 	return val;
21883a03b38SAndrey A. Chernov }
21983a03b38SAndrey A. Chernov 
220bc6847e2SAndrey A. Chernov void
221bc6847e2SAndrey A. Chernov arc4random_buf(void *_buf, size_t n)
222bc6847e2SAndrey A. Chernov {
223bc6847e2SAndrey A. Chernov 	u_char *buf = (u_char *)_buf;
224*c0b48470SDavid Schultz 	_ARC4_LOCK();
225bc6847e2SAndrey A. Chernov 	arc4_check_init();
226bc6847e2SAndrey A. Chernov 	while (n--) {
227bc6847e2SAndrey A. Chernov 		arc4_check_stir();
228860c4e58SAndrey A. Chernov 		buf[n] = arc4_getbyte();
229bc6847e2SAndrey A. Chernov 		arc4_count--;
230bc6847e2SAndrey A. Chernov 	}
231*c0b48470SDavid Schultz 	_ARC4_UNLOCK();
232bc6847e2SAndrey A. Chernov }
233bc6847e2SAndrey A. Chernov 
2346e4fe40aSAndrey A. Chernov /*
2356e4fe40aSAndrey A. Chernov  * Calculate a uniformly distributed random number less than upper_bound
2366e4fe40aSAndrey A. Chernov  * avoiding "modulo bias".
2376e4fe40aSAndrey A. Chernov  *
2386e4fe40aSAndrey A. Chernov  * Uniformity is achieved by generating new random numbers until the one
2396e4fe40aSAndrey A. Chernov  * returned is outside the range [0, 2**32 % upper_bound).  This
2406e4fe40aSAndrey A. Chernov  * guarantees the selected random number will be inside
2416e4fe40aSAndrey A. Chernov  * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
2426e4fe40aSAndrey A. Chernov  * after reduction modulo upper_bound.
2436e4fe40aSAndrey A. Chernov  */
2446e4fe40aSAndrey A. Chernov u_int32_t
2456e4fe40aSAndrey A. Chernov arc4random_uniform(u_int32_t upper_bound)
2466e4fe40aSAndrey A. Chernov {
2476e4fe40aSAndrey A. Chernov 	u_int32_t r, min;
2486e4fe40aSAndrey A. Chernov 
2496e4fe40aSAndrey A. Chernov 	if (upper_bound < 2)
250*c0b48470SDavid Schultz 		return 0;
25161d35b63SAndrey A. Chernov 
2526e4fe40aSAndrey A. Chernov #if (ULONG_MAX > 0xffffffffUL)
2536e4fe40aSAndrey A. Chernov 	min = 0x100000000UL % upper_bound;
2546e4fe40aSAndrey A. Chernov #else
2556e4fe40aSAndrey A. Chernov 	/* Calculate (2**32 % upper_bound) avoiding 64-bit math */
2566e4fe40aSAndrey A. Chernov 	if (upper_bound > 0x80000000)
2576e4fe40aSAndrey A. Chernov 		min = 1 + ~upper_bound;		/* 2**32 - upper_bound */
2586e4fe40aSAndrey A. Chernov 	else {
2596e4fe40aSAndrey A. Chernov 		/* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
2606e4fe40aSAndrey A. Chernov 		min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
2616e4fe40aSAndrey A. Chernov 	}
2626e4fe40aSAndrey A. Chernov #endif
2636e4fe40aSAndrey A. Chernov 
2646e4fe40aSAndrey A. Chernov 	/*
2656e4fe40aSAndrey A. Chernov 	 * This could theoretically loop forever but each retry has
2666e4fe40aSAndrey A. Chernov 	 * p > 0.5 (worst case, usually far better) of selecting a
2676e4fe40aSAndrey A. Chernov 	 * number inside the range we need, so it should rarely need
2686e4fe40aSAndrey A. Chernov 	 * to re-roll.
2696e4fe40aSAndrey A. Chernov 	 */
2706e4fe40aSAndrey A. Chernov 	for (;;) {
2716e4fe40aSAndrey A. Chernov 		r = arc4random();
2726e4fe40aSAndrey A. Chernov 		if (r >= min)
2736e4fe40aSAndrey A. Chernov 			break;
2746e4fe40aSAndrey A. Chernov 	}
2756e4fe40aSAndrey A. Chernov 
276*c0b48470SDavid Schultz 	return r % upper_bound;
2776e4fe40aSAndrey A. Chernov }
2786e4fe40aSAndrey A. Chernov 
27983a03b38SAndrey A. Chernov #if 0
28083a03b38SAndrey A. Chernov /*-------- Test code for i386 --------*/
28183a03b38SAndrey A. Chernov #include <stdio.h>
28283a03b38SAndrey A. Chernov #include <machine/pctr.h>
28383a03b38SAndrey A. Chernov int
28483a03b38SAndrey A. Chernov main(int argc, char **argv)
28583a03b38SAndrey A. Chernov {
28683a03b38SAndrey A. Chernov 	const int iter = 1000000;
28783a03b38SAndrey A. Chernov 	int     i;
28883a03b38SAndrey A. Chernov 	pctrval v;
28983a03b38SAndrey A. Chernov 
29083a03b38SAndrey A. Chernov 	v = rdtsc();
29183a03b38SAndrey A. Chernov 	for (i = 0; i < iter; i++)
29283a03b38SAndrey A. Chernov 		arc4random();
29383a03b38SAndrey A. Chernov 	v = rdtsc() - v;
29483a03b38SAndrey A. Chernov 	v /= iter;
29583a03b38SAndrey A. Chernov 
29683a03b38SAndrey A. Chernov 	printf("%qd cycles\n", v);
29783a03b38SAndrey A. Chernov }
29883a03b38SAndrey A. Chernov #endif
299