use core::marker::PhantomData;
use crate::traits::aligned::{Aligned, A4};
use block_buffer::{BlockBuffer, Eager};
use digest::{FixedOutput, Output, OutputSizeUser};
use crate::{
peripherals::hashcrypt::Hashcrypt,
traits::{
digest::generic_array::{
typenum::{U20, U32, U64},
GenericArray,
},
digest::{Update },
},
typestates::init_state::Enabled,
};
type BlockSize = U64;
mod sealed {
use crate::traits::digest::generic_array::ArrayLength;
pub trait OutputSize: ArrayLength<u8> {}
impl OutputSize for super::U20 {}
impl OutputSize for super::U32 {}
}
use sealed::OutputSize;
pub struct Sha<'a, Size: OutputSize> {
buffer: Aligned<A4, BlockBuffer<BlockSize, Eager>>,
inner: &'a mut Hashcrypt<Enabled>,
len: u64,
size: PhantomData<Size>,
}
pub type Sha1<'a> = Sha<'a, U20>;
pub type Sha256<'a> = Sha<'a, U32>;
impl<'a, Size: OutputSize> Sha<'a, Size> {
pub fn new(hashcrypt: &'a mut Hashcrypt<Enabled>) -> Self {
let mut sha = Self {
buffer: Aligned(Default::default()),
inner: hashcrypt,
len: 0,
size: PhantomData,
};
sha.reset();
sha
}
pub fn into_inner(self) -> &'a mut Hashcrypt<Enabled> {
self.inner
}
pub fn reset(&mut self) {
self.buffer = Aligned(Default::default());
self.len = 0;
self.inner.ctrl.write(|w| w.new_hash().start());
match Size::to_usize() {
20 => self
.inner
.ctrl
.write(|w| w.new_hash().start().mode().sha1()),
32 => self
.inner
.ctrl
.write(|w| w.new_hash().start().mode().sha2_256()),
_ => unreachable!(),
}
}
}
impl<'a, Size: OutputSize> From<&'a mut Hashcrypt<Enabled>> for Sha<'a, Size> {
fn from(hashcrypt: &'a mut Hashcrypt<Enabled>) -> Self {
Sha::new(hashcrypt)
}
}
impl<Size: OutputSize> OutputSizeUser for Sha<'_, Size> {
type OutputSize = Size;
}
impl<Size: OutputSize> FixedOutput for Sha<'_, Size> {
fn finalize_into(mut self, out: &mut Output<Self>) {
self.finish();
for i in 0..Size::to_usize() / 4 {
out.as_mut_slice()[4 * i..4 * i + 4]
.copy_from_slice(&self.inner.raw.digest0[i].read().bits().to_be_bytes());
}
}
}
impl<Size: OutputSize> Update for Sha<'_, Size> {
fn update(&mut self, data: &[u8]) {
self.update(data.as_ref());
}
}
impl<Size: OutputSize> Sha<'_, Size> {
fn update(&mut self, data: &[u8]) {
self.len += (data.len() as u64) << 3;
let peripheral = &mut self.inner;
self.buffer.digest_blocks(data, |data| {
for b in data {
Self::process_block(peripheral, b)
}
});
}
fn process_block(peripheral: &mut Hashcrypt<Enabled>, input: &GenericArray<u8, BlockSize>) {
let input: Aligned<A4, GenericArray<u8, BlockSize>> = Aligned(*input);
let addr: u32 = &input[0] as *const _ as _;
assert_eq!(addr & 0x3, 0);
while peripheral.raw.status.read().waiting().is_not_waiting() {
continue;
}
peripheral.raw.memaddr.write(|w| unsafe { w.bits(addr) });
peripheral
.raw
.memctrl
.write(|w| unsafe { w.master().enabled().count().bits(1) });
}
fn finish(&mut self) {
let peripheral = &mut self.inner;
let l = self.len;
self.buffer
.len64_padding_be(l, |block| Self::process_block(peripheral, block));
while peripheral.raw.status.read().digest().is_not_ready() {
continue;
}
}
}
impl<Size: OutputSize> digest::Reset for Sha<'_, Size> {
fn reset(&mut self) {
self.reset();
}
}