use crate::model::pk::PemLike;
#[cfg(not(any(feature = "fips", fips)))]
use crate::error::jwt_error::JWTError;
#[cfg(not(any(feature = "fips", fips)))]
use rsa::{pkcs1::DecodeRsaPublicKey, pkcs8::DecodePublicKey, RsaPublicKey};
#[cfg(not(any(feature = "fips", fips)))]
pub fn parse_rsa_pk(public_key: String) -> Result<RsaPublicKey, JWTError> {
match parse_pem(public_key) {
Some(encoded_pem) => match RsaPublicKey::from_public_key_der(encoded_pem.contents()) {
Ok(result) => Ok(result),
Err(_) => match RsaPublicKey::from_pkcs1_der(encoded_pem.contents()) {
Ok(result) => Ok(result),
Err(_) => Err(JWTError::RsaPublicKeyParseFailed(String::from(
"Error parsing RSA public key.",
))),
},
},
None => Err(JWTError::RsaPublicKeyParseFailed(String::from(
"Error parsing RSA public key.",
))),
}
}
pub fn parse_pem(mut public_key: String) -> Option<pem::Pem> {
if !public_key.has_pkcs1_header() && !public_key.has_pkcs8_header() {
public_key.complete_to_pkcs8_pem();
}
public_key.encoded_pem()
}
#[cfg(test)]
#[cfg(not(any(feature = "fips", fips)))]
mod tests {
use super::parse_rsa_pk;
use rsa::{BigUint, RsaPublicKey};
use std::str::FromStr;
use test_case::test_case;
extern crate test_case;
const VALID_PUBLIC_KEY_PEM: &str = r#"
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==
-----END PUBLIC KEY-----
"#;
const VALID_PUBLIC_KEY_PEM_WITH_NO_NEW_LINES: &str = r#"-----BEGIN PUBLIC KEY-----MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==-----END PUBLIC KEY-----"#;
const VALID_PUBLIC_KEY_PEM_WITH_ONLY_INTERNAL_NEW_LINES: &str = r#"-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==
-----END PUBLIC KEY-----"#;
const VALID_PUBLIC_KEY_PEM_WITH_TABS: &str = r#"-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==
-----END PUBLIC KEY-----"#;
const INCOMPLETE_VALID_PUBLIC_KEY_PEM: &str = r#"
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==
"#;
const INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_NO_NEW_LINES: &str = r#"MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ=="#;
const INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_NEW_LINES_AND_TABS: &str = r#"
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ==
"#;
const INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_TABS: &str = r#" MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKUzBgXo/7PlC4XuQOTc2dFwKYHSY8ww
eXHXUChtoG/tK/GrV31I61PUD8tB3k+rZg9qG1OQGllT/T+yDM8osV0CAwEAAQ== "#;
const VALID_LONGER_PUBLIC_KEY_PEM: &str = r#"
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8+dw6EKuZ9BBi3yuW2EP
H1b26QQwG5IT7oe16kV/5bR10IqYXrMfOg9hgeR2hMm4Nx81iXaET2Piiluk+y4A
WpppndludQpzl9ngRxZhWsuJP80AelbeJBYCZsncY6pHGsVjNeqGuXyUaZ9JTG9t
FkH5sZeIGEMffkpBYlk4TQlKsc11tgG3ThVedJ4GrWZiR365UBr8U4Ddc5e/YrLG
QXYTD+rXI50d8WIHiCVYGfs4XCyp+O2pWYfXM02akp95rC28GlOXyn3/58PcLat+
uxKGW3VbpcrX/GHvB3mhF7Xg9RY54FdU47CszRNcf8/wPIs48MC7HAf60DH+YAt1
tQIDAQAB
-----END PUBLIC KEY-----
"#;
#[test_case(VALID_PUBLIC_KEY_PEM; "should_parse_pem_correctly")]
#[test_case(VALID_PUBLIC_KEY_PEM_WITH_NO_NEW_LINES; "should_parse_pem_with_no_new_lines_correctly")]
#[test_case(VALID_PUBLIC_KEY_PEM_WITH_ONLY_INTERNAL_NEW_LINES; "should_parse_pem_with_only_internal_new_lines_correctly")]
#[test_case(VALID_PUBLIC_KEY_PEM_WITH_TABS; "should_parse_pem_with_tabs_correctly")]
fn should_parse_valid_pem_correctly(pem: &str) {
let parsed_key = parse_rsa_pk(String::from(pem)).unwrap();
let n = BigUint::from_str(
"86521899508642643190219074874184953161152558718207214618598059900988711563825627\
44295420315688750661664821233322032216880673401416115713978144842695881053",
)
.unwrap();
let e = BigUint::from_str("65537").unwrap();
let pk = RsaPublicKey::new(n, e).unwrap();
assert_eq!(parsed_key, pk);
}
#[test_case(INCOMPLETE_VALID_PUBLIC_KEY_PEM; "should_parse_incomplete_pem_correctly")]
#[test_case(INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_NO_NEW_LINES; "should_parse_incomplete_pem_with_no_new_lines_correctly")]
#[test_case(INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_NEW_LINES_AND_TABS; "should_parse_incomplete_pem_with_new_lines_and_tabs_correctly")]
#[test_case(INCOMPLETE_VALID_PUBLIC_KEY_PEM_WITH_TABS; "should_parse_incomplete_pem_with_tabs_correctly")]
fn should_parse_valid_but_incomplete_pem_correctly(pem: &str) {
let parsed_key = parse_rsa_pk(String::from(pem)).unwrap();
let n = BigUint::from_str(
"86521899508642643190219074874184953161152558718207214618598059900988711563825627\
44295420315688750661664821233322032216880673401416115713978144842695881053",
)
.unwrap();
let e = BigUint::from_str("65537").unwrap();
let pk = RsaPublicKey::new(n, e).unwrap();
assert_eq!(parsed_key, pk);
}
#[test]
fn should_parse_longer_plain_text_pem_correctly() {
let parsed_key = parse_rsa_pk(String::from(VALID_LONGER_PUBLIC_KEY_PEM)).unwrap();
let n_values: &[u64] = &vec![
18073000273240618421u64,
4362643270715251719,
12730775729709502448,
16209886871622735075,
18186079585683183541,
1334854899168824023,
18439922549718220475,
12406779369733212797,
9788348723511861113,
4061170072518764889,
17393472992181754363,
8508161622339198237,
15957264752954230337,
5151758777459692416,
1539796344672970338,
5382308935132886862,
9100157887714970889,
4754026146047738655,
10694253927331753238,
1929057098617239932,
1585943035959814727,
9889848708665105956,
10942023365596109515,
11126597911582149235,
16323960563504644186,
3971952260879241059,
1108309801550793144,
8489437684621844282,
17187906520675771828,
6266452135728878099,
4723005722527338271,
17575140213690099664,
];
let n = BigUint::from_slice_native(n_values);
let e = BigUint::from(65537u32);
let pk = RsaPublicKey::new(n, e).unwrap();
assert_eq!(parsed_key, pk);
}
#[test]
fn should_fail_on_a_bad_formed_public_key() {
let result = parse_rsa_pk(String::from("i am not a valid key"));
assert!(result.is_err());
}
#[test]
fn should_fail_on_a_bad_formed_public_key_for_es256() {
let result = parse_rsa_pk(String::from("i am not a valid key"));
assert!(result.is_err());
}
#[test]
fn should_fail_on_a_bad_formed_pk_with_header_and_footer() {
let result = parse_rsa_pk(String::from(
"-----BEGIN PUBLIC KEY-----i am not a valid key content-----END PUBLIC KEY-----",
));
assert!(result.is_err());
}
#[test]
fn should_fail_on_a_bad_formed_pk_with_header_and_footer_and_b64_encoded() {
let result = parse_rsa_pk(String::from("-----BEGIN PUBLIC KEY-----aSBhbSBub3QgYSB2YWxpZCBrZXkgY29udGVudCBidXQgYmFzZTY0IGVuY29kZWQ=-----END PUBLIC KEY-----"));
assert!(result.is_err());
}
}