119 lines
4.5 KiB
Rust
119 lines
4.5 KiB
Rust
// 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<RpkiSignedObjectParsed, SignedObjectParseError> {
|
|
parse_signed_object_content_info(der, der, CmsParseMode::BerCompatible)
|
|
}
|
|
|
|
pub fn parse_der_strict_cms(
|
|
der: &[u8],
|
|
) -> Result<RpkiSignedObjectParsed, SignedObjectParseError> {
|
|
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<SignedObjectParseError> {
|
|
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<Self, SignedObjectDecodeError> {
|
|
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<Self, SignedObjectDecodeError> {
|
|
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)
|
|
}
|
|
}
|