mirror of
https://github.com/esp-rs/esp-hal.git
synced 2026-03-27 20:36:01 +00:00
Implement BlockSizeUser for ShaContexts (#5050)
* Implement BlockSizeUser for ShaContexts * Use max CPU speed * Only measure the computation * Clean up
This commit is contained in:
@@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- C5: Add PARL_IO support (#5042)
|
||||
- `esp_hal::interrupt::RunLevel` (#4996)
|
||||
- MAC addresses for radio interfaces getter: `esp_hal::efuse::Efuse::interface_mac_address(InterfaceMacAddress::)`. (#5002)
|
||||
- `ShaXContext` objects now implement `digest::core_api::BlockSizeUser` (and thus they can be used with the `hmac` crate) (#5050)
|
||||
|
||||
### Changed
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ procmacros = { version = "0.21.0", package = "esp-hal-procmacros",
|
||||
|
||||
# Dependencies that are optional because they are used by unstable drivers.
|
||||
# They are needed when using the `unstable` feature.
|
||||
digest = { version = "0.10.7", default-features = false, optional = true }
|
||||
digest = { version = "0.10.7", default-features = false, features = ["core-api"], optional = true }
|
||||
embassy-usb-driver = { version = "0.2", optional = true }
|
||||
embassy-usb-synopsys-otg = { version = "0.3", optional = true }
|
||||
embedded-can = { version = "0.4.1", optional = true }
|
||||
|
||||
@@ -1340,7 +1340,7 @@ pub enum FinalizeError {
|
||||
// Now implement the actual public types.
|
||||
// Helper macro to limit the scope of `paste`
|
||||
macro_rules! impl_worker_context {
|
||||
($name:ident, $full_name:literal, $algo:expr, $digest_len:literal ) => {
|
||||
($name:ident, $full_name:literal, $algo:expr, $digest_len:literal, $block_size:literal ) => {
|
||||
#[doc = concat!("A ", $full_name, " context.")]
|
||||
#[cfg_attr(not(esp32), derive(Clone))]
|
||||
pub struct $name(ShaContext<{ $algo.chunk_length() }, { $algo.digest_length() / 4 }>);
|
||||
@@ -1414,13 +1414,21 @@ macro_rules! impl_worker_context {
|
||||
Self::finalize(&mut self, out.as_mut()).wait_blocking();
|
||||
}
|
||||
}
|
||||
|
||||
impl digest::core_api::BlockSizeUser for $name {
|
||||
type BlockSize = paste::paste!(digest::consts::[< U $block_size >]);
|
||||
|
||||
fn block_size() -> usize {
|
||||
$block_size
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for_each_sha_algorithm! {
|
||||
( $name:ident, $full_name:literal (sizes: $block_size:literal, $digest_len:literal, $message_length_bytes:literal) $security:tt, $mode_bits:literal ) => {
|
||||
paste::paste! {
|
||||
impl_worker_context!( [<$name Context>], $full_name, ShaAlgorithmKind::$name, $digest_len );
|
||||
impl_worker_context!( [<$name Context>], $full_name, ShaAlgorithmKind::$name, $digest_len, $block_size );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
//! Demonstrates the use of the HMAC peripheral and compares the speed of
|
||||
//! hardware-accelerated and pure software hashing.
|
||||
//! Demonstrates the two possible ways to use hardware acceleration for HMAC operations and compares
|
||||
//! the speed of hardware-accelerated and pure software hashing.
|
||||
//!
|
||||
//! - Use of the SHA peripheral with a software HMAC implementation
|
||||
//! - Use of the HMAC peripheral (optional if required setup is skipped)
|
||||
//!
|
||||
//! # Writing key
|
||||
//! Before using the HMAC accelerator in upstream mode, you first need to
|
||||
@@ -56,12 +59,15 @@
|
||||
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::CpuClock,
|
||||
hmac::{Hmac, HmacPurpose, KeyId},
|
||||
main,
|
||||
rng::Rng,
|
||||
sha::{Sha256Context, ShaBackend},
|
||||
time::Instant,
|
||||
};
|
||||
use esp_println::println;
|
||||
use hmac::{Hmac as HmacSw, Mac};
|
||||
use esp_println::{print, println};
|
||||
use hmac::{Hmac as HmacSw, Mac, SimpleHmac};
|
||||
use nb::block;
|
||||
use sha2::Sha256;
|
||||
|
||||
@@ -72,50 +78,71 @@ type HmacSha256 = HmacSw<Sha256>;
|
||||
#[main]
|
||||
fn main() -> ! {
|
||||
esp_println::logger::init_logger_from_env();
|
||||
let peripherals = esp_hal::init(esp_hal::Config::default());
|
||||
let mut peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::max()));
|
||||
|
||||
let rng = Rng::new();
|
||||
let mut sha_backend = ShaBackend::new(peripherals.SHA.reborrow());
|
||||
|
||||
// Set sw key
|
||||
let key = [0_u8; 32].as_ref();
|
||||
|
||||
let mut hw_hmac = Hmac::new(peripherals.HMAC);
|
||||
|
||||
let mut src = [0_u8; 1024];
|
||||
rng.read(src.as_mut_slice());
|
||||
Rng::new().read(src.as_mut_slice());
|
||||
|
||||
let mut output = [0u8; 32];
|
||||
|
||||
println!("Beginning stress tests...");
|
||||
println!("Testing length from 0 to {:?} bytes for HMAC...", src.len());
|
||||
for i in 0..src.len() + 1 {
|
||||
print!("Testing for length: {:>4}", i);
|
||||
let (nsrc, _) = src.split_at(i);
|
||||
let mut remaining = nsrc;
|
||||
hw_hmac.init();
|
||||
block!(hw_hmac.configure(HmacPurpose::ToUser, KeyId::Key0)).expect("Key purpose mismatch");
|
||||
let pre_hw_hmac = esp_hal::time::Instant::now();
|
||||
while remaining.len() > 0 {
|
||||
remaining = block!(hw_hmac.update(remaining)).unwrap();
|
||||
}
|
||||
block!(hw_hmac.finalize(output.as_mut_slice())).unwrap();
|
||||
let hw_time = pre_hw_hmac.elapsed();
|
||||
|
||||
// Use a software algorithm to generate the expected value, and timing baseline
|
||||
let mut sw_hmac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size");
|
||||
let pre_sw_hash = esp_hal::time::Instant::now();
|
||||
let pre_sw_hash = Instant::now();
|
||||
sw_hmac.update(nsrc);
|
||||
let soft_result = sw_hmac.finalize().into_bytes();
|
||||
|
||||
let soft_time = pre_sw_hash.elapsed();
|
||||
for (a, b) in output.iter().zip(soft_result) {
|
||||
assert_eq!(*a, b);
|
||||
}
|
||||
println!(
|
||||
"Testing for length: {:>4} | HW: {:>6} cycles, SW: {:>7} cycles (HW HMAC is {:>2}x faster)",
|
||||
i,
|
||||
hw_time,
|
||||
soft_time,
|
||||
soft_time / hw_time
|
||||
print!(" SW: {:>7}", soft_time);
|
||||
|
||||
// Use the hardware to see if we can get the same result, and compare the timing
|
||||
let mut hw_hmac = Hmac::new(peripherals.HMAC.reborrow());
|
||||
hw_hmac.init();
|
||||
if block!(hw_hmac.configure(HmacPurpose::ToUser, KeyId::Key0)).is_ok() {
|
||||
let pre_hw_hmac = Instant::now();
|
||||
let mut remaining = nsrc;
|
||||
while !remaining.is_empty() {
|
||||
remaining = block!(hw_hmac.update(remaining)).unwrap();
|
||||
}
|
||||
block!(hw_hmac.finalize(output.as_mut_slice())).unwrap();
|
||||
|
||||
let hw_time = pre_hw_hmac.elapsed();
|
||||
|
||||
assert_eq!(output, soft_result.as_slice());
|
||||
|
||||
print!(", HW: {:>6} ({:>2}x faster)", hw_time, soft_time / hw_time);
|
||||
} else {
|
||||
print!(", HW HMAC skipped (Key not burned)");
|
||||
};
|
||||
core::mem::drop(hw_hmac);
|
||||
|
||||
// Now let's try a hybrid approach using the SHA peripheral with a software HMAC
|
||||
// implementation
|
||||
let _sha_backend = sha_backend.start();
|
||||
|
||||
let mut hyb_hmac = SimpleHmac::<Sha256Context>::new_from_slice(key)
|
||||
.expect("HMAC can take key of any size");
|
||||
let pre_hyb_hash = Instant::now();
|
||||
hyb_hmac.update(nsrc);
|
||||
let hybrid_result = hyb_hmac.finalize().into_bytes();
|
||||
let hybrid_time = pre_hyb_hash.elapsed();
|
||||
print!(
|
||||
", SW+HW: {:>7} ({:>2}x faster)",
|
||||
hybrid_time,
|
||||
soft_time / hybrid_time
|
||||
);
|
||||
assert_eq!(hybrid_result, soft_result);
|
||||
|
||||
println!();
|
||||
}
|
||||
println!("Finished stress tests!");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user