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