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