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.to_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.to_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.to_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> Add<&'a RistrettoPoint> for &RistrettoPoint {
852 type Output = RistrettoPoint;
853
854 fn add(self, other: &'a 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 AddAssign<&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> Sub<&'a RistrettoPoint> for &RistrettoPoint {
874 type Output = RistrettoPoint;
875
876 fn sub(self, other: &'a 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 SubAssign<&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 Neg for &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<'a> MulAssign<&'a Scalar> for RistrettoPoint {
924 fn mul_assign(&mut self, scalar: &'a Scalar) {
925 let result = (self as &RistrettoPoint) * scalar;
926 *self = result;
927 }
928}
929
930impl<'a> Mul<&'a Scalar> for &RistrettoPoint {
931 type Output = RistrettoPoint;
932 fn mul(self, scalar: &'a Scalar) -> RistrettoPoint {
934 RistrettoPoint(self.0 * scalar)
935 }
936}
937
938impl<'a> Mul<&'a RistrettoPoint> for &Scalar {
939 type Output = RistrettoPoint;
940
941 fn mul(self, point: &'a 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")]
1017pub struct VartimeRistrettoPrecomputation(crate::backend::VartimePrecomputedStraus);
1018
1019#[cfg(feature = "alloc")]
1020impl VartimePrecomputedMultiscalarMul for VartimeRistrettoPrecomputation {
1021 type Point = RistrettoPoint;
1022
1023 fn new<I>(static_points: I) -> Self
1024 where
1025 I: IntoIterator,
1026 I::Item: Borrow<Self::Point>,
1027 {
1028 Self(crate::backend::VartimePrecomputedStraus::new(
1029 static_points.into_iter().map(|P| P.borrow().0),
1030 ))
1031 }
1032
1033 fn len(&self) -> usize {
1034 self.0.len()
1035 }
1036
1037 fn is_empty(&self) -> bool {
1038 self.0.is_empty()
1039 }
1040
1041 fn optional_mixed_multiscalar_mul<I, J, K>(
1042 &self,
1043 static_scalars: I,
1044 dynamic_scalars: J,
1045 dynamic_points: K,
1046 ) -> Option<Self::Point>
1047 where
1048 I: IntoIterator,
1049 I::Item: Borrow<Scalar>,
1050 J: IntoIterator,
1051 J::Item: Borrow<Scalar>,
1052 K: IntoIterator<Item = Option<Self::Point>>,
1053 {
1054 self.0
1055 .optional_mixed_multiscalar_mul(
1056 static_scalars,
1057 dynamic_scalars,
1058 dynamic_points.into_iter().map(|P_opt| P_opt.map(|P| P.0)),
1059 )
1060 .map(RistrettoPoint)
1061 }
1062}
1063
1064impl RistrettoPoint {
1065 pub fn vartime_double_scalar_mul_basepoint(
1068 a: &Scalar,
1069 A: &RistrettoPoint,
1070 b: &Scalar,
1071 ) -> RistrettoPoint {
1072 RistrettoPoint(EdwardsPoint::vartime_double_scalar_mul_basepoint(
1073 a, &A.0, b,
1074 ))
1075 }
1076}
1077
1078#[cfg(feature = "precomputed-tables")]
1091#[derive(Clone)]
1092#[repr(transparent)]
1093pub struct RistrettoBasepointTable(pub(crate) EdwardsBasepointTable);
1094
1095#[cfg(feature = "precomputed-tables")]
1096impl<'b> Mul<&'b Scalar> for &RistrettoBasepointTable {
1097 type Output = RistrettoPoint;
1098
1099 fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
1100 RistrettoPoint(&self.0 * scalar)
1101 }
1102}
1103
1104#[cfg(feature = "precomputed-tables")]
1105impl<'a> Mul<&'a RistrettoBasepointTable> for &Scalar {
1106 type Output = RistrettoPoint;
1107
1108 fn mul(self, basepoint_table: &'a RistrettoBasepointTable) -> RistrettoPoint {
1109 RistrettoPoint(self * &basepoint_table.0)
1110 }
1111}
1112
1113#[cfg(feature = "precomputed-tables")]
1114impl RistrettoBasepointTable {
1115 pub fn create(basepoint: &RistrettoPoint) -> RistrettoBasepointTable {
1117 RistrettoBasepointTable(EdwardsBasepointTable::create(&basepoint.0))
1118 }
1119
1120 pub fn basepoint(&self) -> RistrettoPoint {
1122 RistrettoPoint(self.0.basepoint())
1123 }
1124}
1125
1126impl ConditionallySelectable for RistrettoPoint {
1131 fn conditional_select(
1156 a: &RistrettoPoint,
1157 b: &RistrettoPoint,
1158 choice: Choice,
1159 ) -> RistrettoPoint {
1160 RistrettoPoint(EdwardsPoint::conditional_select(&a.0, &b.0, choice))
1161 }
1162}
1163
1164impl Debug for CompressedRistretto {
1169 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1170 write!(f, "CompressedRistretto: {:?}", self.as_bytes())
1171 }
1172}
1173
1174impl Debug for RistrettoPoint {
1175 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1176 let coset = self.coset4();
1177 write!(
1178 f,
1179 "RistrettoPoint: coset \n{:?}\n{:?}\n{:?}\n{:?}",
1180 coset[0], coset[1], coset[2], coset[3]
1181 )
1182 }
1183}
1184
1185#[cfg(feature = "group")]
1192impl group::Group for RistrettoPoint {
1193 type Scalar = Scalar;
1194
1195 fn random(mut rng: impl RngCore) -> Self {
1196 let mut uniform_bytes = [0u8; 64];
1198 rng.fill_bytes(&mut uniform_bytes);
1199 RistrettoPoint::from_uniform_bytes(&uniform_bytes)
1200 }
1201
1202 fn identity() -> Self {
1203 Identity::identity()
1204 }
1205
1206 fn generator() -> Self {
1207 constants::RISTRETTO_BASEPOINT_POINT
1208 }
1209
1210 fn is_identity(&self) -> Choice {
1211 self.ct_eq(&Identity::identity())
1212 }
1213
1214 fn double(&self) -> Self {
1215 self + self
1216 }
1217}
1218
1219#[cfg(feature = "group")]
1220impl GroupEncoding for RistrettoPoint {
1221 type Repr = [u8; 32];
1222
1223 fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
1224 let (s_encoding_is_canonical, s_is_negative, s) =
1225 decompress::step_1(&CompressedRistretto(*bytes));
1226
1227 let s_is_valid = s_encoding_is_canonical & !s_is_negative;
1228
1229 let (ok, t_is_negative, y_is_zero, res) = decompress::step_2(s);
1230
1231 CtOption::new(res, s_is_valid & ok & !t_is_negative & !y_is_zero)
1232 }
1233
1234 fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
1235 Self::from_bytes(bytes)
1237 }
1238
1239 fn to_bytes(&self) -> Self::Repr {
1240 self.compress().to_bytes()
1241 }
1242}
1243
1244#[cfg(feature = "group")]
1245impl PrimeGroup for RistrettoPoint {}
1246
1247#[cfg(feature = "group")]
1249impl CofactorGroup for RistrettoPoint {
1250 type Subgroup = Self;
1251
1252 fn clear_cofactor(&self) -> Self::Subgroup {
1253 *self
1254 }
1255
1256 fn into_subgroup(self) -> CtOption<Self::Subgroup> {
1257 CtOption::new(self, Choice::from(1))
1258 }
1259
1260 fn is_torsion_free(&self) -> Choice {
1261 Choice::from(1)
1262 }
1263}
1264
1265#[cfg(feature = "zeroize")]
1270impl Zeroize for CompressedRistretto {
1271 fn zeroize(&mut self) {
1272 self.0.zeroize();
1273 }
1274}
1275
1276#[cfg(feature = "zeroize")]
1277impl Zeroize for RistrettoPoint {
1278 fn zeroize(&mut self) {
1279 self.0.zeroize();
1280 }
1281}
1282
1283#[cfg(test)]
1288mod test {
1289 use super::*;
1290 use crate::edwards::CompressedEdwardsY;
1291
1292 use rand_core::OsRng;
1293
1294 #[test]
1295 #[cfg(feature = "serde")]
1296 fn serde_bincode_basepoint_roundtrip() {
1297 use bincode;
1298
1299 let encoded = bincode::serialize(&constants::RISTRETTO_BASEPOINT_POINT).unwrap();
1300 let enc_compressed =
1301 bincode::serialize(&constants::RISTRETTO_BASEPOINT_COMPRESSED).unwrap();
1302 assert_eq!(encoded, enc_compressed);
1303
1304 assert_eq!(encoded.len(), 32);
1306
1307 let dec_uncompressed: RistrettoPoint = bincode::deserialize(&encoded).unwrap();
1308 let dec_compressed: CompressedRistretto = bincode::deserialize(&encoded).unwrap();
1309
1310 assert_eq!(dec_uncompressed, constants::RISTRETTO_BASEPOINT_POINT);
1311 assert_eq!(dec_compressed, constants::RISTRETTO_BASEPOINT_COMPRESSED);
1312
1313 let raw_bytes = constants::RISTRETTO_BASEPOINT_COMPRESSED.as_bytes();
1315 let bp: RistrettoPoint = bincode::deserialize(raw_bytes).unwrap();
1316 assert_eq!(bp, constants::RISTRETTO_BASEPOINT_POINT);
1317 }
1318
1319 #[test]
1320 fn scalarmult_ristrettopoint_works_both_ways() {
1321 let P = constants::RISTRETTO_BASEPOINT_POINT;
1322 let s = Scalar::from(999u64);
1323
1324 let P1 = P * s;
1325 let P2 = s * P;
1326
1327 assert!(P1.compress().as_bytes() == P2.compress().as_bytes());
1328 }
1329
1330 #[test]
1331 #[cfg(feature = "alloc")]
1332 fn impl_sum() {
1333 let BASE = constants::RISTRETTO_BASEPOINT_POINT;
1335
1336 let s1 = Scalar::from(999u64);
1337 let P1 = BASE * s1;
1338
1339 let s2 = Scalar::from(333u64);
1340 let P2 = BASE * s2;
1341
1342 let vec = vec![P1, P2];
1343 let sum: RistrettoPoint = vec.iter().sum();
1344
1345 assert_eq!(sum, P1 + P2);
1346
1347 let empty_vector: Vec<RistrettoPoint> = vec![];
1349 let sum: RistrettoPoint = empty_vector.iter().sum();
1350
1351 assert_eq!(sum, RistrettoPoint::identity());
1352
1353 let s = Scalar::from(2u64);
1355 let mapped = vec.iter().map(|x| x * s);
1356 let sum: RistrettoPoint = mapped.sum();
1357
1358 assert_eq!(sum, P1 * s + P2 * s);
1359 }
1360
1361 #[test]
1362 fn decompress_negative_s_fails() {
1363 let bad_compressed = CompressedRistretto(constants::EDWARDS_D.to_bytes());
1365 assert!(bad_compressed.decompress().is_none());
1366 }
1367
1368 #[test]
1369 fn decompress_id() {
1370 let compressed_id = CompressedRistretto::identity();
1371 let id = compressed_id.decompress().unwrap();
1372 let mut identity_in_coset = false;
1373 for P in &id.coset4() {
1374 if P.compress() == CompressedEdwardsY::identity() {
1375 identity_in_coset = true;
1376 }
1377 }
1378 assert!(identity_in_coset);
1379 }
1380
1381 #[test]
1382 fn compress_id() {
1383 let id = RistrettoPoint::identity();
1384 assert_eq!(id.compress(), CompressedRistretto::identity());
1385 }
1386
1387 #[test]
1388 fn basepoint_roundtrip() {
1389 let bp_compressed_ristretto = constants::RISTRETTO_BASEPOINT_POINT.compress();
1390 let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0;
1391 let diff = constants::RISTRETTO_BASEPOINT_POINT.0 - bp_recaf;
1393 let diff4 = diff.mul_by_pow_2(2);
1394 assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
1395 }
1396
1397 #[test]
1398 fn encodings_of_small_multiples_of_basepoint() {
1399 let compressed = [
1402 CompressedRistretto([
1403 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,
1404 0, 0, 0, 0,
1405 ]),
1406 CompressedRistretto([
1407 226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227,
1408 11, 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118,
1409 ]),
1410 CompressedRistretto([
1411 106, 73, 50, 16, 247, 73, 156, 209, 127, 236, 181, 16, 174, 12, 234, 35, 161, 16,
1412 232, 213, 185, 1, 248, 172, 173, 211, 9, 92, 115, 163, 185, 25,
1413 ]),
1414 CompressedRistretto([
1415 148, 116, 31, 93, 93, 82, 117, 94, 206, 79, 35, 240, 68, 238, 39, 213, 209, 234,
1416 30, 43, 209, 150, 180, 98, 22, 107, 22, 21, 42, 157, 2, 89,
1417 ]),
1418 CompressedRistretto([
1419 218, 128, 134, 39, 115, 53, 139, 70, 111, 250, 223, 224, 179, 41, 58, 179, 217,
1420 253, 83, 197, 234, 108, 149, 83, 88, 245, 104, 50, 45, 175, 106, 87,
1421 ]),
1422 CompressedRistretto([
1423 232, 130, 177, 49, 1, 107, 82, 193, 211, 51, 112, 128, 24, 124, 247, 104, 66, 62,
1424 252, 203, 181, 23, 187, 73, 90, 184, 18, 196, 22, 15, 244, 78,
1425 ]),
1426 CompressedRistretto([
1427 246, 71, 70, 211, 201, 43, 19, 5, 14, 216, 216, 2, 54, 167, 240, 0, 124, 59, 63,
1428 150, 47, 91, 167, 147, 209, 154, 96, 30, 187, 29, 244, 3,
1429 ]),
1430 CompressedRistretto([
1431 68, 245, 53, 32, 146, 110, 200, 31, 189, 90, 56, 120, 69, 190, 183, 223, 133, 169,
1432 106, 36, 236, 225, 135, 56, 189, 207, 166, 167, 130, 42, 23, 109,
1433 ]),
1434 CompressedRistretto([
1435 144, 50, 147, 216, 242, 40, 126, 190, 16, 226, 55, 77, 193, 165, 62, 11, 200, 135,
1436 229, 146, 105, 159, 2, 208, 119, 213, 38, 60, 221, 85, 96, 28,
1437 ]),
1438 CompressedRistretto([
1439 2, 98, 42, 206, 143, 115, 3, 163, 28, 175, 198, 63, 143, 196, 143, 220, 22, 225,
1440 200, 200, 210, 52, 178, 240, 214, 104, 82, 130, 169, 7, 96, 49,
1441 ]),
1442 CompressedRistretto([
1443 32, 112, 111, 215, 136, 178, 114, 10, 30, 210, 165, 218, 212, 149, 43, 1, 244, 19,
1444 188, 240, 231, 86, 77, 232, 205, 200, 22, 104, 158, 45, 185, 95,
1445 ]),
1446 CompressedRistretto([
1447 188, 232, 63, 139, 165, 221, 47, 165, 114, 134, 76, 36, 186, 24, 16, 249, 82, 43,
1448 198, 0, 74, 254, 149, 135, 122, 199, 50, 65, 202, 253, 171, 66,
1449 ]),
1450 CompressedRistretto([
1451 228, 84, 158, 225, 107, 154, 160, 48, 153, 202, 32, 140, 103, 173, 175, 202, 250,
1452 76, 63, 62, 78, 83, 3, 222, 96, 38, 227, 202, 143, 248, 68, 96,
1453 ]),
1454 CompressedRistretto([
1455 170, 82, 224, 0, 223, 46, 22, 245, 95, 177, 3, 47, 195, 59, 196, 39, 66, 218, 214,
1456 189, 90, 143, 192, 190, 1, 103, 67, 108, 89, 72, 80, 31,
1457 ]),
1458 CompressedRistretto([
1459 70, 55, 107, 128, 244, 9, 178, 157, 194, 181, 246, 240, 197, 37, 145, 153, 8, 150,
1460 229, 113, 111, 65, 71, 124, 211, 0, 133, 171, 127, 16, 48, 30,
1461 ]),
1462 CompressedRistretto([
1463 224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, 144,
1464 33, 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78,
1465 ]),
1466 ];
1467 let mut bp = RistrettoPoint::identity();
1468 for point in compressed {
1469 assert_eq!(bp.compress(), point);
1470 bp += constants::RISTRETTO_BASEPOINT_POINT;
1471 }
1472 }
1473
1474 #[test]
1475 fn four_torsion_basepoint() {
1476 let bp = constants::RISTRETTO_BASEPOINT_POINT;
1477 let bp_coset = bp.coset4();
1478 for point in bp_coset {
1479 assert_eq!(bp, RistrettoPoint(point));
1480 }
1481 }
1482
1483 #[test]
1484 fn four_torsion_random() {
1485 let mut rng = OsRng;
1486 let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
1487 let P_coset = P.coset4();
1488 for point in P_coset {
1489 assert_eq!(P, RistrettoPoint(point));
1490 }
1491 }
1492
1493 #[test]
1494 fn elligator_vs_ristretto_sage() {
1495 let bytes: [[u8; 32]; 16] = [
1502 [
1503 184, 249, 135, 49, 253, 123, 89, 113, 67, 160, 6, 239, 7, 105, 211, 41, 192, 249,
1504 185, 57, 9, 102, 70, 198, 15, 127, 7, 26, 160, 102, 134, 71,
1505 ],
1506 [
1507 229, 14, 241, 227, 75, 9, 118, 60, 128, 153, 226, 21, 183, 217, 91, 136, 98, 0,
1508 231, 156, 124, 77, 82, 139, 142, 134, 164, 169, 169, 62, 250, 52,
1509 ],
1510 [
1511 115, 109, 36, 220, 180, 223, 99, 6, 204, 169, 19, 29, 169, 68, 84, 23, 21, 109,
1512 189, 149, 127, 205, 91, 102, 172, 35, 112, 35, 134, 69, 186, 34,
1513 ],
1514 [
1515 16, 49, 96, 107, 171, 199, 164, 9, 129, 16, 64, 62, 241, 63, 132, 173, 209, 160,
1516 112, 215, 105, 50, 157, 81, 253, 105, 1, 154, 229, 25, 120, 83,
1517 ],
1518 [
1519 156, 131, 161, 162, 236, 251, 5, 187, 167, 171, 17, 178, 148, 210, 90, 207, 86, 21,
1520 79, 161, 167, 215, 234, 1, 136, 242, 182, 248, 38, 85, 79, 86,
1521 ],
1522 [
1523 251, 177, 124, 54, 18, 101, 75, 235, 245, 186, 19, 46, 133, 157, 229, 64, 10, 136,
1524 181, 185, 78, 144, 254, 167, 137, 49, 107, 10, 61, 10, 21, 25,
1525 ],
1526 [
1527 232, 193, 20, 68, 240, 77, 186, 77, 183, 40, 44, 86, 150, 31, 198, 212, 76, 81, 3,
1528 217, 197, 8, 126, 128, 126, 152, 164, 208, 153, 44, 189, 77,
1529 ],
1530 [
1531 173, 229, 149, 177, 37, 230, 30, 69, 61, 56, 172, 190, 219, 115, 167, 194, 71, 134,
1532 59, 75, 28, 244, 118, 26, 162, 97, 64, 16, 15, 189, 30, 64,
1533 ],
1534 [
1535 106, 71, 61, 107, 250, 117, 42, 151, 91, 202, 212, 100, 52, 188, 190, 21, 125, 218,
1536 31, 18, 253, 241, 160, 133, 57, 242, 3, 164, 189, 68, 111, 75,
1537 ],
1538 [
1539 112, 204, 182, 90, 220, 198, 120, 73, 173, 107, 193, 17, 227, 40, 162, 36, 150,
1540 141, 235, 55, 172, 183, 12, 39, 194, 136, 43, 153, 244, 118, 91, 89,
1541 ],
1542 [
1543 111, 24, 203, 123, 254, 189, 11, 162, 51, 196, 163, 136, 204, 143, 10, 222, 33,
1544 112, 81, 205, 34, 35, 8, 66, 90, 6, 164, 58, 170, 177, 34, 25,
1545 ],
1546 [
1547 225, 183, 30, 52, 236, 82, 6, 183, 109, 25, 227, 181, 25, 82, 41, 193, 80, 77, 161,
1548 80, 242, 203, 79, 204, 136, 245, 131, 110, 237, 106, 3, 58,
1549 ],
1550 [
1551 207, 246, 38, 56, 30, 86, 176, 90, 27, 200, 61, 42, 221, 27, 56, 210, 79, 178, 189,
1552 120, 68, 193, 120, 167, 77, 185, 53, 197, 124, 128, 191, 126,
1553 ],
1554 [
1555 1, 136, 215, 80, 240, 46, 63, 147, 16, 244, 230, 207, 82, 189, 74, 50, 106, 169,
1556 138, 86, 30, 131, 214, 202, 166, 125, 251, 228, 98, 24, 36, 21,
1557 ],
1558 [
1559 210, 207, 228, 56, 155, 116, 207, 54, 84, 195, 251, 215, 249, 199, 116, 75, 109,
1560 239, 196, 251, 194, 246, 252, 228, 70, 146, 156, 35, 25, 39, 241, 4,
1561 ],
1562 [
1563 34, 116, 123, 9, 8, 40, 93, 189, 9, 103, 57, 103, 66, 227, 3, 2, 157, 107, 134,
1564 219, 202, 74, 230, 154, 78, 107, 219, 195, 214, 14, 84, 80,
1565 ],
1566 ];
1567 let encoded_images: [CompressedRistretto; 16] = [
1568 CompressedRistretto([
1569 176, 157, 237, 97, 66, 29, 140, 166, 168, 94, 26, 157, 212, 216, 229, 160, 195,
1570 246, 232, 239, 169, 112, 63, 193, 64, 32, 152, 69, 11, 190, 246, 86,
1571 ]),
1572 CompressedRistretto([
1573 234, 141, 77, 203, 181, 225, 250, 74, 171, 62, 15, 118, 78, 212, 150, 19, 131, 14,
1574 188, 238, 194, 244, 141, 138, 166, 162, 83, 122, 228, 201, 19, 26,
1575 ]),
1576 CompressedRistretto([
1577 232, 231, 51, 92, 5, 168, 80, 36, 173, 179, 104, 68, 186, 149, 68, 40, 140, 170,
1578 27, 103, 99, 140, 21, 242, 43, 62, 250, 134, 208, 255, 61, 89,
1579 ]),
1580 CompressedRistretto([
1581 208, 120, 140, 129, 177, 179, 237, 159, 252, 160, 28, 13, 206, 5, 211, 241, 192,
1582 218, 1, 97, 130, 241, 20, 169, 119, 46, 246, 29, 79, 80, 77, 84,
1583 ]),
1584 CompressedRistretto([
1585 202, 11, 236, 145, 58, 12, 181, 157, 209, 6, 213, 88, 75, 147, 11, 119, 191, 139,
1586 47, 142, 33, 36, 153, 193, 223, 183, 178, 8, 205, 120, 248, 110,
1587 ]),
1588 CompressedRistretto([
1589 26, 66, 231, 67, 203, 175, 116, 130, 32, 136, 62, 253, 215, 46, 5, 214, 166, 248,
1590 108, 237, 216, 71, 244, 173, 72, 133, 82, 6, 143, 240, 104, 41,
1591 ]),
1592 CompressedRistretto([
1593 40, 157, 102, 96, 201, 223, 200, 197, 150, 181, 106, 83, 103, 126, 143, 33, 145,
1594 230, 78, 6, 171, 146, 210, 143, 112, 5, 245, 23, 183, 138, 18, 120,
1595 ]),
1596 CompressedRistretto([
1597 220, 37, 27, 203, 239, 196, 176, 131, 37, 66, 188, 243, 185, 250, 113, 23, 167,
1598 211, 154, 243, 168, 215, 54, 171, 159, 36, 195, 81, 13, 150, 43, 43,
1599 ]),
1600 CompressedRistretto([
1601 232, 121, 176, 222, 183, 196, 159, 90, 238, 193, 105, 52, 101, 167, 244, 170, 121,
1602 114, 196, 6, 67, 152, 80, 185, 221, 7, 83, 105, 176, 208, 224, 121,
1603 ]),
1604 CompressedRistretto([
1605 226, 181, 183, 52, 241, 163, 61, 179, 221, 207, 220, 73, 245, 242, 25, 236, 67, 84,
1606 179, 222, 167, 62, 167, 182, 32, 9, 92, 30, 165, 127, 204, 68,
1607 ]),
1608 CompressedRistretto([
1609 226, 119, 16, 242, 200, 139, 240, 87, 11, 222, 92, 146, 156, 243, 46, 119, 65, 59,
1610 1, 248, 92, 183, 50, 175, 87, 40, 206, 53, 208, 220, 148, 13,
1611 ]),
1612 CompressedRistretto([
1613 70, 240, 79, 112, 54, 157, 228, 146, 74, 122, 216, 88, 232, 62, 158, 13, 14, 146,
1614 115, 117, 176, 222, 90, 225, 244, 23, 94, 190, 150, 7, 136, 96,
1615 ]),
1616 CompressedRistretto([
1617 22, 71, 241, 103, 45, 193, 195, 144, 183, 101, 154, 50, 39, 68, 49, 110, 51, 44,
1618 62, 0, 229, 113, 72, 81, 168, 29, 73, 106, 102, 40, 132, 24,
1619 ]),
1620 CompressedRistretto([
1621 196, 133, 107, 11, 130, 105, 74, 33, 204, 171, 133, 221, 174, 193, 241, 36, 38,
1622 179, 196, 107, 219, 185, 181, 253, 228, 47, 155, 42, 231, 73, 41, 78,
1623 ]),
1624 CompressedRistretto([
1625 58, 255, 225, 197, 115, 208, 160, 143, 39, 197, 82, 69, 143, 235, 92, 170, 74, 40,
1626 57, 11, 171, 227, 26, 185, 217, 207, 90, 185, 197, 190, 35, 60,
1627 ]),
1628 CompressedRistretto([
1629 88, 43, 92, 118, 223, 136, 105, 145, 238, 186, 115, 8, 214, 112, 153, 253, 38, 108,
1630 205, 230, 157, 130, 11, 66, 101, 85, 253, 110, 110, 14, 148, 112,
1631 ]),
1632 ];
1633 for i in 0..16 {
1634 let r_0 = FieldElement::from_bytes(&bytes[i]);
1635 let Q = RistrettoPoint::elligator_ristretto_flavor(&r_0);
1636 assert_eq!(Q.compress(), encoded_images[i]);
1637 }
1638 }
1639
1640 #[test]
1642 fn one_way_map() {
1643 let test_vectors: &[([u8; 64], CompressedRistretto)] = &[
1646 (
1647 [
1648 0x5d, 0x1b, 0xe0, 0x9e, 0x3d, 0x0c, 0x82, 0xfc, 0x53, 0x81, 0x12, 0x49, 0x0e,
1649 0x35, 0x70, 0x19, 0x79, 0xd9, 0x9e, 0x06, 0xca, 0x3e, 0x2b, 0x5b, 0x54, 0xbf,
1650 0xfe, 0x8b, 0x4d, 0xc7, 0x72, 0xc1, 0x4d, 0x98, 0xb6, 0x96, 0xa1, 0xbb, 0xfb,
1651 0x5c, 0xa3, 0x2c, 0x43, 0x6c, 0xc6, 0x1c, 0x16, 0x56, 0x37, 0x90, 0x30, 0x6c,
1652 0x79, 0xea, 0xca, 0x77, 0x05, 0x66, 0x8b, 0x47, 0xdf, 0xfe, 0x5b, 0xb6,
1653 ],
1654 CompressedRistretto([
1655 0x30, 0x66, 0xf8, 0x2a, 0x1a, 0x74, 0x7d, 0x45, 0x12, 0x0d, 0x17, 0x40, 0xf1,
1656 0x43, 0x58, 0x53, 0x1a, 0x8f, 0x04, 0xbb, 0xff, 0xe6, 0xa8, 0x19, 0xf8, 0x6d,
1657 0xfe, 0x50, 0xf4, 0x4a, 0x0a, 0x46,
1658 ]),
1659 ),
1660 (
1661 [
1662 0xf1, 0x16, 0xb3, 0x4b, 0x8f, 0x17, 0xce, 0xb5, 0x6e, 0x87, 0x32, 0xa6, 0x0d,
1663 0x91, 0x3d, 0xd1, 0x0c, 0xce, 0x47, 0xa6, 0xd5, 0x3b, 0xee, 0x92, 0x04, 0xbe,
1664 0x8b, 0x44, 0xf6, 0x67, 0x8b, 0x27, 0x01, 0x02, 0xa5, 0x69, 0x02, 0xe2, 0x48,
1665 0x8c, 0x46, 0x12, 0x0e, 0x92, 0x76, 0xcf, 0xe5, 0x46, 0x38, 0x28, 0x6b, 0x9e,
1666 0x4b, 0x3c, 0xdb, 0x47, 0x0b, 0x54, 0x2d, 0x46, 0xc2, 0x06, 0x8d, 0x38,
1667 ],
1668 CompressedRistretto([
1669 0xf2, 0x6e, 0x5b, 0x6f, 0x7d, 0x36, 0x2d, 0x2d, 0x2a, 0x94, 0xc5, 0xd0, 0xe7,
1670 0x60, 0x2c, 0xb4, 0x77, 0x3c, 0x95, 0xa2, 0xe5, 0xc3, 0x1a, 0x64, 0xf1, 0x33,
1671 0x18, 0x9f, 0xa7, 0x6e, 0xd6, 0x1b,
1672 ]),
1673 ),
1674 (
1675 [
1676 0x84, 0x22, 0xe1, 0xbb, 0xda, 0xab, 0x52, 0x93, 0x8b, 0x81, 0xfd, 0x60, 0x2e,
1677 0xff, 0xb6, 0xf8, 0x91, 0x10, 0xe1, 0xe5, 0x72, 0x08, 0xad, 0x12, 0xd9, 0xad,
1678 0x76, 0x7e, 0x2e, 0x25, 0x51, 0x0c, 0x27, 0x14, 0x07, 0x75, 0xf9, 0x33, 0x70,
1679 0x88, 0xb9, 0x82, 0xd8, 0x3d, 0x7f, 0xcf, 0x0b, 0x2f, 0xa1, 0xed, 0xff, 0xe5,
1680 0x19, 0x52, 0xcb, 0xe7, 0x36, 0x5e, 0x95, 0xc8, 0x6e, 0xaf, 0x32, 0x5c,
1681 ],
1682 CompressedRistretto([
1683 0x00, 0x6c, 0xcd, 0x2a, 0x9e, 0x68, 0x67, 0xe6, 0xa2, 0xc5, 0xce, 0xa8, 0x3d,
1684 0x33, 0x02, 0xcc, 0x9d, 0xe1, 0x28, 0xdd, 0x2a, 0x9a, 0x57, 0xdd, 0x8e, 0xe7,
1685 0xb9, 0xd7, 0xff, 0xe0, 0x28, 0x26,
1686 ]),
1687 ),
1688 (
1689 [
1690 0xac, 0x22, 0x41, 0x51, 0x29, 0xb6, 0x14, 0x27, 0xbf, 0x46, 0x4e, 0x17, 0xba,
1691 0xee, 0x8d, 0xb6, 0x59, 0x40, 0xc2, 0x33, 0xb9, 0x8a, 0xfc, 0xe8, 0xd1, 0x7c,
1692 0x57, 0xbe, 0xeb, 0x78, 0x76, 0xc2, 0x15, 0x0d, 0x15, 0xaf, 0x1c, 0xb1, 0xfb,
1693 0x82, 0x4b, 0xbd, 0x14, 0x95, 0x5f, 0x2b, 0x57, 0xd0, 0x8d, 0x38, 0x8a, 0xab,
1694 0x43, 0x1a, 0x39, 0x1c, 0xfc, 0x33, 0xd5, 0xba, 0xfb, 0x5d, 0xbb, 0xaf,
1695 ],
1696 CompressedRistretto([
1697 0xf8, 0xf0, 0xc8, 0x7c, 0xf2, 0x37, 0x95, 0x3c, 0x58, 0x90, 0xae, 0xc3, 0x99,
1698 0x81, 0x69, 0x00, 0x5d, 0xae, 0x3e, 0xca, 0x1f, 0xbb, 0x04, 0x54, 0x8c, 0x63,
1699 0x59, 0x53, 0xc8, 0x17, 0xf9, 0x2a,
1700 ]),
1701 ),
1702 (
1703 [
1704 0x16, 0x5d, 0x69, 0x7a, 0x1e, 0xf3, 0xd5, 0xcf, 0x3c, 0x38, 0x56, 0x5b, 0xee,
1705 0xfc, 0xf8, 0x8c, 0x0f, 0x28, 0x2b, 0x8e, 0x7d, 0xbd, 0x28, 0x54, 0x4c, 0x48,
1706 0x34, 0x32, 0xf1, 0xce, 0xc7, 0x67, 0x5d, 0xeb, 0xea, 0x8e, 0xbb, 0x4e, 0x5f,
1707 0xe7, 0xd6, 0xf6, 0xe5, 0xdb, 0x15, 0xf1, 0x55, 0x87, 0xac, 0x4d, 0x4d, 0x4a,
1708 0x1d, 0xe7, 0x19, 0x1e, 0x0c, 0x1c, 0xa6, 0x66, 0x4a, 0xbc, 0xc4, 0x13,
1709 ],
1710 CompressedRistretto([
1711 0xae, 0x81, 0xe7, 0xde, 0xdf, 0x20, 0xa4, 0x97, 0xe1, 0x0c, 0x30, 0x4a, 0x76,
1712 0x5c, 0x17, 0x67, 0xa4, 0x2d, 0x6e, 0x06, 0x02, 0x97, 0x58, 0xd2, 0xd7, 0xe8,
1713 0xef, 0x7c, 0xc4, 0xc4, 0x11, 0x79,
1714 ]),
1715 ),
1716 (
1717 [
1718 0xa8, 0x36, 0xe6, 0xc9, 0xa9, 0xca, 0x9f, 0x1e, 0x8d, 0x48, 0x62, 0x73, 0xad,
1719 0x56, 0xa7, 0x8c, 0x70, 0xcf, 0x18, 0xf0, 0xce, 0x10, 0xab, 0xb1, 0xc7, 0x17,
1720 0x2d, 0xdd, 0x60, 0x5d, 0x7f, 0xd2, 0x97, 0x98, 0x54, 0xf4, 0x7a, 0xe1, 0xcc,
1721 0xf2, 0x04, 0xa3, 0x31, 0x02, 0x09, 0x5b, 0x42, 0x00, 0xe5, 0xbe, 0xfc, 0x04,
1722 0x65, 0xac, 0xcc, 0x26, 0x31, 0x75, 0x48, 0x5f, 0x0e, 0x17, 0xea, 0x5c,
1723 ],
1724 CompressedRistretto([
1725 0xe2, 0x70, 0x56, 0x52, 0xff, 0x9f, 0x5e, 0x44, 0xd3, 0xe8, 0x41, 0xbf, 0x1c,
1726 0x25, 0x1c, 0xf7, 0xdd, 0xdb, 0x77, 0xd1, 0x40, 0x87, 0x0d, 0x1a, 0xb2, 0xed,
1727 0x64, 0xf1, 0xa9, 0xce, 0x86, 0x28,
1728 ]),
1729 ),
1730 (
1731 [
1732 0x2c, 0xdc, 0x11, 0xea, 0xeb, 0x95, 0xda, 0xf0, 0x11, 0x89, 0x41, 0x7c, 0xdd,
1733 0xdb, 0xf9, 0x59, 0x52, 0x99, 0x3a, 0xa9, 0xcb, 0x9c, 0x64, 0x0e, 0xb5, 0x05,
1734 0x8d, 0x09, 0x70, 0x2c, 0x74, 0x62, 0x2c, 0x99, 0x65, 0xa6, 0x97, 0xa3, 0xb3,
1735 0x45, 0xec, 0x24, 0xee, 0x56, 0x33, 0x5b, 0x55, 0x6e, 0x67, 0x7b, 0x30, 0xe6,
1736 0xf9, 0x0a, 0xc7, 0x7d, 0x78, 0x10, 0x64, 0xf8, 0x66, 0xa3, 0xc9, 0x82,
1737 ],
1738 CompressedRistretto([
1739 0x80, 0xbd, 0x07, 0x26, 0x25, 0x11, 0xcd, 0xde, 0x48, 0x63, 0xf8, 0xa7, 0x43,
1740 0x4c, 0xef, 0x69, 0x67, 0x50, 0x68, 0x1c, 0xb9, 0x51, 0x0e, 0xea, 0x55, 0x70,
1741 0x88, 0xf7, 0x6d, 0x9e, 0x50, 0x65,
1742 ]),
1743 ),
1744 (
1745 [
1746 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1747 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1748 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1749 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1750 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1751 ],
1752 CompressedRistretto([
1753 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1754 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1755 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1756 ]),
1757 ),
1758 (
1759 [
1760 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1761 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1762 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1763 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1764 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1765 ],
1766 CompressedRistretto([
1767 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1768 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1769 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1770 ]),
1771 ),
1772 (
1773 [
1774 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1775 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1776 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1777 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1778 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
1779 ],
1780 CompressedRistretto([
1781 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1782 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1783 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1784 ]),
1785 ),
1786 (
1787 [
1788 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1789 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1790 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1791 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1792 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
1793 ],
1794 CompressedRistretto([
1795 0x30, 0x42, 0x82, 0x79, 0x10, 0x23, 0xb7, 0x31, 0x28, 0xd2, 0x77, 0xbd, 0xcb,
1796 0x5c, 0x77, 0x46, 0xef, 0x2e, 0xac, 0x08, 0xdd, 0xe9, 0xf2, 0x98, 0x33, 0x79,
1797 0xcb, 0x8e, 0x5e, 0xf0, 0x51, 0x7f,
1798 ]),
1799 ),
1800 ];
1801 for (input, output) in test_vectors {
1803 let Q = RistrettoPoint::from_uniform_bytes(input);
1804 assert_eq!(&Q.compress(), output);
1805 }
1806 }
1807
1808 #[test]
1809 fn random_roundtrip() {
1810 let mut rng = OsRng;
1811 for _ in 0..100 {
1812 let P = RistrettoPoint::mul_base(&Scalar::random(&mut rng));
1813 let compressed_P = P.compress();
1814 let Q = compressed_P.decompress().unwrap();
1815 assert_eq!(P, Q);
1816 }
1817 }
1818
1819 #[test]
1820 #[cfg(all(feature = "alloc", feature = "rand_core"))]
1821 fn double_and_compress_1024_random_points() {
1822 let mut rng = OsRng;
1823
1824 let mut points: Vec<RistrettoPoint> = (0..1024)
1825 .map(|_| RistrettoPoint::random(&mut rng))
1826 .collect();
1827 points[500] = RistrettoPoint::identity();
1828
1829 let compressed = RistrettoPoint::double_and_compress_batch(&points);
1830
1831 for (P, P2_compressed) in points.iter().zip(compressed.iter()) {
1832 assert_eq!(*P2_compressed, (P + P).compress());
1833 }
1834 }
1835
1836 #[test]
1837 #[cfg(feature = "alloc")]
1838 fn vartime_precomputed_vs_nonprecomputed_multiscalar() {
1839 let mut rng = rand::thread_rng();
1840
1841 let static_scalars = (0..128)
1842 .map(|_| Scalar::random(&mut rng))
1843 .collect::<Vec<_>>();
1844
1845 let dynamic_scalars = (0..128)
1846 .map(|_| Scalar::random(&mut rng))
1847 .collect::<Vec<_>>();
1848
1849 let check_scalar: Scalar = static_scalars
1850 .iter()
1851 .chain(dynamic_scalars.iter())
1852 .map(|s| s * s)
1853 .sum();
1854
1855 let static_points = static_scalars
1856 .iter()
1857 .map(RistrettoPoint::mul_base)
1858 .collect::<Vec<_>>();
1859 let dynamic_points = dynamic_scalars
1860 .iter()
1861 .map(RistrettoPoint::mul_base)
1862 .collect::<Vec<_>>();
1863
1864 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
1865
1866 assert_eq!(precomputation.len(), 128);
1867 assert!(!precomputation.is_empty());
1868
1869 let P = precomputation.vartime_mixed_multiscalar_mul(
1870 &static_scalars,
1871 &dynamic_scalars,
1872 &dynamic_points,
1873 );
1874
1875 use crate::traits::VartimeMultiscalarMul;
1876 let Q = RistrettoPoint::vartime_multiscalar_mul(
1877 static_scalars.iter().chain(dynamic_scalars.iter()),
1878 static_points.iter().chain(dynamic_points.iter()),
1879 );
1880
1881 let R = RistrettoPoint::mul_base(&check_scalar);
1882
1883 assert_eq!(P.compress(), R.compress());
1884 assert_eq!(Q.compress(), R.compress());
1885 }
1886
1887 #[test]
1888 #[cfg(feature = "alloc")]
1889 fn partial_precomputed_mixed_multiscalar_empty() {
1890 let mut rng = rand::thread_rng();
1891
1892 let n_static = 16;
1893 let n_dynamic = 8;
1894
1895 let static_points = (0..n_static)
1896 .map(|_| RistrettoPoint::random(&mut rng))
1897 .collect::<Vec<_>>();
1898
1899 let static_scalars = Vec::new();
1901
1902 let dynamic_points = (0..n_dynamic)
1903 .map(|_| RistrettoPoint::random(&mut rng))
1904 .collect::<Vec<_>>();
1905
1906 let dynamic_scalars = (0..n_dynamic)
1907 .map(|_| Scalar::random(&mut rng))
1908 .collect::<Vec<_>>();
1909
1910 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
1912 let result_multiscalar = precomputation.vartime_mixed_multiscalar_mul(
1913 &static_scalars,
1914 &dynamic_scalars,
1915 &dynamic_points,
1916 );
1917
1918 let mut result_manual = RistrettoPoint::identity();
1920 for i in 0..static_scalars.len() {
1921 result_manual += static_points[i] * static_scalars[i];
1922 }
1923 for i in 0..n_dynamic {
1924 result_manual += dynamic_points[i] * dynamic_scalars[i];
1925 }
1926
1927 assert_eq!(result_multiscalar, result_manual);
1928 }
1929
1930 #[test]
1931 #[cfg(feature = "alloc")]
1932 fn partial_precomputed_mixed_multiscalar() {
1933 let mut rng = rand::thread_rng();
1934
1935 let n_static = 16;
1936 let n_dynamic = 8;
1937
1938 let static_points = (0..n_static)
1939 .map(|_| RistrettoPoint::random(&mut rng))
1940 .collect::<Vec<_>>();
1941
1942 let static_scalars = (0..n_static - 1)
1944 .map(|_| Scalar::random(&mut rng))
1945 .collect::<Vec<_>>();
1946
1947 let dynamic_points = (0..n_dynamic)
1948 .map(|_| RistrettoPoint::random(&mut rng))
1949 .collect::<Vec<_>>();
1950
1951 let dynamic_scalars = (0..n_dynamic)
1952 .map(|_| Scalar::random(&mut rng))
1953 .collect::<Vec<_>>();
1954
1955 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
1957 let result_multiscalar = precomputation.vartime_mixed_multiscalar_mul(
1958 &static_scalars,
1959 &dynamic_scalars,
1960 &dynamic_points,
1961 );
1962
1963 let mut result_manual = RistrettoPoint::identity();
1965 for i in 0..static_scalars.len() {
1966 result_manual += static_points[i] * static_scalars[i];
1967 }
1968 for i in 0..n_dynamic {
1969 result_manual += dynamic_points[i] * dynamic_scalars[i];
1970 }
1971
1972 assert_eq!(result_multiscalar, result_manual);
1973 }
1974
1975 #[test]
1976 #[cfg(feature = "alloc")]
1977 fn partial_precomputed_multiscalar() {
1978 let mut rng = rand::thread_rng();
1979
1980 let n_static = 16;
1981
1982 let static_points = (0..n_static)
1983 .map(|_| RistrettoPoint::random(&mut rng))
1984 .collect::<Vec<_>>();
1985
1986 let static_scalars = (0..n_static - 1)
1988 .map(|_| Scalar::random(&mut rng))
1989 .collect::<Vec<_>>();
1990
1991 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
1993 let result_multiscalar = precomputation.vartime_multiscalar_mul(&static_scalars);
1994
1995 let mut result_manual = RistrettoPoint::identity();
1997 for i in 0..static_scalars.len() {
1998 result_manual += static_points[i] * static_scalars[i];
1999 }
2000
2001 assert_eq!(result_multiscalar, result_manual);
2002 }
2003
2004 #[test]
2005 #[cfg(feature = "alloc")]
2006 fn partial_precomputed_multiscalar_empty() {
2007 let mut rng = rand::thread_rng();
2008
2009 let n_static = 16;
2010
2011 let static_points = (0..n_static)
2012 .map(|_| RistrettoPoint::random(&mut rng))
2013 .collect::<Vec<_>>();
2014
2015 let static_scalars = Vec::new();
2017
2018 let precomputation = VartimeRistrettoPrecomputation::new(static_points.iter());
2020 let result_multiscalar = precomputation.vartime_multiscalar_mul(&static_scalars);
2021
2022 let mut result_manual = RistrettoPoint::identity();
2024 for i in 0..static_scalars.len() {
2025 result_manual += static_points[i] * static_scalars[i];
2026 }
2027
2028 assert_eq!(result_multiscalar, result_manual);
2029 }
2030}