1use super::field::*;
41
42#[derive(Clone, Copy, Debug)]
52pub struct JacobianPoint<const LIMBS: usize> {
53 pub x: FieldElement<LIMBS>,
55 pub y: FieldElement<LIMBS>,
57 pub z: FieldElement<LIMBS>,
59}
60
61impl<const LIMBS: usize> JacobianPoint<LIMBS> {
62 pub fn infinity() -> Self {
64 Self {
65 x: FieldElement::one(),
66 y: FieldElement::one(),
67 z: FieldElement::ZERO,
68 }
69 }
70
71 pub fn from_affine(x: FieldElement<LIMBS>, y: FieldElement<LIMBS>) -> Self {
73 Self {
74 x,
75 y,
76 z: FieldElement::one(),
77 }
78 }
79
80 pub fn is_infinity(&self) -> bool {
82 self.z.is_zero()
83 }
84
85 pub fn to_affine(&self, p: &[u64; LIMBS]) -> Option<(FieldElement<LIMBS>, FieldElement<LIMBS>)> {
87 if self.z.is_zero() {
88 return None;
89 }
90 let z_inv = field_inv(&self.z, p);
91 let z_inv2 = field_sqr(&z_inv, p);
92 let z_inv3 = field_mul(&z_inv2, &z_inv, p);
93 let x = field_mul(&self.x, &z_inv2, p);
94 let y = field_mul(&self.y, &z_inv3, p);
95 Some((x, y))
96 }
97}
98
99pub struct CurveParams<const LIMBS: usize> {
101 pub p: [u64; LIMBS],
103 pub a: FieldElement<LIMBS>,
107 pub b: FieldElement<LIMBS>,
109 pub gx: FieldElement<LIMBS>,
111 pub gy: FieldElement<LIMBS>,
113 pub n: [u64; LIMBS],
115 pub qlen_bits: usize,
125 pub felem_bytes: usize,
152}
153
154pub fn hex_to_fe<const LIMBS: usize>(hex: &str) -> FieldElement<LIMBS> {
161 let bytes: Vec<u8> = (0..hex.len())
162 .step_by(2)
163 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
164 .collect();
165 FieldElement::from_bytes_be(&bytes)
166}
167
168pub fn hex_to_limbs<const LIMBS: usize>(hex: &str) -> [u64; LIMBS] {
171 hex_to_fe::<LIMBS>(hex).limbs
172}
173
174fn hex_to_fe4(hex: &str) -> FieldElement<4> {
176 hex_to_fe::<4>(hex)
177}
178fn hex_to_fe6(hex: &str) -> FieldElement<6> {
179 hex_to_fe::<6>(hex)
180}
181
182pub fn p256_params() -> CurveParams<4> {
188 CurveParams {
189 p: P256_P,
190 a: hex_to_fe4("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"),
191 b: hex_to_fe4("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"),
192 gx: hex_to_fe4("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"),
193 gy: hex_to_fe4("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"),
194 n: P256_N,
195 qlen_bits: 256,
196 felem_bytes: 32,
197 }
198}
199
200pub fn p384_params() -> CurveParams<6> {
202 CurveParams {
203 p: P384_P,
204 a: hex_to_fe6(
205 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
206 ),
207 b: hex_to_fe6(
208 "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
209 ),
210 gx: hex_to_fe6(
211 "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
212 ),
213 gy: hex_to_fe6(
214 "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
215 ),
216 n: P384_N,
217 qlen_bits: 384,
218 felem_bytes: 48,
219 }
220}
221
222pub fn secp256k1_params() -> CurveParams<4> {
236 CurveParams {
237 p: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),
238 a: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000000"),
239 b: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000007"),
240 gx: hex_to_fe::<4>("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
241 gy: hex_to_fe::<4>("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
242 n: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
243 qlen_bits: 256,
244 felem_bytes: 32,
245 }
246}
247
248pub fn brainpoolp256r1_params() -> CurveParams<4> {
254 CurveParams {
255 p: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377"),
256 a: hex_to_fe::<4>("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9"),
257 b: hex_to_fe::<4>("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6"),
258 gx: hex_to_fe::<4>("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262"),
259 gy: hex_to_fe::<4>("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997"),
260 n: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7"),
261 qlen_bits: 256,
262 felem_bytes: 32,
263 }
264}
265
266pub fn brainpoolp384r1_params() -> CurveParams<6> {
272 CurveParams {
273 p: hex_to_limbs::<6>(
274 "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
275 ),
276 a: hex_to_fe::<6>(
277 "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
278 ),
279 b: hex_to_fe::<6>(
280 "04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
281 ),
282 gx: hex_to_fe::<6>(
283 "1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
284 ),
285 gy: hex_to_fe::<6>(
286 "8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
287 ),
288 n: hex_to_limbs::<6>(
289 "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
290 ),
291 qlen_bits: 384,
292 felem_bytes: 48,
293 }
294}
295
296pub fn brainpoolp512r1_params() -> CurveParams<8> {
302 CurveParams {
303 p: hex_to_limbs::<8>(
304 "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
305 ),
306 a: hex_to_fe::<8>(
307 "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
308 ),
309 b: hex_to_fe::<8>(
310 "3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
311 ),
312 gx: hex_to_fe::<8>(
313 "81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
314 ),
315 gy: hex_to_fe::<8>(
316 "7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
317 ),
318 n: hex_to_limbs::<8>(
319 "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
320 ),
321 qlen_bits: 512,
322 felem_bytes: 64,
323 }
324}
325
326pub fn secp521r1_params() -> CurveParams<9> {
347 CurveParams {
350 p: hex_to_limbs::<9>(
351 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
352 ),
353 a: hex_to_fe::<9>(
354 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
355 ),
356 b: hex_to_fe::<9>(
357 "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
358 ),
359 gx: hex_to_fe::<9>(
360 "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
361 ),
362 gy: hex_to_fe::<9>(
363 "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
364 ),
365 n: hex_to_limbs::<9>(
366 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
367 ),
368 qlen_bits: 521,
369 felem_bytes: 66,
373 }
374}
375
376pub fn is_on_curve<const LIMBS: usize>(
390 x: &FieldElement<LIMBS>,
391 y: &FieldElement<LIMBS>,
392 params: &CurveParams<LIMBS>,
393) -> bool {
394 let p = ¶ms.p;
395 let y2 = field_sqr(y, p);
396 let x2 = field_sqr(x, p);
397 let x3 = field_mul(&x2, x, p);
398 let ax = field_mul(¶ms.a, x, p);
399 let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
400 y2 == rhs
401}
402
403pub fn point_double<const LIMBS: usize>(
416 pt: &JacobianPoint<LIMBS>,
417 params: &CurveParams<LIMBS>,
418) -> JacobianPoint<LIMBS> {
419 let p = ¶ms.p;
420
421 let xx = field_sqr(&pt.x, p);
426 let yy = field_sqr(&pt.y, p);
427 let yyyy = field_sqr(&yy, p);
428 let zz = field_sqr(&pt.z, p);
429
430 let x_plus_yy = field_add(&pt.x, &yy, p);
432 let x_plus_yy_sq = field_sqr(&x_plus_yy, p);
433 let s_inner = field_sub(&field_sub(&x_plus_yy_sq, &xx, p), &yyyy, p);
434 let s = field_add(&s_inner, &s_inner, p);
435
436 let two_xx = field_add(&xx, &xx, p);
438 let three_xx = field_add(&two_xx, &xx, p);
439 let zz_sq = field_sqr(&zz, p);
440 let a_zz_sq = field_mul(¶ms.a, &zz_sq, p);
441 let m = field_add(&three_xx, &a_zz_sq, p);
442
443 let m_sq = field_sqr(&m, p);
445 let two_s = field_add(&s, &s, p);
446 let t = field_sub(&m_sq, &two_s, p);
447
448 let x3 = t;
450
451 let s_minus_t = field_sub(&s, &t, p);
453 let m_term = field_mul(&m, &s_minus_t, p);
454 let two_yyyy = field_add(&yyyy, &yyyy, p);
455 let four_yyyy = field_add(&two_yyyy, &two_yyyy, p);
456 let eight_yyyy = field_add(&four_yyyy, &four_yyyy, p);
457 let y3 = field_sub(&m_term, &eight_yyyy, p);
458
459 let y_plus_z = field_add(&pt.y, &pt.z, p);
462 let y_plus_z_sq = field_sqr(&y_plus_z, p);
463 let z3 = field_sub(&field_sub(&y_plus_z_sq, &yy, p), &zz, p);
464
465 JacobianPoint { x: x3, y: y3, z: z3 }
466}
467
468pub fn point_add<const LIMBS: usize>(
484 p_pt: &JacobianPoint<LIMBS>,
485 q_pt: &JacobianPoint<LIMBS>,
486 params: &CurveParams<LIMBS>,
487) -> JacobianPoint<LIMBS> {
488 let p = ¶ms.p;
489
490 let p_inf = p_pt.z.is_zero();
492 let q_inf = q_pt.z.is_zero();
493
494 let z1z1 = field_sqr(&p_pt.z, p);
496 let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
497
498 let z2z2 = field_sqr(&q_pt.z, p);
500 let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
501
502 let u1 = field_mul(&p_pt.x, &z2z2, p);
504 let u2 = field_mul(&q_pt.x, &z1z1, p);
505
506 let s1 = field_mul(&p_pt.y, &z2z2z2, p);
508 let s2 = field_mul(&q_pt.y, &z1z1z1, p);
509
510 let h = field_sub(&u2, &u1, p);
512 let r = field_sub(&s2, &s1, p);
514
515 let h_zero = h.is_zero();
517 let r_zero = r.is_zero();
518
519 if !p_inf && !q_inf && h_zero && r_zero {
520 return point_double(p_pt, params);
521 }
522 if !p_inf && !q_inf && h_zero && !r_zero {
524 return JacobianPoint::infinity();
525 }
526
527 let h2 = field_sqr(&h, p);
528 let h3 = field_mul(&h2, &h, p);
529 let u1h2 = field_mul(&u1, &h2, p);
530
531 let r2 = field_sqr(&r, p);
533 let u1h2_2 = field_add(&u1h2, &u1h2, p);
534 let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
535
536 let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
538 let r_term = field_mul(&r, &u1h2_minus_x3, p);
539 let s1h3 = field_mul(&s1, &h3, p);
540 let y3 = field_sub(&r_term, &s1h3, p);
541
542 let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
544 let z3 = field_mul(&z1z2, &h, p);
545
546 let result = JacobianPoint { x: x3, y: y3, z: z3 };
547
548 if p_inf {
550 *q_pt
551 } else if q_inf {
552 *p_pt
553 } else {
554 result
555 }
556}
557
558fn point_add_ct<const LIMBS: usize>(
575 p_pt: &JacobianPoint<LIMBS>,
576 q_pt: &JacobianPoint<LIMBS>,
577 params: &CurveParams<LIMBS>,
578) -> JacobianPoint<LIMBS> {
579 let p = ¶ms.p;
580
581 let p_inf = p_pt.z.is_zero();
582 let q_inf = q_pt.z.is_zero();
583
584 let z1z1 = field_sqr(&p_pt.z, p);
585 let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
586 let z2z2 = field_sqr(&q_pt.z, p);
587 let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
588
589 let u1 = field_mul(&p_pt.x, &z2z2, p);
590 let u2 = field_mul(&q_pt.x, &z1z1, p);
591 let s1 = field_mul(&p_pt.y, &z2z2z2, p);
592 let s2 = field_mul(&q_pt.y, &z1z1z1, p);
593
594 let h = field_sub(&u2, &u1, p);
595 let r = field_sub(&s2, &s1, p);
596
597 let h2 = field_sqr(&h, p);
598 let h3 = field_mul(&h2, &h, p);
599 let u1h2 = field_mul(&u1, &h2, p);
600
601 let r2 = field_sqr(&r, p);
602 let u1h2_2 = field_add(&u1h2, &u1h2, p);
603 let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
604
605 let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
606 let r_term = field_mul(&r, &u1h2_minus_x3, p);
607 let s1h3 = field_mul(&s1, &h3, p);
608 let y3 = field_sub(&r_term, &s1h3, p);
609
610 let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
611 let z3 = field_mul(&z1z2, &h, p);
612
613 let general = JacobianPoint { x: x3, y: y3, z: z3 };
614
615 let mut out = general;
620 out = ct_select_point(p_pt, &out, q_inf as u64);
621 out = ct_select_point(q_pt, &out, p_inf as u64);
622 out
623}
624
625fn ct_select_point<const LIMBS: usize>(
631 a: &JacobianPoint<LIMBS>,
632 b: &JacobianPoint<LIMBS>,
633 condition: u64,
634) -> JacobianPoint<LIMBS> {
635 let mask = 0u64.wrapping_sub(condition);
636 let inv = !mask;
637 let mut out = JacobianPoint {
638 x: FieldElement { limbs: [0u64; LIMBS] },
639 y: FieldElement { limbs: [0u64; LIMBS] },
640 z: FieldElement { limbs: [0u64; LIMBS] },
641 };
642 for i in 0..LIMBS {
643 out.x.limbs[i] = (a.x.limbs[i] & mask) | (b.x.limbs[i] & inv);
644 out.y.limbs[i] = (a.y.limbs[i] & mask) | (b.y.limbs[i] & inv);
645 out.z.limbs[i] = (a.z.limbs[i] & mask) | (b.z.limbs[i] & inv);
646 }
647 out
648}
649
650pub fn scalar_mul_point<const LIMBS: usize>(
672 k: &FieldElement<LIMBS>,
673 point: &JacobianPoint<LIMBS>,
674 params: &CurveParams<LIMBS>,
675) -> JacobianPoint<LIMBS> {
676 let mut r0 = JacobianPoint::infinity();
677 let mut r1 = *point;
678
679 let total_bits = LIMBS * 64;
680 for i in (0..total_bits).rev() {
681 let limb_idx = i / 64;
682 let bit_idx = i % 64;
683 let bit = (k.limbs[limb_idx] >> bit_idx) & 1;
684
685 ct_swap(&mut r0, &mut r1, bit);
686 r1 = point_add_ct(&r0, &r1, params);
687 r0 = point_double(&r0, params);
688 ct_swap(&mut r0, &mut r1, bit);
689 }
690
691 r0
692}
693
694fn ct_swap<const LIMBS: usize>(a: &mut JacobianPoint<LIMBS>, b: &mut JacobianPoint<LIMBS>, condition: u64) {
696 let mask = 0u64.wrapping_sub(condition);
697 for i in 0..LIMBS {
698 let t = mask & (a.x.limbs[i] ^ b.x.limbs[i]);
699 a.x.limbs[i] ^= t;
700 b.x.limbs[i] ^= t;
701
702 let t = mask & (a.y.limbs[i] ^ b.y.limbs[i]);
703 a.y.limbs[i] ^= t;
704 b.y.limbs[i] ^= t;
705
706 let t = mask & (a.z.limbs[i] ^ b.z.limbs[i]);
707 a.z.limbs[i] ^= t;
708 b.z.limbs[i] ^= t;
709 }
710}
711
712pub fn double_scalar_mul<const LIMBS: usize>(
715 k1: &FieldElement<LIMBS>,
716 g: &JacobianPoint<LIMBS>,
717 k2: &FieldElement<LIMBS>,
718 q: &JacobianPoint<LIMBS>,
719 params: &CurveParams<LIMBS>,
720) -> JacobianPoint<LIMBS> {
721 let total_bits = LIMBS * 64;
723 let mut result = JacobianPoint::infinity();
724
725 let g_plus_q = point_add(g, q, params);
727
728 for i in (0..total_bits).rev() {
729 result = point_double(&result, params);
730
731 let limb_idx = i / 64;
732 let bit_idx = i % 64;
733 let b1 = (k1.limbs[limb_idx] >> bit_idx) & 1;
734 let b2 = (k2.limbs[limb_idx] >> bit_idx) & 1;
735
736 let to_add = match (b1, b2) {
737 (1, 1) => Some(&g_plus_q),
738 (1, 0) => Some(g),
739 (0, 1) => Some(q),
740 _ => None,
741 };
742
743 if let Some(pt) = to_add {
744 result = point_add(&result, pt, params);
745 }
746 }
747
748 result
749}
750
751#[cfg(test)]
752mod tests {
753 use super::*;
754
755 fn hex_to_bytes(hex: &str) -> Vec<u8> {
756 (0..hex.len())
757 .step_by(2)
758 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
759 .collect()
760 }
761
762 #[test]
763 fn test_p256_generator_on_curve() {
764 let params = p256_params();
765 let p = ¶ms.p;
766
767 let x = ¶ms.gx;
769 let y = ¶ms.gy;
770 let y2 = field_sqr(y, p);
771
772 let x2 = field_sqr(x, p);
773 let x3 = field_mul(&x2, x, p);
774 let ax = field_mul(¶ms.a, x, p);
775 let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
776
777 assert_eq!(y2, rhs, "Generator point not on curve");
778 }
779
780 #[test]
781 fn test_p256_scalar_mul_generator() {
782 let params = p256_params();
783 let g = JacobianPoint::from_affine(params.gx, params.gy);
784
785 let one = FieldElement::<4>::one();
787 let result = scalar_mul_point(&one, &g, ¶ms);
788 let (rx, ry) = result.to_affine(¶ms.p).unwrap();
789 assert_eq!(rx, params.gx);
790 assert_eq!(ry, params.gy);
791 }
792
793 #[test]
794 fn test_p256_double_generator() {
795 let params = p256_params();
796 let g = JacobianPoint::from_affine(params.gx, params.gy);
797
798 let two = FieldElement::<4>::from_bytes_be(&[2]);
800 let result = scalar_mul_point(&two, &g, ¶ms);
801 let (rx, _ry) = result.to_affine(¶ms.p).unwrap();
802
803 let doubled = point_double(&g, ¶ms);
805 let (dx, _dy) = doubled.to_affine(¶ms.p).unwrap();
806
807 assert_eq!(rx, dx);
808 }
809
810 #[test]
811 fn test_p256_n_times_g_is_infinity() {
812 let params = p256_params();
813 let g = JacobianPoint::from_affine(params.gx, params.gy);
814
815 let n = FieldElement::<4> { limbs: params.n };
817 let result = scalar_mul_point(&n, &g, ¶ms);
818 assert!(result.is_infinity(), "n*G should be infinity");
819 }
820
821 fn check_generator_on_curve<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
823 assert!(is_on_curve(¶ms.gx, ¶ms.gy, params), "Generator not on curve");
824 }
825
826 fn check_n_times_g_is_infinity<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
829 let g = JacobianPoint::from_affine(params.gx, params.gy);
830 let n = FieldElement::<LIMBS> { limbs: params.n };
831 let result = scalar_mul_point(&n, &g, params);
832 assert!(result.is_infinity(), "n*G is not infinity");
833 }
834
835 #[test]
836 fn test_p384_generator_on_curve() {
837 check_generator_on_curve(&p384_params());
838 }
839
840 #[test]
841 fn test_p384_n_times_g_is_infinity() {
842 check_n_times_g_is_infinity(&p384_params());
843 }
844
845 #[test]
846 fn test_secp256k1_generator_on_curve() {
847 check_generator_on_curve(&secp256k1_params());
848 }
849
850 #[test]
851 fn test_secp256k1_n_times_g_is_infinity() {
852 check_n_times_g_is_infinity(&secp256k1_params());
853 }
854
855 #[test]
856 fn test_brainpoolp256r1_generator_on_curve() {
857 check_generator_on_curve(&brainpoolp256r1_params());
858 }
859
860 #[test]
861 fn test_brainpoolp256r1_n_times_g_is_infinity() {
862 check_n_times_g_is_infinity(&brainpoolp256r1_params());
863 }
864
865 #[test]
866 fn test_brainpoolp384r1_generator_on_curve() {
867 check_generator_on_curve(&brainpoolp384r1_params());
868 }
869
870 #[test]
871 fn test_brainpoolp384r1_n_times_g_is_infinity() {
872 check_n_times_g_is_infinity(&brainpoolp384r1_params());
873 }
874
875 #[test]
876 fn test_brainpoolp512r1_generator_on_curve() {
877 check_generator_on_curve(&brainpoolp512r1_params());
878 }
879
880 #[test]
881 fn test_brainpoolp512r1_n_times_g_is_infinity() {
882 check_n_times_g_is_infinity(&brainpoolp512r1_params());
883 }
884
885 #[test]
886 fn test_secp521r1_generator_on_curve() {
887 check_generator_on_curve(&secp521r1_params());
888 }
889
890 #[test]
891 fn test_secp521r1_n_times_g_is_infinity() {
892 check_n_times_g_is_infinity(&secp521r1_params());
893 }
894
895 #[test]
896 fn test_p256_known_2g() {
897 let params = p256_params();
899 let g = JacobianPoint::from_affine(params.gx, params.gy);
900
901 let two = FieldElement::<4>::from_bytes_be(&[2]);
902 let result = scalar_mul_point(&two, &g, ¶ms);
903 let (rx, ry) = result.to_affine(¶ms.p).unwrap();
904
905 let expected_x = hex_to_bytes("7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978");
906 let expected_y = hex_to_bytes("07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1");
907
908 assert_eq!(rx.to_bytes_be(), expected_x);
909 assert_eq!(ry.to_bytes_be(), expected_y);
910 }
911}