arc4random.c revision 180672
1/*
2 * Copyright (c) 1996, David Mazieres <dm@uun.org>
3 * Copyright (c) 2008, Damien Miller <djm@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * Arc4 random number generator for OpenBSD.
20 *
21 * This code is derived from section 17.1 of Applied Cryptography,
22 * second edition, which describes a stream cipher allegedly
23 * compatible with RSA Labs "RC4" cipher (the actual description of
24 * which is a trade secret).  The same algorithm is used as a stream
25 * cipher called "arcfour" in Tatu Ylonen's ssh package.
26 *
27 * Here the stream cipher has been modified always to include the time
28 * when initializing the state.  That makes it impossible to
29 * regenerate the same random sequence twice, so this can't be used
30 * for encryption, but will generate good random numbers.
31 *
32 * RC4 is a registered trademark of RSA Laboratories.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/lib/libc/gen/arc4random.c 180672 2008-07-21 20:04:32Z ache $");
37
38#include "namespace.h"
39#include <sys/types.h>
40#include <sys/time.h>
41#include <stdlib.h>
42#include <fcntl.h>
43#include <unistd.h>
44#include <pthread.h>
45
46#include "libc_private.h"
47#include "un-namespace.h"
48
49struct arc4_stream {
50	u_int8_t i;
51	u_int8_t j;
52	u_int8_t s[256];
53};
54
55static pthread_mutex_t	arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
56
57#define	RANDOMDEV	"/dev/urandom"
58#define	THREAD_LOCK()						\
59	do {							\
60		if (__isthreaded)				\
61			_pthread_mutex_lock(&arc4random_mtx);	\
62	} while (0)
63
64#define	THREAD_UNLOCK()						\
65	do {							\
66		if (__isthreaded)				\
67			_pthread_mutex_unlock(&arc4random_mtx);	\
68	} while (0)
69
70static struct arc4_stream rs;
71static int rs_initialized;
72static int rs_stired;
73static int arc4_count;
74
75static inline u_int8_t arc4_getbyte(void);
76static void arc4_stir(void);
77
78static inline void
79arc4_init(void)
80{
81	int     n;
82
83	for (n = 0; n < 256; n++)
84		rs.s[n] = n;
85	rs.i = 0;
86	rs.j = 0;
87}
88
89static inline void
90arc4_addrandom(u_char *dat, int datlen)
91{
92	int     n;
93	u_int8_t si;
94
95	rs.i--;
96	for (n = 0; n < 256; n++) {
97		rs.i = (rs.i + 1);
98		si = rs.s[rs.i];
99		rs.j = (rs.j + si + dat[n % datlen]);
100		rs.s[rs.i] = rs.s[rs.j];
101		rs.s[rs.j] = si;
102	}
103	rs.j = rs.i;
104}
105
106static void
107arc4_stir(void)
108{
109	int     fd, n;
110	struct {
111		struct timeval tv;
112		pid_t pid;
113		u_int8_t rnd[128 - sizeof(struct timeval) - sizeof(pid_t)];
114	}       rdat;
115
116	gettimeofday(&rdat.tv, NULL);
117	rdat.pid = getpid();
118	fd = _open(RANDOMDEV, O_RDONLY, 0);
119	if (fd >= 0) {
120		(void)_read(fd, rdat.rnd, sizeof(rdat.rnd));
121		(void)_close(fd);
122	}
123	/* fd < 0?  Ah, what the heck. We'll just take whatever was on the
124	 * stack... */
125
126	arc4_addrandom((u_char *)&rdat, sizeof(rdat));
127
128	/*
129	 * Throw away the first N bytes of output, as suggested in the
130	 * paper "Weaknesses in the Key Scheduling Algorithm of RC4"
131	 * by Fluher, Mantin, and Shamir.  N=1024 is based on
132	 * suggestions in the paper "(Not So) Random Shuffles of RC4"
133	 * by Ilya Mironov.
134	 */
135	for (n = 0; n < 1024; n++)
136		(void)arc4_getbyte();
137	arc4_count = 1600000;
138}
139
140static inline u_int8_t
141arc4_getbyte(void)
142{
143	u_int8_t si, sj;
144
145	rs.i = (rs.i + 1);
146	si = rs.s[rs.i];
147	rs.j = (rs.j + si);
148	sj = rs.s[rs.j];
149	rs.s[rs.i] = sj;
150	rs.s[rs.j] = si;
151
152	return (rs.s[(si + sj) & 0xff]);
153}
154
155static inline u_int32_t
156arc4_getword(void)
157{
158	u_int32_t val;
159
160	val = arc4_getbyte() << 24;
161	val |= arc4_getbyte() << 16;
162	val |= arc4_getbyte() << 8;
163	val |= arc4_getbyte();
164
165	return (val);
166}
167
168static void
169arc4_check_init(void)
170{
171	if (!rs_initialized) {
172		arc4_init();
173		rs_initialized = 1;
174	}
175}
176
177static inline void
178arc4_check_stir(void)
179{
180	if (!rs_stired || arc4_count <= 0) {
181		arc4_stir();
182		rs_stired = 1;
183	}
184}
185
186void
187arc4random_stir(void)
188{
189	THREAD_LOCK();
190	arc4_check_init();
191	arc4_stir();
192	rs_stired = 1;
193	THREAD_UNLOCK();
194}
195
196void
197arc4random_addrandom(u_char *dat, int datlen)
198{
199	THREAD_LOCK();
200	arc4_check_init();
201	arc4_check_stir();
202	arc4_addrandom(dat, datlen);
203	THREAD_UNLOCK();
204}
205
206u_int32_t
207arc4random(void)
208{
209	u_int32_t rnd;
210
211	THREAD_LOCK();
212	arc4_check_init();
213	arc4_check_stir();
214	rnd = arc4_getword();
215	arc4_count -= 4;
216	THREAD_UNLOCK();
217
218	return (rnd);
219}
220
221void
222arc4random_buf(void *_buf, size_t n)
223{
224	u_char *buf = (u_char *)_buf;
225
226	THREAD_LOCK();
227	arc4_check_init();
228	while (n--) {
229		arc4_check_stir();
230		buf[n] = arc4_getbyte();
231		arc4_count--;
232	}
233	THREAD_UNLOCK();
234}
235
236#if 0
237/*-------- Test code for i386 --------*/
238#include <stdio.h>
239#include <machine/pctr.h>
240int
241main(int argc, char **argv)
242{
243	const int iter = 1000000;
244	int     i;
245	pctrval v;
246
247	v = rdtsc();
248	for (i = 0; i < iter; i++)
249		arc4random();
250	v = rdtsc() - v;
251	v /= iter;
252
253	printf("%qd cycles\n", v);
254}
255#endif
256