1use super::super::digest::CertDigest;
4use super::{FIELD, OcspRequest, RequestEntry};
5use crate::algorithm::digest::AsnDigest;
6use crate::error::{CallbackError, CallbackResult, Error, Result};
7use crate::ocsp::{
8 CertId, CertMatch, ExtensionProcessing, Lookup, NonceBinding, RequestNonce, SignedResponse,
9};
10use structured_asn1::memory::{OwnedVec, Work};
11
12pub(super) fn equal<L: Work, R: Work>(
13 left: &[u8],
14 l: &L,
15 right: &[u8],
16 r: &R,
17 field: &'static str,
18 left_at: u64,
19 right_at: u64,
20) -> Result<bool> {
21 l.charge_work(1)
22 .map_err(|e| Error::resource(e, field, left_at))?;
23 r.charge_work(1)
24 .map_err(|e| Error::resource(e, field, right_at))?;
25 if left.len() != right.len() {
26 return Ok(false);
27 }
28 l.charge_work(left.len() as u64)
29 .map_err(|e| Error::resource(e, field, left_at))?;
30 r.charge_work(right.len() as u64)
31 .map_err(|e| Error::resource(e, field, right_at))?;
32 Ok(left == right)
33}
34
35impl CertId<'_> {
36 pub fn matches_id<'d, D: AsnDigest<'d> + ?Sized, W: Work>(
41 &self,
42 other: &CertId<'_>,
43 digest: &'d D,
44 execution: &W,
45 ) -> CallbackResult<CertMatch, D::Error> {
46 const FIELD: &str = "ocsp.certID";
47 let left_work = self.algorithm.query_in(execution);
48 let right_work = other.algorithm.query_in(execution);
49 if !equal(
50 self.serial.as_bytes(),
51 &left_work,
52 other.serial.as_bytes(),
53 &right_work,
54 FIELD,
55 self.span.start,
56 other.span.start,
57 )? {
58 return Ok(CertMatch::Differs);
59 }
60 let left = match self.digest_identity(digest, execution)? {
61 CertDigest::Ready(id) => id,
62 CertDigest::Rejected(reason) => return Ok(reason),
63 };
64 let right = match other.digest_identity(digest, execution)? {
65 CertDigest::Ready(id) => id,
66 CertDigest::Rejected(reason) => return Ok(reason),
67 };
68 execution
69 .charge_work(1)
70 .map_err(|e| Error::resource(e, FIELD, self.span.start))?;
71 if !digest
72 .compare(&left, &right, execution)
73 .map_err(CallbackError::Callback)?
74 .is_eq()
75 {
76 return Ok(CertMatch::Differs);
77 }
78 for (left, right) in [
79 (self.issuer_name_hash, other.issuer_name_hash),
80 (self.issuer_key_hash, other.issuer_key_hash),
81 ] {
82 if !equal(
83 left,
84 &left_work,
85 right,
86 &right_work,
87 FIELD,
88 self.span.start,
89 other.span.start,
90 )? {
91 return Ok(CertMatch::Differs);
92 }
93 }
94 Ok(CertMatch::Matches)
95 }
96}
97
98#[derive(Debug)]
100pub struct RequestLookup<'q, 'a, 'r, 'response> {
101 request: &'q RequestEntry<'a>,
102 lookup: Lookup<'r, 'response>,
103 extensions: ExtensionProcessing<'a>,
104}
105impl<'q, 'a, 'r, 'response> RequestLookup<'q, 'a, 'r, 'response> {
106 #[must_use]
108 pub fn request(&self) -> &'q RequestEntry<'a> {
109 self.request
110 }
111 #[must_use]
113 pub fn lookup(&self) -> Lookup<'r, 'response> {
114 self.lookup
115 }
116 #[must_use]
118 pub fn extensions(&self) -> ExtensionProcessing<'a> {
119 self.extensions
120 }
121}
122
123#[derive(Debug)]
126pub struct BoundResponse<'q, 'a, 'r, 'response> {
127 request: &'q OcspRequest<'a>,
128 response: SignedResponse<'r, 'response>,
129 nonce: NonceBinding,
130 extensions: ExtensionProcessing<'a>,
131 lookups: OwnedVec<RequestLookup<'q, 'a, 'r, 'response>>,
132}
133impl<'q, 'a, 'r, 'response> BoundResponse<'q, 'a, 'r, 'response> {
134 #[must_use]
136 pub fn request(&self) -> &'q OcspRequest<'a> {
137 self.request
138 }
139 #[must_use]
141 pub fn response(&self) -> SignedResponse<'r, 'response> {
142 self.response
143 }
144 #[must_use]
146 pub fn nonce(&self) -> NonceBinding {
147 self.nonce
148 }
149 #[must_use]
151 pub fn extensions(&self) -> ExtensionProcessing<'a> {
152 self.extensions
153 }
154 #[must_use]
156 pub fn lookups(&self) -> &[RequestLookup<'q, 'a, 'r, 'response>] {
157 &self.lookups
158 }
159}
160
161impl<'a> OcspRequest<'a> {
162 pub fn nonce<W: Work>(&self, execution: &W) -> Result<Option<RequestNonce<'a>>> {
166 let source = self.execution_in(execution);
167 let Some(extension) = self.extensions.get(crate::oid::OCSP_NONCE, &source)? else {
168 return Ok(None);
169 };
170 let bytes = super::super::nonce::read(extension, &source, "ocspRequest.nonce")?;
171 RequestNonce::from_sent(bytes).map(Some).map_err(|_| {
172 Error::new(
173 crate::ErrorCode::MalformedValue,
174 "ocspRequest.nonce",
175 extension.span().start,
176 )
177 })
178 }
179
180 pub fn check_extensions<W: Work>(&self, execution: &W) -> Result<ExtensionProcessing<'a>> {
184 process_extensions(&self.extensions, true, &self.execution_in(execution))
185 .map(|processed| processed.processing)
186 }
187
188 pub fn bind_response<'q, 'r, 'response, 'd, D: AsnDigest<'d> + ?Sized, W: Work>(
194 &'q self,
195 response: SignedResponse<'r, 'response>,
196 digest: &'d D,
197 execution: &W,
198 ) -> CallbackResult<BoundResponse<'q, 'a, 'r, 'response>, D::Error> {
199 let source = self.execution_in(execution);
200 let processed = process_extensions(&self.extensions, true, &source)?;
201 let response_nonce = response.response().nonce(execution)?;
202 let response_work = response
203 .response()
204 .signature_algorithm()
205 .query_in(execution);
206 let nonce = super::super::nonce::bind(
207 processed.nonce,
208 response_nonce,
209 (&source, "ocspRequest.nonce", self.span.start),
210 (
211 &response_work,
212 "ocspResponse.nonce",
213 response.response().span().start,
214 ),
215 )?;
216 let mut lookups = source.vector();
217 lookups
220 .try_reserve_exact(self.entries.len())
221 .map_err(|e| Error::resource(e, FIELD, self.span.start))?;
222 for request in &self.entries {
223 source
224 .charge_work(1)
225 .map_err(|e| Error::resource(e, FIELD, request.span.start))?;
226 let extensions = process_extensions(&request.extensions, false, &source)?.processing;
227 let lookup = response.find_id(&request.id, digest, execution)?;
228 lookups
229 .try_push(RequestLookup {
230 request,
231 lookup,
232 extensions,
233 })
234 .map_err(|e| Error::resource(e, FIELD, request.span.start))?;
235 }
236 Ok(BoundResponse {
237 request: self,
238 response,
239 nonce,
240 extensions: processed.processing,
241 lookups: lookups.into_owned(),
242 })
243 }
244}
245
246struct Processed<'a> {
247 processing: ExtensionProcessing<'a>,
248 nonce: Option<RequestNonce<'a>>,
249}
250
251fn process_extensions<'a, W: Work>(
252 extensions: &crate::Extensions<'a>,
253 wide: bool,
254 execution: &W,
255) -> Result<Processed<'a>> {
256 let mut processing = ExtensionProcessing::Understood;
257 let mut nonce = None;
258 for extension in extensions.iter(execution) {
259 let extension = extension?;
260 execution
261 .charge_work(10)
262 .map_err(|e| Error::resource(e, FIELD, extension.span().start))?;
263 if wide && extension.kind() == crate::oid::OCSP_NONCE {
264 let bytes = super::super::nonce::read(extension, execution, "ocspRequest.nonce")?;
265 nonce = Some(RequestNonce::from_sent(bytes).map_err(|_| {
266 Error::new(
267 crate::ErrorCode::MalformedValue,
268 "ocspRequest.nonce",
269 extension.span().start,
270 )
271 })?);
272 } else if extension.is_critical() && matches!(processing, ExtensionProcessing::Understood) {
273 processing = ExtensionProcessing::Unsupported {
274 kind: extension.kind(),
275 span: extension.span(),
276 };
277 }
278 }
279 Ok(Processed { processing, nonce })
280}