Last active
June 29, 2019 20:35
-
-
Save newpavlov/0b00aa5f7b22a0d3df99b232002692d5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use crate::Error; | |
use core::arch::x86_64::_rdrand64_step; | |
use core::mem; | |
use core::num::NonZeroU32; | |
// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software | |
// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures | |
// Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. | |
const RETRY_LIMIT: usize = 10; | |
const WORD_SIZE: usize = mem::size_of::<u64>(); | |
#[target_feature(enable = "rdrand")] | |
unsafe fn rdrand(check_value: bool) -> Result<[u8; WORD_SIZE], Error> { | |
for _ in 0..RETRY_LIMIT { | |
let mut el = mem::uninitialized(); | |
if _rdrand64_step(&mut el) == 1 { | |
// AMD CPUs from families 14h to 16h (pre Ryzen) will sometimes give | |
// bogus random data. Discard these values and warn the user. | |
// See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505 | |
if value_check && (el == 0 || el == !0) { | |
error!("RDRAND returned suspicious value {}, CPU RNG is broken", el); | |
return Err(Error::UNKNOWN); | |
} | |
return Ok(el.to_ne_bytes()); | |
} | |
} | |
error!("RDRAND failed, CPU issue likely"); | |
Err(Error::UNKNOWN) | |
} | |
// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. | |
#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] | |
compile_error!( | |
"SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd." | |
); | |
#[cfg(target_feature = "rdrand")] | |
fn is_rdrand_supported() -> bool { | |
true | |
} | |
// TODO use is_x86_feature_detected!("rdrand") when that works in core. See: | |
// https://github.com/rust-lang-nursery/stdsimd/issues/464 | |
#[cfg(not(target_feature = "rdrand"))] | |
fn is_rdrand_supported() -> bool { | |
use core::arch::x86_64::__cpuid; | |
use lazy_static::lazy_static; | |
// SAFETY: All x86_64 CPUs support CPUID leaf 1 | |
const FLAG: u32 = 1 << 30; | |
lazy_static! { | |
static ref HAS_RDRAND: bool = unsafe { __cpuid(1).ecx & FLAG != 0 }; | |
} | |
*HAS_RDRAND | |
} | |
#[cfg(target_env = "sgx")] | |
fn is_cpu_flawed() -> bool { | |
false | |
} | |
#[cfg(not(target_env = "sgx"))] | |
fn is_cpu_flawed() -> bool { | |
use lazy_static::lazy_static; | |
lazy_static! { | |
static ref CPU_FLAWED: bool = check_cpu(); | |
} | |
*CPU_FLAWED | |
} | |
#[cfg(not(target_env = "sgx"))] | |
fn check_cpu() -> bool { .. } | |
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { | |
if !is_rdrand_supported() { | |
return Err(Error::UNAVAILABLE); | |
} | |
// SAFETY: After this point, rdrand is supported, so calling the rdrand | |
// functions is not undefined behavior. | |
unsafe { rdrand_exact(dest) } | |
} | |
#[target_feature(enable = "rdrand")] | |
unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { | |
let check_value = is_cpu_flawed(); | |
// We use chunks_exact_mut instead of chunks_mut as it allows almost all | |
// calls to memcpy to be elided by the compiler. | |
let mut chunks = dest.chunks_exact_mut(WORD_SIZE); | |
for chunk in chunks.by_ref() { | |
chunk.copy_from_slice(&rdrand(check_value)?); | |
} | |
let tail = chunks.into_remainder(); | |
let n = tail.len(); | |
if n > 0 { | |
tail.copy_from_slice(&rdrand(check_value)?[..n]); | |
} | |
Ok(()) | |
} | |
#[inline(always)] | |
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { | |
None | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment