1#![no_std]
20#![deny(rustdoc::broken_intra_doc_links)]
22#![deny(missing_debug_implementations)]
23#![deny(missing_docs)]
24#![deny(unsafe_code)]
25#![allow(clippy::suspicious_arithmetic_impl)]
30
31#[cfg(feature = "alloc")]
32extern crate alloc;
33
34#[cfg(test)]
35#[macro_use]
36extern crate std;
37
38use bitvec::{order::Lsb0, view::AsBits};
39use core::borrow::Borrow;
40use core::fmt;
41use core::iter::Sum;
42use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
43use ff::{BatchInverter, Field};
44use group::{
45 cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup},
46 prime::PrimeGroup,
47 Curve, Group, GroupEncoding,
48};
49use rand_core::RngCore;
50use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
51
52#[cfg(feature = "alloc")]
53use alloc::vec::Vec;
54
55#[cfg(feature = "alloc")]
56use group::WnafGroup;
57
58#[macro_use]
59mod util;
60
61mod fr;
62pub use bls12_381::Scalar as Fq;
63pub use fr::Fr;
64
65pub type Base = Fq;
68
69pub type Scalar = Fr;
72
73const FR_MODULUS_BYTES: [u8; 32] = [
74 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59,
75 103, 6, 169, 175, 51, 101, 234, 180, 125, 14,
76];
77
78#[derive(Clone, Copy, Debug, Eq)]
81pub struct AffinePoint {
82 u: Fq,
83 v: Fq,
84}
85
86impl fmt::Display for AffinePoint {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(f, "{:?}", self)
89 }
90}
91
92impl Neg for AffinePoint {
93 type Output = AffinePoint;
94
95 #[inline]
98 fn neg(self) -> AffinePoint {
99 AffinePoint {
100 u: -self.u,
101 v: self.v,
102 }
103 }
104}
105
106impl ConstantTimeEq for AffinePoint {
107 fn ct_eq(&self, other: &Self) -> Choice {
108 self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v)
109 }
110}
111
112impl PartialEq for AffinePoint {
113 fn eq(&self, other: &Self) -> bool {
114 bool::from(self.ct_eq(other))
115 }
116}
117
118impl ConditionallySelectable for AffinePoint {
119 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
120 AffinePoint {
121 u: Fq::conditional_select(&a.u, &b.u, choice),
122 v: Fq::conditional_select(&a.v, &b.v, choice),
123 }
124 }
125}
126
127#[derive(Clone, Copy, Debug, Eq)]
139pub struct ExtendedPoint {
140 u: Fq,
141 v: Fq,
142 z: Fq,
143 t1: Fq,
144 t2: Fq,
145}
146
147impl fmt::Display for ExtendedPoint {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "{:?}", self)
150 }
151}
152
153impl ConstantTimeEq for ExtendedPoint {
154 fn ct_eq(&self, other: &Self) -> Choice {
155 (self.u * other.z).ct_eq(&(other.u * self.z))
161 & (self.v * other.z).ct_eq(&(other.v * self.z))
162 }
163}
164
165impl ConditionallySelectable for ExtendedPoint {
166 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
167 ExtendedPoint {
168 u: Fq::conditional_select(&a.u, &b.u, choice),
169 v: Fq::conditional_select(&a.v, &b.v, choice),
170 z: Fq::conditional_select(&a.z, &b.z, choice),
171 t1: Fq::conditional_select(&a.t1, &b.t1, choice),
172 t2: Fq::conditional_select(&a.t2, &b.t2, choice),
173 }
174 }
175}
176
177impl PartialEq for ExtendedPoint {
178 fn eq(&self, other: &Self) -> bool {
179 bool::from(self.ct_eq(other))
180 }
181}
182
183impl<T> Sum<T> for ExtendedPoint
184where
185 T: Borrow<ExtendedPoint>,
186{
187 fn sum<I>(iter: I) -> Self
188 where
189 I: Iterator<Item = T>,
190 {
191 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
192 }
193}
194
195impl Neg for ExtendedPoint {
196 type Output = ExtendedPoint;
197
198 #[inline]
202 fn neg(self) -> ExtendedPoint {
203 ExtendedPoint {
204 u: -self.u,
205 v: self.v,
206 z: self.z,
207 t1: -self.t1,
208 t2: self.t2,
209 }
210 }
211}
212
213impl From<AffinePoint> for ExtendedPoint {
214 fn from(affine: AffinePoint) -> ExtendedPoint {
217 ExtendedPoint {
218 u: affine.u,
219 v: affine.v,
220 z: Fq::one(),
221 t1: affine.u,
222 t2: affine.v,
223 }
224 }
225}
226
227impl<'a> From<&'a ExtendedPoint> for AffinePoint {
228 fn from(extended: &'a ExtendedPoint) -> AffinePoint {
234 let zinv = extended.z.invert().unwrap();
237
238 AffinePoint {
239 u: extended.u * zinv,
240 v: extended.v * zinv,
241 }
242 }
243}
244
245impl From<ExtendedPoint> for AffinePoint {
246 fn from(extended: ExtendedPoint) -> AffinePoint {
247 AffinePoint::from(&extended)
248 }
249}
250
251#[derive(Clone, Copy, Debug)]
255pub struct AffineNielsPoint {
256 v_plus_u: Fq,
257 v_minus_u: Fq,
258 t2d: Fq,
259}
260
261impl AffineNielsPoint {
262 pub const fn identity() -> Self {
264 AffineNielsPoint {
265 v_plus_u: Fq::one(),
266 v_minus_u: Fq::one(),
267 t2d: Fq::zero(),
268 }
269 }
270
271 #[inline]
272 fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint {
273 let zero = AffineNielsPoint::identity();
274
275 let mut acc = ExtendedPoint::identity();
276
277 for bit in by
284 .as_bits::<Lsb0>()
285 .iter()
286 .rev()
287 .skip(4)
288 .map(|bit| Choice::from(if *bit { 1 } else { 0 }))
289 {
290 acc = acc.double();
291 acc += AffineNielsPoint::conditional_select(&zero, &self, bit);
292 }
293
294 acc
295 }
296
297 pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint {
300 self.multiply(by)
301 }
302}
303
304impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint {
305 type Output = ExtendedPoint;
306
307 fn mul(self, other: &'b Fr) -> ExtendedPoint {
308 self.multiply(&other.to_bytes())
309 }
310}
311
312impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint);
313
314impl ConditionallySelectable for AffineNielsPoint {
315 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
316 AffineNielsPoint {
317 v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice),
318 v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice),
319 t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice),
320 }
321 }
322}
323
324#[derive(Clone, Copy, Debug)]
327pub struct ExtendedNielsPoint {
328 v_plus_u: Fq,
329 v_minus_u: Fq,
330 z: Fq,
331 t2d: Fq,
332}
333
334impl ConditionallySelectable for ExtendedNielsPoint {
335 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
336 ExtendedNielsPoint {
337 v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice),
338 v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice),
339 z: Fq::conditional_select(&a.z, &b.z, choice),
340 t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice),
341 }
342 }
343}
344
345impl ExtendedNielsPoint {
346 pub const fn identity() -> Self {
348 ExtendedNielsPoint {
349 v_plus_u: Fq::one(),
350 v_minus_u: Fq::one(),
351 z: Fq::one(),
352 t2d: Fq::zero(),
353 }
354 }
355
356 #[inline]
357 fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint {
358 let zero = ExtendedNielsPoint::identity();
359
360 let mut acc = ExtendedPoint::identity();
361
362 for bit in by
369 .iter()
370 .rev()
371 .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8)))
372 .skip(4)
373 {
374 acc = acc.double();
375 acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit);
376 }
377
378 acc
379 }
380
381 pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint {
384 self.multiply(by)
385 }
386}
387
388impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint {
389 type Output = ExtendedPoint;
390
391 fn mul(self, other: &'b Fr) -> ExtendedPoint {
392 self.multiply(&other.to_bytes())
393 }
394}
395
396impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint);
397
398const EDWARDS_D: Fq = Fq::from_raw([
400 0x0106_5fd6_d634_3eb1,
401 0x292d_7f6d_3757_9d26,
402 0xf5fd_9207_e6bd_7fd4,
403 0x2a93_18e7_4bfa_2b48,
404]);
405
406const EDWARDS_D2: Fq = Fq::from_raw([
408 0x020c_bfad_ac68_7d62,
409 0x525a_feda_6eaf_3a4c,
410 0xebfb_240f_cd7a_ffa8,
411 0x5526_31ce_97f4_5691,
412]);
413
414impl AffinePoint {
415 pub const fn identity() -> Self {
417 AffinePoint {
418 u: Fq::zero(),
419 v: Fq::one(),
420 }
421 }
422
423 pub fn is_identity(&self) -> Choice {
425 ExtendedPoint::from(*self).is_identity()
426 }
427
428 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
431 ExtendedPoint::from(*self).mul_by_cofactor()
432 }
433
434 pub fn is_small_order(&self) -> Choice {
436 ExtendedPoint::from(*self).is_small_order()
437 }
438
439 pub fn is_torsion_free(&self) -> Choice {
442 ExtendedPoint::from(*self).is_torsion_free()
443 }
444
445 pub fn is_prime_order(&self) -> Choice {
450 let extended = ExtendedPoint::from(*self);
451 extended.is_torsion_free() & (!extended.is_identity())
452 }
453
454 pub fn to_bytes(&self) -> [u8; 32] {
456 let mut tmp = self.v.to_bytes();
457 let u = self.u.to_bytes();
458
459 tmp[31] |= u[0] << 7;
462
463 tmp
464 }
465
466 pub fn from_bytes(b: [u8; 32]) -> CtOption<Self> {
470 Self::from_bytes_inner(b, 1.into())
471 }
472
473 pub fn from_bytes_pre_zip216_compatibility(b: [u8; 32]) -> CtOption<Self> {
489 Self::from_bytes_inner(b, 0.into())
490 }
491
492 fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption<Self> {
493 let sign = b[31] >> 7;
495
496 b[31] &= 0b0111_1111;
498
499 Fq::from_bytes(&b).and_then(|v| {
501 let v2 = v.square();
513
514 ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
515 .sqrt()
516 .and_then(|u| {
517 let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
519 let u_negated = -u;
520 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
521
522 let u_is_zero = u.ct_eq(&Fq::zero());
528 CtOption::new(
529 AffinePoint { u: final_u, v },
530 !(zip_216_enabled & u_is_zero & flip_sign),
531 )
532 })
533 })
534 }
535
536 #[cfg(feature = "alloc")]
541 pub fn batch_from_bytes(items: impl Iterator<Item = [u8; 32]>) -> Vec<CtOption<Self>> {
542 use ff::BatchInvert;
543
544 #[derive(Clone, Copy, Default)]
545 struct Item {
546 sign: u8,
547 v: Fq,
548 numerator: Fq,
549 denominator: Fq,
550 }
551
552 impl ConditionallySelectable for Item {
553 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
554 Item {
555 sign: u8::conditional_select(&a.sign, &b.sign, choice),
556 v: Fq::conditional_select(&a.v, &b.v, choice),
557 numerator: Fq::conditional_select(&a.numerator, &b.numerator, choice),
558 denominator: Fq::conditional_select(&a.denominator, &b.denominator, choice),
559 }
560 }
561 }
562
563 let items: Vec<_> = items
564 .map(|mut b| {
565 let sign = b[31] >> 7;
567
568 b[31] &= 0b0111_1111;
570
571 Fq::from_bytes(&b).map(|v| {
573 let v2 = v.square();
585
586 Item {
587 v,
588 sign,
589 numerator: (v2 - Fq::one()),
590 denominator: Fq::one() + EDWARDS_D * v2,
591 }
592 })
593 })
594 .collect();
595
596 let mut denominators: Vec<_> = items
597 .iter()
598 .map(|item| item.map(|item| item.denominator).unwrap_or(Fq::zero()))
599 .collect();
600 denominators.iter_mut().batch_invert();
601
602 items
603 .into_iter()
604 .zip(denominators.into_iter())
605 .map(|(item, inv_denominator)| {
606 item.and_then(
607 |Item {
608 v, sign, numerator, ..
609 }| {
610 (numerator * inv_denominator).sqrt().and_then(|u| {
611 let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
613 let u_negated = -u;
614 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
615
616 let u_is_zero = u.ct_eq(&Fq::zero());
621 CtOption::new(AffinePoint { u: final_u, v }, !(u_is_zero & flip_sign))
622 })
623 },
624 )
625 })
626 .collect()
627 }
628
629 pub fn get_u(&self) -> Fq {
631 self.u
632 }
633
634 pub fn get_v(&self) -> Fq {
636 self.v
637 }
638
639 pub const fn to_extended(&self) -> ExtendedPoint {
641 ExtendedPoint {
642 u: self.u,
643 v: self.v,
644 z: Fq::one(),
645 t1: self.u,
646 t2: self.v,
647 }
648 }
649
650 pub const fn to_niels(&self) -> AffineNielsPoint {
653 AffineNielsPoint {
654 v_plus_u: Fq::add(&self.v, &self.u),
655 v_minus_u: Fq::sub(&self.v, &self.u),
656 t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2),
657 }
658 }
659
660 pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint {
663 AffinePoint { u, v }
664 }
665
666 #[cfg(test)]
670 fn is_on_curve_vartime(&self) -> bool {
671 let u2 = self.u.square();
672 let v2 = self.v.square();
673
674 v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2
675 }
676}
677
678impl ExtendedPoint {
679 pub const fn identity() -> Self {
681 ExtendedPoint {
682 u: Fq::zero(),
683 v: Fq::one(),
684 z: Fq::one(),
685 t1: Fq::zero(),
686 t2: Fq::zero(),
687 }
688 }
689
690 pub fn is_identity(&self) -> Choice {
692 self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z)
696 }
697
698 pub fn is_small_order(&self) -> Choice {
700 self.double().double().u.ct_eq(&Fq::zero())
705 }
706
707 pub fn is_torsion_free(&self) -> Choice {
710 self.multiply(&FR_MODULUS_BYTES).is_identity()
711 }
712
713 pub fn is_prime_order(&self) -> Choice {
718 self.is_torsion_free() & (!self.is_identity())
719 }
720
721 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
723 self.double().double().double()
724 }
725
726 pub fn to_niels(&self) -> ExtendedNielsPoint {
729 ExtendedNielsPoint {
730 v_plus_u: self.v + self.u,
731 v_minus_u: self.v - self.u,
732 z: self.z,
733 t2d: self.t1 * self.t2 * EDWARDS_D2,
734 }
735 }
736
737 pub fn double(&self) -> ExtendedPoint {
740 let uu = self.u.square();
813 let vv = self.v.square();
814 let zz2 = self.z.square().double();
815 let uv2 = (self.u + self.v).square();
816 let vv_plus_uu = vv + uu;
817 let vv_minus_uu = vv - uu;
818
819 CompletedPoint {
822 u: uv2 - vv_plus_uu,
823 v: vv_plus_uu,
824 z: vv_minus_uu,
825 t: zz2 - vv_minus_uu,
826 }
827 .into_extended()
828 }
829
830 #[inline]
831 fn multiply(self, by: &[u8; 32]) -> Self {
832 self.to_niels().multiply(by)
833 }
834
835 fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) {
841 assert_eq!(p.len(), q.len());
842
843 for (p, q) in p.iter().zip(q.iter_mut()) {
844 q.u = p.z;
847 }
848
849 BatchInverter::invert_with_internal_scratch(q, |q| &mut q.u, |q| &mut q.v);
850
851 for (p, q) in p.iter().zip(q.iter_mut()).rev() {
852 let tmp = q.u;
853
854 q.u = p.u * &tmp; q.v = p.v * &tmp; }
858 }
859
860 #[cfg(test)]
864 fn is_on_curve_vartime(&self) -> bool {
865 let affine = AffinePoint::from(*self);
866
867 self.z != Fq::zero()
868 && affine.is_on_curve_vartime()
869 && (affine.u * affine.v * self.z == self.t1 * self.t2)
870 }
871}
872
873impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint {
874 type Output = ExtendedPoint;
875
876 fn mul(self, other: &'b Fr) -> ExtendedPoint {
877 self.multiply(&other.to_bytes())
878 }
879}
880
881impl_binops_multiplicative!(ExtendedPoint, Fr);
882
883impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
884 type Output = ExtendedPoint;
885
886 #[allow(clippy::suspicious_arithmetic_impl)]
887 fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
888 let a = (self.v - self.u) * other.v_minus_u;
906 let b = (self.v + self.u) * other.v_plus_u;
907 let c = self.t1 * self.t2 * other.t2d;
908 let d = (self.z * other.z).double();
909
910 CompletedPoint {
913 u: b - a,
914 v: b + a,
915 z: d + c,
916 t: d - c,
917 }
918 .into_extended()
919 }
920}
921
922impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
923 type Output = ExtendedPoint;
924
925 #[allow(clippy::suspicious_arithmetic_impl)]
926 fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
927 let a = (self.v - self.u) * other.v_plus_u;
928 let b = (self.v + self.u) * other.v_minus_u;
929 let c = self.t1 * self.t2 * other.t2d;
930 let d = (self.z * other.z).double();
931
932 CompletedPoint {
933 u: b - a,
934 v: b + a,
935 z: d - c,
936 t: d + c,
937 }
938 .into_extended()
939 }
940}
941
942impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint);
943
944impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint {
945 type Output = ExtendedPoint;
946
947 #[allow(clippy::suspicious_arithmetic_impl)]
948 fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
949 let a = (self.v - self.u) * other.v_minus_u;
954 let b = (self.v + self.u) * other.v_plus_u;
955 let c = self.t1 * self.t2 * other.t2d;
956 let d = self.z.double();
957
958 CompletedPoint {
961 u: b - a,
962 v: b + a,
963 z: d + c,
964 t: d - c,
965 }
966 .into_extended()
967 }
968}
969
970impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint {
971 type Output = ExtendedPoint;
972
973 #[allow(clippy::suspicious_arithmetic_impl)]
974 fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
975 let a = (self.v - self.u) * other.v_plus_u;
976 let b = (self.v + self.u) * other.v_minus_u;
977 let c = self.t1 * self.t2 * other.t2d;
978 let d = self.z.double();
979
980 CompletedPoint {
981 u: b - a,
982 v: b + a,
983 z: d - c,
984 t: d + c,
985 }
986 .into_extended()
987 }
988}
989
990impl_binops_additive!(ExtendedPoint, AffineNielsPoint);
991
992impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint {
993 type Output = ExtendedPoint;
994
995 #[inline]
996 fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint {
997 self + other.to_niels()
998 }
999}
1000
1001impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint {
1002 type Output = ExtendedPoint;
1003
1004 #[inline]
1005 fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint {
1006 self - other.to_niels()
1007 }
1008}
1009
1010impl_binops_additive!(ExtendedPoint, ExtendedPoint);
1011
1012impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint {
1013 type Output = ExtendedPoint;
1014
1015 #[inline]
1016 fn add(self, other: &'b AffinePoint) -> ExtendedPoint {
1017 self + other.to_niels()
1018 }
1019}
1020
1021impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint {
1022 type Output = ExtendedPoint;
1023
1024 #[inline]
1025 fn sub(self, other: &'b AffinePoint) -> ExtendedPoint {
1026 self - other.to_niels()
1027 }
1028}
1029
1030impl_binops_additive!(ExtendedPoint, AffinePoint);
1031
1032struct CompletedPoint {
1037 u: Fq,
1038 v: Fq,
1039 z: Fq,
1040 t: Fq,
1041}
1042
1043impl CompletedPoint {
1044 #[inline]
1052 fn into_extended(self) -> ExtendedPoint {
1053 ExtendedPoint {
1054 u: self.u * self.t,
1055 v: self.v * self.z,
1056 z: self.z * self.t,
1057 t1: self.u,
1058 t2: self.v,
1059 }
1060 }
1061}
1062
1063impl Default for AffinePoint {
1064 fn default() -> AffinePoint {
1066 AffinePoint::identity()
1067 }
1068}
1069
1070impl Default for ExtendedPoint {
1071 fn default() -> ExtendedPoint {
1073 ExtendedPoint::identity()
1074 }
1075}
1076
1077pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator<Item = AffinePoint> + 'a {
1085 BatchInverter::invert_with_internal_scratch(v, |p| &mut p.z, |p| &mut p.t1);
1087
1088 for p in v.iter_mut() {
1089 let mut q = *p;
1090 let tmp = q.z;
1091
1092 q.u *= &tmp; q.v *= &tmp; q.z = Fq::one(); q.t1 = q.u;
1097 q.t2 = q.v;
1098
1099 *p = q;
1100 }
1101
1102 v.iter().map(|p| AffinePoint { u: p.u, v: p.v })
1107}
1108
1109impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint {
1110 type Output = ExtendedPoint;
1111
1112 fn mul(self, other: &'b Fr) -> ExtendedPoint {
1113 self.to_niels().multiply(&other.to_bytes())
1114 }
1115}
1116
1117impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint);
1118
1119#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1122pub struct SubgroupPoint(ExtendedPoint);
1123
1124impl From<SubgroupPoint> for ExtendedPoint {
1125 fn from(val: SubgroupPoint) -> ExtendedPoint {
1126 val.0
1127 }
1128}
1129
1130impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint {
1131 fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint {
1132 &val.0
1133 }
1134}
1135
1136impl fmt::Display for SubgroupPoint {
1137 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1138 write!(f, "{}", self.0)
1139 }
1140}
1141
1142impl ConditionallySelectable for SubgroupPoint {
1143 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
1144 SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice))
1145 }
1146}
1147
1148impl SubgroupPoint {
1149 pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self {
1157 SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended())
1158 }
1159}
1160
1161impl<T> Sum<T> for SubgroupPoint
1162where
1163 T: Borrow<SubgroupPoint>,
1164{
1165 fn sum<I>(iter: I) -> Self
1166 where
1167 I: Iterator<Item = T>,
1168 {
1169 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
1170 }
1171}
1172
1173impl Neg for SubgroupPoint {
1174 type Output = SubgroupPoint;
1175
1176 #[inline]
1177 fn neg(self) -> SubgroupPoint {
1178 SubgroupPoint(-self.0)
1179 }
1180}
1181
1182impl Neg for &SubgroupPoint {
1183 type Output = SubgroupPoint;
1184
1185 #[inline]
1186 fn neg(self) -> SubgroupPoint {
1187 SubgroupPoint(-self.0)
1188 }
1189}
1190
1191impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint {
1192 type Output = ExtendedPoint;
1193
1194 #[inline]
1195 fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1196 self + &other.0
1197 }
1198}
1199
1200impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint {
1201 type Output = ExtendedPoint;
1202
1203 #[inline]
1204 fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1205 self - &other.0
1206 }
1207}
1208
1209impl_binops_additive!(ExtendedPoint, SubgroupPoint);
1210
1211impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint {
1212 type Output = SubgroupPoint;
1213
1214 #[inline]
1215 fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1216 SubgroupPoint(self.0 + &other.0)
1217 }
1218}
1219
1220impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint {
1221 type Output = SubgroupPoint;
1222
1223 #[inline]
1224 fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1225 SubgroupPoint(self.0 - &other.0)
1226 }
1227}
1228
1229impl_binops_additive!(SubgroupPoint, SubgroupPoint);
1230
1231impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint {
1232 type Output = SubgroupPoint;
1233
1234 fn mul(self, other: &'b Fr) -> SubgroupPoint {
1235 SubgroupPoint(self.0.multiply(&other.to_bytes()))
1236 }
1237}
1238
1239impl_binops_multiplicative!(SubgroupPoint, Fr);
1240
1241impl Group for ExtendedPoint {
1242 type Scalar = Fr;
1243
1244 fn random(mut rng: impl RngCore) -> Self {
1245 loop {
1246 let v = Fq::random(&mut rng);
1247 let flip_sign = rng.next_u32() % 2 != 0;
1248
1249 let v2 = v.square();
1251 let p = ((v2 - Fq::one())
1252 * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
1253 .sqrt()
1254 .map(|u| AffinePoint {
1255 u: if flip_sign { -u } else { u },
1256 v,
1257 });
1258
1259 if p.is_some().into() {
1260 let p = p.unwrap().to_curve();
1261
1262 if bool::from(!p.is_identity()) {
1263 return p;
1264 }
1265 }
1266 }
1267 }
1268
1269 fn identity() -> Self {
1270 Self::identity()
1271 }
1272
1273 fn generator() -> Self {
1274 AffinePoint::generator().into()
1275 }
1276
1277 fn is_identity(&self) -> Choice {
1278 self.is_identity()
1279 }
1280
1281 #[must_use]
1282 fn double(&self) -> Self {
1283 self.double()
1284 }
1285}
1286
1287impl Group for SubgroupPoint {
1288 type Scalar = Fr;
1289
1290 fn random(mut rng: impl RngCore) -> Self {
1291 loop {
1292 let p = ExtendedPoint::random(&mut rng).clear_cofactor();
1293
1294 if bool::from(!p.is_identity()) {
1295 return p;
1296 }
1297 }
1298 }
1299
1300 fn identity() -> Self {
1301 SubgroupPoint(ExtendedPoint::identity())
1302 }
1303
1304 fn generator() -> Self {
1305 ExtendedPoint::generator().clear_cofactor()
1306 }
1307
1308 fn is_identity(&self) -> Choice {
1309 self.0.is_identity()
1310 }
1311
1312 #[must_use]
1313 fn double(&self) -> Self {
1314 SubgroupPoint(self.0.double())
1315 }
1316}
1317
1318#[cfg(feature = "alloc")]
1319impl WnafGroup for ExtendedPoint {
1320 fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
1321 const RECOMMENDATIONS: [usize; 12] =
1323 [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
1324
1325 let mut ret = 4;
1326 for r in &RECOMMENDATIONS {
1327 if num_scalars > *r {
1328 ret += 1;
1329 } else {
1330 break;
1331 }
1332 }
1333
1334 ret
1335 }
1336}
1337
1338impl PrimeGroup for SubgroupPoint {}
1339
1340impl CofactorGroup for ExtendedPoint {
1341 type Subgroup = SubgroupPoint;
1342
1343 fn clear_cofactor(&self) -> Self::Subgroup {
1344 SubgroupPoint(self.mul_by_cofactor())
1345 }
1346
1347 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1348 CtOption::new(SubgroupPoint(self), self.is_torsion_free())
1349 }
1350
1351 fn is_torsion_free(&self) -> Choice {
1352 self.is_torsion_free()
1353 }
1354}
1355
1356impl Curve for ExtendedPoint {
1357 type AffineRepr = AffinePoint;
1358
1359 fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
1360 Self::batch_normalize(p, q);
1361 }
1362
1363 fn to_affine(&self) -> Self::AffineRepr {
1364 self.into()
1365 }
1366}
1367
1368impl CofactorCurve for ExtendedPoint {
1369 type Affine = AffinePoint;
1370}
1371
1372impl CofactorCurveAffine for AffinePoint {
1373 type Scalar = Fr;
1374 type Curve = ExtendedPoint;
1375
1376 fn identity() -> Self {
1377 Self::identity()
1378 }
1379
1380 fn generator() -> Self {
1381 AffinePoint {
1383 u: Fq::from_raw([
1384 0xe4b3_d35d_f1a7_adfe,
1385 0xcaf5_5d1b_29bf_81af,
1386 0x8b0f_03dd_d60a_8187,
1387 0x62ed_cbb8_bf37_87c8,
1388 ]),
1389 v: Fq::from_raw([
1390 0x0000_0000_0000_000b,
1391 0x0000_0000_0000_0000,
1392 0x0000_0000_0000_0000,
1393 0x0000_0000_0000_0000,
1394 ]),
1395 }
1396 }
1397
1398 fn is_identity(&self) -> Choice {
1399 self.is_identity()
1400 }
1401
1402 fn to_curve(&self) -> Self::Curve {
1403 (*self).into()
1404 }
1405}
1406
1407impl GroupEncoding for ExtendedPoint {
1408 type Repr = [u8; 32];
1409
1410 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1411 AffinePoint::from_bytes(*bytes).map(Self::from)
1412 }
1413
1414 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1415 AffinePoint::from_bytes(*bytes).map(Self::from)
1417 }
1418
1419 fn to_bytes(&self) -> Self::Repr {
1420 AffinePoint::from(self).to_bytes()
1421 }
1422}
1423
1424impl GroupEncoding for SubgroupPoint {
1425 type Repr = [u8; 32];
1426
1427 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1428 ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1429 }
1430
1431 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1432 ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint)
1433 }
1434
1435 fn to_bytes(&self) -> Self::Repr {
1436 self.0.to_bytes()
1437 }
1438}
1439
1440impl GroupEncoding for AffinePoint {
1441 type Repr = [u8; 32];
1442
1443 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1444 Self::from_bytes(*bytes)
1445 }
1446
1447 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1448 Self::from_bytes(*bytes)
1449 }
1450
1451 fn to_bytes(&self) -> Self::Repr {
1452 self.to_bytes()
1453 }
1454}
1455
1456#[test]
1457fn test_is_on_curve_var() {
1458 assert!(AffinePoint::identity().is_on_curve_vartime());
1459}
1460
1461#[test]
1462fn test_d_is_non_quadratic_residue() {
1463 assert!(bool::from(EDWARDS_D.sqrt().is_none()));
1464 assert!(bool::from((-EDWARDS_D).sqrt().is_none()));
1465 assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none()));
1466}
1467
1468#[test]
1469fn test_affine_niels_point_identity() {
1470 assert_eq!(
1471 AffineNielsPoint::identity().v_plus_u,
1472 AffinePoint::identity().to_niels().v_plus_u
1473 );
1474 assert_eq!(
1475 AffineNielsPoint::identity().v_minus_u,
1476 AffinePoint::identity().to_niels().v_minus_u
1477 );
1478 assert_eq!(
1479 AffineNielsPoint::identity().t2d,
1480 AffinePoint::identity().to_niels().t2d
1481 );
1482}
1483
1484#[test]
1485fn test_extended_niels_point_identity() {
1486 assert_eq!(
1487 ExtendedNielsPoint::identity().v_plus_u,
1488 ExtendedPoint::identity().to_niels().v_plus_u
1489 );
1490 assert_eq!(
1491 ExtendedNielsPoint::identity().v_minus_u,
1492 ExtendedPoint::identity().to_niels().v_minus_u
1493 );
1494 assert_eq!(
1495 ExtendedNielsPoint::identity().z,
1496 ExtendedPoint::identity().to_niels().z
1497 );
1498 assert_eq!(
1499 ExtendedNielsPoint::identity().t2d,
1500 ExtendedPoint::identity().to_niels().t2d
1501 );
1502}
1503
1504#[test]
1505fn test_assoc() {
1506 let p = ExtendedPoint::from(AffinePoint {
1507 u: Fq::from_raw([
1508 0x81c5_71e5_d883_cfb0,
1509 0x049f_7a68_6f14_7029,
1510 0xf539_c860_bc3e_a21f,
1511 0x4284_715b_7ccc_8162,
1512 ]),
1513 v: Fq::from_raw([
1514 0xbf09_6275_684b_b8ca,
1515 0xc7ba_2458_90af_256d,
1516 0x5911_9f3e_8638_0eb0,
1517 0x3793_de18_2f9f_b1d2,
1518 ]),
1519 })
1520 .mul_by_cofactor();
1521 assert!(p.is_on_curve_vartime());
1522
1523 assert_eq!(
1524 (p * Fr::from(1000u64)) * Fr::from(3938u64),
1525 p * (Fr::from(1000u64) * Fr::from(3938u64)),
1526 );
1527}
1528
1529#[test]
1530fn test_batch_normalize() {
1531 let mut p = ExtendedPoint::from(AffinePoint {
1532 u: Fq::from_raw([
1533 0x81c5_71e5_d883_cfb0,
1534 0x049f_7a68_6f14_7029,
1535 0xf539_c860_bc3e_a21f,
1536 0x4284_715b_7ccc_8162,
1537 ]),
1538 v: Fq::from_raw([
1539 0xbf09_6275_684b_b8ca,
1540 0xc7ba_2458_90af_256d,
1541 0x5911_9f3e_8638_0eb0,
1542 0x3793_de18_2f9f_b1d2,
1543 ]),
1544 })
1545 .mul_by_cofactor();
1546
1547 let mut v = vec![];
1548 for _ in 0..10 {
1549 v.push(p);
1550 p = p.double();
1551 }
1552
1553 for p in &v {
1554 assert!(p.is_on_curve_vartime());
1555 }
1556
1557 let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect();
1558 let mut result0 = vec![AffinePoint::identity(); v.len()];
1559 ExtendedPoint::batch_normalize(&v, &mut result0);
1560 for i in 0..10 {
1561 assert!(expected[i] == result0[i]);
1562 }
1563 let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1564 for i in 0..10 {
1565 assert!(expected[i] == result1[i]);
1566 assert!(v[i].is_on_curve_vartime());
1567 assert!(AffinePoint::from(v[i]) == expected[i]);
1568 }
1569 let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1570 for i in 0..10 {
1571 assert!(expected[i] == result2[i]);
1572 assert!(v[i].is_on_curve_vartime());
1573 assert!(AffinePoint::from(v[i]) == expected[i]);
1574 }
1575}
1576
1577#[cfg(test)]
1578const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked(
1579 Fq::from_raw([
1580 0xe4b3_d35d_f1a7_adfe,
1581 0xcaf5_5d1b_29bf_81af,
1582 0x8b0f_03dd_d60a_8187,
1583 0x62ed_cbb8_bf37_87c8,
1584 ]),
1585 Fq::from_raw([0xb, 0x0, 0x0, 0x0]),
1586);
1587
1588#[cfg(test)]
1589const EIGHT_TORSION: [AffinePoint; 8] = [
1590 AffinePoint::from_raw_unchecked(
1591 Fq::from_raw([
1592 0xd92e_6a79_2720_0d43,
1593 0x7aa4_1ac4_3dae_8582,
1594 0xeaaa_e086_a166_18d1,
1595 0x71d4_df38_ba9e_7973,
1596 ]),
1597 Fq::from_raw([
1598 0xff0d_2068_eff4_96dd,
1599 0x9106_ee90_f384_a4a1,
1600 0x16a1_3035_ad4d_7266,
1601 0x4958_bdb2_1966_982e,
1602 ]),
1603 ),
1604 AffinePoint::from_raw_unchecked(
1605 Fq::from_raw([
1606 0xfffe_ffff_0000_0001,
1607 0x67ba_a400_89fb_5bfe,
1608 0xa5e8_0b39_939e_d334,
1609 0x73ed_a753_299d_7d47,
1610 ]),
1611 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1612 ),
1613 AffinePoint::from_raw_unchecked(
1614 Fq::from_raw([
1615 0xd92e_6a79_2720_0d43,
1616 0x7aa4_1ac4_3dae_8582,
1617 0xeaaa_e086_a166_18d1,
1618 0x71d4_df38_ba9e_7973,
1619 ]),
1620 Fq::from_raw([
1621 0x00f2_df96_100b_6924,
1622 0xc2b6_b572_0c79_b75d,
1623 0x1c98_a7d2_5c54_659e,
1624 0x2a94_e9a1_1036_e51a,
1625 ]),
1626 ),
1627 AffinePoint::from_raw_unchecked(
1628 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1629 Fq::from_raw([
1630 0xffff_ffff_0000_0000,
1631 0x53bd_a402_fffe_5bfe,
1632 0x3339_d808_09a1_d805,
1633 0x73ed_a753_299d_7d48,
1634 ]),
1635 ),
1636 AffinePoint::from_raw_unchecked(
1637 Fq::from_raw([
1638 0x26d1_9585_d8df_f2be,
1639 0xd919_893e_c24f_d67c,
1640 0x488e_f781_683b_bf33,
1641 0x0218_c81a_6eff_03d4,
1642 ]),
1643 Fq::from_raw([
1644 0x00f2_df96_100b_6924,
1645 0xc2b6_b572_0c79_b75d,
1646 0x1c98_a7d2_5c54_659e,
1647 0x2a94_e9a1_1036_e51a,
1648 ]),
1649 ),
1650 AffinePoint::from_raw_unchecked(
1651 Fq::from_raw([
1652 0x0001_0000_0000_0000,
1653 0xec03_0002_7603_0000,
1654 0x8d51_ccce_7603_04d0,
1655 0x0,
1656 ]),
1657 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1658 ),
1659 AffinePoint::from_raw_unchecked(
1660 Fq::from_raw([
1661 0x26d1_9585_d8df_f2be,
1662 0xd919_893e_c24f_d67c,
1663 0x488e_f781_683b_bf33,
1664 0x0218_c81a_6eff_03d4,
1665 ]),
1666 Fq::from_raw([
1667 0xff0d_2068_eff4_96dd,
1668 0x9106_ee90_f384_a4a1,
1669 0x16a1_3035_ad4d_7266,
1670 0x4958_bdb2_1966_982e,
1671 ]),
1672 ),
1673 AffinePoint::from_raw_unchecked(
1674 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1675 Fq::from_raw([0x1, 0x0, 0x0, 0x0]),
1676 ),
1677];
1678
1679#[test]
1680fn find_eight_torsion() {
1681 let g = ExtendedPoint::from(FULL_GENERATOR);
1682 assert!(!bool::from(g.is_small_order()));
1683 let g = g.multiply(&FR_MODULUS_BYTES);
1684 assert!(bool::from(g.is_small_order()));
1685
1686 let mut cur = g;
1687
1688 for (i, point) in EIGHT_TORSION.iter().enumerate() {
1689 let tmp = AffinePoint::from(cur);
1690 if &tmp != point {
1691 panic!("{}th torsion point should be {:?}", i, tmp);
1692 }
1693
1694 cur += &g;
1695 }
1696}
1697
1698#[test]
1699fn find_curve_generator() {
1700 let mut trial_bytes = [0; 32];
1701 for _ in 0..255 {
1702 let a = AffinePoint::from_bytes(trial_bytes);
1703 if bool::from(a.is_some()) {
1704 let a = a.unwrap();
1705 assert!(a.is_on_curve_vartime());
1706 let b = ExtendedPoint::from(a);
1707 let b = b.multiply(&FR_MODULUS_BYTES);
1708 assert!(bool::from(b.is_small_order()));
1709 let b = b.double();
1710 assert!(bool::from(b.is_small_order()));
1711 let b = b.double();
1712 assert!(bool::from(b.is_small_order()));
1713 if !bool::from(b.is_identity()) {
1714 let b = b.double();
1715 assert!(bool::from(b.is_small_order()));
1716 assert!(bool::from(b.is_identity()));
1717 assert_eq!(FULL_GENERATOR, a);
1718 assert_eq!(AffinePoint::generator(), a);
1719 assert!(bool::from(a.mul_by_cofactor().is_torsion_free()));
1720 return;
1721 }
1722 }
1723
1724 trial_bytes[0] += 1;
1725 }
1726
1727 panic!("should have found a generator of the curve");
1728}
1729
1730#[test]
1731fn test_small_order() {
1732 for point in EIGHT_TORSION.iter() {
1733 assert!(bool::from(point.is_small_order()));
1734 }
1735}
1736
1737#[test]
1738fn test_is_identity() {
1739 let a = EIGHT_TORSION[0].mul_by_cofactor();
1740 let b = EIGHT_TORSION[1].mul_by_cofactor();
1741
1742 assert_eq!(a.u, b.u);
1743 assert_eq!(a.v, a.z);
1744 assert_eq!(b.v, b.z);
1745 assert!(a.v != b.v);
1746 assert!(a.z != b.z);
1747
1748 assert!(bool::from(a.is_identity()));
1749 assert!(bool::from(b.is_identity()));
1750
1751 for point in EIGHT_TORSION.iter() {
1752 assert!(bool::from(point.mul_by_cofactor().is_identity()));
1753 }
1754}
1755
1756#[test]
1757fn test_mul_consistency() {
1758 let a = Fr([
1759 0x21e6_1211_d993_4f2e,
1760 0xa52c_058a_693c_3e07,
1761 0x9ccb_77bf_b12d_6360,
1762 0x07df_2470_ec94_398e,
1763 ]);
1764 let b = Fr([
1765 0x0333_6d1c_be19_dbe0,
1766 0x0153_618f_6156_a536,
1767 0x2604_c9e1_fc3c_6b15,
1768 0x04ae_581c_eb02_8720,
1769 ]);
1770 let c = Fr([
1771 0xd7ab_f5bb_2468_3f4c,
1772 0x9d77_12cc_274b_7c03,
1773 0x9732_93db_9683_789f,
1774 0x0b67_7e29_380a_97a7,
1775 ]);
1776 assert_eq!(a * b, c);
1777 let p = ExtendedPoint::from(AffinePoint {
1778 u: Fq::from_raw([
1779 0x81c5_71e5_d883_cfb0,
1780 0x049f_7a68_6f14_7029,
1781 0xf539_c860_bc3e_a21f,
1782 0x4284_715b_7ccc_8162,
1783 ]),
1784 v: Fq::from_raw([
1785 0xbf09_6275_684b_b8ca,
1786 0xc7ba_2458_90af_256d,
1787 0x5911_9f3e_8638_0eb0,
1788 0x3793_de18_2f9f_b1d2,
1789 ]),
1790 })
1791 .mul_by_cofactor();
1792 assert_eq!(p * c, (p * a) * b);
1793
1794 assert_eq!(p * c, (p.to_niels() * a) * b);
1796 assert_eq!(p.to_niels() * c, (p * a) * b);
1797 assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b);
1798
1799 let p_affine_niels = AffinePoint::from(p).to_niels();
1801 assert_eq!(p * c, (p_affine_niels * a) * b);
1802 assert_eq!(p_affine_niels * c, (p * a) * b);
1803 assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b);
1804}
1805
1806#[test]
1807fn test_serialization_consistency() {
1808 let gen = FULL_GENERATOR.mul_by_cofactor();
1809 let mut p = gen;
1810
1811 let v = vec![
1812 [
1813 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97,
1814 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215,
1815 ],
1816 [
1817 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195,
1818 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177,
1819 ],
1820 [
1821 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82,
1822 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199,
1823 ],
1824 [
1825 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83,
1826 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0,
1827 ],
1828 [
1829 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4,
1830 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185,
1831 ],
1832 [
1833 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166,
1834 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140,
1835 ],
1836 [
1837 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33,
1838 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24,
1839 ],
1840 [
1841 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211,
1842 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138,
1843 ],
1844 [
1845 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93,
1846 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203,
1847 ],
1848 [
1849 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14,
1850 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68,
1851 ],
1852 [
1853 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122,
1854 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97,
1855 ],
1856 [
1857 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150,
1858 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67,
1859 ],
1860 [
1861 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166,
1862 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10,
1863 ],
1864 [
1865 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161,
1866 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178,
1867 ],
1868 [
1869 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73,
1870 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159,
1871 ],
1872 [
1873 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0,
1874 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88,
1875 ],
1876 ];
1877
1878 let batched = AffinePoint::batch_from_bytes(v.iter().cloned());
1879
1880 for (expected_serialized, batch_deserialized) in v.into_iter().zip(batched.into_iter()) {
1881 assert!(p.is_on_curve_vartime());
1882 let affine = AffinePoint::from(p);
1883 let serialized = affine.to_bytes();
1884 let deserialized = AffinePoint::from_bytes(serialized).unwrap();
1885 assert_eq!(affine, deserialized);
1886 assert_eq!(affine, batch_deserialized.unwrap());
1887 assert_eq!(expected_serialized, serialized);
1888 p += gen;
1889 }
1890}
1891
1892#[test]
1893fn test_zip_216() {
1894 const NON_CANONICAL_ENCODINGS: [[u8; 32]; 2] = [
1895 [
1897 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1899 0x00, 0x00, 0x00, 0x80,
1900 ],
1901 [
1903 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4,
1904 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29,
1905 0x53, 0xa7, 0xed, 0xf3,
1906 ],
1907 ];
1908
1909 for b in &NON_CANONICAL_ENCODINGS {
1910 {
1911 let mut encoding = *b;
1912
1913 assert!(bool::from(AffinePoint::from_bytes(encoding).is_none()));
1915
1916 encoding[31] &= 0b0111_1111;
1919 assert!(bool::from(AffinePoint::from_bytes(encoding).is_some()));
1920 }
1921
1922 {
1923 let parsed = AffinePoint::from_bytes_pre_zip216_compatibility(*b).unwrap();
1926 let mut encoded = parsed.to_bytes();
1927 assert_ne!(b, &encoded);
1928
1929 encoded[31] |= 0b1000_0000;
1932 assert_eq!(b, &encoded);
1933 }
1934 }
1935}