• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 20895207605

11 Jan 2026 12:33PM UTC coverage: 90.417% (-0.002%) from 90.419%
20895207605

Pull #5228

github

web-flow
Merge 89bd86f2c into 2bea5a273
Pull Request #5228: processor_rng: Add support for ARMv8.5-A FEAT_RNG extension

101691 of 112469 relevant lines covered (90.42%)

12751505.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.11
/src/lib/rng/processor_rng/processor_rng.cpp
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
#elif defined(BOTAN_TARGET_ARCH_IS_ARM64) && !defined(BOTAN_USE_GCC_INLINE_ASM)
16
   #include <arm_acle.h>
17
#endif
18

19
namespace Botan {
20

21
namespace {
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
   */
28
const 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
    */
38
const size_t HWRNG_RETRIES = 10;
39

40
#else
41
/*
42
   * Lacking specific guidance we give the CPU quite a bit of leeway
43
   */
44
const size_t HWRNG_RETRIES = 512;
45
#endif
46

47
#if defined(BOTAN_TARGET_ARCH_IS_X86_32)
48
typedef uint32_t hwrng_output;
49
#else
50
typedef uint64_t hwrng_output;
51
#endif
52

53
hwrng_output read_hwrng(bool& success) {
47,158✔
54
   hwrng_output output = 0;  // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm
47,158✔
55
   success = false;
47,158✔
56

57
#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)
58
   int cf = 0;  // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm
47,158✔
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");
94,316✔
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);
47,158✔
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
#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
88

89
   uint64_t nzcv = 0;  // NOLINT(*-const-correctness) clang-tidy doesn't understand inline asm
90
   #if defined(BOTAN_USE_GCC_INLINE_ASM)
91
   // NOLINTNEXTLINE(*-no-assembler)
92
   asm volatile("mrs %0, rndr; mrs %1, nzcv" : "=r"(output), "=r"(nzcv) : "0"(output), "1"(nzcv) : "cc");
93
   #else
94
   nzcv = __rndr(&output);
95
   #endif
96
   success = (0 == nzcv) && (0 != output);
97

98
#endif
99

100
   if(success) {
47,158✔
101
      return output;
47,158✔
102
   }
103

104
   return 0;
105
}
106

107
hwrng_output read_hwrng() {
47,158✔
108
   for(size_t i = 0; i < HWRNG_RETRIES; ++i) {
47,158✔
109
      bool success = false;
47,158✔
110
      const hwrng_output output = read_hwrng(success);
47,158✔
111

112
      if(success) {
×
113
         return output;
47,158✔
114
      }
115
   }
116

117
   throw PRNG_Unseeded("Processor RNG instruction failed to produce output within expected iterations");
×
118
}
119

120
}  // namespace
121

122
//static
123
bool Processor_RNG::available() {
14✔
124
#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)
125
   return CPUID::has(CPUID::Feature::RDRAND);
14✔
126
#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY)
127
   return CPUID::has(CPUID::Feature::DARN);
128
#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
129
   return CPUID::has(CPUID::Feature::RNG);
130
#else
131
   return false;
132
#endif
133
}
134

135
std::string Processor_RNG::name() const {
15✔
136
#if defined(BOTAN_TARGET_ARCH_IS_X86_FAMILY)
137
   return "rdrand";
15✔
138
#elif defined(BOTAN_TARGET_ARCH_IS_PPC_FAMILY)
139
   return "darn";
140
#elif defined(BOTAN_TARGET_ARCH_IS_ARM64)
141
   return "rng";
142
#else
143
   return "hwrng";
144
#endif
145
}
146

147
void Processor_RNG::fill_bytes_with_input(std::span<uint8_t> out, std::span<const uint8_t> in) {
456✔
148
   // No way to provide entropy to processor-specific generator, ignore...
149
   BOTAN_UNUSED(in);
456✔
150

151
   while(out.size() >= sizeof(hwrng_output)) {
47,501✔
152
      const hwrng_output r = read_hwrng();
47,045✔
153
      store_le(r, out.data());
47,045✔
154
      out = out.subspan(sizeof(hwrng_output));
47,045✔
155
   }
156

157
   if(!out.empty()) {
456✔
158
      // at most sizeof(hwrng_output)-1 bytes left
159
      const hwrng_output r = read_hwrng();
113✔
160
      uint8_t hwrng_bytes[sizeof(hwrng_output)];
113✔
161
      store_le(r, hwrng_bytes);
113✔
162

163
      for(size_t i = 0; i != out.size(); ++i) {
563✔
164
         out[i] = hwrng_bytes[i];
450✔
165
      }
166
   }
167
}
456✔
168

169
Processor_RNG::Processor_RNG() {
7✔
170
   if(!Processor_RNG::available()) {
7✔
171
      throw Invalid_State("Current CPU does not support RNG instruction");
×
172
   }
173
}
7✔
174

175
size_t Processor_RNG::reseed(Entropy_Sources& /*srcs*/,
1✔
176
                             size_t /*poll_bits*/,
177
                             std::chrono::milliseconds /*poll_timeout*/) {
178
   /* no way to add entropy */
179
   return 0;
1✔
180
}
181

182
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc