1use base64::Engine;
9use p256::ecdsa::{Signature as P256Signature, SigningKey as P256SigningKey};
10use p256::pkcs8::{DecodePrivateKey, EncodePublicKey};
11use p384::ecdsa::{Signature as P384Signature, SigningKey as P384SigningKey};
12use roxmltree::{Document, Node};
13use rsa::RsaPrivateKey;
14use rsa::pkcs1v15::Signature as RsaPkcs1v15Signature;
15use rsa::pkcs1v15::SigningKey as RsaPkcs1v15SigningKey;
16use rsa::signature::{RandomizedSigner, SignatureEncoding};
17use rsa::traits::PublicKeyParts;
18use sha2::{Sha256, Sha384, Sha512};
19use signature::hazmat::PrehashSigner;
20use std::collections::HashSet;
21use x509_parser::prelude::FromDer;
22
23use crate::c14n::{canonicalize_bounded_with_xml_base_budget, is_output_limit_error};
24
25use super::builder::{SignatureBuilder, SignatureBuilderError};
26use super::digest::DigestAlgorithm;
27use super::mutation::{
28 XmlMutationError, append_signature_to_root_with_options, fill_key_info_with_options,
29 fill_signature_value_with_options, fill_signed_info_digest_values,
30 fill_signed_info_digest_values_with_options,
31};
32use super::parse::{
33 MAX_REFERENCES_PER_SIGNATURE, SignatureAlgorithm, XMLDSIG_NS, parse_signed_info,
34};
35use super::transforms::{
36 DEFAULT_IMPLICIT_C14N_URI, Transform, TransformExecutionBudget, TransformOptions,
37 XPathHereSemantics, XPathSignatureParseBudget, execute_transforms_with_options_and_budget,
38 parse_transforms_with_budget, transform_chain_produces_binary,
39};
40use super::types::TransformError;
41use super::uri::UriReferenceResolver;
42
43#[derive(Debug, Clone, PartialEq, Eq)]
45#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
46pub struct ComputedReferenceDigest {
47 pub index: usize,
49 pub uri: String,
51 pub digest_method: DigestAlgorithm,
53 pub digest_value: String,
55}
56
57#[derive(Debug, thiserror::Error)]
59pub enum SigningDigestError {
60 #[error("cryptographic provider error: {0}")]
62 Provider(#[from] crate::provider::ProviderError),
63
64 #[error("signing policy violation: {0}")]
66 Policy(#[from] crate::policy::PolicyViolation),
67
68 #[error("XML parse error: {0}")]
70 XmlParse(#[from] roxmltree::Error),
71
72 #[error("missing required element: <{element}>")]
74 MissingElement {
75 element: &'static str,
77 },
78
79 #[error("invalid signing template: {0}")]
81 InvalidStructure(String),
82
83 #[error("unsupported digest algorithm: {uri}")]
85 UnsupportedAlgorithm {
86 uri: String,
88 },
89
90 #[error("digest algorithm is disabled for signing: {uri}")]
92 SigningAlgorithmDisabled {
93 uri: &'static str,
95 },
96
97 #[error("reference processing error: {0}")]
99 Transform(#[from] TransformError),
100
101 #[error("XML mutation error: {0}")]
103 XmlMutation(#[from] XmlMutationError),
104}
105
106#[derive(Debug, thiserror::Error)]
108pub enum SigningError {
109 #[error("signing policy violation: {0}")]
111 Policy(#[from] crate::policy::PolicyViolation),
112
113 #[error("signing digest pass failed: {0}")]
115 Digest(#[from] SigningDigestError),
116
117 #[error("failed to parse SignedInfo after digest fill: {0}")]
119 ParseSignedInfo(#[from] super::parse::ParseError),
120
121 #[error("SignedInfo canonicalization failed: {0}")]
123 Canonicalization(#[from] crate::c14n::C14nError),
124
125 #[error("signing key error: {0}")]
127 Key(#[from] SigningKeyError),
128
129 #[error("signature output must be {expected} bytes, got {actual}")]
131 InvalidSignatureOutputLength {
132 expected: usize,
134 actual: usize,
136 },
137
138 #[error("XML mutation error: {0}")]
140 XmlMutation(#[from] XmlMutationError),
141
142 #[error("KeyInfo writer error: {0}")]
144 KeyInfo(#[from] KeyInfoWriteError),
145
146 #[error("signature template error: {0}")]
148 Template(#[from] SignatureBuilderError),
149}
150
151#[derive(Debug, thiserror::Error)]
153#[non_exhaustive]
154pub enum SigningKeyError {
155 #[error("cryptographic provider error: {0}")]
157 Provider(#[from] crate::provider::ProviderError),
158
159 #[error("invalid PEM private key")]
161 InvalidKeyPem,
162
163 #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
165 InvalidKeyFormat {
166 label: String,
168 },
169
170 #[error("invalid PKCS#8 private key DER")]
172 InvalidKeyDer,
173
174 #[error("signing key does not support algorithm: {uri}")]
176 UnsupportedAlgorithm {
177 uri: String,
179 },
180
181 #[error("private-key signing operation failed")]
183 SigningFailed,
184
185 #[error("failed to encode signing public key as SPKI DER")]
187 PublicKeyEncodingFailed,
188
189 #[error("invalid signing public-key metadata")]
191 InvalidPublicKeyInfo,
192}
193
194#[derive(Debug, Clone, PartialEq, Eq)]
196#[non_exhaustive]
197pub enum SigningPublicKeyInfo {
198 Rsa {
200 spki_der: Vec<u8>,
202 modulus: Vec<u8>,
204 exponent: Vec<u8>,
206 },
207 Ec {
209 spki_der: Vec<u8>,
211 curve_oid: &'static str,
213 public_key: Vec<u8>,
215 },
216}
217
218impl SigningPublicKeyInfo {
219 #[must_use]
221 pub fn spki_der(&self) -> &[u8] {
222 match self {
223 Self::Rsa { spki_der, .. } | Self::Ec { spki_der, .. } => spki_der,
224 }
225 }
226}
227
228fn expected_signature_output_len(
229 key: &dyn SigningKey,
230 algorithm: SignatureAlgorithm,
231 policy: &crate::policy::SigningPolicy,
232) -> Result<usize, SigningError> {
233 let public_key = key.public_key_info()?;
234 let expected = match (algorithm, public_key) {
235 (
236 SignatureAlgorithm::RsaSha1
237 | SignatureAlgorithm::RsaSha256
238 | SignatureAlgorithm::RsaSha384
239 | SignatureAlgorithm::RsaSha512,
240 SigningPublicKeyInfo::Rsa {
241 modulus, exponent, ..
242 },
243 ) => policy
244 .rsa_keys
245 .validate_components("signing", &modulus, &exponent)?,
246 (
247 SignatureAlgorithm::EcdsaSha256 | SignatureAlgorithm::EcdsaSha384,
248 SigningPublicKeyInfo::Ec { public_key, .. },
249 ) if public_key.first() == Some(&0x04)
250 && public_key.len() > 1
251 && (public_key.len() - 1).is_multiple_of(2) =>
252 {
253 public_key.len() - 1
256 }
257 (
258 SignatureAlgorithm::RsaSha1
259 | SignatureAlgorithm::RsaSha256
260 | SignatureAlgorithm::RsaSha384
261 | SignatureAlgorithm::RsaSha512,
262 SigningPublicKeyInfo::Ec { .. },
263 )
264 | (
265 SignatureAlgorithm::EcdsaSha256 | SignatureAlgorithm::EcdsaSha384,
266 SigningPublicKeyInfo::Rsa { .. },
267 ) => {
268 return Err(SigningKeyError::UnsupportedAlgorithm {
269 uri: algorithm.uri().to_owned(),
270 }
271 .into());
272 }
273 _ => return Err(SigningKeyError::InvalidPublicKeyInfo.into()),
274 };
275 Ok(expected)
276}
277
278fn validate_signature_output(expected: usize, signature: &[u8]) -> Result<(), SigningError> {
279 if signature.len() != expected {
280 return Err(SigningError::InvalidSignatureOutputLength {
281 expected,
282 actual: signature.len(),
283 });
284 }
285 Ok(())
286}
287
288pub trait SigningKey {
290 fn sign(
292 &self,
293 algorithm: SignatureAlgorithm,
294 canonical_signed_info: &[u8],
295 ) -> Result<Vec<u8>, SigningKeyError>;
296
297 fn sign_with_provider(
302 &self,
303 provider: &dyn crate::provider::CryptoProvider,
304 algorithm: SignatureAlgorithm,
305 canonical_signed_info: &[u8],
306 ) -> Result<Vec<u8>, SigningKeyError> {
307 let _ = provider;
308 self.sign(algorithm, canonical_signed_info)
309 }
310
311 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
313}
314
315pub trait KeyInfoWriter {
317 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
319}
320
321#[derive(Debug, thiserror::Error)]
323#[non_exhaustive]
324pub enum KeyInfoWriteError {
325 #[error("invalid PEM certificate")]
327 InvalidCertificatePem,
328
329 #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
331 InvalidCertificateFormat {
332 label: String,
334 },
335
336 #[error("invalid X.509 certificate DER")]
338 InvalidCertificateDer,
339
340 #[error("signing key public-key extraction failed: {0}")]
342 SigningKey(#[from] SigningKeyError),
343
344 #[error("X.509 certificate public key does not match signing key")]
346 CertificateKeyMismatch,
347}
348
349pub struct X509CertificateKeyInfoWriter {
351 certificate_der: Vec<u8>,
352}
353
354impl X509CertificateKeyInfoWriter {
355 pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
357 let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
358 .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
359 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
360 return Err(KeyInfoWriteError::InvalidCertificatePem);
361 }
362 if pem.label != "CERTIFICATE" {
363 return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
364 }
365 Self::from_der(&pem.contents)
366 }
367
368 pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
370 let (rest, _) = x509_parser::certificate::X509Certificate::from_der(certificate_der)
371 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
372 if !rest.is_empty() {
373 return Err(KeyInfoWriteError::InvalidCertificateDer);
374 }
375 Ok(Self {
376 certificate_der: certificate_der.to_vec(),
377 })
378 }
379}
380
381impl KeyInfoWriter for X509CertificateKeyInfoWriter {
382 fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
383 let (rest, certificate) =
384 x509_parser::certificate::X509Certificate::from_der(&self.certificate_der)
385 .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
386 if !rest.is_empty() {
387 return Err(KeyInfoWriteError::InvalidCertificateDer);
388 }
389 let signing_public_key = signing_key.public_key_info()?;
390 if certificate.public_key().raw != signing_public_key.spki_der() {
391 return Err(KeyInfoWriteError::CertificateKeyMismatch);
392 }
393
394 let certificate_b64 =
395 base64::engine::general_purpose::STANDARD.encode(&self.certificate_der);
396 Ok(format!(
397 "<X509Data xmlns=\"{XMLDSIG_NS}\"><X509Certificate>{certificate_b64}</X509Certificate></X509Data>"
398 ))
399 }
400}
401
402pub struct RsaSigningKey {
404 key: RsaPrivateKey,
405}
406
407impl RsaSigningKey {
408 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
410 let private_key_der = parse_private_key_pem(private_key_pem)?;
411 Self::from_pkcs8_der(&private_key_der)
412 }
413
414 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
416 let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
417 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
418 Ok(Self { key })
419 }
420}
421
422impl SigningKey for RsaSigningKey {
423 fn sign(
424 &self,
425 algorithm: SignatureAlgorithm,
426 canonical_signed_info: &[u8],
427 ) -> Result<Vec<u8>, SigningKeyError> {
428 self.sign_with_provider(
429 crate::provider::default_provider(),
430 algorithm,
431 canonical_signed_info,
432 )
433 }
434
435 fn sign_with_provider(
436 &self,
437 provider: &dyn crate::provider::CryptoProvider,
438 algorithm: SignatureAlgorithm,
439 canonical_signed_info: &[u8],
440 ) -> Result<Vec<u8>, SigningKeyError> {
441 match algorithm {
442 SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
443 provider,
444 RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
445 canonical_signed_info,
446 ),
447 SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
448 provider,
449 RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
450 canonical_signed_info,
451 ),
452 SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
453 provider,
454 RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
455 canonical_signed_info,
456 ),
457 _ => Err(SigningKeyError::UnsupportedAlgorithm {
458 uri: algorithm.uri().to_string(),
459 }),
460 }
461 }
462
463 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
464 let public_key = self.key.to_public_key();
465 let spki_der = public_key
466 .to_public_key_der()
467 .map(|doc| doc.as_bytes().to_vec())
468 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
469 Ok(SigningPublicKeyInfo::Rsa {
470 spki_der,
471 modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
472 exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
473 })
474 }
475}
476
477fn sign_rsa_pkcs1v15_with_rng(
478 provider: &dyn crate::provider::CryptoProvider,
479 key: impl RandomizedSigner<RsaPkcs1v15Signature>,
480 canonical_signed_info: &[u8],
481) -> Result<Vec<u8>, SigningKeyError> {
482 let mut rng = crate::provider::ProviderRng(provider);
483 let signature = key
484 .try_sign_with_rng(&mut rng, canonical_signed_info)
485 .map_err(|_| SigningKeyError::SigningFailed)?;
486 Ok(signature.to_vec())
487}
488
489pub struct EcdsaP256SigningKey {
491 key: P256SigningKey,
492}
493
494impl EcdsaP256SigningKey {
495 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
497 let private_key_der = parse_private_key_pem(private_key_pem)?;
498 Self::from_pkcs8_der(&private_key_der)
499 }
500
501 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
503 let key = P256SigningKey::from_pkcs8_der(private_key_der)
504 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
505 Ok(Self { key })
506 }
507}
508
509impl SigningKey for EcdsaP256SigningKey {
510 fn sign(
511 &self,
512 algorithm: SignatureAlgorithm,
513 canonical_signed_info: &[u8],
514 ) -> Result<Vec<u8>, SigningKeyError> {
515 self.sign_with_provider(
516 crate::provider::default_provider(),
517 algorithm,
518 canonical_signed_info,
519 )
520 }
521
522 fn sign_with_provider(
523 &self,
524 provider: &dyn crate::provider::CryptoProvider,
525 algorithm: SignatureAlgorithm,
526 canonical_signed_info: &[u8],
527 ) -> Result<Vec<u8>, SigningKeyError> {
528 let digest_algorithm = match algorithm {
529 SignatureAlgorithm::EcdsaSha256 => DigestAlgorithm::Sha256,
530 SignatureAlgorithm::EcdsaSha384 => DigestAlgorithm::Sha384,
531 _ => {
532 return Err(SigningKeyError::UnsupportedAlgorithm {
533 uri: algorithm.uri().to_string(),
534 });
535 }
536 };
537 let prehash =
538 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
539 let signature: P256Signature = self
540 .key
541 .sign_prehash(&prehash)
542 .map_err(|_| SigningKeyError::SigningFailed)?;
543 Ok(signature.to_bytes().to_vec())
544 }
545
546 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
547 let verifying_key = self.key.verifying_key();
548 let spki_der = verifying_key
549 .to_public_key_der()
550 .map(|doc| doc.as_bytes().to_vec())
551 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
552 Ok(SigningPublicKeyInfo::Ec {
553 spki_der,
554 curve_oid: "1.2.840.10045.3.1.7",
555 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
556 })
557 }
558}
559
560pub struct EcdsaP384SigningKey {
562 key: P384SigningKey,
563}
564
565impl EcdsaP384SigningKey {
566 pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
568 let private_key_der = parse_private_key_pem(private_key_pem)?;
569 Self::from_pkcs8_der(&private_key_der)
570 }
571
572 pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
574 let key = P384SigningKey::from_pkcs8_der(private_key_der)
575 .map_err(|_| SigningKeyError::InvalidKeyDer)?;
576 Ok(Self { key })
577 }
578}
579
580impl SigningKey for EcdsaP384SigningKey {
581 fn sign(
582 &self,
583 algorithm: SignatureAlgorithm,
584 canonical_signed_info: &[u8],
585 ) -> Result<Vec<u8>, SigningKeyError> {
586 self.sign_with_provider(
587 crate::provider::default_provider(),
588 algorithm,
589 canonical_signed_info,
590 )
591 }
592
593 fn sign_with_provider(
594 &self,
595 provider: &dyn crate::provider::CryptoProvider,
596 algorithm: SignatureAlgorithm,
597 canonical_signed_info: &[u8],
598 ) -> Result<Vec<u8>, SigningKeyError> {
599 let digest_algorithm = match algorithm {
600 SignatureAlgorithm::EcdsaSha256 => DigestAlgorithm::Sha256,
601 SignatureAlgorithm::EcdsaSha384 => DigestAlgorithm::Sha384,
602 _ => {
603 return Err(SigningKeyError::UnsupportedAlgorithm {
604 uri: algorithm.uri().to_string(),
605 });
606 }
607 };
608 let prehash =
609 super::compute_digest_with_provider(provider, digest_algorithm, canonical_signed_info)?;
610 let signature: P384Signature = self
611 .key
612 .sign_prehash(&prehash)
613 .map_err(|_| SigningKeyError::SigningFailed)?;
614 Ok(signature.to_bytes().to_vec())
615 }
616
617 fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
618 let verifying_key = self.key.verifying_key();
619 let spki_der = verifying_key
620 .to_public_key_der()
621 .map(|doc| doc.as_bytes().to_vec())
622 .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
623 Ok(SigningPublicKeyInfo::Ec {
624 spki_der,
625 curve_oid: "1.3.132.0.34",
626 public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
627 })
628 }
629}
630
631pub struct SignContext<'a> {
633 signing_key: &'a dyn SigningKey,
634 key_info_writer: Option<&'a dyn KeyInfoWriter>,
635 policy: crate::policy::SigningPolicy,
636 provider: &'a dyn crate::provider::CryptoProvider,
637}
638
639impl<'a> SignContext<'a> {
640 pub fn new(signing_key: &'a dyn SigningKey) -> Self {
642 Self {
643 signing_key,
644 key_info_writer: None,
645 policy: crate::policy::SigningPolicy::default(),
646 provider: crate::provider::default_provider(),
647 }
648 }
649
650 #[must_use]
652 pub fn policy(mut self, policy: crate::policy::SigningPolicy) -> Self {
653 self.policy = policy;
654 self
655 }
656
657 #[must_use]
659 pub fn provider(mut self, provider: &'a dyn crate::provider::CryptoProvider) -> Self {
660 self.provider = provider;
661 self
662 }
663
664 #[must_use]
666 pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
667 self.key_info_writer = Some(writer);
668 self
669 }
670
671 #[must_use]
677 pub fn xpath_here_semantics(mut self, semantics: XPathHereSemantics) -> Self {
678 self.policy.xpath_here_semantics = semantics;
679 self
680 }
681
682 pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
689 self.policy.validate()?;
690 self.policy.resources.validate_xml_document_len(xml.len())?;
691 let execution_budget = TransformExecutionBudget::from_resources(&self.policy.resources);
692 let transform_options = TransformOptions::default()
693 .allow_internal_dtd(self.policy.xml.allow_internal_dtd)
694 .xpath_here_semantics(self.policy.xpath_here_semantics);
695 let with_digests = fill_reference_digest_values_with_options(
696 xml,
697 transform_options,
698 Some(&self.policy),
699 self.provider,
700 &execution_budget,
701 )?;
702 self.policy
703 .resources
704 .validate_xml_document_len(with_digests.len())?;
705 let (algorithm, canonical_signed_info) =
706 canonicalize_signed_info(&with_digests, &self.policy, &execution_budget)?;
707 execution_budget
708 .charge_c14n_output(canonical_signed_info.len())
709 .map_err(SigningDigestError::Transform)?;
710 if !algorithm.signing_allowed()
711 || self
712 .policy
713 .signature_algorithms
714 .as_ref()
715 .is_some_and(|allowed| !allowed.contains(&algorithm))
716 {
717 return Err(crate::policy::PolicyViolation::Algorithm {
718 operation: "signing",
719 algorithm: algorithm.uri().to_string(),
720 }
721 .into());
722 }
723 let expected_signature_len =
724 expected_signature_output_len(self.signing_key, algorithm, &self.policy)?;
725 let signature_value =
726 self.provider
727 .sign(self.signing_key, algorithm, &canonical_signed_info)?;
728 validate_signature_output(expected_signature_len, &signature_value)?;
729 let signature_b64 = base64::engine::general_purpose::STANDARD.encode(signature_value);
730 let signed =
731 fill_signature_value_with_options(&with_digests, &signature_b64, Some(&self.policy))?;
732 self.policy
733 .resources
734 .validate_xml_document_len(signed.len())?;
735 if let Some(writer) = self.key_info_writer {
736 let key_info_content = writer.write_key_info(self.signing_key)?;
737 let signed =
738 fill_key_info_with_options(&signed, &key_info_content, Some(&self.policy))?;
739 self.policy
740 .resources
741 .validate_xml_document_len(signed.len())?;
742 Ok(signed)
743 } else {
744 Ok(signed)
745 }
746 }
747
748 pub fn sign_with_builder(
750 &self,
751 xml: &str,
752 builder: &SignatureBuilder,
753 ) -> Result<String, SigningError> {
754 self.policy.validate()?;
755 self.policy.resources.validate_xml_document_len(xml.len())?;
756 let template = builder.build_template()?;
757 let templated = append_signature_to_root_with_options(xml, &template, Some(&self.policy))?;
758 self.sign_template(&templated)
759 }
760}
761
762#[derive(Debug)]
763struct SigningReference {
764 uri: String,
765 transforms: Vec<Transform>,
766 digest_method: DigestAlgorithm,
767}
768
769pub fn compute_reference_digest_values(
776 xml: &str,
777) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
778 let execution_budget = TransformExecutionBudget::default();
779 compute_reference_digest_values_with_options(
780 xml,
781 TransformOptions::default(),
782 None,
783 crate::provider::default_provider(),
784 &execution_budget,
785 )
786}
787
788fn compute_reference_digest_values_with_options(
789 xml: &str,
790 transform_options: TransformOptions,
791 policy: Option<&crate::policy::SigningPolicy>,
792 provider: &dyn crate::provider::CryptoProvider,
793 execution_budget: &TransformExecutionBudget,
794) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
795 let doc = parse_signing_document(xml, policy)?;
796 let signature = find_signing_signature_node(&doc)?;
797 let signed_info = find_required_child(signature, "SignedInfo")?;
798 let references = parse_signing_references(signed_info)?;
799 if let Some(policy) = policy {
800 if references.len() > policy.resources.max_references {
801 return Err(crate::policy::PolicyViolation::ResourceLimit {
802 resource: "signature references",
803 maximum: policy.resources.max_references,
804 actual: references.len(),
805 }
806 .into());
807 }
808 for reference in &references {
809 if reference.transforms.len() > policy.resources.max_transforms_per_reference {
810 return Err(crate::policy::PolicyViolation::ResourceLimit {
811 resource: "reference transforms",
812 maximum: policy.resources.max_transforms_per_reference,
813 actual: reference.transforms.len(),
814 }
815 .into());
816 }
817 if let Some(allowed) = policy.transforms.as_ref() {
818 for transform in &reference.transforms {
819 let uri = transform.algorithm_uri();
820 if !allowed.contains(uri) {
821 return Err(crate::policy::PolicyViolation::Algorithm {
822 operation: "signing transform",
823 algorithm: uri.to_owned(),
824 }
825 .into());
826 }
827 }
828 let initial_binary = !reference.uri.is_empty() && !reference.uri.starts_with('#');
829 if !transform_chain_produces_binary(initial_binary, &reference.transforms)
830 && !allowed.contains(DEFAULT_IMPLICIT_C14N_URI)
831 {
832 return Err(crate::policy::PolicyViolation::Algorithm {
833 operation: "signing transform",
834 algorithm: DEFAULT_IMPLICIT_C14N_URI.to_owned(),
835 }
836 .into());
837 }
838 }
839 }
840 }
841 let resolver = UriReferenceResolver::new(&doc);
842 references
843 .into_iter()
844 .enumerate()
845 .map(|(index, reference)| {
846 if policy.is_some_and(|policy| {
847 policy
848 .digest_algorithms
849 .as_ref()
850 .is_some_and(|allowed| !allowed.contains(&reference.digest_method))
851 }) {
852 return Err(crate::policy::PolicyViolation::Algorithm {
853 operation: "signing",
854 algorithm: reference.digest_method.uri().to_string(),
855 }
856 .into());
857 }
858 let initial_data = resolver.dereference_with_budget(
859 &reference.uri,
860 execution_budget.node_set_materialization(),
861 )?;
862 let pre_digest = execute_transforms_with_options_and_budget(
863 signature,
864 initial_data,
865 &reference.transforms,
866 transform_options,
867 execution_budget,
868 )?;
869 let digest = super::compute_digest_with_provider(
870 provider,
871 reference.digest_method,
872 &pre_digest,
873 )?;
874 let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
875 Ok(ComputedReferenceDigest {
876 index,
877 uri: reference.uri,
878 digest_method: reference.digest_method,
879 digest_value,
880 })
881 })
882 .collect()
883}
884
885pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
892 let execution_budget = TransformExecutionBudget::default();
893 fill_reference_digest_values_with_options(
894 xml,
895 TransformOptions::default(),
896 None,
897 crate::provider::default_provider(),
898 &execution_budget,
899 )
900}
901
902fn fill_reference_digest_values_with_options(
903 xml: &str,
904 transform_options: TransformOptions,
905 policy: Option<&crate::policy::SigningPolicy>,
906 provider: &dyn crate::provider::CryptoProvider,
907 execution_budget: &TransformExecutionBudget,
908) -> Result<String, SigningDigestError> {
909 let digest_values = compute_reference_digest_values_with_options(
910 xml,
911 transform_options,
912 policy,
913 provider,
914 execution_budget,
915 )?
916 .into_iter()
917 .map(|digest| digest.digest_value);
918 Ok(if let Some(policy) = policy {
919 fill_signed_info_digest_values_with_options(xml, digest_values, Some(policy))?
920 } else {
921 fill_signed_info_digest_values(xml, digest_values)?
922 })
923}
924
925fn canonicalize_signed_info(
926 xml: &str,
927 policy: &crate::policy::SigningPolicy,
928 execution_budget: &TransformExecutionBudget,
929) -> Result<(SignatureAlgorithm, Vec<u8>), SigningError> {
930 let doc = parse_signing_document(xml, Some(policy)).map_err(SigningDigestError::XmlParse)?;
931 let signature = find_signing_signature_node(&doc).map_err(SigningError::Digest)?;
932 let signed_info_node =
933 find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
934 let signed_info = parse_signed_info(signed_info_node)?;
935 if policy
936 .transforms
937 .as_ref()
938 .is_some_and(|allowed| !allowed.contains(signed_info.c14n_method.uri()))
939 {
940 return Err(crate::policy::PolicyViolation::Algorithm {
941 operation: "SignedInfo canonicalization",
942 algorithm: signed_info.c14n_method.uri().to_owned(),
943 }
944 .into());
945 }
946 let signed_info_subtree: HashSet<_> = signed_info_node
947 .descendants()
948 .map(|node: Node<'_, '_>| node.id())
949 .collect();
950 let mut canonical_signed_info = Vec::new();
951 canonicalize_bounded_with_xml_base_budget(
952 &doc,
953 Some(&|node| signed_info_subtree.contains(&node.id())),
954 &signed_info.c14n_method,
955 execution_budget.remaining_c14n_output(),
956 execution_budget.xml_base_resolution(),
957 &mut canonical_signed_info,
958 )
959 .map_err(|error| {
960 if is_output_limit_error(&error) {
961 SigningError::Digest(SigningDigestError::Transform(
962 TransformError::C14nOutputTooLarge {
963 max_bytes: execution_budget.c14n_output_limit(),
964 },
965 ))
966 } else {
967 SigningError::Canonicalization(error)
968 }
969 })?;
970 Ok((signed_info.signature_method, canonical_signed_info))
971}
972
973fn parse_signing_document<'a>(
974 xml: &'a str,
975 policy: Option<&crate::policy::SigningPolicy>,
976) -> Result<Document<'a>, roxmltree::Error> {
977 super::mutation::parse_with_options(xml, policy)
978}
979
980fn parse_private_key_pem(private_key_pem: &str) -> Result<Vec<u8>, SigningKeyError> {
981 let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
982 .map_err(|_| SigningKeyError::InvalidKeyPem)?;
983 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
984 return Err(SigningKeyError::InvalidKeyPem);
985 }
986 if pem.label != "PRIVATE KEY" {
987 return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
988 }
989 Ok(pem.contents)
990}
991
992fn find_signing_signature_node<'a>(
993 doc: &'a Document<'a>,
994) -> Result<Node<'a, 'a>, SigningDigestError> {
995 doc.descendants()
996 .rfind(|node| {
997 node.is_element()
998 && node.tag_name().name() == "Signature"
999 && node.tag_name().namespace() == Some(XMLDSIG_NS)
1000 })
1001 .ok_or(SigningDigestError::MissingElement {
1002 element: "Signature",
1003 })
1004}
1005
1006fn parse_signing_references(
1007 signed_info: Node<'_, '_>,
1008) -> Result<Vec<SigningReference>, SigningDigestError> {
1009 verify_ds_element(signed_info, "SignedInfo")?;
1010 let mut children = element_children(signed_info);
1011
1012 let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
1013 element: "CanonicalizationMethod",
1014 })?;
1015 verify_ds_element(c14n_node, "CanonicalizationMethod")?;
1016 required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
1017
1018 let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
1019 element: "SignatureMethod",
1020 })?;
1021 verify_ds_element(signature_method_node, "SignatureMethod")?;
1022 required_algorithm_attr(signature_method_node, "SignatureMethod")?;
1023
1024 let mut references = Vec::new();
1025 let mut xpath_budget = XPathSignatureParseBudget::default();
1026 for child in children {
1027 verify_ds_element(child, "Reference")?;
1028 if references.len() == MAX_REFERENCES_PER_SIGNATURE {
1029 return Err(SigningDigestError::InvalidStructure(format!(
1030 "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
1031 )));
1032 }
1033 references.push(parse_signing_reference(child, &mut xpath_budget)?);
1034 }
1035 if references.is_empty() {
1036 return Err(SigningDigestError::MissingElement {
1037 element: "Reference",
1038 });
1039 }
1040 Ok(references)
1041}
1042
1043fn parse_signing_reference(
1044 reference_node: Node<'_, '_>,
1045 xpath_budget: &mut XPathSignatureParseBudget,
1046) -> Result<SigningReference, SigningDigestError> {
1047 let uri = reference_node
1048 .attribute("URI")
1049 .ok_or_else(|| {
1050 SigningDigestError::InvalidStructure(
1051 "signing Reference must include URI attribute".into(),
1052 )
1053 })?
1054 .to_string();
1055 let mut children = element_children(reference_node);
1056
1057 let mut transforms = Vec::new();
1058 let mut next = children.next().ok_or(SigningDigestError::MissingElement {
1059 element: "DigestMethod",
1060 })?;
1061 if next.tag_name().name() == "Transforms" && next.tag_name().namespace() == Some(XMLDSIG_NS) {
1062 transforms = parse_transforms_with_budget(next, xpath_budget)?;
1063 next = children.next().ok_or(SigningDigestError::MissingElement {
1064 element: "DigestMethod",
1065 })?;
1066 }
1067
1068 verify_ds_element(next, "DigestMethod")?;
1069 let digest_uri = required_algorithm_attr(next, "DigestMethod")?;
1070 let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
1071 SigningDigestError::UnsupportedAlgorithm {
1072 uri: digest_uri.to_string(),
1073 }
1074 })?;
1075 if !digest_method.signing_allowed() {
1076 return Err(SigningDigestError::SigningAlgorithmDisabled {
1077 uri: digest_method.uri(),
1078 });
1079 }
1080
1081 let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
1082 element: "DigestValue",
1083 })?;
1084 verify_ds_element(digest_value_node, "DigestValue")?;
1085
1086 if let Some(unexpected) = children.next() {
1087 return Err(SigningDigestError::InvalidStructure(format!(
1088 "unexpected element <{}> after <DigestValue> in <Reference>",
1089 unexpected.tag_name().name()
1090 )));
1091 }
1092
1093 Ok(SigningReference {
1094 uri,
1095 transforms,
1096 digest_method,
1097 })
1098}
1099
1100fn find_required_child<'a>(
1101 parent: Node<'a, 'a>,
1102 child_name: &'static str,
1103) -> Result<Node<'a, 'a>, SigningDigestError> {
1104 parent
1105 .children()
1106 .find(|node| {
1107 node.is_element()
1108 && node.tag_name().name() == child_name
1109 && node.tag_name().namespace() == Some(XMLDSIG_NS)
1110 })
1111 .ok_or(SigningDigestError::MissingElement {
1112 element: child_name,
1113 })
1114}
1115
1116fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
1117 node.children().filter(Node::is_element)
1118}
1119
1120fn verify_ds_element(
1121 node: Node<'_, '_>,
1122 expected_name: &'static str,
1123) -> Result<(), SigningDigestError> {
1124 if !node.is_element() {
1125 return Err(SigningDigestError::InvalidStructure(format!(
1126 "expected element <{expected_name}>, got non-element node"
1127 )));
1128 }
1129 let tag = node.tag_name();
1130 if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
1131 return Err(SigningDigestError::InvalidStructure(format!(
1132 "expected <ds:{expected_name}>, got <{}>",
1133 tag.name()
1134 )));
1135 }
1136 Ok(())
1137}
1138
1139fn required_algorithm_attr<'a>(
1140 node: Node<'a, 'a>,
1141 element_name: &'static str,
1142) -> Result<&'a str, SigningDigestError> {
1143 node.attribute("Algorithm").ok_or_else(|| {
1144 SigningDigestError::InvalidStructure(format!(
1145 "missing Algorithm attribute on <{element_name}>"
1146 ))
1147 })
1148}