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