1use super::{
4 OcspReferenceError, OcspReferenceIdentity, ReferenceDigest, ReferenceDigestError,
5 ReferenceIdentifier, ReferenceProperty, ReferenceTarget, ReferenceTime, check_ocsp_reference,
6 compare_reference_target,
7};
8use crate::{
9 binary::{self, BinaryXml},
10 certificate::{Agreement, DigestProvider},
11 properties::CalendarXml,
12 scalar::TextXml,
13 xml::{Access, AccessError, Xml},
14};
15use structured_asn1::{
16 Reader,
17 memory::{MemoryError, Work},
18};
19use structured_pkix::{
20 algorithm::digest::AsnDigest,
21 ocsp::{ResponderMatch, ResponseBytes, SignedResponse},
22 oid,
23};
24
25#[derive(Clone, Copy)]
28pub struct OcspReferenceInput<'envelope, 'signed, 'document> {
29 pub envelope: &'envelope ResponseBytes<'document>,
31 pub signed: SignedResponse<'signed, 'document>,
33}
34
35pub struct OcspEnvelopeReference<'r, 'e, 's, 'd, N, D, G> {
38 references: &'r ReferenceProperty<N, D, G>,
39 entry: usize,
40 actual: OcspReferenceInput<'e, 's, 'd>,
41 identity: binary::Located<OcspReferenceIdentity, N, D, G>,
42 digest: Option<ReferenceDigest>,
43}
44impl<'r, 'e, 's, 'd, N, D, G> OcspEnvelopeReference<'r, 'e, 's, 'd, N, D, G> {
45 #[must_use]
47 pub fn references(&self) -> &'r ReferenceProperty<N, D, G> {
48 self.references
49 }
50 #[must_use]
52 pub fn entry(&self) -> usize {
53 self.entry
54 }
55 #[must_use]
57 pub fn actual(&self) -> OcspReferenceInput<'e, 's, 'd> {
58 self.actual
59 }
60 #[must_use]
62 pub fn identity(&self) -> &binary::Located<OcspReferenceIdentity, N, D, G> {
63 &self.identity
64 }
65 #[must_use]
69 pub fn digest(&self) -> Option<ReferenceDigest> {
70 self.digest
71 }
72
73 #[must_use]
79 pub fn identifies_response(&self) -> bool {
80 if self.identity.value.responder() != Some(ResponderMatch::Matches)
81 || self.identity.value.produced_at() != ReferenceTime::Matches
82 {
83 return false;
84 }
85 matches!(
86 self.digest,
87 Some(ReferenceDigest::Compared(Agreement::Matches) | ReferenceDigest::IdentifierOnly)
88 )
89 }
90}
91
92#[derive(Debug)]
94pub enum OcspEnvelopeError<E, KP, DP, N, D, G> {
95 Access(AccessError<E, N>),
97 Index {
99 index: usize,
101 },
102 InputMismatch {
106 node: N,
108 },
109 Resource {
111 node: N,
113 cause: MemoryError,
115 },
116 Identity(OcspReferenceError<E, KP, N, D, G>),
118 Digest(binary::Error<E, ReferenceDigestError<DP>, N, D, G>),
120}
121
122pub type OcspEnvelopeResult<'r, 'e, 's, 'd, X, KP, DP> = Result<
124 Option<
125 OcspEnvelopeReference<
126 'r,
127 'e,
128 's,
129 'd,
130 <X as Xml>::Id,
131 <X as Xml>::Document,
132 <X as Xml>::Generation,
133 >,
134 >,
135 OcspEnvelopeError<
136 <X as Xml>::Error,
137 KP,
138 DP,
139 <X as Xml>::Id,
140 <X as Xml>::Document,
141 <X as Xml>::Generation,
142 >,
143>;
144
145pub(super) fn basic_payload<'a, W: Work>(
146 envelope: &ResponseBytes<'a>,
147 work: &W,
148) -> Result<Option<&'a [u8]>, MemoryError> {
149 work.charge_work(1)?;
150 let kind = envelope.kind().as_bytes();
151 let expected_kind = oid::OCSP_BASIC.as_bytes();
152 if kind.len() != expected_kind.len() {
153 return Ok(None);
154 }
155 work.charge_work(u64::try_from(kind.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
156 if kind != expected_kind {
157 return Ok(None);
158 }
159 Ok(Some(envelope.payload()))
160}
161
162pub(super) fn envelope_contains<W: Work>(
163 envelope: &ResponseBytes<'_>,
164 authenticated: &[u8],
165 work: &W,
166) -> Result<bool, MemoryError> {
167 let Some(payload) = basic_payload(envelope, work)? else {
168 return Ok(false);
169 };
170 if payload.len() != authenticated.len() {
171 return Ok(false);
172 }
173 work.charge_work(u64::try_from(payload.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
174 Ok(payload == authenticated)
175}
176
177pub fn check_ocsp_envelope_reference<'r, 'e, 's, 'd, 'k, 'q, X, K, P, KP>(
192 xml: &X,
193 references: &'r ReferenceProperty<X::Id, X::Document, X::Generation>,
194 index: usize,
195 actual: OcspReferenceInput<'e, 's, 'd>,
196 key_digest: &'k K,
197 provider: &'q P,
198 operation: &Reader<'_>,
199) -> OcspEnvelopeResult<'r, 'e, 's, 'd, X, KP, P::Error>
200where
201 X: BinaryXml + CalendarXml + TextXml,
202 K: AsnDigest<'k, Error = KP>,
203 P: DigestProvider<'q, X>,
204{
205 let work = operation.execution();
206 let access = Access::new(xml, references.snapshot(), &work);
207 access
208 .query(references.node(), |_, _, _| Ok(()))
209 .map_err(OcspEnvelopeError::Access)?;
210 let Some(entry) = references.entries().get(index) else {
211 return Err(OcspEnvelopeError::Index { index });
212 };
213 let ReferenceIdentifier::Ocsp { node, .. } = entry.identifier() else {
214 return Ok(None);
215 };
216 let resource = |cause| OcspEnvelopeError::Resource { node, cause };
217 if !envelope_contains(actual.envelope, actual.signed.response().as_bytes(), &work)
218 .map_err(resource)?
219 {
220 return Err(OcspEnvelopeError::InputMismatch { node });
221 }
222 let identity =
223 check_ocsp_reference(xml, references, index, actual.signed, key_digest, operation)
224 .map_err(OcspEnvelopeError::Identity)?;
225 let Some(identity) = identity else {
226 return Err(OcspEnvelopeError::InputMismatch { node });
227 };
228 let digest = if identity.value.responder().is_some() {
229 Some(
230 compare_reference_target(
231 xml,
232 references,
233 index,
234 ReferenceTarget::Ocsp(actual.envelope),
235 provider,
236 &work,
237 )
238 .map_err(OcspEnvelopeError::Digest)?,
239 )
240 } else {
241 None
242 };
243 access
244 .query(node, |_, _, _| Ok(()))
245 .map_err(OcspEnvelopeError::Access)?;
246 Ok(Some(OcspEnvelopeReference {
247 references,
248 entry: index,
249 actual,
250 identity,
251 digest,
252 }))
253}