Skip to main content

xml_sec/xmldsig/
sign.rs

1//! Signing-side XMLDSig digest computation.
2//!
3//! This pass fills `<DigestValue>` elements before `<SignedInfo>` is
4//! canonicalized and signed. It intentionally uses a signing-template parser
5//! instead of [`crate::xmldsig::parse::parse_signed_info`], because verification
6//! must continue to reject empty or malformed stored digest values.
7
8use base64::Engine;
9use getrandom::SysRng;
10use p256::ecdsa::{Signature as P256Signature, SigningKey as P256SigningKey};
11use p256::pkcs8::{DecodePrivateKey, EncodePublicKey};
12use p384::ecdsa::{Signature as P384Signature, SigningKey as P384SigningKey};
13use roxmltree::{Document, Node};
14use rsa::RsaPrivateKey;
15use rsa::pkcs1v15::Signature as RsaPkcs1v15Signature;
16use rsa::pkcs1v15::SigningKey as RsaPkcs1v15SigningKey;
17use rsa::signature::{RandomizedSigner, SignatureEncoding, Signer};
18use rsa::traits::PublicKeyParts;
19use sha2::{Sha256, Sha384, Sha512};
20use std::collections::HashSet;
21use x509_parser::prelude::FromDer;
22
23use crate::c14n::canonicalize;
24
25use super::builder::{SignatureBuilder, SignatureBuilderError};
26use super::digest::{DigestAlgorithm, compute_digest};
27use super::mutation::{
28    XmlMutationError, append_signature_to_root, fill_key_info, fill_signature_value,
29    fill_signed_info_digest_values,
30};
31use super::parse::{
32    MAX_REFERENCES_PER_SIGNATURE, SignatureAlgorithm, XMLDSIG_NS, parse_signed_info,
33};
34use super::transforms::{
35    Transform, TransformExecutionBudget, TransformOptions, XPathHereSemantics,
36    XPathSignatureParseBudget, execute_transforms_with_options_and_budget,
37    parse_transforms_with_budget,
38};
39use super::types::TransformError;
40use super::uri::UriReferenceResolver;
41
42/// Result for one computed signing-template reference digest.
43#[derive(Debug, Clone, PartialEq, Eq)]
44#[must_use = "use the computed digest value to fill the corresponding <DigestValue>"]
45pub struct ComputedReferenceDigest {
46    /// Zero-based reference index in `<SignedInfo>` document order.
47    pub index: usize,
48    /// Reference URI used for same-document dereference.
49    pub uri: String,
50    /// Digest algorithm declared by `<DigestMethod>`.
51    pub digest_method: DigestAlgorithm,
52    /// Base64-encoded digest value ready for `<DigestValue>`.
53    pub digest_value: String,
54}
55
56/// Errors returned by the XMLDSig signing digest pass.
57#[derive(Debug, thiserror::Error)]
58pub enum SigningDigestError {
59    /// The input XML document is not well-formed.
60    #[error("XML parse error: {0}")]
61    XmlParse(#[from] roxmltree::Error),
62
63    /// Required XMLDSig element is missing.
64    #[error("missing required element: <{element}>")]
65    MissingElement {
66        /// Required element name.
67        element: &'static str,
68    },
69
70    /// XMLDSig template structure is invalid.
71    #[error("invalid signing template: {0}")]
72    InvalidStructure(String),
73
74    /// Digest algorithm URI is not supported.
75    #[error("unsupported digest algorithm: {uri}")]
76    UnsupportedAlgorithm {
77        /// Unrecognized algorithm URI.
78        uri: String,
79    },
80
81    /// Digest algorithm is supported for verification but disabled for signing.
82    #[error("digest algorithm is disabled for signing: {uri}")]
83    SigningAlgorithmDisabled {
84        /// Algorithm URI rejected for new signatures.
85        uri: &'static str,
86    },
87
88    /// URI dereference or transform execution failed.
89    #[error("reference processing error: {0}")]
90    Transform(#[from] TransformError),
91
92    /// Writing computed digest values back into XML failed.
93    #[error("XML mutation error: {0}")]
94    XmlMutation(#[from] XmlMutationError),
95}
96
97/// Errors returned by the full XMLDSig signing pipeline.
98#[derive(Debug, thiserror::Error)]
99pub enum SigningError {
100    /// Reference digest computation failed.
101    #[error("signing digest pass failed: {0}")]
102    Digest(#[from] SigningDigestError),
103
104    /// Parsing the digest-filled `<SignedInfo>` failed.
105    #[error("failed to parse SignedInfo after digest fill: {0}")]
106    ParseSignedInfo(#[from] super::parse::ParseError),
107
108    /// SignedInfo canonicalization failed.
109    #[error("SignedInfo canonicalization failed: {0}")]
110    Canonicalization(#[from] crate::c14n::C14nError),
111
112    /// Signing key preparation or signing failed.
113    #[error("signing key error: {0}")]
114    Key(#[from] SigningKeyError),
115
116    /// Writing `<SignatureValue>` failed.
117    #[error("XML mutation error: {0}")]
118    XmlMutation(#[from] XmlMutationError),
119
120    /// Writing `<KeyInfo>` failed.
121    #[error("KeyInfo writer error: {0}")]
122    KeyInfo(#[from] KeyInfoWriteError),
123
124    /// Signature template generation failed.
125    #[error("signature template error: {0}")]
126    Template(#[from] SignatureBuilderError),
127}
128
129/// Errors while parsing or using XMLDSig signing keys.
130#[derive(Debug, thiserror::Error)]
131#[non_exhaustive]
132pub enum SigningKeyError {
133    /// PEM input could not be parsed.
134    #[error("invalid PEM private key")]
135    InvalidKeyPem,
136
137    /// PEM block was not an unencrypted PKCS#8 private key.
138    #[error("invalid key format: expected PRIVATE KEY PEM, got {label}")]
139    InvalidKeyFormat {
140        /// Actual PEM label.
141        label: String,
142    },
143
144    /// DER bytes could not be decoded for the requested key type.
145    #[error("invalid PKCS#8 private key DER")]
146    InvalidKeyDer,
147
148    /// The signing key cannot produce the requested XMLDSig algorithm.
149    #[error("signing key does not support algorithm: {uri}")]
150    UnsupportedAlgorithm {
151        /// XMLDSig signature algorithm URI.
152        uri: String,
153    },
154
155    /// The private-key signing operation failed.
156    #[error("private-key signing operation failed")]
157    SigningFailed,
158
159    /// Public-key encoding failed for a supported signing key.
160    #[error("failed to encode signing public key as SPKI DER")]
161    PublicKeyEncodingFailed,
162}
163
164/// Public key material corresponding to a private XMLDSig signing key.
165#[derive(Debug, Clone, PartialEq, Eq)]
166#[non_exhaustive]
167pub enum SigningPublicKeyInfo {
168    /// RSA public key with DER SubjectPublicKeyInfo and normalized parameters.
169    Rsa {
170        /// DER-encoded SubjectPublicKeyInfo bytes.
171        spki_der: Vec<u8>,
172        /// Unsigned big-endian RSA modulus (`n`), normalized without leading zeroes.
173        modulus: Vec<u8>,
174        /// Unsigned big-endian RSA public exponent (`e`), normalized without leading zeroes.
175        exponent: Vec<u8>,
176    },
177    /// EC public key with DER SubjectPublicKeyInfo and XMLDSig 1.1 KeyValue data.
178    Ec {
179        /// DER-encoded SubjectPublicKeyInfo bytes.
180        spki_der: Vec<u8>,
181        /// Bare named-curve OID, without the XMLDSig `urn:oid:` prefix.
182        curve_oid: &'static str,
183        /// Uncompressed SEC1 point (`0x04 || x || y`).
184        public_key: Vec<u8>,
185    },
186}
187
188impl SigningPublicKeyInfo {
189    /// Return DER-encoded SubjectPublicKeyInfo bytes for this public key.
190    #[must_use]
191    pub fn spki_der(&self) -> &[u8] {
192        match self {
193            Self::Rsa { spki_der, .. } | Self::Ec { spki_der, .. } => spki_der,
194        }
195    }
196}
197
198/// Private key abstraction used by [`SignContext`].
199pub trait SigningKey {
200    /// Sign canonicalized `<SignedInfo>` bytes for the declared XMLDSig method.
201    fn sign(
202        &self,
203        algorithm: SignatureAlgorithm,
204        canonical_signed_info: &[u8],
205    ) -> Result<Vec<u8>, SigningKeyError>;
206
207    /// Return structured public key material corresponding to this signing key.
208    fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError>;
209}
210
211/// Writes signing key metadata into a template `<KeyInfo>` element.
212pub trait KeyInfoWriter {
213    /// Return XML child content for the direct `<Signature>/<KeyInfo>` element.
214    fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError>;
215}
216
217/// Errors while preparing XMLDSig signing `<KeyInfo>` output.
218#[derive(Debug, thiserror::Error)]
219#[non_exhaustive]
220pub enum KeyInfoWriteError {
221    /// PEM input could not be parsed.
222    #[error("invalid PEM certificate")]
223    InvalidCertificatePem,
224
225    /// PEM block was not an X.509 certificate.
226    #[error("invalid certificate format: expected CERTIFICATE PEM, got {label}")]
227    InvalidCertificateFormat {
228        /// Actual PEM label.
229        label: String,
230    },
231
232    /// DER bytes could not be decoded as one complete X.509 certificate.
233    #[error("invalid X.509 certificate DER")]
234    InvalidCertificateDer,
235
236    /// The signing key could not expose public-key material for validation.
237    #[error("signing key public-key extraction failed: {0}")]
238    SigningKey(#[from] SigningKeyError),
239
240    /// The configured certificate does not contain the signing key's public key.
241    #[error("X.509 certificate public key does not match signing key")]
242    CertificateKeyMismatch,
243}
244
245/// `<KeyInfo>` writer that embeds one DER X.509 certificate.
246pub struct X509CertificateKeyInfoWriter {
247    certificate_der: Vec<u8>,
248}
249
250impl X509CertificateKeyInfoWriter {
251    /// Parse a PEM `CERTIFICATE` block for XMLDSig `<X509Certificate>` output.
252    pub fn from_pem(certificate_pem: &str) -> Result<Self, KeyInfoWriteError> {
253        let (rest, pem) = x509_parser::pem::parse_x509_pem(certificate_pem.as_bytes())
254            .map_err(|_| KeyInfoWriteError::InvalidCertificatePem)?;
255        if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
256            return Err(KeyInfoWriteError::InvalidCertificatePem);
257        }
258        if pem.label != "CERTIFICATE" {
259            return Err(KeyInfoWriteError::InvalidCertificateFormat { label: pem.label });
260        }
261        Self::from_der(&pem.contents)
262    }
263
264    /// Validate and store DER certificate bytes for XMLDSig `<X509Certificate>` output.
265    pub fn from_der(certificate_der: &[u8]) -> Result<Self, KeyInfoWriteError> {
266        let (rest, _) = x509_parser::certificate::X509Certificate::from_der(certificate_der)
267            .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
268        if !rest.is_empty() {
269            return Err(KeyInfoWriteError::InvalidCertificateDer);
270        }
271        Ok(Self {
272            certificate_der: certificate_der.to_vec(),
273        })
274    }
275}
276
277impl KeyInfoWriter for X509CertificateKeyInfoWriter {
278    fn write_key_info(&self, signing_key: &dyn SigningKey) -> Result<String, KeyInfoWriteError> {
279        let (rest, certificate) =
280            x509_parser::certificate::X509Certificate::from_der(&self.certificate_der)
281                .map_err(|_| KeyInfoWriteError::InvalidCertificateDer)?;
282        if !rest.is_empty() {
283            return Err(KeyInfoWriteError::InvalidCertificateDer);
284        }
285        let signing_public_key = signing_key.public_key_info()?;
286        if certificate.public_key().raw != signing_public_key.spki_der() {
287            return Err(KeyInfoWriteError::CertificateKeyMismatch);
288        }
289
290        let certificate_b64 =
291            base64::engine::general_purpose::STANDARD.encode(&self.certificate_der);
292        Ok(format!(
293            "<X509Data xmlns=\"{XMLDSIG_NS}\"><X509Certificate>{certificate_b64}</X509Certificate></X509Data>"
294        ))
295    }
296}
297
298/// RSA PKCS#1 v1.5 private key for XMLDSig signing.
299pub struct RsaSigningKey {
300    key: RsaPrivateKey,
301}
302
303impl RsaSigningKey {
304    /// Parse an unencrypted PKCS#8 `PRIVATE KEY` PEM block.
305    pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
306        let private_key_der = parse_private_key_pem(private_key_pem)?;
307        Self::from_pkcs8_der(&private_key_der)
308    }
309
310    /// Parse unencrypted PKCS#8 private key DER.
311    pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
312        let key = RsaPrivateKey::from_pkcs8_der(private_key_der)
313            .map_err(|_| SigningKeyError::InvalidKeyDer)?;
314        Ok(Self { key })
315    }
316}
317
318impl SigningKey for RsaSigningKey {
319    fn sign(
320        &self,
321        algorithm: SignatureAlgorithm,
322        canonical_signed_info: &[u8],
323    ) -> Result<Vec<u8>, SigningKeyError> {
324        match algorithm {
325            SignatureAlgorithm::RsaSha256 => sign_rsa_pkcs1v15_with_rng(
326                RsaPkcs1v15SigningKey::<Sha256>::new(self.key.clone()),
327                canonical_signed_info,
328            ),
329            SignatureAlgorithm::RsaSha384 => sign_rsa_pkcs1v15_with_rng(
330                RsaPkcs1v15SigningKey::<Sha384>::new(self.key.clone()),
331                canonical_signed_info,
332            ),
333            SignatureAlgorithm::RsaSha512 => sign_rsa_pkcs1v15_with_rng(
334                RsaPkcs1v15SigningKey::<Sha512>::new(self.key.clone()),
335                canonical_signed_info,
336            ),
337            _ => Err(SigningKeyError::UnsupportedAlgorithm {
338                uri: algorithm.uri().to_string(),
339            }),
340        }
341    }
342
343    fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
344        let public_key = self.key.to_public_key();
345        let spki_der = public_key
346            .to_public_key_der()
347            .map(|doc| doc.as_bytes().to_vec())
348            .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
349        Ok(SigningPublicKeyInfo::Rsa {
350            spki_der,
351            modulus: public_key.n().to_be_bytes_trimmed_vartime().into_vec(),
352            exponent: public_key.e().to_be_bytes_trimmed_vartime().into_vec(),
353        })
354    }
355}
356
357fn sign_rsa_pkcs1v15_with_rng(
358    key: impl RandomizedSigner<RsaPkcs1v15Signature>,
359    canonical_signed_info: &[u8],
360) -> Result<Vec<u8>, SigningKeyError> {
361    let signature = key
362        .try_sign_with_rng(&mut SysRng, canonical_signed_info)
363        .map_err(|_| SigningKeyError::SigningFailed)?;
364    Ok(signature.to_vec())
365}
366
367/// ECDSA P-256 private key for XMLDSig signing.
368pub struct EcdsaP256SigningKey {
369    key: P256SigningKey,
370}
371
372impl EcdsaP256SigningKey {
373    /// Parse an unencrypted PKCS#8 `PRIVATE KEY` PEM block.
374    pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
375        let private_key_der = parse_private_key_pem(private_key_pem)?;
376        Self::from_pkcs8_der(&private_key_der)
377    }
378
379    /// Parse unencrypted PKCS#8 private key DER.
380    pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
381        let key = P256SigningKey::from_pkcs8_der(private_key_der)
382            .map_err(|_| SigningKeyError::InvalidKeyDer)?;
383        Ok(Self { key })
384    }
385}
386
387impl SigningKey for EcdsaP256SigningKey {
388    fn sign(
389        &self,
390        algorithm: SignatureAlgorithm,
391        canonical_signed_info: &[u8],
392    ) -> Result<Vec<u8>, SigningKeyError> {
393        if algorithm != SignatureAlgorithm::EcdsaP256Sha256 {
394            return Err(SigningKeyError::UnsupportedAlgorithm {
395                uri: algorithm.uri().to_string(),
396            });
397        }
398        let signature: P256Signature = self
399            .key
400            .try_sign(canonical_signed_info)
401            .map_err(|_| SigningKeyError::SigningFailed)?;
402        Ok(signature.to_bytes().to_vec())
403    }
404
405    fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
406        let verifying_key = self.key.verifying_key();
407        let spki_der = verifying_key
408            .to_public_key_der()
409            .map(|doc| doc.as_bytes().to_vec())
410            .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
411        Ok(SigningPublicKeyInfo::Ec {
412            spki_der,
413            curve_oid: "1.2.840.10045.3.1.7",
414            public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
415        })
416    }
417}
418
419/// ECDSA P-384 private key for XMLDSig signing.
420pub struct EcdsaP384SigningKey {
421    key: P384SigningKey,
422}
423
424impl EcdsaP384SigningKey {
425    /// Parse an unencrypted PKCS#8 `PRIVATE KEY` PEM block.
426    pub fn from_pkcs8_pem(private_key_pem: &str) -> Result<Self, SigningKeyError> {
427        let private_key_der = parse_private_key_pem(private_key_pem)?;
428        Self::from_pkcs8_der(&private_key_der)
429    }
430
431    /// Parse unencrypted PKCS#8 private key DER.
432    pub fn from_pkcs8_der(private_key_der: &[u8]) -> Result<Self, SigningKeyError> {
433        let key = P384SigningKey::from_pkcs8_der(private_key_der)
434            .map_err(|_| SigningKeyError::InvalidKeyDer)?;
435        Ok(Self { key })
436    }
437}
438
439impl SigningKey for EcdsaP384SigningKey {
440    fn sign(
441        &self,
442        algorithm: SignatureAlgorithm,
443        canonical_signed_info: &[u8],
444    ) -> Result<Vec<u8>, SigningKeyError> {
445        if algorithm != SignatureAlgorithm::EcdsaP384Sha384 {
446            return Err(SigningKeyError::UnsupportedAlgorithm {
447                uri: algorithm.uri().to_string(),
448            });
449        }
450        let signature: P384Signature = self
451            .key
452            .try_sign(canonical_signed_info)
453            .map_err(|_| SigningKeyError::SigningFailed)?;
454        Ok(signature.to_bytes().to_vec())
455    }
456
457    fn public_key_info(&self) -> Result<SigningPublicKeyInfo, SigningKeyError> {
458        let verifying_key = self.key.verifying_key();
459        let spki_der = verifying_key
460            .to_public_key_der()
461            .map(|doc| doc.as_bytes().to_vec())
462            .map_err(|_| SigningKeyError::PublicKeyEncodingFailed)?;
463        Ok(SigningPublicKeyInfo::Ec {
464            spki_der,
465            curve_oid: "1.3.132.0.34",
466            public_key: verifying_key.to_sec1_point(false).as_bytes().to_vec(),
467        })
468    }
469}
470
471/// XMLDSig signing context.
472pub struct SignContext<'a> {
473    signing_key: &'a dyn SigningKey,
474    key_info_writer: Option<&'a dyn KeyInfoWriter>,
475    transform_options: TransformOptions,
476}
477
478impl<'a> SignContext<'a> {
479    /// Create a signing context using the supplied private key.
480    pub fn new(signing_key: &'a dyn SigningKey) -> Self {
481        Self {
482            signing_key,
483            key_info_writer: None,
484            transform_options: TransformOptions::default(),
485        }
486    }
487
488    /// Configure signing to populate the direct `<Signature>/<KeyInfo>` placeholder.
489    #[must_use]
490    pub fn key_info_writer(mut self, writer: &'a dyn KeyInfoWriter) -> Self {
491        self.key_info_writer = Some(writer);
492        self
493    }
494
495    /// Select the node returned by XPath's `here()` extension function.
496    ///
497    /// The default follows XMLDSig and returns the `<XPath>` parameter.
498    /// [`XPathHereSemantics::XmlSecLegacy`] is available only for producing
499    /// signatures compatible with libxmlsec1's `<Transform>` interpretation.
500    #[must_use]
501    pub fn xpath_here_semantics(mut self, semantics: XPathHereSemantics) -> Self {
502        self.transform_options = self.transform_options.xpath_here_semantics(semantics);
503        self
504    }
505
506    /// Sign XML that already contains a `<Signature>` template.
507    ///
508    /// The template must include empty `<DigestValue>` and `<SignatureValue>`
509    /// targets. The pipeline fills reference digests, reparses the result,
510    /// canonicalizes `<SignedInfo>`, signs those canonical bytes, and fills the
511    /// base64 `<SignatureValue>`.
512    pub fn sign_template(&self, xml: &str) -> Result<String, SigningError> {
513        let with_digests = fill_reference_digest_values_with_options(xml, self.transform_options)?;
514        let (algorithm, canonical_signed_info) = canonicalize_signed_info(&with_digests)?;
515        let signature_value = self.signing_key.sign(algorithm, &canonical_signed_info)?;
516        let signature_b64 = base64::engine::general_purpose::STANDARD.encode(signature_value);
517        let signed = fill_signature_value(&with_digests, &signature_b64)?;
518        if let Some(writer) = self.key_info_writer {
519            let key_info_content = writer.write_key_info(self.signing_key)?;
520            Ok(fill_key_info(&signed, &key_info_content)?)
521        } else {
522            Ok(signed)
523        }
524    }
525
526    /// Build a signature template, append it to the source root, then sign it.
527    pub fn sign_with_builder(
528        &self,
529        xml: &str,
530        builder: &SignatureBuilder,
531    ) -> Result<String, SigningError> {
532        let template = builder.build_template()?;
533        let templated = append_signature_to_root(xml, &template)?;
534        self.sign_template(&templated)
535    }
536}
537
538#[derive(Debug)]
539struct SigningReference {
540    uri: String,
541    transforms: Vec<Transform>,
542    digest_method: DigestAlgorithm,
543}
544
545/// Compute base64 digest values for every `<Reference>` in the signing template.
546///
547/// References are processed in `<SignedInfo>` document order under the last
548/// XMLDSig `<Signature>` element. `sign_with_builder()` appends a new template
549/// at the end of the source root, so older signatures in an already-signed
550/// document must not become the signing target.
551pub fn compute_reference_digest_values(
552    xml: &str,
553) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
554    compute_reference_digest_values_with_options(xml, TransformOptions::default())
555}
556
557fn compute_reference_digest_values_with_options(
558    xml: &str,
559    transform_options: TransformOptions,
560) -> Result<Vec<ComputedReferenceDigest>, SigningDigestError> {
561    let doc = Document::parse(xml)?;
562    let signature = find_signing_signature_node(&doc)?;
563    let signed_info = find_required_child(signature, "SignedInfo")?;
564    let references = parse_signing_references(signed_info)?;
565    let resolver = UriReferenceResolver::new(&doc);
566    let execution_budget = TransformExecutionBudget::default();
567
568    references
569        .into_iter()
570        .enumerate()
571        .map(|(index, reference)| {
572            let initial_data = resolver.dereference_with_budget(
573                &reference.uri,
574                execution_budget.node_set_materialization(),
575            )?;
576            let pre_digest = execute_transforms_with_options_and_budget(
577                signature,
578                initial_data,
579                &reference.transforms,
580                transform_options,
581                &execution_budget,
582            )?;
583            let digest = compute_digest(reference.digest_method, &pre_digest);
584            let digest_value = base64::engine::general_purpose::STANDARD.encode(digest);
585            Ok(ComputedReferenceDigest {
586                index,
587                uri: reference.uri,
588                digest_method: reference.digest_method,
589                digest_value,
590            })
591        })
592        .collect()
593}
594
595/// Compute and fill all signing-template `<DigestValue>` elements.
596///
597/// This is the signing counterpart to verification reference processing: it
598/// dereferences each `<Reference>`, applies transforms, computes the digest,
599/// and writes the base64 digest into the matching `<DigestValue>` in document
600/// order.
601pub fn fill_reference_digest_values(xml: &str) -> Result<String, SigningDigestError> {
602    fill_reference_digest_values_with_options(xml, TransformOptions::default())
603}
604
605fn fill_reference_digest_values_with_options(
606    xml: &str,
607    transform_options: TransformOptions,
608) -> Result<String, SigningDigestError> {
609    let digest_values = compute_reference_digest_values_with_options(xml, transform_options)?
610        .into_iter()
611        .map(|digest| digest.digest_value);
612    Ok(fill_signed_info_digest_values(xml, digest_values)?)
613}
614
615fn canonicalize_signed_info(xml: &str) -> Result<(SignatureAlgorithm, Vec<u8>), SigningError> {
616    let doc = Document::parse(xml).map_err(SigningDigestError::XmlParse)?;
617    let signature = find_signing_signature_node(&doc).map_err(SigningError::Digest)?;
618    let signed_info_node =
619        find_required_child(signature, "SignedInfo").map_err(SigningError::Digest)?;
620    let signed_info = parse_signed_info(signed_info_node)?;
621    let signed_info_subtree: HashSet<_> = signed_info_node
622        .descendants()
623        .map(|node: Node<'_, '_>| node.id())
624        .collect();
625    let mut canonical_signed_info = Vec::new();
626    canonicalize(
627        &doc,
628        Some(&|node| signed_info_subtree.contains(&node.id())),
629        &signed_info.c14n_method,
630        &mut canonical_signed_info,
631    )?;
632    Ok((signed_info.signature_method, canonical_signed_info))
633}
634
635fn parse_private_key_pem(private_key_pem: &str) -> Result<Vec<u8>, SigningKeyError> {
636    let (rest, pem) = x509_parser::pem::parse_x509_pem(private_key_pem.as_bytes())
637        .map_err(|_| SigningKeyError::InvalidKeyPem)?;
638    if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
639        return Err(SigningKeyError::InvalidKeyPem);
640    }
641    if pem.label != "PRIVATE KEY" {
642        return Err(SigningKeyError::InvalidKeyFormat { label: pem.label });
643    }
644    Ok(pem.contents)
645}
646
647fn find_signing_signature_node<'a>(
648    doc: &'a Document<'a>,
649) -> Result<Node<'a, 'a>, SigningDigestError> {
650    doc.descendants()
651        .rfind(|node| {
652            node.is_element()
653                && node.tag_name().name() == "Signature"
654                && node.tag_name().namespace() == Some(XMLDSIG_NS)
655        })
656        .ok_or(SigningDigestError::MissingElement {
657            element: "Signature",
658        })
659}
660
661fn parse_signing_references(
662    signed_info: Node<'_, '_>,
663) -> Result<Vec<SigningReference>, SigningDigestError> {
664    verify_ds_element(signed_info, "SignedInfo")?;
665    let mut children = element_children(signed_info);
666
667    let c14n_node = children.next().ok_or(SigningDigestError::MissingElement {
668        element: "CanonicalizationMethod",
669    })?;
670    verify_ds_element(c14n_node, "CanonicalizationMethod")?;
671    required_algorithm_attr(c14n_node, "CanonicalizationMethod")?;
672
673    let signature_method_node = children.next().ok_or(SigningDigestError::MissingElement {
674        element: "SignatureMethod",
675    })?;
676    verify_ds_element(signature_method_node, "SignatureMethod")?;
677    required_algorithm_attr(signature_method_node, "SignatureMethod")?;
678
679    let mut references = Vec::new();
680    let mut xpath_budget = XPathSignatureParseBudget::default();
681    for child in children {
682        verify_ds_element(child, "Reference")?;
683        if references.len() == MAX_REFERENCES_PER_SIGNATURE {
684            return Err(SigningDigestError::InvalidStructure(format!(
685                "SignedInfo contains more than {MAX_REFERENCES_PER_SIGNATURE} Reference elements"
686            )));
687        }
688        references.push(parse_signing_reference(child, &mut xpath_budget)?);
689    }
690    if references.is_empty() {
691        return Err(SigningDigestError::MissingElement {
692            element: "Reference",
693        });
694    }
695    Ok(references)
696}
697
698fn parse_signing_reference(
699    reference_node: Node<'_, '_>,
700    xpath_budget: &mut XPathSignatureParseBudget,
701) -> Result<SigningReference, SigningDigestError> {
702    let uri = reference_node
703        .attribute("URI")
704        .ok_or_else(|| {
705            SigningDigestError::InvalidStructure(
706                "signing Reference must include URI attribute".into(),
707            )
708        })?
709        .to_string();
710    let mut children = element_children(reference_node);
711
712    let mut transforms = Vec::new();
713    let mut next = children.next().ok_or(SigningDigestError::MissingElement {
714        element: "DigestMethod",
715    })?;
716    if next.tag_name().name() == "Transforms" && next.tag_name().namespace() == Some(XMLDSIG_NS) {
717        transforms = parse_transforms_with_budget(next, xpath_budget)?;
718        next = children.next().ok_or(SigningDigestError::MissingElement {
719            element: "DigestMethod",
720        })?;
721    }
722
723    verify_ds_element(next, "DigestMethod")?;
724    let digest_uri = required_algorithm_attr(next, "DigestMethod")?;
725    let digest_method = DigestAlgorithm::from_uri(digest_uri).ok_or_else(|| {
726        SigningDigestError::UnsupportedAlgorithm {
727            uri: digest_uri.to_string(),
728        }
729    })?;
730    if !digest_method.signing_allowed() {
731        return Err(SigningDigestError::SigningAlgorithmDisabled {
732            uri: digest_method.uri(),
733        });
734    }
735
736    let digest_value_node = children.next().ok_or(SigningDigestError::MissingElement {
737        element: "DigestValue",
738    })?;
739    verify_ds_element(digest_value_node, "DigestValue")?;
740
741    if let Some(unexpected) = children.next() {
742        return Err(SigningDigestError::InvalidStructure(format!(
743            "unexpected element <{}> after <DigestValue> in <Reference>",
744            unexpected.tag_name().name()
745        )));
746    }
747
748    Ok(SigningReference {
749        uri,
750        transforms,
751        digest_method,
752    })
753}
754
755fn find_required_child<'a>(
756    parent: Node<'a, 'a>,
757    child_name: &'static str,
758) -> Result<Node<'a, 'a>, SigningDigestError> {
759    parent
760        .children()
761        .find(|node| {
762            node.is_element()
763                && node.tag_name().name() == child_name
764                && node.tag_name().namespace() == Some(XMLDSIG_NS)
765        })
766        .ok_or(SigningDigestError::MissingElement {
767            element: child_name,
768        })
769}
770
771fn element_children<'a>(node: Node<'a, 'a>) -> impl Iterator<Item = Node<'a, 'a>> {
772    node.children().filter(Node::is_element)
773}
774
775fn verify_ds_element(
776    node: Node<'_, '_>,
777    expected_name: &'static str,
778) -> Result<(), SigningDigestError> {
779    if !node.is_element() {
780        return Err(SigningDigestError::InvalidStructure(format!(
781            "expected element <{expected_name}>, got non-element node"
782        )));
783    }
784    let tag = node.tag_name();
785    if tag.name() != expected_name || tag.namespace() != Some(XMLDSIG_NS) {
786        return Err(SigningDigestError::InvalidStructure(format!(
787            "expected <ds:{expected_name}>, got <{}>",
788            tag.name()
789        )));
790    }
791    Ok(())
792}
793
794fn required_algorithm_attr<'a>(
795    node: Node<'a, 'a>,
796    element_name: &'static str,
797) -> Result<&'a str, SigningDigestError> {
798    node.attribute("Algorithm").ok_or_else(|| {
799        SigningDigestError::InvalidStructure(format!(
800            "missing Algorithm attribute on <{element_name}>"
801        ))
802    })
803}