use crate::error::TrustError;
use der::Encode;
use x509_cert::Certificate;
pub fn build_chain_from_pool(
leaf: &Certificate,
pool: &[Certificate],
trust_anchor_subjects: &[Vec<u8>],
max_depth: Option<usize>,
) -> Result<Vec<Certificate>, TrustError> {
let max_depth = max_depth.unwrap_or(10);
let mut chain = vec![leaf.clone()];
let mut current = leaf.clone();
let mut visited: Vec<Vec<u8>> = vec![leaf
.to_der()
.map_err(|e| TrustError::CertificateParse(format!("DER encode failed: {e}")))?];
for _ in 0..max_depth {
let issuer_name_der = current.tbs_certificate.issuer.to_der().unwrap_or_default();
let subject_name_der = current.tbs_certificate.subject.to_der().unwrap_or_default();
if issuer_name_der == subject_name_der {
break;
}
if trust_anchor_subjects.iter().any(|s| *s == issuer_name_der) {
break;
}
let mut found = false;
for candidate in pool {
let candidate_der = candidate
.to_der()
.map_err(|e| TrustError::CertificateParse(format!("DER encode failed: {e}")))?;
if visited.contains(&candidate_der) {
continue; }
let candidate_subject_der = candidate
.tbs_certificate
.subject
.to_der()
.unwrap_or_default();
if candidate_subject_der == issuer_name_der {
if crate::crypto::verify::verify_certificate_signature(¤t, candidate).is_ok()
{
visited.push(candidate_der);
chain.push(candidate.clone());
current = candidate.clone();
found = true;
break;
}
}
}
if !found {
return Err(TrustError::ChainBroken {
index: chain.len() - 1,
expected_issuer: format!("{}", current.tbs_certificate.issuer),
found_subject: "no matching certificate in pool".into(),
});
}
}
Ok(chain)
}
pub fn trust_anchor_subjects(store: &super::TrustStore) -> Vec<Vec<u8>> {
store
.certificates()
.filter_map(|cert| cert.tbs_certificate.subject.to_der().ok())
.collect()
}