dcrypt_algorithms/ec/k256/
point.rs1use crate::ec::k256::{
4 constants::{
5 K256_FIELD_ELEMENT_SIZE, K256_POINT_COMPRESSED_SIZE, K256_POINT_UNCOMPRESSED_SIZE,
6 },
7 field::FieldElement,
8 scalar::Scalar,
9};
10use crate::error::{validate, Error, Result};
11use subtle::Choice;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum PointFormat {
16 Identity,
18 Uncompressed,
20 Compressed,
22}
23
24#[derive(Clone, Debug)]
26pub struct Point {
27 pub(crate) is_identity: Choice,
28 pub(crate) x: FieldElement,
29 pub(crate) y: FieldElement,
30}
31
32#[derive(Clone, Debug)]
33pub(crate) struct ProjectivePoint {
34 is_identity: Choice,
35 x: FieldElement,
36 y: FieldElement,
37 z: FieldElement,
38}
39
40impl PartialEq for Point {
41 fn eq(&self, other: &Self) -> bool {
42 let self_is_identity: bool = self.is_identity.into();
43 let other_is_identity: bool = other.is_identity.into();
44 if self_is_identity || other_is_identity {
45 return self_is_identity == other_is_identity;
46 }
47 self.x == other.x && self.y == other.y
48 }
49}
50
51impl Point {
52 pub fn new_uncompressed(
56 x: &[u8; K256_FIELD_ELEMENT_SIZE],
57 y: &[u8; K256_FIELD_ELEMENT_SIZE],
58 ) -> Result<Self> {
59 let x_fe = FieldElement::from_bytes(x)?;
60 let y_fe = FieldElement::from_bytes(y)?;
61 if !Self::is_on_curve(&x_fe, &y_fe) {
62 return Err(Error::param(
63 "K256 Point",
64 "Point coordinates do not satisfy curve equation",
65 ));
66 }
67 Ok(Point {
68 is_identity: Choice::from(0),
69 x: x_fe,
70 y: y_fe,
71 })
72 }
73
74 pub fn identity() -> Self {
76 Point {
77 is_identity: Choice::from(1),
78 x: FieldElement::zero(),
79 y: FieldElement::zero(),
80 }
81 }
82
83 pub fn is_identity(&self) -> bool {
85 self.is_identity.into()
86 }
87
88 pub fn is_valid(&self) -> bool {
90 if self.is_identity() {
91 return true;
92 }
93 Self::is_on_curve(&self.x, &self.y)
94 }
95
96 pub fn x_coordinate_bytes(&self) -> [u8; K256_FIELD_ELEMENT_SIZE] {
98 self.x.to_bytes()
99 }
100
101 pub fn y_coordinate_bytes(&self) -> [u8; K256_FIELD_ELEMENT_SIZE] {
103 self.y.to_bytes()
104 }
105
106 pub fn serialize_uncompressed(&self) -> [u8; K256_POINT_UNCOMPRESSED_SIZE] {
108 let mut out = [0u8; K256_POINT_UNCOMPRESSED_SIZE];
109 if self.is_identity() {
110 return out;
111 }
112 out[0] = 0x04;
113 out[1..33].copy_from_slice(&self.x.to_bytes());
114 out[33..].copy_from_slice(&self.y.to_bytes());
115 out
116 }
117
118 pub fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self> {
122 validate::length(
123 "K256 Uncompressed Point",
124 bytes.len(),
125 K256_POINT_UNCOMPRESSED_SIZE,
126 )?;
127
128 if bytes.iter().all(|&b| b == 0) {
130 return Ok(Self::identity());
131 }
132
133 if bytes[0] != 0x04 {
135 return Err(Error::param(
136 "K256 Point",
137 "Invalid uncompressed point prefix (expected 0x04)",
138 ));
139 }
140
141 let mut x_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
143 let mut y_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
144 x_bytes.copy_from_slice(&bytes[1..33]);
145 y_bytes.copy_from_slice(&bytes[33..65]);
146
147 Self::new_uncompressed(&x_bytes, &y_bytes)
149 }
150
151 pub fn serialize_compressed(&self) -> [u8; K256_POINT_COMPRESSED_SIZE] {
153 let mut out = [0u8; K256_POINT_COMPRESSED_SIZE];
154 if self.is_identity() {
155 return out;
156 }
157 out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
158 out[1..].copy_from_slice(&self.x.to_bytes());
159 out
160 }
161
162 pub fn deserialize_compressed(bytes: &[u8]) -> Result<Self> {
166 validate::length(
167 "K256 Compressed Point",
168 bytes.len(),
169 K256_POINT_COMPRESSED_SIZE,
170 )?;
171 if bytes.iter().all(|&b| b == 0) {
172 return Ok(Self::identity());
173 }
174 let tag = bytes[0];
175 if tag != 0x02 && tag != 0x03 {
176 return Err(Error::param(
177 "K256 Point",
178 "Invalid compressed point prefix",
179 ));
180 }
181 let mut x_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
182 x_bytes.copy_from_slice(&bytes[1..]);
183 let x_fe = FieldElement::from_bytes(&x_bytes)
184 .map_err(|_| Error::param("K256 Point", "Invalid x-coordinate"))?;
185 let rhs = {
187 let x3 = x_fe.square().mul(&x_fe);
188 let mut seven = [0u32; 8];
189 seven[0] = 7;
190 let b = FieldElement(seven);
191 x3.add(&b)
192 };
193 let y_fe = rhs
194 .sqrt()
195 .ok_or_else(|| Error::param("K256 Point", "Invalid compressed point: no sqrt"))?;
196 let y_final = if (y_fe.is_odd() && tag == 0x03) || (!y_fe.is_odd() && tag == 0x02) {
197 y_fe
198 } else {
199 y_fe.negate()
200 };
201 Ok(Point {
202 is_identity: Choice::from(0),
203 x: x_fe,
204 y: y_final,
205 })
206 }
207
208 pub fn add(&self, other: &Self) -> Self {
210 self.to_projective().add(&other.to_projective()).to_affine()
211 }
212
213 pub fn double(&self) -> Self {
215 if self.is_identity() || self.y.is_zero() {
217 return Self::identity();
218 }
219
220 let x_sq = self.x.square();
222 let three_x_sq = x_sq.add(&x_sq).add(&x_sq); let two_y = self.y.double(); let inv_two_y = two_y
225 .invert() .expect("2·y ≠ 0 for non-identity point");
227 let lambda = three_x_sq.mul(&inv_two_y);
228
229 let x3 = lambda.square().sub(&self.x.double());
231
232 let y3 = lambda.mul(&self.x.sub(&x3)).sub(&self.y);
234
235 Point {
236 is_identity: Choice::from(0),
237 x: x3,
238 y: y3,
239 }
240 }
241
242 pub fn mul(&self, scalar: &Scalar) -> Result<Self> {
246 if scalar.is_zero() {
247 return Ok(Self::identity());
248 }
249 let scalar_bytes = scalar.as_secret_buffer().as_ref();
250 let base = self.to_projective();
251 let mut result = ProjectivePoint::identity();
252 for byte in scalar_bytes.iter() {
253 for bit_pos in (0..8).rev() {
254 result = result.double();
255 if (byte >> bit_pos) & 1 == 1 {
256 result = result.add(&base);
257 }
258 }
259 }
260 Ok(result.to_affine())
261 }
262
263 fn is_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
264 let y_squared = y.square();
265 let x_cubed = x.square().mul(x);
266 let mut seven_limbs = [0u32; 8];
267 seven_limbs[0] = 7;
268 let seven = FieldElement(seven_limbs);
269 let rhs = x_cubed.add(&seven);
270 y_squared == rhs
271 }
272
273 fn to_projective(&self) -> ProjectivePoint {
274 if self.is_identity() {
275 return ProjectivePoint::identity();
276 }
277 ProjectivePoint {
278 is_identity: Choice::from(0),
279 x: self.x.clone(),
280 y: self.y.clone(),
281 z: FieldElement::one(),
282 }
283 }
284}
285
286impl ProjectivePoint {
287 pub fn identity() -> Self {
288 ProjectivePoint {
289 is_identity: Choice::from(1),
290 x: FieldElement::zero(),
291 y: FieldElement::one(),
292 z: FieldElement::zero(),
293 }
294 }
295
296 pub fn add(&self, other: &Self) -> Self {
297 if self.is_identity.into() {
298 return other.clone();
299 }
300 if other.is_identity.into() {
301 return self.clone();
302 }
303
304 let z1_sq = self.z.square();
305 let z2_sq = other.z.square();
306 let u1 = self.x.mul(&z2_sq);
307 let u2 = other.x.mul(&z1_sq);
308 let s1 = self.y.mul(&z2_sq).mul(&other.z);
309 let s2 = other.y.mul(&z1_sq).mul(&self.z);
310
311 let h = u2.sub(&u1);
312 if h.is_zero() {
313 if s1 == s2 {
314 return self.double();
315 } else {
316 return Self::identity();
317 }
318 }
319
320 let r = s2.sub(&s1);
321 let h_sq = h.square();
322 let h_cu = h_sq.mul(&h);
323 let v = u1.mul(&h_sq);
324
325 let r_sq = r.square();
326 let two_v = v.add(&v);
327 let x3 = r_sq.sub(&h_cu).sub(&two_v);
328
329 let v_minus_x3 = v.sub(&x3);
330 let y3 = r.mul(&v_minus_x3).sub(&s1.mul(&h_cu));
331
332 let z3 = self.z.mul(&other.z).mul(&h);
333
334 ProjectivePoint {
335 is_identity: Choice::from(0),
336 x: x3,
337 y: y3,
338 z: z3,
339 }
340 }
341
342 pub fn double(&self) -> Self {
343 self.to_affine().double().to_projective()
345 }
346
347 pub fn to_affine(&self) -> Point {
348 if self.is_identity.into() {
349 return Point::identity();
350 }
351 let z_inv = self.z.invert().expect("Nonzero Z should be invertible");
352 let z_inv_sq = z_inv.square();
353 let z_inv_cu = z_inv_sq.mul(&z_inv);
354 let x_aff = self.x.mul(&z_inv_sq);
355 let y_aff = self.y.mul(&z_inv_cu);
356 Point {
357 is_identity: Choice::from(0),
358 x: x_aff,
359 y: y_aff,
360 }
361 }
362}