summaryrefslogtreecommitdiff
path: root/packet-detector/src/validator.rs
diff options
context:
space:
mode:
Diffstat (limited to 'packet-detector/src/validator.rs')
-rw-r--r--packet-detector/src/validator.rs64
1 files changed, 64 insertions, 0 deletions
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 @@
1//! Certificate chain validation - signature only
2
3use anyhow::{anyhow, Result};
4use rustls::pki_types::CertificateDer;
5use x509_parser::prelude::*;
6
7pub struct ValidationResult {
8 pub valid: bool,
9 pub subject: String,
10 pub issuer: String,
11 pub error: Option<String>,
12}
13
14impl ValidationResult {
15 fn fail(subject: String, issuer: String, err: impl ToString) -> Self {
16 Self { valid: false, subject, issuer, error: Some(err.to_string()) }
17 }
18}
19
20pub struct CertValidator {
21 ca_der: Vec<u8>,
22}
23
24impl CertValidator {
25 pub fn with_ca_file(path: &str) -> Result<Self> {
26 let pem = std::fs::read_to_string(path)?;
27 let der = rustls_pemfile::certs(&mut pem.as_bytes())
28 .next()
29 .ok_or_else(|| anyhow!("No cert in PEM"))??;
30 Ok(Self { ca_der: der.to_vec() })
31 }
32
33 pub fn validate(&self, chain: &[Vec<u8>]) -> ValidationResult {
34 let Some(ee_der) = chain.first() else {
35 return ValidationResult::fail(String::new(), String::new(), "Empty chain");
36 };
37
38 let (subject, issuer) = match X509Certificate::from_der(ee_der) {
39 Ok((_, c)) => (c.subject().to_string(), c.issuer().to_string()),
40 Err(e) => return ValidationResult::fail(String::new(), String::new(), format!("{e:?}")),
41 };
42
43 let ca = CertificateDer::from(self.ca_der.clone());
44 let anchor = match webpki::anchor_from_trusted_cert(&ca) {
45 Ok(a) => a,
46 Err(e) => return ValidationResult::fail(subject, issuer, format!("CA: {e:?}")),
47 };
48
49 let cert = CertificateDer::from(ee_der.clone());
50 let ee = match webpki::EndEntityCert::try_from(&cert) {
51 Ok(c) => c,
52 Err(e) => return ValidationResult::fail(subject, issuer, format!("{e:?}")),
53 };
54
55 let intermediates: Vec<_> = chain[1..].iter().map(|c| CertificateDer::from(c.clone())).collect();
56 let algos = webpki::ALL_VERIFICATION_ALGS;
57 let time = webpki::types::UnixTime::since_unix_epoch(std::time::Duration::from_secs(4102444800)); // 2100
58
59 match ee.verify_for_usage(algos, &[anchor], &intermediates, time, webpki::KeyUsage::client_auth(), None, None) {
60 Ok(_) => ValidationResult { valid: true, subject, issuer, error: None },
61 Err(e) => ValidationResult::fail(subject, issuer, format!("{e:?}")),
62 }
63 }
64}