use super::{
ReferenceIdentifier, ReferenceKeyResult, ReferenceNameError, ReferenceProperty, ReferenceTime,
ReferenceTimeError, ResponderIdentifier, ResponderKeyError, matches_reference_key,
matches_reference_name, matches_reference_time,
};
use crate::{
binary::{self, BinaryXml, Location},
properties::CalendarXml,
scalar::TextXml,
xml::{Access, AccessError, Xml},
};
use structured_asn1::{Reader, memory::Execution};
use structured_pkix::ocsp::{Digest, ResponderMatch, SignedResponse};
pub struct OcspReferenceIdentity {
responder: Option<ResponderMatch>,
produced_at: ReferenceTime,
}
impl OcspReferenceIdentity {
#[must_use]
pub fn responder(&self) -> Option<ResponderMatch> {
self.responder
}
#[must_use]
pub fn produced_at(&self) -> ReferenceTime {
self.produced_at
}
}
#[derive(Debug)]
pub enum OcspReferenceError<E, PE, N, D, G> {
Access(AccessError<E, N>),
Index {
index: usize,
},
Name(ReferenceNameError<E, N>),
Key(binary::Error<E, ResponderKeyError<structured_pkix::error::CallbackError<PE>>, N, D, G>),
Time(ReferenceTimeError<E, N>),
}
pub type OcspReferenceResult<X, PE> = Result<
Option<binary::LocatedValue<OcspReferenceIdentity, X>>,
OcspReferenceError<
<X as Xml>::Error,
PE,
<X as Xml>::Id,
<X as Xml>::Document,
<X as Xml>::Generation,
>,
>;
pub fn check_ocsp_reference<X, D, PE>(
xml: &X,
references: &ReferenceProperty<X::Id, X::Document, X::Generation>,
index: usize,
signed: SignedResponse<'_, '_>,
digest: &D,
operation: &Reader<'_>,
) -> OcspReferenceResult<X, PE>
where
X: BinaryXml + TextXml + CalendarXml,
D: for<'work> Digest<Execution<'work>, Error = PE>,
{
let work = operation.execution();
let access = Access::new(xml, references.snapshot(), &work);
access
.query(references.node(), |_, _, _| Ok(()))
.map_err(OcspReferenceError::Access)?;
let Some(entry) = references.entries().get(index) else {
return Err(OcspReferenceError::Index { index });
};
let ReferenceIdentifier::Ocsp {
node, responder, ..
} = entry.identifier()
else {
return Ok(None);
};
let produced_at = matches_reference_time(
xml,
references,
index,
signed.response().produced_at(),
operation,
)
.map_err(OcspReferenceError::Time)?
.expect("original OCSP time entry");
let responder = if matches!(
produced_at,
ReferenceTime::Malformed | ReferenceTime::Unsupported
) {
None
} else {
Some(match responder {
ResponderIdentifier::Name(_) => {
match matches_reference_name(
xml,
references,
index,
signed.signer().tbs().subject(),
operation,
)
.map_err(OcspReferenceError::Name)?
{
Some(true) => ResponderMatch::Matches,
Some(false) => ResponderMatch::Differs,
None => unreachable!("original ByName entry"),
}
}
ResponderIdentifier::Key(_) => {
let result: ReferenceKeyResult<X, PE> = matches_reference_key(
xml,
references,
index,
signed.signer(),
digest,
operation,
);
result
.map_err(OcspReferenceError::Key)?
.expect("original ByKey entry")
.value
}
})
};
access
.query(node, |_, _, _| Ok(()))
.map_err(OcspReferenceError::Access)?;
Ok(Some(binary::Located {
location: Location {
snapshot: references.snapshot(),
node,
},
value: OcspReferenceIdentity {
responder,
produced_at,
},
}))
}