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    interrupt::InterruptHandler,
32    pac,
33    peripheral::{Peripheral, PeripheralRef},
34    peripherals::{Interrupt, ECC},
35    reg_access::{AlignmentHelper, SocDependentEndianess},
36    system::{self, GenericPeripheralGuard},
37    Blocking,
38    DriverMode,
39};
40
41/// The ECC Accelerator driver instance
42pub struct Ecc<'d, Dm: DriverMode> {
43    ecc: PeripheralRef<'d, ECC>,
44    alignment_helper: AlignmentHelper<SocDependentEndianess>,
45    phantom: PhantomData<Dm>,
46    _guard: GenericPeripheralGuard<{ system::Peripheral::Ecc as u8 }>,
47}
48
49/// ECC interface error
50#[derive(Debug)]
51pub enum Error {
52    /// It means the purpose of the selected block does not match the
53    /// configured key purpose and the calculation will not proceed.
54    SizeMismatchCurve,
55    /// It means that the point is not on the curve.
56    PointNotOnSelectedCurve,
57}
58
59/// Represents supported elliptic curves for cryptographic operations.
60pub enum EllipticCurve {
61    /// The P-192 elliptic curve, a 192-bit curve.
62    P192 = 0,
63    /// The P-256 elliptic curve. a 256-bit curve.
64    P256 = 1,
65}
66
67#[derive(Clone)]
68/// Represents the operational modes for elliptic curve or modular arithmetic
69/// computations.
70pub enum WorkMode {
71    /// Point multiplication mode.
72    PointMultiMode          = 0,
73    #[cfg(esp32c2)]
74    /// Division mode.
75    DivisionMode            = 1,
76    /// Point verification mode.
77    PointVerif              = 2,
78    /// Point verification and multiplication mode.
79    PointVerifMulti         = 3,
80    /// Jacobian point multiplication mode.
81    JacobianPointMulti      = 4,
82    #[cfg(esp32h2)]
83    /// Point addition mode.
84    PointAdd                = 5,
85    /// Jacobian point verification mode.
86    JacobianPointVerif      = 6,
87    /// Point verification and multiplication in Jacobian coordinates.
88    PointVerifJacobianMulti = 7,
89    #[cfg(esp32h2)]
90    /// Modular addition mode.
91    ModAdd                  = 8,
92    #[cfg(esp32h2)]
93    /// Modular subtraction mode.
94    ModSub                  = 9,
95    #[cfg(esp32h2)]
96    /// Modular multiplication mode.
97    ModMulti                = 10,
98    #[cfg(esp32h2)]
99    /// Modular division mode.
100    ModDiv                  = 11,
101}
102
103impl<'d> Ecc<'d, Blocking> {
104    /// Create a new instance in [Blocking] mode.
105    pub fn new(ecc: impl Peripheral<P = ECC> + 'd) -> Self {
106        crate::into_ref!(ecc);
107
108        let guard = GenericPeripheralGuard::new();
109
110        Self {
111            ecc,
112            alignment_helper: AlignmentHelper::default(),
113            phantom: PhantomData,
114            _guard: guard,
115        }
116    }
117}
118
119impl crate::private::Sealed for Ecc<'_, Blocking> {}
120
121#[instability::unstable]
122impl crate::interrupt::InterruptConfigurable for Ecc<'_, Blocking> {
123    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
124        self.set_interrupt_handler(handler);
125    }
126}
127
128impl<Dm: DriverMode> Ecc<'_, Dm> {
129    fn regs(&self) -> &pac::ecc::RegisterBlock {
130        self.ecc.register_block()
131    }
132
133    /// Resets the ECC peripheral.
134    pub fn reset(&mut self) {
135        self.regs().mult_conf().reset()
136    }
137
138    /// # Base point multiplication
139    ///
140    /// Base Point Multiplication can be represented as:
141    /// (Q_x, Q_y) = k * (P_x, P_y)
142    ///
143    /// Output is stored in `x` and `y`.
144    ///
145    /// # Error
146    ///
147    /// This function will return an error if any bitlength value is different
148    /// from the bitlength of the prime fields of the curve.
149    pub fn affine_point_multiplication(
150        &mut self,
151        curve: &EllipticCurve,
152        k: &[u8],
153        x: &mut [u8],
154        y: &mut [u8],
155    ) -> Result<(), Error> {
156        let curve = match curve {
157            EllipticCurve::P192 => {
158                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
159                    return Err(Error::SizeMismatchCurve);
160                }
161                false
162            }
163            EllipticCurve::P256 => {
164                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
165                    return Err(Error::SizeMismatchCurve);
166                }
167                true
168            }
169        };
170        let mode = WorkMode::PointMultiMode;
171
172        let mut tmp = [0_u8; 32];
173        self.reverse_words(k, &mut tmp);
174        self.alignment_helper
175            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
176        self.reverse_words(x, &mut tmp);
177        self.alignment_helper.volatile_write_regset(
178            self.regs().px_mem(0).as_ptr(),
179            tmp.as_ref(),
180            8,
181        );
182        self.reverse_words(y, &mut tmp);
183        self.alignment_helper.volatile_write_regset(
184            self.regs().py_mem(0).as_ptr(),
185            tmp.as_ref(),
186            8,
187        );
188
189        self.regs().mult_conf().write(|w| unsafe {
190            w.work_mode()
191                .bits(mode as u8)
192                .key_length()
193                .bit(curve)
194                .start()
195                .set_bit()
196        });
197
198        // wait for interrupt
199        while self.is_busy() {}
200
201        self.alignment_helper
202            .volatile_read_regset(self.regs().px_mem(0).as_ptr(), &mut tmp, 8);
203        self.reverse_words(tmp.as_ref(), x);
204        self.alignment_helper
205            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
206        self.reverse_words(tmp.as_ref(), y);
207
208        Ok(())
209    }
210
211    /// # Finite Field Division
212    ///
213    /// Finite Field Division can be represented as:
214    /// Result = P_y * k^{−1} mod p
215    ///
216    /// Output is stored in `y`.
217    ///
218    /// # Error
219    ///
220    /// This function will return an error if any bitlength value is different
221    /// from the bitlength of the prime fields of the curve.
222    #[cfg(esp32c2)]
223    pub fn finite_field_division(
224        &mut self,
225        curve: &EllipticCurve,
226        k: &[u8],
227        y: &mut [u8],
228    ) -> Result<(), Error> {
229        let curve = match curve {
230            EllipticCurve::P192 => {
231                if k.len() != 24 || y.len() != 24 {
232                    return Err(Error::SizeMismatchCurve);
233                }
234                false
235            }
236            EllipticCurve::P256 => {
237                if k.len() != 32 || y.len() != 32 {
238                    return Err(Error::SizeMismatchCurve);
239                }
240                true
241            }
242        };
243        let mode = WorkMode::DivisionMode;
244
245        let mut tmp = [0_u8; 32];
246        self.reverse_words(k, &mut tmp);
247        self.alignment_helper
248            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
249        self.reverse_words(y, &mut tmp);
250        self.alignment_helper.volatile_write_regset(
251            self.regs().py_mem(0).as_ptr(),
252            tmp.as_ref(),
253            8,
254        );
255
256        self.regs().mult_conf().write(|w| unsafe {
257            w.work_mode()
258                .bits(mode as u8)
259                .key_length()
260                .bit(curve)
261                .start()
262                .set_bit()
263        });
264
265        // wait for interrupt
266        while self.is_busy() {}
267
268        self.alignment_helper
269            .volatile_read_regset(self.regs().py_mem(0).as_ptr(), &mut tmp, 8);
270        self.reverse_words(tmp.as_ref(), y);
271
272        Ok(())
273    }
274
275    /// # Base Point Verification
276    ///
277    /// Base Point Verification can be used to verify if a point (Px, Py) is
278    /// on a selected elliptic curve.
279    ///
280    /// # Error
281    ///
282    /// This function will return an error if any bitlength value is different
283    /// from the bitlength of the prime fields of the curve.
284    ///
285    /// This function will return an error if the point is not on the selected
286    /// elliptic curve.
287    pub fn affine_point_verification(
288        &mut self,
289        curve: &EllipticCurve,
290        x: &[u8],
291        y: &[u8],
292    ) -> Result<(), Error> {
293        let curve = match curve {
294            EllipticCurve::P192 => {
295                if x.len() != 24 || y.len() != 24 {
296                    return Err(Error::SizeMismatchCurve);
297                }
298                false
299            }
300            EllipticCurve::P256 => {
301                if x.len() != 32 || y.len() != 32 {
302                    return Err(Error::SizeMismatchCurve);
303                }
304                true
305            }
306        };
307        let mode = WorkMode::PointVerif;
308
309        let mut tmp = [0_u8; 32];
310        self.reverse_words(x, &mut tmp);
311        self.alignment_helper.volatile_write_regset(
312            self.regs().px_mem(0).as_ptr(),
313            tmp.as_ref(),
314            8,
315        );
316        self.reverse_words(y, &mut tmp);
317        self.alignment_helper.volatile_write_regset(
318            self.regs().py_mem(0).as_ptr(),
319            tmp.as_ref(),
320            8,
321        );
322
323        self.regs().mult_conf().write(|w| unsafe {
324            w.work_mode()
325                .bits(mode as u8)
326                .key_length()
327                .bit(curve)
328                .start()
329                .set_bit()
330        });
331
332        // wait for interrupt
333        while self.is_busy() {}
334
335        if !self.regs().mult_conf().read().verification_result().bit() {
336            self.regs().mult_conf().reset();
337            return Err(Error::PointNotOnSelectedCurve);
338        }
339
340        Ok(())
341    }
342
343    /// # Base Point Verification + Base Point Multiplication
344    ///
345    /// In this working mode, ECC first verifies if Point (P_x, P_y) is on the
346    /// selected elliptic curve or not. If yes, then perform the multiplication:
347    /// (Q_x, Q_y) = k * (P_x, P_y)
348    ///
349    /// Output is stored in `x` and `y`.
350    ///
351    /// # Error
352    ///
353    /// This function will return an error if any bitlength value is different
354    /// from the bitlength of the prime fields of the curve.
355    ///
356    /// This function will return an error if the point is not on the selected
357    /// elliptic curve.
358    #[cfg(not(esp32h2))]
359    pub fn affine_point_verification_multiplication(
360        &mut self,
361        curve: &EllipticCurve,
362        k: &[u8],
363        x: &mut [u8],
364        y: &mut [u8],
365    ) -> Result<(), Error> {
366        let curve = match curve {
367            EllipticCurve::P192 => {
368                if k.len() != 24 || x.len() != 24 || y.len() != 24 {
369                    return Err(Error::SizeMismatchCurve);
370                }
371                false
372            }
373            EllipticCurve::P256 => {
374                if k.len() != 32 || x.len() != 32 || y.len() != 32 {
375                    return Err(Error::SizeMismatchCurve);
376                }
377                true
378            }
379        };
380        let mode = WorkMode::PointVerifMulti;
381
382        let mut tmp = [0_u8; 32];
383        self.reverse_words(k, &mut tmp);
384        self.alignment_helper
385            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
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            8,
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            8,
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, 8);
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, 8);
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    #[allow(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
474            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
475        self.reverse_words(px, &mut tmp);
476        self.alignment_helper.volatile_write_regset(
477            self.regs().px_mem(0).as_ptr(),
478            tmp.as_ref(),
479            8,
480        );
481        self.reverse_words(py, &mut tmp);
482        self.alignment_helper.volatile_write_regset(
483            self.regs().py_mem(0).as_ptr(),
484            tmp.as_ref(),
485            8,
486        );
487
488        self.regs().mult_conf().write(|w| unsafe {
489            w.work_mode()
490                .bits(mode as u8)
491                .key_length()
492                .bit(curve)
493                .start()
494                .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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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
561            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
562        self.reverse_words(x, &mut tmp);
563        self.alignment_helper.volatile_write_regset(
564            self.regs().px_mem(0).as_ptr(),
565            tmp.as_ref(),
566            8,
567        );
568        self.reverse_words(y, &mut tmp);
569        self.alignment_helper.volatile_write_regset(
570            self.regs().py_mem(0).as_ptr(),
571            tmp.as_ref(),
572            8,
573        );
574
575        self.regs().mult_conf().write(|w| unsafe {
576            w.work_mode()
577                .bits(mode as u8)
578                .key_length()
579                .bit(curve)
580                .start()
581                .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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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(), 8);
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(), 8);
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(), 8);
661            } else {
662                self.alignment_helper
663                    .volatile_write_regset(self.regs().qx_mem(0).as_ptr(), tmp.as_ref(), 8);
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(), 8);
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(), 8);
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
734            .volatile_write_regset(self.regs().k_mem(0).as_ptr(), tmp.as_ref(), 8);
735        self.reverse_words(x, &mut tmp);
736        self.alignment_helper.volatile_write_regset(
737            self.regs().px_mem(0).as_ptr(),
738            tmp.as_ref(),
739            8,
740        );
741        self.reverse_words(y, &mut tmp);
742        self.alignment_helper.volatile_write_regset(
743            self.regs().py_mem(0).as_ptr(),
744            tmp.as_ref(),
745            8,
746        );
747
748        self.regs().mult_conf().write(|w| unsafe {
749            w.work_mode()
750                .bits(mode as u8)
751                .key_length()
752                .bit(curve)
753                .start()
754                .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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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, 8);
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                    8,
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                    8,
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}