diff options
author | Jack Lloyd <[email protected]> | 2017-09-02 18:34:37 -0400 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-09-02 18:34:37 -0400 |
commit | 87f19427dbc3662636a84e56b7c7a8a49f1246df (patch) | |
tree | 267749bedfa5e4ee9490d3d50a83e08c50c097ea /src/lib/rng/system_rng/system_rng.cpp | |
parent | e2036b0dba7084728b209fc3901398e8d46b72b9 (diff) |
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.
Diffstat (limited to 'src/lib/rng/system_rng/system_rng.cpp')
-rw-r--r-- | src/lib/rng/system_rng/system_rng.cpp | 250 |
1 files changed, 142 insertions, 108 deletions
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 <botan/build.h> #if defined(BOTAN_TARGET_OS_HAS_CRYPTGENRANDOM) + #define NOMINMAX 1 + #include <windows.h> + #include <wincrypt.h> -#define NOMINMAX 1 -#include <windows.h> -#include <wincrypt.h> +#elif defined(BOTAN_TARGET_OS_HAS_CRYPTO_NG) + #include <bcrypt.h> #elif defined(BOTAN_TARGET_OS_HAS_ARC4RANDOM) - -#include <stdlib.h> + #include <stdlib.h> #else - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <errno.h> - + #include <sys/types.h> + #include <sys/stat.h> + #include <fcntl.h> + #include <unistd.h> + #include <errno.h> #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<DWORD>(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<uint8_t> buf(in, in + length); + ::CryptGenRandom(m_prov, static_cast<DWORD>(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<PUCHAR>(buf), static_cast<ULONG>(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<uint8_t> buf(input, input + len); - ::CryptGenRandom(m_prov, static_cast<DWORD>(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<DWORD>(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 - } } |