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 curve.
6//!
7//! This implementation is specifically for use inside of SP1, and internally uses SP1's Weierstrass precompiles.
8//! Weierstrass precompiles.
9//!
10//! In summary, SP1 overrides curve arithmetic entirely, and patches upstream field operations
11//! to be more efficient in the VM, such as `sqrt` or `inverse`.
12
13use crate::utils::AffinePoint as SP1AffinePointTrait;
14
15use elliptic_curve::{
16    ff, generic_array::typenum::consts::U32, subtle::CtOption, CurveArithmetic, FieldBytes,
17};
18use std::fmt::Debug;
19use std::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 context.
43///
44/// Note: This trait only supports 32 byte base field curves.
45pub trait ECDSACurve
46where
47    Self: CurveArithmetic<
48        FieldBytesSize = FIELD_BYTES_SIZE,
49        AffinePoint = AffinePoint<Self>,
50        ProjectivePoint = ProjectivePoint<Self>,
51    >,
52{
53    type FieldElement: Field<Self> + Neg<Output = Self::FieldElement>;
54
55    /// The underlying [`SP1AffinePointTrait`] implementation.
56    type SP1AffinePoint: ECDSAPoint;
57
58    /// The `a` coefficient in the curve equation.
59    const EQUATION_A: Self::FieldElement;
60
61    /// The `b` coefficient in the curve equation.
62    const EQUATION_B: Self::FieldElement;
63}
64
65/// Alias trait for the [`ff::PrimeField`] with 32 byte field elements.
66///
67/// Note: All bytes should be considered to be in big-endian format.
68pub trait Field<C: ECDSACurve>: ff::PrimeField {
69    /// Create an instance of self from a FieldBytes.
70    fn from_bytes(bytes: &FieldBytes<C>) -> CtOption<Self>;
71
72    /// Convert self to a FieldBytes.
73    ///
74    /// Note: Implementers should ensure these methods normalize first.
75    fn to_bytes(self) -> FieldBytes<C>;
76
77    /// Ensure the field element is normalized.
78    fn normalize(self) -> Self;
79}
80
81pub type FieldElement<C> = <C as ECDSACurve>::FieldElement;
82
83/// Alias trait for the [`SP1AffinePointTrait`] with 32 byte field elements.
84pub trait ECDSAPoint:
85    SP1AffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
86{
87    #[inline]
88    fn from(x: &[u8], y: &[u8]) -> Self {
89        <Self as SP1AffinePointTrait<POINT_LIMBS>>::from(x, y)
90    }
91}
92
93impl<P> ECDSAPoint for P where
94    P: SP1AffinePointTrait<POINT_LIMBS> + Clone + Copy + Debug + Send + Sync
95{
96}
97
98pub mod ecdh {
99    pub use elliptic_curve::ecdh::{diffie_hellman, EphemeralSecret, SharedSecret};
100
101    use super::{AffinePoint, ECDSACurve, Field};
102
103    impl<C: ECDSACurve> From<&AffinePoint<C>> for SharedSecret<C> {
104        fn from(affine: &AffinePoint<C>) -> SharedSecret<C> {
105            let (x, _) = affine.field_elements();
106
107            x.to_bytes().into()
108        }
109    }
110}