use crate::{Error, RevocationChecker};
use der::{Decode as _, Encode as _, ErrorKind, Length};
use pkix_path::SignatureVerifier;
use spki::der::referenced::OwnedToRef as _;
use x509_cert::Certificate;
use x509_ocsp::{BasicOcspResponse, CertStatus, OcspResponse, OcspResponseStatus};
const OID_PKIX_OCSP_BASIC: der::asn1::ObjectIdentifier =
der::asn1::ObjectIdentifier::new_unwrap("1.3.6.1.5.5.7.48.1.1");
fn der_failed() -> der::Error {
der::Error::new(ErrorKind::Failed, Length::ZERO)
}
pub struct OcspChecker<V> {
response_der: Vec<u8>,
now_unix: u64,
verifier: V,
}
impl<V: SignatureVerifier> OcspChecker<V> {
pub fn new(response_der: impl Into<Vec<u8>>, now_unix: u64, verifier: V) -> Self {
Self {
response_der: response_der.into(),
now_unix,
verifier,
}
}
}
impl<V: SignatureVerifier> RevocationChecker for OcspChecker<V> {
fn check_revocation(&self, cert: &Certificate, issuer: &Certificate) -> crate::Result<()> {
let resp = OcspResponse::from_der(&self.response_der).map_err(Error::OcspParseError)?;
if resp.response_status != OcspResponseStatus::Successful {
return Err(Error::OcspStatusUnknown);
}
let resp_bytes = resp
.response_bytes
.ok_or_else(|| Error::OcspParseError(der_failed()))?;
if resp_bytes.response_type != OID_PKIX_OCSP_BASIC {
return Err(Error::OcspParseError(der_failed()));
}
let basic = BasicOcspResponse::from_der(resp_bytes.response.as_bytes())
.map_err(Error::OcspParseError)?;
let tbs_bytes = basic
.tbs_response_data
.to_der()
.map_err(Error::OcspParseError)?;
self.verifier
.verify_signature(
basic.signature_algorithm.owned_to_ref(),
issuer
.tbs_certificate
.subject_public_key_info
.owned_to_ref(),
&tbs_bytes,
basic.signature.raw_bytes(),
)
.map_err(|_| Error::OcspSignatureInvalid)?;
let cert_serial = &cert.tbs_certificate.serial_number;
let single = basic
.tbs_response_data
.responses
.iter()
.find(|r| &r.cert_id.serial_number == cert_serial)
.ok_or(Error::OcspStatusUnknown)?;
let produced_at = basic
.tbs_response_data
.produced_at
.as_ref()
.to_unix_duration()
.as_secs();
if self.now_unix < produced_at {
return Err(Error::OcspStatusUnknown);
}
let this_update = single.this_update.as_ref().to_unix_duration().as_secs();
if self.now_unix < this_update {
return Err(Error::OcspStatusUnknown);
}
match &single.next_update {
Some(next_update) => {
if self.now_unix > next_update.as_ref().to_unix_duration().as_secs() {
return Err(Error::OcspStatusUnknown);
}
}
None => return Err(Error::OcspStatusUnknown),
}
match single.cert_status {
CertStatus::Good(_) => Ok(()),
CertStatus::Revoked(ref info) => Err(Error::Revoked {
serial: cert_serial.clone(),
reason_code: info.revocation_reason,
}),
CertStatus::Unknown(_) => Err(Error::OcspStatusUnknown),
}
}
}