rng.c (2311B)
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 #if HAVE_GETRANDOM 23 #include <sys/random.h> 24 uint32_t 25 rng(void) 26 { 27 uint32_t num = 0; 28 29 (void)getrandom(&num, 4, 0); 30 31 return num; 32 } 33 #elif HAVE_ARC4RANDOM 34 #include <stdlib.h> 35 36 uint32_t 37 rng(void) 38 { 39 40 return arc4random(); 41 } 42 #else 43 #include <fcntl.h> 44 #include <stdlib.h> 45 #include <time.h> 46 #include <unistd.h> 47 48 uint32_t 49 rng(void) 50 { 51 uint32_t num = 0; 52 int fd; 53 54 if ((fd = open("/dev/urandom", O_RDONLY)) != -1) { 55 (void)read(fd, &num, 4); 56 close(fd); 57 } else { 58 srandom(time(NULL)); 59 num = random(); 60 } 61 62 return num; 63 } 64 #endif 65 66 /* 67 * Calculate a uniformly distributed random number less than upper_bound 68 * avoiding "modulo bias". 69 * 70 * Uniformity is achieved by generating new random numbers until the one 71 * returned is outside the range [0, 2**32 % upper_bound). This 72 * guarantees the selected random number will be inside 73 * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) 74 * after reduction modulo upper_bound. 75 */ 76 uint32_t 77 rng_uniform(uint32_t upper_bound) 78 { 79 uint32_t r, min; 80 81 if (upper_bound < 2) 82 return 0; 83 84 /* 2**32 % x == (2**32 - x) % x */ 85 min = -upper_bound % upper_bound; 86 87 /* 88 * This could theoretically loop forever but each retry has 89 * p > 0.5 (worst case, usually far better) of selecting a 90 * number inside the range we need, so it should rarely need 91 * to re-roll. 92 */ 93 for (;;) { 94 r = rng(); 95 if (r >= min) 96 break; 97 } 98 99 return r % upper_bound; 100 }