1use super::{CertificateId, IssuerAgreement, SigningAttributes};
4use crate::digest::{DigestBatch, DigestError, PassError};
5use crate::provider::Finished;
6use crate::stream::RangesError;
7use crate::{Error, Octets};
8use structured_asn1::digest::{Digest, Resolution};
9use structured_asn1::memory::{Execution, OwnedVec, Work};
10use structured_asn1::{Reader, Span};
11use structured_pkix::Certificate;
12use structured_pkix::algorithm::digest::AsnDigest;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum Binding {
19 Matches,
21 HashDiffers,
23 IssuerDiffers,
25 IssuerUnreadable,
27 MalformedHash,
29 MalformedAlgorithm,
31 Unsupported,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct Bindings {
38 pub v1: Option<Binding>,
40 pub v2: Option<Binding>,
42}
43
44#[derive(Debug)]
46pub enum BindingError<E> {
47 Processing(Error),
49 Callback {
51 field: &'static str,
53 span: Span,
55 source: E,
57 },
58 Digest {
60 span: Span,
62 source: DigestError<E>,
64 },
65 Pass {
67 span: Span,
69 source: PassError<RangesError, E>,
71 },
72 LostRequest(usize),
76}
77
78enum Expected<'a, W: Work> {
79 Borrowed(&'a [u8]),
80 Joined(OwnedVec<u8, W>),
81}
82
83impl<W: Work> Expected<'_, W> {
84 fn as_bytes(&self) -> &[u8] {
85 match self {
86 Self::Borrowed(bytes) => bytes,
87 Self::Joined(bytes) => bytes,
88 }
89 }
90}
91
92struct Queued<'s, 'a, W: Work> {
93 identifier: &'s CertificateId<'a>,
94 expected: Expected<'a, W>,
95 ordinal: usize,
96}
97
98enum Request<I, H> {
99 Answer(Binding),
100 Compute { identity: I, expected: H },
101}
102
103type PreparedRequest<'a, 'p, P> = Result<
104 Request<<P as Digest<'a>>::Identity, Expected<'a, Execution<'p>>>,
105 BindingError<<P as Digest<'a>>::Error>,
106>;
107
108impl<'a> SigningAttributes<'a> {
109 pub fn bind_certificate<P: AsnDigest<'a>>(
118 &self,
119 provider: &'a P,
120 certificate: &Certificate<'_>,
121 parent: &Reader<'a>,
122 ) -> Result<Bindings, BindingError<P::Error>> {
123 let current = parent.execution();
124 let execution = certificate.execution_in(¤t);
128 let identifiers = [
129 self.v1().map(super::SigningCertificate::signer),
130 self.v2().map(super::SigningCertificate::signer),
131 ];
132 let mut answers = [None, None];
133 let mut queued = [None, None];
134 let mut identities = None;
135 for (index, identifier) in identifiers.iter().enumerate() {
136 let Some(identifier) = identifier else {
137 continue;
138 };
139 let at = identifier.element().span().start;
140 let field = identifier.field;
141 match prepare_request(provider, identifier, parent)? {
142 Request::Answer(answer) => answers[index] = Some(answer),
143 Request::Compute { identity, expected } => {
144 let identities = match identities.as_mut() {
145 Some(identities) => identities,
146 None => identities.insert(execution.vector()),
147 };
148 let ordinal = identities.len();
149 identities.try_push(identity).map_err(|reason| {
150 BindingError::Processing(Error::resource(reason, field, at))
151 })?;
152 queued[index] = Some(Queued {
153 identifier,
154 expected,
155 ordinal,
156 });
157 }
158 }
159 }
160 let Some(identities) = identities else {
161 return Ok(Bindings {
162 v1: answers[0],
163 v2: answers[1],
164 });
165 };
166 let source_at = certificate.span().start;
167 let batch = DigestBatch::prepare(
168 provider,
169 identities.into_owned(),
170 &execution,
171 "ess.certificate",
172 source_at,
173 )
174 .map_err(|source| BindingError::Digest {
175 span: certificate.span(),
176 source,
177 })?;
178 let mut ranges = [certificate.as_bytes()];
179 let memo = batch
180 .run(&mut ranges, &execution)
181 .map_err(|source| BindingError::Pass {
182 span: certificate.span(),
183 source,
184 })?;
185 let mut issuer = None;
186 for (index, request) in queued.into_iter().enumerate() {
187 let Some(request) = request else {
188 continue;
189 };
190 let Some(digest) = memo
191 .for_request(request.ordinal, &execution)
192 .map_err(|source| BindingError::Digest {
193 span: certificate.span(),
194 source,
195 })?
196 else {
197 return Err(BindingError::LostRequest(request.ordinal));
198 };
199 answers[index] = Some(compare_binding(
200 provider,
201 request.identifier,
202 request.expected.as_bytes(),
203 digest.finished(),
204 certificate,
205 parent,
206 &mut issuer,
207 )?);
208 }
209 Ok(Bindings {
210 v1: answers[0],
211 v2: answers[1],
212 })
213 }
214}
215
216impl<'a> super::SigningCertificate<'a> {
217 pub fn bind_validation_certificate<P: AsnDigest<'a>>(
227 &self,
228 provider: &'a P,
229 certificate: &Certificate<'_>,
230 parent: &Reader<'a>,
231 ) -> Result<OwnedVec<Binding>, BindingError<P::Error>> {
232 let current = parent.execution();
233 let execution = certificate.execution_in(¤t);
236 let field = self.version.field();
237 let at = self.element.span().start;
238 let refusal = |reason| BindingError::Processing(Error::resource(reason, field, at));
239 let later = &self.identifiers()[1..];
240 let mut answers = execution.vector();
241 let mut queued = execution.vector();
242 let mut identities = execution.vector();
243 answers.try_reserve_exact(later.len()).map_err(refusal)?;
244 queued.try_reserve_exact(later.len()).map_err(refusal)?;
245 for identifier in later {
246 execution.charge_work(1).map_err(refusal)?;
247 match prepare_request(provider, identifier, parent)? {
248 Request::Answer(answer) => answers.try_push(answer).map_err(refusal)?,
249 Request::Compute { identity, expected } => {
250 let ordinal = identities.len();
251 identities.try_push(identity).map_err(refusal)?;
252 queued
253 .try_push((
254 answers.len(),
255 Queued {
256 identifier,
257 expected,
258 ordinal,
259 },
260 ))
261 .map_err(refusal)?;
262 answers.try_push(Binding::Unsupported).map_err(refusal)?;
265 }
266 }
267 }
268 if !identities.is_empty() {
269 let batch =
270 DigestBatch::prepare(provider, identities.into_owned(), &execution, field, at)
271 .map_err(|source| BindingError::Digest {
272 span: certificate.span(),
273 source,
274 })?;
275 let mut ranges = [certificate.as_bytes()];
276 let memo = batch
277 .run(&mut ranges, &execution)
278 .map_err(|source| BindingError::Pass {
279 span: certificate.span(),
280 source,
281 })?;
282 let mut issuer = None;
283 for (slot, request) in queued {
284 execution.charge_work(1).map_err(refusal)?;
285 let Some(digest) =
286 memo.for_request(request.ordinal, &execution)
287 .map_err(|source| BindingError::Digest {
288 span: certificate.span(),
289 source,
290 })?
291 else {
292 return Err(BindingError::LostRequest(request.ordinal));
293 };
294 answers[slot] = compare_binding(
295 provider,
296 request.identifier,
297 request.expected.as_bytes(),
298 digest.finished(),
299 certificate,
300 parent,
301 &mut issuer,
302 )?;
303 }
304 }
305 Ok(answers.into_owned())
306 }
307}
308
309fn prepare_request<'a, 'p, P: AsnDigest<'a>>(
310 provider: &'a P,
311 identifier: &CertificateId<'a>,
312 parent: &'p Reader<'a>,
313) -> PreparedRequest<'a, 'p, P> {
314 crate::provider::require_instance(
315 provider,
316 identifier.field,
317 identifier.element().span().start,
318 )
319 .map_err(BindingError::Processing)?;
320 let execution = parent.execution();
321 let at = identifier.element().span().start;
322 let field = identifier.field;
323 execution
324 .charge_work(1)
325 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
326 let resolved = crate::provider::resolve_digest(provider, identifier.digest_algorithm(), parent)
327 .map_err(BindingError::Processing)?;
328 match resolved {
329 Resolution::Unsupported => Ok(Request::Answer(Binding::Unsupported)),
330 Resolution::Malformed => Ok(Request::Answer(Binding::MalformedAlgorithm)),
331 Resolution::Supported(identity) => {
332 if let Some(width) = provider.width(&identity)
333 && width != identifier.hashed().len()
334 {
335 return Ok(Request::Answer(Binding::MalformedHash));
336 }
337 let expected = prepare_expected(identifier.hashed(), &execution, field, at)?;
340 Ok(Request::Compute { identity, expected })
341 }
342 }
343}
344
345fn prepare_expected<'a, W: Work, E>(
346 octets: &Octets<'a>,
347 execution: &W,
348 field: &'static str,
349 at: u64,
350) -> Result<Expected<'a, W>, BindingError<E>> {
351 if let Some(bytes) = octets.as_one() {
352 return Ok(Expected::Borrowed(bytes));
353 }
354 let mut joined = execution.vector();
355 joined
356 .try_reserve_exact(octets.len())
357 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
358 for piece in octets.pieces() {
359 execution.charge_work(1).map_err(|reason| {
360 BindingError::Processing(Error::resource(reason, field, piece.span().start))
361 })?;
362 joined
363 .try_extend_from_slice(piece.as_bytes())
364 .map_err(|reason| {
365 BindingError::Processing(Error::resource(reason, field, piece.span().start))
366 })?;
367 }
368 Ok(Expected::Joined(joined))
369}
370
371fn compare_binding<'a, 'c, 'p, P: Digest<'a>>(
372 provider: &P,
373 identifier: &CertificateId<'a>,
374 expected: &[u8],
375 digest: Finished<'_, P, P::Identity, P::Output>,
376 certificate: &Certificate<'c>,
377 parent: &'p Reader<'_>,
378 issuer: &mut Option<structured_pkix::Ordered<'c, Execution<'p>>>,
379) -> Result<Binding, BindingError<P::Error>> {
380 if expected.len() != digest.output().as_ref().len() {
383 return Ok(Binding::MalformedHash);
384 }
385 let matches = provider
386 .matches(
387 digest.identity(),
388 digest.output(),
389 expected,
390 &parent.execution(),
391 )
392 .map_err(|source| BindingError::Callback {
393 field: identifier.field,
394 span: identifier.element().span(),
395 source,
396 })?;
397 if !matches {
398 return Ok(Binding::HashDiffers);
399 }
400 match identifier
401 .matches_issuer_with(certificate, parent, issuer)
402 .map_err(BindingError::Processing)?
403 {
404 IssuerAgreement::Unstated | IssuerAgreement::Matches => Ok(Binding::Matches),
405 IssuerAgreement::Differs => Ok(Binding::IssuerDiffers),
406 IssuerAgreement::Unreadable => Ok(Binding::IssuerUnreadable),
407 }
408}