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;
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 if ((byte >> i) & 1) == 1 {
226 acc = acc.add(&base);
227 }
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 if self.is_identity.into() {
274 return other.clone();
275 }
276 if other.is_identity.into() {
277 return self.clone();
278 }
279
280 let z1_sq = self.z.square();
282 let z2_sq = other.z.square();
283 let z1_cu = z1_sq.mul(&self.z);
284 let z2_cu = z2_sq.mul(&other.z);
285
286 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);
292 let r = s2.sub(&s1);
293
294 if h.is_zero() {
295 if r.is_zero() {
296 return self.double();
297 } else {
298 return ProjectivePoint::identity();
299 }
300 }
301
302 let h2 = h.square();
303 let h3 = h2.mul(&h);
304 let v = u1.mul(&h2);
305
306 let r2 = r.square();
308 let two_v = v.add(&v);
309 let mut x3 = r2.sub(&h3);
310 x3 = x3.sub(&two_v);
311
312 let v_minus_x3 = v.sub(&x3);
314 let r_times = r.mul(&v_minus_x3);
315 let s1_h3 = s1.mul(&h3);
316 let y3 = r_times.sub(&s1_h3);
317
318 let z1z2 = self.z.mul(&other.z);
320 let z3 = z1z2.mul(&h);
321
322 ProjectivePoint {
323 is_identity: Choice::from(0),
324 x: x3,
325 y: y3,
326 z: z3,
327 }
328 }
329
330 pub fn double(&self) -> Self {
332 if self.is_identity.into() {
333 return self.clone();
334 }
335 if self.y.is_zero() {
336 return ProjectivePoint::identity();
337 }
338
339 let delta = self.z.square();
346 let gamma = self.y.square();
347 let beta = self.x.mul(&gamma);
348
349 let t1 = self.x.add(&delta); let t2 = self.x.sub(&delta); let mut alpha = t1.mul(&t2); let three = FieldElement::from_u32(3);
353 alpha = alpha.mul(&three); let eight_beta = {
357 let two_beta = beta.add(&beta);
358 let four_beta = two_beta.add(&two_beta);
359 four_beta.add(&four_beta) };
361 let x3 = alpha.square().sub(&eight_beta);
362
363 let z3 = self.y.add(&self.z).square().sub(&gamma).sub(&delta);
365
366 let four_beta = {
368 let two_beta = beta.add(&beta);
369 two_beta.add(&two_beta)
370 };
371 let mut y3 = four_beta.sub(&x3);
372 y3 = alpha.mul(&y3);
373
374 let eight_gamma_sq = {
375 let gamma_sq = gamma.square();
376 let two = gamma_sq.add(&gamma_sq);
377 let four = two.add(&two);
378 four.add(&four) };
380 let y3 = y3.sub(&eight_gamma_sq);
381
382 ProjectivePoint {
383 is_identity: Choice::from(0),
384 x: x3,
385 y: y3,
386 z: z3,
387 }
388 }
389
390 pub fn to_affine(&self) -> Point {
392 if self.is_identity.into() {
393 return Point::identity();
394 }
395 let z_inv = self.z.invert().expect("Nonzero Z ⇒ invertible");
396 let z_inv_sq = z_inv.square();
397 let z_inv_cu = z_inv_sq.mul(&z_inv);
398 let x_aff = self.x.mul(&z_inv_sq);
399 let y_aff = self.y.mul(&z_inv_cu);
400 Point {
401 is_identity: Choice::from(0),
402 x: x_aff,
403 y: y_aff,
404 }
405 }
406}