/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use clubcard_crlite::{CRLiteClubcard, CRLiteKey, CRLiteStatus}; use sha2::{Digest, Sha256}; use std::env::args; use std::path::PathBuf; use std::process::ExitCode; use x509_parser::prelude::*; fn read_as_der(path: &PathBuf) -> Result, std::io::Error> { let bytes = std::fs::read(&path)?; match parse_x509_pem(&bytes) { Ok((_, pem)) => Ok(pem.contents), _ => Ok(bytes), } } fn parse_args() -> Option<(PathBuf, PathBuf, PathBuf)> { let mut args = args().map(PathBuf::from); let _name = args.next()?; Some((args.next()?, args.next()?, args.next()?)) } fn main() -> std::process::ExitCode { let Some((filter_path, issuer_cert_path, end_entity_cert_path)) = parse_args() else { eprintln!( "Usage: {} ", args().next().unwrap() ); return ExitCode::FAILURE; }; let Ok(filter_bytes) = std::fs::read(&filter_path) else { eprintln!("Could not read filter"); return ExitCode::FAILURE; }; let Ok(filter) = CRLiteClubcard::from_bytes(&filter_bytes) else { eprintln!("Could not parse filter"); return ExitCode::FAILURE; }; let Ok(issuer_bytes) = read_as_der(&issuer_cert_path) else { eprintln!("Could not read issuer certificate"); return ExitCode::FAILURE; }; let Ok((_, issuer)) = X509Certificate::from_der(&issuer_bytes) else { eprintln!("Could not parse issuer certificate"); return ExitCode::FAILURE; }; let Ok(cert_bytes) = read_as_der(&end_entity_cert_path) else { eprintln!("Could not read end-entity certificate"); return ExitCode::FAILURE; }; let Ok((_, cert)) = X509Certificate::from_der(&cert_bytes) else { eprintln!("Could not parse end-entity certificate"); return ExitCode::FAILURE; }; if cert.verify_signature(Some(issuer.public_key())).is_err() { eprintln!("Invalid signature (wrong issuer certificate?)"); return ExitCode::FAILURE; } if !cert.tbs_certificate.validity.is_valid() { eprintln!("End-entity certificate is expired"); return ExitCode::FAILURE; } let Ok(Some(sct_extension)) = cert .tbs_certificate .get_extension_unique(&x509_parser::oid_registry::OID_CT_LIST_SCT) else { eprintln!("End entity certificate has no SCTs"); return ExitCode::FAILURE; }; let ParsedExtension::SCT(scts) = sct_extension.parsed_extension() else { eprintln!("End entity certificate has no SCTs"); return ExitCode::FAILURE; }; let issuer_spki_hash: [u8; 32] = Sha256::digest(issuer.tbs_certificate.subject_pki.raw).into(); let serial = cert.tbs_certificate.raw_serial(); let key = CRLiteKey::new(&issuer_spki_hash, &serial); match filter.contains(&key, scts.iter().map(|sct| (sct.id.key_id, sct.timestamp))) { CRLiteStatus::Good => println!("Good"), CRLiteStatus::Revoked => println!("Revoked"), CRLiteStatus::NotEnrolled | CRLiteStatus::NotCovered => println!("Unknown"), }; ExitCode::SUCCESS }