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

Inversion tests #639

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
353 changes: 346 additions & 7 deletions src/modular/monty_form/inv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,310 @@ where

#[cfg(test)]
mod tests {
use hex_literal::hex;
use num_bigint::BigUint;
use num_modular::{ModularCoreOps, ModularUnaryOps};
use num_traits::{FromBytes, One};

use super::{MontyForm, MontyParams};
use crate::{Invert, Inverter, Odd, PrecomputeInverter, U256};
use crate::{Invert, Inverter, Odd, PrecomputeInverter, U2048, U256};

// Random modulus.
const MODULUS: [u8; 256] = hex!("5DC56576A9F077F2FD05CC35DD0B1060857CD5A44011891ED05D8C56359A9302FC9FB1D6B2FF411FAC318009C519FB7D883ACF327C2FC1181642B7A076C7DB244AB265D20605AA55EB04B5F5100B961A684033BD4E98A45DFD2AAD4B13625808FF3C947BC3712CE8D2A5688579F08B5B523B9C6EC3361535379620F49E94C85508A6E0D264A284E3F6B3C54447D5DB9A421D1FBE2A1F59FFF92D1D9F68985E51C316CA027B4E6D9AAEED0D9F41DF77CFF021BF8F7A2E55E1F2B80859C466686305671C615757BA9712513A92764F399B486723549976024BEFF7A9484C40F5E765904E3477E1B6849468D513C26997D2A9BD038511C98E48FAE3493EC6A7FE49");

#[cfg(feature = "rand_core")]
mod randomized_tests {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These seem somewhat similar to the existing proptests. Maybe it would be better to expand those to more sizes?

use super::*;
use crate::{Integer, NonZero, Uint, U1024, U4096, U8192};

use crate::RandomMod;
use rand_chacha::ChaChaRng;
use rand_core::{CryptoRngCore, SeedableRng};

// Seed used to ensure deterministc random sequences in tests.
const RANDOM_SEED: [u8; 32] = [17; 32];

#[cfg(target_pointer_width = "32")]
const LIMBS_1024: usize = 32;
#[cfg(target_pointer_width = "32")]
const LIMBS_2048: usize = 64;
#[cfg(target_pointer_width = "32")]
const LIMBS_4096: usize = 128;

#[cfg(target_pointer_width = "64")]
pub const LIMBS_1024: usize = 16;
#[cfg(target_pointer_width = "64")]
pub const LIMBS_2048: usize = 32;
#[cfg(target_pointer_width = "64")]
pub const LIMBS_4096: usize = 64;
// Generates a random `U1024` and returns it as a tuple in: normal form, montgomery form,
// inverted montgomery form and the normal form inverse from the num_modular crate.
fn random_invertible_u1024(
monty_params: MontyParams<LIMBS_1024>,
rng: &mut impl CryptoRngCore,
) -> (U1024, MontyForm<LIMBS_1024>, MontyForm<LIMBS_1024>, BigUint) {
let modulus = monty_params.modulus().to_nz().unwrap();
loop {
let r = Uint::random_mod(rng, &modulus);
let rm = <U1024 as Integer>::Monty::new(&r, monty_params);
let rm_inv = rm.invert();
if rm_inv.is_some().into() {
// Calculate the inverse using the num_modular crate as well
let num_modular_modulus =
BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes());
let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes())
.clone()
.invm(&num_modular_modulus)
.unwrap();

return (r, rm, rm_inv.unwrap(), num_modular_inverse);
}
}
}
// Generates a random `U2048` and returns it as a tuple in: normal form, montgomery form,
// inverted montgomery form and the normal form inverse from the num_modular crate.
fn random_invertible_u2048(
monty_params: MontyParams<LIMBS_2048>,
rng: &mut impl CryptoRngCore,
) -> (U2048, MontyForm<LIMBS_2048>, MontyForm<LIMBS_2048>, BigUint) {
let modulus = monty_params.modulus().to_nz().unwrap();
loop {
let r = Uint::random_mod(rng, &modulus);
let rm = <U2048 as Integer>::Monty::new(&r, monty_params);
let rm_inv = rm.invert();
if rm_inv.is_some().into() {
// Calculate the inverse using the num_modular crate as well
let num_modular_modulus =
BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes());
let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes())
.clone()
.invm(&num_modular_modulus)
.unwrap();

return (r, rm, rm_inv.unwrap(), num_modular_inverse);
}
}
}

// Generates a random `U4096` and returns it as a tuple in: normal form, montgomery form,
// inverted montgomery form and the normal form inverse from the num_modular crate.
fn random_invertible_u4096(
monty_params: MontyParams<LIMBS_4096>,
rng: &mut impl CryptoRngCore,
) -> (U4096, MontyForm<LIMBS_4096>, MontyForm<LIMBS_4096>, BigUint) {
let modulus = monty_params.modulus().to_nz().unwrap();
loop {
let r = Uint::random_mod(rng, &modulus);
let rm = <U4096 as Integer>::Monty::new(&r, monty_params);
let rm_inv = rm.invert();
if rm_inv.is_some().into() {
// Calculate the inverse using the num_modular crate as well
let num_modular_modulus =
BigUint::from_be_bytes(&monty_params.modulus().0.to_be_bytes());
let num_modular_inverse = BigUint::from_be_bytes(&r.to_be_bytes())
.clone()
.invm(&num_modular_modulus)
.unwrap();

return (r, rm, rm_inv.unwrap(), num_modular_inverse);
}
}
}

// Creates random U1024s, inverts and sanity checks the result.
#[test]
fn inversion_random_uints_u1024() {
let mut rng = ChaChaRng::from_seed(RANDOM_SEED);
let modulus = U1024::from_be_hex(concat!(
"1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
));
let wide_modulus = NonZero::new(Into::<U2048>::into(&modulus)).unwrap();
let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap());

// Tested up to 10_000_000
for _ in 0..100 {
let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) =
random_invertible_u1024(monty_params, &mut rng);
let one_monty = rand_uint_monty * rand_uint_monty_inv;

// Inversion works in monty form
assert_eq!(
one_monty,
MontyForm::one(monty_params),
"a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:01024b}",
modulus
);
// …and in normal form
assert_eq!(one_monty.retrieve(), U1024::ONE, "a*a⁻¹ ≠ 1 (normal form)");

// …and when converted back to normal form and used in a widening operation
let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint);
assert_eq!(
one % wide_modulus,
U2048::ONE,
"a*a⁻¹ ≠ 1 (normal form, wide)"
);

// …and agrees with normal form inversion
let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap();
assert_eq!(
normal_form_inv,
rand_uint_monty_inv.retrieve(),
"a*a⁻¹ ≠ 1 (normal form inverted)"
);

// …and agrees with the num_modular crate
assert_eq!(
BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()),
num_modular_inv,
"num_modular ≠ crypto_bigint"
)
}
}

#[test]
fn inversion_random_uints_u2048() {
let mut rng = ChaChaRng::from_seed(RANDOM_SEED);
let modulus = U2048::from_be_hex(concat!(
"1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
));
let wide_modulus = NonZero::new(Into::<U4096>::into(&modulus)).unwrap();
let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap());
for _ in 0..100 {
let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) =
random_invertible_u2048(monty_params, &mut rng);
let one_monty = rand_uint_monty * rand_uint_monty_inv;

// Inversion works in monty form
assert_eq!(
one_monty,
MontyForm::one(monty_params),
"a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}",
modulus
);
// …and in normal form
assert_eq!(one_monty.retrieve(), U2048::ONE, "a*a⁻¹ ≠ 1 (normal form)");

// …and when converted back to normal form and used in a widening operation
let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint);
assert_eq!(
one % wide_modulus,
U4096::ONE,
"a*a⁻¹ ≠ 1 (normal form, wide)"
);

// …and agrees with normal form inversion
let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap();
assert_eq!(
normal_form_inv,
rand_uint_monty_inv.retrieve(),
"a*a⁻¹ ≠ 1 (normal form inverted)"
);

// …and agrees with the num_modular crate
assert_eq!(
BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()),
num_modular_inv,
"num_modular ≠ crypto_bigint"
)
}
}

#[test]
fn inversion_random_uints_u4096() {
let mut rng = ChaChaRng::from_seed(RANDOM_SEED);
let modulus = U4096::from_be_hex(concat!(
"07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
));
let wide_modulus = NonZero::new(Into::<U8192>::into(&modulus)).unwrap();
let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap());

for _ in 0..20 {
let (rand_uint, rand_uint_monty, rand_uint_monty_inv, num_modular_inv) =
random_invertible_u4096(monty_params, &mut rng);
let one_monty = rand_uint_monty * rand_uint_monty_inv;

// Inversion works in monty form
assert_eq!(
one_monty,
MontyForm::one(monty_params),
"a*a⁻¹ ≠ 1 (monty form)\nmodulus: {:02048b}",
modulus
);
// …and in normal form
assert_eq!(one_monty.retrieve(), U4096::ONE, "a*a⁻¹ ≠ 1 (normal form)");

// …and when converted back to normal form and used in a widening operation
let one = rand_uint_monty_inv.retrieve().widening_mul(&rand_uint);
assert_eq!(
one % wide_modulus,
U8192::ONE,
"a*a⁻¹ ≠ 1 (normal form, wide)"
);

// …and agrees with normal form inversion
let normal_form_inv = rand_uint.inv_mod(&modulus).unwrap();
assert_eq!(
normal_form_inv,
rand_uint_monty_inv.retrieve(),
"a*a⁻¹ ≠ 1 (normal form inverted)"
);

// …and agrees with the num_modular crate
assert_eq!(
BigUint::from_be_bytes(&normal_form_inv.to_be_bytes()),
num_modular_inv,
"num_modular ≠ crypto_bigint"
)
}
}

// Creates random U1024s, inverts and sanity checks the result.
#[test]
fn invert_random_u1024s() {
let mut rng = ChaChaRng::from_seed(RANDOM_SEED);
let modulus = U1024::from_be_hex("d69d42ede255db9d20d23f0e7e55c2f3b11955f4354ad01ee1598344a83da132a40162a5315c9b7fa3e41cc5720f4dca3a28db743e07f87b717d2332171a2bd77f6917818473334e7fc6c2ffd071667d1bdb77d72f57ac493c857fff2fe7f7ee33ca8e3a359428ad12a442daca8af09d872ac90b36ebd1bc9aefced07fa13351");
let monty_params = MontyParams::new_vartime(modulus.to_odd().unwrap());
for _ in 0..100 {
let (r, rm, rmi, _) = random_invertible_u1024(monty_params, &mut rng);
let one_monty = rm * rmi;
assert_eq!(one_monty, MontyForm::one(monty_params));
assert_eq!(one_monty.retrieve(), U1024::ONE);

let one = rmi.retrieve().widening_mul(&r);
let wide_modulus = NonZero::new(Into::<U2048>::into(&modulus)).unwrap();
assert_eq!(one % wide_modulus, U2048::ONE);

let ri = r.inv_mod(&modulus).unwrap();
assert_eq!(ri, rmi.retrieve());
}
}
}

fn params() -> MontyParams<{ U256::LIMBS }> {
MontyParams::new_vartime(Odd::<U256>::from_be_hex(
Expand All @@ -124,10 +426,10 @@ mod tests {
let params = params();
let x =
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
let x_mod = MontyForm::new(&x, params);
let x_monty = MontyForm::new(&x, params);

let inv = x_mod.invert().unwrap();
let res = x_mod * inv;
let inv = x_monty.invert().unwrap();
let res = x_monty * inv;

assert_eq!(res.retrieve(), U256::ONE);
}
Expand All @@ -137,12 +439,49 @@ mod tests {
let params = params();
let x =
U256::from_be_hex("77117F1273373C26C700D076B3F780074D03339F56DD0EFB60E7F58441FD3685");
let x_mod = MontyForm::new(&x, params);
let x_monty = MontyForm::new(&x, params);

let inverter = params.precompute_inverter();
let inv = inverter.invert(&x_mod).unwrap();
let res = x_mod * inv;
let inv = inverter.invert(&x_monty).unwrap();
let res = x_monty * inv;

assert_eq!(res.retrieve(), U256::ONE);
}

#[test]
fn invert_u2048() {
let modulus = Odd::new(U2048::from_be_slice(&MODULUS)).unwrap();
let params = MontyParams::new_vartime(modulus);
let int = U2048::from_be_hex("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2");
let mont = MontyForm::new(&int, params);
let inverted = mont.invert();

// This is what v0.5.5 outputs using the same inputs.
let expected_inv = {
let int = U2048::from_be_hex("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E");
MontyForm::new(&int, params)
};
assert!(bool::from(inverted.is_some()));
let inverted = inverted.unwrap();
assert_eq!(inverted, expected_inv);

let one = mont * inverted;
assert_eq!(one.retrieve(), U2048::ONE);
}

/// This test use the same inputs as the test above
/// but using the `num-modular` crate. That crate lacks support for big integer Montgomery form
/// so for this test the values are in normal form.
#[test]
fn invert_u2048_with_num_modular() {
let modulus = BigUint::from_be_bytes(&MODULUS);

let int = BigUint::from_be_bytes(&hex!("408A71A0709CE2CD00E6F48D4D93E1AF2D2A788810B2F3948CC2DEC041BECA2801CDF12A70B044881BE452BA9BEB246D2E4899D7CFE351CE61F95D8DE656146DF610CE4428BFB4CE8B60D3EF8B038C031F59460BDA91F30550C826C912997B2E8849295AFDC104635A9401A5A0E0A8B052D0A3CB50E6E7671D9D68ADB4210A5502341DF41349924B1792DDDE6FA393E4462A5A8D8EE4D4096FBDB66EE4025D8B9167023A02D2661FEE84DB942F02EDF5DFE214E84AA5F4A308E11DEE9503EB0C550111C53A6580B31655F20B75C822BB44F01A2FB1C9A727790AA3BBD6AF32BCDC44365B9774CE909264A5BF2BDF79C0F1169226A1FDA309222B4017023BF6D2"));
let inverted = int.clone().invm(&modulus).expect("inverse exists");

let expected_inv = BigUint::from_bytes_be(&hex!("579F198AFBD0EC0921E8626D386C7F8080F8C0668284BE38FE5B9E67AA81A6F637FFEDBB76E9CF68E5E7BA9892D4938DB90906686CEF06A94D16AA9CDA0C0E24DAB9AB72303316266BF4DCF449E00D8F7795819C06CA5921A31B40A2AB1B0D7C264144D0372D59ADD5754FE02B1328E159B4B58767FFB623D1A6B3CC89B3F724A647A9AFCB55ACD02491544849A4603C013C5313DEC80A8AC46C268BA1245BA1B9D05386A560E1CBACE4F7C39873471101C19C6CE07D4CDDE06B1557081F5C838452135A16216285E1AC92A1F30263AC148BBE74A9397514D6B17E7473C703D965EA68054D4AA5AC9967729997A898AFA78C8D418871B30F502F3E01B89F1C3E"));
assert_eq!(inverted, expected_inv);
let one = int.mulm(inverted, &modulus);
assert_eq!(one, BigUint::one());
}
}
Loading