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, 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;
30
31pub(crate) fn signature_value_matches_algorithm(
32    algorithm: SignatureAlgorithm,
33    signature_value: &[u8],
34) -> bool {
35    match algorithm {
36        SignatureAlgorithm::DsaSha1 => signature_value.len() == 40,
37        SignatureAlgorithm::HmacSha1 => (10..=20).contains(&signature_value.len()),
38        // Opaque custom keys expose no modulus here, so the default can enforce
39        // only non-empty framing under the absolute ceiling. Built-in keys
40        // override this with the exact modulus width.
41        SignatureAlgorithm::RsaSha1
42        | SignatureAlgorithm::RsaSha256
43        | SignatureAlgorithm::RsaSha384
44        | SignatureAlgorithm::RsaSha512 => {
45            (1..=crate::hard_limits::RSA_MODULUS_BIT_CEILING / 8).contains(&signature_value.len())
46        }
47        SignatureAlgorithm::EcdsaSha256 | SignatureAlgorithm::EcdsaSha384 => {
48            [32, 48, 66].into_iter().any(|component_len| {
49                classify_ecdsa_signature_encoding(signature_value, component_len).is_ok()
50            })
51        }
52    }
53}
54
55pub(crate) fn signature_value_matches_spki(
56    algorithm: SignatureAlgorithm,
57    public_key_spki_der: &[u8],
58    signature_value: &[u8],
59) -> Result<bool, SignatureVerificationError> {
60    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
61        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
62    if !rest.is_empty() {
63        return Err(SignatureVerificationError::InvalidKeyDer);
64    }
65    let public_key = spki
66        .parsed()
67        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
68
69    match (algorithm, public_key) {
70        (SignatureAlgorithm::DsaSha1, PublicKey::DSA(_)) => Ok(signature_value.len() == 40),
71        (
72            SignatureAlgorithm::RsaSha1
73            | SignatureAlgorithm::RsaSha256
74            | SignatureAlgorithm::RsaSha384
75            | SignatureAlgorithm::RsaSha512,
76            PublicKey::RSA(_),
77        ) => {
78            let key = rsa::RsaPublicKey::from_public_key_der(public_key_spki_der)
79                .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
80            Ok(signature_value.len() == key.size())
81        }
82        (SignatureAlgorithm::EcdsaSha256 | SignatureAlgorithm::EcdsaSha384, PublicKey::EC(ec)) => {
83            validate_ec_public_key_encoding(&ec, &spki.subject_public_key.data)?;
84            let (_, component_len) = ecdsa_curve_and_component_len(&spki, &ec)?;
85            classify_ecdsa_signature_encoding(signature_value, component_len)?;
86            Ok(true)
87        }
88        (SignatureAlgorithm::HmacSha1, _) => {
89            Err(SignatureVerificationError::KeyAlgorithmMismatch {
90                uri: algorithm.uri().to_owned(),
91            })
92        }
93        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
94            uri: algorithm.uri().to_owned(),
95        }),
96    }
97}
98
99/// Errors while preparing or running XMLDSig signature verification.
100#[derive(Debug, thiserror::Error)]
101#[non_exhaustive]
102pub enum SignatureVerificationError {
103    /// The provided PEM block could not be parsed as PEM input.
104    #[error("invalid PEM public key")]
105    InvalidKeyPem,
106
107    /// The signature method is not an RSA PKCS#1 v1.5 algorithm.
108    #[error("unsupported signature algorithm: {uri}")]
109    UnsupportedAlgorithm {
110        /// XMLDSig algorithm URI used for diagnostics.
111        uri: String,
112    },
113
114    /// The provided PEM block was not a public key.
115    #[error("invalid key format: expected PUBLIC KEY PEM, got {label}")]
116    InvalidKeyFormat {
117        /// The PEM label that was actually supplied.
118        label: String,
119    },
120
121    /// The provided DER bytes were not a valid SPKI-encoded public key.
122    #[error("invalid SubjectPublicKeyInfo DER")]
123    InvalidKeyDer,
124
125    /// A structurally valid key violates the active key-strength policy.
126    #[error("verification key rejected by policy: {0}")]
127    KeyPolicy(#[from] crate::policy::PolicyViolation),
128
129    /// The provided public key does not match the signature algorithm.
130    #[error("public key does not match signature algorithm: {uri}")]
131    KeyAlgorithmMismatch {
132        /// XMLDSig algorithm URI used for diagnostics.
133        uri: String,
134    },
135
136    /// The provided ECDSA signature bytes were neither XMLDSig fixed-width
137    /// nor ASN.1 DER encoded.
138    #[error("invalid ECDSA signature encoding")]
139    InvalidSignatureFormat,
140}
141
142/// Verify an RSA XMLDSig signature using a PEM-encoded SPKI public key.
143///
144/// The PEM must contain a `PUBLIC KEY` block. Returns `Ok(false)` for signature
145/// mismatch and `Err` for algorithm/key preparation errors.
146#[must_use = "discarding the verification result skips signature validation"]
147pub fn verify_rsa_signature_pem(
148    algorithm: SignatureAlgorithm,
149    public_key_pem: &str,
150    signed_data: &[u8],
151    signature_value: &[u8],
152) -> Result<bool, SignatureVerificationError> {
153    let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
154    verify_rsa_signature_spki(
155        algorithm,
156        &public_key_spki_der,
157        signed_data,
158        signature_value,
159    )
160}
161
162/// Verify an ECDSA XMLDSig signature using a PEM-encoded SPKI public key.
163///
164/// The PEM must contain a `PUBLIC KEY` block. The signature value is expected
165/// to use the XMLDSig fixed-width `r || s` format required by RFC 6931 /
166/// XMLDSig 1.1, but ASN.1 DER-encoded ECDSA signatures are also accepted as an
167/// interop fallback. Returns `Ok(false)` for signature mismatch and `Err` for
168/// algorithm/key/signature-format preparation errors (including
169/// `InvalidSignatureFormat` when the bytes are neither valid fixed-width
170/// `r || s` nor valid ASN.1 DER ECDSA).
171#[must_use = "discarding the verification result skips signature validation"]
172pub fn verify_ecdsa_signature_pem(
173    algorithm: SignatureAlgorithm,
174    public_key_pem: &str,
175    signed_data: &[u8],
176    signature_value: &[u8],
177) -> Result<bool, SignatureVerificationError> {
178    let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
179    verify_ecdsa_signature_spki(
180        algorithm,
181        &public_key_spki_der,
182        signed_data,
183        signature_value,
184    )
185}
186
187fn parse_public_key_pem(public_key_pem: &str) -> Result<Vec<u8>, SignatureVerificationError> {
188    let (rest, pem) = x509_parser::pem::parse_x509_pem(public_key_pem.as_bytes())
189        .map_err(|_| SignatureVerificationError::InvalidKeyPem)?;
190    if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
191        return Err(SignatureVerificationError::InvalidKeyPem);
192    }
193    if pem.label != "PUBLIC KEY" {
194        return Err(SignatureVerificationError::InvalidKeyFormat { label: pem.label });
195    }
196
197    Ok(pem.contents)
198}
199
200/// Verify an RSA XMLDSig signature using DER-encoded SPKI public key bytes.
201///
202/// The input must be an X.509 `SubjectPublicKeyInfo` wrapping an RSA key.
203/// Returns `Ok(false)` for signature mismatch and `Err` for algorithm/key
204/// preparation errors.
205#[must_use = "discarding the verification result skips signature validation"]
206pub fn verify_rsa_signature_spki(
207    algorithm: SignatureAlgorithm,
208    public_key_spki_der: &[u8],
209    signed_data: &[u8],
210    signature_value: &[u8],
211) -> Result<bool, SignatureVerificationError> {
212    verify_rsa_signature_spki_with_minimum(
213        algorithm,
214        public_key_spki_der,
215        signed_data,
216        signature_value,
217        2048,
218    )
219}
220
221pub(crate) fn verify_rsa_signature_spki_with_minimum(
222    algorithm: SignatureAlgorithm,
223    public_key_spki_der: &[u8],
224    signed_data: &[u8],
225    signature_value: &[u8],
226    minimum_modulus_bits: usize,
227) -> Result<bool, SignatureVerificationError> {
228    validate_rsa_signature_spki_with_minimum(algorithm, public_key_spki_der, minimum_modulus_bits)?;
229    verify_rsa_signature_spki_primitive(
230        algorithm,
231        public_key_spki_der,
232        signed_data,
233        signature_value,
234    )
235}
236
237pub(crate) fn validate_rsa_signature_spki_with_minimum(
238    algorithm: SignatureAlgorithm,
239    public_key_spki_der: &[u8],
240    minimum_modulus_bits: usize,
241) -> Result<(), SignatureVerificationError> {
242    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
243        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
244    if !rest.is_empty() {
245        return Err(SignatureVerificationError::InvalidKeyDer);
246    }
247    match spki
248        .parsed()
249        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?
250    {
251        PublicKey::RSA(rsa) => validate_rsa_public_key(&rsa, algorithm, minimum_modulus_bits),
252        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
253            uri: algorithm.uri().to_string(),
254        }),
255    }
256}
257
258pub(crate) fn verify_rsa_signature_spki_primitive(
259    algorithm: SignatureAlgorithm,
260    public_key_spki_der: &[u8],
261    signed_data: &[u8],
262    signature_value: &[u8],
263) -> Result<bool, SignatureVerificationError> {
264    ensure_rsa_signature_algorithm(algorithm)?;
265    let key = rsa::RsaPublicKey::from_public_key_der(public_key_spki_der)
266        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
267    let Ok(signature) = RsaPkcs1v15Signature::try_from(signature_value) else {
268        return Ok(false);
269    };
270    let verified = match algorithm {
271        SignatureAlgorithm::RsaSha1 => RsaVerifyingKey::<Sha1>::new(key)
272            .verify(signed_data, &signature)
273            .is_ok(),
274        SignatureAlgorithm::RsaSha256 => RsaVerifyingKey::<Sha256>::new(key)
275            .verify(signed_data, &signature)
276            .is_ok(),
277        SignatureAlgorithm::RsaSha384 => RsaVerifyingKey::<Sha384>::new(key)
278            .verify(signed_data, &signature)
279            .is_ok(),
280        SignatureAlgorithm::RsaSha512 => RsaVerifyingKey::<Sha512>::new(key)
281            .verify(signed_data, &signature)
282            .is_ok(),
283        _ => unreachable!("RSA algorithm checked above"),
284    };
285    Ok(verified)
286}
287
288/// Verify an XMLDSig DSA-SHA1 signature using a DER SPKI public key.
289///
290/// XMLDSig 1.0 encodes the signature as the fixed-width 20-byte `r` followed
291/// by the fixed-width 20-byte `s`, rather than ASN.1 DER.
292#[must_use = "discarding the verification result skips signature validation"]
293pub fn verify_dsa_signature_spki(
294    algorithm: SignatureAlgorithm,
295    public_key_spki_der: &[u8],
296    signed_data: &[u8],
297    signature_value: &[u8],
298) -> Result<bool, SignatureVerificationError> {
299    verify_dsa_signature_spki_with_minimum(
300        algorithm,
301        public_key_spki_der,
302        signed_data,
303        signature_value,
304        crate::policy::DsaKeyPolicy::default().minimum_modulus_bits,
305    )
306}
307
308pub(crate) fn verify_dsa_signature_spki_with_minimum(
309    algorithm: SignatureAlgorithm,
310    public_key_spki_der: &[u8],
311    signed_data: &[u8],
312    signature_value: &[u8],
313    minimum_modulus_bits: usize,
314) -> Result<bool, SignatureVerificationError> {
315    validate_dsa_signature_spki_with_minimum(public_key_spki_der, minimum_modulus_bits)?;
316    verify_dsa_signature_spki_primitive(
317        algorithm,
318        public_key_spki_der,
319        signed_data,
320        signature_value,
321    )
322}
323
324pub(crate) fn validate_dsa_signature_spki_with_minimum(
325    public_key_spki_der: &[u8],
326    minimum_modulus_bits: usize,
327) -> Result<(), SignatureVerificationError> {
328    let key = dsa::VerifyingKey::from_public_key_der(public_key_spki_der)
329        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
330    let modulus_bits = usize::try_from(key.components().p().bits_vartime())
331        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
332    crate::policy::DsaKeyPolicy {
333        minimum_modulus_bits,
334    }
335    .validate_modulus_bits(modulus_bits)
336    .map_err(SignatureVerificationError::KeyPolicy)
337}
338
339pub(crate) fn verify_dsa_signature_spki_primitive(
340    algorithm: SignatureAlgorithm,
341    public_key_spki_der: &[u8],
342    signed_data: &[u8],
343    signature_value: &[u8],
344) -> Result<bool, SignatureVerificationError> {
345    if algorithm != SignatureAlgorithm::DsaSha1 {
346        return Err(SignatureVerificationError::UnsupportedAlgorithm {
347            uri: algorithm.uri().to_string(),
348        });
349    }
350    if signature_value.len() != 40 {
351        return Ok(false);
352    }
353    let key = dsa::VerifyingKey::from_public_key_der(public_key_spki_der)
354        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
355    let Some(signature) = dsa::Signature::from_components(
356        crypto_bigint::BoxedUint::from_be_slice_vartime(&signature_value[..20]),
357        crypto_bigint::BoxedUint::from_be_slice_vartime(&signature_value[20..]),
358    ) else {
359        return Ok(false);
360    };
361    let digest = Sha1::digest(signed_data);
362    Ok(key.verify_prehash(&digest, &signature).is_ok())
363}
364
365/// Verify an ECDSA XMLDSig signature using DER-encoded SPKI public key bytes.
366///
367/// The input must be an X.509 `SubjectPublicKeyInfo` wrapping an EC key. The
368/// signature value may be either XMLDSig fixed-width `r || s` bytes or ASN.1
369/// DER-encoded ECDSA for interop compatibility. Returns `Ok(false)` for
370/// signature mismatch and `Err` for algorithm/key/signature-format preparation
371/// errors.
372#[must_use = "discarding the verification result skips signature validation"]
373pub fn verify_ecdsa_signature_spki(
374    algorithm: SignatureAlgorithm,
375    public_key_spki_der: &[u8],
376    signed_data: &[u8],
377    signature_value: &[u8],
378) -> Result<bool, SignatureVerificationError> {
379    if !matches!(
380        algorithm,
381        SignatureAlgorithm::EcdsaSha256 | SignatureAlgorithm::EcdsaSha384
382    ) {
383        return Err(SignatureVerificationError::UnsupportedAlgorithm {
384            uri: algorithm.uri().to_string(),
385        });
386    }
387
388    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
389        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
390    if !rest.is_empty() {
391        return Err(SignatureVerificationError::InvalidKeyDer);
392    }
393    let public_key = spki
394        .parsed()
395        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
396
397    match public_key {
398        PublicKey::EC(ec) => {
399            validate_ec_public_key_encoding(&ec, &spki.subject_public_key.data)?;
400            let (curve, component_len) = ecdsa_curve_and_component_len(&spki, &ec)?;
401            let signature_encoding =
402                classify_ecdsa_signature_encoding(signature_value, component_len)?;
403            let prehash = match algorithm {
404                SignatureAlgorithm::EcdsaSha256 => Sha256::digest(signed_data).to_vec(),
405                SignatureAlgorithm::EcdsaSha384 => Sha384::digest(signed_data).to_vec(),
406                _ => unreachable!("ECDSA algorithm was validated above"),
407            };
408            match curve {
409                EcCurve::P256 => verify_ecdsa_p256(
410                    &spki.subject_public_key.data,
411                    &prehash,
412                    signature_value,
413                    signature_encoding,
414                ),
415                EcCurve::P384 => verify_ecdsa_p384(
416                    &spki.subject_public_key.data,
417                    &prehash,
418                    signature_value,
419                    signature_encoding,
420                ),
421                EcCurve::P521 => verify_ecdsa_p521(
422                    &spki.subject_public_key.data,
423                    &prehash,
424                    signature_value,
425                    signature_encoding,
426                ),
427            }
428        }
429        _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
430            uri: algorithm.uri().to_string(),
431        }),
432    }
433}
434
435fn validate_rsa_public_key(
436    rsa: &x509_parser::public_key::RSAPublicKey<'_>,
437    algorithm: SignatureAlgorithm,
438    minimum_modulus_bits: usize,
439) -> Result<(), SignatureVerificationError> {
440    ensure_rsa_signature_algorithm(algorithm)?;
441    validate_rsa_key_components(rsa.modulus, rsa.exponent, minimum_modulus_bits)
442}
443
444/// Apply the RSA key-strength invariant shared by XMLDSig and X.509 algorithms.
445pub(crate) fn validate_rsa_key_components(
446    modulus: &[u8],
447    exponent: &[u8],
448    minimum_modulus_bits: usize,
449) -> Result<(), SignatureVerificationError> {
450    crate::policy::RsaKeyPolicy {
451        minimum_modulus_bits,
452    }
453    .validate_components("verification", modulus, exponent)
454    .map(|_| ())
455    .map_err(SignatureVerificationError::KeyPolicy)
456}
457
458fn ensure_rsa_signature_algorithm(
459    algorithm: SignatureAlgorithm,
460) -> Result<(), SignatureVerificationError> {
461    match algorithm {
462        SignatureAlgorithm::RsaSha1
463        | SignatureAlgorithm::RsaSha256
464        | SignatureAlgorithm::RsaSha384
465        | SignatureAlgorithm::RsaSha512 => Ok(()),
466        _ => Err(SignatureVerificationError::UnsupportedAlgorithm {
467            uri: algorithm.uri().to_string(),
468        }),
469    }
470}
471
472#[derive(Clone, Copy, Debug, Eq, PartialEq)]
473enum EcCurve {
474    P256,
475    P384,
476    P521,
477}
478
479fn ecdsa_curve_and_component_len(
480    spki: &SubjectPublicKeyInfo<'_>,
481    ec: &ECPoint<'_>,
482) -> Result<(EcCurve, usize), SignatureVerificationError> {
483    let curve_oid = spki
484        .algorithm
485        .parameters
486        .as_ref()
487        .and_then(|params| params.as_oid().ok())
488        .ok_or(SignatureVerificationError::InvalidKeyDer)?;
489    let point_len = ec.key_size();
490
491    let curve_oid = curve_oid.to_id_string();
492    match (curve_oid.as_str(), point_len) {
493        ("1.2.840.10045.3.1.7", 256) => Ok((EcCurve::P256, 32)),
494        ("1.3.132.0.34", 384) => Ok((EcCurve::P384, 48)),
495        // x509-parser reports the byte-aligned SEC1 point size for P-521.
496        ("1.3.132.0.35", 528) => Ok((EcCurve::P521, 66)),
497        _ => Err(SignatureVerificationError::InvalidKeyDer),
498    }
499}
500
501fn verify_ecdsa_p256(
502    public_key: &[u8],
503    prehash: &[u8],
504    signature_value: &[u8],
505    signature_encoding: EcdsaSignatureEncoding,
506) -> Result<bool, SignatureVerificationError> {
507    let key = P256VerifyingKey::from_sec1_bytes(public_key)
508        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
509    verify_p256_signature(&key, signature_value, signature_encoding, prehash)
510}
511
512fn verify_ecdsa_p384(
513    public_key: &[u8],
514    prehash: &[u8],
515    signature_value: &[u8],
516    signature_encoding: EcdsaSignatureEncoding,
517) -> Result<bool, SignatureVerificationError> {
518    let key = P384VerifyingKey::from_sec1_bytes(public_key)
519        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
520    verify_p384_signature(&key, signature_value, signature_encoding, prehash)
521}
522
523fn verify_ecdsa_p521(
524    public_key: &[u8],
525    prehash: &[u8],
526    signature_value: &[u8],
527    signature_encoding: EcdsaSignatureEncoding,
528) -> Result<bool, SignatureVerificationError> {
529    let key = P521VerifyingKey::from_sec1_bytes(public_key)
530        .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
531    verify_p521_signature(&key, signature_value, signature_encoding, prehash)
532}
533
534fn verify_p256_signature(
535    key: &P256VerifyingKey,
536    signature_value: &[u8],
537    signature_encoding: EcdsaSignatureEncoding,
538    prehash: &[u8],
539) -> Result<bool, SignatureVerificationError> {
540    match signature_encoding {
541        EcdsaSignatureEncoding::XmlDsigFixed => {
542            let signature = P256Signature::from_slice(signature_value)
543                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
544            Ok(key.verify_prehash(prehash, &signature).is_ok())
545        }
546        EcdsaSignatureEncoding::Asn1Der => {
547            let signature = P256Signature::from_der(signature_value)
548                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
549            Ok(key.verify_prehash(prehash, &signature).is_ok())
550        }
551        EcdsaSignatureEncoding::Ambiguous => {
552            if let Ok(signature) = P256Signature::from_der(signature_value)
553                && key.verify_prehash(prehash, &signature).is_ok()
554            {
555                return Ok(true);
556            }
557
558            let signature = P256Signature::from_slice(signature_value)
559                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
560            Ok(key.verify_prehash(prehash, &signature).is_ok())
561        }
562    }
563}
564
565fn verify_p384_signature(
566    key: &P384VerifyingKey,
567    signature_value: &[u8],
568    signature_encoding: EcdsaSignatureEncoding,
569    prehash: &[u8],
570) -> Result<bool, SignatureVerificationError> {
571    match signature_encoding {
572        EcdsaSignatureEncoding::XmlDsigFixed => {
573            let signature = P384Signature::from_slice(signature_value)
574                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
575            Ok(key.verify_prehash(prehash, &signature).is_ok())
576        }
577        EcdsaSignatureEncoding::Asn1Der => {
578            let signature = P384Signature::from_der(signature_value)
579                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
580            Ok(key.verify_prehash(prehash, &signature).is_ok())
581        }
582        EcdsaSignatureEncoding::Ambiguous => {
583            if let Ok(signature) = P384Signature::from_der(signature_value)
584                && key.verify_prehash(prehash, &signature).is_ok()
585            {
586                return Ok(true);
587            }
588
589            let signature = P384Signature::from_slice(signature_value)
590                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
591            Ok(key.verify_prehash(prehash, &signature).is_ok())
592        }
593    }
594}
595
596fn verify_p521_signature(
597    key: &P521VerifyingKey,
598    signature_value: &[u8],
599    signature_encoding: EcdsaSignatureEncoding,
600    prehash: &[u8],
601) -> Result<bool, SignatureVerificationError> {
602    match signature_encoding {
603        EcdsaSignatureEncoding::XmlDsigFixed => {
604            let signature = P521Signature::from_slice(signature_value)
605                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
606            Ok(key.verify_prehash(prehash, &signature).is_ok())
607        }
608        EcdsaSignatureEncoding::Asn1Der => {
609            let signature = P521Signature::from_der(signature_value)
610                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
611            Ok(key.verify_prehash(prehash, &signature).is_ok())
612        }
613        EcdsaSignatureEncoding::Ambiguous => {
614            if let Ok(signature) = P521Signature::from_der(signature_value)
615                && key.verify_prehash(prehash, &signature).is_ok()
616            {
617                return Ok(true);
618            }
619
620            let signature = P521Signature::from_slice(signature_value)
621                .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
622            Ok(key.verify_prehash(prehash, &signature).is_ok())
623        }
624    }
625}
626
627#[derive(Clone, Copy, Debug, Eq, PartialEq)]
628enum EcdsaSignatureEncoding {
629    XmlDsigFixed,
630    Asn1Der,
631    Ambiguous,
632}
633
634fn classify_ecdsa_signature_encoding(
635    signature_value: &[u8],
636    component_len: usize,
637) -> Result<EcdsaSignatureEncoding, SignatureVerificationError> {
638    let expected_len = component_len
639        .checked_mul(2)
640        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
641
642    match inspect_der_encoded_ecdsa_signature(signature_value, component_len) {
643        Ok(Some(())) if signature_value.len() == expected_len => {
644            Ok(EcdsaSignatureEncoding::Ambiguous)
645        }
646        Ok(Some(())) => Ok(EcdsaSignatureEncoding::Asn1Der),
647        Ok(None) | Err(_) if signature_value.len() == expected_len => {
648            Ok(EcdsaSignatureEncoding::XmlDsigFixed)
649        }
650        Ok(None) | Err(_) => Err(SignatureVerificationError::InvalidSignatureFormat),
651    }
652}
653
654fn inspect_der_encoded_ecdsa_signature(
655    signature_value: &[u8],
656    component_len: usize,
657) -> Result<Option<()>, SignatureVerificationError> {
658    let Some((&tag, rest)) = signature_value.split_first() else {
659        return Ok(None);
660    };
661    if tag != 0x30 {
662        return Ok(None);
663    }
664
665    let sequence = parse_der_length(rest)
666        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
667        .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
668    let (sequence_len, sequence_rest) = sequence;
669    let (sequence_content, trailing) = sequence_rest
670        .split_at_checked(sequence_len)
671        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
672    if !trailing.is_empty() {
673        return Err(SignatureVerificationError::InvalidSignatureFormat);
674    }
675
676    let after_r = parse_der_integer(sequence_content, component_len)?;
677    let after_s = parse_der_integer(after_r, component_len)?;
678    if !after_s.is_empty() {
679        return Err(SignatureVerificationError::InvalidSignatureFormat);
680    }
681
682    Ok(Some(()))
683}
684
685fn parse_der_integer(
686    input: &[u8],
687    component_len: usize,
688) -> Result<&[u8], SignatureVerificationError> {
689    let Some((&tag, rest)) = input.split_first() else {
690        return Err(SignatureVerificationError::InvalidSignatureFormat);
691    };
692    if tag != 0x02 {
693        return Err(SignatureVerificationError::InvalidSignatureFormat);
694    }
695
696    let (len, rest) = parse_der_length(rest)
697        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
698        .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
699    let (integer_bytes, remainder) = rest
700        .split_at_checked(len)
701        .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
702
703    if integer_bytes.is_empty() {
704        return Err(SignatureVerificationError::InvalidSignatureFormat);
705    }
706    if integer_bytes.len() > component_len + 1 {
707        return Err(SignatureVerificationError::InvalidSignatureFormat);
708    }
709    if integer_bytes.len() == component_len + 1 && integer_bytes[0] != 0 {
710        return Err(SignatureVerificationError::InvalidSignatureFormat);
711    }
712    if integer_bytes[0] & 0x80 != 0 {
713        return Err(SignatureVerificationError::InvalidSignatureFormat);
714    }
715    if integer_bytes.len() > 1 && integer_bytes[0] == 0 && integer_bytes[1] & 0x80 == 0 {
716        return Err(SignatureVerificationError::InvalidSignatureFormat);
717    }
718
719    Ok(remainder)
720}
721
722fn parse_der_length(input: &[u8]) -> Option<Result<(usize, &[u8]), ()>> {
723    let (&len_byte, rest) = input.split_first()?;
724
725    if len_byte & 0x80 == 0 {
726        return Some(Ok((usize::from(len_byte), rest)));
727    }
728
729    let len_len = usize::from(len_byte & 0x7f);
730    if len_len == 0 || len_len > std::mem::size_of::<usize>() || rest.len() < len_len {
731        return Some(Err(()));
732    }
733
734    let (len_bytes, remainder) = rest.split_at(len_len);
735    if len_bytes[0] == 0 {
736        return Some(Err(()));
737    }
738
739    let mut declared_len = 0_usize;
740    for &byte in len_bytes {
741        declared_len = match declared_len.checked_mul(256) {
742            Some(len) => len,
743            None => return Some(Err(())),
744        };
745        declared_len = match declared_len.checked_add(usize::from(byte)) {
746            Some(len) => len,
747            None => return Some(Err(())),
748        };
749    }
750
751    if declared_len < 128 {
752        return Some(Err(()));
753    }
754
755    Some(Ok((declared_len, remainder)))
756}
757
758fn validate_ec_public_key_encoding(
759    ec: &ECPoint<'_>,
760    public_key_bytes: &[u8],
761) -> Result<(), SignatureVerificationError> {
762    let coordinate_len = ec_coordinate_len_bytes(ec.key_size())?;
763    let expected_len = coordinate_len
764        .checked_mul(2)
765        .and_then(|len| len.checked_add(1))
766        .ok_or(SignatureVerificationError::InvalidKeyDer)?;
767
768    let is_uncompressed_sec1 =
769        public_key_bytes.len() == expected_len && public_key_bytes.first() == Some(&0x04);
770    if !is_uncompressed_sec1 {
771        return Err(SignatureVerificationError::InvalidKeyDer);
772    }
773
774    Ok(())
775}
776
777fn ec_coordinate_len_bytes(key_bits: usize) -> Result<usize, SignatureVerificationError> {
778    key_bits
779        .checked_add(7)
780        .and_then(|bits| bits.checked_div(8))
781        .ok_or(SignatureVerificationError::InvalidKeyDer)
782}
783
784#[cfg(test)]
785#[expect(clippy::unwrap_used, reason = "unit tests use fixed fixture data")]
786mod tests {
787    use super::*;
788
789    #[test]
790    fn ecdsa_algorithms_are_rejected_for_rsa_verification() {
791        for algorithm in [
792            SignatureAlgorithm::EcdsaSha256,
793            SignatureAlgorithm::EcdsaSha384,
794        ] {
795            let err = ensure_rsa_signature_algorithm(algorithm).unwrap_err();
796            assert!(matches!(
797                err,
798                SignatureVerificationError::UnsupportedAlgorithm { .. }
799            ));
800        }
801    }
802
803    #[test]
804    fn malformed_dsa_components_are_verification_misses() {
805        let public_key = include_bytes!(
806            "../../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/certs/lugh.der"
807        );
808        let signature = [0_u8; 40];
809
810        assert!(matches!(
811            verify_dsa_signature_spki_with_minimum(
812                SignatureAlgorithm::DsaSha1,
813                public_key,
814                b"signed",
815                &signature,
816                1024,
817            ),
818            Ok(false)
819        ));
820    }
821
822    #[test]
823    fn spki_signature_framing_uses_the_resolved_key_width() {
824        let rsa = parse_public_key_pem(include_str!(
825            "../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem"
826        ))
827        .expect("RSA fixture must parse");
828        assert!(
829            signature_value_matches_spki(SignatureAlgorithm::RsaSha256, &rsa, &[0; 256]).unwrap()
830        );
831        assert!(
832            !signature_value_matches_spki(SignatureAlgorithm::RsaSha256, &rsa, &[0; 255]).unwrap()
833        );
834
835        let p256 = parse_public_key_pem(include_str!(
836            "../../tests/fixtures/keys/ec/ec-prime256v1-pubkey.pem"
837        ))
838        .expect("P-256 fixture must parse");
839        assert!(
840            signature_value_matches_spki(SignatureAlgorithm::EcdsaSha256, &p256, &[0xAA; 64])
841                .unwrap()
842        );
843        assert!(matches!(
844            signature_value_matches_spki(SignatureAlgorithm::EcdsaSha256, &p256, &[0xAA; 96]),
845            Err(SignatureVerificationError::InvalidSignatureFormat)
846        ));
847    }
848
849    #[test]
850    fn der_like_prefix_with_fixed_width_len_is_classified_as_raw() {
851        let mut signature = vec![0xAA_u8; 96];
852        signature[0] = 0x30;
853        signature[1] = 0x20;
854
855        let encoding = classify_ecdsa_signature_encoding(&signature, 48)
856            .expect("same-width signature with invalid DER must fall back to raw");
857        assert_eq!(encoding, EcdsaSignatureEncoding::XmlDsigFixed);
858    }
859
860    #[test]
861    fn overlong_der_length_below_128_is_rejected() {
862        let bad = [0x81_u8, 0x7f];
863        let parsed = parse_der_length(&bad).expect("length bytes should be present");
864        assert!(
865            matches!(parsed, Err(())),
866            "DER must reject long-form lengths below 128"
867        );
868    }
869
870    #[test]
871    fn ec_coordinate_length_rounds_up_for_non_byte_aligned_curves() {
872        assert_eq!(
873            ec_coordinate_len_bytes(521).expect("521-bit curves require rounded byte length"),
874            66
875        );
876    }
877
878    #[test]
879    fn same_width_valid_der_is_marked_ambiguous() {
880        let mut signature = Vec::with_capacity(64);
881        signature.extend_from_slice(&[0x30, 0x3e, 0x02, 0x1d]);
882        signature.extend(std::iter::repeat_n(0x11_u8, 29));
883        signature.extend_from_slice(&[0x02, 0x1d]);
884        signature.extend(std::iter::repeat_n(0x22_u8, 29));
885
886        let encoding = classify_ecdsa_signature_encoding(&signature, 32)
887            .expect("same-width structurally valid DER should classify as ambiguous");
888        assert_eq!(encoding, EcdsaSignatureEncoding::Ambiguous);
889    }
890
891    #[test]
892    fn der_integer_longer_than_component_requires_sign_byte() {
893        let mut signature = Vec::with_capacity(72);
894        signature.extend_from_slice(&[0x30, 0x46, 0x02, 0x21, 0x01]);
895        signature.extend(std::iter::repeat_n(0x11_u8, 32));
896        signature.extend_from_slice(&[0x02, 0x21, 0x01]);
897        signature.extend(std::iter::repeat_n(0x22_u8, 32));
898
899        let encoding = classify_ecdsa_signature_encoding(&signature, 32);
900        assert!(matches!(
901            encoding,
902            Err(SignatureVerificationError::InvalidSignatureFormat)
903        ));
904    }
905}