Merge pull request #191 from str4d/ff-ct-inv-and-sqrt-apis

Constant-time APIs for ff::Field::[invert, sqrt]
This commit is contained in:
str4d 2019-12-13 22:29:50 +00:00 committed by GitHub
commit e88e2a9dc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 457 additions and 557 deletions

View File

@ -73,11 +73,11 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
coeffs, coeffs,
exp, exp,
omega, omega,
omegainv: omega.inverse().unwrap(), omegainv: omega.invert().unwrap(),
geninv: E::Fr::multiplicative_generator().inverse().unwrap(), geninv: E::Fr::multiplicative_generator().invert().unwrap(),
minv: E::Fr::from_str(&format!("{}", m)) minv: E::Fr::from_str(&format!("{}", m))
.unwrap() .unwrap()
.inverse() .invert()
.unwrap(), .unwrap(),
}) })
} }
@ -141,10 +141,7 @@ impl<E: ScalarEngine, G: Group<E>> EvaluationDomain<E, G> {
/// evaluation domain, so we must perform division over /// evaluation domain, so we must perform division over
/// a coset. /// a coset.
pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { pub fn divide_by_z_on_coset(&mut self, worker: &Worker) {
let i = self let i = self.z(&E::Fr::multiplicative_generator()).invert().unwrap();
.z(&E::Fr::multiplicative_generator())
.inverse()
.unwrap();
worker.scope(self.coeffs.len(), |scope, chunk| { worker.scope(self.coeffs.len(), |scope, chunk| {
for v in self.coeffs.chunks_mut(chunk) { for v in self.coeffs.chunks_mut(chunk) {

View File

@ -288,7 +288,7 @@ impl<E: ScalarEngine> AllocatedNum<E> {
if tmp.is_zero() { if tmp.is_zero() {
Err(SynthesisError::DivisionByZero) Err(SynthesisError::DivisionByZero)
} else { } else {
Ok(tmp.inverse().unwrap()) Ok(tmp.invert().unwrap())
} }
}, },
)?; )?;

View File

@ -215,8 +215,22 @@ where
assembly.num_inputs + assembly.num_aux assembly.num_inputs + assembly.num_aux
}); });
let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; let gamma_inverse = {
let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; 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(); let worker = Worker::new();

View File

@ -1,7 +1,4 @@
use ff::{ use ff::{Field, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine, SqrtField};
Field, LegendreSymbol, PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, ScalarEngine,
SqrtField,
};
use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError}; use group::{CurveAffine, CurveProjective, EncodedPoint, GroupDecodingError};
use pairing::{Engine, PairingCurveAffine}; use pairing::{Engine, PairingCurveAffine};
@ -10,13 +7,25 @@ use std::cmp::Ordering;
use std::fmt; use std::fmt;
use std::num::Wrapping; use std::num::Wrapping;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use subtle::{Choice, ConditionallySelectable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
const MODULUS_R: Wrapping<u32> = Wrapping(64513); const MODULUS_R: Wrapping<u32> = Wrapping(64513);
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Fr(Wrapping<u32>); pub struct Fr(Wrapping<u32>);
impl Default for Fr {
fn default() -> Self {
<Fr as Field>::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 { impl fmt::Display for Fr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", (self.0).0) write!(f, "{}", (self.0).0)
@ -159,11 +168,11 @@ impl Field for Fr {
Fr((self.0 << 1) % MODULUS_R) Fr((self.0 << 1) % MODULUS_R)
} }
fn inverse(&self) -> Option<Self> { fn invert(&self) -> CtOption<Self> {
if <Fr as Field>::is_zero(self) { if <Fr as Field>::is_zero(self) {
None CtOption::new(<Fr as Field>::zero(), Choice::from(0))
} else { } 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 { impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol { fn sqrt(&self) -> CtOption<Self> {
// s = self^((r - 1) // 2)
let s = self.pow([32256]);
if s == <Fr as Field>::zero() {
LegendreSymbol::Zero
} else if s == <Fr as Field>::one() {
LegendreSymbol::QuadraticResidue
} else {
LegendreSymbol::QuadraticNonResidue
}
}
fn sqrt(&self) -> Option<Self> {
// Tonelli-Shank's algorithm for q mod 16 = 1 // Tonelli-Shank's algorithm for q mod 16 = 1
// https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5)
match self.legendre() { let mut c = Fr::root_of_unity();
LegendreSymbol::Zero => Some(*self), // r = self^((t + 1) // 2)
LegendreSymbol::QuadraticNonResidue => None, let mut r = self.pow([32]);
LegendreSymbol::QuadraticResidue => { // t = self^t
let mut c = Fr::root_of_unity(); let mut t = self.pow([63]);
// r = self^((t + 1) // 2) let mut m = Fr::S;
let mut r = self.pow([32]);
// t = self^t
let mut t = self.pow([63]);
let mut m = Fr::S;
while t != <Fr as Field>::one() { while t != <Fr as Field>::one() {
let mut i = 1; let mut i = 1;
{ {
let mut t2i = t.square(); let mut t2i = t.square();
loop { loop {
if t2i == <Fr as Field>::one() { if t2i == <Fr as Field>::one() {
break; break;
}
t2i = t2i.square();
i += 1;
}
} }
t2i = t2i.square();
for _ in 0..(m - i - 1) { i += 1;
c = c.square();
}
MulAssign::mul_assign(&mut r, &c);
c = c.square();
MulAssign::mul_assign(&mut t, &c);
m = i;
} }
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. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(this: &Self::Fqk) -> Option<Self::Fqk> { fn final_exponentiation(this: &Self::Fqk) -> CtOption<Self::Fqk> {
Some(*this) CtOption::new(*this, Choice::from(1))
} }
} }

View File

@ -156,8 +156,8 @@ fn test_xordemo() {
// We expect our H query to be 7 elements of the form... // We expect our H query to be 7 elements of the form...
// {tau^i t(tau) / delta} // {tau^i t(tau) / delta}
let delta_inverse = delta.inverse().unwrap(); let delta_inverse = delta.invert().unwrap();
let gamma_inverse = gamma.inverse().unwrap(); let gamma_inverse = gamma.invert().unwrap();
{ {
let mut coeff = delta_inverse; let mut coeff = delta_inverse;
coeff.mul_assign(&t_at_tau); coeff.mul_assign(&t_at_tau);

View File

@ -413,105 +413,82 @@ fn prime_field_constants_and_sqrt(
); );
let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs); let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs);
let mod_minus_1_over_2 = let sqrt_impl = if (&modulus % BigUint::from_str("4").unwrap())
biguint_to_u64_vec((&modulus - BigUint::from_str("1").unwrap()) >> 1, limbs); == BigUint::from_str("3").unwrap()
let legendre_impl = quote! { {
fn legendre(&self) -> ::ff::LegendreSymbol { let mod_plus_1_over_4 =
// s = self^((modulus - 1) // 2) biguint_to_u64_vec((&modulus + BigUint::from_str("1").unwrap()) >> 2, limbs);
let s = self.pow(#mod_minus_1_over_2);
if s == Self::zero() { quote! {
::ff::LegendreSymbol::Zero impl ::ff::SqrtField for #name {
} else if s == Self::one() { fn sqrt(&self) -> ::subtle::CtOption<Self> {
::ff::LegendreSymbol::QuadraticResidue use ::subtle::ConstantTimeEq;
} else {
::ff::LegendreSymbol::QuadraticNonResidue // 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_minus_1_over_2 = biguint_to_u64_vec((&t - BigUint::one()) >> 1, limbs);
quote! {
impl ::ff::SqrtField for #name {
fn sqrt(&self) -> ::subtle::CtOption<Self> {
// 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! {}
}; };
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<Self> {
// 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);
if a0.0 == #repr(#rneg) {
None
} else {
a1.mul_assign(self);
Some(a1)
}
}
}
}
} 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<Self> {
// 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 {
quote! {}
};
// Compute R^2 mod m // Compute R^2 mod m
let r2 = biguint_to_u64_vec((&r * &r) % &modulus, limbs); 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 multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
let montgomery_impl = mont_impl(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, ... // (self.0).0[0], (self.0).0[1], ..., 0, 0, 0, 0, ...
let mut into_repr_params = proc_macro2::TokenStream::new(); let mut into_repr_params = proc_macro2::TokenStream::new();
into_repr_params.append_separated( 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 { impl ::std::cmp::PartialEq for #name {
fn eq(&self, other: &#name) -> bool { fn eq(&self, other: &#name) -> bool {
self.0 == other.0 self.0 == other.0
@ -1062,9 +1058,11 @@ fn prime_field_impl(
ret ret
} }
fn inverse(&self) -> Option<Self> { /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
/// TODO: Make this constant-time.
fn invert(&self) -> ::subtle::CtOption<Self> {
if self.is_zero() { if self.is_zero() {
None ::subtle::CtOption::new(#name::zero(), ::subtle::Choice::from(0))
} else { } else {
// Guajardo Kumar Paar Pelzl // Guajardo Kumar Paar Pelzl
// Efficient Software-Implementation of Finite Fields with Applications to Cryptography // Efficient Software-Implementation of Finite Fields with Applications to Cryptography
@ -1110,9 +1108,9 @@ fn prime_field_impl(
} }
if u == one { if u == one {
Some(b) ::subtle::CtOption::new(b, ::subtle::Choice::from(1))
} else { } else {
Some(c) ::subtle::CtOption::new(c, ::subtle::Choice::from(1))
} }
} }
} }

View File

@ -12,7 +12,7 @@ use std::error::Error;
use std::fmt; use std::fmt;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 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. /// This trait represents an element of a field.
pub trait Field: pub trait Field:
@ -20,6 +20,7 @@ pub trait Field:
+ Eq + Eq
+ Copy + Copy
+ Clone + Clone
+ Default
+ Send + Send
+ Sync + Sync
+ fmt::Debug + fmt::Debug
@ -60,8 +61,9 @@ pub trait Field:
#[must_use] #[must_use]
fn double(&self) -> Self; fn double(&self) -> Self;
/// Computes the multiplicative inverse of this element, if nonzero. /// Computes the multiplicative inverse of this element,
fn inverse(&self) -> Option<Self>; /// failing if the element is zero.
fn invert(&self) -> CtOption<Self>;
/// Exponentiates this element by a power of the base prime modulus via /// Exponentiates this element by a power of the base prime modulus via
/// the Frobenius automorphism. /// 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. /// This trait represents an element of a field that has a square root operation described for it.
pub trait SqrtField: Field { 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 /// Returns the square root of the field element, if it is
/// quadratic residue. /// quadratic residue.
fn sqrt(&self) -> Option<Self>; fn sqrt(&self) -> CtOption<Self>;
} }
/// This trait represents a wrapper around a biginteger which can encode any element of a particular /// 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 /// An error that may occur when trying to interpret a `PrimeFieldRepr` as a
/// `PrimeField` element. /// `PrimeField` element.
#[derive(Debug)] #[derive(Debug)]

View File

@ -217,7 +217,7 @@ fn bench_fq_square(b: &mut ::test::Bencher) {
} }
#[bench] #[bench]
fn bench_fq_inverse(b: &mut ::test::Bencher) { fn bench_fq_invert(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -230,7 +230,7 @@ fn bench_fq_inverse(b: &mut ::test::Bencher) {
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
count = (count + 1) % SAMPLES; count = (count + 1) % SAMPLES;
v[count].inverse() v[count].invert()
}); });
} }

View File

@ -91,7 +91,7 @@ fn bench_fq12_squaring(b: &mut ::test::Bencher) {
} }
#[bench] #[bench]
fn bench_fq12_inverse(b: &mut ::test::Bencher) { fn bench_fq12_invert(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -103,7 +103,7 @@ fn bench_fq12_inverse(b: &mut ::test::Bencher) {
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
let tmp = v[count].inverse(); let tmp = v[count].invert();
count = (count + 1) % SAMPLES; count = (count + 1) % SAMPLES;
tmp tmp
}); });

View File

@ -91,7 +91,7 @@ fn bench_fq2_squaring(b: &mut ::test::Bencher) {
} }
#[bench] #[bench]
fn bench_fq2_inverse(b: &mut ::test::Bencher) { fn bench_fq2_invert(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -103,7 +103,7 @@ fn bench_fq2_inverse(b: &mut ::test::Bencher) {
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
let tmp = v[count].inverse(); let tmp = v[count].invert();
count = (count + 1) % SAMPLES; count = (count + 1) % SAMPLES;
tmp tmp
}); });

View File

@ -217,7 +217,7 @@ fn bench_fr_square(b: &mut ::test::Bencher) {
} }
#[bench] #[bench]
fn bench_fr_inverse(b: &mut ::test::Bencher) { fn bench_fr_invert(b: &mut ::test::Bencher) {
const SAMPLES: usize = 1000; const SAMPLES: usize = 1000;
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -230,7 +230,7 @@ fn bench_fr_inverse(b: &mut ::test::Bencher) {
let mut count = 0; let mut count = 0;
b.iter(|| { b.iter(|| {
count = (count + 1) % SAMPLES; count = (count + 1) % SAMPLES;
v[count].inverse() v[count].invert()
}); });
} }

View File

@ -97,7 +97,7 @@ macro_rules! curve_impl {
/// ///
/// If and only if `greatest` is set will the lexicographically /// If and only if `greatest` is set will the lexicographically
/// largest y-coordinate be selected. /// 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 // Compute x^3 + b
let mut x3b = x.square(); let mut x3b = x.square();
x3b.mul_assign(&x); x3b.mul_assign(&x);
@ -199,8 +199,9 @@ macro_rules! curve_impl {
let x = $basefield::random(rng); let x = $basefield::random(rng);
let greatest = rng.next_u32() % 2 != 0; let greatest = rng.next_u32() % 2 != 0;
if let Some(p) = $affine::get_point_from_x(x, greatest) { let p = $affine::get_point_from_x(x, greatest);
let p = p.scale_by_cofactor(); if p.is_some().into() {
let p = p.unwrap().scale_by_cofactor();
if !p.is_zero() { if !p.is_zero() {
return p; return p;
@ -251,7 +252,7 @@ macro_rules! curve_impl {
} }
// Invert `tmp`. // 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 // Second pass: iterate backwards to compute inverses
for (g, s) in v for (g, s) in v
@ -571,7 +572,7 @@ macro_rules! curve_impl {
} }
} else { } else {
// Z is nonzero, so it must have an inverse in a field. // 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(); let mut zinv_powered = zinv.square();
// X/Z^2 // X/Z^2
@ -603,6 +604,7 @@ pub mod g1 {
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
curve_impl!( curve_impl!(
"G1", "G1",
@ -807,7 +809,12 @@ pub mod g1 {
let x = Fq::from_repr(x) let x = Fq::from_repr(x)
.map_err(|e| GroupDecodingError::CoordinateDecodingError("x coordinate", e))?; .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 { fn from_affine(affine: G1Affine) -> Self {
@ -919,7 +926,9 @@ pub mod g1 {
rhs.mul_assign(&x); rhs.mul_assign(&x);
rhs.add_assign(&G1Affine::get_coeff_b()); 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 yrepr = y.into_repr();
let negy = y.neg(); let negy = y.neg();
let negyrepr = negy.into_repr(); let negyrepr = negy.into_repr();
@ -1270,6 +1279,7 @@ pub mod g2 {
use rand_core::RngCore; use rand_core::RngCore;
use std::fmt; use std::fmt;
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
curve_impl!( curve_impl!(
"G2", "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 { fn from_affine(affine: G2Affine) -> Self {
@ -1623,7 +1638,9 @@ pub mod g2 {
rhs.mul_assign(&x); rhs.mul_assign(&x);
rhs.add_assign(&G2Affine::get_coeff_b()); 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 negy = y.neg();
let p = G2Affine { let p = G2Affine {

View File

@ -1965,8 +1965,8 @@ fn test_fq_squaring() {
} }
#[test] #[test]
fn test_fq_inverse() { fn test_fq_invert() {
assert!(Fq::zero().inverse().is_none()); assert!(bool::from(Fq::zero().invert().is_none()));
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 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 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fq::random(&mut rng); let mut a = Fq::random(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.invert().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
} }
@ -2074,8 +2074,9 @@ fn test_fq_sqrt() {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fq::random(&mut rng); let a = Fq::random(&mut rng);
if let Some(tmp) = a.sqrt() { let tmp = a.sqrt();
assert_eq!(a, tmp.square()); 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() Fq::root_of_unity()
); );
assert_eq!(Fq::root_of_unity().pow([1 << Fq::S]), Fq::one()); 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] #[test]
@ -2231,40 +2232,3 @@ fn test_fq_ordering() {
fn fq_repr_tests() { fn fq_repr_tests() {
crate::tests::repr::random_repr_tests::<Fq>(); crate::tests::repr::random_repr_tests::<Fq>();
} }
#[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());
}

View File

@ -4,10 +4,10 @@ use super::fq6::Fq6;
use ff::Field; use ff::Field;
use rand_core::RngCore; use rand_core::RngCore;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 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. /// 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 struct Fq12 {
pub c0: Fq6, pub c0: Fq6,
pub c1: Fq6, pub c1: Fq6,
@ -226,13 +226,13 @@ impl Field for Fq12 {
Fq12 { c0, c1 } Fq12 { c0, c1 }
} }
fn inverse(&self) -> Option<Self> { fn invert(&self) -> CtOption<Self> {
let mut c0s = self.c0.square(); let mut c0s = self.c0.square();
let mut c1s = self.c1.square(); let mut c1s = self.c1.square();
c1s.mul_by_nonresidue(); c1s.mul_by_nonresidue();
c0s.sub_assign(&c1s); c0s.sub_assign(&c1s);
c0s.inverse().map(|t| Fq12 { c0s.invert().map(|t| Fq12 {
c0: t.mul(&self.c0), c0: t.mul(&self.c0),
c1: t.mul(&self.c1).neg(), c1: t.mul(&self.c1).neg(),
}) })

View File

@ -3,10 +3,10 @@ use ff::{Field, SqrtField};
use rand_core::RngCore; use rand_core::RngCore;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 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. /// 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 struct Fq2 {
pub c0: Fq, pub c0: Fq,
pub c1: Fq, pub c1: Fq,
@ -228,11 +228,11 @@ impl Field for Fq2 {
} }
} }
fn inverse(&self) -> Option<Self> { fn invert(&self) -> CtOption<Self> {
let t1 = self.c1.square(); let t1 = self.c1.square();
let mut t0 = self.c0.square(); let mut t0 = self.c0.square();
t0.add_assign(&t1); t0.add_assign(&t1);
t0.inverse().map(|t| Fq2 { t0.invert().map(|t| Fq2 {
c0: self.c0.mul(&t), c0: self.c0.mul(&t),
c1: self.c1.mul(&t).neg(), c1: self.c1.mul(&t).neg(),
}) })
@ -244,15 +244,13 @@ impl Field for Fq2 {
} }
impl SqrtField for Fq2 { impl SqrtField for Fq2 {
fn legendre(&self) -> ::ff::LegendreSymbol { /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
self.norm().legendre() /// THIS WILL BE REPLACED BY THE bls12_381 CRATE, WHICH IS CONSTANT TIME!
} fn sqrt(&self) -> CtOption<Self> {
fn sqrt(&self) -> Option<Self> {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf // Algorithm 9, https://eprint.iacr.org/2012/685.pdf
if self.is_zero() { if self.is_zero() {
Some(Self::zero()) CtOption::new(Self::zero(), Choice::from(1))
} else { } else {
// a1 = self^((q - 3) / 4) // a1 = self^((q - 3) / 4)
let mut a1 = self.pow([ let mut a1 = self.pow([
@ -275,7 +273,7 @@ impl SqrtField for Fq2 {
}; };
if a0 == neg1 { if a0 == neg1 {
None CtOption::new(Self::zero(), Choice::from(0))
} else { } else {
a1.mul_assign(self); a1.mul_assign(self);
@ -298,7 +296,7 @@ impl SqrtField for Fq2 {
a1.mul_assign(&alpha); a1.mul_assign(&alpha);
} }
Some(a1) CtOption::new(a1, Choice::from(1))
} }
} }
} }
@ -497,11 +495,11 @@ fn test_fq2_mul() {
} }
#[test] #[test]
fn test_fq2_inverse() { fn test_fq2_invert() {
use super::fq::FqRepr; use super::fq::FqRepr;
use ff::PrimeField; use ff::PrimeField;
assert!(Fq2::zero().inverse().is_none()); assert!(bool::from(Fq2::zero().invert().is_none()));
let a = Fq2 { let a = Fq2 {
c0: Fq::from_repr(FqRepr([ c0: Fq::from_repr(FqRepr([
@ -523,7 +521,7 @@ fn test_fq2_inverse() {
])) ]))
.unwrap(), .unwrap(),
}; };
let a = a.inverse().unwrap(); let a = a.invert().unwrap();
assert_eq!( assert_eq!(
a, a,
Fq2 { 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)] #[cfg(test)]
use rand_core::SeedableRng; use rand_core::SeedableRng;
#[cfg(test)] #[cfg(test)]

View File

@ -3,10 +3,10 @@ use super::fq2::Fq2;
use ff::Field; use ff::Field;
use rand_core::RngCore; use rand_core::RngCore;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 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). /// 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 struct Fq6 {
pub c0: Fq2, pub c0: Fq2,
pub c1: Fq2, pub c1: Fq2,
@ -345,7 +345,7 @@ impl Field for Fq6 {
Fq6 { c0, c1, c2 } Fq6 { c0, c1, c2 }
} }
fn inverse(&self) -> Option<Self> { fn invert(&self) -> CtOption<Self> {
let mut c0 = self.c2; let mut c0 = self.c2;
c0.mul_by_nonresidue(); c0.mul_by_nonresidue();
c0.mul_assign(&self.c1); c0.mul_assign(&self.c1);
@ -378,21 +378,18 @@ impl Field for Fq6 {
tmp2.mul_assign(&c0); tmp2.mul_assign(&c0);
tmp1.add_assign(&tmp2); tmp1.add_assign(&tmp2);
match tmp1.inverse() { tmp1.invert().map(|t| {
Some(t) => { let mut tmp = Fq6 {
let mut tmp = Fq6 { c0: t,
c0: t, c1: t,
c1: t, c2: t,
c2: t, };
}; tmp.c0.mul_assign(&c0);
tmp.c0.mul_assign(&c0); tmp.c1.mul_assign(&c1);
tmp.c1.mul_assign(&c1); tmp.c2.mul_assign(&c2);
tmp.c2.mul_assign(&c2);
Some(tmp) tmp
} })
None => None,
}
} }
} }

View File

@ -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] #[test]
fn test_fr_repr_add_nocarry() { fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -724,8 +700,8 @@ fn test_fr_squaring() {
} }
#[test] #[test]
fn test_fr_inverse() { fn test_fr_invert() {
assert!(Fr::zero().inverse().is_none()); assert!(bool::from(Fr::zero().invert().is_none()));
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 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 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fr::random(&mut rng); let mut a = Fr::random(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.invert().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
} }
@ -833,8 +809,9 @@ fn test_fr_sqrt() {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fr::random(&mut rng); let a = Fr::random(&mut rng);
if let Some(tmp) = a.sqrt() { let tmp = a.sqrt();
assert_eq!(a, tmp.square()); 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() Fr::root_of_unity()
); );
assert_eq!(Fr::root_of_unity().pow([1 << Fr::S]), Fr::one()); 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] #[test]

View File

@ -26,6 +26,7 @@ use super::{Engine, PairingCurveAffine};
use ff::{BitIterator, Field, ScalarEngine}; use ff::{BitIterator, Field, ScalarEngine};
use group::CurveAffine; use group::CurveAffine;
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
// The BLS parameter x for BLS12-381 is -0xd201000000010000 // The BLS parameter x for BLS12-381 is -0xd201000000010000
const BLS_X: u64 = 0xd201000000010000; const BLS_X: u64 = 0xd201000000010000;
@ -111,61 +112,58 @@ impl Engine for Bls12 {
f f
} }
fn final_exponentiation(r: &Fq12) -> Option<Fq12> { fn final_exponentiation(r: &Fq12) -> CtOption<Fq12> {
let mut f1 = *r; let mut f1 = *r;
f1.conjugate(); f1.conjugate();
match r.inverse() { r.invert().map(|mut f2| {
Some(mut f2) => { let mut r = f1;
let mut r = f1; r.mul_assign(&f2);
r.mul_assign(&f2); f2 = r;
f2 = r; r.frobenius_map(2);
r.frobenius_map(2); r.mul_assign(&f2);
r.mul_assign(&f2);
fn exp_by_x(f: &mut Fq12, x: u64) { fn exp_by_x(f: &mut Fq12, x: u64) {
*f = f.pow(&[x]); *f = f.pow(&[x]);
if BLS_X_IS_NEGATIVE { if BLS_X_IS_NEGATIVE {
f.conjugate(); 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
})
} }
} }

View File

@ -193,7 +193,10 @@ fn test_g1_uncompressed_invalid_vectors() {
x3b.mul_assign(&x); x3b.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 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. // 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(); x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
y.into_repr().write_be(&mut o.as_mut()[48..]).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(), c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API? }); // 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. // 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.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).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.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 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()); x.add_assign(&Fq::one());
} else { } else {
x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 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.mul_assign(&x);
x3b.add_assign(&Fq::from_repr(FqRepr::from(4)).unwrap()); // TODO: perhaps expose coeff_b through API? 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. // 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(); x.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
o.as_mut()[0] |= 0b1000_0000; o.as_mut()[0] |= 0b1000_0000;
@ -556,7 +562,7 @@ fn test_g2_compressed_invalid_vectors() {
c1: Fq::from_repr(FqRepr::from(4)).unwrap(), c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API? }); // TODO: perhaps expose coeff_b through API?
if let Some(_) = x3b.sqrt() { if x3b.sqrt().is_some().into() {
x.add_assign(&Fq2::one()); x.add_assign(&Fq2::one());
} else { } else {
x.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap(); 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(), c1: Fq::from_repr(FqRepr::from(4)).unwrap(),
}); // TODO: perhaps expose coeff_b through API? }); // 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. // 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.c1.into_repr().write_be(&mut o.as_mut()[0..]).unwrap();
x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap(); x.c0.into_repr().write_be(&mut o.as_mut()[48..]).unwrap();

View File

@ -22,6 +22,7 @@ pub mod bls12_381;
use ff::{Field, PrimeField, ScalarEngine, SqrtField}; use ff::{Field, PrimeField, ScalarEngine, SqrtField};
use group::{CurveAffine, CurveProjective}; use group::{CurveAffine, CurveProjective};
use subtle::CtOption;
/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) /// An "engine" is a collection of types (fields, elliptic curve groups, etc.)
/// with well-defined relationships. In particular, the G1/G2 curve groups are /// 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. /// Perform final exponentiation of the result of a miller loop.
fn final_exponentiation(_: &Self::Fqk) -> Option<Self::Fqk>; fn final_exponentiation(_: &Self::Fqk) -> CtOption<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`. /// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk

View File

@ -1,4 +1,4 @@
use ff::{Field, LegendreSymbol, PrimeField, SqrtField}; use ff::{Field, PrimeField, SqrtField};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng; use rand_xorshift::XorShiftRng;
@ -32,7 +32,6 @@ pub fn random_sqrt_tests<F: SqrtField>() {
for _ in 0..10000 { for _ in 0..10000 {
let a = F::random(&mut rng); let a = F::random(&mut rng);
let b = a.square(); let b = a.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
let b = b.sqrt().unwrap(); let b = b.sqrt().unwrap();
let negb = b.neg(); let negb = b.neg();
@ -43,7 +42,6 @@ pub fn random_sqrt_tests<F: SqrtField>() {
let mut c = F::one(); let mut c = F::one();
for _ in 0..10000 { for _ in 0..10000 {
let mut b = c.square(); let mut b = c.square();
assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue);
b = b.sqrt().unwrap(); b = b.sqrt().unwrap();
@ -78,7 +76,7 @@ pub fn random_field_tests<F: Field>() {
assert!(z.is_zero()); assert!(z.is_zero());
} }
assert!(F::zero().inverse().is_none()); assert!(bool::from(F::zero().invert().is_none()));
// Multiplication by zero // Multiplication by zero
{ {
@ -222,11 +220,11 @@ fn random_squaring_tests<F: Field, R: RngCore>(rng: &mut R) {
} }
fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) { fn random_inversion_tests<F: Field, R: RngCore>(rng: &mut R) {
assert!(F::zero().inverse().is_none()); assert!(bool::from(F::zero().invert().is_none()));
for _ in 0..10000 { for _ in 0..10000 {
let mut a = F::random(rng); let mut a = F::random(rng);
let b = a.inverse().unwrap(); // probablistically nonzero let b = a.invert().unwrap(); // probablistically nonzero
a.mul_assign(&b); a.mul_assign(&b);
assert_eq!(a, F::one()); assert_eq!(a, F::one());

View File

@ -1,5 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{montgomery, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
@ -90,10 +91,14 @@ impl<E: JubjubEngine> Point<E, Unknown> {
y_repr.as_mut()[3] &= 0x7fffffffffffffff; y_repr.as_mut()[3] &= 0x7fffffffffffffff;
match E::Fr::from_repr(y_repr) { match E::Fr::from_repr(y_repr) {
Ok(y) => match Self::get_for_y(y, x_sign, params) { Ok(y) => {
Some(p) => Ok(p), let p = Self::get_for_y(y, x_sign, params);
None => Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve")), 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( Err(_) => Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,
"y is not in field", "y is not in field",
@ -101,7 +106,7 @@ impl<E: JubjubEngine> Point<E, Unknown> {
} }
} }
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self> { pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1) // Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
// This is defined for all valid y-coordinates, // This is defined for all valid y-coordinates,
// as dy^2 + 1 = 0 has no solution in Fr. // as dy^2 + 1 = 0 has no solution in Fr.
@ -117,33 +122,27 @@ impl<E: JubjubEngine> Point<E, Unknown> {
// tmp1 = y^2 - 1 // tmp1 = y^2 - 1
tmp1.sub_assign(&E::Fr::one()); tmp1.sub_assign(&E::Fr::one());
match tmp2.inverse() { tmp2.invert().and_then(|tmp2| {
Some(tmp2) => { // tmp1 = (y^2 - 1) / (dy^2 + 1)
// tmp1 = (y^2 - 1) / (dy^2 + 1) tmp1.mul_assign(&tmp2);
tmp1.mul_assign(&tmp2);
match tmp1.sqrt() { tmp1.sqrt().map(|mut x| {
Some(mut x) => { if x.into_repr().is_odd() != sign {
if x.into_repr().is_odd() != sign { x = x.neg();
x = x.neg();
}
let mut t = x;
t.mul_assign(&y);
Some(Point {
x,
y,
t,
z: E::Fr::one(),
_marker: PhantomData,
})
}
None => None,
} }
}
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 /// This guarantees the point is in the prime order subgroup
@ -159,8 +158,9 @@ impl<E: JubjubEngine> Point<E, Unknown> {
let y = E::Fr::random(rng); let y = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0; let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_y(y, sign, params) { let p = Self::get_for_y(y, sign, params);
return p; if bool::from(p.is_some()) {
return p.unwrap();
} }
} }
} }
@ -305,7 +305,7 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
/// Convert to affine coordinates /// Convert to affine coordinates
pub fn to_xy(&self) -> (E::Fr, E::Fr) { 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; let mut x = self.x;
x.mul_assign(&zinv); x.mul_assign(&zinv);

View File

@ -1,12 +1,11 @@
use byteorder::{ByteOrder, LittleEndian}; use byteorder::{ByteOrder, LittleEndian};
use ff::{ use ff::{
adc, mac_with_carry, sbb, BitIterator, Field, adc, mac_with_carry, sbb, BitIterator, Field, PrimeField, PrimeFieldDecodingError,
LegendreSymbol::{self, *}, PrimeFieldRepr, SqrtField,
PrimeField, PrimeFieldDecodingError, PrimeFieldRepr, SqrtField,
}; };
use rand_core::RngCore; use rand_core::RngCore;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use subtle::{Choice, ConditionallySelectable}; use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
use super::ToUniform; use super::ToUniform;
@ -258,6 +257,21 @@ impl PrimeFieldRepr for FsRepr {
#[derive(Copy, Clone, PartialEq, Eq, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Fs(FsRepr); 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 { impl ::std::fmt::Display for Fs {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "Fs({})", self.into_repr()) write!(f, "Fs({})", self.into_repr())
@ -526,9 +540,11 @@ impl Field for Fs {
ret ret
} }
fn inverse(&self) -> Option<Self> { /// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
/// THIS WILL BE REPLACED BY THE jubjub CRATE, WHICH IS CONSTANT TIME!
fn invert(&self) -> CtOption<Self> {
if self.is_zero() { if self.is_zero() {
None CtOption::new(Self::zero(), Choice::from(0))
} else { } else {
// Guajardo Kumar Paar Pelzl // Guajardo Kumar Paar Pelzl
// Efficient Software-Implementation of Finite Fields with Applications to Cryptography // Efficient Software-Implementation of Finite Fields with Applications to Cryptography
@ -574,9 +590,9 @@ impl Field for Fs {
} }
if u == one { if u == one {
Some(b) CtOption::new(b, Choice::from(1))
} else { } else {
Some(c) CtOption::new(c, Choice::from(1))
} }
} }
} }
@ -723,24 +739,7 @@ impl ToUniform for Fs {
} }
impl SqrtField for Fs { impl SqrtField for Fs {
fn legendre(&self) -> LegendreSymbol { fn sqrt(&self) -> CtOption<Self> {
// 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<Self> {
// Shank's algorithm for s mod 4 = 3 // Shank's algorithm for s mod 4 = 3
// https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2)
@ -753,13 +752,9 @@ impl SqrtField for Fs {
]); ]);
let mut a0 = a1.square(); let mut a0 = a1.square();
a0.mul_assign(self); a0.mul_assign(self);
a1.mul_assign(self);
if a0 == NEGATIVE_ONE { CtOption::new(a1, !a0.ct_eq(&NEGATIVE_ONE))
None
} else {
a1.mul_assign(self);
Some(a1)
}
} }
} }
@ -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] #[test]
fn test_fr_repr_add_nocarry() { fn test_fr_repr_add_nocarry() {
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
@ -1454,8 +1428,8 @@ fn test_fr_squaring() {
} }
#[test] #[test]
fn test_fs_inverse() { fn test_fs_invert() {
assert!(Fs::zero().inverse().is_none()); assert!(bool::from(Fs::zero().invert().is_none()));
let mut rng = XorShiftRng::from_seed([ let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 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 { for _ in 0..1000 {
// Ensure that a * a^-1 = 1 // Ensure that a * a^-1 = 1
let mut a = Fs::random(&mut rng); let mut a = Fs::random(&mut rng);
let ainv = a.inverse().unwrap(); let ainv = a.invert().unwrap();
a.mul_assign(&ainv); a.mul_assign(&ainv);
assert_eq!(a, one); assert_eq!(a, one);
} }
@ -1561,8 +1535,9 @@ fn test_fs_sqrt() {
// Ensure sqrt(a)^2 = a for random a // Ensure sqrt(a)^2 = a for random a
let a = Fs::random(&mut rng); let a = Fs::random(&mut rng);
if let Some(tmp) = a.sqrt() { let tmp = a.sqrt();
assert_eq!(a, tmp.square()); 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() Fs::root_of_unity()
); );
assert_eq!(Fs::root_of_unity().pow([1 << Fs::S]), Fs::one()); 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()));
} }

View File

@ -1,5 +1,6 @@
use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField}; use ff::{BitIterator, Field, PrimeField, PrimeFieldRepr, SqrtField};
use std::ops::{AddAssign, MulAssign, Neg, SubAssign}; use std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use subtle::CtOption;
use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown}; use super::{edwards, JubjubEngine, JubjubParams, PrimeOrder, Unknown};
@ -47,7 +48,7 @@ impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
} }
impl<E: JubjubEngine> Point<E, Unknown> { impl<E: JubjubEngine> Point<E, Unknown> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self> { pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> CtOption<Self> {
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x) // Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
let mut x2 = x.square(); let mut x2 = x.square();
@ -58,21 +59,18 @@ impl<E: JubjubEngine> Point<E, Unknown> {
x2.mul_assign(&x); x2.mul_assign(&x);
rhs.add_assign(&x2); rhs.add_assign(&x2);
match rhs.sqrt() { rhs.sqrt().map(|mut y| {
Some(mut y) => { if y.into_repr().is_odd() != sign {
if y.into_repr().is_odd() != sign { y = y.neg();
y = y.neg();
}
Some(Point {
x,
y,
infinity: false,
_marker: PhantomData,
})
} }
None => None,
} Point {
x,
y,
infinity: false,
_marker: PhantomData,
}
})
} }
/// This guarantees the point is in the prime order subgroup /// This guarantees the point is in the prime order subgroup
@ -88,8 +86,9 @@ impl<E: JubjubEngine> Point<E, Unknown> {
let x = E::Fr::random(rng); let x = E::Fr::random(rng);
let sign = rng.next_u32() % 2 != 0; let sign = rng.next_u32() % 2 != 0;
if let Some(p) = Self::get_for_x(x, sign, params) { let p = Self::get_for_x(x, sign, params);
return p; if p.is_some().into() {
return p.unwrap();
} }
} }
} }
@ -139,11 +138,11 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{ {
let mut tmp = E::Fr::one(); let mut tmp = E::Fr::one();
tmp.sub_assign(&y); tmp.sub_assign(&y);
u.mul_assign(&tmp.inverse().unwrap()) u.mul_assign(&tmp.invert().unwrap())
} }
let mut v = u; let mut v = u;
v.mul_assign(&x.inverse().unwrap()); v.mul_assign(&x.invert().unwrap());
// Scale it into the correct curve constants // Scale it into the correct curve constants
v.mul_assign(params.scale()); v.mul_assign(params.scale());
@ -226,7 +225,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
} }
{ {
let tmp = self.y.double(); 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(); let mut x3 = delta.square();
@ -272,10 +272,8 @@ impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
{ {
let mut tmp = other.x; let mut tmp = other.x;
tmp.sub_assign(&self.x); tmp.sub_assign(&self.x);
delta.mul_assign( // self.x != other.x, so this must be nonzero
&tmp.inverse() delta.mul_assign(&tmp.invert().unwrap());
.expect("self.x != other.x, so this must be nonzero"),
);
} }
let mut x3 = delta.square(); let mut x3 = delta.square();

View File

@ -1,6 +1,6 @@
use super::{edwards, montgomery, JubjubEngine, JubjubParams, PrimeOrder}; 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 std::ops::{AddAssign, MulAssign, Neg, SubAssign};
use rand_core::{RngCore, SeedableRng}; use rand_core::{RngCore, SeedableRng};
@ -234,7 +234,9 @@ fn test_get_for<E: JubjubEngine>(params: &E::Params) {
let y = E::Fr::random(rng); let y = E::Fr::random(rng);
let sign = rng.next_u32() % 2 == 1; let sign = rng.next_u32() % 2 == 1;
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) { let p = edwards::Point::<E, _>::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); assert!(p.to_xy().0.into_repr().is_odd() == sign);
p = p.negate(); p = p.negate();
assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p); assert!(edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap() == p);
@ -317,8 +319,8 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
// The twisted Edwards addition law is complete when d is nonsquare // The twisted Edwards addition law is complete when d is nonsquare
// and a is square. // and a is square.
assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue); assert!(bool::from(params.edwards_d().sqrt().is_none()));
assert!(a.legendre() == LegendreSymbol::QuadraticResidue); assert!(bool::from(a.sqrt().is_some()));
} }
{ {
@ -328,37 +330,37 @@ fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
let mut tmp = *params.edwards_d(); let mut tmp = *params.edwards_d();
// 1 / d is nonsquare // 1 / d is nonsquare
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue); assert!(bool::from(tmp.invert().unwrap().sqrt().is_none()));
// tmp = -d // tmp = -d
tmp = tmp.neg(); tmp = tmp.neg();
// -d is nonsquare // -d is nonsquare
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue); assert!(bool::from(tmp.sqrt().is_none()));
// 1 / -d is nonsquare // 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: // Check that A^2 - 4 is nonsquare:
let mut tmp = params.montgomery_a().square(); let mut tmp = params.montgomery_a().square();
tmp.sub_assign(&E::Fr::from_str("4").unwrap()); 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: // Check that A - 2 is nonsquare:
let mut tmp = params.montgomery_a().clone(); let mut tmp = params.montgomery_a().clone();
tmp.sub_assign(&E::Fr::from_str("2").unwrap()); 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 // Check the validity of the scaling factor
let mut tmp = a; let mut tmp = a;
tmp.sub_assign(&params.edwards_d()); tmp.sub_assign(&params.edwards_d());
tmp = tmp.inverse().unwrap(); tmp = tmp.invert().unwrap();
tmp.mul_assign(&E::Fr::from_str("4").unwrap()); tmp.mul_assign(&E::Fr::from_str("4").unwrap());
tmp = tmp.sqrt().unwrap(); tmp = tmp.sqrt().unwrap();
assert_eq!(&tmp, params.scale()); assert_eq!(&tmp, params.scale());

View File

@ -344,13 +344,11 @@ impl<E: JubjubEngine> EdwardsPoint<E> {
let mut t1 = E::Fr::one(); let mut t1 = E::Fr::one();
t1.add_assign(c.get_value().get()?); t1.add_assign(c.get_value().get()?);
match t1.inverse() { let res = t1.invert().map(|t1| t0 * &t1);
Some(t1) => { if bool::from(res.is_some()) {
t0.mul_assign(&t1); Ok(res.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -371,13 +369,11 @@ impl<E: JubjubEngine> EdwardsPoint<E> {
let mut t1 = E::Fr::one(); let mut t1 = E::Fr::one();
t1.sub_assign(c.get_value().get()?); t1.sub_assign(c.get_value().get()?);
match t1.inverse() { let res = t1.invert().map(|t1| t0 * &t1);
Some(t1) => { if bool::from(res.is_some()) {
t0.mul_assign(&t1); Ok(res.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -451,13 +447,11 @@ impl<E: JubjubEngine> EdwardsPoint<E> {
let mut t1 = E::Fr::one(); let mut t1 = E::Fr::one();
t1.add_assign(c.get_value().get()?); t1.add_assign(c.get_value().get()?);
match t1.inverse() { let ret = t1.invert().map(|t1| t0 * &t1);
Some(t1) => { if bool::from(ret.is_some()) {
t0.mul_assign(&t1); Ok(ret.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -478,13 +472,11 @@ impl<E: JubjubEngine> EdwardsPoint<E> {
let mut t1 = E::Fr::one(); let mut t1 = E::Fr::one();
t1.sub_assign(c.get_value().get()?); t1.sub_assign(c.get_value().get()?);
match t1.inverse() { let ret = t1.invert().map(|t1| t0 * &t1);
Some(t1) => { if bool::from(ret.is_some()) {
t0.mul_assign(&t1); Ok(ret.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -521,13 +513,11 @@ impl<E: JubjubEngine> MontgomeryPoint<E> {
let mut t0 = *self.x.get_value().get()?; let mut t0 = *self.x.get_value().get()?;
t0.mul_assign(params.scale()); t0.mul_assign(params.scale());
match self.y.get_value().get()?.inverse() { let ret = self.y.get_value().get()?.invert().map(|invy| t0 * &invy);
Some(invy) => { if bool::from(ret.is_some()) {
t0.mul_assign(&invy); Ok(ret.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -545,13 +535,11 @@ impl<E: JubjubEngine> MontgomeryPoint<E> {
t0.sub_assign(&E::Fr::one()); t0.sub_assign(&E::Fr::one());
t1.add_assign(&E::Fr::one()); t1.add_assign(&E::Fr::one());
match t1.inverse() { let ret = t1.invert().map(|t1| t0 * &t1);
Some(t1) => { if bool::from(ret.is_some()) {
t0.mul_assign(&t1); Ok(ret.unwrap())
} else {
Ok(t0) Err(SynthesisError::DivisionByZero)
}
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -593,12 +581,11 @@ impl<E: JubjubEngine> MontgomeryPoint<E> {
let mut d = *other.x.get_value().get()?; let mut d = *other.x.get_value().get()?;
d.sub_assign(self.x.get_value().get()?); d.sub_assign(self.x.get_value().get()?);
match d.inverse() { let ret = d.invert().map(|d| n * &d);
Some(d) => { if bool::from(ret.is_some()) {
n.mul_assign(&d); Ok(ret.unwrap())
Ok(n) } else {
} Err(SynthesisError::DivisionByZero)
None => Err(SynthesisError::DivisionByZero),
} }
})?; })?;
@ -1038,8 +1025,9 @@ mod test {
let x = Fr::random(rng); let x = Fr::random(rng);
let s: bool = rng.next_u32() % 2 != 0; let s: bool = rng.next_u32() % 2 != 0;
if let Some(p) = montgomery::Point::<Bls12, _>::get_for_x(x, s, params) { let p = montgomery::Point::<Bls12, _>::get_for_x(x, s, params);
break p; if p.is_some().into() {
break p.unwrap();
} }
}; };
@ -1047,8 +1035,9 @@ mod test {
let x = Fr::random(rng); let x = Fr::random(rng);
let s: bool = rng.next_u32() % 2 != 0; let s: bool = rng.next_u32() % 2 != 0;
if let Some(p) = montgomery::Point::<Bls12, _>::get_for_x(x, s, params) { let p = montgomery::Point::<Bls12, _>::get_for_x(x, s, params);
break p; if p.is_some().into() {
break p.unwrap();
} }
}; };