Skip to content

Commit f5eaf0e

Browse files
committed
[WIP] Minimum modulus size checks
Changes the existing checked APIs to respect a minimum modulus size in addition to a maximum one. Note: several tests fail because of this, so we'll need to go through them and convert to an unchecked API where appropriate (or decide if the test is bogus to begin with)
1 parent 1ffef57 commit f5eaf0e

File tree

2 files changed

+125
-43
lines changed

2 files changed

+125
-43
lines changed

src/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub enum Error {
4040
/// Invalid coefficient.
4141
InvalidCoefficient,
4242

43+
/// Modulus too small.
44+
ModulusTooSmall,
45+
4346
/// Modulus too large.
4447
ModulusTooLarge,
4548

@@ -92,6 +95,7 @@ impl core::fmt::Display for Error {
9295
Error::InvalidModulus => write!(f, "invalid modulus"),
9396
Error::InvalidExponent => write!(f, "invalid exponent"),
9497
Error::InvalidCoefficient => write!(f, "invalid coefficient"),
98+
Error::ModulusTooSmall => write!(f, "modulus too small"),
9599
Error::ModulusTooLarge => write!(f, "modulus too large"),
96100
Error::PublicExponentTooSmall => write!(f, "public exponent too small"),
97101
Error::PublicExponentTooLarge => write!(f, "public exponent too large"),

src/key.rs

Lines changed: 121 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use alloc::vec::Vec;
22
use core::cmp::Ordering;
33
use core::fmt;
44
use core::hash::{Hash, Hasher};
5+
use core::ops::Range;
56

67
use crypto_bigint::modular::{BoxedMontyForm, BoxedMontyParams};
78
use crypto_bigint::{BoxedUint, Integer, NonZero, Odd, Resize};
@@ -206,29 +207,37 @@ impl RsaPublicKey {
206207
pub fn verify<S: SignatureScheme>(&self, scheme: S, hashed: &[u8], sig: &[u8]) -> Result<()> {
207208
scheme.verify(self, hashed, sig)
208209
}
209-
}
210210

211-
impl RsaPublicKey {
212211
/// Minimum value of the public exponent `e`.
213212
pub const MIN_PUB_EXPONENT: u64 = 2;
214213

215214
/// Maximum value of the public exponent `e`.
216215
pub const MAX_PUB_EXPONENT: u64 = (1 << 33) - 1;
217216

218-
/// Maximum size of the modulus `n` in bits.
219-
pub const MAX_SIZE: usize = 4096;
217+
/// Default minimum size of the modulus `n` in bits.
218+
pub const MIN_SIZE: u32 = 1024;
219+
220+
/// Default maximum size of the modulus `n` in bits.
221+
pub const MAX_SIZE: u32 = 4096;
220222

221223
/// Create a new public key from its components.
222224
///
223225
/// This function accepts public keys with a modulus size up to 4096-bits,
224226
/// i.e. [`RsaPublicKey::MAX_SIZE`].
225227
pub fn new(n: BoxedUint, e: BoxedUint) -> Result<Self> {
226-
Self::new_with_max_size(n, e, Self::MAX_SIZE)
228+
Self::new_with_size_limits(n, e, Self::MIN_SIZE..Self::MAX_SIZE)
227229
}
228230

229231
/// Create a new public key from its components.
230-
pub fn new_with_max_size(n: BoxedUint, e: BoxedUint, max_size: usize) -> Result<Self> {
231-
check_public_with_max_size(&n, &e, max_size)?;
232+
///
233+
/// Accepts a third argument which specifies a range of allowed sizes from minimum to maximum
234+
/// in bits, which by default is `1024..4096`.
235+
pub fn new_with_size_limits(
236+
n: BoxedUint,
237+
e: BoxedUint,
238+
size_range_bits: Range<u32>,
239+
) -> Result<Self> {
240+
check_public_with_size_limits(&n, &e, size_range_bits)?;
232241

233242
let n_odd = Odd::new(n.clone())
234243
.into_option()
@@ -239,19 +248,30 @@ impl RsaPublicKey {
239248
Ok(Self { n, e, n_params })
240249
}
241250

251+
/// Deprecated: this has been replaced with [`RsaPublicKey::new_with_size_limits`].
252+
#[deprecated(since = "0.10.0", note = "please use `new_with_size_limits` instead")]
253+
pub fn new_with_max_size(n: BoxedUint, e: BoxedUint, max_size: usize) -> Result<Self> {
254+
Self::new_with_size_limits(n, e, Self::MIN_SIZE..(max_size as u32))
255+
}
256+
242257
/// Create a new public key, bypassing checks around the modulus and public
243258
/// exponent size.
244259
///
245260
/// This method is not recommended, and only intended for unusual use cases.
246261
/// Most applications should use [`RsaPublicKey::new`] or
247-
/// [`RsaPublicKey::new_with_max_size`] instead.
262+
/// [`RsaPublicKey::new_with_size_limits`] instead.
248263
pub fn new_unchecked(n: BoxedUint, e: BoxedUint) -> Self {
249264
let n_odd = Odd::new(n.clone()).expect("n must be odd");
250265
let n_params = BoxedMontyParams::new(n_odd);
251266
let n = NonZero::new(n).expect("odd numbers are non zero");
252267

253268
Self { n, e, n_params }
254269
}
270+
271+
/// Get the size of the modulus `n` in bits.
272+
pub fn bits(&self) -> u32 {
273+
self.n.bits_vartime()
274+
}
255275
}
256276

257277
impl PublicKeyParts for RsaPrivateKey {
@@ -304,6 +324,36 @@ impl RsaPrivateKey {
304324
///
305325
/// [NIST SP 800-56B Revision 2]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf
306326
pub fn from_components(
327+
n: Odd<BoxedUint>,
328+
e: BoxedUint,
329+
d: BoxedUint,
330+
primes: Vec<BoxedUint>,
331+
) -> Result<RsaPrivateKey> {
332+
// The primes may come in padded with zeros too, so we need to shorten them as well.
333+
let primes = primes
334+
.into_iter()
335+
.map(|p| {
336+
let p_bits = p.bits();
337+
p.resize_unchecked(p_bits)
338+
})
339+
.collect();
340+
341+
let mut k = Self::from_components_unchecked(n, e, d, primes)?;
342+
343+
// Always validate the key, to ensure precompute can't fail
344+
k.validate()?;
345+
346+
// Precompute when possible, ignore error otherwise.
347+
k.precompute().ok();
348+
349+
Ok(k)
350+
}
351+
352+
/// Constructs an RSA key pair from individual components. Bypasses checks on the key's
353+
/// validity like the modulus size.
354+
///
355+
/// Please use [`RsaPrivateKey::from_components`] whenever possible.
356+
pub fn from_components_unchecked(
307357
n: Odd<BoxedUint>,
308358
e: BoxedUint,
309359
d: BoxedUint,
@@ -330,8 +380,8 @@ impl RsaPrivateKey {
330380
1 => return Err(Error::NprimesTooSmall),
331381
_ => {
332382
// Check that the product of primes matches the modulus.
333-
// This also ensures that `bit_precision` of each prime is <= that of the modulus,
334-
// and `bit_precision` of their product is >= that of the modulus.
383+
// This also ensures that `bits_precision` of each prime is <= that of the modulus,
384+
// and `bits_precision` of their product is >= that of the modulus.
335385
if &primes.iter().fold(BoxedUint::one(), |acc, p| acc * p) != n_c.as_ref() {
336386
return Err(Error::InvalidModulus);
337387
}
@@ -347,7 +397,7 @@ impl RsaPrivateKey {
347397
})
348398
.collect();
349399

350-
let mut k = RsaPrivateKey {
400+
Ok(RsaPrivateKey {
351401
pubkey_components: RsaPublicKey {
352402
n: n_c,
353403
e,
@@ -356,15 +406,7 @@ impl RsaPrivateKey {
356406
d,
357407
primes,
358408
precomputed: None,
359-
};
360-
361-
// Alaways validate the key, to ensure precompute can't fail
362-
k.validate()?;
363-
364-
// Precompute when possible, ignore error otherwise.
365-
k.precompute().ok();
366-
367-
Ok(k)
409+
})
368410
}
369411

370412
/// Constructs an RSA key pair from its two primes p and q.
@@ -577,6 +619,11 @@ impl RsaPrivateKey {
577619
) -> Result<Vec<u8>> {
578620
padding.sign(Some(rng), self, digest_in)
579621
}
622+
623+
/// Get the size of the modulus `n` in bits.
624+
pub fn bits(&self) -> u32 {
625+
self.pubkey_components.bits()
626+
}
580627
}
581628

582629
impl PrivateKeyParts for RsaPrivateKey {
@@ -613,16 +660,30 @@ impl PrivateKeyParts for RsaPrivateKey {
613660
}
614661
}
615662

616-
/// Check that the public key is well formed and has an exponent within acceptable bounds.
663+
/// Check that the public key is well-formed and has an exponent within acceptable bounds.
617664
#[inline]
618665
pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> {
619-
check_public_with_max_size(public_key.n(), public_key.e(), RsaPublicKey::MAX_SIZE)
666+
check_public_with_size_limits(
667+
public_key.n(),
668+
public_key.e(),
669+
RsaPublicKey::MIN_SIZE..RsaPublicKey::MAX_SIZE,
670+
)
620671
}
621672

622-
/// Check that the public key is well formed and has an exponent within acceptable bounds.
673+
/// Check that the public key is well-formed and has an exponent within acceptable bounds.
623674
#[inline]
624-
fn check_public_with_max_size(n: &BoxedUint, e: &BoxedUint, max_size: usize) -> Result<()> {
625-
if n.bits_precision() as usize > max_size {
675+
fn check_public_with_size_limits(
676+
n: &BoxedUint,
677+
e: &BoxedUint,
678+
size_range_bits: Range<u32>,
679+
) -> Result<()> {
680+
let modulus_bits = n.bits_vartime();
681+
682+
if modulus_bits < size_range_bits.start {
683+
return Err(Error::ModulusTooSmall);
684+
}
685+
686+
if modulus_bits > size_range_bits.end {
626687
return Err(Error::ModulusTooLarge);
627688
}
628689

@@ -723,7 +784,10 @@ mod tests {
723784
}
724785

725786
fn test_key_basics(private_key: &RsaPrivateKey) {
726-
private_key.validate().expect("invalid private key");
787+
// Some test keys have moduli which are smaller than 1024-bits
788+
if private_key.bits() >= RsaPublicKey::MIN_SIZE {
789+
private_key.validate().expect("invalid private key");
790+
}
727791

728792
assert!(
729793
PrivateKeyParts::d(private_key) < PublicKeyParts::n(private_key).as_ref(),
@@ -769,21 +833,13 @@ mod tests {
769833
};
770834
}
771835

772-
key_generation!(key_generation_128, 2, 128);
773836
key_generation!(key_generation_1024, 2, 1024);
774-
775-
key_generation!(key_generation_multi_3_256, 3, 256);
776-
777-
key_generation!(key_generation_multi_4_64, 4, 64);
778-
779-
key_generation!(key_generation_multi_5_64, 5, 64);
780-
key_generation!(key_generation_multi_8_576, 8, 576);
781837
key_generation!(key_generation_multi_16_1024, 16, 1024);
782838

783839
#[test]
784840
fn test_negative_decryption_value() {
785841
let bits = 128;
786-
let private_key = RsaPrivateKey::from_components(
842+
let private_key = RsaPrivateKey::from_components_unchecked(
787843
Odd::new(
788844
BoxedUint::from_le_slice(
789845
&[
@@ -821,21 +877,43 @@ mod tests {
821877
use serde_test::{assert_tokens, Configure, Token};
822878

823879
let mut rng = ChaCha8Rng::from_seed([42; 32]);
824-
let priv_key = RsaPrivateKey::new(&mut rng, 64).expect("failed to generate key");
880+
let priv_key = RsaPrivateKey::new(&mut rng, 1024).expect("failed to generate key");
825881

826882
let priv_tokens = [Token::Str(concat!(
827-
"3056020100300d06092a864886f70d010101050004423040020100020900a",
828-
"b240c3361d02e370203010001020811e54a15259d22f9020500ceff5cf302",
829-
"0500d3a7aaad020500ccaddf17020500cb529d3d020500bb526d6f"
883+
"30820278020100300d06092a864886f70d0101010500048202623082025e0",
884+
"2010002818100cd1419dc3771354bee0955a90489cce0c98aee6577851358",
885+
"afe386a68bc95287862a1157d5aba8847e8e57b6f2f94748ab7efda3f3c74",
886+
"a6702329397ffe0a8f83e2ef5297aa3d9d883cbeb94ee018fd68e986e08d5",
887+
"b044c15e8170217cd57501d42dd72ef691b2a95bcc090d9bca735bba3ecb8",
888+
"38650f13b1aa36d0f454e37ff020301000102818100935c4248cf3df5c21d",
889+
"c56f5c07faccd129813f5481d189d94c69fdb366f6beeacb2927552a2032f",
890+
"321cd3e92237da40f3fcbfc8df6f9d928b3978c1ec8aab23e857a3ba2db26",
891+
"941ace6ecda8dcb290866a80820b3aa9138179ca867d37825ebcdb48adbe7",
892+
"c397f1e77c4160f0fbf87cc0cd5dff195ac96fd333c0b38384c74c1024100",
893+
"e90ad93c4b19bb40807391b5a9404ce5ea359e7b0556ee25cb2e7455aeb5c",
894+
"af83fc26f34457cdbb173347962c66b6fe0c4686b54dbe0d2c913a7aa924e",
895+
"ff5d67024100e148067566a1fa3aabd0672361be62715516c9d62790b03f4",
896+
"326cc00b2f782e6b64a167689e5c9aebe6a4cf594f3083380fe2a0a7edf1f",
897+
"325e58c523b98199a9024100df15fc8924577892b1a4707b178faf4d751c6",
898+
"91ed928b387486eaafd0ee7866a8916c73fa1b979d1f037ee6fa904563033",
899+
"b4c5f2911e328a3c9f87c0d190d1c7024057461ce26c7141cc6af5608f6f7",
900+
"55f13c2c0024f49a29ef4d321fb9425c1076033ac7e094c20ce4239185b5a",
901+
"246b06795576a178d16fc4d9317db859bfaafa8902410084b2d64651b471b",
902+
"f805af14018db693cdab6059063a6aa4eb8f9ca99b319074b79d7dead3d05",
903+
"68c364978be262d3395aa60541d670f94367babebe7616dbc260"
830904
))];
831905
assert_tokens(&priv_key.clone().readable(), &priv_tokens);
832906

833-
let priv_tokens = [Token::Str(
834-
"3024300d06092a864886f70d01010105000313003010020900ab240c3361d02e370203010001",
835-
)];
907+
let pub_tokens = [Token::Str(concat!(
908+
"30819f300d06092a864886f70d010101050003818d0030818902818100cd1419dc3771354bee",
909+
"0955a90489cce0c98aee6577851358afe386a68bc95287862a1157d5aba8847e8e57b6f2f947",
910+
"48ab7efda3f3c74a6702329397ffe0a8f83e2ef5297aa3d9d883cbeb94ee018fd68e986e08d5",
911+
"b044c15e8170217cd57501d42dd72ef691b2a95bcc090d9bca735bba3ecb838650f13b1aa36d",
912+
"0f454e37ff0203010001",
913+
))];
836914
assert_tokens(
837915
&RsaPublicKey::from(priv_key.clone()).readable(),
838-
&priv_tokens,
916+
&pub_tokens,
839917
);
840918
}
841919

0 commit comments

Comments
 (0)