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
409const EDWARDS_D: Fq = Fq::from_raw([
411 0x0106_5fd6_d634_3eb1,
412 0x292d_7f6d_3757_9d26,
413 0xf5fd_9207_e6bd_7fd4,
414 0x2a93_18e7_4bfa_2b48,
415]);
416
417const EDWARDS_D2: Fq = Fq::from_raw([
419 0x020c_bfad_ac68_7d62,
420 0x525a_feda_6eaf_3a4c,
421 0xebfb_240f_cd7a_ffa8,
422 0x5526_31ce_97f4_5691,
423]);
424
425impl AffinePoint {
426 pub const fn identity() -> Self {
428 AffinePoint {
429 u: Fq::zero(),
430 v: Fq::one(),
431 }
432 }
433
434 pub fn is_identity(&self) -> Choice {
436 ExtendedPoint::from(*self).is_identity()
437 }
438
439 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
442 ExtendedPoint::from(*self).mul_by_cofactor()
443 }
444
445 pub fn is_small_order(&self) -> Choice {
447 ExtendedPoint::from(*self).is_small_order()
448 }
449
450 pub fn is_torsion_free(&self) -> Choice {
453 ExtendedPoint::from(*self).is_torsion_free()
454 }
455
456 pub fn is_prime_order(&self) -> Choice {
461 let extended = ExtendedPoint::from(*self);
462 extended.is_torsion_free() & (!extended.is_identity())
463 }
464
465 pub fn to_bytes(&self) -> [u8; 32] {
467 let mut tmp = self.v.to_bytes();
468 let u = self.u.to_bytes();
469
470 tmp[31] |= u[0] << 7;
473
474 tmp
475 }
476
477 pub fn from_bytes(b: [u8; 32]) -> CtOption<Self> {
481 Self::from_bytes_inner(b, 1.into())
482 }
483
484 pub fn from_bytes_pre_zip216_compatibility(b: [u8; 32]) -> CtOption<Self> {
500 Self::from_bytes_inner(b, 0.into())
501 }
502
503 fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption<Self> {
504 let sign = b[31] >> 7;
506
507 b[31] &= 0b0111_1111;
509
510 Fq::from_bytes(&b).and_then(|v| {
512 let v2 = v.square();
524
525 ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
526 .sqrt()
527 .and_then(|u| {
528 let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
530 let u_negated = -u;
531 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
532
533 let u_is_zero = u.ct_eq(&Fq::zero());
539 CtOption::new(
540 AffinePoint { u: final_u, v },
541 !(zip_216_enabled & u_is_zero & flip_sign),
542 )
543 })
544 })
545 }
546
547 #[cfg(feature = "alloc")]
552 pub fn batch_from_bytes(items: impl Iterator<Item = [u8; 32]>) -> Vec<CtOption<Self>> {
553 use ff::BatchInvert;
554
555 #[derive(Clone, Copy, Default)]
556 struct Item {
557 sign: u8,
558 v: Fq,
559 numerator: Fq,
560 denominator: Fq,
561 }
562
563 impl ConditionallySelectable for Item {
564 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
565 Item {
566 sign: u8::conditional_select(&a.sign, &b.sign, choice),
567 v: Fq::conditional_select(&a.v, &b.v, choice),
568 numerator: Fq::conditional_select(&a.numerator, &b.numerator, choice),
569 denominator: Fq::conditional_select(&a.denominator, &b.denominator, choice),
570 }
571 }
572 }
573
574 let items: Vec<_> = items
575 .map(|mut b| {
576 let sign = b[31] >> 7;
578
579 b[31] &= 0b0111_1111;
581
582 Fq::from_bytes(&b).map(|v| {
584 let v2 = v.square();
596
597 Item {
598 v,
599 sign,
600 numerator: (v2 - Fq::one()),
601 denominator: Fq::one() + EDWARDS_D * v2,
602 }
603 })
604 })
605 .collect();
606
607 let mut denominators: Vec<_> = items
608 .iter()
609 .map(|item| item.map(|item| item.denominator).unwrap_or(Fq::zero()))
610 .collect();
611 denominators.iter_mut().batch_invert();
612
613 items
614 .into_iter()
615 .zip(denominators)
616 .map(|(item, inv_denominator)| {
617 item.and_then(
618 |Item {
619 v, sign, numerator, ..
620 }| {
621 (numerator * inv_denominator).sqrt().and_then(|u| {
622 let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1);
624 let u_negated = -u;
625 let final_u = Fq::conditional_select(&u, &u_negated, flip_sign);
626
627 let u_is_zero = u.ct_eq(&Fq::zero());
632 CtOption::new(AffinePoint { u: final_u, v }, !(u_is_zero & flip_sign))
633 })
634 },
635 )
636 })
637 .collect()
638 }
639
640 pub fn get_u(&self) -> Fq {
642 self.u
643 }
644
645 pub fn get_v(&self) -> Fq {
647 self.v
648 }
649
650 pub const fn to_extended(&self) -> ExtendedPoint {
652 ExtendedPoint {
653 u: self.u,
654 v: self.v,
655 z: Fq::one(),
656 t1: self.u,
657 t2: self.v,
658 }
659 }
660
661 pub const fn to_niels(&self) -> AffineNielsPoint {
664 AffineNielsPoint {
665 v_plus_u: Fq::add(&self.v, &self.u),
666 v_minus_u: Fq::sub(&self.v, &self.u),
667 t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2),
668 }
669 }
670
671 pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint {
674 AffinePoint { u, v }
675 }
676
677 #[cfg(test)]
681 fn is_on_curve_vartime(&self) -> bool {
682 let u2 = self.u.square();
683 let v2 = self.v.square();
684
685 v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2
686 }
687}
688
689impl ExtendedPoint {
690 pub const fn identity() -> Self {
692 ExtendedPoint {
693 u: Fq::zero(),
694 v: Fq::one(),
695 z: Fq::one(),
696 t1: Fq::zero(),
697 t2: Fq::zero(),
698 }
699 }
700
701 pub fn is_identity(&self) -> Choice {
703 self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z)
707 }
708
709 pub fn is_small_order(&self) -> Choice {
711 self.double().double().u.ct_eq(&Fq::zero())
716 }
717
718 pub fn is_torsion_free(&self) -> Choice {
721 self.multiply(&FR_MODULUS_BYTES).is_identity()
722 }
723
724 pub fn is_prime_order(&self) -> Choice {
729 self.is_torsion_free() & (!self.is_identity())
730 }
731
732 pub fn mul_by_cofactor(&self) -> ExtendedPoint {
734 self.double().double().double()
735 }
736
737 pub fn to_niels(&self) -> ExtendedNielsPoint {
740 ExtendedNielsPoint {
741 v_plus_u: self.v + self.u,
742 v_minus_u: self.v - self.u,
743 z: self.z,
744 t2d: self.t1 * self.t2 * EDWARDS_D2,
745 }
746 }
747
748 pub fn double(&self) -> ExtendedPoint {
751 let uu = self.u.square();
824 let vv = self.v.square();
825 let zz2 = self.z.square().double();
826 let uv2 = (self.u + self.v).square();
827 let vv_plus_uu = vv + uu;
828 let vv_minus_uu = vv - uu;
829
830 CompletedPoint {
833 u: uv2 - vv_plus_uu,
834 v: vv_plus_uu,
835 z: vv_minus_uu,
836 t: zz2 - vv_minus_uu,
837 }
838 .into_extended()
839 }
840
841 #[inline]
842 fn multiply(self, by: &[u8; 32]) -> Self {
843 self.to_niels().multiply(by)
844 }
845
846 fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) {
852 assert_eq!(p.len(), q.len());
853
854 for (p, q) in p.iter().zip(q.iter_mut()) {
855 q.u = p.z;
858 }
859
860 BatchInverter::invert_with_internal_scratch(q, |q| &mut q.u, |q| &mut q.v);
861
862 for (p, q) in p.iter().zip(q.iter_mut()).rev() {
863 let tmp = q.u;
864
865 q.u = p.u * tmp; q.v = p.v * tmp; }
869 }
870
871 #[cfg(test)]
875 fn is_on_curve_vartime(&self) -> bool {
876 let affine = AffinePoint::from(*self);
877
878 self.z != Fq::zero()
879 && affine.is_on_curve_vartime()
880 && (affine.u * affine.v * self.z == self.t1 * self.t2)
881 }
882}
883
884impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint {
885 type Output = ExtendedPoint;
886
887 fn mul(self, other: &'b Fr) -> ExtendedPoint {
888 self.multiply(&other.to_bytes())
889 }
890}
891
892impl_binops_multiplicative!(ExtendedPoint, Fr);
893
894impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
895 type Output = ExtendedPoint;
896
897 #[allow(clippy::suspicious_arithmetic_impl)]
898 fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
899 let a = (self.v - self.u) * other.v_minus_u;
917 let b = (self.v + self.u) * other.v_plus_u;
918 let c = self.t1 * self.t2 * other.t2d;
919 let d = (self.z * other.z).double();
920
921 CompletedPoint {
924 u: b - a,
925 v: b + a,
926 z: d + c,
927 t: d - c,
928 }
929 .into_extended()
930 }
931}
932
933impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint {
934 type Output = ExtendedPoint;
935
936 #[allow(clippy::suspicious_arithmetic_impl)]
937 fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint {
938 let a = (self.v - self.u) * other.v_plus_u;
939 let b = (self.v + self.u) * other.v_minus_u;
940 let c = self.t1 * self.t2 * other.t2d;
941 let d = (self.z * other.z).double();
942
943 CompletedPoint {
944 u: b - a,
945 v: b + a,
946 z: d - c,
947 t: d + c,
948 }
949 .into_extended()
950 }
951}
952
953impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint);
954
955impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint {
956 type Output = ExtendedPoint;
957
958 #[allow(clippy::suspicious_arithmetic_impl)]
959 fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
960 let a = (self.v - self.u) * other.v_minus_u;
965 let b = (self.v + self.u) * other.v_plus_u;
966 let c = self.t1 * self.t2 * other.t2d;
967 let d = self.z.double();
968
969 CompletedPoint {
972 u: b - a,
973 v: b + a,
974 z: d + c,
975 t: d - c,
976 }
977 .into_extended()
978 }
979}
980
981impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint {
982 type Output = ExtendedPoint;
983
984 #[allow(clippy::suspicious_arithmetic_impl)]
985 fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint {
986 let a = (self.v - self.u) * other.v_plus_u;
987 let b = (self.v + self.u) * other.v_minus_u;
988 let c = self.t1 * self.t2 * other.t2d;
989 let d = self.z.double();
990
991 CompletedPoint {
992 u: b - a,
993 v: b + a,
994 z: d - c,
995 t: d + c,
996 }
997 .into_extended()
998 }
999}
1000
1001impl_binops_additive!(ExtendedPoint, AffineNielsPoint);
1002
1003impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint {
1004 type Output = ExtendedPoint;
1005
1006 #[inline]
1007 fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint {
1008 self + other.to_niels()
1009 }
1010}
1011
1012impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint {
1013 type Output = ExtendedPoint;
1014
1015 #[inline]
1016 fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint {
1017 self - other.to_niels()
1018 }
1019}
1020
1021impl_binops_additive!(ExtendedPoint, ExtendedPoint);
1022
1023impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint {
1024 type Output = ExtendedPoint;
1025
1026 #[inline]
1027 fn add(self, other: &'b AffinePoint) -> ExtendedPoint {
1028 self + other.to_niels()
1029 }
1030}
1031
1032impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint {
1033 type Output = ExtendedPoint;
1034
1035 #[inline]
1036 fn sub(self, other: &'b AffinePoint) -> ExtendedPoint {
1037 self - other.to_niels()
1038 }
1039}
1040
1041impl_binops_additive!(ExtendedPoint, AffinePoint);
1042
1043struct CompletedPoint {
1048 u: Fq,
1049 v: Fq,
1050 z: Fq,
1051 t: Fq,
1052}
1053
1054impl CompletedPoint {
1055 #[inline]
1063 fn into_extended(self) -> ExtendedPoint {
1064 ExtendedPoint {
1065 u: self.u * self.t,
1066 v: self.v * self.z,
1067 z: self.z * self.t,
1068 t1: self.u,
1069 t2: self.v,
1070 }
1071 }
1072}
1073
1074impl Default for AffinePoint {
1075 fn default() -> AffinePoint {
1077 AffinePoint::identity()
1078 }
1079}
1080
1081impl Default for ExtendedPoint {
1082 fn default() -> ExtendedPoint {
1084 ExtendedPoint::identity()
1085 }
1086}
1087
1088pub fn batch_normalize(v: &mut [ExtendedPoint]) -> impl Iterator<Item = AffinePoint> + '_ {
1096 BatchInverter::invert_with_internal_scratch(v, |p| &mut p.z, |p| &mut p.t1);
1098
1099 for p in v.iter_mut() {
1100 let mut q = *p;
1101 let tmp = q.z;
1102
1103 q.u *= &tmp; q.v *= &tmp; q.z = Fq::one(); q.t1 = q.u;
1108 q.t2 = q.v;
1109
1110 *p = q;
1111 }
1112
1113 v.iter().map(|p| AffinePoint { u: p.u, v: p.v })
1118}
1119
1120impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint {
1121 type Output = ExtendedPoint;
1122
1123 fn mul(self, other: &'b Fr) -> ExtendedPoint {
1124 self.to_niels().multiply(&other.to_bytes())
1125 }
1126}
1127
1128impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint);
1129
1130#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1133#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1134pub struct SubgroupPoint(ExtendedPoint);
1135
1136impl From<SubgroupPoint> for ExtendedPoint {
1137 fn from(val: SubgroupPoint) -> ExtendedPoint {
1138 val.0
1139 }
1140}
1141
1142impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint {
1143 fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint {
1144 &val.0
1145 }
1146}
1147
1148impl fmt::Display for SubgroupPoint {
1149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1150 write!(f, "{}", self.0)
1151 }
1152}
1153
1154impl ConditionallySelectable for SubgroupPoint {
1155 fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
1156 SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice))
1157 }
1158}
1159
1160impl SubgroupPoint {
1161 pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self {
1169 SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended())
1170 }
1171}
1172
1173impl<T> Sum<T> for SubgroupPoint
1174where
1175 T: Borrow<SubgroupPoint>,
1176{
1177 fn sum<I>(iter: I) -> Self
1178 where
1179 I: Iterator<Item = T>,
1180 {
1181 iter.fold(Self::identity(), |acc, item| acc + item.borrow())
1182 }
1183}
1184
1185impl Neg for SubgroupPoint {
1186 type Output = SubgroupPoint;
1187
1188 #[inline]
1189 fn neg(self) -> SubgroupPoint {
1190 SubgroupPoint(-self.0)
1191 }
1192}
1193
1194impl Neg for &SubgroupPoint {
1195 type Output = SubgroupPoint;
1196
1197 #[inline]
1198 fn neg(self) -> SubgroupPoint {
1199 SubgroupPoint(-self.0)
1200 }
1201}
1202
1203impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint {
1204 type Output = ExtendedPoint;
1205
1206 #[inline]
1207 fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1208 self + other.0
1209 }
1210}
1211
1212impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint {
1213 type Output = ExtendedPoint;
1214
1215 #[inline]
1216 fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint {
1217 self - other.0
1218 }
1219}
1220
1221impl_binops_additive!(ExtendedPoint, SubgroupPoint);
1222
1223impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint {
1224 type Output = SubgroupPoint;
1225
1226 #[inline]
1227 fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1228 SubgroupPoint(self.0 + other.0)
1229 }
1230}
1231
1232impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint {
1233 type Output = SubgroupPoint;
1234
1235 #[inline]
1236 fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint {
1237 SubgroupPoint(self.0 - other.0)
1238 }
1239}
1240
1241impl_binops_additive!(SubgroupPoint, SubgroupPoint);
1242
1243impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint {
1244 type Output = SubgroupPoint;
1245
1246 fn mul(self, other: &'b Fr) -> SubgroupPoint {
1247 SubgroupPoint(self.0.multiply(&other.to_bytes()))
1248 }
1249}
1250
1251impl_binops_multiplicative!(SubgroupPoint, Fr);
1252
1253impl Group for ExtendedPoint {
1254 type Scalar = Fr;
1255
1256 fn random(mut rng: impl RngCore) -> Self {
1257 loop {
1258 let v = Fq::random(&mut rng);
1259 let flip_sign = rng.next_u32() % 2 != 0;
1260
1261 let v2 = v.square();
1263 let p = ((v2 - Fq::one())
1264 * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero())))
1265 .sqrt()
1266 .map(|u| AffinePoint {
1267 u: if flip_sign { -u } else { u },
1268 v,
1269 });
1270
1271 if p.is_some().into() {
1272 let p = p.unwrap().to_curve();
1273
1274 if bool::from(!p.is_identity()) {
1275 return p;
1276 }
1277 }
1278 }
1279 }
1280
1281 fn identity() -> Self {
1282 Self::identity()
1283 }
1284
1285 fn generator() -> Self {
1286 AffinePoint::generator().into()
1287 }
1288
1289 fn is_identity(&self) -> Choice {
1290 self.is_identity()
1291 }
1292
1293 #[must_use]
1294 fn double(&self) -> Self {
1295 self.double()
1296 }
1297}
1298
1299impl Group for SubgroupPoint {
1300 type Scalar = Fr;
1301
1302 fn random(mut rng: impl RngCore) -> Self {
1303 loop {
1304 let p = ExtendedPoint::random(&mut rng).clear_cofactor();
1305
1306 if bool::from(!p.is_identity()) {
1307 return p;
1308 }
1309 }
1310 }
1311
1312 fn identity() -> Self {
1313 SubgroupPoint(ExtendedPoint::identity())
1314 }
1315
1316 fn generator() -> Self {
1317 ExtendedPoint::generator().clear_cofactor()
1318 }
1319
1320 fn is_identity(&self) -> Choice {
1321 self.0.is_identity()
1322 }
1323
1324 #[must_use]
1325 fn double(&self) -> Self {
1326 SubgroupPoint(self.0.double())
1327 }
1328}
1329
1330#[cfg(feature = "alloc")]
1331impl WnafGroup for ExtendedPoint {
1332 fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize {
1333 const RECOMMENDATIONS: [usize; 12] =
1335 [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569];
1336
1337 let mut ret = 4;
1338 for r in &RECOMMENDATIONS {
1339 if num_scalars > *r {
1340 ret += 1;
1341 } else {
1342 break;
1343 }
1344 }
1345
1346 ret
1347 }
1348}
1349
1350impl PrimeGroup for SubgroupPoint {}
1351
1352impl CofactorGroup for ExtendedPoint {
1353 type Subgroup = SubgroupPoint;
1354
1355 fn clear_cofactor(&self) -> Self::Subgroup {
1356 SubgroupPoint(self.mul_by_cofactor())
1357 }
1358
1359 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1360 CtOption::new(SubgroupPoint(self), self.is_torsion_free())
1361 }
1362
1363 fn is_torsion_free(&self) -> Choice {
1364 self.is_torsion_free()
1365 }
1366}
1367
1368impl Curve for ExtendedPoint {
1369 type AffineRepr = AffinePoint;
1370
1371 fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) {
1372 Self::batch_normalize(p, q);
1373 }
1374
1375 fn to_affine(&self) -> Self::AffineRepr {
1376 self.into()
1377 }
1378}
1379
1380impl CofactorCurve for ExtendedPoint {
1381 type Affine = AffinePoint;
1382}
1383
1384impl CofactorCurveAffine for AffinePoint {
1385 type Scalar = Fr;
1386 type Curve = ExtendedPoint;
1387
1388 fn identity() -> Self {
1389 Self::identity()
1390 }
1391
1392 fn generator() -> Self {
1393 AffinePoint {
1395 u: Fq::from_raw([
1396 0xe4b3_d35d_f1a7_adfe,
1397 0xcaf5_5d1b_29bf_81af,
1398 0x8b0f_03dd_d60a_8187,
1399 0x62ed_cbb8_bf37_87c8,
1400 ]),
1401 v: Fq::from_raw([
1402 0x0000_0000_0000_000b,
1403 0x0000_0000_0000_0000,
1404 0x0000_0000_0000_0000,
1405 0x0000_0000_0000_0000,
1406 ]),
1407 }
1408 }
1409
1410 fn is_identity(&self) -> Choice {
1411 self.is_identity()
1412 }
1413
1414 fn to_curve(&self) -> Self::Curve {
1415 (*self).into()
1416 }
1417}
1418
1419impl GroupEncoding for ExtendedPoint {
1420 type Repr = [u8; 32];
1421
1422 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1423 AffinePoint::from_bytes(*bytes).map(Self::from)
1424 }
1425
1426 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1427 AffinePoint::from_bytes(*bytes).map(Self::from)
1429 }
1430
1431 fn to_bytes(&self) -> Self::Repr {
1432 AffinePoint::from(self).to_bytes()
1433 }
1434}
1435
1436impl GroupEncoding for SubgroupPoint {
1437 type Repr = [u8; 32];
1438
1439 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1440 ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1441 }
1442
1443 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1444 ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint)
1445 }
1446
1447 fn to_bytes(&self) -> Self::Repr {
1448 self.0.to_bytes()
1449 }
1450}
1451
1452impl GroupEncoding for AffinePoint {
1453 type Repr = [u8; 32];
1454
1455 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1456 Self::from_bytes(*bytes)
1457 }
1458
1459 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1460 Self::from_bytes(*bytes)
1461 }
1462
1463 fn to_bytes(&self) -> Self::Repr {
1464 self.to_bytes()
1465 }
1466}
1467
1468#[test]
1469fn test_is_on_curve_var() {
1470 assert!(AffinePoint::identity().is_on_curve_vartime());
1471}
1472
1473#[test]
1474fn test_d_is_non_quadratic_residue() {
1475 assert!(bool::from(EDWARDS_D.sqrt().is_none()));
1476 assert!(bool::from((-EDWARDS_D).sqrt().is_none()));
1477 assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none()));
1478}
1479
1480#[test]
1481fn test_affine_niels_point_identity() {
1482 assert_eq!(
1483 AffineNielsPoint::identity().v_plus_u,
1484 AffinePoint::identity().to_niels().v_plus_u
1485 );
1486 assert_eq!(
1487 AffineNielsPoint::identity().v_minus_u,
1488 AffinePoint::identity().to_niels().v_minus_u
1489 );
1490 assert_eq!(
1491 AffineNielsPoint::identity().t2d,
1492 AffinePoint::identity().to_niels().t2d
1493 );
1494}
1495
1496#[test]
1497fn test_extended_niels_point_identity() {
1498 assert_eq!(
1499 ExtendedNielsPoint::identity().v_plus_u,
1500 ExtendedPoint::identity().to_niels().v_plus_u
1501 );
1502 assert_eq!(
1503 ExtendedNielsPoint::identity().v_minus_u,
1504 ExtendedPoint::identity().to_niels().v_minus_u
1505 );
1506 assert_eq!(
1507 ExtendedNielsPoint::identity().z,
1508 ExtendedPoint::identity().to_niels().z
1509 );
1510 assert_eq!(
1511 ExtendedNielsPoint::identity().t2d,
1512 ExtendedPoint::identity().to_niels().t2d
1513 );
1514}
1515
1516#[test]
1517fn test_assoc() {
1518 let p = ExtendedPoint::from(AffinePoint {
1519 u: Fq::from_raw([
1520 0x81c5_71e5_d883_cfb0,
1521 0x049f_7a68_6f14_7029,
1522 0xf539_c860_bc3e_a21f,
1523 0x4284_715b_7ccc_8162,
1524 ]),
1525 v: Fq::from_raw([
1526 0xbf09_6275_684b_b8ca,
1527 0xc7ba_2458_90af_256d,
1528 0x5911_9f3e_8638_0eb0,
1529 0x3793_de18_2f9f_b1d2,
1530 ]),
1531 })
1532 .mul_by_cofactor();
1533 assert!(p.is_on_curve_vartime());
1534
1535 assert_eq!(
1536 (p * Fr::from(1000u64)) * Fr::from(3938u64),
1537 p * (Fr::from(1000u64) * Fr::from(3938u64)),
1538 );
1539}
1540
1541#[test]
1542fn test_batch_normalize() {
1543 let mut p = ExtendedPoint::from(AffinePoint {
1544 u: Fq::from_raw([
1545 0x81c5_71e5_d883_cfb0,
1546 0x049f_7a68_6f14_7029,
1547 0xf539_c860_bc3e_a21f,
1548 0x4284_715b_7ccc_8162,
1549 ]),
1550 v: Fq::from_raw([
1551 0xbf09_6275_684b_b8ca,
1552 0xc7ba_2458_90af_256d,
1553 0x5911_9f3e_8638_0eb0,
1554 0x3793_de18_2f9f_b1d2,
1555 ]),
1556 })
1557 .mul_by_cofactor();
1558
1559 let mut v = vec![];
1560 for _ in 0..10 {
1561 v.push(p);
1562 p = p.double();
1563 }
1564
1565 for p in &v {
1566 assert!(p.is_on_curve_vartime());
1567 }
1568
1569 let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect();
1570 let mut result0 = vec![AffinePoint::identity(); v.len()];
1571 ExtendedPoint::batch_normalize(&v, &mut result0);
1572 for i in 0..10 {
1573 assert!(expected[i] == result0[i]);
1574 }
1575 let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1576 for i in 0..10 {
1577 assert!(expected[i] == result1[i]);
1578 assert!(v[i].is_on_curve_vartime());
1579 assert!(AffinePoint::from(v[i]) == expected[i]);
1580 }
1581 let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect();
1582 for i in 0..10 {
1583 assert!(expected[i] == result2[i]);
1584 assert!(v[i].is_on_curve_vartime());
1585 assert!(AffinePoint::from(v[i]) == expected[i]);
1586 }
1587}
1588
1589#[cfg(test)]
1590const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked(
1591 Fq::from_raw([
1592 0xe4b3_d35d_f1a7_adfe,
1593 0xcaf5_5d1b_29bf_81af,
1594 0x8b0f_03dd_d60a_8187,
1595 0x62ed_cbb8_bf37_87c8,
1596 ]),
1597 Fq::from_raw([0xb, 0x0, 0x0, 0x0]),
1598);
1599
1600#[cfg(test)]
1601const EIGHT_TORSION: [AffinePoint; 8] = [
1602 AffinePoint::from_raw_unchecked(
1603 Fq::from_raw([
1604 0xd92e_6a79_2720_0d43,
1605 0x7aa4_1ac4_3dae_8582,
1606 0xeaaa_e086_a166_18d1,
1607 0x71d4_df38_ba9e_7973,
1608 ]),
1609 Fq::from_raw([
1610 0xff0d_2068_eff4_96dd,
1611 0x9106_ee90_f384_a4a1,
1612 0x16a1_3035_ad4d_7266,
1613 0x4958_bdb2_1966_982e,
1614 ]),
1615 ),
1616 AffinePoint::from_raw_unchecked(
1617 Fq::from_raw([
1618 0xfffe_ffff_0000_0001,
1619 0x67ba_a400_89fb_5bfe,
1620 0xa5e8_0b39_939e_d334,
1621 0x73ed_a753_299d_7d47,
1622 ]),
1623 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1624 ),
1625 AffinePoint::from_raw_unchecked(
1626 Fq::from_raw([
1627 0xd92e_6a79_2720_0d43,
1628 0x7aa4_1ac4_3dae_8582,
1629 0xeaaa_e086_a166_18d1,
1630 0x71d4_df38_ba9e_7973,
1631 ]),
1632 Fq::from_raw([
1633 0x00f2_df96_100b_6924,
1634 0xc2b6_b572_0c79_b75d,
1635 0x1c98_a7d2_5c54_659e,
1636 0x2a94_e9a1_1036_e51a,
1637 ]),
1638 ),
1639 AffinePoint::from_raw_unchecked(
1640 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1641 Fq::from_raw([
1642 0xffff_ffff_0000_0000,
1643 0x53bd_a402_fffe_5bfe,
1644 0x3339_d808_09a1_d805,
1645 0x73ed_a753_299d_7d48,
1646 ]),
1647 ),
1648 AffinePoint::from_raw_unchecked(
1649 Fq::from_raw([
1650 0x26d1_9585_d8df_f2be,
1651 0xd919_893e_c24f_d67c,
1652 0x488e_f781_683b_bf33,
1653 0x0218_c81a_6eff_03d4,
1654 ]),
1655 Fq::from_raw([
1656 0x00f2_df96_100b_6924,
1657 0xc2b6_b572_0c79_b75d,
1658 0x1c98_a7d2_5c54_659e,
1659 0x2a94_e9a1_1036_e51a,
1660 ]),
1661 ),
1662 AffinePoint::from_raw_unchecked(
1663 Fq::from_raw([
1664 0x0001_0000_0000_0000,
1665 0xec03_0002_7603_0000,
1666 0x8d51_ccce_7603_04d0,
1667 0x0,
1668 ]),
1669 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1670 ),
1671 AffinePoint::from_raw_unchecked(
1672 Fq::from_raw([
1673 0x26d1_9585_d8df_f2be,
1674 0xd919_893e_c24f_d67c,
1675 0x488e_f781_683b_bf33,
1676 0x0218_c81a_6eff_03d4,
1677 ]),
1678 Fq::from_raw([
1679 0xff0d_2068_eff4_96dd,
1680 0x9106_ee90_f384_a4a1,
1681 0x16a1_3035_ad4d_7266,
1682 0x4958_bdb2_1966_982e,
1683 ]),
1684 ),
1685 AffinePoint::from_raw_unchecked(
1686 Fq::from_raw([0x0, 0x0, 0x0, 0x0]),
1687 Fq::from_raw([0x1, 0x0, 0x0, 0x0]),
1688 ),
1689];
1690
1691#[test]
1692fn find_eight_torsion() {
1693 let g = ExtendedPoint::from(FULL_GENERATOR);
1694 assert!(!bool::from(g.is_small_order()));
1695 let g = g.multiply(&FR_MODULUS_BYTES);
1696 assert!(bool::from(g.is_small_order()));
1697
1698 let mut cur = g;
1699
1700 for (i, point) in EIGHT_TORSION.iter().enumerate() {
1701 let tmp = AffinePoint::from(cur);
1702 if &tmp != point {
1703 panic!("{}th torsion point should be {:?}", i, tmp);
1704 }
1705
1706 cur += &g;
1707 }
1708}
1709
1710#[test]
1711fn find_curve_generator() {
1712 let mut trial_bytes = [0; 32];
1713 for _ in 0..255 {
1714 let a = AffinePoint::from_bytes(trial_bytes);
1715 if bool::from(a.is_some()) {
1716 let a = a.unwrap();
1717 assert!(a.is_on_curve_vartime());
1718 let b = ExtendedPoint::from(a);
1719 let b = b.multiply(&FR_MODULUS_BYTES);
1720 assert!(bool::from(b.is_small_order()));
1721 let b = b.double();
1722 assert!(bool::from(b.is_small_order()));
1723 let b = b.double();
1724 assert!(bool::from(b.is_small_order()));
1725 if !bool::from(b.is_identity()) {
1726 let b = b.double();
1727 assert!(bool::from(b.is_small_order()));
1728 assert!(bool::from(b.is_identity()));
1729 assert_eq!(FULL_GENERATOR, a);
1730 assert_eq!(AffinePoint::generator(), a);
1731 assert!(bool::from(a.mul_by_cofactor().is_torsion_free()));
1732 return;
1733 }
1734 }
1735
1736 trial_bytes[0] += 1;
1737 }
1738
1739 panic!("should have found a generator of the curve");
1740}
1741
1742#[test]
1743fn test_small_order() {
1744 for point in EIGHT_TORSION.iter() {
1745 assert!(bool::from(point.is_small_order()));
1746 }
1747}
1748
1749#[test]
1750fn test_is_identity() {
1751 let a = EIGHT_TORSION[0].mul_by_cofactor();
1752 let b = EIGHT_TORSION[1].mul_by_cofactor();
1753
1754 assert_eq!(a.u, b.u);
1755 assert_eq!(a.v, a.z);
1756 assert_eq!(b.v, b.z);
1757 assert!(a.v != b.v);
1758 assert!(a.z != b.z);
1759
1760 assert!(bool::from(a.is_identity()));
1761 assert!(bool::from(b.is_identity()));
1762
1763 for point in EIGHT_TORSION.iter() {
1764 assert!(bool::from(point.mul_by_cofactor().is_identity()));
1765 }
1766}
1767
1768#[test]
1769fn test_mul_consistency() {
1770 let a = Fr([
1771 0x21e6_1211_d993_4f2e,
1772 0xa52c_058a_693c_3e07,
1773 0x9ccb_77bf_b12d_6360,
1774 0x07df_2470_ec94_398e,
1775 ]);
1776 let b = Fr([
1777 0x0333_6d1c_be19_dbe0,
1778 0x0153_618f_6156_a536,
1779 0x2604_c9e1_fc3c_6b15,
1780 0x04ae_581c_eb02_8720,
1781 ]);
1782 let c = Fr([
1783 0xd7ab_f5bb_2468_3f4c,
1784 0x9d77_12cc_274b_7c03,
1785 0x9732_93db_9683_789f,
1786 0x0b67_7e29_380a_97a7,
1787 ]);
1788 assert_eq!(a * b, c);
1789 let p = ExtendedPoint::from(AffinePoint {
1790 u: Fq::from_raw([
1791 0x81c5_71e5_d883_cfb0,
1792 0x049f_7a68_6f14_7029,
1793 0xf539_c860_bc3e_a21f,
1794 0x4284_715b_7ccc_8162,
1795 ]),
1796 v: Fq::from_raw([
1797 0xbf09_6275_684b_b8ca,
1798 0xc7ba_2458_90af_256d,
1799 0x5911_9f3e_8638_0eb0,
1800 0x3793_de18_2f9f_b1d2,
1801 ]),
1802 })
1803 .mul_by_cofactor();
1804 assert_eq!(p * c, (p * a) * b);
1805
1806 assert_eq!(p * c, (p.to_niels() * a) * b);
1808 assert_eq!(p.to_niels() * c, (p * a) * b);
1809 assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b);
1810
1811 let p_affine_niels = AffinePoint::from(p).to_niels();
1813 assert_eq!(p * c, (p_affine_niels * a) * b);
1814 assert_eq!(p_affine_niels * c, (p * a) * b);
1815 assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b);
1816}
1817
1818#[test]
1819fn test_serialization_consistency() {
1820 let gen = FULL_GENERATOR.mul_by_cofactor();
1821 let mut p = gen;
1822
1823 let v = vec![
1824 [
1825 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97,
1826 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215,
1827 ],
1828 [
1829 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195,
1830 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177,
1831 ],
1832 [
1833 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82,
1834 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199,
1835 ],
1836 [
1837 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83,
1838 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0,
1839 ],
1840 [
1841 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4,
1842 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185,
1843 ],
1844 [
1845 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166,
1846 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140,
1847 ],
1848 [
1849 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33,
1850 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24,
1851 ],
1852 [
1853 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211,
1854 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138,
1855 ],
1856 [
1857 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93,
1858 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203,
1859 ],
1860 [
1861 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14,
1862 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68,
1863 ],
1864 [
1865 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122,
1866 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97,
1867 ],
1868 [
1869 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150,
1870 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67,
1871 ],
1872 [
1873 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166,
1874 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10,
1875 ],
1876 [
1877 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161,
1878 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178,
1879 ],
1880 [
1881 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73,
1882 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159,
1883 ],
1884 [
1885 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0,
1886 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88,
1887 ],
1888 ];
1889
1890 let batched = AffinePoint::batch_from_bytes(v.iter().cloned());
1891
1892 for (expected_serialized, batch_deserialized) in v.into_iter().zip(batched.into_iter()) {
1893 assert!(p.is_on_curve_vartime());
1894 let affine = AffinePoint::from(p);
1895 let serialized = affine.to_bytes();
1896 let deserialized = AffinePoint::from_bytes(serialized).unwrap();
1897 assert_eq!(affine, deserialized);
1898 assert_eq!(affine, batch_deserialized.unwrap());
1899 assert_eq!(expected_serialized, serialized);
1900 p += gen;
1901 }
1902}
1903
1904#[test]
1905fn test_zip_216() {
1906 const NON_CANONICAL_ENCODINGS: [[u8; 32]; 2] = [
1907 [
1909 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1911 0x00, 0x00, 0x00, 0x80,
1912 ],
1913 [
1915 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4,
1916 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29,
1917 0x53, 0xa7, 0xed, 0xf3,
1918 ],
1919 ];
1920
1921 for b in &NON_CANONICAL_ENCODINGS {
1922 {
1923 let mut encoding = *b;
1924
1925 assert!(bool::from(AffinePoint::from_bytes(encoding).is_none()));
1927
1928 encoding[31] &= 0b0111_1111;
1931 assert!(bool::from(AffinePoint::from_bytes(encoding).is_some()));
1932 }
1933
1934 {
1935 let parsed = AffinePoint::from_bytes_pre_zip216_compatibility(*b).unwrap();
1938 let mut encoded = parsed.to_bytes();
1939 assert_ne!(b, &encoded);
1940
1941 encoded[31] |= 0b1000_0000;
1944 assert_eq!(b, &encoded);
1945 }
1946 }
1947}