use core::convert::Infallible;
use crate::{
pac,
peripherals::HMAC,
reg_access::{AlignmentHelper, SocDependentEndianess},
system::{GenericPeripheralGuard, Peripheral as PeripheralEnable},
};
pub struct Hmac<'d> {
hmac: HMAC<'d>,
alignment_helper: AlignmentHelper<SocDependentEndianess>,
byte_written: usize,
next_command: NextCommand,
_guard: GenericPeripheralGuard<{ PeripheralEnable::Hmac as u8 }>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
KeyPurposeMismatch,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(clippy::enum_variant_names, reason = "peripheral is unstable")]
pub enum HmacPurpose {
ToJtag = 6,
ToDs = 7,
ToUser = 8,
ToDsOrJtag = 5,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum KeyId {
Key0 = 0,
Key1 = 1,
Key2 = 2,
Key3 = 3,
Key4 = 4,
Key5 = 5,
}
enum NextCommand {
None,
MessageIng,
MessagePad,
}
impl<'d> Hmac<'d> {
pub fn new(hmac: HMAC<'d>) -> Self {
let guard = GenericPeripheralGuard::new();
Self {
hmac,
alignment_helper: AlignmentHelper::default(),
byte_written: 64,
next_command: NextCommand::None,
_guard: guard,
}
}
fn regs(&self) -> &pac::hmac::RegisterBlock {
self.hmac.register_block()
}
pub fn init(&mut self) {
self.regs().set_start().write(|w| w.set_start().set_bit());
self.alignment_helper.reset();
self.byte_written = 64;
self.next_command = NextCommand::None;
}
pub fn configure(&mut self, m: HmacPurpose, key_id: KeyId) -> nb::Result<(), Error> {
self.regs()
.set_para_purpose()
.write(|w| unsafe { w.purpose_set().bits(m as u8) });
self.regs()
.set_para_key()
.write(|w| unsafe { w.key_set().bits(key_id as u8) });
self.regs()
.set_para_finish()
.write(|w| w.set_para_end().set_bit());
if self.regs().query_error().read().query_check().bit_is_set() {
return Err(nb::Error::Other(Error::KeyPurposeMismatch));
}
Ok(())
}
pub fn update<'a>(&mut self, msg: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
if self.is_busy() {
return Err(nb::Error::WouldBlock);
}
self.next_command();
let remaining = self.write_data(msg).unwrap();
Ok(remaining)
}
pub fn finalize(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> {
if self.is_busy() {
return Err(nb::Error::WouldBlock);
}
self.next_command();
let msg_len = self.byte_written as u64;
nb::block!(self.write_data(&[0x80])).unwrap();
nb::block!(self.flush_data()).unwrap();
self.next_command();
debug_assert!(self.byte_written.is_multiple_of(4));
self.padding(msg_len);
if msg_len < 64 + 56 {
self.regs()
.one_block()
.write(|w| w.set_one_block().set_bit());
while self.is_busy() {}
}
self.alignment_helper.volatile_read_regset(
#[cfg(esp32s2)]
self.regs().rd_result_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().rd_result_mem(0).as_ptr(),
output,
core::cmp::min(output.len(), 32),
);
self.regs()
.set_result_finish()
.write(|w| w.set_result_end().set_bit());
self.byte_written = 64;
self.next_command = NextCommand::None;
Ok(())
}
fn is_busy(&mut self) -> bool {
self.regs().query_busy().read().busy_state().bit_is_set()
}
fn next_command(&mut self) {
match self.next_command {
NextCommand::MessageIng => {
self.regs()
.set_message_ing()
.write(|w| w.set_text_ing().set_bit());
}
NextCommand::MessagePad => {
self.regs()
.set_message_pad()
.write(|w| w.set_text_pad().set_bit());
}
NextCommand::None => {}
}
self.next_command = NextCommand::None;
}
fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy(
#[cfg(esp32s2)]
self.regs().wr_message_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().wr_message_mem(0).as_ptr(),
incoming,
64,
self.byte_written % 64,
);
self.byte_written = self
.byte_written
.wrapping_add(incoming.len() - remaining.len());
if bound_reached {
self.regs()
.set_message_one()
.write(|w| w.set_text_one().set_bit());
if remaining.len() >= 56 {
self.next_command = NextCommand::MessageIng;
} else {
self.next_command = NextCommand::MessagePad;
}
}
Ok(remaining)
}
fn flush_data(&mut self) -> nb::Result<(), Infallible> {
if self.is_busy() {
return Err(nb::Error::WouldBlock);
}
let flushed = self.alignment_helper.flush_to(
#[cfg(esp32s2)]
self.regs().wr_message_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().wr_message_mem(0).as_ptr(),
self.byte_written % 64,
);
self.byte_written = self.byte_written.wrapping_add(flushed);
if flushed > 0 && self.byte_written.is_multiple_of(64) {
self.regs()
.set_message_one()
.write(|w| w.set_text_one().set_bit());
while self.is_busy() {}
self.next_command = NextCommand::MessagePad;
}
Ok(())
}
fn padding(&mut self, msg_len: u64) {
let mod_cursor = self.byte_written % 64;
if mod_cursor > 56 {
let pad_len = 64 - mod_cursor;
self.alignment_helper.volatile_write(
#[cfg(esp32s2)]
self.regs().wr_message_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().wr_message_mem(0).as_ptr(),
0_u8,
pad_len,
mod_cursor,
);
self.regs()
.set_message_one()
.write(|w| w.set_text_one().set_bit());
self.byte_written = self.byte_written.wrapping_add(pad_len);
debug_assert!(self.byte_written.is_multiple_of(64));
while self.is_busy() {}
self.next_command = NextCommand::MessagePad;
self.next_command();
}
let mod_cursor = self.byte_written % 64;
let pad_len = 64 - mod_cursor - core::mem::size_of::<u64>();
self.alignment_helper.volatile_write(
#[cfg(esp32s2)]
self.regs().wr_message_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().wr_message_mem(0).as_ptr(),
0_u8,
pad_len,
mod_cursor,
);
self.byte_written = self.byte_written.wrapping_add(pad_len);
assert_eq!(self.byte_written % 64, 64 - core::mem::size_of::<u64>());
let len_mem = (msg_len * 8).to_be_bytes();
self.alignment_helper.aligned_volatile_copy(
#[cfg(esp32s2)]
self.regs().wr_message_(0).as_ptr(),
#[cfg(not(esp32s2))]
self.regs().wr_message_mem(0).as_ptr(),
&len_mem,
64,
64 - core::mem::size_of::<u64>(),
);
self.regs()
.set_message_one()
.write(|w| w.set_text_one().set_bit());
while self.is_busy() {}
}
}