Skip to content

Commit 43b4c1b

Browse files
committed
Add support for Solaris and Haiku
1 parent cb458cf commit 43b4c1b

File tree

1 file changed

+156
-22
lines changed

1 file changed

+156
-22
lines changed

src/rngs/os.rs

Lines changed: 156 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,22 @@ use rand_core::{CryptoRng, RngCore, Error, impls};
3333
///
3434
/// # Platform sources
3535
///
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])
5052
///
5153
/// Rand doesn't have a blanket implementation for all Unix-like operating
5254
/// systems that reads from `/dev/urandom`. This ensures all supported operating
@@ -100,12 +102,14 @@ use rand_core::{CryptoRng, RngCore, Error, impls};
100102
/// [6]: https://man.openbsd.org/getentropy.2
101103
/// [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
102104
/// [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=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
109113
110114

111115
#[derive(Clone)]
@@ -193,7 +197,8 @@ impl RngCore for OsRng {
193197
// exhaustion of file descriptors.
194198
#[cfg(any(target_os = "linux", target_os = "android",
195199
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"))]
197202
mod unix {
198203
extern crate libc;
199204
use {Error, ErrorKind};
@@ -434,7 +439,7 @@ mod imp {
434439
}
435440

436441
// Read from `/dev/random`
437-
#[cfg(target_os = "dragonfly")]
442+
#[cfg(any(target_os = "dragonfly", target_os = "haiku"))]
438443
mod imp {
439444
use Error;
440445
use super::unix::*;
@@ -483,6 +488,127 @@ mod imp {
483488
}
484489
}
485490

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+
486612
#[cfg(target_os = "cloudabi")]
487613
mod imp {
488614
extern crate cloudabi;
@@ -864,6 +990,14 @@ mod test {
864990
r.fill_bytes(&mut empty);
865991
}
866992

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+
8671001
#[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))]
8681002
#[test]
8691003
fn test_os_rng_tasks() {

0 commit comments

Comments
 (0)