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 execution = parent.execution();
124 let identifiers = [
125 self.v1().map(super::SigningCertificate::signer),
126 self.v2().map(super::SigningCertificate::signer),
127 ];
128 let mut answers = [None, None];
129 let mut queued = [None, None];
130 let mut identities = None;
131 for (index, identifier) in identifiers.iter().enumerate() {
132 let Some(identifier) = identifier else {
133 continue;
134 };
135 let at = identifier.element().span().start;
136 let field = identifier.field;
137 match prepare_request(provider, identifier, parent)? {
138 Request::Answer(answer) => answers[index] = Some(answer),
139 Request::Compute { identity, expected } => {
140 let identities = match identities.as_mut() {
141 Some(identities) => identities,
142 None => identities.insert(execution.vector()),
143 };
144 let ordinal = identities.len();
145 identities.try_push(identity).map_err(|reason| {
146 BindingError::Processing(Error::resource(reason, field, at))
147 })?;
148 queued[index] = Some(Queued {
149 identifier,
150 expected,
151 ordinal,
152 });
153 }
154 }
155 }
156 let Some(identities) = identities else {
157 return Ok(Bindings {
158 v1: answers[0],
159 v2: answers[1],
160 });
161 };
162 let source_at = certificate.span().start;
163 let batch = DigestBatch::prepare(
164 provider,
165 identities.into_owned(),
166 &execution,
167 "ess.certificate",
168 source_at,
169 )
170 .map_err(|source| BindingError::Digest {
171 span: certificate.span(),
172 source,
173 })?;
174 let mut ranges = [certificate.as_bytes()];
175 let memo = batch
176 .run(&mut ranges, &execution)
177 .map_err(|source| BindingError::Pass {
178 span: certificate.span(),
179 source,
180 })?;
181 let mut issuer = None;
182 for (index, request) in queued.into_iter().enumerate() {
183 let Some(request) = request else {
184 continue;
185 };
186 let Some(digest) = memo
187 .for_request(request.ordinal, &execution)
188 .map_err(|source| BindingError::Digest {
189 span: certificate.span(),
190 source,
191 })?
192 else {
193 return Err(BindingError::LostRequest(request.ordinal));
194 };
195 answers[index] = Some(compare_binding(
196 provider,
197 request.identifier,
198 request.expected.as_bytes(),
199 digest.finished(),
200 certificate,
201 parent,
202 &mut issuer,
203 )?);
204 }
205 Ok(Bindings {
206 v1: answers[0],
207 v2: answers[1],
208 })
209 }
210}
211
212impl<'a> super::SigningCertificate<'a> {
213 pub fn bind_validation_certificate<P: AsnDigest<'a>>(
223 &self,
224 provider: &'a P,
225 certificate: &Certificate<'_>,
226 parent: &Reader<'a>,
227 ) -> Result<OwnedVec<Binding>, BindingError<P::Error>> {
228 let execution = parent.execution();
229 let field = self.version.field();
230 let at = self.element.span().start;
231 let refusal = |reason| BindingError::Processing(Error::resource(reason, field, at));
232 let later = &self.identifiers()[1..];
233 let mut answers = execution.vector();
234 let mut queued = execution.vector();
235 let mut identities = execution.vector();
236 answers.try_reserve_exact(later.len()).map_err(refusal)?;
237 queued.try_reserve_exact(later.len()).map_err(refusal)?;
238 for identifier in later {
239 execution.charge_work(1).map_err(refusal)?;
240 match prepare_request(provider, identifier, parent)? {
241 Request::Answer(answer) => answers.try_push(answer).map_err(refusal)?,
242 Request::Compute { identity, expected } => {
243 let ordinal = identities.len();
244 identities.try_push(identity).map_err(refusal)?;
245 queued
246 .try_push((
247 answers.len(),
248 Queued {
249 identifier,
250 expected,
251 ordinal,
252 },
253 ))
254 .map_err(refusal)?;
255 answers.try_push(Binding::Unsupported).map_err(refusal)?;
258 }
259 }
260 }
261 if !identities.is_empty() {
262 let batch =
263 DigestBatch::prepare(provider, identities.into_owned(), &execution, field, at)
264 .map_err(|source| BindingError::Digest {
265 span: certificate.span(),
266 source,
267 })?;
268 let mut ranges = [certificate.as_bytes()];
269 let memo = batch
270 .run(&mut ranges, &execution)
271 .map_err(|source| BindingError::Pass {
272 span: certificate.span(),
273 source,
274 })?;
275 let mut issuer = None;
276 for (slot, request) in queued {
277 execution.charge_work(1).map_err(refusal)?;
278 let Some(digest) =
279 memo.for_request(request.ordinal, &execution)
280 .map_err(|source| BindingError::Digest {
281 span: certificate.span(),
282 source,
283 })?
284 else {
285 return Err(BindingError::LostRequest(request.ordinal));
286 };
287 answers[slot] = compare_binding(
288 provider,
289 request.identifier,
290 request.expected.as_bytes(),
291 digest.finished(),
292 certificate,
293 parent,
294 &mut issuer,
295 )?;
296 }
297 }
298 Ok(answers.into_owned())
299 }
300}
301
302fn prepare_request<'a, 'p, P: AsnDigest<'a>>(
303 provider: &'a P,
304 identifier: &CertificateId<'a>,
305 parent: &'p Reader<'a>,
306) -> PreparedRequest<'a, 'p, P> {
307 crate::provider::require_instance(
308 provider,
309 identifier.field,
310 identifier.element().span().start,
311 )
312 .map_err(BindingError::Processing)?;
313 let execution = parent.execution();
314 let at = identifier.element().span().start;
315 let field = identifier.field;
316 execution
317 .charge_work(1)
318 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
319 let resolved = crate::provider::resolve_digest(provider, identifier.digest_algorithm(), parent)
320 .map_err(BindingError::Processing)?;
321 match resolved {
322 Resolution::Unsupported => Ok(Request::Answer(Binding::Unsupported)),
323 Resolution::Malformed => Ok(Request::Answer(Binding::MalformedAlgorithm)),
324 Resolution::Supported(identity) => {
325 if let Some(width) = provider.width(&identity)
326 && width != identifier.hashed().len()
327 {
328 return Ok(Request::Answer(Binding::MalformedHash));
329 }
330 let expected = prepare_expected(identifier.hashed(), &execution, field, at)?;
333 Ok(Request::Compute { identity, expected })
334 }
335 }
336}
337
338fn prepare_expected<'a, W: Work, E>(
339 octets: &Octets<'a>,
340 execution: &W,
341 field: &'static str,
342 at: u64,
343) -> Result<Expected<'a, W>, BindingError<E>> {
344 if let Some(bytes) = octets.as_one() {
345 return Ok(Expected::Borrowed(bytes));
346 }
347 let mut joined = execution.vector();
348 joined
349 .try_reserve_exact(octets.len())
350 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
351 for piece in octets.pieces() {
352 execution.charge_work(1).map_err(|reason| {
353 BindingError::Processing(Error::resource(reason, field, piece.span().start))
354 })?;
355 joined
356 .try_extend_from_slice(piece.as_bytes())
357 .map_err(|reason| {
358 BindingError::Processing(Error::resource(reason, field, piece.span().start))
359 })?;
360 }
361 Ok(Expected::Joined(joined))
362}
363
364fn compare_binding<'a, 'c, 'p, P: Digest<'a>>(
365 provider: &P,
366 identifier: &CertificateId<'a>,
367 expected: &[u8],
368 digest: Finished<'_, P, P::Identity, P::Output>,
369 certificate: &Certificate<'c>,
370 parent: &'p Reader<'_>,
371 issuer: &mut Option<structured_pkix::Ordered<'c, Execution<'p>>>,
372) -> Result<Binding, BindingError<P::Error>> {
373 if expected.len() != digest.output().as_ref().len() {
376 return Ok(Binding::MalformedHash);
377 }
378 let matches = provider
379 .matches(
380 digest.identity(),
381 digest.output(),
382 expected,
383 &parent.execution(),
384 )
385 .map_err(|source| BindingError::Callback {
386 field: identifier.field,
387 span: identifier.element().span(),
388 source,
389 })?;
390 if !matches {
391 return Ok(Binding::HashDiffers);
392 }
393 match identifier
394 .matches_issuer_with(certificate, parent, issuer)
395 .map_err(BindingError::Processing)?
396 {
397 IssuerAgreement::Unstated | IssuerAgreement::Matches => Ok(Binding::Matches),
398 IssuerAgreement::Differs => Ok(Binding::IssuerDiffers),
399 IssuerAgreement::Unreadable => Ok(Binding::IssuerUnreadable),
400 }
401}