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