From c5b5bd910507520f7974a156a1de9d187f23bc2f Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sun, 8 Jan 2017 15:28:37 -0800 Subject: New ISAAC pRNG. This replaces other local hacks for random data. Platforms must seed the pRNGs by offering an nni_plat_seed_prng() routine. Implementations for POSIX using various options (including the /dev/urandom device) are supplied. --- src/platform/posix/posix_config.h | 25 +++++++++--- src/platform/posix/posix_debug.c | 56 ++++++++++++------------- src/platform/posix/posix_rand.c | 86 +++++++++++++++++++++++++++++++++++++++ src/platform/posix/posix_thread.c | 32 --------------- 4 files changed, 134 insertions(+), 65 deletions(-) create mode 100644 src/platform/posix/posix_rand.c (limited to 'src/platform') diff --git a/src/platform/posix/posix_config.h b/src/platform/posix/posix_config.h index 6d282f63..e1bafd5c 100644 --- a/src/platform/posix/posix_config.h +++ b/src/platform/posix/posix_config.h @@ -27,23 +27,38 @@ // is defined. Platforms that don't use POSIX clocks will probably // ignore any setting here. // -// #define NNG_HAVE_ARC4RANDOM -// This indicates that the platform has the superior arc4random function -// for getting entropy. -// // #define NNG_HAVE_BACKTRACE // If your system has a working backtrace(), and backtrace_symbols(), // along with , you can define this to get richer backtrace // information for debugging. +// +// #define NNG_USE_GETRANDOM +// #define NNG_USE_GETENTROPY +// #define NNG_USE_ARC4RANDOM +// #define NNG_USE_DEVURANDOM +// Thesse are options for obtaining entropy to seed the pRNG. +// All known modern UNIX variants can support NNG_USE_DEVURANDOM, +// but the other options are better still, but not portable. #include +// These are things about systems we know about. +#ifdef __APPLE__ // MacOS X used to lack CLOCK_MONOTONIC. Now it has it, but its // buggy, condition variables set to use it wake early. -#ifdef __APPLE__ #define NNG_USE_CLOCKID CLOCK_REALTIME +// macOS 10.12 has getentropy(), but arc4random() is good enough +// and works on older releases. +#define NNG_USE_ARC4RANDOM 1 #endif // __APPLE__ +// It should never hurt to use DEVURANDOM, since if the device does not +// exist then we won't open it. (Provided: it would be bad if the device +// exists but has somehow very very different semantics. We don't know +// of any such concerns.) This won't be used if any of the other options +// are defined and work. +#define NNG_USE_DEVURANDOM 1 + #define NNG_USE_CLOCKID CLOCK_REALTIME #ifndef CLOCK_REALTIME #define NNG_USE_GETTIMEOFDAY diff --git a/src/platform/posix/posix_debug.c b/src/platform/posix/posix_debug.c index a956d777..71006b25 100644 --- a/src/platform/posix/posix_debug.c +++ b/src/platform/posix/posix_debug.c @@ -55,35 +55,35 @@ static struct { int nng_err; } nni_plat_errnos[] = { - NNI_ERR(EINTR, NNG_EINTR) - NNI_ERR(EINVAL, NNG_EINVAL) - NNI_ERR(ENOMEM, NNG_ENOMEM) - NNI_ERR(EACCES, NNG_EPERM) - NNI_ERR(EADDRINUSE, NNG_EADDRINUSE) - NNI_ERR(EADDRNOTAVAIL, NNG_EADDRINVAL) - NNI_ERR(EAFNOSUPPORT, NNG_ENOTSUP) - NNI_ERR(EAGAIN, NNG_EAGAIN) - NNI_ERR(EBADF, NNG_ECLOSED) - NNI_ERR(EBUSY, NNG_EBUSY) - NNI_ERR(ECONNABORTED, NNG_ECLOSED) - NNI_ERR(ECONNREFUSED, NNG_ECONNREFUSED) - NNI_ERR(ECONNRESET, NNG_ECLOSED) - NNI_ERR(EHOSTUNREACH, NNG_EUNREACHABLE) - NNI_ERR(ENETUNREACH, NNG_EUNREACHABLE) - NNI_ERR(ENAMETOOLONG, NNG_EINVAL) - NNI_ERR(ENOENT, NNG_ENOENT) - NNI_ERR(ENOBUFS, NNG_ENOMEM) - NNI_ERR(ENOPROTOOPT, NNG_ENOTSUP) - NNI_ERR(ENOSYS, NNG_ENOTSUP) - NNI_ERR(ENOTSUP, NNG_ENOTSUP) - NNI_ERR(EPERM, NNG_EPERM) - NNI_ERR(EPIPE, NNG_ECLOSED) - NNI_ERR(EPROTO, NNG_EPROTO) + NNI_ERR(EINTR, NNG_EINTR) + NNI_ERR(EINVAL, NNG_EINVAL) + NNI_ERR(ENOMEM, NNG_ENOMEM) + NNI_ERR(EACCES, NNG_EPERM) + NNI_ERR(EADDRINUSE, NNG_EADDRINUSE) + NNI_ERR(EADDRNOTAVAIL, NNG_EADDRINVAL) + NNI_ERR(EAFNOSUPPORT, NNG_ENOTSUP) + NNI_ERR(EAGAIN, NNG_EAGAIN) + NNI_ERR(EBADF, NNG_ECLOSED) + NNI_ERR(EBUSY, NNG_EBUSY) + NNI_ERR(ECONNABORTED, NNG_ECLOSED) + NNI_ERR(ECONNREFUSED, NNG_ECONNREFUSED) + NNI_ERR(ECONNRESET, NNG_ECLOSED) + NNI_ERR(EHOSTUNREACH, NNG_EUNREACHABLE) + NNI_ERR(ENETUNREACH, NNG_EUNREACHABLE) + NNI_ERR(ENAMETOOLONG, NNG_EINVAL) + NNI_ERR(ENOENT, NNG_ENOENT) + NNI_ERR(ENOBUFS, NNG_ENOMEM) + NNI_ERR(ENOPROTOOPT, NNG_ENOTSUP) + NNI_ERR(ENOSYS, NNG_ENOTSUP) + NNI_ERR(ENOTSUP, NNG_ENOTSUP) + NNI_ERR(EPERM, NNG_EPERM) + NNI_ERR(EPIPE, NNG_ECLOSED) + NNI_ERR(EPROTO, NNG_EPROTO) NNI_ERR(EPROTONOSUPPORT, NNG_ENOTSUP) - NNI_ERR(ETIME, NNG_ETIMEDOUT) - NNI_ERR(ETIMEDOUT, NNG_ETIMEDOUT) - NNI_ERR(EWOULDBLOCK, NNG_EAGAIN) - NNI_ERR(0, 0) // must be last + NNI_ERR(ETIME, NNG_ETIMEDOUT) + NNI_ERR(ETIMEDOUT, NNG_ETIMEDOUT) + NNI_ERR(EWOULDBLOCK, NNG_EAGAIN) + NNI_ERR(0, 0) // must be last }; int diff --git a/src/platform/posix/posix_rand.c b/src/platform/posix/posix_rand.c new file mode 100644 index 00000000..65d1c031 --- /dev/null +++ b/src/platform/posix/posix_rand.c @@ -0,0 +1,86 @@ +// +// Copyright 2017 Garrett D'Amore +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +// POSIX clock stuff. +#include "core/nng_impl.h" + +#ifdef PLATFORM_POSIX_RANDOM + +#include +#include +#include +#include +#include +#include +#include + +#ifdef NNG_USE_GETRANDOM +#include +#endif + +#ifdef NNG_USE_GETENTROPY +#include +#endif + +// This structure represents the very barest minimum that we can use as +// a source of entropy. We mix these into our initial entropy, but really +// really really you want to have more data than this available, especially +// for cryptographic applications. +struct nni_plat_prng_x { + nni_time now; + pid_t pid; + uid_t uid; + struct utsname uts; +}; + +void +nni_plat_seed_prng(void *buf, size_t bufsz) +{ + struct nni_plat_prng_x x; + int i; + +#if defined(NNG_USE_GETRANDOM) + // Latest Linux has a nice API here. + (void) getrandom(buf, bufsz, 0); +#elif defined(NNG_USE_GETENTROPY) + // Modern BSD systems prefer this, but can only generate 256 bytes + (void) getentropy(buf, bufsz > 256 ? 256 : 0); +#elif defined(NNG_USE_ARC4RANDOM) + // This uses BSD style pRNG seeded from the kernel in libc. + (void) arc4random_buf(buf, bufsz); +#elif defined(NNG_USE_DEVURANDOM) + // The historic /dev/urandom device. This is not as a good as + // a system call, since file descriptor attacks are possible, + // and it may need special permissions. We choose /dev/urandom + // over /dev/random to avoid diminishing the system entropy. + int fd; + + if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) { + (void) read(fd, buf, buffz); + (void) close(fd); + } +#endif + + // As a speical extra guard, let's mixin the data from the + // following system calls. This ensures that even on the most + // limited of systems, we have at least *some* level of randomness. + // The mixing is done in a way to avoid diminishing entropy we may + // have already collected. + x.now = nni_clock(); + x.pid = getpid(); + x.uid = getuid(); + uname(&x.uts); + + for (i = 0; (i < bufsz) && (i < sizeof (x)); i++) { + ((uint8_t *) buf)[i] ^= ((uint8_t *) &x)[i]; + } +} + + +#endif // PLATFORM_POSIX_RANDOM diff --git a/src/platform/posix/posix_thread.c b/src/platform/posix/posix_thread.c index e4e5e74b..991a08db 100644 --- a/src/platform/posix/posix_thread.c +++ b/src/platform/posix/posix_thread.c @@ -24,7 +24,6 @@ static pthread_mutex_t nni_plat_lock = PTHREAD_MUTEX_INITIALIZER; static int nni_plat_inited = 0; static int nni_plat_forked = 0; -static int nni_plat_next = 0; pthread_condattr_t nni_cvattr; pthread_mutexattr_t nni_mxattr; @@ -34,18 +33,6 @@ pthread_mutexattr_t nni_mxattr; int nni_plat_devnull = -1; -uint32_t -nni_plat_nextid(void) -{ - uint32_t id; - - pthread_mutex_lock(&nni_plat_lock); - id = nni_plat_next++; - pthread_mutex_unlock(&nni_plat_lock); - return (id); -} - - int nni_plat_mtx_init(nni_plat_mtx *mtx) { @@ -270,25 +257,6 @@ nni_plat_init(int (*helper)(void)) return (NNG_ENOMEM); } - // Generate a starting ID (used for Pipe IDs) -#ifdef NNG_HAVE_ARC4RANDOM - nni_plat_next = arc4random(); -#else - while (nni_plat_next == 0) { - uint16_t xsub[3]; - nni_time now = nni_clock(); - pid_t pid = getpid(); - - xsub[0] = (uint16_t) now; - xsub[1] = (uint16_t) (now >> 16); - xsub[2] = (uint16_t) (now >> 24); - xsub[0] ^= (uint16_t) pid; - xsub[1] ^= (uint16_t) (pid >> 16); - xsub[2] ^= (uint16_t) (pid >> 24); - nni_plat_next = nrand48(xsub); - } -#endif - if (pthread_atfork(NULL, NULL, nni_atfork_child) != 0) { pthread_mutex_unlock(&nni_plat_lock); (void) close(nni_plat_devnull); -- cgit v1.2.3-70-g09d2