Skip to content

Instantly share code, notes, and snippets.

@newpavlov
Last active June 29, 2019 20:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save newpavlov/0b00aa5f7b22a0d3df99b232002692d5 to your computer and use it in GitHub Desktop.
Save newpavlov/0b00aa5f7b22a0d3df99b232002692d5 to your computer and use it in GitHub Desktop.
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