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