// Signed-object decoding and RSA signature verification API. impl RpkiSignedObject { /// Parse a DER-encoded RPKI Signed Object (CMS ContentInfo wrapping SignedData). /// /// This performs encoding/structure parsing only. Profile constraints are enforced by /// `RpkiSignedObjectParsed::validate_profile`. pub fn parse_der(der: &[u8]) -> Result { parse_signed_object_content_info(der, der, CmsParseMode::BerCompatible) } pub fn parse_der_strict_cms( der: &[u8], ) -> Result { parse_signed_object_content_info(der, der, CmsParseMode::DerStrict) } /// Return the strict-DER CMS parse error for an object that was otherwise /// accepted through the normal BER-compatible CMS parser. /// /// Callers must only surface this as a compatibility warning after normal /// decoding and validation have succeeded; a strict parse failure alone /// does not prove that an arbitrary byte string is an RPKI signed object. pub fn strict_cms_der_error(der: &[u8]) -> Option { Self::parse_der_strict_cms(der).err() } /// Decode a DER-encoded RPKI Signed Object (CMS ContentInfo wrapping SignedData) and enforce /// the profile constraints from RFC 6488 §2-§3 and RFC 9589 §4. pub fn decode_der(der: &[u8]) -> Result { let parsed = Self::parse_der(der)?; Ok(parsed.validate_profile()?) } pub fn decode_der_with_strict_options( der: &[u8], strict_cms_der: bool, strict_name: bool, ) -> Result { let parsed = if strict_cms_der { Self::parse_der_strict_cms(der)? } else { Self::parse_der(der)? }; Ok(parsed.validate_profile_with_strict_name(strict_name)?) } /// Scheme-A naming for signature verification. pub fn verify(&self) -> Result<(), SignedObjectVerifyError> { self.verify_signature() } /// Verify the CMS signature using the embedded EE certificate public key. pub fn verify_signature(&self) -> Result<(), SignedObjectVerifyError> { let ee = &self.signed_data.certificates[0]; self.verify_signature_with_rsa_components( &ee.rsa_public_modulus, &ee.rsa_public_exponent, ) } /// Verify the CMS signature using a DER-encoded SubjectPublicKeyInfo. pub fn verify_signature_with_ee_spki_der( &self, ee_spki_der: &[u8], ) -> Result<(), SignedObjectVerifyError> { let (rem, spki) = SubjectPublicKeyInfo::from_der(ee_spki_der) .map_err(|e| SignedObjectVerifyError::EeSpkiParse(e.to_string()))?; if !rem.is_empty() { return Err(SignedObjectVerifyError::EeSpkiTrailingBytes(rem.len())); } self.verify_signature_with_ee_spki(&spki) } /// Verify the CMS signature using a parsed SubjectPublicKeyInfo. pub fn verify_signature_with_ee_spki( &self, ee_spki: &SubjectPublicKeyInfo<'_>, ) -> Result<(), SignedObjectVerifyError> { let pk = ee_spki .parsed() .map_err(|_e| SignedObjectVerifyError::UnsupportedEePublicKeyAlgorithm)?; let (n, e) = match pk { PublicKey::RSA(rsa) => { let n = strip_leading_zeros(rsa.modulus).to_vec(); let e = strip_leading_zeros(rsa.exponent).to_vec(); let _exp = rsa .try_exponent() .map_err(|_e| SignedObjectVerifyError::InvalidEeRsaExponent)?; (n, e) } _ => return Err(SignedObjectVerifyError::UnsupportedEePublicKeyAlgorithm), }; self.verify_signature_with_rsa_components(n.as_slice(), e.as_slice()) } fn verify_signature_with_rsa_components( &self, modulus: &[u8], exponent: &[u8], ) -> Result<(), SignedObjectVerifyError> { let signer = &self.signed_data.signer_infos[0]; let msg = &signer.signed_attrs_der_for_signature; let pk = ring::signature::RsaPublicKeyComponents { n: modulus, e: exponent, }; pk.verify( &ring::signature::RSA_PKCS1_2048_8192_SHA256, msg, &signer.signature, ) .map_err(|_e| SignedObjectVerifyError::InvalidSignature) } }