@@ -33,20 +33,22 @@ use rand_core::{CryptoRng, RngCore, Error, impls};
33
33
///
34
34
/// # Platform sources
35
35
///
36
- /// | OS | interface
37
- /// |-----------------|---------------------------------------------------------
38
- /// | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
39
- /// | Windows | [`RtlGenRandom`][3]
40
- /// | macOS, iOS | [`SecRandomCopyBytes`][4]
41
- /// | FreeBSD | [`kern.arandom`][5]
42
- /// | OpenBSD, Bitrig | [`getentropy`][6]
43
- /// | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once
44
- /// | Dragonfly BSD | [`/dev/random`][8]
45
- /// | Fuchsia OS | [`cprng_draw`][9]
46
- /// | Redox | [`rand:`][10]
47
- /// | CloudABI | [`random_get`][11]
48
- /// | Web browsers | [`Crypto.getRandomValues`][12] (see [Support for WebAssembly and ams.js][14])
49
- /// | Node.js | [`crypto.randomBytes`][13] (see [Support for WebAssembly and ams.js][14])
36
+ /// | OS | interface
37
+ /// |------------------|---------------------------------------------------------
38
+ /// | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
39
+ /// | Windows | [`RtlGenRandom`][3]
40
+ /// | macOS, iOS | [`SecRandomCopyBytes`][4]
41
+ /// | FreeBSD | [`kern.arandom`][5]
42
+ /// | OpenBSD, Bitrig | [`getentropy`][6]
43
+ /// | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once
44
+ /// | Dragonfly BSD | [`/dev/random`][8]
45
+ /// | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
46
+ /// | Fuchsia OS | [`cprng_draw`][11]
47
+ /// | Redox | [`rand:`][12]
48
+ /// | CloudABI | [`random_get`][13]
49
+ /// | Haiku | `/dev/random` (identical to `/dev/urandom`)
50
+ /// | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
51
+ /// | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
50
52
///
51
53
/// Rand doesn't have a blanket implementation for all Unix-like operating
52
54
/// systems that reads from `/dev/urandom`. This ensures all supported operating
@@ -100,12 +102,14 @@ use rand_core::{CryptoRng, RngCore, Error, impls};
100
102
/// [6]: https://man.openbsd.org/getentropy.2
101
103
/// [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
102
104
/// [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random§ion=4
103
- /// [9]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md
104
- /// [10]: https://github.com/redox-os/randd/blob/master/src/main.rs
105
- /// [11]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826
106
- /// [12]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
107
- /// [13]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
108
- /// [14]: #support-for-webassembly-and-amsjs
105
+ /// [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
106
+ /// [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
107
+ /// [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md
108
+ /// [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
109
+ /// [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826
110
+ /// [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
111
+ /// [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
112
+ /// [16]: #support-for-webassembly-and-amsjs
109
113
110
114
111
115
#[ derive( Clone ) ]
@@ -193,7 +197,8 @@ impl RngCore for OsRng {
193
197
// exhaustion of file descriptors.
194
198
#[ cfg( any( target_os = "linux" , target_os = "android" ,
195
199
target_os = "netbsd" , target_os = "dragonfly" ,
196
- target_os = "emscripten" , target_os = "redox" ) ) ]
200
+ target_os = "solaris" , target_os = "redox" ,
201
+ target_os = "haiku" , target_os = "emscripten" ) ) ]
197
202
mod unix {
198
203
extern crate libc;
199
204
use { Error , ErrorKind } ;
@@ -434,7 +439,7 @@ mod imp {
434
439
}
435
440
436
441
// Read from `/dev/random`
437
- #[ cfg( target_os = "dragonfly" ) ]
442
+ #[ cfg( any ( target_os = "dragonfly" , target_os = "haiku" ) ) ]
438
443
mod imp {
439
444
use Error ;
440
445
use super :: unix:: * ;
@@ -483,6 +488,127 @@ mod imp {
483
488
}
484
489
}
485
490
491
+ // Read from `/dev/random`, with chunks of limited size (1040 bytes).
492
+ // `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
493
+ // `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
494
+ // secure. We choose to read from `/dev/random`.
495
+ //
496
+ // Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
497
+ // compile on both Solaris and on OpenSolaris derivatives, that do not have the
498
+ // function, we do a direct syscall instead of calling a library function.
499
+ //
500
+ // We have no way to differentiate between Solaris, illumos, SmartOS, etc.
501
+ #[ cfg( target_os = "solaris" ) ]
502
+ mod imp {
503
+ extern crate libc;
504
+ use { Error , ErrorKind } ;
505
+ use std:: io;
506
+ use super :: unix:: * ;
507
+
508
+ #[ derive( Clone , Debug ) ]
509
+ pub struct OsRng ( OsRngMethod ) ;
510
+
511
+ #[ derive( Clone , Debug ) ]
512
+ enum OsRngMethod {
513
+ GetRandom ,
514
+ RandomDevice ,
515
+ }
516
+
517
+ impl OsRng {
518
+ pub fn new ( ) -> Result < OsRng , Error > {
519
+ if is_getrandom_available ( ) {
520
+ return Ok ( OsRng ( OsRngMethod :: GetRandom ) ) ;
521
+ }
522
+
523
+ open_random_device ( "/dev/random" , false ) ?;
524
+ Ok ( OsRng ( OsRngMethod :: RandomDevice ) )
525
+ }
526
+
527
+ pub fn try_fill_bytes ( & mut self , dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
528
+ for slice in dest. chunks_mut ( 1040 ) {
529
+ match self . 0 {
530
+ OsRngMethod :: GetRandom => getrandom_try_fill ( slice) ?,
531
+ OsRngMethod :: RandomDevice => read_random_device ( slice) ?,
532
+ }
533
+ }
534
+ Ok ( ( ) )
535
+ }
536
+ }
537
+
538
+ fn getrandom ( buf : & mut [ u8 ] ) -> libc:: c_long {
539
+ extern "C" {
540
+ fn syscall ( number : libc:: c_long , ...) -> libc:: c_long ;
541
+ }
542
+
543
+ const SYS_GETRANDOM : libc:: c_long = 143 ;
544
+ const GRND_NONBLOCK : libc:: c_uint = 0x0001 ;
545
+ const GRND_RANDOM : libc:: c_uint = 0x0002 ;
546
+
547
+ unsafe {
548
+ syscall ( SYS_GETRANDOM , buf. as_mut_ptr ( ) , buf. len ( ) ,
549
+ GRND_NONBLOCK | GRND_RANDOM )
550
+ }
551
+ }
552
+
553
+ fn getrandom_try_fill ( dest : & mut [ u8 ] ) -> Result < ( ) , Error > {
554
+ trace ! ( "OsRng: reading {} bytes via getrandom" , dest. len( ) ) ;
555
+ let mut read = 0 ;
556
+ let len = dest. len ( ) ;
557
+ while read < len {
558
+ let result = getrandom ( & mut dest[ read..] ) ;
559
+ if result == -1 {
560
+ let err = io:: Error :: last_os_error ( ) ;
561
+ let kind = err. kind ( ) ;
562
+ if kind == io:: ErrorKind :: Interrupted {
563
+ continue ;
564
+ } else if kind == io:: ErrorKind :: WouldBlock {
565
+ // Potentially this would waste bytes, but since we use
566
+ // /dev/urandom blocking only happens if not initialised.
567
+ // Also, wasting the bytes in dest doesn't matter very much.
568
+ return Err ( Error :: with_cause (
569
+ ErrorKind :: NotReady ,
570
+ "getrandom not ready" ,
571
+ err,
572
+ ) ) ;
573
+ } else {
574
+ return Err ( Error :: with_cause (
575
+ ErrorKind :: Unavailable ,
576
+ "unexpected getrandom error" ,
577
+ err,
578
+ ) ) ;
579
+ }
580
+ } else {
581
+ read += result as usize ;
582
+ }
583
+ }
584
+ Ok ( ( ) )
585
+ }
586
+
587
+ fn is_getrandom_available ( ) -> bool {
588
+ use std:: sync:: atomic:: { AtomicBool , ATOMIC_BOOL_INIT , Ordering } ;
589
+ use std:: sync:: { Once , ONCE_INIT } ;
590
+
591
+ static CHECKER : Once = ONCE_INIT ;
592
+ static AVAILABLE : AtomicBool = ATOMIC_BOOL_INIT ;
593
+
594
+ CHECKER . call_once ( || {
595
+ debug ! ( "OsRng: testing getrandom" ) ;
596
+ let mut buf: [ u8 ; 0 ] = [ ] ;
597
+ let result = getrandom ( & mut buf) ;
598
+ let available = if result == -1 {
599
+ let err = io:: Error :: last_os_error ( ) . raw_os_error ( ) ;
600
+ err != Some ( libc:: ENOSYS )
601
+ } else {
602
+ true
603
+ } ;
604
+ AVAILABLE . store ( available, Ordering :: Relaxed ) ;
605
+ info ! ( "OsRng: using {}" , if available { "getrandom" } else { "/dev/random" } ) ;
606
+ } ) ;
607
+
608
+ AVAILABLE . load ( Ordering :: Relaxed )
609
+ }
610
+ }
611
+
486
612
#[ cfg( target_os = "cloudabi" ) ]
487
613
mod imp {
488
614
extern crate cloudabi;
@@ -864,6 +990,14 @@ mod test {
864
990
r. fill_bytes ( & mut empty) ;
865
991
}
866
992
993
+ #[ test]
994
+ fn test_os_rng_huge ( ) {
995
+ let mut r = OsRng :: new ( ) . unwrap ( ) ;
996
+
997
+ let mut huge = [ 0u8 ; 100_000 ] ;
998
+ r. fill_bytes ( & mut huge) ;
999
+ }
1000
+
867
1001
#[ cfg( not( any( target_arch = "wasm32" , target_arch = "asmjs" ) ) ) ]
868
1002
#[ test]
869
1003
fn test_os_rng_tasks ( ) {
0 commit comments