nanorand/
entropy.rs

1#[cfg(all(target_vendor = "apple", not(feature = "getrandom")))]
2pub use darwin::entropy as system;
3#[cfg(all(
4	any(target_os = "linux", target_os = "android"),
5	not(feature = "getrandom")
6))]
7pub use linux::entropy as system;
8#[cfg(all(windows, not(target_vendor = "uwp"), not(feature = "getrandom")))]
9pub use windows::entropy as system;
10#[cfg(all(windows, target_vendor = "uwp", not(feature = "getrandom")))]
11pub use windows_uwp::entropy as system;
12
13#[cfg(all(
14	any(target_os = "linux", target_os = "android"),
15	not(feature = "getrandom")
16))]
17/// An entropy generator for Linux, using libc's `getrandom` function.
18pub mod linux;
19
20#[cfg(all(target_vendor = "apple", not(feature = "getrandom")))]
21/// An entropy generator for macOS/iOS, using libc's `getrandom` function.
22pub mod darwin;
23
24#[cfg(all(windows, target_vendor = "uwp", not(feature = "getrandom")))]
25/// An entropy generator for Windows, using WinAPI's `BCryptGenRandom` function.
26pub mod windows_uwp;
27
28#[cfg(all(windows, not(target_vendor = "uwp"), not(feature = "getrandom")))]
29/// An entropy generator for Windows, using WinAPI's `RtlGenRandom` function.
30pub mod windows;
31
32#[cfg(feature = "getrandom")]
33/// Pull in system entropy using the [`getrandom`](https://crates.io/crates/getrandom) crate.
34/// Uses backup entropy (rdseed and system time) if it fails.
35pub fn system(out: &mut [u8]) {
36	match getrandom::getrandom(out) {
37		Ok(_) => (),
38		Err(_) => backup(out),
39	}
40}
41
42/// Pull in backup entropy (rdseed and system time).
43#[cfg(not(any(
44	feature = "getrandom",
45	target_os = "linux",
46	target_os = "android",
47	target_vendor = "apple",
48	windows
49)))]
50pub fn system(out: &mut [u8]) {
51	backup_entropy(out);
52}
53
54#[cfg(feature = "rdseed")]
55#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
56fn stupid_rdseed_hack() -> Option<u64> {
57	#[cfg(target_arch = "x86")]
58	use core::arch::x86::_rdseed64_step as rdseed;
59	#[cfg(target_arch = "x86_64")]
60	use core::arch::x86_64::_rdseed64_step as rdseed;
61	let mut x = 0;
62	for _ in 0..10 {
63		if 0 != unsafe { rdseed(&mut x) } {
64			return Some(x);
65		}
66	}
67	None
68}
69
70#[cfg(all(feature = "rdseed", any(target_arch = "x86", target_arch = "x86_64")))]
71/// An rdseed-based entropy source.
72/// Only works on x86/x86_64 platforms where the `rdseed` instructions are available.
73/// Returns [`None`] if `rdseed` is not available.
74/// Returns [`Some`] if it successfully managed to pull some bytes.
75/// ***VERY unreliable.***
76pub fn rdseed(out: &mut [u8]) -> Option<usize> {
77	if !std::is_x86_feature_detected!("rdseed") {
78		return None;
79	}
80	let amt = out.len();
81	let mut bytes_pulled: usize = 0;
82
83	let rdseed_amt = ((amt + core::mem::size_of::<u64>() - 1) / core::mem::size_of::<u64>()).max(0);
84	for n in 0..rdseed_amt {
85		let seed = match stupid_rdseed_hack() {
86			Some(s) => s,
87			None => return Some(bytes_pulled),
88		};
89		let x = seed.to_ne_bytes();
90		bytes_pulled += x.len();
91		x.iter()
92			.enumerate()
93			.for_each(|(i, val)| out[(core::mem::size_of::<u64>() * n) + i] = *val);
94	}
95	Some(bytes_pulled)
96}
97
98/// A wrapper function for non-x86(64) platforms that do not have rdseed.
99#[cfg(any(
100	not(feature = "rdseed"),
101	not(any(target_arch = "x86", target_arch = "x86_64"))
102))]
103pub fn rdseed(_out: &mut [u8]) -> Option<usize> {
104	None
105}
106
107#[cfg(feature = "std")]
108/// A backup entropy source, trying rdseed first,
109/// and if it fails or does not complete, combining it with or
110/// using system time-based entropy generation.
111///
112/// # Panics
113///
114/// This function panics if sufficient entropy could not be obtained.
115pub fn backup(out: &mut [u8]) {
116	if let Some(amt) = rdseed(out) {
117		if amt >= out.len() {
118			return;
119		}
120	};
121
122	panic!("Failed to source sufficient entropy!")
123}
124
125#[cfg(not(feature = "std"))]
126/// This just panics.
127pub fn backup_entropy(_: &mut [u8]) {
128	panic!("Failed to source any entropy!")
129}