diff options
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 |