From 816db74fc6c4eb3db0808a357e7347410b496810 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Tue, 6 Aug 2024 14:15:45 -0400 Subject: [PATCH 1/9] feat(SHA): Refactor SHA to use trait. Implement Digest traits for SHA --- esp-hal/CHANGELOG.md | 2 + esp-hal/Cargo.toml | 1 + esp-hal/src/reg_access.rs | 3 +- esp-hal/src/{sha.rs => sha/mod.rs} | 429 ++++++++++++++++------------- hil-test/Cargo.toml | 3 +- hil-test/tests/sha.rs | 144 +++++++++- 6 files changed, 378 insertions(+), 204 deletions(-) rename esp-hal/src/{sha.rs => sha/mod.rs} (53%) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 0a08a743846..5a14db6d677 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow configuration of period updating method for MCPWM timers (#1898) - Add self-testing mode for TWAI peripheral. (#1929) - Added a `PeripheralClockControl::reset` to the driver constructors where missing (#1893) +- Added `digest::Digest` implementation to SHA (#1908) ### Changed @@ -23,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow DMA to/from psram for esp32s3 (#1827) - DMA buffers now don't require a static lifetime. Make sure to never `mem::forget` an in-progress DMA transfer (consider using `#[deny(clippy::mem_forget)]`) (#1837) - Peripherals (where possible) are now explicitly reset and enabled in their constructors (#1893) +- SHA driver now use specific structs for the hashing algorithm instead of a parameter. (#1908) ### Fixed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 525f41cbae0..7e351f6952b 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -22,6 +22,7 @@ cfg-if = "1.0.0" critical-section = "1.1.2" defmt = { version = "0.3.8", optional = true } delegate = "0.12.0" +digest = { version = "0.10.7", default-features = false, optional = true } document-features = "0.2.10" embassy-futures = { version = "0.1.1", optional = true } embassy-sync = { version = "0.6.0", optional = true } diff --git a/esp-hal/src/reg_access.rs b/esp-hal/src/reg_access.rs index 7ad9f1b0fc6..54d39c88d73 100644 --- a/esp-hal/src/reg_access.rs +++ b/esp-hal/src/reg_access.rs @@ -28,6 +28,7 @@ impl EndianessConverter for NativeEndianess { } /// Use BE for ESP32, NE otherwise +#[derive(Debug, Clone)] pub(crate) struct SocDependentEndianess; #[cfg(not(esp32))] @@ -61,7 +62,7 @@ impl EndianessConverter for SocDependentEndianess { // It assumes incoming `dst` are aligned to desired layout (in future // ptr.is_aligned can be used). It also assumes that writes are done in FIFO // order. -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct AlignmentHelper { buf: [u8; U32_ALIGN_SIZE], buf_fill: usize, diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha/mod.rs similarity index 53% rename from esp-hal/src/sha.rs rename to esp-hal/src/sha/mod.rs index 89474222b23..b4d62e4239d 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha/mod.rs @@ -68,23 +68,9 @@ use crate::{ system::PeripheralClockControl, }; -// All the hash algorithms introduced in FIPS PUB 180-4 Spec. -// – SHA-1 -// – SHA-224 -// – SHA-256 -// – SHA-384 -// – SHA-512 -// – SHA-512/224 -// – SHA-512/256 -// – SHA-512/t (not implemented yet) -// Two working modes -// – Typical SHA -// – DMA-SHA (not implemented yet) - -/// The SHA Accelerator driver instance -pub struct Sha<'d, DM: crate::Mode> { - sha: PeripheralRef<'d, SHA>, - mode: ShaMode, +/// Context for a SHA Accelerator driver instance +#[derive(Debug, Clone)] +pub struct Context { alignment_helper: AlignmentHelper, cursor: usize, first_run: bool, @@ -92,76 +78,10 @@ pub struct Sha<'d, DM: crate::Mode> { phantom: PhantomData, } -/// Hash Algorithm Mode -#[derive(Debug, Clone, Copy)] -pub enum ShaMode { - SHA1, - #[cfg(not(esp32))] - SHA224, - SHA256, - #[cfg(any(esp32, esp32s2, esp32s3))] - SHA384, - #[cfg(any(esp32, esp32s2, esp32s3))] - SHA512, - #[cfg(any(esp32s2, esp32s3))] - SHA512_224, - #[cfg(any(esp32s2, esp32s3))] - SHA512_256, - // SHA512_(u16) // Max 511 -} - -// TODO: Maybe make Sha Generic (Sha) in order to allow for better -// compiler optimizations? (Requires complex const generics which isn't stable -// yet) +impl crate::private::Sealed for Context {} #[cfg(not(esp32))] -fn mode_as_bits(mode: ShaMode) -> u8 { - match mode { - ShaMode::SHA1 => 0, - ShaMode::SHA224 => 1, - ShaMode::SHA256 => 2, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA384 => 3, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512 => 4, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_224 => 5, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_256 => 6, - // _ => 0 // TODO: SHA512/t - } -} - -impl<'d> Sha<'d, crate::Blocking> { - /// Create a new instance in [crate::Blocking] mode. - #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] - pub fn new(sha: impl Peripheral

+ 'd, mode: ShaMode) -> Self { - crate::into_ref!(sha); - - PeripheralClockControl::reset(crate::system::Peripheral::Sha); - PeripheralClockControl::enable(crate::system::Peripheral::Sha); - - // Setup SHA Mode - #[cfg(not(esp32))] - sha.mode() - .write(|w| unsafe { w.mode().bits(mode_as_bits(mode)) }); - - Self { - sha, - mode, - cursor: 0, - first_run: true, - finished: false, - alignment_helper: AlignmentHelper::default(), - phantom: PhantomData, - } - } -} - -impl<'d> crate::private::Sealed for Sha<'d, crate::Blocking> {} - -#[cfg(not(esp32))] -impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { +impl crate::InterruptConfigurable for Context { fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) { unsafe { crate::interrupt::bind_interrupt(crate::peripherals::Interrupt::SHA, handler.handler()); @@ -171,7 +91,15 @@ impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { } } -// TODO: Allow/Implement SHA512_(u16) +impl Context { + pub fn first_run(&self) -> bool { + self.first_run + } + + pub fn finished(&self) -> bool { + self.finished + } +} // A few notes on this implementation with regards to 'memcpy', // - It seems that ptr::write_bytes already acts as volatile, while ptr::copy_* @@ -189,102 +117,63 @@ impl<'d> crate::InterruptConfigurable for Sha<'d, crate::Blocking> { // This implementation might fail after u32::MAX/8 bytes, to increase please see // ::finish() length/self.cursor usage -impl<'d, DM: crate::Mode> Sha<'d, DM> { - pub fn first_run(&self) -> bool { - self.first_run - } +pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { + /// Setup SHA Mode + #[cfg(not(esp32))] + fn mode_as_bits() -> u8; - pub fn finished(&self) -> bool { - self.finished - } + fn chunk_length(&self) -> usize; - #[cfg(not(esp32))] - fn process_buffer(&mut self) { - if self.first_run { - // Set SHA_START_REG - self.sha.start().write(|w| unsafe { w.bits(1) }); - self.first_run = false; - } else { - // SET SHA_CONTINUE_REG - self.sha.continue_().write(|w| unsafe { w.bits(1) }); - } - } + fn digest_length(&self) -> usize; + /// ESP32 requires that a control register to be written to calculate the final SHA hash. #[cfg(esp32)] - fn process_buffer(&mut self) { - if self.first_run { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA256 => self.sha.sha256_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA384 => self.sha.sha384_start().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA512 => self.sha.sha512_start().write(|w| unsafe { w.bits(1) }), - } - self.first_run = false; - } else { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA256 => self.sha.sha256_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA384 => self.sha.sha384_continue().write(|w| unsafe { w.bits(1) }), - ShaMode::SHA512 => self.sha.sha512_continue().write(|w| unsafe { w.bits(1) }), - } - } - } - - fn chunk_length(&self) -> usize { - match self.mode { - ShaMode::SHA1 | ShaMode::SHA256 => 64, - #[cfg(not(esp32))] - ShaMode::SHA224 => 64, - #[cfg(not(any(esp32c2, esp32c3, esp32c6, esp32h2)))] - _ => 128, - } - } + fn load_reg(&self); + /// ESP32 uses a different register per hash mode. #[cfg(esp32)] - fn is_busy(&self) -> bool { - match self.mode { - ShaMode::SHA1 => self.sha.sha1_busy().read().sha1_busy().bit_is_set(), - ShaMode::SHA256 => self.sha.sha256_busy().read().sha256_busy().bit_is_set(), - ShaMode::SHA384 => self.sha.sha384_busy().read().sha384_busy().bit_is_set(), - ShaMode::SHA512 => self.sha.sha512_busy().read().sha512_busy().bit_is_set(), - } - } + fn is_busy(&self) -> bool; #[cfg(not(esp32))] fn is_busy(&self) -> bool { - self.sha.busy().read().bits() != 0 + let sha = unsafe { crate::peripherals::SHA::steal() }; + sha.busy().read().bits() != 0 } - pub fn digest_length(&self) -> usize { - match self.mode { - ShaMode::SHA1 => 20, - #[cfg(not(esp32))] - ShaMode::SHA224 => 28, - ShaMode::SHA256 => 32, - #[cfg(any(esp32, esp32s2, esp32s3))] - ShaMode::SHA384 => 48, - #[cfg(any(esp32, esp32s2, esp32s3))] - ShaMode::SHA512 => 64, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_224 => 28, - #[cfg(any(esp32s2, esp32s3))] - ShaMode::SHA512_256 => 32, + #[cfg(esp32)] + fn process_buffer(&mut self); + + #[cfg(not(esp32))] + fn process_buffer(&mut self) { + let sha = unsafe { crate::peripherals::SHA::steal() }; + // Setup SHA Mode before processing current buffer. + sha.mode() + .write(|w| unsafe { w.mode().bits(Self::mode_as_bits()) }); + if self.first_run { + // Set SHA_START_REG + sha.start().write(|w| unsafe { w.bits(1) }); + self.first_run = false; + } else { + // SET SHA_CONTINUE_REG + sha.continue_().write(|w| unsafe { w.bits(1) }); } } fn flush_data(&mut self) -> nb::Result<(), Infallible> { + let sha = unsafe { crate::peripherals::SHA::steal() }; if self.is_busy() { return Err(nb::Error::WouldBlock); } let chunk_len = self.chunk_length(); - let flushed = self.alignment_helper.flush_to( + let ctx = self.deref_mut(); + let flushed = ctx.alignment_helper.flush_to( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), - (self.cursor % chunk_len) / self.alignment_helper.align_size(), + sha.m_mem(0).as_ptr(), + (ctx.cursor % chunk_len) / ctx.alignment_helper.align_size(), ); self.cursor = self.cursor.wrapping_add(flushed); @@ -299,18 +188,20 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // This function ensures that incoming data is aligned to u32 (due to issues // with cpy_mem) fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { + let sha = unsafe { crate::peripherals::SHA::steal() }; let mod_cursor = self.cursor % self.chunk_length(); let chunk_len = self.chunk_length(); - let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy( + let ctx = self.deref_mut(); + let (remaining, bound_reached) = ctx.alignment_helper.aligned_volatile_copy( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + sha.m_mem(0).as_ptr(), incoming, - chunk_len / self.alignment_helper.align_size(), - mod_cursor / self.alignment_helper.align_size(), + chunk_len / ctx.alignment_helper.align_size(), + mod_cursor / ctx.alignment_helper.align_size(), ); self.cursor = self.cursor.wrapping_add(incoming.len() - remaining.len()); @@ -322,7 +213,7 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { Ok(remaining) } - pub fn update<'a>(&mut self, buffer: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { + fn update<'a>(&mut self, buffer: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { if self.is_busy() { return Err(nb::Error::WouldBlock); } @@ -340,13 +231,15 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // // Typically output is expected to be the size of digest_length(), but smaller // inputs can be given to get a "short hash" - pub fn finish(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> { + fn finish(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> { // The main purpose of this function is to dynamically generate padding for the // input. Padding: Append "1" bit, Pad zeros until 512/1024 filled // then set the message length in the LSB (overwriting the padding) // If not enough free space for length+1, add length at end of a new zero'd // block + let sha = unsafe { crate::peripherals::SHA::steal() }; + if self.is_busy() { return Err(nb::Error::WouldBlock); } @@ -355,7 +248,7 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // Store message length for padding let length = (self.cursor as u64 * 8).to_be_bytes(); - nb::block!(self.update(&[0x80]))?; // Append "1" bit + nb::block!(Sha::update(self, &[0x80]))?; // Append "1" bit nb::block!(self.flush_data())?; // Flush partial data, ensures aligned cursor debug_assert!(self.cursor % 4 == 0); @@ -364,14 +257,15 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // Zero out remaining data if buffer is almost full (>=448/896), and process // buffer let pad_len = chunk_len - mod_cursor; - self.alignment_helper.volatile_write_bytes( + let ctx = self.deref_mut(); + ctx.alignment_helper.volatile_write_bytes( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + sha.m_mem(0).as_ptr(), 0_u8, - pad_len / self.alignment_helper.align_size(), - mod_cursor / self.alignment_helper.align_size(), + pad_len / ctx.alignment_helper.align_size(), + mod_cursor / ctx.alignment_helper.align_size(), ); self.process_buffer(); self.cursor = self.cursor.wrapping_add(pad_len); @@ -385,24 +279,25 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { let mod_cursor = self.cursor % chunk_len; // Should be zero if branched above let pad_len = chunk_len - mod_cursor - core::mem::size_of::(); - self.alignment_helper.volatile_write_bytes( + let ctx = self.deref_mut(); + ctx.alignment_helper.volatile_write_bytes( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + sha.m_mem(0).as_ptr(), 0_u8, - pad_len / self.alignment_helper.align_size(), - mod_cursor / self.alignment_helper.align_size(), + pad_len / ctx.alignment_helper.align_size(), + mod_cursor / ctx.alignment_helper.align_size(), ); - self.alignment_helper.aligned_volatile_copy( + ctx.alignment_helper.aligned_volatile_copy( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + sha.m_mem(0).as_ptr(), &length, - chunk_len / self.alignment_helper.align_size(), - (chunk_len - core::mem::size_of::()) / self.alignment_helper.align_size(), + chunk_len / ctx.alignment_helper.align_size(), + (chunk_len - core::mem::size_of::()) / ctx.alignment_helper.align_size(), ); self.process_buffer(); @@ -412,22 +307,16 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { // ESP32 requires additional load to retrieve output #[cfg(esp32)] { - match self.mode { - ShaMode::SHA1 => unsafe { self.sha.sha1_load().write(|w| w.bits(1)) }, - ShaMode::SHA256 => unsafe { self.sha.sha256_load().write(|w| w.bits(1)) }, - ShaMode::SHA384 => unsafe { self.sha.sha384_load().write(|w| w.bits(1)) }, - ShaMode::SHA512 => unsafe { self.sha.sha512_load().write(|w| w.bits(1)) }, - } - + self.load_reg(); // Spin wait for result, 8-20 clock cycles according to manual while self.is_busy() {} } self.alignment_helper.volatile_read_regset( #[cfg(esp32)] - self.sha.text(0).as_ptr(), + sha.text(0).as_ptr(), #[cfg(not(esp32))] - self.sha.h_mem(0).as_ptr(), + sha.h_mem(0).as_ptr(), output, core::cmp::min(output.len(), 32) / self.alignment_helper.align_size(), ); @@ -438,4 +327,162 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { Ok(()) } + + /// Create a new instance in [crate::Blocking] mode. + #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] + fn new_internal(sha: impl Peripheral

+ 'd) -> (PeripheralRef<'d, SHA>, Context) { + crate::into_ref!(sha); + + PeripheralClockControl::reset(crate::system::Peripheral::Sha); + PeripheralClockControl::enable(crate::system::Peripheral::Sha); + + ( + sha, + Context { + cursor: 0, + first_run: true, + finished: false, + alignment_helper: AlignmentHelper::default(), + phantom: PhantomData, + }, + ) + } } + +/// This macro implements the Sha<'a, DM> trait for a specified Sha algorithm and a set of +/// parameters +macro_rules! impl_sha { + ($name: ident, $mode_bits: tt, $digest_length: tt, $chunk_length: tt) => { + pub struct $name<'d, DM: crate::Mode>(PeripheralRef<'d, SHA>, Context); + + impl<'d> $name<'d, crate::Blocking> { + /// Create a new instance in [crate::Blocking] mode. + #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] + pub fn new(sha: impl Peripheral

+ 'd) -> $name<'d, crate::Blocking> { + let (sha, ctx) = Self::new_internal(sha); + Self(sha, ctx) + } + } + + /// Automatically implement Deref + DerefMut to get access to inner context + impl<'a, DM: crate::Mode> core::ops::Deref for $name<'a, DM> { + type Target = Context; + + fn deref(&self) -> &Self::Target { + &self.1 + } + } + + impl<'a, DM: crate::Mode> core::ops::DerefMut for $name<'a, DM> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.1 + } + } + + /// Implement Default to create hasher out of thin air + /// TODO: Ensure safety is when using multiple Sha at once. + impl<'d> core::default::Default for $name<'d, crate::Blocking> { + fn default() -> Self { + let sha = unsafe { crate::peripherals::SHA::steal() }; + let (sha, ctx) = Self::new_internal(sha); + Self(sha, ctx) + } + } + + impl<'d> $crate::sha::Sha<'d, crate::Blocking> for $name<'d, crate::Blocking> { + #[cfg(not(esp32))] + fn mode_as_bits() -> u8 { + $mode_bits + } + + fn chunk_length(&self) -> usize { + $chunk_length + } + + fn digest_length(&self) -> usize { + $digest_length + } + + // ESP32 uses different registers for its operation + #[cfg(esp32)] + fn load_reg(&self) { + unsafe { self.0.sha1_load().write(|w| w.bits(1)) }; + } + + #[cfg(esp32)] + fn is_busy(&self) -> bool { + paste::paste! { + self.0.[< $name:lower _busy >]().read().[< $name:lower _busy >]().bit_is_set() + } + } + + #[cfg(esp32)] + fn process_buffer(&mut self) { + if self.first_run { + self.0.sha1_start().write(|w| unsafe { w.bits(1) }); + self.first_run = false; + } else { + self.0.sha1_continue().write(|w| unsafe { w.bits(1) }); + } + } + } + + /// implement digest traits if digest feature is present. + /// Note: digest has a blanket trait implementation for [digest::Digest] for any element + /// that implements FixedOutput + Default + Update + HashMarker + #[cfg(feature = "digest")] + impl<'d, DM: crate::Mode> digest::HashMarker for $name<'d, DM> {} + + #[cfg(feature = "digest")] + impl<'a, DM: crate::Mode> digest::OutputSizeUser for $name<'a, DM> { + // We use paste to append `U` to the digest size to match a const defined in digest + paste::paste! { + type OutputSize = digest::consts::[< U $digest_length >]; + } + } + + #[cfg(feature = "digest")] + impl<'a> digest::Update for $name<'a, crate::Blocking> { + fn update(&mut self, data: &[u8]) { + let mut remaining = data.as_ref(); + while remaining.len() > 0 { + remaining = nb::block!(Sha::update(self, remaining)).unwrap(); + } + } + } + + #[cfg(feature = "digest")] + impl<'a> digest::FixedOutput for $name<'a, crate::Blocking> { + fn finalize_into(mut self, out: &mut digest::Output) { + nb::block!(self.finish(out)).unwrap() + } + } + }; +} + +// All the hash algorithms introduced in FIPS PUB 180-4 Spec. +// – SHA-1 +// – SHA-224 +// – SHA-256 +// – SHA-384 +// – SHA-512 +// – SHA-512/224 +// – SHA-512/256 +// – SHA-512/t (not implemented yet) +// Two working modes +// – Typical SHA +// – DMA-SHA (not implemented yet) +// +// TODO: Allow/Implement SHA512_(u16) +impl_sha!(Sha1, 0, 20, 64); +#[cfg(not(esp32))] +impl_sha!(Sha224, 1, 28, 64); +impl_sha!(Sha256, 2, 32, 64); +#[cfg(any(esp32, esp32s2, esp32s3))] +impl_sha!(Sha384, 3, 48, 128); +#[cfg(any(esp32, esp32s2, esp32s3))] +impl_sha!(Sha512, 4, 64, 128); +#[cfg(any(esp32s2, esp32s3))] +impl_sha!(Sha512_224, 5, 28, 128); +#[cfg(any(esp32s2, esp32s3))] +impl_sha!(Sha512_256, 6, 32, 128); diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index b1585ddc9d4..50c07587208 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -137,13 +137,14 @@ embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = [ embedded-hal-async = { version = "1.0.0", optional = true } embedded-hal-nb = { version = "1.0.0", optional = true } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "panic-handler", "defmt", "semihosting"] } -esp-hal = { path = "../esp-hal", features = ["defmt", "embedded-hal", "embedded-hal-02"], optional = true } +esp-hal = { path = "../esp-hal", features = ["defmt", "embedded-hal", "embedded-hal-02", "digest"], optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } portable-atomic = "1.6.0" static_cell = { version = "2.1.0", features = ["nightly"] } [dev-dependencies] crypto-bigint = { version = "0.5.5", default-features = false } +digest = { version = "0.10.7", default-features = false } elliptic-curve = { version = "0.13.8", default-features = false, features = ["sec1"] } embassy-executor = { version = "0.5.0", default-features = false } # Add the `embedded-test/defmt` feature for more verbose testing diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index e9e010edbb7..fe523fcd846 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -10,7 +10,7 @@ use esp_backtrace as _; use esp_hal::{ peripherals::Peripherals, prelude::*, - sha::{Sha, ShaMode}, + sha::{Sha, Sha1, Sha256}, }; use nb::block; @@ -27,7 +27,7 @@ mod tests { #[test] fn test_sha_1() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA1); + let mut sha = Sha1::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -46,20 +46,34 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + fn test_sha_1_digest() { + let mut sha: Sha1 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0x57, 0xf5, 0x3e, 0xd5, 0x59, 0x85, 0x24, 0x49, 0x3e, 0xc5, 0x76, 0x77, 0xa, 0xaf, + 0x3b, 0xb1, 0x0, 0x63, 0xe3, 0xce, + ]; + digest::Digest::update(&mut sha, source_data); + let output: [u8; 20] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] #[cfg(not(feature = "esp32"))] fn test_sha_224() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA224); + let mut sha = esp_hal::sha::Sha224::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; let expected_output = [ 0x3b, 0x29, 0x33, 0xca, 0xfa, 0x6, 0xc0, 0x29, 0x68, 0x10, 0xa1, 0x3e, 0x54, 0x5f, - 0x25, 0x40, 0xa4, 0x35, 0x17, 0x3, 0x6d, 0xa2, 0xb, 0xeb, 0x8c, 0xbe, 0x79, 0x3b, 0xb6, - 0xa8, 0x8c, 0xff, + 0x25, 0x40, 0xa4, 0x35, 0x17, 0x3, 0x6d, 0xa2, 0xb, 0xeb, 0x8c, 0xbe, 0x79, 0x3b, ]; - let mut output = [0u8; 32]; + let mut output = [0u8; 28]; while remaining.len() > 0 { remaining = block!(sha.update(remaining)).unwrap(); @@ -69,10 +83,27 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + #[cfg(not(feature = "esp32"))] + fn test_sha_224_digest() { + let mut sha: esp_hal::sha::Sha224 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0x3b, 0x29, 0x33, 0xca, 0xfa, 0x6, 0xc0, 0x29, 0x68, 0x10, 0xa1, 0x3e, 0x54, 0x5f, + 0x25, 0x40, 0xa4, 0x35, 0x17, 0x3, 0x6d, 0xa2, 0xb, 0xeb, 0x8c, 0xbe, 0x79, 0x3b, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 28] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] fn test_sha_256() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA256); + let mut sha = Sha256::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -91,11 +122,28 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + fn test_sha_256_digest() { + let mut sha: Sha256 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0x1e, 0xbb, 0xda, 0xb3, 0x35, 0xe0, 0x54, 0x01, 0x5f, 0x0f, 0xc1, 0x7f, 0x62, 0x77, + 0x06, 0x09, 0x72, 0x3d, 0x92, 0xc6, 0x40, 0xb6, 0x5b, 0xa9, 0x97, 0x4d, 0x66, 0x6c, + 0x36, 0x4a, 0x3a, 0x63, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 32] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] fn test_sha_384() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA384); + let mut sha = esp_hal::sha::Sha384::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -114,11 +162,30 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + fn test_sha_384_digest() { + let mut sha: esp_hal::sha::Sha384 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0x8a, 0x1d, 0xe0, 0x7f, 0xa9, 0xc, 0x4c, 0xbb, 0xac, 0xe4, 0x62, 0xbd, 0xd9, 0x2f, + 0x90, 0x88, 0x61, 0x69, 0x40, 0xc0, 0x55, 0x6b, 0x80, 0x6, 0xaa, 0xfc, 0xd4, 0xff, + 0xc1, 0x8, 0xe9, 0xb2, 0xcd, 0xd8, 0xa9, 0x77, 0x36, 0x98, 0x2e, 0x36, 0x3f, 0x69, + 0xa0, 0x7a, 0x20, 0xfa, 0x1c, 0xeb, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 48] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512); + let mut sha = esp_hal::sha::Sha512::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -137,11 +204,31 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + fn test_sha_512_digest() { + let mut sha: esp_hal::sha::Sha512 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0xee, 0x8d, 0x0e, 0x15, 0xde, 0xdc, 0xd8, 0xc8, 0x86, 0xa2, 0xef, 0xb1, 0xac, 0x6a, + 0x49, 0xcf, 0xd8, 0x3f, 0x67, 0x65, 0x64, 0xb3, 0x00, 0xce, 0x48, 0x51, 0x5e, 0xce, + 0x5f, 0x4b, 0xee, 0x10, 0xe1, 0x1d, 0x89, 0xc2, 0x1c, 0x21, 0x81, 0x53, 0xc3, 0xb2, + 0x31, 0xab, 0x77, 0xca, 0xed, 0xc9, 0x6c, 0x24, 0xd7, 0xe5, 0x9a, 0x94, 0x86, 0x80, + 0xe1, 0x51, 0x00, 0x1a, 0xe1, 0x8c, 0xec, 0x80, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 64] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512_224() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512_224); + let mut sha = esp_hal::sha::Sha512_224::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -160,11 +247,28 @@ mod tests { assert_eq!(expected_output, output); } + #[test] + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + fn test_sha_512_224_digest() { + let mut sha: esp_hal::sha::Sha512_224 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0x19, 0xf2, 0xb3, 0x88, 0x22, 0x86, 0x94, 0x38, 0xee, 0x24, 0xc1, 0xc3, 0xb0, 0xb1, + 0x21, 0x6a, 0xf4, 0x81, 0x14, 0x8f, 0x4, 0x34, 0xfd, 0xd7, 0x54, 0x3, 0x2b, 0x88, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 28] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } + #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512_256() { let peripherals = Peripherals::take(); - let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA512_256); + let mut sha = esp_hal::sha::Sha512_256::new(peripherals.SHA); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -182,4 +286,22 @@ mod tests { assert_eq!(expected_output, output); } + + #[test] + #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] + fn test_sha_512_256_digest() { + let mut sha: esp_hal::sha::Sha512_256 = digest::Digest::new(); + + let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); + let expected_output = [ + 0xb7, 0x49, 0x4e, 0xe1, 0xdb, 0xcd, 0xe5, 0x47, 0x5a, 0x61, 0x25, 0xac, 0x27, 0xc2, + 0x1b, 0x53, 0xcd, 0x6b, 0x16, 0x33, 0xb4, 0x94, 0xac, 0xa4, 0x2a, 0xe6, 0x99, 0x2f, + 0xe7, 0xd, 0x83, 0x19, + ]; + + digest::Digest::update(&mut sha, source_data); + let output: [u8; 32] = digest::Digest::finalize(sha).into(); + + assert_eq!(expected_output, output); + } } From fc63658f120ff7a5a057756a296f2299311fbe13 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:47:22 -0400 Subject: [PATCH 2/9] Fix CI. Fix wrong sha mode for esp32 --- esp-hal/src/sha/mod.rs | 34 ++++++++++++++++++++-------------- hil-test/Cargo.toml | 2 +- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/esp-hal/src/sha/mod.rs b/esp-hal/src/sha/mod.rs index b4d62e4239d..516462ae289 100644 --- a/esp-hal/src/sha/mod.rs +++ b/esp-hal/src/sha/mod.rs @@ -32,12 +32,12 @@ //! ```rust, no_run #![doc = crate::before_snippet!()] //! # use esp_hal::sha::Sha; -//! # use esp_hal::sha::ShaMode; +//! # use esp_hal::sha::Sha256; //! # use core::option::Option::None; //! # use nb::block; //! let source_data = "HELLO, ESPRESSIF!".as_bytes(); //! let mut remaining = source_data; -//! let mut hasher = Sha::new(peripherals.SHA, ShaMode::SHA256); +//! let mut hasher = Sha256::new(peripherals.SHA); //! // Short hashes can be created by decreasing the output buffer to the //! // desired length //! let mut output = [0u8; 32]; @@ -126,7 +126,8 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { fn digest_length(&self) -> usize; - /// ESP32 requires that a control register to be written to calculate the final SHA hash. + /// ESP32 requires that a control register to be written to calculate the + /// final SHA hash. #[cfg(esp32)] fn load_reg(&self); @@ -349,8 +350,8 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { } } -/// This macro implements the Sha<'a, DM> trait for a specified Sha algorithm and a set of -/// parameters +/// This macro implements the Sha<'a, DM> trait for a specified Sha algorithm +/// and a set of parameters macro_rules! impl_sha { ($name: ident, $mode_bits: tt, $digest_length: tt, $chunk_length: tt) => { pub struct $name<'d, DM: crate::Mode>(PeripheralRef<'d, SHA>, Context); @@ -406,7 +407,9 @@ macro_rules! impl_sha { // ESP32 uses different registers for its operation #[cfg(esp32)] fn load_reg(&self) { - unsafe { self.0.sha1_load().write(|w| w.bits(1)) }; + paste::paste! { + unsafe { self.0.[< $name:lower _load >]().write(|w| w.bits(1)) }; + } } #[cfg(esp32)] @@ -418,24 +421,27 @@ macro_rules! impl_sha { #[cfg(esp32)] fn process_buffer(&mut self) { - if self.first_run { - self.0.sha1_start().write(|w| unsafe { w.bits(1) }); - self.first_run = false; - } else { - self.0.sha1_continue().write(|w| unsafe { w.bits(1) }); + paste::paste! { + if self.first_run { + self.0.[< $name:lower _start >]().write(|w| unsafe { w.bits(1) }); + self.first_run = false; + } else { + self.0.[< $name:lower _continue >]().write(|w| unsafe { w.bits(1) }); + } } } } /// implement digest traits if digest feature is present. - /// Note: digest has a blanket trait implementation for [digest::Digest] for any element - /// that implements FixedOutput + Default + Update + HashMarker + /// Note: digest has a blanket trait implementation for [digest::Digest] for any + /// element that implements FixedOutput + Default + Update + HashMarker #[cfg(feature = "digest")] impl<'d, DM: crate::Mode> digest::HashMarker for $name<'d, DM> {} #[cfg(feature = "digest")] impl<'a, DM: crate::Mode> digest::OutputSizeUser for $name<'a, DM> { - // We use paste to append `U` to the digest size to match a const defined in digest + // We use paste to append `U` to the digest size to match a const defined in + // digest paste::paste! { type OutputSize = digest::consts::[< U $digest_length >]; } diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 50c07587208..419f4ed9a8f 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -137,7 +137,7 @@ embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", features = [ embedded-hal-async = { version = "1.0.0", optional = true } embedded-hal-nb = { version = "1.0.0", optional = true } esp-backtrace = { path = "../esp-backtrace", default-features = false, features = ["exception-handler", "panic-handler", "defmt", "semihosting"] } -esp-hal = { path = "../esp-hal", features = ["defmt", "embedded-hal", "embedded-hal-02", "digest"], optional = true } +esp-hal = { path = "../esp-hal", features = ["defmt", "digest", "embedded-hal", "embedded-hal-02"], optional = true } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } portable-atomic = "1.6.0" static_cell = { version = "2.1.0", features = ["nightly"] } From e315c4306583186fabc169a4fc0683818ad3e4bf Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Thu, 8 Aug 2024 01:56:59 -0400 Subject: [PATCH 3/9] Save hash register for interleaving operation An example (wip) `sha_fuzz.rs` was added to test different functionalities of the SHA driver and to ensure proper functionning under all cases. --- esp-hal/src/sha/mod.rs | 30 +++++ examples/Cargo.toml | 3 +- examples/src/bin/sha_fuzz.rs | 229 +++++++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 examples/src/bin/sha_fuzz.rs diff --git a/esp-hal/src/sha/mod.rs b/esp-hal/src/sha/mod.rs index 516462ae289..4af0339f71d 100644 --- a/esp-hal/src/sha/mod.rs +++ b/esp-hal/src/sha/mod.rs @@ -75,6 +75,8 @@ pub struct Context { cursor: usize, first_run: bool, finished: bool, + /// Saved digest (SHA_H_n_REG) for interleaving operation + saved_digest: Option<[u8; 64]>, phantom: PhantomData, } @@ -221,8 +223,35 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { self.finished = false; + let sha = unsafe { crate::peripherals::SHA::steal() }; + // Restore previously saved hash for interleaving operation. + if let Some(ref saved_digest) = self.saved_digest.take() { + self.alignment_helper.volatile_write_regset( + #[cfg(esp32)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.h_mem(0).as_ptr(), + saved_digest, + 64, + ); + } + let remaining = self.write_data(buffer)?; + // Wait until previous operation finished processing + while self.is_busy() {} + // Save the content of the current hash for interleaving operation. + let mut saved_digest = [0u8; 64]; + self.alignment_helper.volatile_read_regset( + #[cfg(esp32)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.h_mem(0).as_ptr(), + &mut saved_digest, + 64 / self.alignment_helper.align_size(), + ); + self.saved_digest.replace(saved_digest); + Ok(remaining) } @@ -344,6 +373,7 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { first_run: true, finished: false, alignment_helper: AlignmentHelper::default(), + saved_digest: None, phantom: PhantomData, }, ) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index e3bb27e0060..1fefc7bbe1d 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -31,7 +31,7 @@ embedded-io-async = "0.6.1" embedded-storage = "0.3.1" esp-alloc = { path = "../esp-alloc" } esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } -esp-hal = { path = "../esp-hal", features = ["log"] } +esp-hal = { path = "../esp-hal", features = ["log", "digest"] } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-hal-smartled = { path = "../esp-hal-smartled", optional = true } esp-ieee802154 = { path = "../esp-ieee802154", optional = true } @@ -49,6 +49,7 @@ nb = "1.1.0" p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] } portable-atomic = { version = "1.6.0", default-features = false } +sha1 = { version = "0.10.6", default-features = false } sha2 = { version = "0.10.8", default-features = false } smart-leds = "0.4.0" smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw"] } diff --git a/examples/src/bin/sha_fuzz.rs b/examples/src/bin/sha_fuzz.rs new file mode 100644 index 00000000000..1ae817005c1 --- /dev/null +++ b/examples/src/bin/sha_fuzz.rs @@ -0,0 +1,229 @@ +//! Demonstrates the use of the SHA peripheral and compares the speed of +//! hardware-accelerated and pure software hashing. +//! +//! This also includes a series of testing to ensure the proper functioning of the peripheral +//! +//! For simplicity purpose, every hasher is declared using it's full path, to help differenciate +//! between Hardware acceleration and software. Hashers starting with `esp_hal` such as +//! [esp_hal::sha::Sha256] use the hardware accelerated peripheral, and starting with `sha2` such +//! as [sha2::Sha256] use the software implementation. + +#![no_std] +#![no_main] + +use esp_backtrace as _; +use esp_hal::{ + clock::ClockControl, entry, peripherals::Peripherals, prelude::*, sha::Sha, + system::SystemControl, +}; +use esp_println::println; +use nb::block; +use sha2::Digest; + +/// Dummy data used to feed the hasher. +static CHAR_ARRAY: [u8; 4096] = [b'a'; 4096]; + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger(log::LevelFilter::Warn); + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + for i in 1..512 { + println!("Testing with {} chars", i); + + if !rolling_with_digest_trait(i) { + println!("Rolling digest {} Failed", i); + // panic!("failed"); + } + + if !test_for_size::, 20>(i) { + println!("SHA1 {} Failed", i); + // panic!("failed"); + } + #[cfg(not(feature = "esp32"))] + if !test_for_size::, 28>(i) { + println!("SHA224 {} Failed", i); + // panic!("failed"); + } + if !test_for_size::, 32>(i) { + println!("SHA256 {} Failed", i); + // panic!("failed"); + } + + // Notes: SHA512 and SHA384 are broken for now for certain lengths. + if !test_for_size::, 48>(i) { + println!("SHA384 {} Failed", i); + // panic!("failed"); + } + if !test_for_size::, 64>(i) { + log::error!("SHA512 {} Failed", i); + // panic!("failed"); + } + } + + println!("Done!"); + + loop {} +} + +/// A rolling test that loops between hasher for every step to test interleaving. +/// +/// Returns true if the test succeed, else false. +fn rolling_with_digest_trait(size: usize) -> bool { + let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; + + let mut sha1 = esp_hal::sha::Sha1::default(); + #[cfg(not(feature = "esp32"))] + let mut sha224 = esp_hal::sha::Sha224::default(); + let mut sha256 = esp_hal::sha::Sha256::default(); + + // The Digest::update will consume the entirety of remaining. We don't need to loop until + // remaining is fully consumed. + Digest::update(&mut sha1, source_data); + #[cfg(not(feature = "esp32"))] + Digest::update(&mut sha224, source_data); + Digest::update(&mut sha256, source_data); + + let sha1_output: [u8; 20] = Digest::finalize(sha1).into(); + #[cfg(not(feature = "esp32"))] + let sha224_output: [u8; 28] = Digest::finalize(sha224).into(); + let sha256_output: [u8; 32] = Digest::finalize(sha256).into(); + + // Calculate software result to compare against + // Sha1 + let mut sha1_sw = sha1::Sha1::new(); + sha1_sw.update(source_data); + let soft_result = sha1_sw.finalize(); + for (a, b) in sha1_output.iter().zip(soft_result) { + if *a != b { + log::error!("HW: {:02x?}", sha1_output); + log::error!("SW: {:02x?}", soft_result); + return false; + } + } + + // Sha224 + #[cfg(not(feature = "esp32"))] + { + let mut sha224_sw = sha2::Sha224::new(); + sha224_sw.update(source_data); + let soft_result = sha224_sw.finalize(); + for (a, b) in sha224_output.iter().zip(soft_result) { + if *a != b { + log::error!("HW: {:02x?}", sha224_output); + log::error!("SW: {:02x?}", soft_result); + return false; + } + } + } + + // Sha256 + let mut sha256_sw = sha2::Sha256::new(); + sha256_sw.update(source_data); + let soft_result = sha256_sw.finalize(); + for (a, b) in sha256_output.iter().zip(soft_result) { + if *a != b { + log::error!("HW: {:02x?}", sha256_output); + log::error!("SW: {:02x?}", soft_result); + return false; + } + } + + true +} + +/// A simple test using [esp_hal::sha::Sha] trait to test hashing for an algorithm against a +/// specific size. This will compare the result with a software implementation and return false if +/// there's a mismatch +fn test_for_size<'a, D: Digest + Default + Sha<'a, esp_hal::Blocking>, const N: usize>( + size: usize, +) -> bool { + let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; + let mut remaining = source_data; + let mut hasher = D::default(); + + // Short hashes can be created by decreasing the output buffer to the desired + // length + let mut output = [0u8; N]; + + // The hardware implementation takes a subslice of the input, and returns the + // unprocessed parts The unprocessed parts can be input in the next + // iteration, you can always add more data until finish() is called. After + // finish() is called update()'s will contribute to a new hash which + // can be extracted again with finish(). + while remaining.len() > 0 { + // Can add println to view progress, however println takes a few orders of + // magnitude longer than the Sha function itself so not useful for + // comparing processing time println!("Remaining len: {}", + // remaining.len()); + + // All the HW Sha functions are infallible so unwrap is fine to use if you use + // block! + remaining = block!(Sha::update(&mut hasher, &remaining)).unwrap(); + } + + // Finish can be called as many times as desired to get multiple copies of the + // output. + block!(hasher.finish(output.as_mut_slice())).unwrap(); + + // Compare against Software result. + match N { + 20 => { + let mut hasher = sha1::Sha1::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + if *a != b { + return false; + } + } + } + 28 => { + let mut hasher = sha2::Sha224::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + if *a != b { + return false; + } + } + } + 32 => { + let mut hasher = sha2::Sha256::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + if *a != b { + return false; + } + } + } + 48 => { + let mut hasher = sha2::Sha384::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + if *a != b { + return false; + } + } + } + 64 => { + let mut hasher = sha2::Sha512::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + if *a != b { + log::error!("HW: {:02x?}", output); + log::error!("SW: {:02x?}", soft_result); + return false; + } + } + } + _ => todo!(), + }; + + true +} From 3fa92bdb6306bced551bfd2f2397db5497760906 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Thu, 8 Aug 2024 16:09:36 -0400 Subject: [PATCH 4/9] Use random data when testing SHA --- esp-hal/src/{sha/mod.rs => sha.rs} | 5 +++ examples/src/bin/sha_fuzz.rs | 63 +++++++++++++++++++++++------- 2 files changed, 54 insertions(+), 14 deletions(-) rename esp-hal/src/{sha/mod.rs => sha.rs} (99%) diff --git a/esp-hal/src/sha/mod.rs b/esp-hal/src/sha.rs similarity index 99% rename from esp-hal/src/sha/mod.rs rename to esp-hal/src/sha.rs index 4af0339f71d..2154c5af87a 100644 --- a/esp-hal/src/sha/mod.rs +++ b/esp-hal/src/sha.rs @@ -120,6 +120,9 @@ impl Context { // This implementation might fail after u32::MAX/8 bytes, to increase please see // ::finish() length/self.cursor usage pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { + /// Constant containing the name of the algorithm as a string. + const ALGORITHM: &'static str; + /// Setup SHA Mode #[cfg(not(esp32))] fn mode_as_bits() -> u8; @@ -421,6 +424,8 @@ macro_rules! impl_sha { } impl<'d> $crate::sha::Sha<'d, crate::Blocking> for $name<'d, crate::Blocking> { + const ALGORITHM: &'static str = stringify!($name); + #[cfg(not(esp32))] fn mode_as_bits() -> u8 { $mode_bits diff --git a/examples/src/bin/sha_fuzz.rs b/examples/src/bin/sha_fuzz.rs index 1ae817005c1..6a517cd5c6f 100644 --- a/examples/src/bin/sha_fuzz.rs +++ b/examples/src/bin/sha_fuzz.rs @@ -13,7 +13,12 @@ use esp_backtrace as _; use esp_hal::{ - clock::ClockControl, entry, peripherals::Peripherals, prelude::*, sha::Sha, + clock::ClockControl, + entry, + peripherals::Peripherals, + prelude::*, + rng::Rng, + sha::Sha, system::SystemControl, }; use esp_println::println; @@ -23,6 +28,15 @@ use sha2::Digest; /// Dummy data used to feed the hasher. static CHAR_ARRAY: [u8; 4096] = [b'a'; 4096]; +/// Dummy random data used to feed the Sha1 hasher +static mut SHA1_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; + +/// Dummy random data used to feed the Sha224 hasher +static mut SHA224_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; + +/// Dummy random data used to feed the Sha256 hasher +static mut SHA256_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; + #[entry] fn main() -> ! { esp_println::logger::init_logger(log::LevelFilter::Warn); @@ -30,6 +44,20 @@ fn main() -> ! { let system = SystemControl::new(peripherals.SYSTEM); let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut rng = Rng::new(peripherals.RNG); + + // Fill source data with random data + use core::ptr::addr_of_mut; + for slice in unsafe { + [ + addr_of_mut!(SHA1_RANDOM_ARRAY), + addr_of_mut!(SHA224_RANDOM_ARRAY), + addr_of_mut!(SHA256_RANDOM_ARRAY), + ] + } { + rng.read(unsafe { &mut *slice }); + } + for i in 1..512 { println!("Testing with {} chars", i); @@ -53,10 +81,12 @@ fn main() -> ! { } // Notes: SHA512 and SHA384 are broken for now for certain lengths. + #[cfg(any(esp32, esp32s2, esp32s3))] if !test_for_size::, 48>(i) { println!("SHA384 {} Failed", i); // panic!("failed"); } + #[cfg(any(esp32, esp32s2, esp32s3))] if !test_for_size::, 64>(i) { log::error!("SHA512 {} Failed", i); // panic!("failed"); @@ -72,7 +102,12 @@ fn main() -> ! { /// /// Returns true if the test succeed, else false. fn rolling_with_digest_trait(size: usize) -> bool { - let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; + // Use different random data for each hasher. + let sha1_source_data = unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(not(feature = "esp32"))] + let sha224_source_data = unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; + let sha256_source_data = + unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; let mut sha1 = esp_hal::sha::Sha1::default(); #[cfg(not(feature = "esp32"))] @@ -81,10 +116,10 @@ fn rolling_with_digest_trait(size: usize) -> bool { // The Digest::update will consume the entirety of remaining. We don't need to loop until // remaining is fully consumed. - Digest::update(&mut sha1, source_data); + Digest::update(&mut sha1, sha1_source_data); #[cfg(not(feature = "esp32"))] - Digest::update(&mut sha224, source_data); - Digest::update(&mut sha256, source_data); + Digest::update(&mut sha224, sha224_source_data); + Digest::update(&mut sha256, sha256_source_data); let sha1_output: [u8; 20] = Digest::finalize(sha1).into(); #[cfg(not(feature = "esp32"))] @@ -94,12 +129,12 @@ fn rolling_with_digest_trait(size: usize) -> bool { // Calculate software result to compare against // Sha1 let mut sha1_sw = sha1::Sha1::new(); - sha1_sw.update(source_data); + sha1_sw.update(sha1_source_data); let soft_result = sha1_sw.finalize(); for (a, b) in sha1_output.iter().zip(soft_result) { if *a != b { - log::error!("HW: {:02x?}", sha1_output); - log::error!("SW: {:02x?}", soft_result); + log::error!("SHA1 HW: {:02x?}", sha1_output); + log::error!("SHA1 SW: {:02x?}", soft_result); return false; } } @@ -108,12 +143,12 @@ fn rolling_with_digest_trait(size: usize) -> bool { #[cfg(not(feature = "esp32"))] { let mut sha224_sw = sha2::Sha224::new(); - sha224_sw.update(source_data); + sha224_sw.update(sha224_source_data); let soft_result = sha224_sw.finalize(); for (a, b) in sha224_output.iter().zip(soft_result) { if *a != b { - log::error!("HW: {:02x?}", sha224_output); - log::error!("SW: {:02x?}", soft_result); + log::error!("SHA224 HW: {:02x?}", sha224_output); + log::error!("SHA224 SW: {:02x?}", soft_result); return false; } } @@ -121,12 +156,12 @@ fn rolling_with_digest_trait(size: usize) -> bool { // Sha256 let mut sha256_sw = sha2::Sha256::new(); - sha256_sw.update(source_data); + sha256_sw.update(sha256_source_data); let soft_result = sha256_sw.finalize(); for (a, b) in sha256_output.iter().zip(soft_result) { if *a != b { - log::error!("HW: {:02x?}", sha256_output); - log::error!("SW: {:02x?}", soft_result); + log::error!("SHA256 HW: {:02x?}", sha256_output); + log::error!("SHA256 SW: {:02x?}", soft_result); return false; } } From 3e2c1cbe1e4a9c6891b3cb4a49b60693e804df46 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Fri, 9 Aug 2024 17:57:52 -0400 Subject: [PATCH 5/9] fix(SHA): Buffer words until a full block before writing to memory This fixes interleaving operations by buffering words into the SHA context until a full block can be processed. --- esp-hal/src/sha.rs | 83 +++++++++++++++---------- examples/src/bin/sha_fuzz.rs | 115 ++++++++++++++++++++++++++++++----- 2 files changed, 151 insertions(+), 47 deletions(-) diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 2154c5af87a..5009feb1808 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -75,7 +75,10 @@ pub struct Context { cursor: usize, first_run: bool, finished: bool, + /// Buffered bytes (SHA_M_n_REG) to be processed. + buffer: [u32; 32], /// Saved digest (SHA_H_n_REG) for interleaving operation + #[cfg(not(esp32))] saved_digest: Option<[u8; 64]>, phantom: PhantomData, } @@ -160,6 +163,14 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { sha.start().write(|w| unsafe { w.bits(1) }); self.first_run = false; } else { + // Restore previously saved hash if interleaving operation + if let Some(ref saved_digest) = self.saved_digest.take() { + self.alignment_helper.volatile_write_regset( + sha.h_mem(0).as_ptr(), + saved_digest, + 64, + ); + } // SET SHA_CONTINUE_REG sha.continue_().write(|w| unsafe { w.bits(1) }); } @@ -173,6 +184,18 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { let chunk_len = self.chunk_length(); + // Flush aligned buffer in memory before flushing alignment_helper + unsafe { + core::ptr::copy_nonoverlapping( + self.buffer.as_ptr(), + #[cfg(esp32)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.m_mem(0).as_ptr(), + 32, + ); + } + let ctx = self.deref_mut(); let flushed = ctx.alignment_helper.flush_to( #[cfg(esp32)] @@ -200,11 +223,9 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { let chunk_len = self.chunk_length(); let ctx = self.deref_mut(); + // Buffer the incoming bytes into u32 aligned words. let (remaining, bound_reached) = ctx.alignment_helper.aligned_volatile_copy( - #[cfg(esp32)] - sha.text(0).as_ptr(), - #[cfg(not(esp32))] - sha.m_mem(0).as_ptr(), + ctx.buffer.as_mut_ptr(), incoming, chunk_len / ctx.alignment_helper.align_size(), mod_cursor / ctx.alignment_helper.align_size(), @@ -212,8 +233,33 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { self.cursor = self.cursor.wrapping_add(incoming.len() - remaining.len()); + // If bound reached we write the buffer to memory and process it. if bound_reached { + unsafe { + core::ptr::copy_nonoverlapping( + self.buffer.as_ptr(), + #[cfg(esp32)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.m_mem(0).as_ptr(), + 32, + ); + } self.process_buffer(); + + // Wait until buffer has completely processed + while self.is_busy() {} + // Save the content of the current hash for interleaving operation. + #[cfg(not(esp32))] + { + let mut saved_digest = [0u8; 64]; + self.alignment_helper.volatile_read_regset( + sha.h_mem(0).as_ptr(), + &mut saved_digest, + 64 / self.alignment_helper.align_size(), + ); + self.saved_digest.replace(saved_digest); + } } Ok(remaining) @@ -226,35 +272,8 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { self.finished = false; - let sha = unsafe { crate::peripherals::SHA::steal() }; - // Restore previously saved hash for interleaving operation. - if let Some(ref saved_digest) = self.saved_digest.take() { - self.alignment_helper.volatile_write_regset( - #[cfg(esp32)] - sha.text(0).as_ptr(), - #[cfg(not(esp32))] - sha.h_mem(0).as_ptr(), - saved_digest, - 64, - ); - } - let remaining = self.write_data(buffer)?; - // Wait until previous operation finished processing - while self.is_busy() {} - // Save the content of the current hash for interleaving operation. - let mut saved_digest = [0u8; 64]; - self.alignment_helper.volatile_read_regset( - #[cfg(esp32)] - sha.text(0).as_ptr(), - #[cfg(not(esp32))] - sha.h_mem(0).as_ptr(), - &mut saved_digest, - 64 / self.alignment_helper.align_size(), - ); - self.saved_digest.replace(saved_digest); - Ok(remaining) } @@ -376,6 +395,8 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { first_run: true, finished: false, alignment_helper: AlignmentHelper::default(), + buffer: [0u32; 32], + #[cfg(not(esp32))] saved_digest: None, phantom: PhantomData, }, diff --git a/examples/src/bin/sha_fuzz.rs b/examples/src/bin/sha_fuzz.rs index 6a517cd5c6f..bf6b9f65b8e 100644 --- a/examples/src/bin/sha_fuzz.rs +++ b/examples/src/bin/sha_fuzz.rs @@ -39,7 +39,7 @@ static mut SHA256_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; #[entry] fn main() -> ! { - esp_println::logger::init_logger(log::LevelFilter::Warn); + esp_println::logger::init_logger(log::LevelFilter::Error); let peripherals = Peripherals::take(); let system = SystemControl::new(peripherals.SYSTEM); let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); @@ -58,38 +58,36 @@ fn main() -> ! { rng.read(unsafe { &mut *slice }); } - for i in 1..512 { + for i in 1..=1024 { println!("Testing with {} chars", i); + if !rolling_with_sha_trait(i) { + panic!("Rolling sha {} Failed", i); + } + if !rolling_with_digest_trait(i) { - println!("Rolling digest {} Failed", i); - // panic!("failed"); + panic!("Rolling digest {} Failed", i); } if !test_for_size::, 20>(i) { - println!("SHA1 {} Failed", i); - // panic!("failed"); + panic!("SHA1 {} Failed", i); } #[cfg(not(feature = "esp32"))] if !test_for_size::, 28>(i) { - println!("SHA224 {} Failed", i); - // panic!("failed"); + panic!("SHA224 {} Failed", i); } if !test_for_size::, 32>(i) { - println!("SHA256 {} Failed", i); - // panic!("failed"); + panic!("SHA256 {} Failed", i); } // Notes: SHA512 and SHA384 are broken for now for certain lengths. - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] if !test_for_size::, 48>(i) { - println!("SHA384 {} Failed", i); - // panic!("failed"); + log::error!("SHA384 {} Failed", i); } - #[cfg(any(esp32, esp32s2, esp32s3))] + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] if !test_for_size::, 64>(i) { log::error!("SHA512 {} Failed", i); - // panic!("failed"); } } @@ -98,6 +96,90 @@ fn main() -> ! { loop {} } +/// A rolling test that loops between hasher for every step to test interleaving. +/// +/// Returns true if the test succeed, else false. +fn rolling_with_sha_trait(size: usize) -> bool { + // Use different random data for each hasher. + let sha1_source_data = unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(not(feature = "esp32"))] + let sha224_source_data = + unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; + let sha256_source_data = + unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; + + let mut sha1_remaining = sha1_source_data; + #[cfg(not(feature = "esp32"))] + let mut sha224_remaining = sha224_source_data; + let mut sha256_remaining = sha256_source_data; + + let mut sha1 = esp_hal::sha::Sha1::default(); + #[cfg(not(feature = "esp32"))] + let mut sha224 = esp_hal::sha::Sha224::default(); + let mut sha256 = esp_hal::sha::Sha256::default(); + + // All sources are the same length + while sha1_remaining.len() > 0 { + sha1_remaining = block!(Sha::update(&mut sha1, sha1_remaining)).unwrap(); + #[cfg(not(feature = "esp32"))] + { + sha224_remaining = block!(Sha::update(&mut sha224, sha224_remaining)).unwrap(); + } + sha256_remaining = block!(Sha::update(&mut sha256, sha256_remaining)).unwrap(); + } + + let mut sha1_output = [0u8; 20]; + block!(sha1.finish(sha1_output.as_mut_slice())).unwrap(); + #[cfg(not(feature = "esp32"))] + let mut sha224_output = [0u8; 28]; + #[cfg(not(feature = "esp32"))] + block!(sha224.finish(sha224_output.as_mut_slice())).unwrap(); + let mut sha256_output = [0u8; 32]; + block!(sha256.finish(sha256_output.as_mut_slice())).unwrap(); + + // Calculate software result to compare against + // Sha1 + let mut sha1_sw = sha1::Sha1::new(); + sha1_sw.update(sha1_source_data); + let soft_result = sha1_sw.finalize(); + for (a, b) in sha1_output.iter().zip(soft_result) { + if *a != b { + log::error!("SHA1 HW: {:02x?}", sha1_output); + log::error!("SHA1 SW: {:02x?}", soft_result); + return false; + } + } + + // Sha224 + #[cfg(not(feature = "esp32"))] + { + let mut sha224_sw = sha2::Sha224::new(); + sha224_sw.update(sha224_source_data); + let soft_result = sha224_sw.finalize(); + for (a, b) in sha224_output.iter().zip(soft_result) { + if *a != b { + log::error!("SHA224 HW: {:02x?}", sha224_output); + log::error!("SHA224 SW: {:02x?}", soft_result); + return false; + } + } + } + + // Sha256 + let mut sha256_sw = sha2::Sha256::new(); + sha256_sw.update(sha256_source_data); + let soft_result = sha256_sw.finalize(); + for (a, b) in sha256_output.iter().zip(soft_result) { + if *a != b { + log::error!("SHA256 HW: {:02x?}", sha256_output); + log::error!("SHA256 SW: {:02x?}", soft_result); + return false; + } + } + + true +} + /// A rolling test that loops between hasher for every step to test interleaving. /// /// Returns true if the test succeed, else false. @@ -105,7 +187,8 @@ fn rolling_with_digest_trait(size: usize) -> bool { // Use different random data for each hasher. let sha1_source_data = unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; #[cfg(not(feature = "esp32"))] - let sha224_source_data = unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; + let sha224_source_data = + unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; let sha256_source_data = unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; From 82a2ebf35205cb286cce0817cbaa3dd3beed98a5 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Sat, 10 Aug 2024 00:03:36 -0400 Subject: [PATCH 6/9] Fix(SHA): Use correct length padding for SHA384 and SHA512. - This fixes a long running issue with SHA384 and SHA512, where some digest of specific sizes wouldn't compute correctly, by changing the padding length of the size field. --- esp-hal/src/sha.rs | 7 +- examples/Cargo.toml | 3 +- examples/src/bin/sha_fuzz.rs | 347 ------------------------------- hil-test/Cargo.toml | 2 + hil-test/tests/sha.rs | 387 ++++++++++++++++++++++++++++++++++- 5 files changed, 387 insertions(+), 359 deletions(-) delete mode 100644 examples/src/bin/sha_fuzz.rs diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 5009feb1808..41912cd5ef6 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -247,11 +247,12 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { } self.process_buffer(); - // Wait until buffer has completely processed - while self.is_busy() {} // Save the content of the current hash for interleaving operation. #[cfg(not(esp32))] { + // Wait until buffer has completely processed + while self.is_busy() {} + let mut saved_digest = [0u8; 64]; self.alignment_helper.volatile_read_regset( sha.h_mem(0).as_ptr(), @@ -305,7 +306,7 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { debug_assert!(self.cursor % 4 == 0); let mod_cursor = self.cursor % chunk_len; - if (chunk_len - mod_cursor) < core::mem::size_of::() { + if (chunk_len - mod_cursor) < chunk_len / 8 { // Zero out remaining data if buffer is almost full (>=448/896), and process // buffer let pad_len = chunk_len - mod_cursor; diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 1fefc7bbe1d..e3bb27e0060 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -31,7 +31,7 @@ embedded-io-async = "0.6.1" embedded-storage = "0.3.1" esp-alloc = { path = "../esp-alloc" } esp-backtrace = { path = "../esp-backtrace", features = ["exception-handler", "panic-handler", "println"] } -esp-hal = { path = "../esp-hal", features = ["log", "digest"] } +esp-hal = { path = "../esp-hal", features = ["log"] } esp-hal-embassy = { path = "../esp-hal-embassy", optional = true } esp-hal-smartled = { path = "../esp-hal-smartled", optional = true } esp-ieee802154 = { path = "../esp-ieee802154", optional = true } @@ -49,7 +49,6 @@ nb = "1.1.0" p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] } portable-atomic = { version = "1.6.0", default-features = false } -sha1 = { version = "0.10.6", default-features = false } sha2 = { version = "0.10.8", default-features = false } smart-leds = "0.4.0" smoltcp = { version = "0.11.0", default-features = false, features = [ "medium-ethernet", "socket-raw"] } diff --git a/examples/src/bin/sha_fuzz.rs b/examples/src/bin/sha_fuzz.rs deleted file mode 100644 index bf6b9f65b8e..00000000000 --- a/examples/src/bin/sha_fuzz.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! Demonstrates the use of the SHA peripheral and compares the speed of -//! hardware-accelerated and pure software hashing. -//! -//! This also includes a series of testing to ensure the proper functioning of the peripheral -//! -//! For simplicity purpose, every hasher is declared using it's full path, to help differenciate -//! between Hardware acceleration and software. Hashers starting with `esp_hal` such as -//! [esp_hal::sha::Sha256] use the hardware accelerated peripheral, and starting with `sha2` such -//! as [sha2::Sha256] use the software implementation. - -#![no_std] -#![no_main] - -use esp_backtrace as _; -use esp_hal::{ - clock::ClockControl, - entry, - peripherals::Peripherals, - prelude::*, - rng::Rng, - sha::Sha, - system::SystemControl, -}; -use esp_println::println; -use nb::block; -use sha2::Digest; - -/// Dummy data used to feed the hasher. -static CHAR_ARRAY: [u8; 4096] = [b'a'; 4096]; - -/// Dummy random data used to feed the Sha1 hasher -static mut SHA1_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; - -/// Dummy random data used to feed the Sha224 hasher -static mut SHA224_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; - -/// Dummy random data used to feed the Sha256 hasher -static mut SHA256_RANDOM_ARRAY: [u8; 4096] = [0u8; 4096]; - -#[entry] -fn main() -> ! { - esp_println::logger::init_logger(log::LevelFilter::Error); - let peripherals = Peripherals::take(); - let system = SystemControl::new(peripherals.SYSTEM); - let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); - - let mut rng = Rng::new(peripherals.RNG); - - // Fill source data with random data - use core::ptr::addr_of_mut; - for slice in unsafe { - [ - addr_of_mut!(SHA1_RANDOM_ARRAY), - addr_of_mut!(SHA224_RANDOM_ARRAY), - addr_of_mut!(SHA256_RANDOM_ARRAY), - ] - } { - rng.read(unsafe { &mut *slice }); - } - - for i in 1..=1024 { - println!("Testing with {} chars", i); - - if !rolling_with_sha_trait(i) { - panic!("Rolling sha {} Failed", i); - } - - if !rolling_with_digest_trait(i) { - panic!("Rolling digest {} Failed", i); - } - - if !test_for_size::, 20>(i) { - panic!("SHA1 {} Failed", i); - } - #[cfg(not(feature = "esp32"))] - if !test_for_size::, 28>(i) { - panic!("SHA224 {} Failed", i); - } - if !test_for_size::, 32>(i) { - panic!("SHA256 {} Failed", i); - } - - // Notes: SHA512 and SHA384 are broken for now for certain lengths. - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - if !test_for_size::, 48>(i) { - log::error!("SHA384 {} Failed", i); - } - #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] - if !test_for_size::, 64>(i) { - log::error!("SHA512 {} Failed", i); - } - } - - println!("Done!"); - - loop {} -} - -/// A rolling test that loops between hasher for every step to test interleaving. -/// -/// Returns true if the test succeed, else false. -fn rolling_with_sha_trait(size: usize) -> bool { - // Use different random data for each hasher. - let sha1_source_data = unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; - #[cfg(not(feature = "esp32"))] - let sha224_source_data = - unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; - let sha256_source_data = - unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; - - let mut sha1_remaining = sha1_source_data; - #[cfg(not(feature = "esp32"))] - let mut sha224_remaining = sha224_source_data; - let mut sha256_remaining = sha256_source_data; - - let mut sha1 = esp_hal::sha::Sha1::default(); - #[cfg(not(feature = "esp32"))] - let mut sha224 = esp_hal::sha::Sha224::default(); - let mut sha256 = esp_hal::sha::Sha256::default(); - - // All sources are the same length - while sha1_remaining.len() > 0 { - sha1_remaining = block!(Sha::update(&mut sha1, sha1_remaining)).unwrap(); - #[cfg(not(feature = "esp32"))] - { - sha224_remaining = block!(Sha::update(&mut sha224, sha224_remaining)).unwrap(); - } - sha256_remaining = block!(Sha::update(&mut sha256, sha256_remaining)).unwrap(); - } - - let mut sha1_output = [0u8; 20]; - block!(sha1.finish(sha1_output.as_mut_slice())).unwrap(); - #[cfg(not(feature = "esp32"))] - let mut sha224_output = [0u8; 28]; - #[cfg(not(feature = "esp32"))] - block!(sha224.finish(sha224_output.as_mut_slice())).unwrap(); - let mut sha256_output = [0u8; 32]; - block!(sha256.finish(sha256_output.as_mut_slice())).unwrap(); - - // Calculate software result to compare against - // Sha1 - let mut sha1_sw = sha1::Sha1::new(); - sha1_sw.update(sha1_source_data); - let soft_result = sha1_sw.finalize(); - for (a, b) in sha1_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA1 HW: {:02x?}", sha1_output); - log::error!("SHA1 SW: {:02x?}", soft_result); - return false; - } - } - - // Sha224 - #[cfg(not(feature = "esp32"))] - { - let mut sha224_sw = sha2::Sha224::new(); - sha224_sw.update(sha224_source_data); - let soft_result = sha224_sw.finalize(); - for (a, b) in sha224_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA224 HW: {:02x?}", sha224_output); - log::error!("SHA224 SW: {:02x?}", soft_result); - return false; - } - } - } - - // Sha256 - let mut sha256_sw = sha2::Sha256::new(); - sha256_sw.update(sha256_source_data); - let soft_result = sha256_sw.finalize(); - for (a, b) in sha256_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA256 HW: {:02x?}", sha256_output); - log::error!("SHA256 SW: {:02x?}", soft_result); - return false; - } - } - - true -} - -/// A rolling test that loops between hasher for every step to test interleaving. -/// -/// Returns true if the test succeed, else false. -fn rolling_with_digest_trait(size: usize) -> bool { - // Use different random data for each hasher. - let sha1_source_data = unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; - #[cfg(not(feature = "esp32"))] - let sha224_source_data = - unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; - let sha256_source_data = - unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; - - let mut sha1 = esp_hal::sha::Sha1::default(); - #[cfg(not(feature = "esp32"))] - let mut sha224 = esp_hal::sha::Sha224::default(); - let mut sha256 = esp_hal::sha::Sha256::default(); - - // The Digest::update will consume the entirety of remaining. We don't need to loop until - // remaining is fully consumed. - Digest::update(&mut sha1, sha1_source_data); - #[cfg(not(feature = "esp32"))] - Digest::update(&mut sha224, sha224_source_data); - Digest::update(&mut sha256, sha256_source_data); - - let sha1_output: [u8; 20] = Digest::finalize(sha1).into(); - #[cfg(not(feature = "esp32"))] - let sha224_output: [u8; 28] = Digest::finalize(sha224).into(); - let sha256_output: [u8; 32] = Digest::finalize(sha256).into(); - - // Calculate software result to compare against - // Sha1 - let mut sha1_sw = sha1::Sha1::new(); - sha1_sw.update(sha1_source_data); - let soft_result = sha1_sw.finalize(); - for (a, b) in sha1_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA1 HW: {:02x?}", sha1_output); - log::error!("SHA1 SW: {:02x?}", soft_result); - return false; - } - } - - // Sha224 - #[cfg(not(feature = "esp32"))] - { - let mut sha224_sw = sha2::Sha224::new(); - sha224_sw.update(sha224_source_data); - let soft_result = sha224_sw.finalize(); - for (a, b) in sha224_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA224 HW: {:02x?}", sha224_output); - log::error!("SHA224 SW: {:02x?}", soft_result); - return false; - } - } - } - - // Sha256 - let mut sha256_sw = sha2::Sha256::new(); - sha256_sw.update(sha256_source_data); - let soft_result = sha256_sw.finalize(); - for (a, b) in sha256_output.iter().zip(soft_result) { - if *a != b { - log::error!("SHA256 HW: {:02x?}", sha256_output); - log::error!("SHA256 SW: {:02x?}", soft_result); - return false; - } - } - - true -} - -/// A simple test using [esp_hal::sha::Sha] trait to test hashing for an algorithm against a -/// specific size. This will compare the result with a software implementation and return false if -/// there's a mismatch -fn test_for_size<'a, D: Digest + Default + Sha<'a, esp_hal::Blocking>, const N: usize>( - size: usize, -) -> bool { - let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; - let mut remaining = source_data; - let mut hasher = D::default(); - - // Short hashes can be created by decreasing the output buffer to the desired - // length - let mut output = [0u8; N]; - - // The hardware implementation takes a subslice of the input, and returns the - // unprocessed parts The unprocessed parts can be input in the next - // iteration, you can always add more data until finish() is called. After - // finish() is called update()'s will contribute to a new hash which - // can be extracted again with finish(). - while remaining.len() > 0 { - // Can add println to view progress, however println takes a few orders of - // magnitude longer than the Sha function itself so not useful for - // comparing processing time println!("Remaining len: {}", - // remaining.len()); - - // All the HW Sha functions are infallible so unwrap is fine to use if you use - // block! - remaining = block!(Sha::update(&mut hasher, &remaining)).unwrap(); - } - - // Finish can be called as many times as desired to get multiple copies of the - // output. - block!(hasher.finish(output.as_mut_slice())).unwrap(); - - // Compare against Software result. - match N { - 20 => { - let mut hasher = sha1::Sha1::new(); - hasher.update(source_data); - let soft_result = hasher.finalize(); - for (a, b) in output.iter().zip(soft_result) { - if *a != b { - return false; - } - } - } - 28 => { - let mut hasher = sha2::Sha224::new(); - hasher.update(source_data); - let soft_result = hasher.finalize(); - for (a, b) in output.iter().zip(soft_result) { - if *a != b { - return false; - } - } - } - 32 => { - let mut hasher = sha2::Sha256::new(); - hasher.update(source_data); - let soft_result = hasher.finalize(); - for (a, b) in output.iter().zip(soft_result) { - if *a != b { - return false; - } - } - } - 48 => { - let mut hasher = sha2::Sha384::new(); - hasher.update(source_data); - let soft_result = hasher.finalize(); - for (a, b) in output.iter().zip(soft_result) { - if *a != b { - return false; - } - } - } - 64 => { - let mut hasher = sha2::Sha512::new(); - hasher.update(source_data); - let soft_result = hasher.finalize(); - for (a, b) in output.iter().zip(soft_result) { - if *a != b { - log::error!("HW: {:02x?}", output); - log::error!("SW: {:02x?}", soft_result); - return false; - } - } - } - _ => todo!(), - }; - - true -} diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index 419f4ed9a8f..b23a24fd57c 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -153,6 +153,8 @@ hex-literal = "0.4.1" nb = "1.1.0" p192 = { version = "0.13.0", default-features = false, features = ["arithmetic"] } p256 = { version = "0.13.2", default-features = false, features = ["arithmetic"] } +sha1 = { version = "0.10.6", default-features = false } +sha2 = { version = "0.10.8", default-features = false } [features] default = ["async", "embassy"] diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index fe523fcd846..104f364e6d5 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -6,13 +6,39 @@ #![no_main] use defmt_rtt as _; +use digest::Digest; use esp_backtrace as _; use esp_hal::{ + clock::ClockControl, peripherals::Peripherals, prelude::*, + rng::Rng, sha::{Sha, Sha1, Sha256}, + system::SystemControl, }; use nb::block; +use sha1; +use sha2; + +/// Dummy data used to feed the hasher. +static CHAR_ARRAY: [u8; 200] = [b'a'; 200]; + +/// Dummy random data used to feed the Sha1 hasher +static mut SHA1_RANDOM_ARRAY: [u8; 256] = [0u8; 256]; + +/// Dummy random data used to feed the Sha224 hasher +static mut SHA224_RANDOM_ARRAY: [u8; 256] = [0u8; 256]; + +/// Dummy random data used to feed the Sha256 hasher +static mut SHA256_RANDOM_ARRAY: [u8; 256] = [0u8; 256]; + +/// Dummy random data used to feed the Sha384 hasher +#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] +static mut SHA384_RANDOM_ARRAY: [u8; 256] = [0u8; 256]; + +/// Dummy random data used to feed the Sha512 hasher +#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] +static mut SHA512_RANDOM_ARRAY: [u8; 256] = [0u8; 256]; #[cfg(test)] #[embedded_test::tests] @@ -39,7 +65,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -76,7 +102,7 @@ mod tests { let mut output = [0u8; 28]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -115,7 +141,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -155,7 +181,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -197,7 +223,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -240,7 +266,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -280,7 +306,7 @@ mod tests { let mut output = [0u8; 32]; while remaining.len() > 0 { - remaining = block!(sha.update(remaining)).unwrap(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); @@ -304,4 +330,351 @@ mod tests { assert_eq!(expected_output, output); } + + /// A test that runs a hashing on a digest of every size between 1 and 256 + /// inclusively. + #[test] + fn test_digest_of_size_1_to_200() { + for i in 1..=200 { + test_for_size::, 20>(i); + #[cfg(not(feature = "esp32"))] + test_for_size::, 28>(i); + test_for_size::, 32>(i); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + test_for_size::, 48>(i); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + test_for_size::, 64>(i); + } + } + + /// A rolling test that loops between hasher for every step to test + /// interleaving. This specifically test the Sha trait implementation + #[test] + fn test_sha_rolling() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rng = Rng::new(peripherals.RNG); + + // Fill source data with random data + use core::ptr::addr_of_mut; + for slice in unsafe { + [ + addr_of_mut!(SHA1_RANDOM_ARRAY), + addr_of_mut!(SHA224_RANDOM_ARRAY), + addr_of_mut!(SHA256_RANDOM_ARRAY), + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + addr_of_mut!(SHA384_RANDOM_ARRAY), + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + addr_of_mut!(SHA512_RANDOM_ARRAY), + ] + } { + rng.read(unsafe { &mut *slice }); + } + + for size in [1, 64, 128, 256] { + // Use different random data for each hasher. + let sha1_source_data = + unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(not(feature = "esp32"))] + let sha224_source_data = + unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; + let sha256_source_data = + unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha384_source_data = + unsafe { core::slice::from_raw_parts(SHA384_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha512_source_data = + unsafe { core::slice::from_raw_parts(SHA512_RANDOM_ARRAY.as_ptr(), size) }; + + let mut sha1_remaining = sha1_source_data; + #[cfg(not(feature = "esp32"))] + let mut sha224_remaining = sha224_source_data; + let mut sha256_remaining = sha256_source_data; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384_remaining = sha384_source_data; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512_remaining = sha512_source_data; + + let mut sha1 = esp_hal::sha::Sha1::default(); + #[cfg(not(feature = "esp32"))] + let mut sha224 = esp_hal::sha::Sha224::default(); + let mut sha256 = esp_hal::sha::Sha256::default(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384 = esp_hal::sha::Sha384::default(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512 = esp_hal::sha::Sha512::default(); + + // All sources are the same length + while sha1_remaining.len() > 0 { + sha1_remaining = block!(Sha::update(&mut sha1, sha1_remaining)).unwrap(); + #[cfg(not(feature = "esp32"))] + { + sha224_remaining = block!(Sha::update(&mut sha224, sha224_remaining)).unwrap(); + } + sha256_remaining = block!(Sha::update(&mut sha256, sha256_remaining)).unwrap(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + sha384_remaining = block!(Sha::update(&mut sha384, sha384_remaining)).unwrap(); + sha512_remaining = block!(Sha::update(&mut sha512, sha512_remaining)).unwrap(); + } + } + + let mut sha1_output = [0u8; 20]; + block!(sha1.finish(sha1_output.as_mut_slice())).unwrap(); + #[cfg(not(feature = "esp32"))] + let mut sha224_output = [0u8; 28]; + #[cfg(not(feature = "esp32"))] + block!(sha224.finish(sha224_output.as_mut_slice())).unwrap(); + let mut sha256_output = [0u8; 32]; + block!(sha256.finish(sha256_output.as_mut_slice())).unwrap(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384_output = [0u8; 48]; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + block!(sha384.finish(sha384_output.as_mut_slice())).unwrap(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512_output = [0u8; 64]; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + block!(sha512.finish(sha512_output.as_mut_slice())).unwrap(); + + // Calculate software result to compare against + // Sha1 + let mut sha1_sw = sha1::Sha1::new(); + sha1_sw.update(sha1_source_data); + let soft_result = sha1_sw.finalize(); + for (a, b) in sha1_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + + // Sha224 + #[cfg(not(feature = "esp32"))] + { + let mut sha224_sw = sha2::Sha224::new(); + sha224_sw.update(sha224_source_data); + let soft_result = sha224_sw.finalize(); + for (a, b) in sha224_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + + // Sha256 + let mut sha256_sw = sha2::Sha256::new(); + sha256_sw.update(sha256_source_data); + let soft_result = sha256_sw.finalize(); + for (a, b) in sha256_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + + // Sha384 + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut sha384_sw = sha2::Sha384::new(); + sha384_sw.update(sha384_source_data); + let soft_result = sha384_sw.finalize(); + for (a, b) in sha384_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + + // Sha512 + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut sha512_sw = sha2::Sha512::new(); + sha512_sw.update(sha512_source_data); + let soft_result = sha512_sw.finalize(); + for (a, b) in sha512_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + } + } + + /// A rolling test that loops between hasher for every step to test + /// interleaving. This specifically test the Digest trait implementation + #[test] + fn test_for_digest_rolling() { + let peripherals = Peripherals::take(); + let system = SystemControl::new(peripherals.SYSTEM); + let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rng = Rng::new(peripherals.RNG); + + // Fill source data with random data + use core::ptr::addr_of_mut; + for slice in unsafe { + [ + addr_of_mut!(SHA1_RANDOM_ARRAY), + addr_of_mut!(SHA224_RANDOM_ARRAY), + addr_of_mut!(SHA256_RANDOM_ARRAY), + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + addr_of_mut!(SHA384_RANDOM_ARRAY), + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + addr_of_mut!(SHA512_RANDOM_ARRAY), + ] + } { + rng.read(unsafe { &mut *slice }); + } + + for size in [1, 64, 128, 256] { + // Use different random data for each hasher. + let sha1_source_data = + unsafe { core::slice::from_raw_parts(SHA1_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(not(feature = "esp32"))] + let sha224_source_data = + unsafe { core::slice::from_raw_parts(SHA224_RANDOM_ARRAY.as_ptr(), size) }; + let sha256_source_data = + unsafe { core::slice::from_raw_parts(SHA256_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha384_source_data = + unsafe { core::slice::from_raw_parts(SHA384_RANDOM_ARRAY.as_ptr(), size) }; + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha512_source_data = + unsafe { core::slice::from_raw_parts(SHA512_RANDOM_ARRAY.as_ptr(), size) }; + + let mut sha1 = esp_hal::sha::Sha1::default(); + #[cfg(not(feature = "esp32"))] + let mut sha224 = esp_hal::sha::Sha224::default(); + let mut sha256 = esp_hal::sha::Sha256::default(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha384 = esp_hal::sha::Sha384::default(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let mut sha512 = esp_hal::sha::Sha512::default(); + + // The Digest::update will consume the entirety of remaining. We don't need to + // loop until remaining is fully consumed. + Digest::update(&mut sha1, sha1_source_data); + #[cfg(not(feature = "esp32"))] + Digest::update(&mut sha224, sha224_source_data); + Digest::update(&mut sha256, sha256_source_data); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + Digest::update(&mut sha384, sha384_source_data); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + Digest::update(&mut sha512, sha512_source_data); + + let sha1_output: [u8; 20] = Digest::finalize(sha1).into(); + #[cfg(not(feature = "esp32"))] + let sha224_output: [u8; 28] = Digest::finalize(sha224).into(); + let sha256_output: [u8; 32] = Digest::finalize(sha256).into(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha384_output: [u8; 48] = Digest::finalize(sha384).into(); + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + let sha512_output: [u8; 64] = Digest::finalize(sha512).into(); + + // Calculate software result to compare against + // Sha1 + let mut sha1_sw = sha1::Sha1::new(); + sha1_sw.update(sha1_source_data); + let soft_result = sha1_sw.finalize(); + for (a, b) in sha1_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + + // Sha224 + #[cfg(not(feature = "esp32"))] + { + let mut sha224_sw = sha2::Sha224::new(); + sha224_sw.update(sha224_source_data); + let soft_result = sha224_sw.finalize(); + for (a, b) in sha224_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + + // Sha256 + let mut sha256_sw = sha2::Sha256::new(); + sha256_sw.update(sha256_source_data); + let soft_result = sha256_sw.finalize(); + for (a, b) in sha256_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + + // Sha384 + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut sha384_sw = sha2::Sha384::new(); + sha384_sw.update(sha384_source_data); + let soft_result = sha384_sw.finalize(); + for (a, b) in sha384_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + + // Sha512 + #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] + { + let mut sha512_sw = sha2::Sha512::new(); + sha512_sw.update(sha512_source_data); + let soft_result = sha512_sw.finalize(); + for (a, b) in sha512_output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + } + } +} + +/// A simple test using [esp_hal::sha::Sha] trait to test hashing for an +/// algorithm against a specific size. This will compare the result with a +/// software implementation and return false if there's a mismatch +fn test_for_size<'a, D: Digest + Default + Sha<'a, esp_hal::Blocking>, const N: usize>( + size: usize, +) { + let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; + let mut remaining = source_data; + let mut hasher = D::default(); + + let mut output = [0u8; N]; + + while remaining.len() > 0 { + remaining = block!(Sha::update(&mut hasher, &remaining)).unwrap(); + } + + block!(hasher.finish(output.as_mut_slice())).unwrap(); + + // Compare against Software result. + match N { + 20 => { + let mut hasher = sha1::Sha1::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + 28 => { + let mut hasher = sha2::Sha224::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + 32 => { + let mut hasher = sha2::Sha256::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + 48 => { + let mut hasher = sha2::Sha384::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + 64 => { + let mut hasher = sha2::Sha512::new(); + hasher.update(source_data); + let soft_result = hasher.finalize(); + for (a, b) in output.iter().zip(soft_result) { + assert_eq!(*a, b); + } + } + _ => unreachable!(), + }; } From cc8d2bf8f534d8df1689413be7e3f28bb393513f Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Mon, 12 Aug 2024 00:30:58 -0400 Subject: [PATCH 7/9] Re-export digest for convenience --- esp-hal/src/sha.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 41912cd5ef6..3ef16adce60 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -61,6 +61,10 @@ use core::{convert::Infallible, marker::PhantomData}; +/// Re-export digest for convenience +#[cfg(feature = "digest")] +pub use digest::Digest; + use crate::{ peripheral::{Peripheral, PeripheralRef}, peripherals::SHA, From 1ba7795694f8aa9082199229d240a8c0f33872a9 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Mon, 12 Aug 2024 14:12:26 -0400 Subject: [PATCH 8/9] Remove completed TODO --- esp-hal/src/sha.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index 3ef16adce60..a6723185e65 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -440,7 +440,6 @@ macro_rules! impl_sha { } /// Implement Default to create hasher out of thin air - /// TODO: Ensure safety is when using multiple Sha at once. impl<'d> core::default::Default for $name<'d, crate::Blocking> { fn default() -> Self { let sha = unsafe { crate::peripherals::SHA::steal() }; From 1526f0b7ad8af3856d049b6421d5af4f0849394f Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:15:47 -0400 Subject: [PATCH 9/9] Remove SHA peripheral requirement. - Document safety of the SHA driver. --- esp-hal/src/sha.rs | 132 ++++++++++++++++++++---------------------- hil-test/tests/sha.rs | 25 +++----- 2 files changed, 71 insertions(+), 86 deletions(-) diff --git a/esp-hal/src/sha.rs b/esp-hal/src/sha.rs index a6723185e65..2213cb68e46 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.rs @@ -37,7 +37,7 @@ //! # use nb::block; //! let source_data = "HELLO, ESPRESSIF!".as_bytes(); //! let mut remaining = source_data; -//! let mut hasher = Sha256::new(peripherals.SHA); +//! let mut hasher = Sha256::new(); //! // Short hashes can be created by decreasing the output buffer to the //! // desired length //! let mut output = [0u8; 32]; @@ -66,8 +66,6 @@ use core::{convert::Infallible, marker::PhantomData}; pub use digest::Digest; use crate::{ - peripheral::{Peripheral, PeripheralRef}, - peripherals::SHA, reg_access::{AlignmentHelper, SocDependentEndianess}, system::PeripheralClockControl, }; @@ -126,7 +124,7 @@ impl Context { // This implementation might fail after u32::MAX/8 bytes, to increase please see // ::finish() length/self.cursor usage -pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { +pub trait Sha: core::ops::DerefMut> { /// Constant containing the name of the algorithm as a string. const ALGORITHM: &'static str; @@ -149,6 +147,7 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { #[cfg(not(esp32))] fn is_busy(&self) -> bool { + // Safety: This is safe because we only read `SHA_BUSY_REG` let sha = unsafe { crate::peripherals::SHA::steal() }; sha.busy().read().bits() != 0 } @@ -158,6 +157,8 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { #[cfg(not(esp32))] fn process_buffer(&mut self) { + // Safety: This is safe because digest state is restored and saved between + // operations. let sha = unsafe { crate::peripherals::SHA::steal() }; // Setup SHA Mode before processing current buffer. sha.mode() @@ -178,29 +179,44 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { // SET SHA_CONTINUE_REG sha.continue_().write(|w| unsafe { w.bits(1) }); } + + // Wait until buffer has completely processed + while self.is_busy() {} + + // Save the content of the current hash for interleaving operation. + let mut saved_digest = [0u8; 64]; + self.alignment_helper.volatile_read_regset( + sha.h_mem(0).as_ptr(), + &mut saved_digest, + 64 / self.alignment_helper.align_size(), + ); + self.saved_digest.replace(saved_digest); } fn flush_data(&mut self) -> nb::Result<(), Infallible> { - let sha = unsafe { crate::peripherals::SHA::steal() }; if self.is_busy() { return Err(nb::Error::WouldBlock); } + // Safety: This is safe because the buffer is processed after being flushed to + // memory. + let sha = unsafe { crate::peripherals::SHA::steal() }; + let chunk_len = self.chunk_length(); + let ctx = self.deref_mut(); // Flush aligned buffer in memory before flushing alignment_helper unsafe { core::ptr::copy_nonoverlapping( - self.buffer.as_ptr(), + ctx.buffer.as_ptr(), #[cfg(esp32)] sha.text(0).as_ptr(), #[cfg(not(esp32))] sha.m_mem(0).as_ptr(), - 32, + (ctx.cursor % chunk_len) / ctx.alignment_helper.align_size(), ); } - let ctx = self.deref_mut(); let flushed = ctx.alignment_helper.flush_to( #[cfg(esp32)] sha.text(0).as_ptr(), @@ -221,7 +237,6 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { // This function ensures that incoming data is aligned to u32 (due to issues // with cpy_mem) fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> { - let sha = unsafe { crate::peripherals::SHA::steal() }; let mod_cursor = self.cursor % self.chunk_length(); let chunk_len = self.chunk_length(); @@ -239,7 +254,10 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { // If bound reached we write the buffer to memory and process it. if bound_reached { + // Safety: This is safe because the bound has been reached and the buffer will + // be fully processed then saved. unsafe { + let sha = crate::peripherals::SHA::steal(); core::ptr::copy_nonoverlapping( self.buffer.as_ptr(), #[cfg(esp32)] @@ -250,21 +268,6 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { ); } self.process_buffer(); - - // Save the content of the current hash for interleaving operation. - #[cfg(not(esp32))] - { - // Wait until buffer has completely processed - while self.is_busy() {} - - let mut saved_digest = [0u8; 64]; - self.alignment_helper.volatile_read_regset( - sha.h_mem(0).as_ptr(), - &mut saved_digest, - 64 / self.alignment_helper.align_size(), - ); - self.saved_digest.replace(saved_digest); - } } Ok(remaining) @@ -384,71 +387,57 @@ pub trait Sha<'d, DM: crate::Mode>: core::ops::DerefMut> { Ok(()) } - - /// Create a new instance in [crate::Blocking] mode. - #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] - fn new_internal(sha: impl Peripheral

+ 'd) -> (PeripheralRef<'d, SHA>, Context) { - crate::into_ref!(sha); - - PeripheralClockControl::reset(crate::system::Peripheral::Sha); - PeripheralClockControl::enable(crate::system::Peripheral::Sha); - - ( - sha, - Context { - cursor: 0, - first_run: true, - finished: false, - alignment_helper: AlignmentHelper::default(), - buffer: [0u32; 32], - #[cfg(not(esp32))] - saved_digest: None, - phantom: PhantomData, - }, - ) - } } /// This macro implements the Sha<'a, DM> trait for a specified Sha algorithm /// and a set of parameters macro_rules! impl_sha { ($name: ident, $mode_bits: tt, $digest_length: tt, $chunk_length: tt) => { - pub struct $name<'d, DM: crate::Mode>(PeripheralRef<'d, SHA>, Context); + pub struct $name(Context); - impl<'d> $name<'d, crate::Blocking> { + impl $name { /// Create a new instance in [crate::Blocking] mode. #[cfg_attr(not(esp32), doc = "Optionally an interrupt handler can be bound.")] - pub fn new(sha: impl Peripheral

+ 'd) -> $name<'d, crate::Blocking> { - let (sha, ctx) = Self::new_internal(sha); - Self(sha, ctx) + pub fn new() -> $name { + Self::default() } } /// Automatically implement Deref + DerefMut to get access to inner context - impl<'a, DM: crate::Mode> core::ops::Deref for $name<'a, DM> { + impl core::ops::Deref for $name { type Target = Context; fn deref(&self) -> &Self::Target { - &self.1 + &self.0 } } - impl<'a, DM: crate::Mode> core::ops::DerefMut for $name<'a, DM> { + impl core::ops::DerefMut for $name { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.1 + &mut self.0 } } /// Implement Default to create hasher out of thin air - impl<'d> core::default::Default for $name<'d, crate::Blocking> { + impl core::default::Default for $name { fn default() -> Self { - let sha = unsafe { crate::peripherals::SHA::steal() }; - let (sha, ctx) = Self::new_internal(sha); - Self(sha, ctx) + PeripheralClockControl::reset(crate::system::Peripheral::Sha); + PeripheralClockControl::enable(crate::system::Peripheral::Sha); + + Self(Context { + cursor: 0, + first_run: true, + finished: false, + alignment_helper: AlignmentHelper::default(), + buffer: [0u32; 32], + #[cfg(not(esp32))] + saved_digest: None, + phantom: PhantomData, + }) } } - impl<'d> $crate::sha::Sha<'d, crate::Blocking> for $name<'d, crate::Blocking> { + impl $crate::sha::Sha for $name { const ALGORITHM: &'static str = stringify!($name); #[cfg(not(esp32))] @@ -467,26 +456,31 @@ macro_rules! impl_sha { // ESP32 uses different registers for its operation #[cfg(esp32)] fn load_reg(&self) { + // Safety: This is safe because digest state is restored and saved between + // operations. + let sha = unsafe { crate::peripherals::SHA::steal() }; paste::paste! { - unsafe { self.0.[< $name:lower _load >]().write(|w| w.bits(1)) }; + unsafe { sha.[< $name:lower _load >]().write(|w| w.bits(1)) }; } } #[cfg(esp32)] fn is_busy(&self) -> bool { + let sha = unsafe { crate::peripherals::SHA::steal() }; paste::paste! { - self.0.[< $name:lower _busy >]().read().[< $name:lower _busy >]().bit_is_set() + sha.[< $name:lower _busy >]().read().[< $name:lower _busy >]().bit_is_set() } } #[cfg(esp32)] fn process_buffer(&mut self) { + let sha = unsafe { crate::peripherals::SHA::steal() }; paste::paste! { if self.first_run { - self.0.[< $name:lower _start >]().write(|w| unsafe { w.bits(1) }); + sha.[< $name:lower _start >]().write(|w| unsafe { w.bits(1) }); self.first_run = false; } else { - self.0.[< $name:lower _continue >]().write(|w| unsafe { w.bits(1) }); + sha.[< $name:lower _continue >]().write(|w| unsafe { w.bits(1) }); } } } @@ -496,10 +490,10 @@ macro_rules! impl_sha { /// Note: digest has a blanket trait implementation for [digest::Digest] for any /// element that implements FixedOutput + Default + Update + HashMarker #[cfg(feature = "digest")] - impl<'d, DM: crate::Mode> digest::HashMarker for $name<'d, DM> {} + impl digest::HashMarker for $name {} #[cfg(feature = "digest")] - impl<'a, DM: crate::Mode> digest::OutputSizeUser for $name<'a, DM> { + impl digest::OutputSizeUser for $name { // We use paste to append `U` to the digest size to match a const defined in // digest paste::paste! { @@ -508,7 +502,7 @@ macro_rules! impl_sha { } #[cfg(feature = "digest")] - impl<'a> digest::Update for $name<'a, crate::Blocking> { + impl digest::Update for $name { fn update(&mut self, data: &[u8]) { let mut remaining = data.as_ref(); while remaining.len() > 0 { @@ -518,7 +512,7 @@ macro_rules! impl_sha { } #[cfg(feature = "digest")] - impl<'a> digest::FixedOutput for $name<'a, crate::Blocking> { + impl digest::FixedOutput for $name { fn finalize_into(mut self, out: &mut digest::Output) { nb::block!(self.finish(out)).unwrap() } diff --git a/hil-test/tests/sha.rs b/hil-test/tests/sha.rs index 104f364e6d5..ce00c70d9f4 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -52,8 +52,7 @@ mod tests { #[test] fn test_sha_1() { - let peripherals = Peripherals::take(); - let mut sha = Sha1::new(peripherals.SHA); + let mut sha = Sha1::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -90,8 +89,7 @@ mod tests { #[test] #[cfg(not(feature = "esp32"))] fn test_sha_224() { - let peripherals = Peripherals::take(); - let mut sha = esp_hal::sha::Sha224::new(peripherals.SHA); + let mut sha = esp_hal::sha::Sha224::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -128,8 +126,7 @@ mod tests { #[test] fn test_sha_256() { - let peripherals = Peripherals::take(); - let mut sha = Sha256::new(peripherals.SHA); + let mut sha = Sha256::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -168,8 +165,7 @@ mod tests { #[test] #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] fn test_sha_384() { - let peripherals = Peripherals::take(); - let mut sha = esp_hal::sha::Sha384::new(peripherals.SHA); + let mut sha = esp_hal::sha::Sha384::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -210,8 +206,7 @@ mod tests { #[test] #[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512() { - let peripherals = Peripherals::take(); - let mut sha = esp_hal::sha::Sha512::new(peripherals.SHA); + let mut sha = esp_hal::sha::Sha512::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -253,8 +248,7 @@ mod tests { #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512_224() { - let peripherals = Peripherals::take(); - let mut sha = esp_hal::sha::Sha512_224::new(peripherals.SHA); + let mut sha = esp_hal::sha::Sha512_224::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -293,8 +287,7 @@ mod tests { #[test] #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] fn test_sha_512_256() { - let peripherals = Peripherals::take(); - let mut sha = esp_hal::sha::Sha512_256::new(peripherals.SHA); + let mut sha = esp_hal::sha::Sha512_256::new(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -618,9 +611,7 @@ mod tests { /// A simple test using [esp_hal::sha::Sha] trait to test hashing for an /// algorithm against a specific size. This will compare the result with a /// software implementation and return false if there's a mismatch -fn test_for_size<'a, D: Digest + Default + Sha<'a, esp_hal::Blocking>, const N: usize>( - size: usize, -) { +fn test_for_size, const N: usize>(size: usize) { let source_data = unsafe { core::slice::from_raw_parts(CHAR_ARRAY.as_ptr(), size) }; let mut remaining = source_data; let mut hasher = D::default();