Skip to content

Commit e7eb146

Browse files
committed
Add efi_rng opt-in backend
1 parent ce3b017 commit e7eb146

File tree

6 files changed

+153
-1
lines changed

6 files changed

+153
-1
lines changed

.github/workflows/build.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ jobs:
166166
- name: Build
167167
run: cargo build --target ${{ matrix.target.target }} ${{ matrix.feature.feature }} -Zbuild-std=${{ matrix.feature.build-std }}
168168

169+
efi-rng:
170+
name: UEFI RNG Protocol
171+
runs-on: ubuntu-24.04
172+
strategy:
173+
matrix:
174+
target: [
175+
aarch64-unknown-uefi,
176+
x86_64-unknown-uefi,
177+
i686-unknown-uefi,
178+
]
179+
steps:
180+
- uses: actions/checkout@v4
181+
- uses: dtolnay/rust-toolchain@nightly # Required to build libstd
182+
with:
183+
components: rust-src
184+
- uses: Swatinem/rust-cache@v2
185+
- env:
186+
RUSTFLAGS: -Dwarnings --cfg getrandom_backend="efi_rng"
187+
run: cargo build -Z build-std=std --target=${{ matrix.target }} --features std
188+
169189
rdrand-uefi:
170190
name: RDRAND UEFI
171191
runs-on: ubuntu-24.04

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ libc = { version = "0.2.154", default-features = false }
3838
[target.'cfg(any(target_os = "ios", target_os = "visionos", target_os = "watchos", target_os = "tvos"))'.dependencies]
3939
libc = { version = "0.2.154", default-features = false }
4040

41+
# efi_rng
42+
[target.'cfg(all(target_os = "uefi", getrandom_backend = "efi_rng"))'.dependencies]
43+
r-efi = { version = "5.1", default-features = false }
44+
4145
# getentropy
4246
[target.'cfg(any(target_os = "macos", target_os = "openbsd", target_os = "vita", target_os = "emscripten"))'.dependencies]
4347
libc = { version = "0.2.154", default-features = false }
@@ -81,7 +85,7 @@ wasm-bindgen-test = "0.3"
8185
[lints.rust.unexpected_cfgs]
8286
level = "warn"
8387
check-cfg = [
84-
'cfg(getrandom_backend, values("custom", "rdrand", "rndr", "linux_getrandom", "wasm_js"))',
88+
'cfg(getrandom_backend, values("custom", "efi_rng", "rdrand", "rndr", "linux_getrandom", "wasm_js"))',
8589
'cfg(getrandom_msan)',
8690
'cfg(getrandom_test_linux_fallback)',
8791
'cfg(getrandom_test_netbsd_fallback)',

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ of randomness based on their specific needs:
8383
| `rdrand` | x86, x86-64 | `x86_64-*`, `i686-*` | [`RDRAND`] instruction
8484
| `rndr` | AArch64 | `aarch64-*` | [`RNDR`] register
8585
| `wasm_js` | Web Browser, Node.js | `wasm32‑unknown‑unknown`, `wasm32v1-none` | [`Crypto.getRandomValues`]. Requires feature `wasm_js` ([see below](#webassembly-support)).
86+
| `efi_rng` | UEFI | `*-unknown‑uefi` | [`EFI_RNG_PROTOCOL`] with `EFI_RNG_ALGORITHM_RAW` (requires `std` and Nigthly compiler)
8687
| `custom` | All targets | `*` | User-provided custom implementation (see [custom backend])
8788

8889
Opt-in backends can be enabled using the `getrandom_backend` configuration flag.
@@ -351,6 +352,7 @@ dual licensed as above, without any additional terms or conditions.
351352
[`esp_fill_random`]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html#functions
352353
[esp-idf-rng]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/random.html
353354
[esp-trng-docs]: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#rng
355+
[`EFI_RNG_PROTOCOL`]: https://uefi.org/specs/UEFI/2.10/37_Secure_Technologies.html#efi-rng-protocol
354356
[`random_get`]: https://github.com/WebAssembly/WASI/blob/snapshot-01/phases/snapshot/docs.md#-random_getbuf-pointeru8-buf_len-size---errno
355357
[`get-random-u64`]: https://github.com/WebAssembly/WASI/blob/v0.2.1/wasip2/random/random.wit#L23-L28
356358
[configuration flags]: #configuration-flags

src/backends.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ cfg_if! {
1919
} else if #[cfg(getrandom_backend = "rndr")] {
2020
mod rndr;
2121
pub use rndr::*;
22+
} else if #[cfg(getrandom_backend = "efi_rng")] {
23+
mod rdrand;
24+
pub use rdrand::*;
2225
} else if #[cfg(all(getrandom_backend = "wasm_js"))] {
2326
cfg_if! {
2427
if #[cfg(feature = "wasm_js")] {

src/backends/efi_rng.rs

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Implementation for UEFI using EFI_RNG_PROTOCOL
2+
use crate::Error;
3+
use core::{
4+
mem::MaybeUninit,
5+
ptr::{self, null_mut, NonNull},
6+
sync::atomic::{AtomicPtr, Ordering::Relaxed},
7+
};
8+
use r_efi::{
9+
efi::{BootServices, Handle},
10+
protocols::rng,
11+
};
12+
13+
extern crate std;
14+
15+
pub use crate::util::{inner_u32, inner_u64};
16+
17+
#[cfg(not(target_os = "uefi"))]
18+
compile_error!("`efi_rng` backend can be enabled only for UEFI targets!");
19+
20+
static RNG_PROTOCOL: AtomicPtr<rng::Protocol> = AtomicPtr::new(null_mut());
21+
22+
fn init() -> Result<NonNull<rng::Protocol>, Error> {
23+
const HANDLE_SIZE: usize = size_of::<Handle>();
24+
25+
let boot_services = std::os::uefi::env::boot_services()
26+
.ok_or(Error::BOOT_SERVICES_UNAVAILABLE)?
27+
.cast::<BootServices>();
28+
29+
let mut handles = [ptr::null_mut(); 16];
30+
// `locate_handle` operates with length in bytes
31+
let mut buf_size = handles.len() * HANDLE_SIZE;
32+
let mut guid = rng::PROTOCOL_GUID;
33+
let ret = unsafe {
34+
((*boot_services.as_ptr()).locate_handle)(
35+
r_efi::efi::BY_PROTOCOL,
36+
&mut guid,
37+
null_mut(),
38+
&mut buf_size,
39+
handles.as_mut_ptr(),
40+
)
41+
};
42+
43+
if ret.is_error() {
44+
return Err(Error::TEMP_EFI_ERROR);
45+
}
46+
47+
let handles_len = buf_size / HANDLE_SIZE;
48+
let handles = handles.get(..handles_len).ok_or(Error::UNEXPECTED)?;
49+
50+
let system_handle = std::os::uefi::env::image_handle();
51+
for &handle in handles {
52+
let mut protocol: MaybeUninit<*mut rng::Protocol> = MaybeUninit::uninit();
53+
54+
let mut protocol_guid = rng::PROTOCOL_GUID;
55+
let ret = unsafe {
56+
((*boot_services.as_ptr()).open_protocol)(
57+
handle,
58+
&mut protocol_guid,
59+
protocol.as_mut_ptr().cast(),
60+
system_handle.as_ptr(),
61+
ptr::null_mut(),
62+
r_efi::system::OPEN_PROTOCOL_GET_PROTOCOL,
63+
)
64+
};
65+
66+
let protocol = if ret.is_error() {
67+
continue;
68+
} else {
69+
let protocol = unsafe { protocol.assume_init() };
70+
NonNull::new(protocol).ok_or(Error::UNEXPECTED)?
71+
};
72+
73+
// Try to use the acquired protocol handle
74+
let mut buf = [0u8; 8];
75+
let mut alg_guid = rng::ALGORITHM_RAW;
76+
let ret = unsafe {
77+
((*protocol.as_ptr()).get_rng)(
78+
protocol.as_ptr(),
79+
&mut alg_guid,
80+
buf.len(),
81+
buf.as_mut_ptr(),
82+
)
83+
};
84+
85+
if ret.is_error() {
86+
continue;
87+
}
88+
89+
RNG_PROTOCOL.store(protocol.as_ptr(), Relaxed);
90+
return Ok(protocol);
91+
}
92+
Err(Error::NO_RNG_HANDLE)
93+
}
94+
95+
pub fn fill_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
96+
let protocol = match NonNull::new(RNG_PROTOCOL.load(Relaxed)) {
97+
Some(p) => p,
98+
None => init()?,
99+
};
100+
101+
let mut alg_guid = rng::ALGORITHM_RAW;
102+
let ret = unsafe {
103+
((*protocol.as_ptr()).get_rng)(
104+
protocol.as_ptr(),
105+
&mut alg_guid,
106+
dest.len(),
107+
dest.as_mut_ptr().cast::<u8>(),
108+
)
109+
};
110+
111+
if ret.is_error() {
112+
Err(Error::TEMP_EFI_ERROR)
113+
} else {
114+
Ok(())
115+
}
116+
}
117+
118+
impl Error {
119+
pub(crate) const BOOT_SERVICES_UNAVAILABLE: Error = Self::new_internal(10);
120+
pub(crate) const NO_RNG_HANDLE: Error = Self::new_internal(11);
121+
pub(crate) const TEMP_EFI_ERROR: Error = Self::new_internal(12);
122+
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#![doc = include_str!("../README.md")]
1111
#![warn(rust_2018_idioms, unused_lifetimes, missing_docs)]
1212
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
13+
#![cfg_attr(getrandom_backend = "efi_rng", feature(uefi_std))]
1314
#![deny(
1415
clippy::cast_lossless,
1516
clippy::cast_possible_truncation,

0 commit comments

Comments
 (0)