Skip to content

Commit f4165fe

Browse files
committed
Limit integer widths in RNS calculations
1 parent a8cfcbd commit f4165fe

File tree

2 files changed

+55
-24
lines changed

2 files changed

+55
-24
lines changed

src/algorithms/rsa.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use core::cmp::Ordering;
44

55
use crypto_bigint::modular::{BoxedMontyForm, BoxedMontyParams};
6-
use crypto_bigint::{BoxedUint, Gcd, NonZero, Odd, RandomMod, Wrapping};
6+
use crypto_bigint::{BoxedUint, Gcd, NonZero, Odd, RandomMod};
77
use rand_core::TryCryptoRng;
88
use zeroize::Zeroize;
99

@@ -68,32 +68,52 @@ pub fn rsa_decrypt<R: TryCryptoRng + ?Sized>(
6868
(Some(dp), Some(dq), Some(qinv), Some(p_params), Some(q_params)) if !is_multiprime => {
6969
// We have the precalculated values needed for the CRT.
7070

71-
let _p = &priv_key.primes()[0];
71+
let p = &priv_key.primes()[0];
7272
let q = &priv_key.primes()[1];
7373

7474
// precomputed: dP = (1/e) mod (p-1) = d mod (p-1)
7575
// precomputed: dQ = (1/e) mod (q-1) = d mod (q-1)
7676

77+
// TODO: it may be faster to convert to and from Montgomery with prepared parameters
78+
// (modulo `p` and `q`) rather than calculating the remainder directly.
79+
7780
// m1 = c^dP mod p
78-
let cp = BoxedMontyForm::new(c.clone(), p_params.clone());
81+
let c_mod_dp = (&c
82+
% NonZero::new(p_params.modulus().widen(c.bits_precision())).unwrap())
83+
.shorten(dp.bits_precision());
84+
let cp = BoxedMontyForm::new(c_mod_dp, p_params.clone());
7985
let mut m1 = cp.pow(dp);
8086
// m2 = c^dQ mod q
81-
let cq = BoxedMontyForm::new(c, q_params.clone());
87+
let c_mod_dq = (&c
88+
% NonZero::new(q_params.modulus().widen(c.bits_precision())).unwrap())
89+
.shorten(dq.bits_precision());
90+
let cq = BoxedMontyForm::new(c_mod_dq, q_params.clone());
8291
let m2 = cq.pow(dq).retrieve();
8392

93+
// Note that since `p` and `q` may have different `bits_precision`,
94+
// so it will be different for `m1` and `m2` as well.
95+
8496
// (m1 - m2) mod p = (m1 mod p) - (m2 mod p) mod p
85-
let m2r = BoxedMontyForm::new(m2.clone(), p_params.clone());
97+
let m2r = match p_params.bits_precision().cmp(&q_params.bits_precision()) {
98+
Ordering::Less => BoxedMontyForm::new(
99+
(&m2 % NonZero::new(p.widen(q_params.bits_precision())).unwrap())
100+
.shorten(p_params.bits_precision()),
101+
p_params.clone(),
102+
),
103+
Ordering::Greater => {
104+
BoxedMontyForm::new(m2.widen(p_params.bits_precision()), p_params.clone())
105+
}
106+
Ordering::Equal => BoxedMontyForm::new(m2.clone(), p_params.clone()),
107+
};
86108
m1 -= &m2r;
87109

88110
// precomputed: qInv = (1/q) mod p
89111

90112
// h = qInv.(m1 - m2) mod p
91-
let mut m: Wrapping<BoxedUint> = Wrapping(qinv.mul(&m1).retrieve());
113+
let h = qinv.mul(&m1).retrieve();
92114

93115
// m = m2 + h.q
94-
m *= Wrapping(q.clone());
95-
m += Wrapping(m2);
96-
m.0
116+
m2.widen(n.bits_precision()).wrapping_add(&(h * q))
97117
}
98118
_ => {
99119
// c^d (mod n)
@@ -102,7 +122,7 @@ pub fn rsa_decrypt<R: TryCryptoRng + ?Sized>(
102122
};
103123

104124
// Ensure output precision matches input precision
105-
let m = m.shorten(n_params.bits_precision());
125+
let m = m.widen(n_params.bits_precision());
106126
match ir {
107127
Some(ref ir) => {
108128
// unblind

src/key.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use alloc::vec::Vec;
2+
use core::cmp::Ordering;
23
use core::fmt;
34
use core::hash::{Hash, Hasher};
45

@@ -325,6 +326,9 @@ impl RsaPrivateKey {
325326
_ => {}
326327
}
327328

329+
// The primes may come in padded with zeros, so we need to shorten them.
330+
let primes = primes.iter().map(|p| p.shorten(p.bits())).collect();
331+
328332
let mut k = RsaPrivateKey {
329333
pubkey_components: RsaPublicKey {
330334
n: n_c,
@@ -408,9 +412,8 @@ impl RsaPrivateKey {
408412
}
409413

410414
let d = &self.d;
411-
let bits = d.bits_precision();
412-
let p = self.primes[0].widen(bits);
413-
let q = self.primes[1].widen(bits);
415+
let p = self.primes[0].clone();
416+
let q = self.primes[1].clone();
414417

415418
let p_odd = Odd::new(p.clone())
416419
.into_option()
@@ -431,14 +434,23 @@ impl RsaPrivateKey {
431434
.ok_or(Error::InvalidPrime)?;
432435
let dq = d.rem_vartime(&x);
433436

434-
let qinv = BoxedMontyForm::new(q.clone(), p_params.clone());
435-
let qinv = qinv.invert().into_option().ok_or(Error::InvalidPrime)?;
437+
// Note that since `p` and `q` may have different `bits_precision`,
438+
// so we have to equalize them to calculate the remainder.
439+
let q_mod_p = match p.bits_precision().cmp(&q.bits_precision()) {
440+
Ordering::Less => (&q % &NonZero::new(p.widen(q.bits_precision())).unwrap())
441+
.shorten(p.bits_precision()),
442+
Ordering::Greater => q.widen(p.bits_precision()) % &NonZero::new(p.clone()).unwrap(),
443+
Ordering::Equal => &q % NonZero::new(p.clone()).unwrap(),
444+
};
445+
446+
let q_mod_p = BoxedMontyForm::new(q_mod_p, p_params.clone());
447+
let qinv = q_mod_p.invert().into_option().ok_or(Error::InvalidPrime)?;
436448

437-
debug_assert_eq!(dp.bits_precision(), bits);
438-
debug_assert_eq!(dq.bits_precision(), bits);
439-
debug_assert_eq!(qinv.bits_precision(), bits);
440-
debug_assert_eq!(p_params.bits_precision(), bits);
441-
debug_assert_eq!(q_params.bits_precision(), bits);
449+
debug_assert_eq!(dp.bits_precision(), p.bits_precision());
450+
debug_assert_eq!(dq.bits_precision(), q.bits_precision());
451+
debug_assert_eq!(qinv.bits_precision(), p.bits_precision());
452+
debug_assert_eq!(p_params.bits_precision(), p.bits_precision());
453+
debug_assert_eq!(q_params.bits_precision(), q.bits_precision());
442454

443455
self.precomputed = Some(PrecomputedValues {
444456
dp,
@@ -742,8 +754,7 @@ mod tests {
742754

743755
key_generation!(key_generation_multi_5_64, 5, 64);
744756
key_generation!(key_generation_multi_8_576, 8, 576);
745-
// TODO: reenable, currently slow
746-
// key_generation!(key_generation_multi_16_1024, 16, 1024);
757+
key_generation!(key_generation_multi_16_1024, 16, 1024);
747758

748759
#[test]
749760
fn test_negative_decryption_value() {
@@ -768,8 +779,8 @@ mod tests {
768779
)
769780
.unwrap(),
770781
vec![
771-
BoxedUint::from_le_slice(&[105, 101, 60, 173, 19, 153, 3, 192], bits).unwrap(),
772-
BoxedUint::from_le_slice(&[235, 65, 160, 134, 32, 136, 6, 241], bits).unwrap(),
782+
BoxedUint::from_le_slice(&[105, 101, 60, 173, 19, 153, 3, 192], bits / 2).unwrap(),
783+
BoxedUint::from_le_slice(&[235, 65, 160, 134, 32, 136, 6, 241], bits / 2).unwrap(),
773784
],
774785
)
775786
.unwrap();

0 commit comments

Comments
 (0)