Botan 3.0.0-alpha0
Crypto and TLS for C&
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/internal/loadstor.h>
9#include <botan/internal/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
15namespace Botan {
16
17namespace {
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#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
27 /**
28 * PowerISA 3.0 p.78:
29 * When the error value is obtained, software is expected to repeat the
30 * operation. [...] The recommended number of attempts may be
31 * implementation specific. In the absence of other guidance, ten attempts
32 * should be adequate.
33 */
34 const size_t HWRNG_RETRIES = 10;
35
36#else
37 /*
38 * Lacking specific guidance we give the CPU quite a bit of leeway
39 */
40 const size_t HWRNG_RETRIES = 512;
41#endif
42
43#if defined(BOTAN_TARGET_ARCH_IS_X86_32)
44 typedef uint32_t hwrng_output;
45#else
46 typedef uint64_t hwrng_output;
47#endif
48
49hwrng_output read_hwrng(bool& success)
50 {
51 hwrng_output output = 0;
52 success = false;
53
54#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
55 int cf = 0;
56#if defined(BOTAN_USE_GCC_INLINE_ASM)
57 // same asm seq works for 32 and 64 bit
58 asm volatile("rdrand %0; adcl $0,%1" :
59 "=r" (output), "=r" (cf) : "0" (output), "1" (cf) : "cc");
60#elif defined(BOTAN_TARGET_ARCH_IS_X86_32)
61 cf = _rdrand32_step(&output);
62#else
63 cf = _rdrand64_step(&output);
64#endif
65 success = (1 == cf);
66
67#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
68
69 /*
70 DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy.
71 Avoid the bias by invoking it twice and, assuming both succeed, returning the
72 XOR of the two results, which should unbias the output.
73 */
74 uint64_t output2 = 0;
75 // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED)
76 asm volatile("darn %0, 1" : "=r" (output));
77 asm volatile("darn %0, 1" : "=r" (output2));
78
79 if((~output) != 0 && (~output2) != 0)
80 {
81 output ^= output2;
82 success = true;
83 }
84
85#endif
86
87 if(success)
88 return output;
89
90 return 0;
91 }
92
93hwrng_output read_hwrng()
94 {
95 for(size_t i = 0; i < HWRNG_RETRIES; ++i)
96 {
97 bool success = false;
98 hwrng_output output = read_hwrng(success);
99
100 if(success)
101 return output;
102 }
103
104 throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
105 }
106
107}
108
109//static
111 {
112#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
113 return CPUID::has_rdrand();
114#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
115 return CPUID::has_darn_rng();
116#else
117 return false;
118#endif
119 }
120
121std::string Processor_RNG::name() const
122 {
123#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
124 return "rdrand";
125#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
126 return "darn";
127#else
128 return "hwrng";
129#endif
130 }
131
132void Processor_RNG::randomize(uint8_t out[], size_t out_len)
133 {
134 while(out_len >= sizeof(hwrng_output))
135 {
136 const hwrng_output r = read_hwrng();
137 store_le(r, out);
138 out += sizeof(hwrng_output);
139 out_len -= sizeof(hwrng_output);
140 }
141
142 if(out_len > 0) // at most sizeof(hwrng_output)-1
143 {
144 const hwrng_output r = read_hwrng();
145 uint8_t hwrng_bytes[sizeof(hwrng_output)];
146 store_le(r, hwrng_bytes);
147
148 for(size_t i = 0; i != out_len; ++i)
149 out[i] = hwrng_bytes[i];
150 }
151 }
152
154 {
156 throw Invalid_State("Current CPU does not support RNG instruction");
157 }
158
159void Processor_RNG::add_entropy(const uint8_t /*input*/[], size_t /*length*/)
160 {
161 /* no way to add entropy */
162 }
163
164size_t Processor_RNG::reseed(Entropy_Sources& /*srcs*/, size_t /*poll_bits*/, std::chrono::milliseconds /*poll_timeout*/)
165 {
166 /* no way to add entropy */
167 return 0;
168 }
169
170}
static bool available()
void randomize(uint8_t out[], size_t out_len) override
void add_entropy(const uint8_t[], size_t) override
size_t reseed(Entropy_Sources &, size_t, std::chrono::milliseconds) override
std::string name() const override
Definition: alg_id.cpp:13
constexpr void store_le(uint16_t in, uint8_t out[2])
Definition: loadstor.h:465