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