diff --git a/doc/migration_guide.rst b/doc/migration_guide.rst index fbccc291182..e9f226c42c5 100644 --- a/doc/migration_guide.rst +++ b/doc/migration_guide.rst @@ -405,3 +405,17 @@ the constructor of the ``XMSS_PrivateKey``. Private XMSS keys created this way use the old derivation logic and can therefore generate new valid signatures. It is recommended to use ``WOTS_Derivation_Method::NIST_SP800_208`` (default) when creating new XMSS keys. + +Random Number Generator +----------------------- + +Fetching a large number of bytes via `randomize_with_input()` from a stateful +RNG will now incorporate the provided "input" data in the first request to the +underlying DRBG only. This applies to such DRBGs that pose a limit on the number +of bytes per request (most notable ``HMAC_DRBG`` with a 64kB default). Botan 2.x +(erroneously) applied the input to *all* underlying DRBG requests in such cases. + +Applications that rely on a static seed for deterministic RNG output might +observe a different byte stream in such cases. As a workaround, users are +advised to "mimick" the legacy behaviour by manually pulling from the RNG in +"byte limit"-sized chunks and provide the "input" with each invocation. diff --git a/src/lib/ffi/ffi_rng.cpp b/src/lib/ffi/ffi_rng.cpp index a178f9d8484..1a7f1087df9 100644 --- a/src/lib/ffi/ffi_rng.cpp +++ b/src/lib/ffi/ffi_rng.cpp @@ -104,34 +104,34 @@ int botan_rng_init_custom(botan_rng_t* rng_out, const char* rng_name, void* cont Custom_RNG& operator=(const Custom_RNG& other) = delete; Custom_RNG& operator=(Custom_RNG&& other) = delete; - void randomize(uint8_t output[], size_t length) override + protected: + void fill_bytes_with_input(std::span output, std::span input) override { - int rc = m_get_cb(m_context, output, length); - if(rc) + if(accepts_input() && !input.empty()) { - throw Botan::Invalid_State("Failed to get random from C callback, rc=" + std::to_string(rc)); + int rc = m_add_entropy_cb(m_context, input.data(), input.size()); + if(rc) + { + throw Botan::Invalid_State("Failed to add entropy via C callback, rc=" + std::to_string(rc)); + } + } + + if(!output.empty()) + { + int rc = m_get_cb(m_context, output.data(), output.size()); + if(rc) + { + throw Botan::Invalid_State("Failed to get random from C callback, rc=" + std::to_string(rc)); + } } } + public: bool accepts_input() const override { return m_add_entropy_cb != nullptr; } - void add_entropy(const uint8_t input[], size_t length) override - { - if(m_add_entropy_cb == nullptr) - { - return; - } - - int rc = m_add_entropy_cb(m_context, input, length); - if(rc) - { - throw Botan::Invalid_State("Failed to add entropy via C callback, rc=" + std::to_string(rc)); - } - } - std::string name() const override { return m_name; diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.cpp b/src/lib/prov/pkcs11/p11_randomgenerator.cpp index 1c41bab1b10..b26fe56dd08 100644 --- a/src/lib/prov/pkcs11/p11_randomgenerator.cpp +++ b/src/lib/prov/pkcs11/p11_randomgenerator.cpp @@ -14,14 +14,17 @@ PKCS11_RNG::PKCS11_RNG(Session& session) : m_session(session) {} -void PKCS11_RNG::randomize(uint8_t output[], std::size_t length) +void PKCS11_RNG::fill_bytes_with_input(std::span output, std::span input) { - module()->C_GenerateRandom(m_session.get().handle(), output, Ulong(length)); - } + if(!input.empty()) + { + module()->C_SeedRandom(m_session.get().handle(), const_cast(input.data()), Ulong(input.size())); + } -void PKCS11_RNG::add_entropy(const uint8_t in[], std::size_t length) - { - module()->C_SeedRandom(m_session.get().handle(), const_cast(in), Ulong(length)); + if(!output.empty()) + { + module()->C_GenerateRandom(m_session.get().handle(), output.data(), Ulong(output.size())); + } } } diff --git a/src/lib/prov/pkcs11/p11_randomgenerator.h b/src/lib/prov/pkcs11/p11_randomgenerator.h index 339cb95a5c8..8216ce5ead6 100644 --- a/src/lib/prov/pkcs11/p11_randomgenerator.h +++ b/src/lib/prov/pkcs11/p11_randomgenerator.h @@ -51,15 +51,14 @@ class BOTAN_PUBLIC_API(2,0) PKCS11_RNG final : public Hardware_RNG return m_session.get().module(); } - /// Calls `C_GenerateRandom` to generate random data - void randomize(uint8_t output[], std::size_t length) override; - - /// Calls `C_SeedRandom` to add entropy to the random generation function of the token/middleware - void add_entropy(const uint8_t in[], std::size_t length) override; - // C_SeedRandom may suceed bool accepts_input() const override { return true; } + private: + /// Calls `C_GenerateRandom` to generate random data + /// Calls `C_SeedRandom` to add entropy to the random generation function of the token/middleware + void fill_bytes_with_input(std::span output, std::span input) override; + private: const std::reference_wrapper m_session; }; diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h index daa782f743e..15b1a3032aa 100644 --- a/src/lib/prov/tpm/tpm.h +++ b/src/lib/prov/tpm/tpm.h @@ -82,20 +82,20 @@ class BOTAN_PUBLIC_API(2,0) TPM_RNG final : public Hardware_RNG bool accepts_input() const override { return true; } - void add_entropy(const uint8_t in[], size_t in_len) override - { - m_ctx.stir_random(in, in_len); - } - - void randomize(uint8_t out[], size_t out_len) override - { - m_ctx.gen_random(out, out_len); - } - std::string name() const override { return "TPM_RNG"; } bool is_seeded() const override { return true; } + private: + void fill_bytes_with_input(std::span output, std::span input) override + { + if(!input.empty()) + { m_ctx.stir_random(input.data(), input.size()); } + + if(!output.empty()) + { m_ctx.gen_random(output.data(), output.size()); } + } + private: TPM_Context& m_ctx; }; diff --git a/src/lib/rng/auto_rng/auto_rng.cpp b/src/lib/rng/auto_rng/auto_rng.cpp index 78ef27216b6..37866eb7fe3 100644 --- a/src/lib/rng/auto_rng/auto_rng.cpp +++ b/src/lib/rng/auto_rng/auto_rng.cpp @@ -7,6 +7,10 @@ #include #include #include +#include +#include + +#include #if defined(BOTAN_HAS_SYSTEM_RNG) #include @@ -107,11 +111,6 @@ 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) @@ -119,15 +118,12 @@ size_t AutoSeeded_RNG::reseed(Entropy_Sources& srcs, 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) +void AutoSeeded_RNG::fill_bytes_with_input(std::span out, std::span in) { - m_rng->randomize_with_input(output, output_len, ad, ad_len); + if(in.empty()) + { m_rng->randomize_with_ts_input(out); } + else + { m_rng->randomize_with_input(out, in); } } } diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng/auto_rng.h index 8cb2c4a1273..9da5c975bcd 100644 --- a/src/lib/rng/auto_rng/auto_rng.h +++ b/src/lib/rng/auto_rng/auto_rng.h @@ -20,11 +20,6 @@ class Stateful_RNG; 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; } @@ -38,8 +33,6 @@ class BOTAN_PUBLIC_API(2,0) AutoSeeded_RNG final : public RandomNumberGenerator 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; @@ -93,6 +86,9 @@ class BOTAN_PUBLIC_API(2,0) AutoSeeded_RNG final : public RandomNumberGenerator ~AutoSeeded_RNG(); + private: + void fill_bytes_with_input(std::span out, std::span in) override; + private: std::unique_ptr m_rng; }; diff --git a/src/lib/rng/chacha_rng/chacha_rng.cpp b/src/lib/rng/chacha_rng/chacha_rng.cpp index 3dc69ec1b8a..94bb44c0324 100644 --- a/src/lib/rng/chacha_rng/chacha_rng.cpp +++ b/src/lib/rng/chacha_rng/chacha_rng.cpp @@ -16,12 +16,12 @@ ChaCha_RNG::ChaCha_RNG() : Stateful_RNG() clear(); } -ChaCha_RNG::ChaCha_RNG(const secure_vector& seed) : Stateful_RNG() +ChaCha_RNG::ChaCha_RNG(std::span 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()); + add_entropy(seed); } ChaCha_RNG::ChaCha_RNG(RandomNumberGenerator& underlying_rng, @@ -58,24 +58,23 @@ void ChaCha_RNG::clear_state() 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) +void ChaCha_RNG::generate_output(std::span output, std::span input) { - if(input_len > 0) + BOTAN_ASSERT_NOMSG(!output.empty()); + + if(!input.empty()) { - update(input, input_len); + update(input); } - m_chacha->write_keystream(output, output_len); + m_chacha->write_keystream(output); } -void ChaCha_RNG::update(const uint8_t input[], size_t input_len) +void ChaCha_RNG::update(std::span input) { - m_hmac->update(input, input_len); + m_hmac->update(input); m_chacha->set_key(m_hmac->final()); - - secure_vector mac_key(m_hmac->output_length()); - m_chacha->write_keystream(mac_key.data(), mac_key.size()); + const auto mac_key = m_chacha->keystream_bytes(m_hmac->output_length()); m_hmac->set_key(mac_key); } diff --git a/src/lib/rng/chacha_rng/chacha_rng.h b/src/lib/rng/chacha_rng/chacha_rng.h index c50c2d0c25f..1002da8c874 100644 --- a/src/lib/rng/chacha_rng/chacha_rng.h +++ b/src/lib/rng/chacha_rng/chacha_rng.h @@ -62,7 +62,7 @@ class BOTAN_PUBLIC_API(2,3) ChaCha_RNG final : public Stateful_RNG * * @param seed the seed material, should be at least 256 bits */ - ChaCha_RNG(const secure_vector& seed); + ChaCha_RNG(std::span seed); /** * Automatic reseeding from @p underlying_rng will take place after @@ -109,10 +109,9 @@ class BOTAN_PUBLIC_API(2,3) ChaCha_RNG final : public Stateful_RNG 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 update(std::span input) override; - void generate_output(uint8_t output[], size_t output_len, - const uint8_t input[], size_t input_len) override; + void generate_output(std::span output, std::span input) override; void clear_state() override; diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index 7357c675d11..67783d58205 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -139,54 +139,54 @@ std::string HMAC_DRBG::name() const * 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) +void HMAC_DRBG::generate_output(std::span output, std::span input) { - if(input_len > 0) + BOTAN_ASSERT_NOMSG(!output.empty()); + + if(!input.empty()) { - update(input, input_len); + update(input); } - while(output_len > 0) + while(!output.empty()) { - 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); + const size_t to_copy = std::min(output.size(), m_V.size()); + m_mac->update(m_V); + m_mac->final(m_V); + copy_mem(output.data(), m_V.data(), to_copy); - output += to_copy; - output_len -= to_copy; + output = output.subspan(to_copy); } - update(input, input_len); + update(input); } /* * 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) +void HMAC_DRBG::update(std::span input) { secure_vector 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->update(input); + m_mac->final(T); m_mac->set_key(T); - m_mac->update(m_V.data(), m_V.size()); - m_mac->final(m_V.data()); + m_mac->update(m_V); + m_mac->final(m_V); - if(input_len > 0) + if(!input.empty()) { m_mac->update(m_V); m_mac->update(0x01); - m_mac->update(input, input_len); - m_mac->final(T.data()); + m_mac->update(input); + m_mac->final(T); m_mac->set_key(T); - m_mac->update(m_V.data(), m_V.size()); - m_mac->final(m_V.data()); + m_mac->update(m_V); + m_mac->final(m_V); } } diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index a4c288c74ba..043111f4d0d 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -132,10 +132,9 @@ class BOTAN_PUBLIC_API(2,0) HMAC_DRBG final : public Stateful_RNG { return m_max_number_of_bytes_per_request; } private: - void update(const uint8_t input[], size_t input_len) override; + void update(std::span input) override; - void generate_output(uint8_t output[], size_t output_len, - const uint8_t input[], size_t input_len) override; + void generate_output(std::span output, std::span input) override; void clear_state() override; diff --git a/src/lib/rng/processor_rng/processor_rng.cpp b/src/lib/rng/processor_rng/processor_rng.cpp index c58b0a73e7e..13a669a994a 100644 --- a/src/lib/rng/processor_rng/processor_rng.cpp +++ b/src/lib/rng/processor_rng/processor_rng.cpp @@ -129,23 +129,25 @@ std::string Processor_RNG::name() const #endif } -void Processor_RNG::randomize(uint8_t out[], size_t out_len) +void Processor_RNG::fill_bytes_with_input(std::span out, std::span in) { - while(out_len >= sizeof(hwrng_output)) + // No way to provide entropy to processor-specific generator, ignore... + BOTAN_UNUSED(in); + + while(out.size() >= sizeof(hwrng_output)) { const hwrng_output r = read_hwrng(); - store_le(r, out); - out += sizeof(hwrng_output); - out_len -= sizeof(hwrng_output); + store_le(r, out.data()); + out = out.subspan(sizeof(hwrng_output)); } - if(out_len > 0) // at most sizeof(hwrng_output)-1 + if(!out.empty()) // at most sizeof(hwrng_output)-1 { const hwrng_output r = read_hwrng(); uint8_t hwrng_bytes[sizeof(hwrng_output)]; store_le(r, hwrng_bytes); - for(size_t i = 0; i != out_len; ++i) + for(size_t i = 0; i != out.size(); ++i) out[i] = hwrng_bytes[i]; } } @@ -156,11 +158,6 @@ Processor_RNG::Processor_RNG() throw Invalid_State("Current CPU does not support RNG instruction"); } -void Processor_RNG::add_entropy(const uint8_t /*input*/[], size_t /*length*/) - { - /* no way to add entropy */ - } - size_t Processor_RNG::reseed(Entropy_Sources& /*srcs*/, size_t /*poll_bits*/, std::chrono::milliseconds /*poll_timeout*/) { /* no way to add entropy */ diff --git a/src/lib/rng/processor_rng/processor_rng.h b/src/lib/rng/processor_rng/processor_rng.h index 5900e386e02..3507b687292 100644 --- a/src/lib/rng/processor_rng/processor_rng.h +++ b/src/lib/rng/processor_rng/processor_rng.h @@ -32,19 +32,15 @@ class BOTAN_PUBLIC_API(2,15) Processor_RNG final : public Hardware_RNG 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; + + private: + void fill_bytes_with_input(std::span out, std::span in) override; }; } diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index 3619ad8fca8..fabb3130eb9 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -9,35 +9,46 @@ #include #include +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#endif + +#include + namespace Botan { -void RandomNumberGenerator::randomize_with_ts_input(uint8_t output[], size_t output_len) +void RandomNumberGenerator::randomize_with_ts_input(std::span output) { 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); + constexpr auto s_hd_clk = sizeof(decltype(OS::get_high_resolution_clock())); + constexpr auto s_sys_ts = sizeof(decltype(OS::get_system_timestamp_ns())); + constexpr auto s_pid = sizeof(decltype(OS::get_process_id())); + + std::array additional_input = {0}; + auto s_additional_input = std::span(additional_input.begin(), additional_input.end()); + + store_le(OS::get_high_resolution_clock(), s_additional_input.data()); + s_additional_input = s_additional_input.subspan(s_hd_clk); + +#if defined(BOTAN_HAS_SYSTEM_RNG) + System_RNG system_rng; + system_rng.randomize(s_additional_input); +#else + store_le(OS::get_system_timestamp_ns(), s_additional_input.data()); + s_additional_input = s_additional_input.subspan(s_sys_ts); - this->randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); + store_le(OS::get_process_id(), s_additional_input.data()); +#endif + + this->fill_bytes_with_input(output, additional_input); } else { - this->randomize(output, output_len); + this->fill_bytes_with_input(output, {}); } } -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) @@ -56,9 +67,7 @@ void RandomNumberGenerator::reseed_from_rng(RandomNumberGenerator& rng, size_t p { if(this->accepts_input()) { - secure_vector buf(poll_bits / 8); - rng.randomize(buf.data(), buf.size()); - this->add_entropy(buf.data(), buf.size()); + this->add_entropy(rng.random_vec(poll_bits / 8)); } } diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index cb9315af001..a6ce5691c82 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -47,11 +47,13 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator * or a retry because of insufficient entropy is needed. * * @param output the byte array to hold the random output. - * @param length the length of the byte array output in bytes. * @throws PRNG_Unseeded if the RNG fails because it has not enough entropy * @throws Exception if the RNG fails */ - virtual void randomize(uint8_t output[], size_t length) = 0; + void randomize(std::span output) + { this->fill_bytes_with_input(output, {}); } + void randomize(uint8_t output[], size_t length) + { this->randomize(std::span(output, length)); } /** * Returns false if it is known that this RNG object is not able to accept @@ -69,11 +71,12 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator * 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 + * @param input a byte array containing the entropy to be added * @throws Exception may throw if the RNG accepts input, but adding the entropy failed. */ - virtual void add_entropy(const uint8_t input[], size_t length) = 0; + void add_entropy(std::span input) { this->fill_bytes_with_input({}, input); } + void add_entropy(const uint8_t input[], size_t length) + { this->add_entropy(std::span(input, length)); } /** * Incorporate some additional data into the RNG state. @@ -95,15 +98,16 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator * 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 * @throws PRNG_Unseeded if the RNG fails because it has not enough entropy * @throws Exception if the RNG fails * @throws Exception may throw if the RNG accepts input, but adding the entropy failed. */ - virtual void randomize_with_input(uint8_t output[], size_t output_len, - const uint8_t input[], size_t input_len); + void randomize_with_input(std::span output, std::span input) + { this->fill_bytes_with_input(output, input); } + void randomize_with_input(uint8_t output[], size_t output_len, + const uint8_t input[], size_t input_len) + { this->randomize_with_input(std::span(output, output_len), std::span(input, input_len)); } /** * This calls `randomize_with_input` using some timestamps as extra input. @@ -115,12 +119,13 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator * timestamps don't themselves repeat), their outputs will diverge. * * @param output buffer to hold the random output - * @param output_len size of the output buffer in bytes * @throws PRNG_Unseeded if the RNG fails because it has not enough entropy * @throws Exception if the RNG fails * @throws Exception may throw if the RNG accepts input, but adding the entropy failed. */ - virtual void randomize_with_ts_input(uint8_t output[], size_t output_len); + void randomize_with_ts_input(std::span output); + void randomize_with_ts_input(uint8_t output[], size_t output_len) + { this->randomize_with_ts_input(std::span(output, output_len)); } /** * @return the name of this RNG type @@ -165,13 +170,13 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator /** * Fill a given byte container with @p bytes random bytes * + * @todo deprecate this overload (in favor of randomize()) + * * @param v the container to be filled with @p bytes random bytes * @throws Exception if RNG fails */ void random_vec(std::span v) - { - this->randomize(v.data(), v.size()); - } + { this->randomize(v); } /** * Resize a given byte container to @p bytes and fill it with random bytes @@ -214,7 +219,7 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator uint8_t next_byte() { uint8_t b; - this->randomize(&b, 1); + this->fill_bytes_with_input(std::span(&b, 1), {}); return b; } @@ -230,6 +235,24 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator b = this->next_byte(); return b; } + + protected: + /** + * Generic interface to provide entropy to a concrete implementation and to + * fill a given buffer with random output. Both @p output and @p input may + * be empty and should be ignored in that case. If both buffers are + * non-empty implementations should typically first apply the @p input data + * and then generate random data into @p output. + * + * This method must be implemented by all RandomNumberGenerator sub-classes. + * + * @param output Byte buffer to write random bytes into. Implementations + * should not read from this buffer. + * @param input Byte buffer that may contain bytes to be incorporated in + * the RNG's internal state. Implementations may choose to + * ignore the bytes in this buffer. + */ + virtual void fill_bytes_with_input(std::span output, std::span input) = 0; }; /** @@ -259,14 +282,15 @@ class BOTAN_PUBLIC_API(2,0) Null_RNG final : public RandomNumberGenerator void clear() override {} - void randomize(uint8_t[], size_t) override + std::string name() const override { return "Null_RNG"; } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { - throw PRNG_Unseeded("Null_RNG called"); + // throw if caller tries to obtain random bytes + if(output.size() > 0) + { throw PRNG_Unseeded("Null_RNG called"); } } - - void add_entropy(const uint8_t[], size_t) override {} - - std::string name() const override { return "Null_RNG"; } }; } diff --git a/src/lib/rng/stateful_rng/stateful_rng.cpp b/src/lib/rng/stateful_rng/stateful_rng.cpp index e9ab21212cb..d877636e1f6 100644 --- a/src/lib/rng/stateful_rng/stateful_rng.cpp +++ b/src/lib/rng/stateful_rng/stateful_rng.cpp @@ -8,10 +8,6 @@ #include #include -#if defined(BOTAN_HAS_SYSTEM_RNG) - #include -#endif - namespace Botan { void Stateful_RNG::clear() @@ -34,83 +30,60 @@ bool Stateful_RNG::is_seeded() const return m_reseed_counter > 0; } -void Stateful_RNG::add_entropy(const uint8_t input[], size_t input_len) - { - lock_guard_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) +void Stateful_RNG::initialize_with(std::span input) { lock_guard_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)); + add_entropy(input); } -void Stateful_RNG::randomize_with_input(uint8_t output[], size_t output_len, - const uint8_t input[], size_t input_len) +void Stateful_RNG::generate_batched_output(std::span output, std::span input) { - if(output_len == 0) - return; - - lock_guard_type lock(m_mutex); + BOTAN_ASSERT_NOMSG(!output.empty()); 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); + this->generate_output(output, input); } else { - while(output_len > 0) + while(!output.empty()) { - 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. - */ + const size_t this_req = std::min(max_per_request, output.size()); reseed_check(); - this->generate_output(output, this_req, input, input_len); + this->generate_output(output.subspan(0, this_req), input); + + // only include the input for the first iteration + input = {}; + + output = output.subspan(this_req); + } + } + } - output += this_req; - output_len -= this_req; +void Stateful_RNG::fill_bytes_with_input(std::span output, std::span input) + { + lock_guard_type lock(m_mutex); + + if(output.empty()) + { + // Special case for exclusively adding entropy to the stateful RNG. + this->update(input); + + if(8*input.size() >= security_level()) + { + reset_reseed_counter(); } } + else + { + generate_batched_output(output, input); + } } size_t Stateful_RNG::reseed(Entropy_Sources& srcs, diff --git a/src/lib/rng/stateful_rng/stateful_rng.h b/src/lib/rng/stateful_rng/stateful_rng.h index e1311fcc9ff..2ecdeaf651f 100644 --- a/src/lib/rng/stateful_rng/stateful_rng.h +++ b/src/lib/rng/stateful_rng/stateful_rng.h @@ -72,7 +72,11 @@ class BOTAN_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator * of the length of the input or the current seeded state of * the RNG. */ - void initialize_with(const uint8_t input[], size_t length); + void initialize_with(std::span input); + void initialize_with(const uint8_t input[], size_t length) + { + this->initialize_with(std::span(input, length)); + } bool is_seeded() const override final; @@ -86,19 +90,6 @@ class BOTAN_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator 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 @@ -132,14 +123,17 @@ class BOTAN_PUBLIC_API(2,0) Stateful_RNG : public RandomNumberGenerator 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 generate_output(std::span output, std::span input) = 0; - virtual void update(const uint8_t input[], size_t input_len) = 0; + virtual void update(std::span input) = 0; virtual void clear_state() = 0; private: + void generate_batched_output(std::span output, std::span input); + + void fill_bytes_with_input(std::span output, std::span input) override final; + void reset_reseed_counter(); mutable recursive_mutex_type m_mutex; diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp index 1c29f7081b1..8278b688b81 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -51,12 +51,18 @@ class System_RNG_Impl final : public RandomNumberGenerator System_RNG_Impl& operator=(const System_RNG_Impl& other) = delete; System_RNG_Impl& operator=(System_RNG_Impl&& other) = delete; - void randomize(uint8_t buf[], size_t len) override + 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: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { const size_t limit = std::numeric_limits::max(); - uint8_t* pData = buf; - size_t bytesLeft = len; + uint8_t* pData = output.data(); + size_t bytesLeft = output.size(); while (bytesLeft > 0) { const ULONG blockSize = static_cast(std::min(bytesLeft, limit)); @@ -73,11 +79,6 @@ class System_RNG_Impl final : public RandomNumberGenerator } } - 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: using RtlGenRandom_fptr = BOOLEAN (NTAPI *)(PVOID, ULONG); @@ -111,12 +112,23 @@ class System_RNG_Impl final : public RandomNumberGenerator ::BCryptCloseAlgorithmProvider(m_prov, 0); } - void randomize(uint8_t buf[], size_t len) override + 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: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { + /* + There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide + entropy inputs, but it is ignored in Windows 8 and later. + */ + const size_t limit = std::numeric_limits::max(); - uint8_t* pData = buf; - size_t bytesLeft = len; + uint8_t* pData = output.data(); + size_t bytesLeft = output.size(); while (bytesLeft > 0) { const ULONG blockSize = static_cast(std::min(bytesLeft, limit)); @@ -133,18 +145,6 @@ class System_RNG_Impl final : public RandomNumberGenerator } } - 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; }; @@ -154,18 +154,19 @@ class System_RNG_Impl final : public RandomNumberGenerator class System_RNG_Impl final : public RandomNumberGenerator { public: - void randomize(uint8_t buf[], size_t len) override + bool accepts_input() const override { return false; } + bool is_seeded() const override { return true; } + void clear() override { /* not possible */ } + std::string name() const override { return "CCRandomGenerateBytes"; } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { - if (::CCRandomGenerateBytes(buf, len) != kCCSuccess) + if (::CCRandomGenerateBytes(output.data(), output.size()) != kCCSuccess) { throw System_Error("System_RNG CCRandomGenerateBytes failed", errno); } } - 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 "CCRandomGenerateBytes"; } }; #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) @@ -175,22 +176,22 @@ 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 + bool accepts_input() const override { return false; } + bool is_seeded() const override { return true; } + void clear() override { /* not possible */ } + std::string name() const override { return "arc4random"; } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { // macOS 10.15 arc4random crashes if called with buf == nullptr && len == 0 - // however it uses ccrng_generate internally which returns a status, ignored - // to respect arc4random "no-fail" interface contract - if(len > 0) + // however it uses ccrng_generate internally which returns a status, ignored + // to respect arc4random "no-fail" interface contract + if(!output.empty()) { - ::arc4random_buf(buf, len); + ::arc4random_buf(output.data(), output.size()); } } - - 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) @@ -200,10 +201,18 @@ 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 + bool accepts_input() const override { return false; } + bool is_seeded() const override { return true; } + void clear() override { /* not possible */ } + std::string name() const override { return "getrandom"; } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { const unsigned int flags = 0; + uint8_t* buf = output.data(); + size_t len = output.size(); while(len > 0) { const ssize_t got = ::getrandom(buf, len, flags); @@ -219,12 +228,6 @@ class System_RNG_Impl final : public RandomNumberGenerator 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"; } }; @@ -287,19 +290,26 @@ class System_RNG_Impl final : public RandomNumberGenerator 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 "urandom"; } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override; + void maybe_write_entropy(std::span input); + private: int m_fd; bool m_writable; }; -void System_RNG_Impl::randomize(uint8_t buf[], size_t len) +void System_RNG_Impl::fill_bytes_with_input(std::span output, std::span input) { + maybe_write_entropy(input); + + uint8_t* buf = output.data(); + size_t len = output.size(); while(len) { ssize_t got = ::read(m_fd, buf, len); @@ -318,11 +328,13 @@ void System_RNG_Impl::randomize(uint8_t buf[], size_t len) } } -void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) +void System_RNG_Impl::maybe_write_entropy(std::span entropy_input) { - if(!m_writable) + if(!m_writable || entropy_input.empty()) return; + const uint8_t* input = entropy_input.data(); + size_t len = entropy_input.size(); while(len) { ssize_t got = ::write(m_fd, input, len); diff --git a/src/lib/rng/system_rng/system_rng.h b/src/lib/rng/system_rng/system_rng.h index e0f0181ca18..9bb04365899 100644 --- a/src/lib/rng/system_rng/system_rng.h +++ b/src/lib/rng/system_rng/system_rng.h @@ -27,15 +27,16 @@ 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(); } + + protected: + void fill_bytes_with_input(std::span out, std::span in) override + { system_rng().randomize_with_input(out, in); } + }; } diff --git a/src/tests/runner/test_runner.cpp b/src/tests/runner/test_runner.cpp index 50129dd6cb8..a7bc2c63d4b 100644 --- a/src/tests/runner/test_runner.cpp +++ b/src/tests/runner/test_runner.cpp @@ -45,25 +45,18 @@ class Testsuite_RNG final : public Botan::RandomNumberGenerator bool accepts_input() const override { return true; } - void add_entropy(const uint8_t data[], size_t len) override - { - for(size_t i = 0; i != len; ++i) - { - mix(data[i]); - } - } - bool is_seeded() const override { return true; } - void randomize(uint8_t out[], size_t len) override + void fill_bytes_with_input(std::span output, std::span input) override { - for(size_t i = 0; i != len; ++i) - { - out[i] = mix(); - } + for(const auto byte : input) + { mix(byte); } + + for(auto& byte : output) + { byte = mix(); } } Testsuite_RNG(const std::vector& seed, uint64_t test_counter) diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h index 28764cff4ef..e4ee8133801 100644 --- a/src/tests/test_rng.h +++ b/src/tests/test_rng.h @@ -42,19 +42,6 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator return 0; } - void randomize(uint8_t out[], size_t len) override - { - for(size_t j = 0; j != len; j++) - { - out[j] = random(); - } - } - - void add_entropy(const uint8_t b[], size_t s) override - { - m_buf.insert(m_buf.end(), b, b + s); - } - std::string name() const override { return "Fixed_Output_RNG"; @@ -88,7 +75,16 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator : m_fallback(&fallback_rng) {} Fixed_Output_RNG() = default; + protected: + void fill_bytes_with_input(std::span output, std::span input) override + { + m_buf.insert(m_buf.end(), input.begin(), input.end()); + + for(auto& o : output) + { o = random(); } + } + uint8_t random() { if(m_buf.empty()) @@ -121,32 +117,8 @@ class Fixed_Output_Position_RNG final : public Fixed_Output_RNG return Fixed_Output_RNG::is_seeded() || Test::rng().is_seeded(); } - void randomize(uint8_t out[], size_t len) override - { - ++m_requests; - - if(m_requests == m_pos) - { - // return fixed output - for(size_t j = 0; j != len; j++) - { - out[j] = random(); - } - } - else - { - // return random - Test::rng().randomize(out, len); - } - } - bool accepts_input() const override { return false; } - void add_entropy(const uint8_t*, size_t) override - { - throw Test_Error("add_entropy() not supported by this RNG, test bug?"); - } - std::string name() const override { return "Fixed_Output_Position_RNG"; @@ -160,6 +132,26 @@ class Fixed_Output_Position_RNG final : public Fixed_Output_RNG : Fixed_Output_RNG(in_str) , m_pos(pos) {} + private: + void fill_bytes_with_input(std::span output, std::span input) override + { + if(!input.empty()) + { throw Test_Error("add_entropy() not supported by this RNG, test bug?"); } + + ++m_requests; + + if(m_requests == m_pos) + { + // return fixed output + Fixed_Output_RNG::fill_bytes_with_input(output, input); + } + else + { + // return random + Test::rng().random_vec(output); + } + } + private: size_t m_pos = 0; size_t m_requests = 0; @@ -168,19 +160,8 @@ class Fixed_Output_Position_RNG final : public Fixed_Output_RNG class SeedCapturing_RNG final : public Botan::RandomNumberGenerator { public: - void randomize(uint8_t[], size_t) override - { - throw Test_Error("SeedCapturing_RNG has no output"); - } - bool accepts_input() const override { return true; } - void add_entropy(const uint8_t input[], size_t len) override - { - m_samples++; - m_seed.insert(m_seed.end(), input, input + len); - } - void clear() override {} bool is_seeded() const override { @@ -201,6 +182,16 @@ class SeedCapturing_RNG final : public Botan::RandomNumberGenerator return m_seed; } + private: + void fill_bytes_with_input(std::span output, std::span input) override + { + if(!output.empty()) + { throw Test_Error("SeedCapturing_RNG has no output"); } + + m_samples++; + m_seed.insert(m_seed.end(), input.begin(), input.end()); + } + private: std::vector m_seed; size_t m_samples = 0; @@ -232,22 +223,22 @@ class Request_Counting_RNG final : public Botan::RandomNumberGenerator m_randomize_count = 0; } - void randomize(uint8_t out[], size_t out_len) override + std::string name() const override + { + return "Request_Counting_RNG"; + } + + private: + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { /* The HMAC_DRBG and ChaCha reseed KATs assume this RNG type outputs all 0x80 */ - for(size_t i = 0; i != out_len; ++i) - out[i] = 0x80; - m_randomize_count++; - } - - void add_entropy(const uint8_t[], size_t) override {} - - std::string name() const override - { - return "Request_Counting_RNG"; + for(auto& out : output) + out = 0x80; + if(!output.empty()) + m_randomize_count++; } private: @@ -268,21 +259,19 @@ class CTR_DRBG_AES256 final : public Botan::RandomNumberGenerator bool accepts_input() const override { return true; } - void add_entropy(const uint8_t seed_material[], size_t len) override; - bool is_seeded() const override { return true; } - void randomize(uint8_t out[], size_t len) override; - - CTR_DRBG_AES256(const std::vector& seed); + CTR_DRBG_AES256(std::span seed); private: - void incr_V_into(uint8_t output[16]); + void fill_bytes_with_input(std::span output, std::span input) override; + + void incr_V_into(std::span output); - void update(const uint8_t provided_data[]); + void update(std::span provided_data); uint64_t m_V0, m_V1; std::unique_ptr m_cipher; diff --git a/src/tests/test_rng_behavior.cpp b/src/tests/test_rng_behavior.cpp index 0e565838c3c..1e4da3f203b 100644 --- a/src/tests/test_rng_behavior.cpp +++ b/src/tests/test_rng_behavior.cpp @@ -596,7 +596,68 @@ class HMAC_DRBG_Unit_Tests final : public Stateful_RNG_Tests }; +std::vector hmac_drbg_multiple_requests() + { + auto null_rng = Botan::Null_RNG(); + constexpr auto rng_max_output = 1024; + const auto seed = Botan::hex_decode("deadbeefbaadcafedeadbeefbaadcafedeadbeefbaadcafedeadbeefbaadcafe"); + + auto make_seeded_rng = [&](size_t reseed_interval) + { + auto rng = std::make_unique( + Botan::MessageAuthenticationCode::create("HMAC(SHA-256)"), + null_rng, reseed_interval + 1 /* off by one */, rng_max_output); + rng->add_entropy(seed); + return rng; + }; + + return + { + Botan_Tests::CHECK("bulk and split output without input", [&](auto& result) + { + auto rng1 = make_seeded_rng(2); + auto rng2 = make_seeded_rng(2); + + result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded()); + result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded()); + + auto bulk = rng1->random_vec>(2*rng_max_output); + + auto split1 = rng2->random_vec>(rng_max_output); + auto split2 = rng2->random_vec>(rng_max_output); + split1.insert(split1.end(), split2.begin(), split2.end()); + + result.test_eq("Output is equal, regardless bulk request", bulk, split1); + + return result; + }), + + Botan_Tests::CHECK("bulk and split output with input", [&](auto& result) + { + auto rng1 = make_seeded_rng(3); + auto rng2 = make_seeded_rng(3); + + result.confirm("RNG 1 is seeded and ready to go", rng1->is_seeded()); + result.confirm("RNG 2 is seeded and ready to go", rng2->is_seeded()); + + std::vector bulk(3*rng_max_output); + rng1->randomize_with_input(bulk, seed); + + std::vector split(3*rng_max_output); + std::span split_span(split); + rng2->randomize_with_input(split_span.subspan(0, rng_max_output), seed); + rng2->randomize_with_input(split_span.subspan(rng_max_output, rng_max_output), {}); + rng2->randomize_with_input(split_span.subspan(2*rng_max_output), {}); + + result.test_eq("Output is equal, regardless bulk request", bulk, split); + + return result; + }) + }; + } + BOTAN_REGISTER_TEST("rng", "hmac_drbg_unit", HMAC_DRBG_Unit_Tests); +BOTAN_REGISTER_TEST_FN("rng", "hmac_drbg_multi_requst", hmac_drbg_multiple_requests); #endif diff --git a/src/tests/test_rngs.cpp b/src/tests/test_rngs.cpp index c44c66af6f9..27c8e2857c2 100644 --- a/src/tests/test_rngs.cpp +++ b/src/tests/test_rngs.cpp @@ -10,6 +10,8 @@ #include #endif +#include + namespace Botan_Tests { #if defined(BOTAN_HAS_AES) @@ -22,72 +24,79 @@ void CTR_DRBG_AES256::clear() m_V1 = 0; } -void CTR_DRBG_AES256::add_entropy(const uint8_t seed_material[], size_t len) +void CTR_DRBG_AES256::fill_bytes_with_input(std::span output, std::span input) { - if(len != 48) - throw Test_Error("CTR_DRBG(AES-256) assumes 48 byte input"); + if(!input.empty()) + { + if(input.size() != 48) + throw Test_Error("CTR_DRBG(AES-256) assumes 48 byte input"); - clear(); - update(seed_material); - } + clear(); + update(input); + } -void CTR_DRBG_AES256::randomize(uint8_t out[], size_t len) - { - const size_t full_blocks = len / 16; - const size_t leftover_bytes = len % 16; + if(!output.empty()) + { + const size_t full_blocks = output.size() / 16; + const size_t leftover_bytes = output.size() % 16; - for(size_t i = 0; i != full_blocks; ++i) - incr_V_into(out + 16*i); + for(size_t i = 0; i != full_blocks; ++i) + incr_V_into(output.subspan(i*16, 16)); - m_cipher->encrypt_n(out, out, full_blocks); + m_cipher->encrypt_n(output.data(), output.data(), full_blocks); - if(leftover_bytes > 0) - { - uint8_t block[16]; - incr_V_into(block); - m_cipher->encrypt(block); - Botan::copy_mem(out + full_blocks * 16, block, leftover_bytes); - } + if(leftover_bytes > 0) + { + uint8_t block[16]; + incr_V_into(block); + m_cipher->encrypt(block); + Botan::copy_mem(output.subspan(full_blocks * 16).data(), block, leftover_bytes); + } - update(nullptr); + update({}); + } } -CTR_DRBG_AES256::CTR_DRBG_AES256(const std::vector& seed) +CTR_DRBG_AES256::CTR_DRBG_AES256(std::span seed) { m_cipher = Botan::BlockCipher::create_or_throw("AES-256"); - add_entropy(seed.data(), seed.size()); + add_entropy(seed); } -void CTR_DRBG_AES256::incr_V_into(uint8_t output[16]) +void CTR_DRBG_AES256::incr_V_into(std::span output) { + BOTAN_ASSERT_NOMSG(output.size() == 16); + m_V1 += 1; if(m_V1 == 0) m_V0 += 1; - Botan::store_be(output, m_V0, m_V1); + Botan::store_be(output.data(), m_V0, m_V1); } -void CTR_DRBG_AES256::update(const uint8_t provided_data[]) +void CTR_DRBG_AES256::update(std::span provided_data) { - uint8_t temp[3*16] = { 0 }; + std::array temp = { 0 }; + std::span t(temp); for(size_t i = 0; i != 3; ++i) { - incr_V_into(temp + 16*i); + incr_V_into(t.subspan(16*i, 16)); } - m_cipher->encrypt_n(temp, temp, 3); + m_cipher->encrypt_n(temp.data(), temp.data(), 3); - if(provided_data) + if(!provided_data.empty()) { - for(size_t i = 0; i != 48; i++) + BOTAN_ASSERT_NOMSG(provided_data.size() == temp.size()); + for(size_t i = 0; i != provided_data.size(); i++) temp[i] ^= provided_data[i]; } - m_cipher->set_key(temp, 32); + m_cipher->set_key(temp.data(), 32); // TODO: adapt after GH #3297 - m_V0 = Botan::load_be(temp + 32, 0); - m_V1 = Botan::load_be(temp + 32, 1); + m_V0 = Botan::load_be(temp.data() + 32, 0); + m_V1 = Botan::load_be(temp.data() + 32, 1); } #endif diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index fa3a379b06b..15a29ec8399 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -735,15 +735,14 @@ class UUID_Tests : public Test public: explicit AllSame_RNG(uint8_t b) : m_val(b) {} - void randomize(uint8_t out[], size_t len) override + void fill_bytes_with_input(std::span output, std::span /* ignored */) override { - for(size_t i = 0; i != len; ++i) - out[i] = m_val; + for(auto& byte : output) + { byte = m_val; } } std::string name() const override { return "zeros"; } bool accepts_input() const override { return false; } - void add_entropy(const uint8_t /*input*/[], size_t /*length*/) override {} void clear() override {} bool is_seeded() const override { return true; } private: