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::{SignatureAlgorithm, XMLDSIG_NS, parse_signed_info};
32use super::transforms::{Transform, execute_transforms, parse_transforms};
33use super::types::TransformError;
34use super::uri::UriReferenceResolver;
35
36#[derive(Debug, Clone, PartialEq, Eq)]
38#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
39pub struct ComputedReferenceDigest {
40 pub index: usize,
42 pub uri: String,
44 pub digest_method: DigestAlgorithm,
46 pub digest_value: String,
48}
49
50#[derive(Debug, thiserror::Error)]
52pub enum SigningDigestError {
53 #[error("XML parse error: {0}")]
55 XmlParse(#[from] roxmltree::Error),
56
57 #[error("missing required element: <{element}>")]
59 MissingElement {
60 element: &'static str,
62 },
63
64 #[error("invalid signing template: {0}")]
66 InvalidStructure(String),
67
68 #[error("unsupported digest algorithm: {uri}")]
70 UnsupportedAlgorithm {
71 uri: String,
73 },
74
75 #[error("digest algorithm is disabled for signing: {uri}")]
77 SigningAlgorithmDisabled {
78 uri: &'static str,
80 },
81
82 #[error("reference processing error: {0}")]
84 Transform(#[from] TransformError),
85
86 #[error("XML mutation error: {0}")]
88 XmlMutation(#[from] XmlMutationError),
89}
90
91#[derive(Debug, thiserror::Error)]
93pub enum SigningError {
94 #[error("signing digest pass failed: {0}")]
96 Digest(#[from] SigningDigestError),
97
98 #[error("failed to parse SignedInfo after digest fill: {0}")]
100 ParseSignedInfo(#[from] super::parse::ParseError),
101
102 #[error("SignedInfo canonicalization failed: {0}")]
104 Canonicalization(#[from] crate::c14n::C14nError),
105
106 #[error("signing key error: {0}")]
108 Key(#[from] SigningKeyError),
109
110 #[error("XML mutation error: {0}")]
112 XmlMutation(#[from] XmlMutationError),
113
114 #[error("KeyInfo writer error: {0}")]
116 KeyInfo(#[from] KeyInfoWriteError),
117
118 #[error("signature template error: {0}")]
120 Template(#[from] SignatureBuilderError),
121}
122
123#[derive(Debug, thiserror::Error)]
125#[non_exhaustive]
126pub enum SigningKeyError {
127 #[error("invalid PEM private key")]
129 InvalidKeyPem,
130
131 #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
133 InvalidKeyFormat {
134 label: String,
136 },
137
138 #[error("invalid PKCS#8 private key DER")]
140 InvalidKeyDer,
141
142 #[error("signing key does not support algorithm: {uri}")]
144 UnsupportedAlgorithm {
145 uri: String,
147 },
148
149 #[error("private-key signing operation failed")]
151 SigningFailed,
152
153 #[error("failed to encode signing public key as SPKI DER")]
155 PublicKeyEncodingFailed,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160#[non_exhaustive]
161pub enum SigningPublicKeyInfo {
162 Rsa {
164 spki_der: Vec<u8>,
166 modulus: Vec<u8>,
168 exponent: Vec<u8>,
170 },
171 Ec {
173 spki_der: Vec<u8>,
175 curve_oid: &'static str,
177 public_key: Vec<u8>,
179 },
180}
181
182impl SigningPublicKeyInfo {
183 #[must_use]
185 pub fn spki_der(&self) -> &[u8] {
186 match self {
187 Self::Rsa { spki_der, .. } | Self::Ec { spki_der, .. } => spki_der,
188 }
189 }
190}
191
192pub trait SigningKey {
194 fn sign(
196 &self,
197 algorithm: SignatureAlgorithm,
198 canonical_signed_info: &[u8],
199 ) -> Result<Vec<u8>, SigningKeyError>;
200
201 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
203}
204
205pub trait KeyInfoWriter {
207 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
209}
210
211#[derive(Debug, thiserror::Error)]
213#[non_exhaustive]
214pub enum KeyInfoWriteError {
215 #[error("invalid PEM certificate")]
217 InvalidCertificatePem,
218
219 #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
221 InvalidCertificateFormat {
222 label: String,
224 },
225
226 #[error("invalid X.509 certificate DER")]
228 InvalidCertificateDer,
229
230 #[error("signing key public-key extraction failed: {0}")]
232 SigningKey(#[from] SigningKeyError),
233
234 #[error("X.509 certificate public key does not match signing key")]
236 CertificateKeyMismatch,
237}
238
239pub struct X509CertificateKeyInfoWriter {
241 certificate_der: Vec<u8>,
242}
243
244impl X509CertificateKeyInfoWriter {
245 pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
247 let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
248 .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
249 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
250 return Err(KeyInfoWriteError::InvalidCertificatePem);
251 }
252 if pem.label != "CERTIFICATE" {
253 return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
254 }
255 Self::from_der(&pem.contents)
256 }
257
258 pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
260 let (rest, _) = x509_parser::certificate::X509Certificate::from_der(certificate_der)
261 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
262 if !rest.is_empty() {
263 return Err(KeyInfoWriteError::InvalidCertificateDer);
264 }
265 Ok(Self {
266 certificate_der: certificate_der.to_vec(),
267 })
268 }
269}
270
271impl KeyInfoWriter for X509CertificateKeyInfoWriter {
272 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
273 let (rest, certificate) =
274 x509_parser::certificate::X509Certificate::from_der(&self.certificate_der)
275 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
276 if !rest.is_empty() {
277 return Err(KeyInfoWriteError::InvalidCertificateDer);
278 }
279 let signing_public_key = signing_key.public_key_info()?;
280 if certificate.public_key().raw != signing_public_key.spki_der() {
281 return Err(KeyInfoWriteError::CertificateKeyMismatch);
282 }
283
284 let certificate_b64 =
285 base64::engine::general_purpose::STANDARD.encode(&self.certificate_der);
286 Ok(format!(
287 "<X509Data xmlns=\"{XMLDSIG_NS}\"><X509Certificate>{certificate_b64}</X509Certificate></X509Data>"
288 ))
289 }
290}
291
292pub struct RsaSigningKey {
294 key: RsaPrivateKey,
295}
296
297impl RsaSigningKey {
298 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
300 let private_key_der = parse_private_key_pem(private_key_pem)?;
301 Self::from_pkcs8_der(&private_key_der)
302 }
303
304 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
306 let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
307 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
308 Ok(Self { key })
309 }
310}
311
312impl SigningKey for RsaSigningKey {
313 fn sign(
314 &self,
315 algorithm: SignatureAlgorithm,
316 canonical_signed_info: &[u8],
317 ) -> Result<Vec<u8>, SigningKeyError> {
318 match algorithm {
319 SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
320 RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
321 canonical_signed_info,
322 ),
323 SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
324 RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
325 canonical_signed_info,
326 ),
327 SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
328 RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
329 canonical_signed_info,
330 ),
331 _ => Err(SigningKeyError::UnsupportedAlgorithm {
332 uri: algorithm.uri().to_string(),
333 }),
334 }
335 }
336
337 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
338 let public_key = self.key.to_public_key();
339 let spki_der = public_key
340 .to_public_key_der()
341 .map(|doc| doc.as_bytes().to_vec())
342 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
343 Ok(SigningPublicKeyInfo::Rsa {
344 spki_der,
345 modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
346 exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
347 })
348 }
349}
350
351fn sign_rsa_pkcs1v15_with_rng(
352 key: impl RandomizedSigner<RsaPkcs1v15Signature>,
353 canonical_signed_info: &[u8],
354) -> Result<Vec<u8>, SigningKeyError> {
355 let signature = key
356 .try_sign_with_rng(&mut SysRng, canonical_signed_info)
357 .map_err(|_| SigningKeyError::SigningFailed)?;
358 Ok(signature.to_vec())
359}
360
361pub struct EcdsaP256SigningKey {
363 key: P256SigningKey,
364}
365
366impl EcdsaP256SigningKey {
367 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
369 let private_key_der = parse_private_key_pem(private_key_pem)?;
370 Self::from_pkcs8_der(&private_key_der)
371 }
372
373 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
375 let key = P256SigningKey::from_pkcs8_der(private_key_der)
376 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
377 Ok(Self { key })
378 }
379}
380
381impl SigningKey for EcdsaP256SigningKey {
382 fn sign(
383 &self,
384 algorithm: SignatureAlgorithm,
385 canonical_signed_info: &[u8],
386 ) -> Result<Vec<u8>, SigningKeyError> {
387 if algorithm != SignatureAlgorithm::EcdsaP256Sha256 {
388 return Err(SigningKeyError::UnsupportedAlgorithm {
389 uri: algorithm.uri().to_string(),
390 });
391 }
392 let signature: P256Signature = self
393 .key
394 .try_sign(canonical_signed_info)
395 .map_err(|_| SigningKeyError::SigningFailed)?;
396 Ok(signature.to_bytes().to_vec())
397 }
398
399 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
400 let verifying_key = self.key.verifying_key();
401 let spki_der = verifying_key
402 .to_public_key_der()
403 .map(|doc| doc.as_bytes().to_vec())
404 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
405 Ok(SigningPublicKeyInfo::Ec {
406 spki_der,
407 curve_oid: "1.2.840.10045.3.1.7",
408 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
409 })
410 }
411}
412
413pub struct EcdsaP384SigningKey {
415 key: P384SigningKey,
416}
417
418impl EcdsaP384SigningKey {
419 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
421 let private_key_der = parse_private_key_pem(private_key_pem)?;
422 Self::from_pkcs8_der(&private_key_der)
423 }
424
425 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
427 let key = P384SigningKey::from_pkcs8_der(private_key_der)
428 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
429 Ok(Self { key })
430 }
431}
432
433impl SigningKey for EcdsaP384SigningKey {
434 fn sign(
435 &self,
436 algorithm: SignatureAlgorithm,
437 canonical_signed_info: &[u8],
438 ) -> Result<Vec<u8>, SigningKeyError> {
439 if algorithm != SignatureAlgorithm::EcdsaP384Sha384 {
440 return Err(SigningKeyError::UnsupportedAlgorithm {
441 uri: algorithm.uri().to_string(),
442 });
443 }
444 let signature: P384Signature = self
445 .key
446 .try_sign(canonical_signed_info)
447 .map_err(|_| SigningKeyError::SigningFailed)?;
448 Ok(signature.to_bytes().to_vec())
449 }
450
451 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
452 let verifying_key = self.key.verifying_key();
453 let spki_der = verifying_key
454 .to_public_key_der()
455 .map(|doc| doc.as_bytes().to_vec())
456 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
457 Ok(SigningPublicKeyInfo::Ec {
458 spki_der,
459 curve_oid: "1.3.132.0.34",
460 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
461 })
462 }
463}
464
465pub struct SignContext<'a> {
467 signing_key: &'a dyn SigningKey,
468 key_info_writer: Option<&'a dyn KeyInfoWriter>,
469}
470
471impl<'a> SignContext<'a> {
472 pub fn new(signing_key: &'a dyn SigningKey) -> Self {
474 Self {
475 signing_key,
476 key_info_writer: None,
477 }
478 }
479
480 #[must_use]
482 pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
483 self.key_info_writer = Some(writer);
484 self
485 }
486
487 pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
494 let with_digests = fill_reference_digest_values(xml)?;
495 let (algorithm, canonical_signed_info) = canonicalize_signed_info(&with_digests)?;
496 let signature_value = self.signing_key.sign(algorithm, &canonical_signed_info)?;
497 let signature_b64 = base64::engine::general_purpose::STANDARD.encode(signature_value);
498 let signed = fill_signature_value(&with_digests, &signature_b64)?;
499 if let Some(writer) = self.key_info_writer {
500 let key_info_content = writer.write_key_info(self.signing_key)?;
501 Ok(fill_key_info(&signed, &key_info_content)?)
502 } else {
503 Ok(signed)
504 }
505 }
506
507 pub fn sign_with_builder(
509 &self,
510 xml: &str,
511 builder: &SignatureBuilder,
512 ) -> Result<String, SigningError> {
513 let template = builder.build_template()?;
514 let templated = append_signature_to_root(xml, &template)?;
515 self.sign_template(&templated)
516 }
517}
518
519#[derive(Debug)]
520struct SigningReference {
521 uri: String,
522 transforms: Vec<Transform>,
523 digest_method: DigestAlgorithm,
524}
525
526pub fn compute_reference_digest_values(
533 xml: &str,
534) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
535 let doc = Document::parse(xml)?;
536 let signature = find_signing_signature_node(&doc)?;
537 let signed_info = find_required_child(signature, "SignedInfo")?;
538 let references = parse_signing_references(signed_info)?;
539 let resolver = UriReferenceResolver::new(&doc);
540
541 references
542 .into_iter()
543 .enumerate()
544 .map(|(index, reference)| {
545 let initial_data = resolver.dereference(&reference.uri)?;
546 let pre_digest = execute_transforms(signature, initial_data, &reference.transforms)?;
547 let digest = compute_digest(reference.digest_method, &pre_digest);
548 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
549 Ok(ComputedReferenceDigest {
550 index,
551 uri: reference.uri,
552 digest_method: reference.digest_method,
553 digest_value,
554 })
555 })
556 .collect()
557}
558
559pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
566 let digest_values = compute_reference_digest_values(xml)?
567 .into_iter()
568 .map(|digest| digest.digest_value);
569 Ok(fill_signed_info_digest_values(xml, digest_values)?)
570}
571
572fn canonicalize_signed_info(xml: &str) -> Result<(SignatureAlgorithm, Vec<u8>), SigningError> {
573 let doc = Document::parse(xml).map_err(SigningDigestError::XmlParse)?;
574 let signature = find_signing_signature_node(&doc).map_err(SigningError::Digest)?;
575 let signed_info_node =
576 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
577 let signed_info = parse_signed_info(signed_info_node)?;
578 let signed_info_subtree: HashSet<_> = signed_info_node
579 .descendants()
580 .map(|node: Node<'_, '_>| node.id())
581 .collect();
582 let mut canonical_signed_info = Vec::new();
583 canonicalize(
584 &doc,
585 Some(&|node| signed_info_subtree.contains(&node.id())),
586 &signed_info.c14n_method,
587 &mut canonical_signed_info,
588 )?;
589 Ok((signed_info.signature_method, canonical_signed_info))
590}
591
592fn parse_private_key_pem(private_key_pem: &str) -> Result<Vec<u8>, SigningKeyError> {
593 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
594 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
595 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
596 return Err(SigningKeyError::InvalidKeyPem);
597 }
598 if pem.label != "PRIVATE KEY" {
599 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
600 }
601 Ok(pem.contents)
602}
603
604fn find_signing_signature_node<'a>(
605 doc: &'a Document<'a>,
606) -> Result<Node<'a, 'a>, SigningDigestError> {
607 doc.descendants()
608 .rfind(|node| {
609 node.is_element()
610 && node.tag_name().name() == "Signature"
611 && node.tag_name().namespace() == Some(XMLDSIG_NS)
612 })
613 .ok_or(SigningDigestError::MissingElement {
614 element: "Signature",
615 })
616}
617
618fn parse_signing_references(
619 signed_info: Node<'_, '_>,
620) -> Result<Vec<SigningReference>, SigningDigestError> {
621 verify_ds_element(signed_info, "SignedInfo")?;
622 let mut children = element_children(signed_info);
623
624 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
625 element: "CanonicalizationMethod",
626 })?;
627 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
628 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
629
630 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
631 element: "SignatureMethod",
632 })?;
633 verify_ds_element(signature_method_node, "SignatureMethod")?;
634 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
635
636 let mut references = Vec::new();
637 for child in children {
638 verify_ds_element(child, "Reference")?;
639 references.push(parse_signing_reference(child)?);
640 }
641 if references.is_empty() {
642 return Err(SigningDigestError::MissingElement {
643 element: "Reference",
644 });
645 }
646 Ok(references)
647}
648
649fn parse_signing_reference(
650 reference_node: Node<'_, '_>,
651) -> Result<SigningReference, SigningDigestError> {
652 let uri = reference_node
653 .attribute("URI")
654 .ok_or_else(|| {
655 SigningDigestError::InvalidStructure(
656 "signing Reference must include URI attribute".into(),
657 )
658 })?
659 .to_string();
660 let mut children = element_children(reference_node);
661
662 let mut transforms = Vec::new();
663 let mut next = children.next().ok_or(SigningDigestError::MissingElement {
664 element: "DigestMethod",
665 })?;
666 if next.tag_name().name() == "Transforms" && next.tag_name().namespace() == Some(XMLDSIG_NS) {
667 transforms = parse_transforms(next)?;
668 next = children.next().ok_or(SigningDigestError::MissingElement {
669 element: "DigestMethod",
670 })?;
671 }
672
673 verify_ds_element(next, "DigestMethod")?;
674 let digest_uri = required_algorithm_attr(next, "DigestMethod")?;
675 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
676 SigningDigestError::UnsupportedAlgorithm {
677 uri: digest_uri.to_string(),
678 }
679 })?;
680 if !digest_method.signing_allowed() {
681 return Err(SigningDigestError::SigningAlgorithmDisabled {
682 uri: digest_method.uri(),
683 });
684 }
685
686 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
687 element: "DigestValue",
688 })?;
689 verify_ds_element(digest_value_node, "DigestValue")?;
690
691 if let Some(unexpected) = children.next() {
692 return Err(SigningDigestError::InvalidStructure(format!(
693 "unexpected element <{}> after <DigestValue> in <Reference>",
694 unexpected.tag_name().name()
695 )));
696 }
697
698 Ok(SigningReference {
699 uri,
700 transforms,
701 digest_method,
702 })
703}
704
705fn find_required_child<'a>(
706 parent: Node<'a, 'a>,
707 child_name: &'static str,
708) -> Result<Node<'a, 'a>, SigningDigestError> {
709 parent
710 .children()
711 .find(|node| {
712 node.is_element()
713 && node.tag_name().name() == child_name
714 && node.tag_name().namespace() == Some(XMLDSIG_NS)
715 })
716 .ok_or(SigningDigestError::MissingElement {
717 element: child_name,
718 })
719}
720
721fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
722 node.children().filter(Node::is_element)
723}
724
725fn verify_ds_element(
726 node: Node<'_, '_>,
727 expected_name: &'static str,
728) -> Result<(), SigningDigestError> {
729 if !node.is_element() {
730 return Err(SigningDigestError::InvalidStructure(format!(
731 "expected element <{expected_name}>, got non-element node"
732 )));
733 }
734 let tag = node.tag_name();
735 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
736 return Err(SigningDigestError::InvalidStructure(format!(
737 "expected <ds:{expected_name}>, got <{}>",
738 tag.name()
739 )));
740 }
741 Ok(())
742}
743
744fn required_algorithm_attr<'a>(
745 node: Node<'a, 'a>,
746 element_name: &'static str,
747) -> Result<&'a str, SigningDigestError> {
748 node.attribute("Algorithm").ok_or_else(|| {
749 SigningDigestError::InvalidStructure(format!(
750 "missing Algorithm attribute on <{element_name}>"
751 ))
752 })
753}