1use base64::Engine;
9use getrandom::SysRng;
10use p256::ecdsa::{Signature as P256Signature, SigningKey as P256SigningKey};
11use p256::pkcs8::{DecodePrivateKey, EncodePublicKey};
12use p384::ecdsa::{Signature as P384Signature, SigningKey as P384SigningKey};
13use roxmltree::{Document, Node};
14use rsa::RsaPrivateKey;
15use rsa::pkcs1v15::Signature as RsaPkcs1v15Signature;
16use rsa::pkcs1v15::SigningKey as RsaPkcs1v15SigningKey;
17use rsa::signature::{RandomizedSigner, SignatureEncoding, Signer};
18use rsa::traits::PublicKeyParts;
19use sha2::{Sha256, Sha384, Sha512};
20use std::collections::HashSet;
21use x509_parser::prelude::FromDer;
22
23use crate::c14n::canonicalize;
24
25use super::builder::{SignatureBuilder, SignatureBuilderError};
26use super::digest::{DigestAlgorithm, compute_digest};
27use super::mutation::{
28 XmlMutationError, append_signature_to_root, fill_key_info, fill_signature_value,
29 fill_signed_info_digest_values,
30};
31use super::parse::{
32 MAX_REFERENCES_PER_SIGNATURE, SignatureAlgorithm, XMLDSIG_NS, parse_signed_info,
33};
34use super::transforms::{
35 Transform, TransformExecutionBudget, TransformOptions, XPathHereSemantics,
36 XPathSignatureParseBudget, execute_transforms_with_options_and_budget,
37 parse_transforms_with_budget,
38};
39use super::types::TransformError;
40use super::uri::UriReferenceResolver;
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
45pub struct ComputedReferenceDigest {
46 pub index: usize,
48 pub uri: String,
50 pub digest_method: DigestAlgorithm,
52 pub digest_value: String,
54}
55
56#[derive(Debug, thiserror::Error)]
58pub enum SigningDigestError {
59 #[error("XML parse error: {0}")]
61 XmlParse(#[from] roxmltree::Error),
62
63 #[error("missing required element: <{element}>")]
65 MissingElement {
66 element: &'static str,
68 },
69
70 #[error("invalid signing template: {0}")]
72 InvalidStructure(String),
73
74 #[error("unsupported digest algorithm: {uri}")]
76 UnsupportedAlgorithm {
77 uri: String,
79 },
80
81 #[error("digest algorithm is disabled for signing: {uri}")]
83 SigningAlgorithmDisabled {
84 uri: &'static str,
86 },
87
88 #[error("reference processing error: {0}")]
90 Transform(#[from] TransformError),
91
92 #[error("XML mutation error: {0}")]
94 XmlMutation(#[from] XmlMutationError),
95}
96
97#[derive(Debug, thiserror::Error)]
99pub enum SigningError {
100 #[error("signing digest pass failed: {0}")]
102 Digest(#[from] SigningDigestError),
103
104 #[error("failed to parse SignedInfo after digest fill: {0}")]
106 ParseSignedInfo(#[from] super::parse::ParseError),
107
108 #[error("SignedInfo canonicalization failed: {0}")]
110 Canonicalization(#[from] crate::c14n::C14nError),
111
112 #[error("signing key error: {0}")]
114 Key(#[from] SigningKeyError),
115
116 #[error("XML mutation error: {0}")]
118 XmlMutation(#[from] XmlMutationError),
119
120 #[error("KeyInfo writer error: {0}")]
122 KeyInfo(#[from] KeyInfoWriteError),
123
124 #[error("signature template error: {0}")]
126 Template(#[from] SignatureBuilderError),
127}
128
129#[derive(Debug, thiserror::Error)]
131#[non_exhaustive]
132pub enum SigningKeyError {
133 #[error("invalid PEM private key")]
135 InvalidKeyPem,
136
137 #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
139 InvalidKeyFormat {
140 label: String,
142 },
143
144 #[error("invalid PKCS#8 private key DER")]
146 InvalidKeyDer,
147
148 #[error("signing key does not support algorithm: {uri}")]
150 UnsupportedAlgorithm {
151 uri: String,
153 },
154
155 #[error("private-key signing operation failed")]
157 SigningFailed,
158
159 #[error("failed to encode signing public key as SPKI DER")]
161 PublicKeyEncodingFailed,
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166#[non_exhaustive]
167pub enum SigningPublicKeyInfo {
168 Rsa {
170 spki_der: Vec<u8>,
172 modulus: Vec<u8>,
174 exponent: Vec<u8>,
176 },
177 Ec {
179 spki_der: Vec<u8>,
181 curve_oid: &'static str,
183 public_key: Vec<u8>,
185 },
186}
187
188impl SigningPublicKeyInfo {
189 #[must_use]
191 pub fn spki_der(&self) -> &[u8] {
192 match self {
193 Self::Rsa { spki_der, .. } | Self::Ec { spki_der, .. } => spki_der,
194 }
195 }
196}
197
198pub trait SigningKey {
200 fn sign(
202 &self,
203 algorithm: SignatureAlgorithm,
204 canonical_signed_info: &[u8],
205 ) -> Result<Vec<u8>, SigningKeyError>;
206
207 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
209}
210
211pub trait KeyInfoWriter {
213 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
215}
216
217#[derive(Debug, thiserror::Error)]
219#[non_exhaustive]
220pub enum KeyInfoWriteError {
221 #[error("invalid PEM certificate")]
223 InvalidCertificatePem,
224
225 #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
227 InvalidCertificateFormat {
228 label: String,
230 },
231
232 #[error("invalid X.509 certificate DER")]
234 InvalidCertificateDer,
235
236 #[error("signing key public-key extraction failed: {0}")]
238 SigningKey(#[from] SigningKeyError),
239
240 #[error("X.509 certificate public key does not match signing key")]
242 CertificateKeyMismatch,
243}
244
245pub struct X509CertificateKeyInfoWriter {
247 certificate_der: Vec<u8>,
248}
249
250impl X509CertificateKeyInfoWriter {
251 pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
253 let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
254 .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
255 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
256 return Err(KeyInfoWriteError::InvalidCertificatePem);
257 }
258 if pem.label != "CERTIFICATE" {
259 return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
260 }
261 Self::from_der(&pem.contents)
262 }
263
264 pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
266 let (rest, _) = x509_parser::certificate::X509Certificate::from_der(certificate_der)
267 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
268 if !rest.is_empty() {
269 return Err(KeyInfoWriteError::InvalidCertificateDer);
270 }
271 Ok(Self {
272 certificate_der: certificate_der.to_vec(),
273 })
274 }
275}
276
277impl KeyInfoWriter for X509CertificateKeyInfoWriter {
278 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
279 let (rest, certificate) =
280 x509_parser::certificate::X509Certificate::from_der(&self.certificate_der)
281 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
282 if !rest.is_empty() {
283 return Err(KeyInfoWriteError::InvalidCertificateDer);
284 }
285 let signing_public_key = signing_key.public_key_info()?;
286 if certificate.public_key().raw != signing_public_key.spki_der() {
287 return Err(KeyInfoWriteError::CertificateKeyMismatch);
288 }
289
290 let certificate_b64 =
291 base64::engine::general_purpose::STANDARD.encode(&self.certificate_der);
292 Ok(format!(
293 "<X509Data xmlns=\"{XMLDSIG_NS}\"><X509Certificate>{certificate_b64}</X509Certificate></X509Data>"
294 ))
295 }
296}
297
298pub struct RsaSigningKey {
300 key: RsaPrivateKey,
301}
302
303impl RsaSigningKey {
304 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
306 let private_key_der = parse_private_key_pem(private_key_pem)?;
307 Self::from_pkcs8_der(&private_key_der)
308 }
309
310 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
312 let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
313 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
314 Ok(Self { key })
315 }
316}
317
318impl SigningKey for RsaSigningKey {
319 fn sign(
320 &self,
321 algorithm: SignatureAlgorithm,
322 canonical_signed_info: &[u8],
323 ) -> Result<Vec<u8>, SigningKeyError> {
324 match algorithm {
325 SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
326 RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
327 canonical_signed_info,
328 ),
329 SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
330 RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
331 canonical_signed_info,
332 ),
333 SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
334 RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
335 canonical_signed_info,
336 ),
337 _ => Err(SigningKeyError::UnsupportedAlgorithm {
338 uri: algorithm.uri().to_string(),
339 }),
340 }
341 }
342
343 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
344 let public_key = self.key.to_public_key();
345 let spki_der = public_key
346 .to_public_key_der()
347 .map(|doc| doc.as_bytes().to_vec())
348 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
349 Ok(SigningPublicKeyInfo::Rsa {
350 spki_der,
351 modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
352 exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
353 })
354 }
355}
356
357fn sign_rsa_pkcs1v15_with_rng(
358 key: impl RandomizedSigner<RsaPkcs1v15Signature>,
359 canonical_signed_info: &[u8],
360) -> Result<Vec<u8>, SigningKeyError> {
361 let signature = key
362 .try_sign_with_rng(&mut SysRng, canonical_signed_info)
363 .map_err(|_| SigningKeyError::SigningFailed)?;
364 Ok(signature.to_vec())
365}
366
367pub struct EcdsaP256SigningKey {
369 key: P256SigningKey,
370}
371
372impl EcdsaP256SigningKey {
373 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
375 let private_key_der = parse_private_key_pem(private_key_pem)?;
376 Self::from_pkcs8_der(&private_key_der)
377 }
378
379 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
381 let key = P256SigningKey::from_pkcs8_der(private_key_der)
382 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
383 Ok(Self { key })
384 }
385}
386
387impl SigningKey for EcdsaP256SigningKey {
388 fn sign(
389 &self,
390 algorithm: SignatureAlgorithm,
391 canonical_signed_info: &[u8],
392 ) -> Result<Vec<u8>, SigningKeyError> {
393 if algorithm != SignatureAlgorithm::EcdsaP256Sha256 {
394 return Err(SigningKeyError::UnsupportedAlgorithm {
395 uri: algorithm.uri().to_string(),
396 });
397 }
398 let signature: P256Signature = self
399 .key
400 .try_sign(canonical_signed_info)
401 .map_err(|_| SigningKeyError::SigningFailed)?;
402 Ok(signature.to_bytes().to_vec())
403 }
404
405 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
406 let verifying_key = self.key.verifying_key();
407 let spki_der = verifying_key
408 .to_public_key_der()
409 .map(|doc| doc.as_bytes().to_vec())
410 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
411 Ok(SigningPublicKeyInfo::Ec {
412 spki_der,
413 curve_oid: "1.2.840.10045.3.1.7",
414 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
415 })
416 }
417}
418
419pub struct EcdsaP384SigningKey {
421 key: P384SigningKey,
422}
423
424impl EcdsaP384SigningKey {
425 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
427 let private_key_der = parse_private_key_pem(private_key_pem)?;
428 Self::from_pkcs8_der(&private_key_der)
429 }
430
431 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
433 let key = P384SigningKey::from_pkcs8_der(private_key_der)
434 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
435 Ok(Self { key })
436 }
437}
438
439impl SigningKey for EcdsaP384SigningKey {
440 fn sign(
441 &self,
442 algorithm: SignatureAlgorithm,
443 canonical_signed_info: &[u8],
444 ) -> Result<Vec<u8>, SigningKeyError> {
445 if algorithm != SignatureAlgorithm::EcdsaP384Sha384 {
446 return Err(SigningKeyError::UnsupportedAlgorithm {
447 uri: algorithm.uri().to_string(),
448 });
449 }
450 let signature: P384Signature = self
451 .key
452 .try_sign(canonical_signed_info)
453 .map_err(|_| SigningKeyError::SigningFailed)?;
454 Ok(signature.to_bytes().to_vec())
455 }
456
457 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
458 let verifying_key = self.key.verifying_key();
459 let spki_der = verifying_key
460 .to_public_key_der()
461 .map(|doc| doc.as_bytes().to_vec())
462 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
463 Ok(SigningPublicKeyInfo::Ec {
464 spki_der,
465 curve_oid: "1.3.132.0.34",
466 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
467 })
468 }
469}
470
471pub struct SignContext<'a> {
473 signing_key: &'a dyn SigningKey,
474 key_info_writer: Option<&'a dyn KeyInfoWriter>,
475 transform_options: TransformOptions,
476}
477
478impl<'a> SignContext<'a> {
479 pub fn new(signing_key: &'a dyn SigningKey) -> Self {
481 Self {
482 signing_key,
483 key_info_writer: None,
484 transform_options: TransformOptions::default(),
485 }
486 }
487
488 #[must_use]
490 pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
491 self.key_info_writer = Some(writer);
492 self
493 }
494
495 #[must_use]
501 pub fn xpath_here_semantics(mut self, semantics: XPathHereSemantics) -> Self {
502 self.transform_options = self.transform_options.xpath_here_semantics(semantics);
503 self
504 }
505
506 pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
513 let with_digests = fill_reference_digest_values_with_options(xml, self.transform_options)?;
514 let (algorithm, canonical_signed_info) = canonicalize_signed_info(&with_digests)?;
515 let signature_value = self.signing_key.sign(algorithm, &canonical_signed_info)?;
516 let signature_b64 = base64::engine::general_purpose::STANDARD.encode(signature_value);
517 let signed = fill_signature_value(&with_digests, &signature_b64)?;
518 if let Some(writer) = self.key_info_writer {
519 let key_info_content = writer.write_key_info(self.signing_key)?;
520 Ok(fill_key_info(&signed, &key_info_content)?)
521 } else {
522 Ok(signed)
523 }
524 }
525
526 pub fn sign_with_builder(
528 &self,
529 xml: &str,
530 builder: &SignatureBuilder,
531 ) -> Result<String, SigningError> {
532 let template = builder.build_template()?;
533 let templated = append_signature_to_root(xml, &template)?;
534 self.sign_template(&templated)
535 }
536}
537
538#[derive(Debug)]
539struct SigningReference {
540 uri: String,
541 transforms: Vec<Transform>,
542 digest_method: DigestAlgorithm,
543}
544
545pub fn compute_reference_digest_values(
552 xml: &str,
553) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
554 compute_reference_digest_values_with_options(xml, TransformOptions::default())
555}
556
557fn compute_reference_digest_values_with_options(
558 xml: &str,
559 transform_options: TransformOptions,
560) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
561 let doc = Document::parse(xml)?;
562 let signature = find_signing_signature_node(&doc)?;
563 let signed_info = find_required_child(signature, "SignedInfo")?;
564 let references = parse_signing_references(signed_info)?;
565 let resolver = UriReferenceResolver::new(&doc);
566 let execution_budget = TransformExecutionBudget::default();
567
568 references
569 .into_iter()
570 .enumerate()
571 .map(|(index, reference)| {
572 let initial_data = resolver.dereference_with_budget(
573 &reference.uri,
574 execution_budget.node_set_materialization(),
575 )?;
576 let pre_digest = execute_transforms_with_options_and_budget(
577 signature,
578 initial_data,
579 &reference.transforms,
580 transform_options,
581 &execution_budget,
582 )?;
583 let digest = compute_digest(reference.digest_method, &pre_digest);
584 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
585 Ok(ComputedReferenceDigest {
586 index,
587 uri: reference.uri,
588 digest_method: reference.digest_method,
589 digest_value,
590 })
591 })
592 .collect()
593}
594
595pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
602 fill_reference_digest_values_with_options(xml, TransformOptions::default())
603}
604
605fn fill_reference_digest_values_with_options(
606 xml: &str,
607 transform_options: TransformOptions,
608) -> Result<String, SigningDigestError> {
609 let digest_values = compute_reference_digest_values_with_options(xml, transform_options)?
610 .into_iter()
611 .map(|digest| digest.digest_value);
612 Ok(fill_signed_info_digest_values(xml, digest_values)?)
613}
614
615fn canonicalize_signed_info(xml: &str) -> Result<(SignatureAlgorithm, Vec<u8>), SigningError> {
616 let doc = Document::parse(xml).map_err(SigningDigestError::XmlParse)?;
617 let signature = find_signing_signature_node(&doc).map_err(SigningError::Digest)?;
618 let signed_info_node =
619 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
620 let signed_info = parse_signed_info(signed_info_node)?;
621 let signed_info_subtree: HashSet<_> = signed_info_node
622 .descendants()
623 .map(|node: Node<'_, '_>| node.id())
624 .collect();
625 let mut canonical_signed_info = Vec::new();
626 canonicalize(
627 &doc,
628 Some(&|node| signed_info_subtree.contains(&node.id())),
629 &signed_info.c14n_method,
630 &mut canonical_signed_info,
631 )?;
632 Ok((signed_info.signature_method, canonical_signed_info))
633}
634
635fn parse_private_key_pem(private_key_pem: &str) -> Result<Vec<u8>, SigningKeyError> {
636 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
637 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
638 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
639 return Err(SigningKeyError::InvalidKeyPem);
640 }
641 if pem.label != "PRIVATE KEY" {
642 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
643 }
644 Ok(pem.contents)
645}
646
647fn find_signing_signature_node<'a>(
648 doc: &'a Document<'a>,
649) -> Result<Node<'a, 'a>, SigningDigestError> {
650 doc.descendants()
651 .rfind(|node| {
652 node.is_element()
653 && node.tag_name().name() == "Signature"
654 && node.tag_name().namespace() == Some(XMLDSIG_NS)
655 })
656 .ok_or(SigningDigestError::MissingElement {
657 element: "Signature",
658 })
659}
660
661fn parse_signing_references(
662 signed_info: Node<'_, '_>,
663) -> Result<Vec<SigningReference>, SigningDigestError> {
664 verify_ds_element(signed_info, "SignedInfo")?;
665 let mut children = element_children(signed_info);
666
667 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
668 element: "CanonicalizationMethod",
669 })?;
670 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
671 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
672
673 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
674 element: "SignatureMethod",
675 })?;
676 verify_ds_element(signature_method_node, "SignatureMethod")?;
677 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
678
679 let mut references = Vec::new();
680 let mut xpath_budget = XPathSignatureParseBudget::default();
681 for child in children {
682 verify_ds_element(child, "Reference")?;
683 if references.len() == MAX_REFERENCES_PER_SIGNATURE {
684 return Err(SigningDigestError::InvalidStructure(format!(
685 "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
686 )));
687 }
688 references.push(parse_signing_reference(child, &mut xpath_budget)?);
689 }
690 if references.is_empty() {
691 return Err(SigningDigestError::MissingElement {
692 element: "Reference",
693 });
694 }
695 Ok(references)
696}
697
698fn parse_signing_reference(
699 reference_node: Node<'_, '_>,
700 xpath_budget: &mut XPathSignatureParseBudget,
701) -> Result<SigningReference, SigningDigestError> {
702 let uri = reference_node
703 .attribute("URI")
704 .ok_or_else(|| {
705 SigningDigestError::InvalidStructure(
706 "signing Reference must include URI attribute".into(),
707 )
708 })?
709 .to_string();
710 let mut children = element_children(reference_node);
711
712 let mut transforms = Vec::new();
713 let mut next = children.next().ok_or(SigningDigestError::MissingElement {
714 element: "DigestMethod",
715 })?;
716 if next.tag_name().name() == "Transforms" && next.tag_name().namespace() == Some(XMLDSIG_NS) {
717 transforms = parse_transforms_with_budget(next, xpath_budget)?;
718 next = children.next().ok_or(SigningDigestError::MissingElement {
719 element: "DigestMethod",
720 })?;
721 }
722
723 verify_ds_element(next, "DigestMethod")?;
724 let digest_uri = required_algorithm_attr(next, "DigestMethod")?;
725 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
726 SigningDigestError::UnsupportedAlgorithm {
727 uri: digest_uri.to_string(),
728 }
729 })?;
730 if !digest_method.signing_allowed() {
731 return Err(SigningDigestError::SigningAlgorithmDisabled {
732 uri: digest_method.uri(),
733 });
734 }
735
736 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
737 element: "DigestValue",
738 })?;
739 verify_ds_element(digest_value_node, "DigestValue")?;
740
741 if let Some(unexpected) = children.next() {
742 return Err(SigningDigestError::InvalidStructure(format!(
743 "unexpected element <{}> after <DigestValue> in <Reference>",
744 unexpected.tag_name().name()
745 )));
746 }
747
748 Ok(SigningReference {
749 uri,
750 transforms,
751 digest_method,
752 })
753}
754
755fn find_required_child<'a>(
756 parent: Node<'a, 'a>,
757 child_name: &'static str,
758) -> Result<Node<'a, 'a>, SigningDigestError> {
759 parent
760 .children()
761 .find(|node| {
762 node.is_element()
763 && node.tag_name().name() == child_name
764 && node.tag_name().namespace() == Some(XMLDSIG_NS)
765 })
766 .ok_or(SigningDigestError::MissingElement {
767 element: child_name,
768 })
769}
770
771fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
772 node.children().filter(Node::is_element)
773}
774
775fn verify_ds_element(
776 node: Node<'_, '_>,
777 expected_name: &'static str,
778) -> Result<(), SigningDigestError> {
779 if !node.is_element() {
780 return Err(SigningDigestError::InvalidStructure(format!(
781 "expected element <{expected_name}>, got non-element node"
782 )));
783 }
784 let tag = node.tag_name();
785 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
786 return Err(SigningDigestError::InvalidStructure(format!(
787 "expected <ds:{expected_name}>, got <{}>",
788 tag.name()
789 )));
790 }
791 Ok(())
792}
793
794fn required_algorithm_attr<'a>(
795 node: Node<'a, 'a>,
796 element_name: &'static str,
797) -> Result<&'a str, SigningDigestError> {
798 node.attribute("Algorithm").ok_or_else(|| {
799 SigningDigestError::InvalidStructure(format!(
800 "missing Algorithm attribute on <{element_name}>"
801 ))
802 })
803}