xref: /freebsd/lib/libc/gen/arc4random.c (revision 7a0789b485f0d3b395dbbbb2e38d2cddb1286986)
1c0b48470SDavid Schultz /*	$OpenBSD: arc4random.c,v 1.22 2010/12/22 08:23:42 otto 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>
37*7a0789b4SDavid Schultz #include <limits.h>
38*7a0789b4SDavid Schultz #include <stdlib.h>
3983a03b38SAndrey A. Chernov #include <unistd.h>
40*7a0789b4SDavid Schultz #include <sys/types.h>
41*7a0789b4SDavid Schultz #include <sys/param.h>
42*7a0789b4SDavid 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;
77*7a0789b4SDavid Schultz static struct arc4_stream rs;
78*7a0789b4SDavid Schultz static pid_t arc4_stir_pid;
79f68412f9SAndrey A. Chernov static int arc4_count;
8083a03b38SAndrey A. Chernov 
81860c4e58SAndrey A. Chernov static inline u_int8_t arc4_getbyte(void);
82860c4e58SAndrey A. Chernov static void arc4_stir(void);
8360ce8b0eSDavid Schultz 
8483a03b38SAndrey A. Chernov static inline void
85860c4e58SAndrey A. Chernov arc4_init(void)
8683a03b38SAndrey A. Chernov {
8783a03b38SAndrey A. Chernov 	int     n;
8883a03b38SAndrey A. Chernov 
8983a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++)
90860c4e58SAndrey A. Chernov 		rs.s[n] = n;
91860c4e58SAndrey A. Chernov 	rs.i = 0;
92860c4e58SAndrey A. Chernov 	rs.j = 0;
9383a03b38SAndrey A. Chernov }
9483a03b38SAndrey A. Chernov 
9583a03b38SAndrey A. Chernov static inline void
96860c4e58SAndrey A. Chernov arc4_addrandom(u_char *dat, int datlen)
9783a03b38SAndrey A. Chernov {
9883a03b38SAndrey A. Chernov 	int     n;
9983a03b38SAndrey A. Chernov 	u_int8_t si;
10083a03b38SAndrey A. Chernov 
101860c4e58SAndrey A. Chernov 	rs.i--;
10283a03b38SAndrey A. Chernov 	for (n = 0; n < 256; n++) {
103860c4e58SAndrey A. Chernov 		rs.i = (rs.i + 1);
104860c4e58SAndrey A. Chernov 		si = rs.s[rs.i];
105860c4e58SAndrey A. Chernov 		rs.j = (rs.j + si + dat[n % datlen]);
106860c4e58SAndrey A. Chernov 		rs.s[rs.i] = rs.s[rs.j];
107860c4e58SAndrey A. Chernov 		rs.s[rs.j] = si;
10883a03b38SAndrey A. Chernov 	}
109860c4e58SAndrey A. Chernov 	rs.j = rs.i;
11083a03b38SAndrey A. Chernov }
11183a03b38SAndrey A. Chernov 
11283a03b38SAndrey A. Chernov static void
113860c4e58SAndrey A. Chernov arc4_stir(void)
11483a03b38SAndrey A. Chernov {
115c0b48470SDavid Schultz 	int done, fd, i;
11683a03b38SAndrey A. Chernov 	struct {
11783a03b38SAndrey A. Chernov 		struct timeval	tv;
11883a03b38SAndrey A. Chernov 		pid_t		pid;
119c0b48470SDavid Schultz 		u_char	 	rnd[KEYSIZE];
120913e28a4SAndrey A. Chernov 	} rdat;
12183a03b38SAndrey A. Chernov 
122*7a0789b4SDavid Schultz 	if (!rs_initialized) {
123*7a0789b4SDavid Schultz 		arc4_init();
124*7a0789b4SDavid Schultz 		rs_initialized = 1;
125*7a0789b4SDavid Schultz 	}
1266a05bf3aSAndrey A. Chernov 	fd = _open(RANDOMDEV, O_RDONLY, 0);
12789538e75SAndrey A. Chernov 	done = 0;
1286a05bf3aSAndrey A. Chernov 	if (fd >= 0) {
12989538e75SAndrey A. Chernov 		if (_read(fd, &rdat, KEYSIZE) == KEYSIZE)
13089538e75SAndrey A. Chernov 			done = 1;
13189538e75SAndrey A. Chernov 		(void)_close(fd);
132a08f5d95SAndrey A. Chernov 	}
13389538e75SAndrey A. Chernov 	if (!done) {
13489538e75SAndrey A. Chernov 		(void)gettimeofday(&rdat.tv, NULL);
13589538e75SAndrey A. Chernov 		rdat.pid = getpid();
13689538e75SAndrey A. Chernov 		/* We'll just take whatever was on the stack too... */
13789538e75SAndrey A. Chernov 	}
13883a03b38SAndrey A. Chernov 
13989538e75SAndrey A. Chernov 	arc4_addrandom((u_char *)&rdat, KEYSIZE);
14060ce8b0eSDavid Schultz 
14160ce8b0eSDavid Schultz 	/*
142c0b48470SDavid Schultz 	 * Discard early keystream, as per recommendations in:
143c0b48470SDavid Schultz 	 * "(Not So) Random Shuffles of RC4" by Ilya Mironov.
14460ce8b0eSDavid Schultz 	 */
145c0b48470SDavid Schultz 	for (i = 0; i < 1024; i++)
146860c4e58SAndrey A. Chernov 		(void)arc4_getbyte();
1470761bd1fSAndrey A. Chernov 	arc4_count = 1600000;
14883a03b38SAndrey A. Chernov }
14983a03b38SAndrey A. Chernov 
150*7a0789b4SDavid Schultz static void
151*7a0789b4SDavid Schultz arc4_stir_if_needed(void)
152*7a0789b4SDavid Schultz {
153*7a0789b4SDavid Schultz 	pid_t pid = getpid();
154*7a0789b4SDavid Schultz 
155*7a0789b4SDavid Schultz 	if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid)
156*7a0789b4SDavid Schultz 	{
157*7a0789b4SDavid Schultz 		arc4_stir_pid = pid;
158*7a0789b4SDavid Schultz 		arc4_stir();
159*7a0789b4SDavid Schultz 	}
160*7a0789b4SDavid Schultz }
161*7a0789b4SDavid Schultz 
16283a03b38SAndrey A. Chernov static inline u_int8_t
163860c4e58SAndrey A. Chernov arc4_getbyte(void)
16483a03b38SAndrey A. Chernov {
16583a03b38SAndrey A. Chernov 	u_int8_t si, sj;
16683a03b38SAndrey A. Chernov 
167860c4e58SAndrey A. Chernov 	rs.i = (rs.i + 1);
168860c4e58SAndrey A. Chernov 	si = rs.s[rs.i];
169860c4e58SAndrey A. Chernov 	rs.j = (rs.j + si);
170860c4e58SAndrey A. Chernov 	sj = rs.s[rs.j];
171860c4e58SAndrey A. Chernov 	rs.s[rs.i] = sj;
172860c4e58SAndrey A. Chernov 	rs.s[rs.j] = si;
173860c4e58SAndrey A. Chernov 	return (rs.s[(si + sj) & 0xff]);
17483a03b38SAndrey A. Chernov }
17583a03b38SAndrey A. Chernov 
17683a03b38SAndrey A. Chernov static inline u_int32_t
177860c4e58SAndrey A. Chernov arc4_getword(void)
17883a03b38SAndrey A. Chernov {
17983a03b38SAndrey A. Chernov 	u_int32_t val;
180860c4e58SAndrey A. Chernov 	val = arc4_getbyte() << 24;
181860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 16;
182860c4e58SAndrey A. Chernov 	val |= arc4_getbyte() << 8;
183860c4e58SAndrey A. Chernov 	val |= arc4_getbyte();
184c0b48470SDavid Schultz 	return val;
18583a03b38SAndrey A. Chernov }
18683a03b38SAndrey A. Chernov 
1875295209eSBrian Feldman void
188cb4e06ebSXin LI arc4random_stir(void)
1895295209eSBrian Feldman {
190c0b48470SDavid Schultz 	_ARC4_LOCK();
191860c4e58SAndrey A. Chernov 	arc4_stir();
192c0b48470SDavid Schultz 	_ARC4_UNLOCK();
19383a03b38SAndrey A. Chernov }
19483a03b38SAndrey A. Chernov 
19583a03b38SAndrey A. Chernov void
196cb4e06ebSXin LI arc4random_addrandom(u_char *dat, int datlen)
19783a03b38SAndrey A. Chernov {
198c0b48470SDavid Schultz 	_ARC4_LOCK();
199*7a0789b4SDavid Schultz 	if (!rs_initialized)
200*7a0789b4SDavid Schultz 		arc4_stir();
201860c4e58SAndrey A. Chernov 	arc4_addrandom(dat, datlen);
202c0b48470SDavid Schultz 	_ARC4_UNLOCK();
20383a03b38SAndrey A. Chernov }
20483a03b38SAndrey A. Chernov 
20583a03b38SAndrey A. Chernov u_int32_t
206cb4e06ebSXin LI arc4random(void)
20783a03b38SAndrey A. Chernov {
208c0b48470SDavid Schultz 	u_int32_t val;
209c0b48470SDavid Schultz 	_ARC4_LOCK();
210b6634bf8SAndrey A. Chernov 	arc4_count -= 4;
211*7a0789b4SDavid Schultz 	arc4_stir_if_needed();
212*7a0789b4SDavid Schultz 	val = arc4_getword();
213c0b48470SDavid Schultz 	_ARC4_UNLOCK();
214c0b48470SDavid Schultz 	return val;
21583a03b38SAndrey A. Chernov }
21683a03b38SAndrey A. Chernov 
217bc6847e2SAndrey A. Chernov void
218bc6847e2SAndrey A. Chernov arc4random_buf(void *_buf, size_t n)
219bc6847e2SAndrey A. Chernov {
220bc6847e2SAndrey A. Chernov 	u_char *buf = (u_char *)_buf;
221c0b48470SDavid Schultz 	_ARC4_LOCK();
222*7a0789b4SDavid Schultz 	arc4_stir_if_needed();
223bc6847e2SAndrey A. Chernov 	while (n--) {
224*7a0789b4SDavid Schultz 		if (--arc4_count <= 0)
225*7a0789b4SDavid Schultz 			arc4_stir();
226860c4e58SAndrey A. Chernov 		buf[n] = arc4_getbyte();
227bc6847e2SAndrey A. Chernov 	}
228c0b48470SDavid Schultz 	_ARC4_UNLOCK();
229bc6847e2SAndrey A. Chernov }
230bc6847e2SAndrey A. Chernov 
2316e4fe40aSAndrey A. Chernov /*
2326e4fe40aSAndrey A. Chernov  * Calculate a uniformly distributed random number less than upper_bound
2336e4fe40aSAndrey A. Chernov  * avoiding "modulo bias".
2346e4fe40aSAndrey A. Chernov  *
2356e4fe40aSAndrey A. Chernov  * Uniformity is achieved by generating new random numbers until the one
2366e4fe40aSAndrey A. Chernov  * returned is outside the range [0, 2**32 % upper_bound).  This
2376e4fe40aSAndrey A. Chernov  * guarantees the selected random number will be inside
2386e4fe40aSAndrey A. Chernov  * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
2396e4fe40aSAndrey A. Chernov  * after reduction modulo upper_bound.
2406e4fe40aSAndrey A. Chernov  */
2416e4fe40aSAndrey A. Chernov u_int32_t
2426e4fe40aSAndrey A. Chernov arc4random_uniform(u_int32_t upper_bound)
2436e4fe40aSAndrey A. Chernov {
2446e4fe40aSAndrey A. Chernov 	u_int32_t r, min;
2456e4fe40aSAndrey A. Chernov 
2466e4fe40aSAndrey A. Chernov 	if (upper_bound < 2)
247c0b48470SDavid Schultz 		return 0;
24861d35b63SAndrey A. Chernov 
2496e4fe40aSAndrey A. Chernov #if (ULONG_MAX > 0xffffffffUL)
2506e4fe40aSAndrey A. Chernov 	min = 0x100000000UL % upper_bound;
2516e4fe40aSAndrey A. Chernov #else
2526e4fe40aSAndrey A. Chernov 	/* Calculate (2**32 % upper_bound) avoiding 64-bit math */
2536e4fe40aSAndrey A. Chernov 	if (upper_bound > 0x80000000)
2546e4fe40aSAndrey A. Chernov 		min = 1 + ~upper_bound;		/* 2**32 - upper_bound */
2556e4fe40aSAndrey A. Chernov 	else {
2566e4fe40aSAndrey A. Chernov 		/* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
2576e4fe40aSAndrey A. Chernov 		min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
2586e4fe40aSAndrey A. Chernov 	}
2596e4fe40aSAndrey A. Chernov #endif
2606e4fe40aSAndrey A. Chernov 
2616e4fe40aSAndrey A. Chernov 	/*
2626e4fe40aSAndrey A. Chernov 	 * This could theoretically loop forever but each retry has
2636e4fe40aSAndrey A. Chernov 	 * p > 0.5 (worst case, usually far better) of selecting a
2646e4fe40aSAndrey A. Chernov 	 * number inside the range we need, so it should rarely need
2656e4fe40aSAndrey A. Chernov 	 * to re-roll.
2666e4fe40aSAndrey A. Chernov 	 */
2676e4fe40aSAndrey A. Chernov 	for (;;) {
2686e4fe40aSAndrey A. Chernov 		r = arc4random();
2696e4fe40aSAndrey A. Chernov 		if (r >= min)
2706e4fe40aSAndrey A. Chernov 			break;
2716e4fe40aSAndrey A. Chernov 	}
2726e4fe40aSAndrey A. Chernov 
273c0b48470SDavid Schultz 	return r % upper_bound;
2746e4fe40aSAndrey A. Chernov }
2756e4fe40aSAndrey A. Chernov 
27683a03b38SAndrey A. Chernov #if 0
27783a03b38SAndrey A. Chernov /*-------- Test code for i386 --------*/
27883a03b38SAndrey A. Chernov #include <stdio.h>
27983a03b38SAndrey A. Chernov #include <machine/pctr.h>
28083a03b38SAndrey A. Chernov int
28183a03b38SAndrey A. Chernov main(int argc, char **argv)
28283a03b38SAndrey A. Chernov {
28383a03b38SAndrey A. Chernov 	const int iter = 1000000;
28483a03b38SAndrey A. Chernov 	int     i;
28583a03b38SAndrey A. Chernov 	pctrval v;
28683a03b38SAndrey A. Chernov 
28783a03b38SAndrey A. Chernov 	v = rdtsc();
28883a03b38SAndrey A. Chernov 	for (i = 0; i < iter; i++)
28983a03b38SAndrey A. Chernov 		arc4random();
29083a03b38SAndrey A. Chernov 	v = rdtsc() - v;
29183a03b38SAndrey A. Chernov 	v /= iter;
29283a03b38SAndrey A. Chernov 
29383a03b38SAndrey A. Chernov 	printf("%qd cycles\n", v);
29483a03b38SAndrey A. Chernov }
29583a03b38SAndrey A. Chernov #endif
296