Skip to content

Commit e7f4810

Browse files
authored
Add RsaPublicKey::new_with_max_size; 4096-bit default max (#176)
This constructor accepts a configurable maximum key size which can be used in applications that need to deal with unusually large RSA public keys, such as OpenPGP. With the ability to configure an upper limit, this makes it possible to lower the default maximum key size to 4096-bits, which is a reasonable upper limit for most applications. Fixes #166
1 parent 8d3fe53 commit e7f4810

File tree

2 files changed

+105
-15
lines changed

2 files changed

+105
-15
lines changed

src/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub type Result<T> = core::result::Result<T, Error>;
22

33
/// Error types
4-
#[derive(Debug)]
4+
#[derive(Debug, Eq, PartialEq)]
55
#[non_exhaustive]
66
pub enum Error {
77
InvalidPaddingScheme,

src/key.rs

+104-14
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,6 @@ use crate::padding::PaddingScheme;
1616
use crate::raw::{DecryptionPrimitive, EncryptionPrimitive};
1717
use crate::{oaep, pkcs1v15, pss};
1818

19-
const MIN_PUB_EXPONENT: u64 = 2;
20-
const MAX_PUB_EXPONENT: u64 = (1 << 33) - 1;
21-
const MAX_MODULUS_BITS: usize = 16384;
22-
2319
pub trait PublicKeyParts {
2420
/// Returns the modulus of the key.
2521
fn n(&self) -> &BigUint;
@@ -229,11 +225,27 @@ impl PublicKey for RsaPublicKey {
229225
}
230226

231227
impl RsaPublicKey {
232-
/// Create a new key from its components.
228+
/// Minimum value of the public exponent `e`.
229+
pub const MIN_PUB_EXPONENT: u64 = 2;
230+
231+
/// Maximum value of the public exponent `e`.
232+
pub const MAX_PUB_EXPONENT: u64 = (1 << 33) - 1;
233+
234+
/// Maximum size of the modulus `n` in bits.
235+
pub const MAX_SIZE: usize = 4096;
236+
237+
/// Create a new public key from its components.
238+
///
239+
/// This function accepts public keys with a modulus size up to 4096-bits,
240+
/// i.e. [`RsaPublicKey::MAX_SIZE`].
233241
pub fn new(n: BigUint, e: BigUint) -> Result<Self> {
234-
let k = RsaPublicKey { n, e };
235-
check_public(&k)?;
242+
Self::new_with_max_size(n, e, Self::MAX_SIZE)
243+
}
236244

245+
/// Create a new public key from its components.
246+
pub fn new_with_max_size(n: BigUint, e: BigUint, max_size: usize) -> Result<Self> {
247+
let k = RsaPublicKey { n, e };
248+
check_public_with_max_size(&k, max_size)?;
237249
Ok(k)
238250
}
239251
}
@@ -336,10 +348,9 @@ impl RsaPrivateKey {
336348
/// Get the public key from the private key, cloning `n` and `e`.
337349
///
338350
/// Generally this is not needed since `RsaPrivateKey` implements the `PublicKey` trait,
339-
/// but it can occationally be useful to discard the private information entirely.
351+
/// but it can occasionally be useful to discard the private information entirely.
340352
pub fn to_public_key(&self) -> RsaPublicKey {
341-
// Safe to unwrap since n and e are already verified.
342-
RsaPublicKey::new(self.n().clone(), self.e().clone()).unwrap()
353+
self.pubkey_components.clone()
343354
}
344355

345356
/// Performs some calculations to speed up private key operations.
@@ -409,7 +420,7 @@ impl RsaPrivateKey {
409420
}
410421

411422
/// Performs basic sanity checks on the key.
412-
/// Returns `Ok(())` if everything is good, otherwise an approriate error.
423+
/// Returns `Ok(())` if everything is good, otherwise an appropriate error.
413424
pub fn validate(&self) -> Result<()> {
414425
check_public(self)?;
415426

@@ -549,7 +560,13 @@ impl RsaPrivateKey {
549560
/// Check that the public key is well formed and has an exponent within acceptable bounds.
550561
#[inline]
551562
pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> {
552-
if public_key.n().bits() > MAX_MODULUS_BITS {
563+
check_public_with_max_size(public_key, RsaPublicKey::MAX_SIZE)
564+
}
565+
566+
/// Check that the public key is well formed and has an exponent within acceptable bounds.
567+
#[inline]
568+
fn check_public_with_max_size(public_key: &impl PublicKeyParts, max_size: usize) -> Result<()> {
569+
if public_key.n().bits() > max_size {
553570
return Err(Error::ModulusTooLarge);
554571
}
555572

@@ -558,11 +575,11 @@ pub fn check_public(public_key: &impl PublicKeyParts) -> Result<()> {
558575
.to_u64()
559576
.ok_or(Error::PublicExponentTooLarge)?;
560577

561-
if e < MIN_PUB_EXPONENT {
578+
if e < RsaPublicKey::MIN_PUB_EXPONENT {
562579
return Err(Error::PublicExponentTooSmall);
563580
}
564581

565-
if e > MAX_PUB_EXPONENT {
582+
if e > RsaPublicKey::MAX_PUB_EXPONENT {
566583
return Err(Error::PublicExponentTooLarge);
567584
}
568585

@@ -601,6 +618,7 @@ mod tests {
601618

602619
use alloc::string::String;
603620
use digest::{Digest, DynDigest};
621+
use hex_literal::hex;
604622
use num_traits::{FromPrimitive, ToPrimitive};
605623
use rand_chacha::{
606624
rand_core::{RngCore, SeedableRng},
@@ -801,6 +819,78 @@ mod tests {
801819
.unwrap();
802820
}
803821

822+
#[test]
823+
fn reject_oversized_private_key() {
824+
// -----BEGIN PUBLIC KEY-----
825+
// MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEAkMBiB8qsNVXAsJR6Xoto
826+
// H1r2rtZl/xzUK2tIfy99aPE489u+5tLxCQhQf+a89158vSDpr2/xwgK8w9u0Xpu2
827+
// m7XRKjVMS0Y6UIINFoeTc87rVXT92Scr47kNVcGmSFXez4BSDpS+LKpWwXN+0AQu
828+
// +cmcfdtsx2862iEbqQvq4PwKGQJOdOR0yldH8O4yeJK/buvIOXRHjb++vtQND/xi
829+
// bFGAcd9WJqvaOG7tclhbZ277mbO6ER+y9Lj7AyO8ywybWqNeHaVPHMysPhT7HUWI
830+
// 17m59i1OpuVwwEnvzDQQEUf9d5hUmkLYb5qQzuf6Ddnx/04QJCKAgkhyr9CXgnV6
831+
// vEZ3PKtpicCHRxk7eqTEmgBlgwqH5vflRFV1iywQMXJnuRhzWOQaXl/vb8v4HIvF
832+
// 4TatEZKqfzpbyScLIiYbPEAhHXKdZMd2zY8hkSbicifePApAZmuNpAxxJDZzphh7
833+
// r4lD6t8MPT/RUAdtrZfihqaBhduFI6YeVIy6emg05M6YWvlUyer7nYGaPRS1JqD4
834+
// 0v7xOtme5I8Qw6APiFPXhTqBK3occr7TgGb3V3lpC8Eq+esNHrji98R1fITkFXJW
835+
// KdFcTWjBghPxiobUzMCFUrPIDJcWXeBzrARAryU+hXjEiFfzluXrps0B7RJQ/rLD
836+
// LXeTn4vovUeHQVHa7YfoyWMy9pfqeVC+56LBK7SEIAvL0I3lrq5vIv+ZIuOAdbVg
837+
// JiRy8DneCOk2LP3RnA8M0HSevYW93DiC+4h/l4ntjjiOfi6yRVOZ8WbVyXZ/83j4
838+
// 6+pGWgvi0uMyb+btgOXjBQv7bGqdyHMc5Lqk5bF7ExETx51vKQMYCV4351caS6aX
839+
// q16lYZATHgbTADEAZHdroDMJB+HMQaze9O6qU5ZO8wxxAjw89xry0dnoOQD/yA4H
840+
// 7CRCo9vVDpV2hqIvHY9RI2T7cek28kmQpKvNvvK+ovmM138dHKViWULHk0fBRt7m
841+
// 4wQ+tiL2PmJ/Tr8g1gVhM6S9D1XdE9z0KeDnODCWn1Q8sx2G2ah4ynnYQURDWcwO
842+
// McAoP6bdJ7cCt+4F2tEsMPf4S/EwlnjvuNoQjvztxCPahYe9EnyggtQXyHJveIn7
843+
// gDJsP6b93VB6x4QbLy5ch4DUhqDWginuKVeo7CTgDkq03j/IEaS1BHwreSDQceny
844+
// +bYWONwV+4TMpGytKOHvU5288kmHbyZHdXuaXk8LLqbnqr30fa6Cbp4llCi9sH5a
845+
// Kmi5jxQfVTe+elkMs7oVsLsVgkZS6NqPcOuEckAFijNqG223+IJoqvifCzO5Bdcs
846+
// JTOLE+YaUYc8LUJwIaPykgcXmtMvQjeT8MCQ3aAlzkHfDpSvvICrXtqbGiaKolU6
847+
// mQIDAQAB
848+
// -----END PUBLIC KEY-----
849+
850+
let n = BigUint::from_bytes_be(&hex!(
851+
"
852+
90c06207caac3555c0b0947a5e8b681f5af6aed665ff1cd42b6b487f2f7d68f1
853+
38f3dbbee6d2f10908507fe6bcf75e7cbd20e9af6ff1c202bcc3dbb45e9bb69b
854+
b5d12a354c4b463a50820d16879373ceeb5574fdd9272be3b90d55c1a64855de
855+
cf80520e94be2caa56c1737ed0042ef9c99c7ddb6cc76f3ada211ba90beae0fc
856+
0a19024e74e474ca5747f0ee327892bf6eebc83974478dbfbebed40d0ffc626c
857+
518071df5626abda386eed72585b676efb99b3ba111fb2f4b8fb0323bccb0c9b
858+
5aa35e1da54f1cccac3e14fb1d4588d7b9b9f62d4ea6e570c049efcc34101147
859+
fd7798549a42d86f9a90cee7fa0dd9f1ff4e10242280824872afd09782757abc
860+
46773cab6989c08747193b7aa4c49a0065830a87e6f7e54455758b2c10317267
861+
b9187358e41a5e5fef6fcbf81c8bc5e136ad1192aa7f3a5bc9270b22261b3c40
862+
211d729d64c776cd8f219126e27227de3c0a40666b8da40c71243673a6187baf
863+
8943eadf0c3d3fd150076dad97e286a68185db8523a61e548cba7a6834e4ce98
864+
5af954c9eafb9d819a3d14b526a0f8d2fef13ad99ee48f10c3a00f8853d7853a
865+
812b7a1c72bed38066f75779690bc12af9eb0d1eb8e2f7c4757c84e415725629
866+
d15c4d68c18213f18a86d4ccc08552b3c80c97165de073ac0440af253e8578c4
867+
8857f396e5eba6cd01ed1250feb2c32d77939f8be8bd47874151daed87e8c963
868+
32f697ea7950bee7a2c12bb484200bcbd08de5aeae6f22ff9922e38075b56026
869+
2472f039de08e9362cfdd19c0f0cd0749ebd85bddc3882fb887f9789ed8e388e
870+
7e2eb2455399f166d5c9767ff378f8ebea465a0be2d2e3326fe6ed80e5e3050b
871+
fb6c6a9dc8731ce4baa4e5b17b131113c79d6f290318095e37e7571a4ba697ab
872+
5ea56190131e06d300310064776ba0330907e1cc41acdef4eeaa53964ef30c71
873+
023c3cf71af2d1d9e83900ffc80e07ec2442a3dbd50e957686a22f1d8f512364
874+
fb71e936f24990a4abcdbef2bea2f98cd77f1d1ca5625942c79347c146dee6e3
875+
043eb622f63e627f4ebf20d6056133a4bd0f55dd13dcf429e0e73830969f543c
876+
b31d86d9a878ca79d841444359cc0e31c0283fa6dd27b702b7ee05dad12c30f7
877+
f84bf1309678efb8da108efcedc423da8587bd127ca082d417c8726f7889fb80
878+
326c3fa6fddd507ac7841b2f2e5c8780d486a0d68229ee2957a8ec24e00e4ab4
879+
de3fc811a4b5047c2b7920d071e9f2f9b61638dc15fb84cca46cad28e1ef539d
880+
bcf249876f2647757b9a5e4f0b2ea6e7aabdf47dae826e9e259428bdb07e5a2a
881+
68b98f141f5537be7a590cb3ba15b0bb15824652e8da8f70eb847240058a336a
882+
1b6db7f88268aaf89f0b33b905d72c25338b13e61a51873c2d427021a3f29207
883+
179ad32f423793f0c090dda025ce41df0e94afbc80ab5eda9b1a268aa2553a99"
884+
));
885+
886+
let e = BigUint::from_u64(65537).unwrap();
887+
888+
assert_eq!(
889+
RsaPublicKey::new(n, e).err().unwrap(),
890+
Error::ModulusTooLarge
891+
);
892+
}
893+
804894
fn get_private_key() -> RsaPrivateKey {
805895
// -----BEGIN RSA PRIVATE KEY-----
806896
// MIIEpAIBAAKCAQEA05e4TZikwmE47RtpWoEG6tkdVTvwYEG2LT/cUKBB4iK49FKW

0 commit comments

Comments
 (0)