1#![allow(non_snake_case)]
17
18#[cfg(feature = "alloc")]
162use alloc::vec::Vec;
163
164use core::array::TryFromSliceError;
165use core::borrow::Borrow;
166use core::fmt::Debug;
167use core::iter::Sum;
168use core::ops::{Add, Neg, Sub};
169use core::ops::{AddAssign, SubAssign};
170use core::ops::{Mul, MulAssign};
171
172#[cfg(any(test, feature = "rand_core"))]
173use rand_core::CryptoRngCore;
174
175#[cfg(feature = "digest")]
176use digest::generic_array::typenum::U64;
177#[cfg(feature = "digest")]
178use digest::Digest;
179
180use crate::constants;
181use crate::field::FieldElement;
182
183#[cfg(feature = "group")]
184use {
185 group::{cofactor::CofactorGroup, prime::PrimeGroup, GroupEncoding},
186 rand_core::RngCore,
187 subtle::CtOption,
188};
189
190use subtle::Choice;
191use subtle::ConditionallyNegatable;
192use subtle::ConditionallySelectable;
193use subtle::ConstantTimeEq;
194
195#[cfg(feature = "zeroize")]
196use zeroize::Zeroize;
197
198#[cfg(feature = "precomputed-tables")]
199use crate::edwards::EdwardsBasepointTable;
200use crate::edwards::EdwardsPoint;
201
202use crate::scalar::Scalar;
203
204#[cfg(feature = "precomputed-tables")]
205use crate::traits::BasepointTable;
206use crate::traits::Identity;
207#[cfg(feature = "alloc")]
208use crate::traits::{MultiscalarMul, VartimeMultiscalarMul, VartimePrecomputedMultiscalarMul};
209
210#[derive(Copy, Clone, Eq, PartialEq, Hash)]
219pub struct CompressedRistretto(pub [u8; 32]);
220
221impl ConstantTimeEq for CompressedRistretto {
222 fn ct_eq(&self, other: &CompressedRistretto) -> Choice {
223 self.as_bytes().ct_eq(other.as_bytes())
224 }
225}
226
227impl CompressedRistretto {
228 pub const fn to_bytes(&self) -> [u8; 32] {
230 self.0
231 }
232
233 pub const fn as_bytes(&self) -> &[u8; 32] {
235 &self.0
236 }
237
238 pub fn from_slice(bytes: &[u8]) -> Result<CompressedRistretto, TryFromSliceError> {
245 bytes.try_into().map(CompressedRistretto)
246 }
247
248 pub fn decompress(&self) -> Option<RistrettoPoint> {
256 let (s_encoding_is_canonical, s_is_negative, s) = decompress::step_1(self);
257
258 if (!s_encoding_is_canonical | s_is_negative).into() {
259 return None;
260 }
261
262 let (ok, t_is_negative, y_is_zero, res) = decompress::step_2(s);
263
264 if (!ok | t_is_negative | y_is_zero).into() {
265 None
266 } else {
267 Some(res)
268 }
269 }
270}
271
272mod decompress {
273 use super::*;
274
275 pub(super) fn step_1(repr: &CompressedRistretto) -> (Choice, Choice, FieldElement) {
276 let s = FieldElement::from_bytes(repr.as_bytes());
288 let s_bytes_check = s.as_bytes();
289 let s_encoding_is_canonical = s_bytes_check[..].ct_eq(repr.as_bytes());
290 let s_is_negative = s.is_negative();
291
292 (s_encoding_is_canonical, s_is_negative, s)
293 }
294
295 pub(super) fn step_2(s: FieldElement) -> (Choice, Choice, Choice, RistrettoPoint) {
296 let one = FieldElement::ONE;
298 let ss = s.square();
299 let u1 = &one - &ss; let u2 = &one + &ss; let u2_sqr = u2.square(); let v = &(&(-&constants::EDWARDS_D) * &u1.square()) - &u2_sqr;
305
306 let (ok, I) = (&v * &u2_sqr).invsqrt(); let Dx = &I * &u2; let Dy = &I * &(&Dx * &v); let mut x = &(&s + &s) * &Dx;
313 let x_neg = x.is_negative();
314 x.conditional_negate(x_neg);
315
316 let y = &u1 * &Dy;
318
319 let t = &x * &y;
321
322 (
323 ok,
324 t.is_negative(),
325 y.is_zero(),
326 RistrettoPoint(EdwardsPoint {
327 X: x,
328 Y: y,
329 Z: one,
330 T: t,
331 }),
332 )
333 }
334}
335
336impl Identity for CompressedRistretto {
337 fn identity() -> CompressedRistretto {
338 CompressedRistretto([0u8; 32])
339 }
340}
341
342impl Default for CompressedRistretto {
343 fn default() -> CompressedRistretto {
344 CompressedRistretto::identity()
345 }
346}
347
348impl TryFrom<&[u8]> for CompressedRistretto {
349 type Error = TryFromSliceError;
350
351 fn try_from(slice: &[u8]) -> Result<CompressedRistretto, TryFromSliceError> {
352 Self::from_slice(slice)
353 }
354}
355
356#[cfg(feature = "serde")]
365use serde::de::Visitor;
366#[cfg(feature = "serde")]
367use serde::{Deserialize, Deserializer, Serialize, Serializer};
368
369#[cfg(feature = "serde")]
370impl Serialize for RistrettoPoint {
371 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
372 where
373 S: Serializer,
374 {
375 use serde::ser::SerializeTuple;
376 let mut tup = serializer.serialize_tuple(32)?;
377 for byte in self.compress().as_bytes().iter() {
378 tup.serialize_element(byte)?;
379 }
380 tup.end()
381 }
382}
383
384#[cfg(feature = "serde")]
385impl Serialize for CompressedRistretto {
386 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
387 where
388 S: Serializer,
389 {
390 use serde::ser::SerializeTuple;
391 let mut tup = serializer.serialize_tuple(32)?;
392 for byte in self.as_bytes().iter() {
393 tup.serialize_element(byte)?;
394 }
395 tup.end()
396 }
397}
398
399#[cfg(feature = "serde")]
400impl<'de> Deserialize<'de> for RistrettoPoint {
401 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
402 where
403 D: Deserializer<'de>,
404 {
405 struct RistrettoPointVisitor;
406
407 impl<'de> Visitor<'de> for RistrettoPointVisitor {
408 type Value = RistrettoPoint;
409
410 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
411 formatter.write_str("a valid point in Ristretto format")
412 }
413
414 fn visit_seq<A>(self, mut seq: A) -> Result<RistrettoPoint, A::Error>
415 where
416 A: serde::de::SeqAccess<'de>,
417 {
418 let mut bytes = [0u8; 32];
419 #[allow(clippy::needless_range_loop)]
420 for i in 0..32 {
421 bytes[i] = seq
422 .next_element()?
423 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
424 }
425 CompressedRistretto(bytes)
426 .decompress()
427 .ok_or_else(|| serde::de::Error::custom("decompression failed"))
428 }
429 }
430
431 deserializer.deserialize_tuple(32, RistrettoPointVisitor)
432 }
433}
434
435#[cfg(feature = "serde")]
436impl<'de> Deserialize<'de> for CompressedRistretto {
437 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
438 where
439 D: Deserializer<'de>,
440 {
441 struct CompressedRistrettoVisitor;
442
443 impl<'de> Visitor<'de> for CompressedRistrettoVisitor {
444 type Value = CompressedRistretto;
445
446 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
447 formatter.write_str("32 bytes of data")
448 }
449
450 fn visit_seq<A>(self, mut seq: A) -> Result<CompressedRistretto, A::Error>
451 where
452 A: serde::de::SeqAccess<'de>,
453 {
454 let mut bytes = [0u8; 32];
455 #[allow(clippy::needless_range_loop)]
456 for i in 0..32 {
457 bytes[i] = seq
458 .next_element()?
459 .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
460 }
461 Ok(CompressedRistretto(bytes))
462 }
463 }
464
465 deserializer.deserialize_tuple(32, CompressedRistrettoVisitor)
466 }
467}
468
469#[derive(Copy, Clone)]
485pub struct RistrettoPoint(pub(crate) EdwardsPoint);
486
487impl RistrettoPoint {
488 pub fn compress(&self) -> CompressedRistretto {
490 let mut X = self.0.X;
491 let mut Y = self.0.Y;
492 let Z = &self.0.Z;
493 let T = &self.0.T;
494
495 let u1 = &(Z + &Y) * &(Z - &Y);
496 let u2 = &X * &Y;
497 let (_, invsqrt) = (&u1 * &u2.square()).invsqrt();
499 let i1 = &invsqrt * &u1;
500 let i2 = &invsqrt * &u2;
501 let z_inv = &i1 * &(&i2 * T);
502 let mut den_inv = i2;
503
504 let iX = &X * &constants::SQRT_M1;
505 let iY = &Y * &constants::SQRT_M1;
506 let ristretto_magic = &constants::INVSQRT_A_MINUS_D;
507 let enchanted_denominator = &i1 * ristretto_magic;
508
509 let rotate = (T * &z_inv).is_negative();
510
511 X.conditional_assign(&iY, rotate);
512 Y.conditional_assign(&iX, rotate);
513 den_inv.conditional_assign(&enchanted_denominator, rotate);
514
515 Y.conditional_negate((&X * &z_inv).is_negative());
516
517 let mut s = &den_inv * &(Z - &Y);
518 let s_is_negative = s.is_negative();
519 s.conditional_negate(s_is_negative);
520
521 CompressedRistretto(s.as_bytes())
522 }
523
524 #[cfg_attr(feature = "rand_core", doc = "```")]
533 #[cfg_attr(not(feature = "rand_core"), doc = "```ignore")]
534 #[cfg(feature = "alloc")]
553 pub fn double_and_compress_batch<'a, I>(points: I) -> Vec<CompressedRistretto>
554 where
555 I: IntoIterator<Item = &'a RistrettoPoint>,
556 {
557 #[derive(Copy, Clone, Debug)]
558 struct BatchCompressState {
559 e: FieldElement,
560 f: FieldElement,
561 g: FieldElement,
562 h: FieldElement,
563 eg: FieldElement,
564 fh: FieldElement,
565 }
566
567 impl BatchCompressState {
568 fn efgh(&self) -> FieldElement {
569 &self.eg * &self.fh
570 }
571 }
572
573 impl<'a> From<&'a RistrettoPoint> for BatchCompressState {
574 #[rustfmt::skip] fn from(P: &'a RistrettoPoint) -> BatchCompressState {
576 let XX = P.0.X.square();
577 let YY = P.0.Y.square();
578 let ZZ = P.0.Z.square();
579 let dTT = &P.0.T.square() * &constants::EDWARDS_D;
580
581 let e = &P.0.X * &(&P.0.Y + &P.0.Y); let f = &ZZ + &dTT; let g = &YY + &XX; let h = &ZZ - &dTT; let eg = &e * &g;
587 let fh = &f * &h;
588
589 BatchCompressState{ e, f, g, h, eg, fh }
590 }
591 }
592
593 let states: Vec<BatchCompressState> =
594 points.into_iter().map(BatchCompressState::from).collect();
595
596 let mut invs: Vec<FieldElement> = states.iter().map(|state| state.efgh()).collect();
597
598 FieldElement::batch_invert(&mut invs[..]);
599
600 states
601 .iter()
602 .zip(invs.iter())
603 .map(|(state, inv): (&BatchCompressState, &FieldElement)| {
604 let Zinv = &state.eg * inv;
605 let Tinv = &state.fh * inv;
606
607 let mut magic = constants::INVSQRT_A_MINUS_D;
608
609 let negcheck1 = (&state.eg * &Zinv).is_negative();
610
611 let mut e = state.e;
612 let mut g = state.g;
613 let mut h = state.h;
614
615 let minus_e = -&e;
616 let f_times_sqrta = &state.f * &constants::SQRT_M1;
617
618 e.conditional_assign(&state.g, negcheck1);
619 g.conditional_assign(&minus_e, negcheck1);
620 h.conditional_assign(&f_times_sqrta, negcheck1);
621
622 magic.conditional_assign(&constants::SQRT_M1, negcheck1);
623
624 let negcheck2 = (&(&h * &e) * &Zinv).is_negative();
625
626 g.conditional_negate(negcheck2);
627
628 let mut s = &(&h - &g) * &(&magic * &(&g * &Tinv));
629
630 let s_is_negative = s.is_negative();
631 s.conditional_negate(s_is_negative);
632
633 CompressedRistretto(s.as_bytes())
634 })
635 .collect()
636 }
637
638 fn coset4(&self) -> [EdwardsPoint; 4] {
640 [
641 self.0,
642 self.0 + constants::EIGHT_TORSION[2],
643 self.0 + constants::EIGHT_TORSION[4],
644 self.0 + constants::EIGHT_TORSION[6],
645 ]
646 }
647
648 pub(crate) fn elligator_ristretto_flavor(r_0: &FieldElement) -> RistrettoPoint {
657 let i = &constants::SQRT_M1;
658 let d = &constants::EDWARDS_D;
659 let one_minus_d_sq = &constants::ONE_MINUS_EDWARDS_D_SQUARED;
660 let d_minus_one_sq = &constants::EDWARDS_D_MINUS_ONE_SQUARED;
661 let mut c = constants::MINUS_ONE;
662
663 let one = FieldElement::ONE;
664
665 let r = i * &r_0.square();
666 let N_s = &(&r + &one) * one_minus_d_sq;
667 let D = &(&c - &(d * &r)) * &(&r + d);
668
669 let (Ns_D_is_sq, mut s) = FieldElement::sqrt_ratio_i(&N_s, &D);
670 let mut s_prime = &s * r_0;
671 let s_prime_is_pos = !s_prime.is_negative();
672 s_prime.conditional_negate(s_prime_is_pos);
673
674 s.conditional_assign(&s_prime, !Ns_D_is_sq);
675 c.conditional_assign(&r, !Ns_D_is_sq);
676
677 let N_t = &(&(&c * &(&r - &one)) * d_minus_one_sq) - &D;
678 let s_sq = s.square();
679
680 use crate::backend::serial::curve_models::CompletedPoint;
681
682 RistrettoPoint(
684 CompletedPoint {
685 X: &(&s + &s) * &D,
686 Z: &N_t * &constants::SQRT_AD_MINUS_ONE,
687 Y: &FieldElement::ONE - &s_sq,
688 T: &FieldElement::ONE + &s_sq,
689 }
690 .as_extended(),
691 )
692 }
693
694 #[cfg(any(test, feature = "rand_core"))]
695 pub fn random<R: CryptoRngCore + ?Sized>(rng: &mut R) -> Self {
713 let mut uniform_bytes = [0u8; 64];
714 rng.fill_bytes(&mut uniform_bytes);
715
716 RistrettoPoint::from_uniform_bytes(&uniform_bytes)
717 }
718
719 #[cfg(feature = "digest")]
720 #[cfg_attr(feature = "digest", doc = "```")]
737 #[cfg_attr(not(feature = "digest"), doc = "```ignore")]
738 pub fn hash_from_bytes<D>(input: &[u8]) -> RistrettoPoint
750 where
751 D: Digest<OutputSize = U64> + Default,
752 {
753 let mut hash = D::default();
754 hash.update(input);
755 RistrettoPoint::from_hash(hash)
756 }
757
758 #[cfg(feature = "digest")]
759 pub fn from_hash<D>(hash: D) -> RistrettoPoint
765 where
766 D: Digest<OutputSize = U64> + Default,
767 {
768 let output = hash.finalize();
770 let mut output_bytes = [0u8; 64];
771 output_bytes.copy_from_slice(output.as_slice());
772
773 RistrettoPoint::from_uniform_bytes(&output_bytes)
774 }
775
776 pub fn from_uniform_bytes(bytes: &[u8; 64]) -> RistrettoPoint {
788 let mut r_1_bytes = [0u8; 32];
791 r_1_bytes.copy_from_slice(&bytes[0..32]);
792 let r_1 = FieldElement::from_bytes(&r_1_bytes);
793 let R_1 = RistrettoPoint::elligator_ristretto_flavor(&r_1);
794
795 let mut r_2_bytes = [0u8; 32];
796 r_2_bytes.copy_from_slice(&bytes[32..64]);
797 let r_2 = FieldElement::from_bytes(&r_2_bytes);
798 let R_2 = RistrettoPoint::elligator_ristretto_flavor(&r_2);
799
800 R_1 + R_2
803 }
804}
805
806impl Identity for RistrettoPoint {
807 fn identity() -> RistrettoPoint {
808 RistrettoPoint(EdwardsPoint::identity())
809 }
810}
811
812impl Default for RistrettoPoint {
813 fn default() -> RistrettoPoint {
814 RistrettoPoint::identity()
815 }
816}
817
818impl PartialEq for RistrettoPoint {
823 fn eq(&self, other: &RistrettoPoint) -> bool {
824 self.ct_eq(other).into()
825 }
826}
827
828impl ConstantTimeEq for RistrettoPoint {
829 fn ct_eq(&self, other: &RistrettoPoint) -> Choice {
836 let X1Y2 = &self.0.X * &other.0.Y;
837 let Y1X2 = &self.0.Y * &other.0.X;
838 let X1X2 = &self.0.X * &other.0.X;
839 let Y1Y2 = &self.0.Y * &other.0.Y;
840
841 X1Y2.ct_eq(&Y1X2) | X1X2.ct_eq(&Y1Y2)
842 }
843}
844
845impl Eq for RistrettoPoint {}
846
847impl<'a, 'b> Add<&'b RistrettoPoint> for &'a RistrettoPoint {
852 type Output = RistrettoPoint;
853
854 fn add(self, other: &'b RistrettoPoint) -> RistrettoPoint {
855 RistrettoPoint(self.0 + other.0)
856 }
857}
858
859define_add_variants!(
860 LHS = RistrettoPoint,
861 RHS = RistrettoPoint,
862 Output = RistrettoPoint
863);
864
865impl<'b> AddAssign<&'b RistrettoPoint> for RistrettoPoint {
866 fn add_assign(&mut self, _rhs: &RistrettoPoint) {
867 *self = (self as &RistrettoPoint) + _rhs;
868 }
869}
870
871define_add_assign_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint);
872
873impl<'a, 'b> Sub<&'b RistrettoPoint> for &'a RistrettoPoint {
874 type Output = RistrettoPoint;
875
876 fn sub(self, other: &'b RistrettoPoint) -> RistrettoPoint {
877 RistrettoPoint(self.0 - other.0)
878 }
879}
880
881define_sub_variants!(
882 LHS = RistrettoPoint,
883 RHS = RistrettoPoint,
884 Output = RistrettoPoint
885);
886
887impl<'b> SubAssign<&'b RistrettoPoint> for RistrettoPoint {
888 fn sub_assign(&mut self, _rhs: &RistrettoPoint) {
889 *self = (self as &RistrettoPoint) - _rhs;
890 }
891}
892
893define_sub_assign_variants!(LHS = RistrettoPoint, RHS = RistrettoPoint);
894
895impl<T> Sum<T> for RistrettoPoint
896where
897 T: Borrow<RistrettoPoint>,
898{
899 fn sum<I>(iter: I) -> Self
900 where
901 I: Iterator<Item = T>,
902 {
903 iter.fold(RistrettoPoint::identity(), |acc, item| acc + item.borrow())
904 }
905}
906
907impl<'a> Neg for &'a RistrettoPoint {
908 type Output = RistrettoPoint;
909
910 fn neg(self) -> RistrettoPoint {
911 RistrettoPoint(-&self.0)
912 }
913}
914
915impl Neg for RistrettoPoint {
916 type Output = RistrettoPoint;
917
918 fn neg(self) -> RistrettoPoint {
919 -&self
920 }
921}
922
923impl<'b> MulAssign<&'b Scalar> for RistrettoPoint {
924 fn mul_assign(&mut self, scalar: &'b Scalar) {
925 let result = (self as &RistrettoPoint) * scalar;
926 *self = result;
927 }
928}
929
930impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoPoint {
931 type Output = RistrettoPoint;
932 fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
934 RistrettoPoint(self.0 * scalar)
935 }
936}
937
938impl<'a, 'b> Mul<&'b RistrettoPoint> for &'a Scalar {
939 type Output = RistrettoPoint;
940
941 fn mul(self, point: &'b RistrettoPoint) -> RistrettoPoint {
943 RistrettoPoint(self * point.0)
944 }
945}
946
947impl RistrettoPoint {
948 pub fn mul_base(scalar: &Scalar) -> Self {
953 #[cfg(not(feature = "precomputed-tables"))]
954 {
955 scalar * constants::RISTRETTO_BASEPOINT_POINT
956 }
957
958 #[cfg(feature = "precomputed-tables")]
959 {
960 scalar * constants::RISTRETTO_BASEPOINT_TABLE
961 }
962 }
963}
964
965define_mul_assign_variants!(LHS = RistrettoPoint, RHS = Scalar);
966
967define_mul_variants!(LHS = RistrettoPoint, RHS = Scalar, Output = RistrettoPoint);
968define_mul_variants!(LHS = Scalar, RHS = RistrettoPoint, Output = RistrettoPoint);
969
970#[cfg(feature = "alloc")]
978impl MultiscalarMul for RistrettoPoint {
979 type Point = RistrettoPoint;
980
981 fn multiscalar_mul<I, J>(scalars: I, points: J) -> RistrettoPoint
982 where
983 I: IntoIterator,
984 I::Item: Borrow<Scalar>,
985 J: IntoIterator,
986 J::Item: Borrow<RistrettoPoint>,
987 {
988 let extended_points = points.into_iter().map(|P| P.borrow().0);
989 RistrettoPoint(EdwardsPoint::multiscalar_mul(scalars, extended_points))
990 }
991}
992
993#[cfg(feature = "alloc")]
994impl VartimeMultiscalarMul for RistrettoPoint {
995 type Point = RistrettoPoint;
996
997 fn optional_multiscalar_mul<I, J>(scalars: I, points: J) -> Option<RistrettoPoint>
998 where
999 I: IntoIterator,
1000 I::Item: Borrow<Scalar>,
1001 J: IntoIterator<Item = Option<RistrettoPoint>>,
1002 {
1003 let extended_points = points.into_iter().map(|opt_P| opt_P.map(|P| P.0));
1004
1005 EdwardsPoint::optional_multiscalar_mul(scalars, extended_points).map(RistrettoPoint)
1006 }
1007}
1008
1009#[cfg(feature = "alloc")]
1014pub struct VartimeRistrettoPrecomputation(crate::backend::VartimePrecomputedStraus);
1015
1016#[cfg(feature = "alloc")]
1017impl VartimePrecomputedMultiscalarMul for VartimeRistrettoPrecomputation {
1018 type Point = RistrettoPoint;
1019
1020 fn new<I>(static_points: I) -> Self
1021 where
1022 I: IntoIterator,
1023 I::Item: Borrow<Self::Point>,
1024 {
1025 Self(crate::backend::VartimePrecomputedStraus::new(
1026 static_points.into_iter().map(|P| P.borrow().0),
1027 ))
1028 }
1029
1030 fn optional_mixed_multiscalar_mul<I, J, K>(
1031 &self,
1032 static_scalars: I,
1033 dynamic_scalars: J,
1034 dynamic_points: K,
1035 ) -> Option<Self::Point>
1036 where
1037 I: IntoIterator,
1038 I::Item: Borrow<Scalar>,
1039 J: IntoIterator,
1040 J::Item: Borrow<Scalar>,
1041 K: IntoIterator<Item = Option<Self::Point>>,
1042 {
1043 self.0
1044 .optional_mixed_multiscalar_mul(
1045 static_scalars,
1046 dynamic_scalars,
1047 dynamic_points.into_iter().map(|P_opt| P_opt.map(|P| P.0)),
1048 )
1049 .map(RistrettoPoint)
1050 }
1051}
1052
1053impl RistrettoPoint {
1054 pub fn vartime_double_scalar_mul_basepoint(
1057 a: &Scalar,
1058 A: &RistrettoPoint,
1059 b: &Scalar,
1060 ) -> RistrettoPoint {
1061 RistrettoPoint(EdwardsPoint::vartime_double_scalar_mul_basepoint(
1062 a, &A.0, b,
1063 ))
1064 }
1065}
1066
1067#[cfg(feature = "precomputed-tables")]
1080#[derive(Clone)]
1081#[repr(transparent)]
1082pub struct RistrettoBasepointTable(pub(crate) EdwardsBasepointTable);
1083
1084#[cfg(feature = "precomputed-tables")]
1085impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable {
1086 type Output = RistrettoPoint;
1087
1088 fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
1089 RistrettoPoint(&self.0 * scalar)
1090 }
1091}
1092
1093#[cfg(feature = "precomputed-tables")]
1094impl<'a, 'b> Mul<&'a RistrettoBasepointTable> for &'b Scalar {
1095 type Output = RistrettoPoint;
1096
1097 fn mul(self, basepoint_table: &'a RistrettoBasepointTable) -> RistrettoPoint {
1098 RistrettoPoint(self * &basepoint_table.0)
1099 }
1100}
1101
1102#[cfg(feature = "precomputed-tables")]
1103impl RistrettoBasepointTable {
1104 pub fn create(basepoint: &RistrettoPoint) -> RistrettoBasepointTable {
1106 RistrettoBasepointTable(EdwardsBasepointTable::create(&basepoint.0))
1107 }
1108
1109 pub fn basepoint(&self) -> RistrettoPoint {
1111 RistrettoPoint(self.0.basepoint())
1112 }
1113}
1114
1115impl ConditionallySelectable for RistrettoPoint {
1120 fn conditional_select(
1145 a: &RistrettoPoint,
1146 b: &RistrettoPoint,
1147 choice: Choice,
1148 ) -> RistrettoPoint {
1149 RistrettoPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice))
1150 }
1151}
1152
1153impl Debug for CompressedRistretto {
1158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1159 write!(f, "CompressedRistretto: {:?}", self.as_bytes())
1160 }
1161}
1162
1163impl Debug for RistrettoPoint {
1164 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1165 let coset = self.coset4();
1166 write!(
1167 f,
1168 "RistrettoPoint: coset \n{:?}\n{:?}\n{:?}\n{:?}",
1169 coset[0], coset[1], coset[2], coset[3]
1170 )
1171 }
1172}
1173
1174#[cfg(feature = "group")]
1181impl group::Group for RistrettoPoint {
1182 type Scalar = Scalar;
1183
1184 fn random(mut rng: impl RngCore) -> Self {
1185 let mut uniform_bytes = [0u8; 64];
1187 rng.fill_bytes(&mut uniform_bytes);
1188 RistrettoPoint::from_uniform_bytes(&uniform_bytes)
1189 }
1190
1191 fn identity() -> Self {
1192 Identity::identity()
1193 }
1194
1195 fn generator() -> Self {
1196 constants::RISTRETTO_BASEPOINT_POINT
1197 }
1198
1199 fn is_identity(&self) -> Choice {
1200 self.ct_eq(&Identity::identity())
1201 }
1202
1203 fn double(&self) -> Self {
1204 self + self
1205 }
1206}
1207
1208#[cfg(feature = "group")]
1209impl GroupEncoding for RistrettoPoint {
1210 type Repr = [u8; 32];
1211
1212 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1213 let (s_encoding_is_canonical, s_is_negative, s) =
1214 decompress::step_1(&CompressedRistretto(*bytes));
1215
1216 let s_is_valid = s_encoding_is_canonical & !s_is_negative;
1217
1218 let (ok, t_is_negative, y_is_zero, res) = decompress::step_2(s);
1219
1220 CtOption::new(res, s_is_valid & ok & !t_is_negative & !y_is_zero)
1221 }
1222
1223 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1224 Self::from_bytes(bytes)
1226 }
1227
1228 fn to_bytes(&self) -> Self::Repr {
1229 self.compress().to_bytes()
1230 }
1231}
1232
1233#[cfg(feature = "group")]
1234impl PrimeGroup for RistrettoPoint {}
1235
1236#[cfg(feature = "group")]
1238impl CofactorGroup for RistrettoPoint {
1239 type Subgroup = Self;
1240
1241 fn clear_cofactor(&self) -> Self::Subgroup {
1242 *self
1243 }
1244
1245 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1246 CtOption::new(self, Choice::from(1))
1247 }
1248
1249 fn is_torsion_free(&self) -> Choice {
1250 Choice::from(1)
1251 }
1252}
1253
1254#[cfg(feature = "zeroize")]
1259impl Zeroize for CompressedRistretto {
1260 fn zeroize(&mut self) {
1261 self.0.zeroize();
1262 }
1263}
1264
1265#[cfg(feature = "zeroize")]
1266impl Zeroize for RistrettoPoint {
1267 fn zeroize(&mut self) {
1268 self.0.zeroize();
1269 }
1270}
1271
1272#[cfg(test)]
1277mod test {
1278 use super::*;
1279 use crate::edwards::CompressedEdwardsY;
1280
1281 use rand_core::OsRng;
1282
1283 #[test]
1284 #[cfg(feature = "serde")]
1285 fn serde_bincode_basepoint_roundtrip() {
1286 use bincode;
1287
1288 let encoded = bincode::serialize(&constants::RISTRETTO_BASEPOINT_POINT).unwrap();
1289 let enc_compressed =
1290 bincode::serialize(&constants::RISTRETTO_BASEPOINT_COMPRESSED).unwrap();
1291 assert_eq!(encoded, enc_compressed);
1292
1293 assert_eq!(encoded.len(), 32);
1295
1296 let dec_uncompressed: RistrettoPoint = bincode::deserialize(&encoded).unwrap();
1297 let dec_compressed: CompressedRistretto = bincode::deserialize(&encoded).unwrap();
1298
1299 assert_eq!(dec_uncompressed, constants::RISTRETTO_BASEPOINT_POINT);
1300 assert_eq!(dec_compressed, constants::RISTRETTO_BASEPOINT_COMPRESSED);
1301
1302 let raw_bytes = constants::RISTRETTO_BASEPOINT_COMPRESSED.as_bytes();
1304 let bp: RistrettoPoint = bincode::deserialize(raw_bytes).unwrap();
1305 assert_eq!(bp, constants::RISTRETTO_BASEPOINT_POINT);
1306 }
1307
1308 #[test]
1309 fn scalarmult_ristrettopoint_works_both_ways() {
1310 let P = constants::RISTRETTO_BASEPOINT_POINT;
1311 let s = Scalar::from(999u64);
1312
1313 let P1 = P * s;
1314 let P2 = s * P;
1315
1316 assert!(P1.compress().as_bytes() == P2.compress().as_bytes());
1317 }
1318
1319 #[test]
1320 #[cfg(feature = "alloc")]
1321 fn impl_sum() {
1322 let BASE = constants::RISTRETTO_BASEPOINT_POINT;
1324
1325 let s1 = Scalar::from(999u64);
1326 let P1 = BASE * s1;
1327
1328 let s2 = Scalar::from(333u64);
1329 let P2 = BASE * s2;
1330
1331 let vec = vec![P1, P2];
1332 let sum: RistrettoPoint = vec.iter().sum();
1333
1334 assert_eq!(sum, P1 + P2);
1335
1336 let empty_vector: Vec<RistrettoPoint> = vec![];
1338 let sum: RistrettoPoint = empty_vector.iter().sum();
1339
1340 assert_eq!(sum, RistrettoPoint::identity());
1341
1342 let s = Scalar::from(2u64);
1344 let mapped = vec.iter().map(|x| x * s);
1345 let sum: RistrettoPoint = mapped.sum();
1346
1347 assert_eq!(sum, P1 * s + P2 * s);
1348 }
1349
1350 #[test]
1351 fn decompress_negative_s_fails() {
1352 let bad_compressed = CompressedRistretto(constants::EDWARDS_D.as_bytes());
1354 assert!(bad_compressed.decompress().is_none());
1355 }
1356
1357 #[test]
1358 fn decompress_id() {
1359 let compressed_id = CompressedRistretto::identity();
1360 let id = compressed_id.decompress().unwrap();
1361 let mut identity_in_coset = false;
1362 for P in &id.coset4() {
1363 if P.compress() == CompressedEdwardsY::identity() {
1364 identity_in_coset = true;
1365 }
1366 }
1367 assert!(identity_in_coset);
1368 }
1369
1370 #[test]
1371 fn compress_id() {
1372 let id = RistrettoPoint::identity();
1373 assert_eq!(id.compress(), CompressedRistretto::identity());
1374 }
1375
1376 #[test]
1377 fn basepoint_roundtrip() {
1378 let bp_compressed_ristretto = constants::RISTRETTO_BASEPOINT_POINT.compress();
1379 let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0;
1380 let diff = constants::RISTRETTO_BASEPOINT_POINT.0 - bp_recaf;
1382 let diff4 = diff.mul_by_pow_2(2);
1383 assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
1384 }
1385
1386 #[test]
1387 fn encodings_of_small_multiples_of_basepoint() {
1388 let compressed = [
1391 CompressedRistretto([
1392 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,
1393 0, 0, 0, 0,
1394 ]),
1395 CompressedRistretto([
1396 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227,
1397 11, 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118,
1398 ]),
1399 CompressedRistretto([
1400 106, 73, 50, 16, 247, 73, 156, 209, 127, 236, 181, 16, 174, 12, 234, 35, 161, 16,
1401 232, 213, 185, 1, 248, 172, 173, 211, 9, 92, 115, 163, 185, 25,
1402 ]),
1403 CompressedRistretto([
1404 148, 116, 31, 93, 93, 82, 117, 94, 206, 79, 35, 240, 68, 238, 39, 213, 209, 234,
1405 30, 43, 209, 150, 180, 98, 22, 107, 22, 21, 42, 157, 2, 89,
1406 ]),
1407 CompressedRistretto([
1408 218, 128, 134, 39, 115, 53, 139, 70, 111, 250, 223, 224, 179, 41, 58, 179, 217,
1409 253, 83, 197, 234, 108, 149, 83, 88, 245, 104, 50, 45, 175, 106, 87,
1410 ]),
1411 CompressedRistretto([
1412 232, 130, 177, 49, 1, 107, 82, 193, 211, 51, 112, 128, 24, 124, 247, 104, 66, 62,
1413 252, 203, 181, 23, 187, 73, 90, 184, 18, 196, 22, 15, 244, 78,
1414 ]),
1415 CompressedRistretto([
1416 246, 71, 70, 211, 201, 43, 19, 5, 14, 216, 216, 2, 54, 167, 240, 0, 124, 59, 63,
1417 150, 47, 91, 167, 147, 209, 154, 96, 30, 187, 29, 244, 3,
1418 ]),
1419 CompressedRistretto([
1420 68, 245, 53, 32, 146, 110, 200, 31, 189, 90, 56, 120, 69, 190, 183, 223, 133, 169,
1421 106, 36, 236, 225, 135, 56, 189, 207, 166, 167, 130, 42, 23, 109,
1422 ]),
1423 CompressedRistretto([
1424 144, 50, 147, 216, 242, 40, 126, 190, 16, 226, 55, 77, 193, 165, 62, 11, 200, 135,
1425 229, 146, 105, 159, 2, 208, 119, 213, 38, 60, 221, 85, 96, 28,
1426 ]),
1427 CompressedRistretto([
1428 2, 98, 42, 206, 143, 115, 3, 163, 28, 175, 198, 63, 143, 196, 143, 220, 22, 225,
1429 200, 200, 210, 52, 178, 240, 214, 104, 82, 130, 169, 7, 96, 49,
1430 ]),
1431 CompressedRistretto([
1432 32, 112, 111, 215, 136, 178, 114, 10, 30, 210, 165, 218, 212, 149, 43, 1, 244, 19,
1433 188, 240, 231, 86, 77, 232, 205, 200, 22, 104, 158, 45, 185, 95,
1434 ]),
1435 CompressedRistretto([
1436 188, 232, 63, 139, 165, 221, 47, 165, 114, 134, 76, 36, 186, 24, 16, 249, 82, 43,
1437 198, 0, 74, 254, 149, 135, 122, 199, 50, 65, 202, 253, 171, 66,
1438 ]),
1439 CompressedRistretto([
1440 228, 84, 158, 225, 107, 154, 160, 48, 153, 202, 32, 140, 103, 173, 175, 202, 250,
1441 76, 63, 62, 78, 83, 3, 222, 96, 38, 227, 202, 143, 248, 68, 96,
1442 ]),
1443 CompressedRistretto([
1444 170, 82, 224, 0, 223, 46, 22, 245, 95, 177, 3, 47, 195, 59, 196, 39, 66, 218, 214,
1445 189, 90, 143, 192, 190, 1, 103, 67, 108, 89, 72, 80, 31,
1446 ]),
1447 CompressedRistretto([
1448 70, 55, 107, 128, 244, 9, 178, 157, 194, 181, 246, 240, 197, 37, 145, 153, 8, 150,
1449 229, 113, 111, 65, 71, 124, 211, 0, 133, 171, 127, 16, 48, 30,
1450 ]),
1451 CompressedRistretto([
1452 224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, 144,
1453 33, 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78,
1454 ]),
1455 ];
1456 let mut bp = RistrettoPoint::identity();
1457 for point in compressed {
1458 assert_eq!(bp.compress(), point);
1459 bp += constants::RISTRETTO_BASEPOINT_POINT;
1460 }
1461 }
1462
1463 #[test]
1464 fn four_torsion_basepoint() {
1465 let bp = constants::RISTRETTO_BASEPOINT_POINT;
1466 let bp_coset = bp.coset4();
1467 for point in bp_coset {
1468 assert_eq!(bp, RistrettoPoint(point));
1469 }
1470 }
1471
1472 #[test]
1473 fn four_torsion_random() {
1474 let mut rng = OsRng;
1475 let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
1476 let P_coset = P.coset4();
1477 for point in P_coset {
1478 assert_eq!(P, RistrettoPoint(point));
1479 }
1480 }
1481
1482 #[test]
1483 fn elligator_vs_ristretto_sage() {
1484 let bytes: [[u8; 32]; 16] = [
1491 [
1492 184, 249, 135, 49, 253, 123, 89, 113, 67, 160, 6, 239, 7, 105, 211, 41, 192, 249,
1493 185, 57, 9, 102, 70, 198, 15, 127, 7, 26, 160, 102, 134, 71,
1494 ],
1495 [
1496 229, 14, 241, 227, 75, 9, 118, 60, 128, 153, 226, 21, 183, 217, 91, 136, 98, 0,
1497 231, 156, 124, 77, 82, 139, 142, 134, 164, 169, 169, 62, 250, 52,
1498 ],
1499 [
1500 115, 109, 36, 220, 180, 223, 99, 6, 204, 169, 19, 29, 169, 68, 84, 23, 21, 109,
1501 189, 149, 127, 205, 91, 102, 172, 35, 112, 35, 134, 69, 186, 34,
1502 ],
1503 [
1504 16, 49, 96, 107, 171, 199, 164, 9, 129, 16, 64, 62, 241, 63, 132, 173, 209, 160,
1505 112, 215, 105, 50, 157, 81, 253, 105, 1, 154, 229, 25, 120, 83,
1506 ],
1507 [
1508 156, 131, 161, 162, 236, 251, 5, 187, 167, 171, 17, 178, 148, 210, 90, 207, 86, 21,
1509 79, 161, 167, 215, 234, 1, 136, 242, 182, 248, 38, 85, 79, 86,
1510 ],
1511 [
1512 251, 177, 124, 54, 18, 101, 75, 235, 245, 186, 19, 46, 133, 157, 229, 64, 10, 136,
1513 181, 185, 78, 144, 254, 167, 137, 49, 107, 10, 61, 10, 21, 25,
1514 ],
1515 [
1516 232, 193, 20, 68, 240, 77, 186, 77, 183, 40, 44, 86, 150, 31, 198, 212, 76, 81, 3,
1517 217, 197, 8, 126, 128, 126, 152, 164, 208, 153, 44, 189, 77,
1518 ],
1519 [
1520 173, 229, 149, 177, 37, 230, 30, 69, 61, 56, 172, 190, 219, 115, 167, 194, 71, 134,
1521 59, 75, 28, 244, 118, 26, 162, 97, 64, 16, 15, 189, 30, 64,
1522 ],
1523 [
1524 106, 71, 61, 107, 250, 117, 42, 151, 91, 202, 212, 100, 52, 188, 190, 21, 125, 218,
1525 31, 18, 253, 241, 160, 133, 57, 242, 3, 164, 189, 68, 111, 75,
1526 ],
1527 [
1528 112, 204, 182, 90, 220, 198, 120, 73, 173, 107, 193, 17, 227, 40, 162, 36, 150,
1529 141, 235, 55, 172, 183, 12, 39, 194, 136, 43, 153, 244, 118, 91, 89,
1530 ],
1531 [
1532 111, 24, 203, 123, 254, 189, 11, 162, 51, 196, 163, 136, 204, 143, 10, 222, 33,
1533 112, 81, 205, 34, 35, 8, 66, 90, 6, 164, 58, 170, 177, 34, 25,
1534 ],
1535 [
1536 225, 183, 30, 52, 236, 82, 6, 183, 109, 25, 227, 181, 25, 82, 41, 193, 80, 77, 161,
1537 80, 242, 203, 79, 204, 136, 245, 131, 110, 237, 106, 3, 58,
1538 ],
1539 [
1540 207, 246, 38, 56, 30, 86, 176, 90, 27, 200, 61, 42, 221, 27, 56, 210, 79, 178, 189,
1541 120, 68, 193, 120, 167, 77, 185, 53, 197, 124, 128, 191, 126,
1542 ],
1543 [
1544 1, 136, 215, 80, 240, 46, 63, 147, 16, 244, 230, 207, 82, 189, 74, 50, 106, 169,
1545 138, 86, 30, 131, 214, 202, 166, 125, 251, 228, 98, 24, 36, 21,
1546 ],
1547 [
1548 210, 207, 228, 56, 155, 116, 207, 54, 84, 195, 251, 215, 249, 199, 116, 75, 109,
1549 239, 196, 251, 194, 246, 252, 228, 70, 146, 156, 35, 25, 39, 241, 4,
1550 ],
1551 [
1552 34, 116, 123, 9, 8, 40, 93, 189, 9, 103, 57, 103, 66, 227, 3, 2, 157, 107, 134,
1553 219, 202, 74, 230, 154, 78, 107, 219, 195, 214, 14, 84, 80,
1554 ],
1555 ];
1556 let encoded_images: [CompressedRistretto; 16] = [
1557 CompressedRistretto([
1558 176, 157, 237, 97, 66, 29, 140, 166, 168, 94, 26, 157, 212, 216, 229, 160, 195,
1559 246, 232, 239, 169, 112, 63, 193, 64, 32, 152, 69, 11, 190, 246, 86,
1560 ]),
1561 CompressedRistretto([
1562 234, 141, 77, 203, 181, 225, 250, 74, 171, 62, 15, 118, 78, 212, 150, 19, 131, 14,
1563 188, 238, 194, 244, 141, 138, 166, 162, 83, 122, 228, 201, 19, 26,
1564 ]),
1565 CompressedRistretto([
1566 232, 231, 51, 92, 5, 168, 80, 36, 173, 179, 104, 68, 186, 149, 68, 40, 140, 170,
1567 27, 103, 99, 140, 21, 242, 43, 62, 250, 134, 208, 255, 61, 89,
1568 ]),
1569 CompressedRistretto([
1570 208, 120, 140, 129, 177, 179, 237, 159, 252, 160, 28, 13, 206, 5, 211, 241, 192,
1571 218, 1, 97, 130, 241, 20, 169, 119, 46, 246, 29, 79, 80, 77, 84,
1572 ]),
1573 CompressedRistretto([
1574 202, 11, 236, 145, 58, 12, 181, 157, 209, 6, 213, 88, 75, 147, 11, 119, 191, 139,
1575 47, 142, 33, 36, 153, 193, 223, 183, 178, 8, 205, 120, 248, 110,
1576 ]),
1577 CompressedRistretto([
1578 26, 66, 231, 67, 203, 175, 116, 130, 32, 136, 62, 253, 215, 46, 5, 214, 166, 248,
1579 108, 237, 216, 71, 244, 173, 72, 133, 82, 6, 143, 240, 104, 41,
1580 ]),
1581 CompressedRistretto([
1582 40, 157, 102, 96, 201, 223, 200, 197, 150, 181, 106, 83, 103, 126, 143, 33, 145,
1583 230, 78, 6, 171, 146, 210, 143, 112, 5, 245, 23, 183, 138, 18, 120,
1584 ]),
1585 CompressedRistretto([
1586 220, 37, 27, 203, 239, 196, 176, 131, 37, 66, 188, 243, 185, 250, 113, 23, 167,
1587 211, 154, 243, 168, 215, 54, 171, 159, 36, 195, 81, 13, 150, 43, 43,
1588 ]),
1589 CompressedRistretto([
1590 232, 121, 176, 222, 183, 196, 159, 90, 238, 193, 105, 52, 101, 167, 244, 170, 121,
1591 114, 196, 6, 67, 152, 80, 185, 221, 7, 83, 105, 176, 208, 224, 121,
1592 ]),
1593 CompressedRistretto([
1594 226, 181, 183, 52, 241, 163, 61, 179, 221, 207, 220, 73, 245, 242, 25, 236, 67, 84,
1595 179, 222, 167, 62, 167, 182, 32, 9, 92, 30, 165, 127, 204, 68,
1596 ]),
1597 CompressedRistretto([
1598 226, 119, 16, 242, 200, 139, 240, 87, 11, 222, 92, 146, 156, 243, 46, 119, 65, 59,
1599 1, 248, 92, 183, 50, 175, 87, 40, 206, 53, 208, 220, 148, 13,
1600 ]),
1601 CompressedRistretto([
1602 70, 240, 79, 112, 54, 157, 228, 146, 74, 122, 216, 88, 232, 62, 158, 13, 14, 146,
1603 115, 117, 176, 222, 90, 225, 244, 23, 94, 190, 150, 7, 136, 96,
1604 ]),
1605 CompressedRistretto([
1606 22, 71, 241, 103, 45, 193, 195, 144, 183, 101, 154, 50, 39, 68, 49, 110, 51, 44,
1607 62, 0, 229, 113, 72, 81, 168, 29, 73, 106, 102, 40, 132, 24,
1608 ]),
1609 CompressedRistretto([
1610 196, 133, 107, 11, 130, 105, 74, 33, 204, 171, 133, 221, 174, 193, 241, 36, 38,
1611 179, 196, 107, 219, 185, 181, 253, 228, 47, 155, 42, 231, 73, 41, 78,
1612 ]),
1613 CompressedRistretto([
1614 58, 255, 225, 197, 115, 208, 160, 143, 39, 197, 82, 69, 143, 235, 92, 170, 74, 40,
1615 57, 11, 171, 227, 26, 185, 217, 207, 90, 185, 197, 190, 35, 60,
1616 ]),
1617 CompressedRistretto([
1618 88, 43, 92, 118, 223, 136, 105, 145, 238, 186, 115, 8, 214, 112, 153, 253, 38, 108,
1619 205, 230, 157, 130, 11, 66, 101, 85, 253, 110, 110, 14, 148, 112,
1620 ]),
1621 ];
1622 for i in 0..16 {
1623 let r_0 = FieldElement::from_bytes(&bytes[i]);
1624 let Q = RistrettoPoint::elligator_ristretto_flavor(&r_0);
1625 assert_eq!(Q.compress(), encoded_images[i]);
1626 }
1627 }
1628
1629 #[test]
1631 fn one_way_map() {
1632 let test_vectors: &[([u8; 64], CompressedRistretto)] = &[
1635 (
1636 [
1637 0x5d, 0x1b, 0xe0, 0x9e, 0x3d, 0x0c, 0x82, 0xfc, 0x53, 0x81, 0x12, 0x49, 0x0e,
1638 0x35, 0x70, 0x19, 0x79, 0xd9, 0x9e, 0x06, 0xca, 0x3e, 0x2b, 0x5b, 0x54, 0xbf,
1639 0xfe, 0x8b, 0x4d, 0xc7, 0x72, 0xc1, 0x4d, 0x98, 0xb6, 0x96, 0xa1, 0xbb, 0xfb,
1640 0x5c, 0xa3, 0x2c, 0x43, 0x6c, 0xc6, 0x1c, 0x16, 0x56, 0x37, 0x90, 0x30, 0x6c,
1641 0x79, 0xea, 0xca, 0x77, 0x05, 0x66, 0x8b, 0x47, 0xdf, 0xfe, 0x5b, 0xb6,
1642 ],
1643 CompressedRistretto([
1644 0x30, 0x66, 0xf8, 0x2a, 0x1a, 0x74, 0x7d, 0x45, 0x12, 0x0d, 0x17, 0x40, 0xf1,
1645 0x43, 0x58, 0x53, 0x1a, 0x8f, 0x04, 0xbb, 0xff, 0xe6, 0xa8, 0x19, 0xf8, 0x6d,
1646 0xfe, 0x50, 0xf4, 0x4a, 0x0a, 0x46,
1647 ]),
1648 ),
1649 (
1650 [
1651 0xf1, 0x16, 0xb3, 0x4b, 0x8f, 0x17, 0xce, 0xb5, 0x6e, 0x87, 0x32, 0xa6, 0x0d,
1652 0x91, 0x3d, 0xd1, 0x0c, 0xce, 0x47, 0xa6, 0xd5, 0x3b, 0xee, 0x92, 0x04, 0xbe,
1653 0x8b, 0x44, 0xf6, 0x67, 0x8b, 0x27, 0x01, 0x02, 0xa5, 0x69, 0x02, 0xe2, 0x48,
1654 0x8c, 0x46, 0x12, 0x0e, 0x92, 0x76, 0xcf, 0xe5, 0x46, 0x38, 0x28, 0x6b, 0x9e,
1655 0x4b, 0x3c, 0xdb, 0x47, 0x0b, 0x54, 0x2d, 0x46, 0xc2, 0x06, 0x8d, 0x38,
1656 ],
1657 CompressedRistretto([
1658 0xf2, 0x6e, 0x5b, 0x6f, 0x7d, 0x36, 0x2d, 0x2d, 0x2a, 0x94, 0xc5, 0xd0, 0xe7,
1659 0x60, 0x2c, 0xb4, 0x77, 0x3c, 0x95, 0xa2, 0xe5, 0xc3, 0x1a, 0x64, 0xf1, 0x33,
1660 0x18, 0x9f, 0xa7, 0x6e, 0xd6, 0x1b,
1661 ]),
1662 ),
1663 (
1664 [
1665 0x84, 0x22, 0xe1, 0xbb, 0xda, 0xab, 0x52, 0x93, 0x8b, 0x81, 0xfd, 0x60, 0x2e,
1666 0xff, 0xb6, 0xf8, 0x91, 0x10, 0xe1, 0xe5, 0x72, 0x08, 0xad, 0x12, 0xd9, 0xad,
1667 0x76, 0x7e, 0x2e, 0x25, 0x51, 0x0c, 0x27, 0x14, 0x07, 0x75, 0xf9, 0x33, 0x70,
1668 0x88, 0xb9, 0x82, 0xd8, 0x3d, 0x7f, 0xcf, 0x0b, 0x2f, 0xa1, 0xed, 0xff, 0xe5,
1669 0x19, 0x52, 0xcb, 0xe7, 0x36, 0x5e, 0x95, 0xc8, 0x6e, 0xaf, 0x32, 0x5c,
1670 ],
1671 CompressedRistretto([
1672 0x00, 0x6c, 0xcd, 0x2a, 0x9e, 0x68, 0x67, 0xe6, 0xa2, 0xc5, 0xce, 0xa8, 0x3d,
1673 0x33, 0x02, 0xcc, 0x9d, 0xe1, 0x28, 0xdd, 0x2a, 0x9a, 0x57, 0xdd, 0x8e, 0xe7,
1674 0xb9, 0xd7, 0xff, 0xe0, 0x28, 0x26,
1675 ]),
1676 ),
1677 (
1678 [
1679 0xac, 0x22, 0x41, 0x51, 0x29, 0xb6, 0x14, 0x27, 0xbf, 0x46, 0x4e, 0x17, 0xba,
1680 0xee, 0x8d, 0xb6, 0x59, 0x40, 0xc2, 0x33, 0xb9, 0x8a, 0xfc, 0xe8, 0xd1, 0x7c,
1681 0x57, 0xbe, 0xeb, 0x78, 0x76, 0xc2, 0x15, 0x0d, 0x15, 0xaf, 0x1c, 0xb1, 0xfb,
1682 0x82, 0x4b, 0xbd, 0x14, 0x95, 0x5f, 0x2b, 0x57, 0xd0, 0x8d, 0x38, 0x8a, 0xab,
1683 0x43, 0x1a, 0x39, 0x1c, 0xfc, 0x33, 0xd5, 0xba, 0xfb, 0x5d, 0xbb, 0xaf,
1684 ],
1685 CompressedRistretto([
1686 0xf8, 0xf0, 0xc8, 0x7c, 0xf2, 0x37, 0x95, 0x3c, 0x58, 0x90, 0xae, 0xc3, 0x99,
1687 0x81, 0x69, 0x00, 0x5d, 0xae, 0x3e, 0xca, 0x1f, 0xbb, 0x04, 0x54, 0x8c, 0x63,
1688 0x59, 0x53, 0xc8, 0x17, 0xf9, 0x2a,
1689 ]),
1690 ),
1691 (
1692 [
1693 0x16, 0x5d, 0x69, 0x7a, 0x1e, 0xf3, 0xd5, 0xcf, 0x3c, 0x38, 0x56, 0x5b, 0xee,
1694 0xfc, 0xf8, 0x8c, 0x0f, 0x28, 0x2b, 0x8e, 0x7d, 0xbd, 0x28, 0x54, 0x4c, 0x48,
1695 0x34, 0x32, 0xf1, 0xce, 0xc7, 0x67, 0x5d, 0xeb, 0xea, 0x8e, 0xbb, 0x4e, 0x5f,
1696 0xe7, 0xd6, 0xf6, 0xe5, 0xdb, 0x15, 0xf1, 0x55, 0x87, 0xac, 0x4d, 0x4d, 0x4a,
1697 0x1d, 0xe7, 0x19, 0x1e, 0x0c, 0x1c, 0xa6, 0x66, 0x4a, 0xbc, 0xc4, 0x13,
1698 ],
1699 CompressedRistretto([
1700 0xae, 0x81, 0xe7, 0xde, 0xdf, 0x20, 0xa4, 0x97, 0xe1, 0x0c, 0x30, 0x4a, 0x76,
1701 0x5c, 0x17, 0x67, 0xa4, 0x2d, 0x6e, 0x06, 0x02, 0x97, 0x58, 0xd2, 0xd7, 0xe8,
1702 0xef, 0x7c, 0xc4, 0xc4, 0x11, 0x79,
1703 ]),
1704 ),
1705 (
1706 [
1707 0xa8, 0x36, 0xe6, 0xc9, 0xa9, 0xca, 0x9f, 0x1e, 0x8d, 0x48, 0x62, 0x73, 0xad,
1708 0x56, 0xa7, 0x8c, 0x70, 0xcf, 0x18, 0xf0, 0xce, 0x10, 0xab, 0xb1, 0xc7, 0x17,
1709 0x2d, 0xdd, 0x60, 0x5d, 0x7f, 0xd2, 0x97, 0x98, 0x54, 0xf4, 0x7a, 0xe1, 0xcc,
1710 0xf2, 0x04, 0xa3, 0x31, 0x02, 0x09, 0x5b, 0x42, 0x00, 0xe5, 0xbe, 0xfc, 0x04,
1711 0x65, 0xac, 0xcc, 0x26, 0x31, 0x75, 0x48, 0x5f, 0x0e, 0x17, 0xea, 0x5c,
1712 ],
1713 CompressedRistretto([
1714 0xe2, 0x70, 0x56, 0x52, 0xff, 0x9f, 0x5e, 0x44, 0xd3, 0xe8, 0x41, 0xbf, 0x1c,
1715 0x25, 0x1c, 0xf7, 0xdd, 0xdb, 0x77, 0xd1, 0x40, 0x87, 0x0d, 0x1a, 0xb2, 0xed,
1716 0x64, 0xf1, 0xa9, 0xce, 0x86, 0x28,
1717 ]),
1718 ),
1719 (
1720 [
1721 0x2c, 0xdc, 0x11, 0xea, 0xeb, 0x95, 0xda, 0xf0, 0x11, 0x89, 0x41, 0x7c, 0xdd,
1722 0xdb, 0xf9, 0x59, 0x52, 0x99, 0x3a, 0xa9, 0xcb, 0x9c, 0x64, 0x0e, 0xb5, 0x05,
1723 0x8d, 0x09, 0x70, 0x2c, 0x74, 0x62, 0x2c, 0x99, 0x65, 0xa6, 0x97, 0xa3, 0xb3,
1724 0x45, 0xec, 0x24, 0xee, 0x56, 0x33, 0x5b, 0x55, 0x6e, 0x67, 0x7b, 0x30, 0xe6,
1725 0xf9, 0x0a, 0xc7, 0x7d, 0x78, 0x10, 0x64, 0xf8, 0x66, 0xa3, 0xc9, 0x82,
1726 ],
1727 CompressedRistretto([
1728 0x80, 0xbd, 0x07, 0x26, 0x25, 0x11, 0xcd, 0xde, 0x48, 0x63, 0xf8, 0xa7, 0x43,
1729 0x4c, 0xef, 0x69, 0x67, 0x50, 0x68, 0x1c, 0xb9, 0x51, 0x0e, 0xea, 0x55, 0x70,
1730 0x88, 0xf7, 0x6d, 0x9e, 0x50, 0x65,
1731 ]),
1732 ),
1733 (
1734 [
1735 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1736 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1737 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1738 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1739 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1740 ],
1741 CompressedRistretto([
1742 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1743 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1744 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1745 ]),
1746 ),
1747 (
1748 [
1749 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1750 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1751 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1752 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1753 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1754 ],
1755 CompressedRistretto([
1756 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1757 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1758 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1759 ]),
1760 ),
1761 (
1762 [
1763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1766 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1767 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
1768 ],
1769 CompressedRistretto([
1770 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1771 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1772 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1773 ]),
1774 ),
1775 (
1776 [
1777 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1778 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1779 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1780 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1781 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
1782 ],
1783 CompressedRistretto([
1784 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1785 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1786 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1787 ]),
1788 ),
1789 ];
1790 for (input, output) in test_vectors {
1792 let Q = RistrettoPoint::from_uniform_bytes(input);
1793 assert_eq!(&Q.compress(), output);
1794 }
1795 }
1796
1797 #[test]
1798 fn random_roundtrip() {
1799 let mut rng = OsRng;
1800 for _ in 0..100 {
1801 let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
1802 let compressed_P = P.compress();
1803 let Q = compressed_P.decompress().unwrap();
1804 assert_eq!(P, Q);
1805 }
1806 }
1807
1808 #[test]
1809 #[cfg(all(feature = "alloc", feature = "rand_core"))]
1810 fn double_and_compress_1024_random_points() {
1811 let mut rng = OsRng;
1812
1813 let mut points: Vec<RistrettoPoint> = (0..1024)
1814 .map(|_| RistrettoPoint::random(&mut rng))
1815 .collect();
1816 points[500] = RistrettoPoint::identity();
1817
1818 let compressed = RistrettoPoint::double_and_compress_batch(&points);
1819
1820 for (P, P2_compressed) in points.iter().zip(compressed.iter()) {
1821 assert_eq!(*P2_compressed, (P + P).compress());
1822 }
1823 }
1824
1825 #[test]
1826 #[cfg(feature = "alloc")]
1827 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
1828 let mut rng = rand::thread_rng();
1829
1830 let static_scalars = (0..128)
1831 .map(|_| Scalar::random(&mut rng))
1832 .collect::<Vec<_>>();
1833
1834 let dynamic_scalars = (0..128)
1835 .map(|_| Scalar::random(&mut rng))
1836 .collect::<Vec<_>>();
1837
1838 let check_scalar: Scalar = static_scalars
1839 .iter()
1840 .chain(dynamic_scalars.iter())
1841 .map(|s| s * s)
1842 .sum();
1843
1844 let static_points = static_scalars
1845 .iter()
1846 .map(RistrettoPoint::mul_base)
1847 .collect::<Vec<_>>();
1848 let dynamic_points = dynamic_scalars
1849 .iter()
1850 .map(RistrettoPoint::mul_base)
1851 .collect::<Vec<_>>();
1852
1853 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
1854
1855 let P = precomputation.vartime_mixed_multiscalar_mul(
1856 &static_scalars,
1857 &dynamic_scalars,
1858 &dynamic_points,
1859 );
1860
1861 use crate::traits::VartimeMultiscalarMul;
1862 let Q = RistrettoPoint::vartime_multiscalar_mul(
1863 static_scalars.iter().chain(dynamic_scalars.iter()),
1864 static_points.iter().chain(dynamic_points.iter()),
1865 );
1866
1867 let R = RistrettoPoint::mul_base(&check_scalar);
1868
1869 assert_eq!(P.compress(), R.compress());
1870 assert_eq!(Q.compress(), R.compress());
1871 }
1872}