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