From 40363f96a9da84b8a1d4a3cec10447f180662c1a Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Sat, 9 Jan 2016 02:27:32 -0800 Subject: [PATCH] Generic PRNG framework: * A new, unified interface for generating random numbers and filling buffers supporting any engine that fits the UniformRandomNumberGenerator concept; * Automatically seeded replacement for rand using the fast xorshift+ PRNG engine; * A CSPRNG engine that can be used with the new framework when needing to to generate cryptographically secure randomness. * Unit test cleanups to work with new engine. --- Builds/VisualStudio2015/RippleD.vcxproj | 14 +- .../VisualStudio2015/RippleD.vcxproj.filters | 15 +- src/ripple/app/main/Application.cpp | 4 +- src/ripple/app/main/Main.cpp | 39 +--- src/ripple/app/misc/NetworkOPs.cpp | 9 +- src/ripple/basics/random.h | 202 ++++++++++++++++++ src/ripple/core/impl/SNTPClock.cpp | 12 +- src/ripple/crypto/RandomNumbers.h | 67 ------ src/ripple/crypto/csprng.h | 109 ++++++++++ src/ripple/crypto/impl/ECIES.cpp | 8 +- src/ripple/crypto/impl/RandomNumbers.cpp | 72 ------- src/ripple/crypto/impl/csprng.cpp | 138 ++++++++++++ src/ripple/nodestore/tests/Backend.test.cpp | 20 +- src/ripple/nodestore/tests/Base.test.h | 88 ++++---- src/ripple/nodestore/tests/Basics.test.cpp | 22 +- src/ripple/nodestore/tests/Database.test.cpp | 16 +- src/ripple/overlay/impl/PeerImp.cpp | 27 ++- src/ripple/peerfinder/impl/Counts.h | 11 +- src/ripple/protocol/impl/RippleAddress.cpp | 9 +- src/ripple/protocol/impl/SecretKey.cpp | 13 +- src/ripple/protocol/tests/STAmount.test.cpp | 17 +- src/ripple/resource/tests/Logic.test.cpp | 19 +- src/ripple/rpc/handlers/Random.cpp | 8 +- src/ripple/shamap/impl/SHAMapNodeID.cpp | 2 +- src/ripple/shamap/impl/SHAMapSync.cpp | 5 +- src/ripple/shamap/tests/FetchPack.test.cpp | 12 +- src/ripple/shamap/tests/SHAMapSync.test.cpp | 21 +- src/ripple/unity/crypto.cpp | 2 +- 28 files changed, 647 insertions(+), 334 deletions(-) create mode 100644 src/ripple/basics/random.h delete mode 100644 src/ripple/crypto/RandomNumbers.h create mode 100644 src/ripple/crypto/csprng.h delete mode 100644 src/ripple/crypto/impl/RandomNumbers.cpp create mode 100644 src/ripple/crypto/impl/csprng.cpp diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index 04a250ef0b..c33a87d7b6 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -1964,6 +1964,8 @@ + + @@ -2156,6 +2158,8 @@ + + @@ -2176,6 +2180,10 @@ True True + + True + True + True True @@ -2214,18 +2222,12 @@ - - True - True - True True - - diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index 8e33c75f13..e70a976ba8 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -2697,6 +2697,9 @@ ripple\basics + + ripple\basics + ripple\basics @@ -2871,6 +2874,9 @@ ripple\crypto + + ripple\crypto + ripple\crypto @@ -2892,6 +2898,9 @@ ripple\crypto\impl + + ripple\crypto\impl + ripple\crypto\impl @@ -2925,18 +2934,12 @@ ripple\crypto\impl - - ripple\crypto\impl - ripple\crypto\impl ripple\crypto - - ripple\crypto - ripple\crypto diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 3a8f38cc82..4ed53bdddd 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -70,7 +70,7 @@ #include #include #include -#include +#include #include #include #include @@ -844,7 +844,7 @@ public: { if (timer == m_entropyTimer) { - add_entropy (nullptr, 0); + crypto_prng().mix_entropy (); return; } diff --git a/src/ripple/app/main/Main.cpp b/src/ripple/app/main/Main.cpp index 28bbde2d14..4bb64f93d9 100644 --- a/src/ripple/app/main/Main.cpp +++ b/src/ripple/app/main/Main.cpp @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include #include #include @@ -44,7 +44,6 @@ #include #include #include -#include #include #if defined(BEAST_LINUX) || defined(BEAST_MAC) || defined(BEAST_BSD) @@ -113,7 +112,7 @@ void startServer (Application& app) // Try to write out some entropy to use the next time we start. auto entropy = getEntropyFile (app.config()); if (!entropy.empty ()) - stir_entropy (entropy.string ()); + crypto_prng().save_state(entropy.string ()); } void printHelp (const po::options_description& desc) @@ -189,30 +188,6 @@ int run (int argc, char** argv) setCallingThreadName ("main"); - { - // We want to seed the RNG early. We acquire a small amount of - // questionable quality entropy from the current time and our - // environment block which will get stirred into the RNG pool - // along with high-quality entropy from the system. - struct entropy_t - { - std::uint64_t timestamp; - std::size_t tid; - std::uintptr_t ptr[4]; - }; - - auto entropy = std::make_unique (); - - entropy->timestamp = beast::Time::currentTimeMillis (); - entropy->tid = std::hash () (std::this_thread::get_id ()); - entropy->ptr[0] = reinterpret_cast(entropy.get ()); - entropy->ptr[1] = reinterpret_cast(&argc); - entropy->ptr[2] = reinterpret_cast(argv); - entropy->ptr[3] = reinterpret_cast(argv[0]); - - add_entropy (entropy.get (), sizeof (entropy_t)); - } - po::variables_map vm; std::string importText; @@ -333,10 +308,12 @@ int run (int argc, char** argv) config->LEDGER_HISTORY = 0; } - // Use any previously available entropy to stir the pool - auto entropy = getEntropyFile (*config); - if (!entropy.empty ()) - stir_entropy (entropy.string ()); + { + // Stir any previously saved entropy into the pool: + auto entropy = getEntropyFile (*config); + if (!entropy.empty ()) + crypto_prng().load_state(entropy.string ()); + } if (vm.count ("start")) config->START_UP = Config::FRESH; diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 6aced831eb..5752aaa567 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -53,7 +54,7 @@ #include #include #include -#include +#include #include #include #include @@ -70,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -2528,7 +2530,10 @@ bool NetworkOPsImp::subServer (InfoSub::ref isrListener, Json::Value& jvResult, jvResult[jss::stand_alone] = m_standalone; // CHECKME: is it necessary to provide a random number here? - random_fill (uRandom.begin (), uRandom.size ()); + beast::rngfill ( + uRandom.begin(), + uRandom.size(), + crypto_prng()); jvResult[jss::random] = to_string (uRandom); jvResult[jss::server_status] = strOperatingMode (); diff --git a/src/ripple/basics/random.h b/src/ripple/basics/random.h new file mode 100644 index 0000000000..3734afe682 --- /dev/null +++ b/src/ripple/basics/random.h @@ -0,0 +1,202 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or 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. +*/ +//============================================================================== + +#ifndef RIPPLE_BASICS_RANDOM_H_INCLUDED +#define RIPPLE_BASICS_RANDOM_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +static_assert ( + std::is_integral ::value && + std::is_unsigned ::value, + "The Ripple default PRNG engine must return an unsigned integral type."); + +static_assert ( + std::numeric_limits::max() >= + std::numeric_limits::max(), + "The Ripple default PRNG engine return must be at least 64 bits wide."); + +namespace detail { + + +// Determines if a type can be called like an Engine +template +using is_engine = + beast::is_call_possible; +} + +/** Return the default random engine. + + This engine is guaranteed to be deterministic, but by + default will be randomly seeded. It is NOT cryptographically + secure and MUST NOT be used to generate randomness that + will be used for keys, secure cookies, IVs, padding, etc. + + Each thread gets its own instance of the engine which + will be randomly seeded. +*/ +inline +beast::xor_shift_engine& +default_prng () +{ + static + boost::thread_specific_ptr engine; + + if (!engine.get()) + { + std::random_device rng; + + std::uint64_t seed = rng(); + + for (int i = 0; i < 6; ++i) + { + if (seed == 0) + seed = rng(); + + seed ^= (seed << (7 - i)) * rng(); + } + + engine.reset (new beast::xor_shift_engine (seed)); + } + + return *engine; +} + +/** Return a uniformly distributed random integer. + + @param min The smallest value to return. If not specified + the value defaults to 0. + @param max The largest value to return. If not specified + the value defaults to the largest value that + can be represented. + + The randomness is generated by the specified engine (or + the default engine if one is not specified). The result + is only cryptographicallys secure if the PRNG engine is + cryptographically secure. + + @note The range is always a closed interval, so calling + rand_int(-5, 15) can return any integer in the + closed interval [-5, 15]; similarly, calling + rand_int(7) can return any integer in the closed + interval [0, 7]. +*/ +/** @{ */ +template +std::enable_if_t< + std::is_integral::value && + detail::is_engine::value, +Integral> +rand_int ( + Engine& engine, + Integral min, + Integral max) +{ + assert (max > min); + + // This should have no state and constructing it should + // be very cheap. If that turns out not to be the case + // it could be hand-optimized. + return std::uniform_int_distribution(min, max)(engine); +} + +template +std::enable_if_t::value, Integral> +rand_int ( + Integral min, + Integral max) +{ + return rand_int (default_prng(), min, max); +} + +template +std::enable_if_t< + std::is_integral::value && + detail::is_engine::value, +Integral> +rand_int ( + Engine& engine, + Integral max) +{ + return rand_int (engine, Integral(0), max); +} + +template +std::enable_if_t::value, Integral> +rand_int (Integral max) +{ + return rand_int (default_prng(), max); +} + +template +std::enable_if_t< + std::is_integral::value && + detail::is_engine::value, +Integral> +rand_int ( + Engine& engine) +{ + return rand_int ( + engine, + std::numeric_limits::max()); +} + +template +std::enable_if_t::value, Integral> +rand_int () +{ + return rand_int ( + default_prng(), + std::numeric_limits::max()); +} +/** @} */ + +/** Return a random boolean value */ +/** @{ */ +template +inline +bool +rand_bool (Engine& engine) +{ + return rand_int (engine, 1) == 1; +} + +inline +bool +rand_bool () +{ + return rand_bool (default_prng()); +} +/** @} */ + +} // ripple + +#endif // RIPPLE_BASICS_RANDOM_H_INCLUDED diff --git a/src/ripple/core/impl/SNTPClock.cpp b/src/ripple/core/impl/SNTPClock.cpp index c12c6fba1a..740fcb6d8c 100644 --- a/src/ripple/core/impl/SNTPClock.cpp +++ b/src/ripple/core/impl/SNTPClock.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include @@ -351,12 +351,16 @@ public: return; } - ip::udp::resolver::iterator sel = it; + assert (it != ip::udp::resolver::iterator()); + + auto sel = it; int i = 1; while (++it != ip::udp::resolver::iterator()) - if ((rand () % ++i) == 0) + { + if (rand_int (i++) == 0) sel = it; + } if (sel != ip::udp::resolver::iterator ()) { @@ -374,7 +378,7 @@ public: query.replied = false; query.sent = now; - random_fill (&query.nonce); + query.nonce = rand_int(); reinterpret_cast (SNTPQueryData)[NTP_OFF_XMITTS_INT] = static_cast (time (nullptr)) + NTP_UNIX_OFFSET; reinterpret_cast (SNTPQueryData)[NTP_OFF_XMITTS_FRAC] = query.nonce; socket_.async_send_to(buffer(SNTPQueryData, 48), diff --git a/src/ripple/crypto/RandomNumbers.h b/src/ripple/crypto/RandomNumbers.h deleted file mode 100644 index eef8c04a32..0000000000 --- a/src/ripple/crypto/RandomNumbers.h +++ /dev/null @@ -1,67 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or 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. -*/ -//============================================================================== - -#ifndef RIPPLE_CRYPTO_RANDOMNUMBERS_H_INCLUDED -#define RIPPLE_CRYPTO_RANDOMNUMBERS_H_INCLUDED - -#include -#include - -namespace ripple { - -/** Stir the RNG using entropy from stable storage. - - @param file the file from which state is loaded and into - which it is saved. - - @return true if the pool has sufficient entropy; false - otherwise. -*/ -bool stir_entropy (std::string file); - -/** Adds entropy to the RNG pool. - - @param buffer An optional buffer that contains random data. - @param count The size of the buffer, in bytes (or 0). - - This can be called multiple times to stir entropy into the pool - without any locks. -*/ -void add_entropy (void* buffer = nullptr, int count = 0); - -/** Generate random bytes, suitable for cryptography. */ -/**@{*/ -/** - @param buffer The place to store the bytes. - @param count The number of bytes to generate. -*/ -void random_fill (void* buffer, int count); - -/** Fills a POD object with random data */ -template ::value>> -void -random_fill (T* object) -{ - random_fill (object, sizeof (T)); -} -/**@}*/ - -} - -#endif diff --git a/src/ripple/crypto/csprng.h b/src/ripple/crypto/csprng.h new file mode 100644 index 0000000000..736f001273 --- /dev/null +++ b/src/ripple/crypto/csprng.h @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or 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. +*/ +//============================================================================== + +#ifndef RIPPLE_CRYPTO_RANDOM_H_INCLUDED +#define RIPPLE_CRYPTO_RANDOM_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +/** A cryptographically secure random number engine + + The engine is thread-safe (it uses a lock to serialize + access) and will, automatically, mix in some randomness + from std::random_device. + + Meets the requirements of UniformRandomNumberEngine +*/ +class csprng_engine +{ +private: + std::mutex mutex_; + + void + mix ( + void* buffer, + std::size_t count, + double bitsPerByte); + +public: + using result_type = std::uint64_t; + + csprng_engine(csprng_engine const&) = delete; + csprng_engine& operator=(csprng_engine const&) = delete; + + csprng_engine(csprng_engine&&) = delete; + csprng_engine& operator=(csprng_engine&&) = delete; + + csprng_engine (); + ~csprng_engine (); + + /** Mix entropy into the pool */ + void + mix_entropy (void* buffer = nullptr, std::size_t count = 0); + + /** Load entropy from the specified file */ + void + load_state (std::string const& file); + + /** Save entropy to the specified file */ + void + save_state (std::string const& file); + + /** Generate a random integer */ + result_type + operator()(); + + /** Fill a buffer with the requested amount of random data */ + void + operator()(void *ptr, std::size_t count); + + /* The smallest possible value that can be returned */ + static constexpr + result_type + min() + { + return std::numeric_limits::min(); + } + + /* The largest possible value that can be returned */ + static constexpr + result_type + max() + { + return std::numeric_limits::max(); + } +}; + +/** The default cryptographically secure PRNG + + Use this when you need to generate random numbers or + data that will be used for encryption or passed into + cryptographic routines. + + This meets the requirements of UniformRandomNumberEngine +*/ +csprng_engine& crypto_prng(); + +} + +#endif diff --git a/src/ripple/crypto/impl/ECIES.cpp b/src/ripple/crypto/impl/ECIES.cpp index ef6e08bc05..8e1588a33f 100644 --- a/src/ripple/crypto/impl/ECIES.cpp +++ b/src/ripple/crypto/impl/ECIES.cpp @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #include #include #include @@ -142,7 +143,10 @@ Blob encryptECIES (uint256 const& secretKey, Blob const& publicKey, Blob const& { ECIES_ENC_IV_TYPE iv; - random_fill (iv.begin (), ECIES_ENC_BLK_SIZE); + beast::rngfill ( + iv.begin (), + ECIES_ENC_BLK_SIZE, + crypto_prng()); ECIES_ENC_KEY_TYPE secret; ECIES_HMAC_KEY_TYPE hmacKey; diff --git a/src/ripple/crypto/impl/RandomNumbers.cpp b/src/ripple/crypto/impl/RandomNumbers.cpp deleted file mode 100644 index 1c287a8eaa..0000000000 --- a/src/ripple/crypto/impl/RandomNumbers.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or 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. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -bool stir_entropy (std::string file) -{ - // First, we attempt to stir any existing saved entropy - // into the pool: no use letting it go to waste. - RAND_load_file (file.c_str (), 1024); - - // And now, we extract some entropy out, and save it for - // the future. If the quality of the entropy isn't great - // then we let the user know. - return RAND_write_file (file.c_str ()) != -1; -} - -void add_entropy (void* buffer, int count) -{ - assert (buffer == nullptr || count != 0); - - // If we are passed data in we use it but conservatively estimate that it - // contains only around 2 bits of entropy per byte. - if (buffer != nullptr && count != 0) - RAND_add (buffer, count, count / 4.0); - - // And try to add some entropy from the system - unsigned int rdbuf[32]; - - std::random_device rd; - - for (auto& x : rdbuf) - x = rd (); - - // In all our supported platforms, std::random_device is non-deterministic - // but we conservatively estimate it has around 4 bits of entropy per byte. - RAND_add (rdbuf, sizeof (rdbuf), sizeof (rdbuf) / 2.0); -} - -void random_fill (void* buffer, int count) -{ - assert (count > 0); - - if (RAND_bytes (reinterpret_cast (buffer), count) != 1) - Throw ("Insufficient entropy in pool."); -} - -} diff --git a/src/ripple/crypto/impl/csprng.cpp b/src/ripple/crypto/impl/csprng.cpp new file mode 100644 index 0000000000..648abf4fe9 --- /dev/null +++ b/src/ripple/crypto/impl/csprng.cpp @@ -0,0 +1,138 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or 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. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +void +csprng_engine::mix ( + void* data, std::size_t size, double bitsPerByte) +{ + assert (data != nullptr); + assert (size != 0); + assert (bitsPerByte != 0); + + std::lock_guard lock (mutex_); + RAND_add (data, size, (size * bitsPerByte) / 8.0); +} + +csprng_engine::csprng_engine () +{ + mix_entropy (); +} + +csprng_engine::~csprng_engine () +{ + RAND_cleanup (); +} + +void +csprng_engine::load_state (std::string const& file) +{ + if (!file.empty()) + { + std::lock_guard lock (mutex_); + RAND_load_file (file.c_str (), 1024); + RAND_write_file (file.c_str ()); + } +} + +void +csprng_engine::save_state (std::string const& file) +{ + if (!file.empty()) + { + std::lock_guard lock (mutex_); + RAND_write_file (file.c_str ()); + } +} + +void +csprng_engine::mix_entropy (void* buffer, std::size_t count) +{ + std::array entropy; + + { + // On every platform we support, std::random_device + // is non-deterministic and should provide some good + // quality entropy. + std::random_device rd; + + for (auto& e : entropy) + e = rd(); + } + + // Assume 2 bits per byte for the system entropy: + mix ( + entropy.data(), + entropy.size() * sizeof(std::random_device::result_type), + 2.0); + + // We want to be extremely conservative about estimating + // how much entropy the buffer the user gives us contains + // and assume only 0.5 bits of entropy per byte: + if (buffer != nullptr && count != 0) + mix (buffer, count, 0.5); +} + +csprng_engine::result_type +csprng_engine::operator()() +{ + result_type ret; + + std::lock_guard lock (mutex_); + + auto const result = RAND_bytes ( + reinterpret_cast(&ret), + sizeof(ret)); + + if (result == 0) + Throw ("Insufficient entropy"); + + return ret; +} + +void +csprng_engine::operator()(void *ptr, std::size_t count) +{ + std::lock_guard lock (mutex_); + + auto const result = RAND_bytes ( + reinterpret_cast(ptr), + count); + + if (result != 1) + Throw ("Insufficient entropy"); +} + +csprng_engine& crypto_prng() +{ + static csprng_engine engine; + return engine; +} + +} diff --git a/src/ripple/nodestore/tests/Backend.test.cpp b/src/ripple/nodestore/tests/Backend.test.cpp index c97e356cac..a8296d4bf4 100644 --- a/src/ripple/nodestore/tests/Backend.test.cpp +++ b/src/ripple/nodestore/tests/Backend.test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace ripple { namespace NodeStore { @@ -31,8 +32,10 @@ namespace NodeStore { class Backend_test : public TestBase { public: - void testBackend (std::string const& type, std::int64_t const seedValue, - int numObjectsToTest = 2000) + void testBackend ( + std::string const& type, + std::uint64_t const seedValue, + int numObjectsToTest = 2000) { DummyScheduler scheduler; @@ -43,9 +46,11 @@ public: params.set ("type", type); params.set ("path", path.getFullPathName ().toStdString ()); + beast::xor_shift_engine rng (seedValue); + // Create a batch - Batch batch; - createPredictableBatch (batch, numObjectsToTest, seedValue); + auto batch = createPredictableBatch ( + numObjectsToTest, rng()); beast::Journal j; @@ -66,8 +71,11 @@ public: { // Reorder and read the copy again + std::shuffle ( + batch.begin(), + batch.end(), + rng); Batch copy; - beast::UnitTestUtilities::repeatableShuffle (batch.size (), batch, seedValue); fetchCopyOfBatch (*backend, ©, batch); expect (areBatchesEqual (batch, copy), "Should be equal"); } @@ -92,7 +100,7 @@ public: void run () { - int const seedValue = 50; + std::uint64_t const seedValue = 50; testBackend ("nudb", seedValue); diff --git a/src/ripple/nodestore/tests/Base.test.h b/src/ripple/nodestore/tests/Base.test.h index a4db232c1d..52918b63d4 100644 --- a/src/ripple/nodestore/tests/Base.test.h +++ b/src/ripple/nodestore/tests/Base.test.h @@ -21,9 +21,11 @@ #define RIPPLE_NODESTORE_BASE_H_INCLUDED #include +#include #include #include -#include +#include +#include #include #include @@ -66,61 +68,47 @@ class TestBase : public beast::unit_test::suite public: // Tunable parameters // - enum - { - maxPayloadBytes = 2000, - numObjectsToTest = 2000 - }; - - // Creates predictable objects - class PredictableObjectFactory - { - public: - explicit PredictableObjectFactory (std::int64_t seedValue) - : r (seedValue) - { - } - - std::shared_ptr createObject () - { - NodeObjectType type; - switch (r.nextInt (4)) - { - case 0: type = hotLEDGER; break; - case 2: type = hotACCOUNT_NODE; break; - case 3: type = hotTRANSACTION_NODE; break; - default: - case 1: // was hotTRANSACTION - type = hotUNKNOWN; - break; - }; - - uint256 hash; - r.fillBitsRandomly (hash.begin (), hash.size ()); - - int const payloadBytes = 1 + r.nextInt (maxPayloadBytes); - - Blob data (payloadBytes); - - r.fillBitsRandomly (data.data (), payloadBytes); - - return NodeObject::createObject(type, std::move(data), hash); - } - - private: - beast::Random r; - }; + static std::size_t const minPayloadBytes = 1; + static std::size_t const maxPayloadBytes = 2000; + static int const numObjectsToTest = 2000; public: - // Create a predictable batch of objects - static void createPredictableBatch(Batch& batch, int numObjects, - std::int64_t seedValue) { + // Create a predictable batch of objects + static + Batch createPredictableBatch( + int numObjects, std::uint64_t seed) + { + Batch batch; batch.reserve (numObjects); - PredictableObjectFactory factory (seedValue); + beast::xor_shift_engine rng (seed); for (int i = 0; i < numObjects; ++i) - batch.push_back (factory.createObject ()); + { + NodeObjectType type; + + switch (rand_int(rng, 3)) + { + case 0: type = hotLEDGER; break; + case 1: type = hotACCOUNT_NODE; break; + case 2: type = hotTRANSACTION_NODE; break; + case 3: type = hotUNKNOWN; break; + } + + uint256 hash; + beast::rngfill (hash.begin(), hash.size(), rng); + + Blob blob ( + rand_int(rng, + minPayloadBytes, maxPayloadBytes)); + beast::rngfill (blob.data(), blob.size(), rng); + + batch.push_back ( + NodeObject::createObject( + type, std::move(blob), hash)); + } + + return batch; } // Compare two batches for equality diff --git a/src/ripple/nodestore/tests/Basics.test.cpp b/src/ripple/nodestore/tests/Basics.test.cpp index 0d9f6079c8..7bbc320c63 100644 --- a/src/ripple/nodestore/tests/Basics.test.cpp +++ b/src/ripple/nodestore/tests/Basics.test.cpp @@ -33,31 +33,31 @@ class NodeStoreBasic_test : public TestBase { public: // Make sure predictable object generation works! - void testBatches (std::int64_t const seedValue) + void testBatches (std::uint64_t const seedValue) { testcase ("batch"); - Batch batch1; - createPredictableBatch (batch1, numObjectsToTest, seedValue); + auto batch1 = createPredictableBatch ( + numObjectsToTest, seedValue); - Batch batch2; - createPredictableBatch (batch2, numObjectsToTest, seedValue); + auto batch2 = createPredictableBatch ( + numObjectsToTest, seedValue); expect (areBatchesEqual (batch1, batch2), "Should be equal"); - Batch batch3; - createPredictableBatch (batch3, numObjectsToTest, seedValue+1); + auto batch3 = createPredictableBatch ( + numObjectsToTest, seedValue + 1); expect (! areBatchesEqual (batch1, batch3), "Should not be equal"); } // Checks encoding/decoding blobs - void testBlobs (std::int64_t const seedValue) + void testBlobs (std::uint64_t const seedValue) { testcase ("encoding"); - Batch batch; - createPredictableBatch (batch, numObjectsToTest, seedValue); + auto batch = createPredictableBatch ( + numObjectsToTest, seedValue); EncodedBlob encoded; for (int i = 0; i < batch.size (); ++i) @@ -79,7 +79,7 @@ public: void run () { - std::int64_t const seedValue = 50; + std::uint64_t const seedValue = 50; testBatches (seedValue); diff --git a/src/ripple/nodestore/tests/Database.test.cpp b/src/ripple/nodestore/tests/Database.test.cpp index 2b163cb9ab..9cfecf0416 100644 --- a/src/ripple/nodestore/tests/Database.test.cpp +++ b/src/ripple/nodestore/tests/Database.test.cpp @@ -22,6 +22,7 @@ #include #include #include +#include namespace ripple { namespace NodeStore { @@ -40,8 +41,8 @@ public: srcParams.set ("path", node_db.getFullPathName ().toStdString ()); // Create a batch - Batch batch; - createPredictableBatch (batch, numObjectsToTest, seedValue); + auto batch = createPredictableBatch ( + numObjectsToTest, seedValue); beast::Journal j; @@ -102,9 +103,11 @@ public: nodeParams.set ("type", type); nodeParams.set ("path", node_db.getFullPathName ().toStdString ()); + beast::xor_shift_engine rng (seedValue); + // Create a batch - Batch batch; - createPredictableBatch (batch, numObjectsToTest, seedValue); + auto batch = createPredictableBatch ( + numObjectsToTest, rng()); beast::Journal j; @@ -125,8 +128,11 @@ public: { // Reorder and read the copy again + std::shuffle ( + batch.begin(), + batch.end(), + rng); Batch copy; - beast::UnitTestUtilities::repeatableShuffle (batch.size (), batch, seedValue); fetchCopyOfBatch (*db, ©, batch); expect (areBatchesEqual (batch, copy), "Should be equal"); } diff --git a/src/ripple/overlay/impl/PeerImp.cpp b/src/ripple/overlay/impl/PeerImp.cpp index f9065eadf6..ec71fc3a3b 100644 --- a/src/ripple/overlay/impl/PeerImp.cpp +++ b/src/ripple/overlay/impl/PeerImp.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -546,7 +547,7 @@ PeerImp::onTimer (error_code const& ec) { // Make sequence unpredictable enough that a peer // can't fake their latency - lastPingSeq_ = (rand() % 65536); + lastPingSeq_ = rand_int (65535); lastPingTime_ = clock_type::now(); protocol::TMPing message; @@ -2371,23 +2372,21 @@ PeerImp::getScore (bool haveItem) const { // Random component of score, used to break ties and avoid // overloading the "best" peer - static const int spRandom = 10000; + static const int spRandomMax = 9999; // Score for being very likely to have the thing we are - // look for - // Should be roughly spRandom - static const int spHaveItem = 10000; + // look for; should be roughly spRandomMax + static const int spHaveItem = 10000; - // Score reduction for each millisecond of latency - // Should be roughly spRandom divided by - // the maximum reasonable latency - static const int spLatency = 30; + // Score reduction for each millisecond of latency; should + // be roughly spRandomMax divided by the maximum reasonable + // latency + static const int spLatency = 30; - // Penalty for unknown latency - // Should be roughly spRandom - static const int spNoLatency = 8000; + // Penalty for unknown latency; should be roughly spRandomMax + static const int spNoLatency = 8000; - int score = rand() % spRandom; + int score = rand_int(spRandomMax); if (haveItem) score += spHaveItem; @@ -2395,9 +2394,9 @@ PeerImp::getScore (bool haveItem) const std::chrono::milliseconds latency; { std::lock_guard sl (recentLock_); - latency = latency_; } + if (latency != std::chrono::milliseconds (-1)) score -= latency.count() * spLatency; else diff --git a/src/ripple/peerfinder/impl/Counts.h b/src/ripple/peerfinder/impl/Counts.h index 01f5a774cf..e980d6770a 100644 --- a/src/ripple/peerfinder/impl/Counts.h +++ b/src/ripple/peerfinder/impl/Counts.h @@ -20,10 +20,10 @@ #ifndef RIPPLE_PEERFINDER_COUNTS_H_INCLUDED #define RIPPLE_PEERFINDER_COUNTS_H_INCLUDED +#include #include #include #include -#include namespace ripple { namespace PeerFinder { @@ -46,14 +46,9 @@ public: , m_acceptCount (0) , m_closingCount (0) { -#if 1 - std::random_device rd; - std::mt19937 gen (rd()); m_roundingThreshold = - std::generate_canonical (gen); -#else - m_roundingThreshold = Random::getSystemRandom().nextDouble(); -#endif + std::generate_canonical ( + default_prng()); } //-------------------------------------------------------------------------- diff --git a/src/ripple/protocol/impl/RippleAddress.cpp b/src/ripple/protocol/impl/RippleAddress.cpp index 838d26d049..799ff7de91 100644 --- a/src/ripple/protocol/impl/RippleAddress.cpp +++ b/src/ripple/protocol/impl/RippleAddress.cpp @@ -20,11 +20,12 @@ #include #include #include +#include #include #include #include #include -#include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -740,7 +742,10 @@ void RippleAddress::setSeedRandom () // XXX Maybe we should call MakeNewKey uint128 key; - random_fill (key.begin (), key.size ()); + beast::rngfill ( + key.begin(), + key.size(), + crypto_prng()); RippleAddress::setSeed (key); } diff --git a/src/ripple/protocol/impl/SecretKey.cpp b/src/ripple/protocol/impl/SecretKey.cpp index 2db6628596..2c6cab9166 100644 --- a/src/ripple/protocol/impl/SecretKey.cpp +++ b/src/ripple/protocol/impl/SecretKey.cpp @@ -23,8 +23,9 @@ #include #include #include -#include +#include #include +#include #include #include @@ -150,7 +151,10 @@ Seed randomSeed() { std::uint8_t buf[16]; - random_fill(buf, sizeof(buf)); + beast::rngfill( + buf, + sizeof(buf), + crypto_prng()); Seed seed(Slice{ buf, sizeof(buf) }); beast::secure_erase(buf, sizeof(buf)); return seed; @@ -170,7 +174,10 @@ SecretKey randomSecretKey() { std::uint8_t buf[32]; - random_fill(buf, sizeof(buf)); + beast::rngfill( + buf, + sizeof(buf), + crypto_prng()); SecretKey sk(Slice{ buf, sizeof(buf) }); beast::secure_erase(buf, sizeof(buf)); return sk; diff --git a/src/ripple/protocol/tests/STAmount.test.cpp b/src/ripple/protocol/tests/STAmount.test.cpp index 63a05d0cd3..1a496fa8d8 100644 --- a/src/ripple/protocol/tests/STAmount.test.cpp +++ b/src/ripple/protocol/tests/STAmount.test.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -407,9 +408,7 @@ public: for (int i = 0; i < 16; ++i) { - std::uint64_t r = rand (); - r <<= 32; - r |= rand (); + std::uint64_t r = rand_int(); b.setuint64 (r); if (b.getuint64 () != r) @@ -458,7 +457,11 @@ public: roundTest (7, 11, 44); for (int i = 0; i <= 100000; ++i) - mulTest (rand () % 10000000, rand () % 10000000); + { + mulTest ( + rand_int(10000000), + rand_int(10000000)); + } } //-------------------------------------------------------------------------- @@ -484,12 +487,6 @@ public: expect (bigDsmall == zero, "small/big != 0: " + bigDsmall.getText ()); -#if 0 - // TODO(tom): this test makes no sense - we should have no way to have - // the currency not be XRP while the account is XRP. - bigDsmall = divide (smallValue, bigNative, noCurrency(), xrpAccount ()); -#endif - expect (bigDsmall == zero, "small/bigNative != 0: " + bigDsmall.getText ()); diff --git a/src/ripple/resource/tests/Logic.test.cpp b/src/ripple/resource/tests/Logic.test.cpp index 15ea9b25e5..5e7f9e95dd 100644 --- a/src/ripple/resource/tests/Logic.test.cpp +++ b/src/ripple/resource/tests/Logic.test.cpp @@ -19,9 +19,9 @@ #include #include +#include #include #include -#include #include namespace ripple { @@ -60,16 +60,15 @@ public: void createGossip (Gossip& gossip) { - beast::Random r; - int const v (10 + r.nextInt (10)); - int const n (10 + r.nextInt (10)); + int const v (10 + rand_int(9)); + int const n (10 + rand_int(9)); gossip.items.reserve (n); for (int i = 0; i < n; ++i) { Gossip::Item item; - item.balance = 100 + r.nextInt (500); + item.balance = 100 + rand_int(499); item.address = beast::IP::Endpoint ( - beast::IP::AddressV4 (207, 127, 82, v + i)); + beast::IP::AddressV4 (192, 0, 2, v + i)); gossip.items.push_back (item); } } @@ -84,7 +83,7 @@ public: Charge const fee (dropThreshold + 1); beast::IP::Endpoint const addr ( - beast::IP::Endpoint::from_string ("207.127.82.2")); + beast::IP::Endpoint::from_string ("192.0.2.2")); { Consumer c (logic.newInboundEndpoint (addr)); @@ -191,7 +190,7 @@ public: Gossip::Item item; item.balance = 100; item.address = beast::IP::Endpoint ( - beast::IP::AddressV4 (207, 127, 82, 1)); + beast::IP::AddressV4 (192, 0, 2, 1)); g.items.push_back (item); logic.importConsumers ("g", g); @@ -206,7 +205,7 @@ public: TestLogic logic (j); { - beast::IP::Endpoint address (beast::IP::Endpoint::from_string ("207.127.82.1")); + beast::IP::Endpoint address (beast::IP::Endpoint::from_string ("192.0.2.1")); Consumer c (logic.newInboundEndpoint (address)); Charge fee (1000); j.info << @@ -222,7 +221,7 @@ public: } { - beast::IP::Endpoint address (beast::IP::Endpoint::from_string ("207.127.82.2")); + beast::IP::Endpoint address (beast::IP::Endpoint::from_string ("192.0.2.2")); Consumer c (logic.newInboundEndpoint (address)); Charge fee (1000); j.info << diff --git a/src/ripple/rpc/handlers/Random.cpp b/src/ripple/rpc/handlers/Random.cpp index e57e84d04c..4845848352 100644 --- a/src/ripple/rpc/handlers/Random.cpp +++ b/src/ripple/rpc/handlers/Random.cpp @@ -18,12 +18,13 @@ //============================================================================== #include -#include +#include #include #include #include #include #include +#include namespace ripple { @@ -42,7 +43,10 @@ Json::Value doRandom (RPC::Context& context) try { uint256 rand; - random_fill (rand.begin (), rand.size ()); + beast::rngfill ( + rand.begin(), + rand.size(), + crypto_prng()); Json::Value jvResult; jvResult[jss::random] = to_string (rand); diff --git a/src/ripple/shamap/impl/SHAMapNodeID.cpp b/src/ripple/shamap/impl/SHAMapNodeID.cpp index 0e3547f802..f688bd5024 100644 --- a/src/ripple/shamap/impl/SHAMapNodeID.cpp +++ b/src/ripple/shamap/impl/SHAMapNodeID.cpp @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include #include diff --git a/src/ripple/shamap/impl/SHAMapSync.cpp b/src/ripple/shamap/impl/SHAMapSync.cpp index 738be14d34..087a8dff9a 100644 --- a/src/ripple/shamap/impl/SHAMapSync.cpp +++ b/src/ripple/shamap/impl/SHAMapSync.cpp @@ -18,6 +18,7 @@ //============================================================================== #include +#include #include #include #include @@ -162,7 +163,7 @@ SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector // (randomly selected) inner node. This increases the likelihood // that the two threads will produce different request sets (which is // more efficient than sending identical requests). - int firstChild = rand() % 256; + int firstChild = rand_int(255); int currentChild = 0; bool fullBelow = true; @@ -212,7 +213,7 @@ SHAMap::getMissingNodes(std::vector& nodeIDs, std::vector // Switch to processing the child node node = static_cast(d); nodeID = childID; - firstChild = rand() % 256; + firstChild = rand_int(255); currentChild = 0; fullBelow = true; } diff --git a/src/ripple/shamap/tests/FetchPack.test.cpp b/src/ripple/shamap/tests/FetchPack.test.cpp index c8fcd01093..73aa9663f3 100644 --- a/src/ripple/shamap/tests/FetchPack.test.cpp +++ b/src/ripple/shamap/tests/FetchPack.test.cpp @@ -22,10 +22,11 @@ #include #include #include +#include #include #include #include -#include +#include #include #include #include @@ -84,17 +85,20 @@ public: }; std::shared_ptr - make_random_item (beast::Random& r) + make_random_item (beast::xor_shift_engine& r) { Serializer s; for (int d = 0; d < 3; ++d) - s.add32 (r.nextInt ()); + s.add32 (ripple::rand_int(r)); return std::make_shared ( s.getSHA512Half(), s.peekData ()); } void - add_random_items (std::size_t n, Table& t, beast::Random& r) + add_random_items ( + std::size_t n, + Table& t, + beast::xor_shift_engine& r) { while (n--) { diff --git a/src/ripple/shamap/tests/SHAMapSync.test.cpp b/src/ripple/shamap/tests/SHAMapSync.test.cpp index 5898e5ebac..33d8d5647c 100644 --- a/src/ripple/shamap/tests/SHAMapSync.test.cpp +++ b/src/ripple/shamap/tests/SHAMapSync.test.cpp @@ -21,10 +21,10 @@ #include #include #include +#include #include #include #include -#include // DEPRECATED namespace ripple { namespace tests { @@ -40,7 +40,8 @@ public: { Serializer s; - for (int d = 0; d < 3; ++d) s.add32 (rand ()); + for (int d = 0; d < 3; ++d) + s.add32 (rand_int()); return std::make_shared( s.getSHA512Half(), s.peekData ()); @@ -92,13 +93,7 @@ public: void run () { - unsigned int seed; - - // VFALCO DEPRECATED Should use C++11 - RAND_pseudo_bytes (reinterpret_cast (&seed), sizeof (seed)); - srand (seed); - - beast::Journal const j; // debug journal + beast::Journal const j; // debug journal TestFamily f(j); SHAMap source (SHAMapType::FREE, f); SHAMap destination (SHAMapType::FREE, f); @@ -123,9 +118,9 @@ public: destination.setSynching (); - unexpected (!source.getNodeFat (SHAMapNodeID (), nodeIDs, gotNodes, - (rand () % 2) == 0, rand () % 3), - "GetNodeFat"); + unexpected (!source.getNodeFat ( + SHAMapNodeID (), nodeIDs, gotNodes, + rand_bool(), rand_int(2)), "GetNodeFat"); unexpected (gotNodes.size () < 1, "NodeSize"); @@ -153,7 +148,7 @@ public: for (nodeIDIterator = nodeIDs.begin (); nodeIDIterator != nodeIDs.end (); ++nodeIDIterator) { if (!source.getNodeFat (*nodeIDIterator, gotNodeIDs, gotNodes, - (rand () % 2) == 0, rand () % 3)) + rand_bool(), rand_int(2))) { fail ("GetNodeFat"); } diff --git a/src/ripple/unity/crypto.cpp b/src/ripple/unity/crypto.cpp index b99c49411a..fa9d8db72f 100644 --- a/src/ripple/unity/crypto.cpp +++ b/src/ripple/unity/crypto.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include