use crate::cert::Certificate;
use crate::result::{PathFailure, VerificationResult};
use chrono::{DateTime, Utc};
#[derive(Debug, Clone)]
pub struct CertPath<'a> {
pub leaf: &'a Certificate,
pub intermediates: Vec<&'a Certificate>,
pub root: &'a Certificate,
}
pub fn validate_path(path: &CertPath<'_>, now: DateTime<Utc>) -> VerificationResult {
let mut checks = Vec::new();
let mut valid = true;
let chain: Vec<&Certificate> = std::iter::once(path.leaf)
.chain(path.intermediates.iter().copied())
.chain(std::iter::once(path.root))
.collect();
for cert in &chain {
if !cert.is_within_validity(now) {
if now < cert.not_before_chrono() {
checks.push(PathFailure::NotYetValid);
} else {
checks.push(PathFailure::Expired);
}
valid = false;
}
}
if chain.len() > 16 {
checks.push(PathFailure::ChainTooLong);
valid = false;
}
VerificationResult { valid, checks }
}
pub fn verify_path_signatures<F>(path: &CertPath<'_>, verifier: F) -> VerificationResult
where
F: Fn(&[u8], &[u8]) -> Result<(), String>,
{
let mut checks = Vec::new();
let mut valid = true;
let chain: Vec<&Certificate> = std::iter::once(path.leaf)
.chain(path.intermediates.iter().copied())
.chain(std::iter::once(path.root))
.collect();
for i in 0..chain.len().saturating_sub(1) {
let child = chain[i];
let parent = chain[i + 1];
match verifier(parent.public_key_bytes(), child.to_der().as_slice()) {
Ok(()) => {}
Err(_) => {
checks.push(PathFailure::SignatureInvalid);
valid = false;
}
}
}
VerificationResult { valid, checks }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_path_is_valid() {
let now = Utc::now();
let _ = now;
}
}