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