solana_secp256k1/
uncompressed_point.rs

1use core::{fmt::{Debug, Formatter}, ops::Add};
2
3use dashu::integer::UBig;
4use solana_nostd_secp256k1_recover::secp256k1_recover;
5
6#[cfg(feature="big-mod-exp")]
7use solana_nostd_big_mod_exp::big_mod_exp;
8
9
10use crate::{CompressedPoint, Curve, Secp256k1Error, Secp256k1Point};
11
12pub const SEC1_OCTET_UNCOMPRESSED: u8 = 0x04;
13
14#[derive(PartialEq, Eq, Clone, Copy)]
15pub struct UncompressedPoint(pub [u8; Self::SIZE]);
16
17impl Secp256k1Point for UncompressedPoint {
18    const SIZE: usize = 64;
19
20    fn is_odd(&self) -> bool {
21        self.0[63] & 1 != 0
22    }
23
24    fn is_even(&self) -> bool {
25        self.0[63] & 1 != 1
26    }
27
28    fn x(&self) -> [u8; 32] {
29        [
30            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
31            self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14],
32            self.0[15], self.0[16], self.0[17], self.0[18], self.0[19], self.0[20], self.0[21],
33            self.0[22], self.0[23], self.0[24], self.0[25], self.0[26], self.0[27], self.0[28],
34            self.0[29], self.0[30], self.0[31],
35        ]
36    }
37
38    fn y(&self) -> [u8; 32] {
39        [
40            self.0[32], self.0[33], self.0[34], self.0[35], self.0[36], self.0[37], self.0[38],
41            self.0[39], self.0[40], self.0[41], self.0[42], self.0[43], self.0[44], self.0[45],
42            self.0[46], self.0[47], self.0[48], self.0[49], self.0[50], self.0[51], self.0[52],
43            self.0[53], self.0[54], self.0[55], self.0[56], self.0[57], self.0[58], self.0[59],
44            self.0[60], self.0[61], self.0[62], self.0[63],
45        ]
46    }
47
48    #[cfg(feature="big-mod-exp")]
49    fn lift_x(x: &[u8; 32]) -> Result<Self, Secp256k1Error> {
50        // y^2 = x^3 + 7 mod P
51        let x_3 = (&UBig::from_be_bytes(x).pow(3) + UBig::from_word(7)) % &UBig::from_be_bytes(&Curve::P);
52        // Use big_mod_exp for cheap cubed root
53        let y = big_mod_exp(&x_3.to_be_bytes(), &Curve::P_1_4, &Curve::P);
54        if (&UBig::from_be_bytes(&y).pow(2) % &UBig::from_be_bytes(&Curve::P)) != x_3 {
55            return Err(Secp256k1Error::InvalidYCoordinate);
56        }
57        let mut x_y = [0u8; 64];
58        x_y[..32].clone_from_slice(x);
59        x_y[32..].clone_from_slice(&y);
60        Ok(Self(x_y))
61    }
62
63    #[cfg(feature="big-mod-exp")]
64    fn lift_x_unchecked(x: &[u8; 32]) -> Self {
65        // We first compute y^2 = x^3 + 7 mod P
66        let x_3 = (&UBig::from_be_bytes(x).pow(3) + UBig::from_word(7)) % &UBig::from_be_bytes(&Curve::P);
67        // Use big_mod_exp for cheap cubed root
68        let y = big_mod_exp(&x_3.to_be_bytes(), &Curve::P_1_4, &Curve::P);
69        let mut x_y = [0u8; 64];
70        x_y[..32].clone_from_slice(x);
71        x_y[32..].clone_from_slice(&y);
72        Self(x_y)
73    }
74
75    #[cfg(not(feature="big-mod-exp"))]
76    fn lift_x(x: &[u8; 32]) -> Result<Self, Secp256k1Error> {
77        Curve::lift_x(x)
78    }
79
80    #[cfg(not(feature="big-mod-exp"))]
81    fn lift_x_unchecked(x: &[u8; 32]) -> Self {
82        Curve::lift_x_unchecked(x)
83    }
84
85    fn invert(&mut self) {
86        let y = (UBig::from_be_bytes(&Curve::P) - UBig::from_be_bytes(&self.y())).to_be_bytes();
87        self.0[32..64].clone_from_slice(&[0u8;32]);
88        self.0[64-y.len()..].clone_from_slice(&y);
89    }
90    
91    fn compress(&self) -> CompressedPoint {
92        CompressedPoint::from(*self)
93    }
94    
95    fn decompress(&self) -> UncompressedPoint {
96        *self
97    }
98
99    fn tweak(&self, tweak: [u8; 32]) -> Result<Self, Secp256k1Error> {
100        // Compute z = (-r * k) mod N
101        let z_scalar = ((UBig::from_be_bytes(&Curve::negate_n(&self.x())) * UBig::from_be_bytes(&tweak)) % UBig::from_be_bytes(&Curve::N)).to_be_bytes();
102               
103        // Ensure z and s are 32 bytes
104        let mut z = [0u8; 32];
105        z[32 - z_scalar.len()..].copy_from_slice(&z_scalar);
106
107        let s: [u8; 64] = [
108            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
109            self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15],
110            self.0[16], self.0[17], self.0[18], self.0[19], self.0[20], self.0[21], self.0[22], self.0[23],
111            self.0[24], self.0[25], self.0[26], self.0[27], self.0[28], self.0[29], self.0[30], self.0[31],
112            self.0[0], self.0[1], self.0[2], self.0[3], self.0[4], self.0[5], self.0[6], self.0[7],
113            self.0[8], self.0[9], self.0[10], self.0[11], self.0[12], self.0[13], self.0[14], self.0[15],
114            self.0[16], self.0[17], self.0[18], self.0[19], self.0[20], self.0[21], self.0[22], self.0[23],
115            self.0[24], self.0[25], self.0[26], self.0[27], self.0[28], self.0[29], self.0[30], self.0[31],
116        ];
117
118        // Use ecrecover with negated z to perform ECAdd
119        Ok(UncompressedPoint(secp256k1_recover(&z, self.is_odd(), &s)?))
120    }
121}
122
123impl Add<UncompressedPoint> for UncompressedPoint {
124    type Output = UncompressedPoint;
125
126    fn add(self, rhs: UncompressedPoint) -> Self::Output {
127        let rhs: UncompressedPoint = rhs.decompress();
128        let p = UBig::from_be_bytes(&Curve::P); // The modulus
129
130        // Convert [u8; 32] to UBig
131        let x_p = UBig::from_be_bytes(&self.x());
132        let y_p = UBig::from_be_bytes(&self.y());
133        let x_q = UBig::from_be_bytes(&rhs.x());
134        let y_q = UBig::from_be_bytes(&rhs.y());
135
136        // Calculate modular inverse using big_mod_exp
137        let inv = Curve::mod_inv_p(&(&x_q - &x_p).to_be_bytes()).expect("This shouldn't fail");
138        let inv = UBig::from_be_bytes(&inv);
139
140        // m = (y_q - y_p) * modinv(x_q - x_p, p)
141        let m = (&y_q + &p - &y_p) * inv % &p;
142
143        // xr = m^2 - x_p - x_q
144        let xr = (&m * &m + &p - &x_p - &x_q) % &p;
145
146        // yr = m * (x_p - xr) - y_p
147        let yr = (&m * (&x_p + &p - &xr) + &p - &y_p) % &p;
148
149        // Convert results back to [u8; 32]
150        let mut result_x = [0u8; 32];
151        let mut result_y = [0u8; 32];
152        result_x.copy_from_slice(&xr.to_be_bytes());
153        result_y.copy_from_slice(&yr.to_be_bytes());
154
155        // Construct the result as a new UncompressedPoint
156        let mut result = [0u8; 64];
157        result[..32].copy_from_slice(&result_x);
158        result[32..].copy_from_slice(&result_y);
159
160        UncompressedPoint(result)
161    }
162}
163
164impl Add<CompressedPoint> for UncompressedPoint {
165    type Output = UncompressedPoint;
166
167    fn add(self, rhs: CompressedPoint) -> Self::Output {
168        self.add(rhs.decompress())
169    }
170}
171
172impl Debug for UncompressedPoint {
173    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
174        for byte in &self.0 {
175            write!(f, "{:02X}", byte)?;
176        }
177        Ok(())
178    }
179}
180
181impl UncompressedPoint {
182    pub fn to_sec1_bytes(&self) -> [u8; 65] {
183        [
184            SEC1_OCTET_UNCOMPRESSED,
185            self.0[0],
186            self.0[1],
187            self.0[2],
188            self.0[3],
189            self.0[4],
190            self.0[5],
191            self.0[6],
192            self.0[7],
193            self.0[8],
194            self.0[9],
195            self.0[10],
196            self.0[11],
197            self.0[12],
198            self.0[13],
199            self.0[14],
200            self.0[15],
201            self.0[16],
202            self.0[17],
203            self.0[18],
204            self.0[19],
205            self.0[20],
206            self.0[21],
207            self.0[22],
208            self.0[23],
209            self.0[24],
210            self.0[25],
211            self.0[26],
212            self.0[27],
213            self.0[28],
214            self.0[29],
215            self.0[30],
216            self.0[31],
217            self.0[32],
218            self.0[33],
219            self.0[34],
220            self.0[35],
221            self.0[36],
222            self.0[37],
223            self.0[38],
224            self.0[39],
225            self.0[40],
226            self.0[41],
227            self.0[42],
228            self.0[43],
229            self.0[44],
230            self.0[45],
231            self.0[46],
232            self.0[47],
233            self.0[48],
234            self.0[49],
235            self.0[50],
236            self.0[51],
237            self.0[52],
238            self.0[53],
239            self.0[54],
240            self.0[55],
241            self.0[56],
242            self.0[57],
243            self.0[58],
244            self.0[59],
245            self.0[60],
246            self.0[61],
247            self.0[62],
248            self.0[63],
249        ]
250    }
251}
252
253impl TryFrom<CompressedPoint> for UncompressedPoint {
254    type Error = Secp256k1Error;
255
256    fn try_from(x: CompressedPoint) -> Result<Self, Secp256k1Error> {
257        let mut point = UncompressedPoint::lift_x(&x.x())?;
258        if point.is_odd() != x.is_odd() {
259            point.invert();
260        }
261        Ok(point)
262    }
263}
264
265impl From<[u8; 65]> for UncompressedPoint {
266    fn from(p: [u8; 65]) -> Self {
267        let mut s = [0u8; 64];
268        s.clone_from_slice(&p[1..]);
269        UncompressedPoint(s)
270    }
271}
272
273impl TryFrom<[u8; 32]> for UncompressedPoint {
274    type Error = Secp256k1Error;
275
276    fn try_from(scalar: [u8; 32]) -> Result<Self, Self::Error> {
277        Curve::mul_g(&scalar)
278    }
279}