Merge remote-tracking branch 'hdevalence/feature/ct-invsqrt' into develop

This commit is contained in:
Isis Lovecruft 2017-02-25 01:26:09 +00:00
commit 4c5da50359
Failed to extract signature
4 changed files with 90 additions and 73 deletions

View file

@ -1634,7 +1634,8 @@ mod test {
fn test_sqrt_constants_sign() { fn test_sqrt_constants_sign() {
let one = FieldElement([ 1,0,0,0,0,0,0,0,0,0]); let one = FieldElement([ 1,0,0,0,0,0,0,0,0,0]);
let minus_one = FieldElement([-1,0,0,0,0,0,0,0,0,0]); let minus_one = FieldElement([-1,0,0,0,0,0,0,0,0,0]);
let invsqrt_m1 = minus_one.invsqrt().unwrap(); let (was_nonzero_square, invsqrt_m1) = minus_one.invsqrt();
assert_eq!(was_nonzero_square, 1u8);
let sign_test_sqrt = &invsqrt_m1 * &constants::SQRT_M1; let sign_test_sqrt = &invsqrt_m1 * &constants::SQRT_M1;
let sign_test_msqrt = &invsqrt_m1 * &constants::MSQRT_M1; let sign_test_msqrt = &invsqrt_m1 * &constants::MSQRT_M1;
// XXX it seems we have flipped the sign relative to // XXX it seems we have flipped the sign relative to

View file

@ -127,45 +127,24 @@ impl CompressedEdwardsY {
/// Attempt to decompress to an `ExtendedPoint`. /// Attempt to decompress to an `ExtendedPoint`.
/// ///
/// # Warning /// Returns `None` if the input is not the `y`-coordinate of a
/// /// curve point.
/// This function will fail and return None if both vx²-u=0 and vx²+u=0.
pub fn decompress(&self) -> Option<ExtendedPoint> { // FromBytes() pub fn decompress(&self) -> Option<ExtendedPoint> { // FromBytes()
let mut u: FieldElement; let Y = FieldElement::from_bytes(&self.0);
let mut v: FieldElement; let Z = FieldElement::one();
let v3: FieldElement; let YY = Y.square();
let vxx: FieldElement; let u = &YY - &Z; // u = y²-1
let v = &(&YY * &constants::d) + &Z; // v = dy²+1
let (is_nonzero_square, mut X) = FieldElement::sqrt_ratio(&u, &v);
let mut X: FieldElement; if is_nonzero_square != 1u8 { return None; }
let Y: FieldElement;
let Z: FieldElement;
let T: FieldElement;
Y = FieldElement::from_bytes(&self.0); // Flip the sign of X if it's not correct
Z = FieldElement::one(); let compressed_sign_bit = self[31] >> 7;
let current_sign_bit = X.is_negative_ed25519();
X.conditional_negate(current_sign_bit ^ compressed_sign_bit);
u = Y.square(); Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: &X * &Y })
v = &u * &constants::d;
u -= &Z; // u = y²-1
v += &Z; // v = dy²+1
v3 = &v.square() * &v; // v3 = v³
X = (&v3.square() * &(&v * &u)).pow_p58(); // x = (uv⁷)^((q-5)/8)
X *= &(&u * &v3); // x = (uv³)(uv⁷)^((q-5)/8)
vxx = &v * &X.square();
if (&vxx - &u).is_nonzero() == 1 { // vx²-u
if (&vxx + &u).is_nonzero() == 1 { // vx²+u
return None;
}
X *= &constants::SQRT_M1;
}
if X.is_negative_ed25519() != (self[31] >> 7) as i32 {
X = X.neg();
}
T = &X * &Y;
Some(ExtendedPoint{ X: X, Y: Y, Z: Z, T: T })
} }
} }

View file

@ -71,10 +71,12 @@ impl CompressedDecaf {
let Z = &FieldElement::one() - &ss; // Z = 1+as^2 let Z = &FieldElement::one() - &ss; // Z = 1+as^2
let u = &(&Z * &Z) - &(&constants::d4 * &ss); // u = Z^2 - 4ds^2 let u = &(&Z * &Z) - &(&constants::d4 * &ss); // u = Z^2 - 4ds^2
let uss = &u * &ss; let uss = &u * &ss;
let mut v = match uss.invsqrt() {
Some(v) => v, let (uss_is_nonzero_square, mut v) = uss.invsqrt();
None => return None, if (uss_is_nonzero_square | uss.is_zero()) == 0u8 {
}; return None; // us^2 is nonzero nonsquare
}
// Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero. // Now v = 1/sqrt(us^2) if us^2 is a nonzero square, 0 if us^2 is zero.
let uv = &v * &u; let uv = &v * &u;
if uv.is_negative_decaf() == 1u8 { if uv.is_negative_decaf() == 1u8 {
@ -158,9 +160,9 @@ impl DecafPoint {
let Z_plus_Y = &self.0.Z + &Y; let Z_plus_Y = &self.0.Z + &Y;
let Z_minus_Y = &self.0.Z - &Y; let Z_minus_Y = &self.0.Z - &Y;
let t = &constants::a_minus_d * &(&Z_plus_Y * &Z_minus_Y); let t = &constants::a_minus_d * &(&Z_plus_Y * &Z_minus_Y);
let (t_is_nonzero_square, mut r) = t.invsqrt();
// t should always be square (why?) // t should always be square (why?)
// XXX is it safe to use option types here? debug_assert_eq!( t_is_nonzero_square | t.is_zero(), 1u8 );
let mut r = t.invsqrt().unwrap();
// Step 2: Compute u = (a-d)r // Step 2: Compute u = (a-d)r
let u = &constants::a_minus_d * &r; let u = &constants::a_minus_d * &r;

View file

@ -215,6 +215,11 @@ impl FieldElement {
FieldElement([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]) FieldElement([ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
} }
/// Construct -1.
pub fn minus_one() -> FieldElement {
FieldElement([-1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ])
}
fn combine_coeffs(input: &[i64;10]) -> FieldElement { //FeCombine fn combine_coeffs(input: &[i64;10]) -> FieldElement { //FeCombine
let mut c = [0i64;10]; let mut c = [0i64;10];
let mut h = input.clone(); let mut h = input.clone();
@ -512,16 +517,16 @@ impl FieldElement {
(!equal_so_far & 1 & greater) as u8 (!equal_so_far & 1 & greater) as u8
} }
/// Determine if this `FieldElement` is negative, in the /// Determine if this `FieldElement` is negative, in the sense
/// sense used in the ed25519 paper. /// used in the ed25519 paper: `x` is negative if the low bit is
/// set.
/// ///
/// # Return /// # Return
/// ///
/// If negative, return `1i32`. Otherwise, return `0i32`. /// If negative, return `1u8`. Otherwise, return `0u8`.
// XXX should return u8 pub fn is_negative_ed25519(&self) -> u8 { //FeIsNegative
pub fn is_negative_ed25519(&self) -> i32 { //FeIsNegative
let bytes = self.to_bytes(); let bytes = self.to_bytes();
(bytes[0] & 1) as i32 (bytes[0] & 1) as u8
} }
/// Determine if this `FieldElement` is negative, in the /// Determine if this `FieldElement` is negative, in the
@ -817,19 +822,21 @@ impl FieldElement {
t21 t21
} }
/// Try to compute 1/sqrt(self). /// Given `FieldElements` `u` and `v`, attempt to compute
/// `sqrt(u/v)` in constant time.
///
/// 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.
/// ///
/// # Return /// # Return
/// ///
/// * If `self` is zero, returns zero. /// - `(1u8, sqrt(u/v))` if `v` is nonzero and `u/v` is square;
/// * If `self` is square, returns 1/sqrt(self). /// - `(0u8, zero)` if `v` is zero;
/// * If `self` is nonsquare, returns `None`. /// - `(0u8, garbage)` if `u/v` is nonsquare.
pub fn invsqrt(&self) -> Option<FieldElement> { ///
// We are to compute v as: pub fn sqrt_ratio(u: &FieldElement, v: &FieldElement)
// / 1/sqrt(self) if self is square, nonzero; -> (u8, FieldElement) {
// v = | 0 if self is zero;
// \ [reject] if self is nonsquare.
//
// Using the same trick as in ed25519 decoding, we merge the // Using the same trick as in ed25519 decoding, we merge the
// inversion, the square root, and the square test as follows. // inversion, the square root, and the square test as follows.
// //
@ -841,22 +848,50 @@ impl FieldElement {
// 1/β = α^(p-1 - (p+3)/8) = α^((7p-11)/8) // 1/β = α^(p-1 - (p+3)/8) = α^((7p-11)/8)
// = α^3 * (α^7)^((p-5)/8). // = α^3 * (α^7)^((p-5)/8).
// //
// If α is square, then (1/β)^2 = ±(1/α), so that (1/β)^2 α = ±1. // We can therefore compute sqrt(u/v) = sqrt(u)/sqrt(v)
let a3 = &self.square() * self; // α^3 // by first computing
let a7 = &a3.square() * self; // α^7 // r = u^((p+3)/8) v^(p-1-(p+3)/8)
let mut v = &a3 * &a7.pow_p58(); // α^(p-1-(p+3)/8) // = u u^((p-5)/8) v^3 (v^7)^((p-5)/8)
let check = self * &v.square(); // ±1 if α is square // = (uv^3) (uv^7)^((p-5)/8).
//
// If v is nonzero and u/v is square, then r^2 = ±u/v,
// so vr^2 = ±u.
// If vr^2 = u, then sqrt(u/v) = r.
// If vr^2 = -u, then sqrt(u/v) = r*sqrt(-1).
//
// If v is zero, r is also zero.
if v.is_zero() == 1u8 { let v3 = &v.square() * v;
return Some(v); // α was zero all along let v7 = &v3.square() * v;
} else if check == FieldElement::one() { let mut r = &(u * &v3) * &(u * &v7).pow_p58();
return Some(v); // computed the correct sqrt let check = v * &r.square();
} else if check == -&FieldElement::one() {
// wrong sign, multiply by sqrt(-1) let correct_sign_sqrt = check.ct_eq( u);
return Some(&v * &constants::SQRT_M1); let flipped_sign_sqrt = check.ct_eq(&(-u));
} else {
return None; // input was nonsquare let r_prime = &constants::SQRT_M1 * &r;
} r.conditional_assign(&r_prime, flipped_sign_sqrt);
let was_nonzero_square = correct_sign_sqrt | flipped_sign_sqrt;
(was_nonzero_square, r)
}
/// For `self` a nonzero square, compute 1/sqrt(self) in
/// constant time.
///
/// 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.
///
/// # Return
///
/// - `(1u8, 1/sqrt(self))` if `self` is a nonzero square;
/// - `(0u8, zero)` if `self` is zero;
/// - `(0u8, garbage)` if `self` is nonsquare.
///
pub fn invsqrt(&self) -> (u8, FieldElement) {
FieldElement::sqrt_ratio(&FieldElement::one(), self)
} }
/// chi calculates `self^((p-1)/2)`. /// chi calculates `self^((p-1)/2)`.