diff options
author | Jack Lloyd <[email protected]> | 2017-08-04 12:01:08 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-08-04 12:01:08 -0400 |
commit | 58b1f7cc90b3e5c8a4bbff7adf2c001db0ef4d21 (patch) | |
tree | 27e2fcd7fd61c362218dd0c0948844a5c92a5ee2 /src/tests/test_rng.cpp | |
parent | 8a29dc8209c6e93581075bbc4c39ff5bf0cdace5 (diff) | |
parent | 71d8bfac34acbab87a2319a8581b0aefbe762672 (diff) |
Complete merge of #1137 ChaCha_RNG
For whatever reason only the first commit in that branch got
merged to master. Not sure what happened.
Diffstat (limited to 'src/tests/test_rng.cpp')
-rw-r--r-- | src/tests/test_rng.cpp | 684 |
1 files changed, 314 insertions, 370 deletions
diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp index db532054c..899ed0050 100644 --- a/src/tests/test_rng.cpp +++ b/src/tests/test_rng.cpp @@ -16,14 +16,14 @@ #include <botan/auto_rng.h> #endif -#if defined(BOTAN_HAS_SYSTEM_RNG) - #include <botan/system_rng.h> -#endif - #if defined(BOTAN_HAS_CHACHA_RNG) #include <botan/chacha_rng.h> #endif +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include <botan/system_rng.h> +#endif + #if defined(BOTAN_HAS_RDRAND_RNG) #include <botan/rdrand_rng.h> #include <botan/cpuid.h> @@ -38,286 +38,139 @@ #include <sys/wait.h> #endif -#include <iostream> - namespace Botan_Tests { namespace { -#if defined(BOTAN_HAS_HMAC_DRBG) +#if defined(BOTAN_HAS_STATEFUL_RNG) -class HMAC_DRBG_Tests : public Text_Based_Test +class Stateful_RNG_Tests : public Test { public: - HMAC_DRBG_Tests() - : Text_Based_Test("hmac_drbg.vec", - "EntropyInput,EntropyInputReseed,Out", - "AdditionalInput1,AdditionalInput2") {} - - Test::Result run_one_test(const std::string& algo, const VarMap& vars) override + std::vector<Test::Result> run() override { - const std::vector<uint8_t> seed_input = get_req_bin(vars, "EntropyInput"); - const std::vector<uint8_t> reseed_input = get_req_bin(vars, "EntropyInputReseed"); - const std::vector<uint8_t> expected = get_req_bin(vars, "Out"); - - const std::vector<uint8_t> ad1 = get_opt_bin(vars, "AdditionalInput1"); - const std::vector<uint8_t> ad2 = get_opt_bin(vars, "AdditionalInput2"); - - Test::Result result("HMAC_DRBG(" + algo + ")"); - - auto mac = Botan::MessageAuthenticationCode::create("HMAC(" + algo + ")"); - - if(!mac) - { - result.note_missing("HMAC(" + algo + ")"); - return result; - } - - 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 - rng->add_entropy(reseed_input.data(), reseed_input.size()); - - std::vector<uint8_t> 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", out, expected); - return result; + 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()); + results.push_back(test_fork_safety()); + results.push_back(test_randomize_with_ts_input()); + results.push_back(test_security_level()); + return results; } - }; + protected: + virtual std::string rng_name() const = 0; -BOTAN_REGISTER_TEST("hmac_drbg", HMAC_DRBG_Tests); + virtual std::unique_ptr<Botan::Stateful_RNG> create_rng( + Botan::RandomNumberGenerator* underlying_rng, + Botan::Entropy_Sources* underlying_es, + size_t reseed_interval) = 0; -class HMAC_DRBG_Unit_Tests : public Test - { - private: - class Broken_Entropy_Source : public Botan::Entropy_Source + std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng, + size_t reseed_interval = 1024) { - public: - std::string name() const override - { - return "Broken Entropy Source"; - } - - size_t poll(Botan::RandomNumberGenerator&) override - { - throw Botan::Exception("polling not available"); - } - }; - - class Insufficient_Entropy_Source : public Botan::Entropy_Source - { - public: - std::string name() const override - { - return "Insufficient Entropy Source"; - } - - size_t poll(Botan::RandomNumberGenerator&) override - { - return 0; - } - }; + return create_rng(&underlying_rng, nullptr, reseed_interval); + } - class Request_Counting_RNG : public Botan::RandomNumberGenerator + std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::Entropy_Sources& underlying_srcs, + size_t reseed_interval = 1024) { - public: - Request_Counting_RNG() : m_randomize_count(0) {} - - bool is_seeded() const override - { - return true; - } - - void clear() override - { - m_randomize_count = 0; - } - - void randomize(uint8_t[], size_t) override - { - m_randomize_count++; - } - - void add_entropy(const uint8_t[], 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; - }; + return create_rng(nullptr, &underlying_srcs, reseed_interval); + } - public: - Test::Result test_reseed_kat() + std::unique_ptr<Botan::Stateful_RNG> make_rng(Botan::RandomNumberGenerator& underlying_rng, + Botan::Entropy_Sources& underlying_srcs, + size_t reseed_interval = 1024) { - 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; - } - - Request_Counting_RNG counting_rng; - Botan::HMAC_DRBG rng(std::move(mac), counting_rng, Botan::Entropy_Sources::global_sources(), 2); - Botan::secure_vector<uint8_t> seed_input( - { - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, - 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF - }); - Botan::secure_vector<uint8_t> output_after_initialization( - { - 0x48, 0xD3, 0xB4, 0x5A, 0xAB, 0x65, 0xEF, 0x92, 0xCC, 0xFC, 0xB9, 0x42, 0x7E, 0xF2, 0x0C, 0x90, - 0x29, 0x70, 0x65, 0xEC, 0xC1, 0xB8, 0xA5, 0x25, 0xBF, 0xE4, 0xDC, 0x6F, 0xF3, 0x6D, 0x0E, 0x38 - }); - Botan::secure_vector<uint8_t> output_without_reseed( - {0xC4, 0x90, 0x04, 0x5B, 0x35, 0x4F, 0x50, 0x09, 0x68, 0x45, 0xF0, 0x4B, 0x11, 0x03, 0x58, 0xF0}); - result.test_eq("is_seeded", rng.is_seeded(), false); - - rng.initialize_with(seed_input.data(), seed_input.size()); - - Botan::secure_vector<uint8_t> out(32); - - 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); + return create_rng(&underlying_rng, &underlying_srcs, reseed_interval); + } - // 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); + virtual Test::Result test_reseed_kat() = 0; - return result; - } + virtual Test::Result test_security_level() = 0; + virtual Test::Result test_max_number_of_bytes_per_request() = 0; + private: 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::Result result(rng_name() + " Reseed"); // test reseed_interval is enforced Request_Counting_RNG counting_rng; - Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 2); - rng.random_vec(7); + std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2); + + rng->random_vec(7); result.test_eq("initial seeding", counting_rng.randomize_count(), 1); - rng.random_vec(9); + rng->random_vec(9); result.test_eq("still initial seed", counting_rng.randomize_count(), 1); - rng.random_vec(1); + rng->random_vec(1); result.test_eq("first reseed", counting_rng.randomize_count(), 2); - rng.random_vec(15); + rng->random_vec(15); result.test_eq("still first reseed", counting_rng.randomize_count(), 2); - rng.random_vec(15); + rng->random_vec(15); result.test_eq("second reseed", counting_rng.randomize_count(), 3); - rng.random_vec(1); + 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_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 failing_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 ]() + if(rng->max_number_of_bytes_per_request() > 0) { - Botan::HMAC_DRBG failing_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(); + // request > max_number_of_bytes_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(1025); - result.test_eq("17 requests", counting_rng.randomize_count(), 17); + 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"); + Test::Result result(rng_name() + " Broken Entropy Input"); - auto mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); - if(!mac) + class Broken_Entropy_Source : public Botan::Entropy_Source { - result.note_missing("HMAC(SHA-256)"); - return result; - } + public: + std::string name() const override + { + return "Broken Entropy Source"; + } + + size_t poll(Botan::RandomNumberGenerator&) override + { + throw Botan::Exception("polling not available"); + } + }; + + class Insufficient_Entropy_Source : public Botan::Entropy_Source + { + public: + std::string name() const override + { + return "Insufficient Entropy Source"; + } + + size_t poll(Botan::RandomNumberGenerator&) override + { + return 0; + } + }; // 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); + std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng = make_rng(broken_entropy_input_rng); - result.test_throws("broken underlying rng", [&rng_with_broken_rng]() { rng_with_broken_rng.random_vec(16); }); + 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()); @@ -327,75 +180,64 @@ class HMAC_DRBG_Unit_Tests : public Test 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); }); + std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_es = make_rng(broken_entropy_sources); + 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); }); + std::unique_ptr<Botan::Stateful_RNG> rng_with_insufficient_es = make_rng(insufficient_entropy_sources); + 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); + + std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng_and_good_es = + make_rng(broken_entropy_input_rng, Botan::Entropy_Sources::global_sources()); + + result.test_throws("broken underlying rng but good entropy sources", [&rng_with_broken_rng_and_good_es]() + { rng_with_broken_rng_and_good_es->random_vec(16); }); + + std::unique_ptr<Botan::Stateful_RNG> rng_with_good_rng_and_broken_es = + make_rng(Test::rng(), broken_entropy_sources); + + result.test_throws("good underlying rng but broken entropy sources", [&rng_with_good_rng_and_broken_es]() + { rng_with_good_rng_and_broken_es->random_vec(16); }); + + std::unique_ptr<Botan::Stateful_RNG> rng_with_broken_rng_and_broken_es = + make_rng(broken_entropy_input_rng, broken_entropy_sources); + 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); }); + { 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; - } + Test::Result result(rng_name() + " Nonce Check"); - // make sure the nonce has at least 1/2*security_strength bits + // make sure the nonce has at least security_strength bits + std::unique_ptr<Botan::Stateful_RNG> rng = create_rng(nullptr, nullptr, 0); - // SHA-256 -> 256 bits security strength - for(auto nonce_size : { 0, 4, 8, 16, 31, 32, 34 }) + for(size_t nonce_size : { 0, 4, 8, 16, 31, 32, 34, 64 }) { - if(!mac) - { - mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); - } + rng->clear(); + result.test_eq("not seeded", rng->is_seeded(), false); - Botan::HMAC_DRBG rng(std::move(mac)); - result.test_eq("not seeded", rng.is_seeded(), false); - std::vector<uint8_t> nonce(nonce_size); - rng.initialize_with(nonce.data(), nonce.size()); + const std::vector<uint8_t> nonce(nonce_size); + rng->initialize_with(nonce.data(), nonce.size()); - if(nonce_size < 32) + if(nonce_size < rng->security_level() / 8) { - result.test_eq("not seeded", rng.is_seeded(), false); - result.test_throws("invalid nonce size", [&rng, &nonce]() { rng.random_vec(32); }); + result.test_eq("not seeded", rng->is_seeded(), false); + result.test_throws("invalid nonce size", [&rng]() { rng->random_vec(32); }); } else { - result.test_eq("is seeded", rng.is_seeded(), true); - rng.random_vec(32); + result.test_eq("is seeded", rng->is_seeded(), true); + rng->random_vec(32); } } @@ -404,26 +246,19 @@ class HMAC_DRBG_Unit_Tests : public Test 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; - } + Test::Result result(rng_name() + " Prediction Resistance"); // set reseed_interval = 1, forcing a reseed for every RNG request Request_Counting_RNG counting_rng; - Botan::HMAC_DRBG rng(std::move(mac), counting_rng, 1); + std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 1); - rng.random_vec(16); + rng->random_vec(16); result.test_eq("first request", counting_rng.randomize_count(), size_t(1)); - rng.random_vec(16); + rng->random_vec(16); result.test_eq("second request", counting_rng.randomize_count(), size_t(2)); - rng.random_vec(16); + rng->random_vec(16); result.test_eq("third request", counting_rng.randomize_count(), size_t(3)); return result; @@ -431,23 +266,16 @@ class HMAC_DRBG_Unit_Tests : public Test Test::Result test_fork_safety() { - Test::Result result("HMAC_DRBG Fork Safety"); + Test::Result result(rng_name() + " 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); + std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, reseed_interval); - rng.random_vec(16); + 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 @@ -483,7 +311,7 @@ class HMAC_DRBG_Unit_Tests : public Test result.test_failure("Failed to read count size from child process"); } - parent_bytes = rng.random_vec(16); + parent_bytes = rng->random_vec(16); got = ::read(fd[0], &child_bytes[0], child_bytes.size()); if(got > 0) @@ -505,12 +333,12 @@ class HMAC_DRBG_Unit_Tests : public Test { // child process, send randomize_count and first output sequence back to parent ::close(fd[0]); // close read end in child - rng.randomize(&child_bytes[0], child_bytes.size()); + rng->randomize(&child_bytes[0], child_bytes.size()); count = counting_rng.randomize_count(); ssize_t written = ::write(fd[1], &count, sizeof(count)); try { - rng.randomize(&child_bytes[0], child_bytes.size()); + rng->randomize(&child_bytes[0], child_bytes.size()); } catch(std::exception& e) { @@ -525,7 +353,118 @@ class HMAC_DRBG_Unit_Tests : public Test return result; } - Test::Result test_security_level() + Test::Result test_randomize_with_ts_input() + { + Test::Result result(rng_name() + " Randomize With Timestamp Input"); + + const size_t request_bytes = 64; + const std::vector<uint8_t> seed(128); + + // check that randomize_with_ts_input() creates different output based on a timestamp + // and possibly additional data, such as process id even with identical seeds + Fixed_Output_RNG fixed_output_rng1(seed); + Fixed_Output_RNG fixed_output_rng2(seed); + + std::unique_ptr<Botan::Stateful_RNG> rng1 = make_rng(fixed_output_rng1); + std::unique_ptr<Botan::Stateful_RNG> rng2 = make_rng(fixed_output_rng2); + + Botan::secure_vector<uint8_t> output1(request_bytes); + Botan::secure_vector<uint8_t> output2(request_bytes); + + rng1->randomize(output1.data(), output1.size()); + rng2->randomize(output2.data(), output2.size()); + + result.test_eq("equal output due to same seed", output1, output2); + + rng1->randomize_with_ts_input(output1.data(), output1.size()); + rng2->randomize_with_ts_input(output2.data(), output2.size()); + + result.test_ne("output differs due to different timestamp", output1, output2); + + return result; + } + + }; + +#endif + +#if defined(BOTAN_HAS_HMAC_DRBG) && defined(BOTAN_HAS_SHA2_32) + +class HMAC_DRBG_Unit_Tests : public Stateful_RNG_Tests + { + public: + std::string rng_name() const override { return "HMAC_DRBG"; } + + std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng, + Botan::Entropy_Sources* underlying_es, + size_t reseed_interval) override + { + std::unique_ptr<Botan::MessageAuthenticationCode> mac = + Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); + + if(underlying_rng && underlying_es) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_rng, *underlying_es, reseed_interval)); + else if(underlying_rng) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_rng, reseed_interval)); + else if(underlying_es) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac), *underlying_es, reseed_interval)); + else if(reseed_interval == 0) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::HMAC_DRBG(std::move(mac))); + else + throw Test_Error("Invalid reseed interval in HMAC_DRBG unit test"); + } + + Test::Result test_max_number_of_bytes_per_request() override + { + Test::Result result("HMAC_DRBG max_number_of_bytes_per_request"); + + const std::string mac_string = "HMAC(SHA-256)"; + + 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 failing_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 failing_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(Botan::MessageAuthenticationCode::create(mac_string), 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_security_level() override { Test::Result result("HMAC_DRBG Security Level"); @@ -550,104 +489,112 @@ class HMAC_DRBG_Unit_Tests : public Test return result; } - Test::Result test_randomize_with_ts_input() + Test::Result test_reseed_kat() override { - Test::Result result("HMAC_DRBG Randomize With Timestamp Input"); + 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; - } + Request_Counting_RNG counting_rng; + std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2); - const size_t reseed_interval = 1024; - const size_t request_bytes = 64; - const std::vector<uint8_t> seed(128); + const Botan::secure_vector<uint8_t> seed_input( + { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }); - // check that randomize_with_ts_input() creates different output based on a timestamp - // and possibly additional data, such as process id - Fixed_Output_RNG fixed_output_rng1(seed); - Botan::HMAC_DRBG rng1(std::move(mac), fixed_output_rng1, reseed_interval); - Botan::secure_vector<uint8_t> output1(request_bytes); - rng1.randomize(output1.data(), output1.size()); + result.test_eq("is_seeded", rng->is_seeded(), false); - mac = Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"); - Fixed_Output_RNG fixed_output_rng2(seed); - Botan::HMAC_DRBG rng2(std::move(mac), fixed_output_rng2, reseed_interval); - Botan::secure_vector<uint8_t> output2(request_bytes); - rng2.randomize(output2.data(), output2.size()); + rng->initialize_with(seed_input.data(), seed_input.size()); - result.test_eq("equal output due to same seed", output1, output2); + Botan::secure_vector<uint8_t> out(32); - rng1.randomize_with_ts_input(output1.data(), output1.size()); - rng2.randomize_with_ts_input(output2.data(), output2.size()); + 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, "48D3B45AAB65EF92CCFCB9427EF20C90297065ECC1B8A525BFE4DC6FF36D0E38"); - result.test_ne("output differs due to different timestamp", output1, output2); + // 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_eq("out after reseed", out, "2F8FCA696832C984781123FD64F4B20C7379A25C87AB29A21C9BF468B0081CE2"); 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_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()); - results.push_back(test_fork_safety()); - results.push_back(test_randomize_with_ts_input()); - results.push_back(test_security_level()); - return results; - } }; BOTAN_REGISTER_TEST("hmac_drbg_unit", HMAC_DRBG_Unit_Tests); #endif - #if defined(BOTAN_HAS_CHACHA_RNG) -class ChaCha_RNG_Tests : public Text_Based_Test +class ChaCha_RNG_Unit_Tests : public Stateful_RNG_Tests { public: - ChaCha_RNG_Tests() - : Text_Based_Test("rng/chacha_rng.vec", - "EntropyInput,EntropyInputReseed,Out", - "AdditionalInput1,AdditionalInput2") {} - Test::Result run_one_test(const std::string& algo, const VarMap& vars) override + std::string rng_name() const override { return "ChaCha_RNG"; } + + std::unique_ptr<Botan::Stateful_RNG> create_rng(Botan::RandomNumberGenerator* underlying_rng, + Botan::Entropy_Sources* underlying_es, + size_t reseed_interval) override + { + if(underlying_rng && underlying_es) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_rng, *underlying_es, reseed_interval)); + else if(underlying_rng) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_rng, reseed_interval)); + else if(underlying_es) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG(*underlying_es, reseed_interval)); + else if(reseed_interval == 0) + return std::unique_ptr<Botan::Stateful_RNG>(new Botan::ChaCha_RNG()); + else + throw Test_Error("Invalid reseed interval in ChaCha_RNG unit test"); + } + + Test::Result test_security_level() override + { + Test::Result result("ChaCha_RNG Security Level"); + Botan::ChaCha_RNG rng; + result.test_eq("Expected security level", rng.security_level(), size_t(256)); + return result; + } + + Test::Result test_max_number_of_bytes_per_request() override + { + Test::Result result("ChaCha_RNG max_number_of_bytes_per_request"); + // ChaCha_RNG doesn't have this notion + return result; + } + + Test::Result test_reseed_kat() override { - const std::vector<uint8_t> seed_input = get_req_bin(vars, "EntropyInput"); - const std::vector<uint8_t> reseed_input = get_req_bin(vars, "EntropyInputReseed"); - const std::vector<uint8_t> expected = get_req_bin(vars, "Out"); + Test::Result result("ChaCha_RNG Reseed KAT"); - const std::vector<uint8_t> ad1 = get_opt_bin(vars, "AdditionalInput1"); - const std::vector<uint8_t> ad2 = get_opt_bin(vars, "AdditionalInput2"); + Request_Counting_RNG counting_rng; + std::unique_ptr<Botan::Stateful_RNG> rng = make_rng(counting_rng, 2); - Test::Result result("ChaCha_RNG"); + const Botan::secure_vector<uint8_t> seed_input(32); - Botan::ChaCha_RNG rng; - rng.initialize_with(seed_input.data(), seed_input.size()); + result.test_eq("is_seeded", rng->is_seeded(), false); - // now reseed - rng.add_entropy(reseed_input.data(), reseed_input.size()); + rng->initialize_with(seed_input.data(), seed_input.size()); + + Botan::secure_vector<uint8_t> out(32); + + 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, "1F0E6F13429D5073B59C057C37CBE9587740A0A894D247E2596C393CE91DDC6F"); - std::vector<uint8_t> 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()); + // 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_eq("out after reseed", out, "F2CAE73F22684D5D773290B48FDCDA0E6C0661EBA0A854AFEC922832BDBB9C49"); - result.test_eq("rng", out, expected); return result; } }; -BOTAN_REGISTER_TEST("chacha_rng", ChaCha_RNG_Tests); +BOTAN_REGISTER_TEST("chacha_rng_unit", ChaCha_RNG_Unit_Tests); #endif @@ -729,12 +676,9 @@ class AutoSeeded_RNG_Tests : public Test std::vector<Test::Result> run() override { std::vector<Test::Result> results; - results.push_back(auto_rng_tests()); - return results; } - }; BOTAN_REGISTER_TEST("auto_rng_unit", AutoSeeded_RNG_Tests); |