Sfoglia il codice sorgente

bigint: Add a modpow fallback for even modulus

Josh Stone 7 anni fa
parent
commit
ed10d617b5
3 ha cambiato i file con 66 aggiunte e 15 eliminazioni
  1. 28 14
      benches/bigint.rs
  2. 28 1
      bigint/src/biguint.rs
  3. 10 0
      bigint/src/tests/biguint.rs

+ 28 - 14
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));
 }

+ 28 - 1
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
     }
 }
 

+ 10 - 0
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)>)> {