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