1use crate::xml::dom::{Document, Node, NodeId};
9use base64::Engine;
10use hmac::{KeyInit, Mac};
11use p256::ecdsa::{
12 Signature as P256Signature, SigningKey as P256SigningKey, VerifyingKey as P256VerifyingKey,
13};
14use p256::pkcs8::{DecodePrivateKey, EncodePublicKey};
15use p384::ecdsa::{
16 Signature as P384Signature, SigningKey as P384SigningKey, VerifyingKey as P384VerifyingKey,
17};
18use p521::ecdsa::{
19 Signature as P521Signature, SigningKey as P521SigningKey, VerifyingKey as P521VerifyingKey,
20};
21use rsa::RsaPrivateKey;
22use rsa::pkcs1v15::Signature as RsaPkcs1v15Signature;
23use rsa::pkcs1v15::SigningKey as RsaPkcs1v15SigningKey;
24use rsa::signature::{RandomizedSigner, SignatureEncoding};
25use rsa::traits::PublicKeyParts;
26use sha1::Sha1;
27use sha2::{Sha224, Sha256, Sha384, Sha512};
28use signature::hazmat::{PrehashSigner, RandomizedPrehashSigner};
29use std::{collections::HashSet, ops::Range};
30use x509_parser::prelude::FromDer;
31use zeroize::Zeroizing;
32
33use crate::c14n::canonicalize_bounded_with_xml_base_budget;
34
35use super::builder::{SignatureBuilder, SignatureBuilderError};
36use super::digest::DigestAlgorithm;
37use super::mutation::{
38 XmlMutationError, fill_signed_info_digest_values_at_index_with_budget,
39 fill_signed_info_digest_values_with_budget, merge_key_info_source_at_index_with_budget,
40 padded_base64_len_for_xml,
41};
42use super::parse::{
43 EC_P256_OID, EC_P384_OID, EC_P521_OID, MAX_REFERENCES_PER_SIGNATURE, SignatureAlgorithm,
44 XMLDSIG_NS, parse_signed_info_with_xpath_budget,
45};
46use super::signature::{encode_ecdsa_signature_as_der, maximum_ecdsa_der_signature_len};
47use super::transforms::{
48 Transform, TransformExecutionBudget, TransformOptions, XPathHereSemantics,
49 XPathSignatureParseBudget, execute_transforms_with_dependency_nodes,
50 execute_transforms_with_options_and_budget, map_c14n_resource_policy_violation,
51 parse_transforms_with_budget, validate_signing_transform_policy,
52};
53use super::types::TransformError;
54use super::uri::{UriReferenceResolver, validate_signing_reference_uri};
55use super::verify::parse_signature_children;
56use crate::document::{DocumentParseSettings, XmlDocument, XmlDocumentError, XmlParseWorkBudget};
57
58#[derive(Debug, Clone, PartialEq, Eq)]
60#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
61pub struct ComputedReferenceDigest {
62 pub index: usize,
64 pub uri: String,
66 pub digest_method: DigestAlgorithm,
68 pub digest_value: String,
70}
71
72#[derive(Debug, thiserror::Error)]
74pub enum SigningDigestError {
75 #[error("cryptographic provider error: {0}")]
77 Provider(#[from] crate::provider::ProviderError),
78
79 #[error("signing policy violation: {0}")]
84 Policy(#[from] crate::policy::PolicyViolation),
85
86 #[error("XML parse error: {0}")]
88 XmlParse(#[from] crate::xml::dom::ParseError),
89
90 #[error("XML document error: {0}")]
92 Document(#[from] XmlDocumentError),
93
94 #[error("missing required element: <{element}>")]
96 MissingElement {
97 element: &'static str,
99 },
100
101 #[error("invalid signing template: {0}")]
103 InvalidStructure(String),
104
105 #[error("unsupported digest algorithm: {uri}")]
107 UnsupportedAlgorithm {
108 uri: String,
110 },
111
112 #[error("digest algorithm is disabled for signing: {uri}")]
114 SigningAlgorithmDisabled {
115 uri: &'static str,
117 },
118
119 #[error("reference processing error: {0}")]
121 Transform(#[from] TransformError),
122
123 #[error("XML mutation error: {0}")]
125 XmlMutation(#[from] XmlMutationError),
126}
127
128#[derive(Debug, thiserror::Error)]
130pub enum SigningError {
131 #[error("signing policy violation: {0}")]
133 Policy(#[from] crate::policy::PolicyViolation),
134
135 #[error("signing digest pass failed: {0}")]
137 Digest(SigningDigestError),
138
139 #[error("failed to parse SignedInfo after digest fill: {0}")]
141 ParseSignedInfo(super::parse::ParseError),
142
143 #[error("SignedInfo canonicalization failed: {0}")]
145 Canonicalization(#[from] crate::c14n::C14nError),
146
147 #[error("signing key error: {0}")]
149 Key(#[from] SigningKeyError),
150
151 #[error("signature output must be {expected} bytes, got {actual}")]
153 InvalidSignatureOutputLength {
154 expected: usize,
156 actual: usize,
158 },
159
160 #[error("XML mutation error: {0}")]
162 XmlMutation(XmlMutationError),
163
164 #[error("KeyInfo writer error: {0}")]
166 KeyInfo(#[from] KeyInfoWriteError),
167
168 #[error("XML document error: {0}")]
170 Document(#[from] XmlDocumentError),
171
172 #[error("signature template error: {0}")]
174 Template(SignatureBuilderError),
175}
176
177impl From<SigningDigestError> for SigningError {
178 fn from(error: SigningDigestError) -> Self {
179 match error {
180 SigningDigestError::XmlMutation(XmlMutationError::Policy(error)) => Self::Policy(error),
181 SigningDigestError::Policy(error)
182 | SigningDigestError::Transform(TransformError::Policy(error)) => Self::Policy(error),
183 SigningDigestError::Document(error) => Self::Document(error),
184 error => Self::Digest(error),
185 }
186 }
187}
188
189impl From<super::parse::ParseError> for SigningError {
190 fn from(error: super::parse::ParseError) -> Self {
191 match error {
192 super::parse::ParseError::Policy(error)
193 | super::parse::ParseError::Transform(TransformError::Policy(error)) => {
194 Self::Policy(error)
195 }
196 error => Self::ParseSignedInfo(error),
197 }
198 }
199}
200
201impl From<XmlMutationError> for SigningError {
202 fn from(error: XmlMutationError) -> Self {
203 match error {
204 XmlMutationError::Policy(error) => Self::Policy(error),
205 error => Self::XmlMutation(error),
206 }
207 }
208}
209
210impl From<SignatureBuilderError> for SigningError {
211 fn from(error: SignatureBuilderError) -> Self {
212 match error {
213 SignatureBuilderError::Policy(error) => Self::Policy(error),
214 error => Self::Template(error),
215 }
216 }
217}
218
219#[derive(Debug, thiserror::Error)]
221#[non_exhaustive]
222pub enum SigningKeyError {
223 #[error("cryptographic provider error: {0}")]
225 Provider(#[from] crate::provider::ProviderError),
226
227 #[error("invalid PEM private key")]
229 InvalidKeyPem,
230
231 #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
233 InvalidKeyFormat {
234 label: String,
236 },
237
238 #[error("invalid PKCS#8 private key DER")]
240 InvalidKeyDer,
241
242 #[error("signing key does not support algorithm: {uri}")]
244 UnsupportedAlgorithm {
245 uri: String,
247 },
248
249 #[error("private-key signing operation failed")]
251 SigningFailed,
252
253 #[error("failed to encode signing public key as SPKI DER")]
255 PublicKeyEncodingFailed,
256
257 #[error("invalid signing public-key metadata")]
259 InvalidPublicKeyInfo,
260}
261
262#[derive(Debug, Clone, PartialEq, Eq)]
264#[non_exhaustive]
265pub enum SigningPublicKeyInfo {
266 Rsa {
268 spki_der: Vec<u8>,
270 modulus: Vec<u8>,
272 exponent: Vec<u8>,
274 },
275 Ec {
277 spki_der: Vec<u8>,
279 curve_oid: &'static str,
281 public_key: Vec<u8>,
283 },
284 Dsa {
286 spki_der: Vec<u8>,
288 p: Vec<u8>,
290 q: Vec<u8>,
292 g: Vec<u8>,
294 y: Vec<u8>,
296 modulus_bits: usize,
298 component_len: usize,
300 },
301 Hmac {
303 key_bits: usize,
305 },
306}
307
308impl SigningPublicKeyInfo {
309 #[must_use]
311 pub fn spki_der(&self) -> Option<&[u8]> {
312 match self {
313 Self::Rsa { spki_der, .. } | Self::Ec { spki_der, .. } | Self::Dsa { spki_der, .. } => {
314 Some(spki_der)
315 }
316 Self::Hmac { .. } => None,
317 }
318 }
319}
320
321pub fn validate_signing_key(
326 key: &dyn SigningKey,
327 algorithm: SignatureAlgorithm,
328 policy: &crate::policy::SigningPolicy,
329) -> Result<(), SigningError> {
330 policy.resources.validate_key_candidates(1)?;
331 policy.check_signature_algorithm(algorithm)?;
332 expected_signature_output_len(key, algorithm, policy, None).map(|_| ())
333}
334
335fn expected_signature_output_len(
336 key: &dyn SigningKey,
337 algorithm: SignatureAlgorithm,
338 policy: &crate::policy::SigningPolicy,
339 hmac_output_length_bits: Option<usize>,
340) -> Result<usize, SigningError> {
341 let public_key = key.public_key_info()?;
342 let expected = match (algorithm, public_key) {
343 (
344 SignatureAlgorithm::RsaSha1
345 | SignatureAlgorithm::RsaSha224
346 | SignatureAlgorithm::RsaSha256
347 | SignatureAlgorithm::RsaSha384
348 | SignatureAlgorithm::RsaSha512,
349 SigningPublicKeyInfo::Rsa {
350 modulus, exponent, ..
351 },
352 ) => policy
353 .rsa_keys
354 .validate_components("signing", &modulus, &exponent)?,
355 (
356 SignatureAlgorithm::EcdsaSha1
357 | SignatureAlgorithm::EcdsaSha224
358 | SignatureAlgorithm::EcdsaSha256
359 | SignatureAlgorithm::EcdsaSha384
360 | SignatureAlgorithm::EcdsaSha512,
361 SigningPublicKeyInfo::Ec { public_key, .. },
362 ) if public_key.first() == Some(&0x04)
363 && public_key.len() > 1
364 && (public_key.len() - 1).is_multiple_of(2) =>
365 {
366 public_key.len() - 1
369 }
370 (
371 algorithm @ (SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256),
372 SigningPublicKeyInfo::Dsa {
373 modulus_bits,
374 component_len,
375 ..
376 },
377 ) => {
378 policy.dsa_keys.validate_modulus_bits(modulus_bits)?;
379 let required_component_len = algorithm
380 .dsa_component_len()
381 .expect("DSA algorithm matched above");
382 if component_len != required_component_len {
383 return Err(crate::policy::PolicyViolation::InvalidKeyMaterial {
384 operation: "signing",
385 key_type: "DSA",
386 reason: match algorithm {
387 SignatureAlgorithm::DsaSha1 => "DSA-SHA1 requires a 160-bit q parameter",
388 SignatureAlgorithm::DsaSha256 => {
389 "DSA-SHA256 requires a 256-bit q parameter"
390 }
391 _ => unreachable!("DSA algorithm matched above"),
392 },
393 }
394 .into());
395 }
396 component_len.saturating_mul(2)
397 }
398 (
399 SignatureAlgorithm::HmacSha1
400 | SignatureAlgorithm::HmacSha224
401 | SignatureAlgorithm::HmacSha256
402 | SignatureAlgorithm::HmacSha384
403 | SignatureAlgorithm::HmacSha512,
404 SigningPublicKeyInfo::Hmac { key_bits },
405 ) => {
406 policy.hmac.validate_key_bits(key_bits)?;
407 let output_bits = hmac_output_length_bits.unwrap_or(
408 algorithm
409 .hmac_output_bits()
410 .ok_or(SigningKeyError::InvalidPublicKeyInfo)?,
411 );
412 policy.hmac.validate_output(algorithm, output_bits)?;
413 output_bits / 8
414 }
415 (
416 SignatureAlgorithm::RsaSha1
417 | SignatureAlgorithm::RsaSha224
418 | SignatureAlgorithm::RsaSha256
419 | SignatureAlgorithm::RsaSha384
420 | SignatureAlgorithm::RsaSha512,
421 SigningPublicKeyInfo::Ec { .. }
422 | SigningPublicKeyInfo::Dsa { .. }
423 | SigningPublicKeyInfo::Hmac { .. },
424 )
425 | (
426 SignatureAlgorithm::EcdsaSha1
427 | SignatureAlgorithm::EcdsaSha224
428 | SignatureAlgorithm::EcdsaSha256
429 | SignatureAlgorithm::EcdsaSha384
430 | SignatureAlgorithm::EcdsaSha512,
431 SigningPublicKeyInfo::Rsa { .. }
432 | SigningPublicKeyInfo::Dsa { .. }
433 | SigningPublicKeyInfo::Hmac { .. },
434 ) => {
435 return Err(SigningKeyError::UnsupportedAlgorithm {
436 uri: algorithm.uri().to_owned(),
437 }
438 .into());
439 }
440 _ => return Err(SigningKeyError::InvalidPublicKeyInfo.into()),
441 };
442 Ok(expected)
443}
444
445fn validate_signature_output(expected: usize, signature: &[u8]) -> Result<(), SigningError> {
446 if signature.len() != expected {
447 return Err(SigningError::InvalidSignatureOutputLength {
448 expected,
449 actual: signature.len(),
450 });
451 }
452 Ok(())
453}
454
455pub trait SigningKey {
457 fn sign(
459 &self,
460 algorithm: SignatureAlgorithm,
461 canonical_signed_info: &[u8],
462 ) -> Result<Vec<u8>, SigningKeyError>;
463
464 fn sign_with_provider(
469 &self,
470 provider: &dyn crate::provider::CryptoProvider,
471 algorithm: SignatureAlgorithm,
472 canonical_signed_info: &[u8],
473 ) -> Result<Vec<u8>, SigningKeyError> {
474 let _ = provider;
475 self.sign(algorithm, canonical_signed_info)
476 }
477
478 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
480}
481
482pub trait KeyInfoWriter {
484 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
486
487 fn write_key_info_with_provider(
492 &self,
493 signing_key: &dyn SigningKey,
494 provider: &dyn crate::provider::CryptoProvider,
495 ) -> Result<String, KeyInfoWriteError> {
496 let _ = provider;
497 self.write_key_info(signing_key)
498 }
499}
500
501#[derive(Debug, thiserror::Error)]
503#[non_exhaustive]
504pub enum KeyInfoWriteError {
505 #[error("cryptographic provider error: {0}")]
507 Provider(#[from] crate::provider::ProviderError),
508
509 #[error("invalid PEM certificate")]
511 InvalidCertificatePem,
512
513 #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
515 InvalidCertificateFormat {
516 label: String,
518 },
519
520 #[error("invalid X.509 certificate DER")]
522 InvalidCertificateDer,
523
524 #[error("X.509 certificate chain must not be empty")]
526 EmptyCertificateChain,
527
528 #[error("signing key public-key extraction failed: {0}")]
530 SigningKey(#[from] SigningKeyError),
531
532 #[error("signing key has no DER-encodable public key")]
534 MissingPublicKey,
535
536 #[error("signing key cannot be represented as XMLDSig KeyValue")]
538 UnsupportedKeyValue,
539
540 #[error("X.509 certificate public key does not match signing key")]
542 CertificateKeyMismatch,
543}
544
545#[derive(Debug, Clone, Copy, Default)]
547pub struct DerEncodedKeyValueInfoWriter;
548
549impl KeyInfoWriter for DerEncodedKeyValueInfoWriter {
550 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
551 let public_key = signing_key.public_key_info()?;
552 let spki_der = public_key
553 .spki_der()
554 .ok_or(KeyInfoWriteError::MissingPublicKey)?;
555 let encoded = base64::engine::general_purpose::STANDARD.encode(spki_der);
556 Ok(format!(
557 "<dsig11:DEREncodedKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\">{encoded}</dsig11:DEREncodedKeyValue>"
558 ))
559 }
560}
561
562#[derive(Debug, Clone, Copy, Default)]
564pub struct KeyValueInfoWriter;
565
566impl KeyInfoWriter for KeyValueInfoWriter {
567 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
568 let encode = |bytes: &[u8]| base64::engine::general_purpose::STANDARD.encode(bytes);
569 match signing_key.public_key_info()? {
570 SigningPublicKeyInfo::Rsa {
571 modulus, exponent, ..
572 } => Ok(format!(
573 "<ds:KeyValue xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:RSAKeyValue><ds:Modulus>{}</ds:Modulus><ds:Exponent>{}</ds:Exponent></ds:RSAKeyValue></ds:KeyValue>",
574 encode(&modulus),
575 encode(&exponent)
576 )),
577 SigningPublicKeyInfo::Ec {
578 curve_oid,
579 public_key,
580 ..
581 } => Ok(format!(
582 "<ds:KeyValue xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><dsig11:ECKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\"><dsig11:NamedCurve URI=\"urn:oid:{curve_oid}\"/><dsig11:PublicKey>{}</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue>",
583 encode(&public_key)
584 )),
585 SigningPublicKeyInfo::Dsa { p, q, g, y, .. } => Ok(format!(
586 "<ds:KeyValue xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:DSAKeyValue><ds:P>{}</ds:P><ds:Q>{}</ds:Q><ds:G>{}</ds:G><ds:Y>{}</ds:Y></ds:DSAKeyValue></ds:KeyValue>",
587 encode(&p),
588 encode(&q),
589 encode(&g),
590 encode(&y)
591 )),
592 SigningPublicKeyInfo::Hmac { .. } => Err(KeyInfoWriteError::UnsupportedKeyValue),
593 }
594 }
595}
596
597pub struct X509CertificateKeyInfoWriter {
599 certificates_der: Vec<Vec<u8>>,
600}
601
602impl X509CertificateKeyInfoWriter {
603 pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
605 Self::from_pem_chain([certificate_pem])
606 }
607
608 pub fn from_pem_chain<I, S>(certificate_pems: I) -> Result<Self, KeyInfoWriteError>
614 where
615 I: IntoIterator<Item = S>,
616 S: AsRef<str>,
617 {
618 let mut certificates_der = Vec::new();
619 for certificate_pem in certificate_pems {
620 certificates_der.push(parse_certificate_pem(certificate_pem.as_ref())?);
621 }
622 Self::from_der_chain(certificates_der)
623 }
624
625 pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
627 Self::from_der_chain([certificate_der])
628 }
629
630 pub fn from_der_chain<I, B>(certificates_der: I) -> Result<Self, KeyInfoWriteError>
636 where
637 I: IntoIterator<Item = B>,
638 B: AsRef<[u8]>,
639 {
640 let certificates_der = certificates_der
641 .into_iter()
642 .map(|certificate_der| {
643 let certificate_der = certificate_der.as_ref();
644 let (rest, _) =
645 x509_parser::certificate::X509Certificate::from_der(certificate_der)
646 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
647 if !rest.is_empty() {
648 return Err(KeyInfoWriteError::InvalidCertificateDer);
649 }
650 Ok(certificate_der.to_vec())
651 })
652 .collect::<Result<Vec<_>, _>>()?;
653 if certificates_der.is_empty() {
654 return Err(KeyInfoWriteError::EmptyCertificateChain);
655 }
656 Ok(Self { certificates_der })
657 }
658}
659
660fn parse_certificate_pem(certificate_pem: &str) -> Result<Vec<u8>, KeyInfoWriteError> {
661 let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
662 .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
663 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
664 return Err(KeyInfoWriteError::InvalidCertificatePem);
665 }
666 if pem.label != "CERTIFICATE" {
667 return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
668 }
669 Ok(pem.contents)
670}
671
672impl KeyInfoWriter for X509CertificateKeyInfoWriter {
673 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
674 let leaf_der = &self.certificates_der[0];
675 let (rest, certificate) = x509_parser::certificate::X509Certificate::from_der(leaf_der)
676 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
677 if !rest.is_empty() {
678 return Err(KeyInfoWriteError::InvalidCertificateDer);
679 }
680 let signing_public_key = signing_key.public_key_info()?;
681 if signing_public_key.spki_der() != Some(certificate.public_key().raw) {
682 return Err(KeyInfoWriteError::CertificateKeyMismatch);
683 }
684
685 let mut xml = format!("<X509Data xmlns=\"{XMLDSIG_NS}\">");
686 for certificate_der in &self.certificates_der {
687 let certificate_b64 = base64::engine::general_purpose::STANDARD.encode(certificate_der);
688 xml.push_str("<X509Certificate>");
689 xml.push_str(&certificate_b64);
690 xml.push_str("</X509Certificate>");
691 }
692 xml.push_str("</X509Data>");
693 Ok(xml)
694 }
695}
696
697pub struct X509DigestKeyInfoWriter {
699 certificate_der: Vec<u8>,
700 certificate_spki_der: Vec<u8>,
701 digest_algorithm: DigestAlgorithm,
702}
703
704impl X509DigestKeyInfoWriter {
705 pub fn from_der(
707 certificate_der: &[u8],
708 digest_algorithm: DigestAlgorithm,
709 ) -> Result<Self, KeyInfoWriteError> {
710 let (rest, certificate) =
711 x509_parser::certificate::X509Certificate::from_der(certificate_der)
712 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
713 if !rest.is_empty() {
714 return Err(KeyInfoWriteError::InvalidCertificateDer);
715 }
716 Ok(Self {
717 certificate_der: certificate_der.to_vec(),
718 certificate_spki_der: certificate.public_key().raw.to_vec(),
719 digest_algorithm,
720 })
721 }
722
723 pub fn from_pem(
725 certificate_pem: &str,
726 digest_algorithm: DigestAlgorithm,
727 ) -> Result<Self, KeyInfoWriteError> {
728 Self::from_der(&parse_certificate_pem(certificate_pem)?, digest_algorithm)
729 }
730}
731
732impl KeyInfoWriter for X509DigestKeyInfoWriter {
733 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
734 self.write_key_info_with_provider(signing_key, crate::provider::default_provider())
735 }
736
737 fn write_key_info_with_provider(
738 &self,
739 signing_key: &dyn SigningKey,
740 provider: &dyn crate::provider::CryptoProvider,
741 ) -> Result<String, KeyInfoWriteError> {
742 if signing_key.public_key_info()?.spki_der() != Some(self.certificate_spki_der.as_slice()) {
743 return Err(KeyInfoWriteError::CertificateKeyMismatch);
744 }
745 let digest = super::compute_digest_with_provider(
746 provider,
747 self.digest_algorithm,
748 &self.certificate_der,
749 )?;
750 let encoded = base64::engine::general_purpose::STANDARD.encode(digest);
751 Ok(format!(
752 "<ds:X509Data xmlns:ds=\"{XMLDSIG_NS}\"><dsig11:X509Digest xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\" Algorithm=\"{}\">{encoded}</dsig11:X509Digest></ds:X509Data>",
753 self.digest_algorithm.uri()
754 ))
755 }
756}
757
758pub struct RsaSigningKey {
760 key: RsaPrivateKey,
761}
762
763impl RsaSigningKey {
764 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
766 let private_key_der = parse_private_key_pem(private_key_pem)?;
767 Self::from_pkcs8_der(&private_key_der)
768 }
769
770 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
772 let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
773 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
774 Ok(Self { key })
775 }
776
777 pub fn from_pkcs8_encrypted_pem(
779 private_key_pem: &str,
780 password: impl AsRef<[u8]>,
781 ) -> Result<Self, SigningKeyError> {
782 let key = RsaPrivateKey::from_pkcs8_encrypted_pem(private_key_pem, password)
783 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
784 Ok(Self { key })
785 }
786
787 pub fn from_pkcs8_encrypted_der(
789 private_key_der: &[u8],
790 password: impl AsRef<[u8]>,
791 ) -> Result<Self, SigningKeyError> {
792 let key = RsaPrivateKey::from_pkcs8_encrypted_der(private_key_der, password)
793 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
794 Ok(Self { key })
795 }
796}
797
798impl SigningKey for RsaSigningKey {
799 fn sign(
800 &self,
801 algorithm: SignatureAlgorithm,
802 canonical_signed_info: &[u8],
803 ) -> Result<Vec<u8>, SigningKeyError> {
804 self.sign_with_provider(
805 crate::provider::default_provider(),
806 algorithm,
807 canonical_signed_info,
808 )
809 }
810
811 fn sign_with_provider(
812 &self,
813 provider: &dyn crate::provider::CryptoProvider,
814 algorithm: SignatureAlgorithm,
815 canonical_signed_info: &[u8],
816 ) -> Result<Vec<u8>, SigningKeyError> {
817 match algorithm {
818 SignatureAlgorithm::RsaSha1 => sign_rsa_pkcs1v15_with_rng(
819 provider,
820 RsaPkcs1v15SigningKey::<Sha1>::new(self.key.clone()),
821 canonical_signed_info,
822 ),
823 SignatureAlgorithm::RsaSha224 => sign_rsa_pkcs1v15_with_rng(
824 provider,
825 RsaPkcs1v15SigningKey::<Sha224>::new(self.key.clone()),
826 canonical_signed_info,
827 ),
828 SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
829 provider,
830 RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
831 canonical_signed_info,
832 ),
833 SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
834 provider,
835 RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
836 canonical_signed_info,
837 ),
838 SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
839 provider,
840 RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
841 canonical_signed_info,
842 ),
843 _ => Err(SigningKeyError::UnsupportedAlgorithm {
844 uri: algorithm.uri().to_string(),
845 }),
846 }
847 }
848
849 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
850 let public_key = self.key.to_public_key();
851 let spki_der = public_key
852 .to_public_key_der()
853 .map(|doc| doc.as_bytes().to_vec())
854 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
855 Ok(SigningPublicKeyInfo::Rsa {
856 spki_der,
857 modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
858 exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
859 })
860 }
861}
862
863pub struct HmacSigningKey {
867 secret: Zeroizing<Vec<u8>>,
868}
869
870impl HmacSigningKey {
871 pub fn new(secret: impl Into<Vec<u8>>) -> Result<Self, SigningKeyError> {
873 let secret = secret.into();
874 if secret.is_empty() {
875 return Err(SigningKeyError::InvalidKeyDer);
876 }
877 Ok(Self {
878 secret: Zeroizing::new(secret),
879 })
880 }
881}
882
883impl SigningKey for HmacSigningKey {
884 fn sign(
885 &self,
886 algorithm: SignatureAlgorithm,
887 canonical_signed_info: &[u8],
888 ) -> Result<Vec<u8>, SigningKeyError> {
889 macro_rules! sign_hmac {
890 ($digest:ty) => {{
891 let mut mac = hmac::Hmac::<$digest>::new_from_slice(&self.secret)
892 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
893 mac.update(canonical_signed_info);
894 mac.finalize().into_bytes().to_vec()
895 }};
896 }
897 Ok(match algorithm {
898 SignatureAlgorithm::HmacSha1 => sign_hmac!(sha1::Sha1),
899 SignatureAlgorithm::HmacSha224 => sign_hmac!(sha2::Sha224),
900 SignatureAlgorithm::HmacSha256 => sign_hmac!(sha2::Sha256),
901 SignatureAlgorithm::HmacSha384 => sign_hmac!(sha2::Sha384),
902 SignatureAlgorithm::HmacSha512 => sign_hmac!(sha2::Sha512),
903 _ => {
904 return Err(SigningKeyError::UnsupportedAlgorithm {
905 uri: algorithm.uri().to_owned(),
906 });
907 }
908 })
909 }
910
911 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
912 Ok(SigningPublicKeyInfo::Hmac {
913 key_bits: self.secret.len().saturating_mul(8),
914 })
915 }
916}
917
918pub struct DsaSigningKey {
920 key: dsa::SigningKey,
921}
922
923impl DsaSigningKey {
924 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
926 let private_key_der = parse_private_key_pem(private_key_pem)?;
927 Self::from_pkcs8_der(&private_key_der)
928 }
929
930 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
932 let key = dsa::SigningKey::from_pkcs8_der(private_key_der)
933 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
934 Ok(Self { key })
935 }
936
937 pub fn from_pkcs8_encrypted_pem(
939 private_key_pem: &str,
940 password: impl AsRef<[u8]>,
941 ) -> Result<Self, SigningKeyError> {
942 let key = dsa::SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
943 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
944 Ok(Self { key })
945 }
946
947 pub fn from_pkcs8_encrypted_der(
949 private_key_der: &[u8],
950 password: impl AsRef<[u8]>,
951 ) -> Result<Self, SigningKeyError> {
952 let key = dsa::SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
953 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
954 Ok(Self { key })
955 }
956}
957
958impl SigningKey for DsaSigningKey {
959 fn sign(
960 &self,
961 algorithm: SignatureAlgorithm,
962 canonical_signed_info: &[u8],
963 ) -> Result<Vec<u8>, SigningKeyError> {
964 self.sign_with_provider(
965 crate::provider::default_provider(),
966 algorithm,
967 canonical_signed_info,
968 )
969 }
970
971 fn sign_with_provider(
972 &self,
973 provider: &dyn crate::provider::CryptoProvider,
974 algorithm: SignatureAlgorithm,
975 canonical_signed_info: &[u8],
976 ) -> Result<Vec<u8>, SigningKeyError> {
977 let digest_algorithm = match algorithm {
978 SignatureAlgorithm::DsaSha1 => DigestAlgorithm::Sha1,
979 SignatureAlgorithm::DsaSha256 => DigestAlgorithm::Sha256,
980 _ => {
981 return Err(SigningKeyError::UnsupportedAlgorithm {
982 uri: algorithm.uri().to_owned(),
983 });
984 }
985 };
986 let component_len =
987 usize::try_from(self.key.verifying_key().components().q().bits_vartime())
988 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?
989 .div_ceil(8);
990 if algorithm.dsa_component_len() != Some(component_len) {
991 return Err(SigningKeyError::InvalidPublicKeyInfo);
992 }
993 let digest =
994 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
995 let mut rng = crate::provider::ProviderRng(provider);
996 let signature: dsa::Signature = self
997 .key
998 .sign_prehash_with_rng(&mut rng, &digest)
999 .map_err(|_| SigningKeyError::SigningFailed)?;
1000 let mut output = Vec::with_capacity(component_len.saturating_mul(2));
1001 for component in [signature.r(), signature.s()] {
1002 let bytes = component.to_be_bytes_trimmed_vartime();
1003 if bytes.len() > component_len {
1004 return Err(SigningKeyError::SigningFailed);
1005 }
1006 output.resize(output.len() + component_len - bytes.len(), 0);
1007 output.extend_from_slice(bytes.as_ref());
1008 }
1009 Ok(output)
1010 }
1011
1012 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1013 let verifying_key = self.key.verifying_key();
1014 let spki_der = verifying_key
1015 .to_public_key_der()
1016 .map(|doc| doc.as_bytes().to_vec())
1017 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
1018 let modulus_bits = usize::try_from(verifying_key.components().p().bits_vartime())
1019 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?;
1020 let component_len = usize::try_from(verifying_key.components().q().bits_vartime())
1021 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?
1022 .div_ceil(8);
1023 let components = verifying_key.components();
1024 Ok(SigningPublicKeyInfo::Dsa {
1025 spki_der,
1026 p: components.p().to_be_bytes_trimmed_vartime().to_vec(),
1027 q: components.q().to_be_bytes_trimmed_vartime().to_vec(),
1028 g: components.g().to_be_bytes_trimmed_vartime().to_vec(),
1029 y: verifying_key.y().to_be_bytes_trimmed_vartime().to_vec(),
1030 modulus_bits,
1031 component_len,
1032 })
1033 }
1034}
1035
1036fn sign_rsa_pkcs1v15_with_rng(
1037 provider: &dyn crate::provider::CryptoProvider,
1038 key: impl RandomizedSigner<RsaPkcs1v15Signature>,
1039 canonical_signed_info: &[u8],
1040) -> Result<Vec<u8>, SigningKeyError> {
1041 let mut rng = crate::provider::ProviderRng(provider);
1042 let signature = key
1043 .try_sign_with_rng(&mut rng, canonical_signed_info)
1044 .map_err(|_| SigningKeyError::SigningFailed)?;
1045 Ok(signature.to_vec())
1046}
1047
1048fn ecdsa_digest_algorithm(
1049 algorithm: SignatureAlgorithm,
1050) -> Result<DigestAlgorithm, SigningKeyError> {
1051 match algorithm {
1052 SignatureAlgorithm::EcdsaSha1 => Ok(DigestAlgorithm::Sha1),
1053 SignatureAlgorithm::EcdsaSha224 => Ok(DigestAlgorithm::Sha224),
1054 SignatureAlgorithm::EcdsaSha256 => Ok(DigestAlgorithm::Sha256),
1055 SignatureAlgorithm::EcdsaSha384 => Ok(DigestAlgorithm::Sha384),
1056 SignatureAlgorithm::EcdsaSha512 => Ok(DigestAlgorithm::Sha512),
1057 _ => Err(SigningKeyError::UnsupportedAlgorithm {
1058 uri: algorithm.uri().to_owned(),
1059 }),
1060 }
1061}
1062
1063fn sign_ecdsa_with_provider<S, K>(
1064 key: &K,
1065 provider: &dyn crate::provider::CryptoProvider,
1066 algorithm: SignatureAlgorithm,
1067 canonical_signed_info: &[u8],
1068) -> Result<Vec<u8>, SigningKeyError>
1069where
1070 K: PrehashSigner<S>,
1071 S: SignatureEncoding,
1072{
1073 let digest_algorithm = ecdsa_digest_algorithm(algorithm)?;
1074 let prehash =
1075 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
1076 let signature = key
1077 .sign_prehash(&prehash)
1078 .map_err(|_| SigningKeyError::SigningFailed)?;
1079 Ok(signature.to_vec())
1080}
1081
1082trait EcdsaPublicKeyEncoding {
1083 fn spki_der(&self) -> Result<Vec<u8>, SigningKeyError>;
1084 fn uncompressed_sec1(&self) -> Vec<u8>;
1085}
1086
1087macro_rules! impl_ecdsa_public_key_encoding {
1088 ($key:ty) => {
1089 impl EcdsaPublicKeyEncoding for $key {
1090 fn spki_der(&self) -> Result<Vec<u8>, SigningKeyError> {
1091 self.to_public_key_der()
1092 .map(|document| document.as_bytes().to_vec())
1093 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)
1094 }
1095
1096 fn uncompressed_sec1(&self) -> Vec<u8> {
1097 self.to_sec1_point(false).as_bytes().to_vec()
1098 }
1099 }
1100 };
1101}
1102
1103impl_ecdsa_public_key_encoding!(P256VerifyingKey);
1104impl_ecdsa_public_key_encoding!(P384VerifyingKey);
1105impl_ecdsa_public_key_encoding!(P521VerifyingKey);
1106
1107fn ecdsa_public_key_info(
1108 key: &impl EcdsaPublicKeyEncoding,
1109 curve_oid: &'static str,
1110) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1111 Ok(SigningPublicKeyInfo::Ec {
1112 spki_der: key.spki_der()?,
1113 curve_oid,
1114 public_key: key.uncompressed_sec1(),
1115 })
1116}
1117
1118pub struct EcdsaP256SigningKey {
1120 key: P256SigningKey,
1121}
1122
1123impl EcdsaP256SigningKey {
1124 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1126 let key = p256::SecretKey::from_sec1_pem(private_key_pem)
1127 .map(P256SigningKey::from)
1128 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1129 Ok(Self { key })
1130 }
1131
1132 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1134 let key = p256::SecretKey::from_sec1_der(private_key_der)
1135 .map(P256SigningKey::from)
1136 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1137 Ok(Self { key })
1138 }
1139
1140 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1142 let private_key_der = parse_private_key_pem(private_key_pem)?;
1143 Self::from_pkcs8_der(&private_key_der)
1144 }
1145
1146 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1148 let key = P256SigningKey::from_pkcs8_der(private_key_der)
1149 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1150 Ok(Self { key })
1151 }
1152
1153 pub fn from_pkcs8_encrypted_pem(
1155 private_key_pem: &str,
1156 password: impl AsRef<[u8]>,
1157 ) -> Result<Self, SigningKeyError> {
1158 let key = P256SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1159 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1160 Ok(Self { key })
1161 }
1162
1163 pub fn from_pkcs8_encrypted_der(
1165 private_key_der: &[u8],
1166 password: impl AsRef<[u8]>,
1167 ) -> Result<Self, SigningKeyError> {
1168 let key = P256SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1169 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1170 Ok(Self { key })
1171 }
1172}
1173
1174impl SigningKey for EcdsaP256SigningKey {
1175 fn sign(
1176 &self,
1177 algorithm: SignatureAlgorithm,
1178 canonical_signed_info: &[u8],
1179 ) -> Result<Vec<u8>, SigningKeyError> {
1180 self.sign_with_provider(
1181 crate::provider::default_provider(),
1182 algorithm,
1183 canonical_signed_info,
1184 )
1185 }
1186
1187 fn sign_with_provider(
1188 &self,
1189 provider: &dyn crate::provider::CryptoProvider,
1190 algorithm: SignatureAlgorithm,
1191 canonical_signed_info: &[u8],
1192 ) -> Result<Vec<u8>, SigningKeyError> {
1193 sign_ecdsa_with_provider::<P256Signature, _>(
1194 &self.key,
1195 provider,
1196 algorithm,
1197 canonical_signed_info,
1198 )
1199 }
1200
1201 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1202 ecdsa_public_key_info(self.key.verifying_key(), EC_P256_OID)
1203 }
1204}
1205
1206pub struct EcdsaP384SigningKey {
1208 key: P384SigningKey,
1209}
1210
1211impl EcdsaP384SigningKey {
1212 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1214 let key = p384::SecretKey::from_sec1_pem(private_key_pem)
1215 .map(P384SigningKey::from)
1216 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1217 Ok(Self { key })
1218 }
1219
1220 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1222 let key = p384::SecretKey::from_sec1_der(private_key_der)
1223 .map(P384SigningKey::from)
1224 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1225 Ok(Self { key })
1226 }
1227
1228 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1230 let private_key_der = parse_private_key_pem(private_key_pem)?;
1231 Self::from_pkcs8_der(&private_key_der)
1232 }
1233
1234 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1236 let key = P384SigningKey::from_pkcs8_der(private_key_der)
1237 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1238 Ok(Self { key })
1239 }
1240
1241 pub fn from_pkcs8_encrypted_pem(
1243 private_key_pem: &str,
1244 password: impl AsRef<[u8]>,
1245 ) -> Result<Self, SigningKeyError> {
1246 let key = P384SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1247 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1248 Ok(Self { key })
1249 }
1250
1251 pub fn from_pkcs8_encrypted_der(
1253 private_key_der: &[u8],
1254 password: impl AsRef<[u8]>,
1255 ) -> Result<Self, SigningKeyError> {
1256 let key = P384SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1257 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1258 Ok(Self { key })
1259 }
1260}
1261
1262impl SigningKey for EcdsaP384SigningKey {
1263 fn sign(
1264 &self,
1265 algorithm: SignatureAlgorithm,
1266 canonical_signed_info: &[u8],
1267 ) -> Result<Vec<u8>, SigningKeyError> {
1268 self.sign_with_provider(
1269 crate::provider::default_provider(),
1270 algorithm,
1271 canonical_signed_info,
1272 )
1273 }
1274
1275 fn sign_with_provider(
1276 &self,
1277 provider: &dyn crate::provider::CryptoProvider,
1278 algorithm: SignatureAlgorithm,
1279 canonical_signed_info: &[u8],
1280 ) -> Result<Vec<u8>, SigningKeyError> {
1281 sign_ecdsa_with_provider::<P384Signature, _>(
1282 &self.key,
1283 provider,
1284 algorithm,
1285 canonical_signed_info,
1286 )
1287 }
1288
1289 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1290 ecdsa_public_key_info(self.key.verifying_key(), EC_P384_OID)
1291 }
1292}
1293
1294pub struct EcdsaP521SigningKey {
1296 key: P521SigningKey,
1297}
1298
1299impl EcdsaP521SigningKey {
1300 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1302 let key = p521::SecretKey::from_sec1_pem(private_key_pem)
1303 .map(P521SigningKey::from)
1304 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1305 Ok(Self { key })
1306 }
1307
1308 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1310 let key = p521::SecretKey::from_sec1_der(private_key_der)
1311 .map(P521SigningKey::from)
1312 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1313 Ok(Self { key })
1314 }
1315
1316 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1318 let private_key_der = parse_private_key_pem(private_key_pem)?;
1319 Self::from_pkcs8_der(&private_key_der)
1320 }
1321
1322 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1324 let key = P521SigningKey::from_pkcs8_der(private_key_der)
1325 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1326 Ok(Self { key })
1327 }
1328
1329 pub fn from_pkcs8_encrypted_pem(
1331 private_key_pem: &str,
1332 password: impl AsRef<[u8]>,
1333 ) -> Result<Self, SigningKeyError> {
1334 let key = P521SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1335 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1336 Ok(Self { key })
1337 }
1338
1339 pub fn from_pkcs8_encrypted_der(
1341 private_key_der: &[u8],
1342 password: impl AsRef<[u8]>,
1343 ) -> Result<Self, SigningKeyError> {
1344 let key = P521SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1345 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1346 Ok(Self { key })
1347 }
1348}
1349
1350impl SigningKey for EcdsaP521SigningKey {
1351 fn sign(
1352 &self,
1353 algorithm: SignatureAlgorithm,
1354 canonical_signed_info: &[u8],
1355 ) -> Result<Vec<u8>, SigningKeyError> {
1356 self.sign_with_provider(
1357 crate::provider::default_provider(),
1358 algorithm,
1359 canonical_signed_info,
1360 )
1361 }
1362
1363 fn sign_with_provider(
1364 &self,
1365 provider: &dyn crate::provider::CryptoProvider,
1366 algorithm: SignatureAlgorithm,
1367 canonical_signed_info: &[u8],
1368 ) -> Result<Vec<u8>, SigningKeyError> {
1369 sign_ecdsa_with_provider::<P521Signature, _>(
1370 &self.key,
1371 provider,
1372 algorithm,
1373 canonical_signed_info,
1374 )
1375 }
1376
1377 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1378 ecdsa_public_key_info(self.key.verifying_key(), EC_P521_OID)
1379 }
1380}
1381
1382#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1384pub enum SignatureTemplateSelection {
1385 FirstDescendant,
1387 #[default]
1389 LastDescendant,
1390}
1391
1392impl SignatureTemplateSelection {
1393 const fn target(self) -> SigningSignatureTarget {
1394 match self {
1395 Self::FirstDescendant => SigningSignatureTarget::First,
1396 Self::LastDescendant => SigningSignatureTarget::Last,
1397 }
1398 }
1399}
1400
1401pub struct SignContext<'a> {
1403 signing_key: &'a dyn SigningKey,
1404 key_info_writer: Option<&'a dyn KeyInfoWriter>,
1405 start_node_id: Option<&'a str>,
1406 id_attributes: &'a [crate::IdAttributeRegistration],
1407 template_selection: SignatureTemplateSelection,
1408 policy: crate::policy::SigningPolicy,
1409 provider: &'a dyn crate::provider::CryptoProvider,
1410 xml_backend: crate::XmlBackend,
1411}
1412
1413impl<'a> SignContext<'a> {
1414 pub fn new(signing_key: &'a dyn SigningKey) -> Self {
1416 Self {
1417 signing_key,
1418 key_info_writer: None,
1419 start_node_id: None,
1420 id_attributes: &[],
1421 template_selection: SignatureTemplateSelection::default(),
1422 policy: crate::policy::SigningPolicy::default(),
1423 provider: crate::provider::default_provider(),
1424 xml_backend: crate::XmlBackend::default(),
1425 }
1426 }
1427
1428 #[must_use]
1430 pub fn policy(mut self, policy: crate::policy::SigningPolicy) -> Self {
1431 self.policy = policy;
1432 self
1433 }
1434
1435 #[must_use]
1437 pub fn provider(mut self, provider: &'a dyn crate::provider::CryptoProvider) -> Self {
1438 self.provider = provider;
1439 self
1440 }
1441
1442 #[must_use]
1444 pub fn xml_backend(mut self, backend: crate::XmlBackend) -> Self {
1445 self.xml_backend = backend;
1446 self
1447 }
1448
1449 fn document_parse_settings(&self) -> DocumentParseSettings {
1450 DocumentParseSettings::from_policy(&self.policy.xml, &self.policy.resources)
1451 .with_backend(self.xml_backend)
1452 }
1453
1454 #[must_use]
1456 pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
1457 self.key_info_writer = Some(writer);
1458 self
1459 }
1460
1461 #[must_use]
1466 pub fn start_node_id(mut self, id: &'a str) -> Self {
1467 self.start_node_id = Some(id);
1468 self
1469 }
1470
1471 #[must_use]
1477 pub fn signature_template_selection(mut self, selection: SignatureTemplateSelection) -> Self {
1478 self.template_selection = selection;
1479 self
1480 }
1481
1482 #[must_use]
1484 pub fn id_attributes(mut self, registrations: &'a [crate::IdAttributeRegistration]) -> Self {
1485 self.id_attributes = registrations;
1486 self
1487 }
1488
1489 #[must_use]
1495 pub fn xpath_here_semantics(mut self, semantics: XPathHereSemantics) -> Self {
1496 self.policy.transforms.xpath_here_semantics = semantics;
1497 self
1498 }
1499
1500 pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
1509 self.policy.validate()?;
1510 self.policy.resources.validate_xml_document_len(xml.len())?;
1511 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1512 &self.policy.resources,
1513 self.xml_backend,
1514 );
1515 let mut document = XmlDocument::parse_with_settings_and_budget(
1516 xml.to_owned(),
1517 self.document_parse_settings(),
1518 budgets.transforms.xml_parse_work(),
1519 )
1520 .map_err(|error| match owned_document_policy_violation(error) {
1521 Ok(error) => SigningError::Policy(error),
1522 Err(XmlDocumentError::Parse(error)) => {
1523 SigningError::Digest(SigningDigestError::XmlParse(error))
1524 }
1525 Err(error) => SigningError::Document(error),
1526 })?;
1527 self.validate_owned_document_input(&document)?;
1528 self.sign_document_in_place(&mut document, &mut budgets)?;
1529 Ok(document.into_xml())
1530 }
1531
1532 pub fn sign_document(&self, document: &mut XmlDocument) -> Result<(), SigningError> {
1537 self.validate_owned_document_input(document)?;
1538 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1539 &self.policy.resources,
1540 self.xml_backend,
1541 );
1542 let mut staged = document
1543 .staged_copy_with_budget(
1544 self.document_parse_settings(),
1545 budgets.transforms.xml_parse_work(),
1546 )
1547 .map_err(map_owned_document_mutation_error)?;
1548 self.sign_document_in_place(&mut staged, &mut budgets)?;
1549 document
1550 .commit_staged(staged)
1551 .map_err(map_owned_document_mutation_error)
1552 }
1553
1554 fn sign_document_in_place(
1555 &self,
1556 document: &mut XmlDocument,
1557 budgets: &mut SigningOperationBudgets,
1558 ) -> Result<(), SigningError> {
1559 let target_signature = document.with_view(|view| {
1560 signing_signature_index(
1561 view.document(),
1562 self.start_node_id,
1563 self.id_attributes,
1564 self.template_selection,
1565 )
1566 })?;
1567 self.policy.resources.validate_key_candidates(1)?;
1568 self.sign_template_at_index_with_budgets(document, target_signature, budgets)?;
1569 Ok(())
1570 }
1571
1572 fn validate_owned_document_input(&self, document: &XmlDocument) -> Result<(), SigningError> {
1573 self.policy.validate()?;
1574 document.validate_operation_policy(&self.policy.xml, &self.policy.resources)?;
1575 Ok(())
1576 }
1577
1578 fn sign_template_at_index_with_budgets(
1579 &self,
1580 document: &mut XmlDocument,
1581 target_signature: usize,
1582 budgets: &mut SigningOperationBudgets,
1583 ) -> Result<(), SigningError> {
1584 document.with_view(|view| {
1585 let signature = find_signing_signature_node(
1586 view.document(),
1587 SigningSignatureTarget::Index(target_signature),
1588 )?;
1589 parse_signature_children(signature)
1590 .map_err(|error| SigningDigestError::InvalidStructure(error.to_string()))?;
1591 Ok::<_, SigningError>(())
1592 })?;
1593 let transform_options = TransformOptions::default()
1594 .allow_internal_dtd(self.policy.xml.allow_internal_dtd)
1595 .xpath_here_semantics(self.policy.transforms.xpath_here_semantics);
1596 let with_key_info = if let Some(writer) = self.key_info_writer {
1597 let key_info_content =
1598 writer.write_key_info_with_provider(self.signing_key, self.provider)?;
1599 self.policy
1602 .resources
1603 .validate_xml_document_len(key_info_content.len())?;
1604 let populated = merge_key_info_source_at_index_with_budget(
1607 document.as_xml(),
1608 &key_info_content,
1609 target_signature,
1610 Some(&self.policy),
1611 Some(budgets.transforms.xml_parse_work()),
1612 )?;
1613 self.policy
1614 .resources
1615 .validate_xml_document_len(populated.len())?;
1616 Some(populated)
1617 } else {
1618 None
1619 };
1620 if let Some(populated) = with_key_info {
1621 document
1622 .replace_serialized_with_settings(
1623 populated,
1624 self.document_parse_settings(),
1625 Some(budgets.transforms.xml_parse_work()),
1626 )
1627 .map_err(map_owned_document_mutation_error)?;
1628 }
1629 fill_reference_digest_values_in_dependency_order(
1630 document,
1631 transform_options,
1632 &self.policy,
1633 self.provider,
1634 budgets,
1635 target_signature,
1636 self.id_attributes,
1637 )?;
1638 self.policy
1639 .resources
1640 .validate_xml_document_len(document.as_xml().len())?;
1641 let (algorithm, hmac_output_length_bits, canonical_signed_info) =
1642 canonicalize_signed_info(document, &self.policy, budgets, target_signature)?;
1643 budgets
1644 .transforms
1645 .charge_c14n_output(canonical_signed_info.len())
1646 .map_err(SigningDigestError::Transform)?;
1647 self.policy.check_signature_algorithm(algorithm)?;
1648 let expected_signature_len = expected_signature_output_len(
1649 self.signing_key,
1650 algorithm,
1651 &self.policy,
1652 hmac_output_length_bits,
1653 )?;
1654 let projected_signature_len = projected_signature_output_len(
1655 algorithm,
1656 expected_signature_len,
1657 self.policy.ecdsa_signature_value_encoding,
1658 )?;
1659 let encoded_signature_len =
1660 padded_base64_len_for_xml(projected_signature_len, &self.policy)?;
1661 let signature_value_node = document.with_view(|view| {
1662 let signature = find_signing_signature_node(
1663 view.document(),
1664 SigningSignatureTarget::Index(target_signature),
1665 )?;
1666 let signature_value = find_required_child(signature, "SignatureValue")?;
1667 Ok::<_, SigningError>(view.node_identity(signature_value))
1668 })?;
1669 let projected_document_len = document
1670 .projected_content_replacement_len(signature_value_node, encoded_signature_len)?;
1671 self.policy
1672 .resources
1673 .validate_xml_document_len(projected_document_len)?;
1674 self.provider
1675 .require_capability(crate::provider::ProviderCapability::Sign(algorithm))
1676 .map_err(SigningKeyError::from)?;
1677 let mut signature_value =
1678 self.provider
1679 .sign(self.signing_key, algorithm, &canonical_signed_info)?;
1680 if algorithm.hmac_output_bits().is_some() {
1681 signature_value.truncate(expected_signature_len);
1682 }
1683 validate_signature_output(expected_signature_len, &signature_value)?;
1684 let signature_value = encode_signature_output(
1685 algorithm,
1686 signature_value,
1687 self.policy.ecdsa_signature_value_encoding,
1688 )?;
1689 let signature_b64 = base64::engine::general_purpose::STANDARD.encode(signature_value);
1690 document
1691 .replace_base64_contents_with_budget(
1692 &[(signature_value_node, signature_b64)],
1693 self.document_parse_settings(),
1694 budgets.transforms.xml_parse_work(),
1695 )
1696 .map_err(map_owned_document_mutation_error)?;
1697 self.policy
1698 .resources
1699 .validate_xml_document_len(document.as_xml().len())?;
1700 Ok(())
1701 }
1702
1703 pub fn sign_with_builder(
1706 &self,
1707 xml: &str,
1708 builder: &SignatureBuilder,
1709 ) -> Result<String, SigningError> {
1710 self.policy.validate()?;
1711 self.policy.resources.validate_xml_document_len(xml.len())?;
1712 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1713 &self.policy.resources,
1714 self.xml_backend,
1715 );
1716 let mut document = XmlDocument::parse_with_settings_and_budget(
1717 xml.to_owned(),
1718 self.document_parse_settings(),
1719 budgets.transforms.xml_parse_work(),
1720 )
1721 .map_err(|error| match owned_document_policy_violation(error) {
1722 Ok(error) => SigningError::Policy(error),
1723 Err(XmlDocumentError::Parse(error)) => {
1724 SigningError::XmlMutation(XmlMutationError::XmlParse(error))
1725 }
1726 Err(error) => SigningError::Document(error),
1727 })?;
1728 self.validate_owned_document_input(&document)?;
1729 self.sign_document_with_builder_in_place(&mut document, builder, &mut budgets)?;
1730 Ok(document.into_xml())
1731 }
1732
1733 pub fn sign_document_with_builder(
1735 &self,
1736 document: &mut XmlDocument,
1737 builder: &SignatureBuilder,
1738 ) -> Result<(), SigningError> {
1739 self.validate_owned_document_input(document)?;
1740 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1741 &self.policy.resources,
1742 self.xml_backend,
1743 );
1744 let mut staged = document
1745 .staged_copy_with_budget(
1746 self.document_parse_settings(),
1747 budgets.transforms.xml_parse_work(),
1748 )
1749 .map_err(map_owned_document_mutation_error)?;
1750 self.sign_document_with_builder_in_place(&mut staged, builder, &mut budgets)?;
1751 document
1752 .commit_staged(staged)
1753 .map_err(map_owned_document_mutation_error)
1754 }
1755
1756 fn sign_document_with_builder_in_place(
1757 &self,
1758 document: &mut XmlDocument,
1759 builder: &SignatureBuilder,
1760 budgets: &mut SigningOperationBudgets,
1761 ) -> Result<(), SigningError> {
1762 self.policy.resources.validate_key_candidates(1)?;
1763 let expected_signature_len = expected_signature_output_len(
1764 self.signing_key,
1765 builder.signature_method(),
1766 &self.policy,
1767 None,
1768 )?;
1769 let template = builder.build_template_with_policy_for_signature_output(
1770 &self.policy,
1771 expected_signature_len,
1772 &budgets.transforms,
1773 &mut budgets.xpath_parse,
1774 )?;
1775 let signature_parent = if let Some(id) = self.start_node_id {
1776 document.with_view(|view| {
1777 let start = signing_start_node(view.document(), id, self.id_attributes)?;
1778 Ok::<_, SigningError>(view.node_identity(start))
1779 })?
1780 } else {
1781 document.with_view(|view| view.root_element())
1782 };
1783 let projected_document_len =
1784 document.projected_child_append_len(signature_parent, template.len())?;
1785 self.policy
1786 .resources
1787 .validate_xml_document_len(projected_document_len)?;
1788 document
1789 .append_generated_child_with_budget(
1790 signature_parent,
1791 &template,
1792 self.document_parse_settings(),
1793 budgets.transforms.xml_parse_work(),
1794 )
1795 .map_err(map_owned_document_mutation_error)?;
1796 self.policy
1797 .resources
1798 .validate_xml_document_len(document.as_xml().len())?;
1799 let target_signature = document.with_view(|view| {
1800 let parent = if let Some(id) = self.start_node_id {
1801 signing_start_node(view.document(), id, self.id_attributes)?
1802 } else {
1803 view.document().root_element()
1804 };
1805 let appended = parent
1806 .children()
1807 .rfind(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
1808 .ok_or(SigningDigestError::MissingElement {
1809 element: "Signature",
1810 })?;
1811 signature_index(view.document(), appended).map_err(SigningError::from)
1812 })?;
1813 self.sign_template_at_index_with_budgets(document, target_signature, budgets)?;
1814 Ok(())
1815 }
1816}
1817
1818fn projected_signature_output_len(
1819 algorithm: SignatureAlgorithm,
1820 raw_signature_len: usize,
1821 encoding: crate::policy::EcdsaSignatureValueEncoding,
1822) -> Result<usize, SigningError> {
1823 if matches!(
1824 (algorithm, encoding),
1825 (
1826 SignatureAlgorithm::EcdsaSha1
1827 | SignatureAlgorithm::EcdsaSha224
1828 | SignatureAlgorithm::EcdsaSha256
1829 | SignatureAlgorithm::EcdsaSha384
1830 | SignatureAlgorithm::EcdsaSha512,
1831 crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
1832 )
1833 ) {
1834 return maximum_ecdsa_der_signature_len(raw_signature_len)
1835 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
1836 }
1837 Ok(raw_signature_len)
1838}
1839
1840fn map_owned_document_mutation_error(error: XmlDocumentError) -> SigningError {
1841 match owned_document_policy_violation(error) {
1842 Ok(error) => SigningError::Policy(error),
1843 Err(error) => SigningError::Document(error),
1844 }
1845}
1846
1847fn map_owned_document_digest_mutation_error(error: XmlDocumentError) -> SigningDigestError {
1848 match owned_document_policy_violation(error) {
1849 Ok(error) => SigningDigestError::Policy(error),
1850 Err(error) => SigningDigestError::Document(error),
1851 }
1852}
1853
1854fn owned_document_policy_violation(
1855 error: XmlDocumentError,
1856) -> Result<crate::policy::PolicyViolation, XmlDocumentError> {
1857 match error {
1858 XmlDocumentError::Policy(error) => Ok(error),
1859 XmlDocumentError::DocumentTooLarge { maximum, actual } => {
1860 Ok(crate::policy::PolicyViolation::ResourceLimit {
1861 resource: crate::policy::resource_name::XML_DOCUMENT,
1862 maximum,
1863 actual,
1864 })
1865 }
1866 XmlDocumentError::DocumentTooDeep { maximum, actual } => {
1867 Ok(crate::policy::PolicyViolation::ResourceLimit {
1868 resource: crate::policy::resource_name::XML_DEPTH,
1869 maximum,
1870 actual,
1871 })
1872 }
1873 XmlDocumentError::ProjectedNodeLimit { maximum } => {
1874 Ok(crate::policy::PolicyViolation::ResourceLimit {
1875 resource: crate::policy::resource_name::XML_NODES,
1876 maximum,
1877 actual: maximum.saturating_add(1),
1878 })
1879 }
1880 error => Err(error),
1881 }
1882}
1883
1884fn encode_signature_output(
1885 algorithm: SignatureAlgorithm,
1886 signature: Vec<u8>,
1887 encoding: crate::policy::EcdsaSignatureValueEncoding,
1888) -> Result<Vec<u8>, SigningError> {
1889 if matches!(
1890 (algorithm, encoding),
1891 (
1892 SignatureAlgorithm::EcdsaSha1
1893 | SignatureAlgorithm::EcdsaSha224
1894 | SignatureAlgorithm::EcdsaSha256
1895 | SignatureAlgorithm::EcdsaSha384
1896 | SignatureAlgorithm::EcdsaSha512,
1897 crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
1898 )
1899 ) {
1900 return encode_ecdsa_signature_as_der(&signature)
1901 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
1902 }
1903 Ok(signature)
1904}
1905
1906#[derive(Debug, Clone)]
1907struct SigningReference {
1908 uri: String,
1909 transforms: Vec<Transform>,
1910 digest_method: DigestAlgorithm,
1911 digest_value_range: Range<usize>,
1912 digest_value_node_id: NodeId,
1913}
1914
1915struct SigningOperationBudgets {
1916 transforms: TransformExecutionBudget,
1917 xpath_parse: XPathSignatureParseBudget,
1918}
1919
1920impl SigningOperationBudgets {
1921 fn from_resources(resources: &crate::policy::ResourcePolicy) -> Self {
1922 Self {
1923 transforms: TransformExecutionBudget::from_resources(resources),
1924 xpath_parse: XPathSignatureParseBudget::from_resources(resources),
1925 }
1926 }
1927
1928 fn from_resources_with_backend(
1929 resources: &crate::policy::ResourcePolicy,
1930 backend: crate::XmlBackend,
1931 ) -> Self {
1932 Self {
1933 transforms: TransformExecutionBudget::from_resources(resources)
1934 .with_xml_backend(backend),
1935 xpath_parse: XPathSignatureParseBudget::from_resources(resources),
1936 }
1937 }
1938}
1939
1940impl Default for SigningOperationBudgets {
1941 fn default() -> Self {
1942 Self::from_resources(&crate::policy::ResourcePolicy::default())
1943 }
1944}
1945
1946pub fn compute_reference_digest_values(
1953 xml: &str,
1954) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
1955 let execution_budget = TransformExecutionBudget::default();
1956 compute_reference_digest_values_with_options(
1957 xml,
1958 TransformOptions::default(),
1959 None,
1960 crate::provider::default_provider(),
1961 &execution_budget,
1962 None,
1963 &[],
1964 )
1965}
1966
1967fn compute_reference_digest_values_with_options(
1968 xml: &str,
1969 transform_options: TransformOptions,
1970 policy: Option<&crate::policy::SigningPolicy>,
1971 provider: &dyn crate::provider::CryptoProvider,
1972 execution_budget: &TransformExecutionBudget,
1973 target_signature: Option<usize>,
1974 id_attributes: &[crate::IdAttributeRegistration],
1975) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
1976 let doc = parse_signing_document(
1977 xml,
1978 policy,
1979 execution_budget.xml_parse_work(),
1980 crate::XmlBackend::default(),
1981 )?;
1982 let signature = find_signing_signature_node(
1983 &doc,
1984 target_signature.map_or(SigningSignatureTarget::Last, SigningSignatureTarget::Index),
1985 )?;
1986 let signed_info = find_required_child(signature, "SignedInfo")?;
1987 let references = parse_signing_references(signed_info)?;
1988 validate_signing_references(&references, references.len(), policy)?;
1989 compute_signing_reference_digests(
1990 &doc,
1991 signature,
1992 references,
1993 transform_options,
1994 provider,
1995 execution_budget,
1996 SigningUriResolution {
1997 id_attributes,
1998 same_document_id_semantics: policy.map_or(
1999 crate::policy::SameDocumentIdSemantics::Specification,
2000 |policy| policy.transforms.same_document_id_semantics,
2001 ),
2002 },
2003 )
2004}
2005
2006fn fill_reference_digest_values_in_dependency_order(
2007 document: &mut XmlDocument,
2008 transform_options: TransformOptions,
2009 policy: &crate::policy::SigningPolicy,
2010 provider: &dyn crate::provider::CryptoProvider,
2011 budgets: &mut SigningOperationBudgets,
2012 target_signature: usize,
2013 id_attributes: &[crate::IdAttributeRegistration],
2014) -> Result<(), SigningDigestError> {
2015 let reference_limit = policy
2016 .resources
2017 .max_references
2018 .min(MAX_REFERENCES_PER_SIGNATURE);
2019 let process_manifests =
2020 policy.manifest_processing == crate::policy::ManifestProcessing::Process;
2021 let (signed_info_references, manifest_references) = document.with_view(|view| {
2022 let signature = find_signing_signature_node(
2023 view.document(),
2024 SigningSignatureTarget::Index(target_signature),
2025 )?;
2026 let signed_info = find_required_child(signature, "SignedInfo")?;
2027 let signed_info_references =
2028 parse_signing_references_with_budget(signed_info, &mut budgets.xpath_parse)?;
2029 validate_signing_references(
2030 &signed_info_references,
2031 signed_info_references.len(),
2032 Some(policy),
2033 )?;
2034 let manifest_references = if process_manifests {
2035 parse_signing_manifest_references(
2036 signature,
2037 &mut budgets.xpath_parse,
2038 reference_limit.saturating_sub(signed_info_references.len()),
2039 reference_limit,
2040 )?
2041 } else {
2042 Vec::new()
2043 };
2044 Ok::<_, SigningDigestError>((signed_info_references, manifest_references))
2045 })?;
2046 let total_references = signed_info_references
2047 .len()
2048 .checked_add(manifest_references.len())
2049 .ok_or_else(|| SigningDigestError::InvalidStructure("reference count overflow".into()))?;
2050 validate_signing_references(&manifest_references, total_references, Some(policy))?;
2051 let placeholder = "AA==";
2052 let analysis_replacements = document.with_view(|view| {
2056 let signature = find_signing_signature_node(
2057 view.document(),
2058 SigningSignatureTarget::Index(target_signature),
2059 )?;
2060 let signature_value = find_required_child(signature, "SignatureValue")?;
2061 let mut replacements = signed_info_references
2062 .iter()
2063 .chain(&manifest_references)
2064 .map(|reference| {
2065 (
2066 view.node_identity_by_id(reference.digest_value_node_id),
2067 placeholder.to_owned(),
2068 )
2069 })
2070 .collect::<Vec<_>>();
2071 replacements.push((view.node_identity(signature_value), placeholder.to_owned()));
2072 Ok::<_, SigningDigestError>(replacements)
2073 })?;
2074 let analysis_xml = document
2075 .project_base64_contents(
2076 &analysis_replacements,
2077 policy.resources.max_xml_document_bytes,
2078 )
2079 .map_err(map_owned_document_digest_mutation_error)?;
2080 let analysis_doc = parse_signing_document(
2081 &analysis_xml,
2082 Some(policy),
2083 budgets.transforms.xml_parse_work(),
2084 document.xml_backend(),
2085 )?;
2086 let analysis_signature = find_signing_signature_node(
2087 &analysis_doc,
2088 SigningSignatureTarget::Index(target_signature),
2089 )?;
2090 let analysis_signed_info = find_required_child(analysis_signature, "SignedInfo")?;
2091 let mut analysis_references =
2092 parse_signing_references_with_budget(analysis_signed_info, &mut budgets.xpath_parse)?;
2093 if process_manifests {
2094 analysis_references.extend(parse_signing_manifest_references(
2095 analysis_signature,
2096 &mut budgets.xpath_parse,
2097 reference_limit.saturating_sub(signed_info_references.len()),
2098 reference_limit,
2099 )?);
2100 }
2101 let dependency_plan = reference_dependency_levels(
2102 &analysis_doc,
2103 analysis_signature,
2104 &analysis_references,
2105 transform_options,
2106 &budgets.transforms,
2107 id_attributes,
2108 policy.transforms.same_document_id_semantics,
2109 )?;
2110 for level in dependency_plan {
2111 let replacements = document.with_view(|view| {
2112 let current_doc = view.document();
2113 let current_signature = find_signing_signature_node(
2114 current_doc,
2115 SigningSignatureTarget::Index(target_signature),
2116 )?;
2117 let current_signed_info = find_required_child(current_signature, "SignedInfo")?;
2118 let current_signed_info_references = parse_signing_references_with_budget(
2119 current_signed_info,
2120 &mut budgets.xpath_parse,
2121 )?;
2122 let current_manifest_references = if process_manifests {
2123 parse_signing_manifest_references(
2124 current_signature,
2125 &mut budgets.xpath_parse,
2126 reference_limit.saturating_sub(signed_info_references.len()),
2127 reference_limit,
2128 )?
2129 } else {
2130 Vec::new()
2131 };
2132 if current_signed_info_references.len() != signed_info_references.len()
2133 || current_manifest_references.len() != manifest_references.len()
2134 {
2135 return Err(SigningDigestError::InvalidStructure(
2136 "signing Reference set changed while filling digests".into(),
2137 ));
2138 }
2139 let mut destinations = Vec::with_capacity(level.len());
2140 let mut level_references = Vec::with_capacity(level.len());
2141 for index in &level {
2142 let reference = if *index < signed_info_references.len() {
2143 current_signed_info_references.get(*index)
2144 } else {
2145 current_manifest_references.get(*index - signed_info_references.len())
2146 }
2147 .ok_or_else(|| {
2148 SigningDigestError::InvalidStructure(
2149 "signing Reference set changed while filling digests".into(),
2150 )
2151 })?;
2152 destinations.push(view.node_identity_by_id(reference.digest_value_node_id));
2153 level_references.push(reference.clone());
2154 }
2155 let computed = compute_signing_reference_digests(
2156 current_doc,
2157 current_signature,
2158 level_references,
2159 transform_options,
2160 provider,
2161 &budgets.transforms,
2162 SigningUriResolution {
2163 id_attributes,
2164 same_document_id_semantics: policy.transforms.same_document_id_semantics,
2165 },
2166 )?;
2167 if computed.len() != destinations.len() {
2168 return Err(SigningDigestError::InvalidStructure(
2169 "signing Reference set changed while computing digests".into(),
2170 ));
2171 }
2172 Ok::<_, SigningDigestError>(
2173 destinations
2174 .into_iter()
2175 .zip(computed)
2176 .map(|(target, digest)| (target, digest.digest_value))
2177 .collect::<Vec<_>>(),
2178 )
2179 })?;
2180 document
2181 .replace_base64_contents_with_budget(
2182 &replacements,
2183 DocumentParseSettings::from_policy(&policy.xml, &policy.resources)
2184 .with_backend(document.xml_backend()),
2185 budgets.transforms.xml_parse_work(),
2186 )
2187 .map_err(map_owned_document_digest_mutation_error)?;
2188 }
2189 Ok(())
2190}
2191
2192fn reference_dependency_levels(
2193 doc: &Document<'_>,
2194 signature: Node<'_, '_>,
2195 references: &[SigningReference],
2196 transform_options: TransformOptions,
2197 execution_budget: &TransformExecutionBudget,
2198 id_attributes: &[crate::IdAttributeRegistration],
2199 same_document_id_semantics: crate::policy::SameDocumentIdSemantics,
2200) -> Result<Vec<Vec<usize>>, SigningDigestError> {
2201 let resolver = UriReferenceResolver::with_id_registrations(doc, id_attributes)
2202 .with_same_document_id_semantics(same_document_id_semantics);
2203 let terminal_signature_value_index = references.len();
2204 let signature_value = find_required_child(signature, "SignatureValue")?;
2205 let mut tracked_mutable_nodes = references
2206 .iter()
2207 .enumerate()
2208 .flat_map(|(index, reference)| {
2209 std::iter::once((index, reference.digest_value_node_id)).chain(
2210 doc.get_node(reference.digest_value_node_id)
2211 .into_iter()
2212 .flat_map(|node| node.children())
2213 .filter(|node| node.is_text())
2214 .map(move |node| (index, node.id())),
2215 )
2216 })
2217 .collect::<Vec<_>>();
2218 tracked_mutable_nodes.push((terminal_signature_value_index, signature_value.id()));
2219 tracked_mutable_nodes.extend(
2220 signature_value
2221 .children()
2222 .filter(|node| node.is_text())
2223 .map(|node| (terminal_signature_value_index, node.id())),
2224 );
2225 let analyses = references
2226 .iter()
2227 .map(|reference| {
2228 let initial_data = resolver.dereference_with_budget(
2229 &reference.uri,
2230 execution_budget.node_set_materialization(),
2231 )?;
2232 let output = execute_transforms_with_dependency_nodes(
2233 signature,
2234 initial_data,
2235 &reference.transforms,
2236 transform_options,
2237 execution_budget,
2238 tracked_mutable_nodes.clone(),
2239 )?;
2240 Ok(output.dependencies)
2241 })
2242 .collect::<Result<Vec<_>, SigningDigestError>>()?;
2243 if analyses
2244 .iter()
2245 .any(|dependencies| dependencies.contains(&terminal_signature_value_index))
2246 {
2247 return Err(SigningDigestError::InvalidStructure(
2248 "Reference dependency cycle includes the mutable SignatureValue".into(),
2249 ));
2250 }
2251 let mut dependencies = analyses;
2252 let mut completed = vec![false; references.len()];
2253 let mut levels = Vec::new();
2254 while completed.iter().any(|done| !done) {
2255 let ready = dependencies
2256 .iter()
2257 .enumerate()
2258 .filter_map(|(index, dependencies)| {
2259 (!completed[index] && dependencies.is_empty()).then_some(index)
2260 })
2261 .collect::<Vec<_>>();
2262 if ready.is_empty() {
2263 return Err(SigningDigestError::InvalidStructure(
2264 "Manifest Reference digest dependency cycle".into(),
2265 ));
2266 }
2267 for index in &ready {
2268 completed[*index] = true;
2269 }
2270 for dependency_set in &mut dependencies {
2271 dependency_set.retain(|dependency| !completed[*dependency]);
2272 }
2273 levels.push(ready);
2274 }
2275
2276 debug_assert!(references.iter().all(|reference| {
2280 signature.range().start <= reference.digest_value_range.start
2281 && signature.range().end >= reference.digest_value_range.end
2282 }));
2283 Ok(levels)
2284}
2285
2286fn validate_signing_references(
2287 references: &[SigningReference],
2288 total_references: usize,
2289 policy: Option<&crate::policy::SigningPolicy>,
2290) -> Result<(), SigningDigestError> {
2291 if let Some(policy) = policy
2292 && total_references > policy.resources.max_references
2293 {
2294 return Err(crate::policy::PolicyViolation::ResourceLimit {
2295 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
2296 maximum: policy.resources.max_references,
2297 actual: total_references,
2298 }
2299 .into());
2300 }
2301 for reference in references {
2302 if let Some(policy) = policy {
2303 validate_signing_reference_uri(&reference.uri, policy)?;
2304 }
2305 if let Some(policy) = policy
2306 && reference.transforms.len() > policy.resources.max_transforms_per_reference
2307 {
2308 return Err(crate::policy::PolicyViolation::ResourceLimit {
2309 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
2310 maximum: policy.resources.max_transforms_per_reference,
2311 actual: reference.transforms.len(),
2312 }
2313 .into());
2314 }
2315 if let Some(policy) = policy {
2316 policy.check_digest_algorithm(reference.digest_method)?;
2317 } else if !reference.digest_method.signing_allowed() {
2318 return Err(SigningDigestError::SigningAlgorithmDisabled {
2319 uri: reference.digest_method.uri(),
2320 });
2321 }
2322 let initial_binary = !reference.uri.is_empty() && !reference.uri.starts_with('#');
2323 validate_signing_transform_policy(
2324 initial_binary,
2325 &reference.transforms,
2326 policy.and_then(|policy| policy.transforms.allowed_algorithms.as_ref()),
2327 )?;
2328 }
2329 Ok(())
2330}
2331
2332struct SigningUriResolution<'a> {
2333 id_attributes: &'a [crate::IdAttributeRegistration],
2334 same_document_id_semantics: crate::policy::SameDocumentIdSemantics,
2335}
2336
2337fn compute_signing_reference_digests(
2338 doc: &Document<'_>,
2339 signature: Node<'_, '_>,
2340 references: Vec<SigningReference>,
2341 transform_options: TransformOptions,
2342 provider: &dyn crate::provider::CryptoProvider,
2343 execution_budget: &TransformExecutionBudget,
2344 uri_resolution: SigningUriResolution<'_>,
2345) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2346 let resolver = UriReferenceResolver::with_id_registrations(doc, uri_resolution.id_attributes)
2347 .with_same_document_id_semantics(uri_resolution.same_document_id_semantics);
2348 references
2349 .into_iter()
2350 .enumerate()
2351 .map(|(index, reference)| {
2352 let initial_data = resolver.dereference_with_budget(
2353 &reference.uri,
2354 execution_budget.node_set_materialization(),
2355 )?;
2356 let pre_digest = execute_transforms_with_options_and_budget(
2357 signature,
2358 initial_data,
2359 &reference.transforms,
2360 transform_options,
2361 execution_budget,
2362 )?;
2363 let digest = super::compute_digest_with_provider(
2364 provider,
2365 reference.digest_method,
2366 &pre_digest,
2367 )?;
2368 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
2369 Ok(ComputedReferenceDigest {
2370 index,
2371 uri: reference.uri,
2372 digest_method: reference.digest_method,
2373 digest_value,
2374 })
2375 })
2376 .collect()
2377}
2378
2379pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
2386 let execution_budget = TransformExecutionBudget::default();
2387 fill_reference_digest_values_with_options(
2388 xml,
2389 TransformOptions::default(),
2390 None,
2391 crate::provider::default_provider(),
2392 &execution_budget,
2393 None,
2394 &[],
2395 )
2396}
2397
2398fn fill_reference_digest_values_with_options(
2399 xml: &str,
2400 transform_options: TransformOptions,
2401 policy: Option<&crate::policy::SigningPolicy>,
2402 provider: &dyn crate::provider::CryptoProvider,
2403 execution_budget: &TransformExecutionBudget,
2404 target_signature: Option<usize>,
2405 id_attributes: &[crate::IdAttributeRegistration],
2406) -> Result<String, SigningDigestError> {
2407 let digest_values = compute_reference_digest_values_with_options(
2408 xml,
2409 transform_options,
2410 policy,
2411 provider,
2412 execution_budget,
2413 target_signature,
2414 id_attributes,
2415 )?
2416 .into_iter()
2417 .map(|digest| digest.digest_value);
2418 Ok(if let Some(target_signature) = target_signature {
2419 fill_signed_info_digest_values_at_index_with_budget(
2420 xml,
2421 digest_values,
2422 target_signature,
2423 policy,
2424 Some(execution_budget.xml_parse_work()),
2425 )?
2426 } else if let Some(policy) = policy {
2427 fill_signed_info_digest_values_with_budget(
2428 xml,
2429 digest_values,
2430 Some(policy),
2431 Some(execution_budget.xml_parse_work()),
2432 )?
2433 } else {
2434 fill_signed_info_digest_values_with_budget(
2435 xml,
2436 digest_values,
2437 None,
2438 Some(execution_budget.xml_parse_work()),
2439 )?
2440 })
2441}
2442
2443fn canonicalize_signed_info(
2444 document: &XmlDocument,
2445 policy: &crate::policy::SigningPolicy,
2446 budgets: &mut SigningOperationBudgets,
2447 target_signature: usize,
2448) -> Result<(SignatureAlgorithm, Option<usize>, Vec<u8>), SigningError> {
2449 document.with_view(|view| {
2450 let doc = view.document();
2451 let signature =
2452 find_signing_signature_node(doc, SigningSignatureTarget::Index(target_signature))
2453 .map_err(SigningError::Digest)?;
2454 let signed_info_node =
2455 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
2456 let signed_info =
2457 parse_signed_info_with_xpath_budget(signed_info_node, &mut budgets.xpath_parse)?;
2458 if policy
2459 .transforms
2460 .allowed_algorithms
2461 .as_ref()
2462 .is_some_and(|allowed| !allowed.contains(signed_info.c14n_method.uri()))
2463 {
2464 return Err(crate::policy::PolicyViolation::Algorithm {
2465 operation: "SignedInfo canonicalization",
2466 algorithm: signed_info.c14n_method.uri().to_owned(),
2467 }
2468 .into());
2469 }
2470 let signed_info_subtree: HashSet<_> = signed_info_node
2471 .descendants()
2472 .map(|node: Node<'_, '_>| node.id())
2473 .collect();
2474 let mut canonical_signed_info = Vec::new();
2475 canonicalize_bounded_with_xml_base_budget(
2476 doc,
2477 Some(&|node| signed_info_subtree.contains(&node.id())),
2478 &signed_info.c14n_method,
2479 budgets.transforms.remaining_c14n_output(),
2480 budgets.transforms.xml_base_resolution(),
2481 &mut canonical_signed_info,
2482 )
2483 .map_err(|error| {
2484 if let Some(violation) = map_c14n_resource_policy_violation(
2485 &error,
2486 crate::policy::resource_name::CANONICALIZED_BYTES,
2487 budgets.transforms.c14n_output_limit(),
2488 ) {
2489 SigningError::Policy(violation)
2490 } else {
2491 SigningError::Canonicalization(error)
2492 }
2493 })?;
2494 Ok((
2495 signed_info.signature_method,
2496 signed_info.hmac_output_length_bits,
2497 canonical_signed_info,
2498 ))
2499 })
2500}
2501
2502fn parse_signing_document<'a>(
2503 xml: &'a str,
2504 policy: Option<&crate::policy::SigningPolicy>,
2505 budget: &XmlParseWorkBudget,
2506 backend: crate::XmlBackend,
2507) -> Result<Document<'a>, SigningDigestError> {
2508 let settings = policy
2509 .map(|policy| DocumentParseSettings::from_policy(&policy.xml, &policy.resources))
2510 .unwrap_or_default()
2511 .with_backend(backend);
2512 super::mutation::parse_with_options_and_budget(xml, settings, Some(budget)).map_err(|error| {
2513 match error.into_policy_violation(settings) {
2514 Ok(error) => SigningDigestError::Policy(error),
2515 Err(XmlDocumentError::Parse(error)) => SigningDigestError::XmlParse(error),
2516 Err(error) => SigningDigestError::Document(error),
2517 }
2518 })
2519}
2520
2521fn parse_private_key_pem(private_key_pem: &str) -> Result<Zeroizing<Vec<u8>>, SigningKeyError> {
2522 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
2523 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
2524 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
2525 return Err(SigningKeyError::InvalidKeyPem);
2526 }
2527 if pem.label != "PRIVATE KEY" {
2528 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
2529 }
2530 Ok(Zeroizing::new(pem.contents))
2531}
2532
2533enum SigningSignatureTarget {
2534 First,
2535 Last,
2536 Index(usize),
2537}
2538
2539fn find_signing_signature_node<'a>(
2540 doc: &'a Document<'a>,
2541 target: SigningSignatureTarget,
2542) -> Result<Node<'a, 'a>, SigningDigestError> {
2543 let mut signatures = doc.descendants().filter(|node| {
2544 node.is_element()
2545 && node.tag_name().name() == "Signature"
2546 && node.tag_name().namespace() == Some(XMLDSIG_NS)
2547 });
2548 match target {
2549 SigningSignatureTarget::First => signatures.next(),
2550 SigningSignatureTarget::Last => signatures.next_back(),
2551 SigningSignatureTarget::Index(index) => signatures.nth(index),
2552 }
2553 .ok_or(SigningDigestError::MissingElement {
2554 element: "Signature",
2555 })
2556}
2557
2558fn signing_signature_index(
2559 doc: &Document<'_>,
2560 start_node_id: Option<&str>,
2561 id_attributes: &[crate::IdAttributeRegistration],
2562 selection: SignatureTemplateSelection,
2563) -> Result<usize, SigningDigestError> {
2564 let selected = if let Some(id) = start_node_id {
2565 let start = signing_start_node(doc, id, id_attributes)?;
2566 let mut signatures = start
2567 .descendants()
2568 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")));
2569 match selection.target() {
2570 SigningSignatureTarget::First => signatures.next(),
2571 SigningSignatureTarget::Last => signatures.next_back(),
2572 SigningSignatureTarget::Index(_) => unreachable!("public selection is not indexed"),
2573 }
2574 .ok_or_else(|| {
2575 SigningDigestError::InvalidStructure(format!(
2576 "selected node subtree has no Signature: {id}"
2577 ))
2578 })?
2579 } else {
2580 find_signing_signature_node(doc, selection.target())?
2581 };
2582 signature_index(doc, selected)
2583}
2584
2585fn signing_start_node<'a>(
2586 doc: &'a Document<'a>,
2587 id: &str,
2588 id_attributes: &[crate::IdAttributeRegistration],
2589) -> Result<Node<'a, 'a>, SigningDigestError> {
2590 UriReferenceResolver::with_id_registrations(doc, id_attributes)
2591 .node_for_id(id)
2592 .ok_or_else(|| {
2593 SigningDigestError::InvalidStructure(format!(
2594 "selected node ID is missing or ambiguous: {id}"
2595 ))
2596 })
2597}
2598
2599fn signature_index(
2600 doc: &Document<'_>,
2601 selected: Node<'_, '_>,
2602) -> Result<usize, SigningDigestError> {
2603 doc.descendants()
2604 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
2605 .position(|node| node == selected)
2606 .ok_or(SigningDigestError::MissingElement {
2607 element: "Signature",
2608 })
2609}
2610
2611fn parse_signing_references(
2612 signed_info: Node<'_, '_>,
2613) -> Result<Vec<SigningReference>, SigningDigestError> {
2614 parse_signing_references_with_budget(signed_info, &mut XPathSignatureParseBudget::default())
2615}
2616
2617fn parse_signing_references_with_budget(
2618 signed_info: Node<'_, '_>,
2619 xpath_budget: &mut XPathSignatureParseBudget,
2620) -> Result<Vec<SigningReference>, SigningDigestError> {
2621 verify_ds_element(signed_info, "SignedInfo")?;
2622 let mut children = element_children(signed_info);
2623
2624 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
2625 element: "CanonicalizationMethod",
2626 })?;
2627 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
2628 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
2629
2630 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
2631 element: "SignatureMethod",
2632 })?;
2633 verify_ds_element(signature_method_node, "SignatureMethod")?;
2634 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
2635
2636 let mut references = Vec::new();
2637 for child in children {
2638 verify_ds_element(child, "Reference")?;
2639 if references.len() == MAX_REFERENCES_PER_SIGNATURE {
2640 return Err(SigningDigestError::InvalidStructure(format!(
2641 "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
2642 )));
2643 }
2644 references.push(parse_signing_reference(child, xpath_budget)?);
2645 }
2646 if references.is_empty() {
2647 return Err(SigningDigestError::MissingElement {
2648 element: "Reference",
2649 });
2650 }
2651 Ok(references)
2652}
2653
2654fn parse_signing_manifest_references(
2655 signature: Node<'_, '_>,
2656 xpath_budget: &mut XPathSignatureParseBudget,
2657 mut remaining_capacity: usize,
2658 maximum_references: usize,
2659) -> Result<Vec<SigningReference>, SigningDigestError> {
2660 let mut references = Vec::new();
2661 for manifest in signature
2662 .children()
2663 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Object")))
2664 .flat_map(|object| {
2665 object
2666 .children()
2667 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Manifest")))
2668 })
2669 {
2670 let mut manifest_references = 0usize;
2671 for child in element_children(manifest) {
2672 verify_ds_element(child, "Reference")?;
2673 if remaining_capacity == 0 {
2674 return Err(crate::policy::PolicyViolation::ResourceLimit {
2675 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
2676 maximum: maximum_references,
2677 actual: maximum_references.saturating_add(1),
2678 }
2679 .into());
2680 }
2681 remaining_capacity -= 1;
2682 references.push(parse_signing_reference(child, xpath_budget)?);
2683 manifest_references += 1;
2684 }
2685 if manifest_references == 0 {
2686 return Err(SigningDigestError::MissingElement {
2687 element: "Reference",
2688 });
2689 }
2690 }
2691 Ok(references)
2692}
2693
2694fn parse_signing_reference(
2695 reference_node: Node<'_, '_>,
2696 xpath_budget: &mut XPathSignatureParseBudget,
2697) -> Result<SigningReference, SigningDigestError> {
2698 let uri = reference_node
2699 .attribute("URI")
2700 .ok_or_else(|| {
2701 SigningDigestError::InvalidStructure(
2702 "signing Reference must include URI attribute".into(),
2703 )
2704 })?
2705 .to_string();
2706 let mut children = element_children(reference_node);
2707
2708 let mut transforms = Vec::new();
2709 let mut next = children.next().ok_or(SigningDigestError::MissingElement {
2710 element: "DigestMethod",
2711 })?;
2712 if next.tag_name().name() == "Transforms" && next.tag_name().namespace() == Some(XMLDSIG_NS) {
2713 transforms = parse_transforms_with_budget(next, xpath_budget)?;
2714 next = children.next().ok_or(SigningDigestError::MissingElement {
2715 element: "DigestMethod",
2716 })?;
2717 }
2718
2719 verify_ds_element(next, "DigestMethod")?;
2720 let digest_uri = required_algorithm_attr(next, "DigestMethod")?;
2721 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
2722 SigningDigestError::UnsupportedAlgorithm {
2723 uri: digest_uri.to_string(),
2724 }
2725 })?;
2726 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
2727 element: "DigestValue",
2728 })?;
2729 verify_ds_element(digest_value_node, "DigestValue")?;
2730
2731 if let Some(unexpected) = children.next() {
2732 return Err(SigningDigestError::InvalidStructure(format!(
2733 "unexpected element <{}> after <DigestValue> in <Reference>",
2734 unexpected.tag_name().name()
2735 )));
2736 }
2737
2738 Ok(SigningReference {
2739 uri,
2740 transforms,
2741 digest_method,
2742 digest_value_range: digest_value_node.range(),
2743 digest_value_node_id: digest_value_node.id(),
2744 })
2745}
2746
2747fn find_required_child<'a>(
2748 parent: Node<'a, 'a>,
2749 child_name: &'static str,
2750) -> Result<Node<'a, 'a>, SigningDigestError> {
2751 parent
2752 .children()
2753 .find(|node| {
2754 node.is_element()
2755 && node.tag_name().name() == child_name
2756 && node.tag_name().namespace() == Some(XMLDSIG_NS)
2757 })
2758 .ok_or(SigningDigestError::MissingElement {
2759 element: child_name,
2760 })
2761}
2762
2763fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
2764 node.children().filter(Node::is_element)
2765}
2766
2767fn verify_ds_element(
2768 node: Node<'_, '_>,
2769 expected_name: &'static str,
2770) -> Result<(), SigningDigestError> {
2771 if !node.is_element() {
2772 return Err(SigningDigestError::InvalidStructure(format!(
2773 "expected element <{expected_name}>, got non-element node"
2774 )));
2775 }
2776 let tag = node.tag_name();
2777 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
2778 return Err(SigningDigestError::InvalidStructure(format!(
2779 "expected <ds:{expected_name}>, got <{}>",
2780 tag.name()
2781 )));
2782 }
2783 Ok(())
2784}
2785
2786fn required_algorithm_attr<'a>(
2787 node: Node<'a, 'a>,
2788 element_name: &'static str,
2789) -> Result<&'a str, SigningDigestError> {
2790 node.attribute("Algorithm").ok_or_else(|| {
2791 SigningDigestError::InvalidStructure(format!(
2792 "missing Algorithm attribute on <{element_name}>"
2793 ))
2794 })
2795}
2796
2797#[cfg(test)]
2798mod error_conversion_tests {
2799 use super::*;
2800 use crate::policy::PolicyViolation;
2801
2802 struct RejectingSigningKey;
2803
2804 impl SigningKey for RejectingSigningKey {
2805 fn sign(
2806 &self,
2807 _algorithm: SignatureAlgorithm,
2808 _canonical_signed_info: &[u8],
2809 ) -> Result<Vec<u8>, SigningKeyError> {
2810 Err(SigningKeyError::SigningFailed)
2811 }
2812
2813 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
2814 Err(SigningKeyError::PublicKeyEncodingFailed)
2815 }
2816 }
2817
2818 struct FixedRsaSigningKey;
2819
2820 impl SigningKey for FixedRsaSigningKey {
2821 fn sign(
2822 &self,
2823 _algorithm: SignatureAlgorithm,
2824 _canonical_signed_info: &[u8],
2825 ) -> Result<Vec<u8>, SigningKeyError> {
2826 Ok(vec![0x5a; 256])
2827 }
2828
2829 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
2830 Ok(SigningPublicKeyInfo::Rsa {
2831 spki_der: Vec::new(),
2832 modulus: vec![0x80; 256],
2833 exponent: vec![1, 0, 1],
2834 })
2835 }
2836 }
2837
2838 #[test]
2839 fn signing_error_promotes_every_policy_failure() {
2840 let digest = SigningError::from(SigningDigestError::Policy(PolicyViolation::Algorithm {
2843 operation: "signing",
2844 algorithm: "urn:test:digest".into(),
2845 }));
2846 assert!(matches!(digest, SigningError::Policy(_)));
2847
2848 let mutation = SigningError::from(SigningDigestError::XmlMutation(
2849 XmlMutationError::Policy(PolicyViolation::ResourceLimit {
2850 resource: crate::policy::resource_name::XML_DOCUMENT,
2851 maximum: 1,
2852 actual: 2,
2853 }),
2854 ));
2855 assert!(matches!(mutation, SigningError::Policy(_)));
2856 }
2857
2858 #[test]
2859 fn manifest_reparse_consumes_the_signature_wide_xpath_budget() {
2860 let filter = r#"<xf:XPath xmlns:xf="http://www.w3.org/2002/06/xmldsig-filter2" Filter="intersect">true()</xf:XPath>"#;
2864 let signed_info_transforms = format!(
2865 r#"<ds:Transform Algorithm="http://www.w3.org/2002/06/xmldsig-filter2">{}</ds:Transform><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform>"#,
2866 filter.repeat(64)
2867 );
2868 let signed_info_references = (0..62)
2869 .map(|index| {
2870 let extra = if index == 0 {
2871 r#"<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform>"#
2872 } else {
2873 ""
2874 };
2875 format!(
2876 r##"<ds:Reference URI="#payload"><ds:Transforms>{signed_info_transforms}{extra}</ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference>"##
2877 )
2878 })
2879 .collect::<String>();
2880 let manifest_reference = |id: &str| {
2881 format!(
2882 r##"<ds:Reference URI="#{id}"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference>"##
2883 )
2884 };
2885 let xml = format!(
2886 r##"<root><payload Id="payload"/><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>{signed_info_references}</ds:SignedInfo><ds:SignatureValue/><ds:Object><ds:Manifest>{}{}</ds:Manifest></ds:Object></ds:Signature></root>"##,
2887 manifest_reference("payload"),
2888 manifest_reference("payload")
2889 );
2890 let policy = crate::policy::SigningPolicy {
2891 manifest_processing: crate::policy::ManifestProcessing::Process,
2892 ..crate::policy::SigningPolicy::default()
2893 };
2894
2895 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
2896 let error = fill_reference_digest_values_in_dependency_order(
2897 &mut document,
2898 TransformOptions::default(),
2899 &policy,
2900 crate::provider::default_provider(),
2901 &mut SigningOperationBudgets::default(),
2902 0,
2903 &[],
2904 )
2905 .expect_err("Manifest reparse must not reset the XPath parse budget");
2906
2907 assert!(
2908 matches!(
2909 &error,
2910 SigningDigestError::Transform(TransformError::Policy(
2911 crate::policy::PolicyViolation::ResourceLimit {
2912 resource: "XPath expressions",
2913 ..
2914 }
2915 ))
2916 ),
2917 "expected the shared XPath budget error, got: {error:?}"
2918 );
2919 }
2920
2921 #[test]
2922 fn dependency_levels_share_the_xml_parse_work_budget() {
2923 let xml = r##"<root><payload Id="payload">nested payload</payload><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#outer"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/><ds:Object><ds:Manifest Id="outer"><ds:Reference URI="#inner"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:Manifest><ds:Manifest Id="inner"><ds:Reference URI="#payload"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:Manifest></ds:Object></ds:Signature></root>"##;
2927 let policy = crate::policy::SigningPolicy {
2928 manifest_processing: crate::policy::ManifestProcessing::Process,
2929 ..crate::policy::SigningPolicy::default()
2930 };
2931 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
2932 let mut budgets = SigningOperationBudgets::from_resources(&policy.resources);
2933
2934 fill_reference_digest_values_in_dependency_order(
2935 &mut document,
2936 TransformOptions::default(),
2937 &policy,
2938 crate::provider::default_provider(),
2939 &mut budgets,
2940 0,
2941 &[],
2942 )
2943 .expect("the default cumulative budget must cover nested dependencies");
2944 let consumed = budgets.transforms.xml_parse_work().consumed();
2945 assert!(
2946 consumed > xml.len().saturating_mul(6),
2947 "analysis and dependency reparses must all be charged"
2948 );
2949
2950 let mut constrained_policy = policy;
2951 constrained_policy.resources.max_xml_parse_work_bytes = consumed - 1;
2952 let mut constrained_document = XmlDocument::parse(xml).expect("fixture must parse");
2953 let mut constrained_budgets =
2954 SigningOperationBudgets::from_resources(&constrained_policy.resources);
2955 let error = fill_reference_digest_values_in_dependency_order(
2956 &mut constrained_document,
2957 TransformOptions::default(),
2958 &constrained_policy,
2959 crate::provider::default_provider(),
2960 &mut constrained_budgets,
2961 0,
2962 &[],
2963 )
2964 .expect_err("one byte below measured work must fail closed");
2965
2966 assert!(matches!(
2967 error,
2968 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
2969 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
2970 maximum,
2971 actual,
2972 }) if maximum == consumed - 1 && actual >= consumed
2973 ));
2974 }
2975
2976 #[test]
2977 fn final_signed_info_parse_consumes_the_signing_xpath_budget() {
2978 let xml = r##"<root><payload Id="payload">content</payload><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#payload"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
2982 let mut policy = crate::policy::SigningPolicy::default();
2983 policy.resources.max_xpath_expressions = 3;
2984
2985 let error = SignContext::new(&RejectingSigningKey)
2986 .policy(policy)
2987 .sign_template(xml)
2988 .expect_err("the final SignedInfo parse must not reset the XPath budget");
2989
2990 assert!(
2991 matches!(
2992 error,
2993 SigningError::Policy(PolicyViolation::ResourceLimit {
2994 resource: crate::policy::resource_name::XPATH_EXPRESSIONS,
2995 maximum: 3,
2996 ..
2997 })
2998 ),
2999 "expected the shared XPath parse budget error, got: {error:?}"
3000 );
3001 }
3002
3003 #[test]
3004 fn signing_initial_parse_consumes_the_operation_xml_parse_budget() {
3005 let xml = r##"<root><payload Id="payload"/><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#payload"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
3009 let mut policy = crate::policy::SigningPolicy::default();
3010 policy.resources.max_xml_parse_work_bytes = 0;
3011
3012 let error = SignContext::new(&RejectingSigningKey)
3013 .policy(policy)
3014 .sign_template(xml)
3015 .expect_err("a zero parse-work budget must reject the initial parse");
3016
3017 assert!(matches!(
3018 error,
3019 SigningError::Policy(PolicyViolation::ResourceLimit {
3020 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3021 maximum: 0,
3022 actual,
3023 }) if actual == xml.len()
3024 ));
3025 }
3026
3027 #[test]
3028 fn signing_string_entry_point_enforces_policy_depth() {
3029 let mut policy = crate::policy::SigningPolicy::default();
3032 policy.resources.max_xml_depth = 2;
3033 let xml = "<root><child><leaf/></child></root>";
3034
3035 assert!(matches!(
3036 SignContext::new(&RejectingSigningKey)
3037 .policy(policy)
3038 .sign_template(xml),
3039 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
3040 resource: crate::policy::resource_name::XML_DEPTH,
3041 maximum: 2,
3042 actual: 3,
3043 }))
3044 ));
3045 }
3046
3047 #[test]
3048 fn builder_append_reports_policy_depth() {
3049 let mut policy = crate::policy::SigningPolicy::default();
3052 policy.resources.max_xml_depth = 4;
3053 let builder = SignatureBuilder::new(
3054 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
3055 SignatureAlgorithm::RsaSha256,
3056 )
3057 .add_reference(crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri(""));
3058
3059 let result = SignContext::new(&FixedRsaSigningKey)
3060 .policy(policy)
3061 .sign_with_builder("<root/>", &builder);
3062 assert!(
3063 matches!(
3064 result,
3065 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
3066 resource: crate::policy::resource_name::XML_DEPTH,
3067 maximum: 4,
3068 actual: 5,
3069 }))
3070 ),
3071 "unexpected builder depth result: {result:?}"
3072 );
3073 }
3074
3075 #[test]
3076 fn owned_signing_staged_copies_preserve_policy_errors() {
3077 let mut policy = crate::policy::SigningPolicy::default();
3080 policy.resources.max_xml_parse_work_bytes = 0;
3081 let context = SignContext::new(&RejectingSigningKey).policy(policy);
3082
3083 let mut template_document = XmlDocument::parse("<root/>").expect("fixture must parse");
3084 let template_before = template_document.as_xml().to_owned();
3085 let error = context
3086 .sign_document(&mut template_document)
3087 .expect_err("the staged template copy must exhaust the operation budget");
3088 assert!(matches!(
3089 error,
3090 SigningError::Policy(PolicyViolation::ResourceLimit {
3091 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3092 maximum: 0,
3093 actual,
3094 }) if actual == template_before.len()
3095 ));
3096 assert_eq!(template_document.as_xml(), template_before);
3097 assert_eq!(template_document.generation(), 0);
3098
3099 let mut builder_document = XmlDocument::parse("<root/>").expect("fixture must parse");
3100 let builder_before = builder_document.as_xml().to_owned();
3101 let builder = SignatureBuilder::new(
3102 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
3103 SignatureAlgorithm::RsaSha256,
3104 );
3105 let error = context
3106 .sign_document_with_builder(&mut builder_document, &builder)
3107 .expect_err("the staged builder copy must exhaust the operation budget");
3108 assert!(matches!(
3109 error,
3110 SigningError::Policy(PolicyViolation::ResourceLimit {
3111 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3112 maximum: 0,
3113 actual,
3114 }) if actual == builder_before.len()
3115 ));
3116 assert_eq!(builder_document.as_xml(), builder_before);
3117 assert_eq!(builder_document.generation(), 0);
3118 }
3119
3120 #[test]
3121 fn owned_signing_commits_the_validated_stage_without_reparsing() {
3122 let xml = r##"<root><payload Id="payload">content</payload><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#payload"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
3126 let policy = crate::policy::SigningPolicy::default();
3127 let context = SignContext::new(&FixedRsaSigningKey).policy(policy.clone());
3128 let source = XmlDocument::parse(xml).expect("fixture must parse");
3129 let mut measured = SigningOperationBudgets::from_resources(&policy.resources);
3130 let mut staged = source
3131 .staged_copy_with_budget(
3132 DocumentParseSettings::from_policy(&policy.xml, &policy.resources),
3133 measured.transforms.xml_parse_work(),
3134 )
3135 .expect("staging must parse");
3136 context
3137 .sign_document_in_place(&mut staged, &mut measured)
3138 .expect("staged signing must succeed");
3139 let exact_stage_work = measured.transforms.xml_parse_work().consumed();
3140
3141 let mut constrained_policy = policy;
3142 constrained_policy.resources.max_xml_parse_work_bytes = exact_stage_work;
3143 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
3144 SignContext::new(&FixedRsaSigningKey)
3145 .policy(constrained_policy)
3146 .sign_document(&mut document)
3147 .expect("commit must not parse the validated stage again");
3148
3149 assert_eq!(document.generation(), 1);
3150 assert!(!document.as_xml().contains("<ds:DigestValue/>"));
3151 assert!(!document.as_xml().contains("<ds:SignatureValue/>"));
3152 }
3153
3154 #[test]
3155 fn builder_signing_fits_the_document_to_parse_work_ratio() {
3156 let padding = "x".repeat(64 * 1024);
3162 let xml = format!("<root><payload Id=\"payload\"/><padding>{padding}</padding></root>");
3163 let builder = SignatureBuilder::new(
3164 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
3165 SignatureAlgorithm::RsaSha256,
3166 )
3167 .add_reference(
3168 crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri("#payload"),
3169 );
3170 let maximum_document_bytes = xml.len() + 4 * 1024;
3171 let mut policy = crate::policy::SigningPolicy::default();
3172 policy.resources.max_xml_document_bytes = maximum_document_bytes;
3173 policy.resources.max_xml_parse_work_bytes =
3174 maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING;
3175
3176 let mut measurement_policy = policy.clone();
3177 measurement_policy.resources.max_xml_parse_work_bytes =
3178 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING;
3179 let measurement_context =
3180 SignContext::new(&FixedRsaSigningKey).policy(measurement_policy.clone());
3181 let mut measurement_budgets =
3182 SigningOperationBudgets::from_resources(&measurement_policy.resources);
3183 let mut measurement_document = XmlDocument::parse_with_settings_and_budget(
3184 xml.clone(),
3185 DocumentParseSettings::from_policy(
3186 &measurement_policy.xml,
3187 &measurement_policy.resources,
3188 ),
3189 measurement_budgets.transforms.xml_parse_work(),
3190 )
3191 .expect("measurement input must parse");
3192 measurement_context
3193 .sign_document_with_builder_in_place(
3194 &mut measurement_document,
3195 &builder,
3196 &mut measurement_budgets,
3197 )
3198 .expect("measurement signing must succeed");
3199 let consumed = measurement_budgets.transforms.xml_parse_work().consumed();
3200 assert!(
3201 consumed <= maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING,
3202 "builder signing consumed {consumed} bytes for a {maximum_document_bytes}-byte ceiling"
3203 );
3204
3205 let signed = SignContext::new(&FixedRsaSigningKey)
3206 .policy(policy.clone())
3207 .sign_with_builder(&xml, &builder)
3208 .expect("string builder signing must fit the advertised parse-work ratio");
3209 assert!(signed.contains("DigestValue>"));
3210 assert!(signed.contains("SignatureValue>"));
3211
3212 let mut owned = XmlDocument::parse(&xml).expect("fixture must parse");
3213 SignContext::new(&FixedRsaSigningKey)
3214 .policy(policy)
3215 .sign_document_with_builder(&mut owned, &builder)
3216 .expect("owned builder signing must fit the advertised parse-work ratio");
3217 assert!(owned.as_xml().contains("DigestValue>"));
3218 assert!(owned.as_xml().contains("SignatureValue>"));
3219 }
3220
3221 #[test]
3222 fn dtd_capable_staged_copy_charges_both_parser_passes() {
3223 let xml = "<root/>";
3226 let mut parsing_policy = crate::policy::SigningPolicy::default();
3227 parsing_policy.xml.allow_internal_dtd = true;
3228 let document = XmlDocument::parse_with_policy(xml, &parsing_policy)
3229 .expect("the fixture must retain DTD-capable parse settings");
3230
3231 parsing_policy.resources.max_xml_parse_work_bytes = xml.len();
3232 let budget = XmlParseWorkBudget::from_resources(&parsing_policy.resources);
3233 assert!(matches!(
3234 document.staged_copy_with_budget(DocumentParseSettings::default(), &budget),
3235 Err(XmlDocumentError::Policy(PolicyViolation::ResourceLimit {
3236 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
3237 maximum,
3238 actual,
3239 })) if maximum == xml.len() && actual == xml.len() * 2
3240 ));
3241 }
3242
3243 #[test]
3244 fn owned_signing_mappers_preserve_document_size_policy_errors() {
3245 let maximum = 8;
3248 let actual = 9;
3249 assert!(matches!(
3250 map_owned_document_mutation_error(XmlDocumentError::DocumentTooLarge {
3251 maximum,
3252 actual,
3253 }),
3254 SigningError::Policy(PolicyViolation::ResourceLimit {
3255 resource: crate::policy::resource_name::XML_DOCUMENT,
3256 maximum: 8,
3257 actual: 9,
3258 })
3259 ));
3260 assert!(matches!(
3261 map_owned_document_digest_mutation_error(XmlDocumentError::DocumentTooLarge {
3262 maximum,
3263 actual,
3264 }),
3265 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
3266 resource: crate::policy::resource_name::XML_DOCUMENT,
3267 maximum: 8,
3268 actual: 9,
3269 })
3270 ));
3271 }
3272
3273 #[test]
3274 fn signing_rejects_xpath_control_dependencies_on_digest_values() {
3275 let xml = r##"<root><payload Id="payload">content</payload><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#payload"><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>not(ancestor-or-self::ds:DigestValue) and (not(self::payload) or string(//ds:Reference[1]/ds:DigestValue) = '')</ds:XPath></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
3279
3280 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
3281 let error = fill_reference_digest_values_in_dependency_order(
3282 &mut document,
3283 TransformOptions::default(),
3284 &crate::policy::SigningPolicy::default(),
3285 crate::provider::default_provider(),
3286 &mut SigningOperationBudgets::default(),
3287 0,
3288 &[],
3289 )
3290 .expect_err("mutable XPath control dependencies must fail closed");
3291
3292 assert!(
3293 matches!(
3294 &error,
3295 SigningDigestError::InvalidStructure(message)
3296 if message.contains("dependency cycle")
3297 ),
3298 "expected a dependency-cycle rejection, got: {error:?}"
3299 );
3300 }
3301
3302 #[test]
3303 fn signing_allows_payload_local_xpath_value_predicates() {
3304 let xml = r##"<root><payload Id="payload" kind="include">content</payload><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI="#payload"><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>@kind = 'include'</ds:XPath></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
3307
3308 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
3309 fill_reference_digest_values_in_dependency_order(
3310 &mut document,
3311 TransformOptions::default(),
3312 &crate::policy::SigningPolicy::default(),
3313 crate::provider::default_provider(),
3314 &mut SigningOperationBudgets::default(),
3315 0,
3316 &[],
3317 )
3318 .expect("payload-local XPath predicates must not depend on Signature values");
3319
3320 assert_ne!(document.as_xml(), xml);
3321 }
3322
3323 #[test]
3324 fn signing_rejects_references_that_retain_signature_value() {
3325 let xml = r##"<root><ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/><ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/><ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>not(ancestor-or-self::ds:DigestValue)</ds:XPath></ds:Transform></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
3329
3330 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
3331 let error = fill_reference_digest_values_in_dependency_order(
3332 &mut document,
3333 TransformOptions::default(),
3334 &crate::policy::SigningPolicy::default(),
3335 crate::provider::default_provider(),
3336 &mut SigningOperationBudgets::default(),
3337 0,
3338 &[],
3339 )
3340 .expect_err("a mutable SignatureValue dependency must fail before signing");
3341
3342 assert!(
3343 matches!(
3344 &error,
3345 SigningDigestError::InvalidStructure(message)
3346 if message.contains("SignatureValue") && message.contains("cycle")
3347 ),
3348 "expected a SignatureValue dependency-cycle rejection, got: {error:?}"
3349 );
3350 }
3351}