1use crate::primitives::big_number::{BigNumber, Endian};
9use crate::primitives::curve::Curve;
10use crate::primitives::error::PrimitivesError;
11use crate::primitives::jacobian_point::JacobianPoint;
12
13#[derive(Clone, Debug)]
17pub struct Point {
18 pub x: BigNumber,
20 pub y: BigNumber,
22 pub inf: bool,
24}
25
26impl Point {
27 pub fn new(x: BigNumber, y: BigNumber) -> Self {
29 Point { x, y, inf: false }
30 }
31
32 pub fn infinity() -> Self {
34 Point {
35 x: BigNumber::zero(),
36 y: BigNumber::zero(),
37 inf: true,
38 }
39 }
40
41 pub fn is_infinity(&self) -> bool {
43 self.inf
44 }
45
46 pub fn validate(&self) -> bool {
49 if self.inf {
50 return false;
51 }
52
53 let curve = Curve::secp256k1();
54 let red = &curve.red;
55
56 let x_red = self.x.to_red(red.clone());
57 let y_red = self.y.to_red(red.clone());
58
59 let y2 = red.sqr(&y_red);
61
62 let x2 = red.sqr(&x_red);
64 let x3 = red.mul(&x_red, &x2);
65 let seven = BigNumber::from_number(7).to_red(red.clone());
66 let rhs = red.add(&x3, &seven);
67
68 y2.from_red().cmp(&rhs.from_red()) == 0
69 }
70
71 pub fn from_x(x: &BigNumber, odd: bool) -> Result<Self, PrimitivesError> {
74 let curve = Curve::secp256k1();
75 let red = &curve.red;
76
77 let x_red = x.to_red(red.clone());
78
79 let x2 = red.sqr(&x_red);
81 let x3 = red.mul(&x_red, &x2);
82 let seven = BigNumber::from_number(7).to_red(red.clone());
83 let y2 = red.add(&x3, &seven);
84
85 let y_red = red.sqrt(&y2);
88
89 let y_check = red.sqr(&y_red);
91 if y_check.from_red().cmp(&y2.from_red()) != 0 {
92 return Err(PrimitivesError::PointNotOnCurve);
93 }
94
95 let mut y_val = y_red.from_red();
96
97 if y_val.is_odd() != odd {
99 y_val = curve.p.sub(&y_val);
100 }
101
102 let point = Point::new(x.clone(), y_val);
103 if !point.validate() {
104 return Err(PrimitivesError::PointNotOnCurve);
105 }
106 Ok(point)
107 }
108
109 pub fn from_der(bytes: &[u8]) -> Result<Self, PrimitivesError> {
114 if bytes.is_empty() {
115 return Err(PrimitivesError::InvalidDer("empty input".to_string()));
116 }
117
118 let prefix = bytes[0];
119
120 match prefix {
121 0x04 | 0x06 | 0x07 => {
122 if bytes.len() != 65 {
124 return Err(PrimitivesError::InvalidDer(format!(
125 "uncompressed point must be 65 bytes, got {}",
126 bytes.len()
127 )));
128 }
129
130 if prefix == 0x06 {
132 if bytes[64] & 1 != 0 {
133 return Err(PrimitivesError::InvalidDer(
134 "hybrid point parity mismatch (expected even y)".to_string(),
135 ));
136 }
137 } else if prefix == 0x07 && bytes[64] & 1 == 0 {
138 return Err(PrimitivesError::InvalidDer(
139 "hybrid point parity mismatch (expected odd y)".to_string(),
140 ));
141 }
142
143 let x = BigNumber::from_bytes(&bytes[1..33], Endian::Big);
144 let y = BigNumber::from_bytes(&bytes[33..65], Endian::Big);
145
146 let point = Point::new(x, y);
147 if !point.validate() {
148 return Err(PrimitivesError::PointNotOnCurve);
149 }
150 Ok(point)
151 }
152 0x02 | 0x03 => {
153 if bytes.len() != 33 {
155 return Err(PrimitivesError::InvalidDer(format!(
156 "compressed point must be 33 bytes, got {}",
157 bytes.len()
158 )));
159 }
160
161 let x = BigNumber::from_bytes(&bytes[1..33], Endian::Big);
162 let odd = prefix == 0x03;
163 Point::from_x(&x, odd)
164 }
165 _ => Err(PrimitivesError::InvalidDer(format!(
166 "unknown point format prefix: 0x{prefix:02x}"
167 ))),
168 }
169 }
170
171 pub fn from_string(hex: &str) -> Result<Self, PrimitivesError> {
173 let bytes = hex_to_bytes(hex)?;
174 Self::from_der(&bytes)
175 }
176
177 pub fn to_der(&self, compressed: bool) -> Vec<u8> {
182 if self.inf {
183 return vec![0x00];
184 }
185
186 let x_bytes = self.x.to_array(Endian::Big, Some(32));
187
188 if compressed {
189 let prefix = if self.y.is_even() { 0x02 } else { 0x03 };
190 let mut result = Vec::with_capacity(33);
191 result.push(prefix);
192 result.extend_from_slice(&x_bytes);
193 result
194 } else {
195 let y_bytes = self.y.to_array(Endian::Big, Some(32));
196 let mut result = Vec::with_capacity(65);
197 result.push(0x04);
198 result.extend_from_slice(&x_bytes);
199 result.extend_from_slice(&y_bytes);
200 result
201 }
202 }
203
204 pub fn to_hex(&self) -> String {
206 bytes_to_hex(&self.to_der(true))
207 }
208
209 pub fn add(&self, other: &Point) -> Point {
211 if self.inf {
212 return other.clone();
213 }
214 if other.inf {
215 return self.clone();
216 }
217
218 let jp1 = JacobianPoint::from_affine(&self.x, &self.y);
220 let jp2 = JacobianPoint::from_affine(&other.x, &other.y);
221 let result = jp1.add(&jp2);
222
223 if result.is_infinity() {
224 return Point::infinity();
225 }
226
227 let (x, y) = result.to_affine();
228 Point::new(x, y)
229 }
230
231 pub fn mul(&self, k: &BigNumber) -> Point {
233 if k.is_zero() || self.inf {
234 return Point::infinity();
235 }
236
237 let is_neg = k.is_neg();
238 let k_abs = if is_neg { k.neg() } else { k.clone() };
239
240 let curve = Curve::secp256k1();
242 let k_mod = k_abs.umod(&curve.n).unwrap_or(k_abs);
243
244 if k_mod.is_zero() {
245 return Point::infinity();
246 }
247
248 let jp = JacobianPoint::from_affine(&self.x, &self.y);
249 let result = jp.mul_wnaf(&k_mod);
250
251 if result.is_infinity() {
252 return Point::infinity();
253 }
254
255 let (x, y) = result.to_affine();
256 let point = Point::new(x, y);
257
258 if is_neg {
259 point.negate()
260 } else {
261 point
262 }
263 }
264
265 pub fn negate(&self) -> Point {
267 if self.inf {
268 return self.clone();
269 }
270 let curve = Curve::secp256k1();
271 let neg_y = curve.p.sub(&self.y);
272 Point::new(self.x.clone(), neg_y)
273 }
274
275 #[allow(clippy::should_implement_trait)]
277 pub fn eq(&self, other: &Point) -> bool {
278 if self.inf && other.inf {
279 return true;
280 }
281 if self.inf != other.inf {
282 return false;
283 }
284 self.x.cmp(&other.x) == 0 && self.y.cmp(&other.y) == 0
285 }
286
287 pub fn dbl(&self) -> Point {
289 if self.inf {
290 return self.clone();
291 }
292 let jp = JacobianPoint::from_affine(&self.x, &self.y);
293 let result = jp.dbl();
294 if result.is_infinity() {
295 return Point::infinity();
296 }
297 let (x, y) = result.to_affine();
298 Point::new(x, y)
299 }
300
301 pub fn get_x(&self) -> BigNumber {
303 self.x.clone()
304 }
305
306 pub fn get_y(&self) -> BigNumber {
308 self.y.clone()
309 }
310}
311
312fn hex_to_bytes(hex: &str) -> Result<Vec<u8>, PrimitivesError> {
317 if hex.len() & 1 != 0 {
318 return Err(PrimitivesError::InvalidHex(
319 "odd-length hex string".to_string(),
320 ));
321 }
322 let mut bytes = Vec::with_capacity(hex.len() / 2);
323 for i in (0..hex.len()).step_by(2) {
324 let byte = u8::from_str_radix(&hex[i..i + 2], 16)
325 .map_err(|e| PrimitivesError::InvalidHex(e.to_string()))?;
326 bytes.push(byte);
327 }
328 Ok(bytes)
329}
330
331fn bytes_to_hex(bytes: &[u8]) -> String {
332 bytes.iter().map(|b| format!("{b:02x}")).collect()
333}
334
335#[cfg(test)]
340mod tests {
341 use super::*;
342
343 fn g() -> Point {
344 let curve = Curve::secp256k1();
345 curve.generator()
346 }
347
348 #[test]
349 fn test_point_infinity() {
350 let inf = Point::infinity();
351 assert!(inf.is_infinity());
352 }
353
354 #[test]
355 fn test_point_g_on_curve() {
356 let g = g();
357 assert!(g.validate());
358 }
359
360 #[test]
361 fn test_point_infinity_not_on_curve() {
362 let inf = Point::infinity();
363 assert!(!inf.validate());
364 }
365
366 #[test]
367 fn test_point_add_g_plus_g() {
368 let g = g();
369 let two_g = g.add(&g);
370 assert_eq!(
371 two_g.x.to_hex(),
372 "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"
373 );
374 assert_eq!(
375 two_g.y.to_hex(),
376 "1ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a"
377 );
378 }
379
380 #[test]
381 fn test_point_add_identity() {
382 let g = g();
383 let inf = Point::infinity();
384
385 let r1 = g.add(&inf);
386 assert!(r1.eq(&g));
387
388 let r2 = inf.add(&g);
389 assert!(r2.eq(&g));
390 }
391
392 #[test]
393 fn test_point_mul_1() {
394 let g = g();
395 let k = BigNumber::one();
396 let result = g.mul(&k);
397 assert!(result.eq(&g));
398 }
399
400 #[test]
401 fn test_point_mul_2_equals_add() {
402 let g = g();
403 let k = BigNumber::from_number(2);
404 let mul_result = g.mul(&k);
405 let add_result = g.add(&g);
406 assert!(mul_result.eq(&add_result));
407 }
408
409 #[test]
410 fn test_point_mul_n_is_infinity() {
411 let g = g();
412 let curve = Curve::secp256k1();
413 let result = g.mul(&curve.n);
414 assert!(result.is_infinity());
415 }
416
417 #[test]
418 fn test_point_mul_n_minus_1() {
419 let g = g();
420 let curve = Curve::secp256k1();
421 let n_minus_1 = curve.n.subn(1);
422 let result = g.mul(&n_minus_1);
423 assert_eq!(result.x.cmp(&g.x), 0);
425 let neg_y = curve.p.sub(&g.y);
426 assert_eq!(result.y.cmp(&neg_y), 0);
427 }
428
429 #[test]
430 fn test_point_negate() {
431 let g = g();
432 let neg_g = g.negate();
433 assert_eq!(neg_g.x.cmp(&g.x), 0);
434 let curve = Curve::secp256k1();
435 let expected_y = curve.p.sub(&g.y);
436 assert_eq!(neg_g.y.cmp(&expected_y), 0);
437 }
438
439 #[test]
440 fn test_point_negate_add_is_infinity() {
441 let g = g();
442 let neg_g = g.negate();
443 let result = g.add(&neg_g);
444 assert!(result.is_infinity());
445 }
446
447 #[test]
448 fn test_point_compressed_even_y() {
449 let g = g();
450 let der = g.to_der(true);
451 assert_eq!(der.len(), 33);
452 assert_eq!(der[0], 0x02);
454 }
455
456 #[test]
457 fn test_point_uncompressed() {
458 let g = g();
459 let der = g.to_der(false);
460 assert_eq!(der.len(), 65);
461 assert_eq!(der[0], 0x04);
462 }
463
464 #[test]
465 fn test_point_from_der_compressed() {
466 let g = g();
467 let der = g.to_der(true);
468 let recovered = Point::from_der(&der).unwrap();
469 assert!(recovered.eq(&g));
470 }
471
472 #[test]
473 fn test_point_from_der_uncompressed() {
474 let g = g();
475 let der = g.to_der(false);
476 let recovered = Point::from_der(&der).unwrap();
477 assert!(recovered.eq(&g));
478 }
479
480 #[test]
481 fn test_point_from_der_round_trip_compressed() {
482 let g = g();
483 for k in 1..=10 {
484 let p = g.mul(&BigNumber::from_number(k));
485 if p.is_infinity() {
486 continue;
487 }
488 let der = p.to_der(true);
489 let recovered = Point::from_der(&der).unwrap();
490 assert!(recovered.eq(&p), "round-trip failed for k={k}");
491 }
492 }
493
494 #[test]
495 fn test_point_from_der_round_trip_uncompressed() {
496 let g = g();
497 for k in 1..=10 {
498 let p = g.mul(&BigNumber::from_number(k));
499 if p.is_infinity() {
500 continue;
501 }
502 let der = p.to_der(false);
503 let recovered = Point::from_der(&der).unwrap();
504 assert!(recovered.eq(&p), "round-trip failed for k={k}");
505 }
506 }
507
508 #[test]
509 fn test_point_invalid_not_on_curve() {
510 let mut bytes = vec![0x04];
512 bytes.extend_from_slice(&[0x01; 32]); bytes.extend_from_slice(&[0x01; 32]); let result = Point::from_der(&bytes);
515 assert!(result.is_err());
516 }
517
518 #[test]
519 fn test_point_from_string() {
520 let g = g();
521 let hex = g.to_hex();
522 let recovered = Point::from_string(&hex).unwrap();
523 assert!(recovered.eq(&g));
524 }
525
526 #[test]
527 fn test_point_mul_known_multiples() {
528 let g = g();
529 let expected = vec![
530 (
531 2,
532 "c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
533 "1ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a",
534 ),
535 (
536 3,
537 "f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
538 "388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672",
539 ),
540 (
541 5,
542 "2f8bde4d1a07209355b4a7250a5c5128e88b84bddc619ab7cba8d569b240efe4",
543 "d8ac222636e5e3d6d4dba9dda6c9c426f788271bab0d6840dca87d3aa6ac62d6",
544 ),
545 (
546 10,
547 "a0434d9e47f3c86235477c7b1ae6ae5d3442d49b1943c2b752a68e2a47e247c7",
548 "893aba425419bc27a3b6c7e693a24c696f794c2ed877a1593cbee53b037368d7",
549 ),
550 ];
551
552 for (k, ex, ey) in expected {
553 let result = g.mul(&BigNumber::from_number(k));
554 assert_eq!(result.x.to_hex(), ex, "x mismatch for k={k}");
555 assert_eq!(result.y.to_hex(), ey, "y mismatch for k={k}");
556 }
557 }
558
559 #[test]
560 fn test_point_dbl() {
561 let g = g();
562 let dbl = g.dbl();
563 let add = g.add(&g);
564 assert!(dbl.eq(&add));
565 }
566
567 #[test]
568 fn test_point_from_x() {
569 let curve = Curve::secp256k1();
570 let p = Point::from_x(&curve.g_x, false).unwrap();
572 assert_eq!(p.x.cmp(&curve.g_x), 0);
573 assert_eq!(p.y.cmp(&curve.g_y), 0);
574 }
575
576 #[test]
577 fn test_point_from_x_odd() {
578 let curve = Curve::secp256k1();
579 let p = Point::from_x(&curve.g_x, true).unwrap();
581 let neg_y = curve.p.sub(&curve.g_y);
582 assert_eq!(p.y.cmp(&neg_y), 0);
583 }
584}