use crate::{
ext::Nonce, AsResponseBytes, CertId, CertStatus, OcspGeneralizedTime, ResponderId, Version,
};
use alloc::vec::Vec;
use const_oid::{
db::rfc6960::{ID_PKIX_OCSP_BASIC, ID_PKIX_OCSP_NONCE},
AssociatedOid,
};
use core::{default::Default, option::Option};
use der::{
asn1::{BitString, ObjectIdentifier},
Decode, Sequence,
};
use spki::AlgorithmIdentifierOwned;
use x509_cert::{certificate::Certificate, ext::Extensions};
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct BasicOcspResponse {
pub tbs_response_data: ResponseData,
pub signature_algorithm: AlgorithmIdentifierOwned,
pub signature: BitString,
#[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
pub certs: Option<Vec<Certificate>>,
}
impl BasicOcspResponse {
pub fn nonce(&self) -> Option<Nonce> {
self.tbs_response_data.nonce()
}
}
impl AssociatedOid for BasicOcspResponse {
const OID: ObjectIdentifier = ID_PKIX_OCSP_BASIC;
}
impl AsResponseBytes for BasicOcspResponse {}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct ResponseData {
#[asn1(
context_specific = "0",
default = "Default::default",
tag_mode = "EXPLICIT"
)]
pub version: Version,
pub responder_id: ResponderId,
pub produced_at: OcspGeneralizedTime,
pub responses: Vec<SingleResponse>,
#[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
pub response_extensions: Option<Extensions>,
}
impl ResponseData {
pub fn nonce(&self) -> Option<Nonce> {
match &self.response_extensions {
Some(extns) => {
let mut filter = extns.iter().filter(|e| e.extn_id == ID_PKIX_OCSP_NONCE);
match filter.next() {
Some(extn) => Nonce::from_der(extn.extn_value.as_bytes()).ok(),
None => None,
}
}
None => None,
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
#[allow(missing_docs)]
pub struct SingleResponse {
pub cert_id: CertId,
pub cert_status: CertStatus,
pub this_update: OcspGeneralizedTime,
#[asn1(context_specific = "0", optional = "true", tag_mode = "EXPLICIT")]
pub next_update: Option<OcspGeneralizedTime>,
#[asn1(context_specific = "1", optional = "true", tag_mode = "EXPLICIT")]
pub single_extensions: Option<Extensions>,
}
#[cfg(feature = "builder")]
mod builder {
use crate::{builder::Error, CertId, CertStatus, OcspGeneralizedTime, SingleResponse};
use const_oid::AssociatedOid;
use digest::Digest;
use x509_cert::{
crl::CertificateList, ext::AsExtension, name::Name, serial_number::SerialNumber,
Certificate,
};
impl SingleResponse {
pub fn new(
cert_id: CertId,
cert_status: CertStatus,
this_update: OcspGeneralizedTime,
) -> Self {
Self {
cert_id,
cert_status,
this_update,
next_update: None,
single_extensions: None,
}
}
pub fn with_this_update(mut self, this_update: OcspGeneralizedTime) -> Self {
self.this_update = this_update;
self
}
pub fn with_next_update(mut self, next_update: OcspGeneralizedTime) -> Self {
self.next_update = Some(next_update);
self
}
pub fn with_extension(mut self, ext: impl AsExtension) -> Result<Self, Error> {
let ext = ext.to_extension(&Name::default(), &[])?;
match self.single_extensions {
Some(ref mut exts) => exts.push(ext),
None => self.single_extensions = Some(alloc::vec![ext]),
}
Ok(self)
}
pub fn from_crl<D>(
issuer: &Certificate,
crl: &CertificateList,
serial_number: SerialNumber,
) -> Result<Self, Error>
where
D: Digest + AssociatedOid,
{
let cert_status = match &crl.tbs_cert_list.revoked_certificates {
Some(revoked_certs) => {
let mut filter = revoked_certs
.iter()
.filter(|rc| rc.serial_number == serial_number);
match filter.next() {
None => CertStatus::good(),
Some(rc) => CertStatus::revoked(rc),
}
}
None => CertStatus::good(),
};
let cert_id = CertId::from_issuer::<D>(issuer, serial_number)?;
let this_update = crl.tbs_cert_list.this_update.into();
let next_update = crl.tbs_cert_list.next_update.map(|t| t.into());
Ok(Self {
cert_id,
cert_status,
this_update,
next_update,
single_extensions: None,
})
}
}
}