use core::ops::Deref;
use crate::error::SamlError;
#[cfg(feature = "crypto-bergshamra")]
use crate::error::SignatureVerificationReason;
use crate::metadata::Metadata;
#[cfg(feature = "crypto-bergshamra")]
use crate::xml::XmlLimits;
use super::credentials::CertificatePem;
use super::descriptors::EntityId;
#[derive(Debug, Clone, Copy)]
pub enum MetadataTrustPolicy<'a> {
UnsignedForCompatibility,
RequireSignature {
trusted_certificates: &'a [CertificatePem],
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "crypto-bergshamra"), allow(dead_code))]
pub(super) enum AppliedMetadataTrust {
UnsignedForCompatibility,
SignedByPinnedCertificates {
signed_entity_descriptor_xml: String,
},
}
pub(super) fn metadata_entity_id<M>(metadata: &M) -> Result<&str, SamlError>
where
M: Deref<Target = Metadata>,
{
metadata
.get_entity_id()
.ok_or_else(|| SamlError::MissingMetadata("entityID".into()))
}
pub(super) fn ensure_expected_entity_id(
expected: &EntityId,
actual: &str,
) -> Result<(), SamlError> {
if expected.as_str() == actual {
return Ok(());
}
Err(SamlError::Invalid(format!(
"metadata entityID `{actual}` did not match expected `{}`",
expected.as_str()
)))
}
pub(super) fn ensure_metadata_trust<M>(
metadata: &M,
trust: MetadataTrustPolicy<'_>,
) -> Result<AppliedMetadataTrust, SamlError>
where
M: Deref<Target = Metadata>,
{
match trust {
MetadataTrustPolicy::UnsignedForCompatibility => {
Ok(AppliedMetadataTrust::UnsignedForCompatibility)
}
MetadataTrustPolicy::RequireSignature {
trusted_certificates,
} => verify_pinned_metadata_signature(metadata, trusted_certificates),
}
}
#[cfg(feature = "crypto-bergshamra")]
fn verify_pinned_metadata_signature<M>(
metadata: &M,
trusted_certificates: &[CertificatePem],
) -> Result<AppliedMetadataTrust, SamlError>
where
M: Deref<Target = Metadata>,
{
let trusted_certificates: Vec<String> = trusted_certificates
.iter()
.map(|certificate| certificate.as_str().to_string())
.collect();
let verification = metadata
.verify_signature_detailed_with_limits(&trusted_certificates, XmlLimits::default())?;
if verification.verified() {
let signed_entity_descriptor_xml = verification
.into_signed_entity_descriptor_xml()
.ok_or(SamlError::SignedReferenceMismatch)?;
return Ok(AppliedMetadataTrust::SignedByPinnedCertificates {
signed_entity_descriptor_xml,
});
}
Err(SamlError::SignatureVerification {
reason: SignatureVerificationReason::XmlSignature,
})
}
#[cfg(not(feature = "crypto-bergshamra"))]
fn verify_pinned_metadata_signature<M>(
_metadata: &M,
_trusted_certificates: &[CertificatePem],
) -> Result<AppliedMetadataTrust, SamlError>
where
M: Deref<Target = Metadata>,
{
Err(SamlError::Unsupported(
"signed metadata verification requires the crypto-bergshamra feature".into(),
))
}