1use core::convert::Infallible;
38
39use crate::{
40 pac,
41 peripheral::{Peripheral, PeripheralRef},
42 peripherals::HMAC,
43 reg_access::{AlignmentHelper, SocDependentEndianess},
44 system::{GenericPeripheralGuard, Peripheral as PeripheralEnable},
45};
46
47pub struct Hmac<'d> {
51 hmac: PeripheralRef<'d, HMAC>,
52 alignment_helper: AlignmentHelper<SocDependentEndianess>,
53 byte_written: usize,
54 next_command: NextCommand,
55 _guard: GenericPeripheralGuard<{ PeripheralEnable::Hmac as u8 }>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61pub enum Error {
62 KeyPurposeMismatch,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71#[allow(clippy::enum_variant_names, reason = "peripheral is unstable")]
72pub enum HmacPurpose {
73 ToJtag = 6,
75 ToDs = 7,
78 ToUser = 8,
80 ToDsOrJtag = 5,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86pub enum KeyId {
88 Key0 = 0,
90 Key1 = 1,
92 Key2 = 2,
94 Key3 = 3,
96 Key4 = 4,
98 Key5 = 5,
100}
101
102enum NextCommand {
103 None,
104 MessageIng,
105 MessagePad,
106}
107
108impl<'d> Hmac<'d> {
109 pub fn new(hmac: impl Peripheral<P = HMAC> + 'd) -> Self {
111 crate::into_ref!(hmac);
112
113 let guard = GenericPeripheralGuard::new();
114
115 Self {
116 hmac,
117 alignment_helper: AlignmentHelper::default(),
118 byte_written: 64,
119 next_command: NextCommand::None,
120 _guard: guard,
121 }
122 }
123
124 fn regs(&self) -> &pac::hmac::RegisterBlock {
125 self.hmac.register_block()
126 }
127
128 pub fn init(&mut self) {
134 self.regs().set_start().write(|w| w.set_start().set_bit());
135 self.alignment_helper.reset();
136 self.byte_written = 64;
137 self.next_command = NextCommand::None;
138 }
139
140 pub fn configure(&mut self, m: HmacPurpose, key_id: KeyId) -> nb::Result<(), Error> {
142 self.regs()
143 .set_para_purpose()
144 .write(|w| unsafe { w.purpose_set().bits(m as u8) });
145 self.regs()
146 .set_para_key()
147 .write(|w| unsafe { w.key_set().bits(key_id as u8) });
148 self.regs()
149 .set_para_finish()
150 .write(|w| w.set_para_end().set_bit());
151
152 if self.regs().query_error().read().query_check().bit_is_set() {
153 return Err(nb::Error::Other(Error::KeyPurposeMismatch));
154 }
155
156 Ok(())
157 }
158
159 pub fn update<'a>(&mut self, msg: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
163 if self.is_busy() {
164 return Err(nb::Error::WouldBlock);
165 }
166
167 self.next_command();
168
169 let remaining = self.write_data(msg).unwrap();
170
171 Ok(remaining)
172 }
173
174 pub fn finalize(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> {
176 if self.is_busy() {
177 return Err(nb::Error::WouldBlock);
178 }
179
180 self.next_command();
181
182 let msg_len = self.byte_written as u64;
183
184 nb::block!(self.write_data(&[0x80])).unwrap();
185 nb::block!(self.flush_data()).unwrap();
186 self.next_command();
187 debug_assert!(self.byte_written % 4 == 0);
188
189 self.padding(msg_len);
190
191 if msg_len < 64 + 56 {
193 self.regs()
194 .one_block()
195 .write(|w| w.set_one_block().set_bit());
196
197 while self.is_busy() {}
198 }
199
200 self.alignment_helper.volatile_read_regset(
201 #[cfg(esp32s2)]
202 self.regs().rd_result_(0).as_ptr(),
203 #[cfg(not(esp32s2))]
204 self.regs().rd_result_mem(0).as_ptr(),
205 output,
206 core::cmp::min(output.len(), 32) / self.alignment_helper.align_size(),
207 );
208
209 self.regs()
210 .set_result_finish()
211 .write(|w| w.set_result_end().set_bit());
212 self.byte_written = 64;
213 self.next_command = NextCommand::None;
214 Ok(())
215 }
216
217 fn is_busy(&mut self) -> bool {
218 self.regs().query_busy().read().busy_state().bit_is_set()
219 }
220
221 fn next_command(&mut self) {
222 match self.next_command {
223 NextCommand::MessageIng => {
224 self.regs()
225 .set_message_ing()
226 .write(|w| w.set_text_ing().set_bit());
227 }
228 NextCommand::MessagePad => {
229 self.regs()
230 .set_message_pad()
231 .write(|w| w.set_text_pad().set_bit());
232 }
233 NextCommand::None => {}
234 }
235 self.next_command = NextCommand::None;
236 }
237
238 fn write_data<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
239 let mod_length = self.byte_written % 64;
240
241 let (remaining, bound_reached) = self.alignment_helper.aligned_volatile_copy(
242 #[cfg(esp32s2)]
243 self.regs().wr_message_(0).as_ptr(),
244 #[cfg(not(esp32s2))]
245 self.regs().wr_message_mem(0).as_ptr(),
246 incoming,
247 64 / self.alignment_helper.align_size(),
248 mod_length / self.alignment_helper.align_size(),
249 );
250
251 self.byte_written = self
252 .byte_written
253 .wrapping_add(incoming.len() - remaining.len());
254
255 if bound_reached {
256 self.regs()
257 .set_message_one()
258 .write(|w| w.set_text_one().set_bit());
259
260 if remaining.len() >= 56 {
261 self.next_command = NextCommand::MessageIng;
262 } else {
263 self.next_command = NextCommand::MessagePad;
264 }
265 }
266
267 Ok(remaining)
268 }
269
270 fn flush_data(&mut self) -> nb::Result<(), Infallible> {
271 if self.is_busy() {
272 return Err(nb::Error::WouldBlock);
273 }
274
275 let flushed = self.alignment_helper.flush_to(
276 #[cfg(esp32s2)]
277 self.regs().wr_message_(0).as_ptr(),
278 #[cfg(not(esp32s2))]
279 self.regs().wr_message_mem(0).as_ptr(),
280 (self.byte_written % 64) / self.alignment_helper.align_size(),
281 );
282
283 self.byte_written = self.byte_written.wrapping_add(flushed);
284 if flushed > 0 && self.byte_written % 64 == 0 {
285 self.regs()
286 .set_message_one()
287 .write(|w| w.set_text_one().set_bit());
288 while self.is_busy() {}
289 self.next_command = NextCommand::MessagePad;
290 }
291
292 Ok(())
293 }
294
295 fn padding(&mut self, msg_len: u64) {
296 let mod_cursor = self.byte_written % 64;
297
298 if mod_cursor > 56 {
300 let pad_len = 64 - mod_cursor;
301 self.alignment_helper.volatile_write(
302 #[cfg(esp32s2)]
303 self.regs().wr_message_(0).as_ptr(),
304 #[cfg(not(esp32s2))]
305 self.regs().wr_message_mem(0).as_ptr(),
306 0_u8,
307 pad_len / self.alignment_helper.align_size(),
308 mod_cursor / self.alignment_helper.align_size(),
309 );
310 self.regs()
311 .set_message_one()
312 .write(|w| w.set_text_one().set_bit());
313 self.byte_written = self.byte_written.wrapping_add(pad_len);
314 debug_assert!(self.byte_written % 64 == 0);
315 while self.is_busy() {}
316 self.next_command = NextCommand::MessagePad;
317 self.next_command();
318 }
319
320 let mod_cursor = self.byte_written % 64;
321 let pad_len = 64 - mod_cursor - core::mem::size_of::<u64>();
322
323 self.alignment_helper.volatile_write(
324 #[cfg(esp32s2)]
325 self.regs().wr_message_(0).as_ptr(),
326 #[cfg(not(esp32s2))]
327 self.regs().wr_message_mem(0).as_ptr(),
328 0_u8,
329 pad_len / self.alignment_helper.align_size(),
330 mod_cursor / self.alignment_helper.align_size(),
331 );
332
333 self.byte_written = self.byte_written.wrapping_add(pad_len);
334
335 assert_eq!(self.byte_written % 64, 64 - core::mem::size_of::<u64>());
336
337 let len_mem = (msg_len * 8).to_be_bytes();
339
340 self.alignment_helper.aligned_volatile_copy(
341 #[cfg(esp32s2)]
342 self.regs().wr_message_(0).as_ptr(),
343 #[cfg(not(esp32s2))]
344 self.regs().wr_message_mem(0).as_ptr(),
345 &len_mem,
346 64 / self.alignment_helper.align_size(),
347 (64 - core::mem::size_of::<u64>()) / self.alignment_helper.align_size(),
348 );
349 self.regs()
350 .set_message_one()
351 .write(|w| w.set_text_one().set_bit());
352
353 while self.is_busy() {}
354 }
355}