Coverage Report

Created: 2020-05-23 13:54

/src/botan/src/lib/rng/processor_rng/processor_rng.cpp
Line
Count
Source (jump to first uncovered line)
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
0
   {
41
0
   hwrng_output output = 0;
42
0
   success = false;
43
0
44
0
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
45
0
   int cf = 0;
46
0
#if defined(BOTAN_USE_GCC_INLINE_ASM)
47
0
   // same asm seq works for 32 and 64 bit
48
0
   asm volatile("rdrand %0; adcl $0,%1" :
49
0
       "=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
0
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
0
   if(success)
78
0
      return output;
79
0
80
0
   return 0;
81
0
   }
82
83
hwrng_output read_hwrng()
84
0
   {
85
0
   for(size_t i = 0; i < HWRNG_RETRIES; ++i)
86
0
      {
87
0
      bool success = false;
88
0
      hwrng_output output = read_hwrng(success);
89
0
90
0
      if(success)
91
0
         return output;
92
0
      }
93
0
94
0
   throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
95
0
   }
96
97
}
98
99
//static
100
bool Processor_RNG::available()
101
0
   {
102
0
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
103
0
   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
0
   {
113
0
#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
114
0
   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
0
   {
124
0
   while(out_len >= sizeof(hwrng_output))
125
0
      {
126
0
      const hwrng_output r = read_hwrng();
127
0
      store_le(r, out);
128
0
      out += sizeof(hwrng_output);
129
0
      out_len -= sizeof(hwrng_output);
130
0
      }
131
0
132
0
   if(out_len > 0) // at most sizeof(hwrng_output)-1
133
0
      {
134
0
      const hwrng_output r = read_hwrng();
135
0
      for(size_t i = 0; i != out_len; ++i)
136
0
         out[i] = get_byte(i, r);
137
0
      }
138
0
   }
139
140
Processor_RNG::Processor_RNG()
141
0
   {
142
0
   if(!Processor_RNG::available())
143
0
      throw Invalid_State("Current CPU does not support RNG instruction");
144
0
   }
145
146
void Processor_RNG::add_entropy(const uint8_t[], size_t)
147
0
   {
148
0
   /* no way to add entropy */
149
0
   }
150
151
size_t Processor_RNG::reseed(Entropy_Sources&, size_t, std::chrono::milliseconds)
152
0
   {
153
0
   /* no way to add entropy */
154
0
   return 0;
155
0
   }
156
157
}