diff options
Diffstat (limited to 'src')
38 files changed, 1147 insertions, 424 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index 01527b522..e943973a0 100644 --- a/src/build-data/buildh.in +++ b/src/build-data/buildh.in @@ -98,11 +98,10 @@ #define BOTAN_PRIVATE_KEY_STRONG_CHECKS_ON_GENERATE 1 /* -* RNGs will automatically poll the system for additional seed material -* after producing this many bytes of output. Set to zero to disable -* automatic reseeding. +* Userspace RNGs like HMAC_DRBG will reseed after a specified number +* of outputs are generated. Set to zero to disable automatic reseeding. */ -#define BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED 16384 +#define BOTAN_RNG_DEFAULT_RESEED_INTERVAL 1024 #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) @@ -111,8 +110,7 @@ * 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 +#define BOTAN_AUTO_RNG_HMAC "HMAC(SHA-384)" /* * 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 7eb092292..7f2b09610 100644 --- a/src/build-data/policy/bsi.txt +++ b/src/build-data/policy/bsi.txt @@ -41,6 +41,7 @@ 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 30b7fbfd8..a98ec5077 100644 --- a/src/build-data/policy/modern.txt +++ b/src/build-data/policy/modern.txt @@ -38,7 +38,7 @@ eme_oaep emsa_pssr emsa1 -hmac_rng +auto_rng hmac_drbg ffi diff --git a/src/cli/cli.h b/src/cli/cli.h index 11cc8add7..7e2d49f0f 100644 --- a/src/cli/cli.h +++ b/src/cli/cli.h @@ -10,7 +10,10 @@ #include <botan/build.h> #include <botan/parsing.h> #include <botan/rng.h> -#include <botan/auto_rng.h> + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif #if defined(BOTAN_HAS_SYSTEM_RNG) #include <botan/system_rng.h> @@ -471,7 +474,9 @@ class Command if(rng_type == "auto") { +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) m_rng.reset(new Botan::AutoSeeded_RNG); +#endif } if(!m_rng) diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp index 1299b0d19..222a98d3f 100644 --- a/src/cli/speed.cpp +++ b/src/cli/speed.cpp @@ -19,9 +19,12 @@ #include <botan/hash.h> #include <botan/mac.h> #include <botan/cipher_mode.h> -#include <botan/auto_rng.h> #include <botan/entropy_src.h> +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif + #if defined(BOTAN_HAS_SYSTEM_RNG) #include <botan/system_rng.h> #endif @@ -413,8 +416,10 @@ class Speed final : public Command #endif else if(algo == "RNG") { +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) Botan::AutoSeeded_RNG auto_rng; bench_rng(auto_rng, "AutoSeeded_RNG (periodic reseed)", msec, buf_size); +#endif #if defined(BOTAN_HAS_SYSTEM_RNG) bench_rng(Botan::system_rng(), "System_RNG", msec, buf_size); @@ -428,7 +433,7 @@ class Speed final : public Command #if defined(BOTAN_HAS_HMAC_DRBG) for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" }) { - Botan::HMAC_DRBG hmac_drbg(hash, 0); + Botan::HMAC_DRBG hmac_drbg(hash); bench_rng(hmac_drbg, hmac_drbg.name(), msec, buf_size); } #endif @@ -436,7 +441,7 @@ class Speed final : public Command #if defined(BOTAN_HAS_HMAC_RNG) for(std::string hash : { "SHA-256", "SHA-384", "SHA-512" }) { - Botan::HMAC_RNG hmac_rng(hash, 0); + Botan::HMAC_RNG hmac_rng(Botan::MessageAuthenticationCode::create("HMAC(" + hash + ")")); bench_rng(hmac_rng, hmac_rng.name(), msec, buf_size); } #endif @@ -595,8 +600,9 @@ class Speed final : public Command { Botan::secure_vector<uint8_t> buffer(buf_size); - rng.add_entropy(buffer.data(), buffer.size()); - rng.reseed(256); +#if defined(BOTAN_HAS_SYSTEM_RNG) + rng.reseed_from_rng(Botan::system_rng(), 256); +#endif Timer timer(rng_name, "", "generate", buffer.size()); timer.run_until_elapsed(runtime, [&] { rng.randomize(buffer.data(), buffer.size()); }); diff --git a/src/cli/utils.cpp b/src/cli/utils.cpp index b0d364581..610a14dc1 100644 --- a/src/cli/utils.cpp +++ b/src/cli/utils.cpp @@ -7,7 +7,6 @@ #include "cli.h" #include <botan/version.h> -#include <botan/auto_rng.h> #include <botan/hash.h> #include <botan/cpuid.h> #include <botan/hex.h> @@ -16,6 +15,10 @@ #include <botan/base64.h> #endif +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif + #if defined(BOTAN_HAS_SYSTEM_RNG) #include <botan/system_rng.h> #endif @@ -179,7 +182,12 @@ class RNG final : public Command } else { +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) rng.reset(new Botan::AutoSeeded_RNG); +#else + error_output() << "auto_rng disabled in build\n"; + return; +#endif } for(const std::string& req : get_arg_list("bytes")) diff --git a/src/lib/entropy/entropy_src.h b/src/lib/entropy/entropy_src.h index 64d988e7c..94c67a18e 100644 --- a/src/lib/entropy/entropy_src.h +++ b/src/lib/entropy/entropy_src.h @@ -8,12 +8,17 @@ #ifndef BOTAN_ENTROPY_H__ #define BOTAN_ENTROPY_H__ +#include <botan/secmem.h> #include <botan/rng.h> #include <string> #include <chrono> +#include <memory> +#include <vector> namespace Botan { +class RandomNumberGenerator; + /** * Abstract interface to a source of entropy */ diff --git a/src/lib/ffi/ffi.cpp b/src/lib/ffi/ffi.cpp index 028b286b6..e42f32234 100644 --- a/src/lib/ffi/ffi.cpp +++ b/src/lib/ffi/ffi.cpp @@ -287,7 +287,7 @@ int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len) int botan_rng_reseed(botan_rng_t rng, size_t bits) { - return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed(bits); }); + return BOTAN_FFI_DO(Botan::RandomNumberGenerator, rng, r, { r.reseed_from_rng(Botan::system_rng(), bits); }); } int botan_hash_init(botan_hash_t* hash, const char* hash_name, uint32_t flags) diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.h b/src/lib/prov/pkcs11/p11_randomgenerator.h index 84673278d..a291c89f3 100644 --- a/src/lib/prov/pkcs11/p11_randomgenerator.h +++ b/src/lib/prov/pkcs11/p11_randomgenerator.h @@ -22,7 +22,7 @@ namespace PKCS11 { class Module; /// A random generator that only fetches random from the PKCS#11 RNG -class BOTAN_DLL PKCS11_RNG final : public RandomNumberGenerator +class BOTAN_DLL PKCS11_RNG final : public Hardware_RNG { public: /// Initialize the RNG with the PKCS#11 session that provides access to the cryptoki functions @@ -43,7 +43,7 @@ class BOTAN_DLL PKCS11_RNG final : public RandomNumberGenerator } /// No operation - always returns 0 - size_t reseed_with_sources(Entropy_Sources&, size_t, std::chrono::milliseconds) override + size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override { return 0; } diff --git a/src/lib/pubkey/rfc6979/rfc6979.cpp b/src/lib/pubkey/rfc6979/rfc6979.cpp index 1173eefee..94b313c3a 100644 --- a/src/lib/pubkey/rfc6979/rfc6979.cpp +++ b/src/lib/pubkey/rfc6979/rfc6979.cpp @@ -17,10 +17,10 @@ RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(const std::string& hash, m_order(order), m_qlen(m_order.bits()), m_rlen(m_qlen / 8 + (m_qlen % 8 ? 1 : 0)), - m_hmac_drbg(new HMAC_DRBG(hash, 0)), m_rng_in(m_rlen * 2), m_rng_out(m_rlen) { + m_hmac_drbg.reset(new HMAC_DRBG(MessageAuthenticationCode::create("HMAC(" + hash + ")"))); BigInt::encode_1363(m_rng_in.data(), m_rlen, x); } diff --git a/src/lib/rng/auto_rng.h b/src/lib/rng/auto_rng.h deleted file mode 100644 index 3085623ef..000000000 --- a/src/lib/rng/auto_rng.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -* Auto Seeded RNG -* (C) 2008 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_AUTO_SEEDING_RNG_H__ -#define BOTAN_AUTO_SEEDING_RNG_H__ - -#include <botan/rng.h> - -namespace Botan { - -class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator - { - public: - 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(); m_counter = 0; } - - std::string name() const override { return m_rng->name(); } - - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override - { - return m_rng->reseed_with_sources(srcs, poll_bits, poll_timeout); - } - - void add_entropy(const byte in[], size_t len) override - { m_rng->add_entropy(in, len); } - - AutoSeeded_RNG(size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); - private: - std::unique_ptr<RandomNumberGenerator> m_rng; - uint32_t m_counter = 0; - }; - -} - -#endif diff --git a/src/lib/rng/auto_rng/auto_rng.cpp b/src/lib/rng/auto_rng/auto_rng.cpp new file mode 100644 index 000000000..a9da085bc --- /dev/null +++ b/src/lib/rng/auto_rng/auto_rng.cpp @@ -0,0 +1,116 @@ +/* +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/auto_rng.h> +#include <botan/entropy_src.h> + +#if defined(BOTAN_HAS_HMAC_DRBG) + #include <botan/hmac_drbg.h> +#endif + +#if defined(BOTAN_HAS_HMAC_RNG) + #include <botan/hmac_rng.h> +#endif + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include <botan/system_rng.h> +#endif + +namespace Botan { + +AutoSeeded_RNG::~AutoSeeded_RNG() + { + // for unique_ptr + } + +AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval) + { + m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC), + underlying_rng, + reseed_interval)); + force_reseed(); + } + +AutoSeeded_RNG::AutoSeeded_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval) + { + m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC), + entropy_sources, + reseed_interval)); + force_reseed(); + } + +AutoSeeded_RNG::AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval) + { + m_rng.reset(new BOTAN_AUTO_RNG_DRBG(MessageAuthenticationCode::create(BOTAN_AUTO_RNG_HMAC), + underlying_rng, + entropy_sources, + reseed_interval)); + force_reseed(); + } + +AutoSeeded_RNG::AutoSeeded_RNG(size_t reseed_interval) : +#if defined(BOTAN_HAS_SYSTEM_RNG) + AutoSeeded_RNG(system_rng(), reseed_interval) +#else + AutoSeeded_RNG(Entropy_Sources::global_sources(), reseed_interval) +#endif + { + } + +void AutoSeeded_RNG::force_reseed() + { + m_rng->force_reseed(); + m_rng->next_byte(); + + if(!m_rng->is_seeded()) + { + throw Exception("AutoSeeded_RNG reseeding failed"); + } + } + +bool AutoSeeded_RNG::is_seeded() const + { + return m_rng->is_seeded(); + } + +void AutoSeeded_RNG::clear() + { + m_rng->clear(); + } + +std::string AutoSeeded_RNG::name() const + { + return m_rng->name(); + } + +void AutoSeeded_RNG::add_entropy(const byte in[], size_t len) + { + m_rng->add_entropy(in, len); + } + +size_t AutoSeeded_RNG::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + return m_rng->reseed(srcs, poll_bits, poll_timeout); + } + +void AutoSeeded_RNG::randomize(byte output[], size_t output_len) + { + randomize_with_ts_input(output, output_len); + } + +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/auto_rng/auto_rng.h b/src/lib/rng/auto_rng/auto_rng.h new file mode 100644 index 000000000..6ef1aa291 --- /dev/null +++ b/src/lib/rng/auto_rng/auto_rng.h @@ -0,0 +1,67 @@ +/* +* Auto Seeded RNG +* (C) 2008,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_AUTO_SEEDING_RNG_H__ +#define BOTAN_AUTO_SEEDING_RNG_H__ + +#include <botan/rng.h> + +namespace Botan { + +class Stateful_RNG; + +/** +* A userspace PRNG +*/ +class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator + { + public: + 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; + + void force_reseed(); + + size_t reseed(Entropy_Sources& srcs, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, + std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override; + + void add_entropy(const byte in[], size_t len) override; + + std::string name() const override; + + void clear() override; + + /** + * If no RNG or entropy sources are provided to AutoSeeded_RNG, it uses the system RNG + * (if available) or else a default group of entropy sources (all other systems) to + * gather seed material. + */ + AutoSeeded_RNG(size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + AutoSeeded_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + ~AutoSeeded_RNG(); + + private: + std::unique_ptr<Stateful_RNG> m_rng; + }; + +} + +#endif diff --git a/src/lib/rng/auto_rng/info.txt b/src/lib/rng/auto_rng/info.txt new file mode 100644 index 000000000..b77e6aa54 --- /dev/null +++ b/src/lib/rng/auto_rng/info.txt @@ -0,0 +1 @@ +define AUTO_SEEDING_RNG 20160821 diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index 7325804e3..6ea66aa2e 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -10,28 +10,42 @@ namespace Botan { -HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* hmac, - size_t max_output_before_reseed) : - Stateful_RNG(max_output_before_reseed), - m_mac(hmac) +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, reseed_interval), + m_mac(std::move(prf)) { - m_V.resize(m_mac->output_length()); + BOTAN_ASSERT_NONNULL(m_mac); clear(); } -HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash, - size_t max_output_before_reseed) : - Stateful_RNG(max_output_before_reseed) +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, entropy_sources, reseed_interval), + m_mac(std::move(prf)) { - const std::string hmac = "HMAC(" + hmac_hash + ")"; + BOTAN_ASSERT_NONNULL(m_mac); + clear(); + } - m_mac = MessageAuthenticationCode::create(hmac); - if(!m_mac) - { - throw Algorithm_Not_Found(hmac); - } +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(entropy_sources, reseed_interval), + m_mac(std::move(prf)) + { + BOTAN_ASSERT_NONNULL(m_mac); + clear(); + } - m_V.resize(m_mac->output_length()); +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) : + Stateful_RNG(), + m_mac(std::move(prf)) + { + BOTAN_ASSERT_NONNULL(m_mac); clear(); } @@ -39,6 +53,7 @@ void HMAC_DRBG::clear() { Stateful_RNG::clear(); + m_V.resize(m_mac->output_length()); 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)); @@ -61,25 +76,45 @@ 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) { - reseed_check(output_len); - - if(input_len > 0) + /** + * 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) { - update(input, input_len); - } + size_t this_req = std::min(max_number_of_bytes_per_request, output_len); + output_len -= this_req; - while(output_len) - { - const size_t to_copy = std::min(output_len, m_V.size()); - m_mac->update(m_V.data(), m_V.size()); - m_mac->final(m_V.data()); - copy_mem(output, m_V.data(), to_copy); + reseed_check(); + + if(input_len > 0) + { + update(input, input_len); + } + + while(this_req) + { + const size_t to_copy = std::min(this_req, m_V.size()); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); + copy_mem(output, m_V.data(), to_copy); - output += to_copy; - output_len -= to_copy; + output += to_copy; + this_req -= to_copy; + } + + update(input, input_len); } - update(input, input_len); } /* @@ -113,4 +148,10 @@ void HMAC_DRBG::add_entropy(const byte input[], size_t input_len) update(input, input_len); } +size_t HMAC_DRBG::security_level() const + { + // sqrt of hash size + return m_mac->output_length() * 8 / 2; + } + } diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index 0e294dbdb..4f96af816 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -8,11 +8,13 @@ #ifndef BOTAN_HMAC_DRBG_H__ #define BOTAN_HMAC_DRBG_H__ -#include <botan/rng.h> +#include <botan/stateful_rng.h> #include <botan/mac.h> namespace Botan { +class Entropy_Sources; + /** * HMAC_DRBG from NIST SP800-90A */ @@ -20,13 +22,64 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG { public: /** - * Initialize an HMAC_DRBG instance with the given hash function + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * Automatic reseeding is disabled completely, as it as no access to + * any source for seed material. + * + * If a fork is detected, the RNG will be unable to reseed itself + * in response. In this case, an exception will be thrown rather + * than generating duplicated output. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf); + + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * @param underlying_rng is a reference to some RNG which will be used + * 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. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * @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. */ - HMAC_DRBG(const std::string& hmac_hash, - size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); - HMAC_DRBG(MessageAuthenticationCode* hmac, - size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * @param underlying_rng is a reference to some RNG which will be used + * to perform the periodic reseeding + * @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. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Constructor taking a string for the hash + */ + HMAC_DRBG(const std::string& hmac_hash) : Stateful_RNG() + { + m_mac = MessageAuthenticationCode::create("HMAC(" + hmac_hash + ")"); + if(!m_mac) + throw Algorithm_Not_Found(hmac_hash); + clear(); + } std::string name() const override; @@ -38,6 +91,9 @@ class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG const byte input[], size_t input_len) override; void add_entropy(const byte input[], size_t input_len) override; + + size_t security_level() const override; + private: void update(const byte input[], size_t input_len); diff --git a/src/lib/rng/hmac_drbg/info.txt b/src/lib/rng/hmac_drbg/info.txt index f386db199..7f2c12fd0 100644 --- a/src/lib/rng/hmac_drbg/info.txt +++ b/src/lib/rng/hmac_drbg/info.txt @@ -2,4 +2,5 @@ define HMAC_DRBG 20140319 <requires> hmac +stateful_rng </requires> diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp index c100cf70f..d66c538ab 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.cpp +++ b/src/lib/rng/hmac_rng/hmac_rng.cpp @@ -12,43 +12,71 @@ namespace Botan { -HMAC_RNG::HMAC_RNG(const std::string& hash, size_t max_output_before_reseed) : - Stateful_RNG(max_output_before_reseed) +HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, reseed_interval), + m_prf(std::move(prf)) { - m_extractor = MAC::create("HMAC(" + hash + ")"); - if(!m_extractor) - throw Invalid_Argument("HMAC_RNG hash not found"); + BOTAN_ASSERT_NONNULL(m_prf); - m_prf.reset(m_extractor->clone()); + if(!m_prf->valid_keylength(m_prf->output_length())) + { + throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name()); + } + + m_extractor.reset(m_prf->clone()); + this->clear(); + } - if(!m_prf->valid_keylength(m_extractor->output_length()) || - !m_extractor->valid_keylength(m_prf->output_length())) +HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, reseed_interval), + m_prf(std::move(prf)) + { + BOTAN_ASSERT_NONNULL(m_prf); + + if(!m_prf->valid_keylength(m_prf->output_length())) { - throw Invalid_Argument("HMAC_RNG: Bad algo combination " + - m_extractor->name() + " and " + - m_prf->name()); + throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name()); } + m_extractor.reset(m_prf->clone()); this->clear(); } -/* -* HMAC_RNG Constructor -*/ -HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor, - MessageAuthenticationCode* prf, - size_t max_output_before_reseed) : - Stateful_RNG(max_output_before_reseed), - m_extractor(extractor), m_prf(prf) +HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(entropy_sources, reseed_interval), + m_prf(std::move(prf)), + m_extractor(m_prf->clone()) { - if(!m_prf->valid_keylength(m_extractor->output_length()) || - !m_extractor->valid_keylength(m_prf->output_length())) + BOTAN_ASSERT_NONNULL(m_prf); + + if(!m_prf->valid_keylength(m_prf->output_length())) + { + throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name()); + } + + m_extractor.reset(m_prf->clone()); + this->clear(); + } + +HMAC_RNG::HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf) : + Stateful_RNG(), + m_prf(std::move(prf)) + { + BOTAN_ASSERT_NONNULL(m_prf); + + if(!m_prf->valid_keylength(m_prf->output_length())) { - throw Invalid_Argument("HMAC_RNG: Bad algo combination " + - m_extractor->name() + " and " + - m_prf->name()); + throw Invalid_Argument("HMAC_RNG cannot use " + m_prf->name()); } + m_extractor.reset(m_prf->clone()); this->clear(); } @@ -105,7 +133,7 @@ void HMAC_RNG::new_K_value(byte label) */ void HMAC_RNG::randomize(byte out[], size_t length) { - reseed_check(length); + reseed_check(); while(length) { @@ -121,9 +149,9 @@ void HMAC_RNG::randomize(byte out[], size_t length) new_K_value(BlockFinished); } -size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds timeout) +size_t HMAC_RNG::reseed(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 @@ -131,7 +159,7 @@ size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs, /* * This ends up calling add_entropy which provides input to the extractor */ - size_t bits_collected = Stateful_RNG::reseed_with_sources(srcs, poll_bits, timeout); + size_t bits_collected = Stateful_RNG::reseed(srcs, poll_bits, timeout); /* Now derive the new PRK using everything that has been fed into diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h index a2538a83a..d6e9b4896 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.h +++ b/src/lib/rng/hmac_rng/hmac_rng.h @@ -1,6 +1,6 @@ /* * HMAC RNG -* (C) 2008,2013 Jack Lloyd +* (C) 2008,2013,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,9 +8,8 @@ #ifndef BOTAN_HMAC_RNG_H__ #define BOTAN_HMAC_RNG_H__ +#include <botan/stateful_rng.h> #include <botan/mac.h> -#include <botan/rng.h> -#include <vector> namespace Botan { @@ -19,40 +18,69 @@ namespace Botan { * Key Derivation Functions and an HMAC-based KDF" by Hugo Krawczyk * (henceforce, 'E-t-E') * -* However it actually can be parameterized with any two MAC functions, +* However it actually could be parameterized with any two MAC functions, * not restricted to HMAC (this variation is also described in * 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 Stateful_RNG +class BOTAN_DLL HMAC_RNG final : public Stateful_RNG { public: + /** + * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC) + * @param underlying_rng is a reference to some RNG which will be used + * to perform the periodic reseeding. + * @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. + */ + HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC) + * @param underlying_rng is a reference to some RNG which will be used + * 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. + */ + HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /* + * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC) + * @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. + */ + HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Initialize an HMAC_RNG instance with the given MAC as PRF (normally HMAC) + * Automatic reseeding is disabled completely. + */ + HMAC_RNG(std::unique_ptr<MessageAuthenticationCode> prf); + void randomize(byte buf[], size_t len) override; void clear() override; std::string name() const override; - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override; + size_t reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; void add_entropy(const byte[], size_t) override; - /** - * @param extractor a MAC used for extracting the entropy - * @param prf a MAC used as a PRF using HKDF construction - */ - HMAC_RNG(MessageAuthenticationCode* extractor, - MessageAuthenticationCode* prf, - size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); + size_t security_level() const override { return m_prf->output_length() * 8 / 2; } - /** - * Use the specified hash for both the extractor and PRF functions - */ - HMAC_RNG(const std::string& hash, - size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); private: - std::unique_ptr<MessageAuthenticationCode> m_extractor; std::unique_ptr<MessageAuthenticationCode> m_prf; + std::unique_ptr<MessageAuthenticationCode> m_extractor; enum HMAC_PRF_Label { Running, diff --git a/src/lib/rng/hmac_rng/info.txt b/src/lib/rng/hmac_rng/info.txt index 36a8a7a34..2b7f49c8a 100644 --- a/src/lib/rng/hmac_rng/info.txt +++ b/src/lib/rng/hmac_rng/info.txt @@ -2,4 +2,5 @@ define HMAC_RNG 20131128 <requires> mac +stateful_rng </requires> diff --git a/src/lib/rng/info.txt b/src/lib/rng/info.txt index 84ba3ce89..655e35fd1 100644 --- a/src/lib/rng/info.txt +++ b/src/lib/rng/info.txt @@ -1,5 +1,3 @@ -define AUTO_SEEDING_RNG 20131128 - <requires> entropy hmac_drbg diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.h b/src/lib/rng/rdrand_rng/rdrand_rng.h index d0fb37c16..fcd54035b 100644 --- a/src/lib/rng/rdrand_rng/rdrand_rng.h +++ b/src/lib/rng/rdrand_rng/rdrand_rng.h @@ -45,8 +45,7 @@ class BOTAN_DLL RDRAND_RNG : public Hardware_RNG void add_entropy(const uint8_t[], size_t) override { /* no op */ } - size_t reseed_with_sources(Entropy_Sources&, size_t, - std::chrono::milliseconds) override + size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override { return 0; /* no op */ } std::string name() const override { return "RDRAND"; } diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index 5501c143e..8c2982312 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -1,144 +1,62 @@ /* -* Random Number Generator -* (C) 1999-2008,2016 Jack Lloyd +* (C) 2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include <botan/rng.h> -#include <botan/auto_rng.h> -#include <botan/entropy_src.h> #include <botan/loadstor.h> #include <botan/internal/os_utils.h> -#if defined(BOTAN_HAS_HMAC_DRBG) - #include <botan/hmac_drbg.h> -#endif - -#if defined(BOTAN_HAS_HMAC_RNG) - #include <botan/hmac_rng.h> +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> #endif namespace Botan { -size_t RandomNumberGenerator::reseed(size_t bits_to_collect) +void RandomNumberGenerator::randomize_with_ts_input(byte output[], size_t output_len) { - return this->reseed_with_timeout(bits_to_collect, - BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); - } - -size_t RandomNumberGenerator::reseed_with_timeout(size_t bits_to_collect, - std::chrono::milliseconds timeout) - { - return this->reseed_with_sources(Entropy_Sources::global_sources(), - bits_to_collect, - timeout); - } - -size_t RandomNumberGenerator::reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) - { - return srcs.poll(*this, poll_bits, poll_timeout); - } - -Stateful_RNG::Stateful_RNG(size_t max_output_before_reseed) : m_max_output_before_reseed(max_output_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, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) - { - size_t bits_collected = RandomNumberGenerator::reseed_with_sources(srcs, poll_bits, poll_timeout); - - if(bits_collected >= poll_bits) - { - m_successful_initialization = true; - m_bytes_since_reseed = 0; - } + /* + Form additional input which is provided to the PRNG implementation + to paramaterize the KDF output. + */ + byte additional_input[16] = { 0 }; + store_le(OS::get_system_timestamp_ns(), additional_input); + store_le(OS::get_processor_timestamp(), additional_input + 8); - return bits_collected; + randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); } -void Stateful_RNG::reseed_check(size_t bytes_requested) +void RandomNumberGenerator::randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) { - const bool fork_detected = (m_last_pid > 0) && (OS::get_process_id() != m_last_pid); - - m_bytes_since_reseed += bytes_requested; - m_last_pid = OS::get_process_id(); - - if(!is_seeded() || fork_detected) - { - this->reseed(BOTAN_RNG_RESEED_POLL_BITS); - } - else if(m_max_output_before_reseed > 0 && m_bytes_since_reseed >= m_max_output_before_reseed) - { - this->reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS, - BOTAN_RNG_AUTO_RESEED_TIMEOUT); - } - - if(!is_seeded()) - { - throw PRNG_Unseeded(name()); - } + this->add_entropy(input, input_len); + this->randomize(output, output_len); } -void Stateful_RNG::initialize_with(const byte input[], size_t len) +size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) { - add_entropy(input, len); - m_successful_initialization = true; + return srcs.poll(*this, poll_bits, poll_timeout); } -bool Stateful_RNG::is_seeded() const +void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) { - return m_successful_initialization; + secure_vector<byte> buf(poll_bits / 8); + rng.randomize(buf.data(), buf.size()); + this->add_entropy(buf.data(), buf.size()); } RandomNumberGenerator* RandomNumberGenerator::make_rng() { +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) return new AutoSeeded_RNG; +#else + throw Exception("make_rng failed, no AutoSeeded_RNG in this build"); +#endif } -AutoSeeded_RNG::AutoSeeded_RNG(size_t max_output_before_reseed) - { - m_rng.reset(new BOTAN_AUTO_RNG_DRBG(BOTAN_AUTO_RNG_HASH, max_output_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 " + - std::to_string(bits) + " bits"); - } - } - -void AutoSeeded_RNG::randomize(byte output[], size_t output_len) - { - /* - Form additional input which is provided to the PRNG implementation - to paramaterize the KDF output. - */ - 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); - - randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); - } - -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); - } +Serialized_RNG::Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} } diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index 7da560b85..d1cdcfff2 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -8,6 +8,7 @@ #ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H__ #define BOTAN_RANDOM_NUMBER_GENERATOR_H__ +#include <botan/entropy_src.h> #include <botan/secmem.h> #include <botan/exceptn.h> #include <chrono> @@ -19,7 +20,7 @@ namespace Botan { class Entropy_Sources; /** -* An interface to a generic RNG +* An interface to a cryptographic random number generator */ class BOTAN_DLL RandomNumberGenerator { @@ -45,6 +46,8 @@ class BOTAN_DLL RandomNumberGenerator * Incorporate some additional data into the RNG state. For * example adding nonces or timestamps from a peer's protocol * message can help hedge against VM state rollback attacks. + * A few RNG types do not accept any externally provided input, + * in which case this function is a no-op. * * @param inputs a byte array containg the entropy to be added * @param length the length of the byte array in @@ -56,27 +59,41 @@ class BOTAN_DLL RandomNumberGenerator */ template<typename T> void add_entropy_T(const T& t) { - add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T)); + this->add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T)); } /** - * Incorporate entropy into the RNG state then produce output - * Some RNG types implement this using a single operation. + * Incorporate entropy into the RNG state then produce output. + * Some RNG types implement this using a single operation, default + * calls add_entropy + randomize in sequence. + * + * Use this to further bind the outputs to your current + * process/protocol state. For instance if generating a new key + * for use in a session, include a session ID or other such + * value. See NIST SP 800-90 A, B, C series for more ideas. */ virtual void randomize_with_input(byte output[], size_t output_len, - const byte input[], size_t input_len) - { - this->add_entropy(input, input_len); - this->randomize(output, output_len); - } + const byte input[], size_t input_len); + + /** + * This calls `randomize_with_input` using some timestamps as extra input. + * + * For a stateful RNG using non-random but potentially unique data as the + * additional_input can help protect against problems with fork, VM state + * rollback, or other cases where somehow an RNG state is duplicated. If + * both of the duplicated RNG states later incorporate a timestamp (and the + * timestamps don't themselves repeat), their outputs will diverge. + */ + virtual void randomize_with_ts_input(byte output[], size_t output_len); /** - * Return the name of this object + * Return the name of this RNG type */ virtual std::string name() const = 0; /** - * Clear all internally held values of this RNG. + * Clear all internally held values of this RNG + * @post is_seeded() == false */ virtual void clear() = 0; @@ -91,28 +108,17 @@ class BOTAN_DLL RandomNumberGenerator * or until the timeout expires. Returns estimate of the number * of bits collected. */ - virtual size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout); + virtual size_t reseed(Entropy_Sources& srcs, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, + std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); /** - * Reseed this RNG from the default entropy sources and a default timeout - * @param bits_to_collect is the number of bits of entropy to - * attempt to gather from the entropy sources - * @param poll_timeout try not to run longer than this, even if - * not enough entropy has been collected + * Reseed by reading specified bits from the RNG */ - size_t reseed(size_t bits_to_collect = BOTAN_RNG_RESEED_POLL_BITS); + virtual void reseed_from_rng(RandomNumberGenerator& rng, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS); - /** - * Reseed this RNG from the default entropy sources - * @param bits_to_collect is the number of bits of entropy to - * attempt to gather from the entropy sources - * @param poll_timeout try not to run longer than this, even if - * not enough entropy has been collected - */ - size_t reseed_with_timeout(size_t bits_to_collect, - std::chrono::milliseconds poll_timeout); + // Some utility functions built on the interface above: /** * Return a random vector @@ -122,7 +128,7 @@ class BOTAN_DLL RandomNumberGenerator secure_vector<byte> random_vec(size_t bytes) { secure_vector<byte> output(bytes); - randomize(output.data(), output.size()); + this->randomize(output.data(), output.size()); return output; } @@ -139,9 +145,9 @@ class BOTAN_DLL RandomNumberGenerator byte next_nonzero_byte() { - byte b = next_byte(); + byte b = this->next_byte(); while(b == 0) - b = next_byte(); + b = this->next_byte(); return b; } @@ -150,65 +156,11 @@ class BOTAN_DLL RandomNumberGenerator * Added in 1.8.0 * Use AutoSeeded_RNG instead */ + BOTAN_DEPRECATED("Use AutoSeeded_RNG") static RandomNumberGenerator* make_rng(); }; /** -* Inherited by RNGs which maintain in-process state, like HMAC_DRBG. -* On Unix these RNGs are vulnerable to problems with fork, where the -* RNG state is duplicated, and the parent and child process RNGs will -* produce identical output until one of them reseeds. Stateful_RNG -* reseeds itself whenever a fork is detected, or after a set number of -* bytes have been output. -* -* Not implemented by RNGs which access an external RNG, such as the -* system PRNG or a hardware RNG. -*/ -class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator - { - public: - Stateful_RNG(size_t max_output_before_reseed); - - virtual bool is_seeded() const override final; - - /** - * Consume this input and mark the RNG as initialized regardless - * of the length of the input or the current seeded state of - * the RNG. - */ - void initialize_with(const byte input[], size_t length); - - /** - * Poll provided sources for up to poll_bits bits of entropy - * or until the timeout expires. Returns estimate of the number - * of bits collected. - */ - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override; - - 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_max_output_before_reseed; } - - uint32_t last_pid() const { return m_last_pid; } - - mutable std::mutex m_mutex; - - private: - const size_t m_max_output_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; @@ -271,12 +223,12 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator return m_rng->name(); } - size_t reseed_with_sources(Entropy_Sources& src, - size_t bits, - std::chrono::milliseconds msec) override + size_t reseed(Entropy_Sources& src, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, + std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override { std::lock_guard<std::mutex> lock(m_mutex); - return m_rng->reseed_with_sources(src, bits, msec); + return m_rng->reseed(src, poll_bits, poll_timeout); } void add_entropy(const byte in[], size_t len) override @@ -285,7 +237,8 @@ class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator m_rng->add_entropy(in, len); } - Serialized_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} + BOTAN_DEPRECATED("Create an AutoSeeded_RNG for other constructor") Serialized_RNG(); + explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {} private: mutable std::mutex m_mutex; diff --git a/src/lib/rng/stateful_rng/info.txt b/src/lib/rng/stateful_rng/info.txt new file mode 100644 index 000000000..b4dcedf4a --- /dev/null +++ b/src/lib/rng/stateful_rng/info.txt @@ -0,0 +1,2 @@ +define STATEFUL_RNG 20160819 + diff --git a/src/lib/rng/stateful_rng/stateful_rng.cpp b/src/lib/rng/stateful_rng/stateful_rng.cpp new file mode 100644 index 000000000..1349c1208 --- /dev/null +++ b/src/lib/rng/stateful_rng/stateful_rng.cpp @@ -0,0 +1,112 @@ +/* +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/stateful_rng.h> +#include <botan/internal/os_utils.h> +#include <botan/loadstor.h> + +namespace Botan { + +void Stateful_RNG::clear() + { + m_reseed_counter = 0; + m_last_pid = 0; + } + +void Stateful_RNG::force_reseed() + { + m_reseed_counter = 0; + } + +bool Stateful_RNG::is_seeded() const + { + return m_reseed_counter > 0; + } + +void Stateful_RNG::initialize_with(const byte input[], size_t len) + { + add_entropy(input, len); + + if(8*len >= security_level()) + { + m_reseed_counter = 1; + } + } + +void Stateful_RNG::randomize_with_ts_input(byte output[], size_t output_len) + { + 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(m_last_pid, additional_input + 16); + store_le(static_cast<uint32_t>(m_reseed_counter), additional_input + 20); + + randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); + } + +size_t Stateful_RNG::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout); + + if(bits_collected >= security_level()) + { + m_reseed_counter = 1; + } + + return bits_collected; + } + +void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) + { + RandomNumberGenerator::reseed_from_rng(rng, poll_bits); + + if(poll_bits >= security_level()) + { + m_reseed_counter = 1; + } + } + +void Stateful_RNG::reseed_check() + { + const uint32_t cur_pid = OS::get_process_id(); + + const bool fork_detected = (m_last_pid > 0) && (cur_pid != m_last_pid); + + if(is_seeded() == false || + fork_detected || + (m_reseed_interval > 0 && m_reseed_counter >= m_reseed_interval)) + { + m_reseed_counter = 0; + m_last_pid = cur_pid; + + if(m_underlying_rng) + { + reseed_from_rng(*m_underlying_rng, security_level()); + } + + if(m_entropy_sources) + { + reseed(*m_entropy_sources, security_level()); + } + + if(!is_seeded()) + { + if(fork_detected) + throw Exception("Detected use of fork but cannot reseed DRBG"); + else + throw PRNG_Unseeded(name()); + } + } + else + { + BOTAN_ASSERT(m_reseed_counter != 0, "RNG is seeded"); + m_reseed_counter += 1; + } + } + +} diff --git a/src/lib/rng/stateful_rng/stateful_rng.h b/src/lib/rng/stateful_rng/stateful_rng.h new file mode 100644 index 000000000..11f0c7e3d --- /dev/null +++ b/src/lib/rng/stateful_rng/stateful_rng.h @@ -0,0 +1,118 @@ +/* +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_STATEFUL_RNG_H__ +#define BOTAN_STATEFUL_RNG_H__ + +#include <botan/rng.h> + +namespace Botan { + +/** +* Inherited by RNGs which maintain in-process state, like HMAC_DRBG. +* On Unix these RNGs are vulnerable to problems with fork, where the +* RNG state is duplicated, and the parent and child process RNGs will +* produce identical output until one of them reseeds. Stateful_RNG +* reseeds itself whenever a fork is detected, or after a set number of +* bytes have been output. +* +* Not implemented by RNGs which access an external RNG, such as the +* system PRNG or a hardware RNG. +*/ +class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator + { + public: + Stateful_RNG(RandomNumberGenerator& rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + m_underlying_rng(&rng), + m_entropy_sources(&entropy_sources), + m_reseed_interval(reseed_interval) + {} + + Stateful_RNG(RandomNumberGenerator& rng, size_t reseed_interval) : + m_underlying_rng(&rng), + m_reseed_interval(reseed_interval) + {} + + Stateful_RNG(Entropy_Sources& entropy_sources, size_t reseed_interval) : + m_entropy_sources(&entropy_sources), + m_reseed_interval(reseed_interval) + {} + + /** + * In this case, automatic reseeding is impossible + */ + Stateful_RNG() : m_reseed_interval(0) {} + + /** + * Consume this input and mark the RNG as initialized regardless + * of the length of the input or the current seeded state of + * the RNG. + */ + void initialize_with(const byte input[], size_t length); + + bool is_seeded() const override final; + + /** + * Mark state as requiring a reseed on next use + */ + void force_reseed(); + + void reseed_from_rng(RandomNumberGenerator& rng, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS) override final; + + /** + * Overrides default implementation and also includes the current + * process ID and the reseed counter. + */ + void randomize_with_ts_input(byte output[], size_t output_len) override final; + + /** + * Poll provided sources for up to poll_bits bits of entropy + * or until the timeout expires. Returns estimate of the number + * of bits collected. + */ + size_t reseed(Entropy_Sources& srcs, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS, + std::chrono::milliseconds poll_timeout = BOTAN_RNG_RESEED_DEFAULT_TIMEOUT) override; + + /** + * Return intended security level of this DRBG + */ + virtual size_t security_level() const = 0; + + void clear() override; + + protected: + /** + * Called with lock held + */ + void reseed_check(); + + uint32_t last_pid() const { return m_last_pid; } + + private: + // A non-owned and possibly null pointer to shared RNG + RandomNumberGenerator* m_underlying_rng = nullptr; + + // A non-owned and possibly null pointer to a shared Entropy_Source + Entropy_Sources* m_entropy_sources = nullptr; + + const size_t m_reseed_interval; + + /* + * Set to 1 after a sucessful seeding, then incremented. Reset + * to 0 by clear() or a fork. This logic is used even if + * automatic reseeding is disabled (via m_reseed_interval = 0) + */ + size_t m_reseed_counter = 0; + uint32_t m_last_pid = 0; + }; + +} + +#endif diff --git a/src/lib/rng/x931_rng/info.txt b/src/lib/rng/x931_rng/info.txt index b61dc7ec9..4a4418083 100644 --- a/src/lib/rng/x931_rng/info.txt +++ b/src/lib/rng/x931_rng/info.txt @@ -1 +1,5 @@ define X931_RNG 20131128 + +<requires> +stateful_rng +</requires> diff --git a/src/lib/rng/x931_rng/x931_rng.cpp b/src/lib/rng/x931_rng/x931_rng.cpp index 020d9a5a5..ed44dc743 100644 --- a/src/lib/rng/x931_rng/x931_rng.cpp +++ b/src/lib/rng/x931_rng/x931_rng.cpp @@ -14,7 +14,7 @@ void ANSI_X931_RNG::randomize(byte out[], size_t length) { if(!is_seeded()) { - reseed(BOTAN_RNG_RESEED_POLL_BITS); + rekey(); if(!is_seeded()) throw PRNG_Unseeded(name()); @@ -72,11 +72,11 @@ void ANSI_X931_RNG::rekey() } } -size_t ANSI_X931_RNG::reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) +size_t ANSI_X931_RNG::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) { - size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout); + size_t bits = m_prng->reseed(srcs, poll_bits, poll_timeout); rekey(); return bits; } diff --git a/src/lib/rng/x931_rng/x931_rng.h b/src/lib/rng/x931_rng/x931_rng.h index ed7124a08..861fcffde 100644 --- a/src/lib/rng/x931_rng/x931_rng.h +++ b/src/lib/rng/x931_rng/x931_rng.h @@ -16,7 +16,7 @@ namespace Botan { /** * ANSI X9.31 RNG */ -class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator +class BOTAN_DLL ANSI_X931_RNG final : public RandomNumberGenerator { public: void randomize(byte[], size_t) override; @@ -24,9 +24,9 @@ class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator void clear() override; std::string name() const override; - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override; + size_t reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; void add_entropy(const byte[], size_t) override; @@ -35,6 +35,7 @@ class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator * @param rng the underlying PRNG for generating inputs * (eg, an HMAC_RNG) */ + BOTAN_DEPRECATED("X9.31 RNG is deprecated and will be removed soon") ANSI_X931_RNG(BlockCipher* cipher, RandomNumberGenerator* rng); diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index 86776bdd0..c00c898a3 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -19,7 +19,7 @@ #include <unistd.h> #endif -#if defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS) +#if defined(BOTAN_TARGET_OS_IS_WINDOWS) #include <windows.h> #endif @@ -29,12 +29,12 @@ namespace OS { uint32_t get_process_id() { -#if defined(BOTAN_TARGET_OS_IS_UNIX) +#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) return ::getpid(); #elif defined(BOTAN_TARGET_OS_IS_WINDOWS) return ::GetCurrentProcessId(); #else - return 0; + throw Exception("get_process_id not supported"); #endif } diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index 3335463f7..590ed4ae7 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -15,7 +15,7 @@ namespace Botan { namespace OS { /** -* Returns the OS assigned process ID, if available. Otherwise returns 0. +* Returns the OS assigned process ID, if available. Otherwise throws. */ uint32_t get_process_id(); diff --git a/src/tests/main.cpp b/src/tests/main.cpp index 79be98a0e..07aaac519 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -15,8 +15,8 @@ #include <future> #include <botan/version.h> -#include <botan/auto_rng.h> #include <botan/loadstor.h> +#include <botan/hash.h> #if defined(BOTAN_HAS_HMAC_DRBG) #include <botan/hmac_drbg.h> @@ -26,6 +26,10 @@ #include <botan/system_rng.h> #endif +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif + namespace { class Test_Runner : public Botan_CLI::Command @@ -140,7 +144,14 @@ class Test_Runner : public Botan_CLI::Command } output() << " rng:HMAC_DRBG with seed '" << Botan::hex_encode(seed) << "'"; - std::unique_ptr<Botan::HMAC_DRBG> drbg(new Botan::HMAC_DRBG("SHA-384", 0)); + + // Expand out the seed to 512 bits to make the DRBG happy + std::unique_ptr<Botan::HashFunction> sha512(Botan::HashFunction::create("SHA-512")); + sha512->update(seed); + seed.resize(sha512->output_length()); + sha512->final(seed.data()); + + std::unique_ptr<Botan::HMAC_DRBG> drbg(new Botan::HMAC_DRBG("SHA-384")); drbg->initialize_with(seed.data(), seed.size()); rng.reset(new Botan::Serialized_RNG(drbg.release())); @@ -152,16 +163,19 @@ class Test_Runner : public Botan_CLI::Command #if defined(BOTAN_HAS_SYSTEM_RNG) output() << " rng:system"; rng.reset(new Botan::System_RNG); -#else - // AutoSeeded_RNG always available +#elif defined(BOTAN_HAS_AUTO_SEEDING_RNG) output() << " rng:autoseeded"; rng.reset(new Botan::Serialized_RNG(new Botan::AutoSeeded_RNG)); #endif #endif - output() << "\n"; + if(rng.get() == nullptr) + { + throw Botan_Tests::Test_Error("No usable RNG enabled in build, aborting tests"); + } + Botan_Tests::Test::setup_tests(soak_level, log_success, data_dir, pkcs11_lib, rng.get()); const size_t failed = run_tests(req, output(), threads); diff --git a/src/tests/test_mceliece.cpp b/src/tests/test_mceliece.cpp index 8658bf5e6..5e3501b3e 100644 --- a/src/tests/test_mceliece.cpp +++ b/src/tests/test_mceliece.cpp @@ -67,7 +67,7 @@ class McEliece_Keygen_Encrypt_Test : public Text_Based_Test const size_t keygen_n = get_req_sz(vars, "KeyN"); const size_t keygen_t = get_req_sz(vars, "KeyT"); - Botan::HMAC_DRBG rng("SHA-384", 0); + Botan::HMAC_DRBG rng("SHA-384"); rng.initialize_with(keygen_seed.data(), keygen_seed.size()); Botan::McEliece_PrivateKey mce_priv(rng, keygen_n, keygen_t); diff --git a/src/tests/test_pkcs11_high_level.cpp b/src/tests/test_pkcs11_high_level.cpp index 8e894ef28..e8c19224e 100644 --- a/src/tests/test_pkcs11_high_level.cpp +++ b/src/tests/test_pkcs11_high_level.cpp @@ -1346,18 +1346,12 @@ Test::Result test_pkcs11_hmac_drbg() Test::Result result("PKCS11 HMAC_DRBG using PKCS11_RNG"); TestSession test_session(true); - // FIXME: this is only a temporary fix - // It should be possible to instantiate HMAC_DRBG with PKCS11_RNG and - // HMAC_DRBG should reseed itself from PKCS11_RNG - HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)").release()); + PKCS11_RNG p11_rng(test_session.session()); + HMAC_DRBG drbg(MessageAuthenticationCode::create("HMAC(SHA-512)"), p11_rng); // result.test_success("HMAC_DRBG(HMAC(SHA512)) instantiated with PKCS11_RNG"); result.test_eq("HMAC_DRBG is not seeded yet.", drbg.is_seeded(), false); - - PKCS11_RNG p11_rng(test_session.session()); - secure_vector<byte> rnd = p11_rng.random_vec(64); - - drbg.initialize_with(rnd.data(), rnd.size()); + secure_vector<byte> rnd = drbg.random_vec(64); result.test_eq("HMAC_DRBG is seeded now", drbg.is_seeded(), true); std::string personalization_string = "Botan PKCS#11 Tests"; 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 diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h index 6c29b1d55..0379a2ee9 100644 --- a/src/tests/test_rng.h +++ b/src/tests/test_rng.h @@ -15,13 +15,6 @@ #include <botan/hex.h> #include <botan/exceptn.h> -#if defined(BOTAN_HAS_SYSTEM_RNG) - #include <botan/system_rng.h> -#else - #include <botan/auto_rng.h> -#endif - - namespace Botan_Tests { /** @@ -43,9 +36,9 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator return out; } - size_t reseed_with_sources(Botan::Entropy_Sources&, - size_t, - std::chrono::milliseconds) override { return 0; } + size_t reseed(Botan::Entropy_Sources&, + size_t, + std::chrono::milliseconds) override { return 0; } void randomize(uint8_t out[], size_t len) override { @@ -87,7 +80,7 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator class Fixed_Output_Position_RNG : public Fixed_Output_RNG { public: - bool is_seeded() const override { return !m_buf.empty() || m_rng->is_seeded(); } + bool is_seeded() const override { return !m_buf.empty() || Test::rng().is_seeded(); } uint8_t random() override { @@ -114,7 +107,7 @@ class Fixed_Output_Position_RNG : public Fixed_Output_RNG } else { // return random - m_rng->randomize(out,len); + Test::rng().randomize(out,len); } } @@ -127,32 +120,19 @@ class Fixed_Output_Position_RNG : public Fixed_Output_RNG explicit Fixed_Output_Position_RNG(const std::vector<uint8_t>& in, uint32_t pos) : Fixed_Output_RNG(in), - m_pos(pos), - m_rng{} + m_pos(pos) { -#if defined(BOTAN_HAS_SYSTEM_RNG) - m_rng.reset(new Botan::System_RNG); -#else - m_rng.reset(new Botan::AutoSeeded_RNG); -#endif } explicit Fixed_Output_Position_RNG(const std::string& in_str, uint32_t pos) : Fixed_Output_RNG(in_str), - m_pos(pos), - m_rng{} + m_pos(pos) { -#if defined(BOTAN_HAS_SYSTEM_RNG) - m_rng.reset(new Botan::System_RNG); -#else - m_rng.reset(new Botan::AutoSeeded_RNG); -#endif } private: uint32_t m_pos = 0; uint32_t m_requests = 0; - std::unique_ptr<RandomNumberGenerator> m_rng; }; class SeedCapturing_RNG : public Botan::RandomNumberGenerator diff --git a/src/tests/tests.cpp b/src/tests/tests.cpp index d3467460e..a6f96144c 100644 --- a/src/tests/tests.cpp +++ b/src/tests/tests.cpp @@ -127,7 +127,7 @@ bool Test::Result::test_ne(const std::string& what, const uint8_t expected[], size_t expected_len) { if(produced_len == expected_len && Botan::same_mem(produced, expected, expected_len)) - return test_failure(who() + ":" + what + " produced matching"); + return test_failure(who() + ": " + what + " produced matching"); return test_success(); } |