Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rng: Add prng, hwrng support #54

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
30 changes: 30 additions & 0 deletions src/hwrng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use embedded_hal::blocking::rng::Read;

#[derive(Debug)]
#[non_exhaustive]
pub enum HWRNGError {}

/// 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 [`crate::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)]
mod hwrng;

#[cfg(riot_module_periph_hwrng)]
pub mod prng;

#[cfg(all(riot_module_random, riot_module_prng_sha256prng))]
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 the seeds from RIOTs HWRNG that are used
//! provide enough entropy to begin with, which drastically depends on the board used,
//! see remarks in [RIOTs documentation](https://doc.riot-os.org/group__drivers__periph__hwrng.html).

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 32 byte seed generated by RIOTs 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 32 byte seed generated by RIOTs HWRNG.
///
/// See this modules description regarding quality of the used seeds.
pub fn rand_prng() -> StdRng {
let mut buffer = [0u8; 32];

HWRNG.read(&mut buffer).unwrap();

StdRng::from_seed(buffer)
}
134 changes: 134 additions & 0 deletions src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use core::mem::size_of;

use embedded_hal::blocking::rng::Read;

use rand::{RngCore, SeedableRng};

/// Wrapper around RIOTs `random` module.
///
/// ## Seed length
/// Since the module allows a dynamic seed size
/// it needs to be specified in the type.
/// The `SEED_LENGTH` variable specifies the seed length in bytes.
/// **Since RIOT takes in `uint32_t` (`u32`) the length need to be divisible by 4!**
///
/// ## Security
/// Even though `Random` claims to be a cryptographic secure PRNG
/// it only can be if provided sufficiently 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 being 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: (),
}

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(())
}
}

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: () }
}
}

#[cfg(riot_module_prng_sha256prng)]
impl<const SEED_LENGTH: usize> rand::CryptoRng for Random<SEED_LENGTH> {}

/// A seed of length `SEED_LENGTH` intended to be used by [`Random`].
///
/// ## Seed length
/// Since [`Random`] allows a dynamic seed size
/// it needs to be specified in the type.
/// The `SEED_LENGTH` variable specifies the seed length in bytes.
/// **Since RIOT takes in `uint32_t` (`u32`) the length need to be divisible 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) seed container.
///
/// 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 remarks there on the quality of the
/// generated seeds which depends very much on the used board.
#[cfg(riot_module_periph_hwrng)]
pub fn new_from_hwrng() -> Self {
let mut seed = RandomSeed::<SEED_LENGTH>::default();

use crate::hwrng::HWRNG;
HWRNG.read(seed.buffer()).unwrap();

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
}
}