Skip to content

Commit b83ae75

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 c2fe375 commit b83ae75

File tree

2 files changed

+129
-51
lines changed

2 files changed

+129
-51
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: 125 additions & 51 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 {
@@ -309,6 +329,36 @@ impl RsaPrivateKey {
309329
///
310330
/// [NIST SP 800-56B Revision 2]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf
311331
pub fn from_components(
332+
n: BoxedUint,
333+
e: BoxedUint,
334+
d: BoxedUint,
335+
primes: Vec<BoxedUint>,
336+
) -> Result<RsaPrivateKey> {
337+
// The primes may come in padded with zeros too, so we need to shorten them as well.
338+
let primes = primes
339+
.into_iter()
340+
.map(|p| {
341+
let p_bits = p.bits();
342+
p.resize_unchecked(p_bits)
343+
})
344+
.collect();
345+
346+
let mut k = Self::from_components_unchecked(n, e, d, primes)?;
347+
348+
// Always validate the key, to ensure precompute can't fail
349+
k.validate()?;
350+
351+
// Precompute when possible, ignore error otherwise.
352+
k.precompute().ok();
353+
354+
Ok(k)
355+
}
356+
357+
/// Constructs an RSA key pair from individual components. Bypasses checks on the key's
358+
/// validity like the modulus size.
359+
///
360+
/// Please use [`RsaPrivateKey::from_components`] whenever possible.
361+
pub fn from_components_unchecked(
312362
n: BoxedUint,
313363
e: BoxedUint,
314364
d: BoxedUint,
@@ -337,8 +387,8 @@ impl RsaPrivateKey {
337387
1 => return Err(Error::NprimesTooSmall),
338388
_ => {
339389
// Check that the product of primes matches the modulus.
340-
// This also ensures that `bit_precision` of each prime is <= that of the modulus,
341-
// and `bit_precision` of their product is >= that of the modulus.
390+
// This also ensures that `bits_precision` of each prime is <= that of the modulus,
391+
// and `bits_precision` of their product is >= that of the modulus.
342392
if &primes.iter().fold(BoxedUint::one(), |acc, p| acc * p) != n_c.as_ref() {
343393
return Err(Error::InvalidModulus);
344394
}
@@ -354,7 +404,7 @@ impl RsaPrivateKey {
354404
})
355405
.collect();
356406

357-
let mut k = RsaPrivateKey {
407+
Ok(RsaPrivateKey {
358408
pubkey_components: RsaPublicKey {
359409
n: n_c,
360410
e,
@@ -363,15 +413,7 @@ impl RsaPrivateKey {
363413
d,
364414
primes,
365415
precomputed: None,
366-
};
367-
368-
// Alaways validate the key, to ensure precompute can't fail
369-
k.validate()?;
370-
371-
// Precompute when possible, ignore error otherwise.
372-
k.precompute().ok();
373-
374-
Ok(k)
416+
})
375417
}
376418

377419
/// Constructs an RSA key pair from its two primes p and q.
@@ -584,6 +626,11 @@ impl RsaPrivateKey {
584626
) -> Result<Vec<u8>> {
585627
padding.sign(Some(rng), self, digest_in)
586628
}
629+
630+
/// Get the size of the modulus `n` in bits.
631+
pub fn bits(&self) -> u32 {
632+
self.pubkey_components.bits()
633+
}
587634
}
588635

589636
impl PrivateKeyParts for RsaPrivateKey {
@@ -620,16 +667,30 @@ impl PrivateKeyParts for RsaPrivateKey {
620667
}
621668
}
622669

623-
/// Check that the public key is well formed and has an exponent within acceptable bounds.
670+
/// Check that the public key is well-formed and has an exponent within acceptable bounds.
624671
#[inline]
625672
pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> {
626-
check_public_with_max_size(public_key.n(), public_key.e(), RsaPublicKey::MAX_SIZE)
673+
check_public_with_size_limits(
674+
public_key.n(),
675+
public_key.e(),
676+
RsaPublicKey::MIN_SIZE..RsaPublicKey::MAX_SIZE,
677+
)
627678
}
628679

629-
/// Check that the public key is well formed and has an exponent within acceptable bounds.
680+
/// Check that the public key is well-formed and has an exponent within acceptable bounds.
630681
#[inline]
631-
fn check_public_with_max_size(n: &BoxedUint, e: &BoxedUint, max_size: usize) -> Result<()> {
632-
if n.bits_vartime() as usize > max_size {
682+
fn check_public_with_size_limits(
683+
n: &BoxedUint,
684+
e: &BoxedUint,
685+
size_range_bits: Range<u32>,
686+
) -> Result<()> {
687+
let modulus_bits = n.bits_vartime();
688+
689+
if modulus_bits < size_range_bits.start {
690+
return Err(Error::ModulusTooSmall);
691+
}
692+
693+
if modulus_bits > size_range_bits.end {
633694
return Err(Error::ModulusTooLarge);
634695
}
635696

@@ -730,7 +791,10 @@ mod tests {
730791
}
731792

732793
fn test_key_basics(private_key: &RsaPrivateKey) {
733-
private_key.validate().expect("invalid private key");
794+
// Some test keys have moduli which are smaller than 1024-bits
795+
if private_key.bits() >= RsaPublicKey::MIN_SIZE {
796+
private_key.validate().expect("invalid private key");
797+
}
734798

735799
assert!(
736800
PrivateKeyParts::d(private_key) < PublicKeyParts::n(private_key).as_ref(),
@@ -776,29 +840,17 @@ mod tests {
776840
};
777841
}
778842

779-
key_generation!(key_generation_128, 2, 128);
780843
key_generation!(key_generation_1024, 2, 1024);
781-
782-
key_generation!(key_generation_multi_3_256, 3, 256);
783-
784-
key_generation!(key_generation_multi_4_64, 4, 64);
785-
786-
key_generation!(key_generation_multi_5_64, 5, 64);
787-
key_generation!(key_generation_multi_8_576, 8, 576);
788844
key_generation!(key_generation_multi_16_1024, 16, 1024);
789845

790846
#[test]
791847
fn test_negative_decryption_value() {
792848
let bits = 128;
793-
let private_key = RsaPrivateKey::from_components(
794-
BoxedUint::from_le_slice(
795-
&[
796-
99, 192, 208, 179, 0, 220, 7, 29, 49, 151, 75, 107, 75, 73, 200, 180,
797-
],
798-
bits,
799-
)
800-
.unwrap(),
801-
BoxedUint::from_le_slice(&[1, 0, 1, 0, 0, 0, 0, 0], 64).unwrap(),
849+
let private_key = RsaPrivateKey::from_components_unchecked(
850+
BoxedUint::from_le_slice_vartime(&[
851+
99, 192, 208, 179, 0, 220, 7, 29, 49, 151, 75, 107, 75, 73, 200, 180,
852+
]),
853+
BoxedUint::from_le_slice_vartime(&[1, 0, 1, 0, 0, 0, 0, 0]),
802854
BoxedUint::from_le_slice(
803855
&[
804856
81, 163, 254, 144, 171, 159, 144, 42, 244, 133, 51, 249, 28, 12, 63, 65,
@@ -825,21 +877,43 @@ mod tests {
825877
use serde_test::{assert_tokens, Configure, Token};
826878

827879
let mut rng = ChaCha8Rng::from_seed([42; 32]);
828-
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");
829881

830882
let priv_tokens = [Token::Str(concat!(
831-
"3056020100300d06092a864886f70d010101050004423040020100020900a",
832-
"b240c3361d02e370203010001020811e54a15259d22f9020500ceff5cf302",
833-
"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"
834904
))];
835905
assert_tokens(&priv_key.clone().readable(), &priv_tokens);
836906

837-
let priv_tokens = [Token::Str(
838-
"3024300d06092a864886f70d01010105000313003010020900ab240c3361d02e370203010001",
839-
)];
907+
let pub_tokens = [Token::Str(concat!(
908+
"30819f300d06092a864886f70d010101050003818d0030818902818100cd1419dc3771354bee",
909+
"0955a90489cce0c98aee6577851358afe386a68bc95287862a1157d5aba8847e8e57b6f2f947",
910+
"48ab7efda3f3c74a6702329397ffe0a8f83e2ef5297aa3d9d883cbeb94ee018fd68e986e08d5",
911+
"b044c15e8170217cd57501d42dd72ef691b2a95bcc090d9bca735bba3ecb838650f13b1aa36d",
912+
"0f454e37ff0203010001",
913+
))];
840914
assert_tokens(
841915
&RsaPublicKey::from(priv_key.clone()).readable(),
842-
&priv_tokens,
916+
&pub_tokens,
843917
);
844918
}
845919

0 commit comments

Comments
 (0)