Refactor SHA to use trait. Implement Digest traits for SHA (#1908)

* feat(SHA): Refactor SHA to use trait. Implement Digest traits for SHA

* Fix CI. Fix wrong sha mode for esp32

* 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.

* Use random data when testing SHA

* 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.

* 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.

* Re-export digest for convenience

* Remove completed TODO

* Remove SHA peripheral requirement.

- Document safety of the SHA driver.

---------

Co-authored-by: Scott Mabin <scott@mabez.dev>
This commit is contained in:
Anthony Grondin 2024-08-19 10:43:47 -04:00 committed by GitHub
parent ec130877b7
commit 70491b9e37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 831 additions and 230 deletions

View File

@ -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

View File

@ -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 }

View File

@ -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<E: EndianessConverter> {
buf: [u8; U32_ALIGN_SIZE],
buf_fill: usize,

View File

@ -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<DM: crate::Mode> {
alignment_helper: AlignmentHelper<SocDependentEndianess>,
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<DM>,
}
/// 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<Mode>) in order to allow for better
// compiler optimizations? (Requires complex const generics which isn't stable
// yet)
impl crate::private::Sealed for Context<crate::Blocking> {}
#[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<P = SHA> + '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<crate::Blocking> {
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<DM: crate::Mode> Context<DM> {
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<DM: crate::Mode>: core::ops::DerefMut<Target = Context<DM>> {
/// 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;
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 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
}
pub fn finished(&self) -> bool {
self.finished
}
#[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 {
// Set SHA_START_REG
self.sha.start().write(|w| unsafe { w.bits(1) });
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
self.sha.continue_().write(|w| unsafe { w.bits(1) });
}
sha.continue_().write(|w| unsafe { w.bits(1) });
}
#[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) }),
}
}
}
// Wait until buffer has completely processed
while self.is_busy() {}
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
}
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);
}
let chunk_len = self.chunk_length();
// Safety: This is safe because the buffer is processed after being flushed to
// memory.
let sha = unsafe { crate::peripherals::SHA::steal() };
let flushed = self.alignment_helper.flush_to(
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(
ctx.buffer.as_ptr(),
#[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(),
);
}
let flushed = ctx.alignment_helper.flush_to(
#[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(),
);
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::<u64>() {
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::<u64>();
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::<u64>()) / self.alignment_helper.align_size(),
chunk_len / ctx.alignment_helper.align_size(),
(chunk_len - core::mem::size_of::<u64>()) / 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<DM: crate::Mode>(Context<DM>);
impl $name<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() -> $name<crate::Blocking> {
Self::default()
}
}
/// Automatically implement Deref + DerefMut to get access to inner context
impl<DM: crate::Mode> core::ops::Deref for $name<DM> {
type Target = Context<DM>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<DM: crate::Mode> core::ops::DerefMut for $name<DM> {
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<crate::Blocking> {
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<crate::Blocking> for $name<crate::Blocking> {
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<DM: crate::Mode> digest::HashMarker for $name<DM> {}
#[cfg(feature = "digest")]
impl<DM: crate::Mode> digest::OutputSizeUser for $name<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 digest::Update for $name<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 digest::FixedOutput for $name<crate::Blocking> {
fn finalize_into(mut self, out: &mut digest::Output<Self>) {
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);

View File

@ -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"]

View File

@ -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,7 +63,43 @@ 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<esp_hal::Blocking> = 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 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,
];
let mut output = [0u8; 28];
while remaining.len() > 0 {
remaining = block!(Sha::update(&mut sha, remaining)).unwrap();
}
block!(sha.finish(output.as_mut_slice())).unwrap();
@ -47,31 +108,24 @@ mod tests {
#[test]
#[cfg(not(feature = "esp32"))]
fn test_sha_224() {
let peripherals = Peripherals::take();
let mut sha = Sha::new(peripherals.SHA, ShaMode::SHA224);
fn test_sha_224_digest() {
let mut sha: esp_hal::sha::Sha224<esp_hal::Blocking> = digest::Digest::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];
while remaining.len() > 0 {
remaining = block!(sha.update(remaining)).unwrap();
}
block!(sha.finish(output.as_mut_slice())).unwrap();
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<esp_hal::Blocking> = 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<esp_hal::Blocking> = 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<esp_hal::Blocking> = 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<esp_hal::Blocking> = 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<esp_hal::Blocking> = 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::<esp_hal::sha::Sha1<esp_hal::Blocking>, 20>(i);
#[cfg(not(feature = "esp32"))]
test_for_size::<esp_hal::sha::Sha224<esp_hal::Blocking>, 28>(i);
test_for_size::<esp_hal::sha::Sha256<esp_hal::Blocking>, 32>(i);
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
test_for_size::<esp_hal::sha::Sha384<esp_hal::Blocking>, 48>(i);
#[cfg(any(feature = "esp32", feature = "esp32s2", feature = "esp32s3"))]
test_for_size::<esp_hal::sha::Sha512<esp_hal::Blocking>, 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<D: Digest + Default + Sha<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!(),
};
}