Added hash module with blocking implementation. Included SHA256 example.
This commit is contained in:
parent
dcce40c8a2
commit
6e9ddd4626
@ -68,7 +68,7 @@ rand_core = "0.6.3"
|
|||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
#stm32-metapac = { version = "15" }
|
#stm32-metapac = { version = "15" }
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d" }
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" }
|
||||||
vcell = "0.1.3"
|
vcell = "0.1.3"
|
||||||
bxcan = "0.7.0"
|
bxcan = "0.7.0"
|
||||||
nb = "1.0.0"
|
nb = "1.0.0"
|
||||||
@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] }
|
|||||||
proc-macro2 = "1.0.36"
|
proc-macro2 = "1.0.36"
|
||||||
quote = "1.0.15"
|
quote = "1.0.15"
|
||||||
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
|
||||||
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d", default-features = false, features = ["metadata"]}
|
stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]}
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|||||||
260
embassy-stm32/src/hash/mod.rs
Normal file
260
embassy-stm32/src/hash/mod.rs
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
//! Hash generator (HASH)
|
||||||
|
use core::cmp::min;
|
||||||
|
|
||||||
|
use embassy_hal_internal::{into_ref, PeripheralRef};
|
||||||
|
use stm32_metapac::hash::regs::*;
|
||||||
|
|
||||||
|
use crate::pac::HASH as PAC_HASH;
|
||||||
|
use crate::peripherals::HASH;
|
||||||
|
use crate::rcc::sealed::RccPeripheral;
|
||||||
|
use crate::Peripheral;
|
||||||
|
|
||||||
|
const NUM_CONTEXT_REGS: usize = 54;
|
||||||
|
const HASH_BUFFER_LEN: usize = 68;
|
||||||
|
const DIGEST_BLOCK_SIZE: usize = 64;
|
||||||
|
|
||||||
|
///Hash algorithm selection
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum Algorithm {
|
||||||
|
/// SHA-1 Algorithm
|
||||||
|
SHA1 = 0,
|
||||||
|
/// MD5 Algorithm
|
||||||
|
MD5 = 1,
|
||||||
|
/// SHA-224 Algorithm
|
||||||
|
SHA224 = 2,
|
||||||
|
/// SHA-256 Algorithm
|
||||||
|
SHA256 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Input data width selection
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum DataType {
|
||||||
|
///32-bit data, no data is swapped.
|
||||||
|
Width32 = 0,
|
||||||
|
///16-bit data, each half-word is swapped.
|
||||||
|
Width16 = 1,
|
||||||
|
///8-bit data, all bytes are swapped.
|
||||||
|
Width8 = 2,
|
||||||
|
///1-bit data, all bits are swapped.
|
||||||
|
Width1 = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores the state of the HASH peripheral for suspending/resuming
|
||||||
|
/// digest calculation.
|
||||||
|
pub struct Context {
|
||||||
|
first_word_sent: bool,
|
||||||
|
buffer: [u8; HASH_BUFFER_LEN],
|
||||||
|
buflen: usize,
|
||||||
|
algo: Algorithm,
|
||||||
|
format: DataType,
|
||||||
|
imr: u32,
|
||||||
|
str: u32,
|
||||||
|
cr: u32,
|
||||||
|
csr: [u32; NUM_CONTEXT_REGS],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// HASH driver.
|
||||||
|
pub struct Hash<'d> {
|
||||||
|
_peripheral: PeripheralRef<'d, HASH>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d> Hash<'d> {
|
||||||
|
/// Instantiates, resets, and enables the HASH peripheral.
|
||||||
|
pub fn new(peripheral: impl Peripheral<P = HASH> + 'd) -> Self {
|
||||||
|
HASH::enable_and_reset();
|
||||||
|
into_ref!(peripheral);
|
||||||
|
let instance = Self {
|
||||||
|
_peripheral: peripheral,
|
||||||
|
};
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts computation of a new hash and returns the saved peripheral state.
|
||||||
|
pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
|
||||||
|
// Define a context for this new computation.
|
||||||
|
let mut ctx = Context {
|
||||||
|
first_word_sent: false,
|
||||||
|
buffer: [0; 68],
|
||||||
|
buflen: 0,
|
||||||
|
algo: algorithm,
|
||||||
|
format: format,
|
||||||
|
imr: 0,
|
||||||
|
str: 0,
|
||||||
|
cr: 0,
|
||||||
|
csr: [0; NUM_CONTEXT_REGS],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set the data type in the peripheral.
|
||||||
|
PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8));
|
||||||
|
|
||||||
|
// Select the algorithm.
|
||||||
|
let mut algo0 = false;
|
||||||
|
let mut algo1 = false;
|
||||||
|
if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
|
||||||
|
algo0 = true;
|
||||||
|
}
|
||||||
|
if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
|
||||||
|
algo1 = true;
|
||||||
|
}
|
||||||
|
PAC_HASH.cr().modify(|w| w.set_algo0(algo0));
|
||||||
|
PAC_HASH.cr().modify(|w| w.set_algo1(algo1));
|
||||||
|
PAC_HASH.cr().modify(|w| w.set_init(true));
|
||||||
|
|
||||||
|
// Store and return the state of the peripheral.
|
||||||
|
self.store_context(&mut ctx);
|
||||||
|
ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restores the peripheral state using the given context,
|
||||||
|
/// then updates the state with the provided data.
|
||||||
|
pub fn update(&mut self, ctx: &mut Context, input: &[u8]) {
|
||||||
|
let mut data_waiting = input.len() + ctx.buflen;
|
||||||
|
if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
|
||||||
|
// There isn't enough data to digest a block, so append it to the buffer.
|
||||||
|
ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
|
||||||
|
ctx.buflen += input.len();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Restore the peripheral state.
|
||||||
|
self.load_context(&ctx);
|
||||||
|
|
||||||
|
let mut ilen_remaining = input.len();
|
||||||
|
let mut input_start = 0;
|
||||||
|
|
||||||
|
// Handle first block.
|
||||||
|
if !ctx.first_word_sent {
|
||||||
|
let empty_len = ctx.buffer.len() - ctx.buflen;
|
||||||
|
let copy_len = min(empty_len, ilen_remaining);
|
||||||
|
// Fill the buffer.
|
||||||
|
if copy_len > 0 {
|
||||||
|
ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);
|
||||||
|
ctx.buflen += copy_len;
|
||||||
|
ilen_remaining -= copy_len;
|
||||||
|
input_start += copy_len;
|
||||||
|
}
|
||||||
|
assert_eq!(ctx.buflen, HASH_BUFFER_LEN);
|
||||||
|
self.accumulate(ctx.buffer.as_slice());
|
||||||
|
data_waiting -= ctx.buflen;
|
||||||
|
ctx.buflen = 0;
|
||||||
|
ctx.first_word_sent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if data_waiting < 64 {
|
||||||
|
// There isn't enough data remaining to process another block, so store it.
|
||||||
|
assert_eq!(ctx.buflen, 0);
|
||||||
|
ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
|
||||||
|
ctx.buflen += ilen_remaining;
|
||||||
|
} else {
|
||||||
|
let mut total_data_sent = 0;
|
||||||
|
// First ingest the data in the buffer.
|
||||||
|
let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
|
||||||
|
if empty_len > 0 {
|
||||||
|
let copy_len = min(empty_len, ilen_remaining);
|
||||||
|
ctx.buffer[ctx.buflen..ctx.buflen + copy_len]
|
||||||
|
.copy_from_slice(&input[input_start..input_start + copy_len]);
|
||||||
|
ctx.buflen += copy_len;
|
||||||
|
ilen_remaining -= copy_len;
|
||||||
|
input_start += copy_len;
|
||||||
|
}
|
||||||
|
assert_eq!(ctx.buflen % 64, 0);
|
||||||
|
self.accumulate(&ctx.buffer[0..64]);
|
||||||
|
total_data_sent += ctx.buflen;
|
||||||
|
ctx.buflen = 0;
|
||||||
|
|
||||||
|
// Move any extra data to the now-empty buffer.
|
||||||
|
let leftovers = ilen_remaining % 64;
|
||||||
|
if leftovers > 0 {
|
||||||
|
assert!(ilen_remaining >= leftovers);
|
||||||
|
ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
|
||||||
|
ctx.buflen += leftovers;
|
||||||
|
ilen_remaining -= leftovers;
|
||||||
|
}
|
||||||
|
assert_eq!(ilen_remaining % 64, 0);
|
||||||
|
|
||||||
|
// Hash the remaining data.
|
||||||
|
self.accumulate(&input[input_start..input_start + ilen_remaining]);
|
||||||
|
|
||||||
|
total_data_sent += ilen_remaining;
|
||||||
|
assert_eq!(total_data_sent % 64, 0);
|
||||||
|
assert!(total_data_sent >= 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the peripheral context.
|
||||||
|
self.store_context(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes a digest for the given context. A slice of the provided digest buffer is returned.
|
||||||
|
/// The length of the returned slice is dependent on the digest length of the selected algorithm.
|
||||||
|
pub fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] {
|
||||||
|
// Restore the peripheral state.
|
||||||
|
self.load_context(&ctx);
|
||||||
|
// Hash the leftover bytes, if any.
|
||||||
|
self.accumulate(&ctx.buffer[0..ctx.buflen]);
|
||||||
|
ctx.buflen = 0;
|
||||||
|
|
||||||
|
//Start the digest calculation.
|
||||||
|
PAC_HASH.str().write(|w| w.set_dcal(true));
|
||||||
|
|
||||||
|
//Wait for completion.
|
||||||
|
while !PAC_HASH.sr().read().dcis() {}
|
||||||
|
|
||||||
|
//Return the digest.
|
||||||
|
let digest_words = match ctx.algo {
|
||||||
|
Algorithm::SHA1 => 5,
|
||||||
|
Algorithm::MD5 => 4,
|
||||||
|
Algorithm::SHA224 => 7,
|
||||||
|
Algorithm::SHA256 => 8,
|
||||||
|
};
|
||||||
|
let mut i = 0;
|
||||||
|
while i < digest_words {
|
||||||
|
let word = PAC_HASH.hr(i).read();
|
||||||
|
digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
&digest[0..digest_words * 4]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accumulate(&mut self, input: &[u8]) {
|
||||||
|
//Set the number of valid bits.
|
||||||
|
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
|
||||||
|
PAC_HASH.str().modify(|w| w.set_nblw(num_valid_bits));
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < input.len() {
|
||||||
|
let mut word: [u8; 4] = [0; 4];
|
||||||
|
let copy_idx = min(i + 4, input.len());
|
||||||
|
word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]);
|
||||||
|
PAC_HASH.din().write_value(u32::from_ne_bytes(word));
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Save the peripheral state to a context.
|
||||||
|
fn store_context(&mut self, ctx: &mut Context) {
|
||||||
|
while !PAC_HASH.sr().read().dinis() {}
|
||||||
|
ctx.imr = PAC_HASH.imr().read().0;
|
||||||
|
ctx.str = PAC_HASH.str().read().0;
|
||||||
|
ctx.cr = PAC_HASH.cr().read().0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NUM_CONTEXT_REGS {
|
||||||
|
ctx.csr[i] = PAC_HASH.csr(i).read();
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Restore the peripheral state from a context.
|
||||||
|
fn load_context(&mut self, ctx: &Context) {
|
||||||
|
// Restore the peripheral state from the context.
|
||||||
|
PAC_HASH.imr().write_value(Imr { 0: ctx.imr });
|
||||||
|
PAC_HASH.str().write_value(Str { 0: ctx.str });
|
||||||
|
PAC_HASH.cr().write_value(Cr { 0: ctx.cr });
|
||||||
|
PAC_HASH.cr().modify(|w| w.set_init(true));
|
||||||
|
let mut i = 0;
|
||||||
|
while i < NUM_CONTEXT_REGS {
|
||||||
|
PAC_HASH.csr(i).write_value(ctx.csr[i]);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,6 +45,8 @@ pub mod exti;
|
|||||||
pub mod flash;
|
pub mod flash;
|
||||||
#[cfg(fmc)]
|
#[cfg(fmc)]
|
||||||
pub mod fmc;
|
pub mod fmc;
|
||||||
|
#[cfg(hash)]
|
||||||
|
pub mod hash;
|
||||||
#[cfg(hrtim)]
|
#[cfg(hrtim)]
|
||||||
pub mod hrtim;
|
pub mod hrtim;
|
||||||
#[cfg(i2c)]
|
#[cfg(i2c)]
|
||||||
|
|||||||
@ -5,8 +5,8 @@ version = "0.1.0"
|
|||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Change stm32f767zi to your chip name, if necessary.
|
# Change stm32f777zi to your chip name, if necessary.
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
||||||
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
|
embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||||
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||||
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||||
@ -28,6 +28,7 @@ rand_core = "0.6.3"
|
|||||||
critical-section = "1.1"
|
critical-section = "1.1"
|
||||||
embedded-storage = "0.3.1"
|
embedded-storage = "0.3.1"
|
||||||
static_cell = "2"
|
static_cell = "2"
|
||||||
|
sha2 = { version = "0.10.8", default-features = false }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 2
|
debug = 2
|
||||||
|
|||||||
49
examples/stm32f7/src/bin/hash.rs
Normal file
49
examples/stm32f7/src/bin/hash.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_stm32::Config;
|
||||||
|
use embassy_time::{Duration, Instant};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
use embassy_stm32::hash::*;
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
|
const TEST_STRING_1: &[u8] = b"hello world";
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
let config = Config::default();
|
||||||
|
let p = embassy_stm32::init(config);
|
||||||
|
|
||||||
|
let hw_start_time = Instant::now();
|
||||||
|
|
||||||
|
// Compute a digest in hardware.
|
||||||
|
let mut hw_hasher = Hash::new(p.HASH);
|
||||||
|
let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8);
|
||||||
|
hw_hasher.update(&mut context, TEST_STRING_1);
|
||||||
|
let mut buffer: [u8; 32] = [0; 32];
|
||||||
|
let hw_digest = hw_hasher.finish(context, &mut buffer);
|
||||||
|
|
||||||
|
let hw_end_time = Instant::now();
|
||||||
|
let hw_execution_time = hw_end_time - hw_start_time;
|
||||||
|
|
||||||
|
let sw_start_time = Instant::now();
|
||||||
|
|
||||||
|
// Compute a digest in software.
|
||||||
|
let mut sw_hasher = Sha256::new();
|
||||||
|
sw_hasher.update(TEST_STRING_1);
|
||||||
|
let sw_digest = sw_hasher.finalize();
|
||||||
|
|
||||||
|
let sw_end_time = Instant::now();
|
||||||
|
let sw_execution_time = sw_end_time - sw_start_time;
|
||||||
|
|
||||||
|
info!("Hardware Digest: {:?}", hw_digest);
|
||||||
|
info!("Software Digest: {:?}", sw_digest[..]);
|
||||||
|
info!("Hardware Execution Time: {:?}", hw_execution_time);
|
||||||
|
info!("Software Execution Time: {:?}", sw_execution_time);
|
||||||
|
assert_eq!(*hw_digest, sw_digest[..]);
|
||||||
|
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user