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 rcgen::{
1274        CertificateRevocationListParams, Issuer, KeyIdMethod, KeyPair, KeyUsagePurpose,
1275        RevokedCertParams, SerialNumber, date_time_ymd,
1276    };
1277    use rsa::{pkcs8::DecodePublicKey, traits::PublicKeyParts};
1278
1279    use super::*;
1280
1281    struct RejectSecondSha512Provider {
1282        sha512_calls: AtomicUsize,
1283        verification_calls: AtomicUsize,
1284        reject_verification_call: Option<usize>,
1285        rejected_verification_data: Option<Vec<u8>>,
1286    }
1287
1288    impl crate::provider::CryptoProvider for RejectSecondSha512Provider {
1289        fn name(&self) -> &'static str {
1290            "reject-second-sha512"
1291        }
1292
1293        fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
1294            crate::provider::default_provider().supports(capability)
1295        }
1296
1297        fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
1298            crate::provider::default_provider().fill_random(output)
1299        }
1300
1301        fn derive_key(
1302            &self,
1303            parameters: &crate::provider::KdfParameters<'_>,
1304            secret: &[u8],
1305        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1306            crate::provider::default_provider().derive_key(parameters, secret)
1307        }
1308
1309        fn digest(
1310            &self,
1311            algorithm: super::super::DigestAlgorithm,
1312            data: &[u8],
1313        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1314            if algorithm == super::super::DigestAlgorithm::Sha512
1315                && self.sha512_calls.fetch_add(1, Ordering::Relaxed) > 0
1316            {
1317                return Err(crate::provider::ProviderError::Unsupported {
1318                    operation: crate::provider::ProviderOperation::Digest,
1319                    algorithm: Some(algorithm.uri().to_owned()),
1320                });
1321            }
1322            crate::provider::default_provider().digest(algorithm, data)
1323        }
1324
1325        fn sign(
1326            &self,
1327            key: &dyn super::super::SigningKey,
1328            algorithm: SignatureAlgorithm,
1329            data: &[u8],
1330        ) -> Result<Vec<u8>, super::super::SigningKeyError> {
1331            crate::provider::default_provider().sign(key, algorithm, data)
1332        }
1333
1334        fn verify(
1335            &self,
1336            key: &dyn VerifyingKey,
1337            algorithm: SignatureAlgorithm,
1338            data: &[u8],
1339            signature: &[u8],
1340        ) -> Result<bool, DsigError> {
1341            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
1342            if self.reject_verification_call == Some(call)
1343                || self
1344                    .rejected_verification_data
1345                    .as_deref()
1346                    .is_some_and(|rejected| rejected == data)
1347            {
1348                return Err(crate::provider::ProviderError::Unsupported {
1349                    operation: crate::provider::ProviderOperation::Verify,
1350                    algorithm: Some(algorithm.uri().to_owned()),
1351                }
1352                .into());
1353            }
1354            crate::provider::default_provider().verify(key, algorithm, data, signature)
1355        }
1356
1357        fn verify_x509_signature(
1358            &self,
1359            algorithm: crate::provider::X509SignatureAlgorithm,
1360            data: &[u8],
1361            signature: &[u8],
1362            issuer_spki_der: &[u8],
1363        ) -> Result<bool, crate::provider::ProviderError> {
1364            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
1365            if self.reject_verification_call == Some(call)
1366                || self
1367                    .rejected_verification_data
1368                    .as_deref()
1369                    .is_some_and(|rejected| rejected == data)
1370            {
1371                return Err(crate::provider::ProviderError::Unsupported {
1372                    operation: crate::provider::ProviderOperation::VerifyCertificate,
1373                    algorithm: Some(algorithm.oid().to_owned()),
1374                });
1375            }
1376            crate::provider::default_provider().verify_x509_signature(
1377                algorithm,
1378                data,
1379                signature,
1380                issuer_spki_der,
1381            )
1382        }
1383
1384        #[cfg(feature = "xmlenc")]
1385        fn encrypt_data(
1386            &self,
1387            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
1388            key: &[u8],
1389            plaintext: &[u8],
1390        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1391            crate::provider::default_provider().encrypt_data(algorithm, key, plaintext)
1392        }
1393
1394        #[cfg(feature = "xmlenc")]
1395        fn decrypt_data(
1396            &self,
1397            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
1398            key: &[u8],
1399            ciphertext: &[u8],
1400        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1401            crate::provider::default_provider().decrypt_data(algorithm, key, ciphertext)
1402        }
1403
1404        #[cfg(feature = "xmlenc")]
1405        fn wrap_key(
1406            &self,
1407            algorithm: crate::xmlenc::KeyWrapAlgorithm,
1408            kek: &[u8],
1409            key: &[u8],
1410        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1411            crate::provider::default_provider().wrap_key(algorithm, kek, key)
1412        }
1413
1414        #[cfg(feature = "xmlenc")]
1415        fn unwrap_key(
1416            &self,
1417            algorithm: crate::xmlenc::KeyWrapAlgorithm,
1418            kek: &[u8],
1419            wrapped: &[u8],
1420        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1421            crate::provider::default_provider().unwrap_key(algorithm, kek, wrapped)
1422        }
1423
1424        #[cfg(feature = "xmlenc")]
1425        fn transport_key(
1426            &self,
1427            key: &dyn crate::provider::KeyTransportKey,
1428            parameters: &crate::xmlenc::RsaOaepParameters,
1429            plaintext: &[u8],
1430        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1431            crate::provider::default_provider().transport_key(key, parameters, plaintext)
1432        }
1433
1434        #[cfg(feature = "xmlenc")]
1435        fn recover_key(
1436            &self,
1437            key: &dyn crate::provider::KeyRecoveryKey,
1438            parameters: &crate::xmlenc::RsaOaepParameters,
1439            ciphertext: &[u8],
1440        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1441            crate::provider::default_provider().recover_key(key, parameters, ciphertext)
1442        }
1443    }
1444
1445    fn chain_policy() -> crate::policy::KeyTrustPolicy {
1446        crate::policy::KeyTrustPolicy {
1447            verify_x509_chains: true,
1448            ..crate::policy::KeyTrustPolicy::default()
1449        }
1450    }
1451
1452    fn chain_policy_at(verification_time: SystemTime) -> crate::policy::KeyTrustPolicy {
1453        crate::policy::KeyTrustPolicy {
1454            verification_time: Some(verification_time),
1455            ..chain_policy()
1456        }
1457    }
1458
1459    fn verification_policy_with_trust(
1460        key_trust: crate::policy::KeyTrustPolicy,
1461    ) -> crate::policy::VerificationPolicy {
1462        crate::policy::VerificationPolicy {
1463            key_trust,
1464            ..crate::policy::VerificationPolicy::default()
1465        }
1466    }
1467
1468    const SIGNED_SAML: &str =
1469        include_str!("../../tests/fixtures/saml/response_signed_by_idp_ecdsa.xml");
1470    const SAML_PUBLIC_KEY: &str =
1471        include_str!("../../tests/fixtures/keys/ec/saml-idp-ecdsa-pubkey.pem");
1472    const RSA_PUBLIC_KEY: &str = include_str!("../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem");
1473    const RSA_4096_CERTIFICATE: &str =
1474        include_str!("../../tests/fixtures/keys/rsa/rsa-4096-cert.pem");
1475    const X509_DIGEST_SIGNATURE: &str = include_str!(
1476        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha512.xml"
1477    );
1478    const X509_DIGEST_SHA256_SIGNATURE: &str = include_str!(
1479        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha256.xml"
1480    );
1481    const RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
1482        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloping-sha256-rsa-sha256.xml"
1483    );
1484    const LEGACY_RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
1485        "../../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/signature-enveloping-rsa.xml"
1486    );
1487    const EC_P256_KEY_VALUE_SIGNATURE: &str = include_str!(
1488        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p256_sha256.xml"
1489    );
1490    const EC_P384_KEY_VALUE_SIGNATURE: &str = include_str!(
1491        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p384_sha384.xml"
1492    );
1493
1494    fn replace_key_info(xml: &str, replacement: &str) -> String {
1495        let start = xml.find("<ds:KeyInfo>").expect("fixture has KeyInfo");
1496        let end = xml
1497            .find("</ds:KeyInfo>")
1498            .expect("fixture has closing KeyInfo")
1499            + "</ds:KeyInfo>".len();
1500        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
1501    }
1502
1503    fn replace_unprefixed_key_info(xml: &str, replacement: &str) -> String {
1504        let start = xml.find("<KeyInfo>").expect("fixture has KeyInfo");
1505        let end = xml.find("</KeyInfo>").expect("fixture has closing KeyInfo") + "</KeyInfo>".len();
1506        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
1507    }
1508
1509    fn rsa_key_value_parts(public_key: &rsa::RsaPublicKey) -> (String, String) {
1510        (
1511            STANDARD.encode(public_key.n().to_be_bytes_trimmed_vartime()),
1512            STANDARD.encode(public_key.e().to_be_bytes_trimmed_vartime()),
1513        )
1514    }
1515
1516    fn x509_signature_with_leaf_subject() -> String {
1517        replace_unprefixed_key_info(
1518            X509_DIGEST_SIGNATURE,
1519            "<KeyInfo><X509Data><X509SubjectName>CN=Test Key rsa-4096,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName></X509Data></KeyInfo>",
1520        )
1521    }
1522
1523    fn fixture_certificate_time() -> SystemTime {
1524        // 2027-01-15 UTC, inside the donor certificates' 2026-2126 validity window.
1525        SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_800_000_000)
1526    }
1527
1528    fn public_key_der(pem_text: &str) -> Vec<u8> {
1529        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
1530            .expect("fixture public key is PEM");
1531        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1532        assert_eq!(pem.label, "PUBLIC KEY");
1533        pem.contents
1534    }
1535
1536    fn certificate_der(pem_text: &str) -> Vec<u8> {
1537        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
1538            .expect("fixture certificate is PEM");
1539        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1540        assert_eq!(pem.label, "CERTIFICATE");
1541        pem.contents
1542    }
1543
1544    fn crl_der(pem_text: &str) -> Vec<u8> {
1545        let (rest, pem) =
1546            x509_parser::pem::parse_x509_pem(pem_text.as_bytes()).expect("fixture CRL is PEM");
1547        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
1548        assert_eq!(pem.label, "X509 CRL");
1549        pem.contents
1550    }
1551
1552    fn generated_certificate_params(common_name: &str, is_ca: bool) -> rcgen::CertificateParams {
1553        let mut params = rcgen::CertificateParams::new(Vec::new())
1554            .expect("empty SAN list should produce valid certificate parameters");
1555        params
1556            .distinguished_name
1557            .push(rcgen::DnType::CommonName, common_name);
1558        if is_ca {
1559            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1560            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
1561        }
1562        params
1563    }
1564
1565    fn authorized_revoked_chain() -> (X509DataInfo, Vec<u8>) {
1566        let mut root_params = generated_certificate_params("revocation root", true);
1567        root_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
1568        let root = rcgen::CertifiedIssuer::self_signed(
1569            root_params,
1570            KeyPair::generate().expect("root key generation should succeed"),
1571        )
1572        .expect("root should be self-signable");
1573
1574        let issuer_key = KeyPair::generate().expect("issuer key generation should succeed");
1575        let mut issuer_params = generated_certificate_params("revocation issuer", true);
1576        issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
1577        let issuer_certificate = issuer_params
1578            .signed_by(&issuer_key, &root)
1579            .expect("root should sign issuer certificate");
1580        let issuer = Issuer::new(issuer_params, issuer_key);
1581
1582        let mut leaf_params = generated_certificate_params("revoked signer", false);
1583        leaf_params.serial_number = Some(SerialNumber::from(42_u64));
1584        leaf_params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
1585        let leaf = leaf_params
1586            .signed_by(
1587                &KeyPair::generate().expect("leaf key generation should succeed"),
1588                &issuer,
1589            )
1590            .expect("issuer should sign leaf certificate");
1591        let crl = CertificateRevocationListParams {
1592            this_update: date_time_ymd(2026, 3, 15),
1593            next_update: date_time_ymd(2026, 4, 15),
1594            crl_number: SerialNumber::from(1_u64),
1595            issuing_distribution_point: None,
1596            revoked_certs: vec![RevokedCertParams {
1597                serial_number: SerialNumber::from(42_u64),
1598                revocation_time: date_time_ymd(2026, 3, 16),
1599                reason_code: None,
1600                invalidity_date: None,
1601            }],
1602            key_identifier_method: KeyIdMethod::Sha256,
1603        }
1604        .signed_by(&issuer)
1605        .expect("issuer should sign CRL");
1606        let mut info = x509_info(
1607            vec![
1608                leaf.der().to_vec(),
1609                issuer_certificate.der().to_vec(),
1610                root.der().to_vec(),
1611            ],
1612            0,
1613        );
1614        info.crls.push(crl.der().to_vec());
1615        (info, root.der().to_vec())
1616    }
1617
1618    fn x509_info(certificates: Vec<Vec<u8>>, signing_index: usize) -> X509DataInfo {
1619        let parsed_certificates = certificates
1620            .iter()
1621            .map(|certificate| {
1622                parse_x509_certificate(certificate)
1623                    .expect("generated certificate should have supported metadata")
1624            })
1625            .collect();
1626        X509DataInfo {
1627            certificates,
1628            parsed_certificates,
1629            certificate_chain: vec![signing_index],
1630            ..X509DataInfo::default()
1631        }
1632    }
1633
1634    #[test]
1635    fn defaults_match_key_resolution_policy() {
1636        // Defaults must remain compatible with xmlsec1's depth and opt-in trust policy.
1637        let config = KeyResolverConfig::default();
1638
1639        assert!(config.trusted_certs.is_empty());
1640        assert!(config.lookup_certs.is_empty());
1641        assert!(config.named_keys.is_empty());
1642        let trust = crate::policy::VerificationPolicy::default().key_trust;
1643        assert!(!trust.verify_x509_chains);
1644        assert!(!trust.check_crls);
1645        assert_eq!(trust.verification_time, None);
1646        assert_eq!(trust.max_x509_chain_depth, 9);
1647    }
1648
1649    #[test]
1650    fn verification_policy_controls_leaf_extended_key_usage() {
1651        // The immutable operation snapshot must reach certificate-path
1652        // validation; resolver-local trust defaults cannot bypass EKU policy.
1653        let root = rcgen::CertifiedIssuer::self_signed(
1654            generated_certificate_params("EKU policy root", true),
1655            rcgen::KeyPair::generate().expect("root key generation should succeed"),
1656        )
1657        .expect("root should be self-signable");
1658        let mut leaf_params = generated_certificate_params("TLS-only XML signer", false);
1659        leaf_params.key_usages = vec![rcgen::KeyUsagePurpose::DigitalSignature];
1660        leaf_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
1661        let leaf = leaf_params
1662            .signed_by(
1663                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
1664                &root,
1665            )
1666            .expect("root should sign leaf certificate");
1667        let key_info = KeyInfo {
1668            sources: vec![KeyInfoSource::X509Data(x509_info(
1669                vec![leaf.der().to_vec(), root.der().to_vec()],
1670                0,
1671            ))],
1672        };
1673        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1674            trusted_certs: vec![root.der().to_vec()],
1675            ..KeyResolverConfig::default()
1676        });
1677        let mut policy = crate::policy::VerificationPolicy::default();
1678        policy.key_trust.verify_x509_chains = true;
1679
1680        let error = match resolver.resolve_with_policy(
1681            Some(&key_info),
1682            SignatureAlgorithm::EcdsaSha256,
1683            &policy,
1684        ) {
1685            Ok(_) => panic!("unapproved restricted EKU must be rejected"),
1686            Err(error) => error,
1687        };
1688        assert!(matches!(
1689            error,
1690            DsigError::KeyResolution(KeyResolutionError::Chain(
1691                super::super::X509ChainError::InvalidKeyUsage {
1692                    position: 0,
1693                    required: "an approved extended key usage",
1694                }
1695            ))
1696        ));
1697
1698        policy.key_trust.allowed_extended_key_usages =
1699            std::collections::HashSet::from([crate::policy::ExtendedKeyPurpose::ServerAuth]);
1700        assert!(
1701            resolver
1702                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
1703                .expect("approved restricted EKU must pass path validation")
1704                .is_some()
1705        );
1706    }
1707
1708    #[test]
1709    fn operation_policy_rejects_zero_x509_resource_limits() {
1710        // X.509 work limits belong to the immutable operation snapshot and are
1711        // rejected before resolver-owned certificate material is inspected.
1712        for trust in [
1713            crate::policy::KeyTrustPolicy {
1714                verify_x509_chains: true,
1715                max_x509_chain_depth: 0,
1716                ..crate::policy::KeyTrustPolicy::default()
1717            },
1718            crate::policy::KeyTrustPolicy {
1719                verify_x509_chains: true,
1720                max_x509_candidate_paths: 0,
1721                ..crate::policy::KeyTrustPolicy::default()
1722            },
1723        ] {
1724            let certificate = certificate_der(RSA_4096_CERTIFICATE);
1725            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1726                trusted_certs: vec![certificate],
1727                ..KeyResolverConfig::default()
1728            });
1729            let policy = crate::policy::VerificationPolicy {
1730                key_trust: trust,
1731                ..crate::policy::VerificationPolicy::default()
1732            };
1733            let error = super::super::VerifyContext::new()
1734                .policy(policy)
1735                .key_resolver(&resolver)
1736                .verify(&x509_signature_with_leaf_subject())
1737                .expect_err("zero composed X.509 limits must fail as policy errors");
1738
1739            assert!(matches!(
1740                error,
1741                DsigError::Policy(crate::policy::PolicyViolation::InvalidResourceLimit {
1742                    requirement: "limit must be nonzero",
1743                    actual: 0,
1744                    ..
1745                })
1746            ));
1747        }
1748    }
1749
1750    #[test]
1751    fn operation_policy_rejects_crl_checking_without_chain_validation() {
1752        // CRL authentication is part of path validation. A resolver must not
1753        // accept a configuration that would silently skip the requested check.
1754        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1755            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
1756            ..KeyResolverConfig::default()
1757        });
1758        let policy = crate::policy::VerificationPolicy {
1759            key_trust: crate::policy::KeyTrustPolicy {
1760                check_crls: true,
1761                ..crate::policy::KeyTrustPolicy::default()
1762            },
1763            ..crate::policy::VerificationPolicy::default()
1764        };
1765        let error = super::super::VerifyContext::new()
1766            .policy(policy)
1767            .key_resolver(&resolver)
1768            .verify(&x509_signature_with_leaf_subject())
1769            .expect_err("CRL-only trust policy must fail before certificate use");
1770
1771        assert!(matches!(
1772            error,
1773            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
1774                reason: "CRL checking requires X.509 chain validation"
1775            })
1776        ));
1777    }
1778
1779    #[test]
1780    fn hmac_key_rejects_empty_secret_and_wrong_algorithm() {
1781        // HMAC secrets are caller-owned and cannot be reused as asymmetric keys.
1782        assert!(matches!(
1783            HmacSha1VerificationKey::new(Vec::new()),
1784            Err(KeyResolutionError::InvalidPublicKey)
1785        ));
1786        let key = HmacSha1VerificationKey::new(b"secret".to_vec())
1787            .expect("non-empty HMAC secret must be accepted");
1788        assert!(matches!(
1789            key.verify(SignatureAlgorithm::RsaSha256, b"data", b"signature"),
1790            Err(DsigError::KeyResolution(
1791                KeyResolutionError::AlgorithmMismatch
1792            ))
1793        ));
1794    }
1795
1796    #[test]
1797    fn hmac_key_uses_the_operation_policy_for_truncation() {
1798        // Output length belongs to SignatureMethod and operation policy, not
1799        // reusable secret key material. Legacy truncation therefore requires
1800        // an explicit compatibility policy even on the policy-aware key hook.
1801        let key = HmacVerificationKey::new(b"secret".to_vec())
1802            .expect("the fixture HMAC secret is non-empty");
1803        let mut mac = hmac::Hmac::<sha1::Sha1>::new_from_slice(b"secret")
1804            .expect("HMAC accepts an arbitrary non-empty secret");
1805        mac.update(b"data");
1806        let expected = mac.finalize().into_bytes();
1807
1808        let policy = crate::policy::VerificationPolicy {
1809            hmac: crate::policy::HmacPolicy {
1810                minimum_key_bits: 40,
1811                minimum_output_bits: 80,
1812            },
1813            ..crate::policy::VerificationPolicy::default()
1814        };
1815        assert!(
1816            key.verify_with_policy(
1817                &policy,
1818                SignatureAlgorithm::HmacSha1,
1819                b"data",
1820                &expected[..10],
1821            )
1822            .expect("the compatibility policy and algorithm match")
1823        );
1824        assert!(
1825            !key.verify_with_policy(&policy, SignatureAlgorithm::HmacSha1, b"data", &[0_u8; 10],)
1826                .expect("a mismatched truncated MAC must be rejected")
1827        );
1828    }
1829
1830    #[test]
1831    fn hmac_key_direct_api_rejects_attacker_selected_short_output() {
1832        // The policy-free trait method applies secure defaults; signature bytes
1833        // cannot act as their own one-byte truncation declaration.
1834        let key = HmacVerificationKey::new([0x42; 16]).expect("fixed HMAC key must parse");
1835        let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(&[0x42; 16])
1836            .expect("HMAC accepts the fixed secret");
1837        mac.update(b"data");
1838        let expected = mac.finalize().into_bytes();
1839
1840        assert!(matches!(
1841            key.verify(SignatureAlgorithm::HmacSha256, b"data", &expected[..1]),
1842            Err(DsigError::Policy(
1843                crate::policy::PolicyViolation::HmacOutputLength {
1844                    minimum: 128,
1845                    maximum: 256,
1846                    actual: 8,
1847                }
1848            ))
1849        ));
1850    }
1851
1852    #[test]
1853    fn hmac_key_debug_redacts_secret_material() {
1854        // Debug output may expose public verification parameters, never caller secrets.
1855        let secret = b"unique-debug-secret-marker";
1856        let key = HmacVerificationKey::new(secret.to_vec())
1857            .expect("the fixture HMAC secret is non-empty");
1858
1859        let debug = format!("{key:?}");
1860        assert!(
1861            !debug
1862                .contains(std::str::from_utf8(secret).expect("the debug marker is literal ASCII"))
1863        );
1864        assert!(!debug.contains(&format!("{secret:?}")));
1865        assert!(!debug.contains("output_length_bits"));
1866    }
1867
1868    #[test]
1869    fn stores_named_verification_key_metadata() {
1870        // Named resolution must retain every field needed by the later resolver wiring.
1871        let key = VerificationKey {
1872            algorithm: SignatureAlgorithm::RsaSha256,
1873            public_key_bytes: vec![1, 2, 3],
1874            certificate_der: Some(vec![4, 5, 6]),
1875            name: Some("idp-signing".into()),
1876        };
1877        let mut config = KeyResolverConfig::default();
1878        config.named_keys.insert("idp-signing".into(), key.clone());
1879
1880        assert_eq!(config.named_keys.get("idp-signing"), Some(&key));
1881    }
1882
1883    #[test]
1884    fn resolves_embedded_certificate_end_to_end() {
1885        // The default resolver must make parsed X509Data usable by VerifyContext.
1886        let resolver = DefaultKeyResolver::default();
1887        let result = super::super::VerifyContext::new()
1888            .key_resolver(&resolver)
1889            .verify(SIGNED_SAML)
1890            .expect("embedded certificate should resolve");
1891
1892        assert_eq!(result.status, super::super::DsigStatus::Valid);
1893    }
1894
1895    #[test]
1896    fn resolves_x509_digest_from_configured_certificates() {
1897        // Selector-only X509Data must locate the signing certificate without
1898        // embedding key material or supplying a preset verification key.
1899        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
1900        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1901            lookup_certs: vec![leaf_certificate_der],
1902            trusted_certs: vec![
1903                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
1904                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
1905            ],
1906            ..KeyResolverConfig::default()
1907        });
1908        for signature in [X509_DIGEST_SHA256_SIGNATURE, X509_DIGEST_SIGNATURE] {
1909            let result = super::super::VerifyContext::new()
1910                .key_resolver(&resolver)
1911                .verify(signature)
1912                .expect("X509Digest should resolve a configured certificate");
1913
1914            assert_eq!(result.status, super::super::DsigStatus::Valid);
1915        }
1916    }
1917
1918    #[test]
1919    fn selector_resolved_certificate_obeys_chain_policy() {
1920        // Enabling chain verification must apply validity policy even when
1921        // X509Data contains only selectors and the matching cert is configured.
1922        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
1923        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1924            lookup_certs: vec![leaf_certificate_der],
1925            trusted_certs: vec![
1926                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
1927                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
1928            ],
1929            ..KeyResolverConfig::default()
1930        });
1931        let error = super::super::VerifyContext::new()
1932            .policy(verification_policy_with_trust(chain_policy_at(
1933                SystemTime::UNIX_EPOCH,
1934            )))
1935            .key_resolver(&resolver)
1936            .verify(&x509_signature_with_leaf_subject())
1937            .expect_err("selector-resolved certificate must satisfy chain policy");
1938
1939        assert!(
1940            matches!(
1941                &error,
1942                DsigError::KeyResolution(KeyResolutionError::Chain(
1943                    super::super::X509ChainError::CertificateNotValid(_)
1944                ))
1945            ),
1946            "unexpected selector policy error: {error:?}"
1947        );
1948    }
1949
1950    #[test]
1951    fn selector_resolved_configured_root_remains_a_trust_anchor() {
1952        // A certificate explicitly configured in trusted_certs remains an
1953        // anchor when X509Data selects it by subject instead of embedding it.
1954        let mut params = rcgen::CertificateParams::new(Vec::new())
1955            .expect("empty SAN list should produce valid certificate parameters");
1956        params
1957            .distinguished_name
1958            .push(rcgen::DnType::CommonName, "configured root");
1959        params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1960        let key_pair = rcgen::KeyPair::generate().expect("test key generation should succeed");
1961        let certificate = params
1962            .self_signed(&key_pair)
1963            .expect("test root should be self-signable");
1964        let certificate_der = certificate.der().to_vec();
1965        let key_info_xml = concat!(
1966            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
1967            "<X509Data><X509SubjectName>CN=configured root</X509SubjectName></X509Data>",
1968            "</KeyInfo>"
1969        );
1970        let document = roxmltree::Document::parse(key_info_xml)
1971            .expect("static selector KeyInfo should parse as XML");
1972        let key_info = super::super::parse_key_info(document.root_element())
1973            .expect("static selector KeyInfo should satisfy XMLDSig structure");
1974        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
1975            trusted_certs: vec![certificate_der],
1976            ..KeyResolverConfig::default()
1977        });
1978
1979        let resolved = resolver
1980            .resolve_with_policy(
1981                Some(&key_info),
1982                SignatureAlgorithm::EcdsaSha256,
1983                &verification_policy_with_trust(chain_policy()),
1984            )
1985            .expect("configured self-signed certificate should validate as its own anchor");
1986
1987        assert!(resolved.is_some());
1988    }
1989
1990    #[test]
1991    fn selector_resolved_non_self_signed_trust_anchor_terminates_the_path() {
1992        // Trust is assigned to the exact configured certificate, not inferred
1993        // from self-signing. A lookup-only issuer must not extend that anchor
1994        // into a new path that requires another trust decision.
1995        let mut issuer_params = rcgen::CertificateParams::new(Vec::new())
1996            .expect("empty issuer SAN list should be valid");
1997        issuer_params
1998            .distinguished_name
1999            .push(rcgen::DnType::CommonName, "lookup-only issuer");
2000        issuer_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2001        issuer_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2002        let issuer = rcgen::CertifiedIssuer::self_signed(
2003            issuer_params,
2004            rcgen::KeyPair::generate().expect("issuer key generation should succeed"),
2005        )
2006        .expect("issuer certificate should be self-signable");
2007
2008        let mut anchor_params = rcgen::CertificateParams::new(Vec::new())
2009            .expect("empty anchor SAN list should be valid");
2010        anchor_params
2011            .distinguished_name
2012            .push(rcgen::DnType::CommonName, "direct trust anchor");
2013        let anchor = anchor_params
2014            .signed_by(
2015                &rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
2016                &issuer,
2017            )
2018            .expect("issuer should sign the directly trusted certificate");
2019        let key_info_xml = concat!(
2020            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2021            "<X509Data><X509SubjectName>CN=direct trust anchor</X509SubjectName></X509Data>",
2022            "</KeyInfo>"
2023        );
2024        let document = roxmltree::Document::parse(key_info_xml)
2025            .expect("static selector KeyInfo should parse as XML");
2026        let key_info = super::super::parse_key_info(document.root_element())
2027            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2028        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2029            trusted_certs: vec![anchor.der().to_vec()],
2030            lookup_certs: vec![issuer.der().to_vec()],
2031            ..KeyResolverConfig::default()
2032        });
2033
2034        let resolved = resolver
2035            .resolve_with_policy(
2036                Some(&key_info),
2037                SignatureAlgorithm::EcdsaSha256,
2038                &verification_policy_with_trust(chain_policy()),
2039            )
2040            .expect("an explicitly trusted selected certificate must terminate its path");
2041
2042        assert!(resolved.is_some());
2043    }
2044
2045    #[test]
2046    fn selector_resolved_leaf_stops_at_non_self_signed_trust_anchor() {
2047        // A configured anchor terminates trust even when a lookup certificate
2048        // could continue the issuer-name chain beyond it.
2049        let external_issuer = rcgen::CertifiedIssuer::self_signed(
2050            generated_certificate_params("external issuer", true),
2051            rcgen::KeyPair::generate().expect("external issuer key generation should succeed"),
2052        )
2053        .expect("external issuer should be self-signable");
2054        let anchor = rcgen::CertifiedIssuer::signed_by(
2055            generated_certificate_params("non-self-signed anchor", true),
2056            rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
2057            &external_issuer,
2058        )
2059        .expect("external issuer should sign the anchor");
2060        let leaf = generated_certificate_params("anchor leaf", false)
2061            .signed_by(
2062                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2063                &anchor,
2064            )
2065            .expect("anchor should sign the leaf");
2066        let leaf_metadata = parse_x509_certificate(leaf.der())
2067            .expect("generated leaf should have supported metadata");
2068        let key_info = KeyInfo {
2069            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2070                subject_names: vec![leaf_metadata.subject_dn],
2071                ..X509DataInfo::default()
2072            })],
2073        };
2074        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2075            trusted_certs: vec![anchor.der().to_vec()],
2076            lookup_certs: vec![leaf.der().to_vec(), external_issuer.der().to_vec()],
2077            ..KeyResolverConfig::default()
2078        });
2079
2080        let resolved = resolver
2081            .resolve_with_policy(
2082                Some(&key_info),
2083                SignatureAlgorithm::EcdsaSha256,
2084                &verification_policy_with_trust(chain_policy()),
2085            )
2086            .expect("path construction must stop at the configured anchor");
2087
2088        assert!(resolved.is_some());
2089    }
2090
2091    #[test]
2092    fn selector_resolved_leaf_does_not_anchor_itself() {
2093        // A certificate available for selector lookup is not automatically a
2094        // trust anchor; chain verification still requires a separate issuer.
2095        let certificate_der = certificate_der(RSA_4096_CERTIFICATE);
2096        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2097            lookup_certs: vec![certificate_der],
2098            ..KeyResolverConfig::default()
2099        });
2100        let error = super::super::VerifyContext::new()
2101            .policy(verification_policy_with_trust(chain_policy_at(
2102                fixture_certificate_time(),
2103            )))
2104            .key_resolver(&resolver)
2105            .verify(&x509_signature_with_leaf_subject())
2106            .expect_err("selector-resolved leaf must not trust itself");
2107
2108        assert!(matches!(
2109            error,
2110            DsigError::KeyResolution(KeyResolutionError::Chain(
2111                super::super::X509ChainError::UntrustedRoot
2112            ))
2113        ));
2114    }
2115
2116    #[test]
2117    fn selector_resolved_leaf_uses_separate_anchor() {
2118        // Selector lookup may use the leaf from the configured set, but chain
2119        // verification must terminate at a different configured certificate.
2120        let leaf = certificate_der(RSA_4096_CERTIFICATE);
2121        let issuer = certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
2122        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2123            lookup_certs: vec![leaf],
2124            trusted_certs: vec![issuer],
2125            ..KeyResolverConfig::default()
2126        });
2127        let result = super::super::VerifyContext::new()
2128            .policy(verification_policy_with_trust(chain_policy_at(
2129                fixture_certificate_time(),
2130            )))
2131            .key_resolver(&resolver)
2132            .verify(&x509_signature_with_leaf_subject())
2133            .expect("selector-resolved leaf should chain to its configured issuer");
2134
2135        assert_eq!(result.status, super::super::DsigStatus::Valid);
2136    }
2137
2138    #[test]
2139    fn selector_resolved_leaf_uses_lookup_intermediate() {
2140        // Lookup certificates may complete an untrusted path, but only the
2141        // separately configured root is allowed to establish trust.
2142        let mut root_params =
2143            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
2144        root_params
2145            .distinguished_name
2146            .push(rcgen::DnType::CommonName, "lookup root");
2147        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2148        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2149        let root = rcgen::CertifiedIssuer::self_signed(
2150            root_params,
2151            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2152        )
2153        .expect("root certificate should be self-signable");
2154
2155        let mut intermediate_params = rcgen::CertificateParams::new(Vec::new())
2156            .expect("empty intermediate SAN list should be valid");
2157        intermediate_params
2158            .distinguished_name
2159            .push(rcgen::DnType::CommonName, "lookup intermediate");
2160        intermediate_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2161        intermediate_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2162        let intermediate = rcgen::CertifiedIssuer::signed_by(
2163            intermediate_params,
2164            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2165            &root,
2166        )
2167        .expect("root should sign the intermediate certificate");
2168
2169        let mut leaf_params =
2170            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
2171        leaf_params
2172            .distinguished_name
2173            .push(rcgen::DnType::CommonName, "lookup leaf");
2174        let leaf = leaf_params
2175            .signed_by(
2176                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2177                &intermediate,
2178            )
2179            .expect("intermediate should sign the leaf certificate");
2180        let key_info_xml = concat!(
2181            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2182            "<X509Data><X509SubjectName>CN=lookup leaf</X509SubjectName></X509Data>",
2183            "</KeyInfo>"
2184        );
2185        let document = roxmltree::Document::parse(key_info_xml)
2186            .expect("static selector KeyInfo should parse as XML");
2187        let key_info = super::super::parse_key_info(document.root_element())
2188            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2189        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2190            lookup_certs: vec![leaf.der().to_vec(), intermediate.der().to_vec()],
2191            trusted_certs: vec![root.der().to_vec()],
2192            ..KeyResolverConfig::default()
2193        });
2194
2195        let resolved = resolver
2196            .resolve_with_policy(
2197                Some(&key_info),
2198                SignatureAlgorithm::EcdsaSha256,
2199                &verification_policy_with_trust(chain_policy()),
2200            )
2201            .expect("selector-resolved leaf should chain through the lookup intermediate");
2202
2203        assert!(resolved.is_some());
2204    }
2205
2206    #[test]
2207    fn x509_path_signatures_use_the_operation_provider() {
2208        // Embedded and selector-resolved certificates converge on the same
2209        // path validator. Neither source may fall back to a crate-global
2210        // verifier when the operation provider rejects certificate signatures.
2211        let root = rcgen::CertifiedIssuer::self_signed(
2212            generated_certificate_params("provider root", true),
2213            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2214        )
2215        .expect("root should be self-signable");
2216        let leaf = generated_certificate_params("provider leaf", false)
2217            .signed_by(
2218                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2219                &root,
2220            )
2221            .expect("root should sign the leaf");
2222        let leaf_der = leaf.der().to_vec();
2223        let leaf_metadata =
2224            parse_x509_certificate(&leaf_der).expect("generated leaf metadata should parse");
2225        let policy = crate::policy::VerificationPolicy {
2226            key_trust: chain_policy(),
2227            ..crate::policy::VerificationPolicy::default()
2228        };
2229
2230        let cases = [
2231            (
2232                KeyInfo {
2233                    sources: vec![KeyInfoSource::X509Data(x509_info(
2234                        vec![leaf_der.clone()],
2235                        0,
2236                    ))],
2237                },
2238                Vec::new(),
2239            ),
2240            (
2241                KeyInfo {
2242                    sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2243                        subject_names: vec![leaf_metadata.subject_dn],
2244                        ..X509DataInfo::default()
2245                    })],
2246                },
2247                vec![leaf_der],
2248            ),
2249        ];
2250
2251        for (key_info, lookup_certs) in cases {
2252            let provider = RejectSecondSha512Provider {
2253                sha512_calls: AtomicUsize::new(0),
2254                verification_calls: AtomicUsize::new(0),
2255                reject_verification_call: Some(0),
2256                rejected_verification_data: None,
2257            };
2258            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2259                trusted_certs: vec![root.der().to_vec()],
2260                lookup_certs,
2261                ..KeyResolverConfig::default()
2262            });
2263            let error = match resolver.resolve_with_policy_and_provider(
2264                Some(&key_info),
2265                SignatureAlgorithm::EcdsaSha256,
2266                &policy,
2267                &provider,
2268            ) {
2269                Ok(_) => panic!("the operation provider must gate every X.509 path signature"),
2270                Err(error) => error,
2271            };
2272
2273            assert!(matches!(
2274                error,
2275                DsigError::KeyResolution(KeyResolutionError::Chain(
2276                    super::super::X509ChainError::UnsupportedSignatureAlgorithm { ref oid }
2277                )) if oid == "1.2.840.10045.4.3.2"
2278            ));
2279            assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 1);
2280        }
2281
2282        // A provider rejection after path construction proves complete-path
2283        // validation does not switch back to the crate-global provider.
2284        let provider = RejectSecondSha512Provider {
2285            sha512_calls: AtomicUsize::new(0),
2286            verification_calls: AtomicUsize::new(0),
2287            reject_verification_call: Some(1),
2288            rejected_verification_data: None,
2289        };
2290        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2291            trusted_certs: vec![root.der().to_vec()],
2292            ..KeyResolverConfig::default()
2293        });
2294        let key_info = KeyInfo {
2295            sources: vec![KeyInfoSource::X509Data(x509_info(
2296                vec![leaf.der().to_vec()],
2297                0,
2298            ))],
2299        };
2300        let error = match resolver.resolve_with_policy_and_provider(
2301            Some(&key_info),
2302            SignatureAlgorithm::EcdsaSha256,
2303            &policy,
2304            &provider,
2305        ) {
2306            Ok(_) => panic!("complete-path validation must retain the operation provider"),
2307            Err(error) => error,
2308        };
2309        assert!(matches!(
2310            error,
2311            DsigError::KeyResolution(KeyResolutionError::Chain(
2312                super::super::X509ChainError::Provider(_)
2313            ))
2314        ));
2315        assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 2);
2316    }
2317
2318    #[test]
2319    fn embedded_leaf_uses_lookup_intermediate_with_duplicate_anchor() {
2320        // Deduplicating repeated trust anchors must not shift an untrusted
2321        // lookup intermediate into the trusted prefix used by path building.
2322        let trusted_root = rcgen::CertifiedIssuer::self_signed(
2323            generated_certificate_params("unrelated trusted root", true),
2324            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2325        )
2326        .expect("root should be self-signable");
2327        let issuer_root = rcgen::CertifiedIssuer::self_signed(
2328            generated_certificate_params("untrusted issuer root", true),
2329            rcgen::KeyPair::generate().expect("issuer root key generation should succeed"),
2330        )
2331        .expect("issuer root should be self-signable");
2332        let intermediate = rcgen::CertifiedIssuer::signed_by(
2333            generated_certificate_params("embedded intermediate", true),
2334            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2335            &issuer_root,
2336        )
2337        .expect("issuer root should sign the intermediate");
2338        let leaf = generated_certificate_params("embedded leaf", false)
2339            .signed_by(
2340                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2341                &intermediate,
2342            )
2343            .expect("intermediate should sign the leaf");
2344        let key_info = KeyInfo {
2345            sources: vec![KeyInfoSource::X509Data(x509_info(
2346                vec![leaf.der().to_vec()],
2347                0,
2348            ))],
2349        };
2350        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2351            lookup_certs: vec![intermediate.der().to_vec()],
2352            trusted_certs: vec![trusted_root.der().to_vec(), trusted_root.der().to_vec()],
2353            ..KeyResolverConfig::default()
2354        });
2355
2356        let policy = verification_policy_with_trust(chain_policy());
2357        let error = match resolver.resolve_with_policy(
2358            Some(&key_info),
2359            SignatureAlgorithm::EcdsaSha256,
2360            &policy,
2361        ) {
2362            Ok(_) => panic!("an untrusted lookup intermediate must not become a trust anchor"),
2363            Err(error) => error,
2364        };
2365
2366        assert!(matches!(
2367            error,
2368            DsigError::KeyResolution(KeyResolutionError::Chain(
2369                super::super::X509ChainError::UntrustedRoot
2370            ))
2371        ));
2372    }
2373
2374    #[test]
2375    fn selector_resolved_leaf_chooses_unique_valid_same_key_path() {
2376        // Cross-signing can produce issuer certificates with the same subject
2377        // and public key. Trust policy, not the immediate signature edge, must
2378        // select the sole path that reaches a configured anchor.
2379        let trusted_root = rcgen::CertifiedIssuer::self_signed(
2380            generated_certificate_params("trusted cross-sign root", true),
2381            rcgen::KeyPair::generate().expect("trusted root key generation should succeed"),
2382        )
2383        .expect("trusted root should be self-signable");
2384        let untrusted_root = rcgen::CertifiedIssuer::self_signed(
2385            generated_certificate_params("untrusted cross-sign root", true),
2386            rcgen::KeyPair::generate().expect("untrusted root key generation should succeed"),
2387        )
2388        .expect("untrusted root should be self-signable");
2389        let shared_params = generated_certificate_params("shared cross-sign issuer", true);
2390        let shared_key =
2391            rcgen::KeyPair::generate().expect("shared issuer key generation should succeed");
2392        let trusted_intermediate = shared_params
2393            .signed_by(&shared_key, &trusted_root)
2394            .expect("trusted root should cross-sign the shared issuer key");
2395        let untrusted_intermediate = shared_params
2396            .signed_by(&shared_key, &untrusted_root)
2397            .expect("untrusted root should cross-sign the shared issuer key");
2398        let shared_issuer = rcgen::Issuer::from_params(&shared_params, &shared_key);
2399        let leaf = generated_certificate_params("cross-signed leaf", false)
2400            .signed_by(
2401                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2402                &shared_issuer,
2403            )
2404            .expect("shared issuer key should sign the leaf");
2405        let leaf_metadata = parse_x509_certificate(leaf.der())
2406            .expect("generated leaf should have supported metadata");
2407        let key_info = KeyInfo {
2408            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2409                subject_names: vec![leaf_metadata.subject_dn],
2410                ..X509DataInfo::default()
2411            })],
2412        };
2413        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2414            trusted_certs: vec![trusted_root.der().to_vec()],
2415            lookup_certs: vec![
2416                leaf.der().to_vec(),
2417                untrusted_intermediate.der().to_vec(),
2418                trusted_intermediate.der().to_vec(),
2419                untrusted_root.der().to_vec(),
2420            ],
2421            ..KeyResolverConfig::default()
2422        });
2423
2424        let resolved = resolver
2425            .resolve_with_policy(
2426                Some(&key_info),
2427                SignatureAlgorithm::EcdsaSha256,
2428                &verification_policy_with_trust(chain_policy()),
2429            )
2430            .expect("the sole path to a configured anchor should be selected");
2431
2432        assert!(resolved.is_some());
2433    }
2434
2435    #[test]
2436    fn self_issued_rollover_continues_to_same_name_trusted_signer() {
2437        // Subject/issuer name equality does not prove self-signing: rollover
2438        // certificates may be issued by a distinct same-name trust anchor.
2439        let root = rcgen::CertifiedIssuer::self_signed(
2440            generated_certificate_params("rollover authority", true),
2441            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2442        )
2443        .expect("root should be self-signable");
2444        let rollover_params = generated_certificate_params("rollover authority", true);
2445        let rollover_key =
2446            rcgen::KeyPair::generate().expect("rollover key generation should succeed");
2447        let rollover_certificate = rollover_params
2448            .signed_by(&rollover_key, &root)
2449            .expect("root should sign the same-name rollover certificate");
2450        let rollover_issuer = rcgen::Issuer::from_params(&rollover_params, &rollover_key);
2451        let leaf = generated_certificate_params("rollover leaf", false)
2452            .signed_by(
2453                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2454                &rollover_issuer,
2455            )
2456            .expect("rollover key should sign the leaf");
2457        let leaf_metadata =
2458            parse_x509_certificate(leaf.der()).expect("generated leaf metadata should parse");
2459        let key_info = KeyInfo {
2460            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2461                subject_names: vec![leaf_metadata.subject_dn],
2462                ..X509DataInfo::default()
2463            })],
2464        };
2465        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2466            trusted_certs: vec![root.der().to_vec()],
2467            lookup_certs: vec![leaf.der().to_vec(), rollover_certificate.der().to_vec()],
2468            ..KeyResolverConfig::default()
2469        });
2470
2471        let resolved = resolver
2472            .resolve_with_policy(
2473                Some(&key_info),
2474                SignatureAlgorithm::EcdsaSha256,
2475                &verification_policy_with_trust(chain_policy()),
2476            )
2477            .expect("same-name rollover path must reach its configured signer");
2478
2479        assert!(resolved.is_some());
2480    }
2481
2482    #[test]
2483    fn x509_candidate_limit_counts_generated_partial_paths() {
2484        // A narrow DFS frontier can still generate unbounded partial paths over
2485        // time, so the resource limit must account for every generated state.
2486        let root = rcgen::CertifiedIssuer::self_signed(
2487            generated_certificate_params("candidate root", true),
2488            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2489        )
2490        .expect("root should be self-signable");
2491        let intermediate = rcgen::CertifiedIssuer::signed_by(
2492            generated_certificate_params("candidate intermediate", true),
2493            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2494            &root,
2495        )
2496        .expect("root should sign the intermediate");
2497        let leaf = generated_certificate_params("candidate leaf", false)
2498            .signed_by(
2499                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2500                &intermediate,
2501            )
2502            .expect("intermediate should sign the leaf");
2503        let info = x509_info(
2504            vec![
2505                root.der().to_vec(),
2506                intermediate.der().to_vec(),
2507                leaf.der().to_vec(),
2508            ],
2509            2,
2510        );
2511
2512        assert!(matches!(
2513            build_x509_certificate_paths_to_trusted_prefix(
2514                &info,
2515                2,
2516                1,
2517                9,
2518                2,
2519                crate::provider::default_provider(),
2520            ),
2521            Err(X509ChainBuildError::AmbiguousIssuer)
2522        ));
2523    }
2524
2525    #[test]
2526    fn selector_resolved_leaf_disambiguates_same_subject_issuers_by_signature() {
2527        // Certificate renewal may leave multiple configured intermediates with
2528        // the same subject DN. The leaf signature, not pool order, identifies
2529        // the one issuer that belongs to the verification path.
2530        let mut root_params =
2531            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
2532        root_params
2533            .distinguished_name
2534            .push(rcgen::DnType::CommonName, "shared-issuer root");
2535        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2536        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2537        let root = rcgen::CertifiedIssuer::self_signed(
2538            root_params,
2539            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2540        )
2541        .expect("root certificate should be self-signable");
2542
2543        let intermediate = |key: rcgen::KeyPair| {
2544            let mut params = rcgen::CertificateParams::new(Vec::new())
2545                .expect("empty intermediate SAN list should be valid");
2546            params
2547                .distinguished_name
2548                .push(rcgen::DnType::CommonName, "renewed intermediate");
2549            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2550            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2551            rcgen::CertifiedIssuer::signed_by(params, key, &root)
2552                .expect("root should sign the intermediate certificate")
2553        };
2554        let unrelated_intermediate = intermediate(
2555            rcgen::KeyPair::generate().expect("unrelated intermediate key generation should work"),
2556        );
2557        let signing_intermediate = intermediate(
2558            rcgen::KeyPair::generate().expect("signing intermediate key generation should work"),
2559        );
2560
2561        let mut leaf_params =
2562            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
2563        leaf_params
2564            .distinguished_name
2565            .push(rcgen::DnType::CommonName, "same-subject leaf");
2566        let leaf = leaf_params
2567            .signed_by(
2568                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2569                &signing_intermediate,
2570            )
2571            .expect("the selected intermediate should sign the leaf certificate");
2572        let key_info_xml = concat!(
2573            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2574            "<X509Data><X509SubjectName>CN=same-subject leaf</X509SubjectName></X509Data>",
2575            "</KeyInfo>"
2576        );
2577        let document = roxmltree::Document::parse(key_info_xml)
2578            .expect("static selector KeyInfo should parse as XML");
2579        let key_info = super::super::parse_key_info(document.root_element())
2580            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2581        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2582            lookup_certs: vec![
2583                leaf.der().to_vec(),
2584                unrelated_intermediate.der().to_vec(),
2585                signing_intermediate.der().to_vec(),
2586            ],
2587            trusted_certs: vec![root.der().to_vec()],
2588            ..KeyResolverConfig::default()
2589        });
2590
2591        let resolved = resolver
2592            .resolve_with_policy(
2593                Some(&key_info),
2594                SignatureAlgorithm::EcdsaSha256,
2595                &verification_policy_with_trust(chain_policy()),
2596            )
2597            .expect("the leaf signature should select its unique same-subject issuer");
2598
2599        assert!(resolved.is_some());
2600    }
2601
2602    #[test]
2603    fn x509_path_builder_skips_branch_local_unsupported_algorithms() {
2604        // An untrusted intermediate can share both the subject and public key
2605        // of the valid path while using an unsupported signature algorithm on
2606        // its own parent edge. That branch must not suppress the valid path.
2607        let root = rcgen::CertifiedIssuer::self_signed(
2608            generated_certificate_params("unsupported-edge root", true),
2609            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2610        )
2611        .expect("root certificate should be self-signable");
2612        let signing_intermediate = rcgen::CertifiedIssuer::signed_by(
2613            generated_certificate_params("shared unsupported-edge issuer", true),
2614            rcgen::KeyPair::generate().expect("signing issuer key generation should succeed"),
2615            &root,
2616        )
2617        .expect("root should sign the intermediate certificate");
2618        let key_unsupported_intermediate = rcgen::CertifiedIssuer::signed_by(
2619            generated_certificate_params("shared unsupported-edge issuer", true),
2620            rcgen::KeyPair::generate().expect("unsupported issuer key generation should succeed"),
2621            &root,
2622        )
2623        .expect("root should sign the alternate intermediate certificate");
2624        let leaf = generated_certificate_params("unsupported-edge leaf", false)
2625            .signed_by(
2626                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2627                &signing_intermediate,
2628            )
2629            .expect("signing intermediate should sign the leaf");
2630
2631        let ordered = x509_info(
2632            vec![
2633                leaf.der().to_vec(),
2634                key_unsupported_intermediate.der().to_vec(),
2635                signing_intermediate.der().to_vec(),
2636                root.der().to_vec(),
2637            ],
2638            0,
2639        );
2640        let key_selective_provider = RejectSecondSha512Provider {
2641            sha512_calls: AtomicUsize::new(0),
2642            verification_calls: AtomicUsize::new(0),
2643            reject_verification_call: Some(0),
2644            rejected_verification_data: None,
2645        };
2646        assert_eq!(
2647            super::super::parse::build_x509_certificate_chain_from(
2648                &ordered,
2649                0,
2650                &key_selective_provider,
2651            )
2652            .expect("one unsupported issuer key must not suppress a usable candidate"),
2653            vec![0, 2, 3]
2654        );
2655
2656        let anchored_same_edge = x509_info(
2657            vec![
2658                root.der().to_vec(),
2659                leaf.der().to_vec(),
2660                key_unsupported_intermediate.der().to_vec(),
2661                signing_intermediate.der().to_vec(),
2662            ],
2663            1,
2664        );
2665        let first_candidate_unsupported = RejectSecondSha512Provider {
2666            sha512_calls: AtomicUsize::new(0),
2667            verification_calls: AtomicUsize::new(0),
2668            reject_verification_call: Some(0),
2669            rejected_verification_data: None,
2670        };
2671        assert_eq!(
2672            build_x509_certificate_paths_to_trusted_prefix(
2673                &anchored_same_edge,
2674                1,
2675                1,
2676                4,
2677                8,
2678                &first_candidate_unsupported,
2679            )
2680            .expect("a later same-DN issuer must survive an earlier provider capability miss"),
2681            vec![vec![1, 3, 0]]
2682        );
2683
2684        let mut unsupported_intermediate = signing_intermediate.der().to_vec();
2685        let ecdsa_sha256_oid = [0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
2686        let offsets = unsupported_intermediate
2687            .windows(ecdsa_sha256_oid.len())
2688            .enumerate()
2689            .filter_map(|(offset, window)| (window == ecdsa_sha256_oid).then_some(offset))
2690            .collect::<Vec<_>>();
2691        assert_eq!(
2692            offsets.len(),
2693            2,
2694            "certificate must repeat its signature OID"
2695        );
2696        for offset in offsets {
2697            unsupported_intermediate[offset + ecdsa_sha256_oid.len() - 1] = 0x05;
2698        }
2699
2700        let anchored = x509_info(
2701            vec![
2702                root.der().to_vec(),
2703                leaf.der().to_vec(),
2704                signing_intermediate.der().to_vec(),
2705                unsupported_intermediate,
2706            ],
2707            1,
2708        );
2709        assert_eq!(
2710            build_x509_certificate_paths_to_trusted_prefix(
2711                &anchored,
2712                1,
2713                1,
2714                4,
2715                8,
2716                crate::provider::default_provider(),
2717            )
2718            .expect("a branch-local provider gap must not abort path enumeration"),
2719            vec![vec![1, 2, 0]]
2720        );
2721
2722        let unsupported_only = x509_info(
2723            vec![
2724                root.der().to_vec(),
2725                leaf.der().to_vec(),
2726                anchored.certificates[3].clone(),
2727            ],
2728            1,
2729        );
2730        assert!(matches!(
2731            build_x509_certificate_paths_to_trusted_prefix(
2732                &unsupported_only,
2733                1,
2734                1,
2735                4,
2736                8,
2737                crate::provider::default_provider(),
2738            ),
2739            Err(X509ChainBuildError::UnsupportedSignatureAlgorithm { ref oid })
2740                if oid == "1.2.840.10045.4.3.5"
2741        ));
2742    }
2743
2744    #[test]
2745    fn selector_resolved_certificate_preserves_supplied_crls() {
2746        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>";
2747        let crl = crl_der(include_str!(
2748            "../../tests/fixtures/keys/rsa/rsa-2048-cert-revoked-crl.pem"
2749        ));
2750        let (_, parsed_crl) =
2751            x509_parser::revocation_list::CertificateRevocationList::from_der(&crl)
2752                .expect("tracked CRL must parse");
2753        let crl_signed_data = parsed_crl.tbs_cert_list.as_ref().to_vec();
2754        let xml = replace_unprefixed_key_info(
2755            RSA_KEY_VALUE_SIGNATURE,
2756            &selector.replace("CRL_PLACEHOLDER", &STANDARD.encode(&crl)),
2757        );
2758        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2759            lookup_certs: vec![certificate_der(include_str!(
2760                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2761            ))],
2762            trusted_certs: vec![
2763                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2764                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
2765            ],
2766            ..KeyResolverConfig::default()
2767        });
2768        let policy = verification_policy_with_trust(crate::policy::KeyTrustPolicy {
2769            check_crls: true,
2770            max_x509_chain_depth: 3,
2771            ..chain_policy_at(
2772                SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_773_964_800),
2773            )
2774        });
2775
2776        let error = super::super::VerifyContext::new()
2777            .policy(policy.clone())
2778            .key_resolver(&resolver)
2779            .verify(&xml)
2780            .expect_err("selector lookup must retain and enforce the supplied CRL");
2781        // RFC 10007 section 4: the tracked v3 CA lacks KeyUsage, so CRL
2782        // authorization now fails before its revoked-serial list is consumed.
2783        // https://www.rfc-editor.org/rfc/rfc10007.html#section-4
2784        assert!(matches!(
2785            error,
2786            DsigError::KeyResolution(KeyResolutionError::Chain(
2787                super::super::X509ChainError::InvalidKeyUsage {
2788                    position: 1,
2789                    required: "cRLSign"
2790                }
2791            ))
2792        ));
2793
2794        // Match the exact TBSCertList bytes so earlier certificate-edge
2795        // verification succeeds and the provider rejection occurs at CRL
2796        // authentication itself.
2797        let provider = RejectSecondSha512Provider {
2798            sha512_calls: AtomicUsize::new(0),
2799            verification_calls: AtomicUsize::new(0),
2800            reject_verification_call: None,
2801            rejected_verification_data: Some(crl_signed_data),
2802        };
2803        let error = super::super::VerifyContext::new()
2804            .policy(policy)
2805            .key_resolver(&resolver)
2806            .provider(&provider)
2807            .verify(&xml)
2808            .expect_err("CRL authentication must retain the operation provider");
2809        assert!(matches!(
2810            error,
2811            DsigError::KeyResolution(KeyResolutionError::Chain(
2812                super::super::X509ChainError::Provider(_)
2813            ))
2814        ));
2815    }
2816
2817    #[test]
2818    fn resolver_rejects_revoked_leaf_with_authorized_crl_issuer() {
2819        // Keep revoked-serial enforcement covered independently from the cRLSign authorization
2820        // failure exercised by the legacy fixture above.
2821        let (mut info, root) = authorized_revoked_chain();
2822        let issuer = info.certificates[1].clone();
2823        info.certificates.truncate(1);
2824        info.parsed_certificates.truncate(1);
2825        let key_info = KeyInfo {
2826            sources: vec![KeyInfoSource::X509Data(info)],
2827        };
2828        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2829            lookup_certs: vec![issuer],
2830            trusted_certs: vec![root],
2831            ..KeyResolverConfig::default()
2832        });
2833        let policy = verification_policy_with_trust(crate::policy::KeyTrustPolicy {
2834            check_crls: true,
2835            max_x509_chain_depth: 3,
2836            ..chain_policy_at(
2837                SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_773_964_800),
2838            )
2839        });
2840
2841        let error = match resolver.resolve_with_policy(
2842            Some(&key_info),
2843            SignatureAlgorithm::EcdsaSha256,
2844            &policy,
2845        ) {
2846            Ok(_) => panic!("an authenticated CRL must reject its revoked leaf"),
2847            Err(error) => error,
2848        };
2849        assert!(matches!(
2850            error,
2851            DsigError::KeyResolution(KeyResolutionError::Chain(
2852                super::super::X509ChainError::Revoked(0)
2853            ))
2854        ));
2855    }
2856
2857    #[test]
2858    fn resolves_each_x509_selector_from_configured_certificates() {
2859        // Every selector form documented by KeyInfo must independently locate
2860        // the same configured RSA certificate without embedded key material.
2861        let selectors = [
2862            "<X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName>",
2863            "<X509SubjectName>CN=  test   key rsa-2048  ,O=xml security library (HTTP://WWW.ALEKSEY.COM/XMLSEC),ST=california,C=us</X509SubjectName>",
2864            "<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>",
2865            "<X509SKI>bcOXN/nsVl8GatRbcKrPbzIbw0Y=</X509SKI>",
2866        ];
2867        let configured_certificate = certificate_der(include_str!(
2868            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2869        ));
2870
2871        for selector in selectors {
2872            let key_info = format!("<KeyInfo><X509Data>{selector}</X509Data></KeyInfo>");
2873            let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
2874            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2875                lookup_certs: vec![configured_certificate.clone()],
2876                ..KeyResolverConfig::default()
2877            });
2878            let result = super::super::VerifyContext::new()
2879                .key_resolver(&resolver)
2880                .verify(&xml)
2881                .expect("X509 selector should resolve configured certificate");
2882
2883            assert_eq!(result.status, super::super::DsigStatus::Valid);
2884        }
2885    }
2886
2887    #[test]
2888    fn resolves_configured_chain_selectors_across_certificates() {
2889        // Selector categories may identify different members of one configured
2890        // chain; the unique leaf remains the signing certificate.
2891        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>"#;
2892        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
2893        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2894            lookup_certs: vec![
2895                certificate_der(include_str!(
2896                    "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2897                )),
2898                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2899            ],
2900            ..KeyResolverConfig::default()
2901        });
2902        let result = super::super::VerifyContext::new()
2903            .key_resolver(&resolver)
2904            .verify(&xml)
2905            .expect("selectors across one configured chain should resolve its leaf");
2906
2907        assert_eq!(result.status, super::super::DsigStatus::Valid);
2908    }
2909
2910    #[test]
2911    fn selectors_must_all_match_the_selected_certificate_path() {
2912        // Selector categories may identify different certificates only when
2913        // those certificates belong to the one path chosen for the signer.
2914        let signing_certificate = certificate_der(include_str!(
2915            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2916        ));
2917        let issuer_certificate =
2918            certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
2919        let unrelated = generated_certificate_params("unrelated selector certificate", false)
2920            .self_signed(
2921                &rcgen::KeyPair::generate().expect("unrelated key generation should succeed"),
2922            )
2923            .expect("unrelated certificate should be self-signable")
2924            .der()
2925            .to_vec();
2926        let digest = crate::provider::default_provider()
2927            .digest(super::super::DigestAlgorithm::Sha256, &unrelated)
2928            .expect("SHA-256 selector digest must be available");
2929        let key_info_xml = format!(
2930            "<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>",
2931            STANDARD.encode(digest)
2932        );
2933        let document = roxmltree::Document::parse(&key_info_xml)
2934            .expect("generated selector KeyInfo must be XML");
2935        let key_info = super::super::parse_key_info(document.root_element())
2936            .expect("generated selector KeyInfo must be structurally valid");
2937        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2938            lookup_certs: vec![signing_certificate, issuer_certificate, unrelated],
2939            ..KeyResolverConfig::default()
2940        });
2941
2942        assert!(
2943            resolver
2944                .resolve(Some(&key_info), SignatureAlgorithm::RsaSha256)
2945                .expect("disjoint selector matches are a key miss")
2946                .is_none()
2947        );
2948    }
2949
2950    #[test]
2951    fn unmatched_x509_selector_does_not_resolve() {
2952        // A selector mismatch must not fall back to arbitrary configured key material.
2953        let key_info = "<KeyInfo><X509Data><X509SubjectName>CN=not-the-signer</X509SubjectName></X509Data></KeyInfo>";
2954        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
2955        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2956            lookup_certs: vec![certificate_der(include_str!(
2957                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
2958            ))],
2959            ..KeyResolverConfig::default()
2960        });
2961        let result = super::super::VerifyContext::new()
2962            .key_resolver(&resolver)
2963            .verify(&xml)
2964            .expect("an unmatched selector is a key miss, not a parser failure");
2965
2966        assert!(matches!(
2967            result.status,
2968            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
2969        ));
2970    }
2971
2972    #[test]
2973    fn overlapping_trusted_and_lookup_certificate_preserves_trust() {
2974        // One physical certificate appearing in both pools is one candidate;
2975        // deduplication must retain the stronger trusted classification.
2976        let certificate = certificate_der(RSA_4096_CERTIFICATE);
2977        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2978            trusted_certs: vec![certificate.clone()],
2979            lookup_certs: vec![certificate],
2980            ..KeyResolverConfig::default()
2981        });
2982        let result = super::super::VerifyContext::new()
2983            .policy(verification_policy_with_trust(chain_policy_at(
2984                fixture_certificate_time(),
2985            )))
2986            .key_resolver(&resolver)
2987            .verify(&x509_signature_with_leaf_subject())
2988            .expect("trusted/lookup overlap must resolve as one trusted candidate");
2989
2990        assert_eq!(result.status, super::super::DsigStatus::Valid);
2991    }
2992
2993    #[test]
2994    fn distinct_x509_selector_matches_remain_ambiguous() {
2995        // Deduplication is identity-based, not selector-based: two distinct
2996        // certificates with the same subject remain separate candidates.
2997        let certificate = || {
2998            generated_certificate_params("ambiguous selector", false)
2999                .self_signed(
3000                    &rcgen::KeyPair::generate().expect("test key generation should succeed"),
3001                )
3002                .expect("test certificate should be self-signable")
3003                .der()
3004                .to_vec()
3005        };
3006        let xml = replace_unprefixed_key_info(
3007            X509_DIGEST_SIGNATURE,
3008            "<KeyInfo><X509Data><X509SubjectName>CN=ambiguous selector</X509SubjectName></X509Data></KeyInfo>",
3009        );
3010        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3011            lookup_certs: vec![certificate(), certificate()],
3012            ..KeyResolverConfig::default()
3013        });
3014        let error = super::super::VerifyContext::new()
3015            .key_resolver(&resolver)
3016            .verify(&xml)
3017            .expect_err("distinct selector matches must fail closed");
3018
3019        assert!(matches!(
3020            error,
3021            DsigError::KeyResolution(KeyResolutionError::AmbiguousCertificate)
3022        ));
3023    }
3024
3025    #[test]
3026    fn unsupported_x509_digest_selector_fails_closed() {
3027        // Unknown digest URIs must not be treated as a normal key miss because
3028        // that would silently weaken the caller's explicit selector policy.
3029        let key_info = "<KeyInfo xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\"><X509Data><dsig11:X509Digest Algorithm=\"urn:unsupported\">AQ==</dsig11:X509Digest></X509Data></KeyInfo>";
3030        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3031        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3032            lookup_certs: vec![certificate_der(include_str!(
3033                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3034            ))],
3035            ..KeyResolverConfig::default()
3036        });
3037        let error = super::super::VerifyContext::new()
3038            .key_resolver(&resolver)
3039            .verify(&xml)
3040            .expect_err("unsupported X509Digest algorithm must fail closed");
3041
3042        assert!(matches!(
3043            error,
3044            DsigError::KeyResolution(KeyResolutionError::UnsupportedDigestAlgorithm(uri))
3045                if uri == "urn:unsupported"
3046        ));
3047    }
3048
3049    #[test]
3050    fn x509_digest_selector_uses_operation_provider() {
3051        // The SHA-512 selector is distinct from the SHA-256 reference digest,
3052        // so only provider-aware key selection can surface this rejection.
3053        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3054            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
3055            trusted_certs: vec![
3056                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3057                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
3058            ],
3059            ..KeyResolverConfig::default()
3060        });
3061        let provider = RejectSecondSha512Provider {
3062            sha512_calls: AtomicUsize::new(0),
3063            verification_calls: AtomicUsize::new(0),
3064            reject_verification_call: None,
3065            rejected_verification_data: None,
3066        };
3067        let error = super::super::VerifyContext::new()
3068            .key_resolver(&resolver)
3069            .provider(&provider)
3070            .verify(X509_DIGEST_SIGNATURE)
3071            .expect_err("X509Digest selection must use the operation provider");
3072
3073        assert!(
3074            matches!(
3075                error,
3076                DsigError::Provider(crate::provider::ProviderError::Unsupported {
3077                    operation: crate::provider::ProviderOperation::Digest,
3078                    algorithm: Some(ref uri),
3079                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
3080            ),
3081            "unexpected error: {error:?}"
3082        );
3083    }
3084
3085    #[test]
3086    fn resolves_named_key_end_to_end() {
3087        // KeyName lookup must preserve the same cryptographic result as embedded X509Data.
3088        let xml = replace_key_info(
3089            SIGNED_SAML,
3090            "<ds:KeyInfo><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>",
3091        );
3092        let mut config = KeyResolverConfig::default();
3093        config.named_keys.insert(
3094            "idp-signing".into(),
3095            VerificationKey {
3096                algorithm: SignatureAlgorithm::EcdsaSha256,
3097                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3098                certificate_der: None,
3099                name: Some("idp-signing".into()),
3100            },
3101        );
3102        let resolver = DefaultKeyResolver::new(config);
3103        let result = super::super::VerifyContext::new()
3104            .key_resolver(&resolver)
3105            .verify(&xml)
3106            .expect("named key should resolve");
3107
3108        assert_eq!(result.status, super::super::DsigStatus::Valid);
3109    }
3110
3111    #[test]
3112    fn resolves_der_encoded_key_end_to_end() {
3113        // DSig 1.1 DEREncodedKeyValue must feed the same SPKI verifier path.
3114        let encoded = STANDARD.encode(public_key_der(SAML_PUBLIC_KEY));
3115        let xml = replace_key_info(
3116            SIGNED_SAML,
3117            &format!(
3118                "<ds:KeyInfo><dsig11:DEREncodedKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\">{encoded}</dsig11:DEREncodedKeyValue></ds:KeyInfo>"
3119            ),
3120        );
3121        let resolver = DefaultKeyResolver::default();
3122        let result = super::super::VerifyContext::new()
3123            .key_resolver(&resolver)
3124            .verify(&xml)
3125            .expect("DER key should resolve");
3126
3127        assert_eq!(result.status, super::super::DsigStatus::Valid);
3128    }
3129
3130    #[test]
3131    fn resolves_rsa_key_value_end_to_end() {
3132        // Embedded CryptoBinary parameters must verify the original RSA-2048 donor signature.
3133        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3134            .expect("fixture must contain an RSA public key");
3135        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3136        let key_info = format!(
3137            "<KeyInfo><KeyValue><RSAKeyValue><Modulus>{}</Modulus><Exponent>{}</Exponent></RSAKeyValue></KeyValue></KeyInfo>",
3138            modulus, exponent,
3139        );
3140        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
3141        let resolver = DefaultKeyResolver::default();
3142        let result = super::super::VerifyContext::new()
3143            .key_resolver(&resolver)
3144            .verify(&xml)
3145            .expect("RSAKeyValue should resolve");
3146
3147        assert_eq!(result.status, super::super::DsigStatus::Valid);
3148    }
3149
3150    #[test]
3151    fn rsa_key_value_rejects_legacy_weak_modulus() {
3152        // The secure policy rejects legacy RSA-SHA1 independently of whether
3153        // the capable key came from RSAKeyValue, DER, X.509, or KeyName.
3154        let resolver = DefaultKeyResolver::default();
3155        let error = super::super::VerifyContext::new()
3156            .key_resolver(&resolver)
3157            .verify(LEGACY_RSA_KEY_VALUE_SIGNATURE)
3158            .expect_err("context policy must override permissive resolver defaults");
3159
3160        assert!(matches!(
3161            error,
3162            DsigError::Policy(crate::policy::PolicyViolation::Algorithm {
3163                operation: "verification",
3164                ..
3165            })
3166        ));
3167    }
3168
3169    #[test]
3170    fn operation_policy_rejects_disabled_embedded_key_source() {
3171        // Resolver-owned key material cannot override the operation snapshot's
3172        // decision about which attacker-controlled KeyInfo forms are trusted.
3173        let key_info = KeyInfo {
3174            sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
3175                modulus: vec![0x80; 256],
3176                exponent: vec![1, 0, 1],
3177            })],
3178        };
3179        let mut policy = crate::policy::VerificationPolicy::default();
3180        policy.key_sources.key_value = false;
3181
3182        let error = match DefaultKeyResolver::default().resolve_with_policy(
3183            Some(&key_info),
3184            SignatureAlgorithm::RsaSha256,
3185            &policy,
3186        ) {
3187            Ok(_) => panic!("disabled KeyValue must fail before key construction"),
3188            Err(error) => error,
3189        };
3190
3191        assert!(matches!(
3192            error,
3193            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
3194                reason: "KeyValue key sources are disabled"
3195            })
3196        ));
3197    }
3198
3199    #[test]
3200    fn operation_policy_preflights_every_key_info_source_before_resolution() {
3201        // A permitted source resolving first must not hide a later source that
3202        // the immutable operation policy rejects.
3203        let mut config = KeyResolverConfig::default();
3204        config.named_keys.insert(
3205            "idp-signing".into(),
3206            VerificationKey {
3207                algorithm: SignatureAlgorithm::EcdsaSha256,
3208                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3209                certificate_der: None,
3210                name: Some("idp-signing".into()),
3211            },
3212        );
3213        let resolver = DefaultKeyResolver::new(config);
3214        let mut policy = crate::policy::VerificationPolicy::default();
3215        policy.key_sources.x509_data = false;
3216
3217        for sources in [
3218            vec![
3219                KeyInfoSource::KeyName("idp-signing".into()),
3220                KeyInfoSource::X509Data(X509DataInfo::default()),
3221            ],
3222            vec![
3223                KeyInfoSource::X509Data(X509DataInfo::default()),
3224                KeyInfoSource::KeyName("idp-signing".into()),
3225            ],
3226        ] {
3227            let error = match resolver.resolve_with_policy(
3228                Some(&KeyInfo { sources }),
3229                SignatureAlgorithm::EcdsaSha256,
3230                &policy,
3231            ) {
3232                Ok(_) => panic!("source order must not hide disabled X509Data"),
3233                Err(error) => error,
3234            };
3235
3236            assert!(matches!(
3237                error,
3238                DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
3239                    reason: "X509Data key sources are disabled"
3240                })
3241            ));
3242        }
3243    }
3244
3245    #[test]
3246    fn operation_policy_bounds_ordered_key_info_candidates() {
3247        // The candidate ceiling belongs to the complete verification snapshot:
3248        // neither a first source nor fallback to a later source may bypass it.
3249        let mut config = KeyResolverConfig::default();
3250        config.named_keys.insert(
3251            "idp-signing".into(),
3252            VerificationKey {
3253                algorithm: SignatureAlgorithm::EcdsaSha256,
3254                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3255                certificate_der: None,
3256                name: Some("idp-signing".into()),
3257            },
3258        );
3259        let resolver = DefaultKeyResolver::new(config);
3260        let key_info = KeyInfo {
3261            sources: vec![
3262                KeyInfoSource::KeyValue(KeyValueInfo::Ec {
3263                    curve_oid: "1.3.132.0.35".into(),
3264                    public_key: vec![4],
3265                }),
3266                KeyInfoSource::KeyName("idp-signing".into()),
3267            ],
3268        };
3269
3270        for maximum in [0, 1] {
3271            let mut policy = crate::policy::VerificationPolicy::default();
3272            policy.resources.max_key_candidates = maximum;
3273            let error = match resolver.resolve_with_policy(
3274                Some(&key_info),
3275                SignatureAlgorithm::EcdsaSha256,
3276                &policy,
3277            ) {
3278                Ok(_) => panic!("candidate ceiling {maximum} must stop resolution"),
3279                Err(error) => error,
3280            };
3281            assert!(matches!(
3282                error,
3283                DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3284                    resource: crate::policy::resource_name::KEY_CANDIDATES,
3285                    maximum: observed,
3286                    actual,
3287                }) if observed == maximum && actual == maximum + 1
3288            ));
3289        }
3290
3291        let mut policy = crate::policy::VerificationPolicy::default();
3292        policy.resources.max_key_candidates = 2;
3293        assert!(
3294            resolver
3295                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
3296                .expect("two allowed attempts must reach the named key")
3297                .is_some()
3298        );
3299    }
3300
3301    #[test]
3302    fn operation_policy_bounds_configured_x509_selector_candidates() {
3303        // One X509Data selector can fan out across the resolver-owned store.
3304        // Every distinct certificate inspected is candidate work, rather than
3305        // the complete store counting as one KeyInfo source.
3306        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3307            lookup_certs: vec![
3308                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3309                certificate_der(RSA_4096_CERTIFICATE),
3310            ],
3311            ..KeyResolverConfig::default()
3312        });
3313        let mut policy = crate::policy::VerificationPolicy::default();
3314        policy.resources.max_key_candidates = 1;
3315
3316        let error = super::super::VerifyContext::new()
3317            .policy(policy)
3318            .key_resolver(&resolver)
3319            .verify(&x509_signature_with_leaf_subject())
3320            .expect_err("the second configured certificate must exceed the candidate budget");
3321
3322        assert!(matches!(
3323            error,
3324            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3325                resource: crate::policy::resource_name::KEY_CANDIDATES,
3326                maximum: 1,
3327                actual: 2,
3328            })
3329        ));
3330    }
3331
3332    #[test]
3333    fn operation_policy_bounds_embedded_x509_certificate_candidates() {
3334        // Embedded X509Data is also composite key material. Its certificate
3335        // entries must not collapse into one candidate merely because they
3336        // share a single KeyInfo source node.
3337        let key_info = KeyInfo {
3338            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3339                certificates: vec![
3340                    certificate_der(RSA_4096_CERTIFICATE),
3341                    certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3342                ],
3343                certificate_chain: vec![0],
3344                ..X509DataInfo::default()
3345            })],
3346        };
3347        let mut policy = crate::policy::VerificationPolicy::default();
3348        policy.resources.max_key_candidates = 1;
3349
3350        let error = match DefaultKeyResolver::default().resolve_with_policy(
3351            Some(&key_info),
3352            SignatureAlgorithm::RsaSha256,
3353            &policy,
3354        ) {
3355            Ok(_) => panic!("the second embedded certificate must exceed the candidate budget"),
3356            Err(error) => error,
3357        };
3358
3359        assert!(matches!(
3360            error,
3361            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3362                resource: crate::policy::resource_name::KEY_CANDIDATES,
3363                maximum: 1,
3364                actual: 2,
3365            })
3366        ));
3367    }
3368
3369    #[test]
3370    fn operation_policy_charges_duplicate_configured_x509_candidates() {
3371        // Deduplication may avoid repeated parsing, but inspecting a duplicate
3372        // resolver entry still consumes work and must not bypass the budget.
3373        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3374        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3375            lookup_certs: vec![certificate.clone(), certificate],
3376            ..KeyResolverConfig::default()
3377        });
3378        let mut policy = crate::policy::VerificationPolicy::default();
3379        policy.resources.max_key_candidates = 1;
3380
3381        let error = super::super::VerifyContext::new()
3382            .policy(policy)
3383            .key_resolver(&resolver)
3384            .verify(&x509_signature_with_leaf_subject())
3385            .expect_err("the duplicate configured entry must consume candidate work");
3386
3387        assert!(matches!(
3388            error,
3389            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3390                resource: crate::policy::resource_name::KEY_CANDIDATES,
3391                maximum: 1,
3392                actual: 2,
3393            })
3394        ));
3395    }
3396
3397    #[test]
3398    fn operation_policy_charges_duplicate_embedded_x509_candidates() {
3399        // Public callers can construct KeyInfo without passing through parser
3400        // entry limits, so duplicate embedded entries must consume the budget.
3401        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3402        let key_info = KeyInfo {
3403            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3404                certificates: vec![certificate.clone(), certificate],
3405                certificate_chain: vec![0],
3406                ..X509DataInfo::default()
3407            })],
3408        };
3409        let mut policy = crate::policy::VerificationPolicy::default();
3410        policy.resources.max_key_candidates = 1;
3411
3412        let error = match DefaultKeyResolver::default().resolve_with_policy(
3413            Some(&key_info),
3414            SignatureAlgorithm::RsaSha256,
3415            &policy,
3416        ) {
3417            Ok(_) => panic!("the duplicate embedded entry must consume candidate work"),
3418            Err(error) => error,
3419        };
3420
3421        assert!(matches!(
3422            error,
3423            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3424                resource: crate::policy::resource_name::KEY_CANDIDATES,
3425                maximum: 1,
3426                actual: 2,
3427            })
3428        ));
3429    }
3430
3431    #[test]
3432    fn policy_aware_resolver_rejects_resources_above_hard_ceiling() {
3433        // The resolver is a public policy enforcement boundary in its own
3434        // right; callers must not need VerifyContext to validate the snapshot.
3435        let mut policy = crate::policy::VerificationPolicy::default();
3436        policy.resources.max_key_candidates = usize::MAX;
3437
3438        let error = match DefaultKeyResolver::default().resolve_with_policy(
3439            None,
3440            SignatureAlgorithm::RsaSha256,
3441            &policy,
3442        ) {
3443            Ok(_) => panic!("invalid resource policy must fail before key resolution"),
3444            Err(error) => error,
3445        };
3446
3447        assert!(matches!(
3448            error,
3449            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
3450                resource: crate::policy::resource_name::KEY_CANDIDATES,
3451                actual: usize::MAX,
3452                ..
3453            })
3454        ));
3455    }
3456
3457    #[test]
3458    fn embedded_x509_digest_selection_uses_operation_provider() {
3459        // Embedded certificate selection happens while KeyInfo is parsed, so
3460        // that parser path must retain the verification operation's provider.
3461        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3462        let digest =
3463            super::super::compute_digest(super::super::DigestAlgorithm::Sha512, &certificate);
3464        let xml = format!(
3465            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><X509Data><X509Certificate>{}</X509Certificate><X509Digest xmlns=\"http://www.w3.org/2009/xmldsig11#\" Algorithm=\"{}\">{}</X509Digest></X509Data></KeyInfo>",
3466            STANDARD.encode(&certificate),
3467            super::super::DigestAlgorithm::Sha512.uri(),
3468            STANDARD.encode(digest),
3469        );
3470        let document = roxmltree::Document::parse(&xml).expect("generated KeyInfo must be XML");
3471        let provider = RejectSecondSha512Provider {
3472            sha512_calls: AtomicUsize::new(1),
3473            verification_calls: AtomicUsize::new(0),
3474            reject_verification_call: None,
3475            rejected_verification_data: None,
3476        };
3477
3478        let error =
3479            super::super::parse::parse_key_info_with_provider(document.root_element(), &provider)
3480                .expect_err("embedded X509Digest selection must use the operation provider");
3481
3482        assert!(
3483            matches!(
3484                error,
3485                ParseError::Provider(crate::provider::ProviderError::Unsupported {
3486                    operation: crate::provider::ProviderOperation::Digest,
3487                    algorithm: Some(ref uri),
3488                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
3489            ),
3490            "unexpected error: {error:?}"
3491        );
3492    }
3493
3494    #[test]
3495    fn generic_key_resolution_keeps_legacy_capability_source_independent() {
3496        let certificate =
3497            include_bytes!("../../tests/fixtures/xmldsig/phaos-xmldsig-three/certs/rsa-cert.der")
3498                .to_vec();
3499        let (_, parsed_certificate) = X509Certificate::from_der(&certificate)
3500            .expect("the Phaos fixture is a DER certificate");
3501        let public_key = parsed_certificate.public_key().raw.to_vec();
3502        let rsa_public_key = rsa::RsaPublicKey::from_public_key_der(&public_key)
3503            .expect("the Phaos certificate contains an RSA public key");
3504        let certificate_metadata = parse_x509_certificate(&certificate)
3505            .expect("the Phaos fixture has supported X.509 metadata");
3506        let named_key = VerificationKey {
3507            algorithm: SignatureAlgorithm::RsaSha1,
3508            public_key_bytes: public_key.clone(),
3509            certificate_der: None,
3510            name: Some("legacy".into()),
3511        };
3512        let key_infos = [
3513            KeyInfo {
3514                sources: vec![KeyInfoSource::KeyName("legacy".into())],
3515            },
3516            KeyInfo {
3517                sources: vec![KeyInfoSource::DerEncodedKeyValue(public_key.clone())],
3518            },
3519            KeyInfo {
3520                sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
3521                    modulus: rsa_public_key.n().to_be_bytes_trimmed_vartime().to_vec(),
3522                    exponent: rsa_public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
3523                })],
3524            },
3525            KeyInfo {
3526                sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3527                    certificates: vec![certificate],
3528                    parsed_certificates: vec![certificate_metadata],
3529                    certificate_chain: vec![0],
3530                    ..X509DataInfo::default()
3531                })],
3532            },
3533        ];
3534        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3535            named_keys: HashMap::from([("legacy".into(), named_key.clone())]),
3536            ..KeyResolverConfig::default()
3537        });
3538        let mut policy = crate::policy::VerificationPolicy::default();
3539        policy.key_trust.rsa_keys.minimum_modulus_bits = 1024;
3540        policy
3541            .key_trust
3542            .allowed_legacy_signature_algorithms
3543            .insert(SignatureAlgorithm::RsaSha1);
3544
3545        for key_info in &key_infos {
3546            let key = resolver
3547                .resolve_with_policy(Some(key_info), SignatureAlgorithm::RsaSha1, &policy)
3548                .expect("the key source is valid")
3549                .expect("key resolution remains independent from operation policy");
3550            assert!(
3551                !key.verify(SignatureAlgorithm::RsaSha1, b"data", &[0; 128])
3552                    .expect("the legacy RSA key is structurally valid")
3553            );
3554        }
3555    }
3556
3557    #[test]
3558    fn rsa_key_value_rejects_ecdsa_signature_method() {
3559        // Embedded RSA parameters must not be relabeled for an ECDSA SignatureMethod.
3560        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3561            .expect("fixture must contain an RSA public key");
3562        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3563        let key_info = format!(
3564            "<ds:KeyInfo><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>{}</ds:Modulus><ds:Exponent>{}</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo>",
3565            modulus, exponent,
3566        );
3567        let xml = replace_key_info(SIGNED_SAML, &key_info);
3568        let resolver = DefaultKeyResolver::default();
3569        let error = super::super::VerifyContext::new()
3570            .key_resolver(&resolver)
3571            .verify(&xml)
3572            .expect_err("RSAKeyValue must not resolve for ECDSA");
3573
3574        assert!(matches!(
3575            error,
3576            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3577        ));
3578    }
3579
3580    #[test]
3581    fn resolves_ec_p256_key_value_end_to_end() {
3582        // XMLDSig 1.1 ECKeyValue must verify without a preset key or certificate.
3583        let resolver = DefaultKeyResolver::default();
3584        let result = super::super::VerifyContext::new()
3585            .key_resolver(&resolver)
3586            .verify(EC_P256_KEY_VALUE_SIGNATURE)
3587            .expect("P-256 ECKeyValue should resolve");
3588
3589        assert_eq!(result.status, super::super::DsigStatus::Valid);
3590    }
3591
3592    #[test]
3593    fn resolves_ec_p384_key_value_end_to_end() {
3594        // The donor P-384 vector uses NamedCurve + uncompressed PublicKey.
3595        let resolver = DefaultKeyResolver::default();
3596        let result = super::super::VerifyContext::new()
3597            .key_resolver(&resolver)
3598            .verify(EC_P384_KEY_VALUE_SIGNATURE)
3599            .expect("P-384 ECKeyValue should resolve");
3600
3601        assert_eq!(result.status, super::super::DsigStatus::Valid);
3602    }
3603
3604    #[test]
3605    fn ec_key_value_ignored_for_rsa_signature_method() {
3606        // Embedded EC key material must not be relabeled for an RSA SignatureMethod.
3607        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>"#;
3608        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3609        let resolver = DefaultKeyResolver::default();
3610        let result = super::super::VerifyContext::new()
3611            .key_resolver(&resolver)
3612            .verify(&xml)
3613            .expect("single incompatible ECKeyValue should be ignored");
3614
3615        assert_eq!(
3616            result.status,
3617            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
3618        );
3619    }
3620
3621    #[test]
3622    fn incompatible_ec_key_value_falls_back_to_later_rsa_key_value() {
3623        // Mixed KeyInfo should keep scanning after an incompatible ECKeyValue source.
3624        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
3625            .expect("fixture must contain an RSA public key");
3626        let (modulus, exponent) = rsa_key_value_parts(&public_key);
3627        let key_info = format!(
3628            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>"#,
3629            modulus, exponent,
3630        );
3631        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
3632        let resolver = DefaultKeyResolver::default();
3633        let result = super::super::VerifyContext::new()
3634            .key_resolver(&resolver)
3635            .verify(&xml)
3636            .expect("later RSAKeyValue should resolve");
3637
3638        assert_eq!(result.status, super::super::DsigStatus::Valid);
3639    }
3640
3641    #[test]
3642    fn unsupported_ec_key_value_falls_back_to_later_key_name() {
3643        // Unsupported curves are non-fatal so a later compatible source can verify.
3644        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>"#;
3645        let xml = replace_key_info(SIGNED_SAML, key_info);
3646        let mut config = KeyResolverConfig::default();
3647        config.named_keys.insert(
3648            "idp-signing".into(),
3649            VerificationKey {
3650                algorithm: SignatureAlgorithm::EcdsaSha256,
3651                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3652                certificate_der: None,
3653                name: Some("idp-signing".into()),
3654            },
3655        );
3656        let resolver = DefaultKeyResolver::new(config);
3657        let result = super::super::VerifyContext::new()
3658            .key_resolver(&resolver)
3659            .verify(&xml)
3660            .expect("later KeyName should resolve");
3661
3662        assert_eq!(result.status, super::super::DsigStatus::Valid);
3663    }
3664
3665    #[test]
3666    fn invalid_ec_key_value_falls_back_to_later_key_name() {
3667        // Off-curve EC points are typed errors only if no later source can verify.
3668        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>"#;
3669        let xml = replace_key_info(SIGNED_SAML, key_info);
3670        let mut config = KeyResolverConfig::default();
3671        config.named_keys.insert(
3672            "idp-signing".into(),
3673            VerificationKey {
3674                algorithm: SignatureAlgorithm::EcdsaSha256,
3675                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3676                certificate_der: None,
3677                name: Some("idp-signing".into()),
3678            },
3679        );
3680        let resolver = DefaultKeyResolver::new(config);
3681        let result = super::super::VerifyContext::new()
3682            .key_resolver(&resolver)
3683            .verify(&xml)
3684            .expect("later KeyName should resolve after invalid ECKeyValue");
3685
3686        assert_eq!(result.status, super::super::DsigStatus::Valid);
3687    }
3688
3689    #[test]
3690    fn malformed_ec_key_value_falls_back_to_later_key_name() {
3691        // Parse-level EC point errors remain non-fatal while later sources exist.
3692        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>"#;
3693        let xml = replace_key_info(SIGNED_SAML, key_info);
3694        let mut config = KeyResolverConfig::default();
3695        config.named_keys.insert(
3696            "idp-signing".into(),
3697            VerificationKey {
3698                algorithm: SignatureAlgorithm::EcdsaSha256,
3699                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3700                certificate_der: None,
3701                name: Some("idp-signing".into()),
3702            },
3703        );
3704        let resolver = DefaultKeyResolver::new(config);
3705        let result = super::super::VerifyContext::new()
3706            .key_resolver(&resolver)
3707            .verify(&xml)
3708            .expect("later KeyName should resolve after malformed ECKeyValue");
3709
3710        assert_eq!(result.status, super::super::DsigStatus::Valid);
3711    }
3712
3713    #[test]
3714    fn invalid_base64_ec_key_value_falls_back_to_later_key_name() {
3715        // A bad ECKeyValue payload is an unusable source, not a reason to skip
3716        // later ordered KeyInfo sources that can verify the signature.
3717        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>"#;
3718        let xml = replace_key_info(SIGNED_SAML, key_info);
3719        let mut config = KeyResolverConfig::default();
3720        config.named_keys.insert(
3721            "idp-signing".into(),
3722            VerificationKey {
3723                algorithm: SignatureAlgorithm::EcdsaSha256,
3724                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3725                certificate_der: None,
3726                name: Some("idp-signing".into()),
3727            },
3728        );
3729        let resolver = DefaultKeyResolver::new(config);
3730        let result = super::super::VerifyContext::new()
3731            .key_resolver(&resolver)
3732            .verify(&xml)
3733            .expect("later KeyName should resolve after bad ECKeyValue base64");
3734
3735        assert_eq!(result.status, super::super::DsigStatus::Valid);
3736    }
3737
3738    #[test]
3739    fn missing_curve_uri_ec_key_value_falls_back_to_later_key_name() {
3740        // Missing EC curve parameters make only this KeyValue source unusable.
3741        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>"#;
3742        let xml = replace_key_info(SIGNED_SAML, key_info);
3743        let mut config = KeyResolverConfig::default();
3744        config.named_keys.insert(
3745            "idp-signing".into(),
3746            VerificationKey {
3747                algorithm: SignatureAlgorithm::EcdsaSha256,
3748                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3749                certificate_der: None,
3750                name: Some("idp-signing".into()),
3751            },
3752        );
3753        let resolver = DefaultKeyResolver::new(config);
3754        let result = super::super::VerifyContext::new()
3755            .key_resolver(&resolver)
3756            .verify(&xml)
3757            .expect("later KeyName should resolve after missing EC curve URI");
3758
3759        assert_eq!(result.status, super::super::DsigStatus::Valid);
3760    }
3761
3762    #[test]
3763    fn malformed_ec_key_value_children_fall_back_to_later_key_name() {
3764        // An unusable EC source must not prevent later ordered KeyInfo sources
3765        // from resolving, regardless of which required child-shape check fails.
3766        let malformed_ec_key_values = [
3767            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/>"#,
3768            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"/>"#,
3769            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BA==</dsig11:PublicKey><dsig11:PublicKey>BA==</dsig11:PublicKey>"#,
3770        ];
3771
3772        for malformed_children in malformed_ec_key_values {
3773            let key_info = format!(
3774                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>"#
3775            );
3776            let xml = replace_key_info(SIGNED_SAML, &key_info);
3777            let mut config = KeyResolverConfig::default();
3778            config.named_keys.insert(
3779                "idp-signing".into(),
3780                VerificationKey {
3781                    algorithm: SignatureAlgorithm::EcdsaSha256,
3782                    public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3783                    certificate_der: None,
3784                    name: Some("idp-signing".into()),
3785                },
3786            );
3787            let resolver = DefaultKeyResolver::new(config);
3788            let result = super::super::VerifyContext::new()
3789                .key_resolver(&resolver)
3790                .verify(&xml)
3791                .expect("later KeyName should resolve after malformed EC child shape");
3792
3793            assert_eq!(result.status, super::super::DsigStatus::Valid);
3794        }
3795    }
3796
3797    #[test]
3798    fn supported_ec_curve_does_not_fall_back_to_later_key_name() {
3799        // ECDSA-SHA256 accepts P-384, so this first source is a usable key and
3800        // must not be skipped merely because a later P-256 KeyName happens to
3801        // verify the signature. Verification fails against the selected key.
3802        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>"#;
3803        let xml = replace_key_info(SIGNED_SAML, key_info);
3804        let mut config = KeyResolverConfig::default();
3805        config.named_keys.insert(
3806            "idp-signing".into(),
3807            VerificationKey {
3808                algorithm: SignatureAlgorithm::EcdsaSha256,
3809                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3810                certificate_der: None,
3811                name: Some("idp-signing".into()),
3812            },
3813        );
3814        let resolver = DefaultKeyResolver::new(config);
3815        let error = super::super::VerifyContext::new()
3816            .key_resolver(&resolver)
3817            .verify(&xml)
3818            .expect_err("a usable first key source must not fall through after verification");
3819
3820        assert!(matches!(
3821            error,
3822            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
3823        ));
3824    }
3825
3826    #[test]
3827    fn lone_malformed_ec_key_value_reports_invalid_public_key() {
3828        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>"#;
3829        let xml = replace_key_info(SIGNED_SAML, key_info);
3830        let error = super::super::VerifyContext::new()
3831            .key_resolver(&DefaultKeyResolver::default())
3832            .verify(&xml)
3833            .expect_err("lone malformed ECKeyValue should surface typed key error");
3834
3835        assert!(matches!(
3836            error,
3837            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
3838        ));
3839    }
3840
3841    #[test]
3842    fn lone_supported_ec_curve_reaches_signature_verification() {
3843        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>"#;
3844        let xml = replace_key_info(SIGNED_SAML, key_info);
3845        let error = super::super::VerifyContext::new()
3846            .key_resolver(&DefaultKeyResolver::default())
3847            .verify(&xml)
3848            .expect_err("a supported EC curve must reach signature verification");
3849
3850        assert!(matches!(
3851            error,
3852            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
3853        ));
3854    }
3855
3856    #[test]
3857    fn chain_verification_rejects_untrusted_embedded_certificate() {
3858        // Enabling chain policy must fail closed when no trust anchor is configured.
3859        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3860            ..KeyResolverConfig::default()
3861        });
3862        let error = super::super::VerifyContext::new()
3863            .policy(verification_policy_with_trust(chain_policy()))
3864            .key_resolver(&resolver)
3865            .verify(SIGNED_SAML)
3866            .expect_err("untrusted certificate must fail chain validation");
3867
3868        assert!(matches!(
3869            error,
3870            DsigError::KeyResolution(KeyResolutionError::Chain(
3871                super::super::X509ChainError::UntrustedRoot
3872            ))
3873        ));
3874    }
3875
3876    #[test]
3877    fn named_key_algorithm_mismatch_fails_closed() {
3878        // A key registered for RSA must never be attempted for an ECDSA signature.
3879        let xml = replace_key_info(
3880            SIGNED_SAML,
3881            "<ds:KeyInfo><ds:KeyName>wrong-algorithm</ds:KeyName></ds:KeyInfo>",
3882        );
3883        let mut config = KeyResolverConfig::default();
3884        config.named_keys.insert(
3885            "wrong-algorithm".into(),
3886            VerificationKey {
3887                algorithm: SignatureAlgorithm::RsaSha256,
3888                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
3889                certificate_der: None,
3890                name: Some("wrong-algorithm".into()),
3891            },
3892        );
3893        let resolver = DefaultKeyResolver::new(config);
3894        let error = super::super::VerifyContext::new()
3895            .key_resolver(&resolver)
3896            .verify(&xml)
3897            .expect_err("algorithm mismatch must fail closed");
3898
3899        assert!(matches!(
3900            error,
3901            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3902        ));
3903    }
3904
3905    #[test]
3906    fn named_key_spki_type_mismatch_fails_during_resolution() {
3907        // The configured algorithm label cannot override the actual SPKI key type.
3908        let xml = replace_key_info(
3909            SIGNED_SAML,
3910            "<ds:KeyInfo><ds:KeyName>mislabeled</ds:KeyName></ds:KeyInfo>",
3911        );
3912        let mut config = KeyResolverConfig::default();
3913        config.named_keys.insert(
3914            "mislabeled".into(),
3915            VerificationKey {
3916                algorithm: SignatureAlgorithm::EcdsaSha256,
3917                public_key_bytes: public_key_der(RSA_PUBLIC_KEY),
3918                certificate_der: None,
3919                name: Some("mislabeled".into()),
3920            },
3921        );
3922        let resolver = DefaultKeyResolver::new(config);
3923        let error = super::super::VerifyContext::new()
3924            .key_resolver(&resolver)
3925            .verify(&xml)
3926            .expect_err("mislabeled named key must fail during resolution");
3927
3928        assert!(matches!(
3929            error,
3930            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
3931        ));
3932    }
3933
3934    #[test]
3935    fn malformed_named_key_reports_public_key_error() {
3936        // Non-certificate SPKI failures must not be mislabeled as certificate errors.
3937        let xml = replace_key_info(
3938            SIGNED_SAML,
3939            "<ds:KeyInfo><ds:KeyName>malformed</ds:KeyName></ds:KeyInfo>",
3940        );
3941        let mut config = KeyResolverConfig::default();
3942        config.named_keys.insert(
3943            "malformed".into(),
3944            VerificationKey {
3945                algorithm: SignatureAlgorithm::EcdsaSha256,
3946                public_key_bytes: vec![1, 2, 3],
3947                certificate_der: None,
3948                name: Some("malformed".into()),
3949            },
3950        );
3951        let resolver = DefaultKeyResolver::new(config);
3952        let error = super::super::VerifyContext::new()
3953            .key_resolver(&resolver)
3954            .verify(&xml)
3955            .expect_err("malformed named key must fail during resolution");
3956
3957        assert!(matches!(
3958            error,
3959            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
3960        ));
3961    }
3962}