xref: /freebsd/lib/libc/gen/arc4random.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /* $FreeBSD$ */
2 
3 /*
4  * Arc4 random number generator for OpenBSD.
5  * Copyright 1996 David Mazieres <dm@lcs.mit.edu>.
6  *
7  * Modification and redistribution in source and binary forms is
8  * permitted provided that due credit is given to the author and the
9  * OpenBSD project (for instance by leaving this copyright notice
10  * intact).
11  */
12 
13 /*
14  * This code is derived from section 17.1 of Applied Cryptography,
15  * second edition, which describes a stream cipher allegedly
16  * compatible with RSA Labs "RC4" cipher (the actual description of
17  * which is a trade secret).  The same algorithm is used as a stream
18  * cipher called "arcfour" in Tatu Ylonen's ssh package.
19  *
20  * Here the stream cipher has been modified always to include the time
21  * when initializing the state.  That makes it impossible to
22  * regenerate the same random sequence twice, so this can't be used
23  * for encryption, but will generate good random numbers.
24  *
25  * RC4 is a registered trademark of RSA Laboratories.
26  */
27 
28 #include "namespace.h"
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include "un-namespace.h"
35 
36 struct arc4_stream {
37 	u_int8_t i;
38 	u_int8_t j;
39 	u_int8_t s[256];
40 };
41 
42 static int rs_initialized;
43 static struct arc4_stream rs;
44 
45 static inline void
46 arc4_init(as)
47 	struct arc4_stream *as;
48 {
49 	int     n;
50 
51 	for (n = 0; n < 256; n++)
52 		as->s[n] = n;
53 	as->i = 0;
54 	as->j = 0;
55 }
56 
57 static inline void
58 arc4_addrandom(as, dat, datlen)
59 	struct arc4_stream *as;
60 	u_char *dat;
61 	int     datlen;
62 {
63 	int     n;
64 	u_int8_t si;
65 
66 	as->i--;
67 	for (n = 0; n < 256; n++) {
68 		as->i = (as->i + 1);
69 		si = as->s[as->i];
70 		as->j = (as->j + si + dat[n % datlen]);
71 		as->s[as->i] = as->s[as->j];
72 		as->s[as->j] = si;
73 	}
74 }
75 
76 static void
77 arc4_stir(as)
78 	struct arc4_stream *as;
79 {
80 	int     fd;
81 	struct {
82 		struct timeval tv;
83 		pid_t pid;
84 		u_int8_t rnd[128 - sizeof(struct timeval) - sizeof(pid_t)];
85 	}       rdat;
86 
87 	gettimeofday(&rdat.tv, NULL);
88 	rdat.pid = getpid();
89 	fd = _open("/dev/urandom", O_RDONLY, 0);
90 	if (fd >= 0) {
91 		(void) _read(fd, rdat.rnd, sizeof(rdat.rnd));
92 		_close(fd);
93 	}
94 	/* fd < 0?  Ah, what the heck. We'll just take whatever was on the
95 	 * stack... */
96 
97 	arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
98 }
99 
100 static inline u_int8_t
101 arc4_getbyte(as)
102 	struct arc4_stream *as;
103 {
104 	u_int8_t si, sj;
105 
106 	as->i = (as->i + 1);
107 	si = as->s[as->i];
108 	as->j = (as->j + si);
109 	sj = as->s[as->j];
110 	as->s[as->i] = sj;
111 	as->s[as->j] = si;
112 	return (as->s[(si + sj) & 0xff]);
113 }
114 
115 static inline u_int32_t
116 arc4_getword(as)
117 	struct arc4_stream *as;
118 {
119 	u_int32_t val;
120 	val = arc4_getbyte(as) << 24;
121 	val |= arc4_getbyte(as) << 16;
122 	val |= arc4_getbyte(as) << 8;
123 	val |= arc4_getbyte(as);
124 	return val;
125 }
126 
127 void
128 arc4random_stir()
129 {
130 	if (!rs_initialized) {
131 		arc4_init(&rs);
132 		rs_initialized = 1;
133 	}
134 	arc4_stir(&rs);
135 }
136 
137 void
138 arc4random_addrandom(dat, datlen)
139 	u_char *dat;
140 	int     datlen;
141 {
142 	if (!rs_initialized)
143 		arc4random_stir();
144 	arc4_addrandom(&rs, dat, datlen);
145 }
146 
147 u_int32_t
148 arc4random()
149 {
150 	if (!rs_initialized)
151 		arc4random_stir();
152 	return arc4_getword(&rs);
153 }
154 
155 #if 0
156 /*-------- Test code for i386 --------*/
157 #include <stdio.h>
158 #include <machine/pctr.h>
159 int
160 main(int argc, char **argv)
161 {
162 	const int iter = 1000000;
163 	int     i;
164 	pctrval v;
165 
166 	v = rdtsc();
167 	for (i = 0; i < iter; i++)
168 		arc4random();
169 	v = rdtsc() - v;
170 	v /= iter;
171 
172 	printf("%qd cycles\n", v);
173 }
174 #endif
175