summaryrefslogtreecommitdiff
path: root/vendor/rdrand/src/lib.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/rdrand/src/lib.rs
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/rdrand/src/lib.rs')
-rw-r--r--vendor/rdrand/src/lib.rs472
1 files changed, 472 insertions, 0 deletions
diff --git a/vendor/rdrand/src/lib.rs b/vendor/rdrand/src/lib.rs
new file mode 100644
index 0000000..423ae21
--- /dev/null
+++ b/vendor/rdrand/src/lib.rs
@@ -0,0 +1,472 @@
+// Copyright © 2014, Simonas Kazlauskas <rdrand@kazlauskas.me>
+//
+// 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.
+//!
+//! <table>
+//! <tr>
+//! <th>Architecture</th>
+//! <th colspan="3">Latency (cycles)</th>
+//! <th>Maximum throughput (per core)</th>
+//! </tr>
+//! <tr>
+//! <td></td>
+//! <td>u16</td>
+//! <td>u32</td>
+//! <td>u64</td>
+//! <td></td>
+//! </tr>
+//! <tr>
+//! <td>AMD Ryzen</td>
+//! <td>~1200</td>
+//! <td>~1200</td>
+//! <td>~2500</td>
+//! <td>~12MB/s @ 3.7GHz</td>
+//! </tr>
+//! <tr>
+//! <td>Intel Skylake</td>
+//! <td>460</td>
+//! <td>460</td>
+//! <td>460</td>
+//! <td>~72MB/s @ 4.2GHz</td>
+//! </tr>
+//! <tr>
+//! <td>Intel Haswell</td>
+//! <td>320</td>
+//! <td>320</td>
+//! <td>320</td>
+//! <td>~110MB/s @ 4.4GHz</td>
+//! </tr>
+//! </table>
+//!
+//! [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<Self, Error> {
+ 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<u16> {
+ #[target_feature(enable = $feat)]
+ unsafe fn imp()
+ -> Option<u16> {
+ 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<u32> {
+ #[target_feature(enable = $feat)]
+ unsafe fn imp()
+ -> Option<u32> {
+ 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<u64> {
+ #[target_feature(enable = $feat)]
+ unsafe fn imp()
+ -> Option<u64> {
+ 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();
+ });
+}