Constant-time field inversion

WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!

The jubjub and bls12_381 crates will replace our constant-time usages,
but we NEED to fix ff_derive because other users will expect it to
implement the Field trait correctly.
This commit is contained in:
Jack Grigg
2019-05-14 14:18:37 +01:00
parent e85a9f309f
commit 40749da9a7
25 changed files with 243 additions and 221 deletions

View File

@@ -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()
});
}

View File

@@ -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
});

View File

@@ -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
});

View File

@@ -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()
});
}

View File

@@ -251,7 +251,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 +571,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

View File

@@ -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);
}

View File

@@ -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<Self> {
fn invert(&self) -> CtOption<Self> {
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(),
})

View File

@@ -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<Self> {
fn invert(&self) -> CtOption<Self> {
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(),
})
@@ -497,11 +497,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 +523,7 @@ fn test_fq2_inverse() {
]))
.unwrap(),
};
let a = a.inverse().unwrap();
let a = a.invert().unwrap();
assert_eq!(
a,
Fq2 {

View File

@@ -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<Self> {
fn invert(&self) -> CtOption<Self> {
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);
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);
Some(tmp)
}
None => None,
}
tmp
})
}
}

View File

@@ -724,8 +724,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 +737,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);
}

View File

@@ -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<Fq12> {
fn final_exponentiation(r: &Fq12) -> CtOption<Fq12> {
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);
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();
}
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
})
}
}

View File

@@ -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<Self::Fqk>;
fn final_exponentiation(_: &Self::Fqk) -> CtOption<Self::Fqk>;
/// Performs a complete pairing operation `(p, q)`.
fn pairing<G1, G2>(p: G1, q: G2) -> Self::Fqk

View File

@@ -78,7 +78,7 @@ pub fn random_field_tests<F: Field>() {
assert!(z.is_zero());
}
assert!(F::zero().inverse().is_none());
assert!(bool::from(F::zero().invert().is_none()));
// Multiplication by zero
{
@@ -222,11 +222,11 @@ fn random_squaring_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 {
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());