Merge pull request #194 from str4d/ct-invert

Constant-time field inversion in ff_derive using pow_vartime
This commit is contained in:
str4d 2019-12-19 14:33:41 -06:00 committed by GitHub
commit b5523f610e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 42 additions and 69 deletions

View File

@ -47,11 +47,11 @@ pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut gen = proc_macro2::TokenStream::new();
let (constants_impl, sqrt_impl) =
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, modulus, limbs, generator);
prime_field_constants_and_sqrt(&ast.ident, &repr_ident, &modulus, limbs, generator);
gen.extend(constants_impl);
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);
// Return the generated impl
@ -383,7 +383,7 @@ fn test_exp() {
fn prime_field_constants_and_sqrt(
name: &syn::Ident,
repr: &syn::Ident,
modulus: BigUint,
modulus: &BigUint,
limbs: usize,
generator: BigUint,
) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) {
@ -396,28 +396,26 @@ fn prime_field_constants_and_sqrt(
let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone());
// Compute R = 2**(64 * limbs) mod m
let r = (BigUint::one() << (limbs * 64)) % &modulus;
let r = (BigUint::one() << (limbs * 64)) % modulus;
// modulus - 1 = 2^s * t
let mut s: u32 = 0;
let mut t = &modulus - BigUint::from_str("1").unwrap();
let mut t = modulus - BigUint::from_str("1").unwrap();
while t.is_even() {
t = t >> 1;
s += 1;
}
// Compute 2^s root of unity given the generator
let root_of_unity = biguint_to_u64_vec(
(exp(generator.clone(), &t, &modulus) * &r) % &modulus,
limbs,
);
let generator = biguint_to_u64_vec((generator.clone() * &r) % &modulus, limbs);
let root_of_unity =
biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs);
let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs);
let sqrt_impl = if (&modulus % BigUint::from_str("4").unwrap())
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);
biguint_to_u64_vec((modulus + BigUint::from_str("1").unwrap()) >> 2, limbs);
quote! {
impl ::ff::SqrtField for #name {
@ -436,7 +434,7 @@ fn prime_field_constants_and_sqrt(
}
}
}
} else if (&modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() {
} 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! {
@ -490,10 +488,10 @@ fn prime_field_constants_and_sqrt(
};
// 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);
let r = biguint_to_u64_vec(r, limbs);
let modulus = biguint_to_real_u64_vec(modulus, limbs);
let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs);
// Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1
let mut inv = 1u64;
@ -542,6 +540,7 @@ fn prime_field_constants_and_sqrt(
fn prime_field_impl(
name: &syn::Ident,
repr: &syn::Ident,
modulus: &BigUint,
limbs: usize,
) -> proc_macro2::TokenStream {
// Returns r{n} as an ident.
@ -744,8 +743,35 @@ fn prime_field_impl(
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 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);
// (self.0).0[0].ct_eq(&(other.0).0[0]) & (self.0).0[1].ct_eq(&(other.0).0[1]) & ...
@ -1058,61 +1084,8 @@ fn prime_field_impl(
ret
}
/// WARNING: THIS IS NOT ACTUALLY CONSTANT TIME YET!
/// TODO: Make this constant-time.
fn invert(&self) -> ::subtle::CtOption<Self> {
if self.is_zero() {
::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))
}
}
#invert_impl
}
#[inline(always)]