aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2016-08-19 07:51:47 -0400
committerJack Lloyd <[email protected]>2016-08-24 11:31:54 -0400
commit80c160f08f2a69eb4e41a68380796bf31fd2f924 (patch)
tree83259da316524ed3b96b0913e5b023bc40f26a28 /src/lib
parent91474f60d72937ad3c21d8aa53c14f7a0cceb9ca (diff)
RNG changes (GH #593)
Change reseed interval logic to count calls to `randomize` rather than bytes, to match SP 800-90A Changes RNG reseeding API: there is no implicit reference to the global entropy sources within the RNGs anymore. The entropy sources must be supplied with the API call. Adds support for reseding directly from another RNG (such as a system or hardware RNG). Stateful_RNG keeps optional references to both an RNG and a set of entropy sources. During a reseed, both sources are used if set. These can be provided to HMAC_DRBG constructor. For HMAC_DRBG, SP800-90A requires we output no more than 2**16 bytes per DRBG request. We treat requests longer than that as if the caller had instead made several sequential maximum-length requests. This means it is possible for one or more reseeds to trigger even in the course of generating a single (long) output (generate a 256-bit key and use ChaCha or HKDF if this is a problem). Adds RNG::randomize_with_ts_input which takes timestamps and uses them as the additional_data DRBG field. Stateful_RNG overrides this to also include the process ID and the reseed counter. AutoSeeded_RNG's `randomize` uses this. Officially deprecates RNG::make_rng and the Serialized_RNG construtor which creates an AutoSeeded_RNG. With these removed, it would be possible to perform a build with no AutoSeeded_RNG/HMAC_DRBG at all (eg, for applications which only use the system RNG). Tests courtesy @cordney in GH PRs #598 and #600
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/entropy/entropy_src.h5
-rw-r--r--src/lib/ffi/ffi.cpp2
-rw-r--r--src/lib/prov/pkcs11/p11_randomgenerator.h4
-rw-r--r--src/lib/pubkey/rfc6979/rfc6979.cpp2
-rw-r--r--src/lib/rng/auto_rng.h47
-rw-r--r--src/lib/rng/auto_rng/auto_rng.cpp116
-rw-r--r--src/lib/rng/auto_rng/auto_rng.h67
-rw-r--r--src/lib/rng/auto_rng/info.txt1
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.cpp99
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.h68
-rw-r--r--src/lib/rng/hmac_drbg/info.txt1
-rw-r--r--src/lib/rng/hmac_rng/hmac_rng.cpp86
-rw-r--r--src/lib/rng/hmac_rng/hmac_rng.h70
-rw-r--r--src/lib/rng/hmac_rng/info.txt1
-rw-r--r--src/lib/rng/info.txt2
-rw-r--r--src/lib/rng/rdrand_rng/rdrand_rng.h3
-rw-r--r--src/lib/rng/rng.cpp140
-rw-r--r--src/lib/rng/rng.h137
-rw-r--r--src/lib/rng/stateful_rng/info.txt2
-rw-r--r--src/lib/rng/stateful_rng/stateful_rng.cpp112
-rw-r--r--src/lib/rng/stateful_rng/stateful_rng.h118
-rw-r--r--src/lib/rng/x931_rng/info.txt4
-rw-r--r--src/lib/rng/x931_rng/x931_rng.cpp10
-rw-r--r--src/lib/rng/x931_rng/x931_rng.h9
-rw-r--r--src/lib/utils/os_utils.cpp6
-rw-r--r--src/lib/utils/os_utils.h2
26 files changed, 758 insertions, 356 deletions
diff --git a/src/lib/entropy/entropy_src.h b/src/lib/entropy/entropy_src.h
index 64d988e7c..94c67a18e 100644
--- a/src/lib/entropy/entropy_src.h
+++ b/src/lib/entropy/entropy_src.h
@@ -8,12 +8,17 @@
#ifndef BOTAN_ENTROPY_H__
#define BOTAN_ENTROPY_H__
+#include <botan/secmem.h>
#include <botan/rng.h>
#include <string>
#include <chrono>
+#include <memory>
+#include <vector>
namespace Botan {
+class RandomNumberGenerator;
+
/**
* Abstract interface to a source of entropy
*/
diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp
index 028b286b6..e42f32234 100644
--- a/src/lib/ffi/ffi.cpp
+++ b/src/lib/ffi/ffi.cpp
@@ -287,7 +287,7 @@ int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
int botan_rng_reseed(botan_rng_t rng, size_t bits)
{
- return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed(bits); });
+ return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); });
}
int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags)
diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.h b/src/lib/prov/pkcs11/p11_randomgenerator.h
index 84673278d..a291c89f3 100644
--- a/src/lib/prov/pkcs11/p11_randomgenerator.h
+++ b/src/lib/prov/pkcs11/p11_randomgenerator.h
@@ -22,7 +22,7 @@ namespace PKCS11 {
class Module;
/// A random generator that only fetches random from the PKCS#11 RNG
-class BOTAN_DLL PKCS11_RNG final : public RandomNumberGenerator
+class BOTAN_DLL PKCS11_RNG final : public Hardware_RNG
{
public:
/// Initialize the RNG with the PKCS#11 session that provides access to the cryptoki functions
@@ -43,7 +43,7 @@ class BOTAN_DLL PKCS11_RNG final : public RandomNumberGenerator
}
/// No operation - always returns 0
- size_t reseed_with_sources(Entropy_Sources&, size_t, std::chrono::milliseconds) override
+ size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
{
return 0;
}
diff --git a/src/lib/pubkey/rfc6979/rfc6979.cpp b/src/lib/pubkey/rfc6979/rfc6979.cpp
index 1173eefee..94b313c3a 100644
--- a/src/lib/pubkey/rfc6979/rfc6979.cpp
+++ b/src/lib/pubkey/rfc6979/rfc6979.cpp
@@ -17,10 +17,10 @@ RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(const std::string& hash,
m_order(order),
m_qlen(m_order.bits()),
m_rlen(m_qlen / 8 + (m_qlen % 8 ? 1 : 0)),
- m_hmac_drbg(new HMAC_DRBG(hash, 0)),
m_rng_in(m_rlen * 2),
m_rng_out(m_rlen)
{
+ m_hmac_drbg.reset(new HMAC_DRBG(MessageAuthenticationCode::create("HMAC(" + hash + ")")));
BigInt::encode_1363(m_rng_in.data(), m_rlen, x);
}
diff --git a/src/lib/rng/auto_rng.h b/src/lib/rng/auto_rng.h
deleted file mode 100644
index 3085623ef..000000000
--- a/src/lib/rng/auto_rng.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-* Auto Seeded RNG
-* (C) 2008 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_AUTO_SEEDING_RNG_H__
-#define BOTAN_AUTO_SEEDING_RNG_H__
-
-#include <botan/rng.h>
-
-namespace Botan {
-
-class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator
- {
- public:
- void randomize(byte out[], size_t len) override;
-
- void randomize_with_input(byte output[], size_t output_len,
- const byte input[], size_t input_len) override;
-
- bool is_seeded() const override { return m_rng->is_seeded(); }
-
- void clear() override { m_rng->clear(); m_counter = 0; }
-
- 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); }
-
- AutoSeeded_RNG(size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED);
- private:
- std::unique_ptr<RandomNumberGenerator> m_rng;
- uint32_t m_counter = 0;
- };
-
-}
-
-#endif
diff --git a/src/lib/rng/auto_rng/auto_rng.cpp b/src/lib/rng/auto_rng/auto_rng.cpp
new file mode 100644
index 000000000..a9da085bc
--- /dev/null
+++ b/src/lib/rng/auto_rng/auto_rng.cpp
@@ -0,0 +1,116 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/auto_rng.h>
+#include <botan/entropy_src.h>
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+ #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_RNG)
+ #include <botan/hmac_rng.h>
+#endif
+
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ #include <botan/system_rng.h>
+#endif
+
+namespace Botan {
+
+AutoSeeded_RNG::~AutoSeeded_RNG()
+ {
+ // for unique_ptr
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC),
+ underlying_rng,
+ reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(Entropy_Sources& entropy_sources,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC),
+ entropy_sources,
+ reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval)
+ {
+ m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC),
+ underlying_rng,
+ entropy_sources,
+ reseed_interval));
+ force_reseed();
+ }
+
+AutoSeeded_RNG::AutoSeeded_RNG(size_t reseed_interval) :
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ AutoSeeded_RNG(system_rng(), reseed_interval)
+#else
+ AutoSeeded_RNG(Entropy_Sources::global_sources(), reseed_interval)
+#endif
+ {
+ }
+
+void AutoSeeded_RNG::force_reseed()
+ {
+ m_rng->force_reseed();
+ m_rng->next_byte();
+
+ if(!m_rng->is_seeded())
+ {
+ throw Exception("AutoSeeded_RNG reseeding failed");
+ }
+ }
+
+bool AutoSeeded_RNG::is_seeded() const
+ {
+ return m_rng->is_seeded();
+ }
+
+void AutoSeeded_RNG::clear()
+ {
+ m_rng->clear();
+ }
+
+std::string AutoSeeded_RNG::name() const
+ {
+ return m_rng->name();
+ }
+
+void AutoSeeded_RNG::add_entropy(const byte in[], size_t len)
+ {
+ m_rng->add_entropy(in, len);
+ }
+
+size_t AutoSeeded_RNG::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
+ {
+ return m_rng->reseed(srcs, poll_bits, poll_timeout);
+ }
+
+void AutoSeeded_RNG::randomize(byte output[], size_t output_len)
+ {
+ randomize_with_ts_input(output, output_len);
+ }
+
+void AutoSeeded_RNG::randomize_with_input(byte output[], size_t output_len,
+ const byte ad[], size_t ad_len)
+ {
+ m_rng->randomize_with_input(output, output_len, ad, ad_len);
+ }
+
+}
diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng/auto_rng.h
new file mode 100644
index 000000000..6ef1aa291
--- /dev/null
+++ b/src/lib/rng/auto_rng/auto_rng.h
@@ -0,0 +1,67 @@
+/*
+* Auto Seeded RNG
+* (C) 2008,2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_AUTO_SEEDING_RNG_H__
+#define BOTAN_AUTO_SEEDING_RNG_H__
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+class Stateful_RNG;
+
+/**
+* A userspace PRNG
+*/
+class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator
+ {
+ public:
+ void randomize(byte out[], size_t len) override;
+
+ void randomize_with_input(byte output[], size_t output_len,
+ const byte input[], size_t input_len) override;
+
+ bool is_seeded() const override;
+
+ void force_reseed();
+
+ size_t reseed(Entropy_Sources& srcs,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+ std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override;
+
+ void add_entropy(const byte in[], size_t len) override;
+
+ std::string name() const override;
+
+ void clear() override;
+
+ /**
+ * If no RNG or entropy sources are provided to AutoSeeded_RNG, it uses the system RNG
+ * (if available) or else a default group of entropy sources (all other systems) to
+ * gather seed material.
+ */
+ AutoSeeded_RNG(size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ AutoSeeded_RNG(Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ AutoSeeded_RNG(RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ ~AutoSeeded_RNG();
+
+ private:
+ std::unique_ptr<Stateful_RNG> m_rng;
+ };
+
+}
+
+#endif
diff --git a/src/lib/rng/auto_rng/info.txt b/src/lib/rng/auto_rng/info.txt
new file mode 100644
index 000000000..b77e6aa54
--- /dev/null
+++ b/src/lib/rng/auto_rng/info.txt
@@ -0,0 +1 @@
+define AUTO_SEEDING_RNG 20160821
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
index 7325804e3..6ea66aa2e 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
@@ -10,28 +10,42 @@
namespace Botan {
-HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* hmac,
- size_t max_output_before_reseed) :
- Stateful_RNG(max_output_before_reseed),
- m_mac(hmac)
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, reseed_interval),
+ m_mac(std::move(prf))
{
- m_V.resize(m_mac->output_length());
+ BOTAN_ASSERT_NONNULL(m_mac);
clear();
}
-HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash,
- size_t max_output_before_reseed) :
- Stateful_RNG(max_output_before_reseed)
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, entropy_sources, reseed_interval),
+ m_mac(std::move(prf))
{
- const std::string hmac = "HMAC(" + hmac_hash + ")";
+ BOTAN_ASSERT_NONNULL(m_mac);
+ clear();
+ }
- m_mac = MessageAuthenticationCode::create(hmac);
- if(!m_mac)
- {
- throw Algorithm_Not_Found(hmac);
- }
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(entropy_sources, reseed_interval),
+ m_mac(std::move(prf))
+ {
+ BOTAN_ASSERT_NONNULL(m_mac);
+ clear();
+ }
- m_V.resize(m_mac->output_length());
+HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) :
+ Stateful_RNG(),
+ m_mac(std::move(prf))
+ {
+ BOTAN_ASSERT_NONNULL(m_mac);
clear();
}
@@ -39,6 +53,7 @@ void HMAC_DRBG::clear()
{
Stateful_RNG::clear();
+ m_V.resize(m_mac->output_length());
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));
@@ -61,25 +76,45 @@ void HMAC_DRBG::randomize(byte output[], size_t output_len)
void HMAC_DRBG::randomize_with_input(byte output[], size_t output_len,
const byte input[], size_t input_len)
{
- reseed_check(output_len);
-
- if(input_len > 0)
+ /**
+ * SP 800-90A requires we reject any request for a DRBG output
+ * longer than max_number_of_bits_per_request. This is an
+ * implementation-dependent value, but NIST requires for HMAC_DRBG
+ * that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB).
+ *
+ * To avoid inconveniencing the caller who wants a large output for
+ * whatever reason, instead treat very long output requests as
+ * if multiple maximum-length requests had been made.
+ */
+ const size_t max_number_of_bytes_per_request = 64*1024;
+
+ while(output_len > 0)
{
- update(input, input_len);
- }
+ size_t this_req = std::min(max_number_of_bytes_per_request, output_len);
+ output_len -= this_req;
- 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);
+ reseed_check();
+
+ if(input_len > 0)
+ {
+ update(input, input_len);
+ }
+
+ while(this_req)
+ {
+ const size_t to_copy = std::min(this_req, 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;
+ output += to_copy;
+ this_req -= to_copy;
+ }
+
+ update(input, input_len);
}
- update(input, input_len);
}
/*
@@ -113,4 +148,10 @@ void HMAC_DRBG::add_entropy(const byte input[], size_t input_len)
update(input, input_len);
}
+size_t HMAC_DRBG::security_level() const
+ {
+ // sqrt of hash size
+ return m_mac->output_length() * 8 / 2;
+ }
+
}
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h
index 0e294dbdb..4f96af816 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.h
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.h
@@ -8,11 +8,13 @@
#ifndef BOTAN_HMAC_DRBG_H__
#define BOTAN_HMAC_DRBG_H__
-#include <botan/rng.h>
+#include <botan/stateful_rng.h>
#include <botan/mac.h>
namespace Botan {
+class Entropy_Sources;
+
/**
* HMAC_DRBG from NIST SP800-90A
*/
@@ -20,13 +22,64 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
{
public:
/**
- * Initialize an HMAC_DRBG instance with the given hash function
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * Automatic reseeding is disabled completely, as it as no access to
+ * any source for seed material.
+ *
+ * If a fork is detected, the RNG will be unable to reseed itself
+ * in response. In this case, an exception will be thrown rather
+ * than generating duplicated output.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf);
+
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
*/
- HMAC_DRBG(const std::string& hmac_hash,
- size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED);
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
- HMAC_DRBG(MessageAuthenticationCode* hmac,
- size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED);
+ /**
+ * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
+ *
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
+ */
+ HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ /**
+ * Constructor taking a string for the hash
+ */
+ HMAC_DRBG(const std::string& hmac_hash) : Stateful_RNG()
+ {
+ m_mac = MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")");
+ if(!m_mac)
+ throw Algorithm_Not_Found(hmac_hash);
+ clear();
+ }
std::string name() const override;
@@ -38,6 +91,9 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
const byte input[], size_t input_len) override;
void add_entropy(const byte input[], size_t input_len) override;
+
+ size_t security_level() const override;
+
private:
void update(const byte input[], size_t input_len);
diff --git a/src/lib/rng/hmac_drbg/info.txt b/src/lib/rng/hmac_drbg/info.txt
index f386db199..7f2c12fd0 100644
--- a/src/lib/rng/hmac_drbg/info.txt
+++ b/src/lib/rng/hmac_drbg/info.txt
@@ -2,4 +2,5 @@ define HMAC_DRBG 20140319
<requires>
hmac
+stateful_rng
</requires>
diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp
index c100cf70f..d66c538ab 100644
--- a/src/lib/rng/hmac_rng/hmac_rng.cpp
+++ b/src/lib/rng/hmac_rng/hmac_rng.cpp
@@ -12,43 +12,71 @@
namespace Botan {
-HMAC_RNG::HMAC_RNG(const std::string& hash, size_t max_output_before_reseed) :
- Stateful_RNG(max_output_before_reseed)
+HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, reseed_interval),
+ m_prf(std::move(prf))
{
- m_extractor = MAC::create("HMAC(" + hash + ")");
- if(!m_extractor)
- throw Invalid_Argument("HMAC_RNG hash not found");
+ BOTAN_ASSERT_NONNULL(m_prf);
- m_prf.reset(m_extractor->clone());
+ if(!m_prf->valid_keylength(m_prf->output_length()))
+ {
+ throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name());
+ }
+
+ m_extractor.reset(m_prf->clone());
+ this->clear();
+ }
- if(!m_prf->valid_keylength(m_extractor->output_length()) ||
- !m_extractor->valid_keylength(m_prf->output_length()))
+HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval) :
+ Stateful_RNG(underlying_rng, reseed_interval),
+ m_prf(std::move(prf))
+ {
+ BOTAN_ASSERT_NONNULL(m_prf);
+
+ if(!m_prf->valid_keylength(m_prf->output_length()))
{
- throw Invalid_Argument("HMAC_RNG: Bad algo combination " +
- m_extractor->name() + " and " +
- m_prf->name());
+ throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name());
}
+ m_extractor.reset(m_prf->clone());
this->clear();
}
-/*
-* HMAC_RNG Constructor
-*/
-HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor,
- MessageAuthenticationCode* prf,
- size_t max_output_before_reseed) :
- Stateful_RNG(max_output_before_reseed),
- m_extractor(extractor), m_prf(prf)
+HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ Stateful_RNG(entropy_sources, reseed_interval),
+ m_prf(std::move(prf)),
+ m_extractor(m_prf->clone())
{
- if(!m_prf->valid_keylength(m_extractor->output_length()) ||
- !m_extractor->valid_keylength(m_prf->output_length()))
+ BOTAN_ASSERT_NONNULL(m_prf);
+
+ if(!m_prf->valid_keylength(m_prf->output_length()))
+ {
+ throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name());
+ }
+
+ m_extractor.reset(m_prf->clone());
+ this->clear();
+ }
+
+HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf) :
+ Stateful_RNG(),
+ m_prf(std::move(prf))
+ {
+ BOTAN_ASSERT_NONNULL(m_prf);
+
+ if(!m_prf->valid_keylength(m_prf->output_length()))
{
- throw Invalid_Argument("HMAC_RNG: Bad algo combination " +
- m_extractor->name() + " and " +
- m_prf->name());
+ throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name());
}
+ m_extractor.reset(m_prf->clone());
this->clear();
}
@@ -105,7 +133,7 @@ void HMAC_RNG::new_K_value(byte label)
*/
void HMAC_RNG::randomize(byte out[], size_t length)
{
- reseed_check(length);
+ reseed_check();
while(length)
{
@@ -121,9 +149,9 @@ void HMAC_RNG::randomize(byte out[], size_t length)
new_K_value(BlockFinished);
}
-size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds timeout)
+size_t HMAC_RNG::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds timeout)
{
new_K_value(Reseed);
m_extractor->update(m_K); // m_K is the PRF output
@@ -131,7 +159,7 @@ size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs,
/*
* This ends up calling add_entropy which provides input to the extractor
*/
- size_t bits_collected = Stateful_RNG::reseed_with_sources(srcs, poll_bits, timeout);
+ size_t bits_collected = Stateful_RNG::reseed(srcs, poll_bits, timeout);
/*
Now derive the new PRK using everything that has been fed into
diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h
index a2538a83a..d6e9b4896 100644
--- a/src/lib/rng/hmac_rng/hmac_rng.h
+++ b/src/lib/rng/hmac_rng/hmac_rng.h
@@ -1,6 +1,6 @@
/*
* HMAC RNG
-* (C) 2008,2013 Jack Lloyd
+* (C) 2008,2013,2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -8,9 +8,8 @@
#ifndef BOTAN_HMAC_RNG_H__
#define BOTAN_HMAC_RNG_H__
+#include <botan/stateful_rng.h>
#include <botan/mac.h>
-#include <botan/rng.h>
-#include <vector>
namespace Botan {
@@ -19,40 +18,69 @@ namespace Botan {
* Key Derivation Functions and an HMAC-based KDF" by Hugo Krawczyk
* (henceforce, 'E-t-E')
*
-* However it actually can be parameterized with any two MAC functions,
+* However it actually could be parameterized with any two MAC functions,
* not restricted to HMAC (this variation is also described in
* Krawczyk's paper), for instance one could use HMAC(SHA-512) as the
* extractor and CMAC(AES-256) as the PRF.
*/
-class BOTAN_DLL HMAC_RNG : public Stateful_RNG
+class BOTAN_DLL HMAC_RNG final : public Stateful_RNG
{
public:
+ /**
+ * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC)
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding.
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
+ */
+ HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ /**
+ * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC)
+ * @param underlying_rng is a reference to some RNG which will be used
+ * to perform the periodic reseeding.
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
+ */
+ HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ RandomNumberGenerator& underlying_rng,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ /*
+ * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC)
+ * @param entropy_sources will be polled to perform reseeding periodically
+ * @param reseed_interval specifies a limit of how many times
+ * the RNG will be called before automatic reseeding is performed.
+ */
+ HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+
+ /**
+ * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC)
+ * Automatic reseeding is disabled completely.
+ */
+ HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf);
+
void randomize(byte buf[], size_t len) override;
void clear() override;
std::string name() const override;
- size_t reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds poll_timeout) override;
+ size_t reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout) override;
void add_entropy(const byte[], size_t) override;
- /**
- * @param extractor a MAC used for extracting the entropy
- * @param prf a MAC used as a PRF using HKDF construction
- */
- HMAC_RNG(MessageAuthenticationCode* extractor,
- MessageAuthenticationCode* prf,
- size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED);
+ size_t security_level() const override { return m_prf->output_length() * 8 / 2; }
- /**
- * Use the specified hash for both the extractor and PRF functions
- */
- HMAC_RNG(const std::string& hash,
- size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED);
private:
- std::unique_ptr<MessageAuthenticationCode> m_extractor;
std::unique_ptr<MessageAuthenticationCode> m_prf;
+ std::unique_ptr<MessageAuthenticationCode> m_extractor;
enum HMAC_PRF_Label {
Running,
diff --git a/src/lib/rng/hmac_rng/info.txt b/src/lib/rng/hmac_rng/info.txt
index 36a8a7a34..2b7f49c8a 100644
--- a/src/lib/rng/hmac_rng/info.txt
+++ b/src/lib/rng/hmac_rng/info.txt
@@ -2,4 +2,5 @@ define HMAC_RNG 20131128
<requires>
mac
+stateful_rng
</requires>
diff --git a/src/lib/rng/info.txt b/src/lib/rng/info.txt
index 84ba3ce89..655e35fd1 100644
--- a/src/lib/rng/info.txt
+++ b/src/lib/rng/info.txt
@@ -1,5 +1,3 @@
-define AUTO_SEEDING_RNG 20131128
-
<requires>
entropy
hmac_drbg
diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.h b/src/lib/rng/rdrand_rng/rdrand_rng.h
index d0fb37c16..fcd54035b 100644
--- a/src/lib/rng/rdrand_rng/rdrand_rng.h
+++ b/src/lib/rng/rdrand_rng/rdrand_rng.h
@@ -45,8 +45,7 @@ class BOTAN_DLL RDRAND_RNG : public Hardware_RNG
void add_entropy(const uint8_t[], size_t) override
{ /* no op */ }
- size_t reseed_with_sources(Entropy_Sources&, size_t,
- std::chrono::milliseconds) override
+ size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override
{ return 0; /* no op */ }
std::string name() const override { return "RDRAND"; }
diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp
index 5501c143e..8c2982312 100644
--- a/src/lib/rng/rng.cpp
+++ b/src/lib/rng/rng.cpp
@@ -1,144 +1,62 @@
/*
-* Random Number Generator
-* (C) 1999-2008,2016 Jack Lloyd
+* (C) 2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
#include <botan/rng.h>
-#include <botan/auto_rng.h>
-#include <botan/entropy_src.h>
#include <botan/loadstor.h>
#include <botan/internal/os_utils.h>
-#if defined(BOTAN_HAS_HMAC_DRBG)
- #include <botan/hmac_drbg.h>
-#endif
-
-#if defined(BOTAN_HAS_HMAC_RNG)
- #include <botan/hmac_rng.h>
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
#endif
namespace Botan {
-size_t RandomNumberGenerator::reseed(size_t bits_to_collect)
+void RandomNumberGenerator::randomize_with_ts_input(byte output[], size_t output_len)
{
- return this->reseed_with_timeout(bits_to_collect,
- BOTAN_RNG_RESEED_DEFAULT_TIMEOUT);
- }
-
-size_t RandomNumberGenerator::reseed_with_timeout(size_t bits_to_collect,
- std::chrono::milliseconds timeout)
- {
- return this->reseed_with_sources(Entropy_Sources::global_sources(),
- bits_to_collect,
- timeout);
- }
-
-size_t RandomNumberGenerator::reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds poll_timeout)
- {
- return srcs.poll(*this, poll_bits, poll_timeout);
- }
-
-Stateful_RNG::Stateful_RNG(size_t max_output_before_reseed) : m_max_output_before_reseed(max_output_before_reseed)
- {
- }
-
-void Stateful_RNG::clear()
- {
- m_successful_initialization = false;
- m_bytes_since_reseed = 0;
- m_last_pid = 0;
- }
-
-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;
- }
+ /*
+ Form additional input which is provided to the PRNG implementation
+ to paramaterize the KDF output.
+ */
+ byte additional_input[16] = { 0 };
+ store_le(OS::get_system_timestamp_ns(), additional_input);
+ store_le(OS::get_processor_timestamp(), additional_input + 8);
- return bits_collected;
+ randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
}
-void Stateful_RNG::reseed_check(size_t bytes_requested)
+void RandomNumberGenerator::randomize_with_input(byte output[], size_t output_len,
+ const byte input[], size_t input_len)
{
- 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_output_before_reseed > 0 && m_bytes_since_reseed >= m_max_output_before_reseed)
- {
- this->reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS,
- BOTAN_RNG_AUTO_RESEED_TIMEOUT);
- }
-
- if(!is_seeded())
- {
- throw PRNG_Unseeded(name());
- }
+ this->add_entropy(input, input_len);
+ this->randomize(output, output_len);
}
-void Stateful_RNG::initialize_with(const byte input[], size_t len)
+size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
{
- add_entropy(input, len);
- m_successful_initialization = true;
+ return srcs.poll(*this, poll_bits, poll_timeout);
}
-bool Stateful_RNG::is_seeded() const
+void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
{
- return m_successful_initialization;
+ secure_vector<byte> buf(poll_bits / 8);
+ rng.randomize(buf.data(), buf.size());
+ this->add_entropy(buf.data(), buf.size());
}
RandomNumberGenerator* RandomNumberGenerator::make_rng()
{
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
return new AutoSeeded_RNG;
+#else
+ throw Exception("make_rng failed, no AutoSeeded_RNG in this build");
+#endif
}
-AutoSeeded_RNG::AutoSeeded_RNG(size_t max_output_before_reseed)
- {
- m_rng.reset(new BOTAN_AUTO_RNG_DRBG(BOTAN_AUTO_RNG_HASH, max_output_before_reseed));
-
- size_t bits = m_rng->reseed(BOTAN_AUTO_RNG_ENTROPY_TARGET);
-
- 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)
- {
- /*
- Form additional input which is provided to the PRNG implementation
- to paramaterize the KDF output.
- */
- byte additional_input[24] = { 0 };
- store_le(OS::get_system_timestamp_ns(), additional_input);
- store_le(OS::get_processor_timestamp(), additional_input + 8);
- store_le(OS::get_process_id(), additional_input + 16);
- store_le(m_counter++, additional_input + 20);
-
- randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
- }
-
-void AutoSeeded_RNG::randomize_with_input(byte output[], size_t output_len,
- const byte ad[], size_t ad_len)
- {
- m_rng->randomize_with_input(output, output_len, ad, ad_len);
- }
+Serialized_RNG::Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {}
}
diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h
index 7da560b85..d1cdcfff2 100644
--- a/src/lib/rng/rng.h
+++ b/src/lib/rng/rng.h
@@ -8,6 +8,7 @@
#ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H__
#define BOTAN_RANDOM_NUMBER_GENERATOR_H__
+#include <botan/entropy_src.h>
#include <botan/secmem.h>
#include <botan/exceptn.h>
#include <chrono>
@@ -19,7 +20,7 @@ namespace Botan {
class Entropy_Sources;
/**
-* An interface to a generic RNG
+* An interface to a cryptographic random number generator
*/
class BOTAN_DLL RandomNumberGenerator
{
@@ -45,6 +46,8 @@ class BOTAN_DLL RandomNumberGenerator
* Incorporate some additional data into the RNG state. For
* example adding nonces or timestamps from a peer's protocol
* message can help hedge against VM state rollback attacks.
+ * A few RNG types do not accept any externally provided input,
+ * in which case this function is a no-op.
*
* @param inputs a byte array containg the entropy to be added
* @param length the length of the byte array in
@@ -56,27 +59,41 @@ class BOTAN_DLL RandomNumberGenerator
*/
template<typename T> void add_entropy_T(const T& t)
{
- add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T));
+ this->add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T));
}
/**
- * Incorporate entropy into the RNG state then produce output
- * Some RNG types implement this using a single operation.
+ * Incorporate entropy into the RNG state then produce output.
+ * Some RNG types implement this using a single operation, default
+ * calls add_entropy + randomize in sequence.
+ *
+ * Use this to further bind the outputs to your current
+ * process/protocol state. For instance if generating a new key
+ * for use in a session, include a session ID or other such
+ * value. See NIST SP 800-90 A, B, C series for more ideas.
*/
virtual void randomize_with_input(byte output[], size_t output_len,
- const byte input[], size_t input_len)
- {
- this->add_entropy(input, input_len);
- this->randomize(output, output_len);
- }
+ const byte input[], size_t input_len);
+
+ /**
+ * This calls `randomize_with_input` using some timestamps as extra input.
+ *
+ * For a stateful RNG using non-random but potentially unique data as the
+ * additional_input can help protect against problems with fork, VM state
+ * rollback, or other cases where somehow an RNG state is duplicated. If
+ * both of the duplicated RNG states later incorporate a timestamp (and the
+ * timestamps don't themselves repeat), their outputs will diverge.
+ */
+ virtual void randomize_with_ts_input(byte output[], size_t output_len);
/**
- * Return the name of this object
+ * Return the name of this RNG type
*/
virtual std::string name() const = 0;
/**
- * Clear all internally held values of this RNG.
+ * Clear all internally held values of this RNG
+ * @post is_seeded() == false
*/
virtual void clear() = 0;
@@ -91,28 +108,17 @@ class BOTAN_DLL RandomNumberGenerator
* or until the timeout expires. Returns estimate of the number
* of bits collected.
*/
- virtual size_t reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds poll_timeout);
+ virtual size_t reseed(Entropy_Sources& srcs,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+ std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_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
- * @param poll_timeout try not to run longer than this, even if
- * not enough entropy has been collected
+ * Reseed by reading specified bits from the RNG
*/
- size_t reseed(size_t bits_to_collect = BOTAN_RNG_RESEED_POLL_BITS);
+ virtual void reseed_from_rng(RandomNumberGenerator& rng,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS);
- /**
- * 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, even if
- * not enough entropy has been collected
- */
- size_t reseed_with_timeout(size_t bits_to_collect,
- std::chrono::milliseconds poll_timeout);
+ // Some utility functions built on the interface above:
/**
* Return a random vector
@@ -122,7 +128,7 @@ class BOTAN_DLL RandomNumberGenerator
secure_vector<byte> random_vec(size_t bytes)
{
secure_vector<byte> output(bytes);
- randomize(output.data(), output.size());
+ this->randomize(output.data(), output.size());
return output;
}
@@ -139,9 +145,9 @@ class BOTAN_DLL RandomNumberGenerator
byte next_nonzero_byte()
{
- byte b = next_byte();
+ byte b = this->next_byte();
while(b == 0)
- b = next_byte();
+ b = this->next_byte();
return b;
}
@@ -150,65 +156,11 @@ class BOTAN_DLL RandomNumberGenerator
* Added in 1.8.0
* Use AutoSeeded_RNG instead
*/
+ BOTAN_DEPRECATED("Use AutoSeeded_RNG")
static RandomNumberGenerator* make_rng();
};
/**
-* 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 a 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);
-
- void clear() override;
-
- /**
- * Mark state as requiring a reseed on next use
- */
- void force_reseed() { m_bytes_since_reseed = m_max_output_before_reseed; }
-
- uint32_t last_pid() const { return m_last_pid; }
-
- mutable std::mutex m_mutex;
-
- private:
- const size_t m_max_output_before_reseed;
- size_t m_bytes_since_reseed = 0;
- uint32_t m_last_pid = 0;
- bool m_successful_initialization = false;
- };
-
-/**
* Convenience typedef
*/
typedef RandomNumberGenerator RNG;
@@ -271,12 +223,12 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator
return m_rng->name();
}
- size_t reseed_with_sources(Entropy_Sources& src,
- size_t bits,
- std::chrono::milliseconds msec) override
+ size_t reseed(Entropy_Sources& src,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+ std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override
{
std::lock_guard<std::mutex> lock(m_mutex);
- return m_rng->reseed_with_sources(src, bits, msec);
+ return m_rng->reseed(src, poll_bits, poll_timeout);
}
void add_entropy(const byte in[], size_t len) override
@@ -285,7 +237,8 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator
m_rng->add_entropy(in, len);
}
- Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {}
+ BOTAN_DEPRECATED("Create an AutoSeeded_RNG for other constructor") Serialized_RNG();
+
explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {}
private:
mutable std::mutex m_mutex;
diff --git a/src/lib/rng/stateful_rng/info.txt b/src/lib/rng/stateful_rng/info.txt
new file mode 100644
index 000000000..b4dcedf4a
--- /dev/null
+++ b/src/lib/rng/stateful_rng/info.txt
@@ -0,0 +1,2 @@
+define STATEFUL_RNG 20160819
+
diff --git a/src/lib/rng/stateful_rng/stateful_rng.cpp b/src/lib/rng/stateful_rng/stateful_rng.cpp
new file mode 100644
index 000000000..1349c1208
--- /dev/null
+++ b/src/lib/rng/stateful_rng/stateful_rng.cpp
@@ -0,0 +1,112 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/stateful_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Stateful_RNG::clear()
+ {
+ m_reseed_counter = 0;
+ m_last_pid = 0;
+ }
+
+void Stateful_RNG::force_reseed()
+ {
+ m_reseed_counter = 0;
+ }
+
+bool Stateful_RNG::is_seeded() const
+ {
+ return m_reseed_counter > 0;
+ }
+
+void Stateful_RNG::initialize_with(const byte input[], size_t len)
+ {
+ add_entropy(input, len);
+
+ if(8*len >= security_level())
+ {
+ m_reseed_counter = 1;
+ }
+ }
+
+void Stateful_RNG::randomize_with_ts_input(byte output[], size_t output_len)
+ {
+ byte additional_input[24] = { 0 };
+ store_le(OS::get_system_timestamp_ns(), additional_input);
+ store_le(OS::get_processor_timestamp(), additional_input + 8);
+ store_le(m_last_pid, additional_input + 16);
+ store_le(static_cast<uint32_t>(m_reseed_counter), additional_input + 20);
+
+ randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+ }
+
+size_t Stateful_RNG::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
+ {
+ size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout);
+
+ if(bits_collected >= security_level())
+ {
+ m_reseed_counter = 1;
+ }
+
+ return bits_collected;
+ }
+
+void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits)
+ {
+ RandomNumberGenerator::reseed_from_rng(rng, poll_bits);
+
+ if(poll_bits >= security_level())
+ {
+ m_reseed_counter = 1;
+ }
+ }
+
+void Stateful_RNG::reseed_check()
+ {
+ const uint32_t cur_pid = OS::get_process_id();
+
+ const bool fork_detected = (m_last_pid > 0) && (cur_pid != m_last_pid);
+
+ if(is_seeded() == false ||
+ fork_detected ||
+ (m_reseed_interval > 0 && m_reseed_counter >= m_reseed_interval))
+ {
+ m_reseed_counter = 0;
+ m_last_pid = cur_pid;
+
+ if(m_underlying_rng)
+ {
+ reseed_from_rng(*m_underlying_rng, security_level());
+ }
+
+ if(m_entropy_sources)
+ {
+ reseed(*m_entropy_sources, security_level());
+ }
+
+ if(!is_seeded())
+ {
+ if(fork_detected)
+ throw Exception("Detected use of fork but cannot reseed DRBG");
+ else
+ throw PRNG_Unseeded(name());
+ }
+ }
+ else
+ {
+ BOTAN_ASSERT(m_reseed_counter != 0, "RNG is seeded");
+ m_reseed_counter += 1;
+ }
+ }
+
+}
diff --git a/src/lib/rng/stateful_rng/stateful_rng.h b/src/lib/rng/stateful_rng/stateful_rng.h
new file mode 100644
index 000000000..11f0c7e3d
--- /dev/null
+++ b/src/lib/rng/stateful_rng/stateful_rng.h
@@ -0,0 +1,118 @@
+/*
+* (C) 2016 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_STATEFUL_RNG_H__
+#define BOTAN_STATEFUL_RNG_H__
+
+#include <botan/rng.h>
+
+namespace Botan {
+
+/**
+* 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 a hardware RNG.
+*/
+class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator
+ {
+ public:
+ Stateful_RNG(RandomNumberGenerator& rng,
+ Entropy_Sources& entropy_sources,
+ size_t reseed_interval) :
+ m_underlying_rng(&rng),
+ m_entropy_sources(&entropy_sources),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ Stateful_RNG(RandomNumberGenerator& rng, size_t reseed_interval) :
+ m_underlying_rng(&rng),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ Stateful_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval) :
+ m_entropy_sources(&entropy_sources),
+ m_reseed_interval(reseed_interval)
+ {}
+
+ /**
+ * In this case, automatic reseeding is impossible
+ */
+ Stateful_RNG() : m_reseed_interval(0) {}
+
+ /**
+ * 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);
+
+ bool is_seeded() const override final;
+
+ /**
+ * Mark state as requiring a reseed on next use
+ */
+ void force_reseed();
+
+ void reseed_from_rng(RandomNumberGenerator& rng,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS) override final;
+
+ /**
+ * Overrides default implementation and also includes the current
+ * process ID and the reseed counter.
+ */
+ void randomize_with_ts_input(byte output[], size_t output_len) override final;
+
+ /**
+ * 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(Entropy_Sources& srcs,
+ size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS,
+ std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override;
+
+ /**
+ * Return intended security level of this DRBG
+ */
+ virtual size_t security_level() const = 0;
+
+ void clear() override;
+
+ protected:
+ /**
+ * Called with lock held
+ */
+ void reseed_check();
+
+ uint32_t last_pid() const { return m_last_pid; }
+
+ private:
+ // A non-owned and possibly null pointer to shared RNG
+ RandomNumberGenerator* m_underlying_rng = nullptr;
+
+ // A non-owned and possibly null pointer to a shared Entropy_Source
+ Entropy_Sources* m_entropy_sources = nullptr;
+
+ const size_t m_reseed_interval;
+
+ /*
+ * Set to 1 after a sucessful seeding, then incremented. Reset
+ * to 0 by clear() or a fork. This logic is used even if
+ * automatic reseeding is disabled (via m_reseed_interval = 0)
+ */
+ size_t m_reseed_counter = 0;
+ uint32_t m_last_pid = 0;
+ };
+
+}
+
+#endif
diff --git a/src/lib/rng/x931_rng/info.txt b/src/lib/rng/x931_rng/info.txt
index b61dc7ec9..4a4418083 100644
--- a/src/lib/rng/x931_rng/info.txt
+++ b/src/lib/rng/x931_rng/info.txt
@@ -1 +1,5 @@
define X931_RNG 20131128
+
+<requires>
+stateful_rng
+</requires>
diff --git a/src/lib/rng/x931_rng/x931_rng.cpp b/src/lib/rng/x931_rng/x931_rng.cpp
index 020d9a5a5..ed44dc743 100644
--- a/src/lib/rng/x931_rng/x931_rng.cpp
+++ b/src/lib/rng/x931_rng/x931_rng.cpp
@@ -14,7 +14,7 @@ void ANSI_X931_RNG::randomize(byte out[], size_t length)
{
if(!is_seeded())
{
- reseed(BOTAN_RNG_RESEED_POLL_BITS);
+ rekey();
if(!is_seeded())
throw PRNG_Unseeded(name());
@@ -72,11 +72,11 @@ void ANSI_X931_RNG::rekey()
}
}
-size_t ANSI_X931_RNG::reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds poll_timeout)
+size_t ANSI_X931_RNG::reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout)
{
- size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout);
+ size_t bits = m_prng->reseed(srcs, poll_bits, poll_timeout);
rekey();
return bits;
}
diff --git a/src/lib/rng/x931_rng/x931_rng.h b/src/lib/rng/x931_rng/x931_rng.h
index ed7124a08..861fcffde 100644
--- a/src/lib/rng/x931_rng/x931_rng.h
+++ b/src/lib/rng/x931_rng/x931_rng.h
@@ -16,7 +16,7 @@ namespace Botan {
/**
* ANSI X9.31 RNG
*/
-class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator
+class BOTAN_DLL ANSI_X931_RNG final : public RandomNumberGenerator
{
public:
void randomize(byte[], size_t) override;
@@ -24,9 +24,9 @@ class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator
void clear() override;
std::string name() const override;
- size_t reseed_with_sources(Entropy_Sources& srcs,
- size_t poll_bits,
- std::chrono::milliseconds poll_timeout) override;
+ size_t reseed(Entropy_Sources& srcs,
+ size_t poll_bits,
+ std::chrono::milliseconds poll_timeout) override;
void add_entropy(const byte[], size_t) override;
@@ -35,6 +35,7 @@ class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator
* @param rng the underlying PRNG for generating inputs
* (eg, an HMAC_RNG)
*/
+ BOTAN_DEPRECATED("X9.31 RNG is deprecated and will be removed soon")
ANSI_X931_RNG(BlockCipher* cipher,
RandomNumberGenerator* rng);
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp
index 86776bdd0..c00c898a3 100644
--- a/src/lib/utils/os_utils.cpp
+++ b/src/lib/utils/os_utils.cpp
@@ -19,7 +19,7 @@
#include <unistd.h>
#endif
-#if defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS)
+#if defined(BOTAN_TARGET_OS_IS_WINDOWS)
#include <windows.h>
#endif
@@ -29,12 +29,12 @@ namespace OS {
uint32_t get_process_id()
{
-#if defined(BOTAN_TARGET_OS_IS_UNIX)
+#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
return ::getpid();
#elif defined(BOTAN_TARGET_OS_IS_WINDOWS)
return ::GetCurrentProcessId();
#else
- return 0;
+ throw Exception("get_process_id not supported");
#endif
}
diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h
index 3335463f7..590ed4ae7 100644
--- a/src/lib/utils/os_utils.h
+++ b/src/lib/utils/os_utils.h
@@ -15,7 +15,7 @@ namespace Botan {
namespace OS {
/**
-* Returns the OS assigned process ID, if available. Otherwise returns 0.
+* Returns the OS assigned process ID, if available. Otherwise throws.
*/
uint32_t get_process_id();