diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs index 6e8a6a7b17..455a4c0f54 100644 --- a/bellman/src/domain.rs +++ b/bellman/src/domain.rs @@ -73,11 +73,11 @@ impl> EvaluationDomain { coeffs, exp, omega, - omegainv: omega.inverse().unwrap(), - geninv: E::Fr::multiplicative_generator().inverse().unwrap(), + omegainv: omega.invert().unwrap(), + geninv: E::Fr::multiplicative_generator().invert().unwrap(), minv: E::Fr::from_str(&format!("{}", m)) .unwrap() - .inverse() + .invert() .unwrap(), }) } @@ -141,10 +141,7 @@ impl> EvaluationDomain { /// evaluation domain, so we must perform division over /// a coset. pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { - let i = self - .z(&E::Fr::multiplicative_generator()) - .inverse() - .unwrap(); + let i = self.z(&E::Fr::multiplicative_generator()).invert().unwrap(); worker.scope(self.coeffs.len(), |scope, chunk| { for v in self.coeffs.chunks_mut(chunk) { diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs index 65bee25183..e460d201c1 100644 --- a/bellman/src/gadgets/num.rs +++ b/bellman/src/gadgets/num.rs @@ -288,7 +288,7 @@ impl AllocatedNum { if tmp.is_zero() { Err(SynthesisError::DivisionByZero) } else { - Ok(tmp.inverse().unwrap()) + Ok(tmp.invert().unwrap()) } }, )?; diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs index 11844d7039..32c9d07650 100644 --- a/bellman/src/groth16/generator.rs +++ b/bellman/src/groth16/generator.rs @@ -215,8 +215,22 @@ where assembly.num_inputs + assembly.num_aux }); - let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; - let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + let gamma_inverse = { + let inverse = gamma.invert(); + if bool::from(inverse.is_some()) { + Ok(inverse.unwrap()) + } else { + Err(SynthesisError::UnexpectedIdentity) + } + }?; + let delta_inverse = { + let inverse = delta.invert(); + if bool::from(inverse.is_some()) { + Ok(inverse.unwrap()) + } else { + Err(SynthesisError::UnexpectedIdentity) + } + }?; let worker = Worker::new(); diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs index 5d6422f573..d9040049aa 100644 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ b/bellman/src/groth16/tests/dummy_engine.rs @@ -1,7 +1,4 @@ -use ff::{ - Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, - SqrtField, -}; +use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use pairing::{Engine, PairingCurveAffine}; @@ -10,13 +7,25 @@ use std::cmp::Ordering; use std::fmt; use std::num::Wrapping; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; const MODULUS_R: Wrapping = Wrapping(64513); #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Fr(Wrapping); +impl Default for Fr { + fn default() -> Self { + ::zero() + } +} + +impl ConstantTimeEq for Fr { + fn ct_eq(&self, other: &Fr) -> Choice { + (self.0).0.ct_eq(&(other.0).0) + } +} + impl fmt::Display for Fr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { write!(f, "{}", (self.0).0) @@ -159,11 +168,11 @@ impl Field for Fr { Fr((self.0 << 1) % MODULUS_R) } - fn inverse(&self) -> Option { + fn invert(&self) -> CtOption { if ::is_zero(self) { - None + CtOption::new(::zero(), Choice::from(0)) } else { - Some(self.pow(&[(MODULUS_R.0 as u64) - 2])) + CtOption::new(self.pow(&[(MODULUS_R.0 as u64) - 2]), Choice::from(1)) } } @@ -173,57 +182,39 @@ impl Field for Fr { } impl SqrtField for Fr { - fn legendre(&self) -> LegendreSymbol { - // s = self^((r - 1) // 2) - let s = self.pow([32256]); - if s == ::zero() { - LegendreSymbol::Zero - } else if s == ::one() { - LegendreSymbol::QuadraticResidue - } else { - LegendreSymbol::QuadraticNonResidue - } - } - - fn sqrt(&self) -> Option { + fn sqrt(&self) -> CtOption { // Tonelli-Shank's algorithm for q mod 16 = 1 // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - match self.legendre() { - LegendreSymbol::Zero => Some(*self), - LegendreSymbol::QuadraticNonResidue => None, - LegendreSymbol::QuadraticResidue => { - let mut c = Fr::root_of_unity(); - // r = self^((t + 1) // 2) - let mut r = self.pow([32]); - // t = self^t - let mut t = self.pow([63]); - let mut m = Fr::S; - - while t != ::one() { - let mut i = 1; - { - let mut t2i = t.square(); - loop { - if t2i == ::one() { - break; - } - t2i = t2i.square(); - i += 1; - } + let mut c = Fr::root_of_unity(); + // r = self^((t + 1) // 2) + let mut r = self.pow([32]); + // t = self^t + let mut t = self.pow([63]); + let mut m = Fr::S; + + while t != ::one() { + let mut i = 1; + { + let mut t2i = t.square(); + loop { + if t2i == ::one() { + break; } - - for _ in 0..(m - i - 1) { - c = c.square(); - } - MulAssign::mul_assign(&mut r, &c); - c = c.square(); - MulAssign::mul_assign(&mut t, &c); - m = i; + t2i = t2i.square(); + i += 1; } + } - Some(r) + for _ in 0..(m - i - 1) { + c = c.square(); } + MulAssign::mul_assign(&mut r, &c); + c = c.square(); + MulAssign::mul_assign(&mut t, &c); + m = i; } + + CtOption::new(r, (r * r).ct_eq(self)) } } @@ -382,8 +373,8 @@ impl Engine for DummyEngine { } /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(this: &Self::Fqk) -> Option { - Some(*this) + fn final_exponentiation(this: &Self::Fqk) -> CtOption { + CtOption::new(*this, Choice::from(1)) } } diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs index aaefb5f71b..f3349a4216 100644 --- a/bellman/src/groth16/tests/mod.rs +++ b/bellman/src/groth16/tests/mod.rs @@ -156,8 +156,8 @@ fn test_xordemo() { // We expect our H query to be 7 elements of the form... // {tau^i t(tau) / delta} - let delta_inverse = delta.inverse().unwrap(); - let gamma_inverse = gamma.inverse().unwrap(); + let delta_inverse = delta.invert().unwrap(); + let gamma_inverse = gamma.invert().unwrap(); { let mut coeff = delta_inverse; coeff.mul_assign(&t_at_tau); diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs index 09d5e12e29..e3f8ca3e2c 100644 --- a/ff/ff_derive/src/lib.rs +++ b/ff/ff_derive/src/lib.rs @@ -413,104 +413,81 @@ fn prime_field_constants_and_sqrt( ); let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs); - let mod_minus_1_over_2 = - biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs); - let legendre_impl = quote! { - fn legendre(&self) -> ::ff::LegendreSymbol { - // s = self^((modulus - 1) // 2) - let s = self.pow(#mod_minus_1_over_2); - if s == Self::zero() { - ::ff::LegendreSymbol::Zero - } else if s == Self::one() { - ::ff::LegendreSymbol::QuadraticResidue - } else { - ::ff::LegendreSymbol::QuadraticNonResidue - } - } - }; - - let sqrt_impl = - if (&modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { - let mod_minus_3_over_4 = - biguint_to_u64_vec((&modulus - BigUint::from_str("3").unwrap()) >> 2, limbs); - - // Compute -R as (m - r) - let rneg = biguint_to_u64_vec(&modulus - &r, limbs); - - quote! { - impl ::ff::SqrtField for #name { - #legendre_impl - - fn sqrt(&self) -> Option { - // Shank's algorithm for q mod 4 = 3 - // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) - - let mut a1 = self.pow(#mod_minus_3_over_4); - - let mut a0 = a1.square(); - a0.mul_assign(self); + let sqrt_impl = if (&modulus % BigUint::from_str("4").unwrap()) + == BigUint::from_str("3").unwrap() + { + let mod_plus_1_over_4 = + biguint_to_u64_vec((&modulus + BigUint::from_str("1").unwrap()) >> 2, limbs); - if a0.0 == #repr(#rneg) { - None - } else { - a1.mul_assign(self); - Some(a1) - } - } + quote! { + impl ::ff::SqrtField for #name { + fn sqrt(&self) -> ::subtle::CtOption { + use ::subtle::ConstantTimeEq; + + // Because r = 3 (mod 4) + // sqrt can be done with only one exponentiation, + // via the computation of self^((r + 1) // 4) (mod r) + let sqrt = self.pow(#mod_plus_1_over_4); + + ::subtle::CtOption::new( + sqrt, + (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. + ) } } - } else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - let t_plus_1_over_2 = biguint_to_u64_vec((&t + BigUint::one()) >> 1, limbs); - let t = biguint_to_u64_vec(t.clone(), limbs); - - quote! { - impl ::ff::SqrtField for #name { - #legendre_impl - - fn sqrt(&self) -> Option { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - match self.legendre() { - ::ff::LegendreSymbol::Zero => Some(*self), - ::ff::LegendreSymbol::QuadraticNonResidue => None, - ::ff::LegendreSymbol::QuadraticResidue => { - let mut c = #name(ROOT_OF_UNITY); - let mut r = self.pow(#t_plus_1_over_2); - let mut t = self.pow(#t); - let mut m = S; - - while t != Self::one() { - let mut i = 1; - { - let mut t2i = t.square(); - loop { - if t2i == Self::one() { - break; - } - t2i = t2i.square(); - i += 1; - } - } - - for _ in 0..(m - i - 1) { - c = c.square(); - } - r.mul_assign(&c); - c = c.square(); - t.mul_assign(&c); - m = i; - } - - Some(r) - } + } + } else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { + let t_minus_1_over_2 = biguint_to_u64_vec((&t - BigUint::one()) >> 1, limbs); + + quote! { + impl ::ff::SqrtField for #name { + fn sqrt(&self) -> ::subtle::CtOption { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + use ::subtle::{ConditionallySelectable, ConstantTimeEq}; + + // w = self^((t - 1) // 2) + let w = self.pow(#t_minus_1_over_2); + + let mut v = S; + let mut x = *self * &w; + let mut b = x * &w; + + // Initialize z as the 2^S root of unity. + let mut z = #name(ROOT_OF_UNITY); + + for max_v in (1..=S).rev() { + let mut k = 1; + let mut tmp = b.square(); + let mut j_less_than_v: ::subtle::Choice = 1.into(); + + for j in 2..max_v { + let tmp_is_one = tmp.ct_eq(&#name::one()); + let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); + tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); + let new_z = #name::conditional_select(&z, &squared, tmp_is_one); + j_less_than_v &= !j.ct_eq(&v); + k = u32::conditional_select(&j, &k, tmp_is_one); + z = #name::conditional_select(&z, &new_z, j_less_than_v); } + + let result = x * &z; + x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); + z = z.square(); + b = b * &z; + v = k; } + + ::subtle::CtOption::new( + x, + (x * &x).ct_eq(self), // Only return Some if it's the square root. + ) } } - } else { - quote! {} - }; + } + } else { + quote! {} + }; // Compute R^2 mod m let r2 = biguint_to_u64_vec((&r * &r) % &modulus, limbs); @@ -771,6 +748,13 @@ fn prime_field_impl( let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs); let montgomery_impl = mont_impl(limbs); + // (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ... + let mut ct_eq_impl = proc_macro2::TokenStream::new(); + ct_eq_impl.append_separated( + (0..limbs).map(|i| quote! { (self.0).0[#i].ct_eq(&(other.0).0[#i]) }), + proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), + ); + // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ... let mut into_repr_params = proc_macro2::TokenStream::new(); into_repr_params.append_separated( @@ -791,6 +775,18 @@ fn prime_field_impl( } } + impl ::std::default::Default for #name { + fn default() -> #name { + #name::zero() + } + } + + impl ::subtle::ConstantTimeEq for #name { + fn ct_eq(&self, other: &#name) -> ::subtle::Choice { + #ct_eq_impl + } + } + impl ::std::cmp::PartialEq for #name { fn eq(&self, other: &#name) -> bool { self.0 == other.0 @@ -1062,9 +1058,11 @@ fn prime_field_impl( ret } - fn inverse(&self) -> Option { + /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! + /// TODO: Make this constant-time. + fn invert(&self) -> ::subtle::CtOption { if self.is_zero() { - None + ::subtle::CtOption::new(#name::zero(), ::subtle::Choice::from(0)) } else { // Guajardo Kumar Paar Pelzl // Efficient Software-Implementation of Finite Fields with Applications to Cryptography @@ -1110,9 +1108,9 @@ fn prime_field_impl( } if u == one { - Some(b) + ::subtle::CtOption::new(b, ::subtle::Choice::from(1)) } else { - Some(c) + ::subtle::CtOption::new(c, ::subtle::Choice::from(1)) } } } diff --git a/ff/src/lib.rs b/ff/src/lib.rs index e59e627274..d4602ba41c 100644 --- a/ff/src/lib.rs +++ b/ff/src/lib.rs @@ -12,7 +12,7 @@ use std::error::Error; use std::fmt; use std::io::{self, Read, Write}; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::ConditionallySelectable; +use subtle::{ConditionallySelectable, CtOption}; /// This trait represents an element of a field. pub trait Field: @@ -20,6 +20,7 @@ pub trait Field: + Eq + Copy + Clone + + Default + Send + Sync + fmt::Debug @@ -60,8 +61,9 @@ pub trait Field: #[must_use] fn double(&self) -> Self; - /// Computes the multiplicative inverse of this element, if nonzero. - fn inverse(&self) -> Option; + /// Computes the multiplicative inverse of this element, + /// failing if the element is zero. + fn invert(&self) -> CtOption; /// Exponentiates this element by a power of the base prime modulus via /// the Frobenius automorphism. @@ -92,12 +94,9 @@ pub trait Field: /// This trait represents an element of a field that has a square root operation described for it. pub trait SqrtField: Field { - /// Returns the Legendre symbol of the field element. - fn legendre(&self) -> LegendreSymbol; - /// Returns the square root of the field element, if it is /// quadratic residue. - fn sqrt(&self) -> Option; + fn sqrt(&self) -> CtOption; } /// This trait represents a wrapper around a biginteger which can encode any element of a particular @@ -197,13 +196,6 @@ pub trait PrimeFieldRepr: } } -#[derive(Debug, PartialEq)] -pub enum LegendreSymbol { - Zero = 0, - QuadraticResidue = 1, - QuadraticNonResidue = -1, -} - /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a /// `PrimeField` element. #[derive(Debug)] diff --git a/pairing/benches/bls12_381/fq.rs b/pairing/benches/bls12_381/fq.rs index bd4ed15d5d..5ef5768026 100644 --- a/pairing/benches/bls12_381/fq.rs +++ b/pairing/benches/bls12_381/fq.rs @@ -217,7 +217,7 @@ fn bench_fq_square(b: &mut ::test::Bencher) { } #[bench] -fn bench_fq_inverse(b: &mut ::test::Bencher) { +fn bench_fq_invert(b: &mut ::test::Bencher) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -230,7 +230,7 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) { let mut count = 0; b.iter(|| { count = (count + 1) % SAMPLES; - v[count].inverse() + v[count].invert() }); } diff --git a/pairing/benches/bls12_381/fq12.rs b/pairing/benches/bls12_381/fq12.rs index b79bf5c68d..eedd9f8de6 100644 --- a/pairing/benches/bls12_381/fq12.rs +++ b/pairing/benches/bls12_381/fq12.rs @@ -91,7 +91,7 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) { } #[bench] -fn bench_fq12_inverse(b: &mut ::test::Bencher) { +fn bench_fq12_invert(b: &mut ::test::Bencher) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -103,7 +103,7 @@ fn bench_fq12_inverse(b: &mut ::test::Bencher) { let mut count = 0; b.iter(|| { - let tmp = v[count].inverse(); + let tmp = v[count].invert(); count = (count + 1) % SAMPLES; tmp }); diff --git a/pairing/benches/bls12_381/fq2.rs b/pairing/benches/bls12_381/fq2.rs index ace017187c..d3a2b4d511 100644 --- a/pairing/benches/bls12_381/fq2.rs +++ b/pairing/benches/bls12_381/fq2.rs @@ -91,7 +91,7 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) { } #[bench] -fn bench_fq2_inverse(b: &mut ::test::Bencher) { +fn bench_fq2_invert(b: &mut ::test::Bencher) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -103,7 +103,7 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) { let mut count = 0; b.iter(|| { - let tmp = v[count].inverse(); + let tmp = v[count].invert(); count = (count + 1) % SAMPLES; tmp }); diff --git a/pairing/benches/bls12_381/fr.rs b/pairing/benches/bls12_381/fr.rs index e4d07d2867..4e3d4c2e2e 100644 --- a/pairing/benches/bls12_381/fr.rs +++ b/pairing/benches/bls12_381/fr.rs @@ -217,7 +217,7 @@ fn bench_fr_square(b: &mut ::test::Bencher) { } #[bench] -fn bench_fr_inverse(b: &mut ::test::Bencher) { +fn bench_fr_invert(b: &mut ::test::Bencher) { const SAMPLES: usize = 1000; let mut rng = XorShiftRng::from_seed([ @@ -230,7 +230,7 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) { let mut count = 0; b.iter(|| { count = (count + 1) % SAMPLES; - v[count].inverse() + v[count].invert() }); } diff --git a/pairing/src/bls12_381/ec.rs b/pairing/src/bls12_381/ec.rs index abf6873b8a..513228934a 100644 --- a/pairing/src/bls12_381/ec.rs +++ b/pairing/src/bls12_381/ec.rs @@ -97,7 +97,7 @@ macro_rules! curve_impl { /// /// If and only if `greatest` is set will the lexicographically /// largest y-coordinate be selected. - fn get_point_from_x(x: $basefield, greatest: bool) -> Option<$affine> { + fn get_point_from_x(x: $basefield, greatest: bool) -> CtOption<$affine> { // Compute x^3 + b let mut x3b = x.square(); x3b.mul_assign(&x); @@ -199,8 +199,9 @@ macro_rules! curve_impl { let x = $basefield::random(rng); let greatest = rng.next_u32() % 2 != 0; - if let Some(p) = $affine::get_point_from_x(x, greatest) { - let p = p.scale_by_cofactor(); + let p = $affine::get_point_from_x(x, greatest); + if p.is_some().into() { + let p = p.unwrap().scale_by_cofactor(); if !p.is_zero() { return p; @@ -251,7 +252,7 @@ macro_rules! curve_impl { } // Invert `tmp`. - tmp = tmp.inverse().unwrap(); // Guaranteed to be nonzero. + tmp = tmp.invert().unwrap(); // Guaranteed to be nonzero. // Second pass: iterate backwards to compute inverses for (g, s) in v @@ -571,7 +572,7 @@ macro_rules! curve_impl { } } else { // Z is nonzero, so it must have an inverse in a field. - let zinv = p.z.inverse().unwrap(); + let zinv = p.z.invert().unwrap(); let mut zinv_powered = zinv.square(); // X/Z^2 @@ -603,6 +604,7 @@ pub mod g1 { use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; + use subtle::CtOption; curve_impl!( "G1", @@ -807,7 +809,12 @@ pub mod g1 { let x = Fq::from_repr(x) .map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; - G1Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + let ret = G1Affine::get_point_from_x(x, greatest); + if ret.is_some().into() { + Ok(ret.unwrap()) + } else { + Err(GroupDecodingError::NotOnCurve) + } } } fn from_affine(affine: G1Affine) -> Self { @@ -919,7 +926,9 @@ pub mod g1 { rhs.mul_assign(&x); rhs.add_assign(&G1Affine::get_coeff_b()); - if let Some(y) = rhs.sqrt() { + let y = rhs.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); let yrepr = y.into_repr(); let negy = y.neg(); let negyrepr = negy.into_repr(); @@ -1270,6 +1279,7 @@ pub mod g2 { use rand_core::RngCore; use std::fmt; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; + use subtle::CtOption; curve_impl!( "G2", @@ -1498,7 +1508,12 @@ pub mod g2 { })?, }; - G2Affine::get_point_from_x(x, greatest).ok_or(GroupDecodingError::NotOnCurve) + let ret = G2Affine::get_point_from_x(x, greatest); + if ret.is_some().into() { + Ok(ret.unwrap()) + } else { + Err(GroupDecodingError::NotOnCurve) + } } } fn from_affine(affine: G2Affine) -> Self { @@ -1623,7 +1638,9 @@ pub mod g2 { rhs.mul_assign(&x); rhs.add_assign(&G2Affine::get_coeff_b()); - if let Some(y) = rhs.sqrt() { + let y = rhs.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); let negy = y.neg(); let p = G2Affine { diff --git a/pairing/src/bls12_381/fq.rs b/pairing/src/bls12_381/fq.rs index 28326f403b..509008361a 100644 --- a/pairing/src/bls12_381/fq.rs +++ b/pairing/src/bls12_381/fq.rs @@ -1965,8 +1965,8 @@ fn test_fq_squaring() { } #[test] -fn test_fq_inverse() { - assert!(Fq::zero().inverse().is_none()); +fn test_fq_invert() { + assert!(bool::from(Fq::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1978,7 +1978,7 @@ fn test_fq_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fq::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -2074,8 +2074,9 @@ fn test_fq_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fq::random(&mut rng); - if let Some(tmp) = a.sqrt() { - assert_eq!(a, tmp.square()); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } @@ -2205,7 +2206,7 @@ fn test_fq_root_of_unity() { Fq::root_of_unity() ); assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one()); - assert!(Fq::multiplicative_generator().sqrt().is_none()); + assert!(bool::from(Fq::multiplicative_generator().sqrt().is_none())); } #[test] @@ -2231,40 +2232,3 @@ fn test_fq_ordering() { fn fq_repr_tests() { crate::tests::repr::random_repr_tests::(); } - -#[test] -fn test_fq_legendre() { - use ff::LegendreSymbol::*; - use ff::SqrtField; - - assert_eq!(QuadraticResidue, Fq::one().legendre()); - assert_eq!(Zero, Fq::zero().legendre()); - - assert_eq!( - QuadraticNonResidue, - Fq::from_repr(FqRepr::from(2)).unwrap().legendre() - ); - assert_eq!( - QuadraticResidue, - Fq::from_repr(FqRepr::from(4)).unwrap().legendre() - ); - - let e = FqRepr([ - 0x52a112f249778642, - 0xd0bedb989b7991f, - 0xdad3b6681aa63c05, - 0xf2efc0bb4721b283, - 0x6057a98f18c24733, - 0x1022c2fd122889e4, - ]); - assert_eq!(QuadraticNonResidue, Fq::from_repr(e).unwrap().legendre()); - let e = FqRepr([ - 0x6dae594e53a96c74, - 0x19b16ca9ba64b37b, - 0x5c764661a59bfc68, - 0xaa346e9b31c60a, - 0x346059f9d87a9fa9, - 0x1d61ac6bfd5c88b, - ]); - assert_eq!(QuadraticResidue, Fq::from_repr(e).unwrap().legendre()); -} diff --git a/pairing/src/bls12_381/fq12.rs b/pairing/src/bls12_381/fq12.rs index 66608fa872..7e2751b97f 100644 --- a/pairing/src/bls12_381/fq12.rs +++ b/pairing/src/bls12_381/fq12.rs @@ -4,10 +4,10 @@ use super::fq6::Fq6; use ff::Field; use rand_core::RngCore; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq12, represented by c0 + c1 * w. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq12 { pub c0: Fq6, pub c1: Fq6, @@ -226,13 +226,13 @@ impl Field for Fq12 { Fq12 { c0, c1 } } - fn inverse(&self) -> Option { + fn invert(&self) -> CtOption { let mut c0s = self.c0.square(); let mut c1s = self.c1.square(); c1s.mul_by_nonresidue(); c0s.sub_assign(&c1s); - c0s.inverse().map(|t| Fq12 { + c0s.invert().map(|t| Fq12 { c0: t.mul(&self.c0), c1: t.mul(&self.c1).neg(), }) diff --git a/pairing/src/bls12_381/fq2.rs b/pairing/src/bls12_381/fq2.rs index 823c635fef..8f7cbb2d85 100644 --- a/pairing/src/bls12_381/fq2.rs +++ b/pairing/src/bls12_381/fq2.rs @@ -3,10 +3,10 @@ use ff::{Field, SqrtField}; use rand_core::RngCore; use std::cmp::Ordering; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq2, represented by c0 + c1 * u. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq2 { pub c0: Fq, pub c1: Fq, @@ -228,11 +228,11 @@ impl Field for Fq2 { } } - fn inverse(&self) -> Option { + fn invert(&self) -> CtOption { let t1 = self.c1.square(); let mut t0 = self.c0.square(); t0.add_assign(&t1); - t0.inverse().map(|t| Fq2 { + t0.invert().map(|t| Fq2 { c0: self.c0.mul(&t), c1: self.c1.mul(&t).neg(), }) @@ -244,15 +244,13 @@ impl Field for Fq2 { } impl SqrtField for Fq2 { - fn legendre(&self) -> ::ff::LegendreSymbol { - self.norm().legendre() - } - - fn sqrt(&self) -> Option { + /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! + /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME! + fn sqrt(&self) -> CtOption { // Algorithm 9, https://eprint.iacr.org/2012/685.pdf if self.is_zero() { - Some(Self::zero()) + CtOption::new(Self::zero(), Choice::from(1)) } else { // a1 = self^((q - 3) / 4) let mut a1 = self.pow([ @@ -275,7 +273,7 @@ impl SqrtField for Fq2 { }; if a0 == neg1 { - None + CtOption::new(Self::zero(), Choice::from(0)) } else { a1.mul_assign(self); @@ -298,7 +296,7 @@ impl SqrtField for Fq2 { a1.mul_assign(&alpha); } - Some(a1) + CtOption::new(a1, Choice::from(1)) } } } @@ -497,11 +495,11 @@ fn test_fq2_mul() { } #[test] -fn test_fq2_inverse() { +fn test_fq2_invert() { use super::fq::FqRepr; use ff::PrimeField; - assert!(Fq2::zero().inverse().is_none()); + assert!(bool::from(Fq2::zero().invert().is_none())); let a = Fq2 { c0: Fq::from_repr(FqRepr([ @@ -523,7 +521,7 @@ fn test_fq2_inverse() { ])) .unwrap(), }; - let a = a.inverse().unwrap(); + let a = a.invert().unwrap(); assert_eq!( a, Fq2 { @@ -993,18 +991,6 @@ fn test_fq2_sqrt() { ); } -#[test] -fn test_fq2_legendre() { - use ff::LegendreSymbol::*; - - assert_eq!(Zero, Fq2::zero().legendre()); - // i^2 = -1 - let mut m1 = Fq2::one().neg(); - assert_eq!(QuadraticResidue, m1.legendre()); - m1.mul_by_nonresidue(); - assert_eq!(QuadraticNonResidue, m1.legendre()); -} - #[cfg(test)] use rand_core::SeedableRng; #[cfg(test)] diff --git a/pairing/src/bls12_381/fq6.rs b/pairing/src/bls12_381/fq6.rs index a64d25b1ec..1b3be7f06b 100644 --- a/pairing/src/bls12_381/fq6.rs +++ b/pairing/src/bls12_381/fq6.rs @@ -3,10 +3,10 @@ use super::fq2::Fq2; use ff::Field; use rand_core::RngCore; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; +use subtle::{Choice, ConditionallySelectable, CtOption}; /// An element of Fq6, represented by c0 + c1 * v + c2 * v^(2). -#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] pub struct Fq6 { pub c0: Fq2, pub c1: Fq2, @@ -345,7 +345,7 @@ impl Field for Fq6 { Fq6 { c0, c1, c2 } } - fn inverse(&self) -> Option { + fn invert(&self) -> CtOption { let mut c0 = self.c2; c0.mul_by_nonresidue(); c0.mul_assign(&self.c1); @@ -378,21 +378,18 @@ impl Field for Fq6 { tmp2.mul_assign(&c0); tmp1.add_assign(&tmp2); - match tmp1.inverse() { - Some(t) => { - let mut tmp = Fq6 { - c0: t, - c1: t, - c2: t, - }; - tmp.c0.mul_assign(&c0); - tmp.c1.mul_assign(&c1); - tmp.c2.mul_assign(&c2); - - Some(tmp) - } - None => None, - } + tmp1.invert().map(|t| { + let mut tmp = Fq6 { + c0: t, + c1: t, + c2: t, + }; + tmp.c0.mul_assign(&c0); + tmp.c1.mul_assign(&c1); + tmp.c2.mul_assign(&c2); + + tmp + }) } } diff --git a/pairing/src/bls12_381/fr.rs b/pairing/src/bls12_381/fr.rs index 777aa87213..b2fa4e1dff 100644 --- a/pairing/src/bls12_381/fr.rs +++ b/pairing/src/bls12_381/fr.rs @@ -278,30 +278,6 @@ fn test_fr_repr_sub_noborrow() { ); } -#[test] -fn test_fr_legendre() { - use ff::LegendreSymbol::*; - use ff::SqrtField; - - assert_eq!(QuadraticResidue, Fr::one().legendre()); - assert_eq!(Zero, Fr::zero().legendre()); - - let e = FrRepr([ - 0x0dbc5349cd5664da, - 0x8ac5b6296e3ae29d, - 0x127cb819feceaa3b, - 0x3a6b21fb03867191, - ]); - assert_eq!(QuadraticResidue, Fr::from_repr(e).unwrap().legendre()); - let e = FrRepr([ - 0x96341aefd047c045, - 0x9b5f4254500a4d65, - 0x1ee08223b68ac240, - 0x31d9cd545c0ec7c6, - ]); - assert_eq!(QuadraticNonResidue, Fr::from_repr(e).unwrap().legendre()); -} - #[test] fn test_fr_repr_add_nocarry() { let mut rng = XorShiftRng::from_seed([ @@ -724,8 +700,8 @@ fn test_fr_squaring() { } #[test] -fn test_fr_inverse() { - assert!(Fr::zero().inverse().is_none()); +fn test_fr_invert() { + assert!(bool::from(Fr::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -737,7 +713,7 @@ fn test_fr_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fr::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -833,8 +809,9 @@ fn test_fr_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fr::random(&mut rng); - if let Some(tmp) = a.sqrt() { - assert_eq!(a, tmp.square()); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } @@ -996,7 +973,7 @@ fn test_fr_root_of_unity() { Fr::root_of_unity() ); assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one()); - assert!(Fr::multiplicative_generator().sqrt().is_none()); + assert!(bool::from(Fr::multiplicative_generator().sqrt().is_none())); } #[test] diff --git a/pairing/src/bls12_381/mod.rs b/pairing/src/bls12_381/mod.rs index 5843e94eb9..ad66cbde5b 100644 --- a/pairing/src/bls12_381/mod.rs +++ b/pairing/src/bls12_381/mod.rs @@ -26,6 +26,7 @@ use super::{Engine, PairingCurveAffine}; use ff::{BitIterator, Field, ScalarEngine}; use group::CurveAffine; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; // The BLS parameter x for BLS12-381 is -0xd201000000010000 const BLS_X: u64 = 0xd201000000010000; @@ -111,61 +112,58 @@ impl Engine for Bls12 { f } - fn final_exponentiation(r: &Fq12) -> Option { + fn final_exponentiation(r: &Fq12) -> CtOption { let mut f1 = *r; f1.conjugate(); - match r.inverse() { - Some(mut f2) => { - let mut r = f1; - r.mul_assign(&f2); - f2 = r; - r.frobenius_map(2); - r.mul_assign(&f2); - - fn exp_by_x(f: &mut Fq12, x: u64) { - *f = f.pow(&[x]); - if BLS_X_IS_NEGATIVE { - f.conjugate(); - } + r.invert().map(|mut f2| { + let mut r = f1; + r.mul_assign(&f2); + f2 = r; + r.frobenius_map(2); + r.mul_assign(&f2); + + fn exp_by_x(f: &mut Fq12, x: u64) { + *f = f.pow(&[x]); + if BLS_X_IS_NEGATIVE { + f.conjugate(); } - - let mut x = BLS_X; - let y0 = r.square(); - let mut y1 = y0; - exp_by_x(&mut y1, x); - x >>= 1; - let mut y2 = y1; - exp_by_x(&mut y2, x); - x <<= 1; - let mut y3 = r; - y3.conjugate(); - y1.mul_assign(&y3); - y1.conjugate(); - y1.mul_assign(&y2); - y2 = y1; - exp_by_x(&mut y2, x); - y3 = y2; - exp_by_x(&mut y3, x); - y1.conjugate(); - y3.mul_assign(&y1); - y1.conjugate(); - y1.frobenius_map(3); - y2.frobenius_map(2); - y1.mul_assign(&y2); - y2 = y3; - exp_by_x(&mut y2, x); - y2.mul_assign(&y0); - y2.mul_assign(&r); - y1.mul_assign(&y2); - y2 = y3; - y2.frobenius_map(1); - y1.mul_assign(&y2); - - Some(y1) } - None => None, - } + + let mut x = BLS_X; + let y0 = r.square(); + let mut y1 = y0; + exp_by_x(&mut y1, x); + x >>= 1; + let mut y2 = y1; + exp_by_x(&mut y2, x); + x <<= 1; + let mut y3 = r; + y3.conjugate(); + y1.mul_assign(&y3); + y1.conjugate(); + y1.mul_assign(&y2); + y2 = y1; + exp_by_x(&mut y2, x); + y3 = y2; + exp_by_x(&mut y3, x); + y1.conjugate(); + y3.mul_assign(&y1); + y1.conjugate(); + y1.frobenius_map(3); + y2.frobenius_map(2); + y1.mul_assign(&y2); + y2 = y3; + exp_by_x(&mut y2, x); + y2.mul_assign(&y0); + y2.mul_assign(&r); + y1.mul_assign(&y2); + y2 = y3; + y2.frobenius_map(1); + y1.mul_assign(&y2); + + y1 + }) } } diff --git a/pairing/src/bls12_381/tests/mod.rs b/pairing/src/bls12_381/tests/mod.rs index 2fd4debb69..9c5b2c93ff 100644 --- a/pairing/src/bls12_381/tests/mod.rs +++ b/pairing/src/bls12_381/tests/mod.rs @@ -193,7 +193,10 @@ fn test_g1_uncompressed_invalid_vectors() { x3b.mul_assign(&x); x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - if let Some(y) = x3b.sqrt() { + let y = x3b.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); + // We know this is on the curve, but it's likely not going to be in the correct subgroup. x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); y.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); @@ -332,7 +335,10 @@ fn test_g2_uncompressed_invalid_vectors() { c1: Fq::from_repr(FqRepr::from(4)).unwrap(), }); // TODO: perhaps expose coeff_b through API? - if let Some(y) = x3b.sqrt() { + let y = x3b.sqrt(); + if y.is_some().into() { + let y = y.unwrap(); + // We know this is on the curve, but it's likely not going to be in the correct subgroup. x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); @@ -424,7 +430,7 @@ fn test_g1_compressed_invalid_vectors() { x3b.mul_assign(&x); x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { x.add_assign(&Fq::one()); } else { x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); @@ -448,7 +454,7 @@ fn test_g1_compressed_invalid_vectors() { x3b.mul_assign(&x); x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); o.as_mut()[0] |= 0b1000_0000; @@ -556,7 +562,7 @@ fn test_g2_compressed_invalid_vectors() { c1: Fq::from_repr(FqRepr::from(4)).unwrap(), }); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { x.add_assign(&Fq2::one()); } else { x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); @@ -587,7 +593,7 @@ fn test_g2_compressed_invalid_vectors() { c1: Fq::from_repr(FqRepr::from(4)).unwrap(), }); // TODO: perhaps expose coeff_b through API? - if let Some(_) = x3b.sqrt() { + if x3b.sqrt().is_some().into() { // We know this is on the curve, but it's likely not going to be in the correct subgroup. x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs index 9afb42797a..bd060a12fc 100644 --- a/pairing/src/lib.rs +++ b/pairing/src/lib.rs @@ -22,6 +22,7 @@ pub mod bls12_381; use ff::{Field, PrimeField, ScalarEngine, SqrtField}; use group::{CurveAffine, CurveProjective}; +use subtle::CtOption; /// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// with well-defined relationships. In particular, the G1/G2 curve groups are @@ -75,7 +76,7 @@ pub trait Engine: ScalarEngine { >; /// Perform final exponentiation of the result of a miller loop. - fn final_exponentiation(_: &Self::Fqk) -> Option; + fn final_exponentiation(_: &Self::Fqk) -> CtOption; /// Performs a complete pairing operation `(p, q)`. fn pairing(p: G1, q: G2) -> Self::Fqk diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs index eaba476d7b..a073604334 100644 --- a/pairing/src/tests/field.rs +++ b/pairing/src/tests/field.rs @@ -1,4 +1,4 @@ -use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; +use ff::{Field, PrimeField, SqrtField}; use rand_core::{RngCore, SeedableRng}; use rand_xorshift::XorShiftRng; @@ -32,7 +32,6 @@ pub fn random_sqrt_tests() { for _ in 0..10000 { let a = F::random(&mut rng); let b = a.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); let b = b.sqrt().unwrap(); let negb = b.neg(); @@ -43,7 +42,6 @@ pub fn random_sqrt_tests() { let mut c = F::one(); for _ in 0..10000 { let mut b = c.square(); - assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); b = b.sqrt().unwrap(); @@ -78,7 +76,7 @@ pub fn random_field_tests() { assert!(z.is_zero()); } - assert!(F::zero().inverse().is_none()); + assert!(bool::from(F::zero().invert().is_none())); // Multiplication by zero { @@ -222,11 +220,11 @@ fn random_squaring_tests(rng: &mut R) { } fn random_inversion_tests(rng: &mut R) { - assert!(F::zero().inverse().is_none()); + assert!(bool::from(F::zero().invert().is_none())); for _ in 0..10000 { let mut a = F::random(rng); - let b = a.inverse().unwrap(); // probablistically nonzero + let b = a.invert().unwrap(); // probablistically nonzero a.mul_assign(&b); assert_eq!(a, F::one()); diff --git a/zcash_primitives/src/jubjub/edwards.rs b/zcash_primitives/src/jubjub/edwards.rs index 4ae24422db..1b3ebc0b65 100644 --- a/zcash_primitives/src/jubjub/edwards.rs +++ b/zcash_primitives/src/jubjub/edwards.rs @@ -1,5 +1,6 @@ use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; @@ -90,10 +91,14 @@ impl Point { y_repr.as_mut()[3] &= 0x7fffffffffffffff; match E::Fr::from_repr(y_repr) { - Ok(y) => match Self::get_for_y(y, x_sign, params) { - Some(p) => Ok(p), - None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")), - }, + Ok(y) => { + let p = Self::get_for_y(y, x_sign, params); + if bool::from(p.is_some()) { + Ok(p.unwrap()) + } else { + Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")) + } + } Err(_) => Err(io::Error::new( io::ErrorKind::InvalidInput, "y is not in field", @@ -101,7 +106,7 @@ impl Point { } } - pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option { + pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption { // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) // This is defined for all valid y-coordinates, // as dy^2 + 1 = 0 has no solution in Fr. @@ -117,33 +122,27 @@ impl Point { // tmp1 = y^2 - 1 tmp1.sub_assign(&E::Fr::one()); - match tmp2.inverse() { - Some(tmp2) => { - // tmp1 = (y^2 - 1) / (dy^2 + 1) - tmp1.mul_assign(&tmp2); - - match tmp1.sqrt() { - Some(mut x) => { - if x.into_repr().is_odd() != sign { - x = x.neg(); - } - - let mut t = x; - t.mul_assign(&y); - - Some(Point { - x, - y, - t, - z: E::Fr::one(), - _marker: PhantomData, - }) - } - None => None, + tmp2.invert().and_then(|tmp2| { + // tmp1 = (y^2 - 1) / (dy^2 + 1) + tmp1.mul_assign(&tmp2); + + tmp1.sqrt().map(|mut x| { + if x.into_repr().is_odd() != sign { + x = x.neg(); } - } - None => None, - } + + let mut t = x; + t.mul_assign(&y); + + Point { + x, + y, + t, + z: E::Fr::one(), + _marker: PhantomData, + } + }) + }) } /// This guarantees the point is in the prime order subgroup @@ -159,8 +158,9 @@ impl Point { let y = E::Fr::random(rng); let sign = rng.next_u32() % 2 != 0; - if let Some(p) = Self::get_for_y(y, sign, params) { - return p; + let p = Self::get_for_y(y, sign, params); + if bool::from(p.is_some()) { + return p.unwrap(); } } } @@ -305,7 +305,7 @@ impl Point { /// Convert to affine coordinates pub fn to_xy(&self) -> (E::Fr, E::Fr) { - let zinv = self.z.inverse().unwrap(); + let zinv = self.z.invert().unwrap(); let mut x = self.x; x.mul_assign(&zinv); diff --git a/zcash_primitives/src/jubjub/fs.rs b/zcash_primitives/src/jubjub/fs.rs index b1f1d3fca6..a493e7a3a2 100644 --- a/zcash_primitives/src/jubjub/fs.rs +++ b/zcash_primitives/src/jubjub/fs.rs @@ -1,12 +1,11 @@ use byteorder::{ByteOrder, LittleEndian}; use ff::{ - adc, mac_with_carry, sbb, BitIterator, Field, - LegendreSymbol::{self, *}, - PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField, + adc, mac_with_carry, sbb, BitIterator, Field, PrimeField, PrimeFieldDecodingError, + PrimeFieldRepr, SqrtField, }; use rand_core::RngCore; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use subtle::{Choice, ConditionallySelectable}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use super::ToUniform; @@ -258,6 +257,21 @@ impl PrimeFieldRepr for FsRepr { #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Fs(FsRepr); +impl Default for Fs { + fn default() -> Self { + Fs::zero() + } +} + +impl ConstantTimeEq for Fs { + fn ct_eq(&self, other: &Fs) -> Choice { + (self.0).0[0].ct_eq(&(other.0).0[0]) + & (self.0).0[1].ct_eq(&(other.0).0[1]) + & (self.0).0[2].ct_eq(&(other.0).0[2]) + & (self.0).0[3].ct_eq(&(other.0).0[3]) + } +} + impl ::std::fmt::Display for Fs { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { write!(f, "Fs({})", self.into_repr()) @@ -526,9 +540,11 @@ impl Field for Fs { ret } - fn inverse(&self) -> Option { + /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET! + /// THIS WILL BE REPLACED BY THE jubjub CRATE, WHICH IS CONSTANT TIME! + fn invert(&self) -> CtOption { if self.is_zero() { - None + CtOption::new(Self::zero(), Choice::from(0)) } else { // Guajardo Kumar Paar Pelzl // Efficient Software-Implementation of Finite Fields with Applications to Cryptography @@ -574,9 +590,9 @@ impl Field for Fs { } if u == one { - Some(b) + CtOption::new(b, Choice::from(1)) } else { - Some(c) + CtOption::new(c, Choice::from(1)) } } } @@ -723,24 +739,7 @@ impl ToUniform for Fs { } impl SqrtField for Fs { - fn legendre(&self) -> LegendreSymbol { - // s = self^((s - 1) // 2) - let s = self.pow([ - 0x684b872f6b7b965b, - 0x53341049e6640841, - 0x83339d80809a1d80, - 0x73eda753299d7d4, - ]); - if s == Self::zero() { - Zero - } else if s == Self::one() { - QuadraticResidue - } else { - QuadraticNonResidue - } - } - - fn sqrt(&self) -> Option { + fn sqrt(&self) -> CtOption { // Shank's algorithm for s mod 4 = 3 // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) @@ -753,13 +752,9 @@ impl SqrtField for Fs { ]); let mut a0 = a1.square(); a0.mul_assign(self); + a1.mul_assign(self); - if a0 == NEGATIVE_ONE { - None - } else { - a1.mul_assign(self); - Some(a1) - } + CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE)) } } @@ -1017,27 +1012,6 @@ fn test_fs_repr_sub_noborrow() { } } -#[test] -fn test_fs_legendre() { - assert_eq!(QuadraticResidue, Fs::one().legendre()); - assert_eq!(Zero, Fs::zero().legendre()); - - let e = FsRepr([ - 0x8385eec23df1f88e, - 0x9a01fb412b2dba16, - 0x4c928edcdd6c22f, - 0x9f2df7ef69ecef9, - ]); - assert_eq!(QuadraticResidue, Fs::from_repr(e).unwrap().legendre()); - let e = FsRepr([ - 0xe8ed9f299da78568, - 0x35efdebc88b2209, - 0xc82125cb1f916dbe, - 0x6813d2b38c39bd0, - ]); - assert_eq!(QuadraticNonResidue, Fs::from_repr(e).unwrap().legendre()); -} - #[test] fn test_fr_repr_add_nocarry() { let mut rng = XorShiftRng::from_seed([ @@ -1454,8 +1428,8 @@ fn test_fr_squaring() { } #[test] -fn test_fs_inverse() { - assert!(Fs::zero().inverse().is_none()); +fn test_fs_invert() { + assert!(bool::from(Fs::zero().invert().is_none())); let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, @@ -1467,7 +1441,7 @@ fn test_fs_inverse() { for _ in 0..1000 { // Ensure that a * a^-1 = 1 let mut a = Fs::random(&mut rng); - let ainv = a.inverse().unwrap(); + let ainv = a.invert().unwrap(); a.mul_assign(&ainv); assert_eq!(a, one); } @@ -1561,8 +1535,9 @@ fn test_fs_sqrt() { // Ensure sqrt(a)^2 = a for random a let a = Fs::random(&mut rng); - if let Some(tmp) = a.sqrt() { - assert_eq!(a, tmp.square()); + let tmp = a.sqrt(); + if tmp.is_some().into() { + assert_eq!(a, tmp.unwrap().square()); } } } @@ -1722,5 +1697,5 @@ fn test_fs_root_of_unity() { Fs::root_of_unity() ); assert_eq!(Fs::root_of_unity().pow([1 << Fs::S]), Fs::one()); - assert!(Fs::multiplicative_generator().sqrt().is_none()); + assert!(bool::from(Fs::multiplicative_generator().sqrt().is_none())); } diff --git a/zcash_primitives/src/jubjub/montgomery.rs b/zcash_primitives/src/jubjub/montgomery.rs index a708d028d3..9cad803e82 100644 --- a/zcash_primitives/src/jubjub/montgomery.rs +++ b/zcash_primitives/src/jubjub/montgomery.rs @@ -1,5 +1,6 @@ use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; +use subtle::CtOption; use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; @@ -47,7 +48,7 @@ impl PartialEq for Point { } impl Point { - pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option { + pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption { // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) let mut x2 = x.square(); @@ -58,21 +59,18 @@ impl Point { x2.mul_assign(&x); rhs.add_assign(&x2); - match rhs.sqrt() { - Some(mut y) => { - if y.into_repr().is_odd() != sign { - y = y.neg(); - } + rhs.sqrt().map(|mut y| { + if y.into_repr().is_odd() != sign { + y = y.neg(); + } - Some(Point { - x, - y, - infinity: false, - _marker: PhantomData, - }) + Point { + x, + y, + infinity: false, + _marker: PhantomData, } - None => None, - } + }) } /// This guarantees the point is in the prime order subgroup @@ -88,8 +86,9 @@ impl Point { let x = E::Fr::random(rng); let sign = rng.next_u32() % 2 != 0; - if let Some(p) = Self::get_for_x(x, sign, params) { - return p; + let p = Self::get_for_x(x, sign, params); + if p.is_some().into() { + return p.unwrap(); } } } @@ -139,11 +138,11 @@ impl Point { { let mut tmp = E::Fr::one(); tmp.sub_assign(&y); - u.mul_assign(&tmp.inverse().unwrap()) + u.mul_assign(&tmp.invert().unwrap()) } let mut v = u; - v.mul_assign(&x.inverse().unwrap()); + v.mul_assign(&x.invert().unwrap()); // Scale it into the correct curve constants v.mul_assign(params.scale()); @@ -226,7 +225,8 @@ impl Point { } { let tmp = self.y.double(); - delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero")); + // y is nonzero so this must be nonzero + delta.mul_assign(&tmp.invert().unwrap()); } let mut x3 = delta.square(); @@ -272,10 +272,8 @@ impl Point { { let mut tmp = other.x; tmp.sub_assign(&self.x); - delta.mul_assign( - &tmp.inverse() - .expect("self.x != other.x, so this must be nonzero"), - ); + // self.x != other.x, so this must be nonzero + delta.mul_assign(&tmp.invert().unwrap()); } let mut x3 = delta.square(); diff --git a/zcash_primitives/src/jubjub/tests.rs b/zcash_primitives/src/jubjub/tests.rs index f0bb464436..84b3a96497 100644 --- a/zcash_primitives/src/jubjub/tests.rs +++ b/zcash_primitives/src/jubjub/tests.rs @@ -1,6 +1,6 @@ use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; -use ff::{Field, LegendreSymbol, PrimeField, PrimeFieldRepr, SqrtField}; +use ff::{Field, PrimeField, PrimeFieldRepr, SqrtField}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use rand_core::{RngCore, SeedableRng}; @@ -234,7 +234,9 @@ fn test_get_for(params: &E::Params) { let y = E::Fr::random(rng); let sign = rng.next_u32() % 2 == 1; - if let Some(mut p) = edwards::Point::::get_for_y(y, sign, params) { + let p = edwards::Point::::get_for_y(y, sign, params); + if bool::from(p.is_some()) { + let mut p = p.unwrap(); assert!(p.to_xy().0.into_repr().is_odd() == sign); p = p.negate(); assert!(edwards::Point::::get_for_y(y, !sign, params).unwrap() == p); @@ -317,8 +319,8 @@ fn test_jubjub_params(params: &E::Params) { // The twisted Edwards addition law is complete when d is nonsquare // and a is square. - assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue); - assert!(a.legendre() == LegendreSymbol::QuadraticResidue); + assert!(bool::from(params.edwards_d().sqrt().is_none())); + assert!(bool::from(a.sqrt().is_some())); } { @@ -328,37 +330,37 @@ fn test_jubjub_params(params: &E::Params) { let mut tmp = *params.edwards_d(); // 1 / d is nonsquare - assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); // tmp = -d tmp = tmp.neg(); // -d is nonsquare - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); // 1 / -d is nonsquare - assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.invert().unwrap().sqrt().is_none())); } { // Check that A^2 - 4 is nonsquare: let mut tmp = params.montgomery_a().square(); tmp.sub_assign(&E::Fr::from_str("4").unwrap()); - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); } { // Check that A - 2 is nonsquare: let mut tmp = params.montgomery_a().clone(); tmp.sub_assign(&E::Fr::from_str("2").unwrap()); - assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); + assert!(bool::from(tmp.sqrt().is_none())); } { // Check the validity of the scaling factor let mut tmp = a; tmp.sub_assign(¶ms.edwards_d()); - tmp = tmp.inverse().unwrap(); + tmp = tmp.invert().unwrap(); tmp.mul_assign(&E::Fr::from_str("4").unwrap()); tmp = tmp.sqrt().unwrap(); assert_eq!(&tmp, params.scale()); diff --git a/zcash_proofs/src/circuit/ecc.rs b/zcash_proofs/src/circuit/ecc.rs index 29c14a44a9..05baf8b607 100644 --- a/zcash_proofs/src/circuit/ecc.rs +++ b/zcash_proofs/src/circuit/ecc.rs @@ -344,13 +344,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.add_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let res = t1.invert().map(|t1| t0 * &t1); + if bool::from(res.is_some()) { + Ok(res.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -371,13 +369,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.sub_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let res = t1.invert().map(|t1| t0 * &t1); + if bool::from(res.is_some()) { + Ok(res.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -451,13 +447,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.add_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -478,13 +472,11 @@ impl EdwardsPoint { let mut t1 = E::Fr::one(); t1.sub_assign(c.get_value().get()?); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -521,13 +513,11 @@ impl MontgomeryPoint { let mut t0 = *self.x.get_value().get()?; t0.mul_assign(params.scale()); - match self.y.get_value().get()?.inverse() { - Some(invy) => { - t0.mul_assign(&invy); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = self.y.get_value().get()?.invert().map(|invy| t0 * &invy); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -545,13 +535,11 @@ impl MontgomeryPoint { t0.sub_assign(&E::Fr::one()); t1.add_assign(&E::Fr::one()); - match t1.inverse() { - Some(t1) => { - t0.mul_assign(&t1); - - Ok(t0) - } - None => Err(SynthesisError::DivisionByZero), + let ret = t1.invert().map(|t1| t0 * &t1); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -593,12 +581,11 @@ impl MontgomeryPoint { let mut d = *other.x.get_value().get()?; d.sub_assign(self.x.get_value().get()?); - match d.inverse() { - Some(d) => { - n.mul_assign(&d); - Ok(n) - } - None => Err(SynthesisError::DivisionByZero), + let ret = d.invert().map(|d| n * &d); + if bool::from(ret.is_some()) { + Ok(ret.unwrap()) + } else { + Err(SynthesisError::DivisionByZero) } })?; @@ -1038,8 +1025,9 @@ mod test { let x = Fr::random(rng); let s: bool = rng.next_u32() % 2 != 0; - if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { - break p; + let p = montgomery::Point::::get_for_x(x, s, params); + if p.is_some().into() { + break p.unwrap(); } }; @@ -1047,8 +1035,9 @@ mod test { let x = Fr::random(rng); let s: bool = rng.next_u32() % 2 != 0; - if let Some(p) = montgomery::Point::::get_for_x(x, s, params) { - break p; + let p = montgomery::Point::::get_for_x(x, s, params); + if p.is_some().into() { + break p.unwrap(); } };