From ed10d617b5ce5a25c2ccb553ed2ab1278d02530d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Sun, 22 Oct 2017 16:44:05 -0700 Subject: [PATCH] bigint: Add a modpow fallback for even modulus --- benches/bigint.rs | 42 ++++++++++++++++++++++++------------- bigint/src/biguint.rs | 29 ++++++++++++++++++++++++- bigint/src/tests/biguint.rs | 10 +++++++++ 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/benches/bigint.rs b/benches/bigint.rs index 9e18282..23932e6 100644 --- a/benches/bigint.rs +++ b/benches/bigint.rs @@ -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)); } diff --git a/bigint/src/biguint.rs b/bigint/src/biguint.rs index 0bfc158..3e78a43 100644 --- a/bigint/src/biguint.rs +++ b/bigint/src/biguint.rs @@ -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 } } diff --git a/bigint/src/tests/biguint.rs b/bigint/src/tests/biguint.rs index f66564d..e27cc17 100644 --- a/bigint/src/tests/biguint.rs +++ b/bigint/src/tests/biguint.rs @@ -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)>)> {