1#![allow(non_snake_case)]
95
96use core::array::TryFromSliceError;
97use core::borrow::Borrow;
98use core::fmt::Debug;
99use core::iter::Sum;
100use core::ops::{Add, Neg, Sub};
101use core::ops::{AddAssign, SubAssign};
102use core::ops::{Mul, MulAssign};
103
104use cfg_if::cfg_if;
105
106#[cfg(feature = "digest")]
107use digest::{generic_array::typenum::U64, Digest};
108
109#[cfg(feature = "group")]
110use {
111 group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
112 subtle::CtOption,
113};
114
115#[cfg(feature = "group")]
116use rand_core::RngCore;
117
118use subtle::Choice;
119use subtle::ConditionallyNegatable;
120use subtle::ConditionallySelectable;
121use subtle::ConstantTimeEq;
122
123#[cfg(feature = "zeroize")]
124use zeroize::Zeroize;
125
126use crate::constants;
127
128use crate::field::FieldElement;
129use crate::scalar::{clamp_integer, Scalar};
130
131use crate::montgomery::MontgomeryPoint;
132
133use crate::backend::serial::curve_models::AffineNielsPoint;
134use crate::backend::serial::curve_models::CompletedPoint;
135use crate::backend::serial::curve_models::ProjectiveNielsPoint;
136use crate::backend::serial::curve_models::ProjectivePoint;
137
138#[cfg(feature = "precomputed-tables")]
139use crate::window::{
140 LookupTableRadix128, LookupTableRadix16, LookupTableRadix256, LookupTableRadix32,
141 LookupTableRadix64,
142};
143
144#[cfg(feature = "precomputed-tables")]
145use crate::traits::BasepointTable;
146
147use crate::traits::ValidityCheck;
148use crate::traits::{Identity, IsIdentity};
149
150#[cfg(feature = "alloc")]
151use crate::traits::MultiscalarMul;
152#[cfg(feature = "alloc")]
153use crate::traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
154
155#[derive(Copy, Clone, Eq, PartialEq, Hash)]
165pub struct CompressedEdwardsY(pub [u8; 32]);
166
167impl ConstantTimeEq for CompressedEdwardsY {
168 fn ct_eq(&self, other: &CompressedEdwardsY) -> Choice {
169 self.as_bytes().ct_eq(other.as_bytes())
170 }
171}
172
173impl Debug for CompressedEdwardsY {
174 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175 write!(f, "CompressedEdwardsY: {:?}", self.as_bytes())
176 }
177}
178
179impl CompressedEdwardsY {
180 pub const fn as_bytes(&self) -> &[u8; 32] {
182 &self.0
183 }
184
185 pub const fn to_bytes(&self) -> [u8; 32] {
187 self.0
188 }
189
190 pub fn decompress(&self) -> Option<EdwardsPoint> {
195 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
196
197 if is_valid_y_coord.into() {
198 Some(decompress::step_2(self, X, Y, Z))
199 } else {
200 None
201 }
202 }
203}
204
205mod decompress {
206 use super::*;
207
208 #[rustfmt::skip] pub(super) fn step_1(
210 repr: &CompressedEdwardsY,
211 ) -> (Choice, FieldElement, FieldElement, FieldElement) {
212 let Y = FieldElement::from_bytes(repr.as_bytes());
213 let Z = FieldElement::ONE;
214 let YY = Y.square();
215 let u = &YY - &Z; let v = &(&YY * &constants::EDWARDS_D) + &Z; let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v);
218
219 (is_valid_y_coord, X, Y, Z)
220 }
221
222 #[rustfmt::skip]
223 pub(super) fn step_2(
224 repr: &CompressedEdwardsY,
225 mut X: FieldElement,
226 Y: FieldElement,
227 Z: FieldElement,
228 ) -> EdwardsPoint {
229 let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7);
232 X.conditional_negate(compressed_sign_bit);
233
234 EdwardsPoint {
235 X,
236 Y,
237 Z,
238 T: &X * &Y,
239 }
240 }
241}
242
243impl TryFrom<&[u8]> for CompressedEdwardsY {
244 type Error = TryFromSliceError;
245
246 fn try_from(slice: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
247 Self::from_slice(slice)
248 }
249}
250
251#[cfg(feature = "serde")]
260use serde::de::Visitor;
261#[cfg(feature = "serde")]
262use serde::{Deserialize, Deserializer, Serialize, Serializer};
263
264#[cfg(feature = "serde")]
265impl Serialize for EdwardsPoint {
266 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
267 where
268 S: Serializer,
269 {
270 use serde::ser::SerializeTuple;
271 let mut tup = serializer.serialize_tuple(32)?;
272 for byte in self.compress().as_bytes().iter() {
273 tup.serialize_element(byte)?;
274 }
275 tup.end()
276 }
277}
278
279#[cfg(feature = "serde")]
280impl Serialize for CompressedEdwardsY {
281 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
282 where
283 S: Serializer,
284 {
285 use serde::ser::SerializeTuple;
286 let mut tup = serializer.serialize_tuple(32)?;
287 for byte in self.as_bytes().iter() {
288 tup.serialize_element(byte)?;
289 }
290 tup.end()
291 }
292}
293
294#[cfg(feature = "serde")]
295impl<'de> Deserialize<'de> for EdwardsPoint {
296 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
297 where
298 D: Deserializer<'de>,
299 {
300 struct EdwardsPointVisitor;
301
302 impl<'de> Visitor<'de> for EdwardsPointVisitor {
303 type Value = EdwardsPoint;
304
305 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
306 formatter.write_str("a valid point in Edwards y + sign format")
307 }
308
309 fn visit_seq<A>(self, mut seq: A) -> Result<EdwardsPoint, A::Error>
310 where
311 A: serde::de::SeqAccess<'de>,
312 {
313 let mut bytes = [0u8; 32];
314 #[allow(clippy::needless_range_loop)]
315 for i in 0..32 {
316 bytes[i] = seq
317 .next_element()?
318 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
319 }
320 CompressedEdwardsY(bytes)
321 .decompress()
322 .ok_or_else(|| serde::de::Error::custom("decompression failed"))
323 }
324 }
325
326 deserializer.deserialize_tuple(32, EdwardsPointVisitor)
327 }
328}
329
330#[cfg(feature = "serde")]
331impl<'de> Deserialize<'de> for CompressedEdwardsY {
332 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
333 where
334 D: Deserializer<'de>,
335 {
336 struct CompressedEdwardsYVisitor;
337
338 impl<'de> Visitor<'de> for CompressedEdwardsYVisitor {
339 type Value = CompressedEdwardsY;
340
341 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
342 formatter.write_str("32 bytes of data")
343 }
344
345 fn visit_seq<A>(self, mut seq: A) -> Result<CompressedEdwardsY, A::Error>
346 where
347 A: serde::de::SeqAccess<'de>,
348 {
349 let mut bytes = [0u8; 32];
350 #[allow(clippy::needless_range_loop)]
351 for i in 0..32 {
352 bytes[i] = seq
353 .next_element()?
354 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
355 }
356 Ok(CompressedEdwardsY(bytes))
357 }
358 }
359
360 deserializer.deserialize_tuple(32, CompressedEdwardsYVisitor)
361 }
362}
363
364#[derive(Copy, Clone)]
370#[allow(missing_docs)]
371pub struct EdwardsPoint {
372 pub(crate) X: FieldElement,
373 pub(crate) Y: FieldElement,
374 pub(crate) Z: FieldElement,
375 pub(crate) T: FieldElement,
376}
377
378impl Identity for CompressedEdwardsY {
383 fn identity() -> CompressedEdwardsY {
384 CompressedEdwardsY([
385 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
386 0, 0, 0,
387 ])
388 }
389}
390
391impl Default for CompressedEdwardsY {
392 fn default() -> CompressedEdwardsY {
393 CompressedEdwardsY::identity()
394 }
395}
396
397impl CompressedEdwardsY {
398 pub fn from_slice(bytes: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
405 bytes.try_into().map(CompressedEdwardsY)
406 }
407}
408
409impl Identity for EdwardsPoint {
410 fn identity() -> EdwardsPoint {
411 EdwardsPoint {
412 X: FieldElement::ZERO,
413 Y: FieldElement::ONE,
414 Z: FieldElement::ONE,
415 T: FieldElement::ZERO,
416 }
417 }
418}
419
420impl Default for EdwardsPoint {
421 fn default() -> EdwardsPoint {
422 EdwardsPoint::identity()
423 }
424}
425
426#[cfg(feature = "zeroize")]
431impl Zeroize for CompressedEdwardsY {
432 fn zeroize(&mut self) {
434 self.0.zeroize();
435 self.0[0] = 1;
436 }
437}
438
439#[cfg(feature = "zeroize")]
440impl Zeroize for EdwardsPoint {
441 fn zeroize(&mut self) {
443 self.X.zeroize();
444 self.Y = FieldElement::ONE;
445 self.Z = FieldElement::ONE;
446 self.T.zeroize();
447 }
448}
449
450impl ValidityCheck for EdwardsPoint {
455 fn is_valid(&self) -> bool {
456 let point_on_curve = self.as_projective().is_valid();
457 let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T);
458
459 point_on_curve && on_segre_image
460 }
461}
462
463impl ConditionallySelectable for EdwardsPoint {
468 fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint {
469 EdwardsPoint {
470 X: FieldElement::conditional_select(&a.X, &b.X, choice),
471 Y: FieldElement::conditional_select(&a.Y, &b.Y, choice),
472 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
473 T: FieldElement::conditional_select(&a.T, &b.T, choice),
474 }
475 }
476}
477
478impl ConstantTimeEq for EdwardsPoint {
483 fn ct_eq(&self, other: &EdwardsPoint) -> Choice {
484 (&self.X * &other.Z).ct_eq(&(&other.X * &self.Z))
491 & (&self.Y * &other.Z).ct_eq(&(&other.Y * &self.Z))
492 }
493}
494
495impl PartialEq for EdwardsPoint {
496 fn eq(&self, other: &EdwardsPoint) -> bool {
497 self.ct_eq(other).into()
498 }
499}
500
501impl Eq for EdwardsPoint {}
502
503impl EdwardsPoint {
508 pub(crate) fn as_projective_niels(&self) -> ProjectiveNielsPoint {
510 ProjectiveNielsPoint {
511 Y_plus_X: &self.Y + &self.X,
512 Y_minus_X: &self.Y - &self.X,
513 Z: self.Z,
514 T2d: &self.T * &constants::EDWARDS_D2,
515 }
516 }
517
518 pub(crate) const fn as_projective(&self) -> ProjectivePoint {
523 ProjectivePoint {
524 X: self.X,
525 Y: self.Y,
526 Z: self.Z,
527 }
528 }
529
530 pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint {
533 let recip = self.Z.invert();
534 let x = &self.X * &recip;
535 let y = &self.Y * &recip;
536 let xy2d = &(&x * &y) * &constants::EDWARDS_D2;
537 AffineNielsPoint {
538 y_plus_x: &y + &x,
539 y_minus_x: &y - &x,
540 xy2d,
541 }
542 }
543
544 pub fn to_montgomery(&self) -> MontgomeryPoint {
554 let U = &self.Z + &self.Y;
560 let W = &self.Z - &self.Y;
561 let u = &U * &W.invert();
562 MontgomeryPoint(u.as_bytes())
563 }
564
565 pub fn compress(&self) -> CompressedEdwardsY {
567 let recip = self.Z.invert();
568 let x = &self.X * &recip;
569 let y = &self.Y * &recip;
570 let mut s: [u8; 32];
571
572 s = y.as_bytes();
573 s[31] ^= x.is_negative().unwrap_u8() << 7;
574 CompressedEdwardsY(s)
575 }
576
577 #[cfg(feature = "digest")]
578 #[deprecated(
584 since = "4.0.0",
585 note = "previously named `hash_from_bytes`, this is not a secure hash function"
586 )]
587 pub fn nonspec_map_to_curve<D>(bytes: &[u8]) -> EdwardsPoint
588 where
589 D: Digest<OutputSize = U64> + Default,
590 {
591 let mut hash = D::new();
592 hash.update(bytes);
593 let h = hash.finalize();
594 let mut res = [0u8; 32];
595 res.copy_from_slice(&h[..32]);
596
597 let sign_bit = (res[31] & 0x80) >> 7;
598
599 let fe = FieldElement::from_bytes(&res);
600
601 let M1 = crate::montgomery::elligator_encode(&fe);
602 let E1_opt = M1.to_edwards(sign_bit);
603
604 E1_opt
605 .expect("Montgomery conversion to Edwards point in Elligator failed")
606 .mul_by_cofactor()
607 }
608}
609
610impl EdwardsPoint {
615 pub(crate) fn double(&self) -> EdwardsPoint {
617 self.as_projective().double().as_extended()
618 }
619}
620
621impl<'a, 'b> Add<&'b EdwardsPoint> for &'a EdwardsPoint {
626 type Output = EdwardsPoint;
627 fn add(self, other: &'b EdwardsPoint) -> EdwardsPoint {
628 (self + &other.as_projective_niels()).as_extended()
629 }
630}
631
632define_add_variants!(
633 LHS = EdwardsPoint,
634 RHS = EdwardsPoint,
635 Output = EdwardsPoint
636);
637
638impl<'b> AddAssign<&'b EdwardsPoint> for EdwardsPoint {
639 fn add_assign(&mut self, _rhs: &'b EdwardsPoint) {
640 *self = (self as &EdwardsPoint) + _rhs;
641 }
642}
643
644define_add_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
645
646impl<'a, 'b> Sub<&'b EdwardsPoint> for &'a EdwardsPoint {
647 type Output = EdwardsPoint;
648 fn sub(self, other: &'b EdwardsPoint) -> EdwardsPoint {
649 (self - &other.as_projective_niels()).as_extended()
650 }
651}
652
653define_sub_variants!(
654 LHS = EdwardsPoint,
655 RHS = EdwardsPoint,
656 Output = EdwardsPoint
657);
658
659impl<'b> SubAssign<&'b EdwardsPoint> for EdwardsPoint {
660 fn sub_assign(&mut self, _rhs: &'b EdwardsPoint) {
661 *self = (self as &EdwardsPoint) - _rhs;
662 }
663}
664
665define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
666
667impl<T> Sum<T> for EdwardsPoint
668where
669 T: Borrow<EdwardsPoint>,
670{
671 fn sum<I>(iter: I) -> Self
672 where
673 I: Iterator<Item = T>,
674 {
675 iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow())
676 }
677}
678
679impl<'a> Neg for &'a EdwardsPoint {
684 type Output = EdwardsPoint;
685
686 fn neg(self) -> EdwardsPoint {
687 EdwardsPoint {
688 X: -(&self.X),
689 Y: self.Y,
690 Z: self.Z,
691 T: -(&self.T),
692 }
693 }
694}
695
696impl Neg for EdwardsPoint {
697 type Output = EdwardsPoint;
698
699 fn neg(self) -> EdwardsPoint {
700 -&self
701 }
702}
703
704impl<'b> MulAssign<&'b Scalar> for EdwardsPoint {
709 fn mul_assign(&mut self, scalar: &'b Scalar) {
710 let result = (self as &EdwardsPoint) * scalar;
711 *self = result;
712 }
713}
714
715define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar);
716
717define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint);
718define_mul_variants!(LHS = Scalar, RHS = EdwardsPoint, Output = EdwardsPoint);
719
720impl<'a, 'b> Mul<&'b Scalar> for &'a EdwardsPoint {
721 type Output = EdwardsPoint;
722 fn mul(self, scalar: &'b Scalar) -> EdwardsPoint {
727 crate::backend::variable_base_mul(self, scalar)
728 }
729}
730
731impl<'a, 'b> Mul<&'b EdwardsPoint> for &'a Scalar {
732 type Output = EdwardsPoint;
733
734 fn mul(self, point: &'b EdwardsPoint) -> EdwardsPoint {
739 point * self
740 }
741}
742
743impl EdwardsPoint {
744 pub fn mul_base(scalar: &Scalar) -> Self {
749 #[cfg(not(feature = "precomputed-tables"))]
750 {
751 scalar * constants::ED25519_BASEPOINT_POINT
752 }
753
754 #[cfg(feature = "precomputed-tables")]
755 {
756 scalar * constants::ED25519_BASEPOINT_TABLE
757 }
758 }
759
760 pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
763 let s = Scalar {
771 bytes: clamp_integer(bytes),
772 };
773 s * self
774 }
775
776 pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
779 let s = Scalar {
783 bytes: clamp_integer(bytes),
784 };
785 Self::mul_base(&s)
786 }
787}
788
789#[cfg(feature = "alloc")]
797impl MultiscalarMul for EdwardsPoint {
798 type Point = EdwardsPoint;
799
800 fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
801 where
802 I: IntoIterator,
803 I::Item: Borrow<Scalar>,
804 J: IntoIterator,
805 J::Item: Borrow<EdwardsPoint>,
806 {
807 let mut scalars = scalars.into_iter();
809 let mut points = points.into_iter();
810
811 let (s_lo, s_hi) = scalars.by_ref().size_hint();
813 let (p_lo, p_hi) = points.by_ref().size_hint();
814
815 assert_eq!(s_lo, p_lo);
817 assert_eq!(s_hi, Some(s_lo));
818 assert_eq!(p_hi, Some(p_lo));
819
820 let _size = s_lo;
823
824 crate::backend::straus_multiscalar_mul(scalars, points)
825 }
826}
827
828#[cfg(feature = "alloc")]
829impl VartimeMultiscalarMul for EdwardsPoint {
830 type Point = EdwardsPoint;
831
832 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
833 where
834 I: IntoIterator,
835 I::Item: Borrow<Scalar>,
836 J: IntoIterator<Item = Option<EdwardsPoint>>,
837 {
838 let mut scalars = scalars.into_iter();
840 let mut points = points.into_iter();
841
842 let (s_lo, s_hi) = scalars.by_ref().size_hint();
844 let (p_lo, p_hi) = points.by_ref().size_hint();
845
846 assert_eq!(s_lo, p_lo);
848 assert_eq!(s_hi, Some(s_lo));
849 assert_eq!(p_hi, Some(p_lo));
850
851 let size = s_lo;
854
855 if size < 190 {
856 crate::backend::straus_optional_multiscalar_mul(scalars, points)
857 } else {
858 crate::backend::pippenger_optional_multiscalar_mul(scalars, points)
859 }
860 }
861}
862
863#[cfg(feature = "alloc")]
868pub struct VartimeEdwardsPrecomputation(crate::backend::VartimePrecomputedStraus);
869
870#[cfg(feature = "alloc")]
871impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation {
872 type Point = EdwardsPoint;
873
874 fn new<I>(static_points: I) -> Self
875 where
876 I: IntoIterator,
877 I::Item: Borrow<Self::Point>,
878 {
879 Self(crate::backend::VartimePrecomputedStraus::new(static_points))
880 }
881
882 fn optional_mixed_multiscalar_mul<I, J, K>(
883 &self,
884 static_scalars: I,
885 dynamic_scalars: J,
886 dynamic_points: K,
887 ) -> Option<Self::Point>
888 where
889 I: IntoIterator,
890 I::Item: Borrow<Scalar>,
891 J: IntoIterator,
892 J::Item: Borrow<Scalar>,
893 K: IntoIterator<Item = Option<Self::Point>>,
894 {
895 self.0
896 .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points)
897 }
898}
899
900impl EdwardsPoint {
901 pub fn vartime_double_scalar_mul_basepoint(
903 a: &Scalar,
904 A: &EdwardsPoint,
905 b: &Scalar,
906 ) -> EdwardsPoint {
907 crate::backend::vartime_double_base_mul(a, A, b)
908 }
909}
910
911#[cfg(feature = "precomputed-tables")]
912macro_rules! impl_basepoint_table {
913 (Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => {
914 #[derive(Clone)]
946 #[repr(transparent)]
947 pub struct $name(pub(crate) [$table<AffineNielsPoint>; 32]);
948
949 impl BasepointTable for $name {
950 type Point = $point;
951
952 fn create(basepoint: &$point) -> $name {
954 let mut table = $name([$table::default(); 32]);
956 let mut P = *basepoint;
957 for i in 0..32 {
958 table.0[i] = $table::from(&P);
960 P = P.mul_by_pow_2($radix + $radix);
961 }
962 table
963 }
964
965 fn basepoint(&self) -> $point {
967 (&<$point>::identity() + &self.0[0].select(1)).as_extended()
970 }
971
972 fn mul_base(&self, scalar: &Scalar) -> $point {
1015 let a = scalar.as_radix_2w($radix);
1016
1017 let tables = &self.0;
1018 let mut P = <$point>::identity();
1019
1020 for i in (0..$adds).filter(|x| x % 2 == 1) {
1021 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1022 }
1023
1024 P = P.mul_by_pow_2($radix);
1025
1026 for i in (0..$adds).filter(|x| x % 2 == 0) {
1027 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1028 }
1029
1030 P
1031 }
1032 }
1033
1034 impl<'a, 'b> Mul<&'b Scalar> for &'a $name {
1035 type Output = $point;
1036
1037 fn mul(self, scalar: &'b Scalar) -> $point {
1040 self.mul_base(scalar)
1042 }
1043 }
1044
1045 impl<'a, 'b> Mul<&'a $name> for &'b Scalar {
1046 type Output = $point;
1047
1048 fn mul(self, basepoint_table: &'a $name) -> $point {
1051 basepoint_table * self
1052 }
1053 }
1054
1055 impl Debug for $name {
1056 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1057 write!(f, "{:?}([\n", stringify!($name))?;
1058 for i in 0..32 {
1059 write!(f, "\t{:?},\n", &self.0[i])?;
1060 }
1061 write!(f, "])")
1062 }
1063 }
1064 };
1065} cfg_if! {
1069 if #[cfg(feature = "precomputed-tables")] {
1070 impl_basepoint_table! {
1071 Name = EdwardsBasepointTable,
1072 LookupTable = LookupTableRadix16,
1073 Point = EdwardsPoint,
1074 Radix = 4,
1075 Additions = 64
1076 }
1077 impl_basepoint_table! {
1078 Name = EdwardsBasepointTableRadix32,
1079 LookupTable = LookupTableRadix32,
1080 Point = EdwardsPoint,
1081 Radix = 5,
1082 Additions = 52
1083 }
1084 impl_basepoint_table! {
1085 Name = EdwardsBasepointTableRadix64,
1086 LookupTable = LookupTableRadix64,
1087 Point = EdwardsPoint,
1088 Radix = 6,
1089 Additions = 43
1090 }
1091 impl_basepoint_table! {
1092 Name = EdwardsBasepointTableRadix128,
1093 LookupTable = LookupTableRadix128,
1094 Point = EdwardsPoint,
1095 Radix = 7,
1096 Additions = 37
1097 }
1098 impl_basepoint_table! {
1099 Name = EdwardsBasepointTableRadix256,
1100 LookupTable = LookupTableRadix256,
1101 Point = EdwardsPoint,
1102 Radix = 8,
1103 Additions = 33
1104 }
1105
1106 pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable;
1113 }
1114}
1115
1116#[cfg(feature = "precomputed-tables")]
1117macro_rules! impl_basepoint_table_conversions {
1118 (LHS = $lhs:ty, RHS = $rhs:ty) => {
1119 impl<'a> From<&'a $lhs> for $rhs {
1120 fn from(table: &'a $lhs) -> $rhs {
1121 <$rhs>::create(&table.basepoint())
1122 }
1123 }
1124
1125 impl<'a> From<&'a $rhs> for $lhs {
1126 fn from(table: &'a $rhs) -> $lhs {
1127 <$lhs>::create(&table.basepoint())
1128 }
1129 }
1130 };
1131}
1132
1133cfg_if! {
1134 if #[cfg(feature = "precomputed-tables")] {
1135 impl_basepoint_table_conversions! {
1137 LHS = EdwardsBasepointTableRadix16,
1138 RHS = EdwardsBasepointTableRadix32
1139 }
1140 impl_basepoint_table_conversions! {
1141 LHS = EdwardsBasepointTableRadix16,
1142 RHS = EdwardsBasepointTableRadix64
1143 }
1144 impl_basepoint_table_conversions! {
1145 LHS = EdwardsBasepointTableRadix16,
1146 RHS = EdwardsBasepointTableRadix128
1147 }
1148 impl_basepoint_table_conversions! {
1149 LHS = EdwardsBasepointTableRadix16,
1150 RHS = EdwardsBasepointTableRadix256
1151 }
1152
1153 impl_basepoint_table_conversions! {
1155 LHS = EdwardsBasepointTableRadix32,
1156 RHS = EdwardsBasepointTableRadix64
1157 }
1158 impl_basepoint_table_conversions! {
1159 LHS = EdwardsBasepointTableRadix32,
1160 RHS = EdwardsBasepointTableRadix128
1161 }
1162 impl_basepoint_table_conversions! {
1163 LHS = EdwardsBasepointTableRadix32,
1164 RHS = EdwardsBasepointTableRadix256
1165 }
1166
1167 impl_basepoint_table_conversions! {
1169 LHS = EdwardsBasepointTableRadix64,
1170 RHS = EdwardsBasepointTableRadix128
1171 }
1172 impl_basepoint_table_conversions! {
1173 LHS = EdwardsBasepointTableRadix64,
1174 RHS = EdwardsBasepointTableRadix256
1175 }
1176
1177 impl_basepoint_table_conversions! {
1179 LHS = EdwardsBasepointTableRadix128,
1180 RHS = EdwardsBasepointTableRadix256
1181 }
1182 }
1183}
1184
1185impl EdwardsPoint {
1186 pub fn mul_by_cofactor(&self) -> EdwardsPoint {
1188 self.mul_by_pow_2(3)
1189 }
1190
1191 pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint {
1193 debug_assert!(k > 0);
1194 let mut r: CompletedPoint;
1195 let mut s = self.as_projective();
1196 for _ in 0..(k - 1) {
1197 r = s.double();
1198 s = r.as_projective();
1199 }
1200 s.double().as_extended()
1202 }
1203
1204 pub fn is_small_order(&self) -> bool {
1228 self.mul_by_cofactor().is_identity()
1229 }
1230
1231 pub fn is_torsion_free(&self) -> bool {
1258 (self * constants::BASEPOINT_ORDER_PRIVATE).is_identity()
1259 }
1260}
1261
1262impl Debug for EdwardsPoint {
1267 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1268 write!(
1269 f,
1270 "EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
1271 &self.X, &self.Y, &self.Z, &self.T
1272 )
1273 }
1274}
1275
1276#[cfg(feature = "group")]
1283impl group::Group for EdwardsPoint {
1284 type Scalar = Scalar;
1285
1286 fn random(mut rng: impl RngCore) -> Self {
1287 let mut repr = CompressedEdwardsY([0u8; 32]);
1288 loop {
1289 rng.fill_bytes(&mut repr.0);
1290 if let Some(p) = repr.decompress() {
1291 if !IsIdentity::is_identity(&p) {
1292 break p;
1293 }
1294 }
1295 }
1296 }
1297
1298 fn identity() -> Self {
1299 Identity::identity()
1300 }
1301
1302 fn generator() -> Self {
1303 constants::ED25519_BASEPOINT_POINT
1304 }
1305
1306 fn is_identity(&self) -> Choice {
1307 self.ct_eq(&Identity::identity())
1308 }
1309
1310 fn double(&self) -> Self {
1311 self.double()
1312 }
1313}
1314
1315#[cfg(feature = "group")]
1316impl GroupEncoding for EdwardsPoint {
1317 type Repr = [u8; 32];
1318
1319 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1320 let repr = CompressedEdwardsY(*bytes);
1321 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr);
1322 CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord)
1323 }
1324
1325 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1326 Self::from_bytes(bytes)
1328 }
1329
1330 fn to_bytes(&self) -> Self::Repr {
1331 self.compress().to_bytes()
1332 }
1333}
1334
1335#[cfg(feature = "group")]
1338#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1339pub struct SubgroupPoint(EdwardsPoint);
1340
1341#[cfg(feature = "group")]
1342impl From<SubgroupPoint> for EdwardsPoint {
1343 fn from(p: SubgroupPoint) -> Self {
1344 p.0
1345 }
1346}
1347
1348#[cfg(feature = "group")]
1349impl Neg for SubgroupPoint {
1350 type Output = Self;
1351
1352 fn neg(self) -> Self::Output {
1353 SubgroupPoint(-self.0)
1354 }
1355}
1356
1357#[cfg(feature = "group")]
1358impl Add<&SubgroupPoint> for &SubgroupPoint {
1359 type Output = SubgroupPoint;
1360 fn add(self, other: &SubgroupPoint) -> SubgroupPoint {
1361 SubgroupPoint(self.0 + other.0)
1362 }
1363}
1364
1365#[cfg(feature = "group")]
1366define_add_variants!(
1367 LHS = SubgroupPoint,
1368 RHS = SubgroupPoint,
1369 Output = SubgroupPoint
1370);
1371
1372#[cfg(feature = "group")]
1373impl Add<&SubgroupPoint> for &EdwardsPoint {
1374 type Output = EdwardsPoint;
1375 fn add(self, other: &SubgroupPoint) -> EdwardsPoint {
1376 self + other.0
1377 }
1378}
1379
1380#[cfg(feature = "group")]
1381define_add_variants!(
1382 LHS = EdwardsPoint,
1383 RHS = SubgroupPoint,
1384 Output = EdwardsPoint
1385);
1386
1387#[cfg(feature = "group")]
1388impl AddAssign<&SubgroupPoint> for SubgroupPoint {
1389 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1390 self.0 += rhs.0
1391 }
1392}
1393
1394#[cfg(feature = "group")]
1395define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1396
1397#[cfg(feature = "group")]
1398impl AddAssign<&SubgroupPoint> for EdwardsPoint {
1399 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1400 *self += rhs.0
1401 }
1402}
1403
1404#[cfg(feature = "group")]
1405define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1406
1407#[cfg(feature = "group")]
1408impl Sub<&SubgroupPoint> for &SubgroupPoint {
1409 type Output = SubgroupPoint;
1410 fn sub(self, other: &SubgroupPoint) -> SubgroupPoint {
1411 SubgroupPoint(self.0 - other.0)
1412 }
1413}
1414
1415#[cfg(feature = "group")]
1416define_sub_variants!(
1417 LHS = SubgroupPoint,
1418 RHS = SubgroupPoint,
1419 Output = SubgroupPoint
1420);
1421
1422#[cfg(feature = "group")]
1423impl Sub<&SubgroupPoint> for &EdwardsPoint {
1424 type Output = EdwardsPoint;
1425 fn sub(self, other: &SubgroupPoint) -> EdwardsPoint {
1426 self - other.0
1427 }
1428}
1429
1430#[cfg(feature = "group")]
1431define_sub_variants!(
1432 LHS = EdwardsPoint,
1433 RHS = SubgroupPoint,
1434 Output = EdwardsPoint
1435);
1436
1437#[cfg(feature = "group")]
1438impl SubAssign<&SubgroupPoint> for SubgroupPoint {
1439 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1440 self.0 -= rhs.0;
1441 }
1442}
1443
1444#[cfg(feature = "group")]
1445define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1446
1447#[cfg(feature = "group")]
1448impl SubAssign<&SubgroupPoint> for EdwardsPoint {
1449 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1450 *self -= rhs.0;
1451 }
1452}
1453
1454#[cfg(feature = "group")]
1455define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1456
1457#[cfg(feature = "group")]
1458impl<T> Sum<T> for SubgroupPoint
1459where
1460 T: Borrow<SubgroupPoint>,
1461{
1462 fn sum<I>(iter: I) -> Self
1463 where
1464 I: Iterator<Item = T>,
1465 {
1466 use group::Group;
1467 iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow())
1468 }
1469}
1470
1471#[cfg(feature = "group")]
1472impl Mul<&Scalar> for &SubgroupPoint {
1473 type Output = SubgroupPoint;
1474
1475 fn mul(self, scalar: &Scalar) -> SubgroupPoint {
1480 SubgroupPoint(self.0 * scalar)
1481 }
1482}
1483
1484#[cfg(feature = "group")]
1485define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint);
1486
1487#[cfg(feature = "group")]
1488impl Mul<&SubgroupPoint> for &Scalar {
1489 type Output = SubgroupPoint;
1490
1491 fn mul(self, point: &SubgroupPoint) -> SubgroupPoint {
1496 point * self
1497 }
1498}
1499
1500#[cfg(feature = "group")]
1501define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint);
1502
1503#[cfg(feature = "group")]
1504impl MulAssign<&Scalar> for SubgroupPoint {
1505 fn mul_assign(&mut self, scalar: &Scalar) {
1506 self.0 *= scalar;
1507 }
1508}
1509
1510#[cfg(feature = "group")]
1511define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);
1512
1513#[cfg(feature = "group")]
1514impl group::Group for SubgroupPoint {
1515 type Scalar = Scalar;
1516
1517 fn random(mut rng: impl RngCore) -> Self {
1518 use group::ff::Field;
1519
1520 let s = loop {
1523 let s: Scalar = Field::random(&mut rng);
1524 if !s.is_zero_vartime() {
1525 break s;
1526 }
1527 };
1528
1529 Self::generator() * s
1531 }
1532
1533 fn identity() -> Self {
1534 SubgroupPoint(Identity::identity())
1535 }
1536
1537 fn generator() -> Self {
1538 SubgroupPoint(EdwardsPoint::generator())
1539 }
1540
1541 fn is_identity(&self) -> Choice {
1542 self.0.ct_eq(&Identity::identity())
1543 }
1544
1545 fn double(&self) -> Self {
1546 SubgroupPoint(self.0.double())
1547 }
1548}
1549
1550#[cfg(feature = "group")]
1551impl GroupEncoding for SubgroupPoint {
1552 type Repr = <EdwardsPoint as GroupEncoding>::Repr;
1553
1554 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1555 EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1556 }
1557
1558 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1559 EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup())
1560 }
1561
1562 fn to_bytes(&self) -> Self::Repr {
1563 self.0.compress().to_bytes()
1564 }
1565}
1566
1567#[cfg(feature = "group")]
1568impl PrimeGroup for SubgroupPoint {}
1569
1570#[cfg(feature = "group")]
1572impl CofactorGroup for EdwardsPoint {
1573 type Subgroup = SubgroupPoint;
1574
1575 fn clear_cofactor(&self) -> Self::Subgroup {
1576 SubgroupPoint(self.mul_by_cofactor())
1577 }
1578
1579 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1580 CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
1581 }
1582
1583 fn is_torsion_free(&self) -> Choice {
1584 (self * constants::BASEPOINT_ORDER_PRIVATE).ct_eq(&Self::identity())
1585 }
1586}
1587
1588#[cfg(test)]
1593mod test {
1594 use super::*;
1595
1596 #[cfg(not(feature = "group"))]
1598 use rand_core::RngCore;
1599
1600 #[cfg(feature = "alloc")]
1601 use alloc::vec::Vec;
1602
1603 #[cfg(feature = "precomputed-tables")]
1604 use crate::constants::ED25519_BASEPOINT_TABLE;
1605
1606 static BASE_X_COORD_BYTES: [u8; 32] = [
1609 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c,
1610 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36,
1611 0x69, 0x21,
1612 ];
1613
1614 static BASE2_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1616 0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39,
1617 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd,
1618 0x60, 0x22,
1619 ]);
1620
1621 static BASE16_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1623 0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a,
1624 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96,
1625 0x96, 0x70,
1626 ]);
1627
1628 pub static A_SCALAR: Scalar = Scalar {
1630 bytes: [
1631 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
1632 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
1633 0x23, 0x76, 0xef, 0x09,
1634 ],
1635 };
1636
1637 pub static B_SCALAR: Scalar = Scalar {
1639 bytes: [
1640 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0,
1641 0xb3, 0x2e, 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, 0x56, 0xa7, 0xd4, 0xaa,
1642 0xb8, 0x60, 0x8a, 0x05,
1643 ],
1644 };
1645
1646 pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([
1648 0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59, 0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34,
1649 0xc3, 0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e, 0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e,
1650 0x40, 0xa5,
1651 ]);
1652
1653 static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([
1656 0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e, 0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59,
1657 0xc4, 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13,
1658 0x2b, 0x42,
1659 ]);
1660
1661 #[test]
1663 fn basepoint_decompression_compression() {
1664 let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES);
1665 let bp = constants::ED25519_BASEPOINT_COMPRESSED
1666 .decompress()
1667 .unwrap();
1668 assert!(bp.is_valid());
1669 assert_eq!(base_X, bp.X);
1671 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1672 }
1673
1674 #[test]
1676 fn decompression_sign_handling() {
1677 let mut minus_basepoint_bytes = *constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
1679 minus_basepoint_bytes[31] |= 1 << 7;
1680 let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes)
1681 .decompress()
1682 .unwrap();
1683 assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X));
1686 assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y);
1687 assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z);
1688 assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T));
1689 }
1690
1691 #[cfg(feature = "precomputed-tables")]
1693 #[test]
1694 fn basepoint_mult_one_vs_basepoint() {
1695 let bp = ED25519_BASEPOINT_TABLE * &Scalar::ONE;
1696 let compressed = bp.compress();
1697 assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED);
1698 }
1699
1700 #[cfg(feature = "precomputed-tables")]
1702 #[test]
1703 fn basepoint_table_basepoint_function_correct() {
1704 let bp = ED25519_BASEPOINT_TABLE.basepoint();
1705 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1706 }
1707
1708 #[test]
1711 fn basepoint_plus_basepoint_vs_basepoint2() {
1712 let bp = constants::ED25519_BASEPOINT_POINT;
1713 let bp_added = bp + bp;
1714 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1715 }
1716
1717 #[test]
1720 fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() {
1721 let bp = constants::ED25519_BASEPOINT_POINT;
1722 let bp_added = (&bp + &bp.as_projective_niels()).as_extended();
1723 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1724 }
1725
1726 #[test]
1729 fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() {
1730 let bp = constants::ED25519_BASEPOINT_POINT;
1731 let bp_affine_niels = bp.as_affine_niels();
1732 let bp_added = (&bp + &bp_affine_niels).as_extended();
1733 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1734 }
1735
1736 #[test]
1739 fn extended_point_equality_handles_scaling() {
1740 let mut two_bytes = [0u8; 32];
1741 two_bytes[0] = 2;
1742 let id1 = EdwardsPoint::identity();
1743 let id2 = EdwardsPoint {
1744 X: FieldElement::ZERO,
1745 Y: FieldElement::from_bytes(&two_bytes),
1746 Z: FieldElement::from_bytes(&two_bytes),
1747 T: FieldElement::ZERO,
1748 };
1749 assert!(bool::from(id1.ct_eq(&id2)));
1750 }
1751
1752 #[cfg(feature = "precomputed-tables")]
1754 #[test]
1755 fn to_affine_niels_clears_denominators() {
1756 let aB = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1758 let aB_affine_niels = aB.as_affine_niels();
1759 let also_aB = (&EdwardsPoint::identity() + &aB_affine_niels).as_extended();
1760 assert_eq!(aB.compress(), also_aB.compress());
1761 }
1762
1763 #[test]
1765 fn basepoint_mult_vs_ed25519py() {
1766 let aB = EdwardsPoint::mul_base(&A_SCALAR);
1767 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1768 }
1769
1770 #[test]
1772 fn basepoint_mult_by_basepoint_order() {
1773 let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER_PRIVATE);
1774 assert!(should_be_id.is_identity());
1775 }
1776
1777 #[cfg(feature = "precomputed-tables")]
1779 #[test]
1780 fn test_precomputed_basepoint_mult() {
1781 let aB_1 = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1782 let aB_2 = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1783 assert_eq!(aB_1.compress(), aB_2.compress());
1784 }
1785
1786 #[test]
1788 fn scalar_mul_vs_ed25519py() {
1789 let aB = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1790 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1791 }
1792
1793 #[test]
1795 fn basepoint_double_vs_basepoint2() {
1796 assert_eq!(
1797 constants::ED25519_BASEPOINT_POINT.double().compress(),
1798 BASE2_CMPRSSD
1799 );
1800 }
1801
1802 #[test]
1804 fn basepoint_mult_two_vs_basepoint2() {
1805 let two = Scalar::from(2u64);
1806 let bp2 = EdwardsPoint::mul_base(&two);
1807 assert_eq!(bp2.compress(), BASE2_CMPRSSD);
1808 }
1809
1810 #[cfg(feature = "precomputed-tables")]
1812 #[test]
1813 fn basepoint_tables() {
1814 let P = &constants::ED25519_BASEPOINT_POINT;
1815 let a = A_SCALAR;
1816
1817 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1818 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
1819 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
1820 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
1821 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
1822
1823 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
1824 let aP16 = (&table_radix16 * &a).compress();
1825 let aP32 = (&table_radix32 * &a).compress();
1826 let aP64 = (&table_radix64 * &a).compress();
1827 let aP128 = (&table_radix128 * &a).compress();
1828 let aP256 = (&table_radix256 * &a).compress();
1829
1830 assert_eq!(aP, aP16);
1831 assert_eq!(aP16, aP32);
1832 assert_eq!(aP32, aP64);
1833 assert_eq!(aP64, aP128);
1834 assert_eq!(aP128, aP256);
1835 }
1836
1837 #[cfg(feature = "precomputed-tables")]
1840 #[test]
1841 fn basepoint_tables_unreduced_scalar() {
1842 let P = &constants::ED25519_BASEPOINT_POINT;
1843 let a = crate::scalar::test::LARGEST_UNREDUCED_SCALAR;
1844
1845 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1846 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
1847 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
1848 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
1849 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
1850
1851 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
1852 let aP16 = (&table_radix16 * &a).compress();
1853 let aP32 = (&table_radix32 * &a).compress();
1854 let aP64 = (&table_radix64 * &a).compress();
1855 let aP128 = (&table_radix128 * &a).compress();
1856 let aP256 = (&table_radix256 * &a).compress();
1857
1858 assert_eq!(aP, aP16);
1859 assert_eq!(aP16, aP32);
1860 assert_eq!(aP32, aP64);
1861 assert_eq!(aP64, aP128);
1862 assert_eq!(aP128, aP256);
1863 }
1864
1865 #[test]
1867 fn basepoint_projective_extended_round_trip() {
1868 assert_eq!(
1869 constants::ED25519_BASEPOINT_POINT
1870 .as_projective()
1871 .as_extended()
1872 .compress(),
1873 constants::ED25519_BASEPOINT_COMPRESSED
1874 );
1875 }
1876
1877 #[test]
1879 fn basepoint16_vs_mul_by_pow_2_4() {
1880 let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4);
1881 assert_eq!(bp16.compress(), BASE16_CMPRSSD);
1882 }
1883
1884 #[test]
1886 fn mul_base_clamped() {
1887 let mut csprng = rand_core::OsRng;
1888
1889 #[cfg(feature = "precomputed-tables")]
1891 let random_point = {
1892 let mut b = [0u8; 32];
1893 csprng.fill_bytes(&mut b);
1894 EdwardsPoint::mul_base_clamped(b) + constants::EIGHT_TORSION[1]
1895 };
1896 #[cfg(feature = "precomputed-tables")]
1898 let random_table = EdwardsBasepointTableRadix256::create(&random_point);
1899
1900 let a_bytes = [0xff; 32];
1905 assert_eq!(
1906 EdwardsPoint::mul_base_clamped(a_bytes),
1907 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
1908 );
1909 #[cfg(feature = "precomputed-tables")]
1910 assert_eq!(
1911 random_table.mul_base_clamped(a_bytes),
1912 random_point.mul_clamped(a_bytes)
1913 );
1914
1915 for _ in 0..100 {
1917 let mut a_bytes = [0u8; 32];
1919 csprng.fill_bytes(&mut a_bytes);
1920
1921 assert_eq!(
1922 EdwardsPoint::mul_base_clamped(a_bytes),
1923 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
1924 );
1925 #[cfg(feature = "precomputed-tables")]
1926 assert_eq!(
1927 random_table.mul_base_clamped(a_bytes),
1928 random_point.mul_clamped(a_bytes)
1929 );
1930 }
1931 }
1932
1933 #[test]
1934 #[cfg(feature = "alloc")]
1935 fn impl_sum() {
1936 let BASE = constants::ED25519_BASEPOINT_POINT;
1938
1939 let s1 = Scalar::from(999u64);
1940 let P1 = BASE * s1;
1941
1942 let s2 = Scalar::from(333u64);
1943 let P2 = BASE * s2;
1944
1945 let vec = vec![P1, P2];
1946 let sum: EdwardsPoint = vec.iter().sum();
1947
1948 assert_eq!(sum, P1 + P2);
1949
1950 let empty_vector: Vec<EdwardsPoint> = vec![];
1952 let sum: EdwardsPoint = empty_vector.iter().sum();
1953
1954 assert_eq!(sum, EdwardsPoint::identity());
1955
1956 let s = Scalar::from(2u64);
1958 let mapped = vec.iter().map(|x| x * s);
1959 let sum: EdwardsPoint = mapped.sum();
1960
1961 assert_eq!(sum, P1 * s + P2 * s);
1962 }
1963
1964 #[test]
1966 fn conditional_assign_for_affine_niels_point() {
1967 let id = AffineNielsPoint::identity();
1968 let mut p1 = AffineNielsPoint::identity();
1969 let bp = constants::ED25519_BASEPOINT_POINT.as_affine_niels();
1970
1971 p1.conditional_assign(&bp, Choice::from(0));
1972 assert_eq!(p1, id);
1973 p1.conditional_assign(&bp, Choice::from(1));
1974 assert_eq!(p1, bp);
1975 }
1976
1977 #[test]
1978 fn is_small_order() {
1979 assert!(!constants::ED25519_BASEPOINT_POINT.is_small_order());
1981 for torsion_point in &constants::EIGHT_TORSION {
1983 assert!(torsion_point.is_small_order());
1984 }
1985 }
1986
1987 #[test]
1988 fn compressed_identity() {
1989 assert_eq!(
1990 EdwardsPoint::identity().compress(),
1991 CompressedEdwardsY::identity()
1992 );
1993 }
1994
1995 #[test]
1996 fn is_identity() {
1997 assert!(EdwardsPoint::identity().is_identity());
1998 assert!(!constants::ED25519_BASEPOINT_POINT.is_identity());
1999 }
2000
2001 #[test]
2012 fn monte_carlo_overflow_underflow_debug_assert_test() {
2013 let mut P = constants::ED25519_BASEPOINT_POINT;
2014 for _ in 0..1_000 {
2017 P *= &A_SCALAR;
2018 }
2019 }
2020
2021 #[test]
2022 fn scalarmult_extended_point_works_both_ways() {
2023 let G: EdwardsPoint = constants::ED25519_BASEPOINT_POINT;
2024 let s: Scalar = A_SCALAR;
2025
2026 let P1 = G * s;
2027 let P2 = s * G;
2028
2029 assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
2030 }
2031
2032 #[cfg(feature = "alloc")]
2034 fn multiscalar_consistency_iter(n: usize) {
2035 let mut rng = rand::thread_rng();
2036
2037 let xs = (0..n).map(|_| Scalar::random(&mut rng)).collect::<Vec<_>>();
2040 let check = xs.iter().map(|xi| xi * xi).sum::<Scalar>();
2041
2042 let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::<Vec<_>>();
2044
2045 let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs);
2047 let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs);
2049 let H3 = EdwardsPoint::mul_base(&check);
2051
2052 assert_eq!(H1, H3);
2053 assert_eq!(H2, H3);
2054 }
2055
2056 #[test]
2060 #[cfg(feature = "alloc")]
2061 fn multiscalar_consistency_n_100() {
2062 let iters = 50;
2063 for _ in 0..iters {
2064 multiscalar_consistency_iter(100);
2065 }
2066 }
2067
2068 #[test]
2069 #[cfg(feature = "alloc")]
2070 fn multiscalar_consistency_n_250() {
2071 let iters = 50;
2072 for _ in 0..iters {
2073 multiscalar_consistency_iter(250);
2074 }
2075 }
2076
2077 #[test]
2078 #[cfg(feature = "alloc")]
2079 fn multiscalar_consistency_n_500() {
2080 let iters = 50;
2081 for _ in 0..iters {
2082 multiscalar_consistency_iter(500);
2083 }
2084 }
2085
2086 #[test]
2087 #[cfg(feature = "alloc")]
2088 fn multiscalar_consistency_n_1000() {
2089 let iters = 50;
2090 for _ in 0..iters {
2091 multiscalar_consistency_iter(1000);
2092 }
2093 }
2094
2095 #[test]
2096 #[cfg(feature = "alloc")]
2097 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
2098 let mut rng = rand::thread_rng();
2099
2100 let static_scalars = (0..128)
2101 .map(|_| Scalar::random(&mut rng))
2102 .collect::<Vec<_>>();
2103
2104 let dynamic_scalars = (0..128)
2105 .map(|_| Scalar::random(&mut rng))
2106 .collect::<Vec<_>>();
2107
2108 let check_scalar: Scalar = static_scalars
2109 .iter()
2110 .chain(dynamic_scalars.iter())
2111 .map(|s| s * s)
2112 .sum();
2113
2114 let static_points = static_scalars
2115 .iter()
2116 .map(EdwardsPoint::mul_base)
2117 .collect::<Vec<_>>();
2118 let dynamic_points = dynamic_scalars
2119 .iter()
2120 .map(EdwardsPoint::mul_base)
2121 .collect::<Vec<_>>();
2122
2123 let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter());
2124
2125 let P = precomputation.vartime_mixed_multiscalar_mul(
2126 &static_scalars,
2127 &dynamic_scalars,
2128 &dynamic_points,
2129 );
2130
2131 use crate::traits::VartimeMultiscalarMul;
2132 let Q = EdwardsPoint::vartime_multiscalar_mul(
2133 static_scalars.iter().chain(dynamic_scalars.iter()),
2134 static_points.iter().chain(dynamic_points.iter()),
2135 );
2136
2137 let R = EdwardsPoint::mul_base(&check_scalar);
2138
2139 assert_eq!(P.compress(), R.compress());
2140 assert_eq!(Q.compress(), R.compress());
2141 }
2142
2143 mod vartime {
2144 use super::super::*;
2145 use super::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR, DOUBLE_SCALAR_MULT_RESULT};
2146
2147 #[test]
2149 fn double_scalar_mul_basepoint_vs_ed25519py() {
2150 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2151 let result =
2152 EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
2153 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2154 }
2155
2156 #[test]
2157 #[cfg(feature = "alloc")]
2158 fn multiscalar_mul_vs_ed25519py() {
2159 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2160 let result = EdwardsPoint::vartime_multiscalar_mul(
2161 &[A_SCALAR, B_SCALAR],
2162 &[A, constants::ED25519_BASEPOINT_POINT],
2163 );
2164 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2165 }
2166
2167 #[test]
2168 #[cfg(feature = "alloc")]
2169 fn multiscalar_mul_vartime_vs_consttime() {
2170 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2171 let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
2172 &[A_SCALAR, B_SCALAR],
2173 &[A, constants::ED25519_BASEPOINT_POINT],
2174 );
2175 let result_consttime = EdwardsPoint::multiscalar_mul(
2176 &[A_SCALAR, B_SCALAR],
2177 &[A, constants::ED25519_BASEPOINT_POINT],
2178 );
2179
2180 assert_eq!(result_vartime.compress(), result_consttime.compress());
2181 }
2182 }
2183
2184 #[test]
2185 #[cfg(feature = "serde")]
2186 fn serde_bincode_basepoint_roundtrip() {
2187 use bincode;
2188
2189 let encoded = bincode::serialize(&constants::ED25519_BASEPOINT_POINT).unwrap();
2190 let enc_compressed = bincode::serialize(&constants::ED25519_BASEPOINT_COMPRESSED).unwrap();
2191 assert_eq!(encoded, enc_compressed);
2192
2193 assert_eq!(encoded.len(), 32);
2195
2196 let dec_uncompressed: EdwardsPoint = bincode::deserialize(&encoded).unwrap();
2197 let dec_compressed: CompressedEdwardsY = bincode::deserialize(&encoded).unwrap();
2198
2199 assert_eq!(dec_uncompressed, constants::ED25519_BASEPOINT_POINT);
2200 assert_eq!(dec_compressed, constants::ED25519_BASEPOINT_COMPRESSED);
2201
2202 let raw_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
2204 let bp: EdwardsPoint = bincode::deserialize(raw_bytes).unwrap();
2205 assert_eq!(bp, constants::ED25519_BASEPOINT_POINT);
2206 }
2207
2208 #[cfg(all(feature = "alloc", feature = "digest"))]
2214 fn test_vectors() -> Vec<Vec<&'static str>> {
2215 vec![
2216 vec![
2217 "214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f",
2218 "c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052",
2219 ],
2220 vec![
2221 "2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6",
2222 "d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f",
2223 ],
2224 vec![
2225 "84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
2226 "93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
2227 ],
2228 vec![
2229 "c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267",
2230 "43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537",
2231 ],
2232 vec![
2233 "26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76",
2234 "b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835",
2235 ],
2236 vec![
2237 "1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2",
2238 "da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c",
2239 ],
2240 vec![
2241 "48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
2242 "ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
2243 ],
2244 vec![
2245 "a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d",
2246 "fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0",
2247 ],
2248 vec![
2249 "80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
2250 "57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
2251 ],
2252 vec![
2253 "f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617",
2254 "785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0",
2255 ],
2256 ]
2257 }
2258
2259 #[test]
2260 #[allow(deprecated)]
2261 #[cfg(all(feature = "alloc", feature = "digest"))]
2262 fn elligator_signal_test_vectors() {
2263 for vector in test_vectors().iter() {
2264 let input = hex::decode(vector[0]).unwrap();
2265 let output = hex::decode(vector[1]).unwrap();
2266
2267 let point = EdwardsPoint::nonspec_map_to_curve::<sha2::Sha512>(&input);
2268 assert_eq!(point.compress().to_bytes(), output[..]);
2269 }
2270 }
2271}