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;
123#[cfg(feature = "digest")]
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::{clamp_integer, Scalar};
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 LookupTableRadix128, LookupTableRadix16, LookupTableRadix256, LookupTableRadix32,
146 LookupTableRadix64,
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, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
159#[cfg(all(feature = "alloc", not(verify)))]
160use alloc::vec::Vec;
161
162#[derive(Copy, Clone, Eq, PartialEq, Hash)]
172pub struct CompressedEdwardsY(pub [u8; 32]);
173
174impl ConstantTimeEq for CompressedEdwardsY {
175 fn ct_eq(&self, other: &CompressedEdwardsY) -> Choice {
176 self.as_bytes().ct_eq(other.as_bytes())
177 }
178}
179
180impl Debug for CompressedEdwardsY {
181 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182 write!(f, "CompressedEdwardsY: {:?}", self.as_bytes())
183 }
184}
185
186impl CompressedEdwardsY {
187 pub const fn as_bytes(&self) -> &[u8; 32] {
189 &self.0
190 }
191
192 pub const fn to_bytes(&self) -> [u8; 32] {
194 self.0
195 }
196
197 pub fn decompress(&self) -> Option<EdwardsPoint> {
202 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(self);
203
204 if is_valid_y_coord.into() {
205 Some(decompress::step_2(self, X, Y, Z))
206 } else {
207 None
208 }
209 }
210}
211
212mod decompress {
213 use super::*;
214
215 #[rustfmt::skip] pub(super) fn step_1(
217 repr: &CompressedEdwardsY,
218 ) -> (Choice, FieldElement, FieldElement, FieldElement) {
219 let Y = FieldElement::from_bytes(repr.as_bytes());
220 let Z = FieldElement::ONE;
221 let YY = Y.square();
222 let u = &YY - &Z; let v = &(&YY * &constants::EDWARDS_D) + &Z; let (is_valid_y_coord, X) = FieldElement::sqrt_ratio_i(&u, &v);
225
226 (is_valid_y_coord, X, Y, Z)
227 }
228
229 #[rustfmt::skip]
230 pub(super) fn step_2(
231 repr: &CompressedEdwardsY,
232 mut X: FieldElement,
233 Y: FieldElement,
234 Z: FieldElement,
235 ) -> EdwardsPoint {
236 let compressed_sign_bit = Choice::from(repr.as_bytes()[31] >> 7);
239 let x_neg = -&X;
240 X.conditional_assign(&x_neg, 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 #[allow(clippy::redundant_closure)]
416 bytes.try_into().map(|b| CompressedEdwardsY(b))
417 }
418}
419
420impl Identity for EdwardsPoint {
421 fn identity() -> EdwardsPoint {
422 EdwardsPoint {
423 X: FieldElement::ZERO,
424 Y: FieldElement::ONE,
425 Z: FieldElement::ONE,
426 T: FieldElement::ZERO,
427 }
428 }
429}
430
431impl Default for EdwardsPoint {
432 fn default() -> EdwardsPoint {
433 EdwardsPoint::identity()
434 }
435}
436
437#[cfg(feature = "zeroize")]
442impl Zeroize for CompressedEdwardsY {
443 fn zeroize(&mut self) {
445 self.0.zeroize();
446 self.0[0] = 1;
447 }
448}
449
450#[cfg(feature = "zeroize")]
451impl Zeroize for EdwardsPoint {
452 fn zeroize(&mut self) {
454 self.X.zeroize();
455 self.Y = FieldElement::ONE;
456 self.Z = FieldElement::ONE;
457 self.T.zeroize();
458 }
459}
460
461impl ValidityCheck for EdwardsPoint {
466 fn is_valid(&self) -> bool {
467 let point_on_curve = self.as_projective().is_valid();
468 let on_segre_image = (&self.X * &self.Y) == (&self.Z * &self.T);
469
470 point_on_curve && on_segre_image
471 }
472}
473
474impl ConditionallySelectable for EdwardsPoint {
479 fn conditional_select(a: &EdwardsPoint, b: &EdwardsPoint, choice: Choice) -> EdwardsPoint {
480 EdwardsPoint {
481 X: FieldElement::conditional_select(&a.X, &b.X, choice),
482 Y: FieldElement::conditional_select(&a.Y, &b.Y, choice),
483 Z: FieldElement::conditional_select(&a.Z, &b.Z, choice),
484 T: FieldElement::conditional_select(&a.T, &b.T, choice),
485 }
486 }
487}
488
489impl ConstantTimeEq for EdwardsPoint {
494 fn ct_eq(&self, other: &EdwardsPoint) -> Choice {
495 (&self.X * &other.Z).ct_eq(&(&other.X * &self.Z))
502 & (&self.Y * &other.Z).ct_eq(&(&other.Y * &self.Z))
503 }
504}
505
506impl PartialEq for EdwardsPoint {
507 fn eq(&self, other: &EdwardsPoint) -> bool {
508 self.ct_eq(other).into()
509 }
510}
511
512impl Eq for EdwardsPoint {}
513
514impl EdwardsPoint {
519 pub(crate) fn as_projective_niels(&self) -> ProjectiveNielsPoint {
521 ProjectiveNielsPoint {
522 Y_plus_X: &self.Y + &self.X,
523 Y_minus_X: &self.Y - &self.X,
524 Z: self.Z,
525 T2d: &self.T * &constants::EDWARDS_D2,
526 }
527 }
528
529 pub(crate) const fn as_projective(&self) -> ProjectivePoint {
534 ProjectivePoint {
535 X: self.X,
536 Y: self.Y,
537 Z: self.Z,
538 }
539 }
540
541 pub(crate) fn as_affine_niels(&self) -> AffineNielsPoint {
544 let recip = self.Z.invert();
545 let x = &self.X * &recip;
546 let y = &self.Y * &recip;
547 let xy2d = &(&x * &y) * &constants::EDWARDS_D2;
548 AffineNielsPoint {
549 y_plus_x: &y + &x,
550 y_minus_x: &y - &x,
551 xy2d,
552 }
553 }
554
555 pub(crate) fn to_affine(self) -> AffinePoint {
557 let recip = self.Z.invert();
558 let x = &self.X * &recip;
559 let y = &self.Y * &recip;
560 AffinePoint { x, y }
561 }
562
563 pub fn to_montgomery(&self) -> MontgomeryPoint {
573 let U = &self.Z + &self.Y;
579 let W = &self.Z - &self.Y;
580 let u = &U * &W.invert();
581 MontgomeryPoint(u.to_bytes())
582 }
583
584 #[cfg(all(feature = "alloc", not(verify)))]
587 pub fn to_montgomery_batch(eds: &[Self]) -> Vec<MontgomeryPoint> {
588 let mut denominators = eds.iter().map(|p| &p.Z - &p.Y).collect::<Vec<_>>();
594 FieldElement::batch_invert(&mut denominators);
595
596 let mut ret = Vec::with_capacity(eds.len());
598 for (ed, d) in eds.iter().zip(denominators.iter()) {
599 let u = &(&ed.Z + &ed.Y) * d;
600 ret.push(MontgomeryPoint(u.to_bytes()));
601 }
602
603 ret
604 }
605
606 pub fn compress(&self) -> CompressedEdwardsY {
608 self.to_affine().compress()
609 }
610
611 #[cfg(all(feature = "alloc", not(verify)))]
614 pub fn compress_batch(inputs: &[EdwardsPoint]) -> Vec<CompressedEdwardsY> {
615 let mut zs = inputs.iter().map(|input| input.Z).collect::<Vec<_>>();
616 FieldElement::batch_invert(&mut zs);
617
618 inputs
619 .iter()
620 .zip(&zs)
621 .map(|(input, recip)| {
622 let x = &input.X * recip;
623 let y = &input.Y * recip;
624 AffinePoint { x, y }.compress()
625 })
626 .collect()
627 }
628
629 #[cfg(feature = "digest")]
630 pub fn hash_to_curve<D>(bytes: &[&[u8]], domain_sep: &[&[u8]]) -> EdwardsPoint
639 where
640 D: BlockSizeUser + Default + FixedOutput<OutputSize = U64> + HashMarker,
641 D::BlockSize: IsGreater<D::OutputSize, Output = True>,
642 {
643 let fe = FieldElement::hash_to_field::<D>(bytes, domain_sep);
647 let (M1, is_sq) = crate::montgomery::elligator_encode(&fe);
648
649 let mut E1_opt = M1
666 .to_edwards(0)
667 .expect("Montgomery conversion to Edwards point in Elligator failed");
668
669 let mont_v =
671 &(&ED25519_SQRTAM2 * &FieldElement::from_bytes(&M1.to_bytes())) * &E1_opt.X.invert();
672 E1_opt.X.conditional_negate(is_sq ^ mont_v.is_negative());
673 E1_opt.mul_by_cofactor()
674 }
675
676 #[cfg(feature = "digest")]
677 #[deprecated(
686 since = "4.0.0",
687 note = "previously named `hash_from_bytes`, this is not a secure hash function"
688 )]
689 pub fn nonspec_map_to_curve<D>(bytes: &[u8]) -> EdwardsPoint
690 where
691 D: Digest<OutputSize = U64> + Default,
692 {
693 let mut hash = D::new();
694 hash.update(bytes);
695 let h = hash.finalize();
696 let mut res = [0u8; 32];
697 res.copy_from_slice(&h[..32]);
698
699 let sign_bit = (res[31] & 0x80) >> 7;
700
701 let fe = FieldElement::from_bytes(&res);
702
703 let (M1, _) = crate::montgomery::elligator_encode(&fe);
704 let E1_opt = M1.to_edwards(sign_bit);
705
706 E1_opt
707 .expect("Montgomery conversion to Edwards point in Elligator failed")
708 .mul_by_cofactor()
709 }
710
711 #[cfg(any(test, feature = "rand_core"))]
726 pub fn random(mut rng: impl RngCore) -> Self {
727 let mut repr = CompressedEdwardsY([0u8; 32]);
728 loop {
729 rng.fill_bytes(&mut repr.0);
730 if let Some(p) = repr.decompress() {
731 if !IsIdentity::is_identity(&p) {
732 break p;
733 }
734 }
735 }
736 }
737}
738
739impl EdwardsPoint {
744 pub(crate) fn double(&self) -> EdwardsPoint {
746 self.as_projective().double().as_extended()
747 }
748}
749
750impl<'a> Add<&'a EdwardsPoint> for &EdwardsPoint {
755 type Output = EdwardsPoint;
756 fn add(self, other: &'a EdwardsPoint) -> EdwardsPoint {
757 (self + &other.as_projective_niels()).as_extended()
758 }
759}
760
761define_add_variants!(
762 LHS = EdwardsPoint,
763 RHS = EdwardsPoint,
764 Output = EdwardsPoint
765);
766
767impl<'a> AddAssign<&'a EdwardsPoint> for EdwardsPoint {
768 fn add_assign(&mut self, _rhs: &'a EdwardsPoint) {
769 *self = (self as &EdwardsPoint) + _rhs;
770 }
771}
772
773define_add_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
774
775impl<'a> Sub<&'a EdwardsPoint> for &EdwardsPoint {
776 type Output = EdwardsPoint;
777 fn sub(self, other: &'a EdwardsPoint) -> EdwardsPoint {
778 (self - &other.as_projective_niels()).as_extended()
779 }
780}
781
782define_sub_variants!(
783 LHS = EdwardsPoint,
784 RHS = EdwardsPoint,
785 Output = EdwardsPoint
786);
787
788impl<'a> SubAssign<&'a EdwardsPoint> for EdwardsPoint {
789 fn sub_assign(&mut self, _rhs: &'a EdwardsPoint) {
790 *self = (self as &EdwardsPoint) - _rhs;
791 }
792}
793
794define_sub_assign_variants!(LHS = EdwardsPoint, RHS = EdwardsPoint);
795
796impl<T> Sum<T> for EdwardsPoint
797where
798 T: Borrow<EdwardsPoint>,
799{
800 fn sum<I>(iter: I) -> Self
801 where
802 I: Iterator<Item = T>,
803 {
804 iter.fold(EdwardsPoint::identity(), |acc, item| acc + item.borrow())
805 }
806}
807
808impl Neg for &EdwardsPoint {
813 type Output = EdwardsPoint;
814
815 fn neg(self) -> EdwardsPoint {
816 EdwardsPoint {
817 X: -(&self.X),
818 Y: self.Y,
819 Z: self.Z,
820 T: -(&self.T),
821 }
822 }
823}
824
825impl Neg for EdwardsPoint {
826 type Output = EdwardsPoint;
827
828 fn neg(self) -> EdwardsPoint {
829 -&self
830 }
831}
832
833impl<'a> MulAssign<&'a Scalar> for EdwardsPoint {
838 fn mul_assign(&mut self, scalar: &'a Scalar) {
839 let result = (self as &EdwardsPoint) * scalar;
840 *self = result;
841 }
842}
843
844define_mul_assign_variants!(LHS = EdwardsPoint, RHS = Scalar);
845
846define_mul_variants!(LHS = EdwardsPoint, RHS = Scalar, Output = EdwardsPoint);
847define_mul_variants!(LHS = Scalar, RHS = EdwardsPoint, Output = EdwardsPoint);
848
849impl<'a> Mul<&'a Scalar> for &EdwardsPoint {
850 type Output = EdwardsPoint;
851 fn mul(self, scalar: &'a Scalar) -> EdwardsPoint {
856 crate::backend::variable_base_mul(self, scalar)
857 }
858}
859
860impl<'a> Mul<&'a EdwardsPoint> for &Scalar {
861 type Output = EdwardsPoint;
862
863 fn mul(self, point: &'a EdwardsPoint) -> EdwardsPoint {
868 point * self
869 }
870}
871
872impl EdwardsPoint {
873 pub fn mul_base(scalar: &Scalar) -> Self {
878 #[cfg(not(feature = "precomputed-tables"))]
879 {
880 scalar * constants::ED25519_BASEPOINT_POINT
881 }
882
883 #[cfg(feature = "precomputed-tables")]
884 {
885 scalar * constants::ED25519_BASEPOINT_TABLE
886 }
887 }
888
889 pub fn mul_clamped(self, bytes: [u8; 32]) -> Self {
892 let s = Scalar {
900 bytes: clamp_integer(bytes),
901 };
902 s * self
903 }
904
905 pub fn mul_base_clamped(bytes: [u8; 32]) -> Self {
908 let s = Scalar {
912 bytes: clamp_integer(bytes),
913 };
914 Self::mul_base(&s)
915 }
916}
917
918#[cfg(feature = "alloc")]
926impl MultiscalarMul for EdwardsPoint {
927 type Point = EdwardsPoint;
928
929 fn multiscalar_mul<I, J>(scalars: I, points: J) -> EdwardsPoint
930 where
931 I: IntoIterator,
932 I::Item: Borrow<Scalar>,
933 J: IntoIterator,
934 J::Item: Borrow<EdwardsPoint>,
935 {
936 let mut scalars = scalars.into_iter();
938 let mut points = points.into_iter();
939
940 let (s_lo, s_hi) = scalars.by_ref().size_hint();
942 let (p_lo, p_hi) = points.by_ref().size_hint();
943
944 assert_eq!(s_lo, p_lo);
946 assert_eq!(s_hi, Some(s_lo));
947 assert_eq!(p_hi, Some(p_lo));
948
949 let _size = s_lo;
952
953 crate::backend::straus_multiscalar_mul(scalars, points)
954 }
955}
956
957#[cfg(feature = "alloc")]
958impl VartimeMultiscalarMul for EdwardsPoint {
959 type Point = EdwardsPoint;
960
961 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<EdwardsPoint>
962 where
963 I: IntoIterator,
964 I::Item: Borrow<Scalar>,
965 J: IntoIterator<Item = Option<EdwardsPoint>>,
966 {
967 let mut scalars = scalars.into_iter();
969 let mut points = points.into_iter();
970
971 let (s_lo, s_hi) = scalars.by_ref().size_hint();
973 let (p_lo, p_hi) = points.by_ref().size_hint();
974
975 assert_eq!(s_lo, p_lo);
977 assert_eq!(s_hi, Some(s_lo));
978 assert_eq!(p_hi, Some(p_lo));
979
980 let size = s_lo;
983
984 if size < 190 {
985 crate::backend::straus_optional_multiscalar_mul(scalars, points)
986 } else {
987 crate::backend::pippenger_optional_multiscalar_mul(scalars, points)
988 }
989 }
990}
991
992#[cfg(feature = "alloc")]
997pub struct VartimeEdwardsPrecomputation(crate::backend::VartimePrecomputedStraus);
998
999#[cfg(feature = "alloc")]
1000impl VartimePrecomputedMultiscalarMul for VartimeEdwardsPrecomputation {
1001 type Point = EdwardsPoint;
1002
1003 fn new<I>(static_points: I) -> Self
1004 where
1005 I: IntoIterator,
1006 I::Item: Borrow<Self::Point>,
1007 {
1008 Self(crate::backend::VartimePrecomputedStraus::new(static_points))
1009 }
1010
1011 fn len(&self) -> usize {
1012 self.0.len()
1013 }
1014
1015 fn is_empty(&self) -> bool {
1016 self.0.is_empty()
1017 }
1018
1019 fn optional_mixed_multiscalar_mul<I, J, K>(
1020 &self,
1021 static_scalars: I,
1022 dynamic_scalars: J,
1023 dynamic_points: K,
1024 ) -> Option<Self::Point>
1025 where
1026 I: IntoIterator,
1027 I::Item: Borrow<Scalar>,
1028 J: IntoIterator,
1029 J::Item: Borrow<Scalar>,
1030 K: IntoIterator<Item = Option<Self::Point>>,
1031 {
1032 self.0
1033 .optional_mixed_multiscalar_mul(static_scalars, dynamic_scalars, dynamic_points)
1034 }
1035}
1036
1037#[cfg(not(verify))]
1038impl EdwardsPoint {
1039 pub fn vartime_double_scalar_mul_basepoint(
1041 a: &Scalar,
1042 A: &EdwardsPoint,
1043 b: &Scalar,
1044 ) -> EdwardsPoint {
1045 crate::backend::vartime_double_base_mul(a, A, b)
1046 }
1047}
1048
1049#[cfg(feature = "precomputed-tables")]
1050macro_rules! impl_basepoint_table {
1051 (Name = $name:ident, LookupTable = $table:ident, Point = $point:ty, Radix = $radix:expr, Additions = $adds:expr) => {
1052 #[derive(Clone)]
1084 #[repr(transparent)]
1085 pub struct $name(pub(crate) [$table<AffineNielsPoint>; 32]);
1086
1087 impl BasepointTable for $name {
1088 type Point = $point;
1089
1090 fn create(basepoint: &$point) -> $name {
1092 let mut table = $name([$table::default(); 32]);
1094 let mut P = *basepoint;
1095 for i in 0..32 {
1096 table.0[i] = $table::from(&P);
1098 P = P.mul_by_pow_2($radix + $radix);
1099 }
1100 table
1101 }
1102
1103 fn basepoint(&self) -> $point {
1105 (&<$point>::identity() + &self.0[0].select(1)).as_extended()
1108 }
1109
1110 fn mul_base(&self, scalar: &Scalar) -> $point {
1153 let a = scalar.as_radix_2w($radix);
1154
1155 let tables = &self.0;
1156 let mut P = <$point>::identity();
1157
1158 for i in (0..$adds).filter(|x| x % 2 == 1) {
1159 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1160 }
1161
1162 P = P.mul_by_pow_2($radix);
1163
1164 for i in (0..$adds).filter(|x| x % 2 == 0) {
1165 P = (&P + &tables[i / 2].select(a[i])).as_extended();
1166 }
1167
1168 P
1169 }
1170 }
1171
1172 impl<'a, 'b> Mul<&'b Scalar> for &'a $name {
1173 type Output = $point;
1174
1175 fn mul(self, scalar: &'b Scalar) -> $point {
1178 self.mul_base(scalar)
1180 }
1181 }
1182
1183 impl<'a, 'b> Mul<&'a $name> for &'b Scalar {
1184 type Output = $point;
1185
1186 fn mul(self, basepoint_table: &'a $name) -> $point {
1189 basepoint_table * self
1190 }
1191 }
1192
1193 impl Debug for $name {
1194 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1195 write!(f, "{:?}([\n", stringify!($name))?;
1196 for i in 0..32 {
1197 write!(f, "\t{:?},\n", &self.0[i])?;
1198 }
1199 write!(f, "])")
1200 }
1201 }
1202 };
1203} cfg_if! {
1207 if #[cfg(feature = "precomputed-tables")] {
1208 impl_basepoint_table! {
1209 Name = EdwardsBasepointTable,
1210 LookupTable = LookupTableRadix16,
1211 Point = EdwardsPoint,
1212 Radix = 4,
1213 Additions = 64
1214 }
1215 impl_basepoint_table! {
1216 Name = EdwardsBasepointTableRadix32,
1217 LookupTable = LookupTableRadix32,
1218 Point = EdwardsPoint,
1219 Radix = 5,
1220 Additions = 52
1221 }
1222 impl_basepoint_table! {
1223 Name = EdwardsBasepointTableRadix64,
1224 LookupTable = LookupTableRadix64,
1225 Point = EdwardsPoint,
1226 Radix = 6,
1227 Additions = 43
1228 }
1229 impl_basepoint_table! {
1230 Name = EdwardsBasepointTableRadix128,
1231 LookupTable = LookupTableRadix128,
1232 Point = EdwardsPoint,
1233 Radix = 7,
1234 Additions = 37
1235 }
1236 impl_basepoint_table! {
1237 Name = EdwardsBasepointTableRadix256,
1238 LookupTable = LookupTableRadix256,
1239 Point = EdwardsPoint,
1240 Radix = 8,
1241 Additions = 33
1242 }
1243
1244 pub type EdwardsBasepointTableRadix16 = EdwardsBasepointTable;
1251 }
1252}
1253
1254#[cfg(feature = "precomputed-tables")]
1255macro_rules! impl_basepoint_table_conversions {
1256 (LHS = $lhs:ty, RHS = $rhs:ty) => {
1257 impl<'a> From<&'a $lhs> for $rhs {
1258 fn from(table: &'a $lhs) -> $rhs {
1259 <$rhs>::create(&table.basepoint())
1260 }
1261 }
1262
1263 impl<'a> From<&'a $rhs> for $lhs {
1264 fn from(table: &'a $rhs) -> $lhs {
1265 <$lhs>::create(&table.basepoint())
1266 }
1267 }
1268 };
1269}
1270
1271cfg_if! {
1272 if #[cfg(feature = "precomputed-tables")] {
1273 impl_basepoint_table_conversions! {
1275 LHS = EdwardsBasepointTableRadix16,
1276 RHS = EdwardsBasepointTableRadix32
1277 }
1278 impl_basepoint_table_conversions! {
1279 LHS = EdwardsBasepointTableRadix16,
1280 RHS = EdwardsBasepointTableRadix64
1281 }
1282 impl_basepoint_table_conversions! {
1283 LHS = EdwardsBasepointTableRadix16,
1284 RHS = EdwardsBasepointTableRadix128
1285 }
1286 impl_basepoint_table_conversions! {
1287 LHS = EdwardsBasepointTableRadix16,
1288 RHS = EdwardsBasepointTableRadix256
1289 }
1290
1291 impl_basepoint_table_conversions! {
1293 LHS = EdwardsBasepointTableRadix32,
1294 RHS = EdwardsBasepointTableRadix64
1295 }
1296 impl_basepoint_table_conversions! {
1297 LHS = EdwardsBasepointTableRadix32,
1298 RHS = EdwardsBasepointTableRadix128
1299 }
1300 impl_basepoint_table_conversions! {
1301 LHS = EdwardsBasepointTableRadix32,
1302 RHS = EdwardsBasepointTableRadix256
1303 }
1304
1305 impl_basepoint_table_conversions! {
1307 LHS = EdwardsBasepointTableRadix64,
1308 RHS = EdwardsBasepointTableRadix128
1309 }
1310 impl_basepoint_table_conversions! {
1311 LHS = EdwardsBasepointTableRadix64,
1312 RHS = EdwardsBasepointTableRadix256
1313 }
1314
1315 impl_basepoint_table_conversions! {
1317 LHS = EdwardsBasepointTableRadix128,
1318 RHS = EdwardsBasepointTableRadix256
1319 }
1320 }
1321}
1322
1323impl EdwardsPoint {
1324 pub fn mul_by_cofactor(&self) -> EdwardsPoint {
1326 self.mul_by_pow_2(3)
1327 }
1328
1329 pub(crate) fn mul_by_pow_2(&self, k: u32) -> EdwardsPoint {
1331 debug_assert!(k > 0);
1332 let mut r: CompletedPoint;
1333 let mut s = self.as_projective();
1334 let mut i = 0;
1335 while i < k - 1 {
1336 r = s.double();
1337 s = r.as_projective();
1338 i += 1;
1339 }
1340 s.double().as_extended()
1342 }
1343
1344 pub fn is_small_order(&self) -> bool {
1368 self.mul_by_cofactor().is_identity()
1369 }
1370
1371 pub fn is_torsion_free(&self) -> bool {
1398 (self * constants::BASEPOINT_ORDER_PRIVATE).is_identity()
1399 }
1400}
1401
1402impl Debug for EdwardsPoint {
1407 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1408 write!(
1409 f,
1410 "EdwardsPoint{{\n\tX: {:?},\n\tY: {:?},\n\tZ: {:?},\n\tT: {:?}\n}}",
1411 &self.X, &self.Y, &self.Z, &self.T
1412 )
1413 }
1414}
1415
1416#[cfg(feature = "group")]
1423impl group::Group for EdwardsPoint {
1424 type Scalar = Scalar;
1425
1426 fn random(rng: impl RngCore) -> Self {
1427 Self::random(rng)
1429 }
1430
1431 fn identity() -> Self {
1432 Identity::identity()
1433 }
1434
1435 fn generator() -> Self {
1436 constants::ED25519_BASEPOINT_POINT
1437 }
1438
1439 fn is_identity(&self) -> Choice {
1440 self.ct_eq(&Identity::identity())
1441 }
1442
1443 fn double(&self) -> Self {
1444 self.double()
1445 }
1446}
1447
1448#[cfg(feature = "group")]
1449impl GroupEncoding for EdwardsPoint {
1450 type Repr = [u8; 32];
1451
1452 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1453 let repr = CompressedEdwardsY(*bytes);
1454 let (is_valid_y_coord, X, Y, Z) = decompress::step_1(&repr);
1455 CtOption::new(decompress::step_2(&repr, X, Y, Z), is_valid_y_coord)
1456 }
1457
1458 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1459 Self::from_bytes(bytes)
1461 }
1462
1463 fn to_bytes(&self) -> Self::Repr {
1464 self.compress().to_bytes()
1465 }
1466}
1467
1468#[cfg(feature = "group")]
1471#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
1472pub struct SubgroupPoint(EdwardsPoint);
1473
1474#[cfg(feature = "group")]
1475impl From<SubgroupPoint> for EdwardsPoint {
1476 fn from(p: SubgroupPoint) -> Self {
1477 p.0
1478 }
1479}
1480
1481#[cfg(feature = "group")]
1482impl Neg for SubgroupPoint {
1483 type Output = Self;
1484
1485 fn neg(self) -> Self::Output {
1486 SubgroupPoint(-self.0)
1487 }
1488}
1489
1490#[cfg(feature = "group")]
1491impl Add<&SubgroupPoint> for &SubgroupPoint {
1492 type Output = SubgroupPoint;
1493 fn add(self, other: &SubgroupPoint) -> SubgroupPoint {
1494 SubgroupPoint(self.0 + other.0)
1495 }
1496}
1497
1498#[cfg(feature = "group")]
1499define_add_variants!(
1500 LHS = SubgroupPoint,
1501 RHS = SubgroupPoint,
1502 Output = SubgroupPoint
1503);
1504
1505#[cfg(feature = "group")]
1506impl Add<&SubgroupPoint> for &EdwardsPoint {
1507 type Output = EdwardsPoint;
1508 fn add(self, other: &SubgroupPoint) -> EdwardsPoint {
1509 self + other.0
1510 }
1511}
1512
1513#[cfg(feature = "group")]
1514define_add_variants!(
1515 LHS = EdwardsPoint,
1516 RHS = SubgroupPoint,
1517 Output = EdwardsPoint
1518);
1519
1520#[cfg(feature = "group")]
1521impl AddAssign<&SubgroupPoint> for SubgroupPoint {
1522 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1523 self.0 += rhs.0
1524 }
1525}
1526
1527#[cfg(feature = "group")]
1528define_add_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1529
1530#[cfg(feature = "group")]
1531impl AddAssign<&SubgroupPoint> for EdwardsPoint {
1532 fn add_assign(&mut self, rhs: &SubgroupPoint) {
1533 *self += rhs.0
1534 }
1535}
1536
1537#[cfg(feature = "group")]
1538define_add_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1539
1540#[cfg(feature = "group")]
1541impl Sub<&SubgroupPoint> for &SubgroupPoint {
1542 type Output = SubgroupPoint;
1543 fn sub(self, other: &SubgroupPoint) -> SubgroupPoint {
1544 SubgroupPoint(self.0 - other.0)
1545 }
1546}
1547
1548#[cfg(feature = "group")]
1549define_sub_variants!(
1550 LHS = SubgroupPoint,
1551 RHS = SubgroupPoint,
1552 Output = SubgroupPoint
1553);
1554
1555#[cfg(feature = "group")]
1556impl Sub<&SubgroupPoint> for &EdwardsPoint {
1557 type Output = EdwardsPoint;
1558 fn sub(self, other: &SubgroupPoint) -> EdwardsPoint {
1559 self - other.0
1560 }
1561}
1562
1563#[cfg(feature = "group")]
1564define_sub_variants!(
1565 LHS = EdwardsPoint,
1566 RHS = SubgroupPoint,
1567 Output = EdwardsPoint
1568);
1569
1570#[cfg(feature = "group")]
1571impl SubAssign<&SubgroupPoint> for SubgroupPoint {
1572 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1573 self.0 -= rhs.0;
1574 }
1575}
1576
1577#[cfg(feature = "group")]
1578define_sub_assign_variants!(LHS = SubgroupPoint, RHS = SubgroupPoint);
1579
1580#[cfg(feature = "group")]
1581impl SubAssign<&SubgroupPoint> for EdwardsPoint {
1582 fn sub_assign(&mut self, rhs: &SubgroupPoint) {
1583 *self -= rhs.0;
1584 }
1585}
1586
1587#[cfg(feature = "group")]
1588define_sub_assign_variants!(LHS = EdwardsPoint, RHS = SubgroupPoint);
1589
1590#[cfg(feature = "group")]
1591impl<T> Sum<T> for SubgroupPoint
1592where
1593 T: Borrow<SubgroupPoint>,
1594{
1595 fn sum<I>(iter: I) -> Self
1596 where
1597 I: Iterator<Item = T>,
1598 {
1599 use group::Group;
1600 iter.fold(SubgroupPoint::identity(), |acc, item| acc + item.borrow())
1601 }
1602}
1603
1604#[cfg(feature = "group")]
1605impl Mul<&Scalar> for &SubgroupPoint {
1606 type Output = SubgroupPoint;
1607
1608 fn mul(self, scalar: &Scalar) -> SubgroupPoint {
1613 SubgroupPoint(self.0 * scalar)
1614 }
1615}
1616
1617#[cfg(feature = "group")]
1618define_mul_variants!(LHS = Scalar, RHS = SubgroupPoint, Output = SubgroupPoint);
1619
1620#[cfg(feature = "group")]
1621impl Mul<&SubgroupPoint> for &Scalar {
1622 type Output = SubgroupPoint;
1623
1624 fn mul(self, point: &SubgroupPoint) -> SubgroupPoint {
1629 point * self
1630 }
1631}
1632
1633#[cfg(feature = "group")]
1634define_mul_variants!(LHS = SubgroupPoint, RHS = Scalar, Output = SubgroupPoint);
1635
1636#[cfg(feature = "group")]
1637impl MulAssign<&Scalar> for SubgroupPoint {
1638 fn mul_assign(&mut self, scalar: &Scalar) {
1639 self.0 *= scalar;
1640 }
1641}
1642
1643#[cfg(feature = "group")]
1644define_mul_assign_variants!(LHS = SubgroupPoint, RHS = Scalar);
1645
1646#[cfg(feature = "group")]
1647impl ConstantTimeEq for SubgroupPoint {
1648 fn ct_eq(&self, other: &SubgroupPoint) -> Choice {
1649 self.0.ct_eq(&other.0)
1650 }
1651}
1652
1653#[cfg(feature = "group")]
1654impl ConditionallySelectable for SubgroupPoint {
1655 fn conditional_select(a: &SubgroupPoint, b: &SubgroupPoint, choice: Choice) -> SubgroupPoint {
1656 SubgroupPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice))
1657 }
1658}
1659
1660#[cfg(all(feature = "group", feature = "zeroize"))]
1661impl Zeroize for SubgroupPoint {
1662 fn zeroize(&mut self) {
1663 self.0.zeroize();
1664 }
1665}
1666
1667#[cfg(feature = "group")]
1668impl group::Group for SubgroupPoint {
1669 type Scalar = Scalar;
1670
1671 fn random(mut rng: impl RngCore) -> Self {
1672 use group::ff::Field;
1673
1674 let s = loop {
1677 let s: Scalar = Field::random(&mut rng);
1678 if !s.is_zero_vartime() {
1679 break s;
1680 }
1681 };
1682
1683 Self::generator() * s
1685 }
1686
1687 fn identity() -> Self {
1688 SubgroupPoint(Identity::identity())
1689 }
1690
1691 fn generator() -> Self {
1692 SubgroupPoint(EdwardsPoint::generator())
1693 }
1694
1695 fn is_identity(&self) -> Choice {
1696 self.0.ct_eq(&Identity::identity())
1697 }
1698
1699 fn double(&self) -> Self {
1700 SubgroupPoint(self.0.double())
1701 }
1702}
1703
1704#[cfg(feature = "group")]
1705impl GroupEncoding for SubgroupPoint {
1706 type Repr = <EdwardsPoint as GroupEncoding>::Repr;
1707
1708 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1709 EdwardsPoint::from_bytes(bytes).and_then(|p| p.into_subgroup())
1710 }
1711
1712 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1713 EdwardsPoint::from_bytes_unchecked(bytes).and_then(|p| p.into_subgroup())
1714 }
1715
1716 fn to_bytes(&self) -> Self::Repr {
1717 self.0.compress().to_bytes()
1718 }
1719}
1720
1721#[cfg(feature = "group")]
1722impl PrimeGroup for SubgroupPoint {}
1723
1724#[cfg(feature = "group")]
1725impl CofactorGroup for EdwardsPoint {
1726 type Subgroup = SubgroupPoint;
1727
1728 fn clear_cofactor(&self) -> Self::Subgroup {
1729 SubgroupPoint(self.mul_by_cofactor())
1730 }
1731
1732 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1733 CtOption::new(SubgroupPoint(self), CofactorGroup::is_torsion_free(&self))
1734 }
1735
1736 fn is_torsion_free(&self) -> Choice {
1737 (self * constants::BASEPOINT_ORDER_PRIVATE).ct_eq(&Self::identity())
1738 }
1739}
1740
1741#[cfg(test)]
1746mod test {
1747 use super::*;
1748
1749 #[cfg(not(feature = "group"))]
1751 use rand_core::RngCore;
1752
1753 #[cfg(feature = "alloc")]
1754 use alloc::vec::Vec;
1755
1756 #[cfg(feature = "precomputed-tables")]
1757 use crate::constants::ED25519_BASEPOINT_TABLE;
1758
1759 static BASE_X_COORD_BYTES: [u8; 32] = [
1762 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95, 0x60, 0xc7, 0x2c,
1763 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0, 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36,
1764 0x69, 0x21,
1765 ];
1766
1767 static BASE2_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1769 0xc9, 0xa3, 0xf8, 0x6a, 0xae, 0x46, 0x5f, 0xe, 0x56, 0x51, 0x38, 0x64, 0x51, 0x0f, 0x39,
1770 0x97, 0x56, 0x1f, 0xa2, 0xc9, 0xe8, 0x5e, 0xa2, 0x1d, 0xc2, 0x29, 0x23, 0x09, 0xf3, 0xcd,
1771 0x60, 0x22,
1772 ]);
1773
1774 static BASE16_CMPRSSD: CompressedEdwardsY = CompressedEdwardsY([
1776 0xeb, 0x27, 0x67, 0xc1, 0x37, 0xab, 0x7a, 0xd8, 0x27, 0x9c, 0x07, 0x8e, 0xff, 0x11, 0x6a,
1777 0xb0, 0x78, 0x6e, 0xad, 0x3a, 0x2e, 0x0f, 0x98, 0x9f, 0x72, 0xc3, 0x7f, 0x82, 0xf2, 0x96,
1778 0x96, 0x70,
1779 ]);
1780
1781 pub static A_SCALAR: Scalar = Scalar {
1783 bytes: [
1784 0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d, 0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8,
1785 0x26, 0x4d, 0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1, 0x58, 0x9e, 0x7b, 0x7f,
1786 0x23, 0x76, 0xef, 0x09,
1787 ],
1788 };
1789
1790 pub static B_SCALAR: Scalar = Scalar {
1792 bytes: [
1793 0x91, 0x26, 0x7a, 0xcf, 0x25, 0xc2, 0x09, 0x1b, 0xa2, 0x17, 0x74, 0x7b, 0x66, 0xf0,
1794 0xb3, 0x2e, 0x9d, 0xf2, 0xa5, 0x67, 0x41, 0xcf, 0xda, 0xc4, 0x56, 0xa7, 0xd4, 0xaa,
1795 0xb8, 0x60, 0x8a, 0x05,
1796 ],
1797 };
1798
1799 pub static A_TIMES_BASEPOINT: CompressedEdwardsY = CompressedEdwardsY([
1801 0xea, 0x27, 0xe2, 0x60, 0x53, 0xdf, 0x1b, 0x59, 0x56, 0xf1, 0x4d, 0x5d, 0xec, 0x3c, 0x34,
1802 0xc3, 0x84, 0xa2, 0x69, 0xb7, 0x4c, 0xc3, 0x80, 0x3e, 0xa8, 0xe2, 0xe7, 0xc9, 0x42, 0x5e,
1803 0x40, 0xa5,
1804 ]);
1805
1806 static DOUBLE_SCALAR_MULT_RESULT: CompressedEdwardsY = CompressedEdwardsY([
1809 0x7d, 0xfd, 0x6c, 0x45, 0xaf, 0x6d, 0x6e, 0x0e, 0xba, 0x20, 0x37, 0x1a, 0x23, 0x64, 0x59,
1810 0xc4, 0xc0, 0x46, 0x83, 0x43, 0xde, 0x70, 0x4b, 0x85, 0x09, 0x6f, 0xfe, 0x35, 0x4f, 0x13,
1811 0x2b, 0x42,
1812 ]);
1813
1814 #[test]
1816 fn basepoint_decompression_compression() {
1817 let base_X = FieldElement::from_bytes(&BASE_X_COORD_BYTES);
1818 let bp = constants::ED25519_BASEPOINT_COMPRESSED
1819 .decompress()
1820 .unwrap();
1821 assert!(bp.is_valid());
1822 assert_eq!(base_X, bp.X);
1824 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1825 }
1826
1827 #[test]
1829 fn decompression_sign_handling() {
1830 let mut minus_basepoint_bytes = *constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
1832 minus_basepoint_bytes[31] |= 1 << 7;
1833 let minus_basepoint = CompressedEdwardsY(minus_basepoint_bytes)
1834 .decompress()
1835 .unwrap();
1836 assert_eq!(minus_basepoint.X, -(&constants::ED25519_BASEPOINT_POINT.X));
1839 assert_eq!(minus_basepoint.Y, constants::ED25519_BASEPOINT_POINT.Y);
1840 assert_eq!(minus_basepoint.Z, constants::ED25519_BASEPOINT_POINT.Z);
1841 assert_eq!(minus_basepoint.T, -(&constants::ED25519_BASEPOINT_POINT.T));
1842 }
1843
1844 #[cfg(feature = "precomputed-tables")]
1846 #[test]
1847 fn basepoint_mult_one_vs_basepoint() {
1848 let bp = ED25519_BASEPOINT_TABLE * &Scalar::ONE;
1849 let compressed = bp.compress();
1850 assert_eq!(compressed, constants::ED25519_BASEPOINT_COMPRESSED);
1851 }
1852
1853 #[cfg(feature = "precomputed-tables")]
1855 #[test]
1856 fn basepoint_table_basepoint_function_correct() {
1857 let bp = ED25519_BASEPOINT_TABLE.basepoint();
1858 assert_eq!(bp.compress(), constants::ED25519_BASEPOINT_COMPRESSED);
1859 }
1860
1861 #[test]
1864 fn basepoint_plus_basepoint_vs_basepoint2() {
1865 let bp = constants::ED25519_BASEPOINT_POINT;
1866 let bp_added = bp + bp;
1867 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1868 }
1869
1870 #[test]
1873 fn basepoint_plus_basepoint_projective_niels_vs_basepoint2() {
1874 let bp = constants::ED25519_BASEPOINT_POINT;
1875 let bp_added = (&bp + &bp.as_projective_niels()).as_extended();
1876 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1877 }
1878
1879 #[test]
1882 fn basepoint_plus_basepoint_affine_niels_vs_basepoint2() {
1883 let bp = constants::ED25519_BASEPOINT_POINT;
1884 let bp_affine_niels = bp.as_affine_niels();
1885 let bp_added = (&bp + &bp_affine_niels).as_extended();
1886 assert_eq!(bp_added.compress(), BASE2_CMPRSSD);
1887 }
1888
1889 #[test]
1892 fn extended_point_equality_handles_scaling() {
1893 let mut two_bytes = [0u8; 32];
1894 two_bytes[0] = 2;
1895 let id1 = EdwardsPoint::identity();
1896 let id2 = EdwardsPoint {
1897 X: FieldElement::ZERO,
1898 Y: FieldElement::from_bytes(&two_bytes),
1899 Z: FieldElement::from_bytes(&two_bytes),
1900 T: FieldElement::ZERO,
1901 };
1902 assert!(bool::from(id1.ct_eq(&id2)));
1903 }
1904
1905 #[cfg(feature = "precomputed-tables")]
1907 #[test]
1908 fn to_affine_niels_clears_denominators() {
1909 let aB = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1911 let aB_affine_niels = aB.as_affine_niels();
1912 let also_aB = (&EdwardsPoint::identity() + &aB_affine_niels).as_extended();
1913 assert_eq!(aB.compress(), also_aB.compress());
1914 }
1915
1916 #[test]
1918 fn basepoint_mult_vs_ed25519py() {
1919 let aB = EdwardsPoint::mul_base(&A_SCALAR);
1920 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1921 }
1922
1923 #[test]
1925 fn basepoint_mult_by_basepoint_order() {
1926 let should_be_id = EdwardsPoint::mul_base(&constants::BASEPOINT_ORDER_PRIVATE);
1927 assert!(should_be_id.is_identity());
1928 }
1929
1930 #[cfg(feature = "precomputed-tables")]
1932 #[test]
1933 fn test_precomputed_basepoint_mult() {
1934 let aB_1 = ED25519_BASEPOINT_TABLE * &A_SCALAR;
1935 let aB_2 = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1936 assert_eq!(aB_1.compress(), aB_2.compress());
1937 }
1938
1939 #[test]
1941 fn scalar_mul_vs_ed25519py() {
1942 let aB = constants::ED25519_BASEPOINT_POINT * A_SCALAR;
1943 assert_eq!(aB.compress(), A_TIMES_BASEPOINT);
1944 }
1945
1946 #[test]
1948 fn basepoint_double_vs_basepoint2() {
1949 assert_eq!(
1950 constants::ED25519_BASEPOINT_POINT.double().compress(),
1951 BASE2_CMPRSSD
1952 );
1953 }
1954
1955 #[test]
1957 fn basepoint_mult_two_vs_basepoint2() {
1958 let two = Scalar::from(2u64);
1959 let bp2 = EdwardsPoint::mul_base(&two);
1960 assert_eq!(bp2.compress(), BASE2_CMPRSSD);
1961 }
1962
1963 #[cfg(feature = "precomputed-tables")]
1965 #[test]
1966 fn basepoint_tables() {
1967 let P = &constants::ED25519_BASEPOINT_POINT;
1968 let a = A_SCALAR;
1969
1970 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1971 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
1972 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
1973 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
1974 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
1975
1976 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
1977 let aP16 = (&table_radix16 * &a).compress();
1978 let aP32 = (&table_radix32 * &a).compress();
1979 let aP64 = (&table_radix64 * &a).compress();
1980 let aP128 = (&table_radix128 * &a).compress();
1981 let aP256 = (&table_radix256 * &a).compress();
1982
1983 assert_eq!(aP, aP16);
1984 assert_eq!(aP16, aP32);
1985 assert_eq!(aP32, aP64);
1986 assert_eq!(aP64, aP128);
1987 assert_eq!(aP128, aP256);
1988 }
1989
1990 #[cfg(feature = "precomputed-tables")]
1993 #[test]
1994 fn basepoint_tables_unreduced_scalar() {
1995 let P = &constants::ED25519_BASEPOINT_POINT;
1996 let a = crate::scalar::test::LARGEST_UNREDUCED_SCALAR;
1997
1998 let table_radix16 = EdwardsBasepointTableRadix16::create(P);
1999 let table_radix32 = EdwardsBasepointTableRadix32::create(P);
2000 let table_radix64 = EdwardsBasepointTableRadix64::create(P);
2001 let table_radix128 = EdwardsBasepointTableRadix128::create(P);
2002 let table_radix256 = EdwardsBasepointTableRadix256::create(P);
2003
2004 let aP = (ED25519_BASEPOINT_TABLE * &a).compress();
2005 let aP16 = (&table_radix16 * &a).compress();
2006 let aP32 = (&table_radix32 * &a).compress();
2007 let aP64 = (&table_radix64 * &a).compress();
2008 let aP128 = (&table_radix128 * &a).compress();
2009 let aP256 = (&table_radix256 * &a).compress();
2010
2011 assert_eq!(aP, aP16);
2012 assert_eq!(aP16, aP32);
2013 assert_eq!(aP32, aP64);
2014 assert_eq!(aP64, aP128);
2015 assert_eq!(aP128, aP256);
2016 }
2017
2018 #[test]
2020 fn basepoint_projective_extended_round_trip() {
2021 assert_eq!(
2022 constants::ED25519_BASEPOINT_POINT
2023 .as_projective()
2024 .as_extended()
2025 .compress(),
2026 constants::ED25519_BASEPOINT_COMPRESSED
2027 );
2028 }
2029
2030 #[test]
2032 fn basepoint16_vs_mul_by_pow_2_4() {
2033 let bp16 = constants::ED25519_BASEPOINT_POINT.mul_by_pow_2(4);
2034 assert_eq!(bp16.compress(), BASE16_CMPRSSD);
2035 }
2036
2037 #[test]
2039 fn mul_base_clamped() {
2040 let mut csprng = rand_core::OsRng;
2041
2042 #[cfg(feature = "precomputed-tables")]
2044 let random_point = {
2045 let mut b = [0u8; 32];
2046 csprng.fill_bytes(&mut b);
2047 EdwardsPoint::mul_base_clamped(b) + constants::EIGHT_TORSION[1]
2048 };
2049 #[cfg(feature = "precomputed-tables")]
2051 let random_table = EdwardsBasepointTableRadix256::create(&random_point);
2052
2053 let a_bytes = [0xff; 32];
2058 assert_eq!(
2059 EdwardsPoint::mul_base_clamped(a_bytes),
2060 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2061 );
2062 #[cfg(feature = "precomputed-tables")]
2063 assert_eq!(
2064 random_table.mul_base_clamped(a_bytes),
2065 random_point.mul_clamped(a_bytes)
2066 );
2067
2068 for _ in 0..100 {
2070 let mut a_bytes = [0u8; 32];
2072 csprng.fill_bytes(&mut a_bytes);
2073
2074 assert_eq!(
2075 EdwardsPoint::mul_base_clamped(a_bytes),
2076 constants::ED25519_BASEPOINT_POINT.mul_clamped(a_bytes)
2077 );
2078 #[cfg(feature = "precomputed-tables")]
2079 assert_eq!(
2080 random_table.mul_base_clamped(a_bytes),
2081 random_point.mul_clamped(a_bytes)
2082 );
2083 }
2084 }
2085
2086 #[test]
2087 #[cfg(feature = "alloc")]
2088 fn impl_sum() {
2089 let BASE = constants::ED25519_BASEPOINT_POINT;
2091
2092 let s1 = Scalar::from(999u64);
2093 let P1 = BASE * s1;
2094
2095 let s2 = Scalar::from(333u64);
2096 let P2 = BASE * s2;
2097
2098 let vec = vec![P1, P2];
2099 let sum: EdwardsPoint = vec.iter().sum();
2100
2101 assert_eq!(sum, P1 + P2);
2102
2103 let empty_vector: Vec<EdwardsPoint> = vec![];
2105 let sum: EdwardsPoint = empty_vector.iter().sum();
2106
2107 assert_eq!(sum, EdwardsPoint::identity());
2108
2109 let s = Scalar::from(2u64);
2111 let mapped = vec.iter().map(|x| x * s);
2112 let sum: EdwardsPoint = mapped.sum();
2113
2114 assert_eq!(sum, P1 * s + P2 * s);
2115 }
2116
2117 #[test]
2119 fn conditional_assign_for_affine_niels_point() {
2120 let id = AffineNielsPoint::identity();
2121 let mut p1 = AffineNielsPoint::identity();
2122 let bp = constants::ED25519_BASEPOINT_POINT.as_affine_niels();
2123
2124 p1.conditional_assign(&bp, Choice::from(0));
2125 assert_eq!(p1, id);
2126 p1.conditional_assign(&bp, Choice::from(1));
2127 assert_eq!(p1, bp);
2128 }
2129
2130 #[test]
2131 fn is_small_order() {
2132 assert!(!constants::ED25519_BASEPOINT_POINT.is_small_order());
2134 for torsion_point in &constants::EIGHT_TORSION {
2136 assert!(torsion_point.is_small_order());
2137 }
2138 }
2139
2140 #[test]
2141 fn compressed_identity() {
2142 assert_eq!(
2143 EdwardsPoint::identity().compress(),
2144 CompressedEdwardsY::identity()
2145 );
2146
2147 #[cfg(feature = "alloc")]
2148 {
2149 let compressed = EdwardsPoint::compress_batch(&[EdwardsPoint::identity()]);
2150 assert_eq!(&compressed, &[CompressedEdwardsY::identity()]);
2151 }
2152 }
2153
2154 #[cfg(feature = "alloc")]
2155 #[test]
2156 fn compress_batch() {
2157 let mut rng = rand::thread_rng();
2158
2159 let mut points = (1u64..16)
2162 .map(|n| constants::ED25519_BASEPOINT_POINT * Scalar::from(n))
2163 .collect::<Vec<_>>();
2164 points.extend(core::iter::repeat_with(|| EdwardsPoint::random(&mut rng)).take(100));
2165 let compressed = EdwardsPoint::compress_batch(&points);
2166
2167 for (point, compressed) in points.iter().zip(&compressed) {
2169 assert_eq!(&point.compress(), compressed);
2170 }
2171 }
2172
2173 #[test]
2174 fn is_identity() {
2175 assert!(EdwardsPoint::identity().is_identity());
2176 assert!(!constants::ED25519_BASEPOINT_POINT.is_identity());
2177 }
2178
2179 #[test]
2190 fn monte_carlo_overflow_underflow_debug_assert_test() {
2191 let mut P = constants::ED25519_BASEPOINT_POINT;
2192 for _ in 0..1_000 {
2195 P *= &A_SCALAR;
2196 }
2197 }
2198
2199 #[test]
2200 fn scalarmult_extended_point_works_both_ways() {
2201 let G: EdwardsPoint = constants::ED25519_BASEPOINT_POINT;
2202 let s: Scalar = A_SCALAR;
2203
2204 let P1 = G * s;
2205 let P2 = s * G;
2206
2207 assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
2208 }
2209
2210 #[cfg(feature = "alloc")]
2212 fn multiscalar_consistency_iter(n: usize) {
2213 let mut rng = rand::thread_rng();
2214
2215 let xs = (0..n).map(|_| Scalar::random(&mut rng)).collect::<Vec<_>>();
2218 let check = xs.iter().map(|xi| xi * xi).sum::<Scalar>();
2219
2220 let Gs = xs.iter().map(EdwardsPoint::mul_base).collect::<Vec<_>>();
2222
2223 let H1 = EdwardsPoint::multiscalar_mul(&xs, &Gs);
2225 let H2 = EdwardsPoint::vartime_multiscalar_mul(&xs, &Gs);
2227 let H3 = EdwardsPoint::mul_base(&check);
2229
2230 assert_eq!(H1, H3);
2231 assert_eq!(H2, H3);
2232 }
2233
2234 #[test]
2238 #[cfg(feature = "alloc")]
2239 fn multiscalar_consistency_n_100() {
2240 let iters = 50;
2241 for _ in 0..iters {
2242 multiscalar_consistency_iter(100);
2243 }
2244 }
2245
2246 #[test]
2247 #[cfg(feature = "alloc")]
2248 fn multiscalar_consistency_n_250() {
2249 let iters = 50;
2250 for _ in 0..iters {
2251 multiscalar_consistency_iter(250);
2252 }
2253 }
2254
2255 #[test]
2256 #[cfg(feature = "alloc")]
2257 fn multiscalar_consistency_n_500() {
2258 let iters = 50;
2259 for _ in 0..iters {
2260 multiscalar_consistency_iter(500);
2261 }
2262 }
2263
2264 #[test]
2265 #[cfg(feature = "alloc")]
2266 fn multiscalar_consistency_n_1000() {
2267 let iters = 50;
2268 for _ in 0..iters {
2269 multiscalar_consistency_iter(1000);
2270 }
2271 }
2272
2273 #[test]
2274 #[cfg(feature = "alloc")]
2275 fn batch_to_montgomery() {
2276 let mut rng = rand::thread_rng();
2277
2278 let scalars = (0..128)
2279 .map(|_| Scalar::random(&mut rng))
2280 .collect::<Vec<_>>();
2281
2282 let points = scalars
2283 .iter()
2284 .map(EdwardsPoint::mul_base)
2285 .collect::<Vec<_>>();
2286
2287 let single_monts = points
2288 .iter()
2289 .map(EdwardsPoint::to_montgomery)
2290 .collect::<Vec<_>>();
2291
2292 for i in [0, 1, 2, 3, 10, 50, 128] {
2293 let invs = EdwardsPoint::to_montgomery_batch(&points[..i]);
2294 assert_eq!(&invs, &single_monts[..i]);
2295 }
2296 }
2297
2298 #[test]
2299 #[cfg(feature = "alloc")]
2300 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
2301 let mut rng = rand::thread_rng();
2302
2303 let static_scalars = (0..128)
2304 .map(|_| Scalar::random(&mut rng))
2305 .collect::<Vec<_>>();
2306
2307 let dynamic_scalars = (0..128)
2308 .map(|_| Scalar::random(&mut rng))
2309 .collect::<Vec<_>>();
2310
2311 let check_scalar: Scalar = static_scalars
2312 .iter()
2313 .chain(dynamic_scalars.iter())
2314 .map(|s| s * s)
2315 .sum();
2316
2317 let static_points = static_scalars
2318 .iter()
2319 .map(EdwardsPoint::mul_base)
2320 .collect::<Vec<_>>();
2321 let dynamic_points = dynamic_scalars
2322 .iter()
2323 .map(EdwardsPoint::mul_base)
2324 .collect::<Vec<_>>();
2325
2326 let precomputation = VartimeEdwardsPrecomputation::new(static_points.iter());
2327
2328 assert_eq!(precomputation.len(), 128);
2329 assert!(!precomputation.is_empty());
2330
2331 let P = precomputation.vartime_mixed_multiscalar_mul(
2332 &static_scalars,
2333 &dynamic_scalars,
2334 &dynamic_points,
2335 );
2336
2337 use crate::traits::VartimeMultiscalarMul;
2338 let Q = EdwardsPoint::vartime_multiscalar_mul(
2339 static_scalars.iter().chain(dynamic_scalars.iter()),
2340 static_points.iter().chain(dynamic_points.iter()),
2341 );
2342
2343 let R = EdwardsPoint::mul_base(&check_scalar);
2344
2345 assert_eq!(P.compress(), R.compress());
2346 assert_eq!(Q.compress(), R.compress());
2347 }
2348
2349 mod vartime {
2350 use super::super::*;
2351 use super::{A_SCALAR, A_TIMES_BASEPOINT, B_SCALAR, DOUBLE_SCALAR_MULT_RESULT};
2352
2353 #[test]
2355 fn double_scalar_mul_basepoint_vs_ed25519py() {
2356 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2357 let result =
2358 EdwardsPoint::vartime_double_scalar_mul_basepoint(&A_SCALAR, &A, &B_SCALAR);
2359 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2360 }
2361
2362 #[test]
2363 #[cfg(feature = "alloc")]
2364 fn multiscalar_mul_vs_ed25519py() {
2365 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2366 let result = EdwardsPoint::vartime_multiscalar_mul(
2367 &[A_SCALAR, B_SCALAR],
2368 &[A, constants::ED25519_BASEPOINT_POINT],
2369 );
2370 assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
2371 }
2372
2373 #[test]
2374 #[cfg(feature = "alloc")]
2375 fn multiscalar_mul_vartime_vs_consttime() {
2376 let A = A_TIMES_BASEPOINT.decompress().unwrap();
2377 let result_vartime = EdwardsPoint::vartime_multiscalar_mul(
2378 &[A_SCALAR, B_SCALAR],
2379 &[A, constants::ED25519_BASEPOINT_POINT],
2380 );
2381 let result_consttime = EdwardsPoint::multiscalar_mul(
2382 &[A_SCALAR, B_SCALAR],
2383 &[A, constants::ED25519_BASEPOINT_POINT],
2384 );
2385
2386 assert_eq!(result_vartime.compress(), result_consttime.compress());
2387 }
2388 }
2389
2390 #[test]
2391 #[cfg(feature = "serde")]
2392 fn serde_bincode_basepoint_roundtrip() {
2393 use bincode;
2394
2395 let encoded = bincode::serialize(&constants::ED25519_BASEPOINT_POINT).unwrap();
2396 let enc_compressed = bincode::serialize(&constants::ED25519_BASEPOINT_COMPRESSED).unwrap();
2397 assert_eq!(encoded, enc_compressed);
2398
2399 assert_eq!(encoded.len(), 32);
2401
2402 let dec_uncompressed: EdwardsPoint = bincode::deserialize(&encoded).unwrap();
2403 let dec_compressed: CompressedEdwardsY = bincode::deserialize(&encoded).unwrap();
2404
2405 assert_eq!(dec_uncompressed, constants::ED25519_BASEPOINT_POINT);
2406 assert_eq!(dec_compressed, constants::ED25519_BASEPOINT_COMPRESSED);
2407
2408 let raw_bytes = constants::ED25519_BASEPOINT_COMPRESSED.as_bytes();
2410 let bp: EdwardsPoint = bincode::deserialize(raw_bytes).unwrap();
2411 assert_eq!(bp, constants::ED25519_BASEPOINT_POINT);
2412 }
2413
2414 #[cfg(all(feature = "alloc", feature = "digest"))]
2420 fn signal_test_vectors() -> Vec<Vec<&'static str>> {
2421 vec![
2422 vec![
2423 "214f306e1576f5a7577636fe303ca2c625b533319f52442b22a9fa3b7ede809f",
2424 "c95becf0f93595174633b9d4d6bbbeb88e16fa257176f877ce426e1424626052",
2425 ],
2426 vec![
2427 "2eb10d432702ea7f79207da95d206f82d5a3b374f5f89f17a199531f78d3bea6",
2428 "d8f8b508edffbb8b6dab0f602f86a9dd759f800fe18f782fdcac47c234883e7f",
2429 ],
2430 vec![
2431 "84cbe9accdd32b46f4a8ef51c85fd39d028711f77fb00e204a613fc235fd68b9",
2432 "93c73e0289afd1d1fc9e4e78a505d5d1b2642fbdf91a1eff7d281930654b1453",
2433 ],
2434 vec![
2435 "c85165952490dc1839cb69012a3d9f2cc4b02343613263ab93a26dc89fd58267",
2436 "43cbe8685fd3c90665b91835debb89ff1477f906f5170f38a192f6a199556537",
2437 ],
2438 vec![
2439 "26e7fc4a78d863b1a4ccb2ce0951fbcd021e106350730ee4157bacb4502e1b76",
2440 "b6fc3d738c2c40719479b2f23818180cdafa72a14254d4016bbed8f0b788a835",
2441 ],
2442 vec![
2443 "1618c08ef0233f94f0f163f9435ec7457cd7a8cd4bb6b160315d15818c30f7a2",
2444 "da0b703593b29dbcd28ebd6e7baea17b6f61971f3641cae774f6a5137a12294c",
2445 ],
2446 vec![
2447 "48b73039db6fcdcb6030c4a38e8be80b6390d8ae46890e77e623f87254ef149c",
2448 "ca11b25acbc80566603eabeb9364ebd50e0306424c61049e1ce9385d9f349966",
2449 ],
2450 vec![
2451 "a744d582b3a34d14d311b7629da06d003045ae77cebceeb4e0e72734d63bd07d",
2452 "fad25a5ea15d4541258af8785acaf697a886c1b872c793790e60a6837b1adbc0",
2453 ],
2454 vec![
2455 "80a6ff33494c471c5eff7efb9febfbcf30a946fe6535b3451cda79f2154a7095",
2456 "57ac03913309b3f8cd3c3d4c49d878bb21f4d97dc74a1eaccbe5c601f7f06f47",
2457 ],
2458 vec![
2459 "f06fc939bc10551a0fd415aebf107ef0b9c4ee1ef9a164157bdd089127782617",
2460 "785b2a6a00a5579cc9da1ff997ce8339b6f9fb46c6f10cf7a12ff2986341a6e0",
2461 ],
2462 ]
2463 }
2464
2465 #[test]
2466 #[allow(deprecated)]
2467 #[cfg(all(feature = "alloc", feature = "digest"))]
2468 fn elligator_signal_test_vectors() {
2469 for vector in signal_test_vectors().iter() {
2470 let input = hex::decode(vector[0]).unwrap();
2471 let output = hex::decode(vector[1]).unwrap();
2472
2473 let point = EdwardsPoint::nonspec_map_to_curve::<sha2::Sha512>(&input);
2474 assert_eq!(point.compress().to_bytes(), output[..]);
2475 }
2476 }
2477
2478 #[cfg(all(feature = "alloc", feature = "digest"))]
2482 const RFC_HASH_TO_CURVE_KAT: &[(&[u8], &str, &str)] = &[
2483 (
2484 b"",
2485 "1ff2b70ecf862799e11b7ae744e3489aa058ce805dd323a936375a84695e76da",
2486 "222e314d04a4d5725e9f2aff9fb2a6b69ef375a1214eb19021ceab2d687f0f9b",
2487 ),
2488 (
2489 b"abc",
2490 "5f13cc69c891d86927eb37bd4afc6672360007c63f68a33ab423a3aa040fd2a8",
2491 "67732d50f9a26f73111dd1ed5dba225614e538599db58ba30aaea1f5c827fa42",
2492 ),
2493 (
2494 b"abcdef0123456789",
2495 "1dd2fefce934ecfd7aae6ec998de088d7dd03316aa1847198aecf699ba6613f1",
2496 "2f8a6c24dd1adde73909cada6a4a137577b0f179d336685c4a955a0a8e1a86fb",
2497 ),
2498 (
2499 b"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\
2500 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
2501 "35fbdc5143e8a97afd3096f2b843e07df72e15bfca2eaf6879bf97c5d3362f73",
2502 "2af6ff6ef5ebba128b0774f4296cb4c2279a074658b083b8dcca91f57a603450",
2503 ),
2504 (
2505 b"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2506 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2507 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2508 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2509 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\
2510 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
2511 "6e5e1f37e99345887fc12111575fc1c3e36df4b289b8759d23af14d774b66bff",
2512 "2c90c3d39eb18ff291d33441b35f3262cdd307162cc97c31bfcc7a4245891a37"
2513 )
2514 ];
2515
2516 #[test]
2517 #[cfg(all(feature = "alloc", feature = "digest"))]
2518 fn elligator_hash_to_curve_test_vectors() {
2519 let dst = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_NU_";
2520 for (index, vector) in RFC_HASH_TO_CURVE_KAT.iter().enumerate() {
2521 let input = vector.0;
2522
2523 let expected_output = {
2524 let mut x_bytes = hex::decode(vector.1).unwrap();
2525 x_bytes.reverse();
2526 let x = FieldElement::from_bytes(&x_bytes.try_into().unwrap());
2527
2528 let mut y_bytes = hex::decode(vector.2).unwrap();
2529 y_bytes.reverse();
2530 let y = FieldElement::from_bytes(&y_bytes.try_into().unwrap());
2531
2532 EdwardsPoint {
2533 X: x,
2534 Y: y,
2535 Z: FieldElement::ONE,
2536 T: &x * &y,
2537 }
2538 };
2539
2540 let computed = EdwardsPoint::hash_to_curve::<sha2::Sha512>(&[&input], &[dst]);
2541 assert_eq!(computed, expected_output, "Failed in test {}", index);
2542 }
2543 }
2544}