k256/
point.rs

1use core::{
2    iter::Sum,
3    ops::{Mul, MulAssign},
4};
5
6use elliptic_curve::{
7    bigint::{ArrayEncoding, U256},
8    ops::{LinearCombination, MulByGenerator},
9    point::{AffineCoordinates, DecompactPoint, DecompressPoint},
10    rand_core::RngCore,
11    sec1::{FromEncodedPoint, ToEncodedPoint},
12    subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
13    zeroize::DefaultIsZeroes,
14    FieldBytesEncoding,
15};
16use openvm_algebra_guest::IntMod;
17use openvm_ecc_guest::{
18    weierstrass::{IntrinsicCurve, WeierstrassPoint},
19    CyclicGroup,
20};
21
22use crate::{
23    internal::{Secp256k1Coord, Secp256k1Point, Secp256k1Scalar},
24    EncodedPoint, Secp256k1,
25};
26
27// --- Implement elliptic_curve traits on Secp256k1Point ---
28
29/// secp256k1 (K-256) field element serialized as bytes.
30///
31/// Byte array containing a serialized field element value (base field or scalar).
32pub type FieldBytes = elliptic_curve::FieldBytes<Secp256k1>;
33
34impl FieldBytesEncoding<Secp256k1> for U256 {
35    fn decode_field_bytes(field_bytes: &FieldBytes) -> Self {
36        U256::from_be_byte_array(*field_bytes)
37    }
38
39    fn encode_field_bytes(&self) -> FieldBytes {
40        self.to_be_byte_array()
41    }
42}
43
44impl AffineCoordinates for Secp256k1Point {
45    type FieldRepr = FieldBytes;
46
47    fn x(&self) -> FieldBytes {
48        *FieldBytes::from_slice(&<Self as WeierstrassPoint>::x(self).to_be_bytes())
49    }
50
51    fn y_is_odd(&self) -> Choice {
52        (self.y().as_le_bytes()[0] & 1).into()
53    }
54}
55
56impl Copy for Secp256k1Point {}
57
58impl ConditionallySelectable for Secp256k1Point {
59    fn conditional_select(
60        a: &Secp256k1Point,
61        b: &Secp256k1Point,
62        choice: Choice,
63    ) -> Secp256k1Point {
64        unsafe {
65            Secp256k1Point::from_xy_unchecked(
66                Secp256k1Coord::conditional_select(
67                    <Self as WeierstrassPoint>::x(a),
68                    <Self as WeierstrassPoint>::x(b),
69                    choice,
70                ),
71                Secp256k1Coord::conditional_select(a.y(), b.y(), choice),
72            )
73        }
74    }
75}
76
77impl ConstantTimeEq for Secp256k1Point {
78    fn ct_eq(&self, other: &Secp256k1Point) -> Choice {
79        <Self as WeierstrassPoint>::x(self).ct_eq(<Self as WeierstrassPoint>::x(other))
80            & self.y().ct_eq(other.y())
81    }
82}
83
84impl Default for Secp256k1Point {
85    fn default() -> Self {
86        <Self as WeierstrassPoint>::IDENTITY
87    }
88}
89
90impl DefaultIsZeroes for Secp256k1Point {}
91
92impl Sum for Secp256k1Point {
93    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
94        iter.fold(<Self as WeierstrassPoint>::IDENTITY, |a, b| a + b)
95    }
96}
97
98impl<'a> Sum<&'a Secp256k1Point> for Secp256k1Point {
99    fn sum<I: Iterator<Item = &'a Secp256k1Point>>(iter: I) -> Self {
100        iter.cloned().sum()
101    }
102}
103
104impl Mul<Secp256k1Scalar> for Secp256k1Point {
105    type Output = Secp256k1Point;
106
107    fn mul(self, other: Secp256k1Scalar) -> Secp256k1Point {
108        Secp256k1::msm(&[other], &[self])
109    }
110}
111
112impl Mul<&Secp256k1Scalar> for &Secp256k1Point {
113    type Output = Secp256k1Point;
114
115    fn mul(self, other: &Secp256k1Scalar) -> Secp256k1Point {
116        Secp256k1::msm(&[*other], &[*self])
117    }
118}
119
120impl Mul<&Secp256k1Scalar> for Secp256k1Point {
121    type Output = Secp256k1Point;
122
123    fn mul(self, other: &Secp256k1Scalar) -> Secp256k1Point {
124        Secp256k1::msm(&[*other], &[self])
125    }
126}
127
128impl MulAssign<Secp256k1Scalar> for Secp256k1Point {
129    fn mul_assign(&mut self, rhs: Secp256k1Scalar) {
130        *self = Secp256k1::msm(&[rhs], &[*self]);
131    }
132}
133
134impl MulAssign<&Secp256k1Scalar> for Secp256k1Point {
135    fn mul_assign(&mut self, rhs: &Secp256k1Scalar) {
136        *self = Secp256k1::msm(&[*rhs], &[*self]);
137    }
138}
139
140impl elliptic_curve::Group for Secp256k1Point {
141    type Scalar = Secp256k1Scalar;
142
143    fn random(mut _rng: impl RngCore) -> Self {
144        // Self::GENERATOR * Self::Scalar::random(&mut rng)
145        unimplemented!()
146    }
147
148    fn identity() -> Self {
149        <Self as WeierstrassPoint>::IDENTITY
150    }
151
152    fn generator() -> Self {
153        Self::GENERATOR
154    }
155
156    fn is_identity(&self) -> Choice {
157        (<Self as openvm_ecc_guest::Group>::is_identity(self) as u8).into()
158    }
159
160    fn double(&self) -> Self {
161        self + self
162    }
163}
164
165impl elliptic_curve::group::Curve for Secp256k1Point {
166    type AffineRepr = Secp256k1Point;
167
168    fn to_affine(&self) -> Secp256k1Point {
169        *self
170    }
171}
172
173impl LinearCombination for Secp256k1Point {
174    fn lincomb(x: &Self, k: &Self::Scalar, y: &Self, l: &Self::Scalar) -> Self {
175        Secp256k1::msm(&[*k, *l], &[*x, *y])
176    }
177}
178
179// default implementation
180impl MulByGenerator for Secp256k1Point {}
181
182impl DecompressPoint<Secp256k1> for Secp256k1Point {
183    /// Note that this is not constant time
184    fn decompress(x_bytes: &FieldBytes, y_is_odd: Choice) -> CtOption<Self> {
185        use openvm_ecc_guest::weierstrass::FromCompressed;
186
187        let x = Secp256k1Coord::from_be_bytes_unchecked(x_bytes.as_slice());
188        let rec_id = y_is_odd.unwrap_u8();
189        CtOption::new(x, (x.is_reduced() as u8).into()).and_then(|x| {
190            let y = <Secp256k1Point as FromCompressed<Secp256k1Coord>>::decompress(x, &rec_id);
191            match y {
192                Some(point) => CtOption::new(point, 1.into()),
193                None => CtOption::new(Secp256k1Point::default(), 0.into()),
194            }
195        })
196    }
197}
198
199// Taken from https://docs.rs/k256/latest/src/k256/arithmetic/affine.rs.html#207
200impl DecompactPoint<Secp256k1> for Secp256k1Point {
201    fn decompact(x_bytes: &FieldBytes) -> CtOption<Self> {
202        Self::decompress(x_bytes, Choice::from(0))
203    }
204}
205
206impl FromEncodedPoint<Secp256k1> for Secp256k1Point {
207    /// Attempts to parse the given [`EncodedPoint`] as an SEC1-encoded [`Secp256k1Point`].
208    ///
209    /// # Returns
210    ///
211    /// `None` value if `encoded_point` is not on the secp256k1 curve.
212    fn from_encoded_point(encoded_point: &EncodedPoint) -> CtOption<Self> {
213        match openvm_ecc_guest::ecdsa::VerifyingKey::<Secp256k1>::from_sec1_bytes(
214            encoded_point.as_bytes(),
215        ) {
216            Ok(verifying_key) => CtOption::new(*verifying_key.as_affine(), 1.into()),
217            Err(_) => CtOption::new(Secp256k1Point::default(), 0.into()),
218        }
219    }
220}
221
222impl ToEncodedPoint<Secp256k1> for Secp256k1Point {
223    fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
224        EncodedPoint::conditional_select(
225            &EncodedPoint::from_affine_coordinates(
226                &<Self as WeierstrassPoint>::x(self).to_be_bytes().into(),
227                &<Self as WeierstrassPoint>::y(self).to_be_bytes().into(),
228                compress,
229            ),
230            &EncodedPoint::identity(),
231            elliptic_curve::Group::is_identity(self),
232        )
233    }
234}
235
236impl TryFrom<EncodedPoint> for Secp256k1Point {
237    type Error = elliptic_curve::Error;
238
239    fn try_from(point: EncodedPoint) -> elliptic_curve::Result<Secp256k1Point> {
240        Secp256k1Point::try_from(&point)
241    }
242}
243
244impl TryFrom<&EncodedPoint> for Secp256k1Point {
245    type Error = elliptic_curve::Error;
246
247    fn try_from(point: &EncodedPoint) -> elliptic_curve::Result<Secp256k1Point> {
248        Option::from(Secp256k1Point::from_encoded_point(point)).ok_or(elliptic_curve::Error)
249    }
250}
251
252impl From<Secp256k1Point> for EncodedPoint {
253    fn from(affine_point: Secp256k1Point) -> EncodedPoint {
254        EncodedPoint::from(&affine_point)
255    }
256}
257
258impl From<&Secp256k1Point> for EncodedPoint {
259    fn from(affine_point: &Secp256k1Point) -> EncodedPoint {
260        affine_point.to_encoded_point(true)
261    }
262}