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