diff options
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.cpp | 54 | ||||
-rw-r--r-- | src/lib/rng/hmac_drbg/hmac_drbg.h | 52 | ||||
-rw-r--r-- | src/tests/test_rng.cpp | 61 |
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()); |