Skip to content

Commit eef3564

Browse files
committed
Add "insecure" functions
1 parent e1943e4 commit eef3564

27 files changed

+294
-154
lines changed

README.md

+37-26
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ library like [`rand`].
1919

2020
[`rand`]: https://crates.io/crates/rand
2121

22-
## Usage
22+
## Examples
2323

2424
Add the `getrandom` dependency to your `Cargo.toml` file:
2525

@@ -38,6 +38,16 @@ fn get_random_u128() -> Result<u128, getrandom::Error> {
3838
}
3939
```
4040

41+
The crate also support direct generation of random `u32` and `u64` values:
42+
43+
```rust
44+
fn get_rand_u32_u64() -> Result<(u32, u64), getrandom::Error> {
45+
let a = getrandom::u32()?;
46+
let b = getrandom::u64()?;
47+
Ok((a, b))
48+
}
49+
```
50+
4151
## Supported targets
4252

4353
| Target | Target Triple | Implementation
@@ -106,6 +116,25 @@ WILL NOT have any effect on its downstream users.
106116

107117
[`.cargo/config.toml`]: https://doc.rust-lang.org/cargo/reference/config.html
108118

119+
### "Insecure" functions
120+
121+
Sometimes, early in the boot process, the OS has not collected enough
122+
entropy to securely seed its RNG. This is especially common on virtual
123+
machines, where standard "random" events are hard to come by.
124+
125+
Some operating system interfaces always block until the RNG is securely
126+
seeded, which can take anywhere from a few seconds to more than a minute.
127+
Some platforms offer a choice between blocking and getting potentially less
128+
secure randomness, i.e. generated data may be less difficult for an attacker
129+
to predict.
130+
131+
We expose this functionality through two sets of functions. The "secure"
132+
functions ([`fill`], [`fill_uninit`], [`u32`], and [`u64`]) may block but
133+
always return "secure" randomness suitable for cryptographic needs.
134+
Meanwhile, their "insecure" counterparts ([`insecure_fill`],
135+
[`insecure_fill_uninit`], [`insecure_u32`], and [`insecure_u64`]) may return
136+
randomness of lesser quality but are less likely to block in entropy-starved scenarios.
137+
109138
### WebAssembly support
110139

111140
This crate fully supports the [WASI] and [Emscripten] targets. However,
@@ -210,31 +239,6 @@ The fallback can be disabled by enabling the `linux_getrandom` opt-in backend.
210239
Note that doing so will bump minimum supported Linux kernel version to 3.17
211240
and Android API level to 23 (Marshmallow).
212241

213-
### Early boot
214-
215-
Sometimes, early in the boot process, the OS has not collected enough
216-
entropy to securely seed its RNG. This is especially common on virtual
217-
machines, where standard "random" events are hard to come by.
218-
219-
Some operating system interfaces always block until the RNG is securely
220-
seeded. This can take anywhere from a few seconds to more than a minute.
221-
A few (Linux, NetBSD and Solaris) offer a choice between blocking and
222-
getting an error; in these cases, we always choose to block.
223-
224-
On Linux (when the `getrandom` system call is not available), reading from
225-
`/dev/urandom` never blocks, even when the OS hasn't collected enough
226-
entropy yet. To avoid returning low-entropy bytes, we first poll
227-
`/dev/random` and only switch to `/dev/urandom` once this has succeeded.
228-
229-
On OpenBSD, this kind of entropy accounting isn't available, and on
230-
NetBSD, blocking on it is discouraged. On these platforms, nonblocking
231-
interfaces are used, even when reliable entropy may not be available.
232-
On the platforms where it is used, the reliability of entropy accounting
233-
itself isn't free from controversy. This library provides randomness
234-
sourced according to the platform's best practices, but each platform has
235-
its own limits on the grade of randomness it can promise in environments
236-
with few sources of entropy.
237-
238242
## Error handling
239243

240244
We always prioritize failure over returning known insecure "random" bytes.
@@ -346,4 +350,11 @@ dual licensed as above, without any additional terms or conditions.
346350
[LICENSE-MIT]: https://github.com/rust-random/getrandom/blob/master/LICENSE-MIT
347351

348352
[`Error::UNEXPECTED`]: https://docs.rs/getrandom/latest/getrandom/struct.Error.html#associatedconstant.UNEXPECTED
353+
[`fill`]: https://docs.rs/getrandom/latest/getrandom/fn.fill.html
349354
[`fill_uninit`]: https://docs.rs/getrandom/latest/getrandom/fn.fill_uninit.html
355+
[`u32`]: https://docs.rs/getrandom/latest/getrandom/fn.u32.html
356+
[`u64`]: https://docs.rs/getrandom/latest/getrandom/fn.u64.html
357+
[`insecure_fill`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_fill.html
358+
[`insecure_fill_uninit`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_fill_uninit.html
359+
[`insecure_u32`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_u32.html
360+
[`insecure_u64`]: https://docs.rs/getrandom/latest/getrandom/fn.insecure_u64.html

src/backends.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
//! System-specific implementations.
22
//!
3-
//! This module should provide `fill_inner` with the signature
4-
//! `fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
5-
//! The function MUST fully initialize `dest` when `Ok(())` is returned.
6-
//! The function MUST NOT ever write uninitialized bytes into `dest`,
7-
//! regardless of what value it returns.
3+
//! This module should provide the following functions:
4+
//! - `fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`
5+
//! - `fn insecure_fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`
6+
//! - `fn u32() -> Result<u32, Error>`
7+
//! - `fn insecure_u32() -> Result<u32, Error>`
8+
//! - `fn u64() -> Result<u64, Error>`
9+
//! - `fn insecure_u64() -> Result<u64, Error>`
10+
//!
11+
//! `fill_uninit` and `insecure_fill_uninit` MUST fully initialize `dest`
12+
//! when `Ok(())` is returned. The functions MUST NOT ever write uninitialized
13+
//! bytes into `dest`, regardless of what value it returns.
814
915
cfg_if! {
1016
if #[cfg(getrandom_backend = "custom")] {

src/backends/apple_other.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
use crate::Error;
33
use core::{ffi::c_void, mem::MaybeUninit};
44

5-
pub use crate::util::{inner_u32, inner_u64};
5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
66

7-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
7+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
88
let dst_ptr = dest.as_mut_ptr().cast::<c_void>();
99
let ret = unsafe { libc::CCRandomGenerateBytes(dst_ptr, dest.len()) };
1010
if ret == libc::kCCSuccess {

src/backends/custom.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
use crate::Error;
33
use core::mem::MaybeUninit;
44

5-
pub use crate::util::{inner_u32, inner_u64};
5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
66

7-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
7+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
88
extern "Rust" {
99
fn __getrandom_v03_custom(dest: *mut u8, len: usize) -> Result<(), Error>;
1010
}

src/backends/esp_idf.rs

+23-10
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
1-
//! Implementation for ESP-IDF
1+
//! Implementation for ESP-IDF.
2+
//!
3+
//! Note that NOT enabling WiFi, BT, or the voltage noise entropy source
4+
//! (via `bootloader_random_enable`) will cause ESP-IDF to return pseudo-random numbers based on
5+
//! the voltage noise entropy, after the initial boot process:
6+
//! https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
7+
//!
8+
//! However tracking if some of these entropy sources is enabled is way too difficult
9+
//! to implement here.
210
use crate::Error;
311
use core::{ffi::c_void, mem::MaybeUninit};
412

5-
pub use crate::util::{inner_u32, inner_u64};
13+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64};
614

715
#[cfg(not(target_os = "espidf"))]
816
compile_error!("`esp_idf` backend can be enabled only for ESP-IDF targets!");
917

1018
extern "C" {
11-
fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
19+
fn esp_random() -> u32;
20+
fn esp_fill_random(buf: *mut c_void, len: usize);
1221
}
1322

14-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
15-
// Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`)
16-
// will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process:
17-
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
18-
//
19-
// However tracking if some of these entropy sources is enabled is way too difficult to implement here
20-
unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) };
23+
pub fn u32() -> Result<u32, Error> {
24+
Ok(unsafe { esp_random() })
25+
}
2126

27+
pub fn u64() -> Result<u64, Error> {
28+
let (a, b) = unsafe { (esp_random(), esp_random()) };
29+
let res = (u64::from(a) << 32) | u64::from(b);
30+
Ok(res)
31+
}
32+
33+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
34+
unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) };
2235
Ok(())
2336
}

src/backends/fuchsia.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
use crate::Error;
33
use core::mem::MaybeUninit;
44

5-
pub use crate::util::{inner_u32, inner_u64};
5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
66

77
#[link(name = "zircon")]
88
extern "C" {
99
fn zx_cprng_draw(buffer: *mut u8, length: usize);
1010
}
1111

12-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
12+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1313
unsafe { zx_cprng_draw(dest.as_mut_ptr().cast::<u8>(), dest.len()) }
1414
Ok(())
1515
}

src/backends/getentropy.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
use crate::Error;
1111
use core::{ffi::c_void, mem::MaybeUninit};
1212

13-
pub use crate::util::{inner_u32, inner_u64};
13+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
1414

1515
#[path = "../util_libc.rs"]
1616
mod util_libc;
1717

18-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
18+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1919
for chunk in dest.chunks_mut(256) {
2020
let ret = unsafe { libc::getentropy(chunk.as_mut_ptr().cast::<c_void>(), chunk.len()) };
2121
if ret != 0 {

src/backends/getrandom.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@
1818
use crate::Error;
1919
use core::{ffi::c_void, mem::MaybeUninit};
2020

21-
pub use crate::util::{inner_u32, inner_u64};
21+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
2222

2323
#[path = "../util_libc.rs"]
2424
mod util_libc;
2525

26-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
26+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
2727
util_libc::sys_fill_exact(dest, |buf| unsafe {
2828
libc::getrandom(buf.as_mut_ptr().cast::<c_void>(), buf.len(), 0)
2929
})

src/backends/hermit.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
use crate::Error;
33
use core::mem::MaybeUninit;
44

5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64};
6+
57
extern "C" {
68
fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize;
79
// Note that `sys_secure_rand32/64` are implemented using `sys_read_entropy`:
@@ -12,7 +14,7 @@ extern "C" {
1214
fn sys_secure_rand64(value: *mut u64) -> i32;
1315
}
1416

15-
pub fn inner_u32() -> Result<u32, Error> {
17+
pub fn u32() -> Result<u32, Error> {
1618
let mut res = MaybeUninit::uninit();
1719
let ret = unsafe { sys_secure_rand32(res.as_mut_ptr()) };
1820
match ret {
@@ -22,7 +24,7 @@ pub fn inner_u32() -> Result<u32, Error> {
2224
}
2325
}
2426

25-
pub fn inner_u64() -> Result<u64, Error> {
27+
pub fn u64() -> Result<u64, Error> {
2628
let mut res = MaybeUninit::uninit();
2729
let ret = unsafe { sys_secure_rand64(res.as_mut_ptr()) };
2830
match ret {
@@ -32,7 +34,7 @@ pub fn inner_u64() -> Result<u64, Error> {
3234
}
3335
}
3436

35-
pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
37+
pub fn fill_uninit(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
3638
while !dest.is_empty() {
3739
let res = unsafe { sys_read_entropy(dest.as_mut_ptr().cast::<u8>(), dest.len(), 0) };
3840
match res {

src/backends/linux_android.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22
use crate::Error;
33
use core::mem::MaybeUninit;
44

5-
pub use crate::util::{inner_u32, inner_u64};
5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
66

77
#[path = "../util_libc.rs"]
88
mod util_libc;
99

1010
#[cfg(not(any(target_os = "android", target_os = "linux")))]
1111
compile_error!("`linux_getrandom` backend can be enabled only for Linux/Android targets!");
1212

13-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
13+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1414
util_libc::sys_fill_exact(dest, |buf| unsafe {
1515
libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0)
1616
})

src/backends/linux_android_with_fallback.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use core::{
99
};
1010
use use_file::util_libc;
1111

12-
pub use crate::util::{inner_u32, inner_u64};
12+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
1313

1414
type GetRandomFn = unsafe extern "C" fn(*mut c_void, libc::size_t, libc::c_uint) -> libc::ssize_t;
1515

@@ -56,10 +56,10 @@ fn init() -> NonNull<c_void> {
5656
// prevent inlining of the fallback implementation
5757
#[inline(never)]
5858
fn use_file_fallback(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
59-
use_file::fill_inner(dest)
59+
use_file::fill_uninit(dest)
6060
}
6161

62-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
62+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
6363
// Despite being only a single atomic variable, we still cannot always use
6464
// Ordering::Relaxed, as we need to make sure a successful call to `init`
6565
// is "ordered before" any data read through the returned pointer (which

src/backends/linux_rustix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
use crate::{Error, MaybeUninit};
33
use rustix::rand::{getrandom_uninit, GetRandomFlags};
44

5-
pub use crate::util::{inner_u32, inner_u64};
5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
66

77
#[cfg(not(any(target_os = "android", target_os = "linux")))]
88
compile_error!("`linux_rustix` backend can be enabled only for Linux/Android targets!");
99

10-
pub fn fill_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
10+
pub fn fill_uninit(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
1111
loop {
1212
let res = getrandom_uninit(dest, GetRandomFlags::empty()).map(|(res, _)| res.len());
1313
match res {

src/backends/netbsd.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use core::{
1212
sync::atomic::{AtomicPtr, Ordering},
1313
};
1414

15-
pub use crate::util::{inner_u32, inner_u64};
15+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64, u32, u64};
1616

1717
#[path = "../util_libc.rs"]
1818
mod util_libc;
@@ -62,7 +62,7 @@ fn init() -> *mut c_void {
6262
ptr
6363
}
6464

65-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
65+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
6666
// Despite being only a single atomic variable, we still cannot always use
6767
// Ordering::Relaxed, as we need to make sure a successful call to `init`
6868
// is "ordered before" any data read through the returned pointer (which

src/backends/rdrand.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
use crate::{util::slice_as_uninit, Error};
33
use core::mem::{size_of, MaybeUninit};
44

5+
pub use crate::default_impls::{insecure_fill_uninit, insecure_u32, insecure_u64};
6+
57
#[path = "../lazy.rs"]
68
mod lazy;
79

@@ -147,23 +149,23 @@ unsafe fn rdrand_u64() -> Option<u64> {
147149
Some((u64::from(a) << 32) || u64::from(b))
148150
}
149151

150-
pub fn inner_u32() -> Result<u32, Error> {
152+
pub fn u32() -> Result<u32, Error> {
151153
if !RDRAND_GOOD.unsync_init(is_rdrand_good) {
152154
return Err(Error::NO_RDRAND);
153155
}
154156
// SAFETY: After this point, we know rdrand is supported.
155157
unsafe { rdrand_u32() }.ok_or(Error::FAILED_RDRAND)
156158
}
157159

158-
pub fn inner_u64() -> Result<u64, Error> {
160+
pub fn u64() -> Result<u64, Error> {
159161
if !RDRAND_GOOD.unsync_init(is_rdrand_good) {
160162
return Err(Error::NO_RDRAND);
161163
}
162164
// SAFETY: After this point, we know rdrand is supported.
163165
unsafe { rdrand_u64() }.ok_or(Error::FAILED_RDRAND)
164166
}
165167

166-
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
168+
pub fn fill_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
167169
if !RDRAND_GOOD.unsync_init(is_rdrand_good) {
168170
return Err(Error::NO_RDRAND);
169171
}

0 commit comments

Comments
 (0)