diff --git a/Cargo.toml b/Cargo.toml
index 56e8f48fd6..05d397ab78 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -161,6 +161,7 @@ all-features = true
name = "ring"
[dependencies]
+getrandom = { version = "0.2.8" }
untrusted = { version = "0.9" }
[target.'cfg(any(target_arch = "x86",target_arch = "x86_64", all(any(target_arch = "aarch64", target_arch = "arm"), any(target_os = "android", target_os = "fuchsia", target_os = "linux", target_os = "windows"))))'.dependencies]
@@ -168,15 +169,8 @@ spin = { version = "0.9.2", default-features = false, features = ["once"] }
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
libc = { version = "0.2.100", default-features = false }
-once_cell = { version = "1.8.0", default-features = false, features=["std"], optional = true }
-[target.'cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "netbsd", target_os = "openbsd", target_os = "redox", target_os = "solaris"))'.dependencies]
-once_cell = { version = "1.8.0", default-features = false, features=["std"] }
-
-[target.'cfg(all(target_arch = "wasm32", target_vendor = "unknown", target_os = "unknown", target_env = ""))'.dependencies]
-web-sys = { version = "0.3.51", default-features = false, features = ["Crypto", "Window"], optional = true }
-
-[target.'cfg(target_os = "windows")'.dependencies]
+[target.'cfg(all(target_arch = "aarch64", target_os = "windows"))'.dependencies]
winapi = { version = "0.3.9", default-features = false, features = ["ntsecapi", "wtypesbase", "processthreadsapi"] }
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
@@ -195,11 +189,11 @@ criterion = { version = "0.4", default-features = false }
# These features are documented in the top-level module's documentation.
default = ["alloc", "dev_urandom_fallback"]
alloc = []
-dev_urandom_fallback = ["once_cell"]
+dev_urandom_fallback = []
slow_tests = []
std = ["alloc"]
test_logging = []
-wasm32_unknown_unknown_js = ["web-sys"]
+wasm32_unknown_unknown_js = ["getrandom/js"]
# XXX: debug = false because of https://github.com/rust-lang/rust/issues/34122
diff --git a/mk/install-build-tools.sh b/mk/install-build-tools.sh
index 8c0c37ad9e..1475bc4529 100755
--- a/mk/install-build-tools.sh
+++ b/mk/install-build-tools.sh
@@ -73,9 +73,7 @@ case $target in
use_clang=1
;;
--target=wasm32-unknown-unknown)
- # The version of wasm-bindgen-cli must match the wasm-bindgen version.
- wasm_bindgen_version=$(cargo metadata --format-version 1 | jq -r '.packages | map(select( .name == "wasm-bindgen")) | map(.version) | .[0]')
- cargo install wasm-bindgen-cli --vers "$wasm_bindgen_version" --bin wasm-bindgen-test-runner
+ cargo install wasm-bindgen-cli --bin wasm-bindgen-test-runner
use_clang=1
;;
--target=*)
diff --git a/src/lib.rs b/src/lib.rs
index e4240c74e2..0ccc8a2498 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -22,14 +22,6 @@
//!
Description
//! | alloc (default)
//! | Enable features that require use of the heap, RSA in particular.
-//! |
dev_urandom_fallback (default)
-//! | This is only applicable to Linux. On Linux, by default,
-//! ring::rand::SystemRandom will fall back to reading
-//! from /dev/urandom if the getrandom()
-//! syscall isn't supported at runtime. When the
-//! dev_urandom_fallback feature is disabled, such
-//! fallbacks will not occur. See the documentation for
-//! rand::SystemRandom for more details.
//! |
std
//! | Enable features that use libstd, in particular
//! std::error::Error integration. Implies `alloc`.
@@ -37,7 +29,8 @@
//! | When this feature is enabled, for the wasm32-unknown-unknown target,
//! Web APIs will be used to implement features like `ring::rand` that
//! require an operating environment of some kind. This has no effect
-//! for any other target.
+//! for any other target. This enables the `getrandom` crate's `js`
+//! feature.
//!
// When running mk/package.sh, don't actually build any code.
diff --git a/src/rand.rs b/src/rand.rs
index bd8b98ae3c..495ec60b30 100644
--- a/src/rand.rs
+++ b/src/rand.rs
@@ -95,6 +95,11 @@ impl RandomlyConstructable for T where T: self::sealed::RandomlyConstructable
/// A secure random number generator where the random values come directly
/// from the operating system.
///
+/// "Directly from the operating system" here presently means "whatever the
+/// `getrandom` crate does" but that may change in the future. That roughly
+/// means calling libc's `getrandom` function or whatever is analogous to that;
+/// see the `getrandom` crate's documentation for more info.
+///
/// A single `SystemRandom` may be shared across multiple threads safely.
///
/// `new()` is guaranteed to always succeed and to have low latency; it won't
@@ -103,34 +108,6 @@ impl RandomlyConstructable for T where T: self::sealed::RandomlyConstructable
/// initialization is deferred to it. Therefore, it may be a good idea to call
/// `fill()` once at a non-latency-sensitive time to minimize latency for
/// future calls.
-///
-/// On Linux (including Android), `fill()` will use the [`getrandom`] syscall.
-/// If the kernel is too old to support `getrandom` then by default `fill()`
-/// falls back to reading from `/dev/urandom`. This decision is made the first
-/// time `fill` *succeeds*. The fallback to `/dev/urandom` can be disabled by
-/// disabling the `dev_urandom_fallback` default feature; this should be done
-/// whenever the target system is known to support `getrandom`. When
-/// `/dev/urandom` is used, a file handle for `/dev/urandom` won't be opened
-/// until `fill` is called; `SystemRandom::new()` will not open `/dev/urandom`
-/// or do other potentially-high-latency things. The file handle will never be
-/// closed, until the operating system closes it at process shutdown. All
-/// instances of `SystemRandom` will share a single file handle. To properly
-/// implement seccomp filtering when the `dev_urandom_fallback` default feature
-/// is disabled, allow `getrandom` through. When the fallback is enabled, allow
-/// file opening, `getrandom`, and `read` up until the first call to `fill()`
-/// succeeds; after that, allow `getrandom` and `read`.
-///
-/// On macOS and iOS, `fill()` is implemented using `SecRandomCopyBytes`.
-///
-/// On wasm32-unknown-unknown when the "wasm32_unknown_unknown_js" feature is
-/// enabled, `fill()` is implemented using `window.crypto.getRandomValues()`.
-/// It must be used in a context where the global object is a `Window`; i.e. it
-/// must not be used in a Worker or a non-browser context.
-///
-/// On Windows, `fill` is implemented using the platform's API for secure
-/// random number generation.
-///
-/// [`getrandom`]: http://man7.org/linux/man-pages/man2/getrandom.2.html
#[derive(Clone, Debug)]
pub struct SystemRandom(());
@@ -142,270 +119,33 @@ impl SystemRandom {
}
}
-impl sealed::SecureRandom for SystemRandom {
- #[inline(always)]
- fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
- fill_impl(dest)
- }
-}
-
impl crate::sealed::Sealed for SystemRandom {}
+// Use the `getrandom` crate whenever it is using the environment's (operating
+// system's) CSPRNG. Avoid using it on targets where it uses the `rdrand`
+// implementation.
#[cfg(any(
- all(
- any(target_os = "android", target_os = "linux"),
- not(feature = "dev_urandom_fallback")
- ),
- target_arch = "wasm32",
- windows
-))]
-use self::sysrand::fill as fill_impl;
-
-#[cfg(all(
- any(target_os = "android", target_os = "linux"),
- feature = "dev_urandom_fallback"
-))]
-use self::sysrand_or_urandom::fill as fill_impl;
-
-#[cfg(any(
+ target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris",
-))]
-use self::urandom::fill as fill_impl;
-
-#[cfg(any(target_os = "macos", target_os = "ios"))]
-use self::darwin::fill as fill_impl;
-
-#[cfg(any(target_os = "fuchsia"))]
-use self::fuchsia::fill as fill_impl;
-
-#[cfg(any(target_os = "android", target_os = "linux"))]
-mod sysrand_chunk {
- use crate::{c, error};
-
- #[inline]
- pub fn chunk(dest: &mut [u8]) -> Result {
- let chunk_len: c::size_t = dest.len();
- let r = unsafe { libc::syscall(libc::SYS_getrandom, dest.as_mut_ptr(), chunk_len, 0) };
- if r < 0 {
- let errno;
-
- #[cfg(target_os = "linux")]
- {
- errno = unsafe { *libc::__errno_location() };
- }
-
- #[cfg(target_os = "android")]
- {
- errno = unsafe { *libc::__errno() };
- }
-
- if errno == libc::EINTR {
- // If an interrupt occurs while getrandom() is blocking to wait
- // for the entropy pool, then EINTR is returned. Returning 0
- // will cause the caller to try again.
- return Ok(0);
- }
- return Err(error::Unspecified);
- }
- Ok(r as usize)
- }
-}
-
-#[cfg(all(
- feature = "wasm32_unknown_unknown_js",
- target_arch = "wasm32",
- target_vendor = "unknown",
- target_os = "unknown",
- target_env = "",
-))]
-mod sysrand_chunk {
- use crate::error;
-
- pub fn chunk(mut dest: &mut [u8]) -> Result {
- // This limit is specified in
- // https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues.
- const MAX_LEN: usize = 65_536;
- if dest.len() > MAX_LEN {
- dest = &mut dest[..MAX_LEN];
- };
-
- let _ = web_sys::window()
- .ok_or(error::Unspecified)?
- .crypto()
- .map_err(|_| error::Unspecified)?
- .get_random_values_with_u8_array(dest)
- .map_err(|_| error::Unspecified)?;
-
- Ok(dest.len())
- }
-}
-
-#[cfg(windows)]
-mod sysrand_chunk {
- use crate::{error, polyfill};
-
- #[inline]
- pub fn chunk(dest: &mut [u8]) -> Result {
- use winapi::shared::wtypesbase::ULONG;
-
- assert!(core::mem::size_of::() >= core::mem::size_of::());
- let len = core::cmp::min(dest.len(), polyfill::usize_from_u32(ULONG::max_value()));
- let result = unsafe {
- winapi::um::ntsecapi::RtlGenRandom(
- dest.as_mut_ptr() as *mut winapi::ctypes::c_void,
- len as ULONG,
- )
- };
- if result == 0 {
- return Err(error::Unspecified);
- }
-
- Ok(len)
- }
-}
-
-#[cfg(any(
- target_os = "android",
- target_os = "linux",
- target_arch = "wasm32",
- windows
-))]
-mod sysrand {
- use super::sysrand_chunk::chunk;
- use crate::error;
-
- pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
- let mut read_len = 0;
- while read_len < dest.len() {
- let chunk_len = chunk(&mut dest[read_len..])?;
- read_len += chunk_len;
- }
- Ok(())
- }
-}
-
-// Keep the `cfg` conditions in sync with the conditions in lib.rs.
-#[cfg(all(
- any(target_os = "android", target_os = "linux"),
- feature = "dev_urandom_fallback"
-))]
-mod sysrand_or_urandom {
- use crate::error;
-
- enum Mechanism {
- Sysrand,
- DevURandom,
- }
-
- #[inline]
- pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
- use once_cell::sync::Lazy;
- static MECHANISM: Lazy = Lazy::new(|| {
- let mut dummy = [0u8; 1];
- if super::sysrand_chunk::chunk(&mut dummy[..]).is_err() {
- Mechanism::DevURandom
- } else {
- Mechanism::Sysrand
- }
- });
-
- match *MECHANISM {
- Mechanism::Sysrand => super::sysrand::fill(dest),
- Mechanism::DevURandom => super::urandom::fill(dest),
- }
- }
-}
-
-#[cfg(any(
+ target_os = "windows",
all(
- any(target_os = "android", target_os = "linux"),
- feature = "dev_urandom_fallback"
+ feature = "wasm32_unknown_unknown_js",
+ target_arch = "wasm32",
+ target_os = "unknown",
),
- target_os = "dragonfly",
- target_os = "freebsd",
- target_os = "netbsd",
- target_os = "openbsd",
- target_os = "redox",
- target_os = "solaris",
- target_os = "illumos"
))]
-mod urandom {
- use crate::error;
-
- #[cfg_attr(any(target_os = "android", target_os = "linux"), cold, inline(never))]
- pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
- extern crate std;
-
- use once_cell::sync::Lazy;
-
- static FILE: Lazy> =
- Lazy::new(|| std::fs::File::open("/dev/urandom"));
-
- match *FILE {
- Ok(ref file) => {
- use std::io::Read;
- #[allow(clippy::borrow_deref_ref)]
- (&*file).read_exact(dest).map_err(|_| error::Unspecified)
- }
- Err(_) => Err(error::Unspecified),
- }
- }
-}
-
-#[cfg(any(target_os = "macos", target_os = "ios"))]
-mod darwin {
- use crate::{c, error};
-
- pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
- let r = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) };
- match r {
- 0 => Ok(()),
- _ => Err(error::Unspecified),
- }
- }
-
- // XXX: This is emulating an opaque type with a non-opaque type. TODO: Fix
- // this when
- // https://github.com/rust-lang/rfcs/pull/1861#issuecomment-274613536 is
- // resolved.
- //
- // Defined in `SecRandom.h`
- #[repr(C)]
- struct __SecRandom([u8; 0]);
- type SecRandomRef = *const __SecRandom;
-
- // https://developer.apple.com/documentation/security/ksecrandomdefault
- // says "This constant is a synonym for NULL."
- #[allow(non_upper_case_globals)]
- const kSecRandomDefault: SecRandomRef = core::ptr::null();
-
- #[link(name = "Security", kind = "framework")]
- extern "C" {
- // For now `rnd` must be `kSecRandomDefault`.
- #[must_use]
- fn SecRandomCopyBytes(rnd: SecRandomRef, count: c::size_t, bytes: *mut u8) -> c::int;
- }
-}
-
-#[cfg(any(target_os = "fuchsia"))]
-mod fuchsia {
- use crate::error;
-
- pub fn fill(dest: &mut [u8]) -> Result<(), error::Unspecified> {
- unsafe {
- zx_cprng_draw(dest.as_mut_ptr(), dest.len());
- }
- Ok(())
- }
-
- #[link(name = "zircon")]
- extern "C" {
- fn zx_cprng_draw(buffer: *mut u8, length: usize);
+impl sealed::SecureRandom for SystemRandom {
+ #[inline(always)]
+ fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
+ getrandom::getrandom(dest).map_err(|_| error::Unspecified)
}
}
|