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