Repository: randombit/botan
Pull request #2352 (2020-05-08 12:35:56)

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.


Patch:
diff --git a/doc/api_ref/ffi.rst b/doc/api_ref/ffi.rst
index f3274c4d6..faee6e23d 100644
--- a/doc/api_ref/ffi.rst
+++ b/doc/api_ref/ffi.rst
@@ -208,7 +208,7 @@ Random Number Generators
    "user": ``AutoSeeded_RNG``,
    "user-threadsafe": serialized ``AutoSeeded_RNG``,
    "null": ``Null_RNG`` (always fails),
-   "rdrand": ``RDRAND_RNG`` (if available)
+   "hwrnd" or "rdrand": ``Processor_RNG`` (if available)
 
 .. cpp:function:: int botan_rng_get(botan_rng_t rng, uint8_t* out, size_t out_len)
 
diff --git a/doc/api_ref/rng.rst b/doc/api_ref/rng.rst
index 0fc9da388..3a62c2bb2 100644
--- a/doc/api_ref/rng.rst
+++ b/doc/api_ref/rng.rst
@@ -204,12 +204,14 @@ a reseeding. Or, if a RNG or entropy source was provided to the ``ChaCha_RNG``
 constructor, then reseeding will be performed automatically after a certain
 interval of requests.
 
-RDRAND_RNG
+Processor_RNG
 ^^^^^^^^^^^^^^^^^
 
-This RNG type directly calls the x86 ``rdrand`` instruction. If the instruction
-is not available it will throw at runtime, you can check beforehand by calling
-``Botan::CPUID::has_rdrand()``.
+This RNG type directly invokes a CPU instruction capable of generating
+a cryptographically secure random number. On x86 it uses ``rdrand``,
+on POWER ``darn``. If the relevant instruction is not available, the
+constructor of the class will throw at runtime. You can test
+beforehand by checking the result of ``Processor_RNG::available()``.
 
 TPM_RNG
 ^^^^^^^^^^^^^^^^^
diff --git a/doc/dev_ref/todo.rst b/doc/dev_ref/todo.rst
index 1aba71993..87445344e 100644
--- a/doc/dev_ref/todo.rst
+++ b/doc/dev_ref/todo.rst
@@ -65,7 +65,7 @@ Multiparty Protocols
 External Providers, Hardware Support
 ----------------------------------------
 
-* Add support ARMv8.4-A SHA-512, SHA-3 and SM3
+* Add support ARMv8.4-A SHA-512, SHA-3, SM3 and RNG
 * Aarch64 inline asm for BigInt
 * Extend OpenSSL provider (DH, HMAC, CMAC, GCM)
 * Support using BoringSSL instead of OpenSSL or LibreSSL
diff --git a/src/build-data/buildh.in b/src/build-data/buildh.in
index 16ccd8c81..c33e80acf 100644
--- a/src/build-data/buildh.in
+++ b/src/build-data/buildh.in
@@ -175,13 +175,10 @@
 
 /*
 * 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.
 */
 #define BOTAN_ENTROPY_DEFAULT_SOURCES \
-   { "rdseed", "rdrand", "p9_darn", "getentropy", "dev_random", \
+   { "rdseed", "hwrng", "p9_darn", "getentropy", "dev_random", \
      "system_rng", "proc_walk", "system_stats" }
 
 /* Multiplier on a block cipher's native parallelism */
diff --git a/src/build-data/policy/bsi.txt b/src/build-data/policy/bsi.txt
index 6fa3ee9a9..16ce27368 100644
--- a/src/build-data/policy/bsi.txt
+++ b/src/build-data/policy/bsi.txt
@@ -70,12 +70,11 @@ sha3_bmi2
 # entropy sources
 dev_random
 proc_walk
-rdrand
 rdseed
 win32_stats
 
 # rng
-rdrand_rng
+processor_rng
 system_rng
 
 # utils
diff --git a/src/build-data/policy/modern.txt b/src/build-data/policy/modern.txt
index 0c2e7f8c3..97df231c9 100644
--- a/src/build-data/policy/modern.txt
+++ b/src/build-data/policy/modern.txt
@@ -81,13 +81,12 @@ simd
 sessions_sql
 certstor_sql
 
-rdrand_rng
 system_rng
+processor_rng
 
 # entropy sources
 dev_random
 proc_walk
-rdrand
 rdseed
 win32_stats
 </if_available>
diff --git a/src/build-data/policy/nist.txt b/src/build-data/policy/nist.txt
index de399610d..456b065ca 100644
--- a/src/build-data/policy/nist.txt
+++ b/src/build-data/policy/nist.txt
@@ -70,12 +70,10 @@ sha2_32_armv8
 # entropy sources
 dev_random
 proc_walk
-rdrand
 rdseed
 win32_stats
 
 # rng
-rdrand_rng
 system_rng
 
 # utils
diff --git a/src/cli/cli_rng.cpp b/src/cli/cli_rng.cpp
index 8f1cb750b..e3eee0c03 100644
--- a/src/cli/cli_rng.cpp
+++ b/src/cli/cli_rng.cpp
@@ -18,8 +18,8 @@
    #include <botan/system_rng.h>
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-   #include <botan/rdrand_rng.h>
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
 #endif
 
 #if defined(BOTAN_HAS_HMAC_DRBG)
@@ -73,13 +73,13 @@ cli_make_rng(const std::string& rng_type, const std::string& hex_drbg_seed)
       }
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-   if(rng_type == "rdrand" || rng_type.empty())
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   if(rng_type == "rdrand" || rng_type == "cpu" || rng_type.empty())
       {
-      if(Botan::RDRAND_RNG::available())
-         return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::RDRAND_RNG);
+      if(Botan::Processor_RNG::available())
+         return std::unique_ptr<Botan::RandomNumberGenerator>(new Botan::Processor_RNG);
       else if(rng_type.empty() == false)
-         throw CLI_Error("RDRAND instruction not supported on this processor");
+         throw CLI_Error("RNG instruction not supported on this processor");
       }
 #endif
 
diff --git a/src/cli/speed.cpp b/src/cli/speed.cpp
index e81ddd24b..a3e730d80 100644
--- a/src/cli/speed.cpp
+++ b/src/cli/speed.cpp
@@ -61,8 +61,8 @@
    #include <botan/hmac_drbg.h>
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-   #include <botan/rdrand_rng.h>
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
 #endif
 
 #if defined(BOTAN_HAS_CHACHA_RNG)
@@ -771,11 +771,11 @@ class Speed final : public Command
                bench_rng(Botan::system_rng(), "System_RNG", msec, buf_sizes);
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-               if(Botan::CPUID::has_rdrand())
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+               if(Botan::Processor_RNG::available())
                   {
-                  Botan::RDRAND_RNG rdrand;
-                  bench_rng(rdrand, "RDRAND", msec, buf_sizes);
+                  Botan::Processor_RNG hwrng;
+                  bench_rng(hwrng, "Processor_RNG", msec, buf_sizes);
                   }
 #endif
 
diff --git a/src/lib/entropy/entropy_srcs.cpp b/src/lib/entropy/entropy_srcs.cpp
index ba60ed62d..e6573e296 100644
--- a/src/lib/entropy/entropy_srcs.cpp
+++ b/src/lib/entropy/entropy_srcs.cpp
@@ -12,6 +12,10 @@
   #include <botan/system_rng.h>
 #endif
 
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+  #include <botan/processor_rng.h>
+#endif
+
 #if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND)
   #include <botan/internal/rdrand.h>
 #endif
@@ -43,10 +47,10 @@
 
 namespace Botan {
 
-#if defined(BOTAN_HAS_SYSTEM_RNG)
-
 namespace {
 
+#if defined(BOTAN_HAS_SYSTEM_RNG)
+
 class System_RNG_EntropySource final : public Entropy_Source
    {
    public:
@@ -60,10 +64,46 @@ class System_RNG_EntropySource final : public Entropy_Source
       std::string name() const override { return "system_rng"; }
    };
 
-}
+#endif
+
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+
+class Processor_RNG_EntropySource final : public Entropy_Source
+   {
+   public:
+      size_t poll(RandomNumberGenerator& rng) override
+         {
+         /*
+         * Intel's documentation for RDRAND at
+         * https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
+         * claims that software can guarantee a reseed event by polling enough data:
+         * "There is an upper bound of 511 samples per seed in the implementation
+         * where samples are 128 bits in size and can provide two 64-bit random
+         * numbers each."
+         *
+         * By requesting 65536 bits we are asking for 512 samples and thus are assured
+         * that at some point in producing the output, at least one reseed of the
+         * internal state will occur.
+         *
+         * The reseeding conditions of the POWER and ARM processor RNGs are not known
+         * but probably work in a somewhat similar manner. The exact amount requested
+         * may be tweaked if and when such conditions become publically known.
+         */
+         const size_t poll_bits = 65536;
+         rng.reseed_from_rng(m_hwrng, poll_bits);
+         // Avoid trusting a black box, don't count this as contributing entropy:
+         return 0;
+         }
+
+      std::string name() const override { return m_hwrng.name(); }
+   private:
+      Processor_RNG m_hwrng;
+   };
 
 #endif
 
+}
+
 std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
    {
 #if defined(BOTAN_HAS_SYSTEM_RNG)
@@ -73,10 +113,13 @@ std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
       }
 #endif
 
-#if defined(BOTAN_HAS_ENTROPY_SRC_RDRAND)
-   if(name == "rdrand")
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   if(name == "hwrng" || name == "rdrand" || name == "p9_darn")
       {
-      return std::unique_ptr<Entropy_Source>(new Intel_Rdrand);
+      if(Processor_RNG::available())
+         {
+         return std::unique_ptr<Entropy_Source>(new Processor_RNG_EntropySource);
+         }
       }
 #endif
 
@@ -87,13 +130,6 @@ std::unique_ptr<Entropy_Source> Entropy_Source::create(const std::string& name)
       }
 #endif
 
-#if defined(BOTAN_HAS_ENTROPY_SRC_DARN)
-   if(name == "p9_darn")
-      {
-      return std::unique_ptr<Entropy_Source>(new POWER9_DARN);
-      }
-#endif
-
 #if defined(BOTAN_HAS_ENTROPY_SRC_GETENTROPY)
    if(name == "getentropy")
       {
diff --git a/src/lib/entropy/p9_darn/info.txt b/src/lib/entropy/p9_darn/info.txt
deleted file mode 100644
index e703786f4..000000000
--- a/src/lib/entropy/p9_darn/info.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-<defines>
-ENTROPY_SRC_DARN -> 20180610
-</defines>
-
-<header:internal>
-p9_darn.h
-</header:internal>
-
-<arch>
-ppc64
-</arch>
-
-<isa>
-powercrypto
-</isa>
diff --git a/src/lib/entropy/p9_darn/p9_darn.cpp b/src/lib/entropy/p9_darn/p9_darn.cpp
deleted file mode 100644
index 8731b94e1..000000000
--- a/src/lib/entropy/p9_darn/p9_darn.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
-* (C) 2019 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#include <botan/internal/p9_darn.h>
-#include <botan/cpuid.h>
-
-namespace Botan {
-
-namespace {
-
-bool read_darn(secure_vector<uint64_t>& seed)
-   {
-   const size_t DARN_RETRIES = 512;
-
-   for(size_t i = 0; i != DARN_RETRIES; ++i)
-      {
-      uint64_t r = 0;
-
-      // DARN 0: 32-bit conditioned, 1: 64-bit condition, 2: 64-bit raw (ala RDSEED)
-      asm volatile("darn %0, 2" : "=r" (r));
-
-      // DARN indicates error by 0xFF..FF, ie is biased (!?!?)
-      if((~r) != 0)
-         {
-         seed.push_back(r);
-         return true;
-         }
-      }
-
-   return false; // failed to produce an output after many attempts
-   }
-
-}
-
-size_t POWER9_DARN::poll(RandomNumberGenerator& rng)
-   {
-   const size_t DARN_BYTES = 1024;
-   static_assert(DARN_BYTES % 8 == 0, "Bad DARN configuration");
-
-   if(CPUID::has_darn_rng())
-      {
-      secure_vector<uint64_t> seed;
-      seed.reserve(DARN_BYTES / 8);
-
-      for(size_t p = 0; p != DARN_BYTES / 8; ++p)
-         {
-         if(!read_darn(seed))
-            break;
-         }
-
-      if(seed.size() > 0)
-         {
-         rng.add_entropy(reinterpret_cast<const uint8_t*>(seed.data()),
-                         seed.size() * sizeof(uint32_t));
-         }
-      }
-
-   // DARN is used but not trusted
-   return 0;
-   }
-
-}
diff --git a/src/lib/entropy/p9_darn/p9_darn.h b/src/lib/entropy/p9_darn/p9_darn.h
deleted file mode 100644
index 9d8c508a1..000000000
--- a/src/lib/entropy/p9_darn/p9_darn.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
-* (C) 2019 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_ENTROPY_SRC_DARN_H_
-#define BOTAN_ENTROPY_SRC_DARN_H_
-
-#include <botan/entropy_src.h>
-
-namespace Botan {
-
-class POWER9_DARN final : public Entropy_Source
-   {
-   public:
-      std::string name() const override { return "p9_darn"; }
-      size_t poll(RandomNumberGenerator& rng) override;
-   };
-
-}
-
-#endif
diff --git a/src/lib/entropy/rdrand/info.txt b/src/lib/entropy/rdrand/info.txt
deleted file mode 100644
index 6abe8765d..000000000
--- a/src/lib/entropy/rdrand/info.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-<defines>
-ENTROPY_SRC_RDRAND -> 20131128
-</defines>
-
-<requires>
-rdrand_rng
-</requires>
-
-<header:internal>
-rdrand.h
-</header:internal>
diff --git a/src/lib/entropy/rdrand/rdrand.cpp b/src/lib/entropy/rdrand/rdrand.cpp
deleted file mode 100644
index e22227326..000000000
--- a/src/lib/entropy/rdrand/rdrand.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-* Entropy Source Using Intel's rdrand instruction
-* (C) 2012,2015,2019 Jack Lloyd
-* (C) 2015 Daniel Neus
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#include <botan/internal/rdrand.h>
-#include <botan/rdrand_rng.h>
-
-namespace Botan {
-
-size_t Intel_Rdrand::poll(RandomNumberGenerator& rng)
-   {
-   /*
-   * Intel's documentation for RDRAND at
-   * https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
-   * claims that software can guarantee a reseed event by polling enough data:
-   * "There is an upper bound of 511 samples per seed in the implementation
-   * where samples are 128 bits in size and can provide two 64-bit random
-   * numbers each."
-   *
-   * By requesting 8192 bytes we are asking for 512 samples and thus are assured
-   * that at some point in producing the output, at least one reseed of the
-   * internal state will occur.
-   *
-   * The alternative approach is to "Iteratively execute 32 RDRAND invocations
-   * with a 10 us wait period per iteration." however in practice this proves to
-   * be about 20x slower, despite producing much less seed material.
-   */
-   const size_t RDRAND_POLL_BYTES = 8*1024;
-
-   if(RDRAND_RNG::available())
-      {
-      RDRAND_RNG rdrand_rng;
-      secure_vector<uint8_t> buf(RDRAND_POLL_BYTES);
-      rdrand_rng.randomize(&buf[0], buf.size());
-      rng.add_entropy(buf.data(), buf.size());
-      }
-
-   // RDRAND is used but not trusted
-   return 0;
-   }
-
-}
diff --git a/src/lib/entropy/rdrand/rdrand.h b/src/lib/entropy/rdrand/rdrand.h
deleted file mode 100644
index 6544fe57f..000000000
--- a/src/lib/entropy/rdrand/rdrand.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-* Entropy Source Using Intel's rdrand instruction
-* (C) 2012 Jack Lloyd
-*
-* Botan is released under the Simplified BSD License (see license.txt)
-*/
-
-#ifndef BOTAN_ENTROPY_SRC_RDRAND_H_
-#define BOTAN_ENTROPY_SRC_RDRAND_H_
-
-#include <botan/entropy_src.h>
-
-namespace Botan {
-
-/**
-* Entropy source using the rdrand instruction first introduced on
-* Intel's Ivy Bridge architecture.
-*/
-class Intel_Rdrand final : public Entropy_Source
-   {
-   public:
-      std::string name() const override { return "rdrand"; }
-      size_t poll(RandomNumberGenerator& rng) override;
-   };
-
-}
-
-#endif
diff --git a/src/lib/ffi/ffi_rng.cpp b/src/lib/ffi/ffi_rng.cpp
index e312bad80..c32894ff9 100644
--- a/src/lib/ffi/ffi_rng.cpp
+++ b/src/lib/ffi/ffi_rng.cpp
@@ -10,8 +10,8 @@
 #include <botan/system_rng.h>
 #include <botan/auto_rng.h>
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-   #include <botan/rdrand_rng.h>
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
 #endif
 
 extern "C" {
@@ -40,10 +40,10 @@ int botan_rng_init(botan_rng_t* rng_out, const char* rng_type)
          {
          rng.reset(new Botan::Null_RNG);
          }
-#if defined(BOTAN_HAS_RDRAND_RNG)
-      else if(rng_type_s == "rdrand" && Botan::RDRAND_RNG::available())
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+      else if((rng_type_s == "rdrand" || rng_type_s == "hwrng") && Botan::Processor_RNG::available())
          {
-         rng.reset(new Botan::RDRAND_RNG);
+         rng.reset(new Botan::Processor_RNG);
          }
 #endif
 #if defined(BOTAN_TARGET_OS_HAS_THREADS)
diff --git a/src/lib/rng/processor_rng/info.txt b/src/lib/rng/processor_rng/info.txt
new file mode 100644
index 000000000..ec1c7faf3
--- /dev/null
+++ b/src/lib/rng/processor_rng/info.txt
@@ -0,0 +1,16 @@
+<defines>
+PROCESSOR_RNG -> 20200508
+</defines>
+
+<cc>
+gcc
+clang
+icc
+msvc
+</cc>
+
+<arch>
+x86_32
+x86_64
+ppc64
+</arch>
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 <botan/processor_rng.h>
+#include <botan/loadstor.h>
+#include <botan/cpuid.h>
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM)
+  #include <immintrin.h>
+#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 <botan/rng.h>
+
+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
 </defines>
 
-<isa>
-rdrand
-</isa>
-
-<cc>
-gcc
-clang
-icc
-msvc
-</cc>
+<requires>
+processor_rng
+</requires>
 
+# Avoid building RDRAND_RNG on non-x86 since that would be confusing
+<arch>
+x86_32
+x86_64
+</arch>
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 <botan/rdrand_rng.h>
+#include <botan/processor_rng.h>
 #include <botan/loadstor.h>
-#include <botan/cpuid.h>
-
-#if !defined(BOTAN_USE_GCC_INLINE_ASM)
-  #include <immintrin.h>
-#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<uint32_t>(read_rdrand());
+   Processor_RNG rng;
+
+   for(;;)
+      {
+      try
+         {
+         uint8_t out[4];
+         rng.randomize(out, 4);
+         return load_le<uint32_t>(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<uint32_t>(read_rdrand());
+      uint8_t out[4];
+      rng.randomize(out, 4);
       ok = true;
-      return r;
+      return load_le<uint32_t>(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 <botan/internal/os_utils.h>
 #include <botan/loadstor.h>
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-  #include <botan/rdrand_rng.h>
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+  #include <botan/processor_rng.h>
 #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
diff --git a/src/lib/utils/types.h b/src/lib/utils/types.h
index 5cb48a3f6..549163aa8 100644
--- a/src/lib/utils/types.h
+++ b/src/lib/utils/types.h
@@ -46,7 +46,7 @@ namespace Botan {
 * <dt>Message Authentication Codes<dd>
 *        @ref CBC_MAC "CBC-MAC", CMAC, HMAC, Poly1305, SipHash, ANSI_X919_MAC
 * <dt>Random Number Generators<dd>
-*        AutoSeeded_RNG, HMAC_DRBG, RDRAND_RNG, System_RNG
+*        AutoSeeded_RNG, HMAC_DRBG, Processor_RNG, System_RNG
 * <dt>Key Derivation<dd>
 *        HKDF, @ref KDF1 "KDF1 (IEEE 1363)", @ref KDF1_18033 "KDF1 (ISO 18033-2)", @ref KDF2 "KDF2 (IEEE 1363)",
 *        @ref sp800_108.h "SP800-108", @ref SP800_56C "SP800-56C", @ref PKCS5_PBKDF1 "PBKDF1 (PKCS#5),


Test patch:
diff --git a/src/tests/test_rng.cpp b/src/tests/test_rng.cpp
index 1dbb0444e..f54414d26 100644
--- a/src/tests/test_rng.cpp
+++ b/src/tests/test_rng.cpp
@@ -28,8 +28,8 @@
    #include <botan/system_rng.h>
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
-   #include <botan/rdrand_rng.h>
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
+   #include <botan/processor_rng.h>
 #endif
 
 #if defined(BOTAN_HAS_ENTROPY_SOURCE)
@@ -779,31 +779,31 @@ BOTAN_REGISTER_TEST("system_rng", System_RNG_Tests);
 
 #endif
 
-#if defined(BOTAN_HAS_RDRAND_RNG)
+#if defined(BOTAN_HAS_PROCESSOR_RNG)
 
-class RDRAND_RNG_Tests final : public Test
+class Processor_RNG_Tests final : public Test
    {
    public:
       std::vector<Test::Result> run() override
          {
-         Test::Result result("RDRAND_RNG");
+         Test::Result result("Processor_RNG");
 
-         if(Botan::RDRAND_RNG::available())
+         if(Botan::Processor_RNG::available())
             {
-            Botan::RDRAND_RNG rng;
+            Botan::Processor_RNG rng;
 
-            result.test_eq("Expected name", rng.name(), "RDRAND");
-            result.confirm("RDRAND always seeded", rng.is_seeded());
+            result.test_ne("Has a name", rng.name(), "");
+            result.confirm("CPU RNG always seeded", rng.is_seeded());
             rng.clear(); // clear is a noop for rdrand
-            result.confirm("RDRAND always seeded", rng.is_seeded());
+            result.confirm("CPU RNG always seeded", rng.is_seeded());
 
             size_t reseed_bits = rng.reseed(Botan::Entropy_Sources::global_sources(),
                                             256,
                                             std::chrono::seconds(1));
-            result.test_eq("RDRAND cannot consume inputs", reseed_bits, size_t(0));
+            result.test_eq("CPU RNG cannot consume inputs", reseed_bits, size_t(0));
 
             /*
-            RDRAND_RNG ignores add_entropy calls - confirm this by passing
+            Processor_RNG ignores add_entropy calls - confirm this by passing
             an invalid ptr/length field to add_entropy. If it examined its
             arguments, it would crash...
             */
@@ -819,8 +819,8 @@ class RDRAND_RNG_Tests final : public Test
             }
          else
             {
-            result.test_throws("RDRAND_RNG throws if instruction not available",
-                               []() { Botan::RDRAND_RNG rng; });
+            result.test_throws("Processor_RNG throws if instruction not available",
+                               []() { Botan::Processor_RNG rng; });
             }
 
 
@@ -828,7 +828,7 @@ class RDRAND_RNG_Tests final : public Test
          }
    };
 
-BOTAN_REGISTER_TEST("rdrand_rng", RDRAND_RNG_Tests);
+BOTAN_REGISTER_TEST("processor_rng", Processor_RNG_Tests);
 
 #endif