diff options
Diffstat (limited to 'comm/third_party/botan/src/lib/rng')
24 files changed, 2276 insertions, 0 deletions
diff --git a/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp new file mode 100644 index 0000000000..a13429e9b5 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.cpp @@ -0,0 +1,112 @@ +/* +* (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> +#include <botan/hmac_drbg.h> + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include <botan/system_rng.h> +#endif + +#if !defined(BOTAN_AUTO_RNG_HMAC) +#error "No hash function defined for AutoSeeded_RNG in build.h (try enabling sha2_32)" +#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 HMAC_DRBG(MessageAuthenticationCode::create_or_throw(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 HMAC_DRBG(MessageAuthenticationCode::create_or_throw(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 HMAC_DRBG( + MessageAuthenticationCode::create_or_throw(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 Internal_Error("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 uint8_t 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(uint8_t output[], size_t output_len) + { + m_rng->randomize_with_ts_input(output, output_len); + } + +void AutoSeeded_RNG::randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t ad[], size_t ad_len) + { + m_rng->randomize_with_input(output, output_len, ad, ad_len); + } + +} diff --git a/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h new file mode 100644 index 0000000000..8cb2c4a127 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/auto_rng/auto_rng.h @@ -0,0 +1,102 @@ +/* +* 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_PUBLIC_API(2,0) AutoSeeded_RNG final : public RandomNumberGenerator + { + public: + void randomize(uint8_t out[], size_t len) override; + + void randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) override; + + bool is_seeded() const override; + + bool accepts_input() const override { return true; } + + /** + * Mark state as requiring a reseed on next use + */ + 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 uint8_t in[], size_t len) override; + + std::string name() const override; + + void clear() override; + + /** + * Uses the system RNG (if available) or else a default group of + * entropy sources (all other systems) to gather seed material. + * + * @param reseed_interval specifies a limit of how many times + * the RNG will be called before automatic reseeding is performed + */ + AutoSeeded_RNG(size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Create an AutoSeeded_RNG which will get seed material from some other + * RNG instance. For example you could provide a reference to the system + * RNG or a hardware RNG. + * + * @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 + */ + AutoSeeded_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Create an AutoSeeded_RNG which will get seed material from a set of + * entropy sources. + * + * @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 + */ + AutoSeeded_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Create an AutoSeeded_RNG which will get seed material from both an + * underlying RNG and a set of entropy sources. + * + * @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 + */ + 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/comm/third_party/botan/src/lib/rng/auto_rng/info.txt b/comm/third_party/botan/src/lib/rng/auto_rng/info.txt new file mode 100644 index 0000000000..f1adcc8001 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/auto_rng/info.txt @@ -0,0 +1,8 @@ +<defines> +AUTO_SEEDING_RNG -> 20160821 +AUTO_RNG -> 20161126 +</defines> + +<requires> +hmac_drbg +</requires> diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp new file mode 100644 index 0000000000..3dc69ec1b8 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.cpp @@ -0,0 +1,87 @@ +/* +* ChaCha_RNG +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/chacha_rng.h> + +namespace Botan { + +ChaCha_RNG::ChaCha_RNG() : Stateful_RNG() + { + m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); + clear(); + } + +ChaCha_RNG::ChaCha_RNG(const secure_vector<uint8_t>& seed) : Stateful_RNG() + { + m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); + clear(); + add_entropy(seed.data(), seed.size()); + } + +ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, reseed_interval) + { + m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); + clear(); + } + +ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(underlying_rng, entropy_sources, reseed_interval) + { + m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); + clear(); + } + +ChaCha_RNG::ChaCha_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval) : + Stateful_RNG(entropy_sources, reseed_interval) + { + m_hmac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)"); + m_chacha = StreamCipher::create_or_throw("ChaCha(20)"); + clear(); + } + +void ChaCha_RNG::clear_state() + { + m_hmac->set_key(std::vector<uint8_t>(m_hmac->output_length(), 0x00)); + m_chacha->set_key(m_hmac->final()); + } + +void ChaCha_RNG::generate_output(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) + { + if(input_len > 0) + { + update(input, input_len); + } + + m_chacha->write_keystream(output, output_len); + } + +void ChaCha_RNG::update(const uint8_t input[], size_t input_len) + { + m_hmac->update(input, input_len); + m_chacha->set_key(m_hmac->final()); + + secure_vector<uint8_t> mac_key(m_hmac->output_length()); + m_chacha->write_keystream(mac_key.data(), mac_key.size()); + m_hmac->set_key(mac_key); + } + +size_t ChaCha_RNG::security_level() const + { + return 256; + } + +} diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h new file mode 100644 index 0000000000..c50c2d0c25 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/chacha_rng/chacha_rng.h @@ -0,0 +1,125 @@ +/* +* ChaCha_RNG +* (C) 2017 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_CHACHA_RNG_H_ +#define BOTAN_CHACHA_RNG_H_ + +#include <botan/stateful_rng.h> +#include <botan/stream_cipher.h> +#include <botan/mac.h> + +namespace Botan { + +class Entropy_Sources; + +/** +* ChaCha_RNG is a very fast but completely ad-hoc RNG created by +* creating a 256-bit random value and using it as a key for ChaCha20. +* +* The RNG maintains two 256-bit keys, one for HMAC_SHA256 (HK) and the +* other for ChaCha20 (CK). To compute a new key in response to +* reseeding request or add_entropy calls, ChaCha_RNG computes +* CK' = HMAC_SHA256(HK, input_material) +* Then a new HK' is computed by running ChaCha20 with the new key to +* output 32 bytes: +* HK' = ChaCha20(CK') +* +* Now output can be produced by continuing to produce output with ChaCha20 +* under CK' +* +* The first HK (before seeding occurs) is taken as the all zero value. +* +* @warning This RNG construction is probably fine but is non-standard. +* The primary reason to use it is in cases where the other RNGs are +* not fast enough. +*/ +class BOTAN_PUBLIC_API(2,3) ChaCha_RNG final : public Stateful_RNG + { + public: + /** + * Automatic reseeding is disabled completely, as it has 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. + */ + ChaCha_RNG(); + + /** + * Provide an initial seed to the RNG, without providing an + * underlying RNG or entropy source. Automatic reseeding is + * disabled completely, as it has 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. + * + * @param seed the seed material, should be at least 256 bits + */ + ChaCha_RNG(const secure_vector<uint8_t>& seed); + + /** + * Automatic reseeding from @p underlying_rng will take place after + * @p reseed_interval many requests or after a fork was detected. + * + * @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 + */ + ChaCha_RNG(RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Automatic reseeding from @p entropy_sources will take place after + * @p reseed_interval many requests or after a fork was detected. + * + * @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. + */ + ChaCha_RNG(Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + /** + * Automatic reseeding from @p underlying_rng and @p entropy_sources + * will take place after @p reseed_interval many requests or after + * a fork was detected. + * + * @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. + */ + ChaCha_RNG(RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL); + + std::string name() const override { return "ChaCha_RNG"; } + + size_t security_level() const override; + + size_t max_number_of_bytes_per_request() const override { return 0; } + + private: + void update(const uint8_t input[], size_t input_len) override; + + void generate_output(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) override; + + void clear_state() override; + + std::unique_ptr<MessageAuthenticationCode> m_hmac; + std::unique_ptr<StreamCipher> m_chacha; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt b/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt new file mode 100644 index 0000000000..3f51bf4d0e --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/chacha_rng/info.txt @@ -0,0 +1,10 @@ +<defines> +CHACHA_RNG -> 20170728 +</defines> + +<requires> +hmac +sha2_32 +chacha +stateful_rng +</requires> diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp new file mode 100644 index 0000000000..2b66a839c3 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -0,0 +1,197 @@ +/* +* HMAC_DRBG +* (C) 2014,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/hmac_drbg.h> +#include <algorithm> + +namespace Botan { + +namespace { + +size_t hmac_drbg_security_level(size_t mac_output_length) + { + // security strength of the hash function + // for pre-image resistance (see NIST SP 800-57) + // SHA-160: 128 bits + // SHA-224, SHA-512/224: 192 bits, + // SHA-256, SHA-512/256, SHA-384, SHA-512: >= 256 bits + // NIST SP 800-90A only supports up to 256 bits though + + if(mac_output_length < 32) + { + return (mac_output_length - 4) * 8; + } + else + { + return 32 * 8; + } + } + +void check_limits(size_t reseed_interval, + size_t max_number_of_bytes_per_request) + { + // SP800-90A permits up to 2^48, but it is not usable on 32 bit + // platforms, so we only allow up to 2^24, which is still reasonably high + if(reseed_interval == 0 || reseed_interval > static_cast<size_t>(1) << 24) + { + throw Invalid_Argument("Invalid value for reseed_interval"); + } + + if(max_number_of_bytes_per_request == 0 || max_number_of_bytes_per_request > 64 * 1024) + { + throw Invalid_Argument("Invalid value for max_number_of_bytes_per_request"); + } + } + +} + +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval, + size_t max_number_of_bytes_per_request) : + Stateful_RNG(underlying_rng, reseed_interval), + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), + m_security_level(hmac_drbg_security_level(m_mac->output_length())) + { + BOTAN_ASSERT_NONNULL(m_mac); + + check_limits(reseed_interval, max_number_of_bytes_per_request); + + clear(); + } + +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval, + size_t max_number_of_bytes_per_request) : + Stateful_RNG(underlying_rng, entropy_sources, reseed_interval), + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), + m_security_level(hmac_drbg_security_level(m_mac->output_length())) + { + BOTAN_ASSERT_NONNULL(m_mac); + + check_limits(reseed_interval, max_number_of_bytes_per_request); + + clear(); + } + +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval, + size_t max_number_of_bytes_per_request) : + Stateful_RNG(entropy_sources, reseed_interval), + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(max_number_of_bytes_per_request), + m_security_level(hmac_drbg_security_level(m_mac->output_length())) + { + BOTAN_ASSERT_NONNULL(m_mac); + + check_limits(reseed_interval, max_number_of_bytes_per_request); + + clear(); + } + +HMAC_DRBG::HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf) : + Stateful_RNG(), + m_mac(std::move(prf)), + m_max_number_of_bytes_per_request(64*1024), + m_security_level(hmac_drbg_security_level(m_mac->output_length())) + { + BOTAN_ASSERT_NONNULL(m_mac); + clear(); + } + +HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash) : + Stateful_RNG(), + m_mac(MessageAuthenticationCode::create_or_throw("HMAC(" + hmac_hash + ")")), + m_max_number_of_bytes_per_request(64 * 1024), + m_security_level(hmac_drbg_security_level(m_mac->output_length())) + { + clear(); + } + +void HMAC_DRBG::clear_state() + { + if(m_V.size() == 0) + { + const size_t output_length = m_mac->output_length(); + m_V.resize(output_length); + } + + for(size_t i = 0; i != m_V.size(); ++i) + m_V[i] = 0x01; + m_mac->set_key(std::vector<uint8_t>(m_V.size(), 0x00)); + } + +std::string HMAC_DRBG::name() const + { + return "HMAC_DRBG(" + m_mac->name() + ")"; + } + +/* +* HMAC_DRBG generation +* See NIST SP800-90A section 10.1.2.5 +*/ +void HMAC_DRBG::generate_output(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) + { + if(input_len > 0) + { + update(input, input_len); + } + + while(output_len > 0) + { + 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); + + output += to_copy; + output_len -= to_copy; + } + + update(input, input_len); + } + +/* +* Reset V and the mac key with new values +* See NIST SP800-90A section 10.1.2.2 +*/ +void HMAC_DRBG::update(const uint8_t input[], size_t input_len) + { + secure_vector<uint8_t> T(m_V.size()); + m_mac->update(m_V); + m_mac->update(0x00); + m_mac->update(input, input_len); + m_mac->final(T.data()); + m_mac->set_key(T); + + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); + + if(input_len > 0) + { + m_mac->update(m_V); + m_mac->update(0x01); + m_mac->update(input, input_len); + m_mac->final(T.data()); + m_mac->set_key(T); + + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); + } + } + +size_t HMAC_DRBG::security_level() const + { + return m_security_level; + } +} diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h new file mode 100644 index 0000000000..a4c288c74b --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -0,0 +1,150 @@ +/* +* HMAC_DRBG (SP800-90A) +* (C) 2014,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_HMAC_DRBG_H_ +#define BOTAN_HMAC_DRBG_H_ + +#include <botan/stateful_rng.h> +#include <botan/mac.h> + +namespace Botan { + +class Entropy_Sources; + +/** +* HMAC_DRBG from NIST SP800-90A +*/ +class BOTAN_PUBLIC_API(2,0) HMAC_DRBG final : public Stateful_RNG + { + public: + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * Automatic reseeding is disabled completely, as it has 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. + */ + explicit HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf); + + /** + * Constructor taking a string for the hash + */ + explicit HMAC_DRBG(const std::string& hmac_hash); + + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * Automatic reseeding from @p underlying_rng will take place after + * @p reseed_interval many requests or after a fork was detected. + * + * @param prf MAC to use as a PRF + * @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 (max. 2^24) + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); + + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * Automatic reseeding from @p entropy_sources will take place after + * @p reseed_interval many requests or after a fork was detected. + * + * @param prf MAC to use as a PRF + * @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 (max. 2^24) + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); + + /** + * Initialize an HMAC_DRBG instance with the given MAC as PRF (normally HMAC) + * + * Automatic reseeding from @p underlying_rng and @p entropy_sources + * will take place after @p reseed_interval many requests or after + * a fork was detected. + * + * @param prf MAC to use as a PRF + * @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 (max. 2^24) + * @param max_number_of_bytes_per_request requests that are in size higher + * than max_number_of_bytes_per_request are treated as if multiple single + * requests of max_number_of_bytes_per_request size had been made. + * In theory SP 800-90A requires that we reject any request for a DRBG + * output longer than max_number_of_bytes_per_request. To avoid inconveniencing + * the caller who wants an output larger than max_number_of_bytes_per_request, + * instead treat these requests as if multiple requests of + * max_number_of_bytes_per_request size had been made. NIST requires for + * HMAC_DRBG that every implementation set a value no more than 2**19 bits + * (or 64 KiB). Together with @p reseed_interval = 1 you can enforce that for + * example every 512 bit automatic reseeding occurs. + */ + HMAC_DRBG(std::unique_ptr<MessageAuthenticationCode> prf, + RandomNumberGenerator& underlying_rng, + Entropy_Sources& entropy_sources, + size_t reseed_interval = BOTAN_RNG_DEFAULT_RESEED_INTERVAL, + size_t max_number_of_bytes_per_request = 64 * 1024); + + std::string name() const override; + + size_t security_level() const override; + + size_t max_number_of_bytes_per_request() const override + { return m_max_number_of_bytes_per_request; } + + private: + void update(const uint8_t input[], size_t input_len) override; + + void generate_output(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) override; + + void clear_state() override; + + std::unique_ptr<MessageAuthenticationCode> m_mac; + secure_vector<uint8_t> m_V; + const size_t m_max_number_of_bytes_per_request; + const size_t m_security_level; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt b/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt new file mode 100644 index 0000000000..a8922bdf0e --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/hmac_drbg/info.txt @@ -0,0 +1,8 @@ +<defines> +HMAC_DRBG -> 20140319 +</defines> + +<requires> +hmac +stateful_rng +</requires> diff --git a/comm/third_party/botan/src/lib/rng/info.txt b/comm/third_party/botan/src/lib/rng/info.txt new file mode 100644 index 0000000000..4c88ba3826 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/info.txt @@ -0,0 +1,3 @@ +<requires> +entropy +</requires> diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/info.txt b/comm/third_party/botan/src/lib/rng/processor_rng/info.txt new file mode 100644 index 0000000000..61d10f11b4 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/processor_rng/info.txt @@ -0,0 +1,20 @@ +<defines> +PROCESSOR_RNG -> 20200508 +</defines> + +<cc> +gcc +clang +icc +msvc +</cc> + +<arch> +x86_32 +x86_64 +ppc64 +</arch> + +<isa> +ppc64:power9 +</isa> diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp new file mode 100644 index 0000000000..ca52d05e67 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.cpp @@ -0,0 +1,157 @@ +/* +* (C) 2016,2019,2020 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/processor_rng.h> +#include <botan/loadstor.h> +#include <botan/cpuid.h> + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM) + #include <immintrin.h> +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + /* + * According to Intel, RDRAND is guaranteed to generate a random + * number within 10 retries on a working CPU + */ + const size_t HWRNG_RETRIES = 10; + +#else + /* + * Lacking specific guidance we give the CPU quite a bit of leeway + */ + const size_t HWRNG_RETRIES = 512; +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_X86_32) + typedef uint32_t hwrng_output; +#else + typedef uint64_t hwrng_output; +#endif + +hwrng_output read_hwrng(bool& success) + { + hwrng_output output = 0; + success = false; + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + int cf = 0; +#if defined(BOTAN_USE_GCC_INLINE_ASM) + // same asm seq works for 32 and 64 bit + asm volatile("rdrand %0; adcl $0,%1" : + "=r" (output), "=r" (cf) : "0" (output), "1" (cf) : "cc"); +#elif defined(BOTAN_TARGET_ARCH_IS_X86_32) + cf = _rdrand32_step(&output); +#else + cf = _rdrand64_step(&output); +#endif + success = (1 == cf); + +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + + /* + DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy. + Avoid the bias by invoking it twice and, assuming both succeed, returning the + XOR of the two results, which should unbias the output. + */ + uint64_t output2 = 0; + // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED) + asm volatile("darn %0, 1" : "=r" (output)); + asm volatile("darn %0, 1" : "=r" (output2)); + + if((~output) != 0 && (~output2) != 0) + { + output ^= output2; + success = true; + } + +#endif + + if(success) + return output; + + return 0; + } + +hwrng_output read_hwrng() + { + for(size_t i = 0; i < HWRNG_RETRIES; ++i) + { + bool success = false; + hwrng_output output = read_hwrng(success); + + if(success) + return output; + } + + throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations"); + } + +} + +//static +bool Processor_RNG::available() + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return CPUID::has_rdrand(); +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return CPUID::has_darn_rng(); +#else + return false; +#endif + } + +std::string Processor_RNG::name() const + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return "rdrand"; +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return "darn"; +#else + return "hwrng"; +#endif + } + +void Processor_RNG::randomize(uint8_t out[], size_t out_len) + { + while(out_len >= sizeof(hwrng_output)) + { + const hwrng_output r = read_hwrng(); + store_le(r, out); + out += sizeof(hwrng_output); + out_len -= sizeof(hwrng_output); + } + + if(out_len > 0) // at most sizeof(hwrng_output)-1 + { + const hwrng_output r = read_hwrng(); + for(size_t i = 0; i != out_len; ++i) + out[i] = get_byte(i, r); + } + } + +Processor_RNG::Processor_RNG() + { + if(!Processor_RNG::available()) + throw Invalid_State("Current CPU does not support RNG instruction"); + } + +void Processor_RNG::add_entropy(const uint8_t[], size_t) + { + /* no way to add entropy */ + } + +size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) + { + /* no way to add entropy */ + return 0; + } + +} diff --git a/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h new file mode 100644 index 0000000000..5900e386e0 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/processor_rng/processor_rng.h @@ -0,0 +1,52 @@ +/* +* (C) 2016,2019,2020 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RNG_PROCESSOR_RNG_H_ +#define BOTAN_RNG_PROCESSOR_RNG_H_ + +#include <botan/rng.h> + +namespace Botan { + +/** +* Directly invokes a CPU specific instruction to generate random numbers. +* On x86, the RDRAND instruction is used. +* on POWER, the DARN instruction is used. +*/ +class BOTAN_PUBLIC_API(2,15) Processor_RNG final : public Hardware_RNG + { + public: + /** + * Constructor will throw if CPU does not have RDRAND bit set + */ + Processor_RNG(); + + /** + * Return true if RNG instruction is available on the current processor + */ + static bool available(); + + bool accepts_input() const override { return false; } + bool is_seeded() const override { return true; } + + 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 way to reseed processor provided generator, so reseed is ignored + */ + size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override; + + std::string name() const override; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt b/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt new file mode 100644 index 0000000000..5cc616deae --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/info.txt @@ -0,0 +1,13 @@ +<defines> +RDRAND_RNG -> 20160619 +</defines> + +<requires> +processor_rng +</requires> + +# Avoid building RDRAND_RNG on non-x86 since that would be confusing +<arch> +x86_32 +x86_64 +</arch> diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp new file mode 100644 index 0000000000..fade5a1992 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp @@ -0,0 +1,67 @@ +/* +* RDRAND RNG +* (C) 2016,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/rdrand_rng.h> +#include <botan/processor_rng.h> +#include <botan/loadstor.h> + +namespace Botan { + +void RDRAND_RNG::randomize(uint8_t out[], size_t out_len) + { + Processor_RNG rng; + rng.randomize(out, out_len); + } + +RDRAND_RNG::RDRAND_RNG() + { + // Will throw if instruction is not available + Processor_RNG rng; + } + +//static +bool RDRAND_RNG::available() + { + return Processor_RNG::available(); + } + +//static +uint32_t RDRAND_RNG::rdrand() + { + Processor_RNG rng; + + for(;;) + { + try + { + uint8_t out[4]; + rng.randomize(out, 4); + return load_le<uint32_t>(out, 0); + } + catch(PRNG_Unseeded&) {} + } + } + +//static +uint32_t RDRAND_RNG::rdrand_status(bool& ok) + { + ok = false; + Processor_RNG rng; + + try + { + uint8_t out[4]; + rng.randomize(out, 4); + ok = true; + return load_le<uint32_t>(out, 0); + } + catch(PRNG_Unseeded&) {} + + return 0; + } + +} diff --git a/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h new file mode 100644 index 0000000000..1b6977eac3 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/rdrand_rng/rdrand_rng.h @@ -0,0 +1,68 @@ +/* +* RDRAND RNG +* (C) 2016,2019 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RNG_RDRAND_H_ +#define BOTAN_RNG_RDRAND_H_ + +#include <botan/rng.h> + +namespace Botan { + +class BOTAN_PUBLIC_API(2,0) RDRAND_RNG final : public Hardware_RNG + { + public: + /** + * Constructor will throw if CPU does not have RDRAND bit set + */ + BOTAN_DEPRECATED("Use Processor_RNG instead") RDRAND_RNG(); + + /** + * Return true if RDRAND is available on the current processor + */ + static bool available(); + + bool accepts_input() const override { return false; } + + /** + * 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 */ } + + /* + * No way to reseed RDRAND generator, so reseed is ignored + */ + size_t reseed(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; } + + /** + * 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 BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand_status(bool& ok); + + /* + * Calls RDRAND until it succeeds, this could hypothetically + * loop forever on broken hardware. + */ + static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand(); + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/rng.cpp b/comm/third_party/botan/src/lib/rng/rng.cpp new file mode 100644 index 0000000000..743f7c7aa2 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/rng.cpp @@ -0,0 +1,91 @@ +/* +* (C) 2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/rng.h> +#include <botan/entropy_src.h> +#include <botan/loadstor.h> +#include <botan/internal/os_utils.h> + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) + #include <botan/auto_rng.h> +#endif + +namespace Botan { + +void RandomNumberGenerator::randomize_with_ts_input(uint8_t output[], size_t output_len) + { + if(this->accepts_input()) + { + /* + Form additional input which is provided to the PRNG implementation + to paramaterize the KDF output. + */ + uint8_t additional_input[16] = { 0 }; + store_le(OS::get_system_timestamp_ns(), additional_input); + store_le(OS::get_high_resolution_clock(), additional_input + 8); + + this->randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); + } + else + { + this->randomize(output, output_len); + } + } + +void RandomNumberGenerator::randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) + { + this->add_entropy(input, input_len); + this->randomize(output, output_len); + } + +size_t RandomNumberGenerator::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + if(this->accepts_input()) + { + return srcs.poll(*this, poll_bits, poll_timeout); + } + else + { + return 0; + } + } + +void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) + { + if(this->accepts_input()) + { + secure_vector<uint8_t> 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 Not_Implemented("make_rng failed, no AutoSeeded_RNG in this build"); +#endif + } + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) + +#if defined(BOTAN_HAS_AUTO_SEEDING_RNG) +Serialized_RNG::Serialized_RNG() : m_rng(new AutoSeeded_RNG) {} +#else +Serialized_RNG::Serialized_RNG() + { + throw Not_Implemented("Serialized_RNG default constructor failed: AutoSeeded_RNG disabled in build"); + } +#endif + +#endif + +} diff --git a/comm/third_party/botan/src/lib/rng/rng.h b/comm/third_party/botan/src/lib/rng/rng.h new file mode 100644 index 0000000000..54a8ea8319 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/rng.h @@ -0,0 +1,297 @@ +/* +* Random Number Generator base classes +* (C) 1999-2009,2015,2016 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H_ +#define BOTAN_RANDOM_NUMBER_GENERATOR_H_ + +#include <botan/secmem.h> +#include <botan/exceptn.h> +#include <botan/mutex.h> +#include <type_traits> +#include <chrono> +#include <string> + +namespace Botan { + +class Entropy_Sources; + +/** +* An interface to a cryptographic random number generator +*/ +class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator + { + public: + virtual ~RandomNumberGenerator() = default; + + RandomNumberGenerator() = default; + + /* + * Never copy a RNG, create a new one + */ + RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; + RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; + + /** + * Randomize a byte array. + * @param output the byte array to hold the random output. + * @param length the length of the byte array output in bytes. + */ + virtual void randomize(uint8_t output[], size_t length) = 0; + + /** + * Returns false if it is known that this RNG object is not able to accept + * externally provided inputs (via add_entropy, randomize_with_input, etc). + * In this case, any such provided inputs are ignored. + * + * If this function returns true, then inputs may or may not be accepted. + */ + virtual bool accepts_input() const = 0; + + /** + * 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 input a byte array containg the entropy to be added + * @param length the length of the byte array in + */ + virtual void add_entropy(const uint8_t input[], size_t length) = 0; + + /** + * Incorporate some additional data into the RNG state. + */ + template<typename T> void add_entropy_T(const T& t) + { + static_assert(std::is_standard_layout<T>::value && std::is_trivial<T>::value, "add_entropy_T data must be POD"); + 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, 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. + * + * @param output buffer to hold the random output + * @param output_len size of the output buffer in bytes + * @param input entropy buffer to incorporate + * @param input_len size of the input buffer in bytes + */ + virtual void randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t 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 the + * extra 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(uint8_t output[], size_t output_len); + + /** + * @return the name of this RNG type + */ + virtual std::string name() const = 0; + + /** + * Clear all internally held values of this RNG + * @post is_seeded() == false + */ + virtual void clear() = 0; + + /** + * Check whether this RNG is seeded. + * @return true if this RNG was already seeded, false otherwise. + */ + virtual bool is_seeded() const = 0; + + /** + * Poll provided sources for up to poll_bits bits of entropy + * or until the timeout expires. Returns estimate of the number + * of bits collected. + */ + 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 by reading specified bits from the RNG + */ + virtual void reseed_from_rng(RandomNumberGenerator& rng, + size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS); + + // Some utility functions built on the interface above: + + /** + * Return a random vector + * @param bytes number of bytes in the result + * @return randomized vector of length bytes + */ + secure_vector<uint8_t> random_vec(size_t bytes) + { + secure_vector<uint8_t> output; + random_vec(output, bytes); + return output; + } + + template<typename Alloc> + void random_vec(std::vector<uint8_t, Alloc>& v, size_t bytes) + { + v.resize(bytes); + this->randomize(v.data(), v.size()); + } + + /** + * Return a random byte + * @return random byte + */ + uint8_t next_byte() + { + uint8_t b; + this->randomize(&b, 1); + return b; + } + + /** + * @return a random byte that is greater than zero + */ + uint8_t next_nonzero_byte() + { + uint8_t b = this->next_byte(); + while(b == 0) + b = this->next_byte(); + return b; + } + + /** + * Create a seeded and active RNG object for general application use + * Added in 1.8.0 + * Use AutoSeeded_RNG instead + */ + BOTAN_DEPRECATED("Use AutoSeeded_RNG") + static RandomNumberGenerator* make_rng(); + }; + +/** +* Convenience typedef +*/ +typedef RandomNumberGenerator RNG; + +/** +* Hardware_RNG exists to tag hardware RNG types (PKCS11_RNG, TPM_RNG, Processor_RNG) +*/ +class BOTAN_PUBLIC_API(2,0) Hardware_RNG : public RandomNumberGenerator + { + public: + virtual void clear() final override { /* no way to clear state of hardware RNG */ } + }; + +/** +* Null/stub RNG - fails if you try to use it for anything +* This is not generally useful except for in certain tests +*/ +class BOTAN_PUBLIC_API(2,0) Null_RNG final : public RandomNumberGenerator + { + public: + bool is_seeded() const override { return false; } + + bool accepts_input() const override { return false; } + + void clear() override {} + + void randomize(uint8_t[], size_t) override + { + throw PRNG_Unseeded("Null_RNG called"); + } + + void add_entropy(const uint8_t[], size_t) override {} + + std::string name() const override { return "Null_RNG"; } + }; + +#if defined(BOTAN_TARGET_OS_HAS_THREADS) +/** +* Wraps access to a RNG in a mutex +* Note that most of the time it's much better to use a RNG per thread +* otherwise the RNG will act as an unnecessary contention point +* +* Since 2.16.0 all Stateful_RNG instances have an internal lock, so +* this class is no longer needed. It will be removed in a future major +* release. +*/ +class BOTAN_PUBLIC_API(2,0) Serialized_RNG final : public RandomNumberGenerator + { + public: + void randomize(uint8_t out[], size_t len) override + { + lock_guard_type<mutex_type> lock(m_mutex); + m_rng->randomize(out, len); + } + + bool accepts_input() const override + { + lock_guard_type<mutex_type> lock(m_mutex); + return m_rng->accepts_input(); + } + + bool is_seeded() const override + { + lock_guard_type<mutex_type> lock(m_mutex); + return m_rng->is_seeded(); + } + + void clear() override + { + lock_guard_type<mutex_type> lock(m_mutex); + m_rng->clear(); + } + + std::string name() const override + { + lock_guard_type<mutex_type> lock(m_mutex); + return m_rng->name(); + } + + 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 + { + lock_guard_type<mutex_type> lock(m_mutex); + return m_rng->reseed(src, poll_bits, poll_timeout); + } + + void add_entropy(const uint8_t in[], size_t len) override + { + lock_guard_type<mutex_type> lock(m_mutex); + m_rng->add_entropy(in, len); + } + + BOTAN_DEPRECATED("Use Serialized_RNG(new AutoSeeded_RNG) instead") Serialized_RNG(); + + /* + * Since 2.16.0 this is no longer needed for any RNG type. This + * class will be removed in a future major release. + */ + explicit Serialized_RNG(RandomNumberGenerator* rng) : m_rng(rng) {} + private: + mutable mutex_type m_mutex; + std::unique_ptr<RandomNumberGenerator> m_rng; + }; +#endif + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt b/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt new file mode 100644 index 0000000000..edc2d91694 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/stateful_rng/info.txt @@ -0,0 +1,3 @@ +<defines> +STATEFUL_RNG -> 20160819 +</defines> diff --git a/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp new file mode 100644 index 0000000000..c7b3484ee0 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.cpp @@ -0,0 +1,190 @@ +/* +* (C) 2016,2020 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> + +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include <botan/system_rng.h> +#endif + +namespace Botan { + +void Stateful_RNG::clear() + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + m_reseed_counter = 0; + m_last_pid = 0; + clear_state(); + } + +void Stateful_RNG::force_reseed() + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + m_reseed_counter = 0; + } + +bool Stateful_RNG::is_seeded() const + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + return m_reseed_counter > 0; + } + +void Stateful_RNG::add_entropy(const uint8_t input[], size_t input_len) + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + + update(input, input_len); + + if(8*input_len >= security_level()) + { + reset_reseed_counter(); + } + } + +void Stateful_RNG::initialize_with(const uint8_t input[], size_t len) + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + + clear(); + add_entropy(input, len); + } + +void Stateful_RNG::randomize(uint8_t output[], size_t output_len) + { + randomize_with_input(output, output_len, nullptr, 0); + } + +void Stateful_RNG::randomize_with_ts_input(uint8_t output[], size_t output_len) + { + uint8_t additional_input[20] = { 0 }; + + store_le(OS::get_high_resolution_clock(), additional_input); + +#if defined(BOTAN_HAS_SYSTEM_RNG) + System_RNG system_rng; + system_rng.randomize(additional_input + 8, sizeof(additional_input) - 8); +#else + store_le(OS::get_system_timestamp_ns(), additional_input + 8); + store_le(OS::get_process_id(), additional_input + 16); +#endif + + randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); + } + +void Stateful_RNG::randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) + { + if(output_len == 0) + return; + + lock_guard_type<recursive_mutex_type> lock(m_mutex); + + const size_t max_per_request = max_number_of_bytes_per_request(); + + if(max_per_request == 0) // no limit + { + reseed_check(); + this->generate_output(output, output_len, input, input_len); + } + else + { + while(output_len > 0) + { + const size_t this_req = std::min(max_per_request, output_len); + + /* + * We split the request into several requests to the underlying DRBG but + * pass the input to each invocation. It might be more sensible to only + * provide it for the first invocation, however between 2.0 and 2.15 + * HMAC_DRBG always provided it for all requests so retain that here. + */ + + reseed_check(); + this->generate_output(output, this_req, input, input_len); + + output += this_req; + output_len -= this_req; + } + } + } + +size_t Stateful_RNG::reseed(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + + const size_t bits_collected = RandomNumberGenerator::reseed(srcs, poll_bits, poll_timeout); + + if(bits_collected >= security_level()) + { + reset_reseed_counter(); + } + + return bits_collected; + } + +void Stateful_RNG::reseed_from_rng(RandomNumberGenerator& rng, size_t poll_bits) + { + lock_guard_type<recursive_mutex_type> lock(m_mutex); + + RandomNumberGenerator::reseed_from_rng(rng, poll_bits); + + if(poll_bits >= security_level()) + { + reset_reseed_counter(); + } + } + +void Stateful_RNG::reset_reseed_counter() + { + // Lock is held whenever this function is called + m_reseed_counter = 1; + } + +void Stateful_RNG::reseed_check() + { + // Lock is held whenever this function is called + + 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 Invalid_State("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/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h new file mode 100644 index 0000000000..e1311fcc9f --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/stateful_rng/stateful_rng.h @@ -0,0 +1,166 @@ +/* +* (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> +#include <botan/mutex.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_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator + { + public: + /** + * @param 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 + */ + 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) + {} + + /** + * @param 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 + */ + Stateful_RNG(RandomNumberGenerator& rng, size_t reseed_interval) : + m_underlying_rng(&rng), + m_reseed_interval(reseed_interval) + {} + + /** + * @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 + */ + 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 uint8_t input[], size_t length); + + bool is_seeded() const override final; + + bool accepts_input() const override final { return true; } + + /** + * 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; + + void add_entropy(const uint8_t input[], size_t input_len) override final; + + void randomize(uint8_t output[], size_t output_len) override final; + + void randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) override final; + + /** + * Overrides default implementation and also includes the current + * process ID and the reseed counter. + */ + void randomize_with_ts_input(uint8_t 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; + + /** + * Some DRBGs have a notion of the maximum number of bytes per + * request. Longer requests (to randomize) will be treated as + * multiple requests, and may initiate reseeding multiple times, + * depending on the values of max_number_of_bytes_per_request and + * reseed_interval(). This function returns zero if the RNG in + * question does not have such a notion. + * + * @return max number of bytes per request (or zero) + */ + virtual size_t max_number_of_bytes_per_request() const = 0; + + size_t reseed_interval() const { return m_reseed_interval; } + + void clear() override final; + + protected: + void reseed_check(); + + virtual void generate_output(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) = 0; + + virtual void update(const uint8_t input[], size_t input_len) = 0; + + virtual void clear_state() = 0; + + private: + void reset_reseed_counter(); + + mutable recursive_mutex_type m_mutex; + + // 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; + uint32_t m_last_pid = 0; + + /* + * Set to 1 after a successful 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; + }; + +} + +#endif diff --git a/comm/third_party/botan/src/lib/rng/system_rng/info.txt b/comm/third_party/botan/src/lib/rng/system_rng/info.txt new file mode 100644 index 0000000000..e77328b820 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/system_rng/info.txt @@ -0,0 +1,18 @@ +<defines> +SYSTEM_RNG -> 20141202 +</defines> + +<os_features> +dev_random,posix1 +arc4random +rtlgenrandom +crypto_ng +</os_features> + +<libs> +uwp -> bcrypt +</libs> + +<requires> +rtlgenrandom?dyn_load +</requires> diff --git a/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp new file mode 100644 index 0000000000..0c5641e60d --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.cpp @@ -0,0 +1,289 @@ +/* +* System RNG +* (C) 2014,2015,2017,2018 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/system_rng.h> + +#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM) + #include <botan/dyn_load.h> + #define NOMINMAX 1 + #define _WINSOCKAPI_ // stop windows.h including winsock.h + #include <windows.h> + +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) + #include <bcrypt.h> + +#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) + #include <stdlib.h> + +#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM) + #include <sys/random.h> + #include <errno.h> + +#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM) + #include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <unistd.h> + #include <errno.h> +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_TARGET_OS_HAS_RTLGENRANDOM) + +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() : m_advapi("advapi32.dll") + { + // This throws if the function is not found + m_rtlgenrandom = m_advapi.resolve<RtlGenRandom_fptr>("SystemFunction036"); + } + + void randomize(uint8_t buf[], size_t len) override + { + bool success = m_rtlgenrandom(buf, ULONG(len)) == TRUE; + if(!success) + throw System_Error("RtlGenRandom failed"); + } + + void add_entropy(const uint8_t[], size_t) override { /* ignored */ } + bool is_seeded() const override { return true; } + bool accepts_input() const override { return false; } + void clear() override { /* not possible */ } + std::string name() const override { return "RtlGenRandom"; } + private: + // Use type BYTE instead of BOOLEAN because of a naming conflict + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694(v=vs.85).aspx + // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx + using RtlGenRandom_fptr = BYTE (NTAPI *)(PVOID, ULONG); + + Dynamically_Loaded_Library m_advapi; + RtlGenRandom_fptr m_rtlgenrandom; + }; + +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) + +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { + NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov, + BCRYPT_RNG_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if(ret != STATUS_SUCCESS) + throw System_Error("System_RNG failed to acquire crypto provider", ret); + } + + ~System_RNG_Impl() + { + ::BCryptCloseAlgorithmProvider(m_prov, 0); + } + + void randomize(uint8_t buf[], size_t len) override + { + NTSTATUS ret = ::BCryptGenRandom(m_prov, static_cast<PUCHAR>(buf), static_cast<ULONG>(len), 0); + if(ret != STATUS_SUCCESS) + throw System_Error("System_RNG call to BCryptGenRandom failed", ret); + } + + void add_entropy(const uint8_t in[], size_t length) override + { + /* + There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide + entropy inputs, but it is ignored in Windows 8 and later. + */ + } + + bool is_seeded() const override { return true; } + bool accepts_input() const override { return false; } + void clear() override { /* not possible */ } + std::string name() const override { return "crypto_ng"; } + private: + BCRYPT_ALG_HANDLE m_prov; + }; + +#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) + +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + // No constructor or destructor needed as no userland state maintained + + void randomize(uint8_t buf[], size_t len) override + { + // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0 + if(len > 0) + { + ::arc4random_buf(buf, len); + } + } + + bool accepts_input() const override { return false; } + void add_entropy(const uint8_t[], size_t) override { /* ignored */ } + bool is_seeded() const override { return true; } + void clear() override { /* not possible */ } + std::string name() const override { return "arc4random"; } + }; + +#elif defined(BOTAN_TARGET_OS_HAS_GETRANDOM) + +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + // No constructor or destructor needed as no userland state maintained + + void randomize(uint8_t buf[], size_t len) override + { + const unsigned int flags = 0; + + while(len > 0) + { + const ssize_t got = ::getrandom(buf, len, flags); + + if(got < 0) + { + if(errno == EINTR) + continue; + throw System_Error("System_RNG getrandom failed", errno); + } + + buf += got; + len -= got; + } + } + + bool accepts_input() const override { return false; } + void add_entropy(const uint8_t[], size_t) override { /* ignored */ } + bool is_seeded() const override { return true; } + void clear() override { /* not possible */ } + std::string name() const override { return "getrandom"; } + }; + + +#elif defined(BOTAN_TARGET_OS_HAS_DEV_RANDOM) + +// Read a random device + +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); + + if(m_fd >= 0) + { + m_writable = true; + } + else + { + /* + Cannot open in read-write mode. Fall back to read-only, + calls to add_entropy will fail, but randomize will work + */ + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); + m_writable = false; + } + + if(m_fd < 0) + throw System_Error("System_RNG failed to open RNG device", errno); + } + + ~System_RNG_Impl() + { + ::close(m_fd); + m_fd = -1; + } + + void randomize(uint8_t buf[], size_t len) override; + void add_entropy(const uint8_t in[], size_t length) override; + bool is_seeded() const override { return true; } + bool accepts_input() const override { return m_writable; } + void clear() override { /* not possible */ } + std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; } + private: + int m_fd; + bool m_writable; + }; + +void System_RNG_Impl::randomize(uint8_t buf[], size_t len) + { + while(len) + { + ssize_t got = ::read(m_fd, buf, len); + + if(got < 0) + { + if(errno == EINTR) + continue; + throw System_Error("System_RNG read failed", errno); + } + if(got == 0) + throw System_Error("System_RNG EOF on device"); // ?!? + + buf += got; + len -= got; + } + } + +void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) + { + if(!m_writable) + return; + + while(len) + { + ssize_t got = ::write(m_fd, input, len); + + if(got < 0) + { + if(errno == EINTR) + continue; + + /* + * This is seen on OS X CI, despite the fact that the man page + * for macOS urandom explicitly states that writing to it is + * supported, and write(2) does not document EPERM at all. + * But in any case EPERM seems indicative of a policy decision + * by the OS or sysadmin that additional entropy is not wanted + * in the system pool, so we accept that and return here, + * since there is no corrective action possible. + * + * In Linux EBADF or EPERM is returned if m_fd is not opened for + * writing. + */ + if(errno == EPERM || errno == EBADF) + return; + + // maybe just ignore any failure here and return? + throw System_Error("System_RNG write failed", errno); + } + + input += got; + len -= got; + } + } + +#endif + +} + +RandomNumberGenerator& system_rng() + { + static System_RNG_Impl g_system_rng; + return g_system_rng; + } + +} diff --git a/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h new file mode 100644 index 0000000000..e0f0181ca1 --- /dev/null +++ b/comm/third_party/botan/src/lib/rng/system_rng/system_rng.h @@ -0,0 +1,43 @@ +/* +* System RNG interface +* (C) 2014,2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_SYSTEM_RNG_H_ +#define BOTAN_SYSTEM_RNG_H_ + +#include <botan/rng.h> + +namespace Botan { + +/** +* Return a shared reference to a global PRNG instance provided by the +* operating system. For instance might be instantiated by /dev/urandom +* or CryptGenRandom. +*/ +BOTAN_PUBLIC_API(2,0) RandomNumberGenerator& system_rng(); + +/* +* Instantiable reference to the system RNG. +*/ +class BOTAN_PUBLIC_API(2,0) System_RNG final : public RandomNumberGenerator + { + public: + std::string name() const override { return system_rng().name(); } + + void randomize(uint8_t out[], size_t len) override { system_rng().randomize(out, len); } + + void add_entropy(const uint8_t in[], size_t length) override { system_rng().add_entropy(in, length); } + + bool is_seeded() const override { return system_rng().is_seeded(); } + + bool accepts_input() const override { return system_rng().accepts_input(); } + + void clear() override { system_rng().clear(); } + }; + +} + +#endif |