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 let (chunks, _) = out.as_chunks_mut::<4>();
601 for word_bytes in chunks {
602 let word = unsafe { word_ptr.read_volatile() };
603 word_ptr = word_ptr.wrapping_add(1);
604 *word_bytes = word.to_le_bytes();
605 }
606 }
607
608 fn k_mem(&self) -> *mut u32 {
609 self.regs.k_mem(0).as_ptr()
610 }
611
612 fn px_mem(&self) -> *mut u32 {
613 self.regs.px_mem(0).as_ptr()
614 }
615
616 fn py_mem(&self) -> *mut u32 {
617 self.regs.py_mem(0).as_ptr()
618 }
619
620 fn qx_mem(&self) -> *mut u32 {
621 cfg_select! {
622 ecc_separate_jacobian_point_memory => self.regs.qx_mem(0).as_ptr(),
623 _ => self.regs.px_mem(0).as_ptr(),
624 }
625 }
626
627 fn qy_mem(&self) -> *mut u32 {
628 cfg_select! {
629 ecc_separate_jacobian_point_memory => self.regs.qy_mem(0).as_ptr(),
630 _ => self.regs.py_mem(0).as_ptr(),
631 }
632 }
633
634 fn qz_mem(&self) -> *mut u32 {
635 cfg_select! {
636 ecc_separate_jacobian_point_memory => self.regs.qz_mem(0).as_ptr(),
637 _ => self.regs.k_mem(0).as_ptr(),
638 }
639 }
640
641 fn read_point_result(&self, x: &mut [u8], y: &mut [u8]) {
642 self.read_mem(self.px_mem(), x);
643 self.read_mem(self.py_mem(), y);
644 }
645
646 fn read_jacobian_result(&self, qx: &mut [u8], qy: &mut [u8], qz: &mut [u8]) {
647 self.read_mem(self.qx_mem(), qx);
648 self.read_mem(self.qy_mem(), qy);
649 self.read_mem(self.qz_mem(), qz);
650 }
651
652 #[cfg(clear_crypto_secrets)]
654 fn clear_secrets(&self) {
655 self.zero_mem(self.k_mem());
656 self.zero_mem(self.px_mem());
657 self.zero_mem(self.py_mem());
658
659 #[cfg(ecc_separate_jacobian_point_memory)]
660 {
661 self.zero_mem(self.qx_mem());
662 self.zero_mem(self.qy_mem());
663 self.zero_mem(self.qz_mem());
664 }
665 }
666
667 #[cfg(clear_crypto_secrets)]
668 fn zero_mem(&self, mut word_ptr: *mut u32) {
669 for _ in 0..(MEM_BLOCK_SIZE / 4) {
670 unsafe { word_ptr.write_volatile(0) };
671 word_ptr = word_ptr.wrapping_add(1);
672 }
673 }
674
675 fn is_busy(&self) -> bool {
676 self.regs.mult_conf().read().start().bit_is_set()
677 }
678
679 fn start_operation(
680 &self,
681 mode: WorkMode,
682 curve: EllipticCurve,
683 #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
684 ) {
685 let curve_variant;
686 for_each_ecc_curve! {
687 (all $(($_id:tt, $name:ident, $_bits:tt)),*) => {
688 curve_variant = match curve {
689 $(EllipticCurve::$name => KEY_LENGTH::$name,)*
690 }
691 };
692 };
693 self.regs.mult_conf().modify(|_, w| unsafe {
694 w.work_mode().bits(mode as u8);
695 w.key_length().variant(curve_variant);
696
697 #[cfg(ecc_has_modular_arithmetic)]
698 w.mod_base().bit(mod_base as u8 == 1);
699
700 w.start().set_bit()
701 });
702 }
703}
704
705for_each_ecc_working_mode! {
709 (all $(( $id:literal, $mode:tt )),*) => {
710 #[derive(Clone, Copy)]
711 #[doc(hidden)]
712 pub enum WorkMode {
715 $(
716 $mode = $id,
717 )*
718 }
719 };
720}
721
722for_each_ecc_working_mode! {
724 (all $(( $id:literal, $mode:tt )),*) => {
725 $(
726 result_type!($mode);
727 )*
728 };
729}
730
731for_each_ecc_working_mode! {
733 (all $(( $id:literal, $mode:tt )),*) => {
734 impl<'d, Dm: DriverMode> Ecc<'d, Dm> {
735 fn info(&self) -> Info {
736 Info { regs: ECC::regs() }
737 }
738
739 fn run_operation<'op, O: EccOperation>(
740 &'op mut self,
741 curve: EllipticCurve,
742 #[cfg(ecc_has_modular_arithmetic)] mod_base: EccModBase,
743 ) -> EccResultHandle<'op, O> {
744 self.info().start_operation(
745 O::WORK_MODE,
746 curve,
747 #[cfg(ecc_has_modular_arithmetic)] mod_base,
748 );
749
750 while self.info().is_busy() {}
752
753 EccResultHandle::new(curve, self)
754 }
755
756 pub fn apply_config(&mut self, config: &Config) {
758 self.info().apply_config(config);
759 }
760
761 pub fn reset(&mut self) {
763 self.info().reset()
764 }
765
766 #[instability::unstable]
771 pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
772 for core in crate::system::Cpu::other() {
773 crate::interrupt::disable(core, Interrupt::ECC);
774 }
775 crate::interrupt::bind_handler(Interrupt::ECC, handler);
776 }
777
778 $(
779 driver_method!($mode);
780 )*
781 }
782 };
783}
784
785pub trait EccOperation: Sealed {
787 const VERIFIES_POINT: bool;
789
790 #[doc(hidden)]
792 const WORK_MODE: WorkMode;
793}
794
795#[doc(hidden)]
797pub enum ScalarResultLocation {
798 Px,
800 Py,
802 K,
804}
805
806pub trait OperationReturnsScalar: EccOperation {
808 #[doc(hidden)]
810 const LOCATION: ScalarResultLocation;
811}
812
813pub trait OperationReturnsAffinePoint: EccOperation {}
815
816pub trait OperationReturnsJacobianPoint: EccOperation {}
818
819pub trait OperationVerifiesPoint: EccOperation {}
821
822#[must_use]
828pub struct EccResultHandle<'op, O>
829where
830 O: EccOperation,
831{
832 curve: EllipticCurve,
833 info: Info,
834 _marker: PhantomData<(&'op mut (), O)>,
835}
836
837impl<'op, O> EccResultHandle<'op, O>
838where
839 O: EccOperation,
840{
841 fn new<'d, Dm: DriverMode>(curve: EllipticCurve, driver: &'op mut Ecc<'d, Dm>) -> Self {
842 Self {
843 curve,
844 info: driver.info(),
845 _marker: PhantomData,
846 }
847 }
848
849 fn run_checks<const N: usize>(&self, params: [&[u8]; N]) -> Result<(), OperationError> {
850 self.curve.size_check(params)?;
851 if O::VERIFIES_POINT {
852 self.info.check_point_verification_result()?;
853 }
854 Ok(())
855 }
856
857 pub fn success(&self) -> bool {
863 if O::VERIFIES_POINT {
864 self.info.check_point_verification_result().is_ok()
865 } else {
866 true
867 }
868 }
869
870 pub fn read_scalar_result(&self, out: &mut [u8]) -> Result<(), OperationError>
876 where
877 O: OperationReturnsScalar,
878 {
879 self.run_checks([out])?;
880
881 match O::LOCATION {
882 ScalarResultLocation::Px => self.info.read_mem(self.info.px_mem(), out),
883 ScalarResultLocation::Py => self.info.read_mem(self.info.py_mem(), out),
884 ScalarResultLocation::K => self.info.read_mem(self.info.k_mem(), out),
885 }
886
887 Ok(())
888 }
889
890 pub fn read_affine_point_result(&self, x: &mut [u8], y: &mut [u8]) -> Result<(), OperationError>
896 where
897 O: OperationReturnsAffinePoint,
898 {
899 self.run_checks([x, y])?;
900 self.info.read_point_result(x, y);
901 Ok(())
902 }
903
904 pub fn read_jacobian_point_result(
911 &self,
912 x: &mut [u8],
913 y: &mut [u8],
914 z: &mut [u8],
915 ) -> Result<(), OperationError>
916 where
917 O: OperationReturnsJacobianPoint,
918 {
919 self.run_checks([x, y, z])?;
920 self.info.read_jacobian_result(x, y, z);
921 Ok(())
922 }
923}
924
925struct EccWorkItem {
926 curve: EllipticCurve,
927 operation: WorkMode,
928 cancelled: bool,
929 #[cfg(ecc_has_modular_arithmetic)]
930 mod_base: EccModBase,
931 inputs: MemoryPointers,
932 point_verification_result: bool,
933 outputs: MemoryPointers,
934}
935
936#[derive(Default)]
937struct MemoryPointers {
938 k: Option<NonNull<u8>>,
940 px: Option<NonNull<u8>>,
941 py: Option<NonNull<u8>>,
942 #[cfg(ecc_separate_jacobian_point_memory)]
943 qx: Option<NonNull<u8>>,
944 #[cfg(ecc_separate_jacobian_point_memory)]
945 qy: Option<NonNull<u8>>,
946 #[cfg(ecc_separate_jacobian_point_memory)]
947 qz: Option<NonNull<u8>>,
948}
949
950impl MemoryPointers {
951 fn set_scalar(&mut self, location: ScalarResultLocation, ptr: NonNull<[u8]>) {
952 match location {
953 ScalarResultLocation::Px => self.set_px(ptr),
954 ScalarResultLocation::Py => self.set_py(ptr),
955 ScalarResultLocation::K => self.set_k(ptr),
956 }
957 }
958
959 fn set_k(&mut self, ptr: NonNull<[u8]>) {
960 self.k = Some(ptr.cast());
961 }
962
963 fn set_px(&mut self, ptr: NonNull<[u8]>) {
964 self.px = Some(ptr.cast());
965 }
966
967 fn set_py(&mut self, ptr: NonNull<[u8]>) {
968 self.py = Some(ptr.cast());
969 }
970
971 fn set_qx(&mut self, ptr: NonNull<[u8]>) {
972 cfg_select! {
973 ecc_separate_jacobian_point_memory => {
974 self.qx = Some(ptr.cast());
975 }
976 _ => {
977 self.px = Some(ptr.cast());
978 }
979 }
980 }
981
982 fn set_qy(&mut self, ptr: NonNull<[u8]>) {
983 cfg_select! {
984 ecc_separate_jacobian_point_memory => {
985 self.qy = Some(ptr.cast());
986 }
987 _ => {
988 self.py = Some(ptr.cast());
989 }
990 }
991 }
992
993 fn set_qz(&mut self, ptr: NonNull<[u8]>) {
994 cfg_select! {
995 ecc_separate_jacobian_point_memory => {
996 self.qz = Some(ptr.cast());
997 }
998 _ => {
999 self.k = Some(ptr.cast());
1000 }
1001 }
1002 }
1003}
1004
1005unsafe impl Sync for MemoryPointers {}
1010unsafe impl Send for MemoryPointers {}
1012
1013static ECC_WORK_QUEUE: WorkQueue<EccWorkItem> = WorkQueue::new();
1014
1015const ECC_VTABLE: VTable<EccWorkItem> = VTable {
1016 post: |driver, item| {
1017 let driver = unsafe { EccBackend::from_raw(driver) };
1018
1019 if let DriverState::Uninitialized(ecc) = &driver.driver {
1021 let mut ecc = Ecc::new(unsafe { ecc.clone_unchecked() }, driver.config);
1022 ecc.set_interrupt_handler(ecc_work_queue_handler);
1023 driver.driver = DriverState::Initialized(ecc);
1024 };
1025
1026 Some(driver.process(item))
1027 },
1028 poll: |driver, item| {
1029 let driver = unsafe { EccBackend::from_raw(driver) };
1030 driver.poll(item)
1031 },
1032 cancel: |driver, item| {
1033 let driver = unsafe { EccBackend::from_raw(driver) };
1034 driver.cancel(item);
1035 },
1036 stop: |driver| {
1037 let driver = unsafe { EccBackend::from_raw(driver) };
1038 driver.deinitialize()
1039 },
1040};
1041
1042enum DriverState<'d> {
1043 Uninitialized(ECC<'d>),
1044 Initialized(Ecc<'d, Blocking>),
1045}
1046
1047pub struct EccBackend<'d> {
1051 driver: DriverState<'d>,
1052 config: Config,
1053}
1054
1055impl<'d> EccBackend<'d> {
1056 pub fn new(ecc: ECC<'d>, config: Config) -> Self {
1060 Self {
1061 driver: DriverState::Uninitialized(ecc),
1062 config,
1063 }
1064 }
1065
1066 pub fn start(&mut self) -> EccWorkQueueDriver<'_, 'd> {
1070 EccWorkQueueDriver {
1071 inner: WorkQueueDriver::new(self, ECC_VTABLE, &ECC_WORK_QUEUE),
1072 }
1073 }
1074
1075 unsafe fn from_raw<'any>(ptr: NonNull<()>) -> &'any mut Self {
1078 unsafe { ptr.cast::<EccBackend<'_>>().as_mut() }
1079 }
1080
1081 fn process(&mut self, item: &mut EccWorkItem) -> Poll {
1082 let DriverState::Initialized(driver) = &mut self.driver else {
1083 unreachable!()
1084 };
1085
1086 let bytes = item.curve.size();
1087
1088 macro_rules! set_input {
1089 ($input:ident, $input_mem:ident) => {
1090 if let Some($input) = item.inputs.$input {
1091 driver.info().write_mem(driver.info().$input_mem(), unsafe {
1092 core::slice::from_raw_parts($input.as_ptr(), bytes)
1093 });
1094 }
1095 };
1096 }
1097
1098 set_input!(k, k_mem);
1099 set_input!(px, px_mem);
1100 set_input!(py, py_mem);
1101
1102 #[cfg(ecc_separate_jacobian_point_memory)]
1103 {
1104 set_input!(qx, qx_mem);
1105 set_input!(qy, qy_mem);
1106 set_input!(qz, qz_mem);
1107 }
1108
1109 driver.info().start_operation(
1110 item.operation,
1111 item.curve,
1112 #[cfg(ecc_has_modular_arithmetic)]
1113 item.mod_base,
1114 );
1115 Poll::Pending(false)
1116 }
1117
1118 fn poll(&mut self, item: &mut EccWorkItem) -> Poll {
1119 let DriverState::Initialized(driver) = &mut self.driver else {
1120 unreachable!()
1121 };
1122
1123 if driver.info().is_busy() {
1124 return Poll::Pending(false);
1125 }
1126 if item.cancelled {
1127 return Poll::Ready(Status::Cancelled);
1128 }
1129
1130 let bytes = item.curve.size();
1131
1132 macro_rules! read_output {
1133 ($output:ident, $output_mem:ident) => {
1134 if let Some($output) = item.outputs.$output {
1135 driver.info().read_mem(driver.info().$output_mem(), unsafe {
1136 core::slice::from_raw_parts_mut($output.as_ptr(), bytes)
1137 });
1138 }
1139 };
1140 }
1141
1142 read_output!(k, k_mem);
1143 read_output!(px, px_mem);
1144 read_output!(py, py_mem);
1145
1146 #[cfg(ecc_separate_jacobian_point_memory)]
1147 {
1148 read_output!(qx, qx_mem);
1149 read_output!(qy, qy_mem);
1150 read_output!(qz, qz_mem);
1151 }
1152
1153 item.point_verification_result = driver.info().check_point_verification_result().is_ok();
1154
1155 #[cfg(clear_crypto_secrets)]
1156 driver.info().clear_secrets();
1157
1158 Poll::Ready(Status::Completed)
1159 }
1160
1161 fn cancel(&mut self, item: &mut EccWorkItem) {
1162 let DriverState::Initialized(driver) = &mut self.driver else {
1163 unreachable!()
1164 };
1165 driver.reset();
1166
1167 #[cfg(clear_crypto_secrets)]
1169 driver.info().clear_secrets();
1170
1171 item.cancelled = true;
1172 }
1173
1174 fn deinitialize(&mut self) {
1175 if let DriverState::Initialized(ref ecc) = self.driver {
1176 self.driver = DriverState::Uninitialized(unsafe { ecc._ecc.clone_unchecked() });
1177 }
1178 }
1179}
1180
1181pub struct EccWorkQueueDriver<'t, 'd> {
1185 inner: WorkQueueDriver<'t, EccBackend<'d>, EccWorkItem>,
1186}
1187
1188impl<'t, 'd> EccWorkQueueDriver<'t, 'd> {
1189 pub fn stop(self) -> impl Future<Output = ()> {
1191 self.inner.stop()
1192 }
1193}
1194
1195#[crate::ram]
1196#[crate::handler]
1197fn ecc_work_queue_handler() {
1198 if !ECC_WORK_QUEUE.process() {
1199 cfg_select! {
1202 any(esp32c5, esp32c61) => {
1203 let reg = ECC::regs().int_clr();
1204 }
1205 _ => {
1206 let reg = ECC::regs().mult_int_clr();
1207 }
1208 }
1209 reg.write(|w| w.calc_done().clear_bit_by_one());
1210 }
1211}
1212
1213pub struct EccBackendOperation<'op, O: EccOperation> {
1215 frontend: WorkQueueFrontend<EccWorkItem>,
1216 _marker: PhantomData<(&'op mut (), O)>,
1217}
1218
1219impl<'op, O: EccOperation> EccBackendOperation<'op, O> {
1220 fn new(work_item: EccWorkItem) -> Self {
1221 Self {
1222 frontend: WorkQueueFrontend::new(work_item),
1223 _marker: PhantomData,
1224 }
1225 }
1226
1227 pub fn with_scalar_result(mut self, out: &'op mut [u8]) -> Result<Self, KeyLengthMismatch>
1235 where
1236 O: OperationReturnsScalar,
1237 {
1238 self.frontend.data().curve.size_check([out])?;
1239
1240 self.frontend
1241 .data_mut()
1242 .outputs
1243 .set_scalar(O::LOCATION, NonNull::from(out));
1244
1245 Ok(self)
1246 }
1247
1248 pub fn with_affine_point_result(
1256 mut self,
1257 px: &'op mut [u8],
1258 py: &'op mut [u8],
1259 ) -> Result<Self, KeyLengthMismatch>
1260 where
1261 O: OperationReturnsAffinePoint,
1262 {
1263 self.frontend.data().curve.size_check([px, py])?;
1264
1265 self.frontend.data_mut().outputs.set_px(NonNull::from(px));
1266 self.frontend.data_mut().outputs.set_py(NonNull::from(py));
1267
1268 Ok(self)
1269 }
1270
1271 pub fn with_jacobian_point_result(
1279 mut self,
1280 qx: &'op mut [u8],
1281 qy: &'op mut [u8],
1282 qz: &'op mut [u8],
1283 ) -> Result<Self, KeyLengthMismatch>
1284 where
1285 O: OperationReturnsJacobianPoint,
1286 {
1287 self.frontend.data().curve.size_check([qx, qy, qz])?;
1288
1289 self.frontend.data_mut().outputs.set_qx(NonNull::from(qx));
1290 self.frontend.data_mut().outputs.set_qy(NonNull::from(qy));
1291 self.frontend.data_mut().outputs.set_qz(NonNull::from(qz));
1292
1293 Ok(self)
1294 }
1295
1296 pub fn point_on_curve(&self) -> bool
1300 where
1301 O: OperationVerifiesPoint,
1302 {
1303 self.frontend.data().point_verification_result
1304 }
1305
1306 pub fn process(&mut self) -> EccHandle<'_> {
1311 EccHandle(self.frontend.post(&ECC_WORK_QUEUE))
1312 }
1313}
1314
1315#[must_use]
1317pub struct EccHandle<'t>(Handle<'t, EccWorkItem>);
1318
1319impl EccHandle<'_> {
1320 #[inline]
1324 pub fn poll(&mut self) -> bool {
1325 self.0.poll()
1326 }
1327
1328 #[inline]
1332 pub fn wait_blocking(self) -> Status {
1333 self.0.wait_blocking()
1334 }
1335
1336 #[inline]
1338 pub fn wait(&mut self) -> impl Future<Output = Status> {
1339 self.0.wait()
1340 }
1341
1342 #[inline]
1344 pub fn cancel(&mut self) -> impl Future<Output = ()> {
1345 self.0.cancel()
1346 }
1347}