diff options
author | Jack Lloyd <[email protected]> | 2016-01-15 19:42:30 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-07-17 10:43:34 -0400 |
commit | 8a1aead31c9ae9caa405c6951de8aa51d6a4b751 (patch) | |
tree | ac0c166c8b98a4c25b69c91aa4d5c2d0bc5bda42 /src/lib/rng | |
parent | cd1e2d3bff92a2d91343541e2cf83287dce87c6f (diff) |
Switch to HMAC_DRBG for all RNG generation.
Add support and tests for additional_data param to HMAC_DRBG
Add Stateful_RNG class which has fork detection and periodic reseeding.
AutoSeeded_RNG passes the current pid and time as additional_data
Diffstat (limited to 'src/lib/rng')
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.cpp | 138 | ||||
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.h | 35 | ||||
-rw-r--r-- | src/lib/rng/info.txt | 6 | ||||
-rw-r--r-- | src/lib/rng/rng.cpp | 123 | ||||
-rw-r--r-- | src/lib/rng/rng.h | 217 | ||||
-rw-r--r-- | src/lib/rng/system_rng/system_rng.cpp | 76 | ||||
-rw-r--r-- | src/lib/rng/system_rng/system_rng.h | 23 |
7 files changed, 396 insertions, 222 deletions
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index 67325ee1b..201a9f39b 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -1,6 +1,6 @@ /* * HMAC_DRBG -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,53 +10,75 @@ namespace Botan { -HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* mac, - RandomNumberGenerator* prng) : - m_mac(mac), - m_prng(prng), - m_V(m_mac->output_length(), 0x01), - m_reseed_counter(0) +HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash) : + HMAC_DRBG(hmac_hash, BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) + {} + +HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash, + size_t max_bytes_before_reseed) : + Stateful_RNG(max_bytes_before_reseed) { - m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); + const std::string hmac = "HMAC(" + hmac_hash + ")"; + + m_mac = MessageAuthenticationCode::create(hmac); + if(!m_mac) + { + throw Algorithm_Not_Found(hmac); + } + + m_V.resize(m_mac->output_length()); + + clear(); } -HMAC_DRBG::HMAC_DRBG(const std::string& mac_name, - RandomNumberGenerator* prng) : - m_prng(prng), - m_reseed_counter(0) +void HMAC_DRBG::clear() { - m_mac = MessageAuthenticationCode::create(mac_name); - if(!m_mac) - throw Algorithm_Not_Found(mac_name); - m_V = secure_vector<byte>(m_mac->output_length(), 0x01), + for(size_t i = 0; i != m_V.size(); ++i) + m_V[i] = 0x01; m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); } -void HMAC_DRBG::randomize(byte out[], size_t length) +std::string HMAC_DRBG::name() const { - if(!is_seeded() || m_reseed_counter > BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) - reseed(m_mac->output_length() * 8); + return "HMAC_DRBG(" + m_mac->name() + ")"; + } - if(!is_seeded()) - throw PRNG_Unseeded(name()); +void HMAC_DRBG::randomize(byte output[], size_t output_len) + { + randomize_with_input(output, output_len, nullptr, 0); + } - while(length) - { - const size_t to_copy = std::min(length, m_V.size()); - m_V = m_mac->process(m_V); - copy_mem(out, m_V.data(), to_copy); +/* +* HMAC_DRBG generation +* See NIST SP800-90A section 10.1.2.5 +*/ +void HMAC_DRBG::randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) + { + reseed_check(output_len); - length -= to_copy; - out += to_copy; + if(input_len > 0) + { + update(input, input_len); } - m_reseed_counter += length; + while(output_len) + { + const size_t to_copy = std::min(output_len, m_V.size()); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); + copy_mem(output, m_V.data(), to_copy); + + output += to_copy; + output_len -= to_copy; + } - update(nullptr, 0); // additional_data is always empty + update(input, input_len); } /* * Reset V and the mac key with new values +* See NIST SP800-90A section 10.1.2.2 */ void HMAC_DRBG::update(const byte input[], size_t input_len) { @@ -65,66 +87,24 @@ void HMAC_DRBG::update(const byte input[], size_t input_len) m_mac->update(input, input_len); m_mac->set_key(m_mac->final()); - m_V = m_mac->process(m_V); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); - if(input_len) + if(input_len > 0) { m_mac->update(m_V); m_mac->update(0x01); m_mac->update(input, input_len); m_mac->set_key(m_mac->final()); - m_V = m_mac->process(m_V); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); } } -size_t HMAC_DRBG::reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) +void HMAC_DRBG::add_entropy(const byte input[], size_t input_len) { - if(m_prng) - { - size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout); - - if(m_prng->is_seeded()) - { - secure_vector<byte> input = m_prng->random_vec(m_mac->output_length()); - update(input.data(), input.size()); - m_reseed_counter = 1; - } - - return bits; - } - - return 0; - } - -void HMAC_DRBG::add_entropy(const byte input[], size_t length) - { - update(input, length); - m_reseed_counter = 1; - } - -bool HMAC_DRBG::is_seeded() const - { - return m_reseed_counter > 0; - } - -void HMAC_DRBG::clear() - { - m_reseed_counter = 0; - for(size_t i = 0; i != m_V.size(); ++i) - m_V[i] = 0x01; - - m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); - - if(m_prng) - m_prng->clear(); - } - -std::string HMAC_DRBG::name() const - { - return "HMAC_DRBG(" + m_mac->name() + ")"; + update(input, input_len); } } diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index bd2d18d47..d7a1d76aa 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -1,6 +1,6 @@ /* * HMAC_DRBG (SP800-90A) -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -14,40 +14,31 @@ namespace Botan { /** -* HMAC_DRBG (SP800-90A) +* HMAC_DRBG from NIST SP800-90A */ -class BOTAN_DLL HMAC_DRBG : public RandomNumberGenerator +class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG { public: - void randomize(byte buf[], size_t buf_len) override; - bool is_seeded() const override; - void clear() override; - std::string name() const override; + HMAC_DRBG(const std::string& hmac_hash); - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override; + HMAC_DRBG(const std::string& hmac_hash, + size_t max_bytes_before_reseed); - void add_entropy(const byte input[], size_t input_len) override; + std::string name() const override; - /** - * @param mac the underlying mac function (eg HMAC(SHA-512)) - * @param underlying_rng RNG used generating inputs (eg HMAC_RNG) - */ - HMAC_DRBG(MessageAuthenticationCode* mac, - RandomNumberGenerator* underlying_rng = nullptr); + void clear() override; + + void randomize(byte output[], size_t output_len); - HMAC_DRBG(const std::string& mac, - RandomNumberGenerator* underlying_rng = nullptr); + void randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len); + void add_entropy(const byte input[], size_t input_len) override; private: void update(const byte input[], size_t input_len); std::unique_ptr<MessageAuthenticationCode> m_mac; - std::unique_ptr<RandomNumberGenerator> m_prng; - secure_vector<byte> m_V; - size_t m_reseed_counter; }; } diff --git a/src/lib/rng/info.txt b/src/lib/rng/info.txt index ba7aa8e6a..e6063cb1b 100644 --- a/src/lib/rng/info.txt +++ b/src/lib/rng/info.txt @@ -1,5 +1,7 @@ +define AUTO_SEEDING_RNG 20131128 + <requires> entropy -auto_rng -hmac_rng +hmac_drbg +sha2_64 </requires> diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index c17f23dd0..218b9c842 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -1,13 +1,17 @@ /* * Random Number Generator -* (C) 1999-2008 Jack Lloyd +* (C) 1999-2008,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include <botan/rng.h> -#include <botan/hmac_rng.h> +#include <botan/hmac_drbg.h> +#include <botan/auto_rng.h> #include <botan/entropy_src.h> +#include <botan/loadstor.h> +#include <botan/internal/os_utils.h> +#include <chrono> namespace Botan { @@ -25,18 +29,119 @@ size_t RandomNumberGenerator::reseed_with_timeout(size_t bits_to_collect, timeout); } +size_t RandomNumberGenerator::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + typedef std::chrono::system_clock clock; + + auto deadline = clock::now() + poll_timeout; + + double bits_collected = 0; + + Entropy_Accumulator accum([&](const byte in[], size_t in_len, double entropy_estimate) { + add_entropy(in, in_len); + bits_collected += entropy_estimate; + return (bits_collected >= poll_bits || clock::now() > deadline); + }); + + srcs.poll(accum); + + return bits_collected; + } + +Stateful_RNG::Stateful_RNG(size_t bytes_before_reseed) : + m_max_bytes_before_reseed_required(bytes_before_reseed) + { + } + +size_t Stateful_RNG::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + size_t bits_collected = RandomNumberGenerator::reseed_with_sources(srcs, poll_bits, poll_timeout); + + if(bits_collected >= poll_bits) + { + m_successful_initialization = true; + m_bytes_since_reseed = 0; + } + + return bits_collected; + } + +void Stateful_RNG::reseed_check(size_t bytes_requested) + { + const bool fork_detected = (m_last_pid > 0) && (OS::get_process_id() != m_last_pid); + + m_bytes_since_reseed += bytes_requested; + m_last_pid = OS::get_process_id(); + + if(!is_seeded() || fork_detected) + { + this->reseed(BOTAN_RNG_RESEED_POLL_BITS); + } + else if(m_max_bytes_before_reseed_required > 0 && + m_bytes_since_reseed >= m_max_bytes_before_reseed_required) + { + this->reseed_with_timeout(BOTAN_RNG_AUTO_RESEED_POLL_BITS, + BOTAN_RNG_AUTO_RESEED_TIMEOUT); + } + + if(!is_seeded()) + { + throw PRNG_Unseeded(name()); + } + } + +void Stateful_RNG::initialize_with(const byte input[], size_t len) + { + add_entropy(input, len); + m_successful_initialization = true; + } + +bool Stateful_RNG::is_seeded() const + { + return m_successful_initialization; + } + RandomNumberGenerator* RandomNumberGenerator::make_rng() { - std::unique_ptr<MessageAuthenticationCode> h1(MessageAuthenticationCode::create("HMAC(SHA-512)")); - std::unique_ptr<MessageAuthenticationCode> h2(MessageAuthenticationCode::create("HMAC(SHA-512)")); + return new AutoSeeded_RNG; + } + +AutoSeeded_RNG::AutoSeeded_RNG(size_t max_bytes_before_reseed) + { + m_rng.reset(new HMAC_DRBG("SHA-384", max_bytes_before_reseed)); + size_t bits = m_rng->reseed(384); + if(!m_rng->is_seeded()) + { + throw Exception("AutoSeeded_RNG failed to gather enough entropy only got " + + std::to_string(bits) + " bits"); + } + } + +void AutoSeeded_RNG::randomize(byte output[], size_t output_len) + { + /* + This data is not secret so skipping a vector/secure_vector allows + avoiding an allocation. + */ + typedef std::chrono::high_resolution_clock clock; + + byte nonce_buf[16] = { 0 }; + const uint32_t cur_ctr = m_counter++; + const uint32_t cur_pid = OS::get_process_id(); + const uint64_t cur_time = clock::now().time_since_epoch().count(); - if(!h1 || !h2) - throw Algorithm_Not_Found("HMAC_RNG HMACs"); - std::unique_ptr<RandomNumberGenerator> rng(new HMAC_RNG(h1.release(), h2.release())); + store_le(cur_ctr, nonce_buf); + store_le(cur_pid, nonce_buf + 4); + store_le(cur_time, nonce_buf + 8); - rng->reseed(256); + m_rng->randomize_with_input(output, output_len, + nonce_buf, sizeof(nonce_buf)); - return rng.release(); + ++m_counter; } } diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index 3fd3dcec8..2e08ce553 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -1,6 +1,6 @@ /* -* RandomNumberGenerator -* (C) 1999-2009 Jack Lloyd +* Random Number Generator base classes +* (C) 1999-2009,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,16 +19,20 @@ namespace Botan { class Entropy_Sources; /** -* This class represents a random number (RNG) generator object. +* An interface to a generic RNG */ class BOTAN_DLL RandomNumberGenerator { public: - /** - * Create a seeded and active RNG object for general application use - * Added in 1.8.0 + virtual ~RandomNumberGenerator() = default; + + RandomNumberGenerator() = default; + + /* + * Never copy a RNG, create a new one */ - static RandomNumberGenerator* make_rng(); + RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; + RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; /** * Randomize a byte array. @@ -38,41 +42,35 @@ class BOTAN_DLL RandomNumberGenerator virtual void randomize(byte output[], size_t length) = 0; /** - * Return a random vector - * @param bytes number of bytes in the result - * @return randomized vector of length bytes + * Incorporate some entropy into the RNG state. For example + * adding nonces or timestamps from a peer's protocol message + * can help hedge against VM state rollback attacks. + * + * @param inputs a byte array containg the entropy to be added + * @param length the length of the byte array in */ - virtual secure_vector<byte> random_vec(size_t bytes) - { - secure_vector<byte> output(bytes); - randomize(output.data(), output.size()); - return output; - } + virtual void add_entropy(const byte input[], size_t length) = 0; /** - * Only usable with POD types, only useful with integers - * get_random<u64bit>() + * Incorporate entropy into the RNG state then produce output + * Some RNG types implement this using a single operation. */ - template<typename T> T get_random() + virtual void randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) { - T r; - this->randomize(reinterpret_cast<byte*>(&r), sizeof(r)); - return r; + this->add_entropy(input, input_len); + this->randomize(output, output_len); } /** - * Return a random byte - * @return random byte + * Return the name of this object */ - byte next_byte() { return get_random<byte>(); } + virtual std::string name() const = 0; - byte next_nonzero_byte() - { - byte b = next_byte(); - while(b == 0) - b = next_byte(); - return b; - } + /** + * Clear all internally held values of this RNG. + */ + virtual void clear() = 0; /** * Check whether this RNG is seeded. @@ -81,85 +79,144 @@ class BOTAN_DLL RandomNumberGenerator virtual bool is_seeded() const = 0; /** - * Clear all internally held values of this RNG. - */ - virtual void clear() = 0; - - /** - * Return the name of this object + * Poll provided sources for up to poll_bits bits of entropy + * or until the timeout expires. Returns estimate of the number + * of bits collected. */ - virtual std::string name() const = 0; + virtual size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout); /** - * Seed this RNG using the global entropy sources and default timeout + * Reseed this RNG from the default entropy sources and a default timeout * @param bits_to_collect is the number of bits of entropy to - attempt to gather from the entropy sources + * attempt to gather from the entropy sources + * @param poll_timeout try not to run longer than this, even if + * not enough entropy has been collected */ - size_t reseed(size_t bits_to_collect); + size_t reseed(size_t bits_to_collect = BOTAN_RNG_RESEED_POLL_BITS); /** - * Seed this RNG using the global entropy sources + * Reseed this RNG from the default entropy sources * @param bits_to_collect is the number of bits of entropy to - attempt to gather from the entropy sources - * @param poll_timeout try not to run longer than this, no matter what + * attempt to gather from the entropy sources + * @param poll_timeout try not to run longer than this, even if + * not enough entropy has been collected */ size_t reseed_with_timeout(size_t bits_to_collect, std::chrono::milliseconds poll_timeout); /** - * Poll provided sources for up to poll_bits bits of entropy - * or until the timeout expires. Returns estimate of the number - * of bits collected. + * Return a random vector + * @param bytes number of bytes in the result + * @return randomized vector of length bytes */ - virtual size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) = 0; + secure_vector<byte> random_vec(size_t bytes) + { + secure_vector<byte> output(bytes); + randomize(output.data(), output.size()); + return output; + } /** - * Add entropy to this RNG. - * @param in a byte array containg the entropy to be added - * @param length the length of the byte array in + * Return a random byte + * @return random byte */ - virtual void add_entropy(const byte in[], size_t length) = 0; + byte next_byte() + { + byte b; + this->randomize(&b, 1); + return b; + } - /* - * Never copy a RNG, create a new one + byte next_nonzero_byte() + { + byte b = next_byte(); + while(b == 0) + b = next_byte(); + return b; + } + + /** + * Create a seeded and active RNG object for general application use + * Added in 1.8.0 + * Use AutoSeeded_RNG instead */ - RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; - RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; + static RandomNumberGenerator* make_rng(); + }; - RandomNumberGenerator() {} - virtual ~RandomNumberGenerator() {} +/** +* Inherited by RNGs which maintain in-process state, like HMAC_DRBG. +* On Unix these RNGs are vulnerable to problems with fork, where the +* RNG state is duplicated, and the parent and child process RNGs will +* produce identical output until one of them reseeds. Stateful_RNG +* reseeds itself whenever a fork is detected, or after a set number of +* bytes have been output. +* +* Not implemented by RNGs which access an external RNG, such as the +* system PRNG or an hardware RNG. +*/ +class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator + { + public: + Stateful_RNG(size_t max_output_before_reseed); + + virtual bool is_seeded() const override final; + + /** + * Consume this input and mark the RNG as initialized regardless + * of the length of the input or the current seeded state of + * the RNG. + */ + void initialize_with(const byte input[], size_t length); + + /** + * Poll provided sources for up to poll_bits bits of entropy + * or until the timeout expires. Returns estimate of the number + * of bits collected. + */ + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; + + protected: + void reseed_check(size_t bytes_requested); + + private: + const size_t m_max_bytes_before_reseed_required; + size_t m_bytes_since_reseed = 0; + uint32_t m_last_pid = 0; + bool m_successful_initialization = false; }; typedef RandomNumberGenerator RNG; /** * Null/stub RNG - fails if you try to use it for anything +* This is not generally useful except for in certain tests */ -class BOTAN_DLL Null_RNG : public RandomNumberGenerator +class BOTAN_DLL Null_RNG final : public RandomNumberGenerator { public: - void randomize(byte[], size_t) override { throw PRNG_Unseeded("Null_RNG"); } + bool is_seeded() const override { return false; } void clear() override {} - std::string name() const override { return "Null_RNG"; } - - size_t reseed_with_sources(Entropy_Sources&, size_t, - std::chrono::milliseconds) override + void randomize(byte[], size_t) override { - return 0; + throw Exception("Null_RNG called"); } - bool is_seeded() const override { return false; } void add_entropy(const byte[], size_t) override {} + + std::string name() const override { return "Null_RNG"; } }; + /** * Wraps access to a RNG in a mutex */ -class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator +class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator { public: void randomize(byte out[], size_t len) override @@ -168,6 +225,20 @@ class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator m_rng->randomize(out, len); } + void randomize_with_input(byte output[], size_t output_length, + const byte input[], size_t input_length) override + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->randomize_with_input(output, output_length, + input, input_length); + } + + void add_entropy(const byte in[], size_t len) override + { + std::lock_guard<std::mutex> lock(m_mutex); + m_rng->add_entropy(in, len); + } + bool is_seeded() const override { std::lock_guard<std::mutex> lock(m_mutex); @@ -194,12 +265,6 @@ class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator return m_rng->reseed_with_sources(src, bits, msec); } - void add_entropy(const byte in[], size_t len) override - { - std::lock_guard<std::mutex> lock(m_mutex); - m_rng->add_entropy(in, len); - } - Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {} private: diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp index 81e235a8c..b6440d968 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -28,32 +28,23 @@ namespace Botan { namespace { -class System_RNG_Impl : public RandomNumberGenerator +class System_RNG_Impl final : public RandomNumberGenerator { public: System_RNG_Impl(); ~System_RNG_Impl(); - void randomize(byte buf[], size_t len) override; - bool is_seeded() const override { return true; } + void clear() override {} - std::string name() const override { return "system"; } - size_t reseed_with_sources(Entropy_Sources&, - size_t /*poll_bits*/, - std::chrono::milliseconds /*timeout*/) override - { - // We ignore it and assert the PRNG is seeded. - // TODO: could poll and write it to /dev/urandom to help seed it - return 0; - } + void randomize(Botan::byte out[], size_t len) override; - void add_entropy(const byte[], size_t) override - { - } - private: + void add_entropy(const byte in[], size_t length) override; + + std::string name() const override; + private: #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) HCRYPTPROV m_prov; #else @@ -61,6 +52,15 @@ class System_RNG_Impl : public RandomNumberGenerator #endif }; +std::string System_RNG_Impl::name() const + { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + return "cryptoapi"; +#else + return BOTAN_SYSTEM_RNG_DEVICE; +#endif + } + System_RNG_Impl::System_RNG_Impl() { #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) @@ -74,7 +74,7 @@ System_RNG_Impl::System_RNG_Impl() #define O_NOCTTY 0 #endif - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); if(m_fd < 0) throw Exception("System_RNG failed to open RNG device"); #endif @@ -90,6 +90,48 @@ System_RNG_Impl::~System_RNG_Impl() #endif } +void System_RNG_Impl::add_entropy(const byte input[], size_t len) + { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + /* + There is no explicit ConsumeRandom, but all values provided in + the call are incorporated into the state. + + TODO: figure out a way to avoid this copy. Byte at a time updating + seems worse than the allocation. + + for(size_t i = 0; i != len; ++i) + { + byte b = input[i]; + ::CryptGenRandom(m_prov, 1, &b); + } + */ + + if(len > 0) + { + secure_vector<byte> buf(input, input + len); + ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data()); + } +#else + while(len) + { + ssize_t got = ::write(m_fd, input, len); + + if(got < 0) + { + if(errno == EINTR) + continue; + + // maybe just ignore failure here and return? + throw Exception("System_RNG write failed error " + std::to_string(errno)); + } + + input += got; + len -= got; + } +#endif + } + void System_RNG_Impl::randomize(byte buf[], size_t len) { #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) diff --git a/src/lib/rng/system_rng/system_rng.h b/src/lib/rng/system_rng/system_rng.h index 6290b8769..a789631d6 100644 --- a/src/lib/rng/system_rng/system_rng.h +++ b/src/lib/rng/system_rng/system_rng.h @@ -22,29 +22,18 @@ BOTAN_DLL RandomNumberGenerator& system_rng(); /* * Instantiatable reference to the system RNG. */ -class BOTAN_DLL System_RNG : public RandomNumberGenerator +class BOTAN_DLL System_RNG final : public RandomNumberGenerator { public: - System_RNG() : m_rng(system_rng()) {} + std::string name() const override { return system_rng().name(); } - void randomize(Botan::byte out[], size_t len) override { m_rng.randomize(out, len); } + void randomize(Botan::byte out[], size_t len) override { system_rng().randomize(out, len); } - bool is_seeded() const override { return m_rng.is_seeded(); } + void add_entropy(const byte in[], size_t length) override { system_rng().add_entropy(in, length); } - void clear() override { m_rng.clear(); } + bool is_seeded() const override { return true; } - std::string name() const override { return m_rng.name(); } - - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override - { - return m_rng.reseed_with_sources(srcs, poll_bits, poll_timeout); - } - - void add_entropy(const byte in[], size_t len) override { m_rng.add_entropy(in, len); } - private: - Botan::RandomNumberGenerator& m_rng; + void clear() override {} }; } |