aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <lloyd@randombit.net>2016-06-30 13:15:30 -0400
committerJack Lloyd <lloyd@randombit.net>2016-07-17 10:43:40 -0400
commit93922f20f04058ec624f7db3c74d8aa5a3d06440 (patch)
tree81144cfacced43c68c4385683ee0c123a1987042
parent4c5847412d41756aab738a3746666cfaffe5d4af (diff)
Add Stateful_RNG
Handles fork checking for HMAC_RNG and HMAC_DRBG AutoSeeded_RNG change - switch to HMAC_DRBG as default. Start removing the io buffer from entropy poller. Update default RNG poll bits to 256. Fix McEliece test, was using wrong RNG API. Update docs.
-rw-r--r--doc/manual/rng.rst133
-rw-r--r--src/build-data/buildh.in15
-rw-r--r--src/build-data/policy/bsi.txt1
-rw-r--r--src/build-data/policy/modern.txt2
-rw-r--r--src/cli/speed.cpp6
-rw-r--r--src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp6
-rw-r--r--src/lib/entropy/darwin_secrandom/darwin_secrandom.h2
-rw-r--r--src/lib/entropy/dev_random/dev_random.cpp75
-rw-r--r--src/lib/entropy/dev_random/dev_random.h6
-rw-r--r--src/lib/prov/tpm/tpm.h5
-rw-r--r--src/lib/rng/auto_rng.h (renamed from src/lib/rng/auto_rng/auto_rng.h)14
-rw-r--r--src/lib/rng/auto_rng/info.txt9
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.cpp14
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.h10
-rw-r--r--src/lib/rng/hmac_rng/hmac_rng.cpp109
-rw-r--r--src/lib/rng/hmac_rng/hmac_rng.h14
-rw-r--r--src/lib/rng/rng.cpp59
-rw-r--r--src/lib/rng/rng.h46
-rw-r--r--src/tests/test_mceliece.cpp2
-rw-r--r--src/tests/test_rng.cpp51
20 files changed, 281 insertions, 298 deletions
diff --git a/doc/manual/rng.rst b/doc/manual/rng.rst
index 300570c3a..7eb229a5e 100644
--- a/doc/manual/rng.rst
+++ b/doc/manual/rng.rst
@@ -3,108 +3,77 @@
Random Number Generators
========================================
-The random number generators provided in Botan are meant for creating
-keys, IVs, padding, nonces, and anything else that requires 'random'
-data. It is important to remember that the output of these classes
-will vary, even if they are supplied with the same seed (ie, two
-``Randpool`` objects with similar initial states will not produce the
-same output, because the value of high resolution timers is added to
-the state at various points).
-
-To create a random number generator, instantiate a ``AutoSeeded_RNG``
-object. This object will handle choosing the right algorithms from the
-set of enabled ones and doing seeding using OS specific
-routines. The main service a RandomNumberGenerator provides is, of
-course, random numbers:
+The base class ``RandomNumberGenerator`` is in the header ``botan/rng.h``.
-.. cpp:function:: byte RandomNumberGenerator::next_byte()
+The major interfaces are
+
+.. cpp:function:: void RandomNumberGenerator::randomize(byte* output_array, size_t length)
- Generates a single random byte and returns it
+ Places *length* random bytes into the provided buffer.
-.. cpp:function:: void RandomNumberGenerator::randomize(byte* data, size_t length)
+.. cpp:function:: void RandomNumberGenerator::add_entropy(const byte* data, size_t length)
- Places *length* bytes into the array pointed to by *data*
+ Incorporates provided data into the state of the PRNG, if at all
+ possible. This works for most RNG types, including the system and
+ TPM RNGs. But if the RNG doesn't support this operation, the data is
+ dropped, no error is indicated.
-To ensure good quality output, a PRNG needs to be seeded with truly
-random data. Normally this is done for you. However it may happen that
-your application has access to data that is potentially unpredictable
-to an attacker. If so, use
+.. cpp:function:: void RandomNumberGenerator::randomize_with_input(byte* data, size_t length, \
+ const byte* ad, size_t ad_len)
-.. cpp:function:: void RandomNumberGenerator::add_entropy(const byte* data, \
- size_t length)
+ Like randomize, but first incorporates the additional input field
+ into the state of the RNG. The additional input could be anything which
+ parameterizes this request.
-which incorporates the data into the current randomness state. Don't
-worry about filtering the data or doing any kind of cryptographic
-preprocessing (such as hashing); the RNG objects in botan are designed
-such that you can feed them any arbitrary non-random or even
-maliciously chosen data - as long as at some point some of the seed
-data was good the output will be secure.
+.. cpp:function:: byte RandomNumberGenerator::next_byte()
+ Generates a single random byte and returns it. Note that calling this
+ function several times is much slower than calling ``randomize`` once
+ to produce multiple bytes at a time.
-Implementation Notes
+RNG Types
----------------------------------------
-Randpool
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The following RNG types are included
+
+HMAC_DRBG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+HMAC DRBG is a random number generator designed by NIST and specified
+in SP 800-90A. It can be instantiated with any hash function but is
+typically used with SHA-256, SHA-384, or SHA-512.
-``Randpool`` is the primary PRNG within Botan. In recent versions all
-uses of it have been wrapped by an implementation of the X9.31 PRNG
-(see below). If for some reason you should have cause to create a PRNG
-instead of using the "global" one owned by the library, it would be
-wise to consider the same on the grounds of general caution; while
-``Randpool`` is designed with known attacks and PRNG weaknesses in
-mind, it is not an standard/official PRNG. The remainder of this
-section is a (fairly technical, though high-level) description of the
-algorithms used in this PRNG. Unless you have a specific interest in
-this subject, the rest of this section might prove somewhat
-uninteresting.
-
-``Randpool`` has an internal state called pool, which is 512 bytes
-long. This is where entropy is mixed into and extracted from. There is also a
-small output buffer (called buffer), which holds the data which has already
-been generated but has just not been output yet.
-
-It is based around a MAC and a block cipher (which are currently
-HMAC(SHA-256) and AES-256). Where a specific size is mentioned, it
-should be taken as a multiple of the cipher's block size. For example,
-if a 256-bit block cipher were used instead of AES, all the sizes
-internally would double. Every time some new output is needed, we
-compute the MAC of a counter and a high resolution timer. The
-resulting MAC is XORed into the output buffer (wrapping as needed),
-and the output buffer is then encrypted with AES, producing 16 bytes
-of output.
-
-After 8 blocks (or 128 bytes) have been produced, we mix the pool. To
-do this, we first rekey both the MAC and the cipher; the new MAC key
-is the MAC of the current pool under the old MAC key, while the new
-cipher key is the MAC of the current pool under the just-chosen MAC
-key. We then encrypt the entire pool in CBC mode, using the current
-(unused) output buffer as the IV. We then generate a new output
-buffer, using the mechanism described in the previous paragraph.
-
-To add randomness to the PRNG, we compute the MAC of the input and XOR
-the output into the start of the pool. Then we remix the pool and
-produce a new output buffer. The initial MAC operation should make it
-very hard for chosen inputs to harm the security of ``Randpool``, and
-as HMAC should be able to hold roughly 256 bits of state, it is
-unlikely that we are wasting much input entropy (or, if we are, it
-doesn't matter, because we have a very abundant supply).
+HMAC DRBG seems to be the most conservative generator of the NIST
+approved options.
+
+System_RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In ``system_rng.h``, objects of ``System_RNG`` reference a single
+(process global) reference to the system PRNG (/dev/urandom or
+CryptGenRandom).
+
+AutoSeeded_RNG
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+This instantiates a new instance of a userspace PRNG, seeds it with
+a default entropy pool.
ANSI X9.31
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This generator is deprecated and will be removed in a future release.
+
``ANSI_X931_PRNG`` is the standard issue X9.31 Appendix A.2.4 PRNG,
though using AES-256 instead of 3DES as the block cipher. This PRNG
implementation has been checked against official X9.31 test vectors.
-Internally, the PRNG holds a pointer to another PRNG (typically
-Randpool). This internal PRNG generates the key and seed used by the
-X9.31 algorithm, as well as the date/time vectors. Each time an X9.31
-PRNG object receives entropy, it passes it along to the PRNG it is
-holding, and then pulls out some random bits to generate a new key and
-seed. This PRNG considers itself seeded as soon as the internal PRNG
-is seeded.
-
+Internally, the PRNG holds a pointer to another RNG object. This
+internal PRNG generates the key and seed used by the X9.31 algorithm,
+as well as the date/time vectors. Each time an X9.31 PRNG object
+receives entropy, it passes it along to the PRNG it is holding, and
+then pulls out some random bits to generate a new key and seed. This
+PRNG considers itself seeded as soon as the internal PRNG is seeded.
Entropy Sources
---------------------------------
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index bd8cb6899..82a4ecd50 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -99,13 +99,20 @@
/*
* RNGs will automatically poll the system for additional seed material
-* after producing this many bytes of output.
+* after producing this many bytes of output. Set to zero to disable
+* automatic reseeding.
*/
-#define BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED 4096
-#define BOTAN_RNG_RESEED_POLL_BITS 128
+#define BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED 16384
+#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)
-#define BOTAN_AUTO_RNG_DRBG_HASH_FUNCTION "SHA-384"
+
+/**
+* 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
/*
* 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 5e3c5b921..2ae2ac3b2 100644
--- a/src/build-data/policy/bsi.txt
+++ b/src/build-data/policy/bsi.txt
@@ -41,7 +41,6 @@ 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 f0b6934f0..5a8a2f126 100644
--- a/src/build-data/policy/modern.txt
+++ b/src/build-data/policy/modern.txt
@@ -38,8 +38,8 @@ eme_oaep
emsa_pssr
emsa1
-auto_rng
hmac_rng
+hmac_drbg
ffi
</required>
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
index c6149bf68..1fc0d7343 100644
--- a/src/cli/speed.cpp
+++ b/src/cli/speed.cpp
@@ -427,12 +427,10 @@ class Speed final : public Command
#if defined(BOTAN_HAS_HMAC_DRBG)
for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" })
{
-
- auto hmac = Botan::MessageAuthenticationCode::create("HMAC(" + hash + ")");
- Botan::HMAC_DRBG hmac_drbg(hmac->clone());
+ Botan::HMAC_DRBG hmac_drbg(hash);
bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_size);
- Botan::HMAC_RNG hmac_rng(hmac->clone(), hmac->clone());
+ Botan::HMAC_RNG hmac_rng(hash);
bench_rng(hmac_rng, hmac_rng.name(), msec, buf_size);
}
#endif
diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp
index 0a6b85955..7dde17155 100644
--- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp
+++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp
@@ -16,11 +16,11 @@ namespace Botan {
*/
void Darwin_SecRandom::poll(Entropy_Accumulator& accum)
{
- secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
+ m_io_buf.resize(BOTAN_SYSTEM_RNG_POLL_REQUEST);
- if(0 == SecRandomCopyBytes(kSecRandomDefault, buf.size(), buf.data()))
+ if(0 == SecRandomCopyBytes(kSecRandomDefault, m_io_buf.size(), m_io_buf.data()))
{
- accum.add(buf.data(), buf.size(), BOTAN_ENTROPY_ESTIMATE_STRONG_RNG);
+ accum.add(m_io_buf.data(), m_io_buf.size(), BOTAN_ENTROPY_ESTIMATE_STRONG_RNG);
}
}
diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h
index 09cdc208d..267d177f0 100644
--- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h
+++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h
@@ -21,6 +21,8 @@ class Darwin_SecRandom final : public Entropy_Source
std::string name() const override { return "darwin_secrandom"; }
void poll(Entropy_Accumulator& accum) override;
+ private:
+ secure_vector<uint8_t> m_io_buf;
};
}
diff --git a/src/lib/entropy/dev_random/dev_random.cpp b/src/lib/entropy/dev_random/dev_random.cpp
index aca161d64..ff746f34e 100644
--- a/src/lib/entropy/dev_random/dev_random.cpp
+++ b/src/lib/entropy/dev_random/dev_random.cpp
@@ -6,6 +6,7 @@
*/
#include <botan/internal/dev_random.h>
+#include <botan/exceptn.h>
#include <sys/types.h>
#include <sys/select.h>
@@ -31,15 +32,39 @@ Device_EntropySource::Device_EntropySource(const std::vector<std::string>& fsnam
const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY;
+ m_max_fd = 0;
+
for(auto fsname : fsnames)
{
- fd_type fd = ::open(fsname.c_str(), flags);
+ int fd = ::open(fsname.c_str(), flags);
- if(fd >= 0 && fd < FD_SETSIZE)
- m_devices.push_back(fd);
- else if(fd >= 0)
- ::close(fd);
+ if(fd > 0)
+ {
+ if(fd > FD_SETSIZE)
+ {
+ ::close(fd);
+ throw Exception("Open of OS RNG succeeded but fd is too large for fd_set");
+ }
+
+ m_dev_fds.push_back(fd);
+ m_max_fd = std::max(m_max_fd, fd);
+ }
+ else
+ {
+ /*
+ ENOENT or EACCES is normal as some of the named devices may not exist
+ on this system. But any other errno value probably indicates
+ either a bug in the application or file descriptor exhaustion.
+ */
+ if(errno != ENOENT && errno != EACCES)
+ {
+ throw Exception("Opening OS RNG device failed with errno " +
+ std::to_string(errno));
+ }
+ }
}
+
+ m_io_buf.resize(BOTAN_SYSTEM_RNG_POLL_REQUEST);
}
/**
@@ -47,8 +72,11 @@ Device_EntropySource destructor: close all open devices
*/
Device_EntropySource::~Device_EntropySource()
{
- for(size_t i = 0; i != m_devices.size(); ++i)
- ::close(m_devices[i]);
+ for(int fd : m_dev_fds)
+ {
+ // ignoring return value here, can't throw in destructor anyway
+ ::close(fd);
+ }
}
/**
@@ -56,35 +84,36 @@ Device_EntropySource::~Device_EntropySource()
*/
void Device_EntropySource::poll(Entropy_Accumulator& accum)
{
- if(m_devices.empty())
+ if(m_dev_fds.empty())
return;
- fd_type max_fd = m_devices[0];
fd_set read_set;
FD_ZERO(&read_set);
- for(size_t i = 0; i != m_devices.size(); ++i)
+
+ for(int dev_fd : m_dev_fds)
{
- FD_SET(m_devices[i], &read_set);
- max_fd = std::max(m_devices[i], max_fd);
+ FD_SET(dev_fd, &read_set);
}
struct ::timeval timeout;
-
timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000);
timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000;
- if(::select(max_fd + 1, &read_set, nullptr, nullptr, &timeout) < 0)
- return;
-
- secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST);
-
- for(size_t i = 0; i != m_devices.size(); ++i)
+ if(::select(m_max_fd + 1, &read_set, nullptr, nullptr, &timeout) > 0)
{
- if(FD_ISSET(m_devices[i], &read_set))
+ for(int dev_fd : m_dev_fds)
{
- const ssize_t got = ::read(m_devices[i], buf.data(), buf.size());
- if(got > 0)
- accum.add(buf.data(), got, BOTAN_ENTROPY_ESTIMATE_STRONG_RNG);
+ if(FD_ISSET(dev_fd, &read_set))
+ {
+ const ssize_t got = ::read(dev_fd, m_io_buf.data(), m_io_buf.size());
+
+ if(got > 0)
+ {
+ accum.add(m_io_buf.data(),
+ static_cast<size_t>(got),
+ BOTAN_ENTROPY_ESTIMATE_STRONG_RNG);
+ }
+ }
}
}
}
diff --git a/src/lib/entropy/dev_random/dev_random.h b/src/lib/entropy/dev_random/dev_random.h
index 1f29b2f64..05b36f3eb 100644
--- a/src/lib/entropy/dev_random/dev_random.h
+++ b/src/lib/entropy/dev_random/dev_random.h
@@ -25,10 +25,12 @@ class Device_EntropySource final : public Entropy_Source
void poll(Entropy_Accumulator& accum) override;
Device_EntropySource(const std::vector<std::string>& fsnames);
+
~Device_EntropySource();
private:
- typedef int fd_type;
- std::vector<fd_type> m_devices;
+ secure_vector<uint8_t> m_io_buf;
+ std::vector<int> m_dev_fds;
+ int m_max_fd;
};
}
diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h
index 15bc216ab..b8093518c 100644
--- a/src/lib/prov/tpm/tpm.h
+++ b/src/lib/prov/tpm/tpm.h
@@ -1,3 +1,4 @@
+
/*
* TPM 1.2 interface
* (C) 2015 Jack Lloyd
@@ -71,7 +72,7 @@ class BOTAN_DLL TPM_Context
TSS_HTPM m_tpm;
};
-class BOTAN_DLL TPM_RNG : public RandomNumberGenerator
+class BOTAN_DLL TPM_RNG : public Hardware_RNG
{
public:
TPM_RNG(TPM_Context& ctx) : m_ctx(ctx) {}
@@ -90,7 +91,7 @@ class BOTAN_DLL TPM_RNG : public RandomNumberGenerator
bool is_seeded() const override { return true; }
- void clear() const override {}
+ void clear() override {}
private:
TPM_Context& m_ctx;
diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng.h
index 72ea88d3e..b51390ae2 100644
--- a/src/lib/rng/auto_rng/auto_rng.h
+++ b/src/lib/rng/auto_rng.h
@@ -9,19 +9,20 @@
#define BOTAN_AUTO_SEEDING_RNG_H__
#include <botan/rng.h>
-#include <string>
namespace Botan {
-class AutoSeeded_RNG : public RandomNumberGenerator
+class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator
{
public:
- void randomize(byte out[], size_t len) override
- { m_rng->randomize(out, len); }
+ 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(); }
+ void clear() override { m_rng->clear(); m_counter = 0; }
std::string name() const override { return m_rng->name(); }
@@ -35,9 +36,10 @@ class AutoSeeded_RNG : public RandomNumberGenerator
void add_entropy(const byte in[], size_t len) override
{ m_rng->add_entropy(in, len); }
- AutoSeeded_RNG() : m_rng(RandomNumberGenerator::make_rng()) {}
+ AutoSeeded_RNG(size_t bytes_before_reseed = BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED);
private:
std::unique_ptr<RandomNumberGenerator> m_rng;
+ uint32_t m_counter = 0;
};
}
diff --git a/src/lib/rng/auto_rng/info.txt b/src/lib/rng/auto_rng/info.txt
deleted file mode 100644
index 4f48f484b..000000000
--- a/src/lib/rng/auto_rng/info.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-define AUTO_SEEDING_RNG 20131128
-
-<requires>
-hmac_rng
-hmac
-sha2_32
-sha2_64
-#dev_random|cryptoapi_rng|unix_procs|proc_walk
-</requires>
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
index 201a9f39b..6fdd7daf9 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
@@ -10,9 +10,14 @@
namespace Botan {
-HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash) :
- HMAC_DRBG(hmac_hash, BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED)
- {}
+HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* hmac,
+ size_t max_bytes_before_reseed) :
+ Stateful_RNG(max_bytes_before_reseed),
+ m_mac(hmac)
+ {
+ m_V.resize(m_mac->output_length());
+ clear();
+ }
HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash,
size_t max_bytes_before_reseed) :
@@ -27,12 +32,13 @@ HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash,
}
m_V.resize(m_mac->output_length());
-
clear();
}
void HMAC_DRBG::clear()
{
+ Stateful_RNG::clear();
+
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));
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h
index f52ae9de1..8ee598470 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.h
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.h
@@ -19,10 +19,14 @@ namespace Botan {
class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
{
public:
- HMAC_DRBG(const std::string& hmac_hash);
-
+ /**
+ * Initialize an HMAC_DRBG instance with the given hash function
+ */
HMAC_DRBG(const std::string& hmac_hash,
- size_t max_bytes_before_reseed);
+ size_t max_bytes_before_reseed = BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED);
+
+ HMAC_DRBG(MessageAuthenticationCode* hmac,
+ size_t max_bytes_before_reseed = BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED);
std::string name() const override;
diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp
index 7a9e4dbc5..410e3040a 100644
--- a/src/lib/rng/hmac_rng/hmac_rng.cpp
+++ b/src/lib/rng/hmac_rng/hmac_rng.cpp
@@ -9,15 +9,35 @@
#include <botan/entropy_src.h>
#include <botan/internal/os_utils.h>
#include <algorithm>
-#include <chrono>
namespace Botan {
+HMAC_RNG::HMAC_RNG(const std::string& hash, size_t max_before_reseed) :
+ Stateful_RNG(max_before_reseed)
+ {
+ m_extractor = MAC::create("HMAC(" + hash + ")");
+ if(!m_extractor)
+ throw Invalid_Argument("HMAC_RNG hash not found");
+
+ m_prf.reset(m_extractor->clone());
+
+ if(!m_prf->valid_keylength(m_extractor->output_length()) ||
+ !m_extractor->valid_keylength(m_prf->output_length()))
+ {
+ throw Invalid_Argument("HMAC_RNG: Bad algo combination " +
+ m_extractor->name() + " and " +
+ m_prf->name());
+ }
+
+ this->clear();
+ }
+
/*
* HMAC_RNG Constructor
*/
HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor,
MessageAuthenticationCode* prf) :
+ Stateful_RNG(BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED),
m_extractor(extractor), m_prf(prf)
{
if(!m_prf->valid_keylength(m_extractor->output_length()) ||
@@ -33,7 +53,7 @@ HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor,
void HMAC_RNG::clear()
{
- m_collected_entropy_estimate = 0;
+ Stateful_RNG::clear();
m_counter = 0;
// First PRF inputs are all zero, as specified in section 2
@@ -71,7 +91,7 @@ void HMAC_RNG::clear()
void HMAC_RNG::new_K_value(byte label)
{
m_prf->update(m_K);
- m_prf->update_be(m_pid);
+ m_prf->update_be(last_pid());
m_prf->update_be(OS::get_processor_timestamp());
m_prf->update_be(OS::get_system_timestamp_ns());
m_prf->update_be(m_counter++);
@@ -84,76 +104,38 @@ void HMAC_RNG::new_K_value(byte label)
*/
void HMAC_RNG::randomize(byte out[], size_t length)
{
- if(!is_seeded() || m_pid != OS::get_process_id())
- {
- reseed(256);
- if(!is_seeded())
- throw PRNG_Unseeded(name());
- }
-
- const size_t max_per_prf_iter = m_prf->output_length() / 2;
-
- m_output_since_reseed += length;
-
- if(m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED)
- {
- reseed_with_sources(Entropy_Sources::global_sources(),
- BOTAN_RNG_RESEED_POLL_BITS,
- BOTAN_RNG_AUTO_RESEED_TIMEOUT);
- }
+ reseed_check(length);
- /*
- HMAC KDF as described in E-t-E, using a CTXinfo of "rng"
- */
while(length)
{
new_K_value(Running);
- const size_t copied = std::min<size_t>(length, max_per_prf_iter);
+ const size_t copied = std::min<size_t>(length, m_prf->output_length());
copy_mem(out, m_K.data(), copied);
out += copied;
length -= copied;
}
+
+ new_K_value(BlockFinished);
}
size_t HMAC_RNG::reseed_with_sources(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
+
/*
- Using the terminology of E-t-E, XTR is the MAC function (normally
- HMAC) seeded with XTS (below) and we form SKM, the key material, by
- polling as many sources as we think needed to reach our polling
- goal. We then also include feedback of the current PRK so that
- a bad poll doesn't wipe us out.
+ * This ends up calling add_entropy which provides input to the extractor
*/
-
- typedef std::chrono::system_clock clock;
- auto deadline = clock::now() + timeout;
-
- double bits_collected = 0;
-
- Entropy_Accumulator accum([&](const byte in[], size_t in_len, double entropy_estimate) {
- m_extractor->update(in, in_len);
- bits_collected += entropy_estimate;
- return (bits_collected >= poll_bits || clock::now() > deadline);
- });
-
- srcs.poll(accum);
+ size_t bits_collected = Stateful_RNG::reseed_with_sources(srcs, poll_bits, timeout);
/*
- * It is necessary to feed forward poll data. Otherwise, a good poll
- * (collecting a large amount of conditional entropy) followed by a
- * bad one (collecting little) would be unsafe. Do this by
- * generating new PRF outputs using the previous key and feeding
- * them into the extractor function.
+ Now derive the new PRK using everything that has been fed into
+ the extractor, and set the PRF key to that
*/
- new_K_value(Reseed);
- m_extractor->update(m_K); // K is the CTXinfo=reseed PRF output
-
- /* Now derive the new PRK using everything that has been fed into
- the extractor, and set the PRF key to that */
m_prf->set_key(m_extractor->final());
// Now generate a new PRF output to use as the XTS extractor salt
@@ -164,32 +146,17 @@ size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs,
zeroise(m_K);
m_counter = 0;
- m_collected_entropy_estimate =
- std::min<size_t>(m_collected_entropy_estimate + static_cast<size_t>(bits_collected),
- m_extractor->output_length() * 8);
-
- m_output_since_reseed = 0;
- m_pid = OS::get_process_id();
-
- return static_cast<size_t>(bits_collected);
- }
-
-bool HMAC_RNG::is_seeded() const
- {
- return (m_collected_entropy_estimate >= 256);
+ return bits_collected;
}
/*
-* Add user-supplied entropy to the extractor input then reseed
-* to incorporate it into the state
+* Add user-supplied entropy to the extractor input then set remaining
+* output length to for a reseed on next use.
*/
void HMAC_RNG::add_entropy(const byte input[], size_t length)
{
m_extractor->update(input, length);
-
- reseed_with_sources(Entropy_Sources::global_sources(),
- BOTAN_RNG_RESEED_POLL_BITS,
- BOTAN_RNG_RESEED_DEFAULT_TIMEOUT);
+ force_reseed();
}
/*
diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h
index 95ae25e39..f2f8a610d 100644
--- a/src/lib/rng/hmac_rng/hmac_rng.h
+++ b/src/lib/rng/hmac_rng/hmac_rng.h
@@ -24,11 +24,10 @@ namespace Botan {
* 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 RandomNumberGenerator
+class BOTAN_DLL HMAC_RNG : public Stateful_RNG
{
public:
void randomize(byte buf[], size_t len) override;
- bool is_seeded() const override;
void clear() override;
std::string name() const override;
@@ -44,23 +43,26 @@ class BOTAN_DLL HMAC_RNG : public RandomNumberGenerator
*/
HMAC_RNG(MessageAuthenticationCode* extractor,
MessageAuthenticationCode* prf);
+
+ /**
+ * Use the specified hash for both the extractor and PRF functions
+ */
+ HMAC_RNG(const std::string& hash,
+ size_t max_before_reseed = BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED);
private:
std::unique_ptr<MessageAuthenticationCode> m_extractor;
std::unique_ptr<MessageAuthenticationCode> m_prf;
enum HMAC_PRF_Label {
Running,
+ BlockFinished,
Reseed,
ExtractorSeed,
};
void new_K_value(byte label);
- size_t m_collected_entropy_estimate = 0;
- size_t m_output_since_reseed = 0;
-
secure_vector<byte> m_K;
u32bit m_counter = 0;
- u32bit m_pid = 0;
};
}
diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp
index 526693561..8144ac293 100644
--- a/src/lib/rng/rng.cpp
+++ b/src/lib/rng/rng.cpp
@@ -6,12 +6,18 @@
*/
#include <botan/rng.h>
-#include <botan/hmac_drbg.h>
#include <botan/auto_rng.h>
#include <botan/entropy_src.h>
#include <botan/loadstor.h>
#include <botan/internal/os_utils.h>
-#include <chrono>
+
+#if defined(BOTAN_HAS_HMAC_DRBG)
+ #include <botan/hmac_drbg.h>
+#endif
+
+#if defined(BOTAN_HAS_HMAC_RNG)
+ #include <botan/hmac_rng.h>
+#endif
namespace Botan {
@@ -50,9 +56,15 @@ size_t RandomNumberGenerator::reseed_with_sources(Entropy_Sources& srcs,
return bits_collected;
}
-Stateful_RNG::Stateful_RNG(size_t bytes_before_reseed) :
- m_max_bytes_before_reseed_required(bytes_before_reseed)
+Stateful_RNG::Stateful_RNG(size_t bytes_before_reseed) : m_bytes_before_reseed(bytes_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,
@@ -81,10 +93,9 @@ void Stateful_RNG::reseed_check(size_t bytes_requested)
{
this->reseed(BOTAN_RNG_RESEED_POLL_BITS);
}
- else if(m_max_bytes_before_reseed_required > 0 &&
- m_bytes_since_reseed >= m_max_bytes_before_reseed_required)
+ else if(m_bytes_before_reseed > 0 && m_bytes_since_reseed >= m_bytes_before_reseed)
{
- this->reseed_with_timeout(BOTAN_RNG_AUTO_RESEED_POLL_BITS,
+ this->reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS,
BOTAN_RNG_AUTO_RESEED_TIMEOUT);
}
@@ -112,8 +123,10 @@ RandomNumberGenerator* RandomNumberGenerator::make_rng()
AutoSeeded_RNG::AutoSeeded_RNG(size_t max_bytes_before_reseed)
{
- m_rng.reset(new HMAC_DRBG(BOTAN_AUTO_RNG_DRBG_HASH_FUNCTION, max_bytes_before_reseed));
- size_t bits = m_rng->reseed(384);
+ m_rng.reset(new BOTAN_AUTO_RNG_DRBG(BOTAN_AUTO_RNG_HASH, max_bytes_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 " +
@@ -124,24 +137,22 @@ AutoSeeded_RNG::AutoSeeded_RNG(size_t max_bytes_before_reseed)
void AutoSeeded_RNG::randomize(byte output[], size_t output_len)
{
/*
- This data is not secret so skipping a vector/secure_vector allows
- avoiding an allocation.
+ Form additional input which is provided to the PRNG implementation
+ to paramaterize the KDF output.
*/
- typedef std::chrono::high_resolution_clock clock;
-
- byte nonce_buf[16] = { 0 };
- const uint32_t cur_ctr = m_counter++;
- const uint32_t cur_pid = OS::get_process_id();
- const uint64_t cur_time = clock::now().time_since_epoch().count();
-
- store_le(cur_ctr, nonce_buf);
- store_le(cur_pid, nonce_buf + 4);
- store_le(cur_time, nonce_buf + 8);
+ 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);
- m_rng->randomize_with_input(output, output_len,
- nonce_buf, sizeof(nonce_buf));
+ randomize_with_input(output, output_len, additional_input, sizeof(additional_input));
+ }
- ++m_counter;
+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/rng.h b/src/lib/rng/rng.h
index 2e08ce553..d5fae0261 100644
--- a/src/lib/rng/rng.h
+++ b/src/lib/rng/rng.h
@@ -154,7 +154,7 @@ class BOTAN_DLL RandomNumberGenerator
* bytes have been output.
*
* Not implemented by RNGs which access an external RNG, such as the
-* system PRNG or an hardware RNG.
+* system PRNG or a hardware RNG.
*/
class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator
{
@@ -182,16 +182,37 @@ class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator
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_bytes_before_reseed; }
+
+ uint32_t last_pid() const { return m_last_pid; }
+
+ mutable std::mutex m_mutex;
+
private:
- const size_t m_max_bytes_before_reseed_required;
+ const size_t m_bytes_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;
/**
+* Hardware RNG has no members but exists to tag hardware RNG types
+*/
+class BOTAN_DLL Hardware_RNG : public RandomNumberGenerator
+ {
+ };
+
+/**
* Null/stub RNG - fails if you try to use it for anything
* This is not generally useful except for in certain tests
*/
@@ -212,7 +233,6 @@ class BOTAN_DLL Null_RNG final : public RandomNumberGenerator
std::string name() const override { return "Null_RNG"; }
};
-
/**
* Wraps access to a RNG in a mutex
*/
@@ -225,20 +245,6 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator
m_rng->randomize(out, len);
}
- void randomize_with_input(byte output[], size_t output_length,
- const byte input[], size_t input_length) override
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_rng->randomize_with_input(output, output_length,
- input, input_length);
- }
-
- void add_entropy(const byte in[], size_t len) override
- {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_rng->add_entropy(in, len);
- }
-
bool is_seeded() const override
{
std::lock_guard<std::mutex> lock(m_mutex);
@@ -265,6 +271,12 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator
return m_rng->reseed_with_sources(src, bits, msec);
}
+ void add_entropy(const byte in[], size_t len) override
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_rng->add_entropy(in, len);
+ }
+
Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {}
explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {}
private:
diff --git a/src/tests/test_mceliece.cpp b/src/tests/test_mceliece.cpp
index 8c0ad4564..8658bf5e6 100644
--- a/src/tests/test_mceliece.cpp
+++ b/src/tests/test_mceliece.cpp
@@ -77,7 +77,7 @@ class McEliece_Keygen_Encrypt_Test : public Text_Based_Test
result.test_eq("private key fingerprint", hash_bytes(mce_priv.pkcs8_private_key()), fprint_priv);
rng.clear();
- rng.add_entropy(encrypt_seed.data(), encrypt_seed.size());
+ rng.initialize_with(encrypt_seed.data(), encrypt_seed.size());
try
{
diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp
index 7f1c1f123..d8c10bf55 100644
--- a/src/tests/test_rng.cpp
+++ b/src/tests/test_rng.cpp
@@ -21,37 +21,10 @@ namespace {
Botan::RandomNumberGenerator* get_rng(const std::string& algo_str, const std::vector<byte>& ikm)
{
- class AllOnce_RNG : public Fixed_Output_RNG
- {
- public:
- explicit AllOnce_RNG(const std::vector<byte>& in) : Fixed_Output_RNG(in) {}
-
- Botan::secure_vector<byte> random_vec(size_t) override
- {
- Botan::secure_vector<byte> vec(this->remaining());
- this->randomize(vec.data(), vec.size());
- return vec;
- }
- };
-
const std::vector<std::string> algo_name = Botan::parse_algorithm_name(algo_str);
const std::string rng_name = algo_name[0];
-#if defined(BOTAN_HAS_HMAC_DRBG)
- if(rng_name == "HMAC_DRBG")
- {
- auto mac = Botan::MessageAuthenticationCode::create("HMAC(" + algo_name[1] + ")");
-
- if(!mac)
- {
- return nullptr;
- }
-
- return new Botan::HMAC_DRBG(mac.release(), new AllOnce_RNG(ikm));
- }
-
-#endif
#if defined(BOTAN_HAS_X931_RNG)
if(rng_name == "X9.31-RNG")
@@ -110,7 +83,8 @@ class HMAC_DRBG_Tests : public Text_Based_Test
{
public:
HMAC_DRBG_Tests() : Text_Based_Test("hmac_drbg.vec",
- {"EntropyInput", "EntropyInputReseed", "Out"}) {}
+ {"EntropyInput", "EntropyInputReseed", "Out"},
+ {"AdditionalInput1", "AdditionalInput2"}) {}
Test::Result run_one_test(const std::string& algo, const VarMap& vars) override
{
@@ -118,23 +92,30 @@ class HMAC_DRBG_Tests : public Text_Based_Test
const std::vector<byte> reseed_input = get_req_bin(vars, "EntropyInputReseed");
const std::vector<byte> expected = get_req_bin(vars, "Out");
- Test::Result result(algo);
+ const std::vector<byte> ad1 = get_opt_bin(vars, "AdditionalInput1");
+ const std::vector<byte> ad2 = get_opt_bin(vars, "AdditionalInput2");
- std::unique_ptr<Botan::RandomNumberGenerator> rng(get_rng(algo, seed_input));
- if(!rng)
+ Test::Result result("HMAC_DRBG(" + algo + ")");
+
+ auto mac = Botan::MessageAuthenticationCode::create("HMAC(" + algo + ")");
+ if(!mac)
{
- result.note_missing("RNG " + algo);
+ result.note_missing("HMAC(" + algo + ")");
return result;
}
- rng->reseed(0); // force initialization
+ std::unique_ptr<Botan::HMAC_DRBG> rng(new Botan::HMAC_DRBG(mac.release(), 0));
+ rng->initialize_with(seed_input.data(), seed_input.size());
// now reseed
rng->add_entropy(reseed_input.data(), reseed_input.size());
- rng->random_vec(expected.size()); // discard 1st block
+ std::vector<byte> out(expected.size());
+ // first block is discarded
+ rng->randomize_with_input(out.data(), out.size(), ad1.data(), ad1.size());
+ rng->randomize_with_input(out.data(), out.size(), ad2.data(), ad2.size());
- result.test_eq("rng", rng->random_vec(expected.size()), expected);
+ result.test_eq("rng", out, expected);
return result;
}