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 feature = "wasm32_unknown_unknown_js",
249 target_arch = "wasm32",
250 target_vendor = "unknown",
251 target_os = "unknown",
252 target_env = "",
253))]
254mod sysrand_chunk {
255 use crate::error;
256
257 pub fn chunk(mut dest: &mut [u8]) -> Result<usize, error::Unspecified> {
258 const MAX_LEN: usize = 65_536;
261 if dest.len() > MAX_LEN {
262 dest = &mut dest[..MAX_LEN];
263 };
264
265 let _ = web_sys::window()
266 .ok_or(error::Unspecified)?
267 .crypto()
268 .map_err(|_| error::Unspecified)?
269 .get_random_values_with_u8_array(dest)
270 .map_err(|_| error::Unspecified)?;
271
272 Ok(dest.len())
273 }
274}
275
276#[cfg(windows)]
277mod sysrand_chunk {
278 use crate::{error, polyfill};
279
280 #[inline]
281 pub fn chunk(dest: &mut [u8]) -> Result<usize, error::Unspecified> {
282 use winapi::shared::wtypesbase::ULONG;
283
284 assert!(core::mem::size_of::<usize>() >= core::mem::size_of::<ULONG>());
285 let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
286 let result = unsafe {
287 winapi::um::ntsecapi::RtlGenRandom(
288 dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
289 len as ULONG,
290 )
291 };
292 if result == 0 {
293 return Err(error::Unspecified);
294 }
295
296 Ok(len)
297 }
298}
299
300#[cfg(any(
301 target_os = "android",
302 target_os = "linux",
303 target_arch = "wasm32",
304 windows
305))]
306mod sysrand {
307 use super::sysrand_chunk::chunk;
308 use crate::error;
309
310 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
311 let mut read_len = 0;
312 while read_len < dest.len() {
313 let chunk_len = chunk(&mut dest[read_len..])?;
314 read_len += chunk_len;
315 }
316 Ok(())
317 }
318}
319
320#[cfg(all(
322 any(target_os = "android", target_os = "linux"),
323 feature = "dev_urandom_fallback"
324))]
325mod sysrand_or_urandom {
326 use crate::error;
327
328 enum Mechanism {
329 Sysrand,
330 DevURandom,
331 }
332
333 #[inline]
334 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
335 use once_cell::sync::Lazy;
336 static MECHANISM: Lazy<Mechanism> = Lazy::new(|| {
337 let mut dummy = [0u8; 1];
338 if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
339 Mechanism::DevURandom
340 } else {
341 Mechanism::Sysrand
342 }
343 });
344
345 match *MECHANISM {
346 Mechanism::Sysrand => super::sysrand::fill(dest),
347 Mechanism::DevURandom => super::urandom::fill(dest),
348 }
349 }
350}
351
352#[cfg(any(
353 all(
354 any(target_os = "android", target_os = "linux"),
355 feature = "dev_urandom_fallback"
356 ),
357 target_os = "dragonfly",
358 target_os = "freebsd",
359 target_os = "netbsd",
360 target_os = "openbsd",
361 target_os = "solaris",
362 target_os = "illumos"
363))]
364mod urandom {
365 use crate::error;
366
367 #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
368 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
369 extern crate std;
370
371 use once_cell::sync::Lazy;
372
373 static FILE: Lazy<Result<std::fs::File, std::io::Error>> =
374 Lazy::new(|| std::fs::File::open("/dev/urandom"));
375
376 match *FILE {
377 Ok(ref file) => {
378 use std::io::Read;
379 (&*file).read_exact(dest).map_err(|_| error::Unspecified)
380 }
381 Err(_) => Err(error::Unspecified),
382 }
383 }
384}
385
386#[cfg(any(target_os = "macos", target_os = "ios"))]
387mod darwin {
388 use crate::{c, error};
389
390 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
391 let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
392 match r {
393 0 => Ok(()),
394 _ => Err(error::Unspecified),
395 }
396 }
397
398 #[repr(C)]
403 struct SecRandomRef([u8; 0]);
404
405 #[link(name = "Security", kind = "framework")]
406 extern "C" {
407 static kSecRandomDefault: &'static SecRandomRef;
408
409 #[must_use]
411 fn SecRandomCopyBytes(
412 rnd: &'static SecRandomRef,
413 count: c::size_t,
414 bytes: *mut u8,
415 ) -> c::int;
416 }
417}
418
419#[cfg(any(target_os = "fuchsia"))]
420mod fuchsia {
421 use crate::error;
422
423 pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
424 unsafe {
425 zx_cprng_draw(dest.as_mut_ptr(), dest.len());
426 }
427 Ok(())
428 }
429
430 #[link(name = "zircon")]
431 extern "C" {
432 fn zx_cprng_draw(buffer: *mut u8, length: usize);
433 }
434}