bigint: Add a modpow fallback for even modulus

This commit is contained in:
Josh Stone 2017-10-22 16:44:05 -07:00
parent 35b7187e83
commit ed10d617b5
3 changed files with 66 additions and 15 deletions

View File

@ -256,25 +256,39 @@ fn pow_bench(b: &mut Bencher) {
});
}
/// This modulus is the prime from the 2048-bit MODP DH group:
/// https://tools.ietf.org/html/rfc3526#section-3
const RFC3526_2048BIT_MODP_GROUP: &'static str = "\
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";
#[bench]
fn modpow(b: &mut Bencher) {
let mut rng = get_rng();
let base = rng.gen_biguint(2048);
let e = rng.gen_biguint(2048);
// This modulus is the prime from the 2048-bit MODP DH group:
// https://tools.ietf.org/html/rfc3526#section-3
let m = BigUint::from_str_radix("\
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF", 16).unwrap();
let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();
b.iter(|| base.modpow(&e, &m));
}
#[bench]
fn modpow_even(b: &mut Bencher) {
let mut rng = get_rng();
let base = rng.gen_biguint(2048);
let e = rng.gen_biguint(2048);
// Make the modulus even, so monty (base-2^32) doesn't apply.
let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;
b.iter(|| base.modpow(&e, &m));
}

View File

@ -1625,7 +1625,34 @@ impl BigUint {
/// Returns `(self ^ exponent) % modulus`.
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
monty_modpow(self, exponent, modulus)
assert!(!modulus.is_zero(), "divide by zero!");
// For an odd modulus, we can use Montgomery multiplication in base 2^32.
if modulus.is_odd() {
return monty_modpow(self, exponent, modulus);
}
// Otherwise do basically the same as `num::pow`, but with a modulus.
let one = BigUint::one();
if exponent.is_zero() { return one; }
let mut base = self % modulus;
let mut exp = exponent.clone();
while exp.is_even() {
base = &base * &base % modulus;
exp >>= 1;
}
if exp == one { return base }
let mut acc = base.clone();
while exp > one {
exp >>= 1;
base = &base * &base % modulus;
if exp.is_odd() {
acc = acc * &base % modulus;
}
}
acc
}
}

View File

@ -1098,6 +1098,11 @@ fn test_modpow() {
let big_r = BigUint::from(r);
assert_eq!(big_b.modpow(&big_e, &big_m), big_r);
let even_m = &big_m << 1;
let even_modpow = big_b.modpow(&big_e, &even_m);
assert!(even_modpow < even_m);
assert_eq!(even_modpow % big_m, big_r);
}
check(1, 0, 11, 1);
@ -1160,6 +1165,11 @@ fn test_modpow_big() {
109c4735_6e7db425_7b5d74c7_0b709508", 16).unwrap();
assert_eq!(b.modpow(&e, &m), r);
let even_m = &m << 1;
let even_modpow = b.modpow(&e, &even_m);
assert!(even_modpow < even_m);
assert_eq!(even_modpow % m, r);
}
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {