aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build-data/buildh.in10
-rw-r--r--src/build-data/policy/bsi.txt1
-rw-r--r--src/build-data/policy/modern.txt2
-rw-r--r--src/cli/cli.h7
-rw-r--r--src/cli/speed.cpp16
-rw-r--r--src/cli/utils.cpp10
-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
-rw-r--r--src/tests/main.cpp24
-rw-r--r--src/tests/test_mceliece.cpp2
-rw-r--r--src/tests/test_pkcs11_high_level.cpp12
-rw-r--r--src/tests/test_rng.cpp337
-rw-r--r--src/tests/test_rng.h34
-rw-r--r--src/tests/tests.cpp2
38 files changed, 1147 insertions, 424 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index 01527b522..e943973a0 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -98,11 +98,10 @@
#define BOTAN_PRIVATE_KEY_STRONG_CHECKS_ON_GENERATE 1
/*
-* RNGs will automatically poll the system for additional seed material
-* after producing this many bytes of output. Set to zero to disable
-* automatic reseeding.
+* Userspace RNGs like HMAC_DRBG will reseed after a specified number
+* of outputs are generated. Set to zero to disable automatic reseeding.
*/
-#define BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED 16384
+#define BOTAN_RNG_DEFAULT_RESEED_INTERVAL 1024
#define BOTAN_RNG_RESEED_POLL_BITS 256
#define BOTAN_RNG_AUTO_RESEED_TIMEOUT std::chrono::milliseconds(10)
#define BOTAN_RNG_RESEED_DEFAULT_TIMEOUT std::chrono::milliseconds(50)
@@ -111,8 +110,7 @@
* Controls how AutoSeeded_RNG is instantiated
*/
#define BOTAN_AUTO_RNG_DRBG HMAC_DRBG
-#define BOTAN_AUTO_RNG_HASH "SHA-256"
-#define BOTAN_AUTO_RNG_ENTROPY_TARGET 256
+#define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-384)"
/*
* Specifies (in order) the list of entropy sources that will be used
diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt
index 7eb092292..7f2b09610 100644
--- a/src/build-data/policy/bsi.txt
+++ b/src/build-data/policy/bsi.txt
@@ -41,6 +41,7 @@ eckcdsa
ecdh
# rng
+auto_rng
hmac_rng
hmac_drbg
</required>
diff --git a/src/build-data/policy/modern.txt b/src/build-data/policy/modern.txt
index 30b7fbfd8..a98ec5077 100644
--- a/src/build-data/policy/modern.txt
+++ b/src/build-data/policy/modern.txt
@@ -38,7 +38,7 @@ eme_oaep
emsa_pssr
emsa1
-hmac_rng
+auto_rng
hmac_drbg
ffi
diff --git a/src/cli/cli.h b/src/cli/cli.h
index 11cc8add7..7e2d49f0f 100644
--- a/src/cli/cli.h
+++ b/src/cli/cli.h
@@ -10,7 +10,10 @@
#include <botan/build.h>
#include <botan/parsing.h>
#include <botan/rng.h>
-#include <botan/auto_rng.h>
+
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
#if defined(BOTAN_HAS_SYSTEM_RNG)
#include <botan/system_rng.h>
@@ -471,7 +474,9 @@ class Command
if(rng_type == "auto")
{
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
m_rng.reset(new Botan::AutoSeeded_RNG);
+#endif
}
if(!m_rng)
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
index 1299b0d19..222a98d3f 100644
--- a/src/cli/speed.cpp
+++ b/src/cli/speed.cpp
@@ -19,9 +19,12 @@
#include <botan/hash.h>
#include <botan/mac.h>
#include <botan/cipher_mode.h>
-#include <botan/auto_rng.h>
#include <botan/entropy_src.h>
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
#if defined(BOTAN_HAS_SYSTEM_RNG)
#include <botan/system_rng.h>
#endif
@@ -413,8 +416,10 @@ class Speed final : public Command
#endif
else if(algo == "RNG")
{
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
Botan::AutoSeeded_RNG auto_rng;
bench_rng(auto_rng, "AutoSeeded_RNG (periodic reseed)", msec, buf_size);
+#endif
#if defined(BOTAN_HAS_SYSTEM_RNG)
bench_rng(Botan::system_rng(), "System_RNG", msec, buf_size);
@@ -428,7 +433,7 @@ class Speed final : public Command
#if defined(BOTAN_HAS_HMAC_DRBG)
for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
{
- Botan::HMAC_DRBG hmac_drbg(hash, 0);
+ Botan::HMAC_DRBG hmac_drbg(hash);
bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_size);
}
#endif
@@ -436,7 +441,7 @@ class Speed final : public Command
#if defined(BOTAN_HAS_HMAC_RNG)
for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
{
- Botan::HMAC_RNG hmac_rng(hash, 0);
+ Botan::HMAC_RNG hmac_rng(Botan::MessageAuthenticationCode::create("HMAC(" + hash + ")"));
bench_rng(hmac_rng, hmac_rng.name(), msec, buf_size);
}
#endif
@@ -595,8 +600,9 @@ class Speed final : public Command
{
Botan::secure_vector<uint8_t> buffer(buf_size);
- rng.add_entropy(buffer.data(), buffer.size());
- rng.reseed(256);
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+ rng.reseed_from_rng(Botan::system_rng(), 256);
+#endif
Timer timer(rng_name, "", "generate", buffer.size());
timer.run_until_elapsed(runtime, [&] { rng.randomize(buffer.data(), buffer.size()); });
diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp
index b0d364581..610a14dc1 100644
--- a/src/cli/utils.cpp
+++ b/src/cli/utils.cpp
@@ -7,7 +7,6 @@
#include "cli.h"
#include <botan/version.h>
-#include <botan/auto_rng.h>
#include <botan/hash.h>
#include <botan/cpuid.h>
#include <botan/hex.h>
@@ -16,6 +15,10 @@
#include <botan/base64.h>
#endif
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
#if defined(BOTAN_HAS_SYSTEM_RNG)
#include <botan/system_rng.h>
#endif
@@ -179,7 +182,12 @@ class RNG final : public Command
}
else
{
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
rng.reset(new Botan::AutoSeeded_RNG);
+#else
+ error_output() << "auto_rng disabled in build\n";
+ return;
+#endif
}
for(const std::string& req : get_arg_list("bytes"))
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();
diff --git a/src/tests/main.cpp b/src/tests/main.cpp
index 79be98a0e..07aaac519 100644
--- a/src/tests/main.cpp
+++ b/src/tests/main.cpp
@@ -15,8 +15,8 @@
#include <future>
#include <botan/version.h>
-#include <botan/auto_rng.h>
#include <botan/loadstor.h>
+#include <botan/hash.h>
#if defined(BOTAN_HAS_HMAC_DRBG)
#include <botan/hmac_drbg.h>
@@ -26,6 +26,10 @@
#include <botan/system_rng.h>
#endif
+#if defined(BOTAN_HAS_AUTO_SEEDING_RNG)
+ #include <botan/auto_rng.h>
+#endif
+
namespace {
class Test_Runner : public Botan_CLI::Command
@@ -140,7 +144,14 @@ class Test_Runner : public Botan_CLI::Command
}
output() << " rng:HMAC_DRBG with seed '" << Botan::hex_encode(seed) << "'";
- std::unique_ptr<Botan::HMAC_DRBG> drbg(new Botan::HMAC_DRBG("SHA-384", 0));
+
+ // Expand out the seed to 512 bits to make the DRBG happy
+ std::unique_ptr<Botan::HashFunction> sha512(Botan::HashFunction::create("SHA-512"));
+ sha512->update(seed);
+ seed.resize(sha512->output_length());
+ sha512->final(seed.data());
+
+ std::unique_ptr<Botan::HMAC_DRBG> drbg(new Botan::HMAC_DRBG("SHA-384"));
drbg->initialize_with(seed.data(), seed.size());
rng.reset(new Botan::Serialized_RNG(drbg.release()));
@@ -152,16 +163,19 @@ class Test_Runner : public Botan_CLI::Command
#if defined(BOTAN_HAS_SYSTEM_RNG)
output() << " rng:system";
rng.reset(new Botan::System_RNG);
-#else
- // AutoSeeded_RNG always available
+#elif defined(BOTAN_HAS_AUTO_SEEDING_RNG)
output() << " rng:autoseeded";
rng.reset(new Botan::Serialized_RNG(new Botan::AutoSeeded_RNG));
#endif
#endif
-
output() << "\n";
+ if(rng.get() == nullptr)
+ {
+ throw Botan_Tests::Test_Error("No usable RNG enabled in build, aborting tests");
+ }
+
Botan_Tests::Test::setup_tests(soak_level, log_success, data_dir, pkcs11_lib, rng.get());
const size_t failed = run_tests(req, output(), threads);
diff --git a/src/tests/test_mceliece.cpp b/src/tests/test_mceliece.cpp
index 8658bf5e6..5e3501b3e 100644
--- a/src/tests/test_mceliece.cpp
+++ b/src/tests/test_mceliece.cpp
@@ -67,7 +67,7 @@ class McEliece_Keygen_Encrypt_Test : public Text_Based_Test
const size_t keygen_n = get_req_sz(vars, "KeyN");
const size_t keygen_t = get_req_sz(vars, "KeyT");
- Botan::HMAC_DRBG rng("SHA-384", 0);
+ Botan::HMAC_DRBG rng("SHA-384");
rng.initialize_with(keygen_seed.data(), keygen_seed.size());
Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t);
diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp
index 8e894ef28..e8c19224e 100644
--- a/src/tests/test_pkcs11_high_level.cpp
+++ b/src/tests/test_pkcs11_high_level.cpp
@@ -1346,18 +1346,12 @@ Test::Result test_pkcs11_hmac_drbg()
Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG");
TestSession test_session(true);
- // FIXME: this is only a temporary fix
- // It should be possible to instantiate HMAC_DRBG with PKCS11_RNG and
- // HMAC_DRBG should reseed itself from PKCS11_RNG
- HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)").release());
+ PKCS11_RNG p11_rng(test_session.session());
+ HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)"), p11_rng);
// result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG");
result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false);
-
- PKCS11_RNG p11_rng(test_session.session());
- secure_vector<byte> rnd = p11_rng.random_vec(64);
-
- drbg.initialize_with(rnd.data(), rnd.size());
+ secure_vector<byte> rnd = drbg.random_vec(64);
result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true);
std::string personalization_string = "Botan PKCS#11 Tests";
diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp
index ab5a38135..3368ab52b 100644
--- a/src/tests/test_rng.cpp
+++ b/src/tests/test_rng.cpp
@@ -1,5 +1,6 @@
/*
* (C) 2014,2015 Jack Lloyd
+* (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -15,6 +16,17 @@
#include <botan/x931_rng.h>
#endif
+#if defined(BOTAN_HAS_ENTROPY_SOURCE)
+ #include <botan/entropy_src.h>
+#endif
+
+#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
+ #include <unistd.h>
+ #include <sys/wait.h>
+#endif
+
+#include <iostream>
+
namespace Botan_Tests {
namespace {
@@ -81,7 +93,7 @@ class HMAC_DRBG_Tests : public Text_Based_Test
return result;
}
- std::unique_ptr<Botan::HMAC_DRBG> rng(new Botan::HMAC_DRBG(mac.release(), 0));
+ std::unique_ptr<Botan::HMAC_DRBG> rng(new Botan::HMAC_DRBG(std::move(mac)));
rng->initialize_with(seed_input.data(), seed_input.size());
// now reseed
@@ -100,23 +112,69 @@ class HMAC_DRBG_Tests : public Text_Based_Test
BOTAN_REGISTER_TEST("hmac_drbg", HMAC_DRBG_Tests);
-class HMAC_DRBG_Reseed_Tests : public Test
+class HMAC_DRBG_Unit_Tests : public Test
{
- public:
- HMAC_DRBG_Reseed_Tests() : Test() {}
+ private:
+ class Broken_Entropy_Source : public Botan::Entropy_Source
+ {
+ public:
+ std::string name() const override { return "Broken Entropy Source"; }
- std::vector<Test::Result> run() override
+ size_t poll(Botan::RandomNumberGenerator&) override
+ {
+ throw Botan::Exception("polling not available");
+ }
+ };
+
+ class Insufficient_Entropy_Source : public Botan::Entropy_Source
{
- Test::Result result("HMAC_DRBG Reseed");
+ public:
+ std::string name() const override { return "Insufficient Entropy Source"; }
+
+ size_t poll(Botan::RandomNumberGenerator&) override
+ {
+ return 0;
+ }
+ };
+
+ class Request_Counting_RNG : public Botan::RandomNumberGenerator
+ {
+ public:
+ Request_Counting_RNG() : m_randomize_count(0) {};
+
+ bool is_seeded() const override { return true; }
+
+ void clear() override {}
+
+ void randomize(byte[], size_t) override
+ {
+ m_randomize_count++;
+ }
+
+ void add_entropy(const byte[], size_t) override {}
+
+ std::string name() const override { return "Request_Counting_RNG"; }
+
+ size_t randomize_count() { return m_randomize_count; }
+
+ private:
+ size_t m_randomize_count;
+ };
+
+ public:
+ Test::Result test_reseed_kat()
+ {
+ Test::Result result("HMAC_DRBG Reseed KAT");
auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
if(!mac)
{
result.note_missing("HMAC(SHA-256)");
- return {result};
+ return result;
}
- Botan::HMAC_DRBG rng(mac.release(), 17);
+ Request_Counting_RNG counting_rng;
+ Botan::HMAC_DRBG rng(std::move(mac), counting_rng, Botan::Entropy_Sources::global_sources(), 2);
Botan::secure_vector<Botan::byte> seed_input(
{0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF});
Botan::secure_vector<Botan::byte> output_after_initialization(
@@ -130,16 +188,273 @@ class HMAC_DRBG_Reseed_Tests : public Test
Botan::secure_vector<Botan::byte> out(16);
rng.randomize(out.data(), out.size());
+ result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(0));
result.test_eq("out before reseed", out, output_after_initialization);
- // reseed must happend here
+ // reseed must happen here
rng.randomize(out.data(), out.size());
+ result.test_eq("underlying RNG calls", counting_rng.randomize_count(), size_t(1));
result.test_ne("out after reseed", out, output_without_reseed);
- return {result};
+
+ return result;
+ }
+
+ Test::Result test_reseed()
+ {
+ Test::Result result("HMAC_DRBG Reseed");
+
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ if(!mac)
+ {
+ result.note_missing("HMAC(SHA-256)");
+ return result;
+ }
+
+ // test reseed_interval is enforced
+ Request_Counting_RNG counting_rng;
+ Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 2);
+
+ rng.random_vec(7);
+ result.test_eq("initial seeding", counting_rng.randomize_count(), 1);
+ rng.random_vec(9);
+ result.test_eq("still initial seed", counting_rng.randomize_count(), 1);
+
+ rng.random_vec(1);
+ result.test_eq("first reseed", counting_rng.randomize_count(), 2);
+ rng.random_vec(15);
+ result.test_eq("still first reseed", counting_rng.randomize_count(), 2);
+
+ rng.random_vec(15);
+ result.test_eq("second reseed", counting_rng.randomize_count(), 3);
+ rng.random_vec(1);
+ result.test_eq("still second reseed", counting_rng.randomize_count(), 3);
+
+ // request > max_number_of_bits_per_request, do reseeds occur?
+ rng.random_vec(64*1024 + 1);
+ result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 4);
+
+ rng.random_vec(9*64*1024 + 1);
+ result.test_eq("request exceeds output limit", counting_rng.randomize_count(), 9);
+
+ return result;
+ }
+
+ Test::Result test_broken_entropy_input()
+ {
+ Test::Result result("HMAC_DRBG Broken Entropy Input");
+
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ if(!mac)
+ {
+ result.note_missing("HMAC(SHA-256)");
+ return result;
+ }
+
+ // make sure no output is generated when the entropy input source is broken
+
+ const size_t reseed_interval = 1024;
+
+ // underlying_rng throws exception
+ Botan::Null_RNG broken_entropy_input_rng;
+ Botan::HMAC_DRBG rng_with_broken_rng(std::move(mac), broken_entropy_input_rng, reseed_interval);
+
+ result.test_throws("broken underlying rng", [&rng_with_broken_rng] () { rng_with_broken_rng.random_vec(16); });
+
+ // entropy_sources throw exception
+ std::unique_ptr<Broken_Entropy_Source> broken_entropy_source_1(new Broken_Entropy_Source());
+ std::unique_ptr<Broken_Entropy_Source> broken_entropy_source_2(new Broken_Entropy_Source());
+
+ Botan::Entropy_Sources broken_entropy_sources;
+ broken_entropy_sources.add_source(std::move(broken_entropy_source_1));
+ broken_entropy_sources.add_source(std::move(broken_entropy_source_2));
+
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ Botan::HMAC_DRBG rng_with_broken_es(std::move(mac), broken_entropy_sources, reseed_interval);
+ result.test_throws("broken entropy sources", [&rng_with_broken_es] () { rng_with_broken_es.random_vec(16); });
+
+ // entropy source returns insufficient entropy
+ Botan::Entropy_Sources insufficient_entropy_sources;
+ std::unique_ptr<Insufficient_Entropy_Source> insufficient_entropy_source(new Insufficient_Entropy_Source());
+ insufficient_entropy_sources.add_source(std::move(insufficient_entropy_source));
+
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ Botan::HMAC_DRBG rng_with_insufficient_es(std::move(mac), insufficient_entropy_sources, reseed_interval);
+ result.test_throws("insufficient entropy source", [&rng_with_insufficient_es] () { rng_with_insufficient_es.random_vec(16); });
+
+ // one of or both underlying_rng and entropy_sources throw exception
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ Botan::HMAC_DRBG rng_with_broken_rng_and_es(std::move(mac), broken_entropy_input_rng,
+ Botan::Entropy_Sources::global_sources(), reseed_interval);
+ result.test_throws("broken underlying rng but good entropy sources", [&rng_with_broken_rng_and_es] ()
+ { rng_with_broken_rng_and_es.random_vec(16); });
+
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ Botan::HMAC_DRBG rng_with_rng_and_broken_es(std::move(mac), Test::rng(), broken_entropy_sources, reseed_interval);
+ result.test_throws("good underlying rng but broken entropy sources", [&rng_with_rng_and_broken_es] ()
+ { rng_with_rng_and_broken_es.random_vec(16); });
+
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ Botan::HMAC_DRBG rng_with_broken_rng_and_broken_es(std::move(mac), broken_entropy_input_rng, broken_entropy_sources, reseed_interval);
+ result.test_throws("underlying rng and entropy sources broken", [&rng_with_broken_rng_and_broken_es] ()
+ { rng_with_broken_rng_and_broken_es.random_vec(16); });
+
+ return result;
+ }
+
+ Test::Result test_check_nonce()
+ {
+ Test::Result result("HMAC_DRBG Nonce Check");
+
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ if(!mac)
+ {
+ result.note_missing("HMAC(SHA-256)");
+ return result;
+ }
+
+ // make sure the nonce has at least 1/2*security_strength bits
+
+ // SHA-256 -> 128 bits security strength
+ for( auto nonce_size : { 0, 4, 15, 16, 17, 32 } )
+ {
+ if(!mac)
+ {
+ mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ }
+
+ Botan::HMAC_DRBG rng(std::move(mac));
+ result.test_eq("not seeded", rng.is_seeded(), false);
+ std::vector<Botan::byte> nonce(nonce_size);
+ rng.initialize_with(nonce.data(), nonce.size());
+
+ if(nonce_size < 16)
+ {
+ result.test_eq("not seeded", rng.is_seeded(), false);
+ result.test_throws("invalid nonce size", [&rng, &nonce] () { rng.random_vec(16); });
+ }
+ else
+ {
+ result.test_eq("is seeded", rng.is_seeded(), true);
+ rng.random_vec(16);
+ }
+ }
+
+ return result;
+ }
+
+ Test::Result test_prediction_resistance()
+ {
+ Test::Result result("HMAC_DRBG Prediction Resistance");
+
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ if(!mac)
+ {
+ result.note_missing("HMAC(SHA-256)");
+ return result;
+ }
+
+ // set max_output_before_reseed = 1, forcing a reseed on every request
+ Request_Counting_RNG counting_rng;
+ Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 1);
+
+ rng.random_vec(16);
+ result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
+
+ rng.random_vec(16);
+ result.test_eq("second request", counting_rng.randomize_count(), size_t(2));
+
+ rng.random_vec(16);
+ result.test_eq("third request", counting_rng.randomize_count(), size_t(3));
+
+ return result;
+ }
+
+ Test::Result test_fork_safety()
+ {
+ Test::Result result("HMAC_DRBG Fork Safety");
+
+#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)");
+ if(!mac)
+ {
+ result.note_missing("HMAC(SHA-256)");
+ return result;
+ }
+
+ const size_t reseed_interval = 1024;
+
+ // make sure rng is reseeded after every fork
+ Request_Counting_RNG counting_rng;
+ Botan::HMAC_DRBG rng(std::move(mac), counting_rng, reseed_interval);
+
+ rng.random_vec(16);
+ result.test_eq("first request", counting_rng.randomize_count(), size_t(1));
+
+ // fork and request from parent and child, both should output different sequences
+ size_t count = counting_rng.randomize_count();
+ Botan::secure_vector<byte> parent_bytes(16), child_bytes(16);
+ int fd[2];
+ int rc = pipe(fd);
+ if(rc != 0)
+ {
+ result.test_failure("failed to create pipe");
+ }
+
+ pid_t pid = fork();
+ if ( pid == -1 )
+ {
+ result.test_failure("failed to fork process");
+ return result;
+ }
+ else if ( pid != 0 )
+ {
+ // parent process, wait for randomize_count from child's rng
+ close(fd[1]);
+ read(fd[0], &count, sizeof(count));
+ close(fd[0]);
+
+
+ result.test_eq("parent not reseeded", counting_rng.randomize_count(), 1);
+ result.test_eq("child reseed occurred", count, 2);
+
+ parent_bytes = rng.random_vec(16);
+ read(fd[0], &child_bytes[0], child_bytes.size());
+ result.test_ne("parent and child output sequences differ", parent_bytes, child_bytes);
+ close(fd[0]);
+
+ int status = 0;
+ ::waitpid(pid, &status, 0);
+ }
+ else
+ {
+ // child process, send randomize_count and first output sequence back to parent
+ close(fd[0]);
+ rng.randomize(&child_bytes[0], child_bytes.size());
+ count = counting_rng.randomize_count();
+ write(fd[1], &count, sizeof(count));
+ rng.randomize(&child_bytes[0], child_bytes.size());
+ write(fd[1], &child_bytes[0], child_bytes.size());
+ close(fd[1]);
+ _exit(0);
+ }
+#endif
+ return result;
+ }
+
+ std::vector<Test::Result> run() override
+ {
+ std::vector<Test::Result> results;
+ results.push_back(test_reseed_kat());
+ results.push_back(test_reseed());
+ results.push_back(test_broken_entropy_input());
+ results.push_back(test_check_nonce());
+ results.push_back(test_prediction_resistance());
+ results.push_back(test_fork_safety());
+ return results;
}
};
-BOTAN_REGISTER_TEST("hmac_drbg_reseed", HMAC_DRBG_Reseed_Tests);
+BOTAN_REGISTER_TEST("hmac_drbg_unit", HMAC_DRBG_Unit_Tests);
#endif
diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h
index 6c29b1d55..0379a2ee9 100644
--- a/src/tests/test_rng.h
+++ b/src/tests/test_rng.h
@@ -15,13 +15,6 @@
#include <botan/hex.h>
#include <botan/exceptn.h>
-#if defined(BOTAN_HAS_SYSTEM_RNG)
- #include <botan/system_rng.h>
-#else
- #include <botan/auto_rng.h>
-#endif
-
-
namespace Botan_Tests {
/**
@@ -43,9 +36,9 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator
return out;
}
- size_t reseed_with_sources(Botan::Entropy_Sources&,
- size_t,
- std::chrono::milliseconds) override { return 0; }
+ size_t reseed(Botan::Entropy_Sources&,
+ size_t,
+ std::chrono::milliseconds) override { return 0; }
void randomize(uint8_t out[], size_t len) override
{
@@ -87,7 +80,7 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator
class Fixed_Output_Position_RNG : public Fixed_Output_RNG
{
public:
- bool is_seeded() const override { return !m_buf.empty() || m_rng->is_seeded(); }
+ bool is_seeded() const override { return !m_buf.empty() || Test::rng().is_seeded(); }
uint8_t random() override
{
@@ -114,7 +107,7 @@ class Fixed_Output_Position_RNG : public Fixed_Output_RNG
}
else
{ // return random
- m_rng->randomize(out,len);
+ Test::rng().randomize(out,len);
}
}
@@ -127,32 +120,19 @@ class Fixed_Output_Position_RNG : public Fixed_Output_RNG
explicit Fixed_Output_Position_RNG(const std::vector<uint8_t>& in, uint32_t pos) :
Fixed_Output_RNG(in),
- m_pos(pos),
- m_rng{}
+ m_pos(pos)
{
-#if defined(BOTAN_HAS_SYSTEM_RNG)
- m_rng.reset(new Botan::System_RNG);
-#else
- m_rng.reset(new Botan::AutoSeeded_RNG);
-#endif
}
explicit Fixed_Output_Position_RNG(const std::string& in_str, uint32_t pos) :
Fixed_Output_RNG(in_str),
- m_pos(pos),
- m_rng{}
+ m_pos(pos)
{
-#if defined(BOTAN_HAS_SYSTEM_RNG)
- m_rng.reset(new Botan::System_RNG);
-#else
- m_rng.reset(new Botan::AutoSeeded_RNG);
-#endif
}
private:
uint32_t m_pos = 0;
uint32_t m_requests = 0;
- std::unique_ptr<RandomNumberGenerator> m_rng;
};
class SeedCapturing_RNG : public Botan::RandomNumberGenerator
diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp
index d3467460e..a6f96144c 100644
--- a/src/tests/tests.cpp
+++ b/src/tests/tests.cpp
@@ -127,7 +127,7 @@ bool Test::Result::test_ne(const std::string& what,
const uint8_t expected[], size_t expected_len)
{
if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len))
- return test_failure(who() + ":" + what + " produced matching");
+ return test_failure(who() + ": " + what + " produced matching");
return test_success();
}