1use super::profile::{
4 self, MappedProfileCheckError, ProfileCheckError, ProfileFailure, ProfileInput,
5};
6use super::{
7 AuthorityFacts, ComputedInput, ImprintAgreement, RawInput, RecognitionError, Request,
8 RequestAgreement, RequestFields, RequestWindow, RetainedFacts, SignatureError, Timeliness,
9 Token, TokenExtension, TokenSignature, TsaIdentity, TsaUsage,
10};
11use crate::{
12 Error, ErrorCode,
13 content_source::{ContentError, SourceMap},
14 directory, ess,
15 provider::{SignatureAnswer, SignatureProvider},
16 stream::Source,
17 verify,
18};
19use core::convert::Infallible;
20use structured_asn1::{
21 Reader,
22 memory::{Execution, OwnedVec, Work},
23 value::OwnedOid,
24};
25use structured_pkix::{
26 Certificate,
27 path::WorkingKey,
28 temporal::{Instant, LeapTable},
29};
30
31pub struct Requested<'a, 'r> {
33 pub request: &'r Request<'a>,
35 pub parent: &'r Reader<'a>,
37}
38
39pub struct CheckContext<'a, 'r> {
41 pub certificate: &'r Certificate<'a>,
43 pub certificate_parent: &'r Reader<'a>,
45 pub key: WorkingKey<'a>,
47 pub requested: Option<Requested<'a, 'r>>,
49 pub utc: &'r LeapTable<'a>,
51 pub reference: Option<Instant>,
53 pub window: Option<RequestWindow>,
55}
56
57#[derive(Debug)]
59pub enum InfoCheckError<SE, PE, RE> {
60 Nonconforming {
62 span: structured_asn1::Span,
64 },
65 Signature(SignatureError<PE>),
67 Ess(ess::BindingError<PE>),
69 Processing(Error),
71 Provenance(Error),
74 Imprint(super::ImprintError<SE, PE>),
76 RequestImprint(super::RequestImprintError<PE>),
78 RequestField(super::RequestFieldError),
80 Authority(super::AuthorityError),
82 Recognition(RecognitionError<RE>),
84}
85
86#[derive(Debug)]
88pub enum CheckError<SE, PE, RE> {
89 Nonconforming {
91 span: structured_asn1::Span,
93 },
94 Processing(Error),
96 KeyMismatch {
98 certificate: structured_asn1::Span,
100 },
101 Identifier(directory::Error),
103 RequestField(super::RequestFieldError),
105 Information(ContentError<super::InfoError<InfoCheckError<SE, PE, RE>>>),
107}
108
109#[derive(Debug)]
112pub enum MappedCheckError<SE, PE, RE> {
113 Preflight(CheckError<SE, PE, RE>),
116 Provenance(Error),
118 Execution {
120 envelope: SourceMap,
122 source: CheckError<SE, PE, RE>,
124 },
125}
126
127#[derive(Debug)]
131pub struct Evidence {
132 facts: RetainedFacts,
133 source: SourceMap,
134}
135
136#[derive(Debug)]
138pub enum EvidenceCopyError {
139 Provenance(Error),
141 Facts {
143 source: Error,
145 context: SourceMap,
147 },
148}
149impl Evidence {
150 #[expect(
154 clippy::result_large_err,
155 reason = "retain admitted source metadata with the original copy refusal; an error box would itself require unavailable capacity"
156 )]
157 pub fn try_copy_in<W: Work>(&self, work: &W) -> Result<Self, EvidenceCopyError> {
158 let context = self
159 .source
160 .try_copy_in(work)
161 .map_err(EvidenceCopyError::Provenance)?;
162 match self.facts.try_copy_in(work) {
163 Ok(facts) => Ok(Self {
164 facts,
165 source: context,
166 }),
167 Err(source) => Err(EvidenceCopyError::Facts { source, context }),
168 }
169 }
170 #[must_use]
172 pub fn facts(&self) -> &RetainedFacts {
173 &self.facts
174 }
175 #[must_use]
177 pub fn source(&self) -> &SourceMap {
178 &self.source
179 }
180}
181
182pub struct CheckedTimestamp<'a> {
184 pub signature: TokenSignature<'a>,
186 pub identifier: directory::CertificateAgreement,
188 pub ess: ess::SigningAttributes<'a>,
190 pub bindings: ess::Bindings,
192 pub authority: AuthorityFacts,
194 pub imprint: ImprintAgreement,
196 pub requested_imprint: Option<RequestAgreement>,
198 pub requested_fields: Option<RequestFields>,
200 pub carried: Option<bool>,
202 pub timeliness: Timeliness,
204 pub accuracy_error: Option<super::DeviationError>,
206 pub unknown_critical: OwnedVec<OwnedOid>,
208 evidence: Option<Evidence>,
209}
210impl CheckedTimestamp<'_> {
211 pub fn take_evidence(&mut self) -> Option<Evidence> {
216 self.evidence.take()
217 }
218 pub fn into_evidence(self) -> Option<Evidence> {
220 self.evidence
221 }
222 #[must_use]
224 pub fn evidence(&self) -> Option<&Evidence> {
225 self.evidence.as_ref()
226 }
227}
228
229struct Information {
230 authority: AuthorityFacts,
231 imprint: ImprintAgreement,
232 requested_imprint: Option<RequestAgreement>,
233 requested_fields: Option<RequestFields>,
234 timeliness: Timeliness,
235 accuracy_error: Option<super::DeviationError>,
236 unknown: OwnedVec<OwnedOid>,
237 evidence: Option<Evidence>,
238}
239#[derive(Clone, Copy)]
240enum CheckPurpose {
241 Ordinary,
242 Historical,
243}
244
245impl Information {
246 fn holds(&self, purpose: CheckPurpose) -> bool {
247 self.accuracy_error.is_none()
248 && matches!(self.authority.usage, TsaUsage::TimeStampingOnly)
249 && matches!(
250 self.authority.identity,
251 TsaIdentity::Absent | TsaIdentity::Matches
252 )
253 && self.imprint == ImprintAgreement::Matches
254 && self
255 .requested_imprint
256 .is_none_or(|answer| answer == RequestAgreement::Matches)
257 && self
258 .requested_fields
259 .is_none_or(|fields| fields.nonce != Some(false) && fields.policy != Some(false))
260 && self.unknown.is_empty()
261 && match purpose {
262 CheckPurpose::Ordinary => matches!(
263 self.timeliness,
264 Timeliness::NotRequested | Timeliness::LocalWindow | Timeliness::RequestNonce
265 ),
266 CheckPurpose::Historical => self.timeliness == Timeliness::Unverified,
270 }
271 }
272}
273
274struct Inspection<'a, 'r, P, S, R> {
275 provider: &'a P,
276 context: &'r CheckContext<'a, 'r>,
277 covered: &'r mut S,
278 recognizes: &'r mut R,
279 common_holds: bool,
280 purpose: CheckPurpose,
281}
282
283impl<P, S, R> Inspection<'_, '_, P, S, R> {
284 fn inspect<'scope, PE, RE>(
285 &mut self,
286 info: &super::TokenInfo<'scope>,
287 inside: &Reader<'scope>,
288 source: &SourceMap,
289 ) -> Result<Information, InfoCheckError<S::Error, PE, RE>>
290 where
291 P: for<'input> SignatureProvider<'input, Error = PE>,
292 S: ComputedInput<P, PE>,
293 R: FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
294 {
295 let authority = info
296 .authority(
297 self.context.certificate,
298 inside,
299 self.context.certificate_parent,
300 )
301 .map_err(InfoCheckError::Authority)?;
302 let requested_fields = self
303 .context
304 .requested
305 .as_ref()
306 .map(|request| info.matches_request_fields(request.request, inside, request.parent))
307 .transpose()
308 .map_err(InfoCheckError::RequestField)?;
309 let unknown = unrecognized(info, inside, self.recognizes)?;
310 let (imprint, requested_imprint) = match &self.context.requested {
311 Some(request) => {
312 let (imprint, agreement) = info
313 .imprint()
314 .check_requested_input(
315 request.request.imprint(),
316 self.provider,
317 (inside, request.parent),
318 self.covered,
319 )
320 .map_err(|error| match error {
321 super::RequestedContentError::Request(source) => {
322 InfoCheckError::RequestImprint(source)
323 }
324 super::RequestedContentError::Content(source) => {
325 InfoCheckError::Imprint(source)
326 }
327 })?;
328 (imprint, Some(agreement))
329 }
330 None => (
331 info.imprint()
332 .check_input(self.provider, inside, self.covered)
333 .map_err(InfoCheckError::Imprint)?,
334 None,
335 ),
336 };
337 let (accuracy, accuracy_error) = match info
338 .accuracy()
339 .map(super::AccuracyReading::deviation)
340 .transpose()
341 {
342 Ok(accuracy) => (accuracy, None),
343 Err(error) => (None, Some(error)),
344 };
345 let timeliness = super::timeliness(
346 info.gen_time(),
347 accuracy,
348 self.context.utc,
349 self.context.reference,
350 self.context.requested.is_some(),
351 requested_fields.and_then(|fields| fields.nonce),
352 self.context.window,
353 );
354 let mut information = Information {
355 authority,
356 imprint,
357 requested_imprint,
358 requested_fields,
359 timeliness,
360 accuracy_error,
361 unknown,
362 evidence: None,
363 };
364 if self.common_holds && !info.has_faults() && information.holds(self.purpose) {
365 information.evidence = Some(Evidence {
366 facts: info
367 .retain_facts(inside)
368 .map_err(InfoCheckError::Processing)?,
369 source: source
370 .try_copy_in(&inside.execution())
371 .map_err(InfoCheckError::Provenance)?,
372 });
373 }
374 Ok(information)
375 }
376}
377
378fn unrecognized<SE, PE, RE>(
379 info: &super::TokenInfo<'_>,
380 inside: &Reader<'_>,
381 recognizes: &mut impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
382) -> Result<OwnedVec<OwnedOid>, InfoCheckError<SE, PE, RE>> {
383 let scoped = inside.execution();
384 let mut unknown = scoped.vector();
385 if let Some(extensions) = info.extensions() {
386 for kind in extensions
387 .unrecognized_critical(inside, recognizes)
388 .map_err(InfoCheckError::Recognition)?
389 {
390 let owned = OwnedOid::from_oid_in(kind, &scoped).map_err(|reason| {
391 InfoCheckError::Processing(Error::resource(
392 reason,
393 "tstInfo.extensions",
394 extensions.span().start,
395 ))
396 })?;
397 unknown.try_push(owned).map_err(|reason| {
398 InfoCheckError::Processing(Error::resource(
399 reason,
400 "tstInfo.extensions",
401 extensions.span().start,
402 ))
403 })?;
404 }
405 }
406 Ok(unknown.into_owned())
407}
408
409fn common_holds(
410 signature: &TokenSignature<'_>,
411 bindings: ess::Bindings,
412 identifier: &directory::CertificateAgreement,
413 carried: Option<bool>,
414) -> bool {
415 matches!(signature, TokenSignature::Checked(checked)
416 if checked.signature() == Some(SignatureAnswer::Valid)
417 && matches!(checked.content(), verify::ContentAgreement::Matches | verify::ContentAgreement::Direct))
418 && (bindings.v1 == Some(ess::Binding::Matches)
419 || bindings.v2 == Some(ess::Binding::Matches))
420 && bindings
421 .v1
422 .is_none_or(|binding| binding == ess::Binding::Matches)
423 && bindings
424 .v2
425 .is_none_or(|binding| binding == ess::Binding::Matches)
426 && matches!(identifier, directory::CertificateAgreement::Matches)
427 && carried != Some(false)
428}
429
430impl<'doc> Token<'doc> {
431 #[expect(
441 clippy::result_large_err,
442 reason = "preserve original cause and accounted source context without allocating on refusal"
443 )]
444 pub fn check<'a, P, S: Source, PE, RE>(
445 &'a self,
446 provider: &'a P,
447 parent: &Reader<'a>,
448 context: &CheckContext<'a, '_>,
449 covered: &mut S,
450 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
451 ) -> Result<CheckedTimestamp<'a>, CheckError<S::Error, PE, RE>>
452 where
453 'doc: 'a,
454 P: for<'scope> SignatureProvider<'scope, Error = PE>,
455 {
456 self.check_with_profile(provider, parent, context, covered, recognizes, |_| {
457 Ok::<(), ProfileFailure<Infallible>>(())
458 })
459 .map_err(ProfileCheckError::into_common)
460 }
461
462 #[expect(
470 clippy::result_large_err,
471 reason = "keep opaque common/profile refusal and its source context without allocating while returning an error"
472 )]
473 pub fn check_with_profile<'a, P, S: Source, PE, RE, FE>(
474 &'a self,
475 provider: &'a P,
476 parent: &Reader<'a>,
477 context: &CheckContext<'a, '_>,
478 covered: &mut S,
479 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
480 profile: impl for<'view, 'scope> FnOnce(
481 ProfileInput<'view, 'doc, 'a, 'scope>,
482 ) -> Result<(), ProfileFailure<FE>>,
483 ) -> Result<CheckedTimestamp<'a>, ProfileCheckError<S::Error, PE, RE, FE>>
484 where
485 'doc: 'a,
486 P: for<'scope> SignatureProvider<'scope, Error = PE>,
487 {
488 self.check_input_with_profile(
489 provider,
490 parent,
491 context,
492 &mut RawInput::new(covered),
493 recognizes,
494 profile,
495 )
496 }
497
498 #[expect(
503 clippy::result_large_err,
504 reason = "retain opaque input/provider/profile failures and original source maps"
505 )]
506 pub fn check_input_with_profile<'a, P, I, PE, RE, FE>(
507 &'a self,
508 provider: &'a P,
509 parent: &Reader<'a>,
510 context: &CheckContext<'a, '_>,
511 covered: &mut I,
512 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
513 profile: impl for<'view, 'scope> FnOnce(
514 ProfileInput<'view, 'doc, 'a, 'scope>,
515 ) -> Result<(), ProfileFailure<FE>>,
516 ) -> Result<CheckedTimestamp<'a>, ProfileCheckError<I::Error, PE, RE, FE>>
517 where
518 'doc: 'a,
519 P: for<'scope> SignatureProvider<'scope, Error = PE>,
520 I: ComputedInput<P, PE>,
521 {
522 self.check_in(
523 provider,
524 (parent, None),
525 (context, CheckPurpose::Ordinary),
526 covered,
527 recognizes,
528 profile,
529 )
530 }
531
532 #[expect(
539 clippy::result_large_err,
540 reason = "historical checking preserves the same original mapped input and profile causes as the complete token check, without allocating on refusal"
541 )]
542 pub fn check_historical_input_with_profile<'a, P, I, PE, RE, FE>(
543 &'a self,
544 provider: &'a P,
545 parent: &Reader<'a>,
546 context: &CheckContext<'a, '_>,
547 covered: &mut I,
548 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
549 profile: impl for<'view, 'scope> FnOnce(
550 ProfileInput<'view, 'doc, 'a, 'scope>,
551 ) -> Result<(), ProfileFailure<FE>>,
552 ) -> Result<CheckedTimestamp<'a>, ProfileCheckError<I::Error, PE, RE, FE>>
553 where
554 'doc: 'a,
555 P: for<'scope> SignatureProvider<'scope, Error = PE>,
556 I: ComputedInput<P, PE>,
557 {
558 if context.requested.is_some() || context.reference.is_some() || context.window.is_some() {
559 return Err(CheckError::Processing(Error::schema(
560 ErrorCode::InvalidLocation,
561 "tstInfo.historical",
562 self.message().span().start,
563 ))
564 .into());
565 }
566 self.check_in(
567 provider,
568 (parent, None),
569 (context, CheckPurpose::Historical),
570 covered,
571 recognizes,
572 profile,
573 )
574 }
575
576 #[expect(
581 clippy::result_large_err,
582 reason = "preserve both source frames and original opaque refusal without a fallible error box"
583 )]
584 pub fn check_mapped<'a, P, S: Source, PE, RE>(
585 &'a self,
586 provider: &'a P,
587 source: (&Reader<'a>, &SourceMap),
588 context: &CheckContext<'a, '_>,
589 covered: &mut S,
590 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
591 ) -> Result<CheckedTimestamp<'a>, MappedCheckError<S::Error, PE, RE>>
592 where
593 'doc: 'a,
594 P: for<'scope> SignatureProvider<'scope, Error = PE>,
595 {
596 self.check_mapped_with_profile(provider, source, context, covered, recognizes, |_| {
597 Ok::<(), ProfileFailure<Infallible>>(())
598 })
599 .map_err(MappedProfileCheckError::into_common)
600 }
601
602 #[expect(
607 clippy::result_large_err,
608 reason = "preserve both independently retained source frames and the original opaque refusal without a fallible error allocation"
609 )]
610 pub fn check_mapped_with_profile<'a, P, S: Source, PE, RE, FE>(
611 &'a self,
612 provider: &'a P,
613 source: (&Reader<'a>, &SourceMap),
614 context: &CheckContext<'a, '_>,
615 covered: &mut S,
616 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
617 profile: impl for<'view, 'scope> FnOnce(
618 ProfileInput<'view, 'doc, 'a, 'scope>,
619 ) -> Result<(), ProfileFailure<FE>>,
620 ) -> Result<CheckedTimestamp<'a>, MappedProfileCheckError<S::Error, PE, RE, FE>>
621 where
622 'doc: 'a,
623 P: for<'scope> SignatureProvider<'scope, Error = PE>,
624 {
625 self.check_mapped_input_with_profile(
626 provider,
627 source,
628 context,
629 &mut RawInput::new(covered),
630 recognizes,
631 profile,
632 )
633 }
634
635 #[expect(
638 clippy::result_large_err,
639 reason = "preserve both source frames and opaque refusal without allocating on failure"
640 )]
641 pub fn check_mapped_input_with_profile<'a, P, I, PE, RE, FE>(
642 &'a self,
643 provider: &'a P,
644 source: (&Reader<'a>, &SourceMap),
645 context: &CheckContext<'a, '_>,
646 covered: &mut I,
647 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
648 profile: impl for<'view, 'scope> FnOnce(
649 ProfileInput<'view, 'doc, 'a, 'scope>,
650 ) -> Result<(), ProfileFailure<FE>>,
651 ) -> Result<CheckedTimestamp<'a>, MappedProfileCheckError<I::Error, PE, RE, FE>>
652 where
653 'doc: 'a,
654 P: for<'scope> SignatureProvider<'scope, Error = PE>,
655 I: ComputedInput<P, PE>,
656 {
657 let (parent, source) = source;
658 preflight_sessions(self, parent, context)
659 .map_err(|error| MappedProfileCheckError::Preflight(error.into()))?;
660 let envelope = source
661 .try_copy_in(&parent.execution())
662 .map_err(MappedProfileCheckError::Provenance)?;
663 self.check_in(
664 provider,
665 (parent, Some(source)),
666 (context, CheckPurpose::Ordinary),
667 covered,
668 recognizes,
669 profile,
670 )
671 .map_err(|source| MappedProfileCheckError::Execution { envelope, source })
672 }
673
674 #[expect(
675 clippy::result_large_err,
676 reason = "retain original cause and accounted source context on refusal"
677 )]
678 fn check_in<'a, P, S, PE, RE, FE>(
679 &'a self,
680 provider: &'a P,
681 origin: (&Reader<'a>, Option<&SourceMap>),
682 context: (&CheckContext<'a, '_>, CheckPurpose),
683 covered: &mut S,
684 mut recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
685 profile: impl for<'view, 'scope> FnOnce(
686 ProfileInput<'view, 'doc, 'a, 'scope>,
687 ) -> Result<(), ProfileFailure<FE>>,
688 ) -> Result<CheckedTimestamp<'a>, ProfileCheckError<S::Error, PE, RE, FE>>
689 where
690 'doc: 'a,
691 P: for<'scope> SignatureProvider<'scope, Error = PE>,
692 S: ComputedInput<P, PE>,
693 {
694 let (parent, source) = origin;
695 let (context, purpose) = context;
696 preflight(self, parent, context).map_err(CheckError::from)?;
697 let certificate = context.certificate;
698 let identifier =
699 directory::matches_certificate_in(self.signer().sid(), certificate, parent)
700 .map_err(CheckError::Identifier)?;
701 let ess =
702 ess::SigningAttributes::read(self.signer(), parent).map_err(CheckError::Processing)?;
703 let carried = context
704 .requested
705 .as_ref()
706 .map(|requested| {
707 self.matches_cert_req(
708 requested.request,
709 Some(certificate),
710 parent,
711 requested.parent,
712 )
713 })
714 .transpose()
715 .map_err(CheckError::RequestField)?;
716 if !self.faults().is_empty()
717 || !self.message().faults().is_empty()
718 || !self.signer().faults().is_empty()
719 || !ess.faults().is_empty()
720 || ess.v1().is_some_and(|reading| !reading.faults().is_empty())
721 || ess.v2().is_some_and(|reading| !reading.faults().is_empty())
722 {
723 return Err(CheckError::Nonconforming {
724 span: self.message().span(),
725 }
726 .into());
727 }
728 let inspect = |info: &super::TokenInfo<'_>,
729 inside: &Reader<'_>,
730 information: &SourceMap|
731 -> Result<_, profile::InformationFailure<S::Error, PE, RE, FE>> {
732 if info.has_faults() {
733 return Err(InfoCheckError::Nonconforming { span: info.span() }.into());
734 }
735 profile(ProfileInput {
736 token: self,
737 parent,
738 envelope: source,
739 ess: &ess,
740 info,
741 reader: inside,
742 source: information,
743 })
744 .map_err(profile::InformationFailure::Profile)?;
745 let bindings = ess
746 .bind_certificate(provider, certificate, parent)
747 .map_err(InfoCheckError::Ess)?;
748 let signature = self
749 .check_signature(provider, context.key, parent)
750 .map_err(InfoCheckError::Signature)?;
751 let common_holds = common_holds(&signature, bindings, &identifier, carried);
752 let mut inspection = Inspection {
753 provider,
754 context,
755 covered,
756 recognizes: &mut recognizes,
757 common_holds,
758 purpose,
759 };
760 let checked = inspection.inspect(info, inside, information)?;
761 Ok((signature, bindings, checked))
762 };
763 let (signature, bindings, information) = match source {
764 Some(source) => self.with_info_mapped(parent, source, inspect),
765 None => self.with_info(parent, inspect),
766 }
767 .map_err(profile::information_error)?;
768 Ok(CheckedTimestamp {
769 signature,
770 identifier,
771 ess,
772 bindings,
773 authority: information.authority,
774 imprint: information.imprint,
775 requested_imprint: information.requested_imprint,
776 requested_fields: information.requested_fields,
777 carried,
778 timeliness: information.timeliness,
779 accuracy_error: information.accuracy_error,
780 unknown_critical: information.unknown,
781 evidence: information.evidence,
782 })
783 }
784}
785
786enum PreflightError {
787 Processing(Error),
788 Requested(super::RequestFieldError),
789 KeyMismatch(structured_asn1::Span),
790}
791
792impl<SE, PE, RE> From<PreflightError> for CheckError<SE, PE, RE> {
793 fn from(error: PreflightError) -> Self {
794 match error {
795 PreflightError::Processing(error) => Self::Processing(error),
796 PreflightError::Requested(error) => Self::RequestField(error),
797 PreflightError::KeyMismatch(certificate) => Self::KeyMismatch { certificate },
798 }
799 }
800}
801
802fn preflight(
803 token: &Token<'_>,
804 parent: &Reader<'_>,
805 context: &CheckContext<'_, '_>,
806) -> Result<(), PreflightError> {
807 preflight_sessions(token, parent, context)?;
808 let certificate = context.certificate;
809 let written = certificate.tbs().public_key().as_bytes();
810 let key = context.key.key().as_bytes();
811 let length = u64::try_from(written.len().max(key.len()))
812 .ok()
813 .and_then(|length| length.checked_add(1))
814 .ok_or_else(|| {
815 PreflightError::Processing(Error::schema(
816 ErrorCode::LengthOverflow,
817 "certificate.subjectPublicKeyInfo",
818 certificate.span().start,
819 ))
820 })?;
821 parent.execution().charge_work(length).map_err(|reason| {
822 PreflightError::Processing(Error::resource(
823 reason,
824 "certificate.subjectPublicKeyInfo",
825 certificate.span().start,
826 ))
827 })?;
828 if written != key {
829 return Err(PreflightError::KeyMismatch(certificate.span()));
830 }
831 Ok(())
832}
833
834fn preflight_sessions(
835 token: &Token<'_>,
836 parent: &Reader<'_>,
837 context: &CheckContext<'_, '_>,
838) -> Result<(), PreflightError> {
839 token
840 .signer()
841 .require_session(parent, "signerInfo")
842 .map_err(PreflightError::Processing)?;
843 let certificate = context.certificate;
844 if !certificate
845 .tbs()
846 .public_key()
847 .algorithm()
848 .same_session(context.certificate_parent)
849 {
850 return Err(PreflightError::Processing(Error::pkix(
851 structured_pkix::Error::new(
852 structured_pkix::ErrorCode::ForeignReader,
853 "certificate",
854 certificate.span().start,
855 ),
856 )));
857 }
858 if let Some(requested) = &context.requested
859 && !requested.request.same_session(requested.parent)
860 {
861 return Err(PreflightError::Requested(super::RequestFieldError {
862 operand: super::ImprintOperand::Requested,
863 source: Error::pkix(structured_pkix::Error::new(
864 structured_pkix::ErrorCode::ForeignReader,
865 "timeStampReq",
866 requested.request.span().start,
867 )),
868 }));
869 }
870 Ok(())
871}