From ad851c2047273d3317cbdc88cefaa1c3aaa61ee2 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 8 May 2020 07:24:50 -0400 Subject: Add Processor_RNG Replaces RDRAND_RNG, RDRAND entropy source, and DARN entropy source. Provides also DARN-based RNG interface. This also gives an easy path for supporting the ARMv8 RNG instructions. --- src/lib/rng/processor_rng/info.txt | 11 ++ src/lib/rng/processor_rng/processor_rng.cpp | 157 ++++++++++++++++++++++++++++ src/lib/rng/processor_rng/processor_rng.h | 52 +++++++++ src/lib/rng/rdrand_rng/info.txt | 18 ++-- src/lib/rng/rdrand_rng/rdrand_rng.cpp | 92 ++++------------ src/lib/rng/rdrand_rng/rdrand_rng.h | 6 +- src/lib/rng/rng.h | 2 +- src/lib/rng/stateful_rng/stateful_rng.cpp | 12 +-- 8 files changed, 260 insertions(+), 90 deletions(-) create mode 100644 src/lib/rng/processor_rng/info.txt create mode 100644 src/lib/rng/processor_rng/processor_rng.cpp create mode 100644 src/lib/rng/processor_rng/processor_rng.h (limited to 'src/lib/rng') diff --git a/src/lib/rng/processor_rng/info.txt b/src/lib/rng/processor_rng/info.txt new file mode 100644 index 000000000..585dc5e59 --- /dev/null +++ b/src/lib/rng/processor_rng/info.txt @@ -0,0 +1,11 @@ + +PROCESSOR_RNG -> 20200508 + + + +gcc +clang +icc +msvc + + diff --git a/src/lib/rng/processor_rng/processor_rng.cpp b/src/lib/rng/processor_rng/processor_rng.cpp new file mode 100644 index 000000000..ca52d05e6 --- /dev/null +++ b/src/lib/rng/processor_rng/processor_rng.cpp @@ -0,0 +1,157 @@ +/* +* (C) 2016,2019,2020 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include +#include +#include + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM) + #include +#endif + +namespace Botan { + +namespace { + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + /* + * According to Intel, RDRAND is guaranteed to generate a random + * number within 10 retries on a working CPU + */ + const size_t HWRNG_RETRIES = 10; + +#else + /* + * Lacking specific guidance we give the CPU quite a bit of leeway + */ + const size_t HWRNG_RETRIES = 512; +#endif + +#if defined(BOTAN_TARGET_ARCH_IS_X86_32) + typedef uint32_t hwrng_output; +#else + typedef uint64_t hwrng_output; +#endif + +hwrng_output read_hwrng(bool& success) + { + hwrng_output output = 0; + success = false; + +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + int cf = 0; +#if defined(BOTAN_USE_GCC_INLINE_ASM) + // same asm seq works for 32 and 64 bit + asm volatile("rdrand %0; adcl $0,%1" : + "=r" (output), "=r" (cf) : "0" (output), "1" (cf) : "cc"); +#elif defined(BOTAN_TARGET_ARCH_IS_X86_32) + cf = _rdrand32_step(&output); +#else + cf = _rdrand64_step(&output); +#endif + success = (1 == cf); + +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + + /* + DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy. + Avoid the bias by invoking it twice and, assuming both succeed, returning the + XOR of the two results, which should unbias the output. + */ + uint64_t output2 = 0; + // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED) + asm volatile("darn %0, 1" : "=r" (output)); + asm volatile("darn %0, 1" : "=r" (output2)); + + if((~output) != 0 && (~output2) != 0) + { + output ^= output2; + success = true; + } + +#endif + + if(success) + return output; + + return 0; + } + +hwrng_output read_hwrng() + { + for(size_t i = 0; i < HWRNG_RETRIES; ++i) + { + bool success = false; + hwrng_output output = read_hwrng(success); + + if(success) + return output; + } + + throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations"); + } + +} + +//static +bool Processor_RNG::available() + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return CPUID::has_rdrand(); +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return CPUID::has_darn_rng(); +#else + return false; +#endif + } + +std::string Processor_RNG::name() const + { +#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) + return "rdrand"; +#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY) + return "darn"; +#else + return "hwrng"; +#endif + } + +void Processor_RNG::randomize(uint8_t out[], size_t out_len) + { + while(out_len >= sizeof(hwrng_output)) + { + const hwrng_output r = read_hwrng(); + store_le(r, out); + out += sizeof(hwrng_output); + out_len -= sizeof(hwrng_output); + } + + if(out_len > 0) // at most sizeof(hwrng_output)-1 + { + const hwrng_output r = read_hwrng(); + for(size_t i = 0; i != out_len; ++i) + out[i] = get_byte(i, r); + } + } + +Processor_RNG::Processor_RNG() + { + if(!Processor_RNG::available()) + throw Invalid_State("Current CPU does not support RNG instruction"); + } + +void Processor_RNG::add_entropy(const uint8_t[], size_t) + { + /* no way to add entropy */ + } + +size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) + { + /* no way to add entropy */ + return 0; + } + +} diff --git a/src/lib/rng/processor_rng/processor_rng.h b/src/lib/rng/processor_rng/processor_rng.h new file mode 100644 index 000000000..5900e386e --- /dev/null +++ b/src/lib/rng/processor_rng/processor_rng.h @@ -0,0 +1,52 @@ +/* +* (C) 2016,2019,2020 Jack Lloyd +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#ifndef BOTAN_RNG_PROCESSOR_RNG_H_ +#define BOTAN_RNG_PROCESSOR_RNG_H_ + +#include + +namespace Botan { + +/** +* Directly invokes a CPU specific instruction to generate random numbers. +* On x86, the RDRAND instruction is used. +* on POWER, the DARN instruction is used. +*/ +class BOTAN_PUBLIC_API(2,15) Processor_RNG final : public Hardware_RNG + { + public: + /** + * Constructor will throw if CPU does not have RDRAND bit set + */ + Processor_RNG(); + + /** + * Return true if RNG instruction is available on the current processor + */ + static bool available(); + + bool accepts_input() const override { return false; } + bool is_seeded() const override { return true; } + + void randomize(uint8_t out[], size_t out_len) override; + + /* + * No way to provide entropy to RDRAND generator, so add_entropy is ignored + */ + void add_entropy(const uint8_t[], size_t) override; + + /* + * No way to reseed processor provided generator, so reseed is ignored + */ + size_t reseed(Entropy_Sources&, size_t, std::chrono::milliseconds) override; + + std::string name() const override; + }; + +} + +#endif diff --git a/src/lib/rng/rdrand_rng/info.txt b/src/lib/rng/rdrand_rng/info.txt index fa40c6c46..5cc616dea 100644 --- a/src/lib/rng/rdrand_rng/info.txt +++ b/src/lib/rng/rdrand_rng/info.txt @@ -2,14 +2,12 @@ RDRAND_RNG -> 20160619 - -rdrand - - - -gcc -clang -icc -msvc - + +processor_rng + +# Avoid building RDRAND_RNG on non-x86 since that would be confusing + +x86_32 +x86_64 + diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.cpp b/src/lib/rng/rdrand_rng/rdrand_rng.cpp index ece3d9f39..fade5a199 100644 --- a/src/lib/rng/rdrand_rng/rdrand_rng.cpp +++ b/src/lib/rng/rdrand_rng/rdrand_rng.cpp @@ -6,106 +6,58 @@ */ #include +#include #include -#include - -#if !defined(BOTAN_USE_GCC_INLINE_ASM) - #include -#endif namespace Botan { -namespace { - -#if defined(BOTAN_TARGET_ARCH_IS_X86_64) - typedef uint64_t rdrand_output; -#else - typedef uint32_t rdrand_output; -#endif - -#if !defined(BOTAN_USE_GCC_INLINE_ASM) -BOTAN_FUNC_ISA("rdrnd") -#endif -rdrand_output read_rdrand() - { - /* - * According to Intel, RDRAND is guaranteed to generate a random - * number within 10 retries on a working CPU - */ - const size_t RDRAND_RETRIES = 10; - - for(size_t i = 0; i < RDRAND_RETRIES; ++i) - { - rdrand_output r = 0; - int cf = 0; - -#if defined(BOTAN_USE_GCC_INLINE_ASM) - // same asm seq works for 32 and 64 bit - asm("rdrand %0; adcl $0,%1" : - "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); -#elif defined(BOTAN_TARGET_ARCH_IS_X86_64) - cf = _rdrand64_step(&r); -#else - cf = _rdrand32_step(&r); -#endif - if(1 == cf) - { - return r; - } - } - - throw PRNG_Unseeded("RDRAND read failed"); - } - -} - void RDRAND_RNG::randomize(uint8_t out[], size_t out_len) { - while(out_len >= sizeof(rdrand_output)) - { - const rdrand_output r = read_rdrand(); - store_le(r, out); - out += sizeof(rdrand_output); - out_len -= sizeof(rdrand_output); - } - - if(out_len > 0) // at most sizeof(rdrand_output)-1 - { - const rdrand_output r = read_rdrand(); - for(size_t i = 0; i != out_len; ++i) - out[i] = get_byte(i, r); - } + Processor_RNG rng; + rng.randomize(out, out_len); } RDRAND_RNG::RDRAND_RNG() { - if(!RDRAND_RNG::available()) - throw Invalid_State("Current CPU does not support RDRAND instruction"); + // Will throw if instruction is not available + Processor_RNG rng; } //static bool RDRAND_RNG::available() { - return CPUID::has_rdrand(); + return Processor_RNG::available(); } //static uint32_t RDRAND_RNG::rdrand() { - return static_cast(read_rdrand()); + Processor_RNG rng; + + for(;;) + { + try + { + uint8_t out[4]; + rng.randomize(out, 4); + return load_le(out, 0); + } + catch(PRNG_Unseeded&) {} + } } //static -BOTAN_FUNC_ISA("rdrnd") uint32_t RDRAND_RNG::rdrand_status(bool& ok) { ok = false; + Processor_RNG rng; try { - const uint32_t r = static_cast(read_rdrand()); + uint8_t out[4]; + rng.randomize(out, 4); ok = true; - return r; + return load_le(out, 0); } catch(PRNG_Unseeded&) {} diff --git a/src/lib/rng/rdrand_rng/rdrand_rng.h b/src/lib/rng/rdrand_rng/rdrand_rng.h index f441731cf..1b6977eac 100644 --- a/src/lib/rng/rdrand_rng/rdrand_rng.h +++ b/src/lib/rng/rdrand_rng/rdrand_rng.h @@ -18,7 +18,7 @@ class BOTAN_PUBLIC_API(2,0) RDRAND_RNG final : public Hardware_RNG /** * Constructor will throw if CPU does not have RDRAND bit set */ - RDRAND_RNG(); + BOTAN_DEPRECATED("Use Processor_RNG instead") RDRAND_RNG(); /** * Return true if RDRAND is available on the current processor @@ -54,13 +54,13 @@ class BOTAN_PUBLIC_API(2,0) RDRAND_RNG final : public Hardware_RNG * retries RDRAND has still not suceeded, sets ok = false and * returns 0. */ - static uint32_t BOTAN_DEPRECATED("Use RDRAND_RNG::randomize") rdrand_status(bool& ok); + static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand_status(bool& ok); /* * Calls RDRAND until it succeeds, this could hypothetically * loop forever on broken hardware. */ - static uint32_t BOTAN_DEPRECATED("Use RDRAND_RNG::randomize") rdrand(); + static uint32_t BOTAN_DEPRECATED("Use Processor_RNG::randomize") rdrand(); }; } diff --git a/src/lib/rng/rng.h b/src/lib/rng/rng.h index d61a35f2a..0959802df 100644 --- a/src/lib/rng/rng.h +++ b/src/lib/rng/rng.h @@ -189,7 +189,7 @@ class BOTAN_PUBLIC_API(2,0) RandomNumberGenerator typedef RandomNumberGenerator RNG; /** -* Hardware_RNG exists to tag hardware RNG types (PKCS11_RNG, TPM_RNG, RDRAND_RNG) +* Hardware_RNG exists to tag hardware RNG types (PKCS11_RNG, TPM_RNG, Processor_RNG) */ class BOTAN_PUBLIC_API(2,0) Hardware_RNG : public RandomNumberGenerator { diff --git a/src/lib/rng/stateful_rng/stateful_rng.cpp b/src/lib/rng/stateful_rng/stateful_rng.cpp index 1587e50f8..10b3ab84b 100644 --- a/src/lib/rng/stateful_rng/stateful_rng.cpp +++ b/src/lib/rng/stateful_rng/stateful_rng.cpp @@ -8,8 +8,8 @@ #include #include -#if defined(BOTAN_HAS_RDRAND_RNG) - #include +#if defined(BOTAN_HAS_PROCESSOR_RNG) + #include #endif namespace Botan { @@ -46,11 +46,11 @@ void Stateful_RNG::randomize_with_ts_input(uint8_t output[], size_t output_len) store_le(OS::get_high_resolution_clock(), additional_input); -#if defined(BOTAN_HAS_RDRAND_RNG) - if(RDRAND_RNG::available()) +#if defined(BOTAN_HAS_PROCESSOR_RNG) + if(Processor_RNG::available()) { - RDRAND_RNG rdrand; - rdrand.randomize(additional_input + 8, sizeof(additional_input) - 8); + Processor_RNG hwrng; + hwrng.randomize(additional_input + 8, sizeof(additional_input) - 8); } else #endif -- cgit v1.2.3