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