From 059c9f53fdd547d529a50e64d12f1ab8dd6b1439 Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Fri, 10 Mar 2023 09:47:50 +0100 Subject: [PATCH 1/5] 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..0e47a8e7 --- /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, + } + } +} From 2b99ee539048daabddab05cc8ff2a2e6acffa946 Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Mon, 20 Mar 2023 11:17:07 +0100 Subject: [PATCH 2/5] RNG: small fixes * remove unsafe blocks * make error type empty * update comments --- src/hwrng.rs | 6 ++---- src/prng.rs | 6 +++--- src/random.rs | 12 ++++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/hwrng.rs b/src/hwrng.rs index 0e47a8e7..cbb20510 100644 --- a/src/hwrng.rs +++ b/src/hwrng.rs @@ -2,15 +2,13 @@ use embedded_hal::blocking::rng::Read; #[derive(Debug)] #[non_exhaustive] -pub enum HWRNGError { - Other, -} +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 `prng` module). +/// [`rand::rngs::StdRng`] or [`crate::random::Random`] (see [`crate::prng`] module). /// /// # Security /// As stated in RIOTs hwrng module-description the quality of the generated diff --git a/src/prng.rs b/src/prng.rs index bbf89cc5..6194618d 100644 --- a/src/prng.rs +++ b/src/prng.rs @@ -32,8 +32,8 @@ pub fn riot_prng() -> Random<32> { /// 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(); - } + + HWRNG.read(&mut buffer).unwrap(); + StdRng::from_seed(buffer) } diff --git a/src/random.rs b/src/random.rs index 5462ac29..3df9bb36 100644 --- a/src/random.rs +++ b/src/random.rs @@ -1,4 +1,4 @@ -use core::{marker::PhantomData, mem::size_of}; +use core::mem::size_of; use embedded_hal::blocking::rng::Read; @@ -27,7 +27,7 @@ use crate::hwrng::HWRNG; #[derive(Debug)] pub struct Random { // Make sure this gets not manually constructed - private: PhantomData<()>, + _private: (), } impl RngCore for Random { @@ -92,9 +92,7 @@ impl RandomSeed { pub fn new_from_hwrng() -> Self { let mut seed = RandomSeed::::default(); - unsafe { - HWRNG.read(&mut seed.buffer()).unwrap_unchecked(); - } + HWRNG.read(seed.buffer()).unwrap(); seed } @@ -129,8 +127,6 @@ impl SeedableRng for Random { (seed.seed.len() / size_of::()) as i32, ); } - Random { - private: PhantomData, - } + Random { _private: () } } } From 8fcd35a918a01800c0243be1543c66de1633b07c Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Tue, 21 Mar 2023 08:55:06 +0100 Subject: [PATCH 3/5] RNG: make hwrng, random private --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a63b91a2..72073cb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -147,13 +147,13 @@ pub mod socket_embedded_nal_tcp; pub mod gpio; #[cfg(riot_module_periph_hwrng)] -pub mod hwrng; +mod hwrng; #[cfg(riot_module_periph_hwrng)] pub mod prng; #[cfg(all(riot_module_random, riot_module_prng_sha256prng))] -pub mod random; +mod random; #[cfg(riot_module_bluetil_ad)] pub mod bluetil; From 057fa914883e1eca307f94bd95113404f5050b2a Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Thu, 21 Dec 2023 15:33:55 +0100 Subject: [PATCH 4/5] Some Cleanup and add missing featuregate --- src/random.rs | 68 ++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/random.rs b/src/random.rs index 3df9bb36..df4aabf4 100644 --- a/src/random.rs +++ b/src/random.rs @@ -4,27 +4,24 @@ 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 +/// ## Seed length +/// Since the module allows a dynamic seed size /// 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!** +/// 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 sufficently random seeds! See remarks at [`crate::hwrng::HWRNG`] +/// 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 beeing overwritten and -/// both objects representing practically the same prng. -#[derive(Debug)] +/// just results in the global state being overwritten and +/// both objects representing practically the same PRNG.#[derive(Debug)] pub struct Random { // Make sure this gets not manually constructed _private: (), @@ -49,18 +46,35 @@ impl RngCore for Random { } } -/// A seed of length `SEED_LENGTH` inteded to be used by [`Random`]. +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: () } + } +} + +#[cfg(riot_module_prng_sha256prng)] +impl rand::CryptoRng for Random {} + +/// A seed of length `SEED_LENGTH` intended to be used by [`Random`]. /// -/// ## Seedlength -/// Since [`Random`] allows a dynamic seedsize +/// ## Seed length +/// Since [`Random`] allows a dynamic seed size /// 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!** +/// 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. +/// which wholly depends on the method with which the contained seed was generated. #[derive(Debug)] pub struct RandomSeed { seed: [u8; SEED_LENGTH], @@ -70,7 +84,7 @@ 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. + /// 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, @@ -87,11 +101,13 @@ impl RandomSeed { /// Creates a [`RandomSeed`] with a seed generated by /// [`crate::hwrng::HWRNG`]. /// - /// See remakrs there on the quality of the + /// 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::::default(); + use crate::hwrng::HWRNG; HWRNG.read(seed.buffer()).unwrap(); seed @@ -116,17 +132,3 @@ impl AsMut<[u8]> for RandomSeed { &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: () } - } -} From 4af86fd16d6e49555bababc4036c175fd766e23f Mon Sep 17 00:00:00 2001 From: Joel Aschmann Date: Thu, 11 Jan 2024 20:48:05 +0100 Subject: [PATCH 5/5] Fix old doc reference --- src/prng.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/prng.rs b/src/prng.rs index 6194618d..3e9d3026 100644 --- a/src/prng.rs +++ b/src/prng.rs @@ -2,9 +2,9 @@ //! 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! - +//! 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}; @@ -14,7 +14,7 @@ use crate::{ random::{Random, RandomSeed}, }; -/// Seeds a [`crate::random::Random`] prng with a 32bit seed generated by [`crate::hwrng::HWRNG`]. +/// 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. /// @@ -27,7 +27,7 @@ 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`]. +/// 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 {