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}