1use crate::rsa_encoding::{RsaPrivateKeyEncoding as _, RsaPublicKeyEncoding as _};
9use crate::xml::dom::{Document, Node, NodeId};
10use base64::Engine;
11use hmac::{KeyInit, Mac};
12use p256::ecdsa::{
13 Signature as P256Signature, SigningKey as P256SigningKey, VerifyingKey as P256VerifyingKey,
14};
15use p256::pkcs8::{DecodePrivateKey, EncodePublicKey};
16use p384::ecdsa::{
17 Signature as P384Signature, SigningKey as P384SigningKey, VerifyingKey as P384VerifyingKey,
18};
19use p521::ecdsa::{
20 Signature as P521Signature, SigningKey as P521SigningKey, VerifyingKey as P521VerifyingKey,
21};
22use rsa::RsaPrivateKey;
23use rsa::pkcs1v15::Signature as RsaPkcs1v15Signature;
24use rsa::pkcs1v15::SigningKey as RsaPkcs1v15SigningKey;
25use rsa::signature::{RandomizedSigner, SignatureEncoding};
26use rsa::traits::PublicKeyParts;
27use sha1::Sha1;
28use sha2::{Sha224, Sha256, Sha384, Sha512};
29use signature::hazmat::{PrehashSigner, RandomizedPrehashSigner};
30use std::{
31 borrow::Cow,
32 collections::{HashMap, HashSet},
33 ops::Range,
34};
35use x509_parser::prelude::FromDer;
36use zeroize::Zeroizing;
37
38use crate::c14n::canonicalize_bounded_with_xml_base_budget;
39use crate::operation::{
40 OperationExecutionContext, OperationNodeId, OperationNodeKind, OperationPlanError,
41 OperationStage,
42};
43
44use super::builder::{SignatureBuilder, SignatureBuilderError};
45use super::digest::DigestAlgorithm;
46use super::mutation::{
47 XmlMutationError, fill_signed_info_digest_values_at_index_with_budget,
48 merge_key_info_source_at_index_with_budget, padded_base64_len_for_xml,
49};
50use super::parse::{
51 EC_P256_OID, EC_P384_OID, EC_P521_OID, MAX_REFERENCES_PER_SIGNATURE, SignatureAlgorithm,
52 XMLDSIG_NS, parse_signed_info_with_xpath_budget,
53};
54use super::signature::{encode_ecdsa_signature_as_der, maximum_ecdsa_der_signature_len};
55use super::transforms::{
56 BASE64_TRANSFORM_URI, ENVELOPED_SIGNATURE_URI, MAX_TRANSFORMS_PER_REFERENCE, Transform,
57 TransformExecutionBudget, TransformOptions, XPATH_FILTER2_TRANSFORM_URI, XPATH_TRANSFORM_URI,
58 XPathHereSemantics, XPathSignatureParseBudget, execute_transforms_with_dependency_nodes,
59 execute_transforms_with_options_and_budget, map_c14n_resource_policy_violation,
60 parse_transforms_with_budget, validate_signing_transform_policy,
61};
62use super::types::TransformError;
63use super::uri::{
64 ExternalResourceContext, ExternalResourceMapError, UriReferenceResolver,
65 validate_external_resource_map, validate_signing_reference_request,
66 validate_signing_reference_uri,
67};
68use super::verify::parse_signature_children;
69use crate::document::{DocumentParseSettings, XmlDocument, XmlDocumentError, XmlParseWorkBudget};
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
74pub struct ComputedReferenceDigest {
75 pub index: usize,
77 pub uri: String,
79 pub digest_method: DigestAlgorithm,
81 pub digest_value: String,
83}
84
85#[derive(Debug, thiserror::Error)]
87pub enum SigningDigestError {
88 #[error("cryptographic provider error: {0}")]
90 Provider(#[from] crate::provider::ProviderError),
91
92 #[error("signing policy violation: {0}")]
97 Policy(#[from] crate::policy::PolicyViolation),
98
99 #[error("XML parse error: {0}")]
101 XmlParse(#[from] crate::xml::dom::ParseError),
102
103 #[error("XML document error: {0}")]
105 Document(#[from] XmlDocumentError),
106
107 #[error("missing required element: <{element}>")]
109 MissingElement {
110 element: &'static str,
112 },
113
114 #[error("invalid signing template: {0}")]
116 InvalidStructure(String),
117
118 #[error("unsupported digest algorithm: {uri}")]
120 UnsupportedAlgorithm {
121 uri: String,
123 },
124
125 #[error("digest algorithm is disabled for signing: {uri}")]
127 SigningAlgorithmDisabled {
128 uri: &'static str,
130 },
131
132 #[error("reference processing error: {0}")]
134 Transform(#[from] TransformError),
135
136 #[error("XML mutation error: {0}")]
138 XmlMutation(#[from] XmlMutationError),
139}
140
141impl From<OperationPlanError> for SigningDigestError {
142 fn from(error: OperationPlanError) -> Self {
143 Self::InvalidStructure(error.to_string())
144 }
145}
146
147#[derive(Debug, thiserror::Error)]
149pub enum SigningError {
150 #[error("signing policy violation: {0}")]
152 Policy(#[from] crate::policy::PolicyViolation),
153
154 #[error("signing digest pass failed: {0}")]
156 Digest(SigningDigestError),
157
158 #[error("failed to parse SignedInfo: {0}")]
160 ParseSignedInfo(super::parse::ParseError),
161
162 #[error("SignedInfo canonicalization failed: {0}")]
164 Canonicalization(#[from] crate::c14n::C14nError),
165
166 #[error("signing key error: {0}")]
168 Key(#[from] SigningKeyError),
169
170 #[error("signature output must be {expected} bytes, got {actual}")]
172 InvalidSignatureOutputLength {
173 expected: usize,
175 actual: usize,
177 },
178
179 #[error("XML mutation error: {0}")]
181 XmlMutation(XmlMutationError),
182
183 #[error("KeyInfo writer error: {0}")]
185 KeyInfo(#[from] KeyInfoWriteError),
186
187 #[error("XML document error: {0}")]
189 Document(#[from] XmlDocumentError),
190
191 #[error("signature template error: {0}")]
193 Template(SignatureBuilderError),
194}
195
196impl From<OperationPlanError> for SigningError {
197 fn from(error: OperationPlanError) -> Self {
198 SigningDigestError::from(error).into()
199 }
200}
201
202impl From<SigningDigestError> for SigningError {
203 fn from(error: SigningDigestError) -> Self {
204 match error {
205 SigningDigestError::XmlMutation(XmlMutationError::Policy(error)) => Self::Policy(error),
206 SigningDigestError::Policy(error)
207 | SigningDigestError::Transform(TransformError::Policy(error)) => Self::Policy(error),
208 SigningDigestError::Document(error) => Self::Document(error),
209 error => Self::Digest(error),
210 }
211 }
212}
213
214impl From<super::parse::ParseError> for SigningError {
215 fn from(error: super::parse::ParseError) -> Self {
216 match error {
217 super::parse::ParseError::Policy(error)
218 | super::parse::ParseError::Transform(TransformError::Policy(error)) => {
219 Self::Policy(error)
220 }
221 error => Self::ParseSignedInfo(error),
222 }
223 }
224}
225
226impl From<XmlMutationError> for SigningError {
227 fn from(error: XmlMutationError) -> Self {
228 match error {
229 XmlMutationError::Policy(error) => Self::Policy(error),
230 error => Self::XmlMutation(error),
231 }
232 }
233}
234
235impl From<SignatureBuilderError> for SigningError {
236 fn from(error: SignatureBuilderError) -> Self {
237 match error {
238 SignatureBuilderError::Policy(error) => Self::Policy(error),
239 error => Self::Template(error),
240 }
241 }
242}
243
244#[derive(Debug, thiserror::Error)]
246#[non_exhaustive]
247pub enum SigningKeyError {
248 #[error("cryptographic provider error: {0}")]
250 Provider(#[from] crate::provider::ProviderError),
251
252 #[error("invalid PEM private key")]
254 InvalidKeyPem,
255
256 #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
258 InvalidKeyFormat {
259 label: String,
261 },
262
263 #[error("invalid PKCS#8 private key DER")]
265 InvalidKeyDer,
266
267 #[error("signing key does not support algorithm: {uri}")]
269 UnsupportedAlgorithm {
270 uri: String,
272 },
273
274 #[error("private-key signing operation failed")]
276 SigningFailed,
277
278 #[error("failed to encode signing public key as SPKI DER")]
280 PublicKeyEncodingFailed,
281
282 #[error("invalid signing public-key metadata")]
284 InvalidPublicKeyInfo,
285}
286
287#[derive(Debug, Clone, PartialEq, Eq)]
289#[non_exhaustive]
290pub enum SigningPublicKeyInfo {
291 Rsa {
293 spki_der: Vec<u8>,
295 modulus: Vec<u8>,
297 exponent: Vec<u8>,
299 },
300 Ec {
302 spki_der: Vec<u8>,
304 curve_oid: &'static str,
306 public_key: Vec<u8>,
308 },
309 Dsa {
311 spki_der: Vec<u8>,
313 p: Vec<u8>,
315 q: Vec<u8>,
317 g: Vec<u8>,
319 y: Vec<u8>,
321 modulus_bits: usize,
323 component_len: usize,
325 },
326 Hmac {
328 key_bits: usize,
330 },
331 EdDsa {
333 algorithm: SignatureAlgorithm,
335 spki_der: Vec<u8>,
337 },
338 PostQuantum {
340 algorithm: super::PqAlgorithm,
342 spki_der: Vec<u8>,
344 },
345}
346
347impl SigningPublicKeyInfo {
348 #[must_use]
350 pub fn spki_der(&self) -> Option<&[u8]> {
351 match self {
352 Self::Rsa { spki_der, .. }
353 | Self::Ec { spki_der, .. }
354 | Self::Dsa { spki_der, .. }
355 | Self::EdDsa { spki_der, .. } => Some(spki_der),
356 Self::PostQuantum { spki_der, .. } => Some(spki_der),
357 Self::Hmac { .. } => None,
358 }
359 }
360}
361
362pub fn validate_signing_key(
367 key: &dyn SigningKey,
368 algorithm: SignatureAlgorithm,
369 policy: &crate::policy::SigningPolicy,
370) -> Result<(), SigningError> {
371 policy.resources.validate_key_candidates(1)?;
372 policy.check_signature_algorithm(algorithm)?;
373 expected_signature_output_len(key, algorithm, policy, None).map(|_| ())
374}
375
376fn expected_signature_output_len(
377 key: &dyn SigningKey,
378 algorithm: SignatureAlgorithm,
379 policy: &crate::policy::SigningPolicy,
380 hmac_output_length_bits: Option<usize>,
381) -> Result<usize, SigningError> {
382 if let Some(full_bits) = algorithm.hmac_output_bits() {
384 policy
385 .hmac
386 .validate_output(algorithm, hmac_output_length_bits.unwrap_or(full_bits))?;
387 }
388 let public_key = key.public_key_info()?;
389 let expected = match (algorithm, public_key) {
390 (
391 SignatureAlgorithm::PostQuantum(algorithm),
392 SigningPublicKeyInfo::PostQuantum {
393 algorithm: key_algorithm,
394 ..
395 },
396 ) if algorithm == key_algorithm => algorithm.signature_len(),
397 (
398 algorithm,
399 SigningPublicKeyInfo::EdDsa {
400 algorithm: key_algorithm,
401 ..
402 },
403 ) if algorithm.eddsa_key_algorithm() == Some(key_algorithm) => algorithm
404 .eddsa_signature_len()
405 .ok_or(SigningKeyError::InvalidPublicKeyInfo)?,
406 (
407 method,
408 SigningPublicKeyInfo::Rsa {
409 modulus, exponent, ..
410 },
411 ) if method.is_rsa() => {
412 let width = policy
413 .rsa_keys
414 .validate_components("signing", &modulus, &exponent)?;
415 if let Some(parameters) = method.rsa_pss_parameters() {
416 let normalized = modulus
417 .iter()
418 .position(|byte| *byte != 0)
419 .map(|start| &modulus[start..])
420 .ok_or(SigningKeyError::InvalidPublicKeyInfo)?;
421 let bits = normalized.len() * 8 - normalized[0].leading_zeros() as usize;
422 if !parameters.fits_modulus_bits(bits) {
423 return Err(SigningKeyError::InvalidPublicKeyInfo.into());
424 }
425 }
426 width
427 }
428 (method, SigningPublicKeyInfo::Ec { public_key, .. })
429 if method.ecdsa_digest().is_some()
430 && public_key.first() == Some(&0x04)
431 && public_key.len() > 1
432 && (public_key.len() - 1).is_multiple_of(2) =>
433 {
434 public_key.len() - 1
437 }
438 (
439 algorithm @ (SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256),
440 SigningPublicKeyInfo::Dsa {
441 modulus_bits,
442 component_len,
443 ..
444 },
445 ) => {
446 policy.dsa_keys.validate_modulus_bits(modulus_bits)?;
447 let required_component_len = algorithm
448 .dsa_component_len()
449 .expect("DSA algorithm matched above");
450 if component_len != required_component_len {
451 return Err(SigningKeyError::UnsupportedAlgorithm {
452 uri: algorithm.uri().to_owned(),
453 }
454 .into());
455 }
456 component_len.saturating_mul(2)
457 }
458 (method, SigningPublicKeyInfo::Hmac { key_bits })
459 if method.hmac_output_bits().is_some() =>
460 {
461 policy.hmac.validate_key_bits(key_bits)?;
462 let output_bits = hmac_output_length_bits.unwrap_or(
463 algorithm
464 .hmac_output_bits()
465 .ok_or(SigningKeyError::InvalidPublicKeyInfo)?,
466 );
467 output_bits / 8
468 }
469 (
470 SignatureAlgorithm::RsaSha1
471 | SignatureAlgorithm::RsaSha224
472 | SignatureAlgorithm::RsaSha256
473 | SignatureAlgorithm::RsaSha384
474 | SignatureAlgorithm::RsaSha512,
475 SigningPublicKeyInfo::Ec { .. }
476 | SigningPublicKeyInfo::Dsa { .. }
477 | SigningPublicKeyInfo::Hmac { .. },
478 )
479 | (
480 SignatureAlgorithm::EcdsaSha1
481 | SignatureAlgorithm::EcdsaSha224
482 | SignatureAlgorithm::EcdsaSha256
483 | SignatureAlgorithm::EcdsaSha384
484 | SignatureAlgorithm::EcdsaSha512
485 | SignatureAlgorithm::EcdsaSha3_224
486 | SignatureAlgorithm::EcdsaSha3_256
487 | SignatureAlgorithm::EcdsaSha3_384
488 | SignatureAlgorithm::EcdsaSha3_512,
489 SigningPublicKeyInfo::Rsa { .. }
490 | SigningPublicKeyInfo::Dsa { .. }
491 | SigningPublicKeyInfo::Hmac { .. },
492 ) => {
493 return Err(SigningKeyError::UnsupportedAlgorithm {
494 uri: algorithm.uri().to_owned(),
495 }
496 .into());
497 }
498 _ => return Err(SigningKeyError::InvalidPublicKeyInfo.into()),
499 };
500 Ok(expected)
501}
502
503fn validate_signature_output(expected: usize, signature: &[u8]) -> Result<(), SigningError> {
504 if signature.len() != expected {
505 return Err(SigningError::InvalidSignatureOutputLength {
506 expected,
507 actual: signature.len(),
508 });
509 }
510 Ok(())
511}
512
513pub trait SigningKey {
515 fn provider_name(&self) -> Option<&'static str> {
517 None
518 }
519 fn sign_with_provider_context(
521 &self,
522 provider: &dyn crate::provider::CryptoProvider,
523 algorithm: SignatureAlgorithm,
524 context: &super::SignatureContext,
525 data: &[u8],
526 ) -> Result<Vec<u8>, SigningKeyError> {
527 if !context.as_bytes().is_empty() {
528 return Err(SigningKeyError::UnsupportedAlgorithm {
529 uri: algorithm.uri().to_owned(),
530 });
531 }
532 self.sign_with_provider(provider, algorithm, data)
533 }
534 fn sign(
536 &self,
537 algorithm: SignatureAlgorithm,
538 canonical_signed_info: &[u8],
539 ) -> Result<Vec<u8>, SigningKeyError>;
540
541 fn sign_with_provider(
546 &self,
547 provider: &dyn crate::provider::CryptoProvider,
548 algorithm: SignatureAlgorithm,
549 canonical_signed_info: &[u8],
550 ) -> Result<Vec<u8>, SigningKeyError> {
551 let _ = provider;
552 self.sign(algorithm, canonical_signed_info)
553 }
554
555 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
557}
558
559pub trait KeyInfoWriter {
561 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
563
564 fn write_key_info_with_provider(
569 &self,
570 signing_key: &dyn SigningKey,
571 provider: &dyn crate::provider::CryptoProvider,
572 ) -> Result<String, KeyInfoWriteError> {
573 let _ = provider;
574 self.write_key_info(signing_key)
575 }
576}
577
578#[derive(Debug, thiserror::Error)]
580#[non_exhaustive]
581pub enum KeyInfoWriteError {
582 #[error("cryptographic provider error: {0}")]
584 Provider(#[from] crate::provider::ProviderError),
585
586 #[error("invalid PEM certificate")]
588 InvalidCertificatePem,
589
590 #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
592 InvalidCertificateFormat {
593 label: String,
595 },
596
597 #[error("invalid X.509 certificate DER")]
599 InvalidCertificateDer,
600
601 #[error("X.509 certificate chain must not be empty")]
603 EmptyCertificateChain,
604
605 #[error("signing key public-key extraction failed: {0}")]
607 SigningKey(#[from] SigningKeyError),
608
609 #[error("signing key has no DER-encodable public key")]
611 MissingPublicKey,
612
613 #[error("signing key cannot be represented as XMLDSig KeyValue")]
615 UnsupportedKeyValue,
616
617 #[error("X.509 certificate public key does not match signing key")]
619 CertificateKeyMismatch,
620}
621
622#[derive(Debug, Clone, Copy, Default)]
624pub struct DerEncodedKeyValueInfoWriter;
625
626impl KeyInfoWriter for DerEncodedKeyValueInfoWriter {
627 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
628 let public_key = signing_key.public_key_info()?;
629 let spki_der = public_key
630 .spki_der()
631 .ok_or(KeyInfoWriteError::MissingPublicKey)?;
632 let encoded = base64::engine::general_purpose::STANDARD.encode(spki_der);
633 Ok(format!(
634 "<dsig11:DEREncodedKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\">{encoded}</dsig11:DEREncodedKeyValue>"
635 ))
636 }
637}
638
639#[derive(Debug, Clone, Copy, Default)]
641pub struct KeyValueInfoWriter;
642
643impl KeyInfoWriter for KeyValueInfoWriter {
644 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
645 let encode = |bytes: &[u8]| base64::engine::general_purpose::STANDARD.encode(bytes);
646 match signing_key.public_key_info()? {
647 SigningPublicKeyInfo::Rsa {
648 modulus, exponent, ..
649 } => Ok(format!(
650 "<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>",
651 encode(&modulus),
652 encode(&exponent)
653 )),
654 SigningPublicKeyInfo::Ec {
655 curve_oid,
656 public_key,
657 ..
658 } => Ok(format!(
659 "<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>",
660 encode(&public_key)
661 )),
662 SigningPublicKeyInfo::Dsa { p, q, g, y, .. } => Ok(format!(
663 "<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>",
664 encode(&p),
665 encode(&q),
666 encode(&g),
667 encode(&y)
668 )),
669 SigningPublicKeyInfo::Hmac { .. }
670 | SigningPublicKeyInfo::EdDsa { .. }
671 | SigningPublicKeyInfo::PostQuantum { .. } => {
672 Err(KeyInfoWriteError::UnsupportedKeyValue)
673 }
674 }
675 }
676}
677
678pub struct X509CertificateKeyInfoWriter {
680 certificates_der: Vec<Vec<u8>>,
681}
682
683impl X509CertificateKeyInfoWriter {
684 pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
686 Self::from_pem_chain([certificate_pem])
687 }
688
689 pub fn from_pem_chain<I, S>(certificate_pems: I) -> Result<Self, KeyInfoWriteError>
695 where
696 I: IntoIterator<Item = S>,
697 S: AsRef<str>,
698 {
699 let mut certificates_der = Vec::new();
700 for certificate_pem in certificate_pems {
701 certificates_der.push(parse_certificate_pem(certificate_pem.as_ref())?);
702 }
703 Self::from_der_chain(certificates_der)
704 }
705
706 pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
708 Self::from_der_chain([certificate_der])
709 }
710
711 pub fn from_der_chain<I, B>(certificates_der: I) -> Result<Self, KeyInfoWriteError>
717 where
718 I: IntoIterator<Item = B>,
719 B: AsRef<[u8]>,
720 {
721 let certificates_der = certificates_der
722 .into_iter()
723 .map(|certificate_der| {
724 let certificate_der = certificate_der.as_ref();
725 let (rest, _) =
726 x509_parser::certificate::X509Certificate::from_der(certificate_der)
727 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
728 if !rest.is_empty() {
729 return Err(KeyInfoWriteError::InvalidCertificateDer);
730 }
731 Ok(certificate_der.to_vec())
732 })
733 .collect::<Result<Vec<_>, _>>()?;
734 if certificates_der.is_empty() {
735 return Err(KeyInfoWriteError::EmptyCertificateChain);
736 }
737 Ok(Self { certificates_der })
738 }
739}
740
741fn parse_certificate_pem(certificate_pem: &str) -> Result<Vec<u8>, KeyInfoWriteError> {
742 let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
743 .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
744 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
745 return Err(KeyInfoWriteError::InvalidCertificatePem);
746 }
747 if pem.label != "CERTIFICATE" {
748 return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
749 }
750 Ok(pem.contents)
751}
752
753impl KeyInfoWriter for X509CertificateKeyInfoWriter {
754 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
755 let leaf_der = &self.certificates_der[0];
756 let (rest, certificate) = x509_parser::certificate::X509Certificate::from_der(leaf_der)
757 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
758 if !rest.is_empty() {
759 return Err(KeyInfoWriteError::InvalidCertificateDer);
760 }
761 let signing_public_key = signing_key.public_key_info()?;
762 if signing_public_key.spki_der() != Some(certificate.public_key().raw) {
763 return Err(KeyInfoWriteError::CertificateKeyMismatch);
764 }
765
766 let mut xml = format!("<X509Data xmlns=\"{XMLDSIG_NS}\">");
767 for certificate_der in &self.certificates_der {
768 let certificate_b64 = base64::engine::general_purpose::STANDARD.encode(certificate_der);
769 xml.push_str("<X509Certificate>");
770 xml.push_str(&certificate_b64);
771 xml.push_str("</X509Certificate>");
772 }
773 xml.push_str("</X509Data>");
774 Ok(xml)
775 }
776}
777
778pub struct X509DigestKeyInfoWriter {
780 certificate_der: Vec<u8>,
781 certificate_spki_der: Vec<u8>,
782 digest_algorithm: DigestAlgorithm,
783}
784
785impl X509DigestKeyInfoWriter {
786 pub fn from_der(
788 certificate_der: &[u8],
789 digest_algorithm: DigestAlgorithm,
790 ) -> Result<Self, KeyInfoWriteError> {
791 let (rest, certificate) =
792 x509_parser::certificate::X509Certificate::from_der(certificate_der)
793 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
794 if !rest.is_empty() {
795 return Err(KeyInfoWriteError::InvalidCertificateDer);
796 }
797 Ok(Self {
798 certificate_der: certificate_der.to_vec(),
799 certificate_spki_der: certificate.public_key().raw.to_vec(),
800 digest_algorithm,
801 })
802 }
803
804 pub fn from_pem(
806 certificate_pem: &str,
807 digest_algorithm: DigestAlgorithm,
808 ) -> Result<Self, KeyInfoWriteError> {
809 Self::from_der(&parse_certificate_pem(certificate_pem)?, digest_algorithm)
810 }
811}
812
813impl KeyInfoWriter for X509DigestKeyInfoWriter {
814 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
815 self.write_key_info_with_provider(signing_key, crate::provider::default_provider())
816 }
817
818 fn write_key_info_with_provider(
819 &self,
820 signing_key: &dyn SigningKey,
821 provider: &dyn crate::provider::CryptoProvider,
822 ) -> Result<String, KeyInfoWriteError> {
823 if signing_key.public_key_info()?.spki_der() != Some(self.certificate_spki_der.as_slice()) {
824 return Err(KeyInfoWriteError::CertificateKeyMismatch);
825 }
826 let digest = super::compute_digest_with_provider(
827 provider,
828 self.digest_algorithm,
829 &self.certificate_der,
830 )?;
831 let encoded = base64::engine::general_purpose::STANDARD.encode(digest);
832 Ok(format!(
833 "<ds:X509Data xmlns:ds=\"{XMLDSIG_NS}\"><dsig11:X509Digest xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\" Algorithm=\"{}\">{encoded}</dsig11:X509Digest></ds:X509Data>",
834 self.digest_algorithm.uri()
835 ))
836 }
837}
838
839pub struct RsaSigningKey {
841 key: RsaPrivateKey,
842}
843
844impl RsaSigningKey {
845 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
847 let private_key_der = parse_private_key_pem(private_key_pem)?;
848 Self::from_pkcs8_der(&private_key_der)
849 }
850
851 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
853 let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
854 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
855 Ok(Self { key })
856 }
857
858 pub fn from_pkcs8_encrypted_pem(
860 private_key_pem: &str,
861 password: impl AsRef<[u8]>,
862 ) -> Result<Self, SigningKeyError> {
863 let key = RsaPrivateKey::from_pkcs8_encrypted_pem(private_key_pem, password)
864 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
865 Ok(Self { key })
866 }
867
868 pub fn from_pkcs8_encrypted_der(
870 private_key_der: &[u8],
871 password: impl AsRef<[u8]>,
872 ) -> Result<Self, SigningKeyError> {
873 let key = RsaPrivateKey::from_pkcs8_encrypted_der(private_key_der, password)
874 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
875 Ok(Self { key })
876 }
877}
878
879impl SigningKey for RsaSigningKey {
880 fn sign(
881 &self,
882 algorithm: SignatureAlgorithm,
883 canonical_signed_info: &[u8],
884 ) -> Result<Vec<u8>, SigningKeyError> {
885 self.sign_with_provider(
886 crate::provider::default_provider(),
887 algorithm,
888 canonical_signed_info,
889 )
890 }
891
892 fn sign_with_provider(
893 &self,
894 provider: &dyn crate::provider::CryptoProvider,
895 algorithm: SignatureAlgorithm,
896 canonical_signed_info: &[u8],
897 ) -> Result<Vec<u8>, SigningKeyError> {
898 if let Some(parameters) = algorithm.rsa_pss_parameters() {
899 return crate::provider::rsa_pss::sign(
900 provider,
901 &self.key,
902 parameters,
903 canonical_signed_info,
904 );
905 }
906 match algorithm {
907 #[cfg(feature = "legacy-algorithms")]
908 SignatureAlgorithm::RsaMd5 => sign_rsa_pkcs1v15_with_rng(
909 provider,
910 RsaPkcs1v15SigningKey::<md5::Md5>::new(self.key.clone()),
911 canonical_signed_info,
912 ),
913 #[cfg(feature = "legacy-algorithms")]
914 SignatureAlgorithm::RsaRipemd160 => sign_rsa_pkcs1v15_with_rng(
915 provider,
916 RsaPkcs1v15SigningKey::<ripemd::Ripemd160>::new(self.key.clone()),
917 canonical_signed_info,
918 ),
919 SignatureAlgorithm::RsaSha1 => sign_rsa_pkcs1v15_with_rng(
920 provider,
921 RsaPkcs1v15SigningKey::<Sha1>::new(self.key.clone()),
922 canonical_signed_info,
923 ),
924 SignatureAlgorithm::RsaSha224 => sign_rsa_pkcs1v15_with_rng(
925 provider,
926 RsaPkcs1v15SigningKey::<Sha224>::new(self.key.clone()),
927 canonical_signed_info,
928 ),
929 SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
930 provider,
931 RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
932 canonical_signed_info,
933 ),
934 SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
935 provider,
936 RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
937 canonical_signed_info,
938 ),
939 SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
940 provider,
941 RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
942 canonical_signed_info,
943 ),
944 _ => Err(SigningKeyError::UnsupportedAlgorithm {
945 uri: algorithm.uri().to_string(),
946 }),
947 }
948 }
949
950 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
951 let public_key = self.key.to_public_key();
952 let spki_der = public_key
953 .to_public_key_der()
954 .map(|doc| doc.as_bytes().to_vec())
955 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
956 Ok(SigningPublicKeyInfo::Rsa {
957 spki_der,
958 modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
959 exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
960 })
961 }
962}
963
964pub struct HmacSigningKey {
968 secret: Zeroizing<Vec<u8>>,
969}
970
971impl HmacSigningKey {
972 pub fn new(secret: impl Into<Vec<u8>>) -> Result<Self, SigningKeyError> {
974 let secret = secret.into();
975 if secret.is_empty() {
976 return Err(SigningKeyError::InvalidKeyDer);
977 }
978 Ok(Self {
979 secret: Zeroizing::new(secret),
980 })
981 }
982}
983
984impl SigningKey for HmacSigningKey {
985 fn sign(
986 &self,
987 algorithm: SignatureAlgorithm,
988 canonical_signed_info: &[u8],
989 ) -> Result<Vec<u8>, SigningKeyError> {
990 macro_rules! sign_hmac {
991 ($digest:ty) => {{
992 let mut mac = hmac::Hmac::<$digest>::new_from_slice(&self.secret)
993 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
994 mac.update(canonical_signed_info);
995 mac.finalize().into_bytes().to_vec()
996 }};
997 }
998 Ok(match algorithm {
999 #[cfg(feature = "legacy-algorithms")]
1000 SignatureAlgorithm::HmacMd5 => sign_hmac!(md5::Md5),
1001 #[cfg(feature = "legacy-algorithms")]
1002 SignatureAlgorithm::HmacRipemd160 => sign_hmac!(ripemd::Ripemd160),
1003 SignatureAlgorithm::HmacSha1 => sign_hmac!(sha1::Sha1),
1004 SignatureAlgorithm::HmacSha224 => sign_hmac!(sha2::Sha224),
1005 SignatureAlgorithm::HmacSha256 => sign_hmac!(sha2::Sha256),
1006 SignatureAlgorithm::HmacSha384 => sign_hmac!(sha2::Sha384),
1007 SignatureAlgorithm::HmacSha512 => sign_hmac!(sha2::Sha512),
1008 _ => {
1009 return Err(SigningKeyError::UnsupportedAlgorithm {
1010 uri: algorithm.uri().to_owned(),
1011 });
1012 }
1013 })
1014 }
1015
1016 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1017 Ok(SigningPublicKeyInfo::Hmac {
1018 key_bits: self.secret.len().saturating_mul(8),
1019 })
1020 }
1021}
1022
1023pub struct DsaSigningKey {
1025 key: dsa::SigningKey,
1026}
1027
1028impl DsaSigningKey {
1029 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1031 let private_key_der = parse_private_key_pem(private_key_pem)?;
1032 Self::from_pkcs8_der(&private_key_der)
1033 }
1034
1035 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1037 use der::Decode;
1038 let info = pkcs8::PrivateKeyInfoRef::from_der(private_key_der)
1039 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1040 let key = decode_dsa_signing_key(info)?;
1041 Ok(Self { key })
1042 }
1043
1044 pub fn from_pkcs8_encrypted_pem(
1046 private_key_pem: &str,
1047 password: impl AsRef<[u8]>,
1048 ) -> Result<Self, SigningKeyError> {
1049 let (label, encrypted) = pkcs8::SecretDocument::from_pem(private_key_pem)
1050 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1051 if label != "ENCRYPTED PRIVATE KEY" {
1052 return Err(SigningKeyError::InvalidKeyDer);
1053 }
1054 Self::from_pkcs8_encrypted_der(encrypted.as_bytes(), password)
1055 }
1056
1057 pub fn from_pkcs8_encrypted_der(
1059 private_key_der: &[u8],
1060 password: impl AsRef<[u8]>,
1061 ) -> Result<Self, SigningKeyError> {
1062 use der::Decode;
1063 let encrypted = pkcs8::EncryptedPrivateKeyInfoRef::from_der(private_key_der)
1064 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1065 let plain = encrypted
1066 .decrypt(password)
1067 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1068 Self::from_pkcs8_der(plain.as_bytes())
1069 }
1070}
1071
1072pub(crate) fn preflight_dsa_private_key(
1073 info: &pkcs8::PrivateKeyInfoRef<'_>,
1074) -> Result<(), SigningKeyError> {
1075 use der::Decode;
1076 if info.algorithm.oid != dsa::OID {
1077 return Err(SigningKeyError::InvalidKeyDer);
1078 }
1079 let parameters = info
1080 .algorithm
1081 .parameters
1082 .as_ref()
1083 .ok_or(SigningKeyError::InvalidKeyDer)?
1084 .decode_as::<super::signature::BorrowedDsaPublicParameters<'_>>()
1085 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1086 let x = der::asn1::UintRef::from_der(info.private_key.as_bytes())
1087 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1088 for component in [parameters.p, parameters.q, parameters.g, x] {
1091 if component.as_bytes().len() > crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING {
1092 return Err(SigningKeyError::InvalidKeyDer);
1093 }
1094 }
1095 if let Some(public) = info.public_key {
1096 let bytes = public.as_bytes().ok_or(SigningKeyError::InvalidKeyDer)?;
1097 let y = der::asn1::UintRef::from_der(bytes).map_err(|_| SigningKeyError::InvalidKeyDer)?;
1098 if y.as_bytes().len() > crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING {
1099 return Err(SigningKeyError::InvalidKeyDer);
1100 }
1101 }
1102 Ok(())
1103}
1104
1105pub(crate) fn decode_dsa_signing_key(
1106 mut info: pkcs8::PrivateKeyInfoRef<'_>,
1107) -> Result<dsa::SigningKey, SigningKeyError> {
1108 use der::Decode;
1109 preflight_dsa_private_key(&info)?;
1110 let public = info.public_key.take();
1111 let key = dsa::SigningKey::try_from(info).map_err(|_| SigningKeyError::InvalidKeyDer)?;
1116 if let Some(public) = public {
1117 let y = der::asn1::UintRef::from_der(public.raw_bytes())
1118 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1119 let supplied = crypto_bigint::BoxedUint::from_be_slice_vartime(y.as_bytes());
1120 if supplied != **key.verifying_key().y() {
1121 return Err(SigningKeyError::InvalidKeyDer);
1122 }
1123 }
1124 Ok(key)
1125}
1126
1127#[cfg(test)]
1128mod dsa_import_tests {
1129 use super::*;
1130 use der::{Decode, Encode};
1131
1132 fn plaintext() -> pkcs8::SecretDocument {
1133 pkcs8::EncryptedPrivateKeyInfoRef::from_der(include_bytes!(
1134 "../../tests/fixtures/xmldsig/keys/dsa/dsa-2048-key.p8-der"
1135 ))
1136 .expect("valid encrypted DSA fixture")
1137 .decrypt(b"secret123")
1138 .expect("known fixture password")
1139 }
1140
1141 #[test]
1142 fn dsa_plain_pem_reports_the_failing_format_layer() -> Result<(), Box<dyn std::error::Error>> {
1143 assert!(matches!(
1146 DsaSigningKey::from_pkcs8_pem("invalid PEM"),
1147 Err(SigningKeyError::InvalidKeyPem)
1148 ));
1149 let wrong_label = pem::encode(&pem::Pem::new("PUBLIC KEY", vec![1, 2, 3]));
1150 assert!(matches!(
1151 DsaSigningKey::from_pkcs8_pem(&wrong_label),
1152 Err(SigningKeyError::InvalidKeyFormat { label }) if label == "PUBLIC KEY"
1153 ));
1154 let bad_der = pem::encode(&pem::Pem::new("PRIVATE KEY", vec![1, 2, 3]));
1155 assert!(matches!(
1156 DsaSigningKey::from_pkcs8_pem(&bad_der),
1157 Err(SigningKeyError::InvalidKeyDer)
1158 ));
1159 let valid = plaintext().to_pem("PRIVATE KEY", der::pem::LineEnding::LF)?;
1160 assert!(DsaSigningKey::from_pkcs8_pem(&valid).is_ok());
1161 assert!(matches!(
1162 DsaSigningKey::from_pkcs8_pem(&format!("{}unexpected trailing data", valid.as_str())),
1163 Err(SigningKeyError::InvalidKeyPem)
1164 ));
1165 Ok(())
1166 }
1167
1168 #[test]
1169 fn dsa_pkcs8_import_formats_and_password_failures() -> Result<(), Box<dyn std::error::Error>> {
1170 let plain = plaintext();
1173 let pem = plain.to_pem("PRIVATE KEY", der::pem::LineEnding::LF)?;
1174 let encrypted = include_bytes!("../../tests/fixtures/xmldsig/keys/dsa/dsa-2048-key.p8-der");
1175 let encrypted_doc = pkcs8::SecretDocument::try_from(encrypted.as_slice())?;
1176 let encrypted_pem =
1177 encrypted_doc.to_pem("ENCRYPTED PRIVATE KEY", der::pem::LineEnding::LF)?;
1178 let key = DsaSigningKey::from_pkcs8_der(plain.as_bytes())?;
1179 let expected = key.public_key_info()?;
1180 for imported in [
1181 DsaSigningKey::from_pkcs8_pem(&pem)?,
1182 DsaSigningKey::from_pkcs8_encrypted_der(encrypted, b"secret123")?,
1183 DsaSigningKey::from_pkcs8_encrypted_pem(&encrypted_pem, b"secret123")?,
1184 ] {
1185 assert_eq!(imported.public_key_info()?, expected);
1186 }
1187 assert!(DsaSigningKey::from_pkcs8_encrypted_der(encrypted, b"wrong").is_err());
1188 assert!(DsaSigningKey::from_pkcs8_encrypted_pem(&encrypted_pem, b"wrong").is_err());
1189 assert!(DsaSigningKey::from_pkcs8_encrypted_pem(&pem, b"secret123").is_err());
1190 assert!(DsaSigningKey::from_pkcs8_pem(&encrypted_pem).is_err());
1191 assert!(DsaSigningKey::from_pkcs8_pem("invalid PEM").is_err());
1192 assert!(DsaSigningKey::from_pkcs8_der(b"invalid DER").is_err());
1193 Ok(())
1194 }
1195
1196 #[test]
1197 fn dsa_preflight_bounds_every_private_key_component() -> Result<(), Box<dyn std::error::Error>>
1198 {
1199 let plain = plaintext();
1202 let original = pkcs8::PrivateKeyInfoRef::from_der(plain.as_bytes())?;
1203 let oversized = vec![0x7f; crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING + 1];
1204 let large = der::asn1::UintRef::new(&oversized)?;
1205 for field in 0..5 {
1206 let mut parameters = original
1207 .algorithm
1208 .parameters
1209 .as_ref()
1210 .expect("DSA fixture parameters")
1211 .decode_as::<super::super::signature::BorrowedDsaPublicParameters<'_>>()?;
1212 match field {
1213 0 => parameters.p = large,
1214 1 => parameters.q = large,
1215 2 => parameters.g = large,
1216 _ => {}
1217 }
1218 let encoded_parameters = parameters.to_der()?;
1219 let encoded_large = large.to_der()?;
1220 let mut info = original.clone();
1221 info.algorithm.parameters = Some(der::asn1::AnyRef::from_der(&encoded_parameters)?);
1222 if field == 3 {
1223 info.private_key = der::asn1::OctetStringRef::new(&encoded_large)?;
1224 }
1225 if field == 4 {
1226 info.public_key = Some(der::asn1::BitStringRef::new(0, &encoded_large)?);
1227 }
1228 assert!(preflight_dsa_private_key(&info).is_err(), "field {field}");
1229 assert!(
1230 DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err(),
1231 "field {field}"
1232 );
1233 }
1234 Ok(())
1235 }
1236
1237 #[test]
1238 fn accepts_dsa_pkcs8_with_consistent_public_component() -> Result<(), Box<dyn std::error::Error>>
1239 {
1240 let plain = plaintext();
1243 let key = DsaSigningKey::from_pkcs8_der(plain.as_bytes())?;
1244 let SigningPublicKeyInfo::Dsa { spki_der, .. } = key.public_key_info()? else {
1245 panic!("DSA key")
1246 };
1247 let spki = pkcs8::SubjectPublicKeyInfoRef::from_der(&spki_der)?;
1248 let mut info = pkcs8::PrivateKeyInfoRef::from_der(plain.as_bytes())?;
1249 info.public_key = Some(spki.subject_public_key);
1250 let encoded = info.to_der()?;
1251 assert_eq!(
1252 DsaSigningKey::from_pkcs8_der(&encoded)?.public_key_info()?,
1253 key.public_key_info()?
1254 );
1255 crate::key_manager::KeyInventory::default().add_private_der(
1257 "consistent-dsa".into(),
1258 &encoded,
1259 None,
1260 crate::key_manager::KeyUsages::SIGN,
1261 &crate::policy::ResourcePolicy::default(),
1262 )?;
1263 Ok(())
1264 }
1265
1266 #[test]
1267 fn rejects_invalid_dsa_private_exponents_and_parameters()
1268 -> Result<(), Box<dyn std::error::Error>> {
1269 let plain = plaintext();
1272 let original = pkcs8::PrivateKeyInfoRef::from_der(plain.as_bytes())?;
1273 let parameters = original
1274 .algorithm
1275 .parameters
1276 .as_ref()
1277 .expect("DSA fixture parameters")
1278 .decode_as::<super::super::signature::BorrowedDsaPublicParameters<'_>>()?;
1279 for exponent in [der::asn1::UintRef::new(&[0])?, parameters.q] {
1280 let encoded = exponent.to_der()?;
1281 let mut info = original.clone();
1282 info.private_key = der::asn1::OctetStringRef::new(&encoded)?;
1283 assert!(DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err());
1284 }
1285 let mut info = original.clone();
1286 info.algorithm.parameters = None;
1287 assert!(DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err());
1288 let mut info = original.clone();
1290 info.algorithm.parameters = Some(der::asn1::AnyRef::from_der(&[0x02, 0x01, 0x01])?);
1291 assert!(DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err());
1292 let mut info = original.clone();
1294 info.public_key = Some(der::asn1::BitStringRef::new(1, &[0x02])?);
1295 assert!(DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err());
1296 let mut info = original;
1297 info.algorithm.oid = pkcs1::ALGORITHM_OID;
1298 assert!(DsaSigningKey::from_pkcs8_der(&info.to_der()?).is_err());
1299 Ok(())
1300 }
1301
1302 #[test]
1303 fn rejects_dsa_pkcs8_with_inconsistent_public_component()
1304 -> Result<(), Box<dyn std::error::Error>> {
1305 let plain = plaintext();
1308 let mut info = pkcs8::PrivateKeyInfoRef::from_der(plain.as_bytes())?;
1309 let parameters = info
1310 .algorithm
1311 .parameters
1312 .as_ref()
1313 .expect("DSA fixture parameters")
1314 .decode_as::<super::super::signature::BorrowedDsaPublicParameters<'_>>()?;
1315 let wrong_public = parameters.g.to_der()?;
1316 info.public_key = Some(der::asn1::BitStringRef::new(0, &wrong_public)?);
1317 let encoded = info.to_der()?;
1318 assert!(matches!(
1319 DsaSigningKey::from_pkcs8_der(&encoded),
1320 Err(SigningKeyError::InvalidKeyDer)
1321 ));
1322 assert!(matches!(
1324 crate::key_manager::KeyInventory::default().add_private_der(
1325 "inconsistent-dsa".into(),
1326 &encoded,
1327 None,
1328 crate::key_manager::KeyUsages::SIGN,
1329 &crate::policy::ResourcePolicy::default(),
1330 ),
1331 Err(crate::key_manager::KeyStoreError::Selection(
1332 "invalid or oversized DSA private key"
1333 ))
1334 ));
1335 Ok(())
1336 }
1337}
1338
1339impl SigningKey for DsaSigningKey {
1340 fn sign(
1341 &self,
1342 algorithm: SignatureAlgorithm,
1343 canonical_signed_info: &[u8],
1344 ) -> Result<Vec<u8>, SigningKeyError> {
1345 self.sign_with_provider(
1346 crate::provider::default_provider(),
1347 algorithm,
1348 canonical_signed_info,
1349 )
1350 }
1351
1352 fn sign_with_provider(
1353 &self,
1354 provider: &dyn crate::provider::CryptoProvider,
1355 algorithm: SignatureAlgorithm,
1356 canonical_signed_info: &[u8],
1357 ) -> Result<Vec<u8>, SigningKeyError> {
1358 let digest_algorithm = match algorithm {
1359 SignatureAlgorithm::DsaSha1 => DigestAlgorithm::Sha1,
1360 SignatureAlgorithm::DsaSha256 => DigestAlgorithm::Sha256,
1361 _ => {
1362 return Err(SigningKeyError::UnsupportedAlgorithm {
1363 uri: algorithm.uri().to_owned(),
1364 });
1365 }
1366 };
1367 let component_len =
1368 usize::try_from(self.key.verifying_key().components().q().bits_vartime())
1369 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?
1370 .div_ceil(8);
1371 if algorithm.dsa_component_len() != Some(component_len) {
1372 return Err(SigningKeyError::InvalidPublicKeyInfo);
1373 }
1374 let digest =
1375 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
1376 let mut rng = crate::provider::ProviderRng(provider);
1377 let signature: dsa::Signature = self
1378 .key
1379 .sign_prehash_with_rng(&mut rng, &digest)
1380 .map_err(|_| SigningKeyError::SigningFailed)?;
1381 let mut output = Vec::with_capacity(component_len.saturating_mul(2));
1382 for component in [signature.r(), signature.s()] {
1383 let bytes = component.to_be_bytes_trimmed_vartime();
1384 if bytes.len() > component_len {
1385 return Err(SigningKeyError::SigningFailed);
1386 }
1387 output.resize(output.len() + component_len - bytes.len(), 0);
1388 output.extend_from_slice(bytes.as_ref());
1389 }
1390 Ok(output)
1391 }
1392
1393 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1394 let verifying_key = self.key.verifying_key();
1395 let spki_der = verifying_key
1396 .to_public_key_der()
1397 .map(|doc| doc.as_bytes().to_vec())
1398 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
1399 let modulus_bits = usize::try_from(verifying_key.components().p().bits_vartime())
1400 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?;
1401 let component_len = usize::try_from(verifying_key.components().q().bits_vartime())
1402 .map_err(|_| SigningKeyError::InvalidPublicKeyInfo)?
1403 .div_ceil(8);
1404 let components = verifying_key.components();
1405 Ok(SigningPublicKeyInfo::Dsa {
1406 spki_der,
1407 p: components.p().to_be_bytes_trimmed_vartime().to_vec(),
1408 q: components.q().to_be_bytes_trimmed_vartime().to_vec(),
1409 g: components.g().to_be_bytes_trimmed_vartime().to_vec(),
1410 y: verifying_key.y().to_be_bytes_trimmed_vartime().to_vec(),
1411 modulus_bits,
1412 component_len,
1413 })
1414 }
1415}
1416
1417fn sign_rsa_pkcs1v15_with_rng(
1418 provider: &dyn crate::provider::CryptoProvider,
1419 key: impl RandomizedSigner<RsaPkcs1v15Signature>,
1420 canonical_signed_info: &[u8],
1421) -> Result<Vec<u8>, SigningKeyError> {
1422 let mut rng = crate::provider::ProviderRng(provider);
1423 let signature = key
1424 .try_sign_with_rng(&mut rng, canonical_signed_info)
1425 .map_err(|_| SigningKeyError::SigningFailed)?;
1426 Ok(signature.to_vec())
1427}
1428
1429fn ecdsa_digest_algorithm(
1430 algorithm: SignatureAlgorithm,
1431) -> Result<DigestAlgorithm, SigningKeyError> {
1432 algorithm
1433 .ecdsa_digest()
1434 .ok_or_else(|| SigningKeyError::UnsupportedAlgorithm {
1435 uri: algorithm.uri().to_owned(),
1436 })
1437}
1438
1439fn sign_ecdsa_with_provider<S, K>(
1440 key: &K,
1441 provider: &dyn crate::provider::CryptoProvider,
1442 algorithm: SignatureAlgorithm,
1443 canonical_signed_info: &[u8],
1444) -> Result<Vec<u8>, SigningKeyError>
1445where
1446 K: PrehashSigner<S>,
1447 S: SignatureEncoding,
1448{
1449 let digest_algorithm = ecdsa_digest_algorithm(algorithm)?;
1450 let prehash =
1451 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
1452 let signature = key
1453 .sign_prehash(&prehash)
1454 .map_err(|_| SigningKeyError::SigningFailed)?;
1455 Ok(signature.to_vec())
1456}
1457
1458trait EcdsaPublicKeyEncoding {
1459 fn spki_der(&self) -> Result<Vec<u8>, SigningKeyError>;
1460 fn uncompressed_sec1(&self) -> Vec<u8>;
1461}
1462
1463macro_rules! impl_ecdsa_public_key_encoding {
1464 ($key:ty) => {
1465 impl EcdsaPublicKeyEncoding for $key {
1466 fn spki_der(&self) -> Result<Vec<u8>, SigningKeyError> {
1467 self.to_public_key_der()
1468 .map(|document| document.as_bytes().to_vec())
1469 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)
1470 }
1471
1472 fn uncompressed_sec1(&self) -> Vec<u8> {
1473 self.to_sec1_point(false).as_bytes().to_vec()
1474 }
1475 }
1476 };
1477}
1478
1479impl_ecdsa_public_key_encoding!(P256VerifyingKey);
1480impl_ecdsa_public_key_encoding!(P384VerifyingKey);
1481impl_ecdsa_public_key_encoding!(P521VerifyingKey);
1482
1483fn ecdsa_public_key_info(
1484 key: &impl EcdsaPublicKeyEncoding,
1485 curve_oid: &'static str,
1486) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1487 Ok(SigningPublicKeyInfo::Ec {
1488 spki_der: key.spki_der()?,
1489 curve_oid,
1490 public_key: key.uncompressed_sec1(),
1491 })
1492}
1493
1494pub struct EcdsaP256SigningKey {
1496 key: P256SigningKey,
1497}
1498
1499impl EcdsaP256SigningKey {
1500 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1502 let key = p256::SecretKey::from_sec1_pem(private_key_pem)
1503 .map(P256SigningKey::from)
1504 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1505 Ok(Self { key })
1506 }
1507
1508 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1510 let key = p256::SecretKey::from_sec1_der(private_key_der)
1511 .map(P256SigningKey::from)
1512 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1513 Ok(Self { key })
1514 }
1515
1516 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1518 let private_key_der = parse_private_key_pem(private_key_pem)?;
1519 Self::from_pkcs8_der(&private_key_der)
1520 }
1521
1522 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1524 let key = P256SigningKey::from_pkcs8_der(private_key_der)
1525 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1526 Ok(Self { key })
1527 }
1528
1529 pub fn from_pkcs8_encrypted_pem(
1531 private_key_pem: &str,
1532 password: impl AsRef<[u8]>,
1533 ) -> Result<Self, SigningKeyError> {
1534 let key = P256SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1535 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1536 Ok(Self { key })
1537 }
1538
1539 pub fn from_pkcs8_encrypted_der(
1541 private_key_der: &[u8],
1542 password: impl AsRef<[u8]>,
1543 ) -> Result<Self, SigningKeyError> {
1544 let key = P256SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1545 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1546 Ok(Self { key })
1547 }
1548}
1549
1550impl SigningKey for EcdsaP256SigningKey {
1551 fn sign(
1552 &self,
1553 algorithm: SignatureAlgorithm,
1554 canonical_signed_info: &[u8],
1555 ) -> Result<Vec<u8>, SigningKeyError> {
1556 self.sign_with_provider(
1557 crate::provider::default_provider(),
1558 algorithm,
1559 canonical_signed_info,
1560 )
1561 }
1562
1563 fn sign_with_provider(
1564 &self,
1565 provider: &dyn crate::provider::CryptoProvider,
1566 algorithm: SignatureAlgorithm,
1567 canonical_signed_info: &[u8],
1568 ) -> Result<Vec<u8>, SigningKeyError> {
1569 sign_ecdsa_with_provider::<P256Signature, _>(
1570 &self.key,
1571 provider,
1572 algorithm,
1573 canonical_signed_info,
1574 )
1575 }
1576
1577 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1578 ecdsa_public_key_info(self.key.verifying_key(), EC_P256_OID)
1579 }
1580}
1581
1582pub struct EcdsaP384SigningKey {
1584 key: P384SigningKey,
1585}
1586
1587impl EcdsaP384SigningKey {
1588 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1590 let key = p384::SecretKey::from_sec1_pem(private_key_pem)
1591 .map(P384SigningKey::from)
1592 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1593 Ok(Self { key })
1594 }
1595
1596 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1598 let key = p384::SecretKey::from_sec1_der(private_key_der)
1599 .map(P384SigningKey::from)
1600 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1601 Ok(Self { key })
1602 }
1603
1604 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1606 let private_key_der = parse_private_key_pem(private_key_pem)?;
1607 Self::from_pkcs8_der(&private_key_der)
1608 }
1609
1610 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1612 let key = P384SigningKey::from_pkcs8_der(private_key_der)
1613 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1614 Ok(Self { key })
1615 }
1616
1617 pub fn from_pkcs8_encrypted_pem(
1619 private_key_pem: &str,
1620 password: impl AsRef<[u8]>,
1621 ) -> Result<Self, SigningKeyError> {
1622 let key = P384SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1623 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1624 Ok(Self { key })
1625 }
1626
1627 pub fn from_pkcs8_encrypted_der(
1629 private_key_der: &[u8],
1630 password: impl AsRef<[u8]>,
1631 ) -> Result<Self, SigningKeyError> {
1632 let key = P384SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1633 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1634 Ok(Self { key })
1635 }
1636}
1637
1638impl SigningKey for EcdsaP384SigningKey {
1639 fn sign(
1640 &self,
1641 algorithm: SignatureAlgorithm,
1642 canonical_signed_info: &[u8],
1643 ) -> Result<Vec<u8>, SigningKeyError> {
1644 self.sign_with_provider(
1645 crate::provider::default_provider(),
1646 algorithm,
1647 canonical_signed_info,
1648 )
1649 }
1650
1651 fn sign_with_provider(
1652 &self,
1653 provider: &dyn crate::provider::CryptoProvider,
1654 algorithm: SignatureAlgorithm,
1655 canonical_signed_info: &[u8],
1656 ) -> Result<Vec<u8>, SigningKeyError> {
1657 sign_ecdsa_with_provider::<P384Signature, _>(
1658 &self.key,
1659 provider,
1660 algorithm,
1661 canonical_signed_info,
1662 )
1663 }
1664
1665 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1666 ecdsa_public_key_info(self.key.verifying_key(), EC_P384_OID)
1667 }
1668}
1669
1670pub struct EcdsaP521SigningKey {
1672 key: P521SigningKey,
1673}
1674
1675impl EcdsaP521SigningKey {
1676 pub fn from_sec1_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1678 let key = p521::SecretKey::from_sec1_pem(private_key_pem)
1679 .map(P521SigningKey::from)
1680 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1681 Ok(Self { key })
1682 }
1683
1684 pub fn from_sec1_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1686 let key = p521::SecretKey::from_sec1_der(private_key_der)
1687 .map(P521SigningKey::from)
1688 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1689 Ok(Self { key })
1690 }
1691
1692 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
1694 let private_key_der = parse_private_key_pem(private_key_pem)?;
1695 Self::from_pkcs8_der(&private_key_der)
1696 }
1697
1698 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
1700 let key = P521SigningKey::from_pkcs8_der(private_key_der)
1701 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1702 Ok(Self { key })
1703 }
1704
1705 pub fn from_pkcs8_encrypted_pem(
1707 private_key_pem: &str,
1708 password: impl AsRef<[u8]>,
1709 ) -> Result<Self, SigningKeyError> {
1710 let key = P521SigningKey::from_pkcs8_encrypted_pem(private_key_pem, password)
1711 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1712 Ok(Self { key })
1713 }
1714
1715 pub fn from_pkcs8_encrypted_der(
1717 private_key_der: &[u8],
1718 password: impl AsRef<[u8]>,
1719 ) -> Result<Self, SigningKeyError> {
1720 let key = P521SigningKey::from_pkcs8_encrypted_der(private_key_der, password)
1721 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
1722 Ok(Self { key })
1723 }
1724}
1725
1726impl SigningKey for EcdsaP521SigningKey {
1727 fn sign(
1728 &self,
1729 algorithm: SignatureAlgorithm,
1730 canonical_signed_info: &[u8],
1731 ) -> Result<Vec<u8>, SigningKeyError> {
1732 self.sign_with_provider(
1733 crate::provider::default_provider(),
1734 algorithm,
1735 canonical_signed_info,
1736 )
1737 }
1738
1739 fn sign_with_provider(
1740 &self,
1741 provider: &dyn crate::provider::CryptoProvider,
1742 algorithm: SignatureAlgorithm,
1743 canonical_signed_info: &[u8],
1744 ) -> Result<Vec<u8>, SigningKeyError> {
1745 sign_ecdsa_with_provider::<P521Signature, _>(
1746 &self.key,
1747 provider,
1748 algorithm,
1749 canonical_signed_info,
1750 )
1751 }
1752
1753 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
1754 ecdsa_public_key_info(self.key.verifying_key(), EC_P521_OID)
1755 }
1756}
1757
1758#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1760pub enum SignatureTemplateSelection {
1761 FirstDescendant,
1763 #[default]
1765 LastDescendant,
1766}
1767
1768impl SignatureTemplateSelection {
1769 const fn target(self) -> SigningSignatureTarget {
1770 match self {
1771 Self::FirstDescendant => SigningSignatureTarget::First,
1772 Self::LastDescendant => SigningSignatureTarget::Last,
1773 }
1774 }
1775}
1776
1777pub struct SignContext<'a> {
1779 signing_key: &'a dyn SigningKey,
1780 key_info_writer: Option<&'a dyn KeyInfoWriter>,
1781 start_node_id: Option<&'a str>,
1782 id_attributes: &'a [crate::IdAttributeRegistration],
1783 external_resources: Option<&'a HashMap<String, Vec<u8>>>,
1784 template_selection: SignatureTemplateSelection,
1785 policy: crate::policy::SigningPolicy,
1786 provider: &'a dyn crate::provider::CryptoProvider,
1787 xml_backend: crate::XmlBackend,
1788}
1789
1790impl<'a> SignContext<'a> {
1791 pub fn new(signing_key: &'a dyn SigningKey) -> Self {
1793 Self {
1794 signing_key,
1795 key_info_writer: None,
1796 start_node_id: None,
1797 id_attributes: &[],
1798 external_resources: None,
1799 template_selection: SignatureTemplateSelection::default(),
1800 policy: crate::policy::SigningPolicy::default(),
1801 provider: crate::provider::default_provider(),
1802 xml_backend: crate::XmlBackend::default(),
1803 }
1804 }
1805
1806 #[must_use]
1808 pub fn policy(mut self, policy: crate::policy::SigningPolicy) -> Self {
1809 self.policy = policy;
1810 self
1811 }
1812
1813 #[must_use]
1815 pub fn provider(mut self, provider: &'a dyn crate::provider::CryptoProvider) -> Self {
1816 self.provider = provider;
1817 self
1818 }
1819
1820 #[must_use]
1822 pub fn xml_backend(mut self, backend: crate::XmlBackend) -> Self {
1823 self.xml_backend = backend;
1824 self
1825 }
1826
1827 fn document_parse_settings(&self) -> DocumentParseSettings {
1828 DocumentParseSettings::from_policy(&self.policy.xml, &self.policy.resources)
1829 .with_backend(self.xml_backend)
1830 }
1831
1832 #[must_use]
1834 pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
1835 self.key_info_writer = Some(writer);
1836 self
1837 }
1838
1839 #[must_use]
1844 pub fn start_node_id(mut self, id: &'a str) -> Self {
1845 self.start_node_id = Some(id);
1846 self
1847 }
1848
1849 #[must_use]
1855 pub fn signature_template_selection(mut self, selection: SignatureTemplateSelection) -> Self {
1856 self.template_selection = selection;
1857 self
1858 }
1859
1860 #[must_use]
1862 pub fn id_attributes(mut self, registrations: &'a [crate::IdAttributeRegistration]) -> Self {
1863 self.id_attributes = registrations;
1864 self
1865 }
1866
1867 #[must_use]
1874 pub fn external_resources(mut self, resources: &'a HashMap<String, Vec<u8>>) -> Self {
1875 self.external_resources = Some(resources);
1876 self
1877 }
1878
1879 #[must_use]
1885 pub fn xpath_here_semantics(mut self, semantics: XPathHereSemantics) -> Self {
1886 self.policy.transforms.xpath_here_semantics = semantics;
1887 self
1888 }
1889
1890 pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
1899 self.policy.validate()?;
1900 self.policy.resources.validate_xml_document_len(xml.len())?;
1901 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1902 &self.policy.resources,
1903 self.xml_backend,
1904 );
1905 let mut document = XmlDocument::parse_with_settings_and_budget(
1906 xml.to_owned(),
1907 self.document_parse_settings(),
1908 budgets.transforms.xml_parse_work(),
1909 )
1910 .map_err(|error| match owned_document_policy_violation(error) {
1911 Ok(error) => SigningError::Policy(error),
1912 Err(XmlDocumentError::Parse(error)) => {
1913 SigningError::Digest(SigningDigestError::XmlParse(error))
1914 }
1915 Err(error) => SigningError::Document(error),
1916 })?;
1917 self.validate_owned_document_input(&document)?;
1918 self.sign_document_in_place(&mut document, &mut budgets)?;
1919 Ok(document.into_xml())
1920 }
1921
1922 pub fn sign_document(&self, document: &mut XmlDocument) -> Result<(), SigningError> {
1927 self.validate_owned_document_input(document)?;
1928 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
1929 &self.policy.resources,
1930 self.xml_backend,
1931 );
1932 let mut staged = document
1933 .staged_copy_with_budget(
1934 self.document_parse_settings(),
1935 budgets.transforms.xml_parse_work(),
1936 )
1937 .map_err(map_owned_document_mutation_error)?;
1938 self.sign_document_in_place(&mut staged, &mut budgets)?;
1939 commit_signed_staged(document, staged, &self.policy)
1940 }
1941
1942 fn sign_document_in_place(
1943 &self,
1944 document: &mut XmlDocument,
1945 budgets: &mut SigningOperationBudgets,
1946 ) -> Result<(), SigningError> {
1947 let target_signature = document.with_view(|view| {
1948 signing_signature_index(
1949 view.document(),
1950 self.start_node_id,
1951 self.id_attributes,
1952 self.template_selection,
1953 )
1954 })?;
1955 self.policy.resources.validate_key_candidates(1)?;
1956 self.sign_template_at_index_with_budgets(document, target_signature, budgets)?;
1957 Ok(())
1958 }
1959
1960 fn validate_owned_document_input(&self, document: &XmlDocument) -> Result<(), SigningError> {
1961 self.policy.validate()?;
1962 document.validate_operation_policy(&self.policy.xml, &self.policy.resources)?;
1963 if let Some(resources) = self.external_resources {
1964 validate_external_resource_map(
1965 resources,
1966 self.policy.resources.max_external_resource_bytes,
1967 self.policy.resources.max_external_resource_total_bytes,
1968 )
1969 .map_err(map_signing_external_resource_map_error)?;
1970 }
1971 Ok(())
1972 }
1973
1974 fn sign_template_at_index_with_budgets(
1975 &self,
1976 document: &mut XmlDocument,
1977 target_signature: usize,
1978 budgets: &mut SigningOperationBudgets,
1979 ) -> Result<(), SigningError> {
1980 document.with_view(|view| {
1981 let signature = find_signing_signature_node(
1982 view.document(),
1983 SigningSignatureTarget::Index(target_signature),
1984 )?;
1985 parse_signature_children(signature)
1986 .map_err(|error| SigningDigestError::InvalidStructure(error.to_string()))?;
1987 let (algorithm, output_bits) =
1988 validate_signing_signed_info_methods(signature, &self.policy)?;
1989 self.policy.check_signature_algorithm(algorithm)?;
1990 if let Some(full_bits) = algorithm.hmac_output_bits() {
1991 self.policy
1998 .hmac
1999 .validate_output(algorithm, output_bits.unwrap_or(full_bits))?;
2000 }
2001 Ok::<_, SigningError>(())
2002 })?;
2003 let transform_options = TransformOptions::default()
2004 .allow_internal_dtd(self.policy.xml.allow_internal_dtd)
2005 .xpath_here_semantics(self.policy.transforms.xpath_here_semantics);
2006 let external_resources = ExternalResourceContext::new(
2007 self.external_resources,
2008 self.policy.resources.max_external_resource_bytes,
2009 self.policy.resources.max_external_resource_total_bytes,
2010 );
2011 let binding = (document.identity(), document.generation());
2012 let mut operation =
2013 OperationExecutionContext::new(self.policy.clone(), &mut *budgets, Some(binding));
2014 let key_info = operation.add_node(
2015 OperationNodeKind::Key { index: 0 },
2016 OperationStage::Resolve,
2017 None,
2018 );
2019 operation.compile()?;
2020 let key_info_content = operation.run(key_info, || {
2021 let Some(writer) = self.key_info_writer else {
2022 return Ok::<_, SigningError>(None);
2023 };
2024 let key_info_content =
2025 writer.write_key_info_with_provider(self.signing_key, self.provider)?;
2026 self.policy
2029 .resources
2030 .validate_xml_document_len(key_info_content.len())?;
2031 Ok(Some(key_info_content))
2032 })?;
2033 operation.extend();
2034 let c14n_candidate = materialize_second_edition_c14n11_candidate(
2038 document,
2039 target_signature,
2040 &self.policy,
2041 external_resources.is_configured(),
2042 )?;
2043 let normalization = if let Some(candidate) = c14n_candidate {
2044 let mutation =
2045 operation.add_node(OperationNodeKind::Mutation, OperationStage::Resolve, None);
2046 operation.add_dependency(mutation, key_info)?;
2047 operation.compile()?;
2048 operation.run_document_transition(mutation, document, |document, budgets| {
2049 document
2050 .replace_serialized_with_settings(
2051 candidate,
2052 self.document_parse_settings(),
2053 Some(budgets.transforms.xml_parse_work()),
2054 )
2055 .map_err(map_owned_document_mutation_error)
2056 })?;
2057 operation.extend();
2058 Some(mutation)
2059 } else {
2060 None
2061 };
2062 let setup_gate = normalization.unwrap_or(key_info);
2063 let setup_gate = if let Some(key_info_content) = key_info_content {
2064 let mutation =
2065 operation.add_node(OperationNodeKind::Mutation, OperationStage::Resolve, None);
2066 operation.add_dependency(mutation, setup_gate)?;
2067 operation.compile()?;
2068 operation.run_document_transition(mutation, document, |document, budgets| {
2069 let populated = merge_key_info_source_at_index_with_budget(
2072 document.as_xml(),
2073 &key_info_content,
2074 target_signature,
2075 Some(&self.policy),
2076 Some(budgets.transforms.xml_parse_work()),
2077 )?;
2078 self.policy
2079 .resources
2080 .validate_xml_document_len(populated.len())?;
2081 document
2082 .replace_serialized_with_settings(
2083 populated,
2084 self.document_parse_settings(),
2085 Some(budgets.transforms.xml_parse_work()),
2086 )
2087 .map_err(map_owned_document_mutation_error)
2088 })?;
2089 operation.extend();
2090 mutation
2091 } else {
2092 setup_gate
2093 };
2094 let plan_nodes = fill_reference_digest_values_in_dependency_order_with_operation(
2095 document,
2096 transform_options,
2097 self.provider,
2098 &mut operation,
2099 SigningReferenceRequest {
2100 target_signature,
2101 id_attributes: self.id_attributes,
2102 external_resources: &external_resources,
2103 },
2104 Some(setup_gate),
2105 )?;
2106 self.policy
2107 .resources
2108 .validate_xml_document_len(document.as_xml().len())?;
2109 let (algorithm, hmac_output_length_bits, canonical_signed_info, signature_context) =
2110 operation.run_with_budgets(plan_nodes.canonicalization, |budgets| {
2111 let result =
2112 canonicalize_signed_info(document, &self.policy, budgets, target_signature)?;
2113 budgets
2114 .transforms
2115 .charge_c14n_output(result.2.len())
2116 .map_err(SigningDigestError::Transform)?;
2117 Ok::<_, SigningError>(result)
2118 })?;
2119 self.policy.check_signature_algorithm(algorithm)?;
2120 let expected_signature_len = expected_signature_output_len(
2121 self.signing_key,
2122 algorithm,
2123 &self.policy,
2124 hmac_output_length_bits,
2125 )?;
2126 let projected_signature_len = projected_signature_output_len(
2127 algorithm,
2128 expected_signature_len,
2129 self.policy.ecdsa_signature_value_encoding,
2130 )?;
2131 let encoded_signature_len =
2132 padded_base64_len_for_xml(projected_signature_len, &self.policy)?;
2133 let signature_value_node = document.with_view(|view| {
2134 let signature = find_signing_signature_node(
2135 view.document(),
2136 SigningSignatureTarget::Index(target_signature),
2137 )?;
2138 let signature_value = find_required_child(signature, "SignatureValue")?;
2139 Ok::<_, SigningError>(view.node_identity(signature_value))
2140 })?;
2141 let projected_document_len = document
2142 .projected_content_replacement_len(signature_value_node, encoded_signature_len)?;
2143 self.policy
2144 .resources
2145 .validate_xml_document_len(projected_document_len)?;
2146 let signature_value = operation.run(plan_nodes.crypto, || {
2147 self.provider
2148 .require_capability(crate::provider::ProviderCapability::Sign(algorithm))
2149 .map_err(SigningKeyError::from)?;
2150 let mut signature_value = self.provider.sign_with_context(
2151 self.signing_key,
2152 algorithm,
2153 &signature_context,
2154 &canonical_signed_info,
2155 )?;
2156 if algorithm.hmac_output_bits().is_some() {
2157 signature_value.truncate(expected_signature_len);
2158 }
2159 validate_signature_output(expected_signature_len, &signature_value)?;
2160 Ok::<_, SigningError>(signature_value)
2161 })?;
2162 let signature_b64 = operation.run(plan_nodes.evidence, || {
2163 let signature_value = encode_signature_output(
2164 algorithm,
2165 signature_value,
2166 self.policy.ecdsa_signature_value_encoding,
2167 )?;
2168 Ok::<_, SigningError>(base64::engine::general_purpose::STANDARD.encode(signature_value))
2169 })?;
2170 operation.run_document_transition(plan_nodes.mutation, document, |document, budgets| {
2171 document
2172 .replace_base64_contents_with_budget(
2173 &[(signature_value_node, signature_b64)],
2174 self.document_parse_settings(),
2175 budgets.transforms.xml_parse_work(),
2176 )
2177 .map_err(map_owned_document_mutation_error)
2178 })?;
2179 self.policy
2180 .resources
2181 .validate_xml_document_len(document.as_xml().len())?;
2182 Ok(())
2183 }
2184
2185 pub fn sign_with_builder(
2188 &self,
2189 xml: &str,
2190 builder: &SignatureBuilder,
2191 ) -> Result<String, SigningError> {
2192 self.policy.validate()?;
2193 self.policy.resources.validate_xml_document_len(xml.len())?;
2194 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
2195 &self.policy.resources,
2196 self.xml_backend,
2197 );
2198 let mut document = XmlDocument::parse_with_settings_and_budget(
2199 xml.to_owned(),
2200 self.document_parse_settings(),
2201 budgets.transforms.xml_parse_work(),
2202 )
2203 .map_err(|error| match owned_document_policy_violation(error) {
2204 Ok(error) => SigningError::Policy(error),
2205 Err(XmlDocumentError::Parse(error)) => {
2206 SigningError::XmlMutation(XmlMutationError::XmlParse(error))
2207 }
2208 Err(error) => SigningError::Document(error),
2209 })?;
2210 self.validate_owned_document_input(&document)?;
2211 self.sign_document_with_builder_in_place(&mut document, builder, &mut budgets)?;
2212 Ok(document.into_xml())
2213 }
2214
2215 pub fn sign_document_with_builder(
2217 &self,
2218 document: &mut XmlDocument,
2219 builder: &SignatureBuilder,
2220 ) -> Result<(), SigningError> {
2221 self.validate_owned_document_input(document)?;
2222 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
2223 &self.policy.resources,
2224 self.xml_backend,
2225 );
2226 let mut staged = document
2227 .staged_copy_with_budget(
2228 self.document_parse_settings(),
2229 budgets.transforms.xml_parse_work(),
2230 )
2231 .map_err(map_owned_document_mutation_error)?;
2232 self.sign_document_with_builder_in_place(&mut staged, builder, &mut budgets)?;
2233 commit_signed_staged(document, staged, &self.policy)
2234 }
2235
2236 fn sign_document_with_builder_in_place(
2237 &self,
2238 document: &mut XmlDocument,
2239 builder: &SignatureBuilder,
2240 budgets: &mut SigningOperationBudgets,
2241 ) -> Result<(), SigningError> {
2242 self.policy.resources.validate_key_candidates(1)?;
2243 let expected_signature_len = expected_signature_output_len(
2244 self.signing_key,
2245 builder.signature_method(),
2246 &self.policy,
2247 None,
2248 )?;
2249 let template = builder.build_template_with_policy_for_signature_output(
2250 &self.policy,
2251 expected_signature_len,
2252 &budgets.transforms,
2253 &mut budgets.xpath_parse,
2254 )?;
2255 let signature_parent = if let Some(id) = self.start_node_id {
2256 document.with_view(|view| {
2257 let start = signing_start_node(view.document(), id, self.id_attributes)?;
2258 Ok::<_, SigningError>(view.node_identity(start))
2259 })?
2260 } else {
2261 document.with_view(|view| view.root_element())
2262 };
2263 let projected_document_len =
2264 document.projected_child_append_len(signature_parent, template.len())?;
2265 self.policy
2266 .resources
2267 .validate_xml_document_len(projected_document_len)?;
2268 let binding = (document.identity(), document.generation());
2269 let mut operation =
2270 OperationExecutionContext::new(self.policy.clone(), &mut *budgets, Some(binding));
2271 let mutation =
2272 operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
2273 operation.compile()?;
2274 operation.run_document_transition(mutation, document, |document, budgets| {
2275 document
2276 .append_generated_child_with_budget(
2277 signature_parent,
2278 &template,
2279 self.document_parse_settings(),
2280 budgets.transforms.xml_parse_work(),
2281 )
2282 .map_err(map_owned_document_mutation_error)
2283 })?;
2284 drop(operation);
2285 self.policy
2286 .resources
2287 .validate_xml_document_len(document.as_xml().len())?;
2288 let target_signature = document.with_view(|view| {
2289 let parent = if let Some(id) = self.start_node_id {
2290 signing_start_node(view.document(), id, self.id_attributes)?
2291 } else {
2292 view.document().root_element()
2293 };
2294 let appended = parent
2295 .children()
2296 .rfind(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
2297 .ok_or(SigningDigestError::MissingElement {
2298 element: "Signature",
2299 })?;
2300 signature_index(view.document(), appended).map_err(SigningError::from)
2301 })?;
2302 self.sign_template_at_index_with_budgets(document, target_signature, budgets)?;
2303 Ok(())
2304 }
2305}
2306
2307fn projected_signature_output_len(
2308 algorithm: SignatureAlgorithm,
2309 raw_signature_len: usize,
2310 encoding: crate::policy::EcdsaSignatureValueEncoding,
2311) -> Result<usize, SigningError> {
2312 if algorithm.ecdsa_digest().is_some()
2313 && encoding == crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
2314 {
2315 return maximum_ecdsa_der_signature_len(raw_signature_len)
2316 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
2317 }
2318 Ok(raw_signature_len)
2319}
2320
2321fn map_owned_document_mutation_error(error: XmlDocumentError) -> SigningError {
2322 match owned_document_policy_violation(error) {
2323 Ok(error) => SigningError::Policy(error),
2324 Err(error) => SigningError::Document(error),
2325 }
2326}
2327
2328fn map_owned_document_digest_mutation_error(error: XmlDocumentError) -> SigningDigestError {
2329 match owned_document_policy_violation(error) {
2330 Ok(error) => SigningDigestError::Policy(error),
2331 Err(error) => SigningDigestError::Document(error),
2332 }
2333}
2334
2335fn map_signing_external_resource_map_error(error: ExternalResourceMapError) -> SigningError {
2336 match error {
2337 ExternalResourceMapError::Policy(error) => SigningError::Policy(error),
2338 ExternalResourceMapError::TotalLengthOverflow => {
2339 SigningDigestError::InvalidStructure("external resource total length overflow".into())
2340 .into()
2341 }
2342 }
2343}
2344
2345fn owned_document_policy_violation(
2346 error: XmlDocumentError,
2347) -> Result<crate::policy::PolicyViolation, XmlDocumentError> {
2348 match error {
2349 XmlDocumentError::Policy(error) => Ok(error),
2350 XmlDocumentError::Parse(crate::xml::dom::ParseError::NamespaceBindingLimitReached {
2351 maximum,
2352 actual,
2353 }) => Ok(crate::policy::PolicyViolation::ResourceLimit {
2354 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
2355 maximum,
2356 actual,
2357 }),
2358 XmlDocumentError::DocumentTooLarge { maximum, actual } => {
2359 Ok(crate::policy::PolicyViolation::ResourceLimit {
2360 resource: crate::policy::resource_name::XML_DOCUMENT,
2361 maximum,
2362 actual,
2363 })
2364 }
2365 XmlDocumentError::DocumentTooDeep { maximum, actual } => {
2366 Ok(crate::policy::PolicyViolation::ResourceLimit {
2367 resource: crate::policy::resource_name::XML_DEPTH,
2368 maximum,
2369 actual,
2370 })
2371 }
2372 XmlDocumentError::ProjectedNodeLimit { maximum } => {
2373 Ok(crate::policy::PolicyViolation::ResourceLimit {
2374 resource: crate::policy::resource_name::XML_NODES,
2375 maximum,
2376 actual: maximum.saturating_add(1),
2377 })
2378 }
2379 error => Err(error),
2380 }
2381}
2382
2383fn encode_signature_output(
2384 algorithm: SignatureAlgorithm,
2385 signature: Vec<u8>,
2386 encoding: crate::policy::EcdsaSignatureValueEncoding,
2387) -> Result<Vec<u8>, SigningError> {
2388 if algorithm.ecdsa_digest().is_some()
2389 && encoding == crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
2390 {
2391 return encode_ecdsa_signature_as_der(&signature)
2392 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
2393 }
2394 Ok(signature)
2395}
2396
2397#[derive(Debug, Clone)]
2398struct SigningReference {
2399 uri: String,
2400 origin_node_id: NodeId,
2401 transforms: Vec<Transform>,
2402 digest_method: DigestAlgorithm,
2403 digest_value_range: Range<usize>,
2404 digest_value_node_id: NodeId,
2405}
2406
2407struct SigningOperationBudgets {
2408 transforms: TransformExecutionBudget,
2409 xpath_parse: XPathSignatureParseBudget,
2410}
2411
2412struct SigningReferenceRequest<'a, 'resources> {
2413 target_signature: usize,
2414 id_attributes: &'a [crate::IdAttributeRegistration],
2415 external_resources: &'a ExternalResourceContext<'resources>,
2416}
2417
2418struct SigningPlanNodes {
2419 canonicalization: OperationNodeId,
2420 crypto: OperationNodeId,
2421 evidence: OperationNodeId,
2422 mutation: OperationNodeId,
2423}
2424
2425impl SigningOperationBudgets {
2426 fn from_resources(resources: &crate::policy::ResourcePolicy) -> Self {
2427 Self {
2428 transforms: TransformExecutionBudget::from_resources(resources),
2429 xpath_parse: XPathSignatureParseBudget::from_resources(resources),
2430 }
2431 }
2432
2433 fn from_resources_with_backend(
2434 resources: &crate::policy::ResourcePolicy,
2435 backend: crate::XmlBackend,
2436 ) -> Self {
2437 Self {
2438 transforms: TransformExecutionBudget::from_resources(resources)
2439 .with_xml_backend(backend),
2440 xpath_parse: XPathSignatureParseBudget::from_resources(resources),
2441 }
2442 }
2443}
2444
2445impl Default for SigningOperationBudgets {
2446 fn default() -> Self {
2447 Self::from_resources(&crate::policy::ResourcePolicy::default())
2448 }
2449}
2450
2451pub fn compute_reference_digest_values(
2458 xml: &str,
2459) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2460 let execution_budget = TransformExecutionBudget::default();
2461 compute_reference_digest_values_with_options(
2462 xml,
2463 TransformOptions::default(),
2464 None,
2465 crate::provider::default_provider(),
2466 &execution_budget,
2467 None,
2468 &[],
2469 )
2470}
2471
2472fn compute_reference_digest_values_with_options(
2473 xml: &str,
2474 transform_options: TransformOptions,
2475 policy: Option<&crate::policy::SigningPolicy>,
2476 provider: &dyn crate::provider::CryptoProvider,
2477 execution_budget: &TransformExecutionBudget,
2478 target_signature: Option<usize>,
2479 id_attributes: &[crate::IdAttributeRegistration],
2480) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2481 let (prepared, target_signature) =
2482 prepare_reference_digest_input(xml, policy, execution_budget, target_signature)?;
2483 compute_prepared_reference_digest_values_with_options(
2484 prepared.as_ref(),
2485 transform_options,
2486 policy,
2487 provider,
2488 execution_budget,
2489 target_signature,
2490 id_attributes,
2491 )
2492}
2493
2494fn compute_prepared_reference_digest_values_with_options(
2495 xml: &str,
2496 transform_options: TransformOptions,
2497 policy: Option<&crate::policy::SigningPolicy>,
2498 provider: &dyn crate::provider::CryptoProvider,
2499 execution_budget: &TransformExecutionBudget,
2500 target_signature: usize,
2501 id_attributes: &[crate::IdAttributeRegistration],
2502) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2503 let resource_policy = policy
2504 .map(|policy| &policy.resources)
2505 .cloned()
2506 .unwrap_or_default();
2507 let external_resources = ExternalResourceContext::new(
2508 None,
2509 resource_policy.max_external_resource_bytes,
2510 resource_policy.max_external_resource_total_bytes,
2511 );
2512 let doc = parse_signing_document(
2513 xml,
2514 policy,
2515 execution_budget.xml_parse_work(),
2516 crate::XmlBackend::default(),
2517 )?;
2518 let signature =
2519 find_signing_signature_node(&doc, SigningSignatureTarget::Index(target_signature))?;
2520 let signed_info = find_required_child(signature, "SignedInfo")?;
2521 let references = parse_signing_references(signed_info)?;
2522 validate_signing_references(&references, references.len(), policy, false)?;
2523 compute_signing_reference_digests(
2524 &doc,
2525 signature,
2526 references,
2527 transform_options,
2528 provider,
2529 execution_budget,
2530 SigningUriResolution {
2531 id_attributes,
2532 same_document_id_semantics: policy.map_or(
2533 crate::policy::SameDocumentIdSemantics::Specification,
2534 |policy| policy.transforms.same_document_id_semantics,
2535 ),
2536 external_resources: &external_resources,
2537 },
2538 )
2539}
2540
2541fn prepare_reference_digest_input<'a>(
2542 xml: &'a str,
2543 policy: Option<&crate::policy::SigningPolicy>,
2544 execution_budget: &TransformExecutionBudget,
2545 target_signature: Option<usize>,
2546) -> Result<(Cow<'a, str>, usize), SigningDigestError> {
2547 let default_policy = crate::policy::SigningPolicy::default();
2548 let effective_policy = policy.unwrap_or(&default_policy);
2549 let settings =
2550 DocumentParseSettings::from_policy(&effective_policy.xml, &effective_policy.resources);
2551 let document = XmlDocument::parse_with_settings_and_budget(
2552 xml.to_owned(),
2553 settings,
2554 execution_budget.xml_parse_work(),
2555 )
2556 .map_err(map_owned_document_digest_mutation_error)?;
2557 let target_signature = if let Some(target_signature) = target_signature {
2558 target_signature
2559 } else {
2560 document.with_view(|view| {
2561 let selected =
2562 find_signing_signature_node(view.document(), SigningSignatureTarget::Last)?;
2563 signature_index(view.document(), selected)
2564 })?
2565 };
2566 document.with_view(|view| {
2569 let signature = find_signing_signature_node(
2570 view.document(),
2571 SigningSignatureTarget::Index(target_signature),
2572 )?;
2573 let signed_info = find_required_child(signature, "SignedInfo")?;
2574 let references = parse_signing_references(signed_info)?;
2575 validate_signing_references(&references, references.len(), policy, false)
2576 })?;
2577 let materialized = materialize_second_edition_c14n11_candidate(
2578 &document,
2579 target_signature,
2580 effective_policy,
2581 false,
2582 )?;
2583 Ok((
2584 materialized.map_or(Cow::Borrowed(xml), Cow::Owned),
2585 target_signature,
2586 ))
2587}
2588
2589fn fill_reference_digest_values_in_dependency_order_with_operation(
2590 document: &mut XmlDocument,
2591 transform_options: TransformOptions,
2592 provider: &dyn crate::provider::CryptoProvider,
2593 operation: &mut OperationExecutionContext<
2594 crate::policy::SigningPolicy,
2595 &mut SigningOperationBudgets,
2596 >,
2597 request: SigningReferenceRequest<'_, '_>,
2598 setup_gate: Option<OperationNodeId>,
2599) -> Result<SigningPlanNodes, SigningDigestError> {
2600 let policy = operation.policy().clone();
2601 let reference_limit = policy
2602 .resources
2603 .max_references
2604 .min(MAX_REFERENCES_PER_SIGNATURE);
2605 let process_manifests =
2606 policy.manifest_processing == crate::policy::ManifestProcessing::Process;
2607 let (signed_info_references, manifest_references) = {
2608 let budgets = operation.budgets_mut();
2609 document.with_view(|view| {
2610 let signature = find_signing_signature_node(
2611 view.document(),
2612 SigningSignatureTarget::Index(request.target_signature),
2613 )?;
2614 let signed_info = find_required_child(signature, "SignedInfo")?;
2615 let signed_info_references =
2616 parse_signing_references_with_budget(signed_info, &mut budgets.xpath_parse)?;
2617 validate_signing_references(
2618 &signed_info_references,
2619 signed_info_references.len(),
2620 Some(&policy),
2621 request.external_resources.is_configured(),
2622 )?;
2623 let manifest_references = if process_manifests {
2624 parse_signing_manifest_references(
2625 signature,
2626 &mut budgets.xpath_parse,
2627 reference_limit.saturating_sub(signed_info_references.len()),
2628 reference_limit,
2629 )?
2630 } else {
2631 Vec::new()
2632 };
2633 Ok::<_, SigningDigestError>((signed_info_references, manifest_references))
2634 })?
2635 };
2636 let total_references = signed_info_references
2637 .len()
2638 .checked_add(manifest_references.len())
2639 .ok_or_else(|| SigningDigestError::InvalidStructure("reference count overflow".into()))?;
2640 validate_signing_references(
2641 &manifest_references,
2642 total_references,
2643 Some(&policy),
2644 request.external_resources.is_configured(),
2645 )?;
2646 let placeholder = "AA==";
2647 let analysis_replacements = document.with_view(|view| {
2651 let signature = find_signing_signature_node(
2652 view.document(),
2653 SigningSignatureTarget::Index(request.target_signature),
2654 )?;
2655 let signature_value = find_required_child(signature, "SignatureValue")?;
2656 let mut replacements = signed_info_references
2657 .iter()
2658 .chain(&manifest_references)
2659 .map(|reference| {
2660 (
2661 view.node_identity_by_id(reference.digest_value_node_id),
2662 placeholder.to_owned(),
2663 )
2664 })
2665 .collect::<Vec<_>>();
2666 replacements.push((view.node_identity(signature_value), placeholder.to_owned()));
2667 Ok::<_, SigningDigestError>(replacements)
2668 })?;
2669 let analysis_xml = document
2670 .project_base64_contents(
2671 &analysis_replacements,
2672 policy.resources.max_xml_document_bytes,
2673 )
2674 .map_err(map_owned_document_digest_mutation_error)?;
2675 let analysis_doc = parse_signing_document(
2676 &analysis_xml,
2677 Some(&policy),
2678 operation.budgets().transforms.xml_parse_work(),
2679 document.xml_backend(),
2680 )?;
2681 let analysis_signature = find_signing_signature_node(
2682 &analysis_doc,
2683 SigningSignatureTarget::Index(request.target_signature),
2684 )?;
2685 let analysis_signed_info = find_required_child(analysis_signature, "SignedInfo")?;
2686 let mut analysis_references = parse_signing_references_with_budget(
2687 analysis_signed_info,
2688 &mut operation.budgets_mut().xpath_parse,
2689 )?;
2690 if process_manifests {
2691 analysis_references.extend(parse_signing_manifest_references(
2692 analysis_signature,
2693 &mut operation.budgets_mut().xpath_parse,
2694 reference_limit.saturating_sub(signed_info_references.len()),
2695 reference_limit,
2696 )?);
2697 }
2698 let dependency_plan = reference_dependency_levels(
2699 &analysis_doc,
2700 analysis_signature,
2701 &analysis_references,
2702 transform_options,
2703 &operation.budgets().transforms,
2704 SigningUriResolution {
2705 id_attributes: request.id_attributes,
2706 same_document_id_semantics: policy.transforms.same_document_id_semantics,
2707 external_resources: request.external_resources,
2708 },
2709 )?;
2710 let (node_levels, plan_nodes) =
2711 compile_signing_operation_plan(operation, &dependency_plan, setup_gate)?;
2712 for (level, node_level) in dependency_plan.levels.into_iter().zip(node_levels) {
2713 operation.run_document_transition_batch_with_budgets(
2714 &node_level,
2715 document,
2716 |document, budgets| {
2717 let replacements = document.with_view(|view| {
2718 let current_doc = view.document();
2719 let current_signature = find_signing_signature_node(
2720 current_doc,
2721 SigningSignatureTarget::Index(request.target_signature),
2722 )?;
2723 let current_signed_info = find_required_child(current_signature, "SignedInfo")?;
2724 let current_signed_info_references = parse_signing_references_with_budget(
2725 current_signed_info,
2726 &mut budgets.xpath_parse,
2727 )?;
2728 let current_manifest_references = if process_manifests {
2729 parse_signing_manifest_references(
2730 current_signature,
2731 &mut budgets.xpath_parse,
2732 reference_limit.saturating_sub(signed_info_references.len()),
2733 reference_limit,
2734 )?
2735 } else {
2736 Vec::new()
2737 };
2738 if current_signed_info_references.len() != signed_info_references.len()
2739 || current_manifest_references.len() != manifest_references.len()
2740 {
2741 return Err(SigningDigestError::InvalidStructure(
2742 "signing Reference set changed while filling digests".into(),
2743 ));
2744 }
2745 let mut destinations = Vec::with_capacity(level.len());
2746 let mut level_references = Vec::with_capacity(level.len());
2747 for index in &level {
2748 let reference = if *index < signed_info_references.len() {
2749 current_signed_info_references.get(*index)
2750 } else {
2751 current_manifest_references.get(*index - signed_info_references.len())
2752 }
2753 .ok_or_else(|| {
2754 SigningDigestError::InvalidStructure(
2755 "signing Reference set changed while filling digests".into(),
2756 )
2757 })?;
2758 destinations.push(view.node_identity_by_id(reference.digest_value_node_id));
2759 level_references.push(reference.clone());
2760 }
2761 let computed = compute_signing_reference_digests(
2762 current_doc,
2763 current_signature,
2764 level_references,
2765 transform_options,
2766 provider,
2767 &budgets.transforms,
2768 SigningUriResolution {
2769 id_attributes: request.id_attributes,
2770 same_document_id_semantics: policy
2771 .transforms
2772 .same_document_id_semantics,
2773 external_resources: request.external_resources,
2774 },
2775 )?;
2776 if computed.len() != destinations.len() {
2777 return Err(SigningDigestError::InvalidStructure(
2778 "signing Reference set changed while computing digests".into(),
2779 ));
2780 }
2781 Ok::<_, SigningDigestError>(
2782 destinations
2783 .into_iter()
2784 .zip(computed)
2785 .map(|(target, digest)| (target, digest.digest_value))
2786 .collect::<Vec<_>>(),
2787 )
2788 })?;
2789 document
2790 .replace_base64_contents_with_budget(
2791 &replacements,
2792 DocumentParseSettings::from_policy(&policy.xml, &policy.resources)
2793 .with_backend(document.xml_backend()),
2794 budgets.transforms.xml_parse_work(),
2795 )
2796 .map_err(map_owned_document_digest_mutation_error)
2797 },
2798 )?;
2799 }
2800 Ok(plan_nodes)
2801}
2802
2803#[cfg(test)]
2804fn fill_reference_digest_values_in_dependency_order(
2805 document: &mut XmlDocument,
2806 transform_options: TransformOptions,
2807 policy: &crate::policy::SigningPolicy,
2808 provider: &dyn crate::provider::CryptoProvider,
2809 budgets: &mut SigningOperationBudgets,
2810 target_signature: usize,
2811 id_attributes: &[crate::IdAttributeRegistration],
2812) -> Result<(), SigningDigestError> {
2813 let external_resources = ExternalResourceContext::new(
2814 None,
2815 policy.resources.max_external_resource_bytes,
2816 policy.resources.max_external_resource_total_bytes,
2817 );
2818 let binding = (document.identity(), document.generation());
2819 let mut operation = OperationExecutionContext::new(policy.clone(), budgets, Some(binding));
2820 fill_reference_digest_values_in_dependency_order_with_operation(
2821 document,
2822 transform_options,
2823 provider,
2824 &mut operation,
2825 SigningReferenceRequest {
2826 target_signature,
2827 id_attributes,
2828 external_resources: &external_resources,
2829 },
2830 None,
2831 )
2832 .map(|_| ())
2833}
2834
2835struct SigningDependencyPlan {
2836 dependencies: Vec<HashSet<usize>>,
2837 levels: Vec<Vec<usize>>,
2838}
2839
2840fn reference_dependency_levels(
2841 doc: &Document<'_>,
2842 signature: Node<'_, '_>,
2843 references: &[SigningReference],
2844 transform_options: TransformOptions,
2845 execution_budget: &TransformExecutionBudget,
2846 uri_resolution: SigningUriResolution<'_, '_>,
2847) -> Result<SigningDependencyPlan, SigningDigestError> {
2848 let resolver = uri_resolution.external_resources.bind(
2849 doc,
2850 uri_resolution.id_attributes,
2851 uri_resolution.same_document_id_semantics,
2852 );
2853 let terminal_signature_value_index = references.len();
2854 let signature_value = find_required_child(signature, "SignatureValue")?;
2855 let mut tracked_mutable_nodes = references
2856 .iter()
2857 .enumerate()
2858 .flat_map(|(index, reference)| {
2859 std::iter::once((index, reference.digest_value_node_id)).chain(
2860 doc.get_node(reference.digest_value_node_id)
2861 .into_iter()
2862 .flat_map(|node| node.children())
2863 .filter(|node| node.is_text())
2864 .map(move |node| (index, node.id())),
2865 )
2866 })
2867 .collect::<Vec<_>>();
2868 tracked_mutable_nodes.push((terminal_signature_value_index, signature_value.id()));
2869 tracked_mutable_nodes.extend(
2870 signature_value
2871 .children()
2872 .filter(|node| node.is_text())
2873 .map(|node| (terminal_signature_value_index, node.id())),
2874 );
2875 let analyses = references
2876 .iter()
2877 .map(|reference| {
2878 let origin = signing_reference_origin(doc, reference)?;
2879 let initial_data = resolver.dereference_from_with_budget(
2880 &reference.uri,
2881 origin,
2882 execution_budget.node_set_materialization(),
2883 execution_budget.xml_base_resolution(),
2884 )?;
2885 let output = execute_transforms_with_dependency_nodes(
2886 signature,
2887 initial_data,
2888 &reference.transforms,
2889 transform_options,
2890 execution_budget,
2891 tracked_mutable_nodes.clone(),
2892 )?;
2893 Ok(output.dependencies)
2894 })
2895 .collect::<Result<Vec<_>, SigningDigestError>>()?;
2896 if analyses
2897 .iter()
2898 .any(|dependencies| dependencies.contains(&terminal_signature_value_index))
2899 {
2900 return Err(SigningDigestError::InvalidStructure(
2901 "Reference dependency cycle includes the mutable SignatureValue".into(),
2902 ));
2903 }
2904 let mut remaining_dependencies = analyses.clone();
2905 let mut completed = vec![false; references.len()];
2906 let mut levels = Vec::new();
2907 while completed.iter().any(|done| !done) {
2908 let ready = remaining_dependencies
2909 .iter()
2910 .enumerate()
2911 .filter_map(|(index, dependencies)| {
2912 (!completed[index] && dependencies.is_empty()).then_some(index)
2913 })
2914 .collect::<Vec<_>>();
2915 if ready.is_empty() {
2916 return Err(SigningDigestError::InvalidStructure(
2917 "Manifest Reference digest dependency cycle".into(),
2918 ));
2919 }
2920 for index in &ready {
2921 completed[*index] = true;
2922 }
2923 for dependency_set in &mut remaining_dependencies {
2924 dependency_set.retain(|dependency| !completed[*dependency]);
2925 }
2926 levels.push(ready);
2927 }
2928
2929 debug_assert!(references.iter().all(|reference| {
2933 signature.range().start <= reference.digest_value_range.start
2934 && signature.range().end >= reference.digest_value_range.end
2935 }));
2936 Ok(SigningDependencyPlan {
2937 dependencies: analyses,
2938 levels,
2939 })
2940}
2941
2942fn signing_reference_origin<'doc, 'input>(
2943 doc: &'doc Document<'input>,
2944 reference: &SigningReference,
2945) -> Result<Node<'doc, 'input>, SigningDigestError> {
2946 doc.get_node(reference.origin_node_id).ok_or_else(|| {
2947 SigningDigestError::InvalidStructure(
2948 "signing Reference origin changed while computing digests".into(),
2949 )
2950 })
2951}
2952
2953fn compile_signing_operation_plan(
2954 operation: &mut OperationExecutionContext<
2955 crate::policy::SigningPolicy,
2956 &mut SigningOperationBudgets,
2957 >,
2958 dependency_plan: &SigningDependencyPlan,
2959 setup_gate: Option<OperationNodeId>,
2960) -> Result<(Vec<Vec<OperationNodeId>>, SigningPlanNodes), SigningDigestError> {
2961 let reference_count = dependency_plan.levels.iter().map(Vec::len).sum::<usize>();
2962 let mut digest_nodes = vec![None; reference_count];
2963 for (index, digest_slot) in digest_nodes.iter_mut().enumerate() {
2964 let digest_node = operation.add_node(
2965 OperationNodeKind::Digest { index },
2966 OperationStage::Digest,
2967 None,
2968 );
2969 if let Some(setup_gate) = setup_gate {
2970 operation
2971 .add_dependency(digest_node, setup_gate)
2972 .map_err(map_signing_digest_plan_error)?;
2973 }
2974 *digest_slot = Some(digest_node);
2975 }
2976
2977 let mut node_levels = Vec::with_capacity(dependency_plan.levels.len());
2978 for level in &dependency_plan.levels {
2979 let nodes = level
2980 .iter()
2981 .map(|index| {
2982 digest_nodes
2983 .get(*index)
2984 .and_then(|node| *node)
2985 .ok_or_else(|| {
2986 SigningDigestError::InvalidStructure(
2987 "compiled signing Reference index is unavailable".into(),
2988 )
2989 })
2990 })
2991 .collect::<Result<Vec<_>, _>>()?;
2992 for (index, node) in level.iter().zip(&nodes) {
2993 for dependency_index in &dependency_plan.dependencies[*index] {
2994 let dependency = digest_nodes
2995 .get(*dependency_index)
2996 .and_then(|node| *node)
2997 .ok_or_else(|| {
2998 SigningDigestError::InvalidStructure(
2999 "compiled signing dependency index is unavailable".into(),
3000 )
3001 })?;
3002 operation
3003 .add_dependency(*node, dependency)
3004 .map_err(map_signing_digest_plan_error)?;
3005 }
3006 }
3007 node_levels.push(nodes);
3008 }
3009
3010 let canonicalization = operation.add_node(
3011 OperationNodeKind::Canonicalization,
3012 OperationStage::Canonicalization,
3013 None,
3014 );
3015 for digest in digest_nodes.iter().flatten() {
3016 operation
3017 .add_dependency(canonicalization, *digest)
3018 .map_err(map_signing_digest_plan_error)?;
3019 }
3020 let crypto = operation.add_node(OperationNodeKind::Crypto, OperationStage::Crypto, None);
3021 operation
3022 .add_dependency(crypto, canonicalization)
3023 .map_err(map_signing_digest_plan_error)?;
3024 let evidence = operation.add_node(OperationNodeKind::Evidence, OperationStage::Evidence, None);
3025 operation
3026 .add_dependency(evidence, crypto)
3027 .map_err(map_signing_digest_plan_error)?;
3028 let mutation = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
3029 operation
3030 .add_dependency(mutation, evidence)
3031 .map_err(map_signing_digest_plan_error)?;
3032 operation.compile().map_err(map_signing_digest_plan_error)?;
3033
3034 Ok((
3035 node_levels,
3036 SigningPlanNodes {
3037 canonicalization,
3038 crypto,
3039 evidence,
3040 mutation,
3041 },
3042 ))
3043}
3044
3045fn map_signing_digest_plan_error(error: OperationPlanError) -> SigningDigestError {
3046 SigningDigestError::InvalidStructure(error.to_string())
3047}
3048
3049fn commit_signed_staged(
3050 document: &mut XmlDocument,
3051 staged: XmlDocument,
3052 policy: &crate::policy::SigningPolicy,
3053) -> Result<(), SigningError> {
3054 let mut operation = OperationExecutionContext::new(
3055 policy.clone(),
3056 (),
3057 Some((document.identity(), document.generation())),
3058 );
3059 let mutation = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
3060 operation.compile()?;
3061 operation.run_document_transition(mutation, document, |document, _| {
3062 document
3063 .commit_staged(staged)
3064 .map_err(map_owned_document_mutation_error)
3065 })
3066}
3067
3068fn validate_signing_references(
3069 references: &[SigningReference],
3070 total_references: usize,
3071 policy: Option<&crate::policy::SigningPolicy>,
3072 has_external_resources: bool,
3073) -> Result<(), SigningDigestError> {
3074 if let Some(policy) = policy
3075 && total_references > policy.resources.max_references
3076 {
3077 return Err(crate::policy::PolicyViolation::ResourceLimit {
3078 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3079 maximum: policy.resources.max_references,
3080 actual: total_references,
3081 }
3082 .into());
3083 }
3084 for reference in references {
3085 if let Some(policy) = policy {
3086 validate_signing_reference_uri(&reference.uri, policy)?;
3087 validate_signing_reference_request(&reference.uri, has_external_resources)?;
3088 }
3089 if let Some(policy) = policy
3090 && reference.transforms.len() > policy.resources.max_transforms_per_reference
3091 {
3092 return Err(crate::policy::PolicyViolation::ResourceLimit {
3093 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3094 maximum: policy.resources.max_transforms_per_reference,
3095 actual: reference.transforms.len(),
3096 }
3097 .into());
3098 }
3099 if let Some(policy) = policy {
3100 policy.check_digest_algorithm(reference.digest_method)?;
3101 } else if !reference.digest_method.signing_allowed() {
3102 return Err(SigningDigestError::SigningAlgorithmDisabled {
3103 uri: reference.digest_method.uri(),
3104 });
3105 }
3106 let initial_binary = !reference.uri.is_empty() && !reference.uri.starts_with('#');
3107 validate_signing_transform_policy(
3108 initial_binary,
3109 &reference.transforms,
3110 policy.and_then(|policy| policy.transforms.allowed_algorithms.as_ref()),
3111 )?;
3112 }
3113 Ok(())
3114}
3115
3116fn validate_signing_signed_info_methods(
3117 signature: Node<'_, '_>,
3118 policy: &crate::policy::SigningPolicy,
3119) -> Result<(SignatureAlgorithm, Option<usize>), SigningError> {
3120 let signed_info = find_required_child(signature, "SignedInfo")?;
3121 let canonicalization_method =
3122 element_children(signed_info)
3123 .next()
3124 .ok_or(SigningDigestError::MissingElement {
3125 element: "CanonicalizationMethod",
3126 })?;
3127 verify_ds_element(canonicalization_method, "CanonicalizationMethod")?;
3128 let algorithm = required_algorithm_attr(canonicalization_method, "CanonicalizationMethod")?;
3129 if policy
3130 .transforms
3131 .allowed_algorithms
3132 .as_ref()
3133 .is_some_and(|allowed| !allowed.contains(algorithm))
3134 {
3135 return Err(crate::policy::PolicyViolation::Algorithm {
3136 operation: "SignedInfo canonicalization",
3137 algorithm: algorithm.to_owned(),
3138 }
3139 .into());
3140 }
3141 let signature_method =
3142 element_children(signed_info)
3143 .nth(1)
3144 .ok_or(SigningDigestError::MissingElement {
3145 element: "SignatureMethod",
3146 })?;
3147 super::parse::parse_signature_method(signature_method, &policy.resources)
3148 .map(|(algorithm, hmac, _context)| (algorithm, hmac))
3149 .map_err(SigningError::from)
3150}
3151
3152struct SigningUriResolution<'a, 'resources> {
3153 id_attributes: &'a [crate::IdAttributeRegistration],
3154 same_document_id_semantics: crate::policy::SameDocumentIdSemantics,
3155 external_resources: &'a ExternalResourceContext<'resources>,
3156}
3157
3158fn compute_signing_reference_digests(
3159 doc: &Document<'_>,
3160 signature: Node<'_, '_>,
3161 references: Vec<SigningReference>,
3162 transform_options: TransformOptions,
3163 provider: &dyn crate::provider::CryptoProvider,
3164 execution_budget: &TransformExecutionBudget,
3165 uri_resolution: SigningUriResolution<'_, '_>,
3166) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
3167 let resolver = uri_resolution.external_resources.bind(
3168 doc,
3169 uri_resolution.id_attributes,
3170 uri_resolution.same_document_id_semantics,
3171 );
3172 references
3173 .into_iter()
3174 .enumerate()
3175 .map(|(index, reference)| {
3176 let origin = signing_reference_origin(doc, &reference)?;
3177 let initial_data = resolver.dereference_from_with_budget(
3178 &reference.uri,
3179 origin,
3180 execution_budget.node_set_materialization(),
3181 execution_budget.xml_base_resolution(),
3182 )?;
3183 let pre_digest = execute_transforms_with_options_and_budget(
3184 signature,
3185 initial_data,
3186 &reference.transforms,
3187 transform_options,
3188 execution_budget,
3189 )?;
3190 let digest = super::compute_digest_with_provider(
3191 provider,
3192 reference.digest_method,
3193 &pre_digest,
3194 )?;
3195 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
3196 Ok(ComputedReferenceDigest {
3197 index,
3198 uri: reference.uri,
3199 digest_method: reference.digest_method,
3200 digest_value,
3201 })
3202 })
3203 .collect()
3204}
3205
3206pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
3214 let execution_budget = TransformExecutionBudget::default();
3215 fill_reference_digest_values_with_options(
3216 xml,
3217 TransformOptions::default(),
3218 None,
3219 crate::provider::default_provider(),
3220 &execution_budget,
3221 None,
3222 &[],
3223 )
3224}
3225
3226fn fill_reference_digest_values_with_options(
3227 xml: &str,
3228 transform_options: TransformOptions,
3229 policy: Option<&crate::policy::SigningPolicy>,
3230 provider: &dyn crate::provider::CryptoProvider,
3231 execution_budget: &TransformExecutionBudget,
3232 target_signature: Option<usize>,
3233 id_attributes: &[crate::IdAttributeRegistration],
3234) -> Result<String, SigningDigestError> {
3235 let default_policy = crate::policy::SigningPolicy::default();
3236 let effective_policy = policy.unwrap_or(&default_policy);
3237 let (prepared, target_signature) =
3238 prepare_reference_digest_input(xml, policy, execution_budget, target_signature)?;
3239 let digest_values = compute_prepared_reference_digest_values_with_options(
3240 prepared.as_ref(),
3241 transform_options,
3242 Some(effective_policy),
3243 provider,
3244 execution_budget,
3245 target_signature,
3246 id_attributes,
3247 )?
3248 .into_iter()
3249 .map(|digest| digest.digest_value);
3250 Ok(fill_signed_info_digest_values_at_index_with_budget(
3251 prepared.as_ref(),
3252 digest_values,
3253 target_signature,
3254 Some(effective_policy),
3255 Some(execution_budget.xml_parse_work()),
3256 )?)
3257}
3258
3259fn canonicalize_signed_info(
3260 document: &XmlDocument,
3261 policy: &crate::policy::SigningPolicy,
3262 budgets: &mut SigningOperationBudgets,
3263 target_signature: usize,
3264) -> Result<
3265 (
3266 SignatureAlgorithm,
3267 Option<usize>,
3268 Vec<u8>,
3269 super::SignatureContext,
3270 ),
3271 SigningError,
3272> {
3273 document.with_view(|view| {
3274 let doc = view.document();
3275 let signature =
3276 find_signing_signature_node(doc, SigningSignatureTarget::Index(target_signature))
3277 .map_err(SigningError::Digest)?;
3278 let signed_info_node =
3279 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
3280 let signed_info = parse_signed_info_with_xpath_budget(
3281 signed_info_node,
3282 &mut budgets.xpath_parse,
3283 &policy.resources,
3284 )?;
3285 if policy
3286 .transforms
3287 .allowed_algorithms
3288 .as_ref()
3289 .is_some_and(|allowed| !allowed.contains(signed_info.c14n_method.uri()))
3290 {
3291 return Err(crate::policy::PolicyViolation::Algorithm {
3292 operation: "SignedInfo canonicalization",
3293 algorithm: signed_info.c14n_method.uri().to_owned(),
3294 }
3295 .into());
3296 }
3297 let signed_info_subtree: HashSet<_> = signed_info_node
3298 .descendants()
3299 .map(|node: Node<'_, '_>| node.id())
3300 .collect();
3301 let mut canonical_signed_info = Vec::new();
3302 canonicalize_bounded_with_xml_base_budget(
3303 doc,
3304 Some(&|node| signed_info_subtree.contains(&node.id())),
3305 &signed_info.c14n_method,
3306 budgets.transforms.remaining_c14n_output(),
3307 budgets.transforms.xml_base_resolution(),
3308 &mut canonical_signed_info,
3309 )
3310 .map_err(|error| {
3311 if let Some(violation) = map_c14n_resource_policy_violation(
3312 &error,
3313 crate::policy::resource_name::CANONICALIZED_BYTES,
3314 budgets.transforms.c14n_output_limit(),
3315 ) {
3316 SigningError::Policy(violation)
3317 } else {
3318 SigningError::Canonicalization(error)
3319 }
3320 })?;
3321 Ok((
3322 signed_info.signature_method,
3323 signed_info.hmac_output_length_bits,
3324 canonical_signed_info,
3325 signed_info.signature_context,
3326 ))
3327 })
3328}
3329
3330const SECOND_EDITION_GENERATION_C14N_URI: &str = "http://www.w3.org/2006/12/xml-c14n11";
3331
3332struct SourceEdit {
3333 range: Range<usize>,
3334 replacement: String,
3335}
3336
3337fn materialize_second_edition_c14n11_candidate(
3338 document: &XmlDocument,
3339 target_signature: usize,
3340 policy: &crate::policy::SigningPolicy,
3341 has_external_resources: bool,
3342) -> Result<Option<String>, SigningDigestError> {
3343 let edits = document.with_view(|view| {
3344 let signature = find_signing_signature_node(
3345 view.document(),
3346 SigningSignatureTarget::Index(target_signature),
3347 )?;
3348 let signed_info = find_required_child(signature, "SignedInfo")?;
3349 let mut reference_nodes = element_children(signed_info)
3350 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Reference")))
3351 .collect::<Vec<_>>();
3352 if policy.manifest_processing == crate::policy::ManifestProcessing::Process {
3353 let reference_limit = policy
3354 .resources
3355 .max_references
3356 .min(MAX_REFERENCES_PER_SIGNATURE);
3357 for manifest in signature
3358 .children()
3359 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Object")))
3360 .flat_map(|object| {
3361 object
3362 .children()
3363 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Manifest")))
3364 })
3365 {
3366 let mut manifest_references = 0usize;
3367 for child in element_children(manifest) {
3368 verify_ds_element(child, "Reference")?;
3369 if reference_nodes.len() == reference_limit {
3370 return Err(crate::policy::PolicyViolation::ResourceLimit {
3371 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3372 maximum: reference_limit,
3373 actual: reference_limit.saturating_add(1),
3374 }
3375 .into());
3376 }
3377 reference_nodes.push(child);
3378 manifest_references += 1;
3379 }
3380 if manifest_references == 0 {
3381 return Err(SigningDigestError::MissingElement {
3382 element: "Reference",
3383 });
3384 }
3385 }
3386 }
3387 if reference_nodes.len() > policy.resources.max_references {
3388 return Err(crate::policy::PolicyViolation::ResourceLimit {
3389 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3390 maximum: policy.resources.max_references,
3391 actual: reference_nodes.len(),
3392 }
3393 .into());
3394 }
3395
3396 let mut edits = Vec::new();
3397 for reference_node in reference_nodes {
3398 let structure = parse_signing_reference_structure(reference_node)?;
3399 validate_signing_reference_uri(structure.uri, policy)?;
3400 validate_signing_reference_request(structure.uri, has_external_resources)?;
3401 let digest_uri = required_algorithm_attr(structure.digest_method_node, "DigestMethod")?;
3402 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
3403 SigningDigestError::UnsupportedAlgorithm {
3404 uri: digest_uri.to_owned(),
3405 }
3406 })?;
3407 policy.check_digest_algorithm(digest_method)?;
3408
3409 let transform_uris = generation_transform_uris(structure.transforms_node)?;
3410 if transform_uris.len() > policy.resources.max_transforms_per_reference {
3411 return Err(crate::policy::PolicyViolation::ResourceLimit {
3412 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3413 maximum: policy.resources.max_transforms_per_reference,
3414 actual: transform_uris.len(),
3415 }
3416 .into());
3417 }
3418 if let Some(allowed) = policy.transforms.allowed_algorithms.as_ref()
3419 && let Some(disallowed) = transform_uris
3420 .iter()
3421 .find(|algorithm| !allowed.contains(**algorithm))
3422 {
3423 return Err(crate::policy::PolicyViolation::Algorithm {
3424 operation: "signing transform",
3425 algorithm: (*disallowed).to_owned(),
3426 }
3427 .into());
3428 }
3429 let initial_binary = !structure.uri.is_empty() && !structure.uri.starts_with('#');
3430 if generation_chain_produces_binary(initial_binary, &transform_uris) {
3431 continue;
3432 }
3433 if transform_uris.len() >= policy.resources.max_transforms_per_reference {
3434 return Err(crate::policy::PolicyViolation::ResourceLimit {
3435 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3436 maximum: policy.resources.max_transforms_per_reference,
3437 actual: transform_uris.len().saturating_add(1),
3438 }
3439 .into());
3440 }
3441 if policy
3442 .transforms
3443 .allowed_algorithms
3444 .as_ref()
3445 .is_some_and(|allowed| !allowed.contains(SECOND_EDITION_GENERATION_C14N_URI))
3446 {
3447 return Err(crate::policy::PolicyViolation::Algorithm {
3448 operation: "signing transform",
3449 algorithm: SECOND_EDITION_GENERATION_C14N_URI.to_owned(),
3450 }
3451 .into());
3452 }
3453
3454 let edit = if let Some(transforms_node) = structure.transforms_node {
3455 c14n11_edit_for_transforms(view.xml(), transforms_node)?
3456 } else {
3457 let prefix = reference_node.prefix().unwrap_or_default();
3458 let qualifier = xml_qualifier(prefix);
3459 SourceEdit {
3460 range: structure.digest_method_node.range().start
3461 ..structure.digest_method_node.range().start,
3462 replacement: format!(
3463 "<{qualifier}Transforms><{qualifier}Transform Algorithm=\"{SECOND_EDITION_GENERATION_C14N_URI}\"/></{qualifier}Transforms>"
3464 ),
3465 }
3466 };
3467 edits.push(edit);
3468 }
3469 Ok::<_, SigningDigestError>(edits)
3470 })?;
3471 if edits.is_empty() {
3472 return Ok(None);
3473 }
3474
3475 let projected_len = edits
3476 .iter()
3477 .try_fold(document.as_xml().len(), |total, edit| {
3478 total
3479 .checked_sub(edit.range.len())
3480 .and_then(|value| value.checked_add(edit.replacement.len()))
3481 .ok_or_else(|| {
3482 SigningDigestError::InvalidStructure("document length overflow".into())
3483 })
3484 })?;
3485 policy
3486 .resources
3487 .validate_xml_document_len(projected_len)
3488 .map_err(SigningDigestError::Policy)?;
3489
3490 let mut candidate = document.as_xml().to_owned();
3491 let mut edits = edits;
3492 edits.sort_unstable_by_key(|edit| std::cmp::Reverse(edit.range.start));
3493 for edit in edits {
3494 candidate.replace_range(edit.range, &edit.replacement);
3495 }
3496 Ok(Some(candidate))
3497}
3498
3499fn c14n11_edit_for_transforms(
3500 xml: &str,
3501 transforms_node: Node<'_, '_>,
3502) -> Result<SourceEdit, SigningDigestError> {
3503 let range = transforms_node.range();
3504 let source = xml.get(range.clone()).ok_or_else(|| {
3505 SigningDigestError::InvalidStructure("Transforms source range is unavailable".into())
3506 })?;
3507 let prefix = transforms_node.prefix().unwrap_or_default();
3508 let qualifier = xml_qualifier(prefix);
3509 let transform =
3510 format!("<{qualifier}Transform Algorithm=\"{SECOND_EDITION_GENERATION_C14N_URI}\"/>");
3511 if source.trim_end().ends_with("/>") {
3512 let trimmed = source.trim_end();
3513 let opening = trimmed.strip_suffix("/>").ok_or_else(|| {
3514 SigningDigestError::InvalidStructure(
3515 "self-closing Transforms source is unavailable".into(),
3516 )
3517 })?;
3518 let trailing = &source[trimmed.len()..];
3519 return Ok(SourceEdit {
3520 range,
3521 replacement: format!("{opening}>{transform}</{qualifier}Transforms>{trailing}"),
3522 });
3523 }
3524 let closing = source.rfind("</").ok_or_else(|| {
3525 SigningDigestError::InvalidStructure("Transforms closing tag is unavailable".into())
3526 })?;
3527 let offset = range.start + closing;
3528 Ok(SourceEdit {
3529 range: offset..offset,
3530 replacement: transform,
3531 })
3532}
3533
3534fn generation_transform_uris<'a>(
3535 transforms_node: Option<Node<'a, 'a>>,
3536) -> Result<Vec<&'a str>, SigningDigestError> {
3537 let Some(transforms_node) = transforms_node else {
3538 return Ok(Vec::new());
3539 };
3540 let mut algorithms = Vec::new();
3541 for child in element_children(transforms_node) {
3542 if algorithms.len() == MAX_TRANSFORMS_PER_REFERENCE {
3543 return Err(crate::policy::PolicyViolation::ResourceLimit {
3544 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3545 maximum: MAX_TRANSFORMS_PER_REFERENCE,
3546 actual: MAX_TRANSFORMS_PER_REFERENCE.saturating_add(1),
3547 }
3548 .into());
3549 }
3550 if !child.has_tag_name((XMLDSIG_NS, "Transform")) {
3551 return Err(TransformError::UnsupportedTransform(
3552 "unexpected child element of <ds:Transforms>; only <ds:Transform> is allowed"
3553 .into(),
3554 )
3555 .into());
3556 }
3557 let algorithm = child.attribute("Algorithm").ok_or_else(|| {
3558 TransformError::UnsupportedTransform(
3559 "missing Algorithm attribute on <Transform>".into(),
3560 )
3561 })?;
3562 if algorithm != ENVELOPED_SIGNATURE_URI
3563 && algorithm != BASE64_TRANSFORM_URI
3564 && algorithm != XPATH_TRANSFORM_URI
3565 && algorithm != XPATH_FILTER2_TRANSFORM_URI
3566 && crate::c14n::C14nAlgorithm::from_uri(algorithm).is_none()
3567 {
3568 return Err(TransformError::UnsupportedTransform(algorithm.to_owned()).into());
3569 }
3570 algorithms.push(algorithm);
3571 }
3572 Ok(algorithms)
3573}
3574
3575fn generation_chain_produces_binary(initial_binary: bool, algorithms: &[&str]) -> bool {
3576 algorithms.last().map_or(initial_binary, |algorithm| {
3577 *algorithm == BASE64_TRANSFORM_URI
3578 || crate::c14n::C14nAlgorithm::from_uri(algorithm).is_some()
3579 })
3580}
3581
3582fn xml_qualifier(prefix: &str) -> String {
3583 if prefix.is_empty() {
3584 String::new()
3585 } else {
3586 format!("{prefix}:")
3587 }
3588}
3589
3590fn parse_signing_document<'a>(
3591 xml: &'a str,
3592 policy: Option<&crate::policy::SigningPolicy>,
3593 budget: &XmlParseWorkBudget,
3594 backend: crate::XmlBackend,
3595) -> Result<Document<'a>, SigningDigestError> {
3596 let settings = policy
3597 .map(|policy| DocumentParseSettings::from_policy(&policy.xml, &policy.resources))
3598 .unwrap_or_default()
3599 .with_backend(backend);
3600 super::mutation::parse_with_options_and_budget(xml, settings, Some(budget)).map_err(|error| {
3601 match error.into_policy_violation(settings) {
3602 Ok(error) => SigningDigestError::Policy(error),
3603 Err(XmlDocumentError::Parse(error)) => SigningDigestError::XmlParse(error),
3604 Err(error) => SigningDigestError::Document(error),
3605 }
3606 })
3607}
3608
3609fn parse_private_key_pem(private_key_pem: &str) -> Result<Zeroizing<Vec<u8>>, SigningKeyError> {
3610 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
3611 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
3612 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
3613 return Err(SigningKeyError::InvalidKeyPem);
3614 }
3615 if pem.label != "PRIVATE KEY" {
3616 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
3617 }
3618 Ok(Zeroizing::new(pem.contents))
3619}
3620
3621enum SigningSignatureTarget {
3622 First,
3623 Last,
3624 Index(usize),
3625}
3626
3627fn find_signing_signature_node<'a>(
3628 doc: &'a Document<'a>,
3629 target: SigningSignatureTarget,
3630) -> Result<Node<'a, 'a>, SigningDigestError> {
3631 let mut signatures = doc.descendants().filter(|node| {
3632 node.is_element()
3633 && node.tag_name().name() == "Signature"
3634 && node.tag_name().namespace() == Some(XMLDSIG_NS)
3635 });
3636 match target {
3637 SigningSignatureTarget::First => signatures.next(),
3638 SigningSignatureTarget::Last => signatures.next_back(),
3639 SigningSignatureTarget::Index(index) => signatures.nth(index),
3640 }
3641 .ok_or(SigningDigestError::MissingElement {
3642 element: "Signature",
3643 })
3644}
3645
3646fn signing_signature_index(
3647 doc: &Document<'_>,
3648 start_node_id: Option<&str>,
3649 id_attributes: &[crate::IdAttributeRegistration],
3650 selection: SignatureTemplateSelection,
3651) -> Result<usize, SigningDigestError> {
3652 let selected = if let Some(id) = start_node_id {
3653 let start = signing_start_node(doc, id, id_attributes)?;
3654 let mut signatures = start
3655 .descendants()
3656 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")));
3657 match selection.target() {
3658 SigningSignatureTarget::First => signatures.next(),
3659 SigningSignatureTarget::Last => signatures.next_back(),
3660 SigningSignatureTarget::Index(_) => unreachable!("public selection is not indexed"),
3661 }
3662 .ok_or_else(|| {
3663 SigningDigestError::InvalidStructure(format!(
3664 "selected node subtree has no Signature: {id}"
3665 ))
3666 })?
3667 } else {
3668 find_signing_signature_node(doc, selection.target())?
3669 };
3670 signature_index(doc, selected)
3671}
3672
3673fn signing_start_node<'a>(
3674 doc: &'a Document<'a>,
3675 id: &str,
3676 id_attributes: &[crate::IdAttributeRegistration],
3677) -> Result<Node<'a, 'a>, SigningDigestError> {
3678 UriReferenceResolver::with_id_registrations(doc, id_attributes)
3679 .node_for_id(id)
3680 .ok_or_else(|| {
3681 SigningDigestError::InvalidStructure(format!(
3682 "selected node ID is missing or ambiguous: {id}"
3683 ))
3684 })
3685}
3686
3687fn signature_index(
3688 doc: &Document<'_>,
3689 selected: Node<'_, '_>,
3690) -> Result<usize, SigningDigestError> {
3691 doc.descendants()
3692 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
3693 .position(|node| node == selected)
3694 .ok_or(SigningDigestError::MissingElement {
3695 element: "Signature",
3696 })
3697}
3698
3699fn parse_signing_references(
3700 signed_info: Node<'_, '_>,
3701) -> Result<Vec<SigningReference>, SigningDigestError> {
3702 parse_signing_references_with_budget(signed_info, &mut XPathSignatureParseBudget::default())
3703}
3704
3705fn parse_signing_references_with_budget(
3706 signed_info: Node<'_, '_>,
3707 xpath_budget: &mut XPathSignatureParseBudget,
3708) -> Result<Vec<SigningReference>, SigningDigestError> {
3709 verify_ds_element(signed_info, "SignedInfo")?;
3710 let mut children = element_children(signed_info);
3711
3712 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
3713 element: "CanonicalizationMethod",
3714 })?;
3715 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
3716 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
3717
3718 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
3719 element: "SignatureMethod",
3720 })?;
3721 verify_ds_element(signature_method_node, "SignatureMethod")?;
3722 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
3723
3724 let mut references = Vec::new();
3725 for child in children {
3726 verify_ds_element(child, "Reference")?;
3727 if references.len() == MAX_REFERENCES_PER_SIGNATURE {
3728 return Err(SigningDigestError::InvalidStructure(format!(
3729 "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
3730 )));
3731 }
3732 references.push(parse_signing_reference(child, xpath_budget)?);
3733 }
3734 if references.is_empty() {
3735 return Err(SigningDigestError::MissingElement {
3736 element: "Reference",
3737 });
3738 }
3739 Ok(references)
3740}
3741
3742fn parse_signing_manifest_references(
3743 signature: Node<'_, '_>,
3744 xpath_budget: &mut XPathSignatureParseBudget,
3745 mut remaining_capacity: usize,
3746 maximum_references: usize,
3747) -> Result<Vec<SigningReference>, SigningDigestError> {
3748 let mut references = Vec::new();
3749 for manifest in signature
3750 .children()
3751 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Object")))
3752 .flat_map(|object| {
3753 object
3754 .children()
3755 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Manifest")))
3756 })
3757 {
3758 let mut manifest_references = 0usize;
3759 for child in element_children(manifest) {
3760 verify_ds_element(child, "Reference")?;
3761 if remaining_capacity == 0 {
3762 return Err(crate::policy::PolicyViolation::ResourceLimit {
3763 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3764 maximum: maximum_references,
3765 actual: maximum_references.saturating_add(1),
3766 }
3767 .into());
3768 }
3769 remaining_capacity -= 1;
3770 references.push(parse_signing_reference(child, xpath_budget)?);
3771 manifest_references += 1;
3772 }
3773 if manifest_references == 0 {
3774 return Err(SigningDigestError::MissingElement {
3775 element: "Reference",
3776 });
3777 }
3778 }
3779 Ok(references)
3780}
3781
3782fn parse_signing_reference(
3783 reference_node: Node<'_, '_>,
3784 xpath_budget: &mut XPathSignatureParseBudget,
3785) -> Result<SigningReference, SigningDigestError> {
3786 let structure = parse_signing_reference_structure(reference_node)?;
3787 let transforms = structure.transforms_node.map_or_else(
3788 || Ok(Vec::new()),
3789 |node| parse_transforms_with_budget(node, xpath_budget),
3790 )?;
3791 let digest_uri = required_algorithm_attr(structure.digest_method_node, "DigestMethod")?;
3792 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
3793 SigningDigestError::UnsupportedAlgorithm {
3794 uri: digest_uri.to_string(),
3795 }
3796 })?;
3797
3798 Ok(SigningReference {
3799 uri: structure.uri.to_owned(),
3800 origin_node_id: reference_node.id(),
3801 transforms,
3802 digest_method,
3803 digest_value_range: structure.digest_value_node.range(),
3804 digest_value_node_id: structure.digest_value_node.id(),
3805 })
3806}
3807
3808struct SigningReferenceStructure<'a> {
3809 uri: &'a str,
3810 transforms_node: Option<Node<'a, 'a>>,
3811 digest_method_node: Node<'a, 'a>,
3812 digest_value_node: Node<'a, 'a>,
3813}
3814
3815fn parse_signing_reference_structure<'a>(
3816 reference_node: Node<'a, 'a>,
3817) -> Result<SigningReferenceStructure<'a>, SigningDigestError> {
3818 let uri = reference_node.attribute("URI").ok_or_else(|| {
3819 SigningDigestError::InvalidStructure("signing Reference must include URI attribute".into())
3820 })?;
3821 let mut children = element_children(reference_node);
3822 let first = children.next().ok_or(SigningDigestError::MissingElement {
3823 element: "DigestMethod",
3824 })?;
3825 let (transforms_node, digest_method_node) = if first.has_tag_name((XMLDSIG_NS, "Transforms")) {
3826 (
3827 Some(first),
3828 children.next().ok_or(SigningDigestError::MissingElement {
3829 element: "DigestMethod",
3830 })?,
3831 )
3832 } else {
3833 (None, first)
3834 };
3835 verify_ds_element(digest_method_node, "DigestMethod")?;
3836 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
3837 element: "DigestValue",
3838 })?;
3839 verify_ds_element(digest_value_node, "DigestValue")?;
3840 if let Some(unexpected) = children.next() {
3841 return Err(SigningDigestError::InvalidStructure(format!(
3842 "unexpected element <{}> after <DigestValue> in <Reference>",
3843 unexpected.tag_name().name()
3844 )));
3845 }
3846 Ok(SigningReferenceStructure {
3847 uri,
3848 transforms_node,
3849 digest_method_node,
3850 digest_value_node,
3851 })
3852}
3853
3854fn find_required_child<'a>(
3855 parent: Node<'a, 'a>,
3856 child_name: &'static str,
3857) -> Result<Node<'a, 'a>, SigningDigestError> {
3858 parent
3859 .children()
3860 .find(|node| {
3861 node.is_element()
3862 && node.tag_name().name() == child_name
3863 && node.tag_name().namespace() == Some(XMLDSIG_NS)
3864 })
3865 .ok_or(SigningDigestError::MissingElement {
3866 element: child_name,
3867 })
3868}
3869
3870fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
3871 node.children().filter(Node::is_element)
3872}
3873
3874fn verify_ds_element(
3875 node: Node<'_, '_>,
3876 expected_name: &'static str,
3877) -> Result<(), SigningDigestError> {
3878 if !node.is_element() {
3879 return Err(SigningDigestError::InvalidStructure(format!(
3880 "expected element <{expected_name}>, got non-element node"
3881 )));
3882 }
3883 let tag = node.tag_name();
3884 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
3885 return Err(SigningDigestError::InvalidStructure(format!(
3886 "expected <ds:{expected_name}>, got <{}>",
3887 tag.name()
3888 )));
3889 }
3890 Ok(())
3891}
3892
3893fn required_algorithm_attr<'a>(
3894 node: Node<'a, 'a>,
3895 element_name: &'static str,
3896) -> Result<&'a str, SigningDigestError> {
3897 node.attribute("Algorithm").ok_or_else(|| {
3898 SigningDigestError::InvalidStructure(format!(
3899 "missing Algorithm attribute on <{element_name}>"
3900 ))
3901 })
3902}
3903
3904#[cfg(test)]
3905mod error_conversion_tests {
3906 use super::*;
3907 use crate::policy::PolicyViolation;
3908
3909 struct RejectingSigningKey;
3910
3911 impl SigningKey for RejectingSigningKey {
3912 fn sign(
3913 &self,
3914 _algorithm: SignatureAlgorithm,
3915 _canonical_signed_info: &[u8],
3916 ) -> Result<Vec<u8>, SigningKeyError> {
3917 Err(SigningKeyError::SigningFailed)
3918 }
3919
3920 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
3921 Err(SigningKeyError::PublicKeyEncodingFailed)
3922 }
3923 }
3924
3925 struct FixedRsaSigningKey;
3926
3927 struct WrongWidthDsaSigningKey;
3928
3929 impl SigningKey for WrongWidthDsaSigningKey {
3930 fn sign(
3931 &self,
3932 _algorithm: SignatureAlgorithm,
3933 _canonical_signed_info: &[u8],
3934 ) -> Result<Vec<u8>, SigningKeyError> {
3935 unreachable!("preflight must reject this candidate")
3936 }
3937
3938 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
3939 Ok(SigningPublicKeyInfo::Dsa {
3940 spki_der: Vec::new(),
3941 p: Vec::new(),
3942 q: Vec::new(),
3943 g: Vec::new(),
3944 y: Vec::new(),
3945 modulus_bits: 2048,
3946 component_len: 20,
3947 })
3948 }
3949 }
3950
3951 #[test]
3952 fn dsa_q_width_mismatch_is_candidate_incompatibility() {
3953 let error = validate_signing_key(
3955 &WrongWidthDsaSigningKey,
3956 SignatureAlgorithm::DsaSha256,
3957 &crate::policy::SigningPolicy::default(),
3958 )
3959 .expect_err("SHA-256 requires a 256-bit q");
3960 assert!(matches!(
3961 error,
3962 SigningError::Key(SigningKeyError::UnsupportedAlgorithm { .. })
3963 ));
3964 }
3965
3966 impl SigningKey for FixedRsaSigningKey {
3967 fn sign(
3968 &self,
3969 _algorithm: SignatureAlgorithm,
3970 _canonical_signed_info: &[u8],
3971 ) -> Result<Vec<u8>, SigningKeyError> {
3972 Ok(vec![0x5a; 256])
3973 }
3974
3975 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
3976 Ok(SigningPublicKeyInfo::Rsa {
3977 spki_der: Vec::new(),
3978 modulus: vec![0x80; 256],
3979 exponent: vec![1, 0, 1],
3980 })
3981 }
3982 }
3983
3984 #[test]
3985 fn namespace_exhaustion_preserves_typed_signing_errors() {
3986 let make_error = || {
3988 XmlDocumentError::Parse(crate::xml::dom::ParseError::NamespaceBindingLimitReached {
3989 maximum: 2,
3990 actual: 3,
3991 })
3992 };
3993 assert!(matches!(
3994 owned_document_policy_violation(make_error()),
3995 Ok(PolicyViolation::ResourceLimit {
3996 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
3997 maximum: 2,
3998 actual: 3,
3999 })
4000 ));
4001 let mut policy = crate::policy::SigningPolicy::default();
4002 policy.resources.max_xml_namespace_bindings = 0;
4003 let context = SignContext::new(&RejectingSigningKey).policy(policy);
4004 let builder = SignatureBuilder::new(
4005 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4006 SignatureAlgorithm::RsaSha256,
4007 );
4008 let xml = "<root xmlns:p='urn:p'/>";
4009 for result in [
4010 context.sign_template(xml),
4011 context.sign_with_builder(xml, &builder),
4012 ] {
4013 assert!(
4014 matches!(
4015 result,
4016 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4017 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
4018 maximum: 0,
4019 actual: 1,
4020 }))
4021 ),
4022 "unexpected signing result: {result:?}"
4023 );
4024 }
4025 }
4026
4027 #[test]
4028 fn signing_error_promotes_every_policy_failure() {
4029 let digest = SigningError::from(SigningDigestError::Policy(PolicyViolation::Algorithm {
4032 operation: "signing",
4033 algorithm: "urn:test:digest".into(),
4034 }));
4035 assert!(matches!(digest, SigningError::Policy(_)));
4036
4037 let mutation = SigningError::from(SigningDigestError::XmlMutation(
4038 XmlMutationError::Policy(PolicyViolation::ResourceLimit {
4039 resource: crate::policy::resource_name::XML_DOCUMENT,
4040 maximum: 1,
4041 actual: 2,
4042 }),
4043 ));
4044 assert!(matches!(mutation, SigningError::Policy(_)));
4045 }
4046
4047 #[test]
4048 fn manifest_reparse_consumes_the_signature_wide_xpath_budget() {
4049 let filter = r#"<xf:XPath xmlns:xf="http://www.w3.org/2002/06/xmldsig-filter2" Filter="intersect">true()</xf:XPath>"#;
4053 let signed_info_transforms = format!(
4054 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>"#,
4055 filter.repeat(64)
4056 );
4057 let signed_info_references = (0..62)
4058 .map(|index| {
4059 let extra = if index == 0 {
4060 r#"<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform>"#
4061 } else {
4062 ""
4063 };
4064 format!(
4065 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>"##
4066 )
4067 })
4068 .collect::<String>();
4069 let manifest_reference = |id: &str| {
4070 format!(
4071 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>"##
4072 )
4073 };
4074 let xml = format!(
4075 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>"##,
4076 manifest_reference("payload"),
4077 manifest_reference("payload")
4078 );
4079 let policy = crate::policy::SigningPolicy {
4080 manifest_processing: crate::policy::ManifestProcessing::Process,
4081 ..crate::policy::SigningPolicy::default()
4082 };
4083
4084 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4085 let error = fill_reference_digest_values_in_dependency_order(
4086 &mut document,
4087 TransformOptions::default(),
4088 &policy,
4089 crate::provider::default_provider(),
4090 &mut SigningOperationBudgets::default(),
4091 0,
4092 &[],
4093 )
4094 .expect_err("Manifest reparse must not reset the XPath parse budget");
4095
4096 assert!(
4097 matches!(
4098 &error,
4099 SigningDigestError::Transform(TransformError::Policy(
4100 crate::policy::PolicyViolation::ResourceLimit {
4101 resource: "XPath expressions",
4102 ..
4103 }
4104 ))
4105 ),
4106 "expected the shared XPath budget error, got: {error:?}"
4107 );
4108 }
4109
4110 #[test]
4111 fn dependency_levels_share_the_xml_parse_work_budget() {
4112 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>"##;
4116 let policy = crate::policy::SigningPolicy {
4117 manifest_processing: crate::policy::ManifestProcessing::Process,
4118 ..crate::policy::SigningPolicy::default()
4119 };
4120 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4121 let mut budgets = SigningOperationBudgets::from_resources(&policy.resources);
4122
4123 fill_reference_digest_values_in_dependency_order(
4124 &mut document,
4125 TransformOptions::default(),
4126 &policy,
4127 crate::provider::default_provider(),
4128 &mut budgets,
4129 0,
4130 &[],
4131 )
4132 .expect("the default cumulative budget must cover nested dependencies");
4133 let consumed = budgets.transforms.xml_parse_work().consumed();
4134 assert!(
4135 consumed > xml.len().saturating_mul(6),
4136 "analysis and dependency reparses must all be charged"
4137 );
4138
4139 let mut constrained_policy = policy;
4140 constrained_policy.resources.max_xml_parse_work_bytes = consumed - 1;
4141 let mut constrained_document = XmlDocument::parse(xml).expect("fixture must parse");
4142 let mut constrained_budgets =
4143 SigningOperationBudgets::from_resources(&constrained_policy.resources);
4144 let error = fill_reference_digest_values_in_dependency_order(
4145 &mut constrained_document,
4146 TransformOptions::default(),
4147 &constrained_policy,
4148 crate::provider::default_provider(),
4149 &mut constrained_budgets,
4150 0,
4151 &[],
4152 )
4153 .expect_err("one byte below measured work must fail closed");
4154
4155 assert!(matches!(
4156 error,
4157 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
4158 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4159 maximum,
4160 actual,
4161 }) if maximum == consumed - 1 && actual >= consumed
4162 ));
4163 }
4164
4165 #[test]
4166 fn final_signed_info_parse_consumes_the_signing_xpath_budget() {
4167 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>"##;
4171 let mut policy = crate::policy::SigningPolicy::default();
4172 policy.resources.max_xpath_expressions = 3;
4173
4174 let error = SignContext::new(&RejectingSigningKey)
4175 .policy(policy)
4176 .sign_template(xml)
4177 .expect_err("the final SignedInfo parse must not reset the XPath budget");
4178
4179 assert!(
4180 matches!(
4181 error,
4182 SigningError::Policy(PolicyViolation::ResourceLimit {
4183 resource: crate::policy::resource_name::XPATH_EXPRESSIONS,
4184 maximum: 3,
4185 ..
4186 })
4187 ),
4188 "expected the shared XPath parse budget error, got: {error:?}"
4189 );
4190 }
4191
4192 #[test]
4193 fn signing_initial_parse_consumes_the_operation_xml_parse_budget() {
4194 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>"##;
4198 let mut policy = crate::policy::SigningPolicy::default();
4199 policy.resources.max_xml_parse_work_bytes = 0;
4200
4201 let error = SignContext::new(&RejectingSigningKey)
4202 .policy(policy)
4203 .sign_template(xml)
4204 .expect_err("a zero parse-work budget must reject the initial parse");
4205
4206 assert!(matches!(
4207 error,
4208 SigningError::Policy(PolicyViolation::ResourceLimit {
4209 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4210 maximum: 0,
4211 actual,
4212 }) if actual == xml.len()
4213 ));
4214 }
4215
4216 #[test]
4217 fn signing_string_entry_point_enforces_policy_depth() {
4218 let mut policy = crate::policy::SigningPolicy::default();
4221 policy.resources.max_xml_depth = 2;
4222 let xml = "<root><child><leaf/></child></root>";
4223
4224 assert!(matches!(
4225 SignContext::new(&RejectingSigningKey)
4226 .policy(policy)
4227 .sign_template(xml),
4228 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4229 resource: crate::policy::resource_name::XML_DEPTH,
4230 maximum: 2,
4231 actual: 3,
4232 }))
4233 ));
4234 }
4235
4236 #[test]
4237 fn builder_append_reports_policy_depth() {
4238 let mut policy = crate::policy::SigningPolicy::default();
4241 policy.resources.max_xml_depth = 4;
4242 let builder = SignatureBuilder::new(
4243 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4244 SignatureAlgorithm::RsaSha256,
4245 )
4246 .add_reference(crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri(""));
4247
4248 let result = SignContext::new(&FixedRsaSigningKey)
4249 .policy(policy)
4250 .sign_with_builder("<root/>", &builder);
4251 assert!(
4252 matches!(
4253 result,
4254 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4255 resource: crate::policy::resource_name::XML_DEPTH,
4256 maximum: 4,
4257 actual: 5,
4258 }))
4259 ),
4260 "unexpected builder depth result: {result:?}"
4261 );
4262 }
4263
4264 #[test]
4265 fn owned_signing_staged_copies_preserve_policy_errors() {
4266 let mut policy = crate::policy::SigningPolicy::default();
4269 policy.resources.max_xml_parse_work_bytes = 0;
4270 let context = SignContext::new(&RejectingSigningKey).policy(policy);
4271
4272 let mut template_document = XmlDocument::parse("<root/>").expect("fixture must parse");
4273 let template_before = template_document.as_xml().to_owned();
4274 let error = context
4275 .sign_document(&mut template_document)
4276 .expect_err("the staged template copy must exhaust the operation budget");
4277 assert!(matches!(
4278 error,
4279 SigningError::Policy(PolicyViolation::ResourceLimit {
4280 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4281 maximum: 0,
4282 actual,
4283 }) if actual == template_before.len()
4284 ));
4285 assert_eq!(template_document.as_xml(), template_before);
4286 assert_eq!(template_document.generation(), 0);
4287
4288 let mut builder_document = XmlDocument::parse("<root/>").expect("fixture must parse");
4289 let builder_before = builder_document.as_xml().to_owned();
4290 let builder = SignatureBuilder::new(
4291 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4292 SignatureAlgorithm::RsaSha256,
4293 );
4294 let error = context
4295 .sign_document_with_builder(&mut builder_document, &builder)
4296 .expect_err("the staged builder copy must exhaust the operation budget");
4297 assert!(matches!(
4298 error,
4299 SigningError::Policy(PolicyViolation::ResourceLimit {
4300 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4301 maximum: 0,
4302 actual,
4303 }) if actual == builder_before.len()
4304 ));
4305 assert_eq!(builder_document.as_xml(), builder_before);
4306 assert_eq!(builder_document.generation(), 0);
4307 }
4308
4309 #[test]
4310 fn owned_signing_commits_the_validated_stage_without_reparsing() {
4311 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>"##;
4315 let policy = crate::policy::SigningPolicy::default();
4316 let context = SignContext::new(&FixedRsaSigningKey).policy(policy.clone());
4317 let source = XmlDocument::parse(xml).expect("fixture must parse");
4318 let mut measured = SigningOperationBudgets::from_resources(&policy.resources);
4319 let mut staged = source
4320 .staged_copy_with_budget(
4321 DocumentParseSettings::from_policy(&policy.xml, &policy.resources),
4322 measured.transforms.xml_parse_work(),
4323 )
4324 .expect("staging must parse");
4325 context
4326 .sign_document_in_place(&mut staged, &mut measured)
4327 .expect("staged signing must succeed");
4328 let exact_stage_work = measured.transforms.xml_parse_work().consumed();
4329
4330 let mut constrained_policy = policy;
4331 constrained_policy.resources.max_xml_parse_work_bytes = exact_stage_work;
4332 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4333 SignContext::new(&FixedRsaSigningKey)
4334 .policy(constrained_policy)
4335 .sign_document(&mut document)
4336 .expect("commit must not parse the validated stage again");
4337
4338 assert_eq!(document.generation(), 1);
4339 assert!(!document.as_xml().contains("<ds:DigestValue/>"));
4340 assert!(!document.as_xml().contains("<ds:SignatureValue/>"));
4341 }
4342
4343 #[test]
4344 fn builder_signing_fits_the_document_to_parse_work_ratio() {
4345 let padding = "x".repeat(64 * 1024);
4351 let xml = format!("<root><payload Id=\"payload\"/><padding>{padding}</padding></root>");
4352 let builder = SignatureBuilder::new(
4353 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4354 SignatureAlgorithm::RsaSha256,
4355 )
4356 .add_reference(
4357 crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri("#payload"),
4358 );
4359 let maximum_document_bytes = xml.len() + 4 * 1024;
4360 let mut policy = crate::policy::SigningPolicy::default();
4361 policy.resources.max_xml_document_bytes = maximum_document_bytes;
4362 policy.resources.max_xml_parse_work_bytes =
4363 maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING;
4364
4365 let mut measurement_policy = policy.clone();
4366 measurement_policy.resources.max_xml_parse_work_bytes =
4367 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING;
4368 let measurement_context =
4369 SignContext::new(&FixedRsaSigningKey).policy(measurement_policy.clone());
4370 let mut measurement_budgets =
4371 SigningOperationBudgets::from_resources(&measurement_policy.resources);
4372 let mut measurement_document = XmlDocument::parse_with_settings_and_budget(
4373 xml.clone(),
4374 DocumentParseSettings::from_policy(
4375 &measurement_policy.xml,
4376 &measurement_policy.resources,
4377 ),
4378 measurement_budgets.transforms.xml_parse_work(),
4379 )
4380 .expect("measurement input must parse");
4381 measurement_context
4382 .sign_document_with_builder_in_place(
4383 &mut measurement_document,
4384 &builder,
4385 &mut measurement_budgets,
4386 )
4387 .expect("measurement signing must succeed");
4388 let consumed = measurement_budgets.transforms.xml_parse_work().consumed();
4389 assert!(
4390 consumed <= maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING,
4391 "builder signing consumed {consumed} bytes for a {maximum_document_bytes}-byte ceiling"
4392 );
4393
4394 let signed = SignContext::new(&FixedRsaSigningKey)
4395 .policy(policy.clone())
4396 .sign_with_builder(&xml, &builder)
4397 .expect("string builder signing must fit the advertised parse-work ratio");
4398 assert!(signed.contains("DigestValue>"));
4399 assert!(signed.contains("SignatureValue>"));
4400
4401 let mut owned = XmlDocument::parse(&xml).expect("fixture must parse");
4402 SignContext::new(&FixedRsaSigningKey)
4403 .policy(policy)
4404 .sign_document_with_builder(&mut owned, &builder)
4405 .expect("owned builder signing must fit the advertised parse-work ratio");
4406 assert!(owned.as_xml().contains("DigestValue>"));
4407 assert!(owned.as_xml().contains("SignatureValue>"));
4408 }
4409
4410 #[test]
4411 fn dtd_capable_staged_copy_charges_both_parser_passes() {
4412 let xml = "<root/>";
4415 let mut parsing_policy = crate::policy::SigningPolicy::default();
4416 parsing_policy.xml.allow_internal_dtd = true;
4417 let document = XmlDocument::parse_with_policy(xml, &parsing_policy)
4418 .expect("the fixture must retain DTD-capable parse settings");
4419
4420 parsing_policy.resources.max_xml_parse_work_bytes = xml.len();
4421 let budget = XmlParseWorkBudget::from_resources(&parsing_policy.resources);
4422 assert!(matches!(
4423 document.staged_copy_with_budget(DocumentParseSettings::default(), &budget),
4424 Err(XmlDocumentError::Policy(PolicyViolation::ResourceLimit {
4425 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4426 maximum,
4427 actual,
4428 })) if maximum == xml.len() && actual == xml.len() * 2
4429 ));
4430 }
4431
4432 #[test]
4433 fn owned_signing_mappers_preserve_document_size_policy_errors() {
4434 let maximum = 8;
4437 let actual = 9;
4438 assert!(matches!(
4439 map_owned_document_mutation_error(XmlDocumentError::DocumentTooLarge {
4440 maximum,
4441 actual,
4442 }),
4443 SigningError::Policy(PolicyViolation::ResourceLimit {
4444 resource: crate::policy::resource_name::XML_DOCUMENT,
4445 maximum: 8,
4446 actual: 9,
4447 })
4448 ));
4449 assert!(matches!(
4450 map_owned_document_digest_mutation_error(XmlDocumentError::DocumentTooLarge {
4451 maximum,
4452 actual,
4453 }),
4454 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
4455 resource: crate::policy::resource_name::XML_DOCUMENT,
4456 maximum: 8,
4457 actual: 9,
4458 })
4459 ));
4460 }
4461
4462 #[test]
4463 fn signing_rejects_xpath_control_dependencies_on_digest_values() {
4464 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>"##;
4468
4469 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4470 let error = fill_reference_digest_values_in_dependency_order(
4471 &mut document,
4472 TransformOptions::default(),
4473 &crate::policy::SigningPolicy::default(),
4474 crate::provider::default_provider(),
4475 &mut SigningOperationBudgets::default(),
4476 0,
4477 &[],
4478 )
4479 .expect_err("mutable XPath control dependencies must fail closed");
4480
4481 assert!(
4482 matches!(
4483 &error,
4484 SigningDigestError::InvalidStructure(message)
4485 if message.contains("dependency cycle")
4486 ),
4487 "expected a dependency-cycle rejection, got: {error:?}"
4488 );
4489 }
4490
4491 #[test]
4492 fn signing_allows_payload_local_xpath_value_predicates() {
4493 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>"##;
4496
4497 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4498 fill_reference_digest_values_in_dependency_order(
4499 &mut document,
4500 TransformOptions::default(),
4501 &crate::policy::SigningPolicy::default(),
4502 crate::provider::default_provider(),
4503 &mut SigningOperationBudgets::default(),
4504 0,
4505 &[],
4506 )
4507 .expect("payload-local XPath predicates must not depend on Signature values");
4508
4509 assert_ne!(document.as_xml(), xml);
4510 }
4511
4512 #[test]
4513 fn signing_rejects_references_that_retain_signature_value() {
4514 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>"##;
4518
4519 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4520 let error = fill_reference_digest_values_in_dependency_order(
4521 &mut document,
4522 TransformOptions::default(),
4523 &crate::policy::SigningPolicy::default(),
4524 crate::provider::default_provider(),
4525 &mut SigningOperationBudgets::default(),
4526 0,
4527 &[],
4528 )
4529 .expect_err("a mutable SignatureValue dependency must fail before signing");
4530
4531 assert!(
4532 matches!(
4533 &error,
4534 SigningDigestError::InvalidStructure(message)
4535 if message.contains("SignatureValue") && message.contains("cycle")
4536 ),
4537 "expected a SignatureValue dependency-cycle rejection, got: {error:?}"
4538 );
4539 }
4540
4541 #[test]
4542 fn second_edition_c14n_materialization_handles_both_reference_shapes() {
4543 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/2006/12/xml-c14n11"/><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="#payload"><ds:Transforms/><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
4546 let document = XmlDocument::parse(xml).expect("fixture must parse");
4547 let candidate = materialize_second_edition_c14n11_candidate(
4548 &document,
4549 0,
4550 &crate::policy::SigningPolicy::default(),
4551 false,
4552 )
4553 .expect("materialization planning must succeed")
4554 .expect("both node-set chains require explicit canonicalization");
4555
4556 assert_eq!(
4557 candidate
4558 .matches(SECOND_EDITION_GENERATION_C14N_URI)
4559 .count(),
4560 3
4561 );
4562 assert!(candidate.contains("<ds:Transforms><ds:Transform"));
4563 XmlDocument::parse(candidate).expect("materialized candidate must remain well-formed");
4564 }
4565
4566 #[test]
4567 fn second_edition_c14n_materialization_is_idempotent_for_binary_chains() {
4568 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/2006/12/xml-c14n11"/><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/2006/12/xml-c14n11"/></ds:Transforms><ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/><ds:DigestValue/></ds:Reference></ds:SignedInfo><ds:SignatureValue/></ds:Signature></root>"##;
4571 let document = XmlDocument::parse(xml).expect("fixture must parse");
4572 let candidate = materialize_second_edition_c14n11_candidate(
4573 &document,
4574 0,
4575 &crate::policy::SigningPolicy::default(),
4576 false,
4577 )
4578 .expect("materialization planning must succeed");
4579
4580 assert!(candidate.is_none());
4581 }
4582}