From 87f19427dbc3662636a84e56b7c7a8a49f1246df Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Sat, 2 Sep 2017 18:34:37 -0400 Subject: Refactor RNGs to support Windows Phone This OS has its own crypto API and does not support CryptGenRandom. Splits System_RNG_Impl into distinct declarations one per implementation type. Easier to read now that we are up to 4 distinct versions. Removes the CryptoAPI entropy source, and replaces it with an entropy source that calls the system RNG. This is nominally a bit less flexible in that the entropy source allowed polling multiple providers (though we didn't actually make use of that). Plus side is it works on all systems. Currently the dev_random entropy source is still there because we do actually use it to poll both /dev/random and /dev/urandom, and it might be useful (on certain systems) to also poll a HW RNG, which are often assigned their own device node. This could debatably also be removed in favor of just reading the system RNG. --- src/build-data/buildh.in | 13 +- src/build-data/os/winphone.txt | 26 ++++ src/build-data/policy/bsi.txt | 1 - src/build-data/policy/modern.txt | 1 - src/build-data/policy/nist.txt | 1 - src/lib/entropy/cryptoapi_rng/es_capi.cpp | 95 ------------ src/lib/entropy/cryptoapi_rng/es_capi.h | 44 ------ src/lib/entropy/cryptoapi_rng/info.txt | 20 --- src/lib/entropy/entropy_srcs.cpp | 69 ++++++--- src/lib/rng/system_rng/info.txt | 8 + src/lib/rng/system_rng/system_rng.cpp | 250 +++++++++++++++++------------- 11 files changed, 227 insertions(+), 301 deletions(-) create mode 100644 src/build-data/os/winphone.txt delete mode 100644 src/lib/entropy/cryptoapi_rng/es_capi.cpp delete mode 100644 src/lib/entropy/cryptoapi_rng/es_capi.h delete mode 100644 src/lib/entropy/cryptoapi_rng/info.txt (limited to 'src') diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in index 6e60718ee..e1337318e 100644 --- a/src/build-data/buildh.in +++ b/src/build-data/buildh.in @@ -117,15 +117,14 @@ /* * Specifies (in order) the list of entropy sources that will be used -* to seed an in-memory RNG. The first in the default list: "rdseed" and "rdrand" -* do not count as contributing any entropy -* but are included as they are fast and help protect against a -* seriously broken system RNG. +* to seed an in-memory RNG. The first in the default list: "rdseed" +* and "rdrand" 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 \ - { "rdseed", "rdrand", "darwin_secrandom", "getentropy", "dev_random", \ - "win32_cryptoapi", "proc_walk", "system_stats" } - + { "rdseed", "rdrand", "darwin_secrandom", "getentropy", \ + "dev_random", "system_rng", "proc_walk", "system_stats" } /* Multiplier on a block cipher's native parallelism */ #define BOTAN_BLOCK_CIPHER_PAR_MULT 4 diff --git a/src/build-data/os/winphone.txt b/src/build-data/os/winphone.txt new file mode 100644 index 000000000..ee43ee702 --- /dev/null +++ b/src/build-data/os/winphone.txt @@ -0,0 +1,26 @@ +os_type windows + +# ? +program_suffix .exe +obj_suffix obj +static_suffix lib +building_shared_supported no + +install_root c:\\Botan +doc_dir docs + +install_cmd_data "copy" +install_cmd_exec "copy" + + +crypto_ng +gmtime_s +loadlibrary +mkgmtime +query_perf_counter +rtlsecurezeromemory +#stl_filesystem_msvc +threads +filesystem +sockets + diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt index 0f053c337..f66f634ed 100644 --- a/src/build-data/policy/bsi.txt +++ b/src/build-data/policy/bsi.txt @@ -57,7 +57,6 @@ aes_ssse3 clmul # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/build-data/policy/modern.txt b/src/build-data/policy/modern.txt index 588fc7fc8..fb435636c 100644 --- a/src/build-data/policy/modern.txt +++ b/src/build-data/policy/modern.txt @@ -76,7 +76,6 @@ rdrand_rng system_rng # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/build-data/policy/nist.txt b/src/build-data/policy/nist.txt index 50948d4dc..ad7a552dc 100644 --- a/src/build-data/policy/nist.txt +++ b/src/build-data/policy/nist.txt @@ -61,7 +61,6 @@ sha2_32_armv8 clmul # entropy sources -cryptoapi_rng darwin_secrandom dev_random proc_walk diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.cpp b/src/lib/entropy/cryptoapi_rng/es_capi.cpp deleted file mode 100644 index 58503c66d..000000000 --- a/src/lib/entropy/cryptoapi_rng/es_capi.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* -* Win32 CryptoAPI EntropySource -* (C) 1999-2009,2016 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#include -#include -#define NOMINMAX 1 -#define _WINSOCKAPI_ // stop windows.h including winsock.h -#include -#include - -namespace Botan { - -namespace { - -class CSP_Handle_Impl : public Win32_CAPI_EntropySource::CSP_Handle - { - public: - explicit CSP_Handle_Impl(uint64_t capi_provider) - { - m_valid = ::CryptAcquireContext(&m_handle, - nullptr, - nullptr, - static_cast(capi_provider), - CRYPT_VERIFYCONTEXT); - } - - ~CSP_Handle_Impl() - { - if(m_valid) - ::CryptReleaseContext(m_handle, 0); - } - - size_t gen_random(uint8_t out[], size_t n) const - { - if(m_valid && ::CryptGenRandom(m_handle, static_cast(n), out)) - return n; - return 0; - } - - private: - bool m_valid; - HCRYPTPROV m_handle; - }; - -} - -/* -* Gather Entropy from Win32 CAPI -*/ -size_t Win32_CAPI_EntropySource::poll(RandomNumberGenerator& rng) - { - secure_vector buf(BOTAN_SYSTEM_RNG_POLL_REQUEST); - size_t bits = 0; - - for(size_t i = 0; i != m_csp_provs.size(); ++i) - { - size_t got = m_csp_provs[i]->gen_random(buf.data(), buf.size()); - - if(got > 0) - { - rng.add_entropy(buf.data(), got); - bits += got * 8; - } - } - - return bits; - } - -/* -* Win32_Capi_Entropysource Constructor -*/ -Win32_CAPI_EntropySource::Win32_CAPI_EntropySource(const std::string& provs) - { - for(std::string prov_name : split_on(provs, ':')) - { - 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(new CSP_Handle_Impl(prov_type))); - } - } - -} diff --git a/src/lib/entropy/cryptoapi_rng/es_capi.h b/src/lib/entropy/cryptoapi_rng/es_capi.h deleted file mode 100644 index 8439e62fa..000000000 --- a/src/lib/entropy/cryptoapi_rng/es_capi.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Win32 CAPI EntropySource -* (C) 1999-2007 Jack Lloyd -* -* Botan is released under the Simplified BSD License (see license.txt) -*/ - -#ifndef BOTAN_ENTROPY_SRC_WIN32_CAPI_H__ -#define BOTAN_ENTROPY_SRC_WIN32_CAPI_H__ - -#include -#include - -namespace Botan { - -/** -* Win32 CAPI Entropy Source -*/ -class Win32_CAPI_EntropySource final : public Entropy_Source - { - public: - std::string name() const override { return "win32_cryptoapi"; } - - size_t poll(RandomNumberGenerator& rng) override; - - /** - * Win32_Capi_Entropysource Constructor - * @param provs list of providers, separated by ':' - */ - explicit Win32_CAPI_EntropySource(const std::string& provs = ""); - - class CSP_Handle - { - public: - virtual ~CSP_Handle() {} - virtual size_t gen_random(uint8_t out[], size_t n) const = 0; - }; - private: - std::vector> m_csp_provs; - }; - -} - -#endif diff --git a/src/lib/entropy/cryptoapi_rng/info.txt b/src/lib/entropy/cryptoapi_rng/info.txt deleted file mode 100644 index 21b42f2a0..000000000 --- a/src/lib/entropy/cryptoapi_rng/info.txt +++ /dev/null @@ -1,20 +0,0 @@ - -ENTROPY_SRC_CAPI -> 20131128 - - - -es_capi.h - - -# We'll just assume CAPI is there; this is OK except for 3.x, early -# versions of 95, and maybe NT 3.5 - -windows -cygwin -mingw - - - -windows -> advapi32.lib -mingw -> advapi32 - diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp index d9d5cfe4b..76546c5da 100644 --- a/src/lib/entropy/entropy_srcs.cpp +++ b/src/lib/entropy/entropy_srcs.cpp @@ -8,6 +8,10 @@ #include #include +#if defined(BOTAN_HAS_SYSTEM_RNG) + #include +#endif + #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND) #include #endif @@ -20,10 +24,6 @@ #include #endif -#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI) - #include -#endif - #if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) #include #endif @@ -42,65 +42,86 @@ namespace Botan { +#if defined(BOTAN_HAS_SYSTEM_RNG) + +namespace { + +class System_RNG_EntropySource : public Entropy_Source + { + public: + size_t poll(RandomNumberGenerator& rng) override + { + const size_t poll_bits = BOTAN_RNG_RESEED_POLL_BITS; + rng.reseed_from_rng(system_rng(), poll_bits); + return poll_bits; + } + + std::string name() const override { return system_rng().name(); } + }; + +} + +#endif + std::unique_ptr Entropy_Source::create(const std::string& name) { - if(name == "rdrand") +#if defined(BOTAN_HAS_SYSTEM_RNG) + if(name == "system_rng" || name == "win32_cryptoapi") { + return std::unique_ptr(new System_RNG_EntropySource); + } +#endif + #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND) + if(name == "rdrand") + { return std::unique_ptr(new Intel_Rdrand); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED) if(name == "rdseed") { -#if defined(BOTAN_HAS_ENTROPY_SRC_RDSEED) return std::unique_ptr(new Intel_Rdseed); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) if(name == "darwin_secrandom") { -#if defined(BOTAN_HAS_ENTROPY_SRC_DARWIN_SECRANDOM) return std::unique_ptr(new Darwin_SecRandom); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY) if(name == "getentropy") { -#if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY) return std::unique_ptr(new Getentropy); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) if(name == "dev_random") { -#if defined(BOTAN_HAS_ENTROPY_SRC_DEV_RANDOM) return std::unique_ptr(new Device_EntropySource(BOTAN_SYSTEM_RNG_POLL_DEVICES)); -#endif } - - if(name == "win32_cryptoapi") - { -#if defined(BOTAN_HAS_ENTROPY_SRC_CAPI) - return std::unique_ptr(new Win32_CAPI_EntropySource("RSA_FULL")); #endif - } +#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER) if(name == "proc_walk") { -#if defined(BOTAN_HAS_ENTROPY_SRC_PROC_WALKER) const std::string root_dir = BOTAN_ENTROPY_PROC_FS_PATH; if(!root_dir.empty()) return std::unique_ptr(new ProcWalking_EntropySource(root_dir)); -#endif } +#endif +#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) if(name == "system_stats") { -#if defined(BOTAN_HAS_ENTROPY_SRC_WIN32) return std::unique_ptr(new Win32_EntropySource); -#endif } +#endif return std::unique_ptr(); } diff --git a/src/lib/rng/system_rng/info.txt b/src/lib/rng/system_rng/info.txt index 3df7dd387..1ea7eb388 100644 --- a/src/lib/rng/system_rng/info.txt +++ b/src/lib/rng/system_rng/info.txt @@ -24,4 +24,12 @@ openbsd qnx solaris windows +winphone + + +windows -> advapi32.lib +winphone -> bcrypt.lib + +mingw -> advapi32 + diff --git a/src/lib/rng/system_rng/system_rng.cpp b/src/lib/rng/system_rng/system_rng.cpp index 1c3b8e50a..cafdaac78 100644 --- a/src/lib/rng/system_rng/system_rng.cpp +++ b/src/lib/rng/system_rng/system_rng.cpp @@ -1,6 +1,6 @@ /* * System RNG -* (C) 2014,2015 Jack Lloyd +* (C) 2014,2015,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -9,128 +9,187 @@ #include #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + #define NOMINMAX 1 + #include + #include -#define NOMINMAX 1 -#include -#include +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) + #include #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - -#include + #include #else - -#include -#include -#include -#include -#include - + #include + #include + #include + #include + #include #endif namespace Botan { namespace { +#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + class System_RNG_Impl final : public RandomNumberGenerator { public: - System_RNG_Impl(); - ~System_RNG_Impl(); + System_RNG_Impl() + { + if(!CryptAcquireContext(&m_prov, nullptr, nullptr, + BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT)) + throw Exception("System_RNG failed to acquire crypto provider"); + } - bool is_seeded() const override { return true; } + ~System_RNG_Impl() + { + ::CryptReleaseContext(m_prov, 0); + } + + void randomize(uint8_t buf[], size_t len) override + { + ::CryptGenRandom(m_prov, static_cast(len), buf); + } + + void add_entropy(const uint8_t in[], size_t length) override + { + /* + There is no explicit ConsumeRandom, but all values provided in + the call are incorporated into the state. + */ + std::vector buf(in, in + length); + ::CryptGenRandom(m_prov, static_cast(buf.size()), buf.data()); + } + bool is_seeded() const override { return true; } void clear() override {} + std::string name() const override { return "cryptoapi"; } + private: + HCRYPTPROV m_prov; + }; - void randomize(uint8_t out[], size_t len) override; +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) - void add_entropy(const uint8_t in[], size_t length) override; +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { + NTSTATUS ret = ::BCryptOpenAlgorithmProvider(&m_prov, + BCRYPT_RNG_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if(ret != STATUS_SUCCESS) + throw Exception("System_RNG failed to acquire crypto provider"); + } + + ~System_RNG_Impl() + { + ::BCryptCloseAlgorithmProvider(m_prov, 0); + } - std::string name() const override; + void randomize(uint8_t buf[], size_t len) override + { + ::BCryptGenRandom(m_prov, static_cast(buf), static_cast(len), 0); + } + void add_entropy(const uint8_t in[], size_t length) override + { + /* + There is a flag BCRYPT_RNG_USE_ENTROPY_IN_BUFFER to provide + entropy inputs, but it is ignored in Windows 8 and later. + */ + } + + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return "crypto_ng"; } private: -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - HCRYPTPROV m_prov; -#else - int m_fd; -#endif + BCRYPT_ALG_HANDLE m_handle; }; -std::string System_RNG_Impl::name() const - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - return "cryptoapi"; #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - return "arc4random"; -#else - return BOTAN_SYSTEM_RNG_DEVICE; -#endif - } -System_RNG_Impl::System_RNG_Impl() +class System_RNG_Impl final : public RandomNumberGenerator { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + public: + // No constructor or destructor needed as no userland state maintained - if(!CryptAcquireContext(&m_prov, nullptr, nullptr, BOTAN_SYSTEM_RNG_CRYPTOAPI_PROV_TYPE, CRYPT_VERIFYCONTEXT)) - throw Exception("System_RNG failed to acquire crypto provider"); + void randomize(uint8_t buf[], size_t len) override + { + ::arc4random_buf(buf, len); + } + + void add_entropy(const uint8_t[], size_t) override { /* ignored */ } + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return "arc4random"; } + }; -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // Nothing to do, arc4random(3) works from the beginning. #else -#ifndef O_NOCTTY - #define O_NOCTTY 0 -#endif +// Read a random device - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); - - // Cannot open in read-write mode. Fall back to read-only - // Calls to add_entropy will fail, but randomize will work - if(m_fd < 0) - m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); +class System_RNG_Impl final : public RandomNumberGenerator + { + public: + System_RNG_Impl() + { + #ifndef O_NOCTTY + #define O_NOCTTY 0 + #endif - if(m_fd < 0) - throw Exception("System_RNG failed to open RNG device"); -#endif - } + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDWR | O_NOCTTY); -System_RNG_Impl::~System_RNG_Impl() - { -#if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) - ::CryptReleaseContext(m_prov, 0); -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // Nothing to do. -#else - ::close(m_fd); - m_fd = -1; -#endif - } + /* + Cannot open in read-write mode. Fall back to read-only, + calls to add_entropy will fail, but randomize will work + */ + if(m_fd < 0) + m_fd = ::open(BOTAN_SYSTEM_RNG_DEVICE, O_RDONLY | O_NOCTTY); -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. + if(m_fd < 0) + throw Exception("System_RNG failed to open RNG device"); + } - TODO: figure out a way to avoid this copy. Byte at a time updating - seems worse than the allocation. + ~System_RNG_Impl() + { + ::close(m_fd); + m_fd = -1; + } - for(size_t i = 0; i != len; ++i) - { - uint8_t b = input[i]; - ::CryptGenRandom(m_prov, 1, &b); - } - */ + void randomize(uint8_t buf[], size_t len) override; + void add_entropy(const uint8_t in[], size_t length) override; + bool is_seeded() const override { return true; } + void clear() override {} + std::string name() const override { return BOTAN_SYSTEM_RNG_DEVICE; } + private: + int m_fd; + }; - if(len > 0) +void System_RNG_Impl::randomize(uint8_t buf[], size_t len) + { + while(len) { - secure_vector buf(input, input + len); - ::CryptGenRandom(m_prov, static_cast(buf.size()), buf.data()); + ssize_t got = ::read(m_fd, buf, len); + + if(got < 0) + { + if(errno == EINTR) + continue; + throw Exception("System_RNG read failed error " + std::to_string(errno)); + } + if(got == 0) + throw Exception("System_RNG EOF on device"); // ?!? + + buf += got; + len -= got; } -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - // arc4random(3) reseeds itself from the OpenBSD kernel, not from user land. -#else + } + +void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) + { while(len) { ssize_t got = ::write(m_fd, input, len); @@ -162,34 +221,9 @@ void System_RNG_Impl::add_entropy(const uint8_t input[], size_t len) 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(len), buf); -#elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - ::arc4random_buf(buf, len); -#else - while(len) - { - ssize_t got = ::read(m_fd, buf, len); - - if(got < 0) - { - if(errno == EINTR) - continue; - throw Exception("System_RNG read failed error " + std::to_string(errno)); - } - if(got == 0) - throw Exception("System_RNG EOF on device"); // ?!? - - buf += got; - len -= got; - } #endif - } } -- cgit v1.2.3