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