mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 20:11:23 +00:00
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:
@@ -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()
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
});
|
||||
|
@@ -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
|
||||
});
|
||||
|
@@ -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()
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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(),
|
||||
})
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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());
|
||||
|
Reference in New Issue
Block a user