dcrypt_algorithms/ec/p192/
point.rs1use crate::ec::p192::{
4 constants::{
5 P192_FIELD_ELEMENT_SIZE, P192_POINT_COMPRESSED_SIZE, P192_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, Debug)]
34pub(crate) struct ProjectivePoint {
35 pub(crate) is_identity: Choice,
36 pub(crate) x: FieldElement,
37 pub(crate) y: FieldElement,
38 pub(crate) z: FieldElement,
39}
40
41impl PartialEq for Point {
42 fn eq(&self, other: &Self) -> bool {
43 let a_id: bool = self.is_identity.into();
44 let b_id: bool = other.is_identity.into();
45 if a_id || b_id {
46 return a_id == b_id;
47 }
48 self.x == other.x && self.y == other.y
49 }
50}
51
52impl Point {
53 pub fn new_uncompressed(
55 x_bytes: &[u8; P192_FIELD_ELEMENT_SIZE],
56 y_bytes: &[u8; P192_FIELD_ELEMENT_SIZE],
57 ) -> Result<Self> {
58 let x_fe = FieldElement::from_bytes(x_bytes)?;
59 let y_fe = FieldElement::from_bytes(y_bytes)?;
60 if !Self::is_on_curve(&x_fe, &y_fe) {
61 return Err(Error::param("P-192 Point", "Point not on curve"));
62 }
63 Ok(Point {
64 is_identity: Choice::from(0),
65 x: x_fe,
66 y: y_fe,
67 })
68 }
69
70 pub fn identity() -> Self {
72 Point {
73 is_identity: Choice::from(1),
74 x: FieldElement::zero(),
75 y: FieldElement::zero(),
76 }
77 }
78
79 pub fn is_identity(&self) -> bool {
81 self.is_identity.into()
82 }
83
84 pub fn x_coordinate_bytes(&self) -> [u8; P192_FIELD_ELEMENT_SIZE] {
86 self.x.to_bytes()
87 }
88
89 pub fn y_coordinate_bytes(&self) -> [u8; P192_FIELD_ELEMENT_SIZE] {
91 self.y.to_bytes()
92 }
93
94 pub fn detect_format(bytes: &[u8]) -> Result<PointFormat> {
96 if bytes.is_empty() {
97 return Err(Error::param("P-192 Point", "Empty encoding"));
98 }
99 match (bytes[0], bytes.len()) {
100 (0x00, P192_POINT_UNCOMPRESSED_SIZE) => {
101 if bytes.iter().all(|&b| b == 0) {
103 Ok(PointFormat::Identity)
104 } else {
105 Err(Error::param("P-192 Point", "Invalid identity encoding"))
106 }
107 }
108 (0x04, P192_POINT_UNCOMPRESSED_SIZE) => Ok(PointFormat::Uncompressed),
109 (0x02 | 0x03, P192_POINT_COMPRESSED_SIZE) => Ok(PointFormat::Compressed),
110 _ => Err(Error::param("P-192 Point", "Unknown or malformed format")),
111 }
112 }
113
114 pub fn serialize_uncompressed(&self) -> [u8; P192_POINT_UNCOMPRESSED_SIZE] {
116 let mut out = [0u8; P192_POINT_UNCOMPRESSED_SIZE];
117 if self.is_identity() {
118 return out; }
120 out[0] = 0x04;
121 out[1..1 + P192_FIELD_ELEMENT_SIZE].copy_from_slice(&self.x.to_bytes());
122 out[1 + P192_FIELD_ELEMENT_SIZE..].copy_from_slice(&self.y.to_bytes());
123 out
124 }
125
126 pub fn deserialize_uncompressed(bytes: &[u8]) -> Result<Self> {
128 validate::length("P-192 Point", bytes.len(), P192_POINT_UNCOMPRESSED_SIZE)?;
129 if bytes.iter().all(|&b| b == 0) {
130 return Ok(Self::identity());
131 }
132 if bytes[0] != 0x04 {
133 return Err(Error::param(
134 "P-192 Point",
135 "Invalid prefix for uncompressed",
136 ));
137 }
138 let mut xb = [0u8; P192_FIELD_ELEMENT_SIZE];
139 let mut yb = [0u8; P192_FIELD_ELEMENT_SIZE];
140 xb.copy_from_slice(&bytes[1..1 + P192_FIELD_ELEMENT_SIZE]);
141 yb.copy_from_slice(&bytes[1 + P192_FIELD_ELEMENT_SIZE..]);
142 Self::new_uncompressed(&xb, &yb)
143 }
144
145 pub fn serialize_compressed(&self) -> [u8; P192_POINT_COMPRESSED_SIZE] {
147 let mut out = [0u8; P192_POINT_COMPRESSED_SIZE];
148 if self.is_identity() {
149 return out; }
151 out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
152 out[1..].copy_from_slice(&self.x.to_bytes());
153 out
154 }
155
156 pub fn deserialize_compressed(bytes: &[u8]) -> Result<Self> {
158 validate::length(
159 "P-192 Compressed Point",
160 bytes.len(),
161 P192_POINT_COMPRESSED_SIZE,
162 )?;
163 if bytes.iter().all(|&b| b == 0) {
164 return Ok(Self::identity());
165 }
166 let tag = bytes[0];
167 if tag != 0x02 && tag != 0x03 {
168 return Err(Error::param("P-192 Point", "Invalid compressed prefix"));
169 }
170 let mut xb = [0u8; P192_FIELD_ELEMENT_SIZE];
171 xb.copy_from_slice(&bytes[1..]);
172 let x_fe = FieldElement::from_bytes(&xb)
173 .map_err(|_| Error::param("P-192 Point", "Invalid compressed point: x not in field"))?;
174 let rhs = {
176 let x2 = x_fe.square();
177 let x3 = x2.mul(&x_fe);
178 let a = FieldElement(FieldElement::A_M3);
179 let b_coeff = FieldElement::from_bytes(&crate::ec::p192::field::B).unwrap();
180 x3.add(&a.mul(&x_fe)).add(&b_coeff)
181 };
182 let y_candidate = rhs
183 .sqrt()
184 .ok_or_else(|| Error::param("P-192 Point", "Invalid compressed point: no sqrt"))?;
185 let y_final =
186 if (y_candidate.is_odd() && tag == 0x03) || (!y_candidate.is_odd() && tag == 0x02) {
187 y_candidate
188 } else {
189 y_candidate.negate() };
191 Ok(Point {
192 is_identity: Choice::from(0),
193 x: x_fe,
194 y: y_final,
195 })
196 }
197
198 pub fn add(&self, other: &Self) -> Self {
200 let p1 = self.to_projective();
201 let p2 = other.to_projective();
202 let sum = p1.add(&p2);
203 sum.to_affine()
204 }
205
206 pub fn double(&self) -> Self {
208 let p = self.to_projective();
209 let d = p.double();
210 d.to_affine()
211 }
212
213 pub fn mul(&self, scalar: &Scalar) -> Result<Self> {
216 if scalar.is_zero() {
217 return Ok(Self::identity());
218 }
219 let base = self.to_projective();
220 let mut acc = ProjectivePoint::identity();
221 let bytes = scalar.as_secret_buffer().as_ref();
222 for &byte in bytes.iter() {
223 for i in (0..8).rev() {
224 acc = acc.double();
225 let acc_added = acc.add(&base);
226 let choice = Choice::from((byte >> i) & 1);
227 acc = ProjectivePoint::conditional_select(&acc, &acc_added, choice);
228 }
229 }
230 Ok(acc.to_affine())
231 }
232
233 fn is_on_curve(x: &FieldElement, y: &FieldElement) -> bool {
235 let y2 = y.square();
236 let x2 = x.square();
237 let x3 = x2.mul(x);
238 let a = FieldElement(FieldElement::A_M3);
239 let b_coeff = FieldElement::from_bytes(&crate::ec::p192::field::B).unwrap();
240 let rhs = x3.add(&a.mul(x)).add(&b_coeff);
241 y2 == rhs
242 }
243
244 fn to_projective(&self) -> ProjectivePoint {
246 if self.is_identity() {
247 ProjectivePoint::identity()
248 } else {
249 ProjectivePoint {
250 is_identity: Choice::from(0),
251 x: self.x.clone(),
252 y: self.y.clone(),
253 z: FieldElement::one(),
254 }
255 }
256 }
257}
258
259impl ProjectivePoint {
260 pub fn identity() -> Self {
262 ProjectivePoint {
263 is_identity: Choice::from(1),
264 x: FieldElement::zero(),
265 y: FieldElement::one(),
266 z: FieldElement::zero(),
267 }
268 }
269
270 pub fn add(&self, other: &Self) -> Self {
272 let z1_sq = self.z.square();
274 let z2_sq = other.z.square();
275 let z1_cu = z1_sq.mul(&self.z);
276 let z2_cu = z2_sq.mul(&other.z);
277
278 let u1 = self.x.mul(&z2_sq); let u2 = other.x.mul(&z1_sq); let s1 = self.y.mul(&z2_cu); let s2 = other.y.mul(&z1_cu); let h = u2.sub(&u1);
284 let r = s2.sub(&s1);
285
286 let h2 = h.square();
287 let h3 = h2.mul(&h);
288 let v = u1.mul(&h2);
289
290 let r2 = r.square();
292 let two_v = v.add(&v);
293 let mut x3 = r2.sub(&h3);
294 x3 = x3.sub(&two_v);
295
296 let v_minus_x3 = v.sub(&x3);
298 let r_times = r.mul(&v_minus_x3);
299 let s1_h3 = s1.mul(&h3);
300 let y3 = r_times.sub(&s1_h3);
301
302 let z1z2 = self.z.mul(&other.z);
304 let z3 = z1z2.mul(&h);
305
306 let generic = ProjectivePoint {
307 is_identity: Choice::from(0),
308 x: x3,
309 y: y3,
310 z: z3,
311 };
312
313 let double_point = self.double();
314 let h_is_zero = Choice::from(h.is_zero() as u8);
315 let r_is_zero = Choice::from(r.is_zero() as u8);
316 let p_eq_q = h_is_zero & r_is_zero;
317 let p_eq_neg_q = h_is_zero & !r_is_zero;
318
319 let mut result = Self::conditional_select(&generic, &double_point, p_eq_q);
320 result = Self::conditional_select(&result, &Self::identity(), p_eq_neg_q);
321 result = Self::conditional_select(&result, other, self.is_identity);
322 result = Self::conditional_select(&result, self, other.is_identity);
323 result
324 }
325
326 pub fn double(&self) -> Self {
328 let delta = self.z.square();
335 let gamma = self.y.square();
336 let beta = self.x.mul(&gamma);
337
338 let t1 = self.x.add(&delta); let t2 = self.x.sub(&delta); let mut alpha = t1.mul(&t2); let three = FieldElement::from_u32(3);
342 alpha = alpha.mul(&three); let eight_beta = {
346 let two_beta = beta.add(&beta);
347 let four_beta = two_beta.add(&two_beta);
348 four_beta.add(&four_beta) };
350 let x3 = alpha.square().sub(&eight_beta);
351
352 let z3 = self.y.add(&self.z).square().sub(&gamma).sub(&delta);
354
355 let four_beta = {
357 let two_beta = beta.add(&beta);
358 two_beta.add(&two_beta)
359 };
360 let mut y3 = four_beta.sub(&x3);
361 y3 = alpha.mul(&y3);
362
363 let eight_gamma_sq = {
364 let gamma_sq = gamma.square();
365 let two = gamma_sq.add(&gamma_sq);
366 let four = two.add(&two);
367 four.add(&four) };
369 let y3 = y3.sub(&eight_gamma_sq);
370
371 let result = ProjectivePoint {
372 is_identity: Choice::from(0),
373 x: x3,
374 y: y3,
375 z: z3,
376 };
377
378 let return_identity = self.is_identity | Choice::from(self.y.is_zero() as u8);
379 Self::conditional_select(&result, &Self::identity(), return_identity)
380 }
381
382 pub fn to_affine(&self) -> Point {
384 if self.is_identity.into() {
385 return Point::identity();
386 }
387 let z_inv = self.z.invert().expect("Nonzero Z ⇒ invertible");
388 let z_inv_sq = z_inv.square();
389 let z_inv_cu = z_inv_sq.mul(&z_inv);
390 let x_aff = self.x.mul(&z_inv_sq);
391 let y_aff = self.y.mul(&z_inv_cu);
392 Point {
393 is_identity: Choice::from(0),
394 x: x_aff,
395 y: y_aff,
396 }
397 }
398
399 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
400 let select_field = |lhs: &FieldElement, rhs: &FieldElement| {
401 let mut out = [0u32; 6];
402 for (i, limb) in out.iter_mut().enumerate() {
403 *limb = u32::conditional_select(&lhs.0[i], &rhs.0[i], choice);
404 }
405 FieldElement(out)
406 };
407 Self {
408 is_identity: Choice::conditional_select(&a.is_identity, &b.is_identity, choice),
409 x: select_field(&a.x, &b.x),
410 y: select_field(&a.y, &b.y),
411 z: select_field(&a.z, &b.z),
412 }
413 }
414}