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

Feature: correct TRNG mechanism #1538

Merged
merged 16 commits into from
Jul 4, 2024
Merged
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
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