aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2024-02-02 10:38:02 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2024-02-02 10:38:02 +0000
commitde0ba12639dc1a10a5e339d20e9845afd28d6157 (patch)
tree1c4feccd1f48b299a4c2ff42904596640a8cb629
parent60d419ebd502ebc5fe0e73cba13d09daaa5e3ed4 (diff)
parent1872b3e1228910bdcbfd12744c5c6039dbcc9765 (diff)
downloadgetrandom-main.tar.gz
Upgrade getrandom to 0.2.12 am: 1872b3e122HEADmastermainemu-34-2-dev
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/getrandom/+/2944786 Change-Id: I315dfe2e9f343a483e0a525f8f29e2a53fc9e8f7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp8
-rw-r--r--CHANGELOG.md96
-rw-r--r--Cargo.toml21
-rw-r--r--Cargo.toml.orig23
-rw-r--r--LICENSE-MIT2
-rw-r--r--METADATA25
-rw-r--r--README.md33
-rw-r--r--SECURITY.md13
-rw-r--r--benches/buffer.rs71
-rw-r--r--benches/mod.rs94
-rw-r--r--src/3ds.rs11
-rw-r--r--src/apple-other.rs24
-rw-r--r--src/bsd_arandom.rs39
-rw-r--r--src/custom.rs46
-rw-r--r--src/dragonfly.rs15
-rw-r--r--src/emscripten.rs13
-rw-r--r--src/error.rs14
-rw-r--r--src/error_impls.rs7
-rw-r--r--src/espidf.rs12
-rw-r--r--src/fuchsia.rs13
-rw-r--r--src/hermit.rs29
-rw-r--r--src/hurd.rs10
-rw-r--r--src/ios.rs27
-rw-r--r--src/js.rs37
-rw-r--r--src/lazy.rs56
-rw-r--r--src/lib.rs137
-rw-r--r--src/linux_android.rs13
-rw-r--r--src/macos.rs42
-rw-r--r--src/openbsd.rs11
-rw-r--r--src/rdrand.rs126
-rw-r--r--src/solaris_illumos.rs33
-rw-r--r--src/solid.rs14
-rw-r--r--src/use_file.rs52
-rw-r--r--src/util.rs85
-rw-r--r--src/util_libc.rs49
-rw-r--r--src/vita.rs13
-rw-r--r--src/vxworks.rs17
-rw-r--r--src/wasi.rs28
-rw-r--r--src/windows.rs32
-rw-r--r--tests/common/mod.rs46
-rw-r--r--tests/custom.rs26
-rw-r--r--tests/rdrand.rs9
43 files changed, 861 insertions, 613 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index fc6c877..bf9c98e 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "5c1bb00b74a2c72ba182171b93d1c4b9d30c10c4"
+ "sha1": "f68a940b4dac78b49f00599aebd56c72284a92d9"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index bbd1507..e5c7ab1 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.8",
+ cargo_pkg_version: "0.2.12",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -62,7 +62,7 @@ rust_test {
host_supported: true,
crate_name: "normal",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.8",
+ cargo_pkg_version: "0.2.12",
srcs: ["tests/normal.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -83,7 +83,7 @@ rust_test {
host_supported: true,
crate_name: "rdrand",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.8",
+ cargo_pkg_version: "0.2.12",
srcs: ["tests/rdrand.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -104,7 +104,7 @@ rust_library {
host_supported: true,
crate_name: "getrandom",
cargo_env_compat: true,
- cargo_pkg_version: "0.2.8",
+ cargo_pkg_version: "0.2.12",
srcs: ["src/lib.rs"],
edition: "2018",
features: ["std"],
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8cf9a58..a283d38 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,94 @@ 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.12] - 2024-01-09
+### Fixed
+- Custom backend for targets without atomics [#385]
+
+### Changed
+- Improve robustness of the Hermit backend and `sys_fill_exact` [#386]
+- Raise minimum supported Apple OS versions to macOS 10.12 and iOS 10 [#388]
+
+### Added
+- Document platform support policy [#387]
+
+[#385]: https://github.com/rust-random/getrandom/pull/385
+[#386]: https://github.com/rust-random/getrandom/pull/386
+[#387]: https://github.com/rust-random/getrandom/pull/387
+[#388]: https://github.com/rust-random/getrandom/pull/388
+
+## [0.2.11] - 2023-11-08
+### Added
+- GNU/Hurd support [#370]
+
+### Changed
+- Renamed `__getrandom_internal` to `__GETRANDOM_INTERNAL` [#369]
+- Updated link to Hermit docs [#374]
+
+[#369]: https://github.com/rust-random/getrandom/pull/369
+[#370]: https://github.com/rust-random/getrandom/pull/370
+[#374]: https://github.com/rust-random/getrandom/pull/374
+
+## [0.2.10] - 2023-06-06
+### Added
+- Support for PS Vita (`armv7-sony-vita-newlibeabihf`) [#359]
+
+### Changed
+- Use getentropy from libc on Emscripten targets [#362]
+
+[#359]: https://github.com/rust-random/getrandom/pull/359
+[#362]: https://github.com/rust-random/getrandom/pull/362
+
+## [0.2.9] - 2023-04-06
+### Added
+- AIX support [#282]
+- `getrandom_uninit` function [#291]
+- `wasm64-unknown-unknown` support [#303]
+- tvOS and watchOS support [#317]
+- QNX/nto support [#325]
+- Support for `getrandom` syscall on NetBSD ≥ 10.0 [#331]
+- `RtlGenRandom` fallback for non-UWP Windows [#337]
+
+### Breaking Changes
+- Update MSRV to 1.36 [#291]
+
+### Fixed
+- Solaris/OpenBSD/Dragonfly build [#301]
+
+### Changed
+- Update MSRV to 1.36 [#291]
+- Use getentropy on Emscripten [#307]
+- Solaris: consistantly use `/dev/random` source [#310]
+- Move 3ds selection above rdrand/js/custom fallback [#312]
+- Remove buffer zeroing from Node.js implementation [#315]
+- Use `open` instead of `open64` [#326]
+- Remove #cfg from bsd_arandom.rs [#332]
+- Hermit: use `sys_read_entropy` syscall [#333]
+- Eliminate potential panic in sys_fill_exact [#334]
+- rdrand: Remove checking for 0 and !0 and instead check CPU family and do a self-test [#335]
+- Move `__getrandom_custom` definition into a const block [#344]
+- Switch the custom backend to Rust ABI [#347]
+
+[#282]: https://github.com/rust-random/getrandom/pull/282
+[#291]: https://github.com/rust-random/getrandom/pull/291
+[#301]: https://github.com/rust-random/getrandom/pull/301
+[#303]: https://github.com/rust-random/getrandom/pull/303
+[#307]: https://github.com/rust-random/getrandom/pull/307
+[#310]: https://github.com/rust-random/getrandom/pull/310
+[#312]: https://github.com/rust-random/getrandom/pull/312
+[#315]: https://github.com/rust-random/getrandom/pull/315
+[#317]: https://github.com/rust-random/getrandom/pull/317
+[#325]: https://github.com/rust-random/getrandom/pull/325
+[#326]: https://github.com/rust-random/getrandom/pull/326
+[#331]: https://github.com/rust-random/getrandom/pull/331
+[#332]: https://github.com/rust-random/getrandom/pull/332
+[#333]: https://github.com/rust-random/getrandom/pull/333
+[#334]: https://github.com/rust-random/getrandom/pull/334
+[#335]: https://github.com/rust-random/getrandom/pull/335
+[#337]: https://github.com/rust-random/getrandom/pull/337
+[#344]: https://github.com/rust-random/getrandom/pull/344
+[#347]: https://github.com/rust-random/getrandom/pull/347
+
## [0.2.8] - 2022-10-20
### Changed
- The [Web Cryptography API] will now be preferred on `wasm32-unknown-unknown`
@@ -95,7 +183,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.2.2] - 2021-01-19
### Changed
- Forward `rustc-dep-of-std` to dependencies. [#198]
-- Highlight feature-dependend functionality in documentation using the `doc_cfg` feature. [#200]
+- Highlight feature-dependent functionality in documentation using the `doc_cfg` feature. [#200]
[#198]: https://github.com/rust-random/getrandom/pull/198
[#200]: https://github.com/rust-random/getrandom/pull/200
@@ -225,7 +313,7 @@ disabled `dummy` feature. [#90]
## [0.1.9] - 2019-08-14 [YANKED]
### Changed
- Remove `std` dependency for opening and reading files. [#58]
-- Use `wasi` isntead of `libc` on WASI target. [#64]
+- Use `wasi` instead of `libc` on WASI target. [#64]
- By default emit a compile-time error when built for an unsupported target.
This behaviour can be disabled by using the `dummy` feature. [#71]
@@ -331,6 +419,10 @@ Publish initial implementation.
## [0.0.0] - 2019-01-19
Publish an empty template library.
+[0.2.12]: https://github.com/rust-random/getrandom/compare/v0.2.11...v0.2.12
+[0.2.11]: https://github.com/rust-random/getrandom/compare/v0.2.10...v0.2.11
+[0.2.10]: https://github.com/rust-random/getrandom/compare/v0.2.9...v0.2.10
+[0.2.9]: https://github.com/rust-random/getrandom/compare/v0.2.8...v0.2.9
[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
diff --git a/Cargo.toml b/Cargo.toml
index f0252c9..5d94d81 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "getrandom"
-version = "0.2.8"
+version = "0.2.12"
authors = ["The Rand Project Developers"]
exclude = [".*"]
description = "A small cross-platform library for retrieving random data from system source"
@@ -25,6 +25,16 @@ categories = [
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-random/getrandom"
+[package.metadata.cross.target.x86_64-unknown-netbsd]
+pre-build = [
+ "mkdir -p /tmp/netbsd",
+ "curl https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.2/amd64/binary/sets/base.tar.xz -O",
+ "tar -C /tmp/netbsd -xJf base.tar.xz",
+ "cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib",
+ "rm base.tar.xz",
+ "rm -rf /tmp/netbsd",
+]
+
[package.metadata.docs.rs]
features = [
"std",
@@ -63,21 +73,22 @@ rustc-dep-of-std = [
std = []
test-in-browser = []
-[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dependencies.js-sys]
+[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))".dependencies.js-sys]
version = "0.3"
optional = true
-[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dependencies.wasm-bindgen]
+[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))".dependencies.wasm-bindgen]
version = "0.2.62"
optional = true
default-features = false
-[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dev-dependencies.wasm-bindgen-test]
+[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))".dev-dependencies.wasm-bindgen-test]
version = "0.3.18"
[target."cfg(target_os = \"wasi\")".dependencies.wasi]
version = "0.11"
+default-features = false
[target."cfg(unix)".dependencies.libc]
-version = "0.2.120"
+version = "0.2.149"
default-features = false
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 2024c8f..ea57331 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "getrandom"
-version = "0.2.8" # Also update html_root_url in lib.rs when bumping this
+version = "0.2.12" # 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,15 +18,15 @@ 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.120", default-features = false }
+libc = { version = "0.2.149", default-features = false }
[target.'cfg(target_os = "wasi")'.dependencies]
-wasi = "0.11"
+wasi = { version = "0.11", default-features = false }
-[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
+[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dependencies]
wasm-bindgen = { version = "0.2.62", default-features = false, optional = true }
js-sys = { version = "0.3", optional = true }
-[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies]
+[target.'cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"), target_os = "unknown"))'.dev-dependencies]
wasm-bindgen-test = "0.3.18"
[features]
@@ -34,7 +34,7 @@ wasm-bindgen-test = "0.3.18"
std = []
# Feature to enable fallback RDRAND-based implementation on x86/x86_64
rdrand = []
-# Feature to enable JavaScript bindings on wasm32-unknown-unknown
+# Feature to enable JavaScript bindings on wasm*-unknown-unknown
js = ["wasm-bindgen", "js-sys"]
# Feature to enable custom RNG implementations
custom = []
@@ -51,3 +51,14 @@ test-in-browser = []
[package.metadata.docs.rs]
features = ["std", "custom"]
rustdoc-args = ["--cfg", "docsrs"]
+
+# workaround for https://github.com/cross-rs/cross/issues/1345
+[package.metadata.cross.target.x86_64-unknown-netbsd]
+pre-build = [
+ "mkdir -p /tmp/netbsd",
+ "curl https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.2/amd64/binary/sets/base.tar.xz -O",
+ "tar -C /tmp/netbsd -xJf base.tar.xz",
+ "cp /tmp/netbsd/usr/lib/libexecinfo.so /usr/local/x86_64-unknown-netbsd/lib",
+ "rm base.tar.xz",
+ "rm -rf /tmp/netbsd",
+]
diff --git a/LICENSE-MIT b/LICENSE-MIT
index d93b5ba..8ca28a1 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright 2018 Developers of the Rand project
+Copyright (c) 2018-2024 The rust-random Project Developers
Copyright (c) 2014 The Rust Project Developers
Permission is hereby granted, free of charge, to any
diff --git a/METADATA b/METADATA
index 8c76529..1ecb3fe 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# 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
+# Usage: tools/external_updater/updater.sh update external/rust/crates/getrandom
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "getrandom"
description: "A small cross-platform library for retrieving random data from system source"
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/getrandom"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/getrandom/getrandom-0.2.8.crate"
- }
- version: "0.2.8"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 12
- day: 12
+ year: 2024
+ month: 2
+ day: 1
+ }
+ homepage: "https://crates.io/crates/getrandom"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/getrandom/getrandom-0.2.12.crate"
+ version: "0.2.12"
}
}
diff --git a/README.md b/README.md
index df2307b..b4b5a2b 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[![Build Status]][GitHub Actions] [![Crate]][crates.io] [![Documentation]][docs.rs] [![Dependency Status]][deps.rs] [![Downloads]][crates.io] [![License]][LICENSE-MIT]
[GitHub Actions]: https://github.com/rust-random/getrandom/actions?query=workflow:Tests+branch:master
-[Build Status]: https://github.com/rust-random/getrandom/workflows/Tests/badge.svg?branch=master
+[Build Status]: https://github.com/rust-random/getrandom/actions/workflows/tests.yml/badge.svg?branch=master
[crates.io]: https://crates.io/crates/getrandom
[Crate]: https://img.shields.io/crates/v/getrandom
[docs.rs]: https://docs.rs/getrandom
@@ -15,10 +15,10 @@
[License]: https://img.shields.io/crates/l/getrandom
-A Rust library for retrieving random data from (operating) system source. It is
-assumed that system always provides high-quality cryptographically secure random
+A Rust library for retrieving random data from (operating) system sources. It is
+assumed that the system always provides high-quality cryptographically secure random
data, ideally backed by hardware entropy sources. This crate derives its name
-from Linux's `getrandom` function, but is cross platform, roughly supporting
+from Linux's `getrandom` function, but is cross-platform, roughly supporting
the same set of platforms as Rust's `std` lib.
This is a low-level API. Most users should prefer using high-level random-number
@@ -52,13 +52,30 @@ crate features, WASM support and Custom RNGs see the
## Minimum Supported Rust Version
-This crate requires Rust 1.34.0 or later.
+This crate requires Rust 1.36.0 or later.
-# License
+## Platform Support
+
+This crate generally supports the same operating system and platform versions that the Rust standard library does.
+Additional targets may be supported using pluggable custom implementations.
+
+This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc)
+in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions.
+
+## License
The `getrandom` library is distributed under either of
- * [Apache License, Version 2.0](LICENSE-APACHE)
- * [MIT license](LICENSE-MIT)
+ * [Apache License, Version 2.0][LICENSE-APACHE]
+ * [MIT license][LICENSE-MIT]
at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
+
+[LICENSE-APACHE]: https://github.com/rust-random/getrandom/blob/master/LICENSE-APACHE
+[LICENSE-MIT]: https://github.com/rust-random/getrandom/blob/master/LICENSE-MIT
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..19bfb9a
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+
+## Supported Versions
+
+Security updates are applied only to the latest release.
+
+## Reporting a Vulnerability
+
+If you have discovered a security vulnerability in this project, please report it privately. **Do not disclose it as a public issue.** This gives us time to work with you to fix the issue before public exposure, reducing the chance that the exploit will be used before a patch is released.
+
+Please disclose it at [security advisory](https://github.com/rust-random/getrandom/security/advisories/new).
+
+This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
diff --git a/benches/buffer.rs b/benches/buffer.rs
new file mode 100644
index 0000000..b32be43
--- /dev/null
+++ b/benches/buffer.rs
@@ -0,0 +1,71 @@
+#![feature(test, maybe_uninit_uninit_array_transpose)]
+extern crate test;
+
+use std::mem::MaybeUninit;
+
+// Call getrandom on a zero-initialized stack buffer
+#[inline(always)]
+fn bench_getrandom<const N: usize>() {
+ let mut buf = [0u8; N];
+ getrandom::getrandom(&mut buf).unwrap();
+ test::black_box(&buf as &[u8]);
+}
+
+// Call getrandom_uninit on an uninitialized stack buffer
+#[inline(always)]
+fn bench_getrandom_uninit<const N: usize>() {
+ let mut uninit = [MaybeUninit::uninit(); N];
+ let buf: &[u8] = getrandom::getrandom_uninit(&mut uninit).unwrap();
+ test::black_box(buf);
+}
+
+// We benchmark using #[inline(never)] "inner" functions for two reasons:
+// - Avoiding inlining reduces a source of variance when running benchmarks.
+// - It is _much_ easier to get the assembly or IR for the inner loop.
+//
+// For example, using cargo-show-asm (https://github.com/pacak/cargo-show-asm),
+// we can get the assembly for a particular benchmark's inner loop by running:
+// cargo asm --bench buffer --release buffer::p384::bench_getrandom::inner
+macro_rules! bench {
+ ( $name:ident, $size:expr ) => {
+ pub mod $name {
+ #[bench]
+ pub fn bench_getrandom(b: &mut test::Bencher) {
+ #[inline(never)]
+ fn inner() {
+ super::bench_getrandom::<{ $size }>()
+ }
+
+ b.bytes = $size as u64;
+ b.iter(inner);
+ }
+ #[bench]
+ pub fn bench_getrandom_uninit(b: &mut test::Bencher) {
+ #[inline(never)]
+ fn inner() {
+ super::bench_getrandom_uninit::<{ $size }>()
+ }
+
+ b.bytes = $size as u64;
+ b.iter(inner);
+ }
+ }
+ };
+}
+
+// 16 bytes (128 bits) is the size of an 128-bit AES key/nonce.
+bench!(aes128, 128 / 8);
+
+// 32 bytes (256 bits) is the seed sized used for rand::thread_rng
+// and the `random` value in a ClientHello/ServerHello for TLS.
+// This is also the size of a 256-bit AES/HMAC/P-256/Curve25519 key
+// and/or nonce.
+bench!(p256, 256 / 8);
+
+// A P-384/HMAC-384 key and/or nonce.
+bench!(p384, 384 / 8);
+
+// Initializing larger buffers is not the primary use case of this library, as
+// this should normally be done by a userspace CSPRNG. However, we have a test
+// here to see the effects of a lower (amortized) syscall overhead.
+bench!(page, 4096);
diff --git a/benches/mod.rs b/benches/mod.rs
deleted file mode 100644
index 11be47e..0000000
--- a/benches/mod.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-#![feature(test)]
-extern crate test;
-
-use std::{
- alloc::{alloc_zeroed, dealloc, Layout},
- ptr::NonNull,
-};
-
-// AlignedBuffer is like a Box<[u8; N]> except that it is always N-byte aligned
-struct AlignedBuffer<const N: usize>(NonNull<[u8; N]>);
-
-impl<const N: usize> AlignedBuffer<N> {
- fn layout() -> Layout {
- Layout::from_size_align(N, N).unwrap()
- }
-
- fn new() -> Self {
- let p = unsafe { alloc_zeroed(Self::layout()) } as *mut [u8; N];
- Self(NonNull::new(p).unwrap())
- }
-
- fn buf(&mut self) -> &mut [u8; N] {
- unsafe { self.0.as_mut() }
- }
-}
-
-impl<const N: usize> Drop for AlignedBuffer<N> {
- fn drop(&mut self) {
- unsafe { dealloc(self.0.as_ptr() as *mut u8, Self::layout()) }
- }
-}
-
-// 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 = N as u64;
-}
-
-// 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 = 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/src/3ds.rs b/src/3ds.rs
index 6030512..a5aae77 100644
--- a/src/3ds.rs
+++ b/src/3ds.rs
@@ -1,16 +1,9 @@
-// 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;
+use core::mem::MaybeUninit;
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
sys_fill_exact(dest, |buf| unsafe {
libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
})
diff --git a/src/apple-other.rs b/src/apple-other.rs
new file mode 100644
index 0000000..167d8cf
--- /dev/null
+++ b/src/apple-other.rs
@@ -0,0 +1,24 @@
+//! Implementation for iOS, tvOS, and watchOS where `getentropy` is unavailable.
+use crate::Error;
+use core::{ffi::c_void, mem::MaybeUninit};
+
+// libsystem contains the libc of Darwin, and every binary ends up linked against it either way. This
+// makes it a more lightweight choice compared to `Security.framework`.
+extern "C" {
+ // This RNG uses a thread-local CSPRNG to provide data, which is seeded by the operating system's root CSPRNG.
+ // Its the best option after `getentropy` on modern Darwin-based platforms that also avoids the
+ // high startup costs and linking of Security.framework.
+ //
+ // While its just an implementation detail, `Security.framework` just calls into this anyway.
+ fn CCRandomGenerateBytes(bytes: *mut c_void, size: usize) -> i32;
+}
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ let ret = unsafe { CCRandomGenerateBytes(dest.as_mut_ptr() as *mut c_void, dest.len()) };
+ // kCCSuccess (from CommonCryptoError.h) is always zero.
+ if ret != 0 {
+ Err(Error::IOS_SEC_RANDOM)
+ } else {
+ Ok(())
+ }
+}
diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs
index d441212..6e133d8 100644
--- a/src/bsd_arandom.rs
+++ b/src/bsd_arandom.rs
@@ -1,16 +1,11 @@
-// 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;
+use crate::{
+ util_libc::{sys_fill_exact, Weak},
+ Error,
+};
+use core::{mem::MaybeUninit, ptr};
-fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t {
+fn kern_arnd(buf: &mut [MaybeUninit<u8>]) -> libc::ssize_t {
static MIB: [libc::c_int; 2] = [libc::CTL_KERN, libc::KERN_ARND];
let mut len = buf.len();
let ret = unsafe {
@@ -30,20 +25,18 @@ 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;
- 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;
+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) });
- }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ // getrandom(2) was introduced in FreeBSD 12.0 and NetBSD 10.0
+ static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
+ 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() as *mut u8, 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) {
diff --git a/src/custom.rs b/src/custom.rs
index 8432dfd..8dc9cb7 100644
--- a/src/custom.rs
+++ b/src/custom.rs
@@ -1,14 +1,6 @@
-// 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;
+use crate::{util::uninit_slice_fill_zero, Error};
+use core::{mem::MaybeUninit, num::NonZeroU32};
/// Register a function to be invoked by `getrandom` on unsupported targets.
///
@@ -76,24 +68,36 @@ use core::num::NonZeroU32;
#[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(),
+ // TODO(MSRV 1.37): change to unnamed block
+ const __GETRANDOM_INTERNAL: () = {
+ // We use Rust ABI to be safe against potential panics in the passed function.
+ #[no_mangle]
+ unsafe fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 {
+ // Make sure the passed function has the type of getrandom::getrandom
+ type F = fn(&mut [u8]) -> ::core::result::Result<(), $crate::Error>;
+ let _: F = $crate::getrandom;
+ let f: F = $path;
+ let slice = ::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" {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ extern "Rust" {
fn __getrandom_custom(dest: *mut u8, len: usize) -> u32;
}
+ // Previously we always passed a valid, initialized slice to
+ // `__getrandom_custom`. Ensure `dest` has been initialized for backward
+ // compatibility with implementations that rely on that (e.g. Rust
+ // implementations that construct a `&mut [u8]` slice from `dest` and
+ // `len`).
+ let dest = uninit_slice_fill_zero(dest);
let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) };
match NonZeroU32::new(ret) {
None => Ok(()),
diff --git a/src/dragonfly.rs b/src/dragonfly.rs
index 8daaa40..ac4794c 100644
--- a/src/dragonfly.rs
+++ b/src/dragonfly.rs
@@ -1,26 +1,21 @@
-// 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,
};
+use core::mem::MaybeUninit;
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
type GetRandomFn = unsafe extern "C" fn(*mut u8, libc::size_t, libc::c_uint) -> libc::ssize_t;
// 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) });
+ return sys_fill_exact(dest, |buf| unsafe {
+ func(buf.as_mut_ptr() as *mut u8, buf.len(), 0)
+ });
} else {
use_file::getrandom_inner(dest)
}
diff --git a/src/emscripten.rs b/src/emscripten.rs
new file mode 100644
index 0000000..30221c6
--- /dev/null
+++ b/src/emscripten.rs
@@ -0,0 +1,13 @@
+//! Implementation for Emscripten
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ // Emscripten 2.0.5 added getentropy, so we can use it unconditionally.
+ // Unlike other getentropy implementations, there is no max buffer length.
+ let ret = unsafe { libc::getentropy(dest.as_mut_ptr() as *mut libc::c_void, dest.len()) };
+ if ret < 0 {
+ return Err(last_os_error());
+ }
+ Ok(())
+}
diff --git a/src/error.rs b/src/error.rs
index ab39a3c..13c81c7 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,10 +1,3 @@
-// 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
@@ -35,7 +28,11 @@ impl Error {
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.
+ /// Encountered an unexpected situation which should not happen in practice.
+ pub const UNEXPECTED: Error = internal_error(2);
+ /// Call to [`CCRandomGenerateBytes`](https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html) failed
+ /// on iOS, tvOS, or waatchOS.
+ // TODO: Update this constant name in the next breaking release.
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);
@@ -164,6 +161,7 @@ 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::UNEXPECTED => Some("unexpected situation"),
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"),
diff --git a/src/error_impls.rs b/src/error_impls.rs
index 61f46d2..a7bffb8 100644
--- a/src/error_impls.rs
+++ b/src/error_impls.rs
@@ -1,10 +1,3 @@
-// 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;
diff --git a/src/espidf.rs b/src/espidf.rs
index dce8a2a..7da5ca8 100644
--- a/src/espidf.rs
+++ b/src/espidf.rs
@@ -1,20 +1,12 @@
-// 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;
+use core::{ffi::c_void, mem::MaybeUninit};
extern "C" {
fn esp_fill_random(buf: *mut c_void, len: usize) -> u32;
}
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Not that NOT enabling WiFi, BT, or the voltage noise entropy source (via `bootloader_random_enable`)
// will cause ESP-IDF to return pseudo-random numbers based on the voltage noise entropy, after the initial boot process:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html
diff --git a/src/fuchsia.rs b/src/fuchsia.rs
index 572ff53..1197068 100644
--- a/src/fuchsia.rs
+++ b/src/fuchsia.rs
@@ -1,20 +1,13 @@
-// 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;
+use core::mem::MaybeUninit;
#[link(name = "zircon")]
extern "C" {
fn zx_cprng_draw(buffer: *mut u8, length: usize);
}
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
- unsafe { zx_cprng_draw(dest.as_mut_ptr(), dest.len()) }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ unsafe { zx_cprng_draw(dest.as_mut_ptr() as *mut u8, dest.len()) }
Ok(())
}
diff --git a/src/hermit.rs b/src/hermit.rs
new file mode 100644
index 0000000..c4f6194
--- /dev/null
+++ b/src/hermit.rs
@@ -0,0 +1,29 @@
+//! Implementation for Hermit
+use crate::Error;
+use core::{mem::MaybeUninit, num::NonZeroU32};
+
+/// Minimum return value which we should get from syscalls in practice,
+/// because Hermit uses positive `i32`s for error codes:
+/// https://github.com/hermitcore/libhermit-rs/blob/main/src/errno.rs
+const MIN_RET_CODE: isize = -(i32::MAX as isize);
+
+extern "C" {
+ fn sys_read_entropy(buffer: *mut u8, length: usize, flags: u32) -> isize;
+}
+
+pub fn getrandom_inner(mut dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ while !dest.is_empty() {
+ let res = unsafe { sys_read_entropy(dest.as_mut_ptr() as *mut u8, dest.len(), 0) };
+ // Positive `isize`s can be safely casted to `usize`
+ if res > 0 && (res as usize) <= dest.len() {
+ dest = &mut dest[res as usize..];
+ } else {
+ let err = match res {
+ MIN_RET_CODE..=-1 => NonZeroU32::new(-res as u32).unwrap().into(),
+ _ => Error::UNEXPECTED,
+ };
+ return Err(err);
+ }
+ }
+ Ok(())
+}
diff --git a/src/hurd.rs b/src/hurd.rs
new file mode 100644
index 0000000..472a7d8
--- /dev/null
+++ b/src/hurd.rs
@@ -0,0 +1,10 @@
+//! Implementation for GNU/Hurd
+use crate::util_libc::sys_fill_exact;
+use crate::Error;
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ sys_fill_exact(dest, |buf| unsafe {
+ libc::getrandom(buf.as_mut_ptr() as *mut libc::c_void, buf.len(), 0)
+ })
+}
diff --git a/src/ios.rs b/src/ios.rs
deleted file mode 100644
index 226de16..0000000
--- a/src/ios.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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(())
- }
-}
diff --git a/src/js.rs b/src/js.rs
index 574c4dc..e5428f5 100644
--- a/src/js.rs
+++ b/src/js.rs
@@ -1,14 +1,8 @@
-// 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 WASM based on Web and Node.js
use crate::Error;
extern crate std;
-use std::thread_local;
+use std::{mem::MaybeUninit, thread_local};
use js_sys::{global, Function, Uint8Array};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
@@ -16,6 +10,8 @@ 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 WEB_CRYPTO_BUFFER_SIZE: usize = 256;
+// Node.js's crypto.randomFillSync requires the size to be less than 2**31.
+const NODE_MAX_BUFFER_SIZE: usize = (1 << 31) - 1;
enum RngSource {
Node(NodeCrypto),
@@ -28,14 +24,27 @@ thread_local!(
static RNG_SOURCE: Result<RngSource, Error> = getrandom_init();
);
-pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub(crate) fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
RNG_SOURCE.with(|result| {
let source = result.as_ref().map_err(|&e| e)?;
match source {
RngSource::Node(n) => {
- if n.random_fill_sync(dest).is_err() {
- return Err(Error::NODE_RANDOM_FILL_SYNC);
+ for chunk in dest.chunks_mut(NODE_MAX_BUFFER_SIZE) {
+ // SAFETY: chunk is never used directly, the memory is only
+ // modified via the Uint8Array view, which is passed
+ // directly to JavaScript. Also, crypto.randomFillSync does
+ // not resize the buffer. We know the length is less than
+ // u32::MAX because of the chunking above.
+ // Note that this uses the fact that JavaScript doesn't
+ // have a notion of "uninitialized memory", this is purely
+ // a Rust/C/C++ concept.
+ let res = n.random_fill_sync(unsafe {
+ Uint8Array::view_mut_raw(chunk.as_mut_ptr() as *mut u8, chunk.len())
+ });
+ if res.is_err() {
+ return Err(Error::NODE_RANDOM_FILL_SYNC);
+ }
}
}
RngSource::Web(crypto, buf) => {
@@ -49,7 +58,9 @@ pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
if crypto.get_random_values(&sub_buf).is_err() {
return Err(Error::WEB_GET_RANDOM_VALUES);
}
- sub_buf.copy_to(chunk);
+
+ // SAFETY: `sub_buf`'s length is the same length as `chunk`
+ unsafe { sub_buf.raw_copy_to_ptr(chunk.as_mut_ptr() as *mut u8) };
}
}
};
@@ -120,7 +131,7 @@ extern "C" {
type NodeCrypto;
// crypto.randomFillSync()
#[wasm_bindgen(method, js_name = randomFillSync, catch)]
- fn random_fill_sync(this: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>;
+ fn random_fill_sync(this: &NodeCrypto, buf: Uint8Array) -> 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
diff --git a/src/lazy.rs b/src/lazy.rs
new file mode 100644
index 0000000..b9c2f88
--- /dev/null
+++ b/src/lazy.rs
@@ -0,0 +1,56 @@
+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(crate) 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(crate) 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
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 67325a3..e80ec6f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,11 +1,3 @@
-// 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
@@ -14,25 +6,29 @@
//! | ----------------- | ------------------ | --------------
//! | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random`
//! | Windows | `*‑windows‑*` | [`BCryptGenRandom`]
-//! | macOS | `*‑apple‑darwin` | [`getentropy`][3] if available, otherwise [`/dev/random`][4] (identical to `/dev/urandom`)
-//! | iOS | `*‑apple‑ios` | [`SecRandomCopyBytes`]
+//! | macOS | `*‑apple‑darwin` | [`getentropy`][3]
+//! | iOS, tvOS, watchOS | `*‑apple‑ios`, `*-apple-tvos`, `*-apple-watchos` | [`CCRandomGenerateBytes`]
//! | 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]
+//! | NetBSD | `*‑netbsd` | [`getrandom`][16] if available, otherwise [`kern.arandom`][8]
+//! | Dragonfly BSD | `*‑dragonfly` | [`getrandom`][9] if available, otherwise [`/dev/urandom`][10] (identical to `/dev/random`)
//! | 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`]
+//! | Haiku | `*‑haiku` | `/dev/urandom` (identical to `/dev/random`)
+//! | Hermit | `*-hermit` | [`sys_read_entropy`]
+//! | Hurd | `*-hurd-*` | [`getrandom`][17]
//! | SGX | `x86_64‑*‑sgx` | [`RDRAND`]
//! | VxWorks | `*‑wrs‑vxworks‑*` | `randABytes` after checking entropy pool initialization with `randSecure`
//! | ESP-IDF | `*‑espidf` | [`esp_fill_random`]
-//! | Emscripten | `*‑emscripten` | `/dev/random` (identical to `/dev/urandom`)
+//! | Emscripten | `*‑emscripten` | [`getentropy`][13]
//! | WASI | `wasm32‑wasi` | [`random_get`]
-//! | Web Browser and Node.js | `wasm32‑*‑unknown` | [`Crypto.getRandomValues`] if available, then [`crypto.randomFillSync`] if on Node.js, see [WebAssembly support]
+//! | Web Browser and Node.js | `wasm*‑*‑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]
+//! | PS Vita | `armv7-sony-vita-newlibeabihf` | [`getentropy`][13]
+//! | QNX Neutrino | `*‑nto-qnx*` | [`/dev/urandom`][14] (identical to `/dev/random`)
+//! | AIX | `*-ibm-aix` | [`/dev/urandom`][15]
//!
//! There is no blanket implementation on `unix` targets that reads from
//! `/dev/urandom`. This ensures all supported targets are using the recommended
@@ -102,6 +98,13 @@
//! ```
//! This crate will then use the provided `webcrypto` implementation.
//!
+//! ### Platform Support
+//! This crate generally supports the same operating system and platform versions that the Rust standard library does.
+//! Additional targets may be supported using pluggable custom implementations.
+//!
+//! This means that as Rust drops support for old versions of operating systems (such as old Linux kernel versions, Android API levels, etc)
+//! in stable releases, `getrandom` may create new patch releases (`0.N.x`) that remove support for outdated platform versions.
+//!
//! ### Custom implementations
//!
//! The [`register_custom_getrandom!`] macro allows a user to mark their own
@@ -150,7 +153,7 @@
//! [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/
+//! [4]: https://www.unix.com/man-page/mojave/4/urandom/
//! [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
@@ -159,11 +162,16 @@
//! [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
+//! [13]: https://github.com/emscripten-core/emscripten/pull/12240
+//! [14]: https://www.qnx.com/developers/docs/7.1/index.html#com.qnx.doc.neutrino.utilities/topic/r/random.html
+//! [15]: https://www.ibm.com/docs/en/aix/7.3?topic=files-random-urandom-devices
+//! [16]: https://man.netbsd.org/getrandom.2
+//! [17]: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-getrandom
//!
//! [`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
+//! [`CCRandomGenerateBytes`]: https://opensource.apple.com/source/CommonCrypto/CommonCrypto-60074/include/CommonRandom.h.auto.html
//! [`cprng_draw`]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw
//! [`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
@@ -173,11 +181,12 @@
//! [`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
+//! [`sys_read_entropy`]: https://github.com/hermit-os/kernel/blob/315f58ff5efc81d9bf0618af85a59963ff55f8b1/src/syscalls/entropy.rs#L47-L55
#![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.8"
+ html_root_url = "https://docs.rs/getrandom/0.2.12"
)]
#![no_std]
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
@@ -186,6 +195,9 @@
#[macro_use]
extern crate cfg_if;
+use crate::util::{slice_as_uninit_mut, slice_assume_init_mut};
+use core::mem::MaybeUninit;
+
mod error;
mod util;
// To prevent a breaking change when targets are added, we always export the
@@ -199,15 +211,19 @@ pub use crate::error::Error;
// System-specific implementations.
//
-// These should all provide getrandom_inner with the same signature as getrandom.
+// These should all provide getrandom_inner with the signature
+// `fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error>`.
+// The function MUST fully initialize `dest` when `Ok(())` is returned.
+// The function MUST NOT ever write uninitialized bytes into `dest`,
+// regardless of what value it returns.
cfg_if! {
- if #[cfg(any(target_os = "emscripten", target_os = "haiku",
- target_os = "redox"))] {
+ if #[cfg(any(target_os = "haiku", target_os = "redox", target_os = "nto", target_os = "aix"))] {
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;
+ mod lazy;
#[path = "linux_android.rs"] mod imp;
} else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
mod util_libc;
@@ -222,19 +238,18 @@ cfg_if! {
#[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(any(target_os = "ios", target_os = "watchos", target_os = "tvos"))] {
+ #[path = "apple-other.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")] {
+ } else if #[cfg(all(target_arch = "wasm32", 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 = "hermit")] {
+ #[path = "hermit.rs"] mod imp;
} else if #[cfg(target_os = "vxworks")] {
mod util_libc;
#[path = "vxworks.rs"] mod imp;
@@ -244,23 +259,36 @@ cfg_if! {
#[path = "espidf.rs"] mod imp;
} else if #[cfg(windows)] {
#[path = "windows.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(target_os = "vita")] {
+ mod util_libc;
+ #[path = "vita.rs"] mod imp;
+ } else if #[cfg(target_os = "emscripten")] {
+ mod util_libc;
+ #[path = "emscripten.rs"] mod imp;
} else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] {
+ mod lazy;
#[path = "rdrand.rs"] mod imp;
} else if #[cfg(all(feature = "rdrand",
any(target_arch = "x86_64", target_arch = "x86")))] {
+ mod lazy;
#[path = "rdrand.rs"] mod imp;
} else if #[cfg(all(feature = "js",
- target_arch = "wasm32", target_os = "unknown"))] {
+ any(target_arch = "wasm32", target_arch = "wasm64"),
+ 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).
+ } else if #[cfg(target_os = "hurd")] {
mod util_libc;
- #[path = "3ds.rs"] mod imp;
+ #[path = "hurd.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 \
+ } else if #[cfg(all(any(target_arch = "wasm32", target_arch = "wasm64"),
+ target_os = "unknown"))] {
+ compile_error!("the wasm*-unknown-unknown targets are not supported by \
default, you may need to enable the \"js\" feature. \
For more information see: \
https://docs.rs/getrandom/#webassembly-support");
@@ -283,9 +311,42 @@ cfg_if! {
/// In general, `getrandom` will be fast enough for interactive usage, though
/// significantly slower than a user-space CSPRNG; for the latter consider
/// [`rand::thread_rng`](https://docs.rs/rand/*/rand/fn.thread_rng.html).
+#[inline]
pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> {
- if dest.is_empty() {
- return Ok(());
+ // SAFETY: The `&mut MaybeUninit<_>` reference doesn't escape, and
+ // `getrandom_uninit` guarantees it will never de-initialize any part of
+ // `dest`.
+ getrandom_uninit(unsafe { slice_as_uninit_mut(dest) })?;
+ Ok(())
+}
+
+/// Version of the `getrandom` function which fills `dest` with random bytes
+/// returns a mutable reference to those bytes.
+///
+/// On successful completion this function is guaranteed to return a slice
+/// which points to the same memory as `dest` and has the same length.
+/// In other words, it's safe to assume that `dest` is initialized after
+/// this function has returned `Ok`.
+///
+/// No part of `dest` will ever be de-initialized at any point, regardless
+/// of what is returned.
+///
+/// # Examples
+///
+/// ```ignore
+/// # // We ignore this test since `uninit_array` is unstable.
+/// #![feature(maybe_uninit_uninit_array)]
+/// # fn main() -> Result<(), getrandom::Error> {
+/// let mut buf = core::mem::MaybeUninit::uninit_array::<1024>();
+/// let buf: &mut [u8] = getrandom::getrandom_uninit(&mut buf)?;
+/// # Ok(()) }
+/// ```
+#[inline]
+pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error> {
+ if !dest.is_empty() {
+ imp::getrandom_inner(dest)?;
}
- imp::getrandom_inner(dest)
+ // SAFETY: `dest` has been fully initialized by `imp::getrandom_inner`
+ // since it returned `Ok`.
+ Ok(unsafe { slice_assume_init_mut(dest) })
}
diff --git a/src/linux_android.rs b/src/linux_android.rs
index 4270b67..2517159 100644
--- a/src/linux_android.rs
+++ b/src/linux_android.rs
@@ -1,19 +1,12 @@
-// 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,
+ lazy::LazyBool,
util_libc::{last_os_error, sys_fill_exact},
{use_file, Error},
};
+use core::mem::MaybeUninit;
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getrandom(2) was introduced in Linux 3.17
static HAS_GETRANDOM: LazyBool = LazyBool::new();
if HAS_GETRANDOM.unsync_init(is_getrandom_available) {
diff --git a/src/macos.rs b/src/macos.rs
index 671a053..44af76b 100644
--- a/src/macos.rs
+++ b/src/macos.rs
@@ -1,36 +1,18 @@
-// 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;
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
-type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int;
+extern "C" {
+ // Supported as of macOS 10.12+.
+ fn getentropy(buf: *mut u8, size: 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());
- }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ for chunk in dest.chunks_mut(256) {
+ let ret = unsafe { getentropy(chunk.as_mut_ptr() as *mut u8, 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)
}
+ Ok(())
}
diff --git a/src/openbsd.rs b/src/openbsd.rs
index 4137173..f4d64da 100644
--- a/src/openbsd.rs
+++ b/src/openbsd.rs
@@ -1,15 +1,8 @@
-// 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};
+use core::mem::MaybeUninit;
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// getentropy(2) was added in OpenBSD 5.6, so we can use it unconditionally.
for chunk in dest.chunks_mut(256) {
let ret = unsafe { libc::getentropy(chunk.as_mut_ptr() as *mut libc::c_void, chunk.len()) };
diff --git a/src/rdrand.rs b/src/rdrand.rs
index 1df21e5..f527c8c 100644
--- a/src/rdrand.rs
+++ b/src/rdrand.rs
@@ -1,14 +1,6 @@
-// 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;
+//! RDRAND backend for x86(-64) targets
+use crate::{lazy::LazyBool, util::slice_as_uninit, Error};
+use core::mem::{size_of, MaybeUninit};
cfg_if! {
if #[cfg(target_arch = "x86_64")] {
@@ -24,74 +16,106 @@ cfg_if! {
// Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures
// Software Developer’s 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> {
+unsafe fn rdrand() -> Option<usize> {
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.
+ let mut val = 0;
+ if rdrand_step(&mut val) == 1 {
+ return Some(val as usize);
}
}
- Err(Error::FAILED_RDRAND)
+ None
}
-// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653.
+// "rdrand" target feature requires "+rdrand" 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."
+ "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrand."
);
-#[cfg(target_feature = "rdrand")]
-fn is_rdrand_supported() -> bool {
- true
+// Run a small self-test to make sure we aren't repeating values
+// Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c
+// Fails with probability < 2^(-90) on 32-bit systems
+#[target_feature(enable = "rdrand")]
+unsafe fn self_test() -> bool {
+ // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision.
+ let mut prev = !0; // TODO(MSRV 1.43): Move to usize::MAX
+ let mut fails = 0;
+ for _ in 0..8 {
+ match rdrand() {
+ Some(val) if val == prev => fails += 1,
+ Some(val) => prev = val,
+ None => return false,
+ };
+ }
+ fails <= 2
}
-// 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;
+fn is_rdrand_good() -> bool {
+ #[cfg(not(target_feature = "rdrand"))]
+ {
+ // SAFETY: All Rust x86 targets are new enough to have CPUID, and we
+ // check that leaf 1 is supported before using it.
+ let cpuid0 = unsafe { arch::__cpuid(0) };
+ if cpuid0.eax < 1 {
+ return false;
+ }
+ let cpuid1 = unsafe { arch::__cpuid(1) };
- // 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 })
+ let vendor_id = [
+ cpuid0.ebx.to_le_bytes(),
+ cpuid0.edx.to_le_bytes(),
+ cpuid0.ecx.to_le_bytes(),
+ ];
+ if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] {
+ let mut family = (cpuid1.eax >> 8) & 0xF;
+ if family == 0xF {
+ family += (cpuid1.eax >> 20) & 0xFF;
+ }
+ // AMD CPUs families before 17h (Zen) sometimes fail to set CF when
+ // RDRAND fails after suspend. Don't use RDRAND on those families.
+ // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286
+ if family < 0x17 {
+ return false;
+ }
+ }
+
+ const RDRAND_FLAG: u32 = 1 << 30;
+ if cpuid1.ecx & RDRAND_FLAG == 0 {
+ return false;
+ }
+ }
+
+ // SAFETY: We have already checked that rdrand is available.
+ unsafe { self_test() }
}
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
- if !is_rdrand_supported() {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ static RDRAND_GOOD: LazyBool = LazyBool::new();
+ if !RDRAND_GOOD.unsync_init(is_rdrand_good) {
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) }
+ // SAFETY: After this point, we know rdrand is supported.
+ unsafe { rdrand_exact(dest) }.ok_or(Error::FAILED_RDRAND)
}
+// TODO: make this function safe when we have feature(target_feature_11)
#[target_feature(enable = "rdrand")]
-unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> {
+unsafe fn rdrand_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> {
// 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);
+ let mut chunks = dest.chunks_exact_mut(size_of::<usize>());
for chunk in chunks.by_ref() {
- chunk.copy_from_slice(&rdrand()?);
+ let src = rdrand()?.to_ne_bytes();
+ chunk.copy_from_slice(slice_as_uninit(&src));
}
let tail = chunks.into_remainder();
let n = tail.len();
if n > 0 {
- tail.copy_from_slice(&rdrand()?[..n]);
+ let src = rdrand()?.to_ne_bytes();
+ tail.copy_from_slice(slice_as_uninit(&src[..n]));
}
- Ok(())
+ Some(())
}
diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs
index cf3067d..fbc2394 100644
--- a/src/solaris_illumos.rs
+++ b/src/solaris_illumos.rs
@@ -1,19 +1,10 @@
-// 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`.
+//! secure. We choose to read from `/dev/random` (and use GRND_RANDOM).
//!
-//! Since Solaris 11.3 and mid-2015 illumos, the `getrandom` syscall is available.
+//! Solaris 11.3 and late-2018 illumos added the getrandom(2) libc function.
//! 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.
@@ -22,23 +13,25 @@ use crate::{
util_libc::{sys_fill_exact, Weak},
Error,
};
-use core::mem;
+use core::mem::{self, MaybeUninit};
-#[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;
+static GETRANDOM: Weak = unsafe { Weak::new("getrandom\0") };
+type GetRandomFn =
+ unsafe extern "C" fn(*mut libc::c_void, libc::size_t, libc::c_uint) -> libc::ssize_t;
-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") };
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
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
+ // A cast is needed for the flags as libc uses the wrong type.
+ func(
+ buf.as_mut_ptr() as *mut libc::c_void,
+ buf.len(),
+ libc::GRND_RANDOM as libc::c_uint,
+ )
})?
}
Ok(())
diff --git a/src/solid.rs b/src/solid.rs
index dc76aac..cae8caf 100644
--- a/src/solid.rs
+++ b/src/solid.rs
@@ -1,21 +1,13 @@
-// 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;
+use core::{mem::MaybeUninit, num::NonZeroU32};
extern "C" {
pub fn SOLID_RNG_SampleRandomBytes(buffer: *mut u8, length: usize) -> i32;
}
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
- let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr(), dest.len()) };
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ let ret = unsafe { SOLID_RNG_SampleRandomBytes(dest.as_mut_ptr() as *mut u8, dest.len()) };
if ret >= 0 {
Ok(())
} else {
diff --git a/src/use_file.rs b/src/use_file.rs
index 16c0216..333325b 100644
--- a/src/use_file.rs
+++ b/src/use_file.rs
@@ -1,57 +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.
-
//! 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,
+ mem::MaybeUninit,
sync::atomic::{AtomicUsize, Ordering::Relaxed},
};
+// We prefer using /dev/urandom and only use /dev/random if the OS
+// documentation indicates that /dev/urandom is insecure.
+// On Solaris/Illumos, see src/solaris_illumos.rs
+// On Dragonfly, Haiku, and QNX Neutrino the devices are identical.
+#[cfg(any(target_os = "solaris", target_os = "illumos"))]
+const FILE_PATH: &str = "/dev/random\0";
#[cfg(any(
+ target_os = "aix",
+ target_os = "android",
+ target_os = "linux",
+ target_os = "redox",
target_os = "dragonfly",
- target_os = "emscripten",
target_os = "haiku",
- target_os = "macos",
- target_os = "solaris",
- target_os = "illumos"
+ target_os = "nto",
))]
-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";
+const FD_UNINIT: usize = usize::max_value();
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
let fd = get_rng_fd()?;
- let read = |buf: &mut [u8]| unsafe { libc::read(fd, buf.as_mut_ptr() as *mut _, buf.len()) };
-
- 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(())
+ sys_fill_exact(dest, |buf| unsafe {
+ libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void, buf.len())
+ })
}
// 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);
+ static FD: AtomicUsize = AtomicUsize::new(FD_UNINIT);
fn get_fd() -> Option<libc::c_int> {
match FD.load(Relaxed) {
- LazyUsize::UNINIT => None,
+ FD_UNINIT => None,
val => Some(val as libc::c_int),
}
}
@@ -76,8 +66,8 @@ fn get_rng_fd() -> Result<libc::c_int, Error> {
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);
+ // The fd always fits in a usize without conflicting with FD_UNINIT.
+ debug_assert!(fd >= 0 && (fd as usize) < FD_UNINIT);
FD.store(fd as usize, Relaxed);
Ok(fd)
diff --git a/src/util.rs b/src/util.rs
index 06e23c2..1c4e70b 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -1,64 +1,35 @@
-// 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};
+use core::{mem::MaybeUninit, ptr};
-// 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
- }
+/// Polyfill for `maybe_uninit_slice` feature's
+/// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have
+/// been initialized.
+#[inline(always)]
+pub unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] {
+ // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+ &mut *(slice as *mut [MaybeUninit<T>] as *mut [T])
}
-// Identical to LazyUsize except with bool instead of usize.
-pub struct LazyBool(LazyUsize);
+#[inline]
+pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] {
+ unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) };
+ unsafe { slice_assume_init_mut(slice) }
+}
-impl LazyBool {
- pub const fn new() -> Self {
- Self(LazyUsize::new())
- }
+#[inline(always)]
+pub fn slice_as_uninit<T>(slice: &[T]) -> &[MaybeUninit<T>] {
+ // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+ // There is no risk of writing a `MaybeUninit<T>` into the result since
+ // the result isn't mutable.
+ unsafe { &*(slice as *const [T] as *const [MaybeUninit<T>]) }
+}
- pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool {
- self.0.unsync_init(|| init() as usize) != 0
- }
+/// View an mutable initialized array as potentially-uninitialized.
+///
+/// This is unsafe because it allows assigning uninitialized values into
+/// `slice`, which would be undefined behavior.
+#[inline(always)]
+pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] {
+ // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`.
+ &mut *(slice as *mut [T] as *mut [MaybeUninit<T>])
}
diff --git a/src/util_libc.rs b/src/util_libc.rs
index d057071..0b792c3 100644
--- a/src/util_libc.rs
+++ b/src/util_libc.rs
@@ -1,13 +1,7 @@
-// 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::{
+ mem::MaybeUninit,
num::NonZeroU32,
ptr::NonNull,
sync::atomic::{fence, AtomicPtr, Ordering},
@@ -17,7 +11,7 @@ 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"))] {
+ } else if #[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd", 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;
@@ -25,12 +19,16 @@ 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"))] {
+ } else if #[cfg(target_os = "nto")] {
+ use libc::__get_errno_ptr as errno_location;
+ } else if #[cfg(any(all(target_os = "horizon", target_arch = "arm"), target_os = "vita"))] {
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;
+ } else if #[cfg(target_os = "aix")] {
+ use libc::_Errno as errno_location;
}
}
@@ -59,21 +57,24 @@ pub fn last_os_error() -> Error {
// - should return -1 and set errno on failure
// - should return the number of bytes written on success
pub fn sys_fill_exact(
- mut buf: &mut [u8],
- sys_fill: impl Fn(&mut [u8]) -> libc::ssize_t,
+ mut buf: &mut [MaybeUninit<u8>],
+ sys_fill: impl Fn(&mut [MaybeUninit<u8>]) -> libc::ssize_t,
) -> Result<(), Error> {
while !buf.is_empty() {
let res = sys_fill(buf);
- 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);
+ match res {
+ res if res > 0 => buf = buf.get_mut(res as usize..).ok_or(Error::UNEXPECTED)?,
+ -1 => {
+ 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
+ // Negative return codes not equal to -1 should be impossible.
+ // EOF (ret = 0) should be impossible, as the data we are reading
// should be an infinite stream of random bytes.
- buf = &mut buf[(res as usize)..];
+ _ => return Err(Error::UNEXPECTED),
}
}
Ok(())
@@ -135,19 +136,11 @@ impl Weak {
}
}
-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);
+ let fd = libc::open(path.as_ptr() as *const _, libc::O_RDONLY | libc::O_CLOEXEC);
if fd >= 0 {
return Ok(fd);
}
diff --git a/src/vita.rs b/src/vita.rs
new file mode 100644
index 0000000..20a9878
--- /dev/null
+++ b/src/vita.rs
@@ -0,0 +1,13 @@
+//! Implementation for PS Vita
+use crate::{util_libc::last_os_error, Error};
+use core::mem::MaybeUninit;
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ 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(())
+}
diff --git a/src/vxworks.rs b/src/vxworks.rs
index 6cb5d52..7ca9d6b 100644
--- a/src/vxworks.rs
+++ b/src/vxworks.rs
@@ -1,16 +1,11 @@
-// 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};
+use core::{
+ mem::MaybeUninit,
+ sync::atomic::{AtomicBool, Ordering::Relaxed},
+};
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
static RNG_INIT: AtomicBool = AtomicBool::new(false);
while !RNG_INIT.load(Relaxed) {
let ret = unsafe { libc::randSecure() };
@@ -25,7 +20,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
// Prevent overflow of i32
for chunk in dest.chunks_mut(i32::max_value() as usize) {
- let ret = unsafe { libc::randABytes(chunk.as_mut_ptr(), chunk.len() as i32) };
+ let ret = unsafe { libc::randABytes(chunk.as_mut_ptr() as *mut u8, chunk.len() as i32) };
if ret != 0 {
return Err(last_os_error());
}
diff --git a/src/wasi.rs b/src/wasi.rs
index c512182..d6c8a91 100644
--- a/src/wasi.rs
+++ b/src/wasi.rs
@@ -1,19 +1,17 @@
-// 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;
+use core::{
+ mem::MaybeUninit,
+ num::{NonZeroU16, NonZeroU32},
+};
+use wasi::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()),
- }
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
+ unsafe { random_get(dest.as_mut_ptr() as *mut u8, dest.len()) }.map_err(|e| {
+ // The WASI errno will always be non-zero, but we check just in case.
+ match NonZeroU16::new(e.raw()) {
+ Some(r) => Error::from(NonZeroU32::from(r)),
+ None => Error::ERRNO_NOT_POSITIVE,
+ }
+ })
}
diff --git a/src/windows.rs b/src/windows.rs
index 41dc37a..2d1c483 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -1,13 +1,6 @@
-// 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 Windows
use crate::Error;
-use core::{ffi::c_void, num::NonZeroU32, ptr};
+use core::{ffi::c_void, mem::MaybeUninit, num::NonZeroU32, ptr};
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
@@ -21,20 +14,37 @@ extern "system" {
) -> u32;
}
-pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> {
+// Forbidden when targetting UWP
+#[cfg(not(target_vendor = "uwp"))]
+#[link(name = "advapi32")]
+extern "system" {
+ #[link_name = "SystemFunction036"]
+ fn RtlGenRandom(RandomBuffer: *mut c_void, RandomBufferLength: u32) -> u8;
+}
+
+pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
// Prevent overflow of u32
for chunk in dest.chunks_mut(u32::max_value() as usize) {
// BCryptGenRandom was introduced in Windows Vista
let ret = unsafe {
BCryptGenRandom(
ptr::null_mut(),
- chunk.as_mut_ptr(),
+ chunk.as_mut_ptr() as *mut u8,
chunk.len() as u32,
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
// NTSTATUS codes use the two highest bits for severity status.
if ret >> 30 == 0b11 {
+ // Failed. Try RtlGenRandom as a fallback.
+ #[cfg(not(target_vendor = "uwp"))]
+ {
+ let ret =
+ unsafe { RtlGenRandom(chunk.as_mut_ptr() as *mut c_void, chunk.len() as u32) };
+ if ret != 0 {
+ continue;
+ }
+ }
// We zeroize the highest bit, so the error code will reside
// inside the range designated for OS codes.
let code = ret ^ (1 << 31);
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
index 006f230..666f7f5 100644
--- a/tests/common/mod.rs
+++ b/tests/common/mod.rs
@@ -12,7 +12,19 @@ fn test_zero() {
getrandom_impl(&mut [0u8; 0]).unwrap();
}
+// Return the number of bits in which s1 and s2 differ
+#[cfg(not(feature = "custom"))]
+fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
+ assert_eq!(s1.len(), s2.len());
+ s1.iter()
+ .zip(s2.iter())
+ .map(|(a, b)| (a ^ b).count_ones() as usize)
+ .sum()
+}
+
+// Tests the quality of calling getrandom on two large buffers
#[test]
+#[cfg(not(feature = "custom"))]
fn test_diff() {
let mut v1 = [0u8; 1000];
getrandom_impl(&mut v1).unwrap();
@@ -20,13 +32,35 @@ fn test_diff() {
let mut v2 = [0u8; 1000];
getrandom_impl(&mut v2).unwrap();
- let mut n_diff_bits = 0;
- for i in 0..v1.len() {
- n_diff_bits += (v1[i] ^ v2[i]).count_ones();
- }
+ // Between 3.5 and 4.5 bits per byte should differ. Probability of failure:
+ // ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500]
+ let d = num_diff_bits(&v1, &v2);
+ assert!(d > 3500);
+ assert!(d < 4500);
+}
- // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input.
- assert!(n_diff_bits >= v1.len() as u32);
+// Tests the quality of calling getrandom repeatedly on small buffers
+#[test]
+#[cfg(not(feature = "custom"))]
+fn test_small() {
+ // For each buffer size, get at least 256 bytes and check that between
+ // 3 and 5 bits per byte differ. Probability of failure:
+ // ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
+ for size in 1..=64 {
+ let mut num_bytes = 0;
+ let mut diff_bits = 0;
+ while num_bytes < 256 {
+ let mut s1 = vec![0u8; size];
+ getrandom_impl(&mut s1).unwrap();
+ let mut s2 = vec![0u8; size];
+ getrandom_impl(&mut s2).unwrap();
+
+ num_bytes += size;
+ diff_bits += num_diff_bits(&s1, &s2);
+ }
+ assert!(diff_bits > 3 * num_bytes);
+ assert!(diff_bits < 5 * num_bytes);
+ }
}
#[test]
diff --git a/tests/custom.rs b/tests/custom.rs
index 62eae1d..b085094 100644
--- a/tests/custom.rs
+++ b/tests/custom.rs
@@ -7,13 +7,8 @@
))]
use wasm_bindgen_test::wasm_bindgen_test as test;
-#[cfg(feature = "test-in-browser")]
-wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
-use core::{
- num::NonZeroU32,
- sync::atomic::{AtomicU8, Ordering},
-};
+use core::num::NonZeroU32;
use getrandom::{getrandom, register_custom_getrandom, Error};
fn len7_err() -> Error {
@@ -21,27 +16,36 @@ fn len7_err() -> Error {
}
fn super_insecure_rng(buf: &mut [u8]) -> Result<(), Error> {
+ // `getrandom` guarantees it will not call any implementation if the output
+ // buffer is empty.
+ assert!(!buf.is_empty());
// Length 7 buffers return a custom error
if buf.len() == 7 {
return Err(len7_err());
}
- // Otherwise, increment an atomic counter
- static COUNTER: AtomicU8 = AtomicU8::new(0);
+ // Otherwise, fill bytes based on input length
+ let mut start = buf.len() as u8;
for b in buf {
- *b = COUNTER.fetch_add(1, Ordering::Relaxed);
+ *b = start;
+ start = start.wrapping_mul(3);
}
Ok(())
}
register_custom_getrandom!(super_insecure_rng);
+use getrandom::getrandom as getrandom_impl;
+mod common;
+
#[test]
fn custom_rng_output() {
let mut buf = [0u8; 4];
assert_eq!(getrandom(&mut buf), Ok(()));
- assert_eq!(buf, [0, 1, 2, 3]);
+ assert_eq!(buf, [4, 12, 36, 108]);
+
+ let mut buf = [0u8; 3];
assert_eq!(getrandom(&mut buf), Ok(()));
- assert_eq!(buf, [4, 5, 6, 7]);
+ assert_eq!(buf, [3, 9, 27]);
}
#[test]
diff --git a/tests/rdrand.rs b/tests/rdrand.rs
index 4ff85c4..a355c31 100644
--- a/tests/rdrand.rs
+++ b/tests/rdrand.rs
@@ -6,10 +6,17 @@
use getrandom::Error;
#[macro_use]
extern crate cfg_if;
+#[path = "../src/lazy.rs"]
+mod lazy;
#[path = "../src/rdrand.rs"]
mod rdrand;
#[path = "../src/util.rs"]
mod util;
-use rdrand::getrandom_inner as getrandom_impl;
+// The rdrand implementation has the signature of getrandom_uninit(), but our
+// tests expect getrandom_impl() to have the signature of getrandom().
+fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> {
+ rdrand::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?;
+ Ok(())
+}
mod common;