diff options
author | Jack Lloyd <[email protected]> | 2016-08-19 07:51:47 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-08-24 11:31:54 -0400 |
commit | 80c160f08f2a69eb4e41a68380796bf31fd2f924 (patch) | |
tree | 83259da316524ed3b96b0913e5b023bc40f26a28 /src/lib/rng/stateful_rng | |
parent | 91474f60d72937ad3c21d8aa53c14f7a0cceb9ca (diff) |
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
Diffstat (limited to 'src/lib/rng/stateful_rng')
-rw-r--r-- | src/lib/rng/stateful_rng/info.txt | 2 | ||||
-rw-r--r-- | src/lib/rng/stateful_rng/stateful_rng.cpp | 112 | ||||
-rw-r--r-- | src/lib/rng/stateful_rng/stateful_rng.h | 118 |
3 files changed, 232 insertions, 0 deletions
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 |