aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/rng
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-01-15 19:42:30 -0500
committerJack Lloyd <[email protected]>2016-07-17 10:43:34 -0400
commit8a1aead31c9ae9caa405c6951de8aa51d6a4b751 (patch)
treeac0c166c8b98a4c25b69c91aa4d5c2d0bc5bda42 /src/lib/rng
parentcd1e2d3bff92a2d91343541e2cf83287dce87c6f (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.cpp138
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.h35
-rw-r--r--src/lib/rng/info.txt6
-rw-r--r--src/lib/rng/rng.cpp123
-rw-r--r--src/lib/rng/rng.h217
-rw-r--r--src/lib/rng/system_rng/system_rng.cpp76
-rw-r--r--src/lib/rng/system_rng/system_rng.h23
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 {}
};
}