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