Botan 3.6.1
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
9#include <botan/internal/cpuid.h>
10#include <botan/internal/loadstor.h>
11
12#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
13 #include <immintrin.h>
14#endif
15
16namespace Botan {
17
18namespace {
19
20#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
21/*
22 * According to Intel, RDRAND is guaranteed to generate a random
23 * number within 10 retries on a working CPU
24 */
25const size_t HWRNG_RETRIES = 10;
26
27#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
28/**
29 * PowerISA 3.0 p.78:
30 * When the error value is obtained, software is expected to repeat the
31 * operation. [...] The recommended number of attempts may be
32 * implementation specific. In the absence of other guidance, ten attempts
33 * should be adequate.
34 */
35const size_t HWRNG_RETRIES = 10;
36
37#else
38/*
39 * Lacking specific guidance we give the CPU quite a bit of leeway
40 */
41const size_t HWRNG_RETRIES = 512;
42#endif
43
44#if defined(BOTAN_TARGET_ARCH_IS_X86_32)
45typedef uint32_t hwrng_output;
46#else
47typedef uint64_t hwrng_output;
48#endif
49
50hwrng_output read_hwrng(bool& success) {
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" : "=r"(output), "=r"(cf) : "0"(output), "1"(cf) : "cc");
59 #elif defined(BOTAN_TARGET_ARCH_IS_X86_32)
60 cf = _rdrand32_step(&output);
61 #else
62 cf = _rdrand64_step(reinterpret_cast<unsigned long long*>(&output));
63 #endif
64 success = (1 == cf);
65
66#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
67
68 /*
69 DARN indicates error by returning 0xFF..FF, ie is biased. Which is crazy.
70 Avoid the bias by invoking it twice and, assuming both succeed, returning the
71 XOR of the two results, which should unbias the output.
72 */
73 uint64_t output2 = 0;
74 // DARN codes are 0: 32-bit conditioned, 1: 64-bit conditioned, 2: 64-bit raw (ala RDSEED)
75 asm volatile("darn %0, 1" : "=r"(output));
76 asm volatile("darn %0, 1" : "=r"(output2));
77
78 if((~output) != 0 && (~output2) != 0) {
79 output ^= output2;
80 success = true;
81 }
82
83#endif
84
85 if(success) {
86 return output;
87 }
88
89 return 0;
90}
91
92hwrng_output read_hwrng() {
93 for(size_t i = 0; i < HWRNG_RETRIES; ++i) {
94 bool success = false;
95 hwrng_output output = read_hwrng(success);
96
97 if(success) {
98 return output;
99 }
100 }
101
102 throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
103}
104
105} // namespace
106
107//static
109#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
110 return CPUID::has_rdrand();
111#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
112 return CPUID::has_darn_rng();
113#else
114 return false;
115#endif
116}
117
118std::string Processor_RNG::name() const {
119#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
120 return "rdrand";
121#elif defined(BOTAN_TARGET_CPU_IS_PPC_FAMILY)
122 return "darn";
123#else
124 return "hwrng";
125#endif
126}
127
128void Processor_RNG::fill_bytes_with_input(std::span<uint8_t> out, std::span<const uint8_t> in) {
129 // No way to provide entropy to processor-specific generator, ignore...
130 BOTAN_UNUSED(in);
131
132 while(out.size() >= sizeof(hwrng_output)) {
133 const hwrng_output r = read_hwrng();
134 store_le(r, out.data());
135 out = out.subspan(sizeof(hwrng_output));
136 }
137
138 if(!out.empty()) {
139 // at most sizeof(hwrng_output)-1 bytes left
140 const hwrng_output r = read_hwrng();
141 uint8_t hwrng_bytes[sizeof(hwrng_output)];
142 store_le(r, hwrng_bytes);
143
144 for(size_t i = 0; i != out.size(); ++i) {
145 out[i] = hwrng_bytes[i];
146 }
147 }
148}
149
152 throw Invalid_State("Current CPU does not support RNG instruction");
153 }
154}
155
157 size_t /*poll_bits*/,
158 std::chrono::milliseconds /*poll_timeout*/) {
159 /* no way to add entropy */
160 return 0;
161}
162
163} // namespace Botan
#define BOTAN_UNUSED
Definition assert.h:118
size_t reseed(Entropy_Sources &, size_t, std::chrono::milliseconds) override
std::string name() const override
constexpr auto store_le(ParamTs &&... params)
Definition loadstor.h:764