1use super::{CertificateId, IssuerAgreement, SigningAttributes};
4use crate::digest::{DigestBatch, DigestError, PassError};
5use crate::provider::{DigestProvider, Resolution};
6use crate::stream::RangesError;
7use crate::{Error, Octets};
8use structured_asn1::memory::{Execution, OwnedVec, Work};
9use structured_asn1::{Reader, Span};
10use structured_pkix::Certificate;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Binding {
17 Matches,
19 HashDiffers,
21 IssuerDiffers,
23 IssuerUnreadable,
25 MalformedHash,
27 MalformedAlgorithm,
29 Unsupported,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct Bindings {
36 pub v1: Option<Binding>,
38 pub v2: Option<Binding>,
40}
41
42#[derive(Debug)]
44pub enum BindingError<E> {
45 Processing(Error),
47 Callback {
49 field: &'static str,
51 span: Span,
53 source: E,
55 },
56 Digest {
58 span: Span,
60 source: DigestError<E>,
62 },
63 Pass {
65 span: Span,
67 source: PassError<RangesError, E>,
69 },
70 LostRequest(usize),
74}
75
76enum Expected<'a, W: Work> {
77 Borrowed(&'a [u8]),
78 Joined(OwnedVec<u8, W>),
79}
80
81impl<W: Work> Expected<'_, W> {
82 fn as_bytes(&self) -> &[u8] {
83 match self {
84 Self::Borrowed(bytes) => bytes,
85 Self::Joined(bytes) => bytes,
86 }
87 }
88}
89
90struct Queued<'s, 'a, W: Work> {
91 identifier: &'s CertificateId<'a>,
92 expected: Expected<'a, W>,
93 ordinal: usize,
94}
95
96enum Request<I, H> {
97 Answer(Binding),
98 Compute { identity: I, expected: H },
99}
100
101type PreparedRequest<'a, 'p, P> = Result<
102 Request<<P as DigestProvider<'a>>::Identity, Expected<'a, Execution<'p>>>,
103 BindingError<<P as DigestProvider<'a>>::Error>,
104>;
105
106impl<'a> SigningAttributes<'a> {
107 pub fn bind_certificate<P: DigestProvider<'a>>(
116 &self,
117 provider: &'a P,
118 certificate: &Certificate<'_>,
119 parent: &Reader<'a>,
120 ) -> Result<Bindings, BindingError<P::Error>> {
121 let execution = parent.execution();
122 let identifiers = [
123 self.v1().map(super::SigningCertificate::signer),
124 self.v2().map(super::SigningCertificate::signer),
125 ];
126 let mut answers = [None, None];
127 let mut queued = [None, None];
128 let mut identities = None;
129 for (index, identifier) in identifiers.iter().enumerate() {
130 let Some(identifier) = identifier else {
131 continue;
132 };
133 let at = identifier.element().span().start;
134 let field = identifier.field;
135 match prepare_request(provider, identifier, parent)? {
136 Request::Answer(answer) => answers[index] = Some(answer),
137 Request::Compute { identity, expected } => {
138 let identities = match identities.as_mut() {
139 Some(identities) => identities,
140 None => identities.insert(execution.vector()),
141 };
142 let ordinal = identities.len();
143 identities.try_push(identity).map_err(|reason| {
144 BindingError::Processing(Error::resource(reason, field, at))
145 })?;
146 queued[index] = Some(Queued {
147 identifier,
148 expected,
149 ordinal,
150 });
151 }
152 }
153 }
154 let Some(identities) = identities else {
155 return Ok(Bindings {
156 v1: answers[0],
157 v2: answers[1],
158 });
159 };
160 let source_at = certificate.span().start;
161 let batch = DigestBatch::prepare(
162 provider,
163 identities.into_owned(),
164 &execution,
165 "ess.certificate",
166 source_at,
167 )
168 .map_err(|source| BindingError::Digest {
169 span: certificate.span(),
170 source,
171 })?;
172 let mut ranges = [certificate.as_bytes()];
173 let memo = batch
174 .run(&mut ranges, &execution)
175 .map_err(|source| BindingError::Pass {
176 span: certificate.span(),
177 source,
178 })?;
179 let mut issuer = None;
180 for (index, request) in queued.into_iter().enumerate() {
181 let Some(request) = request else {
182 continue;
183 };
184 let Some(digest) = memo
185 .for_request(request.ordinal, &execution)
186 .map_err(|source| BindingError::Digest {
187 span: certificate.span(),
188 source,
189 })?
190 else {
191 return Err(BindingError::LostRequest(request.ordinal));
192 };
193 answers[index] = Some(compare_binding(
194 provider,
195 request.identifier,
196 request.expected.as_bytes(),
197 digest.prehash(),
198 certificate,
199 parent,
200 &mut issuer,
201 )?);
202 }
203 Ok(Bindings {
204 v1: answers[0],
205 v2: answers[1],
206 })
207 }
208}
209
210fn prepare_request<'a, 'p, P: DigestProvider<'a>>(
211 provider: &'a P,
212 identifier: &CertificateId<'a>,
213 parent: &'p Reader<'a>,
214) -> PreparedRequest<'a, 'p, P> {
215 crate::provider::require_instance(
216 provider,
217 identifier.field,
218 identifier.element().span().start,
219 )
220 .map_err(BindingError::Processing)?;
221 let execution = parent.execution();
222 let at = identifier.element().span().start;
223 let field = identifier.field;
224 execution
225 .charge_work(1)
226 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
227 let resolved = provider
228 .resolve_digest(identifier.digest_algorithm(), parent.encoding(), parent)
229 .map_err(|source| BindingError::Callback {
230 field,
231 span: identifier.element().span(),
232 source,
233 })?;
234 match resolved {
235 Resolution::Unsupported => Ok(Request::Answer(Binding::Unsupported)),
236 Resolution::Malformed => Ok(Request::Answer(Binding::MalformedAlgorithm)),
237 Resolution::Supported(identity) => {
238 if let Some(width) = provider.digest_width(&identity)
239 && width != identifier.hashed().len()
240 {
241 return Ok(Request::Answer(Binding::MalformedHash));
242 }
243 let expected = prepare_expected(identifier.hashed(), &execution, field, at)?;
246 Ok(Request::Compute { identity, expected })
247 }
248 }
249}
250
251fn prepare_expected<'a, W: Work, E>(
252 octets: &Octets<'a>,
253 execution: &W,
254 field: &'static str,
255 at: u64,
256) -> Result<Expected<'a, W>, BindingError<E>> {
257 if let Some(bytes) = octets.as_one() {
258 return Ok(Expected::Borrowed(bytes));
259 }
260 let mut joined = execution.vector();
261 joined
262 .try_reserve_exact(octets.len())
263 .map_err(|reason| BindingError::Processing(Error::resource(reason, field, at)))?;
264 for piece in octets.pieces() {
265 execution.charge_work(1).map_err(|reason| {
266 BindingError::Processing(Error::resource(reason, field, piece.span().start))
267 })?;
268 joined
269 .try_extend_from_slice(piece.as_bytes())
270 .map_err(|reason| {
271 BindingError::Processing(Error::resource(reason, field, piece.span().start))
272 })?;
273 }
274 Ok(Expected::Joined(joined))
275}
276
277fn compare_binding<'a, 'c, 'p, P: DigestProvider<'a>>(
278 provider: &P,
279 identifier: &CertificateId<'a>,
280 expected: &[u8],
281 digest: crate::provider::Prehash<'_, '_, P, P::Identity>,
282 certificate: &Certificate<'c>,
283 parent: &'p Reader<'_>,
284 issuer: &mut Option<structured_pkix::Ordered<'c, Execution<'p>>>,
285) -> Result<Binding, BindingError<P::Error>> {
286 if expected.len() != digest.as_bytes().len() {
289 return Ok(Binding::MalformedHash);
290 }
291 let matches = provider
292 .matches_digest(digest, expected, &parent.execution())
293 .map_err(|source| BindingError::Callback {
294 field: identifier.field,
295 span: identifier.element().span(),
296 source,
297 })?;
298 if !matches {
299 return Ok(Binding::HashDiffers);
300 }
301 match identifier
302 .matches_issuer_with(certificate, parent, issuer)
303 .map_err(BindingError::Processing)?
304 {
305 IssuerAgreement::Unstated | IssuerAgreement::Matches => Ok(Binding::Matches),
306 IssuerAgreement::Differs => Ok(Binding::IssuerDiffers),
307 IssuerAgreement::Unreadable => Ok(Binding::IssuerUnreadable),
308 }
309}