mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-01 08:12:14 +00:00
Constant-time field inversion in ff_derive using Field::pow_vartime
This is around 2.5-3x slower than the non-constant-time inversion. We can regain some of this speed later by dynamically generating addition chains.
This commit is contained in:
parent
26ef9c9842
commit
56999d0f73
@ -51,7 +51,7 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||||||
|
|
||||||
gen.extend(constants_impl);
|
gen.extend(constants_impl);
|
||||||
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
|
gen.extend(prime_field_repr_impl(&repr_ident, limbs));
|
||||||
gen.extend(prime_field_impl(&ast.ident, &repr_ident, limbs));
|
gen.extend(prime_field_impl(&ast.ident, &repr_ident, &modulus, limbs));
|
||||||
gen.extend(sqrt_impl);
|
gen.extend(sqrt_impl);
|
||||||
|
|
||||||
// Return the generated impl
|
// Return the generated impl
|
||||||
@ -540,6 +540,7 @@ fn prime_field_constants_and_sqrt(
|
|||||||
fn prime_field_impl(
|
fn prime_field_impl(
|
||||||
name: &syn::Ident,
|
name: &syn::Ident,
|
||||||
repr: &syn::Ident,
|
repr: &syn::Ident,
|
||||||
|
modulus: &BigUint,
|
||||||
limbs: usize,
|
limbs: usize,
|
||||||
) -> proc_macro2::TokenStream {
|
) -> proc_macro2::TokenStream {
|
||||||
// Returns r{n} as an ident.
|
// Returns r{n} as an ident.
|
||||||
@ -742,8 +743,35 @@ fn prime_field_impl(
|
|||||||
gen
|
gen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an implementation of multiplicative inversion within the target prime
|
||||||
|
/// field.
|
||||||
|
fn inv_impl(
|
||||||
|
a: proc_macro2::TokenStream,
|
||||||
|
name: &syn::Ident,
|
||||||
|
modulus: &BigUint,
|
||||||
|
limbs: usize,
|
||||||
|
) -> proc_macro2::TokenStream {
|
||||||
|
let mod_minus_2 = biguint_to_u64_vec(modulus - BigUint::from(2u64), limbs);
|
||||||
|
|
||||||
|
// TODO: Improve on this by computing an addition chain for mod_minus_two
|
||||||
|
quote! {
|
||||||
|
use ::subtle::ConstantTimeEq;
|
||||||
|
|
||||||
|
// By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then:
|
||||||
|
// a^-1 ≡ a^(phi(p) - 1) mod p
|
||||||
|
//
|
||||||
|
// `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and
|
||||||
|
// thus:
|
||||||
|
// a^-1 ≡ a^(p - 2) mod p
|
||||||
|
let inv = #a.pow_vartime(#mod_minus_2);
|
||||||
|
|
||||||
|
::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let squaring_impl = sqr_impl(quote! {self}, limbs);
|
let squaring_impl = sqr_impl(quote! {self}, limbs);
|
||||||
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
|
let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs);
|
||||||
|
let invert_impl = inv_impl(quote! {self}, name, modulus, 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]) & ...
|
// (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ...
|
||||||
@ -1056,61 +1084,8 @@ fn prime_field_impl(
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
/// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
|
|
||||||
/// TODO: Make this constant-time.
|
|
||||||
fn invert(&self) -> ::subtle::CtOption<Self> {
|
fn invert(&self) -> ::subtle::CtOption<Self> {
|
||||||
if self.is_zero() {
|
#invert_impl
|
||||||
::subtle::CtOption::new(#name::zero(), ::subtle::Choice::from(0))
|
|
||||||
} else {
|
|
||||||
// Guajardo Kumar Paar Pelzl
|
|
||||||
// Efficient Software-Implementation of Finite Fields with Applications to Cryptography
|
|
||||||
// Algorithm 16 (BEA for Inversion in Fp)
|
|
||||||
|
|
||||||
let one = #repr::from(1);
|
|
||||||
|
|
||||||
let mut u = self.0;
|
|
||||||
let mut v = MODULUS;
|
|
||||||
let mut b = #name(R2); // Avoids unnecessary reduction step.
|
|
||||||
let mut c = Self::zero();
|
|
||||||
|
|
||||||
while u != one && v != one {
|
|
||||||
while u.is_even() {
|
|
||||||
u.div2();
|
|
||||||
|
|
||||||
if b.0.is_even() {
|
|
||||||
b.0.div2();
|
|
||||||
} else {
|
|
||||||
b.0.add_nocarry(&MODULUS);
|
|
||||||
b.0.div2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while v.is_even() {
|
|
||||||
v.div2();
|
|
||||||
|
|
||||||
if c.0.is_even() {
|
|
||||||
c.0.div2();
|
|
||||||
} else {
|
|
||||||
c.0.add_nocarry(&MODULUS);
|
|
||||||
c.0.div2();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if v < u {
|
|
||||||
u.sub_noborrow(&v);
|
|
||||||
b.sub_assign(&c);
|
|
||||||
} else {
|
|
||||||
v.sub_noborrow(&u);
|
|
||||||
c.sub_assign(&b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if u == one {
|
|
||||||
::subtle::CtOption::new(b, ::subtle::Choice::from(1))
|
|
||||||
} else {
|
|
||||||
::subtle::CtOption::new(c, ::subtle::Choice::from(1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
Loading…
Reference in New Issue
Block a user