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