aboutsummaryrefslogtreecommitdiffstats
path: root/src/tests/test_rng.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/test_rng.cpp')
-rw-r--r--src/tests/test_rng.cpp337
1 files changed, 326 insertions, 11 deletions
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