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        for word_bytes in out.chunks_exact_mut(4) {
601            let word = unsafe { word_ptr.read_volatile() };
602            word_ptr = word_ptr.wrapping_add(1);
603            word_bytes.copy_from_slice(&word.to_le_bytes());
604        }
605    }
606
607    fn k_mem(&self) -> *mut u32 {
608        self.regs.k_mem(0).as_ptr()
609    }
610
611    fn px_mem(&self) -> *mut u32 {
612        self.regs.px_mem(0).as_ptr()
613    }
614
615    fn py_mem(&self) -> *mut u32 {
616        self.regs.py_mem(0).as_ptr()
617    }
618
619    fn qx_mem(&self) -> *mut u32 {
620        cfg_if::cfg_if! {
621            if #[cfg(ecc_separate_jacobian_point_memory)] {
622                self.regs.qx_mem(0).as_ptr()
623            } else {
624                self.regs.px_mem(0).as_ptr()
625            }
626        }
627    }
628
629    fn qy_mem(&self) -> *mut u32 {
630        cfg_if::cfg_if! {
631            if #[cfg(ecc_separate_jacobian_point_memory)] {
632                self.regs.qy_mem(0).as_ptr()
633            } else {
634                self.regs.py_mem(0).as_ptr()
635            }
636        }
637    }
638
639    fn qz_mem(&self) -> *mut u32 {
640        cfg_if::cfg_if! {
641            if #[cfg(ecc_separate_jacobian_point_memory)] {
642                self.regs.qz_mem(0).as_ptr()
643            } else {
644                self.regs.k_mem(0).as_ptr()
645            }
646        }
647    }
648
649    fn read_point_result(&self, x: &mut [u8], y: &mut [u8]) {
650        self.read_mem(self.px_mem(), x);
651        self.read_mem(self.py_mem(), y);
652    }
653
654    fn read_jacobian_result(&self, qx: &mut [u8], qy: &mut [u8], qz: &mut [u8]) {
655        self.read_mem(self.qx_mem(), qx);
656        self.read_mem(self.qy_mem(), qy);
657        self.read_mem(self.qz_mem(), qz);
658    }
659
660    fn is_busy(&self) -> bool {
661        self.regs.mult_conf().read().start().bit_is_set()
662    }
663
664    fn start_operation(
665        &self,
666        mode: WorkMode,
667        curve: EllipticCurve,
668        #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
669    ) {
670        let curve_variant;
671        for_each_ecc_curve! {
672            (all $(($_id:tt, $name:ident, $_bits:tt)),*) => {
673                curve_variant = match curve {
674                    $(EllipticCurve::$name => KEY_LENGTH::$name,)*
675                }
676            };
677        };
678        self.regs.mult_conf().modify(|_, w| unsafe {
679            w.work_mode().bits(mode as u8);
680            w.key_length().variant(curve_variant);
681
682            #[cfg(ecc_has_modular_arithmetic)]
683            w.mod_base().bit(mod_base as u8 == 1);
684
685            w.start().set_bit()
686        });
687    }
688}
689
690// Broken into separate macro invocations per item, to make the "Expand macro" LSP output more
691// readable
692
693for_each_ecc_working_mode! {
694    (all $(( $id:literal, $mode:tt )),*) => {
695        #[derive(Clone, Copy)]
696        #[doc(hidden)]
697        /// Represents the operational modes for elliptic curve or modular arithmetic
698        /// computations.
699        pub enum WorkMode {
700            $(
701                $mode = $id,
702            )*
703        }
704    };
705}
706
707// Result type for each operation
708for_each_ecc_working_mode! {
709    (all $(( $id:literal, $mode:tt )),*) => {
710        $(
711            result_type!($mode);
712        )*
713    };
714}
715
716// The main driver implementation
717for_each_ecc_working_mode! {
718    (all $(( $id:literal, $mode:tt )),*) => {
719        impl<'d, Dm: DriverMode> Ecc<'d, Dm> {
720            fn info(&self) -> Info {
721                Info { regs: ECC::regs() }
722            }
723
724            fn run_operation<'op, O: EccOperation>(
725                &'op mut self,
726                curve: EllipticCurve,
727                #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
728            ) -> EccResultHandle<'op, O> {
729                self.info().start_operation(
730                    O::WORK_MODE,
731                    curve,
732                    #[cfg(ecc_has_modular_arithmetic)] mod_base,
733                );
734
735                // wait for interrupt
736                while self.info().is_busy() {}
737
738                EccResultHandle::new(curve, self)
739            }
740
741            /// Applies the given configuration to the ECC peripheral.
742            pub fn apply_config(&mut self, config: &Config) {
743                self.info().apply_config(config);
744            }
745
746            /// Resets the ECC peripheral.
747            pub fn reset(&mut self) {
748                self.info().reset()
749            }
750
751            /// Register an interrupt handler for the ECC peripheral.
752            ///
753            /// Note that this will replace any previously registered interrupt
754            /// handlers.
755            #[instability::unstable]
756            pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
757                for core in crate::system::Cpu::other() {
758                    crate::interrupt::disable(core, Interrupt::ECC);
759                }
760                crate::interrupt::bind_handler(Interrupt::ECC, handler);
761            }
762
763            $(
764                driver_method!($mode);
765            )*
766        }
767    };
768}
769
770/// Marks an ECC operation.
771pub trait EccOperation: Sealed {
772    /// Whether the operation verifies that the input point is on the curve.
773    const VERIFIES_POINT: bool;
774
775    /// Work mode
776    #[doc(hidden)]
777    const WORK_MODE: WorkMode;
778}
779
780/// Scalar result location.
781#[doc(hidden)]
782pub enum ScalarResultLocation {
783    /// The scalar value is stored in the `Px` memory location.
784    Px,
785    /// The scalar value is stored in the `Py` memory location.
786    Py,
787    /// The scalar value is stored in the `k` memory location.
788    K,
789}
790
791/// Marks operations that return a scalar value.
792pub trait OperationReturnsScalar: EccOperation {
793    /// Where the scalar value is stored.
794    #[doc(hidden)]
795    const LOCATION: ScalarResultLocation;
796}
797
798/// Marks operations that return a point in affine format.
799pub trait OperationReturnsAffinePoint: EccOperation {}
800
801/// Marks operations that return a point in Jacobian format.
802pub trait OperationReturnsJacobianPoint: EccOperation {}
803
804/// Marks operations that verify that the input point is on the curve.
805pub trait OperationVerifiesPoint: EccOperation {}
806
807/// The result of an ECC operation.
808///
809/// This struct can be used to read the result of an ECC operation. The methods which can be used
810/// depend on the operation. An operation can compute multiple values, such as an affine point and
811/// a Jacobian point at the same time.
812#[must_use]
813pub struct EccResultHandle<'op, O>
814where
815    O: EccOperation,
816{
817    curve: EllipticCurve,
818    info: Info,
819    _marker: PhantomData<(&'op mut (), O)>,
820}
821
822impl<'op, O> EccResultHandle<'op, O>
823where
824    O: EccOperation,
825{
826    fn new<'d, Dm: DriverMode>(curve: EllipticCurve, driver: &'op mut Ecc<'d, Dm>) -> Self {
827        Self {
828            curve,
829            info: driver.info(),
830            _marker: PhantomData,
831        }
832    }
833
834    fn run_checks<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
835        self.curve.size_check(params)?;
836        if O::VERIFIES_POINT {
837            self.info.check_point_verification_result()?;
838        }
839        Ok(())
840    }
841
842    /// Returns whether the operation was successful.
843    ///
844    /// For operations that only perform point verification, this method returns whether the point
845    /// is on the curve. For operations that do not perform point verification, this method always
846    /// returns true.
847    pub fn success(&self) -> bool {
848        if O::VERIFIES_POINT {
849            self.info.check_point_verification_result().is_ok()
850        } else {
851            true
852        }
853    }
854
855    /// Retrieve the scalar result of the operation.
856    ///
857    /// ## Errors
858    ///
859    /// Returns an error if point verification failed, or if `out` is not the correct size.
860    pub fn read_scalar_result(&self, out: &mut [u8]) -> Result<(), OperationError>
861    where
862        O: OperationReturnsScalar,
863    {
864        self.run_checks([out])?;
865
866        match O::LOCATION {
867            ScalarResultLocation::Px => self.info.read_mem(self.info.px_mem(), out),
868            ScalarResultLocation::Py => self.info.read_mem(self.info.py_mem(), out),
869            ScalarResultLocation::K => self.info.read_mem(self.info.k_mem(), out),
870        }
871
872        Ok(())
873    }
874
875    /// Retrieve the affine point result of the operation.
876    ///
877    /// ## Errors
878    ///
879    /// Returns an error if point verification failed, or if `x` or `y` are not the correct size.
880    pub fn read_affine_point_result(&self, x: &mut [u8], y: &mut [u8]) -> Result<(), OperationError>
881    where
882        O: OperationReturnsAffinePoint,
883    {
884        self.run_checks([x, y])?;
885        self.info.read_point_result(x, y);
886        Ok(())
887    }
888
889    /// Retrieve the Jacobian point result of the operation.
890    ///
891    /// ## Errors
892    ///
893    /// Returns an error if point verification failed, or if `x`, `y`, or `z` are not the correct
894    /// size.
895    pub fn read_jacobian_point_result(
896        &self,
897        x: &mut [u8],
898        y: &mut [u8],
899        z: &mut [u8],
900    ) -> Result<(), OperationError>
901    where
902        O: OperationReturnsJacobianPoint,
903    {
904        self.run_checks([x, y, z])?;
905        self.info.read_jacobian_result(x, y, z);
906        Ok(())
907    }
908}
909
910struct EccWorkItem {
911    curve: EllipticCurve,
912    operation: WorkMode,
913    cancelled: bool,
914    #[cfg(ecc_has_modular_arithmetic)]
915    mod_base: EccModBase,
916    inputs: MemoryPointers,
917    point_verification_result: bool,
918    outputs: MemoryPointers,
919}
920
921#[derive(Default)]
922struct MemoryPointers {
923    // All of these pointers point to slices with curve-appropriate lengths.
924    k: Option<NonNull<u8>>,
925    px: Option<NonNull<u8>>,
926    py: Option<NonNull<u8>>,
927    #[cfg(ecc_separate_jacobian_point_memory)]
928    qx: Option<NonNull<u8>>,
929    #[cfg(ecc_separate_jacobian_point_memory)]
930    qy: Option<NonNull<u8>>,
931    #[cfg(ecc_separate_jacobian_point_memory)]
932    qz: Option<NonNull<u8>>,
933}
934
935impl MemoryPointers {
936    fn set_scalar(&mut self, location: ScalarResultLocation, ptr: NonNull<[u8]>) {
937        match location {
938            ScalarResultLocation::Px => self.set_px(ptr),
939            ScalarResultLocation::Py => self.set_py(ptr),
940            ScalarResultLocation::K => self.set_k(ptr),
941        }
942    }
943
944    fn set_k(&mut self, ptr: NonNull<[u8]>) {
945        self.k = Some(ptr.cast());
946    }
947
948    fn set_px(&mut self, ptr: NonNull<[u8]>) {
949        self.px = Some(ptr.cast());
950    }
951
952    fn set_py(&mut self, ptr: NonNull<[u8]>) {
953        self.py = Some(ptr.cast());
954    }
955
956    fn set_qx(&mut self, ptr: NonNull<[u8]>) {
957        cfg_if::cfg_if! {
958            if #[cfg(ecc_separate_jacobian_point_memory)] {
959                self.qx = Some(ptr.cast());
960            } else {
961                self.px = Some(ptr.cast());
962            }
963        }
964    }
965
966    fn set_qy(&mut self, ptr: NonNull<[u8]>) {
967        cfg_if::cfg_if! {
968            if #[cfg(ecc_separate_jacobian_point_memory)] {
969                self.qy = Some(ptr.cast());
970            } else {
971                self.py = Some(ptr.cast());
972            }
973        }
974    }
975
976    fn set_qz(&mut self, ptr: NonNull<[u8]>) {
977        cfg_if::cfg_if! {
978            if #[cfg(ecc_separate_jacobian_point_memory)] {
979                self.qz = Some(ptr.cast());
980            } else {
981                self.k = Some(ptr.cast());
982            }
983        }
984    }
985}
986
987// Safety: MemoryPointers is safe to share between threads, in the context of a WorkQueue. The
988// WorkQueue ensures that only a single location can access the data. All the internals, except
989// for the pointers, are Sync. The pointers are safe to share because they point at data that the
990// ECC driver ensures can be accessed safely and soundly.
991unsafe impl Sync for MemoryPointers {}
992// Safety: we will not hold on to the pointers when the work item leaves the queue.
993unsafe impl Send for MemoryPointers {}
994
995static ECC_WORK_QUEUE: WorkQueue<EccWorkItem> = WorkQueue::new();
996
997const ECC_VTABLE: VTable<EccWorkItem> = VTable {
998    post: |driver, item| {
999        let driver = unsafe { EccBackend::from_raw(driver) };
1000
1001        // Ensure driver is initialized
1002        if let DriverState::Uninitialized(ecc) = &driver.driver {
1003            let mut ecc = Ecc::new(unsafe { ecc.clone_unchecked() }, driver.config);
1004            ecc.set_interrupt_handler(ecc_work_queue_handler);
1005            driver.driver = DriverState::Initialized(ecc);
1006        };
1007
1008        Some(driver.process(item))
1009    },
1010    poll: |driver, item| {
1011        let driver = unsafe { EccBackend::from_raw(driver) };
1012        driver.poll(item)
1013    },
1014    cancel: |driver, item| {
1015        let driver = unsafe { EccBackend::from_raw(driver) };
1016        driver.cancel(item);
1017    },
1018    stop: |driver| {
1019        let driver = unsafe { EccBackend::from_raw(driver) };
1020        driver.deinitialize()
1021    },
1022};
1023
1024enum DriverState<'d> {
1025    Uninitialized(ECC<'d>),
1026    Initialized(Ecc<'d, Blocking>),
1027}
1028
1029/// ECC processing backend.
1030///
1031/// This struct enables shared access to the device's ECC hardware using a work queue.
1032pub struct EccBackend<'d> {
1033    driver: DriverState<'d>,
1034    config: Config,
1035}
1036
1037impl<'d> EccBackend<'d> {
1038    /// Creates a new ECC backend.
1039    ///
1040    /// The backend needs to be [`start`][Self::start]ed before it can execute ECC operations.
1041    pub fn new(ecc: ECC<'d>, config: Config) -> Self {
1042        Self {
1043            driver: DriverState::Uninitialized(ecc),
1044            config,
1045        }
1046    }
1047
1048    /// Registers the ECC driver to process ECC operations.
1049    ///
1050    /// The driver stops operating when the returned object is dropped.
1051    pub fn start(&mut self) -> EccWorkQueueDriver<'_, 'd> {
1052        EccWorkQueueDriver {
1053            inner: WorkQueueDriver::new(self, ECC_VTABLE, &ECC_WORK_QUEUE),
1054        }
1055    }
1056
1057    // WorkQueue callbacks. They may run in any context.
1058
1059    unsafe fn from_raw<'any>(ptr: NonNull<()>) -> &'any mut Self {
1060        unsafe { ptr.cast::<EccBackend<'_>>().as_mut() }
1061    }
1062
1063    fn process(&mut self, item: &mut EccWorkItem) -> Poll {
1064        let DriverState::Initialized(driver) = &mut self.driver else {
1065            unreachable!()
1066        };
1067
1068        let bytes = item.curve.size();
1069
1070        macro_rules! set_input {
1071            ($input:ident, $input_mem:ident) => {
1072                if let Some($input) = item.inputs.$input {
1073                    driver.info().write_mem(driver.info().$input_mem(), unsafe {
1074                        core::slice::from_raw_parts($input.as_ptr(), bytes)
1075                    });
1076                }
1077            };
1078        }
1079
1080        set_input!(k, k_mem);
1081        set_input!(px, px_mem);
1082        set_input!(py, py_mem);
1083
1084        #[cfg(ecc_separate_jacobian_point_memory)]
1085        {
1086            set_input!(qx, qx_mem);
1087            set_input!(qy, qy_mem);
1088            set_input!(qz, qz_mem);
1089        }
1090
1091        driver.info().start_operation(
1092            item.operation,
1093            item.curve,
1094            #[cfg(ecc_has_modular_arithmetic)]
1095            item.mod_base,
1096        );
1097        Poll::Pending(false)
1098    }
1099
1100    fn poll(&mut self, item: &mut EccWorkItem) -> Poll {
1101        let DriverState::Initialized(driver) = &mut self.driver else {
1102            unreachable!()
1103        };
1104
1105        if driver.info().is_busy() {
1106            return Poll::Pending(false);
1107        }
1108        if item.cancelled {
1109            return Poll::Ready(Status::Cancelled);
1110        }
1111
1112        let bytes = item.curve.size();
1113
1114        macro_rules! read_output {
1115            ($output:ident, $output_mem:ident) => {
1116                if let Some($output) = item.outputs.$output {
1117                    driver.info().read_mem(driver.info().$output_mem(), unsafe {
1118                        core::slice::from_raw_parts_mut($output.as_ptr(), bytes)
1119                    });
1120                }
1121            };
1122        }
1123
1124        read_output!(k, k_mem);
1125        read_output!(px, px_mem);
1126        read_output!(py, py_mem);
1127
1128        #[cfg(ecc_separate_jacobian_point_memory)]
1129        {
1130            read_output!(qx, qx_mem);
1131            read_output!(qy, qy_mem);
1132            read_output!(qz, qz_mem);
1133        }
1134
1135        item.point_verification_result = driver.info().check_point_verification_result().is_ok();
1136
1137        Poll::Ready(Status::Completed)
1138    }
1139
1140    fn cancel(&mut self, item: &mut EccWorkItem) {
1141        let DriverState::Initialized(driver) = &mut self.driver else {
1142            unreachable!()
1143        };
1144        driver.reset();
1145        item.cancelled = true;
1146    }
1147
1148    fn deinitialize(&mut self) {
1149        if let DriverState::Initialized(ref ecc) = self.driver {
1150            self.driver = DriverState::Uninitialized(unsafe { ecc._ecc.clone_unchecked() });
1151        }
1152    }
1153}
1154
1155/// An active work queue driver.
1156///
1157/// This object must be kept around, otherwise ECC operations will never complete.
1158pub struct EccWorkQueueDriver<'t, 'd> {
1159    inner: WorkQueueDriver<'t, EccBackend<'d>, EccWorkItem>,
1160}
1161
1162impl<'t, 'd> EccWorkQueueDriver<'t, 'd> {
1163    /// Finishes processing the current work queue item, then stops the driver.
1164    pub fn stop(self) -> impl Future<Output = ()> {
1165        self.inner.stop()
1166    }
1167}
1168
1169#[crate::ram]
1170#[crate::handler]
1171fn ecc_work_queue_handler() {
1172    if !ECC_WORK_QUEUE.process() {
1173        // The queue may indicate that it needs to be polled again. In this case, we do not clear
1174        // the interrupt bit, which causes the interrupt to be re-handled.
1175        cfg_if::cfg_if! {
1176            if #[cfg(any(esp32c5, esp32c61))] {
1177                let reg = ECC::regs().int_clr();
1178            } else {
1179                let reg = ECC::regs().mult_int_clr();
1180            }
1181        }
1182        reg.write(|w| w.calc_done().clear_bit_by_one());
1183    }
1184}
1185
1186/// An ECC operation that can be enqueued on the work queue.
1187pub struct EccBackendOperation<'op, O: EccOperation> {
1188    frontend: WorkQueueFrontend<EccWorkItem>,
1189    _marker: PhantomData<(&'op mut (), O)>,
1190}
1191
1192impl<'op, O: EccOperation> EccBackendOperation<'op, O> {
1193    fn new(work_item: EccWorkItem) -> Self {
1194        Self {
1195            frontend: WorkQueueFrontend::new(work_item),
1196            _marker: PhantomData,
1197        }
1198    }
1199
1200    /// Designate a buffer for the scalar result of the operation.
1201    ///
1202    /// Once the operation is processed, the result can be retrieved from the designated buffer.
1203    ///
1204    /// ## Errors
1205    ///
1206    /// Returns an error if `out` is not the correct size.
1207    pub fn with_scalar_result(mut self, out: &'op mut [u8]) -> Result<Self, KeyLengthMismatch>
1208    where
1209        O: OperationReturnsScalar,
1210    {
1211        self.frontend.data().curve.size_check([out])?;
1212
1213        self.frontend
1214            .data_mut()
1215            .outputs
1216            .set_scalar(O::LOCATION, NonNull::from(out));
1217
1218        Ok(self)
1219    }
1220
1221    /// Designate buffers for the affine point result of the operation.
1222    ///
1223    /// Once the operation is processed, the result can be retrieved from the designated buffers.
1224    ///
1225    /// ## Errors
1226    ///
1227    /// Returns an error if `x` or `y` are not the correct size.
1228    pub fn with_affine_point_result(
1229        mut self,
1230        px: &'op mut [u8],
1231        py: &'op mut [u8],
1232    ) -> Result<Self, KeyLengthMismatch>
1233    where
1234        O: OperationReturnsAffinePoint,
1235    {
1236        self.frontend.data().curve.size_check([px, py])?;
1237
1238        self.frontend.data_mut().outputs.set_px(NonNull::from(px));
1239        self.frontend.data_mut().outputs.set_py(NonNull::from(py));
1240
1241        Ok(self)
1242    }
1243
1244    /// Designate buffers for the Jacobian point result of the operation.
1245    ///
1246    /// Once the operation is processed, the result can be retrieved from the designated buffers.
1247    ///
1248    /// ## Errors
1249    ///
1250    /// Returns an error if `x`, `y`, or `z` are not the correct size.
1251    pub fn with_jacobian_point_result(
1252        mut self,
1253        qx: &'op mut [u8],
1254        qy: &'op mut [u8],
1255        qz: &'op mut [u8],
1256    ) -> Result<Self, KeyLengthMismatch>
1257    where
1258        O: OperationReturnsJacobianPoint,
1259    {
1260        self.frontend.data().curve.size_check([qx, qy, qz])?;
1261
1262        self.frontend.data_mut().outputs.set_qx(NonNull::from(qx));
1263        self.frontend.data_mut().outputs.set_qy(NonNull::from(qy));
1264        self.frontend.data_mut().outputs.set_qz(NonNull::from(qz));
1265
1266        Ok(self)
1267    }
1268
1269    /// Returns `true` if the input point is on the curve.
1270    ///
1271    /// The operation must be processed before this method returns a meaningful value.
1272    pub fn point_on_curve(&self) -> bool
1273    where
1274        O: OperationVerifiesPoint,
1275    {
1276        self.frontend.data().point_verification_result
1277    }
1278
1279    /// Starts processing the operation.
1280    ///
1281    /// The returned [`EccHandle`] must be polled to completion before the operation is considered
1282    /// complete.
1283    pub fn process(&mut self) -> EccHandle<'_> {
1284        EccHandle(self.frontend.post(&ECC_WORK_QUEUE))
1285    }
1286}
1287
1288/// A handle for an in-progress operation.
1289#[must_use]
1290pub struct EccHandle<'t>(Handle<'t, EccWorkItem>);
1291
1292impl EccHandle<'_> {
1293    /// Polls the status of the work item.
1294    ///
1295    /// This function returns `true` if the item has been processed.
1296    #[inline]
1297    pub fn poll(&mut self) -> bool {
1298        self.0.poll()
1299    }
1300
1301    /// Polls the work item to completion, by busy-looping.
1302    ///
1303    /// This function returns immediately if `poll` returns `true`.
1304    #[inline]
1305    pub fn wait_blocking(self) -> Status {
1306        self.0.wait_blocking()
1307    }
1308
1309    /// Waits until the work item is completed.
1310    #[inline]
1311    pub fn wait(&mut self) -> impl Future<Output = Status> {
1312        self.0.wait()
1313    }
1314
1315    /// Cancels the work item and asynchronously waits until it is removed from the work queue.
1316    #[inline]
1317    pub fn cancel(&mut self) -> impl Future<Output = ()> {
1318        self.0.cancel()
1319    }
1320}