diff options
| author | Garrett D'Amore <garrett@damore.org> | 2020-01-01 17:57:12 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2020-01-01 17:57:12 -0800 |
| commit | 11985f8c59cccc0364bde7dd314e246ea53cff90 (patch) | |
| tree | c75c15db75b297817c10c9f2f6188aa767cbfa82 | |
| parent | ec7de57627a2bba8fadfb34d118ac478fbc351aa (diff) | |
| download | nng-11985f8c59cccc0364bde7dd314e246ea53cff90.tar.gz nng-11985f8c59cccc0364bde7dd314e246ea53cff90.tar.bz2 nng-11985f8c59cccc0364bde7dd314e246ea53cff90.zip | |
fixes #1083 Random number improvements
| -rw-r--r-- | CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 373 | ||||
| -rw-r--r-- | src/core/init.c | 4 | ||||
| -rw-r--r-- | src/core/nng_impl.h | 1 | ||||
| -rw-r--r-- | src/core/platform.h | 7 | ||||
| -rw-r--r-- | src/core/random.c | 202 | ||||
| -rw-r--r-- | src/core/random.h | 26 | ||||
| -rw-r--r-- | src/platform/posix/posix_rand.c | 88 | ||||
| -rw-r--r-- | src/platform/posix/posix_rand_arc4random.c | 24 | ||||
| -rw-r--r-- | src/platform/posix/posix_rand_getrandom.c | 40 | ||||
| -rw-r--r-- | src/platform/posix/posix_rand_urandom.c | 58 | ||||
| -rw-r--r-- | src/platform/windows/win_rand.c | 28 | ||||
| -rw-r--r-- | tests/platform.c | 77 |
13 files changed, 376 insertions, 556 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index c13365f4..2789e0bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -221,9 +221,6 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Android") elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_definitions(-DNNG_PLATFORM_POSIX) add_definitions(-DNNG_PLATFORM_DARWIN) - # macOS 10.12 and later have getentropy, but the older releases - # have ARC4_RANDOM, and that is sufficient to our needs. - add_definitions(-DNNG_USE_ARC4RANDOM) set(NNG_PLATFORM_POSIX ON) elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") @@ -325,7 +322,6 @@ else () nng_check_func(lockf NNG_HAVE_LOCKF) nng_check_func(flock NNG_HAVE_FLOCK) - nng_check_func(getentropy NNG_HAVE_GETENTROPY) nng_check_func(getrandom NNG_HAVE_GETRANDOM) nng_check_func(arc4random_buf NNG_HAVE_ARC4RANDOM) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f5e2eca0..1b177aa1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,6 +1,6 @@ # +# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> # Copyright 2018 Capitar IT Group BV <info@capitar.com> -# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> # Copyright (c) 2012-2013 Martin Sustrik All rights reserved. # Copyright (c) 2013 GoPivotal, Inc. All rights reserved. # Copyright (c) 2015-2016 Jack R. Dunaway. All rights reserved. @@ -24,7 +24,7 @@ # IN THE SOFTWARE. # -add_library (${PROJECT_NAME} +add_library(${PROJECT_NAME} nng.c ${PROJECT_SOURCE_DIR}/include/nng/nng.h) @@ -34,148 +34,145 @@ if (NNG_TESTS) ${PROJECT_SOURCE_DIR}/tests/testutil.c ${PROJECT_SOURCE_DIR}/tests/testutil.h ${PROJECT_SOURCE_DIR}/include/nng/nng.h) -endif() - -set (NNG_SRCS - - core/defs.h - - core/aio.c - core/aio.h - core/clock.c - core/clock.h - core/device.c - core/device.h - core/dialer.c - core/dialer.h - core/file.c - core/file.h - core/idhash.c - core/idhash.h - core/init.c - core/init.h - core/list.c - core/list.h - core/listener.c - core/listener.h - core/lmq.c - core/lmq.h - core/message.c - core/message.h - core/msgqueue.c - core/msgqueue.h - core/nng_impl.h - core/options.c - core/options.h - core/pollable.c - core/pollable.h - core/panic.c - core/panic.h - core/pipe.c - core/pipe.h - core/platform.h - core/protocol.c - core/protocol.h - core/random.c - core/random.h - core/reap.c - core/reap.h - core/socket.c - core/socket.h - core/sockimpl.h - core/stats.c - core/stats.h - core/stream.c - core/stream.h - core/strs.c - core/strs.h - core/taskq.c - core/taskq.h - core/thread.c - core/thread.h - core/timer.c - core/timer.h - core/transport.c - core/transport.h - core/url.c - core/url.h -) +endif () + +set(NNG_SRCS + + core/defs.h + + core/aio.c + core/aio.h + core/clock.c + core/clock.h + core/device.c + core/device.h + core/dialer.c + core/dialer.h + core/file.c + core/file.h + core/idhash.c + core/idhash.h + core/init.c + core/init.h + core/list.c + core/list.h + core/listener.c + core/listener.h + core/lmq.c + core/lmq.h + core/message.c + core/message.h + core/msgqueue.c + core/msgqueue.h + core/nng_impl.h + core/options.c + core/options.h + core/pollable.c + core/pollable.h + core/panic.c + core/panic.h + core/pipe.c + core/pipe.h + core/platform.h + core/protocol.c + core/protocol.h + core/reap.c + core/reap.h + core/socket.c + core/socket.h + core/sockimpl.h + core/stats.c + core/stats.h + core/stream.c + core/stream.h + core/strs.c + core/strs.h + core/taskq.c + core/taskq.h + core/thread.c + core/thread.h + core/timer.c + core/timer.h + core/transport.c + core/transport.h + core/url.c + core/url.h + ) if (NNG_PLATFORM_POSIX) - find_package (Threads REQUIRED) + find_package(Threads REQUIRED) list(APPEND NNG_LIBS Threads::Threads) - set (NNG_SRCS ${NNG_SRCS} - platform/posix/posix_impl.h - platform/posix/posix_aio.h - platform/posix/posix_ipc.h - platform/posix/posix_config.h - platform/posix/posix_pollq.h - platform/posix/posix_tcp.h - - platform/posix/posix_alloc.c - platform/posix/posix_atomic.c - platform/posix/posix_clock.c - platform/posix/posix_debug.c - platform/posix/posix_file.c - platform/posix/posix_ipcconn.c - platform/posix/posix_ipcdial.c - platform/posix/posix_ipclisten.c - platform/posix/posix_pipe.c - platform/posix/posix_rand.c - platform/posix/posix_resolv_gai.c - platform/posix/posix_sockaddr.c - platform/posix/posix_tcpconn.c - platform/posix/posix_tcpdial.c - platform/posix/posix_tcplisten.c - platform/posix/posix_thread.c - platform/posix/posix_udp.c - ) + set(NNG_SRCS ${NNG_SRCS} + platform/posix/posix_impl.h + platform/posix/posix_aio.h + platform/posix/posix_ipc.h + platform/posix/posix_config.h + platform/posix/posix_pollq.h + platform/posix/posix_tcp.h + + platform/posix/posix_alloc.c + platform/posix/posix_atomic.c + platform/posix/posix_clock.c + platform/posix/posix_debug.c + platform/posix/posix_file.c + platform/posix/posix_ipcconn.c + platform/posix/posix_ipcdial.c + platform/posix/posix_ipclisten.c + platform/posix/posix_pipe.c + platform/posix/posix_resolv_gai.c + platform/posix/posix_sockaddr.c + platform/posix/posix_tcpconn.c + platform/posix/posix_tcpdial.c + platform/posix/posix_tcplisten.c + platform/posix/posix_thread.c + platform/posix/posix_udp.c + ) if (NNG_HAVE_PORT_CREATE) - set (NNG_SRCS ${NNG_SRCS} - platform/posix/posix_pollq_port.c - ) + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_pollq_port.c) elseif (NNG_HAVE_KQUEUE) - set (NNG_SRCS ${NNG_SRCS} - platform/posix/posix_pollq_kqueue.c - ) + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_pollq_kqueue.c) elseif (NNG_HAVE_EPOLL AND NNG_HAVE_EVENTFD) - set (NNG_SRCS ${NNG_SRCS} - platform/posix/posix_pollq_epoll.c - ) - else() - set (NNG_SRCS ${NNG_SRCS} - platform/posix/posix_pollq_poll.c - ) - endif() -endif() + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_pollq_epoll.c) + else () + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_pollq_poll.c) + endif () + + if (NNG_HAVE_ARC4RANDOM) + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_rand_arc4random.c) + elseif (NNG_HAVE_GETRANDOM) + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_rand_getrandom.c) + else () + set(NNG_SRCS ${NNG_SRCS} platform/posix/posix_rand_urandom.c) + endif () +endif () if (NNG_PLATFORM_WINDOWS) - set (NNG_SRCS ${NNG_SRCS} - platform/windows/win_impl.h - platform/windows/win_ipc.h - platform/windows/win_tcp.h - - platform/windows/win_clock.c - platform/windows/win_debug.c - platform/windows/win_file.c - platform/windows/win_io.c - platform/windows/win_ipcconn.c - platform/windows/win_ipcdial.c - platform/windows/win_ipclisten.c - platform/windows/win_pipe.c - platform/windows/win_rand.c - platform/windows/win_resolv.c - platform/windows/win_sockaddr.c - platform/windows/win_tcp.c - platform/windows/win_tcpconn.c - platform/windows/win_tcpdial.c - platform/windows/win_tcplisten.c - platform/windows/win_thread.c - platform/windows/win_udp.c - ) -endif() + set(NNG_SRCS ${NNG_SRCS} + platform/windows/win_impl.h + platform/windows/win_ipc.h + platform/windows/win_tcp.h + + platform/windows/win_clock.c + platform/windows/win_debug.c + platform/windows/win_file.c + platform/windows/win_io.c + platform/windows/win_ipcconn.c + platform/windows/win_ipcdial.c + platform/windows/win_ipclisten.c + platform/windows/win_pipe.c + platform/windows/win_rand.c + platform/windows/win_resolv.c + platform/windows/win_sockaddr.c + platform/windows/win_tcp.c + platform/windows/win_tcpconn.c + platform/windows/win_tcpdial.c + platform/windows/win_tcplisten.c + platform/windows/win_thread.c + platform/windows/win_udp.c + ) +endif () add_subdirectory(compat/nanomsg) @@ -207,8 +204,8 @@ include_directories(AFTER SYSTEM ${PROJECT_SOURCE_DIR}/src ${NNG_INCS}) add_definitions(${NNG_DEFS}) -foreach(_PKG IN ITEMS ${NNG_PKGS}) - find_package(${_PKG} REQUIRED) +foreach (_PKG IN ITEMS ${NNG_PKGS}) + find_package(${_PKG} REQUIRED) endforeach () # Library @@ -216,10 +213,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${NNG_SRCS}) if (NNG_TESTS) target_sources(${PROJECT_NAME}_testlib PRIVATE ${NNG_SRCS}) - target_link_libraries (${PROJECT_NAME}_testlib PUBLIC ${NNG_LIBS}) + target_link_libraries(${PROJECT_NAME}_testlib PUBLIC ${NNG_LIBS}) target_compile_definitions(${PROJECT_NAME}_testlib PUBLIC NNG_STATIC_LIB NNG_TEST_LIB) - target_include_directories (${PROJECT_NAME}_testlib PUBLIC ${PROJECT_SOURCE_DIR}/include) -endif() + target_include_directories(${PROJECT_NAME}_testlib PUBLIC ${PROJECT_SOURCE_DIR}/include) +endif () # When building shared libraries we prefer to suppress default symbol # visibility, so that only the symbols that should be exposed in the @@ -229,55 +226,55 @@ if (BUILD_SHARED_LIBS) if (NNG_HIDDEN_VISIBILITY) target_compile_definitions(${PROJECT_NAME} PRIVATE -DNNG_HIDDEN_VISIBILITY) set_target_properties(${PROJECT_NAME} PROPERTIES C_VISIBILITY_PRESET hidden) - endif() -else() + endif () +else () target_compile_definitions(${PROJECT_NAME} PUBLIC -DNNG_STATIC_LIB) -endif() +endif () -set_target_properties (${PROJECT_NAME} - PROPERTIES SOVERSION ${NNG_ABI_SOVERSION} VERSION "${NNG_ABI_VERSION}") +set_target_properties(${PROJECT_NAME} + PROPERTIES SOVERSION ${NNG_ABI_SOVERSION} VERSION "${NNG_ABI_VERSION}") # Set library outputs same as top-level project binary outputs -set_target_properties (${PROJECT_NAME} - PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -set_target_properties (${PROJECT_NAME} - PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -set_target_properties (${PROJECT_NAME} - PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) - -set_target_properties (${PROJECT_NAME} ${PROJECT_NAME} - PROPERTIES FRAMEWORK OFF) - -target_link_libraries (${PROJECT_NAME} PRIVATE ${NNG_LIBS}) - -target_include_directories (${PROJECT_NAME} INTERFACE $<INSTALL_INTERFACE:include> -$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) - -target_include_directories (${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include) - -install (TARGETS ${PROJECT_NAME} - EXPORT ${PROJECT_NAME}-target - FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Tools -) - -install (EXPORT ${PROJECT_NAME}-target - FILE ${PROJECT_NAME}-targets.cmake - NAMESPACE nng:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} - COMPONENT Library -) - -export (EXPORT ${PROJECT_NAME}-target - FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake - NAMESPACE nng::) +set_target_properties(${PROJECT_NAME} + PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set_target_properties(${PROJECT_NAME} + PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +set_target_properties(${PROJECT_NAME} + PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) + +set_target_properties(${PROJECT_NAME} ${PROJECT_NAME} + PROPERTIES FRAMEWORK OFF) + +target_link_libraries(${PROJECT_NAME} PRIVATE ${NNG_LIBS}) + +target_include_directories(${PROJECT_NAME} INTERFACE $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>) + +target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include) + +install(TARGETS ${PROJECT_NAME} + EXPORT ${PROJECT_NAME}-target + FRAMEWORK DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT Library + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Tools + ) + +install(EXPORT ${PROJECT_NAME}-target + FILE ${PROJECT_NAME}-targets.cmake + NAMESPACE nng:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} + COMPONENT Library + ) + +export(EXPORT ${PROJECT_NAME}-target + FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-targets.cmake + NAMESPACE nng::) # Install the header files. install(DIRECTORY ../include/nng - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - COMPONENT Headers) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + COMPONENT Headers) # Promote settings to parent set(NNG_LIBS ${NNG_LIBS} PARENT_SCOPE) @@ -300,13 +297,13 @@ set(version_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.c set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake") write_basic_package_version_file("${version_config}" - VERSION ${NNG_PACKAGE_VERSION} - COMPATIBILITY SameMajorVersion -) + VERSION ${NNG_PACKAGE_VERSION} + COMPATIBILITY SameMajorVersion + ) configure_package_config_file(${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake.in "${project_config}" - INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" - PATH_VARS INCLUDE_INSTALL_DIRS) + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + PATH_VARS INCLUDE_INSTALL_DIRS) install(FILES "${project_config}" "${version_config}" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" - COMPONENT Library) + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" + COMPONENT Library) diff --git a/src/core/init.c b/src/core/init.c index 60736fb7..4749516f 100644 --- a/src/core/init.c +++ b/src/core/init.c @@ -1,5 +1,5 @@ // -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a @@ -32,7 +32,6 @@ nni_init_helper(void) ((rv = nni_reap_sys_init()) != 0) || ((rv = nni_timer_sys_init()) != 0) || ((rv = nni_aio_sys_init()) != 0) || - ((rv = nni_random_sys_init()) != 0) || ((rv = nni_sock_sys_init()) != 0) || ((rv = nni_listener_sys_init()) != 0) || ((rv = nni_dialer_sys_init()) != 0) || @@ -77,7 +76,6 @@ nni_fini(void) nni_listener_sys_fini(); nni_sock_sys_fini(); nni_reap_drain(); - nni_random_sys_fini(); nni_aio_sys_fini(); nni_timer_sys_fini(); nni_taskq_sys_fini(); diff --git a/src/core/nng_impl.h b/src/core/nng_impl.h index 0dcc976f..cfcd0e08 100644 --- a/src/core/nng_impl.h +++ b/src/core/nng_impl.h @@ -40,7 +40,6 @@ #include "core/panic.h" #include "core/pollable.h" #include "core/protocol.h" -#include "core/random.h" #include "core/reap.h" #include "core/stats.h" #include "core/stream.h" diff --git a/src/core/platform.h b/src/core/platform.h index b56f9a68..355ef7eb 100644 --- a/src/core/platform.h +++ b/src/core/platform.h @@ -208,11 +208,8 @@ extern void nni_plat_sleep(nni_duration); // Entropy Support // -// nni_plat_seed_prng seeds the PRNG subsystem. The specified number -// of bytes of entropy should be stashed. When possible, cryptographic -// quality entropy sources should be used. Note that today we prefer -// to seed up to 256 bytes of data. -extern void nni_plat_seed_prng(void *, size_t); +// nni_random returns a cryptographically secure random uint32. +uint32_t nni_random(void); // nni_plat_init is called to allow the platform the chance to // do any necessary initialization. This routine MUST be idempotent, diff --git a/src/core/random.c b/src/core/random.c deleted file mode 100644 index 2d899f21..00000000 --- a/src/core/random.c +++ /dev/null @@ -1,202 +0,0 @@ -// -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> -// Copyright 2018 Capitar IT Group BV <info@capitar.com> -// -// 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. -// - -#include "core/nng_impl.h" - -// This is ISAAC, a (reputedly) cryptographically secure PRNG that is also -// quite efficient. While the particular adjustments to fit in our code -// base are under our copyright, the actual algorithm itself, as well as -// sample implementations, are part of the public domain. See this: -// http://www.burtleburtle.net/bob/c/readable.c -// -// Our changes include making this code thread safe/reentrant, and naming -// and style changes, to fit C99. - -typedef struct { - // the rsl is the actual results, and the randcnt is the length - // of the results. - uint32_t randrsl[256]; - uint32_t randcnt; - - // lock to protect concurrent access - nni_mtx mx; - - // more or less internal state - uint32_t mm[256]; - uint32_t aa; - uint32_t bb; - uint32_t cc; -} nni_isaac_ctx; - -static void -nni_isaac(nni_isaac_ctx *ctx) -{ - ctx->cc++; // cc incremented once per 256 results - ctx->bb += ctx->cc; // then combined with bb - - for (uint32_t i = 0; i < 256; ++i) { - uint32_t x = ctx->mm[i]; - uint32_t y; - switch (i % 4) { - case 0: - ctx->aa ^= (ctx->aa << 13); - break; - case 1: - ctx->aa ^= (ctx->aa >> 6); - break; - case 2: - ctx->aa ^= (ctx->aa << 2); - break; - case 3: - ctx->aa ^= (ctx->aa >> 16); - break; - } - ctx->aa += ctx->mm[(i + 128) % 256]; - ctx->mm[i] = y = ctx->mm[(x >> 2) % 256] + ctx->aa + ctx->bb; - ctx->randrsl[i] = ctx->bb = ctx->mm[(y >> 10) % 256] + x; - - // Note that bits 2..9 are chosen from x but 10..17 are chosen - // from y. The only important thing here is that 2..9 and - // 10..17 don't overlap. 2..9 and 10..17 were then chosen - // for speed in the optimized version (rand.c) - - // See http://burtleburtle.net/bob/rand/isaac.html - // for further explanations and analysis. - } -} - -// if (flag!=0), then use the contents of randrsl[] to initialize mm[]. -#define nni_isaac_mix(a, b, c, d, e, f, g, h) \ - { \ - a ^= b << 11; \ - d += a; \ - b += c; \ - b ^= c >> 2; \ - e += b; \ - c += d; \ - c ^= d << 8; \ - f += c; \ - d += e; \ - d ^= e >> 16; \ - g += d; \ - e += f; \ - e ^= f << 10; \ - h += e; \ - f += g; \ - f ^= g >> 4; \ - a += f; \ - g += h; \ - g ^= h << 8; \ - b += g; \ - h += a; \ - h ^= a >> 9; \ - c += h; \ - a += b; \ - } - -static void -nni_isaac_randinit(nni_isaac_ctx *ctx, int flag) -{ - int i; - uint32_t a, b, c, d, e, f, g, h; - - ctx->aa = ctx->bb = ctx->cc = 0; - a = b = c = d = e = f = g = h = 0x9e3779b9; // the golden ratio - - for (i = 0; i < 4; ++i) { // scramble it - nni_isaac_mix(a, b, c, d, e, f, g, h); - } - - for (i = 0; i < 256; i += 8) { // fill in mm[] with messy stuff - if (flag) { // use all the information in the seed - a += ctx->randrsl[i]; - b += ctx->randrsl[i + 1]; - c += ctx->randrsl[i + 2]; - d += ctx->randrsl[i + 3]; - e += ctx->randrsl[i + 4]; - f += ctx->randrsl[i + 5]; - g += ctx->randrsl[i + 6]; - h += ctx->randrsl[i + 7]; - } - nni_isaac_mix(a, b, c, d, e, f, g, h); - ctx->mm[i] = a; - ctx->mm[i + 1] = b; - ctx->mm[i + 2] = c; - ctx->mm[i + 3] = d; - ctx->mm[i + 4] = e; - ctx->mm[i + 5] = f; - ctx->mm[i + 6] = g; - ctx->mm[i + 7] = h; - } - - if (flag) { - // do a second pass to make all of the seed affect all of mm - for (i = 0; i < 256; i += 8) { - a += ctx->mm[i]; - b += ctx->mm[i + 1]; - c += ctx->mm[i + 2]; - d += ctx->mm[i + 3]; - e += ctx->mm[i + 4]; - f += ctx->mm[i + 5]; - g += ctx->mm[i + 6]; - h += ctx->mm[i + 7]; - nni_isaac_mix(a, b, c, d, e, f, g, h); - ctx->mm[i] = a; - ctx->mm[i + 1] = b; - ctx->mm[i + 2] = c; - ctx->mm[i + 3] = d; - ctx->mm[i + 4] = e; - ctx->mm[i + 5] = f; - ctx->mm[i + 6] = g; - ctx->mm[i + 7] = h; - } - } - - nni_isaac(ctx); // fill in the first set of results - ctx->randcnt = 256; // prepare to use the first set of results -} - -static nni_isaac_ctx nni_random_ctx; - -int -nni_random_sys_init(void) -{ - // minimally, grab the system clock - nni_isaac_ctx *ctx = &nni_random_ctx; - - nni_mtx_init(&ctx->mx); - nni_plat_seed_prng(ctx->randrsl, sizeof(ctx->randrsl)); - nni_isaac_randinit(ctx, 1); - return (0); -} - -uint32_t -nni_random(void) -{ - uint32_t rv; - nni_isaac_ctx *ctx = &nni_random_ctx; - - nni_mtx_lock(&ctx->mx); - if (ctx->randcnt < 1) { - nni_isaac(ctx); - ctx->randcnt = 256; - } - ctx->randcnt--; - rv = ctx->randrsl[ctx->randcnt]; - nni_mtx_unlock(&ctx->mx); - - return (rv); -} - -void -nni_random_sys_fini(void) -{ - nni_mtx_fini(&nni_random_ctx.mx); -} diff --git a/src/core/random.h b/src/core/random.h deleted file mode 100644 index 33229b09..00000000 --- a/src/core/random.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright 2017 Garrett D'Amore <garrett@damore.org> -// -// 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. -// - -#ifndef CORE_RANDOM_H -#define CORE_RANDOM_H - -// nni_random_sys_init initializes the pRNG subsystem. This includes obtaining -// suitable seeding material from the platform. -extern int nni_random_sys_init(void); - -// nni_random_sys_fini destroys the pRNG subsystem. -extern void nni_random_sys_fini(void); - -// nni_random returns a random 32-bit integer. Note that this routine is -// thread-safe/reentrant. The pRNG is very robust, should be of crypto -// quality. However, its usefulness for cryptography will be determined -// by the quality of the seeding material provided by the platform. -extern uint32_t nni_random(void); - -#endif // CORE_RANDOM_H diff --git a/src/platform/posix/posix_rand.c b/src/platform/posix/posix_rand.c deleted file mode 100644 index 2b704e2f..00000000 --- a/src/platform/posix/posix_rand.c +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> -// Copyright 2018 Capitar IT Group BV <info@capitar.com> -// -// 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 NNG_PLATFORM_POSIX - -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/utsname.h> -#include <time.h> -#include <unistd.h> - -#if defined(NNG_USE_GETRANDOM) -#include <linux/random.h> -#elif defined(NNG_USE_GETENTROPY) -#include <sys/random.h> -#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; - size_t i; - - memset(buf, 0, bufsz); - -#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, bufsz); - (void) close(fd); - } -#endif - - // As a special 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. - memset(&x, 0, sizeof(x)); // satisfy valgrind - 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 // NNG_PLATFORM_POSIX diff --git a/src/platform/posix/posix_rand_arc4random.c b/src/platform/posix/posix_rand_arc4random.c new file mode 100644 index 00000000..a3cc265b --- /dev/null +++ b/src/platform/posix/posix_rand_arc4random.c @@ -0,0 +1,24 @@ +// +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// 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. +// + +// arc4random is the preferred source of cryptographic random numbers +// on any platform where it is found. +#include <stdlib.h> + +#include "core/nng_impl.h" + +#ifdef NNG_HAVE_ARC4RANDOM + +uint32_t +nni_random(void) +{ + return (arc4random()); +} + +#endif
\ No newline at end of file diff --git a/src/platform/posix/posix_rand_getrandom.c b/src/platform/posix/posix_rand_getrandom.c new file mode 100644 index 00000000..489b2c76 --- /dev/null +++ b/src/platform/posix/posix_rand_getrandom.c @@ -0,0 +1,40 @@ +// +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// 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. +// + +// getrandom is not as nice as arc4random, but on platforms where it +// exists and arc4random does not, we should use it. +// +// getrandom will block only if the urandom device is not seeded yet. +// That can only happen during very early boot (earlier than we should +// normally be running. This is the only time it can fail with correct +// arguments, and then only if it is interrupted with a signal. + +#include <sys/random.h> + +#include "core/nng_impl.h" + +#ifdef NNG_HAVE_GETRANDOM + +uint32_t +nni_random(void) +{ + uint32_t val; + + // Documentation claims that as long as we are not using + // GRND_RANDOM and buflen < 256, this should never fail. + // The exception here is that we could fail if for some + // reason we got a signal while blocked at very early boot + // (i.e. /dev/urandom was not yet seeded). + if (getrandom(&val, sizeof(val), 0) != sizeof(val)) { + nni_panic("getrandom failed"); + } + return (val); +} + +#endif
\ No newline at end of file diff --git a/src/platform/posix/posix_rand_urandom.c b/src/platform/posix/posix_rand_urandom.c new file mode 100644 index 00000000..32ea6a51 --- /dev/null +++ b/src/platform/posix/posix_rand_urandom.c @@ -0,0 +1,58 @@ +// +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> +// +// 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. +// + +#include <fcntl.h> +#include <pthread.h> +#include <stdint.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include "core/nng_impl.h" + +// 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. Modern advice is to always use /dev/urandom +// unless you have very particular reasons for doing otherwise. +// If you're in this code base, you're probably on either an ancient OS, +// or one of the off-beat ones that hasn't updated for support with +// arc4random or getrandom. + +// We could use ISAAC or something like that to seed it only once, +// but instead we just keep our file descriptor open. This will have +// the apparent effect of leaking these file descriptors across fork. + +static int urandom_fd = -1; +static pthread_mutex_t urandom_lock = PTHREAD_MUTEX_INITIALIZER; + +#ifndef O_CLOEXEC +#define O_CLOEXEC 0u +#endif + +uint32_t +nni_random(void) +{ + int fd; + uint32_t val; + + (void) pthread_mutex_lock(&urandom_lock); + if ((fd = urandom_fd) == -1) { + if ((fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC)) < 0) { + (void) pthread_mutex_unlock(&urandom_lock); + nni_panic("failed to open /dev/urandom"); + } + urandom_fd = fd; + } + (void) pthread_mutex_unlock(&urandom_lock); + + if (read(fd, &val, sizeof(val)) != sizeof(val)) { + nni_panic("failed reading /dev/urandom"); + } + return (val); +}
\ No newline at end of file diff --git a/src/platform/windows/win_rand.c b/src/platform/windows/win_rand.c index afe703ef..21568a81 100644 --- a/src/platform/windows/win_rand.c +++ b/src/platform/windows/win_rand.c @@ -1,5 +1,5 @@ // -// Copyright 2017 Garrett D'Amore <garrett@damore.org> +// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech> // // This software is supplied under the terms of the MIT License, a // copy of which should be located in the distribution where this @@ -11,22 +11,24 @@ #ifdef NNG_PLATFORM_WINDOWS +#ifndef _CRT_RAND_S +#define _CRT_RAND_S +#endif + #include <stdlib.h> -void -nni_plat_seed_prng(void *buf, size_t bufsz) +uint32_t +nni_random(void) { unsigned val; - // The rand_s routine uses RtlGenRandom to get high quality - // pseudo random numbers (i.e. numbers that should be good enough - // for use with crypto keying.) - while (bufsz > sizeof(val)) { - rand_s(&val); - memcpy(buf, &val, sizeof(val)); - buf = (((char *) buf) + sizeof(val)); - bufsz -= sizeof(val); - } + // rand_s is claimed by Microsoft to generate cryptographically + // secure numbers. It also is claimed that this will only fail + // for EINVAL if val is NULL (not the case here). Other error + // conditions might be possible, but we have no way to tell. + // For now we just ignore that possibility. + rand_s(&val); + return ((uint32_t)val); } -#endif // NNG_PLATFORM_WINDOWS +#endif // NNG_PLATFORM_WINDOWS
\ No newline at end of file diff --git a/tests/platform.c b/tests/platform.c index 7546fa7f..fc9eb210 100644 --- a/tests/platform.c +++ b/tests/platform.c @@ -66,10 +66,10 @@ test_clock(void) TEST_CHECK(usdelta >= 200); TEST_CHECK(usdelta < 500); // increased tolerance for CIs if (msdelta > usdelta) { - TEST_CHECK((msdelta - usdelta) < 50); + TEST_CHECK((msdelta - usdelta) < 50); } else { - TEST_CHECK((usdelta - msdelta) < 50); - } + TEST_CHECK((usdelta - msdelta) < 50); + } } void @@ -108,40 +108,65 @@ test_thread(void) nng_thread_destroy(thr); TEST_CHECK(aa.cnt == 1); - nng_cv_free(aa.cv); + nng_cv_free(aa.cv); nng_mtx_free(aa.mx); } void test_condvar(void) { - nng_thread * thr; - int rv; - struct addarg aa; - - TEST_CHECK(nng_mtx_alloc(&aa.mx) == 0); - TEST_CHECK(nng_cv_alloc(&aa.cv, aa.mx) == 0); - aa.cnt = 0; - - TEST_CHECK((rv = nng_thread_create(&thr, add, &aa)) == 0); - - nng_mtx_lock(aa.mx); - while (aa.cnt == 0) { - nng_cv_wait(aa.cv); - } - nng_mtx_unlock(aa.mx); - nng_thread_destroy(thr); - TEST_CHECK(aa.cnt == 1); - - nng_cv_free(aa.cv); - nng_mtx_free(aa.mx); + nng_thread * thr; + int rv; + struct addarg aa; + + TEST_CHECK(nng_mtx_alloc(&aa.mx) == 0); + TEST_CHECK(nng_cv_alloc(&aa.cv, aa.mx) == 0); + aa.cnt = 0; + + TEST_CHECK((rv = nng_thread_create(&thr, add, &aa)) == 0); + + nng_mtx_lock(aa.mx); + while (aa.cnt == 0) { + nng_cv_wait(aa.cv); + } + nng_mtx_unlock(aa.mx); + nng_thread_destroy(thr); + TEST_CHECK(aa.cnt == 1); + + nng_cv_free(aa.cv); + nng_mtx_free(aa.mx); +} + +void +test_random(void) +{ + int same = 0; + uint32_t values[1000]; + + for (int i = 0; i < 1000; i++) { + values[i] = nng_random(); + } + for (int i = 0; i < 1000; i++) { + for (int j = 0; j < i; j++) { + if (values[j] == values[i]) { + same++; + } + } + } + + // 1% reproduction is *highly* unlikely. + // There are 4 billion possible options, we are only looking at + // 1000 of them. In general, it would be an extreme outlier + // to see more than 2 repeats, unless you RNG is biased. + TEST_CHECK_(same < 5, "fewer than 5 in 1000 repeats: %d", same); } TEST_LIST = { { "sleep", test_sleep }, { "clock", test_clock }, { "mutex", test_mutex }, - { "thread", test_thread }, - { "condvar", test_condvar }, + { "thread", test_thread }, + { "condvar", test_condvar }, + { "random", test_random }, { NULL, NULL }, }; |
