Add getrandom_uninit_slice(dest: &mut [MaybeUninit<u8>]) -> ...
. (#291)
* Add `getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> ...`. Add a public API for filling an `&mut [MaybeUninit<u8>]`. This will primarily serve as the building block for more typeful APIs for constructing random arrays. Increase the MSRV to 1.36, as `MaybeUninit` was added in that release. Fixes #226. * Revert testing changes Signed-off-by: Joe Richey <joerichey@google.com> * Allow rdrand tests to work with new implementation Signed-off-by: Joe Richey <joerichey@google.com> * Add Additional benchmarks and buffer size Signed-off-by: Joe Richey <joerichey@google.com> * Use pointer casts instead of transmute Signed-off-by: Joe Richey <joerichey@google.com> * Avoid initializing the buffer in `getrandom_uninit` benchmarks. * Benchmarks: Consume the result in `black_box`. Signed-off-by: Joe Richey <joerichey@google.com> Co-authored-by: Joe Richey <joerichey@google.com>
This commit is contained in:
parent
5c1bb00b74
commit
47a59dda25
2
.github/workflows/tests.yml
vendored
2
.github/workflows/tests.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
toolchain: [nightly, beta, stable, 1.34]
|
||||
toolchain: [nightly, beta, stable, 1.36]
|
||||
# Only Test macOS on stable to reduce macOS CI jobs
|
||||
include:
|
||||
- os: macos-latest
|
||||
|
@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
### Added
|
||||
- `getrandom_uninit` [#291]
|
||||
|
||||
### Breaking Changes
|
||||
- Update MSRV to 1.36 [#291]
|
||||
|
||||
[#291]: https://github.com/rust-random/getrandom/pull/291
|
||||
|
||||
## [0.2.8] - 2022-10-20
|
||||
### Changed
|
||||
- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown`
|
||||
|
@ -52,7 +52,7 @@ crate features, WASM support and Custom RNGs see the
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
This crate requires Rust 1.34.0 or later.
|
||||
This crate requires Rust 1.36.0 or later.
|
||||
|
||||
# License
|
||||
|
||||
|
118
benches/mod.rs
118
benches/mod.rs
@ -1,94 +1,64 @@
|
||||
#![feature(test)]
|
||||
#![feature(maybe_uninit_as_bytes)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use std::{
|
||||
alloc::{alloc_zeroed, dealloc, Layout},
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned
|
||||
struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>);
|
||||
|
||||
impl<const N: usize> AlignedBuffer<N> {
|
||||
fn layout() -> Layout {
|
||||
Layout::from_size_align(N, N).unwrap()
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N];
|
||||
Self(NonNull::new(p).unwrap())
|
||||
}
|
||||
|
||||
fn buf(&mut self) -> &mut [u8; N] {
|
||||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Drop for AlignedBuffer<N> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) }
|
||||
}
|
||||
}
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
// Used to benchmark the throughput of getrandom in an optimal scenario.
|
||||
// The buffer is hot, and does not require initialization.
|
||||
#[inline(always)]
|
||||
fn bench<const N: usize>(b: &mut test::Bencher) {
|
||||
let mut ab = AlignedBuffer::<N>::new();
|
||||
let buf = ab.buf();
|
||||
b.iter(|| {
|
||||
getrandom::getrandom(&mut buf[..]).unwrap();
|
||||
test::black_box(&buf);
|
||||
});
|
||||
fn bench_getrandom<const N: usize>(b: &mut test::Bencher) {
|
||||
b.bytes = N as u64;
|
||||
b.iter(|| {
|
||||
let mut buf = [0u8; N];
|
||||
getrandom::getrandom(&mut buf[..]).unwrap();
|
||||
test::black_box(buf);
|
||||
});
|
||||
}
|
||||
|
||||
// Used to benchmark the throughput of getrandom is a slightly less optimal
|
||||
// scenario. The buffer is still hot, but requires initialization.
|
||||
#[inline(always)]
|
||||
fn bench_with_init<const N: usize>(b: &mut test::Bencher) {
|
||||
let mut ab = AlignedBuffer::<N>::new();
|
||||
let buf = ab.buf();
|
||||
b.iter(|| {
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
getrandom::getrandom(&mut buf[..]).unwrap();
|
||||
test::black_box(&buf);
|
||||
});
|
||||
fn bench_getrandom_uninit<const N: usize>(b: &mut test::Bencher) {
|
||||
b.bytes = N as u64;
|
||||
b.iter(|| {
|
||||
let mut buf: MaybeUninit<[u8; N]> = MaybeUninit::uninit();
|
||||
let _ = getrandom::getrandom_uninit(buf.as_bytes_mut()).unwrap();
|
||||
let buf: [u8; N] = unsafe { buf.assume_init() };
|
||||
test::black_box(buf)
|
||||
});
|
||||
}
|
||||
|
||||
// 32 bytes (256-bit) is the seed sized used for rand::thread_rng
|
||||
const SEED: usize = 32;
|
||||
// Common size of a page, 4 KiB
|
||||
const PAGE: usize = 4096;
|
||||
// Large buffer to get asymptotic performance, 2 MiB
|
||||
const LARGE: usize = 1 << 21;
|
||||
macro_rules! bench {
|
||||
( $name:ident, $size:expr ) => {
|
||||
pub mod $name {
|
||||
#[bench]
|
||||
pub fn bench_getrandom(b: &mut test::Bencher) {
|
||||
super::bench_getrandom::<{ $size }>(b);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_seed(b: &mut test::Bencher) {
|
||||
bench::<SEED>(b);
|
||||
}
|
||||
#[bench]
|
||||
fn bench_seed_init(b: &mut test::Bencher) {
|
||||
bench_with_init::<SEED>(b);
|
||||
#[bench]
|
||||
pub fn bench_getrandom_uninit(b: &mut test::Bencher) {
|
||||
super::bench_getrandom_uninit::<{ $size }>(b);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_page(b: &mut test::Bencher) {
|
||||
bench::<PAGE>(b);
|
||||
}
|
||||
#[bench]
|
||||
fn bench_page_init(b: &mut test::Bencher) {
|
||||
bench_with_init::<PAGE>(b);
|
||||
}
|
||||
// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce.
|
||||
bench!(aes128, 128 / 8);
|
||||
|
||||
#[bench]
|
||||
fn bench_large(b: &mut test::Bencher) {
|
||||
bench::<LARGE>(b);
|
||||
}
|
||||
#[bench]
|
||||
fn bench_large_init(b: &mut test::Bencher) {
|
||||
bench_with_init::<LARGE>(b);
|
||||
}
|
||||
// 32 bytes (256 bits) is the seed sized used for rand::thread_rng
|
||||
// and the `random` value in a ClientHello/ServerHello for TLS.
|
||||
// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key
|
||||
// and/or nonce.
|
||||
bench!(p256, 256 / 8);
|
||||
|
||||
// A P-384/HMAC-384 key and/or nonce.
|
||||
bench!(p384, 384 / 8);
|
||||
|
||||
// Initializing larger buffers is not the primary use case of this library, as
|
||||
// this should normally be done by a userspace CSPRNG. However, we have a test
|
||||
// here to see the effects of a lower (amortized) syscall overhead.
|
||||
bench!(page, 4096);
|
||||
|
@ -9,8 +9,9 @@
|
||||
//! Implementation for Nintendo 3DS
|
||||
use crate::util_libc::sys_fill_exact;
|
||||
use crate::Error;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
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)
|
||||
})
|
||||
|
@ -8,9 +8,9 @@
|
||||
|
||||
//! Implementation for FreeBSD and NetBSD
|
||||
use crate::{util_libc::sys_fill_exact, Error};
|
||||
use core::ptr;
|
||||
use core::{mem::MaybeUninit, ptr};
|
||||
|
||||
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
|
||||
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 {
|
||||
@ -30,7 +30,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")]
|
||||
{
|
||||
@ -41,7 +41,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 u8, buf.len(), 0)
|
||||
});
|
||||
}
|
||||
}
|
||||
// Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
|
||||
|
@ -7,8 +7,8 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! An implementation which calls out to an externally defined function.
|
||||
use crate::Error;
|
||||
use core::num::NonZeroU32;
|
||||
use crate::{util::uninit_slice_fill_zero, Error};
|
||||
use core::{mem::MaybeUninit, num::NonZeroU32};
|
||||
|
||||
/// Register a function to be invoked by `getrandom` on unsupported targets.
|
||||
///
|
||||
@ -90,10 +90,16 @@ macro_rules! register_custom_getrandom {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
extern "C" {
|
||||
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
|
||||
}
|
||||
// Previously we always passed a valid, initialized slice to
|
||||
// `__getrandom_custom`. Ensure `dest` has been initialized for backward
|
||||
// compatibility with implementations that rely on that (e.g. Rust
|
||||
// implementations that construct a `&mut [u8]` slice from `dest` and
|
||||
// `len`).
|
||||
let dest = uninit_slice_fill_zero(dest);
|
||||
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
|
||||
match NonZeroU32::new(ret) {
|
||||
None => Ok(()),
|
||||
|
@ -12,8 +12,9 @@ use crate::{
|
||||
util_libc::{sys_fill_exact, Weak},
|
||||
Error,
|
||||
};
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), 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;
|
||||
|
||||
|
@ -8,13 +8,13 @@
|
||||
|
||||
//! Implementation for ESP-IDF
|
||||
use crate::Error;
|
||||
use core::ffi::c_void;
|
||||
use core::{ffi::c_void, mem::MaybeUninit};
|
||||
|
||||
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
|
||||
|
@ -8,13 +8,14 @@
|
||||
|
||||
//! Implementation for Fuchsia Zircon
|
||||
use crate::Error;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
#[link(name = "zircon")]
|
||||
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 u8, dest.len()) }
|
||||
Ok(())
|
||||
}
|
||||
|
@ -8,16 +8,16 @@
|
||||
|
||||
//! Implementation for iOS
|
||||
use crate::Error;
|
||||
use core::{ffi::c_void, ptr::null};
|
||||
use core::{ffi::c_void, mem::MaybeUninit, ptr::null};
|
||||
|
||||
#[link(name = "Security", kind = "framework")]
|
||||
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)
|
||||
|
14
src/js.rs
14
src/js.rs
@ -5,10 +5,10 @@
|
||||
// <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 crate::Error;
|
||||
use crate::{util::uninit_slice_fill_zero, Error};
|
||||
|
||||
extern crate std;
|
||||
use std::thread_local;
|
||||
use std::{mem::MaybeUninit, thread_local};
|
||||
|
||||
use js_sys::{global, Function, Uint8Array};
|
||||
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
|
||||
@ -28,12 +28,16 @@ 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)?;
|
||||
|
||||
match source {
|
||||
RngSource::Node(n) => {
|
||||
// XXX(perf): `random_fill_sync` requires a `&mut [u8]` so we
|
||||
// have to ensure the memory in `dest` is initialized.
|
||||
let dest = uninit_slice_fill_zero(dest);
|
||||
|
||||
if n.random_fill_sync(dest).is_err() {
|
||||
return Err(Error::NODE_RANDOM_FILL_SYNC);
|
||||
}
|
||||
@ -49,7 +53,9 @@ 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: `sub_buf`'s length is the same length as `chunk`
|
||||
unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
48
src/lib.rs
48
src/lib.rs
@ -186,6 +186,9 @@
|
||||
#[macro_use]
|
||||
extern crate cfg_if;
|
||||
|
||||
use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
mod error;
|
||||
mod util;
|
||||
// To prevent a breaking change when targets are added, we always export the
|
||||
@ -199,7 +202,11 @@ pub use crate::error::Error;
|
||||
|
||||
// System-specific implementations.
|
||||
//
|
||||
// These should all provide getrandom_inner with the same signature as getrandom.
|
||||
// These should all provide getrandom_inner with the signature
|
||||
// `fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
|
||||
// The function MUST fully initialize `dest` when `Ok(())` is returned.
|
||||
// The function MUST NOT ever write uninitialized bytes into `dest`,
|
||||
// regardless of what value it returns.
|
||||
cfg_if! {
|
||||
if #[cfg(any(target_os = "emscripten", target_os = "haiku",
|
||||
target_os = "redox"))] {
|
||||
@ -283,9 +290,40 @@ cfg_if! {
|
||||
/// 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).
|
||||
#[inline]
|
||||
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
|
||||
if dest.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
imp::getrandom_inner(dest)
|
||||
// SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, and
|
||||
// `getrandom_uninit` guarantees it will never de-initialize any part of
|
||||
// `dest`.
|
||||
getrandom_uninit(unsafe { slice_as_uninit_mut(dest) })?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Version of the `getrandom` function which fills `dest` with random bytes
|
||||
/// returns a mutable reference to those bytes.
|
||||
///
|
||||
/// On successful completion this function is guaranteed to return a slice
|
||||
/// which points to the same memory as `dest` and has the same length.
|
||||
/// In other words, it's safe to assume that `dest` is initialized after
|
||||
/// this function has returned `Ok`.
|
||||
///
|
||||
/// No part of `dest` will ever be de-initialized at any point, regardless
|
||||
/// of what is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// # // We ignore this test since `uninit_array` is unstable.
|
||||
/// #![feature(maybe_uninit_uninit_array)]
|
||||
/// # fn main() -> Result<(), getrandom::Error> {
|
||||
/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>();
|
||||
/// let buf: &mut [u8] = getrandom::getrandom_uninit(&mut buf)?;
|
||||
/// # Ok(()) }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
|
||||
imp::getrandom_inner(dest)?;
|
||||
// SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
|
||||
// since it returned `Ok`.
|
||||
Ok(unsafe { slice_assume_init_mut(dest) })
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ use crate::{
|
||||
util_libc::{last_os_error, sys_fill_exact},
|
||||
{use_file, Error},
|
||||
};
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
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) {
|
||||
|
@ -12,17 +12,17 @@ use crate::{
|
||||
util_libc::{last_os_error, Weak},
|
||||
Error,
|
||||
};
|
||||
use core::mem;
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
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) };
|
||||
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());
|
||||
}
|
||||
|
@ -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()) };
|
||||
|
@ -7,8 +7,8 @@
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementation for SGX using RDRAND instruction
|
||||
use crate::Error;
|
||||
use core::mem;
|
||||
use crate::{util::slice_as_uninit, Error};
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "x86_64")] {
|
||||
@ -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 [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
if !is_rdrand_supported() {
|
||||
return Err(Error::NO_RDRAND);
|
||||
}
|
||||
@ -80,18 +80,18 @@ 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 [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()?);
|
||||
chunk.copy_from_slice(slice_as_uninit(&rdrand()?));
|
||||
}
|
||||
|
||||
let tail = chunks.into_remainder();
|
||||
let n = tail.len();
|
||||
if n > 0 {
|
||||
tail.copy_from_slice(&rdrand()?[..n]);
|
||||
tail.copy_from_slice(slice_as_uninit(&rdrand()?[..n]));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
|
||||
#[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 [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() {
|
||||
|
@ -8,14 +8,14 @@
|
||||
|
||||
//! Implementation for SOLID
|
||||
use crate::Error;
|
||||
use core::num::NonZeroU32;
|
||||
use core::{mem::MaybeUninit, num::NonZeroU32};
|
||||
|
||||
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 {
|
||||
|
@ -14,6 +14,7 @@ use crate::{
|
||||
};
|
||||
use core::{
|
||||
cell::UnsafeCell,
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{AtomicUsize, Ordering::Relaxed},
|
||||
};
|
||||
|
||||
@ -29,9 +30,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.
|
||||
|
39
src/util.rs
39
src/util.rs
@ -6,7 +6,11 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![allow(dead_code)]
|
||||
use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
ptr,
|
||||
sync::atomic::{AtomicUsize, Ordering::Relaxed},
|
||||
};
|
||||
|
||||
// This structure represents a lazily initialized static usize value. Useful
|
||||
// when it is preferable to just rerun initialization instead of locking.
|
||||
@ -62,3 +66,36 @@ impl LazyBool {
|
||||
self.0.unsync_init(|| init() as usize) != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// Polyfill for `maybe_uninit_slice` feature's
|
||||
/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have
|
||||
/// been initialized.
|
||||
#[inline(always)]
|
||||
pub unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
|
||||
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
|
||||
&mut *(slice as *mut [MaybeUninit<T>] as *mut [T])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
|
||||
unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) };
|
||||
unsafe { slice_assume_init_mut(slice) }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn slice_as_uninit<T>(slice: &[T]) -> &[MaybeUninit<T>] {
|
||||
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
|
||||
// There is no risk of writing a `MaybeUninit<T>` into the result since
|
||||
// the result isn't mutable.
|
||||
unsafe { &*(slice as *const [T] as *const [MaybeUninit<T>]) }
|
||||
}
|
||||
|
||||
/// View an mutable initialized array as potentially-uninitialized.
|
||||
///
|
||||
/// This is unsafe because it allows assigning uninitialized values into
|
||||
/// `slice`, which would be undefined behavior.
|
||||
#[inline(always)]
|
||||
pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
|
||||
// SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
|
||||
&mut *(slice as *mut [T] as *mut [MaybeUninit<T>])
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
#![allow(dead_code)]
|
||||
use crate::Error;
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
num::NonZeroU32,
|
||||
ptr::NonNull,
|
||||
sync::atomic::{fence, AtomicPtr, Ordering},
|
||||
@ -59,8 +60,8 @@ pub fn last_os_error() -> Error {
|
||||
// - should return -1 and set errno on failure
|
||||
// - should return the number of bytes written on success
|
||||
pub fn sys_fill_exact(
|
||||
mut buf: &mut [u8],
|
||||
sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
|
||||
mut buf: &mut [MaybeUninit<u8>],
|
||||
sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> libc::ssize_t,
|
||||
) -> Result<(), Error> {
|
||||
while !buf.is_empty() {
|
||||
let res = sys_fill(buf);
|
||||
|
@ -8,9 +8,12 @@
|
||||
|
||||
//! Implementation for VxWorks
|
||||
use crate::{util_libc::last_os_error, Error};
|
||||
use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||
};
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
static RNG_INIT: AtomicBool = AtomicBool::new(false);
|
||||
while !RNG_INIT.load(Relaxed) {
|
||||
let ret = unsafe { libc::randSecure() };
|
||||
@ -25,7 +28,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
|
||||
// Prevent overflow of i32
|
||||
for chunk in dest.chunks_mut(i32::max_value() as usize) {
|
||||
let ret = unsafe { libc::randABytes(chunk.as_mut_ptr(), chunk.len() as i32) };
|
||||
let ret = unsafe { libc::randABytes(chunk.as_mut_ptr() as *mut u8, chunk.len() as i32) };
|
||||
if ret != 0 {
|
||||
return Err(last_os_error());
|
||||
}
|
||||
|
@ -8,10 +8,10 @@
|
||||
|
||||
//! Implementation for WASI
|
||||
use crate::Error;
|
||||
use core::num::NonZeroU32;
|
||||
use core::{mem::MaybeUninit, num::NonZeroU32};
|
||||
use wasi::wasi_snapshot_preview1::random_get;
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
match unsafe { random_get(dest.as_mut_ptr() as i32, dest.len() as i32) } {
|
||||
0 => Ok(()),
|
||||
err => Err(unsafe { NonZeroU32::new_unchecked(err as u32) }.into()),
|
||||
|
@ -7,7 +7,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use crate::Error;
|
||||
use core::{ffi::c_void, num::NonZeroU32, ptr};
|
||||
use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
|
||||
|
||||
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
|
||||
|
||||
@ -21,14 +21,14 @@ extern "system" {
|
||||
) -> u32;
|
||||
}
|
||||
|
||||
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
|
||||
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
|
||||
// Prevent overflow of u32
|
||||
for chunk in dest.chunks_mut(u32::max_value() as usize) {
|
||||
// BCryptGenRandom was introduced in Windows Vista
|
||||
let ret = unsafe {
|
||||
BCryptGenRandom(
|
||||
ptr::null_mut(),
|
||||
chunk.as_mut_ptr(),
|
||||
chunk.as_mut_ptr() as *mut u8,
|
||||
chunk.len() as u32,
|
||||
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
|
||||
)
|
||||
|
@ -11,5 +11,10 @@ mod rdrand;
|
||||
#[path = "../src/util.rs"]
|
||||
mod util;
|
||||
|
||||
use rdrand::getrandom_inner as getrandom_impl;
|
||||
// The rdrand implementation has the signature of getrandom_uninit(), but our
|
||||
// tests expect getrandom_impl() to have the signature of getrandom().
|
||||
fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> {
|
||||
rdrand::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?;
|
||||
Ok(())
|
||||
}
|
||||
mod common;
|
||||
|
Loading…
x
Reference in New Issue
Block a user