Skip to content

Commit ed10d61

Browse files
committed
bigint: Add a modpow fallback for even modulus
1 parent 35b7187 commit ed10d61

File tree

3 files changed

+66
-15
lines changed

3 files changed

+66
-15
lines changed

benches/bigint.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -256,25 +256,39 @@ fn pow_bench(b: &mut Bencher) {
256256
});
257257
}
258258

259+
260+
/// This modulus is the prime from the 2048-bit MODP DH group:
261+
/// https://tools.ietf.org/html/rfc3526#section-3
262+
const RFC3526_2048BIT_MODP_GROUP: &'static str = "\
263+
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
264+
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
265+
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
266+
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
267+
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
268+
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
269+
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
270+
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
271+
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
272+
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
273+
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";
274+
259275
#[bench]
260276
fn modpow(b: &mut Bencher) {
261277
let mut rng = get_rng();
262278
let base = rng.gen_biguint(2048);
263279
let e = rng.gen_biguint(2048);
264-
// This modulus is the prime from the 2048-bit MODP DH group:
265-
// https://tools.ietf.org/html/rfc3526#section-3
266-
let m = BigUint::from_str_radix("\
267-
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
268-
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
269-
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
270-
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
271-
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
272-
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
273-
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
274-
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
275-
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
276-
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
277-
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF", 16).unwrap();
280+
let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap();
281+
282+
b.iter(|| base.modpow(&e, &m));
283+
}
284+
285+
#[bench]
286+
fn modpow_even(b: &mut Bencher) {
287+
let mut rng = get_rng();
288+
let base = rng.gen_biguint(2048);
289+
let e = rng.gen_biguint(2048);
290+
// Make the modulus even, so monty (base-2^32) doesn't apply.
291+
let m = BigUint::from_str_radix(RFC3526_2048BIT_MODP_GROUP, 16).unwrap() - 1u32;
278292

279293
b.iter(|| base.modpow(&e, &m));
280294
}

bigint/src/biguint.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,34 @@ impl BigUint {
16251625

16261626
/// Returns `(self ^ exponent) % modulus`.
16271627
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
1628-
monty_modpow(self, exponent, modulus)
1628+
assert!(!modulus.is_zero(), "divide by zero!");
1629+
1630+
// For an odd modulus, we can use Montgomery multiplication in base 2^32.
1631+
if modulus.is_odd() {
1632+
return monty_modpow(self, exponent, modulus);
1633+
}
1634+
1635+
// Otherwise do basically the same as `num::pow`, but with a modulus.
1636+
let one = BigUint::one();
1637+
if exponent.is_zero() { return one; }
1638+
1639+
let mut base = self % modulus;
1640+
let mut exp = exponent.clone();
1641+
while exp.is_even() {
1642+
base = &base * &base % modulus;
1643+
exp >>= 1;
1644+
}
1645+
if exp == one { return base }
1646+
1647+
let mut acc = base.clone();
1648+
while exp > one {
1649+
exp >>= 1;
1650+
base = &base * &base % modulus;
1651+
if exp.is_odd() {
1652+
acc = acc * &base % modulus;
1653+
}
1654+
}
1655+
acc
16291656
}
16301657
}
16311658

bigint/src/tests/biguint.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,11 @@ fn test_modpow() {
10981098
let big_r = BigUint::from(r);
10991099

11001100
assert_eq!(big_b.modpow(&big_e, &big_m), big_r);
1101+
1102+
let even_m = &big_m << 1;
1103+
let even_modpow = big_b.modpow(&big_e, &even_m);
1104+
assert!(even_modpow < even_m);
1105+
assert_eq!(even_modpow % big_m, big_r);
11011106
}
11021107

11031108
check(1, 0, 11, 1);
@@ -1160,6 +1165,11 @@ fn test_modpow_big() {
11601165
109c4735_6e7db425_7b5d74c7_0b709508", 16).unwrap();
11611166

11621167
assert_eq!(b.modpow(&e, &m), r);
1168+
1169+
let even_m = &m << 1;
1170+
let even_modpow = b.modpow(&e, &even_m);
1171+
assert!(even_modpow < even_m);
1172+
assert_eq!(even_modpow % m, r);
11631173
}
11641174

11651175
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {

0 commit comments

Comments
 (0)