aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/rng
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2020-05-08 07:24:50 -0400
committerJack Lloyd <[email protected]>2020-05-08 08:31:06 -0400
commitad851c2047273d3317cbdc88cefaa1c3aaa61ee2 (patch)
tree95119539acb70860525c7adc51893fd4e6731d79 /src/lib/rng
parent0ad3c8a4aac6a8a19ff71323eb1a7adb5fae2dee (diff)
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.
Diffstat (limited to 'src/lib/rng')
-rw-r--r--src/lib/rng/processor_rng/info.txt11
-rw-r--r--src/lib/rng/processor_rng/processor_rng.cpp157
-rw-r--r--src/lib/rng/processor_rng/processor_rng.h52
-rw-r--r--src/lib/rng/rdrand_rng/info.txt18
-rw-r--r--src/lib/rng/rdrand_rng/rdrand_rng.cpp92
-rw-r--r--src/lib/rng/rdrand_rng/rdrand_rng.h6
-rw-r--r--src/lib/rng/rng.h2
-rw-r--r--src/lib/rng/stateful_rng/stateful_rng.cpp12
8 files changed, 260 insertions, 90 deletions
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 @@
+<defines>
+PROCESSOR_RNG -> 20200508
+</defines>
+
+<cc>
+gcc
+clang
+icc
+msvc
+</cc>
+
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