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 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 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 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 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 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 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 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 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