rng.c (2180B)
1 /* 2 * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2020, Stephen Gregoratto <dev@sgregoratto.me> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include "config.h" 18 19 #include <stdint.h> 20 21 #include "rng.h" 22 23 #if HAVE_ARC4RANDOM 24 #include <stdlib.h> 25 26 uint32_t 27 rng(void) 28 { 29 30 return arc4random(); 31 } 32 #else 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <time.h> 36 #include <unistd.h> 37 38 uint32_t 39 rng(void) 40 { 41 uint32_t num = 0; 42 int fd; 43 44 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { 45 (void)read(fd, &num, 4); 46 close(fd); 47 } else { 48 srandom(time(NULL)); 49 num = random(); 50 } 51 52 return num; 53 } 54 #endif 55 56 /* 57 * Calculate a uniformly distributed random number less than upper_bound 58 * avoiding "modulo bias". 59 * 60 * Uniformity is achieved by generating new random numbers until the one 61 * returned is outside the range [0, 2**32 % upper_bound). This 62 * guarantees the selected random number will be inside 63 * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) 64 * after reduction modulo upper_bound. 65 */ 66 uint32_t 67 rng_uniform(uint32_t upper_bound) 68 { 69 uint32_t r, min; 70 71 if (upper_bound < 2) 72 return 0; 73 74 /* 2**32 % x == (2**32 - x) % x */ 75 min = -upper_bound % upper_bound; 76 77 /* 78 * This could theoretically loop forever but each retry has 79 * p > 0.5 (worst case, usually far better) of selecting a 80 * number inside the range we need, so it should rarely need 81 * to re-roll. 82 */ 83 for (;;) { 84 r = rng(); 85 if (r >= min) 86 break; 87 } 88 89 return r % upper_bound; 90 }