use c2pa_raw_crypto::OpenSslMutex;
use openssl::{
stack::Stack,
x509::{verify::X509VerifyFlags, X509StoreContext, X509},
};
use crate::crypto::cose::{CertificateTrustError, CertificateTrustPolicy, TrustAnchorType};
const CERT_NOT_YET_VALID: i32 = 9;
const CERT_HAS_EXPIRED: i32 = 10;
pub(crate) fn check_certificate_trust(
ctp: &CertificateTrustPolicy,
anchor_type: Option<TrustAnchorType>,
chain_der: &[Vec<u8>],
cert_der: &[u8],
signing_time_epoch: Option<i64>,
) -> Result<(TrustAnchorType, String), CertificateTrustError> {
let _openssl = OpenSslMutex::acquire()?;
let mut ca_outside_validity = false;
if ctp.anchor_sets_of_type(anchor_type).count() == 0 {
return Err(CertificateTrustError::CertificateNotTrusted);
}
for anchor_set in ctp.anchor_sets_of_type(anchor_type) {
let mut cert_chain = Stack::new()?;
for cert_der in chain_der {
let x509_cert = X509::from_der(cert_der)?;
cert_chain.push(x509_cert)?;
}
let cert = X509::from_der(cert_der)?;
let mut builder = openssl::x509::store::X509StoreBuilder::new()?;
builder.set_flags(X509VerifyFlags::X509_STRICT)?;
let mut verify_param = openssl::x509::verify::X509VerifyParam::new()?;
verify_param.set_flags(X509VerifyFlags::X509_STRICT)?;
verify_param.set_flags(X509VerifyFlags::PARTIAL_CHAIN)?;
if let Some(st) = signing_time_epoch {
verify_param.set_time(st);
}
builder.set_param(&verify_param)?;
for der in &anchor_set.trust_anchor_ders {
let root_cert = X509::from_der(der)?;
builder.add_cert(root_cert)?;
}
let store = builder.build();
let mut store_ctx = X509StoreContext::new()?;
if store_ctx.init(&store, cert.as_ref(), &cert_chain, |f| {
let verified = f.verify_cert()?;
let error = f.error().as_raw();
if !verified
&& f.error_depth() > 0
&& (error == CERT_NOT_YET_VALID || error == CERT_HAS_EXPIRED)
{
ca_outside_validity = true;
}
Ok(verified)
})? {
return Ok((
anchor_set.trust_anchor_type,
anchor_set.trust_anchor_uri.clone(),
));
}
}
if ca_outside_validity {
return Err(CertificateTrustError::CaCertificateOutsideValidity);
}
Err(CertificateTrustError::CertificateNotTrusted)
}