1#![allow(non_snake_case)]
95
96mod affine;
97
98use cfg_if::cfg_if;
99use core::array::TryFromSliceError;
100use core::borrow::Borrow;
101use core::fmt::Debug;
102use core::iter::Sum;
103use core::ops::{Add, Neg, Sub};
104use core::ops::{AddAssign, SubAssign};
105use core::ops::{Mul, MulAssign};
106
107#[cfg(feature = "digest")]
108use digest::{
109 FixedOutput, HashMarker, array::typenum::U64, common::BlockSizeUser, consts::True,
110 typenum::IsGreater,
111};
112
113#[cfg(feature = "group")]
114use {
115 group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup},
116 rand_core::TryRng,
117 subtle::CtOption,
118};
119
120#[cfg(feature = "rand_core")]
121use rand_core::Rng;
122
123use subtle::Choice;
124use subtle::ConditionallyNegatable;
125use subtle::ConditionallySelectable;
126use subtle::ConstantTimeEq;
127
128#[cfg(feature = "zeroize")]
129use zeroize::Zeroize;
130
131use crate::constants;
132
133use crate::field::FieldElement;
134use crate::scalar::{Scalar, clamp_integer};
135
136use crate::montgomery::MontgomeryPoint;
137
138use crate::backend::serial::curve_models::AffineNielsPoint;
139use crate::backend::serial::curve_models::CompletedPoint;
140use crate::backend::serial::curve_models::ProjectiveNielsPoint;
141use crate::backend::serial::curve_models::ProjectivePoint;
142
143#[cfg(feature = "precomputed-tables")]
144use crate::window::{
145 LookupTableRadix16, LookupTableRadix32, LookupTableRadix64, LookupTableRadix128,
146 LookupTableRadix256,
147};
148
149#[cfg(feature = "precomputed-tables")]
150use crate::traits::BasepointTable;
151
152use crate::traits::ValidityCheck;
153use crate::traits::{Identity, IsIdentity};
154
155use affine::AffinePoint;
156
157#[cfg(feature = "alloc")]
158use crate::traits::MultiscalarMul;
159#[cfg(feature = "alloc")]
160use crate::traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
161#[cfg(feature = "alloc")]
162use alloc::vec::Vec;
163
164#[allow(clippy::derived_hash_with_manual_eq)]
174#[derive(Copy, Clone, Hash)]
175pub struct CompressedEdwardsY(pub [u8; 32]);
176
177impl ConstantTimeEq for CompressedEdwardsY {
178 fn ct_eq(&self, other: &CompressedEdwardsY) -> Choice {
179 self.as_bytes().ct_eq(other.as_bytes())
180 }
181}
182
183impl Eq for CompressedEdwardsY {}
184impl PartialEq for CompressedEdwardsY {
185 fn eq(&self, other: &Self) -> bool {
186 self.ct_eq(other).into()
187 }
188}
189
190impl Debug for CompressedEdwardsY {
191 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
192 write!(f, "CompressedEdwardsY: {:?}", self.as_bytes())
193 }
194}
195
196impl CompressedEdwardsY {
197 pub const fn as_bytes(&self) -> &[u8; 32] {
199 &self.0
200 }
201
202 pub const fn to_bytes(&self) -> [u8; 32] {
204 self.0
205 }
206
207 pub fn decompress(&self) -> Option<EdwardsPoint> {
212 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
213
214 if is_valid_y_coord.into() {
215 Some(decompress::step_2(self, X, Y, Z))
216 } else {
217 None
218 }
219 }
220}
221
222mod decompress {
223 use super::*;
224
225 #[rustfmt::skip] pub(super) fn step_1(
227 repr: &CompressedEdwardsY,
228 ) -> (Choice, FieldElement, FieldElement, FieldElement) {
229 let Y = FieldElement::from_bytes(repr.as_bytes());
230 let Z = FieldElement::ONE;
231 let YY = Y.square();
232 let u = &YY - &Z; let v = &(&YY * &constants::EDWARDS_D) + &Z; let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v);
235
236 (is_valid_y_coord, X, Y, Z)
237 }
238
239 #[rustfmt::skip]
240 pub(super) fn step_2(
241 repr: &CompressedEdwardsY,
242 mut X: FieldElement,
243 Y: FieldElement,
244 Z: FieldElement,
245 ) -> EdwardsPoint {
246 let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7);
249 X.conditional_negate(compressed_sign_bit);
250
251 EdwardsPoint {
252 X,
253 Y,
254 Z,
255 T: &X * &Y,
256 }
257 }
258}
259
260impl TryFrom<&[u8]> for CompressedEdwardsY {
261 type Error = TryFromSliceError;
262
263 fn try_from(slice: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
264 Self::from_slice(slice)
265 }
266}
267
268#[cfg(feature = "digest")]
277use constants::ED25519_SQRTAM2;
278#[cfg(feature = "serde")]
279use serde::de::Visitor;
280#[cfg(feature = "serde")]
281use serde::{Deserialize, Deserializer, Serialize, Serializer};
282
283#[cfg(feature = "serde")]
284impl Serialize for EdwardsPoint {
285 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
286 where
287 S: Serializer,
288 {
289 use serde::ser::SerializeTuple;
290 let mut tup = serializer.serialize_tuple(32)?;
291 for byte in self.compress().as_bytes().iter() {
292 tup.serialize_element(byte)?;
293 }
294 tup.end()
295 }
296}
297
298#[cfg(feature = "serde")]
299impl Serialize for CompressedEdwardsY {
300 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
301 where
302 S: Serializer,
303 {
304 use serde::ser::SerializeTuple;
305 let mut tup = serializer.serialize_tuple(32)?;
306 for byte in self.as_bytes().iter() {
307 tup.serialize_element(byte)?;
308 }
309 tup.end()
310 }
311}
312
313#[cfg(feature = "serde")]
314impl<'de> Deserialize<'de> for EdwardsPoint {
315 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
316 where
317 D: Deserializer<'de>,
318 {
319 struct EdwardsPointVisitor;
320
321 impl<'de> Visitor<'de> for EdwardsPointVisitor {
322 type Value = EdwardsPoint;
323
324 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
325 formatter.write_str("a valid point in Edwards y + sign format")
326 }
327
328 fn visit_seq<A>(self, mut seq: A) -> Result<EdwardsPoint, A::Error>
329 where
330 A: serde::de::SeqAccess<'de>,
331 {
332 let mut bytes = [0u8; 32];
333 #[allow(clippy::needless_range_loop)]
334 for i in 0..32 {
335 bytes[i] = seq
336 .next_element()?
337 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
338 }
339 CompressedEdwardsY(bytes)
340 .decompress()
341 .ok_or_else(|| serde::de::Error::custom("decompression failed"))
342 }
343 }
344
345 deserializer.deserialize_tuple(32, EdwardsPointVisitor)
346 }
347}
348
349#[cfg(feature = "serde")]
350impl<'de> Deserialize<'de> for CompressedEdwardsY {
351 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
352 where
353 D: Deserializer<'de>,
354 {
355 struct CompressedEdwardsYVisitor;
356
357 impl<'de> Visitor<'de> for CompressedEdwardsYVisitor {
358 type Value = CompressedEdwardsY;
359
360 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
361 formatter.write_str("32 bytes of data")
362 }
363
364 fn visit_seq<A>(self, mut seq: A) -> Result<CompressedEdwardsY, A::Error>
365 where
366 A: serde::de::SeqAccess<'de>,
367 {
368 let mut bytes = [0u8; 32];
369 #[allow(clippy::needless_range_loop)]
370 for i in 0..32 {
371 bytes[i] = seq
372 .next_element()?
373 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
374 }
375 Ok(CompressedEdwardsY(bytes))
376 }
377 }
378
379 deserializer.deserialize_tuple(32, CompressedEdwardsYVisitor)
380 }
381}
382
383#[derive(Copy, Clone)]
389#[allow(missing_docs)]
390pub struct EdwardsPoint {
391 pub(crate) X: FieldElement,
392 pub(crate) Y: FieldElement,
393 pub(crate) Z: FieldElement,
394 pub(crate) T: FieldElement,
395}
396
397impl Identity for CompressedEdwardsY {
402 fn identity() -> CompressedEdwardsY {
403 CompressedEdwardsY([
404 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,
405 0, 0, 0,
406 ])
407 }
408}
409
410impl Default for CompressedEdwardsY {
411 fn default() -> CompressedEdwardsY {
412 CompressedEdwardsY::identity()
413 }
414}
415
416impl CompressedEdwardsY {
417 pub fn from_slice(bytes: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
424 bytes.try_into().map(CompressedEdwardsY)
425 }
426}
427
428impl Identity for EdwardsPoint {
429 fn identity() -> EdwardsPoint {
430 EdwardsPoint {
431 X: FieldElement::ZERO,
432 Y: FieldElement::ONE,
433 Z: FieldElement::ONE,
434 T: FieldElement::ZERO,
435 }
436 }
437}
438
439impl Default for EdwardsPoint {
440 fn default() -> EdwardsPoint {
441 EdwardsPoint::identity()
442 }
443}
444
445#[cfg(feature = "zeroize")]
450impl Zeroize for CompressedEdwardsY {
451 fn zeroize(&mut self) {
453 self.0.zeroize();
454 self.0[0] = 1;
455 }
456}
457
458#[cfg(feature = "zeroize")]
459impl Zeroize for EdwardsPoint {
460 fn zeroize(&mut self) {
462 self.X.zeroize();
463 self.Y = FieldElement::ONE;
464 self.Z = FieldElement::ONE;
465 self.T.zeroize();
466 }
467}
468
469impl ValidityCheck for EdwardsPoint {
474 fn is_valid(&self) -> bool {
475 let point_on_curve = self.as_projective().is_valid();
476 let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T);
477
478 point_on_curve && on_segre_image
479 }
480}
481
482impl ConditionallySelectable for EdwardsPoint {
487 fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint {
488 EdwardsPoint {
489 X: FieldElement::conditional_select(&a.X, &b.X, choice),
490 Y: FieldElement::conditional_select(&a.Y, &b.Y, choice),
491 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
492 T: FieldElement::conditional_select(&a.T, &b.T, choice),
493 }
494 }
495}
496
497impl ConstantTimeEq for EdwardsPoint {
502 fn ct_eq(&self, other: &EdwardsPoint) -> Choice {
503 (&self.X * &other.Z).ct_eq(&(&other.X * &self.Z))
510 & (&self.Y * &other.Z).ct_eq(&(&other.Y * &self.Z))
511 }
512}
513
514impl PartialEq for EdwardsPoint {
515 fn eq(&self, other: &EdwardsPoint) -> bool {
516 self.ct_eq(other).into()
517 }
518}
519
520impl Eq for EdwardsPoint {}
521
522impl EdwardsPoint {
527 pub(crate) fn as_projective_niels(&self) -> ProjectiveNielsPoint {
529 ProjectiveNielsPoint {
530 Y_plus_X: &self.Y + &self.X,
531 Y_minus_X: &self.Y - &self.X,
532 Z: self.Z,
533 T2d: &self.T * &constants::EDWARDS_D2,
534 }
535 }
536
537 pub(crate) const fn as_projective(&self) -> ProjectivePoint {
542 ProjectivePoint {
543 X: self.X,
544 Y: self.Y,
545 Z: self.Z,
546 }
547 }
548
549 pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint {
552 let recip = self.Z.invert();
553 let x = &self.X * &recip;
554 let y = &self.Y * &recip;
555 let xy2d = &(&x * &y) * &constants::EDWARDS_D2;
556 AffineNielsPoint {
557 y_plus_x: &y + &x,
558 y_minus_x: &y - &x,
559 xy2d,
560 }
561 }
562
563 pub(crate) fn to_affine(self) -> AffinePoint {
565 let recip = self.Z.invert();
566 let x = &self.X * &recip;
567 let y = &self.Y * &recip;
568 AffinePoint { x, y }
569 }
570
571 pub fn to_montgomery(&self) -> MontgomeryPoint {
581 let U = &self.Z + &self.Y;
587 let W = &self.Z - &self.Y;
588 let u = &U * &W.invert();
589 MontgomeryPoint(u.to_bytes())
590 }
591
592 #[cfg(feature = "alloc")]
595 pub fn to_montgomery_batch(eds: &[Self]) -> Vec<MontgomeryPoint> {
596 let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::<Vec<_>>();
602 FieldElement::invert_batch_alloc(&mut denominators);
603
604 let mut ret = Vec::with_capacity(eds.len());
606 for (ed, d) in eds.iter().zip(denominators.iter()) {
607 let u = &(&ed.Z + &ed.Y) * d;
608 ret.push(MontgomeryPoint(u.to_bytes()));
609 }
610
611 ret
612 }
613
614 pub fn compress(&self) -> CompressedEdwardsY {
616 self.to_affine().compress()
617 }
618
619 pub fn compress_batch<const N: usize>(inputs: &[EdwardsPoint; N]) -> [CompressedEdwardsY; N] {
622 let mut zs: [_; N] = core::array::from_fn(|i| inputs[i].Z);
623 FieldElement::invert_batch(&mut zs);
624
625 core::array::from_fn(|i| {
626 let x = &inputs[i].X * &zs[i];
627 let y = &inputs[i].Y * &zs[i];
628 AffinePoint { x, y }.compress()
629 })
630 }
631 #[cfg(feature = "alloc")]
634 pub fn compress_batch_alloc(inputs: &[EdwardsPoint]) -> Vec<CompressedEdwardsY> {
635 let mut zs = inputs.iter().map(|input| input.Z).collect::<Vec<_>>();
636 FieldElement::invert_batch_alloc(&mut zs);
637
638 inputs
639 .iter()
640 .zip(&zs)
641 .map(|(input, recip)| {
642 let x = &input.X * recip;
643 let y = &input.Y * recip;
644 AffinePoint { x, y }.compress()
645 })
646 .collect()
647 }
648
649 #[cfg(feature = "digest")]
650 fn map_to_curve(fe: FieldElement) -> EdwardsPoint {
652 let c1 = ED25519_SQRTAM2;
653
654 let (xMn, xMd, yMn, yMd) = crate::montgomery::elligator_encode(&fe);
656 let xn = &xMn * &yMd;
658 let xn = &xn * &c1;
660 let xd = &xMd * &yMn;
662 let yn = &xMn - &xMd;
664 let yd = &xMn + &xMd;
666 let tv1 = &xd * &yd;
668 let e = tv1.ct_eq(&FieldElement::ZERO);
670 let xn = FieldElement::conditional_select(&xn, &FieldElement::ZERO, e);
672 let xd = FieldElement::conditional_select(&xd, &FieldElement::ONE, e);
674 let yn = FieldElement::conditional_select(&yn, &FieldElement::ONE, e);
676 let yd = FieldElement::conditional_select(&yd, &FieldElement::ONE, e);
678 EdwardsPoint {
681 X: &xn * &yd,
682 Y: &xd * &yn,
683 Z: &xd * &yd,
684 T: &xn * &yn,
685 }
686 }
687
688 #[cfg(feature = "digest")]
689 pub fn encode_to_curve<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
711 where
712 D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
713 D::BlockSize: IsGreater<D::OutputSize, Output = True>,
714 {
715 let fe = FieldElement::hash_to_field::<D, 1>(bytes, domain_sep);
719 let Q = Self::map_to_curve(fe[0]);
720 Q.mul_by_cofactor()
721 }
722
723 #[cfg(feature = "digest")]
724 pub fn hash_to_curve<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
737 where
738 D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
739 D::BlockSize: IsGreater<D::OutputSize, Output = True>,
740 {
741 let fe = FieldElement::hash_to_field::<D, 2>(bytes, domain_sep);
745 let Q0 = Self::map_to_curve(fe[0]);
746 let Q1 = Self::map_to_curve(fe[1]);
747
748 let R = Q0 + Q1;
749 R.mul_by_cofactor()
750 }
751
752 #[cfg(feature = "rand_core")]
767 pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
768 let mut repr = CompressedEdwardsY([0u8; 32]);
769 loop {
770 rng.fill_bytes(&mut repr.0);
771 if let Some(p) = repr.decompress() {
772 if !IsIdentity::is_identity(&p) {
773 break p;
774 }
775 }
776 }
777 }
778}
779
780impl EdwardsPoint {
785 pub(crate) fn double(&self) -> EdwardsPoint {
787 self.as_projective().double().as_extended()
788 }
789}
790
791impl<'a> Add<&'a EdwardsPoint> for &EdwardsPoint {
796 type Output = EdwardsPoint;
797 fn add(self, other: &'a EdwardsPoint) -> EdwardsPoint {
798 (self + &other.as_projective_niels()).as_extended()
799 }
800}
801
802define_add_variants!(
803 LHS = EdwardsPoint,
804 RHS = EdwardsPoint,
805 Output = EdwardsPoint
806);
807
808impl<'a> AddAssign<&'a EdwardsPoint> for EdwardsPoint {
809 fn add_assign(&mut self, _rhs: &'a EdwardsPoint) {
810 *self = (self as &EdwardsPoint) + _rhs;
811 }
812}
813
814define_add_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
815
816impl<'a> Sub<&'a EdwardsPoint> for &EdwardsPoint {
817 type Output = EdwardsPoint;
818 fn sub(self, other: &'a EdwardsPoint) -> EdwardsPoint {
819 (self - &other.as_projective_niels()).as_extended()
820 }
821}
822
823define_sub_variants!(
824 LHS = EdwardsPoint,
825 RHS = EdwardsPoint,
826 Output = EdwardsPoint
827);
828
829impl<'a> SubAssign<&'a EdwardsPoint> for EdwardsPoint {
830 fn sub_assign(&mut self, _rhs: &'a EdwardsPoint) {
831 *self = (self as &EdwardsPoint) - _rhs;
832 }
833}
834
835define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
836
837impl<T> Sum<T> for EdwardsPoint
838where
839 T: Borrow<EdwardsPoint>,
840{
841 fn sum<I>(iter: I) -> Self
842 where
843 I: Iterator<Item = T>,
844 {
845 iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow())
846 }
847}
848
849impl Neg for &EdwardsPoint {
854 type Output = EdwardsPoint;
855
856 fn neg(self) -> EdwardsPoint {
857 EdwardsPoint {
858 X: -(&self.X),
859 Y: self.Y,
860 Z: self.Z,
861 T: -(&self.T),
862 }
863 }
864}
865
866impl Neg for EdwardsPoint {
867 type Output = EdwardsPoint;
868
869 fn neg(self) -> EdwardsPoint {
870 -&self
871 }
872}
873
874impl<'a> MulAssign<&'a Scalar> for EdwardsPoint {
879 fn mul_assign(&mut self, scalar: &'a Scalar) {
880 let result = (self as &EdwardsPoint) * scalar;
881 *self = result;
882 }
883}
884
885define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar);
886
887define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint);
888define_mul_variants!(LHS = Scalar, RHS = EdwardsPoint, Output = EdwardsPoint);
889
890impl<'a> Mul<&'a Scalar> for &EdwardsPoint {
891 type Output = EdwardsPoint;
892 fn mul(self, scalar: &'a Scalar) -> EdwardsPoint {
897 crate::backend::variable_base_mul(self, scalar)
898 }
899}
900
901impl<'a> Mul<&'a EdwardsPoint> for &Scalar {
902 type Output = EdwardsPoint;
903
904 fn mul(self, point: &'a EdwardsPoint) -> EdwardsPoint {
909 point * self
910 }
911}
912
913impl EdwardsPoint {
914 pub fn mul_base(scalar: &Scalar) -> Self {
919 #[cfg(not(feature = "precomputed-tables"))]
920 {
921 scalar * constants::ED25519_BASEPOINT_POINT
922 }
923
924 #[cfg(feature = "precomputed-tables")]
925 {
926 scalar * constants::ED25519_BASEPOINT_TABLE
927 }
928 }
929
930 pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
933 let s = Scalar {
941 bytes: clamp_integer(bytes),
942 };
943 s * self
944 }
945
946 pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
949 let s = Scalar {
953 bytes: clamp_integer(bytes),
954 };
955 Self::mul_base(&s)
956 }
957}
958
959#[cfg(feature = "alloc")]
967impl MultiscalarMul for EdwardsPoint {
968 type Point = EdwardsPoint;
969
970 fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
971 where
972 I: IntoIterator,
973 I::Item: Borrow<Scalar>,
974 J: IntoIterator,
975 J::Item: Borrow<EdwardsPoint>,
976 {
977 let mut scalars = scalars.into_iter();
979 let mut points = points.into_iter();
980
981 let (s_lo, s_hi) = scalars.by_ref().size_hint();
983 let (p_lo, p_hi) = points.by_ref().size_hint();
984
985 assert_eq!(s_lo, p_lo);
987 assert_eq!(s_hi, Some(s_lo));
988 assert_eq!(p_hi, Some(p_lo));
989
990 let _size = s_lo;
993
994 crate::backend::straus_multiscalar_mul(scalars, points)
995 }
996}
997
998#[cfg(feature = "alloc")]
999impl VartimeMultiscalarMul for EdwardsPoint {
1000 type Point = EdwardsPoint;
1001
1002 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
1003 where
1004 I: IntoIterator,
1005 I::Item: Borrow<Scalar>,
1006 J: IntoIterator<Item = Option<EdwardsPoint>>,
1007 {
1008 let mut scalars = scalars.into_iter();
1010 let mut points = points.into_iter();
1011
1012 let (s_lo, s_hi) = scalars.by_ref().size_hint();
1014 let (p_lo, p_hi) = points.by_ref().size_hint();
1015
1016 assert_eq!(s_lo, p_lo);
1018 assert_eq!(s_hi, Some(s_lo));
1019 assert_eq!(p_hi, Some(p_lo));
1020
1021 let size = s_lo;
1024
1025 if size < 190 {
1026 crate::backend::straus_optional_multiscalar_mul(scalars, points)
1027 } else {
1028 crate::backend::pippenger_optional_multiscalar_mul(scalars, points)
1029 }
1030 }
1031}
1032
1033#[cfg(feature = "alloc")]
1038pub struct VartimeEdwardsPrecomputation(crate::backend::VartimePrecomputedStraus);
1039
1040#[cfg(feature = "alloc")]
1041impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation {
1042 type Point = EdwardsPoint;
1043
1044 fn new<I>(static_points: I) -> Self
1045 where
1046 I: IntoIterator,
1047 I::Item: Borrow<Self::Point>,
1048 {
1049 Self(crate::backend::VartimePrecomputedStraus::new(static_points))
1050 }
1051
1052 fn len(&self) -> usize {
1053 self.0.len()
1054 }
1055
1056 fn is_empty(&self) -> bool {
1057 self.0.is_empty()
1058 }
1059
1060 fn optional_mixed_multiscalar_mul<I, J, K>(
1061 &self,
1062 static_scalars: I,
1063 dynamic_scalars: J,
1064 dynamic_points: K,
1065 ) -> Option<Self::Point>
1066 where
1067 I: IntoIterator,
1068 I::Item: Borrow<Scalar>,
1069 J: IntoIterator,
1070 J::Item: Borrow<Scalar>,
1071 K: IntoIterator<Item = Option<Self::Point>>,
1072 {
1073 self.0
1074 .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points)
1075 }
1076}
1077
1078impl EdwardsPoint {
1079 pub fn vartime_double_scalar_mul_basepoint(
1081 a: &Scalar,
1082 A: &EdwardsPoint,
1083 b: &Scalar,
1084 ) -> EdwardsPoint {
1085 crate::backend::vartime_double_base_mul(a, A, b)
1086 }
1087}
1088
1089#[cfg(feature = "precomputed-tables")]
1090macro_rules! impl_basepoint_table {
1091 (Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => {
1092 #[derive(Clone)]
1124 #[repr(transparent)]
1125 pub struct $name(pub(crate) [$table<AffineNielsPoint>; 32]);
1126
1127 impl BasepointTable for $name {
1128 type Point = $point;
1129
1130 fn create(basepoint: &$point) -> $name {
1132 let mut table = $name([$table::default(); 32]);
1134 let mut P = *basepoint;
1135 for i in 0..32 {
1136 table.0[i] = $table::from(&P);
1138 P = P.mul_by_pow_2($radix + $radix);
1139 }
1140 table
1141 }
1142
1143 fn basepoint(&self) -> $point {
1145 (&<$point>::identity() + &self.0[0].select(1)).as_extended()
1148 }
1149
1150 fn mul_base(&self, scalar: &Scalar) -> $point {
1193 let a = scalar.as_radix_2w($radix);
1194
1195 let tables = &self.0;
1196 let mut P = <$point>::identity();
1197
1198 for i in (0..$adds).filter(|x| x % 2 == 1) {
1199 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1200 }
1201
1202 P = P.mul_by_pow_2($radix);
1203
1204 for i in (0..$adds).filter(|x| x % 2 == 0) {
1205 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1206 }
1207
1208 P
1209 }
1210 }
1211
1212 impl<'a, 'b> Mul<&'b Scalar> for &'a $name {
1213 type Output = $point;
1214
1215 fn mul(self, scalar: &'b Scalar) -> $point {
1218 self.mul_base(scalar)
1220 }
1221 }
1222
1223 impl<'a, 'b> Mul<&'a $name> for &'b Scalar {
1224 type Output = $point;
1225
1226 fn mul(self, basepoint_table: &'a $name) -> $point {
1229 basepoint_table * self
1230 }
1231 }
1232
1233 impl Debug for $name {
1234 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1235 write!(f, "{:?}([\n", stringify!($name))?;
1236 for i in 0..32 {
1237 write!(f, "\t{:?},\n", &self.0[i])?;
1238 }
1239 write!(f, "])")
1240 }
1241 }
1242 };
1243} cfg_if! {
1247 if #[cfg(feature = "precomputed-tables")] {
1248 impl_basepoint_table! {
1249 Name = EdwardsBasepointTable,
1250 LookupTable = LookupTableRadix16,
1251 Point = EdwardsPoint,
1252 Radix = 4,
1253 Additions = 64
1254 }
1255 impl_basepoint_table! {
1256 Name = EdwardsBasepointTableRadix32,
1257 LookupTable = LookupTableRadix32,
1258 Point = EdwardsPoint,
1259 Radix = 5,
1260 Additions = 52
1261 }
1262 impl_basepoint_table! {
1263 Name = EdwardsBasepointTableRadix64,
1264 LookupTable = LookupTableRadix64,
1265 Point = EdwardsPoint,
1266 Radix = 6,
1267 Additions = 43
1268 }
1269 impl_basepoint_table! {
1270 Name = EdwardsBasepointTableRadix128,
1271 LookupTable = LookupTableRadix128,
1272 Point = EdwardsPoint,
1273 Radix = 7,
1274 Additions = 37
1275 }
1276 impl_basepoint_table! {
1277 Name = EdwardsBasepointTableRadix256,
1278 LookupTable = LookupTableRadix256,
1279 Point = EdwardsPoint,
1280 Radix = 8,
1281 Additions = 33
1282 }
1283
1284 pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable;
1291 }
1292}
1293
1294#[cfg(feature = "precomputed-tables")]
1295macro_rules! impl_basepoint_table_conversions {
1296 (LHS = $lhs:ty, RHS = $rhs:ty) => {
1297 impl<'a> From<&'a $lhs> for $rhs {
1298 fn from(table: &'a $lhs) -> $rhs {
1299 <$rhs>::create(&table.basepoint())
1300 }
1301 }
1302
1303 impl<'a> From<&'a $rhs> for $lhs {
1304 fn from(table: &'a $rhs) -> $lhs {
1305 <$lhs>::create(&table.basepoint())
1306 }
1307 }
1308 };
1309}
1310
1311cfg_if! {
1312 if #[cfg(feature = "precomputed-tables")] {
1313 impl_basepoint_table_conversions! {
1315 LHS = EdwardsBasepointTableRadix16,
1316 RHS = EdwardsBasepointTableRadix32
1317 }
1318 impl_basepoint_table_conversions! {
1319 LHS = EdwardsBasepointTableRadix16,
1320 RHS = EdwardsBasepointTableRadix64
1321 }
1322 impl_basepoint_table_conversions! {
1323 LHS = EdwardsBasepointTableRadix16,
1324 RHS = EdwardsBasepointTableRadix128
1325 }
1326 impl_basepoint_table_conversions! {
1327 LHS = EdwardsBasepointTableRadix16,
1328 RHS = EdwardsBasepointTableRadix256
1329 }
1330
1331 impl_basepoint_table_conversions! {
1333 LHS = EdwardsBasepointTableRadix32,
1334 RHS = EdwardsBasepointTableRadix64
1335 }
1336 impl_basepoint_table_conversions! {
1337 LHS = EdwardsBasepointTableRadix32,
1338 RHS = EdwardsBasepointTableRadix128
1339 }
1340 impl_basepoint_table_conversions! {
1341 LHS = EdwardsBasepointTableRadix32,
1342 RHS = EdwardsBasepointTableRadix256
1343 }
1344
1345 impl_basepoint_table_conversions! {
1347 LHS = EdwardsBasepointTableRadix64,
1348 RHS = EdwardsBasepointTableRadix128
1349 }
1350 impl_basepoint_table_conversions! {
1351 LHS = EdwardsBasepointTableRadix64,
1352 RHS = EdwardsBasepointTableRadix256
1353 }
1354
1355 impl_basepoint_table_conversions! {
1357 LHS = EdwardsBasepointTableRadix128,
1358 RHS = EdwardsBasepointTableRadix256
1359 }
1360 }
1361}
1362
1363impl EdwardsPoint {
1364 pub fn mul_by_cofactor(&self) -> EdwardsPoint {
1366 self.mul_by_pow_2(3)
1367 }
1368
1369 pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint {
1371 debug_assert!(k > 0);
1372 let mut r: CompletedPoint;
1373 let mut s = self.as_projective();
1374 for _ in 0..(k - 1) {
1375 r = s.double();
1376 s = r.as_projective();
1377 }
1378 s.double().as_extended()
1380 }
1381
1382 pub fn is_small_order(&self) -> bool {
1406 self.mul_by_cofactor().is_identity()
1407 }
1408
1409 pub fn is_torsion_free(&self) -> bool {
1436 (self * constants::BASEPOINT_ORDER).is_identity()
1437 }
1438}
1439
1440impl Debug for EdwardsPoint {
1445 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1446 write!(
1447 f,
1448 "EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
1449 &self.X, &self.Y, &self.Z, &self.T
1450 )
1451 }
1452}
1453
1454#[cfg(feature = "group")]
1461impl group::Group for EdwardsPoint {
1462 type Scalar = Scalar;
1463
1464 fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
1465 let mut repr = CompressedEdwardsY([0u8; 32]);
1466 loop {
1467 rng.try_fill_bytes(&mut repr.0)?;
1468 if let Some(p) = repr.decompress() {
1469 if !IsIdentity::is_identity(&p) {
1470 break Ok(p);
1471 }
1472 }
1473 }
1474 }
1475
1476 fn identity() -> Self {
1477 Identity::identity()
1478 }
1479
1480 fn generator() -> Self {
1481 constants::ED25519_BASEPOINT_POINT
1482 }
1483
1484 fn is_identity(&self) -> Choice {
1485 self.ct_eq(&Identity::identity())
1486 }
1487
1488 fn double(&self) -> Self {
1489 self.double()
1490 }
1491}
1492
1493#[cfg(feature = "group")]
1494impl GroupEncoding for EdwardsPoint {
1495 type Repr = [u8; 32];
1496
1497 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1498 let repr = CompressedEdwardsY(*bytes);
1499 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr);
1500 CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord)
1501 }
1502
1503 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1504 Self::from_bytes(bytes)
1506 }
1507
1508 fn to_bytes(&self) -> Self::Repr {
1509 self.compress().to_bytes()
1510 }
1511}
1512
1513#[cfg(feature = "group")]
1516#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1517pub struct SubgroupPoint(EdwardsPoint);
1518
1519#[cfg(feature = "group")]
1520impl From<SubgroupPoint> for EdwardsPoint {
1521 fn from(p: SubgroupPoint) -> Self {
1522 p.0
1523 }
1524}
1525
1526#[cfg(feature = "group")]
1527impl Neg for SubgroupPoint {
1528 type Output = Self;
1529
1530 fn neg(self) -> Self::Output {
1531 SubgroupPoint(-self.0)
1532 }
1533}
1534
1535#[cfg(feature = "group")]
1536impl Add<&SubgroupPoint> for &SubgroupPoint {
1537 type Output = SubgroupPoint;
1538 fn add(self, other: &SubgroupPoint) -> SubgroupPoint {
1539 SubgroupPoint(self.0 + other.0)
1540 }
1541}
1542
1543#[cfg(feature = "group")]
1544define_add_variants!(
1545 LHS = SubgroupPoint,
1546 RHS = SubgroupPoint,
1547 Output = SubgroupPoint
1548);
1549
1550#[cfg(feature = "group")]
1551impl Add<&SubgroupPoint> for &EdwardsPoint {
1552 type Output = EdwardsPoint;
1553 fn add(self, other: &SubgroupPoint) -> EdwardsPoint {
1554 self + other.0
1555 }
1556}
1557
1558#[cfg(feature = "group")]
1559define_add_variants!(
1560 LHS = EdwardsPoint,
1561 RHS = SubgroupPoint,
1562 Output = EdwardsPoint
1563);
1564
1565#[cfg(feature = "group")]
1566impl AddAssign<&SubgroupPoint> for SubgroupPoint {
1567 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1568 self.0 += rhs.0
1569 }
1570}
1571
1572#[cfg(feature = "group")]
1573define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1574
1575#[cfg(feature = "group")]
1576impl AddAssign<&SubgroupPoint> for EdwardsPoint {
1577 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1578 *self += rhs.0
1579 }
1580}
1581
1582#[cfg(feature = "group")]
1583define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1584
1585#[cfg(feature = "group")]
1586impl Sub<&SubgroupPoint> for &SubgroupPoint {
1587 type Output = SubgroupPoint;
1588 fn sub(self, other: &SubgroupPoint) -> SubgroupPoint {
1589 SubgroupPoint(self.0 - other.0)
1590 }
1591}
1592
1593#[cfg(feature = "group")]
1594define_sub_variants!(
1595 LHS = SubgroupPoint,
1596 RHS = SubgroupPoint,
1597 Output = SubgroupPoint
1598);
1599
1600#[cfg(feature = "group")]
1601impl Sub<&SubgroupPoint> for &EdwardsPoint {
1602 type Output = EdwardsPoint;
1603 fn sub(self, other: &SubgroupPoint) -> EdwardsPoint {
1604 self - other.0
1605 }
1606}
1607
1608#[cfg(feature = "group")]
1609define_sub_variants!(
1610 LHS = EdwardsPoint,
1611 RHS = SubgroupPoint,
1612 Output = EdwardsPoint
1613);
1614
1615#[cfg(feature = "group")]
1616impl SubAssign<&SubgroupPoint> for SubgroupPoint {
1617 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1618 self.0 -= rhs.0;
1619 }
1620}
1621
1622#[cfg(feature = "group")]
1623define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1624
1625#[cfg(feature = "group")]
1626impl SubAssign<&SubgroupPoint> for EdwardsPoint {
1627 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1628 *self -= rhs.0;
1629 }
1630}
1631
1632#[cfg(feature = "group")]
1633define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1634
1635#[cfg(feature = "group")]
1636impl<T> Sum<T> for SubgroupPoint
1637where
1638 T: Borrow<SubgroupPoint>,
1639{
1640 fn sum<I>(iter: I) -> Self
1641 where
1642 I: Iterator<Item = T>,
1643 {
1644 use group::Group;
1645 iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow())
1646 }
1647}
1648
1649#[cfg(feature = "group")]
1650impl Mul<&Scalar> for &SubgroupPoint {
1651 type Output = SubgroupPoint;
1652
1653 fn mul(self, scalar: &Scalar) -> SubgroupPoint {
1658 SubgroupPoint(self.0 * scalar)
1659 }
1660}
1661
1662#[cfg(feature = "group")]
1663define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint);
1664
1665#[cfg(feature = "group")]
1666impl Mul<&SubgroupPoint> for &Scalar {
1667 type Output = SubgroupPoint;
1668
1669 fn mul(self, point: &SubgroupPoint) -> SubgroupPoint {
1674 point * self
1675 }
1676}
1677
1678#[cfg(feature = "group")]
1679define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint);
1680
1681#[cfg(feature = "group")]
1682impl MulAssign<&Scalar> for SubgroupPoint {
1683 fn mul_assign(&mut self, scalar: &Scalar) {
1684 self.0 *= scalar;
1685 }
1686}
1687
1688#[cfg(feature = "group")]
1689define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);
1690
1691#[cfg(feature = "group")]
1692impl ConstantTimeEq for SubgroupPoint {
1693 fn ct_eq(&self, other: &SubgroupPoint) -> Choice {
1694 self.0.ct_eq(&other.0)
1695 }
1696}
1697
1698#[cfg(feature = "group")]
1699impl ConditionallySelectable for SubgroupPoint {
1700 fn conditional_select(a: &SubgroupPoint, b: &SubgroupPoint, choice: Choice) -> SubgroupPoint {
1701 SubgroupPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice))
1702 }
1703}
1704
1705#[cfg(all(feature = "group", feature = "zeroize"))]
1706impl Zeroize for SubgroupPoint {
1707 fn zeroize(&mut self) {
1708 self.0.zeroize();
1709 }
1710}
1711
1712#[cfg(feature = "group")]
1713impl group::Group for SubgroupPoint {
1714 type Scalar = Scalar;
1715
1716 fn try_random<R: TryRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
1717 use group::ff::Field;
1718
1719 let s = loop {
1722 let s: Scalar = Field::try_random(rng)?;
1723 if !s.is_zero_vartime() {
1724 break s;
1725 }
1726 };
1727
1728 Ok(Self::generator() * s)
1730 }
1731
1732 fn identity() -> Self {
1733 SubgroupPoint(Identity::identity())
1734 }
1735
1736 fn generator() -> Self {
1737 SubgroupPoint(EdwardsPoint::generator())
1738 }
1739
1740 fn is_identity(&self) -> Choice {
1741 self.0.ct_eq(&Identity::identity())
1742 }
1743
1744 fn double(&self) -> Self {
1745 SubgroupPoint(self.0.double())
1746 }
1747}
1748
1749#[cfg(feature = "group")]
1750impl GroupEncoding for SubgroupPoint {
1751 type Repr = <EdwardsPoint as GroupEncoding>::Repr;
1752
1753 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1754 EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1755 }
1756
1757 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1758 EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup())
1759 }
1760
1761 fn to_bytes(&self) -> Self::Repr {
1762 self.0.compress().to_bytes()
1763 }
1764}
1765
1766#[cfg(feature = "group")]
1767impl PrimeGroup for SubgroupPoint {}
1768
1769#[cfg(feature = "group")]
1770impl CofactorGroup for EdwardsPoint {
1771 type Subgroup = SubgroupPoint;
1772
1773 fn clear_cofactor(&self) -> Self::Subgroup {
1774 SubgroupPoint(self.mul_by_cofactor())
1775 }
1776
1777 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1778 CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
1779 }
1780
1781 fn is_torsion_free(&self) -> Choice {
1782 (self * constants::BASEPOINT_ORDER).ct_eq(&Self::identity())
1783 }
1784}
1785
1786#[cfg(test)]
1791mod test {
1792 use super::*;
1793 use getrandom::{
1794 SysRng,
1795 rand_core::{TryRng, UnwrapErr},
1796 };
1797
1798 #[cfg(feature = "alloc")]
1799 use alloc::vec::Vec;
1800
1801 #[cfg(feature = "precomputed-tables")]
1802 use crate::constants::ED25519_BASEPOINT_TABLE;
1803
1804 static BASE_X_COORD_BYTES: [u8; 32] = [
1807 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c,
1808 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36,
1809 0x69, 0x21,
1810 ];
1811
1812 static BASE2_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1814 0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39,
1815 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd,
1816 0x60, 0x22,
1817 ]);
1818
1819 static BASE16_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1821 0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a,
1822 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96,
1823 0x96, 0x70,
1824 ]);
1825
1826 pub static A_SCALAR: Scalar = Scalar {
1828 bytes: [
1829 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
1830 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
1831 0x23, 0x76, 0xef, 0x09,
1832 ],
1833 };
1834
1835 pub static B_SCALAR: Scalar = Scalar {
1837 bytes: [
1838 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0,
1839 0xb3, 0x2e, 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, 0x56, 0xa7, 0xd4, 0xaa,
1840 0xb8, 0x60, 0x8a, 0x05,
1841 ],
1842 };
1843
1844 pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([
1846 0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59, 0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34,
1847 0xc3, 0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e, 0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e,
1848 0x40, 0xa5,
1849 ]);
1850
1851 static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([
1854 0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e, 0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59,
1855 0xc4, 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13,
1856 0x2b, 0x42,
1857 ]);
1858
1859 #[test]
1861 fn basepoint_decompression_compression() {
1862 let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES);
1863 let bp = constants::ED25519_BASEPOINT_COMPRESSED
1864 .decompress()
1865 .unwrap();
1866 assert!(bp.is_valid());
1867 assert_eq!(base_X, bp.X);
1869 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1870 }
1871
1872 #[test]
1874 fn decompression_sign_handling() {
1875 let mut minus_basepoint_bytes = *constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
1877 minus_basepoint_bytes[31] |= 1 << 7;
1878 let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes)
1879 .decompress()
1880 .unwrap();
1881 assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X));
1884 assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y);
1885 assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z);
1886 assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T));
1887 }
1888
1889 #[cfg(feature = "precomputed-tables")]
1891 #[test]
1892 fn basepoint_mult_one_vs_basepoint() {
1893 let bp = ED25519_BASEPOINT_TABLE * &Scalar::ONE;
1894 let compressed = bp.compress();
1895 assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED);
1896 }
1897
1898 #[cfg(feature = "precomputed-tables")]
1900 #[test]
1901 fn basepoint_table_basepoint_function_correct() {
1902 let bp = ED25519_BASEPOINT_TABLE.basepoint();
1903 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1904 }
1905
1906 #[test]
1909 fn basepoint_plus_basepoint_vs_basepoint2() {
1910 let bp = constants::ED25519_BASEPOINT_POINT;
1911 let bp_added = bp + bp;
1912 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1913 }
1914
1915 #[test]
1918 fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() {
1919 let bp = constants::ED25519_BASEPOINT_POINT;
1920 let bp_added = (&bp + &bp.as_projective_niels()).as_extended();
1921 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1922 }
1923
1924 #[test]
1927 fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() {
1928 let bp = constants::ED25519_BASEPOINT_POINT;
1929 let bp_affine_niels = bp.as_affine_niels();
1930 let bp_added = (&bp + &bp_affine_niels).as_extended();
1931 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1932 }
1933
1934 #[test]
1937 fn extended_point_equality_handles_scaling() {
1938 let mut two_bytes = [0u8; 32];
1939 two_bytes[0] = 2;
1940 let id1 = EdwardsPoint::identity();
1941 let id2 = EdwardsPoint {
1942 X: FieldElement::ZERO,
1943 Y: FieldElement::from_bytes(&two_bytes),
1944 Z: FieldElement::from_bytes(&two_bytes),
1945 T: FieldElement::ZERO,
1946 };
1947 assert!(bool::from(id1.ct_eq(&id2)));
1948 }
1949
1950 #[cfg(feature = "precomputed-tables")]
1952 #[test]
1953 fn to_affine_niels_clears_denominators() {
1954 let aB = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1956 let aB_affine_niels = aB.as_affine_niels();
1957 let also_aB = (&EdwardsPoint::identity() + &aB_affine_niels).as_extended();
1958 assert_eq!(aB.compress(), also_aB.compress());
1959 }
1960
1961 #[test]
1963 fn basepoint_mult_vs_ed25519py() {
1964 let aB = EdwardsPoint::mul_base(&A_SCALAR);
1965 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1966 }
1967
1968 #[test]
1970 fn basepoint_mult_by_basepoint_order() {
1971 let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER);
1972 assert!(should_be_id.is_identity());
1973 }
1974
1975 #[cfg(feature = "precomputed-tables")]
1977 #[test]
1978 fn test_precomputed_basepoint_mult() {
1979 let aB_1 = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1980 let aB_2 = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1981 assert_eq!(aB_1.compress(), aB_2.compress());
1982 }
1983
1984 #[test]
1986 fn scalar_mul_vs_ed25519py() {
1987 let aB = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1988 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1989 }
1990
1991 #[test]
1993 fn basepoint_double_vs_basepoint2() {
1994 assert_eq!(
1995 constants::ED25519_BASEPOINT_POINT.double().compress(),
1996 BASE2_CMPRSSD
1997 );
1998 }
1999
2000 #[test]
2002 fn basepoint_mult_two_vs_basepoint2() {
2003 let two = Scalar::from(2u64);
2004 let bp2 = EdwardsPoint::mul_base(&two);
2005 assert_eq!(bp2.compress(), BASE2_CMPRSSD);
2006 }
2007
2008 #[cfg(feature = "precomputed-tables")]
2010 #[test]
2011 fn basepoint_tables() {
2012 let P = &constants::ED25519_BASEPOINT_POINT;
2013 let a = A_SCALAR;
2014
2015 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
2016 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
2017 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
2018 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
2019 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
2020
2021 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
2022 let aP16 = (&table_radix16 * &a).compress();
2023 let aP32 = (&table_radix32 * &a).compress();
2024 let aP64 = (&table_radix64 * &a).compress();
2025 let aP128 = (&table_radix128 * &a).compress();
2026 let aP256 = (&table_radix256 * &a).compress();
2027
2028 assert_eq!(aP, aP16);
2029 assert_eq!(aP16, aP32);
2030 assert_eq!(aP32, aP64);
2031 assert_eq!(aP64, aP128);
2032 assert_eq!(aP128, aP256);
2033 }
2034
2035 #[cfg(feature = "precomputed-tables")]
2038 #[test]
2039 fn basepoint_tables_unreduced_scalar() {
2040 let P = &constants::ED25519_BASEPOINT_POINT;
2041 let a = crate::scalar::test::LARGEST_UNREDUCED_SCALAR;
2042
2043 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
2044 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
2045 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
2046 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
2047 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
2048
2049 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
2050 let aP16 = (&table_radix16 * &a).compress();
2051 let aP32 = (&table_radix32 * &a).compress();
2052 let aP64 = (&table_radix64 * &a).compress();
2053 let aP128 = (&table_radix128 * &a).compress();
2054 let aP256 = (&table_radix256 * &a).compress();
2055
2056 assert_eq!(aP, aP16);
2057 assert_eq!(aP16, aP32);
2058 assert_eq!(aP32, aP64);
2059 assert_eq!(aP64, aP128);
2060 assert_eq!(aP128, aP256);
2061 }
2062
2063 #[test]
2065 fn basepoint_projective_extended_round_trip() {
2066 assert_eq!(
2067 constants::ED25519_BASEPOINT_POINT
2068 .as_projective()
2069 .as_extended()
2070 .compress(),
2071 constants::ED25519_BASEPOINT_COMPRESSED
2072 );
2073 }
2074
2075 #[test]
2077 fn basepoint16_vs_mul_by_pow_2_4() {
2078 let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4);
2079 assert_eq!(bp16.compress(), BASE16_CMPRSSD);
2080 }
2081
2082 #[test]
2084 fn mul_base_clamped() {
2085 let mut csprng = UnwrapErr(SysRng);
2086
2087 #[cfg(feature = "precomputed-tables")]
2089 let random_point = {
2090 let mut b = [0u8; 32];
2091 csprng.try_fill_bytes(&mut b).unwrap();
2092 EdwardsPoint::mul_base_clamped(b) + constants::EIGHT_TORSION[1]
2093 };
2094 #[cfg(feature = "precomputed-tables")]
2096 let random_table = EdwardsBasepointTableRadix256::create(&random_point);
2097
2098 let a_bytes = [0xff; 32];
2103 assert_eq!(
2104 EdwardsPoint::mul_base_clamped(a_bytes),
2105 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2106 );
2107 #[cfg(feature = "precomputed-tables")]
2108 assert_eq!(
2109 random_table.mul_base_clamped(a_bytes),
2110 random_point.mul_clamped(a_bytes)
2111 );
2112
2113 for _ in 0..100 {
2115 let mut a_bytes = [0u8; 32];
2117 csprng.try_fill_bytes(&mut a_bytes).unwrap();
2118
2119 assert_eq!(
2120 EdwardsPoint::mul_base_clamped(a_bytes),
2121 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2122 );
2123 #[cfg(feature = "precomputed-tables")]
2124 assert_eq!(
2125 random_table.mul_base_clamped(a_bytes),
2126 random_point.mul_clamped(a_bytes)
2127 );
2128 }
2129 }
2130
2131 #[test]
2132 #[cfg(feature = "alloc")]
2133 fn impl_sum() {
2134 let BASE = constants::ED25519_BASEPOINT_POINT;
2136
2137 let s1 = Scalar::from(999u64);
2138 let P1 = BASE * s1;
2139
2140 let s2 = Scalar::from(333u64);
2141 let P2 = BASE * s2;
2142
2143 let vec = vec![P1, P2];
2144 let sum: EdwardsPoint = vec.iter().sum();
2145
2146 assert_eq!(sum, P1 + P2);
2147
2148 let empty_vector: Vec<EdwardsPoint> = vec![];
2150 let sum: EdwardsPoint = empty_vector.iter().sum();
2151
2152 assert_eq!(sum, EdwardsPoint::identity());
2153
2154 let s = Scalar::from(2u64);
2156 let mapped = vec.iter().map(|x| x * s);
2157 let sum: EdwardsPoint = mapped.sum();
2158
2159 assert_eq!(sum, P1 * s + P2 * s);
2160 }
2161
2162 #[test]
2164 fn conditional_assign_for_affine_niels_point() {
2165 let id = AffineNielsPoint::identity();
2166 let mut p1 = AffineNielsPoint::identity();
2167 let bp = constants::ED25519_BASEPOINT_POINT.as_affine_niels();
2168
2169 p1.conditional_assign(&bp, Choice::from(0));
2170 assert_eq!(p1, id);
2171 p1.conditional_assign(&bp, Choice::from(1));
2172 assert_eq!(p1, bp);
2173 }
2174
2175 #[test]
2176 fn is_small_order() {
2177 assert!(!constants::ED25519_BASEPOINT_POINT.is_small_order());
2179 for torsion_point in &constants::EIGHT_TORSION {
2181 assert!(torsion_point.is_small_order());
2182 }
2183 }
2184
2185 #[test]
2186 fn compressed_identity() {
2187 assert_eq!(
2188 EdwardsPoint::identity().compress(),
2189 CompressedEdwardsY::identity()
2190 );
2191
2192 assert_eq!(
2193 EdwardsPoint::compress_batch(&[EdwardsPoint::identity()]),
2194 [CompressedEdwardsY::identity()]
2195 );
2196 #[cfg(feature = "alloc")]
2197 assert_eq!(
2198 &EdwardsPoint::compress_batch_alloc(&[EdwardsPoint::identity()]),
2199 &[CompressedEdwardsY::identity()]
2200 );
2201 }
2202
2203 #[cfg(feature = "rand_core")]
2204 #[test]
2205 fn compress_batch() {
2206 let mut rng = UnwrapErr(SysRng);
2207
2208 const TEST_VEC_LEN: usize = 117;
2212 let points: [EdwardsPoint; TEST_VEC_LEN] = core::array::from_fn(|i| {
2213 if i < 17 {
2214 constants::ED25519_BASEPOINT_POINT * Scalar::from(i as u64)
2216 } else {
2217 EdwardsPoint::random(&mut rng)
2219 }
2220 });
2221
2222 let expected_compressed = core::array::from_fn(|i| points[i].compress());
2224
2225 assert_eq!(EdwardsPoint::compress_batch(&points), expected_compressed);
2227
2228 #[cfg(feature = "alloc")]
2231 assert_eq!(
2232 EdwardsPoint::compress_batch_alloc(&points),
2233 expected_compressed
2234 );
2235 }
2236
2237 #[test]
2238 fn is_identity() {
2239 assert!(EdwardsPoint::identity().is_identity());
2240 assert!(!constants::ED25519_BASEPOINT_POINT.is_identity());
2241 }
2242
2243 #[test]
2254 fn monte_carlo_overflow_underflow_debug_assert_test() {
2255 let mut P = constants::ED25519_BASEPOINT_POINT;
2256 for _ in 0..1_000 {
2259 P *= &A_SCALAR;
2260 }
2261 }
2262
2263 #[test]
2264 fn scalarmult_extended_point_works_both_ways() {
2265 let G: EdwardsPoint = constants::ED25519_BASEPOINT_POINT;
2266 let s: Scalar = A_SCALAR;
2267
2268 let P1 = G * s;
2269 let P2 = s * G;
2270
2271 assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
2272 }
2273
2274 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2276 fn multiscalar_consistency_iter(n: usize) {
2277 let mut rng = UnwrapErr(SysRng);
2278
2279 let xs = (0..n).map(|_| Scalar::random(&mut rng)).collect::<Vec<_>>();
2282 let check = xs.iter().map(|xi| xi * xi).sum::<Scalar>();
2283
2284 let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::<Vec<_>>();
2286
2287 let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs);
2289 let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs);
2291 let H3 = EdwardsPoint::mul_base(&check);
2293
2294 assert_eq!(H1, H3);
2295 assert_eq!(H2, H3);
2296 }
2297
2298 #[test]
2302 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2303 fn multiscalar_consistency_n_100() {
2304 let iters = 50;
2305 for _ in 0..iters {
2306 multiscalar_consistency_iter(100);
2307 }
2308 }
2309
2310 #[test]
2311 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2312 fn multiscalar_consistency_n_250() {
2313 let iters = 50;
2314 for _ in 0..iters {
2315 multiscalar_consistency_iter(250);
2316 }
2317 }
2318
2319 #[test]
2320 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2321 fn multiscalar_consistency_n_500() {
2322 let iters = 50;
2323 for _ in 0..iters {
2324 multiscalar_consistency_iter(500);
2325 }
2326 }
2327
2328 #[test]
2329 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2330 fn multiscalar_consistency_n_1000() {
2331 let iters = 50;
2332 for _ in 0..iters {
2333 multiscalar_consistency_iter(1000);
2334 }
2335 }
2336
2337 #[test]
2338 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2339 fn batch_to_montgomery() {
2340 let mut rng = UnwrapErr(SysRng);
2341
2342 let scalars = (0..128)
2343 .map(|_| Scalar::random(&mut rng))
2344 .collect::<Vec<_>>();
2345
2346 let points = scalars
2347 .iter()
2348 .map(EdwardsPoint::mul_base)
2349 .collect::<Vec<_>>();
2350
2351 let single_monts = points
2352 .iter()
2353 .map(EdwardsPoint::to_montgomery)
2354 .collect::<Vec<_>>();
2355
2356 for i in [0, 1, 2, 3, 10, 50, 128] {
2357 let invs = EdwardsPoint::to_montgomery_batch(&points[..i]);
2358 assert_eq!(&invs, &single_monts[..i]);
2359 }
2360 }
2361
2362 #[test]
2363 #[cfg(all(feature = "alloc", feature = "rand_core"))]
2364 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
2365 let mut rng = UnwrapErr(SysRng);
2366
2367 let static_scalars = (0..128)
2368 .map(|_| Scalar::random(&mut rng))
2369 .collect::<Vec<_>>();
2370
2371 let dynamic_scalars = (0..128)
2372 .map(|_| Scalar::random(&mut rng))
2373 .collect::<Vec<_>>();
2374
2375 let check_scalar: Scalar = static_scalars
2376 .iter()
2377 .chain(dynamic_scalars.iter())
2378 .map(|s| s * s)
2379 .sum();
2380
2381 let static_points = static_scalars
2382 .iter()
2383 .map(EdwardsPoint::mul_base)
2384 .collect::<Vec<_>>();
2385 let dynamic_points = dynamic_scalars
2386 .iter()
2387 .map(EdwardsPoint::mul_base)
2388 .collect::<Vec<_>>();
2389
2390 let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter());
2391
2392 assert_eq!(precomputation.len(), 128);
2393 assert!(!precomputation.is_empty());
2394
2395 let P = precomputation.vartime_mixed_multiscalar_mul(
2396 &static_scalars,
2397 &dynamic_scalars,
2398 &dynamic_points,
2399 );
2400
2401 use crate::traits::VartimeMultiscalarMul;
2402 let Q = EdwardsPoint::vartime_multiscalar_mul(
2403 static_scalars.iter().chain(dynamic_scalars.iter()),
2404 static_points.iter().chain(dynamic_points.iter()),
2405 );
2406
2407 let R = EdwardsPoint::mul_base(&check_scalar);
2408
2409 assert_eq!(P.compress(), R.compress());
2410 assert_eq!(Q.compress(), R.compress());
2411 }
2412
2413 mod vartime {
2414 use super::super::*;
2415 use super::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR, DOUBLE_SCALAR_MULT_RESULT};
2416
2417 #[test]
2419 fn double_scalar_mul_basepoint_vs_ed25519py() {
2420 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2421 let result =
2422 EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
2423 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2424 }
2425
2426 #[test]
2427 #[cfg(feature = "alloc")]
2428 fn multiscalar_mul_vs_ed25519py() {
2429 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2430 let result = EdwardsPoint::vartime_multiscalar_mul(
2431 &[A_SCALAR, B_SCALAR],
2432 &[A, constants::ED25519_BASEPOINT_POINT],
2433 );
2434 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2435 }
2436
2437 #[test]
2438 #[cfg(feature = "alloc")]
2439 fn multiscalar_mul_vartime_vs_consttime() {
2440 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2441 let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
2442 &[A_SCALAR, B_SCALAR],
2443 &[A, constants::ED25519_BASEPOINT_POINT],
2444 );
2445 let result_consttime = EdwardsPoint::multiscalar_mul(
2446 &[A_SCALAR, B_SCALAR],
2447 &[A, constants::ED25519_BASEPOINT_POINT],
2448 );
2449
2450 assert_eq!(result_vartime.compress(), result_consttime.compress());
2451 }
2452 }
2453
2454 #[test]
2455 #[cfg(feature = "serde")]
2456 fn serde_postcard_basepoint_roundtrip() {
2457 let encoded = postcard::to_allocvec(&constants::ED25519_BASEPOINT_POINT).unwrap();
2458 let enc_compressed =
2459 postcard::to_allocvec(&constants::ED25519_BASEPOINT_COMPRESSED).unwrap();
2460 assert_eq!(encoded, enc_compressed);
2461
2462 assert_eq!(encoded.len(), 32);
2464
2465 let dec_uncompressed: EdwardsPoint = postcard::from_bytes(&encoded).unwrap();
2466 let dec_compressed: CompressedEdwardsY = postcard::from_bytes(&encoded).unwrap();
2467
2468 assert_eq!(dec_uncompressed, constants::ED25519_BASEPOINT_POINT);
2469 assert_eq!(dec_compressed, constants::ED25519_BASEPOINT_COMPRESSED);
2470
2471 let raw_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
2475 let bp: EdwardsPoint = postcard::from_bytes(raw_bytes).unwrap();
2476 assert_eq!(bp, constants::ED25519_BASEPOINT_POINT);
2477 }
2478
2479 #[cfg(all(feature = "alloc", feature = "digest"))]
2483 const RFC_ENCODE_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[
2484 (
2485 b"",
2486 "1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da",
2487 "222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b",
2488 ),
2489 (
2490 b"abc",
2491 "5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8",
2492 "67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42",
2493 ),
2494 (
2495 b"abcdef0123456789",
2496 "1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1",
2497 "2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb",
2498 ),
2499 (
2500 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
2501 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
2502 "35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73",
2503 "2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450",
2504 ),
2505 (
2506 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2507 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2508 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2509 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2510 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2511 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2512 "6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff",
2513 "2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37"
2514 )
2515 ];
2516
2517 #[cfg(all(feature = "alloc", feature = "digest"))]
2518 fn hex_str_to_fe(hex_str: &str) -> FieldElement {
2519 let mut bytes = hex::decode(hex_str).unwrap().to_vec();
2520 bytes.reverse();
2521 FieldElement::from_bytes(&bytes.try_into().unwrap())
2522 }
2523
2524 #[test]
2525 #[cfg(all(feature = "alloc", feature = "digest"))]
2526 fn elligator_encode_to_curve_test_vectors() {
2527 let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_";
2528 for (index, vector) in RFC_ENCODE_TO_CURVE_KAT.iter().enumerate() {
2529 let input = vector.0;
2530
2531 let expected_output = {
2532 let x = hex_str_to_fe(vector.1);
2533 let y = hex_str_to_fe(vector.2);
2534 AffinePoint { x, y }.to_edwards()
2535 };
2536
2537 let computed = EdwardsPoint::encode_to_curve::<sha2::Sha512>(&[&input], &[dst]);
2538 assert_eq!(computed, expected_output, "Failed in test {}", index);
2539 }
2540 }
2541
2542 #[cfg(all(feature = "alloc", feature = "digest"))]
2546 const RFC_HASH_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[
2547 (
2548 b"",
2549 "3c3da6925a3c3c268448dcabb47ccde5439559d9599646a8260e47b1e4822fc6",
2550 "09a6c8561a0b22bef63124c588ce4c62ea83a3c899763af26d795302e115dc21",
2551 ),
2552
2553 (
2554 b"abc",
2555 "608040b42285cc0d72cbb3985c6b04c935370c7361f4b7fbdb1ae7f8c1a8ecad",
2556 "1a8395b88338f22e435bbd301183e7f20a5f9de643f11882fb237f88268a5531",
2557 ),
2558
2559 (
2560 b"abcdef0123456789",
2561 "6d7fabf47a2dc03fe7d47f7dddd21082c5fb8f86743cd020f3fb147d57161472",
2562 "53060a3d140e7fbcda641ed3cf42c88a75411e648a1add71217f70ea8ec561a6",
2563 ),
2564 (
2565 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
2566 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
2567 "5fb0b92acedd16f3bcb0ef83f5c7b7a9466b5f1e0d8d217421878ea3686f8524",
2568 "2eca15e355fcfa39d2982f67ddb0eea138e2994f5956ed37b7f72eea5e89d2f7",
2569 ),
2570 (
2571 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2572 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2573 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2574 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2575 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2576 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2577 "0efcfde5898a839b00997fbe40d2ebe950bc81181afbd5cd6b9618aa336c1e8c",
2578 "6dc2fc04f266c5c27f236a80b14f92ccd051ef1ff027f26a07f8c0f327d8f995"
2579 )
2580 ];
2581
2582 #[test]
2583 #[cfg(all(feature = "alloc", feature = "digest"))]
2584 fn elligator_hash_to_curve_test_vectors() {
2585 let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_";
2586 for (index, vector) in RFC_HASH_TO_CURVE_KAT.iter().enumerate() {
2587 let input = vector.0;
2588
2589 let expected_output = {
2590 let x = hex_str_to_fe(vector.1);
2591 let y = hex_str_to_fe(vector.2);
2592
2593 AffinePoint { x, y }.to_edwards()
2594 };
2595
2596 let computed = EdwardsPoint::hash_to_curve::<sha2::Sha512>(&[&input], &[dst]);
2597
2598 assert_eq!(expected_output, computed, "Failed in test {}", index);
2599 }
2600 }
2601}