Skip to main content

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
13use core::{marker::PhantomData, ptr::NonNull};
14
15use procmacros::BuilderLite;
16
17#[cfg(ecc_supports_enhanced_security)]
18use crate::efuse::ChipRevision;
19use crate::{
20    Blocking,
21    DriverMode,
22    interrupt::InterruptHandler,
23    pac::{self, ecc::mult_conf::KEY_LENGTH},
24    peripherals::{ECC, Interrupt},
25    private::Sealed,
26    system::{self, GenericPeripheralGuard},
27    work_queue::{Handle, Poll, Status, VTable, WorkQueue, WorkQueueDriver, WorkQueueFrontend},
28};
29
30/// This macro defines 4 other macros:
31/// - `doc_summary` that takes the first line of the documentation and returns it as a string
32/// - `result_type` that generates the return types for each operation
33/// - `operation` that generates the operation function
34/// - `backend_operation` that generates the backend operation function
35///
36/// These generated macros can then be fed to `for_each_ecc_working_mode!` to generate operations
37/// the device supports.
38macro_rules! define_operations {
39    ($($op:tt {
40        // The first line is used for summary, and it is prepended with `# ` on the driver method.
41        docs: [$first_line:literal $(, $lines:literal)*],
42        // The driver method name
43        function: $function:ident,
44        // Whether the operation is modular, i.e. whether it needs a modulus argument.
45        $(modular_arithmetic_method: $is_modular:literal,)?
46        // Whether the operation does point verification first.
47        $(verifies_point: $verifies_point:literal,)?
48        // Input parameters. This determines the name and order of the function arguments,
49        // as well as which memory block they will be written to. Depending on the value of
50        // cfg(ecc_separate_jacobian_point_memory), qx, qy and qz may be mapped to px, py and k.
51        inputs: [$($input:ident),*],
52        // What data does the output contain?
53        // - Scalar (and which memory block contains the scalar)
54        // - AffinePoint
55        // - JacobianPoint
56        returns: [
57            $(
58                // What data is computed may be device specific.
59                $(#[$returns_meta:meta])*
60                $returns:ident $({ const $c:ident: $t:tt = $v:expr })?
61            ),*
62        ]
63    }),*) => {
64        macro_rules! doc_summary {
65            $(
66                ($op) => { $first_line };
67            )*
68        }
69        macro_rules! result_type {
70            $(
71                ($op) => {
72                    #[doc = concat!("A marker type representing ", doc_summary!($op))]
73                    #[non_exhaustive]
74                    pub struct $op;
75
76                    impl crate::private::Sealed for $op {}
77
78                    impl EccOperation for $op {
79                        const WORK_MODE: WorkMode = WorkMode::$op;
80                        const VERIFIES_POINT: bool = $crate::if_set!($($verifies_point)?, false);
81                    }
82
83                    paste::paste! {
84                        $(
85                            $(#[$returns_meta])*
86                            impl [<OperationReturns $returns>] for $op {
87                                $(
88                                    const $c: $t = $v;
89                                )?
90                            }
91                        )*
92
93                        $(
94                            const _: bool = $verifies_point; // I just need this ignored.
95                            impl OperationVerifiesPoint for $op {}
96                        )?
97                    }
98                };
99            )*
100        }
101        macro_rules! driver_method {
102            $(
103                ($op) => {
104                    #[doc = concat!("# ", $first_line)]
105                    $(#[doc = $lines])*
106                    #[doc = r"
107
108## Errors
109
110This function will return an error if the bitlength of the parameters is different
111from the bitlength of the prime fields of the curve."]
112                    #[inline]
113                    pub fn $function<'op>(
114                        &'op mut self,
115                        curve: EllipticCurve,
116                        $(#[cfg($is_modular)] modulus: EccModBase,)?
117                        $($input: &[u8],)*
118                    ) -> Result<EccResultHandle<'op, $op>, KeyLengthMismatch> {
119                        curve.size_check([$($input),*])?;
120
121                        paste::paste! {
122                            $(
123                                self.info().write_mem(self.info().[<$input _mem>](), $input);
124                            )*
125                        };
126
127                        #[cfg(ecc_has_modular_arithmetic)]
128                        let mod_base = $crate::if_set! {
129                            $(
130                                {
131                                    $crate::ignore!($is_modular);
132                                    modulus
133                                }
134                            )?,
135                            // else
136                            EccModBase::OrderOfCurve
137                        };
138
139                        Ok(self.run_operation::<$op>(
140                            curve,
141                            #[cfg(ecc_has_modular_arithmetic)] mod_base,
142                        ))
143                    }
144                };
145            )*
146        }
147
148        macro_rules! backend_operation {
149            $(
150                ($op) => {
151                    #[doc = concat!("Configures a new ", $first_line, " operation with the given inputs, to be executed on [`EccBackend`].")]
152                    ///
153                    /// Outputs need to be assigned separately before executing the operation.
154                    pub fn $function<'op>(
155                        self,
156                        $(#[cfg($is_modular)] modulus: EccModBase,)?
157                        $($input: &'op [u8],)*
158                    ) -> Result<EccBackendOperation<'op, $op>, KeyLengthMismatch> {
159                        self.size_check([&$($input,)*])?;
160
161                        #[cfg(ecc_has_modular_arithmetic)]
162                        let mod_base = $crate::if_set! {
163                            $(
164                                {
165                                    $crate::ignore!($is_modular);
166                                    modulus
167                                }
168                            )?,
169                            // else
170                            EccModBase::OrderOfCurve
171                        };
172
173                        let work_item = EccWorkItem {
174                            curve: self,
175                            operation: WorkMode::$op,
176                            cancelled: false,
177                            #[cfg(ecc_has_modular_arithmetic)]
178                            mod_base,
179                            inputs: {
180                                let mut inputs = MemoryPointers::default();
181                                $(
182                                    paste::paste! {
183                                        inputs.[<set_ $input>](NonNull::from($input));
184                                    };
185                                )*
186                                inputs
187                            },
188                            point_verification_result: false,
189                            outputs: MemoryPointers::default(),
190                        };
191
192                        Ok(EccBackendOperation::new(work_item))
193                    }
194                };
195            )*
196        }
197    }
198}
199
200define_operations! {
201    AffinePointMultiplication {
202        docs: [
203            "Base Point Multiplication",
204            "",
205            "This operation performs `(Qx, Qy) = k * (Px, Py)`."
206        ],
207        function: affine_point_multiplication,
208        inputs: [k, px, py],
209        returns: [AffinePoint]
210    },
211
212    AffinePointVerification {
213        docs: [
214            "Base Point Verification",
215            "",
216            "This operation verifies whether Point (Px, Py) is on the selected elliptic curve."
217        ],
218        function: affine_point_verification,
219        verifies_point: true,
220        inputs: [px, py],
221        returns: []
222    },
223
224    AffinePointVerificationAndMultiplication {
225        docs: [
226            "Base Point Verification and Multiplication",
227            "",
228            "This operation verifies whether Point (Px, Py) is on the selected elliptic curve and performs `(Qx, Qy) = k * (Px, Py)`."
229        ],
230        function: affine_point_verification_multiplication,
231        verifies_point: true,
232        inputs: [k, px, py],
233        returns: [
234            AffinePoint,
235            #[cfg(ecc_separate_jacobian_point_memory)]
236            JacobianPoint
237        ]
238    },
239
240    AffinePointAddition {
241        docs: [
242            "Point Addition",
243            "",
244            "This operation performs `(Rx, Ry) = (Jx, Jy, Jz) = (Px, Py, 1) + (Qx, Qy, Qz)`."
245        ],
246        function: affine_point_addition,
247        inputs: [px, py, qx, qy, qz],
248        returns: [
249            AffinePoint,
250            #[cfg(ecc_separate_jacobian_point_memory)]
251            JacobianPoint
252        ]
253    },
254
255    JacobianPointMultiplication {
256        docs: [
257            "Jacobian Point Multiplication",
258            "",
259            "This operation performs `(Qx, Qy, Qz) = k * (Px, Py, 1)`."
260        ],
261        function: jacobian_point_multiplication,
262        inputs: [k, px, py],
263        returns: [
264            JacobianPoint
265        ]
266    },
267
268    JacobianPointVerification {
269        docs: [
270            "Jacobian Point Verification",
271            "",
272            "This operation verifies whether Point (Qx, Qy, Qz) is on the selected elliptic curve."
273        ],
274        function: jacobian_point_verification,
275        verifies_point: true,
276        inputs: [qx, qy, qz],
277        returns: [
278            JacobianPoint
279        ]
280    },
281
282    AffinePointVerificationAndJacobianPointMultiplication {
283        docs: [
284            "Base Point Verification + Jacobian Point Multiplication",
285            "",
286            "This operation first verifies whether Point (Px, Py) is on the selected elliptic curve. If yes, it performs `(Qx, Qy, Qz) = k * (Px, Py, 1)`."
287        ],
288        function: affine_point_verification_jacobian_multiplication,
289        verifies_point: true,
290        inputs: [k, px, py],
291        returns: [
292            JacobianPoint
293        ]
294    },
295
296    FiniteFieldDivision {
297        docs: [
298            "Finite Field Division",
299            "",
300            "This operation performs `R = Py * k^{−1} mod p`."
301        ],
302        function: finite_field_division,
303        inputs: [k, py],
304        returns: [
305            Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
306        ]
307    },
308
309    ModularAddition {
310        docs: [
311            "Modular Addition",
312            "",
313            "This operation performs `R = Px + Py mod p`."
314        ],
315        function: modular_addition,
316        modular_arithmetic_method: true,
317        inputs: [px, py],
318        returns: [
319            Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Px }
320        ]
321    },
322
323    ModularSubtraction {
324        docs: [
325            "Modular Subtraction",
326            "",
327            "This operation performs `R = Px - Py mod p`."
328        ],
329        function: modular_subtraction,
330        modular_arithmetic_method: true,
331        inputs: [px, py],
332        returns: [
333            Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Px }
334        ]
335    },
336
337    ModularMultiplication {
338        docs: [
339            "Modular Multiplication",
340            "",
341            "This operation performs `R = Px * Py mod p`."
342        ],
343        function: modular_multiplication,
344        modular_arithmetic_method: true,
345        inputs: [px, py],
346        returns: [
347            Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
348        ]
349    },
350
351    ModularDivision {
352        docs: [
353            "Modular Division",
354            "",
355            "This operation performs `R = Px * Py^{−1} mod p`."
356        ],
357        function: modular_division,
358        modular_arithmetic_method: true,
359        inputs: [px, py],
360        returns: [
361            Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
362        ]
363    }
364}
365
366const MEM_BLOCK_SIZE: usize = property!("ecc.mem_block_size");
367
368/// The ECC Accelerator driver.
369///
370/// Note that as opposed to commonly used standards, this driver operates on
371/// **little-endian** data.
372pub struct Ecc<'d, Dm: DriverMode> {
373    _ecc: ECC<'d>,
374    phantom: PhantomData<Dm>,
375    _memory_guard: EccMemoryPowerGuard,
376    _guard: GenericPeripheralGuard<{ system::Peripheral::Ecc as u8 }>,
377}
378
379struct EccMemoryPowerGuard;
380
381impl EccMemoryPowerGuard {
382    fn new() -> Self {
383        #[cfg(soc_has_pcr)]
384        crate::peripherals::PCR::regs()
385            .ecc_pd_ctrl()
386            .modify(|_, w| {
387                w.ecc_mem_force_pd().clear_bit();
388                w.ecc_mem_force_pu().set_bit();
389                w.ecc_mem_pd().clear_bit()
390            });
391        Self
392    }
393}
394
395impl Drop for EccMemoryPowerGuard {
396    fn drop(&mut self) {
397        #[cfg(soc_has_pcr)]
398        crate::peripherals::PCR::regs()
399            .ecc_pd_ctrl()
400            .modify(|_, w| {
401                w.ecc_mem_force_pd().clear_bit();
402                w.ecc_mem_force_pu().clear_bit();
403                w.ecc_mem_pd().set_bit()
404            });
405    }
406}
407
408/// ECC peripheral configuration.
409#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, BuilderLite)]
410#[cfg_attr(feature = "defmt", derive(defmt::Format))]
411pub struct Config {
412    /// Force enable register clock.
413    force_enable_reg_clock: bool,
414
415    /// Force enable memory clock.
416    #[cfg(ecc_has_memory_clock_gate)]
417    force_enable_mem_clock: bool,
418
419    /// Enable constant time operation and minimized power consumption variation for
420    /// point-multiplication operations.
421    #[cfg_attr(
422        esp32h2,
423        doc = r"
424
425Only available on chip revision 1.2 and above."
426    )]
427    #[cfg(ecc_supports_enhanced_security)]
428    enhanced_security: bool,
429}
430
431/// The length of the arguments do not match the length required by the curve.
432#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub struct KeyLengthMismatch;
434
435/// ECC operation error.
436#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub enum OperationError {
438    /// The length of the arguments do not match the length required by the curve.
439    ParameterLengthMismatch,
440
441    /// Point verification failed.
442    PointNotOnCurve,
443}
444
445/// Modulus base.
446#[cfg(ecc_has_modular_arithmetic)]
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub enum EccModBase {
449    /// The order of the curve.
450    OrderOfCurve = 0,
451
452    /// Prime modulus.
453    PrimeModulus = 1,
454}
455
456impl From<KeyLengthMismatch> for OperationError {
457    fn from(_: KeyLengthMismatch) -> Self {
458        OperationError::ParameterLengthMismatch
459    }
460}
461
462for_each_ecc_curve! {
463    (all $(( $id:literal, $name:ident, $bits:literal )),*) => {
464        /// Represents supported elliptic curves for cryptographic operations.
465        ///
466        /// The methods that represent operations require the `EccBackend` to be started before use.
467        #[derive(Clone, Copy, PartialEq, Eq, Debug)]
468        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
469        pub enum EllipticCurve {
470            $(
471                #[doc = concat!("The ", stringify!($name), " elliptic curve, a ", $bits, "-bit curve.")]
472                $name,
473            )*
474        }
475        impl EllipticCurve {
476            /// Returns the size of the elliptic curve in bytes.
477            pub const fn size(self) -> usize {
478                match self {
479                    $(
480                        EllipticCurve::$name => $bits / 8,
481                    )*
482                }
483            }
484        }
485    };
486}
487
488for_each_ecc_working_mode! {
489    (all $(($wm_id:literal, $op:tt)),*) => {
490        impl EllipticCurve {
491            fn size_check<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), KeyLengthMismatch> {
492                let bytes = self.size();
493
494                if params.iter().any(|p| p.len() != bytes) {
495                    return Err(KeyLengthMismatch);
496                }
497
498                Ok(())
499            }
500
501            $(
502                // Macro defined by `define_operations`
503                backend_operation!($op);
504            )*
505        }
506    };
507}
508
509impl<'d> Ecc<'d, Blocking> {
510    /// Create a new instance in [Blocking] mode.
511    pub fn new(ecc: ECC<'d>, config: Config) -> Self {
512        let this = Self {
513            _ecc: ecc,
514            phantom: PhantomData,
515            _memory_guard: EccMemoryPowerGuard::new(),
516            _guard: GenericPeripheralGuard::new(),
517        };
518
519        this.info().apply_config(&config);
520
521        this
522    }
523}
524
525impl crate::private::Sealed for Ecc<'_, Blocking> {}
526
527#[instability::unstable]
528impl crate::interrupt::InterruptConfigurable for Ecc<'_, Blocking> {
529    fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
530        self.set_interrupt_handler(handler);
531    }
532}
533
534struct Info {
535    regs: &'static pac::ecc::RegisterBlock,
536}
537
538impl Info {
539    fn reset(&self) {
540        self.regs.mult_conf().reset()
541    }
542
543    fn apply_config(&self, config: &Config) {
544        self.regs.mult_conf().modify(|_, w| {
545            w.clk_en().bit(config.force_enable_reg_clock);
546
547            #[cfg(ecc_has_memory_clock_gate)]
548            w.mem_clock_gate_force_on()
549                .bit(config.force_enable_mem_clock);
550
551            #[cfg(ecc_supports_enhanced_security)]
552            if !cfg!(esp32h2) || crate::soc::chip_revision_above(ChipRevision::from_combined(102)) {
553                w.security_mode().bit(config.enhanced_security);
554            }
555
556            w
557        });
558    }
559
560    fn check_point_verification_result(&self) -> Result<(), OperationError> {
561        if self
562            .regs
563            .mult_conf()
564            .read()
565            .verification_result()
566            .bit_is_set()
567        {
568            Ok(())
569        } else {
570            Err(OperationError::PointNotOnCurve)
571        }
572    }
573
574    #[inline]
575    fn write_mem(&self, mut word_ptr: *mut u32, data: &[u8]) {
576        // Note that at least the C2 requires writing this memory in words.
577
578        debug_assert!(data.len() <= MEM_BLOCK_SIZE);
579
580        #[cfg(ecc_zero_extend_writes)]
581        let end = word_ptr.wrapping_byte_add(MEM_BLOCK_SIZE);
582
583        let (chunks, remainder) = data.as_chunks::<4>();
584        debug_assert!(remainder.is_empty());
585
586        for word_bytes in chunks {
587            unsafe { word_ptr.write_volatile(u32::from_le_bytes(*word_bytes)) };
588            word_ptr = word_ptr.wrapping_add(1);
589        }
590
591        #[cfg(ecc_zero_extend_writes)]
592        while word_ptr < end {
593            unsafe { word_ptr.write_volatile(0) };
594            word_ptr = word_ptr.wrapping_add(1);
595        }
596    }
597
598    #[inline]
599    fn read_mem(&self, mut word_ptr: *const u32, out: &mut [u8]) {
600        let (chunks, _) = out.as_chunks_mut::<4>();
601        for word_bytes in chunks {
602            let word = unsafe { word_ptr.read_volatile() };
603            word_ptr = word_ptr.wrapping_add(1);
604            *word_bytes = word.to_le_bytes();
605        }
606    }
607
608    fn k_mem(&self) -> *mut u32 {
609        self.regs.k_mem(0).as_ptr()
610    }
611
612    fn px_mem(&self) -> *mut u32 {
613        self.regs.px_mem(0).as_ptr()
614    }
615
616    fn py_mem(&self) -> *mut u32 {
617        self.regs.py_mem(0).as_ptr()
618    }
619
620    fn qx_mem(&self) -> *mut u32 {
621        cfg_select! {
622            ecc_separate_jacobian_point_memory => self.regs.qx_mem(0).as_ptr(),
623            _ => self.regs.px_mem(0).as_ptr(),
624        }
625    }
626
627    fn qy_mem(&self) -> *mut u32 {
628        cfg_select! {
629            ecc_separate_jacobian_point_memory => self.regs.qy_mem(0).as_ptr(),
630            _ => self.regs.py_mem(0).as_ptr(),
631        }
632    }
633
634    fn qz_mem(&self) -> *mut u32 {
635        cfg_select! {
636            ecc_separate_jacobian_point_memory => self.regs.qz_mem(0).as_ptr(),
637            _ => self.regs.k_mem(0).as_ptr(),
638        }
639    }
640
641    fn read_point_result(&self, x: &mut [u8], y: &mut [u8]) {
642        self.read_mem(self.px_mem(), x);
643        self.read_mem(self.py_mem(), y);
644    }
645
646    fn read_jacobian_result(&self, qx: &mut [u8], qy: &mut [u8], qz: &mut [u8]) {
647        self.read_mem(self.qx_mem(), qx);
648        self.read_mem(self.qy_mem(), qy);
649        self.read_mem(self.qz_mem(), qz);
650    }
651
652    /// Clears all peripheral memory blocks
653    #[cfg(clear_crypto_secrets)]
654    fn clear_secrets(&self) {
655        self.zero_mem(self.k_mem());
656        self.zero_mem(self.px_mem());
657        self.zero_mem(self.py_mem());
658
659        #[cfg(ecc_separate_jacobian_point_memory)]
660        {
661            self.zero_mem(self.qx_mem());
662            self.zero_mem(self.qy_mem());
663            self.zero_mem(self.qz_mem());
664        }
665    }
666
667    #[cfg(clear_crypto_secrets)]
668    fn zero_mem(&self, mut word_ptr: *mut u32) {
669        for _ in 0..(MEM_BLOCK_SIZE / 4) {
670            unsafe { word_ptr.write_volatile(0) };
671            word_ptr = word_ptr.wrapping_add(1);
672        }
673    }
674
675    fn is_busy(&self) -> bool {
676        self.regs.mult_conf().read().start().bit_is_set()
677    }
678
679    fn start_operation(
680        &self,
681        mode: WorkMode,
682        curve: EllipticCurve,
683        #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
684    ) {
685        let curve_variant;
686        for_each_ecc_curve! {
687            (all $(($_id:tt, $name:ident, $_bits:tt)),*) => {
688                curve_variant = match curve {
689                    $(EllipticCurve::$name => KEY_LENGTH::$name,)*
690                }
691            };
692        };
693        self.regs.mult_conf().modify(|_, w| unsafe {
694            w.work_mode().bits(mode as u8);
695            w.key_length().variant(curve_variant);
696
697            #[cfg(ecc_has_modular_arithmetic)]
698            w.mod_base().bit(mod_base as u8 == 1);
699
700            w.start().set_bit()
701        });
702    }
703}
704
705// Broken into separate macro invocations per item, to make the "Expand macro" LSP output more
706// readable
707
708for_each_ecc_working_mode! {
709    (all $(( $id:literal, $mode:tt )),*) => {
710        #[derive(Clone, Copy)]
711        #[doc(hidden)]
712        /// Represents the operational modes for elliptic curve or modular arithmetic
713        /// computations.
714        pub enum WorkMode {
715            $(
716                $mode = $id,
717            )*
718        }
719    };
720}
721
722// Result type for each operation
723for_each_ecc_working_mode! {
724    (all $(( $id:literal, $mode:tt )),*) => {
725        $(
726            result_type!($mode);
727        )*
728    };
729}
730
731// The main driver implementation
732for_each_ecc_working_mode! {
733    (all $(( $id:literal, $mode:tt )),*) => {
734        impl<'d, Dm: DriverMode> Ecc<'d, Dm> {
735            fn info(&self) -> Info {
736                Info { regs: ECC::regs() }
737            }
738
739            fn run_operation<'op, O: EccOperation>(
740                &'op mut self,
741                curve: EllipticCurve,
742                #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
743            ) -> EccResultHandle<'op, O> {
744                self.info().start_operation(
745                    O::WORK_MODE,
746                    curve,
747                    #[cfg(ecc_has_modular_arithmetic)] mod_base,
748                );
749
750                // wait for interrupt
751                while self.info().is_busy() {}
752
753                EccResultHandle::new(curve, self)
754            }
755
756            /// Applies the given configuration to the ECC peripheral.
757            pub fn apply_config(&mut self, config: &Config) {
758                self.info().apply_config(config);
759            }
760
761            /// Resets the ECC peripheral.
762            pub fn reset(&mut self) {
763                self.info().reset()
764            }
765
766            /// Register an interrupt handler for the ECC peripheral.
767            ///
768            /// Note that this will replace any previously registered interrupt
769            /// handlers.
770            #[instability::unstable]
771            pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
772                for core in crate::system::Cpu::other() {
773                    crate::interrupt::disable(core, Interrupt::ECC);
774                }
775                crate::interrupt::bind_handler(Interrupt::ECC, handler);
776            }
777
778            $(
779                driver_method!($mode);
780            )*
781        }
782    };
783}
784
785/// Marks an ECC operation.
786pub trait EccOperation: Sealed {
787    /// Whether the operation verifies that the input point is on the curve.
788    const VERIFIES_POINT: bool;
789
790    /// Work mode
791    #[doc(hidden)]
792    const WORK_MODE: WorkMode;
793}
794
795/// Scalar result location.
796#[doc(hidden)]
797pub enum ScalarResultLocation {
798    /// The scalar value is stored in the `Px` memory location.
799    Px,
800    /// The scalar value is stored in the `Py` memory location.
801    Py,
802    /// The scalar value is stored in the `k` memory location.
803    K,
804}
805
806/// Marks operations that return a scalar value.
807pub trait OperationReturnsScalar: EccOperation {
808    /// Where the scalar value is stored.
809    #[doc(hidden)]
810    const LOCATION: ScalarResultLocation;
811}
812
813/// Marks operations that return a point in affine format.
814pub trait OperationReturnsAffinePoint: EccOperation {}
815
816/// Marks operations that return a point in Jacobian format.
817pub trait OperationReturnsJacobianPoint: EccOperation {}
818
819/// Marks operations that verify that the input point is on the curve.
820pub trait OperationVerifiesPoint: EccOperation {}
821
822/// The result of an ECC operation.
823///
824/// This struct can be used to read the result of an ECC operation. The methods which can be used
825/// depend on the operation. An operation can compute multiple values, such as an affine point and
826/// a Jacobian point at the same time.
827#[must_use]
828pub struct EccResultHandle<'op, O>
829where
830    O: EccOperation,
831{
832    curve: EllipticCurve,
833    info: Info,
834    _marker: PhantomData<(&'op mut (), O)>,
835}
836
837impl<'op, O> EccResultHandle<'op, O>
838where
839    O: EccOperation,
840{
841    fn new<'d, Dm: DriverMode>(curve: EllipticCurve, driver: &'op mut Ecc<'d, Dm>) -> Self {
842        Self {
843            curve,
844            info: driver.info(),
845            _marker: PhantomData,
846        }
847    }
848
849    fn run_checks<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
850        self.curve.size_check(params)?;
851        if O::VERIFIES_POINT {
852            self.info.check_point_verification_result()?;
853        }
854        Ok(())
855    }
856
857    /// Returns whether the operation was successful.
858    ///
859    /// For operations that only perform point verification, this method returns whether the point
860    /// is on the curve. For operations that do not perform point verification, this method always
861    /// returns true.
862    pub fn success(&self) -> bool {
863        if O::VERIFIES_POINT {
864            self.info.check_point_verification_result().is_ok()
865        } else {
866            true
867        }
868    }
869
870    /// Retrieve the scalar result of the operation.
871    ///
872    /// ## Errors
873    ///
874    /// Returns an error if point verification failed, or if `out` is not the correct size.
875    pub fn read_scalar_result(&self, out: &mut [u8]) -> Result<(), OperationError>
876    where
877        O: OperationReturnsScalar,
878    {
879        self.run_checks([out])?;
880
881        match O::LOCATION {
882            ScalarResultLocation::Px => self.info.read_mem(self.info.px_mem(), out),
883            ScalarResultLocation::Py => self.info.read_mem(self.info.py_mem(), out),
884            ScalarResultLocation::K => self.info.read_mem(self.info.k_mem(), out),
885        }
886
887        Ok(())
888    }
889
890    /// Retrieve the affine point result of the operation.
891    ///
892    /// ## Errors
893    ///
894    /// Returns an error if point verification failed, or if `x` or `y` are not the correct size.
895    pub fn read_affine_point_result(&self, x: &mut [u8], y: &mut [u8]) -> Result<(), OperationError>
896    where
897        O: OperationReturnsAffinePoint,
898    {
899        self.run_checks([x, y])?;
900        self.info.read_point_result(x, y);
901        Ok(())
902    }
903
904    /// Retrieve the Jacobian point result of the operation.
905    ///
906    /// ## Errors
907    ///
908    /// Returns an error if point verification failed, or if `x`, `y`, or `z` are not the correct
909    /// size.
910    pub fn read_jacobian_point_result(
911        &self,
912        x: &mut [u8],
913        y: &mut [u8],
914        z: &mut [u8],
915    ) -> Result<(), OperationError>
916    where
917        O: OperationReturnsJacobianPoint,
918    {
919        self.run_checks([x, y, z])?;
920        self.info.read_jacobian_result(x, y, z);
921        Ok(())
922    }
923}
924
925struct EccWorkItem {
926    curve: EllipticCurve,
927    operation: WorkMode,
928    cancelled: bool,
929    #[cfg(ecc_has_modular_arithmetic)]
930    mod_base: EccModBase,
931    inputs: MemoryPointers,
932    point_verification_result: bool,
933    outputs: MemoryPointers,
934}
935
936#[derive(Default)]
937struct MemoryPointers {
938    // All of these pointers point to slices with curve-appropriate lengths.
939    k: Option<NonNull<u8>>,
940    px: Option<NonNull<u8>>,
941    py: Option<NonNull<u8>>,
942    #[cfg(ecc_separate_jacobian_point_memory)]
943    qx: Option<NonNull<u8>>,
944    #[cfg(ecc_separate_jacobian_point_memory)]
945    qy: Option<NonNull<u8>>,
946    #[cfg(ecc_separate_jacobian_point_memory)]
947    qz: Option<NonNull<u8>>,
948}
949
950impl MemoryPointers {
951    fn set_scalar(&mut self, location: ScalarResultLocation, ptr: NonNull<[u8]>) {
952        match location {
953            ScalarResultLocation::Px => self.set_px(ptr),
954            ScalarResultLocation::Py => self.set_py(ptr),
955            ScalarResultLocation::K => self.set_k(ptr),
956        }
957    }
958
959    fn set_k(&mut self, ptr: NonNull<[u8]>) {
960        self.k = Some(ptr.cast());
961    }
962
963    fn set_px(&mut self, ptr: NonNull<[u8]>) {
964        self.px = Some(ptr.cast());
965    }
966
967    fn set_py(&mut self, ptr: NonNull<[u8]>) {
968        self.py = Some(ptr.cast());
969    }
970
971    fn set_qx(&mut self, ptr: NonNull<[u8]>) {
972        cfg_select! {
973            ecc_separate_jacobian_point_memory => {
974                self.qx = Some(ptr.cast());
975            }
976            _ => {
977                self.px = Some(ptr.cast());
978            }
979        }
980    }
981
982    fn set_qy(&mut self, ptr: NonNull<[u8]>) {
983        cfg_select! {
984            ecc_separate_jacobian_point_memory => {
985                self.qy = Some(ptr.cast());
986            }
987            _ => {
988                self.py = Some(ptr.cast());
989            }
990        }
991    }
992
993    fn set_qz(&mut self, ptr: NonNull<[u8]>) {
994        cfg_select! {
995            ecc_separate_jacobian_point_memory => {
996                self.qz = Some(ptr.cast());
997            }
998            _ => {
999                self.k = Some(ptr.cast());
1000            }
1001        }
1002    }
1003}
1004
1005// Safety: MemoryPointers is safe to share between threads, in the context of a WorkQueue. The
1006// WorkQueue ensures that only a single location can access the data. All the internals, except
1007// for the pointers, are Sync. The pointers are safe to share because they point at data that the
1008// ECC driver ensures can be accessed safely and soundly.
1009unsafe impl Sync for MemoryPointers {}
1010// Safety: we will not hold on to the pointers when the work item leaves the queue.
1011unsafe impl Send for MemoryPointers {}
1012
1013static ECC_WORK_QUEUE: WorkQueue<EccWorkItem> = WorkQueue::new();
1014
1015const ECC_VTABLE: VTable<EccWorkItem> = VTable {
1016    post: |driver, item| {
1017        let driver = unsafe { EccBackend::from_raw(driver) };
1018
1019        // Ensure driver is initialized
1020        if let DriverState::Uninitialized(ecc) = &driver.driver {
1021            let mut ecc = Ecc::new(unsafe { ecc.clone_unchecked() }, driver.config);
1022            ecc.set_interrupt_handler(ecc_work_queue_handler);
1023            driver.driver = DriverState::Initialized(ecc);
1024        };
1025
1026        Some(driver.process(item))
1027    },
1028    poll: |driver, item| {
1029        let driver = unsafe { EccBackend::from_raw(driver) };
1030        driver.poll(item)
1031    },
1032    cancel: |driver, item| {
1033        let driver = unsafe { EccBackend::from_raw(driver) };
1034        driver.cancel(item);
1035    },
1036    stop: |driver| {
1037        let driver = unsafe { EccBackend::from_raw(driver) };
1038        driver.deinitialize()
1039    },
1040};
1041
1042enum DriverState<'d> {
1043    Uninitialized(ECC<'d>),
1044    Initialized(Ecc<'d, Blocking>),
1045}
1046
1047/// ECC processing backend.
1048///
1049/// This struct enables shared access to the device's ECC hardware using a work queue.
1050pub struct EccBackend<'d> {
1051    driver: DriverState<'d>,
1052    config: Config,
1053}
1054
1055impl<'d> EccBackend<'d> {
1056    /// Creates a new ECC backend.
1057    ///
1058    /// The backend needs to be [`start`][Self::start]ed before it can execute ECC operations.
1059    pub fn new(ecc: ECC<'d>, config: Config) -> Self {
1060        Self {
1061            driver: DriverState::Uninitialized(ecc),
1062            config,
1063        }
1064    }
1065
1066    /// Registers the ECC driver to process ECC operations.
1067    ///
1068    /// The driver stops operating when the returned object is dropped.
1069    pub fn start(&mut self) -> EccWorkQueueDriver<'_, 'd> {
1070        EccWorkQueueDriver {
1071            inner: WorkQueueDriver::new(self, ECC_VTABLE, &ECC_WORK_QUEUE),
1072        }
1073    }
1074
1075    // WorkQueue callbacks. They may run in any context.
1076
1077    unsafe fn from_raw<'any>(ptr: NonNull<()>) -> &'any mut Self {
1078        unsafe { ptr.cast::<EccBackend<'_>>().as_mut() }
1079    }
1080
1081    fn process(&mut self, item: &mut EccWorkItem) -> Poll {
1082        let DriverState::Initialized(driver) = &mut self.driver else {
1083            unreachable!()
1084        };
1085
1086        let bytes = item.curve.size();
1087
1088        macro_rules! set_input {
1089            ($input:ident, $input_mem:ident) => {
1090                if let Some($input) = item.inputs.$input {
1091                    driver.info().write_mem(driver.info().$input_mem(), unsafe {
1092                        core::slice::from_raw_parts($input.as_ptr(), bytes)
1093                    });
1094                }
1095            };
1096        }
1097
1098        set_input!(k, k_mem);
1099        set_input!(px, px_mem);
1100        set_input!(py, py_mem);
1101
1102        #[cfg(ecc_separate_jacobian_point_memory)]
1103        {
1104            set_input!(qx, qx_mem);
1105            set_input!(qy, qy_mem);
1106            set_input!(qz, qz_mem);
1107        }
1108
1109        driver.info().start_operation(
1110            item.operation,
1111            item.curve,
1112            #[cfg(ecc_has_modular_arithmetic)]
1113            item.mod_base,
1114        );
1115        Poll::Pending(false)
1116    }
1117
1118    fn poll(&mut self, item: &mut EccWorkItem) -> Poll {
1119        let DriverState::Initialized(driver) = &mut self.driver else {
1120            unreachable!()
1121        };
1122
1123        if driver.info().is_busy() {
1124            return Poll::Pending(false);
1125        }
1126        if item.cancelled {
1127            return Poll::Ready(Status::Cancelled);
1128        }
1129
1130        let bytes = item.curve.size();
1131
1132        macro_rules! read_output {
1133            ($output:ident, $output_mem:ident) => {
1134                if let Some($output) = item.outputs.$output {
1135                    driver.info().read_mem(driver.info().$output_mem(), unsafe {
1136                        core::slice::from_raw_parts_mut($output.as_ptr(), bytes)
1137                    });
1138                }
1139            };
1140        }
1141
1142        read_output!(k, k_mem);
1143        read_output!(px, px_mem);
1144        read_output!(py, py_mem);
1145
1146        #[cfg(ecc_separate_jacobian_point_memory)]
1147        {
1148            read_output!(qx, qx_mem);
1149            read_output!(qy, qy_mem);
1150            read_output!(qz, qz_mem);
1151        }
1152
1153        item.point_verification_result = driver.info().check_point_verification_result().is_ok();
1154
1155        #[cfg(clear_crypto_secrets)]
1156        driver.info().clear_secrets();
1157
1158        Poll::Ready(Status::Completed)
1159    }
1160
1161    fn cancel(&mut self, item: &mut EccWorkItem) {
1162        let DriverState::Initialized(driver) = &mut self.driver else {
1163            unreachable!()
1164        };
1165        driver.reset();
1166
1167        // The operands may have already been written to the peripheral.
1168        #[cfg(clear_crypto_secrets)]
1169        driver.info().clear_secrets();
1170
1171        item.cancelled = true;
1172    }
1173
1174    fn deinitialize(&mut self) {
1175        if let DriverState::Initialized(ref ecc) = self.driver {
1176            self.driver = DriverState::Uninitialized(unsafe { ecc._ecc.clone_unchecked() });
1177        }
1178    }
1179}
1180
1181/// An active work queue driver.
1182///
1183/// This object must be kept around, otherwise ECC operations will never complete.
1184pub struct EccWorkQueueDriver<'t, 'd> {
1185    inner: WorkQueueDriver<'t, EccBackend<'d>, EccWorkItem>,
1186}
1187
1188impl<'t, 'd> EccWorkQueueDriver<'t, 'd> {
1189    /// Finishes processing the current work queue item, then stops the driver.
1190    pub fn stop(self) -> impl Future<Output = ()> {
1191        self.inner.stop()
1192    }
1193}
1194
1195#[crate::ram]
1196#[crate::handler]
1197fn ecc_work_queue_handler() {
1198    if !ECC_WORK_QUEUE.process() {
1199        // The queue may indicate that it needs to be polled again. In this case, we do not clear
1200        // the interrupt bit, which causes the interrupt to be re-handled.
1201        cfg_select! {
1202            any(esp32c5, esp32c61) => {
1203                let reg = ECC::regs().int_clr();
1204            }
1205            _ => {
1206                let reg = ECC::regs().mult_int_clr();
1207            }
1208        }
1209        reg.write(|w| w.calc_done().clear_bit_by_one());
1210    }
1211}
1212
1213/// An ECC operation that can be enqueued on the work queue.
1214pub struct EccBackendOperation<'op, O: EccOperation> {
1215    frontend: WorkQueueFrontend<EccWorkItem>,
1216    _marker: PhantomData<(&'op mut (), O)>,
1217}
1218
1219impl<'op, O: EccOperation> EccBackendOperation<'op, O> {
1220    fn new(work_item: EccWorkItem) -> Self {
1221        Self {
1222            frontend: WorkQueueFrontend::new(work_item),
1223            _marker: PhantomData,
1224        }
1225    }
1226
1227    /// Designate a buffer for the scalar result of the operation.
1228    ///
1229    /// Once the operation is processed, the result can be retrieved from the designated buffer.
1230    ///
1231    /// ## Errors
1232    ///
1233    /// Returns an error if `out` is not the correct size.
1234    pub fn with_scalar_result(mut self, out: &'op mut [u8]) -> Result<Self, KeyLengthMismatch>
1235    where
1236        O: OperationReturnsScalar,
1237    {
1238        self.frontend.data().curve.size_check([out])?;
1239
1240        self.frontend
1241            .data_mut()
1242            .outputs
1243            .set_scalar(O::LOCATION, NonNull::from(out));
1244
1245        Ok(self)
1246    }
1247
1248    /// Designate buffers for the affine point result of the operation.
1249    ///
1250    /// Once the operation is processed, the result can be retrieved from the designated buffers.
1251    ///
1252    /// ## Errors
1253    ///
1254    /// Returns an error if `x` or `y` are not the correct size.
1255    pub fn with_affine_point_result(
1256        mut self,
1257        px: &'op mut [u8],
1258        py: &'op mut [u8],
1259    ) -> Result<Self, KeyLengthMismatch>
1260    where
1261        O: OperationReturnsAffinePoint,
1262    {
1263        self.frontend.data().curve.size_check([px, py])?;
1264
1265        self.frontend.data_mut().outputs.set_px(NonNull::from(px));
1266        self.frontend.data_mut().outputs.set_py(NonNull::from(py));
1267
1268        Ok(self)
1269    }
1270
1271    /// Designate buffers for the Jacobian point result of the operation.
1272    ///
1273    /// Once the operation is processed, the result can be retrieved from the designated buffers.
1274    ///
1275    /// ## Errors
1276    ///
1277    /// Returns an error if `x`, `y`, or `z` are not the correct size.
1278    pub fn with_jacobian_point_result(
1279        mut self,
1280        qx: &'op mut [u8],
1281        qy: &'op mut [u8],
1282        qz: &'op mut [u8],
1283    ) -> Result<Self, KeyLengthMismatch>
1284    where
1285        O: OperationReturnsJacobianPoint,
1286    {
1287        self.frontend.data().curve.size_check([qx, qy, qz])?;
1288
1289        self.frontend.data_mut().outputs.set_qx(NonNull::from(qx));
1290        self.frontend.data_mut().outputs.set_qy(NonNull::from(qy));
1291        self.frontend.data_mut().outputs.set_qz(NonNull::from(qz));
1292
1293        Ok(self)
1294    }
1295
1296    /// Returns `true` if the input point is on the curve.
1297    ///
1298    /// The operation must be processed before this method returns a meaningful value.
1299    pub fn point_on_curve(&self) -> bool
1300    where
1301        O: OperationVerifiesPoint,
1302    {
1303        self.frontend.data().point_verification_result
1304    }
1305
1306    /// Starts processing the operation.
1307    ///
1308    /// The returned [`EccHandle`] must be polled to completion before the operation is considered
1309    /// complete.
1310    pub fn process(&mut self) -> EccHandle<'_> {
1311        EccHandle(self.frontend.post(&ECC_WORK_QUEUE))
1312    }
1313}
1314
1315/// A handle for an in-progress operation.
1316#[must_use]
1317pub struct EccHandle<'t>(Handle<'t, EccWorkItem>);
1318
1319impl EccHandle<'_> {
1320    /// Polls the status of the work item.
1321    ///
1322    /// This function returns `true` if the item has been processed.
1323    #[inline]
1324    pub fn poll(&mut self) -> bool {
1325        self.0.poll()
1326    }
1327
1328    /// Polls the work item to completion, by busy-looping.
1329    ///
1330    /// This function returns immediately if `poll` returns `true`.
1331    #[inline]
1332    pub fn wait_blocking(self) -> Status {
1333        self.0.wait_blocking()
1334    }
1335
1336    /// Waits until the work item is completed.
1337    #[inline]
1338    pub fn wait(&mut self) -> impl Future<Output = Status> {
1339        self.0.wait()
1340    }
1341
1342    /// Cancels the work item and asynchronously waits until it is removed from the work queue.
1343    #[inline]
1344    pub fn cancel(&mut self) -> impl Future<Output = ()> {
1345        self.0.cancel()
1346    }
1347}