random.c revision 1.3
1/* $NetBSD: random.c,v 1.3 2019/01/09 16:55:14 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * See the COPYRIGHT file distributed with this work for additional 11 * information regarding copyright ownership. 12 */ 13 14/* 15 * Portions of isc_random_uniform(): 16 * 17 * Copyright (c) 1996, David Mazieres <dm@uun.org> 18 * Copyright (c) 2008, Damien Miller <djm@openbsd.org> 19 * 20 * Permission to use, copy, modify, and distribute this software for any 21 * purpose with or without fee is hereby granted, provided that the above 22 * copyright notice and this permission notice appear in all copies. 23 * 24 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 25 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 26 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 27 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 28 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 29 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 30 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 31 */ 32 33#include <config.h> 34 35#include <inttypes.h> 36#include <stdlib.h> 37#include <string.h> 38#include <unistd.h> 39 40#include <isc/platform.h> 41#include <isc/random.h> 42#include <isc/result.h> 43#include <isc/types.h> 44#include <isc/util.h> 45 46#include <isc/once.h> 47 48#include "entropy_private.h" 49 50/* 51 * The specific implementation for PRNG is included as a C file 52 * that has to provide a static variable named seed, and a function 53 * uint32_t next(void) that provides next random number. 54 * 55 * The implementation must be thread-safe. 56 */ 57 58/* 59 * Two contestants have been considered: the xoroshiro family of the 60 * functions by Villa&Blackman, and PCG by O'Neill. After 61 * consideration, the xoshiro128starstar function has been chosen as 62 * the uint32_t random number provider because it is very fast and has 63 * good enough properties for our usage pattern. 64 */ 65#include "xoshiro128starstar.c" 66 67#if defined(HAVE_TLS) 68#if defined(HAVE_THREAD_LOCAL) 69#include <threads.h> 70static thread_local isc_once_t isc_random_once = ISC_ONCE_INIT; 71#elif defined(HAVE___THREAD) 72static __thread isc_once_t isc_random_once = ISC_ONCE_INIT; 73#elif defined(HAVE___DECLSPEC_THREAD) 74static __declspec( thread ) isc_once_t isc_random_once = ISC_ONCE_INIT; 75#else 76#error "Unknown method for defining a TLS variable!" 77#endif 78#else 79static isc_once_t isc_random_once = ISC_ONCE_INIT; 80#endif 81 82static void 83isc_random_initialize(void) { 84 int useed[4] = {0,0,0,1}; 85#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION 86 /* 87 * Set a constant seed to help in problem reproduction should fuzzing 88 * find a crash or a hang. The seed array must be non-zero else 89 * xoshiro128starstar will generate an infinite series of zeroes. 90 */ 91#else 92 isc_entropy_get(useed, sizeof(useed)); 93#endif 94 memcpy(seed, useed, sizeof(seed)); 95} 96 97uint8_t 98isc_random8(void) { 99 RUNTIME_CHECK(isc_once_do(&isc_random_once, 100 isc_random_initialize) == ISC_R_SUCCESS); 101 return (next() & 0xff); 102} 103 104uint16_t 105isc_random16(void) { 106 RUNTIME_CHECK(isc_once_do(&isc_random_once, 107 isc_random_initialize) == ISC_R_SUCCESS); 108 return (next() & 0xffff); 109} 110 111uint32_t 112isc_random32(void) { 113 RUNTIME_CHECK(isc_once_do(&isc_random_once, 114 isc_random_initialize) == ISC_R_SUCCESS); 115 return (next()); 116} 117 118void 119isc_random_buf(void *buf, size_t buflen) { 120 int i; 121 uint32_t r; 122 123 REQUIRE(buf != NULL); 124 REQUIRE(buflen > 0); 125 126 RUNTIME_CHECK(isc_once_do(&isc_random_once, 127 isc_random_initialize) == ISC_R_SUCCESS); 128 129 for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) { 130 r = next(); 131 memmove((uint8_t *)buf + i, &r, sizeof(r)); 132 } 133 r = next(); 134 memmove((uint8_t *)buf + i, &r, buflen % sizeof(r)); 135 return; 136} 137 138uint32_t 139isc_random_uniform(uint32_t upper_bound) { 140 /* Copy of arc4random_uniform from OpenBSD */ 141 uint32_t r, min; 142 143 RUNTIME_CHECK(isc_once_do(&isc_random_once, 144 isc_random_initialize) == ISC_R_SUCCESS); 145 146 if (upper_bound < 2) { 147 return (0); 148 } 149 150#if (ULONG_MAX > 0xffffffffUL) 151 min = 0x100000000UL % upper_bound; 152#else /* if (ULONG_MAX > 0xffffffffUL) */ 153 /* Calculate (2**32 % upper_bound) avoiding 64-bit math */ 154 if (upper_bound > 0x80000000) { 155 min = 1 + ~upper_bound; /* 2**32 - upper_bound */ 156 } else { 157 /* (2**32 - (x * 2)) % x == 2**32 % x when x <= 2**31 */ 158 min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound; 159 } 160#endif /* if (ULONG_MAX > 0xffffffffUL) */ 161 162 /* 163 * This could theoretically loop forever but each retry has 164 * p > 0.5 (worst case, usually far better) of selecting a 165 * number inside the range we need, so it should rarely need 166 * to re-roll. 167 */ 168 for (;;) { 169 r = next(); 170 if (r >= min) { 171 break; 172 } 173 } 174 175 return (r % upper_bound); 176} 177