1use crate::binary::{self, BinaryXml, Location};
4use crate::binding::{Bound, Coverage, Status};
5use crate::container::{DS, XADES};
6use crate::reference::{Boolean, LexicalSource, ReferenceXml, ReplayOutcome, Resolution};
7use crate::xml::{Access, AccessError, ChildError, Name, Snapshot, Xml};
8use crate::{ISSUER, Xades};
9use structured_asn1::{
10 Reader,
11 memory::{Execution, MemoryError, OwnedVec, Work},
12};
13use structured_cms::stream::{Receiver, Refused, Source};
14use structured_cms::{
15 provider::SignatureProvider,
16 timestamp::{CheckContext, CheckedTimestamp, TokenExtension},
17};
18use structured_ltv::timestamp::{CheckedActor, Purpose, Verified};
19
20pub const XADES_141: &str = "http://uri.etsi.org/01903/v1.4.1#";
22
23mod counters;
24mod coverage;
25mod material;
26pub use counters::{
27 CounterStamp, CounterTimestampError, CounterTimestampResult, CounterTimestamps,
28 counter_timestamps,
29};
30pub use coverage::{CoverageError, CoverageFor};
31pub(crate) mod profile;
32mod selection;
33mod tokens;
34pub use material::{Carried, CarriedCause, CarriedError, CarriedMaterial, OtherCarried};
35pub use profile::{DigestError, DigestUse};
36pub use selection::{
37 RequirementError, RequirementResult, TimestampSelection, requirements,
38 requirements_with_counters,
39};
40pub use tokens::{
41 EncodingConformance, TokenEntry, TokenPipelineError, TokenPipelineResult, TokenProfile,
42 TokenReading, TokenSource, TokenView, with_counter_tokens, with_tokens,
43};
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum Role {
48 Signature,
50 AllDataObjects,
52 IndividualDataObjects,
54 SignatureAndReferences,
56 ReferencesOnly,
58 Archive,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum AppendKind {
66 Signature,
68 Archive,
70}
71
72impl Role {
73 #[must_use]
75 pub fn purpose(self) -> Purpose {
76 match self {
77 Self::AllDataObjects | Self::IndividualDataObjects => Purpose::Content,
78 Self::Signature => Purpose::Signature,
79 Self::SignatureAndReferences | Self::ReferencesOnly => Purpose::ValidationData,
80 Self::Archive => Purpose::Archive,
81 }
82 }
83}
84
85#[derive(Debug)]
87pub enum InputError<E, N> {
88 Receiver(Refused),
90 Access(AccessError<E, N>),
92 Unavailable {
95 node: N,
97 cause: E,
99 },
100 Child(ChildError<E, N>),
102 Resource {
104 node: N,
106 cause: MemoryError,
108 },
109 Missing {
111 node: N,
113 local: &'static str,
115 },
116 Unexpected {
118 node: N,
120 },
121 Duplicate {
123 node: N,
125 },
126 Order {
128 node: N,
130 },
131 Foreign {
133 node: N,
135 },
136 ReferencedData {
138 node: N,
140 value: Boolean,
142 },
143 Reference {
145 node: N,
147 resolution: Resolution<()>,
149 },
150}
151
152pub trait TimestampXml: ReferenceXml {
159 fn canonicalize<W: Work>(
163 &self,
164 snapshot: Snapshot<Self::Document, Self::Generation>,
165 node: Self::Id,
166 method: Self::Id,
167 receiver: &mut impl Receiver,
168 execution: &W,
169 ) -> Result<(), Self::Error>;
170}
171
172#[derive(Debug)]
174pub struct Stamp<N, D, G> {
175 snapshot: Snapshot<D, G>,
176 signature: N,
177 node: N,
178 parent: N,
179 method: N,
180 role: Role,
181 tokens: OwnedVec<N>,
182 includes: OwnedVec<N>,
183}
184
185impl<N: Copy, D: Copy, G: Copy> Stamp<N, D, G> {
186 #[must_use]
188 pub fn signature(&self) -> N {
189 self.signature
190 }
191 #[must_use]
193 pub fn snapshot(&self) -> Snapshot<D, G> {
194 self.snapshot
195 }
196 #[must_use]
198 pub fn node(&self) -> N {
199 self.node
200 }
201 #[must_use]
203 pub fn method(&self) -> N {
204 self.method
205 }
206 #[must_use]
208 pub fn role(&self) -> Role {
209 self.role
210 }
211 #[must_use]
213 pub fn tokens(&self) -> &[N] {
214 &self.tokens
215 }
216}
217
218pub type Timestamp<X> = Stamp<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
220pub type VerifiedTimestamp<X> =
222 Verified<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, Xades>;
223pub type TimestampActor<'certificate, 'document, X> = CheckedActor<
225 'certificate,
226 'document,
227 <X as Xml>::Id,
228 <X as Xml>::Document,
229 <X as Xml>::Generation,
230 Xades,
231>;
232pub type InputResult<T, X> = Result<T, InputError<<X as Xml>::Error, <X as Xml>::Id>>;
234
235pub struct Check<'xml, 'document, 'certificate, 'context, X, P> {
241 pub xml: &'xml X,
243 pub provider: &'context P,
245 pub context: &'context CheckContext<'document, 'certificate>,
247}
248
249#[derive(Debug)]
251pub enum CheckError<E, N, D, G, PE, RE> {
252 Input(InputError<E, N>),
254 TokenIndex {
256 node: N,
258 index: usize,
260 },
261 Token(TokenCheckError<E, N, D, G, PE, RE>),
263}
264
265pub type TokenCheckError<E, N, D, G, PE, RE> = binary::Error<
267 E,
268 binary::TokenError<
269 structured_cms::timestamp::ProfileCheckError<InputError<E, N>, PE, RE, DigestError>,
270 >,
271 N,
272 D,
273 G,
274>;
275
276pub type CheckResult<R, X, PE, RE> = Result<
278 R,
279 CheckError<
280 <X as Xml>::Error,
281 <X as Xml>::Id,
282 <X as Xml>::Document,
283 <X as Xml>::Generation,
284 PE,
285 RE,
286 >,
287>;
288
289impl<'xml, 'document, 'certificate, X: TimestampXml + BinaryXml, P>
290 Check<'xml, 'document, 'certificate, '_, X, P>
291{
292 #[expect(
299 clippy::result_large_err,
300 reason = "retain original opaque causes and source maps without allocating on refusal"
301 )]
302 pub fn retain_actor<PE, RE>(
303 &self,
304 bound: &Bound<X::Id, X::Document, X::Generation>,
305 stamp: &Timestamp<X>,
306 index: usize,
307 operation: &Reader<'_>,
308 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
309 ) -> CheckResult<Option<TimestampActor<'certificate, 'document, X>>, X, PE, RE>
310 where
311 P: for<'scope> SignatureProvider<'scope, Error = PE>,
312 {
313 Ok(self
314 .retain(bound, stamp, index, operation, recognizes)?
315 .map(|verified| {
316 CheckedActor::new(ISSUER, verified, self.context.certificate, self.context.key)
317 }))
318 }
319
320 #[expect(
325 clippy::result_large_err,
326 reason = "retain original opaque causes and source maps without allocating on refusal"
327 )]
328 pub fn retain<PE, RE>(
329 &self,
330 bound: &Bound<X::Id, X::Document, X::Generation>,
331 stamp: &Timestamp<X>,
332 index: usize,
333 operation: &Reader<'_>,
334 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
335 ) -> CheckResult<Option<VerifiedTimestamp<X>>, X, PE, RE>
336 where
337 P: for<'scope> SignatureProvider<'scope, Error = PE>,
338 {
339 fn evidence(checked: CheckedTimestamp<'_>) -> Option<structured_cms::timestamp::Evidence> {
340 checked.into_evidence()
341 }
342 let located = self.run(bound, stamp, index, operation, recognizes, evidence)?;
343 Ok(located.value.map(|evidence| {
344 Verified::new(
345 ISSUER,
346 located.location,
347 stamp.signature,
348 stamp.node,
349 stamp.role.purpose(),
350 evidence,
351 )
352 }))
353 }
354
355 #[expect(
362 clippy::result_large_err,
363 reason = "preserve opaque original failures and admitted source maps without allocating while handling a refusal"
364 )]
365 pub fn run<R, PE, RE>(
366 &self,
367 bound: &Bound<X::Id, X::Document, X::Generation>,
368 stamp: &Timestamp<X>,
369 index: usize,
370 operation: &Reader<'_>,
371 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
372 visitor: impl for<'checked> FnOnce(CheckedTimestamp<'checked>) -> R,
373 ) -> CheckResult<binary::LocatedValue<R, X>, X, PE, RE>
374 where
375 P: for<'scope> SignatureProvider<'scope, Error = PE>,
376 {
377 if index >= stamp.tokens().len() {
378 return Err(CheckError::TokenIndex {
379 node: stamp.node(),
380 index,
381 });
382 }
383 let execution = operation.execution();
384 let input =
385 Input::prepare(self.xml, bound, stamp, &execution).map_err(CheckError::Input)?;
386 self.run_prepared(stamp, index, operation, input, recognizes, visitor)
387 }
388
389 #[expect(
393 clippy::result_large_err,
394 reason = "retain original source maps and opaque callback causes"
395 )]
396 pub fn retain_counter_actor<PE, RE>(
397 &self,
398 counter: &CounterStamp<X::Id, X::Document, X::Generation>,
399 index: usize,
400 operation: &Reader<'_>,
401 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
402 ) -> CheckResult<Option<TimestampActor<'certificate, 'document, X>>, X, PE, RE>
403 where
404 P: for<'scope> SignatureProvider<'scope, Error = PE>,
405 {
406 if index >= counter.stamp().tokens().len() {
407 return Err(CheckError::TokenIndex {
408 node: counter.stamp().node(),
409 index,
410 });
411 }
412 let execution = operation.execution();
413 let input = counter
414 .input(self.xml, &execution)
415 .map_err(CheckError::Input)?;
416 let stamp = counter.stamp();
417 let located = self.run_prepared(stamp, index, operation, input, recognizes, |checked| {
418 checked.into_evidence()
419 })?;
420 Ok(located.value.map(|evidence| {
421 CheckedActor::new(
422 ISSUER,
423 Verified::new(
424 ISSUER,
425 located.location,
426 stamp.signature,
427 stamp.node,
428 stamp.role.purpose(),
429 evidence,
430 ),
431 self.context.certificate,
432 self.context.key,
433 )
434 }))
435 }
436
437 #[expect(
443 clippy::result_large_err,
444 reason = "retain original source maps and opaque callback causes"
445 )]
446 pub fn retain_actor_from<PE, RE>(
447 &self,
448 bound: &Bound<X::Id, X::Document, X::Generation>,
449 stamp: &Timestamp<X>,
450 entry: TokenEntry<'_, '_, X::Id, X::Document, X::Generation>,
451 operation: &Reader<'_>,
452 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
453 ) -> CheckResult<Option<TimestampActor<'certificate, 'document, X>>, X, PE, RE>
454 where
455 P: for<'scope> SignatureProvider<'scope, Error = PE>,
456 {
457 self.retain_from(stamp, entry, operation, recognizes, |execution| {
458 Input::prepare(self.xml, bound, stamp, execution)
459 })
460 }
461
462 #[expect(
468 clippy::result_large_err,
469 reason = "retain original source maps and opaque callback causes"
470 )]
471 pub fn retain_counter_from<PE, RE>(
472 &self,
473 counter: &CounterStamp<X::Id, X::Document, X::Generation>,
474 entry: TokenEntry<'_, '_, X::Id, X::Document, X::Generation>,
475 operation: &Reader<'_>,
476 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
477 ) -> CheckResult<Option<TimestampActor<'certificate, 'document, X>>, X, PE, RE>
478 where
479 P: for<'scope> SignatureProvider<'scope, Error = PE>,
480 {
481 self.retain_from(counter.stamp(), entry, operation, recognizes, |execution| {
482 counter.input(self.xml, execution)
483 })
484 }
485
486 #[expect(
487 clippy::result_large_err,
488 reason = "retain original source maps and opaque callback causes"
489 )]
490 fn retain_from<PE, RE>(
491 &self,
492 stamp: &Timestamp<X>,
493 entry: TokenEntry<'_, '_, X::Id, X::Document, X::Generation>,
494 operation: &Reader<'_>,
495 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
496 prepare: impl FnOnce(&Execution<'_>) -> InputResult<Input<'xml, X>, X>,
497 ) -> CheckResult<Option<TimestampActor<'certificate, 'document, X>>, X, PE, RE>
498 where
499 P: for<'scope> SignatureProvider<'scope, Error = PE>,
500 {
501 let execution = operation.execution();
502 let access = Access::new(self.xml, stamp.snapshot, &execution);
503 access
504 .query(stamp.node, |_, _, work| Ok(work.charge_work(1)))
505 .map_err(|cause| CheckError::Input(InputError::Access(cause)))?
506 .map_err(|cause| {
507 CheckError::Input(InputError::Resource {
508 node: stamp.node,
509 cause,
510 })
511 })?;
512 let source = entry.source;
513 let equal = |left, right| {
514 access.equal_id(left, right).map_err(|cause| {
515 CheckError::Input(InputError::Resource {
516 node: stamp.node,
517 cause,
518 })
519 })
520 };
521 let expected = stamp.tokens.get(source.token_index());
522 if source.location().snapshot != stamp.snapshot
523 || !equal(source.signature(), stamp.signature)?
524 || !equal(source.property(), stamp.node)?
525 || source.role() != stamp.role
526 || match expected {
527 Some(expected) => !equal(source.location().node, *expected)?,
528 None => true,
529 }
530 {
531 return Err(CheckError::Input(InputError::Foreign {
532 node: source.location().node,
533 }));
534 }
535 let mut input = prepare(&execution).map_err(CheckError::Input)?;
536 let location = source.location();
537 let reader = entry.reader_in(operation).map_err(|cause| {
538 CheckError::Token(binary::Error::ReaderCreation { location, cause })
539 })?;
540 let checked = entry
541 .reading
542 .token()
543 .check_with_profile(
544 self.provider,
545 &reader,
546 self.context,
547 &mut input,
548 recognizes,
549 |fields| profile::check(&fields),
550 )
551 .map_err(|cause| {
552 CheckError::Token(binary::Error::Callback {
553 location,
554 cause: binary::TokenError::Callback(cause),
555 })
556 })?;
557 let evidence = checked.into_evidence();
558 access
559 .query(stamp.node, |_, _, _| Ok(()))
560 .map_err(|cause| CheckError::Input(InputError::Access(cause)))?;
561 Ok(evidence.map(|evidence| {
562 CheckedActor::new(
563 ISSUER,
564 Verified::new(
565 ISSUER,
566 location,
567 stamp.signature,
568 stamp.node,
569 stamp.role.purpose(),
570 evidence,
571 ),
572 self.context.certificate,
573 self.context.key,
574 )
575 }))
576 }
577
578 #[expect(
579 clippy::result_large_err,
580 reason = "retain original source maps and opaque callback causes"
581 )]
582 fn run_prepared<R, PE, RE>(
583 &self,
584 stamp: &Timestamp<X>,
585 index: usize,
586 operation: &Reader<'_>,
587 mut input: Input<'_, X>,
588 recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
589 visitor: impl for<'checked> FnOnce(CheckedTimestamp<'checked>) -> R,
590 ) -> CheckResult<binary::LocatedValue<R, X>, X, PE, RE>
591 where
592 P: for<'scope> SignatureProvider<'scope, Error = PE>,
593 {
594 let Some(&node) = stamp.tokens.get(index) else {
595 return Err(CheckError::TokenIndex {
596 node: stamp.node,
597 index,
598 });
599 };
600 let location = Location {
601 snapshot: stamp.snapshot,
602 node,
603 };
604 binary::with_token(self.xml, location, operation, |token, reader| {
605 let checked = token.check_with_profile(
606 self.provider,
607 reader,
608 self.context,
609 &mut input,
610 recognizes,
611 |fields| profile::check(&fields),
612 )?;
613 Ok(binary::Located {
614 location,
615 value: visitor(checked),
616 })
617 })
618 .map_err(CheckError::Token)
619 }
620}
621
622pub fn read<X: Xml, W: Work>(
628 access: &Access<'_, X, W>,
629 bound: &Bound<X::Id, X::Document, X::Generation>,
630 node: X::Id,
631) -> InputResult<Timestamp<X>, X> {
632 read_in(access, bound.containers(), node)
633}
634
635fn read_in<X: Xml, W: Work>(
636 access: &Access<'_, X, W>,
637 layout: &crate::container::Layout<X>,
638 node: X::Id,
639) -> InputResult<Timestamp<X>, X> {
640 let parent = unsigned_parent_in(access, layout, node)?;
641 let mut contained = false;
642 for child in access.children(parent) {
643 let child = child.map_err(InputError::Child)?;
644 if access
645 .equal_id(child, node)
646 .map_err(|cause| InputError::Resource { node: child, cause })?
647 {
648 contained = true;
649 }
650 }
651 if !contained {
652 return Err(InputError::Foreign { node });
653 }
654 let role = if named(access, node, XADES, "SignatureTimeStamp")? {
655 Role::Signature
656 } else if named(access, node, XADES_141, "ArchiveTimeStamp")? {
657 Role::Archive
658 } else if named(access, node, XADES_141, "SigAndRefsTimeStampV2")? {
659 Role::SignatureAndReferences
660 } else if named(access, node, XADES_141, "RefsOnlyTimeStampV2")? {
661 Role::ReferencesOnly
662 } else {
663 return Err(InputError::Unexpected { node });
664 };
665 read_fields(access, layout.signature(), parent, node, role)
666}
667
668pub(crate) fn unsigned_parent<X: Xml, W: Work>(
669 access: &Access<'_, X, W>,
670 bound: &Bound<X::Id, X::Document, X::Generation>,
671 node: X::Id,
672) -> InputResult<X::Id, X> {
673 unsigned_parent_in(access, bound.containers(), node)
674}
675
676fn unsigned_parent_in<X: Xml, W: Work>(
677 access: &Access<'_, X, W>,
678 layout: &crate::container::Layout<X>,
679 node: X::Id,
680) -> InputResult<X::Id, X> {
681 if layout.snapshot() != access.snapshot() {
682 return Err(InputError::Access(AccessError::Changed { node }));
683 }
684 let unsigned = layout.unsigned().ok_or(InputError::Foreign { node })?;
685 singleton(access, unsigned, XADES, "UnsignedSignatureProperties")
686}
687
688pub fn read_all_data_objects<X: Xml, W: Work>(
692 access: &Access<'_, X, W>,
693 bound: &Bound<X::Id, X::Document, X::Generation>,
694 signed: &crate::properties::Reading<X>,
695 node: X::Id,
696) -> InputResult<Timestamp<X>, X> {
697 read_data_objects(access, bound, signed, node, Role::AllDataObjects)
698}
699
700pub fn read_individual_data_objects<X: Xml, W: Work>(
703 access: &Access<'_, X, W>,
704 bound: &Bound<X::Id, X::Document, X::Generation>,
705 signed: &crate::properties::Reading<X>,
706 node: X::Id,
707) -> InputResult<Timestamp<X>, X> {
708 read_data_objects(access, bound, signed, node, Role::IndividualDataObjects)
709}
710
711fn read_data_objects<X: Xml, W: Work>(
712 access: &Access<'_, X, W>,
713 bound: &Bound<X::Id, X::Document, X::Generation>,
714 signed: &crate::properties::Reading<X>,
715 node: X::Id,
716 role: Role,
717) -> InputResult<Timestamp<X>, X> {
718 let layout = bound.containers();
719 if signed.snapshot() != layout.snapshot() {
720 return Err(InputError::Foreign { node });
721 }
722 if !access
723 .equal_id(signed.signature(), layout.signature())
724 .map_err(|cause| InputError::Resource { node, cause })?
725 {
726 return Err(InputError::Foreign { node });
727 }
728 if access.snapshot() != signed.snapshot() {
729 return Err(InputError::Access(AccessError::Changed { node }));
730 }
731 let objects = signed.data_objects().ok_or(InputError::Foreign { node })?;
732 read_object_fields(access, layout, objects, node, role)
733}
734
735fn read_object_fields<X: Xml, W: Work>(
736 access: &Access<'_, X, W>,
737 layout: &crate::container::Layout<X>,
738 objects: &crate::properties::DataObjects<X::Id>,
739 node: X::Id,
740 role: Role,
741) -> InputResult<Timestamp<X>, X> {
742 let mut contained = false;
743 for index in 0..objects.entries().len() {
744 access
745 .query(node, |_, _, work| Ok(work.charge_work(1)))
746 .map_err(InputError::Access)?
747 .map_err(|cause| InputError::Resource { node, cause })?;
748 let entry = objects.entries()[index];
749 if access
750 .equal_id(entry.node, node)
751 .map_err(|cause| InputError::Resource { node, cause })?
752 {
753 let expected = if role == Role::IndividualDataObjects {
754 crate::properties::DataObjectRole::IndividualTimestamp
755 } else {
756 crate::properties::DataObjectRole::AllTimestamp
757 };
758 if entry.role != expected {
759 return Err(InputError::Unexpected { node });
760 }
761 contained = true;
762 break;
763 }
764 }
765 if !contained {
766 return Err(InputError::Foreign { node });
767 }
768 read_fields(access, layout.signature(), objects.parent(), node, role)
769}
770
771fn read_fields<X: Xml, W: Work>(
772 access: &Access<'_, X, W>,
773 signature: X::Id,
774 parent: X::Id,
775 node: X::Id,
776 role: Role,
777) -> InputResult<Timestamp<X>, X> {
778 let mut method = None;
779 let mut tokens = access
780 .query(node, |_, _, work| Ok(work.vector()))
781 .map_err(InputError::Access)?;
782 let mut includes = access
783 .query(node, |_, _, work| Ok(work.vector()))
784 .map_err(InputError::Access)?;
785 for child in access.children(node) {
786 let child = child.map_err(InputError::Child)?;
787 if named(access, child, XADES, "Include")? {
788 if role != Role::IndividualDataObjects {
789 return Err(InputError::Unexpected { node: child });
790 }
791 if method.is_some() || !tokens.is_empty() {
792 return Err(InputError::Order { node: child });
793 }
794 includes
795 .try_push(child)
796 .map_err(|cause| InputError::Resource { node, cause })?;
797 } else if named(access, child, DS, "CanonicalizationMethod")? {
798 if method.is_some() {
799 return Err(InputError::Duplicate { node: child });
800 }
801 if !tokens.is_empty() {
802 return Err(InputError::Order { node: child });
803 }
804 method = Some(child);
805 } else if named(access, child, XADES, "EncapsulatedTimeStamp")? {
806 if role == Role::Signature && !tokens.is_empty() {
807 return Err(InputError::Duplicate { node: child });
808 }
809 tokens
810 .try_push(child)
811 .map_err(|cause| InputError::Resource { node, cause })?;
812 } else {
813 return Err(InputError::Unexpected { node: child });
814 }
815 }
816 let method = method.ok_or(InputError::Missing {
821 node,
822 local: "CanonicalizationMethod",
823 })?;
824 if tokens.is_empty() {
825 return Err(InputError::Missing {
826 node,
827 local: "EncapsulatedTimeStamp",
828 });
829 }
830 if role == Role::IndividualDataObjects && includes.is_empty() {
831 return Err(InputError::Missing {
832 node,
833 local: "Include",
834 });
835 }
836 Ok(Stamp {
837 snapshot: access.snapshot(),
838 signature,
839 node,
840 parent,
841 method,
842 role,
843 tokens: tokens.into_owned(),
844 includes: includes.into_owned(),
845 })
846}
847
848#[derive(Debug, Clone, Copy)]
849enum Part<N> {
850 Element(N),
851 Reference { node: N, covered: Option<N> },
852}
853
854pub struct Input<'a, X: TimestampXml> {
856 xml: &'a X,
857 snapshot: Snapshot<X::Document, X::Generation>,
858 signature: X::Id,
859 timestamp: X::Id,
860 method: X::Id,
861 role: Role,
862 parts: OwnedVec<Part<X::Id>>,
863}
864
865impl<'a, X: TimestampXml> Input<'a, X> {
866 pub fn prepare_append<W: Work>(
877 xml: &'a X,
878 bound: &Bound<X::Id, X::Document, X::Generation>,
879 kind: AppendKind,
880 method: X::Id,
881 execution: &W,
882 ) -> InputResult<Self, X> {
883 let layout = bound.containers();
884 let access = Access::new(xml, layout.snapshot(), execution);
885 if !named(&access, method, DS, "CanonicalizationMethod")? {
886 return Err(InputError::Unexpected { node: method });
887 }
888 let mut parts = execution.vector();
889 let role = match kind {
890 AppendKind::Signature => {
891 let value = singleton(&access, layout.signature(), DS, "SignatureValue")?;
892 parts
893 .try_push(Part::Element(value))
894 .map_err(|cause| InputError::Resource {
895 node: method,
896 cause,
897 })?;
898 Role::Signature
899 }
900 AppendKind::Archive => {
901 append_archive(&access, layout, method, None, &mut parts)?;
902 Role::Archive
903 }
904 };
905 Ok(Self {
906 xml,
907 snapshot: layout.snapshot(),
908 signature: layout.signature(),
909 timestamp: method,
910 method,
911 role,
912 parts: parts.into_owned(),
913 })
914 }
915
916 pub fn prepare<W: Work>(
918 xml: &'a X,
919 bound: &Bound<X::Id, X::Document, X::Generation>,
920 stamp: &Timestamp<X>,
921 execution: &W,
922 ) -> InputResult<Self, X> {
923 Self::prepare_in(xml, bound.containers(), stamp, execution)
924 }
925
926 fn prepare_in<W: Work>(
927 xml: &'a X,
928 layout: &crate::container::Layout<X>,
929 stamp: &Timestamp<X>,
930 execution: &W,
931 ) -> InputResult<Self, X> {
932 if stamp.snapshot != layout.snapshot() {
933 return Err(InputError::Foreign { node: stamp.node });
934 }
935 let access = Access::new(xml, stamp.snapshot, execution);
936 let refusal = |cause| InputError::Resource {
937 node: stamp.node,
938 cause,
939 };
940 if !access
941 .equal_id(stamp.signature, layout.signature())
942 .map_err(refusal)?
943 {
944 return Err(InputError::Foreign { node: stamp.node });
945 }
946 let mut parts = execution.vector();
947 match stamp.role {
948 Role::Signature => {
949 let value = singleton(&access, stamp.signature, DS, "SignatureValue")?;
950 parts.try_push(Part::Element(value)).map_err(refusal)?;
951 }
952 Role::AllDataObjects => {
953 append_references(
958 &access,
959 stamp.signature,
960 stamp.node,
961 Some(layout.signed()),
962 &mut parts,
963 )?;
964 }
965 Role::IndividualDataObjects => {
966 append_includes(&access, stamp, layout.signed(), &mut parts)?;
967 }
968 Role::SignatureAndReferences | Role::ReferencesOnly => {
969 append_validation_references(&access, stamp, &mut parts)?;
970 }
971 Role::Archive => {
972 append_archive(
973 &access,
974 layout,
975 stamp.node,
976 Some((stamp.parent, stamp.node)),
977 &mut parts,
978 )?;
979 }
980 }
981 Ok(Self {
982 xml,
983 snapshot: stamp.snapshot,
984 signature: stamp.signature,
985 timestamp: stamp.node,
986 method: stamp.method,
987 role: stamp.role,
988 parts: parts.into_owned(),
989 })
990 }
991
992 #[must_use]
995 pub fn location(&self) -> (Snapshot<X::Document, X::Generation>, X::Id) {
996 (self.snapshot, self.timestamp)
997 }
998}
999
1000fn append_validation_references<X: TimestampXml, W: Work>(
1001 access: &Access<'_, X, W>,
1002 stamp: &Timestamp<X>,
1003 parts: &mut OwnedVec<Part<X::Id>, W>,
1004) -> InputResult<(), X> {
1005 let refusal = |cause| InputError::Resource {
1009 node: stamp.node,
1010 cause,
1011 };
1012 let with_signature = stamp.role == Role::SignatureAndReferences;
1013 if with_signature {
1014 let value = singleton(access, stamp.signature, DS, "SignatureValue")?;
1015 parts.try_push(Part::Element(value)).map_err(refusal)?;
1016 }
1017 let mut found = [false; 4];
1018 let roles = [
1019 (XADES_141, "CompleteCertificateRefsV2"),
1020 (XADES, "CompleteRevocationRefs"),
1021 (XADES_141, "AttributeCertificateRefsV2"),
1022 (XADES, "AttributeRevocationRefs"),
1023 ];
1024 let mut reached = false;
1025 for child in access.children(stamp.parent) {
1026 let node = child.map_err(InputError::Child)?;
1027 if access.equal_id(node, stamp.node).map_err(refusal)? {
1028 reached = true;
1029 break;
1030 }
1031 let mut selected = with_signature && named(access, node, XADES, "SignatureTimeStamp")?;
1032 for (index, (namespace, local)) in roles.iter().enumerate() {
1033 if named(access, node, namespace, local)? {
1034 if found[index] {
1035 return Err(InputError::Duplicate { node });
1036 }
1037 found[index] = true;
1038 selected = true;
1039 break;
1040 }
1041 }
1042 if selected {
1043 parts.try_push(Part::Element(node)).map_err(refusal)?;
1044 }
1045 }
1046 if !reached {
1047 return Err(InputError::Foreign { node: stamp.node });
1048 }
1049 for index in 0..2 {
1050 if !found[index] {
1051 return Err(InputError::Missing {
1052 node: stamp.node,
1053 local: roles[index].1,
1054 });
1055 }
1056 }
1057 Ok(())
1058}
1059
1060fn append_includes<X: TimestampXml, W: Work>(
1061 access: &Access<'_, X, W>,
1062 stamp: &Timestamp<X>,
1063 signed_properties: X::Id,
1064 parts: &mut OwnedVec<Part<X::Id>, W>,
1065) -> InputResult<(), X> {
1066 for index in 0..stamp.includes.len() {
1070 access
1071 .query(stamp.node, |_, _, work| Ok(work.charge_work(1)))
1072 .map_err(InputError::Access)?
1073 .map_err(|cause| InputError::Resource {
1074 node: stamp.node,
1075 cause,
1076 })?;
1077 let include = stamp.includes[index];
1078 let value = access
1079 .query(include, |xml, snapshot, work| {
1080 xml.boolean_attribute(
1081 snapshot,
1082 include,
1083 Name {
1084 namespace: "",
1085 local: "referencedData",
1086 },
1087 work,
1088 )
1089 })
1090 .map_err(InputError::Access)?;
1091 if value != Boolean::Value(true) {
1092 return Err(InputError::ReferencedData {
1093 node: include,
1094 value,
1095 });
1096 }
1097 let resolved = access
1098 .query(include, |xml, snapshot, work| {
1099 xml.resolve_reference(
1100 snapshot,
1101 stamp.signature,
1102 include,
1103 LexicalSource::Attribute(Name {
1104 namespace: "",
1105 local: "URI",
1106 }),
1107 work,
1108 |descriptor| {
1109 let r = descriptor.reference;
1110 (r.node, r.target, r.status, r.coverage)
1111 },
1112 )
1113 })
1114 .map_err(InputError::Access)?;
1115 let reference = match resolved {
1116 Resolution::Resolved((node, target, Status::Valid, coverage)) => {
1117 if let Some(target) = target
1118 && access
1119 .equal_id(target, signed_properties)
1120 .map_err(|cause| InputError::Resource {
1121 node: include,
1122 cause,
1123 })?
1124 {
1125 return Err(InputError::Reference {
1126 node: include,
1127 resolution: Resolution::Resolved(()),
1128 });
1129 }
1130 Part::Reference {
1131 node,
1132 covered: if coverage == Coverage::ElementContent {
1133 target
1134 } else {
1135 None
1136 },
1137 }
1138 }
1139 other => {
1140 return Err(InputError::Reference {
1141 node: include,
1142 resolution: other.map(|_| ()),
1143 });
1144 }
1145 };
1146 parts
1147 .try_push(reference)
1148 .map_err(|cause| InputError::Resource {
1149 node: include,
1150 cause,
1151 })?;
1152 }
1153 Ok(())
1154}
1155
1156fn append_references<X: TimestampXml, W: Work>(
1157 access: &Access<'_, X, W>,
1158 signature: X::Id,
1159 context: X::Id,
1160 excluded: Option<X::Id>,
1161 parts: &mut OwnedVec<Part<X::Id>, W>,
1162) -> InputResult<(), X> {
1163 let count = access
1164 .query(signature, |xml, snapshot, work| {
1165 xml.reference_count(snapshot, signature, work)
1166 })
1167 .map_err(InputError::Access)?;
1168 for ordinal in 0..count {
1169 let (part, target) = access
1170 .query(signature, |xml, snapshot, work| {
1171 xml.reference(snapshot, signature, ordinal, work, |r| {
1172 (
1173 Part::Reference {
1174 node: r.node,
1175 covered: if r.status == Status::Valid
1176 && r.coverage == Coverage::ElementContent
1177 {
1178 r.target
1179 } else {
1180 None
1181 },
1182 },
1183 r.target,
1184 )
1185 })
1186 })
1187 .map_err(InputError::Access)?;
1188 if let (Some(target), Some(excluded)) = (target, excluded)
1189 && access
1190 .equal_id(target, excluded)
1191 .map_err(|cause| InputError::Resource {
1192 node: context,
1193 cause,
1194 })?
1195 {
1196 continue;
1197 }
1198 parts.try_push(part).map_err(|cause| InputError::Resource {
1199 node: context,
1200 cause,
1201 })?;
1202 }
1203 Ok(())
1204}
1205
1206fn append_archive<X: TimestampXml, W: Work>(
1209 access: &Access<'_, X, W>,
1210 layout: &crate::container::Layout<X>,
1211 context: X::Id,
1212 before: Option<(X::Id, X::Id)>,
1213 parts: &mut OwnedVec<Part<X::Id>, W>,
1214) -> InputResult<(), X> {
1215 let refusal = |cause| InputError::Resource {
1216 node: context,
1217 cause,
1218 };
1219 let signature = layout.signature();
1220 append_references(access, signature, context, None, parts)?;
1221 parts
1222 .try_push(Part::Element(singleton(
1223 access,
1224 signature,
1225 DS,
1226 "SignedInfo",
1227 )?))
1228 .map_err(refusal)?;
1229 parts
1230 .try_push(Part::Element(singleton(
1231 access,
1232 signature,
1233 DS,
1234 "SignatureValue",
1235 )?))
1236 .map_err(refusal)?;
1237 if let Some(key) = optional(access, signature, DS, "KeyInfo")? {
1238 parts.try_push(Part::Element(key)).map_err(refusal)?;
1239 }
1240 let parent = match before {
1241 Some((parent, _)) => Some(parent),
1242 None => match layout.unsigned() {
1243 Some(unsigned) => optional(access, unsigned, XADES, "UnsignedSignatureProperties")?,
1244 None => None,
1245 },
1246 };
1247 let mut reached = before.is_none();
1248 if let Some(parent) = parent {
1249 for child in access.children(parent) {
1250 let node = child.map_err(InputError::Child)?;
1251 if let Some((_, boundary)) = before
1252 && access.equal_id(node, boundary).map_err(refusal)?
1253 {
1254 reached = true;
1255 break;
1256 }
1257 parts.try_push(Part::Element(node)).map_err(refusal)?;
1258 }
1259 }
1260 if !reached {
1261 return Err(InputError::Foreign { node: context });
1262 }
1263 for child in access.children(signature) {
1264 let node = child.map_err(InputError::Child)?;
1265 if !access.equal_id(node, layout.object()).map_err(refusal)?
1266 && named(access, node, DS, "Object")?
1267 {
1268 parts.try_push(Part::Element(node)).map_err(refusal)?;
1269 }
1270 }
1271 Ok(())
1272}
1273
1274impl<X: TimestampXml> Source for Input<'_, X> {
1275 type Error = InputError<X::Error, X::Id>;
1276
1277 fn stream<W: Work>(
1278 &mut self,
1279 receiver: &mut impl Receiver,
1280 execution: &W,
1281 ) -> Result<(), Self::Error> {
1282 let access = Access::new(self.xml, self.snapshot, execution);
1283 let mut receiver = Stop {
1284 receiver,
1285 refused: false,
1286 };
1287 for index in 0..self.parts.len() {
1288 execution
1289 .charge_work(1)
1290 .map_err(|cause| InputError::Resource {
1291 node: self.timestamp,
1292 cause,
1293 })?;
1294 match self.parts[index] {
1295 Part::Element(node) => access
1296 .query(node, |xml, snapshot, work| {
1297 xml.canonicalize(snapshot, node, self.method, &mut receiver, work)
1298 })
1299 .map_err(InputError::Access)?,
1300 Part::Reference { node, .. } => {
1301 let outcome = access
1302 .query(node, |xml, snapshot, work| {
1303 xml.replay_reference(
1304 snapshot,
1305 self.signature,
1306 node,
1307 crate::reference::Replay::Timestamp(self.method),
1308 &mut receiver,
1309 work,
1310 )
1311 })
1312 .map_err(InputError::Access)?;
1313 if let ReplayOutcome::Unavailable(cause) = outcome {
1314 return Err(InputError::Unavailable { node, cause });
1315 }
1316 }
1317 }
1318 if receiver.refused {
1319 return Err(InputError::Receiver(Refused));
1320 }
1321 }
1322 Ok(())
1323 }
1324}
1325
1326struct Stop<'a, R: Receiver> {
1327 receiver: &'a mut R,
1328 refused: bool,
1329}
1330
1331impl<R: Receiver> Receiver for Stop<'_, R> {
1332 fn write(&mut self, bytes: &[u8]) -> Result<(), Refused> {
1333 if self.refused {
1334 return Err(Refused);
1335 }
1336 if self.receiver.write(bytes).is_err() {
1337 self.refused = true;
1338 return Err(Refused);
1339 }
1340 Ok(())
1341 }
1342}
1343
1344fn named<X: Xml, W: Work>(
1345 access: &Access<'_, X, W>,
1346 node: X::Id,
1347 namespace: &str,
1348 local: &str,
1349) -> InputResult<bool, X> {
1350 access
1351 .has_name(node, Name { namespace, local })
1352 .map_err(InputError::Access)
1353}
1354
1355pub(crate) fn singleton<X: Xml, W: Work>(
1356 access: &Access<'_, X, W>,
1357 parent: X::Id,
1358 namespace: &str,
1359 local: &'static str,
1360) -> InputResult<X::Id, X> {
1361 optional(access, parent, namespace, local)?.ok_or(InputError::Missing {
1362 node: parent,
1363 local,
1364 })
1365}
1366
1367pub(crate) fn optional<X: Xml, W: Work>(
1368 access: &Access<'_, X, W>,
1369 parent: X::Id,
1370 namespace: &str,
1371 local: &str,
1372) -> InputResult<Option<X::Id>, X> {
1373 let mut found = None;
1374 for child in access.children(parent) {
1375 let node = child.map_err(InputError::Child)?;
1376 if named(access, node, namespace, local)? {
1377 if found.is_some() {
1378 return Err(InputError::Duplicate { node });
1379 }
1380 found = Some(node);
1381 }
1382 }
1383 Ok(found)
1384}