diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index f2b5a0044d5..3fa8355ce83 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added touch pad support for esp32 (#1873, #1956) - 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) - Added `debugger::debugger_connected`. (#1961) ### Changed @@ -24,6 +26,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) - Reset peripherals in driver constructors where missing (#1893, #1961) ### Fixed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index b1887d248e9..b3457d94a27 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.rs index 89474222b23..2213cb68e46 100644 --- a/esp-hal/src/sha.rs +++ b/esp-hal/src/sha.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(); //! // Short hashes can be created by decreasing the output buffer to the //! // desired length //! let mut output = [0u8; 32]; @@ -61,107 +61,34 @@ 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, reg_access::{AlignmentHelper, SocDependentEndianess}, 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, finished: bool, - phantom: PhantomData, -} - -/// Hash Algorithm Mode -#[derive(Debug, Clone, Copy)] -pub enum ShaMode { - SHA1, + /// Buffered bytes (SHA_M_n_REG) to be processed. + buffer: [u32; 32], + /// Saved digest (SHA_H_n_REG) for interleaving operation #[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) - -#[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, - } - } + saved_digest: Option<[u8; 64]>, + phantom: PhantomData, } -impl<'d> crate::private::Sealed for Sha<'d, crate::Blocking> {} +impl crate::private::Sealed for Context {} #[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 +98,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,87 +124,73 @@ 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: core::ops::DerefMut> { + /// Constant containing the name of the algorithm as a string. + const ALGORITHM: &'static str; - pub fn finished(&self) -> bool { - self.finished - } + /// Setup SHA Mode + #[cfg(not(esp32))] + fn mode_as_bits() -> u8; + + fn chunk_length(&self) -> usize; + + fn digest_length(&self) -> usize; + + /// ESP32 requires that a control register to be written to calculate the + /// final SHA hash. + #[cfg(esp32)] + fn load_reg(&self); + + /// ESP32 uses a different register per hash mode. + #[cfg(esp32)] + fn is_busy(&self) -> bool; #[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 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 } #[cfg(esp32)] + fn process_buffer(&mut self); + + #[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() + .write(|w| unsafe { w.mode().bits(Self::mode_as_bits()) }); 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) }), - } + // Set SHA_START_REG + sha.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) }), + // 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) }); } - } - - 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, - } - } - - #[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(), - } - } - #[cfg(not(esp32))] - fn is_busy(&self) -> bool { - self.sha.busy().read().bits() != 0 - } + // Wait until buffer has completely processed + while self.is_busy() {} - 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, - } + // 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> { @@ -277,14 +198,31 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { 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(); - let flushed = self.alignment_helper.flush_to( + // Flush aligned buffer in memory before flushing alignment_helper + unsafe { + core::ptr::copy_nonoverlapping( + ctx.buffer.as_ptr(), + #[cfg(esp32)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.m_mem(0).as_ptr(), + (ctx.cursor % chunk_len) / ctx.alignment_helper.align_size(), + ); + } + + 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); @@ -303,26 +241,39 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { let chunk_len = self.chunk_length(); - let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy( - #[cfg(esp32)] - self.sha.text(0).as_ptr(), - #[cfg(not(esp32))] - self.sha.m_mem(0).as_ptr(), + let ctx = self.deref_mut(); + // Buffer the incoming bytes into u32 aligned words. + let (remaining, bound_reached) = ctx.alignment_helper.aligned_volatile_copy( + ctx.buffer.as_mut_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()); + // 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)] + sha.text(0).as_ptr(), + #[cfg(not(esp32))] + sha.m_mem(0).as_ptr(), + 32, + ); + } self.process_buffer(); } 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 +291,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,23 +308,24 @@ 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); 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; - 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 +339,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 +367,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(), ); @@ -439,3 +388,161 @@ impl<'d, DM: crate::Mode> Sha<'d, DM> { Ok(()) } } + +/// 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(Context); + + 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() -> $name { + Self::default() + } + } + + /// Automatically implement Deref + DerefMut to get access to inner context + impl core::ops::Deref for $name { + type Target = Context; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl core::ops::DerefMut for $name { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } + + /// Implement Default to create hasher out of thin air + impl core::default::Default for $name { + fn default() -> Self { + 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 $crate::sha::Sha for $name { + const ALGORITHM: &'static str = stringify!($name); + + #[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) { + // Safety: This is safe because digest state is restored and saved between + // operations. + let sha = unsafe { crate::peripherals::SHA::steal() }; + paste::paste! { + 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! { + 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 { + sha.[< $name:lower _start >]().write(|w| unsafe { w.bits(1) }); + self.first_run = false; + } else { + sha.[< $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 + #[cfg(feature = "digest")] + impl digest::HashMarker for $name {} + + #[cfg(feature = "digest")] + impl digest::OutputSizeUser for $name { + // 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 digest::Update for $name { + 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 digest::FixedOutput for $name { + 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 3d918b4312e..b8779724d4e 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -151,13 +151,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", "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"] } [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.6.0", default-features = false } # Add the `embedded-test/defmt` feature for more verbose testing @@ -166,6 +167,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 fb1716f2d53..a1a63a3fd2a 100644 --- a/hil-test/tests/sha.rs +++ b/hil-test/tests/sha.rs @@ -5,13 +5,39 @@ #![no_std] #![no_main] +use digest::Digest; use esp_hal::{ + clock::ClockControl, peripherals::Peripherals, prelude::*, - sha::{Sha, ShaMode}, + rng::Rng, + sha::{Sha, Sha1, Sha256}, + system::SystemControl, }; use hil_test as _; 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] @@ -25,8 +51,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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -38,40 +63,69 @@ 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(); 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(); 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(); + remaining = block!(Sha::update(&mut sha, remaining)).unwrap(); } block!(sha.finish(output.as_mut_slice())).unwrap(); 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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -83,18 +137,34 @@ 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(); 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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -106,18 +176,36 @@ 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(); 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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -129,18 +217,37 @@ 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(); 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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -152,18 +259,34 @@ 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(); 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(); let source_data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa".as_bytes(); let mut remaining = source_data; @@ -175,10 +298,373 @@ 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(); 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); + } + + /// 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, 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!(), + }; }