feat: works

This commit is contained in:
2022-07-17 10:11:20 +08:00
parent 4ba63b4c2e
commit 74a202f1ed
458 changed files with 125067 additions and 8 deletions

View File

@@ -0,0 +1,17 @@
// Copyright 2021 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for Nintendo 3DS
use crate::util_libc::sys_fill_exact;
use crate::Error;
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
sys_fill_exact(dest, |buf| unsafe {
libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
}

View File

@@ -0,0 +1,53 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for FreeBSD and NetBSD
use crate::{util_libc::sys_fill_exact, Error};
use core::ptr;
fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
let mut len = buf.len();
let ret = unsafe {
libc::sysctl(
MIB.as_ptr(),
MIB.len() as libc::c_uint,
buf.as_mut_ptr() as *mut _,
&mut len,
ptr::null(),
0,
)
};
if ret == -1 {
-1
} else {
len as libc::ssize_t
}
}
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
// getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
#[cfg(target_os = "freebsd")]
{
use crate::util_libc::Weak;
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;
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) });
}
}
// Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and
// older NetBSD kernels will fail on longer buffers.
for chunk in dest.chunks_mut(256) {
sys_fill_exact(chunk, kern_arnd)?
}
Ok(())
}

View File

@@ -0,0 +1,102 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! An implementation which calls out to an externally defined function.
use crate::Error;
use core::num::NonZeroU32;
/// Register a function to be invoked by `getrandom` on unsupported targets.
///
/// ## Writing a custom `getrandom` implementation
///
/// The function to register must have the same signature as
/// [`getrandom::getrandom`](crate::getrandom). The function can be defined
/// wherever you want, either in root crate or a dependent crate.
///
/// For example, if we wanted a `failure-getrandom` crate containing an
/// implementation that always fails, we would first depend on `getrandom`
/// (for the [`Error`] type) in `failure-getrandom/Cargo.toml`:
/// ```toml
/// [dependencies]
/// getrandom = "0.2"
/// ```
/// Note that the crate containing this function does **not** need to enable the
/// `"custom"` Cargo feature.
///
/// Next, in `failure-getrandom/src/lib.rs`, we define our function:
/// ```rust
/// use core::num::NonZeroU32;
/// use getrandom::Error;
///
/// // Some application-specific error code
/// const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42;
/// pub fn always_fail(buf: &mut [u8]) -> Result<(), Error> {
/// let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap();
/// Err(Error::from(code))
/// }
/// ```
///
/// ## Registering a custom `getrandom` implementation
///
/// Functions can only be registered in the root binary crate. Attempting to
/// register a function in a non-root crate will result in a linker error.
/// This is similar to
/// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or
/// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html),
/// where helper crates define handlers/allocators but only the binary crate
/// actually _uses_ the functionality.
///
/// To register the function, we first depend on `failure-getrandom` _and_
/// `getrandom` in `Cargo.toml`:
/// ```toml
/// [dependencies]
/// failure-getrandom = "0.1"
/// getrandom = { version = "0.2", features = ["custom"] }
/// ```
///
/// Then, we register the function in `src/main.rs`:
/// ```rust
/// # mod failure_getrandom { pub fn always_fail(_: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!() } }
/// use failure_getrandom::always_fail;
/// use getrandom::register_custom_getrandom;
///
/// register_custom_getrandom!(always_fail);
/// ```
///
/// Now any user of `getrandom` (direct or indirect) on this target will use the
/// registered function. As noted in the
/// [top-level documentation](index.html#custom-implementations) this
/// registration only has an effect on unsupported targets.
#[macro_export]
#[cfg_attr(docsrs, doc(cfg(feature = "custom")))]
macro_rules! register_custom_getrandom {
($path:path) => {
// We use an extern "C" function to get the guarantees of a stable ABI.
#[no_mangle]
extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
let f: fn(&mut [u8]) -> Result<(), $crate::Error> = $path;
let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) };
match f(slice) {
Ok(()) => 0,
Err(e) => e.code().get(),
}
}
};
}
#[allow(dead_code)]
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
extern "C" {
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
}
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
match NonZeroU32::new(ret) {
None => Ok(()),
Some(code) => Err(Error::from(code)),
}
}

View File

@@ -0,0 +1,27 @@
// Copyright 2021 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for DragonFly BSD
use crate::{
use_file,
util_libc::{sys_fill_exact, Weak},
Error,
};
pub fn getrandom_inner(dest: &mut [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;
// getrandom(2) was introduced in DragonflyBSD 5.7
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) });
} else {
use_file::getrandom_inner(dest)
}
}

View File

@@ -0,0 +1,187 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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::{fmt, num::NonZeroU32};
/// A small and `no_std` compatible error type
///
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
/// if so, which error code the OS gave the application. If such an error is
/// encountered, please consult with your system documentation.
///
/// Internally this type is a NonZeroU32, with certain values reserved for
/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
///
/// *If this crate's `"std"` Cargo feature is enabled*, then:
/// - [`getrandom::Error`][Error] implements
/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
/// [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);
const fn internal_error(n: u16) -> Error {
// SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
let code = Error::INTERNAL_START + (n as u32);
Error(unsafe { NonZeroU32::new_unchecked(code) })
}
impl Error {
/// This target/platform is not supported by `getrandom`.
pub const UNSUPPORTED: Error = internal_error(0);
/// The platform-specific `errno` returned a non-positive value.
pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
/// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
pub const IOS_SEC_RANDOM: Error = internal_error(3);
/// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
/// RDRAND instruction failed due to a hardware issue.
pub const FAILED_RDRAND: Error = internal_error(5);
/// RDRAND instruction unsupported on this target.
pub const NO_RDRAND: Error = internal_error(6);
/// The browser does not have support for `self.crypto`.
pub const WEB_CRYPTO: Error = internal_error(7);
/// The browser does not have support for `crypto.getRandomValues`.
pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
/// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
/// NodeJS does not have support for the `crypto` module.
pub const NODE_CRYPTO: Error = internal_error(12);
/// NodeJS does not have support for `crypto.randomFillSync`.
pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
/// reserved for use by the `rand` and `getrandom` crates.
pub const INTERNAL_START: u32 = 1 << 31;
/// Codes at or above this point can be used by users to define their own
/// custom errors.
pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);
/// Extract the raw OS error code (if this error came from the OS)
///
/// This method is identical to [`std::io::Error::raw_os_error()`][1], except
/// that it works in `no_std` contexts. If this method returns `None`, the
/// error value can still be formatted via the `Display` implementation.
///
/// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
#[inline]
pub fn raw_os_error(self) -> Option<i32> {
if self.0.get() < Self::INTERNAL_START {
match () {
#[cfg(target_os = "solid_asp3")]
// On SOLID, negate the error code again to obtain the original
// error code.
() => Some(-(self.0.get() as i32)),
#[cfg(not(target_os = "solid_asp3"))]
() => Some(self.0.get() as i32),
}
} else {
None
}
}
/// Extract the bare error code.
///
/// This code can either come from the underlying OS, or be a custom error.
/// Use [`Error::raw_os_error()`] to disambiguate.
#[inline]
pub const fn code(self) -> NonZeroU32 {
self.0
}
}
cfg_if! {
if #[cfg(unix)] {
fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
return None;
}
// Take up to trailing null byte
let n = buf.len();
let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
core::str::from_utf8(&buf[..idx]).ok()
}
} else {
fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
None
}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut dbg = f.debug_struct("Error");
if let Some(errno) = self.raw_os_error() {
dbg.field("os_error", &errno);
let mut buf = [0u8; 128];
if let Some(err) = os_err(errno, &mut buf) {
dbg.field("description", &err);
}
} else if let Some(desc) = internal_desc(*self) {
dbg.field("internal_code", &self.0.get());
dbg.field("description", &desc);
} else {
dbg.field("unknown_code", &self.0.get());
}
dbg.finish()
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(errno) = self.raw_os_error() {
let mut buf = [0u8; 128];
match os_err(errno, &mut buf) {
Some(err) => err.fmt(f),
None => write!(f, "OS Error: {}", errno),
}
} else if let Some(desc) = internal_desc(*self) {
f.write_str(desc)
} else {
write!(f, "Unknown Error: {}", self.0.get())
}
}
}
impl From<NonZeroU32> for Error {
fn from(code: NonZeroU32) -> Self {
Self(code)
}
}
fn internal_desc(error: Error) -> Option<&'static str> {
match error {
Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"),
Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"),
Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::Error;
use core::mem::size_of;
#[test]
fn test_size() {
assert_eq!(size_of::<Error>(), 4);
assert_eq!(size_of::<Result<(), Error>>(), 4);
}
}

View File

@@ -0,0 +1,24 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
#![cfg_attr(docsrs, doc(cfg(feature = "std")))]
extern crate std;
use crate::Error;
use core::convert::From;
use std::io;
impl From<Error> for io::Error {
fn from(err: Error) -> Self {
match err.raw_os_error() {
Some(errno) => io::Error::from_raw_os_error(errno),
None => io::Error::new(io::ErrorKind::Other, err),
}
}
}
impl std::error::Error for Error {}

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for ESP-IDF
use crate::Error;
use core::ffi::c_void;
extern "C" {
fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
}
pub fn getrandom_inner(dest: &mut [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
//
// However tracking if some of these entropy sources is enabled is way too difficult to implement here
unsafe { esp_fill_random(dest.as_mut_ptr().cast(), dest.len()) };
Ok(())
}

View File

@@ -0,0 +1,20 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for Fuchsia Zircon
use crate::Error;
#[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()) }
Ok(())
}

View File

@@ -0,0 +1,27 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for iOS
use crate::Error;
use core::{ffi::c_void, 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> {
// Apple's documentation guarantees kSecRandomDefault is a synonym for NULL.
let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) };
// errSecSuccess (from SecBase.h) is always zero.
if ret != 0 {
Err(Error::IOS_SEC_RANDOM)
} else {
Ok(())
}
}

View File

@@ -0,0 +1,130 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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;
extern crate std;
// use std::thread_local;
// use js_sys::{global, Uint8Array};
// use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
// const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
// enum RngSource {
// Node(NodeCrypto),
// Browser(BrowserCrypto, Uint8Array),
// }
// // JsValues are always per-thread, so we initialize RngSource for each thread.
// // See: https://github.com/rustwasm/wasm-bindgen/pull/955
// thread_local!(
// static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
// );
pub(crate) fn getrandom_inner(_dest: &mut [u8]) -> Result<(), Error> {
// RNG_SOURCE.with(|result| {
// let source = result.as_ref().map_err(|&e| e)?;
//
// match source {
// RngSource::Node(n) => {
// if n.random_fill_sync(dest).is_err() {
// return Err(Error::NODE_RANDOM_FILL_SYNC);
// }
// }
// RngSource::Browser(crypto, buf) => {
// // getRandomValues does not work with all types of WASM memory,
// // so we initially write to browser memory to avoid exceptions.
// for chunk in dest.chunks_mut(BROWSER_CRYPTO_BUFFER_SIZE) {
// // The chunk can be smaller than buf's length, so we call to
// // JS to create a smaller view of buf without allocation.
// let sub_buf = buf.subarray(0, chunk.len() as u32);
//
// if crypto.get_random_values(&sub_buf).is_err() {
// return Err(Error::WEB_GET_RANDOM_VALUES);
// }
// sub_buf.copy_to(chunk);
// }
// }
// };
// Ok(())
// })
Err(Error::UNSUPPORTED)
}
// fn getrandom_init() -> Result<RngSource, Error> {
// let global: Global = global().unchecked_into();
// if is_node(&global) {
// let crypto = NODE_MODULE
// .require("crypto")
// .map_err(|_| Error::NODE_CRYPTO)?;
// return Ok(RngSource::Node(crypto));
// }
//
// // Assume we are in some Web environment (browser or web worker). We get
// // `self.crypto` (called `msCrypto` on IE), so we can call
// // `crypto.getRandomValues`. If `crypto` isn't defined, we assume that
// // we are in an older web browser and the OS RNG isn't available.
// let crypto = match (global.crypto(), global.ms_crypto()) {
// (c, _) if c.is_object() => c,
// (_, c) if c.is_object() => c,
// _ => return Err(Error::WEB_CRYPTO),
// };
//
// let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32);
// Ok(RngSource::Browser(crypto, buf))
// }
// // Taken from https://www.npmjs.com/package/browser-or-node
// fn is_node(global: &Global) -> bool {
// let process = global.process();
// if process.is_object() {
// let versions = process.versions();
// if versions.is_object() {
// return versions.node().is_string();
// }
// }
// false
// }
// #[wasm_bindgen]
// extern "C" {
// type Global; // Return type of js_sys::global()
//
// // Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
// #[wasm_bindgen(method, getter, js_name = "msCrypto")]
// fn ms_crypto(this: &Global) -> BrowserCrypto;
// #[wasm_bindgen(method, getter)]
// fn crypto(this: &Global) -> BrowserCrypto;
// type BrowserCrypto;
// #[wasm_bindgen(method, js_name = getRandomValues, catch)]
// fn get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
//
// // We use a "module" object here instead of just annotating require() with
// // js_name = "module.require", so that Webpack doesn't give a warning. See:
// // https://github.com/rust-random/getrandom/issues/224
// type NodeModule;
// #[wasm_bindgen(js_name = module)]
// static NODE_MODULE: NodeModule;
// // Node JS crypto module (https://nodejs.org/api/crypto.html)
// #[wasm_bindgen(method, catch)]
// 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>;
//
// // Node JS process Object (https://nodejs.org/api/process.html)
// #[wasm_bindgen(method, getter)]
// fn process(this: &Global) -> Process;
// type Process;
// #[wasm_bindgen(method, getter)]
// fn versions(this: &Process) -> Versions;
// type Versions;
// #[wasm_bindgen(method, getter)]
// fn node(this: &Versions) -> JsValue;
// }

View File

@@ -0,0 +1,263 @@
// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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 operating system's random number generator.
//!
//! # Supported targets
//!
//! | Target | Target Triple | Implementation
//! | ----------------- | ------------------ | --------------
//! | Linux, Android | `*linux*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
//! | Windows | `*windows*` | [`BCryptGenRandom`]
//! | macOS | `*appledarwin` | [`getentropy`][3] if available, otherwise [`/dev/random`][4] (identical to `/dev/urandom`)
//! | iOS | `*appleios` | [`SecRandomCopyBytes`]
//! | FreeBSD | `*freebsd` | [`getrandom`][5] if available, otherwise [`kern.arandom`][6]
//! | OpenBSD | `*openbsd` | [`getentropy`][7]
//! | NetBSD | `*netbsd` | [`kern.arandom`][8]
//! | Dragonfly BSD | `*dragonfly` | [`getrandom`][9] if available, otherwise [`/dev/random`][10]
//! | Solaris, illumos | `*solaris`, `*illumos` | [`getrandom`][11] if available, otherwise [`/dev/random`][12]
//! | Fuchsia OS | `*fuchsia` | [`cprng_draw`]
//! | Redox | `*redox` | `/dev/urandom`
//! | Haiku | `*haiku` | `/dev/random` (identical to `/dev/urandom`)
//! | Hermit | `x86_64-*-hermit` | [`RDRAND`]
//! | SGX | `x86_64*sgx` | [`RDRAND`]
//! | VxWorks | `*wrsvxworks*` | `randABytes` after checking entropy pool initialization with `randSecure`
//! | ESP-IDF | `*espidf` | [`esp_fill_random`]
//! | Emscripten | `*emscripten` | `/dev/random` (identical to `/dev/urandom`)
//! | WASI | `wasm32wasi` | [`random_get`]
//! | Web Browser | `wasm32*unknown` | [`Crypto.getRandomValues`], see [WebAssembly support]
//! | Node.js | `wasm32*unknown` | [`crypto.randomBytes`], see [WebAssembly support]
//! | SOLID | `*-kmc-solid_*` | `SOLID_RNG_SampleRandomBytes`
//! | Nintendo 3DS | `armv6k-nintendo-3ds` | [`getrandom`][1]
//!
//! There is no blanket implementation on `unix` targets that reads from
//! `/dev/urandom`. This ensures all supported targets are using the recommended
//! interface and respect maximum buffer sizes.
//!
//! Pull Requests that add support for new targets to `getrandom` are always welcome.
//!
//! ## Unsupported targets
//!
//! By default, `getrandom` will not compile on unsupported targets, but certain
//! features allow a user to select a "fallback" implementation if no supported
//! implementation exists.
//!
//! All of the below mechanisms only affect unsupported
//! targets. Supported targets will _always_ use their supported implementations.
//! This prevents a crate from overriding a secure source of randomness
//! (either accidentally or intentionally).
//!
//! ### RDRAND on x86
//!
//! *If the `rdrand` Cargo feature is enabled*, `getrandom` will fallback to using
//! the [`RDRAND`] instruction to get randomness on `no_std` `x86`/`x86_64`
//! targets. This feature has no effect on other CPU architectures.
//!
//! ### WebAssembly support
//!
//! This crate fully supports the
//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and
//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/)
//! targets. However, the `wasm32-unknown-unknown` target (i.e. the target used
//! by `wasm-pack`) is not automatically
//! supported since, from the target name alone, we cannot deduce which
//! JavaScript interface is in use (or if JavaScript is available at all).
//!
//! Instead, *if the `js` Cargo feature is enabled*, this crate will assume
//! that you are building for an environment containing JavaScript, and will
//! call the appropriate methods. Both web browser (main window and Web Workers)
//! and Node.js environments are supported, invoking the methods
//! [described above](#supported-targets) using the
//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain.
//!
//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
//!
//! ### Custom implementations
//!
//! The [`register_custom_getrandom!`] macro allows a user to mark their own
//! function as the backing implementation for [`getrandom`]. See the macro's
//! documentation for more information about writing and registering your own
//! custom implementations.
//!
//! Note that registering a custom implementation only has an effect on targets
//! that would otherwise not compile. Any supported targets (including those
//! using `rdrand` and `js` Cargo features) continue using their normal
//! implementations even if a function is registered.
//!
//! ### Indirect Dependencies
//!
//! If `getrandom` is not a direct dependency of your crate, you can still
//! enable any of the above fallback behaviors by enabling the relevant
//! feature in your root crate's `Cargo.toml`:
//! ```toml
//! [dependencies]
//! getrandom = { version = "0.2", features = ["js"] }
//! ```
//!
//! ## Early boot
//!
//! Sometimes, early in the boot process, the OS has not collected enough
//! entropy to securely seed its RNG. This is especially common on virtual
//! machines, where standard "random" events are hard to come by.
//!
//! Some operating system interfaces always block until the RNG is securely
//! seeded. This can take anywhere from a few seconds to more than a minute.
//! 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 `getrandom` system call is not available), reading from
//! `/dev/urandom` never blocks, even when the OS hasn't collected enough
//! entropy yet. To avoid returning low-entropy bytes, we first poll
//! `/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 highly 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.
//!
//! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html
//! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html
//! [3]: https://www.unix.com/man-page/mojave/2/getentropy/
//! [4]: https://www.unix.com/man-page/mojave/4/random/
//! [5]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable
//! [6]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4
//! [7]: https://man.openbsd.org/getentropy.2
//! [8]: https://man.netbsd.org/sysctl.7
//! [9]: https://leaf.dragonflybsd.org/cgi/web-man?command=getrandom
//! [10]: https://leaf.dragonflybsd.org/cgi/web-man?command=random&section=4
//! [11]: https://docs.oracle.com/cd/E88353_01/html/E37841/getrandom-2.html
//! [12]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html
//!
//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
//! [`Crypto.getRandomValues`]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues
//! [`RDRAND`]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide
//! [`SecRandomCopyBytes`]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc
//! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [`crypto.randomBytes`]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback
//! [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t
//! [`random_get`]: https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
//! [WebAssembly support]: #webassembly-support
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://docs.rs/getrandom/0.2.7"
)]
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[macro_use]
extern crate cfg_if;
mod error;
mod util;
// To prevent a breaking change when targets are added, we always export the
// register_custom_getrandom macro, so old Custom RNG crates continue to build.
#[cfg(feature = "custom")]
mod custom;
#[cfg(feature = "std")]
mod error_impls;
pub use crate::error::Error;
// System-specific implementations.
//
// These should all provide getrandom_inner with the same signature as getrandom.
cfg_if! {
if #[cfg(any(target_os = "emscripten", target_os = "haiku",
target_os = "redox"))] {
mod util_libc;
#[path = "use_file.rs"] mod imp;
} else if #[cfg(any(target_os = "android", target_os = "linux"))] {
mod util_libc;
mod use_file;
#[path = "linux_android.rs"] mod imp;
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
mod util_libc;
mod use_file;
#[path = "solaris_illumos.rs"] mod imp;
} else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] {
mod util_libc;
#[path = "bsd_arandom.rs"] mod imp;
} else if #[cfg(target_os = "dragonfly")] {
mod util_libc;
mod use_file;
#[path = "dragonfly.rs"] mod imp;
} else if #[cfg(target_os = "fuchsia")] {
#[path = "fuchsia.rs"] mod imp;
} else if #[cfg(target_os = "ios")] {
#[path = "ios.rs"] mod imp;
} else if #[cfg(target_os = "macos")] {
mod util_libc;
mod use_file;
#[path = "macos.rs"] mod imp;
} else if #[cfg(target_os = "openbsd")] {
mod util_libc;
#[path = "openbsd.rs"] mod imp;
} else if #[cfg(target_os = "wasi")] {
#[path = "wasi.rs"] mod imp;
} else if #[cfg(all(target_arch = "x86_64", target_os = "hermit"))] {
#[path = "rdrand.rs"] mod imp;
} else if #[cfg(target_os = "vxworks")] {
mod util_libc;
#[path = "vxworks.rs"] mod imp;
} else if #[cfg(target_os = "solid_asp3")] {
#[path = "solid.rs"] mod imp;
} else if #[cfg(target_os = "espidf")] {
#[path = "espidf.rs"] mod imp;
} else if #[cfg(windows)] {
#[path = "windows.rs"] mod imp;
} else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
#[path = "rdrand.rs"] mod imp;
} else if #[cfg(all(feature = "rdrand",
any(target_arch = "x86_64", target_arch = "x86")))] {
#[path = "rdrand.rs"] mod imp;
} else if #[cfg(all(feature = "js",
target_arch = "wasm32", target_os = "unknown"))] {
#[path = "js.rs"] mod imp;
} else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {
// We check for target_arch = "arm" because the Nintendo Switch also
// uses Horizon OS (it is aarch64).
mod util_libc;
#[path = "3ds.rs"] mod imp;
} else if #[cfg(feature = "custom")] {
use custom as imp;
} else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] {
compile_error!("the wasm32-unknown-unknown target is not supported by \
default, you may need to enable the \"js\" feature. \
For more information see: \
https://docs.rs/getrandom/#webassembly-support");
} else {
compile_error!("target is not supported, for more information see: \
https://docs.rs/getrandom/#unsupported-targets");
}
}
/// 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. If `dest` is
/// empty, `getrandom` immediately returns success, making no calls to the
/// underlying operating system.
///
/// 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> {
if dest.is_empty() {
return Ok(());
}
imp::getrandom_inner(dest)
}

View File

@@ -0,0 +1,47 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for Linux / Android
use crate::{
util::LazyBool,
util_libc::{last_os_error, sys_fill_exact},
{use_file, Error},
};
pub fn getrandom_inner(dest: &mut [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) {
sys_fill_exact(dest, |buf| unsafe {
getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
} else {
use_file::getrandom_inner(dest)
}
}
fn is_getrandom_available() -> bool {
let res = unsafe { getrandom(core::ptr::null_mut(), 0, libc::GRND_NONBLOCK) };
if res < 0 {
match last_os_error().raw_os_error() {
Some(libc::ENOSYS) => false, // No kernel support
Some(libc::EPERM) => false, // Blocked by seccomp
_ => true,
}
} else {
true
}
}
unsafe fn getrandom(
buf: *mut libc::c_void,
buflen: libc::size_t,
flags: libc::c_uint,
) -> libc::ssize_t {
libc::syscall(libc::SYS_getrandom, buf, buflen, flags) as libc::ssize_t
}

View File

@@ -0,0 +1,36 @@
// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for macOS
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> {
// 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()) };
if ret != 0 {
return Err(last_os_error());
}
}
Ok(())
} else {
// We fallback to reading from /dev/random instead of SecRandomCopyBytes
// to avoid high startup costs and linking the Security framework.
use_file::getrandom_inner(dest)
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for OpenBSD
use crate::{util_libc::last_os_error, Error};
pub fn getrandom_inner(dest: &mut [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()) };
if ret == -1 {
return Err(last_os_error());
}
}
Ok(())
}

View File

@@ -0,0 +1,97 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for SGX using RDRAND instruction
use crate::Error;
use core::mem;
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
use core::arch::x86_64 as arch;
use arch::_rdrand64_step as rdrand_step;
} else if #[cfg(target_arch = "x86")] {
use core::arch::x86 as arch;
use arch::_rdrand32_step as rdrand_step;
}
}
// Recommendation from "Intel® Digital Random Number Generator (DRNG) Software
// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
// Software Developers Manual" - Volume 1 - Section 7.3.17.1.
const RETRY_LIMIT: usize = 10;
const WORD_SIZE: usize = mem::size_of::<usize>();
#[target_feature(enable = "rdrand")]
unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> {
for _ in 0..RETRY_LIMIT {
let mut el = mem::zeroed();
if rdrand_step(&mut el) == 1 {
// AMD CPUs from families 14h to 16h (pre Ryzen) sometimes fail to
// set CF on bogus random data, so we check these values explicitly.
// See https://github.com/systemd/systemd/issues/11810#issuecomment-489727505
// We perform this check regardless of target to guard against
// any implementation that incorrectly fails to set CF.
if el != 0 && el != !0 {
return Ok(el.to_ne_bytes());
}
// Keep looping in case this was a false positive.
}
}
Err(Error::FAILED_RDRAND)
}
// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653.
#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))]
compile_error!(
"SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd."
);
#[cfg(target_feature = "rdrand")]
fn is_rdrand_supported() -> bool {
true
}
// TODO use is_x86_feature_detected!("rdrand") when that works in core. See:
// https://github.com/rust-lang-nursery/stdsimd/issues/464
#[cfg(not(target_feature = "rdrand"))]
fn is_rdrand_supported() -> bool {
use crate::util::LazyBool;
// SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID
// is supported, CPUID leaf 1 is always supported.
const FLAG: u32 = 1 << 30;
static HAS_RDRAND: LazyBool = LazyBool::new();
HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 })
}
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
if !is_rdrand_supported() {
return Err(Error::NO_RDRAND);
}
// SAFETY: After this point, rdrand is supported, so calling the rdrand
// functions is not undefined behavior.
unsafe { rdrand_exact(dest) }
}
#[target_feature(enable = "rdrand")]
unsafe fn rdrand_exact(dest: &mut [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 tail = chunks.into_remainder();
let n = tail.len();
if n > 0 {
tail.copy_from_slice(&rdrand()?[..n]);
}
Ok(())
}

View File

@@ -0,0 +1,48 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for the Solaris family
//!
//! Read from `/dev/random`, with chunks of limited size (256 bytes).
//! `/dev/random` uses the Hash_DRBG with SHA512 algorithm from NIST SP 800-90A.
//! `/dev/urandom` uses the FIPS 186-2 algorithm, which is considered less
//! secure. We choose to read from `/dev/random`.
//!
//! Since Solaris 11.3 and mid-2015 illumos, the `getrandom` syscall is available.
//! 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 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> {
// 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() {
let func: GetRandomFn = unsafe { mem::transmute(fptr) };
// 256 bytes is the lowest common denominator across all the Solaris
// 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
})?
}
Ok(())
} else {
use_file::getrandom_inner(dest)
}
}

View File

@@ -0,0 +1,26 @@
// Copyright 2021 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for SOLID
use crate::Error;
use core::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()) };
if ret >= 0 {
Ok(())
} else {
// ITRON error numbers are always negative, so we negate it so that it
// falls in the dedicated OS error range (1..INTERNAL_START).
Err(NonZeroU32::new((-ret) as u32).unwrap().into())
}
}

View File

@@ -0,0 +1,139 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementations that just need to read from a file
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",
target_os = "emscripten",
target_os = "haiku",
target_os = "macos",
target_os = "solaris",
target_os = "illumos"
))]
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> {
let fd = get_rng_fd()?;
let read = |buf: &mut [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.
for chunk in dest.chunks_mut(65536) {
sys_fill_exact(chunk, read)?;
}
} else {
sys_fill_exact(dest, read)?;
}
Ok(())
}
// Returns the file descriptor for the device file used to retrieve random
// bytes. The file will be opened exactly once. All subsequent calls will
// return the same file descriptor. This file descriptor is never closed.
fn get_rng_fd() -> Result<libc::c_int, Error> {
static FD: AtomicUsize = AtomicUsize::new(LazyUsize::UNINIT);
fn get_fd() -> Option<libc::c_int> {
match FD.load(Relaxed) {
LazyUsize::UNINIT => None,
val => Some(val as libc::c_int),
}
}
// Use double-checked locking to avoid acquiring the lock if possible.
if let Some(fd) = get_fd() {
return Ok(fd);
}
// SAFETY: We use the mutex only in this method, and we always unlock it
// before returning, making sure we don't violate the pthread_mutex_t API.
static MUTEX: Mutex = Mutex::new();
unsafe { MUTEX.lock() };
let _guard = DropGuard(|| unsafe { MUTEX.unlock() });
if let Some(fd) = get_fd() {
return Ok(fd);
}
// On Linux, /dev/urandom might return insecure values.
#[cfg(any(target_os = "android", target_os = "linux"))]
wait_until_rng_ready()?;
let fd = unsafe { open_readonly(FILE_PATH)? };
// The fd always fits in a usize without conflicting with UNINIT.
debug_assert!(fd >= 0 && (fd as usize) < LazyUsize::UNINIT);
FD.store(fd as usize, Relaxed);
Ok(fd)
}
// Succeeds once /dev/urandom is safe to read from
#[cfg(any(target_os = "android", target_os = "linux"))]
fn wait_until_rng_ready() -> Result<(), Error> {
// Poll /dev/random to make sure it is ok to read from /dev/urandom.
let fd = unsafe { open_readonly("/dev/random\0")? };
let mut pfd = libc::pollfd {
fd,
events: libc::POLLIN,
revents: 0,
};
let _guard = DropGuard(|| unsafe {
libc::close(fd);
});
loop {
// A negative timeout means an infinite timeout.
let res = unsafe { libc::poll(&mut pfd, 1, -1) };
if res >= 0 {
debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout.
return Ok(());
}
let err = crate::util_libc::last_os_error();
match err.raw_os_error() {
Some(libc::EINTR) | Some(libc::EAGAIN) => continue,
_ => return Err(err),
}
}
}
struct Mutex(UnsafeCell<libc::pthread_mutex_t>);
impl Mutex {
const fn new() -> Self {
Self(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))
}
unsafe fn lock(&self) {
let r = libc::pthread_mutex_lock(self.0.get());
debug_assert_eq!(r, 0);
}
unsafe fn unlock(&self) {
let r = libc::pthread_mutex_unlock(self.0.get());
debug_assert_eq!(r, 0);
}
}
unsafe impl Sync for Mutex {}
struct DropGuard<F: FnMut()>(F);
impl<F: FnMut()> Drop for DropGuard<F> {
fn drop(&mut self) {
self.0()
}
}

View File

@@ -0,0 +1,64 @@
// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
#![allow(dead_code)]
use core::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.
// Both unsync_init and sync_init will invoke an init() function until it
// succeeds, then return the cached value for future calls.
//
// Both methods support init() "failing". If the init() method returns UNINIT,
// that value will be returned as normal, but will not be cached.
//
// Users should only depend on the _value_ returned by init() functions.
// Specifically, for the following init() function:
// fn init() -> usize {
// a();
// let v = b();
// c();
// v
// }
// the effects of c() or writes to shared memory will not necessarily be
// observed and additional synchronization methods with be needed.
pub struct LazyUsize(AtomicUsize);
impl LazyUsize {
pub const fn new() -> Self {
Self(AtomicUsize::new(Self::UNINIT))
}
// The initialization is not completed.
pub const UNINIT: usize = usize::max_value();
// Runs the init() function at least once, returning the value of some run
// of init(). Multiple callers can run their init() functions in parallel.
// init() should always return the same value, if it succeeds.
pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize {
// Relaxed ordering is fine, as we only have a single atomic variable.
let mut val = self.0.load(Relaxed);
if val == Self::UNINIT {
val = init();
self.0.store(val, Relaxed);
}
val
}
}
// Identical to LazyUsize except with bool instead of usize.
pub struct LazyBool(LazyUsize);
impl LazyBool {
pub const fn new() -> Self {
Self(LazyUsize::new())
}
pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
self.0.unsync_init(|| init() as usize) != 0
}
}

View File

@@ -0,0 +1,160 @@
// Copyright 2019 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
#![allow(dead_code)]
use crate::Error;
use core::{
num::NonZeroU32,
ptr::NonNull,
sync::atomic::{fence, AtomicPtr, Ordering},
};
use libc::c_void;
cfg_if! {
if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] {
use libc::__errno as errno_location;
} else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "redox"))] {
use libc::__errno_location as errno_location;
} else if #[cfg(any(target_os = "solaris", target_os = "illumos"))] {
use libc::___errno as errno_location;
} else if #[cfg(any(target_os = "macos", target_os = "freebsd"))] {
use libc::__error as errno_location;
} else if #[cfg(target_os = "haiku")] {
use libc::_errnop as errno_location;
} else if #[cfg(all(target_os = "horizon", target_arch = "arm"))] {
extern "C" {
// Not provided by libc: https://github.com/rust-lang/libc/issues/1995
fn __errno() -> *mut libc::c_int;
}
use __errno as errno_location;
}
}
cfg_if! {
if #[cfg(target_os = "vxworks")] {
use libc::errnoGet as get_errno;
} else if #[cfg(target_os = "dragonfly")] {
// Until rust-lang/rust#29594 is stable, we cannot get the errno value
// on DragonFlyBSD. So we just return an out-of-range errno.
unsafe fn get_errno() -> libc::c_int { -1 }
} else {
unsafe fn get_errno() -> libc::c_int { *errno_location() }
}
}
pub fn last_os_error() -> Error {
let errno = unsafe { get_errno() };
if errno > 0 {
Error::from(NonZeroU32::new(errno as u32).unwrap())
} else {
Error::ERRNO_NOT_POSITIVE
}
}
// Fill a buffer by repeatedly invoking a system call. The `sys_fill` function:
// - 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,
) -> Result<(), Error> {
while !buf.is_empty() {
let res = sys_fill(buf);
if res < 0 {
let err = last_os_error();
// We should try again if the call was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
return Err(err);
}
} else {
// We don't check for EOF (ret = 0) as the data we are reading
// should be an infinite stream of random bytes.
buf = &mut buf[(res as usize)..];
}
}
Ok(())
}
// A "weak" binding to a C function that may or may not be present at runtime.
// Used for supporting newer OS features while still building on older systems.
// Based off of the DlsymWeak struct in libstd:
// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
// except that the caller must manually cast self.ptr() to a function pointer.
pub struct Weak {
name: &'static str,
addr: AtomicPtr<c_void>,
}
impl Weak {
// A non-null pointer value which indicates we are uninitialized. This
// constant should ideally not be a valid address of a function pointer.
// However, if by chance libc::dlsym does return UNINIT, there will not
// be undefined behavior. libc::dlsym will just be called each time ptr()
// is called. This would be inefficient, but correct.
// TODO: Replace with core::ptr::invalid_mut(1) when that is stable.
const UNINIT: *mut c_void = 1 as *mut c_void;
// Construct a binding to a C function with a given name. This function is
// unsafe because `name` _must_ be null terminated.
pub const unsafe fn new(name: &'static str) -> Self {
Self {
name,
addr: AtomicPtr::new(Self::UNINIT),
}
}
// Return the address of a function if present at runtime. Otherwise,
// return None. Multiple callers can call ptr() concurrently. It will
// always return _some_ value returned by libc::dlsym. However, the
// dlsym function may be called multiple times.
pub fn ptr(&self) -> Option<NonNull<c_void>> {
// Despite having only a single atomic variable (self.addr), we still
// cannot always use Ordering::Relaxed, as we need to make sure a
// successful call to dlsym() is "ordered before" any data read through
// the returned pointer (which occurs when the function is called).
// Our implementation mirrors that of the one in libstd, meaning that
// the use of non-Relaxed operations is probably unnecessary.
match self.addr.load(Ordering::Relaxed) {
Self::UNINIT => {
let symbol = self.name.as_ptr() as *const _;
let addr = unsafe { libc::dlsym(libc::RTLD_DEFAULT, symbol) };
// Synchronizes with the Acquire fence below
self.addr.store(addr, Ordering::Release);
NonNull::new(addr)
}
addr => {
let func = NonNull::new(addr)?;
fence(Ordering::Acquire);
Some(func)
}
}
}
}
cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "emscripten"))] {
use libc::open64 as open;
} else {
use libc::open;
}
}
// SAFETY: path must be null terminated, FD must be manually closed.
pub unsafe fn open_readonly(path: &str) -> Result<libc::c_int, Error> {
debug_assert_eq!(path.as_bytes().last(), Some(&0));
loop {
let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
if fd >= 0 {
return Ok(fd);
}
let err = last_os_error();
// We should try again if open() was interrupted.
if err.raw_os_error() != Some(libc::EINTR) {
return Err(err);
}
}
}

View File

@@ -0,0 +1,34 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for VxWorks
use crate::{util_libc::last_os_error, Error};
use core::sync::atomic::{AtomicBool, Ordering::Relaxed};
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
static RNG_INIT: AtomicBool = AtomicBool::new(false);
while !RNG_INIT.load(Relaxed) {
let ret = unsafe { libc::randSecure() };
if ret < 0 {
return Err(Error::VXWORKS_RAND_SECURE);
} else if ret > 0 {
RNG_INIT.store(true, Relaxed);
break;
}
unsafe { libc::usleep(10) };
}
// 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) };
if ret != 0 {
return Err(last_os_error());
}
}
Ok(())
}

View File

@@ -0,0 +1,19 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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.
//! Implementation for WASI
use crate::Error;
use core::num::NonZeroU32;
use wasi::wasi_snapshot_preview1::random_get;
pub fn getrandom_inner(dest: &mut [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()),
}
}

View File

@@ -0,0 +1,49 @@
// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <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 core::{ffi::c_void, num::NonZeroU32, ptr};
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
#[link(name = "bcrypt")]
extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut c_void,
pBuffer: *mut u8,
cbBuffer: u32,
dwFlags: u32,
) -> u32;
}
pub fn getrandom_inner(dest: &mut [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.len() as u32,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
// NTSTATUS codes use the two highest bits for severity status.
if ret >> 30 == 0b11 {
// We zeroize the highest bit, so the error code will reside
// inside the range designated for OS codes.
let code = ret ^ (1 << 31);
// SAFETY: the second highest bit is always equal to one,
// so it's impossible to get zero. Unfortunately the type
// system does not have a way to express this yet.
let code = unsafe { NonZeroU32::new_unchecked(code) };
return Err(Error::from(code));
}
}
Ok(())
}