diff options
Diffstat (limited to 'src')
46 files changed, 459 insertions, 156 deletions
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index 31277ff0c..a289fbcd8 100644 --- a/src/build-data/buildh.in +++ b/src/build-data/buildh.in @@ -101,10 +101,43 @@ * RNGs will automatically poll the system for additional seed material * after producing this many bytes of output. */ -#define BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED 512 +#define BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED 4096 #define BOTAN_RNG_RESEED_POLL_BITS 128 #define BOTAN_RNG_AUTO_RESEED_TIMEOUT std::chrono::milliseconds(10) -#define BOTAN_RNG_RESEED_DEFAULT_TIMEOUT std::chrono::milliseconds(100) +#define BOTAN_RNG_RESEED_DEFAULT_TIMEOUT std::chrono::milliseconds(50) + +/* +* Specifies (in order) the list of entropy sources that will be used +* to seed an in-memory RNG. The first few in the default list +* ("timer", "proc_info", etc) do not count as contributing any entropy +* but are included as they are fast and help protect against a +* seriously broken system RNG. +*/ +#define BOTAN_ENTROPY_DEFAULT_SOURCES \ + { "timestamp", "rdrand", "proc_info", \ + "darwin_secrandom", "dev_random", "win32_cryptoapi", "egd", \ + "proc_walk", "system_stats", "unix_procs" } + +/* +* These control the RNG used by the system RNG interface +*/ +#define BOTAN_SYSTEM_RNG_DEVICE "/dev/urandom" +#define BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE PROV_RSA_FULL + +/* +* These paramaters control how many bytes to read from the system +* PRNG, and how long to block if applicable. +* +* Timeout is ignored on Windows as CryptGenRandom doesn't block +*/ +#define BOTAN_SYSTEM_RNG_POLL_DEVICES { "/dev/urandom", "/dev/random", "/dev/srandom" } + +#define BOTAN_SYSTEM_RNG_POLL_REQUEST 64 +#define BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS 20 + +#define BOTAN_ENTROPY_EGD_PATHS { "/var/run/egd-pool", "/dev/egd-pool" } +#define BOTAN_ENTROPY_PROC_FS_PATH "/proc" +#define BOTAN_ENTROPY_SAFE_PATHS { "/bin", "/sbin", "/usr/bin", "/usr/sbin" } /* Should we use GCC-style inline assembler? */ #if !defined(BOTAN_USE_GCC_INLINE_ASM) && defined(__GNUG__) diff --git a/src/build-data/os/aix.txt b/src/build-data/os/aix.txt index 546fbf9ad..369b720c4 100644 --- a/src/build-data/os/aix.txt +++ b/src/build-data/os/aix.txt @@ -2,5 +2,4 @@ os_type unix <target_features> gettimeofday -getsid </target_features> diff --git a/src/build-data/os/cygwin.txt b/src/build-data/os/cygwin.txt index 7788cd3ca..ee934f787 100644 --- a/src/build-data/os/cygwin.txt +++ b/src/build-data/os/cygwin.txt @@ -12,6 +12,5 @@ doc_dir docs <target_features> gettimeofday -getsid timegm </target_features> diff --git a/src/build-data/os/darwin.txt b/src/build-data/os/darwin.txt index 56285ccf7..b1f2363dc 100644 --- a/src/build-data/os/darwin.txt +++ b/src/build-data/os/darwin.txt @@ -16,7 +16,6 @@ gettimeofday gmtime_r memset_s readdir -getsid timegm </target_features> diff --git a/src/build-data/os/freebsd.txt b/src/build-data/os/freebsd.txt index 32767cb1f..510a7accd 100644 --- a/src/build-data/os/freebsd.txt +++ b/src/build-data/os/freebsd.txt @@ -7,6 +7,5 @@ posix_mlock gmtime_r dlopen readdir -getsid timegm </target_features> diff --git a/src/build-data/os/haiku.txt b/src/build-data/os/haiku.txt index 981ce6795..8e35e76eb 100644 --- a/src/build-data/os/haiku.txt +++ b/src/build-data/os/haiku.txt @@ -8,7 +8,6 @@ doc_dir system/documentation <target_features> gettimeofday gmtime_r -getsid </target_features> <aliases> diff --git a/src/build-data/os/hpux.txt b/src/build-data/os/hpux.txt index 726b0b960..22d51ef0a 100644 --- a/src/build-data/os/hpux.txt +++ b/src/build-data/os/hpux.txt @@ -2,7 +2,6 @@ os_type unix <target_features> gettimeofday -getsid </target_features> <aliases> diff --git a/src/build-data/os/irix.txt b/src/build-data/os/irix.txt index 546fbf9ad..369b720c4 100644 --- a/src/build-data/os/irix.txt +++ b/src/build-data/os/irix.txt @@ -2,5 +2,4 @@ os_type unix <target_features> gettimeofday -getsid </target_features> diff --git a/src/build-data/os/linux.txt b/src/build-data/os/linux.txt index c83e57afa..bb65055e4 100644 --- a/src/build-data/os/linux.txt +++ b/src/build-data/os/linux.txt @@ -11,7 +11,6 @@ posix_mlock gmtime_r dlopen readdir -getsid timegm </target_features> diff --git a/src/build-data/os/netbsd.txt b/src/build-data/os/netbsd.txt index 32767cb1f..510a7accd 100644 --- a/src/build-data/os/netbsd.txt +++ b/src/build-data/os/netbsd.txt @@ -7,6 +7,5 @@ posix_mlock gmtime_r dlopen readdir -getsid timegm </target_features> diff --git a/src/build-data/os/qnx.txt b/src/build-data/os/qnx.txt index 71c85eb3c..4c0965764 100644 --- a/src/build-data/os/qnx.txt +++ b/src/build-data/os/qnx.txt @@ -6,5 +6,4 @@ gettimeofday posix_mlock gmtime_r dlopen -getsid </target_features> diff --git a/src/build-data/os/solaris.txt b/src/build-data/os/solaris.txt index 52749e397..0ed785036 100644 --- a/src/build-data/os/solaris.txt +++ b/src/build-data/os/solaris.txt @@ -6,7 +6,6 @@ install_cmd_exec '/usr/ucb/install -m 755' <target_features> posix_mlock gettimeofday -getsid </target_features> <aliases> diff --git a/src/cmd/rng.cpp b/src/cmd/rng.cpp index 901607bea..b89376b4c 100644 --- a/src/cmd/rng.cpp +++ b/src/cmd/rng.cpp @@ -49,7 +49,7 @@ int rng(int argc, char* argv[]) return total_collected >= amt; }); - EntropySource::poll_available_sources(accum); + Entropy_Sources::global_sources().poll(accum); } } catch(std::exception& e) diff --git a/src/lib/entropy/beos_stats/es_beos.h b/src/lib/entropy/beos_stats/es_beos.h index 2565b9180..db5824f6f 100644 --- a/src/lib/entropy/beos_stats/es_beos.h +++ b/src/lib/entropy/beos_stats/es_beos.h @@ -15,10 +15,10 @@ namespace Botan { /** * BeOS Entropy Source */ -class BeOS_EntropySource : public EntropySource +class BeOS_EntropySource : public Entropy_Source { private: - std::string name() const override { return "BeOS Statistics"; } + std::string name() const override { return "system_stats"; } void poll(Entropy_Accumulator& accum) override; }; diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.cpp b/src/lib/entropy/cryptoapi_rng/es_capi.cpp index 019b55a10..6ffc03c12 100644 --- a/src/lib/entropy/cryptoapi_rng/es_capi.cpp +++ b/src/lib/entropy/cryptoapi_rng/es_capi.cpp @@ -57,15 +57,17 @@ class CSP_Handle */ void Win32_CAPI_EntropySource::poll(Entropy_Accumulator& accum) { - m_buf.resize(32); + const size_t ENTROPY_BITS_PER_BYTE = 8; + + secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); for(size_t i = 0; i != prov_types.size(); ++i) { CSP_Handle csp(prov_types[i]); - if(size_t got = csp.gen_random(m_buf.data(), m_buf.size())) + if(size_t got = csp.gen_random(buf.data(), buf.size())) { - accum.add(m_buf.data(), got, 6); + accum.add(buf.data(), got, ENTROPY_BITS_PER_BYTE); break; } } diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.h b/src/lib/entropy/cryptoapi_rng/es_capi.h index 81a5003b2..eb63183e9 100644 --- a/src/lib/entropy/cryptoapi_rng/es_capi.h +++ b/src/lib/entropy/cryptoapi_rng/es_capi.h @@ -16,10 +16,10 @@ namespace Botan { /** * Win32 CAPI Entropy Source */ -class Win32_CAPI_EntropySource : public EntropySource +class Win32_CAPI_EntropySource : public Entropy_Source { public: - std::string name() const override { return "Win32 CryptoGenRandom"; } + std::string name() const override { return "win32_cryptoapi"; } void poll(Entropy_Accumulator& accum) override; @@ -30,7 +30,6 @@ class Win32_CAPI_EntropySource : public EntropySource Win32_CAPI_EntropySource(const std::string& provs = ""); private: std::vector<u64bit> prov_types; - secure_vector<byte> m_buf; }; } diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp index f04b75a12..08b464ff0 100644 --- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp +++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.cpp @@ -16,12 +16,12 @@ namespace Botan { void Darwin_SecRandom::poll(Entropy_Accumulator& accum) { const size_t ENTROPY_BITS_PER_BYTE = 8; - const size_t BUF_SIZE = 256; - m_buf.resize(BUF_SIZE); - if (0 == SecRandomCopyBytes(kSecRandomDefault, m_buf.size(), m_buf.data())) + secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); + + if(0 == SecRandomCopyBytes(kSecRandomDefault, buf.size(), buf.data())) { - accum.add(m_buf.data(), m_buf.size(), ENTROPY_BITS_PER_BYTE); + accum.add(buf.data(), buf.size(), ENTROPY_BITS_PER_BYTE); } } diff --git a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h index 504d5cc64..970cd7941 100644 --- a/src/lib/entropy/darwin_secrandom/darwin_secrandom.h +++ b/src/lib/entropy/darwin_secrandom/darwin_secrandom.h @@ -15,15 +15,12 @@ namespace Botan { /** * Entropy source using SecRandomCopyBytes from Darwin's Security.framework */ -class Darwin_SecRandom : public EntropySource +class Darwin_SecRandom : public Entropy_Source { public: - std::string name() const override { return "Darwin SecRandomCopyBytes"; } + std::string name() const override { return "darwin_secrandom"; } void poll(Entropy_Accumulator& accum) override; - - private: - secure_vector<byte> m_buf; }; } diff --git a/src/lib/entropy/dev_random/dev_random.cpp b/src/lib/entropy/dev_random/dev_random.cpp index 526835fea..0115368da 100644 --- a/src/lib/entropy/dev_random/dev_random.cpp +++ b/src/lib/entropy/dev_random/dev_random.cpp @@ -6,7 +6,6 @@ */ #include <botan/internal/dev_random.h> -#include <botan/internal/rounding.h> #include <sys/types.h> #include <sys/select.h> @@ -61,10 +60,8 @@ void Device_EntropySource::poll(Entropy_Accumulator& accum) return; const size_t ENTROPY_BITS_PER_BYTE = 8; - const size_t MS_WAIT_TIME = 32; - const size_t READ_ATTEMPT = 32; - int max_fd = m_devices[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) @@ -75,21 +72,21 @@ void Device_EntropySource::poll(Entropy_Accumulator& accum) struct ::timeval timeout; - timeout.tv_sec = (MS_WAIT_TIME / 1000); - timeout.tv_usec = (MS_WAIT_TIME % 1000) * 1000; + timeout.tv_sec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS / 1000); + timeout.tv_usec = (BOTAN_SYSTEM_RNG_POLL_TIMEOUT_MS % 1000) * 1000; if(::select(max_fd + 1, &read_set, nullptr, nullptr, &timeout) < 0) return; - m_buf.resize(READ_ATTEMPT); + secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); for(size_t i = 0; i != m_devices.size(); ++i) { if(FD_ISSET(m_devices[i], &read_set)) { - const ssize_t got = ::read(m_devices[i], m_buf.data(), m_buf.size()); + const ssize_t got = ::read(m_devices[i], buf.data(), buf.size()); if(got > 0) - accum.add(m_buf.data(), got, ENTROPY_BITS_PER_BYTE); + accum.add(buf.data(), got, ENTROPY_BITS_PER_BYTE); } } } diff --git a/src/lib/entropy/dev_random/dev_random.h b/src/lib/entropy/dev_random/dev_random.h index 0d0c2df60..f634cf16c 100644 --- a/src/lib/entropy/dev_random/dev_random.h +++ b/src/lib/entropy/dev_random/dev_random.h @@ -17,10 +17,10 @@ namespace Botan { /** * Entropy source reading from kernel devices like /dev/random */ -class Device_EntropySource : public EntropySource +class Device_EntropySource : public Entropy_Source { public: - std::string name() const override { return "RNG Device Reader"; } + std::string name() const override { return "dev_random"; } void poll(Entropy_Accumulator& accum) override; @@ -28,8 +28,6 @@ class Device_EntropySource : public EntropySource ~Device_EntropySource(); private: typedef int fd_type; - - secure_vector<byte> m_buf; std::vector<fd_type> m_devices; }; diff --git a/src/lib/entropy/egd/es_egd.cpp b/src/lib/entropy/egd/es_egd.cpp index d64b87ba1..9b625d051 100644 --- a/src/lib/entropy/egd/es_egd.cpp +++ b/src/lib/entropy/egd/es_egd.cpp @@ -137,19 +137,19 @@ EGD_EntropySource::~EGD_EntropySource() */ void EGD_EntropySource::poll(Entropy_Accumulator& accum) { - const size_t READ_ATTEMPT = 32; + const size_t ENTROPY_BITS_PER_BYTE = 8; std::lock_guard<std::mutex> lock(m_mutex); - m_buf.resize(READ_ATTEMPT); + secure_vector<byte>& buf = accum.get_io_buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); for(size_t i = 0; i != sockets.size(); ++i) { - size_t got = sockets[i].read(m_buf.data(), m_buf.size()); + size_t got = sockets[i].read(buf.data(), buf.size()); if(got) { - accum.add(m_buf.data(), got, 6); + accum.add(buf.data(), got, ENTROPY_BITS_PER_BYTE); break; } } diff --git a/src/lib/entropy/egd/es_egd.h b/src/lib/entropy/egd/es_egd.h index 7f7df1133..0b497a8bd 100644 --- a/src/lib/entropy/egd/es_egd.h +++ b/src/lib/entropy/egd/es_egd.h @@ -18,10 +18,10 @@ namespace Botan { /** * EGD Entropy Source */ -class EGD_EntropySource : public EntropySource +class EGD_EntropySource : public Entropy_Source { public: - std::string name() const override { return "EGD/PRNGD"; } + std::string name() const override { return "egd"; } void poll(Entropy_Accumulator& accum) override; @@ -44,7 +44,6 @@ class EGD_EntropySource : public EntropySource std::mutex m_mutex; std::vector<EGD_Socket> sockets; - secure_vector<byte> m_buf; }; } diff --git a/src/lib/entropy/entropy_src.h b/src/lib/entropy/entropy_src.h index c635b8756..0f4c38358 100644 --- a/src/lib/entropy/entropy_src.h +++ b/src/lib/entropy/entropy_src.h @@ -66,18 +66,27 @@ class BOTAN_DLL Entropy_Accumulator { 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 EntropySource +class BOTAN_DLL Entropy_Source { public: - static void poll_available_sources(class Entropy_Accumulator& accum); + /* + * Return a new entropy source of a particular type, or null + * Each entropy source may require substantial resources (eg, a file handle + * or socket instance), so try to share them among multiple RNGs, or just + * use the preconfigured global list accessed by global_entropy_sources() + */ + static std::unique_ptr<Entropy_Source> create(const std::string& type); /** * @return name identifying this entropy source @@ -90,7 +99,27 @@ class BOTAN_DLL EntropySource */ virtual void poll(Entropy_Accumulator& accum) = 0; - virtual ~EntropySource() {} + virtual ~Entropy_Source() {} + }; + +class BOTAN_DLL Entropy_Sources + { + public: + static Entropy_Sources& global_sources(); + + void add_source(std::unique_ptr<Entropy_Source> src); + + std::vector<std::string> enabled_sources() const; + + void poll(Entropy_Accumulator& accum); + bool poll_just(Entropy_Accumulator& accum, const std::string& src); + + Entropy_Sources() {} + Entropy_Sources(const std::vector<std::string>& sources); + + ~Entropy_Sources(); + private: + std::vector<Entropy_Source*> m_srcs; }; } diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp index d57160c88..cbf13d488 100644 --- a/src/lib/entropy/entropy_srcs.cpp +++ b/src/lib/entropy/entropy_srcs.cpp @@ -49,83 +49,147 @@ namespace Botan { -namespace { - -std::vector<std::unique_ptr<EntropySource>> get_default_entropy_sources() +std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name) { - std::vector<std::unique_ptr<EntropySource>> sources; - + if(name == "timestamp") + { #if defined(BOTAN_HAS_ENTROPY_SRC_HIGH_RESOLUTION_TIMER) - sources.push_back(std::unique_ptr<EntropySource>(new High_Resolution_Timestamp)); + return std::unique_ptr<Entropy_Source>(new High_Resolution_Timestamp); #endif + } + if(name == "rdrand") + { #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND) - sources.push_back(std::unique_ptr<EntropySource>(new Intel_Rdrand)); + return std::unique_ptr<Entropy_Source>(new Intel_Rdrand); #endif + } + if(name == "proc_info") + { #if defined(BOTAN_HAS_ENTROPY_SRC_UNIX_PROCESS_RUNNER) - sources.push_back(std::unique_ptr<EntropySource>(new UnixProcessInfo_EntropySource)); + return std::unique_ptr<Entropy_Source>(new UnixProcessInfo_EntropySource); #endif + } -#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) - sources.push_back(std::unique_ptr<EntropySource>(new Device_EntropySource( - { "/dev/random", "/dev/srandom", "/dev/urandom" } - ))); + if(name == "darwin_secrandom") + { +#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) + return std::unique_ptr<Entropy_Source>(new Darwin_SecRandom); #endif + } -#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI) - sources.push_back(std::unique_ptr<EntropySource>(new Win32_CAPI_EntropySource)); + if(name == "dev_random") + { +#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) + return std::unique_ptr<Entropy_Source>(new Device_EntropySource(BOTAN_SYSTEM_RNG_POLL_DEVICES)); + } + + if(name == "win32_cryptoapi") + { +#elif defined(BOTAN_HAS_ENTROPY_SRC_CAPI) + return std::unique_ptr<Entropy_Source>(new Win32_CAPI_EntropySource); #endif + } + if(name == "proc_walk") + { #if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER) - sources.push_back(std::unique_ptr<EntropySource>(new ProcWalking_EntropySource("/proc"))); + return std::unique_ptr<Entropy_Source>(new ProcWalking_EntropySource("/proc")); #endif + } + if(name == "system_stats") + { #if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) - sources.push_back(std::unique_ptr<EntropySource>(new Win32_EntropySource)); -#endif - -#if defined(BOTAN_HAS_ENTROPY_SRC_BEOS) - sources.push_back(std::unique_ptr<EntropySource>(new BeOS_EntropySource)); + return std::unique_ptr<Entropy_Source>(new Win32_EntropySource); +#elif defined(BOTAN_HAS_ENTROPY_SRC_BEOS) + return std::unique_ptr<Entropy_Source>(new BeOS_EntropySource); #endif + } + if(name == "unix_procs") + { #if defined(BOTAN_HAS_ENTROPY_SRC_UNIX_PROCESS_RUNNER) - sources.push_back(std::unique_ptr<EntropySource>(new Unix_EntropySource( - { "/bin", "/sbin", "/usr/bin", "/usr/sbin" } - ))); + return std::unique_ptr<Entropy_Source>(new Unix_EntropySource(BOTAN_ENTROPY_SAFE_PATHS)); #endif + } + if(name == "egd") + { #if defined(BOTAN_HAS_ENTROPY_SRC_EGD) - sources.push_back(std::unique_ptr<EntropySource>( - new EGD_EntropySource({ "/var/run/egd-pool", "/dev/egd-pool" }) - )); + return std::unique_ptr<Entropy_Source>(new EGD_EntropySource(BOTAN_ENTROPY_EGD_PATHS)); #endif + } -#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) - sources.push_back(std::unique_ptr<EntropySource>(new Darwin_SecRandom)); -#endif + return std::unique_ptr<Entropy_Source>(); + } +void Entropy_Sources::add_source(std::unique_ptr<Entropy_Source> src) + { + if(src.get()) + { + m_srcs.push_back(src.release()); + } + } + +std::vector<std::string> Entropy_Sources::enabled_sources() const + { + std::vector<std::string> sources; + for(size_t i = 0; i != m_srcs.size(); ++i) + { + sources.push_back(m_srcs[i]->name()); + } return sources; } -} +void Entropy_Sources::poll(Entropy_Accumulator& accum) + { + for(size_t i = 0; i != m_srcs.size(); ++i) + { + m_srcs[i]->poll(accum); + if(accum.polling_goal_achieved()) + break; + } + } -//static -void EntropySource::poll_available_sources(class Entropy_Accumulator& accum) +bool Entropy_Sources::poll_just(Entropy_Accumulator& accum, const std::string& the_src) { - static std::vector<std::unique_ptr<EntropySource>> g_sources(get_default_entropy_sources()); + for(size_t i = 0; i != m_srcs.size(); ++i) + { + if(m_srcs[i]->name() == the_src) + { + m_srcs[i]->poll(accum); + return true; + } + } - if(g_sources.empty()) - throw std::runtime_error("No entropy sources enabled at build time, RNG poll failed"); + return false; + } - size_t poll_attempt = 0; +Entropy_Sources::Entropy_Sources(const std::vector<std::string>& sources) + { + for(auto&& src_name : sources) + { + add_source(Entropy_Source::create(src_name)); + } + } - while(!accum.polling_finished() && poll_attempt < 16) +Entropy_Sources::~Entropy_Sources() + { + for(size_t i = 0; i != m_srcs.size(); ++i) { - const size_t src_idx = poll_attempt % g_sources.size(); - g_sources[src_idx]->poll(accum); - ++poll_attempt; + delete m_srcs[i]; + m_srcs[i] = nullptr; } + m_srcs.clear(); + } + +Entropy_Sources& Entropy_Sources::global_sources() + { + static Entropy_Sources global_entropy_sources(BOTAN_ENTROPY_DEFAULT_SOURCES); + + return global_entropy_sources; } } diff --git a/src/lib/entropy/hres_timer/hres_timer.h b/src/lib/entropy/hres_timer/hres_timer.h index b5b92fd97..93ced283a 100644 --- a/src/lib/entropy/hres_timer/hres_timer.h +++ b/src/lib/entropy/hres_timer/hres_timer.h @@ -18,10 +18,10 @@ namespace Botan { * @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 : public EntropySource +class High_Resolution_Timestamp : public Entropy_Source { public: - std::string name() const override { return "High Resolution Timestamp"; } + std::string name() const override { return "timestamp"; } void poll(Entropy_Accumulator& accum) override; }; diff --git a/src/lib/entropy/info.txt b/src/lib/entropy/info.txt index 77c2669e9..ba5a4044d 100644 --- a/src/lib/entropy/info.txt +++ b/src/lib/entropy/info.txt @@ -1 +1 @@ -define ENTROPY_SOURCE 20150201 +define ENTROPY_SOURCE 20151120 diff --git a/src/lib/entropy/proc_walk/proc_walk.cpp b/src/lib/entropy/proc_walk/proc_walk.cpp index 3d63e5d5a..817aa80a5 100644 --- a/src/lib/entropy/proc_walk/proc_walk.cpp +++ b/src/lib/entropy/proc_walk/proc_walk.cpp @@ -113,7 +113,7 @@ int Directory_Walker::next_fd() void ProcWalking_EntropySource::poll(Entropy_Accumulator& accum) { const size_t MAX_FILES_READ_PER_POLL = 2048; - const double ENTROPY_ESTIMATE = 1.0 / (8*1024); + const double ENTROPY_ESTIMATE = 1.0 / 128; std::lock_guard<std::mutex> lock(m_mutex); diff --git a/src/lib/entropy/proc_walk/proc_walk.h b/src/lib/entropy/proc_walk/proc_walk.h index ec56f9e2d..b67f71111 100644 --- a/src/lib/entropy/proc_walk/proc_walk.h +++ b/src/lib/entropy/proc_walk/proc_walk.h @@ -23,10 +23,10 @@ class File_Descriptor_Source /** * File Tree Walking Entropy Source */ -class ProcWalking_EntropySource : public EntropySource +class ProcWalking_EntropySource : public Entropy_Source { public: - std::string name() const override { return "Proc Walker"; } + std::string name() const override { return "proc_walk"; } void poll(Entropy_Accumulator& accum) override; diff --git a/src/lib/entropy/rdrand/rdrand.h b/src/lib/entropy/rdrand/rdrand.h index 9ff6e557f..1fa928641 100644 --- a/src/lib/entropy/rdrand/rdrand.h +++ b/src/lib/entropy/rdrand/rdrand.h @@ -16,10 +16,10 @@ namespace Botan { * Entropy source using the rdrand instruction first introduced on * Intel's Ivy Bridge architecture. */ -class Intel_Rdrand : public EntropySource +class Intel_Rdrand : public Entropy_Source { public: - std::string name() const override { return "Intel Rdrand"; } + std::string name() const override { return "rdrand"; } void poll(Entropy_Accumulator& accum) override; }; diff --git a/src/lib/entropy/unix_procs/unix_procs.cpp b/src/lib/entropy/unix_procs/unix_procs.cpp index c6ad6a700..abfe341e0 100644 --- a/src/lib/entropy/unix_procs/unix_procs.cpp +++ b/src/lib/entropy/unix_procs/unix_procs.cpp @@ -43,8 +43,8 @@ size_t concurrent_processes(size_t user_request) const size_t DEFAULT_CONCURRENT = 2; const size_t MAX_CONCURRENT = 8; - if(user_request > 0 && user_request < MAX_CONCURRENT) - return user_request; + if(user_request > 0) + return std::min(user_request, MAX_CONCURRENT); const long online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); @@ -72,9 +72,6 @@ void UnixProcessInfo_EntropySource::poll(Entropy_Accumulator& accum) accum.add(::getppid(), 0.0); accum.add(::getuid(), 0.0); accum.add(::getgid(), 0.0); -#if defined(BOTAN_TARGET_OS_HAS_GETSID) - accum.add(::getsid(0), 0.0); -#endif accum.add(::getpgrp(), 0.0); struct ::rusage usage; diff --git a/src/lib/entropy/unix_procs/unix_procs.h b/src/lib/entropy/unix_procs/unix_procs.h index 808d34221..bc2fd87d1 100644 --- a/src/lib/entropy/unix_procs/unix_procs.h +++ b/src/lib/entropy/unix_procs/unix_procs.h @@ -20,10 +20,10 @@ namespace Botan { * effective against local attackers as they can sample from the same * distribution. */ -class Unix_EntropySource : public EntropySource +class Unix_EntropySource : public Entropy_Source { public: - std::string name() const override { return "Unix Process Runner"; } + std::string name() const override { return "unix_procs"; } void poll(Entropy_Accumulator& accum) override; @@ -78,10 +78,10 @@ class Unix_EntropySource : public EntropySource secure_vector<byte> m_buf; }; -class UnixProcessInfo_EntropySource : public EntropySource +class UnixProcessInfo_EntropySource : public Entropy_Source { public: - std::string name() const override { return "Unix Process Info"; } + std::string name() const override { return "proc_info"; } void poll(Entropy_Accumulator& accum) override; }; diff --git a/src/lib/entropy/win32_stats/es_win32.h b/src/lib/entropy/win32_stats/es_win32.h index 98bfb0e36..958a79e19 100644 --- a/src/lib/entropy/win32_stats/es_win32.h +++ b/src/lib/entropy/win32_stats/es_win32.h @@ -15,10 +15,10 @@ namespace Botan { /** * Win32 Entropy Source */ -class Win32_EntropySource : public EntropySource +class Win32_EntropySource : public Entropy_Source { public: - std::string name() const override { return "Win32 Statistics"; } + std::string name() const override { return "system_stats"; } void poll(Entropy_Accumulator& accum) override; }; diff --git a/src/lib/rng/auto_rng/auto_rng.h b/src/lib/rng/auto_rng/auto_rng.h index a7b28af92..ce0c5a7d2 100644 --- a/src/lib/rng/auto_rng/auto_rng.h +++ b/src/lib/rng/auto_rng/auto_rng.h @@ -25,7 +25,12 @@ class BOTAN_DLL AutoSeeded_RNG : public RandomNumberGenerator std::string name() const override { return m_rng->name(); } - void reseed(size_t poll_bits = 256) override { m_rng->reseed(poll_bits); } + 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); } diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.cpp b/src/lib/rng/hmac_drbg/hmac_drbg.cpp index ad731b6b3..67325ee1b 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.cpp +++ b/src/lib/rng/hmac_drbg/hmac_drbg.cpp @@ -78,11 +78,13 @@ void HMAC_DRBG::update(const byte input[], size_t input_len) } } -void HMAC_DRBG::reseed(size_t poll_bits) +size_t HMAC_DRBG::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) { if(m_prng) { - m_prng->reseed(poll_bits); + size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout); if(m_prng->is_seeded()) { @@ -90,7 +92,11 @@ void HMAC_DRBG::reseed(size_t poll_bits) update(input.data(), input.size()); m_reseed_counter = 1; } + + return bits; } + + return 0; } void HMAC_DRBG::add_entropy(const byte input[], size_t length) diff --git a/src/lib/rng/hmac_drbg/hmac_drbg.h b/src/lib/rng/hmac_drbg/hmac_drbg.h index c9d0e3d20..bd2d18d47 100644 --- a/src/lib/rng/hmac_drbg/hmac_drbg.h +++ b/src/lib/rng/hmac_drbg/hmac_drbg.h @@ -24,7 +24,9 @@ class BOTAN_DLL HMAC_DRBG : public RandomNumberGenerator void clear() override; std::string name() const override; - void reseed(size_t poll_bits) override; + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; void add_entropy(const byte input[], size_t input_len) override; diff --git a/src/lib/rng/hmac_rng/hmac_rng.cpp b/src/lib/rng/hmac_rng/hmac_rng.cpp index 5456b3bac..f5a782526 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.cpp +++ b/src/lib/rng/hmac_rng/hmac_rng.cpp @@ -95,7 +95,11 @@ void HMAC_RNG::randomize(byte out[], size_t length) m_output_since_reseed += length; if(m_output_since_reseed >= BOTAN_RNG_MAX_OUTPUT_BEFORE_RESEED) - reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS, BOTAN_RNG_AUTO_RESEED_TIMEOUT); + { + reseed_with_sources(Entropy_Sources::global_sources(), + BOTAN_RNG_RESEED_POLL_BITS, + BOTAN_RNG_AUTO_RESEED_TIMEOUT); + } /* HMAC KDF as described in E-t-E, using a CTXinfo of "rng" @@ -112,15 +116,9 @@ void HMAC_RNG::randomize(byte out[], size_t length) } } -/* -* Poll for entropy and reset the internal keys -*/ -void HMAC_RNG::reseed(size_t poll_bits) - { - reseed_with_timeout(poll_bits, BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); - } - -void HMAC_RNG::reseed_with_timeout(size_t poll_bits, std::chrono::milliseconds timeout) +size_t HMAC_RNG::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds timeout) { /* Using the terminology of E-t-E, XTR is the MAC function (normally @@ -130,20 +128,18 @@ void HMAC_RNG::reseed_with_timeout(size_t poll_bits, std::chrono::milliseconds t a bad poll doesn't wipe us out. */ - double bits_collected = 0; - - typedef std::chrono::high_resolution_clock clock; + typedef std::chrono::system_clock clock; auto deadline = clock::now() + timeout; - Entropy_Accumulator accum( - [&](const byte in[], size_t in_len, double entropy_estimate) - { + 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); }); - EntropySource::poll_available_sources(accum); + srcs.poll(accum); /* * It is necessary to feed forward poll data. Otherwise, a good poll @@ -172,6 +168,8 @@ void HMAC_RNG::reseed_with_timeout(size_t poll_bits, std::chrono::milliseconds t m_extractor->output_length() * 8); m_output_since_reseed = 0; + + return static_cast<size_t>(bits_collected); } bool HMAC_RNG::is_seeded() const @@ -180,12 +178,16 @@ bool HMAC_RNG::is_seeded() const } /* -* Add user-supplied entropy to the extractor input +* Add user-supplied entropy to the extractor input then reseed +* to incorporate it into the state */ void HMAC_RNG::add_entropy(const byte input[], size_t length) { m_extractor->update(input, length); - reseed_with_timeout(BOTAN_RNG_RESEED_POLL_BITS, BOTAN_RNG_AUTO_RESEED_TIMEOUT); + + reseed_with_sources(Entropy_Sources::global_sources(), + BOTAN_RNG_RESEED_POLL_BITS, + BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); } /* diff --git a/src/lib/rng/hmac_rng/hmac_rng.h b/src/lib/rng/hmac_rng/hmac_rng.h index ba12e665d..1e38daa08 100644 --- a/src/lib/rng/hmac_rng/hmac_rng.h +++ b/src/lib/rng/hmac_rng/hmac_rng.h @@ -32,9 +32,9 @@ class BOTAN_DLL HMAC_RNG : public RandomNumberGenerator void clear() override; std::string name() const override; - void reseed(size_t poll_bits) override; - - void reseed_with_timeout(size_t poll_bits, std::chrono::milliseconds ms); + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; void add_entropy(const byte[], size_t) override; diff --git a/src/lib/rng/rng.cpp b/src/lib/rng/rng.cpp index d4fd5fb10..c17f23dd0 100644 --- a/src/lib/rng/rng.cpp +++ b/src/lib/rng/rng.cpp @@ -7,9 +7,24 @@ #include <botan/rng.h> #include <botan/hmac_rng.h> +#include <botan/entropy_src.h> namespace Botan { +size_t RandomNumberGenerator::reseed(size_t bits_to_collect) + { + return this->reseed_with_timeout(bits_to_collect, + BOTAN_RNG_RESEED_DEFAULT_TIMEOUT); + } + +size_t RandomNumberGenerator::reseed_with_timeout(size_t bits_to_collect, + std::chrono::milliseconds timeout) + { + return this->reseed_with_sources(Entropy_Sources::global_sources(), + bits_to_collect, + timeout); + } + RandomNumberGenerator* RandomNumberGenerator::make_rng() { std::unique_ptr<MessageAuthenticationCode> h1(MessageAuthenticationCode::create("HMAC(SHA-512)")); diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index a28a676a6..1ce0d5153 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -8,13 +8,16 @@ #ifndef BOTAN_RANDOM_NUMBER_GENERATOR_H__ #define BOTAN_RANDOM_NUMBER_GENERATOR_H__ -#include <botan/entropy_src.h> +#include <botan/secmem.h> #include <botan/exceptn.h> +#include <chrono> #include <string> #include <mutex> namespace Botan { +class Entropy_Sources; + /** * This class represents a random number (RNG) generator object. */ @@ -100,11 +103,29 @@ class BOTAN_DLL RandomNumberGenerator virtual std::string name() const = 0; /** - * Seed this RNG using the entropy sources it contains. + * Seed this RNG using the global entropy sources and default timeout + * @param bits_to_collect is the number of bits of entropy to + attempt to gather from the entropy sources + */ + size_t reseed(size_t bits_to_collect); + + /** + * Seed this RNG using the global 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 */ - virtual void reseed(size_t bits_to_collect) = 0; + size_t reseed_with_timeout(size_t bits_to_collect, + std::chrono::milliseconds poll_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) = 0; /** * Add entropy to this RNG. @@ -135,7 +156,12 @@ class BOTAN_DLL Null_RNG : public RandomNumberGenerator std::string name() const override { return "Null_RNG"; } - void reseed(size_t) override {} + size_t reseed_with_sources(Entropy_Sources&, size_t, + std::chrono::milliseconds) override + { + return 0; + } + bool is_seeded() const override { return false; } void add_entropy(const byte[], size_t) override {} }; @@ -170,10 +196,12 @@ class BOTAN_DLL Serialized_RNG : public RandomNumberGenerator return m_rng->name(); } - void reseed(size_t poll_bits) override + size_t reseed_with_sources(Entropy_Sources& src, + size_t bits, + std::chrono::milliseconds msec) override { std::lock_guard<std::mutex> lock(m_mutex); - m_rng->reseed(poll_bits); + return m_rng->reseed_with_sources(src, bits, msec); } void add_entropy(const byte in[], 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 8b949d071..02ad07736 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -40,8 +40,18 @@ class System_RNG_Impl : public RandomNumberGenerator void clear() override {} std::string name() const override { return "system"; } - void reseed(size_t) override {} - void add_entropy(const byte[], size_t) override {} + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override + { + return 0; + } + + void add_entropy(const byte[], size_t) override + { + // We could write this back to /dev/urandom to help seed the PRNG + // Unclear if this is valuable on current systems + } private: #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) @@ -55,14 +65,18 @@ System_RNG_Impl::System_RNG_Impl() { #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - if(!CryptAcquireContext(&m_prov, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + if(!CryptAcquireContext(&m_prov, 0, 0, BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT)) throw std::runtime_error("System_RNG failed to acquire crypto provider"); #else - m_fd = ::open("/dev/urandom", O_RDONLY); +#ifndef O_NOCTTY + #define O_NOCTTY 0 +#endif + + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); if(m_fd < 0) - throw std::runtime_error("System_RNG failed to open /dev/urandom"); + throw std::runtime_error("System_RNG failed to open RNG device"); #endif } diff --git a/src/lib/rng/system_rng/system_rng.h b/src/lib/rng/system_rng/system_rng.h index 0f4b94725..6290b8769 100644 --- a/src/lib/rng/system_rng/system_rng.h +++ b/src/lib/rng/system_rng/system_rng.h @@ -35,7 +35,12 @@ class BOTAN_DLL System_RNG : public RandomNumberGenerator std::string name() const override { return m_rng.name(); } - void reseed(size_t poll_bits = 256) override { m_rng.reseed(poll_bits); } + 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: diff --git a/src/lib/rng/x931_rng/x931_rng.cpp b/src/lib/rng/x931_rng/x931_rng.cpp index d531cf4a9..020d9a5a5 100644 --- a/src/lib/rng/x931_rng/x931_rng.cpp +++ b/src/lib/rng/x931_rng/x931_rng.cpp @@ -72,10 +72,13 @@ void ANSI_X931_RNG::rekey() } } -void ANSI_X931_RNG::reseed(size_t poll_bits) +size_t ANSI_X931_RNG::reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) { - m_prng->reseed(poll_bits); + size_t bits = m_prng->reseed_with_sources(srcs, poll_bits, poll_timeout); rekey(); + return bits; } void ANSI_X931_RNG::add_entropy(const byte input[], size_t length) diff --git a/src/lib/rng/x931_rng/x931_rng.h b/src/lib/rng/x931_rng/x931_rng.h index 899fed956..ed7124a08 100644 --- a/src/lib/rng/x931_rng/x931_rng.h +++ b/src/lib/rng/x931_rng/x931_rng.h @@ -24,7 +24,10 @@ class BOTAN_DLL ANSI_X931_RNG : public RandomNumberGenerator void clear() override; std::string name() const override; - void reseed(size_t poll_bits) override; + size_t reseed_with_sources(Entropy_Sources& srcs, + size_t poll_bits, + std::chrono::milliseconds poll_timeout) override; + void add_entropy(const byte[], size_t) override; /** diff --git a/src/tests/test_entropy.cpp b/src/tests/test_entropy.cpp new file mode 100644 index 000000000..f2475e47e --- /dev/null +++ b/src/tests/test_entropy.cpp @@ -0,0 +1,107 @@ +/* +* (C) 2015 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include "tests.h" +#include <botan/entropy_src.h> + +#if defined(BOTAN_HAS_COMPRESSION) + #include <botan/compression.h> +#endif + +namespace Botan_Tests { + +namespace { + +class Entropy_Source_Tests : public Test + { + public: + std::vector<Test::Result> run() override + { + static const size_t MAX_ENTROPY = 512; + static const size_t MAX_SAMPLES = 256; + static const size_t MAX_ENTROPY_BYTES = 256*1024; + + Botan::Entropy_Sources& srcs = Botan::Entropy_Sources::global_sources(); + + std::vector<std::string> src_names = srcs.enabled_sources(); + + std::vector<Test::Result> results; + + for(auto&& src_name : src_names) + { + Test::Result result("Entropy source " + src_name); + + result.start_timer(); + + std::vector<uint8_t> entropy; + size_t samples = 0; + size_t entropy_estimate = 0; + + // TODO: add a timeout + + Botan::Entropy_Accumulator accum([&](const uint8_t buf[], size_t buf_len, size_t buf_entropy) -> bool + { + entropy.insert(entropy.end(), buf, buf + buf_len); + entropy_estimate += buf_entropy; + ++samples; + + result.test_note("sample " + std::to_string(samples) + " " + + Botan::hex_encode(buf, buf_len) + " " + std::to_string(buf_entropy)); + + result.test_gte("impossible entropy", buf_len * 8, buf_entropy); + + return (entropy_estimate > MAX_ENTROPY || + samples > MAX_SAMPLES || + entropy.size() > MAX_ENTROPY_BYTES); + }); + + result.confirm("polled source", srcs.poll_just(accum, src_name)); + + result.test_note("saw " + std::to_string(samples) + + " samples with total estimated entropy " + + std::to_string(entropy_estimate)); + + //result.test_gte("impossible entropy", entropy.size() * 8, entropy_estimate); + + if(!entropy.empty()) + { +#if defined(BOTAN_HAS_COMPRESSION) + for(auto comp_algo : { "zlib", "bzip2", "lzma" }) + { + std::unique_ptr<Botan::Compressor_Transform> comp(Botan::make_compressor(comp_algo, 9)); + + if(comp) + { + Botan::secure_vector<byte> compressed; + compressed.assign(entropy.begin(), entropy.end()); + + comp->start(); + comp->finish(compressed); + + result.test_gte("compressed entropy better than advertised", + compressed.size() * 8, entropy_estimate); + + // TODO: perform 2nd poll and check compression differential + } + + } +#endif + } + + result.end_timer(); + + results.push_back(result); + } + + return results; + } + }; + +BOTAN_REGISTER_TEST("entropy", Entropy_Source_Tests); + +} + +} diff --git a/src/tests/test_rng.h b/src/tests/test_rng.h index f1d40f7f1..7d30fd6cc 100644 --- a/src/tests/test_rng.h +++ b/src/tests/test_rng.h @@ -30,7 +30,9 @@ class Fixed_Output_RNG : public Botan::RandomNumberGenerator return out; } - void reseed(size_t) override {} + size_t reseed_with_sources(Botan::Entropy_Sources&, + size_t, + std::chrono::milliseconds) { return 0; } void randomize(byte out[], size_t len) override { diff --git a/src/tests/tests.h b/src/tests/tests.h index 9f511c4fb..1af278cce 100644 --- a/src/tests/tests.h +++ b/src/tests/tests.h @@ -99,7 +99,7 @@ class Test else { Result r(result.who()); - r.test_note("Got expected failure " + result.result_string()); + r.test_note("Got expected failure"); return r; } } @@ -108,6 +108,13 @@ class Test void test_note(const std::string& note, const char* extra = nullptr); + template<typename Alloc> + void test_note(const std::string& who, const std::vector<uint8_t, Alloc>& vec) + { + const std::string hex = Botan::hex_encode(vec); + return test_note(who, hex.c_str()); + } + void note_missing(const std::string& thing); bool test_success(const std::string& note = ""); |