1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// ---------------- [ File: bitcoin-random/src/rd_rand.rs ]
crate::ix!();
/**
| Read 64 bits of entropy using rdrand.
|
| Must only be called when RdRand is supported.
|
*/
#[cfg(have_getcpuid)]
pub fn get_rd_rand() -> u64 {
// 32-bit x86: stitch two 32-bit RDRAND results
#[cfg(target_arch = "x86")]
{
// RdRand may very rarely fail. Invoke it
// up to 10 times in a loop to reduce this
// risk.
let mut ok: u8 = 0;
// Initialize to 0 to silence a compiler
// warning that r1 or r2 may be used
// uninitialized. Even if rdrand fails
// (!ok) it will set the output to 0, but
// there is no way that the compiler could
// know that.
let mut r1: u32 = 0;
let mut r2: u32 = 0;
for _ in 0..10 {
unsafe {
// rdrand %eax
core::arch::asm!(".byte 0x0f, 0xc7, 0xf0; setc {ok}",
out("eax") r1, ok = out(reg_byte) ok,
options(nostack, preserves_flags));
}
if ok != 0 { break; }
}
for _ in 0..10 {
unsafe {
// rdrand %eax
core::arch::asm!(".byte 0x0f, 0xc7, 0xf0; setc {ok}",
out("eax") r2, ok = out(reg_byte) ok,
options(nostack, preserves_flags));
}
if ok != 0 { break; }
}
return ((r2 as u64) << 32) | (r1 as u64);
}
// 64-bit x86_64: single 64-bit RDRAND
#[cfg(target_arch = "x86_64")]
{
let mut ok: u8 = 0;
// See above why we initialize to 0.
let mut r: u64 = 0;
for _ in 0..10 {
unsafe {
// rdrand %rax
core::arch::asm!(".byte 0x48, 0x0f, 0xc7, 0xf0; setc {ok}",
out("rax") r, ok = out(reg_byte) ok,
options(nostack, preserves_flags));
}
if ok != 0 { break; }
}
return r;
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
{
panic!("RdRand is only supported on x86 and x86_64");
}
}
#[cfg(test)]
mod rd_rand_spec {
use super::*;
#[traced_test]
#[cfg(all(have_getcpuid, any(target_arch = "x86", target_arch = "x86_64")))]
fn rd_rand_callable_if_supported() {
// Ensure flags are initialized for this process.
init_hardware_rand();
if G_RDRAND_SUPPORTED.load(core::sync::atomic::Ordering::Relaxed) {
let _ = get_rd_rand(); // should not panic on supported HW
}
}
}