Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce valid modulus for DynResidueParams #240

Merged
merged 8 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ fn bench_division<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
});
}

#[allow(deprecated)]
AaronFeickert marked this conversation as resolved.
Show resolved Hide resolved
fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
let params = DynResidueParams::new(&(U256::random(&mut OsRng) | U256::ONE));
group.bench_function("multiplication, U256*U256", |b| {
Expand Down Expand Up @@ -103,6 +104,7 @@ fn bench_montgomery_ops<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
});
}

#[allow(deprecated)]
fn bench_montgomery_conversion<M: Measurement>(group: &mut BenchmarkGroup<'_, M>) {
group.bench_function("DynResidueParams creation", |b| {
b.iter_batched(
Expand Down
91 changes: 73 additions & 18 deletions src/uint/modular/runtime_mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ mod runtime_pow;
/// Subtractions between residues with a modulus set at runtime
mod runtime_sub;

/// The parameters to efficiently go to and from the Montgomery form for a modulus provided at runtime.
/// The parameters to efficiently go to and from the Montgomery form for an odd modulus provided at runtime.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DynResidueParams<const LIMBS: usize> {
// The constant modulus
Expand All @@ -37,25 +37,48 @@ pub struct DynResidueParams<const LIMBS: usize> {
}

impl<const LIMBS: usize> DynResidueParams<LIMBS> {
/// Instantiates a new set of `ResidueParams` representing the given `modulus`.
/// Instantiates a new set of `ResidueParams` representing the given `modulus`, which _must_ be odd.
/// If `modulus` is not odd, this function will panic; use [`new_checked`][`DynResidueParams::new_checked`] if you want to be able to detect an invalid modulus.
#[deprecated(
AaronFeickert marked this conversation as resolved.
Show resolved Hide resolved
since = "0.5.3",
note = "This will return an `Option` in a future version to account for an invalid modulus, but for now will panic if this happens. Consider using `new_checked` until then."
)]
AaronFeickert marked this conversation as resolved.
Show resolved Hide resolved
pub const fn new(modulus: &Uint<LIMBS>) -> Self {
let r = Uint::MAX.const_rem(modulus).0.wrapping_add(&Uint::ONE);
let r2 = Uint::const_rem_wide(r.square_wide(), modulus).0;

// Since we are calculating the inverse modulo (Word::MAX+1),
// we can take the modulo right away and calculate the inverse of the first limb only.
let modulus_lo = Uint::<1>::from_words([modulus.limbs[0].0]);
let mod_neg_inv =
Limb(Word::MIN.wrapping_sub(modulus_lo.inv_mod2k(Word::BITS as usize).limbs[0].0));

let r3 = montgomery_reduction(&r2.square_wide(), modulus, mod_neg_inv);
match Self::new_checked(modulus) {
Some(params) => params,
None => panic!("modulus must be odd"),
}
}

Self {
modulus: *modulus,
r,
r2,
r3,
mod_neg_inv,
/// Instantiates a new set of `ResidueParams` representing the given `modulus` if it is odd.
/// Returns an `Option` that is `None` if the provided modulus is not odd; this is a safer version of [`new`][`DynResidueParams::new`], which can panic.
pub const fn new_checked(modulus: &Uint<LIMBS>) -> Option<Self> {
AaronFeickert marked this conversation as resolved.
Show resolved Hide resolved
match modulus.ct_is_odd().to_u8() {
AaronFeickert marked this conversation as resolved.
Show resolved Hide resolved
// The modulus is odd, which is valid
1 => {
let r = Uint::MAX.const_rem(modulus).0.wrapping_add(&Uint::ONE);
let r2 = Uint::const_rem_wide(r.square_wide(), modulus).0;

// Since we are calculating the inverse modulo (Word::MAX+1),
// we can take the modulo right away and calculate the inverse of the first limb only.
let modulus_lo = Uint::<1>::from_words([modulus.limbs[0].0]);
let mod_neg_inv = Limb(
Word::MIN.wrapping_sub(modulus_lo.inv_mod2k(Word::BITS as usize).limbs[0].0),
);

let r3 = montgomery_reduction(&r2.square_wide(), modulus, mod_neg_inv);

Some(Self {
modulus: *modulus,
r,
r2,
r3,
mod_neg_inv,
})
}

// The modulus is even, which is invalid
_ => None,
}
}

Expand Down Expand Up @@ -194,3 +217,35 @@ impl<const LIMBS: usize> zeroize::Zeroize for DynResidue<LIMBS> {
self.montgomery_form.zeroize()
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::nlimbs;

const LIMBS: usize = nlimbs!(64);

#[test]
#[allow(deprecated)]
// Test that a valid modulus yields `DynResidueParams`
fn test_valid_modulus() {
let valid_modulus = Uint::<LIMBS>::from(3u8);

assert!(DynResidueParams::<LIMBS>::new_checked(&valid_modulus).is_some());
DynResidueParams::<LIMBS>::new(&valid_modulus);
}

#[test]
// Test that an invalid checked modulus does not yield `DynResidueParams`
fn test_invalid_checked_modulus() {
assert!(DynResidueParams::<LIMBS>::new_checked(&Uint::from(2u8)).is_none());
}

#[test]
#[allow(deprecated)]
#[should_panic]
// Tets that an invalid modulus panics
fn test_invalid_modulus() {
DynResidueParams::<LIMBS>::new(&Uint::from(2u8));
}
}
1 change: 1 addition & 0 deletions src/uint/modular/runtime_mod/runtime_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ mod tests {
};

#[test]
#[allow(deprecated)]
fn add_overflow() {
let params = DynResidueParams::new(&U256::from_be_hex(
"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
Expand Down
1 change: 1 addition & 0 deletions src/uint/modular/runtime_mod/runtime_sub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ mod tests {
};

#[test]
#[allow(deprecated)]
fn sub_overflow() {
let params = DynResidueParams::new(&U256::from_be_hex(
"ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551",
Expand Down
3 changes: 3 additions & 0 deletions tests/proptests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ proptest! {
}

#[test]
#[allow(deprecated)]
fn residue_pow(a in uint_mod_p(P), b in uint()) {
let a_bi = to_biguint(&a);
let b_bi = to_biguint(&b);
Expand All @@ -266,6 +267,7 @@ proptest! {
}

#[test]
#[allow(deprecated)]
fn residue_pow_bounded_exp(a in uint_mod_p(P), b in uint(), exponent_bits in any::<u8>()) {

let b_masked = b & (U256::ONE << exponent_bits.into()).wrapping_sub(&U256::ONE);
Expand All @@ -284,6 +286,7 @@ proptest! {
}

#[test]
#[allow(deprecated)]
fn residue_div_by_2(a in uint_mod_p(P)) {
let a_bi = to_biguint(&a);
let p_bi = to_biguint(&P);
Expand Down