Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added possibility to pass uninit arrays to random generator #271

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ rustc-dep-of-std = [
# Unstable/test-only feature to run wasm-bindgen tests in a browser
test-in-browser = []

# Nightly-only implementation with `std::io::ReadBuf` struct.
rdbuf-impl = []

[package.metadata.docs.rs]
features = ["std", "custom"]
rustdoc-args = ["--cfg", "docsrs"]
4 changes: 3 additions & 1 deletion src/3ds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
// except according to those terms.

//! Implementation for Nintendo 3DS
use core::mem::MaybeUninit;
Copy link
Member

@josephlr josephlr Jul 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This increases our MSRV to 1.36 (current is 1.34) so is a breaking change. However, if we gate everything behind the read_buf feature, this shouldn't be an issue, as it will require nightly anyway.


use crate::util_libc::sys_fill_exact;
use crate::Error;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
sys_fill_exact(dest, |buf| unsafe {
libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
Expand Down
13 changes: 9 additions & 4 deletions src/bsd_arandom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@
// except according to those terms.

//! Implementation for FreeBSD and NetBSD
use crate::{util_libc::sys_fill_exact, Error};
use core::mem::MaybeUninit;
use core::ptr;

fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
use crate::util_libc::sys_fill_exact;
use crate::Error;

fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
let mut len = buf.len();
let ret = unsafe {
Expand All @@ -30,7 +33,7 @@ fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
}
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
#[cfg(target_os = "freebsd")]
{
Expand All @@ -41,7 +44,9 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {

if let Some(fptr) = GETRANDOM.ptr() {
let func: GetRandomFn = unsafe { core::mem::transmute(fptr) };
return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) });
return sys_fill_exact(dest, |buf| unsafe {
func(buf.as_mut_ptr() as *mut MaybeUninit<u8>, buf.len(), 0)
});
}
}
// Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
Expand Down
12 changes: 6 additions & 6 deletions src/dragonfly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
// except according to those terms.

//! Implementation for DragonFly BSD
use crate::{
use_file,
util_libc::{sys_fill_exact, Weak},
Error,
};
use core::mem::MaybeUninit;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
use crate::use_file;
use crate::util_libc::{sys_fill_exact, Weak};
use crate::Error;

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;

Expand Down
6 changes: 4 additions & 2 deletions src/espidf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
// except according to those terms.

//! Implementation for ESP-IDF
use crate::Error;
use core::ffi::c_void;
use core::mem::MaybeUninit;

use crate::Error;

extern "C" {
fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`)
// will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
Expand Down
4 changes: 2 additions & 2 deletions src/fuchsia.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extern "C" {
fn zx_cprng_draw(buffer: *mut u8, length: usize);
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut _, dest.len()) }
Ok(())
}
4 changes: 2 additions & 2 deletions src/ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ extern "C" {
fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32;
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Apple's documentation guarantees kSecRandomDefault is a synonym for NULL.
let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) };
let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr() as *mut u8) };
// errSecSuccess (from SecBase.h) is always zero.
if ret != 0 {
Err(Error::IOS_SEC_RANDOM)
Expand Down
19 changes: 16 additions & 3 deletions src/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
// <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.
use core::mem::MaybeUninit;
use core::ptr;

use crate::Error;

extern crate std;
Expand All @@ -27,7 +30,7 @@ thread_local!(
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
);

pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
RNG_SOURCE.with(|result| {
let source = result.as_ref().map_err(|&e| e)?;

Expand All @@ -48,7 +51,17 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
if crypto.get_random_values(&sub_buf).is_err() {
return Err(Error::WEB_GET_RANDOM_VALUES);
}
sub_buf.copy_to(chunk);
// SAFETY: Safe because MaybeUninit have same alignment and size as wrapped type
// and exclusive slices are always valid.
// Switch to https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write_slice
// when min supported Rust would have this.
unsafe {
ptr::copy_nonoverlapping(
chunk.as_ptr(),
chunk.as_mut_ptr() as *mut u8,
chunk.len(),
);
}
}
}
};
Expand Down Expand Up @@ -115,7 +128,7 @@ extern "C" {
fn require(this: &NodeModule, s: &str) -> Result<NodeCrypto, JsValue>;
type NodeCrypto;
#[wasm_bindgen(method, js_name = randomFillSync, catch)]
fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
fn random_fill_sync(this: &NodeCrypto, buf: &mut [MaybeUninit<u8>]) -> Result<(), JsValue>;

// Node JS process Object (https://nodejs.org/api/process.html)
#[wasm_bindgen(method, getter)]
Expand Down
40 changes: 39 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

use core::mem::MaybeUninit;

#[macro_use]
extern crate cfg_if;

Expand Down Expand Up @@ -259,5 +261,41 @@ pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
if dest.is_empty() {
return Ok(());
}
imp::getrandom_inner(dest)
imp::getrandom_inner(
// SAFETY: `MaybeUninit` have same size and alignment as wrapped value
// and it is safe to convert init value to MaybeUninit.
// See https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#layout
unsafe {
core::slice::from_raw_parts_mut(dest.as_mut_ptr() as *mut MaybeUninit<u8>, dest.len())
},
)
}

/// This version uses rdbuf-impl
/// It is intended to replace function that accepts `&mut [u8]`
/// once MSRV is bumped so you may want to use this to make easier switch
///
/// Caller must clear dest before use because function only append data to buffer
/// until fill it to capacity.
///
/// 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).
#[cfg(any(doc, feature = "rdbuf-impl"))]
pub fn getrandom_buf(dest: &mut std::io::ReadBuf) -> Result<(), Error> {
if dest.capacity() == dest.filled_len() {
// Already filled
return Ok(());
}
// SAFETY:
// 1. getrandom_inner must fill all bytes provided if succeedes.
// 2. getrandom_inner must not read from provided buffer because it can be uninit.
unsafe {
imp::getrandom_inner(dest.unfilled_mut())?;
dest.assume_init(dest.capacity());
dest.set_filled(dest.capacity());
}
Ok(())
}
4 changes: 3 additions & 1 deletion src/linux_android.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
// except according to those terms.

//! Implementation for Linux / Android
use core::mem::MaybeUninit;

use crate::{
util::LazyBool,
util_libc::{last_os_error, sys_fill_exact},
{use_file, Error},
};

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Linux 3.17
static HAS_GETRANDOM: LazyBool = LazyBool::new();
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
Expand Down
9 changes: 5 additions & 4 deletions src/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,23 @@
// except according to those terms.

//! Implementation for macOS
use core::mem::{transmute, MaybeUninit};

use crate::{
use_file,
util_libc::{last_os_error, Weak},
Error,
};
use core::mem;

type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getentropy(2) was added in 10.12, Rust supports 10.7+
static GETENTROPY: Weak = unsafe { Weak::new("getentropy\0") };
if let Some(fptr) = GETENTROPY.ptr() {
let func: GetEntropyFn = unsafe { mem::transmute(fptr) };
let func: GetEntropyFn = unsafe { transmute(fptr) };
for chunk in dest.chunks_mut(256) {
let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) };
let ret = unsafe { func(chunk.as_mut_ptr() as *mut u8, chunk.len()) };
if ret != 0 {
return Err(last_os_error());
}
Expand Down
2 changes: 1 addition & 1 deletion src/openbsd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//! Implementation for OpenBSD
use crate::{util_libc::last_os_error, Error};

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally.
for chunk in dest.chunks_mut(256) {
let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
Expand Down
14 changes: 10 additions & 4 deletions src/rdrand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fn is_rdrand_supported() -> bool {
HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 })
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [mem::MaybeUninit<u8>]) -> Result<(), Error> {
if !is_rdrand_supported() {
return Err(Error::NO_RDRAND);
}
Expand All @@ -80,18 +80,24 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
}

#[target_feature(enable = "rdrand")]
unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> {
unsafe fn rdrand_exact(dest: &mut [mem::MaybeUninit<u8>]) -> Result<(), Error> {
// We use chunks_exact_mut instead of chunks_mut as it allows almost all
// calls to memcpy to be elided by the compiler.
let mut chunks = dest.chunks_exact_mut(WORD_SIZE);
for chunk in chunks.by_ref() {
chunk.copy_from_slice(&rdrand()?);
let data = rdrand()?;
// SAFETY: MaybeUninit has same alignment and size as wrapped type.
// Switch to https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.write_slice
// when it would be supported by minimal Rust version.
core::ptr::copy_nonoverlapping(data.as_ptr(), chunk.as_mut_ptr() as *mut u8, data.len());
}

let tail = chunks.into_remainder();
let n = tail.len();
if n > 0 {
tail.copy_from_slice(&rdrand()?[..n]);
let data = rdrand()?;
// SAFETY: same as above
core::ptr::copy_nonoverlapping(data.as_ptr(), tail.as_mut_ptr() as *mut u8, n);
}
Ok(())
}
7 changes: 4 additions & 3 deletions src/solaris_illumos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@
//! To make sure we can compile on both Solaris and its derivatives, as well as
//! function, we check for the existence of getrandom(2) in libc by calling
//! libc::dlsym.
use core::mem;

use crate::{
use_file,
util_libc::{sys_fill_exact, Weak},
Error,
};
use core::mem;

#[cfg(target_os = "illumos")]
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
#[cfg(target_os = "solaris")]
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::c_int;

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [mem::MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Solaris 11.3 for Illumos in 2015.
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
if let Some(fptr) = GETRANDOM.ptr() {
Expand All @@ -38,7 +39,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
// derived platforms for atomically obtaining random data.
for chunk in dest.chunks_mut(256) {
sys_fill_exact(chunk, |buf| unsafe {
func(buf.as_mut_ptr(), buf.len(), 0) as libc::ssize_t
func(buf.as_mut_ptr() as *mut u8, buf.len(), 0) as libc::ssize_t
})?
}
Ok(())
Expand Down
8 changes: 5 additions & 3 deletions src/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
// except according to those terms.

//! Implementation for SOLID
use crate::Error;
use core::mem::MaybeUninit;
use core::num::NonZeroU32;

use crate::Error;

extern "C" {
pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32;
}

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr(), dest.len()) };
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr() as *mut u8, dest.len()) };
if ret >= 0 {
Ok(())
} else {
Expand Down
14 changes: 8 additions & 6 deletions src/use_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
// except according to those terms.

//! Implementations that just need to read from a file
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};

use crate::{
util::LazyUsize,
util_libc::{open_readonly, sys_fill_exact},
Error,
};
use core::{
cell::UnsafeCell,
sync::atomic::{AtomicUsize, Ordering::Relaxed},
};

#[cfg(any(
target_os = "dragonfly",
Expand All @@ -29,9 +29,11 @@ const FILE_PATH: &str = "/dev/random\0";
#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
const FILE_PATH: &str = "/dev/urandom\0";

pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
let fd = get_rng_fd()?;
let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
let read = |buf: &mut [MaybeUninit<u8>]| unsafe {
libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len())
};

if cfg!(target_os = "emscripten") {
// `Crypto.getRandomValues` documents `dest` should be at most 65536 bytes.
Expand Down