From 9791b30a0556f3979848c45b893c52b6c980fc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thi=C3=A9baud=20Weksteen?= Date: Wed, 3 Mar 2021 16:30:20 +0100 Subject: Update to 0.2.2 Enable the std feature, required by rand_core. Bug: 181306757 Test: m libgetrandom Change-Id: Id1ca9eac70eb5900e7491bb8156a51d09a51dd9c --- .cargo_vcs_info.json | 2 +- Android.bp | 97 ++++++++-------- CHANGELOG.md | 76 +++++++++++++ Cargo.toml | 47 ++++---- Cargo.toml.orig | 48 ++++---- METADATA | 10 +- README.md | 52 ++++----- TEST_MAPPING | 24 +++- benches/mod.rs | 1 - build.rs | 7 +- src/bsd_arandom.rs | 11 +- src/cloudabi.rs | 25 ----- src/custom.rs | 102 +++++++++++++++++ src/dummy.rs | 14 --- src/error.rs | 95 ++++++++-------- src/error_impls.rs | 15 +-- src/ios.rs | 17 +-- src/js.rs | 106 +++++++++++++++++ src/lib.rs | 300 +++++++++++++++++++++---------------------------- src/linux_android.rs | 8 +- src/macos.rs | 11 +- src/openbsd.rs | 7 +- src/rdrand.rs | 31 +++-- src/solaris_illumos.rs | 7 +- src/use_file.rs | 16 ++- src/util.rs | 2 +- src/util_libc.rs | 16 +-- src/vxworks.rs | 5 +- src/wasm32_bindgen.rs | 113 ------------------- src/wasm32_stdweb.rs | 114 ------------------- src/windows.rs | 35 ++++-- src/windows_uwp.rs | 59 ---------- tests/common.rs | 68 ----------- tests/common/mod.rs | 66 +++++++++++ tests/custom.rs | 50 +++++++++ tests/normal.rs | 11 ++ tests/rdrand.rs | 15 +++ 37 files changed, 850 insertions(+), 833 deletions(-) delete mode 100644 src/cloudabi.rs create mode 100644 src/custom.rs delete mode 100644 src/dummy.rs create mode 100644 src/js.rs delete mode 100644 src/wasm32_bindgen.rs delete mode 100644 src/wasm32_stdweb.rs delete mode 100644 src/windows_uwp.rs delete mode 100644 tests/common.rs create mode 100644 tests/common/mod.rs create mode 100644 tests/custom.rs create mode 100644 tests/normal.rs create mode 100644 tests/rdrand.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index d17f1ee..64fafa1 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "33b859bae16ae0b1c782e48db2ed96eb2306a2fc" + "sha1": "d79de0c95c01860268e071bcb6b0d019e18cd608" } } diff --git a/Android.bp b/Android.bp index 9b9b8a0..d494d22 100644 --- a/Android.bp +++ b/Android.bp @@ -1,40 +1,5 @@ -// This file is generated by cargo2android.py --device --run --dependencies --tests. - -package { - default_applicable_licenses: ["external_rust_crates_getrandom_license"], -} - -// Added automatically by a large-scale-change that took the approach of -// 'apply every license found to every target'. While this makes sure we respect -// every license restriction, it may not be entirely correct. -// -// e.g. GPL in an MIT project might only apply to the contrib/ directory. -// -// Please consider splitting the single license below into multiple licenses, -// taking care not to lose any license_kind information, and overriding the -// default license using the 'licenses: [...]' property on targets as needed. -// -// For unused files, consider creating a 'fileGroup' with "//visibility:private" -// to attach the license to, and including a comment whether the files may be -// used in the current project. -// -// large-scale-change included anything that looked like it might be a license -// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc. -// -// Please consider removing redundant or irrelevant files from 'license_text:'. -// See: http://go/android-license-faq -license { - name: "external_rust_crates_getrandom_license", - visibility: [":__subpackages__"], - license_kinds: [ - "SPDX-license-identifier-Apache-2.0", - "SPDX-license-identifier-MIT", - ], - license_text: [ - "LICENSE-APACHE", - "LICENSE-MIT", - ], -} +// This file is generated by cargo2android.py --device --run --dependencies --tests --features=std. +// Do not modify this file as changes will be overridden on upgrade. rust_defaults { name: "getrandom_defaults", @@ -43,6 +8,7 @@ rust_defaults { test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", + features: ["std"], rustlibs: [ "libcfg_if", "liblibc", @@ -52,6 +18,9 @@ rust_defaults { rust_test_host { name: "getrandom_host_test_src_lib", defaults: ["getrandom_defaults"], + test_options: { + unit_test: true, + }, } rust_test { @@ -60,12 +29,12 @@ rust_test { } rust_defaults { - name: "getrandom_defaults_common", - crate_name: "common", - srcs: ["tests/common.rs"], + name: "getrandom_defaults_getrandom", + crate_name: "getrandom", test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", + features: ["std"], rustlibs: [ "libcfg_if", "libgetrandom", @@ -74,13 +43,48 @@ rust_defaults { } rust_test_host { - name: "getrandom_host_test_tests_common", - defaults: ["getrandom_defaults_common"], + name: "getrandom_host_test_tests_custom", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/custom.rs"], + test_options: { + unit_test: true, + }, +} + +rust_test { + name: "getrandom_device_test_tests_custom", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/custom.rs"], +} + +rust_test_host { + name: "getrandom_host_test_tests_normal", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/normal.rs"], + test_options: { + unit_test: true, + }, +} + +rust_test { + name: "getrandom_device_test_tests_normal", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/normal.rs"], +} + +rust_test_host { + name: "getrandom_host_test_tests_rdrand", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/rdrand.rs"], + test_options: { + unit_test: true, + }, } rust_test { - name: "getrandom_device_test_tests_common", - defaults: ["getrandom_defaults_common"], + name: "getrandom_device_test_tests_rdrand", + defaults: ["getrandom_defaults_getrandom"], + srcs: ["tests/rdrand.rs"], } rust_library { @@ -89,6 +93,7 @@ rust_library { crate_name: "getrandom", srcs: ["src/lib.rs"], edition: "2018", + features: ["std"], rustlibs: [ "libcfg_if", "liblibc", @@ -96,5 +101,5 @@ rust_library { } // dependent_library ["feature_list"] -// cfg-if-0.1.10 -// libc-0.2.72 +// cfg-if-1.0.0 +// libc-0.2.87 diff --git a/CHANGELOG.md b/CHANGELOG.md index 29b447c..c3ca728 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,82 @@ 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.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] + +[#198]: https://github.com/rust-random/getrandom/pull/198 +[#200]: https://github.com/rust-random/getrandom/pull/200 + +## [0.2.1] - 2021-01-03 +### Changed +- Update `cfg-if` to v1.0. [#166] +- Update `wasi` to v0.10. [#167] + +### Fixed +- Multithreaded WASM support. [#165] + +### Removed +- Windows XP support. [#177] +- Direct `stdweb` support. [#178] +- CloudABI support. [#184] + +[#165]: https://github.com/rust-random/getrandom/pull/165 +[#166]: https://github.com/rust-random/getrandom/pull/166 +[#167]: https://github.com/rust-random/getrandom/pull/167 +[#177]: https://github.com/rust-random/getrandom/pull/177 +[#178]: https://github.com/rust-random/getrandom/pull/178 +[#184]: https://github.com/rust-random/getrandom/pull/184 + +## [0.2.0] - 2020-09-10 +### Features for using getrandom on unsupported targets + +The following (off by default) Cargo features have been added: +- `"rdrand"` - use the RDRAND instruction on `no_std` `x86`/`x86_64` targets [#133] +- `"js"` - use JavaScript calls on `wasm32-unknown-unknown` [#149] + - Replaces the `stdweb` and `wasm-bindgen` features (which are removed) +- `"custom"` - allows a user to specify a custom implementation [#109] + +### Breaking Changes +- Unsupported targets no longer compile [#107] +- Change/Add `Error` constants [#120] +- Only impl `std` traits when the `"std"` Cargo feature is specified [#106] +- Remove offical support for Hermit, L4Re, and UEFI [#133] +- Remove optional `"log"` dependancy [#131] +- Update minimum supported Linux kernel to 2.6.32 [#153] +- Update MSRV to 1.34 [#159] + +[#106]: https://github.com/rust-random/getrandom/pull/106 +[#107]: https://github.com/rust-random/getrandom/pull/107 +[#109]: https://github.com/rust-random/getrandom/pull/109 +[#120]: https://github.com/rust-random/getrandom/pull/120 +[#131]: https://github.com/rust-random/getrandom/pull/131 +[#133]: https://github.com/rust-random/getrandom/pull/133 +[#149]: https://github.com/rust-random/getrandom/pull/149 +[#153]: https://github.com/rust-random/getrandom/pull/153 +[#159]: https://github.com/rust-random/getrandom/pull/159 + +## [0.1.16] - 2020-12-31 +### Changed +- Update `cfg-if` to v1.0. [#173] +- Implement `std::error::Error` for the `Error` type on additional targets. [#169] + +### Fixed +- Multithreaded WASM support. [#171] + +[#173]: https://github.com/rust-random/getrandom/pull/173 +[#171]: https://github.com/rust-random/getrandom/pull/171 +[#169]: https://github.com/rust-random/getrandom/pull/169 + +## [0.1.15] - 2020-09-10 +### Changed +- Added support for Internet Explorer 11 [#139] +- Fix Webpack require warning with `wasm-bindgen` [#137] + +[#137]: https://github.com/rust-random/getrandom/pull/137 +[#139]: https://github.com/rust-random/getrandom/pull/139 + ## [0.1.14] - 2020-01-07 ### Changed - Remove use of spin-locks in the `use_file` module. [#125] diff --git a/Cargo.toml b/Cargo.toml index 1c1f718..2c0c056 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,16 +13,19 @@ [package] edition = "2018" name = "getrandom" -version = "0.1.14" +version = "0.2.2" authors = ["The Rand Project Developers"] -exclude = ["utils/*", ".*", "appveyor.yml"] +exclude = [".*"] description = "A small cross-platform library for retrieving random data from system source" documentation = "https://docs.rs/getrandom" categories = ["os", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/rust-random/getrandom" +[package.metadata.docs.rs] +features = ["std", "custom"] +rustdoc-args = ["--cfg", "docsrs"] [dependencies.cfg-if] -version = "0.1.2" +version = "1" [dependencies.compiler_builtins] version = "0.1" @@ -33,31 +36,25 @@ version = "1.0" optional = true package = "rustc-std-workspace-core" -[dependencies.log] -version = "0.4" -optional = true - [features] -dummy = [] -rustc-dep-of-std = ["compiler_builtins", "core"] +custom = [] +js = ["wasm-bindgen", "js-sys"] +rdrand = [] +rustc-dep-of-std = ["compiler_builtins", "core", "libc/rustc-dep-of-std", "wasi/rustc-dep-of-std"] std = [] -test-in-browser = ["wasm-bindgen"] +test-in-browser = [] +[target."cfg(all(target_arch = \"wasm32\", target_os = \"unknown\"))".dependencies.js-sys] +version = "0.3" +optional = true + +[target."cfg(all(target_arch = \"wasm32\", 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] +version = "0.3.18" [target."cfg(target_os = \"wasi\")".dependencies.wasi] -version = "0.9" +version = "0.10" [target."cfg(unix)".dependencies.libc] version = "0.2.64" default-features = false -[target.wasm32-unknown-unknown.dependencies.stdweb] -version = "0.4.18" -optional = true - -[target.wasm32-unknown-unknown.dependencies.wasm-bindgen] -version = "0.2.29" -optional = true -[target.wasm32-unknown-unknown.dev-dependencies.wasm-bindgen-test] -version = "0.2" -[badges.appveyor] -repository = "rust-random/getrandom" - -[badges.travis-ci] -repository = "rust-random/getrandom" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index d10183f..dabf016 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "getrandom" -version = "0.1.14" +version = "0.2.2" # Also update html_root_url in lib.rs when bumping this edition = "2018" authors = ["The Rand Project Developers"] license = "MIT OR Apache-2.0" @@ -8,15 +8,10 @@ description = "A small cross-platform library for retrieving random data from sy documentation = "https://docs.rs/getrandom" repository = "https://github.com/rust-random/getrandom" categories = ["os", "no-std"] -exclude = ["utils/*", ".*", "appveyor.yml"] - -[badges] -travis-ci = { repository = "rust-random/getrandom" } -appveyor = { repository = "rust-random/getrandom" } +exclude = [".*"] [dependencies] -log = { version = "0.4", optional = true } -cfg-if = "0.1.2" +cfg-if = "1" # When built as part of libstd compiler_builtins = { version = "0.1", optional = true } @@ -26,20 +21,33 @@ core = { version = "1.0", optional = true, package = "rustc-std-workspace-core" libc = { version = "0.2.64", default-features = false } [target.'cfg(target_os = "wasi")'.dependencies] -wasi = "0.9" - -[target.wasm32-unknown-unknown.dependencies] -wasm-bindgen = { version = "0.2.29", optional = true } -stdweb = { version = "0.4.18", optional = true } +wasi = "0.10" -[target.wasm32-unknown-unknown.dev-dependencies] -wasm-bindgen-test = "0.2" +[target.'cfg(all(target_arch = "wasm32", 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] +wasm-bindgen-test = "0.3.18" [features] +# Implement std-only traits for getrandom::Error std = [] -# Enables dummy implementation for unsupported targets -dummy = [] +# Feature to enable fallback RDRAND-based implementation on x86/x86_64 +rdrand = [] +# Feature to enable JavaScript bindings on wasm32-unknown-unknown +js = ["wasm-bindgen", "js-sys"] +# Feature to enable custom RNG implementations +custom = [] # Unstable feature to support being a libstd dependency -rustc-dep-of-std = ["compiler_builtins", "core"] -# Unstable feature for testing -test-in-browser = ["wasm-bindgen"] +rustc-dep-of-std = [ + "compiler_builtins", + "core", + "libc/rustc-dep-of-std", + "wasi/rustc-dep-of-std", +] +# Unstable/test-only feature to run wasm-bindgen tests in a browser +test-in-browser = [] + +[package.metadata.docs.rs] +features = ["std", "custom"] +rustdoc-args = ["--cfg", "docsrs"] diff --git a/METADATA b/METADATA index 27f871d..60dca65 100644 --- a/METADATA +++ b/METADATA @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/getrandom/getrandom-0.1.14.crate" + value: "https://static.crates.io/crates/getrandom/getrandom-0.2.2.crate" } - version: "0.1.14" + version: "0.2.2" license_type: NOTICE last_upgrade_date { - year: 2020 - month: 6 - day: 18 + year: 2021 + month: 3 + day: 3 } } diff --git a/README.md b/README.md index 01bbfb5..df2307b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,18 @@ # getrandom -[![Build Status](https://travis-ci.org/rust-random/getrandom.svg?branch=master)](https://travis-ci.org/rust-random/getrandom) -[![Build Status](https://ci.appveyor.com/api/projects/status/github/rust-random/getrandom?svg=true)](https://ci.appveyor.com/project/rust-random/getrandom) -[![Crate](https://img.shields.io/crates/v/getrandom.svg)](https://crates.io/crates/getrandom) -[![Documentation](https://docs.rs/getrandom/badge.svg)](https://docs.rs/getrandom) -[![Dependency status](https://deps.rs/repo/github/rust-random/getrandom/status.svg)](https://deps.rs/repo/github/rust-random/getrandom) +[![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 +[crates.io]: https://crates.io/crates/getrandom +[Crate]: https://img.shields.io/crates/v/getrandom +[docs.rs]: https://docs.rs/getrandom +[Documentation]: https://docs.rs/getrandom/badge.svg +[deps.rs]: https://deps.rs/repo/github/rust-random/getrandom +[Dependency Status]: https://deps.rs/repo/github/rust-random/getrandom/status.svg +[Downloads]: https://img.shields.io/crates/d/getrandom +[LICENSE-MIT]: https://raw.githubusercontent.com/rust-random/getrandom/master/LICENSE-MIT +[License]: https://img.shields.io/crates/l/getrandom A Rust library for retrieving random data from (operating) system source. It is @@ -24,7 +32,7 @@ Add this to your `Cargo.toml`: ```toml [dependencies] -getrandom = "0.1" +getrandom = "0.2" ``` Then invoke the `getrandom` function: @@ -37,36 +45,14 @@ fn get_random_buf() -> Result<[u8; 32], getrandom::Error> { } ``` -## Features - -This library is `no_std` for every supported target. However, getting randomness -usually requires calling some external system API. This means most platforms -will require linking against system libraries (i.e. `libc` for Unix, -`Advapi32.dll` for Windows, Security framework on iOS, etc...). - -The `log` library is supported as an optional dependency. If enabled, error -reporting will be improved on some platforms. - -For the `wasm32-unknown-unknown` target, one of the following features should be -enabled: - -- [`wasm-bindgen`](https://crates.io/crates/wasm_bindgen) -- [`stdweb`](https://crates.io/crates/stdweb) - -By default, compiling `getrandom` for an unsupported target will result in -a compilation error. If you want to build an application which uses `getrandom` -for such target, you can either: -- Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml` -to switch to a custom implementation with a support of your target. -- Enable the `dummy` feature to have getrandom use an implementation that always -fails at run-time on unsupported targets. - -[replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section -[patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section +For more information about supported targets, entropy sources, `no_std` targets, +crate features, WASM support and Custom RNGs see the +[`getrandom` documentation](https://docs.rs/getrandom/latest) and +[`getrandom::Error` documentation](https://docs.rs/getrandom/latest/getrandom/struct.Error.html). ## Minimum Supported Rust Version -This crate requires Rust 1.32.0 or later. +This crate requires Rust 1.34.0 or later. # License diff --git a/TEST_MAPPING b/TEST_MAPPING index bc55ed7..efa80d7 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,14 +1,32 @@ -// Generated by cargo2android.py for tests in Android.bp +// Generated by update_crate_tests.py for tests that depend on this crate. { "presubmit": [ { - "name": "getrandom_device_test_src_lib" + "name": "keystore2_test" + }, + { + "name": "getrandom_device_test_tests_custom" + }, + { + "name": "rand_xorshift_device_test_tests_mod" + }, + { + "name": "getrandom_device_test_tests_normal" }, { - "name": "getrandom_device_test_tests_common" + "name": "rand_xorshift_device_test_src_lib" + }, + { + "name": "getrandom_device_test_tests_rdrand" }, { "name": "rand_core_device_test_src_lib" + }, + { + "name": "getrandom_device_test_src_lib" + }, + { + "name": "vpnprofilestore_test" } ] } diff --git a/benches/mod.rs b/benches/mod.rs index 07953f1..a93e720 100644 --- a/benches/mod.rs +++ b/benches/mod.rs @@ -1,5 +1,4 @@ #![feature(test)] -extern crate getrandom; extern crate test; #[bench] diff --git a/build.rs b/build.rs index 1beb4ed..95f4b90 100644 --- a/build.rs +++ b/build.rs @@ -4,14 +4,9 @@ use std::env; fn main() { let target = env::var("TARGET").expect("TARGET was not set"); - if target.contains("-uwp-windows-") { + if target.contains("windows") { // for BCryptGenRandom println!("cargo:rustc-link-lib=bcrypt"); - // to work around unavailability of `target_vendor` on Rust 1.33 - println!("cargo:rustc-cfg=getrandom_uwp"); - } else if target.contains("windows") { - // for RtlGenRandom (aka SystemFunction036) - println!("cargo:rustc-link-lib=advapi32"); } else if target.contains("apple-ios") { // for SecRandomCopyBytes and kSecRandomDefault println!("cargo:rustc-link-lib=framework=Security"); diff --git a/src/bsd_arandom.rs b/src/bsd_arandom.rs index eb564ff..f26f260 100644 --- a/src/bsd_arandom.rs +++ b/src/bsd_arandom.rs @@ -7,8 +7,7 @@ // except according to those terms. //! Implementation for FreeBSD and NetBSD -use crate::util_libc::sys_fill_exact; -use crate::Error; +use crate::{util_libc::sys_fill_exact, Error}; use core::ptr; fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { @@ -25,7 +24,6 @@ fn kern_arnd(buf: &mut [u8]) -> libc::ssize_t { ) }; if ret == -1 { - error!("sysctl kern.arandom: syscall failed"); -1 } else { len as libc::ssize_t @@ -45,5 +43,10 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { return sys_fill_exact(dest, |buf| unsafe { func(buf.as_mut_ptr(), buf.len(), 0) }); } } - sys_fill_exact(dest, kern_arnd) + // Both FreeBSD and NetBSD will only return up to 256 bytes at a time, and + // older NetBSD kernels will fail on longer buffers. + for chunk in dest.chunks_mut(256) { + sys_fill_exact(chunk, kern_arnd)? + } + Ok(()) } diff --git a/src/cloudabi.rs b/src/cloudabi.rs deleted file mode 100644 index d3d0928..0000000 --- a/src/cloudabi.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for CloudABI -use crate::Error; -use core::num::NonZeroU32; - -extern "C" { - fn cloudabi_sys_random_get(buf: *mut u8, buf_len: usize) -> u16; -} - -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let errno = unsafe { cloudabi_sys_random_get(dest.as_mut_ptr(), dest.len()) }; - if let Some(code) = NonZeroU32::new(errno as u32) { - error!("cloudabi_sys_random_get: failed with {}", errno); - Err(Error::from(code)) - } else { - Ok(()) // Zero means success for CloudABI - } -} diff --git a/src/custom.rs b/src/custom.rs new file mode 100644 index 0000000..0d3123c --- /dev/null +++ b/src/custom.rs @@ -0,0 +1,102 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An implementation which calls out to an externally defined function. +use crate::Error; +use core::num::NonZeroU32; + +/// Register a function to be invoked by `getrandom` on unsupported targets. +/// +/// ## Writing a custom `getrandom` implementation +/// +/// The function to register must have the same signature as +/// [`getrandom::getrandom`](crate::getrandom). The function can be defined +/// wherever you want, either in root crate or a dependant crate. +/// +/// For example, if we wanted a `failure-getrandom` crate containing an +/// implementation that always fails, we would first depend on `getrandom` +/// (for the [`Error`] type) in `failure-getrandom/Cargo.toml`: +/// ```toml +/// [dependencies] +/// getrandom = "0.2" +/// ``` +/// Note that the crate containing this function does **not** need to enable the +/// `"custom"` Cargo feature. +/// +/// Next, in `failure-getrandom/src/lib.rs`, we define our function: +/// ```rust +/// use core::num::NonZeroU32; +/// use getrandom::Error; +/// +/// // Some application-specific error code +/// const MY_CUSTOM_ERROR_CODE: u32 = Error::CUSTOM_START + 42; +/// pub fn always_fail(buf: &mut [u8]) -> Result<(), Error> { +/// let code = NonZeroU32::new(MY_CUSTOM_ERROR_CODE).unwrap(); +/// Err(Error::from(code)) +/// } +/// ``` +/// +/// ## Registering a custom `getrandom` implementation +/// +/// Functions can only be registered in the root binary crate. Attempting to +/// register a function in a non-root crate will result in a linker error. +/// This is similar to +/// [`#[panic_handler]`](https://doc.rust-lang.org/nomicon/panic-handler.html) or +/// [`#[global_allocator]`](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/global-allocators.html), +/// where helper crates define handlers/allocators but only the binary crate +/// actually _uses_ the functionality. +/// +/// To register the function, we first depend on `failure-getrandom` _and_ +/// `getrandom` in `Cargo.toml`: +/// ```toml +/// [dependencies] +/// failure-getrandom = "0.1" +/// getrandom = { version = "0.2", features = ["custom"] } +/// ``` +/// +/// Then, we register the function in `src/main.rs`: +/// ```rust +/// # mod failure_getrandom { pub fn always_fail(_: &mut [u8]) -> Result<(), getrandom::Error> { unimplemented!() } } +/// use failure_getrandom::always_fail; +/// use getrandom::register_custom_getrandom; +/// +/// register_custom_getrandom!(always_fail); +/// ``` +/// +/// Now any user of `getrandom` (direct or indirect) on this target will use the +/// registered function. As noted in the +/// [top-level documentation](index.html#custom-implementations) this +/// registration only has an effect on unsupported targets. +#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "custom")))] +macro_rules! register_custom_getrandom { + ($path:path) => { + // We use an extern "C" function to get the guarantees of a stable ABI. + #[no_mangle] + extern "C" fn __getrandom_custom(dest: *mut u8, len: usize) -> u32 { + let f: fn(&mut [u8]) -> Result<(), ::getrandom::Error> = $path; + let slice = unsafe { ::core::slice::from_raw_parts_mut(dest, len) }; + match f(slice) { + Ok(()) => 0, + Err(e) => e.code().get(), + } + } + }; +} + +#[allow(dead_code)] +pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + extern "C" { + fn __getrandom_custom(dest: *mut u8, len: usize) -> u32; + } + let ret = unsafe { __getrandom_custom(dest.as_mut_ptr(), dest.len()) }; + match NonZeroU32::new(ret) { + None => Ok(()), + Some(code) => Err(Error::from(code)), + } +} diff --git a/src/dummy.rs b/src/dummy.rs deleted file mode 100644 index 0c24ba0..0000000 --- a/src/dummy.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A dummy implementation for unsupported targets which always fails -use crate::{error::UNSUPPORTED, Error}; - -pub fn getrandom_inner(_: &mut [u8]) -> Result<(), Error> { - Err(UNSUPPORTED) -} diff --git a/src/error.rs b/src/error.rs index 31ae24d..48abdc1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,10 +5,9 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use core::fmt; -use core::num::NonZeroU32; +use core::{fmt, num::NonZeroU32}; -/// A small and `no_std` compatible error type. +/// A small and `no_std` compatible error type /// /// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and /// if so, which error code the OS gave the application. If such an error is @@ -16,16 +15,44 @@ use core::num::NonZeroU32; /// /// Internally this type is a NonZeroU32, with certain values reserved for /// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`]. +/// +/// *If this crate's `"std"` Cargo feature is enabled*, then: +/// - [`getrandom::Error`][Error] implements +/// [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html) +/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements +/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html). #[derive(Copy, Clone, Eq, PartialEq)] pub struct Error(NonZeroU32); +const fn internal_error(n: u16) -> Error { + // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32. + let code = Error::INTERNAL_START + (n as u32); + Error(unsafe { NonZeroU32::new_unchecked(code) }) +} + impl Error { - #[deprecated(since = "0.1.7")] - /// Unknown error. - pub const UNKNOWN: Error = UNSUPPORTED; - #[deprecated(since = "0.1.7")] - /// System entropy source is unavailable. - pub const UNAVAILABLE: Error = UNSUPPORTED; + /// This target/platform is not supported by `getrandom`. + pub const UNSUPPORTED: Error = internal_error(0); + /// The platform-specific `errno` returned a non-positive value. + pub const ERRNO_NOT_POSITIVE: Error = internal_error(1); + /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed. + pub const IOS_SEC_RANDOM: Error = internal_error(3); + /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed. + pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4); + /// RDRAND instruction failed due to a hardware issue. + pub const FAILED_RDRAND: Error = internal_error(5); + /// RDRAND instruction unsupported on this target. + pub const NO_RDRAND: Error = internal_error(6); + /// The browser does not have support for `self.crypto`. + pub const WEB_CRYPTO: Error = internal_error(7); + /// The browser does not have support for `crypto.getRandomValues`. + pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8); + /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized). + pub const VXWORKS_RAND_SECURE: Error = internal_error(11); + /// NodeJS does not have support for the `crypto` module. + pub const NODE_CRYPTO: Error = internal_error(12); + /// NodeJS does not have support for `crypto.randomFillSync`. + pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13); /// Codes below this point represent OS Errors (i.e. positive i32 values). /// Codes at or above this point, but below [`Error::CUSTOM_START`] are @@ -38,9 +65,11 @@ impl Error { /// Extract the raw OS error code (if this error came from the OS) /// - /// This method is identical to `std::io::Error::raw_os_error()`, except + /// This method is identical to [`std::io::Error::raw_os_error()`][1], except /// that it works in `no_std` contexts. If this method returns `None`, the /// error value can still be formatted via the `Display` implementation. + /// + /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error #[inline] pub fn raw_os_error(self) -> Option { if self.0.get() < Self::INTERNAL_START { @@ -55,7 +84,7 @@ impl Error { /// This code can either come from the underlying OS, or be a custom error. /// Use [`Error::raw_os_error()`] to disambiguate. #[inline] - pub fn code(self) -> NonZeroU32 { + pub const fn code(self) -> NonZeroU32 { self.0 } } @@ -125,41 +154,19 @@ impl From for Error { } } -// TODO: Convert to a function when min_version >= 1.33 -macro_rules! internal_error { - ($n:expr) => { - Error(unsafe { NonZeroU32::new_unchecked(Error::INTERNAL_START + $n as u16 as u32) }) - }; -} - -/// Internal Error constants -pub(crate) const UNSUPPORTED: Error = internal_error!(0); -pub(crate) const ERRNO_NOT_POSITIVE: Error = internal_error!(1); -pub(crate) const UNKNOWN_IO_ERROR: Error = internal_error!(2); -pub(crate) const SEC_RANDOM_FAILED: Error = internal_error!(3); -pub(crate) const RTL_GEN_RANDOM_FAILED: Error = internal_error!(4); -pub(crate) const FAILED_RDRAND: Error = internal_error!(5); -pub(crate) const NO_RDRAND: Error = internal_error!(6); -pub(crate) const BINDGEN_CRYPTO_UNDEF: Error = internal_error!(7); -pub(crate) const BINDGEN_GRV_UNDEF: Error = internal_error!(8); -pub(crate) const STDWEB_NO_RNG: Error = internal_error!(9); -pub(crate) const STDWEB_RNG_FAILED: Error = internal_error!(10); -pub(crate) const RAND_SECURE_FATAL: Error = internal_error!(11); - fn internal_desc(error: Error) -> Option<&'static str> { match error { - UNSUPPORTED => Some("getrandom: this target is not supported"), - ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), - UNKNOWN_IO_ERROR => Some("Unknown std::io::Error"), - SEC_RANDOM_FAILED => Some("SecRandomCopyBytes: call failed"), - RTL_GEN_RANDOM_FAILED => Some("RtlGenRandom: call failed"), - FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), - NO_RDRAND => Some("RDRAND: instruction not supported"), - BINDGEN_CRYPTO_UNDEF => Some("wasm-bindgen: self.crypto is undefined"), - BINDGEN_GRV_UNDEF => Some("wasm-bindgen: crypto.getRandomValues is undefined"), - STDWEB_NO_RNG => Some("stdweb: no randomness source available"), - STDWEB_RNG_FAILED => Some("stdweb: failed to get randomness"), - RAND_SECURE_FATAL => Some("randSecure: random number generator module is not initialized"), + Error::UNSUPPORTED => Some("getrandom: this target is not supported"), + Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"), + Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"), + Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"), + Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"), + Error::NO_RDRAND => Some("RDRAND: instruction not supported"), + Error::WEB_CRYPTO => Some("Web API self.crypto is unavailable"), + Error::WEB_GET_RANDOM_VALUES => Some("Web API crypto.getRandomValues is unavailable"), + Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"), + Error::NODE_CRYPTO => Some("Node.js crypto module is unavailable"), + Error::NODE_RANDOM_FILL_SYNC => Some("Node.js API crypto.randomFillSync is unavailable"), _ => None, } } diff --git a/src/error_impls.rs b/src/error_impls.rs index 007472e..61f46d2 100644 --- a/src/error_impls.rs +++ b/src/error_impls.rs @@ -5,24 +5,13 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. +#![cfg_attr(docsrs, doc(cfg(feature = "std")))] extern crate std; -use crate::{error::UNKNOWN_IO_ERROR, Error}; +use crate::Error; use core::convert::From; -use core::num::NonZeroU32; use std::io; -impl From for Error { - fn from(err: io::Error) -> Self { - if let Some(errno) = err.raw_os_error() { - if let Some(code) = NonZeroU32::new(errno as u32) { - return Error::from(code); - } - } - UNKNOWN_IO_ERROR - } -} - impl From for io::Error { fn from(err: Error) -> Self { match err.raw_os_error() { diff --git a/src/ios.rs b/src/ios.rs index 30c008c..5e38474 100644 --- a/src/ios.rs +++ b/src/ios.rs @@ -7,24 +7,19 @@ // except according to those terms. //! Implementation for iOS -use crate::{error::SEC_RANDOM_FAILED, Error}; - -// TODO: Make extern once extern_types feature is stabilized. See: -// https://github.com/rust-lang/rust/issues/43467 -#[repr(C)] -struct SecRandom([u8; 0]); +use crate::Error; +use core::{ffi::c_void, ptr::null}; #[link(name = "Security", kind = "framework")] extern "C" { - static kSecRandomDefault: *const SecRandom; - - fn SecRandomCopyBytes(rnd: *const SecRandom, count: usize, bytes: *mut u8) -> i32; + fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> i32; } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, dest.len(), dest.as_mut_ptr()) }; + // Apple's documentation guarantees kSecRandomDefault is a synonym for NULL. + let ret = unsafe { SecRandomCopyBytes(null(), dest.len(), dest.as_mut_ptr()) }; if ret == -1 { - Err(SEC_RANDOM_FAILED) + Err(Error::IOS_SEC_RANDOM) } else { Ok(()) } diff --git a/src/js.rs b/src/js.rs new file mode 100644 index 0000000..d48c7d3 --- /dev/null +++ b/src/js.rs @@ -0,0 +1,106 @@ +// Copyright 2018 Developers of the Rand project. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +use crate::Error; + +extern crate std; +use std::thread_local; + +use js_sys::Uint8Array; +use wasm_bindgen::prelude::*; + +// Maximum is 65536 bytes see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues +const BROWSER_CRYPTO_BUFFER_SIZE: usize = 256; + +enum RngSource { + Node(NodeCrypto), + Browser(BrowserCrypto, Uint8Array), +} + +// JsValues are always per-thread, so we initialize RngSource for each thread. +// See: https://github.com/rustwasm/wasm-bindgen/pull/955 +thread_local!( + static RNG_SOURCE: Result = getrandom_init(); +); + +pub(crate) fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + RNG_SOURCE.with(|result| { + let source = result.as_ref().map_err(|&e| e)?; + + match source { + RngSource::Node(n) => { + if n.random_fill_sync(dest).is_err() { + return Err(Error::NODE_RANDOM_FILL_SYNC); + } + } + RngSource::Browser(crypto, buf) => { + // getRandomValues does not work with all types of WASM memory, + // so we initially write to browser memory to avoid exceptions. + for chunk in dest.chunks_mut(BROWSER_CRYPTO_BUFFER_SIZE) { + // The chunk can be smaller than buf's length, so we call to + // JS to create a smaller view of buf without allocation. + let sub_buf = buf.subarray(0, chunk.len() as u32); + + if crypto.get_random_values(&sub_buf).is_err() { + return Err(Error::WEB_GET_RANDOM_VALUES); + } + sub_buf.copy_to(chunk); + } + } + }; + Ok(()) + }) +} + +fn getrandom_init() -> Result { + if let Ok(self_) = Global::get_self() { + // If `self` is defined then we're in a browser somehow (main window + // or web worker). We get `self.crypto` (called `msCrypto` on IE), so we + // can call `crypto.getRandomValues`. If `crypto` isn't defined, we + // assume we're in an older web browser and the OS RNG isn't available. + + let crypto: BrowserCrypto = match (self_.crypto(), self_.ms_crypto()) { + (crypto, _) if !crypto.is_undefined() => crypto, + (_, crypto) if !crypto.is_undefined() => crypto, + _ => return Err(Error::WEB_CRYPTO), + }; + + let buf = Uint8Array::new_with_length(BROWSER_CRYPTO_BUFFER_SIZE as u32); + return Ok(RngSource::Browser(crypto, buf)); + } + + let crypto = MODULE.require("crypto").map_err(|_| Error::NODE_CRYPTO)?; + Ok(RngSource::Node(crypto)) +} + +#[wasm_bindgen] +extern "C" { + type Global; + #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)] + fn get_self() -> Result; + + type Self_; + #[wasm_bindgen(method, getter, js_name = "msCrypto")] + fn ms_crypto(me: &Self_) -> BrowserCrypto; + #[wasm_bindgen(method, getter)] + fn crypto(me: &Self_) -> BrowserCrypto; + + type BrowserCrypto; + #[wasm_bindgen(method, js_name = getRandomValues, catch)] + fn get_random_values(me: &BrowserCrypto, buf: &Uint8Array) -> Result<(), JsValue>; + + #[wasm_bindgen(js_name = module)] + static MODULE: NodeModule; + + type NodeModule; + #[wasm_bindgen(method, catch)] + fn require(this: &NodeModule, s: &str) -> Result; + + type NodeCrypto; + #[wasm_bindgen(method, js_name = randomFillSync, catch)] + fn random_fill_sync(crypto: &NodeCrypto, buf: &mut [u8]) -> Result<(), JsValue>; +} diff --git a/src/lib.rs b/src/lib.rs index c305406..b1a5b10 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,89 +6,111 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Interface to the random number generator of the operating system. +//! Interface to the operating system's random number generator. +//! +//! # Supported targets +//! +//! | Target | Target Triple | Implementation +//! | ----------------- | ------------------ | -------------- +//! | Linux, Android | `*‑linux‑*` | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` | +//! | Windows | `*‑windows‑*` | [`BCryptGenRandom`][3] | +//! | macOS | `*‑apple‑darwin` | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`) +//! | iOS | `*‑apple‑ios` | [`SecRandomCopyBytes`][4] +//! | FreeBSD | `*‑freebsd` | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5] +//! | OpenBSD | `*‑openbsd` | [`getentropy`][6] +//! | NetBSD | `*‑netbsd` | [`kern.arandom`][7] +//! | Dragonfly BSD | `*‑dragonfly` | [`/dev/random`][8] +//! | Solaris, illumos | `*‑solaris`, `*‑illumos` | [`getrandom()`][9] if available, otherwise [`/dev/random`][10] +//! | Fuchsia OS | `*‑fuchsia` | [`cprng_draw`][11] +//! | Redox | `*‑redox` | [`rand:`][12] +//! | Haiku | `*‑haiku` | `/dev/random` (identical to `/dev/urandom`) +//! | SGX | `x86_64‑*‑sgx` | [RDRAND][18] +//! | VxWorks | `*‑wrs‑vxworks‑*` | `randABytes` after checking entropy pool initialization with `randSecure` +//! | Emscripten | `*‑emscripten` | `/dev/random` (identical to `/dev/urandom`) +//! | WASI | `wasm32‑wasi` | [`__wasi_random_get`][17] +//! | Web Browser | `wasm32‑*‑unknown` | [`Crypto.getRandomValues()`][14], see [WebAssembly support][16] +//! | Node.js | `wasm32‑*‑unknown` | [`crypto.randomBytes`][15], see [WebAssembly support][16] +//! +//! There is no blanket implementation on `unix` targets that reads from +//! `/dev/urandom`. This ensures all supported targets are using the recommended +//! interface and respect maximum buffer sizes. +//! +//! Pull Requests that add support for new targets to `getrandom` are always welcome. //! -//! # Platform sources +//! ## Unsupported targets //! -//! | OS | interface -//! |------------------|--------------------------------------------------------- -//! | Linux, Android | [`getrandom`][1] system call if available, otherwise [`/dev/urandom`][2] after successfully polling `/dev/random` -//! | Windows | [`RtlGenRandom`][3] -//! | macOS | [`getentropy()`][19] if available, otherwise [`/dev/random`][20] (identical to `/dev/urandom`) -//! | iOS | [`SecRandomCopyBytes`][4] -//! | FreeBSD | [`getrandom()`][21] if available, otherwise [`kern.arandom`][5] -//! | OpenBSD | [`getentropy`][6] -//! | NetBSD | [`kern.arandom`][7] -//! | Dragonfly BSD | [`/dev/random`][8] -//! | Solaris, illumos | [`getrandom`][9] system call if available, otherwise [`/dev/random`][10] -//! | Fuchsia OS | [`cprng_draw`][11] -//! | Redox | [`rand:`][12] -//! | CloudABI | [`cloudabi_sys_random_get`][13] -//! | Haiku | `/dev/random` (identical to `/dev/urandom`) -//! | L4RE, SGX, UEFI | [RDRAND][18] -//! | Hermit | [RDRAND][18] as [`sys_rand`][22] is currently broken. -//! | VxWorks | `randABytes` after checking entropy pool initialization with `randSecure` -//! | Web browsers | [`Crypto.getRandomValues`][14] (see [Support for WebAssembly and asm.js][16]) -//! | Node.js | [`crypto.randomBytes`][15] (see [Support for WebAssembly and asm.js][16]) -//! | WASI | [`__wasi_random_get`][17] +//! By default, `getrandom` will not compile on unsupported targets, but certain +//! features allow a user to select a "fallback" implementation if no supported +//! implementation exists. //! -//! Getrandom doesn't have a blanket implementation for all Unix-like operating -//! systems that reads from `/dev/urandom`. This ensures all supported operating -//! systems are using the recommended interface and respect maximum buffer -//! sizes. +//! All of the below mechanisms only affect unsupported +//! targets. Supported targets will _always_ use their supported implementations. +//! This prevents a crate from overriding a secure source of randomness +//! (either accidentally or intentionally). //! -//! ## Unsupported targets +//! ### RDRAND on x86 +//! +//! *If the `"rdrand"` Cargo feature is enabled*, `getrandom` will fallback to using +//! the [`RDRAND`][18] instruction to get randomness on `no_std` `x86`/`x86_64` +//! targets. This feature has no effect on other CPU architectures. +//! +//! ### WebAssembly support +//! +//! This crate fully supports the +//! [`wasm32-wasi`](https://github.com/CraneStation/wasi) and +//! [`wasm32-unknown-emscripten`](https://www.hellorust.com/setup/emscripten/) +//! targets. However, the `wasm32-unknown-unknown` target is not automatically +//! supported since, from the target name alone, we cannot deduce which +//! JavaScript interface is in use (or if JavaScript is available at all). //! -//! By default, compiling `getrandom` for an unsupported target will result in -//! a compilation error. If you want to build an application which uses `getrandom` -//! for such target, you can either: -//! - Use [`[replace]`][replace] or [`[patch]`][patch] section in your `Cargo.toml` -//! to switch to a custom implementation with a support of your target. -//! - Enable the `dummy` feature to have getrandom use an implementation that always -//! fails at run-time on unsupported targets. +//! Instead, *if the `"js"` Cargo feature is enabled*, this crate will assume +//! that you are building for an environment containing JavaScript, and will +//! call the appropriate methods. Both web browser (main window and Web Workers) +//! and Node.js environments are supported, invoking the methods +//! [described above](#supported-targets) using the +//! [wasm-bindgen](https://github.com/rust-lang/rust-bindgen) toolchain. //! -//! [replace]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-replace-section -//! [patch]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-patch-section +//! This feature has no effect on targets other than `wasm32-unknown-unknown`. //! -//! ## Support for WebAssembly and asm.js +//! ### Custom implementations //! -//! Getrandom supports all of Rust's current `wasm32` targets, and it works with -//! both Node.js and web browsers. The three Emscripten targets -//! `asmjs-unknown-emscripten`, `wasm32-unknown-emscripten`, and -//! `wasm32-experimental-emscripten` use Emscripten's `/dev/random` emulation. -//! The WASI target `wasm32-wasi` uses the [`__wasi_random_get`][17] function -//! defined by the WASI standard. +//! The [`register_custom_getrandom!`] macro allows a user to mark their own +//! function as the backing implementation for [`getrandom`]. See the macro's +//! documentation for more information about writing and registering your own +//! custom implementations. //! -//! Getrandom also supports `wasm32-unknown-unknown` by directly calling -//! JavaScript methods. Rust currently has two ways to do this: [bindgen] and -//! [stdweb]. Getrandom supports using either one by enabling the -//! `wasm-bindgen` or `stdweb` crate features. Note that if both features are -//! enabled, `wasm-bindgen` will be used. If neither feature is enabled, calls -//! to `getrandom` will always fail at runtime. +//! Note that registering a custom implementation only has an effect on targets +//! that would otherwise not compile. Any supported targets (including those +//! using `"rdrand"` and `"js"` Cargo features) continue using their normal +//! implementations even if a function is registered. //! -//! [bindgen]: https://github.com/rust-lang/rust-bindgen -//! [stdweb]: https://github.com/koute/stdweb +//! ### 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 //! -//! It is possible that early in the boot process the OS hasn't had enough time -//! yet to collect entropy to securely seed its RNG, especially on virtual -//! machines. +//! Sometimes, early in the boot process, the OS has not collected enough +//! entropy to securely seed its RNG. This is especially common on virtual +//! machines, where standard "random" events are hard to come by. //! -//! Some operating systems always block the thread until the RNG is securely +//! Some operating system interfaces always block until the RNG is securely //! seeded. This can take anywhere from a few seconds to more than a minute. -//! Others make a best effort to use a seed from before the shutdown and don't -//! document much. -//! -//! A few, Linux, NetBSD and Solaris, offer a choice between blocking and -//! getting an error; in these cases we always choose to block. +//! A few (Linux, NetBSD and Solaris) offer a choice between blocking and +//! getting an error; in these cases, we always choose to block. //! -//! On Linux (when the `getrandom` system call is not available) and on NetBSD -//! reading from `/dev/urandom` never blocks, even when the OS hasn't collected -//! enough entropy yet. To avoid returning low-entropy bytes, we first read from +//! On Linux (when the `getrandom` system call is not available), reading from +//! `/dev/urandom` never blocks, even when the OS hasn't collected enough +//! entropy yet. To avoid returning low-entropy bytes, we first poll //! `/dev/random` and only switch to `/dev/urandom` once this has succeeded. //! -//! # Error handling +//! ## Error handling //! //! We always choose failure over returning insecure "random" bytes. In general, //! on supported platforms, failure is highly unlikely, though not impossible. @@ -96,12 +118,9 @@ //! `getrandom`, hence after the first successful call one can be reasonably //! confident that no errors will occur. //! -//! On unsupported platforms, `getrandom` always fails. See the [`Error`] type -//! for more information on what data is returned on failure. -//! //! [1]: http://man7.org/linux/man-pages/man2/getrandom.2.html //! [2]: http://man7.org/linux/man-pages/man4/urandom.4.html -//! [3]: https://docs.microsoft.com/en-us/windows/desktop/api/ntsecapi/nf-ntsecapi-rtlgenrandom +//! [3]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom //! [4]: https://developer.apple.com/documentation/security/1399291-secrandomcopybytes?language=objc //! [5]: https://www.freebsd.org/cgi/man.cgi?query=random&sektion=4 //! [6]: https://man.openbsd.org/getentropy.2 @@ -111,153 +130,88 @@ //! [10]: https://docs.oracle.com/cd/E86824_01/html/E54777/random-7d.html //! [11]: https://fuchsia.dev/fuchsia-src/zircon/syscalls/cprng_draw //! [12]: https://github.com/redox-os/randd/blob/master/src/main.rs -//! [13]: https://github.com/nuxinl/cloudabi#random_get //! [14]: https://www.w3.org/TR/WebCryptoAPI/#Crypto-method-getRandomValues //! [15]: https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback -//! [16]: #support-for-webassembly-and-asmjs +//! [16]: #webassembly-support //! [17]: https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md#__wasi_random_get //! [18]: https://software.intel.com/en-us/articles/intel-digital-random-number-generator-drng-software-implementation-guide //! [19]: https://www.unix.com/man-page/mojave/2/getentropy/ //! [20]: https://www.unix.com/man-page/mojave/4/random/ //! [21]: https://www.freebsd.org/cgi/man.cgi?query=getrandom&manpath=FreeBSD+12.0-stable -//! [22]: https://github.com/hermitcore/libhermit-rs/blob/09c38b0371cee6f56a541400ba453e319e43db53/src/syscalls/random.rs#L21 #![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://rust-random.github.io/rand/" + html_root_url = "https://docs.rs/getrandom/0.2.2" )] #![no_std] -#![cfg_attr(feature = "stdweb", recursion_limit = "128")] #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] +#![cfg_attr(docsrs, feature(doc_cfg))] #[macro_use] extern crate cfg_if; -cfg_if! { - if #[cfg(feature = "log")] { - #[allow(unused)] - #[macro_use] - extern crate log; - } else { - #[allow(unused)] - macro_rules! error { - ($($x:tt)*) => {}; - } - #[allow(unused)] - macro_rules! warn { - ($($x:tt)*) => {}; - } - #[allow(unused)] - macro_rules! info { - ($($x:tt)*) => {}; - } - } -} - mod error; -pub use crate::error::Error; - -#[allow(dead_code)] mod util; +// To prevent a breaking change when targets are added, we always export the +// register_custom_getrandom macro, so old Custom RNG crates continue to build. +#[cfg(feature = "custom")] +mod custom; +#[cfg(feature = "std")] +mod error_impls; -#[cfg(target_os = "vxworks")] -#[allow(dead_code)] -mod util_libc; - -cfg_if! { - // Unlike the other Unix, Fuchsia and iOS don't use the libc to make any calls. - if #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "emscripten", - target_os = "freebsd", target_os = "haiku", target_os = "illumos", - target_os = "linux", target_os = "macos", target_os = "netbsd", - target_os = "openbsd", target_os = "redox", target_os = "solaris"))] { - #[allow(dead_code)] - mod util_libc; - // Keep std-only trait definitions for backwards compatibility - mod error_impls; - } else if #[cfg(feature = "std")] { - mod error_impls; - } -} - -// These targets read from a file as a fallback method. -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos", - target_os = "solaris", - target_os = "illumos", -))] -mod use_file; +pub use crate::error::Error; // System-specific implementations. // // These should all provide getrandom_inner with the same signature as getrandom. cfg_if! { - if #[cfg(target_os = "android")] { - #[path = "linux_android.rs"] mod imp; - } else if #[cfg(target_os = "cloudabi")] { - #[path = "cloudabi.rs"] mod imp; - } else if #[cfg(target_os = "dragonfly")] { - #[path = "use_file.rs"] mod imp; - } else if #[cfg(target_os = "emscripten")] { + if #[cfg(any(target_os = "dragonfly", target_os = "emscripten", + target_os = "haiku", target_os = "redox"))] { + mod util_libc; #[path = "use_file.rs"] mod imp; - } else if #[cfg(target_os = "freebsd")] { + } else if #[cfg(any(target_os = "android", target_os = "linux"))] { + mod util_libc; + mod use_file; + #[path = "linux_android.rs"] mod imp; + } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] { + mod util_libc; + mod use_file; + #[path = "solaris_illumos.rs"] mod imp; + } else if #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] { + mod util_libc; #[path = "bsd_arandom.rs"] mod imp; } else if #[cfg(target_os = "fuchsia")] { #[path = "fuchsia.rs"] mod imp; - } else if #[cfg(target_os = "haiku")] { - #[path = "use_file.rs"] mod imp; - } else if #[cfg(target_os = "illumos")] { - #[path = "solaris_illumos.rs"] mod imp; } else if #[cfg(target_os = "ios")] { #[path = "ios.rs"] mod imp; - } else if #[cfg(target_os = "linux")] { - #[path = "linux_android.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 = "netbsd")] { - #[path = "bsd_arandom.rs"] mod imp; } else if #[cfg(target_os = "openbsd")] { + mod util_libc; #[path = "openbsd.rs"] mod imp; - } else if #[cfg(target_os = "redox")] { - #[path = "use_file.rs"] mod imp; - } else if #[cfg(target_os = "solaris")] { - #[path = "solaris_illumos.rs"] mod imp; } else if #[cfg(target_os = "wasi")] { #[path = "wasi.rs"] mod imp; } else if #[cfg(target_os = "vxworks")] { + mod util_libc; #[path = "vxworks.rs"] mod imp; - } else if #[cfg(all(windows, getrandom_uwp))] { - #[path = "windows_uwp.rs"] mod imp; } else if #[cfg(windows)] { #[path = "windows.rs"] mod imp; - } else if #[cfg(all(target_arch = "x86_64", any( - target_os = "hermit", - target_os = "l4re", - target_os = "uefi", - target_env = "sgx", - )))] { + } else if #[cfg(all(target_arch = "x86_64", target_env = "sgx"))] { + #[path = "rdrand.rs"] mod imp; + } else if #[cfg(all(feature = "rdrand", + any(target_arch = "x86_64", target_arch = "x86")))] { #[path = "rdrand.rs"] mod imp; - } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - cfg_if! { - if #[cfg(feature = "wasm-bindgen")] { - #[path = "wasm32_bindgen.rs"] mod imp; - } else if #[cfg(feature = "stdweb")] { - #[path = "wasm32_stdweb.rs"] mod imp; - } else { - // Always have an implementation for wasm32-unknown-unknown. - // See https://github.com/rust-random/getrandom/issues/87 - #[path = "dummy.rs"] mod imp; - } - } - } else if #[cfg(feature = "dummy")] { - #[path = "dummy.rs"] mod imp; + } else if #[cfg(all(feature = "js", + target_arch = "wasm32", target_os = "unknown"))] { + #[path = "js.rs"] mod imp; + } else if #[cfg(feature = "custom")] { + use custom as imp; } else { - compile_error!("\ - target is not supported, for more information see: \ - https://docs.rs/getrandom/#unsupported-targets\ - "); + compile_error!("target is not supported, for more information see: \ + https://docs.rs/getrandom/#unsupported-targets"); } } @@ -274,7 +228,7 @@ 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). -pub fn getrandom(dest: &mut [u8]) -> Result<(), error::Error> { +pub fn getrandom(dest: &mut [u8]) -> Result<(), Error> { if dest.is_empty() { return Ok(()); } diff --git a/src/linux_android.rs b/src/linux_android.rs index a29feb5..5508fdd 100644 --- a/src/linux_android.rs +++ b/src/linux_android.rs @@ -7,9 +7,11 @@ // except according to those terms. //! Implementation for Linux / Android -use crate::util::LazyBool; -use crate::util_libc::{last_os_error, sys_fill_exact}; -use crate::{use_file, Error}; +use crate::{ + util::LazyBool, + util_libc::{last_os_error, sys_fill_exact}, + {use_file, Error}, +}; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { static HAS_GETRANDOM: LazyBool = LazyBool::new(); diff --git a/src/macos.rs b/src/macos.rs index c3bc533..585a35a 100644 --- a/src/macos.rs +++ b/src/macos.rs @@ -7,8 +7,11 @@ // except according to those terms. //! Implementation for macOS -use crate::util_libc::{last_os_error, Weak}; -use crate::{use_file, Error}; +use crate::{ + use_file, + util_libc::{last_os_error, Weak}, + Error, +}; use core::mem; type GetEntropyFn = unsafe extern "C" fn(*mut u8, libc::size_t) -> libc::c_int; @@ -20,9 +23,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { for chunk in dest.chunks_mut(256) { let ret = unsafe { func(chunk.as_mut_ptr(), chunk.len()) }; if ret != 0 { - let err = last_os_error(); - error!("getentropy syscall failed"); - return Err(err); + return Err(last_os_error()); } } Ok(()) diff --git a/src/openbsd.rs b/src/openbsd.rs index e1ac179..c8d28b3 100644 --- a/src/openbsd.rs +++ b/src/openbsd.rs @@ -7,16 +7,13 @@ // except according to those terms. //! Implementation for OpenBSD -use crate::util_libc::last_os_error; -use crate::Error; +use crate::{util_libc::last_os_error, Error}; pub fn getrandom_inner(dest: &mut [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 { - let err = last_os_error(); - error!("libc::getentropy call failed"); - return Err(err); + return Err(last_os_error()); } } Ok(()) diff --git a/src/rdrand.rs b/src/rdrand.rs index e441682..1df21e5 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -7,24 +7,30 @@ // except according to those terms. //! Implementation for SGX using RDRAND instruction -use crate::error::{FAILED_RDRAND, NO_RDRAND}; -#[cfg(not(target_feature = "rdrand"))] -use crate::util::LazyBool; use crate::Error; -use core::arch::x86_64::_rdrand64_step; use core::mem; +cfg_if! { + if #[cfg(target_arch = "x86_64")] { + use core::arch::x86_64 as arch; + use arch::_rdrand64_step as rdrand_step; + } else if #[cfg(target_arch = "x86")] { + use core::arch::x86 as arch; + use arch::_rdrand32_step as rdrand_step; + } +} + // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. const RETRY_LIMIT: usize = 10; -const WORD_SIZE: usize = mem::size_of::(); +const WORD_SIZE: usize = mem::size_of::(); #[target_feature(enable = "rdrand")] unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { for _ in 0..RETRY_LIMIT { let mut el = mem::zeroed(); - if _rdrand64_step(&mut el) == 1 { + 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 @@ -33,11 +39,10 @@ unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { if el != 0 && el != !0 { return Ok(el.to_ne_bytes()); } - error!("RDRAND returned {:X}, CPU RNG may be broken", el); // Keep looping in case this was a false positive. } } - Err(FAILED_RDRAND) + Err(Error::FAILED_RDRAND) } // "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. @@ -55,16 +60,18 @@ fn is_rdrand_supported() -> bool { // https://github.com/rust-lang-nursery/stdsimd/issues/464 #[cfg(not(target_feature = "rdrand"))] fn is_rdrand_supported() -> bool { - use core::arch::x86_64::__cpuid; - // SAFETY: All x86_64 CPUs support CPUID leaf 1 + use crate::util::LazyBool; + + // SAFETY: All Rust x86 targets are new enough to have CPUID, and if CPUID + // is supported, CPUID leaf 1 is always supported. const FLAG: u32 = 1 << 30; static HAS_RDRAND: LazyBool = LazyBool::new(); - HAS_RDRAND.unsync_init(|| unsafe { (__cpuid(1).ecx & FLAG) != 0 }) + HAS_RDRAND.unsync_init(|| unsafe { (arch::__cpuid(1).ecx & FLAG) != 0 }) } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { if !is_rdrand_supported() { - return Err(NO_RDRAND); + return Err(Error::NO_RDRAND); } // SAFETY: After this point, rdrand is supported, so calling the rdrand diff --git a/src/solaris_illumos.rs b/src/solaris_illumos.rs index 9473123..2d1b767 100644 --- a/src/solaris_illumos.rs +++ b/src/solaris_illumos.rs @@ -17,8 +17,11 @@ //! To make sure we can compile on both Solaris and its derivatives, as well as //! function, we check for the existence of getrandom(2) in libc by calling //! libc::dlsym. -use crate::util_libc::{sys_fill_exact, Weak}; -use crate::{use_file, Error}; +use crate::{ + use_file, + util_libc::{sys_fill_exact, Weak}, + Error, +}; use core::mem; #[cfg(target_os = "illumos")] diff --git a/src/use_file.rs b/src/use_file.rs index 6e50955..465c069 100644 --- a/src/use_file.rs +++ b/src/use_file.rs @@ -7,11 +7,15 @@ // except according to those terms. //! Implementations that just need to read from a file -use crate::util::LazyUsize; -use crate::util_libc::{open_readonly, sys_fill_exact}; -use crate::Error; -use core::cell::UnsafeCell; -use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; +use crate::{ + util::LazyUsize, + util_libc::{open_readonly, sys_fill_exact}, + Error, +}; +use core::{ + cell::UnsafeCell, + sync::atomic::{AtomicUsize, Ordering::Relaxed}, +}; #[cfg(target_os = "redox")] const FILE_PATH: &str = "rand:\0"; @@ -99,7 +103,7 @@ fn wait_until_rng_ready() -> Result<(), Error> { // A negative timeout means an infinite timeout. let res = unsafe { libc::poll(&mut pfd, 1, -1) }; if res >= 0 { - assert_eq!(res, 1); // We only used one fd, and cannot timeout. + debug_assert_eq!(res, 1); // We only used one fd, and cannot timeout. return Ok(()); } let err = crate::util_libc::last_os_error(); diff --git a/src/util.rs b/src/util.rs index 8dbd8ae..06e23c2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,7 +5,7 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. - +#![allow(dead_code)] use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; // This structure represents a lazily initialized static usize value. Useful diff --git a/src/util_libc.rs b/src/util_libc.rs index 1cdc13e..6823609 100644 --- a/src/util_libc.rs +++ b/src/util_libc.rs @@ -5,11 +5,9 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::error::ERRNO_NOT_POSITIVE; -use crate::util::LazyUsize; -use crate::Error; -use core::num::NonZeroU32; -use core::ptr::NonNull; +#![allow(dead_code)] +use crate::{util::LazyUsize, Error}; +use core::{num::NonZeroU32, ptr::NonNull}; cfg_if! { if #[cfg(any(target_os = "netbsd", target_os = "openbsd", target_os = "android"))] { @@ -42,7 +40,7 @@ pub fn last_os_error() -> Error { if errno > 0 { Error::from(NonZeroU32::new(errno as u32).unwrap()) } else { - ERRNO_NOT_POSITIVE + Error::ERRNO_NOT_POSITIVE } } @@ -108,14 +106,10 @@ cfg_if! { // SAFETY: path must be null terminated, FD must be manually closed. pub unsafe fn open_readonly(path: &str) -> Result { - debug_assert!(path.as_bytes().last() == Some(&0)); + 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()); } - // O_CLOEXEC works on all Unix targets except for older Linux kernels (pre - // 2.6.23), so we also use an ioctl to make sure FD_CLOEXEC is set. - #[cfg(target_os = "linux")] - libc::ioctl(fd, libc::FIOCLEX); Ok(fd) } diff --git a/src/vxworks.rs b/src/vxworks.rs index a2fe52a..6cb5d52 100644 --- a/src/vxworks.rs +++ b/src/vxworks.rs @@ -7,8 +7,7 @@ // except according to those terms. //! Implementation for VxWorks -use crate::error::{Error, RAND_SECURE_FATAL}; -use crate::util_libc::last_os_error; +use crate::{util_libc::last_os_error, Error}; use core::sync::atomic::{AtomicBool, Ordering::Relaxed}; pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { @@ -16,7 +15,7 @@ pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { while !RNG_INIT.load(Relaxed) { let ret = unsafe { libc::randSecure() }; if ret < 0 { - return Err(RAND_SECURE_FATAL); + return Err(Error::VXWORKS_RAND_SECURE); } else if ret > 0 { RNG_INIT.store(true, Relaxed); break; diff --git a/src/wasm32_bindgen.rs b/src/wasm32_bindgen.rs deleted file mode 100644 index 86839a0..0000000 --- a/src/wasm32_bindgen.rs +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via wasm-bindgen -extern crate std; - -use core::cell::RefCell; -use core::mem; -use std::thread_local; - -use wasm_bindgen::prelude::*; - -use crate::error::{BINDGEN_CRYPTO_UNDEF, BINDGEN_GRV_UNDEF}; -use crate::Error; - -#[derive(Clone, Debug)] -enum RngSource { - Node(NodeCrypto), - Browser(BrowserCrypto), -} - -// JsValues are always per-thread, so we initialize RngSource for each thread. -// See: https://github.com/rustwasm/wasm-bindgen/pull/955 -thread_local!( - static RNG_SOURCE: RefCell> = RefCell::new(None); -); - -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - assert_eq!(mem::size_of::(), 4); - - RNG_SOURCE.with(|f| { - let mut source = f.borrow_mut(); - if source.is_none() { - *source = Some(getrandom_init()?); - } - - match source.as_ref().unwrap() { - RngSource::Node(n) => n.random_fill_sync(dest), - RngSource::Browser(n) => { - // see https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues - // - // where it says: - // - // > A QuotaExceededError DOMException is thrown if the - // > requested length is greater than 65536 bytes. - for chunk in dest.chunks_mut(65536) { - n.get_random_values(chunk) - } - } - }; - Ok(()) - }) -} - -fn getrandom_init() -> Result { - if let Ok(self_) = Global::get_self() { - // If `self` is defined then we're in a browser somehow (main window - // or web worker). Here we want to try to use - // `crypto.getRandomValues`, but if `crypto` isn't defined we assume - // we're in an older web browser and the OS RNG isn't available. - - let crypto = self_.crypto(); - if crypto.is_undefined() { - return Err(BINDGEN_CRYPTO_UNDEF); - } - - // Test if `crypto.getRandomValues` is undefined as well - let crypto: BrowserCrypto = crypto.into(); - if crypto.get_random_values_fn().is_undefined() { - return Err(BINDGEN_GRV_UNDEF); - } - - return Ok(RngSource::Browser(crypto)); - } - - return Ok(RngSource::Node(node_require("crypto"))); -} - -#[wasm_bindgen] -extern "C" { - type Global; - #[wasm_bindgen(getter, catch, static_method_of = Global, js_class = self, js_name = self)] - fn get_self() -> Result; - - type Self_; - #[wasm_bindgen(method, getter, structural)] - fn crypto(me: &Self_) -> JsValue; - - #[derive(Clone, Debug)] - type BrowserCrypto; - - // TODO: these `structural` annotations here ideally wouldn't be here to - // avoid a JS shim, but for now with feature detection they're - // unavoidable. - #[wasm_bindgen(method, js_name = getRandomValues, structural, getter)] - fn get_random_values_fn(me: &BrowserCrypto) -> JsValue; - #[wasm_bindgen(method, js_name = getRandomValues, structural)] - fn get_random_values(me: &BrowserCrypto, buf: &mut [u8]); - - #[wasm_bindgen(js_name = require)] - fn node_require(s: &str) -> NodeCrypto; - - #[derive(Clone, Debug)] - type NodeCrypto; - - #[wasm_bindgen(method, js_name = randomFillSync, structural)] - fn random_fill_sync(me: &NodeCrypto, buf: &mut [u8]); -} diff --git a/src/wasm32_stdweb.rs b/src/wasm32_stdweb.rs deleted file mode 100644 index 6e5e78a..0000000 --- a/src/wasm32_stdweb.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for WASM via stdweb -extern crate std; - -use core::mem; - -use stdweb::js; -use stdweb::unstable::TryInto; -use stdweb::web::error::Error as WebError; - -use crate::error::{STDWEB_NO_RNG, STDWEB_RNG_FAILED}; -use crate::Error; -use std::sync::Once; - -#[derive(Clone, Copy, Debug)] -enum RngSource { - Browser, - Node, -} - -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - assert_eq!(mem::size_of::(), 4); - static ONCE: Once = Once::new(); - static mut RNG_SOURCE: Result = Ok(RngSource::Node); - - // SAFETY: RNG_SOURCE is only written once, before being read. - ONCE.call_once(|| unsafe { - RNG_SOURCE = getrandom_init(); - }); - getrandom_fill(unsafe { RNG_SOURCE }?, dest) -} - -fn getrandom_init() -> Result { - let result = js! { - try { - if ( - typeof self === "object" && - typeof self.crypto === "object" && - typeof self.crypto.getRandomValues === "function" - ) { - return { success: true, ty: 1 }; - } - - if (typeof require("crypto").randomBytes === "function") { - return { success: true, ty: 2 }; - } - - return { success: false, error: new Error("not supported") }; - } catch(err) { - return { success: false, error: err }; - } - }; - - if js! { return @{ result.as_ref() }.success } == true { - let ty = js! { return @{ result }.ty }; - - if ty == 1 { - Ok(RngSource::Browser) - } else if ty == 2 { - Ok(RngSource::Node) - } else { - unreachable!() - } - } else { - let _err: WebError = js! { return @{ result }.error }.try_into().unwrap(); - error!("getrandom unavailable: {}", _err); - Err(STDWEB_NO_RNG) - } -} - -fn getrandom_fill(source: RngSource, dest: &mut [u8]) -> Result<(), Error> { - for chunk in dest.chunks_mut(65536) { - let len = chunk.len() as u32; - let ptr = chunk.as_mut_ptr() as i32; - - let result = match source { - RngSource::Browser => js! { - try { - let array = new Uint8Array(@{ len }); - self.crypto.getRandomValues(array); - HEAPU8.set(array, @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - }, - RngSource::Node => js! { - try { - let bytes = require("crypto").randomBytes(@{ len }); - HEAPU8.set(new Uint8Array(bytes), @{ ptr }); - - return { success: true }; - } catch(err) { - return { success: false, error: err }; - } - }, - }; - - if js! { return @{ result.as_ref() }.success } != true { - let _err: WebError = js! { return @{ result }.error }.try_into().unwrap(); - error!("getrandom failed: {}", _err); - return Err(STDWEB_RNG_FAILED); - } - } - Ok(()) -} diff --git a/src/windows.rs b/src/windows.rs index e1b8df6..56b3d07 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -6,20 +6,41 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Implementation for Windows -use crate::{error::RTL_GEN_RANDOM_FAILED, Error}; +use crate::Error; +use core::{ffi::c_void, num::NonZeroU32, ptr}; + +const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; extern "system" { - #[link_name = "SystemFunction036"] - fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: u32) -> u8; + fn BCryptGenRandom( + hAlgorithm: *mut c_void, + pBuffer: *mut u8, + cbBuffer: u32, + dwFlags: u32, + ) -> u32; } pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { // Prevent overflow of u32 for chunk in dest.chunks_mut(u32::max_value() as usize) { - let ret = unsafe { RtlGenRandom(chunk.as_mut_ptr(), chunk.len() as u32) }; - if ret == 0 { - return Err(RTL_GEN_RANDOM_FAILED); + let ret = unsafe { + BCryptGenRandom( + ptr::null_mut(), + chunk.as_mut_ptr(), + chunk.len() as u32, + BCRYPT_USE_SYSTEM_PREFERRED_RNG, + ) + }; + // NTSTATUS codes use the two highest bits for severity status. + if ret >> 30 == 0b11 { + // We zeroize the highest bit, so the error code will reside + // inside the range designated for OS codes. + let code = ret ^ (1 << 31); + // SAFETY: the second highest bit is always equal to one, + // so it's impossible to get zero. Unfortunately the type + // system does not have a way to express this yet. + let code = unsafe { NonZeroU32::new_unchecked(code) }; + return Err(Error::from(code)); } } Ok(()) diff --git a/src/windows_uwp.rs b/src/windows_uwp.rs deleted file mode 100644 index 586c6f6..0000000 --- a/src/windows_uwp.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2018 Developers of the Rand project. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation for Windows UWP targets. After deprecation of Windows XP -//! and Vista, this can supersede the `RtlGenRandom`-based implementation. -use crate::Error; -use core::{ffi::c_void, num::NonZeroU32, ptr}; - -const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002; - -extern "system" { - fn BCryptGenRandom( - hAlgorithm: *mut c_void, - pBuffer: *mut u8, - cbBuffer: u32, - dwFlags: u32, - ) -> u32; -} - -pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { - // Prevent overflow of u32 - for chunk in dest.chunks_mut(u32::max_value() as usize) { - let ret = unsafe { - BCryptGenRandom( - ptr::null_mut(), - chunk.as_mut_ptr(), - chunk.len() as u32, - BCRYPT_USE_SYSTEM_PREFERRED_RNG, - ) - }; - // NTSTATUS codes use two highest bits for severity status - match ret >> 30 { - 0b01 => { - info!("BCryptGenRandom: information code 0x{:08X}", ret); - } - 0b10 => { - warn!("BCryptGenRandom: warning code 0x{:08X}", ret); - } - 0b11 => { - error!("BCryptGenRandom: failed with 0x{:08X}", ret); - // We zeroize the highest bit, so the error code will reside - // inside the range of designated for OS codes. - let code = ret ^ (1 << 31); - // SAFETY: the second highest bit is always equal to one, - // so it's impossible to get zero. Unfortunately compiler - // is not smart enough to figure out it yet. - let code = unsafe { NonZeroU32::new_unchecked(code) }; - return Err(Error::from(code)); - } - _ => (), - } - } - Ok(()) -} diff --git a/tests/common.rs b/tests/common.rs deleted file mode 100644 index afefa03..0000000 --- a/tests/common.rs +++ /dev/null @@ -1,68 +0,0 @@ -#[cfg(feature = "wasm-bindgen")] -use wasm_bindgen_test::*; - -use getrandom::getrandom; - -#[cfg(feature = "test-in-browser")] -wasm_bindgen_test_configure!(run_in_browser); - -#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] -#[test] -fn test_zero() { - // Test that APIs are happy with zero-length requests - getrandom(&mut [0u8; 0]).unwrap(); -} - -#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] -#[test] -fn test_diff() { - let mut v1 = [0u8; 1000]; - getrandom(&mut v1).unwrap(); - - let mut v2 = [0u8; 1000]; - getrandom(&mut v2).unwrap(); - - let mut n_diff_bits = 0; - for i in 0..v1.len() { - n_diff_bits += (v1[i] ^ v2[i]).count_ones(); - } - - // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input. - assert!(n_diff_bits >= v1.len() as u32); -} - -#[cfg_attr(feature = "wasm-bindgen", wasm_bindgen_test)] -#[test] -fn test_huge() { - let mut huge = [0u8; 100_000]; - getrandom(&mut huge).unwrap(); -} - -#[cfg(any(unix, windows, target_os = "redox", target_os = "fuchsia"))] -#[test] -fn test_multithreading() { - use std::sync::mpsc::channel; - use std::thread; - - let mut txs = vec![]; - for _ in 0..20 { - let (tx, rx) = channel(); - txs.push(tx); - - thread::spawn(move || { - // wait until all the tasks are ready to go. - rx.recv().unwrap(); - let mut v = [0u8; 1000]; - - for _ in 0..100 { - getrandom(&mut v).unwrap(); - thread::yield_now(); - } - }); - } - - // start all the tasks - for tx in txs.iter() { - tx.send(()).unwrap(); - } -} diff --git a/tests/common/mod.rs b/tests/common/mod.rs new file mode 100644 index 0000000..006f230 --- /dev/null +++ b/tests/common/mod.rs @@ -0,0 +1,66 @@ +use super::getrandom_impl; + +#[cfg(all(target_arch = "wasm32", target_os = "unknown"))] +use wasm_bindgen_test::wasm_bindgen_test as test; + +#[cfg(feature = "test-in-browser")] +wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); + +#[test] +fn test_zero() { + // Test that APIs are happy with zero-length requests + getrandom_impl(&mut [0u8; 0]).unwrap(); +} + +#[test] +fn test_diff() { + let mut v1 = [0u8; 1000]; + getrandom_impl(&mut v1).unwrap(); + + 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(); + } + + // Check at least 1 bit per byte differs. p(failure) < 1e-1000 with random input. + assert!(n_diff_bits >= v1.len() as u32); +} + +#[test] +fn test_huge() { + let mut huge = [0u8; 100_000]; + getrandom_impl(&mut huge).unwrap(); +} + +// On WASM, the thread API always fails/panics +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn test_multithreading() { + extern crate std; + use std::{sync::mpsc::channel, thread, vec}; + + let mut txs = vec![]; + for _ in 0..20 { + let (tx, rx) = channel(); + txs.push(tx); + + thread::spawn(move || { + // wait until all the tasks are ready to go. + rx.recv().unwrap(); + let mut v = [0u8; 1000]; + + for _ in 0..100 { + getrandom_impl(&mut v).unwrap(); + thread::yield_now(); + } + }); + } + + // start all the tasks + for tx in txs.iter() { + tx.send(()).unwrap(); + } +} diff --git a/tests/custom.rs b/tests/custom.rs new file mode 100644 index 0000000..62eae1d --- /dev/null +++ b/tests/custom.rs @@ -0,0 +1,50 @@ +// Test that a custom handler works on wasm32-unknown-unknown +#![cfg(all( + target_arch = "wasm32", + target_os = "unknown", + feature = "custom", + not(feature = "js") +))] + +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 getrandom::{getrandom, register_custom_getrandom, Error}; + +fn len7_err() -> Error { + NonZeroU32::new(Error::INTERNAL_START + 7).unwrap().into() +} + +fn super_insecure_rng(buf: &mut [u8]) -> Result<(), Error> { + // 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); + for b in buf { + *b = COUNTER.fetch_add(1, Ordering::Relaxed); + } + Ok(()) +} + +register_custom_getrandom!(super_insecure_rng); + +#[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!(getrandom(&mut buf), Ok(())); + assert_eq!(buf, [4, 5, 6, 7]); +} + +#[test] +fn rng_err_output() { + assert_eq!(getrandom(&mut [0; 7]), Err(len7_err())); +} diff --git a/tests/normal.rs b/tests/normal.rs new file mode 100644 index 0000000..5fff13b --- /dev/null +++ b/tests/normal.rs @@ -0,0 +1,11 @@ +// Don't test on custom wasm32-unknown-unknown +#![cfg(not(all( + target_arch = "wasm32", + target_os = "unknown", + feature = "custom", + not(feature = "js") +)))] + +// Use the normal getrandom implementation on this architecture. +use getrandom::getrandom as getrandom_impl; +mod common; diff --git a/tests/rdrand.rs b/tests/rdrand.rs new file mode 100644 index 0000000..4ff85c4 --- /dev/null +++ b/tests/rdrand.rs @@ -0,0 +1,15 @@ +// We only test the RDRAND-based RNG source on supported architectures. +#![cfg(any(target_arch = "x86_64", target_arch = "x86"))] + +// rdrand.rs expects to be part of the getrandom main crate, so we need these +// additional imports to get rdrand.rs to compile. +use getrandom::Error; +#[macro_use] +extern crate cfg_if; +#[path = "../src/rdrand.rs"] +mod rdrand; +#[path = "../src/util.rs"] +mod util; + +use rdrand::getrandom_inner as getrandom_impl; +mod common; -- cgit v1.2.3