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