Skip to main content

xml_sec/xmldsig/
signature.rs

1//! Signature verification helpers for XMLDSig.
2//!
3//! This module covers RSA PKCS#1 v1.5, DSA-SHA1, and ECDSA verification,
4//! including donor P-521 interoperability under the XMLDSig `ecdsa-sha384` URI.
5//!
6//! Input public keys are accepted in SubjectPublicKeyInfo (SPKI) form because
7//! that is how the vendored PEM fixtures are stored.
8//! - RSA keys are parsed from full SPKI DER (`PUBLIC KEY`) and verified via
9//!   RustCrypto `rsa::pkcs1v15`.
10//! - ECDSA keys are validated as uncompressed SEC1 points from the SPKI bit
11//!   string and verified with RustCrypto curve crates (`p256`/`p384`/`p521`).
12
13use p256::ecdsa::{Signature as P256Signature, VerifyingKey as P256VerifyingKey};
14use p384::ecdsa::{Signature as P384Signature, VerifyingKey as P384VerifyingKey};
15use p521::ecdsa::{Signature as P521Signature, VerifyingKey as P521VerifyingKey};
16use rsa::pkcs8::DecodePublicKey;
17use rsa::signature::hazmat::PrehashVerifier;
18use rsa::{
19    pkcs1v15::{Signature as RsaPkcs1v15Signature, VerifyingKey as RsaVerifyingKey},
20    traits::PublicKeyParts,
21};
22use sha1::Sha1;
23use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
24use signature::Verifier;
25use x509_parser::prelude::FromDer;
26use x509_parser::public_key::{ECPoint, PublicKey};
27use x509_parser::x509::SubjectPublicKeyInfo;
28
29use super::parse::SignatureAlgorithm;
30use crate::policy::EcdsaSignatureValueEncoding;
31
32pub(crate) fn signature_value_matches_algorithm(
33    algorithm: SignatureAlgorithm,
34    signature_value: &[u8],
35) -> bool {
36    signature_value_matches_algorithm_with_encoding(
37        algorithm,
38        signature_value,
39        EcdsaSignatureValueEncoding::XmlDsig,
40    )
41}
42
43pub(crate) fn signature_value_matches_algorithm_with_encoding(
44    algorithm: SignatureAlgorithm,
45    signature_value: &[u8],
46    encoding: EcdsaSignatureValueEncoding,
47) -> bool {
48    match algorithm {
49        SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => algorithm
50            .dsa_component_len()
51            .is_some_and(|component_len| signature_value.len() == component_len * 2),
52        SignatureAlgorithm::HmacSha1
53        | SignatureAlgorithm::HmacSha224
54        | SignatureAlgorithm::HmacSha256
55        | SignatureAlgorithm::HmacSha384
56        | SignatureAlgorithm::HmacSha512 => algorithm
57            .hmac_output_bits()
58            .is_some_and(|bits| (1..=bits / 8).contains(&signature_value.len())),
59        // Opaque custom keys expose no modulus here, so the default can enforce
60        // only non-empty framing under the absolute ceiling. Built-in keys
61        // override this with the exact modulus width.
62        SignatureAlgorithm::RsaSha1
63        | SignatureAlgorithm::RsaSha224
64        | SignatureAlgorithm::RsaSha256
65        | SignatureAlgorithm::RsaSha384
66        | SignatureAlgorithm::RsaSha512 => {
67            (1..=crate::hard_limits::RSA_MODULUS_BIT_CEILING / 8).contains(&signature_value.len())
68        }
69        SignatureAlgorithm::EcdsaSha1
70        | SignatureAlgorithm::EcdsaSha224
71        | SignatureAlgorithm::EcdsaSha256
72        | SignatureAlgorithm::EcdsaSha384
73        | SignatureAlgorithm::EcdsaSha512 => {
74            [32, 48, 66]
75                .into_iter()
76                .any(|component_len| match encoding {
77                    EcdsaSignatureValueEncoding::XmlDsig => {
78                        signature_value.len() == component_len * 2
79                    }
80                    EcdsaSignatureValueEncoding::XmlSecAsn1Der => {
81                        inspect_der_encoded_ecdsa_signature(signature_value, component_len)
82                            .is_ok_and(|value| value.is_some())
83                    }
84                })
85        }
86    }
87}
88
89pub(crate) fn signature_value_matches_spki(
90    algorithm: SignatureAlgorithm,
91    public_key_spki_der: &[u8],
92    signature_value: &[u8],
93) -> Result<bool, SignatureVerificationError> {
94    signature_value_matches_spki_with_encoding(
95        algorithm,
96        public_key_spki_der,
97        signature_value,
98        EcdsaSignatureValueEncoding::XmlDsig,
99    )
100}
101
102pub(crate) fn signature_value_matches_spki_with_encoding(
103    algorithm: SignatureAlgorithm,
104    public_key_spki_der: &[u8],
105    signature_value: &[u8],
106    encoding: EcdsaSignatureValueEncoding,
107) -> Result<bool, SignatureVerificationError> {
108    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
109        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
110    if !rest.is_empty() {
111        return Err(SignatureVerificationError::InvalidKeyDer);
112    }
113    let public_key = spki
114        .parsed()
115        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
116
117    match (algorithm, public_key) {
118        (
119            algorithm @ (SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256),
120            PublicKey::DSA(_),
121        ) => {
122            let key = dsa::VerifyingKey::from_public_key_der(public_key_spki_der)
123                .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
124            let component_len = usize::try_from(key.components().q().bits_vartime())
125                .map_err(|_| SignatureVerificationError::InvalidKeyDer)?
126                .div_ceil(8);
127            Ok(algorithm.dsa_component_len() == Some(component_len)
128                && signature_value.len() == component_len * 2)
129        }
130        (
131            SignatureAlgorithm::RsaSha1
132            | SignatureAlgorithm::RsaSha224
133            | SignatureAlgorithm::RsaSha256
134            | SignatureAlgorithm::RsaSha384
135            | SignatureAlgorithm::RsaSha512,
136            PublicKey::RSA(_),
137        ) => {
138            let key = rsa::RsaPublicKey::from_public_key_der(public_key_spki_der)
139                .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
140            Ok(signature_value.len() == key.size())
141        }
142        (
143            SignatureAlgorithm::EcdsaSha1
144            | SignatureAlgorithm::EcdsaSha224
145            | SignatureAlgorithm::EcdsaSha256
146            | SignatureAlgorithm::EcdsaSha384
147            | SignatureAlgorithm::EcdsaSha512,
148            PublicKey::EC(ec),
149        ) => {
150            validate_ec_public_key_encoding(&ec, &spki.subject_public_key.data)?;
151            let (_, component_len) = ecdsa_curve_and_component_len(&spki, &ec)?;
152            match encoding {
153                EcdsaSignatureValueEncoding::XmlDsig
154                    if signature_value.len() == component_len * 2 => {}
155                EcdsaSignatureValueEncoding::XmlSecAsn1Der
156                    if inspect_der_encoded_ecdsa_signature(signature_value, component_len)?
157                        .is_some() => {}
158                _ => return Err(SignatureVerificationError::InvalidSignatureFormat),
159            }
160            Ok(true)
161        }
162        (
163            SignatureAlgorithm::HmacSha1
164            | SignatureAlgorithm::HmacSha224
165            | SignatureAlgorithm::HmacSha256
166            | SignatureAlgorithm::HmacSha384
167            | SignatureAlgorithm::HmacSha512,
168            _,
169        ) => Err(SignatureVerificationError::KeyAlgorithmMismatch {
170            uri: algorithm.uri().to_owned(),
171        }),
172        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
173            uri: algorithm.uri().to_owned(),
174        }),
175    }
176}
177
178/// Errors while preparing or running XMLDSig signature verification.
179#[derive(Debug, thiserror::Error)]
180#[non_exhaustive]
181pub enum SignatureVerificationError {
182    /// The provided PEM block could not be parsed as PEM input.
183    #[error("invalid PEM public key")]
184    InvalidKeyPem,
185
186    /// The signature method is not an RSA PKCS#1 v1.5 algorithm.
187    #[error("unsupported signature algorithm: {uri}")]
188    UnsupportedAlgorithm {
189        /// XMLDSig algorithm URI used for diagnostics.
190        uri: String,
191    },
192
193    /// The provided PEM block was not a public key.
194    #[error("invalid key format: expected PUBLIC KEY PEM, got {label}")]
195    InvalidKeyFormat {
196        /// The PEM label that was actually supplied.
197        label: String,
198    },
199
200    /// The provided DER bytes were not a valid SPKI-encoded public key.
201    #[error("invalid SubjectPublicKeyInfo DER")]
202    InvalidKeyDer,
203
204    /// A structurally valid key violates the active key-strength policy.
205    #[error("verification key rejected by policy: {0}")]
206    KeyPolicy(#[from] crate::policy::PolicyViolation),
207
208    /// The provided public key does not match the signature algorithm.
209    #[error("public key does not match signature algorithm: {uri}")]
210    KeyAlgorithmMismatch {
211        /// XMLDSig algorithm URI used for diagnostics.
212        uri: String,
213    },
214
215    /// The provided ECDSA signature bytes do not use the encoding selected by
216    /// the active verification policy.
217    #[error("invalid ECDSA signature encoding")]
218    InvalidSignatureFormat,
219}
220
221/// Verify an RSA XMLDSig signature using a PEM-encoded SPKI public key.
222///
223/// The PEM must contain a `PUBLIC KEY` block. Returns `Ok(false)` for signature
224/// mismatch and `Err` for algorithm/key preparation errors.
225#[must_use = "discarding the verification result skips signature validation"]
226pub fn verify_rsa_signature_pem(
227    algorithm: SignatureAlgorithm,
228    public_key_pem: &str,
229    signed_data: &[u8],
230    signature_value: &[u8],
231) -> Result<bool, SignatureVerificationError> {
232    let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
233    verify_rsa_signature_spki(
234        algorithm,
235        &public_key_spki_der,
236        signed_data,
237        signature_value,
238    )
239}
240
241/// Verify an ECDSA XMLDSig signature using a PEM-encoded SPKI public key.
242///
243/// The PEM must contain a `PUBLIC KEY` block. The signature value is expected
244/// to use the XMLDSig fixed-width `r || s` format required by
245/// [RFC 9231 section 2.3.6](https://www.rfc-editor.org/rfc/rfc9231.html#section-2.3.6)
246/// (which replaces RFC 6931) and XMLDSig 1.1. Use [`verify_ecdsa_signature_pem_with_encoding`] to opt into
247/// libxmlsec1's ASN.1 compatibility representation. Returns `Ok(false)` for a
248/// signature mismatch and `Err` for key or signature-format errors.
249#[must_use = "discarding the verification result skips signature validation"]
250pub fn verify_ecdsa_signature_pem(
251    algorithm: SignatureAlgorithm,
252    public_key_pem: &str,
253    signed_data: &[u8],
254    signature_value: &[u8],
255) -> Result<bool, SignatureVerificationError> {
256    verify_ecdsa_signature_pem_with_encoding(
257        algorithm,
258        public_key_pem,
259        signed_data,
260        signature_value,
261        EcdsaSignatureValueEncoding::XmlDsig,
262    )
263}
264
265/// Verify an ECDSA signature using an explicit XMLDSig compatibility encoding.
266#[must_use = "discarding the verification result skips signature validation"]
267pub fn verify_ecdsa_signature_pem_with_encoding(
268    algorithm: SignatureAlgorithm,
269    public_key_pem: &str,
270    signed_data: &[u8],
271    signature_value: &[u8],
272    encoding: EcdsaSignatureValueEncoding,
273) -> Result<bool, SignatureVerificationError> {
274    let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
275    verify_ecdsa_signature_spki_with_encoding(
276        algorithm,
277        &public_key_spki_der,
278        signed_data,
279        signature_value,
280        encoding,
281    )
282}
283
284fn parse_public_key_pem(public_key_pem: &str) -> Result<Vec<u8>, SignatureVerificationError> {
285    let (rest, pem) = x509_parser::pem::parse_x509_pem(public_key_pem.as_bytes())
286        .map_err(|_| SignatureVerificationError::InvalidKeyPem)?;
287    if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
288        return Err(SignatureVerificationError::InvalidKeyPem);
289    }
290    if pem.label != "PUBLIC KEY" {
291        return Err(SignatureVerificationError::InvalidKeyFormat { label: pem.label });
292    }
293
294    Ok(pem.contents)
295}
296
297/// Verify an RSA XMLDSig signature using DER-encoded SPKI public key bytes.
298///
299/// The input must be an X.509 `SubjectPublicKeyInfo` wrapping an RSA key.
300/// Returns `Ok(false)` for signature mismatch and `Err` for algorithm/key
301/// preparation errors.
302#[must_use = "discarding the verification result skips signature validation"]
303pub fn verify_rsa_signature_spki(
304    algorithm: SignatureAlgorithm,
305    public_key_spki_der: &[u8],
306    signed_data: &[u8],
307    signature_value: &[u8],
308) -> Result<bool, SignatureVerificationError> {
309    verify_rsa_signature_spki_with_minimum(
310        algorithm,
311        public_key_spki_der,
312        signed_data,
313        signature_value,
314        2048,
315    )
316}
317
318pub(crate) fn verify_rsa_signature_spki_with_minimum(
319    algorithm: SignatureAlgorithm,
320    public_key_spki_der: &[u8],
321    signed_data: &[u8],
322    signature_value: &[u8],
323    minimum_modulus_bits: usize,
324) -> Result<bool, SignatureVerificationError> {
325    validate_rsa_signature_spki_with_minimum(algorithm, public_key_spki_der, minimum_modulus_bits)?;
326    verify_rsa_signature_spki_primitive(
327        algorithm,
328        public_key_spki_der,
329        signed_data,
330        signature_value,
331    )
332}
333
334pub(crate) fn validate_rsa_signature_spki_with_minimum(
335    algorithm: SignatureAlgorithm,
336    public_key_spki_der: &[u8],
337    minimum_modulus_bits: usize,
338) -> Result<(), SignatureVerificationError> {
339    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
340        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
341    if !rest.is_empty() {
342        return Err(SignatureVerificationError::InvalidKeyDer);
343    }
344    match spki
345        .parsed()
346        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?
347    {
348        PublicKey::RSA(rsa) => validate_rsa_public_key(&rsa, algorithm, minimum_modulus_bits),
349        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
350            uri: algorithm.uri().to_string(),
351        }),
352    }
353}
354
355pub(crate) fn verify_rsa_signature_spki_primitive(
356    algorithm: SignatureAlgorithm,
357    public_key_spki_der: &[u8],
358    signed_data: &[u8],
359    signature_value: &[u8],
360) -> Result<bool, SignatureVerificationError> {
361    ensure_rsa_signature_algorithm(algorithm)?;
362    let key = rsa::RsaPublicKey::from_public_key_der(public_key_spki_der)
363        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
364    let Ok(signature) = RsaPkcs1v15Signature::try_from(signature_value) else {
365        return Ok(false);
366    };
367    let verified = match algorithm {
368        SignatureAlgorithm::RsaSha1 => RsaVerifyingKey::<Sha1>::new(key)
369            .verify(signed_data, &signature)
370            .is_ok(),
371        SignatureAlgorithm::RsaSha224 => RsaVerifyingKey::<Sha224>::new(key)
372            .verify(signed_data, &signature)
373            .is_ok(),
374        SignatureAlgorithm::RsaSha256 => RsaVerifyingKey::<Sha256>::new(key)
375            .verify(signed_data, &signature)
376            .is_ok(),
377        SignatureAlgorithm::RsaSha384 => RsaVerifyingKey::<Sha384>::new(key)
378            .verify(signed_data, &signature)
379            .is_ok(),
380        SignatureAlgorithm::RsaSha512 => RsaVerifyingKey::<Sha512>::new(key)
381            .verify(signed_data, &signature)
382            .is_ok(),
383        _ => unreachable!("RSA algorithm checked above"),
384    };
385    Ok(verified)
386}
387
388/// Verify an XMLDSig DSA-SHA1 signature using a DER SPKI public key.
389///
390/// XMLDSig 1.0 encodes the signature as the fixed-width 20-byte `r` followed
391/// by the fixed-width 20-byte `s`, rather than ASN.1 DER.
392#[must_use = "discarding the verification result skips signature validation"]
393pub fn verify_dsa_signature_spki(
394    algorithm: SignatureAlgorithm,
395    public_key_spki_der: &[u8],
396    signed_data: &[u8],
397    signature_value: &[u8],
398) -> Result<bool, SignatureVerificationError> {
399    verify_dsa_signature_spki_with_minimum(
400        algorithm,
401        public_key_spki_der,
402        signed_data,
403        signature_value,
404        crate::policy::DsaKeyPolicy::default().minimum_modulus_bits,
405    )
406}
407
408pub(crate) fn verify_dsa_signature_spki_with_minimum(
409    algorithm: SignatureAlgorithm,
410    public_key_spki_der: &[u8],
411    signed_data: &[u8],
412    signature_value: &[u8],
413    minimum_modulus_bits: usize,
414) -> Result<bool, SignatureVerificationError> {
415    validate_dsa_signature_spki_with_minimum(public_key_spki_der, minimum_modulus_bits)?;
416    verify_dsa_signature_spki_primitive(
417        algorithm,
418        public_key_spki_der,
419        signed_data,
420        signature_value,
421    )
422}
423
424pub(crate) fn validate_dsa_signature_spki_with_minimum(
425    public_key_spki_der: &[u8],
426    minimum_modulus_bits: usize,
427) -> Result<(), SignatureVerificationError> {
428    let key = dsa::VerifyingKey::from_public_key_der(public_key_spki_der)
429        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
430    let modulus_bits = usize::try_from(key.components().p().bits_vartime())
431        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
432    crate::policy::DsaKeyPolicy {
433        minimum_modulus_bits,
434    }
435    .validate_modulus_bits(modulus_bits)
436    .map_err(SignatureVerificationError::KeyPolicy)
437}
438
439pub(crate) fn verify_dsa_signature_spki_primitive(
440    algorithm: SignatureAlgorithm,
441    public_key_spki_der: &[u8],
442    signed_data: &[u8],
443    signature_value: &[u8],
444) -> Result<bool, SignatureVerificationError> {
445    if !matches!(
446        algorithm,
447        SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256
448    ) {
449        return Err(SignatureVerificationError::UnsupportedAlgorithm {
450            uri: algorithm.uri().to_string(),
451        });
452    }
453    let key = dsa::VerifyingKey::from_public_key_der(public_key_spki_der)
454        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
455    let component_len = usize::try_from(key.components().q().bits_vartime())
456        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?
457        .div_ceil(8);
458    if algorithm.dsa_component_len() != Some(component_len) {
459        return Ok(false);
460    }
461    if signature_value.len() != component_len.saturating_mul(2) {
462        return Ok(false);
463    }
464    let Some(signature) = dsa::Signature::from_components(
465        crypto_bigint::BoxedUint::from_be_slice_vartime(&signature_value[..component_len]),
466        crypto_bigint::BoxedUint::from_be_slice_vartime(&signature_value[component_len..]),
467    ) else {
468        return Ok(false);
469    };
470    let verified = match algorithm {
471        SignatureAlgorithm::DsaSha1 => key.verify_prehash(&Sha1::digest(signed_data), &signature),
472        SignatureAlgorithm::DsaSha256 => {
473            key.verify_prehash(&Sha256::digest(signed_data), &signature)
474        }
475        _ => unreachable!("DSA algorithm checked above"),
476    };
477    Ok(verified.is_ok())
478}
479
480/// Verify an ECDSA XMLDSig signature using DER-encoded SPKI public key bytes.
481///
482/// The input must be an X.509 `SubjectPublicKeyInfo` wrapping an EC key. The
483/// signature value must use XMLDSig fixed-width `r || s` bytes. Use
484/// [`verify_ecdsa_signature_spki_with_encoding`] to select libxmlsec1's ASN.1
485/// compatibility representation explicitly.
486#[must_use = "discarding the verification result skips signature validation"]
487pub fn verify_ecdsa_signature_spki(
488    algorithm: SignatureAlgorithm,
489    public_key_spki_der: &[u8],
490    signed_data: &[u8],
491    signature_value: &[u8],
492) -> Result<bool, SignatureVerificationError> {
493    verify_ecdsa_signature_spki_with_encoding(
494        algorithm,
495        public_key_spki_der,
496        signed_data,
497        signature_value,
498        EcdsaSignatureValueEncoding::XmlDsig,
499    )
500}
501
502/// Verify an ECDSA signature using an explicit XMLDSig compatibility encoding.
503#[must_use = "discarding the verification result skips signature validation"]
504pub fn verify_ecdsa_signature_spki_with_encoding(
505    algorithm: SignatureAlgorithm,
506    public_key_spki_der: &[u8],
507    signed_data: &[u8],
508    signature_value: &[u8],
509    encoding: EcdsaSignatureValueEncoding,
510) -> Result<bool, SignatureVerificationError> {
511    if !matches!(
512        algorithm,
513        SignatureAlgorithm::EcdsaSha1
514            | SignatureAlgorithm::EcdsaSha224
515            | SignatureAlgorithm::EcdsaSha256
516            | SignatureAlgorithm::EcdsaSha384
517            | SignatureAlgorithm::EcdsaSha512
518    ) {
519        return Err(SignatureVerificationError::UnsupportedAlgorithm {
520            uri: algorithm.uri().to_string(),
521        });
522    }
523
524    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
525        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
526    if !rest.is_empty() {
527        return Err(SignatureVerificationError::InvalidKeyDer);
528    }
529    let public_key = spki
530        .parsed()
531        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
532
533    match public_key {
534        PublicKey::EC(ec) => {
535            validate_ec_public_key_encoding(&ec, &spki.subject_public_key.data)?;
536            let (curve, component_len) = ecdsa_curve_and_component_len(&spki, &ec)?;
537            let signature_encoding = match encoding {
538                EcdsaSignatureValueEncoding::XmlDsig => {
539                    if signature_value.len() != component_len * 2 {
540                        return Err(SignatureVerificationError::InvalidSignatureFormat);
541                    }
542                    EcdsaSignatureEncoding::XmlDsigFixed
543                }
544                EcdsaSignatureValueEncoding::XmlSecAsn1Der => {
545                    if inspect_der_encoded_ecdsa_signature(signature_value, component_len)?
546                        .is_none()
547                    {
548                        return Err(SignatureVerificationError::InvalidSignatureFormat);
549                    }
550                    EcdsaSignatureEncoding::Asn1Der
551                }
552            };
553            let prehash = match algorithm {
554                SignatureAlgorithm::EcdsaSha1 => Sha1::digest(signed_data).to_vec(),
555                SignatureAlgorithm::EcdsaSha224 => Sha224::digest(signed_data).to_vec(),
556                SignatureAlgorithm::EcdsaSha256 => Sha256::digest(signed_data).to_vec(),
557                SignatureAlgorithm::EcdsaSha384 => Sha384::digest(signed_data).to_vec(),
558                SignatureAlgorithm::EcdsaSha512 => Sha512::digest(signed_data).to_vec(),
559                _ => unreachable!("ECDSA algorithm was validated above"),
560            };
561            match curve {
562                EcCurve::P256 => verify_ecdsa_p256(
563                    &spki.subject_public_key.data,
564                    &prehash,
565                    signature_value,
566                    signature_encoding,
567                ),
568                EcCurve::P384 => verify_ecdsa_p384(
569                    &spki.subject_public_key.data,
570                    &prehash,
571                    signature_value,
572                    signature_encoding,
573                ),
574                EcCurve::P521 => verify_ecdsa_p521(
575                    &spki.subject_public_key.data,
576                    &prehash,
577                    signature_value,
578                    signature_encoding,
579                ),
580            }
581        }
582        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
583            uri: algorithm.uri().to_string(),
584        }),
585    }
586}
587
588/// Verify the ASN.1 DER ECDSA representation required by X.509 signatures.
589///
590/// This is intentionally separate from the XMLDSig policy-aware entry point:
591/// certificate signature framing is fixed by X.509 and must not inherit the
592/// document's `SignatureValue` compatibility mode.
593pub(crate) fn verify_ecdsa_signature_spki_asn1_der(
594    algorithm: SignatureAlgorithm,
595    public_key_spki_der: &[u8],
596    signed_data: &[u8],
597    signature_value: &[u8],
598) -> Result<bool, SignatureVerificationError> {
599    verify_ecdsa_signature_spki_with_encoding(
600        algorithm,
601        public_key_spki_der,
602        signed_data,
603        signature_value,
604        EcdsaSignatureValueEncoding::XmlSecAsn1Der,
605    )
606}
607
608fn validate_rsa_public_key(
609    rsa: &x509_parser::public_key::RSAPublicKey<'_>,
610    algorithm: SignatureAlgorithm,
611    minimum_modulus_bits: usize,
612) -> Result<(), SignatureVerificationError> {
613    ensure_rsa_signature_algorithm(algorithm)?;
614    validate_rsa_key_components(rsa.modulus, rsa.exponent, minimum_modulus_bits)
615}
616
617/// Apply the RSA key-strength invariant shared by XMLDSig and X.509 algorithms.
618pub(crate) fn validate_rsa_key_components(
619    modulus: &[u8],
620    exponent: &[u8],
621    minimum_modulus_bits: usize,
622) -> Result<(), SignatureVerificationError> {
623    crate::policy::RsaKeyPolicy {
624        minimum_modulus_bits,
625    }
626    .validate_components("verification", modulus, exponent)
627    .map(|_| ())
628    .map_err(SignatureVerificationError::KeyPolicy)
629}
630
631fn ensure_rsa_signature_algorithm(
632    algorithm: SignatureAlgorithm,
633) -> Result<(), SignatureVerificationError> {
634    match algorithm {
635        SignatureAlgorithm::RsaSha1
636        | SignatureAlgorithm::RsaSha224
637        | SignatureAlgorithm::RsaSha256
638        | SignatureAlgorithm::RsaSha384
639        | SignatureAlgorithm::RsaSha512 => Ok(()),
640        _ => Err(SignatureVerificationError::UnsupportedAlgorithm {
641            uri: algorithm.uri().to_string(),
642        }),
643    }
644}
645
646#[derive(Clone, Copy, Debug, Eq, PartialEq)]
647enum EcCurve {
648    P256,
649    P384,
650    P521,
651}
652
653fn ecdsa_curve_and_component_len(
654    spki: &SubjectPublicKeyInfo<'_>,
655    ec: &ECPoint<'_>,
656) -> Result<(EcCurve, usize), SignatureVerificationError> {
657    let curve_oid = spki
658        .algorithm
659        .parameters
660        .as_ref()
661        .and_then(|params| params.as_oid().ok())
662        .ok_or(SignatureVerificationError::InvalidKeyDer)?;
663    let point_len = ec.key_size();
664
665    let curve_oid = curve_oid.to_id_string();
666    match (curve_oid.as_str(), point_len) {
667        ("1.2.840.10045.3.1.7", 256) => Ok((EcCurve::P256, 32)),
668        ("1.3.132.0.34", 384) => Ok((EcCurve::P384, 48)),
669        // x509-parser reports the byte-aligned SEC1 point size for P-521.
670        ("1.3.132.0.35", 528) => Ok((EcCurve::P521, 66)),
671        _ => Err(SignatureVerificationError::InvalidKeyDer),
672    }
673}
674
675fn verify_ecdsa_p256(
676    public_key: &[u8],
677    prehash: &[u8],
678    signature_value: &[u8],
679    signature_encoding: EcdsaSignatureEncoding,
680) -> Result<bool, SignatureVerificationError> {
681    let key = P256VerifyingKey::from_sec1_bytes(public_key)
682        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
683    verify_p256_signature(&key, signature_value, signature_encoding, prehash)
684}
685
686fn verify_ecdsa_p384(
687    public_key: &[u8],
688    prehash: &[u8],
689    signature_value: &[u8],
690    signature_encoding: EcdsaSignatureEncoding,
691) -> Result<bool, SignatureVerificationError> {
692    let key = P384VerifyingKey::from_sec1_bytes(public_key)
693        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
694    verify_p384_signature(&key, signature_value, signature_encoding, prehash)
695}
696
697fn verify_ecdsa_p521(
698    public_key: &[u8],
699    prehash: &[u8],
700    signature_value: &[u8],
701    signature_encoding: EcdsaSignatureEncoding,
702) -> Result<bool, SignatureVerificationError> {
703    let key = P521VerifyingKey::from_sec1_bytes(public_key)
704        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
705    verify_p521_signature(&key, signature_value, signature_encoding, prehash)
706}
707
708trait DecodeEcdsaSignature: Sized {
709    fn decode_fixed_width(bytes: &[u8]) -> Result<Self, SignatureVerificationError>;
710    fn decode_der(bytes: &[u8]) -> Result<Self, SignatureVerificationError>;
711}
712
713macro_rules! impl_ecdsa_signature_decoder {
714    ($signature:ty) => {
715        impl DecodeEcdsaSignature for $signature {
716            fn decode_fixed_width(bytes: &[u8]) -> Result<Self, SignatureVerificationError> {
717                Self::from_slice(bytes)
718                    .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)
719            }
720
721            fn decode_der(bytes: &[u8]) -> Result<Self, SignatureVerificationError> {
722                Self::from_der(bytes)
723                    .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)
724            }
725        }
726    };
727}
728
729impl_ecdsa_signature_decoder!(P256Signature);
730impl_ecdsa_signature_decoder!(P384Signature);
731impl_ecdsa_signature_decoder!(P521Signature);
732
733fn verify_ecdsa_signature<K, S>(
734    key: &K,
735    signature_value: &[u8],
736    signature_encoding: EcdsaSignatureEncoding,
737    prehash: &[u8],
738) -> Result<bool, SignatureVerificationError>
739where
740    K: PrehashVerifier<S>,
741    S: DecodeEcdsaSignature,
742{
743    let signature = match signature_encoding {
744        EcdsaSignatureEncoding::XmlDsigFixed => S::decode_fixed_width(signature_value)?,
745        EcdsaSignatureEncoding::Asn1Der => S::decode_der(signature_value)?,
746    };
747    Ok(key.verify_prehash(prehash, &signature).is_ok())
748}
749
750fn verify_p256_signature(
751    key: &P256VerifyingKey,
752    signature_value: &[u8],
753    signature_encoding: EcdsaSignatureEncoding,
754    prehash: &[u8],
755) -> Result<bool, SignatureVerificationError> {
756    verify_ecdsa_signature::<_, P256Signature>(key, signature_value, signature_encoding, prehash)
757}
758
759fn verify_p384_signature(
760    key: &P384VerifyingKey,
761    signature_value: &[u8],
762    signature_encoding: EcdsaSignatureEncoding,
763    prehash: &[u8],
764) -> Result<bool, SignatureVerificationError> {
765    verify_ecdsa_signature::<_, P384Signature>(key, signature_value, signature_encoding, prehash)
766}
767
768fn verify_p521_signature(
769    key: &P521VerifyingKey,
770    signature_value: &[u8],
771    signature_encoding: EcdsaSignatureEncoding,
772    prehash: &[u8],
773) -> Result<bool, SignatureVerificationError> {
774    verify_ecdsa_signature::<_, P521Signature>(key, signature_value, signature_encoding, prehash)
775}
776
777#[derive(Clone, Copy, Debug, Eq, PartialEq)]
778enum EcdsaSignatureEncoding {
779    XmlDsigFixed,
780    Asn1Der,
781}
782
783pub(crate) fn encode_ecdsa_signature_as_der(signature: &[u8]) -> Option<Vec<u8>> {
784    if signature.is_empty() || !signature.len().is_multiple_of(2) {
785        return None;
786    }
787    let component_len = signature.len() / 2;
788    let mut content = Vec::with_capacity(signature.len() + 8);
789    encode_der_integer(&signature[..component_len], &mut content);
790    encode_der_integer(&signature[component_len..], &mut content);
791
792    let mut encoded = Vec::with_capacity(content.len() + 3);
793    encoded.push(0x30);
794    encode_der_length(content.len(), &mut encoded);
795    encoded.extend_from_slice(&content);
796    Some(encoded)
797}
798
799pub(crate) fn maximum_ecdsa_der_signature_len(raw_signature_len: usize) -> Option<usize> {
800    if raw_signature_len == 0 || !raw_signature_len.is_multiple_of(2) {
801        return None;
802    }
803    let component_len = raw_signature_len / 2;
804    let integer_content_len = component_len.checked_add(1)?;
805    let integer_len = 1_usize
806        .checked_add(der_length_octets(integer_content_len)?)?
807        .checked_add(integer_content_len)?;
808    let sequence_content_len = integer_len.checked_mul(2)?;
809    1_usize
810        .checked_add(der_length_octets(sequence_content_len)?)?
811        .checked_add(sequence_content_len)
812}
813
814fn encode_der_integer(component: &[u8], output: &mut Vec<u8>) {
815    let first_nonzero = component
816        .iter()
817        .position(|byte| *byte != 0)
818        .unwrap_or(component.len() - 1);
819    let magnitude = &component[first_nonzero..];
820    let needs_sign_octet = magnitude[0] & 0x80 != 0;
821    output.push(0x02);
822    encode_der_length(magnitude.len() + usize::from(needs_sign_octet), output);
823    if needs_sign_octet {
824        output.push(0);
825    }
826    output.extend_from_slice(magnitude);
827}
828
829fn encode_der_length(len: usize, output: &mut Vec<u8>) {
830    if len < 128 {
831        output.push(len as u8);
832        return;
833    }
834
835    let bytes = len.to_be_bytes();
836    let first_nonzero = bytes
837        .iter()
838        .position(|byte| *byte != 0)
839        .expect("non-short DER lengths are nonzero");
840    let encoded = &bytes[first_nonzero..];
841    output.push(0x80 | encoded.len() as u8);
842    output.extend_from_slice(encoded);
843}
844
845fn der_length_octets(len: usize) -> Option<usize> {
846    if len < 128 {
847        return Some(1);
848    }
849    let significant_bytes = (usize::BITS - len.leading_zeros()).div_ceil(8) as usize;
850    significant_bytes.checked_add(1)
851}
852
853fn inspect_der_encoded_ecdsa_signature(
854    signature_value: &[u8],
855    component_len: usize,
856) -> Result<Option<()>, SignatureVerificationError> {
857    let Some((&tag, rest)) = signature_value.split_first() else {
858        return Ok(None);
859    };
860    if tag != 0x30 {
861        return Ok(None);
862    }
863
864    let sequence = parse_der_length(rest)
865        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
866        .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
867    let (sequence_len, sequence_rest) = sequence;
868    let (sequence_content, trailing) = sequence_rest
869        .split_at_checked(sequence_len)
870        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
871    if !trailing.is_empty() {
872        return Err(SignatureVerificationError::InvalidSignatureFormat);
873    }
874
875    let after_r = parse_der_integer(sequence_content, component_len)?;
876    let after_s = parse_der_integer(after_r, component_len)?;
877    if !after_s.is_empty() {
878        return Err(SignatureVerificationError::InvalidSignatureFormat);
879    }
880
881    Ok(Some(()))
882}
883
884fn parse_der_integer(
885    input: &[u8],
886    component_len: usize,
887) -> Result<&[u8], SignatureVerificationError> {
888    let Some((&tag, rest)) = input.split_first() else {
889        return Err(SignatureVerificationError::InvalidSignatureFormat);
890    };
891    if tag != 0x02 {
892        return Err(SignatureVerificationError::InvalidSignatureFormat);
893    }
894
895    let (len, rest) = parse_der_length(rest)
896        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
897        .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
898    let (integer_bytes, remainder) = rest
899        .split_at_checked(len)
900        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
901
902    if integer_bytes.is_empty() {
903        return Err(SignatureVerificationError::InvalidSignatureFormat);
904    }
905    if integer_bytes.len() > component_len + 1 {
906        return Err(SignatureVerificationError::InvalidSignatureFormat);
907    }
908    if integer_bytes.len() == component_len + 1 && integer_bytes[0] != 0 {
909        return Err(SignatureVerificationError::InvalidSignatureFormat);
910    }
911    if integer_bytes[0] & 0x80 != 0 {
912        return Err(SignatureVerificationError::InvalidSignatureFormat);
913    }
914    if integer_bytes.len() > 1 && integer_bytes[0] == 0 && integer_bytes[1] & 0x80 == 0 {
915        return Err(SignatureVerificationError::InvalidSignatureFormat);
916    }
917
918    Ok(remainder)
919}
920
921fn parse_der_length(input: &[u8]) -> Option<Result<(usize, &[u8]), ()>> {
922    let (&len_byte, rest) = input.split_first()?;
923
924    if len_byte & 0x80 == 0 {
925        return Some(Ok((usize::from(len_byte), rest)));
926    }
927
928    let len_len = usize::from(len_byte & 0x7f);
929    if len_len == 0 || len_len > std::mem::size_of::<usize>() || rest.len() < len_len {
930        return Some(Err(()));
931    }
932
933    let (len_bytes, remainder) = rest.split_at(len_len);
934    if len_bytes[0] == 0 {
935        return Some(Err(()));
936    }
937
938    let mut declared_len = 0_usize;
939    for &byte in len_bytes {
940        declared_len = match declared_len.checked_mul(256) {
941            Some(len) => len,
942            None => return Some(Err(())),
943        };
944        declared_len = match declared_len.checked_add(usize::from(byte)) {
945            Some(len) => len,
946            None => return Some(Err(())),
947        };
948    }
949
950    if declared_len < 128 {
951        return Some(Err(()));
952    }
953
954    Some(Ok((declared_len, remainder)))
955}
956
957fn validate_ec_public_key_encoding(
958    ec: &ECPoint<'_>,
959    public_key_bytes: &[u8],
960) -> Result<(), SignatureVerificationError> {
961    let coordinate_len = ec_coordinate_len_bytes(ec.key_size())?;
962    let expected_len = coordinate_len
963        .checked_mul(2)
964        .and_then(|len| len.checked_add(1))
965        .ok_or(SignatureVerificationError::InvalidKeyDer)?;
966
967    let is_uncompressed_sec1 =
968        public_key_bytes.len() == expected_len && public_key_bytes.first() == Some(&0x04);
969    if !is_uncompressed_sec1 {
970        return Err(SignatureVerificationError::InvalidKeyDer);
971    }
972
973    Ok(())
974}
975
976fn ec_coordinate_len_bytes(key_bits: usize) -> Result<usize, SignatureVerificationError> {
977    key_bits
978        .checked_add(7)
979        .and_then(|bits| bits.checked_div(8))
980        .ok_or(SignatureVerificationError::InvalidKeyDer)
981}
982
983#[cfg(test)]
984#[expect(clippy::unwrap_used, reason = "unit tests use fixed fixture data")]
985mod tests {
986    use super::*;
987
988    #[test]
989    fn ecdsa_algorithms_are_rejected_for_rsa_verification() {
990        for algorithm in [
991            SignatureAlgorithm::EcdsaSha256,
992            SignatureAlgorithm::EcdsaSha384,
993        ] {
994            let err = ensure_rsa_signature_algorithm(algorithm).unwrap_err();
995            assert!(matches!(
996                err,
997                SignatureVerificationError::UnsupportedAlgorithm { .. }
998            ));
999        }
1000    }
1001
1002    #[test]
1003    fn malformed_dsa_components_are_verification_misses() {
1004        let public_key = include_bytes!(
1005            "../../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/certs/lugh.der"
1006        );
1007        let signature = [0_u8; 40];
1008
1009        assert!(matches!(
1010            verify_dsa_signature_spki_with_minimum(
1011                SignatureAlgorithm::DsaSha1,
1012                public_key,
1013                b"signed",
1014                &signature,
1015                1024,
1016            ),
1017            Ok(false)
1018        ));
1019    }
1020
1021    #[test]
1022    fn spki_signature_framing_uses_the_resolved_key_width() {
1023        let rsa = parse_public_key_pem(include_str!(
1024            "../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem"
1025        ))
1026        .expect("RSA fixture must parse");
1027        assert!(
1028            signature_value_matches_spki(SignatureAlgorithm::RsaSha256, &rsa, &[0; 256]).unwrap()
1029        );
1030        assert!(
1031            !signature_value_matches_spki(SignatureAlgorithm::RsaSha256, &rsa, &[0; 255]).unwrap()
1032        );
1033
1034        let p256 = parse_public_key_pem(include_str!(
1035            "../../tests/fixtures/keys/ec/ec-prime256v1-pubkey.pem"
1036        ))
1037        .expect("P-256 fixture must parse");
1038        assert!(
1039            signature_value_matches_spki(SignatureAlgorithm::EcdsaSha256, &p256, &[0xAA; 64])
1040                .unwrap()
1041        );
1042        assert!(matches!(
1043            signature_value_matches_spki(SignatureAlgorithm::EcdsaSha256, &p256, &[0xAA; 96]),
1044            Err(SignatureVerificationError::InvalidSignatureFormat)
1045        ));
1046    }
1047
1048    #[test]
1049    fn der_like_prefix_with_fixed_width_len_is_not_valid_der() {
1050        let mut signature = vec![0xAA_u8; 96];
1051        signature[0] = 0x30;
1052        signature[1] = 0x20;
1053
1054        assert!(inspect_der_encoded_ecdsa_signature(&signature, 48).is_err());
1055    }
1056
1057    #[test]
1058    fn overlong_der_length_below_128_is_rejected() {
1059        let bad = [0x81_u8, 0x7f];
1060        let parsed = parse_der_length(&bad).expect("length bytes should be present");
1061        assert!(
1062            matches!(parsed, Err(())),
1063            "DER must reject long-form lengths below 128"
1064        );
1065    }
1066
1067    #[test]
1068    fn ec_coordinate_length_rounds_up_for_non_byte_aligned_curves() {
1069        assert_eq!(
1070            ec_coordinate_len_bytes(521).expect("521-bit curves require rounded byte length"),
1071            66
1072        );
1073    }
1074
1075    #[test]
1076    fn same_width_valid_der_is_recognized_in_explicit_mode() {
1077        let mut signature = Vec::with_capacity(64);
1078        signature.extend_from_slice(&[0x30, 0x3e, 0x02, 0x1d]);
1079        signature.extend(std::iter::repeat_n(0x11_u8, 29));
1080        signature.extend_from_slice(&[0x02, 0x1d]);
1081        signature.extend(std::iter::repeat_n(0x22_u8, 29));
1082
1083        assert_eq!(
1084            inspect_der_encoded_ecdsa_signature(&signature, 32).unwrap(),
1085            Some(())
1086        );
1087    }
1088
1089    #[test]
1090    fn der_integer_longer_than_component_requires_sign_byte() {
1091        let mut signature = Vec::with_capacity(72);
1092        signature.extend_from_slice(&[0x30, 0x46, 0x02, 0x21, 0x01]);
1093        signature.extend(std::iter::repeat_n(0x11_u8, 32));
1094        signature.extend_from_slice(&[0x02, 0x21, 0x01]);
1095        signature.extend(std::iter::repeat_n(0x22_u8, 32));
1096
1097        let encoding = inspect_der_encoded_ecdsa_signature(&signature, 32);
1098        assert!(matches!(
1099            encoding,
1100            Err(SignatureVerificationError::InvalidSignatureFormat)
1101        ));
1102    }
1103
1104    #[test]
1105    fn fixed_width_ecdsa_is_encoded_as_canonical_der() {
1106        // Leading zeroes are stripped, while a high-bit magnitude receives the
1107        // sign-protecting zero octet required by DER INTEGER canonicalization.
1108        let mut raw = vec![0_u8; 64];
1109        raw[31] = 1;
1110        raw[32] = 0x80;
1111
1112        let encoded = encode_ecdsa_signature_as_der(&raw).unwrap();
1113        assert_eq!(
1114            encoded,
1115            [
1116                &[0x30, 0x26, 0x02, 0x01, 0x01, 0x02, 0x21, 0x00][..],
1117                &[0x80],
1118                &[0; 31],
1119            ]
1120            .concat()
1121        );
1122        assert_eq!(
1123            inspect_der_encoded_ecdsa_signature(&encoded, 32).unwrap(),
1124            Some(())
1125        );
1126    }
1127
1128    #[test]
1129    fn maximum_der_length_covers_supported_curve_widths() {
1130        // Signing preflight must reserve the worst-case canonical DER framing
1131        // for every supported curve and reject impossible raw widths.
1132        assert_eq!(maximum_ecdsa_der_signature_len(64), Some(72));
1133        assert_eq!(maximum_ecdsa_der_signature_len(96), Some(104));
1134        assert_eq!(maximum_ecdsa_der_signature_len(132), Some(141));
1135        assert_eq!(maximum_ecdsa_der_signature_len(0), None);
1136        assert_eq!(maximum_ecdsa_der_signature_len(65), None);
1137    }
1138}