/src/botan/src/lib/rng/rdrand_rng/rdrand_rng.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * RDRAND RNG |
3 | | * (C) 2016,2019 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/rdrand_rng.h> |
9 | | #include <botan/loadstor.h> |
10 | | #include <botan/cpuid.h> |
11 | | |
12 | | #if !defined(BOTAN_USE_GCC_INLINE_ASM) |
13 | | #include <immintrin.h> |
14 | | #endif |
15 | | |
16 | | namespace Botan { |
17 | | |
18 | | namespace { |
19 | | |
20 | | #if defined(BOTAN_TARGET_ARCH_IS_X86_64) |
21 | | typedef uint64_t rdrand_output; |
22 | | #else |
23 | | typedef uint32_t rdrand_output; |
24 | | #endif |
25 | | |
26 | | BOTAN_FUNC_ISA("rdrnd") |
27 | | rdrand_output read_rdrand() |
28 | 0 | { |
29 | 0 | /* |
30 | 0 | * According to Intel, RDRAND is guaranteed to generate a random |
31 | 0 | * number within 10 retries on a working CPU |
32 | 0 | */ |
33 | 0 | const size_t RDRAND_RETRIES = 10; |
34 | 0 |
|
35 | 0 | for(size_t i = 0; i < RDRAND_RETRIES; ++i) |
36 | 0 | { |
37 | 0 | rdrand_output r = 0; |
38 | 0 | int cf = 0; |
39 | 0 |
|
40 | 0 | #if defined(BOTAN_USE_GCC_INLINE_ASM) |
41 | 0 | // same asm seq works for 32 and 64 bit |
42 | 0 | asm("rdrand %0; adcl $0,%1" : |
43 | 0 | "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); |
44 | | #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) |
45 | | cf = _rdrand64_step(&r); |
46 | | #else |
47 | | cf = _rdrand32_step(&r); |
48 | | #endif |
49 | 0 | if(1 == cf) |
50 | 0 | { |
51 | 0 | return r; |
52 | 0 | } |
53 | 0 | } |
54 | 0 |
|
55 | 0 | throw PRNG_Unseeded("RDRAND read failed"); |
56 | 0 | } |
57 | | |
58 | | } |
59 | | |
60 | | void RDRAND_RNG::randomize(uint8_t out[], size_t out_len) |
61 | 0 | { |
62 | 0 | while(out_len >= sizeof(rdrand_output)) |
63 | 0 | { |
64 | 0 | const rdrand_output r = read_rdrand(); |
65 | 0 | store_le(r, out); |
66 | 0 | out += sizeof(rdrand_output); |
67 | 0 | out_len -= sizeof(rdrand_output); |
68 | 0 | } |
69 | 0 |
|
70 | 0 | if(out_len > 0) // at most sizeof(rdrand_output)-1 |
71 | 0 | { |
72 | 0 | const rdrand_output r = read_rdrand(); |
73 | 0 | for(size_t i = 0; i != out_len; ++i) |
74 | 0 | out[i] = get_byte(i, r); |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | RDRAND_RNG::RDRAND_RNG() |
79 | 0 | { |
80 | 0 | if(!RDRAND_RNG::available()) |
81 | 0 | throw Invalid_State("Current CPU does not support RDRAND instruction"); |
82 | 0 | } |
83 | | |
84 | | //static |
85 | | bool RDRAND_RNG::available() |
86 | 0 | { |
87 | 0 | return CPUID::has_rdrand(); |
88 | 0 | } |
89 | | |
90 | | //static |
91 | | uint32_t RDRAND_RNG::rdrand() |
92 | 0 | { |
93 | 0 | return static_cast<uint32_t>(read_rdrand()); |
94 | 0 | } |
95 | | |
96 | | //static |
97 | | BOTAN_FUNC_ISA("rdrnd") |
98 | | uint32_t RDRAND_RNG::rdrand_status(bool& ok) |
99 | 0 | { |
100 | 0 | ok = false; |
101 | 0 |
|
102 | 0 | try |
103 | 0 | { |
104 | 0 | const uint32_t r = static_cast<uint32_t>(read_rdrand()); |
105 | 0 | ok = true; |
106 | 0 | return r; |
107 | 0 | } |
108 | 0 | catch(PRNG_Unseeded&) {} |
109 | 0 |
|
110 | 0 | return 0; |
111 | 0 | } |
112 | | |
113 | | } |