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