p256/
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::{P256Coord, P256Point, P256Scalar},
24    EncodedPoint, NistP256,
25};
26
27// --- Implement elliptic_curve traits on P256Point ---
28
29/// P256 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<NistP256>;
33
34impl FieldBytesEncoding<NistP256> 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 P256Point {
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 P256Point {}
57
58impl ConditionallySelectable for P256Point {
59    fn conditional_select(a: &P256Point, b: &P256Point, choice: Choice) -> P256Point {
60        unsafe {
61            P256Point::from_xy_unchecked(
62                P256Coord::conditional_select(
63                    <Self as WeierstrassPoint>::x(a),
64                    <Self as WeierstrassPoint>::x(b),
65                    choice,
66                ),
67                P256Coord::conditional_select(a.y(), b.y(), choice),
68            )
69        }
70    }
71}
72
73impl ConstantTimeEq for P256Point {
74    fn ct_eq(&self, other: &P256Point) -> Choice {
75        <Self as WeierstrassPoint>::x(self).ct_eq(<Self as WeierstrassPoint>::x(other))
76            & self.y().ct_eq(other.y())
77    }
78}
79
80impl Default for P256Point {
81    fn default() -> Self {
82        <Self as WeierstrassPoint>::IDENTITY
83    }
84}
85
86impl DefaultIsZeroes for P256Point {}
87
88impl Sum for P256Point {
89    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
90        iter.fold(<Self as WeierstrassPoint>::IDENTITY, |a, b| a + b)
91    }
92}
93
94impl<'a> Sum<&'a P256Point> for P256Point {
95    fn sum<I: Iterator<Item = &'a P256Point>>(iter: I) -> Self {
96        iter.cloned().sum()
97    }
98}
99
100impl Mul<P256Scalar> for P256Point {
101    type Output = P256Point;
102
103    fn mul(self, other: P256Scalar) -> P256Point {
104        NistP256::msm(&[other], &[self])
105    }
106}
107
108impl Mul<&P256Scalar> for &P256Point {
109    type Output = P256Point;
110
111    fn mul(self, other: &P256Scalar) -> P256Point {
112        NistP256::msm(&[*other], &[*self])
113    }
114}
115
116impl Mul<&P256Scalar> for P256Point {
117    type Output = P256Point;
118
119    fn mul(self, other: &P256Scalar) -> P256Point {
120        NistP256::msm(&[*other], &[self])
121    }
122}
123
124impl MulAssign<P256Scalar> for P256Point {
125    fn mul_assign(&mut self, rhs: P256Scalar) {
126        *self = NistP256::msm(&[rhs], &[*self]);
127    }
128}
129
130impl MulAssign<&P256Scalar> for P256Point {
131    fn mul_assign(&mut self, rhs: &P256Scalar) {
132        *self = NistP256::msm(&[*rhs], &[*self]);
133    }
134}
135
136impl elliptic_curve::Group for P256Point {
137    type Scalar = P256Scalar;
138
139    fn random(mut _rng: impl RngCore) -> Self {
140        // Self::GENERATOR * Self::Scalar::random(&mut rng)
141        unimplemented!()
142    }
143
144    fn identity() -> Self {
145        <Self as WeierstrassPoint>::IDENTITY
146    }
147
148    fn generator() -> Self {
149        Self::GENERATOR
150    }
151
152    fn is_identity(&self) -> Choice {
153        (<Self as openvm_ecc_guest::Group>::is_identity(self) as u8).into()
154    }
155
156    fn double(&self) -> Self {
157        self + self
158    }
159}
160
161impl elliptic_curve::group::Curve for P256Point {
162    type AffineRepr = P256Point;
163
164    fn to_affine(&self) -> P256Point {
165        *self
166    }
167}
168
169impl LinearCombination for P256Point {
170    fn lincomb(x: &Self, k: &Self::Scalar, y: &Self, l: &Self::Scalar) -> Self {
171        NistP256::msm(&[*k, *l], &[*x, *y])
172    }
173}
174
175// default implementation
176impl MulByGenerator for P256Point {}
177
178impl DecompressPoint<NistP256> for P256Point {
179    /// Note that this is not constant time
180    fn decompress(x_bytes: &FieldBytes, y_is_odd: Choice) -> CtOption<Self> {
181        use openvm_ecc_guest::weierstrass::FromCompressed;
182
183        let x = P256Coord::from_be_bytes_unchecked(x_bytes.as_slice());
184        let rec_id = y_is_odd.unwrap_u8();
185        CtOption::new(x, (x.is_reduced() as u8).into()).and_then(|x| {
186            let y = <P256Point as FromCompressed<P256Coord>>::decompress(x, &rec_id);
187            match y {
188                Some(point) => CtOption::new(point, 1.into()),
189                None => CtOption::new(P256Point::default(), 0.into()),
190            }
191        })
192    }
193}
194
195impl DecompactPoint<NistP256> for P256Point {
196    fn decompact(x_bytes: &FieldBytes) -> CtOption<Self> {
197        Self::decompress(x_bytes, Choice::from(0))
198    }
199}
200
201impl FromEncodedPoint<NistP256> for P256Point {
202    /// Attempts to parse the given [`EncodedPoint`] as an SEC1-encoded [`P256Point`].
203    ///
204    /// # Returns
205    ///
206    /// `None` value if `encoded_point` is not on the secp256k1 curve.
207    fn from_encoded_point(encoded_point: &EncodedPoint) -> CtOption<Self> {
208        match openvm_ecc_guest::ecdsa::VerifyingKey::<NistP256>::from_sec1_bytes(
209            encoded_point.as_bytes(),
210        ) {
211            Ok(verifying_key) => CtOption::new(*verifying_key.as_affine(), 1.into()),
212            Err(_) => CtOption::new(P256Point::default(), 0.into()),
213        }
214    }
215}
216
217impl ToEncodedPoint<NistP256> for P256Point {
218    fn to_encoded_point(&self, compress: bool) -> EncodedPoint {
219        EncodedPoint::conditional_select(
220            &EncodedPoint::from_affine_coordinates(
221                &<Self as WeierstrassPoint>::x(self).to_be_bytes().into(),
222                &<Self as WeierstrassPoint>::y(self).to_be_bytes().into(),
223                compress,
224            ),
225            &EncodedPoint::identity(),
226            elliptic_curve::Group::is_identity(self),
227        )
228    }
229}
230
231impl TryFrom<EncodedPoint> for P256Point {
232    type Error = elliptic_curve::Error;
233
234    fn try_from(point: EncodedPoint) -> elliptic_curve::Result<P256Point> {
235        P256Point::try_from(&point)
236    }
237}
238
239impl TryFrom<&EncodedPoint> for P256Point {
240    type Error = elliptic_curve::Error;
241
242    fn try_from(point: &EncodedPoint) -> elliptic_curve::Result<P256Point> {
243        Option::from(P256Point::from_encoded_point(point)).ok_or(elliptic_curve::Error)
244    }
245}
246
247impl From<P256Point> for EncodedPoint {
248    fn from(affine_point: P256Point) -> EncodedPoint {
249        EncodedPoint::from(&affine_point)
250    }
251}
252
253impl From<&P256Point> for EncodedPoint {
254    fn from(affine_point: &P256Point) -> EncodedPoint {
255        affine_point.to_encoded_point(true)
256    }
257}