Skip to main content

xml_sec/xmldsig/
keys.rs

1//! Configuration and key material for XMLDSig key resolution.
2
3use std::{collections::HashMap, fmt, time::SystemTime};
4
5use crypto_bigint::BoxedUint;
6use dsa::pkcs8::{DecodePublicKey as DsaDecodePublicKey, EncodePublicKey as DsaEncodePublicKey};
7use hmac::{KeyInit, Mac};
8use x509_parser::{
9    prelude::{FromDer, X509Certificate},
10    public_key::PublicKey,
11    x509::SubjectPublicKeyInfo,
12};
13use zeroize::Zeroizing;
14
15use super::signature::{
16    signature_value_matches_spki, signature_value_matches_spki_with_encoding,
17    validate_dsa_signature_spki_with_minimum, validate_rsa_signature_spki_with_minimum,
18    verify_dsa_signature_spki_primitive, verify_dsa_signature_spki_with_minimum,
19    verify_rsa_signature_spki_primitive, verify_rsa_signature_spki_with_minimum,
20};
21use super::{
22    DsigError, KeyInfo, KeyInfoSource, KeyResolver, KeyValueInfo, SignatureAlgorithm, VerifyingKey,
23    X509ChainOptions, X509DataInfo,
24    parse::{
25        EC_P256_OID, EC_P384_OID, EC_P521_OID, ParseError, X509ChainBuildError,
26        build_x509_certificate_paths_to_selector_targets,
27        build_x509_certificate_paths_to_trusted_prefix, distinguished_names_equal,
28        parse_x509_certificate, x509_certificate_matches_any_selector,
29        x509_data_has_lookup_identifiers, x509_selector_categories_match_chain,
30    },
31    verify_ecdsa_signature_spki, verify_ecdsa_signature_spki_with_encoding,
32    x509::verify_x509_certificate_chain_with_provider,
33};
34
35/// Caller-owned HMAC verification key.
36///
37/// Policy-free [`VerifyingKey`] calls enforce [`crate::policy::HmacPolicy::default`].
38/// [`super::VerifyContext`] supplies its immutable operation policy through the
39/// policy-aware hooks, so legacy truncation always requires an explicit opt-in.
40/// Owned secret bytes are zeroized when the key is dropped.
41#[derive(Clone)]
42pub struct HmacVerificationKey {
43    secret: Zeroizing<Vec<u8>>,
44}
45
46impl fmt::Debug for HmacVerificationKey {
47    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48        formatter
49            .debug_struct("HmacVerificationKey")
50            .finish_non_exhaustive()
51    }
52}
53
54impl HmacVerificationKey {
55    /// Construct a key from non-empty secret bytes.
56    pub fn new(secret: impl Into<Vec<u8>>) -> Result<Self, KeyResolutionError> {
57        let secret = secret.into();
58        if secret.is_empty() {
59            return Err(KeyResolutionError::InvalidPublicKey);
60        }
61        Ok(Self {
62            secret: Zeroizing::new(secret),
63        })
64    }
65
66    fn validate_output(
67        &self,
68        policy: crate::policy::HmacPolicy,
69        algorithm: SignatureAlgorithm,
70        signature_value: &[u8],
71    ) -> Result<(), DsigError> {
72        if algorithm.hmac_output_bits().is_none() {
73            return Err(KeyResolutionError::AlgorithmMismatch.into());
74        }
75        policy.validate_key_bytes(self.secret.len())?;
76        policy.validate_output(algorithm, signature_value.len().saturating_mul(8))?;
77        Ok(())
78    }
79
80    fn verify_with_hmac_policy(
81        &self,
82        policy: crate::policy::HmacPolicy,
83        algorithm: SignatureAlgorithm,
84        signed_data: &[u8],
85        signature_value: &[u8],
86    ) -> Result<bool, DsigError> {
87        self.validate_output(policy, algorithm, signature_value)?;
88        macro_rules! verify_hmac {
89            ($digest:ty) => {{
90                let mut mac = hmac::Hmac::<$digest>::new_from_slice(&self.secret)
91                    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
92                mac.update(signed_data);
93                let expected = mac.finalize().into_bytes();
94                subtle::ConstantTimeEq::ct_eq(&expected[..signature_value.len()], signature_value)
95                    .into()
96            }};
97        }
98        Ok(match algorithm {
99            SignatureAlgorithm::HmacSha1 => verify_hmac!(sha1::Sha1),
100            SignatureAlgorithm::HmacSha224 => verify_hmac!(sha2::Sha224),
101            SignatureAlgorithm::HmacSha256 => verify_hmac!(sha2::Sha256),
102            SignatureAlgorithm::HmacSha384 => verify_hmac!(sha2::Sha384),
103            SignatureAlgorithm::HmacSha512 => verify_hmac!(sha2::Sha512),
104            _ => return Err(KeyResolutionError::AlgorithmMismatch.into()),
105        })
106    }
107}
108
109impl VerifyingKey for HmacVerificationKey {
110    fn validate_policy(&self, policy: &crate::policy::VerificationPolicy) -> Result<(), DsigError> {
111        policy
112            .hmac
113            .validate_key_bytes(self.secret.len())
114            .map_err(Into::into)
115    }
116
117    fn validate_signature_value(
118        &self,
119        algorithm: SignatureAlgorithm,
120        signature_value: &[u8],
121    ) -> Result<bool, DsigError> {
122        self.validate_output(
123            crate::policy::HmacPolicy::default(),
124            algorithm,
125            signature_value,
126        )?;
127        Ok(true)
128    }
129
130    fn validate_signature_value_with_policy(
131        &self,
132        policy: &crate::policy::VerificationPolicy,
133        algorithm: SignatureAlgorithm,
134        signature_value: &[u8],
135    ) -> Result<bool, DsigError> {
136        self.validate_output(policy.hmac, algorithm, signature_value)?;
137        Ok(true)
138    }
139
140    fn verify(
141        &self,
142        algorithm: SignatureAlgorithm,
143        signed_data: &[u8],
144        signature_value: &[u8],
145    ) -> Result<bool, DsigError> {
146        self.verify_with_hmac_policy(
147            crate::policy::HmacPolicy::default(),
148            algorithm,
149            signed_data,
150            signature_value,
151        )
152    }
153
154    fn verify_with_policy(
155        &self,
156        policy: &crate::policy::VerificationPolicy,
157        algorithm: SignatureAlgorithm,
158        signed_data: &[u8],
159        signature_value: &[u8],
160    ) -> Result<bool, DsigError> {
161        self.verify_with_hmac_policy(policy.hmac, algorithm, signed_data, signature_value)
162    }
163}
164
165/// Compatibility name for the verification key originally limited to HMAC-SHA1.
166pub type HmacSha1VerificationKey = HmacVerificationKey;
167
168/// A public verification key available to key resolvers.
169#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct VerificationKey {
171    /// Signature algorithm this key is configured to verify.
172    pub algorithm: SignatureAlgorithm,
173    /// DER-encoded SubjectPublicKeyInfo bytes.
174    pub public_key_bytes: Vec<u8>,
175    /// DER certificate from which the key was extracted, when applicable.
176    pub certificate_der: Option<Vec<u8>>,
177    /// Name used to register this key for `<KeyName>` resolution.
178    pub name: Option<String>,
179}
180
181impl VerifyingKey for VerificationKey {
182    fn validate_policy(&self, policy: &crate::policy::VerificationPolicy) -> Result<(), DsigError> {
183        let result = match self.algorithm {
184            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
185                validate_dsa_signature_spki_with_minimum(
186                    &self.public_key_bytes,
187                    policy.key_trust.dsa_keys.minimum_modulus_bits,
188                )
189            }
190            SignatureAlgorithm::RsaSha1
191            | SignatureAlgorithm::RsaSha224
192            | SignatureAlgorithm::RsaSha256
193            | SignatureAlgorithm::RsaSha384
194            | SignatureAlgorithm::RsaSha512 => validate_rsa_signature_spki_with_minimum(
195                self.algorithm,
196                &self.public_key_bytes,
197                policy.key_trust.rsa_keys.minimum_modulus_bits,
198            ),
199            SignatureAlgorithm::HmacSha1
200            | SignatureAlgorithm::HmacSha224
201            | SignatureAlgorithm::HmacSha256
202            | SignatureAlgorithm::HmacSha384
203            | SignatureAlgorithm::HmacSha512
204            | SignatureAlgorithm::EcdsaSha1
205            | SignatureAlgorithm::EcdsaSha224
206            | SignatureAlgorithm::EcdsaSha256
207            | SignatureAlgorithm::EcdsaSha384
208            | SignatureAlgorithm::EcdsaSha512 => Ok(()),
209        };
210        result.map_err(DsigError::Crypto)
211    }
212
213    fn validate_signature_value(
214        &self,
215        algorithm: SignatureAlgorithm,
216        signature_value: &[u8],
217    ) -> Result<bool, DsigError> {
218        if algorithm != self.algorithm {
219            return Err(KeyResolutionError::AlgorithmMismatch.into());
220        }
221        signature_value_matches_spki(algorithm, &self.public_key_bytes, signature_value)
222            .map_err(DsigError::Crypto)
223    }
224
225    fn validate_signature_value_with_policy(
226        &self,
227        policy: &crate::policy::VerificationPolicy,
228        algorithm: SignatureAlgorithm,
229        signature_value: &[u8],
230    ) -> Result<bool, DsigError> {
231        if algorithm != self.algorithm {
232            return Err(KeyResolutionError::AlgorithmMismatch.into());
233        }
234        signature_value_matches_spki_with_encoding(
235            algorithm,
236            &self.public_key_bytes,
237            signature_value,
238            policy.ecdsa_signature_value_encoding,
239        )
240        .map_err(DsigError::Crypto)
241    }
242
243    fn verify(
244        &self,
245        algorithm: SignatureAlgorithm,
246        signed_data: &[u8],
247        signature_value: &[u8],
248    ) -> Result<bool, DsigError> {
249        if algorithm != self.algorithm {
250            return Err(KeyResolutionError::AlgorithmMismatch.into());
251        }
252        let result = match algorithm {
253            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
254                verify_dsa_signature_spki_primitive(
255                    algorithm,
256                    &self.public_key_bytes,
257                    signed_data,
258                    signature_value,
259                )
260            }
261            SignatureAlgorithm::HmacSha1
262            | SignatureAlgorithm::HmacSha224
263            | SignatureAlgorithm::HmacSha256
264            | SignatureAlgorithm::HmacSha384
265            | SignatureAlgorithm::HmacSha512 => {
266                return Err(KeyResolutionError::AlgorithmMismatch.into());
267            }
268            SignatureAlgorithm::RsaSha1
269            | SignatureAlgorithm::RsaSha224
270            | SignatureAlgorithm::RsaSha256
271            | SignatureAlgorithm::RsaSha384
272            | SignatureAlgorithm::RsaSha512 => verify_rsa_signature_spki_primitive(
273                algorithm,
274                &self.public_key_bytes,
275                signed_data,
276                signature_value,
277            ),
278            SignatureAlgorithm::EcdsaSha1
279            | SignatureAlgorithm::EcdsaSha224
280            | SignatureAlgorithm::EcdsaSha256
281            | SignatureAlgorithm::EcdsaSha384
282            | SignatureAlgorithm::EcdsaSha512 => verify_ecdsa_signature_spki(
283                algorithm,
284                &self.public_key_bytes,
285                signed_data,
286                signature_value,
287            ),
288        };
289        result.map_err(DsigError::Crypto)
290    }
291
292    fn verify_with_policy(
293        &self,
294        policy: &crate::policy::VerificationPolicy,
295        algorithm: SignatureAlgorithm,
296        signed_data: &[u8],
297        signature_value: &[u8],
298    ) -> Result<bool, DsigError> {
299        if algorithm != self.algorithm {
300            return Err(KeyResolutionError::AlgorithmMismatch.into());
301        }
302        if matches!(
303            algorithm,
304            SignatureAlgorithm::EcdsaSha1
305                | SignatureAlgorithm::EcdsaSha224
306                | SignatureAlgorithm::EcdsaSha256
307                | SignatureAlgorithm::EcdsaSha384
308                | SignatureAlgorithm::EcdsaSha512
309        ) {
310            return verify_ecdsa_signature_spki_with_encoding(
311                algorithm,
312                &self.public_key_bytes,
313                signed_data,
314                signature_value,
315                policy.ecdsa_signature_value_encoding,
316            )
317            .map_err(DsigError::Crypto);
318        }
319        self.verify(algorithm, signed_data, signature_value)
320    }
321}
322
323struct PolicyBoundVerificationKey {
324    key: VerificationKey,
325    rsa_minimum_bits: usize,
326    dsa_minimum_bits: usize,
327}
328
329impl VerifyingKey for PolicyBoundVerificationKey {
330    fn validate_signature_value(
331        &self,
332        algorithm: SignatureAlgorithm,
333        signature_value: &[u8],
334    ) -> Result<bool, DsigError> {
335        self.key
336            .validate_signature_value(algorithm, signature_value)
337    }
338
339    fn validate_signature_value_with_policy(
340        &self,
341        policy: &crate::policy::VerificationPolicy,
342        algorithm: SignatureAlgorithm,
343        signature_value: &[u8],
344    ) -> Result<bool, DsigError> {
345        self.key
346            .validate_signature_value_with_policy(policy, algorithm, signature_value)
347    }
348
349    fn verify(
350        &self,
351        algorithm: SignatureAlgorithm,
352        signed_data: &[u8],
353        signature_value: &[u8],
354    ) -> Result<bool, DsigError> {
355        if algorithm != self.key.algorithm {
356            return Err(KeyResolutionError::AlgorithmMismatch.into());
357        }
358        let result = match algorithm {
359            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
360                verify_dsa_signature_spki_with_minimum(
361                    algorithm,
362                    &self.key.public_key_bytes,
363                    signed_data,
364                    signature_value,
365                    self.dsa_minimum_bits,
366                )
367            }
368            SignatureAlgorithm::RsaSha1
369            | SignatureAlgorithm::RsaSha224
370            | SignatureAlgorithm::RsaSha256
371            | SignatureAlgorithm::RsaSha384
372            | SignatureAlgorithm::RsaSha512 => verify_rsa_signature_spki_with_minimum(
373                algorithm,
374                &self.key.public_key_bytes,
375                signed_data,
376                signature_value,
377                self.rsa_minimum_bits,
378            ),
379            _ => return self.key.verify(algorithm, signed_data, signature_value),
380        };
381        result.map_err(DsigError::Crypto)
382    }
383
384    fn verify_with_policy(
385        &self,
386        policy: &crate::policy::VerificationPolicy,
387        algorithm: SignatureAlgorithm,
388        signed_data: &[u8],
389        signature_value: &[u8],
390    ) -> Result<bool, DsigError> {
391        if matches!(
392            algorithm,
393            SignatureAlgorithm::EcdsaSha1
394                | SignatureAlgorithm::EcdsaSha224
395                | SignatureAlgorithm::EcdsaSha256
396                | SignatureAlgorithm::EcdsaSha384
397                | SignatureAlgorithm::EcdsaSha512
398        ) {
399            return self
400                .key
401                .verify_with_policy(policy, algorithm, signed_data, signature_value);
402        }
403        self.verify(algorithm, signed_data, signature_value)
404    }
405}
406
407/// Failures while applying [`KeyResolverConfig`] to parsed key material.
408#[derive(Debug, thiserror::Error)]
409#[non_exhaustive]
410pub enum KeyResolutionError {
411    /// A configured or embedded key does not match the signature method.
412    #[error("verification key does not match the signature algorithm")]
413    AlgorithmMismatch,
414    /// An embedded certificate could not be parsed completely.
415    #[error("invalid embedded certificate DER")]
416    InvalidCertificate,
417    /// Configured or embedded public key DER could not be parsed completely.
418    #[error("invalid public key DER")]
419    InvalidPublicKey,
420    /// More than one configured certificate satisfies all X.509 selectors.
421    #[error("X.509 lookup selectors match multiple configured certificates")]
422    AmbiguousCertificate,
423    /// An X.509 selector uses a digest algorithm unsupported by this crate.
424    #[error("unsupported X.509 digest algorithm: {0}")]
425    UnsupportedDigestAlgorithm(String),
426    /// Embedded certificate path validation failed.
427    #[error("certificate chain validation failed: {0}")]
428    Chain(#[from] super::X509ChainError),
429    /// System time was unavailable for certificate validation.
430    #[error("system time is unavailable")]
431    SystemTime,
432}
433
434/// Configuration for the default XMLDSig key resolver.
435///
436/// The configuration owns all key material and has no global registry. Chain
437/// verification is opt-in so callers that pin an embedded certificate can use
438/// the documented TOFU model without constructing a certificate path.
439#[derive(Debug, Clone, Default, PartialEq, Eq)]
440pub struct KeyResolverConfig {
441    /// DER-encoded certificates available to X.509 selectors and as untrusted
442    /// path intermediates. They establish trust only by chaining to an entry in
443    /// [`Self::trusted_certs`].
444    pub lookup_certs: Vec<Vec<u8>>,
445    /// DER-encoded certificates accepted as trust anchors.
446    pub trusted_certs: Vec<Vec<u8>>,
447    /// Verification keys addressable by `<KeyName>` content.
448    pub named_keys: HashMap<String, VerificationKey>,
449}
450
451/// Configuration-driven resolver for embedded certificates, DER keys, and key names.
452#[derive(Debug, Clone, Default)]
453pub struct DefaultKeyResolver {
454    config: KeyResolverConfig,
455}
456
457/// Counts candidates actually inspected by one resolver invocation.
458///
459/// Parser cardinality preflights prevent expensive materialization, but do not
460/// replace this runtime accounting: embedded and indirect candidates both
461/// consume resolver work when inspected.
462struct InspectedKeyCandidateBudget {
463    maximum: usize,
464    attempted: usize,
465}
466
467impl InspectedKeyCandidateBudget {
468    fn new(maximum: usize) -> Self {
469        Self {
470            maximum,
471            attempted: 0,
472        }
473    }
474
475    fn charge(&mut self) -> Result<(), DsigError> {
476        self.charge_many(1)
477    }
478
479    fn charge_many(&mut self, count: usize) -> Result<(), DsigError> {
480        self.attempted = self.attempted.saturating_add(count);
481        if self.attempted > self.maximum {
482            return Err(crate::policy::PolicyViolation::ResourceLimit {
483                resource: crate::policy::resource_name::KEY_CANDIDATES,
484                maximum: self.maximum,
485                actual: self.attempted,
486            }
487            .into());
488        }
489        Ok(())
490    }
491}
492
493fn validate_key_info_source_permissions(
494    key_info: &KeyInfo,
495    allowed: crate::policy::KeySourcePolicy,
496) -> Result<(), crate::policy::PolicyViolation> {
497    for source in &key_info.sources {
498        let disabled_reason = match source {
499            KeyInfoSource::X509Data(_) if !allowed.x509_data => {
500                Some("X509Data key sources are disabled")
501            }
502            KeyInfoSource::DerEncodedKeyValue(_) if !allowed.der_encoded_key_value => {
503                Some("DEREncodedKeyValue key sources are disabled")
504            }
505            KeyInfoSource::KeyName(_) if !allowed.key_name => {
506                Some("KeyName key sources are disabled")
507            }
508            KeyInfoSource::KeyValue(_) if !allowed.key_value => {
509                Some("KeyValue key sources are disabled")
510            }
511            KeyInfoSource::KeyInfoReference { .. } if !allowed.key_info_reference => {
512                Some("KeyInfoReference key sources are disabled")
513            }
514            KeyInfoSource::X509Data(_)
515            | KeyInfoSource::DerEncodedKeyValue(_)
516            | KeyInfoSource::KeyName(_)
517            | KeyInfoSource::KeyValue(_)
518            | KeyInfoSource::RetrievalMethod { .. }
519            | KeyInfoSource::KeyInfoReference { .. } => None,
520        };
521        if let Some(reason) = disabled_reason {
522            return Err(crate::policy::PolicyViolation::KeyTrust { reason });
523        }
524    }
525    Ok(())
526}
527
528impl DefaultKeyResolver {
529    /// Construct a resolver from explicit caller-owned key and certificate stores.
530    #[must_use]
531    pub fn new(config: KeyResolverConfig) -> Self {
532        Self { config }
533    }
534
535    /// Borrow the active resolver configuration.
536    #[must_use]
537    pub fn config(&self) -> &KeyResolverConfig {
538        &self.config
539    }
540
541    fn resolve_x509(
542        &self,
543        info: &X509DataInfo,
544        algorithm: SignatureAlgorithm,
545        trust: &crate::policy::KeyTrustPolicy,
546        provider: &dyn crate::provider::CryptoProvider,
547        budget: &mut InspectedKeyCandidateBudget,
548    ) -> Result<Option<VerificationKey>, DsigError> {
549        let certificate_der = if let Some(&signing_index) = info.certificate_chain.first() {
550            if trust.verify_x509_chains {
551                self.prepare_embedded_x509(info, signing_index, trust, provider, budget)?;
552            } else {
553                budget.charge_many(info.certificates.len())?;
554            }
555            info.certificates
556                .get(signing_index)
557                .ok_or(KeyResolutionError::InvalidCertificate)?
558                .clone()
559        } else {
560            let Some(selected) = self.resolve_configured_x509(info, trust, provider, budget)?
561            else {
562                return Ok(None);
563            };
564            selected
565                .certificate_chain
566                .first()
567                .and_then(|index| selected.certificates.get(*index))
568                .ok_or(KeyResolutionError::InvalidCertificate)?
569                .clone()
570        };
571
572        let (rest, certificate) = X509Certificate::from_der(&certificate_der)
573            .map_err(|_| KeyResolutionError::InvalidCertificate)?;
574        if !rest.is_empty() {
575            return Err(KeyResolutionError::InvalidCertificate.into());
576        }
577        let public_key_bytes = certificate.public_key().raw.to_vec();
578        validate_spki_algorithm(&public_key_bytes, algorithm)?;
579        Ok(Some(VerificationKey {
580            algorithm,
581            public_key_bytes,
582            certificate_der: Some(certificate_der),
583            name: None,
584        }))
585    }
586
587    fn verify_x509_policy(
588        &self,
589        info: &X509DataInfo,
590        trust: &crate::policy::KeyTrustPolicy,
591        provider: &dyn crate::provider::CryptoProvider,
592    ) -> Result<(), KeyResolutionError> {
593        let options = X509ChainOptions {
594            trusted_certs: &self.config.trusted_certs,
595            verification_time: trust.verification_time.unwrap_or_else(SystemTime::now),
596            max_chain_depth: trust.max_x509_chain_depth,
597            check_crls: trust.check_crls,
598            allowed_extended_key_usages: Some(&trust.allowed_extended_key_usages),
599            rsa_keys: trust.rsa_keys,
600            dsa_keys: trust.dsa_keys,
601        };
602        verify_x509_certificate_chain_with_provider(info, &options, provider)?;
603        Ok(())
604    }
605
606    fn prepare_embedded_x509(
607        &self,
608        info: &X509DataInfo,
609        signing_index: usize,
610        trust: &crate::policy::KeyTrustPolicy,
611        provider: &dyn crate::provider::CryptoProvider,
612        budget: &mut InspectedKeyCandidateBudget,
613    ) -> Result<X509DataInfo, DsigError> {
614        let signing_der = info
615            .certificates
616            .get(signing_index)
617            .ok_or(KeyResolutionError::InvalidCertificate)?;
618        let mut available = X509DataInfo {
619            crls: info.crls.clone(),
620            ..X509DataInfo::default()
621        };
622        let mut trusted_prefix_len = 0;
623        for certificate in &self.config.trusted_certs {
624            budget.charge()?;
625            if available
626                .certificates
627                .iter()
628                .any(|known| known == certificate)
629            {
630                continue;
631            }
632            available.parsed_certificates.push(
633                parse_x509_certificate(certificate)
634                    .map_err(|_| KeyResolutionError::InvalidCertificate)?,
635            );
636            available.certificates.push(certificate.clone());
637            trusted_prefix_len += 1;
638        }
639        for certificate in self.config.lookup_certs.iter().chain(&info.certificates) {
640            budget.charge()?;
641            if available
642                .certificates
643                .iter()
644                .any(|known| known == certificate)
645            {
646                continue;
647            }
648            available.parsed_certificates.push(
649                parse_x509_certificate(certificate)
650                    .map_err(|_| KeyResolutionError::InvalidCertificate)?,
651            );
652            available.certificates.push(certificate.clone());
653        }
654        let signing_index = available
655            .certificates
656            .iter()
657            .position(|certificate| certificate == signing_der)
658            .ok_or(KeyResolutionError::InvalidCertificate)?;
659        self.select_valid_x509_path(
660            &mut available,
661            signing_index,
662            trusted_prefix_len,
663            trust,
664            provider,
665            None,
666        )?;
667        Ok(available)
668    }
669
670    fn select_valid_x509_path(
671        &self,
672        available: &mut X509DataInfo,
673        signing_index: usize,
674        trusted_prefix_len: usize,
675        trust: &crate::policy::KeyTrustPolicy,
676        provider: &dyn crate::provider::CryptoProvider,
677        selectors: Option<&X509DataInfo>,
678    ) -> Result<bool, KeyResolutionError> {
679        let candidates = build_x509_certificate_paths_to_trusted_prefix(
680            available,
681            signing_index,
682            trusted_prefix_len,
683            trust.max_x509_chain_depth,
684            trust.max_x509_candidate_paths,
685            provider,
686        )
687        .map_err(|error| match error {
688            X509ChainBuildError::AmbiguousIssuer => KeyResolutionError::AmbiguousCertificate,
689            X509ChainBuildError::Provider(error) => {
690                KeyResolutionError::Chain(super::X509ChainError::Provider(error))
691            }
692            X509ChainBuildError::UnsupportedSignatureAlgorithm { oid } => {
693                KeyResolutionError::Chain(super::X509ChainError::UnsupportedSignatureAlgorithm {
694                    oid,
695                })
696            }
697            _ => KeyResolutionError::InvalidCertificate,
698        })?;
699        let mut first_error = None;
700        let mut valid_path_without_selector_match = false;
701        for candidate in candidates {
702            available.certificate_chain = candidate;
703            match self.verify_x509_policy(available, trust, provider) {
704                Ok(()) => {
705                    if match selectors {
706                        Some(selectors) => {
707                            selected_x509_path_matches_selectors(available, selectors, provider)?
708                        }
709                        None => true,
710                    } {
711                        return Ok(true);
712                    }
713                    valid_path_without_selector_match = true;
714                }
715                Err(error) => {
716                    first_error.get_or_insert(error);
717                }
718            }
719        }
720        if valid_path_without_selector_match {
721            return Ok(false);
722        }
723        Err(first_error.unwrap_or(KeyResolutionError::Chain(
724            super::X509ChainError::UntrustedRoot,
725        )))
726    }
727
728    fn select_x509_selector_path(
729        &self,
730        available: &mut X509DataInfo,
731        signing_index: usize,
732        matching_indices: &[usize],
733        trust: &crate::policy::KeyTrustPolicy,
734        provider: &dyn crate::provider::CryptoProvider,
735        selectors: &X509DataInfo,
736    ) -> Result<bool, KeyResolutionError> {
737        let targets = matching_indices
738            .iter()
739            .copied()
740            .filter(|index| *index != signing_index)
741            .collect::<Vec<_>>();
742        if targets.is_empty() {
743            return Ok(false);
744        }
745        let candidates = build_x509_certificate_paths_to_selector_targets(
746            available,
747            signing_index,
748            &targets,
749            trust.max_x509_chain_depth,
750            trust.max_x509_candidate_paths,
751            provider,
752        )
753        .map_err(|error| match error {
754            X509ChainBuildError::AmbiguousIssuer => KeyResolutionError::AmbiguousCertificate,
755            X509ChainBuildError::Provider(error) => {
756                KeyResolutionError::Chain(super::X509ChainError::Provider(error))
757            }
758            X509ChainBuildError::UnsupportedSignatureAlgorithm { oid } => {
759                KeyResolutionError::Chain(super::X509ChainError::UnsupportedSignatureAlgorithm {
760                    oid,
761                })
762            }
763            _ => KeyResolutionError::InvalidCertificate,
764        })?;
765        for candidate in candidates {
766            available.certificate_chain = candidate;
767            if selected_x509_path_matches_selectors(available, selectors, provider)? {
768                return Ok(true);
769            }
770        }
771        Ok(false)
772    }
773
774    fn resolve_configured_x509(
775        &self,
776        info: &X509DataInfo,
777        trust: &crate::policy::KeyTrustPolicy,
778        provider: &dyn crate::provider::CryptoProvider,
779        budget: &mut InspectedKeyCandidateBudget,
780    ) -> Result<Option<X509DataInfo>, DsigError> {
781        if !x509_data_has_lookup_identifiers(info) {
782            return Ok(None);
783        }
784
785        let mut available = X509DataInfo {
786            subject_names: info.subject_names.clone(),
787            issuer_serials: info.issuer_serials.clone(),
788            skis: info.skis.clone(),
789            crls: info.crls.clone(),
790            digests: info.digests.clone(),
791            ..X509DataInfo::default()
792        };
793        let mut matches = Vec::new();
794        let mut trusted_prefix_len = 0usize;
795        for (trusted, certificate_der) in self
796            .config
797            .trusted_certs
798            .iter()
799            .map(|certificate| (true, certificate))
800            .chain(
801                self.config
802                    .lookup_certs
803                    .iter()
804                    .map(|certificate| (false, certificate)),
805            )
806        {
807            budget.charge()?;
808            if available
809                .certificates
810                .iter()
811                .any(|available_der| available_der == certificate_der)
812            {
813                continue;
814            }
815            let parsed = parse_x509_certificate(certificate_der)
816                .map_err(|_| KeyResolutionError::InvalidCertificate)?;
817            let is_match =
818                x509_certificate_matches_any_selector(info, &parsed, certificate_der, provider)
819                    .map_err(map_x509_selector_error)?;
820            if is_match {
821                matches.push((available.certificates.len(), parsed.clone()));
822            }
823            available.certificates.push(certificate_der.clone());
824            available.parsed_certificates.push(parsed);
825            if trusted {
826                trusted_prefix_len += 1;
827            }
828        }
829
830        let matched_chain = X509DataInfo {
831            certificates: matches
832                .iter()
833                .map(|(index, _)| available.certificates[*index].clone())
834                .collect(),
835            parsed_certificates: matches.iter().map(|(_, parsed)| parsed.clone()).collect(),
836            ..X509DataInfo::default()
837        };
838        if !x509_selector_categories_match_chain(
839            &X509DataInfo {
840                subject_names: info.subject_names.clone(),
841                issuer_serials: info.issuer_serials.clone(),
842                skis: info.skis.clone(),
843                digests: info.digests.clone(),
844                ..matched_chain
845            },
846            provider,
847        )
848        .map_err(map_x509_selector_error)?
849        {
850            return Ok(None);
851        }
852
853        let signing_index = match matches.as_slice() {
854            [] => return Ok(None),
855            [(index, _)] => *index,
856            _ => {
857                let leaves = matches
858                    .iter()
859                    .filter(|(_, candidate)| {
860                        !distinguished_names_equal(&candidate.subject_dn, &candidate.issuer_dn)
861                            && !matches.iter().any(|(_, other)| {
862                                distinguished_names_equal(&other.issuer_dn, &candidate.subject_dn)
863                            })
864                    })
865                    .collect::<Vec<_>>();
866                match leaves.as_slice() {
867                    [(index, _)] => *index,
868                    _ => return Err(KeyResolutionError::AmbiguousCertificate.into()),
869                }
870            }
871        };
872        let matching_indices = matches.iter().map(|(index, _)| *index).collect::<Vec<_>>();
873        // `available` preserves trusted certificates as a prefix. Selecting
874        // one of those exact certificates is already a terminal trust
875        // decision, even when the certificate is not self-signed.
876        available.certificate_chain =
877            if signing_index < trusted_prefix_len || !trust.verify_x509_chains {
878                vec![signing_index]
879            } else {
880                if !self.select_valid_x509_path(
881                    &mut available,
882                    signing_index,
883                    trusted_prefix_len,
884                    trust,
885                    provider,
886                    Some(info),
887                )? {
888                    return Ok(None);
889                }
890                available.certificate_chain.clone()
891            };
892        if trust.verify_x509_chains && signing_index < trusted_prefix_len {
893            self.verify_x509_policy(&available, trust, provider)?;
894        }
895        if !trust.verify_x509_chains || signing_index < trusted_prefix_len {
896            let direct_match = selected_x509_path_matches_selectors(&available, info, provider)?;
897            if !direct_match
898                && (signing_index < trusted_prefix_len
899                    || !self.select_x509_selector_path(
900                        &mut available,
901                        signing_index,
902                        &matching_indices,
903                        trust,
904                        provider,
905                        info,
906                    )?)
907            {
908                return Ok(None);
909            }
910        }
911        Ok(Some(available))
912    }
913
914    fn resolve_key_value(
915        key_value: &KeyValueInfo,
916        algorithm: SignatureAlgorithm,
917    ) -> Result<Option<VerificationKey>, KeyResolutionError> {
918        let public_key_bytes = match key_value {
919            KeyValueInfo::Dsa { p, q, g, y } => {
920                if !matches!(
921                    algorithm,
922                    SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256
923                ) {
924                    return Err(KeyResolutionError::AlgorithmMismatch);
925                }
926                let (Some(p), Some(q), Some(g)) = (p.as_deref(), q.as_deref(), g.as_deref()) else {
927                    return Err(KeyResolutionError::InvalidPublicKey);
928                };
929                dsa_key_value_to_spki_der(p, q, g, y)?
930            }
931            KeyValueInfo::Rsa { modulus, exponent } => {
932                if !matches!(
933                    algorithm,
934                    SignatureAlgorithm::RsaSha1
935                        | SignatureAlgorithm::RsaSha224
936                        | SignatureAlgorithm::RsaSha256
937                        | SignatureAlgorithm::RsaSha384
938                        | SignatureAlgorithm::RsaSha512
939                ) {
940                    return Err(KeyResolutionError::AlgorithmMismatch);
941                }
942                rsa_key_value_to_spki_der(modulus, exponent)?
943            }
944            KeyValueInfo::Ec {
945                curve_oid,
946                public_key,
947            } => {
948                if !matches!(
949                    algorithm,
950                    SignatureAlgorithm::EcdsaSha1
951                        | SignatureAlgorithm::EcdsaSha224
952                        | SignatureAlgorithm::EcdsaSha256
953                        | SignatureAlgorithm::EcdsaSha384
954                        | SignatureAlgorithm::EcdsaSha512
955                ) {
956                    return Ok(None);
957                }
958                ec_key_value_to_spki_der(curve_oid, public_key)?
959            }
960            KeyValueInfo::InvalidEcKeyValue => return Err(KeyResolutionError::InvalidPublicKey),
961            KeyValueInfo::Unsupported { .. } => return Ok(None),
962        };
963        validate_spki_algorithm(&public_key_bytes, algorithm)?;
964
965        Ok(Some(VerificationKey {
966            algorithm,
967            public_key_bytes,
968            certificate_der: None,
969            name: None,
970        }))
971    }
972
973    fn resolve_with_trust<'a>(
974        &'a self,
975        key_info: Option<&KeyInfo>,
976        algorithm: SignatureAlgorithm,
977        sources: crate::policy::KeySourcePolicy,
978        trust: &crate::policy::KeyTrustPolicy,
979        resources: &crate::policy::ResourcePolicy,
980        provider: &dyn crate::provider::CryptoProvider,
981    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
982        trust.validate()?;
983        resources.validate()?;
984        let Some(key_info) = key_info else {
985            return Ok(None);
986        };
987        validate_key_info_source_permissions(key_info, sources)?;
988        let mut candidate_budget = InspectedKeyCandidateBudget::new(resources.max_key_candidates);
989        let mut deferred_key_value_error = None;
990        for source in &key_info.sources {
991            let resolved = match source {
992                KeyInfoSource::X509Data(info) => {
993                    self.resolve_x509(info, algorithm, trust, provider, &mut candidate_budget)?
994                }
995                KeyInfoSource::DerEncodedKeyValue(public_key_bytes) => {
996                    candidate_budget.charge()?;
997                    validate_spki_algorithm(public_key_bytes, algorithm)?;
998                    Some(VerificationKey {
999                        algorithm,
1000                        public_key_bytes: public_key_bytes.clone(),
1001                        certificate_der: None,
1002                        name: None,
1003                    })
1004                }
1005                KeyInfoSource::KeyName(name) => {
1006                    candidate_budget.charge()?;
1007                    self.config
1008                        .named_keys
1009                        .get(name)
1010                        .map(|key| {
1011                            if key.algorithm != algorithm {
1012                                return Err(KeyResolutionError::AlgorithmMismatch);
1013                            }
1014                            validate_spki_algorithm(&key.public_key_bytes, algorithm)?;
1015                            Ok(key.clone())
1016                        })
1017                        .transpose()?
1018                }
1019                KeyInfoSource::KeyValue(key_value) => {
1020                    candidate_budget.charge()?;
1021                    match Self::resolve_key_value(key_value, algorithm) {
1022                        Ok(resolved) => resolved,
1023                        Err(error) if key_value_error_allows_fallback(key_value, &error) => {
1024                            deferred_key_value_error.get_or_insert(error);
1025                            None
1026                        }
1027                        Err(error) => return Err(error.into()),
1028                    }
1029                }
1030                KeyInfoSource::RetrievalMethod { .. } => {
1031                    candidate_budget.charge()?;
1032                    None
1033                }
1034                KeyInfoSource::KeyInfoReference { .. } => {
1035                    candidate_budget.charge()?;
1036                    None
1037                }
1038            };
1039            if let Some(key) = resolved {
1040                return Ok(Some(Box::new(PolicyBoundVerificationKey {
1041                    key,
1042                    rsa_minimum_bits: trust.rsa_keys.minimum_modulus_bits,
1043                    dsa_minimum_bits: trust.dsa_keys.minimum_modulus_bits,
1044                })));
1045            }
1046        }
1047        if let Some(error) = deferred_key_value_error {
1048            return Err(error.into());
1049        }
1050        Ok(None)
1051    }
1052}
1053
1054impl KeyResolver for DefaultKeyResolver {
1055    fn resolve<'a>(
1056        &'a self,
1057        key_info: Option<&KeyInfo>,
1058        algorithm: SignatureAlgorithm,
1059    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1060        let policy = crate::policy::VerificationPolicy::default();
1061        self.resolve_with_trust(
1062            key_info,
1063            algorithm,
1064            policy.key_sources,
1065            &policy.key_trust,
1066            &policy.resources,
1067            crate::provider::default_provider(),
1068        )
1069    }
1070
1071    fn resolve_with_policy<'a>(
1072        &'a self,
1073        key_info: Option<&KeyInfo>,
1074        algorithm: SignatureAlgorithm,
1075        policy: &crate::policy::VerificationPolicy,
1076    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1077        self.resolve_with_policy_and_provider(
1078            key_info,
1079            algorithm,
1080            policy,
1081            crate::provider::default_provider(),
1082        )
1083    }
1084
1085    fn resolve_with_policy_and_provider<'a>(
1086        &'a self,
1087        key_info: Option<&KeyInfo>,
1088        algorithm: SignatureAlgorithm,
1089        policy: &crate::policy::VerificationPolicy,
1090        provider: &dyn crate::provider::CryptoProvider,
1091    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1092        self.resolve_with_trust(
1093            key_info,
1094            algorithm,
1095            policy.key_sources,
1096            &policy.key_trust,
1097            &policy.resources,
1098            provider,
1099        )
1100    }
1101
1102    fn consumes_document_key_info(&self) -> bool {
1103        true
1104    }
1105}
1106
1107fn map_x509_selector_error(error: ParseError) -> DsigError {
1108    match error {
1109        ParseError::Provider(error) => DsigError::Provider(error),
1110        ParseError::UnsupportedAlgorithm { uri } => {
1111            KeyResolutionError::UnsupportedDigestAlgorithm(uri).into()
1112        }
1113        _ => KeyResolutionError::InvalidCertificate.into(),
1114    }
1115}
1116
1117fn selected_x509_path_matches_selectors(
1118    available: &X509DataInfo,
1119    selectors: &X509DataInfo,
1120    provider: &dyn crate::provider::CryptoProvider,
1121) -> Result<bool, KeyResolutionError> {
1122    let selected = X509DataInfo {
1123        subject_names: selectors.subject_names.clone(),
1124        issuer_serials: selectors.issuer_serials.clone(),
1125        skis: selectors.skis.clone(),
1126        digests: selectors.digests.clone(),
1127        certificates: available
1128            .certificate_chain
1129            .iter()
1130            .map(|index| available.certificates[*index].clone())
1131            .collect(),
1132        parsed_certificates: available
1133            .certificate_chain
1134            .iter()
1135            .map(|index| available.parsed_certificates[*index].clone())
1136            .collect(),
1137        ..X509DataInfo::default()
1138    };
1139    x509_selector_categories_match_chain(&selected, provider).map_err(|error| match error {
1140        ParseError::Provider(error) => {
1141            KeyResolutionError::Chain(super::X509ChainError::Provider(error))
1142        }
1143        ParseError::UnsupportedAlgorithm { uri } => {
1144            KeyResolutionError::UnsupportedDigestAlgorithm(uri)
1145        }
1146        _ => KeyResolutionError::InvalidCertificate,
1147    })
1148}
1149
1150fn rsa_key_value_to_spki_der(
1151    modulus: &[u8],
1152    exponent: &[u8],
1153) -> Result<Vec<u8>, KeyResolutionError> {
1154    let key = rsa::RsaPublicKey::new(
1155        BoxedUint::from_be_slice_vartime(modulus),
1156        BoxedUint::from_be_slice_vartime(exponent),
1157    )
1158    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1159    key.to_public_key_der()
1160        .map_err(|_| KeyResolutionError::InvalidPublicKey)
1161        .map(|der| der.as_bytes().to_vec())
1162}
1163
1164fn dsa_key_value_to_spki_der(
1165    p: &[u8],
1166    q: &[u8],
1167    g: &[u8],
1168    y: &[u8],
1169) -> Result<Vec<u8>, KeyResolutionError> {
1170    let components = dsa::Components::from_components(
1171        BoxedUint::from_be_slice_vartime(p),
1172        BoxedUint::from_be_slice_vartime(q),
1173        BoxedUint::from_be_slice_vartime(g),
1174    )
1175    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1176    dsa::VerifyingKey::from_components(components, BoxedUint::from_be_slice_vartime(y))
1177        .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1178        .to_public_key_der()
1179        .map_err(|_| KeyResolutionError::InvalidPublicKey)
1180        .map(|der| der.as_bytes().to_vec())
1181}
1182
1183fn ec_key_value_to_spki_der(
1184    curve_oid: &str,
1185    public_key: &[u8],
1186) -> Result<Vec<u8>, KeyResolutionError> {
1187    match curve_oid {
1188        EC_P256_OID => p256::PublicKey::from_sec1_bytes(public_key)
1189            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1190            .to_public_key_der()
1191            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1192            .map(|der| der.as_bytes().to_vec()),
1193        EC_P384_OID => p384::PublicKey::from_sec1_bytes(public_key)
1194            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1195            .to_public_key_der()
1196            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1197            .map(|der| der.as_bytes().to_vec()),
1198        EC_P521_OID => p521::PublicKey::from_sec1_bytes(public_key)
1199            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1200            .to_public_key_der()
1201            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1202            .map(|der| der.as_bytes().to_vec()),
1203        _ => Err(KeyResolutionError::InvalidPublicKey),
1204    }
1205}
1206
1207fn key_value_error_allows_fallback(key_value: &KeyValueInfo, error: &KeyResolutionError) -> bool {
1208    matches!(
1209        key_value,
1210        KeyValueInfo::Dsa { .. } | KeyValueInfo::Ec { .. } | KeyValueInfo::InvalidEcKeyValue
1211    ) && matches!(
1212        error,
1213        KeyResolutionError::InvalidPublicKey | KeyResolutionError::AlgorithmMismatch
1214    )
1215}
1216
1217fn validate_spki_algorithm(
1218    public_key_bytes: &[u8],
1219    algorithm: SignatureAlgorithm,
1220) -> Result<(), KeyResolutionError> {
1221    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_bytes)
1222        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1223    if !rest.is_empty() {
1224        return Err(KeyResolutionError::InvalidPublicKey);
1225    }
1226    let parsed = spki
1227        .parsed()
1228        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1229    let curve_oid = spki
1230        .algorithm
1231        .parameters
1232        .as_ref()
1233        .and_then(|value| value.as_oid().ok())
1234        .map(|oid| oid.to_id_string());
1235    match (algorithm, parsed) {
1236        (SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256, PublicKey::DSA(_)) => {
1237            let _ = dsa::VerifyingKey::from_public_key_der(public_key_bytes)
1238                .map_err(|_| KeyResolutionError::AlgorithmMismatch)?;
1239            Ok(())
1240        }
1241        (
1242            SignatureAlgorithm::RsaSha1
1243            | SignatureAlgorithm::RsaSha224
1244            | SignatureAlgorithm::RsaSha256
1245            | SignatureAlgorithm::RsaSha384
1246            | SignatureAlgorithm::RsaSha512,
1247            PublicKey::RSA(_),
1248        ) => Ok(()),
1249        (
1250            SignatureAlgorithm::EcdsaSha1
1251            | SignatureAlgorithm::EcdsaSha224
1252            | SignatureAlgorithm::EcdsaSha256
1253            | SignatureAlgorithm::EcdsaSha384
1254            | SignatureAlgorithm::EcdsaSha512,
1255            PublicKey::EC(_),
1256        ) if matches!(
1257            curve_oid.as_deref(),
1258            Some(EC_P256_OID | EC_P384_OID | EC_P521_OID)
1259        ) =>
1260        {
1261            Ok(())
1262        }
1263        _ => Err(KeyResolutionError::AlgorithmMismatch),
1264    }
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269    use crate::xml::dom as roxmltree;
1270    use std::sync::atomic::{AtomicUsize, Ordering};
1271
1272    use base64::{Engine, engine::general_purpose::STANDARD};
1273    use rsa::{pkcs8::DecodePublicKey, traits::PublicKeyParts};
1274
1275    use super::*;
1276
1277    struct RejectSecondSha512Provider {
1278        sha512_calls: AtomicUsize,
1279        verification_calls: AtomicUsize,
1280        reject_verification_call: Option<usize>,
1281        rejected_verification_data: Option<Vec<u8>>,
1282    }
1283
1284    impl crate::provider::CryptoProvider for RejectSecondSha512Provider {
1285        fn name(&self) -> &'static str {
1286            "reject-second-sha512"
1287        }
1288
1289        fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
1290            crate::provider::default_provider().supports(capability)
1291        }
1292
1293        fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
1294            crate::provider::default_provider().fill_random(output)
1295        }
1296
1297        fn derive_key(
1298            &self,
1299            parameters: &crate::provider::KdfParameters<'_>,
1300            secret: &[u8],
1301        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1302            crate::provider::default_provider().derive_key(parameters, secret)
1303        }
1304
1305        fn digest(
1306            &self,
1307            algorithm: super::super::DigestAlgorithm,
1308            data: &[u8],
1309        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1310            if algorithm == super::super::DigestAlgorithm::Sha512
1311                && self.sha512_calls.fetch_add(1, Ordering::Relaxed) > 0
1312            {
1313                return Err(crate::provider::ProviderError::Unsupported {
1314                    operation: crate::provider::ProviderOperation::Digest,
1315                    algorithm: Some(algorithm.uri().to_owned()),
1316                });
1317            }
1318            crate::provider::default_provider().digest(algorithm, data)
1319        }
1320
1321        fn sign(
1322            &self,
1323            key: &dyn super::super::SigningKey,
1324            algorithm: SignatureAlgorithm,
1325            data: &[u8],
1326        ) -> Result<Vec<u8>, super::super::SigningKeyError> {
1327            crate::provider::default_provider().sign(key, algorithm, data)
1328        }
1329
1330        fn verify(
1331            &self,
1332            key: &dyn VerifyingKey,
1333            algorithm: SignatureAlgorithm,
1334            data: &[u8],
1335            signature: &[u8],
1336        ) -> Result<bool, DsigError> {
1337            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
1338            if self.reject_verification_call == Some(call)
1339                || self
1340                    .rejected_verification_data
1341                    .as_deref()
1342                    .is_some_and(|rejected| rejected == data)
1343            {
1344                return Err(crate::provider::ProviderError::Unsupported {
1345                    operation: crate::provider::ProviderOperation::Verify,
1346                    algorithm: Some(algorithm.uri().to_owned()),
1347                }
1348                .into());
1349            }
1350            crate::provider::default_provider().verify(key, algorithm, data, signature)
1351        }
1352
1353        fn verify_x509_signature(
1354            &self,
1355            algorithm: crate::provider::X509SignatureAlgorithm,
1356            data: &[u8],
1357            signature: &[u8],
1358            issuer_spki_der: &[u8],
1359        ) -> Result<bool, crate::provider::ProviderError> {
1360            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
1361            if self.reject_verification_call == Some(call)
1362                || self
1363                    .rejected_verification_data
1364                    .as_deref()
1365                    .is_some_and(|rejected| rejected == data)
1366            {
1367                return Err(crate::provider::ProviderError::Unsupported {
1368                    operation: crate::provider::ProviderOperation::VerifyCertificate,
1369                    algorithm: Some(algorithm.oid().to_owned()),
1370                });
1371            }
1372            crate::provider::default_provider().verify_x509_signature(
1373                algorithm,
1374                data,
1375                signature,
1376                issuer_spki_der,
1377            )
1378        }
1379
1380        #[cfg(feature = "xmlenc")]
1381        fn encrypt_data(
1382            &self,
1383            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
1384            key: &[u8],
1385            plaintext: &[u8],
1386        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1387            crate::provider::default_provider().encrypt_data(algorithm, key, plaintext)
1388        }
1389
1390        #[cfg(feature = "xmlenc")]
1391        fn decrypt_data(
1392            &self,
1393            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
1394            key: &[u8],
1395            ciphertext: &[u8],
1396        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1397            crate::provider::default_provider().decrypt_data(algorithm, key, ciphertext)
1398        }
1399
1400        #[cfg(feature = "xmlenc")]
1401        fn wrap_key(
1402            &self,
1403            algorithm: crate::xmlenc::KeyWrapAlgorithm,
1404            kek: &[u8],
1405            key: &[u8],
1406        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1407            crate::provider::default_provider().wrap_key(algorithm, kek, key)
1408        }
1409
1410        #[cfg(feature = "xmlenc")]
1411        fn unwrap_key(
1412            &self,
1413            algorithm: crate::xmlenc::KeyWrapAlgorithm,
1414            kek: &[u8],
1415            wrapped: &[u8],
1416        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1417            crate::provider::default_provider().unwrap_key(algorithm, kek, wrapped)
1418        }
1419
1420        #[cfg(feature = "xmlenc")]
1421        fn transport_key(
1422            &self,
1423            key: &dyn crate::provider::KeyTransportKey,
1424            parameters: &crate::xmlenc::RsaOaepParameters,
1425            plaintext: &[u8],
1426        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1427            crate::provider::default_provider().transport_key(key, parameters, plaintext)
1428        }
1429
1430        #[cfg(feature = "xmlenc")]
1431        fn recover_key(
1432            &self,
1433            key: &dyn crate::provider::KeyRecoveryKey,
1434            parameters: &crate::xmlenc::RsaOaepParameters,
1435            ciphertext: &[u8],
1436        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1437            crate::provider::default_provider().recover_key(key, parameters, ciphertext)
1438        }
1439    }
1440
1441    fn chain_policy() -> crate::policy::KeyTrustPolicy {
1442        crate::policy::KeyTrustPolicy {
1443            verify_x509_chains: true,
1444            ..crate::policy::KeyTrustPolicy::default()
1445        }
1446    }
1447
1448    fn chain_policy_at(verification_time: SystemTime) -> crate::policy::KeyTrustPolicy {
1449        crate::policy::KeyTrustPolicy {
1450            verification_time: Some(verification_time),
1451            ..chain_policy()
1452        }
1453    }
1454
1455    fn verification_policy_with_trust(
1456        key_trust: crate::policy::KeyTrustPolicy,
1457    ) -> crate::policy::VerificationPolicy {
1458        crate::policy::VerificationPolicy {
1459            key_trust,
1460            ..crate::policy::VerificationPolicy::default()
1461        }
1462    }
1463
1464    const SIGNED_SAML: &str =
1465        include_str!("../../tests/fixtures/saml/response_signed_by_idp_ecdsa.xml");
1466    const SAML_PUBLIC_KEY: &str =
1467        include_str!("../../tests/fixtures/keys/ec/saml-idp-ecdsa-pubkey.pem");
1468    const RSA_PUBLIC_KEY: &str = include_str!("../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem");
1469    const RSA_4096_CERTIFICATE: &str =
1470        include_str!("../../tests/fixtures/keys/rsa/rsa-4096-cert.pem");
1471    const X509_DIGEST_SIGNATURE: &str = include_str!(
1472        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha512.xml"
1473    );
1474    const X509_DIGEST_SHA256_SIGNATURE: &str = include_str!(
1475        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha256.xml"
1476    );
1477    const RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
1478        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloping-sha256-rsa-sha256.xml"
1479    );
1480    const LEGACY_RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
1481        "../../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/signature-enveloping-rsa.xml"
1482    );
1483    const EC_P256_KEY_VALUE_SIGNATURE: &str = include_str!(
1484        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p256_sha256.xml"
1485    );
1486    const EC_P384_KEY_VALUE_SIGNATURE: &str = include_str!(
1487        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p384_sha384.xml"
1488    );
1489
1490    fn replace_key_info(xml: &str, replacement: &str) -> String {
1491        let start = xml.find("<ds:KeyInfo>").expect("fixture has KeyInfo");
1492        let end = xml
1493            .find("</ds:KeyInfo>")
1494            .expect("fixture has closing KeyInfo")
1495            + "</ds:KeyInfo>".len();
1496        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
1497    }
1498
1499    fn replace_unprefixed_key_info(xml: &str, replacement: &str) -> String {
1500        let start = xml.find("<KeyInfo>").expect("fixture has KeyInfo");
1501        let end = xml.find("</KeyInfo>").expect("fixture has closing KeyInfo") + "</KeyInfo>".len();
1502        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
1503    }
1504
1505    fn rsa_key_value_parts(public_key: &rsa::RsaPublicKey) -> (String, String) {
1506        (
1507            STANDARD.encode(public_key.n().to_be_bytes_trimmed_vartime()),
1508            STANDARD.encode(public_key.e().to_be_bytes_trimmed_vartime()),
1509        )
1510    }
1511
1512    fn x509_signature_with_leaf_subject() -> String {
1513        replace_unprefixed_key_info(
1514            X509_DIGEST_SIGNATURE,
1515            "<KeyInfo><X509Data><X509SubjectName>CN=Test Key rsa-4096,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName></X509Data></KeyInfo>",
1516        )
1517    }
1518
1519    fn fixture_certificate_time() -> SystemTime {
1520        // 2027-01-15 UTC, inside the donor certificates' 2026-2126 validity window.
1521        SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_800_000_000)
1522    }
1523
1524    fn public_key_der(pem_text: &str) -> Vec<u8> {
1525        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
1526            .expect("fixture public key is PEM");
1527        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1528        assert_eq!(pem.label, "PUBLIC KEY");
1529        pem.contents
1530    }
1531
1532    fn certificate_der(pem_text: &str) -> Vec<u8> {
1533        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
1534            .expect("fixture certificate is PEM");
1535        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1536        assert_eq!(pem.label, "CERTIFICATE");
1537        pem.contents
1538    }
1539
1540    fn crl_der(pem_text: &str) -> Vec<u8> {
1541        let (rest, pem) =
1542            x509_parser::pem::parse_x509_pem(pem_text.as_bytes()).expect("fixture CRL is PEM");
1543        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1544        assert_eq!(pem.label, "X509 CRL");
1545        pem.contents
1546    }
1547
1548    fn generated_certificate_params(common_name: &str, is_ca: bool) -> rcgen::CertificateParams {
1549        let mut params = rcgen::CertificateParams::new(Vec::new())
1550            .expect("empty SAN list should produce valid certificate parameters");
1551        params
1552            .distinguished_name
1553            .push(rcgen::DnType::CommonName, common_name);
1554        if is_ca {
1555            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1556            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
1557        }
1558        params
1559    }
1560
1561    fn x509_info(certificates: Vec<Vec<u8>>, signing_index: usize) -> X509DataInfo {
1562        let parsed_certificates = certificates
1563            .iter()
1564            .map(|certificate| {
1565                parse_x509_certificate(certificate)
1566                    .expect("generated certificate should have supported metadata")
1567            })
1568            .collect();
1569        X509DataInfo {
1570            certificates,
1571            parsed_certificates,
1572            certificate_chain: vec![signing_index],
1573            ..X509DataInfo::default()
1574        }
1575    }
1576
1577    #[test]
1578    fn defaults_match_key_resolution_policy() {
1579        // Defaults must remain compatible with xmlsec1's depth and opt-in trust policy.
1580        let config = KeyResolverConfig::default();
1581
1582        assert!(config.trusted_certs.is_empty());
1583        assert!(config.lookup_certs.is_empty());
1584        assert!(config.named_keys.is_empty());
1585        let trust = crate::policy::VerificationPolicy::default().key_trust;
1586        assert!(!trust.verify_x509_chains);
1587        assert!(!trust.check_crls);
1588        assert_eq!(trust.verification_time, None);
1589        assert_eq!(trust.max_x509_chain_depth, 9);
1590    }
1591
1592    #[test]
1593    fn verification_policy_controls_leaf_extended_key_usage() {
1594        // The immutable operation snapshot must reach certificate-path
1595        // validation; resolver-local trust defaults cannot bypass EKU policy.
1596        let root = rcgen::CertifiedIssuer::self_signed(
1597            generated_certificate_params("EKU policy root", true),
1598            rcgen::KeyPair::generate().expect("root key generation should succeed"),
1599        )
1600        .expect("root should be self-signable");
1601        let mut leaf_params = generated_certificate_params("TLS-only XML signer", false);
1602        leaf_params.key_usages = vec![rcgen::KeyUsagePurpose::DigitalSignature];
1603        leaf_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
1604        let leaf = leaf_params
1605            .signed_by(
1606                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
1607                &root,
1608            )
1609            .expect("root should sign leaf certificate");
1610        let key_info = KeyInfo {
1611            sources: vec![KeyInfoSource::X509Data(x509_info(
1612                vec![leaf.der().to_vec(), root.der().to_vec()],
1613                0,
1614            ))],
1615        };
1616        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1617            trusted_certs: vec![root.der().to_vec()],
1618            ..KeyResolverConfig::default()
1619        });
1620        let mut policy = crate::policy::VerificationPolicy::default();
1621        policy.key_trust.verify_x509_chains = true;
1622
1623        let error = match resolver.resolve_with_policy(
1624            Some(&key_info),
1625            SignatureAlgorithm::EcdsaSha256,
1626            &policy,
1627        ) {
1628            Ok(_) => panic!("unapproved restricted EKU must be rejected"),
1629            Err(error) => error,
1630        };
1631        assert!(matches!(
1632            error,
1633            DsigError::KeyResolution(KeyResolutionError::Chain(
1634                super::super::X509ChainError::InvalidKeyUsage {
1635                    position: 0,
1636                    required: "an approved extended key usage",
1637                }
1638            ))
1639        ));
1640
1641        policy.key_trust.allowed_extended_key_usages =
1642            std::collections::HashSet::from([crate::policy::ExtendedKeyPurpose::ServerAuth]);
1643        assert!(
1644            resolver
1645                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
1646                .expect("approved restricted EKU must pass path validation")
1647                .is_some()
1648        );
1649    }
1650
1651    #[test]
1652    fn operation_policy_rejects_zero_x509_resource_limits() {
1653        // X.509 work limits belong to the immutable operation snapshot and are
1654        // rejected before resolver-owned certificate material is inspected.
1655        for trust in [
1656            crate::policy::KeyTrustPolicy {
1657                verify_x509_chains: true,
1658                max_x509_chain_depth: 0,
1659                ..crate::policy::KeyTrustPolicy::default()
1660            },
1661            crate::policy::KeyTrustPolicy {
1662                verify_x509_chains: true,
1663                max_x509_candidate_paths: 0,
1664                ..crate::policy::KeyTrustPolicy::default()
1665            },
1666        ] {
1667            let certificate = certificate_der(RSA_4096_CERTIFICATE);
1668            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1669                trusted_certs: vec![certificate],
1670                ..KeyResolverConfig::default()
1671            });
1672            let policy = crate::policy::VerificationPolicy {
1673                key_trust: trust,
1674                ..crate::policy::VerificationPolicy::default()
1675            };
1676            let error = super::super::VerifyContext::new()
1677                .policy(policy)
1678                .key_resolver(&resolver)
1679                .verify(&x509_signature_with_leaf_subject())
1680                .expect_err("zero composed X.509 limits must fail as policy errors");
1681
1682            assert!(matches!(
1683                error,
1684                DsigError::Policy(crate::policy::PolicyViolation::InvalidResourceLimit {
1685                    requirement: "limit must be nonzero",
1686                    actual: 0,
1687                    ..
1688                })
1689            ));
1690        }
1691    }
1692
1693    #[test]
1694    fn operation_policy_rejects_crl_checking_without_chain_validation() {
1695        // CRL authentication is part of path validation. A resolver must not
1696        // accept a configuration that would silently skip the requested check.
1697        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1698            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
1699            ..KeyResolverConfig::default()
1700        });
1701        let policy = crate::policy::VerificationPolicy {
1702            key_trust: crate::policy::KeyTrustPolicy {
1703                check_crls: true,
1704                ..crate::policy::KeyTrustPolicy::default()
1705            },
1706            ..crate::policy::VerificationPolicy::default()
1707        };
1708        let error = super::super::VerifyContext::new()
1709            .policy(policy)
1710            .key_resolver(&resolver)
1711            .verify(&x509_signature_with_leaf_subject())
1712            .expect_err("CRL-only trust policy must fail before certificate use");
1713
1714        assert!(matches!(
1715            error,
1716            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
1717                reason: "CRL checking requires X.509 chain validation"
1718            })
1719        ));
1720    }
1721
1722    #[test]
1723    fn hmac_key_rejects_empty_secret_and_wrong_algorithm() {
1724        // HMAC secrets are caller-owned and cannot be reused as asymmetric keys.
1725        assert!(matches!(
1726            HmacSha1VerificationKey::new(Vec::new()),
1727            Err(KeyResolutionError::InvalidPublicKey)
1728        ));
1729        let key = HmacSha1VerificationKey::new(b"secret".to_vec())
1730            .expect("non-empty HMAC secret must be accepted");
1731        assert!(matches!(
1732            key.verify(SignatureAlgorithm::RsaSha256, b"data", b"signature"),
1733            Err(DsigError::KeyResolution(
1734                KeyResolutionError::AlgorithmMismatch
1735            ))
1736        ));
1737    }
1738
1739    #[test]
1740    fn hmac_key_uses_the_operation_policy_for_truncation() {
1741        // Output length belongs to SignatureMethod and operation policy, not
1742        // reusable secret key material. Legacy truncation therefore requires
1743        // an explicit compatibility policy even on the policy-aware key hook.
1744        let key = HmacVerificationKey::new(b"secret".to_vec())
1745            .expect("the fixture HMAC secret is non-empty");
1746        let mut mac = hmac::Hmac::<sha1::Sha1>::new_from_slice(b"secret")
1747            .expect("HMAC accepts an arbitrary non-empty secret");
1748        mac.update(b"data");
1749        let expected = mac.finalize().into_bytes();
1750
1751        let policy = crate::policy::VerificationPolicy {
1752            hmac: crate::policy::HmacPolicy {
1753                minimum_key_bits: 40,
1754                minimum_output_bits: 80,
1755            },
1756            ..crate::policy::VerificationPolicy::default()
1757        };
1758        assert!(
1759            key.verify_with_policy(
1760                &policy,
1761                SignatureAlgorithm::HmacSha1,
1762                b"data",
1763                &expected[..10],
1764            )
1765            .expect("the compatibility policy and algorithm match")
1766        );
1767        assert!(
1768            !key.verify_with_policy(&policy, SignatureAlgorithm::HmacSha1, b"data", &[0_u8; 10],)
1769                .expect("a mismatched truncated MAC must be rejected")
1770        );
1771    }
1772
1773    #[test]
1774    fn hmac_key_direct_api_rejects_attacker_selected_short_output() {
1775        // The policy-free trait method applies secure defaults; signature bytes
1776        // cannot act as their own one-byte truncation declaration.
1777        let key = HmacVerificationKey::new([0x42; 16]).expect("fixed HMAC key must parse");
1778        let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(&[0x42; 16])
1779            .expect("HMAC accepts the fixed secret");
1780        mac.update(b"data");
1781        let expected = mac.finalize().into_bytes();
1782
1783        assert!(matches!(
1784            key.verify(SignatureAlgorithm::HmacSha256, b"data", &expected[..1]),
1785            Err(DsigError::Policy(
1786                crate::policy::PolicyViolation::HmacOutputLength {
1787                    minimum: 128,
1788                    maximum: 256,
1789                    actual: 8,
1790                }
1791            ))
1792        ));
1793    }
1794
1795    #[test]
1796    fn hmac_key_debug_redacts_secret_material() {
1797        // Debug output may expose public verification parameters, never caller secrets.
1798        let secret = b"unique-debug-secret-marker";
1799        let key = HmacVerificationKey::new(secret.to_vec())
1800            .expect("the fixture HMAC secret is non-empty");
1801
1802        let debug = format!("{key:?}");
1803        assert!(
1804            !debug
1805                .contains(std::str::from_utf8(secret).expect("the debug marker is literal ASCII"))
1806        );
1807        assert!(!debug.contains(&format!("{secret:?}")));
1808        assert!(!debug.contains("output_length_bits"));
1809    }
1810
1811    #[test]
1812    fn stores_named_verification_key_metadata() {
1813        // Named resolution must retain every field needed by the later resolver wiring.
1814        let key = VerificationKey {
1815            algorithm: SignatureAlgorithm::RsaSha256,
1816            public_key_bytes: vec![1, 2, 3],
1817            certificate_der: Some(vec![4, 5, 6]),
1818            name: Some("idp-signing".into()),
1819        };
1820        let mut config = KeyResolverConfig::default();
1821        config.named_keys.insert("idp-signing".into(), key.clone());
1822
1823        assert_eq!(config.named_keys.get("idp-signing"), Some(&key));
1824    }
1825
1826    #[test]
1827    fn resolves_embedded_certificate_end_to_end() {
1828        // The default resolver must make parsed X509Data usable by VerifyContext.
1829        let resolver = DefaultKeyResolver::default();
1830        let result = super::super::VerifyContext::new()
1831            .key_resolver(&resolver)
1832            .verify(SIGNED_SAML)
1833            .expect("embedded certificate should resolve");
1834
1835        assert_eq!(result.status, super::super::DsigStatus::Valid);
1836    }
1837
1838    #[test]
1839    fn resolves_x509_digest_from_configured_certificates() {
1840        // Selector-only X509Data must locate the signing certificate without
1841        // embedding key material or supplying a preset verification key.
1842        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
1843        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1844            lookup_certs: vec![leaf_certificate_der],
1845            trusted_certs: vec![
1846                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
1847                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
1848            ],
1849            ..KeyResolverConfig::default()
1850        });
1851        for signature in [X509_DIGEST_SHA256_SIGNATURE, X509_DIGEST_SIGNATURE] {
1852            let result = super::super::VerifyContext::new()
1853                .key_resolver(&resolver)
1854                .verify(signature)
1855                .expect("X509Digest should resolve a configured certificate");
1856
1857            assert_eq!(result.status, super::super::DsigStatus::Valid);
1858        }
1859    }
1860
1861    #[test]
1862    fn selector_resolved_certificate_obeys_chain_policy() {
1863        // Enabling chain verification must apply validity policy even when
1864        // X509Data contains only selectors and the matching cert is configured.
1865        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
1866        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1867            lookup_certs: vec![leaf_certificate_der],
1868            trusted_certs: vec![
1869                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
1870                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
1871            ],
1872            ..KeyResolverConfig::default()
1873        });
1874        let error = super::super::VerifyContext::new()
1875            .policy(verification_policy_with_trust(chain_policy_at(
1876                SystemTime::UNIX_EPOCH,
1877            )))
1878            .key_resolver(&resolver)
1879            .verify(&x509_signature_with_leaf_subject())
1880            .expect_err("selector-resolved certificate must satisfy chain policy");
1881
1882        assert!(
1883            matches!(
1884                &error,
1885                DsigError::KeyResolution(KeyResolutionError::Chain(
1886                    super::super::X509ChainError::CertificateNotValid(_)
1887                ))
1888            ),
1889            "unexpected selector policy error: {error:?}"
1890        );
1891    }
1892
1893    #[test]
1894    fn selector_resolved_configured_root_remains_a_trust_anchor() {
1895        // A certificate explicitly configured in trusted_certs remains an
1896        // anchor when X509Data selects it by subject instead of embedding it.
1897        let mut params = rcgen::CertificateParams::new(Vec::new())
1898            .expect("empty SAN list should produce valid certificate parameters");
1899        params
1900            .distinguished_name
1901            .push(rcgen::DnType::CommonName, "configured root");
1902        params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1903        let key_pair = rcgen::KeyPair::generate().expect("test key generation should succeed");
1904        let certificate = params
1905            .self_signed(&key_pair)
1906            .expect("test root should be self-signable");
1907        let certificate_der = certificate.der().to_vec();
1908        let key_info_xml = concat!(
1909            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
1910            "<X509Data><X509SubjectName>CN=configured root</X509SubjectName></X509Data>",
1911            "</KeyInfo>"
1912        );
1913        let document = roxmltree::Document::parse(key_info_xml)
1914            .expect("static selector KeyInfo should parse as XML");
1915        let key_info = super::super::parse_key_info(document.root_element())
1916            .expect("static selector KeyInfo should satisfy XMLDSig structure");
1917        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1918            trusted_certs: vec![certificate_der],
1919            ..KeyResolverConfig::default()
1920        });
1921
1922        let resolved = resolver
1923            .resolve_with_policy(
1924                Some(&key_info),
1925                SignatureAlgorithm::EcdsaSha256,
1926                &verification_policy_with_trust(chain_policy()),
1927            )
1928            .expect("configured self-signed certificate should validate as its own anchor");
1929
1930        assert!(resolved.is_some());
1931    }
1932
1933    #[test]
1934    fn selector_resolved_non_self_signed_trust_anchor_terminates_the_path() {
1935        // Trust is assigned to the exact configured certificate, not inferred
1936        // from self-signing. A lookup-only issuer must not extend that anchor
1937        // into a new path that requires another trust decision.
1938        let mut issuer_params = rcgen::CertificateParams::new(Vec::new())
1939            .expect("empty issuer SAN list should be valid");
1940        issuer_params
1941            .distinguished_name
1942            .push(rcgen::DnType::CommonName, "lookup-only issuer");
1943        issuer_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1944        issuer_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
1945        let issuer = rcgen::CertifiedIssuer::self_signed(
1946            issuer_params,
1947            rcgen::KeyPair::generate().expect("issuer key generation should succeed"),
1948        )
1949        .expect("issuer certificate should be self-signable");
1950
1951        let mut anchor_params = rcgen::CertificateParams::new(Vec::new())
1952            .expect("empty anchor SAN list should be valid");
1953        anchor_params
1954            .distinguished_name
1955            .push(rcgen::DnType::CommonName, "direct trust anchor");
1956        let anchor = anchor_params
1957            .signed_by(
1958                &rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
1959                &issuer,
1960            )
1961            .expect("issuer should sign the directly trusted certificate");
1962        let key_info_xml = concat!(
1963            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
1964            "<X509Data><X509SubjectName>CN=direct trust anchor</X509SubjectName></X509Data>",
1965            "</KeyInfo>"
1966        );
1967        let document = roxmltree::Document::parse(key_info_xml)
1968            .expect("static selector KeyInfo should parse as XML");
1969        let key_info = super::super::parse_key_info(document.root_element())
1970            .expect("static selector KeyInfo should satisfy XMLDSig structure");
1971        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1972            trusted_certs: vec![anchor.der().to_vec()],
1973            lookup_certs: vec![issuer.der().to_vec()],
1974            ..KeyResolverConfig::default()
1975        });
1976
1977        let resolved = resolver
1978            .resolve_with_policy(
1979                Some(&key_info),
1980                SignatureAlgorithm::EcdsaSha256,
1981                &verification_policy_with_trust(chain_policy()),
1982            )
1983            .expect("an explicitly trusted selected certificate must terminate its path");
1984
1985        assert!(resolved.is_some());
1986    }
1987
1988    #[test]
1989    fn selector_resolved_leaf_stops_at_non_self_signed_trust_anchor() {
1990        // A configured anchor terminates trust even when a lookup certificate
1991        // could continue the issuer-name chain beyond it.
1992        let external_issuer = rcgen::CertifiedIssuer::self_signed(
1993            generated_certificate_params("external issuer", true),
1994            rcgen::KeyPair::generate().expect("external issuer key generation should succeed"),
1995        )
1996        .expect("external issuer should be self-signable");
1997        let anchor = rcgen::CertifiedIssuer::signed_by(
1998            generated_certificate_params("non-self-signed anchor", true),
1999            rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
2000            &external_issuer,
2001        )
2002        .expect("external issuer should sign the anchor");
2003        let leaf = generated_certificate_params("anchor leaf", false)
2004            .signed_by(
2005                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2006                &anchor,
2007            )
2008            .expect("anchor should sign the leaf");
2009        let leaf_metadata = parse_x509_certificate(leaf.der())
2010            .expect("generated leaf should have supported metadata");
2011        let key_info = KeyInfo {
2012            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2013                subject_names: vec![leaf_metadata.subject_dn],
2014                ..X509DataInfo::default()
2015            })],
2016        };
2017        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2018            trusted_certs: vec![anchor.der().to_vec()],
2019            lookup_certs: vec![leaf.der().to_vec(), external_issuer.der().to_vec()],
2020            ..KeyResolverConfig::default()
2021        });
2022
2023        let resolved = resolver
2024            .resolve_with_policy(
2025                Some(&key_info),
2026                SignatureAlgorithm::EcdsaSha256,
2027                &verification_policy_with_trust(chain_policy()),
2028            )
2029            .expect("path construction must stop at the configured anchor");
2030
2031        assert!(resolved.is_some());
2032    }
2033
2034    #[test]
2035    fn selector_resolved_leaf_does_not_anchor_itself() {
2036        // A certificate available for selector lookup is not automatically a
2037        // trust anchor; chain verification still requires a separate issuer.
2038        let certificate_der = certificate_der(RSA_4096_CERTIFICATE);
2039        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2040            lookup_certs: vec![certificate_der],
2041            ..KeyResolverConfig::default()
2042        });
2043        let error = super::super::VerifyContext::new()
2044            .policy(verification_policy_with_trust(chain_policy_at(
2045                fixture_certificate_time(),
2046            )))
2047            .key_resolver(&resolver)
2048            .verify(&x509_signature_with_leaf_subject())
2049            .expect_err("selector-resolved leaf must not trust itself");
2050
2051        assert!(matches!(
2052            error,
2053            DsigError::KeyResolution(KeyResolutionError::Chain(
2054                super::super::X509ChainError::UntrustedRoot
2055            ))
2056        ));
2057    }
2058
2059    #[test]
2060    fn selector_resolved_leaf_uses_separate_anchor() {
2061        // Selector lookup may use the leaf from the configured set, but chain
2062        // verification must terminate at a different configured certificate.
2063        let leaf = certificate_der(RSA_4096_CERTIFICATE);
2064        let issuer = certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
2065        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2066            lookup_certs: vec![leaf],
2067            trusted_certs: vec![issuer],
2068            ..KeyResolverConfig::default()
2069        });
2070        let result = super::super::VerifyContext::new()
2071            .policy(verification_policy_with_trust(chain_policy_at(
2072                fixture_certificate_time(),
2073            )))
2074            .key_resolver(&resolver)
2075            .verify(&x509_signature_with_leaf_subject())
2076            .expect("selector-resolved leaf should chain to its configured issuer");
2077
2078        assert_eq!(result.status, super::super::DsigStatus::Valid);
2079    }
2080
2081    #[test]
2082    fn selector_resolved_leaf_uses_lookup_intermediate() {
2083        // Lookup certificates may complete an untrusted path, but only the
2084        // separately configured root is allowed to establish trust.
2085        let mut root_params =
2086            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
2087        root_params
2088            .distinguished_name
2089            .push(rcgen::DnType::CommonName, "lookup root");
2090        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2091        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2092        let root = rcgen::CertifiedIssuer::self_signed(
2093            root_params,
2094            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2095        )
2096        .expect("root certificate should be self-signable");
2097
2098        let mut intermediate_params = rcgen::CertificateParams::new(Vec::new())
2099            .expect("empty intermediate SAN list should be valid");
2100        intermediate_params
2101            .distinguished_name
2102            .push(rcgen::DnType::CommonName, "lookup intermediate");
2103        intermediate_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2104        intermediate_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2105        let intermediate = rcgen::CertifiedIssuer::signed_by(
2106            intermediate_params,
2107            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2108            &root,
2109        )
2110        .expect("root should sign the intermediate certificate");
2111
2112        let mut leaf_params =
2113            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
2114        leaf_params
2115            .distinguished_name
2116            .push(rcgen::DnType::CommonName, "lookup leaf");
2117        let leaf = leaf_params
2118            .signed_by(
2119                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2120                &intermediate,
2121            )
2122            .expect("intermediate should sign the leaf certificate");
2123        let key_info_xml = concat!(
2124            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2125            "<X509Data><X509SubjectName>CN=lookup leaf</X509SubjectName></X509Data>",
2126            "</KeyInfo>"
2127        );
2128        let document = roxmltree::Document::parse(key_info_xml)
2129            .expect("static selector KeyInfo should parse as XML");
2130        let key_info = super::super::parse_key_info(document.root_element())
2131            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2132        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2133            lookup_certs: vec![leaf.der().to_vec(), intermediate.der().to_vec()],
2134            trusted_certs: vec![root.der().to_vec()],
2135            ..KeyResolverConfig::default()
2136        });
2137
2138        let resolved = resolver
2139            .resolve_with_policy(
2140                Some(&key_info),
2141                SignatureAlgorithm::EcdsaSha256,
2142                &verification_policy_with_trust(chain_policy()),
2143            )
2144            .expect("selector-resolved leaf should chain through the lookup intermediate");
2145
2146        assert!(resolved.is_some());
2147    }
2148
2149    #[test]
2150    fn x509_path_signatures_use_the_operation_provider() {
2151        // Embedded and selector-resolved certificates converge on the same
2152        // path validator. Neither source may fall back to a crate-global
2153        // verifier when the operation provider rejects certificate signatures.
2154        let root = rcgen::CertifiedIssuer::self_signed(
2155            generated_certificate_params("provider root", true),
2156            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2157        )
2158        .expect("root should be self-signable");
2159        let leaf = generated_certificate_params("provider leaf", false)
2160            .signed_by(
2161                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2162                &root,
2163            )
2164            .expect("root should sign the leaf");
2165        let leaf_der = leaf.der().to_vec();
2166        let leaf_metadata =
2167            parse_x509_certificate(&leaf_der).expect("generated leaf metadata should parse");
2168        let policy = crate::policy::VerificationPolicy {
2169            key_trust: chain_policy(),
2170            ..crate::policy::VerificationPolicy::default()
2171        };
2172
2173        let cases = [
2174            (
2175                KeyInfo {
2176                    sources: vec![KeyInfoSource::X509Data(x509_info(
2177                        vec![leaf_der.clone()],
2178                        0,
2179                    ))],
2180                },
2181                Vec::new(),
2182            ),
2183            (
2184                KeyInfo {
2185                    sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2186                        subject_names: vec![leaf_metadata.subject_dn],
2187                        ..X509DataInfo::default()
2188                    })],
2189                },
2190                vec![leaf_der],
2191            ),
2192        ];
2193
2194        for (key_info, lookup_certs) in cases {
2195            let provider = RejectSecondSha512Provider {
2196                sha512_calls: AtomicUsize::new(0),
2197                verification_calls: AtomicUsize::new(0),
2198                reject_verification_call: Some(0),
2199                rejected_verification_data: None,
2200            };
2201            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2202                trusted_certs: vec![root.der().to_vec()],
2203                lookup_certs,
2204                ..KeyResolverConfig::default()
2205            });
2206            let error = match resolver.resolve_with_policy_and_provider(
2207                Some(&key_info),
2208                SignatureAlgorithm::EcdsaSha256,
2209                &policy,
2210                &provider,
2211            ) {
2212                Ok(_) => panic!("the operation provider must gate every X.509 path signature"),
2213                Err(error) => error,
2214            };
2215
2216            assert!(matches!(
2217                error,
2218                DsigError::KeyResolution(KeyResolutionError::Chain(
2219                    super::super::X509ChainError::UnsupportedSignatureAlgorithm { ref oid }
2220                )) if oid == "1.2.840.10045.4.3.2"
2221            ));
2222            assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 1);
2223        }
2224
2225        // A provider rejection after path construction proves complete-path
2226        // validation does not switch back to the crate-global provider.
2227        let provider = RejectSecondSha512Provider {
2228            sha512_calls: AtomicUsize::new(0),
2229            verification_calls: AtomicUsize::new(0),
2230            reject_verification_call: Some(1),
2231            rejected_verification_data: None,
2232        };
2233        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2234            trusted_certs: vec![root.der().to_vec()],
2235            ..KeyResolverConfig::default()
2236        });
2237        let key_info = KeyInfo {
2238            sources: vec![KeyInfoSource::X509Data(x509_info(
2239                vec![leaf.der().to_vec()],
2240                0,
2241            ))],
2242        };
2243        let error = match resolver.resolve_with_policy_and_provider(
2244            Some(&key_info),
2245            SignatureAlgorithm::EcdsaSha256,
2246            &policy,
2247            &provider,
2248        ) {
2249            Ok(_) => panic!("complete-path validation must retain the operation provider"),
2250            Err(error) => error,
2251        };
2252        assert!(matches!(
2253            error,
2254            DsigError::KeyResolution(KeyResolutionError::Chain(
2255                super::super::X509ChainError::Provider(_)
2256            ))
2257        ));
2258        assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 2);
2259    }
2260
2261    #[test]
2262    fn embedded_leaf_uses_lookup_intermediate_with_duplicate_anchor() {
2263        // Deduplicating repeated trust anchors must not shift an untrusted
2264        // lookup intermediate into the trusted prefix used by path building.
2265        let trusted_root = rcgen::CertifiedIssuer::self_signed(
2266            generated_certificate_params("unrelated trusted root", true),
2267            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2268        )
2269        .expect("root should be self-signable");
2270        let issuer_root = rcgen::CertifiedIssuer::self_signed(
2271            generated_certificate_params("untrusted issuer root", true),
2272            rcgen::KeyPair::generate().expect("issuer root key generation should succeed"),
2273        )
2274        .expect("issuer root should be self-signable");
2275        let intermediate = rcgen::CertifiedIssuer::signed_by(
2276            generated_certificate_params("embedded intermediate", true),
2277            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2278            &issuer_root,
2279        )
2280        .expect("issuer root should sign the intermediate");
2281        let leaf = generated_certificate_params("embedded leaf", false)
2282            .signed_by(
2283                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2284                &intermediate,
2285            )
2286            .expect("intermediate should sign the leaf");
2287        let key_info = KeyInfo {
2288            sources: vec![KeyInfoSource::X509Data(x509_info(
2289                vec![leaf.der().to_vec()],
2290                0,
2291            ))],
2292        };
2293        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2294            lookup_certs: vec![intermediate.der().to_vec()],
2295            trusted_certs: vec![trusted_root.der().to_vec(), trusted_root.der().to_vec()],
2296            ..KeyResolverConfig::default()
2297        });
2298
2299        let policy = verification_policy_with_trust(chain_policy());
2300        let error = match resolver.resolve_with_policy(
2301            Some(&key_info),
2302            SignatureAlgorithm::EcdsaSha256,
2303            &policy,
2304        ) {
2305            Ok(_) => panic!("an untrusted lookup intermediate must not become a trust anchor"),
2306            Err(error) => error,
2307        };
2308
2309        assert!(matches!(
2310            error,
2311            DsigError::KeyResolution(KeyResolutionError::Chain(
2312                super::super::X509ChainError::UntrustedRoot
2313            ))
2314        ));
2315    }
2316
2317    #[test]
2318    fn selector_resolved_leaf_chooses_unique_valid_same_key_path() {
2319        // Cross-signing can produce issuer certificates with the same subject
2320        // and public key. Trust policy, not the immediate signature edge, must
2321        // select the sole path that reaches a configured anchor.
2322        let trusted_root = rcgen::CertifiedIssuer::self_signed(
2323            generated_certificate_params("trusted cross-sign root", true),
2324            rcgen::KeyPair::generate().expect("trusted root key generation should succeed"),
2325        )
2326        .expect("trusted root should be self-signable");
2327        let untrusted_root = rcgen::CertifiedIssuer::self_signed(
2328            generated_certificate_params("untrusted cross-sign root", true),
2329            rcgen::KeyPair::generate().expect("untrusted root key generation should succeed"),
2330        )
2331        .expect("untrusted root should be self-signable");
2332        let shared_params = generated_certificate_params("shared cross-sign issuer", true);
2333        let shared_key =
2334            rcgen::KeyPair::generate().expect("shared issuer key generation should succeed");
2335        let trusted_intermediate = shared_params
2336            .signed_by(&shared_key, &trusted_root)
2337            .expect("trusted root should cross-sign the shared issuer key");
2338        let untrusted_intermediate = shared_params
2339            .signed_by(&shared_key, &untrusted_root)
2340            .expect("untrusted root should cross-sign the shared issuer key");
2341        let shared_issuer = rcgen::Issuer::from_params(&shared_params, &shared_key);
2342        let leaf = generated_certificate_params("cross-signed leaf", false)
2343            .signed_by(
2344                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2345                &shared_issuer,
2346            )
2347            .expect("shared issuer key should sign the leaf");
2348        let leaf_metadata = parse_x509_certificate(leaf.der())
2349            .expect("generated leaf should have supported metadata");
2350        let key_info = KeyInfo {
2351            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2352                subject_names: vec![leaf_metadata.subject_dn],
2353                ..X509DataInfo::default()
2354            })],
2355        };
2356        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2357            trusted_certs: vec![trusted_root.der().to_vec()],
2358            lookup_certs: vec![
2359                leaf.der().to_vec(),
2360                untrusted_intermediate.der().to_vec(),
2361                trusted_intermediate.der().to_vec(),
2362                untrusted_root.der().to_vec(),
2363            ],
2364            ..KeyResolverConfig::default()
2365        });
2366
2367        let resolved = resolver
2368            .resolve_with_policy(
2369                Some(&key_info),
2370                SignatureAlgorithm::EcdsaSha256,
2371                &verification_policy_with_trust(chain_policy()),
2372            )
2373            .expect("the sole path to a configured anchor should be selected");
2374
2375        assert!(resolved.is_some());
2376    }
2377
2378    #[test]
2379    fn self_issued_rollover_continues_to_same_name_trusted_signer() {
2380        // Subject/issuer name equality does not prove self-signing: rollover
2381        // certificates may be issued by a distinct same-name trust anchor.
2382        let root = rcgen::CertifiedIssuer::self_signed(
2383            generated_certificate_params("rollover authority", true),
2384            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2385        )
2386        .expect("root should be self-signable");
2387        let rollover_params = generated_certificate_params("rollover authority", true);
2388        let rollover_key =
2389            rcgen::KeyPair::generate().expect("rollover key generation should succeed");
2390        let rollover_certificate = rollover_params
2391            .signed_by(&rollover_key, &root)
2392            .expect("root should sign the same-name rollover certificate");
2393        let rollover_issuer = rcgen::Issuer::from_params(&rollover_params, &rollover_key);
2394        let leaf = generated_certificate_params("rollover leaf", false)
2395            .signed_by(
2396                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2397                &rollover_issuer,
2398            )
2399            .expect("rollover key should sign the leaf");
2400        let leaf_metadata =
2401            parse_x509_certificate(leaf.der()).expect("generated leaf metadata should parse");
2402        let key_info = KeyInfo {
2403            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2404                subject_names: vec![leaf_metadata.subject_dn],
2405                ..X509DataInfo::default()
2406            })],
2407        };
2408        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2409            trusted_certs: vec![root.der().to_vec()],
2410            lookup_certs: vec![leaf.der().to_vec(), rollover_certificate.der().to_vec()],
2411            ..KeyResolverConfig::default()
2412        });
2413
2414        let resolved = resolver
2415            .resolve_with_policy(
2416                Some(&key_info),
2417                SignatureAlgorithm::EcdsaSha256,
2418                &verification_policy_with_trust(chain_policy()),
2419            )
2420            .expect("same-name rollover path must reach its configured signer");
2421
2422        assert!(resolved.is_some());
2423    }
2424
2425    #[test]
2426    fn x509_candidate_limit_counts_generated_partial_paths() {
2427        // A narrow DFS frontier can still generate unbounded partial paths over
2428        // time, so the resource limit must account for every generated state.
2429        let root = rcgen::CertifiedIssuer::self_signed(
2430            generated_certificate_params("candidate root", true),
2431            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2432        )
2433        .expect("root should be self-signable");
2434        let intermediate = rcgen::CertifiedIssuer::signed_by(
2435            generated_certificate_params("candidate intermediate", true),
2436            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2437            &root,
2438        )
2439        .expect("root should sign the intermediate");
2440        let leaf = generated_certificate_params("candidate leaf", false)
2441            .signed_by(
2442                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2443                &intermediate,
2444            )
2445            .expect("intermediate should sign the leaf");
2446        let info = x509_info(
2447            vec![
2448                root.der().to_vec(),
2449                intermediate.der().to_vec(),
2450                leaf.der().to_vec(),
2451            ],
2452            2,
2453        );
2454
2455        assert!(matches!(
2456            build_x509_certificate_paths_to_trusted_prefix(
2457                &info,
2458                2,
2459                1,
2460                9,
2461                2,
2462                crate::provider::default_provider(),
2463            ),
2464            Err(X509ChainBuildError::AmbiguousIssuer)
2465        ));
2466    }
2467
2468    #[test]
2469    fn selector_resolved_leaf_disambiguates_same_subject_issuers_by_signature() {
2470        // Certificate renewal may leave multiple configured intermediates with
2471        // the same subject DN. The leaf signature, not pool order, identifies
2472        // the one issuer that belongs to the verification path.
2473        let mut root_params =
2474            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
2475        root_params
2476            .distinguished_name
2477            .push(rcgen::DnType::CommonName, "shared-issuer root");
2478        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2479        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2480        let root = rcgen::CertifiedIssuer::self_signed(
2481            root_params,
2482            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2483        )
2484        .expect("root certificate should be self-signable");
2485
2486        let intermediate = |key: rcgen::KeyPair| {
2487            let mut params = rcgen::CertificateParams::new(Vec::new())
2488                .expect("empty intermediate SAN list should be valid");
2489            params
2490                .distinguished_name
2491                .push(rcgen::DnType::CommonName, "renewed intermediate");
2492            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2493            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2494            rcgen::CertifiedIssuer::signed_by(params, key, &root)
2495                .expect("root should sign the intermediate certificate")
2496        };
2497        let unrelated_intermediate = intermediate(
2498            rcgen::KeyPair::generate().expect("unrelated intermediate key generation should work"),
2499        );
2500        let signing_intermediate = intermediate(
2501            rcgen::KeyPair::generate().expect("signing intermediate key generation should work"),
2502        );
2503
2504        let mut leaf_params =
2505            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
2506        leaf_params
2507            .distinguished_name
2508            .push(rcgen::DnType::CommonName, "same-subject leaf");
2509        let leaf = leaf_params
2510            .signed_by(
2511                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2512                &signing_intermediate,
2513            )
2514            .expect("the selected intermediate should sign the leaf certificate");
2515        let key_info_xml = concat!(
2516            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2517            "<X509Data><X509SubjectName>CN=same-subject leaf</X509SubjectName></X509Data>",
2518            "</KeyInfo>"
2519        );
2520        let document = roxmltree::Document::parse(key_info_xml)
2521            .expect("static selector KeyInfo should parse as XML");
2522        let key_info = super::super::parse_key_info(document.root_element())
2523            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2524        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2525            lookup_certs: vec![
2526                leaf.der().to_vec(),
2527                unrelated_intermediate.der().to_vec(),
2528                signing_intermediate.der().to_vec(),
2529            ],
2530            trusted_certs: vec![root.der().to_vec()],
2531            ..KeyResolverConfig::default()
2532        });
2533
2534        let resolved = resolver
2535            .resolve_with_policy(
2536                Some(&key_info),
2537                SignatureAlgorithm::EcdsaSha256,
2538                &verification_policy_with_trust(chain_policy()),
2539            )
2540            .expect("the leaf signature should select its unique same-subject issuer");
2541
2542        assert!(resolved.is_some());
2543    }
2544
2545    #[test]
2546    fn x509_path_builder_skips_branch_local_unsupported_algorithms() {
2547        // An untrusted intermediate can share both the subject and public key
2548        // of the valid path while using an unsupported signature algorithm on
2549        // its own parent edge. That branch must not suppress the valid path.
2550        let root = rcgen::CertifiedIssuer::self_signed(
2551            generated_certificate_params("unsupported-edge root", true),
2552            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2553        )
2554        .expect("root certificate should be self-signable");
2555        let signing_intermediate = rcgen::CertifiedIssuer::signed_by(
2556            generated_certificate_params("shared unsupported-edge issuer", true),
2557            rcgen::KeyPair::generate().expect("signing issuer key generation should succeed"),
2558            &root,
2559        )
2560        .expect("root should sign the intermediate certificate");
2561        let key_unsupported_intermediate = rcgen::CertifiedIssuer::signed_by(
2562            generated_certificate_params("shared unsupported-edge issuer", true),
2563            rcgen::KeyPair::generate().expect("unsupported issuer key generation should succeed"),
2564            &root,
2565        )
2566        .expect("root should sign the alternate intermediate certificate");
2567        let leaf = generated_certificate_params("unsupported-edge leaf", false)
2568            .signed_by(
2569                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2570                &signing_intermediate,
2571            )
2572            .expect("signing intermediate should sign the leaf");
2573
2574        let ordered = x509_info(
2575            vec![
2576                leaf.der().to_vec(),
2577                key_unsupported_intermediate.der().to_vec(),
2578                signing_intermediate.der().to_vec(),
2579                root.der().to_vec(),
2580            ],
2581            0,
2582        );
2583        let key_selective_provider = RejectSecondSha512Provider {
2584            sha512_calls: AtomicUsize::new(0),
2585            verification_calls: AtomicUsize::new(0),
2586            reject_verification_call: Some(0),
2587            rejected_verification_data: None,
2588        };
2589        assert_eq!(
2590            super::super::parse::build_x509_certificate_chain_from(
2591                &ordered,
2592                0,
2593                &key_selective_provider,
2594            )
2595            .expect("one unsupported issuer key must not suppress a usable candidate"),
2596            vec![0, 2, 3]
2597        );
2598
2599        let anchored_same_edge = x509_info(
2600            vec![
2601                root.der().to_vec(),
2602                leaf.der().to_vec(),
2603                key_unsupported_intermediate.der().to_vec(),
2604                signing_intermediate.der().to_vec(),
2605            ],
2606            1,
2607        );
2608        let first_candidate_unsupported = RejectSecondSha512Provider {
2609            sha512_calls: AtomicUsize::new(0),
2610            verification_calls: AtomicUsize::new(0),
2611            reject_verification_call: Some(0),
2612            rejected_verification_data: None,
2613        };
2614        assert_eq!(
2615            build_x509_certificate_paths_to_trusted_prefix(
2616                &anchored_same_edge,
2617                1,
2618                1,
2619                4,
2620                8,
2621                &first_candidate_unsupported,
2622            )
2623            .expect("a later same-DN issuer must survive an earlier provider capability miss"),
2624            vec![vec![1, 3, 0]]
2625        );
2626
2627        let mut unsupported_intermediate = signing_intermediate.der().to_vec();
2628        let ecdsa_sha256_oid = [0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
2629        let offsets = unsupported_intermediate
2630            .windows(ecdsa_sha256_oid.len())
2631            .enumerate()
2632            .filter_map(|(offset, window)| (window == ecdsa_sha256_oid).then_some(offset))
2633            .collect::<Vec<_>>();
2634        assert_eq!(
2635            offsets.len(),
2636            2,
2637            "certificate must repeat its signature OID"
2638        );
2639        for offset in offsets {
2640            unsupported_intermediate[offset + ecdsa_sha256_oid.len() - 1] = 0x05;
2641        }
2642
2643        let anchored = x509_info(
2644            vec![
2645                root.der().to_vec(),
2646                leaf.der().to_vec(),
2647                signing_intermediate.der().to_vec(),
2648                unsupported_intermediate,
2649            ],
2650            1,
2651        );
2652        assert_eq!(
2653            build_x509_certificate_paths_to_trusted_prefix(
2654                &anchored,
2655                1,
2656                1,
2657                4,
2658                8,
2659                crate::provider::default_provider(),
2660            )
2661            .expect("a branch-local provider gap must not abort path enumeration"),
2662            vec![vec![1, 2, 0]]
2663        );
2664
2665        let unsupported_only = x509_info(
2666            vec![
2667                root.der().to_vec(),
2668                leaf.der().to_vec(),
2669                anchored.certificates[3].clone(),
2670            ],
2671            1,
2672        );
2673        assert!(matches!(
2674            build_x509_certificate_paths_to_trusted_prefix(
2675                &unsupported_only,
2676                1,
2677                1,
2678                4,
2679                8,
2680                crate::provider::default_provider(),
2681            ),
2682            Err(X509ChainBuildError::UnsupportedSignatureAlgorithm { ref oid })
2683                if oid == "1.2.840.10045.4.3.5"
2684        ));
2685    }
2686
2687    #[test]
2688    fn selector_resolved_certificate_preserves_supplied_crls() {
2689        let selector = "<KeyInfo><X509Data><X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName><X509CRL>CRL_PLACEHOLDER</X509CRL></X509Data></KeyInfo>";
2690        let crl = crl_der(include_str!(
2691            "../../tests/fixtures/keys/rsa/rsa-2048-cert-revoked-crl.pem"
2692        ));
2693        let (_, parsed_crl) =
2694            x509_parser::revocation_list::CertificateRevocationList::from_der(&crl)
2695                .expect("tracked CRL must parse");
2696        let crl_signed_data = parsed_crl.tbs_cert_list.as_ref().to_vec();
2697        let xml = replace_unprefixed_key_info(
2698            RSA_KEY_VALUE_SIGNATURE,
2699            &selector.replace("CRL_PLACEHOLDER", &STANDARD.encode(&crl)),
2700        );
2701        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2702            lookup_certs: vec![certificate_der(include_str!(
2703                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2704            ))],
2705            trusted_certs: vec![
2706                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2707                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
2708            ],
2709            ..KeyResolverConfig::default()
2710        });
2711        let policy = verification_policy_with_trust(crate::policy::KeyTrustPolicy {
2712            check_crls: true,
2713            max_x509_chain_depth: 3,
2714            ..chain_policy_at(
2715                SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_773_964_800),
2716            )
2717        });
2718
2719        let error = super::super::VerifyContext::new()
2720            .policy(policy.clone())
2721            .key_resolver(&resolver)
2722            .verify(&xml)
2723            .expect_err("selector lookup must retain and enforce the supplied CRL");
2724        assert!(matches!(
2725            error,
2726            DsigError::KeyResolution(KeyResolutionError::Chain(
2727                super::super::X509ChainError::Revoked(0)
2728            ))
2729        ));
2730
2731        // Match the exact TBSCertList bytes so earlier certificate-edge
2732        // verification succeeds and the provider rejection occurs at CRL
2733        // authentication itself.
2734        let provider = RejectSecondSha512Provider {
2735            sha512_calls: AtomicUsize::new(0),
2736            verification_calls: AtomicUsize::new(0),
2737            reject_verification_call: None,
2738            rejected_verification_data: Some(crl_signed_data),
2739        };
2740        let error = super::super::VerifyContext::new()
2741            .policy(policy)
2742            .key_resolver(&resolver)
2743            .provider(&provider)
2744            .verify(&xml)
2745            .expect_err("CRL authentication must retain the operation provider");
2746        assert!(matches!(
2747            error,
2748            DsigError::KeyResolution(KeyResolutionError::Chain(
2749                super::super::X509ChainError::Provider(_)
2750            ))
2751        ));
2752    }
2753
2754    #[test]
2755    fn resolves_each_x509_selector_from_configured_certificates() {
2756        // Every selector form documented by KeyInfo must independently locate
2757        // the same configured RSA certificate without embedded key material.
2758        let selectors = [
2759            "<X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName>",
2760            "<X509SubjectName>CN=  test   key rsa-2048  ,O=xml security library (HTTP://WWW.ALEKSEY.COM/XMLSEC),ST=california,C=us</X509SubjectName>",
2761            "<X509IssuerSerial><X509IssuerName>Email=xmlsec@aleksey.com,CN=Aleksey Sanin,OU=Second level CA,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509IssuerName><X509SerialNumber>680572598617295163017172295025714171905498632019</X509SerialNumber></X509IssuerSerial>",
2762            "<X509SKI>bcOXN/nsVl8GatRbcKrPbzIbw0Y=</X509SKI>",
2763        ];
2764        let configured_certificate = certificate_der(include_str!(
2765            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2766        ));
2767
2768        for selector in selectors {
2769            let key_info = format!("<KeyInfo><X509Data>{selector}</X509Data></KeyInfo>");
2770            let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
2771            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2772                lookup_certs: vec![configured_certificate.clone()],
2773                ..KeyResolverConfig::default()
2774            });
2775            let result = super::super::VerifyContext::new()
2776                .key_resolver(&resolver)
2777                .verify(&xml)
2778                .expect("X509 selector should resolve configured certificate");
2779
2780            assert_eq!(result.status, super::super::DsigStatus::Valid);
2781        }
2782    }
2783
2784    #[test]
2785    fn resolves_configured_chain_selectors_across_certificates() {
2786        // Selector categories may identify different members of one configured
2787        // chain; the unique leaf remains the signing certificate.
2788        let key_info = r#"<KeyInfo><X509Data><X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName><X509SKI>0X0XrEVCio75sBcl1TxymJ2IOiU=</X509SKI></X509Data></KeyInfo>"#;
2789        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
2790        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2791            lookup_certs: vec![
2792                certificate_der(include_str!(
2793                    "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2794                )),
2795                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2796            ],
2797            ..KeyResolverConfig::default()
2798        });
2799        let result = super::super::VerifyContext::new()
2800            .key_resolver(&resolver)
2801            .verify(&xml)
2802            .expect("selectors across one configured chain should resolve its leaf");
2803
2804        assert_eq!(result.status, super::super::DsigStatus::Valid);
2805    }
2806
2807    #[test]
2808    fn selectors_must_all_match_the_selected_certificate_path() {
2809        // Selector categories may identify different certificates only when
2810        // those certificates belong to the one path chosen for the signer.
2811        let signing_certificate = certificate_der(include_str!(
2812            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2813        ));
2814        let issuer_certificate =
2815            certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
2816        let unrelated = generated_certificate_params("unrelated selector certificate", false)
2817            .self_signed(
2818                &rcgen::KeyPair::generate().expect("unrelated key generation should succeed"),
2819            )
2820            .expect("unrelated certificate should be self-signable")
2821            .der()
2822            .to_vec();
2823        let digest = crate::provider::default_provider()
2824            .digest(super::super::DigestAlgorithm::Sha256, &unrelated)
2825            .expect("SHA-256 selector digest must be available");
2826        let key_info_xml = format!(
2827            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\" xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\"><X509Data><X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName><dsig11:X509Digest Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\">{}</dsig11:X509Digest></X509Data></KeyInfo>",
2828            STANDARD.encode(digest)
2829        );
2830        let document = roxmltree::Document::parse(&key_info_xml)
2831            .expect("generated selector KeyInfo must be XML");
2832        let key_info = super::super::parse_key_info(document.root_element())
2833            .expect("generated selector KeyInfo must be structurally valid");
2834        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2835            lookup_certs: vec![signing_certificate, issuer_certificate, unrelated],
2836            ..KeyResolverConfig::default()
2837        });
2838
2839        assert!(
2840            resolver
2841                .resolve(Some(&key_info), SignatureAlgorithm::RsaSha256)
2842                .expect("disjoint selector matches are a key miss")
2843                .is_none()
2844        );
2845    }
2846
2847    #[test]
2848    fn unmatched_x509_selector_does_not_resolve() {
2849        // A selector mismatch must not fall back to arbitrary configured key material.
2850        let key_info = "<KeyInfo><X509Data><X509SubjectName>CN=not-the-signer</X509SubjectName></X509Data></KeyInfo>";
2851        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
2852        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2853            lookup_certs: vec![certificate_der(include_str!(
2854                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2855            ))],
2856            ..KeyResolverConfig::default()
2857        });
2858        let result = super::super::VerifyContext::new()
2859            .key_resolver(&resolver)
2860            .verify(&xml)
2861            .expect("an unmatched selector is a key miss, not a parser failure");
2862
2863        assert!(matches!(
2864            result.status,
2865            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
2866        ));
2867    }
2868
2869    #[test]
2870    fn overlapping_trusted_and_lookup_certificate_preserves_trust() {
2871        // One physical certificate appearing in both pools is one candidate;
2872        // deduplication must retain the stronger trusted classification.
2873        let certificate = certificate_der(RSA_4096_CERTIFICATE);
2874        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2875            trusted_certs: vec![certificate.clone()],
2876            lookup_certs: vec![certificate],
2877            ..KeyResolverConfig::default()
2878        });
2879        let result = super::super::VerifyContext::new()
2880            .policy(verification_policy_with_trust(chain_policy_at(
2881                fixture_certificate_time(),
2882            )))
2883            .key_resolver(&resolver)
2884            .verify(&x509_signature_with_leaf_subject())
2885            .expect("trusted/lookup overlap must resolve as one trusted candidate");
2886
2887        assert_eq!(result.status, super::super::DsigStatus::Valid);
2888    }
2889
2890    #[test]
2891    fn distinct_x509_selector_matches_remain_ambiguous() {
2892        // Deduplication is identity-based, not selector-based: two distinct
2893        // certificates with the same subject remain separate candidates.
2894        let certificate = || {
2895            generated_certificate_params("ambiguous selector", false)
2896                .self_signed(
2897                    &rcgen::KeyPair::generate().expect("test key generation should succeed"),
2898                )
2899                .expect("test certificate should be self-signable")
2900                .der()
2901                .to_vec()
2902        };
2903        let xml = replace_unprefixed_key_info(
2904            X509_DIGEST_SIGNATURE,
2905            "<KeyInfo><X509Data><X509SubjectName>CN=ambiguous selector</X509SubjectName></X509Data></KeyInfo>",
2906        );
2907        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2908            lookup_certs: vec![certificate(), certificate()],
2909            ..KeyResolverConfig::default()
2910        });
2911        let error = super::super::VerifyContext::new()
2912            .key_resolver(&resolver)
2913            .verify(&xml)
2914            .expect_err("distinct selector matches must fail closed");
2915
2916        assert!(matches!(
2917            error,
2918            DsigError::KeyResolution(KeyResolutionError::AmbiguousCertificate)
2919        ));
2920    }
2921
2922    #[test]
2923    fn unsupported_x509_digest_selector_fails_closed() {
2924        // Unknown digest URIs must not be treated as a normal key miss because
2925        // that would silently weaken the caller's explicit selector policy.
2926        let key_info = "<KeyInfo xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\"><X509Data><dsig11:X509Digest Algorithm=\"urn:unsupported\">AQ==</dsig11:X509Digest></X509Data></KeyInfo>";
2927        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
2928        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2929            lookup_certs: vec![certificate_der(include_str!(
2930                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2931            ))],
2932            ..KeyResolverConfig::default()
2933        });
2934        let error = super::super::VerifyContext::new()
2935            .key_resolver(&resolver)
2936            .verify(&xml)
2937            .expect_err("unsupported X509Digest algorithm must fail closed");
2938
2939        assert!(matches!(
2940            error,
2941            DsigError::KeyResolution(KeyResolutionError::UnsupportedDigestAlgorithm(uri))
2942                if uri == "urn:unsupported"
2943        ));
2944    }
2945
2946    #[test]
2947    fn x509_digest_selector_uses_operation_provider() {
2948        // The SHA-512 selector is distinct from the SHA-256 reference digest,
2949        // so only provider-aware key selection can surface this rejection.
2950        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2951            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
2952            trusted_certs: vec![
2953                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2954                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
2955            ],
2956            ..KeyResolverConfig::default()
2957        });
2958        let provider = RejectSecondSha512Provider {
2959            sha512_calls: AtomicUsize::new(0),
2960            verification_calls: AtomicUsize::new(0),
2961            reject_verification_call: None,
2962            rejected_verification_data: None,
2963        };
2964        let error = super::super::VerifyContext::new()
2965            .key_resolver(&resolver)
2966            .provider(&provider)
2967            .verify(X509_DIGEST_SIGNATURE)
2968            .expect_err("X509Digest selection must use the operation provider");
2969
2970        assert!(
2971            matches!(
2972                error,
2973                DsigError::Provider(crate::provider::ProviderError::Unsupported {
2974                    operation: crate::provider::ProviderOperation::Digest,
2975                    algorithm: Some(ref uri),
2976                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
2977            ),
2978            "unexpected error: {error:?}"
2979        );
2980    }
2981
2982    #[test]
2983    fn resolves_named_key_end_to_end() {
2984        // KeyName lookup must preserve the same cryptographic result as embedded X509Data.
2985        let xml = replace_key_info(
2986            SIGNED_SAML,
2987            "<ds:KeyInfo><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>",
2988        );
2989        let mut config = KeyResolverConfig::default();
2990        config.named_keys.insert(
2991            "idp-signing".into(),
2992            VerificationKey {
2993                algorithm: SignatureAlgorithm::EcdsaSha256,
2994                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
2995                certificate_der: None,
2996                name: Some("idp-signing".into()),
2997            },
2998        );
2999        let resolver = DefaultKeyResolver::new(config);
3000        let result = super::super::VerifyContext::new()
3001            .key_resolver(&resolver)
3002            .verify(&xml)
3003            .expect("named key should resolve");
3004
3005        assert_eq!(result.status, super::super::DsigStatus::Valid);
3006    }
3007
3008    #[test]
3009    fn resolves_der_encoded_key_end_to_end() {
3010        // DSig 1.1 DEREncodedKeyValue must feed the same SPKI verifier path.
3011        let encoded = STANDARD.encode(public_key_der(SAML_PUBLIC_KEY));
3012        let xml = replace_key_info(
3013            SIGNED_SAML,
3014            &format!(
3015                "<ds:KeyInfo><dsig11:DEREncodedKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\">{encoded}</dsig11:DEREncodedKeyValue></ds:KeyInfo>"
3016            ),
3017        );
3018        let resolver = DefaultKeyResolver::default();
3019        let result = super::super::VerifyContext::new()
3020            .key_resolver(&resolver)
3021            .verify(&xml)
3022            .expect("DER key should resolve");
3023
3024        assert_eq!(result.status, super::super::DsigStatus::Valid);
3025    }
3026
3027    #[test]
3028    fn resolves_rsa_key_value_end_to_end() {
3029        // Embedded CryptoBinary parameters must verify the original RSA-2048 donor signature.
3030        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3031            .expect("fixture must contain an RSA public key");
3032        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3033        let key_info = format!(
3034            "<KeyInfo><KeyValue><RSAKeyValue><Modulus>{}</Modulus><Exponent>{}</Exponent></RSAKeyValue></KeyValue></KeyInfo>",
3035            modulus, exponent,
3036        );
3037        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
3038        let resolver = DefaultKeyResolver::default();
3039        let result = super::super::VerifyContext::new()
3040            .key_resolver(&resolver)
3041            .verify(&xml)
3042            .expect("RSAKeyValue should resolve");
3043
3044        assert_eq!(result.status, super::super::DsigStatus::Valid);
3045    }
3046
3047    #[test]
3048    fn rsa_key_value_rejects_legacy_weak_modulus() {
3049        // The secure policy rejects legacy RSA-SHA1 independently of whether
3050        // the capable key came from RSAKeyValue, DER, X.509, or KeyName.
3051        let resolver = DefaultKeyResolver::default();
3052        let error = super::super::VerifyContext::new()
3053            .key_resolver(&resolver)
3054            .verify(LEGACY_RSA_KEY_VALUE_SIGNATURE)
3055            .expect_err("context policy must override permissive resolver defaults");
3056
3057        assert!(matches!(
3058            error,
3059            DsigError::Policy(crate::policy::PolicyViolation::Algorithm {
3060                operation: "verification",
3061                ..
3062            })
3063        ));
3064    }
3065
3066    #[test]
3067    fn operation_policy_rejects_disabled_embedded_key_source() {
3068        // Resolver-owned key material cannot override the operation snapshot's
3069        // decision about which attacker-controlled KeyInfo forms are trusted.
3070        let key_info = KeyInfo {
3071            sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
3072                modulus: vec![0x80; 256],
3073                exponent: vec![1, 0, 1],
3074            })],
3075        };
3076        let mut policy = crate::policy::VerificationPolicy::default();
3077        policy.key_sources.key_value = false;
3078
3079        let error = match DefaultKeyResolver::default().resolve_with_policy(
3080            Some(&key_info),
3081            SignatureAlgorithm::RsaSha256,
3082            &policy,
3083        ) {
3084            Ok(_) => panic!("disabled KeyValue must fail before key construction"),
3085            Err(error) => error,
3086        };
3087
3088        assert!(matches!(
3089            error,
3090            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
3091                reason: "KeyValue key sources are disabled"
3092            })
3093        ));
3094    }
3095
3096    #[test]
3097    fn operation_policy_preflights_every_key_info_source_before_resolution() {
3098        // A permitted source resolving first must not hide a later source that
3099        // the immutable operation policy rejects.
3100        let mut config = KeyResolverConfig::default();
3101        config.named_keys.insert(
3102            "idp-signing".into(),
3103            VerificationKey {
3104                algorithm: SignatureAlgorithm::EcdsaSha256,
3105                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3106                certificate_der: None,
3107                name: Some("idp-signing".into()),
3108            },
3109        );
3110        let resolver = DefaultKeyResolver::new(config);
3111        let mut policy = crate::policy::VerificationPolicy::default();
3112        policy.key_sources.x509_data = false;
3113
3114        for sources in [
3115            vec![
3116                KeyInfoSource::KeyName("idp-signing".into()),
3117                KeyInfoSource::X509Data(X509DataInfo::default()),
3118            ],
3119            vec![
3120                KeyInfoSource::X509Data(X509DataInfo::default()),
3121                KeyInfoSource::KeyName("idp-signing".into()),
3122            ],
3123        ] {
3124            let error = match resolver.resolve_with_policy(
3125                Some(&KeyInfo { sources }),
3126                SignatureAlgorithm::EcdsaSha256,
3127                &policy,
3128            ) {
3129                Ok(_) => panic!("source order must not hide disabled X509Data"),
3130                Err(error) => error,
3131            };
3132
3133            assert!(matches!(
3134                error,
3135                DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
3136                    reason: "X509Data key sources are disabled"
3137                })
3138            ));
3139        }
3140    }
3141
3142    #[test]
3143    fn operation_policy_bounds_ordered_key_info_candidates() {
3144        // The candidate ceiling belongs to the complete verification snapshot:
3145        // neither a first source nor fallback to a later source may bypass it.
3146        let mut config = KeyResolverConfig::default();
3147        config.named_keys.insert(
3148            "idp-signing".into(),
3149            VerificationKey {
3150                algorithm: SignatureAlgorithm::EcdsaSha256,
3151                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3152                certificate_der: None,
3153                name: Some("idp-signing".into()),
3154            },
3155        );
3156        let resolver = DefaultKeyResolver::new(config);
3157        let key_info = KeyInfo {
3158            sources: vec![
3159                KeyInfoSource::KeyValue(KeyValueInfo::Ec {
3160                    curve_oid: "1.3.132.0.35".into(),
3161                    public_key: vec![4],
3162                }),
3163                KeyInfoSource::KeyName("idp-signing".into()),
3164            ],
3165        };
3166
3167        for maximum in [0, 1] {
3168            let mut policy = crate::policy::VerificationPolicy::default();
3169            policy.resources.max_key_candidates = maximum;
3170            let error = match resolver.resolve_with_policy(
3171                Some(&key_info),
3172                SignatureAlgorithm::EcdsaSha256,
3173                &policy,
3174            ) {
3175                Ok(_) => panic!("candidate ceiling {maximum} must stop resolution"),
3176                Err(error) => error,
3177            };
3178            assert!(matches!(
3179                error,
3180                DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3181                    resource: crate::policy::resource_name::KEY_CANDIDATES,
3182                    maximum: observed,
3183                    actual,
3184                }) if observed == maximum && actual == maximum + 1
3185            ));
3186        }
3187
3188        let mut policy = crate::policy::VerificationPolicy::default();
3189        policy.resources.max_key_candidates = 2;
3190        assert!(
3191            resolver
3192                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
3193                .expect("two allowed attempts must reach the named key")
3194                .is_some()
3195        );
3196    }
3197
3198    #[test]
3199    fn operation_policy_bounds_configured_x509_selector_candidates() {
3200        // One X509Data selector can fan out across the resolver-owned store.
3201        // Every distinct certificate inspected is candidate work, rather than
3202        // the complete store counting as one KeyInfo source.
3203        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3204            lookup_certs: vec![
3205                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3206                certificate_der(RSA_4096_CERTIFICATE),
3207            ],
3208            ..KeyResolverConfig::default()
3209        });
3210        let mut policy = crate::policy::VerificationPolicy::default();
3211        policy.resources.max_key_candidates = 1;
3212
3213        let error = super::super::VerifyContext::new()
3214            .policy(policy)
3215            .key_resolver(&resolver)
3216            .verify(&x509_signature_with_leaf_subject())
3217            .expect_err("the second configured certificate must exceed the candidate budget");
3218
3219        assert!(matches!(
3220            error,
3221            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3222                resource: crate::policy::resource_name::KEY_CANDIDATES,
3223                maximum: 1,
3224                actual: 2,
3225            })
3226        ));
3227    }
3228
3229    #[test]
3230    fn operation_policy_bounds_embedded_x509_certificate_candidates() {
3231        // Embedded X509Data is also composite key material. Its certificate
3232        // entries must not collapse into one candidate merely because they
3233        // share a single KeyInfo source node.
3234        let key_info = KeyInfo {
3235            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3236                certificates: vec![
3237                    certificate_der(RSA_4096_CERTIFICATE),
3238                    certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3239                ],
3240                certificate_chain: vec![0],
3241                ..X509DataInfo::default()
3242            })],
3243        };
3244        let mut policy = crate::policy::VerificationPolicy::default();
3245        policy.resources.max_key_candidates = 1;
3246
3247        let error = match DefaultKeyResolver::default().resolve_with_policy(
3248            Some(&key_info),
3249            SignatureAlgorithm::RsaSha256,
3250            &policy,
3251        ) {
3252            Ok(_) => panic!("the second embedded certificate must exceed the candidate budget"),
3253            Err(error) => error,
3254        };
3255
3256        assert!(matches!(
3257            error,
3258            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3259                resource: crate::policy::resource_name::KEY_CANDIDATES,
3260                maximum: 1,
3261                actual: 2,
3262            })
3263        ));
3264    }
3265
3266    #[test]
3267    fn operation_policy_charges_duplicate_configured_x509_candidates() {
3268        // Deduplication may avoid repeated parsing, but inspecting a duplicate
3269        // resolver entry still consumes work and must not bypass the budget.
3270        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3271        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3272            lookup_certs: vec![certificate.clone(), certificate],
3273            ..KeyResolverConfig::default()
3274        });
3275        let mut policy = crate::policy::VerificationPolicy::default();
3276        policy.resources.max_key_candidates = 1;
3277
3278        let error = super::super::VerifyContext::new()
3279            .policy(policy)
3280            .key_resolver(&resolver)
3281            .verify(&x509_signature_with_leaf_subject())
3282            .expect_err("the duplicate configured entry must consume candidate work");
3283
3284        assert!(matches!(
3285            error,
3286            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3287                resource: crate::policy::resource_name::KEY_CANDIDATES,
3288                maximum: 1,
3289                actual: 2,
3290            })
3291        ));
3292    }
3293
3294    #[test]
3295    fn operation_policy_charges_duplicate_embedded_x509_candidates() {
3296        // Public callers can construct KeyInfo without passing through parser
3297        // entry limits, so duplicate embedded entries must consume the budget.
3298        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3299        let key_info = KeyInfo {
3300            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3301                certificates: vec![certificate.clone(), certificate],
3302                certificate_chain: vec![0],
3303                ..X509DataInfo::default()
3304            })],
3305        };
3306        let mut policy = crate::policy::VerificationPolicy::default();
3307        policy.resources.max_key_candidates = 1;
3308
3309        let error = match DefaultKeyResolver::default().resolve_with_policy(
3310            Some(&key_info),
3311            SignatureAlgorithm::RsaSha256,
3312            &policy,
3313        ) {
3314            Ok(_) => panic!("the duplicate embedded entry must consume candidate work"),
3315            Err(error) => error,
3316        };
3317
3318        assert!(matches!(
3319            error,
3320            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3321                resource: crate::policy::resource_name::KEY_CANDIDATES,
3322                maximum: 1,
3323                actual: 2,
3324            })
3325        ));
3326    }
3327
3328    #[test]
3329    fn policy_aware_resolver_rejects_resources_above_hard_ceiling() {
3330        // The resolver is a public policy enforcement boundary in its own
3331        // right; callers must not need VerifyContext to validate the snapshot.
3332        let mut policy = crate::policy::VerificationPolicy::default();
3333        policy.resources.max_key_candidates = usize::MAX;
3334
3335        let error = match DefaultKeyResolver::default().resolve_with_policy(
3336            None,
3337            SignatureAlgorithm::RsaSha256,
3338            &policy,
3339        ) {
3340            Ok(_) => panic!("invalid resource policy must fail before key resolution"),
3341            Err(error) => error,
3342        };
3343
3344        assert!(matches!(
3345            error,
3346            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3347                resource: crate::policy::resource_name::KEY_CANDIDATES,
3348                actual: usize::MAX,
3349                ..
3350            })
3351        ));
3352    }
3353
3354    #[test]
3355    fn embedded_x509_digest_selection_uses_operation_provider() {
3356        // Embedded certificate selection happens while KeyInfo is parsed, so
3357        // that parser path must retain the verification operation's provider.
3358        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3359        let digest =
3360            super::super::compute_digest(super::super::DigestAlgorithm::Sha512, &certificate);
3361        let xml = format!(
3362            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><X509Data><X509Certificate>{}</X509Certificate><X509Digest xmlns=\"http://www.w3.org/2009/xmldsig11#\" Algorithm=\"{}\">{}</X509Digest></X509Data></KeyInfo>",
3363            STANDARD.encode(&certificate),
3364            super::super::DigestAlgorithm::Sha512.uri(),
3365            STANDARD.encode(digest),
3366        );
3367        let document = roxmltree::Document::parse(&xml).expect("generated KeyInfo must be XML");
3368        let provider = RejectSecondSha512Provider {
3369            sha512_calls: AtomicUsize::new(1),
3370            verification_calls: AtomicUsize::new(0),
3371            reject_verification_call: None,
3372            rejected_verification_data: None,
3373        };
3374
3375        let error =
3376            super::super::parse::parse_key_info_with_provider(document.root_element(), &provider)
3377                .expect_err("embedded X509Digest selection must use the operation provider");
3378
3379        assert!(
3380            matches!(
3381                error,
3382                ParseError::Provider(crate::provider::ProviderError::Unsupported {
3383                    operation: crate::provider::ProviderOperation::Digest,
3384                    algorithm: Some(ref uri),
3385                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
3386            ),
3387            "unexpected error: {error:?}"
3388        );
3389    }
3390
3391    #[test]
3392    fn generic_key_resolution_keeps_legacy_capability_source_independent() {
3393        let certificate =
3394            include_bytes!("../../tests/fixtures/xmldsig/phaos-xmldsig-three/certs/rsa-cert.der")
3395                .to_vec();
3396        let (_, parsed_certificate) = X509Certificate::from_der(&certificate)
3397            .expect("the Phaos fixture is a DER certificate");
3398        let public_key = parsed_certificate.public_key().raw.to_vec();
3399        let rsa_public_key = rsa::RsaPublicKey::from_public_key_der(&public_key)
3400            .expect("the Phaos certificate contains an RSA public key");
3401        let certificate_metadata = parse_x509_certificate(&certificate)
3402            .expect("the Phaos fixture has supported X.509 metadata");
3403        let named_key = VerificationKey {
3404            algorithm: SignatureAlgorithm::RsaSha1,
3405            public_key_bytes: public_key.clone(),
3406            certificate_der: None,
3407            name: Some("legacy".into()),
3408        };
3409        let key_infos = [
3410            KeyInfo {
3411                sources: vec![KeyInfoSource::KeyName("legacy".into())],
3412            },
3413            KeyInfo {
3414                sources: vec![KeyInfoSource::DerEncodedKeyValue(public_key.clone())],
3415            },
3416            KeyInfo {
3417                sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
3418                    modulus: rsa_public_key.n().to_be_bytes_trimmed_vartime().to_vec(),
3419                    exponent: rsa_public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
3420                })],
3421            },
3422            KeyInfo {
3423                sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3424                    certificates: vec![certificate],
3425                    parsed_certificates: vec![certificate_metadata],
3426                    certificate_chain: vec![0],
3427                    ..X509DataInfo::default()
3428                })],
3429            },
3430        ];
3431        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3432            named_keys: HashMap::from([("legacy".into(), named_key.clone())]),
3433            ..KeyResolverConfig::default()
3434        });
3435        let mut policy = crate::policy::VerificationPolicy::default();
3436        policy.key_trust.rsa_keys.minimum_modulus_bits = 1024;
3437        policy
3438            .key_trust
3439            .allowed_legacy_signature_algorithms
3440            .insert(SignatureAlgorithm::RsaSha1);
3441
3442        for key_info in &key_infos {
3443            let key = resolver
3444                .resolve_with_policy(Some(key_info), SignatureAlgorithm::RsaSha1, &policy)
3445                .expect("the key source is valid")
3446                .expect("key resolution remains independent from operation policy");
3447            assert!(
3448                !key.verify(SignatureAlgorithm::RsaSha1, b"data", &[0; 128])
3449                    .expect("the legacy RSA key is structurally valid")
3450            );
3451        }
3452    }
3453
3454    #[test]
3455    fn rsa_key_value_rejects_ecdsa_signature_method() {
3456        // Embedded RSA parameters must not be relabeled for an ECDSA SignatureMethod.
3457        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3458            .expect("fixture must contain an RSA public key");
3459        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3460        let key_info = format!(
3461            "<ds:KeyInfo><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>{}</ds:Modulus><ds:Exponent>{}</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo>",
3462            modulus, exponent,
3463        );
3464        let xml = replace_key_info(SIGNED_SAML, &key_info);
3465        let resolver = DefaultKeyResolver::default();
3466        let error = super::super::VerifyContext::new()
3467            .key_resolver(&resolver)
3468            .verify(&xml)
3469            .expect_err("RSAKeyValue must not resolve for ECDSA");
3470
3471        assert!(matches!(
3472            error,
3473            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3474        ));
3475    }
3476
3477    #[test]
3478    fn resolves_ec_p256_key_value_end_to_end() {
3479        // XMLDSig 1.1 ECKeyValue must verify without a preset key or certificate.
3480        let resolver = DefaultKeyResolver::default();
3481        let result = super::super::VerifyContext::new()
3482            .key_resolver(&resolver)
3483            .verify(EC_P256_KEY_VALUE_SIGNATURE)
3484            .expect("P-256 ECKeyValue should resolve");
3485
3486        assert_eq!(result.status, super::super::DsigStatus::Valid);
3487    }
3488
3489    #[test]
3490    fn resolves_ec_p384_key_value_end_to_end() {
3491        // The donor P-384 vector uses NamedCurve + uncompressed PublicKey.
3492        let resolver = DefaultKeyResolver::default();
3493        let result = super::super::VerifyContext::new()
3494            .key_resolver(&resolver)
3495            .verify(EC_P384_KEY_VALUE_SIGNATURE)
3496            .expect("P-384 ECKeyValue should resolve");
3497
3498        assert_eq!(result.status, super::super::DsigStatus::Valid);
3499    }
3500
3501    #[test]
3502    fn ec_key_value_ignored_for_rsa_signature_method() {
3503        // Embedded EC key material must not be relabeled for an RSA SignatureMethod.
3504        let key_info = r#"<KeyInfo xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BJ/yaXNlq4FRObyJCBhb5jAz8GVzinK3bBGLjSDfjbJwNfydtgjnlS4EsDmxSRhWyJWq6GIqy5wvnaiARK04uB4=</dsig11:PublicKey></dsig11:ECKeyValue></KeyValue></KeyInfo>"#;
3505        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3506        let resolver = DefaultKeyResolver::default();
3507        let result = super::super::VerifyContext::new()
3508            .key_resolver(&resolver)
3509            .verify(&xml)
3510            .expect("single incompatible ECKeyValue should be ignored");
3511
3512        assert_eq!(
3513            result.status,
3514            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
3515        );
3516    }
3517
3518    #[test]
3519    fn incompatible_ec_key_value_falls_back_to_later_rsa_key_value() {
3520        // Mixed KeyInfo should keep scanning after an incompatible ECKeyValue source.
3521        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3522            .expect("fixture must contain an RSA public key");
3523        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3524        let key_info = format!(
3525            r#"<KeyInfo xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BJ/yaXNlq4FRObyJCBhb5jAz8GVzinK3bBGLjSDfjbJwNfydtgjnlS4EsDmxSRhWyJWq6GIqy5wvnaiARK04uB4=</dsig11:PublicKey></dsig11:ECKeyValue></KeyValue><KeyValue><RSAKeyValue><Modulus>{}</Modulus><Exponent>{}</Exponent></RSAKeyValue></KeyValue></KeyInfo>"#,
3526            modulus, exponent,
3527        );
3528        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
3529        let resolver = DefaultKeyResolver::default();
3530        let result = super::super::VerifyContext::new()
3531            .key_resolver(&resolver)
3532            .verify(&xml)
3533            .expect("later RSAKeyValue should resolve");
3534
3535        assert_eq!(result.status, super::super::DsigStatus::Valid);
3536    }
3537
3538    #[test]
3539    fn unsupported_ec_key_value_falls_back_to_later_key_name() {
3540        // Unsupported curves are non-fatal so a later compatible source can verify.
3541        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.3.132.0.35"/><dsig11:PublicKey>BA==</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3542        let xml = replace_key_info(SIGNED_SAML, key_info);
3543        let mut config = KeyResolverConfig::default();
3544        config.named_keys.insert(
3545            "idp-signing".into(),
3546            VerificationKey {
3547                algorithm: SignatureAlgorithm::EcdsaSha256,
3548                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3549                certificate_der: None,
3550                name: Some("idp-signing".into()),
3551            },
3552        );
3553        let resolver = DefaultKeyResolver::new(config);
3554        let result = super::super::VerifyContext::new()
3555            .key_resolver(&resolver)
3556            .verify(&xml)
3557            .expect("later KeyName should resolve");
3558
3559        assert_eq!(result.status, super::super::DsigStatus::Valid);
3560    }
3561
3562    #[test]
3563    fn invalid_ec_key_value_falls_back_to_later_key_name() {
3564        // Off-curve EC points are typed errors only if no later source can verify.
3565        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3566        let xml = replace_key_info(SIGNED_SAML, key_info);
3567        let mut config = KeyResolverConfig::default();
3568        config.named_keys.insert(
3569            "idp-signing".into(),
3570            VerificationKey {
3571                algorithm: SignatureAlgorithm::EcdsaSha256,
3572                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3573                certificate_der: None,
3574                name: Some("idp-signing".into()),
3575            },
3576        );
3577        let resolver = DefaultKeyResolver::new(config);
3578        let result = super::super::VerifyContext::new()
3579            .key_resolver(&resolver)
3580            .verify(&xml)
3581            .expect("later KeyName should resolve after invalid ECKeyValue");
3582
3583        assert_eq!(result.status, super::super::DsigStatus::Valid);
3584    }
3585
3586    #[test]
3587    fn malformed_ec_key_value_falls_back_to_later_key_name() {
3588        // Parse-level EC point errors remain non-fatal while later sources exist.
3589        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3590        let xml = replace_key_info(SIGNED_SAML, key_info);
3591        let mut config = KeyResolverConfig::default();
3592        config.named_keys.insert(
3593            "idp-signing".into(),
3594            VerificationKey {
3595                algorithm: SignatureAlgorithm::EcdsaSha256,
3596                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3597                certificate_der: None,
3598                name: Some("idp-signing".into()),
3599            },
3600        );
3601        let resolver = DefaultKeyResolver::new(config);
3602        let result = super::super::VerifyContext::new()
3603            .key_resolver(&resolver)
3604            .verify(&xml)
3605            .expect("later KeyName should resolve after malformed ECKeyValue");
3606
3607        assert_eq!(result.status, super::super::DsigStatus::Valid);
3608    }
3609
3610    #[test]
3611    fn invalid_base64_ec_key_value_falls_back_to_later_key_name() {
3612        // A bad ECKeyValue payload is an unusable source, not a reason to skip
3613        // later ordered KeyInfo sources that can verify the signature.
3614        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>not base64!</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3615        let xml = replace_key_info(SIGNED_SAML, key_info);
3616        let mut config = KeyResolverConfig::default();
3617        config.named_keys.insert(
3618            "idp-signing".into(),
3619            VerificationKey {
3620                algorithm: SignatureAlgorithm::EcdsaSha256,
3621                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3622                certificate_der: None,
3623                name: Some("idp-signing".into()),
3624            },
3625        );
3626        let resolver = DefaultKeyResolver::new(config);
3627        let result = super::super::VerifyContext::new()
3628            .key_resolver(&resolver)
3629            .verify(&xml)
3630            .expect("later KeyName should resolve after bad ECKeyValue base64");
3631
3632        assert_eq!(result.status, super::super::DsigStatus::Valid);
3633    }
3634
3635    #[test]
3636    fn missing_curve_uri_ec_key_value_falls_back_to_later_key_name() {
3637        // Missing EC curve parameters make only this KeyValue source unusable.
3638        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve/><dsig11:PublicKey>BA==</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3639        let xml = replace_key_info(SIGNED_SAML, key_info);
3640        let mut config = KeyResolverConfig::default();
3641        config.named_keys.insert(
3642            "idp-signing".into(),
3643            VerificationKey {
3644                algorithm: SignatureAlgorithm::EcdsaSha256,
3645                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3646                certificate_der: None,
3647                name: Some("idp-signing".into()),
3648            },
3649        );
3650        let resolver = DefaultKeyResolver::new(config);
3651        let result = super::super::VerifyContext::new()
3652            .key_resolver(&resolver)
3653            .verify(&xml)
3654            .expect("later KeyName should resolve after missing EC curve URI");
3655
3656        assert_eq!(result.status, super::super::DsigStatus::Valid);
3657    }
3658
3659    #[test]
3660    fn malformed_ec_key_value_children_fall_back_to_later_key_name() {
3661        // An unusable EC source must not prevent later ordered KeyInfo sources
3662        // from resolving, regardless of which required child-shape check fails.
3663        let malformed_ec_key_values = [
3664            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/>"#,
3665            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/>"#,
3666            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BA==</dsig11:PublicKey><dsig11:PublicKey>BA==</dsig11:PublicKey>"#,
3667        ];
3668
3669        for malformed_children in malformed_ec_key_values {
3670            let key_info = format!(
3671                r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue>{malformed_children}</dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#
3672            );
3673            let xml = replace_key_info(SIGNED_SAML, &key_info);
3674            let mut config = KeyResolverConfig::default();
3675            config.named_keys.insert(
3676                "idp-signing".into(),
3677                VerificationKey {
3678                    algorithm: SignatureAlgorithm::EcdsaSha256,
3679                    public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3680                    certificate_der: None,
3681                    name: Some("idp-signing".into()),
3682                },
3683            );
3684            let resolver = DefaultKeyResolver::new(config);
3685            let result = super::super::VerifyContext::new()
3686                .key_resolver(&resolver)
3687                .verify(&xml)
3688                .expect("later KeyName should resolve after malformed EC child shape");
3689
3690            assert_eq!(result.status, super::super::DsigStatus::Valid);
3691        }
3692    }
3693
3694    #[test]
3695    fn supported_ec_curve_does_not_fall_back_to_later_key_name() {
3696        // ECDSA-SHA256 accepts P-384, so this first source is a usable key and
3697        // must not be skipped merely because a later P-256 KeyName happens to
3698        // verify the signature. Verification fails against the selected key.
3699        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.3.132.0.34"/><dsig11:PublicKey>BO/yd/OZzDfjX4qivDY/vsUIuh6KWAxoxW5P4ukvwd+T6pVljWsX2UBJNNy5MdhTwB8e2YwB8kUbJwdsAS/XGi/fz8unFrs+lVlAgIs6s/xBYFbfUoRiAacD2SpVDe6XBA==</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>"#;
3700        let xml = replace_key_info(SIGNED_SAML, key_info);
3701        let mut config = KeyResolverConfig::default();
3702        config.named_keys.insert(
3703            "idp-signing".into(),
3704            VerificationKey {
3705                algorithm: SignatureAlgorithm::EcdsaSha256,
3706                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3707                certificate_der: None,
3708                name: Some("idp-signing".into()),
3709            },
3710        );
3711        let resolver = DefaultKeyResolver::new(config);
3712        let error = super::super::VerifyContext::new()
3713            .key_resolver(&resolver)
3714            .verify(&xml)
3715            .expect_err("a usable first key source must not fall through after verification");
3716
3717        assert!(matches!(
3718            error,
3719            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
3720        ));
3721    }
3722
3723    #[test]
3724    fn lone_malformed_ec_key_value_reports_invalid_public_key() {
3725        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue></ds:KeyInfo>"#;
3726        let xml = replace_key_info(SIGNED_SAML, key_info);
3727        let error = super::super::VerifyContext::new()
3728            .key_resolver(&DefaultKeyResolver::default())
3729            .verify(&xml)
3730            .expect_err("lone malformed ECKeyValue should surface typed key error");
3731
3732        assert!(matches!(
3733            error,
3734            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
3735        ));
3736    }
3737
3738    #[test]
3739    fn lone_supported_ec_curve_reaches_signature_verification() {
3740        let key_info = r#"<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:dsig11="http://www.w3.org/2009/xmldsig11#"><ds:KeyValue><dsig11:ECKeyValue><dsig11:NamedCurve URI="urn:oid:1.3.132.0.34"/><dsig11:PublicKey>BO/yd/OZzDfjX4qivDY/vsUIuh6KWAxoxW5P4ukvwd+T6pVljWsX2UBJNNy5MdhTwB8e2YwB8kUbJwdsAS/XGi/fz8unFrs+lVlAgIs6s/xBYFbfUoRiAacD2SpVDe6XBA==</dsig11:PublicKey></dsig11:ECKeyValue></ds:KeyValue></ds:KeyInfo>"#;
3741        let xml = replace_key_info(SIGNED_SAML, key_info);
3742        let error = super::super::VerifyContext::new()
3743            .key_resolver(&DefaultKeyResolver::default())
3744            .verify(&xml)
3745            .expect_err("a supported EC curve must reach signature verification");
3746
3747        assert!(matches!(
3748            error,
3749            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
3750        ));
3751    }
3752
3753    #[test]
3754    fn chain_verification_rejects_untrusted_embedded_certificate() {
3755        // Enabling chain policy must fail closed when no trust anchor is configured.
3756        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3757            ..KeyResolverConfig::default()
3758        });
3759        let error = super::super::VerifyContext::new()
3760            .policy(verification_policy_with_trust(chain_policy()))
3761            .key_resolver(&resolver)
3762            .verify(SIGNED_SAML)
3763            .expect_err("untrusted certificate must fail chain validation");
3764
3765        assert!(matches!(
3766            error,
3767            DsigError::KeyResolution(KeyResolutionError::Chain(
3768                super::super::X509ChainError::UntrustedRoot
3769            ))
3770        ));
3771    }
3772
3773    #[test]
3774    fn named_key_algorithm_mismatch_fails_closed() {
3775        // A key registered for RSA must never be attempted for an ECDSA signature.
3776        let xml = replace_key_info(
3777            SIGNED_SAML,
3778            "<ds:KeyInfo><ds:KeyName>wrong-algorithm</ds:KeyName></ds:KeyInfo>",
3779        );
3780        let mut config = KeyResolverConfig::default();
3781        config.named_keys.insert(
3782            "wrong-algorithm".into(),
3783            VerificationKey {
3784                algorithm: SignatureAlgorithm::RsaSha256,
3785                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3786                certificate_der: None,
3787                name: Some("wrong-algorithm".into()),
3788            },
3789        );
3790        let resolver = DefaultKeyResolver::new(config);
3791        let error = super::super::VerifyContext::new()
3792            .key_resolver(&resolver)
3793            .verify(&xml)
3794            .expect_err("algorithm mismatch must fail closed");
3795
3796        assert!(matches!(
3797            error,
3798            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3799        ));
3800    }
3801
3802    #[test]
3803    fn named_key_spki_type_mismatch_fails_during_resolution() {
3804        // The configured algorithm label cannot override the actual SPKI key type.
3805        let xml = replace_key_info(
3806            SIGNED_SAML,
3807            "<ds:KeyInfo><ds:KeyName>mislabeled</ds:KeyName></ds:KeyInfo>",
3808        );
3809        let mut config = KeyResolverConfig::default();
3810        config.named_keys.insert(
3811            "mislabeled".into(),
3812            VerificationKey {
3813                algorithm: SignatureAlgorithm::EcdsaSha256,
3814                public_key_bytes: public_key_der(RSA_PUBLIC_KEY),
3815                certificate_der: None,
3816                name: Some("mislabeled".into()),
3817            },
3818        );
3819        let resolver = DefaultKeyResolver::new(config);
3820        let error = super::super::VerifyContext::new()
3821            .key_resolver(&resolver)
3822            .verify(&xml)
3823            .expect_err("mislabeled named key must fail during resolution");
3824
3825        assert!(matches!(
3826            error,
3827            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3828        ));
3829    }
3830
3831    #[test]
3832    fn malformed_named_key_reports_public_key_error() {
3833        // Non-certificate SPKI failures must not be mislabeled as certificate errors.
3834        let xml = replace_key_info(
3835            SIGNED_SAML,
3836            "<ds:KeyInfo><ds:KeyName>malformed</ds:KeyName></ds:KeyInfo>",
3837        );
3838        let mut config = KeyResolverConfig::default();
3839        config.named_keys.insert(
3840            "malformed".into(),
3841            VerificationKey {
3842                algorithm: SignatureAlgorithm::EcdsaSha256,
3843                public_key_bytes: vec![1, 2, 3],
3844                certificate_der: None,
3845                name: Some("malformed".into()),
3846            },
3847        );
3848        let resolver = DefaultKeyResolver::new(config);
3849        let error = super::super::VerifyContext::new()
3850            .key_resolver(&resolver)
3851            .verify(&xml)
3852            .expect_err("malformed named key must fail during resolution");
3853
3854        assert!(matches!(
3855            error,
3856            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
3857        ));
3858    }
3859}