/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 | | #if !defined(BOTAN_USE_GCC_INLINE_ASM) |
27 | | BOTAN_FUNC_ISA("rdrnd") |
28 | | #endif |
29 | | rdrand_output read_rdrand() |
30 | 0 | { |
31 | 0 | /* |
32 | 0 | * According to Intel, RDRAND is guaranteed to generate a random |
33 | 0 | * number within 10 retries on a working CPU |
34 | 0 | */ |
35 | 0 | const size_t RDRAND_RETRIES = 10; |
36 | 0 |
|
37 | 0 | for(size_t i = 0; i < RDRAND_RETRIES; ++i) |
38 | 0 | { |
39 | 0 | rdrand_output r = 0; |
40 | 0 | int cf = 0; |
41 | 0 |
|
42 | 0 | #if defined(BOTAN_USE_GCC_INLINE_ASM) |
43 | 0 | // same asm seq works for 32 and 64 bit |
44 | 0 | asm("rdrand %0; adcl $0,%1" : |
45 | 0 | "=r" (r), "=r" (cf) : "0" (r), "1" (cf) : "cc"); |
46 | | #elif defined(BOTAN_TARGET_ARCH_IS_X86_64) |
47 | | cf = _rdrand64_step(&r); |
48 | | #else |
49 | | cf = _rdrand32_step(&r); |
50 | | #endif |
51 | 0 | if(1 == cf) |
52 | 0 | { |
53 | 0 | return r; |
54 | 0 | } |
55 | 0 | } |
56 | 0 |
|
57 | 0 | throw PRNG_Unseeded("RDRAND read failed"); |
58 | 0 | } |
59 | | |
60 | | } |
61 | | |
62 | | void RDRAND_RNG::randomize(uint8_t out[], size_t out_len) |
63 | 0 | { |
64 | 0 | while(out_len >= sizeof(rdrand_output)) |
65 | 0 | { |
66 | 0 | const rdrand_output r = read_rdrand(); |
67 | 0 | store_le(r, out); |
68 | 0 | out += sizeof(rdrand_output); |
69 | 0 | out_len -= sizeof(rdrand_output); |
70 | 0 | } |
71 | 0 |
|
72 | 0 | if(out_len > 0) // at most sizeof(rdrand_output)-1 |
73 | 0 | { |
74 | 0 | const rdrand_output r = read_rdrand(); |
75 | 0 | for(size_t i = 0; i != out_len; ++i) |
76 | 0 | out[i] = get_byte(i, r); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | RDRAND_RNG::RDRAND_RNG() |
81 | 0 | { |
82 | 0 | if(!RDRAND_RNG::available()) |
83 | 0 | throw Invalid_State("Current CPU does not support RDRAND instruction"); |
84 | 0 | } |
85 | | |
86 | | //static |
87 | | bool RDRAND_RNG::available() |
88 | 0 | { |
89 | 0 | return CPUID::has_rdrand(); |
90 | 0 | } |
91 | | |
92 | | //static |
93 | | uint32_t RDRAND_RNG::rdrand() |
94 | 0 | { |
95 | 0 | return static_cast<uint32_t>(read_rdrand()); |
96 | 0 | } |
97 | | |
98 | | //static |
99 | | BOTAN_FUNC_ISA("rdrnd") |
100 | | uint32_t RDRAND_RNG::rdrand_status(bool& ok) |
101 | 0 | { |
102 | 0 | ok = false; |
103 | 0 |
|
104 | 0 | try |
105 | 0 | { |
106 | 0 | const uint32_t r = static_cast<uint32_t>(read_rdrand()); |
107 | 0 | ok = true; |
108 | 0 | return r; |
109 | 0 | } |
110 | 0 | catch(PRNG_Unseeded&) {} |
111 | 0 |
|
112 | 0 | return 0; |
113 | 0 | } |
114 | | |
115 | | } |