From 1951b063d7ec6d6e8db8a0b5074c73f887749208 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 29 Dec 2025 22:18:04 +0800 Subject: initial commit --- packet-detector/src/validator.rs | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 packet-detector/src/validator.rs (limited to 'packet-detector/src/validator.rs') diff --git a/packet-detector/src/validator.rs b/packet-detector/src/validator.rs new file mode 100644 index 0000000..92e64d7 --- /dev/null +++ b/packet-detector/src/validator.rs @@ -0,0 +1,64 @@ +//! Certificate chain validation - signature only + +use anyhow::{anyhow, Result}; +use rustls::pki_types::CertificateDer; +use x509_parser::prelude::*; + +pub struct ValidationResult { + pub valid: bool, + pub subject: String, + pub issuer: String, + pub error: Option, +} + +impl ValidationResult { + fn fail(subject: String, issuer: String, err: impl ToString) -> Self { + Self { valid: false, subject, issuer, error: Some(err.to_string()) } + } +} + +pub struct CertValidator { + ca_der: Vec, +} + +impl CertValidator { + pub fn with_ca_file(path: &str) -> Result { + let pem = std::fs::read_to_string(path)?; + let der = rustls_pemfile::certs(&mut pem.as_bytes()) + .next() + .ok_or_else(|| anyhow!("No cert in PEM"))??; + Ok(Self { ca_der: der.to_vec() }) + } + + pub fn validate(&self, chain: &[Vec]) -> ValidationResult { + let Some(ee_der) = chain.first() else { + return ValidationResult::fail(String::new(), String::new(), "Empty chain"); + }; + + let (subject, issuer) = match X509Certificate::from_der(ee_der) { + Ok((_, c)) => (c.subject().to_string(), c.issuer().to_string()), + Err(e) => return ValidationResult::fail(String::new(), String::new(), format!("{e:?}")), + }; + + let ca = CertificateDer::from(self.ca_der.clone()); + let anchor = match webpki::anchor_from_trusted_cert(&ca) { + Ok(a) => a, + Err(e) => return ValidationResult::fail(subject, issuer, format!("CA: {e:?}")), + }; + + let cert = CertificateDer::from(ee_der.clone()); + let ee = match webpki::EndEntityCert::try_from(&cert) { + Ok(c) => c, + Err(e) => return ValidationResult::fail(subject, issuer, format!("{e:?}")), + }; + + let intermediates: Vec<_> = chain[1..].iter().map(|c| CertificateDer::from(c.clone())).collect(); + let algos = webpki::ALL_VERIFICATION_ALGS; + let time = webpki::types::UnixTime::since_unix_epoch(std::time::Duration::from_secs(4102444800)); // 2100 + + match ee.verify_for_usage(algos, &[anchor], &intermediates, time, webpki::KeyUsage::client_auth(), None, None) { + Ok(_) => ValidationResult { valid: true, subject, issuer, error: None }, + Err(e) => ValidationResult::fail(subject, issuer, format!("{e:?}")), + } + } +} -- cgit v1.2.3