diff --git a/src/rust/cryptography-key-parsing/src/lib.rs b/src/rust/cryptography-key-parsing/src/lib.rs index 77191745c563..0f903a5a3f37 100644 --- a/src/rust/cryptography-key-parsing/src/lib.rs +++ b/src/rust/cryptography-key-parsing/src/lib.rs @@ -51,6 +51,14 @@ impl From for KeyParsingError { pub type KeyParsingResult = Result; +pub enum ParsedPrivateKey { + Pkey(openssl::pkey::PKey), +} + +pub enum ParsedPublicKey { + Pkey(openssl::pkey::PKey), +} + pub enum KeySerializationError { PasswordMustBeUtf8, Write(asn1::WriteError), diff --git a/src/rust/cryptography-key-parsing/src/pkcs8.rs b/src/rust/cryptography-key-parsing/src/pkcs8.rs index d35220d4bbba..94ae1d1caff3 100644 --- a/src/rust/cryptography-key-parsing/src/pkcs8.rs +++ b/src/rust/cryptography-key-parsing/src/pkcs8.rs @@ -10,7 +10,7 @@ use cryptography_x509::pkcs8::EncryptedPrivateKeyInfo; #[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))] use crate::MIN_DH_MODULUS_SIZE; -use crate::{ec, pbe, rsa, KeyParsingError, KeyParsingResult}; +use crate::{ec, pbe, rsa, KeyParsingError, KeyParsingResult, ParsedPrivateKey}; // RFC 5208 Section 5 #[derive(asn1::Asn1Read, asn1::Asn1Write)] @@ -44,19 +44,17 @@ pub fn mldsa_seed_from_pkey( Ok(asn1::parse_single::(pki.private_key).unwrap()) } -pub fn parse_private_key( - data: &[u8], -) -> KeyParsingResult> { +pub fn parse_private_key(data: &[u8]) -> KeyParsingResult { let k = asn1::parse_single::>(data)?; if k.version != 0 { return Err(crate::KeyParsingError::InvalidKey); } match k.algorithm.params { AlgorithmParameters::Rsa(_) | AlgorithmParameters::RsaPss(_) => { - rsa::parse_pkcs1_private_key(k.private_key) + rsa::parse_pkcs1_private_key(k.private_key).map(ParsedPrivateKey::Pkey) } AlgorithmParameters::Ec(ec_params) => { - ec::parse_pkcs1_private_key(k.private_key, Some(ec_params)) + ec::parse_pkcs1_private_key(k.private_key, Some(ec_params)).map(ParsedPrivateKey::Pkey) } AlgorithmParameters::Dsa(dsa_params) => { @@ -73,7 +71,7 @@ pub fn parse_private_key( let dsa = openssl::dsa::Dsa::from_private_components(p, q, g, dsa_private_key, pub_key)?; - Ok(openssl::pkey::PKey::from_dsa(dsa)?) + Ok(ParsedPrivateKey::Pkey(openssl::pkey::PKey::from_dsa(dsa)?)) } #[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))] @@ -81,22 +79,23 @@ pub fn parse_private_key( let p = openssl::bn::BigNum::from_slice(dh_params.p.as_bytes())?; let g = openssl::bn::BigNum::from_slice(dh_params.g.as_bytes())?; let q = Some(openssl::bn::BigNum::from_slice(dh_params.q.as_bytes())?); - parse_dh_private_key(k.private_key, p, g, q) + parse_dh_private_key(k.private_key, p, g, q).map(ParsedPrivateKey::Pkey) } #[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))] AlgorithmParameters::DhKeyAgreement(dh_params) => { let p = openssl::bn::BigNum::from_slice(dh_params.p.as_bytes())?; let g = openssl::bn::BigNum::from_slice(dh_params.g.as_bytes())?; - parse_dh_private_key(k.private_key, p, g, None) + parse_dh_private_key(k.private_key, p, g, None).map(ParsedPrivateKey::Pkey) } AlgorithmParameters::X25519 => { let key_bytes = asn1::parse_single(k.private_key)?; - Ok(openssl::pkey::PKey::private_key_from_raw_bytes( + let pkey = openssl::pkey::PKey::private_key_from_raw_bytes( key_bytes, openssl::pkey::Id::X25519, - )?) + )?; + Ok(ParsedPrivateKey::Pkey(pkey)) } #[cfg(not(any( CRYPTOGRAPHY_IS_LIBRESSL, @@ -105,17 +104,19 @@ pub fn parse_private_key( )))] AlgorithmParameters::X448 => { let key_bytes = asn1::parse_single(k.private_key)?; - Ok(openssl::pkey::PKey::private_key_from_raw_bytes( + let pkey = openssl::pkey::PKey::private_key_from_raw_bytes( key_bytes, openssl::pkey::Id::X448, - )?) + )?; + Ok(ParsedPrivateKey::Pkey(pkey)) } AlgorithmParameters::Ed25519 => { let key_bytes = asn1::parse_single(k.private_key)?; - Ok(openssl::pkey::PKey::private_key_from_raw_bytes( + let pkey = openssl::pkey::PKey::private_key_from_raw_bytes( key_bytes, openssl::pkey::Id::ED25519, - )?) + )?; + Ok(ParsedPrivateKey::Pkey(pkey)) } #[cfg(not(any( CRYPTOGRAPHY_IS_LIBRESSL, @@ -124,19 +125,22 @@ pub fn parse_private_key( )))] AlgorithmParameters::Ed448 => { let key_bytes = asn1::parse_single(k.private_key)?; - Ok(openssl::pkey::PKey::private_key_from_raw_bytes( + let pkey = openssl::pkey::PKey::private_key_from_raw_bytes( key_bytes, openssl::pkey::Id::ED448, - )?) + )?; + Ok(ParsedPrivateKey::Pkey(pkey)) } #[cfg(CRYPTOGRAPHY_IS_AWSLC)] AlgorithmParameters::MlDsa65 => { let MlDsaPrivateKey::Seed(seed) = asn1::parse_single::(k.private_key)?; - Ok(cryptography_openssl::mldsa::new_raw_private_key( - cryptography_openssl::mldsa::MlDsaVariant::MlDsa65, - &seed, - )?) + Ok(ParsedPrivateKey::Pkey( + cryptography_openssl::mldsa::new_raw_private_key( + cryptography_openssl::mldsa::MlDsaVariant::MlDsa65, + &seed, + )?, + )) } _ => Err(KeyParsingError::UnsupportedKeyType( @@ -223,7 +227,7 @@ fn pkcs5_pbe_decrypt( pub fn parse_encrypted_private_key( data: &[u8], password: Option<&[u8]>, -) -> KeyParsingResult> { +) -> KeyParsingResult { let epki = asn1::parse_single::>(data)?; let password = match password { None | Some(b"") => return Err(KeyParsingError::EncryptedKeyWithoutPassword), diff --git a/src/rust/cryptography-key-parsing/src/spki.rs b/src/rust/cryptography-key-parsing/src/spki.rs index 204d56821a53..cb03024b53e1 100644 --- a/src/rust/cryptography-key-parsing/src/spki.rs +++ b/src/rust/cryptography-key-parsing/src/spki.rs @@ -7,11 +7,9 @@ use cryptography_x509::common::{ SubjectPublicKeyInfo, }; -use crate::{KeyParsingError, KeyParsingResult, KeySerializationResult}; +use crate::{KeyParsingError, KeyParsingResult, KeySerializationResult, ParsedPublicKey}; -pub fn parse_public_key( - data: &[u8], -) -> KeyParsingResult> { +pub fn parse_public_key(data: &[u8]) -> KeyParsingResult { let k = asn1::parse_single::>(data)?; match k.algorithm.params { @@ -25,41 +23,51 @@ pub fn parse_public_key( ) .map_err(|_| KeyParsingError::InvalidKey)?; let ec_key = openssl::ec::EcKey::from_public_key(&group, &ec_point)?; - Ok(openssl::pkey::PKey::from_ec_key(ec_key)?) + let pkey = openssl::pkey::PKey::from_ec_key(ec_key)?; + Ok(ParsedPublicKey::Pkey(pkey)) } - AlgorithmParameters::Ed25519 => Ok(openssl::pkey::PKey::public_key_from_raw_bytes( - k.subject_public_key.as_bytes(), - openssl::pkey::Id::ED25519, - ) - .map_err(|_| KeyParsingError::InvalidKey)?), + AlgorithmParameters::Ed25519 => Ok(ParsedPublicKey::Pkey( + openssl::pkey::PKey::public_key_from_raw_bytes( + k.subject_public_key.as_bytes(), + openssl::pkey::Id::ED25519, + ) + .map_err(|_| KeyParsingError::InvalidKey)?, + )), #[cfg(not(any( CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC )))] - AlgorithmParameters::Ed448 => Ok(openssl::pkey::PKey::public_key_from_raw_bytes( - k.subject_public_key.as_bytes(), - openssl::pkey::Id::ED448, - ) - .map_err(|_| KeyParsingError::InvalidKey)?), - AlgorithmParameters::X25519 => Ok(openssl::pkey::PKey::public_key_from_raw_bytes( - k.subject_public_key.as_bytes(), - openssl::pkey::Id::X25519, - ) - .map_err(|_| KeyParsingError::InvalidKey)?), + AlgorithmParameters::Ed448 => Ok(ParsedPublicKey::Pkey( + openssl::pkey::PKey::public_key_from_raw_bytes( + k.subject_public_key.as_bytes(), + openssl::pkey::Id::ED448, + ) + .map_err(|_| KeyParsingError::InvalidKey)?, + )), + AlgorithmParameters::X25519 => Ok(ParsedPublicKey::Pkey( + openssl::pkey::PKey::public_key_from_raw_bytes( + k.subject_public_key.as_bytes(), + openssl::pkey::Id::X25519, + ) + .map_err(|_| KeyParsingError::InvalidKey)?, + )), #[cfg(not(any( CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC )))] - AlgorithmParameters::X448 => Ok(openssl::pkey::PKey::public_key_from_raw_bytes( - k.subject_public_key.as_bytes(), - openssl::pkey::Id::X448, - ) - .map_err(|_| KeyParsingError::InvalidKey)?), + AlgorithmParameters::X448 => Ok(ParsedPublicKey::Pkey( + openssl::pkey::PKey::public_key_from_raw_bytes( + k.subject_public_key.as_bytes(), + openssl::pkey::Id::X448, + ) + .map_err(|_| KeyParsingError::InvalidKey)?, + )), AlgorithmParameters::Rsa(_) | AlgorithmParameters::RsaPss(_) => { // RSA-PSS keys are treated the same as bare RSA keys. crate::rsa::parse_pkcs1_public_key(k.subject_public_key.as_bytes()) + .map(ParsedPublicKey::Pkey) } AlgorithmParameters::Dsa(dsa_params) => { let p = openssl::bn::BigNum::from_slice(dsa_params.p.as_bytes())?; @@ -71,7 +79,7 @@ pub fn parse_public_key( let pub_key = openssl::bn::BigNum::from_slice(pub_key_int.as_bytes())?; let dsa = openssl::dsa::Dsa::from_public_components(p, q, g, pub_key)?; - Ok(openssl::pkey::PKey::from_dsa(dsa)?) + Ok(ParsedPublicKey::Pkey(openssl::pkey::PKey::from_dsa(dsa)?)) } #[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))] AlgorithmParameters::Dh(dh_params) => { @@ -85,7 +93,7 @@ pub fn parse_public_key( let pub_key = openssl::bn::BigNum::from_slice(pub_key_int.as_bytes())?; let dh = dh.set_public_key(pub_key)?; - Ok(openssl::pkey::PKey::from_dh(dh)?) + Ok(ParsedPublicKey::Pkey(openssl::pkey::PKey::from_dh(dh)?)) } #[cfg(not(CRYPTOGRAPHY_IS_BORINGSSL))] AlgorithmParameters::DhKeyAgreement(dh_params) => { @@ -98,14 +106,16 @@ pub fn parse_public_key( let pub_key = openssl::bn::BigNum::from_slice(pub_key_int.as_bytes())?; let dh = dh.set_public_key(pub_key)?; - Ok(openssl::pkey::PKey::from_dh(dh)?) + Ok(ParsedPublicKey::Pkey(openssl::pkey::PKey::from_dh(dh)?)) } #[cfg(CRYPTOGRAPHY_IS_AWSLC)] - AlgorithmParameters::MlDsa65 => Ok(cryptography_openssl::mldsa::new_raw_public_key( - cryptography_openssl::mldsa::MlDsaVariant::MlDsa65, - k.subject_public_key.as_bytes(), - ) - .map_err(|_| KeyParsingError::InvalidKey)?), + AlgorithmParameters::MlDsa65 => Ok(ParsedPublicKey::Pkey( + cryptography_openssl::mldsa::new_raw_public_key( + cryptography_openssl::mldsa::MlDsaVariant::MlDsa65, + k.subject_public_key.as_bytes(), + ) + .map_err(|_| KeyParsingError::InvalidKey)?, + )), _ => Err(KeyParsingError::UnsupportedKeyType( k.algorithm.oid().clone(), diff --git a/src/rust/src/backend/keys.rs b/src/rust/src/backend/keys.rs index eb7c6d1218de..3b37dcaaf45f 100644 --- a/src/rust/src/backend/keys.rs +++ b/src/rust/src/backend/keys.rs @@ -33,14 +33,28 @@ pub(crate) fn load_der_private_key_bytes<'p>( password: Option<&[u8]>, unsafe_skip_rsa_key_validation: bool, ) -> CryptographyResult> { - let parsers: [fn(&[u8]) -> cryptography_key_parsing::KeyParsingResult<_>; 4] = [ + type Parser = fn( + &[u8], + ) -> cryptography_key_parsing::KeyParsingResult< + cryptography_key_parsing::ParsedPrivateKey, + >; + let parsers: [Parser; 4] = [ cryptography_key_parsing::pkcs8::parse_private_key, - |d| cryptography_key_parsing::ec::parse_pkcs1_private_key(d, None), - cryptography_key_parsing::rsa::parse_pkcs1_private_key, - cryptography_key_parsing::dsa::parse_pkcs1_private_key, + |d| { + cryptography_key_parsing::ec::parse_pkcs1_private_key(d, None) + .map(cryptography_key_parsing::ParsedPrivateKey::Pkey) + }, + |d| { + cryptography_key_parsing::rsa::parse_pkcs1_private_key(d) + .map(cryptography_key_parsing::ParsedPrivateKey::Pkey) + }, + |d| { + cryptography_key_parsing::dsa::parse_pkcs1_private_key(d) + .map(cryptography_key_parsing::ParsedPrivateKey::Pkey) + }, ]; - let pkey = parsers.iter().find_map(|parser| match parser(data) { + let parsed = parsers.iter().find_map(|parser| match parser(data) { Ok(key) => Some(Ok(key)), // Try next parser Err(cryptography_key_parsing::KeyParsingError::Parse(_)) => None, @@ -48,7 +62,7 @@ pub(crate) fn load_der_private_key_bytes<'p>( Err(e) => Some(Err(e)), }); - if let Some(Ok(pkey)) = pkey { + if let Some(Ok(parsed)) = parsed { if password.is_some() { return Err(CryptographyError::from( pyo3::exceptions::PyTypeError::new_err( @@ -56,14 +70,13 @@ pub(crate) fn load_der_private_key_bytes<'p>( ), )); } - return private_key_from_pkey(py, &pkey, unsafe_skip_rsa_key_validation); - } else if let Some(Err(e)) = pkey { + return private_key_from_parsed(py, parsed, unsafe_skip_rsa_key_validation); + } else if let Some(Err(e)) = parsed { return Err(e.into()); } - let pkey = cryptography_key_parsing::pkcs8::parse_encrypted_private_key(data, password)?; - - private_key_from_pkey(py, &pkey, unsafe_skip_rsa_key_validation) + let parsed = cryptography_key_parsing::pkcs8::parse_encrypted_private_key(data, password)?; + private_key_from_parsed(py, parsed, unsafe_skip_rsa_key_validation) } #[pyo3::pyfunction] @@ -85,15 +98,15 @@ fn load_pem_private_key<'p>( let password = password.as_ref().map(|v| v.as_bytes()); let (data, mut password_used) = cryptography_key_parsing::pem::decrypt_pem(&p, password)?; - let pkey = match p.tag() { + let parsed = match p.tag() { "PRIVATE KEY" => cryptography_key_parsing::pkcs8::parse_private_key(&data)?, - "RSA PRIVATE KEY" => cryptography_key_parsing::rsa::parse_pkcs1_private_key(&data).map_err(|e| { + "RSA PRIVATE KEY" => cryptography_key_parsing::rsa::parse_pkcs1_private_key(&data).map(cryptography_key_parsing::ParsedPrivateKey::Pkey).map_err(|e| { CryptographyError::from(e).add_note(py, "If your key is in PKCS#8 format, you must use BEGIN/END PRIVATE KEY PEM delimiters") })?, - "EC PRIVATE KEY" => cryptography_key_parsing::ec::parse_pkcs1_private_key(&data, None).map_err(|e| { + "EC PRIVATE KEY" => cryptography_key_parsing::ec::parse_pkcs1_private_key(&data, None).map(cryptography_key_parsing::ParsedPrivateKey::Pkey).map_err(|e| { CryptographyError::from(e).add_note(py, "If your key is in PKCS#8 format, you must use BEGIN/END PRIVATE KEY PEM delimiters") })?, - "DSA PRIVATE KEY" => cryptography_key_parsing::dsa::parse_pkcs1_private_key(&data).map_err(|e| { + "DSA PRIVATE KEY" => cryptography_key_parsing::dsa::parse_pkcs1_private_key(&data).map(cryptography_key_parsing::ParsedPrivateKey::Pkey).map_err(|e| { CryptographyError::from(e).add_note(py, "If your key is in PKCS#8 format, you must use BEGIN/END PRIVATE KEY PEM delimiters") })?, _ => { @@ -109,7 +122,7 @@ fn load_pem_private_key<'p>( ), )); } - private_key_from_pkey(py, &pkey, unsafe_skip_rsa_key_validation) + private_key_from_parsed(py, parsed, unsafe_skip_rsa_key_validation) } fn private_key_from_pkey<'p>( @@ -184,6 +197,29 @@ fn private_key_from_pkey<'p>( } } +fn private_key_from_parsed<'p>( + py: pyo3::Python<'p>, + parsed: cryptography_key_parsing::ParsedPrivateKey, + unsafe_skip_rsa_key_validation: bool, +) -> CryptographyResult> { + match parsed { + cryptography_key_parsing::ParsedPrivateKey::Pkey(pkey) => { + private_key_from_pkey(py, &pkey, unsafe_skip_rsa_key_validation) + } + } +} + +fn public_key_from_parsed<'p>( + py: pyo3::Python<'p>, + parsed: cryptography_key_parsing::ParsedPublicKey, +) -> CryptographyResult> { + match parsed { + cryptography_key_parsing::ParsedPublicKey::Pkey(pkey) => { + public_key_from_pkey(py, &pkey, pkey.id()) + } + } +} + #[pyo3::pyfunction] #[pyo3(signature = (data, backend=None))] fn load_der_public_key<'p>( @@ -200,7 +236,7 @@ pub(crate) fn load_der_public_key_bytes<'p>( data: &[u8], ) -> CryptographyResult> { match cryptography_key_parsing::spki::parse_public_key(data) { - Ok(pkey) => public_key_from_pkey(py, &pkey, pkey.id()), + Ok(parsed) => public_key_from_parsed(py, parsed), // It's not a (RSA/DSA/ECDSA) subjectPublicKeyInfo, but we still need // to check to see if it is a pure PKCS1 RSA public key (not embedded // in a subjectPublicKeyInfo) @@ -208,7 +244,7 @@ pub(crate) fn load_der_public_key_bytes<'p>( // Use the original error. let pkey = cryptography_key_parsing::rsa::parse_pkcs1_public_key(data).map_err(|_| e)?; - public_key_from_pkey(py, &pkey, pkey.id()) + public_key_from_parsed(py, cryptography_key_parsing::ParsedPublicKey::Pkey(pkey)) } } } @@ -222,24 +258,30 @@ fn load_pem_public_key<'p>( ) -> CryptographyResult> { let _ = backend; let p = pem::parse(data.as_bytes())?; - let pkey = match p.tag() { + let parsed = match p.tag() { "RSA PUBLIC KEY" => { // We try to parse it as a PKCS1 first since that's the PEM delimiter, and if // that fails we try to parse it as an SPKI. This is to match the permissiveness // of OpenSSL, which doesn't care about the delimiter. match cryptography_key_parsing::rsa::parse_pkcs1_public_key(p.contents()) { - Ok(pkey) => pkey, + Ok(pkey) => cryptography_key_parsing::ParsedPublicKey::Pkey(pkey), Err(err) => { - let pkey = cryptography_key_parsing::spki::parse_public_key(p.contents()) + let parsed = cryptography_key_parsing::spki::parse_public_key(p.contents()) .map_err(|_| err)?; - if pkey.id() != openssl::pkey::Id::RSA { - return Err(CryptographyError::from( - pyo3::exceptions::PyValueError::new_err( - "Incorrect PEM delimiter for key type.", - ), - )); + match parsed { + cryptography_key_parsing::ParsedPublicKey::Pkey(ref pkey) + if pkey.id() == openssl::pkey::Id::RSA => + { + parsed + } + _ => { + return Err(CryptographyError::from( + pyo3::exceptions::PyValueError::new_err( + "Incorrect PEM delimiter for key type.", + ), + )); + } } - pkey } } } @@ -248,7 +290,7 @@ fn load_pem_public_key<'p>( "Valid PEM but no BEGIN PUBLIC KEY/END PUBLIC KEY delimiters. Are you sure this is a public key?" ))), }; - public_key_from_pkey(py, &pkey, pkey.id()) + public_key_from_parsed(py, parsed) } fn public_key_from_pkey<'p>(