1use crate::error;
29
30pub trait SecureRandom: sealed::SecureRandom {
32 fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
34}
35
36impl<T> SecureRandom for T
37where
38 T: sealed::SecureRandom,
39{
40 #[inline(always)]
41 fn fill(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
42 self.fill_impl(dest)
43 }
44}
45
46pub struct Random<T: RandomlyConstructable>(T);
51
52impl<T: RandomlyConstructable> Random<T> {
53 #[inline]
55 pub fn expose(self) -> T {
56 self.0
57 }
58}
59
60#[inline]
62pub fn generate<T: RandomlyConstructable>(
63 rng: &dyn SecureRandom,
64) -> Result<Random<T>, error::Unspecified>
65where
66 T: RandomlyConstructable,
67{
68 let mut r = T::zero();
69 rng.fill(r.as_mut_bytes())?;
70 Ok(Random(r))
71}
72
73pub(crate) mod sealed {
74 use crate::error;
75
76 pub trait SecureRandom: core::fmt::Debug {
77 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified>;
79 }
80
81 pub trait RandomlyConstructable: Sized {
82 fn zero() -> Self; fn as_mut_bytes(&mut self) -> &mut [u8]; }
85
86 macro_rules! impl_random_arrays {
87 [ $($len:expr)+ ] => {
88 $(
89 impl RandomlyConstructable for [u8; $len] {
90 #[inline]
91 fn zero() -> Self { [0; $len] }
92
93 #[inline]
94 fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self[..] }
95 }
96 )+
97 }
98 }
99
100 impl_random_arrays![4 8 16 32 48 64 128 256];
101}
102
103pub trait RandomlyConstructable: self::sealed::RandomlyConstructable {}
105impl<T> RandomlyConstructable for T where T: self::sealed::RandomlyConstructable {}
106
107#[derive(Clone, Debug)]
147pub struct SystemRandom(());
148
149impl SystemRandom {
150 #[inline(always)]
152 pub fn new() -> Self {
153 Self(())
154 }
155}
156
157#[cfg(any(target_os = "wasi", target_os = "wasix"))]
158impl sealed::SecureRandom for SystemRandom {
159 #[inline(always)]
160 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
161 getrandom::getrandom(dest).map_err(|_| error::Unspecified)
162 }
163}
164
165#[cfg(not(any(target_os = "wasi", target_os = "wasix")))]
166impl sealed::SecureRandom for SystemRandom {
167 #[inline(always)]
168 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
169 fill_impl(dest)
170 }
171}
172
173impl crate::sealed::Sealed for SystemRandom {}
174
175#[cfg(any(
176 all(
177 any(target_os = "android", target_os = "linux"),
178 not(feature = "dev_urandom_fallback")
179 ),
180 all(target_arch = "wasm32", target_os = "unknown"),
181 windows
182))]
183use self::sysrand::fill as fill_impl;
184
185#[cfg(all(
186 any(target_os = "android", target_os = "linux"),
187 feature = "dev_urandom_fallback"
188))]
189use self::sysrand_or_urandom::fill as fill_impl;
190
191#[cfg(any(
192 target_os = "dragonfly",
193 target_os = "freebsd",
194 target_os = "illumos",
195 target_os = "netbsd",
196 target_os = "openbsd",
197 target_os = "solaris",
198))]
199use self::urandom::fill as fill_impl;
200
201#[cfg(any(target_os = "macos", target_os = "ios"))]
202use self::darwin::fill as fill_impl;
203
204#[cfg(any(target_os = "fuchsia"))]
205use self::fuchsia::fill as fill_impl;
206
207#[cfg(any(target_os = "android", target_os = "linux"))]
208mod sysrand_chunk {
209 use crate::{c, error};
210
211 #[inline]
212 pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
213 use libc::c_long;
214
215 #[cfg(target_arch = "aarch64")]
218 const SYS_GETRANDOM: c_long = 278;
219
220 #[cfg(target_arch = "arm")]
221 const SYS_GETRANDOM: c_long = 384;
222
223 #[cfg(target_arch = "x86")]
224 const SYS_GETRANDOM: c_long = 355;
225
226 #[cfg(target_arch = "x86_64")]
227 const SYS_GETRANDOM: c_long = 318;
228
229 let chunk_len: c::size_t = dest.len();
230 let r = unsafe { libc::syscall(SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, 0) };
231 if r < 0 {
232 let errno;
233
234 #[cfg(target_os = "linux")]
235 {
236 errno = unsafe { *libc::__errno_location() };
237 }
238
239 #[cfg(target_os = "android")]
240 {
241 errno = unsafe { *libc::__errno() };
242 }
243
244 if errno == libc::EINTR {
245 return Ok(0);
249 }
250 return Err(error::Unspecified);
251 }
252 Ok(r as usize)
253 }
254}
255
256#[cfg(all(
257 target_arch = "wasm32",
258 target_vendor = "unknown",
259 target_os = "unknown",
260 target_env = "",
261))]
262mod sysrand_chunk {
263 use crate::error;
264
265 pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
266 const MAX_LEN: usize = 65_536;
269 if dest.len() > MAX_LEN {
270 dest = &mut dest[..MAX_LEN];
271 };
272
273 let _ = web_sys::window()
274 .ok_or(error::Unspecified)?
275 .crypto()
276 .map_err(|_| error::Unspecified)?
277 .get_random_values_with_u8_array(dest)
278 .map_err(|_| error::Unspecified)?;
279
280 Ok(dest.len())
281 }
282}
283
284#[cfg(windows)]
285mod sysrand_chunk {
286 use crate::{error, polyfill};
287
288 #[inline]
289 pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
290 use winapi::shared::wtypesbase::ULONG;
291
292 assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
293 let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
294 let result = unsafe {
295 winapi::um::ntsecapi::RtlGenRandom(
296 dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
297 len as ULONG,
298 )
299 };
300 if result == 0 {
301 return Err(error::Unspecified);
302 }
303
304 Ok(len)
305 }
306}
307
308#[cfg(any(
309 target_os = "android",
310 target_os = "linux",
311 all(target_arch = "wasm32", target_os = "unknown"),
312 windows
313))]
314mod sysrand {
315 use super::sysrand_chunk::chunk;
316 use crate::error;
317
318 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
319 let mut read_len = 0;
320 while read_len < dest.len() {
321 let chunk_len = chunk(&mut dest[read_len..])?;
322 read_len += chunk_len;
323 }
324 Ok(())
325 }
326}
327
328#[cfg(all(
330 any(target_os = "android", target_os = "linux"),
331 feature = "dev_urandom_fallback"
332))]
333mod sysrand_or_urandom {
334 use crate::error;
335
336 enum Mechanism {
337 Sysrand,
338 DevURandom,
339 }
340
341 #[inline]
342 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
343 use once_cell::sync::Lazy;
344 static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
345 let mut dummy = [0u8; 1];
346 if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
347 Mechanism::DevURandom
348 } else {
349 Mechanism::Sysrand
350 }
351 });
352
353 match *MECHANISM {
354 Mechanism::Sysrand => super::sysrand::fill(dest),
355 Mechanism::DevURandom => super::urandom::fill(dest),
356 }
357 }
358}
359
360#[cfg(any(
361 all(
362 any(target_os = "android", target_os = "linux"),
363 feature = "dev_urandom_fallback"
364 ),
365 target_os = "dragonfly",
366 target_os = "freebsd",
367 target_os = "netbsd",
368 target_os = "openbsd",
369 target_os = "solaris",
370 target_os = "illumos"
371))]
372mod urandom {
373 use crate::error;
374
375 #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
376 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
377 extern crate std;
378
379 use once_cell::sync::Lazy;
380
381 static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
382 Lazy::new(|| std::fs::File::open("/dev/urandom"));
383
384 match *FILE {
385 Ok(ref file) => {
386 use std::io::Read;
387 (&*file).read_exact(dest).map_err(|_| error::Unspecified)
388 }
389 Err(_) => Err(error::Unspecified),
390 }
391 }
392}
393
394#[cfg(any(target_os = "macos", target_os = "ios"))]
395mod darwin {
396 use crate::{c, error};
397
398 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
399 let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
400 match r {
401 0 => Ok(()),
402 _ => Err(error::Unspecified),
403 }
404 }
405
406 #[repr(C)]
411 struct SecRandomRef([u8; 0]);
412
413 #[link(name = "Security", kind = "framework")]
414 extern "C" {
415 static kSecRandomDefault: &'static SecRandomRef;
416
417 #[must_use]
419 fn SecRandomCopyBytes(
420 rnd: &'static SecRandomRef,
421 count: c::size_t,
422 bytes: *mut u8,
423 ) -> c::int;
424 }
425}
426
427#[cfg(any(target_os = "fuchsia"))]
428mod fuchsia {
429 use crate::error;
430
431 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
432 unsafe {
433 zx_cprng_draw(dest.as_mut_ptr(), dest.len());
434 }
435 Ok(())
436 }
437
438 #[link(name = "zircon")]
439 extern "C" {
440 fn zx_cprng_draw(buffer: *mut u8, length: usize);
441 }
442}