From 71a3cb8a96b9430500daef92c2e75c6ecbef7d67 Mon Sep 17 00:00:00 2001 From: newpavlov Date: Mon, 4 Feb 2019 20:57:30 +0300 Subject: [PATCH 01/13] first commit --- Cargo.toml | 19 ++++-- src/cloudabi.rs | 21 ++++++ src/dragonfly_haiku.rs | 31 +++++++++ src/dummy.rs | 15 +++++ src/emscripten.rs | 38 +++++++++++ src/error.rs | 21 ++++++ src/freebsd.rs | 31 +++++++++ src/fuchsia.rs | 22 ++++++ src/lib.rs | 150 +++++++++++++++++++++++++++++++++++++++++ src/linux_android.rs | 86 +++++++++++++++++++++++ src/macos.rs | 38 +++++++++++ src/netbsd.rs | 36 ++++++++++ src/openbsd_bitrig.rs | 27 ++++++++ src/redox.rs | 31 +++++++++ src/sgx.rs | 48 +++++++++++++ src/solaris.rs | 103 ++++++++++++++++++++++++++++ src/wasm32_bindgen.rs | 9 +++ src/wasm32_stdweb.rs | 9 +++ src/windows.rs | 23 +++++++ tests/mod.rs | 55 +++++++++++++++ 20 files changed, 809 insertions(+), 4 deletions(-) create mode 100644 src/cloudabi.rs create mode 100644 src/dragonfly_haiku.rs create mode 100644 src/dummy.rs create mode 100644 src/emscripten.rs create mode 100644 src/error.rs create mode 100644 src/freebsd.rs create mode 100644 src/fuchsia.rs create mode 100644 src/linux_android.rs create mode 100644 src/macos.rs create mode 100644 src/netbsd.rs create mode 100644 src/openbsd_bitrig.rs create mode 100644 src/redox.rs create mode 100644 src/sgx.rs create mode 100644 src/solaris.rs create mode 100644 src/wasm32_bindgen.rs create mode 100644 src/wasm32_stdweb.rs create mode 100644 src/windows.rs create mode 100644 tests/mod.rs diff --git a/Cargo.toml b/Cargo.toml index d79a95d0..ced82aca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,20 @@ [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.wasm32-unknown-unknown.dependencies] +wasm-bindgen = { version = "0.2.12", optional = true } +stdweb = { version = "0.4", optional = true } diff --git a/src/cloudabi.rs b/src/cloudabi.rs new file mode 100644 index 00000000..27c5d256 --- /dev/null +++ b/src/cloudabi.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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::Unknown) + } +} diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs new file mode 100644 index 00000000..53dcfeeb --- /dev/null +++ b/src/dragonfly_haiku.rs @@ -0,0 +1,31 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 std::fs::File; +use std::io::Read; +use std::cell::RefCell; +use std::ops::DerefMut; + +thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + RNG_FILE.with(|f| { + let mut f = f.borrow_mut(); + let f: &mut Option = f.deref_mut(); + if let Some(f) = f { + f.read_exact(dest) + } else { + let mut rng_file = File::open("/dev/random")?; + rng_file.read_exact(dest)?; + *f = Some(rng_file); + Ok(()) + } + }).map_err(|_| Error::Unknown) +} diff --git a/src/dummy.rs b/src/dummy.rs new file mode 100644 index 00000000..8414eb68 --- /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 or the MIT license +// , 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(Error::Unavailable)` +use super::Error; + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + Err(Error::Unavailable) +} diff --git a/src/emscripten.rs b/src/emscripten.rs new file mode 100644 index 00000000..28bcd052 --- /dev/null +++ b/src/emscripten.rs @@ -0,0 +1,38 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for DragonFly / Haiku / Emscripten +use super::Error; +use std::fs::File; +use std::io::Read; +use std::cell::RefCell; +use std::ops::DerefMut; + +thread_local!(static RNG_FILE: RefCell> = 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. + for chunk in dest.chunks_mut(65536) { + RNG_FILE.with(|f| { + let mut f = f.borrow_mut(); + let f: &mut Option = f.deref_mut(); + if let Some(f) = f { + f.read_exact(chunk) + } else { + let mut rng_file = File::open("/dev/random")?; + rng_file.read_exact(chunk)?; + *f = Some(rng_file); + Ok(()) + } + }).map_err(|_| Error::Unknown)?; + } + Ok(()) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 00000000..9cbb7805 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,21 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Error { + /// Call was interrupted. + /// + /// Typically it can be retried. + Interrupted, + /// RNG source is unavailable on a given system. + Unavailable, + /// Unknown error. + Unknown, + #[doc(hidden)] + __Nonexhaustive, +} diff --git a/src/freebsd.rs b/src/freebsd.rs new file mode 100644 index 00000000..a415b905 --- /dev/null +++ b/src/freebsd.rs @@ -0,0 +1,31 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 std::ptr; + +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(Error::Unknown); + } + } + Ok(()) +} \ No newline at end of file diff --git a/src/fuchsia.rs b/src/fuchsia.rs new file mode 100644 index 00000000..d0796ad6 --- /dev/null +++ b/src/fuchsia.rs @@ -0,0 +1,22 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Fuchsia Zircon +use super::Error; + +#[link(name = "zircon")] +extern { + fn zx_cprng_draw(buffer: *mut u8, len: usize); +} + +pub fn getrandom(&mut self, dest: &mut [u8]) -> Result<(), Error> { + for chunk in dest.chunks(256) { + unsafe { zx_cprng_draw(chunk.as_mut_ptr(), chunk.len()) }; + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index cb7eb8bb..9df8ae09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,3 +5,153 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. + + +#![no_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", +))] +#[macro_use] extern crate std; + +mod error; +pub use error::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 +); + +// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os +// modules, so hack around it for now and place it at the root. +#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub mod __wbg_shims { + + // `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a + // macro to work around that. + macro_rules! rust_122_compat { + ($($t:tt)*) => ($($t)*) + } + + rust_122_compat! { + extern crate wasm_bindgen; + + pub use wasm_bindgen::prelude::*; + + #[wasm_bindgen] + extern "C" { + pub type Function; + #[wasm_bindgen(constructor)] + pub fn new(s: &str) -> Function; + #[wasm_bindgen(method)] + pub fn call(this: &Function, self_: &JsValue) -> JsValue; + + pub type This; + #[wasm_bindgen(method, getter, structural, js_name = self)] + pub fn self_(me: &This) -> JsValue; + #[wasm_bindgen(method, getter, structural)] + pub fn crypto(me: &This) -> JsValue; + + #[derive(Clone, Debug)] + pub type BrowserCrypto; + + // TODO: these `structural` annotations here ideally wouldn't be here to + // avoid a JS shim, but for now with feature detection they're + // unavoidable. + #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] + pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; + #[wasm_bindgen(method, js_name = getRandomValues, structural)] + pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); + + #[wasm_bindgen(js_name = require)] + pub fn node_require(s: &str) -> NodeCrypto; + + #[derive(Clone, Debug)] + pub type NodeCrypto; + + #[wasm_bindgen(method, js_name = randomFillSync, structural)] + pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); + } + } +} diff --git a/src/linux_android.rs b/src/linux_android.rs new file mode 100644 index 00000000..13179b7b --- /dev/null +++ b/src/linux_android.rs @@ -0,0 +1,86 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Linux / Android +extern crate std; +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), + None, +} + +thread_local!( + static RNG_SOURCE: RefCell = RefCell::new(RngSource::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| { + let mut f = f.borrow_mut(); + let f: &mut RngSource = f.deref_mut(); + if let RngSource::None = f { + *f = if is_getrandom_available() { + RngSource::GetRandom + } else { + let mut buf = [0u8; 1]; + File::open("/dev/random") + .and_then(|mut f| f.read_exact(&mut buf)) + .map_err(|_| Error::Unknown)?; + let mut rng_file = File::open("/dev/urandom") + .map_err(|_| Error::Unknown)?; + RngSource::Device(rng_file) + } + } + if let RngSource::Device(f) = f { + f.read_exact(dest) + .map_err(|_| Error::Unknown) + } else { + syscall_getrandom(dest) + .map_err(|_| Error::Unknown) + } + })?; + Ok(()) +} + +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/macos.rs b/src/macos.rs new file mode 100644 index 00000000..57a66ee0 --- /dev/null +++ b/src/macos.rs @@ -0,0 +1,38 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for MacOS / iOS +extern crate libc; + +use super::Error; + +// 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(Error::Unknown) + } else { + Ok(()) + } +} diff --git a/src/netbsd.rs b/src/netbsd.rs new file mode 100644 index 00000000..02c53c0f --- /dev/null +++ b/src/netbsd.rs @@ -0,0 +1,36 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for NetBSD + +use super::Error; +use std::fs::File; +use std::io::Read; +use std::cell::RefCell; +use std::ops::DerefMut; + +thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + RNG_FILE.with(|f| { + let mut f = f.borrow_mut(); + let f: &mut Option = f.deref_mut(); + if let Some(f) = f { + f.read_exact(dest) + } else { + // read one byte from /dev/random to ensure that RNG is bootstrapped + let mut buf = [0u8]; + File::open("/dev/random")?.read_exact(&mut buf)?; + + let mut rng_file = File::open("/dev/urandom")?; + rng_file.read_exact(dest)?; + *f = Some(rng_file); + Ok(()) + } + }).map_err(|_| Error::Unknown) +} diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs new file mode 100644 index 00000000..2cfe3ff0 --- /dev/null +++ b/src/openbsd_bitrig.rs @@ -0,0 +1,27 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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; + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + for chunk in dest.chunks_mut(256) { + let ret = unsafe { + libc::getentropy( + dest.as_mut_ptr() as *mut libc::c_void, + dest.len() + ) + }; + if ret == -1 { + return Err(Error::Unknown); + } + } + Ok(()) +} diff --git a/src/redox.rs b/src/redox.rs new file mode 100644 index 00000000..a2ac7852 --- /dev/null +++ b/src/redox.rs @@ -0,0 +1,31 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation for Redox +use super::Error; +use std::fs::File; +use std::io::Read; +use std::cell::RefCell; +use std::ops::DerefMut; + +thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + RNG_FILE.with(|f| { + let mut f = f.borrow_mut(); + let f: &mut Option = f.deref_mut(); + if let Some(f) = f { + f.read_exact(dest) + } else { + let mut rng_file = File::open("rand:")?; + rng_file.read_exact(dest)?; + *f = Some(rng_file); + Ok(()) + } + }).map_err(|_| Error::Unknown) +} diff --git a/src/sgx.rs b/src/sgx.rs new file mode 100644 index 00000000..9a2c4156 --- /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 or the MIT license +// , 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; + +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 { + for _ in 0..RETRY_LIMIT { + unsafe { + let mut el = mem::uninitialized(); + if _rdrand64_step(&mut el) == 1 { + return Ok(el); + } + }; + } + Err(Error::Unknown) +} + +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 00000000..3caa795d --- /dev/null +++ b/src/solaris.rs @@ -0,0 +1,103 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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), + None, +} + +thread_local!( + static RNG_SOURCE: RefCell = RefCell::new(RngSource::None); +); + +fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::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()); + } + Ok(()) +} + +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + // The documentation says 1024 is the maximum for getrandom, + // but 1040 for /dev/random. + for chunk in dest.chunks_mut(1024) { + RNG_SOURCE.with(|f| { + let mut f = f.borrow_mut(); + let f: &mut RngSource = f.deref_mut(); + if let RngSource::None = f { + *f = if is_getrandom_available() { + RngSource::GetRandom + } else { + let mut rng_file = File::open("/dev/random") + .map_err(|_| Error::Unknown)?; + RngSource::Device(rng_file) + } + } + if let RngSource::Device(f) = f { + f.read_exact(chunk) + .map_err(|_| Error::Unknown) + } else { + syscall_getrandom(chunk) + .map_err(|_| Error::Unknown) + } + })?; + } + Ok(()) +} + +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/wasm32_bindgen.rs b/src/wasm32_bindgen.rs new file mode 100644 index 00000000..6de8f046 --- /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 or the MIT license +// , 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 00000000..7db8afea --- /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 or the MIT license +// , 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 00000000..3d0c14e2 --- /dev/null +++ b/src/windows.rs @@ -0,0 +1,23 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , 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 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(Error::Unknown); } + Ok(()) +} diff --git a/tests/mod.rs b/tests/mod.rs new file mode 100644 index 00000000..47db835e --- /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(not(any(target_arch = "wasm32", target_arch = "asmjs")))] +#[test] +fn test_os_rng_tasks() { + 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(); + } +} From 9420df9149f4a4bdb56c022b6861c95662bbafdb Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Tue, 5 Feb 2019 09:03:55 +0000 Subject: [PATCH 02/13] remove fuchsia loop per https://github.com/rust-random/getrandom/pull/6#discussion_r253737722 --- src/fuchsia.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/fuchsia.rs b/src/fuchsia.rs index d0796ad6..d8ce1bf1 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -15,8 +15,6 @@ extern { } pub fn getrandom(&mut self, dest: &mut [u8]) -> Result<(), Error> { - for chunk in dest.chunks(256) { - unsafe { zx_cprng_draw(chunk.as_mut_ptr(), chunk.len()) }; - } + unsafe { zx_cprng_draw(chunk.as_mut_ptr(), chunk.len()) }; Ok(()) } From 99a1310986e1ca2c37f951de5fe0b762b26a5f6f Mon Sep 17 00:00:00 2001 From: newpavlov Date: Tue, 5 Feb 2019 13:00:43 +0300 Subject: [PATCH 03/13] use fuchsia-cprng --- Cargo.toml | 3 +++ src/fuchsia.rs | 11 ++++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ced82aca..302ea636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,9 @@ 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/src/fuchsia.rs b/src/fuchsia.rs index d8ce1bf1..b152cde1 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -7,14 +7,11 @@ // except according to those terms. //! Implementation for Fuchsia Zircon -use super::Error; +extern crate fuchsia_cprng; -#[link(name = "zircon")] -extern { - fn zx_cprng_draw(buffer: *mut u8, len: usize); -} +use super::Error; -pub fn getrandom(&mut self, dest: &mut [u8]) -> Result<(), Error> { - unsafe { zx_cprng_draw(chunk.as_mut_ptr(), chunk.len()) }; +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { + fuchsia_cprng::cprng_draw(dest); Ok(()) } From d8c356b9e72331427eefafdad9ab435be48e4bba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Feb 2019 18:16:33 +0300 Subject: [PATCH 04/13] remove wasm-bindgen shims --- src/lib.rs | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 9df8ae09..becf9c05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,57 +101,3 @@ mod_use!( dummy ); -// Due to rustwasm/wasm-bindgen#201 this can't be defined in the inner os -// modules, so hack around it for now and place it at the root. -#[cfg(all(feature = "wasm-bindgen", target_arch = "wasm32"))] -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub mod __wbg_shims { - - // `extern { type Foo; }` isn't supported on 1.22 syntactically, so use a - // macro to work around that. - macro_rules! rust_122_compat { - ($($t:tt)*) => ($($t)*) - } - - rust_122_compat! { - extern crate wasm_bindgen; - - pub use wasm_bindgen::prelude::*; - - #[wasm_bindgen] - extern "C" { - pub type Function; - #[wasm_bindgen(constructor)] - pub fn new(s: &str) -> Function; - #[wasm_bindgen(method)] - pub fn call(this: &Function, self_: &JsValue) -> JsValue; - - pub type This; - #[wasm_bindgen(method, getter, structural, js_name = self)] - pub fn self_(me: &This) -> JsValue; - #[wasm_bindgen(method, getter, structural)] - pub fn crypto(me: &This) -> JsValue; - - #[derive(Clone, Debug)] - pub type BrowserCrypto; - - // TODO: these `structural` annotations here ideally wouldn't be here to - // avoid a JS shim, but for now with feature detection they're - // unavoidable. - #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] - pub fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; - #[wasm_bindgen(method, js_name = getRandomValues, structural)] - pub fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); - - #[wasm_bindgen(js_name = require)] - pub fn node_require(s: &str) -> NodeCrypto; - - #[derive(Clone, Debug)] - pub type NodeCrypto; - - #[wasm_bindgen(method, js_name = randomFillSync, structural)] - pub fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); - } - } -} From 8f6edad1bc94ddc081b5e2de52f1c6f33a5d6384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Feb 2019 20:59:26 +0300 Subject: [PATCH 05/13] add benchmarks --- benches/mod.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 benches/mod.rs diff --git a/benches/mod.rs b/benches/mod.rs new file mode 100644 index 00000000..cf9747aa --- /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; +} + From 1049ce888e0394a9d4ef8941cbd7792a65965ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Feb 2019 21:57:35 +0300 Subject: [PATCH 06/13] review updates --- src/dragonfly_haiku.rs | 14 +++---------- src/emscripten.rs | 26 +++++++++--------------- src/freebsd.rs | 5 +++-- src/fuchsia.rs | 1 + src/lib.rs | 11 ++++++++++ src/linux_android.rs | 44 +++++++++++++++++----------------------- src/macos.rs | 2 -- src/netbsd.rs | 23 ++++++++------------- src/openbsd_bitrig.rs | 5 +++-- src/redox.rs | 14 +++---------- src/sgx.rs | 4 ++-- src/solaris.rs | 46 +++++++++++++++++++----------------------- src/utils.rs | 21 +++++++++++++++++++ 13 files changed, 104 insertions(+), 112 deletions(-) create mode 100644 src/utils.rs diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs index 53dcfeeb..5e69da15 100644 --- a/src/dragonfly_haiku.rs +++ b/src/dragonfly_haiku.rs @@ -8,24 +8,16 @@ //! Implementation for DragonFly / Haiku use super::Error; +use super::utils::use_init; use std::fs::File; use std::io::Read; use std::cell::RefCell; -use std::ops::DerefMut; thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_FILE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut Option = f.deref_mut(); - if let Some(f) = f { - f.read_exact(dest) - } else { - let mut rng_file = File::open("/dev/random")?; - rng_file.read_exact(dest)?; - *f = Some(rng_file); - Ok(()) - } + use_init(f, || File::open("/dev/random"), |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } + diff --git a/src/emscripten.rs b/src/emscripten.rs index 28bcd052..10830475 100644 --- a/src/emscripten.rs +++ b/src/emscripten.rs @@ -6,12 +6,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Implementation for DragonFly / Haiku / Emscripten +//! Implementation for Emscripten use super::Error; use std::fs::File; use std::io::Read; use std::cell::RefCell; -use std::ops::DerefMut; +use super::utils::use_init; thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); @@ -20,19 +20,13 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { // bytes. `crypto.randomBytes` documents: "To minimize threadpool // task length variation, partition large randomBytes requests when // doing so as part of fulfilling a client request. - for chunk in dest.chunks_mut(65536) { - RNG_FILE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut Option = f.deref_mut(); - if let Some(f) = f { - f.read_exact(chunk) - } else { - let mut rng_file = File::open("/dev/random")?; - rng_file.read_exact(chunk)?; - *f = Some(rng_file); - Ok(()) + RNG_FILE.with(|f| { + use_init(f, || File::open("/dev/random"), |f| { + for chunk in dest.chunks_mut(65536) { + f.read_exact(chunk)?; } - }).map_err(|_| Error::Unknown)?; - } - Ok(()) + Ok(()) + }) + }).map_err(|_| Error::Unknown) } + diff --git a/src/freebsd.rs b/src/freebsd.rs index a415b905..c197f7a7 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -10,7 +10,7 @@ extern crate libc; use super::Error; -use std::ptr; +use core::ptr; pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { let mib = [libc::CTL_KERN, libc::KERN_ARND]; @@ -28,4 +28,5 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } } Ok(()) -} \ No newline at end of file +} + diff --git a/src/fuchsia.rs b/src/fuchsia.rs index b152cde1..e429f6b7 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -15,3 +15,4 @@ 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 becf9c05..c17a8191 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,17 @@ ))] #[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; diff --git a/src/linux_android.rs b/src/linux_android.rs index 13179b7b..6f8bf362 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -11,20 +11,19 @@ extern crate std; 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::ops::DerefMut; enum RngSource { GetRandom, Device(File), - None, } thread_local!( - static RNG_SOURCE: RefCell = RefCell::new(RngSource::None); + static RNG_SOURCE: RefCell> = RefCell::new(None); ); fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { @@ -39,30 +38,24 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_SOURCE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut RngSource = f.deref_mut(); - if let RngSource::None = f { - *f = if is_getrandom_available() { + use_init(f, + || { + let s = if is_getrandom_available() { RngSource::GetRandom } else { - let mut buf = [0u8; 1]; - File::open("/dev/random") - .and_then(|mut f| f.read_exact(&mut buf)) - .map_err(|_| Error::Unknown)?; - let mut rng_file = File::open("/dev/urandom") - .map_err(|_| Error::Unknown)?; - RngSource::Device(rng_file) + // read one byte from "/dev/random" to ensure that + // OS RNG has initialized + File::open("/dev/random")?.read_exact(&mut [0u8; 1])?; + RngSource::Device(File::open("/dev/urandom")?) + }; + Ok(s) + }, |f| { + match f { + RngSource::GetRandom => syscall_getrandom(dest), + RngSource::Device(f) => f.read_exact(dest), } - } - if let RngSource::Device(f) = f { - f.read_exact(dest) - .map_err(|_| Error::Unknown) - } else { - syscall_getrandom(dest) - .map_err(|_| Error::Unknown) - } - })?; - Ok(()) + }).map_err(|_| Error::Unknown) + }) } fn is_getrandom_available() -> bool { @@ -76,8 +69,7 @@ fn is_getrandom_available() -> bool { 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, + Err(err) => err.raw_os_error() != Some(libc::ENOSYS), }; AVAILABLE.store(available, Ordering::Relaxed); }); diff --git a/src/macos.rs b/src/macos.rs index 57a66ee0..0b0e8441 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -7,8 +7,6 @@ // except according to those terms. //! Implementation for MacOS / iOS -extern crate libc; - use super::Error; // TODO: check correctness diff --git a/src/netbsd.rs b/src/netbsd.rs index 02c53c0f..3142a42e 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -9,28 +9,21 @@ //! 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::ops::DerefMut; thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_FILE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut Option = f.deref_mut(); - if let Some(f) = f { - f.read_exact(dest) - } else { - // read one byte from /dev/random to ensure that RNG is bootstrapped - let mut buf = [0u8]; - File::open("/dev/random")?.read_exact(&mut buf)?; - - let mut rng_file = File::open("/dev/urandom")?; - rng_file.read_exact(dest)?; - *f = Some(rng_file); - Ok(()) - } + use_init(f, || { + // read one byte from "/dev/random" to ensure that + // OS RNG has initialized + File::open("/dev/random")?.read_exact(&mut [0u8; 1])?; + File::open("/dev/urandom") + }, |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } + diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs index 2cfe3ff0..bc26aecd 100644 --- a/src/openbsd_bitrig.rs +++ b/src/openbsd_bitrig.rs @@ -15,8 +15,8 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { let ret = unsafe { libc::getentropy( - dest.as_mut_ptr() as *mut libc::c_void, - dest.len() + chunk.as_mut_ptr() as *mut libc::c_void, + chunk.len() ) }; if ret == -1 { @@ -25,3 +25,4 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } Ok(()) } + diff --git a/src/redox.rs b/src/redox.rs index a2ac7852..5e2e7c39 100644 --- a/src/redox.rs +++ b/src/redox.rs @@ -8,24 +8,16 @@ //! Implementation for Redox use super::Error; +use super::utils::use_init; use std::fs::File; use std::io::Read; use std::cell::RefCell; -use std::ops::DerefMut; thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_FILE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut Option = f.deref_mut(); - if let Some(f) = f { - f.read_exact(dest) - } else { - let mut rng_file = File::open("rand:")?; - rng_file.read_exact(dest)?; - *f = Some(rng_file); - Ok(()) - } + use_init(f, || File::open("rand:"), |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } + diff --git a/src/sgx.rs b/src/sgx.rs index 9a2c4156..7fd90c0f 100644 --- a/src/sgx.rs +++ b/src/sgx.rs @@ -12,8 +12,8 @@ use super::Error; use core::{mem, ptr}; use core::arch::x86_64::_rdrand64_step; -//#[cfg(not(target_feature = "rdrand"))] -//compile_error!("enable rdrand target feature!"); +#[cfg(not(target_feature = "rdrand"))] +compile_error!("enable rdrand target feature!"); const RETRY_LIMIT: usize = 32; diff --git a/src/solaris.rs b/src/solaris.rs index 3caa795d..032a61b8 100644 --- a/src/solaris.rs +++ b/src/solaris.rs @@ -30,11 +30,10 @@ use std::ops::DerefMut; enum RngSource { GetRandom, Device(File), - None, } thread_local!( - static RNG_SOURCE: RefCell = RefCell::new(RngSource::None); + static RNG_SOURCE: RefCell> = RefCell::new(None); ); fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { @@ -55,31 +54,28 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { } pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { - // The documentation says 1024 is the maximum for getrandom, - // but 1040 for /dev/random. - for chunk in dest.chunks_mut(1024) { - RNG_SOURCE.with(|f| { - let mut f = f.borrow_mut(); - let f: &mut RngSource = f.deref_mut(); - if let RngSource::None = f { - *f = if is_getrandom_available() { - RngSource::GetRandom - } else { - let mut rng_file = File::open("/dev/random") - .map_err(|_| Error::Unknown)?; - RngSource::Device(rng_file) - } - } - if let RngSource::Device(f) = f { - f.read_exact(chunk) - .map_err(|_| Error::Unknown) + // 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 { - syscall_getrandom(chunk) - .map_err(|_| Error::Unknown) + 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) + }, } - })?; - } - Ok(()) + }) + }).map_err(|_| Error::Unknown) } fn is_getrandom_available() -> bool { diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 00000000..14bb8eba --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,21 @@ +use std::cell::RefCell; +use std::ops::DerefMut; +use std::{io, hint}; + +pub(crate) fn use_init(f: &RefCell>, init_f: F, mut use_f: U) + -> io::Result<()> + where F: FnOnce() -> io::Result, U: FnMut(&mut T) -> io::Result<()> +{ + let mut f = f.borrow_mut(); + let f: &mut Option = f.deref_mut(); + match f { + None => *f = Some(init_f()?), + _ => (), + } + + match f { + Some(f) => use_f(f), + None => unsafe { hint::unreachable_unchecked() }, + } +} + From 39ba5d2c924b3c931bfe893e49d62882f4f83465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Feb 2019 22:00:30 +0300 Subject: [PATCH 07/13] remove redundant extern crate --- src/linux_android.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/linux_android.rs b/src/linux_android.rs index 6f8bf362..91d86b51 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -7,7 +7,6 @@ // except according to those terms. //! Implementation for Linux / Android -extern crate std; extern crate libc; use super::Error; From 9855b52df2cfcf334a6f87e2834d50b9d45d375b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Tue, 5 Feb 2019 22:07:56 +0300 Subject: [PATCH 08/13] change test cfg --- tests/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mod.rs b/tests/mod.rs index 47db835e..61719e5b 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -25,9 +25,9 @@ fn test_huge() { getrandom(&mut huge).unwrap(); } -#[cfg(not(any(target_arch = "wasm32", target_arch = "asmjs")))] +#[cfg(any(unix, windows, target_os="redox", target_os = "fuchsia"))] #[test] -fn test_os_rng_tasks() { +fn test_multithreading() { use std::sync::mpsc::channel; use std::thread; From 5ebbaf787e92c7b3f896ffb11332088ae916e05f Mon Sep 17 00:00:00 2001 From: newpavlov Date: Wed, 6 Feb 2019 13:04:47 +0300 Subject: [PATCH 09/13] remove unsafe and newlines, add notice --- src/dragonfly_haiku.rs | 1 - src/emscripten.rs | 1 - src/freebsd.rs | 1 - src/fuchsia.rs | 1 - src/lib.rs | 1 - src/netbsd.rs | 1 - src/openbsd_bitrig.rs | 1 - src/redox.rs | 1 - src/utils.rs | 15 ++++++++++++--- 9 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs index 5e69da15..a30baeef 100644 --- a/src/dragonfly_haiku.rs +++ b/src/dragonfly_haiku.rs @@ -20,4 +20,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { use_init(f, || File::open("/dev/random"), |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } - diff --git a/src/emscripten.rs b/src/emscripten.rs index 10830475..85764179 100644 --- a/src/emscripten.rs +++ b/src/emscripten.rs @@ -29,4 +29,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { }) }).map_err(|_| Error::Unknown) } - diff --git a/src/freebsd.rs b/src/freebsd.rs index c197f7a7..93916438 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -29,4 +29,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } Ok(()) } - diff --git a/src/fuchsia.rs b/src/fuchsia.rs index e429f6b7..b152cde1 100644 --- a/src/fuchsia.rs +++ b/src/fuchsia.rs @@ -15,4 +15,3 @@ 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 c17a8191..dad44fa0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,4 +111,3 @@ mod_use!( ))), dummy ); - diff --git a/src/netbsd.rs b/src/netbsd.rs index 3142a42e..c62d3ea8 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -26,4 +26,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { }, |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } - diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs index bc26aecd..89fee4ea 100644 --- a/src/openbsd_bitrig.rs +++ b/src/openbsd_bitrig.rs @@ -25,4 +25,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } Ok(()) } - diff --git a/src/redox.rs b/src/redox.rs index 5e2e7c39..5ac98174 100644 --- a/src/redox.rs +++ b/src/redox.rs @@ -20,4 +20,3 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { use_init(f, || File::open("rand:"), |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) } - diff --git a/src/utils.rs b/src/utils.rs index 14bb8eba..95f567b8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,17 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. use std::cell::RefCell; use std::ops::DerefMut; -use std::{io, hint}; +use std::io; +/// 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(f: &RefCell>, init_f: F, mut use_f: U) -> io::Result<()> where F: FnOnce() -> io::Result, U: FnMut(&mut T) -> io::Result<()> @@ -15,7 +25,6 @@ pub(crate) fn use_init(f: &RefCell>, init_f: F, mut use_f: U) match f { Some(f) => use_f(f), - None => unsafe { hint::unreachable_unchecked() }, + None => unreachable!(), } } - From f860bde8d8fcbc693fffef9df18cd7eee99cbb16 Mon Sep 17 00:00:00 2001 From: newpavlov Date: Wed, 6 Feb 2019 13:47:36 +0300 Subject: [PATCH 10/13] use atomic bool to check RNG initialization --- src/lib.rs | 2 -- src/linux_android.rs | 9 +++++++-- src/netbsd.rs | 8 +++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index dad44fa0..463ce879 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,8 +5,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. - - #![no_std] #[cfg(any( diff --git a/src/linux_android.rs b/src/linux_android.rs index 91d86b51..74960554 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -15,6 +15,9 @@ 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, @@ -44,7 +47,10 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } else { // read one byte from "/dev/random" to ensure that // OS RNG has initialized - File::open("/dev/random")?.read_exact(&mut [0u8; 1])?; + 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) @@ -58,7 +64,6 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { } 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; diff --git a/src/netbsd.rs b/src/netbsd.rs index c62d3ea8..b0f9bdea 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -13,6 +13,9 @@ 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> = RefCell::new(None)); @@ -21,7 +24,10 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { use_init(f, || { // read one byte from "/dev/random" to ensure that // OS RNG has initialized - File::open("/dev/random")?.read_exact(&mut [0u8; 1])?; + 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") }, |f| f.read_exact(dest)) }).map_err(|_| Error::Unknown) From 1e2a5aa75e89e95f8b13c37a98177facd7c6058f Mon Sep 17 00:00:00 2001 From: newpavlov Date: Wed, 6 Feb 2019 14:51:04 +0300 Subject: [PATCH 11/13] new error --- Cargo.toml | 4 +++ src/cloudabi.rs | 4 ++- src/dragonfly_haiku.rs | 7 +++-- src/dummy.rs | 6 ++-- src/emscripten.rs | 4 +-- src/error.rs | 70 +++++++++++++++++++++++++++++++++++------- src/freebsd.rs | 3 +- src/lib.rs | 13 ++------ src/linux_android.rs | 4 +-- src/macos.rs | 3 +- src/netbsd.rs | 6 ++-- src/openbsd_bitrig.rs | 3 +- src/redox.rs | 7 +++-- src/sgx.rs | 4 +-- src/solaris.rs | 6 ++-- src/utils.rs | 10 +++--- src/windows.rs | 3 +- 17 files changed, 106 insertions(+), 51 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 302ea636..39904ac4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,7 @@ fuchsia-cprng = "0.1" [target.wasm32-unknown-unknown.dependencies] wasm-bindgen = { version = "0.2.12", optional = true } stdweb = { version = "0.4", optional = true } + +[features] +# Forces syscall usage on Linux, Android and Solaris +force_syscall = [] diff --git a/src/cloudabi.rs b/src/cloudabi.rs index 27c5d256..77515050 100644 --- a/src/cloudabi.rs +++ b/src/cloudabi.rs @@ -16,6 +16,8 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { if errno == 0 { Ok(()) } else { - Err(Error::Unknown) + Err(Error(unsafe { + NonZeroU32::new_unchecked(errno as u32) + })) } } diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs index a30baeef..7691616e 100644 --- a/src/dragonfly_haiku.rs +++ b/src/dragonfly_haiku.rs @@ -17,6 +17,9 @@ thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_FILE.with(|f| { - use_init(f, || File::open("/dev/random"), |f| f.read_exact(dest)) - }).map_err(|_| Error::Unknown) + 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 index 8414eb68..41fa39fd 100644 --- a/src/dummy.rs +++ b/src/dummy.rs @@ -7,9 +7,9 @@ // except according to those terms. //! A dummy implementation for unsupported targets which always returns -//! `Err(Error::Unavailable)` -use super::Error; +//! `Err(UNAVAILABLE_ERROR)` +use super::UNAVAILABLE_ERROR; pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { - Err(Error::Unavailable) + Err(UNAVAILABLE_ERROR) } diff --git a/src/emscripten.rs b/src/emscripten.rs index 85764179..446e4634 100644 --- a/src/emscripten.rs +++ b/src/emscripten.rs @@ -21,11 +21,11 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { // 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"), |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(()) }) - }).map_err(|_| Error::Unknown) + }) } diff --git a/src/error.rs b/src/error.rs index 9cbb7805..fab97b77 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,16 +6,64 @@ // 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 enum Error { - /// Call was interrupted. - /// - /// Typically it can be retried. - Interrupted, - /// RNG source is unavailable on a given system. - Unavailable, - /// Unknown error. - Unknown, - #[doc(hidden)] - __Nonexhaustive, +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 for Error { + fn from(err: io::Error) -> Self { + err.raw_os_error() + .map(|code| Error(unsafe { + // all supported targets use 0 as success code + NonZeroU32::new_unchecked(code as u32) + })) + // in practice this should never happen + .unwrap_or(UNKNOWN_ERROR) + } +} + +#[cfg(not(target_env = "sgx"))] +impl Into 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 index 93916438..ac990f3f 100644 --- a/src/freebsd.rs +++ b/src/freebsd.rs @@ -11,6 +11,7 @@ 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]; @@ -24,7 +25,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { ) }; if ret == -1 || len != chunk.len() { - return Err(Error::Unknown); + return Err(io::Error::last_os_error().into()); } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 463ce879..104b5884 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,16 +7,7 @@ // except according to those terms. #![no_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", -))] +#[cfg(not(target_env = "sgx"))] #[macro_use] extern crate std; #[cfg(any( @@ -31,7 +22,7 @@ ))] mod utils; mod error; -pub use error::Error; +pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR}; macro_rules! mod_use { ($cond:meta, $module:ident) => { diff --git a/src/linux_android.rs b/src/linux_android.rs index 74960554..db6c6e48 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -58,8 +58,8 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { match f { RngSource::GetRandom => syscall_getrandom(dest), RngSource::Device(f) => f.read_exact(dest), - } - }).map_err(|_| Error::Unknown) + }.map_err(From::from) + }) }) } diff --git a/src/macos.rs b/src/macos.rs index 0b0e8441..3855f1ac 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -8,6 +8,7 @@ //! Implementation for MacOS / iOS use super::Error; +use std::io; // TODO: check correctness #[allow(non_upper_case_globals)] @@ -29,7 +30,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { ) }; if ret == -1 { - Err(Error::Unknown) + Err(io::Error::last_os_error().into()) } else { Ok(()) } diff --git a/src/netbsd.rs b/src/netbsd.rs index b0f9bdea..f2aa68ca 100644 --- a/src/netbsd.rs +++ b/src/netbsd.rs @@ -28,7 +28,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { File::open("/dev/random")?.read_exact(&mut [0u8; 1])?; RNG_INIT.store(true, Ordering::Relaxed) } - File::open("/dev/urandom") - }, |f| f.read_exact(dest)) - }).map_err(|_| Error::Unknown) + 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 index 89fee4ea..f8096fa2 100644 --- a/src/openbsd_bitrig.rs +++ b/src/openbsd_bitrig.rs @@ -10,6 +10,7 @@ extern crate libc; use super::Error; +use std::io; pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { @@ -20,7 +21,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { ) }; if ret == -1 { - return Err(Error::Unknown); + return Err(io::Error::last_os_error().into()); } } Ok(()) diff --git a/src/redox.rs b/src/redox.rs index 5ac98174..0c7e8578 100644 --- a/src/redox.rs +++ b/src/redox.rs @@ -17,6 +17,9 @@ thread_local!(static RNG_FILE: RefCell> = RefCell::new(None)); pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { RNG_FILE.with(|f| { - use_init(f, || File::open("rand:"), |f| f.read_exact(dest)) - }).map_err(|_| Error::Unknown) + 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 index 7fd90c0f..a98f55f1 100644 --- a/src/sgx.rs +++ b/src/sgx.rs @@ -7,7 +7,7 @@ // except according to those terms. //! Implementation for SGX using RDRAND instruction -use super::Error; +use super::{Error, UNKNOWN_ERROR}; use core::{mem, ptr}; use core::arch::x86_64::_rdrand64_step; @@ -26,7 +26,7 @@ fn get_rand_u64() -> Result { } }; } - Err(Error::Unknown) + Err(UNKNOWN_ERROR) } pub fn getrandom(mut dest: &mut [u8]) -> Result<(), Error> { diff --git a/src/solaris.rs b/src/solaris.rs index 032a61b8..e27e4bc0 100644 --- a/src/solaris.rs +++ b/src/solaris.rs @@ -36,7 +36,7 @@ thread_local!( static RNG_SOURCE: RefCell> = RefCell::new(None); ); -fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { +fn syscall_getrandom(dest: &mut [u8]) -> Result<(), Error> { // repalce with libc? const SYS_GETRANDOM: libc::c_long = 143; @@ -48,7 +48,7 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> { 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()); + return Err(io::Error::last_os_error().from()); } Ok(()) } @@ -71,7 +71,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { syscall_getrandom(chunk) }, RngSource::Device(f) => for chunk in dest.chunks_mut(1040) { - f.read_exact(dest) + f.read_exact(dest).map_err(From::from) }, } }) diff --git a/src/utils.rs b/src/utils.rs index 95f567b8..5b70bd15 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,16 +5,16 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::RefCell; -use std::ops::DerefMut; -use std::io; +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(f: &RefCell>, init_f: F, mut use_f: U) - -> io::Result<()> - where F: FnOnce() -> io::Result, U: FnMut(&mut T) -> io::Result<()> + -> Result<(), Error> + where F: FnOnce() -> Result, U: FnMut(&mut T) -> Result<(), Error> { let mut f = f.borrow_mut(); let f: &mut Option = f.deref_mut(); diff --git a/src/windows.rs b/src/windows.rs index 3d0c14e2..6507b6cd 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -12,12 +12,13 @@ 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(Error::Unknown); } + if ret == 0 { return Err(io::Error::last_os_error().into()); } Ok(()) } From 9871d55e0a45f291588cde5fa7b9def9ad271ae3 Mon Sep 17 00:00:00 2001 From: newpavlov Date: Wed, 6 Feb 2019 15:01:59 +0300 Subject: [PATCH 12/13] remove unsafe --- src/error.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index fab97b77..a6dd3d7e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,10 +43,8 @@ impl fmt::Display for Error { impl From for Error { fn from(err: io::Error) -> Self { err.raw_os_error() - .map(|code| Error(unsafe { - // all supported targets use 0 as success code - NonZeroU32::new_unchecked(code as u32) - })) + .and_then(|code| NonZeroU32::new(code as u32)) + .map(|code| Error(code)) // in practice this should never happen .unwrap_or(UNKNOWN_ERROR) } From af5035f548fe8ec72dca2fe171a669f403312133 Mon Sep 17 00:00:00 2001 From: Artyom Pavlov Date: Wed, 6 Feb 2019 22:03:33 +0000 Subject: [PATCH 13/13] remove unused feature --- Cargo.toml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39904ac4..302ea636 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,3 @@ fuchsia-cprng = "0.1" [target.wasm32-unknown-unknown.dependencies] wasm-bindgen = { version = "0.2.12", optional = true } stdweb = { version = "0.4", optional = true } - -[features] -# Forces syscall usage on Linux, Android and Solaris -force_syscall = []