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 2 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)]
pub mod hwrng;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand, the sensible way to use all of this is through the prng module.

Is there any good reason that the hwrng module and the random module are pub in the first place, or that RandomSeed::new_from_hwrng would be public?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is intended to be used through prng. I just left the rest public in case someone might need it e.g to seed his own prng but we could make it private to make people use the "intended" way. I do not have a strong opinion on that matter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then please limit the public parts. Public APIs that are not used are a needless compatibility liability -- whereas the two functions that will stay pub can easily be maintained. If it turns out that any part of this is needed, it's still easy to make a few more parts pub, whereas going back on that is a breaking change.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made the two modules random and hwrng private


#[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];

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

StdRng::from_seed(buffer)
}
132 changes: 132 additions & 0 deletions src/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use core::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: (),
}

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

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

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