/* Portable arc4random.c based on arc4random.c from OpenBSD. * Portable version by Chris Davis, adapted for Libevent by Nick Mathewson * Copyright (c) 2010 Chris Davis, Niels Provos, and Nick Mathewson * Copyright (c) 2010-2012 Niels Provos and Nick Mathewson * * Note that in Libevent, this file isn't compiled directly. Instead, * it's included from evutil_rand.c */ /* * Copyright (c) 1996, David Mazieres * Copyright (c) 2008, Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Arc4 random number generator for OpenBSD. * * This code is derived from section 17.1 of Applied Cryptography, * second edition, which describes a stream cipher allegedly * compatible with RSA Labs "RC4" cipher (the actual description of * which is a trade secret). The same algorithm is used as a stream * cipher called "arcfour" in Tatu Ylonen's ssh package. * * Here the stream cipher has been modified always to include the time * when initializing the state. That makes it impossible to * regenerate the same random sequence twice, so this can't be used * for encryption, but will generate good random numbers. * * RC4 is a registered trademark of RSA Laboratories. */ #ifndef ARC4RANDOM_EXPORT #define ARC4RANDOM_EXPORT #endif #ifndef ARC4RANDOM_UINT32 #define ARC4RANDOM_UINT32 uint32_t #endif #ifndef ARC4RANDOM_NO_INCLUDES #include "evconfig-private.h" #ifdef _WIN32 #include #include #include #else #include #include #include #include #ifdef EVENT__HAVE_SYS_SYSCTL_H #include #endif #ifdef EVENT__HAVE_SYS_RANDOM_H #include #endif #endif #include #include #include #endif /* Add platform entropy 32 bytes (256 bits) at a time. */ #define ADD_ENTROPY 32 #define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */ struct arc4_stream { unsigned char i; unsigned char j; unsigned char s[256]; }; #ifdef _WIN32 #define getpid _getpid #define pid_t int #endif #ifndef O_RDONLY #define O_RDONLY _O_RDONLY #endif static int rs_initialized; static struct arc4_stream rs; static pid_t arc4_stir_pid; static int arc4_count; static inline unsigned char arc4_getbyte(void); static inline void arc4_init(void) { int n; for (n = 0; n < 256; n++) rs.s[n] = n; rs.i = 0; rs.j = 0; } static inline void arc4_addrandom(const unsigned char *dat, int datlen) { int n; unsigned char si; rs.i--; for (n = 0; n < 256; n++) { rs.i = (rs.i + 1); si = rs.s[rs.i]; rs.j = (rs.j + si + dat[n % datlen]); rs.s[rs.i] = rs.s[rs.j]; rs.s[rs.j] = si; } rs.j = rs.i; } #ifndef _WIN32 static ssize_t read_all(int fd, unsigned char *buf, size_t count) { size_t numread = 0; ssize_t result; while (numread < count) { result = read(fd, buf+numread, count-numread); if (result<0) return -1; else if (result == 0) break; numread += result; } return (ssize_t)numread; } #endif #ifdef _WIN32 #define TRY_SEED_WIN32 static int arc4_seed_win32(void) { unsigned char buf[ADD_ENTROPY]; if (BCryptGenRandom(NULL, buf, sizeof(buf), BCRYPT_USE_SYSTEM_PREFERRED_RNG)) return -1; arc4_addrandom(buf, sizeof(buf)); evutil_memclear_(buf, sizeof(buf)); return 0; } #endif #if defined(EVENT__HAVE_GETRANDOM) #define TRY_SEED_GETRANDOM static int arc4_seed_getrandom(void) { unsigned char buf[ADD_ENTROPY]; size_t len; ssize_t n = 0; for (len = 0; len < sizeof(buf); len += n) { n = getrandom(&buf[len], sizeof(buf) - len, 0); if (n < 0) return -1; } arc4_addrandom(buf, sizeof(buf)); evutil_memclear_(buf, sizeof(buf)); return 0; } #endif /* EVENT__HAVE_GETRANDOM */ #if defined(EVENT__HAVE_SYS_SYSCTL_H) && defined(EVENT__HAVE_SYSCTL) #if EVENT__HAVE_DECL_CTL_KERN && EVENT__HAVE_DECL_KERN_ARND #define TRY_SEED_SYSCTL_BSD static int arc4_seed_sysctl_bsd(void) { /* Based on code from William Ahern and from OpenBSD, this function * tries to use the KERN_ARND syscall to get entropy from the kernel. * This can work even if /dev/urandom is inaccessible for some reason * (e.g., we're running in a chroot). */ int mib[] = { CTL_KERN, KERN_ARND }; unsigned char buf[ADD_ENTROPY]; size_t len, n; int i, any_set; memset(buf, 0, sizeof(buf)); len = sizeof(buf); if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) { for (len = 0; len < sizeof(buf); len += sizeof(unsigned)) { n = sizeof(unsigned); if (n + len > sizeof(buf)) n = len - sizeof(buf); if (sysctl(mib, 2, &buf[len], &n, NULL, 0) == -1) return -1; } } /* make sure that the buffer actually got set. */ for (i=any_set=0; i 0.5 (worst case, usually far better) of selecting a * number inside the range we need, so it should rarely need * to re-roll. */ for (;;) { r = arc4random(); if (r >= min) break; } return r % upper_bound; } #endif