From 43ccf16f3aa15e13c75e0373f497d15c8f51faf5 Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Fri, 10 Mar 2023 09:47:50 +0100 Subject: [PATCH] Rng: Add prng, hwrng support --- Cargo.toml | 3 ++ src/hwrng.rs | 32 ++++++++++++ src/lib.rs | 9 ++++ src/prng.rs | 39 +++++++++++++++ src/random.rs | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 219 insertions(+) create mode 100644 src/hwrng.rs create mode 100644 src/prng.rs create mode 100644 src/random.rs diff --git a/Cargo.toml b/Cargo.toml index df8de3fb..e7f01c5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,9 @@ embedded-nal = { version = "0.6.0", optional = true } embedded-nal-tcpextensions = { version = "0.1", optional = true } pin-utils = "0.1" +rand = {version = "0.8.5", features = ["std_rng"], default-features = false } +rand_core = {version = "0.6.4", default-features = false} + [build-dependencies] shlex = "0.1.1" diff --git a/src/hwrng.rs b/src/hwrng.rs new file mode 100644 index 00000000..4dd0d76d --- /dev/null +++ b/src/hwrng.rs @@ -0,0 +1,32 @@ +use embedded_hal::blocking::rng::Read; + +#[derive(Debug)] +#[non_exhaustive] +pub enum HWRNGError { + Other, +} + +/// Represents RIOTs hwrng module. It can be used via +/// `embedded_hal`s [`embedded_hal::blocking::rng::Read`] trait. +/// +/// The main purpose of this module is to generate seeds for PRNGs like +/// [`rand::rngs::StdRng`] or [`crate::random::Random`] (see `prng` module). +/// +/// # Security +/// As stated in RIOTs hwrng module-description the quality of the generated +/// random data may vary drastically between boards. If you want to use this +/// for e.g. cryptography make sure your current boards hwrng implementation +/// provides random data with sufficient randomness. +#[derive(Debug)] +pub struct HWRNG; + +impl Read for HWRNG { + type Error = HWRNGError; + + fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> { + unsafe { + riot_sys::hwrng_read(buffer.as_mut_ptr() as *mut _, buffer.len() as u32); + } + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8c976e6a..a63b91a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -146,6 +146,15 @@ pub mod socket_embedded_nal_tcp; #[cfg(riot_module_periph_gpio)] pub mod gpio; +#[cfg(riot_module_periph_hwrng)] +pub mod hwrng; + +#[cfg(riot_module_periph_hwrng)] +pub mod prng; + +#[cfg(all(riot_module_random, riot_module_prng_sha256prng))] +pub mod random; + #[cfg(riot_module_bluetil_ad)] pub mod bluetil; diff --git a/src/prng.rs b/src/prng.rs new file mode 100644 index 00000000..bbf89cc5 --- /dev/null +++ b/src/prng.rs @@ -0,0 +1,39 @@ +//! This module provides helper methods to setup pseudo-random-number-generators (prng's) +//! by seeding them with data obtained from the [`crate::hwrng`] module. +//! +//! At the moment two prngs are available: [`rand::rngs::StdRng`] and [`crate::random::Random`]. +//! Both of these claim to be cryptographic secure prngs. Provided of course the seeds from `hwrng` +//! are secure to begin with, which drastically depends on the board used, see remarks in [`crate::hwrng`] module! + + +use embedded_hal::prelude::_embedded_hal_blocking_rng_Read; +use rand::{rngs::StdRng, SeedableRng}; + +use crate::{ + hwrng::HWRNG, + random::{Random, RandomSeed}, +}; + +/// Seeds a [`crate::random::Random`] prng with a 32bit seed generated by [`crate::hwrng::HWRNG`]. +/// +/// See this modules description regarding quality of the used seeds. +/// +/// Be aware that there should be only one `Random` object at a time, +/// since RIOT uses a global state for this internally, so creating a second object +/// just results in the global state beeing overwritten and +/// both objects representing practically the same prng. +#[cfg(riot_module_random)] +pub fn riot_prng() -> Random<32> { + Random::<32>::from_seed(RandomSeed::new_from_hwrng()) +} + +/// Seeds a [`rand::rngs::StdRng`] prng with a 32bit seed generated by [`crate::hwrng::HWRNG`]. +/// +/// See this modules description regarding quality of the used seeds. +pub fn rand_prng() -> StdRng { + let mut buffer = [0u8; 32]; + unsafe { + HWRNG.read(&mut buffer).unwrap_unchecked(); + } + StdRng::from_seed(buffer) +} diff --git a/src/random.rs b/src/random.rs new file mode 100644 index 00000000..5462ac29 --- /dev/null +++ b/src/random.rs @@ -0,0 +1,136 @@ +use core::{marker::PhantomData, mem::size_of}; + +use embedded_hal::blocking::rng::Read; + +use rand::{RngCore, SeedableRng}; + +use crate::hwrng::HWRNG; + +/// Wrapper around RIOTs `random` module. +/// +/// ## Seedlength +/// Since the module allows a dynamic seedsize +/// it needs to be specified in the type. +/// The `SEED_LENGTH` variable specifies the seedlength in bytes. +/// **Since RIOT takes in `uint32_t` (`u32`) the length need to be divisable by 4!** +/// +/// ## Security +/// Even though `Random` claims to be a cryptographic secure prng +/// it only can be if provided sufficently random seeds! See remarks at [`crate::hwrng::HWRNG`] +/// if when using it to generate seeds. +/// +/// ## Global state +/// Be aware that there should be only one `Random` object at a time, +/// since RIOT uses a global state for this internally, so creating a second object +/// just results in the global state beeing overwritten and +/// both objects representing practically the same prng. +#[derive(Debug)] +pub struct Random { + // Make sure this gets not manually constructed + private: PhantomData<()>, +} + +impl RngCore for Random { + fn next_u32(&mut self) -> u32 { + unsafe { riot_sys::random_uint32() } + } + + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_u32(self) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + unsafe { riot_sys::random_bytes(dest.as_mut_ptr() as *mut _, dest.len() as u32) } + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> { + self.fill_bytes(dest); + Ok(()) + } +} + +/// A seed of length `SEED_LENGTH` inteded to be used by [`Random`]. +/// +/// ## Seedlength +/// Since [`Random`] allows a dynamic seedsize +/// it needs to be specified in the type. +/// The `SEED_LENGTH` variable specifies the seedlength in bytes. +/// **Since RIOT takes in `uint32_t` (`u32`) the length need to be divisable by 4!** +/// +/// ## Security +/// This is only a container for a seed and therefore +/// can not give any assurances as to the quality of the contained seed, +/// which wholly depends on the method with which the contained seed was generated. +#[derive(Debug)] +pub struct RandomSeed { + seed: [u8; SEED_LENGTH], +} + +impl RandomSeed { + // Workaround: see https://github.com/nvzqz/static-assertions-rs/issues/40#issuecomment-1458897730 + const CHECK_DIVISIBLE_BY_FOUR: () = assert!(SEED_LENGTH & 3 == 0); + + /// Creates an empty (zeroed) seedcontainer. + /// + /// This should **not** be used as a seed for anything that + /// should provide any security. It is only meant to setup the buffer, + /// which then can be accessed via its `buffer()` method. + pub fn new_empty() -> Self { + // Needed here to force the evaluation of the const + let _ = Self::CHECK_DIVISIBLE_BY_FOUR; + + RandomSeed { + seed: [0; SEED_LENGTH], + } + } + + /// Creates a [`RandomSeed`] with a seed generated by + /// [`crate::hwrng::HWRNG`]. + /// + /// See remakrs there on the quality of the + /// generated seeds which depends very much on the used board. + pub fn new_from_hwrng() -> Self { + let mut seed = RandomSeed::::default(); + + unsafe { + HWRNG.read(&mut seed.buffer()).unwrap_unchecked(); + } + + seed + } + + /// The internal buffer + pub fn buffer(&mut self) -> &mut [u8] { + &mut self.seed + } +} + +// Enforced by `rand::SeedableRng` +impl Default for RandomSeed { + fn default() -> Self { + Self::new_empty() + } +} + +// Enforced by `rand::SeedableRng` +impl AsMut<[u8]> for RandomSeed { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.seed + } +} + +impl SeedableRng for Random { + type Seed = RandomSeed; + + fn from_seed(mut seed: Self::Seed) -> Self { + unsafe { + riot_sys::random_init_by_array( + seed.seed.as_mut_ptr() as *mut u32, + (seed.seed.len() / size_of::()) as i32, + ); + } + Random { + private: PhantomData, + } + } +}