Implement modpow() for BigUint backed by Montgomery Multiplication
Based on this Gist: https://gist.github.com/yshui/027eecdf95248ea69606 Closes #136
This commit is contained in:
parent
741a5a6207
commit
f523b9c359
|
@ -6,7 +6,7 @@ extern crate rand;
|
||||||
|
|
||||||
use std::mem::replace;
|
use std::mem::replace;
|
||||||
use test::Bencher;
|
use test::Bencher;
|
||||||
use num::{BigInt, BigUint, Zero, One, FromPrimitive};
|
use num::{BigInt, BigUint, Zero, One, FromPrimitive, Num};
|
||||||
use num::bigint::RandBigInt;
|
use num::bigint::RandBigInt;
|
||||||
use rand::{SeedableRng, StdRng};
|
use rand::{SeedableRng, StdRng};
|
||||||
|
|
||||||
|
@ -255,3 +255,26 @@ fn pow_bench(b: &mut Bencher) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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();
|
||||||
|
|
||||||
|
b.iter(|| base.modpow(&e, &m));
|
||||||
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ use traits::{ToPrimitive, FromPrimitive, Float, Num, Unsigned, CheckedAdd, Check
|
||||||
|
|
||||||
#[path = "algorithms.rs"]
|
#[path = "algorithms.rs"]
|
||||||
mod algorithms;
|
mod algorithms;
|
||||||
|
#[path = "monty.rs"]
|
||||||
|
mod monty;
|
||||||
pub use self::algorithms::big_digit;
|
pub use self::algorithms::big_digit;
|
||||||
pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};
|
pub use self::big_digit::{BigDigit, DoubleBigDigit, ZERO_BIG_DIGIT};
|
||||||
|
|
||||||
|
@ -28,6 +30,7 @@ use self::algorithms::{mac_with_carry, mul3, scalar_mul, div_rem, div_rem_digit}
|
||||||
use self::algorithms::{__add2, add2, sub2, sub2rev};
|
use self::algorithms::{__add2, add2, sub2, sub2rev};
|
||||||
use self::algorithms::{biguint_shl, biguint_shr};
|
use self::algorithms::{biguint_shl, biguint_shr};
|
||||||
use self::algorithms::{cmp_slice, fls, ilog2};
|
use self::algorithms::{cmp_slice, fls, ilog2};
|
||||||
|
use self::monty::{MontyReducer, monty_modpow};
|
||||||
|
|
||||||
use UsizePromotion;
|
use UsizePromotion;
|
||||||
|
|
||||||
|
@ -1611,6 +1614,12 @@ impl BigUint {
|
||||||
self.normalize();
|
self.normalize();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns `(self ^ exponent) % modulus`.
|
||||||
|
pub fn modpow(&self, exponent: &Self, modulus: &Self) -> Self {
|
||||||
|
let mr = MontyReducer::new(modulus);
|
||||||
|
monty_modpow(self, exponent, &mr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
use std::iter::repeat;
|
||||||
|
use integer::Integer;
|
||||||
|
use traits::{Zero, One};
|
||||||
|
|
||||||
|
use biguint::BigUint;
|
||||||
|
|
||||||
|
pub struct MontyReducer<'a> {
|
||||||
|
p: &'a BigUint,
|
||||||
|
n: Vec<u32>,
|
||||||
|
n0inv: u64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the modular inverse of `num`, using Extended GCD.
|
||||||
|
//
|
||||||
|
// Reference:
|
||||||
|
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 1.20
|
||||||
|
fn inv_mod_u32(num: u32) -> u64 {
|
||||||
|
// num needs to be relatively prime to u32::max_value()
|
||||||
|
assert!(num % 2 != 0);
|
||||||
|
|
||||||
|
let mut a: i64 = num as i64;
|
||||||
|
let mut b: i64 = (u32::max_value() as i64) + 1;
|
||||||
|
let mu = b;
|
||||||
|
|
||||||
|
// ExtendedGcd
|
||||||
|
// Input: positive integers a and b
|
||||||
|
// Output: integers (g, u, v) such that g = gcd(a, b) = ua + vb
|
||||||
|
// As we don't need v for modular inverse, we don't calculate it.
|
||||||
|
|
||||||
|
// 1: (u, w) <- (1, 0)
|
||||||
|
let mut u = 1;
|
||||||
|
let mut w = 0;
|
||||||
|
// 3: while b != 0
|
||||||
|
while b != 0 {
|
||||||
|
// 4: (q, r) <- DivRem(a, b)
|
||||||
|
let q = a / b;
|
||||||
|
let r = a % b;
|
||||||
|
// 5: (a, b) <- (b, r)
|
||||||
|
a = b; b = r;
|
||||||
|
// 6: (u, w) <- (w, u - qw)
|
||||||
|
let m = u - w*q;
|
||||||
|
u = w; w = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(a == 1);
|
||||||
|
// Ensure returned value is in-range
|
||||||
|
if u < 0 {
|
||||||
|
(u + mu) as u64
|
||||||
|
} else {
|
||||||
|
u as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MontyReducer<'a> {
|
||||||
|
pub fn new(p: &'a BigUint) -> Self {
|
||||||
|
let n : Vec<u32> = p.data.clone();
|
||||||
|
let n0inv = inv_mod_u32(n[0]);
|
||||||
|
MontyReducer { p: p, n: n, n0inv: n0inv }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Montgomery Reduction
|
||||||
|
//
|
||||||
|
// Reference:
|
||||||
|
// Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 2.6
|
||||||
|
pub fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint {
|
||||||
|
let mut c = a.data;
|
||||||
|
let n = &mr.n;
|
||||||
|
let n_size = n.len();
|
||||||
|
let old_size = c.len();
|
||||||
|
|
||||||
|
// Allocate sufficient work space
|
||||||
|
c.reserve(2*n_size+2-old_size);
|
||||||
|
c.extend(repeat(0).take(2*n_size+2-old_size));
|
||||||
|
|
||||||
|
// β is the size of a word, in this case 32 bits. So "a mod β" is
|
||||||
|
// equivalent to masking a to 32 bits.
|
||||||
|
let beta_mask = u32::max_value() as u64;
|
||||||
|
// mu <- -N^(-1) mod β
|
||||||
|
let mu = (beta_mask-mr.n0inv)+1;
|
||||||
|
|
||||||
|
// 1: for i = 0 to (n-1)
|
||||||
|
for i in 0..n_size {
|
||||||
|
// Carry storage
|
||||||
|
let mut carry = 0;
|
||||||
|
|
||||||
|
// 2: q_i <- mu*c_i mod β
|
||||||
|
let q_i = ((c[i] as u64) * mu) & beta_mask;
|
||||||
|
|
||||||
|
// 3: C <- C + q_i * N * β^i
|
||||||
|
// When iterating over each word, this becomes:
|
||||||
|
for j in 0..n_size {
|
||||||
|
// c_(i+j) <- c_(i+j) + q_i * n_j
|
||||||
|
let x = (c[i+j] as u64) + q_i * (n[j] as u64) + carry;
|
||||||
|
c[i+j] = (x & beta_mask) as u32;
|
||||||
|
carry = x >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the remaining carry to the rest of the work space
|
||||||
|
for j in n_size..2*n_size-i+2 {
|
||||||
|
let x = (c[i+j] as u64) + carry;
|
||||||
|
c[i+j] = (x & beta_mask) as u32;
|
||||||
|
carry = x >> 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4: R <- C * β^(-n)
|
||||||
|
// This is an n-word bitshift, equivalent to skipping n words.
|
||||||
|
let r : Vec<u32> = c.iter().skip(n_size).cloned().collect();
|
||||||
|
let ret = BigUint::new(r);
|
||||||
|
|
||||||
|
// 5: if R >= β^n then return R-N else return R.
|
||||||
|
if &ret < mr.p {
|
||||||
|
ret
|
||||||
|
} else {
|
||||||
|
&ret-mr.p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Montgomery Multiplication
|
||||||
|
fn monty_mult(a: BigUint, b: &BigUint, mr: &MontyReducer) -> BigUint {
|
||||||
|
monty_redc(a * b, mr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Montgomery Squaring
|
||||||
|
fn monty_sqr(a: BigUint, mr: &MontyReducer) -> BigUint {
|
||||||
|
// TODO: Replace with an optimised squaring function
|
||||||
|
monty_redc(&a * &a, mr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monty_modpow(a: &BigUint, exp: &BigUint, mr: &MontyReducer) -> BigUint{
|
||||||
|
// Calculate the Montgomery parameter
|
||||||
|
let mut r : BigUint = One::one();
|
||||||
|
while &r < mr.p {
|
||||||
|
r = r << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the base to the Montgomery domain
|
||||||
|
let mut apri = a * &r % mr.p;
|
||||||
|
|
||||||
|
// Binary exponentiation
|
||||||
|
let mut ans = &r % mr.p;
|
||||||
|
let mut e = exp.clone();
|
||||||
|
let zero = Zero::zero();
|
||||||
|
while e > zero {
|
||||||
|
if e.is_odd() {
|
||||||
|
ans = monty_mult(ans, &apri, mr);
|
||||||
|
}
|
||||||
|
apri = monty_sqr(apri, mr);
|
||||||
|
e = e >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map the result back to the residues domain
|
||||||
|
monty_redc(ans, mr)
|
||||||
|
}
|
|
@ -1089,6 +1089,79 @@ fn test_is_even() {
|
||||||
assert!(((&one << 64) + one).is_odd());
|
assert!(((&one << 64) + one).is_odd());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_modpow() {
|
||||||
|
fn check(b: usize, e: usize, m: usize, r: usize) {
|
||||||
|
let big_b: BigUint = FromPrimitive::from_usize(b).unwrap();
|
||||||
|
let big_e: BigUint = FromPrimitive::from_usize(e).unwrap();
|
||||||
|
let big_m: BigUint = FromPrimitive::from_usize(m).unwrap();
|
||||||
|
let big_r: BigUint = FromPrimitive::from_usize(r).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(big_b.modpow(&big_e, &big_m), big_r);
|
||||||
|
}
|
||||||
|
|
||||||
|
check(1, 0, 11, 1);
|
||||||
|
check(0, 15, 11, 0);
|
||||||
|
check(3, 7, 11, 9);
|
||||||
|
check(5, 117, 19, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_modpow_big() {
|
||||||
|
let b = BigUint::from_str_radix("\
|
||||||
|
efac3c0a_0de55551_fee0bfe4_67fa017a_1a898fa1_6ca57cb1\
|
||||||
|
ca9e3248_cacc09a9_b99d6abc_38418d0f_82ae4238_d9a68832\
|
||||||
|
aadec7c1_ac5fed48_7a56a71b_67ac59d5_afb28022_20d9592d\
|
||||||
|
247c4efc_abbd9b75_586088ee_1dc00dc4_232a8e15_6e8191dd\
|
||||||
|
675b6ae0_c80f5164_752940bc_284b7cee_885c1e10_e495345b\
|
||||||
|
8fbe9cfd_e5233fe1_19459d0b_d64be53c_27de5a02_a829976b\
|
||||||
|
33096862_82dad291_bd38b6a9_be396646_ddaf8039_a2573c39\
|
||||||
|
1b14e8bc_2cb53e48_298c047e_d9879e9c_5a521076_f0e27df3\
|
||||||
|
990e1659_d3d8205b_6443ebc0_9918ebee_6764f668_9f2b2be3\
|
||||||
|
b59cbc76_d76d0dfc_d737c3ec_0ccf9c00_ad0554bf_17e776ad\
|
||||||
|
b4edf9cc_6ce540be_76229093_5c53893b", 16).unwrap();
|
||||||
|
let e = BigUint::from_str_radix("\
|
||||||
|
be0e6ea6_08746133_e0fbc1bf_82dba91e_e2b56231_a81888d2\
|
||||||
|
a833a1fc_f7ff002a_3c486a13_4f420bf3_a5435be9_1a5c8391\
|
||||||
|
774d6e6c_085d8357_b0c97d4d_2bb33f7c_34c68059_f78d2541\
|
||||||
|
eacc8832_426f1816_d3be001e_b69f9242_51c7708e_e10efe98\
|
||||||
|
449c9a4a_b55a0f23_9d797410_515da00d_3ea07970_4478a2ca\
|
||||||
|
c3d5043c_bd9be1b4_6dce479d_4302d344_84a939e6_0ab5ada7\
|
||||||
|
12ae34b2_30cc473c_9f8ee69d_2cac5970_29f5bf18_bc8203e4\
|
||||||
|
f3e895a2_13c94f1e_24c73d77_e517e801_53661fdd_a2ce9e47\
|
||||||
|
a73dd7f8_2f2adb1e_3f136bf7_8ae5f3b8_08730de1_a4eff678\
|
||||||
|
e77a06d0_19a522eb_cbefba2a_9caf7736_b157c5c6_2d192591\
|
||||||
|
17946850_2ddb1822_117b68a0_32f7db88", 16).unwrap();
|
||||||
|
// 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 r = BigUint::from_str_radix("\
|
||||||
|
a1468311_6e56edc9_7a98228b_5e924776_0dd7836e_caabac13\
|
||||||
|
eda5373b_4752aa65_a1454850_40dc770e_30aa8675_6be7d3a8\
|
||||||
|
9d3085e4_da5155cf_b451ef62_54d0da61_cf2b2c87_f495e096\
|
||||||
|
055309f7_77802bbb_37271ba8_1313f1b5_075c75d1_024b6c77\
|
||||||
|
fdb56f17_b05bce61_e527ebfd_2ee86860_e9907066_edd526e7\
|
||||||
|
93d289bf_6726b293_41b0de24_eff82424_8dfd374b_4ec59542\
|
||||||
|
35ced2b2_6b195c90_10042ffb_8f58ce21_bc10ec42_64fda779\
|
||||||
|
d352d234_3d4eaea6_a86111ad_a37e9555_43ca78ce_2885bed7\
|
||||||
|
5a30d182_f1cf6834_dc5b6e27_1a41ac34_a2e91e11_33363ff0\
|
||||||
|
f88a7b04_900227c9_f6e6d06b_7856b4bb_4e354d61_060db6c8\
|
||||||
|
109c4735_6e7db425_7b5d74c7_0b709508", 16).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(b.modpow(&e, &m), r);
|
||||||
|
}
|
||||||
|
|
||||||
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {
|
fn to_str_pairs() -> Vec<(BigUint, Vec<(u32, String)>)> {
|
||||||
let bits = big_digit::BITS;
|
let bits = big_digit::BITS;
|
||||||
vec![(Zero::zero(),
|
vec![(Zero::zero(),
|
||||||
|
|
Loading…
Reference in New Issue