1use 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
30macro_rules! define_operations {
39 ($($op:tt {
40 docs: [$first_line:literal $(, $lines:literal)*],
42 function: $function:ident,
44 $(modular_arithmetic_method: $is_modular:literal,)?
46 $(verifies_point: $verifies_point:literal,)?
48 inputs: [$($input:ident),*],
52 returns: [
57 $(
58 $(#[$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; 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 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 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 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
368pub 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#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, BuilderLite)]
410#[cfg_attr(feature = "defmt", derive(defmt::Format))]
411pub struct Config {
412 force_enable_reg_clock: bool,
414
415 #[cfg(ecc_has_memory_clock_gate)]
417 force_enable_mem_clock: bool,
418
419 #[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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
433pub struct KeyLengthMismatch;
434
435#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub enum OperationError {
438 ParameterLengthMismatch,
440
441 PointNotOnCurve,
443}
444
445#[cfg(ecc_has_modular_arithmetic)]
447#[derive(Debug, Clone, Copy, PartialEq, Eq)]
448pub enum EccModBase {
449 OrderOfCurve = 0,
451
452 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 #[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 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 backend_operation!($op);
504 )*
505 }
506 };
507}
508
509impl<'d> Ecc<'d, Blocking> {
510 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 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
690for_each_ecc_working_mode! {
694 (all $(( $id:literal, $mode:tt )),*) => {
695 #[derive(Clone, Copy)]
696 #[doc(hidden)]
697 pub enum WorkMode {
700 $(
701 $mode = $id,
702 )*
703 }
704 };
705}
706
707for_each_ecc_working_mode! {
709 (all $(( $id:literal, $mode:tt )),*) => {
710 $(
711 result_type!($mode);
712 )*
713 };
714}
715
716for_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 while self.info().is_busy() {}
737
738 EccResultHandle::new(curve, self)
739 }
740
741 pub fn apply_config(&mut self, config: &Config) {
743 self.info().apply_config(config);
744 }
745
746 pub fn reset(&mut self) {
748 self.info().reset()
749 }
750
751 #[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
770pub trait EccOperation: Sealed {
772 const VERIFIES_POINT: bool;
774
775 #[doc(hidden)]
777 const WORK_MODE: WorkMode;
778}
779
780#[doc(hidden)]
782pub enum ScalarResultLocation {
783 Px,
785 Py,
787 K,
789}
790
791pub trait OperationReturnsScalar: EccOperation {
793 #[doc(hidden)]
795 const LOCATION: ScalarResultLocation;
796}
797
798pub trait OperationReturnsAffinePoint: EccOperation {}
800
801pub trait OperationReturnsJacobianPoint: EccOperation {}
803
804pub trait OperationVerifiesPoint: EccOperation {}
806
807#[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 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 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 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 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 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
987unsafe impl Sync for MemoryPointers {}
992unsafe 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 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
1029pub struct EccBackend<'d> {
1033 driver: DriverState<'d>,
1034 config: Config,
1035}
1036
1037impl<'d> EccBackend<'d> {
1038 pub fn new(ecc: ECC<'d>, config: Config) -> Self {
1042 Self {
1043 driver: DriverState::Uninitialized(ecc),
1044 config,
1045 }
1046 }
1047
1048 pub fn start(&mut self) -> EccWorkQueueDriver<'_, 'd> {
1052 EccWorkQueueDriver {
1053 inner: WorkQueueDriver::new(self, ECC_VTABLE, &ECC_WORK_QUEUE),
1054 }
1055 }
1056
1057 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
1155pub struct EccWorkQueueDriver<'t, 'd> {
1159 inner: WorkQueueDriver<'t, EccBackend<'d>, EccWorkItem>,
1160}
1161
1162impl<'t, 'd> EccWorkQueueDriver<'t, 'd> {
1163 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 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
1186pub 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 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 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 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 pub fn point_on_curve(&self) -> bool
1273 where
1274 O: OperationVerifiesPoint,
1275 {
1276 self.frontend.data().point_verification_result
1277 }
1278
1279 pub fn process(&mut self) -> EccHandle<'_> {
1284 EccHandle(self.frontend.post(&ECC_WORK_QUEUE))
1285 }
1286}
1287
1288#[must_use]
1290pub struct EccHandle<'t>(Handle<'t, EccWorkItem>);
1291
1292impl EccHandle<'_> {
1293 #[inline]
1297 pub fn poll(&mut self) -> bool {
1298 self.0.poll()
1299 }
1300
1301 #[inline]
1305 pub fn wait_blocking(self) -> Status {
1306 self.0.wait_blocking()
1307 }
1308
1309 #[inline]
1311 pub fn wait(&mut self) -> impl Future<Output = Status> {
1312 self.0.wait()
1313 }
1314
1315 #[inline]
1317 pub fn cancel(&mut self) -> impl Future<Output = ()> {
1318 self.0.cancel()
1319 }
1320}