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
157impl sealed::SecureRandom for SystemRandom {
158 #[inline(always)]
159 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
160 fill_impl(dest)
161 }
162}
163
164impl crate::sealed::Sealed for SystemRandom {}
165
166#[cfg(any(
167 all(
168 any(target_os = "android", target_os = "linux"),
169 not(feature = "dev_urandom_fallback")
170 ),
171 target_arch = "wasm32",
172 windows
173))]
174use self::sysrand::fill as fill_impl;
175
176#[cfg(all(
177 any(target_os = "android", target_os = "linux"),
178 feature = "dev_urandom_fallback"
179))]
180use self::sysrand_or_urandom::fill as fill_impl;
181
182#[cfg(any(
183 target_os = "dragonfly",
184 target_os = "freebsd",
185 target_os = "illumos",
186 target_os = "netbsd",
187 target_os = "openbsd",
188 target_os = "solaris",
189))]
190use self::urandom::fill as fill_impl;
191
192#[cfg(any(target_os = "macos", target_os = "ios"))]
193use self::darwin::fill as fill_impl;
194
195#[cfg(any(target_os = "fuchsia"))]
196use self::fuchsia::fill as fill_impl;
197
198#[cfg(any(target_os = "android", target_os = "linux"))]
199mod sysrand_chunk {
200 use crate::{c, error};
201
202 #[inline]
203 pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
204 use libc::c_long;
205
206 #[cfg(target_arch = "aarch64")]
209 const SYS_GETRANDOM: c_long = 278;
210
211 #[cfg(target_arch = "arm")]
212 const SYS_GETRANDOM: c_long = 384;
213
214 #[cfg(target_arch = "x86")]
215 const SYS_GETRANDOM: c_long = 355;
216
217 #[cfg(target_arch = "x86_64")]
218 const SYS_GETRANDOM: c_long = 318;
219
220 let chunk_len: c::size_t = dest.len();
221 let r = unsafe { libc::syscall(SYS_GETRANDOM, dest.as_mut_ptr(), chunk_len, 0) };
222 if r < 0 {
223 let errno;
224
225 #[cfg(target_os = "linux")]
226 {
227 errno = unsafe { *libc::__errno_location() };
228 }
229
230 #[cfg(target_os = "android")]
231 {
232 errno = unsafe { *libc::__errno() };
233 }
234
235 if errno == libc::EINTR {
236 return Ok(0);
240 }
241 return Err(error::Unspecified);
242 }
243 Ok(r as usize)
244 }
245}
246
247#[cfg(all(
248 target_arch = "wasm32",
249 target_vendor = "unknown",
250 target_os = "unknown",
251 target_env = "",
252))]
253mod sysrand_chunk {
254 use crate::error;
255
256 pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
257 const MAX_LEN: usize = 65_536;
260 if dest.len() > MAX_LEN {
261 dest = &mut dest[..MAX_LEN];
262 };
263
264 let _ = web_sys::window()
265 .ok_or(error::Unspecified)?
266 .crypto()
267 .map_err(|_| error::Unspecified)?
268 .get_random_values_with_u8_array(dest)
269 .map_err(|_| error::Unspecified)?;
270
271 Ok(dest.len())
272 }
273}
274
275#[cfg(windows)]
276mod sysrand_chunk {
277 use crate::{error, polyfill};
278
279 #[inline]
280 pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
281 use winapi::shared::wtypesbase::ULONG;
282
283 assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
284 let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
285 let result = unsafe {
286 winapi::um::ntsecapi::RtlGenRandom(
287 dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
288 len as ULONG,
289 )
290 };
291 if result == 0 {
292 return Err(error::Unspecified);
293 }
294
295 Ok(len)
296 }
297}
298
299#[cfg(any(
300 target_os = "android",
301 target_os = "linux",
302 target_arch = "wasm32",
303 windows
304))]
305mod sysrand {
306 use super::sysrand_chunk::chunk;
307 use crate::error;
308
309 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
310 let mut read_len = 0;
311 while read_len < dest.len() {
312 let chunk_len = chunk(&mut dest[read_len..])?;
313 read_len += chunk_len;
314 }
315 Ok(())
316 }
317}
318
319#[cfg(all(
321 any(target_os = "android", target_os = "linux"),
322 feature = "dev_urandom_fallback"
323))]
324mod sysrand_or_urandom {
325 use crate::error;
326
327 enum Mechanism {
328 Sysrand,
329 DevURandom,
330 }
331
332 #[inline]
333 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
334 use once_cell::sync::Lazy;
335 static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
336 let mut dummy = [0u8; 1];
337 if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
338 Mechanism::DevURandom
339 } else {
340 Mechanism::Sysrand
341 }
342 });
343
344 match *MECHANISM {
345 Mechanism::Sysrand => super::sysrand::fill(dest),
346 Mechanism::DevURandom => super::urandom::fill(dest),
347 }
348 }
349}
350
351#[cfg(any(
352 all(
353 any(target_os = "android", target_os = "linux"),
354 feature = "dev_urandom_fallback"
355 ),
356 target_os = "dragonfly",
357 target_os = "freebsd",
358 target_os = "netbsd",
359 target_os = "openbsd",
360 target_os = "solaris",
361 target_os = "illumos"
362))]
363mod urandom {
364 use crate::error;
365
366 #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
367 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
368 extern crate std;
369
370 use once_cell::sync::Lazy;
371
372 static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
373 Lazy::new(|| std::fs::File::open("/dev/urandom"));
374
375 match *FILE {
376 Ok(ref file) => {
377 use std::io::Read;
378 (&*file).read_exact(dest).map_err(|_| error::Unspecified)
379 }
380 Err(_) => Err(error::Unspecified),
381 }
382 }
383}
384
385#[cfg(any(target_os = "macos", target_os = "ios"))]
386mod darwin {
387 use crate::{c, error};
388
389 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
390 let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
391 match r {
392 0 => Ok(()),
393 _ => Err(error::Unspecified),
394 }
395 }
396
397 #[repr(C)]
402 struct SecRandomRef([u8; 0]);
403
404 #[link(name = "Security", kind = "framework")]
405 extern "C" {
406 static kSecRandomDefault: &'static SecRandomRef;
407
408 #[must_use]
410 fn SecRandomCopyBytes(
411 rnd: &'static SecRandomRef,
412 count: c::size_t,
413 bytes: *mut u8,
414 ) -> c::int;
415 }
416}
417
418#[cfg(any(target_os = "fuchsia"))]
419mod fuchsia {
420 use crate::error;
421
422 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
423 unsafe {
424 zx_cprng_draw(dest.as_mut_ptr(), dest.len());
425 }
426 Ok(())
427 }
428
429 #[link(name = "zircon")]
430 extern "C" {
431 fn zx_cprng_draw(buffer: *mut u8, length: usize);
432 }
433}