use super::{BasicResponse, RecordedStatus, SingleResponse, end};
use crate::crl::Reason;
use crate::error::{Error, ErrorCode, Located, Result};
use crate::extension::Extension;
use crate::oid;
use crate::temporal::{Instant, Reading};
use structured_asn1::memory::Work;
use structured_asn1::{Tag, universal};
const CUTOFF: &str = "ocspResponse.singleResponse.archiveCutoff";
const EXTENDED: &str = "ocspResponse.extendedRevoke";
pub(super) fn archive_cutoff<W: Work>(extension: &Extension<'_>, execution: &W) -> Result<Reading> {
let mut reader = extension.reader_scoped(execution).field(CUTOFF)?;
let value = reader
.expect(Tag::primitive(universal::GENERALIZED_TIME))
.field(CUTOFF)?;
end(&reader, CUTOFF)?;
crate::time::read_generalized_in(&reader, &value, CUTOFF)
}
pub(super) fn extended_revoke<W: Work>(extension: &Extension<'_>, execution: &W) -> Result<()> {
if extension.is_critical() {
return Err(Error::new(
ErrorCode::MalformedValue,
EXTENDED,
extension.span().start,
));
}
let mut reader = extension.reader_scoped(execution).field(EXTENDED)?;
let value = reader
.expect(Tag::primitive(universal::NULL))
.field(EXTENDED)?;
reader.null(&value).field(EXTENDED)?;
end(&reader, EXTENDED)
}
pub(super) fn misplaced(extension: &Extension<'_>) -> Error {
Error::new(
ErrorCode::MalformedValue,
"ocspResponse.singleResponse.extendedRevoke",
extension.span().start,
)
}
pub(super) fn not_issued<W: Work>(assertion: &SingleResponse<'_>, execution: &W) -> Result<bool> {
let RecordedStatus::Revoked {
time,
reason: Some(Reason::CertificateHold),
..
} = assertion.status
else {
return Ok(false);
};
if time.instant() != Instant::whole(0) {
return Ok(false);
}
for kind in [
oid::OCSP_CRL,
oid::CRL_REASON,
oid::INVALIDITY_DATE,
oid::CERTIFICATE_ISSUER,
] {
if assertion.extensions.get(kind, execution)?.is_some() {
return Ok(false);
}
}
Ok(true)
}
impl BasicResponse<'_> {
pub fn extended_revoke<W: Work>(&self, execution: &W) -> Result<bool> {
match self.extensions.get(oid::OCSP_EXTENDED_REVOKE, execution)? {
Some(extension) => extended_revoke(extension, execution).map(|()| true),
None => Ok(false),
}
}
pub fn status_of<W: Work>(&self, index: usize, execution: &W) -> Result<Option<super::Status>> {
let Some(assertion) = self.responses.get(index) else {
return Ok(None);
};
if let Some(extension) = assertion
.extensions
.get(oid::OCSP_EXTENDED_REVOKE, execution)?
{
return Err(misplaced(extension));
}
let extended = self.extended_revoke(execution)?;
status(assertion, extended, execution).map(Some)
}
}
pub(super) fn status<W: Work>(
assertion: &SingleResponse<'_>,
extended: bool,
execution: &W,
) -> Result<super::Status> {
if extended && not_issued(assertion, execution)? {
return Ok(super::Status::NotIssued);
}
Ok(assertion.status())
}
impl SingleResponse<'_> {
pub fn archive_cutoff<W: Work>(&self, execution: &W) -> Result<Option<Reading>> {
match self.extensions.get(oid::OCSP_ARCHIVE_CUTOFF, execution)? {
Some(extension) => archive_cutoff(extension, execution).map(Some),
None => Ok(None),
}
}
}