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