sp1_lib/ecdsa.rs
1//! An implementation of the types needed for [`CurveArithmetic`].
2//!
3//! [`CurveArithmetic`] is a trait that is used in [RustCryptos ECDSA](https://github.com/RustCrypto/signatures).
4//!
5//! [`CurveArithmetic`] contains all the types needed to implement the ECDSA algorithm over some
6//! curve.
7//!
8//! This implementation is specifically for use inside of SP1, and internally uses SP1's Weierstrass
9//! precompiles. Weierstrass precompiles.
10//!
11//! In summary, SP1 overrides curve arithmetic entirely, and patches upstream field operations
12//! to be more efficient in the VM, such as `sqrt` or `inverse`.
13
14use crate::utils::AffinePoint as SP1AffinePointTrait;
15
16use elliptic_curve::{
17 ff, generic_array::typenum::consts::U32, subtle::CtOption, CurveArithmetic, FieldBytes,
18};
19use std::{fmt::Debug, ops::Neg};
20
21/// The affine point type for SP1.
22pub mod affine;
23pub use affine::AffinePoint;
24
25/// The projective point type for SP1.
26pub mod projective;
27pub use projective::ProjectivePoint;
28
29/// NOTE: The only supported ECDSA curves are secp256k1 and secp256r1, which both
30/// have 8 limbs in their field elements.
31const POINT_LIMBS: usize = 8 * 2;
32
33/// The number of bytes in a field element as an [`usize`].
34const FIELD_BYTES_SIZE_USIZE: usize = 32;
35
36/// The number of bytes in a field element as an [`elliptic_curve::generic_array::U32`].
37#[allow(non_camel_case_types)]
38type FIELD_BYTES_SIZE = U32;
39
40/// A [`CurveArithmetic`] extension for SP1 acceleration.
41///
42/// Patched crates implement this trait to take advantage of SP1-specific acceleration in the zkVM
43/// context.
44///
45/// Note: This trait only supports 32 byte base field curves.
46pub trait ECDSACurve
47where
48 Self: CurveArithmetic<
49 FieldBytesSize = FIELD_BYTES_SIZE,
50 AffinePoint = AffinePoint<Self>,
51 ProjectivePoint = ProjectivePoint<Self>,
52 >,
53{
54 type FieldElement: Field<Self> + Neg<Output = Self::FieldElement>;
55
56 /// The underlying [`SP1AffinePointTrait`] implementation.
57 type SP1AffinePoint: ECDSAPoint;
58
59 /// The `a` coefficient in the curve equation.
60 const EQUATION_A: Self::FieldElement;
61
62 /// The `b` coefficient in the curve equation.
63 const EQUATION_B: Self::FieldElement;
64}
65
66/// Alias trait for the [`ff::PrimeField`] with 32 byte field elements.
67///
68/// Note: All bytes should be considered to be in big-endian format.
69pub trait Field<C: ECDSACurve>: ff::PrimeField {
70 /// Create an instance of self from a FieldBytes.
71 fn from_bytes(bytes: &FieldBytes<C>) -> CtOption<Self>;
72
73 /// Convert self to a FieldBytes.
74 ///
75 /// Note: Implementers should ensure these methods normalize first.
76 fn to_bytes(self) -> FieldBytes<C>;
77
78 /// Ensure the field element is normalized.
79 fn normalize(self) -> Self;
80}
81
82pub type FieldElement<C> = <C as ECDSACurve>::FieldElement;
83
84/// Alias trait for the [`SP1AffinePointTrait`] with 32 byte field elements.
85pub trait ECDSAPoint:
86 SP1AffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
87{
88 #[inline]
89 fn from(x: &[u8], y: &[u8]) -> Self {
90 <Self as SP1AffinePointTrait<POINT_LIMBS>>::from(x, y)
91 }
92}
93
94impl<P> ECDSAPoint for P where
95 P: SP1AffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
96{
97}
98
99pub mod ecdh {
100 pub use elliptic_curve::ecdh::{diffie_hellman, EphemeralSecret, SharedSecret};
101
102 use super::{AffinePoint, ECDSACurve, Field};
103
104 impl<C: ECDSACurve> From<&AffinePoint<C>> for SharedSecret<C> {
105 fn from(affine: &AffinePoint<C>) -> SharedSecret<C> {
106 let (x, _) = affine.field_elements();
107
108 x.to_bytes().into()
109 }
110 }
111}