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, CryptoClockGuard, 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
110[`OperationError::ParameterLengthMismatch`] if an input does not match the curve's size."]
111 #[cfg_attr(ecc_has_curve_sm2, doc = "\n[`OperationError::UnsupportedCurve`] if SM2 is disabled by eFuse.")]
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>, OperationError> {
119 curve.check_parameters([$($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 #[cfg_attr(ecc_has_curve_sm2, doc = "\n[`OperationError::UnsupportedCurve`] if SM2 is disabled by eFuse.")]
159 pub fn $function<'op>(
160 self,
161 $(#[cfg($is_modular)] modulus: EccModBase,)?
162 $($input: &'op [u8],)*
163 ) -> Result<EccBackendOperation<'op, $op>, OperationError> {
164 self.check_parameters([&$($input,)*])?;
165
166 #[cfg(ecc_has_modular_arithmetic)]
167 let mod_base = $crate::if_set! {
168 $(
169 {
170 $crate::ignore!($is_modular);
171 modulus
172 }
173 )?,
174 EccModBase::OrderOfCurve
176 };
177
178 let work_item = EccWorkItem {
179 curve: self,
180 operation: WorkMode::$op,
181 cancelled: false,
182 #[cfg(ecc_has_modular_arithmetic)]
183 mod_base,
184 inputs: {
185 let mut inputs = MemoryPointers::default();
186 $(
187 paste::paste! {
188 inputs.[<set_ $input>](NonNull::from($input));
189 };
190 )*
191 inputs
192 },
193 point_verification_result: false,
194 outputs: MemoryPointers::default(),
195 };
196
197 Ok(EccBackendOperation::new(work_item))
198 }
199 };
200 )*
201 }
202 }
203}
204
205define_operations! {
206 AffinePointMultiplication {
207 docs: [
208 "Base Point Multiplication",
209 "",
210 "This operation performs `(Qx, Qy) = k * (Px, Py)`."
211 ],
212 function: affine_point_multiplication,
213 inputs: [k, px, py],
214 returns: [AffinePoint]
215 },
216
217 AffinePointVerification {
218 docs: [
219 "Base Point Verification",
220 "",
221 "This operation verifies whether Point (Px, Py) is on the selected elliptic curve."
222 ],
223 function: affine_point_verification,
224 verifies_point: true,
225 inputs: [px, py],
226 returns: []
227 },
228
229 AffinePointVerificationAndMultiplication {
230 docs: [
231 "Base Point Verification and Multiplication",
232 "",
233 "This operation verifies whether Point (Px, Py) is on the selected elliptic curve and performs `(Qx, Qy) = k * (Px, Py)`."
234 ],
235 function: affine_point_verification_multiplication,
236 verifies_point: true,
237 inputs: [k, px, py],
238 returns: [
239 AffinePoint,
240 #[cfg(ecc_separate_jacobian_point_memory)]
241 JacobianPoint
242 ]
243 },
244
245 AffinePointAddition {
246 docs: [
247 "Point Addition",
248 "",
249 "This operation performs `(Rx, Ry) = (Jx, Jy, Jz) = (Px, Py, 1) + (Qx, Qy, Qz)`."
250 ],
251 function: affine_point_addition,
252 inputs: [px, py, qx, qy, qz],
253 returns: [
254 AffinePoint,
255 #[cfg(ecc_separate_jacobian_point_memory)]
256 JacobianPoint
257 ]
258 },
259
260 JacobianPointMultiplication {
261 docs: [
262 "Jacobian Point Multiplication",
263 "",
264 "This operation performs `(Qx, Qy, Qz) = k * (Px, Py, 1)`."
265 ],
266 function: jacobian_point_multiplication,
267 inputs: [k, px, py],
268 returns: [
269 JacobianPoint
270 ]
271 },
272
273 JacobianPointVerification {
274 docs: [
275 "Jacobian Point Verification",
276 "",
277 "This operation verifies whether Point (Qx, Qy, Qz) is on the selected elliptic curve."
278 ],
279 function: jacobian_point_verification,
280 verifies_point: true,
281 inputs: [qx, qy, qz],
282 returns: [
283 JacobianPoint
284 ]
285 },
286
287 AffinePointVerificationAndJacobianPointMultiplication {
288 docs: [
289 "Base Point Verification + Jacobian Point Multiplication",
290 "",
291 "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)`."
292 ],
293 function: affine_point_verification_jacobian_multiplication,
294 verifies_point: true,
295 inputs: [k, px, py],
296 returns: [
297 JacobianPoint
298 ]
299 },
300
301 FiniteFieldDivision {
302 docs: [
303 "Finite Field Division",
304 "",
305 "This operation performs `R = Py * k^{−1} mod p`."
306 ],
307 function: finite_field_division,
308 inputs: [k, py],
309 returns: [
310 Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
311 ]
312 },
313
314 ModularAddition {
315 docs: [
316 "Modular Addition",
317 "",
318 "This operation performs `R = Px + Py mod p`."
319 ],
320 function: modular_addition,
321 modular_arithmetic_method: true,
322 inputs: [px, py],
323 returns: [
324 Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Px }
325 ]
326 },
327
328 ModularSubtraction {
329 docs: [
330 "Modular Subtraction",
331 "",
332 "This operation performs `R = Px - Py mod p`."
333 ],
334 function: modular_subtraction,
335 modular_arithmetic_method: true,
336 inputs: [px, py],
337 returns: [
338 Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Px }
339 ]
340 },
341
342 ModularMultiplication {
343 docs: [
344 "Modular Multiplication",
345 "",
346 "This operation performs `R = Px * Py mod p`."
347 ],
348 function: modular_multiplication,
349 modular_arithmetic_method: true,
350 inputs: [px, py],
351 returns: [
352 Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
353 ]
354 },
355
356 ModularDivision {
357 docs: [
358 "Modular Division",
359 "",
360 "This operation performs `R = Px * Py^{−1} mod p`."
361 ],
362 function: modular_division,
363 modular_arithmetic_method: true,
364 inputs: [px, py],
365 returns: [
366 Scalar { const LOCATION: ScalarResultLocation = ScalarResultLocation::Py }
367 ]
368 }
369}
370
371const MEM_BLOCK_SIZE: usize = property!("ecc.mem_block_size");
372
373pub struct Ecc<'d, Dm: DriverMode> {
377 _ecc: ECC<'d>,
378 phantom: PhantomData<Dm>,
379 _memory_guard: EccMemoryPowerGuard,
380 _guard: GenericPeripheralGuard<{ system::Peripheral::Ecc as u8 }>,
381 _clock_guard: CryptoClockGuard,
382}
383
384struct EccMemoryPowerGuard;
385
386impl EccMemoryPowerGuard {
387 fn new() -> Self {
388 #[cfg(soc_has_pcr)]
389 crate::peripherals::PCR::regs()
390 .ecc_pd_ctrl()
391 .modify(|_, w| {
392 w.ecc_mem_force_pd().clear_bit();
393 w.ecc_mem_force_pu().set_bit();
394 w.ecc_mem_pd().clear_bit()
395 });
396 Self
397 }
398}
399
400impl Drop for EccMemoryPowerGuard {
401 fn drop(&mut self) {
402 #[cfg(soc_has_pcr)]
403 crate::peripherals::PCR::regs()
404 .ecc_pd_ctrl()
405 .modify(|_, w| {
406 w.ecc_mem_force_pd().clear_bit();
407 w.ecc_mem_force_pu().clear_bit();
408 w.ecc_mem_pd().set_bit()
409 });
410 }
411}
412
413#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, BuilderLite)]
415#[cfg_attr(feature = "defmt", derive(defmt::Format))]
416pub struct Config {
417 force_enable_reg_clock: bool,
419
420 #[cfg(ecc_has_memory_clock_gate)]
422 force_enable_mem_clock: bool,
423
424 #[cfg_attr(
427 esp32h2,
428 doc = r"
429
430Only available on chip revision 1.2 and above."
431 )]
432 #[cfg(ecc_supports_enhanced_security)]
433 enhanced_security: bool,
434}
435
436#[derive(Debug, Clone, Copy, PartialEq, Eq)]
438pub enum OperationError {
439 ParameterLengthMismatch,
441
442 PointNotOnCurve,
444
445 UnsupportedCurve,
447}
448
449#[cfg(ecc_has_modular_arithmetic)]
451#[derive(Debug, Clone, Copy, PartialEq, Eq)]
452pub enum EccModBase {
453 OrderOfCurve = 0,
455
456 PrimeModulus = 1,
458}
459
460for_each_ecc_curve! {
461 (all $(( $id:literal, $name:ident, $bits:literal )),*) => {
462 #[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 #[cfg_attr(ecc_has_curve_sm2, doc = "\nSM2 is disabled when the `DIS_SM_CRYPT` eFuse is set.")]
476 #[instability::unstable]
477 pub fn is_supported(self) -> bool {
478 match self {
479 #[cfg(ecc_has_curve_sm2)]
480 Self::Sm2 => !crate::efuse::read_bit(crate::efuse::DIS_SM_CRYPT),
481 _ => true,
482 }
483 }
484
485 pub const fn size(self) -> usize {
487 match self {
488 $(
489 EllipticCurve::$name => $bits / 8,
490 )*
491 }
492 }
493 }
494 };
495}
496
497for_each_ecc_working_mode! {
498 (all $(($wm_id:literal, $op:tt)),*) => {
499 impl EllipticCurve {
500 fn check_parameters<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
501 if !self.is_supported() {
502 return Err(OperationError::UnsupportedCurve);
503 }
504
505 self.size_check(params)
506 }
507
508 fn size_check<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
509 let bytes = self.size();
510
511 if params.iter().any(|p| p.len() != bytes) {
512 return Err(OperationError::ParameterLengthMismatch);
513 }
514
515 Ok(())
516 }
517
518 $(
519 backend_operation!($op);
521 )*
522 }
523 };
524}
525
526impl<'d> Ecc<'d, Blocking> {
527 pub fn new(ecc: ECC<'d>, config: Config) -> Self {
529 let clock_guard = CryptoClockGuard::new();
530 let this = Self {
531 _ecc: ecc,
532 phantom: PhantomData,
533 _memory_guard: EccMemoryPowerGuard::new(),
534 _guard: GenericPeripheralGuard::new(),
535 _clock_guard: clock_guard,
536 };
537
538 this.info().apply_config(&config);
539
540 this
541 }
542}
543
544impl crate::private::Sealed for Ecc<'_, Blocking> {}
545
546#[instability::unstable]
547impl crate::interrupt::InterruptConfigurable for Ecc<'_, Blocking> {
548 fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
549 self.set_interrupt_handler(handler);
550 }
551}
552
553struct Info {
554 regs: &'static pac::ecc::RegisterBlock,
555}
556
557impl Info {
558 fn reset(&self) {
559 self.regs.mult_conf().reset()
560 }
561
562 fn apply_config(&self, config: &Config) {
563 self.regs.mult_conf().modify(|_, w| {
564 w.clk_en().bit(config.force_enable_reg_clock);
565
566 #[cfg(ecc_has_memory_clock_gate)]
567 w.mem_clock_gate_force_on()
568 .bit(config.force_enable_mem_clock);
569
570 #[cfg(ecc_supports_enhanced_security)]
571 if !cfg!(esp32h2) || crate::soc::chip_revision_above(ChipRevision::from_combined(102)) {
572 w.security_mode().bit(config.enhanced_security);
573 }
574
575 w
576 });
577 }
578
579 fn check_point_verification_result(&self) -> Result<(), OperationError> {
580 if self
581 .regs
582 .mult_conf()
583 .read()
584 .verification_result()
585 .bit_is_set()
586 {
587 Ok(())
588 } else {
589 Err(OperationError::PointNotOnCurve)
590 }
591 }
592
593 #[inline]
594 fn write_mem(&self, mut word_ptr: *mut u32, data: &[u8]) {
595 debug_assert!(data.len() <= MEM_BLOCK_SIZE);
598
599 #[cfg(ecc_zero_extend_writes)]
600 let end = word_ptr.wrapping_byte_add(MEM_BLOCK_SIZE);
601
602 let (chunks, remainder) = data.as_chunks::<4>();
603 debug_assert!(remainder.is_empty());
604
605 for word_bytes in chunks {
606 unsafe { word_ptr.write_volatile(u32::from_le_bytes(*word_bytes)) };
607 word_ptr = word_ptr.wrapping_add(1);
608 }
609
610 #[cfg(ecc_zero_extend_writes)]
611 while word_ptr < end {
612 unsafe { word_ptr.write_volatile(0) };
613 word_ptr = word_ptr.wrapping_add(1);
614 }
615 }
616
617 #[inline]
618 fn read_mem(&self, mut word_ptr: *const u32, out: &mut [u8]) {
619 let (chunks, _) = out.as_chunks_mut::<4>();
620 for word_bytes in chunks {
621 let word = unsafe { word_ptr.read_volatile() };
622 word_ptr = word_ptr.wrapping_add(1);
623 *word_bytes = word.to_le_bytes();
624 }
625 }
626
627 fn k_mem(&self) -> *mut u32 {
628 self.regs.k_mem(0).as_ptr()
629 }
630
631 fn px_mem(&self) -> *mut u32 {
632 self.regs.px_mem(0).as_ptr()
633 }
634
635 fn py_mem(&self) -> *mut u32 {
636 self.regs.py_mem(0).as_ptr()
637 }
638
639 fn qx_mem(&self) -> *mut u32 {
640 cfg_select! {
641 ecc_separate_jacobian_point_memory => self.regs.qx_mem(0).as_ptr(),
642 _ => self.regs.px_mem(0).as_ptr(),
643 }
644 }
645
646 fn qy_mem(&self) -> *mut u32 {
647 cfg_select! {
648 ecc_separate_jacobian_point_memory => self.regs.qy_mem(0).as_ptr(),
649 _ => self.regs.py_mem(0).as_ptr(),
650 }
651 }
652
653 fn qz_mem(&self) -> *mut u32 {
654 cfg_select! {
655 ecc_separate_jacobian_point_memory => self.regs.qz_mem(0).as_ptr(),
656 _ => self.regs.k_mem(0).as_ptr(),
657 }
658 }
659
660 fn read_point_result(&self, x: &mut [u8], y: &mut [u8]) {
661 self.read_mem(self.px_mem(), x);
662 self.read_mem(self.py_mem(), y);
663 }
664
665 fn read_jacobian_result(&self, qx: &mut [u8], qy: &mut [u8], qz: &mut [u8]) {
666 self.read_mem(self.qx_mem(), qx);
667 self.read_mem(self.qy_mem(), qy);
668 self.read_mem(self.qz_mem(), qz);
669 }
670
671 #[cfg(clear_crypto_secrets)]
673 fn clear_secrets(&self) {
674 self.zero_mem(self.k_mem());
675 self.zero_mem(self.px_mem());
676 self.zero_mem(self.py_mem());
677
678 #[cfg(ecc_separate_jacobian_point_memory)]
679 {
680 self.zero_mem(self.qx_mem());
681 self.zero_mem(self.qy_mem());
682 self.zero_mem(self.qz_mem());
683 }
684 }
685
686 #[cfg(clear_crypto_secrets)]
687 fn zero_mem(&self, mut word_ptr: *mut u32) {
688 for _ in 0..(MEM_BLOCK_SIZE / 4) {
689 unsafe { word_ptr.write_volatile(0) };
690 word_ptr = word_ptr.wrapping_add(1);
691 }
692 }
693
694 fn is_busy(&self) -> bool {
695 self.regs.mult_conf().read().start().bit_is_set()
696 }
697
698 fn start_operation(
699 &self,
700 mode: WorkMode,
701 curve: EllipticCurve,
702 #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
703 ) {
704 let curve_variant;
705 for_each_ecc_curve! {
706 (all $(($_id:tt, $name:ident, $_bits:tt)),*) => {
707 curve_variant = match curve {
708 $(EllipticCurve::$name => KEY_LENGTH::$name,)*
709 }
710 };
711 };
712 self.regs.mult_conf().modify(|_, w| unsafe {
713 w.work_mode().bits(mode as u8);
714 w.key_length().variant(curve_variant);
715
716 #[cfg(ecc_has_modular_arithmetic)]
717 w.mod_base().bit(mod_base as u8 == 1);
718
719 w.start().set_bit()
720 });
721 }
722}
723
724for_each_ecc_working_mode! {
728 (all $(( $id:literal, $mode:tt )),*) => {
729 #[derive(Clone, Copy)]
730 #[doc(hidden)]
731 pub enum WorkMode {
734 $(
735 $mode = $id,
736 )*
737 }
738 };
739}
740
741for_each_ecc_working_mode! {
743 (all $(( $id:literal, $mode:tt )),*) => {
744 $(
745 result_type!($mode);
746 )*
747 };
748}
749
750for_each_ecc_working_mode! {
752 (all $(( $id:literal, $mode:tt )),*) => {
753 impl<'d, Dm: DriverMode> Ecc<'d, Dm> {
754 fn info(&self) -> Info {
755 Info { regs: ECC::regs() }
756 }
757
758 fn run_operation<'op, O: EccOperation>(
759 &'op mut self,
760 curve: EllipticCurve,
761 #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
762 ) -> EccResultHandle<'op, O> {
763 self.info().start_operation(
764 O::WORK_MODE,
765 curve,
766 #[cfg(ecc_has_modular_arithmetic)] mod_base,
767 );
768
769 while self.info().is_busy() {}
771
772 EccResultHandle::new(curve, self)
773 }
774
775 pub fn apply_config(&mut self, config: &Config) {
777 self.info().apply_config(config);
778 }
779
780 pub fn reset(&mut self) {
782 self.info().reset()
783 }
784
785 #[instability::unstable]
789 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
790 for core in crate::system::Cpu::other() {
791 crate::interrupt::disable(core, Interrupt::ECC);
792 }
793 crate::interrupt::bind_handler(Interrupt::ECC, handler);
794 }
795
796 $(
797 driver_method!($mode);
798 )*
799 }
800 };
801}
802
803pub trait EccOperation: Sealed {
805 const VERIFIES_POINT: bool;
807
808 #[doc(hidden)]
810 const WORK_MODE: WorkMode;
811}
812
813#[doc(hidden)]
815pub enum ScalarResultLocation {
816 Px,
818 Py,
820 K,
822}
823
824pub trait OperationReturnsScalar: EccOperation {
826 #[doc(hidden)]
828 const LOCATION: ScalarResultLocation;
829}
830
831pub trait OperationReturnsAffinePoint: EccOperation {}
833
834pub trait OperationReturnsJacobianPoint: EccOperation {}
836
837pub trait OperationVerifiesPoint: EccOperation {}
839
840#[must_use]
846pub struct EccResultHandle<'op, O>
847where
848 O: EccOperation,
849{
850 curve: EllipticCurve,
851 info: Info,
852 _marker: PhantomData<(&'op mut (), O)>,
853}
854
855impl<'op, O> EccResultHandle<'op, O>
856where
857 O: EccOperation,
858{
859 fn new<'d, Dm: DriverMode>(curve: EllipticCurve, driver: &'op mut Ecc<'d, Dm>) -> Self {
860 Self {
861 curve,
862 info: driver.info(),
863 _marker: PhantomData,
864 }
865 }
866
867 fn run_checks<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
868 self.curve.size_check(params)?;
869 if O::VERIFIES_POINT {
870 self.info.check_point_verification_result()?;
871 }
872 Ok(())
873 }
874
875 pub fn success(&self) -> bool {
881 if O::VERIFIES_POINT {
882 self.info.check_point_verification_result().is_ok()
883 } else {
884 true
885 }
886 }
887
888 pub fn read_scalar_result(&self, out: &mut [u8]) -> Result<(), OperationError>
894 where
895 O: OperationReturnsScalar,
896 {
897 self.run_checks([out])?;
898
899 match O::LOCATION {
900 ScalarResultLocation::Px => self.info.read_mem(self.info.px_mem(), out),
901 ScalarResultLocation::Py => self.info.read_mem(self.info.py_mem(), out),
902 ScalarResultLocation::K => self.info.read_mem(self.info.k_mem(), out),
903 }
904
905 Ok(())
906 }
907
908 pub fn read_affine_point_result(&self, x: &mut [u8], y: &mut [u8]) -> Result<(), OperationError>
915 where
916 O: OperationReturnsAffinePoint,
917 {
918 self.run_checks([x, y])?;
919 self.info.read_point_result(x, y);
920 Ok(())
921 }
922
923 pub fn read_jacobian_point_result(
930 &self,
931 x: &mut [u8],
932 y: &mut [u8],
933 z: &mut [u8],
934 ) -> Result<(), OperationError>
935 where
936 O: OperationReturnsJacobianPoint,
937 {
938 self.run_checks([x, y, z])?;
939 self.info.read_jacobian_result(x, y, z);
940 Ok(())
941 }
942}
943
944struct EccWorkItem {
945 curve: EllipticCurve,
946 operation: WorkMode,
947 cancelled: bool,
948 #[cfg(ecc_has_modular_arithmetic)]
949 mod_base: EccModBase,
950 inputs: MemoryPointers,
951 point_verification_result: bool,
952 outputs: MemoryPointers,
953}
954
955#[derive(Default)]
956struct MemoryPointers {
957 k: Option<NonNull<u8>>,
959 px: Option<NonNull<u8>>,
960 py: Option<NonNull<u8>>,
961 #[cfg(ecc_separate_jacobian_point_memory)]
962 qx: Option<NonNull<u8>>,
963 #[cfg(ecc_separate_jacobian_point_memory)]
964 qy: Option<NonNull<u8>>,
965 #[cfg(ecc_separate_jacobian_point_memory)]
966 qz: Option<NonNull<u8>>,
967}
968
969impl MemoryPointers {
970 fn set_scalar(&mut self, location: ScalarResultLocation, ptr: NonNull<[u8]>) {
971 match location {
972 ScalarResultLocation::Px => self.set_px(ptr),
973 ScalarResultLocation::Py => self.set_py(ptr),
974 ScalarResultLocation::K => self.set_k(ptr),
975 }
976 }
977
978 fn set_k(&mut self, ptr: NonNull<[u8]>) {
979 self.k = Some(ptr.cast());
980 }
981
982 fn set_px(&mut self, ptr: NonNull<[u8]>) {
983 self.px = Some(ptr.cast());
984 }
985
986 fn set_py(&mut self, ptr: NonNull<[u8]>) {
987 self.py = Some(ptr.cast());
988 }
989
990 fn set_qx(&mut self, ptr: NonNull<[u8]>) {
991 cfg_select! {
992 ecc_separate_jacobian_point_memory => {
993 self.qx = Some(ptr.cast());
994 }
995 _ => {
996 self.px = Some(ptr.cast());
997 }
998 }
999 }
1000
1001 fn set_qy(&mut self, ptr: NonNull<[u8]>) {
1002 cfg_select! {
1003 ecc_separate_jacobian_point_memory => {
1004 self.qy = Some(ptr.cast());
1005 }
1006 _ => {
1007 self.py = Some(ptr.cast());
1008 }
1009 }
1010 }
1011
1012 fn set_qz(&mut self, ptr: NonNull<[u8]>) {
1013 cfg_select! {
1014 ecc_separate_jacobian_point_memory => {
1015 self.qz = Some(ptr.cast());
1016 }
1017 _ => {
1018 self.k = Some(ptr.cast());
1019 }
1020 }
1021 }
1022}
1023
1024unsafe impl Sync for MemoryPointers {}
1029unsafe impl Send for MemoryPointers {}
1031
1032static ECC_WORK_QUEUE: WorkQueue<EccWorkItem> = WorkQueue::new();
1033
1034const ECC_VTABLE: VTable<EccWorkItem> = VTable {
1035 post: |driver, item| {
1036 let driver = unsafe { EccBackend::from_raw(driver) };
1037
1038 if let DriverState::Uninitialized(ecc) = &driver.driver {
1040 let mut ecc = Ecc::new(unsafe { ecc.clone_unchecked() }, driver.config);
1041 ecc.set_interrupt_handler(ecc_work_queue_handler);
1042 driver.driver = DriverState::Initialized(ecc);
1043 };
1044
1045 Some(driver.process(item))
1046 },
1047 poll: |driver, item| {
1048 let driver = unsafe { EccBackend::from_raw(driver) };
1049 driver.poll(item)
1050 },
1051 cancel: |driver, item| {
1052 let driver = unsafe { EccBackend::from_raw(driver) };
1053 driver.cancel(item);
1054 },
1055 stop: |driver| {
1056 let driver = unsafe { EccBackend::from_raw(driver) };
1057 driver.deinitialize()
1058 },
1059};
1060
1061enum DriverState<'d> {
1062 Uninitialized(ECC<'d>),
1063 Initialized(Ecc<'d, Blocking>),
1064}
1065
1066pub struct EccBackend<'d> {
1070 driver: DriverState<'d>,
1071 config: Config,
1072}
1073
1074impl<'d> EccBackend<'d> {
1075 pub fn new(ecc: ECC<'d>, config: Config) -> Self {
1079 Self {
1080 driver: DriverState::Uninitialized(ecc),
1081 config,
1082 }
1083 }
1084
1085 pub fn start(&mut self) -> EccWorkQueueDriver<'_, 'd> {
1089 EccWorkQueueDriver {
1090 inner: WorkQueueDriver::new(self, ECC_VTABLE, &ECC_WORK_QUEUE),
1091 }
1092 }
1093
1094 unsafe fn from_raw<'any>(ptr: NonNull<()>) -> &'any mut Self {
1097 unsafe { ptr.cast::<EccBackend<'_>>().as_mut() }
1098 }
1099
1100 fn process(&mut self, item: &mut EccWorkItem) -> Poll {
1101 let DriverState::Initialized(driver) = &mut self.driver else {
1102 unreachable!()
1103 };
1104
1105 let bytes = item.curve.size();
1106
1107 macro_rules! set_input {
1108 ($input:ident, $input_mem:ident) => {
1109 if let Some($input) = item.inputs.$input {
1110 driver.info().write_mem(driver.info().$input_mem(), unsafe {
1111 core::slice::from_raw_parts($input.as_ptr(), bytes)
1112 });
1113 }
1114 };
1115 }
1116
1117 set_input!(k, k_mem);
1118 set_input!(px, px_mem);
1119 set_input!(py, py_mem);
1120
1121 #[cfg(ecc_separate_jacobian_point_memory)]
1122 {
1123 set_input!(qx, qx_mem);
1124 set_input!(qy, qy_mem);
1125 set_input!(qz, qz_mem);
1126 }
1127
1128 driver.info().start_operation(
1129 item.operation,
1130 item.curve,
1131 #[cfg(ecc_has_modular_arithmetic)]
1132 item.mod_base,
1133 );
1134 Poll::Pending(false)
1135 }
1136
1137 fn poll(&mut self, item: &mut EccWorkItem) -> Poll {
1138 let DriverState::Initialized(driver) = &mut self.driver else {
1139 unreachable!()
1140 };
1141
1142 if driver.info().is_busy() {
1143 return Poll::Pending(false);
1144 }
1145 if item.cancelled {
1146 return Poll::Ready(Status::Cancelled);
1147 }
1148
1149 let bytes = item.curve.size();
1150
1151 macro_rules! read_output {
1152 ($output:ident, $output_mem:ident) => {
1153 if let Some($output) = item.outputs.$output {
1154 driver.info().read_mem(driver.info().$output_mem(), unsafe {
1155 core::slice::from_raw_parts_mut($output.as_ptr(), bytes)
1156 });
1157 }
1158 };
1159 }
1160
1161 read_output!(k, k_mem);
1162 read_output!(px, px_mem);
1163 read_output!(py, py_mem);
1164
1165 #[cfg(ecc_separate_jacobian_point_memory)]
1166 {
1167 read_output!(qx, qx_mem);
1168 read_output!(qy, qy_mem);
1169 read_output!(qz, qz_mem);
1170 }
1171
1172 item.point_verification_result = driver.info().check_point_verification_result().is_ok();
1173
1174 #[cfg(clear_crypto_secrets)]
1175 driver.info().clear_secrets();
1176
1177 Poll::Ready(Status::Completed)
1178 }
1179
1180 fn cancel(&mut self, item: &mut EccWorkItem) {
1181 let DriverState::Initialized(driver) = &mut self.driver else {
1182 unreachable!()
1183 };
1184 driver.reset();
1185
1186 #[cfg(clear_crypto_secrets)]
1188 driver.info().clear_secrets();
1189
1190 item.cancelled = true;
1191 }
1192
1193 fn deinitialize(&mut self) {
1194 if let DriverState::Initialized(ref ecc) = self.driver {
1195 self.driver = DriverState::Uninitialized(unsafe { ecc._ecc.clone_unchecked() });
1196 }
1197 }
1198}
1199
1200pub struct EccWorkQueueDriver<'t, 'd> {
1204 inner: WorkQueueDriver<'t, EccBackend<'d>, EccWorkItem>,
1205}
1206
1207impl<'t, 'd> EccWorkQueueDriver<'t, 'd> {
1208 pub fn stop(self) -> impl Future<Output = ()> {
1210 self.inner.stop()
1211 }
1212}
1213
1214#[crate::ram]
1215#[crate::handler]
1216fn ecc_work_queue_handler() {
1217 if !ECC_WORK_QUEUE.process() {
1218 cfg_select! {
1221 any(esp32c5, esp32c61) => {
1222 let reg = ECC::regs().int_clr();
1223 }
1224 _ => {
1225 let reg = ECC::regs().mult_int_clr();
1226 }
1227 }
1228 reg.write(|w| w.calc_done().clear_bit_by_one());
1229 }
1230}
1231
1232pub struct EccBackendOperation<'op, O: EccOperation> {
1234 frontend: WorkQueueFrontend<EccWorkItem>,
1235 _marker: PhantomData<(&'op mut (), O)>,
1236}
1237
1238impl<'op, O: EccOperation> EccBackendOperation<'op, O> {
1239 fn new(work_item: EccWorkItem) -> Self {
1240 Self {
1241 frontend: WorkQueueFrontend::new(work_item),
1242 _marker: PhantomData,
1243 }
1244 }
1245
1246 pub fn with_scalar_result(mut self, out: &'op mut [u8]) -> Result<Self, OperationError>
1254 where
1255 O: OperationReturnsScalar,
1256 {
1257 self.frontend.data().curve.size_check([out])?;
1258
1259 self.frontend
1260 .data_mut()
1261 .outputs
1262 .set_scalar(O::LOCATION, NonNull::from(out));
1263
1264 Ok(self)
1265 }
1266
1267 pub fn with_affine_point_result(
1275 mut self,
1276 px: &'op mut [u8],
1277 py: &'op mut [u8],
1278 ) -> Result<Self, OperationError>
1279 where
1280 O: OperationReturnsAffinePoint,
1281 {
1282 self.frontend.data().curve.size_check([px, py])?;
1283
1284 self.frontend.data_mut().outputs.set_px(NonNull::from(px));
1285 self.frontend.data_mut().outputs.set_py(NonNull::from(py));
1286
1287 Ok(self)
1288 }
1289
1290 pub fn with_jacobian_point_result(
1298 mut self,
1299 qx: &'op mut [u8],
1300 qy: &'op mut [u8],
1301 qz: &'op mut [u8],
1302 ) -> Result<Self, OperationError>
1303 where
1304 O: OperationReturnsJacobianPoint,
1305 {
1306 self.frontend.data().curve.size_check([qx, qy, qz])?;
1307
1308 self.frontend.data_mut().outputs.set_qx(NonNull::from(qx));
1309 self.frontend.data_mut().outputs.set_qy(NonNull::from(qy));
1310 self.frontend.data_mut().outputs.set_qz(NonNull::from(qz));
1311
1312 Ok(self)
1313 }
1314
1315 pub fn point_on_curve(&self) -> bool
1319 where
1320 O: OperationVerifiesPoint,
1321 {
1322 self.frontend.data().point_verification_result
1323 }
1324
1325 pub fn process(&mut self) -> EccHandle<'_> {
1330 EccHandle(self.frontend.post(&ECC_WORK_QUEUE))
1331 }
1332}
1333
1334#[must_use]
1336pub struct EccHandle<'t>(Handle<'t, EccWorkItem>);
1337
1338impl EccHandle<'_> {
1339 #[inline]
1343 pub fn poll(&mut self) -> bool {
1344 self.0.poll()
1345 }
1346
1347 #[inline]
1351 pub fn wait_blocking(self) -> Status {
1352 self.0.wait_blocking()
1353 }
1354
1355 #[inline]
1357 pub fn wait(&mut self) -> impl Future<Output = Status> {
1358 self.0.wait()
1359 }
1360
1361 #[inline]
1363 pub fn cancel(&mut self) -> impl Future<Output = ()> {
1364 self.0.cancel()
1365 }
1366}