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 consts::True, crypto_common::BlockSizeUser, generic_array::typenum::U64, typenum::IsGreater,
110 Digest, FixedOutput, HashMarker,
111};
112
113#[cfg(feature = "group")]
114use {
115 group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
116 subtle::CtOption,
117};
118
119#[cfg(any(test, feature = "rand_core"))]
120use rand_core::RngCore;
121
122use subtle::Choice;
123use subtle::ConditionallyNegatable;
124use subtle::ConditionallySelectable;
125use subtle::ConstantTimeEq;
126
127#[cfg(feature = "zeroize")]
128use zeroize::Zeroize;
129
130use crate::constants;
131
132use crate::field::FieldElement;
133use crate::scalar::{clamp_integer, Scalar};
134
135use crate::montgomery::MontgomeryPoint;
136
137use crate::backend::serial::curve_models::AffineNielsPoint;
138use crate::backend::serial::curve_models::CompletedPoint;
139use crate::backend::serial::curve_models::ProjectiveNielsPoint;
140use crate::backend::serial::curve_models::ProjectivePoint;
141
142#[cfg(feature = "precomputed-tables")]
143use crate::window::{
144 LookupTableRadix128, LookupTableRadix16, LookupTableRadix256, LookupTableRadix32,
145 LookupTableRadix64,
146};
147
148#[cfg(feature = "precomputed-tables")]
149use crate::traits::BasepointTable;
150
151use crate::traits::ValidityCheck;
152use crate::traits::{Identity, IsIdentity};
153
154use affine::AffinePoint;
155
156#[cfg(feature = "alloc")]
157use crate::traits::MultiscalarMul;
158#[cfg(feature = "alloc")]
159use crate::traits::{VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
160#[cfg(feature = "alloc")]
161use alloc::vec::Vec;
162
163#[derive(Copy, Clone, Eq, PartialEq, Hash)]
173pub struct CompressedEdwardsY(pub [u8; 32]);
174
175impl ConstantTimeEq for CompressedEdwardsY {
176 fn ct_eq(&self, other: &CompressedEdwardsY) -> Choice {
177 self.as_bytes().ct_eq(other.as_bytes())
178 }
179}
180
181impl Debug for CompressedEdwardsY {
182 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
183 write!(f, "CompressedEdwardsY: {:?}", self.as_bytes())
184 }
185}
186
187impl CompressedEdwardsY {
188 pub const fn as_bytes(&self) -> &[u8; 32] {
190 &self.0
191 }
192
193 pub const fn to_bytes(&self) -> [u8; 32] {
195 self.0
196 }
197
198 pub fn decompress(&self) -> Option<EdwardsPoint> {
203 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
204
205 if is_valid_y_coord.into() {
206 Some(decompress::step_2(self, X, Y, Z))
207 } else {
208 None
209 }
210 }
211}
212
213mod decompress {
214 use super::*;
215
216 #[rustfmt::skip] pub(super) fn step_1(
218 repr: &CompressedEdwardsY,
219 ) -> (Choice, FieldElement, FieldElement, FieldElement) {
220 let Y = FieldElement::from_bytes(repr.as_bytes());
221 let Z = FieldElement::ONE;
222 let YY = Y.square();
223 let u = &YY - &Z; let v = &(&YY * &constants::EDWARDS_D) + &Z; let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v);
226
227 (is_valid_y_coord, X, Y, Z)
228 }
229
230 #[rustfmt::skip]
231 pub(super) fn step_2(
232 repr: &CompressedEdwardsY,
233 mut X: FieldElement,
234 Y: FieldElement,
235 Z: FieldElement,
236 ) -> EdwardsPoint {
237 let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7);
240 X.conditional_negate(compressed_sign_bit);
241
242 EdwardsPoint {
243 X,
244 Y,
245 Z,
246 T: &X * &Y,
247 }
248 }
249}
250
251impl TryFrom<&[u8]> for CompressedEdwardsY {
252 type Error = TryFromSliceError;
253
254 fn try_from(slice: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
255 Self::from_slice(slice)
256 }
257}
258
259#[cfg(feature = "digest")]
268use constants::ED25519_SQRTAM2;
269#[cfg(feature = "serde")]
270use serde::de::Visitor;
271#[cfg(feature = "serde")]
272use serde::{Deserialize, Deserializer, Serialize, Serializer};
273
274#[cfg(feature = "serde")]
275impl Serialize for EdwardsPoint {
276 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
277 where
278 S: Serializer,
279 {
280 use serde::ser::SerializeTuple;
281 let mut tup = serializer.serialize_tuple(32)?;
282 for byte in self.compress().as_bytes().iter() {
283 tup.serialize_element(byte)?;
284 }
285 tup.end()
286 }
287}
288
289#[cfg(feature = "serde")]
290impl Serialize for CompressedEdwardsY {
291 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
292 where
293 S: Serializer,
294 {
295 use serde::ser::SerializeTuple;
296 let mut tup = serializer.serialize_tuple(32)?;
297 for byte in self.as_bytes().iter() {
298 tup.serialize_element(byte)?;
299 }
300 tup.end()
301 }
302}
303
304#[cfg(feature = "serde")]
305impl<'de> Deserialize<'de> for EdwardsPoint {
306 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
307 where
308 D: Deserializer<'de>,
309 {
310 struct EdwardsPointVisitor;
311
312 impl<'de> Visitor<'de> for EdwardsPointVisitor {
313 type Value = EdwardsPoint;
314
315 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
316 formatter.write_str("a valid point in Edwards y + sign format")
317 }
318
319 fn visit_seq<A>(self, mut seq: A) -> Result<EdwardsPoint, A::Error>
320 where
321 A: serde::de::SeqAccess<'de>,
322 {
323 let mut bytes = [0u8; 32];
324 #[allow(clippy::needless_range_loop)]
325 for i in 0..32 {
326 bytes[i] = seq
327 .next_element()?
328 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
329 }
330 CompressedEdwardsY(bytes)
331 .decompress()
332 .ok_or_else(|| serde::de::Error::custom("decompression failed"))
333 }
334 }
335
336 deserializer.deserialize_tuple(32, EdwardsPointVisitor)
337 }
338}
339
340#[cfg(feature = "serde")]
341impl<'de> Deserialize<'de> for CompressedEdwardsY {
342 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
343 where
344 D: Deserializer<'de>,
345 {
346 struct CompressedEdwardsYVisitor;
347
348 impl<'de> Visitor<'de> for CompressedEdwardsYVisitor {
349 type Value = CompressedEdwardsY;
350
351 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
352 formatter.write_str("32 bytes of data")
353 }
354
355 fn visit_seq<A>(self, mut seq: A) -> Result<CompressedEdwardsY, A::Error>
356 where
357 A: serde::de::SeqAccess<'de>,
358 {
359 let mut bytes = [0u8; 32];
360 #[allow(clippy::needless_range_loop)]
361 for i in 0..32 {
362 bytes[i] = seq
363 .next_element()?
364 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
365 }
366 Ok(CompressedEdwardsY(bytes))
367 }
368 }
369
370 deserializer.deserialize_tuple(32, CompressedEdwardsYVisitor)
371 }
372}
373
374#[derive(Copy, Clone)]
380#[allow(missing_docs)]
381pub struct EdwardsPoint {
382 pub(crate) X: FieldElement,
383 pub(crate) Y: FieldElement,
384 pub(crate) Z: FieldElement,
385 pub(crate) T: FieldElement,
386}
387
388impl Identity for CompressedEdwardsY {
393 fn identity() -> CompressedEdwardsY {
394 CompressedEdwardsY([
395 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,
396 0, 0, 0,
397 ])
398 }
399}
400
401impl Default for CompressedEdwardsY {
402 fn default() -> CompressedEdwardsY {
403 CompressedEdwardsY::identity()
404 }
405}
406
407impl CompressedEdwardsY {
408 pub fn from_slice(bytes: &[u8]) -> Result<CompressedEdwardsY, TryFromSliceError> {
415 bytes.try_into().map(CompressedEdwardsY)
416 }
417}
418
419impl Identity for EdwardsPoint {
420 fn identity() -> EdwardsPoint {
421 EdwardsPoint {
422 X: FieldElement::ZERO,
423 Y: FieldElement::ONE,
424 Z: FieldElement::ONE,
425 T: FieldElement::ZERO,
426 }
427 }
428}
429
430impl Default for EdwardsPoint {
431 fn default() -> EdwardsPoint {
432 EdwardsPoint::identity()
433 }
434}
435
436#[cfg(feature = "zeroize")]
441impl Zeroize for CompressedEdwardsY {
442 fn zeroize(&mut self) {
444 self.0.zeroize();
445 self.0[0] = 1;
446 }
447}
448
449#[cfg(feature = "zeroize")]
450impl Zeroize for EdwardsPoint {
451 fn zeroize(&mut self) {
453 self.X.zeroize();
454 self.Y = FieldElement::ONE;
455 self.Z = FieldElement::ONE;
456 self.T.zeroize();
457 }
458}
459
460impl ValidityCheck for EdwardsPoint {
465 fn is_valid(&self) -> bool {
466 let point_on_curve = self.as_projective().is_valid();
467 let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T);
468
469 point_on_curve && on_segre_image
470 }
471}
472
473impl ConditionallySelectable for EdwardsPoint {
478 fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint {
479 EdwardsPoint {
480 X: FieldElement::conditional_select(&a.X, &b.X, choice),
481 Y: FieldElement::conditional_select(&a.Y, &b.Y, choice),
482 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
483 T: FieldElement::conditional_select(&a.T, &b.T, choice),
484 }
485 }
486}
487
488impl ConstantTimeEq for EdwardsPoint {
493 fn ct_eq(&self, other: &EdwardsPoint) -> Choice {
494 (&self.X * &other.Z).ct_eq(&(&other.X * &self.Z))
501 & (&self.Y * &other.Z).ct_eq(&(&other.Y * &self.Z))
502 }
503}
504
505impl PartialEq for EdwardsPoint {
506 fn eq(&self, other: &EdwardsPoint) -> bool {
507 self.ct_eq(other).into()
508 }
509}
510
511impl Eq for EdwardsPoint {}
512
513impl EdwardsPoint {
518 pub(crate) fn as_projective_niels(&self) -> ProjectiveNielsPoint {
520 ProjectiveNielsPoint {
521 Y_plus_X: &self.Y + &self.X,
522 Y_minus_X: &self.Y - &self.X,
523 Z: self.Z,
524 T2d: &self.T * &constants::EDWARDS_D2,
525 }
526 }
527
528 pub(crate) const fn as_projective(&self) -> ProjectivePoint {
533 ProjectivePoint {
534 X: self.X,
535 Y: self.Y,
536 Z: self.Z,
537 }
538 }
539
540 pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint {
543 let recip = self.Z.invert();
544 let x = &self.X * &recip;
545 let y = &self.Y * &recip;
546 let xy2d = &(&x * &y) * &constants::EDWARDS_D2;
547 AffineNielsPoint {
548 y_plus_x: &y + &x,
549 y_minus_x: &y - &x,
550 xy2d,
551 }
552 }
553
554 pub(crate) fn to_affine(self) -> AffinePoint {
556 let recip = self.Z.invert();
557 let x = &self.X * &recip;
558 let y = &self.Y * &recip;
559 AffinePoint { x, y }
560 }
561
562 pub fn to_montgomery(&self) -> MontgomeryPoint {
572 let U = &self.Z + &self.Y;
578 let W = &self.Z - &self.Y;
579 let u = &U * &W.invert();
580 MontgomeryPoint(u.to_bytes())
581 }
582
583 #[cfg(feature = "alloc")]
586 pub fn to_montgomery_batch(eds: &[Self]) -> Vec<MontgomeryPoint> {
587 let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::<Vec<_>>();
593 FieldElement::batch_invert(&mut denominators);
594
595 let mut ret = Vec::with_capacity(eds.len());
597 for (ed, d) in eds.iter().zip(denominators.iter()) {
598 let u = &(&ed.Z + &ed.Y) * d;
599 ret.push(MontgomeryPoint(u.to_bytes()));
600 }
601
602 ret
603 }
604
605 pub fn compress(&self) -> CompressedEdwardsY {
607 self.to_affine().compress()
608 }
609
610 #[cfg(feature = "alloc")]
613 pub fn compress_batch(inputs: &[EdwardsPoint]) -> Vec<CompressedEdwardsY> {
614 let mut zs = inputs.iter().map(|input| input.Z).collect::<Vec<_>>();
615 FieldElement::batch_invert(&mut zs);
616
617 inputs
618 .iter()
619 .zip(&zs)
620 .map(|(input, recip)| {
621 let x = &input.X * recip;
622 let y = &input.Y * recip;
623 AffinePoint { x, y }.compress()
624 })
625 .collect()
626 }
627
628 #[cfg(feature = "digest")]
629 pub fn hash_to_curve<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
638 where
639 D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
640 D::BlockSize: IsGreater<D::OutputSize, Output = True>,
641 {
642 let fe = FieldElement::hash_to_field::<D>(bytes, domain_sep);
646 let (M1, is_sq) = crate::montgomery::elligator_encode(&fe);
647
648 let mut E1_opt = M1
665 .to_edwards(0)
666 .expect("Montgomery conversion to Edwards point in Elligator failed");
667
668 let mont_v =
670 &(&ED25519_SQRTAM2 * &FieldElement::from_bytes(&M1.to_bytes())) * &E1_opt.X.invert();
671 E1_opt.X.conditional_negate(is_sq ^ mont_v.is_negative());
672 E1_opt.mul_by_cofactor()
673 }
674
675 #[cfg(feature = "digest")]
676 #[deprecated(
685 since = "4.0.0",
686 note = "previously named `hash_from_bytes`, this is not a secure hash function"
687 )]
688 pub fn nonspec_map_to_curve<D>(bytes: &[u8]) -> EdwardsPoint
689 where
690 D: Digest<OutputSize = U64> + Default,
691 {
692 let mut hash = D::new();
693 hash.update(bytes);
694 let h = hash.finalize();
695 let mut res = [0u8; 32];
696 res.copy_from_slice(&h[..32]);
697
698 let sign_bit = (res[31] & 0x80) >> 7;
699
700 let fe = FieldElement::from_bytes(&res);
701
702 let (M1, _) = crate::montgomery::elligator_encode(&fe);
703 let E1_opt = M1.to_edwards(sign_bit);
704
705 E1_opt
706 .expect("Montgomery conversion to Edwards point in Elligator failed")
707 .mul_by_cofactor()
708 }
709
710 #[cfg(any(test, feature = "rand_core"))]
725 pub fn random(mut rng: impl RngCore) -> Self {
726 let mut repr = CompressedEdwardsY([0u8; 32]);
727 loop {
728 rng.fill_bytes(&mut repr.0);
729 if let Some(p) = repr.decompress() {
730 if !IsIdentity::is_identity(&p) {
731 break p;
732 }
733 }
734 }
735 }
736}
737
738impl EdwardsPoint {
743 pub(crate) fn double(&self) -> EdwardsPoint {
745 self.as_projective().double().as_extended()
746 }
747}
748
749impl<'a> Add<&'a EdwardsPoint> for &EdwardsPoint {
754 type Output = EdwardsPoint;
755 fn add(self, other: &'a EdwardsPoint) -> EdwardsPoint {
756 (self + &other.as_projective_niels()).as_extended()
757 }
758}
759
760define_add_variants!(
761 LHS = EdwardsPoint,
762 RHS = EdwardsPoint,
763 Output = EdwardsPoint
764);
765
766impl<'a> AddAssign<&'a EdwardsPoint> for EdwardsPoint {
767 fn add_assign(&mut self, _rhs: &'a EdwardsPoint) {
768 *self = (self as &EdwardsPoint) + _rhs;
769 }
770}
771
772define_add_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
773
774impl<'a> Sub<&'a EdwardsPoint> for &EdwardsPoint {
775 type Output = EdwardsPoint;
776 fn sub(self, other: &'a EdwardsPoint) -> EdwardsPoint {
777 (self - &other.as_projective_niels()).as_extended()
778 }
779}
780
781define_sub_variants!(
782 LHS = EdwardsPoint,
783 RHS = EdwardsPoint,
784 Output = EdwardsPoint
785);
786
787impl<'a> SubAssign<&'a EdwardsPoint> for EdwardsPoint {
788 fn sub_assign(&mut self, _rhs: &'a EdwardsPoint) {
789 *self = (self as &EdwardsPoint) - _rhs;
790 }
791}
792
793define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
794
795impl<T> Sum<T> for EdwardsPoint
796where
797 T: Borrow<EdwardsPoint>,
798{
799 fn sum<I>(iter: I) -> Self
800 where
801 I: Iterator<Item = T>,
802 {
803 iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow())
804 }
805}
806
807impl Neg for &EdwardsPoint {
812 type Output = EdwardsPoint;
813
814 fn neg(self) -> EdwardsPoint {
815 EdwardsPoint {
816 X: -(&self.X),
817 Y: self.Y,
818 Z: self.Z,
819 T: -(&self.T),
820 }
821 }
822}
823
824impl Neg for EdwardsPoint {
825 type Output = EdwardsPoint;
826
827 fn neg(self) -> EdwardsPoint {
828 -&self
829 }
830}
831
832impl<'a> MulAssign<&'a Scalar> for EdwardsPoint {
837 fn mul_assign(&mut self, scalar: &'a Scalar) {
838 let result = (self as &EdwardsPoint) * scalar;
839 *self = result;
840 }
841}
842
843define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar);
844
845define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint);
846define_mul_variants!(LHS = Scalar, RHS = EdwardsPoint, Output = EdwardsPoint);
847
848impl<'a> Mul<&'a Scalar> for &EdwardsPoint {
849 type Output = EdwardsPoint;
850 fn mul(self, scalar: &'a Scalar) -> EdwardsPoint {
855 crate::backend::variable_base_mul(self, scalar)
856 }
857}
858
859impl<'a> Mul<&'a EdwardsPoint> for &Scalar {
860 type Output = EdwardsPoint;
861
862 fn mul(self, point: &'a EdwardsPoint) -> EdwardsPoint {
867 point * self
868 }
869}
870
871impl EdwardsPoint {
872 pub fn mul_base(scalar: &Scalar) -> Self {
877 #[cfg(not(feature = "precomputed-tables"))]
878 {
879 scalar * constants::ED25519_BASEPOINT_POINT
880 }
881
882 #[cfg(feature = "precomputed-tables")]
883 {
884 scalar * constants::ED25519_BASEPOINT_TABLE
885 }
886 }
887
888 pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
891 let s = Scalar {
899 bytes: clamp_integer(bytes),
900 };
901 s * self
902 }
903
904 pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
907 let s = Scalar {
911 bytes: clamp_integer(bytes),
912 };
913 Self::mul_base(&s)
914 }
915}
916
917#[cfg(feature = "alloc")]
925impl MultiscalarMul for EdwardsPoint {
926 type Point = EdwardsPoint;
927
928 fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
929 where
930 I: IntoIterator,
931 I::Item: Borrow<Scalar>,
932 J: IntoIterator,
933 J::Item: Borrow<EdwardsPoint>,
934 {
935 let mut scalars = scalars.into_iter();
937 let mut points = points.into_iter();
938
939 let (s_lo, s_hi) = scalars.by_ref().size_hint();
941 let (p_lo, p_hi) = points.by_ref().size_hint();
942
943 assert_eq!(s_lo, p_lo);
945 assert_eq!(s_hi, Some(s_lo));
946 assert_eq!(p_hi, Some(p_lo));
947
948 let _size = s_lo;
951
952 crate::backend::straus_multiscalar_mul(scalars, points)
953 }
954}
955
956#[cfg(feature = "alloc")]
957impl VartimeMultiscalarMul for EdwardsPoint {
958 type Point = EdwardsPoint;
959
960 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
961 where
962 I: IntoIterator,
963 I::Item: Borrow<Scalar>,
964 J: IntoIterator<Item = Option<EdwardsPoint>>,
965 {
966 let mut scalars = scalars.into_iter();
968 let mut points = points.into_iter();
969
970 let (s_lo, s_hi) = scalars.by_ref().size_hint();
972 let (p_lo, p_hi) = points.by_ref().size_hint();
973
974 assert_eq!(s_lo, p_lo);
976 assert_eq!(s_hi, Some(s_lo));
977 assert_eq!(p_hi, Some(p_lo));
978
979 let size = s_lo;
982
983 if size < 190 {
984 crate::backend::straus_optional_multiscalar_mul(scalars, points)
985 } else {
986 crate::backend::pippenger_optional_multiscalar_mul(scalars, points)
987 }
988 }
989}
990
991#[cfg(feature = "alloc")]
996pub struct VartimeEdwardsPrecomputation(crate::backend::VartimePrecomputedStraus);
997
998#[cfg(feature = "alloc")]
999impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation {
1000 type Point = EdwardsPoint;
1001
1002 fn new<I>(static_points: I) -> Self
1003 where
1004 I: IntoIterator,
1005 I::Item: Borrow<Self::Point>,
1006 {
1007 Self(crate::backend::VartimePrecomputedStraus::new(static_points))
1008 }
1009
1010 fn len(&self) -> usize {
1011 self.0.len()
1012 }
1013
1014 fn is_empty(&self) -> bool {
1015 self.0.is_empty()
1016 }
1017
1018 fn optional_mixed_multiscalar_mul<I, J, K>(
1019 &self,
1020 static_scalars: I,
1021 dynamic_scalars: J,
1022 dynamic_points: K,
1023 ) -> Option<Self::Point>
1024 where
1025 I: IntoIterator,
1026 I::Item: Borrow<Scalar>,
1027 J: IntoIterator,
1028 J::Item: Borrow<Scalar>,
1029 K: IntoIterator<Item = Option<Self::Point>>,
1030 {
1031 self.0
1032 .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points)
1033 }
1034}
1035
1036impl EdwardsPoint {
1037 pub fn vartime_double_scalar_mul_basepoint(
1039 a: &Scalar,
1040 A: &EdwardsPoint,
1041 b: &Scalar,
1042 ) -> EdwardsPoint {
1043 crate::backend::vartime_double_base_mul(a, A, b)
1044 }
1045}
1046
1047#[cfg(feature = "precomputed-tables")]
1048macro_rules! impl_basepoint_table {
1049 (Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => {
1050 #[derive(Clone)]
1082 #[repr(transparent)]
1083 pub struct $name(pub(crate) [$table<AffineNielsPoint>; 32]);
1084
1085 impl BasepointTable for $name {
1086 type Point = $point;
1087
1088 fn create(basepoint: &$point) -> $name {
1090 let mut table = $name([$table::default(); 32]);
1092 let mut P = *basepoint;
1093 for i in 0..32 {
1094 table.0[i] = $table::from(&P);
1096 P = P.mul_by_pow_2($radix + $radix);
1097 }
1098 table
1099 }
1100
1101 fn basepoint(&self) -> $point {
1103 (&<$point>::identity() + &self.0[0].select(1)).as_extended()
1106 }
1107
1108 fn mul_base(&self, scalar: &Scalar) -> $point {
1151 let a = scalar.as_radix_2w($radix);
1152
1153 let tables = &self.0;
1154 let mut P = <$point>::identity();
1155
1156 for i in (0..$adds).filter(|x| x % 2 == 1) {
1157 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1158 }
1159
1160 P = P.mul_by_pow_2($radix);
1161
1162 for i in (0..$adds).filter(|x| x % 2 == 0) {
1163 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1164 }
1165
1166 P
1167 }
1168 }
1169
1170 impl<'a, 'b> Mul<&'b Scalar> for &'a $name {
1171 type Output = $point;
1172
1173 fn mul(self, scalar: &'b Scalar) -> $point {
1176 self.mul_base(scalar)
1178 }
1179 }
1180
1181 impl<'a, 'b> Mul<&'a $name> for &'b Scalar {
1182 type Output = $point;
1183
1184 fn mul(self, basepoint_table: &'a $name) -> $point {
1187 basepoint_table * self
1188 }
1189 }
1190
1191 impl Debug for $name {
1192 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1193 write!(f, "{:?}([\n", stringify!($name))?;
1194 for i in 0..32 {
1195 write!(f, "\t{:?},\n", &self.0[i])?;
1196 }
1197 write!(f, "])")
1198 }
1199 }
1200 };
1201} cfg_if! {
1205 if #[cfg(feature = "precomputed-tables")] {
1206 impl_basepoint_table! {
1207 Name = EdwardsBasepointTable,
1208 LookupTable = LookupTableRadix16,
1209 Point = EdwardsPoint,
1210 Radix = 4,
1211 Additions = 64
1212 }
1213 impl_basepoint_table! {
1214 Name = EdwardsBasepointTableRadix32,
1215 LookupTable = LookupTableRadix32,
1216 Point = EdwardsPoint,
1217 Radix = 5,
1218 Additions = 52
1219 }
1220 impl_basepoint_table! {
1221 Name = EdwardsBasepointTableRadix64,
1222 LookupTable = LookupTableRadix64,
1223 Point = EdwardsPoint,
1224 Radix = 6,
1225 Additions = 43
1226 }
1227 impl_basepoint_table! {
1228 Name = EdwardsBasepointTableRadix128,
1229 LookupTable = LookupTableRadix128,
1230 Point = EdwardsPoint,
1231 Radix = 7,
1232 Additions = 37
1233 }
1234 impl_basepoint_table! {
1235 Name = EdwardsBasepointTableRadix256,
1236 LookupTable = LookupTableRadix256,
1237 Point = EdwardsPoint,
1238 Radix = 8,
1239 Additions = 33
1240 }
1241
1242 pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable;
1249 }
1250}
1251
1252#[cfg(feature = "precomputed-tables")]
1253macro_rules! impl_basepoint_table_conversions {
1254 (LHS = $lhs:ty, RHS = $rhs:ty) => {
1255 impl<'a> From<&'a $lhs> for $rhs {
1256 fn from(table: &'a $lhs) -> $rhs {
1257 <$rhs>::create(&table.basepoint())
1258 }
1259 }
1260
1261 impl<'a> From<&'a $rhs> for $lhs {
1262 fn from(table: &'a $rhs) -> $lhs {
1263 <$lhs>::create(&table.basepoint())
1264 }
1265 }
1266 };
1267}
1268
1269cfg_if! {
1270 if #[cfg(feature = "precomputed-tables")] {
1271 impl_basepoint_table_conversions! {
1273 LHS = EdwardsBasepointTableRadix16,
1274 RHS = EdwardsBasepointTableRadix32
1275 }
1276 impl_basepoint_table_conversions! {
1277 LHS = EdwardsBasepointTableRadix16,
1278 RHS = EdwardsBasepointTableRadix64
1279 }
1280 impl_basepoint_table_conversions! {
1281 LHS = EdwardsBasepointTableRadix16,
1282 RHS = EdwardsBasepointTableRadix128
1283 }
1284 impl_basepoint_table_conversions! {
1285 LHS = EdwardsBasepointTableRadix16,
1286 RHS = EdwardsBasepointTableRadix256
1287 }
1288
1289 impl_basepoint_table_conversions! {
1291 LHS = EdwardsBasepointTableRadix32,
1292 RHS = EdwardsBasepointTableRadix64
1293 }
1294 impl_basepoint_table_conversions! {
1295 LHS = EdwardsBasepointTableRadix32,
1296 RHS = EdwardsBasepointTableRadix128
1297 }
1298 impl_basepoint_table_conversions! {
1299 LHS = EdwardsBasepointTableRadix32,
1300 RHS = EdwardsBasepointTableRadix256
1301 }
1302
1303 impl_basepoint_table_conversions! {
1305 LHS = EdwardsBasepointTableRadix64,
1306 RHS = EdwardsBasepointTableRadix128
1307 }
1308 impl_basepoint_table_conversions! {
1309 LHS = EdwardsBasepointTableRadix64,
1310 RHS = EdwardsBasepointTableRadix256
1311 }
1312
1313 impl_basepoint_table_conversions! {
1315 LHS = EdwardsBasepointTableRadix128,
1316 RHS = EdwardsBasepointTableRadix256
1317 }
1318 }
1319}
1320
1321impl EdwardsPoint {
1322 pub fn mul_by_cofactor(&self) -> EdwardsPoint {
1324 self.mul_by_pow_2(3)
1325 }
1326
1327 pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint {
1329 debug_assert!(k > 0);
1330 let mut r: CompletedPoint;
1331 let mut s = self.as_projective();
1332 let mut i = 0;
1333 while i < k - 1 {
1334 r = s.double();
1335 s = r.as_projective();
1336 i += 1;
1337 }
1338 s.double().as_extended()
1340 }
1341
1342 pub fn is_small_order(&self) -> bool {
1366 self.mul_by_cofactor().is_identity()
1367 }
1368
1369 pub fn is_torsion_free(&self) -> bool {
1396 (self * constants::BASEPOINT_ORDER_PRIVATE).is_identity()
1397 }
1398}
1399
1400impl Debug for EdwardsPoint {
1405 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1406 write!(
1407 f,
1408 "EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
1409 &self.X, &self.Y, &self.Z, &self.T
1410 )
1411 }
1412}
1413
1414#[cfg(feature = "group")]
1421impl group::Group for EdwardsPoint {
1422 type Scalar = Scalar;
1423
1424 fn random(rng: impl RngCore) -> Self {
1425 Self::random(rng)
1427 }
1428
1429 fn identity() -> Self {
1430 Identity::identity()
1431 }
1432
1433 fn generator() -> Self {
1434 constants::ED25519_BASEPOINT_POINT
1435 }
1436
1437 fn is_identity(&self) -> Choice {
1438 self.ct_eq(&Identity::identity())
1439 }
1440
1441 fn double(&self) -> Self {
1442 self.double()
1443 }
1444}
1445
1446#[cfg(feature = "group")]
1447impl GroupEncoding for EdwardsPoint {
1448 type Repr = [u8; 32];
1449
1450 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1451 let repr = CompressedEdwardsY(*bytes);
1452 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr);
1453 CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord)
1454 }
1455
1456 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1457 Self::from_bytes(bytes)
1459 }
1460
1461 fn to_bytes(&self) -> Self::Repr {
1462 self.compress().to_bytes()
1463 }
1464}
1465
1466#[cfg(feature = "group")]
1469#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1470pub struct SubgroupPoint(EdwardsPoint);
1471
1472#[cfg(feature = "group")]
1473impl From<SubgroupPoint> for EdwardsPoint {
1474 fn from(p: SubgroupPoint) -> Self {
1475 p.0
1476 }
1477}
1478
1479#[cfg(feature = "group")]
1480impl Neg for SubgroupPoint {
1481 type Output = Self;
1482
1483 fn neg(self) -> Self::Output {
1484 SubgroupPoint(-self.0)
1485 }
1486}
1487
1488#[cfg(feature = "group")]
1489impl Add<&SubgroupPoint> for &SubgroupPoint {
1490 type Output = SubgroupPoint;
1491 fn add(self, other: &SubgroupPoint) -> SubgroupPoint {
1492 SubgroupPoint(self.0 + other.0)
1493 }
1494}
1495
1496#[cfg(feature = "group")]
1497define_add_variants!(
1498 LHS = SubgroupPoint,
1499 RHS = SubgroupPoint,
1500 Output = SubgroupPoint
1501);
1502
1503#[cfg(feature = "group")]
1504impl Add<&SubgroupPoint> for &EdwardsPoint {
1505 type Output = EdwardsPoint;
1506 fn add(self, other: &SubgroupPoint) -> EdwardsPoint {
1507 self + other.0
1508 }
1509}
1510
1511#[cfg(feature = "group")]
1512define_add_variants!(
1513 LHS = EdwardsPoint,
1514 RHS = SubgroupPoint,
1515 Output = EdwardsPoint
1516);
1517
1518#[cfg(feature = "group")]
1519impl AddAssign<&SubgroupPoint> for SubgroupPoint {
1520 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1521 self.0 += rhs.0
1522 }
1523}
1524
1525#[cfg(feature = "group")]
1526define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1527
1528#[cfg(feature = "group")]
1529impl AddAssign<&SubgroupPoint> for EdwardsPoint {
1530 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1531 *self += rhs.0
1532 }
1533}
1534
1535#[cfg(feature = "group")]
1536define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1537
1538#[cfg(feature = "group")]
1539impl Sub<&SubgroupPoint> for &SubgroupPoint {
1540 type Output = SubgroupPoint;
1541 fn sub(self, other: &SubgroupPoint) -> SubgroupPoint {
1542 SubgroupPoint(self.0 - other.0)
1543 }
1544}
1545
1546#[cfg(feature = "group")]
1547define_sub_variants!(
1548 LHS = SubgroupPoint,
1549 RHS = SubgroupPoint,
1550 Output = SubgroupPoint
1551);
1552
1553#[cfg(feature = "group")]
1554impl Sub<&SubgroupPoint> for &EdwardsPoint {
1555 type Output = EdwardsPoint;
1556 fn sub(self, other: &SubgroupPoint) -> EdwardsPoint {
1557 self - other.0
1558 }
1559}
1560
1561#[cfg(feature = "group")]
1562define_sub_variants!(
1563 LHS = EdwardsPoint,
1564 RHS = SubgroupPoint,
1565 Output = EdwardsPoint
1566);
1567
1568#[cfg(feature = "group")]
1569impl SubAssign<&SubgroupPoint> for SubgroupPoint {
1570 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1571 self.0 -= rhs.0;
1572 }
1573}
1574
1575#[cfg(feature = "group")]
1576define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1577
1578#[cfg(feature = "group")]
1579impl SubAssign<&SubgroupPoint> for EdwardsPoint {
1580 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1581 *self -= rhs.0;
1582 }
1583}
1584
1585#[cfg(feature = "group")]
1586define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1587
1588#[cfg(feature = "group")]
1589impl<T> Sum<T> for SubgroupPoint
1590where
1591 T: Borrow<SubgroupPoint>,
1592{
1593 fn sum<I>(iter: I) -> Self
1594 where
1595 I: Iterator<Item = T>,
1596 {
1597 use group::Group;
1598 iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow())
1599 }
1600}
1601
1602#[cfg(feature = "group")]
1603impl Mul<&Scalar> for &SubgroupPoint {
1604 type Output = SubgroupPoint;
1605
1606 fn mul(self, scalar: &Scalar) -> SubgroupPoint {
1611 SubgroupPoint(self.0 * scalar)
1612 }
1613}
1614
1615#[cfg(feature = "group")]
1616define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint);
1617
1618#[cfg(feature = "group")]
1619impl Mul<&SubgroupPoint> for &Scalar {
1620 type Output = SubgroupPoint;
1621
1622 fn mul(self, point: &SubgroupPoint) -> SubgroupPoint {
1627 point * self
1628 }
1629}
1630
1631#[cfg(feature = "group")]
1632define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint);
1633
1634#[cfg(feature = "group")]
1635impl MulAssign<&Scalar> for SubgroupPoint {
1636 fn mul_assign(&mut self, scalar: &Scalar) {
1637 self.0 *= scalar;
1638 }
1639}
1640
1641#[cfg(feature = "group")]
1642define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);
1643
1644#[cfg(feature = "group")]
1645impl ConstantTimeEq for SubgroupPoint {
1646 fn ct_eq(&self, other: &SubgroupPoint) -> Choice {
1647 self.0.ct_eq(&other.0)
1648 }
1649}
1650
1651#[cfg(feature = "group")]
1652impl ConditionallySelectable for SubgroupPoint {
1653 fn conditional_select(a: &SubgroupPoint, b: &SubgroupPoint, choice: Choice) -> SubgroupPoint {
1654 SubgroupPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice))
1655 }
1656}
1657
1658#[cfg(all(feature = "group", feature = "zeroize"))]
1659impl Zeroize for SubgroupPoint {
1660 fn zeroize(&mut self) {
1661 self.0.zeroize();
1662 }
1663}
1664
1665#[cfg(feature = "group")]
1666impl group::Group for SubgroupPoint {
1667 type Scalar = Scalar;
1668
1669 fn random(mut rng: impl RngCore) -> Self {
1670 use group::ff::Field;
1671
1672 let s = loop {
1675 let s: Scalar = Field::random(&mut rng);
1676 if !s.is_zero_vartime() {
1677 break s;
1678 }
1679 };
1680
1681 Self::generator() * s
1683 }
1684
1685 fn identity() -> Self {
1686 SubgroupPoint(Identity::identity())
1687 }
1688
1689 fn generator() -> Self {
1690 SubgroupPoint(EdwardsPoint::generator())
1691 }
1692
1693 fn is_identity(&self) -> Choice {
1694 self.0.ct_eq(&Identity::identity())
1695 }
1696
1697 fn double(&self) -> Self {
1698 SubgroupPoint(self.0.double())
1699 }
1700}
1701
1702#[cfg(feature = "group")]
1703impl GroupEncoding for SubgroupPoint {
1704 type Repr = <EdwardsPoint as GroupEncoding>::Repr;
1705
1706 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1707 EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1708 }
1709
1710 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1711 EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup())
1712 }
1713
1714 fn to_bytes(&self) -> Self::Repr {
1715 self.0.compress().to_bytes()
1716 }
1717}
1718
1719#[cfg(feature = "group")]
1720impl PrimeGroup for SubgroupPoint {}
1721
1722#[cfg(feature = "group")]
1723impl CofactorGroup for EdwardsPoint {
1724 type Subgroup = SubgroupPoint;
1725
1726 fn clear_cofactor(&self) -> Self::Subgroup {
1727 SubgroupPoint(self.mul_by_cofactor())
1728 }
1729
1730 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1731 CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
1732 }
1733
1734 fn is_torsion_free(&self) -> Choice {
1735 (self * constants::BASEPOINT_ORDER_PRIVATE).ct_eq(&Self::identity())
1736 }
1737}
1738
1739#[cfg(test)]
1744mod test {
1745 use super::*;
1746
1747 #[cfg(not(feature = "group"))]
1749 use rand_core::RngCore;
1750
1751 #[cfg(feature = "alloc")]
1752 use alloc::vec::Vec;
1753
1754 #[cfg(feature = "precomputed-tables")]
1755 use crate::constants::ED25519_BASEPOINT_TABLE;
1756
1757 static BASE_X_COORD_BYTES: [u8; 32] = [
1760 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c,
1761 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36,
1762 0x69, 0x21,
1763 ];
1764
1765 static BASE2_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1767 0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39,
1768 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd,
1769 0x60, 0x22,
1770 ]);
1771
1772 static BASE16_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1774 0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a,
1775 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96,
1776 0x96, 0x70,
1777 ]);
1778
1779 pub static A_SCALAR: Scalar = Scalar {
1781 bytes: [
1782 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
1783 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
1784 0x23, 0x76, 0xef, 0x09,
1785 ],
1786 };
1787
1788 pub static B_SCALAR: Scalar = Scalar {
1790 bytes: [
1791 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0,
1792 0xb3, 0x2e, 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, 0x56, 0xa7, 0xd4, 0xaa,
1793 0xb8, 0x60, 0x8a, 0x05,
1794 ],
1795 };
1796
1797 pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([
1799 0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59, 0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34,
1800 0xc3, 0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e, 0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e,
1801 0x40, 0xa5,
1802 ]);
1803
1804 static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([
1807 0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e, 0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59,
1808 0xc4, 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13,
1809 0x2b, 0x42,
1810 ]);
1811
1812 #[test]
1814 fn basepoint_decompression_compression() {
1815 let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES);
1816 let bp = constants::ED25519_BASEPOINT_COMPRESSED
1817 .decompress()
1818 .unwrap();
1819 assert!(bp.is_valid());
1820 assert_eq!(base_X, bp.X);
1822 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1823 }
1824
1825 #[test]
1827 fn decompression_sign_handling() {
1828 let mut minus_basepoint_bytes = *constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
1830 minus_basepoint_bytes[31] |= 1 << 7;
1831 let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes)
1832 .decompress()
1833 .unwrap();
1834 assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X));
1837 assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y);
1838 assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z);
1839 assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T));
1840 }
1841
1842 #[cfg(feature = "precomputed-tables")]
1844 #[test]
1845 fn basepoint_mult_one_vs_basepoint() {
1846 let bp = ED25519_BASEPOINT_TABLE * &Scalar::ONE;
1847 let compressed = bp.compress();
1848 assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED);
1849 }
1850
1851 #[cfg(feature = "precomputed-tables")]
1853 #[test]
1854 fn basepoint_table_basepoint_function_correct() {
1855 let bp = ED25519_BASEPOINT_TABLE.basepoint();
1856 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1857 }
1858
1859 #[test]
1862 fn basepoint_plus_basepoint_vs_basepoint2() {
1863 let bp = constants::ED25519_BASEPOINT_POINT;
1864 let bp_added = bp + bp;
1865 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1866 }
1867
1868 #[test]
1871 fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() {
1872 let bp = constants::ED25519_BASEPOINT_POINT;
1873 let bp_added = (&bp + &bp.as_projective_niels()).as_extended();
1874 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1875 }
1876
1877 #[test]
1880 fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() {
1881 let bp = constants::ED25519_BASEPOINT_POINT;
1882 let bp_affine_niels = bp.as_affine_niels();
1883 let bp_added = (&bp + &bp_affine_niels).as_extended();
1884 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1885 }
1886
1887 #[test]
1890 fn extended_point_equality_handles_scaling() {
1891 let mut two_bytes = [0u8; 32];
1892 two_bytes[0] = 2;
1893 let id1 = EdwardsPoint::identity();
1894 let id2 = EdwardsPoint {
1895 X: FieldElement::ZERO,
1896 Y: FieldElement::from_bytes(&two_bytes),
1897 Z: FieldElement::from_bytes(&two_bytes),
1898 T: FieldElement::ZERO,
1899 };
1900 assert!(bool::from(id1.ct_eq(&id2)));
1901 }
1902
1903 #[cfg(feature = "precomputed-tables")]
1905 #[test]
1906 fn to_affine_niels_clears_denominators() {
1907 let aB = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1909 let aB_affine_niels = aB.as_affine_niels();
1910 let also_aB = (&EdwardsPoint::identity() + &aB_affine_niels).as_extended();
1911 assert_eq!(aB.compress(), also_aB.compress());
1912 }
1913
1914 #[test]
1916 fn basepoint_mult_vs_ed25519py() {
1917 let aB = EdwardsPoint::mul_base(&A_SCALAR);
1918 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1919 }
1920
1921 #[test]
1923 fn basepoint_mult_by_basepoint_order() {
1924 let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER_PRIVATE);
1925 assert!(should_be_id.is_identity());
1926 }
1927
1928 #[cfg(feature = "precomputed-tables")]
1930 #[test]
1931 fn test_precomputed_basepoint_mult() {
1932 let aB_1 = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1933 let aB_2 = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1934 assert_eq!(aB_1.compress(), aB_2.compress());
1935 }
1936
1937 #[test]
1939 fn scalar_mul_vs_ed25519py() {
1940 let aB = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1941 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1942 }
1943
1944 #[test]
1946 fn basepoint_double_vs_basepoint2() {
1947 assert_eq!(
1948 constants::ED25519_BASEPOINT_POINT.double().compress(),
1949 BASE2_CMPRSSD
1950 );
1951 }
1952
1953 #[test]
1955 fn basepoint_mult_two_vs_basepoint2() {
1956 let two = Scalar::from(2u64);
1957 let bp2 = EdwardsPoint::mul_base(&two);
1958 assert_eq!(bp2.compress(), BASE2_CMPRSSD);
1959 }
1960
1961 #[cfg(feature = "precomputed-tables")]
1963 #[test]
1964 fn basepoint_tables() {
1965 let P = &constants::ED25519_BASEPOINT_POINT;
1966 let a = A_SCALAR;
1967
1968 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1969 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
1970 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
1971 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
1972 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
1973
1974 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
1975 let aP16 = (&table_radix16 * &a).compress();
1976 let aP32 = (&table_radix32 * &a).compress();
1977 let aP64 = (&table_radix64 * &a).compress();
1978 let aP128 = (&table_radix128 * &a).compress();
1979 let aP256 = (&table_radix256 * &a).compress();
1980
1981 assert_eq!(aP, aP16);
1982 assert_eq!(aP16, aP32);
1983 assert_eq!(aP32, aP64);
1984 assert_eq!(aP64, aP128);
1985 assert_eq!(aP128, aP256);
1986 }
1987
1988 #[cfg(feature = "precomputed-tables")]
1991 #[test]
1992 fn basepoint_tables_unreduced_scalar() {
1993 let P = &constants::ED25519_BASEPOINT_POINT;
1994 let a = crate::scalar::test::LARGEST_UNREDUCED_SCALAR;
1995
1996 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1997 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
1998 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
1999 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
2000 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
2001
2002 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
2003 let aP16 = (&table_radix16 * &a).compress();
2004 let aP32 = (&table_radix32 * &a).compress();
2005 let aP64 = (&table_radix64 * &a).compress();
2006 let aP128 = (&table_radix128 * &a).compress();
2007 let aP256 = (&table_radix256 * &a).compress();
2008
2009 assert_eq!(aP, aP16);
2010 assert_eq!(aP16, aP32);
2011 assert_eq!(aP32, aP64);
2012 assert_eq!(aP64, aP128);
2013 assert_eq!(aP128, aP256);
2014 }
2015
2016 #[test]
2018 fn basepoint_projective_extended_round_trip() {
2019 assert_eq!(
2020 constants::ED25519_BASEPOINT_POINT
2021 .as_projective()
2022 .as_extended()
2023 .compress(),
2024 constants::ED25519_BASEPOINT_COMPRESSED
2025 );
2026 }
2027
2028 #[test]
2030 fn basepoint16_vs_mul_by_pow_2_4() {
2031 let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4);
2032 assert_eq!(bp16.compress(), BASE16_CMPRSSD);
2033 }
2034
2035 #[test]
2037 fn mul_base_clamped() {
2038 let mut csprng = rand_core::OsRng;
2039
2040 #[cfg(feature = "precomputed-tables")]
2042 let random_point = {
2043 let mut b = [0u8; 32];
2044 csprng.fill_bytes(&mut b);
2045 EdwardsPoint::mul_base_clamped(b) + constants::EIGHT_TORSION[1]
2046 };
2047 #[cfg(feature = "precomputed-tables")]
2049 let random_table = EdwardsBasepointTableRadix256::create(&random_point);
2050
2051 let a_bytes = [0xff; 32];
2056 assert_eq!(
2057 EdwardsPoint::mul_base_clamped(a_bytes),
2058 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2059 );
2060 #[cfg(feature = "precomputed-tables")]
2061 assert_eq!(
2062 random_table.mul_base_clamped(a_bytes),
2063 random_point.mul_clamped(a_bytes)
2064 );
2065
2066 for _ in 0..100 {
2068 let mut a_bytes = [0u8; 32];
2070 csprng.fill_bytes(&mut a_bytes);
2071
2072 assert_eq!(
2073 EdwardsPoint::mul_base_clamped(a_bytes),
2074 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2075 );
2076 #[cfg(feature = "precomputed-tables")]
2077 assert_eq!(
2078 random_table.mul_base_clamped(a_bytes),
2079 random_point.mul_clamped(a_bytes)
2080 );
2081 }
2082 }
2083
2084 #[test]
2085 #[cfg(feature = "alloc")]
2086 fn impl_sum() {
2087 let BASE = constants::ED25519_BASEPOINT_POINT;
2089
2090 let s1 = Scalar::from(999u64);
2091 let P1 = BASE * s1;
2092
2093 let s2 = Scalar::from(333u64);
2094 let P2 = BASE * s2;
2095
2096 let vec = vec![P1, P2];
2097 let sum: EdwardsPoint = vec.iter().sum();
2098
2099 assert_eq!(sum, P1 + P2);
2100
2101 let empty_vector: Vec<EdwardsPoint> = vec![];
2103 let sum: EdwardsPoint = empty_vector.iter().sum();
2104
2105 assert_eq!(sum, EdwardsPoint::identity());
2106
2107 let s = Scalar::from(2u64);
2109 let mapped = vec.iter().map(|x| x * s);
2110 let sum: EdwardsPoint = mapped.sum();
2111
2112 assert_eq!(sum, P1 * s + P2 * s);
2113 }
2114
2115 #[test]
2117 fn conditional_assign_for_affine_niels_point() {
2118 let id = AffineNielsPoint::identity();
2119 let mut p1 = AffineNielsPoint::identity();
2120 let bp = constants::ED25519_BASEPOINT_POINT.as_affine_niels();
2121
2122 p1.conditional_assign(&bp, Choice::from(0));
2123 assert_eq!(p1, id);
2124 p1.conditional_assign(&bp, Choice::from(1));
2125 assert_eq!(p1, bp);
2126 }
2127
2128 #[test]
2129 fn is_small_order() {
2130 assert!(!constants::ED25519_BASEPOINT_POINT.is_small_order());
2132 for torsion_point in &constants::EIGHT_TORSION {
2134 assert!(torsion_point.is_small_order());
2135 }
2136 }
2137
2138 #[test]
2139 fn compressed_identity() {
2140 assert_eq!(
2141 EdwardsPoint::identity().compress(),
2142 CompressedEdwardsY::identity()
2143 );
2144
2145 #[cfg(feature = "alloc")]
2146 {
2147 let compressed = EdwardsPoint::compress_batch(&[EdwardsPoint::identity()]);
2148 assert_eq!(&compressed, &[CompressedEdwardsY::identity()]);
2149 }
2150 }
2151
2152 #[cfg(feature = "alloc")]
2153 #[test]
2154 fn compress_batch() {
2155 let mut rng = rand::thread_rng();
2156
2157 let mut points = (1u64..16)
2160 .map(|n| constants::ED25519_BASEPOINT_POINT * Scalar::from(n))
2161 .collect::<Vec<_>>();
2162 points.extend(core::iter::repeat_with(|| EdwardsPoint::random(&mut rng)).take(100));
2163 let compressed = EdwardsPoint::compress_batch(&points);
2164
2165 for (point, compressed) in points.iter().zip(&compressed) {
2167 assert_eq!(&point.compress(), compressed);
2168 }
2169 }
2170
2171 #[test]
2172 fn is_identity() {
2173 assert!(EdwardsPoint::identity().is_identity());
2174 assert!(!constants::ED25519_BASEPOINT_POINT.is_identity());
2175 }
2176
2177 #[test]
2188 fn monte_carlo_overflow_underflow_debug_assert_test() {
2189 let mut P = constants::ED25519_BASEPOINT_POINT;
2190 for _ in 0..1_000 {
2193 P *= &A_SCALAR;
2194 }
2195 }
2196
2197 #[test]
2198 fn scalarmult_extended_point_works_both_ways() {
2199 let G: EdwardsPoint = constants::ED25519_BASEPOINT_POINT;
2200 let s: Scalar = A_SCALAR;
2201
2202 let P1 = G * s;
2203 let P2 = s * G;
2204
2205 assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
2206 }
2207
2208 #[cfg(feature = "alloc")]
2210 fn multiscalar_consistency_iter(n: usize) {
2211 let mut rng = rand::thread_rng();
2212
2213 let xs = (0..n).map(|_| Scalar::random(&mut rng)).collect::<Vec<_>>();
2216 let check = xs.iter().map(|xi| xi * xi).sum::<Scalar>();
2217
2218 let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::<Vec<_>>();
2220
2221 let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs);
2223 let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs);
2225 let H3 = EdwardsPoint::mul_base(&check);
2227
2228 assert_eq!(H1, H3);
2229 assert_eq!(H2, H3);
2230 }
2231
2232 #[test]
2236 #[cfg(feature = "alloc")]
2237 fn multiscalar_consistency_n_100() {
2238 let iters = 50;
2239 for _ in 0..iters {
2240 multiscalar_consistency_iter(100);
2241 }
2242 }
2243
2244 #[test]
2245 #[cfg(feature = "alloc")]
2246 fn multiscalar_consistency_n_250() {
2247 let iters = 50;
2248 for _ in 0..iters {
2249 multiscalar_consistency_iter(250);
2250 }
2251 }
2252
2253 #[test]
2254 #[cfg(feature = "alloc")]
2255 fn multiscalar_consistency_n_500() {
2256 let iters = 50;
2257 for _ in 0..iters {
2258 multiscalar_consistency_iter(500);
2259 }
2260 }
2261
2262 #[test]
2263 #[cfg(feature = "alloc")]
2264 fn multiscalar_consistency_n_1000() {
2265 let iters = 50;
2266 for _ in 0..iters {
2267 multiscalar_consistency_iter(1000);
2268 }
2269 }
2270
2271 #[test]
2272 #[cfg(feature = "alloc")]
2273 fn batch_to_montgomery() {
2274 let mut rng = rand::thread_rng();
2275
2276 let scalars = (0..128)
2277 .map(|_| Scalar::random(&mut rng))
2278 .collect::<Vec<_>>();
2279
2280 let points = scalars
2281 .iter()
2282 .map(EdwardsPoint::mul_base)
2283 .collect::<Vec<_>>();
2284
2285 let single_monts = points
2286 .iter()
2287 .map(EdwardsPoint::to_montgomery)
2288 .collect::<Vec<_>>();
2289
2290 for i in [0, 1, 2, 3, 10, 50, 128] {
2291 let invs = EdwardsPoint::to_montgomery_batch(&points[..i]);
2292 assert_eq!(&invs, &single_monts[..i]);
2293 }
2294 }
2295
2296 #[test]
2297 #[cfg(feature = "alloc")]
2298 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
2299 let mut rng = rand::thread_rng();
2300
2301 let static_scalars = (0..128)
2302 .map(|_| Scalar::random(&mut rng))
2303 .collect::<Vec<_>>();
2304
2305 let dynamic_scalars = (0..128)
2306 .map(|_| Scalar::random(&mut rng))
2307 .collect::<Vec<_>>();
2308
2309 let check_scalar: Scalar = static_scalars
2310 .iter()
2311 .chain(dynamic_scalars.iter())
2312 .map(|s| s * s)
2313 .sum();
2314
2315 let static_points = static_scalars
2316 .iter()
2317 .map(EdwardsPoint::mul_base)
2318 .collect::<Vec<_>>();
2319 let dynamic_points = dynamic_scalars
2320 .iter()
2321 .map(EdwardsPoint::mul_base)
2322 .collect::<Vec<_>>();
2323
2324 let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter());
2325
2326 assert_eq!(precomputation.len(), 128);
2327 assert!(!precomputation.is_empty());
2328
2329 let P = precomputation.vartime_mixed_multiscalar_mul(
2330 &static_scalars,
2331 &dynamic_scalars,
2332 &dynamic_points,
2333 );
2334
2335 use crate::traits::VartimeMultiscalarMul;
2336 let Q = EdwardsPoint::vartime_multiscalar_mul(
2337 static_scalars.iter().chain(dynamic_scalars.iter()),
2338 static_points.iter().chain(dynamic_points.iter()),
2339 );
2340
2341 let R = EdwardsPoint::mul_base(&check_scalar);
2342
2343 assert_eq!(P.compress(), R.compress());
2344 assert_eq!(Q.compress(), R.compress());
2345 }
2346
2347 mod vartime {
2348 use super::super::*;
2349 use super::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR, DOUBLE_SCALAR_MULT_RESULT};
2350
2351 #[test]
2353 fn double_scalar_mul_basepoint_vs_ed25519py() {
2354 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2355 let result =
2356 EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
2357 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2358 }
2359
2360 #[test]
2361 #[cfg(feature = "alloc")]
2362 fn multiscalar_mul_vs_ed25519py() {
2363 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2364 let result = EdwardsPoint::vartime_multiscalar_mul(
2365 &[A_SCALAR, B_SCALAR],
2366 &[A, constants::ED25519_BASEPOINT_POINT],
2367 );
2368 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2369 }
2370
2371 #[test]
2372 #[cfg(feature = "alloc")]
2373 fn multiscalar_mul_vartime_vs_consttime() {
2374 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2375 let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
2376 &[A_SCALAR, B_SCALAR],
2377 &[A, constants::ED25519_BASEPOINT_POINT],
2378 );
2379 let result_consttime = EdwardsPoint::multiscalar_mul(
2380 &[A_SCALAR, B_SCALAR],
2381 &[A, constants::ED25519_BASEPOINT_POINT],
2382 );
2383
2384 assert_eq!(result_vartime.compress(), result_consttime.compress());
2385 }
2386 }
2387
2388 #[test]
2389 #[cfg(feature = "serde")]
2390 fn serde_bincode_basepoint_roundtrip() {
2391 use bincode;
2392
2393 let encoded = bincode::serialize(&constants::ED25519_BASEPOINT_POINT).unwrap();
2394 let enc_compressed = bincode::serialize(&constants::ED25519_BASEPOINT_COMPRESSED).unwrap();
2395 assert_eq!(encoded, enc_compressed);
2396
2397 assert_eq!(encoded.len(), 32);
2399
2400 let dec_uncompressed: EdwardsPoint = bincode::deserialize(&encoded).unwrap();
2401 let dec_compressed: CompressedEdwardsY = bincode::deserialize(&encoded).unwrap();
2402
2403 assert_eq!(dec_uncompressed, constants::ED25519_BASEPOINT_POINT);
2404 assert_eq!(dec_compressed, constants::ED25519_BASEPOINT_COMPRESSED);
2405
2406 let raw_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
2408 let bp: EdwardsPoint = bincode::deserialize(raw_bytes).unwrap();
2409 assert_eq!(bp, constants::ED25519_BASEPOINT_POINT);
2410 }
2411
2412 #[cfg(all(feature = "alloc", feature = "digest"))]
2418 fn signal_test_vectors() -> Vec<Vec<&'static str>> {
2419 vec![
2420 vec![
2421 "214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f",
2422 "c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052",
2423 ],
2424 vec![
2425 "2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6",
2426 "d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f",
2427 ],
2428 vec![
2429 "84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
2430 "93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
2431 ],
2432 vec![
2433 "c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267",
2434 "43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537",
2435 ],
2436 vec![
2437 "26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76",
2438 "b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835",
2439 ],
2440 vec![
2441 "1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2",
2442 "da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c",
2443 ],
2444 vec![
2445 "48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
2446 "ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
2447 ],
2448 vec![
2449 "a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d",
2450 "fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0",
2451 ],
2452 vec![
2453 "80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
2454 "57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
2455 ],
2456 vec![
2457 "f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617",
2458 "785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0",
2459 ],
2460 ]
2461 }
2462
2463 #[test]
2464 #[allow(deprecated)]
2465 #[cfg(all(feature = "alloc", feature = "digest"))]
2466 fn elligator_signal_test_vectors() {
2467 for vector in signal_test_vectors().iter() {
2468 let input = hex::decode(vector[0]).unwrap();
2469 let output = hex::decode(vector[1]).unwrap();
2470
2471 let point = EdwardsPoint::nonspec_map_to_curve::<sha2::Sha512>(&input);
2472 assert_eq!(point.compress().to_bytes(), output[..]);
2473 }
2474 }
2475
2476 #[cfg(all(feature = "alloc", feature = "digest"))]
2480 const RFC_HASH_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[
2481 (
2482 b"",
2483 "1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da",
2484 "222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b",
2485 ),
2486 (
2487 b"abc",
2488 "5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8",
2489 "67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42",
2490 ),
2491 (
2492 b"abcdef0123456789",
2493 "1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1",
2494 "2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb",
2495 ),
2496 (
2497 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
2498 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
2499 "35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73",
2500 "2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450",
2501 ),
2502 (
2503 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2504 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2505 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2506 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2507 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2508 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2509 "6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff",
2510 "2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37"
2511 )
2512 ];
2513
2514 #[test]
2515 #[cfg(all(feature = "alloc", feature = "digest"))]
2516 fn elligator_hash_to_curve_test_vectors() {
2517 let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_";
2518 for (index, vector) in RFC_HASH_TO_CURVE_KAT.iter().enumerate() {
2519 let input = vector.0;
2520
2521 let expected_output = {
2522 let mut x_bytes = hex::decode(vector.1).unwrap();
2523 x_bytes.reverse();
2524 let x = FieldElement::from_bytes(&x_bytes.try_into().unwrap());
2525
2526 let mut y_bytes = hex::decode(vector.2).unwrap();
2527 y_bytes.reverse();
2528 let y = FieldElement::from_bytes(&y_bytes.try_into().unwrap());
2529
2530 EdwardsPoint {
2531 X: x,
2532 Y: y,
2533 Z: FieldElement::ONE,
2534 T: &x * &y,
2535 }
2536 };
2537
2538 let computed = EdwardsPoint::hash_to_curve::<sha2::Sha512>(&[&input], &[dst]);
2539 assert_eq!(computed, expected_output, "Failed in test {}", index);
2540 }
2541 }
2542}