diff --git a/Cargo.toml b/Cargo.toml
index 7b901d7..ba96515 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -51,6 +51,7 @@ nightly = ["radix_51", "subtle/nightly"]
default = ["std"]
std = ["rand", "subtle/std"]
alloc = []
+# This isn't used at the moment, but keep it around for future yolocrypto features.
yolocrypto = []
bench = []
# Radix-51 arithmetic using u128
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..eb4f935
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+
+doc:
+ cargo rustdoc --features "nightly yolocrypto" -- --html-in-header katex-header.html
diff --git a/katex-header.html b/katex-header.html
new file mode 100644
index 0000000..695c735
--- /dev/null
+++ b/katex-header.html
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/src/constants.rs b/src/constants.rs
index 4c08102..09880dd 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -19,8 +19,7 @@
#![allow(non_snake_case)]
use edwards::CompressedEdwardsY;
-#[cfg(feature = "yolocrypto")]
-use decaf::{DecafPoint, DecafBasepointTable};
+use ristretto::{RistrettoPoint, RistrettoBasepointTable};
use montgomery::CompressedMontgomeryU;
use scalar::Scalar;
@@ -61,10 +60,9 @@ pub const BASE_COMPRESSED_MONTGOMERY: CompressedMontgomeryU =
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
-/// The Ed25519 basepoint, as a `DecafPoint`. This is called `_POINT` to distinguish it from
+/// The Ed25519 basepoint, as a `RistrettoPoint`. This is called `_POINT` to distinguish it from
/// `_TABLE`, which provides fast scalar multiplication.
-#[cfg(feature = "yolocrypto")] pub const DECAF_ED25519_BASEPOINT_POINT: DecafPoint =
-DecafPoint(ED25519_BASEPOINT_POINT);
+pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT);
/// `l` is the order of base point, i.e. 2^252 +
/// 27742317777372353535851937790883648493, in little-endian form
@@ -87,10 +85,9 @@ pub const l_minus_2: Scalar = Scalar([ 0xeb, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 ]);
-#[cfg(feature = "yolocrypto")]
-/// The Ed25519 basepoint
-pub const DECAF_ED25519_BASEPOINT_TABLE: DecafBasepointTable
- = DecafBasepointTable(ED25519_BASEPOINT_TABLE);
+/// The Ed25519 basepoint, as a RistrettoPoint
+pub const RISTRETTO_BASEPOINT_TABLE: RistrettoBasepointTable
+ = RistrettoBasepointTable(ED25519_BASEPOINT_TABLE);
#[cfg(test)]
mod test {
@@ -215,6 +212,14 @@ mod test {
assert_eq!(&constants::d * &four, constants::d4);
}
+ #[test]
+ fn test_sqrt_ad_minus_one() {
+ let a = FieldElement::minus_one();
+ let ad_minus_one = &(&a * &constants::d) + &a;
+ let should_be_ad_minus_one = constants::sqrt_ad_minus_one.square();
+ assert_eq!(should_be_ad_minus_one, ad_minus_one);
+ }
+
#[test]
fn test_a_minus_d() {
let a = FieldElement::minus_one();
diff --git a/src/constants_32bit.rs b/src/constants_32bit.rs
index bf46fe5..6cbe4db 100644
--- a/src/constants_32bit.rs
+++ b/src/constants_32bit.rs
@@ -39,6 +39,10 @@ pub const a_minus_d: FieldElement32 = FieldElement32([
10913609, -13857413, 15372611, -6949391, -114729,
8787816, 6275908, 3247719, 18696448, 12055116, ]);
+pub const sqrt_ad_minus_one: FieldElement32 = FieldElement32([
+ 24849947, -153582, -23613485, 6347715, -21072328, -667138, -25271143, -15367704, -870347, 14525639
+]);
+
pub const invsqrt_a_minus_d: FieldElement32 = FieldElement32([
6111485, 4156064, -27798727, 12243468, -25904040,
120897, 20826367, -7060776, 6093568, -1986012
diff --git a/src/constants_64bit.rs b/src/constants_64bit.rs
index ba7b2d9..f497b0d 100644
--- a/src/constants_64bit.rs
+++ b/src/constants_64bit.rs
@@ -33,6 +33,10 @@ pub const d4: FieldElement64 = FieldElement64([1468021120295602, 186546288051685
pub const a_minus_d: FieldElement64 = FieldElement64([1321844580190025, 1785434093556034, 589740348686294, 217950738957124, 809005158844672]);
+pub const sqrt_ad_minus_one: FieldElement64 = FieldElement64([
+ 2241493124984347, 425987919032274, 2207028919301688, 1220490630685848, 974799131293748
+]);
+
pub const invsqrt_a_minus_d: FieldElement64 = FieldElement64([
278908739862762, 821645201101625, 8113234426968, 1777959178193151, 2118520810568447
]);
diff --git a/src/decaf.rs b/src/decaf.rs
deleted file mode 100644
index 9bc2c8a..0000000
--- a/src/decaf.rs
+++ /dev/null
@@ -1,871 +0,0 @@
-// -*- mode: rust; -*-
-//
-// This file is part of curve25519-dalek.
-// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
-// See LICENSE for licensing information.
-//
-// Authors:
-// - Isis Agora Lovecruft
-// - Henry de Valence
-
-//! An implementation of Mike Hamburg's Decaf cofactor-eliminating
-//! point-compression scheme, providing a prime-order group on top of
-//! a non-prime-order elliptic curve.
-//!
-//! Note: this code is currently feature-gated with the `yolocrypto`
-//! feature flag, because our implementation is still unfinished.
-
-// We allow non snake_case names because coordinates in projective space are
-// traditionally denoted by the capitalisation of their respective
-// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my
-// affine and projective cakes and eat both of them too.
-#![allow(non_snake_case)]
-
-use core::fmt::Debug;
-
-#[cfg(feature = "std")]
-use rand::Rng;
-
-use digest::Digest;
-use generic_array::typenum::U32;
-
-use constants;
-use field::FieldElement;
-
-use core::ops::{Add, Sub, Neg};
-use core::ops::{AddAssign, SubAssign};
-use core::ops::{Mul, MulAssign};
-
-use edwards;
-use edwards::ExtendedPoint;
-use edwards::CompletedPoint;
-use edwards::EdwardsBasepointTable;
-use edwards::Identity;
-use scalar::Scalar;
-
-use subtle::ConditionallyAssignable;
-use subtle::ConditionallyNegatable;
-
-// ------------------------------------------------------------------------
-// Compressed points
-// ------------------------------------------------------------------------
-
-/// A point serialized using Mike Hamburg's Decaf scheme.
-///
-/// XXX think about how this API should work
-#[derive(Copy, Clone, Eq, PartialEq)]
-pub struct CompressedDecaf(pub [u8; 32]);
-
-/// The result of compressing a `DecafPoint`.
-impl CompressedDecaf {
- /// Convert this `CompressedDecaf` to an array of bytes.
- pub fn to_bytes(&self) -> [u8; 32] {
- self.0
- }
-
- /// View this `CompressedDecaf` as an array of bytes.
- pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] {
- &self.0
- }
-
- /// Attempt to decompress to an `DecafPoint`.
- pub fn decompress(&self) -> Option {
- // XXX should decoding be CT ?
- // XXX need to check that xy is nonnegative and reject otherwise
- let s = FieldElement::from_bytes(self.as_bytes());
-
- // Check that s = |s| and reject otherwise.
- let mut abs_s = s;
- let neg = abs_s.is_negative_decaf();
- abs_s.conditional_negate(neg);
- if abs_s != s { return None; }
-
- let ss = s.square();
- let X = &s + &s; // X = 2s
- let Z = &FieldElement::one() - &ss; // Z = 1+as^2
- let ZZ = Z.square();
- let u = &ZZ- &(&constants::d4 * &ss); // u = Z^2 - 4ds^2
- let uss = &u * &ss;
- let ussZZ = &uss * &ZZ;
-
- if Z.is_zero() == 1u8 { return None; }
-
- // Batch inversion: set b = 1/sqrt(us^2 Z^2)
- let (ussZZ_is_nonzero_square, b) = ussZZ.invsqrt();
- if (ussZZ_is_nonzero_square | uss.is_zero()) == 0u8 {
- return None; // us^2 is nonzero nonsquare
- }
-
- let mut v = &b * &Z; // now v = 1/sqrt(us^2)
- let Zinv = &b * &(&v * &uss); // now Zinv = b^2 Z us^2 = 1/Z
-
- // Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero.
- let uv = &v * &u;
- if uv.is_negative_decaf() == 1u8 {
- v.negate();
- }
- let mut two_minus_Z = -&Z; two_minus_Z.0[0] += 2;
- let mut w = &v * &(&s * &two_minus_Z);
- w.conditional_assign(&FieldElement::one(), s.is_zero());
- let Y = &w * &Z;
- let T = &w * &X;
-
- // "To decode the point, one must decode it to affine form
- // instead of projective, and check that xy is non-negative."
-
- // Use the value of 1/Z previously computed in the batch inversion
- let xy = &T * &Zinv;
- if (Y.is_nonzero() & xy.is_nonnegative_decaf()) == 1u8 {
- Some(DecafPoint(ExtendedPoint{ X: X, Y: Y, Z: Z, T: T }))
- } else {
- None
- }
- }
-}
-
-impl Identity for CompressedDecaf {
- fn identity() -> CompressedDecaf {
- CompressedDecaf([0u8; 32])
- }
-}
-
-// ------------------------------------------------------------------------
-// Serde support
-// ------------------------------------------------------------------------
-// Serializes to and from `DecafPoint` directly, doing compression
-// and decompression internally. This means that users can create
-// structs containing `DecafPoint`s and use Serde's derived
-// serializers to serialize those structures.
-
-#[cfg(feature = "serde")]
-use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
-#[cfg(feature = "serde")]
-use serde::de::Visitor;
-
-#[cfg(feature = "serde")]
-impl Serialize for DecafPoint {
- fn serialize(&self, serializer: S) -> Result
- where S: Serializer
- {
- serializer.serialize_bytes(self.compress().as_bytes())
- }
-}
-
-#[cfg(feature = "serde")]
-impl<'de> Deserialize<'de> for DecafPoint {
- fn deserialize(deserializer: D) -> Result
- where D: Deserializer<'de>
- {
- struct DecafPointVisitor;
-
- impl<'de> Visitor<'de> for DecafPointVisitor {
- type Value = DecafPoint;
-
- fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- formatter.write_str("a valid point in Decaf format")
- }
-
- fn visit_bytes(self, v: &[u8]) -> Result
- where E: serde::de::Error
- {
- if v.len() == 32 {
- let arr32 = array_ref!(v, 0, 32); // &[u8;32] from &[u8]
- CompressedDecaf(*arr32)
- .decompress()
- .ok_or(serde::de::Error::custom("decompression failed"))
- } else {
- Err(serde::de::Error::invalid_length(v.len(), &self))
- }
- }
- }
-
- deserializer.deserialize_bytes(DecafPointVisitor)
- }
-}
-
-// ------------------------------------------------------------------------
-// Internal point representations
-// ------------------------------------------------------------------------
-
-/// A point in a prime-order group.
-///
-// XXX think about how this API should work
-#[derive(Copy, Clone)]
-pub struct DecafPoint(pub ExtendedPoint);
-
-impl DecafPoint {
- /// Compress in Decaf format.
- pub fn compress(&self) -> CompressedDecaf {
- // Q: Do we want to encode twisted or untwisted?
- //
- // Notes:
- // Recall that the twisted Edwards curve E_{a,d} is of the form
- //
- // ax^2 + y^2 = 1 + dx^2y^2.
- //
- // Internally, we operate on the curve with a = -1, d =
- // -121665/121666, a.k.a., the twist. But maybe we would like
- // to use Decaf on the untwisted curve with a = 1, d =
- // 121665/121666. (why? interop?)
- //
- // Fix i, a square root of -1 (mod p).
- //
- // The map x -> ix is an isomorphism from E_{a,d} to E_{-a,-d}.
- // Its inverse is x -> -ix.
- // let untwisted_X = &self.X * &constants::MSQRT_M1;
- // etc.
- //
- // Step 0: pre-rotation, needed for Decaf with E[8] = Z/8.
- //
- // We want to select a point (x,y) in the coset P + E[4] with
- // y nonzero and xy nonnegative. The naive approach is as
- // follows. First, compute xy = T/Z and check that Y is
- // nonzero and xy is nonnegative. If not, then "rotate" the
- // original point by adding (x,y) + (i,0) = (iy,ix) = (x',y'):
- // this rotated point has x'y' = -xy. Then perform the normal
- // Decaf encoding, as described in Appendix A.1 of the Decaf
- // paper, using the rotated point (x',y').
- //
- // This is straightforward but requires an extra inversion.
- // We would like to batch the inversion in xy = T/Z with the
- // inverse square root in the computation of
- //
- // r = invsqrt((a-d)*(Z+Y)*(Z-Y))
- // = invsqrt(a-d)*invsqrt(Z^2-Y^2),
- //
- // but the X and Y we are trying to decode depend on whether
- // we rotated the coset representative!
- //
- // However, it is possible to batch these inversions. Credit:
- // the following explanation (and trick) is adapted from an
- // email from Mike Hamburg, but of course any errors are ours.
- //
- // Let the initial point be ( X_0 : Y_0 : Z_0 : T_0).
- // The rotated point is then (iY_0 : iX_0 : Z_0 : -T_0).
- //
- // We want to relate the computation of:
- //
- // invsqrt(Z^2 - Y^2) = invsqrt(Z_0^2 - Y_0^2) [non-rotated]
- // invsqrt(Z^2 - Y^2) = invsqrt(Z_0^2 + X_0^2) [rotated]
- //
- // The curve equation in extended coordinates is
- //
- // 0 = (-X^2 + Y^2)*Z^2 - Z^4 - d*X^2*Y^2,
- //
- // so
- // 0 = (-X^2 + Y^2)*Z^2 - Z^4 - d*T^2*Z^2 since XY=TZ
- // = (-X^2 + Y^2 - Z^2 - d*T^2)*Z^2
- // = ( X^2 - Y^2 + Z^2 + d*T^2)*Z^2 mult by -1
- // -T^2*Z^2 = (X^2 - Y^2 + Z^2 + d*T^2)*Z^2 - T^2*Z^2 sub T^2*Z^2
- // -T^2*Z^2 = (X^2 - Y^2 + Z^2 - T^2)*Z^2 + d*T^2*Z^2
- // (-1-d)*T^2*Z^2 = (X^2 - Y^2 + Z^2 - T^2)*Z^2
- //
- // for any point (X:Y:Z:T) in extended coordinates. Therefore,
- //
- // (Z^2 - Y^2)*(Z^2 + X^2) = Z^4 + Z^2*X^2 - Y^2*Z^2 - Y^2*X^2
- // = Z^4 + Z^2*X^2 - Y^2*Z^2 - T^2*Z^2 since XY=TZ
- // = Z^2*(X^2 - Y^2 + Z^2 - T^2)
- // = (-1-d)*T^2*Z^2.
- //
- // Taking square roots of both sides and rearranging, we get
- //
- // invsqrt(Z^2 - Y^2) = invsqrt(-1-d)*(Z^2+X^2)*(1/TZ)*invsqrt(Z^2+X^2)
- // `-----------' `---------------------'
- // curve constant batchable
- //
- // for any point (X:Y:Z:T) in extended coordinates.
- //
- // Therefore, we can do the computation with only one inverse
- // square root like so:
- //
- // W <--- invsqrt((T_0 * Z_0)^2 * (Z_0^2+X_0^2))
- // = 1/(T_0 * Z_0 * sqrt(Z_0^2 + X_0^2))
- //
- // xy <--- T_0^2 * W^2 * (T_0 * Z_0) * (Z_0^2 + X_0^2)
- // = T_0 / Z_0 = xy
- //
- // if Y_0 nonzero and xy nonnegative:
- // (X : Y : Z : T) <--- (X_0 : Y_0 : Z_0 : T_0)
- // r <--- (1/(-1-d)) * (Z_0^2 + X_0^2) * W
- // = invsqrt(a-d) * invsqrt(Z_0^2 - Y_0^2) since a = -1
- // = invsqrt(a-d) * invsqrt(Z^2 - Y^2)
- // otherwise:
- // (X : Y : Z : T) <--- (i*Y_0 : i*X_0 : Z_0 : -T_0)
- // r <--- invsqrt(a-d) * (T_0 * Z_0) * W
- // = invsqrt(a-d) * invsqrt(Z_0^2 + X_0^2)
- // = invsqrt(a-d) * invsqrt(Z^2 - Y^2)
- //
- // The rest of the compression follows the steps in the
- // appendix of the Decaf paper.
-
- let mut X = self.0.X;
- let mut Y = self.0.Y;
- let mut T = self.0.T;
- let Z = &self.0.Z;
-
- let TZ = &T * Z;
- let ZZ_plus_XX = &Z.square() + &X.square();
- let tmp = &TZ.square() * &ZZ_plus_XX;
- let (tmp_is_nonzero_square, W) = tmp.invsqrt();
- // tmp should always be a square (why? related to being in the
- // image of the isogeny?)
- debug_assert_eq!(tmp_is_nonzero_square | tmp.is_zero(), 1u8);
-
- let xy = &T.square() * &(&W.square() * &(&TZ * &ZZ_plus_XX));
- let rotate = 1u8 & !(Y.is_nonzero() & xy.is_nonnegative_decaf());
-
- let mut r = &W * &(&ZZ_plus_XX * &constants::inv_a_minus_d);
- let r_rot = &W * &(&TZ * &constants::invsqrt_a_minus_d);
-
- let iX = &X * &constants::SQRT_M1;
- let iY = &Y * &constants::SQRT_M1;
-
- r.conditional_assign(&r_rot, rotate);
- X.conditional_assign(&iY, rotate);
- Y.conditional_assign(&iX, rotate);
- T.conditional_negate(rotate);
-
- // Step 2: Compute u = (a-d)r
- let u = &constants::a_minus_d * &r;
-
- // Step 3: Negate r if -2uZ is negative.
- let uZ = &u * Z;
- let m2uZ = -&(&uZ + &uZ);
- r.conditional_negate(m2uZ.is_negative_decaf());
-
- // Step 4: Compute s = | u(r(aZX - dYT)+Y)/a|
- // = |u(r(-ZX - dYT)+Y)| since a = -1
- let minus_ZX = -&(Z * &X);
- let dYT = &constants::d * &(&Y * &T);
- // Compute s = u(r(aZX - dYT)+Y) and cnegate for abs
- let mut s = &u * &(&(&r * &(&minus_ZX - &dYT)) + &Y);
- let neg = s.is_negative_decaf();
- s.conditional_negate(neg);
- CompressedDecaf(s.to_bytes())
- }
-
- /// Return the coset self + E[4], for debugging.
- fn coset4(&self) -> [ExtendedPoint; 4] {
- [ self.0
- , &self.0 + &constants::EIGHT_TORSION[2]
- , &self.0 + &constants::EIGHT_TORSION[4]
- , &self.0 + &constants::EIGHT_TORSION[6]
- ]
- }
-
- /// Computes the Elligator map as described in the Decaf paper.
- ///
- /// # Note
- ///
- /// This method is not public because it's just used for hashing
- /// to a point -- proper elligator support is deferred for now.
- pub fn elligator_decaf_flavour(r_0: &FieldElement) -> DecafPoint {
- // Follows Appendix C of the Decaf paper.
- // Use n = 2 as the quadratic nonresidue so that n*x = x + x.
- let minus_one = -&FieldElement::one();
-
- // 1. Compute r <--- nr_0^2.
- let r_0_squared = r_0.square();
- let r = &r_0_squared + &r_0_squared;
-
- // 2. Compute D <--- (dr + (a-d)) * (dr - (d + ar))
- let dr = &constants::d * &r;
- // D = (dr + (a-d)) * (dr - (d + ar))
- // = (dr + (a-d)) * (dr - (d-r)) since a=-1
- // writing as
- // = (dr + (a-d)) * dr - (dr + (a-d)) * (d - r)
- // avoids two consecutive additions (could cause overflow)
- let dr_plus_amd = &dr + &constants::a_minus_d;
- let D = &(&dr_plus_amd * &dr) - &(&dr_plus_amd * &(&constants::d - &r));
-
- // 3. Compute N <--- (r+1) * (a-2d)
- let N = &(&r + &FieldElement::one()) * &(&minus_one - &constants::d2);
-
- // 4. Compute
- // / +1, 1 / sqrt(ND) if ND is square
- // c, e <--- | +1, 0 if N or D = 0
- // \ -1, nr_0 / sqrt(nND) otherwise
- let ND = &N * &D;
- let nND = &ND + &ND;
- let mut c = FieldElement::one();
- let mut e = FieldElement::zero();
- let (ND_is_nonzero_square, ND_invsqrt) = ND.invsqrt();
- e.conditional_assign(&ND_invsqrt, ND_is_nonzero_square);
- let (nND_is_nonzero_square, nND_invsqrt) = nND.invsqrt();
- let nr_0_nND_invsqrt = &nND_invsqrt * &(r_0 + r_0);
- c.conditional_assign(&minus_one, nND_is_nonzero_square);
- e.conditional_assign(&nr_0_nND_invsqrt, nND_is_nonzero_square);
-
- // 5. Compute s <--- c*|N*e|
- let mut s = &N * &e;
- let neg = s.is_negative_decaf();
- s.conditional_negate(neg);
- s *= &c;
-
- // 6. Compute t <--- -c*N*(r-1)* ((a-2d)*e)^2 -1
- let a_minus_2d_e_sq = (&(&minus_one - &constants::d2) * &e).square();
- let c_N_r_minus_1 = &c * &(&N * &(&r + &minus_one));
- let t = &minus_one - &(&c_N_r_minus_1 * &a_minus_2d_e_sq);
-
- // 7. Apply the isogeny:
- // (x,y) = ((2s)/(1+as^2), (1-as^2)/(t))
- let as_sq = &minus_one * &s.square();
- let P = CompletedPoint{
- X: &s + &s,
- Z: &FieldElement::one() + &as_sq,
- Y: &FieldElement::one() - &as_sq,
- T: t,
- };
-
- // Convert to extended and return.
- DecafPoint(P.to_extended())
- }
-
- /// Return a `DecafPoint` chosen uniformly at random using a user-provided RNG.
- ///
- /// # Inputs
- ///
- /// * `rng`: any RNG which implements the `rand::Rng` interface.
- ///
- /// # Returns
- ///
- /// A random element of the Decaf group.
- ///
- /// # Implementation
- ///
- /// Uses the Decaf-flavoured Elligator 2 map, so that the discrete log of the
- /// output point with respect to any other point should be unknown.
- #[cfg(feature = "std")]
- pub fn random(rng: &mut T) -> Self {
- let mut field_bytes = [0u8; 32];
- rng.fill_bytes(&mut field_bytes);
- let r_0 = FieldElement::from_bytes(&field_bytes);
- DecafPoint::elligator_decaf_flavour(&r_0)
- }
-
- /// Hash a slice of bytes into a `DecafPoint`.
- ///
- /// Takes a type parameter `D`, which is any `Digest` producing 32
- /// bytes (256 bits) of output.
- ///
- /// Convenience wrapper around `from_hash`.
- ///
- /// # Implementation
- ///
- /// Uses the Decaf-flavoured Elligator 2 map, so that the discrete log of the
- /// output point with respect to any other point should be unknown.
- ///
- /// # Example
- ///
- /// ```
- /// # extern crate curve25519_dalek;
- /// # use curve25519_dalek::decaf::DecafPoint;
- /// extern crate sha2;
- /// use sha2::Sha256;
- ///
- /// # // Need fn main() here in comment so the doctest compiles
- /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
- /// # fn main() {
- /// let msg = "To really appreciate architecture, you may even need to commit a murder";
- /// let P = DecafPoint::hash_from_bytes::(msg.as_bytes());
- /// # }
- /// ```
- ///
- pub fn hash_from_bytes(input: &[u8]) -> DecafPoint
- where D: Digest + Default
- {
- let mut hash = D::default();
- hash.input(input);
- DecafPoint::from_hash(hash)
- }
-
- /// Construct a `DecafPoint` from an existing `Digest` instance.
- ///
- /// Use this instead of `hash_from_bytes` if it is more convenient
- /// to stream data into the `Digest` than to pass a single byte
- /// slice.
- pub fn from_hash(hash: D) -> DecafPoint
- where D: Digest + Default
- {
- // XXX this seems clumsy
- let mut output = [0u8; 32];
- output.copy_from_slice(hash.result().as_slice());
- let r_0 = FieldElement::from_bytes(&output);
- DecafPoint::elligator_decaf_flavour(&r_0)
- }
-}
-
-impl Identity for DecafPoint {
- fn identity() -> DecafPoint {
- DecafPoint(ExtendedPoint::identity())
- }
-}
-
-// ------------------------------------------------------------------------
-// Equality
-// ------------------------------------------------------------------------
-
-/// XXX check whether there's a simple way to do equality checking
-/// with cofactor 8, not just cofactor 4, and add a CT equality function?
-impl PartialEq for DecafPoint {
- fn eq(&self, other: &DecafPoint) -> bool {
- let self_compressed = self.compress();
- let other_compressed = other.compress();
- self_compressed == other_compressed
- }
-}
-
-impl Eq for DecafPoint {}
-
-// ------------------------------------------------------------------------
-// Arithmetic
-// ------------------------------------------------------------------------
-
-impl<'a, 'b> Add<&'b DecafPoint> for &'a DecafPoint {
- type Output = DecafPoint;
-
- fn add(self, other: &'b DecafPoint) -> DecafPoint {
- DecafPoint(&self.0 + &other.0)
- }
-}
-
-impl<'b> AddAssign<&'b DecafPoint> for DecafPoint {
- fn add_assign(&mut self, _rhs: &DecafPoint) {
- *self = (self as &DecafPoint) + _rhs;
- }
-}
-
-impl<'a, 'b> Sub<&'b DecafPoint> for &'a DecafPoint {
- type Output = DecafPoint;
-
- fn sub(self, other: &'b DecafPoint) -> DecafPoint {
- DecafPoint(&self.0 - &other.0)
- }
-}
-
-impl<'b> SubAssign<&'b DecafPoint> for DecafPoint {
- fn sub_assign(&mut self, _rhs: &DecafPoint) {
- *self = (self as &DecafPoint) - _rhs;
- }
-}
-
-impl<'a> Neg for &'a DecafPoint {
- type Output = DecafPoint;
-
- fn neg(self) -> DecafPoint {
- DecafPoint(-&self.0)
- }
-}
-
-impl<'b> MulAssign<&'b Scalar> for DecafPoint {
- fn mul_assign(&mut self, scalar: &'b Scalar) {
- let result = (self as &DecafPoint) * scalar;
- *self = result;
- }
-}
-
-impl<'a, 'b> Mul<&'b Scalar> for &'a DecafPoint {
- type Output = DecafPoint;
- /// Scalar multiplication: compute `scalar * self`.
- fn mul(self, scalar: &'b Scalar) -> DecafPoint {
- DecafPoint(&self.0 * scalar)
- }
-}
-
-impl<'a, 'b> Mul<&'b DecafPoint> for &'a Scalar {
- type Output = DecafPoint;
-
- /// Scalar multiplication: compute `self * scalar`.
- fn mul(self, point: &'b DecafPoint) -> DecafPoint {
- DecafPoint(self * &point.0)
- }
-}
-
-/// Given a vector of (possibly secret) scalars and a vector of
-/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`.
-///
-/// This function has the same behaviour as
-/// `vartime::multiscalar_mult` but is constant-time.
-///
-/// # Input
-///
-/// A vector of `Scalar`s and a vector of `DecafPoints`. It is an
-/// error to call this function with two vectors of different lengths.
-#[cfg(any(feature = "alloc", feature = "std"))]
-pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint
- where I: IntoIterator- ,
- J: IntoIterator
- ,
-{
- let extended_points = points.into_iter().map(|P| &P.0);
- DecafPoint(edwards::multiscalar_mult(scalars, extended_points))
-}
-
-/// Precomputation
-#[derive(Clone)]
-pub struct DecafBasepointTable(pub EdwardsBasepointTable);
-
-impl<'a, 'b> Mul<&'b Scalar> for &'a DecafBasepointTable {
- type Output = DecafPoint;
-
- fn mul(self, scalar: &'b Scalar) -> DecafPoint {
- DecafPoint(&self.0 * scalar)
- }
-}
-
-impl<'a, 'b> Mul<&'a DecafBasepointTable> for &'b Scalar {
- type Output = DecafPoint;
-
- fn mul(self, basepoint_table: &'a DecafBasepointTable) -> DecafPoint {
- DecafPoint(self * &basepoint_table.0)
- }
-}
-
-impl DecafBasepointTable {
- /// Create a precomputed table of multiples of the given `basepoint`.
- pub fn create(basepoint: &DecafPoint) -> DecafBasepointTable {
- DecafBasepointTable(EdwardsBasepointTable::create(&basepoint.0))
- }
-
- /// Get the basepoint for this table as a `DecafPoint`.
- pub fn basepoint(&self) -> DecafPoint {
- DecafPoint(self.0.basepoint())
- }
-}
-
-// ------------------------------------------------------------------------
-// Constant-time conditional assignment
-// ------------------------------------------------------------------------
-
-impl ConditionallyAssignable for DecafPoint {
- /// Conditionally assign `other` to `self`, if `choice == 1u8`.
- ///
- /// # Example
- ///
- /// ```
- /// # extern crate subtle;
- /// # extern crate curve25519_dalek;
- /// #
- /// # use subtle::ConditionallyAssignable;
- /// #
- /// # use curve25519_dalek::edwards::Identity;
- /// # use curve25519_dalek::decaf::DecafPoint;
- /// # use curve25519_dalek::constants;
- /// # fn main() {
- /// let A = DecafPoint::identity();
- /// let B = constants::DECAF_ED25519_BASEPOINT_POINT;
- ///
- /// let mut P = A;
- ///
- /// P.conditional_assign(&B, 0u8);
- /// assert!(P == A);
- /// P.conditional_assign(&B, 1u8);
- /// assert!(P == B);
- /// # }
- /// ```
- fn conditional_assign(&mut self, other: &DecafPoint, choice: u8) {
- self.0.X.conditional_assign(&other.0.X, choice);
- self.0.Y.conditional_assign(&other.0.Y, choice);
- self.0.Z.conditional_assign(&other.0.Z, choice);
- self.0.T.conditional_assign(&other.0.T, choice);
- }
-}
-
-// ------------------------------------------------------------------------
-// Debug traits
-// ------------------------------------------------------------------------
-
-impl Debug for CompressedDecaf {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- write!(f, "CompressedDecaf: {:?}", self.as_bytes())
- }
-}
-
-impl Debug for DecafPoint {
- fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- let coset = self.coset4();
- write!(f, "DecafPoint: coset \n{:?}\n{:?}\n{:?}\n{:?}",
- coset[0], coset[1], coset[2], coset[3])
- }
-}
-
-// ------------------------------------------------------------------------
-// Variable-time functions
-// ------------------------------------------------------------------------
-
-pub mod vartime {
- //! Variable-time operations on decaf points, useful for non-secret data.
- use super::*;
-
- /// Given a vector of public scalars and a vector of (possibly secret)
- /// points, compute
- ///
- /// c_1 P_1 + ... + c_n P_n.
- ///
- /// # Input
- ///
- /// A vector of `Scalar`s and a vector of `ExtendedPoints`. It is an
- /// error to call this function with two vectors of different lengths.
- pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> DecafPoint
- where I: IntoIterator
- ,
- J: IntoIterator
-
- {
- let extended_points = points.into_iter().map(|P| &P.0);
- DecafPoint(edwards::vartime::multiscalar_mult(scalars, extended_points))
- }
-}
-
-// ------------------------------------------------------------------------
-// Tests
-// ------------------------------------------------------------------------
-
-#[cfg(test)]
-mod test {
- use rand::OsRng;
-
- use scalar::Scalar;
- use constants;
- use edwards::CompressedEdwardsY;
- use edwards::Identity;
- use edwards::ValidityCheck;
- use super::*;
-
- #[cfg(feature = "serde")]
- use serde_cbor;
-
- #[test]
- #[cfg(feature = "serde")]
- fn serde_cbor_basepoint_roundtrip() {
- let output = serde_cbor::to_vec(&constants::DECAF_ED25519_BASEPOINT_POINT).unwrap();
- let parsed: DecafPoint = serde_cbor::from_slice(&output).unwrap();
- assert_eq!(parsed, constants::DECAF_ED25519_BASEPOINT_POINT);
- }
-
-
- #[test]
- fn decaf_decompress_negative_s_fails() {
- // constants::d is neg, so decompression should fail as |d| != d.
- let bad_compressed = CompressedDecaf(constants::d.to_bytes());
- assert!(bad_compressed.decompress().is_none());
- }
-
- #[test]
- fn decaf_decompress_id() {
- let compressed_id = CompressedDecaf::identity();
- let id = compressed_id.decompress().unwrap();
- assert_eq!(id.0.compress(), CompressedEdwardsY::identity());
- }
-
- #[test]
- fn decaf_compress_id() {
- let id = DecafPoint::identity();
- assert_eq!(id.compress(), CompressedDecaf::identity());
- }
-
- #[test]
- fn decaf_basepoint_roundtrip() {
- let bp_compressed_decaf = constants::DECAF_ED25519_BASEPOINT_POINT.compress();
- let bp_recaf = bp_compressed_decaf.decompress().unwrap().0;
- // Check that bp_recaf differs from bp by a point of order 4
- let diff = &constants::ED25519_BASEPOINT_POINT - &bp_recaf;
- let diff4 = diff.mult_by_pow_2(4); // XXX this is wrong
- assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
- }
-
- #[test]
- fn encodings_of_small_multiples_of_basepoint() {
- // Table of encodings of (1+i)*basepoint
- // Generated using the previous naive implementation.
- let compressed = [
- CompressedDecaf([141, 190, 226, 107, 177, 201, 35, 118, 14, 55, 160, 165, 242, 207, 121, 161, 177, 80, 8, 132, 205, 254, 101, 169, 233, 65, 124, 96, 255, 182, 249, 40]),
- CompressedDecaf([131, 57, 148, 16, 8, 196, 141, 82, 144, 220, 105, 112, 66, 33, 48, 16, 182, 198, 173, 35, 248, 181, 92, 231, 222, 35, 85, 56, 5, 252, 91, 40]),
- CompressedDecaf([199, 132, 32, 144, 156, 143, 81, 170, 240, 56, 232, 6, 178, 37, 118, 190, 110, 201, 26, 173, 156, 97, 59, 162, 240, 247, 226, 107, 197, 111, 107, 26]),
- CompressedDecaf([210, 120, 34, 214, 175, 27, 61, 6, 229, 181, 216, 36, 11, 245, 146, 232, 130, 215, 77, 29, 210, 30, 54, 155, 191, 81, 59, 124, 174, 3, 135, 36]),
- CompressedDecaf([155, 52, 159, 52, 189, 27, 181, 0, 245, 131, 0, 197, 79, 208, 252, 122, 104, 161, 245, 143, 67, 94, 13, 129, 153, 173, 129, 179, 118, 231, 90, 52]),
- CompressedDecaf([42, 117, 252, 118, 8, 1, 72, 25, 111, 246, 247, 103, 236, 86, 235, 29, 100, 156, 186, 209, 159, 21, 61, 26, 249, 25, 137, 228, 84, 23, 10, 27]),
- CompressedDecaf([21, 126, 181, 117, 58, 90, 216, 28, 184, 57, 9, 23, 158, 68, 159, 171, 109, 150, 232, 140, 144, 73, 139, 122, 124, 105, 125, 160, 94, 185, 150, 52]),
- CompressedDecaf([232, 167, 112, 233, 126, 33, 105, 63, 151, 6, 88, 225, 181, 17, 223, 12, 116, 138, 203, 47, 243, 225, 50, 171, 21, 220, 186, 179, 132, 20, 48, 6]),
- CompressedDecaf([99, 44, 97, 48, 242, 174, 78, 198, 112, 154, 146, 36, 239, 34, 94, 4, 0, 244, 175, 34, 46, 0, 83, 187, 5, 163, 225, 63, 51, 237, 234, 22]),
- CompressedDecaf([2, 33, 89, 176, 178, 123, 159, 75, 235, 172, 251, 11, 137, 177, 90, 122, 149, 186, 52, 243, 153, 190, 185, 202, 59, 137, 204, 160, 150, 152, 148, 55]),
- CompressedDecaf([245, 79, 78, 226, 114, 69, 247, 112, 18, 54, 90, 225, 176, 77, 231, 235, 196, 123, 49, 221, 34, 205, 151, 228, 244, 112, 82, 58, 30, 31, 58, 12]),
- CompressedDecaf([135, 53, 175, 167, 13, 94, 62, 31, 29, 248, 13, 132, 29, 69, 7, 188, 145, 49, 62, 55, 181, 109, 214, 11, 248, 162, 70, 15, 236, 126, 100, 60]),
- CompressedDecaf([98, 150, 69, 229, 144, 122, 237, 107, 127, 177, 33, 64, 59, 173, 210, 102, 74, 34, 23, 16, 252, 117, 14, 97, 231, 178, 63, 193, 157, 28, 178, 17]),
- CompressedDecaf([222, 104, 6, 1, 72, 12, 72, 178, 204, 238, 128, 70, 41, 150, 235, 96, 153, 150, 18, 4, 141, 206, 0, 38, 122, 112, 249, 51, 94, 251, 20, 57]),
- CompressedDecaf([7, 221, 140, 57, 13, 146, 248, 27, 56, 4, 128, 23, 145, 120, 126, 4, 158, 173, 52, 213, 164, 250, 26, 55, 89, 96, 187, 111, 211, 18, 63, 19]),
- CompressedDecaf([91, 213, 193, 10, 102, 92, 199, 124, 61, 176, 1, 47, 111, 59, 183, 91, 79, 56, 208, 109, 172, 209, 17, 167, 229, 216, 3, 236, 200, 208, 15, 20]),
- ];
- let mut bp = constants::DECAF_ED25519_BASEPOINT_POINT;
- for i in 0..16 {
- assert_eq!(bp.compress(), compressed[i]);
- bp = &bp + &constants::DECAF_ED25519_BASEPOINT_POINT;
- }
- }
-
- #[test]
- fn decaf_four_torsion_basepoint() {
- let bp = constants::DECAF_ED25519_BASEPOINT_POINT;
- let bp_coset = bp.coset4();
- for i in 0..4 {
- assert_eq!(bp, DecafPoint(bp_coset[i]));
- }
- }
-
- #[test]
- fn decaf_four_torsion_random() {
- let mut rng = OsRng::new().unwrap();
- let B = &constants::DECAF_ED25519_BASEPOINT_TABLE;
- let P = B * &Scalar::random(&mut rng);
- let P_coset = P.coset4();
- for i in 0..4 {
- assert_eq!(P, DecafPoint(P_coset[i]));
- }
- }
-
- #[test]
- fn decaf_random_roundtrip() {
- let mut rng = OsRng::new().unwrap();
- let B = &constants::DECAF_ED25519_BASEPOINT_TABLE;
- for _ in 0..100 {
- let P = B * &Scalar::random(&mut rng);
- let compressed_P = P.compress();
- let Q = compressed_P.decompress().unwrap();
- assert_eq!(P, Q);
- }
- }
-
- #[test]
- fn decaf_random_is_valid() {
- let mut rng = OsRng::new().unwrap();
- for _ in 0..100 {
- let P = DecafPoint::random(&mut rng);
- // Check that P is on the curve
- assert!(P.0.is_valid());
- // Check that P is in the image of the decaf map
- P.compress();
- }
- }
-}
-
-#[cfg(all(test, feature = "bench"))]
-mod bench {
- use rand::OsRng;
- use test::Bencher;
-
- use super::*;
-
- #[bench]
- fn decompression(b: &mut Bencher) {
- let mut rng = OsRng::new().unwrap();
- let B = &constants::DECAF_ED25519_BASEPOINT_TABLE;
- let P = B * &Scalar::random(&mut rng);
- let P_compressed = P.compress();
- b.iter(|| P_compressed.decompress().unwrap());
- }
-
- #[bench]
- fn compression(b: &mut Bencher) {
- let mut rng = OsRng::new().unwrap();
- let B = &constants::DECAF_ED25519_BASEPOINT_TABLE;
- let P = B * &Scalar::random(&mut rng);
- b.iter(|| P.compress());
- }
-}
diff --git a/src/edwards.rs b/src/edwards.rs
index 0b8b333..feed487 100644
--- a/src/edwards.rs
+++ b/src/edwards.rs
@@ -143,7 +143,7 @@ impl CompressedEdwardsY {
// Flip the sign of X if it's not correct
let compressed_sign_bit = self.as_bytes()[31] >> 7;
- let current_sign_bit = X.is_negative_ed25519();
+ let current_sign_bit = X.is_negative();
X.conditional_negate(current_sign_bit ^ compressed_sign_bit);
Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: &X * &Y })
@@ -442,7 +442,7 @@ impl ProjectivePoint {
let mut s: [u8; 32];
s = y.to_bytes();
- s[31] ^= (x.is_negative_ed25519() << 7) as u8;
+ s[31] ^= (x.is_negative() << 7) as u8;
CompressedEdwardsY(s)
}
@@ -1269,8 +1269,6 @@ pub mod vartime {
#[cfg(test)]
mod test {
- #[cfg(feature = "yolocrypto")]
- use decaf::DecafPoint;
use field::FieldElement;
use scalar::Scalar;
use subtle::ConditionallyAssignable;
@@ -1550,18 +1548,6 @@ mod test {
assert!(P1.compress().to_bytes() == P2.compress().to_bytes());
}
- #[test]
- #[cfg(feature = "yolocrypto")]
- fn scalarmult_decafpoint_works_both_ways() {
- let P: DecafPoint = DecafPoint(constants::ED25519_BASEPOINT_POINT);
- let s: Scalar = A_SCALAR;
-
- let P1 = &P * &s;
- let P2 = &s * &P;
-
- assert!(P1.compress().as_bytes() == P2.compress().as_bytes());
- }
-
mod vartime {
use super::super::*;
use super::{A_SCALAR, B_SCALAR, A_TIMES_BASEPOINT, DOUBLE_SCALAR_MULT_RESULT};
diff --git a/src/field.rs b/src/field.rs
index 730c3fa..840355d 100644
--- a/src/field.rs
+++ b/src/field.rs
@@ -23,6 +23,7 @@ use core::cmp::{Eq, PartialEq};
use subtle::slices_equal;
use subtle::byte_is_nonzero;
use subtle::ConditionallyAssignable;
+use subtle::ConditionallyNegatable;
use subtle::Equal;
use constants;
@@ -81,44 +82,11 @@ impl FieldElement {
/// # Return
///
/// If negative, return `1u8`. Otherwise, return `0u8`.
- pub fn is_negative_ed25519(&self) -> u8 { //FeIsNegative
+ pub fn is_negative(&self) -> u8 {
let bytes = self.to_bytes();
(bytes[0] & 1) as u8
}
- /// Determine if this `FieldElement` is negative, in the
- /// sense used by Decaf: `x` is nonnegative if the least
- /// absolute residue for `x` lies in `[0, (p-1)/2]`, and
- /// is negative otherwise.
- ///
- /// # Return
- ///
- /// Returns `1u8` if negative, `0u8` if nonnegative.
- ///
- /// # Implementation
- ///
- /// Uses a trick borrowed from Mike Hamburg's code. Let `x \in
- /// F_p` and let `y \in Z` be the least absolute residue for `x`.
- /// Suppose `y ≤ (p-1)/2`. Then `2y < p` so `2y = 2y mod p` and
- /// `2y mod p` is even. On the other hand, if `y > (p-1)/2` then
- /// `2y ≥ p`; since `y < p`, `2y \in [p, 2p)`, so `2y mod p =
- /// 2y-p`, which is odd.
- ///
- /// Thus we can test whether `y ≤ (p-1)/2` by checking whether `2y
- /// mod p` is even.
- pub fn is_negative_decaf(&self) -> u8 {
- let y = self + self;
- (y.to_bytes()[0] & 1) as u8
- }
-
- /// Determine if this `FieldElement` is nonnegative, in the
- /// sense used by Decaf: `x` is nonnegative if the least
- /// absolute residue for `x` lies in `[0, (p-1)/2]`, and
- /// is negative otherwise.
- pub fn is_nonnegative_decaf(&self) -> u8 {
- 1u8 & (!self.is_negative_decaf())
- }
-
/// Determine if this `FieldElement` is zero.
///
/// # Return
@@ -232,6 +200,8 @@ impl FieldElement {
/// Given `FieldElements` `u` and `v`, attempt to compute
/// `sqrt(u/v)` in constant time.
///
+ /// This function always returns the nonnegative square root, if it exists.
+ ///
/// It would be much better to use an `Option` type here, but
/// doing so forces the caller to branch, which we don't want to
/// do. This seems like the least bad solution.
@@ -278,6 +248,10 @@ impl FieldElement {
let r_prime = &constants::SQRT_M1 * &r;
r.conditional_assign(&r_prime, flipped_sign_sqrt);
+ // Choose the nonnegative square root.
+ let r_is_negative = r.is_negative();
+ r.conditional_negate(r_is_negative);
+
let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt;
(was_nonzero_square, r)
@@ -465,6 +439,20 @@ mod test {
x.conditional_negate(1u8);
assert_eq!(x, one);
}
+
+ #[test]
+ fn encoding_is_canonical() {
+ // Encode 1 wrongly as 1 + (2^255 - 19) = 2^255 - 18
+ let one_encoded_wrongly_bytes: [u8;32] = [0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f];
+ // Decode to a field element
+ let one = FieldElement::from_bytes(&one_encoded_wrongly_bytes);
+ // .. then check that the encoding is correct
+ let one_bytes = one.to_bytes();
+ assert_eq!(one_bytes[0], 1);
+ for i in 1..32 {
+ assert_eq!(one_bytes[i], 0);
+ }
+ }
}
#[cfg(all(test, feature = "bench"))]
diff --git a/src/lib.rs b/src/lib.rs
index 31c18ff..3e18477 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -74,9 +74,7 @@ pub mod scalar;
pub mod edwards;
pub mod montgomery;
-// Feature gate decaf while our implementation is unfinished and probably incorrect.
-#[cfg(feature = "yolocrypto")]
-pub mod decaf;
+pub mod ristretto;
// Other miscelaneous utilities.
diff --git a/src/montgomery.rs b/src/montgomery.rs
index 9ba71ed..33ddae7 100644
--- a/src/montgomery.rs
+++ b/src/montgomery.rs
@@ -183,7 +183,7 @@ impl CompressedMontgomeryU {
pub fn to_edwards_x(u: &FieldElement, v: &FieldElement, sign: &u8) -> FieldElement {
let mut x: FieldElement = &(u * &v.invert()) * &constants::SQRT_MINUS_APLUS2;
let neg_x: FieldElement = -(&x);
- let current_sign: u8 = x.is_negative_ed25519();
+ let current_sign: u8 = x.is_negative();
// Negate x to match the sign:
x.conditional_assign(&neg_x, current_sign ^ sign);
diff --git a/src/ristretto.rs b/src/ristretto.rs
new file mode 100644
index 0000000..7a09256
--- /dev/null
+++ b/src/ristretto.rs
@@ -0,0 +1,1245 @@
+// -*- mode: rust; -*-
+//
+// This file is part of curve25519-dalek.
+// Copyright (c) 2016-2017 Isis Lovecruft, Henry de Valence
+// See LICENSE for licensing information.
+//
+// Authors:
+// - Isis Agora Lovecruft
+// - Henry de Valence
+
+//! An implementation of Ristretto, which provides a prime-order group.
+//!
+//! Ristretto is a modification of Mike Hamburg's [Decaf
+//! cofactor-eliminating point-compression
+//! scheme](https://eprint.iacr.org/2015/673.pdf) to work on top of the
+//! Curve25519 group.
+//!
+//! Below are some notes on Ristretto, which are *NOT* a full writeup and which may have errors.
+//!
+//! # Notes on Ristretto
+//!
+//! ## Decaf
+//!
+//! The introduction of the Decaf paper, [_Decaf: Eliminating cofactors
+//! through point compression_](https://eprint.iacr.org/2015/673.pdf)
+//! notes that while most cryptographic systems require a group of prime
+//! order, most concrete implementations using elliptic curve groups
+//! fall short -- they either provide a group of prime order, but with
+//! incomplete or variable-time addition formulae (for instance, most
+//! Weierstrass models), or else they provide a fast and safe
+//! implementation of a group whose order is not quite a prime \\(q\\),
+//! but \\(hq\\) for a small cofactor \\(h\\) (for instance, Edwards
+//! curves, which have cofactor at least \\(4\\)).
+//!
+//! This abstraction mismatch requires ad-hoc protocol modifications to
+//! ensure security; these modifications require careful analysis and
+//! are a recurring source of vulnerabilities.
+//!
+//! The Decaf suggestion is to use a quotient group, such as \\(\mathcal
+//! E / \mathcal E[4]\\) or \\(2 \mathcal E / \mathcal E[2] \\), to
+//! implement a prime-order group.
+//!
+//! This requires only changing
+//!
+//! 1. the function for equality checking (so that two representatives
+//! of the same coset are considered equal);
+//! 2. the function for encoding (so that two representatives of the
+//! same coset are encoded as identical bitstrings);
+//! 3. the function for decoding (so that only the canonical encoding of
+//! a coset is accepted).
+//!
+//! Internally, each coset is represented by a curve point; two points
+//! may represent the same coset in the same way that two points with
+//! different \\(X,Y,Z\\) coordinates may represent the same point. The
+//! group operations are carried out using the fast, safe Edwards
+//! formulas.
+//!
+//! The Decaf paper suggests implementing the compression and
+//! decompression routines using an isogeny from a Jacobi quartic; for
+//! curves of cofactor \\(4\\), this eliminates the cofactor, and
+//! explains the name: Decaf is named "after the procedure which divides
+//! the effect of coffee by \\(4\\)". However, Curve25519 has a
+//! cofactor of \\(8\\). To eliminate its cofactor, we tweak Decaf to
+//! restrict further. This gives the
+//! [Ristretto](https://en.wikipedia.org/wiki/Ristretto) encoding.
+//!
+//! ## The Jacobi Quartic
+//!
+//! The Jacobi quartic is parameterized by \\(e, A\\), and is of the
+//! form $$ \mathcal J\_{e,A} : t\^2 = es\^4 + 2As\^2 + 1, $$ with
+//! identity point \\((0,1)\\). For more details on the Jacobi quartic,
+//! see the [Decaf paper](https://eprint.iacr.org/2015/673.pdf) or
+//! [_Jacobi Quartic Curves
+//! Revisited_](https://eprint.iacr.org/2009/312.pdf) by Hisil, Wong,
+//! Carter, and Dawson).
+//!
+//! When \\(e = a\^2\\), \\(\mathcal J\_{e,A}\\) has full
+//! \\(2\\)-torsion (i.e., \\(\mathcal J[2] \cong \mathbb Z /2 \times
+//! \mathbb Z/2\\)), and
+//! we can write the \\(\mathcal J[2]\\)-coset of a point \\(P =
+//! (s,t)\\) as
+//! $$
+//! P + \mathcal J[2] = \left\\{
+//! (s,t),
+//! (-s,-t),
+//! (1/as, -t/as\^2),
+//! (-1/as, t/as\^2) \right\\}.
+//! $$
+//! Notice that replacing \\(a\\) by \\(-a\\) just swaps the last two
+//! points, so this set does not depend on the choice of \\(a\\). In
+//! what follows we require \\(a = \pm 1\\).
+//!
+//! ## Encoding \\(\mathcal J / \mathcal J[2]\\)
+//!
+//! To encode points on \\(\mathcal J\\) modulo \\(\mathcal J[2]\\),
+//! we need to choose a canonical representative of the above coset.
+//! To do this, it's sufficient to make two independent sign choices:
+//! the Decaf paper suggests choosing \\((s,t)\\) with \\(s\\)
+//! non-negative and finite, and \\(t/s\\) non-negative or infinite.
+//!
+//! The encoding is then the (canonical byte encoding of the)
+//! \\(s\\)-value of the canonical representative.
+//!
+//! ## The Edwards Curve
+//!
+//! Our primary internal model for Curve25519 points are the [_Extended
+//! Twisted Edwards Coordinates_](https://eprint.iacr.org/2008/522.pdf)
+//! of Hisil, Wong, Carter, and Dawson.
+//! These correspond to the affine model
+//!
+//! $$\mathcal E\_{a,d} : ax\^2 + y\^2 = 1 + dx\^2y\^2.$$
+//!
+//! In projective coordinates, we represent a point as \\((X:Y:Z:T)\\)
+//! with $$XY = ZT, \quad aX\^2 + Y\^2 = Z\^2 + dT\^2.$$ (For more
+//! details on this model, see the documentation for the `edwards`
+//! module). The case \\(a = 1\\) is the _untwisted_ case; we only
+//! consider \\(a = \pm 1\\), and in particular we focus on the twisted
+//! Edwards form of Curve25519, which has \\(a = -1, d =
+//! -121665/121666\\). When not otherwise specified, we write
+//! \\(\mathcal E\\) for \\(\mathcal E\_{-1, -121665/121666}\\).
+//!
+//! When both \\(d\\) and \\(ad\\) are nonsquare (which forces \\(a\\)
+//! to be square), the curve is *complete*. In this case the
+//! four-torsion subgroup is cyclic, and we
+//! can write it explicitly as
+//! $$
+//! \mathcal E\_{a,d}[4] = \\{ (0,1),\; (1/\sqrt a, 0),\; (0, -1),\; (-1/\sqrt{a}, 0)\\}.
+//! $$
+//! These are the only points with \\(xy = 0\\); the points with \\( y
+//! \neq 0 \\) are \\(2\\)-torsion. The \\(\mathcal
+//! E\_{a,d}[4]\\)-coset of \\(P = (x,y)\\) is then
+//! $$
+//! P + \mathcal E\_{a,d}[4] = \\{ (x,y),\; (y/\sqrt a, -x\sqrt a),\; (-x, -y),\; (-y/\sqrt a, x\sqrt a)\\}.
+//! $$
+//! Notice that if \\(xy \neq 0 \\), then exactly two of
+//! these points have \\( xy \\) non-negative, and they differ by the
+//! \\(2\\)-torsion point \\( (0,-1) \\). This means that we can select
+//! a representative modulo \\(\mathcal
+//! E\_{a,d}[2] \\) by requiring \\(xy\\) nonnegative and \\(y \neq
+//! 0\\), and we can ensure this condition by conditionally adding a
+//! \\(4\\)-torsion point if \\(xy\\) is negative or \\(y = 0\\).
+//!
+//! This procedure gives a canonical lift from \\(\mathcal E / \mathcal
+//! E[4]\\) to \\(\mathcal E / \mathcal E[2]\\). Since it involves a
+//! conditional rotation, we refer to it as *torquing* the point.
+//!
+//! The structure of the Curve25519 group is \\( \mathcal E(\mathbb
+//! F\_p) \cong \mathbb Z / 8 \times \mathbb Z / \ell\\), where \\( \ell
+//! = 2\^{252} + \cdots \\) is a large prime. Because \\(\mathcal E[8]
+//! \cong \mathbb Z / 8\\), we have \\(\[2\](\mathcal E[8]) = \mathcal
+//! E[4]\\), \\(\mathcal E[4] \cong \mathbb Z / 4
+//! \\) and \\( \mathcal E[2] \cong \mathbb Z / 2\\). In particular
+//! this tells us that the group
+//! $$
+//! \frac{\[2\](\mathcal E)}{\mathcal E[4]}
+//! $$
+//! is well-defined and has prime order \\( (8\ell / 2) / 4 = \ell \\).
+//! This is the group we will construct using Ristretto.
+//!
+//! ## The Isogeny
+//!
+//! For \\(a = \pm 1\\), we have a \\(2\\)-isogeny
+//! $$
+//! \theta\_{a,d} : \mathcal J\_{a\^2, -a(a+d)/(a-d)} \longrightarrow \mathcal E\_{a,d}
+//! $$
+//! (or simply \\(\theta\\)) defined by
+//! $$
+//! \theta\_{a,d} : (s,t) \mapsto \left( \frac{1}{\sqrt{ad-1}} \cdot \frac{2s}{t},\quad \frac{1+as\^2}{1-as\^2} \right).
+//! $$
+//!
+//! XXX Its dual is ... ?
+//!
+//! The kernel of the isogeny is \\( \{(0, \pm 1)\} \\).
+//! The image of the isogeny is \\(\[2\](\mathcal E)\\). To see this,
+//! first note that because \\( \theta \circ \hat{\theta} = [2] \\), we
+//! know that \\( \[2\](\mathcal E) \subseteq \theta(\mathcal J)\\); then, to see that
+//! \\(\theta(\mathcal J)\\) is exactly \\(\[2\](\mathcal E)\\),
+//! recall that isogenous elliptic curves over a finite field have the
+//! same number of points (exercise 5.4 of Silverman), so that
+//! $$
+//! \\# \theta(\mathcal J) = \frac {\\# \mathcal J} {\\# \ker \theta}
+//! = \frac {\\# \mathcal E}{2} = \\# \[2\](\mathcal E).
+//! $$
+//!
+//! To determine the image \\(\theta(\mathcal J[2])\\) of the
+//! \\(2\\)-torsion, we consider the image of the coset \\(\theta((s,t)
+//! + \mathcal J[2])\\). Let \\((x,y) = \theta(s,t)\\); then
+//! \\(\theta(-s,-t) = (x,y)\\) and \\(\theta(1/as, -t/as\^2) = (-x,
+//! -y)\\), so that \\(\theta(\mathcal J[2]) = \mathcal E[2]\\).
+//!
+//! The Decaf paper recalls that, for a group \\( G \\) with normal
+//! subgroup \\(G' \leq G\\), a group homomorphism \\( \phi : G
+//! \rightarrow H \\) induces a homomorphism
+//! $$
+//! \bar{\phi} : \frac G {G'} \longrightarrow \frac {\phi(G)}{\phi(G')} \leq \frac {H} {\phi(G')},
+//! $$
+//! and that the induced homomorphism \\(\bar{\phi}\\) is injective if
+//! \\( \ker \phi \leq G' \\). In our context, the kernel of
+//! \\(\theta\\) is \\( \\{(0, \pm 1)\\} \leq \mathcal J[2] \\),
+//! so \\(\theta\\) gives an isomorphism
+//! $$
+//! \frac {\mathcal J} {\mathcal J[2]}
+//! \cong
+//! \frac {\theta(\mathcal J)} {\theta(\mathcal J[2])}
+//! \cong
+//! \frac {\[2\](\mathcal E)} {\mathcal E[2]}.
+//! $$
+//!
+//! We can use the isomorphism to transfer the encoding of \\(\mathcal
+//! J / \mathcal J[2] \\) defined above to \\(\[2\](\mathcal E)/\mathcal
+//! E[2]\\), by encoding the Edwards point \\((x,y)\\) using the Jacobi
+//! quartic encoding of \\(\theta\^{-1}(x,y)\\).
+//!
+//! Since \\(\\# (\[2\](\mathcal E) / \mathcal E[2]) = (\\#\mathcal
+//! E)/4\\), if \\(\mathcal E\\) has cofactor \\(4\\), we're done.
+//! Otherwise, if \\(\mathcal E\\) has cofactor \\(8\\), as in the
+//! Curve25519 case, we use the torquing procedure to lift \\(\mathcal E
+//! / \mathcal E[4]\\) to \\(\mathcal E / \mathcal E[2]\\), and then
+//! apply the encoding for \\( \[2\](\mathcal E) / \mathcal E[2] \\).
+//!
+//! ## The Ristretto Encoding
+//!
+//! We can write the above encoding/decoding procedure concretely (in affine
+//! coordinates) as follows:
+//!
+//! ### Encoding
+//!
+//! On input \\( (x,y) \in \[2\](\mathcal E)\\), a representative for a
+//! coset in \\( \[2\](\mathcal E) / \mathcal E[4] \\):
+//!
+//! 1. Check if \\( xy \\) is negative or \\( x = 0 \\); if so, torque
+//! the point by setting \\( (x,y) \gets (x,y) + P_4 \\), where
+//! \\(P_4\\) is a \\(4\\)-torsion point.
+//!
+//! 2. Check if \\(x\\) is negative or \\( y = -1 \\); if so, set
+//! \\( (x,y) \gets (x,y) + (0,-1) = (-x, -y) \\).
+//!
+//! 3. Compute $$ s = +\sqrt {(-a) \frac {1 - y} {1 + y} }, $$ choosing
+//! the positive square root.
+//!
+//! The output is then the (canonical) byte-encoding of \\(s\\).
+//!
+//! If \\(\mathcal E\\) has cofactor \\(4\\), we skip the first step,
+//! since our input already represents a coset in
+//! \\( \[2\](\mathcal E) / \mathcal E[2] \\).
+//!
+//! To see that this corresponds to the encoding procedure above, notice
+//! that the first step lifts from \\( \mathcal E / \mathcal E[4] \\) to
+//! \\(\mathcal E / \mathcal E[2]\\). To understand steps 2 and 3,
+//! notice that the \\(y\\)-coordinate of \\(\theta(s,t)\\) is
+//! $$
+//! y = \frac {1 + as\^2}{1 - as\^2},
+//! $$
+//! so that the \\(s\\)-coordinate of \\(\theta\^{-1}(x,y)\\) has
+//! $$
+//! s\^2 = (-a)\frac {1-y}{1+y}.
+//! $$
+//! Since
+//! $$
+//! x = \frac 1 {\sqrt {ad - 1}} \frac {2s} {t},
+//! $$
+//! we also have
+//! $$
+//! \frac s t = x \frac {\sqrt {ad-1}} 2,
+//! $$
+//! so that the sign of \\(s/t\\) is determined by the sign of \\(x\\).
+//!
+//! Recall that to choose a canonical representative of \\( (s,t) +
+//! \mathcal J[2] \\), it's sufficient to make two sign choices: the
+//! sign of \\(s\\) and the sign of \\(s/t\\). Step 2 determines the
+//! sign of \\(s/t\\), while step 3 computes \\(s\\) and determines its
+//! sign (by choosing the positive square root). Finally, the check
+//! that \\(y \neq -1\\) prevents division-by-zero when encoding the
+//! identity; it falls out of the optimized formulas below.
+//!
+//! ### Decoding
+//!
+//! On input `s_bytes`, decoding proceeds as follows:
+//!
+//! 1. Decode `s_bytes` to \\(s\\); reject if `s_bytes` is not the
+//! canonical encoding of \\(s\\).
+//!
+//! 2. Check whether \\(s\\) is negative; if so, reject.
+//!
+//! 3. Compute
+//! $$
+//! y \gets \frac {1 + as\^2}{1 - as\^2}.
+//! $$
+//!
+//! 4. Compute
+//! $$
+//! x \gets +\sqrt{ \frac{4s\^2} {ad(1+as\^2)\^2 - (1-as\^2)\^2}},
+//! $$
+//! choosing the positive square root, or reject if the square root does
+//! not exist.
+//!
+//! 5. Check whether \\(xy\\) is negative or \\(y = 0\\); if so, reject.
+//!
+//! ## Encoding in Extended Coordinates
+//!
+//! The formulas above are given in affine coordinates, but the usual
+//! internal representation is extended twisted Edwards coordinates \\(
+//! (X:Y:Z:T) \\) with \\( x = X/Z \\), \\(y = Y/Z\\), \\(xy = T/Z \\).
+//! Selecting the distinguished representative of the coset
+//! requires the affine coordinates \\( (x,y) \\), and computing \\( s
+//! \\) requires an inverse square root.
+//! As inversions are expensive, we'd like to be able to do this
+//! whole computation with only one inverse square root, by batching
+//! together the inversion and the inverse square root.
+//!
+//! However, it is not obvious how to do this, since the inverse square
+//! root computation depends on the affine coordinates (which select the
+//! distinguished representative).
+//!
+//! In what follows we consider only the case
+//! \\(a = -1\\); a similar argument applies to the case \\( a = 1\\).
+//!
+//! Since \\(y = Y/Z\\), in extended coordinates the formula for \\(s\\) becomes
+//! $$
+//! s = \sqrt{ \frac{ 1 - Y/Z}{1+Y/Z}} = \sqrt{\frac{Z - Y}{Z+Y}}
+//! = \frac {Z - Y} {\sqrt{Z\^2 - Y\^2}}.
+//! $$
+//!
+//! Here \\( (X:Y:Z:T) \\) are the coordinates of the distinguished
+//! representative of the coset.
+//! Write \\( (X\_0 : Y\_0 : Z\_0 : T\_0) \\)
+//! for the coordinates of the initial representative. Then the
+//! torquing procedure in step 1 replaces \\( (X\_0 : Y\_0 : Z\_0 :
+//! T\_0) \\) by \\( (iY\_0 : iX\_0 : Z\_0 : -T\_0) \\). This means we
+//! want to obtain either
+//! $$
+//! \frac {1} { \sqrt{Z\_0\^2 - Y\_0\^2}}
+//! \quad \text{or} \quad
+//! \frac {1} { \sqrt{Z\_0\^2 + X\_0\^2}}.
+//! $$
+//!
+//! We can relate these using the identity
+//! $$
+//! (a-d)X\^2Y\^2 = (Z\^2 - aX\^2)(Z\^2 - Y\^2),
+//! $$
+//! which is valid for all curve points. To see this, recall from the curve equation that
+//! $$
+//! -dX\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2,
+//! $$
+//! so that
+//! $$
+//! (a-d)X\^2Y\^2 = Z\^4 - aZ\^2X\^2 - Z\^2Y\^2 + aX\^2Y\^2 = (Z\^2 - Y\^2)(Z\^2 + X\^2).
+//! $$
+//!
+//! The encoding procedure is as follows:
+//!
+//! 1. \\(u\_1 \gets (Z\_0 + Y\_0)(Z\_0 - Y\_0) = Z\_0\^2 - Y\_0\^2 \\)
+//! 2. \\(u\_2 \gets X\_0 Y\_0 \\)
+//! 3. \\(I \gets \mathrm{invsqrt}(u\_1 u\_2\^2) = 1/\sqrt{X\_0\^2 Y\_0\^2 (Z\_0\^2 - Y\_0\^2)} \\)
+//! 4. \\(D\_1 \gets u\_1 I = \sqrt{(Z\_0\^2 - Y\_0\^2)/(X\_0\^2 Y\_0\^2)} \\)
+//! 5. \\(D\_2 \gets u\_2 I = \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} \\)
+//! 6. \\(Z\_{inv} \gets D\_1 D\_2 T\_0 = (u\_1 u\_2)/(u\_1 u\_2\^2) T\_0 = T\_0 / X\_0 Y\_0 = 1/Z\_0 \\)
+//! 7. If \\( T\_0 Z\_{inv} = x\_0 y\_0 \\) is negative:
+//! 1. \\( X \gets iY\_0 \\)
+//! 2. \\( Y \gets iX\_0 \\)
+//! 3. \\( D \gets D\_1 / \sqrt{a-d} = 1/\sqrt{Z\_0\^2 + X\_0\^2} \\)
+//! 8. Otherwise:
+//! 1. \\( X \gets X\_0 \\)
+//! 2. \\( Y \gets Y\_0 \\)
+//! 3. \\( D \gets D\_2 = \pm \sqrt{1/(Z\_0\^2 - Y\_0\^2)} \\)
+//! 9. If \\( X Z\_{inv} = x \\) is negative, set \\( Y \gets - Y\\)
+//! 10. Compute \\( s \gets (Z - Y) D = (Z - Y) / \sqrt{Z\^2 - Y\^2} \\) and return.
+//!
+//! ## Decoding to Extended Coordinates
+//!
+//! ## Equality Testing
+//!
+//! ## Elligator
+//!
+//! ## The Double-Ristretto Encoding
+//!
+//! It's possible to do batch encoding of \\( [2]P \\) using the dual
+//! isogeny \\(\hat{\theta}\\). Defer this for now.
+//!
+//! ## ???
+
+// We allow non snake_case names because coordinates in projective space are
+// traditionally denoted by the capitalisation of their respective
+// counterparts in affine space. Yeah, you heard me, rustc, I'm gonna have my
+// affine and projective cakes and eat both of them too.
+#![allow(non_snake_case)]
+
+use core::fmt::Debug;
+
+#[cfg(feature = "std")]
+use rand::Rng;
+
+use digest::Digest;
+use generic_array::typenum::U32;
+
+use constants;
+use field::FieldElement;
+
+use core::ops::{Add, Sub, Neg};
+use core::ops::{AddAssign, SubAssign};
+use core::ops::{Mul, MulAssign};
+
+use edwards;
+use edwards::ExtendedPoint;
+use edwards::CompletedPoint;
+use edwards::EdwardsBasepointTable;
+use edwards::Identity;
+use scalar::Scalar;
+
+use subtle;
+use subtle::ConditionallyAssignable;
+use subtle::ConditionallyNegatable;
+use subtle::Equal;
+
+// ------------------------------------------------------------------------
+// Compressed points
+// ------------------------------------------------------------------------
+
+/// A point serialized using Mike Hamburg's Ristretto scheme.
+///
+/// XXX think about how this API should work
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub struct CompressedRistretto(pub [u8; 32]);
+
+/// The result of compressing a `RistrettoPoint`.
+impl CompressedRistretto {
+ /// View this `CompressedRistretto` as an array of bytes.
+ pub fn as_bytes<'a>(&'a self) -> &'a [u8; 32] {
+ &self.0
+ }
+
+ /// Attempt to decompress to an `RistrettoPoint`.
+ ///
+ /// This function executes in constant time for all valid inputs.
+ /// Inputs which do not decode to a RistrettoPoint may return
+ /// early.
+ pub fn decompress(&self) -> Option {
+ // Step 1. Check s for validity:
+ // 1.a) s must be 32 bytes (we get this from the type system)
+ // 1.b) s < p
+ // 1.c) s is nonnegative
+ //
+ // Our decoding routine ignores the high bit, so the only
+ // possible failure for 1.b) is if someone encodes s in 0..18
+ // as s+p in 2^255-19..2^255-1. We can check this by
+ // converting back to bytes, and checking that we get the
+ // original input, since our encoding routine is canonical.
+
+ let s = FieldElement::from_bytes(self.as_bytes());
+ let s_bytes_check = s.to_bytes();
+ let s_encoding_is_canonical =
+ subtle::slices_equal(&s_bytes_check[..], self.as_bytes());
+ let s_is_negative = s.is_negative();
+
+ if s_encoding_is_canonical == 0u8 || s_is_negative == 1u8 {
+ return None;
+ }
+
+ // Step 2. The rest. (XXX write comments)
+ let one = FieldElement::one();
+ let ss = s.square();
+ let yden = &one + &ss; // 1 - a*s^2
+ let ynum = &one - &ss; // 1 + a*s^2
+ let yden_sqr = yden.square();
+ let xden_sqr = &(&(-&constants::d) * &ynum.square()) - &yden_sqr;
+
+ let (ok, invsqrt) = (&xden_sqr * &yden_sqr).invsqrt();
+
+ let xden_inv = &invsqrt * &yden;
+ let yden_inv = &invsqrt * &(&xden_inv * &xden_sqr);
+
+ let mut x = &(&s + &s) * &xden_inv; // 2*s*xden_inv
+ let x_is_negative = x.is_negative();
+ x.conditional_negate(x_is_negative);
+ let y = &ynum * &yden_inv;
+
+ let t = &x * &y;
+
+ if ok == 0u8 || t.is_negative() == 1u8 || y.is_zero() == 1u8 {
+ return None;
+ } else {
+ return Some(RistrettoPoint(ExtendedPoint{X: x, Y: y, Z: one, T: t}));
+ }
+ }
+}
+
+impl Identity for CompressedRistretto {
+ fn identity() -> CompressedRistretto {
+ CompressedRistretto([0u8; 32])
+ }
+}
+
+// ------------------------------------------------------------------------
+// Serde support
+// ------------------------------------------------------------------------
+// Serializes to and from `RistrettoPoint` directly, doing compression
+// and decompression internally. This means that users can create
+// structs containing `RistrettoPoint`s and use Serde's derived
+// serializers to serialize those structures.
+
+#[cfg(feature = "serde")]
+use serde::{self, Serialize, Deserialize, Serializer, Deserializer};
+#[cfg(feature = "serde")]
+use serde::de::Visitor;
+
+#[cfg(feature = "serde")]
+impl Serialize for RistrettoPoint {
+ fn serialize
(&self, serializer: S) -> Result
+ where S: Serializer
+ {
+ serializer.serialize_bytes(self.compress().as_bytes())
+ }
+}
+
+#[cfg(feature = "serde")]
+impl<'de> Deserialize<'de> for RistrettoPoint {
+ fn deserialize(deserializer: D) -> Result
+ where D: Deserializer<'de>
+ {
+ struct RistrettoPointVisitor;
+
+ impl<'de> Visitor<'de> for RistrettoPointVisitor {
+ type Value = RistrettoPoint;
+
+ fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ formatter.write_str("a valid point in Ristretto format")
+ }
+
+ fn visit_bytes(self, v: &[u8]) -> Result
+ where E: serde::de::Error
+ {
+ if v.len() == 32 {
+ let arr32 = array_ref!(v, 0, 32); // &[u8;32] from &[u8]
+ CompressedRistretto(*arr32)
+ .decompress()
+ .ok_or(serde::de::Error::custom("decompression failed"))
+ } else {
+ Err(serde::de::Error::invalid_length(v.len(), &self))
+ }
+ }
+ }
+
+ deserializer.deserialize_bytes(RistrettoPointVisitor)
+ }
+}
+
+// ------------------------------------------------------------------------
+// Internal point representations
+// ------------------------------------------------------------------------
+
+/// A `RistrettoPoint` represents a point in the Ristretto group for
+/// Curve25519. Ristretto, a variant of Decaf, constructs a
+/// prime-order group as a quotient group of a subgroup of (the
+/// Edwards form of) Curve25519.
+///
+/// Internally, a `RistrettoPoint` is a wrapper type around
+/// `ExtendedPoint`, with custom equality, compression, and
+/// decompression routines to account for the quotient.
+#[derive(Copy, Clone)]
+pub struct RistrettoPoint(pub ExtendedPoint);
+
+impl RistrettoPoint {
+ /// Compress in Ristretto format.
+ ///
+ /// # Implementation Notes
+ ///
+ /// The Ristretto encoding is as follows, on input in affine coordinates `(x,y)`:
+ ///
+ /// 1. If `xy` is negative or `x = 0`, "rotate" the point by
+ /// setting `(x,y) = (iy, ix)`.
+ /// 2. If `x` is negative, set `(x,y) = (-x, -y)`.
+ /// 3. Compute `s = +sqrt((1-y)/(1+y))`.
+ /// 4. Return the little-endian 32-byte encoding of `s`.
+ ///
+ /// However, our input is in extended twisted Edwards coordinates
+ /// `(X:Y:Z:T)` with `x = X/Z`, `y = Y/Z`, `xy = T/Z` (see the
+ /// module-level documentation on curve representations for more
+ /// details). Since inversions are expensive, we'd like to be
+ /// able to do this whole computation with only one inversion.
+ ///
+ /// Since `y = Y/Z`, in extended coordinates the formula for `s` becomes
+ ///
+ /// s = sqrt((1 - Y/Z)/(1 + Y/Z)) = sqrt((Z-Y)/(Z+Y)). (1)
+ ///
+ /// We can compute this as
+ ///
+ /// s = (Z - Y) / sqrt((Z-Y)(Z+Y)). (1)
+ ///
+ /// The denominator is
+ ///
+ /// invsqrt((Z-Y)(Z+Y)) = invsqrt(Z² - Y²). (1)
+ ///
+ /// Write the input point as `(X₀:Y₀:Z₀:T₀)`. The rotation in
+ /// step 1 of the encoding procedure replaces `(X₀:Y₀:Z₀:T₀)` by
+ /// `(iY₀:iX₀:Z₀:-T₀)`. We therefore wish to relate the
+ /// computation of
+ ///
+ /// invsqrt(Z² - Y²) = invsqrt(Z₀² - Y₀²) [non-rotated case]
+ ///
+ /// with the computation of
+ ///
+ /// invsqrt(Z² - Y²) = invsqrt(Z₀² + X₀²). [rotated case]
+ ///
+ /// Recall the curve equation (in the 𝗣² model):
+ ///
+ /// (-X² + Y²)Z² = Z⁴ + dX²Y². (1)
+ ///
+ /// This means that, for any point `(X:Y:Z:T)` in extended coordinates, we have
+ ///
+ /// -dX²Y² = Z⁴ + Z²X² - Z²Y², (2)
+ ///
+ /// so that
+ ///
+ /// (-1-d)X²Y² = Z⁴ + Z²X² - Z²Y² - X²Y², (3)
+ ///
+ /// and hence
+ ///
+ /// (-1-d)X²Y² = (Z² - Y²)(Z² + X²). (4)
+ ///
+ /// Taking inverse square roots gives
+ ///
+ /// invsqrt(Z² + X²) = invsqrt(-1-d) sqrt((Z² - Y²)/(X²Y²)). (4)
+ ///
+ ///
+ pub fn compress(&self) -> CompressedRistretto {
+ let mut X = self.0.X;
+ let mut Y = self.0.Y;
+ let Z = &self.0.Z;
+ let T = &self.0.T;
+
+ let u1 = &(Z + &Y) * &(Z - &Y);
+ let u2 = &X * &Y;
+ // Ignore return value since this is always square
+ let (_, invsqrt) = (&u1 * &u2.square()).invsqrt();
+ let i1 = &invsqrt * &u1;
+ let i2 = &invsqrt * &u2;
+ let z_inv = &i1 * &(&i2 * T);
+ let mut den_inv = i2;
+
+ let iX = &X * &constants::SQRT_M1;
+ let iY = &Y * &constants::SQRT_M1;
+ let ristretto_magic = &constants::invsqrt_a_minus_d;
+ let enchanted_denominator = &i1 * ristretto_magic;
+
+ let rotate = (T * &z_inv).is_negative();
+
+ X.conditional_assign(&iY, rotate);
+ Y.conditional_assign(&iX, rotate);
+ den_inv.conditional_assign(&enchanted_denominator, rotate);
+
+ Y.conditional_negate((&X * &z_inv).is_negative());
+
+ let mut s = &den_inv * &(Z - &Y);
+ let s_is_negative = s.is_negative();
+ s.conditional_negate(s_is_negative);
+
+ CompressedRistretto(s.to_bytes())
+ }
+
+ /// Return the coset self + E[4], for debugging.
+ fn coset4(&self) -> [ExtendedPoint; 4] {
+ [ self.0
+ , &self.0 + &constants::EIGHT_TORSION[2]
+ , &self.0 + &constants::EIGHT_TORSION[4]
+ , &self.0 + &constants::EIGHT_TORSION[6]
+ ]
+ }
+
+ /// Computes the Ristretto Elligator map.
+ ///
+ /// # Note
+ ///
+ /// This method is not public because it's just used for hashing
+ /// to a point -- proper elligator support is deferred for now.
+ pub fn elligator_ristretto_flavour(r_0: &FieldElement) -> RistrettoPoint {
+ let (i, d) = (&constants::SQRT_M1, &constants::d);
+ let one = FieldElement::one();
+
+ let r = i * &r_0.square();
+
+ // D = (dr -a)(ar-d) = -(dr+1)(r+d)
+ let D = -&( &(&(d * &r) + &one) * &(&r + d) );
+ // N = a(d-a)(d+a)(r+1) = -(r+1)(d^2 -1)
+ let d_sq = d.square();
+ let N = -&( &(&d_sq - &one) * &(&r + &one) );
+
+ let mut s = FieldElement::zero();
+ let mut c = -&one;
+
+ let (N_over_D_is_square, maybe_s) = FieldElement::sqrt_ratio(&N, &D);
+ // s = sqrt(N/D) if N/D is square
+ s.conditional_assign(&maybe_s, N_over_D_is_square);
+
+ // XXX how do we reuse the computation of sqrt(N/D) to find sqrt(rN/D) ?
+ let (rN_over_D_is_square, mut maybe_s) = FieldElement::sqrt_ratio(&(&r*&N), &D);
+ maybe_s.negate();
+
+ // s = -sqrt(rN/D) if rN/D is square (should happen exactly when N/D is nonsquare)
+ debug_assert_eq!(N_over_D_is_square ^ rN_over_D_is_square, 1u8);
+ s.conditional_assign(&maybe_s, rN_over_D_is_square);
+ c.conditional_assign(&r, rN_over_D_is_square);
+
+ // T = (c * (r - one) * (d-one).square()) - D;
+ let T = &(&c * &(&(&r - &one) * &((d - &one).square()))) - &D;
+
+ let s_sq = s.square();
+ let P = CompletedPoint{
+ X: &(&s + &s) * &D,
+ Z: &T * &constants::sqrt_ad_minus_one,
+ Y: &FieldElement::one() - &s_sq,
+ T: &FieldElement::one() + &s_sq,
+ };
+
+ // Convert to extended and return.
+ RistrettoPoint(P.to_extended())
+ }
+
+ /// Return a `RistrettoPoint` chosen uniformly at random using a user-provided RNG.
+ ///
+ /// # Inputs
+ ///
+ /// * `rng`: any RNG which implements the `rand::Rng` interface.
+ ///
+ /// # Returns
+ ///
+ /// A random element of the Ristretto group.
+ ///
+ /// # Implementation
+ ///
+ /// Uses the Ristretto-flavoured Elligator 2 map, so that the discrete log of the
+ /// output point with respect to any other point should be unknown.
+ #[cfg(feature = "std")]
+ pub fn random(rng: &mut T) -> Self {
+ let mut field_bytes = [0u8; 32];
+ rng.fill_bytes(&mut field_bytes);
+ let r_0 = FieldElement::from_bytes(&field_bytes);
+ RistrettoPoint::elligator_ristretto_flavour(&r_0)
+ }
+
+ /// Hash a slice of bytes into a `RistrettoPoint`.
+ ///
+ /// Takes a type parameter `D`, which is any `Digest` producing 32
+ /// bytes (256 bits) of output.
+ ///
+ /// Convenience wrapper around `from_hash`.
+ ///
+ /// # Implementation
+ ///
+ /// Uses the Ristretto-flavoured Elligator 2 map, so that the discrete log of the
+ /// output point with respect to any other point should be unknown.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # extern crate curve25519_dalek;
+ /// # use curve25519_dalek::ristretto::RistrettoPoint;
+ /// extern crate sha2;
+ /// use sha2::Sha256;
+ ///
+ /// # // Need fn main() here in comment so the doctest compiles
+ /// # // See https://doc.rust-lang.org/book/documentation.html#documentation-as-tests
+ /// # fn main() {
+ /// let msg = "To really appreciate architecture, you may even need to commit a murder";
+ /// let P = RistrettoPoint::hash_from_bytes::(msg.as_bytes());
+ /// # }
+ /// ```
+ ///
+ pub fn hash_from_bytes(input: &[u8]) -> RistrettoPoint
+ where D: Digest + Default
+ {
+ let mut hash = D::default();
+ hash.input(input);
+ RistrettoPoint::from_hash(hash)
+ }
+
+ /// Construct a `RistrettoPoint` from an existing `Digest` instance.
+ ///
+ /// Use this instead of `hash_from_bytes` if it is more convenient
+ /// to stream data into the `Digest` than to pass a single byte
+ /// slice.
+ pub fn from_hash(hash: D) -> RistrettoPoint
+ where D: Digest + Default
+ {
+ // XXX this seems clumsy
+ let mut output = [0u8; 32];
+ output.copy_from_slice(hash.result().as_slice());
+ let r_0 = FieldElement::from_bytes(&output);
+ RistrettoPoint::elligator_ristretto_flavour(&r_0)
+ }
+}
+
+impl Identity for RistrettoPoint {
+ fn identity() -> RistrettoPoint {
+ RistrettoPoint(ExtendedPoint::identity())
+ }
+}
+
+// ------------------------------------------------------------------------
+// Equality
+// ------------------------------------------------------------------------
+
+impl PartialEq for RistrettoPoint {
+ fn eq(&self, other: &RistrettoPoint) -> bool {
+ self.ct_eq(other) == 1u8
+ }
+}
+
+impl Equal for RistrettoPoint {
+ /// Test equality between two `RistrettoPoint`s.
+ ///
+ /// # Returns
+ ///
+ /// `1u8` if the two `RistrettoPoint`s are equal, and `0u8` otherwise.
+ fn ct_eq(&self, other: &RistrettoPoint) -> u8 {
+ let X1Y2 = &self.0.X * &other.0.Y;
+ let Y1X2 = &self.0.Y * &other.0.X;
+ let X1X2 = &self.0.X * &other.0.X;
+ let Y1Y2 = &self.0.Y * &other.0.Y;
+
+ X1Y2.ct_eq(&Y1X2) | X1X2.ct_eq(&Y1Y2)
+ }
+}
+
+impl Eq for RistrettoPoint {}
+
+// ------------------------------------------------------------------------
+// Arithmetic
+// ------------------------------------------------------------------------
+
+impl<'a, 'b> Add<&'b RistrettoPoint> for &'a RistrettoPoint {
+ type Output = RistrettoPoint;
+
+ fn add(self, other: &'b RistrettoPoint) -> RistrettoPoint {
+ RistrettoPoint(&self.0 + &other.0)
+ }
+}
+
+impl<'b> AddAssign<&'b RistrettoPoint> for RistrettoPoint {
+ fn add_assign(&mut self, _rhs: &RistrettoPoint) {
+ *self = (self as &RistrettoPoint) + _rhs;
+ }
+}
+
+impl<'a, 'b> Sub<&'b RistrettoPoint> for &'a RistrettoPoint {
+ type Output = RistrettoPoint;
+
+ fn sub(self, other: &'b RistrettoPoint) -> RistrettoPoint {
+ RistrettoPoint(&self.0 - &other.0)
+ }
+}
+
+impl<'b> SubAssign<&'b RistrettoPoint> for RistrettoPoint {
+ fn sub_assign(&mut self, _rhs: &RistrettoPoint) {
+ *self = (self as &RistrettoPoint) - _rhs;
+ }
+}
+
+impl<'a> Neg for &'a RistrettoPoint {
+ type Output = RistrettoPoint;
+
+ fn neg(self) -> RistrettoPoint {
+ RistrettoPoint(-&self.0)
+ }
+}
+
+impl<'b> MulAssign<&'b Scalar> for RistrettoPoint {
+ fn mul_assign(&mut self, scalar: &'b Scalar) {
+ let result = (self as &RistrettoPoint) * scalar;
+ *self = result;
+ }
+}
+
+impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoPoint {
+ type Output = RistrettoPoint;
+ /// Scalar multiplication: compute `scalar * self`.
+ fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
+ RistrettoPoint(&self.0 * scalar)
+ }
+}
+
+impl<'a, 'b> Mul<&'b RistrettoPoint> for &'a Scalar {
+ type Output = RistrettoPoint;
+
+ /// Scalar multiplication: compute `self * scalar`.
+ fn mul(self, point: &'b RistrettoPoint) -> RistrettoPoint {
+ RistrettoPoint(self * &point.0)
+ }
+}
+
+/// Given a vector of (possibly secret) scalars and a vector of
+/// (possibly secret) points, compute `c_1 P_1 + ... + c_n P_n`.
+///
+/// This function has the same behaviour as
+/// `vartime::multiscalar_mult` but is constant-time.
+///
+/// # Input
+///
+/// An iterable of `Scalar`s and a iterable of `DecafPoints`. It is an
+/// error to call this function with two iterators of different lengths.
+#[cfg(any(feature = "alloc", feature = "std"))]
+pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint
+ where I: IntoIterator- ,
+ J: IntoIterator
- ,
+{
+ let extended_points = points.into_iter().map(|P| &P.0);
+ RistrettoPoint(edwards::multiscalar_mult(scalars, extended_points))
+}
+
+/// Precomputation
+#[derive(Clone)]
+pub struct RistrettoBasepointTable(pub EdwardsBasepointTable);
+
+impl<'a, 'b> Mul<&'b Scalar> for &'a RistrettoBasepointTable {
+ type Output = RistrettoPoint;
+
+ fn mul(self, scalar: &'b Scalar) -> RistrettoPoint {
+ RistrettoPoint(&self.0 * scalar)
+ }
+}
+
+impl<'a, 'b> Mul<&'a RistrettoBasepointTable> for &'b Scalar {
+ type Output = RistrettoPoint;
+
+ fn mul(self, basepoint_table: &'a RistrettoBasepointTable) -> RistrettoPoint {
+ RistrettoPoint(self * &basepoint_table.0)
+ }
+}
+
+impl RistrettoBasepointTable {
+ /// Create a precomputed table of multiples of the given `basepoint`.
+ pub fn create(basepoint: &RistrettoPoint) -> RistrettoBasepointTable {
+ RistrettoBasepointTable(EdwardsBasepointTable::create(&basepoint.0))
+ }
+
+ /// Get the basepoint for this table as a `RistrettoPoint`.
+ pub fn basepoint(&self) -> RistrettoPoint {
+ RistrettoPoint(self.0.basepoint())
+ }
+}
+
+// ------------------------------------------------------------------------
+// Constant-time conditional assignment
+// ------------------------------------------------------------------------
+
+impl ConditionallyAssignable for RistrettoPoint {
+ /// Conditionally assign `other` to `self`, if `choice == 1u8`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # extern crate subtle;
+ /// # extern crate curve25519_dalek;
+ /// #
+ /// # use subtle::ConditionallyAssignable;
+ /// #
+ /// # use curve25519_dalek::edwards::Identity;
+ /// # use curve25519_dalek::ristretto::RistrettoPoint;
+ /// # use curve25519_dalek::constants;
+ /// # fn main() {
+ /// let A = RistrettoPoint::identity();
+ /// let B = constants::RISTRETTO_BASEPOINT_POINT;
+ ///
+ /// let mut P = A;
+ ///
+ /// P.conditional_assign(&B, 0u8);
+ /// assert!(P == A);
+ /// P.conditional_assign(&B, 1u8);
+ /// assert!(P == B);
+ /// # }
+ /// ```
+ fn conditional_assign(&mut self, other: &RistrettoPoint, choice: u8) {
+ self.0.X.conditional_assign(&other.0.X, choice);
+ self.0.Y.conditional_assign(&other.0.Y, choice);
+ self.0.Z.conditional_assign(&other.0.Z, choice);
+ self.0.T.conditional_assign(&other.0.T, choice);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Debug traits
+// ------------------------------------------------------------------------
+
+impl Debug for CompressedRistretto {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ write!(f, "CompressedRistretto: {:?}", self.as_bytes())
+ }
+}
+
+impl Debug for RistrettoPoint {
+ fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+ let coset = self.coset4();
+ write!(f, "RistrettoPoint: coset \n{:?}\n{:?}\n{:?}\n{:?}",
+ coset[0], coset[1], coset[2], coset[3])
+ }
+}
+
+// ------------------------------------------------------------------------
+// Variable-time functions
+// ------------------------------------------------------------------------
+
+pub mod vartime {
+ //! Variable-time operations on ristretto points, useful for non-secret data.
+ use super::*;
+
+ /// Given a vector of public scalars and a vector of (possibly secret)
+ /// points, compute
+ ///
+ /// c_1 P_1 + ... + c_n P_n.
+ ///
+ /// # Input
+ ///
+ /// A vector of `Scalar`s and a vector of `RistrettoPoints`. It is an
+ /// error to call this function with two vectors of different lengths.
+ #[cfg(any(feature = "alloc", feature = "std"))]
+ pub fn multiscalar_mult<'a, 'b, I, J>(scalars: I, points: J) -> RistrettoPoint
+ where I: IntoIterator
- ,
+ J: IntoIterator
-
+ {
+ let extended_points = points.into_iter().map(|P| &P.0);
+ RistrettoPoint(edwards::vartime::multiscalar_mult(scalars, extended_points))
+ }
+}
+
+// ------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------
+
+#[cfg(test)]
+mod test {
+ use rand::OsRng;
+
+ use scalar::Scalar;
+ use constants;
+ use edwards::CompressedEdwardsY;
+ use edwards::Identity;
+ use edwards::ValidityCheck;
+ use super::*;
+
+ #[cfg(feature = "serde")]
+ use serde_cbor;
+
+ #[test]
+ #[cfg(feature = "serde")]
+ fn serde_cbor_basepoint_roundtrip() {
+ let output = serde_cbor::to_vec(&constants::RISTRETTO_BASEPOINT_POINT).unwrap();
+ let parsed: RistrettoPoint = serde_cbor::from_slice(&output).unwrap();
+ assert_eq!(parsed, constants::RISTRETTO_BASEPOINT_POINT);
+ }
+
+ #[test]
+ fn scalarmult_ristrettopoint_works_both_ways() {
+ let P = constants::RISTRETTO_BASEPOINT_POINT;
+ let s = Scalar::from_u64(999);
+
+ let P1 = &P * &s;
+ let P2 = &s * &P;
+
+ assert!(P1.compress().as_bytes() == P2.compress().as_bytes());
+ }
+
+ #[test]
+ fn decompress_negative_s_fails() {
+ // constants::d is neg, so decompression should fail as |d| != d.
+ let bad_compressed = CompressedRistretto(constants::d.to_bytes());
+ assert!(bad_compressed.decompress().is_none());
+ }
+
+ #[test]
+ fn decompress_id() {
+ let compressed_id = CompressedRistretto::identity();
+ let id = compressed_id.decompress().unwrap();
+ let mut identity_in_coset = false;
+ for P in &id.coset4() {
+ if P.compress() == CompressedEdwardsY::identity() {
+ identity_in_coset = true;
+ }
+ }
+ assert!(identity_in_coset);
+ }
+
+ #[test]
+ fn compress_id() {
+ let id = RistrettoPoint::identity();
+ assert_eq!(id.compress(), CompressedRistretto::identity());
+ }
+
+ #[test]
+ fn basepoint_roundtrip() {
+ let bp_compressed_ristretto = constants::RISTRETTO_BASEPOINT_POINT.compress();
+ let bp_recaf = bp_compressed_ristretto.decompress().unwrap().0;
+ // Check that bp_recaf differs from bp by a point of order 4
+ let diff = &constants::RISTRETTO_BASEPOINT_POINT.0 - &bp_recaf;
+ let diff4 = diff.mult_by_pow_2(2);
+ assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
+ }
+
+ #[test]
+ fn encodings_of_small_multiples_of_basepoint() {
+ // Table of encodings of i*basepoint
+ // Generated using ristretto.sage
+ let compressed = [
+ CompressedRistretto([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, 0, 0, 0, 0]),
+ CompressedRistretto([226, 242, 174, 10, 106, 188, 78, 113, 168, 132, 169, 97, 197, 0, 81, 95, 88, 227, 11, 106, 165, 130, 221, 141, 182, 166, 89, 69, 224, 141, 45, 118]),
+ CompressedRistretto([106, 73, 50, 16, 247, 73, 156, 209, 127, 236, 181, 16, 174, 12, 234, 35, 161, 16, 232, 213, 185, 1, 248, 172, 173, 211, 9, 92, 115, 163, 185, 25]),
+ CompressedRistretto([148, 116, 31, 93, 93, 82, 117, 94, 206, 79, 35, 240, 68, 238, 39, 213, 209, 234, 30, 43, 209, 150, 180, 98, 22, 107, 22, 21, 42, 157, 2, 89]),
+ CompressedRistretto([218, 128, 134, 39, 115, 53, 139, 70, 111, 250, 223, 224, 179, 41, 58, 179, 217, 253, 83, 197, 234, 108, 149, 83, 88, 245, 104, 50, 45, 175, 106, 87]),
+ CompressedRistretto([232, 130, 177, 49, 1, 107, 82, 193, 211, 51, 112, 128, 24, 124, 247, 104, 66, 62, 252, 203, 181, 23, 187, 73, 90, 184, 18, 196, 22, 15, 244, 78]),
+ CompressedRistretto([246, 71, 70, 211, 201, 43, 19, 5, 14, 216, 216, 2, 54, 167, 240, 0, 124, 59, 63, 150, 47, 91, 167, 147, 209, 154, 96, 30, 187, 29, 244, 3]),
+ CompressedRistretto([68, 245, 53, 32, 146, 110, 200, 31, 189, 90, 56, 120, 69, 190, 183, 223, 133, 169, 106, 36, 236, 225, 135, 56, 189, 207, 166, 167, 130, 42, 23, 109]),
+ CompressedRistretto([144, 50, 147, 216, 242, 40, 126, 190, 16, 226, 55, 77, 193, 165, 62, 11, 200, 135, 229, 146, 105, 159, 2, 208, 119, 213, 38, 60, 221, 85, 96, 28]),
+ CompressedRistretto([2, 98, 42, 206, 143, 115, 3, 163, 28, 175, 198, 63, 143, 196, 143, 220, 22, 225, 200, 200, 210, 52, 178, 240, 214, 104, 82, 130, 169, 7, 96, 49]),
+ CompressedRistretto([32, 112, 111, 215, 136, 178, 114, 10, 30, 210, 165, 218, 212, 149, 43, 1, 244, 19, 188, 240, 231, 86, 77, 232, 205, 200, 22, 104, 158, 45, 185, 95]),
+ CompressedRistretto([188, 232, 63, 139, 165, 221, 47, 165, 114, 134, 76, 36, 186, 24, 16, 249, 82, 43, 198, 0, 74, 254, 149, 135, 122, 199, 50, 65, 202, 253, 171, 66]),
+ CompressedRistretto([228, 84, 158, 225, 107, 154, 160, 48, 153, 202, 32, 140, 103, 173, 175, 202, 250, 76, 63, 62, 78, 83, 3, 222, 96, 38, 227, 202, 143, 248, 68, 96]),
+ CompressedRistretto([170, 82, 224, 0, 223, 46, 22, 245, 95, 177, 3, 47, 195, 59, 196, 39, 66, 218, 214, 189, 90, 143, 192, 190, 1, 103, 67, 108, 89, 72, 80, 31]),
+ CompressedRistretto([70, 55, 107, 128, 244, 9, 178, 157, 194, 181, 246, 240, 197, 37, 145, 153, 8, 150, 229, 113, 111, 65, 71, 124, 211, 0, 133, 171, 127, 16, 48, 30]),
+ CompressedRistretto([224, 196, 24, 247, 200, 217, 196, 205, 215, 57, 91, 147, 234, 18, 79, 58, 217, 144, 33, 187, 104, 29, 252, 51, 2, 169, 217, 154, 46, 83, 230, 78]),
+ ];
+ let mut bp = RistrettoPoint::identity();
+ for i in 0..16 {
+ assert_eq!(bp.compress(), compressed[i]);
+ bp = &bp + &constants::RISTRETTO_BASEPOINT_POINT;
+ }
+ }
+
+ #[test]
+ fn four_torsion_basepoint() {
+ let bp = constants::RISTRETTO_BASEPOINT_POINT;
+ let bp_coset = bp.coset4();
+ for i in 0..4 {
+ assert_eq!(bp, RistrettoPoint(bp_coset[i]));
+ }
+ }
+
+ #[test]
+ fn four_torsion_random() {
+ let mut rng = OsRng::new().unwrap();
+ let B = &constants::RISTRETTO_BASEPOINT_TABLE;
+ let P = B * &Scalar::random(&mut rng);
+ let P_coset = P.coset4();
+ for i in 0..4 {
+ assert_eq!(P, RistrettoPoint(P_coset[i]));
+ }
+ }
+
+ #[test]
+ fn elligator_vs_ristretto_sage() {
+ // Test vectors extracted from ristretto.sage.
+ //
+ // Notice that all of the byte sequences have bit 255 set to 0; this is because
+ // ristretto.sage does not mask the high bit of a field element. When the high bit is set,
+ // the ristretto.sage elligator implementation gives different results, since it takes a
+ // different field element as input.
+ let bytes: [[u8;32]; 16] = [
+ [184, 249, 135, 49, 253, 123, 89, 113, 67, 160, 6, 239, 7, 105, 211, 41, 192, 249, 185, 57, 9, 102, 70, 198, 15, 127, 7, 26, 160, 102, 134, 71],
+ [229, 14, 241, 227, 75, 9, 118, 60, 128, 153, 226, 21, 183, 217, 91, 136, 98, 0, 231, 156, 124, 77, 82, 139, 142, 134, 164, 169, 169, 62, 250, 52],
+ [115, 109, 36, 220, 180, 223, 99, 6, 204, 169, 19, 29, 169, 68, 84, 23, 21, 109, 189, 149, 127, 205, 91, 102, 172, 35, 112, 35, 134, 69, 186, 34],
+ [16, 49, 96, 107, 171, 199, 164, 9, 129, 16, 64, 62, 241, 63, 132, 173, 209, 160, 112, 215, 105, 50, 157, 81, 253, 105, 1, 154, 229, 25, 120, 83],
+ [156, 131, 161, 162, 236, 251, 5, 187, 167, 171, 17, 178, 148, 210, 90, 207, 86, 21, 79, 161, 167, 215, 234, 1, 136, 242, 182, 248, 38, 85, 79, 86],
+ [251, 177, 124, 54, 18, 101, 75, 235, 245, 186, 19, 46, 133, 157, 229, 64, 10, 136, 181, 185, 78, 144, 254, 167, 137, 49, 107, 10, 61, 10, 21, 25],
+ [232, 193, 20, 68, 240, 77, 186, 77, 183, 40, 44, 86, 150, 31, 198, 212, 76, 81, 3, 217, 197, 8, 126, 128, 126, 152, 164, 208, 153, 44, 189, 77],
+ [173, 229, 149, 177, 37, 230, 30, 69, 61, 56, 172, 190, 219, 115, 167, 194, 71, 134, 59, 75, 28, 244, 118, 26, 162, 97, 64, 16, 15, 189, 30, 64],
+ [106, 71, 61, 107, 250, 117, 42, 151, 91, 202, 212, 100, 52, 188, 190, 21, 125, 218, 31, 18, 253, 241, 160, 133, 57, 242, 3, 164, 189, 68, 111, 75],
+ [112, 204, 182, 90, 220, 198, 120, 73, 173, 107, 193, 17, 227, 40, 162, 36, 150, 141, 235, 55, 172, 183, 12, 39, 194, 136, 43, 153, 244, 118, 91, 89],
+ [111, 24, 203, 123, 254, 189, 11, 162, 51, 196, 163, 136, 204, 143, 10, 222, 33, 112, 81, 205, 34, 35, 8, 66, 90, 6, 164, 58, 170, 177, 34, 25],
+ [225, 183, 30, 52, 236, 82, 6, 183, 109, 25, 227, 181, 25, 82, 41, 193, 80, 77, 161, 80, 242, 203, 79, 204, 136, 245, 131, 110, 237, 106, 3, 58],
+ [207, 246, 38, 56, 30, 86, 176, 90, 27, 200, 61, 42, 221, 27, 56, 210, 79, 178, 189, 120, 68, 193, 120, 167, 77, 185, 53, 197, 124, 128, 191, 126],
+ [1, 136, 215, 80, 240, 46, 63, 147, 16, 244, 230, 207, 82, 189, 74, 50, 106, 169, 138, 86, 30, 131, 214, 202, 166, 125, 251, 228, 98, 24, 36, 21],
+ [210, 207, 228, 56, 155, 116, 207, 54, 84, 195, 251, 215, 249, 199, 116, 75, 109, 239, 196, 251, 194, 246, 252, 228, 70, 146, 156, 35, 25, 39, 241, 4],
+ [34, 116, 123, 9, 8, 40, 93, 189, 9, 103, 57, 103, 66, 227, 3, 2, 157, 107, 134, 219, 202, 74, 230, 154, 78, 107, 219, 195, 214, 14, 84, 80],
+ ];
+ let encoded_images: [CompressedRistretto; 16] = [
+ CompressedRistretto([176, 157, 237, 97, 66, 29, 140, 166, 168, 94, 26, 157, 212, 216, 229, 160, 195, 246, 232, 239, 169, 112, 63, 193, 64, 32, 152, 69, 11, 190, 246, 86]),
+ CompressedRistretto([234, 141, 77, 203, 181, 225, 250, 74, 171, 62, 15, 118, 78, 212, 150, 19, 131, 14, 188, 238, 194, 244, 141, 138, 166, 162, 83, 122, 228, 201, 19, 26]),
+ CompressedRistretto([232, 231, 51, 92, 5, 168, 80, 36, 173, 179, 104, 68, 186, 149, 68, 40, 140, 170, 27, 103, 99, 140, 21, 242, 43, 62, 250, 134, 208, 255, 61, 89]),
+ CompressedRistretto([208, 120, 140, 129, 177, 179, 237, 159, 252, 160, 28, 13, 206, 5, 211, 241, 192, 218, 1, 97, 130, 241, 20, 169, 119, 46, 246, 29, 79, 80, 77, 84]),
+ CompressedRistretto([202, 11, 236, 145, 58, 12, 181, 157, 209, 6, 213, 88, 75, 147, 11, 119, 191, 139, 47, 142, 33, 36, 153, 193, 223, 183, 178, 8, 205, 120, 248, 110]),
+ CompressedRistretto([26, 66, 231, 67, 203, 175, 116, 130, 32, 136, 62, 253, 215, 46, 5, 214, 166, 248, 108, 237, 216, 71, 244, 173, 72, 133, 82, 6, 143, 240, 104, 41]),
+ CompressedRistretto([40, 157, 102, 96, 201, 223, 200, 197, 150, 181, 106, 83, 103, 126, 143, 33, 145, 230, 78, 6, 171, 146, 210, 143, 112, 5, 245, 23, 183, 138, 18, 120]),
+ CompressedRistretto([220, 37, 27, 203, 239, 196, 176, 131, 37, 66, 188, 243, 185, 250, 113, 23, 167, 211, 154, 243, 168, 215, 54, 171, 159, 36, 195, 81, 13, 150, 43, 43]),
+ CompressedRistretto([232, 121, 176, 222, 183, 196, 159, 90, 238, 193, 105, 52, 101, 167, 244, 170, 121, 114, 196, 6, 67, 152, 80, 185, 221, 7, 83, 105, 176, 208, 224, 121]),
+ CompressedRistretto([226, 181, 183, 52, 241, 163, 61, 179, 221, 207, 220, 73, 245, 242, 25, 236, 67, 84, 179, 222, 167, 62, 167, 182, 32, 9, 92, 30, 165, 127, 204, 68]),
+ CompressedRistretto([226, 119, 16, 242, 200, 139, 240, 87, 11, 222, 92, 146, 156, 243, 46, 119, 65, 59, 1, 248, 92, 183, 50, 175, 87, 40, 206, 53, 208, 220, 148, 13]),
+ CompressedRistretto([70, 240, 79, 112, 54, 157, 228, 146, 74, 122, 216, 88, 232, 62, 158, 13, 14, 146, 115, 117, 176, 222, 90, 225, 244, 23, 94, 190, 150, 7, 136, 96]),
+ CompressedRistretto([22, 71, 241, 103, 45, 193, 195, 144, 183, 101, 154, 50, 39, 68, 49, 110, 51, 44, 62, 0, 229, 113, 72, 81, 168, 29, 73, 106, 102, 40, 132, 24]),
+ CompressedRistretto([196, 133, 107, 11, 130, 105, 74, 33, 204, 171, 133, 221, 174, 193, 241, 36, 38, 179, 196, 107, 219, 185, 181, 253, 228, 47, 155, 42, 231, 73, 41, 78]),
+ CompressedRistretto([58, 255, 225, 197, 115, 208, 160, 143, 39, 197, 82, 69, 143, 235, 92, 170, 74, 40, 57, 11, 171, 227, 26, 185, 217, 207, 90, 185, 197, 190, 35, 60]),
+ CompressedRistretto([88, 43, 92, 118, 223, 136, 105, 145, 238, 186, 115, 8, 214, 112, 153, 253, 38, 108, 205, 230, 157, 130, 11, 66, 101, 85, 253, 110, 110, 14, 148, 112]),
+ ];
+ for i in 0..16 {
+ let r_0 = FieldElement::from_bytes(&bytes[i]);
+ let Q = RistrettoPoint::elligator_ristretto_flavour(&r_0);
+ assert_eq!(Q.compress(), encoded_images[i]);
+ }
+ }
+
+ #[test]
+ fn random_roundtrip() {
+ let mut rng = OsRng::new().unwrap();
+ let B = &constants::RISTRETTO_BASEPOINT_TABLE;
+ for _ in 0..100 {
+ let P = B * &Scalar::random(&mut rng);
+ let compressed_P = P.compress();
+ let Q = compressed_P.decompress().unwrap();
+ assert_eq!(P, Q);
+ }
+ }
+
+ #[test]
+ fn random_is_valid() {
+ let mut rng = OsRng::new().unwrap();
+ for _ in 0..100 {
+ let P = RistrettoPoint::random(&mut rng);
+ // Check that P is on the curve
+ assert!(P.0.is_valid());
+ // Check that P is in the image of the ristretto map
+ P.compress();
+ }
+ }
+}
+
+#[cfg(all(test, feature = "bench"))]
+mod bench {
+ use rand::OsRng;
+ use test::Bencher;
+
+ use super::*;
+
+ #[bench]
+ fn decompression(b: &mut Bencher) {
+ let mut rng = OsRng::new().unwrap();
+ let B = &constants::RISTRETTO_BASEPOINT_TABLE;
+ let P = B * &Scalar::random(&mut rng);
+ let P_compressed = P.compress();
+ b.iter(|| P_compressed.decompress().unwrap());
+ }
+
+ #[bench]
+ fn compression(b: &mut Bencher) {
+ let mut rng = OsRng::new().unwrap();
+ let B = &constants::RISTRETTO_BASEPOINT_TABLE;
+ let P = B * &Scalar::random(&mut rng);
+ b.iter(|| P.compress());
+ }
+}
diff --git a/vendor/ristretto.sage b/vendor/ristretto.sage
new file mode 100644
index 0000000..f40bebd
--- /dev/null
+++ b/vendor/ristretto.sage
@@ -0,0 +1,611 @@
+import binascii
+class InvalidEncodingException(Exception): pass
+class NotOnCurveException(Exception): pass
+class SpecException(Exception): pass
+
+def lobit(x): return int(x) & 1
+def hibit(x): return lobit(2*x)
+def negative(x): return lobit(x)
+def enc_le(x,n): return bytearray([int(x)>>(8*i) & 0xFF for i in xrange(n)])
+def dec_le(x): return sum(b<<(8*i) for i,b in enumerate(x))
+def randombytes(n): return bytearray([randint(0,255) for _ in range(n)])
+
+def optimized_version_of(spec):
+ """Decorator: This function is an optimized version of some specification"""
+ def decorator(f):
+ def wrapper(self,*args,**kwargs):
+ def pr(x):
+ if isinstance(x,bytearray): return binascii.hexlify(x)
+ else: return str(x)
+ try: spec_ans = getattr(self,spec,spec)(*args,**kwargs),None
+ except Exception as e: spec_ans = None,e
+ try: opt_ans = f(self,*args,**kwargs),None
+ except Exception as e: opt_ans = None,e
+ if spec_ans[1] is None and opt_ans[1] is not None:
+ raise
+ #raise SpecException("Mismatch in %s: spec returned %s but opt threw %s"
+ # % (f.__name__,str(spec_ans[0]),str(opt_ans[1])))
+ if spec_ans[1] is not None and opt_ans[1] is None:
+ raise
+ #raise SpecException("Mismatch in %s: spec threw %s but opt returned %s"
+ # % (f.__name__,str(spec_ans[1]),str(opt_ans[0])))
+ if spec_ans[0] != opt_ans[0]:
+ raise SpecException("Mismatch in %s: %s != %s"
+ % (f.__name__,pr(spec_ans[0]),pr(opt_ans[0])))
+ if opt_ans[1] is not None: raise
+ else: return opt_ans[0]
+ wrapper.__name__ = f.__name__
+ return wrapper
+ return decorator
+
+def xsqrt(x,exn=InvalidEncodingException("Not on curve")):
+ """Return sqrt(x)"""
+ if not is_square(x): raise exn
+ s = sqrt(x)
+ if negative(s): s=-s
+ return s
+
+def isqrt(x,exn=InvalidEncodingException("Not on curve")):
+ """Return 1/sqrt(x)"""
+ if x==0: return 0
+ if not is_square(x): raise exn
+ return 1/sqrt(x)
+
+def isqrt_i(x):
+ """Return 1/sqrt(x) or 1/sqrt(zeta * x)"""
+ if x==0: return True,0
+ gen = x.parent(-1)
+ while is_square(gen): gen = sqrt(gen)
+ if is_square(x): return True,1/sqrt(x)
+ else: return False,1/sqrt(x*gen)
+
+class QuotientEdwardsPoint(object):
+ """Abstract class for point an a quotiented Edwards curve; needs F,a,d,cofactor to work"""
+ def __init__(self,x=0,y=1):
+ x = self.x = self.F(x)
+ y = self.y = self.F(y)
+ if y^2 + self.a*x^2 != 1 + self.d*x^2*y^2:
+ raise NotOnCurveException(str(self))
+
+ def __repr__(self):
+ return "%s(0x%x,0x%x)" % (self.__class__.__name__, self.x, self.y)
+
+ def __iter__(self):
+ yield self.x
+ yield self.y
+
+ def __add__(self,other):
+ x,y = self
+ X,Y = other
+ a,d = self.a,self.d
+ return self.__class__(
+ (x*Y+y*X)/(1+d*x*y*X*Y),
+ (y*Y-a*x*X)/(1-d*x*y*X*Y)
+ )
+
+ def __neg__(self): return self.__class__(-self.x,self.y)
+ def __sub__(self,other): return self + (-other)
+ def __rmul__(self,other): return self*other
+ def __eq__(self,other):
+ """NB: this is the only method that is different from the usual one"""
+ x,y = self
+ X,Y = other
+ return x*Y == X*y or (self.cofactor==8 and -self.a*x*X == y*Y)
+ def __ne__(self,other): return not (self==other)
+
+ def __mul__(self,exp):
+ exp = int(exp)
+ if exp < 0: exp,self = -exp,-self
+ total = self.__class__()
+ work = self
+ while exp != 0:
+ if exp & 1: total += work
+ work += work
+ exp >>= 1
+ return total
+
+ def xyzt(self):
+ x,y = self
+ z = self.F.random_element()
+ return x*z,y*z,z,x*y*z
+
+ def torque(self):
+ """Apply cofactor group, except keeping the point even"""
+ if self.cofactor == 8:
+ if self.a == -1: return self.__class__(self.y*self.i, self.x*self.i)
+ if self.a == 1: return self.__class__(-self.y, self.x)
+ else:
+ return self.__class__(-self.x, -self.y)
+
+
+ # Utility functions
+ @classmethod
+ def bytesToGf(cls,bytes,mustBeProper=True,mustBePositive=False):
+ """Convert little-endian bytes to field element, sanity check length"""
+ if len(bytes) != cls.encLen:
+ raise InvalidEncodingException("wrong length %d" % len(bytes))
+ s = dec_le(bytes)
+ if mustBeProper and s >= cls.F.modulus():
+ raise InvalidEncodingException("%d out of range!" % s)
+ s = cls.F(s)
+ if mustBePositive and negative(s):
+ raise InvalidEncodingException("%d is negative!" % s)
+ return s
+
+ @classmethod
+ def gfToBytes(cls,x,mustBePositive=False):
+ """Convert little-endian bytes to field element, sanity check length"""
+ if negative(x) and mustBePositive: x = -x
+ return enc_le(x,cls.encLen)
+
+class RistrettoPoint(QuotientEdwardsPoint):
+ """The new Ristretto group"""
+ def encodeSpec(self):
+ """Unoptimized specification for encoding"""
+ x,y = self
+ if self.cofactor==8 and (negative(x*y) or y==0): (x,y) = self.torque()
+ if y == -1: y = 1 # Avoid divide by 0; doesn't affect impl
+
+ if negative(x): x,y = -x,-y
+ s = xsqrt(self.mneg*(1-y)/(1+y),exn=Exception("Unimplemented: point is odd: " + str(self)))
+ return self.gfToBytes(s)
+
+ @classmethod
+ def decodeSpec(cls,s):
+ """Unoptimized specification for decoding"""
+ s = cls.bytesToGf(s,mustBePositive=True)
+
+ a,d = cls.a,cls.d
+ x = xsqrt(4*s^2 / (a*d*(1+a*s^2)^2 - (1-a*s^2)^2))
+ y = (1+a*s^2) / (1-a*s^2)
+
+ if cls.cofactor==8 and (negative(x*y) or y==0):
+ raise InvalidEncodingException("x*y has high bit")
+
+ return cls(x,y)
+
+ @optimized_version_of("encodeSpec")
+ def encode(self):
+ """Encode, optimized version"""
+ a,d,mneg = self.a,self.d,self.mneg
+ x,y,z,t = self.xyzt()
+
+ if self.cofactor==8:
+ u1 = mneg*(z+y)*(z-y)
+ u2 = x*y # = t*z
+ isr = isqrt(u1*u2^2)
+ i1 = isr*u1 # sqrt(mneg*(z+y)*(z-y))/(x*y)
+ i2 = isr*u2 # 1/sqrt(a*(y+z)*(y-z))
+ z_inv = i1*i2*t # 1/z
+
+ if negative(t*z_inv):
+ if a==-1:
+ x,y = y*self.i,x*self.i
+ den_inv = self.magic * i1
+ else:
+ x,y = -y,x
+ den_inv = self.i * self.magic * i1
+
+ else:
+ den_inv = i2
+
+ if negative(x*z_inv): y = -y
+ s = (z-y) * den_inv
+ else:
+ num = mneg*(z+y)*(z-y)
+ isr = isqrt(num*y^2)
+ if negative(isr^2*num*y*t): y = -y
+ s = isr*y*(z-y)
+
+
+ return self.gfToBytes(s,mustBePositive=True)
+
+ @classmethod
+ @optimized_version_of("decodeSpec")
+ def decode(cls,s):
+ """Decode, optimized version"""
+ s = cls.bytesToGf(s,mustBePositive=True)
+
+ a,d = cls.a,cls.d
+ yden = 1-a*s^2
+ ynum = 1+a*s^2
+ yden_sqr = yden^2
+ xden_sqr = a*d*ynum^2 - yden_sqr
+
+ isr = isqrt(xden_sqr * yden_sqr)
+
+ xden_inv = isr * yden
+ yden_inv = xden_inv * isr * xden_sqr
+
+ x = 2*s*xden_inv
+ if negative(x): x = -x
+ y = ynum * yden_inv
+
+ if cls.cofactor==8 and (negative(x*y) or y==0):
+ raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y))
+
+ return cls(x,y)
+
+ @classmethod
+ def fromJacobiQuartic(cls,s,t,sgn=1):
+ """Convert point from its Jacobi Quartic representation"""
+ a,d = cls.a,cls.d
+ assert s^4 - 2*cls.a*(1-2*d/(d-a))*s^2 + 1 == t^2
+ x = 2*s*cls.magic / t
+ y = (1+a*s^2) / (1-a*s^2)
+ return cls(sgn*x,y)
+
+ @classmethod
+ def elligatorSpec(cls,r0):
+ a,d = cls.a,cls.d
+ r = cls.qnr * cls.bytesToGf(r0)^2
+ den = (d*r-a)*(a*r-d)
+ n1 = cls.a*(r+1)*(a+d)*(d-a)/den
+ n2 = r*n1
+ if is_square(n1):
+ sgn,s,t = 1, xsqrt(n1), -(r-1)*(a+d)^2 / den - 1
+ else:
+ sgn,s,t = -1,-xsqrt(n2), r*(r-1)*(a+d)^2 / den - 1
+
+ return cls.fromJacobiQuartic(s,t)
+
+ @classmethod
+ @optimized_version_of("elligatorSpec")
+ def elligator(cls,r0):
+ a,d = cls.a,cls.d
+ r0 = cls.bytesToGf(r0)
+ r = cls.qnr * r0^2
+ den = (d*r-a)*(a*r-d)
+ num = cls.a*(r+1)*(a+d)*(d-a)
+
+ iss,isri = isqrt_i(num*den)
+ if iss: sgn,twiddle = 1,1
+ else: sgn,twiddle = -1,r0*cls.qnr
+ isri *= twiddle
+ s = isri*num
+ t = -sgn*isri*s*(r-1)*(d+a)^2 - 1
+ if negative(s) == iss: s = -s
+ return cls.fromJacobiQuartic(s,t)
+
+
+class Decaf_1_1_Point(QuotientEdwardsPoint):
+ """Like current decaf but tweaked for simplicity"""
+ def encodeSpec(self):
+ """Unoptimized specification for encoding"""
+ a,d = self.a,self.d
+ x,y = self
+ if x==0 or y==0: return(self.gfToBytes(0))
+
+ if self.cofactor==8 and negative(x*y*self.isoMagic):
+ x,y = self.torque()
+
+ isr2 = isqrt(a*(y^2-1)) * sqrt(a*d-1)
+
+ sr = xsqrt(1-a*x^2)
+ assert sr in [isr2*x*y,-isr2*x*y]
+
+ altx = 1/isr2*self.isoMagic
+ if negative(altx): s = (1+x*y*isr2)/(a*x)
+ else: s = (1-x*y*isr2)/(a*x)
+
+ return self.gfToBytes(s,mustBePositive=True)
+
+ @classmethod
+ def decodeSpec(cls,s):
+ """Unoptimized specification for decoding"""
+ a,d = cls.a,cls.d
+ s = cls.bytesToGf(s,mustBePositive=True)
+
+ if s==0: return cls()
+ isr = isqrt(s^4 + 2*(a-2*d)*s^2 + 1)
+ altx = 2*s*isr*cls.isoMagic
+ if negative(altx): isr = -isr
+ x = 2*s / (1+a*s^2)
+ y = (1-a*s^2) * isr
+
+ if cls.cofactor==8 and (negative(x*y*cls.isoMagic) or y==0):
+ raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y))
+
+ return cls(x,y)
+
+ @optimized_version_of("encodeSpec")
+ def encode(self):
+ """Encode, optimized version"""
+ a,d = self.a,self.d
+ x,y,z,t = self.xyzt()
+
+ if self.cofactor == 8:
+ # Cofactor 8 version
+ num = (z+y)*(z-y)
+ den = x*y
+ tmp = isqrt(num*(a-d)*den^2)
+
+ if negative(tmp^2*den*num*(a-d)*t^2*self.isoMagic):
+ den,num = num,den
+ tmp *= sqrt(a-d) # witness that cofactor is 8
+ yisr = x*sqrt(a)
+ toggle = (a==1)
+ else:
+ yisr = y*(a*d-1)
+ toggle = False
+
+ tiisr = tmp*num
+ altx = tiisr*t*self.isoMagic
+ if negative(altx) != toggle: tiisr =- tiisr
+ s = tmp*den*yisr*(tiisr*z - 1)
+
+ else:
+ # Much simpler cofactor 4 version
+ num = (x+t)*(x-t)
+ isr = isqrt(num*(a-d)*x^2)
+ ratio = isr*num
+ if negative(ratio*self.isoMagic): ratio=-ratio
+ s = (a-d)*isr*x*(ratio*z - t)
+
+ return self.gfToBytes(s,mustBePositive=True)
+
+ @classmethod
+ @optimized_version_of("decodeSpec")
+ def decode(cls,s):
+ """Decode, optimized version"""
+ a,d = cls.a,cls.d
+ s = cls.bytesToGf(s,mustBePositive=True)
+
+ if s==0: return cls()
+ s2 = s^2
+ den = 1+a*s2
+ num = den^2 - 4*d*s2
+ isr = isqrt(num*den^2)
+ altx = 2*s*isr*den*cls.isoMagic
+ if negative(altx): isr = -isr
+ x = 2*s *isr^2*den*num
+ y = (1-a*s^2) * isr*den
+
+ if cls.cofactor==8 and (negative(x*y*cls.isoMagic) or y==0):
+ raise InvalidEncodingException("x*y is invalid: %d, %d" % (x,y))
+
+ return cls(x,y)
+
+ @classmethod
+ def fromJacobiQuartic(cls,s,t,sgn=1):
+ """Convert point from its Jacobi Quartic representation"""
+ a,d = cls.a,cls.d
+ if s==0: return cls()
+ x = 2*s / (1+a*s^2)
+ y = (1-a*s^2) / t
+ return cls(x,sgn*y)
+
+ @classmethod
+ def elligatorSpec(cls,r0):
+ a,d = cls.a,cls.d
+ r = cls.qnr * cls.bytesToGf(r0)^2
+
+ den = (d*r-(d-a))*((d-a)*r-d)
+ n1 = (r+1)*(a-2*d)/den
+ n2 = r*n1
+ if is_square(n1):
+ sgn,s,t = 1, xsqrt(n1), -(r-1)*(a-2*d)^2 / den - 1
+ else:
+ sgn,s,t = -1, -xsqrt(n2), r*(r-1)*(a-2*d)^2 / den - 1
+
+ return cls.fromJacobiQuartic(s,t)
+
+ @classmethod
+ @optimized_version_of("elligatorSpec")
+ def elligator(cls,r0):
+ a,d = cls.a,cls.d
+ r0 = cls.bytesToGf(r0)
+ r = cls.qnr * r0^2
+ den = (d*r-(d-a))*((d-a)*r-d)
+ num = (r+1)*(a-2*d)
+
+ iss,isri = isqrt_i(num*den)
+ if iss: sgn,twiddle = 1,1
+ else: sgn,twiddle = -1,r0*cls.qnr
+ isri *= twiddle
+ s = isri*num
+ t = -sgn*isri*s*(r-1)*(a-2*d)^2 - 1
+ if negative(s) == iss: s = -s
+ return cls.fromJacobiQuartic(s,t)
+
+class Ed25519Point(RistrettoPoint):
+ F = GF(2^255-19)
+ d = F(-121665/121666)
+ a = F(-1)
+ i = sqrt(F(-1))
+ mneg = F(1)
+ qnr = i
+ magic = isqrt(a*d-1)
+ cofactor = 8
+ encLen = 32
+
+ @classmethod
+ def base(cls):
+ return cls( 15112221349535400772501151409588531511454012693041857206046113283949847762202, 46316835694926478169428394003475163141307993866256225615783033603165251855960
+ )
+
+class NegEd25519Point(RistrettoPoint):
+ F = GF(2^255-19)
+ d = F(121665/121666)
+ a = F(1)
+ i = sqrt(F(-1))
+ mneg = F(-1) # TODO checkme vs 1-ad or whatever
+ qnr = i
+ magic = isqrt(a*d-1)
+ cofactor = 8
+ encLen = 32
+
+ @classmethod
+ def base(cls):
+ y = cls.F(4/5)
+ x = sqrt((y^2-1)/(cls.d*y^2-cls.a))
+ if negative(x): x = -x
+ return cls(x,y)
+
+class IsoEd448Point(RistrettoPoint):
+ F = GF(2^448-2^224-1)
+ d = F(39082/39081)
+ a = F(1)
+ mneg = F(-1)
+ qnr = -1
+ magic = isqrt(a*d-1)
+ cofactor = 4
+ encLen = 56
+
+ @classmethod
+ def base(cls):
+ return cls( # RFC has it wrong
+ -345397493039729516374008604150537410266655260075183290216406970281645695073672344430481787759340633221708391583424041788924124567700732,
+ -363419362147803445274661903944002267176820680343659030140745099590306164083365386343198191849338272965044442230921818680526749009182718
+ )
+
+class TwistedEd448GoldilocksPoint(Decaf_1_1_Point):
+ F = GF(2^448-2^224-1)
+ d = F(-39082)
+ a = F(-1)
+ qnr = -1
+ magic = isqrt(a*d-1)
+ cofactor = 4
+ encLen = 56
+ isoMagic = IsoEd448Point.magic
+
+ @classmethod
+ def base(cls):
+ return cls.decodeSpec(Ed448GoldilocksPoint.base().encodeSpec())
+
+class Ed448GoldilocksPoint(Decaf_1_1_Point):
+ F = GF(2^448-2^224-1)
+ d = F(-39081)
+ a = F(1)
+ qnr = -1
+ magic = isqrt(a*d-1)
+ cofactor = 4
+ encLen = 56
+ isoMagic = IsoEd448Point.magic
+
+ @classmethod
+ def base(cls):
+ return -2*cls( # FIXME: make not negative
+ 224580040295924300187604334099896036246789641632564134246125461686950415467406032909029192869357953282578032075146446173674602635247710, 298819210078481492676017930443930673437544040154080242095928241372331506189835876003536878655418784733982303233503462500531545062832660
+ )
+
+class IsoEd25519Point(Decaf_1_1_Point):
+ # TODO: twisted iso too!
+ # TODO: twisted iso might have to IMAGINE_TWIST or whatever
+ F = GF(2^255-19)
+ d = F(-121665)
+ a = F(1)
+ i = sqrt(F(-1))
+ qnr = i
+ magic = isqrt(a*d-1)
+ cofactor = 8
+ encLen = 32
+ isoMagic = Ed25519Point.magic
+ isoA = Ed25519Point.a
+
+ @classmethod
+ def base(cls):
+ return cls.decodeSpec(Ed25519Point.base().encode())
+
+class TestFailedException(Exception): pass
+
+def test(cls,n):
+ print "Testing curve %s" % cls.__name__
+
+ specials = [1]
+ ii = cls.F(-1)
+ while is_square(ii):
+ specials.append(ii)
+ ii = sqrt(ii)
+ specials.append(ii)
+ for i in specials:
+ if negative(cls.F(i)): i = -i
+ i = enc_le(i,cls.encLen)
+ try:
+ Q = cls.decode(i)
+ QE = Q.encode()
+ if QE != i:
+ raise TestFailedException("Round trip special %s != %s" %
+ (binascii.hexlify(QE),binascii.hexlify(i)))
+ except NotOnCurveException: pass
+ except InvalidEncodingException: pass
+
+
+ P = cls.base()
+ print "base", list(P.encode())
+ for i in xrange(16):
+ Q = P*i
+ print i, list(Q.encode())
+
+ Q = cls()
+ for i in xrange(n):
+ #print i, binascii.hexlify(Q.encode())
+ QQ = cls.decode(Q.encode())
+ if QQ != Q: raise TestFailedException("Round trip %s != %s" % (str(QQ),str(Q)))
+
+ QT = Q
+ QE = Q.encode()
+ for h in xrange(cls.cofactor):
+ QT = QT.torque()
+ if QT.encode() != QE:
+ raise TestFailedException("Can't torque %s,%d" % (str(Q),h+1))
+
+ Q0 = Q + P
+ if Q0 == Q: raise TestFailedException("Addition doesn't work")
+ if Q0-P != Q: raise TestFailedException("Subtraction doesn't work")
+
+ r = randint(1,1000)
+ Q1 = Q0*r
+ Q2 = Q0*(r+1)
+ if Q1 + Q0 != Q2: raise TestFailedException("Scalarmul doesn't work")
+ Q = Q1
+
+test(Ed25519Point,100)
+#test(NegEd25519Point,100)
+#test(IsoEd25519Point,100)
+#test(IsoEd448Point,100)
+#test(TwistedEd448GoldilocksPoint,100)
+#test(Ed448GoldilocksPoint,100)
+
+
+def testElligator(cls,n):
+ print "Testing elligator on %s" % cls.__name__
+ for i in xrange(n):
+ r = randombytes(cls.encLen)
+ Q = cls.elligator(r)
+ print list(r), list(Q.encode())
+
+testElligator(Ed25519Point,100)
+#testElligator(NegEd25519Point,100)
+#testElligator(IsoEd448Point,100)
+#testElligator(Ed448GoldilocksPoint,100)
+#testElligator(TwistedEd448GoldilocksPoint,100)
+
+def gangtest(classes,n):
+ print "Gang test",[cls.__name__ for cls in classes]
+ specials = [1]
+ ii = classes[0].F(-1)
+ while is_square(ii):
+ specials.append(ii)
+ ii = sqrt(ii)
+ specials.append(ii)
+
+ for i in xrange(n):
+ rets = [bytes((cls.base()*i).encode()) for cls in classes]
+ if len(set(rets)) != 1:
+ print "Divergence in encode at %d" % i
+ for c,ret in zip(classes,rets):
+ print c,binascii.hexlify(ret)
+ print
+
+ if i < len(specials): r0 = enc_le(specials[i],classes[0].encLen)
+ else: r0 = randombytes(classes[0].encLen)
+
+ rets = [bytes((cls.elligator(r0)*i).encode()) for cls in classes]
+ if len(set(rets)) != 1:
+ print "Divergence in elligator at %d" % i
+ for c,ret in zip(classes,rets):
+ print c,binascii.hexlify(ret)
+ print
+gangtest([IsoEd448Point,TwistedEd448GoldilocksPoint,Ed448GoldilocksPoint],100)
+gangtest([Ed25519Point,IsoEd25519Point],100)