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