1
#![cfg_attr(docsrs, procmacros::doc_replace)]
2
//! # Secure Hash Algorithm (SHA) Accelerator
3
//!
4
//! ## Overview
5
//! This SHA accelerator is a hardware device that speeds up the SHA algorithm
6
//! significantly, compared to a SHA algorithm implemented solely in software
7
//!
8
//! ## Configuration
9
//! This driver performs cryptographic hash operations using
10
//! various hash algorithms supported by the SHA peripheral, such as:
11
//! * SHA-1
12
//! * SHA-224
13
//! * SHA-256
14
//! * SHA-384
15
//! * SHA-512
16
//! * SM3
17
//!
18
//! It provides functions to update the hash calculation with input data, finish
19
//! the hash calculation and retrieve the resulting hash value. The SHA
20
//! peripheral on ESP chips can handle large data streams efficiently, making it
21
//! suitable for cryptographic applications that require secure hashing.
22
//!
23
//! To use the SHA Peripheral Driver, you need to initialize it with the desired
24
//! SHA mode and the corresponding SHA peripheral. Once initialized, you can
25
//! update the hash calculation by providing input data, finish the calculation
26
//! to retrieve the hash value and repeat the process for a new hash calculation
27
//! if needed.
28
//!
29
//! ## Examples
30
//!
31
//! ### Using the `Sha` driver
32
//!
33
//! ```rust, no_run
34
//! # {before_snippet}
35
//! # use esp_hal::sha::Sha;
36
//! # use esp_hal::sha::Sha256;
37
//! # use nb::block;
38
//! let mut source_data = "HELLO, ESPRESSIF!".as_bytes();
39
//! let mut sha = Sha::new(peripherals.SHA);
40
//! let mut hasher = sha.start::<Sha256>();
41
//! // Short hashes can be created by decreasing the output buffer to the
42
//! // desired length
43
//! let mut output = [0u8; 32];
44
//!
45
//! while !source_data.is_empty() {
46
//!     // All the HW Sha functions are infallible so unwrap is fine to use if
47
//!     // you use block!
48
//!     source_data = block!(hasher.update(source_data))?;
49
//! }
50
//!
51
//! // Finish can be called as many times as desired to get multiple copies of
52
//! // the output.
53
//! block!(hasher.finish(output.as_mut_slice()))?;
54
//!
55
//! # {after_snippet}
56
//! ```
57
//!
58
//! ### Using the `ShaBackend` driver
59
//!
60
//! ```rust, no_run
61
//! # {before_snippet}
62
//! use esp_hal::sha::{Sha1Context, ShaBackend};
63
//! #
64
//! let mut sha = ShaBackend::new(peripherals.SHA);
65
//! // Start the backend, which allows processing SHA operations.
66
//! let _backend = sha.start();
67
//!
68
//! // Create a new context to hash data with SHA-1.
69
//! let mut sha1_ctx = Sha1Context::new();
70
//!
71
//! // SHA-1 outputs a 20-byte digest.
72
//! let mut digest: [u8; 20] = [0; 20];
73
//!
74
//! // Process data. The `update` function returns a handle which can be used to wait
75
//! // for the operation to finish.
76
//! sha1_ctx.update(b"input data").wait_blocking();
77
//! sha1_ctx.update(b"input data").wait_blocking();
78
//! sha1_ctx.update(b"input data").wait_blocking();
79
//!
80
//! // Extract the final hash. This resets the context.
81
//! sha1_ctx.finalize(&mut digest).wait_blocking();
82
//!
83
//! // digest now contains the SHA-1 hash of the input.
84
//! #
85
//! # {after_snippet}
86
//! ```
87

            
88
#![allow(deprecated, reason = "generic_array 0.14 has been deprecated")]
89

            
90
use core::{
91
    borrow::BorrowMut,
92
    convert::Infallible,
93
    marker::PhantomData,
94
    mem::size_of,
95
    ptr::NonNull,
96
};
97

            
98
/// Re-export digest for convenience
99
pub use digest_010::Digest as Digest010;
100
pub use digest_011::Digest as Digest011;
101

            
102
use crate::{
103
    peripherals::SHA,
104
    reg_access::{AlignmentHelper, SocDependentEndianess},
105
    system::{CryptoClockGuard, GenericPeripheralGuard},
106
    work_queue::{Handle, Poll, Status, VTable, WorkQueue, WorkQueueDriver, WorkQueueFrontend},
107
};
108

            
109
// ESP32 quirks:
110
// - Big endian text register (what about hash?)
111
// - Text and hash is in the same register -> needs an additional load operation to place the hash
112
//   in the text registers
113
// - Each algorithm has its own register cluster
114
// - No support for interleaved operation
115

            
116
/// The SHA Accelerator driver instance.
117
pub struct Sha<'d> {
118
    sha: SHA<'d>,
119
    _guard: GenericPeripheralGuard<{ crate::system::Peripheral::Sha as u8 }>,
120
    _clock_guard: CryptoClockGuard,
121
}
122

            
123
impl<'d> Sha<'d> {
124
    /// Creates a new instance of the SHA Accelerator driver.
125
4
    pub fn new(sha: SHA<'d>) -> Self {
126
        let clock_guard = CryptoClockGuard::new();
127
        let guard = GenericPeripheralGuard::new();
128

            
129
        Self {
130
            sha,
131
            _guard: guard,
132
            _clock_guard: clock_guard,
133
        }
134
4
    }
135

            
136
    /// Starts a new digest.
137
    #[cfg_attr(
138
        sha_has_sm3,
139
        doc = "\n# Panics\nPanics if SM3 is selected and disabled by eFuse."
140
    )]
141
    pub fn start<'a, A: ShaAlgorithm>(&'a mut self) -> ShaDigest<'d, A, &'a mut Self> {
142
        ShaDigest::new(self)
143
    }
144

            
145
    /// Starts a new digest and take ownership of the driver.
146
    /// This is useful for storage outside a function body. i.e. in static or
147
    /// struct.
148
    #[cfg_attr(
149
        sha_has_sm3,
150
        doc = "\n# Panics\nPanics if SM3 is selected and disabled by eFuse."
151
    )]
152
    pub fn start_owned<A: ShaAlgorithm>(self) -> ShaDigest<'d, A, Self> {
153
        ShaDigest::new(self)
154
    }
155

            
156
    /// Returns whether the hardware is processing the next message.
157
    fn is_busy(&self, algo: ShaAlgorithmKind) -> bool {
158
        algo.is_busy(&self.sha)
159
    }
160

            
161
    fn process_buffer(&self, state: &mut DigestState) {
162
1476
        if state.first_run {
163
            state.algorithm.start(&self.sha);
164
879
            state.first_run = false;
165
        } else {
166
            state.algorithm.r#continue(&self.sha);
167
        }
168
    }
169

            
170
3925
    fn write_data<'a>(
171
        &self,
172
        state: &mut DigestState,
173
        incoming: &'a [u8],
174
    ) -> nb::Result<&'a [u8], Infallible> {
175
3925
        if state.message_buffer_is_full {
176
            if self.is_busy(state.algorithm) {
177
                // The message buffer is full and the hardware is still processing the previous
178
                // message. There's nothing to be done besides wait for the hardware.
179
                return Err(nb::Error::WouldBlock);
180
            } else {
181
                // Submit the full buffer.
182
                self.process_buffer(state);
183
                // The buffer is now free for filling.
184
                state.message_buffer_is_full = false;
185
            }
186
        }
187

            
188
        let chunk_len = state.algorithm.chunk_length();
189
3925
        let mod_cursor = state.cursor % chunk_len;
190

            
191
3925
        let (remaining, bound_reached) = state.alignment_helper.aligned_volatile_copy(
192
            m_mem(&self.sha, 0),
193
            incoming,
194
            chunk_len,
195
            mod_cursor,
196
        );
197

            
198
3925
        state.cursor += incoming.len() - remaining.len();
199

            
200
        if bound_reached {
201
            // Message is full now. We don't have to wait for the result, just start the processing
202
            // or set the flag.
203
            _ = self.process_buffer_or_wait(state);
204
        }
205

            
206
        Ok(remaining)
207
2449
    }
208

            
209
    fn process_buffer_or_wait(&self, state: &mut DigestState) -> nb::Result<(), Infallible> {
210
1266
        if self.is_busy(state.algorithm) {
211
            // The message buffer is full and the hardware is still processing the
212
            // previous message. There's nothing to be done besides wait for the
213
            // hardware.
214
            state.message_buffer_is_full = true;
215
            return Err(nb::Error::WouldBlock);
216
        }
217

            
218
        // Send the full buffer.
219
        self.process_buffer(state);
220

            
221
        Ok(())
222
    }
223

            
224
1266
    fn finish(&self, state: &mut DigestState, output: &mut [u8]) -> nb::Result<(), Infallible> {
225
1266
        if state.message_buffer_is_full {
226
            // Wait for the hardware to become idle.
227
            if self.is_busy(state.algorithm) {
228
                return Err(nb::Error::WouldBlock);
229
            }
230

            
231
            // Start processing so that we can continue writing into SHA memory.
232
            self.process_buffer(state);
233
            state.message_buffer_is_full = false;
234
        }
235

            
236
        let chunk_len = state.algorithm.chunk_length();
237
1266
        if state.finalize_state == FinalizeState::NotStarted {
238
1266
            let cursor = state.cursor;
239
            self.update(state, &[0x80])?; // Append "1" bit
240
1266
            state.finished_message_size = cursor;
241

            
242
1266
            state.finalize_state = FinalizeState::FlushAlignBuffer;
243
        }
244

            
245
        if state.finalize_state == FinalizeState::FlushAlignBuffer {
246
            let flushed = state
247
                .alignment_helper
248
1266
                .flush_to(m_mem(&self.sha, 0), state.cursor % chunk_len);
249

            
250
966
            state.finalize_state = FinalizeState::ZeroPadAlmostFull;
251
            if flushed > 0 {
252
966
                state.cursor += flushed;
253
966
                if state.cursor.is_multiple_of(chunk_len) {
254
                    self.process_buffer_or_wait(state)?;
255
                }
256
            }
257
        }
258

            
259
966
        let mut mod_cursor = state.cursor % chunk_len;
260
966
        if state.finalize_state == FinalizeState::ZeroPadAlmostFull {
261
            // Zero out remaining data if buffer is almost full (>=448/896), and process
262
            // buffer.
263
            //
264
            // In either case, we'll continue to the next state.
265
966
            state.finalize_state = FinalizeState::WriteMessageLength;
266
72
            let pad_len = chunk_len - mod_cursor;
267
966
            if pad_len < state.algorithm.message_length_bytes() {
268
72
                state.alignment_helper.volatile_write(
269
                    m_mem(&self.sha, 0),
270
                    0_u8,
271
                    pad_len,
272
                    mod_cursor,
273
                );
274
72
                state.cursor += pad_len;
275

            
276
                self.process_buffer_or_wait(state)?;
277
                mod_cursor = 0;
278
            }
279
        }
280

            
281
48
        if state.finalize_state == FinalizeState::WriteMessageLength {
282
            // In this state, we pad the remainder of the message block with 0s and append the
283
            // message length to the very end.
284
            // FIXME: this u64 should be u128 for 1024-bit block algos. Since cursor is only usize
285
            // (u32), this makes no difference currently, but may limit maximum message length in
286
            // the future.
287
            let message_len_bytes = size_of::<u64>();
288

            
289
1266
            let pad_len = chunk_len - mod_cursor - message_len_bytes;
290
            // Fill remaining space with zeros
291
            state
292
                .alignment_helper
293
1266
                .volatile_write(m_mem(&self.sha, 0), 0, pad_len, mod_cursor);
294

            
295
            // Write message length
296
1266
            let length = state.finished_message_size as u64 * 8;
297
1266
            state.alignment_helper.aligned_volatile_copy(
298
                m_mem(&self.sha, 0),
299
                &length.to_be_bytes(),
300
                chunk_len,
301
                chunk_len - message_len_bytes,
302
            );
303

            
304
            // Set up last state, start processing
305
1266
            state.finalize_state = FinalizeState::ReadResult;
306
            self.process_buffer_or_wait(state)?;
307
        }
308

            
309
        if state.finalize_state == FinalizeState::ReadResult {
310
1266
            if state.algorithm.is_busy(&self.sha) {
311
                return Err(nb::Error::WouldBlock);
312
            }
313
            if state.algorithm.load(&self.sha) {
314
                // Spin wait for result, 8-20 clock cycles according to manual
315
                while self.is_busy(state.algorithm) {}
316
            }
317

            
318
            state.alignment_helper.volatile_read_regset(
319
                h_mem(&self.sha, 0),
320
                output,
321
                core::cmp::min(output.len(), 32),
322
            );
323

            
324
1266
            state.first_run = true;
325
1266
            state.cursor = 0;
326
            state.alignment_helper.reset();
327
1266
            state.finalize_state = FinalizeState::NotStarted;
328

            
329
1266
            return Ok(());
330
        }
331

            
332
        Err(nb::Error::WouldBlock)
333
1266
    }
334

            
335
2524
    fn update<'a>(
336
        &self,
337
        state: &mut DigestState,
338
        incoming: &'a [u8],
339
    ) -> nb::Result<&'a [u8], Infallible> {
340
2524
        state.finalize_state = FinalizeState::default();
341
2524
        self.write_data(state, incoming)
342
    }
343
}
344

            
345
impl crate::private::Sealed for Sha<'_> {}
346

            
347
#[cfg(sha_supports_dma)]
348
#[instability::unstable]
349
impl crate::interrupt::InterruptConfigurable for Sha<'_> {
350
    fn set_interrupt_handler(&mut self, handler: crate::interrupt::InterruptHandler) {
351
        self.sha.disable_peri_interrupt_on_all_cores();
352
        self.sha.bind_peri_interrupt(handler);
353
    }
354
}
355

            
356
// A few notes on this implementation with regards to 'memcpy',
357
// - The registers are *not* cleared after processing, so padding needs to be written out
358
// - Registers need to be written one u32 at a time, no u8 access
359
// - This means that we need to buffer bytes coming in up to 4 u8's in order to create a full u32
360

            
361
/// An active digest.
362
///
363
/// This implementation might fail after u32::MAX/8 bytes. See `finish()`
364
/// length/`self.cursor` usage to increase the limit.
365
pub struct ShaDigest<'d, A, S: BorrowMut<Sha<'d>>> {
366
    sha: S,
367
    state: DigestState,
368
    phantom: PhantomData<(&'d (), A)>,
369
}
370

            
371
1266
#[derive(Clone, Copy, Debug, PartialEq, Default)]
372
enum FinalizeState {
373
    #[default]
374
    NotStarted,
375
    FlushAlignBuffer,
376
    ZeroPadAlmostFull,
377
    WriteMessageLength,
378
    ReadResult,
379
}
380

            
381
13
#[derive(Clone, Debug)]
382
struct DigestState {
383
    algorithm: ShaAlgorithmKind,
384
    alignment_helper: AlignmentHelper<SocDependentEndianess>,
385
    cursor: usize,
386
    first_run: bool,
387
    finished_message_size: usize,
388
    message_buffer_is_full: bool,
389
    finalize_state: FinalizeState,
390
}
391

            
392
impl DigestState {
393
    fn new(algorithm: ShaAlgorithmKind) -> Self {
394
        assert!(algorithm.is_supported(), "SM3 is disabled by eFuse");
395

            
396
        Self {
397
            algorithm,
398
            alignment_helper: AlignmentHelper::default(),
399
            cursor: 0,
400
            first_run: true,
401
            finished_message_size: 0,
402
            message_buffer_is_full: false,
403
            finalize_state: FinalizeState::default(),
404
        }
405
    }
406
}
407

            
408
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> ShaDigest<'d, A, S> {
409
    /// Creates a new digest.
410
    #[cfg_attr(
411
        sha_has_sm3,
412
        doc = "\n# Panics\nPanics if SM3 is selected and disabled by eFuse."
413
    )]
414
    #[allow(unused_mut)]
415
    pub fn new(mut sha: S) -> Self {
416
        #[cfg(not(esp32))]
417
        // Setup SHA Mode.
418
        sha.borrow_mut()
419
            .sha
420
            .register_block()
421
            .mode()
422
            .write(|w| unsafe { w.mode().bits(A::ALGORITHM_KIND.mode_bits()) });
423

            
424
200
        Self {
425
            sha,
426
            state: DigestState::new(A::ALGORITHM_KIND),
427
            phantom: PhantomData,
428
        }
429
    }
430

            
431
    /// Restores a previously saved digest.
432
    #[cfg(not(esp32))]
433
    pub fn restore(mut sha: S, ctx: &mut Context<A>) -> Self {
434
        // Setup SHA Mode.
435
        sha.borrow_mut()
436
            .sha
437
            .register_block()
438
            .mode()
439
            .write(|w| unsafe { w.mode().bits(A::ALGORITHM_KIND.mode_bits()) });
440

            
441
        // Restore the message buffer
442
        unsafe {
443
            core::ptr::copy_nonoverlapping(
444
                ctx.buffer.as_ptr(),
445
                m_mem(&sha.borrow_mut().sha, 0),
446
                32,
447
            );
448
        }
449

            
450
        // Restore previously saved hash
451
        ctx.state.alignment_helper.volatile_write_regset(
452
            h_mem(&sha.borrow_mut().sha, 0),
453
            &ctx.saved_digest,
454
            64,
455
        );
456

            
457
13
        Self {
458
            sha,
459
            state: ctx.state.clone(),
460
            phantom: PhantomData,
461
        }
462
    }
463

            
464
    /// Returns whether the hardware is processing the next message.
465
    pub fn is_busy(&self) -> bool {
466
13
        A::ALGORITHM_KIND.is_busy(&self.sha.borrow().sha)
467
    }
468

            
469
    /// Updates the SHA digest with the provided data buffer.
470
    pub fn update<'a>(&mut self, incoming: &'a [u8]) -> nb::Result<&'a [u8], Infallible> {
471
407
        self.sha.borrow_mut().update(&mut self.state, incoming)
472
    }
473

            
474
    /// Finishes the calculation (if not already finished) and copies the result to output.
475
    ///
476
    /// After `finish()` is called, `update()`s contribute to a new hash which can be
477
    /// calculated again with `finish()`
478
    ///
479
    /// Typically, output is expected to be the size of
480
    /// [`ShaAlgorithm::DIGEST_LENGTH`], but smaller inputs can be given to
481
    /// get a "short hash"
482
    pub fn finish(&mut self, output: &mut [u8]) -> nb::Result<(), Infallible> {
483
200
        self.sha.borrow_mut().finish(&mut self.state, output)
484
    }
485

            
486
    /// Saves the current state of the digest for later continuation.
487
    #[cfg(not(esp32))]
488
    pub fn save(&mut self, context: &mut Context<A>) -> nb::Result<(), Infallible> {
489
13
        if self.is_busy() {
490
            return Err(nb::Error::WouldBlock);
491
        }
492

            
493
13
        context.state = self.state.clone();
494

            
495
        // Save the content of the current hash.
496
        self.state.alignment_helper.volatile_read_regset(
497
            h_mem(&self.sha.borrow_mut().sha, 0),
498
            &mut context.saved_digest,
499
            64,
500
        );
501

            
502
        // Save the content of the current (probably partially written) message.
503
        unsafe {
504
            core::ptr::copy_nonoverlapping(
505
                m_mem(&self.sha.borrow_mut().sha, 0),
506
                context.buffer.as_mut_ptr(),
507
                32,
508
            );
509
        }
510

            
511
        Ok(())
512
    }
513

            
514
    /// Discards the current digest and returns the peripheral.
515
    pub fn cancel(self) -> S {
516
        self.sha
517
    }
518
}
519

            
520
#[cfg(not(esp32))]
521
/// Context for a SHA Accelerator driver instance
522
#[derive(Debug, Clone)]
523
pub struct Context<A: ShaAlgorithm> {
524
    state: DigestState,
525
    /// Buffered bytes (SHA_M_n_REG) to be processed.
526
    buffer: [u32; 32],
527
    /// Saved digest (SHA_H_n_REG) for interleaving operation
528
    saved_digest: [u8; 64],
529
    phantom: PhantomData<A>,
530
}
531

            
532
#[cfg(not(esp32))]
533
impl<A: ShaAlgorithm> Context<A> {
534
    /// Creates a new empty context.
535
    #[cfg_attr(
536
        sha_has_sm3,
537
        doc = "\n# Panics\nPanics if SM3 is selected and disabled by eFuse."
538
    )]
539
    pub fn new() -> Self {
540
5
        Self {
541
            state: DigestState::new(A::ALGORITHM_KIND),
542
            buffer: [0; 32],
543
5
            saved_digest: [0; 64],
544
            phantom: PhantomData,
545
        }
546
    }
547

            
548
    /// Indicates if the SHA context is in the first run.
549
    ///
550
    /// Returns whether this is the first time processing data with the SHA
551
    /// instance.
552
    pub fn first_run(&self) -> bool {
553
        self.state.first_run
554
    }
555
}
556

            
557
#[cfg(not(esp32))]
558
impl<A: ShaAlgorithm> Default for Context<A> {
559
    fn default() -> Self {
560
        Self::new()
561
    }
562
}
563

            
564
/// Encapsulates the configuration for a specific SHA algorithm.
565
pub trait ShaAlgorithm: crate::private::Sealed {
566
    /// Constant containing the name of the algorithm as a string.
567
    const ALGORITHM: &'static str;
568

            
569
    /// Constant containing the kind of the algorithm.
570
    const ALGORITHM_KIND: ShaAlgorithmKind;
571

            
572
    /// The length of the chunk that the algorithm processes at a time.
573
    ///
574
    /// For example, in SHA-256, this would typically be 64 bytes.
575
    const CHUNK_LENGTH: usize;
576

            
577
    /// The length of the resulting digest produced by the algorithm.
578
    ///
579
    /// For example, in SHA-256, this would be 32 bytes.
580
    const DIGEST_LENGTH: usize;
581

            
582
    #[doc(hidden)]
583
    type Digest010OutputSize: digest_010::generic_array::ArrayLength<u8> + 'static;
584
    #[doc(hidden)]
585
    type Digest011OutputSize: digest_011::array::ArraySize;
586
}
587

            
588
/// `digest` has a blanket trait implementation for `Digest` for any
589
/// element that implements FixedOutput + Default + Update + HashMarker.
590
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_010::HashMarker for ShaDigest<'d, A, S> {}
591
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_011::HashMarker for ShaDigest<'d, A, S> {}
592

            
593
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_010::OutputSizeUser
594
    for ShaDigest<'d, A, S>
595
{
596
    type OutputSize = A::Digest010OutputSize;
597
}
598
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_011::OutputSizeUser
599
    for ShaDigest<'d, A, S>
600
{
601
    type OutputSize = A::Digest011OutputSize;
602
}
603

            
604
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_010::Update for ShaDigest<'d, A, S> {
605
    fn update(&mut self, mut remaining: &[u8]) {
606
        while !remaining.is_empty() {
607
            remaining = nb::block!(Self::update(self, remaining)).unwrap();
608
        }
609
    }
610
}
611

            
612
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_011::Update for ShaDigest<'d, A, S> {
613
    fn update(&mut self, mut remaining: &[u8]) {
614
407
        while !remaining.is_empty() {
615
            remaining = nb::block!(Self::update(self, remaining)).unwrap();
616
        }
617
    }
618
}
619

            
620
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_010::FixedOutput for ShaDigest<'d, A, S> {
621
    fn finalize_into(mut self, out: &mut digest_010::Output<Self>) {
622
        nb::block!(self.finish(out)).unwrap();
623
    }
624
}
625

            
626
impl<'d, A: ShaAlgorithm, S: BorrowMut<Sha<'d>>> digest_011::FixedOutput for ShaDigest<'d, A, S> {
627
    fn finalize_into(mut self, out: &mut digest_011::Output<Self>) {
628
200
        nb::block!(self.finish(out)).unwrap();
629
    }
630
}
631

            
632
for_each_sha_algorithm! {
633
    (algos $( ( $name:ident, $full_name:literal $sizes:tt $security:tt, $mode_bits:literal ) ),*) => {
634

            
635
        /// Specifies particular SHA algorithm.
636
        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
637
        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
638
        #[non_exhaustive]
639
        pub enum ShaAlgorithmKind {
640
            $(
641
                #[doc = concat!("The ", $full_name, " algorithm.")]
642
                $name,
643
            )*
644
        }
645
    };
646
}
647

            
648
35
for_each_sha_algorithm! {
649
    (algos $( ( $name:ident, $full_name:literal (sizes: $block_size:literal, $digest_len:literal, $message_length_bytes:literal) $security:tt, $mode_bits:literal ) ),*) => {
650
        impl ShaAlgorithmKind {
651
            #[cfg(not(esp32))]
652
            const fn mode_bits(self) -> u8 {
653
                match self {
654
                    $(ShaAlgorithmKind::$name => $mode_bits,)*
655
                }
656
            }
657

            
658
            const fn chunk_length(self) -> usize {
659
                match self {
660
                    $(ShaAlgorithmKind::$name => $block_size,)*
661
                }
662
            }
663

            
664
            /// Bytes needed to represent the length of the longest possible message.
665
            const fn message_length_bytes(self) -> usize {
666
                match self {
667
                    $(ShaAlgorithmKind::$name => $message_length_bytes,)*
668
                }
669
            }
670

            
671
            const fn digest_length(self) -> usize {
672
                match self {
673
                    $(ShaAlgorithmKind::$name => $digest_len,)*
674
                }
675
            }
676
        }
677
    };
678
}
679

            
680
impl ShaAlgorithmKind {
681
    /// Returns whether the algorithm is enabled.
682
    #[cfg_attr(
683
        sha_has_sm3,
684
        doc = "\nSM3 is disabled when the `DIS_SM_CRYPT` eFuse is set."
685
    )]
686
    #[instability::unstable]
687
    pub fn is_supported(self) -> bool {
688
        match self {
689
            #[cfg(sha_has_sm3)]
690
            Self::Sm3 => !crate::efuse::read_bit(crate::efuse::DIS_SM_CRYPT),
691
            _ => true,
692
        }
693
    }
694

            
695
    fn start(self, sha: &crate::peripherals::SHA<'_>) {
696
        let regs = sha.register_block();
697
        cfg_select! {
698
            esp32 => {
699
                match self {
700
                    ShaAlgorithmKind::Sha1 => regs.sha1_start().write(|w| w.sha1_start().set_bit()),
701
                    ShaAlgorithmKind::Sha256 => {
702
                        regs.sha256_start().write(|w| w.sha256_start().set_bit())
703
                    }
704
                    ShaAlgorithmKind::Sha384 => {
705
                        regs.sha384_start().write(|w| w.sha384_start().set_bit())
706
                    }
707
                    ShaAlgorithmKind::Sha512 => {
708
                        regs.sha512_start().write(|w| w.sha512_start().set_bit())
709
                    }
710
                };
711
            }
712
            _ => {
713
                regs.start().write(|w| w.start().set_bit());
714
            }
715
        }
716
    }
717

            
718
    fn r#continue(self, sha: &crate::peripherals::SHA<'_>) {
719
        let regs = sha.register_block();
720
        cfg_select! {
721
            esp32 => {
722
                match self {
723
                    ShaAlgorithmKind::Sha1 => {
724
                        regs.sha1_continue().write(|w| w.sha1_continue().set_bit())
725
                    }
726
                    ShaAlgorithmKind::Sha256 => regs
727
                        .sha256_continue()
728
                        .write(|w| w.sha256_continue().set_bit()),
729
                    ShaAlgorithmKind::Sha384 => regs
730
                        .sha384_continue()
731
                        .write(|w| w.sha384_continue().set_bit()),
732
                    ShaAlgorithmKind::Sha512 => regs
733
                        .sha512_continue()
734
                        .write(|w| w.sha512_continue().set_bit()),
735
                };
736
            }
737
            _ => {
738
                regs.continue_().write(|w| w.continue_().set_bit());
739
            }
740
        }
741
    }
742

            
743
    /// Starts loading the hash into the output registers.
744
    ///
745
    /// Returns whether the caller needs to wait for the hash to be loaded.
746
    fn load(self, _sha: &crate::peripherals::SHA<'_>) -> bool {
747
        cfg_select! {
748
            esp32 => {
749
                let regs = _sha.register_block();
750
                match self {
751
                    ShaAlgorithmKind::Sha1 => regs.sha1_load().write(|w| w.sha1_load().set_bit()),
752
                    ShaAlgorithmKind::Sha256 => {
753
                        regs.sha256_load().write(|w| w.sha256_load().set_bit())
754
                    }
755
                    ShaAlgorithmKind::Sha384 => {
756
                        regs.sha384_load().write(|w| w.sha384_load().set_bit())
757
                    }
758
                    ShaAlgorithmKind::Sha512 => {
759
                        regs.sha512_load().write(|w| w.sha512_load().set_bit())
760
                    }
761
                };
762

            
763
                true
764
            }
765
            _ => {
766
                // Return that no waiting is necessary
767
                false
768
            }
769
        }
770
    }
771

            
772
39
    fn is_busy(self, sha: &crate::peripherals::SHA<'_>) -> bool {
773
        let regs = sha.register_block();
774
        cfg_select! {
775
            esp32 => {
776
                let bit = match self {
777
                    ShaAlgorithmKind::Sha1 => regs.sha1_busy().read().sha1_busy(),
778
                    ShaAlgorithmKind::Sha256 => regs.sha256_busy().read().sha256_busy(),
779
                    ShaAlgorithmKind::Sha384 => regs.sha384_busy().read().sha384_busy(),
780
                    ShaAlgorithmKind::Sha512 => regs.sha512_busy().read().sha512_busy(),
781
                };
782
            }
783
            _ => {
784
                let bit = regs.busy().read().state();
785
            }
786
        }
787

            
788
        bit.bit_is_set()
789
39
    }
790
}
791

            
792
// This macro call creates Sha algorithm structs that implement the ShaAlgorithm trait.
793
for_each_sha_algorithm! {
794
    ( $name:ident, $full_name:literal (sizes: $block_size:literal, $digest_len:literal, $message_length_bytes:literal) (insecure_against: $($attack_kind:literal),*), $mode_bits:literal ) => {
795
        #[doc = concat!("Hardware-accelerated ", $full_name, " implementation")]
796
        ///
797
        /// Manages the context and state required for processing data using the selected hashing algorithm.
798

            
799
        ///
800
        /// The struct provides various functionalities such as initializing the hashing
801
        /// process, updating the internal state with new data, and finalizing the
802
        /// hashing operation to generate the final digest.
803
        $(
804
            #[doc = ""]
805
            #[doc = concat!(" > ⚠️ This algorithm is known to be insecure against ", $attack_kind, " attacks.")]
806
        )*
807
        #[non_exhaustive]
808
        pub struct $name;
809

            
810
        impl crate::private::Sealed for $name {}
811

            
812
        impl ShaAlgorithm for $name {
813
            const ALGORITHM: &'static str = stringify!($name);
814
            const ALGORITHM_KIND: ShaAlgorithmKind = ShaAlgorithmKind::$name;
815

            
816
            const CHUNK_LENGTH: usize = Self::ALGORITHM_KIND.chunk_length();
817
            const DIGEST_LENGTH: usize = Self::ALGORITHM_KIND.digest_length();
818

            
819
            type Digest010OutputSize = paste::paste!(digest_010::consts::[< U $digest_len >]);
820
            type Digest011OutputSize = paste::paste!(digest_011::consts::[< U $digest_len >]);
821
        }
822
    };
823
}
824

            
825
fn h_mem(sha: &crate::peripherals::SHA<'_>, index: usize) -> *mut u32 {
826
    let sha = sha.register_block();
827
    cfg_select! {
828
        esp32 => sha.text(index).as_ptr(),
829
        _ => sha.h_mem(index).as_ptr(),
830
    }
831
}
832

            
833
fn m_mem(sha: &crate::peripherals::SHA<'_>, index: usize) -> *mut u32 {
834
    let sha = sha.register_block();
835
    cfg_select! {
836
        esp32 => sha.text(index).as_ptr(),
837
        _ => sha.m_mem(index).as_ptr(),
838
    }
839
}
840

            
841
1
#[derive(Clone)]
842
struct ShaOperation {
843
    operation: ShaOperationKind,
844
    // Buffer containing pieced-together message bytes, not necessarily a complete block. Not a fat
845
    // pointer because the driver will update the number of buffered bytes.
846
    buffer: NonNull<u8>,
847
    buffered_bytes: u8,
848
    message: NonNull<[u8]>,
849
    // Intermediate hash value. For SHA-512/t, this may also contain the initial hash (unverified
850
    // claim)
851
    #[cfg(not(esp32))]
852
    hw_state: NonNull<[u32]>,
853
    // TODO: we don't really need DigestState, especially not the alignment helper, but the driver
854
    // is formulated like this and so reusing what we have is simpler.
855
    state: DigestState,
856
}
857

            
858
impl ShaOperation {
859
    fn reset(&mut self) {
860
35
        self.state = DigestState::new(self.state.algorithm);
861
35
        self.buffered_bytes = 0;
862
    }
863
}
864

            
865
// Safety: ShaOperation is safe to share between threads, in the context of a WorkQueue. The
866
// WorkQueue ensures that only a single location can access the data. All the internals, except
867
// for the pointers, are Sync. The pointers are safe to share because they point at data that the
868
// SHA driver ensures can be accessed safely and soundly.
869
unsafe impl Sync for ShaOperation {}
870
// Safety: we will not hold on to the pointers when the work item leaves the queue.
871
unsafe impl Send for ShaOperation {}
872

            
873
static SHA_WORK_QUEUE: WorkQueue<ShaOperation> = WorkQueue::new();
874
const BLOCKING_SHA_VTABLE: VTable<ShaOperation> = VTable {
875
    post: |driver, item| {
876
        let driver = unsafe { ShaBackend::from_raw(driver) };
877

            
878
        // Ensure driver is initialized
879
61
        if let DriverState::Uninitialized(sha) = &driver.driver {
880
30
            driver.driver = DriverState::Initialized(Sha::new(unsafe { sha.clone_unchecked() }));
881
        };
882

            
883
61
        Some(driver.process(item))
884
    },
885
    poll: |driver, item| {
886
        let driver = unsafe { ShaBackend::from_raw(driver) };
887
74
        driver.process(item)
888
    },
889
    cancel: |_driver, _item| {
890
        // To achieve a decent performance in Typical SHA mode, we run the operations in a blocking
891
        // manner and so they can't be cancelled.
892
    },
893
    stop: |driver| {
894
        // Drop the SHA driver to conserve power when there is nothing to do (or when the driver was
895
        // stopped).
896
        let driver = unsafe { ShaBackend::from_raw(driver) };
897
        driver.deinitialize()
898
    },
899
};
900

            
901
/// Contains information about processing the current work item.
902
struct ProcessingState {
903
    message_partially_processed: bool,
904
    message_bytes_processed: usize,
905
}
906

            
907
enum DriverState<'d> {
908
    Uninitialized(SHA<'d>),
909
    Initialized(Sha<'d>),
910
}
911

            
912
135
#[derive(Clone, Copy, PartialEq)]
913
enum ShaOperationKind {
914
    Update,
915
    Finalize,
916
}
917

            
918
#[procmacros::doc_replace]
919
/// CPU-driven SHA processing backend.
920
///
921
/// # Examples
922
///
923
/// ```rust, no_run
924
/// # {before_snippet}
925
/// use esp_hal::sha::{Sha1Context, ShaBackend};
926
/// #
927
/// let mut sha = ShaBackend::new(peripherals.SHA);
928
/// // Start the backend, which allows processing SHA operations.
929
/// let _backend = sha.start();
930
///
931
/// // Create a new context to hash data with SHA-1.
932
/// let mut sha1_ctx = Sha1Context::new();
933
///
934
/// // SHA-1 outputs a 20-byte digest.
935
/// let mut digest: [u8; 20] = [0; 20];
936
///
937
/// // Process data. The `update` function returns a handle which can be used to wait
938
/// // for the operation to finish.
939
/// sha1_ctx.update(b"input data").wait_blocking();
940
///
941
/// // Extract the final hash. This resets the context.
942
/// sha1_ctx.finalize(&mut digest).wait_blocking();
943
///
944
/// // digest now contains the SHA-1 hash of the input.
945
/// #
946
/// # {after_snippet}
947
/// ```
948
pub struct ShaBackend<'d> {
949
    driver: DriverState<'d>,
950
    processing_state: ProcessingState,
951
}
952
impl<'d> ShaBackend<'d> {
953
    /// Creates a new SHA backend.
954
    ///
955
    /// The backend must be started with [`Self::start`] before it can execute SHA operations.
956
    pub fn new(sha: SHA<'d>) -> Self {
957
        Self {
958
            driver: DriverState::Uninitialized(sha),
959
            processing_state: ProcessingState {
960
                message_partially_processed: false,
961
                message_bytes_processed: 0,
962
            },
963
        }
964
    }
965

            
966
    /// Registers the CPU-driven SHA driver to process SHA operations.
967
    ///
968
    /// The driver stops operating when the returned object is dropped.
969
4
    pub fn start(&mut self) -> ShaWorkQueueDriver<'_, 'd> {
970
        ShaWorkQueueDriver {
971
            inner: WorkQueueDriver::new(self, BLOCKING_SHA_VTABLE, &SHA_WORK_QUEUE),
972
        }
973
4
    }
974

            
975
    // WorkQueue callbacks. They may run in any context.
976

            
977
    unsafe fn from_raw<'any>(ptr: NonNull<()>) -> &'any mut Self {
978
        unsafe { ptr.cast::<ShaBackend<'_>>().as_mut() }
979
    }
980

            
981
135
    fn process(&mut self, item: &mut ShaOperation) -> Poll {
982
        // The hardware allows us to write the next message block while it's still processing the
983
        // current one. We can only do this if the current message block belongs to the same message
984
        // as the previous one. This is unfortunately not a safe operation in the work queue,
985
        // because we can't inspect the next work item during the processing of the current
986
        // one.
987
        // However, we can receive a message with multiple blocks and we can apply the optimization
988
        // while processing the message bytes here.
989
        // Another optimization we can do is skipping restoring the intermediate hash value if the
990
        // next operation is a continuation of the current one. We still need to save it between
991
        // operations, though.
992

            
993
        // In this function we process a number of message blocks.
994
        // There are two possible cases we need to handle:
995
        // - We receive message bytes that, including the buffered bytes, are enough for at least
996
        //   one complete SHA block. We need to copy whole blocks into the hardware, do the
997
        //   computation, save the state, and save the remaining data in the buffer.
998
        // - We receive message bytes that, including the buffered bytes, are not enough for a
999
        //   complete SHA block. This case marks the end of the message. We will need to apply
        //   padding, then finalize the computation.
135
        if item.operation == ShaOperationKind::Update {
            self.process_update(item)
        } else {
            self.process_finalize(item)
        }
135
    }
    #[cfg(not(esp32))]
    fn restore_state(driver: &mut Sha<'_>, item: &ShaOperation) {
        driver
            .sha
            .register_block()
            .mode()
            .write(|w| unsafe { w.mode().bits(item.state.algorithm.mode_bits()) });
        // Restore previously saved hash. Don't bother on first_run, the start operation will
        // use a hard-coded initial hash.
35
        if !item.state.first_run {
            for (i, reg) in driver.sha.register_block().h_mem_iter().enumerate() {
216
                reg.write(|w| unsafe { w.bits(item.hw_state.as_ref()[i]) });
            }
        }
    }
    fn process_update(&mut self, item: &mut ShaOperation) -> Poll {
100
        let driver = if let DriverState::Initialized(sha) = &mut self.driver {
            sha
        } else {
            unreachable!()
        };
        // The message can be longer than a SHA block, so we track the processed bytes.
100
        if !self.processing_state.message_partially_processed {
26
            self.processing_state = ProcessingState {
                message_partially_processed: true,
                message_bytes_processed: 0,
            };
            #[cfg(not(esp32))]
            Self::restore_state(driver, item);
            let buffered = unsafe {
26
                core::slice::from_raw_parts(item.buffer.as_ptr(), item.buffered_bytes as usize)
            };
            debug!(
                "update: restored state with {} buffered bytes",
                buffered.len()
            );
            // This is never supposed to block or even start processing, we're writing an incomplete
            // block into idle hardware.
26
            debug_assert!(buffered.len() < item.state.algorithm.chunk_length());
26
            nb::block!(driver.write_data(&mut item.state, buffered)).unwrap();
        }
        let remaining_message =
74
            unsafe { &item.message.as_ref()[self.processing_state.message_bytes_processed..] };
        // Even if we don't have a full chunk's worth of message, this function was called because
        // with the buffered data we have enough. If `message_processed == 0`, we have written a
        // partial block loaded in the hardware and will need to fill it out with message bytes.
74
        if remaining_message.len() >= item.state.algorithm.chunk_length()
            || self.processing_state.message_bytes_processed == 0
        {
74
            if let Ok(remaining) = driver.write_data(&mut item.state, remaining_message) {
74
                let total_consumed = item.message.len() - remaining.len();
74
                self.processing_state.message_bytes_processed = total_consumed;
            }
            // Request recall, SHA doesn't have an interrupt.
            return Poll::Pending(true);
        } else {
            // It's not necessary to wait here in the first call - no operation can be running when
            // we only restored a partial buffer.
            // Unfortunately we don't see the future - we can't tell if the next operation can be
            // overlapped with this one, so we need to wait for processing to complete so that we
            // can save the context - ESP32 is an exception, as only one context can use the
            // hardware at a time.
            #[cfg(not(esp32))]
26
            if driver.is_busy(item.state.algorithm) {
                // Request recall, SHA doesn't have an interrupt.
                return Poll::Pending(true);
            }
        }
        // Save hash. If `!first_run`, we did not start an operation so there is no hash to save.
        #[cfg(not(esp32))]
26
        if !item.state.first_run {
            for (i, reg) in driver.sha.register_block().h_mem_iter().enumerate() {
208
                unsafe { item.hw_state.as_mut()[i] = reg.read().bits() };
            }
        }
        // We can only process complete blocks before finalization. Write back the unprocessed bytes
        // to the item's buffer.
26
        if !remaining_message.is_empty() {
            debug!(
                "Writing back {} unprocessed bytes to buffer",
                remaining_message.len()
            );
            unsafe {
                // Safety: the frontend ensures that the buffer is large enough to hold the
                // remaining message.
                core::ptr::copy_nonoverlapping(
                    remaining_message.as_ptr(),
6
                    item.buffer.as_ptr(),
                    remaining_message.len(),
                );
            }
        }
26
        item.buffered_bytes = remaining_message.len() as u8;
26
        self.processing_state.message_partially_processed = false;
        Poll::Ready(Status::Completed)
    }
    fn process_finalize(&mut self, item: &mut ShaOperation) -> Poll {
35
        let driver = if let DriverState::Initialized(sha) = &mut self.driver {
            sha
        } else {
            unreachable!()
        };
35
        if !self.processing_state.message_partially_processed {
            // We don't need to track the byte count here, just that we've restored the hash and
            // written the buffered data. `process_finalize` ignores `message_bytes_processed`.
35
            self.processing_state.message_partially_processed = true;
            #[cfg(not(esp32))]
            Self::restore_state(driver, item);
            let buffered = unsafe { item.message.as_ref() };
            debug!(
                "finalize: restored state with {} buffered bytes",
                buffered.len()
            );
            // This is never supposed to block or even start processing, we're writing an incomplete
            // block into idle hardware.
35
            debug_assert!(buffered.len() < item.state.algorithm.chunk_length());
35
            nb::block!(driver.write_data(&mut item.state, buffered)).unwrap();
        }
        // Safety: caller must ensure that result buffer is large enough.
        let result = unsafe {
            core::slice::from_raw_parts_mut(
35
                item.buffer.as_ptr(),
35
                item.state.algorithm.digest_length(),
            )
        };
35
        if driver.finish(&mut item.state, result).is_err() {
            return Poll::Pending(true);
        }
35
        self.processing_state.message_partially_processed = false;
        item.reset();
        Poll::Ready(Status::Completed)
35
    }
    fn deinitialize(&mut self) {
30
        if let DriverState::Initialized(ref sha) = self.driver {
30
            self.driver = DriverState::Uninitialized(unsafe { sha.sha.clone_unchecked() });
        }
    }
}
/// An active work queue driver.
///
/// This object must be kept around, otherwise SHA operations will never complete.
pub struct ShaWorkQueueDriver<'t, 'd> {
    inner: WorkQueueDriver<'t, ShaBackend<'d>, ShaOperation>,
}
impl<'t, 'd> ShaWorkQueueDriver<'t, 'd> {
    /// Finishes processing the current work queue item, then stops the driver.
    pub fn stop(self) -> impl Future<Output = ()> {
        self.inner.stop()
    }
}
// ESP32 can't save (or rather, restore) the hash context, so we need to fall back to software
// if a context already uses the hardware. Also we must not power down the hardware when a
// context uses it, as it corrupts state.
#[cfg(esp32)]
#[expect(clippy::large_enum_variant)]
enum Esp32Hasher {
    Hardware(GenericPeripheralGuard<{ crate::system::Peripheral::Sha as u8 }>),
    Software(SoftwareHasher),
}
#[cfg(esp32)]
enum SoftwareHasher {
    Sha1(sha1::Sha1),
    Sha256(sha2::Sha256),
    Sha384(sha2::Sha384),
    Sha512(sha2::Sha512),
}
// Common implementation, to be hidden behind algo-dependent contexts.
1
#[cfg_attr(not(esp32), derive(Clone))]
struct ShaContext<const CHUNK_BYTES: usize, const DIGEST_WORDS: usize> {
    frontend: WorkQueueFrontend<ShaOperation>,
    buffer: [u8; CHUNK_BYTES],
    #[cfg(not(esp32))] // Saved H_MEM registers
    state: [u32; 16],
    #[cfg(esp32)]
    hasher: Esp32Hasher,
}
// ESP32 can't save the context, so it can't support interleaved operation. To support the digest
// API, we need to limit the number of concurrent contexts - to 1. Thankfully ESP32 has no DSA/HMAC,
// so we don't need to worry about figuring THAT edge case out.
// Currently we fall back to a software implementation if multiple contexts are created. Instead, we
// could save the hash like we do on other MCUs, and continue with software if another context has
// been created in the mean time. This would prevent a case where a context could indefinitely
// reserve the hardware.
#[cfg(esp32)]
use portable_atomic::{AtomicBool, Ordering};
#[cfg(esp32)]
static ACCELERATOR_IN_USE: AtomicBool = AtomicBool::new(false);
impl<const CHUNK_BYTES: usize, const DIGEST_WORDS: usize> ShaContext<CHUNK_BYTES, DIGEST_WORDS> {
    fn new(algorithm: ShaAlgorithmKind) -> Self {
        #[cfg(esp32)]
        let hasher = if ACCELERATOR_IN_USE.swap(true, Ordering::SeqCst) {
            let hasher = match algorithm {
                ShaAlgorithmKind::Sha1 => SoftwareHasher::Sha1(sha1::Sha1::new()),
                ShaAlgorithmKind::Sha256 => SoftwareHasher::Sha256(sha2::Sha256::new()),
                ShaAlgorithmKind::Sha384 => SoftwareHasher::Sha384(sha2::Sha384::new()),
                ShaAlgorithmKind::Sha512 => SoftwareHasher::Sha512(sha2::Sha512::new()),
            };
            Esp32Hasher::Software(hasher)
        } else {
            Esp32Hasher::Hardware(GenericPeripheralGuard::new())
        };
        Self {
            frontend: WorkQueueFrontend::new(ShaOperation {
                operation: ShaOperationKind::Update,
                buffer: NonNull::dangling(),
                message: NonNull::from(&mut []),
                #[cfg(not(esp32))]
                hw_state: NonNull::from(&mut []),
                buffered_bytes: 0,
                state: DigestState::new(algorithm),
            }),
1
            buffer: [0; CHUNK_BYTES],
            #[cfg(not(esp32))]
1
            state: [0; 16],
            #[cfg(esp32)]
            hasher,
        }
    }
    fn update<'t>(&'t mut self, data: &'t [u8]) -> ShaHandle<'t> {
        debug!(
            "Update {:?} with {} bytes",
            self.frontend.data_mut().state.algorithm,
            data.len()
        );
        #[cfg(esp32)]
        if let Esp32Hasher::Software(ref mut hasher) = self.hasher {
            Self::update_using_software(hasher, data);
            return ShaHandle(self.frontend.post_completed(&SHA_WORK_QUEUE));
        }
        let op_data = self.frontend.data_mut();
        // We want to pass complete blocks to the worker. If we pass an incomplete block, it must be
        // the last one. Because update can take any number of bytes, we need to buffer a block's
        // worth, truncate data to the nearest block size, then save the rest.
        // Buffering data here, and handling head/data/tail processing is way too annoying.
        // Therefore, we implement the following strategy:
        // - If we can buffer the data, we do.
        // - If we can't, we post a work item to the queue. The work item includes the buffer, the
        //   data, and the state pointers.
        // - The worker will load and process data as it sees fit. The worker will put the remaining
        //   data into the buffer, and update the state and buffer length.
        // - The worker is responsible for updating the buffer and state. It will set `first` to
        //   false if it saves an intermediate result into `state`.
        // This means chunking and remainder handling is the responsibility of the worker.
32
        let buffered = op_data.buffered_bytes as usize;
32
        if data.len() + buffered < CHUNK_BYTES {
6
            op_data.buffered_bytes += data.len() as u8;
6
            op_data.message = NonNull::from(data); // Ensure message.len() returns the consumed length
            self.buffer[buffered..][..data.len()].copy_from_slice(data);
            return ShaHandle(self.frontend.post_completed(&SHA_WORK_QUEUE));
        }
        // If a block is complete, we send it to the hardware. If the data we send is a whole number
        // of complete blocks, the driver will leave `buffer` empty. This ensures that we
        // never start a finalize operation with a complete block.
26
        op_data.operation = ShaOperationKind::Update;
26
        op_data.message = NonNull::from(data);
26
        op_data.buffer = NonNull::from(&mut self.buffer).cast();
        #[cfg(not(esp32))]
        {
26
            op_data.hw_state = NonNull::from(&mut self.state);
        }
        ShaHandle(self.frontend.post(&SHA_WORK_QUEUE))
32
    }
    fn finalize<'t>(&'t mut self, result: &mut [u8]) -> ShaHandle<'t> {
        debug!(
            "Finalize {:?} into buffer of {} bytes",
            self.frontend.data_mut().state.algorithm,
            result.len()
        );
        #[cfg(esp32)]
        if let Esp32Hasher::Software(ref mut hasher) = self.hasher {
            Self::finalize_using_software(hasher, result);
            return ShaHandle(self.frontend.post_completed(&SHA_WORK_QUEUE));
        }
        let op_data = self.frontend.data_mut();
10
        debug_assert!((op_data.buffered_bytes as usize) < op_data.state.algorithm.chunk_length());
10
        op_data.operation = ShaOperationKind::Finalize;
10
        op_data.message = NonNull::from(&mut self.buffer[..op_data.buffered_bytes as usize]);
10
        op_data.buffer = NonNull::from(result).cast();
        #[cfg(not(esp32))]
        {
10
            op_data.hw_state = NonNull::from(&mut self.state);
        }
        ShaHandle(self.frontend.post(&SHA_WORK_QUEUE))
5
    }
    #[cfg(esp32)]
    fn update_using_software(hasher: &mut SoftwareHasher, data: &[u8]) {
        match hasher {
            SoftwareHasher::Sha1(sha) => sha.update(data),
            SoftwareHasher::Sha256(sha) => sha.update(data),
            SoftwareHasher::Sha384(sha) => sha.update(data),
            SoftwareHasher::Sha512(sha) => sha.update(data),
        }
    }
    #[cfg(esp32)]
    fn finalize_using_software(hasher: &mut SoftwareHasher, result: &mut [u8]) {
        match hasher {
            SoftwareHasher::Sha1(sha) => {
                let output = sha.finalize_reset();
                result.copy_from_slice(output.as_slice())
            }
            SoftwareHasher::Sha256(sha) => {
                let output = sha.finalize_reset();
                result.copy_from_slice(output.as_slice())
            }
            SoftwareHasher::Sha384(sha) => {
                let output = sha.finalize_reset();
                result.copy_from_slice(output.as_slice())
            }
            SoftwareHasher::Sha512(sha) => {
                let output = sha.finalize_reset();
                result.copy_from_slice(output.as_slice())
            }
        }
    }
}
#[cfg(esp32)]
impl<const CHUNK_BYTES: usize, const DIGEST_WORDS: usize> Drop
    for ShaContext<CHUNK_BYTES, DIGEST_WORDS>
{
    fn drop(&mut self) {
        if matches!(self.hasher, Esp32Hasher::Hardware(_)) {
            ACCELERATOR_IN_USE.store(false, Ordering::Release);
        }
    }
}
/// A handle for an in-progress operation, returned by [`update`](Sha1Context::update) or
/// [`finalize`](Sha1Context::finalize).
pub struct ShaHandle<'t>(Handle<'t, ShaOperation>);
impl ShaHandle<'_> {
    /// Polls the status of the work item.
    ///
    /// Returns whether the item has been processed.
    #[inline]
    pub fn poll(&mut self) -> bool {
        self.0.poll()
    }
    /// Polls the work item to completion, by busy-looping.
    ///
    /// Returns immediately if `poll` returns `true`.
    #[inline]
    pub fn wait_blocking(self) -> Status {
        self.0.wait_blocking()
    }
    /// Waits until the work item is completed.
    #[inline]
    pub fn wait(&mut self) -> impl Future<Output = Status> {
        self.0.wait()
    }
    /// Cancels the work item and asynchronously waits until it is removed from the work queue.
    #[inline]
    pub fn cancel(&mut self) -> impl Future<Output = ()> {
        self.0.cancel()
    }
}
/// Error type returned by [`finalize_into_slice`](Sha1Context::finalize_into_slice).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum FinalizeError {
    /// The provided buffer is smaller than the digest output of the algorithm.
    BufferTooSmall,
}
// Now implement the actual public types.
// Helper macro to limit the scope of `paste`
macro_rules! impl_worker_context {
    (@panic_docs Sm3Context) => {
        "\n# Panics\nPanics if SM3 is disabled by eFuse."
    };
    (@panic_docs $name:ident) => {
        ""
    };
    ($name:ident, $full_name:literal, $algo:expr, $digest_len:literal, $block_size:literal ) => {
        #[doc = concat!("A ", $full_name, " context.")]
        #[cfg_attr(not(esp32), derive(Clone))]
        pub struct $name(ShaContext<{ $algo.chunk_length() }, { $algo.digest_length() / 4 }>);
        impl $name {
            /// Creates a new context.
            ///
            /// The context represents the in-progress processing of a single message.
            /// Feed message bytes to [`Self::update`], then finalize the process using
            /// [`Self::finalize`].
            ///
            /// Any number of contexts can be created, to hash any number of messages concurrently.
            #[doc = impl_worker_context!(@panic_docs $name)]
            pub fn new() -> Self {
                Self(ShaContext::new($algo))
            }
            /// Hashes `data`.
32
            pub fn update<'t>(&'t mut self, data: &'t [u8]) -> ShaHandle<'t> {
                self.0.update(data)
32
            }
            /// Finishes the hashing process, writes the final hash to `result`.
            ///
            /// Resets the context to an empty state.
5
            pub fn finalize<'t>(
                &'t mut self,
                result: &mut [u8; { $algo.digest_length() }],
            ) -> ShaHandle<'t> {
                self.0.finalize(result)
5
            }
            /// Finishes the hashing process, writes the final hash to `result`.
            ///
            /// Resets the context to an empty state.
            ///
            /// Returns [`FinalizeError::BufferTooSmall`] if `result` is not large enough to hold
            /// the final hash.
10
            pub fn finalize_into_slice<'t>(
                &'t mut self,
                result: &mut [u8],
            ) -> Result<ShaHandle<'t>, FinalizeError> {
10
                if result.len() < $algo.digest_length() {
                    return Err(FinalizeError::BufferTooSmall);
                }
                Ok(self.0.finalize(result))
10
            }
        }
        impl Default for $name {
            fn default() -> Self {
                Self::new()
            }
        }
        // Implementing these implies Digest, too
        impl digest_010::HashMarker for $name {}
        impl digest_011::HashMarker for $name {}
        impl digest_010::OutputSizeUser for $name {
            type OutputSize = paste::paste!(digest_010::consts::[< U $digest_len >]);
        }
        impl digest_011::OutputSizeUser for $name {
            type OutputSize = paste::paste!(digest_011::consts::[< U $digest_len >]);
        }
        impl digest_010::Update for $name {
            fn update(&mut self, data: &[u8]) {
                Self::update(self, data).wait_blocking();
            }
        }
        impl digest_011::Update for $name {
            fn update(&mut self, data: &[u8]) {
                Self::update(self, data).wait_blocking();
            }
        }
        impl digest_010::FixedOutput for $name {
            fn finalize_into(mut self, out: &mut digest_010::Output<Self>) {
                Self::finalize(&mut self, out.as_mut()).wait_blocking();
            }
        }
        impl digest_011::FixedOutput for $name {
            fn finalize_into(mut self, out: &mut digest_011::Output<Self>) {
                Self::finalize(&mut self, out.as_mut()).wait_blocking();
            }
        }
        impl digest_010::core_api::BlockSizeUser for $name {
            type BlockSize = paste::paste!(digest_010::consts::[< U $block_size >]);
            fn block_size() -> usize {
                $block_size
            }
        }
        impl digest_011::block_api::BlockSizeUser for $name {
            type BlockSize = paste::paste!(digest_011::consts::[< U $block_size >]);
            fn block_size() -> usize {
                $block_size
            }
        }
    };
}
5
for_each_sha_algorithm! {
    ( $name:ident, $full_name:literal (sizes: $block_size:literal, $digest_len:literal, $message_length_bytes:literal) $security:tt, $mode_bits:literal ) => {
        paste::paste! {
            impl_worker_context!( [<$name Context>], $full_name, ShaAlgorithmKind::$name, $digest_len, $block_size );
        }
    };
}