1 /* 2 * Arc4 random number generator for OpenBSD. 3 * Copyright 1996 David Mazieres <dm (at) lcs.mit.edu>. 4 * 5 * Modification and redistribution in source and binary forms is 6 * permitted provided that due credit is given to the author and the 7 * OpenBSD project by leaving this copyright notice intact. 8 */ 9 10 /* 11 * This code is derived from section 17.1 of Applied Cryptography, 12 * second edition, which describes a stream cipher allegedly 13 * compatible with RSA Labs "RC4" cipher (the actual description of 14 * which is a trade secret). The same algorithm is used as a stream 15 * cipher called "arcfour" in Tatu Ylonen's ssh package. 16 * 17 * Here the stream cipher has been modified always to include the time 18 * when initializing the state. That makes it impossible to 19 * regenerate the same random sequence twice, so this can't be used 20 * for encryption, but will generate good random numbers. 21 * 22 * RC4 is a registered trademark of RSA Laboratories. 23 */ 24 25 #include <sys/time.h> 26 27 #include <fcntl.h> 28 #include <stdint.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 32 #include "arc4random.h" 33 34 struct arc4_stream { 35 uint8_t i; 36 uint8_t j; 37 uint8_t s[256]; 38 }; 39 40 static int rs_initialized; 41 static struct arc4_stream rs; 42 static int arc4_count; 43 44 static void 45 arc4_init(struct arc4_stream *as) 46 { 47 int n; 48 49 for (n = 0; n < 256; n++) 50 as->s[n] = n; 51 as->i = 0; 52 as->j = 0; 53 } 54 55 static void 56 arc4_addrandom(struct arc4_stream *as, unsigned char *dat, int datlen) 57 { 58 int n; 59 uint8_t si; 60 61 as->i--; 62 for (n = 0; n < 256; n++) { 63 as->i = (as->i + 1); 64 si = as->s[as->i]; 65 as->j = (as->j + si + dat[n % datlen]); 66 as->s[as->i] = as->s[as->j]; 67 as->s[as->j] = si; 68 } 69 as->j = as->i; 70 } 71 72 static uint8_t 73 arc4_getbyte(struct arc4_stream *as) 74 { 75 uint8_t si, sj; 76 77 as->i = (as->i + 1); 78 si = as->s[as->i]; 79 as->j = (as->j + si); 80 sj = as->s[as->j]; 81 as->s[as->i] = sj; 82 as->s[as->j] = si; 83 return (as->s[(si + sj) & 0xff]); 84 } 85 86 static uint32_t 87 arc4_getword(struct arc4_stream *as) 88 { 89 uint32_t val; 90 91 val = arc4_getbyte(as) << 24; 92 val |= arc4_getbyte(as) << 16; 93 val |= arc4_getbyte(as) << 8; 94 val |= arc4_getbyte(as); 95 return val; 96 } 97 98 static void 99 arc4_stir(struct arc4_stream *as) 100 { 101 int fd; 102 struct { 103 struct timeval tv; 104 unsigned int rnd[(128 - sizeof(struct timeval)) / 105 sizeof(unsigned int)]; 106 } rdat; 107 int n; 108 109 gettimeofday(&rdat.tv, NULL); 110 fd = open("/dev/urandom", O_RDONLY); 111 if (fd != -1) { 112 n = read(fd, rdat.rnd, sizeof(rdat.rnd)); 113 close(fd); 114 } 115 116 /* fd < 0? Ah, what the heck. We'll just take 117 * whatever was on the stack... */ 118 arc4_addrandom(as, (void *) &rdat, sizeof(rdat)); 119 120 /* 121 * Throw away the first N words of output, as suggested in the 122 * paper "Weaknesses in the Key Scheduling Algorithm of RC4" 123 * by Fluher, Mantin, and Shamir. (N = 256 in our case.) 124 */ 125 for (n = 0; n < 256 * 4; n++) 126 arc4_getbyte(as); 127 arc4_count = 1600000; 128 } 129 130 void 131 arc4random_stir() 132 { 133 134 if (!rs_initialized) { 135 arc4_init(&rs); 136 rs_initialized = 1; 137 } 138 arc4_stir(&rs); 139 } 140 141 void 142 arc4random_addrandom(unsigned char *dat, int datlen) 143 { 144 145 if (!rs_initialized) 146 arc4random_stir(); 147 arc4_addrandom(&rs, dat, datlen); 148 } 149 150 uint32_t 151 arc4random() 152 { 153 154 arc4_count -= 4; 155 if (!rs_initialized || arc4_count <= 0) 156 arc4random_stir(); 157 return arc4_getword(&rs); 158 } 159