use core::cmp::min;
#[cfg(hash_v2)]
use core::future::poll_fn;
use core::marker::PhantomData;
#[cfg(hash_v2)]
use core::ptr;
#[cfg(hash_v2)]
use core::task::Poll;
use embassy_hal_internal::{into_ref, PeripheralRef};
use embassy_sync::waitqueue::AtomicWaker;
use stm32_metapac::hash::regs::*;
use crate::dma::NoDma;
#[cfg(hash_v2)]
use crate::dma::Transfer;
use crate::interrupt::typelevel::Interrupt;
use crate::peripherals::HASH;
use crate::{interrupt, pac, peripherals, rcc, Peripheral};
#[cfg(hash_v1)]
const NUM_CONTEXT_REGS: usize = 51;
#[cfg(hash_v3)]
const NUM_CONTEXT_REGS: usize = 103;
#[cfg(any(hash_v2, hash_v4))]
const NUM_CONTEXT_REGS: usize = 54;
const HASH_BUFFER_LEN: usize = 132;
const DIGEST_BLOCK_SIZE: usize = 128;
static HASH_WAKER: AtomicWaker = AtomicWaker::new();
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
let bits = T::regs().sr().read();
if bits.dinis() {
T::regs().imr().modify(|reg| reg.set_dinie(false));
HASH_WAKER.wake();
}
if bits.dcis() {
T::regs().imr().modify(|reg| reg.set_dcie(false));
HASH_WAKER.wake();
}
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum Algorithm {
SHA1 = 0,
#[cfg(any(hash_v1, hash_v2, hash_v4))]
MD5 = 1,
SHA224 = 2,
SHA256 = 3,
#[cfg(hash_v3)]
SHA384 = 12,
#[cfg(hash_v3)]
SHA512_224 = 13,
#[cfg(hash_v3)]
SHA512_256 = 14,
#[cfg(hash_v3)]
SHA512 = 15,
}
#[repr(u8)]
#[derive(Clone, Copy)]
pub enum DataType {
Width32 = 0,
Width16 = 1,
Width8 = 2,
Width1 = 3,
}
pub struct Context<'c> {
first_word_sent: bool,
key_sent: bool,
buffer: [u8; HASH_BUFFER_LEN],
buflen: usize,
algo: Algorithm,
format: DataType,
imr: u32,
str: u32,
cr: u32,
csr: [u32; NUM_CONTEXT_REGS],
key: HmacKey<'c>,
}
type HmacKey<'k> = Option<&'k [u8]>;
pub struct Hash<'d, T: Instance, D = NoDma> {
_peripheral: PeripheralRef<'d, T>,
#[allow(dead_code)]
dma: PeripheralRef<'d, D>,
}
impl<'d, T: Instance, D> Hash<'d, T, D> {
pub fn new(
peripheral: impl Peripheral<P = T> + 'd,
dma: impl Peripheral<P = D> + 'd,
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
) -> Self {
rcc::enable_and_reset::<HASH>();
into_ref!(peripheral, dma);
let instance = Self {
_peripheral: peripheral,
dma: dma,
};
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
instance
}
pub fn start<'c>(&mut self, algorithm: Algorithm, format: DataType, key: HmacKey<'c>) -> Context<'c> {
let mut ctx = Context {
first_word_sent: false,
key_sent: false,
buffer: [0; HASH_BUFFER_LEN],
buflen: 0,
algo: algorithm,
format: format,
imr: 0,
str: 0,
cr: 0,
csr: [0; NUM_CONTEXT_REGS],
key,
};
T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
#[cfg(hash_v1)]
if ctx.algo == Algorithm::MD5 {
T::regs().cr().modify(|w| w.set_algo(true));
}
#[cfg(hash_v2)]
{
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;
}
T::regs().cr().modify(|w| w.set_algo0(algo0));
T::regs().cr().modify(|w| w.set_algo1(algo1));
}
#[cfg(any(hash_v3, hash_v4))]
T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));
if let Some(key) = ctx.key {
T::regs().cr().modify(|w| w.set_mode(true));
if key.len() > 64 {
T::regs().cr().modify(|w| w.set_lkey(true));
}
}
T::regs().cr().modify(|w| w.set_init(true));
self.store_context(&mut ctx);
ctx
}
pub fn update_blocking<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8]) {
self.load_context(&ctx);
if !ctx.key_sent {
if let Some(key) = ctx.key {
self.accumulate_blocking(key);
T::regs().str().write(|w| w.set_dcal(true));
while !T::regs().sr().read().dinis() {}
}
ctx.key_sent = true;
}
let mut data_waiting = input.len() + ctx.buflen;
if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
ctx.buflen += input.len();
self.store_context(ctx);
return;
}
let mut ilen_remaining = input.len();
let mut input_start = 0;
if !ctx.first_word_sent {
let empty_len = ctx.buffer.len() - ctx.buflen;
let copy_len = min(empty_len, ilen_remaining);
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;
}
self.accumulate_blocking(ctx.buffer.as_slice());
data_waiting -= ctx.buflen;
ctx.buflen = 0;
ctx.first_word_sent = true;
}
if data_waiting < DIGEST_BLOCK_SIZE {
ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
ctx.buflen += ilen_remaining;
} else {
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;
}
self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]);
ctx.buflen = 0;
let leftovers = ilen_remaining % 64;
if leftovers > 0 {
ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
ctx.buflen += leftovers;
ilen_remaining -= leftovers;
}
self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]);
}
self.store_context(ctx);
}
#[cfg(hash_v2)]
pub async fn update<'c>(&mut self, ctx: &mut Context<'c>, input: &[u8])
where
D: crate::hash::Dma<T>,
{
self.load_context(&ctx);
if !ctx.key_sent {
if let Some(key) = ctx.key {
self.accumulate(key).await;
}
ctx.key_sent = true;
}
let data_waiting = input.len() + ctx.buflen;
if data_waiting < DIGEST_BLOCK_SIZE {
ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
ctx.buflen += input.len();
self.store_context(ctx);
return;
}
T::regs().cr().modify(|w| w.set_mdmat(true));
let mut ilen_remaining = input.len();
let mut input_start = 0;
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;
}
self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await;
ctx.buflen = 0;
let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
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;
} else {
ctx.buffer
.copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
ctx.buflen += DIGEST_BLOCK_SIZE;
ilen_remaining -= DIGEST_BLOCK_SIZE;
}
self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
self.store_context(ctx);
}
pub fn finish_blocking<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize {
self.load_context(&ctx);
self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]);
ctx.buflen = 0;
T::regs().str().write(|w| w.set_dcal(true));
if let Some(key) = ctx.key {
while !T::regs().sr().read().dinis() {}
self.accumulate_blocking(key);
T::regs().str().write(|w| w.set_dcal(true));
}
while !T::regs().sr().read().dcis() {}
let digest_words = match ctx.algo {
Algorithm::SHA1 => 5,
#[cfg(any(hash_v1, hash_v2, hash_v4))]
Algorithm::MD5 => 4,
Algorithm::SHA224 => 7,
Algorithm::SHA256 => 8,
#[cfg(hash_v3)]
Algorithm::SHA384 => 12,
#[cfg(hash_v3)]
Algorithm::SHA512_224 => 7,
#[cfg(hash_v3)]
Algorithm::SHA512_256 => 8,
#[cfg(hash_v3)]
Algorithm::SHA512 => 16,
};
let digest_len_bytes = digest_words * 4;
if digest.len() < digest_len_bytes {
panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
}
let mut i = 0;
while i < digest_words {
let word = T::regs().hr(i).read();
digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
i += 1;
}
digest_len_bytes
}
#[cfg(hash_v2)]
pub async fn finish<'c>(&mut self, mut ctx: Context<'c>, digest: &mut [u8]) -> usize
where
D: crate::hash::Dma<T>,
{
self.load_context(&ctx);
T::regs().cr().modify(|w| w.set_mdmat(false));
self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
ctx.buflen = 0;
if let Some(key) = ctx.key {
self.accumulate(key).await;
}
poll_fn(|cx| {
let bits = T::regs().sr().read();
if bits.dcis() {
return Poll::Ready(());
}
HASH_WAKER.register(cx.waker());
T::regs().imr().modify(|reg| reg.set_dcie(true));
let bits = T::regs().sr().read();
if bits.dcis() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await;
let digest_words = match ctx.algo {
Algorithm::SHA1 => 5,
#[cfg(any(hash_v1, hash_v2, hash_v4))]
Algorithm::MD5 => 4,
Algorithm::SHA224 => 7,
Algorithm::SHA256 => 8,
#[cfg(hash_v3)]
Algorithm::SHA384 => 12,
#[cfg(hash_v3)]
Algorithm::SHA512_224 => 7,
#[cfg(hash_v3)]
Algorithm::SHA512_256 => 8,
#[cfg(hash_v3)]
Algorithm::SHA512 => 16,
};
let digest_len_bytes = digest_words * 4;
if digest.len() < digest_len_bytes {
panic!("Digest buffer must be at least {} bytes long.", digest_words * 4);
}
let mut i = 0;
while i < digest_words {
let word = T::regs().hr(i).read();
digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
i += 1;
}
digest_len_bytes
}
fn accumulate_blocking(&mut self, input: &[u8]) {
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
T::regs().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]);
T::regs().din().write_value(u32::from_ne_bytes(word));
i += 4;
}
}
#[cfg(hash_v2)]
async fn accumulate(&mut self, input: &[u8])
where
D: crate::hash::Dma<T>,
{
if input.len() == 0 {
return;
}
let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
let dma_request = self.dma.request();
let dst_ptr = T::regs().din().as_ptr();
let mut num_words = input.len() / 4;
if input.len() % 4 > 0 {
num_words += 1;
}
let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
let dma_transfer =
unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
T::regs().cr().modify(|w| w.set_dmae(true));
dma_transfer.await;
}
fn store_context<'c>(&mut self, ctx: &mut Context<'c>) {
while !T::regs().sr().read().dinis() {}
ctx.imr = T::regs().imr().read().0;
ctx.str = T::regs().str().read().0;
ctx.cr = T::regs().cr().read().0;
let mut i = 0;
while i < NUM_CONTEXT_REGS {
ctx.csr[i] = T::regs().csr(i).read();
i += 1;
}
}
fn load_context(&mut self, ctx: &Context) {
T::regs().imr().write_value(Imr { 0: ctx.imr });
T::regs().str().write_value(Str { 0: ctx.str });
T::regs().cr().write_value(Cr { 0: ctx.cr });
T::regs().cr().modify(|w| w.set_init(true));
let mut i = 0;
while i < NUM_CONTEXT_REGS {
T::regs().csr(i).write_value(ctx.csr[i]);
i += 1;
}
}
}
trait SealedInstance {
fn regs() -> pac::hash::Hash;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
type Interrupt: interrupt::typelevel::Interrupt;
}
foreach_interrupt!(
($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
impl Instance for peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
impl SealedInstance for peripherals::$inst {
fn regs() -> crate::pac::hash::Hash {
crate::pac::$inst
}
}
};
);
dma_trait!(Dma, Instance);