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 map_c14n_resource_policy_violation, parse_transforms_with_budget,
59 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 .xslt_stylesheet_semantics(self.policy.transforms.xslt_stylesheet_semantics);
2133 let external_resources = ExternalResourceContext::new(
2134 self.external_resources,
2135 self.policy.resources.max_external_resource_bytes,
2136 self.policy.resources.max_external_resource_total_bytes,
2137 );
2138 let binding = (document.identity(), document.generation());
2139 let mut operation =
2140 OperationExecutionContext::new(self.policy.clone(), &mut *budgets, Some(binding));
2141 let key_info = operation.add_node(
2142 OperationNodeKind::Key { index: 0 },
2143 OperationStage::Resolve,
2144 None,
2145 );
2146 operation.compile()?;
2147 let (key_info_content, encapsulated) = operation.run_with_budgets(key_info, |budgets| {
2148 let mechanism = document.with_view(|view| {
2149 let signature = find_signing_signature_node(
2150 view.document(),
2151 SigningSignatureTarget::Index(target_signature),
2152 )?;
2153 let children = parse_signature_children(signature)
2154 .map_err(|error| SigningDigestError::InvalidStructure(error.to_string()))?;
2155 let metadata_error = |error| match error {
2156 super::DsigError::Policy(violation) => SigningError::Policy(violation),
2157 error => SigningDigestError::InvalidStructure(error.to_string()).into(),
2158 };
2159 let resolver = external_resources.bind(
2160 view.document(),
2161 self.id_attributes,
2162 self.policy.transforms.same_document_id_semantics,
2163 );
2164 if self.encapsulation_key.is_none() {
2165 if let Some(info) = children.key_info_node
2166 && super::verify::has_signing_encapsulation_with_budget(
2167 info,
2168 &resolver,
2169 &self.policy,
2170 &budgets.transforms,
2171 self.xml_backend,
2172 )
2173 .map_err(metadata_error)?
2174 {
2175 return Err(crate::key_establishment::KeyEstablishmentError::Structure(
2176 "mechanism and explicit recipient key must be supplied together",
2177 )
2178 .into());
2179 }
2180 return Ok(None);
2181 }
2182 let metadata = children
2183 .key_info_node
2184 .map(|node| {
2185 super::verify::inspect_signing_key_info_with_budgets(
2186 node,
2187 &resolver,
2188 &self.policy,
2189 self.provider,
2190 (&budgets.transforms, &mut budgets.xpath_parse),
2191 self.xml_backend,
2192 )
2193 })
2194 .transpose()
2195 .map_err(metadata_error)?;
2196 let mechanism =
2197 metadata.and_then(|metadata| metadata.encapsulation.zip(metadata.cipher_value));
2198 if mechanism.is_some() != self.encapsulation_key.is_some() {
2199 return Err(crate::key_establishment::KeyEstablishmentError::Structure(
2200 "mechanism and explicit recipient key must be supplied together",
2201 )
2202 .into());
2203 }
2204 mechanism
2205 .map(|(algorithm, target)| {
2206 self.policy
2207 .key_establishment
2208 .check_encapsulation(algorithm)?;
2209 if self
2210 .encapsulation_key
2211 .is_some_and(|key| key.algorithm() != algorithm)
2212 {
2213 return Err(
2214 crate::key_establishment::KeyEstablishmentError::Structure(
2215 "recipient parameter set mismatch",
2216 )
2217 .into(),
2218 );
2219 }
2220 let target = view.document().get_node(target).ok_or(
2221 crate::key_establishment::KeyEstablishmentError::Structure(
2222 "missing ciphertext target",
2223 ),
2224 )?;
2225 Ok::<_, SigningError>(view.node_identity(target))
2226 })
2227 .transpose()
2228 })?;
2229 if let Some(key) = self.encapsulation_key {
2230 if self.key_info_writer.is_some() {
2231 return Err(crate::key_establishment::KeyEstablishmentError::Structure(
2232 "KeyInfo writer cannot overwrite EncapsulationMechanism",
2233 )
2234 .into());
2235 }
2236 if signing_algorithm.hmac_output_bits().is_none() {
2237 return Err(crate::key_establishment::KeyEstablishmentError::Structure(
2238 "encapsulation requires HMAC",
2239 )
2240 .into());
2241 }
2242 self.policy.hmac.validate_key_bits(256)?;
2243 let target = mechanism.expect("recipient requires mechanism");
2244 let encoded_len = key.algorithm().ciphertext_len().div_ceil(3) * 4;
2245 self.policy.resources.validate_xml_document_len(
2248 document.projected_content_replacement_len(target, encoded_len)?,
2249 )?;
2250 budgets.key_establishment.commit_all(
2251 &self.policy.key_establishment,
2252 0,
2253 0,
2254 32 + encoded_len as u128,
2255 )?;
2256 let result = budgets.key_establishment.encapsulate(
2257 &self.policy.key_establishment,
2258 self.provider,
2259 key,
2260 )?;
2261 return Ok((None, Some((target, result))));
2262 }
2263 let Some(writer) = self.key_info_writer else {
2264 return Ok::<_, SigningError>((None, None));
2265 };
2266 let certificates = budgets.certificates.get_or_insert_with(|| {
2267 super::pkix::CertificateSession::new(&self.policy.resources)
2268 });
2269 let mut parsing = super::parse::KeyInfoParsingSession::with_certificate_account(
2270 &self.policy.resources,
2271 certificates,
2272 )
2273 .map_err(SigningError::from)?;
2274 let key_info_content = writer
2275 .write_key_info_in_session(
2276 self.signing_key.expect("ordinary signing request"),
2277 self.provider,
2278 &mut parsing,
2279 )
2280 .map_err(|error| match error {
2281 KeyInfoWriteError::Policy(error) => SigningError::Policy(error),
2282 error => SigningError::KeyInfo(error),
2283 })?;
2284 self.policy
2287 .resources
2288 .validate_xml_document_len(key_info_content.len())?;
2289 Ok((Some(key_info_content), None))
2290 })?;
2291 let established_key = if let Some((_, result)) = encapsulated.as_ref() {
2292 Some(HmacSigningKey::new(result.shared_secret.to_vec())?)
2293 } else {
2294 None
2295 };
2296 let signing_key: &dyn SigningKey = match (&established_key, self.signing_key) {
2297 (Some(key), _) => key,
2298 (None, Some(key)) => key,
2299 _ => {
2300 return Err(crate::key_establishment::KeyEstablishmentError::Structure(
2301 "missing signing key",
2302 )
2303 .into());
2304 }
2305 };
2306 operation.extend();
2307 let kem_mutation = if let Some((target, result)) = encapsulated {
2308 let mutation =
2309 operation.add_node(OperationNodeKind::Mutation, OperationStage::Resolve, None);
2310 operation.add_dependency(mutation, key_info)?;
2311 operation.compile()?;
2312 operation.run_document_transition(mutation, document, |document, budgets| {
2313 let encoded = base64::engine::general_purpose::STANDARD.encode(&result.ciphertext);
2314 document
2315 .replace_base64_contents_with_budget(
2316 &[(target, encoded)],
2317 self.document_parse_settings(),
2318 budgets.transforms.xml_parse_work(),
2319 )
2320 .map_err(map_owned_document_mutation_error)
2321 })?;
2322 operation.extend();
2323 Some(mutation)
2324 } else {
2325 None
2326 };
2327 let c14n_candidate = materialize_second_edition_c14n11_candidate(
2331 document,
2332 target_signature,
2333 &self.policy,
2334 external_resources.is_configured(),
2335 )?;
2336 let normalization = if let Some(candidate) = c14n_candidate {
2337 let mutation =
2338 operation.add_node(OperationNodeKind::Mutation, OperationStage::Resolve, None);
2339 operation.add_dependency(mutation, kem_mutation.unwrap_or(key_info))?;
2340 operation.compile()?;
2341 operation.run_document_transition(mutation, document, |document, budgets| {
2342 document
2343 .replace_serialized_with_settings(
2344 candidate,
2345 self.document_parse_settings(),
2346 Some(budgets.transforms.xml_parse_work()),
2347 )
2348 .map_err(map_owned_document_mutation_error)
2349 })?;
2350 operation.extend();
2351 Some(mutation)
2352 } else {
2353 None
2354 };
2355 let setup_gate = normalization.or(kem_mutation).unwrap_or(key_info);
2356 let setup_gate = if let Some(key_info_content) = key_info_content {
2357 let mutation =
2358 operation.add_node(OperationNodeKind::Mutation, OperationStage::Resolve, None);
2359 operation.add_dependency(mutation, setup_gate)?;
2360 operation.compile()?;
2361 operation.run_document_transition(mutation, document, |document, budgets| {
2362 let populated = merge_key_info_source_at_index_with_budget(
2365 document.as_xml(),
2366 &key_info_content,
2367 target_signature,
2368 Some(&self.policy),
2369 Some(budgets.transforms.xml_parse_work()),
2370 )?;
2371 self.policy
2372 .resources
2373 .validate_xml_document_len(populated.len())?;
2374 document
2375 .replace_serialized_with_settings(
2376 populated,
2377 self.document_parse_settings(),
2378 Some(budgets.transforms.xml_parse_work()),
2379 )
2380 .map_err(map_owned_document_mutation_error)
2381 })?;
2382 operation.extend();
2383 mutation
2384 } else {
2385 setup_gate
2386 };
2387 let plan_nodes = fill_reference_digest_values_in_dependency_order_with_operation(
2388 document,
2389 transform_options,
2390 self.provider,
2391 &mut operation,
2392 SigningReferenceRequest {
2393 target_signature,
2394 id_attributes: self.id_attributes,
2395 external_resources: &external_resources,
2396 },
2397 Some(setup_gate),
2398 )?;
2399 self.policy
2400 .resources
2401 .validate_xml_document_len(document.as_xml().len())?;
2402 let (algorithm, hmac_output_length_bits, canonical_signed_info, signature_context) =
2403 operation.run_with_budgets(plan_nodes.canonicalization, |budgets| {
2404 let result =
2405 canonicalize_signed_info(document, &self.policy, budgets, target_signature)?;
2406 budgets
2407 .transforms
2408 .charge_c14n_output(result.2.len())
2409 .map_err(SigningDigestError::Transform)?;
2410 Ok::<_, SigningError>(result)
2411 })?;
2412 self.policy.check_signature_algorithm(algorithm)?;
2413 let expected_signature_len = expected_signature_output_len(
2414 signing_key,
2415 algorithm,
2416 &self.policy,
2417 hmac_output_length_bits,
2418 )?;
2419 let projected_signature_len = projected_signature_output_len(
2420 algorithm,
2421 expected_signature_len,
2422 self.policy.ecdsa_signature_value_encoding,
2423 )?;
2424 let encoded_signature_len =
2425 padded_base64_len_for_xml(projected_signature_len, &self.policy)?;
2426 let signature_value_node = document.with_view(|view| {
2427 let signature = find_signing_signature_node(
2428 view.document(),
2429 SigningSignatureTarget::Index(target_signature),
2430 )?;
2431 let signature_value = find_required_child(signature, "SignatureValue")?;
2432 Ok::<_, SigningError>(view.node_identity(signature_value))
2433 })?;
2434 let projected_document_len = document
2435 .projected_content_replacement_len(signature_value_node, encoded_signature_len)?;
2436 self.policy
2437 .resources
2438 .validate_xml_document_len(projected_document_len)?;
2439 let signature_value = operation.run(plan_nodes.crypto, || {
2440 self.provider
2441 .require_capability(crate::provider::ProviderCapability::Sign(algorithm))
2442 .map_err(SigningKeyError::from)?;
2443 let mut signature_value = self.provider.sign_with_context(
2444 signing_key,
2445 algorithm,
2446 &signature_context,
2447 &canonical_signed_info,
2448 )?;
2449 if algorithm.hmac_output_bits().is_some() {
2450 signature_value.truncate(expected_signature_len);
2451 }
2452 validate_signature_output(expected_signature_len, &signature_value)?;
2453 Ok::<_, SigningError>(signature_value)
2454 })?;
2455 let signature_b64 = operation.run(plan_nodes.evidence, || {
2456 let signature_value = encode_signature_output(
2457 algorithm,
2458 signature_value,
2459 self.policy.ecdsa_signature_value_encoding,
2460 )?;
2461 Ok::<_, SigningError>(base64::engine::general_purpose::STANDARD.encode(signature_value))
2462 })?;
2463 operation.run_document_transition(plan_nodes.mutation, document, |document, budgets| {
2464 document
2465 .replace_base64_contents_with_budget(
2466 &[(signature_value_node, signature_b64)],
2467 self.document_parse_settings(),
2468 budgets.transforms.xml_parse_work(),
2469 )
2470 .map_err(map_owned_document_mutation_error)
2471 })?;
2472 self.policy
2473 .resources
2474 .validate_xml_document_len(document.as_xml().len())?;
2475 Ok(())
2476 }
2477
2478 pub fn sign_with_builder(
2481 &self,
2482 xml: &str,
2483 builder: &SignatureBuilder,
2484 ) -> Result<String, SigningError> {
2485 self.policy.validate()?;
2486 self.policy.resources.validate_xml_document_len(xml.len())?;
2487 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
2488 &self.policy.resources,
2489 self.xml_backend,
2490 );
2491 let mut document = XmlDocument::parse_with_settings_and_budget(
2492 xml.to_owned(),
2493 self.document_parse_settings(),
2494 budgets.transforms.xml_parse_work(),
2495 )
2496 .map_err(|error| match owned_document_policy_violation(error) {
2497 Ok(error) => SigningError::Policy(error),
2498 Err(XmlDocumentError::Parse(error)) => {
2499 SigningError::XmlMutation(XmlMutationError::XmlParse(error))
2500 }
2501 Err(error) => SigningError::Document(error),
2502 })?;
2503 self.validate_owned_document_input(&document)?;
2504 self.sign_document_with_builder_in_place(&mut document, builder, &mut budgets)?;
2505 Ok(document.into_xml())
2506 }
2507
2508 pub fn sign_document_with_builder(
2510 &self,
2511 document: &mut XmlDocument,
2512 builder: &SignatureBuilder,
2513 ) -> Result<(), SigningError> {
2514 self.validate_owned_document_input(document)?;
2515 let mut budgets = SigningOperationBudgets::from_resources_with_backend(
2516 &self.policy.resources,
2517 self.xml_backend,
2518 );
2519 let mut staged = document
2520 .staged_copy_with_budget(
2521 self.document_parse_settings(),
2522 budgets.transforms.xml_parse_work(),
2523 )
2524 .map_err(map_owned_document_mutation_error)?;
2525 self.sign_document_with_builder_in_place(&mut staged, builder, &mut budgets)?;
2526 commit_signed_staged(document, staged, &self.policy)
2527 }
2528
2529 fn sign_document_with_builder_in_place(
2530 &self,
2531 document: &mut XmlDocument,
2532 builder: &SignatureBuilder,
2533 budgets: &mut SigningOperationBudgets,
2534 ) -> Result<(), SigningError> {
2535 self.policy.resources.validate_key_candidates(1)?;
2536 let expected_signature_len = if let Some(key) = self.signing_key {
2537 expected_signature_output_len(key, builder.signature_method(), &self.policy, None)?
2538 } else {
2539 self.policy.hmac.validate_key_bits(256)?;
2540 builder.signature_method().hmac_output_bits().ok_or(
2541 crate::key_establishment::KeyEstablishmentError::Structure(
2542 "encapsulation requires HMAC",
2543 ),
2544 )? / 8
2545 };
2546 let template = builder.build_template_with_policy_for_signature_output(
2547 &self.policy,
2548 expected_signature_len,
2549 self.encapsulation_key
2550 .map(crate::provider::KeyEncapsulationKey::algorithm),
2551 &budgets.transforms,
2552 &mut budgets.xpath_parse,
2553 )?;
2554 let signature_parent = if let Some(id) = self.start_node_id {
2555 document.with_view(|view| {
2556 let start = signing_start_node(view.document(), id, self.id_attributes)?;
2557 Ok::<_, SigningError>(view.node_identity(start))
2558 })?
2559 } else {
2560 document.with_view(|view| view.root_element())
2561 };
2562 let projected_document_len =
2563 document.projected_child_append_len(signature_parent, template.len())?;
2564 self.policy
2565 .resources
2566 .validate_xml_document_len(projected_document_len)?;
2567 let binding = (document.identity(), document.generation());
2568 let mut operation =
2569 OperationExecutionContext::new(self.policy.clone(), &mut *budgets, Some(binding));
2570 let mutation =
2571 operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
2572 operation.compile()?;
2573 operation.run_document_transition(mutation, document, |document, budgets| {
2574 document
2575 .append_generated_child_with_budget(
2576 signature_parent,
2577 &template,
2578 self.document_parse_settings(),
2579 budgets.transforms.xml_parse_work(),
2580 )
2581 .map_err(map_owned_document_mutation_error)
2582 })?;
2583 drop(operation);
2584 self.policy
2585 .resources
2586 .validate_xml_document_len(document.as_xml().len())?;
2587 let target_signature = document.with_view(|view| {
2588 let parent = if let Some(id) = self.start_node_id {
2589 signing_start_node(view.document(), id, self.id_attributes)?
2590 } else {
2591 view.document().root_element()
2592 };
2593 let appended = parent
2594 .children()
2595 .rfind(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
2596 .ok_or(SigningDigestError::MissingElement {
2597 element: "Signature",
2598 })?;
2599 signature_index(view.document(), appended).map_err(SigningError::from)
2600 })?;
2601 self.sign_template_at_index_with_budgets(document, target_signature, budgets)?;
2602 Ok(())
2603 }
2604}
2605
2606fn projected_signature_output_len(
2607 algorithm: SignatureAlgorithm,
2608 raw_signature_len: usize,
2609 encoding: crate::policy::EcdsaSignatureValueEncoding,
2610) -> Result<usize, SigningError> {
2611 if algorithm.ecdsa_digest().is_some()
2612 && encoding == crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
2613 {
2614 return maximum_ecdsa_der_signature_len(raw_signature_len)
2615 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
2616 }
2617 Ok(raw_signature_len)
2618}
2619
2620fn map_owned_document_mutation_error(error: XmlDocumentError) -> SigningError {
2621 match owned_document_policy_violation(error) {
2622 Ok(error) => SigningError::Policy(error),
2623 Err(error) => SigningError::Document(error),
2624 }
2625}
2626
2627fn map_owned_document_digest_mutation_error(error: XmlDocumentError) -> SigningDigestError {
2628 match owned_document_policy_violation(error) {
2629 Ok(error) => SigningDigestError::Policy(error),
2630 Err(error) => SigningDigestError::Document(error),
2631 }
2632}
2633
2634fn map_signing_external_resource_map_error(error: ExternalResourceMapError) -> SigningError {
2635 match error {
2636 ExternalResourceMapError::Policy(error) => SigningError::Policy(error),
2637 ExternalResourceMapError::TotalLengthOverflow => {
2638 SigningDigestError::InvalidStructure("external resource total length overflow".into())
2639 .into()
2640 }
2641 }
2642}
2643
2644fn owned_document_policy_violation(
2645 error: XmlDocumentError,
2646) -> Result<crate::policy::PolicyViolation, XmlDocumentError> {
2647 match error {
2648 XmlDocumentError::Policy(error) => Ok(error),
2649 XmlDocumentError::Parse(crate::xml::dom::ParseError::NamespaceBindingLimitReached {
2650 maximum,
2651 actual,
2652 }) => Ok(crate::policy::PolicyViolation::ResourceLimit {
2653 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
2654 maximum,
2655 actual,
2656 }),
2657 XmlDocumentError::DocumentTooLarge { maximum, actual } => {
2658 Ok(crate::policy::PolicyViolation::ResourceLimit {
2659 resource: crate::policy::resource_name::XML_DOCUMENT,
2660 maximum,
2661 actual,
2662 })
2663 }
2664 XmlDocumentError::DocumentTooDeep { maximum, actual } => {
2665 Ok(crate::policy::PolicyViolation::ResourceLimit {
2666 resource: crate::policy::resource_name::XML_DEPTH,
2667 maximum,
2668 actual,
2669 })
2670 }
2671 XmlDocumentError::ProjectedNodeLimit { maximum } => {
2672 Ok(crate::policy::PolicyViolation::ResourceLimit {
2673 resource: crate::policy::resource_name::XML_NODES,
2674 maximum,
2675 actual: maximum.saturating_add(1),
2676 })
2677 }
2678 error => Err(error),
2679 }
2680}
2681
2682fn encode_signature_output(
2683 algorithm: SignatureAlgorithm,
2684 signature: Vec<u8>,
2685 encoding: crate::policy::EcdsaSignatureValueEncoding,
2686) -> Result<Vec<u8>, SigningError> {
2687 if algorithm.ecdsa_digest().is_some()
2688 && encoding == crate::policy::EcdsaSignatureValueEncoding::XmlSecAsn1Der
2689 {
2690 return encode_ecdsa_signature_as_der(&signature)
2691 .ok_or(SigningKeyError::InvalidPublicKeyInfo.into());
2692 }
2693 Ok(signature)
2694}
2695
2696#[derive(Debug, Clone)]
2697struct SigningReference {
2698 uri: String,
2699 origin_node_id: NodeId,
2700 transforms: Vec<Transform>,
2701 digest_method: DigestAlgorithm,
2702 digest_value_range: Range<usize>,
2703 digest_value_node_id: NodeId,
2704}
2705
2706struct SigningOperationBudgets {
2707 certificates: Option<super::pkix::CertificateSession>,
2708 key_establishment: crate::key_establishment::KeyEstablishmentUsage,
2709 transforms: TransformExecutionBudget,
2710 xpath_parse: XPathSignatureParseBudget,
2711}
2712
2713struct SigningReferenceRequest<'a, 'resources> {
2714 target_signature: usize,
2715 id_attributes: &'a [crate::IdAttributeRegistration],
2716 external_resources: &'a ExternalResourceContext<'resources>,
2717}
2718
2719struct SigningPlanNodes {
2720 canonicalization: OperationNodeId,
2721 crypto: OperationNodeId,
2722 evidence: OperationNodeId,
2723 mutation: OperationNodeId,
2724}
2725
2726impl SigningOperationBudgets {
2727 fn from_resources(resources: &crate::policy::ResourcePolicy) -> Self {
2728 let transforms = TransformExecutionBudget::from_resources(resources);
2729 let xpath_parse = XPathSignatureParseBudget::for_execution(resources, &transforms);
2730 Self {
2731 certificates: None,
2732 key_establishment: Default::default(),
2733 transforms,
2734 xpath_parse,
2735 }
2736 }
2737
2738 fn from_resources_with_backend(
2739 resources: &crate::policy::ResourcePolicy,
2740 backend: crate::XmlBackend,
2741 ) -> Self {
2742 let transforms =
2743 TransformExecutionBudget::from_resources(resources).with_xml_backend(backend);
2744 let xpath_parse = XPathSignatureParseBudget::for_execution(resources, &transforms);
2745 Self {
2746 certificates: None,
2747 key_establishment: Default::default(),
2748 transforms,
2749 xpath_parse,
2750 }
2751 }
2752}
2753
2754impl Default for SigningOperationBudgets {
2755 fn default() -> Self {
2756 Self::from_resources(&crate::policy::ResourcePolicy::default())
2757 }
2758}
2759
2760pub fn compute_reference_digest_values(
2767 xml: &str,
2768) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2769 let execution_budget = TransformExecutionBudget::default();
2770 compute_reference_digest_values_with_options(
2771 xml,
2772 TransformOptions::default(),
2773 None,
2774 crate::provider::default_provider(),
2775 &execution_budget,
2776 None,
2777 &[],
2778 )
2779}
2780
2781fn compute_reference_digest_values_with_options(
2782 xml: &str,
2783 transform_options: TransformOptions<'_>,
2784 policy: Option<&crate::policy::SigningPolicy>,
2785 provider: &dyn crate::provider::CryptoProvider,
2786 execution_budget: &TransformExecutionBudget,
2787 target_signature: Option<usize>,
2788 id_attributes: &[crate::IdAttributeRegistration],
2789) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2790 let (prepared, target_signature) =
2791 prepare_reference_digest_input(xml, policy, execution_budget, target_signature)?;
2792 compute_prepared_reference_digest_values_with_options(
2793 prepared.as_ref(),
2794 transform_options,
2795 policy,
2796 provider,
2797 execution_budget,
2798 target_signature,
2799 id_attributes,
2800 )
2801}
2802
2803fn compute_prepared_reference_digest_values_with_options(
2804 xml: &str,
2805 transform_options: TransformOptions<'_>,
2806 policy: Option<&crate::policy::SigningPolicy>,
2807 provider: &dyn crate::provider::CryptoProvider,
2808 execution_budget: &TransformExecutionBudget,
2809 target_signature: usize,
2810 id_attributes: &[crate::IdAttributeRegistration],
2811) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
2812 let resource_policy = policy
2813 .map(|policy| &policy.resources)
2814 .cloned()
2815 .unwrap_or_default();
2816 let external_resources = ExternalResourceContext::new(
2817 None,
2818 resource_policy.max_external_resource_bytes,
2819 resource_policy.max_external_resource_total_bytes,
2820 );
2821 let doc = parse_signing_document(
2822 xml,
2823 policy,
2824 execution_budget.xml_parse_work(),
2825 crate::XmlBackend::default(),
2826 )?;
2827 let signature =
2828 find_signing_signature_node(&doc, SigningSignatureTarget::Index(target_signature))?;
2829 let signed_info = find_required_child(signature, "SignedInfo")?;
2830 let references = parse_signing_references(signed_info)?;
2831 validate_signing_references(&references, references.len(), policy, false)?;
2832 compute_signing_reference_digests(
2833 &doc,
2834 signature,
2835 references,
2836 transform_options,
2837 provider,
2838 execution_budget,
2839 SigningUriResolution {
2840 id_attributes,
2841 same_document_id_semantics: policy.map_or(
2842 crate::policy::SameDocumentIdSemantics::Specification,
2843 |policy| policy.transforms.same_document_id_semantics,
2844 ),
2845 external_resources: &external_resources,
2846 },
2847 )
2848}
2849
2850fn prepare_reference_digest_input<'a>(
2851 xml: &'a str,
2852 policy: Option<&crate::policy::SigningPolicy>,
2853 execution_budget: &TransformExecutionBudget,
2854 target_signature: Option<usize>,
2855) -> Result<(Cow<'a, str>, usize), SigningDigestError> {
2856 let default_policy = crate::policy::SigningPolicy::default();
2857 let effective_policy = policy.unwrap_or(&default_policy);
2858 let settings =
2859 DocumentParseSettings::from_policy(&effective_policy.xml, &effective_policy.resources);
2860 let document = XmlDocument::parse_with_settings_and_budget(
2861 xml.to_owned(),
2862 settings,
2863 execution_budget.xml_parse_work(),
2864 )
2865 .map_err(map_owned_document_digest_mutation_error)?;
2866 let target_signature = if let Some(target_signature) = target_signature {
2867 target_signature
2868 } else {
2869 document.with_view(|view| {
2870 let selected =
2871 find_signing_signature_node(view.document(), SigningSignatureTarget::Last)?;
2872 signature_index(view.document(), selected)
2873 })?
2874 };
2875 document.with_view(|view| {
2878 let signature = find_signing_signature_node(
2879 view.document(),
2880 SigningSignatureTarget::Index(target_signature),
2881 )?;
2882 let signed_info = find_required_child(signature, "SignedInfo")?;
2883 let references = parse_signing_references(signed_info)?;
2884 validate_signing_references(&references, references.len(), policy, false)
2885 })?;
2886 let materialized = materialize_second_edition_c14n11_candidate(
2887 &document,
2888 target_signature,
2889 effective_policy,
2890 false,
2891 )?;
2892 Ok((
2893 materialized.map_or(Cow::Borrowed(xml), Cow::Owned),
2894 target_signature,
2895 ))
2896}
2897
2898fn fill_reference_digest_values_in_dependency_order_with_operation(
2899 document: &mut XmlDocument,
2900 transform_options: TransformOptions<'_>,
2901 provider: &dyn crate::provider::CryptoProvider,
2902 operation: &mut OperationExecutionContext<
2903 crate::policy::SigningPolicy,
2904 &mut SigningOperationBudgets,
2905 >,
2906 request: SigningReferenceRequest<'_, '_>,
2907 setup_gate: Option<OperationNodeId>,
2908) -> Result<SigningPlanNodes, SigningDigestError> {
2909 let policy = operation.policy().clone();
2910 let reference_limit = policy
2911 .resources
2912 .max_references
2913 .min(MAX_REFERENCES_PER_SIGNATURE);
2914 let process_manifests =
2915 policy.manifest_processing == crate::policy::ManifestProcessing::Process;
2916 let (signed_info_references, manifest_references) = {
2917 let budgets = operation.budgets_mut();
2918 document.with_view(|view| {
2919 let signature = find_signing_signature_node(
2920 view.document(),
2921 SigningSignatureTarget::Index(request.target_signature),
2922 )?;
2923 let signed_info = find_required_child(signature, "SignedInfo")?;
2924 let signed_info_references =
2925 parse_signing_references_with_budget(signed_info, &mut budgets.xpath_parse)?;
2926 validate_signing_references(
2927 &signed_info_references,
2928 signed_info_references.len(),
2929 Some(&policy),
2930 request.external_resources.is_configured(),
2931 )?;
2932 let manifest_references = if process_manifests {
2933 parse_signing_manifest_references(
2934 signature,
2935 &mut budgets.xpath_parse,
2936 reference_limit.saturating_sub(signed_info_references.len()),
2937 reference_limit,
2938 )?
2939 } else {
2940 Vec::new()
2941 };
2942 Ok::<_, SigningDigestError>((signed_info_references, manifest_references))
2943 })?
2944 };
2945 let total_references = signed_info_references
2946 .len()
2947 .checked_add(manifest_references.len())
2948 .ok_or_else(|| SigningDigestError::InvalidStructure("reference count overflow".into()))?;
2949 validate_signing_references(
2950 &manifest_references,
2951 total_references,
2952 Some(&policy),
2953 request.external_resources.is_configured(),
2954 )?;
2955 let placeholder = "AA==";
2956 let analysis_replacements = document.with_view(|view| {
2960 let signature = find_signing_signature_node(
2961 view.document(),
2962 SigningSignatureTarget::Index(request.target_signature),
2963 )?;
2964 let signature_value = find_required_child(signature, "SignatureValue")?;
2965 let mut replacements = signed_info_references
2966 .iter()
2967 .chain(&manifest_references)
2968 .map(|reference| {
2969 (
2970 view.node_identity_by_id(reference.digest_value_node_id),
2971 placeholder.to_owned(),
2972 )
2973 })
2974 .collect::<Vec<_>>();
2975 replacements.push((view.node_identity(signature_value), placeholder.to_owned()));
2976 Ok::<_, SigningDigestError>(replacements)
2977 })?;
2978 let analysis_xml = document
2979 .project_base64_contents(
2980 &analysis_replacements,
2981 policy.resources.max_xml_document_bytes,
2982 )
2983 .map_err(map_owned_document_digest_mutation_error)?;
2984 let analysis_doc = parse_signing_document(
2985 &analysis_xml,
2986 Some(&policy),
2987 operation.budgets().transforms.xml_parse_work(),
2988 document.xml_backend(),
2989 )?;
2990 let analysis_signature = find_signing_signature_node(
2991 &analysis_doc,
2992 SigningSignatureTarget::Index(request.target_signature),
2993 )?;
2994 let analysis_signed_info = find_required_child(analysis_signature, "SignedInfo")?;
2995 let mut analysis_references = parse_signing_references_with_budget(
2996 analysis_signed_info,
2997 &mut operation.budgets_mut().xpath_parse,
2998 )?;
2999 if process_manifests {
3000 analysis_references.extend(parse_signing_manifest_references(
3001 analysis_signature,
3002 &mut operation.budgets_mut().xpath_parse,
3003 reference_limit.saturating_sub(signed_info_references.len()),
3004 reference_limit,
3005 )?);
3006 }
3007 let dependency_plan = reference_dependency_levels(
3008 &analysis_doc,
3009 analysis_signature,
3010 &analysis_references,
3011 transform_options,
3012 &operation.budgets().transforms,
3013 SigningUriResolution {
3014 id_attributes: request.id_attributes,
3015 same_document_id_semantics: policy.transforms.same_document_id_semantics,
3016 external_resources: request.external_resources,
3017 },
3018 )?;
3019 let (node_levels, plan_nodes) =
3020 compile_signing_operation_plan(operation, &dependency_plan, setup_gate)?;
3021 for (level, node_level) in dependency_plan.levels.into_iter().zip(node_levels) {
3022 operation.run_document_transition_batch_with_budgets(
3023 &node_level,
3024 document,
3025 |document, budgets| {
3026 let replacements = document.with_view(|view| {
3027 let current_doc = view.document();
3028 let current_signature = find_signing_signature_node(
3029 current_doc,
3030 SigningSignatureTarget::Index(request.target_signature),
3031 )?;
3032 let current_signed_info = find_required_child(current_signature, "SignedInfo")?;
3033 let current_signed_info_references = parse_signing_references_with_budget(
3034 current_signed_info,
3035 &mut budgets.xpath_parse,
3036 )?;
3037 let current_manifest_references = if process_manifests {
3038 parse_signing_manifest_references(
3039 current_signature,
3040 &mut budgets.xpath_parse,
3041 reference_limit.saturating_sub(signed_info_references.len()),
3042 reference_limit,
3043 )?
3044 } else {
3045 Vec::new()
3046 };
3047 if current_signed_info_references.len() != signed_info_references.len()
3048 || current_manifest_references.len() != manifest_references.len()
3049 {
3050 return Err(SigningDigestError::InvalidStructure(
3051 "signing Reference set changed while filling digests".into(),
3052 ));
3053 }
3054 let mut destinations = Vec::with_capacity(level.len());
3055 let mut level_references = Vec::with_capacity(level.len());
3056 for index in &level {
3057 let reference = if *index < signed_info_references.len() {
3058 current_signed_info_references.get(*index)
3059 } else {
3060 current_manifest_references.get(*index - signed_info_references.len())
3061 }
3062 .ok_or_else(|| {
3063 SigningDigestError::InvalidStructure(
3064 "signing Reference set changed while filling digests".into(),
3065 )
3066 })?;
3067 destinations.push(view.node_identity_by_id(reference.digest_value_node_id));
3068 level_references.push(reference.clone());
3069 }
3070 let computed = compute_signing_reference_digests(
3071 current_doc,
3072 current_signature,
3073 level_references,
3074 transform_options,
3075 provider,
3076 &budgets.transforms,
3077 SigningUriResolution {
3078 id_attributes: request.id_attributes,
3079 same_document_id_semantics: policy
3080 .transforms
3081 .same_document_id_semantics,
3082 external_resources: request.external_resources,
3083 },
3084 )?;
3085 if computed.len() != destinations.len() {
3086 return Err(SigningDigestError::InvalidStructure(
3087 "signing Reference set changed while computing digests".into(),
3088 ));
3089 }
3090 Ok::<_, SigningDigestError>(
3091 destinations
3092 .into_iter()
3093 .zip(computed)
3094 .map(|(target, digest)| (target, digest.digest_value))
3095 .collect::<Vec<_>>(),
3096 )
3097 })?;
3098 document
3099 .replace_base64_contents_with_budget(
3100 &replacements,
3101 DocumentParseSettings::from_policy(&policy.xml, &policy.resources)
3102 .with_backend(document.xml_backend()),
3103 budgets.transforms.xml_parse_work(),
3104 )
3105 .map_err(map_owned_document_digest_mutation_error)
3106 },
3107 )?;
3108 }
3109 Ok(plan_nodes)
3110}
3111
3112#[cfg(test)]
3113fn fill_reference_digest_values_in_dependency_order(
3114 document: &mut XmlDocument,
3115 transform_options: TransformOptions<'_>,
3116 policy: &crate::policy::SigningPolicy,
3117 provider: &dyn crate::provider::CryptoProvider,
3118 budgets: &mut SigningOperationBudgets,
3119 target_signature: usize,
3120 id_attributes: &[crate::IdAttributeRegistration],
3121) -> Result<(), SigningDigestError> {
3122 let external_resources = ExternalResourceContext::new(
3123 None,
3124 policy.resources.max_external_resource_bytes,
3125 policy.resources.max_external_resource_total_bytes,
3126 );
3127 let binding = (document.identity(), document.generation());
3128 let mut operation = OperationExecutionContext::new(policy.clone(), budgets, Some(binding));
3129 fill_reference_digest_values_in_dependency_order_with_operation(
3130 document,
3131 transform_options,
3132 provider,
3133 &mut operation,
3134 SigningReferenceRequest {
3135 target_signature,
3136 id_attributes,
3137 external_resources: &external_resources,
3138 },
3139 None,
3140 )
3141 .map(|_| ())
3142}
3143
3144struct SigningDependencyPlan {
3145 dependencies: Vec<HashSet<usize>>,
3146 levels: Vec<Vec<usize>>,
3147}
3148
3149fn reference_dependency_levels(
3150 doc: &Document<'_>,
3151 signature: Node<'_, '_>,
3152 references: &[SigningReference],
3153 transform_options: TransformOptions<'_>,
3154 execution_budget: &TransformExecutionBudget,
3155 uri_resolution: SigningUriResolution<'_, '_>,
3156) -> Result<SigningDependencyPlan, SigningDigestError> {
3157 let resolver = uri_resolution.external_resources.bind(
3158 doc,
3159 uri_resolution.id_attributes,
3160 uri_resolution.same_document_id_semantics,
3161 );
3162 let terminal_signature_value_index = references.len();
3163 let signature_value = find_required_child(signature, "SignatureValue")?;
3164 let mut tracked_mutable_nodes = references
3165 .iter()
3166 .enumerate()
3167 .flat_map(|(index, reference)| {
3168 std::iter::once((index, reference.digest_value_node_id)).chain(
3169 doc.get_node(reference.digest_value_node_id)
3170 .into_iter()
3171 .flat_map(|node| node.children())
3172 .filter(|node| node.is_text())
3173 .map(move |node| (index, node.id())),
3174 )
3175 })
3176 .collect::<Vec<_>>();
3177 tracked_mutable_nodes.push((terminal_signature_value_index, signature_value.id()));
3178 tracked_mutable_nodes.extend(
3179 signature_value
3180 .children()
3181 .filter(|node| node.is_text())
3182 .map(|node| (terminal_signature_value_index, node.id())),
3183 );
3184 let analyses = references
3185 .iter()
3186 .map(|reference| {
3187 let origin = signing_reference_origin(doc, reference)?;
3188 let initial_data = resolver.dereference_from_with_budget(
3189 &reference.uri,
3190 origin,
3191 execution_budget.node_set_materialization(),
3192 execution_budget.xml_base_resolution(),
3193 )?;
3194 let output = execute_transforms_with_dependency_nodes(
3195 signature,
3196 initial_data,
3197 &reference.transforms,
3198 transform_options,
3199 execution_budget,
3200 tracked_mutable_nodes.clone(),
3201 )?;
3202 Ok(output.dependencies)
3203 })
3204 .collect::<Result<Vec<_>, SigningDigestError>>()?;
3205 if analyses
3206 .iter()
3207 .any(|dependencies| dependencies.contains(&terminal_signature_value_index))
3208 {
3209 return Err(SigningDigestError::InvalidStructure(
3210 "Reference dependency cycle includes the mutable SignatureValue".into(),
3211 ));
3212 }
3213 let mut remaining_dependencies = analyses.clone();
3214 let mut completed = vec![false; references.len()];
3215 let mut levels = Vec::new();
3216 while completed.iter().any(|done| !done) {
3217 let ready = remaining_dependencies
3218 .iter()
3219 .enumerate()
3220 .filter_map(|(index, dependencies)| {
3221 (!completed[index] && dependencies.is_empty()).then_some(index)
3222 })
3223 .collect::<Vec<_>>();
3224 if ready.is_empty() {
3225 return Err(SigningDigestError::InvalidStructure(
3226 "Manifest Reference digest dependency cycle".into(),
3227 ));
3228 }
3229 for index in &ready {
3230 completed[*index] = true;
3231 }
3232 for dependency_set in &mut remaining_dependencies {
3233 dependency_set.retain(|dependency| !completed[*dependency]);
3234 }
3235 levels.push(ready);
3236 }
3237
3238 debug_assert!(references.iter().all(|reference| {
3242 signature.range().start <= reference.digest_value_range.start
3243 && signature.range().end >= reference.digest_value_range.end
3244 }));
3245 Ok(SigningDependencyPlan {
3246 dependencies: analyses,
3247 levels,
3248 })
3249}
3250
3251fn signing_reference_origin<'doc, 'input>(
3252 doc: &'doc Document<'input>,
3253 reference: &SigningReference,
3254) -> Result<Node<'doc, 'input>, SigningDigestError> {
3255 doc.get_node(reference.origin_node_id).ok_or_else(|| {
3256 SigningDigestError::InvalidStructure(
3257 "signing Reference origin changed while computing digests".into(),
3258 )
3259 })
3260}
3261
3262fn compile_signing_operation_plan(
3263 operation: &mut OperationExecutionContext<
3264 crate::policy::SigningPolicy,
3265 &mut SigningOperationBudgets,
3266 >,
3267 dependency_plan: &SigningDependencyPlan,
3268 setup_gate: Option<OperationNodeId>,
3269) -> Result<(Vec<Vec<OperationNodeId>>, SigningPlanNodes), SigningDigestError> {
3270 let reference_count = dependency_plan.levels.iter().map(Vec::len).sum::<usize>();
3271 let mut digest_nodes = vec![None; reference_count];
3272 for (index, digest_slot) in digest_nodes.iter_mut().enumerate() {
3273 let digest_node = operation.add_node(
3274 OperationNodeKind::Digest { index },
3275 OperationStage::Digest,
3276 None,
3277 );
3278 if let Some(setup_gate) = setup_gate {
3279 operation
3280 .add_dependency(digest_node, setup_gate)
3281 .map_err(map_signing_digest_plan_error)?;
3282 }
3283 *digest_slot = Some(digest_node);
3284 }
3285
3286 let mut node_levels = Vec::with_capacity(dependency_plan.levels.len());
3287 for level in &dependency_plan.levels {
3288 let nodes = level
3289 .iter()
3290 .map(|index| {
3291 digest_nodes
3292 .get(*index)
3293 .and_then(|node| *node)
3294 .ok_or_else(|| {
3295 SigningDigestError::InvalidStructure(
3296 "compiled signing Reference index is unavailable".into(),
3297 )
3298 })
3299 })
3300 .collect::<Result<Vec<_>, _>>()?;
3301 for (index, node) in level.iter().zip(&nodes) {
3302 for dependency_index in &dependency_plan.dependencies[*index] {
3303 let dependency = digest_nodes
3304 .get(*dependency_index)
3305 .and_then(|node| *node)
3306 .ok_or_else(|| {
3307 SigningDigestError::InvalidStructure(
3308 "compiled signing dependency index is unavailable".into(),
3309 )
3310 })?;
3311 operation
3312 .add_dependency(*node, dependency)
3313 .map_err(map_signing_digest_plan_error)?;
3314 }
3315 }
3316 node_levels.push(nodes);
3317 }
3318
3319 let canonicalization = operation.add_node(
3320 OperationNodeKind::Canonicalization,
3321 OperationStage::Canonicalization,
3322 None,
3323 );
3324 for digest in digest_nodes.iter().flatten() {
3325 operation
3326 .add_dependency(canonicalization, *digest)
3327 .map_err(map_signing_digest_plan_error)?;
3328 }
3329 let crypto = operation.add_node(OperationNodeKind::Crypto, OperationStage::Crypto, None);
3330 operation
3331 .add_dependency(crypto, canonicalization)
3332 .map_err(map_signing_digest_plan_error)?;
3333 let evidence = operation.add_node(OperationNodeKind::Evidence, OperationStage::Evidence, None);
3334 operation
3335 .add_dependency(evidence, crypto)
3336 .map_err(map_signing_digest_plan_error)?;
3337 let mutation = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
3338 operation
3339 .add_dependency(mutation, evidence)
3340 .map_err(map_signing_digest_plan_error)?;
3341 operation.compile().map_err(map_signing_digest_plan_error)?;
3342
3343 Ok((
3344 node_levels,
3345 SigningPlanNodes {
3346 canonicalization,
3347 crypto,
3348 evidence,
3349 mutation,
3350 },
3351 ))
3352}
3353
3354fn map_signing_digest_plan_error(error: OperationPlanError) -> SigningDigestError {
3355 SigningDigestError::InvalidStructure(error.to_string())
3356}
3357
3358fn commit_signed_staged(
3359 document: &mut XmlDocument,
3360 staged: XmlDocument,
3361 policy: &crate::policy::SigningPolicy,
3362) -> Result<(), SigningError> {
3363 let mut operation = OperationExecutionContext::new(
3364 policy.clone(),
3365 (),
3366 Some((document.identity(), document.generation())),
3367 );
3368 let mutation = operation.add_node(OperationNodeKind::Mutation, OperationStage::Mutation, None);
3369 operation.compile()?;
3370 operation.run_document_transition(mutation, document, |document, _| {
3371 document
3372 .commit_staged(staged)
3373 .map_err(map_owned_document_mutation_error)
3374 })
3375}
3376
3377fn validate_signing_references(
3378 references: &[SigningReference],
3379 total_references: usize,
3380 policy: Option<&crate::policy::SigningPolicy>,
3381 has_external_resources: bool,
3382) -> Result<(), SigningDigestError> {
3383 if let Some(policy) = policy
3384 && total_references > policy.resources.max_references
3385 {
3386 return Err(crate::policy::PolicyViolation::ResourceLimit {
3387 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3388 maximum: policy.resources.max_references,
3389 actual: total_references,
3390 }
3391 .into());
3392 }
3393 for reference in references {
3394 super::transforms::validate_relationship_chain(
3395 &reference.transforms,
3396 policy.map_or(crate::policy::OpcRelationshipEdition::default(), |policy| {
3397 policy.transforms.opc_relationship_edition
3398 }),
3399 )?;
3400 if let Some(policy) = policy {
3401 validate_signing_reference_uri(&reference.uri, policy)?;
3402 validate_signing_reference_request(&reference.uri, has_external_resources)?;
3403 }
3404 if let Some(policy) = policy
3405 && reference.transforms.len() > policy.resources.max_transforms_per_reference
3406 {
3407 return Err(crate::policy::PolicyViolation::ResourceLimit {
3408 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3409 maximum: policy.resources.max_transforms_per_reference,
3410 actual: reference.transforms.len(),
3411 }
3412 .into());
3413 }
3414 if let Some(policy) = policy {
3415 policy.check_digest_algorithm(reference.digest_method)?;
3416 } else if !reference.digest_method.signing_allowed() {
3417 return Err(SigningDigestError::SigningAlgorithmDisabled {
3418 uri: reference.digest_method.uri(),
3419 });
3420 }
3421 let initial_binary = !reference.uri.is_empty() && !reference.uri.starts_with('#');
3422 validate_signing_transform_policy(
3423 initial_binary,
3424 &reference.transforms,
3425 policy.and_then(|policy| policy.transforms.allowed_algorithms.as_ref()),
3426 )?;
3427 }
3428 Ok(())
3429}
3430
3431fn validate_signing_signed_info_methods(
3432 signature: Node<'_, '_>,
3433 policy: &crate::policy::SigningPolicy,
3434) -> Result<(SignatureAlgorithm, Option<usize>), SigningError> {
3435 let signed_info = find_required_child(signature, "SignedInfo")?;
3436 let canonicalization_method =
3437 element_children(signed_info)
3438 .next()
3439 .ok_or(SigningDigestError::MissingElement {
3440 element: "CanonicalizationMethod",
3441 })?;
3442 verify_ds_element(canonicalization_method, "CanonicalizationMethod")?;
3443 let algorithm = required_algorithm_attr(canonicalization_method, "CanonicalizationMethod")?;
3444 if policy
3445 .transforms
3446 .allowed_algorithms
3447 .as_ref()
3448 .is_some_and(|allowed| !allowed.contains(algorithm))
3449 {
3450 return Err(crate::policy::PolicyViolation::Algorithm {
3451 operation: "SignedInfo canonicalization",
3452 algorithm: algorithm.to_owned(),
3453 }
3454 .into());
3455 }
3456 let signature_method =
3457 element_children(signed_info)
3458 .nth(1)
3459 .ok_or(SigningDigestError::MissingElement {
3460 element: "SignatureMethod",
3461 })?;
3462 super::parse::parse_signature_method(signature_method, &policy.resources)
3463 .map(|(algorithm, hmac, _context)| (algorithm, hmac))
3464 .map_err(SigningError::from)
3465}
3466
3467struct SigningUriResolution<'a, 'resources> {
3468 id_attributes: &'a [crate::IdAttributeRegistration],
3469 same_document_id_semantics: crate::policy::SameDocumentIdSemantics,
3470 external_resources: &'a ExternalResourceContext<'resources>,
3471}
3472
3473fn compute_signing_reference_digests(
3474 doc: &Document<'_>,
3475 signature: Node<'_, '_>,
3476 references: Vec<SigningReference>,
3477 transform_options: TransformOptions<'_>,
3478 provider: &dyn crate::provider::CryptoProvider,
3479 execution_budget: &TransformExecutionBudget,
3480 uri_resolution: SigningUriResolution<'_, '_>,
3481) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
3482 let resolver = uri_resolution.external_resources.bind(
3483 doc,
3484 uri_resolution.id_attributes,
3485 uri_resolution.same_document_id_semantics,
3486 );
3487 references
3488 .into_iter()
3489 .enumerate()
3490 .map(|(index, reference)| {
3491 let origin = signing_reference_origin(doc, &reference)?;
3492 let initial_data = resolver.dereference_from_with_budget(
3493 &reference.uri,
3494 origin,
3495 execution_budget.node_set_materialization(),
3496 execution_budget.xml_base_resolution(),
3497 )?;
3498 let digest = super::transforms::digest_reference_transforms_with_budget(
3499 signature,
3500 Some(signature),
3501 initial_data,
3502 &reference.transforms,
3503 transform_options,
3504 execution_budget,
3505 execution_budget.xml_parse_work(),
3506 super::transforms::DigestRequest {
3507 provider,
3508 algorithm: reference.digest_method,
3509 retain_pre_digest: false,
3510 },
3511 )
3512 .map_err(|error| -> SigningDigestError {
3513 match error {
3514 super::transforms::ReferenceDigestError::Transform(error) => error.into(),
3515 super::transforms::ReferenceDigestError::Provider(error) => error.into(),
3516 }
3517 })?
3518 .digest;
3519 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
3520 Ok(ComputedReferenceDigest {
3521 index,
3522 uri: reference.uri,
3523 digest_method: reference.digest_method,
3524 digest_value,
3525 })
3526 })
3527 .collect()
3528}
3529
3530pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
3538 let execution_budget = TransformExecutionBudget::default();
3539 fill_reference_digest_values_with_options(
3540 xml,
3541 TransformOptions::default(),
3542 None,
3543 crate::provider::default_provider(),
3544 &execution_budget,
3545 None,
3546 &[],
3547 )
3548}
3549
3550fn fill_reference_digest_values_with_options(
3551 xml: &str,
3552 transform_options: TransformOptions<'_>,
3553 policy: Option<&crate::policy::SigningPolicy>,
3554 provider: &dyn crate::provider::CryptoProvider,
3555 execution_budget: &TransformExecutionBudget,
3556 target_signature: Option<usize>,
3557 id_attributes: &[crate::IdAttributeRegistration],
3558) -> Result<String, SigningDigestError> {
3559 let default_policy = crate::policy::SigningPolicy::default();
3560 let effective_policy = policy.unwrap_or(&default_policy);
3561 let (prepared, target_signature) =
3562 prepare_reference_digest_input(xml, policy, execution_budget, target_signature)?;
3563 let digest_values = compute_prepared_reference_digest_values_with_options(
3564 prepared.as_ref(),
3565 transform_options,
3566 Some(effective_policy),
3567 provider,
3568 execution_budget,
3569 target_signature,
3570 id_attributes,
3571 )?
3572 .into_iter()
3573 .map(|digest| digest.digest_value);
3574 Ok(fill_signed_info_digest_values_at_index_with_budget(
3575 prepared.as_ref(),
3576 digest_values,
3577 target_signature,
3578 Some(effective_policy),
3579 Some(execution_budget.xml_parse_work()),
3580 )?)
3581}
3582
3583fn canonicalize_signed_info(
3584 document: &XmlDocument,
3585 policy: &crate::policy::SigningPolicy,
3586 budgets: &mut SigningOperationBudgets,
3587 target_signature: usize,
3588) -> Result<
3589 (
3590 SignatureAlgorithm,
3591 Option<usize>,
3592 Vec<u8>,
3593 super::SignatureContext,
3594 ),
3595 SigningError,
3596> {
3597 document.with_view(|view| {
3598 let doc = view.document();
3599 let signature =
3600 find_signing_signature_node(doc, SigningSignatureTarget::Index(target_signature))
3601 .map_err(SigningError::Digest)?;
3602 let signed_info_node =
3603 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
3604 let signed_info = parse_signed_info_with_xpath_budget(
3605 signed_info_node,
3606 &mut budgets.xpath_parse,
3607 &policy.resources,
3608 )?;
3609 if policy
3610 .transforms
3611 .allowed_algorithms
3612 .as_ref()
3613 .is_some_and(|allowed| !allowed.contains(signed_info.c14n_method.uri()))
3614 {
3615 return Err(crate::policy::PolicyViolation::Algorithm {
3616 operation: "SignedInfo canonicalization",
3617 algorithm: signed_info.c14n_method.uri().to_owned(),
3618 }
3619 .into());
3620 }
3621 let signed_info_subtree: HashSet<_> = signed_info_node
3622 .descendants()
3623 .map(|node: Node<'_, '_>| node.id())
3624 .collect();
3625 let mut canonical_signed_info = Vec::new();
3626 canonicalize_bounded_with_xml_base_budget(
3627 doc,
3628 Some(&|node| signed_info_subtree.contains(&node.id())),
3629 &signed_info.c14n_method,
3630 budgets.transforms.remaining_c14n_output(),
3631 budgets.transforms.xml_base_resolution(),
3632 &mut canonical_signed_info,
3633 )
3634 .map_err(|error| {
3635 if let Some(violation) = map_c14n_resource_policy_violation(
3636 &error,
3637 crate::policy::resource_name::CANONICALIZED_BYTES,
3638 budgets.transforms.c14n_output_limit(),
3639 ) {
3640 SigningError::Policy(violation)
3641 } else {
3642 SigningError::Canonicalization(error)
3643 }
3644 })?;
3645 Ok((
3646 signed_info.signature_method,
3647 signed_info.hmac_output_length_bits,
3648 canonical_signed_info,
3649 signed_info.signature_context,
3650 ))
3651 })
3652}
3653
3654const SECOND_EDITION_GENERATION_C14N_URI: &str = "http://www.w3.org/2006/12/xml-c14n11";
3655
3656struct SourceEdit {
3657 range: Range<usize>,
3658 replacement: String,
3659}
3660
3661fn materialize_second_edition_c14n11_candidate(
3662 document: &XmlDocument,
3663 target_signature: usize,
3664 policy: &crate::policy::SigningPolicy,
3665 has_external_resources: bool,
3666) -> Result<Option<String>, SigningDigestError> {
3667 let edits = document.with_view(|view| {
3668 let signature = find_signing_signature_node(
3669 view.document(),
3670 SigningSignatureTarget::Index(target_signature),
3671 )?;
3672 let signed_info = find_required_child(signature, "SignedInfo")?;
3673 let mut reference_nodes = element_children(signed_info)
3674 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Reference")))
3675 .collect::<Vec<_>>();
3676 if policy.manifest_processing == crate::policy::ManifestProcessing::Process {
3677 let reference_limit = policy
3678 .resources
3679 .max_references
3680 .min(MAX_REFERENCES_PER_SIGNATURE);
3681 for manifest in signature
3682 .children()
3683 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Object")))
3684 .flat_map(|object| {
3685 object
3686 .children()
3687 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Manifest")))
3688 })
3689 {
3690 let mut manifest_references = 0usize;
3691 for child in element_children(manifest) {
3692 verify_ds_element(child, "Reference")?;
3693 if reference_nodes.len() == reference_limit {
3694 return Err(crate::policy::PolicyViolation::ResourceLimit {
3695 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3696 maximum: reference_limit,
3697 actual: reference_limit.saturating_add(1),
3698 }
3699 .into());
3700 }
3701 reference_nodes.push(child);
3702 manifest_references += 1;
3703 }
3704 if manifest_references == 0 {
3705 return Err(SigningDigestError::MissingElement {
3706 element: "Reference",
3707 });
3708 }
3709 }
3710 }
3711 if reference_nodes.len() > policy.resources.max_references {
3712 return Err(crate::policy::PolicyViolation::ResourceLimit {
3713 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
3714 maximum: policy.resources.max_references,
3715 actual: reference_nodes.len(),
3716 }
3717 .into());
3718 }
3719
3720 let mut edits = Vec::new();
3721 for reference_node in reference_nodes {
3722 let structure = parse_signing_reference_structure(reference_node)?;
3723 validate_signing_reference_uri(structure.uri, policy)?;
3724 validate_signing_reference_request(structure.uri, has_external_resources)?;
3725 let digest_uri = required_algorithm_attr(structure.digest_method_node, "DigestMethod")?;
3726 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
3727 SigningDigestError::UnsupportedAlgorithm {
3728 uri: digest_uri.to_owned(),
3729 }
3730 })?;
3731 policy.check_digest_algorithm(digest_method)?;
3732
3733 let transform_uris = generation_transform_uris(structure.transforms_node)?;
3734 if transform_uris.len() > policy.resources.max_transforms_per_reference {
3735 return Err(crate::policy::PolicyViolation::ResourceLimit {
3736 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3737 maximum: policy.resources.max_transforms_per_reference,
3738 actual: transform_uris.len(),
3739 }
3740 .into());
3741 }
3742 if let Some(allowed) = policy.transforms.allowed_algorithms.as_ref()
3743 && let Some(disallowed) = transform_uris
3744 .iter()
3745 .find(|algorithm| !allowed.contains(**algorithm))
3746 {
3747 return Err(crate::policy::PolicyViolation::Algorithm {
3748 operation: "signing transform",
3749 algorithm: (*disallowed).to_owned(),
3750 }
3751 .into());
3752 }
3753 let initial_binary = !structure.uri.is_empty() && !structure.uri.starts_with('#');
3754 if generation_chain_produces_binary(initial_binary, &transform_uris) {
3755 continue;
3756 }
3757 if transform_uris.len() >= policy.resources.max_transforms_per_reference {
3758 return Err(crate::policy::PolicyViolation::ResourceLimit {
3759 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3760 maximum: policy.resources.max_transforms_per_reference,
3761 actual: transform_uris.len().saturating_add(1),
3762 }
3763 .into());
3764 }
3765 if policy
3766 .transforms
3767 .allowed_algorithms
3768 .as_ref()
3769 .is_some_and(|allowed| !allowed.contains(SECOND_EDITION_GENERATION_C14N_URI))
3770 {
3771 return Err(crate::policy::PolicyViolation::Algorithm {
3772 operation: "signing transform",
3773 algorithm: SECOND_EDITION_GENERATION_C14N_URI.to_owned(),
3774 }
3775 .into());
3776 }
3777
3778 let edit = if let Some(transforms_node) = structure.transforms_node {
3779 c14n11_edit_for_transforms(view.xml(), transforms_node)?
3780 } else {
3781 let prefix = reference_node.prefix().unwrap_or_default();
3782 let qualifier = xml_qualifier(prefix);
3783 SourceEdit {
3784 range: structure.digest_method_node.range().start
3785 ..structure.digest_method_node.range().start,
3786 replacement: format!(
3787 "<{qualifier}Transforms><{qualifier}Transform Algorithm=\"{SECOND_EDITION_GENERATION_C14N_URI}\"/></{qualifier}Transforms>"
3788 ),
3789 }
3790 };
3791 edits.push(edit);
3792 }
3793 Ok::<_, SigningDigestError>(edits)
3794 })?;
3795 if edits.is_empty() {
3796 return Ok(None);
3797 }
3798
3799 let projected_len = edits
3800 .iter()
3801 .try_fold(document.as_xml().len(), |total, edit| {
3802 total
3803 .checked_sub(edit.range.len())
3804 .and_then(|value| value.checked_add(edit.replacement.len()))
3805 .ok_or_else(|| {
3806 SigningDigestError::InvalidStructure("document length overflow".into())
3807 })
3808 })?;
3809 policy
3810 .resources
3811 .validate_xml_document_len(projected_len)
3812 .map_err(SigningDigestError::Policy)?;
3813
3814 let mut candidate = document.as_xml().to_owned();
3815 let mut edits = edits;
3816 edits.sort_unstable_by_key(|edit| std::cmp::Reverse(edit.range.start));
3817 for edit in edits {
3818 candidate.replace_range(edit.range, &edit.replacement);
3819 }
3820 Ok(Some(candidate))
3821}
3822
3823fn c14n11_edit_for_transforms(
3824 xml: &str,
3825 transforms_node: Node<'_, '_>,
3826) -> Result<SourceEdit, SigningDigestError> {
3827 let range = transforms_node.range();
3828 let source = xml.get(range.clone()).ok_or_else(|| {
3829 SigningDigestError::InvalidStructure("Transforms source range is unavailable".into())
3830 })?;
3831 let prefix = transforms_node.prefix().unwrap_or_default();
3832 let qualifier = xml_qualifier(prefix);
3833 let transform =
3834 format!("<{qualifier}Transform Algorithm=\"{SECOND_EDITION_GENERATION_C14N_URI}\"/>");
3835 if source.trim_end().ends_with("/>") {
3836 let trimmed = source.trim_end();
3837 let opening = trimmed.strip_suffix("/>").ok_or_else(|| {
3838 SigningDigestError::InvalidStructure(
3839 "self-closing Transforms source is unavailable".into(),
3840 )
3841 })?;
3842 let trailing = &source[trimmed.len()..];
3843 return Ok(SourceEdit {
3844 range,
3845 replacement: format!("{opening}>{transform}</{qualifier}Transforms>{trailing}"),
3846 });
3847 }
3848 let closing = source.rfind("</").ok_or_else(|| {
3849 SigningDigestError::InvalidStructure("Transforms closing tag is unavailable".into())
3850 })?;
3851 let offset = range.start + closing;
3852 Ok(SourceEdit {
3853 range: offset..offset,
3854 replacement: transform,
3855 })
3856}
3857
3858fn generation_transform_uris<'a>(
3859 transforms_node: Option<Node<'a, 'a>>,
3860) -> Result<Vec<&'a str>, SigningDigestError> {
3861 let Some(transforms_node) = transforms_node else {
3862 return Ok(Vec::new());
3863 };
3864 let mut algorithms = Vec::new();
3865 for child in element_children(transforms_node) {
3866 if algorithms.len() == MAX_TRANSFORMS_PER_REFERENCE {
3867 return Err(crate::policy::PolicyViolation::ResourceLimit {
3868 resource: crate::policy::resource_name::REFERENCE_TRANSFORMS,
3869 maximum: MAX_TRANSFORMS_PER_REFERENCE,
3870 actual: MAX_TRANSFORMS_PER_REFERENCE.saturating_add(1),
3871 }
3872 .into());
3873 }
3874 if !child.has_tag_name((XMLDSIG_NS, "Transform")) {
3875 return Err(TransformError::UnsupportedTransform(
3876 "unexpected child element of <ds:Transforms>; only <ds:Transform> is allowed"
3877 .into(),
3878 )
3879 .into());
3880 }
3881 let algorithm = child.attribute("Algorithm").ok_or_else(|| {
3882 TransformError::UnsupportedTransform(
3883 "missing Algorithm attribute on <Transform>".into(),
3884 )
3885 })?;
3886 if algorithm != ENVELOPED_SIGNATURE_URI
3887 && algorithm != BASE64_TRANSFORM_URI
3888 && algorithm != XPATH_TRANSFORM_URI
3889 && algorithm != XPATH_FILTER2_TRANSFORM_URI
3890 && algorithm != super::RELATIONSHIP_TRANSFORM_URI
3891 && !(cfg!(feature = "xslt") && algorithm == super::XSLT_TRANSFORM_URI)
3892 && crate::c14n::C14nAlgorithm::from_uri(algorithm).is_none()
3893 {
3894 return Err(TransformError::UnsupportedTransform(algorithm.to_owned()).into());
3895 }
3896 algorithms.push(algorithm);
3897 }
3898 Ok(algorithms)
3899}
3900
3901fn generation_chain_produces_binary(initial_binary: bool, algorithms: &[&str]) -> bool {
3902 algorithms.last().map_or(initial_binary, |algorithm| {
3903 *algorithm == BASE64_TRANSFORM_URI
3904 || *algorithm == super::RELATIONSHIP_TRANSFORM_URI
3905 || (cfg!(feature = "xslt") && *algorithm == super::XSLT_TRANSFORM_URI)
3906 || crate::c14n::C14nAlgorithm::from_uri(algorithm).is_some()
3907 })
3908}
3909
3910#[cfg(test)]
3911mod generation_output_tests;
3912
3913fn xml_qualifier(prefix: &str) -> String {
3914 if prefix.is_empty() {
3915 String::new()
3916 } else {
3917 format!("{prefix}:")
3918 }
3919}
3920
3921fn parse_signing_document<'a>(
3922 xml: &'a str,
3923 policy: Option<&crate::policy::SigningPolicy>,
3924 budget: &XmlParseWorkBudget,
3925 backend: crate::XmlBackend,
3926) -> Result<Document<'a>, SigningDigestError> {
3927 let settings = policy
3928 .map(|policy| DocumentParseSettings::from_policy(&policy.xml, &policy.resources))
3929 .unwrap_or_default()
3930 .with_backend(backend);
3931 super::mutation::parse_with_options_and_budget(xml, settings, Some(budget)).map_err(|error| {
3932 match error.into_policy_violation(settings) {
3933 Ok(error) => SigningDigestError::Policy(error),
3934 Err(XmlDocumentError::Parse(error)) => SigningDigestError::XmlParse(error),
3935 Err(error) => SigningDigestError::Document(error),
3936 }
3937 })
3938}
3939
3940fn parse_private_key_pem(private_key_pem: &str) -> Result<Zeroizing<Vec<u8>>, SigningKeyError> {
3941 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
3942 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
3943 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
3944 return Err(SigningKeyError::InvalidKeyPem);
3945 }
3946 if pem.label != "PRIVATE KEY" {
3947 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
3948 }
3949 Ok(Zeroizing::new(pem.contents))
3950}
3951
3952enum SigningSignatureTarget {
3953 First,
3954 Last,
3955 Index(usize),
3956}
3957
3958fn find_signing_signature_node<'a>(
3959 doc: &'a Document<'a>,
3960 target: SigningSignatureTarget,
3961) -> Result<Node<'a, 'a>, SigningDigestError> {
3962 let mut signatures = doc.descendants().filter(|node| {
3963 node.is_element()
3964 && node.tag_name().name() == "Signature"
3965 && node.tag_name().namespace() == Some(XMLDSIG_NS)
3966 });
3967 match target {
3968 SigningSignatureTarget::First => signatures.next(),
3969 SigningSignatureTarget::Last => signatures.next_back(),
3970 SigningSignatureTarget::Index(index) => signatures.nth(index),
3971 }
3972 .ok_or(SigningDigestError::MissingElement {
3973 element: "Signature",
3974 })
3975}
3976
3977fn signing_signature_index(
3978 doc: &Document<'_>,
3979 start_node_id: Option<&str>,
3980 id_attributes: &[crate::IdAttributeRegistration],
3981 selection: SignatureTemplateSelection,
3982) -> Result<usize, SigningDigestError> {
3983 let selected = if let Some(id) = start_node_id {
3984 let start = signing_start_node(doc, id, id_attributes)?;
3985 let mut signatures = start
3986 .descendants()
3987 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")));
3988 match selection.target() {
3989 SigningSignatureTarget::First => signatures.next(),
3990 SigningSignatureTarget::Last => signatures.next_back(),
3991 SigningSignatureTarget::Index(_) => unreachable!("public selection is not indexed"),
3992 }
3993 .ok_or_else(|| {
3994 SigningDigestError::InvalidStructure(format!(
3995 "selected node subtree has no Signature: {id}"
3996 ))
3997 })?
3998 } else {
3999 find_signing_signature_node(doc, selection.target())?
4000 };
4001 signature_index(doc, selected)
4002}
4003
4004fn signing_start_node<'a>(
4005 doc: &'a Document<'a>,
4006 id: &str,
4007 id_attributes: &[crate::IdAttributeRegistration],
4008) -> Result<Node<'a, 'a>, SigningDigestError> {
4009 UriReferenceResolver::with_id_registrations(doc, id_attributes)
4010 .node_for_id(id)
4011 .ok_or_else(|| {
4012 SigningDigestError::InvalidStructure(format!(
4013 "selected node ID is missing or ambiguous: {id}"
4014 ))
4015 })
4016}
4017
4018fn signature_index(
4019 doc: &Document<'_>,
4020 selected: Node<'_, '_>,
4021) -> Result<usize, SigningDigestError> {
4022 doc.descendants()
4023 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Signature")))
4024 .position(|node| node == selected)
4025 .ok_or(SigningDigestError::MissingElement {
4026 element: "Signature",
4027 })
4028}
4029
4030fn parse_signing_references(
4031 signed_info: Node<'_, '_>,
4032) -> Result<Vec<SigningReference>, SigningDigestError> {
4033 parse_signing_references_with_budget(signed_info, &mut XPathSignatureParseBudget::default())
4034}
4035
4036fn parse_signing_references_with_budget(
4037 signed_info: Node<'_, '_>,
4038 xpath_budget: &mut XPathSignatureParseBudget,
4039) -> Result<Vec<SigningReference>, SigningDigestError> {
4040 verify_ds_element(signed_info, "SignedInfo")?;
4041 let mut children = element_children(signed_info);
4042
4043 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
4044 element: "CanonicalizationMethod",
4045 })?;
4046 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
4047 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
4048
4049 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
4050 element: "SignatureMethod",
4051 })?;
4052 verify_ds_element(signature_method_node, "SignatureMethod")?;
4053 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
4054
4055 let mut references = Vec::new();
4056 for child in children {
4057 verify_ds_element(child, "Reference")?;
4058 if references.len() == MAX_REFERENCES_PER_SIGNATURE {
4059 return Err(SigningDigestError::InvalidStructure(format!(
4060 "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
4061 )));
4062 }
4063 references.push(parse_signing_reference(child, xpath_budget)?);
4064 }
4065 if references.is_empty() {
4066 return Err(SigningDigestError::MissingElement {
4067 element: "Reference",
4068 });
4069 }
4070 Ok(references)
4071}
4072
4073fn parse_signing_manifest_references(
4074 signature: Node<'_, '_>,
4075 xpath_budget: &mut XPathSignatureParseBudget,
4076 mut remaining_capacity: usize,
4077 maximum_references: usize,
4078) -> Result<Vec<SigningReference>, SigningDigestError> {
4079 let mut references = Vec::new();
4080 for manifest in signature
4081 .children()
4082 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Object")))
4083 .flat_map(|object| {
4084 object
4085 .children()
4086 .filter(|node| node.has_tag_name((XMLDSIG_NS, "Manifest")))
4087 })
4088 {
4089 let mut manifest_references = 0usize;
4090 for child in element_children(manifest) {
4091 verify_ds_element(child, "Reference")?;
4092 if remaining_capacity == 0 {
4093 return Err(crate::policy::PolicyViolation::ResourceLimit {
4094 resource: crate::policy::resource_name::SIGNATURE_REFERENCES,
4095 maximum: maximum_references,
4096 actual: maximum_references.saturating_add(1),
4097 }
4098 .into());
4099 }
4100 remaining_capacity -= 1;
4101 references.push(parse_signing_reference(child, xpath_budget)?);
4102 manifest_references += 1;
4103 }
4104 if manifest_references == 0 {
4105 return Err(SigningDigestError::MissingElement {
4106 element: "Reference",
4107 });
4108 }
4109 }
4110 Ok(references)
4111}
4112
4113fn parse_signing_reference(
4114 reference_node: Node<'_, '_>,
4115 xpath_budget: &mut XPathSignatureParseBudget,
4116) -> Result<SigningReference, SigningDigestError> {
4117 let structure = parse_signing_reference_structure(reference_node)?;
4118 let transforms = structure.transforms_node.map_or_else(
4119 || Ok(Vec::new()),
4120 |node| parse_transforms_with_budget(node, xpath_budget),
4121 )?;
4122 let digest_uri = required_algorithm_attr(structure.digest_method_node, "DigestMethod")?;
4123 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
4124 SigningDigestError::UnsupportedAlgorithm {
4125 uri: digest_uri.to_string(),
4126 }
4127 })?;
4128
4129 Ok(SigningReference {
4130 uri: structure.uri.to_owned(),
4131 origin_node_id: reference_node.id(),
4132 transforms,
4133 digest_method,
4134 digest_value_range: structure.digest_value_node.range(),
4135 digest_value_node_id: structure.digest_value_node.id(),
4136 })
4137}
4138
4139struct SigningReferenceStructure<'a> {
4140 uri: &'a str,
4141 transforms_node: Option<Node<'a, 'a>>,
4142 digest_method_node: Node<'a, 'a>,
4143 digest_value_node: Node<'a, 'a>,
4144}
4145
4146fn parse_signing_reference_structure<'a>(
4147 reference_node: Node<'a, 'a>,
4148) -> Result<SigningReferenceStructure<'a>, SigningDigestError> {
4149 let uri = reference_node.attribute("URI").ok_or_else(|| {
4150 SigningDigestError::InvalidStructure("signing Reference must include URI attribute".into())
4151 })?;
4152 let mut children = element_children(reference_node);
4153 let first = children.next().ok_or(SigningDigestError::MissingElement {
4154 element: "DigestMethod",
4155 })?;
4156 let (transforms_node, digest_method_node) = if first.has_tag_name((XMLDSIG_NS, "Transforms")) {
4157 (
4158 Some(first),
4159 children.next().ok_or(SigningDigestError::MissingElement {
4160 element: "DigestMethod",
4161 })?,
4162 )
4163 } else {
4164 (None, first)
4165 };
4166 verify_ds_element(digest_method_node, "DigestMethod")?;
4167 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
4168 element: "DigestValue",
4169 })?;
4170 verify_ds_element(digest_value_node, "DigestValue")?;
4171 if let Some(unexpected) = children.next() {
4172 return Err(SigningDigestError::InvalidStructure(format!(
4173 "unexpected element <{}> after <DigestValue> in <Reference>",
4174 unexpected.tag_name().name()
4175 )));
4176 }
4177 Ok(SigningReferenceStructure {
4178 uri,
4179 transforms_node,
4180 digest_method_node,
4181 digest_value_node,
4182 })
4183}
4184
4185fn find_required_child<'a>(
4186 parent: Node<'a, 'a>,
4187 child_name: &'static str,
4188) -> Result<Node<'a, 'a>, SigningDigestError> {
4189 parent
4190 .children()
4191 .find(|node| {
4192 node.is_element()
4193 && node.tag_name().name() == child_name
4194 && node.tag_name().namespace() == Some(XMLDSIG_NS)
4195 })
4196 .ok_or(SigningDigestError::MissingElement {
4197 element: child_name,
4198 })
4199}
4200
4201fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
4202 node.children().filter(Node::is_element)
4203}
4204
4205fn verify_ds_element(
4206 node: Node<'_, '_>,
4207 expected_name: &'static str,
4208) -> Result<(), SigningDigestError> {
4209 if !node.is_element() {
4210 return Err(SigningDigestError::InvalidStructure(format!(
4211 "expected element <{expected_name}>, got non-element node"
4212 )));
4213 }
4214 let tag = node.tag_name();
4215 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
4216 return Err(SigningDigestError::InvalidStructure(format!(
4217 "expected <ds:{expected_name}>, got <{}>",
4218 tag.name()
4219 )));
4220 }
4221 Ok(())
4222}
4223
4224fn required_algorithm_attr<'a>(
4225 node: Node<'a, 'a>,
4226 element_name: &'static str,
4227) -> Result<&'a str, SigningDigestError> {
4228 node.attribute("Algorithm").ok_or_else(|| {
4229 SigningDigestError::InvalidStructure(format!(
4230 "missing Algorithm attribute on <{element_name}>"
4231 ))
4232 })
4233}
4234
4235#[cfg(test)]
4236mod error_conversion_tests {
4237 use super::*;
4238 use crate::policy::PolicyViolation;
4239
4240 struct RejectingSigningKey;
4241
4242 impl SigningKey for RejectingSigningKey {
4243 fn sign(
4244 &self,
4245 _algorithm: SignatureAlgorithm,
4246 _canonical_signed_info: &[u8],
4247 ) -> Result<Vec<u8>, SigningKeyError> {
4248 Err(SigningKeyError::SigningFailed)
4249 }
4250
4251 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
4252 Err(SigningKeyError::PublicKeyEncodingFailed)
4253 }
4254 }
4255
4256 struct FixedRsaSigningKey;
4257
4258 struct WrongWidthDsaSigningKey;
4259
4260 impl SigningKey for WrongWidthDsaSigningKey {
4261 fn sign(
4262 &self,
4263 _algorithm: SignatureAlgorithm,
4264 _canonical_signed_info: &[u8],
4265 ) -> Result<Vec<u8>, SigningKeyError> {
4266 unreachable!("preflight must reject this candidate")
4267 }
4268
4269 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
4270 Ok(SigningPublicKeyInfo::Dsa {
4271 spki_der: Vec::new(),
4272 p: Vec::new(),
4273 q: Vec::new(),
4274 g: Vec::new(),
4275 y: Vec::new(),
4276 modulus_bits: 2048,
4277 component_len: 20,
4278 })
4279 }
4280 }
4281
4282 #[test]
4283 fn dsa_q_width_mismatch_is_candidate_incompatibility() {
4284 let error = validate_signing_key(
4286 &WrongWidthDsaSigningKey,
4287 SignatureAlgorithm::DsaSha256,
4288 &crate::policy::SigningPolicy::default(),
4289 )
4290 .expect_err("SHA-256 requires a 256-bit q");
4291 assert!(matches!(
4292 error,
4293 SigningError::Key(SigningKeyError::UnsupportedAlgorithm { .. })
4294 ));
4295 }
4296
4297 impl SigningKey for FixedRsaSigningKey {
4298 fn sign(
4299 &self,
4300 _algorithm: SignatureAlgorithm,
4301 _canonical_signed_info: &[u8],
4302 ) -> Result<Vec<u8>, SigningKeyError> {
4303 Ok(vec![0x5a; 256])
4304 }
4305
4306 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
4307 Ok(SigningPublicKeyInfo::Rsa {
4308 spki_der: Vec::new(),
4309 modulus: vec![0x80; 256],
4310 exponent: vec![1, 0, 1],
4311 })
4312 }
4313 }
4314
4315 #[test]
4316 fn namespace_exhaustion_preserves_typed_signing_errors() {
4317 let make_error = || {
4319 XmlDocumentError::Parse(crate::xml::dom::ParseError::NamespaceBindingLimitReached {
4320 maximum: 2,
4321 actual: 3,
4322 })
4323 };
4324 assert!(matches!(
4325 owned_document_policy_violation(make_error()),
4326 Ok(PolicyViolation::ResourceLimit {
4327 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
4328 maximum: 2,
4329 actual: 3,
4330 })
4331 ));
4332 let mut policy = crate::policy::SigningPolicy::default();
4333 policy.resources.max_xml_namespace_bindings = 0;
4334 let context = SignContext::new(&RejectingSigningKey).policy(policy);
4335 let builder = SignatureBuilder::new(
4336 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4337 SignatureAlgorithm::RsaSha256,
4338 );
4339 let xml = "<root xmlns:p='urn:p'/>";
4340 for result in [
4341 context.sign_template(xml),
4342 context.sign_with_builder(xml, &builder),
4343 ] {
4344 assert!(
4345 matches!(
4346 result,
4347 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4348 resource: crate::policy::resource_name::XML_NAMESPACE_BINDINGS,
4349 maximum: 0,
4350 actual: 1,
4351 }))
4352 ),
4353 "unexpected signing result: {result:?}"
4354 );
4355 }
4356 }
4357
4358 #[test]
4359 fn signing_error_promotes_every_policy_failure() {
4360 let digest = SigningError::from(SigningDigestError::Policy(PolicyViolation::Algorithm {
4363 operation: "signing",
4364 algorithm: "urn:test:digest".into(),
4365 }));
4366 assert!(matches!(digest, SigningError::Policy(_)));
4367
4368 let mutation = SigningError::from(SigningDigestError::XmlMutation(
4369 XmlMutationError::Policy(PolicyViolation::ResourceLimit {
4370 resource: crate::policy::resource_name::XML_DOCUMENT,
4371 maximum: 1,
4372 actual: 2,
4373 }),
4374 ));
4375 assert!(matches!(mutation, SigningError::Policy(_)));
4376 }
4377
4378 #[test]
4379 fn manifest_reparse_consumes_the_signature_wide_xpath_budget() {
4380 let filter = r#"<xf:XPath xmlns:xf="http://www.w3.org/2002/06/xmldsig-filter2" Filter="intersect">true()</xf:XPath>"#;
4384 let signed_info_transforms = format!(
4385 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>"#,
4386 filter.repeat(64)
4387 );
4388 let signed_info_references = (0..62)
4389 .map(|index| {
4390 let extra = if index == 0 {
4391 r#"<ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"><ds:XPath>true()</ds:XPath></ds:Transform>"#
4392 } else {
4393 ""
4394 };
4395 format!(
4396 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>"##
4397 )
4398 })
4399 .collect::<String>();
4400 let manifest_reference = |id: &str| {
4401 format!(
4402 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>"##
4403 )
4404 };
4405 let xml = format!(
4406 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>"##,
4407 manifest_reference("payload"),
4408 manifest_reference("payload")
4409 );
4410 let policy = crate::policy::SigningPolicy {
4411 manifest_processing: crate::policy::ManifestProcessing::Process,
4412 ..crate::policy::SigningPolicy::default()
4413 };
4414
4415 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4416 let error = fill_reference_digest_values_in_dependency_order(
4417 &mut document,
4418 TransformOptions::default(),
4419 &policy,
4420 crate::provider::default_provider(),
4421 &mut SigningOperationBudgets::default(),
4422 0,
4423 &[],
4424 )
4425 .expect_err("Manifest reparse must not reset the XPath parse budget");
4426
4427 assert!(
4428 matches!(
4429 &error,
4430 SigningDigestError::Transform(TransformError::Policy(
4431 crate::policy::PolicyViolation::ResourceLimit {
4432 resource: "XPath expressions",
4433 ..
4434 }
4435 ))
4436 ),
4437 "expected the shared XPath budget error, got: {error:?}"
4438 );
4439 }
4440
4441 #[test]
4442 fn dependency_levels_share_the_xml_parse_work_budget() {
4443 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>"##;
4447 let policy = crate::policy::SigningPolicy {
4448 manifest_processing: crate::policy::ManifestProcessing::Process,
4449 ..crate::policy::SigningPolicy::default()
4450 };
4451 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4452 let mut budgets = SigningOperationBudgets::from_resources(&policy.resources);
4453
4454 fill_reference_digest_values_in_dependency_order(
4455 &mut document,
4456 TransformOptions::default(),
4457 &policy,
4458 crate::provider::default_provider(),
4459 &mut budgets,
4460 0,
4461 &[],
4462 )
4463 .expect("the default cumulative budget must cover nested dependencies");
4464 let consumed = budgets.transforms.xml_parse_work().consumed();
4465 assert!(
4466 consumed > xml.len().saturating_mul(6),
4467 "analysis and dependency reparses must all be charged"
4468 );
4469
4470 let mut constrained_policy = policy;
4471 constrained_policy.resources.max_xml_parse_work_bytes = consumed - 1;
4472 let mut constrained_document = XmlDocument::parse(xml).expect("fixture must parse");
4473 let mut constrained_budgets =
4474 SigningOperationBudgets::from_resources(&constrained_policy.resources);
4475 let error = fill_reference_digest_values_in_dependency_order(
4476 &mut constrained_document,
4477 TransformOptions::default(),
4478 &constrained_policy,
4479 crate::provider::default_provider(),
4480 &mut constrained_budgets,
4481 0,
4482 &[],
4483 )
4484 .expect_err("one byte below measured work must fail closed");
4485
4486 assert!(matches!(
4487 error,
4488 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
4489 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4490 maximum,
4491 actual,
4492 }) if maximum == consumed - 1 && actual >= consumed
4493 ));
4494 }
4495
4496 #[test]
4497 fn final_signed_info_parse_consumes_the_signing_xpath_budget() {
4498 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>"##;
4502 let mut policy = crate::policy::SigningPolicy::default();
4503 policy.resources.max_xpath_expressions = 3;
4504
4505 let error = SignContext::new(&RejectingSigningKey)
4506 .policy(policy)
4507 .sign_template(xml)
4508 .expect_err("the final SignedInfo parse must not reset the XPath budget");
4509
4510 assert!(
4511 matches!(
4512 error,
4513 SigningError::Policy(PolicyViolation::ResourceLimit {
4514 resource: crate::policy::resource_name::XPATH_EXPRESSIONS,
4515 maximum: 3,
4516 ..
4517 })
4518 ),
4519 "expected the shared XPath parse budget error, got: {error:?}"
4520 );
4521 }
4522
4523 #[test]
4524 fn signing_initial_parse_consumes_the_operation_xml_parse_budget() {
4525 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>"##;
4529 let mut policy = crate::policy::SigningPolicy::default();
4530 policy.resources.max_xml_parse_work_bytes = 0;
4531
4532 let error = SignContext::new(&RejectingSigningKey)
4533 .policy(policy)
4534 .sign_template(xml)
4535 .expect_err("a zero parse-work budget must reject the initial parse");
4536
4537 assert!(matches!(
4538 error,
4539 SigningError::Policy(PolicyViolation::ResourceLimit {
4540 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4541 maximum: 0,
4542 actual,
4543 }) if actual == xml.len()
4544 ));
4545 }
4546
4547 #[test]
4548 fn signing_string_entry_point_enforces_policy_depth() {
4549 let mut policy = crate::policy::SigningPolicy::default();
4552 policy.resources.max_xml_depth = 2;
4553 let xml = "<root><child><leaf/></child></root>";
4554
4555 assert!(matches!(
4556 SignContext::new(&RejectingSigningKey)
4557 .policy(policy)
4558 .sign_template(xml),
4559 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4560 resource: crate::policy::resource_name::XML_DEPTH,
4561 maximum: 2,
4562 actual: 3,
4563 }))
4564 ));
4565 }
4566
4567 #[test]
4568 fn builder_append_reports_policy_depth() {
4569 let mut policy = crate::policy::SigningPolicy::default();
4572 policy.resources.max_xml_depth = 4;
4573 let builder = SignatureBuilder::new(
4574 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4575 SignatureAlgorithm::RsaSha256,
4576 )
4577 .add_reference(crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri(""));
4578
4579 let result = SignContext::new(&FixedRsaSigningKey)
4580 .policy(policy)
4581 .sign_with_builder("<root/>", &builder);
4582 assert!(
4583 matches!(
4584 result,
4585 Err(SigningError::Policy(PolicyViolation::ResourceLimit {
4586 resource: crate::policy::resource_name::XML_DEPTH,
4587 maximum: 4,
4588 actual: 5,
4589 }))
4590 ),
4591 "unexpected builder depth result: {result:?}"
4592 );
4593 }
4594
4595 #[test]
4596 fn owned_signing_staged_copies_preserve_policy_errors() {
4597 let mut policy = crate::policy::SigningPolicy::default();
4600 policy.resources.max_xml_parse_work_bytes = 0;
4601 let context = SignContext::new(&RejectingSigningKey).policy(policy);
4602
4603 let mut template_document = XmlDocument::parse("<root/>").expect("fixture must parse");
4604 let template_before = template_document.as_xml().to_owned();
4605 let error = context
4606 .sign_document(&mut template_document)
4607 .expect_err("the staged template copy must exhaust the operation budget");
4608 assert!(matches!(
4609 error,
4610 SigningError::Policy(PolicyViolation::ResourceLimit {
4611 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4612 maximum: 0,
4613 actual,
4614 }) if actual == template_before.len()
4615 ));
4616 assert_eq!(template_document.as_xml(), template_before);
4617 assert_eq!(template_document.generation(), 0);
4618
4619 let mut builder_document = XmlDocument::parse("<root/>").expect("fixture must parse");
4620 let builder_before = builder_document.as_xml().to_owned();
4621 let builder = SignatureBuilder::new(
4622 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4623 SignatureAlgorithm::RsaSha256,
4624 );
4625 let error = context
4626 .sign_document_with_builder(&mut builder_document, &builder)
4627 .expect_err("the staged builder copy must exhaust the operation budget");
4628 assert!(matches!(
4629 error,
4630 SigningError::Policy(PolicyViolation::ResourceLimit {
4631 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4632 maximum: 0,
4633 actual,
4634 }) if actual == builder_before.len()
4635 ));
4636 assert_eq!(builder_document.as_xml(), builder_before);
4637 assert_eq!(builder_document.generation(), 0);
4638 }
4639
4640 #[test]
4641 fn owned_signing_commits_the_validated_stage_without_reparsing() {
4642 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>"##;
4646 let policy = crate::policy::SigningPolicy::default();
4647 let context = SignContext::new(&FixedRsaSigningKey).policy(policy.clone());
4648 let source = XmlDocument::parse(xml).expect("fixture must parse");
4649 let mut measured = SigningOperationBudgets::from_resources(&policy.resources);
4650 let mut staged = source
4651 .staged_copy_with_budget(
4652 DocumentParseSettings::from_policy(&policy.xml, &policy.resources),
4653 measured.transforms.xml_parse_work(),
4654 )
4655 .expect("staging must parse");
4656 context
4657 .sign_document_in_place(&mut staged, &mut measured)
4658 .expect("staged signing must succeed");
4659 let exact_stage_work = measured.transforms.xml_parse_work().consumed();
4660
4661 let mut constrained_policy = policy;
4662 constrained_policy.resources.max_xml_parse_work_bytes = exact_stage_work;
4663 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4664 SignContext::new(&FixedRsaSigningKey)
4665 .policy(constrained_policy)
4666 .sign_document(&mut document)
4667 .expect("commit must not parse the validated stage again");
4668
4669 assert_eq!(document.generation(), 1);
4670 assert!(!document.as_xml().contains("<ds:DigestValue/>"));
4671 assert!(!document.as_xml().contains("<ds:SignatureValue/>"));
4672 }
4673
4674 #[test]
4675 fn builder_signing_fits_the_document_to_parse_work_ratio() {
4676 let padding = "x".repeat(64 * 1024);
4682 let xml = format!("<root><payload Id=\"payload\"/><padding>{padding}</padding></root>");
4683 let builder = SignatureBuilder::new(
4684 crate::c14n::C14nAlgorithm::new(crate::c14n::C14nMode::Exclusive1_0, false),
4685 SignatureAlgorithm::RsaSha256,
4686 )
4687 .add_reference(
4688 crate::xmldsig::ReferenceBuilder::new(DigestAlgorithm::Sha256).uri("#payload"),
4689 );
4690 let maximum_document_bytes = xml.len() + 4 * 1024;
4691 let mut policy = crate::policy::SigningPolicy::default();
4692 policy.resources.max_xml_document_bytes = maximum_document_bytes;
4693 policy.resources.max_xml_parse_work_bytes =
4694 maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING;
4695
4696 let mut measurement_policy = policy.clone();
4697 measurement_policy.resources.max_xml_parse_work_bytes =
4698 crate::hard_limits::XML_PARSE_WORK_BYTE_CEILING;
4699 let measurement_context =
4700 SignContext::new(&FixedRsaSigningKey).policy(measurement_policy.clone());
4701 let mut measurement_budgets =
4702 SigningOperationBudgets::from_resources(&measurement_policy.resources);
4703 let mut measurement_document = XmlDocument::parse_with_settings_and_budget(
4704 xml.clone(),
4705 DocumentParseSettings::from_policy(
4706 &measurement_policy.xml,
4707 &measurement_policy.resources,
4708 ),
4709 measurement_budgets.transforms.xml_parse_work(),
4710 )
4711 .expect("measurement input must parse");
4712 measurement_context
4713 .sign_document_with_builder_in_place(
4714 &mut measurement_document,
4715 &builder,
4716 &mut measurement_budgets,
4717 )
4718 .expect("measurement signing must succeed");
4719 let consumed = measurement_budgets.transforms.xml_parse_work().consumed();
4720 assert!(
4721 consumed <= maximum_document_bytes * crate::hard_limits::XML_PARSE_WORK_PASS_CEILING,
4722 "builder signing consumed {consumed} bytes for a {maximum_document_bytes}-byte ceiling"
4723 );
4724
4725 let signed = SignContext::new(&FixedRsaSigningKey)
4726 .policy(policy.clone())
4727 .sign_with_builder(&xml, &builder)
4728 .expect("string builder signing must fit the advertised parse-work ratio");
4729 assert!(signed.contains("DigestValue>"));
4730 assert!(signed.contains("SignatureValue>"));
4731
4732 let mut owned = XmlDocument::parse(&xml).expect("fixture must parse");
4733 SignContext::new(&FixedRsaSigningKey)
4734 .policy(policy)
4735 .sign_document_with_builder(&mut owned, &builder)
4736 .expect("owned builder signing must fit the advertised parse-work ratio");
4737 assert!(owned.as_xml().contains("DigestValue>"));
4738 assert!(owned.as_xml().contains("SignatureValue>"));
4739 }
4740
4741 #[test]
4742 fn dtd_capable_staged_copy_charges_both_parser_passes() {
4743 let xml = "<root/>";
4746 let mut parsing_policy = crate::policy::SigningPolicy::default();
4747 parsing_policy.xml.allow_internal_dtd = true;
4748 let document = XmlDocument::parse_with_policy(xml, &parsing_policy)
4749 .expect("the fixture must retain DTD-capable parse settings");
4750
4751 parsing_policy.resources.max_xml_parse_work_bytes = xml.len();
4752 let budget = XmlParseWorkBudget::from_resources(&parsing_policy.resources);
4753 assert!(matches!(
4754 document.staged_copy_with_budget(DocumentParseSettings::default(), &budget),
4755 Err(XmlDocumentError::Policy(PolicyViolation::ResourceLimit {
4756 resource: crate::policy::resource_name::XML_PARSE_WORK_BYTES,
4757 maximum,
4758 actual,
4759 })) if maximum == xml.len() && actual == xml.len() * 2
4760 ));
4761 }
4762
4763 #[test]
4764 fn owned_signing_mappers_preserve_document_size_policy_errors() {
4765 let maximum = 8;
4768 let actual = 9;
4769 assert!(matches!(
4770 map_owned_document_mutation_error(XmlDocumentError::DocumentTooLarge {
4771 maximum,
4772 actual,
4773 }),
4774 SigningError::Policy(PolicyViolation::ResourceLimit {
4775 resource: crate::policy::resource_name::XML_DOCUMENT,
4776 maximum: 8,
4777 actual: 9,
4778 })
4779 ));
4780 assert!(matches!(
4781 map_owned_document_digest_mutation_error(XmlDocumentError::DocumentTooLarge {
4782 maximum,
4783 actual,
4784 }),
4785 SigningDigestError::Policy(PolicyViolation::ResourceLimit {
4786 resource: crate::policy::resource_name::XML_DOCUMENT,
4787 maximum: 8,
4788 actual: 9,
4789 })
4790 ));
4791 }
4792
4793 #[test]
4794 fn signing_rejects_xpath_control_dependencies_on_digest_values() {
4795 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>"##;
4799
4800 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4801 let error = fill_reference_digest_values_in_dependency_order(
4802 &mut document,
4803 TransformOptions::default(),
4804 &crate::policy::SigningPolicy::default(),
4805 crate::provider::default_provider(),
4806 &mut SigningOperationBudgets::default(),
4807 0,
4808 &[],
4809 )
4810 .expect_err("mutable XPath control dependencies must fail closed");
4811
4812 assert!(
4813 matches!(
4814 &error,
4815 SigningDigestError::InvalidStructure(message)
4816 if message.contains("dependency cycle")
4817 ),
4818 "expected a dependency-cycle rejection, got: {error:?}"
4819 );
4820 }
4821
4822 #[test]
4823 fn signing_allows_payload_local_xpath_value_predicates() {
4824 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>"##;
4827
4828 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4829 fill_reference_digest_values_in_dependency_order(
4830 &mut document,
4831 TransformOptions::default(),
4832 &crate::policy::SigningPolicy::default(),
4833 crate::provider::default_provider(),
4834 &mut SigningOperationBudgets::default(),
4835 0,
4836 &[],
4837 )
4838 .expect("payload-local XPath predicates must not depend on Signature values");
4839
4840 assert_ne!(document.as_xml(), xml);
4841 }
4842
4843 #[test]
4844 fn signing_rejects_references_that_retain_signature_value() {
4845 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>"##;
4849
4850 let mut document = XmlDocument::parse(xml).expect("fixture must parse");
4851 let error = fill_reference_digest_values_in_dependency_order(
4852 &mut document,
4853 TransformOptions::default(),
4854 &crate::policy::SigningPolicy::default(),
4855 crate::provider::default_provider(),
4856 &mut SigningOperationBudgets::default(),
4857 0,
4858 &[],
4859 )
4860 .expect_err("a mutable SignatureValue dependency must fail before signing");
4861
4862 assert!(
4863 matches!(
4864 &error,
4865 SigningDigestError::InvalidStructure(message)
4866 if message.contains("SignatureValue") && message.contains("cycle")
4867 ),
4868 "expected a SignatureValue dependency-cycle rejection, got: {error:?}"
4869 );
4870 }
4871
4872 #[test]
4873 fn second_edition_c14n_materialization_handles_both_reference_shapes() {
4874 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>"##;
4877 let document = XmlDocument::parse(xml).expect("fixture must parse");
4878 let candidate = materialize_second_edition_c14n11_candidate(
4879 &document,
4880 0,
4881 &crate::policy::SigningPolicy::default(),
4882 false,
4883 )
4884 .expect("materialization planning must succeed")
4885 .expect("both node-set chains require explicit canonicalization");
4886
4887 assert_eq!(
4888 candidate
4889 .matches(SECOND_EDITION_GENERATION_C14N_URI)
4890 .count(),
4891 3
4892 );
4893 assert!(candidate.contains("<ds:Transforms><ds:Transform"));
4894 XmlDocument::parse(candidate).expect("materialized candidate must remain well-formed");
4895 }
4896
4897 #[test]
4898 fn second_edition_c14n_materialization_is_idempotent_for_binary_chains() {
4899 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>"##;
4902 let document = XmlDocument::parse(xml).expect("fixture must parse");
4903 let candidate = materialize_second_edition_c14n11_candidate(
4904 &document,
4905 0,
4906 &crate::policy::SigningPolicy::default(),
4907 false,
4908 )
4909 .expect("materialization planning must succeed");
4910
4911 assert!(candidate.is_none());
4912 }
4913}