aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.cpp54
-rw-r--r--src/lib/rng/hmac_drbg/hmac_drbg.h52
-rw-r--r--src/tests/test_rng.cpp61
3 files changed, 139 insertions, 28 deletions
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
index 6ea66aa2e..2e056e726 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp
@@ -12,38 +12,63 @@ namespace Botan {
HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
RandomNumberGenerator& underlying_rng,
- size_t reseed_interval) :
+ size_t reseed_interval,
+ size_t max_number_of_bytes_per_request) :
Stateful_RNG(underlying_rng, reseed_interval),
- m_mac(std::move(prf))
+ m_mac(std::move(prf)),
+ m_max_number_of_bytes_per_request(max_number_of_bytes_per_request)
{
BOTAN_ASSERT_NONNULL(m_mac);
+
+ if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024)
+ {
+ throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request");
+ }
+
clear();
}
HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
RandomNumberGenerator& underlying_rng,
Entropy_Sources& entropy_sources,
- size_t reseed_interval) :
+ size_t reseed_interval,
+ size_t max_number_of_bytes_per_request ) :
Stateful_RNG(underlying_rng, entropy_sources, reseed_interval),
- m_mac(std::move(prf))
+ m_mac(std::move(prf)),
+ m_max_number_of_bytes_per_request(max_number_of_bytes_per_request)
{
BOTAN_ASSERT_NONNULL(m_mac);
+
+ if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024)
+ {
+ throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request");
+ }
+
clear();
}
HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
Entropy_Sources& entropy_sources,
- size_t reseed_interval) :
+ size_t reseed_interval,
+ size_t max_number_of_bytes_per_request) :
Stateful_RNG(entropy_sources, reseed_interval),
- m_mac(std::move(prf))
+ m_mac(std::move(prf)),
+ m_max_number_of_bytes_per_request(max_number_of_bytes_per_request)
{
BOTAN_ASSERT_NONNULL(m_mac);
+
+ if(m_max_number_of_bytes_per_request == 0 || m_max_number_of_bytes_per_request > 64 * 1024)
+ {
+ throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request");
+ }
+
clear();
}
HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) :
Stateful_RNG(),
- m_mac(std::move(prf))
+ m_mac(std::move(prf)),
+ m_max_number_of_bytes_per_request(64*1024)
{
BOTAN_ASSERT_NONNULL(m_mac);
clear();
@@ -76,22 +101,9 @@ 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)
{
- /**
- * 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)
{
- size_t this_req = std::min(max_number_of_bytes_per_request, output_len);
+ size_t this_req = std::min(m_max_number_of_bytes_per_request, output_len);
output_len -= this_req;
reseed_check();
diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h
index 11d355d70..9210cbca2 100644
--- a/src/lib/rng/hmac_drbg/hmac_drbg.h
+++ b/src/lib/rng/hmac_drbg/hmac_drbg.h
@@ -24,7 +24,7 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
/**
* Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
*
- * Automatic reseeding is disabled completely, as it as no access to
+ * Automatic reseeding is disabled completely, as it has no access to
* any source for seed material.
*
* If a fork is detected, the RNG will be unable to reseed itself
@@ -44,10 +44,22 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
* 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
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
*/
HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
RandomNumberGenerator& underlying_rng,
- size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
/**
* Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
@@ -59,10 +71,22 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
* @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.
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
*/
HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
Entropy_Sources& entropy_sources,
- size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
/**
* Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC)
@@ -77,20 +101,35 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
* @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.
+ * @param max_number_of_bytes_per_request requests that are in size higher
+ * than max_number_of_bytes_per_request are treated as if multiple single
+ * requests of max_number_of_bytes_per_request size had been made.
+ * In theory SP 800-90A requires that we reject any request for a DRBG
+ * output longer than max_number_of_bytes_per_request. To avoid inconveniencing
+ * the caller who wants an output larger than max_number_of_bytes_per_request,
+ * instead treat these requests as if multiple requests of
+ * max_number_of_bytes_per_request size had been made. NIST requires for
+ * HMAC_DRBG that every implementation set a value no more than 2**19 bits
+ * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for
+ * example every 512 bit automatic reseeding occurs.
*/
HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf,
RandomNumberGenerator& underlying_rng,
Entropy_Sources& entropy_sources,
- size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL);
+ size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL,
+ size_t max_number_of_bytes_per_request = 64 * 1024);
/**
* Constructor taking a string for the hash
*/
- HMAC_DRBG(const std::string& hmac_hash) : Stateful_RNG()
+ HMAC_DRBG(const std::string& hmac_hash) :
+ Stateful_RNG(), m_max_number_of_bytes_per_request(64 * 1024),
+ m_mac(MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")"))
{
- m_mac = MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")");
if(!m_mac)
+ {
throw Algorithm_Not_Found(hmac_hash);
+ }
clear();
}
@@ -112,6 +151,7 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG
std::unique_ptr<MessageAuthenticationCode> m_mac;
secure_vector<byte> m_V;
+ const size_t m_max_number_of_bytes_per_request;
};
}
diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp
index e815caf65..021273fe2 100644
--- a/src/tests/test_rng.cpp
+++ b/src/tests/test_rng.cpp
@@ -144,7 +144,10 @@ class HMAC_DRBG_Unit_Tests : public Test
bool is_seeded() const override { return true; }
- void clear() override {}
+ void clear() override
+ {
+ m_randomize_count = 0;
+ }
void randomize(byte[], size_t) override
{
@@ -239,6 +242,61 @@ class HMAC_DRBG_Unit_Tests : public Test
return result;
}
+ Test::Result test_max_number_of_bytes_per_request()
+ {
+ Test::Result result("HMAC_DRBG max_number_of_bytes_per_request");
+
+ std::string mac_string = "HMAC(SHA-256)";
+ auto mac = Botan::MessageAuthenticationCode::create(mac_string);
+ if(!mac)
+ {
+ result.note_missing(mac_string);
+ return result;
+ }
+
+ Request_Counting_RNG counting_rng;
+
+ result.test_throws("HMAC_DRBG does not accept 0 for max_number_of_bytes_per_request", [&mac_string, &counting_rng ]()
+ {
+ Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 0);
+ });
+
+ result.test_throws("HMAC_DRBG does not accept values higher than 64KB for max_number_of_bytes_per_request", [ &mac_string,
+ &counting_rng ]()
+ {
+ Botan::HMAC_DRBG rng(Botan::MessageAuthenticationCode::create(mac_string), counting_rng, 2, 64 * 1024 + 1);
+ });
+
+ // set reseed_interval to 1 so we can test that a long request is split
+ // into multiple, max_number_of_bytes_per_request long requests
+ // for each smaller request, reseed_check() calls counting_rng::randomize(),
+ // which we can compare with
+ Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 1, 64);
+
+ rng.random_vec(63);
+ result.test_eq("one request", counting_rng.randomize_count(), 1);
+
+ rng.clear();
+ counting_rng.clear();
+
+ rng.random_vec(64);
+ result.test_eq("one request", counting_rng.randomize_count(), 1);
+
+ rng.clear();
+ counting_rng.clear();
+
+ rng.random_vec(65);
+ result.test_eq("two requests", counting_rng.randomize_count(), 2);
+
+ rng.clear();
+ counting_rng.clear();
+
+ rng.random_vec(1025);
+ result.test_eq("17 requests", counting_rng.randomize_count(), 17);
+
+ return result;
+ }
+
Test::Result test_broken_entropy_input()
{
Test::Result result("HMAC_DRBG Broken Entropy Input");
@@ -506,6 +564,7 @@ class HMAC_DRBG_Unit_Tests : public Test
std::vector<Test::Result> results;
results.push_back(test_reseed_kat());
results.push_back(test_reseed());
+ results.push_back(test_max_number_of_bytes_per_request());
results.push_back(test_broken_entropy_input());
results.push_back(test_check_nonce());
results.push_back(test_prediction_resistance());