//! Demonstrates the use of the HMAC peripheral and compares the speed of //! hardware-accelerated and pure software hashing. //! //! # Writing key //! Before using the HMAC accelerator in upstream mode, you first need to //! prepare a secret 256-bit HMAC key and burn the key to an empty eFuse block. //! //! ## ⚠️ Before writing ⚠️ //! - From the factory, the eFuse keyblocks are programmed to be 32-byte 0x00. //! - This example is programmed to use this value so you can skip this step if //! you don't want to burn an eFuse key. //! - If you skip the skip burning a custom key, you still need to [burn the //! purpose](#burn-key-purpose). //! - [Read more about eFuses](https://docs.espressif.com/projects/esptool/en/latest/esp32c3/espefuse/index.html) //! //! ## Burn key purpose //! You first need to burn the efuse key purpose for the specified key below //! (Default Key0). Purposes: //! //! | Purpose | Mode | Value | Description | //! |--------------------------------|------------|-------|-----------------------------------------------| //! | JTAG Re-enable | Downstream | 6 | EFUSE_KEY_PURPOSE_HMAC_DOWN_JTAG | //! | DS Key Derivation | Downstream | 7 | EFUSE_KEY_PURPOSE_HMAC_DOWN_DIGITAL_SIGNATURE | //! | HMAC Calculation | Upstream | 8 | EFUSE_KEY_PURPOSE_HMAC_UP | //! | Both JTAG Re-enable and DS KDF | Downstream | 5 | EFUSE_KEY_PURPOSE_HMAC_DOWN_ALL | //! //! To burn the efuse key purpose `HMAC_UP` to `Key0`: //! ```sh //! espefuse.py burn_efuse KEY_PURPOSE_0 8 //! ``` //! //! ## Use a custom key //! You can generate a custom key file from a string using the following command //! ```sh //! echo -n "" | openssl dgst -sha256 -binary > key.bin //! ``` //! //! You can then write your key using the following command //! - `BLOCK_KEY0` The keyblock to program the key to. By default this example //! uses key0. //! - `HMAC_UP` The purpose for the key. Use HMAC_UP for upstream. //! - `--no-read-protect` Allow to read the key from software, after writing it. //! ```sh //! espefuse.py burn_key BLOCK_KEY0 key.bin HMAC_UP --no-read-protect //! ``` //! To see the key in bytes, you can do the following: //! ```sh //! echo -n "" | openssl dgst -sha256 -binary | xxd -p //! ``` //! or from the binary file //! ```sh //! xxd -p key.bin //! ``` #![no_std] #![no_main] use esp32c3_hal::{ clock::ClockControl, hmac::{Hmac, HmacPurpose, KeyId}, peripherals::Peripherals, prelude::*, systimer::SystemTimer, Rng, }; use esp_backtrace as _; use esp_println::println; use hmac::{Hmac as HmacSw, Mac}; use nb::block; use sha2::Sha256; type HmacSha256 = HmacSw; #[entry] fn main() -> ! { let peripherals = Peripherals::take(); let system = peripherals.SYSTEM.split(); let _clocks = ClockControl::boot_defaults(system.clock_control).freeze(); let mut rng = Rng::new(peripherals.RNG); // 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()).unwrap(); // println!("HMAC input {:02X?}", src); 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 { 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 = SystemTimer::now(); while remaining.len() > 0 { remaining = block!(hw_hmac.update(remaining)).unwrap(); } block!(hw_hmac.finalize(output.as_mut_slice())).unwrap(); let post_hw_hmac = SystemTimer::now(); let hw_time = post_hw_hmac - pre_hw_hmac; let mut sw_hmac = HmacSha256::new_from_slice(key).expect("HMAC can take key of any size"); let pre_sw_hash = SystemTimer::now(); sw_hmac.update(nsrc); let soft_result = sw_hmac.finalize().into_bytes(); let post_sw_hash = SystemTimer::now(); let soft_time = post_sw_hash - pre_sw_hash; 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); } println!("Finished stress tests!"); loop {} }