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, ConditionallySelectable};
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, Copy, 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) {
129 return Ok(Self::identity());
130 }
131
132 if bytes[0] != 0x04 {
133 return Err(Error::param(
134 "K256 Point",
135 "Invalid uncompressed point prefix (expected 0x04)",
136 ));
137 }
138
139 let mut x_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
140 let mut y_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
141 x_bytes.copy_from_slice(&bytes[1..33]);
142 y_bytes.copy_from_slice(&bytes[33..65]);
143
144 Self::new_uncompressed(&x_bytes, &y_bytes)
145 }
146
147 pub fn serialize_compressed(&self) -> [u8; K256_POINT_COMPRESSED_SIZE] {
149 let mut out = [0u8; K256_POINT_COMPRESSED_SIZE];
150 if self.is_identity() {
151 return out;
152 }
153 out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
154 out[1..].copy_from_slice(&self.x.to_bytes());
155 out
156 }
157
158 pub fn deserialize_compressed(bytes: &[u8]) -> Result<Self> {
162 validate::length(
163 "K256 Compressed Point",
164 bytes.len(),
165 K256_POINT_COMPRESSED_SIZE,
166 )?;
167 if bytes.iter().all(|&b| b == 0) {
168 return Ok(Self::identity());
169 }
170 let tag = bytes[0];
171 if tag != 0x02 && tag != 0x03 {
172 return Err(Error::param(
173 "K256 Point",
174 "Invalid compressed point prefix",
175 ));
176 }
177 let mut x_bytes = [0u8; K256_FIELD_ELEMENT_SIZE];
178 x_bytes.copy_from_slice(&bytes[1..]);
179 let x_fe = FieldElement::from_bytes(&x_bytes)
180 .map_err(|_| Error::param("K256 Point", "Invalid x-coordinate"))?;
181
182 let rhs = {
183 let x3 = x_fe.square().mul(&x_fe);
184 let mut seven = [0u32; 8];
185 seven[0] = 7;
186 let b = FieldElement(seven);
187 x3.add(&b)
188 };
189 let y_fe = rhs
190 .sqrt()
191 .ok_or_else(|| Error::param("K256 Point", "Invalid compressed point: no sqrt"))?;
192 let y_final = if (y_fe.is_odd() && tag == 0x03) || (!y_fe.is_odd() && tag == 0x02) {
193 y_fe
194 } else {
195 y_fe.negate()
196 };
197 Ok(Point {
198 is_identity: Choice::from(0),
199 x: x_fe,
200 y: y_final,
201 })
202 }
203
204 pub fn add(&self, other: &Self) -> Self {
206 self.to_projective().add(&other.to_projective()).to_affine()
207 }
208
209 pub fn double(&self) -> Self {
211 self.to_projective().double().to_affine()
212 }
213
214 pub fn mul(&self, scalar: &Scalar) -> Result<Self> {
218 if scalar.is_zero() {
219 return Ok(Self::identity());
220 }
221 let scalar_bytes = scalar.as_secret_buffer().as_ref();
222 let base = self.to_projective();
223 let mut result = ProjectivePoint::identity();
224
225 for byte in scalar_bytes.iter() {
226 for bit_pos in (0..8).rev() {
227 result = result.double();
228 let bit = (byte >> bit_pos) & 1;
229 let choice = Choice::from(bit);
230 let result_added = result.add(&base);
231 result = ProjectivePoint::conditional_select(&result, &result_added, choice);
232 }
233 }
234 Ok(result.to_affine())
235 }
236
237 fn is_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
238 let y_squared = y.square();
239 let x_cubed = x.square().mul(x);
240 let mut seven_limbs = [0u32; 8];
241 seven_limbs[0] = 7;
242 let seven = FieldElement(seven_limbs);
243 let rhs = x_cubed.add(&seven);
244 y_squared == rhs
245 }
246
247 fn to_projective(&self) -> ProjectivePoint {
248 if self.is_identity() {
249 return ProjectivePoint::identity();
250 }
251 ProjectivePoint {
252 is_identity: Choice::from(0),
253 x: self.x,
254 y: self.y,
255 z: FieldElement::one(),
256 }
257 }
258}
259
260impl ConditionallySelectable for ProjectivePoint {
261 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
262 Self {
263 is_identity: Choice::conditional_select(&a.is_identity, &b.is_identity, choice),
264 x: FieldElement::conditional_select(&a.x, &b.x, choice),
265 y: FieldElement::conditional_select(&a.y, &b.y, choice),
266 z: FieldElement::conditional_select(&a.z, &b.z, choice),
267 }
268 }
269}
270
271impl ProjectivePoint {
272 pub fn identity() -> Self {
273 ProjectivePoint {
274 is_identity: Choice::from(1),
275 x: FieldElement::zero(),
276 y: FieldElement::one(),
277 z: FieldElement::zero(),
278 }
279 }
280
281 pub fn add(&self, other: &Self) -> Self {
282 let z1_sq = self.z.square();
284 let z2_sq = other.z.square();
285 let u1 = self.x.mul(&z2_sq);
286 let u2 = other.x.mul(&z1_sq);
287 let s1 = self.y.mul(&z2_sq).mul(&other.z);
288 let s2 = other.y.mul(&z1_sq).mul(&self.z);
289
290 let h = u2.sub(&u1);
291 let r = s2.sub(&s1);
292
293 let h_sq = h.square();
295 let h_cu = h_sq.mul(&h);
296 let v = u1.mul(&h_sq);
297
298 let r_sq = r.square();
299 let two_v = v.add(&v);
300 let mut x3 = r_sq.sub(&h_cu);
301 x3 = x3.sub(&two_v);
302
303 let v_minus_x3 = v.sub(&x3);
304 let mut y3 = r.mul(&v_minus_x3);
305 let s1_h_cu = s1.mul(&h_cu);
306 y3 = y3.sub(&s1_h_cu);
307
308 let mut z3 = self.z.mul(&other.z);
309 z3 = z3.mul(&h);
310
311 let generic = ProjectivePoint {
312 is_identity: Choice::from(0),
313 x: x3,
314 y: y3,
315 z: z3,
316 };
317
318 let double = self.double();
320
321 let h_is_zero = Choice::from((h.is_zero() as u8) & 1);
323 let r_is_zero = Choice::from((r.is_zero() as u8) & 1);
324 let p_eq_q = h_is_zero & r_is_zero;
325 let p_eq_neg_q = h_is_zero & !r_is_zero;
326
327 let mut result = Self::conditional_select(&generic, &double, p_eq_q);
328 result = Self::conditional_select(&result, &Self::identity(), p_eq_neg_q);
329 result = Self::conditional_select(&result, other, self.is_identity);
330 result = Self::conditional_select(&result, self, other.is_identity);
331
332 result
333 }
334
335 pub fn double(&self) -> Self {
336 let y_sq = self.y.square();
339 let mut s = self.x.mul(&y_sq);
340 s = s.add(&s); s = s.add(&s); let x_sq = self.x.square();
344 let mut m = x_sq.add(&x_sq);
345 m = m.add(&x_sq); let mut x3 = m.square();
348 let s_plus_s = s.add(&s);
349 x3 = x3.sub(&s_plus_s); let mut y3 = s.sub(&x3);
352 y3 = m.mul(&y3);
353
354 let y_sq_sq = y_sq.square();
355 let mut eight_y4 = y_sq_sq.add(&y_sq_sq); eight_y4 = eight_y4.add(&eight_y4); eight_y4 = eight_y4.add(&eight_y4); y3 = y3.sub(&eight_y4); let mut z3 = self.y.mul(&self.z);
361 z3 = z3.add(&z3); let result = ProjectivePoint {
364 is_identity: Choice::from(0),
365 x: x3,
366 y: y3,
367 z: z3,
368 };
369
370 let is_y_zero = self.y.is_zero();
372 let return_identity = self.is_identity | Choice::from(is_y_zero as u8);
373
374 Self::conditional_select(&result, &Self::identity(), return_identity)
375 }
376
377 pub fn to_affine(&self) -> Point {
378 if self.is_identity.into() {
379 return Point::identity();
380 }
381 let z_inv = self.z.invert().expect("Nonzero Z should be invertible");
382 let z_inv_sq = z_inv.square();
383 let z_inv_cu = z_inv_sq.mul(&z_inv);
384 let x_aff = self.x.mul(&z_inv_sq);
385 let y_aff = self.y.mul(&z_inv_cu);
386 Point {
387 is_identity: Choice::from(0),
388 x: x_aff,
389 y: y_aff,
390 }
391 }
392}