1 // Copyright 2018 Developers of the Rand project.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
9 //! Implementation for the Solaris family
11 //! Read from `/dev/random`, with chunks of limited size (1040 bytes).
12 //! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
13 //! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
14 //! secure. We choose to read from `/dev/random`.
16 //! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
17 //! compile on both Solaris and on OpenSolaris derivatives, that do not have the
18 //! function, we do a direct syscall instead of calling a library function.
20 //! We have no way to differentiate between Solaris, illumos, SmartOS, etc.
23 use rand_core
::{Error, ErrorKind}
;
24 use super::random_device
;
29 use std
::fs
::{File, OpenOptions}
;
30 use std
::os
::unix
::fs
::OpenOptionsExt
;
31 use std
::sync
::atomic
::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}
;
34 #[derive(Clone, Debug)]
40 #[derive(Clone, Debug)]
46 impl OsRngImpl
for OsRng
{
47 fn new() -> Result
<OsRng
, Error
> {
48 if is_getrandom_available() {
49 return Ok(OsRng
{ method
: OsRngMethod
::GetRandom
,
50 initialized
: false });
52 let open
= |p
| OpenOptions
::new()
54 .custom_flags(libc
::O_NONBLOCK
)
56 random_device
::open("/dev/random", &open
)?
;
57 Ok(OsRng { method: OsRngMethod::RandomDevice, initialized: false }
)
60 fn fill_chunk(&mut self, dest
: &mut [u8]) -> Result
<(), Error
> {
62 OsRngMethod
::GetRandom
=> getrandom_try_fill(dest
, false),
63 OsRngMethod
::RandomDevice
=> random_device
::read(dest
),
67 fn test_initialized(&mut self, dest
: &mut [u8], blocking
: bool
)
68 -> Result
<usize, Error
>
70 static OS_RNG_INITIALIZED
: AtomicBool
= ATOMIC_BOOL_INIT
;
71 if !self.initialized
{
72 self.initialized
= OS_RNG_INITIALIZED
.load(Ordering
::Relaxed
);
74 if self.initialized { return Ok(0); }
76 let chunk_len
= cmp
::min(1024, dest
.len());
77 let dest
= &mut dest
[..chunk_len
];
80 OsRngMethod
::GetRandom
=> getrandom_try_fill(dest
, blocking
)?
,
81 OsRngMethod
::RandomDevice
=> {
83 info
!("OsRng: testing random device /dev/random");
84 // We already have a non-blocking handle, but now need a
85 // blocking one. Not much choice except opening it twice
86 let mut file
= File
::open("/dev/random")
87 .map_err(random_device
::map_err
)?
;
88 file
.read(dest
).map_err(random_device
::map_err
)?
;
90 self.fill_chunk(dest
)?
;
94 OS_RNG_INITIALIZED
.store(true, Ordering
::Relaxed
);
95 self.initialized
= true;
99 fn max_chunk_size(&self) -> usize {
100 // The documentation says 1024 is the maximum for getrandom, but
101 // 1040 for /dev/random.
105 fn method_str(&self) -> &'
static str {
107 OsRngMethod
::GetRandom
=> "getrandom",
108 OsRngMethod
::RandomDevice
=> "/dev/random",
113 fn getrandom(buf
: &mut [u8], blocking
: bool
) -> libc
::c_long
{
115 fn syscall(number
: libc
::c_long
, ...) -> libc
::c_long
;
118 const SYS_GETRANDOM
: libc
::c_long
= 143;
119 const GRND_NONBLOCK
: libc
::c_uint
= 0x0001;
120 const GRND_RANDOM
: libc
::c_uint
= 0x0002;
123 syscall(SYS_GETRANDOM
, buf
.as_mut_ptr(), buf
.len(),
124 if blocking { 0 }
else { GRND_NONBLOCK }
| GRND_RANDOM
)
128 fn getrandom_try_fill(dest
: &mut [u8], blocking
: bool
) -> Result
<(), Error
> {
129 let result
= getrandom(dest
, blocking
);
130 if result
== -1 || result
== 0 {
131 let err
= io
::Error
::last_os_error();
132 let kind
= err
.kind();
133 if kind
== io
::ErrorKind
::WouldBlock
{
134 return Err(Error
::with_cause(
136 "getrandom not ready",
140 return Err(Error
::with_cause(
141 ErrorKind
::Unavailable
,
142 "unexpected getrandom error",
146 } else if result
!= dest
.len() as i64 {
147 return Err(Error
::new(ErrorKind
::Unavailable
,
148 "unexpected getrandom error"));
153 fn is_getrandom_available() -> bool
{
154 use std
::sync
::atomic
::{AtomicBool, ATOMIC_BOOL_INIT, Ordering}
;
155 use std
::sync
::{Once, ONCE_INIT}
;
157 static CHECKER
: Once
= ONCE_INIT
;
158 static AVAILABLE
: AtomicBool
= ATOMIC_BOOL_INIT
;
160 CHECKER
.call_once(|| {
161 debug
!("OsRng: testing getrandom");
162 let mut buf
: [u8; 0] = [];
163 let result
= getrandom(&mut buf
, false);
164 let available
= if result
== -1 {
165 let err
= io
::Error
::last_os_error().raw_os_error();
166 err
!= Some(libc
::ENOSYS
)
170 AVAILABLE
.store(available
, Ordering
::Relaxed
);
171 info
!("OsRng: using {}", if available { "getrandom" }
else { "/dev/random" }
);
174 AVAILABLE
.load(Ordering
::Relaxed
)