esp_hal/
hmac.rs

1//! # Hash-based Message Authentication Code (HMAC) Accelerator
2//!
3//! ## Overview
4//! HMAC is a secure authentication technique that verifies the authenticity and
5//! integrity of a message with a pre-shared key. This module provides hardware
6//! acceleration for SHA256-HMAC generation using a key burned into an eFuse
7//! block.
8//!
9//! Main features:
10//!
11//! - Standard HMAC-SHA-256 algorithm.
12//! - Hash result only accessible by configurable hardware peripheral (in
13//!   downstream mode).
14//! - Compatible to challenge-response authentication algorithm.
15//! - Generates required keys for the Digital Signature (DS) peripheral (in
16//!   downstream mode).
17//! - Re-enables soft-disabled JTAG (in downstream mode).
18//!
19//! ## Configuration
20//! The HMAC module can be used in two modes - in ”upstream” mode the HMAC
21//! message is supplied by the user and the calculation result is read back by
22//! the user. In ”downstream” mode the HMAC module is used as a Key Derivation
23//! Function (KDF) for other internal hardwares.
24//!
25//! ### HMAC padding
26//!
27//! The HMAC padding is handled by the driver. In downstream mode, users do not
28//! need to input any message or apply padding. The HMAC module uses a default
29//! 32-byte pattern of 0x00 for re-enabling JTAG and a 32-byte pattern of 0xff
30//! for deriving the AES key for the DS module.
31//!
32//! ## Examples
33//! Visit the [HMAC] example to learn how to use the HMAC accelerator
34//!
35//! [HMAC]: https://github.com/esp-rs/esp-hal/blob/main/examples/src/bin/hmac.rs
36
37use 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
47/// Provides an interface for interacting with the HMAC hardware peripheral.
48/// It allows users to compute HMACs for cryptographic purposes, ensuring data
49/// integrity and authenticity.
50pub 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/// HMAC interface error
59#[derive(Debug, Clone, Copy, PartialEq)]
60#[cfg_attr(feature = "defmt", derive(defmt::Format))]
61pub enum Error {
62    /// It means the purpose of the selected block does not match the
63    /// configured key purpose and the calculation will not proceed.
64    KeyPurposeMismatch,
65}
66
67/// The peripheral can be configured to deliver its output directly to the
68/// user. It can also deliver to other peripherals.
69#[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    /// HMAC is used to re-enable JTAG after soft-disabling it.
74    ToJtag     = 6,
75    /// HMAC is provided to the digital signature peripheral to decrypt the
76    /// private key.
77    ToDs       = 7,
78    /// Let the user provide a message and read the result.
79    ToUser     = 8,
80    /// HMAC is used for both the digital signature or JTAG.
81    ToDsOrJtag = 5,
82}
83
84#[derive(Debug, Clone, Copy, PartialEq)]
85#[cfg_attr(feature = "defmt", derive(defmt::Format))]
86/// Represents the key identifiers for the HMAC peripheral.
87pub enum KeyId {
88    /// Key 0.
89    Key0 = 0,
90    /// Key 1.
91    Key1 = 1,
92    /// Key 2.
93    Key2 = 2,
94    /// Key 3.
95    Key3 = 3,
96    /// Key 4.
97    Key4 = 4,
98    /// Key 5.
99    Key5 = 5,
100}
101
102enum NextCommand {
103    None,
104    MessageIng,
105    MessagePad,
106}
107
108impl<'d> Hmac<'d> {
109    /// Creates a new instance of the HMAC peripheral.
110    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    /// Step 1. Enable HMAC module.
129    ///
130    /// Before these steps, the user shall set the peripheral clocks bits for
131    /// HMAC and SHA peripherals and clear the corresponding peripheral
132    /// reset bits.
133    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    /// Step 2. Configure HMAC keys and key purposes.
141    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    /// Process the msg block after block
160    ///
161    /// Call this function as many times as necessary (msg.len() > 0)
162    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    /// Finalizes the HMAC computation and retrieves the resulting hash output.
175    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        // Checking if the message is one block including padding
192        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        // The padding will be spanned over 2 blocks
299        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        // Add padded key
338        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}