aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:03:06 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:03:06 +0000
commitf4b5c5a8081ff86ba216719a49aa956748a8705e (patch)
tree34ebe92523b65cd444f0b368644cf41f8dc9d5ec
parentae8425f47991c0dce547d36bde0d305e98e96d3a (diff)
parent370648b9291c82bbfb0b3efed0b00fe8bae89257 (diff)
downloadgetrandom-android14-mainline-cellbroadcast-release.tar.gz
Change-Id: I33abd6bb0a4bb2580afeaa899b432d068d871ef2
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp9
-rw-r--r--CHANGELOG.md53
-rw-r--r--Cargo.toml7
-rw-r--r--Cargo.toml.orig6
-rw-r--r--METADATA12
-rw-r--r--TEST_MAPPING64
-rw-r--r--benches/mod.rs88
-rw-r--r--cargo2android.json4
-rw-r--r--src/3ds.rs17
-rw-r--r--src/bsd_arandom.rs1
-rw-r--r--src/custom.rs2
-rw-r--r--src/dragonfly.rs1
-rw-r--r--src/error.rs22
-rw-r--r--src/js.rs87
-rw-r--r--src/lib.rs76
-rw-r--r--src/linux_android.rs1
-rw-r--r--src/macos.rs1
-rw-r--r--src/openbsd.rs1
-rw-r--r--src/solaris_illumos.rs1
-rw-r--r--src/util_libc.rs77
-rw-r--r--src/wasi.rs12
-rw-r--r--src/windows.rs1
23 files changed, 393 insertions, 152 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 279a9af..fc6c877 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "ffd22552daa7d21b77ec20c1623bb4789b02122a"
+ "sha1": "5c1bb00b74a2c72ba182171b93d1c4b9d30c10c4"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 27336fc..a23a93d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -42,7 +42,7 @@ rust_test {
host_supported: true,
crate_name: "getrandom",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.5",
+ cargo_pkg_version: "0.2.8",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -61,7 +61,7 @@ rust_defaults {
name: "getrandom_test_defaults",
crate_name: "getrandom",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.5",
+ cargo_pkg_version: "0.2.8",
test_suites: ["general-tests"],
auto_gen_config: true,
edition: "2018",
@@ -98,7 +98,7 @@ rust_library {
host_supported: true,
crate_name: "getrandom",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.5",
+ cargo_pkg_version: "0.2.8",
srcs: ["src/lib.rs"],
edition: "2018",
features: ["std"],
@@ -108,7 +108,8 @@ rust_library {
],
apex_available: [
"//apex_available:platform",
- "com.android.virt",
+ "//apex_available:anyapex",
],
+ product_available: true,
vendor_available: true,
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99b442b..8cf9a58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,56 @@ 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).
+## [0.2.8] - 2022-10-20
+### Changed
+- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown`
+ when using the `"js"` feature, even on Node.js [#284] [#295]
+
+### Added
+- Added benchmarks to track buffer initialization cost [#272]
+
+### Fixed
+- Use `$crate` in `register_custom_getrandom!` [#270]
+
+### Documentation
+- Add information about enabling `"js"` feature [#280]
+- Fix link to `wasm-bindgen` [#278]
+- Document the varied implementations for underlying randomness sources [#276]
+
+[Web Cryptography API]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API
+[#284]: https://github.com/rust-random/getrandom/pull/284
+[#295]: https://github.com/rust-random/getrandom/pull/295
+[#272]: https://github.com/rust-random/getrandom/pull/272
+[#270]: https://github.com/rust-random/getrandom/pull/270
+[#280]: https://github.com/rust-random/getrandom/pull/280
+[#278]: https://github.com/rust-random/getrandom/pull/278
+[#276]: https://github.com/rust-random/getrandom/pull/276
+
+## [0.2.7] - 2022-06-14
+### Changed
+- Update `wasi` dependency to `0.11` [#253]
+
+### Fixed
+- Use `AtomicPtr` instead of `AtomicUsize` for Strict Provenance compatibility. [#263]
+
+### Documentation
+- Add comments explaining use of fallback mechanisms [#257] [#260]
+
+[#263]: https://github.com/rust-random/getrandom/pull/263
+[#260]: https://github.com/rust-random/getrandom/pull/260
+[#253]: https://github.com/rust-random/getrandom/pull/253
+[#257]: https://github.com/rust-random/getrandom/pull/257
+
+## [0.2.6] - 2022-03-28
+### Added
+- Nintendo 3DS (`armv6k-nintendo-3ds`) support [#248]
+
+### Changed
+- Retry `open` when interrupted [#252]
+
+[#248]: https://github.com/rust-random/getrandom/pull/248
+[#252]: https://github.com/rust-random/getrandom/pull/252
+
## [0.2.5] - 2022-02-22
### Added
- ESP-IDF targets (`*‑espidf`) support [#245]
@@ -281,6 +331,9 @@ Publish initial implementation.
## [0.0.0] - 2019-01-19
Publish an empty template library.
+[0.2.8]: https://github.com/rust-random/getrandom/compare/v0.2.7...v0.2.8
+[0.2.7]: https://github.com/rust-random/getrandom/compare/v0.2.6...v0.2.7
+[0.2.6]: https://github.com/rust-random/getrandom/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/rust-random/getrandom/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/rust-random/getrandom/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/rust-random/getrandom/compare/v0.2.2...v0.2.3
diff --git a/Cargo.toml b/Cargo.toml
index b3d191b..f0252c9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,11 +12,12 @@
[package]
edition = "2018"
name = "getrandom"
-version = "0.2.5"
+version = "0.2.8"
authors = ["The Rand Project Developers"]
exclude = [".*"]
description = "A small cross-platform library for retrieving random data from system source"
documentation = "https://docs.rs/getrandom"
+readme = "README.md"
categories = [
"os",
"no-std",
@@ -75,8 +76,8 @@ default-features = false
version = "0.3.18"
[target."cfg(target_os = \"wasi\")".dependencies.wasi]
-version = "0.10"
+version = "0.11"
[target."cfg(unix)".dependencies.libc]
-version = "0.2.64"
+version = "0.2.120"
default-features = false
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index eb51149..2024c8f 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "getrandom"
-version = "0.2.5" # Also update html_root_url in lib.rs when bumping this
+version = "0.2.8" # Also update html_root_url in lib.rs when bumping this
edition = "2018"
authors = ["The Rand Project Developers"]
license = "MIT OR Apache-2.0"
@@ -18,10 +18,10 @@ compiler_builtins = { version = "0.1", optional = true }
core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" }
[target.'cfg(unix)'.dependencies]
-libc = { version = "0.2.64", default-features = false }
+libc = { version = "0.2.120", default-features = false }
[target.'cfg(target_os = "wasi")'.dependencies]
-wasi = "0.10"
+wasi = "0.11"
[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
diff --git a/METADATA b/METADATA
index f9af3d4..8c76529 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/getrandom
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "getrandom"
description: "A small cross-platform library for retrieving random data from system source"
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/getrandom/getrandom-0.2.5.crate"
+ value: "https://static.crates.io/crates/getrandom/getrandom-0.2.8.crate"
}
- version: "0.2.5"
+ version: "0.2.8"
license_type: NOTICE
last_upgrade_date {
year: 2022
- month: 3
- day: 1
+ month: 12
+ day: 12
}
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 42e04b4..68e252c 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -23,6 +23,15 @@
"path": "external/rust/crates/crossbeam-utils"
},
{
+ "path": "external/rust/crates/flate2"
+ },
+ {
+ "path": "external/rust/crates/hashbrown"
+ },
+ {
+ "path": "external/rust/crates/hashlink"
+ },
+ {
"path": "external/rust/crates/mio"
},
{
@@ -45,54 +54,49 @@
},
{
"path": "external/rust/crates/tokio"
- }
- ],
- "presubmit": [
- {
- "name": "ZipFuseTest"
},
{
- "name": "apkdmverity.test"
+ "path": "external/rust/crates/zerocopy"
},
{
- "name": "authfs_device_test_src_lib"
+ "path": "external/uwb/src"
},
{
- "name": "getrandom_test_src_lib"
+ "path": "packages/modules/Virtualization/apkdmverity"
},
{
- "name": "getrandom_test_tests_normal"
+ "path": "packages/modules/Virtualization/authfs"
},
{
- "name": "getrandom_test_tests_rdrand"
+ "path": "packages/modules/Virtualization/avmd"
},
{
- "name": "keystore2_test"
+ "path": "packages/modules/Virtualization/encryptedstore"
},
{
- "name": "keystore2_test_utils_test"
+ "path": "packages/modules/Virtualization/libs/devicemapper"
},
{
- "name": "legacykeystore_test"
+ "path": "packages/modules/Virtualization/microdroid_manager"
},
{
- "name": "microdroid_manager_test"
+ "path": "packages/modules/Virtualization/virtualizationmanager"
},
{
- "name": "virtualizationservice_device_test"
- }
- ],
- "presubmit-rust": [
- {
- "name": "ZipFuseTest"
+ "path": "packages/modules/Virtualization/vm"
},
{
- "name": "apkdmverity.test"
+ "path": "packages/modules/Virtualization/zipfuse"
},
{
- "name": "authfs_device_test_src_lib"
+ "path": "system/security/keystore2"
},
{
+ "path": "system/security/keystore2/legacykeystore"
+ }
+ ],
+ "presubmit": [
+ {
"name": "getrandom_test_src_lib"
},
{
@@ -100,21 +104,17 @@
},
{
"name": "getrandom_test_tests_rdrand"
- },
- {
- "name": "keystore2_test"
- },
- {
- "name": "keystore2_test_utils_test"
- },
+ }
+ ],
+ "presubmit-rust": [
{
- "name": "legacykeystore_test"
+ "name": "getrandom_test_src_lib"
},
{
- "name": "microdroid_manager_test"
+ "name": "getrandom_test_tests_normal"
},
{
- "name": "virtualizationservice_device_test"
+ "name": "getrandom_test_tests_rdrand"
}
]
}
diff --git a/benches/mod.rs b/benches/mod.rs
index a93e720..11be47e 100644
--- a/benches/mod.rs
+++ b/benches/mod.rs
@@ -1,22 +1,94 @@
#![feature(test)]
extern crate test;
-#[bench]
-fn bench_64(b: &mut test::Bencher) {
- let mut buf = [0u8; 64];
+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()) }
+ }
+}
+
+// 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);
});
- b.bytes = buf.len() as u64;
+ b.bytes = N as u64;
}
-#[bench]
-fn bench_65536(b: &mut test::Bencher) {
- let mut buf = [0u8; 65536];
+// 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);
});
- b.bytes = buf.len() as u64;
+ b.bytes = N as u64;
+}
+
+// 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;
+
+#[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]
+fn bench_page(b: &mut test::Bencher) {
+ bench::<PAGE>(b);
+}
+#[bench]
+fn bench_page_init(b: &mut test::Bencher) {
+ bench_with_init::<PAGE>(b);
+}
+
+#[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);
}
diff --git a/cargo2android.json b/cargo2android.json
index 8945bb0..305a6eb 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,8 +1,4 @@
{
- "apex-available": [
- "//apex_available:platform",
- "com.android.virt"
- ],
"dependencies": true,
"device": true,
"features": "std",
diff --git a/src/3ds.rs b/src/3ds.rs
new file mode 100644
index 0000000..6030512
--- /dev/null
+++ b/src/3ds.rs
@@ -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)
+ })
+}
diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs
index f26f260..d441212 100644
--- a/src/bsd_arandom.rs
+++ b/src/bsd_arandom.rs
@@ -31,6 +31,7 @@ fn kern_arnd(buf: &mut [u8]) -> 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;
diff --git a/src/custom.rs b/src/custom.rs
index 6110b05..8432dfd 100644
--- a/src/custom.rs
+++ b/src/custom.rs
@@ -79,7 +79,7 @@ macro_rules! register_custom_getrandom {
// 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<(), ::getrandom::Error> = $path;
+ 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,
diff --git a/src/dragonfly.rs b/src/dragonfly.rs
index f27e906..8daaa40 100644
--- a/src/dragonfly.rs
+++ b/src/dragonfly.rs
@@ -17,6 +17,7 @@ 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) });
diff --git a/src/error.rs b/src/error.rs
index 6615753..ab39a3c 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -43,16 +43,19 @@ impl Error {
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`.
+ /// The environment does not support the Web Crypto API.
pub const WEB_CRYPTO: Error = internal_error(7);
- /// The browser does not have support for `crypto.getRandomValues`.
+ /// Calling Web Crypto API `crypto.getRandomValues` failed.
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.
+ /// Node.js does not have the `crypto` CommonJS module.
pub const NODE_CRYPTO: Error = internal_error(12);
- /// NodeJS does not have support for `crypto.randomFillSync`.
+ /// Calling Node.js function `crypto.randomFillSync` failed.
pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
+ /// Called from an ES module on Node.js. This is unsupported, see:
+ /// <https://docs.rs/getrandom#nodejs-es-module-support>.
+ pub const NODE_ES_MODULE: Error = internal_error(14);
/// Codes below this point represent OS Errors (i.e. positive i32 values).
/// Codes at or above this point, but below [`Error::CUSTOM_START`] are
@@ -109,10 +112,6 @@ cfg_if! {
let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
core::str::from_utf8(&buf[..idx]).ok()
}
- } else if #[cfg(target_os = "wasi")] {
- fn os_err(errno: i32, _buf: &mut [u8]) -> Option<wasi::Error> {
- wasi::Error::from_raw_error(errno as _)
- }
} else {
fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
None
@@ -170,10 +169,11 @@ fn internal_desc(error: Error) -> Option<&'static str> {
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::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
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"),
+ Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
+ Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
+ Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
_ => None,
}
}
diff --git a/src/js.rs b/src/js.rs
index e910f2b..574c4dc 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -10,15 +10,16 @@ use crate::Error;
extern crate std;
use std::thread_local;
-use js_sys::{global, Uint8Array};
+use js_sys::{global, Function, Uint8Array};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
+// Size of our temporary Uint8Array buffer used with WebCrypto methods
// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues
-const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256;
+const WEB_CRYPTO_BUFFER_SIZE: usize = 256;
enum RngSource {
Node(NodeCrypto),
- Browser(BrowserCrypto, Uint8Array),
+ Web(WebCrypto, Uint8Array),
}
// JsValues are always per-thread, so we initialize RngSource for each thread.
@@ -37,10 +38,10 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
return Err(Error::NODE_RANDOM_FILL_SYNC);
}
}
- RngSource::Browser(crypto, buf) => {
+ RngSource::Web(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) {
+ for chunk in dest.chunks_mut(WEB_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);
@@ -58,25 +59,33 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
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),
+ // Get the Web Crypto interface if we are in a browser, Web Worker, Deno,
+ // or another environment that supports the Web Cryptography API. This
+ // also allows for user-provided polyfills in unsupported environments.
+ let crypto = match global.crypto() {
+ // Standard Web Crypto interface
+ c if c.is_object() => c,
+ // Node.js CommonJS Crypto module
+ _ if is_node(&global) => {
+ // If module.require isn't a valid function, we are in an ES module.
+ match Module::require_fn().and_then(JsCast::dyn_into::<Function>) {
+ Ok(require_fn) => match require_fn.call1(&global, &JsValue::from_str("crypto")) {
+ Ok(n) => return Ok(RngSource::Node(n.unchecked_into())),
+ Err(_) => return Err(Error::NODE_CRYPTO),
+ },
+ Err(_) => return Err(Error::NODE_ES_MODULE),
+ }
+ }
+ // IE 11 Workaround
+ _ => match global.ms_crypto() {
+ 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))
+ let buf = Uint8Array::new_with_length(WEB_CRYPTO_BUFFER_SIZE as u32);
+ Ok(RngSource::Web(crypto, buf))
}
// Taken from https://www.npmjs.com/package/browser-or-node
@@ -93,30 +102,36 @@ fn is_node(global: &Global) -> bool {
#[wasm_bindgen]
extern "C" {
- type Global; // Return type of js_sys::global()
+ // Return type of js_sys::global()
+ type Global;
- // Web Crypto API (https://www.w3.org/TR/WebCryptoAPI/)
- #[wasm_bindgen(method, getter, js_name = "msCrypto")]
- fn ms_crypto(this: &Global) -> BrowserCrypto;
+ // Web Crypto API: Crypto interface (https://www.w3.org/TR/WebCryptoAPI/)
+ type WebCrypto;
+ // Getters for the WebCrypto API
#[wasm_bindgen(method, getter)]
- fn crypto(this: &Global) -> BrowserCrypto;
- type BrowserCrypto;
+ fn crypto(this: &Global) -> WebCrypto;
+ #[wasm_bindgen(method, getter, js_name = msCrypto)]
+ fn ms_crypto(this: &Global) -> WebCrypto;
+ // Crypto.getRandomValues()
#[wasm_bindgen(method, js_name = getRandomValues, catch)]
- fn get_random_values(this: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>;
+ fn get_random_values(this: &WebCrypto, 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;
+ // crypto.randomFillSync()
#[wasm_bindgen(method, js_name = randomFillSync, catch)]
fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
+ // Ideally, we would just use `fn require(s: &str)` here. However, doing
+ // this causes a Webpack warning. So we instead return the function itself
+ // and manually invoke it using call1. This also lets us to check that the
+ // function actually exists, allowing for better error messages. See:
+ // https://github.com/rust-random/getrandom/issues/224
+ // https://github.com/rust-random/getrandom/issues/256
+ type Module;
+ #[wasm_bindgen(getter, static_method_of = Module, js_class = module, js_name = require, catch)]
+ fn require_fn() -> Result<JsValue, JsValue>;
+
// Node JS process Object (https://nodejs.org/api/process.html)
#[wasm_bindgen(method, getter)]
fn process(this: &Global) -> Process;
diff --git a/src/lib.rs b/src/lib.rs
index 175da47..67325a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -30,9 +30,9 @@
//! | ESP-IDF | `*‑espidf` | [`esp_fill_random`]
//! | Emscripten | `*‑emscripten` | `/dev/random` (identical to `/dev/urandom`)
//! | WASI | `wasm32‑wasi` | [`random_get`]
-//! | Web Browser | `wasm32‑*‑unknown` | [`Crypto.getRandomValues`], see [WebAssembly support]
-//! | Node.js | `wasm32‑*‑unknown` | [`crypto.randomBytes`], see [WebAssembly support]
+//! | Web Browser and Node.js | `wasm32‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, 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
@@ -71,11 +71,37 @@
//! 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.
+//! [described above](#supported-targets) using the [`wasm-bindgen`] toolchain.
+//!
+//! To enable the `js` Cargo feature, add the following to the `dependencies`
+//! section in your `Cargo.toml` file:
+//! ```toml
+//! [dependencies]
+//! getrandom = { version = "0.2", features = ["js"] }
+//! ```
+//!
+//! This can be done even if `getrandom` is not a direct dependency. Cargo
+//! allows crates to enable features for indirect dependencies.
+//!
+//! This feature should only be enabled for binary, test, or benchmark crates.
+//! Library crates should generally not enable this feature, leaving such a
+//! decision to *users* of their library. Also, libraries should not introduce
+//! their own `js` features *just* to enable `getrandom`'s `js` feature.
//!
//! This feature has no effect on targets other than `wasm32-unknown-unknown`.
//!
+//! #### Node.js ES module support
+//!
+//! Node.js supports both [CommonJS modules] and [ES modules]. Due to
+//! limitations in wasm-bindgen's [`module`] support, we cannot directly
+//! support ES Modules running on Node.js. However, on Node v15 and later, the
+//! module author can add a simple shim to support the Web Cryptography API:
+//! ```js
+//! import { webcrypto } from 'node:crypto'
+//! globalThis.crypto = webcrypto
+//! ```
+//! This crate will then use the provided `webcrypto` implementation.
+//!
//! ### Custom implementations
//!
//! The [`register_custom_getrandom!`] macro allows a user to mark their own
@@ -88,16 +114,6 @@
//! 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
@@ -114,13 +130,22 @@
//! entropy yet. To avoid returning low-entropy bytes, we first poll
//! `/dev/random` and only switch to `/dev/urandom` once this has succeeded.
//!
+//! On OpenBSD, this kind of entropy accounting isn't available, and on
+//! NetBSD, blocking on it is discouraged. On these platforms, nonblocking
+//! interfaces are used, even when reliable entropy may not be available.
+//! On the platforms where it is used, the reliability of entropy accounting
+//! itself isn't free from controversy. This library provides randomness
+//! sourced according to the platform's best practices, but each platform has
+//! its own limits on the grade of randomness it can promise in environments
+//! with few sources of entropy.
+//!
//! ## 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.
+//! We always choose failure over returning known 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
@@ -140,15 +165,19 @@
//! [`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
+//! [`crypto.randomFillSync`]: https://nodejs.org/api/crypto.html#cryptorandomfillsyncbuffer-offset-size
//! [`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
+//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
+//! [`module`]: https://rustwasm.github.io/wasm-bindgen/reference/attributes/on-js-imports/module.html
+//! [CommonJS modules]: https://nodejs.org/api/modules.html
+//! [ES modules]: https://nodejs.org/api/esm.html
#![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.5"
+ html_root_url = "https://docs.rs/getrandom/0.2.8"
)]
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
@@ -223,6 +252,11 @@ cfg_if! {
} 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"))] {
diff --git a/src/linux_android.rs b/src/linux_android.rs
index 5508fdd..4270b67 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -14,6 +14,7 @@ use crate::{
};
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 {
diff --git a/src/macos.rs b/src/macos.rs
index 585a35a..671a053 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -17,6 +17,7 @@ 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) };
diff --git a/src/openbsd.rs b/src/openbsd.rs
index c8d28b3..4137173 100644
--- a/src/openbsd.rs
+++ b/src/openbsd.rs
@@ -10,6 +10,7 @@
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 {
diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs
index 2d1b767..cf3067d 100644
--- a/src/solaris_illumos.rs
+++ b/src/solaris_illumos.rs
@@ -30,6 +30,7 @@ type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) ->
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) };
diff --git a/src/util_libc.rs b/src/util_libc.rs
index 6823609..d057071 100644
--- a/src/util_libc.rs
+++ b/src/util_libc.rs
@@ -6,8 +6,13 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(dead_code)]
-use crate::{util::LazyUsize, Error};
-use core::{num::NonZeroU32, ptr::NonNull};
+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"))] {
@@ -20,6 +25,12 @@ cfg_if! {
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;
}
}
@@ -70,29 +81,57 @@ pub fn sys_fill_exact(
// 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.
-// F must be a function pointer of type `unsafe extern "C" fn`. Based off of the
-// weak! macro in libstd.
+// 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: LazyUsize,
+ 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: LazyUsize::new(),
+ addr: AtomicPtr::new(Self::UNINIT),
}
}
- // Return a function pointer if present at runtime. Otherwise, return null.
- pub fn ptr(&self) -> Option<NonNull<libc::c_void>> {
- let addr = self.addr.unsync_init(|| unsafe {
- libc::dlsym(libc::RTLD_DEFAULT, self.name.as_ptr() as *const _) as usize
- });
- NonNull::new(addr as *mut _)
+ // 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)
+ }
+ }
}
}
@@ -107,9 +146,15 @@ cfg_if! {
// 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));
- let fd = open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
- if fd < 0 {
- return Err(last_os_error());
+ 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);
+ }
}
- Ok(fd)
}
diff --git a/src/wasi.rs b/src/wasi.rs
index 2d413e0..c512182 100644
--- a/src/wasi.rs
+++ b/src/wasi.rs
@@ -9,15 +9,11 @@
//! Implementation for WASI
use crate::Error;
use core::num::NonZeroU32;
-use wasi::random_get;
+use wasi::wasi_snapshot_preview1::random_get;
pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
- unsafe {
- random_get(dest.as_mut_ptr(), dest.len()).map_err(|e: wasi::Error| {
- // convert wasi's Error into getrandom's NonZeroU32 error
- // SAFETY: `wasi::Error` is `NonZeroU16` internally, so `e.raw_error()`
- // will never return 0
- NonZeroU32::new_unchecked(e.raw_error() as u32).into()
- })
+ 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()),
}
}
diff --git a/src/windows.rs b/src/windows.rs
index 643badd..41dc37a 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -24,6 +24,7 @@ extern "system" {
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(),