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