Skip to content

Commit 5af97d2

Browse files
committed
rsa: reduce code duplication, enforce exponent throuhgout
1 parent 4a1d33e commit 5af97d2

File tree

2 files changed

+59
-19
lines changed

2 files changed

+59
-19
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ eddsa = ["dep:ed25519-dalek", "dep:sha2", "dep:signature"]
2424
hkdf = ["dep:hkdf", "dep:sha2"]
2525
mldsa = ["dep:der", "dep:ml-dsa", "dep:pem", "dep:pkcs8", "dep:sha2", "dep:spki", "dep:subtle", "dep:zeroize"]
2626
rand = []
27-
rsa = ["dep:rsa", "dep:signature"]
27+
rsa = ["dep:pem", "dep:rsa", "dep:signature"]
2828
stream = ["dep:age-core", "dep:chacha20poly1305", "dep:pin-project", "dep:zeroize"]
2929
x509 = ["xdsa", "dep:bcder", "dep:bytes", "dep:chrono", "dep:ring", "dep:x509-certificate"]
3030
xdsa = ["eddsa", "mldsa", "x509", "dep:x509-parser"]

src/rsa/mod.rs

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99
//! https://datatracker.ietf.org/doc/html/rfc8017
1010
1111
use rsa::pkcs1v15::Signature;
12-
use rsa::pkcs8::{
13-
DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, Error, LineEnding,
14-
};
12+
use rsa::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, Error};
1513
use rsa::rand_core::OsRng;
1614
use rsa::sha2::{Digest, Sha256};
1715
use rsa::signature::hazmat::PrehashVerifier;
@@ -48,21 +46,48 @@ impl SecretKey {
4846
let e = BigUint::from_bytes_be(&bytes[512..520]);
4947

5048
let n = &p * &q;
49+
50+
// Whilst the RSA algorithm permits different exponents, every modern
51+
// system only ever uses 65537 and most also enforce this. Might as
52+
// well do the same.
53+
if e != BigUint::from(65537u32) {
54+
return Err(rsa::Error::InvalidExponent);
55+
}
5156
let key = RsaPrivateKey::from_components(n, e, d, vec![p, q])?;
5257
let sig = rsa::pkcs1v15::SigningKey::<Sha256>::new(key);
5358
Ok(Self { inner: sig })
5459
}
5560

5661
/// from_der parses a DER buffer into a private key.
57-
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
62+
pub fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
5863
let inner = rsa::pkcs1v15::SigningKey::<Sha256>::from_pkcs8_der(der)?;
64+
65+
// Whilst the RSA algorithm permits different exponents, every modern
66+
// system only ever uses 65537 and most also enforce this. Might as
67+
// well do the same.
68+
let key: &RsaPrivateKey = inner.as_ref();
69+
if *key.e() != BigUint::from(65537u32) {
70+
return Err(Error::KeyMalformed.into());
71+
}
72+
// The upstream rsa crate ignores CRT parameters (dP, dQ, qInv) and
73+
// recomputes them, accepting malformed values. We don't want to allow
74+
// that, so just round trip the format and see if it's matching or not.
75+
let recoded = rsa::pkcs1v15::SigningKey::<Sha256>::to_pkcs8_der(&inner)?;
76+
if recoded.as_bytes() != der {
77+
return Err(Error::KeyMalformed.into());
78+
}
5979
Ok(Self { inner })
6080
}
6181

6282
/// from_pem parses a PEM string into a private key.
63-
pub fn from_pem(pem: &str) -> Result<Self, Error> {
64-
let inner = rsa::pkcs1v15::SigningKey::<Sha256>::from_pkcs8_pem(pem)?;
65-
Ok(Self { inner })
83+
pub fn from_pem(pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
84+
// Crack open the PEM to get to the private key info
85+
let res = pem::parse(pem.as_bytes())?;
86+
if res.tag() != "PRIVATE KEY" {
87+
return Err(format!("invalid PEM tag {}", res.tag()).into());
88+
}
89+
// Parse the DER content
90+
Self::from_der(res.contents())
6691
}
6792

6893
/// to_bytes serializes a private key into a 520-byte array.
@@ -98,12 +123,12 @@ impl SecretKey {
98123
.to_vec()
99124
}
100125

101-
/// to_pem serializes a public key into a PEM string.
126+
/// to_pem serializes a private key into a PEM string.
102127
pub fn to_pem(&self) -> String {
103-
rsa::pkcs1v15::SigningKey::<Sha256>::to_pkcs8_pem(&self.inner, LineEnding::LF)
104-
.unwrap()
105-
.as_str()
106-
.to_string()
128+
pem::encode_config(
129+
&pem::Pem::new("PRIVATE KEY", self.to_der()),
130+
pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
131+
)
107132
}
108133

109134
/// public_key retrieves the public counterpart of the secret key.
@@ -153,15 +178,28 @@ impl PublicKey {
153178
}
154179

155180
/// from_der parses a DER buffer into a public key.
156-
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
181+
pub fn from_der(der: &[u8]) -> Result<Self, Box<dyn std::error::Error>> {
157182
let inner = rsa::pkcs1v15::VerifyingKey::<Sha256>::from_public_key_der(der)?;
183+
184+
// Whilst the RSA algorithm permits different exponents, every modern
185+
// system only ever uses 65537 and most also enforce this. Might as
186+
// well do the same.
187+
let key: &RsaPublicKey = inner.as_ref();
188+
if *key.e() != BigUint::from(65537u32) {
189+
return Err(Error::KeyMalformed.into());
190+
}
158191
Ok(Self { inner })
159192
}
160193

161194
/// from_pem parses a PEM string into a public key.
162-
pub fn from_pem(pem: &str) -> Result<Self, Error> {
163-
let inner = rsa::pkcs1v15::VerifyingKey::<Sha256>::from_public_key_pem(pem)?;
164-
Ok(Self { inner })
195+
pub fn from_pem(pem: &str) -> Result<Self, Box<dyn std::error::Error>> {
196+
// Crack open the PEM to get to the public key info
197+
let res = pem::parse(pem.as_bytes())?;
198+
if res.tag() != "PUBLIC KEY" {
199+
return Err(format!("invalid PEM tag {}", res.tag()).into());
200+
}
201+
// Parse the DER content
202+
Self::from_der(res.contents())
165203
}
166204

167205
/// to_bytes serializes a public key into a 264-byte array.
@@ -191,8 +229,10 @@ impl PublicKey {
191229

192230
/// to_pem serializes a public key into a PEM string.
193231
pub fn to_pem(&self) -> String {
194-
rsa::pkcs1v15::VerifyingKey::<Sha256>::to_public_key_pem(&self.inner, LineEnding::LF)
195-
.unwrap()
232+
pem::encode_config(
233+
&pem::Pem::new("PUBLIC KEY", self.to_der()),
234+
pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
235+
)
196236
}
197237

198238
/// fingerprint returns a 256bit unique identified for this key. For RSA, that

0 commit comments

Comments
 (0)