first commit
This commit is contained in:
parent
c9440a20bd
commit
71a3cb8a96
19
Cargo.toml
19
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 }
|
||||
|
21
src/cloudabi.rs
Normal file
21
src/cloudabi.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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::Unknown)
|
||||
}
|
||||
}
|
31
src/dragonfly_haiku.rs
Normal file
31
src/dragonfly_haiku.rs
Normal file
@ -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 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<Option<File>> = 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<File> = 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)
|
||||
}
|
15
src/dummy.rs
Normal file
15
src/dummy.rs
Normal file
@ -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(Error::Unavailable)`
|
||||
use super::Error;
|
||||
|
||||
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
|
||||
Err(Error::Unavailable)
|
||||
}
|
38
src/emscripten.rs
Normal file
38
src/emscripten.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 / 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<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.
|
||||
for chunk in dest.chunks_mut(65536) {
|
||||
RNG_FILE.with(|f| {
|
||||
let mut f = f.borrow_mut();
|
||||
let f: &mut Option<File> = 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(())
|
||||
}
|
21
src/error.rs
Normal file
21
src/error.rs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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.
|
||||
|
||||
#[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,
|
||||
}
|
31
src/freebsd.rs
Normal file
31
src/freebsd.rs
Normal file
@ -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 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(())
|
||||
}
|
22
src/fuchsia.rs
Normal file
22
src/fuchsia.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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
|
||||
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(())
|
||||
}
|
150
src/lib.rs
150
src/lib.rs
@ -5,3 +5,153 @@
|
||||
// <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(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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
86
src/linux_android.rs
Normal file
86
src/linux_android.rs
Normal file
@ -0,0 +1,86 @@
|
||||
// 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 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<RngSource> = 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)
|
||||
}
|
38
src/macos.rs
Normal file
38
src/macos.rs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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
|
||||
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(())
|
||||
}
|
||||
}
|
36
src/netbsd.rs
Normal file
36
src/netbsd.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::cell::RefCell;
|
||||
use std::ops::DerefMut;
|
||||
|
||||
thread_local!(static RNG_FILE: RefCell<Option<File>> = 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<File> = 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)
|
||||
}
|
27
src/openbsd_bitrig.rs
Normal file
27
src/openbsd_bitrig.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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;
|
||||
|
||||
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(())
|
||||
}
|
31
src/redox.rs
Normal file
31
src/redox.rs
Normal file
@ -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 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<Option<File>> = 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<File> = 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)
|
||||
}
|
48
src/sgx.rs
Normal file
48
src/sgx.rs
Normal file
@ -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;
|
||||
|
||||
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(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(())
|
||||
}
|
103
src/solaris.rs
Normal file
103
src/solaris.rs
Normal file
@ -0,0 +1,103 @@
|
||||
// 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),
|
||||
None,
|
||||
}
|
||||
|
||||
thread_local!(
|
||||
static RNG_SOURCE: RefCell<RngSource> = 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)
|
||||
}
|
9
src/wasm32_bindgen.rs
Normal file
9
src/wasm32_bindgen.rs
Normal file
@ -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
|
9
src/wasm32_stdweb.rs
Normal file
9
src/wasm32_stdweb.rs
Normal file
@ -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
|
23
src/windows.rs
Normal file
23
src/windows.rs
Normal file
@ -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.
|
||||
|
||||
//! 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(())
|
||||
}
|
55
tests/mod.rs
Normal file
55
tests/mod.rs
Normal file
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user