Skip to content

Commit

Permalink
Rng: Add prng, hwrng support
Browse files Browse the repository at this point in the history
  • Loading branch information
Joel Aschmann committed Mar 10, 2023
1 parent e4973a6 commit 059c9f5
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
32 changes: 32 additions & 0 deletions src/hwrng.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
39 changes: 39 additions & 0 deletions src/prng.rs
Original file line number Diff line number Diff line change
@@ -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)
}
136 changes: 136 additions & 0 deletions src/random.rs
Original file line number Diff line number Diff line change
@@ -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<const SEED_LENGTH: usize> {
// Make sure this gets not manually constructed
private: PhantomData<()>,
}

impl<const SEED_LENGTH: usize> RngCore for Random<SEED_LENGTH> {
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<const SEED_LENGTH: usize> {
seed: [u8; SEED_LENGTH],
}

impl<const SEED_LENGTH: usize> RandomSeed<SEED_LENGTH> {
// 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::<SEED_LENGTH>::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<const SEED_LENGTH: usize> Default for RandomSeed<SEED_LENGTH> {
fn default() -> Self {
Self::new_empty()
}
}

// Enforced by `rand::SeedableRng`
impl<const SEED_LENGTH: usize> AsMut<[u8]> for RandomSeed<SEED_LENGTH> {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.seed
}
}

impl<const SEED_LENGTH: usize> SeedableRng for Random<SEED_LENGTH> {
type Seed = RandomSeed<SEED_LENGTH>;

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::<i32>()) as i32,
);
}
Random {
private: PhantomData,
}
}
}

0 comments on commit 059c9f5

Please sign in to comment.