Home | History | Annotate | Download | only in bionic
      1 /*      $OpenBSD: arc4random.c,v 1.19 2008/06/04 00:50:23 djm Exp $        */
      2 
      3 /*
      4  * Copyright (c) 1996, David Mazieres <dm (at) uun.org>
      5  * Copyright (c) 2008, Damien Miller <djm (at) openbsd.org>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 /*
     21  * Arc4 random number generator for OpenBSD.
     22  *
     23  * This code is derived from section 17.1 of Applied Cryptography,
     24  * second edition, which describes a stream cipher allegedly
     25  * compatible with RSA Labs "RC4" cipher (the actual description of
     26  * which is a trade secret).  The same algorithm is used as a stream
     27  * cipher called "arcfour" in Tatu Ylonen's ssh package.
     28  *
     29  * Here the stream cipher has been modified always to include the time
     30  * when initializing the state.  That makes it impossible to
     31  * regenerate the same random sequence twice, so this can't be used
     32  * for encryption, but will generate good random numbers.
     33  *
     34  * RC4 is a registered trademark of RSA Laboratories.
     35  */
     36 
     37 #include <fcntl.h>
     38 #include <limits.h>
     39 #include <stdlib.h>
     40 #include <unistd.h>
     41 #include <sys/types.h>
     42 #include <sys/param.h>
     43 #include <sys/time.h>
     44 #include "thread_private.h"
     45 
     46 /* BIONIC-BEGIN */
     47 /* this lock should protect the global variables in this file */
     48 static pthread_mutex_t  _arc4_lock = PTHREAD_MUTEX_INITIALIZER;
     49 #define  _ARC4_LOCK()      pthread_mutex_lock(&_arc4_lock)
     50 #define  _ARC4_UNLOCK()    pthread_mutex_unlock(&_arc4_lock)
     51 /* BIONIC-END */
     52 
     53 #ifdef __GNUC__
     54 #define inline __inline
     55 #else                           /* !__GNUC__ */
     56 #define inline
     57 #endif                          /* !__GNUC__ */
     58 
     59 struct arc4_stream {
     60         u_int8_t i;
     61         u_int8_t j;
     62         u_int8_t s[256];
     63 };
     64 
     65 static int rs_initialized;
     66 static struct arc4_stream rs;
     67 static pid_t arc4_stir_pid;
     68 static int arc4_count;
     69 
     70 static inline u_int8_t arc4_getbyte(void);
     71 
     72 static inline void
     73 arc4_init(void)
     74 {
     75         int     n;
     76 
     77         for (n = 0; n < 256; n++)
     78                 rs.s[n] = n;
     79         rs.i = 0;
     80         rs.j = 0;
     81 }
     82 
     83 static inline void
     84 arc4_addrandom(u_char *dat, int datlen)
     85 {
     86         int     n;
     87         u_int8_t si;
     88 
     89         rs.i--;
     90         for (n = 0; n < 256; n++) {
     91                 rs.i = (rs.i + 1);
     92                 si = rs.s[rs.i];
     93                 rs.j = (rs.j + si + dat[n % datlen]);
     94                 rs.s[rs.i] = rs.s[rs.j];
     95                 rs.s[rs.j] = si;
     96         }
     97         rs.j = rs.i;
     98 }
     99 
    100 static void
    101 arc4_stir(void)
    102 {
    103 #if 1  /* BIONIC-BEGIN */
    104 	int     i, fd;
    105 	union {
    106 		struct timeval tv;
    107 		u_int rnd[128 / sizeof(u_int)];
    108 	}       rdat;
    109 	int	n;
    110 
    111         if (!rs_initialized) {
    112                 arc4_init();
    113                 rs_initialized = 1;
    114         }
    115 
    116 	fd = open("/dev/urandom", O_RDONLY);
    117 	if (fd != -1) {
    118 		read(fd, rdat.rnd, sizeof(rdat.rnd));
    119 		close(fd);
    120 	}
    121         else
    122         {
    123 	    /* fd < 0 ?  Ah, what the heck. We'll just take
    124 	     * whatever was on the stack. just add a little more
    125              * time-based randomness though
    126              */
    127             gettimeofday(&rdat.tv, NULL);
    128         }
    129 
    130         arc4_stir_pid = getpid();
    131 	arc4_addrandom((void *) &rdat, sizeof(rdat));
    132 #else  /* BIONIC-END */
    133         int     i, mib[2];
    134         size_t        len;
    135         u_char rnd[128];
    136 
    137         if (!rs_initialized) {
    138                 arc4_init();
    139                 rs_initialized = 1;
    140         }
    141 
    142         mib[0] = CTL_KERN;
    143         mib[1] = KERN_ARND;
    144 
    145         len = sizeof(rnd);
    146         sysctl(mib, 2, rnd, &len, NULL, 0);
    147 
    148         arc4_stir_pid = getpid();
    149         arc4_addrandom(rnd, sizeof(rnd));
    150 #endif
    151         /*
    152          * Discard early keystream, as per recommendations in:
    153          * http://www.wisdom.weizmann.ac.il/~itsik/RC4/Papers/Rc4_ksa.ps
    154          */
    155         for (i = 0; i < 256; i++)
    156                 (void)arc4_getbyte();
    157         arc4_count = 1600000;
    158 }
    159 
    160 static inline u_int8_t
    161 arc4_getbyte(void)
    162 {
    163         u_int8_t si, sj;
    164 
    165         rs.i = (rs.i + 1);
    166         si = rs.s[rs.i];
    167         rs.j = (rs.j + si);
    168         sj = rs.s[rs.j];
    169         rs.s[rs.i] = sj;
    170         rs.s[rs.j] = si;
    171         return (rs.s[(si + sj) & 0xff]);
    172 }
    173 
    174 u_int8_t
    175 __arc4_getbyte(void)
    176 {
    177         u_int8_t val;
    178 
    179         _ARC4_LOCK();
    180         if (--arc4_count == 0 || !rs_initialized)
    181                 arc4_stir();
    182         val = arc4_getbyte();
    183         _ARC4_UNLOCK();
    184         return val;
    185 }
    186 
    187 static inline u_int32_t
    188 arc4_getword(void)
    189 {
    190         u_int32_t val;
    191         val = arc4_getbyte() << 24;
    192         val |= arc4_getbyte() << 16;
    193         val |= arc4_getbyte() << 8;
    194         val |= arc4_getbyte();
    195         return val;
    196 }
    197 
    198 void
    199 arc4random_stir(void)
    200 {
    201         _ARC4_LOCK();
    202         arc4_stir();
    203         _ARC4_UNLOCK();
    204 }
    205 
    206 void
    207 arc4random_addrandom(u_char *dat, int datlen)
    208 {
    209         _ARC4_LOCK();
    210         if (!rs_initialized)
    211                 arc4_stir();
    212         arc4_addrandom(dat, datlen);
    213         _ARC4_UNLOCK();
    214 }
    215 
    216 u_int32_t
    217 arc4random(void)
    218 {
    219         u_int32_t val;
    220         _ARC4_LOCK();
    221         arc4_count -= 4;
    222         if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != getpid())
    223                 arc4_stir();
    224         val = arc4_getword();
    225         _ARC4_UNLOCK();
    226         return val;
    227 }
    228 
    229 void
    230 arc4random_buf(void *_buf, size_t n)
    231 {
    232         u_char *buf = (u_char *)_buf;
    233         _ARC4_LOCK();
    234         if (!rs_initialized || arc4_stir_pid != getpid())
    235                 arc4_stir();
    236         while (n--) {
    237                 if (--arc4_count <= 0)
    238                         arc4_stir();
    239                 buf[n] = arc4_getbyte();
    240         }
    241         _ARC4_UNLOCK();
    242 }
    243 
    244 /*
    245  * Calculate a uniformly distributed random number less than upper_bound
    246  * avoiding "modulo bias".
    247  *
    248  * Uniformity is achieved by generating new random numbers until the one
    249  * returned is outside the range [0, 2**32 % upper_bound).  This
    250  * guarantees the selected random number will be inside
    251  * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
    252  * after reduction modulo upper_bound.
    253  */
    254 u_int32_t
    255 arc4random_uniform(u_int32_t upper_bound)
    256 {
    257         u_int32_t r, min;
    258 
    259         if (upper_bound < 2)
    260                 return 0;
    261 
    262 #if (ULONG_MAX > 0xffffffffUL)
    263         min = 0x100000000UL % upper_bound;
    264 #else
    265         /* Calculate (2**32 % upper_bound) avoiding 64-bit math */
    266         if (upper_bound > 0x80000000)
    267                 min = 1 + ~upper_bound;                /* 2**32 - upper_bound */
    268         else {
    269                 /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */
    270                 min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
    271         }
    272 #endif
    273 
    274         /*
    275          * This could theoretically loop forever but each retry has
    276          * p > 0.5 (worst case, usually far better) of selecting a
    277          * number inside the range we need, so it should rarely need
    278          * to re-roll.
    279          */
    280         for (;;) {
    281                 r = arc4random();
    282                 if (r >= min)
    283                         break;
    284         }
    285 
    286         return r % upper_bound;
    287 }
    288 
    289 #if 0
    290 /*-------- Test code for i386 --------*/
    291 #include <stdio.h>
    292 #include <machine/pctr.h>
    293 int
    294 main(int argc, char **argv)
    295 {
    296         const int iter = 1000000;
    297         int     i;
    298         pctrval v;
    299 
    300         v = rdtsc();
    301         for (i = 0; i < iter; i++)
    302                 arc4random();
    303         v = rdtsc() - v;
    304         v /= iter;
    305 
    306         printf("%qd cycles\n", v);
    307 }
    308 #endif
    309