From 729a7c2cc5e5fe1d9badb0a0f1c758ba2c57b6aa Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 4 Nov 2025 13:10:45 +0100 Subject: feat: initial support for nrf54 CRACEN peripheral The CRACEN peripheral supports random number generation, digest and key generation, and key exchange. The initial support implements random number generation. --- embassy-nrf/src/chips/nrf54l15_app.rs | 2 + embassy-nrf/src/cracen.rs | 161 ++++++++++++++++++++++++++++++++++ embassy-nrf/src/lib.rs | 4 +- examples/nrf54l15/Cargo.toml | 2 + examples/nrf54l15/src/bin/rng.rs | 29 ++++++ 5 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 embassy-nrf/src/cracen.rs create mode 100644 examples/nrf54l15/src/bin/rng.rs diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index d0068eb20..285cdec07 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -682,6 +682,8 @@ impl_saadc_input!(P1_12, 1, 12); impl_saadc_input!(P1_13, 1, 13); impl_saadc_input!(P1_14, 1, 14); +impl_cracen!(CRACEN, CRACEN, CRACEN); + embassy_hal_internal::interrupt_mod!( SWI00, SWI01, diff --git a/embassy-nrf/src/cracen.rs b/embassy-nrf/src/cracen.rs new file mode 100644 index 000000000..ddc592689 --- /dev/null +++ b/embassy-nrf/src/cracen.rs @@ -0,0 +1,161 @@ +//! CRACEN - Cryptographic Accelerator Engine driver. + +#![macro_use] + +use crate::mode::{Async, Blocking, Mode}; +use crate::{Peri, interrupt, pac, peripherals}; +use core::marker::PhantomData; + +pub struct Cracen<'d, M: Mode> { + _peri: Peri<'d, peripherals::CRACEN>, + _p: PhantomData, +} + +impl<'d> Cracen<'d, Blocking> { + /// Create a new CRACEN driver. + pub fn new_blocking(_peri: Peri<'d, peripherals::CRACEN>) -> Self { + let r = pac::CRACEN; + + let me = Self { _peri, _p: PhantomData }; + + me.stop(); + me + } +} + +impl<'d, M: Mode> Cracen<'d, M> { + fn regs() -> pac::cracen::Cracen { + pac::CRACEN + } + + fn core() -> pac::cracencore::Cracencore { + pac::CRACENCORE + } + + fn start_rng(&self) { + let r = Self::regs(); + r.enable().write(|w| { + w.set_rng(true); + }); + + let r = Self::core(); + r.rngcontrol().control().write(|w| { + w.set_enable(true); + }); + + while r.rngcontrol().status().read().state() == pac::cracencore::vals::State::STARTUP {} + } + + fn stop(&self) { + let r = Self::regs(); + r.enable().write(|w| { + w.set_cryptomaster(false); + w.set_rng(false); + w.set_pkeikg(false); + }); + } + + /// Fill the buffer with random bytes, blocking version. + pub fn blocking_fill_bytes(&mut self, dest: &mut [u8]) { + self.start_rng(); + + let r = Self::core(); + for chunk in dest.chunks_mut(4) { + while r.rngcontrol().fifolevel().read() == 0 {} + let word = r.rngcontrol().fifo(0).read().to_ne_bytes(); + let to_copy = word.len().min(chunk.len()); + chunk[..to_copy].copy_from_slice(&word[..to_copy]); + } + + self.stop(); + } + + /// Generate a random u32 + pub fn blocking_next_u32(&mut self) -> u32 { + let mut bytes = [0; 4]; + self.blocking_fill_bytes(&mut bytes); + // We don't care about the endianness, so just use the native one. + u32::from_ne_bytes(bytes) + } + + /// Generate a random u64 + pub fn blocking_next_u64(&mut self) -> u64 { + let mut bytes = [0; 8]; + self.blocking_fill_bytes(&mut bytes); + u64::from_ne_bytes(bytes) + } +} + +impl<'d, M: Mode> Drop for Cracen<'d, M> { + fn drop(&mut self) { + let r = Self::core(); + r.rngcontrol().control().write(|w| { + w.set_enable(false); + }); + + while r.rngcontrol().status().read().state() != pac::cracencore::vals::State::RESET {} + + let r = Self::regs(); + r.enable().write(|w| { + w.set_cryptomaster(false); + w.set_rng(false); + w.set_pkeikg(false); + }); + } +} + +impl<'d, M: Mode> rand_core_06::RngCore for Cracen<'d, M> { + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.blocking_fill_bytes(dest); + } + fn next_u32(&mut self) -> u32 { + self.blocking_next_u32() + } + fn next_u64(&mut self) -> u64 { + self.blocking_next_u64() + } + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core_06::Error> { + self.blocking_fill_bytes(dest); + Ok(()) + } +} + +impl<'d, M: Mode> rand_core_06::CryptoRng for Cracen<'d, M> {} + +impl<'d, M: Mode> rand_core_09::RngCore for Cracen<'d, M> { + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.blocking_fill_bytes(dest); + } + fn next_u32(&mut self) -> u32 { + self.blocking_next_u32() + } + fn next_u64(&mut self) -> u64 { + self.blocking_next_u64() + } +} + +impl<'d, M: Mode> rand_core_09::CryptoRng for Cracen<'d, M> {} + +pub(crate) trait SealedInstance { + fn regs() -> pac::cracen::Cracen; +} + +/// CRACEN peripheral instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static + Send { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} + +macro_rules! impl_cracen { + ($type:ident, $pac_type:ident, $irq:ident) => { + impl crate::cracen::SealedInstance for peripherals::$type { + fn regs() -> crate::pac::cracen::Cracen { + pac::$pac_type + } + } + impl crate::cracen::Instance for peripherals::$type { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 4c3b92a83..530964107 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -145,10 +145,12 @@ pub mod radio; #[cfg(feature = "_net-driver")] pub mod embassy_net_802154_driver; +#[cfg(feature = "_nrf54l")] +pub mod cracen; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(feature = "_nrf5340")] pub mod reset; -#[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(not(feature = "_nrf54l"))] #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; pub mod rtc; diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 14a80efe7..4ef77279f 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -14,6 +14,8 @@ embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defm embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } +rand = { version = "0.9.0", default-features = false } + defmt = "1.0.1" defmt-rtt = "1.0.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } diff --git a/examples/nrf54l15/src/bin/rng.rs b/examples/nrf54l15/src/bin/rng.rs new file mode 100644 index 000000000..3be035b9c --- /dev/null +++ b/examples/nrf54l15/src/bin/rng.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use embassy_nrf::cracen::Cracen; +use embassy_nrf::{bind_interrupts, cracen, peripherals}; +use rand::Rng as _; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut rng = Cracen::new_blocking(p.CRACEN); + + // Async API + let mut bytes = [0; 4]; + rng.blocking_fill_bytes(&mut bytes); + defmt::info!("Some random bytes: {:?}", bytes); + + // Sync API with `rand` + defmt::info!("A random number from 1 to 10: {:?}", rng.random_range(1..=10)); + + let mut bytes = [0; 1024]; + rng.blocking_fill_bytes(&mut bytes); + let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros()); + let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones()); + defmt::info!("Chance of zero: {}%", zero_count * 100 / (bytes.len() as u32 * 8)); + defmt::info!("Chance of one: {}%", one_count * 100 / (bytes.len() as u32 * 8)); +} -- cgit