1use super::field::*;
9
10#[derive(Clone, Copy, Debug)]
20pub struct JacobianPoint<const LIMBS: usize> {
21 pub x: FieldElement<LIMBS>,
23 pub y: FieldElement<LIMBS>,
25 pub z: FieldElement<LIMBS>,
27}
28
29impl<const LIMBS: usize> JacobianPoint<LIMBS> {
30 pub fn infinity() -> Self {
32 Self {
33 x: FieldElement::one(),
34 y: FieldElement::one(),
35 z: FieldElement::ZERO,
36 }
37 }
38
39 pub fn from_affine(x: FieldElement<LIMBS>, y: FieldElement<LIMBS>) -> Self {
41 Self {
42 x,
43 y,
44 z: FieldElement::one(),
45 }
46 }
47
48 pub fn is_infinity(&self) -> bool {
50 self.z.is_zero()
51 }
52
53 pub fn to_affine(&self, p: &[u64; LIMBS]) -> Option<(FieldElement<LIMBS>, FieldElement<LIMBS>)> {
55 if self.z.is_zero() {
56 return None;
57 }
58 let z_inv = field_inv(&self.z, p);
59 let z_inv2 = field_sqr(&z_inv, p);
60 let z_inv3 = field_mul(&z_inv2, &z_inv, p);
61 let x = field_mul(&self.x, &z_inv2, p);
62 let y = field_mul(&self.y, &z_inv3, p);
63 Some((x, y))
64 }
65}
66
67pub struct CurveParams<const LIMBS: usize> {
69 pub p: [u64; LIMBS],
71 pub a: FieldElement<LIMBS>,
73 pub b: FieldElement<LIMBS>,
75 pub gx: FieldElement<LIMBS>,
77 pub gy: FieldElement<LIMBS>,
79 pub n: [u64; LIMBS],
81 pub qlen_bits: usize,
91 pub felem_bytes: usize,
118}
119
120pub fn hex_to_fe<const LIMBS: usize>(hex: &str) -> FieldElement<LIMBS> {
127 let bytes: Vec<u8> = (0..hex.len())
128 .step_by(2)
129 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
130 .collect();
131 FieldElement::from_bytes_be(&bytes)
132}
133
134pub fn hex_to_limbs<const LIMBS: usize>(hex: &str) -> [u64; LIMBS] {
137 hex_to_fe::<LIMBS>(hex).limbs
138}
139
140fn hex_to_fe4(hex: &str) -> FieldElement<4> {
142 hex_to_fe::<4>(hex)
143}
144fn hex_to_fe6(hex: &str) -> FieldElement<6> {
145 hex_to_fe::<6>(hex)
146}
147
148pub fn p256_params() -> CurveParams<4> {
154 CurveParams {
155 p: P256_P,
156 a: hex_to_fe4("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"),
157 b: hex_to_fe4("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"),
158 gx: hex_to_fe4("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"),
159 gy: hex_to_fe4("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"),
160 n: P256_N,
161 qlen_bits: 256,
162 felem_bytes: 32,
163 }
164}
165
166pub fn p384_params() -> CurveParams<6> {
168 CurveParams {
169 p: P384_P,
170 a: hex_to_fe6(
171 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
172 ),
173 b: hex_to_fe6(
174 "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
175 ),
176 gx: hex_to_fe6(
177 "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
178 ),
179 gy: hex_to_fe6(
180 "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
181 ),
182 n: P384_N,
183 qlen_bits: 384,
184 felem_bytes: 48,
185 }
186}
187
188pub fn secp256k1_params() -> CurveParams<4> {
202 CurveParams {
203 p: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"),
204 a: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000000"),
205 b: hex_to_fe::<4>("0000000000000000000000000000000000000000000000000000000000000007"),
206 gx: hex_to_fe::<4>("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"),
207 gy: hex_to_fe::<4>("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8"),
208 n: hex_to_limbs::<4>("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"),
209 qlen_bits: 256,
210 felem_bytes: 32,
211 }
212}
213
214pub fn brainpoolp256r1_params() -> CurveParams<4> {
220 CurveParams {
221 p: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377"),
222 a: hex_to_fe::<4>("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9"),
223 b: hex_to_fe::<4>("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6"),
224 gx: hex_to_fe::<4>("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262"),
225 gy: hex_to_fe::<4>("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997"),
226 n: hex_to_limbs::<4>("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7"),
227 qlen_bits: 256,
228 felem_bytes: 32,
229 }
230}
231
232pub fn brainpoolp384r1_params() -> CurveParams<6> {
238 CurveParams {
239 p: hex_to_limbs::<6>(
240 "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53",
241 ),
242 a: hex_to_fe::<6>(
243 "7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826",
244 ),
245 b: hex_to_fe::<6>(
246 "04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11",
247 ),
248 gx: hex_to_fe::<6>(
249 "1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E",
250 ),
251 gy: hex_to_fe::<6>(
252 "8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315",
253 ),
254 n: hex_to_limbs::<6>(
255 "8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565",
256 ),
257 qlen_bits: 384,
258 felem_bytes: 48,
259 }
260}
261
262pub fn brainpoolp512r1_params() -> CurveParams<8> {
268 CurveParams {
269 p: hex_to_limbs::<8>(
270 "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3",
271 ),
272 a: hex_to_fe::<8>(
273 "7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA",
274 ),
275 b: hex_to_fe::<8>(
276 "3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723",
277 ),
278 gx: hex_to_fe::<8>(
279 "81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822",
280 ),
281 gy: hex_to_fe::<8>(
282 "7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892",
283 ),
284 n: hex_to_limbs::<8>(
285 "AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069",
286 ),
287 qlen_bits: 512,
288 felem_bytes: 64,
289 }
290}
291
292pub fn secp521r1_params() -> CurveParams<9> {
313 CurveParams {
316 p: hex_to_limbs::<9>(
317 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
318 ),
319 a: hex_to_fe::<9>(
320 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
321 ),
322 b: hex_to_fe::<9>(
323 "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
324 ),
325 gx: hex_to_fe::<9>(
326 "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
327 ),
328 gy: hex_to_fe::<9>(
329 "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
330 ),
331 n: hex_to_limbs::<9>(
332 "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
333 ),
334 qlen_bits: 521,
335 felem_bytes: 66,
339 }
340}
341
342pub fn is_on_curve<const LIMBS: usize>(
356 x: &FieldElement<LIMBS>,
357 y: &FieldElement<LIMBS>,
358 params: &CurveParams<LIMBS>,
359) -> bool {
360 let p = ¶ms.p;
361 let y2 = field_sqr(y, p);
362 let x2 = field_sqr(x, p);
363 let x3 = field_mul(&x2, x, p);
364 let ax = field_mul(¶ms.a, x, p);
365 let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
366 y2 == rhs
367}
368
369pub fn point_double<const LIMBS: usize>(
382 pt: &JacobianPoint<LIMBS>,
383 params: &CurveParams<LIMBS>,
384) -> JacobianPoint<LIMBS> {
385 let p = ¶ms.p;
386
387 let xx = field_sqr(&pt.x, p);
392 let yy = field_sqr(&pt.y, p);
393 let yyyy = field_sqr(&yy, p);
394 let zz = field_sqr(&pt.z, p);
395
396 let x_plus_yy = field_add(&pt.x, &yy, p);
398 let x_plus_yy_sq = field_sqr(&x_plus_yy, p);
399 let s_inner = field_sub(&field_sub(&x_plus_yy_sq, &xx, p), &yyyy, p);
400 let s = field_add(&s_inner, &s_inner, p);
401
402 let two_xx = field_add(&xx, &xx, p);
404 let three_xx = field_add(&two_xx, &xx, p);
405 let zz_sq = field_sqr(&zz, p);
406 let a_zz_sq = field_mul(¶ms.a, &zz_sq, p);
407 let m = field_add(&three_xx, &a_zz_sq, p);
408
409 let m_sq = field_sqr(&m, p);
411 let two_s = field_add(&s, &s, p);
412 let t = field_sub(&m_sq, &two_s, p);
413
414 let x3 = t;
416
417 let s_minus_t = field_sub(&s, &t, p);
419 let m_term = field_mul(&m, &s_minus_t, p);
420 let two_yyyy = field_add(&yyyy, &yyyy, p);
421 let four_yyyy = field_add(&two_yyyy, &two_yyyy, p);
422 let eight_yyyy = field_add(&four_yyyy, &four_yyyy, p);
423 let y3 = field_sub(&m_term, &eight_yyyy, p);
424
425 let y_plus_z = field_add(&pt.y, &pt.z, p);
428 let y_plus_z_sq = field_sqr(&y_plus_z, p);
429 let z3 = field_sub(&field_sub(&y_plus_z_sq, &yy, p), &zz, p);
430
431 JacobianPoint { x: x3, y: y3, z: z3 }
432}
433
434pub fn point_add<const LIMBS: usize>(
450 p_pt: &JacobianPoint<LIMBS>,
451 q_pt: &JacobianPoint<LIMBS>,
452 params: &CurveParams<LIMBS>,
453) -> JacobianPoint<LIMBS> {
454 let p = ¶ms.p;
455
456 let p_inf = p_pt.z.is_zero();
458 let q_inf = q_pt.z.is_zero();
459
460 let z1z1 = field_sqr(&p_pt.z, p);
462 let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
463
464 let z2z2 = field_sqr(&q_pt.z, p);
466 let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
467
468 let u1 = field_mul(&p_pt.x, &z2z2, p);
470 let u2 = field_mul(&q_pt.x, &z1z1, p);
471
472 let s1 = field_mul(&p_pt.y, &z2z2z2, p);
474 let s2 = field_mul(&q_pt.y, &z1z1z1, p);
475
476 let h = field_sub(&u2, &u1, p);
478 let r = field_sub(&s2, &s1, p);
480
481 let h_zero = h.is_zero();
483 let r_zero = r.is_zero();
484
485 if !p_inf && !q_inf && h_zero && r_zero {
486 return point_double(p_pt, params);
487 }
488 if !p_inf && !q_inf && h_zero && !r_zero {
490 return JacobianPoint::infinity();
491 }
492
493 let h2 = field_sqr(&h, p);
494 let h3 = field_mul(&h2, &h, p);
495 let u1h2 = field_mul(&u1, &h2, p);
496
497 let r2 = field_sqr(&r, p);
499 let u1h2_2 = field_add(&u1h2, &u1h2, p);
500 let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
501
502 let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
504 let r_term = field_mul(&r, &u1h2_minus_x3, p);
505 let s1h3 = field_mul(&s1, &h3, p);
506 let y3 = field_sub(&r_term, &s1h3, p);
507
508 let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
510 let z3 = field_mul(&z1z2, &h, p);
511
512 let result = JacobianPoint { x: x3, y: y3, z: z3 };
513
514 if p_inf {
516 *q_pt
517 } else if q_inf {
518 *p_pt
519 } else {
520 result
521 }
522}
523
524fn point_add_ct<const LIMBS: usize>(
541 p_pt: &JacobianPoint<LIMBS>,
542 q_pt: &JacobianPoint<LIMBS>,
543 params: &CurveParams<LIMBS>,
544) -> JacobianPoint<LIMBS> {
545 let p = ¶ms.p;
546
547 let p_inf = p_pt.z.is_zero();
548 let q_inf = q_pt.z.is_zero();
549
550 let z1z1 = field_sqr(&p_pt.z, p);
551 let z1z1z1 = field_mul(&z1z1, &p_pt.z, p);
552 let z2z2 = field_sqr(&q_pt.z, p);
553 let z2z2z2 = field_mul(&z2z2, &q_pt.z, p);
554
555 let u1 = field_mul(&p_pt.x, &z2z2, p);
556 let u2 = field_mul(&q_pt.x, &z1z1, p);
557 let s1 = field_mul(&p_pt.y, &z2z2z2, p);
558 let s2 = field_mul(&q_pt.y, &z1z1z1, p);
559
560 let h = field_sub(&u2, &u1, p);
561 let r = field_sub(&s2, &s1, p);
562
563 let h2 = field_sqr(&h, p);
564 let h3 = field_mul(&h2, &h, p);
565 let u1h2 = field_mul(&u1, &h2, p);
566
567 let r2 = field_sqr(&r, p);
568 let u1h2_2 = field_add(&u1h2, &u1h2, p);
569 let x3 = field_sub(&field_sub(&r2, &h3, p), &u1h2_2, p);
570
571 let u1h2_minus_x3 = field_sub(&u1h2, &x3, p);
572 let r_term = field_mul(&r, &u1h2_minus_x3, p);
573 let s1h3 = field_mul(&s1, &h3, p);
574 let y3 = field_sub(&r_term, &s1h3, p);
575
576 let z1z2 = field_mul(&p_pt.z, &q_pt.z, p);
577 let z3 = field_mul(&z1z2, &h, p);
578
579 let general = JacobianPoint { x: x3, y: y3, z: z3 };
580
581 let mut out = general;
586 out = ct_select_point(p_pt, &out, q_inf as u64);
587 out = ct_select_point(q_pt, &out, p_inf as u64);
588 out
589}
590
591fn ct_select_point<const LIMBS: usize>(
597 a: &JacobianPoint<LIMBS>,
598 b: &JacobianPoint<LIMBS>,
599 condition: u64,
600) -> JacobianPoint<LIMBS> {
601 let mask = 0u64.wrapping_sub(condition);
602 let inv = !mask;
603 let mut out = JacobianPoint {
604 x: FieldElement { limbs: [0u64; LIMBS] },
605 y: FieldElement { limbs: [0u64; LIMBS] },
606 z: FieldElement { limbs: [0u64; LIMBS] },
607 };
608 for i in 0..LIMBS {
609 out.x.limbs[i] = (a.x.limbs[i] & mask) | (b.x.limbs[i] & inv);
610 out.y.limbs[i] = (a.y.limbs[i] & mask) | (b.y.limbs[i] & inv);
611 out.z.limbs[i] = (a.z.limbs[i] & mask) | (b.z.limbs[i] & inv);
612 }
613 out
614}
615
616pub fn scalar_mul_point<const LIMBS: usize>(
638 k: &FieldElement<LIMBS>,
639 point: &JacobianPoint<LIMBS>,
640 params: &CurveParams<LIMBS>,
641) -> JacobianPoint<LIMBS> {
642 let mut r0 = JacobianPoint::infinity();
643 let mut r1 = *point;
644
645 let total_bits = LIMBS * 64;
646 for i in (0..total_bits).rev() {
647 let limb_idx = i / 64;
648 let bit_idx = i % 64;
649 let bit = (k.limbs[limb_idx] >> bit_idx) & 1;
650
651 ct_swap(&mut r0, &mut r1, bit);
652 r1 = point_add_ct(&r0, &r1, params);
653 r0 = point_double(&r0, params);
654 ct_swap(&mut r0, &mut r1, bit);
655 }
656
657 r0
658}
659
660fn ct_swap<const LIMBS: usize>(a: &mut JacobianPoint<LIMBS>, b: &mut JacobianPoint<LIMBS>, condition: u64) {
662 let mask = 0u64.wrapping_sub(condition);
663 for i in 0..LIMBS {
664 let t = mask & (a.x.limbs[i] ^ b.x.limbs[i]);
665 a.x.limbs[i] ^= t;
666 b.x.limbs[i] ^= t;
667
668 let t = mask & (a.y.limbs[i] ^ b.y.limbs[i]);
669 a.y.limbs[i] ^= t;
670 b.y.limbs[i] ^= t;
671
672 let t = mask & (a.z.limbs[i] ^ b.z.limbs[i]);
673 a.z.limbs[i] ^= t;
674 b.z.limbs[i] ^= t;
675 }
676}
677
678pub fn double_scalar_mul<const LIMBS: usize>(
681 k1: &FieldElement<LIMBS>,
682 g: &JacobianPoint<LIMBS>,
683 k2: &FieldElement<LIMBS>,
684 q: &JacobianPoint<LIMBS>,
685 params: &CurveParams<LIMBS>,
686) -> JacobianPoint<LIMBS> {
687 let total_bits = LIMBS * 64;
689 let mut result = JacobianPoint::infinity();
690
691 let g_plus_q = point_add(g, q, params);
693
694 for i in (0..total_bits).rev() {
695 result = point_double(&result, params);
696
697 let limb_idx = i / 64;
698 let bit_idx = i % 64;
699 let b1 = (k1.limbs[limb_idx] >> bit_idx) & 1;
700 let b2 = (k2.limbs[limb_idx] >> bit_idx) & 1;
701
702 let to_add = match (b1, b2) {
703 (1, 1) => Some(&g_plus_q),
704 (1, 0) => Some(g),
705 (0, 1) => Some(q),
706 _ => None,
707 };
708
709 if let Some(pt) = to_add {
710 result = point_add(&result, pt, params);
711 }
712 }
713
714 result
715}
716
717#[cfg(test)]
718mod tests {
719 use super::*;
720
721 fn hex_to_bytes(hex: &str) -> Vec<u8> {
722 (0..hex.len())
723 .step_by(2)
724 .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
725 .collect()
726 }
727
728 #[test]
729 fn test_p256_generator_on_curve() {
730 let params = p256_params();
731 let p = ¶ms.p;
732
733 let x = ¶ms.gx;
735 let y = ¶ms.gy;
736 let y2 = field_sqr(y, p);
737
738 let x2 = field_sqr(x, p);
739 let x3 = field_mul(&x2, x, p);
740 let ax = field_mul(¶ms.a, x, p);
741 let rhs = field_add(&field_add(&x3, &ax, p), ¶ms.b, p);
742
743 assert_eq!(y2, rhs, "Generator point not on curve");
744 }
745
746 #[test]
747 fn test_p256_scalar_mul_generator() {
748 let params = p256_params();
749 let g = JacobianPoint::from_affine(params.gx, params.gy);
750
751 let one = FieldElement::<4>::one();
753 let result = scalar_mul_point(&one, &g, ¶ms);
754 let (rx, ry) = result.to_affine(¶ms.p).unwrap();
755 assert_eq!(rx, params.gx);
756 assert_eq!(ry, params.gy);
757 }
758
759 #[test]
760 fn test_p256_double_generator() {
761 let params = p256_params();
762 let g = JacobianPoint::from_affine(params.gx, params.gy);
763
764 let two = FieldElement::<4>::from_bytes_be(&[2]);
766 let result = scalar_mul_point(&two, &g, ¶ms);
767 let (rx, _ry) = result.to_affine(¶ms.p).unwrap();
768
769 let doubled = point_double(&g, ¶ms);
771 let (dx, _dy) = doubled.to_affine(¶ms.p).unwrap();
772
773 assert_eq!(rx, dx);
774 }
775
776 #[test]
777 fn test_p256_n_times_g_is_infinity() {
778 let params = p256_params();
779 let g = JacobianPoint::from_affine(params.gx, params.gy);
780
781 let n = FieldElement::<4> { limbs: params.n };
783 let result = scalar_mul_point(&n, &g, ¶ms);
784 assert!(result.is_infinity(), "n*G should be infinity");
785 }
786
787 fn check_generator_on_curve<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
789 assert!(is_on_curve(¶ms.gx, ¶ms.gy, params), "Generator not on curve");
790 }
791
792 fn check_n_times_g_is_infinity<const LIMBS: usize>(params: &CurveParams<LIMBS>) {
795 let g = JacobianPoint::from_affine(params.gx, params.gy);
796 let n = FieldElement::<LIMBS> { limbs: params.n };
797 let result = scalar_mul_point(&n, &g, params);
798 assert!(result.is_infinity(), "n*G is not infinity");
799 }
800
801 #[test]
802 fn test_p384_generator_on_curve() {
803 check_generator_on_curve(&p384_params());
804 }
805
806 #[test]
807 fn test_p384_n_times_g_is_infinity() {
808 check_n_times_g_is_infinity(&p384_params());
809 }
810
811 #[test]
812 fn test_secp256k1_generator_on_curve() {
813 check_generator_on_curve(&secp256k1_params());
814 }
815
816 #[test]
817 fn test_secp256k1_n_times_g_is_infinity() {
818 check_n_times_g_is_infinity(&secp256k1_params());
819 }
820
821 #[test]
822 fn test_brainpoolp256r1_generator_on_curve() {
823 check_generator_on_curve(&brainpoolp256r1_params());
824 }
825
826 #[test]
827 fn test_brainpoolp256r1_n_times_g_is_infinity() {
828 check_n_times_g_is_infinity(&brainpoolp256r1_params());
829 }
830
831 #[test]
832 fn test_brainpoolp384r1_generator_on_curve() {
833 check_generator_on_curve(&brainpoolp384r1_params());
834 }
835
836 #[test]
837 fn test_brainpoolp384r1_n_times_g_is_infinity() {
838 check_n_times_g_is_infinity(&brainpoolp384r1_params());
839 }
840
841 #[test]
842 fn test_brainpoolp512r1_generator_on_curve() {
843 check_generator_on_curve(&brainpoolp512r1_params());
844 }
845
846 #[test]
847 fn test_brainpoolp512r1_n_times_g_is_infinity() {
848 check_n_times_g_is_infinity(&brainpoolp512r1_params());
849 }
850
851 #[test]
852 fn test_secp521r1_generator_on_curve() {
853 check_generator_on_curve(&secp521r1_params());
854 }
855
856 #[test]
857 fn test_secp521r1_n_times_g_is_infinity() {
858 check_n_times_g_is_infinity(&secp521r1_params());
859 }
860
861 #[test]
862 fn test_p256_known_2g() {
863 let params = p256_params();
865 let g = JacobianPoint::from_affine(params.gx, params.gy);
866
867 let two = FieldElement::<4>::from_bytes_be(&[2]);
868 let result = scalar_mul_point(&two, &g, ¶ms);
869 let (rx, ry) = result.to_affine(¶ms.p).unwrap();
870
871 let expected_x = hex_to_bytes("7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978");
872 let expected_y = hex_to_bytes("07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1");
873
874 assert_eq!(rx.to_bytes_be(), expected_x);
875 assert_eq!(ry.to_bytes_be(), expected_y);
876 }
877}