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