use crate::{Error, RevocationChecker};
use der::{Decode as _, Encode as _};
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");
const OID_SHA1: der::asn1::ObjectIdentifier =
der::asn1::ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
const OID_SHA256: der::asn1::ObjectIdentifier =
der::asn1::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
#[derive(Clone, Debug)]
pub struct OcspChecker<V> {
response_der: Vec<u8>,
now_unix: u64,
verifier: V,
}
impl<V: SignatureVerifier> OcspChecker<V> {
#[must_use]
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(Error::OcspMalformed)?;
if resp_bytes.response_type != OID_PKIX_OCSP_BASIC {
return Err(Error::OcspMalformed);
}
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 hash_oid = &single.cert_id.hash_algorithm.oid;
let name_der = issuer
.tbs_certificate
.subject
.to_der()
.map_err(Error::OcspParseError)?;
let key_raw = issuer
.tbs_certificate
.subject_public_key_info
.subject_public_key
.raw_bytes();
let expected_name_hash = hash_certid_input(hash_oid, &name_der)?;
let expected_key_hash = hash_certid_input(hash_oid, key_raw)?;
if single.cert_id.issuer_name_hash.as_bytes() != expected_name_hash.as_slice()
|| single.cert_id.issuer_key_hash.as_bytes() != expected_key_hash.as_slice()
{
return Err(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),
}
}
}
fn hash_certid_input(oid: &der::asn1::ObjectIdentifier, data: &[u8]) -> crate::Result<Vec<u8>> {
match *oid {
OID_SHA1 => {
use sha1::Digest as _;
Ok(sha1::Sha1::digest(data).to_vec())
}
OID_SHA256 => {
use sha2::Digest as _;
Ok(sha2::Sha256::digest(data).to_vec())
}
_ => Err(Error::OcspMalformed),
}
}