Skip to content

Commit

Permalink
Feature: correct TRNG mechanism (#1538)
Browse files Browse the repository at this point in the history
* interstate

* Use type-state over creating new struct

* Getting revert_trng function into the driver

* Finish revert functions

* More progress

* Adjust for new driver release

* Small fixes

fmt

* Fmt + clippy + changelog

* Comments, `RngCore` trait for `Trng`

* fmt

* Make ADC work correctly on TRNG drop, PAC functions instead of raw regs

* Get docs buildable

rustfmt

* Doc comments fix

fmt

* Fix docs for esp32

* Small fixes + exclude `downgrade` and `drop` for `esp32c6`

Downgrade for `esp32c6` is not implemented so far

* TRNG/ADC on `esp32c6` warning comment
  • Loading branch information
playfulFence authored Jul 4, 2024
1 parent 786682c commit 921ecc4
Show file tree
Hide file tree
Showing 16 changed files with 1,849 additions and 1 deletion.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `embassy-usb` support (#1517)
- SPI Slave support for ESP32-S2 (#1562)
- Add new generic `OneShotTimer` and `PeriodicTimer` drivers, plus new `Timer` trait which is implemented for `TIMGx` and `SYSTIMER` (#1570)
- Feature: correct `TRNG` mechanism #1804

### Fixed

Expand Down
136 changes: 135 additions & 1 deletion esp-hal/src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@

use core::marker::PhantomData;

use crate::{peripheral::Peripheral, peripherals::RNG};
use crate::{
peripheral::{Peripheral, PeripheralRef},
peripherals::{ADC1, RNG},
};

/// Random number generator driver
#[derive(Clone, Copy)]
Expand Down Expand Up @@ -119,3 +122,134 @@ impl rand_core::RngCore for Rng {
Ok(())
}
}

/// True Random Number Generator (TRNG) driver
///
/// The `Trng` struct represents a true random number generator that combines
/// the randomness from the hardware RNG and an ADC. This struct provides
/// methods to generate random numbers and fill buffers with random bytes.
/// Due to pulling the entropy source from the ADC, it uses the associated
/// regiters, so to use TRNG we need to "occupy" the ADC peripheral.
///
/// For now, even after calling `core::mem::drop()` on `TRNG` ADC1 will not be
/// usable (details in esp-hal/#1750)
///
/// ```rust, no_run
#[doc = crate::before_snippet!()]
/// # use esp_hal::rng::Trng;
/// # use esp_hal::peripherals::Peripherals;
/// # use esp_hal::peripherals::ADC1;
/// # use esp_hal::analog::adc::{AdcConfig, Attenuation, Adc};
/// # use esp_hal::gpio::Io;
///
/// let mut peripherals = Peripherals::take();
/// let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
/// let mut buf = [0u8; 16];
///
/// // ADC is not available from now
/// let mut trng = Trng::new(peripherals.RNG, &mut peripherals.ADC1);
/// trng.read(&mut buf);
/// let mut true_rand = trng.random();
#[cfg_attr(not(esp32c6), doc = "let mut rng = trng.downgrade();")]
/// // ADC is available now
#[cfg_attr(esp32, doc = "let analog_pin = io.pins.gpio32;")]
#[cfg_attr(not(esp32), doc = "let analog_pin = io.pins.gpio3;")]
/// let mut adc1_config = AdcConfig::new();
/// let mut adc1_pin = adc1_config.enable_pin(analog_pin,
/// Attenuation::Attenuation11dB); let mut adc1 =
/// Adc::<ADC1>::new(peripherals.ADC1, adc1_config); let pin_value: u16 =
/// nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap();
#[cfg_attr(not(esp32c6), doc = "rng.read(&mut buf);")]
#[cfg_attr(not(esp32c6), doc = "true_rand = rng.random();")]
/// let pin_value: u16 = nb::block!(adc1.read_oneshot(&mut adc1_pin)).unwrap();
/// # }
/// ```
pub struct Trng<'d> {
/// The hardware random number generator instance.
pub rng: Rng,
/// A mutable reference to the ADC1 instance.
_adc: PeripheralRef<'d, ADC1>,
}

impl<'d> Trng<'d> {
/// Creates a new True Random Number Generator (TRNG) instance.
///
/// # Arguments
///
/// * `rng` - A peripheral instance implementing the `RNG` trait.
/// * `adc` - A mutable reference to an `Adc` instance.
///
/// # Returns
///
/// Returns a new `Trng` instance.
pub fn new(rng: impl Peripheral<P = RNG>, adc: impl Peripheral<P = ADC1> + 'd) -> Self {
crate::into_ref!(adc);

let gen = Rng::new(rng);
crate::soc::trng::ensure_randomness();
Self {
rng: gen,
_adc: adc,
}
}

/// Reads currently available `u32` integer from `TRNG`
pub fn random(&mut self) -> u32 {
self.rng.random()
}

/// Fills the provided buffer with random bytes.
pub fn read(&mut self, buffer: &mut [u8]) {
self.rng.read(buffer);
}

/// Downgrades the `Trng` instance to a `Rng` instance and releases the
/// ADC1.
/// For esp32c6 - blocked on https://github.com/espressif/esp-idf/issues/14124
#[cfg(not(esp32c6))]
pub fn downgrade(self) -> Rng {
self.rng
}
}

/// For esp32c6 - blocked on https://github.com/espressif/esp-idf/issues/14124
#[cfg(not(esp32c6))]
impl<'d> Drop for Trng<'d> {
fn drop(&mut self) {
crate::soc::trng::revert_trng();
}
}

#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::blocking::rng::Read for Trng<'_> {
type Error = core::convert::Infallible;
/// Fills the provided buffer with random bytes.
fn read(&mut self, buffer: &mut [u8]) -> Result<(), Self::Error> {
self.rng.read(buffer);
Ok(())
}
}

/// Implementing RngCore trait from rand_core for `Trng` structure
impl rand_core::RngCore for Trng<'_> {
fn next_u32(&mut self) -> u32 {
self.rng.next_u32()
}

fn next_u64(&mut self) -> u64 {
self.rng.next_u64()
}

fn fill_bytes(&mut self, dest: &mut [u8]) {
self.rng.fill_bytes(dest)
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.rng.try_fill_bytes(dest)
}
}

/// Implementing a CryptoRng marker trait that indicates that the generator is
/// cryptographically secure.
impl rand_core::CryptoRng for Trng<'_> {}
1 change: 1 addition & 0 deletions esp-hal/src/soc/esp32/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod peripherals;
#[cfg(psram)]
pub mod psram;
pub mod radio_clocks;
pub mod trng;

/// The name of the chip ("esp32") as `&str`
#[macro_export]
Expand Down
155 changes: 155 additions & 0 deletions esp-hal/src/soc/esp32/trng.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
//! Helper functions for TRNG functionality
pub fn ensure_randomness() {
let rtc_cntl = unsafe { &*crate::peripherals::RTC_CNTL::ptr() };
let sens = unsafe { &*crate::peripherals::SENS::ptr() };
let dport = unsafe { &*crate::peripherals::DPORT::ptr() };
let apb_ctrl = unsafe { &*crate::peripherals::APB_CTRL::ptr() };
let i2s0 = unsafe { &*crate::peripherals::I2S0::ptr() };

unsafe {
rtc_cntl.test_mux().modify(|_, w| w.dtest_rtc().bits(2));

rtc_cntl.test_mux().modify(|_, w| w.ent_rtc().set_bit());

sens.sar_start_force()
.modify(|_, w| w.sar2_en_test().set_bit());

// periph_module_enable(PERIPH_I2S0_MODULE);
dport
.perip_clk_en()
.modify(|_, w| w.i2c0_ext0_clk_en().set_bit());

dport
.perip_rst_en()
.modify(|_, w| w.i2c0_ext0_rst().clear_bit());

sens.sar_start_force()
.modify(|_, w| w.ulp_cp_force_start_top().clear_bit());

sens.sar_start_force()
.modify(|_, w| w.ulp_cp_start_top().clear_bit());

// Test pattern configuration byte 0xAD:
//--[7:4] channel_sel: 10-->en_test
//--[3:2] bit_width : 3-->12bit
//--[1:0] atten : 1-->3dB attenuation
apb_ctrl
.apb_saradc_sar2_patt_tab1()
.write(|w| w.bits(0xADADADAD));
apb_ctrl
.apb_saradc_sar2_patt_tab2()
.write(|w| w.bits(0xADADADAD));
apb_ctrl
.apb_saradc_sar2_patt_tab3()
.write(|w| w.bits(0xADADADAD));
apb_ctrl
.apb_saradc_sar2_patt_tab4()
.write(|w| w.bits(0xADADADAD));

sens.sar_meas_wait2()
.modify(|_, w| w.force_xpd_sar().bits(3));

sens.sar_read_ctrl()
.modify(|_, w| w.sar1_dig_force().set_bit());

sens.sar_read_ctrl2()
.modify(|_, w| w.sar2_dig_force().set_bit());

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_sar2_mux().set_bit());

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_sar_clk_div().bits(4));

apb_ctrl
.apb_saradc_fsm()
.modify(|_, w| w.saradc_rstb_wait().bits(8));

apb_ctrl
.apb_saradc_fsm()
.modify(|_, w| w.saradc_start_wait().bits(10));

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_work_mode().bits(0));

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_sar_sel().set_bit());

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_data_sar_sel().clear_bit());

i2s0.sample_rate_conf()
.modify(|_, w| w.rx_bck_div_num().bits(20));

apb_ctrl
.apb_saradc_ctrl()
.modify(|_, w| w.saradc_data_to_i2s().set_bit());

i2s0.conf2().modify(|_, w| w.camera_en().set_bit());

i2s0.conf2().modify(|_, w| w.lcd_en().set_bit());

i2s0.conf2().modify(|_, w| w.data_enable().set_bit());

i2s0.conf2()
.modify(|_, w| w.data_enable_test_en().set_bit());

i2s0.conf().modify(|_, w| w.rx_start().set_bit());
}
}

pub fn revert_trng() {
let sens = unsafe { &*crate::peripherals::SENS::ptr() };
let i2s0 = unsafe { &*crate::peripherals::I2S0::ptr() };
let apb_ctrl = unsafe { &*crate::peripherals::APB_CTRL::ptr() };

unsafe {
i2s0.conf().modify(|_, w| w.rx_start().clear_bit());

i2s0.conf().modify(|_, w| w.rx_reset().set_bit());

i2s0.conf().modify(|_, w| w.rx_reset().clear_bit());

i2s0.conf2().modify(|_, w| {
w.camera_en()
.clear_bit()
.lcd_en()
.clear_bit()
.data_enable_test_en()
.clear_bit()
.data_enable()
.clear_bit()
});

sens.sar_read_ctrl()
.modify(|_, w| w.sar1_dig_force().clear_bit());

sens.sar_read_ctrl2()
.modify(|_, w| w.sar2_dig_force().clear_bit());

sens.sar_start_force()
.modify(|_, w| w.sar2_en_test().clear_bit());

apb_ctrl.apb_saradc_ctrl().modify(|_, w| {
w.saradc_sar2_mux()
.clear_bit()
.saradc_sar_sel()
.clear_bit()
.saradc_data_to_i2s()
.clear_bit()
});

sens.sar_meas_wait2()
.modify(|_, w| w.force_xpd_sar().bits(0));

apb_ctrl
.apb_saradc_fsm()
.modify(|_, w| w.saradc_start_wait().bits(8));
}
}
1 change: 1 addition & 0 deletions esp-hal/src/soc/esp32c2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod efuse;
pub mod gpio;
pub mod peripherals;
pub mod radio_clocks;
pub mod trng;

/// The name of the chip ("esp32c2") as `&str`
#[macro_export]
Expand Down
Loading

0 comments on commit 921ecc4

Please sign in to comment.