Skip to content

Commit a203e9f

Browse files
committed
Merge #339
339: Implement modpow() for BigUint backed by Montgomery Multiplication r=cuviper a=str4d Based on this Gist: https://gist.github.com/yshui/027eecdf95248ea69606 Also adds support to `BigUint.from_str_radix()` for using `_` as a visual separator. Closes #136
2 parents fc39e1b + ed10d61 commit a203e9f

File tree

5 files changed

+298
-2
lines changed

5 files changed

+298
-2
lines changed

benches/bigint.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ extern crate rand;
66

77
use std::mem::replace;
88
use test::Bencher;
9-
use num::{BigInt, BigUint, Zero, One, FromPrimitive};
9+
use num::{BigInt, BigUint, Zero, One, FromPrimitive, Num};
1010
use num::bigint::RandBigInt;
1111
use rand::{SeedableRng, StdRng};
1212

@@ -255,3 +255,40 @@ fn pow_bench(b: &mut Bencher) {
255255
}
256256
});
257257
}
258+
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+
275+
#[bench]
276+
fn modpow(b: &mut Bencher) {
277+
let mut rng = get_rng();
278+
let base = rng.gen_biguint(2048);
279+
let e = rng.gen_biguint(2048);
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;
292+
293+
b.iter(|| base.modpow(&e, &m));
294+
}

bigint/src/algorithms.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ pub fn sub_sign(a: &[BigDigit], b: &[BigDigit]) -> (Sign, BigUint) {
220220

221221
/// Three argument multiply accumulate:
222222
/// acc += b * c
223-
fn mac_digit(acc: &mut [BigDigit], b: &[BigDigit], c: BigDigit) {
223+
pub fn mac_digit(acc: &mut [BigDigit], b: &[BigDigit], c: BigDigit) {
224224
if c == 0 {
225225
return;
226226
}

bigint/src/biguint.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,16 @@ use traits::{ToPrimitive, FromPrimitive, Float, Num, Unsigned, CheckedAdd, Check
2121

2222
#[path = "algorithms.rs"]
2323
mod algorithms;
24+
#[path = "monty.rs"]
25+
mod monty;
2426
pub use self::algorithms::big_digit;
2527
pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};
2628

2729
use self::algorithms::{mac_with_carry, mul3, scalar_mul, div_rem, div_rem_digit};
2830
use self::algorithms::{__add2, add2, sub2, sub2rev};
2931
use self::algorithms::{biguint_shl, biguint_shr};
3032
use self::algorithms::{cmp_slice, fls, ilog2};
33+
use self::monty::monty_modpow;
3134

3235
use UsizePromotion;
3336

@@ -233,13 +236,21 @@ impl Num for BigUint {
233236
return Err(e.into());
234237
}
235238

239+
if s.starts_with('_') {
240+
// Must lead with a real digit!
241+
// create ParseIntError::InvalidDigit
242+
let e = u64::from_str_radix(s, radix).unwrap_err();
243+
return Err(e.into());
244+
}
245+
236246
// First normalize all characters to plain digit values
237247
let mut v = Vec::with_capacity(s.len());
238248
for b in s.bytes() {
239249
let d = match b {
240250
b'0'...b'9' => b - b'0',
241251
b'a'...b'z' => b - b'a' + 10,
242252
b'A'...b'Z' => b - b'A' + 10,
253+
b'_' => continue,
243254
_ => u8::MAX,
244255
};
245256
if d < radix as u8 {
@@ -1611,6 +1622,38 @@ impl BigUint {
16111622
self.normalize();
16121623
self
16131624
}
1625+
1626+
/// Returns `(self ^ exponent) % modulus`.
1627+
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
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
1656+
}
16141657
}
16151658

16161659
#[cfg(feature = "serde")]

bigint/src/monty.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use integer::Integer;
2+
use traits::Zero;
3+
4+
use biguint::BigUint;
5+
6+
struct MontyReducer<'a> {
7+
n: &'a BigUint,
8+
n0inv: u32
9+
}
10+
11+
// Calculate the modular inverse of `num`, using Extended GCD.
12+
//
13+
// Reference:
14+
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.20
15+
fn inv_mod_u32(num: u32) -> u32 {
16+
// num needs to be relatively prime to 2**32 -- i.e. it must be odd.
17+
assert!(num % 2 != 0);
18+
19+
let mut a: i64 = num as i64;
20+
let mut b: i64 = (u32::max_value() as i64) + 1;
21+
22+
// ExtendedGcd
23+
// Input: positive integers a and b
24+
// Output: integers (g, u, v) such that g = gcd(a, b) = ua + vb
25+
// As we don't need v for modular inverse, we don't calculate it.
26+
27+
// 1: (u, w) <- (1, 0)
28+
let mut u = 1;
29+
let mut w = 0;
30+
// 3: while b != 0
31+
while b != 0 {
32+
// 4: (q, r) <- DivRem(a, b)
33+
let q = a / b;
34+
let r = a % b;
35+
// 5: (a, b) <- (b, r)
36+
a = b; b = r;
37+
// 6: (u, w) <- (w, u - qw)
38+
let m = u - w*q;
39+
u = w; w = m;
40+
}
41+
42+
assert!(a == 1);
43+
// Downcasting acts like a mod 2^32 too.
44+
u as u32
45+
}
46+
47+
impl<'a> MontyReducer<'a> {
48+
fn new(n: &'a BigUint) -> Self {
49+
let n0inv = inv_mod_u32(n.data[0]);
50+
MontyReducer { n: n, n0inv: n0inv }
51+
}
52+
}
53+
54+
// Montgomery Reduction
55+
//
56+
// Reference:
57+
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 2.6
58+
fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint {
59+
let mut c = a.data;
60+
let n = &mr.n.data;
61+
let n_size = n.len();
62+
63+
// Allocate sufficient work space
64+
c.resize(2 * n_size + 2, 0);
65+
66+
// β is the size of a word, in this case 32 bits. So "a mod β" is
67+
// equivalent to masking a to 32 bits.
68+
// mu <- -N^(-1) mod β
69+
let mu = 0u32.wrapping_sub(mr.n0inv);
70+
71+
// 1: for i = 0 to (n-1)
72+
for i in 0..n_size {
73+
// 2: q_i <- mu*c_i mod β
74+
let q_i = c[i].wrapping_mul(mu);
75+
76+
// 3: C <- C + q_i * N * β^i
77+
super::algorithms::mac_digit(&mut c[i..], n, q_i);
78+
}
79+
80+
// 4: R <- C * β^(-n)
81+
// This is an n-word bitshift, equivalent to skipping n words.
82+
let ret = BigUint::new(c[n_size..].to_vec());
83+
84+
// 5: if R >= β^n then return R-N else return R.
85+
if &ret < mr.n {
86+
ret
87+
} else {
88+
ret - mr.n
89+
}
90+
}
91+
92+
// Montgomery Multiplication
93+
fn monty_mult(a: BigUint, b: &BigUint, mr: &MontyReducer) -> BigUint {
94+
monty_redc(a * b, mr)
95+
}
96+
97+
// Montgomery Squaring
98+
fn monty_sqr(a: BigUint, mr: &MontyReducer) -> BigUint {
99+
// TODO: Replace with an optimised squaring function
100+
monty_redc(&a * &a, mr)
101+
}
102+
103+
pub fn monty_modpow(a: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint{
104+
let mr = MontyReducer::new(modulus);
105+
106+
// Calculate the Montgomery parameter
107+
let mut v = vec![0; modulus.data.len()];
108+
v.push(1);
109+
let r = BigUint::new(v);
110+
111+
// Map the base to the Montgomery domain
112+
let mut apri = a * &r % modulus;
113+
114+
// Binary exponentiation
115+
let mut ans = &r % modulus;
116+
let mut e = exp.clone();
117+
while !e.is_zero() {
118+
if e.is_odd() {
119+
ans = monty_mult(ans, &apri, &mr);
120+
}
121+
apri = monty_sqr(apri, &mr);
122+
e = e >> 1;
123+
}
124+
125+
// Map the result back to the residues domain
126+
monty_redc(ans, &mr)
127+
}

bigint/src/tests/biguint.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,89 @@ 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+
10921175
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {
10931176
let bits = big_digit::BITS;
10941177
vec![(Zero::zero(),
@@ -1468,6 +1551,8 @@ fn test_from_str_radix() {
14681551
assert_eq!(zed, None);
14691552
let blank = BigUint::from_str_radix("_", 2).ok();
14701553
assert_eq!(blank, None);
1554+
let blank_one = BigUint::from_str_radix("_1", 2).ok();
1555+
assert_eq!(blank_one, None);
14711556
let plus_one = BigUint::from_str_radix("+1", 10).ok();
14721557
assert_eq!(plus_one, Some(BigUint::from_slice(&[1])));
14731558
let plus_plus_one = BigUint::from_str_radix("++1", 10).ok();
@@ -1476,6 +1561,10 @@ fn test_from_str_radix() {
14761561
assert_eq!(minus_one, None);
14771562
let zero_plus_two = BigUint::from_str_radix("0+2", 10).ok();
14781563
assert_eq!(zero_plus_two, None);
1564+
let three = BigUint::from_str_radix("1_1", 2).ok();
1565+
assert_eq!(three, Some(BigUint::from_slice(&[3])));
1566+
let ff = BigUint::from_str_radix("1111_1111", 2).ok();
1567+
assert_eq!(ff, Some(BigUint::from_slice(&[0xff])));
14791568
}
14801569

14811570
#[test]

0 commit comments

Comments
 (0)