Botan  2.15.0
Crypto and TLS for C++11
processor_rng.cpp
Go to the documentation of this file.
1 /*
2 * (C) 2016,2019,2020 Jack Lloyd
3 *
4 * Botan is released under the Simplified BSD License (see license.txt)
5 */
6 
7 #include <botan/processor_rng.h>
8 #include <botan/loadstor.h>
9 #include <botan/cpuid.h>
10 
11 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY) && !defined(BOTAN_USE_GCC_INLINE_ASM)
12  #include <immintrin.h>
13 #endif
14 
15 namespace Botan {
16 
17 namespace {
18 
19 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
20  /*
21  * According to Intel, RDRAND is guaranteed to generate a random
22  * number within 10 retries on a working CPU
23  */
24  const size_t HWRNG_RETRIES = 10;
25 
26 #else
27  /*
28  * Lacking specific guidance we give the CPU quite a bit of leeway
29  */
30  const size_t HWRNG_RETRIES = 512;
31 #endif
32 
33 #if defined(BOTAN_TARGET_ARCH_IS_X86_32)
34  typedef uint32_t hwrng_output;
35 #else
36  typedef uint64_t hwrng_output;
37 #endif
38 
39 hwrng_output read_hwrng(bool& success)
40  {
41  hwrng_output output = 0;
42  success = false;
43 
44 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
45  int cf = 0;
46 #if defined(BOTAN_USE_GCC_INLINE_ASM)
47  // same asm seq works for 32 and 64 bit
48  asm volatile("rdrand %0; adcl $0,%1" :
49  "=r" (output), "=r" (cf) : "0" (output), "1" (cf) : "cc");
50 #elif defined(BOTAN_TARGET_ARCH_IS_X86_32)
51  cf = _rdrand32_step(&output);
52 #else
53  cf = _rdrand64_step(&output);
54 #endif
55  success = (1 == cf);
56 
57 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
58 
59  /*
60  DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy.
61  Avoid the bias by invoking it twice and, assuming both succeed, returning the
62  XOR of the two results, which should unbias the output.
63  */
64  uint64_t output2 = 0;
65  // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED)
66  asm volatile("darn %0, 1" : "=r" (output));
67  asm volatile("darn %0, 1" : "=r" (output2));
68 
69  if((~output) != 0 && (~output2) != 0)
70  {
71  output ^= output2;
72  success = true;
73  }
74 
75 #endif
76 
77  if(success)
78  return output;
79 
80  return 0;
81  }
82 
83 hwrng_output read_hwrng()
84  {
85  for(size_t i = 0; i < HWRNG_RETRIES; ++i)
86  {
87  bool success = false;
88  hwrng_output output = read_hwrng(success);
89 
90  if(success)
91  return output;
92  }
93 
94  throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
95  }
96 
97 }
98 
99 //static
101  {
102 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
103  return CPUID::has_rdrand();
104 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
105  return CPUID::has_darn_rng();
106 #else
107  return false;
108 #endif
109  }
110 
111 std::string Processor_RNG::name() const
112  {
113 #if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
114  return "rdrand";
115 #elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
116  return "darn";
117 #else
118  return "hwrng";
119 #endif
120  }
121 
122 void Processor_RNG::randomize(uint8_t out[], size_t out_len)
123  {
124  while(out_len >= sizeof(hwrng_output))
125  {
126  const hwrng_output r = read_hwrng();
127  store_le(r, out);
128  out += sizeof(hwrng_output);
129  out_len -= sizeof(hwrng_output);
130  }
131 
132  if(out_len > 0) // at most sizeof(hwrng_output)-1
133  {
134  const hwrng_output r = read_hwrng();
135  for(size_t i = 0; i != out_len; ++i)
136  out[i] = get_byte(i, r);
137  }
138  }
139 
141  {
143  throw Invalid_State("Current CPU does not support RNG instruction");
144  }
145 
146 void Processor_RNG::add_entropy(const uint8_t[], size_t)
147  {
148  /* no way to add entropy */
149  }
150 
151 size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds)
152  {
153  /* no way to add entropy */
154  return 0;
155  }
156 
157 }
constexpr uint8_t get_byte(size_t byte_num, T input)
Definition: loadstor.h:41
void add_entropy(const uint8_t[], size_t) override
size_t reseed(Entropy_Sources &, size_t, std::chrono::milliseconds) override
Definition: alg_id.cpp:13
static bool available()
void randomize(uint8_t out[], size_t out_len) override
std::string name() const override
void store_le(uint16_t in, uint8_t out[2])
Definition: loadstor.h:454