Skip to content

Commit

Permalink
Improve RDRAND implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
josephlr committed Jun 11, 2019
1 parent 5302a81 commit ff7f9ba
Showing 1 changed file with 26 additions and 15 deletions.
41 changes: 26 additions & 15 deletions src/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,22 @@

//! Implementation for SGX using RDRAND instruction
use crate::Error;
use core::{mem, ptr};
use core::{cmp, mem};
use core::arch::x86_64::_rdrand64_step;
use core::num::NonZeroU32;

#[cfg(not(target_feature = "rdrand"))]
compile_error!("enable rdrand target feature!");

const RETRY_LIMIT: usize = 32;
// 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;

fn get_rand_u64() -> Result<u64, Error> {
for _ in 0..RETRY_LIMIT {
unsafe {
// SAFETY: we check for RDRAND support, and u64 can have any value.
let mut el = mem::uninitialized();
if _rdrand64_step(&mut el) == 1 {
return Ok(el);
Expand All @@ -29,23 +33,30 @@ fn get_rand_u64() -> Result<u64, Error> {
Err(Error::UNKNOWN)
}

pub fn getrandom_inner(mut dest: &mut [u8]) -> Result<(), Error> {
while dest.len() >= 8 {
let (chunk, left) = {dest}.split_at_mut(8);
fn get_rand_unaligned(mut dest: &mut [u8]) -> Result<(), Error> {
// slice::align_to_mut does not guarantee that the prefix/suffix slices are
// smaller than a u64, so we may need to call get_rand_u64 multiple times.
while !dest.is_empty() {
let data = get_rand_u64()?;
let n = cmp::min(dest.len(), mem::size_of_val(&data));

let (chunk, left) = { dest }.split_at_mut(n);
dest = left;
let r = get_rand_u64()?;
unsafe {
ptr::write_unaligned(chunk.as_mut_ptr() as *mut u64, r)
}
}
let n = dest.len();
if n != 0 {
let r = get_rand_u64()?;
let r: [u8; 8] = unsafe { mem::transmute(r) };
dest.copy_from_slice(&r[..n]);
chunk.copy_from_slice(&data.to_ne_bytes()[..n]);
}
Ok(())
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
// SAFETY: casting [u8] to [u64] is safe, if size/alignment are correct.
let (prefix, aligned, suffix) = unsafe { dest.align_to_mut() };

get_rand_unaligned(prefix)?;
for data in aligned {
*data = get_rand_u64()?;
}
get_rand_unaligned(suffix)
}

#[inline(always)]
pub fn error_msg_inner(_: NonZeroU32) -> Option<&'static str> { None }

0 comments on commit ff7f9ba

Please sign in to comment.