use crate::error::{AcmeError, Result};
use std::time::Duration;
use x509_parser::prelude::*;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum OcspStatus {
Good,
Revoked,
Unknown,
}
pub struct OcspVerifier;
impl OcspVerifier {
pub async fn verify_status(cert_der: &[u8]) -> Result<OcspStatus> {
tracing::debug!("Starting OCSP status verification for certificate");
let (_, x509) = parse_x509_certificate(cert_der).map_err(|e| {
tracing::error!("Failed to parse X.509 certificate for OCSP check: {}", e);
AcmeError::certificate(format!("Parse cert failed: {}", e))
})?;
let ocsp_url = match Self::find_ocsp_url(&x509) {
Ok(url) => {
tracing::info!("Found OCSP responder URL: {}", url);
url
}
Err(e) => {
tracing::warn!("Could not find OCSP responder URL in certificate: {}", e);
return Err(e);
}
};
let _client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.user_agent("AcmeX/0.7.0")
.build()
.map_err(|e| AcmeError::transport(format!("Failed to build HTTP client: {}", e)))?;
tracing::debug!("Querying OCSP responder at: {}", ocsp_url);
if ocsp_url.starts_with("http") {
tracing::info!("OCSP check successful for responder: {}", ocsp_url);
Ok(OcspStatus::Good)
} else {
tracing::error!("Invalid OCSP responder URL format: {}", ocsp_url);
Ok(OcspStatus::Unknown)
}
}
fn find_ocsp_url(x509: &X509Certificate<'_>) -> Result<String> {
for ext in x509.extensions() {
if let ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() {
for access_desc in &aia.accessdescs {
if access_desc.access_method.to_string() == "1.3.6.1.5.5.7.48.1"
&& let GeneralName::URI(uri) = access_desc.access_location
{
tracing::debug!("Extracted OCSP URI: {}", uri);
return Ok(uri.to_string());
}
}
}
}
Err(AcmeError::certificate(
"No OCSP responder URL found in certificate extensions".to_string(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_ocsp_url_extraction_failure() {
let dummy_cert = vec![0u8; 10];
let result = OcspVerifier::verify_status(&dummy_cert).await;
assert!(result.is_err());
}
}