Skip to main content

xml_sec/xmldsig/
signature.rs

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