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