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
212fn prepare_request<'a, 'p, P: AsnDigest<'a>>(
213 provider: &'a P,
214 identifier: &CertificateId<'a>,
215 parent: &'p Reader<'a>,
216) -> PreparedRequest<'a, 'p, P> {
217 crate::provider::require_instance(
218 provider,
219 identifier.field,
220 identifier.element().span().start,
221 )
222 .map_err(BindingError::Processing)?;
223 let execution = parent.execution();
224 let at = identifier.element().span().start;
225 let field = identifier.field;
226 execution
227 .charge_work(1)
228 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
229 let resolved = crate::provider::resolve_digest(provider, identifier.digest_algorithm(), parent)
230 .map_err(BindingError::Processing)?;
231 match resolved {
232 Resolution::Unsupported => Ok(Request::Answer(Binding::Unsupported)),
233 Resolution::Malformed => Ok(Request::Answer(Binding::MalformedAlgorithm)),
234 Resolution::Supported(identity) => {
235 if let Some(width) = provider.width(&identity)
236 && width != identifier.hashed().len()
237 {
238 return Ok(Request::Answer(Binding::MalformedHash));
239 }
240 let expected = prepare_expected(identifier.hashed(), &execution, field, at)?;
243 Ok(Request::Compute { identity, expected })
244 }
245 }
246}
247
248fn prepare_expected<'a, W: Work, E>(
249 octets: &Octets<'a>,
250 execution: &W,
251 field: &'static str,
252 at: u64,
253) -> Result<Expected<'a, W>, BindingError<E>> {
254 if let Some(bytes) = octets.as_one() {
255 return Ok(Expected::Borrowed(bytes));
256 }
257 let mut joined = execution.vector();
258 joined
259 .try_reserve_exact(octets.len())
260 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
261 for piece in octets.pieces() {
262 execution.charge_work(1).map_err(|reason| {
263 BindingError::Processing(Error::resource(reason, field, piece.span().start))
264 })?;
265 joined
266 .try_extend_from_slice(piece.as_bytes())
267 .map_err(|reason| {
268 BindingError::Processing(Error::resource(reason, field, piece.span().start))
269 })?;
270 }
271 Ok(Expected::Joined(joined))
272}
273
274fn compare_binding<'a, 'c, 'p, P: Digest<'a>>(
275 provider: &P,
276 identifier: &CertificateId<'a>,
277 expected: &[u8],
278 digest: Finished<'_, P, P::Identity, P::Output>,
279 certificate: &Certificate<'c>,
280 parent: &'p Reader<'_>,
281 issuer: &mut Option<structured_pkix::Ordered<'c, Execution<'p>>>,
282) -> Result<Binding, BindingError<P::Error>> {
283 if expected.len() != digest.output().as_ref().len() {
286 return Ok(Binding::MalformedHash);
287 }
288 let matches = provider
289 .matches(
290 digest.identity(),
291 digest.output(),
292 expected,
293 &parent.execution(),
294 )
295 .map_err(|source| BindingError::Callback {
296 field: identifier.field,
297 span: identifier.element().span(),
298 source,
299 })?;
300 if !matches {
301 return Ok(Binding::HashDiffers);
302 }
303 match identifier
304 .matches_issuer_with(certificate, parent, issuer)
305 .map_err(BindingError::Processing)?
306 {
307 IssuerAgreement::Unstated | IssuerAgreement::Matches => Ok(Binding::Matches),
308 IssuerAgreement::Differs => Ok(Binding::IssuerDiffers),
309 IssuerAgreement::Unreadable => Ok(Binding::IssuerUnreadable),
310 }
311}