// Copyright © 2014, Simonas Kazlauskas // // Permission to use, copy, modify, and/or distribute this software for any purpose with or without // fee is hereby granted, provided that the above copyright notice and this permission notice // appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE // OF THIS SOFTWARE. //! An implementation of random number generators based on `rdrand` and `rdseed` instructions. //! //! The random number generators provided by this crate are fairly slow (the latency for these //! instructions is pretty high), but provide high quality random bits. Caveat is: neither AMD’s //! nor Intel’s designs are public and therefore are not verifiable for lack of backdoors. //! //! Unless you know what you are doing, use the random number generators provided by the `rand` //! crate (such as `OsRng`) instead. //! //! Here are a measurements for select processor architectures. Check [Agner’s instruction tables] //! for up-to-date listings. //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //! //!
ArchitectureLatency (cycles)Maximum throughput (per core)
u16u32u64
AMD Ryzen~1200~1200~2500~12MB/s @ 3.7GHz
Intel Skylake460460460~72MB/s @ 4.2GHz
Intel Haswell320320320~110MB/s @ 4.4GHz
//! //! [Agner’s instruction tables]: http://agner.org/optimize/ #![cfg_attr(not(feature = "std"), no_std)] extern crate rand_core; #[cfg(feature = "std")] extern crate core; pub mod changelog; use rand_core::{RngCore, CryptoRng, Error, ErrorKind}; use core::slice; const RETRY_LIMIT: u8 = 127; #[cold] #[inline(never)] pub(crate) fn busy_loop_fail() -> ! { panic!("hardware generator failure"); } /// A cryptographically secure statistically uniform, non-periodic and non-deterministic random bit /// generator. /// /// Note that this generator may be implemented using a deterministic algorithm that is reseeded /// routinely from a non-deterministic entropy source to achieve the desirable properties. /// /// This generator is a viable replacement to any generator, however, since nobody has audited /// Intel or AMD hardware yet, the usual disclaimers as to their suitability apply. /// /// It is potentially faster than `OsRng`, but is only supported on more recent Intel (Ivy Bridge /// and later) and AMD (Ryzen and later) processors. #[derive(Clone, Copy)] pub struct RdRand(()); /// A cryptographically secure non-deterministic random bit generator. /// /// This generator produces high-entropy output and is suited to seed other pseudo-random /// generators. /// /// This instruction currently is only available in Intel Broadwell (and later) and AMD Ryzen /// processors. /// /// This generator is not intended for general random number generation purposes and should be used /// to seed other generators implementing [rand_core::SeedableRng]. #[derive(Clone, Copy)] pub struct RdSeed(()); impl CryptoRng for RdRand {} impl CryptoRng for RdSeed {} mod arch { #[cfg(target_arch = "x86_64")] pub use core::arch::x86_64::*; #[cfg(target_arch = "x86")] pub use core::arch::x86::*; #[cfg(target_arch = "x86")] pub(crate) unsafe fn _rdrand64_step(dest: &mut u64) -> i32 { let mut ret1: u32 = ::core::mem::uninitialized(); let mut ret2: u32 = ::core::mem::uninitialized(); if _rdrand32_step(&mut ret1) != 0 && _rdrand32_step(&mut ret2) != 0 { *dest = (ret1 as u64) << 32 | (ret2 as u64); 1 } else { 0 } } #[cfg(target_arch = "x86")] pub(crate) unsafe fn _rdseed64_step(dest: &mut u64) -> i32 { let mut ret1: u32 = ::core::mem::uninitialized(); let mut ret2: u32 = ::core::mem::uninitialized(); if _rdseed32_step(&mut ret1) != 0 && _rdseed32_step(&mut ret2) != 0 { *dest = (ret1 as u64) << 32 | (ret2 as u64); 1 } else { 0 } } } #[cfg(not(feature = "std"))] macro_rules! is_x86_feature_detected { ("rdrand") => {{ if cfg!(target_feature="rdrand") { true } else if cfg!(target_env = "sgx") { false } else { const FLAG : u32 = 1 << 30; unsafe { ::arch::__cpuid(1).ecx & FLAG == FLAG } } }}; ("rdseed") => {{ if cfg!(target_feature = "rdseed") { true } else if cfg!(target_env = "sgx") { false } else { const FLAG : u32 = 1 << 18; unsafe { ::arch::__cpuid(7).ebx & FLAG == FLAG } } }}; } macro_rules! loop_rand { ($el: ty, $step: path) => { { let mut idx = 0; loop { let mut el: $el = ::core::mem::uninitialized(); if $step(&mut el) != 0 { break Some(el); } else if idx == RETRY_LIMIT { break None; } idx += 1; } } } } macro_rules! impl_rand { ($gen:ident, $feat:tt, $step16: path, $step32:path, $step64:path, maxstep = $maxstep:path, maxty = $maxty: ty) => { impl $gen { /// Create a new instance of the random number generator. /// /// This constructor checks whether the CPU the program is running on supports the /// instruction necessary for this generator to operate. If the instruction is not /// supported, an error is returned. pub fn new() -> Result { if is_x86_feature_detected!($feat) { Ok($gen(())) } else { Err(Error::new(rand_core::ErrorKind::Unavailable, "the instruction is not supported")) } } /// Generate a single random `u16` value. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will return `None`. /// /// In case `None` is returned, the caller should assume that an non-recoverable /// hardware failure has occured and use another random number genrator instead. #[inline(always)] pub fn try_next_u16(&self) -> Option { #[target_feature(enable = $feat)] unsafe fn imp() -> Option { loop_rand!(u16, $step16) } unsafe { imp() } } /// Generate a single random `u32` value. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will return `None`. /// /// In case `None` is returned, the caller should assume that an non-recoverable /// hardware failure has occured and use another random number genrator instead. #[inline(always)] pub fn try_next_u32(&self) -> Option { #[target_feature(enable = $feat)] unsafe fn imp() -> Option { loop_rand!(u32, $step32) } unsafe { imp() } } /// Generate a single random `u64` value. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will return `None`. /// /// In case `None` is returned, the caller should assume that an non-recoverable /// hardware failure has occured and use another random number genrator instead. /// /// Note, that on 32-bit targets, there’s no underlying instruction to generate a /// 64-bit number, so it is emulated with the 32-bit version of the instruction. #[inline(always)] pub fn try_next_u64(&self) -> Option { #[target_feature(enable = $feat)] unsafe fn imp() -> Option { loop_rand!(u64, $step64) } unsafe { imp() } } } impl RngCore for $gen { /// Generate a single random `u32` value. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// # Panic /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will `panic`. /// /// In case `panic` occurs, the caller should assume that an non-recoverable /// hardware failure has occured and use another random number genrator instead. #[inline(always)] fn next_u32(&mut self) -> u32 { if let Some(result) = self.try_next_u32() { result } else { busy_loop_fail() } } /// Generate a single random `u64` value. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// Note, that on 32-bit targets, there’s no underlying instruction to generate a /// 64-bit number, so it is emulated with the 32-bit version of the instruction. /// /// # Panic /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will `panic`. /// /// In case `panic` occurs, the caller should assume that an non-recoverable /// hardware failure has occured and use another random number genrator instead. #[inline(always)] fn next_u64(&mut self) -> u64 { if let Some(result) = self.try_next_u64() { result } else { busy_loop_fail() } } /// Fill a buffer `dest` with random data. /// /// See `try_fill_bytes` for a more extensive documentation. /// /// # Panic /// /// This method will panic any time `try_fill_bytes` would return an error. #[inline(always)] fn fill_bytes(&mut self, dest: &mut [u8]) { if let Err(_) = self.try_fill_bytes(dest) { busy_loop_fail() } } /// Fill a buffer `dest` with random data. /// /// This method will use the most appropriate variant of the instruction available on /// the machine to achieve the greatest single-core throughput, however it has a /// slightly higher setup cost than the plain `next_u32` or `next_u64` methods. /// /// The underlying instruction may fail for variety reasons (such as actual hardware /// failure or exhausted entropy), however the exact reason for the failure is not /// usually exposed. /// /// This method will retry calling the instruction a few times, however if all the /// attempts fail, it will return an error. /// /// If an error is returned, the caller should assume that an non-recoverable hardware /// failure has occured and use another random number genrator instead. #[inline(always)] fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { #[target_feature(enable = $feat)] unsafe fn imp(dest: &mut [u8]) -> Result<(), Error> { unsafe fn imp_less_fast(mut dest: &mut [u8], word: &mut $maxty, buffer: &mut &[u8]) -> Result<(), Error> { while !dest.is_empty() { if buffer.is_empty() { if let Some(w) = loop_rand!($maxty, $maxstep) { *word = w; *buffer = slice::from_raw_parts( word as *const _ as *const u8, ::core::mem::size_of::<$maxty>() ); } else { return Err(Error::new(ErrorKind::Unexpected, "hardware generator failure")); } } let len = dest.len().min(buffer.len()); let (copy_src, leftover) = buffer.split_at(len); let (copy_dest, dest_leftover) = { dest }.split_at_mut(len); *buffer = leftover; dest = dest_leftover; ::core::ptr::copy_nonoverlapping( copy_src.as_ptr(), copy_dest.as_mut_ptr(), len ); } Ok(()) } let destlen = dest.len(); if destlen > ::core::mem::size_of::<$maxty>() { let (left, mid, right) = dest.align_to_mut(); let mut word = 0; let mut buffer: &[u8] = &[]; for el in mid { if let Some(val) = loop_rand!($maxty, $maxstep) { *el = val; } else { return Err(Error::new(ErrorKind::Unexpected, "hardware generator failure")); } } imp_less_fast(left, &mut word, &mut buffer)?; imp_less_fast(right, &mut word, &mut buffer) } else { let mut word = 0; let mut buffer: &[u8] = &[]; imp_less_fast(dest, &mut word, &mut buffer) } } unsafe { imp(dest) } } } } } #[cfg(target_arch = "x86_64")] impl_rand!(RdRand, "rdrand", ::arch::_rdrand16_step, ::arch::_rdrand32_step, ::arch::_rdrand64_step, maxstep = ::arch::_rdrand64_step, maxty = u64); #[cfg(target_arch = "x86_64")] impl_rand!(RdSeed, "rdseed", ::arch::_rdseed16_step, ::arch::_rdseed32_step, ::arch::_rdseed64_step, maxstep = ::arch::_rdseed64_step, maxty = u64); #[cfg(target_arch = "x86")] impl_rand!(RdRand, "rdrand", ::arch::_rdrand16_step, ::arch::_rdrand32_step, ::arch::_rdrand64_step, maxstep = ::arch::_rdrand32_step, maxty = u32); #[cfg(target_arch = "x86")] impl_rand!(RdSeed, "rdseed", ::arch::_rdseed16_step, ::arch::_rdseed32_step, ::arch::_rdseed64_step, maxstep = ::arch::_rdseed32_step, maxty = u32); #[test] fn rdrand_works() { let _ = RdRand::new().map(|mut r| { r.next_u32(); r.next_u64(); }); } #[test] fn fill_fills_all_bytes() { let _ = RdRand::new().map(|mut r| { let mut peach; let mut banana; let mut start = 0; let mut end = 128; 'outer: while start < end { banana = [0; 128]; for _ in 0..512 { peach = [0; 128]; r.fill_bytes(&mut peach[start..end]); for (b, p) in banana.iter_mut().zip(peach.iter()) { *b = *b | *p; } if (&banana[start..end]).iter().all(|x| *x != 0) { assert!(banana[..start].iter().all(|x| *x == 0), "all other values must be 0"); assert!(banana[end..].iter().all(|x| *x == 0), "all other values must be 0"); if start < 17 { start += 1; } else { end -= 3; } continue 'outer; } } panic!("wow, we broke it? {} {} {:?}", start, end, &banana[..]) } }); } #[test] fn rdseed_works() { let _ = RdSeed::new().map(|mut r| { r.next_u32(); r.next_u64(); }); }