Add getrandom wrapper func and documentation
This commit is contained in:
parent
24e7f8ffbb
commit
4374fd7783
15
README.md
15
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,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
|
||||
|
@ -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(())
|
||||
|
@ -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),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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(())
|
||||
}
|
||||
|
108
src/lib.rs
108
src/lib.rs
@ -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§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 +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)
|
||||
}
|
||||
|
@ -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,
|
||||
|| {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
|
@ -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| {
|
||||
|
@ -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)
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user