diff --git a/README.md b/README.md
index a9c8fb087..fc47f2e28 100644
--- a/README.md
+++ b/README.md
@@ -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,25 @@ 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
+}
+```
+
+## Features
+
+This library is `no_std` compatible on SGX but requires `std` on most platforms.
+
+For WebAssembly (`wasm32`), Enscripten targets are supported directly; otherwise
+one of the following features must be enabled:
+
+- [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen)
+- [`stdweb`](https://crates.io/crates/stdweb)
# License
diff --git a/src/cloudabi.rs b/src/cloudabi.rs
index 775150502..f30e064a8 100644
--- a/src/cloudabi.rs
+++ b/src/cloudabi.rs
@@ -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_inner(dest: &mut [u8]) -> Result<(), Error> {
let errno = unsafe { cloudabi_sys_random_get(dest.as_ptr(), dest.len()) };
if errno == 0 {
Ok(())
diff --git a/src/dragonfly_haiku.rs b/src/dragonfly_haiku.rs
index 7691616ed..751266be3 100644
--- a/src/dragonfly_haiku.rs
+++ b/src/dragonfly_haiku.rs
@@ -15,7 +15,7 @@ use std::cell::RefCell;
thread_local!(static RNG_FILE: RefCell> = RefCell::new(None));
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f,
|| File::open("/dev/random").map_err(From::from),
diff --git a/src/dummy.rs b/src/dummy.rs
index 41fa39fd3..96acb910c 100644
--- a/src/dummy.rs
+++ b/src/dummy.rs
@@ -10,6 +10,6 @@
//! `Err(UNAVAILABLE_ERROR)`
use super::UNAVAILABLE_ERROR;
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
Err(UNAVAILABLE_ERROR)
}
diff --git a/src/emscripten.rs b/src/emscripten.rs
index 446e46345..dd98985ab 100644
--- a/src/emscripten.rs
+++ b/src/emscripten.rs
@@ -15,7 +15,7 @@ use super::utils::use_init;
thread_local!(static RNG_FILE: RefCell > = RefCell::new(None));
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(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
diff --git a/src/error.rs b/src/error.rs
index a6dd3d7e3..b3fca9d8d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -12,18 +12,29 @@ use core::fmt;
#[cfg(not(target_env = "sgx"))]
use std::{io, error};
+/// An unknown error.
pub const UNKNOWN_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x756e6b6e) // "unkn"
});
+/// No generator is available.
pub const UNAVAILABLE_ERROR: Error = Error(unsafe {
NonZeroU32::new_unchecked(0x4e416e61) // "NAna"
});
+/// The error type.
+///
+/// This type is small and no-std compatible.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Error(NonZeroU32);
impl Error {
+ /// Extract the error code.
+ ///
+ /// This may equal one of the codes defined in this library or may be a
+ /// system error code.
+ ///
+ /// One may attempt to format this error via the `Display` implementation.
pub fn code(&self) -> NonZeroU32 {
self.0
}
@@ -51,9 +62,9 @@ impl From for Error {
}
#[cfg(not(target_env = "sgx"))]
-impl Into for Error {
- fn into(self) -> io::Error {
- match self {
+impl From for io::Error {
+ fn from(err: Error) -> Self {
+ match err {
UNKNOWN_ERROR => io::Error::new(io::ErrorKind::Other,
"getrandom error: unknown"),
UNAVAILABLE_ERROR => io::Error::new(io::ErrorKind::Other,
diff --git a/src/freebsd.rs b/src/freebsd.rs
index ac990f3f4..9756d1ba8 100644
--- a/src/freebsd.rs
+++ b/src/freebsd.rs
@@ -13,7 +13,7 @@ use super::Error;
use core::ptr;
use std::io;
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(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) {
diff --git a/src/fuchsia.rs b/src/fuchsia.rs
index b152cde15..39741eb40 100644
--- a/src/fuchsia.rs
+++ b/src/fuchsia.rs
@@ -11,7 +11,7 @@ extern crate fuchsia_cprng;
use super::Error;
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
fuchsia_cprng::cprng_draw(dest);
Ok(())
}
diff --git a/src/lib.rs b/src/lib.rs
index 104b58844..f60b8ae64 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,6 +5,92 @@
// , 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. If neither feature is enabled,
+//! `getrandom` will always fail.
+//!
+//! ## 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§ion=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 +110,17 @@ mod utils;
mod error;
pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};
+
+// System-specific implementations.
+//
+// These should all provide getrandom_inner 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_inner;
}
}
@@ -100,3 +191,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_inner(dest)
+}
diff --git a/src/linux_android.rs b/src/linux_android.rs
index db6c6e481..13dfb3b7c 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -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_inner(dest: &mut [u8]) -> Result<(), Error> {
RNG_SOURCE.with(|f| {
use_init(f,
|| {
diff --git a/src/macos.rs b/src/macos.rs
index 3855f1ac6..5cb541e39 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -21,7 +21,7 @@ extern {
) -> c_int;
}
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
let ret = unsafe {
SecRandomCopyBytes(
kSecRandomDefault,
diff --git a/src/netbsd.rs b/src/netbsd.rs
index f2aa68ca4..6a59509b4 100644
--- a/src/netbsd.rs
+++ b/src/netbsd.rs
@@ -19,7 +19,7 @@ static RNG_INIT: AtomicBool = ATOMIC_BOOL_INIT;
thread_local!(static RNG_FILE: RefCell> = RefCell::new(None));
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f, || {
// read one byte from "/dev/random" to ensure that
diff --git a/src/openbsd_bitrig.rs b/src/openbsd_bitrig.rs
index f8096fa29..abdea21ce 100644
--- a/src/openbsd_bitrig.rs
+++ b/src/openbsd_bitrig.rs
@@ -12,7 +12,7 @@ extern crate libc;
use super::Error;
use std::io;
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
for chunk in dest.chunks_mut(256) {
let ret = unsafe {
libc::getentropy(
diff --git a/src/redox.rs b/src/redox.rs
index 0c7e85786..23026664a 100644
--- a/src/redox.rs
+++ b/src/redox.rs
@@ -15,7 +15,7 @@ use std::cell::RefCell;
thread_local!(static RNG_FILE: RefCell > = RefCell::new(None));
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
RNG_FILE.with(|f| {
use_init(f,
|| File::open("rand:").map_err(From::from),
diff --git a/src/sgx.rs b/src/sgx.rs
index a98f55f11..7517d1f44 100644
--- a/src/sgx.rs
+++ b/src/sgx.rs
@@ -29,7 +29,7 @@ fn get_rand_u64() -> Result {
Err(UNKNOWN_ERROR)
}
-pub fn getrandom(mut dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(mut dest: &mut [u8]) -> Result<(), Error> {
while dest.len() >= 8 {
let (chunk, left) = {dest}.split_at_mut(8);
dest = left;
diff --git a/src/solaris.rs b/src/solaris.rs
index e27e4bc0d..742578a48 100644
--- a/src/solaris.rs
+++ b/src/solaris.rs
@@ -48,12 +48,12 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), 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().from());
+ return Err(io::Error::last_os_error().into());
}
Ok(())
}
-pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
// The documentation says 1024 is the maximum for getrandom
// and 1040 for /dev/random.
RNG_SOURCE.with(|f| {
@@ -75,7 +75,7 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
},
}
})
- }).map_err(|_| Error::Unknown)
+ })
}
fn is_getrandom_available() -> bool {
diff --git a/src/windows.rs b/src/windows.rs
index 6507b6cdd..1063bdd80 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -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_inner(dest: &mut [u8]) -> Result<(), Error> {
let ret = unsafe {
RtlGenRandom(dest.as_mut_ptr() as PVOID, dest.len() as ULONG)
};