Skip to main content

rdrand/
lib.rs

1// Copyright © 2014, Simonas Kazlauskas <rdrand@kazlauskas.me>
2//
3// Permission to use, copy, modify, and/or distribute this software for any purpose with or without
4// fee is hereby granted, provided that the above copyright notice and this permission notice
5// appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
8// SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
9// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
11// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
12// OF THIS SOFTWARE.
13//! An implementation of random number generators based on `rdrand` and `rdseed` instructions.
14//!
15//! The random number generators provided by this crate are fairly slow (the latency for these
16//! instructions is pretty high), but provide high quality random bits. Caveat is: neither AMD’s
17//! nor Intel’s designs are public and therefore are not verifiable for lack of backdoors.
18//!
19//! Unless you know what you are doing, use the random number generators provided by the `rand`
20//! crate (such as `OsRng`) instead.
21//!
22//! Here are a measurements for select processor architectures. Check [Agner’s instruction tables]
23//! for up-to-date listings.
24//!
25//! <table>
26//!   <tr>
27//!     <th>Architecture</th>
28//!     <th colspan="3">Latency (cycles)</th>
29//!     <th>Maximum throughput (per core)</th>
30//!   </tr>
31//!   <tr>
32//!     <td></td>
33//!     <td>u16</td>
34//!     <td>u32</td>
35//!     <td>u64</td>
36//!     <td></td>
37//!   </tr>
38//!   <tr>
39//!     <td>AMD Ryzen</td>
40//!     <td>~1200</td>
41//!     <td>~1200</td>
42//!     <td>~2500</td>
43//!     <td>~12MB/s @ 3.7GHz</td>
44//!   </tr>
45//!   <tr>
46//!     <td>Intel Skylake</td>
47//!     <td>460</td>
48//!     <td>460</td>
49//!     <td>460</td>
50//!     <td>~72MB/s @ 4.2GHz</td>
51//!   </tr>
52//!   <tr>
53//!     <td>Intel Haswell</td>
54//!     <td>320</td>
55//!     <td>320</td>
56//!     <td>320</td>
57//!     <td>~110MB/s @ 4.4GHz</td>
58//!   </tr>
59//! </table>
60//!
61//! [Agner’s instruction tables]: http://agner.org/optimize/
62#![no_std]
63pub mod changelog;
64mod errors;
65
66use core::hint::spin_loop;
67pub use errors::ErrorCode;
68use rand_core::{TryCryptoRng, TryRng};
69
70/// A cryptographically secure statistically uniform, non-periodic and non-deterministic random bit
71/// generator.
72///
73/// Note that this generator may be implemented using a deterministic algorithm that is reseeded
74/// routinely from a non-deterministic entropy source to achieve the desirable properties.
75///
76/// This generator is a viable replacement to any generator, however, since nobody has audited
77/// this hardware implementation yet, the usual disclaimers as to their suitability apply.
78///
79/// It is potentially faster than `OsRng`, but is only supported by more recent architectures such
80/// as Intel Ivy Bridge and AMD Zen.
81#[derive(Clone, Copy)]
82pub struct RdRand(());
83
84/// A cryptographically secure non-deterministic random bit generator.
85///
86/// This generator produces high-entropy output and is suited to seed other pseudo-random
87/// generators.
88///
89/// This instruction is only supported by recent architectures such as Intel Broadwell, AMD Zen,
90/// and as an optional feature on AArch64 Armv8.1 and newer.
91///
92/// This generator is not intended for general random number generation purposes and should be used
93/// to seed other generators implementing [rand_core::SeedableRng].
94#[derive(Clone, Copy)]
95pub struct RdSeed(());
96
97impl TryCryptoRng for RdRand {}
98impl TryCryptoRng for RdSeed {}
99
100mod arch {
101    #[cfg(target_arch = "x86")]
102    pub use core::arch::x86::*;
103    #[cfg(target_arch = "x86_64")]
104    pub use core::arch::x86_64::*;
105
106    #[cfg(target_arch = "x86")]
107    pub(crate) unsafe fn _rdrand64_step(dest: &mut u64) -> i32 {
108        let mut ret1: u32 = 0;
109        let mut ret2: u32 = 0;
110        let ok = _rdrand32_step(&mut ret1) & _rdrand32_step(&mut ret2);
111        *dest = (ret1 as u64) << 32 | (ret2 as u64);
112        ok
113    }
114
115    #[cfg(target_arch = "x86")]
116    pub(crate) unsafe fn _rdseed64_step(dest: &mut u64) -> i32 {
117        let mut ret1: u32 = 0;
118        let mut ret2: u32 = 0;
119        let ok = _rdseed32_step(&mut ret1) & _rdseed32_step(&mut ret2);
120        *dest = (ret1 as u64) << 32 | (ret2 as u64);
121        ok
122    }
123
124    #[cfg(target_arch = "aarch64")]
125    pub(crate) unsafe fn rand(out: &mut u64) -> i32 {
126        let value: u64;
127        let success: u64;
128
129        unsafe {
130            core::arch::asm!(
131                "mrs {0}, S3_3_C2_C4_0", // RNDR
132                "cset {1:w}, cs",  // Set w{1} to 1 if carry flag is set, else 0
133                out(reg) value,
134                lateout(reg) success,
135                options(nostack)
136            );
137        }
138        *out = value;
139        // From ARM spec:
140        // If the hardware returns a genuine random number, PSTATE.NZCV is set to 0b0000.
141        //
142        // If the instruction cannot return a genuine random number in a reasonable period of
143        // time, PSTATE.NZCV is set to 0b0100 and the data value returned is 0.
144        // So the assembly code returns 0 for success and nonzero for failure, but loop_rand expects
145        // the opposite.
146        (success == 0) as i32 // Returns 1 for success, 0 for failure
147    }
148
149    #[cfg(target_arch = "aarch64")]
150    pub(crate) unsafe fn rand32(out: &mut u32) -> i32 {
151        let mut out64 = 0u64;
152        let status = unsafe { rand(&mut out64) };
153        *out = out64 as u32;
154        status
155    }
156
157    #[cfg(target_arch = "aarch64")]
158    pub(crate) unsafe fn seed(out: &mut u64) -> i32 {
159        let value: u64;
160        let success: u64;
161
162        unsafe {
163            core::arch::asm!(
164                "mrs {0}, S3_3_C2_C4_1", // RNDRRS
165                "cset {1:w}, cs",  // Set w{1} to 1 if carry flag is set, else 0
166                out(reg) value,
167                lateout(reg) success,
168                options(nostack)
169            );
170        }
171
172        *out = value;
173        (success == 0) as i32 // See rand() above for note on the inverted status.
174    }
175
176    #[cfg(target_arch = "aarch64")]
177    pub(crate) unsafe fn seed32(out: &mut u32) -> i32 {
178        let mut out64 = 0u64;
179        let status = unsafe { seed(&mut out64) };
180        *out = out64 as u32;
181        status
182    }
183}
184
185// See the following documentation for usage (in particular wrt retries) recommendations:
186//
187// https://software.intel.com/content/www/us/en/develop/articles/intel-digital-random-number-generator-drng-software-implementation-guide.html
188macro_rules! loop_rand {
189    ("rdrand", $el: ty, $step: path) => {{
190        let mut idx = 0;
191        #[allow(unused_unsafe)]
192        loop {
193            let mut el: $el = 0;
194            if unsafe { $step(&mut el) } != 0 {
195                break Ok(el);
196            } else if idx == 10 {
197                break Err(ErrorCode::HardwareFailure);
198            }
199            idx += 1;
200        }
201    }};
202    ("rdseed", $el: ty, $step: path) => {{
203        let mut idx = 0;
204        #[allow(unused_unsafe)]
205        loop {
206            let mut el: $el = 0;
207            if unsafe { $step(&mut el) } != 0 {
208                break Ok(el);
209            } else if idx == 127 {
210                break Err(ErrorCode::HardwareFailure);
211            }
212            idx += 1;
213            spin_loop();
214        }
215    }};
216}
217
218#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
219#[allow(unused_unsafe)]
220#[inline(always)]
221fn authentic_amd() -> bool {
222    let cpuid0 = unsafe { arch::__cpuid(0) };
223    matches!(
224        (cpuid0.ebx, cpuid0.ecx, cpuid0.edx),
225        (0x68747541, 0x444D4163, 0x69746E65)
226    )
227}
228
229#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
230#[inline(always)]
231fn amd_family(cpuid1: &arch::CpuidResult) -> u32 {
232    ((cpuid1.eax >> 8) & 0xF) + ((cpuid1.eax >> 20) & 0xFF)
233}
234
235#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
236#[inline(always)]
237fn has_rdrand(cpuid1: &arch::CpuidResult) -> bool {
238    const FLAG: u32 = 1 << 30;
239    cpuid1.ecx & FLAG == FLAG
240}
241
242#[cfg(target_arch = "aarch64")]
243#[inline(always)]
244fn has_rand() -> bool {
245    #[cfg(target_os = "windows")]
246    {
247        // On Windows, use IsProcessorFeaturePresent
248        use core::ffi::c_int;
249        const PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE: c_int = 33;
250        unsafe extern "C" {
251            fn IsProcessorFeaturePresent(feature: c_int) -> i32;
252        }
253        // RNDR requires at least ARMv8.1, so check for atomic instructions
254        // as a minimum.
255        // FIXME: May falsely report available on rare hardware. Ideally Windows would have
256        // a check specifically for RNDR.
257        unsafe { IsProcessorFeaturePresent(PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE) != 0 }
258    }
259    #[cfg(any(target_os = "macos"))]
260    {
261        let mut value: u32 = 0;
262        let mut size = core::mem::size_of::<u32>();
263        let name = b"hw.optional.arm.FEAT_RNG\0";
264        unsafe extern "C" {
265            fn sysctlbyname(
266                name: *const u8,
267                oldp: *mut u32,
268                oldlenp: *mut usize,
269                newp: *const core::ffi::c_void,
270                newlen: usize,
271            ) -> core::ffi::c_int;
272        }
273        unsafe {
274            sysctlbyname(name.as_ptr(), &mut value, &mut size, core::ptr::null(), 0) == 0
275                && value != 0
276        }
277    }
278    #[cfg(any(
279        target_os = "linux",
280        target_os = "android",
281        target_os = "freebsd",
282        target_os = "netbsd",
283        target_os = "none",
284        target_os = "uefi"))]
285    {
286        let value: u64;
287        unsafe {
288            // MRS is a privileged instruction (EL1),
289            // but it's emulated on Linux, FreeBSD and NetBSD.
290            core::arch::asm!(
291                "mrs {0}, ID_AA64ISAR0_EL1", // feature register
292                out(reg) value,
293                options(nostack)
294            );
295        }
296        (value & 0xF000_0000_0000_0000) != 0
297    }
298    #[cfg(not(any(
299        target_os = "linux",
300        target_os = "android",
301        target_os = "windows",
302        target_os = "macos",
303        target_os = "freebsd",
304        target_os = "netbsd",
305        target_os = "uefi",
306        target_os = "none"
307    )))]
308    {
309        #[cfg(feature = "std")]
310        {
311            extern crate std;
312            std::arch::is_aarch64_feature_detected!("rand")
313        }
314
315        #[cfg(not(feature = "std"))]
316        {
317            #[cfg(target_os = "openbsd")]
318            {
319                // FIXME: Not tested.
320                const CTL_MACHDEP: c_int = 7;
321                const CPU_ID_AA64ISAR0: c_int = 2;
322
323                let mib = [CTL_MACHDEP, CPU_ID_AA64ISAR0];
324                let mut isar0: u64 = 0;
325                let mut len = core::mem::size_of_val(&isar0);
326
327                let result = unsafe {
328                    libc::sysctl(
329                        mib.as_ptr(),
330                        mib.len() as u32,
331                        &mut isar0 as *mut _ as *mut c_void,
332                        &mut len,
333                        core::ptr::null_mut(),
334                        0,
335                    )
336                };
337
338                if result == 0 {
339                    // Extract the RND field (bits 60-63)
340                    ((isar0 >> 60) & 0xF) >= 1
341                } else {
342                    false
343                }
344            }
345            #[cfg(not(target_os = "openbsd"))] {
346                // When we can't detect the feature, assume it's unavailable unless compiling with
347                // `-Ctarget-feature=+rand` (in which case `has_rand` is bypassed altogether).
348                // FIXME: Detection on iOS should be possible, but no known method is future-proof.
349                false
350            }
351        }
352    }
353}
354
355#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
356#[allow(unused_unsafe)]
357#[inline(always)]
358fn has_rdseed() -> bool {
359    const FLAG: u32 = 1 << 18;
360    (unsafe { arch::__cpuid(7) }.ebx & FLAG) == FLAG
361}
362
363/// NB: On AMD processor families < 0x17, we want to unconditionally disable RDRAND
364/// and RDSEED. Executing these instructions on these processors can return
365/// non-random data (0) while also reporting a success.
366///
367/// See:
368/// * https://github.com/systemd/systemd/issues/11810
369/// * https://lore.kernel.org/all/776cb5c2d33e7fd0d2893904724c0e52b394f24a.1565817448.git.thomas.lendacky@amd.com/
370///
371/// We take extra care to do so even if `-Ctarget-features=+rdrand` have been
372/// specified, in order to prevent users from shooting themselves in their feet.
373#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
374const FIRST_GOOD_AMD_FAMILY: u32 = 0x17;
375
376macro_rules! is_available {
377    ("rdrand") => {{
378        #[allow(unused_unsafe)]
379        if authentic_amd() {
380            let cpuid1 = unsafe { arch::__cpuid(1) };
381            has_rdrand(&cpuid1) && amd_family(&cpuid1) >= FIRST_GOOD_AMD_FAMILY
382        } else {
383            cfg!(target_feature = "rdrand") || has_rdrand(&unsafe { arch::__cpuid(1) })
384        }
385    }};
386    ("rand") => {{
387        #[cfg(target_arch = "aarch64")]
388        {
389            cfg!(target_feature = "rand") || has_rand()
390        }
391        #[cfg(not(target_arch = "aarch64"))]
392        {
393            unreachable!()
394        }
395    }};
396    ("rdseed") => {{
397        #[allow(unused_unsafe)]
398        if authentic_amd() {
399            amd_family(&unsafe { arch::__cpuid(1) }) >= FIRST_GOOD_AMD_FAMILY && has_rdseed()
400        } else {
401            cfg!(target_feature = "rdrand") || has_rdseed()
402        }
403    }};
404}
405
406macro_rules! impl_rand {
407    ($gen:ident, $feat:tt, $loop_mode:tt, $step32:path, $step64:path,
408     maxstep = $maxstep:path, maxty = $maxty: ty) => {
409        impl $gen {
410            /// Create a new instance of the random number generator.
411            ///
412            /// This constructor checks whether the CPU the program is running on supports the
413            /// instruction necessary for this generator to operate. If the instruction is not
414            /// supported, an error is returned.
415            pub fn new() -> Result<Self, ErrorCode> {
416                if cfg!(target_env = "sgx") {
417                    if cfg!(target_feature = $feat) {
418                        Ok($gen(()))
419                    } else {
420                        Err(ErrorCode::UnsupportedInstruction)
421                    }
422                } else if is_available!($feat) {
423                    Ok($gen(()))
424                } else {
425                    Err(ErrorCode::UnsupportedInstruction)
426                }
427            }
428
429            /// Create a new instance of the random number generator.
430            ///
431            /// # Safety
432            ///
433            /// This constructor is unsafe because it doesn't check that the CPU supports the
434            /// instruction, but devolves this responsibility to the caller.
435            pub unsafe fn new_unchecked() -> Self {
436                $gen(())
437            }
438        }
439        impl TryRng for $gen {
440            type Error = ErrorCode;
441            /// Generate a single random `u32` value.
442            ///
443            /// The underlying instruction may fail for variety reasons (such as actual hardware
444            /// failure or exhausted entropy), however the exact reason for the failure is not
445            /// usually exposed.
446            ///
447            /// This method will retry calling the instruction a few times, however if all the
448            /// attempts fail, it will return `None`.
449            ///
450            /// In case `Err` is returned, the caller should assume that a non-recoverable failure
451            /// has occured and use another random number genrator instead.
452            #[inline(always)]
453            #[allow(unused_unsafe)]
454            fn try_next_u32(&mut self) -> Result<u32, ErrorCode> {
455                #[target_feature(enable = $feat)]
456                #[allow(unused_unsafe)]
457                unsafe fn imp() -> Result<u32, ErrorCode> {
458                    loop_rand!($loop_mode, u32, $step32)
459                }
460                unsafe { imp() }
461            }
462
463            /// Generate a single random `u64` value.
464            ///
465            /// The underlying instruction may fail for variety reasons (such as actual hardware
466            /// failure or exhausted entropy), however the exact reason for the failure is not
467            /// usually exposed.
468            ///
469            /// This method will retry calling the instruction a few times, however if all the
470            /// attempts fail, it will return `None`.
471            ///
472            /// In case `Err` is returned, the caller should assume that a non-recoverable failure
473            /// has occured and use another random number genrator instead.
474            ///
475            /// Note, that on 32-bit targets, there’s no underlying instruction to generate a
476            /// 64-bit number, so it is emulated with the 32-bit version of the instruction.
477            #[inline(always)]
478            fn try_next_u64(&mut self) -> Result<u64, ErrorCode> {
479                #[target_feature(enable = $feat)]
480                #[allow(unused_unsafe)]
481                unsafe fn imp() -> Result<u64, ErrorCode> {
482                    loop_rand!($loop_mode, u64, $step64)
483                }
484                unsafe { imp() }
485            }
486
487            /// Fill a buffer `dest` with random data.
488            ///
489            /// This method will use the most appropriate variant of the instruction available on
490            /// the machine to achieve the greatest single-core throughput, however it has a
491            /// slightly higher setup cost than the plain `next_u32` or `next_u64` methods.
492            ///
493            /// The underlying instruction may fail for variety reasons (such as actual hardware
494            /// failure or exhausted entropy), however the exact reason for the failure is not
495            /// usually exposed.
496            ///
497            /// This method will retry calling the instruction a few times, however if all the
498            /// attempts fail, it will return an error.
499            ///
500            /// If an error is returned, the caller should assume that an non-recoverable hardware
501            /// failure has occured and use another random number genrator instead.
502            #[inline(always)]
503            fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ErrorCode> {
504                #[target_feature(enable = $feat)]
505                #[allow(unused_unsafe)]
506                unsafe fn imp(dest: &mut [u8]) -> Result<(), ErrorCode> {
507                    fn slow_fill_bytes<'a>(
508                        mut left: &'a mut [u8],
509                        mut right: &'a mut [u8],
510                    ) -> Result<(), ErrorCode> {
511                        let mut word;
512                        let mut buffer: &[u8] = &[];
513                        loop {
514                            if left.is_empty() {
515                                if right.is_empty() {
516                                    break;
517                                }
518                                ::core::mem::swap(&mut left, &mut right);
519                            }
520                            #[allow(unused_unsafe)]
521                            if buffer.is_empty() {
522                                word = unsafe { loop_rand!($loop_mode, $maxty, $maxstep) }?
523                                    .to_ne_bytes();
524                                buffer = &word[..];
525                            }
526                            let len = left.len().min(buffer.len());
527                            let (copy_src, leftover) = buffer.split_at(len);
528                            let (copy_dest, dest_leftover) = { left }.split_at_mut(len);
529                            buffer = leftover;
530                            left = dest_leftover;
531                            copy_dest.copy_from_slice(copy_src);
532                        }
533                        Ok(())
534                    }
535
536                    let destlen = dest.len();
537                    if destlen > ::core::mem::size_of::<$maxty>() {
538                        let (left, mid, right) = unsafe { dest.align_to_mut() };
539                        for el in mid {
540                            *el = loop_rand!($loop_mode, $maxty, $maxstep)?;
541                        }
542
543                        slow_fill_bytes(left, right)
544                    } else {
545                        slow_fill_bytes(dest, &mut [])
546                    }
547                }
548                unsafe { imp(dest) }
549            }
550        }
551    };
552}
553
554#[cfg(target_arch = "x86_64")]
555impl_rand!(
556    RdRand,
557    "rdrand",
558    "rdrand",
559    arch::_rdrand32_step,
560    arch::_rdrand64_step,
561    maxstep = arch::_rdrand64_step,
562    maxty = u64
563);
564#[cfg(target_arch = "x86_64")]
565impl_rand!(
566    RdSeed,
567    "rdseed",
568    "rdseed",
569    arch::_rdseed32_step,
570    arch::_rdseed64_step,
571    maxstep = arch::_rdseed64_step,
572    maxty = u64
573);
574#[cfg(target_arch = "x86")]
575impl_rand!(
576    RdRand,
577    "rdrand",
578    "rdrand",
579    arch::_rdrand32_step,
580    arch::_rdrand64_step,
581    maxstep = arch::_rdrand32_step,
582    maxty = u32
583);
584#[cfg(target_arch = "x86")]
585impl_rand!(
586    RdSeed,
587    "rdseed",
588    "rdseed",
589    arch::_rdseed32_step,
590    arch::_rdseed64_step,
591    maxstep = arch::_rdseed32_step,
592    maxty = u32
593);
594#[cfg(target_arch = "aarch64")]
595impl_rand!(
596    RdRand,
597    "rand",
598    "rdrand",
599    arch::rand32,
600    arch::rand,
601    maxstep = arch::rand,
602    maxty = u64
603);
604#[cfg(target_arch = "aarch64")]
605impl_rand!(
606    RdSeed,
607    "rand",
608    "rdseed",
609    arch::seed32,
610    arch::seed,
611    maxstep = arch::seed,
612    maxty = u64
613);
614
615#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
616impl RdRand {
617    fn new() -> Result<Self, ErrorCode> {
618        Err(ErrorCode::UnsupportedInstruction)
619    }
620}
621
622#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
623impl TryRng for RdRand {
624    type Error = ErrorCode;
625    fn try_next_u32(&mut self) -> Result<u32, ErrorCode> {
626        Err(ErrorCode::UnsupportedInstruction)
627    }
628    fn try_next_u64(&mut self) -> Result<u64, ErrorCode> {
629        Err(ErrorCode::UnsupportedInstruction)
630    }
631    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ErrorCode> {
632        Err(ErrorCode::UnsupportedInstruction)
633    }
634}
635
636#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
637impl RdSeed {
638    fn new() -> Result<Self, ErrorCode> {
639        Err(ErrorCode::UnsupportedInstruction)
640    }
641}
642
643#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
644impl TryRng for RdSeed {
645    type Error = ErrorCode;
646    fn try_next_u32(&mut self) -> Result<u32, ErrorCode> {
647        Err(ErrorCode::UnsupportedInstruction)
648    }
649    fn try_next_u64(&mut self) -> Result<u64, ErrorCode> {
650        Err(ErrorCode::UnsupportedInstruction)
651    }
652    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), ErrorCode> {
653        Err(ErrorCode::UnsupportedInstruction)
654    }
655}
656
657#[cfg(test)]
658mod test {
659    use super::{RdRand, RdSeed};
660    use rand_core::{Rng, TryRng, UnwrapErr};
661
662    #[test]
663    fn rdrand_works() {
664        extern crate std;
665        use std::eprintln;
666        eprintln!("Checking RdRand::new()...");
667        match RdRand::new() {
668            Ok(mut r) => {
669                eprintln!("RdRand created successfully, calling try_next_u32()");
670                match r.try_next_u32() {
671                    Ok(val) => eprintln!("Got random value: {}", val),
672                    Err(e) => eprintln!("try_next_u32 failed: {:?}", e),
673                }
674            }
675            Err(e) => {
676                eprintln!("RdRand::new() failed with: {:?}", e);
677                eprintln!("This is expected on CPUs without RDRAND support");
678            }
679        }
680    }
681
682    #[repr(C, align(8))]
683    struct FillBuffer([u8; 64]);
684
685    #[test]
686    fn fill_fills_all_bytes() {
687        let _status = RdRand::new().map(|r| {
688            let mut r = UnwrapErr(r);
689            let mut test_buffer;
690            let mut fill_buffer = FillBuffer([0; 64]); // make sure buffer is aligned to 8-bytes...
691            let test_cases = [
692                (0, 64), // well aligned
693                (8, 64), // well aligned
694                (0, 64), // well aligned
695                (5, 64), // left is non-empty, right is empty.
696                (0, 63), // left is empty, right is non-empty.
697                (5, 63), // left and right both are non-empty.
698                (5, 61), // left and right both are non-empty.
699                (0, 8),  // 1 word-worth of data, aligned.
700                (1, 9),  // 1 word-worth of data, misaligned.
701                (0, 7),  // less than 1 word of data.
702                (1, 7),  // less than 1 word of data.
703            ];
704            'outer: for &(start, end) in &test_cases {
705                test_buffer = [0; 64];
706                for _ in 0..512 {
707                    fill_buffer.0 = [0; 64];
708                    r.fill_bytes(&mut fill_buffer.0[start..end]);
709                    for (b, p) in test_buffer.iter_mut().zip(fill_buffer.0.iter()) {
710                        *b = *b | *p;
711                    }
712                    if (&test_buffer[start..end]).iter().all(|x| *x != 0) {
713                        assert!(
714                            test_buffer[..start].iter().all(|x| *x == 0),
715                            "all other values must be 0"
716                        );
717                        assert!(
718                            test_buffer[end..].iter().all(|x| *x == 0),
719                            "all other values must be 0"
720                        );
721                        continue 'outer;
722                    }
723                }
724                panic!("wow, we broke it? {} {} {:?}", start, end, &test_buffer[..])
725            }
726        });
727        #[cfg(any(
728            all(target_feature = "rand", target_arch = "aarch64"),
729            target_arch = "x86_64",
730            target_arch = "x86"
731        ))]
732        _status.unwrap();
733    }
734
735    #[test]
736    fn rdseed_works() {
737        let _status = RdSeed::new().map(|mut r| {
738            r.try_next_u32().unwrap();
739            r.try_next_u64().unwrap();
740        });
741        #[cfg(any(
742            all(target_feature = "rand", target_arch = "aarch64"),
743            target_arch = "x86_64",
744            target_arch = "x86"
745        ))]
746        _status.unwrap();
747    }
748}