Skip to content

Commit f4cc2ab

Browse files
committed
Add BigInt::modpow
This performs modular exponentiation on signed `BigInt`. The exponent must be positive, and the modulus must be non-zero. The implementation leverages `BigUint::modpow`, fixing the signs as needed afterward.
1 parent 16fa6c9 commit f4cc2ab

File tree

4 files changed

+179
-83
lines changed

4 files changed

+179
-83
lines changed

src/bigint.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,33 @@ impl BigInt {
17351735
}
17361736
return Some(self.div(v));
17371737
}
1738+
1739+
/// Returns `(self ^ exponent) mod modulus`
1740+
///
1741+
/// Note that this rounds like `mod_floor`, not like the `%` operator,
1742+
/// which makes a difference when given a negative `self` or `modulus`.
1743+
/// The result will be in the interval `[0, modulus)` for `modulus > 0`,
1744+
/// or in the interval `(modulus, 0]` for `modulus < 0`
1745+
///
1746+
/// Panics if the exponent is negative or the modulus is zero.
1747+
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
1748+
assert!(!exponent.is_negative(), "negative exponentiation is not supported!");
1749+
assert!(!modulus.is_zero(), "divide by zero!");
1750+
1751+
let result = self.data.modpow(&exponent.data, &modulus.data);
1752+
if result.is_zero() {
1753+
return BigInt::zero();
1754+
}
1755+
1756+
// The sign of the result follows the modulus, like `mod_floor`.
1757+
let (sign, mag) = match (self.is_negative(), modulus.is_negative()) {
1758+
(false, false) => (Plus, result),
1759+
(true, false) => (Plus, &modulus.data - result),
1760+
(false, true) => (Minus, &modulus.data - result),
1761+
(true, true) => (Minus, result),
1762+
};
1763+
BigInt::from_biguint(sign, mag)
1764+
}
17381765
}
17391766

17401767
/// Perform in-place two's complement of the given binary representation,

src/biguint.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,8 @@ impl BigUint {
16551655
}
16561656

16571657
/// Returns `(self ^ exponent) % modulus`.
1658+
///
1659+
/// Panics if the modulus is zero.
16581660
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
16591661
assert!(!modulus.is_zero(), "divide by zero!");
16601662

src/tests/biguint.rs

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,89 +1089,6 @@ fn test_is_even() {
10891089
assert!(((&one << 64) + one).is_odd());
10901090
}
10911091

1092-
#[test]
1093-
fn test_modpow() {
1094-
fn check(b: usize, e: usize, m: usize, r: usize) {
1095-
let big_b = BigUint::from(b);
1096-
let big_e = BigUint::from(e);
1097-
let big_m = BigUint::from(m);
1098-
let big_r = BigUint::from(r);
1099-
1100-
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);
1106-
}
1107-
1108-
check(1, 0, 11, 1);
1109-
check(0, 15, 11, 0);
1110-
check(3, 7, 11, 9);
1111-
check(5, 117, 19, 1);
1112-
}
1113-
1114-
#[test]
1115-
fn test_modpow_big() {
1116-
let b = BigUint::from_str_radix("\
1117-
efac3c0a_0de55551_fee0bfe4_67fa017a_1a898fa1_6ca57cb1\
1118-
ca9e3248_cacc09a9_b99d6abc_38418d0f_82ae4238_d9a68832\
1119-
aadec7c1_ac5fed48_7a56a71b_67ac59d5_afb28022_20d9592d\
1120-
247c4efc_abbd9b75_586088ee_1dc00dc4_232a8e15_6e8191dd\
1121-
675b6ae0_c80f5164_752940bc_284b7cee_885c1e10_e495345b\
1122-
8fbe9cfd_e5233fe1_19459d0b_d64be53c_27de5a02_a829976b\
1123-
33096862_82dad291_bd38b6a9_be396646_ddaf8039_a2573c39\
1124-
1b14e8bc_2cb53e48_298c047e_d9879e9c_5a521076_f0e27df3\
1125-
990e1659_d3d8205b_6443ebc0_9918ebee_6764f668_9f2b2be3\
1126-
b59cbc76_d76d0dfc_d737c3ec_0ccf9c00_ad0554bf_17e776ad\
1127-
b4edf9cc_6ce540be_76229093_5c53893b", 16).unwrap();
1128-
let e = BigUint::from_str_radix("\
1129-
be0e6ea6_08746133_e0fbc1bf_82dba91e_e2b56231_a81888d2\
1130-
a833a1fc_f7ff002a_3c486a13_4f420bf3_a5435be9_1a5c8391\
1131-
774d6e6c_085d8357_b0c97d4d_2bb33f7c_34c68059_f78d2541\
1132-
eacc8832_426f1816_d3be001e_b69f9242_51c7708e_e10efe98\
1133-
449c9a4a_b55a0f23_9d797410_515da00d_3ea07970_4478a2ca\
1134-
c3d5043c_bd9be1b4_6dce479d_4302d344_84a939e6_0ab5ada7\
1135-
12ae34b2_30cc473c_9f8ee69d_2cac5970_29f5bf18_bc8203e4\
1136-
f3e895a2_13c94f1e_24c73d77_e517e801_53661fdd_a2ce9e47\
1137-
a73dd7f8_2f2adb1e_3f136bf7_8ae5f3b8_08730de1_a4eff678\
1138-
e77a06d0_19a522eb_cbefba2a_9caf7736_b157c5c6_2d192591\
1139-
17946850_2ddb1822_117b68a0_32f7db88", 16).unwrap();
1140-
// This modulus is the prime from the 2048-bit MODP DH group:
1141-
// https://tools.ietf.org/html/rfc3526#section-3
1142-
let m = BigUint::from_str_radix("\
1143-
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
1144-
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
1145-
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
1146-
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
1147-
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
1148-
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
1149-
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
1150-
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
1151-
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
1152-
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
1153-
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF", 16).unwrap();
1154-
let r = BigUint::from_str_radix("\
1155-
a1468311_6e56edc9_7a98228b_5e924776_0dd7836e_caabac13\
1156-
eda5373b_4752aa65_a1454850_40dc770e_30aa8675_6be7d3a8\
1157-
9d3085e4_da5155cf_b451ef62_54d0da61_cf2b2c87_f495e096\
1158-
055309f7_77802bbb_37271ba8_1313f1b5_075c75d1_024b6c77\
1159-
fdb56f17_b05bce61_e527ebfd_2ee86860_e9907066_edd526e7\
1160-
93d289bf_6726b293_41b0de24_eff82424_8dfd374b_4ec59542\
1161-
35ced2b2_6b195c90_10042ffb_8f58ce21_bc10ec42_64fda779\
1162-
d352d234_3d4eaea6_a86111ad_a37e9555_43ca78ce_2885bed7\
1163-
5a30d182_f1cf6834_dc5b6e27_1a41ac34_a2e91e11_33363ff0\
1164-
f88a7b04_900227c9_f6e6d06b_7856b4bb_4e354d61_060db6c8\
1165-
109c4735_6e7db425_7b5d74c7_0b709508", 16).unwrap();
1166-
1167-
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);
1173-
}
1174-
11751092
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {
11761093
let bits = big_digit::BITS;
11771094
vec![(Zero::zero(),

tests/modpow.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
extern crate num_bigint;
2+
extern crate num_integer;
3+
extern crate num_traits;
4+
5+
static BIG_B: &'static str = "\
6+
efac3c0a_0de55551_fee0bfe4_67fa017a_1a898fa1_6ca57cb1\
7+
ca9e3248_cacc09a9_b99d6abc_38418d0f_82ae4238_d9a68832\
8+
aadec7c1_ac5fed48_7a56a71b_67ac59d5_afb28022_20d9592d\
9+
247c4efc_abbd9b75_586088ee_1dc00dc4_232a8e15_6e8191dd\
10+
675b6ae0_c80f5164_752940bc_284b7cee_885c1e10_e495345b\
11+
8fbe9cfd_e5233fe1_19459d0b_d64be53c_27de5a02_a829976b\
12+
33096862_82dad291_bd38b6a9_be396646_ddaf8039_a2573c39\
13+
1b14e8bc_2cb53e48_298c047e_d9879e9c_5a521076_f0e27df3\
14+
990e1659_d3d8205b_6443ebc0_9918ebee_6764f668_9f2b2be3\
15+
b59cbc76_d76d0dfc_d737c3ec_0ccf9c00_ad0554bf_17e776ad\
16+
b4edf9cc_6ce540be_76229093_5c53893b";
17+
18+
static BIG_E: &'static str = "\
19+
be0e6ea6_08746133_e0fbc1bf_82dba91e_e2b56231_a81888d2\
20+
a833a1fc_f7ff002a_3c486a13_4f420bf3_a5435be9_1a5c8391\
21+
774d6e6c_085d8357_b0c97d4d_2bb33f7c_34c68059_f78d2541\
22+
eacc8832_426f1816_d3be001e_b69f9242_51c7708e_e10efe98\
23+
449c9a4a_b55a0f23_9d797410_515da00d_3ea07970_4478a2ca\
24+
c3d5043c_bd9be1b4_6dce479d_4302d344_84a939e6_0ab5ada7\
25+
12ae34b2_30cc473c_9f8ee69d_2cac5970_29f5bf18_bc8203e4\
26+
f3e895a2_13c94f1e_24c73d77_e517e801_53661fdd_a2ce9e47\
27+
a73dd7f8_2f2adb1e_3f136bf7_8ae5f3b8_08730de1_a4eff678\
28+
e77a06d0_19a522eb_cbefba2a_9caf7736_b157c5c6_2d192591\
29+
17946850_2ddb1822_117b68a0_32f7db88";
30+
31+
// This modulus is the prime from the 2048-bit MODP DH group:
32+
// https://tools.ietf.org/html/rfc3526#section-3
33+
static BIG_M: &'static str = "\
34+
FFFFFFFF_FFFFFFFF_C90FDAA2_2168C234_C4C6628B_80DC1CD1\
35+
29024E08_8A67CC74_020BBEA6_3B139B22_514A0879_8E3404DD\
36+
EF9519B3_CD3A431B_302B0A6D_F25F1437_4FE1356D_6D51C245\
37+
E485B576_625E7EC6_F44C42E9_A637ED6B_0BFF5CB6_F406B7ED\
38+
EE386BFB_5A899FA5_AE9F2411_7C4B1FE6_49286651_ECE45B3D\
39+
C2007CB8_A163BF05_98DA4836_1C55D39A_69163FA8_FD24CF5F\
40+
83655D23_DCA3AD96_1C62F356_208552BB_9ED52907_7096966D\
41+
670C354E_4ABC9804_F1746C08_CA18217C_32905E46_2E36CE3B\
42+
E39E772C_180E8603_9B2783A2_EC07A28F_B5C55DF0_6F4C52C9\
43+
DE2BCBF6_95581718_3995497C_EA956AE5_15D22618_98FA0510\
44+
15728E5A_8AACAA68_FFFFFFFF_FFFFFFFF";
45+
46+
static BIG_R: &'static str = "\
47+
a1468311_6e56edc9_7a98228b_5e924776_0dd7836e_caabac13\
48+
eda5373b_4752aa65_a1454850_40dc770e_30aa8675_6be7d3a8\
49+
9d3085e4_da5155cf_b451ef62_54d0da61_cf2b2c87_f495e096\
50+
055309f7_77802bbb_37271ba8_1313f1b5_075c75d1_024b6c77\
51+
fdb56f17_b05bce61_e527ebfd_2ee86860_e9907066_edd526e7\
52+
93d289bf_6726b293_41b0de24_eff82424_8dfd374b_4ec59542\
53+
35ced2b2_6b195c90_10042ffb_8f58ce21_bc10ec42_64fda779\
54+
d352d234_3d4eaea6_a86111ad_a37e9555_43ca78ce_2885bed7\
55+
5a30d182_f1cf6834_dc5b6e27_1a41ac34_a2e91e11_33363ff0\
56+
f88a7b04_900227c9_f6e6d06b_7856b4bb_4e354d61_060db6c8\
57+
109c4735_6e7db425_7b5d74c7_0b709508";
58+
59+
mod biguint {
60+
use num_bigint::BigUint;
61+
use num_integer::Integer;
62+
use num_traits::Num;
63+
64+
fn check_modpow<T: Into<BigUint>>(b: T, e: T, m: T, r: T) {
65+
let b: BigUint = b.into();
66+
let e: BigUint = e.into();
67+
let m: BigUint = m.into();
68+
let r: BigUint = r.into();
69+
70+
assert_eq!(b.modpow(&e, &m), r);
71+
72+
let even_m = &m << 1;
73+
let even_modpow = b.modpow(&e, &even_m);
74+
assert!(even_modpow < even_m);
75+
assert_eq!(even_modpow.mod_floor(&m), r);
76+
}
77+
78+
#[test]
79+
fn test_modpow() {
80+
check_modpow::<u32>(1, 0, 11, 1);
81+
check_modpow::<u32>(0, 15, 11, 0);
82+
check_modpow::<u32>(3, 7, 11, 9);
83+
check_modpow::<u32>(5, 117, 19, 1);
84+
}
85+
86+
#[test]
87+
fn test_modpow_big() {
88+
let b = BigUint::from_str_radix(super::BIG_B, 16).unwrap();
89+
let e = BigUint::from_str_radix(super::BIG_E, 16).unwrap();
90+
let m = BigUint::from_str_radix(super::BIG_M, 16).unwrap();
91+
let r = BigUint::from_str_radix(super::BIG_R, 16).unwrap();
92+
93+
assert_eq!(b.modpow(&e, &m), r);
94+
95+
let even_m = &m << 1;
96+
let even_modpow = b.modpow(&e, &even_m);
97+
assert!(even_modpow < even_m);
98+
assert_eq!(even_modpow % m, r);
99+
}
100+
}
101+
102+
mod bigint {
103+
use num_bigint::BigInt;
104+
use num_integer::Integer;
105+
use num_traits::{Num, Zero, One, Signed};
106+
107+
fn check_modpow<T: Into<BigInt>>(b: T, e: T, m: T, r: T) {
108+
fn check(b: &BigInt, e: &BigInt, m: &BigInt, r: &BigInt) {
109+
assert_eq!(&b.modpow(e, m), r);
110+
111+
let even_m = m << 1;
112+
let even_modpow = b.modpow(e, m);
113+
assert!(even_modpow.abs() < even_m.abs());
114+
assert_eq!(&even_modpow.mod_floor(&m), r);
115+
116+
// the sign of the result follows the modulus like `mod_floor`, not `rem`
117+
assert_eq!(b.modpow(&BigInt::one(), m), b.mod_floor(m));
118+
}
119+
120+
let b: BigInt = b.into();
121+
let e: BigInt = e.into();
122+
let m: BigInt = m.into();
123+
let r: BigInt = r.into();
124+
125+
let neg_r = if r.is_zero() { BigInt::zero() } else { &m - &r };
126+
127+
check(&b, &e, &m, &r);
128+
check(&-&b, &e, &m, &neg_r);
129+
check(&b, &e, &-&m, &-neg_r);
130+
check(&-b, &e, &-m, &-r);
131+
}
132+
133+
#[test]
134+
fn test_modpow() {
135+
check_modpow(1, 0, 11, 1);
136+
check_modpow(0, 15, 11, 0);
137+
check_modpow(3, 7, 11, 9);
138+
check_modpow(5, 117, 19, 1);
139+
}
140+
141+
#[test]
142+
fn test_modpow_big() {
143+
let b = BigInt::from_str_radix(super::BIG_B, 16).unwrap();
144+
let e = BigInt::from_str_radix(super::BIG_E, 16).unwrap();
145+
let m = BigInt::from_str_radix(super::BIG_M, 16).unwrap();
146+
let r = BigInt::from_str_radix(super::BIG_R, 16).unwrap();
147+
148+
check_modpow(b, e, m, r);
149+
}
150+
}

0 commit comments

Comments
 (0)