1#![doc = include_str!("../README.md")]
2
3#![no_std]
4#![deny(rustdoc::broken_intra_doc_links)]
6#![deny(missing_debug_implementations)]
7#![deny(missing_docs)]
8#![deny(unsafe_code)]
9#![allow(clippy::suspicious_arithmetic_impl)]
14
15#[cfg(feature = "alloc")]
16extern crate alloc;
17
18#[cfg(test)]
19#[macro_use]
20extern crate std;
21
22use bitvec::{order::Lsb0, view::AsBits};
23use core::borrow::Borrow;
24use core::fmt;
25use core::iter::Sum;
26use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
27use ff::{BatchInverter, Field};
28use group::{
29 cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
30 prime::PrimeGroup,
31 Curve, Group, GroupEncoding,
32};
33use lazy_static::lazy_static;
34use rand_core::RngCore;
35use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
36
37#[cfg(feature = "alloc")]
38use alloc::vec::Vec;
39
40#[cfg(feature = "alloc")]
41use group::WnafGroup;
42
43#[macro_use]
44mod util;
45
46mod fr;
47pub use blstrs::Scalar as Fq;
48pub use fr::Fr;
49
50pub type Base = Fq;
53
54pub type Scalar = Fr;
57
58const FR_MODULUS_BYTES: [u8; 32] = [
59 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59,
60 103, 6, 169, 175, 51, 101, 234, 180, 125, 14,
61];
62
63#[cfg(feature = "stats")]
64use core::sync::atomic::AtomicUsize;
65#[cfg(feature = "stats")]
66use core::sync::atomic::Ordering::Relaxed;
67
68#[cfg(feature = "stats")]
69static AFFINE_MULS: AtomicUsize = AtomicUsize::new(0);
70#[cfg(feature = "stats")]
71static EXTENDED_MULS: AtomicUsize = AtomicUsize::new(0);
72#[cfg(all(feature = "stats", feature = "multiply-many"))]
73static EXTENDED_MUL_MANY_CALLS: AtomicUsize = AtomicUsize::new(0);
74#[cfg(all(feature = "stats", feature = "multiply-many"))]
75static EXTENDED_MUL_MANY_OPERANDS: AtomicUsize = AtomicUsize::new(0);
76
77#[cfg(feature = "stats")]
79#[derive(Copy, Clone, PartialEq, Eq, Debug)]
80pub struct Stats {
81 pub affine_muls: usize,
83 pub extended_muls: usize,
85 #[cfg(feature = "multiply-many")]
88 pub extended_mul_many_calls: usize,
89 #[cfg(feature = "multiply-many")]
92 pub extended_mul_many_operands: usize,
93}
94
95#[cfg(feature = "stats")]
97pub fn stats() -> Stats {
98 Stats {
99 affine_muls: AFFINE_MULS.load(Relaxed),
100 extended_muls: EXTENDED_MULS.load(Relaxed),
101 #[cfg(feature = "multiply-many")]
102 extended_mul_many_calls: EXTENDED_MUL_MANY_CALLS.load(Relaxed),
103 #[cfg(feature = "multiply-many")]
104 extended_mul_many_operands: EXTENDED_MUL_MANY_OPERANDS.load(Relaxed),
105 }
106}
107
108#[derive(Clone, Copy, Debug, Eq)]
111pub struct AffinePoint {
112 u: Fq,
113 v: Fq,
114}
115
116impl fmt::Display for AffinePoint {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 write!(f, "{:?}", self)
119 }
120}
121
122impl Neg for AffinePoint {
123 type Output = AffinePoint;
124
125 #[inline]
128 fn neg(self) -> AffinePoint {
129 AffinePoint {
130 u: -self.u,
131 v: self.v,
132 }
133 }
134}
135
136impl ConstantTimeEq for AffinePoint {
137 fn ct_eq(&self, other: &Self) -> Choice {
138 self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v)
139 }
140}
141
142impl PartialEq for AffinePoint {
143 fn eq(&self, other: &Self) -> bool {
144 bool::from(self.ct_eq(other))
145 }
146}
147
148impl ConditionallySelectable for AffinePoint {
149 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
150 AffinePoint {
151 u: Fq::conditional_select(&a.u, &b.u, choice),
152 v: Fq::conditional_select(&a.v, &b.v, choice),
153 }
154 }
155}
156
157#[derive(Clone, Copy, Debug, Eq)]
169pub struct ExtendedPoint {
170 u: Fq,
171 v: Fq,
172 z: Fq,
173 t1: Fq,
174 t2: Fq,
175}
176
177impl fmt::Display for ExtendedPoint {
178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179 write!(f, "{:?}", self)
180 }
181}
182
183impl ConstantTimeEq for ExtendedPoint {
184 fn ct_eq(&self, other: &Self) -> Choice {
185 (self.u * other.z).ct_eq(&(other.u * self.z))
191 & (self.v * other.z).ct_eq(&(other.v * self.z))
192 }
193}
194
195impl ConditionallySelectable for ExtendedPoint {
196 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
197 ExtendedPoint {
198 u: Fq::conditional_select(&a.u, &b.u, choice),
199 v: Fq::conditional_select(&a.v, &b.v, choice),
200 z: Fq::conditional_select(&a.z, &b.z, choice),
201 t1: Fq::conditional_select(&a.t1, &b.t1, choice),
202 t2: Fq::conditional_select(&a.t2, &b.t2, choice),
203 }
204 }
205}
206
207impl PartialEq for ExtendedPoint {
208 fn eq(&self, other: &Self) -> bool {
209 bool::from(self.ct_eq(other))
210 }
211}
212
213impl<T> Sum<T> for ExtendedPoint
214where
215 T: Borrow<ExtendedPoint>,
216{
217 fn sum<I>(iter: I) -> Self
218 where
219 I: Iterator<Item = T>,
220 {
221 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
222 }
223}
224
225impl Neg for ExtendedPoint {
226 type Output = ExtendedPoint;
227
228 #[inline]
232 fn neg(self) -> ExtendedPoint {
233 ExtendedPoint {
234 u: -self.u,
235 v: self.v,
236 z: self.z,
237 t1: -self.t1,
238 t2: self.t2,
239 }
240 }
241}
242
243impl From<AffinePoint> for ExtendedPoint {
244 fn from(affine: AffinePoint) -> ExtendedPoint {
247 ExtendedPoint {
248 u: affine.u,
249 v: affine.v,
250 z: Fq::one(),
251 t1: affine.u,
252 t2: affine.v,
253 }
254 }
255}
256
257impl<'a> From<&'a ExtendedPoint> for AffinePoint {
258 fn from(extended: &'a ExtendedPoint) -> AffinePoint {
264 let zinv = extended.z.invert().unwrap();
267
268 AffinePoint {
269 u: extended.u * zinv,
270 v: extended.v * zinv,
271 }
272 }
273}
274
275impl From<ExtendedPoint> for AffinePoint {
276 fn from(extended: ExtendedPoint) -> AffinePoint {
277 AffinePoint::from(&extended)
278 }
279}
280
281#[derive(Clone, Copy, Debug)]
285pub struct AffineNielsPoint {
286 v_plus_u: Fq,
287 v_minus_u: Fq,
288 t2d: Fq,
289}
290
291impl AffineNielsPoint {
292 pub fn identity() -> Self {
294 AffineNielsPoint {
295 v_plus_u: Fq::one(),
296 v_minus_u: Fq::one(),
297 t2d: Fq::zero(),
298 }
299 }
300
301 #[inline]
302 fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint {
303 #[cfg(feature = "stats")]
304 AFFINE_MULS.fetch_add(1, Relaxed);
305
306 let zero = AffineNielsPoint::identity();
307
308 let mut acc = ExtendedPoint::identity();
309
310 for bit in by
317 .as_bits::<Lsb0>()
318 .iter()
319 .rev()
320 .skip(4)
321 .map(|bit| Choice::from(if *bit { 1 } else { 0 }))
322 {
323 acc = acc.double();
324 acc += AffineNielsPoint::conditional_select(&zero, &self, bit);
325 }
326
327 acc
328 }
329
330 pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint {
333 self.multiply(by)
334 }
335}
336
337impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint {
338 type Output = ExtendedPoint;
339
340 fn mul(self, other: &'b Fr) -> ExtendedPoint {
341 self.multiply(&other.to_bytes())
342 }
343}
344
345impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint);
346
347impl ConditionallySelectable for AffineNielsPoint {
348 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
349 AffineNielsPoint {
350 v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice),
351 v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice),
352 t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice),
353 }
354 }
355}
356
357#[derive(Clone, Copy, Debug)]
360pub struct ExtendedNielsPoint {
361 v_plus_u: Fq,
362 v_minus_u: Fq,
363 z: Fq,
364 t2d: Fq,
365}
366
367impl ConditionallySelectable for ExtendedNielsPoint {
368 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
369 ExtendedNielsPoint {
370 v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice),
371 v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice),
372 z: Fq::conditional_select(&a.z, &b.z, choice),
373 t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice),
374 }
375 }
376}
377
378impl ExtendedNielsPoint {
379 pub fn identity() -> Self {
381 ExtendedNielsPoint {
382 v_plus_u: Fq::one(),
383 v_minus_u: Fq::one(),
384 z: Fq::one(),
385 t2d: Fq::zero(),
386 }
387 }
388
389 #[inline]
390 fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint {
391 #[cfg(feature = "stats")]
392 EXTENDED_MULS.fetch_add(1, Relaxed);
393
394 let zero = ExtendedNielsPoint::identity();
395
396 let mut acc = ExtendedPoint::identity();
397
398 for bit in by
405 .iter()
406 .rev()
407 .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
408 .skip(4)
409 {
410 acc = acc.double();
411 acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit);
412 }
413
414 acc
415 }
416
417 pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint {
420 self.multiply(by)
421 }
422}
423
424impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint {
425 type Output = ExtendedPoint;
426
427 fn mul(self, other: &'b Fr) -> ExtendedPoint {
428 self.multiply(&other.to_bytes())
429 }
430}
431
432impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint);
433
434lazy_static! {
435 static ref EDWARDS_D: Fq = Fq::from_u64s_le(&[
437 0x0106_5fd6_d634_3eb1,
438 0x292d_7f6d_3757_9d26,
439 0xf5fd_9207_e6bd_7fd4,
440 0x2a93_18e7_4bfa_2b48,
441 ]).unwrap();
442
443 static ref EDWARDS_D2: Fq = Fq::from_u64s_le(&[
445 0x020c_bfad_ac68_7d62,
446 0x525a_feda_6eaf_3a4c,
447 0xebfb_240f_cd7a_ffa8,
448 0x5526_31ce_97f4_5691,
449 ]).unwrap();
450}
451
452
453
454impl AffinePoint {
455 pub fn identity() -> Self {
457 AffinePoint {
458 u: Fq::zero(),
459 v: Fq::one(),
460 }
461 }
462
463 pub fn is_identity(&self) -> Choice {
465 ExtendedPoint::from(*self).is_identity()
466 }
467
468 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
471 ExtendedPoint::from(*self).mul_by_cofactor()
472 }
473
474 pub fn is_small_order(&self) -> Choice {
476 ExtendedPoint::from(*self).is_small_order()
477 }
478
479 pub fn is_torsion_free(&self) -> Choice {
482 ExtendedPoint::from(*self).is_torsion_free()
483 }
484
485 pub fn is_prime_order(&self) -> Choice {
490 let extended = ExtendedPoint::from(*self);
491 extended.is_torsion_free() & (!extended.is_identity())
492 }
493
494 pub fn to_bytes(&self) -> [u8; 32] {
496 let mut tmp = self.v.to_bytes_le();
497 let u = self.u.to_bytes_le();
498
499 tmp[31] |= u[0] << 7;
502
503 tmp
504 }
505
506 pub fn from_bytes(b: [u8; 32]) -> CtOption<Self> {
510 Self::from_bytes_inner(b, 1.into())
511 }
512
513 pub fn from_bytes_pre_zip216_compatibility(b: [u8; 32]) -> CtOption<Self> {
529 Self::from_bytes_inner(b, 0.into())
530 }
531
532 fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption<Self> {
533 let sign = b[31] >> 7;
535
536 b[31] &= 0b0111_1111;
538
539 Fq::from_bytes_le(&b).and_then(|v| {
541 let v2 = v.square();
553
554 ((v2 - Fq::one()) * ((Fq::one() + *EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
555 .sqrt()
556 .and_then(|u| {
557 let flip_sign = Choice::from((u.to_bytes_le()[0] ^ sign) & 1);
559 let u_negated = -u;
560 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
561
562 let u_is_zero = u.ct_eq(&Fq::zero());
568 CtOption::new(
569 AffinePoint { u: final_u, v },
570 !(zip_216_enabled & u_is_zero & flip_sign),
571 )
572 })
573 })
574 }
575
576 #[cfg(feature = "alloc")]
581 pub fn batch_from_bytes(items: impl Iterator<Item = [u8; 32]>) -> Vec<CtOption<Self>> {
582 use ff::BatchInvert;
583
584 #[derive(Clone, Copy, Default)]
585 struct Item {
586 sign: u8,
587 v: Fq,
588 numerator: Fq,
589 denominator: Fq,
590 }
591
592 impl ConditionallySelectable for Item {
593 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
594 Item {
595 sign: u8::conditional_select(&a.sign, &b.sign, choice),
596 v: Fq::conditional_select(&a.v, &b.v, choice),
597 numerator: Fq::conditional_select(&a.numerator, &b.numerator, choice),
598 denominator: Fq::conditional_select(&a.denominator, &b.denominator, choice),
599 }
600 }
601 }
602
603 let items: Vec<_> = items
604 .map(|mut b| {
605 let sign = b[31] >> 7;
607
608 b[31] &= 0b0111_1111;
610
611 Fq::from_bytes_le(&b).map(|v| {
613 let v2 = v.square();
625
626 Item {
627 v,
628 sign,
629 numerator: (v2 - Fq::one()),
630 denominator: Fq::one() + *EDWARDS_D * v2,
631 }
632 })
633 })
634 .collect();
635
636 let mut denominators: Vec<_> = items
637 .iter()
638 .map(|item| item.map(|item| item.denominator).unwrap_or(Fq::zero()))
639 .collect();
640 denominators.iter_mut().batch_invert();
641
642 items
643 .into_iter()
644 .zip(denominators.into_iter())
645 .map(|(item, inv_denominator)| {
646 item.and_then(
647 |Item {
648 v, sign, numerator, ..
649 }| {
650 (numerator * inv_denominator).sqrt().and_then(|u| {
651 let flip_sign = Choice::from((u.to_bytes_le()[0] ^ sign) & 1);
653 let u_negated = -u;
654 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
655
656 let u_is_zero = u.ct_eq(&Fq::zero());
661 CtOption::new(AffinePoint { u: final_u, v }, !(u_is_zero & flip_sign))
662 })
663 },
664 )
665 })
666 .collect()
667 }
668
669 pub fn get_u(&self) -> Fq {
671 self.u
672 }
673
674 pub fn get_v(&self) -> Fq {
676 self.v
677 }
678
679 pub fn to_extended(&self) -> ExtendedPoint {
681 ExtendedPoint {
682 u: self.u,
683 v: self.v,
684 z: Fq::one(),
685 t1: self.u,
686 t2: self.v,
687 }
688 }
689
690 pub fn to_niels(&self) -> AffineNielsPoint {
693 AffineNielsPoint {
694 v_plus_u: Fq::add(self.v, &self.u),
695 v_minus_u: Fq::sub(self.v, &self.u),
696 t2d: Fq::mul(Fq::mul(self.u, &self.v), &*EDWARDS_D2),
697 }
698 }
699
700 pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint {
703 AffinePoint { u, v }
704 }
705
706 #[cfg(test)]
710 fn is_on_curve_vartime(&self) -> bool {
711 let u2 = self.u.square();
712 let v2 = self.v.square();
713
714 v2 - u2 == Fq::one() + *EDWARDS_D * u2 * v2
715 }
716}
717
718impl ExtendedPoint {
719 pub fn identity() -> Self {
721 ExtendedPoint {
722 u: Fq::zero(),
723 v: Fq::one(),
724 z: Fq::one(),
725 t1: Fq::zero(),
726 t2: Fq::zero(),
727 }
728 }
729
730 pub fn is_identity(&self) -> Choice {
732 self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z)
736 }
737
738 pub fn is_small_order(&self) -> Choice {
740 self.double().double().u.ct_eq(&Fq::zero())
745 }
746
747 pub fn is_torsion_free(&self) -> Choice {
750 self.multiply(&FR_MODULUS_BYTES).is_identity()
751 }
752
753 pub fn is_prime_order(&self) -> Choice {
758 self.is_torsion_free() & (!self.is_identity())
759 }
760
761 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
763 self.double().double().double()
764 }
765
766 pub fn to_niels(&self) -> ExtendedNielsPoint {
769 ExtendedNielsPoint {
770 v_plus_u: self.v + self.u,
771 v_minus_u: self.v - self.u,
772 z: self.z,
773 t2d: self.t1 * self.t2 * *EDWARDS_D2,
774 }
775 }
776
777 pub fn double(&self) -> ExtendedPoint {
780 let uu = self.u.square();
853 let vv = self.v.square();
854 let zz2 = self.z.square().double();
855 let uv2 = (self.u + self.v).square();
856 let vv_plus_uu = vv + uu;
857 let vv_minus_uu = vv - uu;
858
859 CompletedPoint {
862 u: uv2 - vv_plus_uu,
863 v: vv_plus_uu,
864 z: vv_minus_uu,
865 t: zz2 - vv_minus_uu,
866 }
867 .into_extended()
868 }
869
870 #[inline]
871 fn multiply(self, by: &[u8; 32]) -> Self {
872 self.to_niels().multiply(by)
873 }
874
875 #[cfg(feature = "multiply-many")]
908 pub fn multiply_many(&self, by: &[[u8; 32]]) -> Vec<ExtendedPoint> {
909 #[cfg(feature = "stats")]
910 EXTENDED_MUL_MANY_CALLS.fetch_add(1, Relaxed);
911 #[cfg(feature = "stats")]
912 EXTENDED_MUL_MANY_OPERANDS.fetch_add(by.len(), Relaxed);
913
914 let mut base = *self;
915 let zero = ExtendedNielsPoint::identity();
916 let mut accs = alloc::vec![ExtendedPoint::identity(); by.len()];
917
918 for bit_index in 0..32 * 8 - 4 {
919 let niels_base = base.to_niels();
920
921 for (array, acc) in by.iter().zip(accs.iter_mut()) {
922 let bit = Choice::from((array[bit_index >> 3] >> (bit_index & 0x7)) & 0b1);
923 *acc += ExtendedNielsPoint::conditional_select(&zero, &niels_base, bit);
924 }
925
926 base = base.double();
927 }
928
929 accs
930 }
931
932 fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) {
938 assert_eq!(p.len(), q.len());
939
940 for (p, q) in p.iter().zip(q.iter_mut()) {
941 q.u = p.z;
944 }
945
946 BatchInverter::invert_with_internal_scratch(q, |q| &mut q.u, |q| &mut q.v);
947
948 for (p, q) in p.iter().zip(q.iter_mut()).rev() {
949 let tmp = q.u;
950
951 q.u = p.u * &tmp; q.v = p.v * &tmp; }
955 }
956
957 #[cfg(test)]
961 fn is_on_curve_vartime(&self) -> bool {
962 let affine = AffinePoint::from(*self);
963
964 self.z != Fq::zero()
965 && affine.is_on_curve_vartime()
966 && (affine.u * affine.v * self.z == self.t1 * self.t2)
967 }
968}
969
970impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint {
971 type Output = ExtendedPoint;
972
973 fn mul(self, other: &'b Fr) -> ExtendedPoint {
974 self.multiply(&other.to_bytes())
975 }
976}
977
978impl_binops_multiplicative!(ExtendedPoint, Fr);
979
980impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
981 type Output = ExtendedPoint;
982
983 #[allow(clippy::suspicious_arithmetic_impl)]
984 fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
985 let a = (self.v - self.u) * other.v_minus_u;
1003 let b = (self.v + self.u) * other.v_plus_u;
1004 let c = self.t1 * self.t2 * other.t2d;
1005 let d = (self.z * other.z).double();
1006
1007 CompletedPoint {
1010 u: b - a,
1011 v: b + a,
1012 z: d + c,
1013 t: d - c,
1014 }
1015 .into_extended()
1016 }
1017}
1018
1019impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
1020 type Output = ExtendedPoint;
1021
1022 #[allow(clippy::suspicious_arithmetic_impl)]
1023 fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
1024 let a = (self.v - self.u) * other.v_plus_u;
1025 let b = (self.v + self.u) * other.v_minus_u;
1026 let c = self.t1 * self.t2 * other.t2d;
1027 let d = (self.z * other.z).double();
1028
1029 CompletedPoint {
1030 u: b - a,
1031 v: b + a,
1032 z: d - c,
1033 t: d + c,
1034 }
1035 .into_extended()
1036 }
1037}
1038
1039impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint);
1040
1041impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint {
1042 type Output = ExtendedPoint;
1043
1044 #[allow(clippy::suspicious_arithmetic_impl)]
1045 fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
1046 let a = (self.v - self.u) * other.v_minus_u;
1051 let b = (self.v + self.u) * other.v_plus_u;
1052 let c = self.t1 * self.t2 * other.t2d;
1053 let d = self.z.double();
1054
1055 CompletedPoint {
1058 u: b - a,
1059 v: b + a,
1060 z: d + c,
1061 t: d - c,
1062 }
1063 .into_extended()
1064 }
1065}
1066
1067impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint {
1068 type Output = ExtendedPoint;
1069
1070 #[allow(clippy::suspicious_arithmetic_impl)]
1071 fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
1072 let a = (self.v - self.u) * other.v_plus_u;
1073 let b = (self.v + self.u) * other.v_minus_u;
1074 let c = self.t1 * self.t2 * other.t2d;
1075 let d = self.z.double();
1076
1077 CompletedPoint {
1078 u: b - a,
1079 v: b + a,
1080 z: d - c,
1081 t: d + c,
1082 }
1083 .into_extended()
1084 }
1085}
1086
1087impl_binops_additive!(ExtendedPoint, AffineNielsPoint);
1088
1089impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint {
1090 type Output = ExtendedPoint;
1091
1092 #[inline]
1093 fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint {
1094 self + other.to_niels()
1095 }
1096}
1097
1098impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint {
1099 type Output = ExtendedPoint;
1100
1101 #[inline]
1102 fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint {
1103 self - other.to_niels()
1104 }
1105}
1106
1107impl_binops_additive!(ExtendedPoint, ExtendedPoint);
1108
1109impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint {
1110 type Output = ExtendedPoint;
1111
1112 #[inline]
1113 fn add(self, other: &'b AffinePoint) -> ExtendedPoint {
1114 self + other.to_niels()
1115 }
1116}
1117
1118impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint {
1119 type Output = ExtendedPoint;
1120
1121 #[inline]
1122 fn sub(self, other: &'b AffinePoint) -> ExtendedPoint {
1123 self - other.to_niels()
1124 }
1125}
1126
1127impl_binops_additive!(ExtendedPoint, AffinePoint);
1128
1129struct CompletedPoint {
1134 u: Fq,
1135 v: Fq,
1136 z: Fq,
1137 t: Fq,
1138}
1139
1140impl CompletedPoint {
1141 #[inline]
1149 fn into_extended(self) -> ExtendedPoint {
1150 ExtendedPoint {
1151 u: self.u * self.t,
1152 v: self.v * self.z,
1153 z: self.z * self.t,
1154 t1: self.u,
1155 t2: self.v,
1156 }
1157 }
1158}
1159
1160impl Default for AffinePoint {
1161 fn default() -> AffinePoint {
1163 AffinePoint::identity()
1164 }
1165}
1166
1167impl Default for ExtendedPoint {
1168 fn default() -> ExtendedPoint {
1170 ExtendedPoint::identity()
1171 }
1172}
1173
1174pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator<Item = AffinePoint> + 'a {
1182 BatchInverter::invert_with_internal_scratch(v, |p| &mut p.z, |p| &mut p.t1);
1184
1185 for p in v.iter_mut() {
1186 let mut q = *p;
1187 let tmp = q.z;
1188
1189 q.u *= &tmp; q.v *= &tmp; q.z = Fq::one(); q.t1 = q.u;
1194 q.t2 = q.v;
1195
1196 *p = q;
1197 }
1198
1199 v.iter().map(|p| AffinePoint { u: p.u, v: p.v })
1204}
1205
1206impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint {
1207 type Output = ExtendedPoint;
1208
1209 fn mul(self, other: &'b Fr) -> ExtendedPoint {
1210 self.to_niels().multiply(&other.to_bytes())
1211 }
1212}
1213
1214impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint);
1215
1216#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1219pub struct SubgroupPoint(ExtendedPoint);
1220
1221impl From<SubgroupPoint> for ExtendedPoint {
1222 fn from(val: SubgroupPoint) -> ExtendedPoint {
1223 val.0
1224 }
1225}
1226
1227impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint {
1228 fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint {
1229 &val.0
1230 }
1231}
1232
1233impl fmt::Display for SubgroupPoint {
1234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1235 write!(f, "{}", self.0)
1236 }
1237}
1238
1239impl ConditionallySelectable for SubgroupPoint {
1240 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
1241 SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice))
1242 }
1243}
1244
1245impl SubgroupPoint {
1246 pub fn from_raw_unchecked(u: Fq, v: Fq) -> Self {
1254 SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended())
1255 }
1256
1257 pub fn as_extended(&self) -> &ExtendedPoint {
1259 &self.0
1260 }
1261}
1262
1263impl<T> Sum<T> for SubgroupPoint
1264where
1265 T: Borrow<SubgroupPoint>,
1266{
1267 fn sum<I>(iter: I) -> Self
1268 where
1269 I: Iterator<Item = T>,
1270 {
1271 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
1272 }
1273}
1274
1275impl Neg for SubgroupPoint {
1276 type Output = SubgroupPoint;
1277
1278 #[inline]
1279 fn neg(self) -> SubgroupPoint {
1280 SubgroupPoint(-self.0)
1281 }
1282}
1283
1284impl Neg for &SubgroupPoint {
1285 type Output = SubgroupPoint;
1286
1287 #[inline]
1288 fn neg(self) -> SubgroupPoint {
1289 SubgroupPoint(-self.0)
1290 }
1291}
1292
1293impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint {
1294 type Output = ExtendedPoint;
1295
1296 #[inline]
1297 fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1298 self + &other.0
1299 }
1300}
1301
1302impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint {
1303 type Output = ExtendedPoint;
1304
1305 #[inline]
1306 fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1307 self - &other.0
1308 }
1309}
1310
1311impl_binops_additive!(ExtendedPoint, SubgroupPoint);
1312
1313impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint {
1314 type Output = SubgroupPoint;
1315
1316 #[inline]
1317 fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1318 SubgroupPoint(self.0 + &other.0)
1319 }
1320}
1321
1322impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint {
1323 type Output = SubgroupPoint;
1324
1325 #[inline]
1326 fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1327 SubgroupPoint(self.0 - &other.0)
1328 }
1329}
1330
1331impl_binops_additive!(SubgroupPoint, SubgroupPoint);
1332
1333impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint {
1334 type Output = SubgroupPoint;
1335
1336 fn mul(self, other: &'b Fr) -> SubgroupPoint {
1337 SubgroupPoint(self.0.multiply(&other.to_bytes()))
1338 }
1339}
1340
1341impl_binops_multiplicative!(SubgroupPoint, Fr);
1342
1343impl Group for ExtendedPoint {
1344 type Scalar = Fr;
1345
1346 fn random(mut rng: impl RngCore) -> Self {
1347 loop {
1348 let v = Fq::random(&mut rng);
1349 let flip_sign = rng.next_u32() % 2 != 0;
1350
1351 let v2 = v.square();
1353 let p = ((v2 - Fq::one())
1354 * ((Fq::one() + *EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
1355 .sqrt()
1356 .map(|u| AffinePoint {
1357 u: if flip_sign { -u } else { u },
1358 v,
1359 });
1360
1361 if p.is_some().into() {
1362 let p = p.unwrap().to_curve();
1363
1364 if bool::from(!p.is_identity()) {
1365 return p;
1366 }
1367 }
1368 }
1369 }
1370
1371 fn identity() -> Self {
1372 Self::identity()
1373 }
1374
1375 fn generator() -> Self {
1376 AffinePoint::generator().into()
1377 }
1378
1379 fn is_identity(&self) -> Choice {
1380 self.is_identity()
1381 }
1382
1383 #[must_use]
1384 fn double(&self) -> Self {
1385 self.double()
1386 }
1387}
1388
1389impl Group for SubgroupPoint {
1390 type Scalar = Fr;
1391
1392 fn random(mut rng: impl RngCore) -> Self {
1393 loop {
1394 let p = ExtendedPoint::random(&mut rng).clear_cofactor();
1395
1396 if bool::from(!p.is_identity()) {
1397 return p;
1398 }
1399 }
1400 }
1401
1402 fn identity() -> Self {
1403 SubgroupPoint(ExtendedPoint::identity())
1404 }
1405
1406 fn generator() -> Self {
1407 ExtendedPoint::generator().clear_cofactor()
1408 }
1409
1410 fn is_identity(&self) -> Choice {
1411 self.0.is_identity()
1412 }
1413
1414 #[must_use]
1415 fn double(&self) -> Self {
1416 SubgroupPoint(self.0.double())
1417 }
1418}
1419
1420#[cfg(feature = "alloc")]
1421impl WnafGroup for ExtendedPoint {
1422 fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
1423 const RECOMMENDATIONS: [usize; 12] =
1425 [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
1426
1427 let mut ret = 4;
1428 for r in &RECOMMENDATIONS {
1429 if num_scalars > *r {
1430 ret += 1;
1431 } else {
1432 break;
1433 }
1434 }
1435
1436 ret
1437 }
1438}
1439
1440impl PrimeGroup for SubgroupPoint {}
1441
1442impl CofactorGroup for ExtendedPoint {
1443 type Subgroup = SubgroupPoint;
1444
1445 fn clear_cofactor(&self) -> Self::Subgroup {
1446 SubgroupPoint(self.mul_by_cofactor())
1447 }
1448
1449 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1450 CtOption::new(SubgroupPoint(self), self.is_torsion_free())
1451 }
1452
1453 fn is_torsion_free(&self) -> Choice {
1454 self.is_torsion_free()
1455 }
1456}
1457
1458impl Curve for ExtendedPoint {
1459 type AffineRepr = AffinePoint;
1460
1461 fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
1462 Self::batch_normalize(p, q);
1463 }
1464
1465 fn to_affine(&self) -> Self::AffineRepr {
1466 self.into()
1467 }
1468}
1469
1470impl CofactorCurve for ExtendedPoint {
1471 type Affine = AffinePoint;
1472}
1473
1474impl CofactorCurveAffine for AffinePoint {
1475 type Scalar = Fr;
1476 type Curve = ExtendedPoint;
1477
1478 fn identity() -> Self {
1479 Self::identity()
1480 }
1481
1482 fn generator() -> Self {
1483 AffinePoint {
1485 u: Fq::from_u64s_le(&[
1486 0xe4b3_d35d_f1a7_adfe,
1487 0xcaf5_5d1b_29bf_81af,
1488 0x8b0f_03dd_d60a_8187,
1489 0x62ed_cbb8_bf37_87c8,
1490 ]).unwrap(),
1491 v: Fq::from_u64s_le(&[
1492 0x0000_0000_0000_000b,
1493 0x0000_0000_0000_0000,
1494 0x0000_0000_0000_0000,
1495 0x0000_0000_0000_0000,
1496 ]).unwrap(),
1497 }
1498 }
1499
1500 fn is_identity(&self) -> Choice {
1501 self.is_identity()
1502 }
1503
1504 fn to_curve(&self) -> Self::Curve {
1505 (*self).into()
1506 }
1507}
1508
1509impl GroupEncoding for ExtendedPoint {
1510 type Repr = [u8; 32];
1511
1512 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1513 AffinePoint::from_bytes(*bytes).map(Self::from)
1514 }
1515
1516 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1517 AffinePoint::from_bytes(*bytes).map(Self::from)
1519 }
1520
1521 fn to_bytes(&self) -> Self::Repr {
1522 AffinePoint::from(self).to_bytes()
1523 }
1524}
1525
1526impl GroupEncoding for SubgroupPoint {
1527 type Repr = [u8; 32];
1528
1529 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1530 ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1531 }
1532
1533 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1534 ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint)
1535 }
1536
1537 fn to_bytes(&self) -> Self::Repr {
1538 self.0.to_bytes()
1539 }
1540}
1541
1542impl GroupEncoding for AffinePoint {
1543 type Repr = [u8; 32];
1544
1545 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1546 Self::from_bytes(*bytes)
1547 }
1548
1549 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1550 Self::from_bytes(*bytes)
1551 }
1552
1553 fn to_bytes(&self) -> Self::Repr {
1554 self.to_bytes()
1555 }
1556}
1557
1558#[test]
1559fn test_is_on_curve_var() {
1560 assert!(AffinePoint::identity().is_on_curve_vartime());
1561}
1562
1563#[test]
1564fn test_d_is_non_quadratic_residue() {
1565 assert!(bool::from(EDWARDS_D.sqrt().is_none()));
1566 assert!(bool::from((-*EDWARDS_D).sqrt().is_none()));
1567 assert!(bool::from((-*EDWARDS_D).invert().unwrap().sqrt().is_none()));
1568}
1569
1570#[test]
1571fn test_affine_niels_point_identity() {
1572 assert_eq!(
1573 AffineNielsPoint::identity().v_plus_u,
1574 AffinePoint::identity().to_niels().v_plus_u
1575 );
1576 assert_eq!(
1577 AffineNielsPoint::identity().v_minus_u,
1578 AffinePoint::identity().to_niels().v_minus_u
1579 );
1580 assert_eq!(
1581 AffineNielsPoint::identity().t2d,
1582 AffinePoint::identity().to_niels().t2d
1583 );
1584}
1585
1586#[test]
1587fn test_extended_niels_point_identity() {
1588 assert_eq!(
1589 ExtendedNielsPoint::identity().v_plus_u,
1590 ExtendedPoint::identity().to_niels().v_plus_u
1591 );
1592 assert_eq!(
1593 ExtendedNielsPoint::identity().v_minus_u,
1594 ExtendedPoint::identity().to_niels().v_minus_u
1595 );
1596 assert_eq!(
1597 ExtendedNielsPoint::identity().z,
1598 ExtendedPoint::identity().to_niels().z
1599 );
1600 assert_eq!(
1601 ExtendedNielsPoint::identity().t2d,
1602 ExtendedPoint::identity().to_niels().t2d
1603 );
1604}
1605
1606#[test]
1607fn test_assoc() {
1608 let p = ExtendedPoint::from(AffinePoint {
1609 u: Fq::from_u64s_le(&[
1610 0x81c5_71e5_d883_cfb0,
1611 0x049f_7a68_6f14_7029,
1612 0xf539_c860_bc3e_a21f,
1613 0x4284_715b_7ccc_8162,
1614 ]).unwrap(),
1615 v: Fq::from_u64s_le(&[
1616 0xbf09_6275_684b_b8ca,
1617 0xc7ba_2458_90af_256d,
1618 0x5911_9f3e_8638_0eb0,
1619 0x3793_de18_2f9f_b1d2,
1620 ]).unwrap(),
1621 })
1622 .mul_by_cofactor();
1623 assert!(p.is_on_curve_vartime());
1624
1625 assert_eq!(
1626 (p * Fr::from(1000u64)) * Fr::from(3938u64),
1627 p * (Fr::from(1000u64) * Fr::from(3938u64)),
1628 );
1629}
1630
1631#[test]
1632fn test_batch_normalize() {
1633 let mut p = ExtendedPoint::from(AffinePoint {
1634 u: Fq::from_u64s_le(&[
1635 0x81c5_71e5_d883_cfb0,
1636 0x049f_7a68_6f14_7029,
1637 0xf539_c860_bc3e_a21f,
1638 0x4284_715b_7ccc_8162,
1639 ]).unwrap(),
1640 v: Fq::from_u64s_le(&[
1641 0xbf09_6275_684b_b8ca,
1642 0xc7ba_2458_90af_256d,
1643 0x5911_9f3e_8638_0eb0,
1644 0x3793_de18_2f9f_b1d2,
1645 ]).unwrap(),
1646 })
1647 .mul_by_cofactor();
1648
1649 let mut v = vec![];
1650 for _ in 0..10 {
1651 v.push(p);
1652 p = p.double();
1653 }
1654
1655 for p in &v {
1656 assert!(p.is_on_curve_vartime());
1657 }
1658
1659 let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect();
1660 let mut result0 = vec![AffinePoint::identity(); v.len()];
1661 ExtendedPoint::batch_normalize(&v, &mut result0);
1662 for i in 0..10 {
1663 assert!(expected[i] == result0[i]);
1664 }
1665 let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1666 for i in 0..10 {
1667 assert!(expected[i] == result1[i]);
1668 assert!(v[i].is_on_curve_vartime());
1669 assert!(AffinePoint::from(v[i]) == expected[i]);
1670 }
1671 let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1672 for i in 0..10 {
1673 assert!(expected[i] == result2[i]);
1674 assert!(v[i].is_on_curve_vartime());
1675 assert!(AffinePoint::from(v[i]) == expected[i]);
1676 }
1677}
1678
1679#[cfg(test)]
1680fn full_generator() -> AffinePoint {
1681 AffinePoint::from_raw_unchecked(
1682 Fq::from_u64s_le(&[
1683 0xe4b3_d35d_f1a7_adfe,
1684 0xcaf5_5d1b_29bf_81af,
1685 0x8b0f_03dd_d60a_8187,
1686 0x62ed_cbb8_bf37_87c8,
1687 ]).unwrap(),
1688 Fq::from_u64s_le(&[0xb, 0x0, 0x0, 0x0]).unwrap(),
1689 )
1690}
1691
1692#[cfg(test)]
1693fn eight_torsion() -> [AffinePoint; 8] {
1694 [
1695 AffinePoint::from_raw_unchecked(
1696 Fq::from_u64s_le(&[
1697 0xd92e_6a79_2720_0d43,
1698 0x7aa4_1ac4_3dae_8582,
1699 0xeaaa_e086_a166_18d1,
1700 0x71d4_df38_ba9e_7973,
1701 ]).unwrap(),
1702 Fq::from_u64s_le(&[
1703 0xff0d_2068_eff4_96dd,
1704 0x9106_ee90_f384_a4a1,
1705 0x16a1_3035_ad4d_7266,
1706 0x4958_bdb2_1966_982e,
1707 ]).unwrap(),
1708 ),
1709 AffinePoint::from_raw_unchecked(
1710 Fq::from_u64s_le(&[
1711 0xfffe_ffff_0000_0001,
1712 0x67ba_a400_89fb_5bfe,
1713 0xa5e8_0b39_939e_d334,
1714 0x73ed_a753_299d_7d47,
1715 ]).unwrap(),
1716 Fq::from_u64s_le(&[0x0, 0x0, 0x0, 0x0]).unwrap(),
1717 ),
1718 AffinePoint::from_raw_unchecked(
1719 Fq::from_u64s_le(&[
1720 0xd92e_6a79_2720_0d43,
1721 0x7aa4_1ac4_3dae_8582,
1722 0xeaaa_e086_a166_18d1,
1723 0x71d4_df38_ba9e_7973,
1724 ]).unwrap(),
1725 Fq::from_u64s_le(&[
1726 0x00f2_df96_100b_6924,
1727 0xc2b6_b572_0c79_b75d,
1728 0x1c98_a7d2_5c54_659e,
1729 0x2a94_e9a1_1036_e51a,
1730 ]).unwrap(),
1731 ),
1732 AffinePoint::from_raw_unchecked(
1733 Fq::from_u64s_le(&[0x0, 0x0, 0x0, 0x0]).unwrap(),
1734 Fq::from_u64s_le(&[
1735 0xffff_ffff_0000_0000,
1736 0x53bd_a402_fffe_5bfe,
1737 0x3339_d808_09a1_d805,
1738 0x73ed_a753_299d_7d48,
1739 ]).unwrap(),
1740 ),
1741 AffinePoint::from_raw_unchecked(
1742 Fq::from_u64s_le(&[
1743 0x26d1_9585_d8df_f2be,
1744 0xd919_893e_c24f_d67c,
1745 0x488e_f781_683b_bf33,
1746 0x0218_c81a_6eff_03d4,
1747 ]).unwrap(),
1748 Fq::from_u64s_le(&[
1749 0x00f2_df96_100b_6924,
1750 0xc2b6_b572_0c79_b75d,
1751 0x1c98_a7d2_5c54_659e,
1752 0x2a94_e9a1_1036_e51a,
1753 ]).unwrap(),
1754 ),
1755 AffinePoint::from_raw_unchecked(
1756 Fq::from_u64s_le(&[
1757 0x0001_0000_0000_0000,
1758 0xec03_0002_7603_0000,
1759 0x8d51_ccce_7603_04d0,
1760 0x0,
1761 ]).unwrap(),
1762 Fq::from_u64s_le(&[0x0, 0x0, 0x0, 0x0]).unwrap(),
1763 ),
1764 AffinePoint::from_raw_unchecked(
1765 Fq::from_u64s_le(&[
1766 0x26d1_9585_d8df_f2be,
1767 0xd919_893e_c24f_d67c,
1768 0x488e_f781_683b_bf33,
1769 0x0218_c81a_6eff_03d4,
1770 ]).unwrap(),
1771 Fq::from_u64s_le(&[
1772 0xff0d_2068_eff4_96dd,
1773 0x9106_ee90_f384_a4a1,
1774 0x16a1_3035_ad4d_7266,
1775 0x4958_bdb2_1966_982e,
1776 ]).unwrap(),
1777 ),
1778 AffinePoint::from_raw_unchecked(
1779 Fq::from_u64s_le(&[0x0, 0x0, 0x0, 0x0]).unwrap(),
1780 Fq::from_u64s_le(&[0x1, 0x0, 0x0, 0x0]).unwrap(),
1781 ),
1782 ]
1783}
1784
1785#[test]
1786fn find_eight_torsion() {
1787 let g = ExtendedPoint::from(full_generator());
1788 assert!(!bool::from(g.is_small_order()));
1789 let g = g.multiply(&FR_MODULUS_BYTES);
1790 assert!(bool::from(g.is_small_order()));
1791
1792 let mut cur = g;
1793
1794 for (i, point) in eight_torsion().iter().enumerate() {
1795 let tmp = AffinePoint::from(cur);
1796 if &tmp != point {
1797 panic!("{}th torsion point should be {:?}", i, tmp);
1798 }
1799
1800 cur += &g;
1801 }
1802}
1803
1804#[test]
1805fn find_curve_generator() {
1806 let mut trial_bytes = [0; 32];
1807 for _ in 0..255 {
1808 let a = AffinePoint::from_bytes(trial_bytes);
1809 if bool::from(a.is_some()) {
1810 let a = a.unwrap();
1811 assert!(a.is_on_curve_vartime());
1812 let b = ExtendedPoint::from(a);
1813 let b = b.multiply(&FR_MODULUS_BYTES);
1814 assert!(bool::from(b.is_small_order()));
1815 let b = b.double();
1816 assert!(bool::from(b.is_small_order()));
1817 let b = b.double();
1818 assert!(bool::from(b.is_small_order()));
1819 if !bool::from(b.is_identity()) {
1820 let b = b.double();
1821 assert!(bool::from(b.is_small_order()));
1822 assert!(bool::from(b.is_identity()));
1823 assert_eq!(full_generator(), a);
1824 assert_eq!(AffinePoint::generator(), a);
1825 assert!(bool::from(a.mul_by_cofactor().is_torsion_free()));
1826 return;
1827 }
1828 }
1829
1830 trial_bytes[0] += 1;
1831 }
1832
1833 panic!("should have found a generator of the curve");
1834}
1835
1836#[test]
1837fn test_small_order() {
1838 for point in eight_torsion().iter() {
1839 assert!(bool::from(point.is_small_order()));
1840 }
1841}
1842
1843#[test]
1844fn test_is_identity() {
1845 let a = eight_torsion()[0].mul_by_cofactor();
1846 let b = eight_torsion()[1].mul_by_cofactor();
1847
1848 assert_eq!(a.u, b.u);
1849 assert_eq!(a.v, a.z);
1850 assert_eq!(b.v, b.z);
1851 assert!(a.v != b.v);
1852 assert!(a.z != b.z);
1853
1854 assert!(bool::from(a.is_identity()));
1855 assert!(bool::from(b.is_identity()));
1856
1857 for point in eight_torsion().iter() {
1858 assert!(bool::from(point.mul_by_cofactor().is_identity()));
1859 }
1860}
1861
1862#[test]
1863fn test_mul_consistency() {
1864 let a = Fr(blst::blst_fr { l: [
1865 0x21e6_1211_d993_4f2e,
1866 0xa52c_058a_693c_3e07,
1867 0x9ccb_77bf_b12d_6360,
1868 0x07df_2470_ec94_398e,
1869 ]});
1870 let b = Fr(blst::blst_fr { l: [
1871 0x0333_6d1c_be19_dbe0,
1872 0x0153_618f_6156_a536,
1873 0x2604_c9e1_fc3c_6b15,
1874 0x04ae_581c_eb02_8720,
1875 ]});
1876 let c = Fr(blst::blst_fr { l: [
1877 0xd7ab_f5bb_2468_3f4c,
1878 0x9d77_12cc_274b_7c03,
1879 0x9732_93db_9683_789f,
1880 0x0b67_7e29_380a_97a7,
1881 ]});
1882 assert_eq!(a * b, c);
1883 let p = ExtendedPoint::from(AffinePoint {
1884 u: Fq::from_u64s_le(&[
1885 0x81c5_71e5_d883_cfb0,
1886 0x049f_7a68_6f14_7029,
1887 0xf539_c860_bc3e_a21f,
1888 0x4284_715b_7ccc_8162,
1889 ]).unwrap(),
1890 v: Fq::from_u64s_le(&[
1891 0xbf09_6275_684b_b8ca,
1892 0xc7ba_2458_90af_256d,
1893 0x5911_9f3e_8638_0eb0,
1894 0x3793_de18_2f9f_b1d2,
1895 ]).unwrap(),
1896 })
1897 .mul_by_cofactor();
1898 assert_eq!(p * c, (p * a) * b);
1899
1900 assert_eq!(p * c, (p.to_niels() * a) * b);
1902 assert_eq!(p.to_niels() * c, (p * a) * b);
1903 assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b);
1904
1905 let p_affine_niels = AffinePoint::from(p).to_niels();
1907 assert_eq!(p * c, (p_affine_niels * a) * b);
1908 assert_eq!(p_affine_niels * c, (p * a) * b);
1909 assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b);
1910}
1911
1912#[test]
1913#[cfg(feature = "multiply-many")]
1914fn test_mul_many() {
1915 let mut rng = rand::thread_rng();
1916 let p = ExtendedPoint::random(&mut rng);
1917 let inputs = (0..256).map(move |_| rand::random::<[u8; 32]>()).collect::<Vec<_>>();
1918
1919 let results_from_multiply = inputs.iter().map(|bits| p.multiply(bits)).collect::<Vec<_>>();
1920 let results_from_multiply_many = p.multiply_many(&inputs);
1921
1922 assert_eq!(results_from_multiply, results_from_multiply_many);
1923}
1924
1925#[test]
1926fn test_serialization_consistency() {
1927 let gen = full_generator().mul_by_cofactor();
1928 let mut p = gen;
1929
1930 let v = vec![
1931 [
1932 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97,
1933 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215,
1934 ],
1935 [
1936 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195,
1937 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177,
1938 ],
1939 [
1940 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82,
1941 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199,
1942 ],
1943 [
1944 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83,
1945 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0,
1946 ],
1947 [
1948 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4,
1949 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185,
1950 ],
1951 [
1952 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166,
1953 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140,
1954 ],
1955 [
1956 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33,
1957 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24,
1958 ],
1959 [
1960 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211,
1961 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138,
1962 ],
1963 [
1964 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93,
1965 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203,
1966 ],
1967 [
1968 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14,
1969 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68,
1970 ],
1971 [
1972 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122,
1973 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97,
1974 ],
1975 [
1976 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150,
1977 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67,
1978 ],
1979 [
1980 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166,
1981 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10,
1982 ],
1983 [
1984 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161,
1985 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178,
1986 ],
1987 [
1988 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73,
1989 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159,
1990 ],
1991 [
1992 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0,
1993 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88,
1994 ],
1995 ];
1996
1997 let batched = AffinePoint::batch_from_bytes(v.iter().cloned());
1998
1999 for (expected_serialized, batch_deserialized) in v.into_iter().zip(batched.into_iter()) {
2000 assert!(p.is_on_curve_vartime());
2001 let affine = AffinePoint::from(p);
2002 let serialized = affine.to_bytes();
2003 let deserialized = AffinePoint::from_bytes(serialized).unwrap();
2004 assert_eq!(affine, deserialized);
2005 assert_eq!(affine, batch_deserialized.unwrap());
2006 assert_eq!(expected_serialized, serialized);
2007 p += gen;
2008 }
2009}
2010
2011#[test]
2012fn test_zip_216() {
2013 const NON_CANONICAL_ENCODINGS: [[u8; 32]; 2] = [
2014 [
2016 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2017 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2018 0x00, 0x00, 0x00, 0x80,
2019 ],
2020 [
2022 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4,
2023 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29,
2024 0x53, 0xa7, 0xed, 0xf3,
2025 ],
2026 ];
2027
2028 for b in &NON_CANONICAL_ENCODINGS {
2029 {
2030 let mut encoding = *b;
2031
2032 assert!(bool::from(AffinePoint::from_bytes(encoding).is_none()));
2034
2035 encoding[31] &= 0b0111_1111;
2038 assert!(bool::from(AffinePoint::from_bytes(encoding).is_some()));
2039 }
2040
2041 {
2042 let parsed = AffinePoint::from_bytes_pre_zip216_compatibility(*b).unwrap();
2045 let mut encoded = parsed.to_bytes();
2046 assert_ne!(b, &encoded);
2047
2048 encoded[31] |= 0b1000_0000;
2051 assert_eq!(b, &encoded);
2052 }
2053 }
2054}