Skip to content

Commit 10fdedd

Browse files
authored
Merge pull request rust-random#7 from dhardy/master
Add getrandom wrapper func and documentation
2 parents 24e7f8f + 0f371be commit 10fdedd

17 files changed

+162
-21
lines changed

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ A Rust library to securely get random entropy. This crate derives its name from
99
Linux's `getrandom` function, but is cross platform, roughly supporting the same
1010
set of platforms as Rust's `std` lib.
1111

12+
This is a low-level API. Most users should prefer a high-level random-number
13+
library like [Rand] or a cryptography library.
14+
15+
[Rand]: https://crates.io/crates/rand
16+
1217

1318
## Usage
1419

@@ -19,7 +24,25 @@ Add this to your `Cargo.toml`:
1924
getrandom = "0.1"
2025
```
2126

22-
TODO
27+
Then invoke the `getrandom` function:
28+
29+
```rust
30+
fn get_random_buf() -> Result<[u8; 32], getrandom::Error> {
31+
let mut buf = [0u8; 32];
32+
getrandom::getrandom(&mut buf)?;
33+
buf
34+
}
35+
```
36+
37+
## Features
38+
39+
This library is `no_std` compatible on SGX but requires `std` on most platforms.
40+
41+
For WebAssembly (`wasm32`), Enscripten targets are supported directly; otherwise
42+
one of the following features must be enabled:
43+
44+
- [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen)
45+
- [`stdweb`](https://crates.io/crates/stdweb)
2346

2447

2548
# License

src/cloudabi.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern "C" {
1111
fn cloudabi_sys_random_get(buf: *mut u8, len: usize) -> u16;
1212
}
1313

14-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
14+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1515
let errno = unsafe { cloudabi_sys_random_get(dest.as_ptr(), dest.len()) };
1616
if errno == 0 {
1717
Ok(())

src/dragonfly_haiku.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use std::cell::RefCell;
1515

1616
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
1717

18-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
18+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1919
RNG_FILE.with(|f| {
2020
use_init(f,
2121
|| File::open("/dev/random").map_err(From::from),

src/dummy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@
1010
//! `Err(UNAVAILABLE_ERROR)`
1111
use super::UNAVAILABLE_ERROR;
1212

13-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
13+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1414
Err(UNAVAILABLE_ERROR)
1515
}

src/emscripten.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use super::utils::use_init;
1515

1616
thread_local!(static RNG_FILE: RefCell<Option<File>> = RefCell::new(None));
1717

18-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
18+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1919
// `Crypto.getRandomValues` documents `dest` should be at most 65536
2020
// bytes. `crypto.randomBytes` documents: "To minimize threadpool
2121
// task length variation, partition large randomBytes requests when

src/error.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,29 @@ use core::fmt;
1212
#[cfg(not(target_env = "sgx"))]
1313
use std::{io, error};
1414

15+
/// An unknown error.
1516
pub const UNKNOWN_ERROR: Error = Error(unsafe {
1617
NonZeroU32::new_unchecked(0x756e6b6e) // "unkn"
1718
});
1819

20+
/// No generator is available.
1921
pub const UNAVAILABLE_ERROR: Error = Error(unsafe {
2022
NonZeroU32::new_unchecked(0x4e416e61) // "NAna"
2123
});
2224

25+
/// The error type.
26+
///
27+
/// This type is small and no-std compatible.
2328
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
2429
pub struct Error(NonZeroU32);
2530

2631
impl Error {
32+
/// Extract the error code.
33+
///
34+
/// This may equal one of the codes defined in this library or may be a
35+
/// system error code.
36+
///
37+
/// One may attempt to format this error via the `Display` implementation.
2738
pub fn code(&self) -> NonZeroU32 {
2839
self.0
2940
}
@@ -51,9 +62,9 @@ impl From<io::Error> for Error {
5162
}
5263

5364
#[cfg(not(target_env = "sgx"))]
54-
impl Into<io::Error> for Error {
55-
fn into(self) -> io::Error {
56-
match self {
65+
impl From<Error> for io::Error {
66+
fn from(err: Error) -> Self {
67+
match err {
5768
UNKNOWN_ERROR => io::Error::new(io::ErrorKind::Other,
5869
"getrandom error: unknown"),
5970
UNAVAILABLE_ERROR => io::Error::new(io::ErrorKind::Other,

src/freebsd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::Error;
1313
use core::ptr;
1414
use std::io;
1515

16-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
16+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1717
let mib = [libc::CTL_KERN, libc::KERN_ARND];
1818
// kern.arandom permits a maximum buffer size of 256 bytes
1919
for chunk in dest.chunks_mut(256) {

src/fuchsia.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ extern crate fuchsia_cprng;
1111

1212
use super::Error;
1313

14-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
14+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
1515
fuchsia_cprng::cprng_draw(dest);
1616
Ok(())
1717
}

src/lib.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,92 @@
55
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
66
// option. This file may not be copied, modified, or distributed
77
// except according to those terms.
8+
9+
//! Interface to the random number generator of the operating system.
10+
//!
11+
//! # Platform sources
12+
//!
13+
//! | OS | interface
14+
//! |------------------|---------------------------------------------------------
15+
//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after reading from `/dev/random` once
16+
//! | Windows | [`RtlGenRandom`][3]
17+
//! | macOS, iOS | [`SecRandomCopyBytes`][4]
18+
//! | FreeBSD | [`kern.arandom`][5]
19+
//! | OpenBSD, Bitrig | [`getentropy`][6]
20+
//! | NetBSD | [`/dev/urandom`][7] after reading from `/dev/random` once
21+
//! | Dragonfly BSD | [`/dev/random`][8]
22+
//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10]
23+
//! | Fuchsia OS | [`cprng_draw`][11]
24+
//! | Redox | [`rand:`][12]
25+
//! | CloudABI | [`random_get`][13]
26+
//! | Haiku | `/dev/random` (identical to `/dev/urandom`)
27+
//! | SGX | RDRAND
28+
//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and ams.js][14])
29+
//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and ams.js][16])
30+
//!
31+
//! Getrandom doesn't have a blanket implementation for all Unix-like operating
32+
//! systems that reads from `/dev/urandom`. This ensures all supported operating
33+
//! systems are using the recommended interface and respect maximum buffer
34+
//! sizes.
35+
//!
36+
//! ## Support for WebAssembly and ams.js
37+
//!
38+
//! The three Emscripten targets `asmjs-unknown-emscripten`,
39+
//! `wasm32-unknown-emscripten` and `wasm32-experimental-emscripten` use
40+
//! Emscripten's emulation of `/dev/random` on web browsers and Node.js.
41+
//!
42+
//! The bare WASM target `wasm32-unknown-unknown` tries to call the javascript
43+
//! methods directly, using either `stdweb` or `wasm-bindgen` depending on what
44+
//! features are activated for this crate. Note that if both features are
45+
//! enabled `wasm-bindgen` will be used. If neither feature is enabled,
46+
//! `getrandom` will always fail.
47+
//!
48+
//! ## Early boot
49+
//!
50+
//! It is possible that early in the boot process the OS hasn't had enough time
51+
//! yet to collect entropy to securely seed its RNG, especially on virtual
52+
//! machines.
53+
//!
54+
//! Some operating systems always block the thread until the RNG is securely
55+
//! seeded. This can take anywhere from a few seconds to more than a minute.
56+
//! Others make a best effort to use a seed from before the shutdown and don't
57+
//! document much.
58+
//!
59+
//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and
60+
//! getting an error; in these cases we always choose to block.
61+
//!
62+
//! On Linux (when the `genrandom` system call is not available) and on NetBSD
63+
//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected
64+
//! enough entropy yet. To avoid returning low-entropy bytes, we first read from
65+
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
66+
//!
67+
//! # Error handling
68+
//!
69+
//! We always choose failure over returning insecure "random" bytes. In general,
70+
//! on supported platforms, failure is unlikely, though not impossible. If an
71+
//! error does occur, then it is likely that it will occur on every call to
72+
//! `getrandom`, hence after the first successful call one can be reasonably
73+
//! confident that no errors will occur.
74+
//!
75+
//! On unsupported platforms, `getrandom` always fails.
76+
//!
77+
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
78+
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
79+
//! [3]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
80+
//! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
81+
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
82+
//! [6]: https://man.openbsd.org/getentropy.2
83+
//! [7]: http://netbsd.gw.com/cgi-bin/man-cgi?random+4+NetBSD-current
84+
//! [8]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
85+
//! [9]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
86+
//! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
87+
//! [11]: https://fuchsia.googlesource.com/zircon/+/HEAD/docs/syscalls/cprng_draw.md
88+
//! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs
89+
//! [13]: https://github.com/NuxiNL/cloudabi/blob/v0.20/cloudabi.txt#L1826
90+
//! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
91+
//! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
92+
//! [16]: #support-for-webassembly-and-amsjs
93+
894
#![no_std]
995

1096
#[cfg(not(target_env = "sgx"))]
@@ -24,12 +110,17 @@ mod utils;
24110
mod error;
25111
pub use error::{Error, UNKNOWN_ERROR, UNAVAILABLE_ERROR};
26112

113+
114+
// System-specific implementations.
115+
//
116+
// These should all provide getrandom_inner with the same signature as getrandom.
117+
27118
macro_rules! mod_use {
28119
($cond:meta, $module:ident) => {
29120
#[$cond]
30121
mod $module;
31122
#[$cond]
32-
pub use $module::getrandom;
123+
use $module::getrandom_inner;
33124
}
34125
}
35126

@@ -100,3 +191,19 @@ mod_use!(
100191
))),
101192
dummy
102193
);
194+
195+
196+
/// Fill `dest` with random bytes from the system's preferred random number
197+
/// source.
198+
///
199+
/// This function returns an error on any failure, including partial reads. We
200+
/// make no guarantees regarding the contents of `dest` on error.
201+
///
202+
/// Blocking is possible, at least during early boot; see module documentation.
203+
///
204+
/// In general, `getrandom` will be fast enough for interactive usage, though
205+
/// significantly slower than a user-space CSPRNG; for the latter consider
206+
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
207+
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
208+
getrandom_inner(dest)
209+
}

src/linux_android.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn syscall_getrandom(dest: &mut [u8]) -> Result<(), io::Error> {
3838
Ok(())
3939
}
4040

41-
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
41+
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
4242
RNG_SOURCE.with(|f| {
4343
use_init(f,
4444
|| {

0 commit comments

Comments
 (0)