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