diff --git a/book/src/applet/prelude/usb.md b/book/src/applet/prelude/usb.md index d30310dcb..07a673d76 100644 --- a/book/src/applet/prelude/usb.md +++ b/book/src/applet/prelude/usb.md @@ -43,7 +43,7 @@ We then wait until the player presses Enter. We can read a single byte from the {{#include usb.rs:ready}} ``` -To generate the next question, we use `rng::fill_bytes()` which fills a buffer with random bytes. We +To generate the next question, we use `rng::bytes()` which returns a slice of random bytes. We provide a buffer with the length of the current level. For the string to be printable we truncate the entropy of each byte from 8 to 5 bits and convert it to a `base32` symbol. diff --git a/book/src/applet/prelude/usb.rs b/book/src/applet/prelude/usb.rs index 0b9e43fe0..df4059fea 100644 --- a/book/src/applet/prelude/usb.rs +++ b/book/src/applet/prelude/usb.rs @@ -22,9 +22,9 @@ #![no_std] wasefire::applet!(); +use alloc::format; use alloc::rc::Rc; use alloc::string::String; -use alloc::{format, vec}; use core::cell::Cell; use core::time::Duration; @@ -49,13 +49,12 @@ fn main() { //{ ANCHOR: generate // Generate a question for this level. - let mut question = vec![0; level]; - rng::fill_bytes(&mut question).unwrap(); + let mut question = rng::bytes(level).unwrap(); for byte in &mut question { const BASE32: [u8; 32] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; *byte = BASE32[(*byte & 0x1f) as usize]; } - let mut question = String::from_utf8(question).unwrap(); + let mut question = String::from_utf8(question.into()).unwrap(); //} ANCHOR_END: generate //{ ANCHOR: question diff --git a/crates/prelude/CHANGELOG.md b/crates/prelude/CHANGELOG.md index 78f2c0f8c..bf5de59c5 100644 --- a/crates/prelude/CHANGELOG.md +++ b/crates/prelude/CHANGELOG.md @@ -4,6 +4,7 @@ ### Minor +- Add `rng::bytes{,_array}()` as safer alternatives to `rng::fill_bytes()` - Use Rust edition 2024 ### Patch diff --git a/crates/prelude/src/lib.rs b/crates/prelude/src/lib.rs index 0a172f21b..6a9241807 100644 --- a/crates/prelude/src/lib.rs +++ b/crates/prelude/src/lib.rs @@ -30,6 +30,8 @@ #![feature(alloc_error_handler)] #![feature(doc_auto_cfg)] #![feature(macro_metavar_expr)] +#![feature(maybe_uninit_array_assume_init)] +#![feature(maybe_uninit_uninit_array)] #![feature(negative_impls)] #![feature(never_type)] #![feature(vec_into_raw_parts)] diff --git a/crates/prelude/src/rng.rs b/crates/prelude/src/rng.rs index b3f4c97eb..496598401 100644 --- a/crates/prelude/src/rng.rs +++ b/crates/prelude/src/rng.rs @@ -14,12 +14,41 @@ //! Provides API for random number generation. +use alloc::boxed::Box; +use core::mem::MaybeUninit; + use wasefire_applet_api::rng as api; use crate::{Error, convert_unit}; +/// Returns a slice of random bytes. +pub fn bytes(len: usize) -> Result, Error> { + let mut buf = Box::new_uninit_slice(len); + fill_uninit_bytes(&mut buf)?; + // SAFETY: `fill_uninit_bytes()` only succeeds if all bytes are initialized. + Ok(unsafe { buf.assume_init() }) +} + +/// Returns an array of random bytes. +pub fn bytes_array() -> Result<[u8; N], Error> { + let mut buf = MaybeUninit::uninit_array(); + fill_uninit_bytes(&mut buf)?; + // SAFETY: `fill_uninit_bytes()` only succeeds if all bytes are initialized. + Ok(unsafe { MaybeUninit::array_assume_init(buf) }) +} + /// Fills a slice with random bytes. +/// +/// Prefer [`bytes()`] if you don't already have an allocation. pub fn fill_bytes(buf: &mut [u8]) -> Result<(), Error> { - let params = api::fill_bytes::Params { ptr: buf.as_mut_ptr(), len: buf.len() }; + // SAFETY: `fill_uninit_bytes()` only writes initialized bytes. + let buf = unsafe { + core::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut MaybeUninit, buf.len()) + }; + fill_uninit_bytes(buf) +} + +fn fill_uninit_bytes(buf: &mut [MaybeUninit]) -> Result<(), Error> { + let params = api::fill_bytes::Params { ptr: buf.as_mut_ptr() as *mut u8, len: buf.len() }; convert_unit(unsafe { api::fill_bytes(params) }) } diff --git a/examples/rust/ec_test/src/lib.rs b/examples/rust/ec_test/src/lib.rs index 893f12334..6814d31bf 100644 --- a/examples/rust/ec_test/src/lib.rs +++ b/examples/rust/ec_test/src/lib.rs @@ -17,7 +17,6 @@ #![no_std] wasefire::applet!(); -use alloc::vec; use alloc::vec::Vec; use wasefire::crypto::ec::{ @@ -103,8 +102,7 @@ fn test_ecdsa_random(name: &str) { let mut rs = Vec::new(); for _ in 0 .. 5 { let d = EcdsaPrivate::::random().unwrap(); - let mut m = vec![0; 257]; - rng::fill_bytes(&mut m).unwrap(); + let mut m = rng::bytes(257).unwrap().into_vec(); m.truncate(m[256] as usize); let s = d.sign(&m).unwrap(); debug!("- {:02x?}{:02x?} ({} bytes message)", &s.r()[.. 4], &s.s()[.. 4], m.len()); diff --git a/examples/rust/hsm/src/lib.rs b/examples/rust/hsm/src/lib.rs index df73ec358..16b2c7ab1 100644 --- a/examples/rust/hsm/src/lib.rs +++ b/examples/rust/hsm/src/lib.rs @@ -33,8 +33,7 @@ fn main() { fn process(request: Request) -> Result { match request { Request::GenerateKey { key } => { - let mut secret = [0; 16]; - rng::fill_bytes(&mut secret).map_err(|_| Error::RngError)?; + let secret = rng::bytes_array::<16>().map_err(|_| Error::RngError)?; store::insert(key, &secret).map_err(|_| Error::StoreError)?; Ok(Response::GenerateKey) } diff --git a/examples/rust/memory_game/src/lib.rs b/examples/rust/memory_game/src/lib.rs index 78762189b..31ee9d746 100644 --- a/examples/rust/memory_game/src/lib.rs +++ b/examples/rust/memory_game/src/lib.rs @@ -25,9 +25,9 @@ #![no_std] wasefire::applet!(); +use alloc::format; use alloc::rc::Rc; use alloc::string::String; -use alloc::{format, vec}; use core::cell::Cell; use core::time::Duration; @@ -43,13 +43,12 @@ fn main() { while serial::read_byte(&UsbSerial).unwrap() != 0x0d {} // Generate a question for this level. - let mut question = vec![0; level]; - rng::fill_bytes(&mut question).unwrap(); + let mut question = rng::bytes(level).unwrap(); for byte in &mut question { const BASE32: [u8; 32] = *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; *byte = BASE32[(*byte & 0x1f) as usize]; } - let mut question = String::from_utf8(question).unwrap(); + let mut question = String::from_utf8(question.into()).unwrap(); // Display the question. process(3, "Memorize this", &mut question, |_, x| x == 0x0d); diff --git a/examples/rust/oom/src/lib.rs b/examples/rust/oom/src/lib.rs index ff8e069aa..2d20bd9fb 100644 --- a/examples/rust/oom/src/lib.rs +++ b/examples/rust/oom/src/lib.rs @@ -17,10 +17,7 @@ #![no_std] wasefire::applet!(); -use alloc::vec; - fn main() { - let mut data = vec![0; 100000]; - rng::fill_bytes(&mut data).unwrap(); + let data = rng::bytes(100000).unwrap(); store::insert(0, &data).unwrap(); } diff --git a/examples/rust/rand/src/lib.rs b/examples/rust/rand/src/lib.rs index 050cb1a4d..5143c103e 100644 --- a/examples/rust/rand/src/lib.rs +++ b/examples/rust/rand/src/lib.rs @@ -21,7 +21,7 @@ #![no_std] wasefire::applet!(); -use alloc::{format, vec}; +use alloc::format; use wasefire::usb::serial::UsbSerial; @@ -32,8 +32,7 @@ fn main() { b'0' => 10, _ => continue, }; - let mut buf = vec![0; len as usize]; - rng::fill_bytes(&mut buf).unwrap(); + let buf = rng::bytes(len as usize).unwrap(); serial::write_all(&UsbSerial, format!("{buf:02x?}\r\n").as_bytes()).unwrap(); } }