From 1d7165f7434ffc25e4e1fcea33779d0b5d2654b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 18 Dec 2024 18:39:57 +0300 Subject: [PATCH 1/5] Add `efi_rng` opt-in backend --- .github/workflows/build.yml | 20 ++++++ Cargo.toml | 6 +- README.md | 2 + src/backends.rs | 3 + src/backends/efi_rng.rs | 120 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 6 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/backends/efi_rng.rs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a251776..8789f94a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -165,6 +165,26 @@ jobs: - name: Build run: cargo build --target ${{ matrix.target.target }} ${{ matrix.feature.feature }} -Zbuild-std=${{ matrix.feature.build-std }} + efi-rng: + name: UEFI RNG Protocol + runs-on: ubuntu-24.04 + strategy: + matrix: + target: [ + x86_64-unknown-uefi, + i686-unknown-uefi, + ] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly # Required to build libstd + with: + components: rust-src + - uses: Swatinem/rust-cache@v2 + - env: + RUSTFLAGS: -Dwarnings --cfg getrandom_backend="efi_rng" + run: cargo build -Z build-std=std --target=${{ matrix.target }} --features std + + rdrand-uefi: name: RDRAND UEFI runs-on: ubuntu-24.04 diff --git a/Cargo.toml b/Cargo.toml index e8c00a58..39c7f13c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,10 @@ libc = { version = "0.2.154", default-features = false } [target.'cfg(all(any(target_os = "linux", target_os = "android"), any(target_env = "", getrandom_backend = "linux_rustix")))'.dependencies] rustix = { version = "0.38.38", default-features = false, features = ["rand"] } +# efi_rng +[target.'cfg(all(target_os = "uefi", getrandom_backend = "efi_rng"))'.dependencies] +r-efi = { version = "5.1", default-features = false } + # apple-other [target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))'.dependencies] libc = { version = "0.2.154", default-features = false } @@ -80,7 +84,7 @@ rustc-dep-of-std = ["dep:compiler_builtins", "dep:core"] [lints.rust.unexpected_cfgs] level = "warn" check-cfg = [ - 'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "linux_rustix", "wasm_js", "esp_idf"))', + 'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "linux_rustix", "wasm_js", "efi_rng", "esp_idf"))', 'cfg(getrandom_sanitize)', 'cfg(getrandom_test_linux_fallback)', 'cfg(getrandom_test_netbsd_fallback)', diff --git a/README.md b/README.md index ebd1b76a..0c320375 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ of randomness based on their specific needs: | `linux_rustix` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses [`rustix`] instead of `libc`. | `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction | `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register +| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] (requires `std` and Nigthly compiler) | `esp_idf` | ESP-IDF | `*‑espidf` | [`esp_fill_random`]. WARNING: can return low-quality entropy without proper hardware configuration! | `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`] | `custom` | All targets | `*` | User-provided custom implementation (see [custom backend]) @@ -336,6 +337,7 @@ dual licensed as above, without any additional terms or conditions. [`RNDR`]: https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/RNDR--Random-Number [`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 +[`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#efi-rng-protocol [`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/random.html#_CPPv415esp_fill_randomPv6size_t [`random_get`]: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno [`get-random-u64`]: https://github.com/WebAssembly/WASI/blob/v0.2.1/wasip2/random/random.wit#L23-L28 diff --git a/src/backends.rs b/src/backends.rs index f7b720f5..6e7e9c04 100644 --- a/src/backends.rs +++ b/src/backends.rs @@ -25,6 +25,9 @@ cfg_if! { } else if #[cfg(getrandom_backend = "wasm_js")] { mod wasm_js; pub use wasm_js::*; + } else if #[cfg(getrandom_backend = "efi_rng")] { + mod efi_rng; + pub use efi_rng::*; } else if #[cfg(getrandom_backend = "esp_idf")] { mod esp_idf; pub use esp_idf::*; diff --git a/src/backends/efi_rng.rs b/src/backends/efi_rng.rs new file mode 100644 index 00000000..89bdc822 --- /dev/null +++ b/src/backends/efi_rng.rs @@ -0,0 +1,120 @@ +//! Implementation for UEFI using EFI_RNG_PROTOCOL +use crate::Error; +use core::{ + mem::MaybeUninit, + ptr::{self, null_mut, NonNull}, + sync::atomic::{AtomicPtr, Ordering::Relaxed}, +}; +use r_efi::{ + efi::{BootServices, Handle}, + protocols::rng, +}; + +extern crate std; + +pub use crate::util::{inner_u32, inner_u64}; + +#[cfg(not(target_os = "uefi"))] +compile_error!("`efi_rng` backend can be enabled only for UEFI targets!"); + +static RNG_PROTOCOL: AtomicPtr = AtomicPtr::new(null_mut()); + +fn init() -> Result, Error> { + const HANDLE_SIZE: usize = size_of::(); + + let boot_services = std::os::uefi::env::boot_services() + .ok_or(Error::BOOT_SERVICES_UNAVAILABLE)? + .cast::(); + + let mut handles = [ptr::null_mut(); 16]; + // `locate_handle` operates with length in bytes + let mut buf_size = handles.len() * HANDLE_SIZE; + let mut guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).locate_handle)( + r_efi::efi::BY_PROTOCOL, + &mut guid, + null_mut(), + &mut buf_size, + handles.as_mut_ptr(), + ) + }; + + if ret.is_error() { + return Err(Error::TEMP_EFI_ERROR); + } + + let handles_len = buf_size / HANDLE_SIZE; + let handles = handles.get(..handles_len).ok_or(Error::UNEXPECTED)?; + + let system_handle = std::os::uefi::env::image_handle(); + for &handle in handles { + let mut protocol: MaybeUninit<*mut rng::Protocol> = MaybeUninit::uninit(); + + let mut protocol_guid = rng::PROTOCOL_GUID; + let ret = unsafe { + ((*boot_services.as_ptr()).open_protocol)( + handle, + &mut protocol_guid, + protocol.as_mut_ptr().cast(), + system_handle.as_ptr(), + ptr::null_mut(), + r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL, + ) + }; + + let protocol = if ret.is_error() { + continue; + } else { + let protocol = unsafe { protocol.assume_init() }; + NonNull::new(protocol).ok_or(Error::UNEXPECTED)? + }; + + // Try to use the acquired protocol handle + let mut buf = [0u8; 8]; + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + ptr::null_mut(), + buf.len(), + buf.as_mut_ptr(), + ) + }; + + if ret.is_error() { + continue; + } + + RNG_PROTOCOL.store(protocol.as_ptr(), Relaxed); + return Ok(protocol); + } + Err(Error::NO_RNG_HANDLE) +} + +pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { + let protocol = match NonNull::new(RNG_PROTOCOL.load(Relaxed)) { + Some(p) => p, + None => init()?, + }; + + let ret = unsafe { + ((*protocol.as_ptr()).get_rng)( + protocol.as_ptr(), + ptr::null_mut(), + dest.len(), + dest.as_mut_ptr().cast::(), + ) + }; + + if ret.is_error() { + Err(Error::TEMP_EFI_ERROR) + } else { + Ok(()) + } +} + +impl Error { + pub(crate) const BOOT_SERVICES_UNAVAILABLE: Error = Self::new_internal(10); + pub(crate) const NO_RNG_HANDLE: Error = Self::new_internal(11); + pub(crate) const TEMP_EFI_ERROR: Error = Self::new_internal(12); +} diff --git a/src/lib.rs b/src/lib.rs index 5b0f47fd..fe7d0be9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ #![warn(rust_2018_idioms, unused_lifetimes, missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] #![cfg_attr(getrandom_sanitize, feature(cfg_sanitize))] +#![cfg_attr(getrandom_backend = "efi_rng", feature(uefi_std))] #![deny( clippy::cast_lossless, clippy::cast_possible_truncation, From 8ea14dd460ca52dccc69b71a1a61c550d8d6899a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 18 Dec 2024 18:49:13 +0300 Subject: [PATCH 2/5] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36cabb35..7b0526e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `linux_rustix` opt-in backend [#520] - Memory sanitizer support gated behind `getrandom_sanitize` configuration flag [#521] - `u32` and `u64` functions for generating random values of the respective type [#544] +- `efi_rng` opt-in backend [#570] ### Fixed - NetBSD fallback code based on `KERN_ARND` [#555] @@ -61,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [#554]: https://github.com/rust-random/getrandom/pull/554 [#555]: https://github.com/rust-random/getrandom/pull/555 [#557]: https://github.com/rust-random/getrandom/pull/557 +[#570]: https://github.com/rust-random/getrandom/pull/570 ## [0.2.15] - 2024-05-06 ### Added From 6070ef58fe8c4ea677c2a0afb3514db703460826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 18 Dec 2024 18:51:22 +0300 Subject: [PATCH 3/5] tweak readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c320375..d3f8bbce 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ of randomness based on their specific needs: | `linux_rustix` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses [`rustix`] instead of `libc`. | `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction | `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register -| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] (requires `std` and Nigthly compiler) +| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] (requires `std` and Nigthly compiler) | `esp_idf` | ESP-IDF | `*‑espidf` | [`esp_fill_random`]. WARNING: can return low-quality entropy without proper hardware configuration! | `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`] | `custom` | All targets | `*` | User-provided custom implementation (see [custom backend]) From 871e40e2230341ad8a4fea9021c1863b4742b8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 18 Dec 2024 18:52:40 +0300 Subject: [PATCH 4/5] Build AArch64 UEFI --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8789f94a..3325c279 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -171,6 +171,7 @@ jobs: strategy: matrix: target: [ + aarch64-unknown-uefi, x86_64-unknown-uefi, i686-unknown-uefi, ] From 9829ebad0833ade29c6b4ef65d19e18ec210e17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D1=80=D1=82=D1=91=D0=BC=20=D0=9F=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=20=5BArtyom=20Pavlov=5D?= Date: Wed, 18 Dec 2024 19:11:59 +0300 Subject: [PATCH 5/5] Use ALGORITHM_RAW --- README.md | 2 +- src/backends/efi_rng.rs | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d3f8bbce..49ed72fc 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ of randomness based on their specific needs: | `linux_rustix` | Linux, Android | `*‑linux‑*` | Same as `linux_getrandom`, but uses [`rustix`] instead of `libc`. | `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction | `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register -| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] (requires `std` and Nigthly compiler) +| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] with `EFI_RNG_ALGORITHM_RAW` (requires `std` and Nigthly compiler) | `esp_idf` | ESP-IDF | `*‑espidf` | [`esp_fill_random`]. WARNING: can return low-quality entropy without proper hardware configuration! | `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`] | `custom` | All targets | `*` | User-provided custom implementation (see [custom backend]) diff --git a/src/backends/efi_rng.rs b/src/backends/efi_rng.rs index 89bdc822..fb89e59b 100644 --- a/src/backends/efi_rng.rs +++ b/src/backends/efi_rng.rs @@ -72,10 +72,11 @@ fn init() -> Result, Error> { // Try to use the acquired protocol handle let mut buf = [0u8; 8]; + let mut alg_guid = rng::ALGORITHM_RAW; let ret = unsafe { ((*protocol.as_ptr()).get_rng)( protocol.as_ptr(), - ptr::null_mut(), + &mut alg_guid, buf.len(), buf.as_mut_ptr(), ) @@ -97,10 +98,11 @@ pub fn fill_inner(dest: &mut [MaybeUninit]) -> Result<(), Error> { None => init()?, }; + let mut alg_guid = rng::ALGORITHM_RAW; let ret = unsafe { ((*protocol.as_ptr()).get_rng)( protocol.as_ptr(), - ptr::null_mut(), + &mut alg_guid, dest.len(), dest.as_mut_ptr().cast::(), )