mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-04 20:24:10 +00:00
Prototype of Ristretto encoding
This commit is contained in:
parent
58a55117be
commit
fafdae7a60
2 changed files with 92 additions and 174 deletions
14
src/field.rs
14
src/field.rs
|
|
@ -465,6 +465,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"))]
|
||||
|
|
|
|||
252
src/ristretto.rs
252
src/ristretto.rs
|
|
@ -45,6 +45,7 @@ use scalar::Scalar;
|
|||
|
||||
use subtle::ConditionallyAssignable;
|
||||
use subtle::ConditionallyNegatable;
|
||||
use subtle;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Compressed points
|
||||
|
|
@ -64,63 +65,65 @@ impl CompressedRistretto {
|
|||
}
|
||||
|
||||
/// 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<RistrettoPoint> {
|
||||
// XXX should decoding be CT ?
|
||||
// XXX need to check that xy is nonnegative and reject otherwise
|
||||
// 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_ed25519();
|
||||
|
||||
// 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; }
|
||||
if s_encoding_is_canonical == 0u8 || s_is_negative == 1u8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Step 2. The rest. (XXX write comments)
|
||||
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;
|
||||
let yden = &FieldElement::one() + &ss;
|
||||
let ynum = &FieldElement::one() - &ss;
|
||||
let yden_sqr = yden.square();
|
||||
let xden_sqr = &(&(-&constants::d) * &ynum.square()) - &yden_sqr;
|
||||
|
||||
if Z.is_zero() == 1u8 { return None; }
|
||||
let (ok, invsqrt) = (&xden_sqr * &yden_sqr).invsqrt();
|
||||
|
||||
// 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 xden_inv = &invsqrt * &yden;
|
||||
let yden_inv = &invsqrt * &(&xden_inv * &xden_sqr);
|
||||
|
||||
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
|
||||
let mut x = &(&s + &s) * &xden_inv;
|
||||
let x_is_negative = x.is_negative_ed25519();
|
||||
x.conditional_negate(x_is_negative);
|
||||
let y = &ynum * &yden_inv;
|
||||
|
||||
// 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;
|
||||
let t = &x * &y;
|
||||
|
||||
// "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(RistrettoPoint(ExtendedPoint{ X: X, Y: Y, Z: Z, T: T }))
|
||||
if ok == 0u8 || t.is_negative_ed25519() == 1u8 || x.is_zero() == 1u8 {
|
||||
return None;
|
||||
} else {
|
||||
None
|
||||
return Some(RistrettoPoint(ExtendedPoint{
|
||||
X: x, Y: y, Z: FieldElement::one(), T: t
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Identity for CompressedRistretto {
|
||||
fn identity() -> CompressedRistretto {
|
||||
CompressedRistretto([0u8; 32])
|
||||
// After tweaking Decaf to Ristretto, the identity compresses as -1 :(
|
||||
CompressedRistretto([0xec, 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])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -191,151 +194,43 @@ pub struct RistrettoPoint(pub ExtendedPoint);
|
|||
impl RistrettoPoint {
|
||||
/// Compress in Ristretto format.
|
||||
pub fn compress(&self) -> CompressedRistretto {
|
||||
// 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 Ristretto 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 Ristretto 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 T = &self.0.T;
|
||||
|
||||
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);
|
||||
println!("{:?}", self);
|
||||
println!("Z = {:?}", self.0.Z);
|
||||
println!("Y = {:?}", self.0.Y);
|
||||
|
||||
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 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;
|
||||
|
||||
r.conditional_assign(&r_rot, rotate);
|
||||
X.conditional_assign(&iY, rotate);
|
||||
Y.conditional_assign(&iX, rotate);
|
||||
T.conditional_negate(rotate);
|
||||
let rotate = (T * &z_inv).is_negative_ed25519();
|
||||
|
||||
// Step 2: Compute u = (a-d)r
|
||||
let u = &constants::a_minus_d * &r;
|
||||
X.conditional_assign(&iY, rotate);
|
||||
Y.conditional_assign(&iX, rotate);
|
||||
den_inv.conditional_assign(&enchanted_denominator, rotate);
|
||||
|
||||
// Step 3: Negate r if -2uZ is negative.
|
||||
let uZ = &u * Z;
|
||||
let m2uZ = -&(&uZ + &uZ);
|
||||
r.conditional_negate(m2uZ.is_negative_decaf());
|
||||
Y.conditional_negate((&X * &z_inv).is_negative_ed25519());
|
||||
|
||||
let mut s = &den_inv * &(Z - &Y);
|
||||
let s_is_zero = s.is_zero();
|
||||
s.conditional_assign(&FieldElement::one(), s_is_zero);
|
||||
let s_is_negative = s.is_negative_ed25519();
|
||||
s.conditional_negate(s_is_negative);
|
||||
|
||||
// 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);
|
||||
CompressedRistretto(s.to_bytes())
|
||||
}
|
||||
|
||||
|
|
@ -757,7 +652,13 @@ mod test {
|
|||
fn decompress_id() {
|
||||
let compressed_id = CompressedRistretto::identity();
|
||||
let id = compressed_id.decompress().unwrap();
|
||||
assert_eq!(id.0.compress(), CompressedEdwardsY::identity());
|
||||
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]
|
||||
|
|
@ -776,6 +677,8 @@ mod test {
|
|||
assert_eq!(diff4.compress(), CompressedEdwardsY::identity());
|
||||
}
|
||||
|
||||
/*
|
||||
// XXX replace these with ristretto encodings from the python implementation
|
||||
#[test]
|
||||
fn encodings_of_small_multiples_of_basepoint() {
|
||||
// Table of encodings of (1+i)*basepoint
|
||||
|
|
@ -804,6 +707,7 @@ mod test {
|
|||
bp = &bp + &constants::RISTRETTO_BASEPOINT_POINT;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[test]
|
||||
fn four_torsion_basepoint() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue