Skip to main content

ed448_goldilocks/decaf/
points.rs

1use crate::curve::twedwards::extended::ExtendedPoint;
2use crate::field::FieldElement;
3use crate::*;
4
5use core::fmt::{Display, Formatter, LowerHex, Result as FmtResult, UpperHex};
6use elliptic_curve::{
7    CurveGroup, Error, Generate, Group,
8    array::Array,
9    consts::U56,
10    ctutils,
11    group::{GroupEncoding, cofactor::CofactorGroup, prime::PrimeGroup},
12    ops::LinearCombination,
13    point::NonIdentity,
14};
15use rand_core::{CryptoRng, TryCryptoRng, TryRng};
16use subtle::{Choice, ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption};
17
18/// The bytes representation of a compressed point
19pub type DecafPointBytes = [u8; 56];
20/// The group bytes representation
21pub type DecafPointRepr = Array<u8, U56>;
22
23/// A Decaf point in the Twisted Edwards curve
24#[derive(Copy, Clone, Debug)]
25pub struct DecafPoint(pub(crate) ExtendedPoint);
26
27impl DecafPoint {
28    /// The generator point
29    pub const GENERATOR: DecafPoint = DecafPoint(TWISTED_EDWARDS_BASE_POINT);
30    /// The identity point
31    pub const IDENTITY: DecafPoint = DecafPoint(ExtendedPoint::IDENTITY);
32
33    /// Check if the point is the identity
34    pub fn is_identity(&self) -> Choice {
35        self.ct_eq(&DecafPoint::IDENTITY)
36    }
37
38    /// Add two points
39    pub fn add(&self, other: &DecafPoint) -> DecafPoint {
40        DecafPoint(self.0.add_extended(&other.0).to_extended())
41    }
42
43    /// Subtract two points
44    pub fn sub(&self, other: &DecafPoint) -> DecafPoint {
45        DecafPoint(self.0.sub_extended(&other.0).to_extended())
46    }
47
48    /// Compress this point
49    pub fn compress(&self) -> CompressedDecaf {
50        let X = self.0.X;
51        // let Y = self.0.Y;
52        let Z = self.0.Z;
53        let T = self.0.T;
54
55        let XX_TT = (X + T) * (X - T);
56
57        let (isr, _) = (X.square() * XX_TT * FieldElement::NEG_EDWARDS_D).inverse_square_root();
58        let mut ratio = isr * XX_TT;
59        let altx = ratio * FieldElement::DECAF_FACTOR; // Sign choice
60        ratio.conditional_negate(altx.is_negative());
61        let k = ratio * Z - T;
62
63        let mut s = k * FieldElement::NEG_EDWARDS_D * isr * X;
64        s.conditional_negate(s.is_negative());
65
66        CompressedDecaf(s.to_bytes())
67    }
68
69    /// Construct a `DecafPoint` from 112 bytes of data.
70    ///
71    /// If the input bytes are uniformly distributed, the resulting
72    /// point will be uniformly distributed over the group, and its
73    /// discrete log with respect to other points is unknown.
74    ///
75    /// Implements map to curve according
76    /// see <https://datatracker.ietf.org/doc/rfc9380/>
77    /// section 5.3.4 by splitting the input into two 56-byte halves,
78    /// then applies the decaf448_map to each, and adds the results.
79    pub fn from_uniform_bytes(bytes: &[u8; 112]) -> Self {
80        let lo: [u8; 56] = (&bytes[..56])
81            .try_into()
82            .expect("how does the slice have an incorrect length");
83        let hi: [u8; 56] = (&bytes[56..])
84            .try_into()
85            .expect("how does the slice have an incorrect length");
86
87        let u0 = FieldElement::from_bytes(&lo);
88        let u1 = FieldElement::from_bytes(&hi);
89        let q0 = u0.map_to_curve_decaf448();
90        let q1 = u1.map_to_curve_decaf448();
91        Self(q0.add_extended(&q1).to_extended())
92    }
93
94    /// DEPRECATED: Return a `DecafPoint` chosen uniformly at random using a user-provided RNG.
95    ///
96    /// Use the [`Generate`] trait instead.
97    #[deprecated(since = "0.14.0", note = "use the `Generate` trait instead")]
98    pub fn random(mut rng: impl CryptoRng) -> Self {
99        let mut uniform_bytes = [0u8; 112];
100        rng.fill_bytes(&mut uniform_bytes);
101        Self::from_uniform_bytes(&uniform_bytes)
102    }
103}
104
105impl Default for DecafPoint {
106    fn default() -> Self {
107        Self::IDENTITY
108    }
109}
110
111impl Display for DecafPoint {
112    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
113        write!(
114            f,
115            "{{ X: {}, Y: {}, Z: {}, T: {} }}",
116            self.0.X, self.0.Y, self.0.Z, self.0.T
117        )
118    }
119}
120
121impl LowerHex for DecafPoint {
122    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
123        write!(
124            f,
125            "{{ X: {:x}, Y: {:x}, Z: {:x}, T: {:x} }}",
126            self.0.X, self.0.Y, self.0.Z, self.0.T
127        )
128    }
129}
130
131impl UpperHex for DecafPoint {
132    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
133        write!(
134            f,
135            "{{ X: {:X}, Y: {:X}, Z: {:X}, T: {:X} }}",
136            self.0.X, self.0.Y, self.0.Z, self.0.T
137        )
138    }
139}
140
141impl ConstantTimeEq for DecafPoint {
142    fn ct_eq(&self, other: &DecafPoint) -> Choice {
143        (self.0.X * other.0.Y).ct_eq(&(self.0.Y * other.0.X))
144    }
145}
146
147impl ConditionallySelectable for DecafPoint {
148    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
149        DecafPoint(ExtendedPoint {
150            X: FieldElement::conditional_select(&a.0.X, &b.0.X, choice),
151            Y: FieldElement::conditional_select(&a.0.Y, &b.0.Y, choice),
152            Z: FieldElement::conditional_select(&a.0.Z, &b.0.Z, choice),
153            T: FieldElement::conditional_select(&a.0.T, &b.0.T, choice),
154        })
155    }
156}
157
158impl ctutils::CtEq for DecafPoint {
159    fn ct_eq(&self, other: &Self) -> ctutils::Choice {
160        ConstantTimeEq::ct_eq(self, other).into()
161    }
162}
163
164impl ctutils::CtSelect for DecafPoint {
165    fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self {
166        ConditionallySelectable::conditional_select(self, other, choice.into())
167    }
168}
169
170impl Eq for DecafPoint {}
171impl PartialEq for DecafPoint {
172    fn eq(&self, other: &DecafPoint) -> bool {
173        self.ct_eq(other).into()
174    }
175}
176
177impl From<DecafPoint> for DecafPointBytes {
178    fn from(point: DecafPoint) -> DecafPointBytes {
179        point.compress().0
180    }
181}
182
183impl From<&DecafPoint> for DecafPointBytes {
184    fn from(compressed: &DecafPoint) -> DecafPointBytes {
185        Self::from(*compressed)
186    }
187}
188
189#[cfg(feature = "alloc")]
190impl From<DecafPoint> for Vec<u8> {
191    fn from(compressed: DecafPoint) -> Vec<u8> {
192        Self::from(&compressed)
193    }
194}
195
196#[cfg(feature = "alloc")]
197impl From<&DecafPoint> for Vec<u8> {
198    fn from(point: &DecafPoint) -> Vec<u8> {
199        point.compress().0.to_vec()
200    }
201}
202
203#[cfg(feature = "alloc")]
204impl TryFrom<Vec<u8>> for DecafPoint {
205    type Error = &'static str;
206
207    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
208        Self::try_from(bytes.as_slice())
209    }
210}
211
212#[cfg(feature = "alloc")]
213impl TryFrom<&Vec<u8>> for DecafPoint {
214    type Error = &'static str;
215
216    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
217        Self::try_from(bytes.as_slice())
218    }
219}
220
221#[cfg(feature = "alloc")]
222impl TryFrom<&[u8]> for DecafPoint {
223    type Error = &'static str;
224
225    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
226        let compressed =
227            <DecafPointBytes>::try_from(bytes).map_err(|_| "bytes is not the correct length")?;
228        Self::try_from(compressed)
229    }
230}
231
232#[cfg(feature = "alloc")]
233impl TryFrom<Box<[u8]>> for DecafPoint {
234    type Error = &'static str;
235
236    fn try_from(bytes: Box<[u8]>) -> Result<Self, Self::Error> {
237        Self::try_from(bytes.as_ref())
238    }
239}
240
241impl TryFrom<DecafPointBytes> for DecafPoint {
242    type Error = &'static str;
243
244    fn try_from(bytes: DecafPointBytes) -> Result<Self, Self::Error> {
245        let pt = CompressedDecaf(bytes);
246        Option::<DecafPoint>::from(pt.decompress()).ok_or("Invalid point encoding")
247    }
248}
249
250impl TryFrom<&DecafPointBytes> for DecafPoint {
251    type Error = &'static str;
252
253    fn try_from(bytes: &DecafPointBytes) -> Result<Self, Self::Error> {
254        Self::try_from(*bytes)
255    }
256}
257
258impl Generate for DecafPoint {
259    fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(rng: &mut R) -> Result<Self, R::Error> {
260        let mut uniform_bytes = [0u8; 112];
261        rng.try_fill_bytes(&mut uniform_bytes)?;
262        Ok(Self::from_uniform_bytes(&uniform_bytes))
263    }
264}
265
266impl Group for DecafPoint {
267    type Scalar = DecafScalar;
268
269    fn try_random<R>(rng: &mut R) -> Result<Self, R::Error>
270    where
271        R: TryRng + ?Sized,
272    {
273        let mut bytes = DecafPointRepr::default();
274
275        loop {
276            rng.try_fill_bytes(&mut bytes)?;
277            if let Some(point) = Self::from_bytes(&bytes)
278                .into_option()
279                .filter(|&point| point != Self::IDENTITY)
280            {
281                return Ok(point);
282            }
283        }
284    }
285
286    fn identity() -> Self {
287        Self::IDENTITY
288    }
289
290    fn generator() -> Self {
291        Self::GENERATOR
292    }
293
294    fn is_identity(&self) -> Choice {
295        self.ct_eq(&Self::IDENTITY)
296    }
297
298    fn double(&self) -> Self {
299        Self(self.0.to_extensible().double().to_extended())
300    }
301}
302
303impl GroupEncoding for DecafPoint {
304    type Repr = DecafPointRepr;
305
306    fn from_bytes(bytes: &Self::Repr) -> CtOption<Self> {
307        let pt = CompressedDecaf(*(bytes.as_ref()));
308        pt.decompress()
309    }
310
311    fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption<Self> {
312        let pt = CompressedDecaf(*(bytes.as_ref()));
313        pt.decompress()
314    }
315
316    fn to_bytes(&self) -> Self::Repr {
317        DecafPointRepr::from(self.compress().0)
318    }
319}
320
321impl CofactorGroup for DecafPoint {
322    type Subgroup = DecafPoint;
323
324    fn clear_cofactor(&self) -> Self::Subgroup {
325        *self
326    }
327
328    fn into_subgroup(self) -> CtOption<Self::Subgroup> {
329        CtOption::new(self, Choice::from(1))
330    }
331
332    fn is_torsion_free(&self) -> Choice {
333        Choice::from(1)
334    }
335}
336
337impl PrimeGroup for DecafPoint {}
338
339impl<const N: usize> LinearCombination<[(DecafPoint, DecafScalar); N]> for DecafPoint {}
340
341impl LinearCombination<[(DecafPoint, DecafScalar)]> for DecafPoint {}
342
343impl CurveGroup for DecafPoint {
344    type Affine = DecafAffinePoint;
345
346    fn to_affine(&self) -> Self::Affine {
347        DecafAffinePoint(self.0.to_extensible().to_affine())
348    }
349}
350
351impl From<EdwardsPoint> for DecafPoint {
352    fn from(point: EdwardsPoint) -> Self {
353        Self(point.to_twisted().to_extended())
354    }
355}
356
357impl From<&EdwardsPoint> for DecafPoint {
358    fn from(point: &EdwardsPoint) -> Self {
359        Self(point.to_twisted().to_extended())
360    }
361}
362
363impl From<DecafPoint> for EdwardsPoint {
364    fn from(point: DecafPoint) -> Self {
365        point.0.to_untwisted()
366    }
367}
368
369impl From<&DecafPoint> for EdwardsPoint {
370    fn from(point: &DecafPoint) -> Self {
371        point.0.to_untwisted()
372    }
373}
374
375impl From<DecafAffinePoint> for DecafPoint {
376    fn from(point: DecafAffinePoint) -> Self {
377        Self(point.0.to_extended())
378    }
379}
380
381impl From<&DecafAffinePoint> for DecafPoint {
382    fn from(point: &DecafAffinePoint) -> Self {
383        Self(point.0.to_extended())
384    }
385}
386
387impl From<DecafPoint> for DecafAffinePoint {
388    fn from(point: DecafPoint) -> Self {
389        DecafAffinePoint(point.0.to_extensible().to_affine())
390    }
391}
392
393impl From<&DecafPoint> for DecafAffinePoint {
394    fn from(point: &DecafPoint) -> Self {
395        DecafAffinePoint(point.0.to_extensible().to_affine())
396    }
397}
398
399impl elliptic_curve::zeroize::DefaultIsZeroes for DecafPoint {}
400
401/// A compressed decaf point
402#[derive(Copy, Clone, Debug)]
403#[repr(transparent)]
404pub struct CompressedDecaf(pub DecafPointBytes);
405
406impl Default for CompressedDecaf {
407    fn default() -> CompressedDecaf {
408        Self::IDENTITY
409    }
410}
411
412impl ConstantTimeEq for CompressedDecaf {
413    fn ct_eq(&self, other: &CompressedDecaf) -> Choice {
414        self.as_bytes().ct_eq(other.as_bytes())
415    }
416}
417
418impl ConditionallySelectable for CompressedDecaf {
419    fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
420        let mut bytes = [0u8; 56];
421        for (i, byte) in bytes.iter_mut().enumerate() {
422            *byte = u8::conditional_select(&a.0[i], &b.0[i], choice);
423        }
424        Self(bytes)
425    }
426}
427
428impl PartialEq for CompressedDecaf {
429    fn eq(&self, other: &CompressedDecaf) -> bool {
430        self.ct_eq(other).into()
431    }
432}
433
434impl Eq for CompressedDecaf {}
435
436impl From<CompressedDecaf> for DecafPointBytes {
437    fn from(compressed: CompressedDecaf) -> DecafPointBytes {
438        compressed.0
439    }
440}
441
442impl From<&CompressedDecaf> for DecafPointBytes {
443    fn from(compressed: &CompressedDecaf) -> DecafPointBytes {
444        Self::from(*compressed)
445    }
446}
447
448#[cfg(feature = "alloc")]
449impl From<CompressedDecaf> for Vec<u8> {
450    fn from(compressed: CompressedDecaf) -> Vec<u8> {
451        Self::from(&compressed)
452    }
453}
454
455#[cfg(feature = "alloc")]
456impl From<&CompressedDecaf> for Vec<u8> {
457    fn from(compressed: &CompressedDecaf) -> Vec<u8> {
458        compressed.0.to_vec()
459    }
460}
461
462#[cfg(feature = "alloc")]
463impl TryFrom<Vec<u8>> for CompressedDecaf {
464    type Error = &'static str;
465
466    fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
467        Self::try_from(bytes.as_slice())
468    }
469}
470
471#[cfg(feature = "alloc")]
472impl TryFrom<&Vec<u8>> for CompressedDecaf {
473    type Error = &'static str;
474
475    fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
476        Self::try_from(bytes.as_slice())
477    }
478}
479
480#[cfg(feature = "alloc")]
481impl TryFrom<&[u8]> for CompressedDecaf {
482    type Error = &'static str;
483
484    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
485        let compressed = <DecafPointBytes>::try_from(bytes).map_err(|_| "invalid length")?;
486        Self::try_from(compressed)
487    }
488}
489
490#[cfg(feature = "alloc")]
491impl TryFrom<Box<[u8]>> for CompressedDecaf {
492    type Error = &'static str;
493
494    fn try_from(bytes: Box<[u8]>) -> Result<Self, Self::Error> {
495        Self::try_from(bytes.as_ref())
496    }
497}
498
499impl TryFrom<DecafPointBytes> for CompressedDecaf {
500    type Error = &'static str;
501
502    fn try_from(bytes: DecafPointBytes) -> Result<Self, Self::Error> {
503        let pt = CompressedDecaf(bytes);
504        let _ = Option::<DecafPoint>::from(pt.decompress()).ok_or("Invalid point encoding")?;
505        Ok(pt)
506    }
507}
508
509impl TryFrom<&DecafPointBytes> for CompressedDecaf {
510    type Error = &'static str;
511
512    fn try_from(bytes: &DecafPointBytes) -> Result<Self, Self::Error> {
513        Self::try_from(*bytes)
514    }
515}
516
517impl AsRef<DecafPointBytes> for CompressedDecaf {
518    fn as_ref(&self) -> &DecafPointBytes {
519        &self.0
520    }
521}
522
523#[cfg(feature = "serde")]
524impl serdect::serde::Serialize for CompressedDecaf {
525    fn serialize<S: serdect::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
526        serdect::slice::serialize_hex_lower_or_bin(&self.0, s)
527    }
528}
529
530#[cfg(feature = "serde")]
531impl<'de> serdect::serde::Deserialize<'de> for CompressedDecaf {
532    fn deserialize<D>(d: D) -> Result<Self, D::Error>
533    where
534        D: serdect::serde::Deserializer<'de>,
535    {
536        let mut bytes = [0u8; 56];
537        serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
538        Self::try_from(bytes).map_err(serdect::serde::de::Error::custom)
539    }
540}
541
542impl elliptic_curve::zeroize::DefaultIsZeroes for CompressedDecaf {}
543
544impl CompressedDecaf {
545    /// The compressed generator point
546    pub const GENERATOR: Self = Self([
547        102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
548        102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
549        51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
550    ]);
551    /// The compressed identity point
552    pub const IDENTITY: Self = Self([0u8; 56]);
553
554    /// Decompress a point if it is valid
555    pub fn decompress(&self) -> CtOption<DecafPoint> {
556        let s = FieldElement::from_bytes(&self.0);
557        //XX: Check for canonical encoding and sign,
558        // Copied this check from Dalek: The From_bytes function does not throw an error, if the bytes exceed the prime.
559        // However, to_bytes reduces the Field element before serialising
560        // So we can use to_bytes -> from_bytes and if the representations are the same, then the element was already in reduced form
561        let s_bytes_check = s.to_bytes();
562        let s_encoding_is_canonical = s_bytes_check[..].ct_eq(&self.0);
563        let s_is_negative = s.is_negative();
564        // if s_encoding_is_canonical.unwrap_u8() == 0u8 || s.is_negative().unwrap_u8() == 1u8 {
565        //     return None;
566        // }
567
568        let ss = s.square();
569        let u1 = FieldElement::ONE - ss;
570        let u2 = FieldElement::ONE + ss;
571        let u1_sqr = u1.square();
572
573        let v = ss * (FieldElement::NEG_FOUR_TIMES_TWISTED_D) + u1_sqr; // XXX: constantify please
574
575        let (I, ok) = (v * u1_sqr).inverse_square_root();
576
577        let Dx = I * u1;
578        let Dxs = s.double() * Dx;
579
580        let mut X = (Dxs * I) * v;
581        let k = Dxs * FieldElement::DECAF_FACTOR;
582        X.conditional_negate(k.is_negative());
583
584        let Y = Dx * u2;
585        let Z = FieldElement::ONE;
586        let T = X * Y;
587        let pt = ExtendedPoint { X, Y, Z, T };
588
589        CtOption::new(
590            DecafPoint(pt),
591            ok & pt.is_on_curve() & s_encoding_is_canonical & !s_is_negative,
592        )
593    }
594
595    /// Get the bytes of this compressed point
596    pub fn as_bytes(&self) -> &[u8] {
597        &self.0
598    }
599}
600
601/// The constant-time alternative is available at [`NonIdentity::new()`].
602impl TryFrom<DecafPoint> for NonIdentity<DecafPoint> {
603    type Error = Error;
604
605    fn try_from(point: DecafPoint) -> Result<Self, Error> {
606        NonIdentity::new(point).into_option().ok_or(Error)
607    }
608}
609
610impl From<NonIdentity<DecafPoint>> for DecafPoint {
611    fn from(decaf: NonIdentity<DecafPoint>) -> Self {
612        decaf.to_point()
613    }
614}
615
616#[cfg(test)]
617mod test {
618    use super::*;
619    use crate::TWISTED_EDWARDS_BASE_POINT;
620    use hash2curve::ExpandMsgXof;
621    use shake::Shake256;
622
623    #[test]
624    fn test_edwards_decaf_operations() {
625        // Basic test that if P1 + P2 = P3
626        // Then Decaf(P1) + Decaf(P2) = Decaf(P3)
627
628        let P = TWISTED_EDWARDS_BASE_POINT;
629
630        let P2 = P.to_extensible().double().to_extended();
631        let P3 = P2.add_extended(&P).to_extended();
632
633        // Encode and decode to make them Decaf points
634        let Decaf_P = DecafPoint(P).compress().decompress().unwrap();
635        let Decaf_P2 = DecafPoint(P2).compress().decompress().unwrap();
636        let expected_Decaf_P3 = DecafPoint(P3).compress().decompress().unwrap();
637
638        // Adding the DecafPoint should be the same as adding the Edwards points and encoding the result as Decaf
639        let Decaf_P3 = Decaf_P + Decaf_P2;
640
641        assert_eq!(Decaf_P3, expected_Decaf_P3);
642    }
643
644    #[test]
645    fn test_identity() {
646        // Basic test to check the identity is being encoded properly
647        let compress_identity = DecafPoint::IDENTITY.compress();
648        assert!(compress_identity == CompressedDecaf::IDENTITY)
649    }
650
651    #[test]
652    fn test_vectors_lib_decaf() {
653        // Testing small multiples of basepoint. Taken from reference implementation.
654        let compressed = [
655            // Taken from libdecaf, where they were computed using SAGE script
656            CompressedDecaf([
657                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,
658                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,
659            ]),
660            CompressedDecaf([
661                102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102,
662                102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 102, 51, 51, 51, 51, 51, 51,
663                51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
664                51,
665            ]),
666            CompressedDecaf([
667                200, 152, 235, 79, 135, 249, 124, 86, 76, 111, 214, 31, 199, 228, 150, 137, 49, 74,
668                31, 129, 142, 200, 94, 235, 59, 213, 81, 74, 200, 22, 211, 135, 120, 246, 158, 243,
669                71, 168, 159, 202, 129, 126, 102, 222, 253, 237, 206, 23, 140, 124, 199, 9, 178,
670                17, 110, 117,
671            ]),
672            CompressedDecaf([
673                160, 192, 155, 242, 186, 114, 8, 253, 160, 244, 191, 227, 208, 245, 178, 154, 84,
674                48, 18, 48, 109, 67, 131, 27, 90, 220, 111, 231, 248, 89, 111, 163, 8, 118, 61,
675                177, 84, 104, 50, 59, 17, 207, 110, 74, 235, 140, 24, 254, 68, 103, 143, 68, 84,
676                90, 105, 188,
677            ]),
678            CompressedDecaf([
679                180, 111, 24, 54, 170, 40, 124, 10, 90, 86, 83, 240, 236, 94, 249, 233, 3, 244, 54,
680                226, 28, 21, 112, 194, 154, 217, 229, 245, 150, 218, 151, 238, 175, 23, 21, 10,
681                227, 11, 203, 49, 116, 208, 75, 194, 215, 18, 200, 199, 120, 157, 124, 180, 253,
682                161, 56, 244,
683            ]),
684            CompressedDecaf([
685                28, 91, 190, 207, 71, 65, 223, 170, 231, 157, 183, 45, 250, 206, 0, 234, 170, 197,
686                2, 194, 6, 9, 52, 182, 234, 174, 202, 106, 32, 189, 61, 169, 224, 190, 135, 119,
687                247, 208, 32, 51, 209, 177, 88, 132, 35, 34, 129, 164, 31, 199, 248, 14, 237, 4,
688                175, 94,
689            ]),
690            CompressedDecaf([
691                134, 255, 1, 130, 212, 15, 127, 158, 219, 120, 98, 81, 88, 33, 189, 103, 191, 214,
692                22, 90, 60, 68, 222, 149, 215, 223, 121, 184, 119, 156, 207, 100, 96, 227, 198,
693                139, 112, 193, 106, 170, 40, 15, 45, 123, 63, 34, 215, 69, 185, 122, 137, 144, 108,
694                252, 71, 108,
695            ]),
696            CompressedDecaf([
697                80, 43, 203, 104, 66, 235, 6, 240, 228, 144, 50, 186, 232, 124, 85, 76, 3, 29, 109,
698                77, 45, 118, 148, 239, 191, 156, 70, 141, 72, 34, 12, 80, 248, 202, 40, 132, 51,
699                100, 215, 12, 238, 146, 214, 254, 36, 110, 97, 68, 143, 157, 185, 128, 139, 59, 36,
700                8,
701            ]),
702            CompressedDecaf([
703                12, 152, 16, 241, 226, 235, 211, 137, 202, 167, 137, 55, 77, 120, 0, 121, 116, 239,
704                77, 23, 34, 115, 22, 244, 14, 87, 139, 51, 104, 39, 218, 63, 107, 72, 42, 71, 148,
705                235, 106, 57, 117, 185, 113, 181, 225, 56, 143, 82, 233, 30, 162, 241, 188, 176,
706                249, 18,
707            ]),
708            CompressedDecaf([
709                32, 212, 29, 133, 161, 141, 86, 87, 162, 150, 64, 50, 21, 99, 187, 208, 76, 47,
710                251, 208, 163, 122, 123, 164, 58, 79, 125, 38, 60, 226, 111, 175, 78, 31, 116, 249,
711                244, 181, 144, 198, 146, 41, 174, 87, 31, 227, 127, 166, 57, 181, 184, 235, 72,
712                189, 154, 85,
713            ]),
714            CompressedDecaf([
715                230, 180, 184, 244, 8, 199, 1, 13, 6, 1, 231, 237, 160, 195, 9, 161, 164, 39, 32,
716                214, 208, 107, 87, 89, 253, 196, 225, 239, 226, 45, 7, 109, 108, 68, 212, 47, 80,
717                141, 103, 190, 70, 41, 20, 210, 139, 142, 220, 227, 46, 112, 148, 48, 81, 100, 175,
718                23,
719            ]),
720            CompressedDecaf([
721                190, 136, 187, 184, 108, 89, 193, 61, 142, 157, 9, 171, 152, 16, 95, 105, 194, 209,
722                221, 19, 77, 188, 211, 176, 134, 54, 88, 245, 49, 89, 219, 100, 192, 225, 57, 209,
723                128, 243, 200, 155, 130, 150, 208, 174, 50, 68, 25, 192, 111, 168, 127, 199, 218,
724                175, 52, 193,
725            ]),
726            CompressedDecaf([
727                164, 86, 249, 54, 151, 105, 232, 240, 137, 2, 18, 74, 3, 20, 199, 160, 101, 55,
728                160, 110, 50, 65, 31, 79, 147, 65, 89, 80, 161, 123, 173, 250, 116, 66, 182, 33,
729                116, 52, 163, 160, 94, 244, 91, 229, 241, 11, 215, 178, 239, 142, 160, 12, 67, 30,
730                222, 197,
731            ]),
732            CompressedDecaf([
733                24, 110, 69, 44, 68, 102, 170, 67, 131, 180, 192, 2, 16, 213, 46, 121, 34, 219,
734                249, 119, 30, 139, 71, 226, 41, 169, 183, 183, 60, 141, 16, 253, 126, 240, 182,
735                228, 21, 48, 249, 31, 36, 163, 237, 154, 183, 31, 163, 139, 152, 178, 254, 71, 70,
736                213, 29, 104,
737            ]),
738            CompressedDecaf([
739                74, 231, 253, 202, 233, 69, 63, 25, 90, 142, 173, 92, 190, 26, 123, 150, 153, 103,
740                59, 82, 196, 10, 178, 121, 39, 70, 72, 135, 190, 83, 35, 127, 127, 58, 33, 185, 56,
741                212, 13, 14, 201, 225, 91, 29, 81, 48, 177, 63, 254, 216, 19, 115, 165, 62, 43, 67,
742            ]),
743            CompressedDecaf([
744                132, 25, 129, 195, 191, 238, 195, 246, 12, 254, 202, 117, 217, 216, 220, 23, 244,
745                108, 240, 16, 111, 36, 34, 181, 154, 236, 88, 10, 88, 243, 66, 39, 46, 58, 94, 87,
746                90, 5, 93, 219, 5, 19, 144, 197, 76, 36, 198, 236, 177, 224, 172, 235, 7, 95, 96,
747                86,
748            ]),
749        ];
750        let mut point = DecafPoint::IDENTITY;
751        let generator = DecafPoint::GENERATOR;
752        for compressed_point in compressed.iter() {
753            assert_eq!(&point.compress(), compressed_point);
754            point += generator;
755            let decompressed_point = compressed_point.decompress();
756            assert_eq!(decompressed_point.is_some().unwrap_u8(), 1u8);
757        }
758    }
759
760    #[test]
761    fn test_invalid_point() {
762        // Test that the identity point is not on the curve
763        let all_ones = CompressedDecaf([1u8; 56]);
764        assert_eq!(all_ones.decompress().is_none().unwrap_u8(), 1u8);
765        let all_twos = CompressedDecaf([2u8; 56]);
766        assert_eq!(all_twos.decompress().is_none().unwrap_u8(), 1u8);
767    }
768
769    #[test]
770    fn test_hash_to_curve() {
771        let msg = b"Hello, world!";
772        let point = hash2curve::hash_from_bytes::<Decaf448, ExpandMsgXof<Shake256>>(
773            &[msg],
774            &[b"test_hash_to_curve"],
775        )
776        .unwrap();
777        assert_eq!(point.0.is_on_curve().unwrap_u8(), 1u8);
778        assert_ne!(point, DecafPoint::IDENTITY);
779        assert_ne!(point, DecafPoint::GENERATOR);
780    }
781
782    // TODO: uncomment once elliptic-curve-tools is updated to match elliptic-curve 0.14
783    // #[test]
784    // fn test_sum_of_products() { use elliptic_curve_tools::SumOfProducts; let values = [ (Scalar::from(8u8), DecafPoint::GENERATOR), (Scalar::from(9u8), DecafPoint::GENERATOR), (Scalar::from(10u8), DecafPoint::GENERATOR), (Scalar::from(11u8), DecafPoint::GENERATOR), (Scalar::from(12u8), DecafPoint::GENERATOR), ]; let expected = DecafPoint::GENERATOR * Scalar::from(50u8); let result = DecafPoint::sum_of_products(&values); assert_eq!(result, expected); }
785    //
786    // #[test]
787    // fn test_sum_of_products2() {
788    //     use elliptic_curve_tools::SumOfProducts;
789    //     use rand_core::SeedableRng;
790    //
791    //     const TESTS: usize = 5;
792    //     const CHUNKS: usize = 10;
793    //     let mut rng = rand_chacha::ChaCha8Rng::from_seed([3u8; 32]);
794    //
795    //     for _ in 0..TESTS {
796    //         let scalars = (0..CHUNKS)
797    //             .map(|_| Scalar::random(&mut rng))
798    //             .collect::<Vec<_>>();
799    //         let points = (0..CHUNKS)
800    //             .map(|_| DecafPoint::random(&mut rng))
801    //             .collect::<Vec<_>>();
802    //
803    //         let input = scalars
804    //             .iter()
805    //             .zip(points.iter())
806    //             .map(|(&s, &p)| (s, p))
807    //             .collect::<Vec<_>>();
808    //         let rhs = DecafPoint::sum_of_products(&input);
809    //
810    //         let expected = points
811    //             .iter()
812    //             .zip(scalars.iter())
813    //             .fold(DecafPoint::IDENTITY, |acc, (&p, &s)| acc + (p * s));
814    //
815    //         assert_eq!(rhs, expected);
816    //     }
817    // }
818}