Repository: rust-random/getrandom
Pull request #6 (2019-02-04 18:09:13)

Crate implementation
Currently only WASM targets are not migrated. I've introduced some changes to the code, but tested only Linux and SGX, thus crate should be carefully reviewed and tested on all supported  platforms before using it for `rand_os` and `rand`. Notable changes include:

- Thread local storage is used instead of global statics in several places. (in theory should be more efficient for long-lived threads, but uses one file descriptor per thread)
- Dependence on `cloudabi` and `fuchsia-zircon` is removed in favor of direct `extern`s.
- Logging is removed. (can be added later)

A certain amount of work will be needed to translate `errno` values to `Error` variants, currently I simply return `Error::Unknown`. Ans we will need to create CI config as well.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index d79a95d..302ea63 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,9 +1,23 @@
 [package]
 name = "getrandom"
-version = "0.0.0"
+version = "0.1.0"
 authors = ["The Rand Project Developers"]
-license = "MIT/Apache-2.0"
-edition = "2015"
+license = "MIT OR Apache-2.0"
 description = "A small cross-platform library to securely get random data (entropy)"
 
-[dependencies]
+[badges]
+travis-ci = { repository = "rust-random/getrandom" }
+appveyor = { repository = "rust-random/getrandom" }
+
+[target.'cfg(unix)'.dependencies]
+libc = "0.2"
+
+[target.'cfg(windows)'.dependencies]
+winapi = { version = "0.3", features = ["minwindef", "ntsecapi", "winnt"] }
+
+[target.'cfg(fuchsia)'.dependencies]
+fuchsia-cprng = "0.1"
+
+[target.wasm32-unknown-unknown.dependencies]
+wasm-bindgen = { version = "0.2.12", optional = true }
+stdweb = { version = "0.4", optional = true }
diff --git a/benches/mod.rs b/benches/mod.rs
new file mode 100644
index 0000000..cf9747a
--- /dev/null
+++ b/benches/mod.rs
@@ -0,0 +1,24 @@
+#![feature(test)]
+extern crate test;
+extern crate getrandom;
+
+#[bench]
+fn bench_64(b: &mut test::Bencher) {
+    let mut buf = [0u8; 64];
+    b.iter(|| {
+        getrandom::getrandom(&mut buf[..]).unwrap();
+        test::black_box(&buf);        
+    });
+    b.bytes = buf.len() as u64;
+}
+
+#[bench]
+fn bench_65536(b: &mut test::Bencher) {
+    let mut buf = [0u8; 65536];
+    b.iter(|| {
+        getrandom::getrandom(&mut buf[..]).unwrap();
+        test::black_box(&buf);        
+    });
+    b.bytes = buf.len() as u64;
+}
+
diff --git a/src/cloudabi.rs b/src/cloudabi.rs
new file mode 100644
index 0000000..7751505
--- /dev/null
+++ b/src/cloudabi.rs
@@ -0,0 +1,23 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use error::Error;
+
+extern "C" {
+    fn cloudabi_sys_random_get(buf: *mut u8, len: usize) -> u16;
+}
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    let errno = unsafe { cloudabi_sys_random_get(dest.as_ptr(), dest.len()) };
+    if errno == 0 {
+        Ok(())
+    } else {
+        Err(Error(unsafe {
+            NonZeroU32::new_unchecked(errno as u32)
+        }))
+    }
+}
diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs
new file mode 100644
index 0000000..7691616
--- /dev/null
+++ b/src/dragonfly_haiku.rs
@@ -0,0 +1,25 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for DragonFly / Haiku
+use super::Error;
+use super::utils::use_init;
+use std::fs::File;
+use std::io::Read;
+use std::cell::RefCell;
+
+thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    RNG_FILE.with(|f| {
+        use_init(f,
+            || File::open("/dev/random").map_err(From::from),
+            |f| f.read_exact(dest).map_err(From::from),
+        )
+    })
+}
diff --git a/src/dummy.rs b/src/dummy.rs
new file mode 100644
index 0000000..41fa39f
--- /dev/null
+++ b/src/dummy.rs
@@ -0,0 +1,15 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A dummy implementation for unsupported targets which always returns
+//! `Err(UNAVAILABLE_ERROR)`
+use super::UNAVAILABLE_ERROR;
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    Err(UNAVAILABLE_ERROR)
+}
diff --git a/src/emscripten.rs b/src/emscripten.rs
new file mode 100644
index 0000000..446e463
--- /dev/null
+++ b/src/emscripten.rs
@@ -0,0 +1,31 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for Emscripten
+use super::Error;
+use std::fs::File;
+use std::io::Read;
+use std::cell::RefCell;
+use super::utils::use_init;
+
+thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    // `Crypto.getRandomValues` documents `dest` should be at most 65536
+    // bytes. `crypto.randomBytes` documents: "To minimize threadpool
+    // task length variation, partition large randomBytes requests when
+    // doing so as part of fulfilling a client request.
+    RNG_FILE.with(|f| {
+        use_init(f, || File::open("/dev/random").map_err(From::from), |f| {
+            for chunk in dest.chunks_mut(65536) {
+                f.read_exact(chunk)?;
+            }
+            Ok(())
+        })
+    })
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..a6dd3d7
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,67 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use core::num::NonZeroU32;
+use core::convert::From;
+use core::fmt;
+#[cfg(not(target_env = "sgx"))]
+use std::{io, error};
+
+pub const UNKNOWN_ERROR: Error = Error(unsafe {
+    NonZeroU32::new_unchecked(0x756e6b6e) // "unkn"
+});
+
+pub const UNAVAILABLE_ERROR: Error = Error(unsafe {
+    NonZeroU32::new_unchecked(0x4e416e61) // "NAna"
+});
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Error(NonZeroU32);
+
+impl Error {
+    pub fn code(&self) -> NonZeroU32 {
+        self.0
+    }
+}
+
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        match *self {
+            UNKNOWN_ERROR => write!(f, "Getrandom Error: unknown"),
+            UNAVAILABLE_ERROR => write!(f, "Getrandom Error: unavailable"),
+            code => write!(f, "Getrandom Error: {}", code.0.get()),
+        }
+    }
+}
+
+#[cfg(not(target_env = "sgx"))]
+impl From<io::Error> for Error {
+    fn from(err: io::Error) -> Self {
+        err.raw_os_error()
+            .and_then(|code| NonZeroU32::new(code as u32))
+            .map(|code| Error(code))
+            // in practice this should never happen
+            .unwrap_or(UNKNOWN_ERROR)
+    }
+}
+
+#[cfg(not(target_env = "sgx"))]
+impl Into<io::Error> for Error {
+    fn into(self) -> io::Error {
+        match self {
+            UNKNOWN_ERROR => io::Error::new(io::ErrorKind::Other,
+                "getrandom error: unknown"),
+            UNAVAILABLE_ERROR => io::Error::new(io::ErrorKind::Other,
+                "getrandom error: entropy source is unavailable"),
+            code => io::Error::from_raw_os_error(code.0.get() as i32),
+        }
+    }
+}
+
+#[cfg(not(target_env = "sgx"))]
+impl error::Error for Error { }
diff --git a/src/freebsd.rs b/src/freebsd.rs
new file mode 100644
index 0000000..ac990f3
--- /dev/null
+++ b/src/freebsd.rs
@@ -0,0 +1,32 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for FreeBSD
+extern crate libc;
+
+use super::Error;
+use core::ptr;
+use std::io;
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    let mib = [libc::CTL_KERN, libc::KERN_ARND];
+    // kern.arandom permits a maximum buffer size of 256 bytes
+    for chunk in dest.chunks_mut(256) {
+        let mut len = chunk.len();
+        let ret = unsafe {
+            libc::sysctl(
+                mib.as_ptr(), mib.len() as libc::c_uint,
+                chunk.as_mut_ptr() as *mut _, &mut len, ptr::null(), 0,
+            )
+        };
+        if ret == -1 || len != chunk.len() {
+            return Err(io::Error::last_os_error().into());
+        }
+    }
+    Ok(())
+}
diff --git a/src/fuchsia.rs b/src/fuchsia.rs
new file mode 100644
index 0000000..b152cde
--- /dev/null
+++ b/src/fuchsia.rs
@@ -0,0 +1,17 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for Fuchsia Zircon
+extern crate fuchsia_cprng;
+
+use super::Error;
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    fuchsia_cprng::cprng_draw(dest);
+    Ok(())
+}
diff --git a/src/lib.rs b/src/lib.rs
index cb7eb8b..104b588 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,3 +5,98 @@
 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
+#![no_std]
+
+#[cfg(not(target_env = "sgx"))]
+#[macro_use] extern crate std;
+
+#[cfg(any(
+    target_os = "android",
+    target_os = "netbsd",
+    target_os = "solaris",
+    target_os = "redox",
+    target_os = "dragonfly",
+    target_os = "haiku",
+    target_os = "emscripten",
+    target_os = "linux",
+))]
+mod utils;
+mod error;
+pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};
+
+macro_rules! mod_use {
+    ($cond:meta, $module:ident) => {
+        #[$cond]
+        mod $module;
+        #[$cond]
+        pub use $module::getrandom;
+    }
+}
+
+mod_use!(cfg(target_os = "android"), linux_android);
+mod_use!(cfg(target_os = "bitrig"), openbsd_bitrig);
+mod_use!(cfg(target_os = "cloudabi"), cloudabi);
+mod_use!(cfg(target_os = "dragonfly"), dragonfly_haiku);
+mod_use!(cfg(target_os = "emscripten"), emscripten);
+mod_use!(cfg(target_os = "freebsd"), freebsd);
+mod_use!(cfg(target_os = "fuchsia"), fuchsia);
+mod_use!(cfg(target_os = "haiku"), dragonfly_haiku);
+mod_use!(cfg(target_os = "ios"), macos);
+mod_use!(cfg(target_os = "linux"), linux_android);
+mod_use!(cfg(target_os = "macos"), macos);
+mod_use!(cfg(target_os = "netbsd"), netbsd);
+mod_use!(cfg(target_os = "openbsd"), openbsd_bitrig);
+mod_use!(cfg(target_os = "redox"), redox);
+mod_use!(cfg(target_os = "solaris"), solaris);
+mod_use!(cfg(windows), windows);
+mod_use!(cfg(target_env = "sgx"), sgx);
+
+mod_use!(
+    cfg(all(
+        target_arch = "wasm32",
+        not(target_os = "emscripten"),
+        feature = "wasm-bindgen"
+    )),
+    wasm32_bindgen
+);
+
+mod_use!(
+    cfg(all(
+        target_arch = "wasm32",
+        not(target_os = "emscripten"),
+        not(feature = "wasm-bindgen"),
+        feature = "stdweb",
+    )),
+    wasm32_stdweb
+);
+
+mod_use!(
+    cfg(not(any(
+        target_os = "android",
+        target_os = "bitrig",
+        target_os = "cloudabi",
+        target_os = "dragonfly",
+        target_os = "emscripten",
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "haiku",
+        target_os = "ios",
+        target_os = "linux",
+        target_os = "macos",
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "redox",
+        target_os = "solaris",
+        target_env = "sgx",
+        windows,
+        all(
+            target_arch = "wasm32",
+            any(
+                target_os = "emscripten",
+                feature = "wasm-bindgen",
+                feature = "stdweb",
+            ),
+        ),
+    ))),
+    dummy
+);
diff --git a/src/linux_android.rs b/src/linux_android.rs
new file mode 100644
index 0000000..db6c6e4
--- /dev/null
+++ b/src/linux_android.rs
@@ -0,0 +1,82 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for Linux / Android
+extern crate libc;
+
+use super::Error;
+use super::utils::use_init;
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::cell::RefCell;
+use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+
+static RNG_INIT: AtomicBool = ATOMIC_BOOL_INIT;
+
+enum RngSource {
+    GetRandom,
+    Device(File),
+}
+
+thread_local!(
+    static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
+);
+
+fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
+    let ret = unsafe {
+        libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), dest.len(), 0)
+    };
+    if ret == -1 || ret != dest.len() as i64 {
+        return Err(io::Error::last_os_error());
+    }
+    Ok(())
+}
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    RNG_SOURCE.with(|f| {
+        use_init(f,
+        || {
+            let s = if is_getrandom_available() {
+                RngSource::GetRandom
+            } else {
+                // read one byte from "/dev/random" to ensure that
+                // OS RNG has initialized
+                if !RNG_INIT.load(Ordering::Relaxed) {
+                    File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
+                    RNG_INIT.store(true, Ordering::Relaxed)
+                }
+                RngSource::Device(File::open("/dev/urandom")?)
+            };
+            Ok(s)
+        }, |f| {
+            match f {
+                RngSource::GetRandom => syscall_getrandom(dest),
+                RngSource::Device(f) => f.read_exact(dest),
+            }.map_err(From::from)
+        })
+    })
+}
+
+fn is_getrandom_available() -> bool {
+    use std::sync::{Once, ONCE_INIT};
+
+    static CHECKER: Once = ONCE_INIT;
+    static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
+
+    CHECKER.call_once(|| {
+        let mut buf: [u8; 0] = [];
+        let available = match syscall_getrandom(&mut buf) {
+            Ok(()) => true,
+            Err(err) => err.raw_os_error() != Some(libc::ENOSYS),
+        };
+        AVAILABLE.store(available, Ordering::Relaxed);
+    });
+
+    AVAILABLE.load(Ordering::Relaxed)
+}
diff --git a/src/macos.rs b/src/macos.rs
new file mode 100644
index 0000000..3855f1a
--- /dev/null
+++ b/src/macos.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for MacOS / iOS
+use super::Error;
+use std::io;
+
+// TODO: check correctness
+#[allow(non_upper_case_globals)]
+const kSecRandomDefault: *const SecRandom = 0 as *const SecRandom;
+
+#[link(name = "Security", kind = "framework")]
+extern {
+    fn SecRandomCopyBytes(
+        rnd: *const SecRandom, count: size_t, bytes: *mut u8,
+    ) -> c_int;
+}
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    let ret = unsafe {
+        SecRandomCopyBytes(
+            kSecRandomDefault,
+            dest.len() as size_t,
+            dest.as_mut_ptr(),
+        )
+    };
+    if ret == -1 {
+        Err(io::Error::last_os_error().into())
+    } else {
+        Ok(())
+    }
+}
diff --git a/src/netbsd.rs b/src/netbsd.rs
new file mode 100644
index 0000000..f2aa68c
--- /dev/null
+++ b/src/netbsd.rs
@@ -0,0 +1,34 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for NetBSD
+
+use super::Error;
+use super::utils::use_init;
+use std::fs::File;
+use std::io::Read;
+use std::cell::RefCell;
+use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+
+static RNG_INIT: AtomicBool = ATOMIC_BOOL_INIT;
+
+thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    RNG_FILE.with(|f| {
+        use_init(f, || {
+            // read one byte from "/dev/random" to ensure that
+            // OS RNG has initialized
+            if !RNG_INIT.load(Ordering::Relaxed) {
+                File::open("/dev/random")?.read_exact(&mut [0u8; 1])?;
+                RNG_INIT.store(true, Ordering::Relaxed)
+            }
+            File::open("/dev/urandom").map_err(From::from)
+        }, |f| f.read_exact(dest).map_err(From::from))
+    })
+}
diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs
new file mode 100644
index 0000000..f8096fa
--- /dev/null
+++ b/src/openbsd_bitrig.rs
@@ -0,0 +1,28 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for OpenBSD / Bitrig
+extern crate libc;
+
+use super::Error;
+use std::io;
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    for chunk in dest.chunks_mut(256) {
+        let ret = unsafe {
+            libc::getentropy(
+                chunk.as_mut_ptr() as *mut libc::c_void,
+                chunk.len()
+            )
+        };
+        if ret == -1 {
+            return Err(io::Error::last_os_error().into());
+        }
+    }
+    Ok(())
+}
diff --git a/src/redox.rs b/src/redox.rs
new file mode 100644
index 0000000..0c7e857
--- /dev/null
+++ b/src/redox.rs
@@ -0,0 +1,25 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for Redox
+use super::Error;
+use super::utils::use_init;
+use std::fs::File;
+use std::io::Read;
+use std::cell::RefCell;
+
+thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    RNG_FILE.with(|f| {
+        use_init(f,
+            || File::open("rand:").map_err(From::from),
+            |f| f.read_exact(dest).map_err(From::from),
+        )
+    }).map_err(From::from)
+}
diff --git a/src/sgx.rs b/src/sgx.rs
new file mode 100644
index 0000000..a98f55f
--- /dev/null
+++ b/src/sgx.rs
@@ -0,0 +1,48 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for SGX using RDRAND instruction
+use super::{Error, UNKNOWN_ERROR};
+
+use core::{mem, ptr};
+use core::arch::x86_64::_rdrand64_step;
+
+#[cfg(not(target_feature = "rdrand"))]
+compile_error!("enable rdrand target feature!");
+
+const RETRY_LIMIT: usize = 32;
+
+fn get_rand_u64() -> Result<u64, Error> {
+    for _ in 0..RETRY_LIMIT {
+        unsafe {
+            let mut el = mem::uninitialized();
+            if _rdrand64_step(&mut el) == 1 {
+                return Ok(el);
+            }
+        };
+    }
+    Err(UNKNOWN_ERROR)
+}
+
+pub fn getrandom(mut dest: &mut [u8]) -> Result<(), Error> {
+    while dest.len() >= 8 {
+        let (chunk, left) = {dest}.split_at_mut(8);
+        dest = left;
+        let r = get_rand_u64()?;
+        unsafe {
+            ptr::write_unaligned(chunk.as_mut_ptr() as *mut u64, r)
+        }
+    }
+    let n = dest.len();
+    if n != 0 {
+        let r = get_rand_u64()?;
+        let r: [u8; 8] = unsafe { mem::transmute(r) };
+        dest.copy_from_slice(&r[..n]);
+    }
+    Ok(())
+}
diff --git a/src/solaris.rs b/src/solaris.rs
new file mode 100644
index 0000000..e27e4bc
--- /dev/null
+++ b/src/solaris.rs
@@ -0,0 +1,99 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for the Solaris family
+//!
+//! Read from `/dev/random`, with chunks of limited size (1040 bytes).
+//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
+//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
+//! secure. We choose to read from `/dev/random`.
+//!
+//! Since Solaris 11.3 the `getrandom` syscall is available. To make sure we can
+//! compile on both Solaris and on OpenSolaris derivatives, that do not have the
+//! function, we do a direct syscall instead of calling a library function.
+//!
+//! We have no way to differentiate between Solaris, illumos, SmartOS, etc.
+extern crate libc;
+
+use super::Error;
+use std::fs::File;
+use std::io;
+use std::io::Read;
+use std::cell::RefCell;
+use std::ops::DerefMut;
+
+enum RngSource {
+    GetRandom,
+    Device(File),
+}
+
+thread_local!(
+    static RNG_SOURCE: RefCell<Option<RngSource>> = RefCell::new(None);
+);
+
+fn syscall_getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    // repalce with libc?
+    const SYS_GETRANDOM: libc::c_long = 143;
+
+    extern "C" {
+        fn syscall(number: libc::c_long, ...) -> libc::c_long;
+    }
+
+    let ret = unsafe {
+        syscall(SYS_GETRANDOM, dest.as_mut_ptr(), dest.len(), 0)
+    };
+    if ret == -1 || ret != dest.len() as i64 {
+        return Err(io::Error::last_os_error().from());
+    }
+    Ok(())
+}
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    // The documentation says 1024 is the maximum for getrandom
+    // and 1040 for /dev/random.
+    RNG_SOURCE.with(|f| {
+        use_init(f,
+        || {
+            let s = if is_getrandom_available() {
+                RngSource::GetRandom
+            } else {
+                RngSource::Device(File::open("/dev/random")?)
+            };
+            Ok(s)
+        }, |f| {
+            match f {
+                RngSource::GetRandom => for chunk in dest.chunks_mut(1024) {
+                    syscall_getrandom(chunk)
+                },
+                RngSource::Device(f) => for chunk in dest.chunks_mut(1040) {
+                    f.read_exact(dest).map_err(From::from)
+                },
+            }
+        })
+    }).map_err(|_| Error::Unknown)
+}
+
+fn is_getrandom_available() -> bool {
+    use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, Ordering};
+    use std::sync::{Once, ONCE_INIT};
+
+    static CHECKER: Once = ONCE_INIT;
+    static AVAILABLE: AtomicBool = ATOMIC_BOOL_INIT;
+
+    CHECKER.call_once(|| {
+        let mut buf: [u8; 0] = [];
+        let available = match syscall_getrandom(&mut buf) {
+            Ok(()) => true,
+            Err(ref err) if err.raw_os_error() == Some(libc::ENOSYS) => false,
+            Err(_) => true,
+        };
+        AVAILABLE.store(available, Ordering::Relaxed);
+    });
+
+    AVAILABLE.load(Ordering::Relaxed)
+}
diff --git a/src/utils.rs b/src/utils.rs
new file mode 100644
index 0000000..5b70bd1
--- /dev/null
+++ b/src/utils.rs
@@ -0,0 +1,30 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+use super::Error;
+use core::cell::RefCell;
+use core::ops::DerefMut;
+
+/// If `f` contains `Some(T)` call `use_f` using contents of `f` as an argument,
+/// otherwise initialize `f` value using `init_f`, store resulting value in `f`
+/// and call `use_f`.
+pub(crate) fn use_init<T, F, U>(f: &RefCell<Option<T>>, init_f: F, mut use_f: U)
+    -> Result<(), Error>
+    where F: FnOnce() -> Result<T, Error>, U: FnMut(&mut T) -> Result<(), Error>
+{
+    let mut f = f.borrow_mut();
+    let f: &mut Option<T> = f.deref_mut();
+    match f {
+        None => *f = Some(init_f()?),
+        _ => (),
+    }
+
+    match f {
+        Some(f) => use_f(f),
+        None => unreachable!(),
+    }
+}
diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs
new file mode 100644
index 0000000..6de8f04
--- /dev/null
+++ b/src/wasm32_bindgen.rs
@@ -0,0 +1,9 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for WASM via wasm-bindgen
diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs
new file mode 100644
index 0000000..7db8afe
--- /dev/null
+++ b/src/wasm32_stdweb.rs
@@ -0,0 +1,9 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for WASM via stdweb
diff --git a/src/windows.rs b/src/windows.rs
new file mode 100644
index 0000000..6507b6c
--- /dev/null
+++ b/src/windows.rs
@@ -0,0 +1,24 @@
+// Copyright 2018 Developers of the Rand project.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation for Windows
+extern crate winapi;
+
+use self::winapi::shared::minwindef::ULONG;
+use self::winapi::um::ntsecapi::RtlGenRandom;
+use self::winapi::um::winnt::PVOID;
+use std::io;
+use super::Error;
+
+pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+    let ret = unsafe {
+        RtlGenRandom(dest.as_mut_ptr() as PVOID, dest.len() as ULONG)
+    };
+    if ret == 0 { return Err(io::Error::last_os_error().into()); }
+    Ok(())
+}


Test patch:
diff --git a/tests/mod.rs b/tests/mod.rs
new file mode 100644
index 0000000..61719e5
--- /dev/null
+++ b/tests/mod.rs
@@ -0,0 +1,55 @@
+extern crate getrandom;
+
+use getrandom::getrandom;
+
+#[test]
+fn test_diff() {
+    let mut v1 = [0u8; 1000];
+    getrandom(&mut v1).unwrap();
+
+    let mut v2 = [0u8; 1000];
+    getrandom(&mut v2).unwrap();
+
+    let mut n_diff_bits = 0;
+    for i in 0..v1.len() {
+        n_diff_bits += (v1[i] ^ v2[i]).count_ones();
+    }
+
+    // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input.
+    assert!(n_diff_bits >= v1.len() as u32);
+}
+
+#[test]
+fn test_huge() {
+    let mut huge = [0u8; 100_000];
+    getrandom(&mut huge).unwrap();
+}
+
+#[cfg(any(unix, windows, target_os="redox", target_os = "fuchsia"))]
+#[test]
+fn test_multithreading() {
+    use std::sync::mpsc::channel;
+    use std::thread;
+
+    let mut txs = vec!();
+    for _ in 0..20 {
+        let (tx, rx) = channel();
+        txs.push(tx);
+
+        thread::spawn(move|| {
+            // wait until all the tasks are ready to go.
+            rx.recv().unwrap();
+            let mut v = [0u8; 1000];
+
+            for _ in 0..100 {
+                getrandom(&mut v).unwrap();
+                thread::yield_now();
+            }
+        });
+    }
+
+    // start all the tasks
+    for tx in txs.iter() {
+        tx.send(()).unwrap();
+    }
+}