diff options
author | Jack Lloyd <[email protected]> | 2016-07-18 15:36:27 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2016-07-18 15:36:27 -0400 |
commit | e33c989417b7ad9557b02936a1a814e37bf85fcd (patch) | |
tree | c36944002c46e7098d4ab33209c2deaca391b4c1 /src/lib | |
parent | 7438e0b4c85403ba6f4c864a76cd8865f9659ed3 (diff) | |
parent | 8f2f800c7ea841fa5ab963349178ac3a9f56a513 (diff) |
Merge GH #520 RNG changes
Adds Stateful_RNG base class which handles reseeding after some
amount of output (configurable at instantiation time, defaults to
the build.h value) as well as detecting forks (just using pid
comparisons, so still vulnerable to pid wraparound). Implemented
by HMAC_RNG and HMAC_DRBG. I did not update X9.31 since its
underlying RNG should already be fork safe and handle reseeding
at the appropriate time, since a new block is taken from the
underlying RNG (for the datetime vector) for each block of
output.
Adds RNG::randomize_with_input which for most PRNGs is just a
call to add_entropy followed by randomize. However for HMAC_DRBG
it is used for additional input. Adds tests for HMAC_DRBG with AD
from the CAVS file.
RNG::add_entropy is implemented by System_RNG now, as both
CryptGenRandom and /dev/urandom support receiving application
provided data.
The AutoSeeded_RNG underlying type is currently selectable in
build.h and defaults to HMAC_DRBG(SHA-256). AutoSeeded_RNG
provides additional input with each output request, consisting of
the current pid, a counter, and timestamp (unless the application
explicitly calls randomize_with_input, in which case we just take
what they provided). This is the same hedge used in HMAC_RNGs
output PRF.
AutoSeeded_RNG is part of the base library now and cannot be
compiled out.
Removes Entropy_Accumulator type (which just served to bridge
between the RNG and the entropy source), instead the
Entropy_Source is passed a reference to the RNG being reseeded,
and it can call add_entropy on whatever it can come up with.
Diffstat (limited to 'src/lib')
43 files changed, 837 insertions, 695 deletions
diff --git a/src/lib/entropy/beos_stats/es_beos.cpp b/src/lib/entropy/beos_stats/es_beos.cpp index aa0e257a9..fb9be6f86 100644 --- a/src/lib/entropy/beos_stats/es_beos.cpp +++ b/src/lib/entropy/beos_stats/es_beos.cpp @@ -16,48 +16,51 @@ namespace Botan { /** * BeOS entropy poll */ -void BeOS_EntropySource::poll(Entropy_Accumulator& accum) +size_t BeOS_EntropySource::poll(RandomNumberGenerator& rng) { + size_t bits = 0; + system_info info_sys; get_system_info(&info_sys); - accum.add(info_sys, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_sys); key_info info_key; // current state of the keyboard get_key_info(&info_key); - accum.add(info_key, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_key); team_info info_team; int32 cookie_team = 0; while(get_next_team_info(&cookie_team, &info_team) == B_OK) { - accum.add(info_team, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_team); team_id id = info_team.team; int32 cookie = 0; thread_info info_thr; while(get_next_thread_info(id, &cookie, &info_thr) == B_OK) - accum.add(info_thr, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_thr); cookie = 0; image_info info_img; while(get_next_image_info(id, &cookie, &info_img) == B_OK) - accum.add(info_img, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_img); cookie = 0; sem_info info_sem; while(get_next_sem_info(id, &cookie, &info_sem) == B_OK) - accum.add(info_sem, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_sem); cookie = 0; area_info info_area; while(get_next_area_info(id, &cookie, &info_area) == B_OK) - accum.add(info_area, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy(info_area); - if(accum.polling_finished()) - break; + bits += 32; } + + return bits; } } diff --git a/src/lib/entropy/beos_stats/es_beos.h b/src/lib/entropy/beos_stats/es_beos.h index a5b90a607..e40433b6c 100644 --- a/src/lib/entropy/beos_stats/es_beos.h +++ b/src/lib/entropy/beos_stats/es_beos.h @@ -20,7 +20,7 @@ class BeOS_EntropySource final : public Entropy_Source private: std::string name() const override { return "system_stats"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.cpp b/src/lib/entropy/cryptoapi_rng/es_capi.cpp index c9d8fb7c4..a1d809d0d 100644 --- a/src/lib/entropy/cryptoapi_rng/es_capi.cpp +++ b/src/lib/entropy/cryptoapi_rng/es_capi.cpp @@ -1,6 +1,6 @@ /* * Win32 CryptoAPI EntropySource -* (C) 1999-2009 Jack Lloyd +* (C) 1999-2009,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -16,38 +16,34 @@ namespace Botan { namespace { -class CSP_Handle +class CSP_Handle_Impl : public Win32_CAPI_EntropySource::CSP_Handle { public: - explicit CSP_Handle(u64bit capi_provider) + explicit CSP_Handle_Impl(u64bit capi_provider) { - m_valid = false; - DWORD prov_type = (DWORD)capi_provider; - - if(CryptAcquireContext(&m_handle, 0, 0, - prov_type, CRYPT_VERIFYCONTEXT)) - m_valid = true; + m_valid = ::CryptAcquireContext(&m_handle, + 0, + 0, + static_cast<DWORD>(capi_provider), + CRYPT_VERIFYCONTEXT); } - ~CSP_Handle() + ~CSP_Handle_Impl() { - if(is_valid()) - CryptReleaseContext(m_handle, 0); + if(m_valid) + ::CryptReleaseContext(m_handle, 0); } size_t gen_random(byte out[], size_t n) const { - if(is_valid() && CryptGenRandom(m_handle, static_cast<DWORD>(n), out)) + if(m_valid && ::CryptGenRandom(m_handle, static_cast<DWORD>(n), out)) return n; return 0; } - bool is_valid() const { return m_valid; } - - HCRYPTPROV get_handle() const { return m_handle; } private: - HCRYPTPROV m_handle; bool m_valid; + HCRYPTPROV m_handle; }; } @@ -55,20 +51,23 @@ class CSP_Handle /* * Gather Entropy from Win32 CAPI */ -void Win32_CAPI_EntropySource::poll(Entropy_Accumulator& accum) +size_t Win32_CAPI_EntropySource::poll(RandomNumberGenerator& rng) { - secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + secure_vector<uint8_t> buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + size_t bits = 0; - for(size_t i = 0; i != m_prov_types.size(); ++i) + for(size_t i = 0; i != m_csp_provs.size(); ++i) { - CSP_Handle csp(m_prov_types[i]); + size_t got = m_csp_provs[i]->gen_random(buf.data(), buf.size()); - if(size_t got = csp.gen_random(buf.data(), buf.size())) + if(got > 0) { - accum.add(buf.data(), got, BOTAN_ENTROPY_ESTIMATE_STRONG_RNG); - break; + rng.add_entropy(buf.data(), got); + bits += got * 8; } } + + return bits; } /* @@ -76,18 +75,21 @@ void Win32_CAPI_EntropySource::poll(Entropy_Accumulator& accum) */ Win32_CAPI_EntropySource::Win32_CAPI_EntropySource(const std::string& provs) { - std::vector<std::string> capi_provs = split_on(provs, ':'); - - for(size_t i = 0; i != capi_provs.size(); ++i) + for(std::string prov_name : split_on(provs, ':')) { - if(capi_provs[i] == "RSA_FULL") m_prov_types.push_back(PROV_RSA_FULL); - if(capi_provs[i] == "INTEL_SEC") m_prov_types.push_back(PROV_INTEL_SEC); - if(capi_provs[i] == "FORTEZZA") m_prov_types.push_back(PROV_FORTEZZA); - if(capi_provs[i] == "RNG") m_prov_types.push_back(PROV_RNG); + DWORD prov_type; + + if(prov_name == "RSA_FULL") + prov_type = PROV_RSA_FULL; + else if(prov_name == "INTEL_SEC") + prov_type = PROV_INTEL_SEC; + else if(prov_name == "RNG") + prov_type = PROV_RNG; + else + continue; + + m_csp_provs.push_back(std::unique_ptr<CSP_Handle>(new CSP_Handle_Impl(prov_type))); } - - if(m_prov_types.size() == 0) - m_prov_types.push_back(PROV_RSA_FULL); } } diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.h b/src/lib/entropy/cryptoapi_rng/es_capi.h index b1c60bfa1..82a779672 100644 --- a/src/lib/entropy/cryptoapi_rng/es_capi.h +++ b/src/lib/entropy/cryptoapi_rng/es_capi.h @@ -21,15 +21,21 @@ class Win32_CAPI_EntropySource final : public Entropy_Source public: std::string name() const override { return "win32_cryptoapi"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; - /** - * Win32_Capi_Entropysource Constructor - * @param provs list of providers, separated by ':' - */ + /** + * Win32_Capi_Entropysource Constructor + * @param provs list of providers, separated by ':' + */ explicit Win32_CAPI_EntropySource(const std::string& provs = ""); + + class CSP_Handle + { + public: + virtual size_t gen_random(byte out[], size_t n) const = 0; + }; private: - std::vector<u64bit> m_prov_types; + std::vector<std::unique_ptr<CSP_Handle>> m_csp_provs; }; } diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp index 0a6b85955..b53e4061e 100644 --- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp +++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp @@ -14,13 +14,14 @@ namespace Botan { /** * Gather entropy from SecRandomCopyBytes */ -void Darwin_SecRandom::poll(Entropy_Accumulator& accum) +size_t Darwin_SecRandom::poll(RandomNumberGenerator& rng) { - secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + secure_vector<uint8_t> buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); if(0 == SecRandomCopyBytes(kSecRandomDefault, buf.size(), buf.data())) { - accum.add(buf.data(), buf.size(), BOTAN_ENTROPY_ESTIMATE_STRONG_RNG); + rng.add_entropy(buf.data(), buf.size()); + return buf.size() * 8; } } diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h index 09cdc208d..e1c012459 100644 --- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h +++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h @@ -20,7 +20,7 @@ class Darwin_SecRandom final : public Entropy_Source public: std::string name() const override { return "darwin_secrandom"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/entropy/dev_random/dev_random.cpp b/src/lib/entropy/dev_random/dev_random.cpp index aca161d64..b51f19ecb 100644 --- a/src/lib/entropy/dev_random/dev_random.cpp +++ b/src/lib/entropy/dev_random/dev_random.cpp @@ -6,6 +6,7 @@ */ #include <botan/internal/dev_random.h> +#include <botan/exceptn.h> #include <sys/types.h> #include <sys/select.h> @@ -31,14 +32,36 @@ Device_EntropySource::Device_EntropySource(const std::vector<std::string>& fsnam const int flags = O_RDONLY | O_NONBLOCK | O_NOCTTY; + m_max_fd = 0; + for(auto fsname : fsnames) { - fd_type fd = ::open(fsname.c_str(), flags); + int fd = ::open(fsname.c_str(), flags); - if(fd >= 0 && fd < FD_SETSIZE) - m_devices.push_back(fd); - else if(fd >= 0) - ::close(fd); + if(fd > 0) + { + if(fd > FD_SETSIZE) + { + ::close(fd); + throw Exception("Open of OS RNG succeeded but fd is too large for fd_set"); + } + + m_dev_fds.push_back(fd); + m_max_fd = std::max(m_max_fd, fd); + } + else + { + /* + ENOENT or EACCES is normal as some of the named devices may not exist + on this system. But any other errno value probably indicates + either a bug in the application or file descriptor exhaustion. + */ + if(errno != ENOENT && errno != EACCES) + { + throw Exception("Opening OS RNG device failed with errno " + + std::to_string(errno)); + } + } } } @@ -47,46 +70,55 @@ Device_EntropySource destructor: close all open devices */ Device_EntropySource::~Device_EntropySource() { - for(size_t i = 0; i != m_devices.size(); ++i) - ::close(m_devices[i]); + for(int fd : m_dev_fds) + { + // ignoring return value here, can't throw in destructor anyway + ::close(fd); + } } /** * Gather entropy from a RNG device */ -void Device_EntropySource::poll(Entropy_Accumulator& accum) +size_t Device_EntropySource::poll(RandomNumberGenerator& rng) { - if(m_devices.empty()) - return; + size_t bits = 0; - fd_type max_fd = m_devices[0]; - fd_set read_set; - FD_ZERO(&read_set); - for(size_t i = 0; i != m_devices.size(); ++i) + if(m_dev_fds.size() > 0) { - FD_SET(m_devices[i], &read_set); - max_fd = std::max(m_devices[i], max_fd); - } - - struct ::timeval timeout; + fd_set read_set; + FD_ZERO(&read_set); - timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000); - timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000; + for(int dev_fd : m_dev_fds) + { + FD_SET(dev_fd, &read_set); + } - if(::select(max_fd + 1, &read_set, nullptr, nullptr, &timeout) < 0) - return; + secure_vector<uint8_t> io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); - secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + struct ::timeval timeout; + timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000); + timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000; - for(size_t i = 0; i != m_devices.size(); ++i) - { - if(FD_ISSET(m_devices[i], &read_set)) + if(::select(m_max_fd + 1, &read_set, nullptr, nullptr, &timeout) > 0) { - const ssize_t got = ::read(m_devices[i], buf.data(), buf.size()); - if(got > 0) - accum.add(buf.data(), got, BOTAN_ENTROPY_ESTIMATE_STRONG_RNG); + for(int dev_fd : m_dev_fds) + { + if(FD_ISSET(dev_fd, &read_set)) + { + const ssize_t got = ::read(dev_fd, io_buf.data(), io_buf.size()); + + if(got > 0) + { + rng.add_entropy(io_buf.data(), static_cast<size_t>(got)); + bits += got * 8; + } + } + } } } + + return bits; } } diff --git a/src/lib/entropy/dev_random/dev_random.h b/src/lib/entropy/dev_random/dev_random.h index 1f29b2f64..7c8df0553 100644 --- a/src/lib/entropy/dev_random/dev_random.h +++ b/src/lib/entropy/dev_random/dev_random.h @@ -22,13 +22,14 @@ class Device_EntropySource final : public Entropy_Source public: std::string name() const override { return "dev_random"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; Device_EntropySource(const std::vector<std::string>& fsnames); + ~Device_EntropySource(); private: - typedef int fd_type; - std::vector<fd_type> m_devices; + std::vector<int> m_dev_fds; + int m_max_fd; }; } diff --git a/src/lib/entropy/egd/es_egd.cpp b/src/lib/entropy/egd/es_egd.cpp index 9bc6de6fe..384516aa8 100644 --- a/src/lib/entropy/egd/es_egd.cpp +++ b/src/lib/entropy/egd/es_egd.cpp @@ -134,22 +134,24 @@ EGD_EntropySource::~EGD_EntropySource() /** * Gather Entropy from EGD */ -void EGD_EntropySource::poll(Entropy_Accumulator& accum) +size_t EGD_EntropySource::poll(RandomNumberGenerator& rng) { std::lock_guard<std::mutex> lock(m_mutex); - secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + secure_vector<byte> buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); for(size_t i = 0; i != m_sockets.size(); ++i) { - size_t got = m_sockets[i].read(buf.data(), buf.size()); + size_t got = m_sockets[i].read(m_io_buf.data(), m_io_buf.size()); if(got) { - accum.add(buf.data(), got, BOTAN_ENTROPY_ESTIMATE_STRONG_RNG); - break; + rng.add_entropy(m_io_buf.data(), got); + return got * 8; } } + + return 0; } } diff --git a/src/lib/entropy/egd/es_egd.h b/src/lib/entropy/egd/es_egd.h index 1a624713a..04b4591e3 100644 --- a/src/lib/entropy/egd/es_egd.h +++ b/src/lib/entropy/egd/es_egd.h @@ -23,7 +23,7 @@ class EGD_EntropySource final : public Entropy_Source public: std::string name() const override { return "egd"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; EGD_EntropySource(const std::vector<std::string>&); ~EGD_EntropySource(); @@ -44,6 +44,7 @@ class EGD_EntropySource final : public Entropy_Source std::mutex m_mutex; std::vector<EGD_Socket> m_sockets; + secure_vector<uint8_t> m_io_buf; }; } diff --git a/src/lib/entropy/entropy_src.h b/src/lib/entropy/entropy_src.h index 539df809a..64d988e7c 100644 --- a/src/lib/entropy/entropy_src.h +++ b/src/lib/entropy/entropy_src.h @@ -1,6 +1,6 @@ /* * EntropySource -* (C) 2008,2009,2014,2015 Jack Lloyd +* (C) 2008,2009,2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -8,71 +8,13 @@ #ifndef BOTAN_ENTROPY_H__ #define BOTAN_ENTROPY_H__ -#include <botan/secmem.h> +#include <botan/rng.h> #include <string> -#include <functional> +#include <chrono> namespace Botan { /** -* Class used to accumulate the poll results of EntropySources -*/ -class BOTAN_DLL Entropy_Accumulator final - { - public: - /** - * Initialize an Entropy_Accumulator - * - * @param accum will be called with poll results, first params the data and - * length, the second a best estimate of min-entropy for the entire buffer; - * out of an abundance of caution this will be zero for many sources. - * accum should return true if it wants the polling to stop, though it may - * still be called again a few more times, and should be careful to return - * true then as well. - */ - explicit Entropy_Accumulator(std::function<bool (const byte[], size_t, double)> accum) : - m_accum_fn(accum) {} - - /** - * @return if our polling goal has been achieved - */ - bool polling_goal_achieved() const { return m_done; } - - bool polling_finished() const { return m_done; } - - /** - * Add entropy to the accumulator - * @param bytes the input bytes - * @param length specifies how many bytes the input is - * @param entropy_bits_per_byte is a best guess at how much - * entropy per byte is in this input - */ - void add(const void* bytes, size_t length, double entropy_bits_per_byte) - { - m_done = m_accum_fn(reinterpret_cast<const byte*>(bytes), - length, entropy_bits_per_byte * length) || m_done; - } - - /** - * Add entropy to the accumulator - * @param v is some value - * @param entropy_bits_per_byte is a best guess at how much - * entropy per byte is in this input - */ - template<typename T> - void add(const T& v, double entropy_bits_per_byte) - { - add(&v, sizeof(T), entropy_bits_per_byte); - } - - secure_vector<byte>& get_io_buf(size_t sz) { m_io_buf.resize(sz); return m_io_buf; } - private: - std::function<bool (const byte[], size_t, double)> m_accum_fn; - secure_vector<byte> m_io_buf; - bool m_done = false; - }; - -/** * Abstract interface to a source of entropy */ class BOTAN_DLL Entropy_Source @@ -93,9 +35,10 @@ class BOTAN_DLL Entropy_Source /** * Perform an entropy gathering poll - * @param accum is an accumulator object that will be given entropy + * @param rng will be provided with entropy via calls to add_entropy + @ @return conservative estimate of actual entropy added to rng during poll */ - virtual void poll(Entropy_Accumulator& accum) = 0; + virtual size_t poll(RandomNumberGenerator& rng) = 0; virtual ~Entropy_Source() {} }; @@ -109,8 +52,14 @@ class BOTAN_DLL Entropy_Sources final std::vector<std::string> enabled_sources() const; - void poll(Entropy_Accumulator& accum); - bool poll_just(Entropy_Accumulator& accum, const std::string& src); + size_t poll(RandomNumberGenerator& rng, + size_t bits, + std::chrono::milliseconds timeout); + + /** + * Poll just a single named source. Ordinally only used for testing + */ + size_t poll_just(RandomNumberGenerator& rng, const std::string& src); Entropy_Sources() {} explicit Entropy_Sources(const std::vector<std::string>& sources); diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp index a5dc0a819..22d2e5e4b 100644 --- a/src/lib/entropy/entropy_srcs.cpp +++ b/src/lib/entropy/entropy_srcs.cpp @@ -6,6 +6,7 @@ */ #include <botan/entropy_src.h> +#include <botan/rng.h> #if defined(BOTAN_HAS_ENTROPY_SRC_HIGH_RESOLUTION_TIMER) #include <botan/internal/hres_timer.h> @@ -68,7 +69,7 @@ std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name) return std::unique_ptr<Entropy_Source>(new Intel_Rdrand); #endif } - + if(name == "rdseed") { #if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED) @@ -154,28 +155,38 @@ std::vector<std::string> Entropy_Sources::enabled_sources() const return sources; } -void Entropy_Sources::poll(Entropy_Accumulator& accum) +size_t Entropy_Sources::poll(RandomNumberGenerator& rng, + size_t poll_bits, + std::chrono::milliseconds timeout) { - for(size_t i = 0; i != m_srcs.size(); ++i) + typedef std::chrono::system_clock clock; + + auto deadline = clock::now() + timeout; + + size_t bits_collected = 0; + + for(Entropy_Source* src : m_srcs) { - m_srcs[i]->poll(accum); - if(accum.polling_goal_achieved()) + bits_collected += src->poll(rng); + + if (bits_collected >= poll_bits || clock::now() > deadline) break; } + + return bits_collected; } -bool Entropy_Sources::poll_just(Entropy_Accumulator& accum, const std::string& the_src) +size_t Entropy_Sources::poll_just(RandomNumberGenerator& rng, const std::string& the_src) { for(size_t i = 0; i != m_srcs.size(); ++i) { if(m_srcs[i]->name() == the_src) { - m_srcs[i]->poll(accum); - return true; + return m_srcs[i]->poll(rng); } } - return false; + return 0; } Entropy_Sources::Entropy_Sources(const std::vector<std::string>& sources) diff --git a/src/lib/entropy/hres_timer/hres_timer.cpp b/src/lib/entropy/hres_timer/hres_timer.cpp deleted file mode 100644 index e2a5ddbef..000000000 --- a/src/lib/entropy/hres_timer/hres_timer.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* -* High Resolution Timestamp Entropy Source -* (C) 1999-2009,2011,2014,2016 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include <botan/internal/hres_timer.h> -#include <botan/internal/os_utils.h> - -#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) - #include <time.h> -#endif - -namespace Botan { - -/* -* Get the timestamp -*/ -void High_Resolution_Timestamp::poll(Entropy_Accumulator& accum) - { - accum.add(OS::get_processor_timestamp(), BOTAN_ENTROPY_ESTIMATE_TIMESTAMPS); - - accum.add(OS::get_system_timestamp_ns(), BOTAN_ENTROPY_ESTIMATE_TIMESTAMPS); - -#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) - -#define CLOCK_GETTIME_POLL(src) \ - do { \ - struct timespec ts; \ - ::clock_gettime(src, &ts); \ - accum.add(&ts, sizeof(ts), BOTAN_ENTROPY_ESTIMATE_TIMESTAMPS); \ - } while(0) - -#if defined(CLOCK_REALTIME) - CLOCK_GETTIME_POLL(CLOCK_REALTIME); -#endif - -#if defined(CLOCK_MONOTONIC) - CLOCK_GETTIME_POLL(CLOCK_MONOTONIC); -#endif - -#if defined(CLOCK_MONOTONIC_RAW) - CLOCK_GETTIME_POLL(CLOCK_MONOTONIC_RAW); -#endif - -#if defined(CLOCK_PROCESS_CPUTIME_ID) - CLOCK_GETTIME_POLL(CLOCK_PROCESS_CPUTIME_ID); -#endif - -#if defined(CLOCK_THREAD_CPUTIME_ID) - CLOCK_GETTIME_POLL(CLOCK_THREAD_CPUTIME_ID); -#endif - -#undef CLOCK_GETTIME_POLL - -#endif - } - -} diff --git a/src/lib/entropy/hres_timer/hres_timer.h b/src/lib/entropy/hres_timer/hres_timer.h deleted file mode 100644 index d297a87b1..000000000 --- a/src/lib/entropy/hres_timer/hres_timer.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -* High Resolution Timestamp Entropy Source -* (C) 1999-2009 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_ENTROPY_SRC_HRES_TIMER_H__ -#define BOTAN_ENTROPY_SRC_HRES_TIMER_H__ - -#include <botan/entropy_src.h> - -namespace Botan { - -/** -* Entropy source using high resolution timers -* -* @note Any results from timers are marked as not contributing entropy -* to the poll, as a local attacker could observe them directly. -*/ -class High_Resolution_Timestamp final : public Entropy_Source - { - public: - std::string name() const override { return "timestamp"; } - void poll(Entropy_Accumulator& accum) override; - }; - -} - -#endif diff --git a/src/lib/entropy/hres_timer/info.txt b/src/lib/entropy/hres_timer/info.txt deleted file mode 100644 index dfe8fab0b..000000000 --- a/src/lib/entropy/hres_timer/info.txt +++ /dev/null @@ -1,13 +0,0 @@ -define ENTROPY_SRC_HIGH_RESOLUTION_TIMER 20131128 - -<source> -hres_timer.cpp -</source> - -<header:internal> -hres_timer.h -</header:internal> - -<libs> -linux -> rt -</libs> diff --git a/src/lib/entropy/info.txt b/src/lib/entropy/info.txt index ba5a4044d..d80176113 100644 --- a/src/lib/entropy/info.txt +++ b/src/lib/entropy/info.txt @@ -1 +1,5 @@ define ENTROPY_SOURCE 20151120 + +<requires> +rng +</requires> diff --git a/src/lib/entropy/proc_walk/proc_walk.cpp b/src/lib/entropy/proc_walk/proc_walk.cpp index c59a8227b..a0c3f830a 100644 --- a/src/lib/entropy/proc_walk/proc_walk.cpp +++ b/src/lib/entropy/proc_walk/proc_walk.cpp @@ -110,7 +110,7 @@ int Directory_Walker::next_fd() } -void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) +size_t ProcWalking_EntropySource::poll(RandomNumberGenerator& rng) { const size_t MAX_FILES_READ_PER_POLL = 2048; @@ -121,6 +121,8 @@ void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) m_buf.resize(4096); + size_t bits = 0; + for(size_t i = 0; i != MAX_FILES_READ_PER_POLL; ++i) { int fd = m_dir->next_fd(); @@ -136,11 +138,15 @@ void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) ::close(fd); if(got > 0) - accum.add(m_buf.data(), got, BOTAN_ENTROPY_ESTIMATE_SYSTEM_TEXT); + { + rng.add_entropy(m_buf.data(), static_cast<size_t>(got)); + } - if(accum.polling_finished()) + if(bits > 128) break; } + + return bits; } } diff --git a/src/lib/entropy/proc_walk/proc_walk.h b/src/lib/entropy/proc_walk/proc_walk.h index f6db8185a..369b52699 100644 --- a/src/lib/entropy/proc_walk/proc_walk.h +++ b/src/lib/entropy/proc_walk/proc_walk.h @@ -28,7 +28,7 @@ class ProcWalking_EntropySource final : public Entropy_Source public: std::string name() const override { return "proc_walk"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; ProcWalking_EntropySource(const std::string& root_dir) : m_path(root_dir), m_dir(nullptr) {} diff --git a/src/lib/entropy/rdrand/rdrand.cpp b/src/lib/entropy/rdrand/rdrand.cpp index 89234b460..fb04d7b78 100644 --- a/src/lib/entropy/rdrand/rdrand.cpp +++ b/src/lib/entropy/rdrand/rdrand.cpp @@ -16,32 +16,35 @@ namespace Botan { -void Intel_Rdrand::poll(Entropy_Accumulator& accum) { - if(!CPUID::has_rdrand()) - return; - - for(size_t p = 0; p != BOTAN_ENTROPY_INTEL_RNG_POLLS; ++p) +size_t Intel_Rdrand::poll(RandomNumberGenerator& rng) { + if(CPUID::has_rdrand()) { - for(size_t i = 0; i != BOTAN_ENTROPY_RDRAND_RETRIES; ++i) + for(size_t p = 0; p != BOTAN_ENTROPY_INTEL_RNG_POLLS; ++p) { - uint32_t r = 0; + for(size_t i = 0; i != BOTAN_ENTROPY_RDRAND_RETRIES; ++i) + { + uint32_t r = 0; #if defined(BOTAN_USE_GCC_INLINE_ASM) - int cf = 0; + int cf = 0; - // Encoding of rdrand %eax - asm(".byte 0x0F, 0xC7, 0xF0; adcl $0,%1" : - "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); + // Encoding of rdrand %eax + asm(".byte 0x0F, 0xC7, 0xF0; adcl $0,%1" : + "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); #else - int cf = _rdrand32_step(&r); + int cf = _rdrand32_step(&r); #endif - if(1 == cf) - { - accum.add(r, BOTAN_ENTROPY_ESTIMATE_HARDWARE_RNG); - break; + if(1 == cf) + { + rng.add_entropy_T(r); + break; + } } } } + + // RDRAND is used but not trusted + return 0; } } diff --git a/src/lib/entropy/rdrand/rdrand.h b/src/lib/entropy/rdrand/rdrand.h index 48d090775..db9de39b6 100644 --- a/src/lib/entropy/rdrand/rdrand.h +++ b/src/lib/entropy/rdrand/rdrand.h @@ -20,7 +20,7 @@ class Intel_Rdrand final : public Entropy_Source { public: std::string name() const override { return "rdrand"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/entropy/rdseed/rdseed.cpp b/src/lib/entropy/rdseed/rdseed.cpp index 2ba2075cc..325edfd41 100644 --- a/src/lib/entropy/rdseed/rdseed.cpp +++ b/src/lib/entropy/rdseed/rdseed.cpp @@ -15,32 +15,34 @@ namespace Botan { -void Intel_Rdseed::poll(Entropy_Accumulator& accum) { - if(!CPUID::has_rdseed()) - return; - - for(size_t p = 0; p != BOTAN_ENTROPY_INTEL_RNG_POLLS; ++p) +size_t Intel_Rdseed::poll(RandomNumberGenerator& rng) { + if(CPUID::has_rdseed()) { - for(size_t i = 0; i != BOTAN_ENTROPY_RDSEED_RETRIES; ++i) + for(size_t p = 0; p != BOTAN_ENTROPY_INTEL_RNG_POLLS; ++p) { - uint32_t r = 0; + for(size_t i = 0; i != BOTAN_ENTROPY_RDSEED_RETRIES; ++i) + { + uint32_t r = 0; #if defined(BOTAN_USE_GCC_INLINE_ASM) - int cf = 0; + int cf = 0; - // Encoding of rdseed %eax - asm(".byte 0x0F, 0xC7, 0xF8; adcl $0,%1" : - "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); + // Encoding of rdseed %eax + asm(".byte 0x0F, 0xC7, 0xF8; adcl $0,%1" : + "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); #else - int cf = _rdseed32_step(&r); + int cf = _rdseed32_step(&r); #endif - if(1 == cf) - { - accum.add(r, BOTAN_ENTROPY_ESTIMATE_HARDWARE_RNG); - break; + if(1 == cf) + { + rng.add_entropy_T(r); + break; + } } } } + + return 0; } } diff --git a/src/lib/entropy/rdseed/rdseed.h b/src/lib/entropy/rdseed/rdseed.h index f86c32768..4ea584354 100644 --- a/src/lib/entropy/rdseed/rdseed.h +++ b/src/lib/entropy/rdseed/rdseed.h @@ -20,7 +20,7 @@ class Intel_Rdseed final : public Entropy_Source { public: std::string name() const override { return "rdseed"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/entropy/unix_procs/unix_procs.cpp b/src/lib/entropy/unix_procs/unix_procs.cpp index 55ad295cd..8f885cfcf 100644 --- a/src/lib/entropy/unix_procs/unix_procs.cpp +++ b/src/lib/entropy/unix_procs/unix_procs.cpp @@ -67,17 +67,52 @@ Unix_EntropySource::Unix_EntropySource(const std::vector<std::string>& trusted_p { } -void UnixProcessInfo_EntropySource::poll(Entropy_Accumulator& accum) +size_t UnixProcessInfo_EntropySource::poll(RandomNumberGenerator& rng) { - accum.add(::getpid(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); - accum.add(::getppid(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); - accum.add(::getuid(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); - accum.add(::getgid(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); - accum.add(::getpgrp(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); + rng.add_entropy_T(::getpid()); + rng.add_entropy_T(::getppid()); + rng.add_entropy_T(::getuid()); + rng.add_entropy_T(::getgid()); + rng.add_entropy_T(::getpgrp()); struct ::rusage usage; ::getrusage(RUSAGE_SELF, &usage); - accum.add(usage, BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); + rng.add_entropy_T(usage); + +#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME) + +#define CLOCK_GETTIME_POLL(src) \ + do { \ + struct timespec ts; \ + ::clock_gettime(src, &ts); \ + rng.add_entropy_T(ts); \ + } while(0) + +#if defined(CLOCK_REALTIME) + CLOCK_GETTIME_POLL(CLOCK_REALTIME); +#endif + +#if defined(CLOCK_MONOTONIC) + CLOCK_GETTIME_POLL(CLOCK_MONOTONIC); +#endif + +#if defined(CLOCK_MONOTONIC_RAW) + CLOCK_GETTIME_POLL(CLOCK_MONOTONIC_RAW); +#endif + +#if defined(CLOCK_PROCESS_CPUTIME_ID) + CLOCK_GETTIME_POLL(CLOCK_PROCESS_CPUTIME_ID); +#endif + +#if defined(CLOCK_THREAD_CPUTIME_ID) + CLOCK_GETTIME_POLL(CLOCK_THREAD_CPUTIME_ID); +#endif + +#undef CLOCK_GETTIME_POLL + +#endif + + return 0; } void Unix_EntropySource::Unix_Process::spawn(const std::vector<std::string>& args) @@ -168,11 +203,11 @@ const std::vector<std::string>& Unix_EntropySource::next_source() return src; } -void Unix_EntropySource::poll(Entropy_Accumulator& accum) +size_t Unix_EntropySource::poll(RandomNumberGenerator& rng) { // refuse to run setuid or setgid, or as root if((getuid() != geteuid()) || (getgid() != getegid()) || (geteuid() == 0)) - return; + return 0; std::lock_guard<std::mutex> lock(m_mutex); @@ -192,13 +227,15 @@ void Unix_EntropySource::poll(Entropy_Accumulator& accum) } if(m_sources.empty()) - return; // still empty, really nothing to try + return 0; // still empty, really nothing to try const size_t MS_WAIT_TIME = 32; m_buf.resize(4096); - while(!accum.polling_finished()) + size_t bytes = 0; + + while(bytes < 128 * 1024) // arbitrary limit... { while(m_procs.size() < m_concurrent) m_procs.emplace_back(Unix_Process(next_source())); @@ -228,7 +265,7 @@ void Unix_EntropySource::poll(Entropy_Accumulator& accum) timeout.tv_usec = (MS_WAIT_TIME % 1000) * 1000; if(::select(max_fd + 1, &read_set, nullptr, nullptr, &timeout) < 0) - return; // or continue? + break; // or continue? for(auto& proc : m_procs) { @@ -237,13 +274,19 @@ void Unix_EntropySource::poll(Entropy_Accumulator& accum) if(FD_ISSET(fd, &read_set)) { const ssize_t got = ::read(fd, m_buf.data(), m_buf.size()); + if(got > 0) - accum.add(m_buf.data(), got, BOTAN_ENTROPY_ESTIMATE_SYSTEM_TEXT); + { + rng.add_entropy(m_buf.data(), got); + bytes += got; + } else proc.spawn(next_source()); } } } + + return bytes / 1024; } } diff --git a/src/lib/entropy/unix_procs/unix_procs.h b/src/lib/entropy/unix_procs/unix_procs.h index e1749af5f..27f7ab5bb 100644 --- a/src/lib/entropy/unix_procs/unix_procs.h +++ b/src/lib/entropy/unix_procs/unix_procs.h @@ -25,7 +25,7 @@ class Unix_EntropySource final : public Entropy_Source public: std::string name() const override { return "unix_procs"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; /** * @param trusted_paths is a list of directories that are assumed @@ -83,7 +83,7 @@ class UnixProcessInfo_EntropySource final : public Entropy_Source public: std::string name() const override { return "proc_info"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/entropy/win32_stats/es_win32.cpp b/src/lib/entropy/win32_stats/es_win32.cpp index ce0edea83..bbc64eaab 100644 --- a/src/lib/entropy/win32_stats/es_win32.cpp +++ b/src/lib/entropy/win32_stats/es_win32.cpp @@ -1,6 +1,6 @@ /* * Win32 EntropySource -* (C) 1999-2009 Jack Lloyd +* (C) 1999-2009,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -14,44 +14,44 @@ namespace Botan { /** * Win32 poll using stats functions including Tooltip32 */ -void Win32_EntropySource::poll(Entropy_Accumulator& accum) +size_t Win32_EntropySource::poll(RandomNumberGenerator& rng) { /* - First query a bunch of basic statistical stuff, though - don't count it for much in terms of contributed entropy. + First query a bunch of basic statistical stuff */ - accum.add(GetTickCount(), BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); - accum.add(GetMessagePos(), BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); - accum.add(GetMessageTime(), BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); - accum.add(GetInputState(), BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + rng.add_entropy_T(::GetTickCount()); + rng.add_entropy_T(::GetMessagePos()); + rng.add_entropy_T(::GetMessageTime()); + rng.add_entropy_T(::GetInputState()); - accum.add(GetCurrentProcessId(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); - accum.add(GetCurrentThreadId(), BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); + rng.add_entropy_T(::GetCurrentProcessId()); + rng.add_entropy_T(::GetCurrentThreadId()); SYSTEM_INFO sys_info; - GetSystemInfo(&sys_info); - accum.add(sys_info, BOTAN_ENTROPY_ESTIMATE_STATIC_SYSTEM_DATA); + ::GetSystemInfo(&sys_info); + rng.add_entropy_T(sys_info); MEMORYSTATUSEX mem_info; - GlobalMemoryStatusEx(&mem_info); - accum.add(mem_info, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + ::GlobalMemoryStatusEx(&mem_info); + rng.add_entropy_T(mem_info); POINT point; - GetCursorPos(&point); - accum.add(point, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + ::GetCursorPos(&point); + rng.add_entropy_T(point); - GetCaretPos(&point); - accum.add(point, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); + ::GetCaretPos(&point); + rng.add_entropy_T(point); /* - Now use the Tooltip library to iterate throug various objects on + Now use the Tooltip library to iterate through various objects on the system, including processes, threads, and heap objects. */ - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); + HANDLE snapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0); + size_t bits = 0; #define TOOLHELP32_ITER(DATA_TYPE, FUNC_FIRST, FUNC_NEXT) \ - if(!accum.polling_finished()) \ + if(bits < 256) \ { \ DATA_TYPE info; \ info.dwSize = sizeof(DATA_TYPE); \ @@ -59,57 +59,52 @@ void Win32_EntropySource::poll(Entropy_Accumulator& accum) { \ do \ { \ - accum.add(info, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); \ + rng.add_entropy_T(info); \ + bits += 4; \ } while(FUNC_NEXT(snapshot, &info)); \ } \ } - TOOLHELP32_ITER(MODULEENTRY32, Module32First, Module32Next); - TOOLHELP32_ITER(PROCESSENTRY32, Process32First, Process32Next); - TOOLHELP32_ITER(THREADENTRY32, Thread32First, Thread32Next); + TOOLHELP32_ITER(MODULEENTRY32, ::Module32First, ::Module32Next); + TOOLHELP32_ITER(PROCESSENTRY32, ::Process32First, ::Process32Next); + TOOLHELP32_ITER(THREADENTRY32, ::Thread32First, ::Thread32Next); #undef TOOLHELP32_ITER - if(!accum.polling_finished()) + if(bits <= 256) { HEAPLIST32 heap_list; heap_list.dwSize = sizeof(HEAPLIST32); - const size_t HEAP_LISTS_MAX = 32; - const size_t HEAP_OBJS_PER_LIST = 128; - - if(Heap32ListFirst(snapshot, &heap_list)) + if(::Heap32ListFirst(snapshot, &heap_list)) { - size_t heap_lists_found = 0; do { - accum.add(heap_list, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); - - if(++heap_lists_found > HEAP_LISTS_MAX) - break; + rng.add_entropy_T(heap_list); HEAPENTRY32 heap_entry; heap_entry.dwSize = sizeof(HEAPENTRY32); - if(Heap32First(&heap_entry, heap_list.th32ProcessID, - heap_list.th32HeapID)) + if(::Heap32First(&heap_entry, + heap_list.th32ProcessID, + heap_list.th32HeapID)) { - size_t heap_objs_found = 0; do { - if(heap_objs_found++ > HEAP_OBJS_PER_LIST) - break; - accum.add(heap_entry, BOTAN_ENTROPY_ESTIMATE_SYSTEM_DATA); - } while(Heap32Next(&heap_entry)); + rng.add_entropy_T(heap_entry); + bits += 4; + } while(::Heap32Next(&heap_entry)); } - if(accum.polling_finished()) + if(bits >= 256) break; - } while(Heap32ListNext(snapshot, &heap_list)); + } while(::Heap32ListNext(snapshot, &heap_list)); } } - CloseHandle(snapshot); + ::CloseHandle(snapshot); + + return bits; } } diff --git a/src/lib/entropy/win32_stats/es_win32.h b/src/lib/entropy/win32_stats/es_win32.h index 5dc3f7f17..26b904bbb 100644 --- a/src/lib/entropy/win32_stats/es_win32.h +++ b/src/lib/entropy/win32_stats/es_win32.h @@ -19,7 +19,7 @@ class Win32_EntropySource final : public Entropy_Source { public: std::string name() const override { return "system_stats"; } - void poll(Entropy_Accumulator& accum) override; + size_t poll(RandomNumberGenerator& rng) override; }; } diff --git a/src/lib/ffi/info.txt b/src/lib/ffi/info.txt index 7c8968ff0..057bbd012 100644 --- a/src/lib/ffi/info.txt +++ b/src/lib/ffi/info.txt @@ -7,7 +7,6 @@ pbkdf pubkey x509 #tls -auto_rng system_rng </requires> diff --git a/src/lib/prov/tpm/tpm.h b/src/lib/prov/tpm/tpm.h index 4a9dcd3c6..b8093518c 100644 --- a/src/lib/prov/tpm/tpm.h +++ b/src/lib/prov/tpm/tpm.h @@ -1,3 +1,4 @@ + /* * TPM 1.2 interface * (C) 2015 Jack Lloyd @@ -71,34 +72,27 @@ class BOTAN_DLL TPM_Context TSS_HTPM m_tpm; }; -class BOTAN_DLL TPM_RNG : public RandomNumberGenerator +class BOTAN_DLL TPM_RNG : public Hardware_RNG { public: TPM_RNG(TPM_Context& ctx) : m_ctx(ctx) {} + void add_entropy(const byte in[], size_t in_len) override + { + m_ctx.stir_random(in, in_len); + } + void randomize(byte out[], size_t out_len) override { m_ctx.gen_random(out, out_len); } - void clear() override {} - std::string name() const override { return "TPM_RNG"; } - size_t reseed_with_sources(Entropy_Sources&, - size_t, - std::chrono::milliseconds) override - { - // TODO: poll and stir - return 0; - } + bool is_seeded() const override { return true; } - void add_entropy(const byte in[], size_t in_len) override - { - m_ctx.stir_random(in, in_len); - } + void clear() override {} - bool is_seeded() const override { return true; } private: TPM_Context& m_ctx; }; diff --git a/src/lib/pubkey/dsa/dsa.cpp b/src/lib/pubkey/dsa/dsa.cpp index c42e70914..6effb81dd 100644 --- a/src/lib/pubkey/dsa/dsa.cpp +++ b/src/lib/pubkey/dsa/dsa.cpp @@ -116,6 +116,7 @@ DSA_Signature_Operation::raw_sign(const byte msg[], size_t msg_len, i -= m_q; #if defined(BOTAN_HAS_RFC6979_GENERATOR) + BOTAN_UNUSED(rng); const BigInt k = generate_rfc6979_nonce(m_x, m_q, i, hash_for_emsa(m_emsa)); #else const BigInt k = BigInt::random_integer(rng, 1, m_q); diff --git a/src/lib/pubkey/rfc6979/rfc6979.cpp b/src/lib/pubkey/rfc6979/rfc6979.cpp index 0b26aadb5..1173eefee 100644 --- a/src/lib/pubkey/rfc6979/rfc6979.cpp +++ b/src/lib/pubkey/rfc6979/rfc6979.cpp @@ -17,18 +17,23 @@ RFC6979_Nonce_Generator::RFC6979_Nonce_Generator(const std::string& hash, m_order(order), m_qlen(m_order.bits()), m_rlen(m_qlen / 8 + (m_qlen % 8 ? 1 : 0)), - m_hmac_drbg(new HMAC_DRBG(MessageAuthenticationCode::create("HMAC(" + hash + ")").release())), + m_hmac_drbg(new HMAC_DRBG(hash, 0)), m_rng_in(m_rlen * 2), m_rng_out(m_rlen) { BigInt::encode_1363(m_rng_in.data(), m_rlen, x); } +RFC6979_Nonce_Generator::~RFC6979_Nonce_Generator() + { + // for ~unique_ptr + } + const BigInt& RFC6979_Nonce_Generator::nonce_for(const BigInt& m) { BigInt::encode_1363(&m_rng_in[m_rlen], m_rlen, m); m_hmac_drbg->clear(); - m_hmac_drbg->add_entropy(m_rng_in.data(), m_rng_in.size()); + m_hmac_drbg->initialize_with(m_rng_in.data(), m_rng_in.size()); do { diff --git a/src/lib/pubkey/rfc6979/rfc6979.h b/src/lib/pubkey/rfc6979/rfc6979.h index 32728befb..2518535f7 100644 --- a/src/lib/pubkey/rfc6979/rfc6979.h +++ b/src/lib/pubkey/rfc6979/rfc6979.h @@ -14,7 +14,7 @@ namespace Botan { -class RandomNumberGenerator; +class HMAC_DRBG; class BOTAN_DLL RFC6979_Nonce_Generator { @@ -26,12 +26,14 @@ class BOTAN_DLL RFC6979_Nonce_Generator const BigInt& order, const BigInt& x); + ~RFC6979_Nonce_Generator(); + const BigInt& nonce_for(const BigInt& m); private: const BigInt& m_order; BigInt m_k; size_t m_qlen, m_rlen; - std::unique_ptr<RandomNumberGenerator> m_hmac_drbg; + std::unique_ptr<HMAC_DRBG> m_hmac_drbg; secure_vector<byte> m_rng_in, m_rng_out; }; diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng.h index 72ea88d3e..3085623ef 100644 --- a/src/lib/rng/auto_rng/auto_rng.h +++ b/src/lib/rng/auto_rng.h @@ -9,19 +9,20 @@ #define BOTAN_AUTO_SEEDING_RNG_H__ #include <botan/rng.h> -#include <string> namespace Botan { -class AutoSeeded_RNG : public RandomNumberGenerator +class BOTAN_DLL AutoSeeded_RNG final : public RandomNumberGenerator { public: - void randomize(byte out[], size_t len) override - { m_rng->randomize(out, len); } + void randomize(byte out[], size_t len) override; + + void randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) override; bool is_seeded() const override { return m_rng->is_seeded(); } - void clear() override { m_rng->clear(); } + void clear() override { m_rng->clear(); m_counter = 0; } std::string name() const override { return m_rng->name(); } @@ -35,9 +36,10 @@ class AutoSeeded_RNG : public RandomNumberGenerator void add_entropy(const byte in[], size_t len) override { m_rng->add_entropy(in, len); } - AutoSeeded_RNG() : m_rng(RandomNumberGenerator::make_rng()) {} + AutoSeeded_RNG(size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); private: std::unique_ptr<RandomNumberGenerator> m_rng; + uint32_t m_counter = 0; }; } diff --git a/src/lib/rng/auto_rng/info.txt b/src/lib/rng/auto_rng/info.txt deleted file mode 100644 index 4f48f484b..000000000 --- a/src/lib/rng/auto_rng/info.txt +++ /dev/null @@ -1,9 +0,0 @@ -define AUTO_SEEDING_RNG 20131128 - -<requires> -hmac_rng -hmac -sha2_32 -sha2_64 -#dev_random|cryptoapi_rng|unix_procs|proc_walk -</requires> diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index 67325ee1b..7325804e3 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -1,6 +1,6 @@ /* * HMAC_DRBG -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -10,53 +10,81 @@ namespace Botan { -HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* mac, - RandomNumberGenerator* prng) : - m_mac(mac), - m_prng(prng), - m_V(m_mac->output_length(), 0x01), - m_reseed_counter(0) +HMAC_DRBG::HMAC_DRBG(MessageAuthenticationCode* hmac, + size_t max_output_before_reseed) : + Stateful_RNG(max_output_before_reseed), + m_mac(hmac) { - m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); + m_V.resize(m_mac->output_length()); + clear(); } -HMAC_DRBG::HMAC_DRBG(const std::string& mac_name, - RandomNumberGenerator* prng) : - m_prng(prng), - m_reseed_counter(0) +HMAC_DRBG::HMAC_DRBG(const std::string& hmac_hash, + size_t max_output_before_reseed) : + Stateful_RNG(max_output_before_reseed) { - m_mac = MessageAuthenticationCode::create(mac_name); + const std::string hmac = "HMAC(" + hmac_hash + ")"; + + m_mac = MessageAuthenticationCode::create(hmac); if(!m_mac) - throw Algorithm_Not_Found(mac_name); - m_V = secure_vector<byte>(m_mac->output_length(), 0x01), + { + throw Algorithm_Not_Found(hmac); + } + + m_V.resize(m_mac->output_length()); + clear(); + } + +void HMAC_DRBG::clear() + { + Stateful_RNG::clear(); + + for(size_t i = 0; i != m_V.size(); ++i) + m_V[i] = 0x01; m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); } -void HMAC_DRBG::randomize(byte out[], size_t length) +std::string HMAC_DRBG::name() const { - if(!is_seeded() || m_reseed_counter > BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) - reseed(m_mac->output_length() * 8); + return "HMAC_DRBG(" + m_mac->name() + ")"; + } - if(!is_seeded()) - throw PRNG_Unseeded(name()); +void HMAC_DRBG::randomize(byte output[], size_t output_len) + { + randomize_with_input(output, output_len, nullptr, 0); + } - while(length) - { - const size_t to_copy = std::min(length, m_V.size()); - m_V = m_mac->process(m_V); - copy_mem(out, m_V.data(), to_copy); +/* +* HMAC_DRBG generation +* See NIST SP800-90A section 10.1.2.5 +*/ +void HMAC_DRBG::randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) + { + reseed_check(output_len); - length -= to_copy; - out += to_copy; + if(input_len > 0) + { + update(input, input_len); } - m_reseed_counter += length; + while(output_len) + { + const size_t to_copy = std::min(output_len, m_V.size()); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); + copy_mem(output, m_V.data(), to_copy); + + output += to_copy; + output_len -= to_copy; + } - update(nullptr, 0); // additional_data is always empty + 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 byte input[], size_t input_len) { @@ -65,66 +93,24 @@ void HMAC_DRBG::update(const byte input[], size_t input_len) m_mac->update(input, input_len); m_mac->set_key(m_mac->final()); - m_V = m_mac->process(m_V); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); - if(input_len) + if(input_len > 0) { m_mac->update(m_V); m_mac->update(0x01); m_mac->update(input, input_len); m_mac->set_key(m_mac->final()); - m_V = m_mac->process(m_V); + m_mac->update(m_V.data(), m_V.size()); + m_mac->final(m_V.data()); } } -size_t HMAC_DRBG::reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) +void HMAC_DRBG::add_entropy(const byte input[], size_t input_len) { - if(m_prng) - { - size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout); - - if(m_prng->is_seeded()) - { - secure_vector<byte> input = m_prng->random_vec(m_mac->output_length()); - update(input.data(), input.size()); - m_reseed_counter = 1; - } - - return bits; - } - - return 0; - } - -void HMAC_DRBG::add_entropy(const byte input[], size_t length) - { - update(input, length); - m_reseed_counter = 1; - } - -bool HMAC_DRBG::is_seeded() const - { - return m_reseed_counter > 0; - } - -void HMAC_DRBG::clear() - { - m_reseed_counter = 0; - for(size_t i = 0; i != m_V.size(); ++i) - m_V[i] = 0x01; - - m_mac->set_key(std::vector<byte>(m_mac->output_length(), 0x00)); - - if(m_prng) - m_prng->clear(); - } - -std::string HMAC_DRBG::name() const - { - return "HMAC_DRBG(" + m_mac->name() + ")"; + update(input, input_len); } } diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index bd2d18d47..0e294dbdb 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -1,6 +1,6 @@ /* * HMAC_DRBG (SP800-90A) -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -14,40 +14,35 @@ namespace Botan { /** -* HMAC_DRBG (SP800-90A) +* HMAC_DRBG from NIST SP800-90A */ -class BOTAN_DLL HMAC_DRBG : public RandomNumberGenerator +class BOTAN_DLL HMAC_DRBG final : public Stateful_RNG { public: - void randomize(byte buf[], size_t buf_len) override; - bool is_seeded() const override; - void clear() override; - std::string name() const override; + /** + * Initialize an HMAC_DRBG instance with the given hash function + */ + HMAC_DRBG(const std::string& hmac_hash, + size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override; + HMAC_DRBG(MessageAuthenticationCode* hmac, + size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); - void add_entropy(const byte input[], size_t input_len) override; + std::string name() const override; - /** - * @param mac the underlying mac function (eg HMAC(SHA-512)) - * @param underlying_rng RNG used generating inputs (eg HMAC_RNG) - */ - HMAC_DRBG(MessageAuthenticationCode* mac, - RandomNumberGenerator* underlying_rng = nullptr); + void clear() override; - HMAC_DRBG(const std::string& mac, - RandomNumberGenerator* underlying_rng = nullptr); + void randomize(byte output[], size_t output_len) override; + void randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) override; + + void add_entropy(const byte input[], size_t input_len) override; private: void update(const byte input[], size_t input_len); std::unique_ptr<MessageAuthenticationCode> m_mac; - std::unique_ptr<RandomNumberGenerator> m_prng; - secure_vector<byte> m_V; - size_t m_reseed_counter; }; } diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp index 7a9e4dbc5..c100cf70f 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.cpp +++ b/src/lib/rng/hmac_rng/hmac_rng.cpp @@ -9,15 +9,36 @@ #include <botan/entropy_src.h> #include <botan/internal/os_utils.h> #include <algorithm> -#include <chrono> namespace Botan { +HMAC_RNG::HMAC_RNG(const std::string& hash, size_t max_output_before_reseed) : + Stateful_RNG(max_output_before_reseed) + { + m_extractor = MAC::create("HMAC(" + hash + ")"); + if(!m_extractor) + throw Invalid_Argument("HMAC_RNG hash not found"); + + m_prf.reset(m_extractor->clone()); + + if(!m_prf->valid_keylength(m_extractor->output_length()) || + !m_extractor->valid_keylength(m_prf->output_length())) + { + throw Invalid_Argument("HMAC_RNG: Bad algo combination " + + m_extractor->name() + " and " + + m_prf->name()); + } + + this->clear(); + } + /* * HMAC_RNG Constructor */ HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor, - MessageAuthenticationCode* prf) : + MessageAuthenticationCode* prf, + size_t max_output_before_reseed) : + Stateful_RNG(max_output_before_reseed), m_extractor(extractor), m_prf(prf) { if(!m_prf->valid_keylength(m_extractor->output_length()) || @@ -33,7 +54,7 @@ HMAC_RNG::HMAC_RNG(MessageAuthenticationCode* extractor, void HMAC_RNG::clear() { - m_collected_entropy_estimate = 0; + Stateful_RNG::clear(); m_counter = 0; // First PRF inputs are all zero, as specified in section 2 @@ -71,7 +92,7 @@ void HMAC_RNG::clear() void HMAC_RNG::new_K_value(byte label) { m_prf->update(m_K); - m_prf->update_be(m_pid); + m_prf->update_be(last_pid()); m_prf->update_be(OS::get_processor_timestamp()); m_prf->update_be(OS::get_system_timestamp_ns()); m_prf->update_be(m_counter++); @@ -84,76 +105,38 @@ void HMAC_RNG::new_K_value(byte label) */ void HMAC_RNG::randomize(byte out[], size_t length) { - if(!is_seeded() || m_pid != OS::get_process_id()) - { - reseed(256); - if(!is_seeded()) - throw PRNG_Unseeded(name()); - } - - const size_t max_per_prf_iter = m_prf->output_length() / 2; - - m_output_since_reseed += length; - - if(m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) - { - reseed_with_sources(Entropy_Sources::global_sources(), - BOTAN_RNG_RESEED_POLL_BITS, - BOTAN_RNG_AUTO_RESEED_TIMEOUT); - } + reseed_check(length); - /* - HMAC KDF as described in E-t-E, using a CTXinfo of "rng" - */ while(length) { new_K_value(Running); - const size_t copied = std::min<size_t>(length, max_per_prf_iter); + const size_t copied = std::min<size_t>(length, m_prf->output_length()); copy_mem(out, m_K.data(), copied); out += copied; length -= copied; } + + new_K_value(BlockFinished); } size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs, size_t poll_bits, std::chrono::milliseconds timeout) { + new_K_value(Reseed); + m_extractor->update(m_K); // m_K is the PRF output + /* - Using the terminology of E-t-E, XTR is the MAC function (normally - HMAC) seeded with XTS (below) and we form SKM, the key material, by - polling as many sources as we think needed to reach our polling - goal. We then also include feedback of the current PRK so that - a bad poll doesn't wipe us out. + * This ends up calling add_entropy which provides input to the extractor */ - - typedef std::chrono::system_clock clock; - auto deadline = clock::now() + timeout; - - double bits_collected = 0; - - Entropy_Accumulator accum([&](const byte in[], size_t in_len, double entropy_estimate) { - m_extractor->update(in, in_len); - bits_collected += entropy_estimate; - return (bits_collected >= poll_bits || clock::now() > deadline); - }); - - srcs.poll(accum); + size_t bits_collected = Stateful_RNG::reseed_with_sources(srcs, poll_bits, timeout); /* - * It is necessary to feed forward poll data. Otherwise, a good poll - * (collecting a large amount of conditional entropy) followed by a - * bad one (collecting little) would be unsafe. Do this by - * generating new PRF outputs using the previous key and feeding - * them into the extractor function. + Now derive the new PRK using everything that has been fed into + the extractor, and set the PRF key to that */ - new_K_value(Reseed); - m_extractor->update(m_K); // K is the CTXinfo=reseed PRF output - - /* Now derive the new PRK using everything that has been fed into - the extractor, and set the PRF key to that */ m_prf->set_key(m_extractor->final()); // Now generate a new PRF output to use as the XTS extractor salt @@ -164,32 +147,17 @@ size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs, zeroise(m_K); m_counter = 0; - m_collected_entropy_estimate = - std::min<size_t>(m_collected_entropy_estimate + static_cast<size_t>(bits_collected), - m_extractor->output_length() * 8); - - m_output_since_reseed = 0; - m_pid = OS::get_process_id(); - - return static_cast<size_t>(bits_collected); - } - -bool HMAC_RNG::is_seeded() const - { - return (m_collected_entropy_estimate >= 256); + return bits_collected; } /* -* Add user-supplied entropy to the extractor input then reseed -* to incorporate it into the state +* Add user-supplied entropy to the extractor input then set remaining +* output length to for a reseed on next use. */ void HMAC_RNG::add_entropy(const byte input[], size_t length) { m_extractor->update(input, length); - - reseed_with_sources(Entropy_Sources::global_sources(), - BOTAN_RNG_RESEED_POLL_BITS, - BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); + force_reseed(); } /* diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h index 95ae25e39..a2538a83a 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.h +++ b/src/lib/rng/hmac_rng/hmac_rng.h @@ -24,11 +24,10 @@ namespace Botan { * Krawczyk's paper), for instance one could use HMAC(SHA-512) as the * extractor and CMAC(AES-256) as the PRF. */ -class BOTAN_DLL HMAC_RNG : public RandomNumberGenerator +class BOTAN_DLL HMAC_RNG : public Stateful_RNG { public: void randomize(byte buf[], size_t len) override; - bool is_seeded() const override; void clear() override; std::string name() const override; @@ -43,24 +42,28 @@ class BOTAN_DLL HMAC_RNG : public RandomNumberGenerator * @param prf a MAC used as a PRF using HKDF construction */ HMAC_RNG(MessageAuthenticationCode* extractor, - MessageAuthenticationCode* prf); + MessageAuthenticationCode* prf, + size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); + + /** + * Use the specified hash for both the extractor and PRF functions + */ + HMAC_RNG(const std::string& hash, + size_t max_output_before_reseed = BOTAN_RNG_DEFAULT_MAX_OUTPUT_BEFORE_RESEED); private: std::unique_ptr<MessageAuthenticationCode> m_extractor; std::unique_ptr<MessageAuthenticationCode> m_prf; enum HMAC_PRF_Label { Running, + BlockFinished, Reseed, ExtractorSeed, }; void new_K_value(byte label); - size_t m_collected_entropy_estimate = 0; - size_t m_output_since_reseed = 0; - secure_vector<byte> m_K; u32bit m_counter = 0; - u32bit m_pid = 0; }; } diff --git a/src/lib/rng/info.txt b/src/lib/rng/info.txt index ba7aa8e6a..84ba3ce89 100644 --- a/src/lib/rng/info.txt +++ b/src/lib/rng/info.txt @@ -1,5 +1,6 @@ +define AUTO_SEEDING_RNG 20131128 + <requires> entropy -auto_rng -hmac_rng +hmac_drbg </requires> diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index c17f23dd0..5501c143e 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -1,13 +1,23 @@ /* * Random Number Generator -* (C) 1999-2008 Jack Lloyd +* (C) 1999-2008,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ #include <botan/rng.h> -#include <botan/hmac_rng.h> +#include <botan/auto_rng.h> #include <botan/entropy_src.h> +#include <botan/loadstor.h> +#include <botan/internal/os_utils.h> + +#if defined(BOTAN_HAS_HMAC_DRBG) + #include <botan/hmac_drbg.h> +#endif + +#if defined(BOTAN_HAS_HMAC_RNG) + #include <botan/hmac_rng.h> +#endif namespace Botan { @@ -25,18 +35,110 @@ size_t RandomNumberGenerator::reseed_with_timeout(size_t bits_to_collect, timeout); } +size_t RandomNumberGenerator::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + return srcs.poll(*this, poll_bits, poll_timeout); + } + +Stateful_RNG::Stateful_RNG(size_t max_output_before_reseed) : m_max_output_before_reseed(max_output_before_reseed) + { + } + +void Stateful_RNG::clear() + { + m_successful_initialization = false; + m_bytes_since_reseed = 0; + m_last_pid = 0; + } + +size_t Stateful_RNG::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) + { + size_t bits_collected = RandomNumberGenerator::reseed_with_sources(srcs, poll_bits, poll_timeout); + + if(bits_collected >= poll_bits) + { + m_successful_initialization = true; + m_bytes_since_reseed = 0; + } + + return bits_collected; + } + +void Stateful_RNG::reseed_check(size_t bytes_requested) + { + const bool fork_detected = (m_last_pid > 0) && (OS::get_process_id() != m_last_pid); + + m_bytes_since_reseed += bytes_requested; + m_last_pid = OS::get_process_id(); + + if(!is_seeded() || fork_detected) + { + this->reseed(BOTAN_RNG_RESEED_POLL_BITS); + } + else if(m_max_output_before_reseed > 0 && m_bytes_since_reseed >= m_max_output_before_reseed) + { + this->reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS, + BOTAN_RNG_AUTO_RESEED_TIMEOUT); + } + + if(!is_seeded()) + { + throw PRNG_Unseeded(name()); + } + } + +void Stateful_RNG::initialize_with(const byte input[], size_t len) + { + add_entropy(input, len); + m_successful_initialization = true; + } + +bool Stateful_RNG::is_seeded() const + { + return m_successful_initialization; + } + RandomNumberGenerator* RandomNumberGenerator::make_rng() { - std::unique_ptr<MessageAuthenticationCode> h1(MessageAuthenticationCode::create("HMAC(SHA-512)")); - std::unique_ptr<MessageAuthenticationCode> h2(MessageAuthenticationCode::create("HMAC(SHA-512)")); + return new AutoSeeded_RNG; + } - if(!h1 || !h2) - throw Algorithm_Not_Found("HMAC_RNG HMACs"); - std::unique_ptr<RandomNumberGenerator> rng(new HMAC_RNG(h1.release(), h2.release())); +AutoSeeded_RNG::AutoSeeded_RNG(size_t max_output_before_reseed) + { + m_rng.reset(new BOTAN_AUTO_RNG_DRBG(BOTAN_AUTO_RNG_HASH, max_output_before_reseed)); - rng->reseed(256); + size_t bits = m_rng->reseed(BOTAN_AUTO_RNG_ENTROPY_TARGET); - return rng.release(); + if(!m_rng->is_seeded()) + { + throw Exception("AutoSeeded_RNG failed to gather enough entropy only got " + + std::to_string(bits) + " bits"); + } + } + +void AutoSeeded_RNG::randomize(byte output[], size_t output_len) + { + /* + Form additional input which is provided to the PRNG implementation + to paramaterize the KDF output. + */ + byte additional_input[24] = { 0 }; + store_le(OS::get_system_timestamp_ns(), additional_input); + store_le(OS::get_processor_timestamp(), additional_input + 8); + store_le(OS::get_process_id(), additional_input + 16); + store_le(m_counter++, additional_input + 20); + + randomize_with_input(output, output_len, additional_input, sizeof(additional_input)); + } + +void AutoSeeded_RNG::randomize_with_input(byte output[], size_t output_len, + const byte ad[], size_t ad_len) + { + m_rng->randomize_with_input(output, output_len, ad, ad_len); } } diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index 3fd3dcec8..7da560b85 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -1,6 +1,6 @@ /* -* RandomNumberGenerator -* (C) 1999-2009 Jack Lloyd +* Random Number Generator base classes +* (C) 1999-2009,2015,2016 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -19,16 +19,20 @@ namespace Botan { class Entropy_Sources; /** -* This class represents a random number (RNG) generator object. +* An interface to a generic RNG */ class BOTAN_DLL RandomNumberGenerator { public: - /** - * Create a seeded and active RNG object for general application use - * Added in 1.8.0 + virtual ~RandomNumberGenerator() = default; + + RandomNumberGenerator() = default; + + /* + * Never copy a RNG, create a new one */ - static RandomNumberGenerator* make_rng(); + RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; + RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; /** * Randomize a byte array. @@ -38,47 +42,38 @@ class BOTAN_DLL RandomNumberGenerator virtual void randomize(byte output[], size_t length) = 0; /** - * Return a random vector - * @param bytes number of bytes in the result - * @return randomized vector of length bytes + * 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. + * + * @param inputs a byte array containg the entropy to be added + * @param length the length of the byte array in */ - virtual secure_vector<byte> random_vec(size_t bytes) - { - secure_vector<byte> output(bytes); - randomize(output.data(), output.size()); - return output; - } + virtual void add_entropy(const byte input[], size_t length) = 0; /** - * Only usable with POD types, only useful with integers - * get_random<u64bit>() + * Incorporate some additional data into the RNG state. */ - template<typename T> T get_random() + template<typename T> void add_entropy_T(const T& t) { - T r; - this->randomize(reinterpret_cast<byte*>(&r), sizeof(r)); - return r; + add_entropy(reinterpret_cast<const uint8_t*>(&t), sizeof(T)); } /** - * Return a random byte - * @return random byte + * Incorporate entropy into the RNG state then produce output + * Some RNG types implement this using a single operation. */ - byte next_byte() { return get_random<byte>(); } - - byte next_nonzero_byte() + virtual void randomize_with_input(byte output[], size_t output_len, + const byte input[], size_t input_len) { - byte b = next_byte(); - while(b == 0) - b = next_byte(); - return b; + this->add_entropy(input, input_len); + this->randomize(output, output_len); } /** - * Check whether this RNG is seeded. - * @return true if this RNG was already seeded, false otherwise. + * Return the name of this object */ - virtual bool is_seeded() const = 0; + virtual std::string name() const = 0; /** * Clear all internally held values of this RNG. @@ -86,80 +81,170 @@ class BOTAN_DLL RandomNumberGenerator virtual void clear() = 0; /** - * Return the name of this object + * Check whether this RNG is seeded. + * @return true if this RNG was already seeded, false otherwise. */ - virtual std::string name() const = 0; + virtual bool is_seeded() const = 0; /** - * Seed this RNG using the global entropy sources and default timeout + * 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_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout); + + /** + * Reseed this RNG from the default entropy sources and a default timeout * @param bits_to_collect is the number of bits of entropy to - attempt to gather from the entropy sources + * attempt to gather from the entropy sources + * @param poll_timeout try not to run longer than this, even if + * not enough entropy has been collected */ - size_t reseed(size_t bits_to_collect); + size_t reseed(size_t bits_to_collect = BOTAN_RNG_RESEED_POLL_BITS); /** - * Seed this RNG using the global entropy sources + * Reseed this RNG from the default entropy sources * @param bits_to_collect is the number of bits of entropy to - attempt to gather from the entropy sources - * @param poll_timeout try not to run longer than this, no matter what + * attempt to gather from the entropy sources + * @param poll_timeout try not to run longer than this, even if + * not enough entropy has been collected */ size_t reseed_with_timeout(size_t bits_to_collect, std::chrono::milliseconds poll_timeout); /** + * Return a random vector + * @param bytes number of bytes in the result + * @return randomized vector of length bytes + */ + secure_vector<byte> random_vec(size_t bytes) + { + secure_vector<byte> output(bytes); + randomize(output.data(), output.size()); + return output; + } + + /** + * Return a random byte + * @return random byte + */ + byte next_byte() + { + byte b; + this->randomize(&b, 1); + return b; + } + + byte next_nonzero_byte() + { + byte b = next_byte(); + while(b == 0) + b = next_byte(); + return b; + } + + /** + * Create a seeded and active RNG object for general application use + * Added in 1.8.0 + * Use AutoSeeded_RNG instead + */ + static RandomNumberGenerator* make_rng(); + }; + +/** +* Inherited by RNGs which maintain in-process state, like HMAC_DRBG. +* On Unix these RNGs are vulnerable to problems with fork, where the +* RNG state is duplicated, and the parent and child process RNGs will +* produce identical output until one of them reseeds. Stateful_RNG +* reseeds itself whenever a fork is detected, or after a set number of +* bytes have been output. +* +* Not implemented by RNGs which access an external RNG, such as the +* system PRNG or a hardware RNG. +*/ +class BOTAN_DLL Stateful_RNG : public RandomNumberGenerator + { + public: + Stateful_RNG(size_t max_output_before_reseed); + + virtual bool is_seeded() const override final; + + /** + * Consume this input and mark the RNG as initialized regardless + * of the length of the input or the current seeded state of + * the RNG. + */ + void initialize_with(const byte input[], size_t length); + + /** * Poll provided sources for up to poll_bits bits of entropy * or until the timeout expires. Returns estimate of the number * of bits collected. */ - virtual size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) = 0; + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; + + protected: + void reseed_check(size_t bytes_requested); + + void clear() override; /** - * Add entropy to this RNG. - * @param in a byte array containg the entropy to be added - * @param length the length of the byte array in + * Mark state as requiring a reseed on next use */ - virtual void add_entropy(const byte in[], size_t length) = 0; + void force_reseed() { m_bytes_since_reseed = m_max_output_before_reseed; } - /* - * Never copy a RNG, create a new one - */ - RandomNumberGenerator(const RandomNumberGenerator& rng) = delete; - RandomNumberGenerator& operator=(const RandomNumberGenerator& rng) = delete; + uint32_t last_pid() const { return m_last_pid; } + + mutable std::mutex m_mutex; - RandomNumberGenerator() {} - virtual ~RandomNumberGenerator() {} + private: + const size_t m_max_output_before_reseed; + size_t m_bytes_since_reseed = 0; + uint32_t m_last_pid = 0; + bool m_successful_initialization = false; }; +/** +* Convenience typedef +*/ typedef RandomNumberGenerator RNG; /** +* Hardware RNG has no members but exists to tag hardware RNG types +*/ +class BOTAN_DLL Hardware_RNG : public RandomNumberGenerator + { + }; + +/** * Null/stub RNG - fails if you try to use it for anything +* This is not generally useful except for in certain tests */ -class BOTAN_DLL Null_RNG : public RandomNumberGenerator +class BOTAN_DLL Null_RNG final : public RandomNumberGenerator { public: - void randomize(byte[], size_t) override { throw PRNG_Unseeded("Null_RNG"); } + bool is_seeded() const override { return false; } void clear() override {} - std::string name() const override { return "Null_RNG"; } - - size_t reseed_with_sources(Entropy_Sources&, size_t, - std::chrono::milliseconds) override + void randomize(byte[], size_t) override { - return 0; + throw Exception("Null_RNG called"); } - bool is_seeded() const override { return false; } void add_entropy(const byte[], size_t) override {} + + std::string name() const override { return "Null_RNG"; } }; /** * Wraps access to a RNG in a mutex */ -class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator +class BOTAN_DLL Serialized_RNG final : public RandomNumberGenerator { public: void randomize(byte out[], size_t len) override diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp index 81e235a8c..135f4fabd 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -28,32 +28,23 @@ namespace Botan { namespace { -class System_RNG_Impl : public RandomNumberGenerator +class System_RNG_Impl final : public RandomNumberGenerator { public: System_RNG_Impl(); ~System_RNG_Impl(); - void randomize(byte buf[], size_t len) override; - bool is_seeded() const override { return true; } + void clear() override {} - std::string name() const override { return "system"; } - size_t reseed_with_sources(Entropy_Sources&, - size_t /*poll_bits*/, - std::chrono::milliseconds /*timeout*/) override - { - // We ignore it and assert the PRNG is seeded. - // TODO: could poll and write it to /dev/urandom to help seed it - return 0; - } + void randomize(uint8_t out[], size_t len) override; - void add_entropy(const byte[], size_t) override - { - } - private: + void add_entropy(const uint8_t in[], size_t length) override; + + std::string name() const override; + private: #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) HCRYPTPROV m_prov; #else @@ -61,6 +52,15 @@ class System_RNG_Impl : public RandomNumberGenerator #endif }; +std::string System_RNG_Impl::name() const + { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + return "cryptoapi"; +#else + return BOTAN_SYSTEM_RNG_DEVICE; +#endif + } + System_RNG_Impl::System_RNG_Impl() { #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) @@ -74,7 +74,7 @@ System_RNG_Impl::System_RNG_Impl() #define O_NOCTTY 0 #endif - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); if(m_fd < 0) throw Exception("System_RNG failed to open RNG device"); #endif @@ -90,7 +90,61 @@ System_RNG_Impl::~System_RNG_Impl() #endif } -void System_RNG_Impl::randomize(byte buf[], size_t len) +void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) + { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + /* + There is no explicit ConsumeRandom, but all values provided in + the call are incorporated into the state. + + TODO: figure out a way to avoid this copy. Byte at a time updating + seems worse than the allocation. + + for(size_t i = 0; i != len; ++i) + { + uint8_t b = input[i]; + ::CryptGenRandom(m_prov, 1, &b); + } + */ + + if(len > 0) + { + secure_vector<uint8_t> buf(input, input + len); + ::CryptGenRandom(m_prov, static_cast<DWORD>(buf.size()), buf.data()); + } +#else + 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 Darwin 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. + */ + if(errno == EPERM) + return; + + // maybe just ignore any failure here and return? + throw Exception("System_RNG write failed error " + std::to_string(errno)); + } + + input += got; + len -= got; + } +#endif + } + +void System_RNG_Impl::randomize(uint8_t buf[], size_t len) { #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) ::CryptGenRandom(m_prov, static_cast<DWORD>(len), buf); diff --git a/src/lib/rng/system_rng/system_rng.h b/src/lib/rng/system_rng/system_rng.h index 6290b8769..9cf31e78b 100644 --- a/src/lib/rng/system_rng/system_rng.h +++ b/src/lib/rng/system_rng/system_rng.h @@ -22,29 +22,18 @@ BOTAN_DLL RandomNumberGenerator& system_rng(); /* * Instantiatable reference to the system RNG. */ -class BOTAN_DLL System_RNG : public RandomNumberGenerator +class BOTAN_DLL System_RNG final : public RandomNumberGenerator { public: - System_RNG() : m_rng(system_rng()) {} + std::string name() const override { return system_rng().name(); } - void randomize(Botan::byte out[], size_t len) override { m_rng.randomize(out, len); } + void randomize(uint8_t out[], size_t len) override { system_rng().randomize(out, len); } - bool is_seeded() const override { return m_rng.is_seeded(); } + void add_entropy(const uint8_t in[], size_t length) override { system_rng().add_entropy(in, length); } - void clear() override { m_rng.clear(); } + bool is_seeded() const override { return true; } - std::string name() const override { return m_rng.name(); } - - size_t reseed_with_sources(Entropy_Sources& srcs, - size_t poll_bits, - std::chrono::milliseconds poll_timeout) override - { - return m_rng.reseed_with_sources(srcs, poll_bits, poll_timeout); - } - - void add_entropy(const byte in[], size_t len) override { m_rng.add_entropy(in, len); } - private: - Botan::RandomNumberGenerator& m_rng; + void clear() override {} }; } diff --git a/src/lib/utils/zero_mem.cpp b/src/lib/utils/zero_mem.cpp index 371c434ca..df195048a 100644 --- a/src/lib/utils/zero_mem.cpp +++ b/src/lib/utils/zero_mem.cpp @@ -18,6 +18,13 @@ void zero_mem(void* ptr, size_t n) #if defined(BOTAN_TARGET_OS_HAS_RTLSECUREZEROMEMORY) ::RtlSecureZeroMemory(ptr, n); #elif defined(BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO) && (BOTAN_USE_VOLATILE_MEMSET_FOR_ZERO == 1) + /* + Call memset through a static volatile pointer, which the compiler + should not elide. This construct should be safe in conforming + compilers, but who knows. I did confirm that on x86-64 GCC 6.1 and + Clang 3.8 both create code that saves the memset address in the + data segment and uncondtionally loads and jumps to that address. + */ static void* (*const volatile memset_ptr)(void*, int, size_t) = std::memset; (memset_ptr)(ptr, 0, n); #else |