Add getrandom wrapper func and documentation

This commit is contained in:
Diggory Hardy 2019-02-18 12:09:23 +00:00
parent 24e7f8ffbb
commit 4374fd7783
16 changed files with 135 additions and 16 deletions

View File

@ -9,6 +9,11 @@ A Rust library to securely get random entropy. This crate derives its name from
Linux's `getrandom` function, but is cross platform, roughly supporting the same
set of platforms as Rust's `std` lib.
This is a low-level API. Most users should prefer a high-level random-number
library like [Rand] or a cryptography library.
[Rand]: https://crates.io/crates/rand
## Usage
@ -19,7 +24,15 @@ Add this to your `Cargo.toml`:
getrandom = "0.1"
```
TODO
Then invoke the `getrandom` function:
```rust
fn get_random_buf() -> Result<[u8; 32], getrandom::Error> {
let mut buf = [0u8; 32];
getrandom::getrandom(&mut buf)?;
buf
}
```
# License

View File

@ -11,7 +11,7 @@ extern "C" {
fn cloudabi_sys_random_get(buf: *mut u8, len: usize) -> u16;
}
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
let errno = unsafe { cloudabi_sys_random_get(dest.as_ptr(), dest.len()) };
if errno == 0 {
Ok(())

View File

@ -15,7 +15,7 @@ use std::cell::RefCell;
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f,
|| File::open("/dev/random").map_err(From::from),

View File

@ -10,6 +10,6 @@
//! `Err(UNAVAILABLE_ERROR)`
use super::UNAVAILABLE_ERROR;
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
Err(UNAVAILABLE_ERROR)
}

View File

@ -15,7 +15,7 @@ use super::utils::use_init;
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(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

View File

@ -13,7 +13,7 @@ use super::Error;
use core::ptr;
use std::io;
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(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) {

View File

@ -11,7 +11,7 @@ extern crate fuchsia_cprng;
use super::Error;
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
fuchsia_cprng::cprng_draw(dest);
Ok(())
}

View File

@ -5,6 +5,91 @@
// <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.
//! Interface to the random number generator of the operating system.
//!
//! # Platform sources
//!
//! | OS | interface
//! |------------------|---------------------------------------------------------
//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
//! | Windows | [`RtlGenRandom`][3]
//! | macOS, iOS | [`SecRandomCopyBytes`][4]
//! | FreeBSD | [`kern.arandom`][5]
//! | OpenBSD, Bitrig | [`getentropy`][6]
//! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once
//! | Dragonfly BSD | [`/dev/random`][8]
//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
//! | Fuchsia OS | [`cprng_draw`][11]
//! | Redox | [`rand:`][12]
//! | CloudABI | [`random_get`][13]
//! | Haiku | `/dev/random` (identical to `/dev/urandom`)
//! | SGX | RDRAND
//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
//!
//! Getrandom doesn't have a blanket implementation for all Unix-like operating
//! systems that reads from `/dev/urandom`. This ensures all supported operating
//! systems are using the recommended interface and respect maximum buffer
//! sizes.
//!
//! ## Support for WebAssembly and ams.js
//!
//! The three Emscripten targets `asmjs-unknown-emscripten`,
//! `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use
//! Emscripten's emulation of `/dev/random` on web browsers and Node.js.
//!
//! The bare WASM target `wasm32-unknown-unknown` tries to call the javascript
//! methods directly, using either `stdweb` or `wasm-bindgen` depending on what
//! features are activated for this crate. Note that if both features are
//! enabled `wasm-bindgen` will be used.
//!
//! ## Early boot
//!
//! It is possible that early in the boot process the OS hasn't had enough time
//! yet to collect entropy to securely seed its RNG, especially on virtual
//! machines.
//!
//! Some operating systems always block the thread until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! Others make a best effort to use a seed from before the shutdown and don't
//! document much.
//!
//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and
//! getting an error; in these cases we always choose to block.
//!
//! On Linux (when the `genrandom` system call is not available) and on NetBSD
//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
//! enough entropy yet. To avoid returning low-entropy bytes, we first read from
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
//! # Error handling
//!
//! We always choose failure over returning insecure "random" bytes. In general,
//! on supported platforms, failure is unlikely, though not impossible. If an
//! error does occur, then it is likely that it will occur on every call to
//! `getrandom`, hence after the first successful call one can be reasonably
//! confident that no errors will occur.
//!
//! On unsupported platforms, `getrandom` always fails.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [6]: https://man.openbsd.org/getentropy.2
//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//! [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
//! [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [16]: #support-for-webassembly-and-amsjs
#![no_std]
#[cfg(not(target_env = "sgx"))]
@ -24,12 +109,17 @@ mod utils;
mod error;
pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};
// System-specific implementations.
//
// These should all provide getrandom_os with the same signature as getrandom.
macro_rules! mod_use {
($cond:meta, $module:ident) => {
#[$cond]
mod $module;
#[$cond]
pub use $module::getrandom;
use $module::getrandom_os;
}
}
@ -100,3 +190,19 @@ mod_use!(
))),
dummy
);
/// Fill `dest` with random bytes from the system's preferred random number
/// source.
///
/// This function returns an error on any failure, including partial reads. We
/// make no guarantees regarding the contents of `dest` on error.
///
/// Blocking is possible, at least during early boot; see module documentation.
///
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
getrandom_os(dest)
}

View File

@ -38,7 +38,7 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
Ok(())
}
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
RNG_SOURCE.with(|f| {
use_init(f,
|| {

View File

@ -21,7 +21,7 @@ extern {
) -> c_int;
}
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
let ret = unsafe {
SecRandomCopyBytes(
kSecRandomDefault,

View File

@ -19,7 +19,7 @@ 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> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f, || {
// read one byte from "/dev/random" to ensure that

View File

@ -12,7 +12,7 @@ extern crate libc;
use super::Error;
use std::io;
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
for chunk in dest.chunks_mut(256) {
let ret = unsafe {
libc::getentropy(

View File

@ -15,7 +15,7 @@ use std::cell::RefCell;
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f,
|| File::open("rand:").map_err(From::from),

View File

@ -29,7 +29,7 @@ fn get_rand_u64() -> Result<u64, Error> {
Err(UNKNOWN_ERROR)
}
pub fn getrandom(mut dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(mut dest: &mut [u8]) -> Result<(), Error> {
while dest.len() >= 8 {
let (chunk, left) = {dest}.split_at_mut(8);
dest = left;

View File

@ -53,7 +53,7 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), Error> {
Ok(())
}
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
// The documentation says 1024 is the maximum for getrandom
// and 1040 for /dev/random.
RNG_SOURCE.with(|f| {

View File

@ -15,7 +15,7 @@ use self::winapi::um::winnt::PVOID;
use std::io;
use super::Error;
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_os(dest: &mut [u8]) -> Result<(), Error> {
let ret = unsafe {
RtlGenRandom(dest.as_mut_ptr() as PVOID, dest.len() as ULONG)
};