1use crate::public_key::bigint::{BigUint, MontgomeryCtx};
47use crate::public_key::gf2m::{gf2m_add, gf2m_half_trace, gf2m_inv, gf2m_mul, gf2m_sq};
48use crate::public_key::primes::{mod_inverse, random_nonzero_below};
49use crate::Csprng;
50
51#[derive(Clone, Debug)]
60pub(crate) enum FieldCtx {
61 Prime(MontgomeryCtx),
63 Binary {
65 poly: BigUint,
67 degree: usize,
69 },
70}
71
72#[derive(Clone, Debug)]
81pub struct CurveParams {
82 pub p: BigUint,
84 pub a: BigUint,
86 pub b: BigUint,
88 pub n: BigUint,
90 pub h: u64,
92 pub gx: BigUint,
94 pub gy: BigUint,
96 pub(crate) field: FieldCtx,
103 pub(crate) _scalar: MontgomeryCtx,
109 pub coord_len: usize,
114}
115
116#[derive(Clone, Debug, Eq, PartialEq)]
121pub struct AffinePoint {
122 pub x: BigUint,
124 pub y: BigUint,
126 pub infinity: bool,
128}
129
130struct JacobianPoint {
138 x: BigUint,
139 y: BigUint,
140 z: BigUint,
141}
142
143impl AffinePoint {
146 #[must_use]
148 pub fn infinity() -> Self {
149 Self {
150 x: BigUint::zero(),
151 y: BigUint::zero(),
152 infinity: true,
153 }
154 }
155
156 #[must_use]
161 pub fn new(x: BigUint, y: BigUint) -> Self {
162 Self {
163 x,
164 y,
165 infinity: false,
166 }
167 }
168
169 #[must_use]
171 pub fn is_infinity(&self) -> bool {
172 self.infinity
173 }
174}
175
176impl JacobianPoint {
179 fn infinity() -> Self {
181 Self {
184 x: BigUint::one(),
185 y: BigUint::one(),
186 z: BigUint::zero(),
187 }
188 }
189
190 fn is_infinity(&self) -> bool {
191 self.z.is_zero()
192 }
193
194 fn from_affine(p: &AffinePoint) -> Self {
199 if p.infinity {
200 return Self::infinity();
201 }
202 Self {
203 x: p.x.clone(),
204 y: p.y.clone(),
205 z: BigUint::one(),
206 }
207 }
208
209 fn to_affine(&self, curve: &CurveParams) -> AffinePoint {
217 if self.is_infinity() {
218 return AffinePoint::infinity();
219 }
220 if self.z == BigUint::one() {
222 return AffinePoint::new(self.x.clone(), self.y.clone());
223 }
224
225 let ctx = curve.prime_ctx();
226 let p = &curve.p;
227
228 let p_minus_2 = p.sub_ref(&BigUint::from_u64(2));
230 let z_inv = ctx.pow(&self.z, &p_minus_2);
231
232 let z_inv2 = ctx.square(&z_inv);
234 let z_inv3 = ctx.mul(&z_inv2, &z_inv);
235
236 let x = ctx.mul(&self.x, &z_inv2);
237 let y = ctx.mul(&self.y, &z_inv3);
238
239 AffinePoint::new(x, y)
240 }
241}
242
243#[inline]
250fn field_add(a: &BigUint, b: &BigUint, p: &BigUint) -> BigUint {
251 let sum = a.add_ref(b);
252 if &sum >= p {
253 sum.sub_ref(p)
254 } else {
255 sum
256 }
257}
258
259#[inline]
263fn field_sub(a: &BigUint, b: &BigUint, p: &BigUint) -> BigUint {
264 if a >= b {
265 a.sub_ref(b)
266 } else {
267 p.sub_ref(&b.sub_ref(a))
270 }
271}
272
273#[inline]
275fn field_neg(a: &BigUint, p: &BigUint) -> BigUint {
276 if a.is_zero() {
277 BigUint::zero()
278 } else {
279 p.sub_ref(a)
280 }
281}
282
283fn pad_to(bytes: Vec<u8>, len: usize) -> Vec<u8> {
289 if bytes.len() >= len {
290 return bytes;
291 }
292 let mut out = vec![0u8; len - bytes.len()];
293 out.extend_from_slice(&bytes);
294 out
295}
296
297fn point_double_jacobian(curve: &CurveParams, p: &JacobianPoint) -> JacobianPoint {
316 if p.is_infinity() {
317 return JacobianPoint::infinity();
318 }
319
320 let ctx = curve.prime_ctx();
321 let m = &curve.p;
322
323 let y2 = ctx.square(&p.y);
325 let y4 = ctx.square(&y2);
326
327 let xy2 = ctx.mul(&p.x, &y2);
329 let two_xy2 = field_add(&xy2, &xy2, m);
330 let a = field_add(&two_xy2, &two_xy2, m);
331
332 let x2 = ctx.square(&p.x);
334 let z2 = ctx.square(&p.z);
335 let z4 = ctx.square(&z2);
336
337 let three_x2 = field_add(&field_add(&x2, &x2, m), &x2, m);
339 let a_coeff_z4 = ctx.mul(&curve.a, &z4);
340 let b = field_add(&three_x2, &a_coeff_z4, m);
341
342 let b2 = ctx.square(&b);
344 let two_a = field_add(&a, &a, m);
345 let x_new = field_sub(&b2, &two_a, m);
346
347 let a_minus_x = field_sub(&a, &x_new, m);
349 let b_times = ctx.mul(&b, &a_minus_x);
350 let two_y4 = field_add(&y4, &y4, m);
351 let four_y4 = field_add(&two_y4, &two_y4, m);
352 let eight_y4 = field_add(&four_y4, &four_y4, m);
353 let y_new = field_sub(&b_times, &eight_y4, m);
354
355 let yz = ctx.mul(&p.y, &p.z);
357 let z_new = field_add(&yz, &yz, m);
358
359 JacobianPoint {
360 x: x_new,
361 y: y_new,
362 z: z_new,
363 }
364}
365
366fn point_add_jacobian(
382 curve: &CurveParams,
383 p1: &JacobianPoint,
384 p2: &JacobianPoint,
385) -> JacobianPoint {
386 if p1.is_infinity() {
387 return JacobianPoint {
388 x: p2.x.clone(),
389 y: p2.y.clone(),
390 z: p2.z.clone(),
391 };
392 }
393 if p2.is_infinity() {
394 return JacobianPoint {
395 x: p1.x.clone(),
396 y: p1.y.clone(),
397 z: p1.z.clone(),
398 };
399 }
400
401 let ctx = curve.prime_ctx();
402 let m = &curve.p;
403
404 let z1_2 = ctx.square(&p1.z);
405 let z2_2 = ctx.square(&p2.z);
406 let z1_3 = ctx.mul(&z1_2, &p1.z);
407 let z2_3 = ctx.mul(&z2_2, &p2.z);
408
409 let u1 = ctx.mul(&p1.x, &z2_2);
410 let u2 = ctx.mul(&p2.x, &z1_2);
411 let s1 = ctx.mul(&p1.y, &z2_3);
412 let s2 = ctx.mul(&p2.y, &z1_3);
413
414 let h = field_sub(&u2, &u1, m);
415 let r = field_sub(&s2, &s1, m);
416
417 if h.is_zero() {
418 return if r.is_zero() {
419 point_double_jacobian(curve, p1)
422 } else {
423 JacobianPoint::infinity()
425 };
426 }
427
428 let h2 = ctx.square(&h);
429 let h3 = ctx.mul(&h2, &h);
430 let u1h2 = ctx.mul(&u1, &h2);
431
432 let r2 = ctx.square(&r);
434 let two_u1h2 = field_add(&u1h2, &u1h2, m);
435 let x3 = field_sub(&field_sub(&r2, &h3, m), &two_u1h2, m);
436
437 let u1h2_minus_x3 = field_sub(&u1h2, &x3, m);
439 let r_term = ctx.mul(&r, &u1h2_minus_x3);
440 let s1h3 = ctx.mul(&s1, &h3);
441 let y3 = field_sub(&r_term, &s1h3, m);
442
443 let hz1 = ctx.mul(&h, &p1.z);
445 let z3 = ctx.mul(&hz1, &p2.z);
446
447 JacobianPoint {
448 x: x3,
449 y: y3,
450 z: z3,
451 }
452}
453
454fn scalar_mul_jacobian(curve: &CurveParams, point: &AffinePoint, k: &BigUint) -> AffinePoint {
463 if k.is_zero() || point.is_infinity() {
464 return AffinePoint::infinity();
465 }
466
467 let mut result = JacobianPoint::infinity();
468 let p_jac = JacobianPoint::from_affine(point);
469
470 for i in (0..k.bits()).rev() {
472 result = point_double_jacobian(curve, &result);
473 if k.bit(i) {
474 result = point_add_jacobian(curve, &result, &p_jac);
475 }
476 }
477
478 result.to_affine(curve)
479}
480
481fn is_on_curve_binary(
485 x: &BigUint,
486 y: &BigUint,
487 a: &BigUint,
488 b: &BigUint,
489 poly: &BigUint,
490 degree: usize,
491) -> bool {
492 let y2 = gf2m_sq(y, poly, degree);
494 let xy = gf2m_mul(x, y, poly, degree);
495 let lhs = gf2m_add(&y2, &xy);
496 let x2 = gf2m_sq(x, poly, degree);
498 let x3 = gf2m_mul(x, &x2, poly, degree);
499 let ax2 = gf2m_mul(a, &x2, poly, degree);
500 let rhs = gf2m_add(&gf2m_add(&x3, &ax2), b);
501 lhs == rhs
502}
503
504fn add_binary(
514 p: &AffinePoint,
515 q: &AffinePoint,
516 a: &BigUint,
517 poly: &BigUint,
518 degree: usize,
519) -> AffinePoint {
520 if p.is_infinity() {
521 return q.clone();
522 }
523 if q.is_infinity() {
524 return p.clone();
525 }
526
527 let x1 = &p.x;
528 let y1 = &p.y;
529 let x2 = &q.x;
530 let y2 = &q.y;
531
532 let dx = gf2m_add(x1, x2);
533 if dx.is_zero() {
534 let dy = gf2m_add(y1, y2);
536 return if dy.is_zero() {
537 double_binary(p, a, poly, degree)
539 } else {
540 AffinePoint::infinity()
543 };
544 }
545
546 let dy = gf2m_add(y1, y2);
548 let dx_inv = gf2m_inv(&dx, poly, degree).expect("dx is non-zero");
549 let lambda = gf2m_mul(&dy, &dx_inv, poly, degree);
550
551 let lambda_sq = gf2m_sq(&lambda, poly, degree);
553 let mut xr = gf2m_add(&lambda_sq, &lambda);
554 xr.bitxor_assign(x1);
555 xr.bitxor_assign(x2);
556 xr.bitxor_assign(a);
557
558 let x1_xr = gf2m_add(x1, &xr);
560 let lambda_term = gf2m_mul(&lambda, &x1_xr, poly, degree);
561 let mut yr = gf2m_add(&lambda_term, &xr);
562 yr.bitxor_assign(y1);
563
564 AffinePoint::new(xr, yr)
565}
566
567fn double_binary(p: &AffinePoint, a: &BigUint, poly: &BigUint, degree: usize) -> AffinePoint {
578 if p.is_infinity() {
579 return AffinePoint::infinity();
580 }
581 if p.x.is_zero() {
582 return AffinePoint::infinity();
584 }
585
586 let x1 = &p.x;
587 let y1 = &p.y;
588
589 let x1_inv = gf2m_inv(x1, poly, degree).expect("x is non-zero");
591 let y1_over_x1 = gf2m_mul(y1, &x1_inv, poly, degree);
592 let lambda = gf2m_add(x1, &y1_over_x1);
593
594 let lambda_sq = gf2m_sq(&lambda, poly, degree);
596 let mut xr = gf2m_add(&lambda_sq, &lambda);
597 xr.bitxor_assign(a);
598
599 let x1_sq = gf2m_sq(x1, poly, degree);
601 let lambda_plus_1 = gf2m_add(&lambda, &BigUint::one());
602 let lambda_plus_1_xr = gf2m_mul(&lambda_plus_1, &xr, poly, degree);
603 let yr = gf2m_add(&x1_sq, &lambda_plus_1_xr);
604
605 AffinePoint::new(xr, yr)
606}
607
608fn scalar_mul_binary(curve: &CurveParams, point: &AffinePoint, k: &BigUint) -> AffinePoint {
611 if k.is_zero() || point.is_infinity() {
612 return AffinePoint::infinity();
613 }
614
615 let mut result = AffinePoint::infinity();
616 for i in (0..k.bits()).rev() {
617 result = curve.double(&result);
618 if k.bit(i) {
619 result = curve.add(&result, point);
620 }
621 }
622 result
623}
624
625impl CurveParams {
628 #[must_use]
635 pub fn new(
636 field_prime: BigUint,
637 curve_a: BigUint,
638 curve_b: BigUint,
639 subgroup_order: BigUint,
640 cofactor: u64,
641 base_x: BigUint,
642 base_y: BigUint,
643 ) -> Option<Self> {
644 let field = MontgomeryCtx::new(&field_prime)?;
645 let scalar = MontgomeryCtx::new(&subgroup_order)?;
646 let coord_len = field_prime.bits().div_ceil(8);
647 Some(Self {
648 p: field_prime,
649 a: curve_a,
650 b: curve_b,
651 n: subgroup_order,
652 h: cofactor,
653 gx: base_x,
654 gy: base_y,
655 field: FieldCtx::Prime(field),
656 _scalar: scalar,
657 coord_len,
658 })
659 }
660
661 #[must_use]
672 pub fn new_binary(
673 modulus_poly: BigUint,
674 degree: usize,
675 curve_a: BigUint,
676 curve_b: BigUint,
677 subgroup_order: BigUint,
678 cofactor: u64,
679 base_point: (BigUint, BigUint),
680 ) -> Option<Self> {
681 let (base_x, base_y) = base_point;
682 let scalar = MontgomeryCtx::new(&subgroup_order)?;
683 let coord_len = degree.div_ceil(8);
684 let field_prime = modulus_poly.clone();
685 Some(Self {
686 p: field_prime,
687 a: curve_a,
688 b: curve_b,
689 n: subgroup_order,
690 h: cofactor,
691 gx: base_x,
692 gy: base_y,
693 field: FieldCtx::Binary {
694 poly: modulus_poly,
695 degree,
696 },
697 _scalar: scalar,
698 coord_len,
699 })
700 }
701
702 fn prime_ctx(&self) -> &MontgomeryCtx {
710 match &self.field {
711 FieldCtx::Prime(ctx) => ctx,
712 FieldCtx::Binary { .. } => {
713 panic!("prime_ctx called on a binary-field curve")
714 }
715 }
716 }
717
718 #[must_use]
721 pub fn gf2m_degree(&self) -> Option<usize> {
722 match &self.field {
723 FieldCtx::Binary { degree, .. } => Some(*degree),
724 FieldCtx::Prime(_) => None,
725 }
726 }
727
728 #[must_use]
730 pub fn base_point(&self) -> AffinePoint {
731 AffinePoint::new(self.gx.clone(), self.gy.clone())
732 }
733
734 #[must_use]
740 pub fn is_on_curve(&self, point: &AffinePoint) -> bool {
741 if point.infinity {
742 return true;
743 }
744 match &self.field {
745 FieldCtx::Prime(ctx) => {
746 let lhs = ctx.square(&point.y);
748 let x2 = ctx.square(&point.x);
750 let x3 = ctx.mul(&x2, &point.x);
751 let ax = ctx.mul(&self.a, &point.x);
752 let rhs = field_add(&field_add(&x3, &ax, &self.p), &self.b, &self.p);
753 lhs == rhs
754 }
755 FieldCtx::Binary { poly, degree } => {
756 is_on_curve_binary(&point.x, &point.y, &self.a, &self.b, poly, *degree)
757 }
758 }
759 }
760
761 #[must_use]
766 pub fn negate(&self, point: &AffinePoint) -> AffinePoint {
767 if point.infinity {
768 return point.clone();
769 }
770 match &self.field {
771 FieldCtx::Prime(_) => AffinePoint::new(point.x.clone(), field_neg(&point.y, &self.p)),
772 FieldCtx::Binary { .. } => {
773 let neg_y = gf2m_add(&point.x, &point.y);
775 AffinePoint::new(point.x.clone(), neg_y)
776 }
777 }
778 }
779
780 #[must_use]
782 pub fn add(&self, p: &AffinePoint, q: &AffinePoint) -> AffinePoint {
783 match &self.field {
784 FieldCtx::Prime(_) => {
785 let pj = JacobianPoint::from_affine(p);
786 let qj = JacobianPoint::from_affine(q);
787 point_add_jacobian(self, &pj, &qj).to_affine(self)
788 }
789 FieldCtx::Binary { poly, degree } => add_binary(p, q, &self.a, poly, *degree),
790 }
791 }
792
793 #[must_use]
795 pub fn double(&self, p: &AffinePoint) -> AffinePoint {
796 match &self.field {
797 FieldCtx::Prime(_) => {
798 let pj = JacobianPoint::from_affine(p);
799 point_double_jacobian(self, &pj).to_affine(self)
800 }
801 FieldCtx::Binary { poly, degree } => double_binary(p, &self.a, poly, *degree),
802 }
803 }
804
805 #[must_use]
809 pub fn scalar_mul(&self, point: &AffinePoint, k: &BigUint) -> AffinePoint {
810 match &self.field {
811 FieldCtx::Prime(_) => scalar_mul_jacobian(self, point, k),
812 FieldCtx::Binary { .. } => scalar_mul_binary(self, point, k),
813 }
814 }
815
816 #[must_use]
822 pub fn diffie_hellman(
823 &self,
824 private_scalar: &BigUint,
825 public_point: &AffinePoint,
826 ) -> AffinePoint {
827 self.scalar_mul(public_point, private_scalar)
828 }
829
830 pub fn random_scalar<R: Csprng>(&self, rng: &mut R) -> BigUint {
841 random_nonzero_below(rng, &self.n)
844 .expect("curve order n is always > 1 for any valid cryptographic curve")
845 }
846
847 pub fn generate_keypair<R: Csprng>(&self, rng: &mut R) -> (BigUint, AffinePoint) {
856 let d = self.random_scalar(rng);
857 let q = self.scalar_mul(&self.base_point(), &d);
858 (d, q)
859 }
860
861 #[must_use]
866 pub fn scalar_invert(&self, k: &BigUint) -> Option<BigUint> {
867 mod_inverse(k, &self.n)
868 }
869
870 #[must_use]
878 pub fn encode_point(&self, point: &AffinePoint) -> Vec<u8> {
879 if point.infinity {
880 return vec![0x00];
881 }
882 let mut out = Vec::with_capacity(1 + 2 * self.coord_len);
883 out.push(0x04);
884 out.extend_from_slice(&pad_to(point.x.to_be_bytes(), self.coord_len));
885 out.extend_from_slice(&pad_to(point.y.to_be_bytes(), self.coord_len));
886 out
887 }
888
889 #[must_use]
903 pub fn encode_point_compressed(&self, point: &AffinePoint) -> Vec<u8> {
904 if point.infinity {
905 return vec![0x00];
906 }
907 let parity = match &self.field {
908 FieldCtx::Prime(_) => point.y.is_odd(),
909 FieldCtx::Binary { poly, degree } => {
910 if point.x.is_zero() {
911 false
912 } else {
913 let x_inv =
914 gf2m_inv(&point.x, poly, *degree).expect("x is non-zero in binary curve");
915 let z = gf2m_mul(&point.y, &x_inv, poly, *degree);
916 z.is_odd()
917 }
918 }
919 };
920 let tag = if parity { 0x03u8 } else { 0x02u8 };
921 let mut out = Vec::with_capacity(1 + self.coord_len);
922 out.push(tag);
923 out.extend_from_slice(&pad_to(point.x.to_be_bytes(), self.coord_len));
924 out
925 }
926
927 #[must_use]
936 pub fn decode_point(&self, bytes: &[u8]) -> Option<AffinePoint> {
937 if bytes == [0x00] {
938 return Some(AffinePoint::infinity());
939 }
940 match bytes.first()? {
941 0x04 => {
942 let expected_len = 1 + 2 * self.coord_len;
944 if bytes.len() != expected_len {
945 return None;
946 }
947 let coord_bytes = &bytes[1..];
948 let x = BigUint::from_be_bytes(&coord_bytes[..self.coord_len]);
949 let y = BigUint::from_be_bytes(&coord_bytes[self.coord_len..]);
950 let pt = AffinePoint::new(x, y);
951 if self.is_on_curve(&pt) {
952 Some(pt)
953 } else {
954 None
955 }
956 }
957 tag @ (0x02 | 0x03) => {
958 let expected_len = 1 + self.coord_len;
960 if bytes.len() != expected_len {
961 return None;
962 }
963 let x = BigUint::from_be_bytes(&bytes[1..]);
964 let odd_tag = *tag == 0x03;
965 match &self.field {
966 FieldCtx::Prime(_) => {
967 let y = self.field_sqrt_from_x(&x, odd_tag)?;
968 Some(AffinePoint::new(x, y))
969 }
970 FieldCtx::Binary { poly, degree } => {
971 self.decompress_binary_point(&x, odd_tag, poly, *degree)
972 }
973 }
974 }
975 _ => None,
976 }
977 }
978
979 fn decompress_binary_point(
987 &self,
988 x: &BigUint,
989 odd_z: bool,
990 poly: &BigUint,
991 degree: usize,
992 ) -> Option<AffinePoint> {
993 if x.is_zero() {
994 return None;
997 }
998 let x_inv = gf2m_inv(x, poly, degree)?;
1000 let x_inv2 = gf2m_sq(&x_inv, poly, degree);
1001 let b_x_inv2 = gf2m_mul(&self.b, &x_inv2, poly, degree);
1002 let beta = gf2m_add(&gf2m_add(x, &self.a), &b_x_inv2);
1003
1004 let z0 = gf2m_half_trace(&beta, poly, degree);
1006 let z = if z0.is_odd() == odd_z {
1008 z0
1009 } else {
1010 gf2m_add(&z0, &BigUint::one())
1011 };
1012
1013 let y = gf2m_mul(&z, x, poly, degree);
1014 let pt = AffinePoint::new(x.clone(), y);
1015 if self.is_on_curve(&pt) {
1016 Some(pt)
1017 } else {
1018 None
1019 }
1020 }
1021
1022 fn field_sqrt_from_x(&self, x: &BigUint, odd_y: bool) -> Option<BigUint> {
1032 let ctx = self.prime_ctx();
1033
1034 let x2 = ctx.square(x);
1036 let x3 = ctx.mul(&x2, x);
1037 let ax = ctx.mul(&self.a, x);
1038 let rhs = field_add(&field_add(&x3, &ax, &self.p), &self.b, &self.p);
1039
1040 if self.p.rem_u64(4) != 3 {
1042 return None;
1043 }
1044 let exp = {
1045 let p_plus_1 = self.p.add_ref(&BigUint::one());
1046 let (q, _) = p_plus_1.div_rem(&BigUint::from_u64(4));
1049 q
1050 };
1051
1052 let y_candidate = ctx.pow(&rhs, &exp);
1053
1054 if ctx.square(&y_candidate) != rhs {
1057 return None;
1058 }
1059
1060 let y = if y_candidate.is_odd() == odd_y {
1062 y_candidate
1063 } else {
1064 field_neg(&y_candidate, &self.p)
1065 };
1066 Some(y)
1067 }
1068}
1069
1070fn from_hex(hex: &str) -> BigUint {
1077 let cleaned: String = hex.chars().filter(|c| !c.is_ascii_whitespace()).collect();
1080 assert!(
1081 cleaned.len().is_multiple_of(2),
1082 "hex string must have even length: {cleaned}"
1083 );
1084 let bytes: Vec<u8> = (0..cleaned.len())
1085 .step_by(2)
1086 .map(|i| u8::from_str_radix(&cleaned[i..i + 2], 16).expect("valid hex digit"))
1087 .collect();
1088 BigUint::from_be_bytes(&bytes)
1089}
1090
1091#[must_use]
1104pub fn p256() -> CurveParams {
1105 let p = from_hex(
1107 "FFFFFFFF 00000001 00000000 00000000 \
1108 00000000 FFFFFFFF FFFFFFFF FFFFFFFF",
1109 );
1110 let a = from_hex(
1112 "FFFFFFFF 00000001 00000000 00000000 \
1113 00000000 FFFFFFFF FFFFFFFF FFFFFFFC",
1114 );
1115 let b = from_hex(
1116 "5AC635D8 AA3A93E7 B3EBBD55 769886BC \
1117 651D06B0 CC53B0F6 3BCE3C3E 27D2604B",
1118 );
1119 let n = from_hex(
1121 "FFFFFFFF 00000000 FFFFFFFF FFFFFFFF \
1122 BCE6FAAD A7179E84 F3B9CAC2 FC632551",
1123 );
1124 let gx = from_hex(
1125 "6B17D1F2 E12C4247 F8BCE6E5 63A440F2 \
1126 77037D81 2DEB33A0 F4A13945 D898C296",
1127 );
1128 let gy = from_hex(
1129 "4FE342E2 FE1A7F9B 8EE7EB4A 7C0F9E16 \
1130 2BCE3357 6B315ECE CBB64068 37BF51F5",
1131 );
1132 CurveParams::new(p, a, b, n, 1, gx, gy).expect("P-256 parameters are well-formed")
1133}
1134
1135#[must_use]
1148pub fn p384() -> CurveParams {
1149 let p = from_hex(
1151 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1152 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE \
1153 FFFFFFFF 00000000 00000000 FFFFFFFF",
1154 );
1155 let a = from_hex(
1157 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1158 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE \
1159 FFFFFFFF 00000000 00000000 FFFFFFFC",
1160 );
1161 let b = from_hex(
1162 "B3312FA7 E23EE7E4 988E056B E3F82D19 \
1163 181D9C6E FE814112 0314088F 5013875A \
1164 C656398D 8A2ED19D 2A85C8ED D3EC2AEF",
1165 );
1166 let n = from_hex(
1167 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1168 FFFFFFFF FFFFFFFF C7634D81 F4372DDF \
1169 581A0DB2 48B0A77A ECEC196A CCC52973",
1170 );
1171 let gx = from_hex(
1172 "AA87CA22 BE8B0537 8EB1C71E F320AD74 \
1173 6E1D3B62 8BA79B98 59F741E0 82542A38 \
1174 5502F25D BF55296C 3A545E38 72760AB7",
1175 );
1176 let gy = from_hex(
1177 "3617DE4A 96262C6F 5D9E98BF 9292DC29 \
1178 F8F41DBD 289A147C E9DA3113 B5F0B8C0 \
1179 0A60B1CE 1D7E819D 7A431D7C 90EA0E5F",
1180 );
1181 CurveParams::new(p, a, b, n, 1, gx, gy).expect("P-384 parameters are well-formed")
1182}
1183
1184#[must_use]
1198pub fn secp256k1() -> CurveParams {
1199 let p = from_hex(
1201 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1202 FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F",
1203 );
1204 let a = BigUint::zero();
1207 let b = BigUint::from_u64(7);
1209 let n = from_hex(
1210 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE \
1211 BAAEDCE6 AF48A03B BFD25E8C D0364141",
1212 );
1213 let gx = from_hex(
1214 "79BE667E F9DCBBAC 55A06295 CE870B07 \
1215 029BFCDB 2DCE28D9 59F2815B 16F81798",
1216 );
1217 let gy = from_hex(
1218 "483ADA77 26A3C465 5DA4FBFC 0E1108A8 \
1219 FD17B448 A6855419 9C47D08F FB10D4B8",
1220 );
1221 CurveParams::new(p, a, b, n, 1, gx, gy).expect("secp256k1 parameters are well-formed")
1222}
1223
1224#[must_use]
1240pub fn p192() -> CurveParams {
1241 let p = from_hex("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFF");
1243 let a = from_hex("FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFFFF FFFFFFFC");
1245 let b = from_hex("64210519 E59C80E7 0FA7E9AB 72243049 FEB8DEEC C146B9B1");
1246 let n = from_hex("FFFFFFFF FFFFFFFF FFFFFFFF 99DEF836 146BC9B1 B4D22831");
1247 let gx = from_hex("188DA80E B03090F6 7CBF20EB 43A18800 F4FF0AFD 82FF1012");
1248 let gy = from_hex("07192B95 FFC8DA78 631011ED 6B24CDD5 73F977A1 1E794811");
1249 CurveParams::new(p, a, b, n, 1, gx, gy).expect("P-192 parameters are well-formed")
1250}
1251
1252#[must_use]
1269pub fn p224() -> CurveParams {
1270 let p = from_hex(
1272 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1273 00000000 00000000 00000001",
1274 );
1275 let a = from_hex(
1277 "FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE \
1278 FFFFFFFF FFFFFFFF FFFFFFFE",
1279 );
1280 let b = from_hex(
1281 "B4050A85 0C04B3AB F5413256 5044B0B7 \
1282 D7BFD8BA 270B3943 2355FFB4",
1283 );
1284 let n = from_hex(
1285 "FFFFFFFF FFFFFFFF FFFFFFFF FFFF16A2 \
1286 E0B8F03E 13DD2945 5C5C2A3D",
1287 );
1288 let gx = from_hex(
1289 "B70E0CBD 6BB4BF7F 321390B9 4A03C1D3 \
1290 56C21122 343280D6 115C1D21",
1291 );
1292 let gy = from_hex(
1293 "BD376388 B5F723FB 4C22DFE6 CD4375A0 \
1294 5A074764 44D58199 85007E34",
1295 );
1296 CurveParams::new(p, a, b, n, 1, gx, gy).expect("P-224 parameters are well-formed")
1297}
1298
1299#[must_use]
1318pub fn p521() -> CurveParams {
1319 let p = from_hex(
1321 "01FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1322 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1323 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1324 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF",
1325 );
1326 let a = from_hex(
1328 "01FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1329 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1330 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1331 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFC",
1332 );
1333 let b = from_hex(
1334 "0051 953EB961 8E1C9A1F 929A21A0 B68540EE \
1335 A2DA725B 99B315F3 B8B48991 8EF109E1 \
1336 56193951 EC7E937B 1652C0BD 3BB1BF07 \
1337 3573DF88 3D2C34F1 EF451FD4 6B503F00",
1338 );
1339 let n = from_hex(
1340 "01FF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF \
1341 FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFA \
1342 51868783 BF2F966B 7FCC0148 F709A5D0 \
1343 3BB5C9B8 899C47AE BB6FB71E 91386409",
1344 );
1345 let gx = from_hex(
1346 "00C6 858E06B7 0404E9CD 9E3ECB66 2395B442 \
1347 9C648139 053FB521 F828AF60 6B4D3DBA \
1348 A14B5E77 EFE75928 FE1DC127 A2FFA8DE \
1349 3348B3C1 856A429B F97E7E31 C2E5BD66",
1350 );
1351 let gy = from_hex(
1352 "0118 39296A78 9A3BC004 5C8A5FB4 2C7D1BD9 \
1353 98F54449 579B4468 17AFBD17 273E662C \
1354 97EE7299 5EF42640 C550B901 3FAD0761 \
1355 353C7086 A272C240 88BE9476 9FD16650",
1356 );
1357 CurveParams::new(p, a, b, n, 1, gx, gy).expect("P-521 parameters are well-formed")
1358}
1359
1360#[must_use]
1381pub fn b163() -> CurveParams {
1382 let poly = from_hex("0800000000000000000000000000000000000000C9");
1383 let a = BigUint::one();
1384 let b = from_hex("020A601907B8C953CA1481EB10512F78744A3205FD");
1385 let n = from_hex("040000000000000000000292FE77E70C12A4234C33");
1386 let gx = from_hex("03F0EBA16286A2D57EA0991168D4994637E8343E36");
1387 let gy = from_hex("00D51FBC6C71A0094FA2CDD545B11C5C0C797324F1");
1388 CurveParams::new_binary(poly, 163, a, b, n, 2, (gx, gy))
1389 .expect("B-163 parameters are well-formed")
1390}
1391
1392#[must_use]
1401pub fn k163() -> CurveParams {
1402 let poly = from_hex("0800000000000000000000000000000000000000C9");
1403 let a = BigUint::one();
1404 let b = BigUint::one();
1405 let n = from_hex("04000000000000000000020108A2E0CC0D99F8A5EF");
1406 let gx = from_hex("02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8");
1407 let gy = from_hex("0289070FB05D38FF58321F2E800536D538CCDAA3D9");
1408 CurveParams::new_binary(poly, 163, a, b, n, 2, (gx, gy))
1409 .expect("K-163 parameters are well-formed")
1410}
1411
1412#[must_use]
1421pub fn b233() -> CurveParams {
1422 let mut poly = BigUint::zero();
1423 for bit in [233, 74, 0] {
1424 poly.set_bit(bit);
1425 }
1426 let a = BigUint::one();
1427 let b = from_hex("0066647EDE6C332C7F8C0923BB58213B333B20E9CE4281FE115F7D8F90AD");
1428 let n = from_hex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
1429 let gx = from_hex("00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B");
1430 let gy = from_hex("01006A08A41903350678E58528BEBF8A0BEFF867A7CA36716F7E01F81052");
1431 CurveParams::new_binary(poly, 233, a, b, n, 2, (gx, gy))
1432 .expect("B-233 parameters are well-formed")
1433}
1434
1435#[must_use]
1444pub fn k233() -> CurveParams {
1445 let mut poly = BigUint::zero();
1446 for bit in [233, 74, 0] {
1447 poly.set_bit(bit);
1448 }
1449 let a = BigUint::zero();
1450 let b = BigUint::one();
1451 let n = from_hex("008000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
1452 let gx = from_hex("017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126");
1453 let gy = from_hex("01DB537DECE819B7F70F555A67C427A8CD9BF18AEB9B56E0C11056FAE6A3");
1454 CurveParams::new_binary(poly, 233, a, b, n, 4, (gx, gy))
1455 .expect("K-233 parameters are well-formed")
1456}
1457
1458#[must_use]
1467pub fn b283() -> CurveParams {
1468 let mut poly = BigUint::zero();
1469 for bit in [283, 12, 7, 5, 0] {
1470 poly.set_bit(bit);
1471 }
1472 let a = BigUint::one();
1473 let b = from_hex("027B680AC8B8596DA5A4AF8A19A0303FCA97FD7645309FA2A581485AF6263E313B79A2F5");
1474 let n = from_hex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
1475 let gx = from_hex("05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053");
1476 let gy = from_hex("03676854FE24141CB98FE6D4B20D02B4516FF702350EDDB0826779C813F0DF45BE8112F4");
1477 CurveParams::new_binary(poly, 283, a, b, n, 2, (gx, gy))
1478 .expect("B-283 parameters are well-formed")
1479}
1480
1481#[must_use]
1490pub fn k283() -> CurveParams {
1491 let mut poly = BigUint::zero();
1492 for bit in [283, 12, 7, 5, 0] {
1493 poly.set_bit(bit);
1494 }
1495 let a = BigUint::zero();
1496 let b = BigUint::one();
1497 let n = from_hex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
1498 let gx = from_hex("0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836");
1499 let gy = from_hex("01CCDA380F1C9E318D90F95D07E5426FE87E45C0E8184698E45962364E34116177DD2259");
1500 CurveParams::new_binary(poly, 283, a, b, n, 4, (gx, gy))
1501 .expect("K-283 parameters are well-formed")
1502}
1503
1504#[must_use]
1513pub fn b409() -> CurveParams {
1514 let mut poly = BigUint::zero();
1515 for bit in [409, 87, 0] {
1516 poly.set_bit(bit);
1517 }
1518 let a = BigUint::one();
1519 let b = from_hex(
1520 "0021A5C2C8EE9FEB5C4B9A753B7B476B7FD6422EF1F3DD674761FA99D6AC27C8A9A197B272822F6CD57A55AA4F50AE317B13545F",
1521 );
1522 let n = from_hex(
1523 "010000000000000000000000000000000000000000000000012F7B6E4B64E2C26F2B04E76B1B9D77B6CCBB99EE3A7BCED5CB4ECB",
1524 );
1525 let gx = from_hex(
1526 "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7",
1527 );
1528 let gy = from_hex(
1529 "0061B1CFAB6BE5F32BBFA78324ED106A7636B9C5A7BD198D0158AA4F5488D08F38514F1FDF4B4F40D2181B3681C364BA0273C706",
1530 );
1531 CurveParams::new_binary(poly, 409, a, b, n, 2, (gx, gy))
1532 .expect("B-409 parameters are well-formed")
1533}
1534
1535#[must_use]
1544pub fn k409() -> CurveParams {
1545 let mut poly = BigUint::zero();
1546 for bit in [409, 87, 0] {
1547 poly.set_bit(bit);
1548 }
1549 let a = BigUint::zero();
1550 let b = BigUint::one();
1551 let n = from_hex(
1552 "007FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF",
1553 );
1554 let gx = from_hex(
1555 "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746",
1556 );
1557 let gy = from_hex(
1558 "01E369050B7C4E42ACBA1DACBF04299C3460782F918EA427E6325165E9EA10E3DA5F6C42E9C55215AA9CA27A5863EC48D8E0286B",
1559 );
1560 CurveParams::new_binary(poly, 409, a, b, n, 4, (gx, gy))
1561 .expect("K-409 parameters are well-formed")
1562}
1563
1564#[must_use]
1573pub fn b571() -> CurveParams {
1574 let mut poly = BigUint::zero();
1575 for bit in [571, 10, 5, 2, 0] {
1576 poly.set_bit(bit);
1577 }
1578 let a = BigUint::one();
1579 let b = from_hex(
1580 "02F40E7E2221F295DE297117B7F3D62F5C6A97FFCB8CEFF1CD6BA8CE4A9A18AD84FFABBD8EFA59332BE7AD6756A66E294AFD185A78FF12AA520E4DE739BACA0C7FFEFF7F2955727A",
1581 );
1582 let n = from_hex(
1583 "03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47",
1584 );
1585 let gx = from_hex(
1586 "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19",
1587 );
1588 let gy = from_hex(
1589 "037BF27342DA639B6DCCFFFEB73D69D78C6C27A6009CBBCA1980F8533921E8A684423E43BAB08A576291AF8F461BB2A8B3531D2F0485C19B16E2F1516E23DD3C1A4827AF1B8AC15B",
1590 );
1591 CurveParams::new_binary(poly, 571, a, b, n, 2, (gx, gy))
1592 .expect("B-571 parameters are well-formed")
1593}
1594
1595#[must_use]
1604pub fn k571() -> CurveParams {
1605 let mut poly = BigUint::zero();
1606 for bit in [571, 10, 5, 2, 0] {
1607 poly.set_bit(bit);
1608 }
1609 let a = BigUint::zero();
1610 let b = BigUint::one();
1611 let n = from_hex(
1612 "020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001",
1613 );
1614 let gx = from_hex(
1615 "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972",
1616 );
1617 let gy = from_hex(
1618 "0349DC807F4FBF374F4AEADE3BCA95314DD58CEC9F307A54FFC61EFC006D8A2C9D4979C0AC44AEA74FBEBBB9F772AEDCB620B01A7BA7AF1B320430C8591984F601CD4C143EF1C7A3",
1619 );
1620 CurveParams::new_binary(poly, 571, a, b, n, 4, (gx, gy))
1621 .expect("K-571 parameters are well-formed")
1622}
1623
1624#[cfg(test)]
1627mod tests {
1628 use super::*;
1629
1630 #[test]
1633 fn p256_base_point_on_curve() {
1634 let curve = p256();
1635 let g = curve.base_point();
1636 assert!(
1637 curve.is_on_curve(&g),
1638 "P-256 base point G must satisfy y² = x³ + ax + b"
1639 );
1640 }
1641
1642 #[test]
1643 fn p256_double_equals_add_self() {
1644 let curve = p256();
1646 let g = curve.base_point();
1647 let via_double = curve.double(&g);
1648 let via_add = curve.add(&g, &g);
1649 assert_eq!(
1650 via_double, via_add,
1651 "2G via double must equal G+G via add for P-256"
1652 );
1653 assert!(curve.is_on_curve(&via_double), "2G must lie on P-256");
1654 }
1655
1656 #[test]
1657 fn p256_scalar_mul_matches_repeated_add() {
1658 let curve = p256();
1660 let g = curve.base_point();
1661 let four_g_scalar = curve.scalar_mul(&g, &BigUint::from_u64(4));
1662 let two_g = curve.double(&g);
1663 let four_g_add = curve.add(&two_g, &two_g);
1664 assert_eq!(
1665 four_g_scalar, four_g_add,
1666 "4G via scalar_mul must equal 2G+2G"
1667 );
1668 }
1669
1670 #[test]
1671 fn p256_order_times_base_point_is_infinity() {
1672 let curve = p256();
1674 let g = curve.base_point();
1675 let n = curve.n.clone();
1676 let result = curve.scalar_mul(&g, &n);
1677 assert!(
1678 result.is_infinity(),
1679 "n·G must be the point at infinity for P-256"
1680 );
1681 }
1682
1683 #[test]
1684 fn p256_negation_sums_to_infinity() {
1685 let curve = p256();
1687 let g = curve.base_point();
1688 let neg_g = curve.negate(&g);
1689 let sum = curve.add(&g, &neg_g);
1690 assert!(sum.is_infinity(), "G + (−G) must be the point at infinity");
1691 }
1692
1693 #[test]
1694 fn p256_encode_decode_uncompressed_roundtrip() {
1695 let curve = p256();
1696 let g = curve.base_point();
1697 let encoded = curve.encode_point(&g);
1698 let decoded = curve
1699 .decode_point(&encoded)
1700 .expect("decode must succeed for a valid point");
1701 assert_eq!(
1702 decoded, g,
1703 "uncompressed encode/decode must be the identity"
1704 );
1705 }
1706
1707 #[test]
1708 fn p256_encode_decode_compressed_roundtrip() {
1709 let curve = p256();
1710 let g = curve.base_point();
1711 let encoded = curve.encode_point_compressed(&g);
1712 let decoded = curve
1713 .decode_point(&encoded)
1714 .expect("compressed decode must succeed");
1715 assert_eq!(decoded, g, "compressed encode/decode must be the identity");
1716 }
1717
1718 #[test]
1719 fn p256_infinity_encodes_as_single_zero_byte() {
1720 let curve = p256();
1721 let inf = AffinePoint::infinity();
1722 let enc = curve.encode_point(&inf);
1723 assert_eq!(enc, vec![0x00]);
1724 let dec = curve.decode_point(&enc).expect("decode of infinity");
1725 assert!(dec.is_infinity());
1726 }
1727
1728 #[test]
1729 fn p256_decode_rejects_bad_length() {
1730 let curve = p256();
1731 let g = curve.base_point();
1732 let mut enc = curve.encode_point(&g);
1733 enc.pop(); assert!(
1735 curve.decode_point(&enc).is_none(),
1736 "truncated encoding must be rejected"
1737 );
1738 }
1739
1740 #[test]
1741 fn p256_decode_rejects_off_curve_point() {
1742 let curve = p256();
1743 let g = curve.base_point();
1745 let mut enc = curve.encode_point(&g);
1746 let last = enc.last_mut().unwrap();
1747 *last ^= 0xff; assert!(
1749 curve.decode_point(&enc).is_none(),
1750 "off-curve point must be rejected"
1751 );
1752 }
1753
1754 #[test]
1757 fn p384_base_point_on_curve() {
1758 let curve = p384();
1759 assert!(curve.is_on_curve(&curve.base_point()));
1760 }
1761
1762 #[test]
1763 fn p384_double_equals_add_self() {
1764 let curve = p384();
1765 let g = curve.base_point();
1766 assert_eq!(curve.double(&g), curve.add(&g, &g));
1767 }
1768
1769 #[test]
1770 fn p384_order_times_base_point_is_infinity() {
1771 let curve = p384();
1772 let n = curve.n.clone();
1773 let result = curve.scalar_mul(&curve.base_point(), &n);
1774 assert!(result.is_infinity());
1775 }
1776
1777 #[test]
1780 fn secp256k1_base_point_on_curve() {
1781 let curve = secp256k1();
1782 assert!(curve.is_on_curve(&curve.base_point()));
1783 }
1784
1785 #[test]
1786 fn secp256k1_double_equals_add_self() {
1787 let curve = secp256k1();
1788 let g = curve.base_point();
1789 assert_eq!(curve.double(&g), curve.add(&g, &g));
1790 }
1791
1792 #[test]
1793 fn secp256k1_order_times_base_point_is_infinity() {
1794 let curve = secp256k1();
1795 let n = curve.n.clone();
1796 let result = curve.scalar_mul(&curve.base_point(), &n);
1797 assert!(result.is_infinity());
1798 }
1799
1800 #[test]
1801 fn secp256k1_encode_decode_compressed_roundtrip() {
1802 let curve = secp256k1();
1803 let g = curve.base_point();
1804 let enc = curve.encode_point_compressed(&g);
1805 let dec = curve.decode_point(&enc).expect("decode must succeed");
1806 assert_eq!(dec, g);
1807 }
1808
1809 #[test]
1812 fn p256_ecdh_shared_secret_agrees() {
1813 use crate::CtrDrbgAes256;
1814
1815 let curve = p256();
1816 let mut rng = CtrDrbgAes256::new(&[0xab; 48]);
1817
1818 let (d_a, q_a) = curve.generate_keypair(&mut rng);
1819 let (d_b, q_b) = curve.generate_keypair(&mut rng);
1820
1821 let shared_a = curve.diffie_hellman(&d_a, &q_b);
1823 let shared_b = curve.diffie_hellman(&d_b, &q_a);
1824 assert_eq!(shared_a, shared_b, "ECDH shared points must agree");
1825 assert!(
1826 !shared_a.is_infinity(),
1827 "ECDH shared point must not be infinity"
1828 );
1829 assert!(
1830 curve.is_on_curve(&shared_a),
1831 "ECDH shared point must lie on the curve"
1832 );
1833 }
1834
1835 #[test]
1840 fn p192_base_point_on_curve() {
1841 let curve = p192();
1842 assert!(curve.is_on_curve(&curve.base_point()));
1843 }
1844
1845 #[test]
1846 fn p192_double_equals_add_self() {
1847 let curve = p192();
1848 let g = curve.base_point();
1849 assert_eq!(curve.double(&g), curve.add(&g, &g));
1850 }
1851
1852 #[test]
1853 fn p192_encode_decode_uncompressed_roundtrip() {
1854 let curve = p192();
1855 let g = curve.base_point();
1856 let enc = curve.encode_point(&g);
1857 let dec = curve.decode_point(&enc).expect("P-192 uncompressed decode");
1858 assert_eq!(dec, g);
1859 }
1860
1861 #[test]
1862 fn p192_encode_decode_compressed_roundtrip() {
1863 let curve = p192();
1864 let g = curve.base_point();
1865 let enc = curve.encode_point_compressed(&g);
1866 let dec = curve.decode_point(&enc).expect("P-192 compressed decode");
1867 assert_eq!(dec, g);
1868 }
1869
1870 #[test]
1873 fn p224_base_point_on_curve() {
1874 let curve = p224();
1875 assert!(curve.is_on_curve(&curve.base_point()));
1876 }
1877
1878 #[test]
1879 fn p224_double_equals_add_self() {
1880 let curve = p224();
1881 let g = curve.base_point();
1882 assert_eq!(curve.double(&g), curve.add(&g, &g));
1883 }
1884
1885 #[test]
1886 fn p224_uncompressed_roundtrip() {
1887 let curve = p224();
1889 let g = curve.base_point();
1890 let enc = curve.encode_point(&g);
1891 let dec = curve.decode_point(&enc).expect("P-224 uncompressed decode");
1892 assert_eq!(dec, g);
1893 }
1894
1895 #[test]
1896 fn p224_compressed_decode_returns_none() {
1897 let curve = p224();
1899 let enc = curve.encode_point_compressed(&curve.base_point());
1900 assert!(
1901 curve.decode_point(&enc).is_none(),
1902 "P-224 compressed decoding must return None (p ≡ 1 mod 4)"
1903 );
1904 }
1905
1906 #[test]
1909 fn p521_base_point_on_curve() {
1910 let curve = p521();
1911 assert!(curve.is_on_curve(&curve.base_point()));
1912 }
1913
1914 #[test]
1915 fn p521_double_equals_add_self() {
1916 let curve = p521();
1917 let g = curve.base_point();
1918 assert_eq!(curve.double(&g), curve.add(&g, &g));
1919 }
1920
1921 #[test]
1922 fn p521_encode_decode_compressed_roundtrip() {
1923 let curve = p521();
1924 let g = curve.base_point();
1925 let enc = curve.encode_point_compressed(&g);
1926 let dec = curve.decode_point(&enc).expect("P-521 compressed decode");
1927 assert_eq!(dec, g);
1928 }
1929
1930 #[test]
1933 fn p256_scalar_invert_roundtrip() {
1934 let curve = p256();
1936 let k = BigUint::from_u64(0x1234_5678_9abc_def0);
1937 let k_inv = curve
1938 .scalar_invert(&k)
1939 .expect("k is non-zero and coprime with n");
1940 let product = BigUint::mod_mul(&k, &k_inv, &curve.n);
1942 assert_eq!(product, BigUint::one(), "k * k⁻¹ must equal 1 mod n");
1943 }
1944
1945 macro_rules! binary_base_point_on_curve {
1948 ($name:ident, $constructor:ident) => {
1949 #[test]
1950 fn $name() {
1951 let curve = $constructor();
1952 let g = curve.base_point();
1953 assert!(
1954 curve.is_on_curve(&g),
1955 "{} base point must satisfy y² + xy = x³ + ax² + b",
1956 stringify!($constructor)
1957 );
1958 }
1959 };
1960 }
1961
1962 binary_base_point_on_curve!(b163_base_point_on_curve, b163);
1963 binary_base_point_on_curve!(k163_base_point_on_curve, k163);
1964 binary_base_point_on_curve!(b233_base_point_on_curve, b233);
1965 binary_base_point_on_curve!(k233_base_point_on_curve, k233);
1966 binary_base_point_on_curve!(b283_base_point_on_curve, b283);
1967 binary_base_point_on_curve!(k283_base_point_on_curve, k283);
1968 binary_base_point_on_curve!(b409_base_point_on_curve, b409);
1969 binary_base_point_on_curve!(k409_base_point_on_curve, k409);
1970 binary_base_point_on_curve!(b571_base_point_on_curve, b571);
1971 binary_base_point_on_curve!(k571_base_point_on_curve, k571);
1972
1973 macro_rules! binary_double_add_consistency {
1976 ($name:ident, $constructor:ident) => {
1977 #[test]
1978 fn $name() {
1979 let curve = $constructor();
1980 let g = curve.base_point();
1981 let via_double = curve.double(&g);
1982 let via_add = curve.add(&g, &g);
1983 assert_eq!(
1984 via_double,
1985 via_add,
1986 "2G via double must equal G+G via add for {}",
1987 stringify!($constructor)
1988 );
1989 assert!(
1990 curve.is_on_curve(&via_double),
1991 "2G must lie on {}",
1992 stringify!($constructor)
1993 );
1994 }
1995 };
1996 }
1997
1998 binary_double_add_consistency!(b163_double_add_consistency, b163);
1999 binary_double_add_consistency!(k163_double_add_consistency, k163);
2000 binary_double_add_consistency!(b233_double_add_consistency, b233);
2001 binary_double_add_consistency!(k233_double_add_consistency, k233);
2002 binary_double_add_consistency!(b283_double_add_consistency, b283);
2003 binary_double_add_consistency!(k283_double_add_consistency, k283);
2004 #[test]
2009 fn b163_negation_sums_to_infinity() {
2010 let curve = b163();
2011 let g = curve.base_point();
2012 let neg_g = curve.negate(&g);
2013 let sum = curve.add(&g, &neg_g);
2014 assert!(sum.is_infinity(), "G + (-G) must be infinity on B-163");
2015 }
2016
2017 #[test]
2018 fn k163_negation_sums_to_infinity() {
2019 let curve = k163();
2020 let g = curve.base_point();
2021 let neg_g = curve.negate(&g);
2022 let sum = curve.add(&g, &neg_g);
2023 assert!(sum.is_infinity(), "G + (-G) must be infinity on K-163");
2024 }
2025
2026 macro_rules! binary_order_test {
2029 ($name:ident, $constructor:ident) => {
2030 #[test]
2031 fn $name() {
2032 let curve = $constructor();
2033 let g = curve.base_point();
2034 let n = curve.n.clone();
2035 let result = curve.scalar_mul(&g, &n);
2036 assert!(
2037 result.is_infinity(),
2038 "n·G must be the point at infinity for {}",
2039 stringify!($constructor)
2040 );
2041 }
2042 };
2043 }
2044
2045 binary_order_test!(b163_order_times_base_is_infinity, b163);
2046 binary_order_test!(k163_order_times_base_is_infinity, k163);
2047 binary_order_test!(b233_order_times_base_is_infinity, b233);
2048 binary_order_test!(k233_order_times_base_is_infinity, k233);
2049
2050 #[test]
2053 fn b163_encode_decode_uncompressed() {
2054 let curve = b163();
2055 let g = curve.base_point();
2056 let enc = curve.encode_point(&g);
2057 let dec = curve.decode_point(&enc).expect("B-163 uncompressed decode");
2058 assert_eq!(dec, g);
2059 }
2060
2061 #[test]
2062 fn k163_encode_decode_uncompressed() {
2063 let curve = k163();
2064 let g = curve.base_point();
2065 let enc = curve.encode_point(&g);
2066 let dec = curve.decode_point(&enc).expect("K-163 uncompressed decode");
2067 assert_eq!(dec, g);
2068 }
2069
2070 #[test]
2071 fn b163_encode_decode_compressed() {
2072 let curve = b163();
2073 let g = curve.base_point();
2074 let enc = curve.encode_point_compressed(&g);
2075 let dec = curve.decode_point(&enc).expect("B-163 compressed decode");
2076 assert_eq!(dec, g);
2077 }
2078
2079 #[test]
2080 fn k163_encode_decode_compressed() {
2081 let curve = k163();
2082 let g = curve.base_point();
2083 let enc = curve.encode_point_compressed(&g);
2084 let dec = curve.decode_point(&enc).expect("K-163 compressed decode");
2085 assert_eq!(dec, g);
2086 }
2087
2088 #[test]
2091 fn b163_ecdh_shared_secret_agrees() {
2092 use crate::CtrDrbgAes256;
2093 let curve = b163();
2094 let mut rng = CtrDrbgAes256::new(&[0x42; 48]);
2095 let (da, qa) = curve.generate_keypair(&mut rng);
2096 let (db, qb) = curve.generate_keypair(&mut rng);
2097 let shared_a = curve.diffie_hellman(&da, &qb);
2098 let shared_b = curve.diffie_hellman(&db, &qa);
2099 assert_eq!(shared_a, shared_b, "B-163 ECDH shared points must agree");
2100 assert!(!shared_a.is_infinity());
2101 assert!(curve.is_on_curve(&shared_a));
2102 }
2103
2104 #[test]
2105 fn k283_ecdh_shared_secret_agrees() {
2106 use crate::CtrDrbgAes256;
2107 let curve = k283();
2108 let mut rng = CtrDrbgAes256::new(&[0x7F; 48]);
2109 let (da, qa) = curve.generate_keypair(&mut rng);
2110 let (db, qb) = curve.generate_keypair(&mut rng);
2111 let shared_a = curve.diffie_hellman(&da, &qb);
2112 let shared_b = curve.diffie_hellman(&db, &qa);
2113 assert_eq!(shared_a, shared_b, "K-283 ECDH shared points must agree");
2114 assert!(!shared_a.is_infinity());
2115 }
2116}