aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/entropy/rdseed/rdseed.cpp
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2019-06-30 16:11:07 -0400
committerJack Lloyd <[email protected]>2019-08-16 12:55:17 -0400
commitd146e8aae19336ee625b604bce57b0feb1961aa8 (patch)
tree8a27925f5190350bcdbb8f32bd6f5f86a1a89839 /src/lib/entropy/rdseed/rdseed.cpp
parent2db314bb9659ac2a34ab2954a9717c6f17279cb8 (diff)
Use RDRAND in such a way that an internal reseed is performed
At least according to Intel's docs. Closes #447
Diffstat (limited to 'src/lib/entropy/rdseed/rdseed.cpp')
-rw-r--r--src/lib/entropy/rdseed/rdseed.cpp94
1 files changed, 71 insertions, 23 deletions
diff --git a/src/lib/entropy/rdseed/rdseed.cpp b/src/lib/entropy/rdseed/rdseed.cpp
index fccc53f6a..1830edf9d 100644
--- a/src/lib/entropy/rdseed/rdseed.cpp
+++ b/src/lib/entropy/rdseed/rdseed.cpp
@@ -1,6 +1,7 @@
/*
* Entropy Source Using Intel's rdseed instruction
-* (C) 2015 Jack Lloyd, Daniel Neus
+* (C) 2015 Daniel Neus
+* (C) 2015,2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/
@@ -8,41 +9,88 @@
#include <botan/internal/rdseed.h>
#include <botan/cpuid.h>
-#if !defined(BOTAN_USE_GCC_INLINE_ASM)
- #include <immintrin.h>
-#endif
+#include <immintrin.h>
namespace Botan {
+namespace {
+
BOTAN_FUNC_ISA("rdseed")
-size_t Intel_Rdseed::poll(RandomNumberGenerator& rng)
+bool read_rdseed(secure_vector<uint32_t>& seed)
{
- if(CPUID::has_rdseed())
+ /*
+ * RDSEED is not guaranteed to generate an output within any specific number
+ * of attempts. However in testing on a Skylake system, with all hyperthreads
+ * occupied in tight RDSEED loops, RDSEED will still usually succeed in under
+ * 150 attempts. The maximum ever seen was 230 attempts until success. When
+ * idle, RDSEED usually succeeds in 1 or 2 attempts.
+ *
+ * We set an upper bound of 512 attempts, because it is possible that due
+ * to firmware issue RDSEED is simply broken and never succeeds. We do not
+ * want to loop forever in that case. If we exceed that limit, then we assume
+ * the hardware is actually just broken, and stop the poll.
+ */
+ const size_t RDSEED_RETRIES = 512;
+
+ for(size_t i = 0; i != RDSEED_RETRIES; ++i)
{
- for(size_t p = 0; p != BOTAN_ENTROPY_INTEL_RNG_POLLS; ++p)
- {
- for(size_t i = 0; i != BOTAN_ENTROPY_RDSEED_RETRIES; ++i)
- {
- uint32_t r = 0;
+ uint32_t r = 0;
+ int cf = 0;
#if defined(BOTAN_USE_GCC_INLINE_ASM)
- int cf = 0;
-
- // Encoding of rdseed %eax
- asm(".byte 0x0F, 0xC7, 0xF8; adcl $0,%1" :
- "=a" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc");
+ asm("rdseed %0; adcl $0,%1" :
+ "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc");
#else
- int cf = _rdseed32_step(&r);
+ cf = _rdseed32_step(&r);
#endif
- if(1 == cf)
- {
- rng.add_entropy_T(r);
- break;
- }
- }
+
+ if(1 == cf)
+ {
+ seed.push_back(r);
+ return true;
+ }
+
+ // Intel suggests pausing if RDSEED fails.
+ _mm_pause();
+ }
+
+ return false; // failed to produce an output after many attempts
+ }
+
+}
+
+size_t Intel_Rdseed::poll(RandomNumberGenerator& rng)
+ {
+ const size_t RDSEED_BYTES = 1024;
+ static_assert(RDSEED_BYTES % 4 == 0, "Bad RDSEED configuration");
+
+ if(CPUID::has_rdseed())
+ {
+ secure_vector<uint32_t> seed;
+ seed.reserve(RDSEED_BYTES / 4);
+
+ for(size_t p = 0; p != RDSEED_BYTES / 4; ++p)
+ {
+ /*
+ If at any point we exceed our retry count, we stop the entire seed
+ gathering process. This situation will only occur in situations of
+ extremely high RDSEED utilization. If RDSEED is currently so highly
+ contended, then the rest of the poll is likely to also face contention and
+ it is better to quit now rather than (presumably) face very high retry
+ times for the rest of the poll.
+ */
+ if(!read_rdseed(seed))
+ break;
+ }
+
+ if(seed.size() > 0)
+ {
+ rng.add_entropy(reinterpret_cast<const uint8_t*>(seed.data()),
+ seed.size() * sizeof(uint32_t));
}
}
+ // RDSEED is used but not trusted
return 0;
}