1use super::*;
4use structured_asn1::memory::{MemoryError, OwnedMap, OwnedText, OwnedVec, Work};
5
6mod hash_uses;
7pub use hash_uses::*;
8mod key_receipt;
9pub use key_receipt::*;
10
11type RetainedOperation<'scope, 'inputs, W> = CapturedOperation<
12 &'scope crate::policy::VerificationPolicy,
13 &'scope VerificationOperationBudgets<'inputs>,
14 ScopedCapture<'scope, W>,
15>;
16
17pub struct VerifiedSession<'view, 'scope, 'inputs, W: Work> {
20 view: DocumentView<'view>,
21 context: &'scope VerifyContext<'inputs>,
22 budgets: &'scope VerificationOperationBudgets<'inputs>,
23 evidence: VerifyEvidence<OwnedVec<SignatureEvidence, W>>,
24 operations: OwnedVec<RetainedOperation<'scope, 'inputs, W>, W>,
25 resolver: UriReferenceResolver<'view>,
26 work: &'scope W,
27 replay_active: Cell<bool>,
28}
29
30pub fn with_verified_request<'inputs, W: Work, R>(
34 document: &XmlDocument,
35 context: &VerifyContext<'inputs>,
36 request: &VerificationRequest<'inputs>,
37 work: &W,
38 continuation: impl for<'view, 'scope> FnOnce(
39 VerifiedSession<'view, 'scope, 'inputs, W>,
40 ) -> Result<R, DsigError>,
41) -> Result<R, DsigError> {
42 with_verified_document(
43 document,
44 context,
45 request,
46 ScopedRequestStorage(work),
47 |view, context, budgets, evidence, operations, resolver| {
48 continuation(VerifiedSession {
49 view,
50 context,
51 budgets,
52 evidence,
53 operations,
54 resolver,
55 work,
56 replay_active: Cell::new(false),
57 })
58 },
59 )
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
64pub enum ReferenceParent {
65 SignedInfo(NodeIdentity),
67 Manifest(NodeIdentity),
69}
70
71#[derive(Debug)]
74pub struct AuthenticatedReference<'a> {
75 pub identity: NodeIdentity,
77 pub parent: ReferenceParent,
79 pub uri: Option<&'a str>,
81 pub reference_type: Option<&'a str>,
83 pub result: Option<&'a ReferenceResult>,
85 pub location: ReferenceLocation,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91pub enum ReferenceLocation {
92 SameDocument(NodeIdentity),
94 Detached(Option<[u8; 32]>),
96 Unchecked,
98}
99
100#[derive(Clone, Copy, Debug, PartialEq, Eq)]
102pub enum CatalogueCompleteness {
103 Complete,
105 Unchecked(NodeIdentity),
107 Incomplete(NodeIdentity),
109}
110
111#[derive(Debug)]
113pub enum CatalogueError<E> {
114 Processing(DsigError),
116 Visitor(E),
118}
119
120fn flatten_catalogue<T, E>(
121 result: Result<T, CatalogueError<CatalogueError<E>>>,
122) -> Result<T, CatalogueError<E>> {
123 match result {
124 Ok(value) => Ok(value),
125 Err(CatalogueError::Processing(error)) => Err(CatalogueError::Processing(error)),
126 Err(CatalogueError::Visitor(error)) => Err(error),
127 }
128}
129
130#[derive(Clone, Copy, Debug)]
133pub enum LexicalSource<'a> {
134 Attribute {
136 namespace: Option<&'a str>,
138 local_name: &'a str,
140 },
141 SimpleContent,
143}
144
145#[derive(Debug)]
147pub enum ReferenceResolution<'a> {
148 Absent,
150 Malformed,
152 Unsupported,
155 Detached,
157 Unresolved,
159 NotReference(NodeIdentity),
161 Unauthenticated(NodeIdentity),
163 Reference(AuthenticatedReference<'a>),
165}
166
167enum LexicalValue<'a, W: Work> {
168 Borrowed(&'a str),
169 Joined(OwnedText<W>),
170}
171impl<W: Work> LexicalValue<'_, W> {
172 fn as_str(&self) -> &str {
173 match self {
174 Self::Borrowed(value) => value,
175 Self::Joined(value) => value.as_str(),
178 }
179 }
180}
181
182pub struct SelectedSignature<'session, 'view, 'scope, 'inputs, W: Work, Q: Work = W> {
184 session: &'session VerifiedSession<'view, 'scope, 'inputs, W>,
185 index: usize,
186 work: &'session Q,
187 results: OwnedMap<NodeIdentity, (ReferenceSet, usize), Q>,
188 replays: RefCell<OwnedVec<ReplayRecord, Q>>,
189}
190
191impl<'view, 'scope, 'inputs, W: Work> VerifiedSession<'view, 'scope, 'inputs, W> {
192 pub fn view(&self) -> DocumentView<'view> {
194 self.view
195 }
196 pub fn evidence(&self) -> &VerifyEvidence<OwnedVec<SignatureEvidence, W>> {
199 &self.evidence
200 }
201
202 pub fn into_evidence(self) -> VerifyEvidence<OwnedVec<SignatureEvidence, W>> {
205 self.evidence
206 }
207
208 pub fn select_signature(
211 &self,
212 identity: NodeIdentity,
213 ) -> Result<SelectedSignature<'_, 'view, 'scope, 'inputs, W>, DsigError> {
214 self.select_signature_in(identity, self.work)
215 }
216
217 pub fn with_query<Q: Work, R, E>(
224 &self,
225 identity: NodeIdentity,
226 work: &Q,
227 visitor: impl for<'query> FnOnce(
228 SelectedSignature<'query, 'view, 'scope, 'inputs, W, Q>,
229 ) -> Result<R, E>,
230 ) -> Result<R, CatalogueError<E>> {
231 let selected = self
232 .select_signature_in(identity, work)
233 .map_err(CatalogueError::Processing)?;
234 visitor(selected).map_err(CatalogueError::Visitor)
235 }
236
237 fn select_signature_in<'query, Q: Work>(
238 &'query self,
239 identity: NodeIdentity,
240 work: &'query Q,
241 ) -> Result<SelectedSignature<'query, 'view, 'scope, 'inputs, W, Q>, DsigError> {
242 let index = self.authenticated_signature_in(identity, work)?;
243 let entry = &self.evidence.signatures()[index];
244 let mut results = work.map();
245 if let Ok(result) = &entry.result {
246 for (set, entries) in [
247 (ReferenceSet::SignedInfo, &result.signed_info_references),
248 (ReferenceSet::Manifest, &result.manifest_references),
249 ] {
250 for (ordinal, reference) in entries.iter().enumerate() {
251 work.charge_work(1)?;
252 if let Some(node) = reference.reference_identity
253 && results.try_insert(node, (set, ordinal))?.is_some()
254 {
255 return Err(DsigError::InvalidRequest {
256 reason: "ambiguous original Reference evidence",
257 });
258 }
259 }
260 }
261 }
262 Ok(SelectedSignature {
263 session: self,
264 index,
265 work,
266 results,
267 replays: RefCell::new(work.vector()),
268 })
269 }
270
271 fn authenticated_signature_in<Q: Work>(
274 &self,
275 identity: NodeIdentity,
276 work: &Q,
277 ) -> Result<usize, DsigError> {
278 work.charge_work(1)?;
279 self.view.resolve_node(identity)?;
280 for (index, entry) in self.evidence.signatures().iter().enumerate() {
281 work.charge_work(1)?;
282 if entry.identity != identity {
283 continue;
284 }
285 let retained = &self.operations[index];
286 if let Some(error) = retained.capture.failure.get() {
287 return Err(error.into());
288 }
289 let operation = &retained.operation;
290 operation.validate_document_view(self.view)?;
291 work.charge_work(
292 u64::try_from(operation.decision_count()).map_err(|_| MemoryError::WorkExceeded)?,
293 )?;
294 if operation.accepted_signature().is_none() {
295 return Err(DsigError::InvalidRequest {
296 reason: "selected Signature is not authenticated",
297 });
298 }
299 return Ok(index);
300 }
301 Err(DsigError::InvalidRequest {
302 reason: "Signature is not part of this request",
303 })
304 }
305
306 fn signature_method_in<Q: Work>(
307 &self,
308 index: usize,
309 work: &Q,
310 ) -> Result<SelectedSignatureMethod, DsigError> {
311 work.charge_work(1)?;
312 let retained = &self.operations[index];
313 retained.operation.validate_document_view(self.view)?;
314 if let Some(error) = retained.capture.failure.get() {
315 return Err(error.into());
316 }
317 retained
318 .capture
319 .method
320 .get()
321 .ok_or(DsigError::InvalidRequest {
322 reason: "selected Signature has no original method receipt",
323 })
324 }
325}
326
327impl<'session, 'view, 'scope, 'inputs, W: Work, Q: Work>
328 SelectedSignature<'session, 'view, 'scope, 'inputs, W, Q>
329{
330 fn operation(
331 &self,
332 ) -> &OperationExecutionContext<
333 &'scope crate::policy::VerificationPolicy,
334 &'scope VerificationOperationBudgets<'inputs>,
335 > {
336 &self.session.operations[self.index].operation
337 }
338 fn signature(&self) -> Result<Node<'_, '_>, DsigError> {
339 Ok(self
340 .session
341 .view
342 .resolve_node(self.session.evidence.signatures()[self.index].identity)?)
343 }
344 fn authenticates_element(&self, node: Node<'_, '_>) -> Result<bool, DsigError> {
345 let signature = self.signature()?;
346 for ancestor in node.ancestors() {
347 self.work.charge_work(1)?;
348 if ancestor.has_tag_name((XMLDSIG_NS, "SignedInfo"))
349 && ancestor.parent() == Some(signature)
350 {
351 return Ok(true);
352 }
353 if self
354 .operation()
355 .is_authenticated(self.session.view.node_identity(ancestor))
356 {
357 return Ok(true);
358 }
359 }
360 Ok(false)
361 }
362
363 pub fn resolve_reference(
367 &self,
368 property: NodeIdentity,
369 source: LexicalSource<'_>,
370 ) -> Result<ReferenceResolution<'_>, DsigError> {
371 self.work.charge_work(1)?;
372 self.operation().validate_document_view(self.session.view)?;
373 let node = self.session.view.resolve_node(property)?;
374 if !node.is_element() {
375 return Err(DsigError::InvalidRequest {
376 reason: "reference property must be an element",
377 });
378 }
379 if !self.authenticates_element(node)? {
380 return Ok(ReferenceResolution::Unauthenticated(property));
381 }
382 let value = match source {
383 LexicalSource::Attribute {
384 namespace,
385 local_name,
386 } => {
387 let mut value = None;
388 for attribute in node.attributes() {
389 let bytes = attribute
390 .name()
391 .len()
392 .checked_add(attribute.namespace().map_or(0, str::len))
393 .and_then(|n| n.checked_add(1))
394 .ok_or(MemoryError::WorkExceeded)?;
395 self.work.charge_work(
396 u64::try_from(bytes).map_err(|_| MemoryError::WorkExceeded)?,
397 )?;
398 if attribute.name() == local_name && attribute.namespace() == namespace {
399 value = Some(attribute.value());
400 break;
401 }
402 }
403 let Some(value) = value else {
404 return Ok(ReferenceResolution::Absent);
405 };
406 LexicalValue::Borrowed(value)
407 }
408 LexicalSource::SimpleContent => {
409 let mut value = LexicalValue::Borrowed("");
410 for child in node.children() {
411 self.work.charge_work(1)?;
412 if child.is_element() {
413 return Ok(ReferenceResolution::Malformed);
414 }
415 let Some(text) = child.text().filter(|_| child.is_text()) else {
416 continue;
417 };
418 match &mut value {
419 LexicalValue::Borrowed(first) if first.is_empty() => *first = text,
420 LexicalValue::Borrowed(first) => {
421 let mut joined = OwnedText::try_from_str(first, self.work)?;
422 joined.try_push_str(text)?;
423 value = LexicalValue::Joined(joined);
424 }
425 LexicalValue::Joined(joined) => joined.try_push_str(text)?,
426 }
427 }
428 value
429 }
430 };
431 let uri = value.as_str();
432 self.work
433 .charge_work(u64::try_from(uri.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
434 if !self.session.context.policy.uris.references.allows(uri) {
435 return Err(crate::policy::PolicyViolation::Uri {
436 operation: "extension reference resolution",
437 reason: "reference URI class is not permitted",
438 }
439 .into());
440 }
441 if !uri.is_empty() && !uri.starts_with('#') {
442 crate::c14n::xml_base::resolve_uri_from_node_in(
443 node,
444 uri,
445 None,
446 self.session.budgets.transforms.xml_base_resolution(),
447 &crate::c14n::scratch::Accounted(self.work),
448 )
449 .map_err(super::super::uri::map_xml_base_execution_error)
450 .map_err(|error| {
451 DsigError::Reference(ReferenceProcessingError::UriDereference(error))
452 })?;
453 return Ok(ReferenceResolution::Detached);
454 }
455 let target = self.session.resolver.node_for_same_document_reference_in(
456 uri,
457 Some(self.session.budgets.transforms.node_set_materialization()),
458 &crate::c14n::scratch::Accounted(self.work),
459 );
460 let target = match target {
461 Ok(target) => target,
462 Err(
463 TransformError::UnsupportedUri(_)
464 | TransformError::AccountedUnsupportedUri(_)
465 | TransformError::InvalidFragment,
466 ) => return Ok(ReferenceResolution::Unsupported),
467 Err(error) => {
468 return Err(DsigError::Reference(
469 ReferenceProcessingError::UriDereference(error),
470 ));
471 }
472 };
473 let Some(target) = target else {
474 return Ok(ReferenceResolution::Unresolved);
475 };
476 let identity = self.session.view.node_identity(target);
477 if !target.has_tag_name((XMLDSIG_NS, "Reference")) {
478 return Ok(ReferenceResolution::NotReference(identity));
479 }
480 match self.descriptor(identity)? {
481 Some(reference) => Ok(ReferenceResolution::Reference(reference)),
482 None => Ok(ReferenceResolution::Unauthenticated(identity)),
483 }
484 }
485 fn parent(&self, reference: Node<'_, '_>) -> Result<Option<ReferenceParent>, DsigError> {
486 if !reference.has_tag_name((XMLDSIG_NS, "Reference")) {
487 return Ok(None);
488 }
489 let Some(parent) = reference.parent() else {
490 return Ok(None);
491 };
492 let signature = self.signature()?;
493 let view = self.session.view;
494 if parent.has_tag_name((XMLDSIG_NS, "SignedInfo")) && parent.parent() == Some(signature) {
495 return Ok(Some(ReferenceParent::SignedInfo(
496 view.node_identity(parent),
497 )));
498 }
499 if !parent.has_tag_name((XMLDSIG_NS, "Manifest")) {
500 return Ok(None);
501 }
502 for ancestor in parent.ancestors() {
503 self.work.charge_work(1)?;
504 if self
505 .operation()
506 .is_authenticated(view.node_identity(ancestor))
507 {
508 return Ok(Some(ReferenceParent::Manifest(view.node_identity(parent))));
509 }
510 }
511 Ok(None)
512 }
513 fn descriptor(
514 &self,
515 reference: NodeIdentity,
516 ) -> Result<Option<AuthenticatedReference<'_>>, DsigError> {
517 self.work.charge_work(1)?;
518 self.operation().validate_document_view(self.session.view)?;
519 let node = self.session.view.resolve_node(reference)?;
520 let Some(parent) = self.parent(node)? else {
521 return Ok(None);
522 };
523 self.descriptor_in_parent(node, parent).map(Some)
524 }
525
526 fn descriptor_in_parent<'a>(
527 &'a self,
528 node: Node<'a, 'a>,
529 parent: ReferenceParent,
530 ) -> Result<AuthenticatedReference<'a>, DsigError> {
531 let reference = self.session.view.node_identity(node);
532 let result = match self.results.get(&reference, self.work)? {
533 Some((set, index)) => self.session.evidence.signatures()[self.index]
534 .result
535 .as_ref()
536 .ok()
537 .map(|result| match set {
538 ReferenceSet::SignedInfo => &result.signed_info_references[*index],
539 ReferenceSet::Manifest => &result.manifest_references[*index],
540 }),
541 None => None,
542 };
543 for _ in node.attributes() {
546 self.work.charge_work(9)?;
547 }
548 Ok(AuthenticatedReference {
549 identity: reference,
550 parent,
551 uri: node.attribute("URI"),
552 reference_type: node.attribute("Type"),
553 result,
554 location: if let Some(target) = result.and_then(|result| result.target_identity) {
555 ReferenceLocation::SameDocument(target)
556 } else {
557 match self
558 .session
559 .budgets
560 .resolved_reference_resources
561 .borrow()
562 .get(&node.id())
563 {
564 Some(OperationResourceIdentity::External { fingerprint, .. }) => {
565 ReferenceLocation::Detached(Some(*fingerprint))
566 }
567 Some(OperationResourceIdentity::MissingExternal) => {
568 ReferenceLocation::Detached(None)
569 }
570 _ => ReferenceLocation::Unchecked,
571 }
572 },
573 })
574 }
575
576 pub fn visit_authenticated_references<E>(
579 &self,
580 mut visitor: impl for<'a> FnMut(AuthenticatedReference<'a>) -> Result<(), E>,
581 ) -> Result<CatalogueCompleteness, CatalogueError<E>> {
582 flatten_catalogue(self.visit_reference_instructions(|node, parent, _| {
583 let reference = self
584 .descriptor_in_parent(node, parent)
585 .map_err(CatalogueError::Processing)?;
586 visitor(reference).map_err(CatalogueError::Visitor)
587 }))
588 }
589
590 fn visit_reference_instructions<E>(
591 &self,
592 mut visitor: impl for<'a> FnMut(
593 Node<'a, 'a>,
594 ReferenceParent,
595 Option<Node<'a, 'a>>,
596 ) -> Result<(), E>,
597 ) -> Result<CatalogueCompleteness, CatalogueError<E>> {
598 let mut completeness = CatalogueCompleteness::Complete;
599 let mut references = 0;
600 let maximum = self.session.context.policy.resources.max_references;
601 self.operation()
602 .validate_document_view(self.session.view)
603 .map_err(|error| CatalogueError::Processing(error.into()))?;
604 for node in self.session.view.document().descendants() {
605 self.work
606 .charge_work(1)
607 .map_err(|error| CatalogueError::Processing(error.into()))?;
608 if node.has_tag_name((XMLDSIG_NS, "Manifest"))
609 && self
610 .authenticates_element(node)
611 .map_err(CatalogueError::Processing)?
612 {
613 let mut count = 0;
614 let mut malformed = false;
615 for child in node.children() {
616 self.work
617 .charge_work(1)
618 .map_err(|error| CatalogueError::Processing(error.into()))?;
619 if child.has_tag_name((XMLDSIG_NS, "Reference")) {
620 count += 1;
621 } else if child.is_element() {
622 malformed = true;
623 } else if child.is_text() {
624 let text = child.text().unwrap_or_default();
625 self.work
626 .charge_work(text.len() as u64)
627 .map_err(|error| CatalogueError::Processing(error.into()))?;
628 if !is_xml_whitespace_only(text) {
629 malformed = true;
630 }
631 }
632 }
633 if (malformed || count == 0)
634 && !matches!(completeness, CatalogueCompleteness::Incomplete(_))
635 {
636 completeness =
637 CatalogueCompleteness::Incomplete(self.session.view.node_identity(node));
638 }
639 }
640 if !node.has_tag_name((XMLDSIG_NS, "Reference")) {
641 continue;
642 }
643 let Some(parent) = self.parent(node).map_err(CatalogueError::Processing)? else {
644 continue;
645 };
646 let identity = self.session.view.node_identity(node);
647 if references == maximum {
648 return Err(CatalogueError::Processing(
649 crate::policy::PolicyViolation::ResourceLimit {
650 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
651 maximum,
652 actual: references + 1,
653 }
654 .into(),
655 ));
656 }
657 references += 1;
658 self.work
659 .charge_work(128)
660 .map_err(|error| CatalogueError::Processing(error.into()))?;
661 for child in node.children() {
662 let text = child.text().filter(|_| child.is_text()).map_or(0, str::len);
663 let cost = u64::try_from(text)
664 .ok()
665 .and_then(|length| length.checked_add(128))
666 .ok_or_else(|| CatalogueError::Processing(MemoryError::WorkExceeded.into()))?;
667 self.work
668 .charge_work(cost)
669 .map_err(|error| CatalogueError::Processing(error.into()))?;
670 }
671 let method = super::super::parse::inspect_reference_layout(node)
672 .ok()
673 .map(|(_, method, _)| method);
674 if method.is_none() {
675 if !matches!(completeness, CatalogueCompleteness::Incomplete(_)) {
676 completeness = CatalogueCompleteness::Incomplete(identity);
677 }
678 } else if let ReferenceParent::Manifest(parent) = parent
679 && matches!(completeness, CatalogueCompleteness::Complete)
680 && self.session.context.policy.manifest_processing
681 == crate::policy::ManifestProcessing::Ignore
682 {
683 completeness = CatalogueCompleteness::Unchecked(parent);
684 }
685 visitor(node, parent, method).map_err(CatalogueError::Visitor)?;
686 }
687 Ok(completeness)
688 }
689}
690
691#[derive(Clone, Copy, Debug, PartialEq, Eq)]
694pub enum ReplayMode {
695 ReferenceOutput,
697 Timestamp(NodeIdentity),
699}
700
701#[derive(Clone, Copy, Debug, PartialEq, Eq)]
704pub enum ReplayOutcome {
705 Complete,
707 Unavailable(ExternalResourceUnavailable),
709}
710
711#[derive(Clone, Copy, Debug, PartialEq, Eq)]
713pub struct ExternalResourceUnavailable {
714 pub reference: NodeIdentity,
716}
717
718#[derive(Debug)]
720pub enum ReplayError<E> {
721 Processing(DsigError),
723 Receiver(E),
725}
726impl<E> From<TransformError> for ReplayError<E> {
727 fn from(error: TransformError) -> Self {
728 Self::Processing(match error {
729 TransformError::C14n(crate::c14n::C14nError::SharedExecution(error)) => error.into(),
730 error => ReferenceProcessingError::Transform(error).into(),
731 })
732 }
733}
734impl<E> From<OperationPlanError> for ReplayError<E> {
735 fn from(error: OperationPlanError) -> Self {
736 Self::Processing(error.into())
737 }
738}
739
740struct ReplayWriter<'a, W: Work, E, F> {
741 work: &'a W,
742 budget: &'a TransformExecutionBudget,
743 canonicalizing: bool,
744 receiver: &'a mut F,
745 failure: Option<ReplayError<E>>,
746}
747
748struct ReplayTerminal<'a, W: Work, E, F> {
749 work: &'a W,
750 receiver: &'a mut F,
751 timestamp: Option<(crate::c14n::C14nAlgorithm, Option<&'a str>)>,
752 error: std::marker::PhantomData<fn() -> E>,
753}
754
755impl<'a, W: Work, E, F: FnMut(&[u8]) -> Result<(), E>>
756 super::super::transforms::TransformTerminal<crate::c14n::scratch::Accounted<'a, W>>
757 for ReplayTerminal<'a, W, E, F>
758{
759 type Output = ();
760 type Error = ReplayError<E>;
761
762 fn finish<D: crate::xml::dom::storage::Storage>(
763 self,
764 data: super::super::transforms::ExecutionData<
765 '_,
766 crate::c14n::scratch::Accounted<'a, W>,
767 D,
768 >,
769 explicit: Option<(&crate::c14n::C14nAlgorithm, Option<&str>)>,
770 budget: &TransformExecutionBudget,
771 ) -> Result<(), ReplayError<E>> {
772 let mut output = ReplayWriter {
773 work: self.work,
774 budget,
775 canonicalizing: data.nodes().is_some(),
776 receiver: self.receiver,
777 failure: None,
778 };
779 let result = if let Some(bytes) = data.binary() {
780 use std::io::Write;
781 output.write_all(bytes).map_err(crate::c14n::C14nError::Io)
782 } else {
783 let nodes = data.nodes().expect("execution has nodes or bytes");
784 let implicit = crate::c14n::C14nAlgorithm::from_uri(DEFAULT_IMPLICIT_C14N_URI)
785 .expect("constant C14N URI");
786 let algorithm = explicit
787 .map(|(algorithm, _)| algorithm)
788 .or(self.timestamp.as_ref().map(|(algorithm, _)| algorithm))
789 .unwrap_or(&implicit);
790 let prefixes = if explicit.is_none() {
791 self.timestamp.as_ref().and_then(|(_, prefixes)| *prefixes)
792 } else {
793 explicit.and_then(|(_, prefixes)| prefixes)
794 };
795 crate::c14n::canonicalize_visible_to_writer_accounted(
796 nodes.document(),
797 Some(nodes),
798 algorithm,
799 super::super::transforms::replay_output_limit(budget),
800 budget.xml_base_resolution(),
801 &mut output,
802 self.work,
803 prefixes,
804 )
805 };
806 if let Some(error) = output.failure {
807 return Err(error);
808 }
809 result.map_err(|error| match error {
810 crate::c14n::C14nError::SharedExecution(error) => ReplayError::Processing(error.into()),
811 error => super::super::transforms::replay_c14n_error(error, budget).into(),
812 })
813 }
814}
815
816#[derive(Clone, Copy, Debug, PartialEq, Eq)]
817enum ReplayState {
818 Pending,
819 Complete,
820 Unavailable,
821 Rejected,
822}
823struct ReplayRecord {
824 signature: NodeIdentity,
825 reference: NodeIdentity,
826 state: ReplayState,
827}
828
829struct AdmittedReplay<'a, W: Work> {
830 records: &'a RefCell<OwnedVec<ReplayRecord, W>>,
831 active: &'a Cell<bool>,
832 index: usize,
833}
834impl<W: Work> AdmittedReplay<'_, W> {
835 fn finish<E>(&self, result: &Result<ReplayOutcome, ReplayError<E>>) {
836 self.records.borrow_mut()[self.index].state = match result {
837 Ok(ReplayOutcome::Complete) => ReplayState::Complete,
838 Ok(ReplayOutcome::Unavailable(_)) => ReplayState::Unavailable,
839 Err(_) => ReplayState::Rejected,
840 };
841 }
842}
843impl<W: Work> Drop for AdmittedReplay<'_, W> {
844 fn drop(&mut self) {
845 let mut records = self.records.borrow_mut();
846 if records[self.index].state == ReplayState::Pending {
847 records[self.index].state = ReplayState::Rejected;
848 }
849 self.active.set(false);
850 }
851}
852impl<W: Work, E, F: FnMut(&[u8]) -> Result<(), E>> std::io::Write for ReplayWriter<'_, W, E, F> {
853 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
854 if self.failure.is_some() {
855 return Err(std::io::ErrorKind::Other.into());
856 }
857 let result = (|| {
858 let units = u64::try_from(bytes.len())
859 .map_err(|_| ReplayError::Processing(MemoryError::WorkExceeded.into()))?;
860 self.work
861 .charge_work(units)
862 .map_err(|error| ReplayError::Processing(error.into()))?;
863 if self.canonicalizing {
864 super::super::transforms::charge_replay_output(self.budget, bytes.len())?;
865 }
866 (self.receiver)(bytes).map_err(ReplayError::Receiver)
867 })();
868 match result {
869 Ok(()) => Ok(bytes.len()),
870 Err(error) => {
871 self.failure = Some(error);
872 Err(std::io::ErrorKind::Other.into())
873 }
874 }
875 }
876 fn flush(&mut self) -> std::io::Result<()> {
877 Ok(())
878 }
879}
880
881impl<W: Work, Q: Work> SelectedSignature<'_, '_, '_, '_, W, Q> {
882 pub fn replay_reference<E>(
887 &self,
888 reference: NodeIdentity,
889 mode: ReplayMode,
890 receiver: &mut impl FnMut(&[u8]) -> Result<(), E>,
891 ) -> Result<ReplayOutcome, ReplayError<E>> {
892 self.work
893 .charge_work(
894 u64::try_from(self.operation().decision_count())
895 .map_err(|_| ReplayError::Processing(MemoryError::WorkExceeded.into()))?,
896 )
897 .map_err(|error| ReplayError::Processing(error.into()))?;
898 self.operation()
899 .run_reference_replay(self.session.view, reference, || {
900 self.replay_admitted(reference, mode, receiver)
901 })
902 }
903
904 fn replay_admitted<E>(
905 &self,
906 reference: NodeIdentity,
907 mode: ReplayMode,
908 receiver: &mut impl FnMut(&[u8]) -> Result<(), E>,
909 ) -> Result<ReplayOutcome, ReplayError<E>> {
910 let processing = ReplayError::Processing;
911 let descriptor = self
912 .descriptor(reference)
913 .map_err(processing)?
914 .ok_or_else(|| {
915 processing(DsigError::InvalidRequest {
916 reason: "Reference instruction is not authenticated by selected Signature",
917 })
918 })?;
919 if matches!(descriptor.parent, ReferenceParent::Manifest(_))
920 && self.session.context.policy.manifest_processing
921 == crate::policy::ManifestProcessing::Ignore
922 {
923 return Err(processing(
924 crate::policy::PolicyViolation::ManifestProcessingDisabled.into(),
925 ));
926 }
927 if self.session.replay_active.get() {
928 return Err(processing(DsigError::InvalidRequest {
929 reason: "reentrant replay would reset the active transform chain",
930 }));
931 }
932 let index = {
933 let mut records = self.replays.borrow_mut();
934 let index = records.len();
935 records
936 .try_push(ReplayRecord {
937 signature: self.session.evidence.signatures()[self.index].identity,
938 reference,
939 state: ReplayState::Pending,
940 })
941 .map_err(|error| processing(error.into()))?;
942 debug_assert_eq!(records[index].reference, reference);
943 debug_assert_eq!(
944 records[index].signature,
945 self.session.evidence.signatures()[self.index].identity
946 );
947 index
948 };
949 self.session.replay_active.set(true);
950 let admission = AdmittedReplay {
951 records: &self.replays,
952 active: &self.session.replay_active,
953 index,
954 };
955 let result = self.replay_instruction(descriptor, mode, receiver);
956 admission.finish(&result);
957 result
958 }
959
960 fn replay_instruction<E>(
961 &self,
962 descriptor: AuthenticatedReference<'_>,
963 mode: ReplayMode,
964 receiver: &mut impl FnMut(&[u8]) -> Result<(), E>,
965 ) -> Result<ReplayOutcome, ReplayError<E>> {
966 let processing = ReplayError::Processing;
967 let node = self
968 .session
969 .view
970 .resolve_node(descriptor.identity)
971 .map_err(|error| processing(error.into()))?;
972 let uri = descriptor.uri.ok_or_else(|| {
973 processing(DsigError::Reference(ReferenceProcessingError::MissingUri))
974 })?;
975 if !self.session.context.policy.uris.references.allows(uri) {
976 return Err(processing(
977 crate::policy::PolicyViolation::Uri {
978 operation: "reference replay",
979 reason: "reference URI class is not permitted",
980 }
981 .into(),
982 ));
983 }
984 let allowed = self
985 .session
986 .context
987 .policy
988 .transforms
989 .allowed_algorithms
990 .as_ref();
991 let maximum = self
992 .session
993 .context
994 .policy
995 .resources
996 .max_transforms_per_reference;
997 let mut transform_count = 0;
998 self.work
999 .charge_work(node.children().len() as u64)
1000 .map_err(|error| processing(error.into()))?;
1001 for transforms in node
1002 .children()
1003 .filter(|child| child.has_tag_name((XMLDSIG_NS, "Transforms")))
1004 {
1005 self.work
1006 .charge_work(transforms.children().len() as u64)
1007 .map_err(|error| processing(error.into()))?;
1008 for transform in transforms.children().filter(Node::is_element) {
1009 self.work
1010 .charge_work(1)
1011 .map_err(|error| processing(error.into()))?;
1012 transform_count += 1;
1013 if transform_count > maximum {
1014 return Err(processing(
1015 crate::policy::PolicyViolation::ResourceLimit {
1016 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
1017 maximum,
1018 actual: transform_count,
1019 }
1020 .into(),
1021 ));
1022 }
1023 self.work
1024 .charge_work(transform.attributes().len() as u64)
1025 .map_err(|error| processing(error.into()))?;
1026 if let Some(algorithm) = transform.attribute("Algorithm") {
1027 self.work
1028 .charge_work(algorithm.len() as u64)
1029 .map_err(|error| processing(error.into()))?;
1030 enforce_current_transform_allowed(allowed, algorithm, self.work)
1031 .map_err(processing)?;
1032 }
1033 }
1034 }
1035 let parse_failure = |error| match error {
1036 super::super::parse::ParseError::Transform(TransformError::C14n(
1037 crate::c14n::C14nError::SharedExecution(error),
1038 )) => processing(error.into()),
1039 error => processing(DsigError::ParseManifestReference(error)),
1040 };
1041 let transforms = super::super::parse::parse_replay_transforms(
1042 node,
1043 &mut self.session.budgets.xpath_parse.borrow_mut(),
1044 &crate::c14n::scratch::Accounted(self.work),
1045 )
1046 .map_err(parse_failure)?;
1047 if matches!(mode, ReplayMode::ReferenceOutput)
1048 && !transform_chain_produces_binary(
1049 classify_uri(uri) == UriClass::External,
1050 &transforms,
1051 )
1052 {
1053 enforce_transform_allowed(allowed, DEFAULT_IMPLICIT_C14N_URI).map_err(processing)?;
1054 }
1055 let timestamp = match mode {
1056 ReplayMode::ReferenceOutput => None,
1057 ReplayMode::Timestamp(method) => {
1058 let method = self
1059 .session
1060 .view
1061 .resolve_node(method)
1062 .map_err(|error| processing(error.into()))?;
1063 let uri = method.attribute("Algorithm").ok_or_else(|| {
1064 processing(DsigError::InvalidStructure {
1065 reason: "timestamp method has no Algorithm",
1066 })
1067 })?;
1068 self.work
1069 .charge_work(uri.len() as u64)
1070 .map_err(|error| processing(error.into()))?;
1071 enforce_current_transform_allowed(allowed, uri, self.work).map_err(processing)?;
1072 let algorithm = super::super::parse::parse_replay_canonicalization_method(
1073 method,
1074 &crate::c14n::scratch::Accounted(self.work),
1075 )
1076 .map_err(parse_failure)?;
1077 Some(algorithm)
1078 }
1079 };
1080 let budget = &self.session.budgets.transforms;
1081 let Some(data) = self.session.resolver.replay_input_from_with_budget(
1082 uri,
1083 node,
1084 budget.node_set_materialization(),
1085 budget.xml_base_resolution(),
1086 &crate::c14n::scratch::Accounted(self.work),
1087 )?
1088 else {
1089 return Ok(ReplayOutcome::Unavailable(ExternalResourceUnavailable {
1090 reference: descriptor.identity,
1091 }));
1092 };
1093 let enveloped_signature =
1094 super::super::transforms::enveloped_signature_for_reference(node, &transforms, || {
1095 self.work
1096 .charge_work(1)
1097 .map_err(|error| processing(error.into()))
1098 })?;
1099 super::super::transforms::replay_reference_transforms(
1100 node,
1101 enveloped_signature,
1102 data,
1103 &transforms,
1104 super::super::transforms::ReplayExecution {
1105 options: self.session.context.transform_options(),
1106 budget,
1107 work: self.work,
1108 },
1109 ReplayTerminal {
1110 work: self.work,
1111 receiver,
1112 timestamp,
1113 error: std::marker::PhantomData,
1114 },
1115 )?;
1116 Ok(ReplayOutcome::Complete)
1117 }
1118}