From 4e78e6ea5608edddda8929f3b43a978e89f927ba Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Tue, 19 Jul 2016 14:57:12 -0400 Subject: Add RDRAND_RNG For those that are willing to trust uninspectible hardware. :) Changes RDRAND entropy source to call RDRAND_RNG Add --rdrand flag to rng cmdlet --- src/lib/rng/rdrand_rng/info.txt | 16 +++++++ src/lib/rng/rdrand_rng/rdrand_rng.cpp | 84 +++++++++++++++++++++++++++++++++++ src/lib/rng/rdrand_rng/rdrand_rng.h | 61 +++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 src/lib/rng/rdrand_rng/info.txt create mode 100644 src/lib/rng/rdrand_rng/rdrand_rng.cpp create mode 100644 src/lib/rng/rdrand_rng/rdrand_rng.h (limited to 'src/lib/rng/rdrand_rng') diff --git a/src/lib/rng/rdrand_rng/info.txt b/src/lib/rng/rdrand_rng/info.txt new file mode 100644 index 000000000..2e597ebec --- /dev/null +++ b/src/lib/rng/rdrand_rng/info.txt @@ -0,0 +1,16 @@ +define RDRAND_RNG 20160619 + +need_isa rdrand + + +x86_32 +x86_64 + + + +gcc +clang +icc +msvc + + diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.cpp b/src/lib/rng/rdrand_rng/rdrand_rng.cpp new file mode 100644 index 000000000..4d2e51cf8 --- /dev/null +++ b/src/lib/rng/rdrand_rng/rdrand_rng.cpp @@ -0,0 +1,84 @@ +/** +* RDRAND RNG +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#if !defined(BOTAN_USE_GCC_INLINE_ASM) + #include +#endif + +namespace Botan { + +RDRAND_RNG::RDRAND_RNG() + { + if(!CPUID::has_rdrand()) + throw Exception("Current CPU does not support RDRAND instruction"); + } + +//static +uint32_t RDRAND_RNG::rdrand() + { + bool ok = false; + uint32_t r = rdrand_status(ok); + + while(!ok) + { + r = rdrand_status(ok); + } + + return r; + } + +//static +uint32_t RDRAND_RNG::rdrand_status(bool& ok) + { + ok = false; + uint32_t r = 0; + + for(size_t i = 0; i != BOTAN_ENTROPY_RDRAND_RETRIES; ++i) + { +#if defined(BOTAN_USE_GCC_INLINE_ASM) + int cf = 0; + + // Encoding of rdrand %eax + asm(".byte 0x0F, 0xC7, 0xF0; adcl $0,%1" : + "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); +#else + int cf = _rdrand32_step(&r); +#endif + if(1 == cf) + { + ok = true; + return r; + } + } + + return 0; + } + +void RDRAND_RNG::randomize(uint8_t out[], size_t out_len) + { + while(out_len >= 4) + { + uint32_t r = RDRAND_RNG::rdrand(); + + store_le(r, out); + out += 4; + out_len -= 4; + } + + if(out_len) // between 1 and 3 trailing bytes + { + uint32_t r = RDRAND_RNG::rdrand(); + for(size_t i = 0; i != out_len; ++i) + out[i] = get_byte(i, r); + } + } + +} diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.h b/src/lib/rng/rdrand_rng/rdrand_rng.h new file mode 100644 index 000000000..b9445f28e --- /dev/null +++ b/src/lib/rng/rdrand_rng/rdrand_rng.h @@ -0,0 +1,61 @@ +/** +* RDRAND RNG +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RNG_RDRAND_H_ +#define BOTAN_RNG_RDRAND_H_ + +#include + +namespace Botan { + +class BOTAN_DLL RDRAND_RNG : public Hardware_RNG + { + public: + /** + * On correctly working hardware, RDRAND is always supposed to + * succeed within a set number of retries. If after that many + * retries RDRAND has still not suceeded, sets ok = false and + * returns 0. + */ + static uint32_t rdrand_status(bool& ok); + + /* + * Calls RDRAND until it succeeds, this could hypothetically + * loop forever on broken hardware. + */ + static uint32_t rdrand(); + + /** + * Constructor will throw if CPU does not have RDRAND bit set + */ + RDRAND_RNG(); + + /** + * Uses RDRAND to produce output + */ + void randomize(uint8_t out[], size_t out_len) override; + + /* + * No way to provide entropy to RDRAND generator, so add_entropy is ignored + */ + void add_entropy(const uint8_t[], size_t) override + { /* no op */ } + + size_t reseed_with_sources(Entropy_Sources&, size_t, + std::chrono::milliseconds) override + { return 0; /* no op */ } + + std::string name() const override { return "RDRAND"; } + + bool is_seeded() const override { return true; } + + void clear() override {} + }; + +} + +#endif -- cgit v1.2.3 From c7d0f80c43b880a2418e152984437cee66fd4cdc Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Wed, 20 Jul 2016 06:51:30 -0400 Subject: Fix header guard format configure.py expects all header guards to match a specific form, for finding them when generating the amalgamation file. --- src/lib/rng/rdrand_rng/rdrand_rng.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/lib/rng/rdrand_rng') diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.h b/src/lib/rng/rdrand_rng/rdrand_rng.h index b9445f28e..d0fb37c16 100644 --- a/src/lib/rng/rdrand_rng/rdrand_rng.h +++ b/src/lib/rng/rdrand_rng/rdrand_rng.h @@ -5,8 +5,8 @@ * Botan is released under the Simplified BSD License (see license.txt) */ -#ifndef BOTAN_RNG_RDRAND_H_ -#define BOTAN_RNG_RDRAND_H_ +#ifndef BOTAN_RNG_RDRAND_H__ +#define BOTAN_RNG_RDRAND_H__ #include -- cgit v1.2.3 From 80c160f08f2a69eb4e41a68380796bf31fd2f924 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 19 Aug 2016 07:51:47 -0400 Subject: RNG changes (GH #593) Change reseed interval logic to count calls to `randomize` rather than bytes, to match SP 800-90A Changes RNG reseeding API: there is no implicit reference to the global entropy sources within the RNGs anymore. The entropy sources must be supplied with the API call. Adds support for reseding directly from another RNG (such as a system or hardware RNG). Stateful_RNG keeps optional references to both an RNG and a set of entropy sources. During a reseed, both sources are used if set. These can be provided to HMAC_DRBG constructor. For HMAC_DRBG, SP800-90A requires we output no more than 2**16 bytes per DRBG request. We treat requests longer than that as if the caller had instead made several sequential maximum-length requests. This means it is possible for one or more reseeds to trigger even in the course of generating a single (long) output (generate a 256-bit key and use ChaCha or HKDF if this is a problem). Adds RNG::randomize_with_ts_input which takes timestamps and uses them as the additional_data DRBG field. Stateful_RNG overrides this to also include the process ID and the reseed counter. AutoSeeded_RNG's `randomize` uses this. Officially deprecates RNG::make_rng and the Serialized_RNG construtor which creates an AutoSeeded_RNG. With these removed, it would be possible to perform a build with no AutoSeeded_RNG/HMAC_DRBG at all (eg, for applications which only use the system RNG). Tests courtesy @cordney in GH PRs #598 and #600 --- doc/deprecated.txt | 5 + src/build-data/buildh.in | 10 +- src/build-data/policy/bsi.txt | 1 + src/build-data/policy/modern.txt | 2 +- src/cli/cli.h | 7 +- src/cli/speed.cpp | 16 +- src/cli/utils.cpp | 10 +- src/lib/entropy/entropy_src.h | 5 + src/lib/ffi/ffi.cpp | 2 +- src/lib/prov/pkcs11/p11_randomgenerator.h | 4 +- src/lib/pubkey/rfc6979/rfc6979.cpp | 2 +- src/lib/rng/auto_rng.h | 47 ----- src/lib/rng/auto_rng/auto_rng.cpp | 116 ++++++++++ src/lib/rng/auto_rng/auto_rng.h | 67 ++++++ src/lib/rng/auto_rng/info.txt | 1 + src/lib/rng/hmac_drbg/hmac_drbg.cpp | 99 ++++++--- src/lib/rng/hmac_drbg/hmac_drbg.h | 68 +++++- src/lib/rng/hmac_drbg/info.txt | 1 + src/lib/rng/hmac_rng/hmac_rng.cpp | 86 +++++--- src/lib/rng/hmac_rng/hmac_rng.h | 70 +++++-- src/lib/rng/hmac_rng/info.txt | 1 + src/lib/rng/info.txt | 2 - src/lib/rng/rdrand_rng/rdrand_rng.h | 3 +- src/lib/rng/rng.cpp | 140 +++---------- src/lib/rng/rng.h | 137 ++++-------- src/lib/rng/stateful_rng/info.txt | 2 + src/lib/rng/stateful_rng/stateful_rng.cpp | 112 ++++++++++ src/lib/rng/stateful_rng/stateful_rng.h | 118 +++++++++++ src/lib/rng/x931_rng/info.txt | 4 + src/lib/rng/x931_rng/x931_rng.cpp | 10 +- src/lib/rng/x931_rng/x931_rng.h | 9 +- src/lib/utils/os_utils.cpp | 6 +- src/lib/utils/os_utils.h | 2 +- src/tests/main.cpp | 24 ++- src/tests/test_mceliece.cpp | 2 +- src/tests/test_pkcs11_high_level.cpp | 12 +- src/tests/test_rng.cpp | 337 +++++++++++++++++++++++++++++- src/tests/test_rng.h | 34 +-- src/tests/tests.cpp | 2 +- 39 files changed, 1152 insertions(+), 424 deletions(-) delete mode 100644 src/lib/rng/auto_rng.h create mode 100644 src/lib/rng/auto_rng/auto_rng.cpp create mode 100644 src/lib/rng/auto_rng/auto_rng.h create mode 100644 src/lib/rng/auto_rng/info.txt create mode 100644 src/lib/rng/stateful_rng/info.txt create mode 100644 src/lib/rng/stateful_rng/stateful_rng.cpp create mode 100644 src/lib/rng/stateful_rng/stateful_rng.h (limited to 'src/lib/rng/rdrand_rng') diff --git a/doc/deprecated.txt b/doc/deprecated.txt index 456a7e6b7..d62954d1d 100644 --- a/doc/deprecated.txt +++ b/doc/deprecated.txt @@ -1,5 +1,10 @@ Currently deprecated: +- PRNGs X9.31 (no longer approved) and HMAC_RNG (non-standard) + Use HMAC_DRBG + +- ECB mode for block ciphers + - Rabin-Williams signatures - Nyberg-Rueppel signatures 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 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 #include #include -#include + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include +#endif #if defined(BOTAN_HAS_SYSTEM_RNG) #include @@ -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 #include #include -#include #include +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include +#endif + #if defined(BOTAN_HAS_SYSTEM_RNG) #include #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 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 -#include #include #include #include @@ -16,6 +15,10 @@ #include #endif +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include +#endif + #if defined(BOTAN_HAS_SYSTEM_RNG) #include #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 #include #include #include +#include +#include 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 - -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 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 +#include + +#if defined(BOTAN_HAS_HMAC_DRBG) + #include +#endif + +#if defined(BOTAN_HAS_HMAC_RNG) + #include +#endif + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#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 + +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 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 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 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 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 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(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 +#include #include 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 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 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 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 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 hmac +stateful_rng 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 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 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 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 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 #include -#include -#include 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 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 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 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 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 m_extractor; std::unique_ptr m_prf; + std::unique_ptr 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 mac +stateful_rng 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 - 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 -#include -#include #include #include -#if defined(BOTAN_HAS_HMAC_DRBG) - #include -#endif - -#if defined(BOTAN_HAS_HMAC_RNG) - #include +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include #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 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 #include #include #include @@ -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 void add_entropy_T(const T& t) { - add_entropy(reinterpret_cast(&t), sizeof(T)); + this->add_entropy(reinterpret_cast(&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 random_vec(size_t bytes) { secure_vector 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,64 +156,10 @@ 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 */ @@ -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 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 +#include +#include + +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(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 + +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 + + +stateful_rng + 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 #endif -#if defined(BOTAN_TARGET_OS_TYPE_IS_WINDOWS) +#if defined(BOTAN_TARGET_OS_IS_WINDOWS) #include #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 #include -#include #include +#include #if defined(BOTAN_HAS_HMAC_DRBG) #include @@ -26,6 +26,10 @@ #include #endif +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include +#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 drbg(new Botan::HMAC_DRBG("SHA-384", 0)); + + // Expand out the seed to 512 bits to make the DRBG happy + std::unique_ptr sha512(Botan::HashFunction::create("SHA-512")); + sha512->update(seed); + seed.resize(sha512->output_length()); + sha512->final(seed.data()); + + std::unique_ptr 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 rnd = p11_rng.random_vec(64); - - drbg.initialize_with(rnd.data(), rnd.size()); + secure_vector 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 #endif +#if defined(BOTAN_HAS_ENTROPY_SOURCE) + #include +#endif + +#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) + #include + #include +#endif + +#include + namespace Botan_Tests { namespace { @@ -81,7 +93,7 @@ class HMAC_DRBG_Tests : public Text_Based_Test return result; } - std::unique_ptr rng(new Botan::HMAC_DRBG(mac.release(), 0)); + std::unique_ptr 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 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 seed_input( {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}); Botan::secure_vector output_after_initialization( @@ -130,16 +188,273 @@ class HMAC_DRBG_Reseed_Tests : public Test Botan::secure_vector 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_1(new Broken_Entropy_Source()); + std::unique_ptr 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(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 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 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 run() override + { + std::vector 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 #include -#if defined(BOTAN_HAS_SYSTEM_RNG) - #include -#else - #include -#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& 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 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(); } -- cgit v1.2.3