esp_hal/
ecc.rs

1//! # Elliptic Curve Cryptography (ECC) Accelerator
2//!
3//! ## Overview
4//!
5//! Elliptic Curve Cryptography (ECC) is an approach to public-key cryptography
6//! based on the algebraic structure of elliptic curves. ECC allows smaller
7//! keys compared to RSA cryptography while providing equivalent security.
8//!
9//! ECC Accelerator can complete various calculation based on different
10//! elliptic curves, thus accelerating ECC algorithm and ECC-derived
11//! algorithms (such as ECDSA).
12//!
13//! ## Configuration
14//! ECC Accelerator supports:
15//! - Two different elliptic curves, namely P-192 and P-256 defined in FIPS 186-3.
16//! - Seven working modes.
17//! - Interrupt upon completion of calculation.
18//!
19//! Inputs of the ECC hardware accelerator must be provided in big-endian
20//! representation. The driver handles the inner representation of the blocks.
21//!
22//! ## Examples
23//! Visit the [ECC] test for an example of using the ECC Accelerator.
24//!
25//! [ECC]: https://github.com/esp-rs/esp-hal/blob/main/hil-test/tests/ecc.rs
26
27use core::marker::PhantomData;
28
29use crate::{
30    Blocking,
31    DriverMode,
32    interrupt::InterruptHandler,
33    pac,
34    peripherals::{ECC, Interrupt},
35    reg_access::{AlignmentHelper, SocDependentEndianess},
36    system::{self, GenericPeripheralGuard},
37};
38
39/// The ECC Accelerator driver instance
40pub struct Ecc<'d, Dm: DriverMode> {
41    ecc: ECC<'d>,
42    alignment_helper: AlignmentHelper<SocDependentEndianess>,
43    phantom: PhantomData<Dm>,
44    _guard: GenericPeripheralGuard<{ system::Peripheral::Ecc as u8 }>,
45}
46
47/// ECC interface error
48#[derive(Debug)]
49pub enum Error {
50    /// It means the purpose of the selected block does not match the
51    /// configured key purpose and the calculation will not proceed.
52    SizeMismatchCurve,
53    /// It means that the point is not on the curve.
54    PointNotOnSelectedCurve,
55}
56
57/// Represents supported elliptic curves for cryptographic operations.
58pub enum EllipticCurve {
59    /// The P-192 elliptic curve, a 192-bit curve.
60    P192 = 0,
61    /// The P-256 elliptic curve. a 256-bit curve.
62    P256 = 1,
63}
64
65#[derive(Clone)]
66/// Represents the operational modes for elliptic curve or modular arithmetic
67/// computations.
68pub enum WorkMode {
69    /// Point multiplication mode.
70    PointMultiMode          = 0,
71    #[cfg(esp32c2)]
72    /// Division mode.
73    DivisionMode            = 1,
74    /// Point verification mode.
75    PointVerif              = 2,
76    /// Point verification and multiplication mode.
77    PointVerifMulti         = 3,
78    /// Jacobian point multiplication mode.
79    JacobianPointMulti      = 4,
80    #[cfg(esp32h2)]
81    /// Point addition mode.
82    PointAdd                = 5,
83    /// Jacobian point verification mode.
84    JacobianPointVerif      = 6,
85    /// Point verification and multiplication in Jacobian coordinates.
86    PointVerifJacobianMulti = 7,
87    #[cfg(esp32h2)]
88    /// Modular addition mode.
89    ModAdd                  = 8,
90    #[cfg(esp32h2)]
91    /// Modular subtraction mode.
92    ModSub                  = 9,
93    #[cfg(esp32h2)]
94    /// Modular multiplication mode.
95    ModMulti                = 10,
96    #[cfg(esp32h2)]
97    /// Modular division mode.
98    ModDiv                  = 11,
99}
100
101impl<'d> Ecc<'d, Blocking> {
102    /// Create a new instance in [Blocking] mode.
103    pub fn new(ecc: ECC<'d>) -> Self {
104        let guard = GenericPeripheralGuard::new();
105
106        Self {
107            ecc,
108            alignment_helper: AlignmentHelper::default(),
109            phantom: PhantomData,
110            _guard: guard,
111        }
112    }
113}
114
115impl crate::private::Sealed for Ecc<'_, Blocking> {}
116
117#[instability::unstable]
118impl crate::interrupt::InterruptConfigurable for Ecc<'_, Blocking> {
119    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
120        self.set_interrupt_handler(handler);
121    }
122}
123
124impl<Dm: DriverMode> Ecc<'_, Dm> {
125    fn regs(&self) -> &pac::ecc::RegisterBlock {
126        self.ecc.register_block()
127    }
128
129    /// Resets the ECC peripheral.
130    pub fn reset(&mut self) {
131        self.regs().mult_conf().reset()
132    }
133
134    /// # Base point multiplication
135    ///
136    /// Base Point Multiplication can be represented as:
137    /// (Q_x, Q_y) = k * (P_x, P_y)
138    ///
139    /// Output is stored in `x` and `y`.
140    ///
141    /// # Error
142    ///
143    /// This function will return an error if any bitlength value is different
144    /// from the bitlength of the prime fields of the curve.
145    pub fn affine_point_multiplication(
146        &mut self,
147        curve: &EllipticCurve,
148        k: &[u8],
149        x: &mut [u8],
150        y: &mut [u8],
151    ) -> Result<(), Error> {
152        let curve = match curve {
153            EllipticCurve::P192 => {
154                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
155                    return Err(Error::SizeMismatchCurve);
156                }
157                false
158            }
159            EllipticCurve::P256 => {
160                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
161                    return Err(Error::SizeMismatchCurve);
162                }
163                true
164            }
165        };
166        let mode = WorkMode::PointMultiMode;
167
168        let mut tmp = [0_u8; 32];
169        self.reverse_words(k, &mut tmp);
170        self.alignment_helper
171            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
172        self.reverse_words(x, &mut tmp);
173        self.alignment_helper.volatile_write_regset(
174            self.regs().px_mem(0).as_ptr(),
175            tmp.as_ref(),
176            8,
177        );
178        self.reverse_words(y, &mut tmp);
179        self.alignment_helper.volatile_write_regset(
180            self.regs().py_mem(0).as_ptr(),
181            tmp.as_ref(),
182            8,
183        );
184
185        self.regs().mult_conf().write(|w| unsafe {
186            w.work_mode()
187                .bits(mode as u8)
188                .key_length()
189                .bit(curve)
190                .start()
191                .set_bit()
192        });
193
194        // wait for interrupt
195        while self.is_busy() {}
196
197        self.alignment_helper
198            .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
199        self.reverse_words(tmp.as_ref(), x);
200        self.alignment_helper
201            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
202        self.reverse_words(tmp.as_ref(), y);
203
204        Ok(())
205    }
206
207    /// # Finite Field Division
208    ///
209    /// Finite Field Division can be represented as:
210    /// Result = P_y * k^{−1} mod p
211    ///
212    /// Output is stored in `y`.
213    ///
214    /// # Error
215    ///
216    /// This function will return an error if any bitlength value is different
217    /// from the bitlength of the prime fields of the curve.
218    #[cfg(esp32c2)]
219    pub fn finite_field_division(
220        &mut self,
221        curve: &EllipticCurve,
222        k: &[u8],
223        y: &mut [u8],
224    ) -> Result<(), Error> {
225        let curve = match curve {
226            EllipticCurve::P192 => {
227                if k.len() != 24 || y.len() != 24 {
228                    return Err(Error::SizeMismatchCurve);
229                }
230                false
231            }
232            EllipticCurve::P256 => {
233                if k.len() != 32 || y.len() != 32 {
234                    return Err(Error::SizeMismatchCurve);
235                }
236                true
237            }
238        };
239        let mode = WorkMode::DivisionMode;
240
241        let mut tmp = [0_u8; 32];
242        self.reverse_words(k, &mut tmp);
243        self.alignment_helper
244            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
245        self.reverse_words(y, &mut tmp);
246        self.alignment_helper.volatile_write_regset(
247            self.regs().py_mem(0).as_ptr(),
248            tmp.as_ref(),
249            8,
250        );
251
252        self.regs().mult_conf().write(|w| unsafe {
253            w.work_mode()
254                .bits(mode as u8)
255                .key_length()
256                .bit(curve)
257                .start()
258                .set_bit()
259        });
260
261        // wait for interrupt
262        while self.is_busy() {}
263
264        self.alignment_helper
265            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
266        self.reverse_words(tmp.as_ref(), y);
267
268        Ok(())
269    }
270
271    /// # Base Point Verification
272    ///
273    /// Base Point Verification can be used to verify if a point (Px, Py) is
274    /// on a selected elliptic curve.
275    ///
276    /// # Error
277    ///
278    /// This function will return an error if any bitlength value is different
279    /// from the bitlength of the prime fields of the curve.
280    ///
281    /// This function will return an error if the point is not on the selected
282    /// elliptic curve.
283    pub fn affine_point_verification(
284        &mut self,
285        curve: &EllipticCurve,
286        x: &[u8],
287        y: &[u8],
288    ) -> Result<(), Error> {
289        let curve = match curve {
290            EllipticCurve::P192 => {
291                if x.len() != 24 || y.len() != 24 {
292                    return Err(Error::SizeMismatchCurve);
293                }
294                false
295            }
296            EllipticCurve::P256 => {
297                if x.len() != 32 || y.len() != 32 {
298                    return Err(Error::SizeMismatchCurve);
299                }
300                true
301            }
302        };
303        let mode = WorkMode::PointVerif;
304
305        let mut tmp = [0_u8; 32];
306        self.reverse_words(x, &mut tmp);
307        self.alignment_helper.volatile_write_regset(
308            self.regs().px_mem(0).as_ptr(),
309            tmp.as_ref(),
310            8,
311        );
312        self.reverse_words(y, &mut tmp);
313        self.alignment_helper.volatile_write_regset(
314            self.regs().py_mem(0).as_ptr(),
315            tmp.as_ref(),
316            8,
317        );
318
319        self.regs().mult_conf().write(|w| unsafe {
320            w.work_mode()
321                .bits(mode as u8)
322                .key_length()
323                .bit(curve)
324                .start()
325                .set_bit()
326        });
327
328        // wait for interrupt
329        while self.is_busy() {}
330
331        if !self.regs().mult_conf().read().verification_result().bit() {
332            self.regs().mult_conf().reset();
333            return Err(Error::PointNotOnSelectedCurve);
334        }
335
336        Ok(())
337    }
338
339    /// # Base Point Verification + Base Point Multiplication
340    ///
341    /// In this working mode, ECC first verifies if Point (P_x, P_y) is on the
342    /// selected elliptic curve or not. If yes, then perform the multiplication:
343    /// (Q_x, Q_y) = k * (P_x, P_y)
344    ///
345    /// Output is stored in `x` and `y`.
346    ///
347    /// # Error
348    ///
349    /// This function will return an error if any bitlength value is different
350    /// from the bitlength of the prime fields of the curve.
351    ///
352    /// This function will return an error if the point is not on the selected
353    /// elliptic curve.
354    #[cfg(not(esp32h2))]
355    pub fn affine_point_verification_multiplication(
356        &mut self,
357        curve: &EllipticCurve,
358        k: &[u8],
359        x: &mut [u8],
360        y: &mut [u8],
361    ) -> Result<(), Error> {
362        let curve = match curve {
363            EllipticCurve::P192 => {
364                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
365                    return Err(Error::SizeMismatchCurve);
366                }
367                false
368            }
369            EllipticCurve::P256 => {
370                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
371                    return Err(Error::SizeMismatchCurve);
372                }
373                true
374            }
375        };
376        let mode = WorkMode::PointVerifMulti;
377
378        let mut tmp = [0_u8; 32];
379        self.reverse_words(k, &mut tmp);
380        self.alignment_helper
381            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
382        self.reverse_words(x, &mut tmp);
383        self.alignment_helper.volatile_write_regset(
384            self.regs().px_mem(0).as_ptr(),
385            tmp.as_ref(),
386            8,
387        );
388        self.reverse_words(y, &mut tmp);
389        self.alignment_helper.volatile_write_regset(
390            self.regs().py_mem(0).as_ptr(),
391            tmp.as_ref(),
392            8,
393        );
394
395        self.regs().mult_conf().write(|w| unsafe {
396            w.work_mode()
397                .bits(mode as u8)
398                .key_length()
399                .bit(curve)
400                .start()
401                .set_bit()
402        });
403
404        // wait for interrupt
405        while self.is_busy() {}
406
407        if !self.regs().mult_conf().read().verification_result().bit() {
408            self.regs().mult_conf().reset();
409            return Err(Error::PointNotOnSelectedCurve);
410        }
411
412        self.alignment_helper
413            .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
414        self.reverse_words(tmp.as_ref(), x);
415        self.alignment_helper
416            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
417        self.reverse_words(tmp.as_ref(), y);
418
419        Ok(())
420    }
421
422    /// # Base Point Verification + Base Point Multiplication
423    ///
424    /// In this working mode, ECC first verifies if Point (P_x, P_y) is on the
425    /// selected elliptic curve or not. If yes, then perform the multiplication:
426    /// (Q_x, Q_y) = (J_x, J_y, J_z) = k * (P_x, P_y)
427    ///
428    /// The affine point representation output is stored in `px` and `py`.
429    /// The Jacobian point representation output is stored in `qx`, `qy`, and
430    /// `qz`.
431    ///
432    /// # Error
433    ///
434    /// This function will return an error if any bitlength value is different
435    /// from the bitlength of the prime fields of the curve.
436    ///
437    /// This function will return an error if the point is not on the selected
438    /// elliptic curve.
439    #[allow(clippy::too_many_arguments)]
440    #[cfg(esp32h2)]
441    pub fn affine_point_verification_multiplication(
442        &mut self,
443        curve: &EllipticCurve,
444        k: &[u8],
445        px: &mut [u8],
446        py: &mut [u8],
447        qx: &mut [u8],
448        qy: &mut [u8],
449        qz: &mut [u8],
450    ) -> Result<(), Error> {
451        let curve = match curve {
452            EllipticCurve::P192 => {
453                if k.len() != 24 || px.len() != 24 || py.len() != 24 {
454                    return Err(Error::SizeMismatchCurve);
455                }
456                false
457            }
458            EllipticCurve::P256 => {
459                if k.len() != 32 || px.len() != 32 || py.len() != 32 {
460                    return Err(Error::SizeMismatchCurve);
461                }
462                true
463            }
464        };
465        let mode = WorkMode::PointVerifMulti;
466
467        let mut tmp = [0_u8; 32];
468        self.reverse_words(k, &mut tmp);
469        self.alignment_helper
470            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
471        self.reverse_words(px, &mut tmp);
472        self.alignment_helper.volatile_write_regset(
473            self.regs().px_mem(0).as_ptr(),
474            tmp.as_ref(),
475            8,
476        );
477        self.reverse_words(py, &mut tmp);
478        self.alignment_helper.volatile_write_regset(
479            self.regs().py_mem(0).as_ptr(),
480            tmp.as_ref(),
481            8,
482        );
483
484        self.regs().mult_conf().write(|w| unsafe {
485            w.work_mode()
486                .bits(mode as u8)
487                .key_length()
488                .bit(curve)
489                .start()
490                .set_bit()
491        });
492
493        // wait for interrupt
494        while self.is_busy() {}
495
496        if !self.regs().mult_conf().read().verification_result().bit() {
497            self.regs().mult_conf().reset();
498            return Err(Error::PointNotOnSelectedCurve);
499        }
500
501        self.alignment_helper
502            .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
503        self.reverse_words(tmp.as_ref(), px);
504        self.alignment_helper
505            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
506        self.reverse_words(tmp.as_ref(), py);
507        self.alignment_helper
508            .volatile_read_regset(self.regs().qx_mem(0).as_ptr(), &mut tmp, 8);
509        self.reverse_words(tmp.as_ref(), qx);
510        self.alignment_helper
511            .volatile_read_regset(self.regs().qy_mem(0).as_ptr(), &mut tmp, 8);
512        self.reverse_words(tmp.as_ref(), qy);
513        self.alignment_helper
514            .volatile_read_regset(self.regs().qz_mem(0).as_ptr(), &mut tmp, 8);
515        self.reverse_words(tmp.as_ref(), qz);
516
517        Ok(())
518    }
519
520    /// # Jacobian Point Multiplication
521    ///
522    /// Jacobian Point Multiplication can be represented as:
523    /// (Q_x, Q_y, Q_z) = k * (P_x, P_y, 1)
524    ///
525    /// Output is stored in `x`, `y`, and `k`.
526    ///
527    /// # Error
528    ///
529    /// This function will return an error if any bitlength value is different
530    /// from the bitlength of the prime fields of the curve.
531    pub fn jacobian_point_multiplication(
532        &mut self,
533        curve: &EllipticCurve,
534        k: &mut [u8],
535        x: &mut [u8],
536        y: &mut [u8],
537    ) -> Result<(), Error> {
538        let curve = match curve {
539            EllipticCurve::P192 => {
540                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
541                    return Err(Error::SizeMismatchCurve);
542                }
543                false
544            }
545            EllipticCurve::P256 => {
546                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
547                    return Err(Error::SizeMismatchCurve);
548                }
549                true
550            }
551        };
552        let mode = WorkMode::JacobianPointMulti;
553
554        let mut tmp = [0_u8; 32];
555        self.reverse_words(k, &mut tmp);
556        self.alignment_helper
557            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
558        self.reverse_words(x, &mut tmp);
559        self.alignment_helper.volatile_write_regset(
560            self.regs().px_mem(0).as_ptr(),
561            tmp.as_ref(),
562            8,
563        );
564        self.reverse_words(y, &mut tmp);
565        self.alignment_helper.volatile_write_regset(
566            self.regs().py_mem(0).as_ptr(),
567            tmp.as_ref(),
568            8,
569        );
570
571        self.regs().mult_conf().write(|w| unsafe {
572            w.work_mode()
573                .bits(mode as u8)
574                .key_length()
575                .bit(curve)
576                .start()
577                .set_bit()
578        });
579
580        while self.is_busy() {}
581
582        cfg_if::cfg_if! {
583            if #[cfg(not(esp32h2))] {
584            self.alignment_helper
585                .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
586            self.reverse_words(tmp.as_ref(), x);
587            self.alignment_helper
588                .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
589            self.reverse_words(tmp.as_ref(), y);
590            self.alignment_helper
591                .volatile_read_regset(self.regs().k_mem(0).as_ptr(), &mut tmp, 8);
592            self.reverse_words(tmp.as_ref(), k);
593            } else {
594            self.alignment_helper
595                .volatile_read_regset(self.regs().qx_mem(0).as_ptr(), &mut tmp, 8);
596            self.reverse_words(tmp.as_ref(), x);
597            self.alignment_helper
598                .volatile_read_regset(self.regs().qy_mem(0).as_ptr(), &mut tmp, 8);
599            self.reverse_words(tmp.as_ref(), y);
600            self.alignment_helper
601                .volatile_read_regset(self.regs().qz_mem(0).as_ptr(), &mut tmp, 8);
602            self.reverse_words(tmp.as_ref(), k);
603            }
604        }
605
606        Ok(())
607    }
608
609    /// # Jacobian Point Verification
610    ///
611    /// Jacobian Point Verification can be used to verify if a point (Q_x, Q_y,
612    /// Q_z) is on a selected elliptic curve.
613    ///
614    /// # Error
615    ///
616    /// This function will return an error if any bitlength value is different
617    /// from the bitlength of the prime fields of the curve.
618    ///
619    /// This function will return an error if the point is not on the selected
620    /// elliptic curve.
621    pub fn jacobian_point_verification(
622        &mut self,
623        curve: &EllipticCurve,
624        x: &[u8],
625        y: &[u8],
626        z: &[u8],
627    ) -> Result<(), Error> {
628        let curve = match curve {
629            EllipticCurve::P192 => {
630                if x.len() != 24 || y.len() != 24 || z.len() != 24 {
631                    return Err(Error::SizeMismatchCurve);
632                }
633                false
634            }
635            EllipticCurve::P256 => {
636                if x.len() != 32 || y.len() != 32 || z.len() != 32 {
637                    return Err(Error::SizeMismatchCurve);
638                }
639                true
640            }
641        };
642        let mode = WorkMode::JacobianPointVerif;
643
644        let mut tmp = [0_u8; 32];
645        self.reverse_words(x, &mut tmp);
646
647        cfg_if::cfg_if! {
648            if #[cfg(not(esp32h2))] {
649                self.alignment_helper
650                    .volatile_write_regset(self.regs().px_mem(0).as_ptr(), tmp.as_ref(), 8);
651                self.reverse_words(y, &mut tmp);
652                self.alignment_helper
653                    .volatile_write_regset(self.regs().py_mem(0).as_ptr(), tmp.as_ref(), 8);
654                self.reverse_words(z, &mut tmp);
655                self.alignment_helper
656                    .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
657            } else {
658                self.alignment_helper
659                    .volatile_write_regset(self.regs().qx_mem(0).as_ptr(), tmp.as_ref(), 8);
660                self.reverse_words(y, &mut tmp);
661                self.alignment_helper
662                    .volatile_write_regset(self.regs().qy_mem(0).as_ptr(), tmp.as_ref(), 8);
663                self.reverse_words(z, &mut tmp);
664                self.alignment_helper
665                    .volatile_write_regset(self.regs().qz_mem(0).as_ptr(), tmp.as_ref(), 8);
666            }
667        }
668
669        self.regs().mult_conf().write(|w| unsafe {
670            w.work_mode()
671                .bits(mode as u8)
672                .key_length()
673                .bit(curve)
674                .start()
675                .set_bit()
676        });
677
678        // wait for interrupt
679        while self.is_busy() {}
680
681        if !self.regs().mult_conf().read().verification_result().bit() {
682            self.regs().mult_conf().reset();
683            return Err(Error::PointNotOnSelectedCurve);
684        }
685
686        Ok(())
687    }
688
689    /// # Base Point Verification + Jacobian Point Multiplication
690    ///
691    /// In this working mode, ECC first verifies if Point (Px, Py) is on the
692    /// selected elliptic curve or not. If yes, then perform the multiplication:
693    /// (Q_x, Q_y, Q_z) = k * (P_x, P_y, 1)
694    ///
695    /// Output is stored in `x`, `y`, and `k`.
696    ///
697    /// # Error
698    ///
699    /// This function will return an error if any bitlength value is different
700    /// from the bitlength of the prime fields of the curve.
701    ///
702    /// This function will return an error if the point is not on the selected
703    /// elliptic curve.
704    pub fn affine_point_verification_jacobian_multiplication(
705        &mut self,
706        curve: &EllipticCurve,
707        k: &mut [u8],
708        x: &mut [u8],
709        y: &mut [u8],
710    ) -> Result<(), Error> {
711        let curve = match curve {
712            EllipticCurve::P192 => {
713                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
714                    return Err(Error::SizeMismatchCurve);
715                }
716                false
717            }
718            EllipticCurve::P256 => {
719                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
720                    return Err(Error::SizeMismatchCurve);
721                }
722                true
723            }
724        };
725        let mode = WorkMode::PointVerifJacobianMulti;
726
727        let mut tmp = [0_u8; 32];
728        self.reverse_words(k, &mut tmp);
729        self.alignment_helper
730            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
731        self.reverse_words(x, &mut tmp);
732        self.alignment_helper.volatile_write_regset(
733            self.regs().px_mem(0).as_ptr(),
734            tmp.as_ref(),
735            8,
736        );
737        self.reverse_words(y, &mut tmp);
738        self.alignment_helper.volatile_write_regset(
739            self.regs().py_mem(0).as_ptr(),
740            tmp.as_ref(),
741            8,
742        );
743
744        self.regs().mult_conf().write(|w| unsafe {
745            w.work_mode()
746                .bits(mode as u8)
747                .key_length()
748                .bit(curve)
749                .start()
750                .set_bit()
751        });
752
753        // wait for interrupt
754        while self.is_busy() {}
755
756        if !self.regs().mult_conf().read().verification_result().bit() {
757            self.regs().mult_conf().reset();
758            return Err(Error::PointNotOnSelectedCurve);
759        }
760
761        if !self.regs().mult_conf().read().verification_result().bit() {
762            self.regs().mult_conf().reset();
763            return Err(Error::PointNotOnSelectedCurve);
764        }
765
766        cfg_if::cfg_if! {
767            if #[cfg(not(esp32h2))] {
768                self.alignment_helper
769                    .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
770                self.reverse_words(tmp.as_ref(), x);
771                self.alignment_helper
772                    .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
773                self.reverse_words(tmp.as_ref(), y);
774                self.alignment_helper
775                    .volatile_read_regset(self.regs().k_mem(0).as_ptr(), &mut tmp, 8);
776                self.reverse_words(tmp.as_ref(), k);
777            } else {
778                self.alignment_helper
779                    .volatile_read_regset(self.regs().qx_mem(0).as_ptr(), &mut tmp, 8);
780                self.reverse_words(tmp.as_ref(), x);
781                self.alignment_helper
782                    .volatile_read_regset(self.regs().qy_mem(0).as_ptr(), &mut tmp, 8);
783                self.reverse_words(tmp.as_ref(), y);
784                self.alignment_helper
785                    .volatile_read_regset(self.regs().qz_mem(0).as_ptr(), &mut tmp, 8);
786                self.reverse_words(tmp.as_ref(), k);
787            }
788        }
789
790        Ok(())
791    }
792
793    /// # Point Addition
794    ///
795    /// In this working mode, ECC first verifies if Point (Px, Py) is on the
796    /// selected elliptic curve or not. If yes, then perform the addition:
797    /// (R_x, R_y) = (J_x, J_y, J_z) = (P_x, P_y, 1) + (Q_x, Q_y, Q_z)
798    ///
799    /// This functions requires data in Little Endian.
800    /// The affine point representation output is stored in `px` and `py`.
801    /// The Jacobian point representation output is stored in `qx`, `qy`, and
802    /// `qz`.
803    ///
804    /// # Error
805    ///
806    /// This function will return an error if any bitlength value is different
807    /// from the bitlength of the prime fields of the curve.
808    ///
809    /// This function will return an error if the point is not on the selected
810    /// elliptic curve.
811    #[cfg(esp32h2)]
812    pub fn affine_point_addition(
813        &mut self,
814        curve: &EllipticCurve,
815        px: &mut [u8],
816        py: &mut [u8],
817        qx: &mut [u8],
818        qy: &mut [u8],
819        qz: &mut [u8],
820    ) -> Result<(), Error> {
821        let curve = match curve {
822            EllipticCurve::P192 => {
823                if px.len() != 24
824                    || py.len() != 24
825                    || qx.len() != 24
826                    || qy.len() != 24
827                    || qz.len() != 24
828                {
829                    return Err(Error::SizeMismatchCurve);
830                }
831                false
832            }
833            EllipticCurve::P256 => {
834                if px.len() != 32
835                    || py.len() != 32
836                    || qx.len() != 32
837                    || qy.len() != 32
838                    || qz.len() != 32
839                {
840                    return Err(Error::SizeMismatchCurve);
841                }
842                true
843            }
844        };
845        let mode = WorkMode::PointAdd;
846
847        let mut tmp = [0_u8; 32];
848
849        tmp[0..px.len()].copy_from_slice(px);
850        self.alignment_helper
851            .volatile_write_regset(self.regs().px_mem(0).as_ptr(), &tmp, 8);
852        tmp[0..py.len()].copy_from_slice(py);
853        self.alignment_helper
854            .volatile_write_regset(self.regs().py_mem(0).as_ptr(), &tmp, 8);
855        tmp[0..qx.len()].copy_from_slice(qx);
856        self.alignment_helper
857            .volatile_write_regset(self.regs().qx_mem(0).as_ptr(), &tmp, 8);
858        tmp[0..qy.len()].copy_from_slice(qy);
859        self.alignment_helper
860            .volatile_write_regset(self.regs().qy_mem(0).as_ptr(), &tmp, 8);
861        tmp[0..qz.len()].copy_from_slice(qz);
862        self.alignment_helper
863            .volatile_write_regset(self.regs().qz_mem(0).as_ptr(), &tmp, 8);
864
865        self.regs().mult_conf().write(|w| unsafe {
866            w.work_mode()
867                .bits(mode as u8)
868                .key_length()
869                .bit(curve)
870                .start()
871                .set_bit()
872        });
873
874        // wait for interrupt
875        while self.is_busy() {}
876
877        self.alignment_helper
878            .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
879        let mut tmp_len = px.len();
880        px[..].copy_from_slice(&tmp[..tmp_len]);
881        self.alignment_helper
882            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
883        tmp_len = py.len();
884        py[..].copy_from_slice(&tmp[..tmp_len]);
885        self.alignment_helper
886            .volatile_read_regset(self.regs().qx_mem(0).as_ptr(), &mut tmp, 8);
887        tmp_len = qx.len();
888        qx[..].copy_from_slice(&tmp[..tmp_len]);
889        self.alignment_helper
890            .volatile_read_regset(self.regs().qy_mem(0).as_ptr(), &mut tmp, 8);
891        tmp_len = qy.len();
892        qy[..].copy_from_slice(&tmp[..tmp_len]);
893        self.alignment_helper
894            .volatile_read_regset(self.regs().qz_mem(0).as_ptr(), &mut tmp, 8);
895        tmp_len = qz.len();
896        qz[..].copy_from_slice(&tmp[..tmp_len]);
897
898        Ok(())
899    }
900
901    /// # Mod Operations (+-*/)
902    ///
903    /// In this working mode, ECC first verifies if Point (A, B) is on the
904    /// selected elliptic curve or not. If yes, then perform single mod
905    /// operation: R = A (+-*/) B mod N
906    ///
907    /// This functions requires data in Little Endian.
908    /// Output is stored in `a` (+-) and in `b` (*/).
909    ///
910    /// # Error
911    ///
912    /// This function will return an error if any bitlength value is different
913    /// from the bitlength of the prime fields of the curve.
914    ///
915    /// This function will return an error if the point is not on the selected
916    /// elliptic curve.
917    #[cfg(esp32h2)]
918    pub fn mod_operations(
919        &mut self,
920        curve: &EllipticCurve,
921        a: &mut [u8],
922        b: &mut [u8],
923        work_mode: WorkMode,
924    ) -> Result<(), Error> {
925        let curve = match curve {
926            EllipticCurve::P192 => {
927                if a.len() != 24 || b.len() != 24 {
928                    return Err(Error::SizeMismatchCurve);
929                }
930                false
931            }
932            EllipticCurve::P256 => {
933                if a.len() != 32 || b.len() != 32 {
934                    return Err(Error::SizeMismatchCurve);
935                }
936                true
937            }
938        };
939
940        let mut tmp = [0_u8; 32];
941        tmp[0..a.len()].copy_from_slice(a);
942        self.alignment_helper
943            .volatile_write_regset(self.regs().px_mem(0).as_ptr(), &tmp, 8);
944        tmp[0..b.len()].copy_from_slice(b);
945        self.alignment_helper
946            .volatile_write_regset(self.regs().py_mem(0).as_ptr(), &tmp, 8);
947
948        self.regs().mult_conf().write(|w| unsafe {
949            w.work_mode()
950                .bits(work_mode.clone() as u8)
951                .key_length()
952                .bit(curve)
953                .start()
954                .set_bit()
955        });
956
957        // wait for interrupt
958        while self.is_busy() {}
959
960        match work_mode {
961            WorkMode::ModAdd | WorkMode::ModSub => {
962                self.alignment_helper.volatile_read_regset(
963                    self.regs().px_mem(0).as_ptr(),
964                    &mut tmp,
965                    8,
966                );
967                let tmp_len = a.len();
968                a[..].copy_from_slice(&tmp[..tmp_len]);
969            }
970            WorkMode::ModMulti | WorkMode::ModDiv => {
971                self.alignment_helper.volatile_read_regset(
972                    self.regs().py_mem(0).as_ptr(),
973                    &mut tmp,
974                    8,
975                );
976                let tmp_len = b.len();
977                b[..].copy_from_slice(&tmp[..tmp_len]);
978            }
979            _ => unreachable!(),
980        }
981
982        Ok(())
983    }
984
985    /// Register an interrupt handler for the ECC peripheral.
986    ///
987    /// Note that this will replace any previously registered interrupt
988    /// handlers.
989    #[instability::unstable]
990    pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
991        for core in crate::system::Cpu::other() {
992            crate::interrupt::disable(core, Interrupt::ECC);
993        }
994        unsafe { crate::interrupt::bind_interrupt(Interrupt::ECC, handler.handler()) };
995        unwrap!(crate::interrupt::enable(Interrupt::ECC, handler.priority()));
996    }
997
998    fn is_busy(&self) -> bool {
999        self.regs().mult_conf().read().start().bit_is_set()
1000    }
1001
1002    fn reverse_words(&self, src: &[u8], dst: &mut [u8]) {
1003        let n = core::cmp::min(src.len(), dst.len());
1004        let nsrc = if src.len() > n {
1005            src.split_at(n).0
1006        } else {
1007            src
1008        };
1009        let ndst = if dst.len() > n {
1010            dst.split_at_mut(n).0
1011        } else {
1012            dst
1013        };
1014        for (a, b) in nsrc.chunks_exact(4).zip(ndst.rchunks_exact_mut(4)) {
1015            b.copy_from_slice(&u32::from_be_bytes(a.try_into().unwrap()).to_ne_bytes());
1016        }
1017    }
1018}