1use crate::Error as ProcessingError;
4use crate::signer::SignerIdentifier;
5use core::cmp::Ordering;
6use core::fmt;
7use structured_asn1::Reader;
8use structured_asn1::memory::{Execution, Memory, MemoryError, OwnedVec, SortError, Work};
9use structured_pkix::{Certificate, Name, Ordered};
10
11#[derive(Debug)]
14pub enum Error {
15 Processing(ProcessingError),
17 Resource(MemoryError),
19}
20
21impl Error {
22 fn pkix(source: structured_pkix::Error) -> Self {
23 Self::Processing(ProcessingError::pkix(source))
24 }
25 fn resource(reason: MemoryError, field: &'static str, at: u64) -> Self {
26 Self::Processing(ProcessingError::resource(reason, field, at))
27 }
28 fn admission(reason: MemoryError, origin: Option<u64>) -> Self {
29 match origin {
30 Some(at) => Self::resource(reason, "signedData.certificates", at),
31 None => Self::Resource(reason),
32 }
33 }
34}
35
36impl fmt::Display for Error {
37 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 Self::Processing(source) => source.fmt(formatter),
40 Self::Resource(source) => source.fmt(formatter),
41 }
42 }
43}
44
45impl core::error::Error for Error {
46 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
47 match self {
48 Self::Processing(source) => Some(source),
49 Self::Resource(source) => Some(source),
50 }
51 }
52}
53
54pub type Result<T> = core::result::Result<T, Error>;
56
57#[derive(Debug)]
60pub enum Match<'c, 'a> {
61 None,
63 One(&'c Certificate<'a>),
65 Ambiguous,
67 Unreadable,
70}
71
72#[derive(Debug)]
74pub enum CertificateAgreement {
75 Matches,
77 Differs,
79 Unreadable(structured_pkix::Error),
81}
82
83pub fn matches_certificate_in(
90 named: &SignerIdentifier<'_>,
91 certificate: &Certificate<'_>,
92 parent: &Reader<'_>,
93) -> Result<CertificateAgreement> {
94 let execution = parent.execution();
95 let at = certificate.span().start;
96 match named {
97 SignerIdentifier::IssuerAndSerial { issuer, serial } => {
98 if bytes_order(
99 serial.as_bytes(),
100 certificate.tbs().serial().as_bytes(),
101 &execution,
102 at,
103 )? != Ordering::Equal
104 {
105 return Ok(CertificateAgreement::Differs);
106 }
107 if issuer
108 .matches_in(certificate.tbs().issuer(), parent)
109 .map_err(Error::pkix)?
110 {
111 Ok(CertificateAgreement::Matches)
112 } else {
113 Ok(CertificateAgreement::Differs)
114 }
115 }
116 SignerIdentifier::SubjectKeyIdentifier(wanted) => {
117 let Some(extension) = certificate
118 .tbs()
119 .extensions()
120 .get(structured_pkix::oid::SUBJECT_KEY_IDENTIFIER, &execution)
121 .map_err(Error::pkix)?
122 else {
123 return Ok(CertificateAgreement::Differs);
124 };
125 let identifier =
126 match structured_pkix::identifier::SubjectKeyIdentifier::read_in(extension, parent)
127 {
128 Ok(identifier) => identifier,
129 Err(error)
130 if error.is_resource()
131 || error.code() == structured_pkix::ErrorCode::ForeignReader =>
132 {
133 return Err(Error::pkix(error));
134 }
135 Err(error) => return Ok(CertificateAgreement::Unreadable(error)),
136 };
137 if segmented_matches(wanted, identifier.as_bytes(), &execution, at)? {
138 Ok(CertificateAgreement::Matches)
139 } else {
140 Ok(CertificateAgreement::Differs)
141 }
142 }
143 }
144}
145
146#[derive(Debug)]
148pub struct Unreadable<'c, 'a> {
149 certificate: &'c Certificate<'a>,
150 reason: structured_pkix::Error,
151}
152impl<'c, 'a> Unreadable<'c, 'a> {
153 #[must_use]
155 pub fn certificate(&self) -> &'c Certificate<'a> {
156 self.certificate
157 }
158 #[must_use]
160 pub fn reason(&self) -> &structured_pkix::Error {
161 &self.reason
162 }
163}
164
165struct Identifier<'a> {
166 bytes: &'a [u8],
167 index: usize,
168}
169
170pub struct Directory<'c, 'a, W: Work = Memory> {
177 certificates: OwnedVec<&'c Certificate<'a>, W>,
178 issuers: OwnedVec<Ordered<'a, W>, W>,
179 serials: OwnedVec<usize, W>,
180 identifiers: OwnedVec<Identifier<'a>, W>,
181 unreadable: OwnedVec<Unreadable<'c, 'a>, W>,
182}
183
184impl<'c, 'a> Directory<'c, 'a> {
185 pub fn of(certificates: &[&'c Certificate<'a>], execution: &Memory) -> Result<Self> {
189 let mut kept = execution.vector();
190 kept.try_extend_from_slice(certificates)
191 .map_err(Error::Resource)?;
192 build(
193 kept,
194 execution,
195 None,
196 |name| name.ordered(execution),
197 |extension| {
198 structured_pkix::identifier::SubjectKeyIdentifier::read(extension, execution)
199 },
200 )
201 }
202}
203
204impl<'c, 'a, 'p> Directory<'c, 'a, Execution<'p>> {
205 pub fn of_in(certificates: &[&'c Certificate<'a>], parent: &'p Reader<'_>) -> Result<Self>
211 where
212 'a: 'p,
213 {
214 let execution = parent.execution();
215 for certificate in certificates {
218 execution.charge_work(1).map_err(|reason| {
219 Error::resource(reason, "signedData.certificates", parent.offset())
220 })?;
221 if !certificate
222 .tbs()
223 .public_key()
224 .algorithm()
225 .same_session(parent)
226 {
227 return Err(Error::pkix(structured_pkix::Error::new(
228 structured_pkix::ErrorCode::ForeignReader,
229 "signedData.certificates",
230 certificate.span().start,
231 )));
232 }
233 }
234 let mut gathered = execution.vector();
235 gathered
236 .try_extend_from_slice(certificates)
237 .map_err(|reason| Error::admission(reason, Some(parent.offset())))?;
238 build(
239 gathered,
240 &execution,
241 Some(parent.offset()),
242 |name| name.ordered_in(parent),
243 |extension| {
244 structured_pkix::identifier::SubjectKeyIdentifier::read_in(extension, parent)
245 },
246 )
247 }
248}
249
250impl<'c, 'a, W: Work> Directory<'c, 'a, W> {
251 #[must_use]
253 pub fn len(&self) -> usize {
254 self.certificates.len()
255 }
256 #[must_use]
258 pub fn is_empty(&self) -> bool {
259 self.certificates.is_empty()
260 }
261 #[must_use]
263 pub fn unreadable_identifiers(&self) -> &[Unreadable<'c, 'a>] {
264 &self.unreadable
265 }
266
267 pub fn find(&self, named: &SignerIdentifier<'_>, execution: &Memory) -> Result<Match<'c, 'a>> {
269 self.find_with(named, execution, None, |name| name.ordered(execution))
270 }
271 pub fn find_in(
274 &self,
275 named: &SignerIdentifier<'_>,
276 parent: &Reader<'_>,
277 ) -> Result<Match<'c, 'a>> {
278 self.find_with(named, &parent.execution(), Some(parent.offset()), |name| {
279 name.ordered_in(parent)
280 })
281 }
282
283 fn find_with<'n, V: Work, E: Work>(
284 &self,
285 named: &SignerIdentifier<'n>,
286 execution: &E,
287 origin: Option<u64>,
288 prepare: impl FnOnce(&Name<'n>) -> structured_pkix::Result<Ordered<'n, V>>,
289 ) -> Result<Match<'c, 'a>> {
290 match named {
291 SignerIdentifier::IssuerAndSerial { issuer, serial } => {
292 let asked = prepare(issuer).map_err(Error::pkix)?;
293 let against = |index: usize| -> Result<Ordering> {
294 let held = self.certificates[index].tbs().serial().as_bytes();
295 let order = bytes_order(
296 held,
297 serial.as_bytes(),
298 execution,
299 self.certificates[index].span().start,
300 )?;
301 if order != Ordering::Equal {
302 return Ok(order);
303 }
304 self.issuers[index]
305 .order(&asked, execution)
306 .map_err(Error::pkix)
307 };
308 let from = lower_bound(self.serials.len(), execution, origin, |at| {
309 against(self.serials[at])
310 })?;
311 let mut first = None;
312 for &index in &self.serials[from..] {
313 if against(index)? != Ordering::Equal {
314 break;
315 }
316 if first.is_some() {
317 return Ok(Match::Ambiguous);
318 }
319 first = Some(self.certificates[index]);
320 }
321 Ok(match first {
322 Some(certificate) => Match::One(certificate),
323 None => Match::None,
324 })
325 }
326 SignerIdentifier::SubjectKeyIdentifier(wanted) => {
327 let mut first = None;
328 if let Some(bytes) = wanted.as_one() {
329 let from = lower_bound(self.identifiers.len(), execution, origin, |at| {
330 bytes_order(
331 self.identifiers[at].bytes,
332 bytes,
333 execution,
334 self.certificates[self.identifiers[at].index].span().start,
335 )
336 })?;
337 for held in &self.identifiers[from..] {
338 if bytes_order(
339 held.bytes,
340 bytes,
341 execution,
342 self.certificates[held.index].span().start,
343 )? != Ordering::Equal
344 {
345 break;
346 }
347 if first.is_some() {
348 return Ok(Match::Ambiguous);
349 }
350 first = Some(self.certificates[held.index]);
351 }
352 } else {
353 for held in &self.identifiers {
355 let at = self.certificates[held.index].span().start;
356 execution
357 .charge_work(1)
358 .map_err(|reason| Error::resource(reason, "signerInfo.sid", at))?;
359 if segmented_matches(wanted, held.bytes, execution, at)? {
360 if first.is_some() {
361 return Ok(Match::Ambiguous);
362 }
363 first = Some(self.certificates[held.index]);
364 }
365 }
366 }
367 if !self.unreadable.is_empty() {
368 return Ok(Match::Unreadable);
369 }
370 Ok(match first {
371 Some(certificate) => Match::One(certificate),
372 None => Match::None,
373 })
374 }
375 }
376 }
377}
378
379fn build<'c, 'a, W: Work>(
380 kept: OwnedVec<&'c Certificate<'a>, W>,
381 execution: &W,
382 origin: Option<u64>,
383 mut prepare: impl FnMut(&Name<'a>) -> structured_pkix::Result<Ordered<'a, W>>,
384 mut read_identifier: impl FnMut(
385 &structured_pkix::Extension<'a>,
386 ) -> structured_pkix::Result<
387 structured_pkix::identifier::SubjectKeyIdentifier<'a>,
388 >,
389) -> Result<Directory<'c, 'a, W>> {
390 let mut issuers = execution.vector();
391 let mut serials = execution.vector();
392 let mut identifiers = execution.vector();
393 let mut unreadable = execution.vector();
394 for (index, certificate) in kept.iter().enumerate() {
395 let at = certificate.span().start;
396 execution
397 .charge_work(1)
398 .map_err(|reason| Error::resource(reason, "signedData.certificates", at))?;
399 issuers
400 .try_push(prepare(certificate.tbs().issuer()).map_err(Error::pkix)?)
401 .map_err(|reason| Error::resource(reason, "signedData.certificates", at))?;
402 serials
403 .try_push(index)
404 .map_err(|reason| Error::resource(reason, "signedData.certificates", at))?;
405 let extension = certificate
406 .tbs()
407 .extensions()
408 .get(structured_pkix::oid::SUBJECT_KEY_IDENTIFIER, execution)
409 .map_err(Error::pkix)?;
410 if let Some(extension) = extension {
411 match read_identifier(extension) {
412 Ok(identifier) => identifiers
413 .try_push(Identifier {
414 bytes: identifier.as_bytes(),
415 index,
416 })
417 .map_err(|reason| {
418 Error::resource(
419 reason,
420 "extensions.subjectKeyIdentifier",
421 extension.span().start,
422 )
423 })?,
424 Err(reason) if reason.is_resource() => return Err(Error::pkix(reason)),
425 Err(reason) => unreadable
426 .try_push(Unreadable {
427 certificate,
428 reason,
429 })
430 .map_err(|reason| {
431 Error::resource(
432 reason,
433 "extensions.subjectKeyIdentifier",
434 extension.span().start,
435 )
436 })?,
437 }
438 }
439 }
440 serials
441 .try_sort_unstable_by(|left, right| {
442 let order = bytes_order(
443 kept[*left].tbs().serial().as_bytes(),
444 kept[*right].tbs().serial().as_bytes(),
445 execution,
446 kept[*left].span().start,
447 )?;
448 if order != Ordering::Equal {
449 return Ok(order);
450 }
451 issuers[*left]
452 .order(&issuers[*right], execution)
453 .map_err(Error::pkix)
454 })
455 .map_err(|error| sort_error(error, origin))?;
456 identifiers
457 .try_sort_unstable_by(|left, right| {
458 bytes_order(
459 left.bytes,
460 right.bytes,
461 execution,
462 kept[left.index].span().start,
463 )
464 })
465 .map_err(|error| sort_error(error, origin))?;
466 Ok(Directory {
467 certificates: kept,
468 issuers,
469 serials,
470 identifiers,
471 unreadable,
472 })
473}
474
475fn sort_error(error: SortError<Error>, origin: Option<u64>) -> Error {
476 match error {
477 SortError::Resource(reason) => Error::admission(reason, origin),
478 SortError::Comparison(error) => error,
479 }
480}
481fn bytes_order<W: Work>(left: &[u8], right: &[u8], execution: &W, at: u64) -> Result<Ordering> {
482 execution
483 .charge_work(1 + left.len().max(right.len()) as u64)
484 .map_err(|reason| Error::resource(reason, "signerInfo.sid", at))?;
485 Ok(left.cmp(right))
486}
487fn lower_bound<W: Work>(
488 length: usize,
489 execution: &W,
490 origin: Option<u64>,
491 mut compare: impl FnMut(usize) -> Result<Ordering>,
492) -> Result<usize> {
493 let mut from = 0;
494 let mut to = length;
495 while from < to {
496 execution
497 .charge_work(1)
498 .map_err(|reason| Error::admission(reason, origin))?;
499 let middle = from + (to - from) / 2;
500 if compare(middle)? == Ordering::Less {
501 from = middle + 1;
502 } else {
503 to = middle;
504 }
505 }
506 Ok(from)
507}
508fn segmented_matches<W: Work>(
509 wanted: &crate::Octets<'_>,
510 held: &[u8],
511 execution: &W,
512 at: u64,
513) -> Result<bool> {
514 execution
515 .charge_work(1)
516 .map_err(|reason| Error::resource(reason, "signerInfo.sid", at))?;
517 if wanted.len() != held.len() {
518 return Ok(false);
519 }
520 let mut offset = 0;
521 for piece in wanted.pieces() {
522 let bytes = piece.as_bytes();
523 execution
524 .charge_work(1 + bytes.len() as u64)
525 .map_err(|reason| Error::resource(reason, "signerInfo.sid", at))?;
526 if bytes != &held[offset..offset + bytes.len()] {
527 return Ok(false);
528 }
529 offset += bytes.len();
530 }
531 debug_assert_eq!(offset, held.len());
532 Ok(true)
533}