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::EncodePublicKey as DsaEncodePublicKey;
7use hmac::{KeyInit, Mac};
8use rsa::pkcs8::DecodePublicKey as _;
9use x509_parser::{
10    prelude::{FromDer, X509Certificate},
11    public_key::PublicKey,
12    x509::SubjectPublicKeyInfo,
13};
14use zeroize::Zeroizing;
15
16use super::signature::{
17    decode_dsa_verifying_key, signature_value_matches_spki,
18    signature_value_matches_spki_with_encoding, validate_dsa_signature_spki_with_minimum,
19    validate_ec_public_key_encoding, validate_rsa_signature_spki_with_minimum,
20    verify_dsa_signature_spki_primitive, verify_dsa_signature_spki_with_minimum,
21    verify_rsa_signature_spki_primitive, verify_rsa_signature_spki_with_minimum,
22};
23use super::{
24    DsigError, KeyInfo, KeyInfoSource, KeyResolver, KeyValueInfo, SignatureAlgorithm, VerifyingKey,
25    X509ChainOptions, X509DataInfo,
26    parse::{
27        EC_P256_OID, EC_P384_OID, EC_P521_OID, ParseError, X509ChainBuildError,
28        build_x509_certificate_paths_to_selector_targets,
29        build_x509_certificate_paths_to_trusted_prefix, distinguished_names_equal,
30        parse_x509_certificate, x509_certificate_matches_any_selector,
31        x509_data_has_lookup_identifiers, x509_selector_categories_match_chain,
32    },
33    verify_ecdsa_signature_spki, verify_ecdsa_signature_spki_with_encoding,
34    x509::verify_x509_certificate_chain_with_provider_and_crls,
35};
36use super::{ResolvedVerificationKey, TrustedPublicKey, X509TrustEvidence};
37
38/// Caller-owned HMAC verification key.
39///
40/// Policy-free [`VerifyingKey`] calls enforce [`crate::policy::HmacPolicy::default`].
41/// [`super::VerifyContext`] supplies its immutable operation policy through the
42/// policy-aware hooks, so legacy truncation always requires an explicit opt-in.
43/// Owned secret bytes are zeroized when the key is dropped.
44#[derive(Clone)]
45pub struct HmacVerificationKey {
46    secret: Zeroizing<Vec<u8>>,
47}
48
49impl fmt::Debug for HmacVerificationKey {
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        formatter
52            .debug_struct("HmacVerificationKey")
53            .finish_non_exhaustive()
54    }
55}
56
57impl HmacVerificationKey {
58    /// Construct a key from non-empty secret bytes.
59    pub fn new(secret: impl Into<Vec<u8>>) -> Result<Self, KeyResolutionError> {
60        let secret = secret.into();
61        if secret.is_empty() {
62            return Err(KeyResolutionError::InvalidPublicKey);
63        }
64        Ok(Self {
65            secret: Zeroizing::new(secret),
66        })
67    }
68
69    fn validate_output(
70        &self,
71        policy: crate::policy::HmacPolicy,
72        algorithm: SignatureAlgorithm,
73        signature_value: &[u8],
74    ) -> Result<(), DsigError> {
75        if algorithm.hmac_output_bits().is_none() {
76            return Err(KeyResolutionError::AlgorithmMismatch.into());
77        }
78        policy.validate_key_bytes(self.secret.len())?;
79        policy.validate_output(algorithm, signature_value.len().saturating_mul(8))?;
80        Ok(())
81    }
82
83    fn verify_with_hmac_policy(
84        &self,
85        policy: crate::policy::HmacPolicy,
86        algorithm: SignatureAlgorithm,
87        signed_data: &[u8],
88        signature_value: &[u8],
89    ) -> Result<bool, DsigError> {
90        self.validate_output(policy, algorithm, signature_value)?;
91        macro_rules! verify_hmac {
92            ($digest:ty) => {{
93                let mut mac = hmac::Hmac::<$digest>::new_from_slice(&self.secret)
94                    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
95                mac.update(signed_data);
96                let expected = mac.finalize().into_bytes();
97                subtle::ConstantTimeEq::ct_eq(&expected[..signature_value.len()], signature_value)
98                    .into()
99            }};
100        }
101        Ok(match algorithm {
102            SignatureAlgorithm::HmacSha1 => verify_hmac!(sha1::Sha1),
103            SignatureAlgorithm::HmacSha224 => verify_hmac!(sha2::Sha224),
104            SignatureAlgorithm::HmacSha256 => verify_hmac!(sha2::Sha256),
105            SignatureAlgorithm::HmacSha384 => verify_hmac!(sha2::Sha384),
106            SignatureAlgorithm::HmacSha512 => verify_hmac!(sha2::Sha512),
107            _ => return Err(KeyResolutionError::AlgorithmMismatch.into()),
108        })
109    }
110}
111
112impl VerifyingKey for HmacVerificationKey {
113    fn validate_policy(&self, policy: &crate::policy::VerificationPolicy) -> Result<(), DsigError> {
114        policy
115            .hmac
116            .validate_key_bytes(self.secret.len())
117            .map_err(Into::into)
118    }
119
120    fn validate_signature_value(
121        &self,
122        algorithm: SignatureAlgorithm,
123        signature_value: &[u8],
124    ) -> Result<bool, DsigError> {
125        self.validate_output(
126            crate::policy::HmacPolicy::default(),
127            algorithm,
128            signature_value,
129        )?;
130        Ok(true)
131    }
132
133    fn validate_signature_value_with_policy(
134        &self,
135        policy: &crate::policy::VerificationPolicy,
136        algorithm: SignatureAlgorithm,
137        signature_value: &[u8],
138    ) -> Result<bool, DsigError> {
139        self.validate_output(policy.hmac, algorithm, signature_value)?;
140        Ok(true)
141    }
142
143    fn verify(
144        &self,
145        algorithm: SignatureAlgorithm,
146        signed_data: &[u8],
147        signature_value: &[u8],
148    ) -> Result<bool, DsigError> {
149        self.verify_with_hmac_policy(
150            crate::policy::HmacPolicy::default(),
151            algorithm,
152            signed_data,
153            signature_value,
154        )
155    }
156
157    fn verify_with_policy(
158        &self,
159        policy: &crate::policy::VerificationPolicy,
160        algorithm: SignatureAlgorithm,
161        signed_data: &[u8],
162        signature_value: &[u8],
163    ) -> Result<bool, DsigError> {
164        self.verify_with_hmac_policy(policy.hmac, algorithm, signed_data, signature_value)
165    }
166}
167
168/// Compatibility name for the verification key originally limited to HMAC-SHA1.
169pub type HmacSha1VerificationKey = HmacVerificationKey;
170
171/// A public verification key available to key resolvers.
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct VerificationKey {
174    /// Signature algorithm this key is configured to verify.
175    pub algorithm: SignatureAlgorithm,
176    /// DER-encoded SubjectPublicKeyInfo bytes.
177    pub public_key_bytes: Vec<u8>,
178    /// DER certificate from which the key was extracted, when applicable.
179    pub certificate_der: Option<Vec<u8>>,
180    /// Name used to register this key for `<KeyName>` resolution.
181    pub name: Option<String>,
182}
183
184impl VerifyingKey for VerificationKey {
185    fn verification_spki(&self, algorithm: SignatureAlgorithm) -> Result<Option<&[u8]>, DsigError> {
186        if algorithm != self.algorithm {
187            return Err(KeyResolutionError::AlgorithmMismatch.into());
188        }
189        Ok(Some(&self.public_key_bytes))
190    }
191    fn verify_with_context(
192        &self,
193        algorithm: SignatureAlgorithm,
194        context: &super::SignatureContext,
195        data: &[u8],
196        signature: &[u8],
197    ) -> Result<bool, DsigError> {
198        if algorithm != self.algorithm {
199            return Err(KeyResolutionError::AlgorithmMismatch.into());
200        }
201        if algorithm.eddsa_signature_len().is_some()
202            || matches!(algorithm, SignatureAlgorithm::PostQuantum(_))
203        {
204            return super::modern::verify_with_context(
205                algorithm,
206                &self.public_key_bytes,
207                context,
208                data,
209                signature,
210            )
211            .map_err(DsigError::Crypto);
212        }
213        if !context.as_bytes().is_empty() {
214            return Err(KeyResolutionError::AlgorithmMismatch.into());
215        }
216        self.verify(algorithm, data, signature)
217    }
218    fn validate_policy(&self, policy: &crate::policy::VerificationPolicy) -> Result<(), DsigError> {
219        let result = match self.algorithm {
220            SignatureAlgorithm::Ed25519
221            | SignatureAlgorithm::Ed25519Ctx
222            | SignatureAlgorithm::Ed25519Ph
223            | SignatureAlgorithm::Ed448
224            | SignatureAlgorithm::Ed448Ph
225            | SignatureAlgorithm::PostQuantum(_) => {
226                super::modern::validate_public_key(self.algorithm, &self.public_key_bytes)
227            }
228            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
229                validate_dsa_signature_spki_with_minimum(
230                    &self.public_key_bytes,
231                    policy.key_trust.dsa_keys.minimum_modulus_bits,
232                )
233            }
234            SignatureAlgorithm::RsaSha1
235            | SignatureAlgorithm::RsaSha224
236            | SignatureAlgorithm::RsaSha256
237            | SignatureAlgorithm::RsaSha384
238            | SignatureAlgorithm::RsaSha512 => validate_rsa_signature_spki_with_minimum(
239                self.algorithm,
240                &self.public_key_bytes,
241                policy.key_trust.rsa_keys.minimum_modulus_bits,
242            ),
243            SignatureAlgorithm::HmacSha1
244            | SignatureAlgorithm::HmacSha224
245            | SignatureAlgorithm::HmacSha256
246            | SignatureAlgorithm::HmacSha384
247            | SignatureAlgorithm::HmacSha512
248            | SignatureAlgorithm::EcdsaSha1
249            | SignatureAlgorithm::EcdsaSha224
250            | SignatureAlgorithm::EcdsaSha256
251            | SignatureAlgorithm::EcdsaSha384
252            | SignatureAlgorithm::EcdsaSha512
253            | SignatureAlgorithm::EcdsaSha3_224
254            | SignatureAlgorithm::EcdsaSha3_256
255            | SignatureAlgorithm::EcdsaSha3_384
256            | SignatureAlgorithm::EcdsaSha3_512 => Ok(()),
257        };
258        result.map_err(DsigError::Crypto)
259    }
260
261    fn validate_signature_value(
262        &self,
263        algorithm: SignatureAlgorithm,
264        signature_value: &[u8],
265    ) -> Result<bool, DsigError> {
266        if algorithm != self.algorithm {
267            return Err(KeyResolutionError::AlgorithmMismatch.into());
268        }
269        signature_value_matches_spki(algorithm, &self.public_key_bytes, signature_value)
270            .map_err(DsigError::Crypto)
271    }
272
273    fn validate_signature_value_with_policy(
274        &self,
275        policy: &crate::policy::VerificationPolicy,
276        algorithm: SignatureAlgorithm,
277        signature_value: &[u8],
278    ) -> Result<bool, DsigError> {
279        if algorithm != self.algorithm {
280            return Err(KeyResolutionError::AlgorithmMismatch.into());
281        }
282        signature_value_matches_spki_with_encoding(
283            algorithm,
284            &self.public_key_bytes,
285            signature_value,
286            policy.ecdsa_signature_value_encoding,
287        )
288        .map_err(DsigError::Crypto)
289    }
290
291    fn verify(
292        &self,
293        algorithm: SignatureAlgorithm,
294        signed_data: &[u8],
295        signature_value: &[u8],
296    ) -> Result<bool, DsigError> {
297        if algorithm != self.algorithm {
298            return Err(KeyResolutionError::AlgorithmMismatch.into());
299        }
300        let result = match algorithm {
301            SignatureAlgorithm::Ed25519
302            | SignatureAlgorithm::Ed25519Ctx
303            | SignatureAlgorithm::Ed25519Ph
304            | SignatureAlgorithm::Ed448
305            | SignatureAlgorithm::Ed448Ph
306            | SignatureAlgorithm::PostQuantum(_) => super::modern::verify(
307                algorithm,
308                &self.public_key_bytes,
309                signed_data,
310                signature_value,
311            ),
312            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
313                verify_dsa_signature_spki_primitive(
314                    algorithm,
315                    &self.public_key_bytes,
316                    signed_data,
317                    signature_value,
318                )
319            }
320            SignatureAlgorithm::HmacSha1
321            | SignatureAlgorithm::HmacSha224
322            | SignatureAlgorithm::HmacSha256
323            | SignatureAlgorithm::HmacSha384
324            | SignatureAlgorithm::HmacSha512 => {
325                return Err(KeyResolutionError::AlgorithmMismatch.into());
326            }
327            SignatureAlgorithm::RsaSha1
328            | SignatureAlgorithm::RsaSha224
329            | SignatureAlgorithm::RsaSha256
330            | SignatureAlgorithm::RsaSha384
331            | SignatureAlgorithm::RsaSha512 => verify_rsa_signature_spki_primitive(
332                algorithm,
333                &self.public_key_bytes,
334                signed_data,
335                signature_value,
336            ),
337            SignatureAlgorithm::EcdsaSha1
338            | SignatureAlgorithm::EcdsaSha224
339            | SignatureAlgorithm::EcdsaSha256
340            | SignatureAlgorithm::EcdsaSha384
341            | SignatureAlgorithm::EcdsaSha512
342            | SignatureAlgorithm::EcdsaSha3_224
343            | SignatureAlgorithm::EcdsaSha3_256
344            | SignatureAlgorithm::EcdsaSha3_384
345            | SignatureAlgorithm::EcdsaSha3_512 => verify_ecdsa_signature_spki(
346                algorithm,
347                &self.public_key_bytes,
348                signed_data,
349                signature_value,
350            ),
351        };
352        result.map_err(DsigError::Crypto)
353    }
354
355    fn verify_with_policy(
356        &self,
357        policy: &crate::policy::VerificationPolicy,
358        algorithm: SignatureAlgorithm,
359        signed_data: &[u8],
360        signature_value: &[u8],
361    ) -> Result<bool, DsigError> {
362        if algorithm != self.algorithm {
363            return Err(KeyResolutionError::AlgorithmMismatch.into());
364        }
365        if algorithm.ecdsa_digest().is_some() {
366            return verify_ecdsa_signature_spki_with_encoding(
367                algorithm,
368                &self.public_key_bytes,
369                signed_data,
370                signature_value,
371                policy.ecdsa_signature_value_encoding,
372            )
373            .map_err(DsigError::Crypto);
374        }
375        self.verify(algorithm, signed_data, signature_value)
376    }
377}
378
379struct PolicyBoundVerificationKey {
380    key: VerificationKey,
381    rsa_minimum_bits: usize,
382    dsa_minimum_bits: usize,
383}
384
385impl VerifyingKey for PolicyBoundVerificationKey {
386    fn verification_spki(&self, algorithm: SignatureAlgorithm) -> Result<Option<&[u8]>, DsigError> {
387        // Preserve strength checks carried by this resolver-produced handle.
388        if matches!(
389            algorithm,
390            SignatureAlgorithm::RsaSha1
391                | SignatureAlgorithm::RsaSha224
392                | SignatureAlgorithm::RsaSha256
393                | SignatureAlgorithm::RsaSha384
394                | SignatureAlgorithm::RsaSha512
395        ) {
396            validate_rsa_signature_spki_with_minimum(
397                algorithm,
398                &self.key.public_key_bytes,
399                self.rsa_minimum_bits,
400            )
401            .map_err(DsigError::Crypto)?;
402        }
403        self.key.verification_spki(algorithm)
404    }
405    fn verify_with_context(
406        &self,
407        algorithm: SignatureAlgorithm,
408        context: &super::SignatureContext,
409        data: &[u8],
410        signature: &[u8],
411    ) -> Result<bool, DsigError> {
412        if context.as_bytes().is_empty() {
413            return self.verify(algorithm, data, signature);
414        }
415        self.key
416            .verify_with_context(algorithm, context, data, signature)
417    }
418    fn validate_signature_value(
419        &self,
420        algorithm: SignatureAlgorithm,
421        signature_value: &[u8],
422    ) -> Result<bool, DsigError> {
423        self.key
424            .validate_signature_value(algorithm, signature_value)
425    }
426
427    fn validate_signature_value_with_policy(
428        &self,
429        policy: &crate::policy::VerificationPolicy,
430        algorithm: SignatureAlgorithm,
431        signature_value: &[u8],
432    ) -> Result<bool, DsigError> {
433        self.key
434            .validate_signature_value_with_policy(policy, algorithm, signature_value)
435    }
436
437    fn verify(
438        &self,
439        algorithm: SignatureAlgorithm,
440        signed_data: &[u8],
441        signature_value: &[u8],
442    ) -> Result<bool, DsigError> {
443        if algorithm != self.key.algorithm {
444            return Err(KeyResolutionError::AlgorithmMismatch.into());
445        }
446        let result = match algorithm {
447            SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256 => {
448                verify_dsa_signature_spki_with_minimum(
449                    algorithm,
450                    &self.key.public_key_bytes,
451                    signed_data,
452                    signature_value,
453                    self.dsa_minimum_bits,
454                )
455            }
456            SignatureAlgorithm::RsaSha1
457            | SignatureAlgorithm::RsaSha224
458            | SignatureAlgorithm::RsaSha256
459            | SignatureAlgorithm::RsaSha384
460            | SignatureAlgorithm::RsaSha512 => verify_rsa_signature_spki_with_minimum(
461                algorithm,
462                &self.key.public_key_bytes,
463                signed_data,
464                signature_value,
465                self.rsa_minimum_bits,
466            ),
467            _ => return self.key.verify(algorithm, signed_data, signature_value),
468        };
469        result.map_err(DsigError::Crypto)
470    }
471
472    fn verify_with_policy(
473        &self,
474        policy: &crate::policy::VerificationPolicy,
475        algorithm: SignatureAlgorithm,
476        signed_data: &[u8],
477        signature_value: &[u8],
478    ) -> Result<bool, DsigError> {
479        if algorithm.ecdsa_digest().is_some() {
480            return self
481                .key
482                .verify_with_policy(policy, algorithm, signed_data, signature_value);
483        }
484        self.verify(algorithm, signed_data, signature_value)
485    }
486}
487
488/// Failures while applying [`KeyResolverConfig`] to parsed key material.
489#[derive(Debug, thiserror::Error)]
490#[non_exhaustive]
491pub enum KeyResolutionError {
492    /// A configured or embedded key does not match the signature method.
493    #[error("verification key does not match the signature algorithm")]
494    AlgorithmMismatch,
495    /// An embedded certificate could not be parsed completely.
496    #[error("invalid embedded certificate DER")]
497    InvalidCertificate,
498    /// Configured or embedded public key DER could not be parsed completely.
499    #[error("invalid public key DER")]
500    InvalidPublicKey,
501    /// More than one configured certificate satisfies all X.509 selectors.
502    #[error("X.509 lookup selectors match multiple configured certificates")]
503    AmbiguousCertificate,
504    /// An X.509 selector uses a digest algorithm unsupported by this crate.
505    #[error("unsupported X.509 digest algorithm: {0}")]
506    UnsupportedDigestAlgorithm(String),
507    /// Embedded certificate path validation failed.
508    #[error("certificate chain validation failed: {0}")]
509    Chain(#[from] super::X509ChainError),
510    /// System time was unavailable for certificate validation.
511    #[error("system time is unavailable")]
512    SystemTime,
513}
514
515/// Configuration for the default XMLDSig key resolver.
516///
517/// The configuration owns all key material and has no global registry. Chain
518/// verification is opt-in so callers that pin an embedded certificate can use
519/// the documented TOFU model without constructing a certificate path.
520#[derive(Debug, Clone, Default, PartialEq, Eq)]
521pub struct KeyResolverConfig {
522    /// DER-encoded certificates available to X.509 selectors and as untrusted
523    /// path intermediates. They establish trust only by chaining to an entry in
524    /// [`Self::trusted_certs`].
525    pub lookup_certs: Vec<Vec<u8>>,
526    /// DER-encoded certificates accepted as trust anchors.
527    pub trusted_certs: Vec<Vec<u8>>,
528    /// Caller-owned revocation evidence applied without copying it into each XML source.
529    pub crls: Vec<Vec<u8>>,
530    /// Verification keys addressable by `<KeyName>` content.
531    pub named_keys: HashMap<String, VerificationKey>,
532}
533
534/// Configuration-driven resolver for embedded certificates, DER keys, and key names.
535#[derive(Debug, Clone, Default)]
536pub struct DefaultKeyResolver {
537    config: KeyResolverConfig,
538}
539
540#[derive(Clone, Copy)]
541pub(crate) enum ResolutionScope {
542    Document,
543    Trusted,
544    DocumentPrefix(usize),
545    TrustedPrefix(usize),
546    DocumentSuffix(usize),
547    TrustedSuffix(usize),
548}
549
550/// A partial source scan separates a deferred mismatch from terminal errors.
551/// Continuations may retain the former, but cannot retry the latter.
552pub(crate) struct SourceResolution {
553    pub(crate) key: Option<ResolvedVerificationKey<'static>>,
554    pub(crate) deferred_error: Option<KeyResolutionError>,
555}
556
557impl SourceResolution {
558    pub(crate) fn finish(self) -> Result<Option<ResolvedVerificationKey<'static>>, DsigError> {
559        if let Some(error) = self.deferred_error {
560            return Err(error.into());
561        }
562        Ok(self.key)
563    }
564}
565
566/// Counts candidates actually inspected by one resolver invocation.
567///
568/// Parser cardinality preflights prevent expensive materialization, but do not
569/// replace this runtime accounting: embedded and indirect candidates both
570/// consume resolver work when inspected.
571/// Cumulative source-inspection work shared across one key resolution operation.
572/// Reuse this budget when resolving multiple caller-owned candidate records.
573#[derive(Debug)]
574pub struct InspectedKeyCandidateBudget {
575    maximum: usize,
576    attempted: usize,
577}
578
579impl InspectedKeyCandidateBudget {
580    /// Start shared accounting derived solely from the operation policy.
581    #[must_use]
582    pub fn new(policy: &crate::policy::VerificationPolicy) -> Self {
583        Self {
584            maximum: policy.resources.max_key_candidates,
585            attempted: 0,
586        }
587    }
588
589    /// Reserve one direct-key inspection before copying or decoding it.
590    pub fn charge(&mut self) -> Result<(), DsigError> {
591        self.charge_many(1)
592    }
593
594    pub(crate) fn charge_many(&mut self, count: usize) -> Result<(), DsigError> {
595        if self.attempted > self.maximum || count > self.maximum - self.attempted {
596            return Err(crate::policy::PolicyViolation::ResourceLimit {
597                resource: crate::policy::resource_name::KEY_CANDIDATES,
598                maximum: self.maximum,
599                actual: self.attempted.saturating_add(count),
600            }
601            .into());
602        }
603        debug_assert!(self.attempted <= self.maximum);
604        self.attempted += count;
605        Ok(())
606    }
607}
608
609pub(crate) fn validate_key_info_source_permissions(
610    key_info: &KeyInfo,
611    allowed: crate::policy::KeySourcePolicy,
612) -> Result<(), crate::policy::PolicyViolation> {
613    for source in &key_info.sources {
614        let disabled_reason = match source {
615            KeyInfoSource::X509Data(_) if !allowed.x509_data => {
616                Some("X509Data key sources are disabled")
617            }
618            KeyInfoSource::DerEncodedKeyValue(_) if !allowed.der_encoded_key_value => {
619                Some("DEREncodedKeyValue key sources are disabled")
620            }
621            KeyInfoSource::KeyName(_) if !allowed.key_name => {
622                Some("KeyName key sources are disabled")
623            }
624            KeyInfoSource::KeyValue(_) if !allowed.key_value => {
625                Some("KeyValue key sources are disabled")
626            }
627            KeyInfoSource::KeyInfoReference { .. } if !allowed.key_info_reference => {
628                Some("KeyInfoReference key sources are disabled")
629            }
630            KeyInfoSource::X509Data(_)
631            | KeyInfoSource::DerEncodedKeyValue(_)
632            | KeyInfoSource::KeyName(_)
633            | KeyInfoSource::KeyValue(_)
634            | KeyInfoSource::RetrievalMethod { .. }
635            | KeyInfoSource::KeyInfoReference { .. } => None,
636        };
637        if let Some(reason) = disabled_reason {
638            return Err(crate::policy::PolicyViolation::KeyTrust { reason });
639        }
640    }
641    Ok(())
642}
643
644impl DefaultKeyResolver {
645    /// Resolve another candidate without resetting aggregate inspection work.
646    /// The caller must keep the same budget throughout an operation, including
647    /// failed attempts; policy denials must not be treated as candidate misses.
648    pub fn resolve_with_candidate_budget(
649        &self,
650        key_info: Option<&KeyInfo>,
651        algorithm: SignatureAlgorithm,
652        policy: &crate::policy::VerificationPolicy,
653        provider: &dyn crate::provider::CryptoProvider,
654        candidate_budget: &mut InspectedKeyCandidateBudget,
655    ) -> Result<Option<ResolvedVerificationKey<'static>>, DsigError> {
656        candidate_budget.maximum = candidate_budget
657            .maximum
658            .min(policy.resources.max_key_candidates);
659        candidate_budget.charge_many(0)?;
660        self.resolve_with_trust(
661            key_info,
662            algorithm,
663            policy,
664            provider,
665            candidate_budget,
666            ResolutionScope::Document,
667        )
668    }
669
670    pub(crate) fn resolve_trusted_material_with_candidate_budget(
671        &self,
672        key_info: &KeyInfo,
673        algorithm: SignatureAlgorithm,
674        policy: &crate::policy::VerificationPolicy,
675        provider: &dyn crate::provider::CryptoProvider,
676        candidate_budget: &mut InspectedKeyCandidateBudget,
677    ) -> Result<Option<ResolvedVerificationKey<'static>>, DsigError> {
678        self.resolve_with_trust(
679            Some(key_info),
680            algorithm,
681            policy,
682            provider,
683            candidate_budget,
684            ResolutionScope::Trusted,
685        )
686    }
687
688    pub(crate) fn resolve_sources_with_candidate_budget(
689        &self,
690        key_info: &KeyInfo,
691        algorithm: SignatureAlgorithm,
692        policy: &crate::policy::VerificationPolicy,
693        provider: &dyn crate::provider::CryptoProvider,
694        candidate_budget: &mut InspectedKeyCandidateBudget,
695        scope: ResolutionScope,
696    ) -> Result<SourceResolution, DsigError> {
697        self.resolve_source_range(
698            Some(key_info),
699            algorithm,
700            policy,
701            provider,
702            candidate_budget,
703            scope,
704        )
705    }
706    /// Construct a resolver from explicit caller-owned key and certificate stores.
707    #[must_use]
708    pub fn new(config: KeyResolverConfig) -> Self {
709        Self { config }
710    }
711
712    /// Borrow the active resolver configuration.
713    #[must_use]
714    pub fn config(&self) -> &KeyResolverConfig {
715        &self.config
716    }
717
718    fn check_configured_x509_material(
719        &self,
720        info: &X509DataInfo,
721        resources: &crate::policy::ResourcePolicy,
722        trust: &crate::policy::KeyTrustPolicy,
723        budget: &mut InspectedKeyCandidateBudget,
724        charge_crls: bool,
725    ) -> Result<(), DsigError> {
726        if charge_crls && trust.check_crls && trust.verify_x509_chains {
727            budget.charge_many(self.config.crls.len())?;
728        }
729        let certificates = self
730            .config
731            .trusted_certs
732            .iter()
733            .chain(&self.config.lookup_certs);
734        let crls = self
735            .config
736            .crls
737            .iter()
738            .filter(|_| trust.check_crls && trust.verify_x509_chains);
739        let mut total = 0_usize;
740        // Embedded and configured bytes coexist during chain assembly; this
741        // combined preflight precedes certificate parsing and cloning.
742        for material in info
743            .certificates
744            .iter()
745            .chain(&info.crls)
746            .chain(certificates)
747            .chain(crls)
748        {
749            if material.len() > resources.max_external_resource_bytes {
750                return Err(crate::policy::PolicyViolation::ResourceLimit {
751                    resource: crate::policy::resource_name::EXTERNAL_RESOURCE_BYTES,
752                    maximum: resources.max_external_resource_bytes,
753                    actual: material.len(),
754                }
755                .into());
756            }
757            debug_assert!(total <= resources.max_external_resource_total_bytes);
758            if material.len() > resources.max_external_resource_total_bytes - total {
759                return Err(crate::policy::PolicyViolation::ResourceLimit {
760                    resource: crate::policy::resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
761                    maximum: resources.max_external_resource_total_bytes,
762                    actual: total.saturating_add(material.len()),
763                }
764                .into());
765            }
766            total += material.len();
767        }
768        Ok(())
769    }
770
771    fn resolve_x509(
772        &self,
773        info: &X509DataInfo,
774        algorithm: SignatureAlgorithm,
775        trust: &crate::policy::KeyTrustPolicy,
776        provider: &dyn crate::provider::CryptoProvider,
777        budget: &mut InspectedKeyCandidateBudget,
778    ) -> Result<Option<(VerificationKey, Option<X509TrustEvidence>)>, DsigError> {
779        let mut evidence = None;
780        let effective_trust;
781        let trust = if trust.verify_x509_chains && trust.verification_time.is_none() {
782            effective_trust = {
783                let mut captured = trust.clone();
784                captured.verification_time = Some(SystemTime::now());
785                captured
786            };
787            &effective_trust
788        } else {
789            trust
790        };
791        let certificate_der = if let Some(&signing_index) = info.certificate_chain.first() {
792            if trust.verify_x509_chains {
793                let path =
794                    self.prepare_embedded_x509(info, signing_index, trust, provider, budget)?;
795                evidence = Some(Self::x509_trust_evidence(&path, trust)?);
796            } else {
797                budget.charge_many(info.certificates.len())?;
798            }
799            info.certificates
800                .get(signing_index)
801                .ok_or(KeyResolutionError::InvalidCertificate)?
802                .clone()
803        } else {
804            let Some(selected) = self.resolve_configured_x509(info, trust, provider, budget)?
805            else {
806                return Ok(None);
807            };
808            if trust.verify_x509_chains {
809                evidence = Some(Self::x509_trust_evidence(&selected, trust)?);
810            }
811            selected
812                .certificate_chain
813                .first()
814                .and_then(|index| selected.certificates.get(*index))
815                .ok_or(KeyResolutionError::InvalidCertificate)?
816                .clone()
817        };
818
819        let (rest, certificate) = X509Certificate::from_der(&certificate_der)
820            .map_err(|_| KeyResolutionError::InvalidCertificate)?;
821        if !rest.is_empty() {
822            return Err(KeyResolutionError::InvalidCertificate.into());
823        }
824        let public_key_bytes = certificate.public_key().raw.to_vec();
825        validate_spki_algorithm(&public_key_bytes, algorithm)?;
826        Ok(Some((
827            VerificationKey {
828                algorithm,
829                public_key_bytes,
830                certificate_der: Some(certificate_der),
831                name: None,
832            },
833            evidence,
834        )))
835    }
836
837    fn x509_trust_evidence(
838        info: &X509DataInfo,
839        trust: &crate::policy::KeyTrustPolicy,
840    ) -> Result<X509TrustEvidence, DsigError> {
841        let time = trust
842            .verification_time
843            .ok_or(KeyResolutionError::SystemTime)?;
844        X509TrustEvidence::new(
845            info.certificate_chain
846                .iter()
847                .map(|index| info.certificates[*index].as_slice()),
848            time,
849            trust.check_crls,
850        )
851    }
852
853    fn verify_x509_policy(
854        &self,
855        info: &X509DataInfo,
856        trust: &crate::policy::KeyTrustPolicy,
857        provider: &dyn crate::provider::CryptoProvider,
858    ) -> Result<(), KeyResolutionError> {
859        let options = X509ChainOptions {
860            trusted_certs: &self.config.trusted_certs,
861            verification_time: trust.verification_time.unwrap_or_else(SystemTime::now),
862            max_chain_depth: trust.max_x509_chain_depth,
863            check_crls: trust.check_crls,
864            allowed_extended_key_usages: Some(&trust.allowed_extended_key_usages),
865            rsa_keys: trust.rsa_keys,
866            dsa_keys: trust.dsa_keys,
867            certificate_signature_algorithms: trust.certificate_signature_algorithms.as_ref(),
868        };
869        verify_x509_certificate_chain_with_provider_and_crls(
870            info,
871            &options,
872            provider,
873            &self.config.crls,
874        )?;
875        Ok(())
876    }
877
878    fn prepare_embedded_x509(
879        &self,
880        info: &X509DataInfo,
881        signing_index: usize,
882        trust: &crate::policy::KeyTrustPolicy,
883        provider: &dyn crate::provider::CryptoProvider,
884        budget: &mut InspectedKeyCandidateBudget,
885    ) -> Result<X509DataInfo, DsigError> {
886        let signing_der = info
887            .certificates
888            .get(signing_index)
889            .ok_or(KeyResolutionError::InvalidCertificate)?;
890        let mut available = X509DataInfo {
891            crls: info.crls.clone(),
892            ..X509DataInfo::default()
893        };
894        let mut trusted_prefix_len = 0;
895        for certificate in &self.config.trusted_certs {
896            budget.charge()?;
897            if available
898                .certificates
899                .iter()
900                .any(|known| known == certificate)
901            {
902                continue;
903            }
904            available.parsed_certificates.push(
905                parse_x509_certificate(certificate)
906                    .map_err(|_| KeyResolutionError::InvalidCertificate)?,
907            );
908            available.certificates.push(certificate.clone());
909            trusted_prefix_len += 1;
910        }
911        for certificate in self.config.lookup_certs.iter().chain(&info.certificates) {
912            budget.charge()?;
913            if available
914                .certificates
915                .iter()
916                .any(|known| known == certificate)
917            {
918                continue;
919            }
920            available.parsed_certificates.push(
921                parse_x509_certificate(certificate)
922                    .map_err(|_| KeyResolutionError::InvalidCertificate)?,
923            );
924            available.certificates.push(certificate.clone());
925        }
926        let signing_index = available
927            .certificates
928            .iter()
929            .position(|certificate| certificate == signing_der)
930            .ok_or(KeyResolutionError::InvalidCertificate)?;
931        self.select_valid_x509_path(
932            &mut available,
933            signing_index,
934            trusted_prefix_len,
935            trust,
936            provider,
937            None,
938        )?;
939        Ok(available)
940    }
941
942    fn select_valid_x509_path(
943        &self,
944        available: &mut X509DataInfo,
945        signing_index: usize,
946        trusted_prefix_len: usize,
947        trust: &crate::policy::KeyTrustPolicy,
948        provider: &dyn crate::provider::CryptoProvider,
949        selectors: Option<&X509DataInfo>,
950    ) -> Result<bool, KeyResolutionError> {
951        let candidates = build_x509_certificate_paths_to_trusted_prefix(
952            available,
953            signing_index,
954            trusted_prefix_len,
955            trust.max_x509_chain_depth,
956            trust.max_x509_candidate_paths,
957            provider,
958            trust.certificate_signature_algorithms.as_ref(),
959        )
960        .map_err(|error| match error {
961            X509ChainBuildError::AmbiguousIssuer => KeyResolutionError::AmbiguousCertificate,
962            X509ChainBuildError::Policy(error) => {
963                KeyResolutionError::Chain(super::X509ChainError::Policy(error))
964            }
965            X509ChainBuildError::Provider(error) => {
966                KeyResolutionError::Chain(super::X509ChainError::Provider(error))
967            }
968            X509ChainBuildError::UnsupportedSignatureAlgorithm { oid } => {
969                KeyResolutionError::Chain(super::X509ChainError::UnsupportedSignatureAlgorithm {
970                    oid,
971                })
972            }
973            _ => KeyResolutionError::InvalidCertificate,
974        })?;
975        let mut first_error = None;
976        let mut valid_path_without_selector_match = false;
977        for candidate in candidates {
978            available.certificate_chain = candidate;
979            match self.verify_x509_policy(available, trust, provider) {
980                Ok(()) => {
981                    if match selectors {
982                        Some(selectors) => {
983                            selected_x509_path_matches_selectors(available, selectors, provider)?
984                        }
985                        None => true,
986                    } {
987                        return Ok(true);
988                    }
989                    valid_path_without_selector_match = true;
990                }
991                Err(error) => {
992                    first_error.get_or_insert(error);
993                }
994            }
995        }
996        if valid_path_without_selector_match {
997            return Ok(false);
998        }
999        Err(first_error.unwrap_or(KeyResolutionError::Chain(
1000            super::X509ChainError::UntrustedRoot,
1001        )))
1002    }
1003
1004    fn select_x509_selector_path(
1005        &self,
1006        available: &mut X509DataInfo,
1007        signing_index: usize,
1008        matching_indices: &[usize],
1009        trust: &crate::policy::KeyTrustPolicy,
1010        provider: &dyn crate::provider::CryptoProvider,
1011        selectors: &X509DataInfo,
1012    ) -> Result<bool, KeyResolutionError> {
1013        let targets = matching_indices
1014            .iter()
1015            .copied()
1016            .filter(|index| *index != signing_index)
1017            .collect::<Vec<_>>();
1018        if targets.is_empty() {
1019            return Ok(false);
1020        }
1021        let candidates = build_x509_certificate_paths_to_selector_targets(
1022            available,
1023            signing_index,
1024            &targets,
1025            trust.max_x509_chain_depth,
1026            trust.max_x509_candidate_paths,
1027            provider,
1028            trust.certificate_signature_algorithms.as_ref(),
1029        )
1030        .map_err(|error| match error {
1031            X509ChainBuildError::AmbiguousIssuer => KeyResolutionError::AmbiguousCertificate,
1032            X509ChainBuildError::Policy(error) => {
1033                KeyResolutionError::Chain(super::X509ChainError::Policy(error))
1034            }
1035            X509ChainBuildError::Provider(error) => {
1036                KeyResolutionError::Chain(super::X509ChainError::Provider(error))
1037            }
1038            X509ChainBuildError::UnsupportedSignatureAlgorithm { oid } => {
1039                KeyResolutionError::Chain(super::X509ChainError::UnsupportedSignatureAlgorithm {
1040                    oid,
1041                })
1042            }
1043            _ => KeyResolutionError::InvalidCertificate,
1044        })?;
1045        for candidate in candidates {
1046            available.certificate_chain = candidate;
1047            if selected_x509_path_matches_selectors(available, selectors, provider)? {
1048                return Ok(true);
1049            }
1050        }
1051        Ok(false)
1052    }
1053
1054    fn resolve_configured_x509(
1055        &self,
1056        info: &X509DataInfo,
1057        trust: &crate::policy::KeyTrustPolicy,
1058        provider: &dyn crate::provider::CryptoProvider,
1059        budget: &mut InspectedKeyCandidateBudget,
1060    ) -> Result<Option<X509DataInfo>, DsigError> {
1061        if !x509_data_has_lookup_identifiers(info) {
1062            return Ok(None);
1063        }
1064
1065        let mut available = X509DataInfo {
1066            subject_names: info.subject_names.clone(),
1067            issuer_serials: info.issuer_serials.clone(),
1068            skis: info.skis.clone(),
1069            crls: info.crls.clone(),
1070            digests: info.digests.clone(),
1071            ..X509DataInfo::default()
1072        };
1073        let mut matches = Vec::new();
1074        let mut trusted_prefix_len = 0usize;
1075        for (trusted, certificate_der) in self
1076            .config
1077            .trusted_certs
1078            .iter()
1079            .map(|certificate| (true, certificate))
1080            .chain(
1081                self.config
1082                    .lookup_certs
1083                    .iter()
1084                    .map(|certificate| (false, certificate)),
1085            )
1086        {
1087            budget.charge()?;
1088            if available
1089                .certificates
1090                .iter()
1091                .any(|available_der| available_der == certificate_der)
1092            {
1093                continue;
1094            }
1095            let parsed = parse_x509_certificate(certificate_der)
1096                .map_err(|_| KeyResolutionError::InvalidCertificate)?;
1097            let is_match =
1098                x509_certificate_matches_any_selector(info, &parsed, certificate_der, provider)
1099                    .map_err(map_x509_selector_error)?;
1100            if is_match {
1101                matches.push((available.certificates.len(), parsed.clone()));
1102            }
1103            available.certificates.push(certificate_der.clone());
1104            available.parsed_certificates.push(parsed);
1105            if trusted {
1106                trusted_prefix_len += 1;
1107            }
1108        }
1109
1110        let matched_chain = X509DataInfo {
1111            certificates: matches
1112                .iter()
1113                .map(|(index, _)| available.certificates[*index].clone())
1114                .collect(),
1115            parsed_certificates: matches.iter().map(|(_, parsed)| parsed.clone()).collect(),
1116            ..X509DataInfo::default()
1117        };
1118        if !x509_selector_categories_match_chain(
1119            &X509DataInfo {
1120                subject_names: info.subject_names.clone(),
1121                issuer_serials: info.issuer_serials.clone(),
1122                skis: info.skis.clone(),
1123                digests: info.digests.clone(),
1124                ..matched_chain
1125            },
1126            provider,
1127        )
1128        .map_err(map_x509_selector_error)?
1129        {
1130            return Ok(None);
1131        }
1132
1133        let signing_index = match matches.as_slice() {
1134            [] => return Ok(None),
1135            [(index, _)] => *index,
1136            _ => {
1137                let leaves = matches
1138                    .iter()
1139                    .filter(|(_, candidate)| {
1140                        !distinguished_names_equal(&candidate.subject_dn, &candidate.issuer_dn)
1141                            && !matches.iter().any(|(_, other)| {
1142                                distinguished_names_equal(&other.issuer_dn, &candidate.subject_dn)
1143                            })
1144                    })
1145                    .collect::<Vec<_>>();
1146                match leaves.as_slice() {
1147                    [(index, _)] => *index,
1148                    _ => return Err(KeyResolutionError::AmbiguousCertificate.into()),
1149                }
1150            }
1151        };
1152        let matching_indices = matches.iter().map(|(index, _)| *index).collect::<Vec<_>>();
1153        // `available` preserves trusted certificates as a prefix. Selecting
1154        // one of those exact certificates is already a terminal trust
1155        // decision, even when the certificate is not self-signed.
1156        available.certificate_chain =
1157            if signing_index < trusted_prefix_len || !trust.verify_x509_chains {
1158                vec![signing_index]
1159            } else {
1160                if !self.select_valid_x509_path(
1161                    &mut available,
1162                    signing_index,
1163                    trusted_prefix_len,
1164                    trust,
1165                    provider,
1166                    Some(info),
1167                )? {
1168                    return Ok(None);
1169                }
1170                available.certificate_chain.clone()
1171            };
1172        if trust.verify_x509_chains && signing_index < trusted_prefix_len {
1173            self.verify_x509_policy(&available, trust, provider)?;
1174        }
1175        if !trust.verify_x509_chains || signing_index < trusted_prefix_len {
1176            let direct_match = selected_x509_path_matches_selectors(&available, info, provider)?;
1177            if !direct_match
1178                && (signing_index < trusted_prefix_len
1179                    || !self.select_x509_selector_path(
1180                        &mut available,
1181                        signing_index,
1182                        &matching_indices,
1183                        trust,
1184                        provider,
1185                        info,
1186                    )?)
1187            {
1188                return Ok(None);
1189            }
1190        }
1191        Ok(Some(available))
1192    }
1193
1194    fn resolve_key_value(
1195        key_value: &KeyValueInfo,
1196        algorithm: SignatureAlgorithm,
1197    ) -> Result<Option<VerificationKey>, KeyResolutionError> {
1198        let public_key_bytes = match key_value {
1199            KeyValueInfo::Dsa { p, q, g, y } => {
1200                if !matches!(
1201                    algorithm,
1202                    SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256
1203                ) {
1204                    return Err(KeyResolutionError::AlgorithmMismatch);
1205                }
1206                let (Some(p), Some(q), Some(g)) = (p.as_deref(), q.as_deref(), g.as_deref()) else {
1207                    return Err(KeyResolutionError::InvalidPublicKey);
1208                };
1209                dsa_key_value_to_spki_der(p, q, g, y)?
1210            }
1211            KeyValueInfo::Rsa { modulus, exponent } => {
1212                if !matches!(
1213                    algorithm,
1214                    SignatureAlgorithm::RsaSha1
1215                        | SignatureAlgorithm::RsaSha224
1216                        | SignatureAlgorithm::RsaSha256
1217                        | SignatureAlgorithm::RsaSha384
1218                        | SignatureAlgorithm::RsaSha512
1219                ) {
1220                    return Err(KeyResolutionError::AlgorithmMismatch);
1221                }
1222                rsa_key_value_to_spki_der(modulus, exponent)?
1223            }
1224            KeyValueInfo::Ec {
1225                curve_oid,
1226                public_key,
1227            } => {
1228                if algorithm.ecdsa_digest().is_none() {
1229                    return Ok(None);
1230                }
1231                ec_key_value_to_spki_der(curve_oid, public_key)?
1232            }
1233            KeyValueInfo::InvalidEcKeyValue => return Err(KeyResolutionError::InvalidPublicKey),
1234            KeyValueInfo::Unsupported { .. } => return Ok(None),
1235        };
1236        validate_spki_algorithm(&public_key_bytes, algorithm)?;
1237
1238        Ok(Some(VerificationKey {
1239            algorithm,
1240            public_key_bytes,
1241            certificate_der: None,
1242            name: None,
1243        }))
1244    }
1245
1246    fn resolve_with_trust(
1247        &self,
1248        key_info: Option<&KeyInfo>,
1249        algorithm: SignatureAlgorithm,
1250        policy: &crate::policy::VerificationPolicy,
1251        provider: &dyn crate::provider::CryptoProvider,
1252        candidate_budget: &mut InspectedKeyCandidateBudget,
1253        scope: ResolutionScope,
1254    ) -> Result<Option<ResolvedVerificationKey<'static>>, DsigError> {
1255        self.resolve_source_range(
1256            key_info,
1257            algorithm,
1258            policy,
1259            provider,
1260            candidate_budget,
1261            scope,
1262        )?
1263        .finish()
1264    }
1265
1266    fn resolve_source_range(
1267        &self,
1268        key_info: Option<&KeyInfo>,
1269        algorithm: SignatureAlgorithm,
1270        policy: &crate::policy::VerificationPolicy,
1271        provider: &dyn crate::provider::CryptoProvider,
1272        candidate_budget: &mut InspectedKeyCandidateBudget,
1273        scope: ResolutionScope,
1274    ) -> Result<SourceResolution, DsigError> {
1275        let trust = &policy.key_trust;
1276        let resources = &policy.resources;
1277        trust.validate()?;
1278        resources.validate()?;
1279        let Some(key_info) = key_info else {
1280            return Ok(SourceResolution {
1281                key: None,
1282                deferred_error: None,
1283            });
1284        };
1285        let document_sources = matches!(
1286            scope,
1287            ResolutionScope::Document
1288                | ResolutionScope::DocumentPrefix(_)
1289                | ResolutionScope::DocumentSuffix(_)
1290        );
1291        if document_sources {
1292            validate_key_info_source_permissions(key_info, policy.key_sources)?;
1293        }
1294        let source_end = match scope {
1295            ResolutionScope::DocumentPrefix(end) | ResolutionScope::TrustedPrefix(end) => end,
1296            _ => key_info.sources.len(),
1297        };
1298        let source_start = match scope {
1299            ResolutionScope::DocumentSuffix(start) | ResolutionScope::TrustedSuffix(start) => start,
1300            _ => 0,
1301        };
1302        let mut deferred_key_value_error = None;
1303        let mut configured_material_checked = false;
1304        for source in &key_info.sources[source_start..source_end] {
1305            if !document_sources && matches!(source, KeyInfoSource::KeyName(_)) {
1306                continue;
1307            }
1308            let mut path_evidence = None;
1309            let mut caller_trusted =
1310                !document_sources || matches!(source, KeyInfoSource::KeyName(_));
1311            let resolved = match source {
1312                KeyInfoSource::X509Data(info) => {
1313                    if if info.certificate_chain.is_empty() {
1314                        x509_data_has_lookup_identifiers(info)
1315                    } else {
1316                        trust.verify_x509_chains
1317                    } {
1318                        self.check_configured_x509_material(
1319                            info,
1320                            resources,
1321                            trust,
1322                            candidate_budget,
1323                            !configured_material_checked,
1324                        )?;
1325                        configured_material_checked = true;
1326                    }
1327                    match self.resolve_x509(info, algorithm, trust, provider, candidate_budget)? {
1328                        Some((key, evidence)) => {
1329                            if evidence.is_none()
1330                                && !caller_trusted
1331                                && trust.mode
1332                                    == crate::policy::VerificationTrustMode::RequireTrustedKey
1333                                && let Some(der) = key.certificate_der.as_ref()
1334                            {
1335                                // Pin lookup shares the same work allowance as
1336                                // discovery; charge before each DER comparison.
1337                                for anchor in &self.config.trusted_certs {
1338                                    candidate_budget.charge()?;
1339                                    if anchor == der {
1340                                        caller_trusted = true;
1341                                        break;
1342                                    }
1343                                }
1344                            }
1345                            path_evidence = evidence;
1346                            Some(key)
1347                        }
1348                        None => None,
1349                    }
1350                }
1351                KeyInfoSource::DerEncodedKeyValue(public_key_bytes) => {
1352                    candidate_budget.charge()?;
1353                    validate_spki_algorithm(public_key_bytes, algorithm)?;
1354                    Some(VerificationKey {
1355                        algorithm,
1356                        public_key_bytes: public_key_bytes.clone(),
1357                        certificate_der: None,
1358                        name: None,
1359                    })
1360                }
1361                KeyInfoSource::KeyName(name) => {
1362                    candidate_budget.charge()?;
1363                    self.config
1364                        .named_keys
1365                        .get(name)
1366                        .map(|key| {
1367                            if key.algorithm != algorithm {
1368                                return Err(KeyResolutionError::AlgorithmMismatch);
1369                            }
1370                            validate_spki_algorithm(&key.public_key_bytes, algorithm)?;
1371                            Ok(key.clone())
1372                        })
1373                        .transpose()?
1374                }
1375                KeyInfoSource::KeyValue(key_value) => {
1376                    candidate_budget.charge()?;
1377                    match Self::resolve_key_value(key_value, algorithm) {
1378                        Ok(resolved) => resolved,
1379                        Err(error) if key_value_error_allows_fallback(key_value, &error) => {
1380                            deferred_key_value_error.get_or_insert(error);
1381                            None
1382                        }
1383                        Err(error) => return Err(error.into()),
1384                    }
1385                }
1386                KeyInfoSource::RetrievalMethod { .. } => {
1387                    candidate_budget.charge()?;
1388                    None
1389                }
1390                KeyInfoSource::KeyInfoReference { .. } => {
1391                    candidate_budget.charge()?;
1392                    None
1393                }
1394            };
1395            if let Some(key) = resolved {
1396                let key = Box::new(PolicyBoundVerificationKey {
1397                    key,
1398                    rsa_minimum_bits: trust.rsa_keys.minimum_modulus_bits,
1399                    dsa_minimum_bits: trust.dsa_keys.minimum_modulus_bits,
1400                });
1401                let key = if policy.key_trust.mode
1402                    == crate::policy::VerificationTrustMode::CryptographicOnly
1403                {
1404                    ResolvedVerificationKey::Candidate(key)
1405                } else if let Some(evidence) = path_evidence {
1406                    ResolvedVerificationKey::Validated(TrustedPublicKey::new(
1407                        key,
1408                        trust.clone(),
1409                        evidence,
1410                    ))
1411                } else if caller_trusted {
1412                    ResolvedVerificationKey::CallerTrusted(key)
1413                } else {
1414                    ResolvedVerificationKey::Candidate(key)
1415                };
1416                return Ok(SourceResolution {
1417                    key: Some(key),
1418                    deferred_error: None,
1419                });
1420            }
1421        }
1422        Ok(SourceResolution {
1423            key: None,
1424            deferred_error: deferred_key_value_error,
1425        })
1426    }
1427}
1428
1429impl KeyResolver for DefaultKeyResolver {
1430    fn resolve_for_verification<'a>(
1431        &'a self,
1432        key_info: Option<&KeyInfo>,
1433        algorithm: SignatureAlgorithm,
1434        policy: &crate::policy::VerificationPolicy,
1435        provider: &dyn crate::provider::CryptoProvider,
1436    ) -> Result<Option<ResolvedVerificationKey<'a>>, DsigError> {
1437        let mut budget = InspectedKeyCandidateBudget::new(policy);
1438        self.resolve_with_candidate_budget(key_info, algorithm, policy, provider, &mut budget)
1439    }
1440    fn resolve<'a>(
1441        &'a self,
1442        key_info: Option<&KeyInfo>,
1443        algorithm: SignatureAlgorithm,
1444    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1445        let policy = crate::policy::VerificationPolicy::default();
1446        let mut candidate_budget = InspectedKeyCandidateBudget::new(&policy);
1447        self.resolve_with_trust(
1448            key_info,
1449            algorithm,
1450            &policy,
1451            crate::provider::default_provider(),
1452            &mut candidate_budget,
1453            ResolutionScope::Document,
1454        )
1455        .map(|key| key.map(ResolvedVerificationKey::into_key))
1456    }
1457
1458    fn resolve_with_policy<'a>(
1459        &'a self,
1460        key_info: Option<&KeyInfo>,
1461        algorithm: SignatureAlgorithm,
1462        policy: &crate::policy::VerificationPolicy,
1463    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1464        self.resolve_with_policy_and_provider(
1465            key_info,
1466            algorithm,
1467            policy,
1468            crate::provider::default_provider(),
1469        )
1470    }
1471
1472    fn resolve_with_policy_and_provider<'a>(
1473        &'a self,
1474        key_info: Option<&KeyInfo>,
1475        algorithm: SignatureAlgorithm,
1476        policy: &crate::policy::VerificationPolicy,
1477        provider: &dyn crate::provider::CryptoProvider,
1478    ) -> Result<Option<Box<dyn VerifyingKey + 'a>>, DsigError> {
1479        let mut candidate_budget = InspectedKeyCandidateBudget::new(policy);
1480        self.resolve_with_candidate_budget(
1481            key_info,
1482            algorithm,
1483            policy,
1484            provider,
1485            &mut candidate_budget,
1486        )
1487        .map(|key| key.map(ResolvedVerificationKey::into_key))
1488    }
1489
1490    fn consumes_document_key_info(&self) -> bool {
1491        true
1492    }
1493}
1494
1495fn map_x509_selector_error(error: ParseError) -> DsigError {
1496    match error {
1497        ParseError::Provider(error) => DsigError::Provider(error),
1498        ParseError::UnsupportedAlgorithm { uri } => {
1499            KeyResolutionError::UnsupportedDigestAlgorithm(uri).into()
1500        }
1501        _ => KeyResolutionError::InvalidCertificate.into(),
1502    }
1503}
1504
1505fn selected_x509_path_matches_selectors(
1506    available: &X509DataInfo,
1507    selectors: &X509DataInfo,
1508    provider: &dyn crate::provider::CryptoProvider,
1509) -> Result<bool, KeyResolutionError> {
1510    let selected = X509DataInfo {
1511        subject_names: selectors.subject_names.clone(),
1512        issuer_serials: selectors.issuer_serials.clone(),
1513        skis: selectors.skis.clone(),
1514        digests: selectors.digests.clone(),
1515        certificates: available
1516            .certificate_chain
1517            .iter()
1518            .map(|index| available.certificates[*index].clone())
1519            .collect(),
1520        parsed_certificates: available
1521            .certificate_chain
1522            .iter()
1523            .map(|index| available.parsed_certificates[*index].clone())
1524            .collect(),
1525        ..X509DataInfo::default()
1526    };
1527    x509_selector_categories_match_chain(&selected, provider).map_err(|error| match error {
1528        ParseError::Provider(error) => {
1529            KeyResolutionError::Chain(super::X509ChainError::Provider(error))
1530        }
1531        ParseError::UnsupportedAlgorithm { uri } => {
1532            KeyResolutionError::UnsupportedDigestAlgorithm(uri)
1533        }
1534        _ => KeyResolutionError::InvalidCertificate,
1535    })
1536}
1537
1538fn rsa_key_value_to_spki_der(
1539    modulus: &[u8],
1540    exponent: &[u8],
1541) -> Result<Vec<u8>, KeyResolutionError> {
1542    let (modulus, exponent) = bounded_rsa_public_components(modulus, exponent)?;
1543    let key = rsa::RsaPublicKey::new(
1544        BoxedUint::from_be_slice_vartime(modulus),
1545        BoxedUint::from_be_slice_vartime(exponent),
1546    )
1547    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1548    key.to_public_key_der()
1549        .map_err(|_| KeyResolutionError::InvalidPublicKey)
1550        .map(|der| der.as_bytes().to_vec())
1551}
1552
1553pub(crate) fn bounded_rsa_public_components<'a>(
1554    modulus: &'a [u8],
1555    exponent: &'a [u8],
1556) -> Result<(&'a [u8], &'a [u8]), KeyResolutionError> {
1557    let modulus = &modulus[modulus
1558        .iter()
1559        .position(|byte| *byte != 0)
1560        .unwrap_or(modulus.len())..];
1561    let exponent = &exponent[exponent
1562        .iter()
1563        .position(|byte| *byte != 0)
1564        .unwrap_or(exponent.len())..];
1565    let maximum = crate::hard_limits::RSA_MODULUS_BIT_CEILING;
1566    if modulus.is_empty()
1567        || exponent.is_empty()
1568        || modulus.len() > maximum.div_ceil(8)
1569        || exponent.len() > maximum.div_ceil(8)
1570        || (modulus.len() * 8 - modulus[0].leading_zeros() as usize) > maximum
1571    {
1572        return Err(KeyResolutionError::InvalidPublicKey);
1573    }
1574    Ok((modulus, exponent))
1575}
1576
1577fn dsa_key_value_to_spki_der(
1578    p: &[u8],
1579    q: &[u8],
1580    g: &[u8],
1581    y: &[u8],
1582) -> Result<Vec<u8>, KeyResolutionError> {
1583    // Bound borrowed unsigned components before any bigint allocation or
1584    // subgroup exponentiation, not only after the SPKI has been produced.
1585    let trim = |bytes: &[u8]| bytes.iter().take_while(|byte| **byte == 0).count();
1586    let p = &p[trim(p)..];
1587    let q = &q[trim(q)..];
1588    let g = &g[trim(g)..];
1589    let y = &y[trim(y)..];
1590    for value in [p, q, g, y] {
1591        if value.is_empty() || value.len() > crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING {
1592            return Err(KeyResolutionError::InvalidPublicKey);
1593        }
1594    }
1595    let components = dsa::Components::from_components(
1596        BoxedUint::from_be_slice_vartime(p),
1597        BoxedUint::from_be_slice_vartime(q),
1598        BoxedUint::from_be_slice_vartime(g),
1599    )
1600    .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1601    dsa::VerifyingKey::from_components(components, BoxedUint::from_be_slice_vartime(y))
1602        .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1603        .to_public_key_der()
1604        .map_err(|_| KeyResolutionError::InvalidPublicKey)
1605        .map(|der| der.as_bytes().to_vec())
1606}
1607
1608fn ec_key_value_to_spki_der(
1609    curve_oid: &str,
1610    public_key: &[u8],
1611) -> Result<Vec<u8>, KeyResolutionError> {
1612    match curve_oid {
1613        EC_P256_OID => p256::PublicKey::from_sec1_bytes(public_key)
1614            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1615            .to_public_key_der()
1616            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1617            .map(|der| der.as_bytes().to_vec()),
1618        EC_P384_OID => p384::PublicKey::from_sec1_bytes(public_key)
1619            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1620            .to_public_key_der()
1621            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1622            .map(|der| der.as_bytes().to_vec()),
1623        EC_P521_OID => p521::PublicKey::from_sec1_bytes(public_key)
1624            .map_err(|_| KeyResolutionError::InvalidPublicKey)?
1625            .to_public_key_der()
1626            .map_err(|_| KeyResolutionError::InvalidPublicKey)
1627            .map(|der| der.as_bytes().to_vec()),
1628        _ => Err(KeyResolutionError::InvalidPublicKey),
1629    }
1630}
1631
1632fn key_value_error_allows_fallback(key_value: &KeyValueInfo, error: &KeyResolutionError) -> bool {
1633    matches!(
1634        key_value,
1635        KeyValueInfo::Dsa { .. } | KeyValueInfo::Ec { .. } | KeyValueInfo::InvalidEcKeyValue
1636    ) && matches!(
1637        error,
1638        KeyResolutionError::InvalidPublicKey | KeyResolutionError::AlgorithmMismatch
1639    )
1640}
1641
1642fn validate_spki_algorithm(
1643    public_key_bytes: &[u8],
1644    algorithm: SignatureAlgorithm,
1645) -> Result<(), KeyResolutionError> {
1646    if algorithm.eddsa_signature_len().is_some()
1647        || matches!(algorithm, SignatureAlgorithm::PostQuantum(_))
1648    {
1649        return super::modern::validate_public_key(algorithm, public_key_bytes)
1650            .map_err(|_| KeyResolutionError::InvalidPublicKey);
1651    }
1652    let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_bytes)
1653        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1654    if !rest.is_empty() {
1655        return Err(KeyResolutionError::InvalidPublicKey);
1656    }
1657    let parsed = spki
1658        .parsed()
1659        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1660    let curve_oid = spki
1661        .algorithm
1662        .parameters
1663        .as_ref()
1664        .and_then(|value| value.as_oid().ok())
1665        .map(|oid| oid.to_id_string());
1666    match (algorithm, parsed) {
1667        (SignatureAlgorithm::DsaSha1 | SignatureAlgorithm::DsaSha256, PublicKey::DSA(_)) => {
1668            let _ = decode_dsa_verifying_key(public_key_bytes)
1669                .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1670            Ok(())
1671        }
1672        (
1673            SignatureAlgorithm::RsaSha1
1674            | SignatureAlgorithm::RsaSha224
1675            | SignatureAlgorithm::RsaSha256
1676            | SignatureAlgorithm::RsaSha384
1677            | SignatureAlgorithm::RsaSha512,
1678            PublicKey::RSA(_),
1679        ) => Ok(()),
1680        (
1681            SignatureAlgorithm::EcdsaSha1
1682            | SignatureAlgorithm::EcdsaSha224
1683            | SignatureAlgorithm::EcdsaSha256
1684            | SignatureAlgorithm::EcdsaSha384
1685            | SignatureAlgorithm::EcdsaSha512
1686            | SignatureAlgorithm::EcdsaSha3_224
1687            | SignatureAlgorithm::EcdsaSha3_256
1688            | SignatureAlgorithm::EcdsaSha3_384
1689            | SignatureAlgorithm::EcdsaSha3_512,
1690            PublicKey::EC(ec),
1691        ) if matches!(
1692            curve_oid.as_deref(),
1693            Some(EC_P256_OID | EC_P384_OID | EC_P521_OID)
1694        ) =>
1695        {
1696            validate_ec_public_key_encoding(&ec, spki.subject_public_key.data.as_ref())
1697                .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1698            validate_ec_point(curve_oid.as_deref(), spki.subject_public_key.data.as_ref())?;
1699            Ok(())
1700        }
1701        _ => Err(KeyResolutionError::AlgorithmMismatch),
1702    }
1703}
1704
1705pub(crate) fn supported_parsed_spki_is_rsa(
1706    spki: &SubjectPublicKeyInfo<'_>,
1707    public_key_bytes: &[u8],
1708) -> Result<bool, KeyResolutionError> {
1709    let borrowed = pkcs8::SubjectPublicKeyInfoRef::try_from(public_key_bytes)
1710        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1711    if let Some(parameter) = super::PqAlgorithm::from_oid(borrowed.algorithm.oid) {
1712        super::modern::validate_public_key(
1713            SignatureAlgorithm::PostQuantum(parameter),
1714            public_key_bytes,
1715        )
1716        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1717        return Ok(false);
1718    }
1719    let eddsa = match spki.algorithm.algorithm.to_id_string().as_str() {
1720        "1.3.101.112" => Some(SignatureAlgorithm::Ed25519),
1721        "1.3.101.113" => Some(SignatureAlgorithm::Ed448),
1722        _ => None,
1723    };
1724    if let Some(algorithm) = eddsa {
1725        super::modern::validate_public_key(algorithm, public_key_bytes)
1726            .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1727        return Ok(false);
1728    }
1729    let parsed = spki
1730        .parsed()
1731        .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1732    match parsed {
1733        PublicKey::RSA(key) => {
1734            bounded_rsa_public_components(key.modulus, key.exponent)?;
1735            rsa::RsaPublicKey::from_public_key_der(public_key_bytes)
1736                .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1737            Ok(true)
1738        }
1739        PublicKey::DSA(_) => {
1740            let _ = decode_dsa_verifying_key(public_key_bytes)
1741                .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1742            Ok(false)
1743        }
1744        PublicKey::EC(ec) => {
1745            validate_ec_public_key_encoding(&ec, spki.subject_public_key.data.as_ref())
1746                .map_err(|_| KeyResolutionError::InvalidPublicKey)?;
1747            let curve_oid = spki
1748                .algorithm
1749                .parameters
1750                .as_ref()
1751                .and_then(|value| value.as_oid().ok())
1752                .map(|oid| oid.to_id_string());
1753            validate_ec_point(curve_oid.as_deref(), spki.subject_public_key.data.as_ref())?;
1754            Ok(false)
1755        }
1756        _ => Err(KeyResolutionError::AlgorithmMismatch),
1757    }
1758}
1759
1760fn validate_ec_point(curve_oid: Option<&str>, point: &[u8]) -> Result<(), KeyResolutionError> {
1761    match curve_oid {
1762        Some(EC_P256_OID) => p256::PublicKey::from_sec1_bytes(point).map(|_| ()),
1763        Some(EC_P384_OID) => p384::PublicKey::from_sec1_bytes(point).map(|_| ()),
1764        Some(EC_P521_OID) => p521::PublicKey::from_sec1_bytes(point).map(|_| ()),
1765        _ => return Err(KeyResolutionError::AlgorithmMismatch),
1766    }
1767    .map_err(|_| KeyResolutionError::InvalidPublicKey)
1768}
1769
1770pub(crate) fn supported_key_value_is_rsa(value: &KeyValueInfo) -> Result<bool, KeyResolutionError> {
1771    let (spki, is_rsa) = match value {
1772        KeyValueInfo::Rsa { modulus, exponent } => {
1773            (rsa_key_value_to_spki_der(modulus, exponent)?, true)
1774        }
1775        KeyValueInfo::Dsa {
1776            p: Some(p),
1777            q: Some(q),
1778            g: Some(g),
1779            y,
1780        } => (dsa_key_value_to_spki_der(p, q, g, y)?, false),
1781        KeyValueInfo::Ec {
1782            curve_oid,
1783            public_key,
1784        } => (ec_key_value_to_spki_der(curve_oid, public_key)?, false),
1785        _ => return Err(KeyResolutionError::InvalidPublicKey),
1786    };
1787    let algorithm = if is_rsa {
1788        SignatureAlgorithm::RsaSha256
1789    } else if matches!(value, KeyValueInfo::Dsa { .. }) {
1790        SignatureAlgorithm::DsaSha256
1791    } else {
1792        SignatureAlgorithm::EcdsaSha256
1793    };
1794    validate_spki_algorithm(&spki, algorithm)?;
1795    Ok(is_rsa)
1796}
1797
1798#[cfg(test)]
1799mod tests {
1800    use crate::xml::dom as roxmltree;
1801    use std::sync::atomic::{AtomicUsize, Ordering};
1802
1803    use base64::{Engine, engine::general_purpose::STANDARD};
1804    use der::Decode as _;
1805    use rcgen::{
1806        CertificateRevocationListParams, Issuer, KeyIdMethod, KeyPair, KeyUsagePurpose,
1807        RevokedCertParams, SerialNumber, date_time_ymd,
1808    };
1809    use rsa::{pkcs8::DecodePublicKey, traits::PublicKeyParts};
1810
1811    use super::*;
1812
1813    #[test]
1814    fn shared_candidate_budget_cannot_relax_active_policy() {
1815        // A policy-derived budget cannot override policy, nor can a
1816        // later tighter snapshot forget work already performed.
1817        let resolver = DefaultKeyResolver::new(KeyResolverConfig::default());
1818        let info = KeyInfo {
1819            sources: vec![KeyInfoSource::KeyName("missing".into())],
1820        };
1821        let mut policy = crate::policy::VerificationPolicy::default();
1822        policy.resources.max_key_candidates = 1;
1823        let mut budget = InspectedKeyCandidateBudget::new(&policy);
1824        assert!(
1825            resolver
1826                .resolve_with_candidate_budget(
1827                    Some(&info),
1828                    SignatureAlgorithm::RsaSha256,
1829                    &policy,
1830                    crate::provider::default_provider(),
1831                    &mut budget
1832                )
1833                .expect("first candidate fits the active policy")
1834                .is_none()
1835        );
1836        assert!(matches!(
1837            resolver.resolve_with_candidate_budget(
1838                Some(&info),
1839                SignatureAlgorithm::RsaSha256,
1840                &policy,
1841                crate::provider::default_provider(),
1842                &mut budget
1843            ),
1844            Err(DsigError::Policy(
1845                crate::policy::PolicyViolation::ResourceLimit {
1846                    maximum: 1,
1847                    actual: 2,
1848                    ..
1849                }
1850            ))
1851        ));
1852        let mut spent =
1853            InspectedKeyCandidateBudget::new(&crate::policy::VerificationPolicy::default());
1854        spent
1855            .charge_many(2)
1856            .expect("initial budget admits two candidates");
1857        assert!(matches!(
1858            resolver.resolve_with_candidate_budget(
1859                None,
1860                SignatureAlgorithm::RsaSha256,
1861                &policy,
1862                crate::provider::default_provider(),
1863                &mut spent
1864            ),
1865            Err(DsigError::Policy(
1866                crate::policy::PolicyViolation::ResourceLimit {
1867                    maximum: 1,
1868                    actual: 2,
1869                    ..
1870                }
1871            ))
1872        ));
1873    }
1874
1875    #[test]
1876    fn dsa_key_value_components_are_bounded_before_bigint_decode() {
1877        // Every unsigned XML component uses the same pre-conversion ceiling;
1878        // redundant zero padding must not inflate bigint precision or change it.
1879        let public = dsa::VerifyingKey::from_public_key_pem(include_str!(
1880            "../../tests/fixtures/keys/dsa/dsa-2048-public.pem"
1881        ))
1882        .expect("DSA fixture");
1883        let components = public.components();
1884        let values = [
1885            components.p().to_be_bytes_trimmed_vartime().to_vec(),
1886            components.q().to_be_bytes_trimmed_vartime().to_vec(),
1887            components.g().to_be_bytes_trimmed_vartime().to_vec(),
1888            public.y().to_be_bytes_trimmed_vartime().to_vec(),
1889        ];
1890        let expected = dsa_key_value_to_spki_der(&values[0], &values[1], &values[2], &values[3])
1891            .expect("valid DSA");
1892        for index in 0..4 {
1893            let mut oversized = values.clone();
1894            oversized[index] = vec![1; crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING + 1];
1895            assert!(
1896                dsa_key_value_to_spki_der(
1897                    &oversized[0],
1898                    &oversized[1],
1899                    &oversized[2],
1900                    &oversized[3]
1901                )
1902                .is_err()
1903            );
1904        }
1905        let padded = values.map(|value| {
1906            let mut padded = vec![0; 1024];
1907            padded.extend(value);
1908            padded
1909        });
1910        assert_eq!(
1911            dsa_key_value_to_spki_der(&padded[0], &padded[1], &padded[2], &padded[3])
1912                .expect("zero padding preserves unsigned DSA value"),
1913            expected
1914        );
1915    }
1916
1917    #[test]
1918    fn xml_rsa_components_are_bounded_before_bigint_decode() {
1919        // KeyValue import must reject oversized decoded modulus before conversion.
1920        let oversized = vec![1_u8; crate::hard_limits::RSA_MODULUS_BIT_CEILING.div_ceil(8) + 1];
1921        assert!(matches!(
1922            rsa_key_value_to_spki_der(&oversized, &[1, 0, 1]),
1923            Err(KeyResolutionError::InvalidPublicKey)
1924        ));
1925    }
1926
1927    #[test]
1928    fn oversized_dsa_spki_parameter_is_rejected_before_bigint_decode() {
1929        // A bounded SPKI may still contain a parameter much larger than the
1930        // non-configurable DSA component ceiling.
1931        let oversized = vec![1_u8; crate::hard_limits::DSA_KEY_COMPONENT_BYTE_CEILING + 1];
1932        let one = [1_u8];
1933        let params = der::Encode::to_der(&super::super::signature::BorrowedDsaPublicParameters {
1934            p: der::asn1::UintRef::new(&oversized).expect("positive P"),
1935            q: der::asn1::UintRef::new(&one).expect("positive Q"),
1936            g: der::asn1::UintRef::new(&one).expect("positive G"),
1937        })
1938        .expect("parameters encode");
1939        let y = der::Encode::to_der(&der::asn1::UintRef::new(&one).expect("positive Y"))
1940            .expect("public value encodes");
1941        let spki = rsa::pkcs8::SubjectPublicKeyInfoRef {
1942            algorithm: rsa::pkcs8::AlgorithmIdentifierRef {
1943                oid: dsa::OID,
1944                parameters: Some(der::asn1::AnyRef::from_der(&params).expect("parameters")),
1945            },
1946            subject_public_key: der::asn1::BitStringRef::new(0, &y).expect("bit string"),
1947        };
1948        let encoded = der::Encode::to_der(&spki).expect("SPKI encodes");
1949        assert!(matches!(
1950            decode_dsa_verifying_key(&encoded),
1951            Err(super::super::signature::SignatureVerificationError::InvalidKeyDer)
1952        ));
1953        // Ordinary verification must use the same borrowed preflight, not
1954        // reject only after an allocating crypto decoder reports mismatch.
1955        assert!(matches!(
1956            validate_spki_algorithm(&encoded, SignatureAlgorithm::DsaSha256),
1957            Err(KeyResolutionError::InvalidPublicKey)
1958        ));
1959    }
1960
1961    struct RejectSecondSha512Provider {
1962        sha512_calls: AtomicUsize,
1963        verification_calls: AtomicUsize,
1964        reject_verification_call: Option<usize>,
1965        rejected_verification_data: Option<Vec<u8>>,
1966    }
1967
1968    impl crate::provider::CryptoProvider for RejectSecondSha512Provider {
1969        fn name(&self) -> &'static str {
1970            "reject-second-sha512"
1971        }
1972
1973        fn supports(&self, capability: crate::provider::ProviderCapability<'_>) -> bool {
1974            crate::provider::default_provider().supports(capability)
1975        }
1976
1977        fn fill_random(&self, output: &mut [u8]) -> Result<(), crate::provider::ProviderError> {
1978            crate::provider::default_provider().fill_random(output)
1979        }
1980
1981        fn derive_key(
1982            &self,
1983            parameters: &crate::provider::KdfParameters<'_>,
1984            secret: &[u8],
1985        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1986            crate::provider::default_provider().derive_key(parameters, secret)
1987        }
1988
1989        fn digest(
1990            &self,
1991            algorithm: super::super::DigestAlgorithm,
1992            data: &[u8],
1993        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
1994            if algorithm == super::super::DigestAlgorithm::Sha512
1995                && self.sha512_calls.fetch_add(1, Ordering::Relaxed) > 0
1996            {
1997                return Err(crate::provider::ProviderError::Unsupported {
1998                    operation: crate::provider::ProviderOperation::Digest,
1999                    algorithm: Some(algorithm.uri().to_owned()),
2000                });
2001            }
2002            crate::provider::default_provider().digest(algorithm, data)
2003        }
2004
2005        fn sign(
2006            &self,
2007            key: &dyn super::super::SigningKey,
2008            algorithm: SignatureAlgorithm,
2009            data: &[u8],
2010        ) -> Result<Vec<u8>, super::super::SigningKeyError> {
2011            crate::provider::default_provider().sign(key, algorithm, data)
2012        }
2013
2014        fn verify(
2015            &self,
2016            key: &dyn VerifyingKey,
2017            algorithm: SignatureAlgorithm,
2018            data: &[u8],
2019            signature: &[u8],
2020        ) -> Result<bool, DsigError> {
2021            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
2022            if self.reject_verification_call == Some(call)
2023                || self
2024                    .rejected_verification_data
2025                    .as_deref()
2026                    .is_some_and(|rejected| rejected == data)
2027            {
2028                return Err(crate::provider::ProviderError::Unsupported {
2029                    operation: crate::provider::ProviderOperation::Verify,
2030                    algorithm: Some(algorithm.uri().to_owned()),
2031                }
2032                .into());
2033            }
2034            crate::provider::default_provider().verify(key, algorithm, data, signature)
2035        }
2036
2037        fn verify_x509_signature(
2038            &self,
2039            algorithm: crate::provider::X509SignatureAlgorithm,
2040            data: &[u8],
2041            signature: &[u8],
2042            issuer_spki_der: &[u8],
2043        ) -> Result<bool, crate::provider::ProviderError> {
2044            let call = self.verification_calls.fetch_add(1, Ordering::Relaxed);
2045            if self.reject_verification_call == Some(call)
2046                || self
2047                    .rejected_verification_data
2048                    .as_deref()
2049                    .is_some_and(|rejected| rejected == data)
2050            {
2051                return Err(crate::provider::ProviderError::Unsupported {
2052                    operation: crate::provider::ProviderOperation::VerifyCertificate,
2053                    algorithm: Some(algorithm.oid().to_owned()),
2054                });
2055            }
2056            crate::provider::default_provider().verify_x509_signature(
2057                algorithm,
2058                data,
2059                signature,
2060                issuer_spki_der,
2061            )
2062        }
2063
2064        #[cfg(feature = "xmlenc")]
2065        fn encrypt_data(
2066            &self,
2067            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
2068            key: &[u8],
2069            plaintext: &[u8],
2070        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2071            crate::provider::default_provider().encrypt_data(algorithm, key, plaintext)
2072        }
2073
2074        #[cfg(feature = "xmlenc")]
2075        fn decrypt_data(
2076            &self,
2077            algorithm: crate::xmlenc::DataEncryptionAlgorithm,
2078            key: &[u8],
2079            ciphertext: &[u8],
2080        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2081            crate::provider::default_provider().decrypt_data(algorithm, key, ciphertext)
2082        }
2083
2084        #[cfg(feature = "xmlenc")]
2085        fn wrap_key(
2086            &self,
2087            algorithm: crate::xmlenc::KeyWrapAlgorithm,
2088            kek: &[u8],
2089            key: &[u8],
2090        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2091            crate::provider::default_provider().wrap_key(algorithm, kek, key)
2092        }
2093
2094        #[cfg(feature = "xmlenc")]
2095        fn unwrap_key(
2096            &self,
2097            algorithm: crate::xmlenc::KeyWrapAlgorithm,
2098            kek: &[u8],
2099            wrapped: &[u8],
2100        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2101            crate::provider::default_provider().unwrap_key(algorithm, kek, wrapped)
2102        }
2103
2104        #[cfg(feature = "xmlenc")]
2105        fn transport_key(
2106            &self,
2107            key: &dyn crate::provider::KeyTransportKey,
2108            parameters: &crate::xmlenc::RsaOaepParameters,
2109            plaintext: &[u8],
2110        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2111            crate::provider::default_provider().transport_key(key, parameters, plaintext)
2112        }
2113
2114        #[cfg(feature = "xmlenc")]
2115        fn recover_key(
2116            &self,
2117            key: &dyn crate::provider::KeyRecoveryKey,
2118            parameters: &crate::xmlenc::RsaOaepParameters,
2119            ciphertext: &[u8],
2120        ) -> Result<Vec<u8>, crate::provider::ProviderError> {
2121            crate::provider::default_provider().recover_key(key, parameters, ciphertext)
2122        }
2123    }
2124
2125    fn chain_policy() -> crate::policy::KeyTrustPolicy {
2126        crate::policy::KeyTrustPolicy {
2127            verify_x509_chains: true,
2128            ..crate::policy::KeyTrustPolicy::default()
2129        }
2130    }
2131
2132    fn cryptographic_policy() -> crate::policy::VerificationPolicy {
2133        // Key representation/selector tests do not assert signer authorization.
2134        verification_policy_with_trust(crate::policy::KeyTrustPolicy {
2135            mode: crate::policy::VerificationTrustMode::CryptographicOnly,
2136            ..crate::policy::KeyTrustPolicy::default()
2137        })
2138    }
2139
2140    fn chain_policy_at(verification_time: SystemTime) -> crate::policy::KeyTrustPolicy {
2141        crate::policy::KeyTrustPolicy {
2142            verification_time: Some(verification_time),
2143            ..chain_policy()
2144        }
2145    }
2146
2147    fn verification_policy_with_trust(
2148        key_trust: crate::policy::KeyTrustPolicy,
2149    ) -> crate::policy::VerificationPolicy {
2150        crate::policy::VerificationPolicy {
2151            key_trust,
2152            ..crate::policy::VerificationPolicy::default()
2153        }
2154    }
2155
2156    const SIGNED_SAML: &str =
2157        include_str!("../../tests/fixtures/saml/response_signed_by_idp_ecdsa.xml");
2158    const SAML_PUBLIC_KEY: &str =
2159        include_str!("../../tests/fixtures/keys/ec/saml-idp-ecdsa-pubkey.pem");
2160    const RSA_PUBLIC_KEY: &str = include_str!("../../tests/fixtures/keys/rsa/rsa-2048-pubkey.pem");
2161    const RSA_4096_CERTIFICATE: &str =
2162        include_str!("../../tests/fixtures/keys/rsa/rsa-4096-cert.pem");
2163    const X509_DIGEST_SIGNATURE: &str = include_str!(
2164        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha512.xml"
2165    );
2166    const X509_DIGEST_SHA256_SIGNATURE: &str = include_str!(
2167        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloped-x509-digest-sha256.xml"
2168    );
2169    const RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
2170        "../../tests/fixtures/xmldsig/aleksey-xmldsig-01/enveloping-sha256-rsa-sha256.xml"
2171    );
2172    const LEGACY_RSA_KEY_VALUE_SIGNATURE: &str = include_str!(
2173        "../../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/signature-enveloping-rsa.xml"
2174    );
2175    const EC_P256_KEY_VALUE_SIGNATURE: &str = include_str!(
2176        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p256_sha256.xml"
2177    );
2178    const EC_P384_KEY_VALUE_SIGNATURE: &str = include_str!(
2179        "../../tests/fixtures/xmldsig/xmldsig11-interop-2012/signature-enveloping-p384_sha384.xml"
2180    );
2181
2182    fn replace_key_info(xml: &str, replacement: &str) -> String {
2183        let start = xml.find("<ds:KeyInfo>").expect("fixture has KeyInfo");
2184        let end = xml
2185            .find("</ds:KeyInfo>")
2186            .expect("fixture has closing KeyInfo")
2187            + "</ds:KeyInfo>".len();
2188        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
2189    }
2190
2191    fn replace_unprefixed_key_info(xml: &str, replacement: &str) -> String {
2192        let start = xml.find("<KeyInfo>").expect("fixture has KeyInfo");
2193        let end = xml.find("</KeyInfo>").expect("fixture has closing KeyInfo") + "</KeyInfo>".len();
2194        format!("{}{}{}", &xml[..start], replacement, &xml[end..])
2195    }
2196
2197    fn rsa_key_value_parts(public_key: &rsa::RsaPublicKey) -> (String, String) {
2198        (
2199            STANDARD.encode(public_key.n().to_be_bytes_trimmed_vartime()),
2200            STANDARD.encode(public_key.e().to_be_bytes_trimmed_vartime()),
2201        )
2202    }
2203
2204    fn x509_signature_with_leaf_subject() -> String {
2205        replace_unprefixed_key_info(
2206            X509_DIGEST_SIGNATURE,
2207            "<KeyInfo><X509Data><X509SubjectName>CN=Test Key rsa-4096,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName></X509Data></KeyInfo>",
2208        )
2209    }
2210
2211    fn fixture_certificate_time() -> SystemTime {
2212        // 2027-01-15 UTC, inside the donor certificates' 2026-2126 validity window.
2213        SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_800_000_000)
2214    }
2215
2216    fn public_key_der(pem_text: &str) -> Vec<u8> {
2217        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
2218            .expect("fixture public key is PEM");
2219        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
2220        assert_eq!(pem.label, "PUBLIC KEY");
2221        pem.contents
2222    }
2223
2224    fn certificate_der(pem_text: &str) -> Vec<u8> {
2225        let (rest, pem) = x509_parser::pem::parse_x509_pem(pem_text.as_bytes())
2226            .expect("fixture certificate is PEM");
2227        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
2228        assert_eq!(pem.label, "CERTIFICATE");
2229        pem.contents
2230    }
2231
2232    fn crl_der(pem_text: &str) -> Vec<u8> {
2233        let (rest, pem) =
2234            x509_parser::pem::parse_x509_pem(pem_text.as_bytes()).expect("fixture CRL is PEM");
2235        assert!(rest.iter().all(|byte| byte.is_ascii_whitespace()));
2236        assert_eq!(pem.label, "X509 CRL");
2237        pem.contents
2238    }
2239
2240    fn generated_certificate_params(common_name: &str, is_ca: bool) -> rcgen::CertificateParams {
2241        let mut params = rcgen::CertificateParams::new(Vec::new())
2242            .expect("empty SAN list should produce valid certificate parameters");
2243        params
2244            .distinguished_name
2245            .push(rcgen::DnType::CommonName, common_name);
2246        if is_ca {
2247            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2248            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2249        }
2250        params
2251    }
2252
2253    fn authorized_revoked_chain() -> (X509DataInfo, Vec<u8>) {
2254        let mut root_params = generated_certificate_params("revocation root", true);
2255        root_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
2256        let root = rcgen::CertifiedIssuer::self_signed(
2257            root_params,
2258            KeyPair::generate().expect("root key generation should succeed"),
2259        )
2260        .expect("root should be self-signable");
2261
2262        let issuer_key = KeyPair::generate().expect("issuer key generation should succeed");
2263        let mut issuer_params = generated_certificate_params("revocation issuer", true);
2264        issuer_params.key_usages = vec![KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::CrlSign];
2265        let issuer_certificate = issuer_params
2266            .signed_by(&issuer_key, &root)
2267            .expect("root should sign issuer certificate");
2268        let issuer = Issuer::new(issuer_params, issuer_key);
2269
2270        let mut leaf_params = generated_certificate_params("revoked signer", false);
2271        leaf_params.serial_number = Some(SerialNumber::from(42_u64));
2272        leaf_params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
2273        let leaf = leaf_params
2274            .signed_by(
2275                &KeyPair::generate().expect("leaf key generation should succeed"),
2276                &issuer,
2277            )
2278            .expect("issuer should sign leaf certificate");
2279        let crl = CertificateRevocationListParams {
2280            this_update: date_time_ymd(2026, 3, 15),
2281            next_update: date_time_ymd(2026, 4, 15),
2282            crl_number: SerialNumber::from(1_u64),
2283            issuing_distribution_point: None,
2284            revoked_certs: vec![RevokedCertParams {
2285                serial_number: SerialNumber::from(42_u64),
2286                revocation_time: date_time_ymd(2026, 3, 16),
2287                reason_code: None,
2288                invalidity_date: None,
2289            }],
2290            key_identifier_method: KeyIdMethod::Sha256,
2291        }
2292        .signed_by(&issuer)
2293        .expect("issuer should sign CRL");
2294        let mut info = x509_info(
2295            vec![
2296                leaf.der().to_vec(),
2297                issuer_certificate.der().to_vec(),
2298                root.der().to_vec(),
2299            ],
2300            0,
2301        );
2302        info.crls.push(crl.der().to_vec());
2303        (info, root.der().to_vec())
2304    }
2305
2306    fn x509_info(certificates: Vec<Vec<u8>>, signing_index: usize) -> X509DataInfo {
2307        let parsed_certificates = certificates
2308            .iter()
2309            .map(|certificate| {
2310                parse_x509_certificate(certificate)
2311                    .expect("generated certificate should have supported metadata")
2312            })
2313            .collect();
2314        X509DataInfo {
2315            certificates,
2316            parsed_certificates,
2317            certificate_chain: vec![signing_index],
2318            ..X509DataInfo::default()
2319        }
2320    }
2321
2322    #[test]
2323    fn defaults_match_key_resolution_policy() {
2324        // Defaults must remain compatible with xmlsec1's depth and opt-in trust policy.
2325        let config = KeyResolverConfig::default();
2326
2327        assert!(config.trusted_certs.is_empty());
2328        assert!(config.lookup_certs.is_empty());
2329        assert!(config.named_keys.is_empty());
2330        let trust = crate::policy::VerificationPolicy::default().key_trust;
2331        assert!(!trust.verify_x509_chains);
2332        assert!(!trust.check_crls);
2333        assert_eq!(trust.verification_time, None);
2334        assert_eq!(trust.max_x509_chain_depth, 9);
2335    }
2336
2337    #[test]
2338    fn verification_policy_controls_leaf_extended_key_usage() {
2339        // The immutable operation snapshot must reach certificate-path
2340        // validation; resolver-local trust defaults cannot bypass EKU policy.
2341        let root = rcgen::CertifiedIssuer::self_signed(
2342            generated_certificate_params("EKU policy root", true),
2343            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2344        )
2345        .expect("root should be self-signable");
2346        let mut leaf_params = generated_certificate_params("TLS-only XML signer", false);
2347        leaf_params.key_usages = vec![rcgen::KeyUsagePurpose::DigitalSignature];
2348        leaf_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth];
2349        let leaf = leaf_params
2350            .signed_by(
2351                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2352                &root,
2353            )
2354            .expect("root should sign leaf certificate");
2355        let key_info = KeyInfo {
2356            sources: vec![KeyInfoSource::X509Data(x509_info(
2357                vec![leaf.der().to_vec(), root.der().to_vec()],
2358                0,
2359            ))],
2360        };
2361        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2362            trusted_certs: vec![root.der().to_vec()],
2363            ..KeyResolverConfig::default()
2364        });
2365        let mut policy = crate::policy::VerificationPolicy::default();
2366        policy.key_trust.verify_x509_chains = true;
2367
2368        let error = match resolver.resolve_with_policy(
2369            Some(&key_info),
2370            SignatureAlgorithm::EcdsaSha256,
2371            &policy,
2372        ) {
2373            Ok(_) => panic!("unapproved restricted EKU must be rejected"),
2374            Err(error) => error,
2375        };
2376        assert!(matches!(
2377            error,
2378            DsigError::KeyResolution(KeyResolutionError::Chain(
2379                super::super::X509ChainError::InvalidKeyUsage {
2380                    position: 0,
2381                    required: "an approved extended key usage",
2382                }
2383            ))
2384        ));
2385
2386        policy.key_trust.allowed_extended_key_usages =
2387            std::collections::HashSet::from([crate::policy::ExtendedKeyPurpose::ServerAuth]);
2388        assert!(
2389            resolver
2390                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
2391                .expect("approved restricted EKU must pass path validation")
2392                .is_some()
2393        );
2394    }
2395
2396    #[test]
2397    fn operation_policy_rejects_zero_x509_resource_limits() {
2398        // X.509 work limits belong to the immutable operation snapshot and are
2399        // rejected before resolver-owned certificate material is inspected.
2400        for trust in [
2401            crate::policy::KeyTrustPolicy {
2402                verify_x509_chains: true,
2403                max_x509_chain_depth: 0,
2404                ..crate::policy::KeyTrustPolicy::default()
2405            },
2406            crate::policy::KeyTrustPolicy {
2407                verify_x509_chains: true,
2408                max_x509_candidate_paths: 0,
2409                ..crate::policy::KeyTrustPolicy::default()
2410            },
2411        ] {
2412            let certificate = certificate_der(RSA_4096_CERTIFICATE);
2413            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2414                trusted_certs: vec![certificate],
2415                ..KeyResolverConfig::default()
2416            });
2417            let policy = crate::policy::VerificationPolicy {
2418                key_trust: trust,
2419                ..crate::policy::VerificationPolicy::default()
2420            };
2421            let error = super::super::VerifyContext::new()
2422                .policy(policy)
2423                .key_resolver(&resolver)
2424                .verify(&x509_signature_with_leaf_subject())
2425                .expect_err("zero composed X.509 limits must fail as policy errors");
2426
2427            assert!(matches!(
2428                error,
2429                DsigError::Policy(crate::policy::PolicyViolation::InvalidResourceLimit {
2430                    requirement: "limit must be nonzero",
2431                    actual: 0,
2432                    ..
2433                })
2434            ));
2435        }
2436    }
2437
2438    #[test]
2439    fn operation_policy_rejects_crl_checking_without_chain_validation() {
2440        // CRL authentication is part of path validation. A resolver must not
2441        // accept a configuration that would silently skip the requested check.
2442        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2443            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
2444            ..KeyResolverConfig::default()
2445        });
2446        let policy = crate::policy::VerificationPolicy {
2447            key_trust: crate::policy::KeyTrustPolicy {
2448                check_crls: true,
2449                ..crate::policy::KeyTrustPolicy::default()
2450            },
2451            ..crate::policy::VerificationPolicy::default()
2452        };
2453        let error = super::super::VerifyContext::new()
2454            .policy(policy)
2455            .key_resolver(&resolver)
2456            .verify(&x509_signature_with_leaf_subject())
2457            .expect_err("CRL-only trust policy must fail before certificate use");
2458
2459        assert!(matches!(
2460            error,
2461            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
2462                reason: "CRL checking requires X.509 chain validation"
2463            })
2464        ));
2465    }
2466
2467    #[test]
2468    fn hmac_key_rejects_empty_secret_and_wrong_algorithm() {
2469        // HMAC secrets are caller-owned and cannot be reused as asymmetric keys.
2470        assert!(matches!(
2471            HmacSha1VerificationKey::new(Vec::new()),
2472            Err(KeyResolutionError::InvalidPublicKey)
2473        ));
2474        let key = HmacSha1VerificationKey::new(b"secret".to_vec())
2475            .expect("non-empty HMAC secret must be accepted");
2476        assert!(matches!(
2477            key.verify(SignatureAlgorithm::RsaSha256, b"data", b"signature"),
2478            Err(DsigError::KeyResolution(
2479                KeyResolutionError::AlgorithmMismatch
2480            ))
2481        ));
2482    }
2483
2484    #[test]
2485    fn hmac_key_uses_the_operation_policy_for_truncation() {
2486        // Output length belongs to SignatureMethod and operation policy, not
2487        // reusable secret key material. Legacy truncation therefore requires
2488        // an explicit compatibility policy even on the policy-aware key hook.
2489        let key = HmacVerificationKey::new(b"secret".to_vec())
2490            .expect("the fixture HMAC secret is non-empty");
2491        let mut mac = hmac::Hmac::<sha1::Sha1>::new_from_slice(b"secret")
2492            .expect("HMAC accepts an arbitrary non-empty secret");
2493        mac.update(b"data");
2494        let expected = mac.finalize().into_bytes();
2495
2496        let policy = crate::policy::VerificationPolicy {
2497            hmac: crate::policy::HmacPolicy {
2498                minimum_key_bits: 40,
2499                minimum_output_bits: 80,
2500            },
2501            ..crate::policy::VerificationPolicy::default()
2502        };
2503        assert!(
2504            key.verify_with_policy(
2505                &policy,
2506                SignatureAlgorithm::HmacSha1,
2507                b"data",
2508                &expected[..10],
2509            )
2510            .expect("the compatibility policy and algorithm match")
2511        );
2512        assert!(
2513            !key.verify_with_policy(&policy, SignatureAlgorithm::HmacSha1, b"data", &[0_u8; 10],)
2514                .expect("a mismatched truncated MAC must be rejected")
2515        );
2516    }
2517
2518    #[test]
2519    fn hmac_key_direct_api_rejects_attacker_selected_short_output() {
2520        // The policy-free trait method applies secure defaults; signature bytes
2521        // cannot act as their own one-byte truncation declaration.
2522        let key = HmacVerificationKey::new([0x42; 16]).expect("fixed HMAC key must parse");
2523        let mut mac = hmac::Hmac::<sha2::Sha256>::new_from_slice(&[0x42; 16])
2524            .expect("HMAC accepts the fixed secret");
2525        mac.update(b"data");
2526        let expected = mac.finalize().into_bytes();
2527
2528        assert!(matches!(
2529            key.verify(SignatureAlgorithm::HmacSha256, b"data", &expected[..1]),
2530            Err(DsigError::Policy(
2531                crate::policy::PolicyViolation::HmacOutputLength {
2532                    minimum: 128,
2533                    maximum: 256,
2534                    actual: 8,
2535                }
2536            ))
2537        ));
2538    }
2539
2540    #[test]
2541    fn hmac_key_debug_redacts_secret_material() {
2542        // Debug output may expose public verification parameters, never caller secrets.
2543        let secret = b"unique-debug-secret-marker";
2544        let key = HmacVerificationKey::new(secret.to_vec())
2545            .expect("the fixture HMAC secret is non-empty");
2546
2547        let debug = format!("{key:?}");
2548        assert!(
2549            !debug
2550                .contains(std::str::from_utf8(secret).expect("the debug marker is literal ASCII"))
2551        );
2552        assert!(!debug.contains(&format!("{secret:?}")));
2553        assert!(!debug.contains("output_length_bits"));
2554    }
2555
2556    #[test]
2557    fn stores_named_verification_key_metadata() {
2558        // Named resolution must retain every field needed by the later resolver wiring.
2559        let key = VerificationKey {
2560            algorithm: SignatureAlgorithm::RsaSha256,
2561            public_key_bytes: vec![1, 2, 3],
2562            certificate_der: Some(vec![4, 5, 6]),
2563            name: Some("idp-signing".into()),
2564        };
2565        let mut config = KeyResolverConfig::default();
2566        config.named_keys.insert("idp-signing".into(), key.clone());
2567
2568        assert_eq!(config.named_keys.get("idp-signing"), Some(&key));
2569    }
2570
2571    #[test]
2572    fn resolves_embedded_certificate_end_to_end() {
2573        // The default resolver must make parsed X509Data usable by VerifyContext.
2574        let resolver = DefaultKeyResolver::default();
2575        let result = super::super::VerifyContext::new()
2576            .policy(cryptographic_policy())
2577            .key_resolver(&resolver)
2578            .verify(SIGNED_SAML)
2579            .expect("embedded certificate should resolve");
2580
2581        assert_eq!(result.status, super::super::DsigStatus::Valid);
2582        assert_eq!(
2583            result.key_trust,
2584            super::super::KeyTrustEvidence::NotEstablished
2585        );
2586    }
2587
2588    #[test]
2589    fn embedded_certificate_requires_trust_but_explicit_pin_authorizes_it() {
2590        // Cryptographic correctness alone must not authorize an embedded signer.
2591        let untrusted = DefaultKeyResolver::default();
2592        let error = super::super::VerifyContext::new()
2593            .key_resolver(&untrusted)
2594            .verify(SIGNED_SAML)
2595            .expect_err("an embedded certificate is not a trust declaration");
2596        assert!(matches!(
2597            error,
2598            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust { .. })
2599        ));
2600
2601        let xml = roxmltree::Document::parse(SIGNED_SAML).expect("signed SAML fixture must parse");
2602        let certificate = xml
2603            .descendants()
2604            .find(|node| {
2605                node.has_tag_name(("http://www.w3.org/2000/09/xmldsig#", "X509Certificate"))
2606            })
2607            .expect("signed SAML must embed its certificate");
2608        let der = STANDARD
2609            .decode(
2610                certificate
2611                    .text()
2612                    .expect("fixture certificate must contain Base64 text")
2613                    .split_whitespace()
2614                    .collect::<String>(),
2615            )
2616            .expect("fixture certificate must be valid Base64");
2617        let trusted = DefaultKeyResolver::new(KeyResolverConfig {
2618            trusted_certs: vec![der],
2619            ..KeyResolverConfig::default()
2620        });
2621        let result = super::super::VerifyContext::new()
2622            .key_resolver(&trusted)
2623            .verify(SIGNED_SAML)
2624            .expect("an exact caller certificate pin authorizes the key");
2625        assert_eq!(result.status, super::super::DsigStatus::Valid);
2626        assert_eq!(
2627            result.key_trust,
2628            super::super::KeyTrustEvidence::CallerTrusted
2629        );
2630
2631        // A different operation must not retain the previous resolver's trust.
2632        assert!(
2633            super::super::VerifyContext::new()
2634                .key_resolver(&untrusted)
2635                .verify(SIGNED_SAML)
2636                .is_err()
2637        );
2638    }
2639
2640    #[test]
2641    fn certificate_pin_inspections_share_the_candidate_budget() {
2642        // Authorization is actual key-store work, not a free post-resolution scan.
2643        let xml = roxmltree::Document::parse(SIGNED_SAML).expect("fixture must parse");
2644        let text = xml
2645            .descendants()
2646            .find(|node| {
2647                node.has_tag_name(("http://www.w3.org/2000/09/xmldsig#", "X509Certificate"))
2648            })
2649            .and_then(|node| node.text())
2650            .expect("fixture embeds a certificate");
2651        let certificate = STANDARD
2652            .decode(text.split_whitespace().collect::<String>())
2653            .expect("fixture certificate must decode");
2654        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2655            trusted_certs: vec![certificate],
2656            ..KeyResolverConfig::default()
2657        });
2658        let mut policy = crate::policy::VerificationPolicy::default();
2659        policy.resources.max_key_candidates = 1;
2660        let error = super::super::VerifyContext::new()
2661            .policy(policy)
2662            .key_resolver(&resolver)
2663            .verify(SIGNED_SAML)
2664            .expect_err("the certificate and pin are separate inspections");
2665        assert!(matches!(
2666            error,
2667            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
2668                resource: crate::policy::resource_name::KEY_CANDIDATES,
2669                maximum: 1,
2670                actual: 2,
2671            })
2672        ));
2673    }
2674
2675    #[test]
2676    fn resolves_x509_digest_from_configured_certificates() {
2677        // Selector-only X509Data must locate the signing certificate without
2678        // embedding key material or supplying a preset verification key.
2679        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
2680        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2681            lookup_certs: vec![leaf_certificate_der],
2682            trusted_certs: vec![
2683                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2684                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
2685            ],
2686            ..KeyResolverConfig::default()
2687        });
2688        for signature in [X509_DIGEST_SHA256_SIGNATURE, X509_DIGEST_SIGNATURE] {
2689            let result = super::super::VerifyContext::new()
2690                .policy(cryptographic_policy())
2691                .key_resolver(&resolver)
2692                .verify(signature)
2693                .expect("X509Digest should resolve a configured certificate");
2694
2695            assert_eq!(result.status, super::super::DsigStatus::Valid);
2696        }
2697    }
2698
2699    #[test]
2700    fn selector_resolved_certificate_obeys_chain_policy() {
2701        // Enabling chain verification must apply validity policy even when
2702        // X509Data contains only selectors and the matching cert is configured.
2703        let leaf_certificate_der = certificate_der(RSA_4096_CERTIFICATE);
2704        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2705            lookup_certs: vec![leaf_certificate_der],
2706            trusted_certs: vec![
2707                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
2708                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
2709            ],
2710            ..KeyResolverConfig::default()
2711        });
2712        let error = super::super::VerifyContext::new()
2713            .policy(verification_policy_with_trust(chain_policy_at(
2714                SystemTime::UNIX_EPOCH,
2715            )))
2716            .key_resolver(&resolver)
2717            .verify(&x509_signature_with_leaf_subject())
2718            .expect_err("selector-resolved certificate must satisfy chain policy");
2719
2720        assert!(
2721            matches!(
2722                &error,
2723                DsigError::KeyResolution(KeyResolutionError::Chain(
2724                    super::super::X509ChainError::CertificateNotValid(_)
2725                ))
2726            ),
2727            "unexpected selector policy error: {error:?}"
2728        );
2729    }
2730
2731    #[test]
2732    fn selector_resolved_configured_root_remains_a_trust_anchor() {
2733        // A certificate explicitly configured in trusted_certs remains an
2734        // anchor when X509Data selects it by subject instead of embedding it.
2735        let mut params = rcgen::CertificateParams::new(Vec::new())
2736            .expect("empty SAN list should produce valid certificate parameters");
2737        params
2738            .distinguished_name
2739            .push(rcgen::DnType::CommonName, "configured root");
2740        params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2741        let key_pair = rcgen::KeyPair::generate().expect("test key generation should succeed");
2742        let certificate = params
2743            .self_signed(&key_pair)
2744            .expect("test root should be self-signable");
2745        let certificate_der = certificate.der().to_vec();
2746        let key_info_xml = concat!(
2747            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2748            "<X509Data><X509SubjectName>CN=configured root</X509SubjectName></X509Data>",
2749            "</KeyInfo>"
2750        );
2751        let document = roxmltree::Document::parse(key_info_xml)
2752            .expect("static selector KeyInfo should parse as XML");
2753        let key_info = super::super::parse_key_info(document.root_element())
2754            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2755        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2756            trusted_certs: vec![certificate_der],
2757            ..KeyResolverConfig::default()
2758        });
2759
2760        let resolved = resolver
2761            .resolve_with_policy(
2762                Some(&key_info),
2763                SignatureAlgorithm::EcdsaSha256,
2764                &verification_policy_with_trust(chain_policy()),
2765            )
2766            .expect("configured self-signed certificate should validate as its own anchor");
2767
2768        assert!(resolved.is_some());
2769    }
2770
2771    #[test]
2772    fn selector_resolved_non_self_signed_trust_anchor_terminates_the_path() {
2773        // Trust is assigned to the exact configured certificate, not inferred
2774        // from self-signing. A lookup-only issuer must not extend that anchor
2775        // into a new path that requires another trust decision.
2776        let mut issuer_params = rcgen::CertificateParams::new(Vec::new())
2777            .expect("empty issuer SAN list should be valid");
2778        issuer_params
2779            .distinguished_name
2780            .push(rcgen::DnType::CommonName, "lookup-only issuer");
2781        issuer_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2782        issuer_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2783        let issuer = rcgen::CertifiedIssuer::self_signed(
2784            issuer_params,
2785            rcgen::KeyPair::generate().expect("issuer key generation should succeed"),
2786        )
2787        .expect("issuer certificate should be self-signable");
2788
2789        let mut anchor_params = rcgen::CertificateParams::new(Vec::new())
2790            .expect("empty anchor SAN list should be valid");
2791        anchor_params
2792            .distinguished_name
2793            .push(rcgen::DnType::CommonName, "direct trust anchor");
2794        let anchor = anchor_params
2795            .signed_by(
2796                &rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
2797                &issuer,
2798            )
2799            .expect("issuer should sign the directly trusted certificate");
2800        let key_info_xml = concat!(
2801            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2802            "<X509Data><X509SubjectName>CN=direct trust anchor</X509SubjectName></X509Data>",
2803            "</KeyInfo>"
2804        );
2805        let document = roxmltree::Document::parse(key_info_xml)
2806            .expect("static selector KeyInfo should parse as XML");
2807        let key_info = super::super::parse_key_info(document.root_element())
2808            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2809        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2810            trusted_certs: vec![anchor.der().to_vec()],
2811            lookup_certs: vec![issuer.der().to_vec()],
2812            ..KeyResolverConfig::default()
2813        });
2814
2815        let resolved = resolver
2816            .resolve_with_policy(
2817                Some(&key_info),
2818                SignatureAlgorithm::EcdsaSha256,
2819                &verification_policy_with_trust(chain_policy()),
2820            )
2821            .expect("an explicitly trusted selected certificate must terminate its path");
2822
2823        assert!(resolved.is_some());
2824    }
2825
2826    #[test]
2827    fn selector_resolved_leaf_stops_at_non_self_signed_trust_anchor() {
2828        // A configured anchor terminates trust even when a lookup certificate
2829        // could continue the issuer-name chain beyond it.
2830        let external_issuer = rcgen::CertifiedIssuer::self_signed(
2831            generated_certificate_params("external issuer", true),
2832            rcgen::KeyPair::generate().expect("external issuer key generation should succeed"),
2833        )
2834        .expect("external issuer should be self-signable");
2835        let anchor = rcgen::CertifiedIssuer::signed_by(
2836            generated_certificate_params("non-self-signed anchor", true),
2837            rcgen::KeyPair::generate().expect("anchor key generation should succeed"),
2838            &external_issuer,
2839        )
2840        .expect("external issuer should sign the anchor");
2841        let leaf = generated_certificate_params("anchor leaf", false)
2842            .signed_by(
2843                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2844                &anchor,
2845            )
2846            .expect("anchor should sign the leaf");
2847        let leaf_metadata = parse_x509_certificate(leaf.der())
2848            .expect("generated leaf should have supported metadata");
2849        let key_info = KeyInfo {
2850            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
2851                subject_names: vec![leaf_metadata.subject_dn],
2852                ..X509DataInfo::default()
2853            })],
2854        };
2855        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2856            trusted_certs: vec![anchor.der().to_vec()],
2857            lookup_certs: vec![leaf.der().to_vec(), external_issuer.der().to_vec()],
2858            ..KeyResolverConfig::default()
2859        });
2860
2861        let resolved = resolver
2862            .resolve_with_policy(
2863                Some(&key_info),
2864                SignatureAlgorithm::EcdsaSha256,
2865                &verification_policy_with_trust(chain_policy()),
2866            )
2867            .expect("path construction must stop at the configured anchor");
2868
2869        assert!(resolved.is_some());
2870    }
2871
2872    #[test]
2873    fn selector_resolved_leaf_does_not_anchor_itself() {
2874        // A certificate available for selector lookup is not automatically a
2875        // trust anchor; chain verification still requires a separate issuer.
2876        let certificate_der = certificate_der(RSA_4096_CERTIFICATE);
2877        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2878            lookup_certs: vec![certificate_der],
2879            ..KeyResolverConfig::default()
2880        });
2881        let error = super::super::VerifyContext::new()
2882            .policy(verification_policy_with_trust(chain_policy_at(
2883                fixture_certificate_time(),
2884            )))
2885            .key_resolver(&resolver)
2886            .verify(&x509_signature_with_leaf_subject())
2887            .expect_err("selector-resolved leaf must not trust itself");
2888
2889        assert!(matches!(
2890            error,
2891            DsigError::KeyResolution(KeyResolutionError::Chain(
2892                super::super::X509ChainError::UntrustedRoot
2893            ))
2894        ));
2895    }
2896
2897    #[test]
2898    fn selector_resolved_leaf_uses_separate_anchor() {
2899        // Selector lookup may use the leaf from the configured set, but chain
2900        // verification must terminate at a different configured certificate.
2901        let leaf = certificate_der(RSA_4096_CERTIFICATE);
2902        let issuer = certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
2903        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2904            lookup_certs: vec![leaf],
2905            trusted_certs: vec![issuer],
2906            ..KeyResolverConfig::default()
2907        });
2908        let result = super::super::VerifyContext::new()
2909            .policy(verification_policy_with_trust(chain_policy_at(
2910                fixture_certificate_time(),
2911            )))
2912            .key_resolver(&resolver)
2913            .verify(&x509_signature_with_leaf_subject())
2914            .expect("selector-resolved leaf should chain to its configured issuer");
2915
2916        assert_eq!(result.status, super::super::DsigStatus::Valid);
2917    }
2918
2919    #[test]
2920    fn selector_resolved_leaf_uses_lookup_intermediate() {
2921        // Lookup certificates may complete an untrusted path, but only the
2922        // separately configured root is allowed to establish trust.
2923        let mut root_params =
2924            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
2925        root_params
2926            .distinguished_name
2927            .push(rcgen::DnType::CommonName, "lookup root");
2928        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2929        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2930        let root = rcgen::CertifiedIssuer::self_signed(
2931            root_params,
2932            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2933        )
2934        .expect("root certificate should be self-signable");
2935
2936        let mut intermediate_params = rcgen::CertificateParams::new(Vec::new())
2937            .expect("empty intermediate SAN list should be valid");
2938        intermediate_params
2939            .distinguished_name
2940            .push(rcgen::DnType::CommonName, "lookup intermediate");
2941        intermediate_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
2942        intermediate_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
2943        let intermediate = rcgen::CertifiedIssuer::signed_by(
2944            intermediate_params,
2945            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
2946            &root,
2947        )
2948        .expect("root should sign the intermediate certificate");
2949
2950        let mut leaf_params =
2951            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
2952        leaf_params
2953            .distinguished_name
2954            .push(rcgen::DnType::CommonName, "lookup leaf");
2955        let leaf = leaf_params
2956            .signed_by(
2957                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
2958                &intermediate,
2959            )
2960            .expect("intermediate should sign the leaf certificate");
2961        let key_info_xml = concat!(
2962            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
2963            "<X509Data><X509SubjectName>CN=lookup leaf</X509SubjectName></X509Data>",
2964            "</KeyInfo>"
2965        );
2966        let document = roxmltree::Document::parse(key_info_xml)
2967            .expect("static selector KeyInfo should parse as XML");
2968        let key_info = super::super::parse_key_info(document.root_element())
2969            .expect("static selector KeyInfo should satisfy XMLDSig structure");
2970        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
2971            lookup_certs: vec![leaf.der().to_vec(), intermediate.der().to_vec()],
2972            trusted_certs: vec![root.der().to_vec()],
2973            ..KeyResolverConfig::default()
2974        });
2975
2976        let resolved = resolver
2977            .resolve_with_policy(
2978                Some(&key_info),
2979                SignatureAlgorithm::EcdsaSha256,
2980                &verification_policy_with_trust(chain_policy()),
2981            )
2982            .expect("selector-resolved leaf should chain through the lookup intermediate");
2983
2984        assert!(resolved.is_some());
2985    }
2986
2987    #[test]
2988    fn x509_path_signatures_use_the_operation_provider() {
2989        // Embedded and selector-resolved certificates converge on the same
2990        // path validator. Neither source may fall back to a crate-global
2991        // verifier when the operation provider rejects certificate signatures.
2992        let root = rcgen::CertifiedIssuer::self_signed(
2993            generated_certificate_params("provider root", true),
2994            rcgen::KeyPair::generate().expect("root key generation should succeed"),
2995        )
2996        .expect("root should be self-signable");
2997        let leaf = generated_certificate_params("provider leaf", false)
2998            .signed_by(
2999                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3000                &root,
3001            )
3002            .expect("root should sign the leaf");
3003        let leaf_der = leaf.der().to_vec();
3004        let leaf_metadata =
3005            parse_x509_certificate(&leaf_der).expect("generated leaf metadata should parse");
3006        let policy = crate::policy::VerificationPolicy {
3007            key_trust: chain_policy(),
3008            ..crate::policy::VerificationPolicy::default()
3009        };
3010
3011        let cases = [
3012            (
3013                KeyInfo {
3014                    sources: vec![KeyInfoSource::X509Data(x509_info(
3015                        vec![leaf_der.clone()],
3016                        0,
3017                    ))],
3018                },
3019                Vec::new(),
3020            ),
3021            (
3022                KeyInfo {
3023                    sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3024                        subject_names: vec![leaf_metadata.subject_dn],
3025                        ..X509DataInfo::default()
3026                    })],
3027                },
3028                vec![leaf_der],
3029            ),
3030        ];
3031
3032        for (key_info, lookup_certs) in cases {
3033            let provider = RejectSecondSha512Provider {
3034                sha512_calls: AtomicUsize::new(0),
3035                verification_calls: AtomicUsize::new(0),
3036                reject_verification_call: Some(0),
3037                rejected_verification_data: None,
3038            };
3039            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3040                trusted_certs: vec![root.der().to_vec()],
3041                lookup_certs,
3042                ..KeyResolverConfig::default()
3043            });
3044            let error = match resolver.resolve_with_policy_and_provider(
3045                Some(&key_info),
3046                SignatureAlgorithm::EcdsaSha256,
3047                &policy,
3048                &provider,
3049            ) {
3050                Ok(_) => panic!("the operation provider must gate every X.509 path signature"),
3051                Err(error) => error,
3052            };
3053
3054            assert!(matches!(
3055                error,
3056                DsigError::KeyResolution(KeyResolutionError::Chain(
3057                    super::super::X509ChainError::UnsupportedSignatureAlgorithm { ref oid }
3058                )) if oid == "1.2.840.10045.4.3.2"
3059            ));
3060            assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 1);
3061        }
3062
3063        // A provider rejection after path construction proves complete-path
3064        // validation does not switch back to the crate-global provider.
3065        let provider = RejectSecondSha512Provider {
3066            sha512_calls: AtomicUsize::new(0),
3067            verification_calls: AtomicUsize::new(0),
3068            reject_verification_call: Some(1),
3069            rejected_verification_data: None,
3070        };
3071        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3072            trusted_certs: vec![root.der().to_vec()],
3073            ..KeyResolverConfig::default()
3074        });
3075        let key_info = KeyInfo {
3076            sources: vec![KeyInfoSource::X509Data(x509_info(
3077                vec![leaf.der().to_vec()],
3078                0,
3079            ))],
3080        };
3081        let error = match resolver.resolve_with_policy_and_provider(
3082            Some(&key_info),
3083            SignatureAlgorithm::EcdsaSha256,
3084            &policy,
3085            &provider,
3086        ) {
3087            Ok(_) => panic!("complete-path validation must retain the operation provider"),
3088            Err(error) => error,
3089        };
3090        assert!(matches!(
3091            error,
3092            DsigError::KeyResolution(KeyResolutionError::Chain(
3093                super::super::X509ChainError::Provider(_)
3094            ))
3095        ));
3096        assert_eq!(provider.verification_calls.load(Ordering::Relaxed), 2);
3097    }
3098
3099    #[test]
3100    fn embedded_leaf_uses_lookup_intermediate_with_duplicate_anchor() {
3101        // Deduplicating repeated trust anchors must not shift an untrusted
3102        // lookup intermediate into the trusted prefix used by path building.
3103        let trusted_root = rcgen::CertifiedIssuer::self_signed(
3104            generated_certificate_params("unrelated trusted root", true),
3105            rcgen::KeyPair::generate().expect("root key generation should succeed"),
3106        )
3107        .expect("root should be self-signable");
3108        let issuer_root = rcgen::CertifiedIssuer::self_signed(
3109            generated_certificate_params("untrusted issuer root", true),
3110            rcgen::KeyPair::generate().expect("issuer root key generation should succeed"),
3111        )
3112        .expect("issuer root should be self-signable");
3113        let intermediate = rcgen::CertifiedIssuer::signed_by(
3114            generated_certificate_params("embedded intermediate", true),
3115            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
3116            &issuer_root,
3117        )
3118        .expect("issuer root should sign the intermediate");
3119        let leaf = generated_certificate_params("embedded leaf", false)
3120            .signed_by(
3121                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3122                &intermediate,
3123            )
3124            .expect("intermediate should sign the leaf");
3125        let key_info = KeyInfo {
3126            sources: vec![KeyInfoSource::X509Data(x509_info(
3127                vec![leaf.der().to_vec()],
3128                0,
3129            ))],
3130        };
3131        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3132            lookup_certs: vec![intermediate.der().to_vec()],
3133            trusted_certs: vec![trusted_root.der().to_vec(), trusted_root.der().to_vec()],
3134            ..KeyResolverConfig::default()
3135        });
3136
3137        let policy = verification_policy_with_trust(chain_policy());
3138        let error = match resolver.resolve_with_policy(
3139            Some(&key_info),
3140            SignatureAlgorithm::EcdsaSha256,
3141            &policy,
3142        ) {
3143            Ok(_) => panic!("an untrusted lookup intermediate must not become a trust anchor"),
3144            Err(error) => error,
3145        };
3146
3147        assert!(matches!(
3148            error,
3149            DsigError::KeyResolution(KeyResolutionError::Chain(
3150                super::super::X509ChainError::UntrustedRoot
3151            ))
3152        ));
3153    }
3154
3155    #[test]
3156    fn selector_resolved_leaf_chooses_unique_valid_same_key_path() {
3157        // Cross-signing can produce issuer certificates with the same subject
3158        // and public key. Trust policy, not the immediate signature edge, must
3159        // select the sole path that reaches a configured anchor.
3160        let trusted_root = rcgen::CertifiedIssuer::self_signed(
3161            generated_certificate_params("trusted cross-sign root", true),
3162            rcgen::KeyPair::generate().expect("trusted root key generation should succeed"),
3163        )
3164        .expect("trusted root should be self-signable");
3165        let untrusted_root = rcgen::CertifiedIssuer::self_signed(
3166            generated_certificate_params("untrusted cross-sign root", true),
3167            rcgen::KeyPair::generate().expect("untrusted root key generation should succeed"),
3168        )
3169        .expect("untrusted root should be self-signable");
3170        let shared_params = generated_certificate_params("shared cross-sign issuer", true);
3171        let shared_key =
3172            rcgen::KeyPair::generate().expect("shared issuer key generation should succeed");
3173        let trusted_intermediate = shared_params
3174            .signed_by(&shared_key, &trusted_root)
3175            .expect("trusted root should cross-sign the shared issuer key");
3176        let untrusted_intermediate = shared_params
3177            .signed_by(&shared_key, &untrusted_root)
3178            .expect("untrusted root should cross-sign the shared issuer key");
3179        let shared_issuer = rcgen::Issuer::from_params(&shared_params, &shared_key);
3180        let leaf = generated_certificate_params("cross-signed leaf", false)
3181            .signed_by(
3182                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3183                &shared_issuer,
3184            )
3185            .expect("shared issuer key should sign the leaf");
3186        let leaf_metadata = parse_x509_certificate(leaf.der())
3187            .expect("generated leaf should have supported metadata");
3188        let key_info = KeyInfo {
3189            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3190                subject_names: vec![leaf_metadata.subject_dn],
3191                ..X509DataInfo::default()
3192            })],
3193        };
3194        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3195            trusted_certs: vec![trusted_root.der().to_vec()],
3196            lookup_certs: vec![
3197                leaf.der().to_vec(),
3198                untrusted_intermediate.der().to_vec(),
3199                trusted_intermediate.der().to_vec(),
3200                untrusted_root.der().to_vec(),
3201            ],
3202            ..KeyResolverConfig::default()
3203        });
3204
3205        let resolved = resolver
3206            .resolve_with_policy(
3207                Some(&key_info),
3208                SignatureAlgorithm::EcdsaSha256,
3209                &verification_policy_with_trust(chain_policy()),
3210            )
3211            .expect("the sole path to a configured anchor should be selected");
3212
3213        assert!(resolved.is_some());
3214    }
3215
3216    #[test]
3217    fn self_issued_rollover_continues_to_same_name_trusted_signer() {
3218        // Subject/issuer name equality does not prove self-signing: rollover
3219        // certificates may be issued by a distinct same-name trust anchor.
3220        let root = rcgen::CertifiedIssuer::self_signed(
3221            generated_certificate_params("rollover authority", true),
3222            rcgen::KeyPair::generate().expect("root key generation should succeed"),
3223        )
3224        .expect("root should be self-signable");
3225        let rollover_params = generated_certificate_params("rollover authority", true);
3226        let rollover_key =
3227            rcgen::KeyPair::generate().expect("rollover key generation should succeed");
3228        let rollover_certificate = rollover_params
3229            .signed_by(&rollover_key, &root)
3230            .expect("root should sign the same-name rollover certificate");
3231        let rollover_issuer = rcgen::Issuer::from_params(&rollover_params, &rollover_key);
3232        let leaf = generated_certificate_params("rollover leaf", false)
3233            .signed_by(
3234                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3235                &rollover_issuer,
3236            )
3237            .expect("rollover key should sign the leaf");
3238        let leaf_metadata =
3239            parse_x509_certificate(leaf.der()).expect("generated leaf metadata should parse");
3240        let key_info = KeyInfo {
3241            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
3242                subject_names: vec![leaf_metadata.subject_dn],
3243                ..X509DataInfo::default()
3244            })],
3245        };
3246        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3247            trusted_certs: vec![root.der().to_vec()],
3248            lookup_certs: vec![leaf.der().to_vec(), rollover_certificate.der().to_vec()],
3249            ..KeyResolverConfig::default()
3250        });
3251
3252        let resolved = resolver
3253            .resolve_with_policy(
3254                Some(&key_info),
3255                SignatureAlgorithm::EcdsaSha256,
3256                &verification_policy_with_trust(chain_policy()),
3257            )
3258            .expect("same-name rollover path must reach its configured signer");
3259
3260        assert!(resolved.is_some());
3261    }
3262
3263    #[test]
3264    fn x509_candidate_limit_counts_generated_partial_paths() {
3265        // A narrow DFS frontier can still generate unbounded partial paths over
3266        // time, so the resource limit must account for every generated state.
3267        let root = rcgen::CertifiedIssuer::self_signed(
3268            generated_certificate_params("candidate root", true),
3269            rcgen::KeyPair::generate().expect("root key generation should succeed"),
3270        )
3271        .expect("root should be self-signable");
3272        let intermediate = rcgen::CertifiedIssuer::signed_by(
3273            generated_certificate_params("candidate intermediate", true),
3274            rcgen::KeyPair::generate().expect("intermediate key generation should succeed"),
3275            &root,
3276        )
3277        .expect("root should sign the intermediate");
3278        let leaf = generated_certificate_params("candidate leaf", false)
3279            .signed_by(
3280                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3281                &intermediate,
3282            )
3283            .expect("intermediate should sign the leaf");
3284        let info = x509_info(
3285            vec![
3286                root.der().to_vec(),
3287                intermediate.der().to_vec(),
3288                leaf.der().to_vec(),
3289            ],
3290            2,
3291        );
3292
3293        assert!(matches!(
3294            build_x509_certificate_paths_to_trusted_prefix(
3295                &info,
3296                2,
3297                1,
3298                9,
3299                2,
3300                crate::provider::default_provider(),
3301                None,
3302            ),
3303            Err(X509ChainBuildError::AmbiguousIssuer)
3304        ));
3305    }
3306
3307    #[test]
3308    fn selector_resolved_leaf_disambiguates_same_subject_issuers_by_signature() {
3309        // Certificate renewal may leave multiple configured intermediates with
3310        // the same subject DN. The leaf signature, not pool order, identifies
3311        // the one issuer that belongs to the verification path.
3312        let mut root_params =
3313            rcgen::CertificateParams::new(Vec::new()).expect("empty root SAN list should be valid");
3314        root_params
3315            .distinguished_name
3316            .push(rcgen::DnType::CommonName, "shared-issuer root");
3317        root_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
3318        root_params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
3319        let root = rcgen::CertifiedIssuer::self_signed(
3320            root_params,
3321            rcgen::KeyPair::generate().expect("root key generation should succeed"),
3322        )
3323        .expect("root certificate should be self-signable");
3324
3325        let intermediate = |key: rcgen::KeyPair| {
3326            let mut params = rcgen::CertificateParams::new(Vec::new())
3327                .expect("empty intermediate SAN list should be valid");
3328            params
3329                .distinguished_name
3330                .push(rcgen::DnType::CommonName, "renewed intermediate");
3331            params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
3332            params.key_usages = vec![rcgen::KeyUsagePurpose::KeyCertSign];
3333            rcgen::CertifiedIssuer::signed_by(params, key, &root)
3334                .expect("root should sign the intermediate certificate")
3335        };
3336        let unrelated_intermediate = intermediate(
3337            rcgen::KeyPair::generate().expect("unrelated intermediate key generation should work"),
3338        );
3339        let signing_intermediate = intermediate(
3340            rcgen::KeyPair::generate().expect("signing intermediate key generation should work"),
3341        );
3342
3343        let mut leaf_params =
3344            rcgen::CertificateParams::new(Vec::new()).expect("empty leaf SAN list should be valid");
3345        leaf_params
3346            .distinguished_name
3347            .push(rcgen::DnType::CommonName, "same-subject leaf");
3348        let leaf = leaf_params
3349            .signed_by(
3350                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3351                &signing_intermediate,
3352            )
3353            .expect("the selected intermediate should sign the leaf certificate");
3354        let key_info_xml = concat!(
3355            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\">",
3356            "<X509Data><X509SubjectName>CN=same-subject leaf</X509SubjectName></X509Data>",
3357            "</KeyInfo>"
3358        );
3359        let document = roxmltree::Document::parse(key_info_xml)
3360            .expect("static selector KeyInfo should parse as XML");
3361        let key_info = super::super::parse_key_info(document.root_element())
3362            .expect("static selector KeyInfo should satisfy XMLDSig structure");
3363        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3364            lookup_certs: vec![
3365                leaf.der().to_vec(),
3366                unrelated_intermediate.der().to_vec(),
3367                signing_intermediate.der().to_vec(),
3368            ],
3369            trusted_certs: vec![root.der().to_vec()],
3370            ..KeyResolverConfig::default()
3371        });
3372
3373        let resolved = resolver
3374            .resolve_with_policy(
3375                Some(&key_info),
3376                SignatureAlgorithm::EcdsaSha256,
3377                &verification_policy_with_trust(chain_policy()),
3378            )
3379            .expect("the leaf signature should select its unique same-subject issuer");
3380
3381        assert!(resolved.is_some());
3382    }
3383
3384    #[test]
3385    fn x509_path_builder_skips_policy_rejected_alternatives() {
3386        use crate::provider::X509SignatureAlgorithm;
3387        use std::collections::HashSet;
3388        // One cross-signed issuer has a forbidden parent-edge algorithm;
3389        // neither its position nor traversal order may hide a permitted path.
3390        let root = rcgen::CertifiedIssuer::self_signed(
3391            generated_certificate_params("policy root", true),
3392            rcgen::KeyPair::generate().expect("root key"),
3393        )
3394        .expect("root certificate");
3395        let alternate_root = rcgen::CertifiedIssuer::self_signed(
3396            generated_certificate_params("policy alternate root", true),
3397            rcgen::KeyPair::generate_for(&rcgen::PKCS_ECDSA_P384_SHA384)
3398                .expect("alternate root key"),
3399        )
3400        .expect("alternate root certificate");
3401        let shared_params = generated_certificate_params("policy shared issuer", true);
3402        let shared_key = rcgen::KeyPair::generate().expect("issuer key");
3403        let rejected = shared_params
3404            .signed_by(&shared_key, &alternate_root)
3405            .expect("cross-signed issuer");
3406        let shared = rcgen::CertifiedIssuer::signed_by(shared_params, shared_key, &root)
3407            .expect("issuer certificate");
3408        let leaf = generated_certificate_params("policy leaf", false)
3409            .signed_by(&rcgen::KeyPair::generate().expect("leaf key"), &shared)
3410            .expect("leaf certificate");
3411        let rejected = rejected.der().to_vec();
3412        let allowed = crate::policy::CertificateSignatureAlgorithms::Allowlist(HashSet::from([
3413            X509SignatureAlgorithm::Ecdsa(super::super::DigestAlgorithm::Sha256),
3414        ]));
3415        for rejected_first in [true, false] {
3416            let mut alternatives = vec![shared.der().to_vec(), rejected.clone()];
3417            if rejected_first {
3418                alternatives.reverse();
3419            }
3420            let permitted_index = if rejected_first { 3 } else { 2 };
3421            let info = x509_info(
3422                vec![
3423                    root.der().to_vec(),
3424                    leaf.der().to_vec(),
3425                    alternatives.remove(0),
3426                    alternatives.remove(0),
3427                    alternate_root.der().to_vec(),
3428                ],
3429                1,
3430            );
3431            assert_eq!(
3432                build_x509_certificate_paths_to_trusted_prefix(
3433                    &info,
3434                    1,
3435                    1,
3436                    4,
3437                    8,
3438                    crate::provider::default_provider(),
3439                    Some(&allowed),
3440                )
3441                .expect("forbidden alternative must not abort search"),
3442                vec![vec![1, permitted_index, 0]]
3443            );
3444        }
3445        let info = x509_info(
3446            vec![
3447                root.der().to_vec(),
3448                leaf.der().to_vec(),
3449                rejected,
3450                alternate_root.der().to_vec(),
3451            ],
3452            1,
3453        );
3454        assert!(matches!(
3455            build_x509_certificate_paths_to_trusted_prefix(
3456                &info,
3457                1,
3458                1,
3459                4,
3460                8,
3461                crate::provider::default_provider(),
3462                Some(&allowed),
3463            ),
3464            Err(X509ChainBuildError::Policy(_))
3465        ));
3466    }
3467
3468    #[test]
3469    fn x509_path_builder_skips_branch_local_unsupported_algorithms() {
3470        // An untrusted intermediate can share both the subject and public key
3471        // of the valid path while using an unsupported signature algorithm on
3472        // its own parent edge. That branch must not suppress the valid path.
3473        let root = rcgen::CertifiedIssuer::self_signed(
3474            generated_certificate_params("unsupported-edge root", true),
3475            rcgen::KeyPair::generate().expect("root key generation should succeed"),
3476        )
3477        .expect("root certificate should be self-signable");
3478        let signing_intermediate = rcgen::CertifiedIssuer::signed_by(
3479            generated_certificate_params("shared unsupported-edge issuer", true),
3480            rcgen::KeyPair::generate().expect("signing issuer key generation should succeed"),
3481            &root,
3482        )
3483        .expect("root should sign the intermediate certificate");
3484        let key_unsupported_intermediate = rcgen::CertifiedIssuer::signed_by(
3485            generated_certificate_params("shared unsupported-edge issuer", true),
3486            rcgen::KeyPair::generate().expect("unsupported issuer key generation should succeed"),
3487            &root,
3488        )
3489        .expect("root should sign the alternate intermediate certificate");
3490        let leaf = generated_certificate_params("unsupported-edge leaf", false)
3491            .signed_by(
3492                &rcgen::KeyPair::generate().expect("leaf key generation should succeed"),
3493                &signing_intermediate,
3494            )
3495            .expect("signing intermediate should sign the leaf");
3496
3497        let ordered = x509_info(
3498            vec![
3499                leaf.der().to_vec(),
3500                key_unsupported_intermediate.der().to_vec(),
3501                signing_intermediate.der().to_vec(),
3502                root.der().to_vec(),
3503            ],
3504            0,
3505        );
3506        let key_selective_provider = RejectSecondSha512Provider {
3507            sha512_calls: AtomicUsize::new(0),
3508            verification_calls: AtomicUsize::new(0),
3509            reject_verification_call: Some(0),
3510            rejected_verification_data: None,
3511        };
3512        assert_eq!(
3513            super::super::parse::build_x509_certificate_chain_from(
3514                &ordered,
3515                0,
3516                &key_selective_provider,
3517                None,
3518            )
3519            .expect("one unsupported issuer key must not suppress a usable candidate"),
3520            vec![0, 2, 3]
3521        );
3522
3523        let anchored_same_edge = x509_info(
3524            vec![
3525                root.der().to_vec(),
3526                leaf.der().to_vec(),
3527                key_unsupported_intermediate.der().to_vec(),
3528                signing_intermediate.der().to_vec(),
3529            ],
3530            1,
3531        );
3532        let first_candidate_unsupported = RejectSecondSha512Provider {
3533            sha512_calls: AtomicUsize::new(0),
3534            verification_calls: AtomicUsize::new(0),
3535            reject_verification_call: Some(0),
3536            rejected_verification_data: None,
3537        };
3538        assert_eq!(
3539            build_x509_certificate_paths_to_trusted_prefix(
3540                &anchored_same_edge,
3541                1,
3542                1,
3543                4,
3544                8,
3545                &first_candidate_unsupported,
3546                None,
3547            )
3548            .expect("a later same-DN issuer must survive an earlier provider capability miss"),
3549            vec![vec![1, 3, 0]]
3550        );
3551
3552        let mut unsupported_intermediate = signing_intermediate.der().to_vec();
3553        let ecdsa_sha256_oid = [0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
3554        let offsets = unsupported_intermediate
3555            .windows(ecdsa_sha256_oid.len())
3556            .enumerate()
3557            .filter_map(|(offset, window)| (window == ecdsa_sha256_oid).then_some(offset))
3558            .collect::<Vec<_>>();
3559        assert_eq!(
3560            offsets.len(),
3561            2,
3562            "certificate must repeat its signature OID"
3563        );
3564        for offset in offsets {
3565            unsupported_intermediate[offset + ecdsa_sha256_oid.len() - 1] = 0x05;
3566        }
3567
3568        let anchored = x509_info(
3569            vec![
3570                root.der().to_vec(),
3571                leaf.der().to_vec(),
3572                signing_intermediate.der().to_vec(),
3573                unsupported_intermediate,
3574            ],
3575            1,
3576        );
3577        assert_eq!(
3578            build_x509_certificate_paths_to_trusted_prefix(
3579                &anchored,
3580                1,
3581                1,
3582                4,
3583                8,
3584                crate::provider::default_provider(),
3585                None,
3586            )
3587            .expect("a branch-local provider gap must not abort path enumeration"),
3588            vec![vec![1, 2, 0]]
3589        );
3590
3591        let unsupported_only = x509_info(
3592            vec![
3593                root.der().to_vec(),
3594                leaf.der().to_vec(),
3595                anchored.certificates[3].clone(),
3596            ],
3597            1,
3598        );
3599        assert!(matches!(
3600            build_x509_certificate_paths_to_trusted_prefix(
3601                &unsupported_only,
3602                1,
3603                1,
3604                4,
3605                8,
3606                crate::provider::default_provider(),
3607                None,
3608            ),
3609            Err(X509ChainBuildError::UnsupportedSignatureAlgorithm { ref oid })
3610                if oid == "1.2.840.10045.4.3.5"
3611        ));
3612    }
3613
3614    #[test]
3615    fn selector_resolved_certificate_preserves_supplied_crls() {
3616        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>";
3617        let crl = crl_der(include_str!(
3618            "../../tests/fixtures/keys/rsa/rsa-2048-cert-revoked-crl.pem"
3619        ));
3620        let (_, parsed_crl) =
3621            x509_parser::revocation_list::CertificateRevocationList::from_der(&crl)
3622                .expect("tracked CRL must parse");
3623        let crl_signed_data = parsed_crl.tbs_cert_list.as_ref().to_vec();
3624        let xml = replace_unprefixed_key_info(
3625            RSA_KEY_VALUE_SIGNATURE,
3626            &selector.replace("CRL_PLACEHOLDER", &STANDARD.encode(&crl)),
3627        );
3628        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3629            lookup_certs: vec![certificate_der(include_str!(
3630                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3631            ))],
3632            trusted_certs: vec![
3633                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3634                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
3635            ],
3636            ..KeyResolverConfig::default()
3637        });
3638        let policy = verification_policy_with_trust(crate::policy::KeyTrustPolicy {
3639            check_crls: true,
3640            max_x509_chain_depth: 3,
3641            ..chain_policy_at(
3642                SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_773_964_800),
3643            )
3644        });
3645
3646        let error = super::super::VerifyContext::new()
3647            .policy(policy.clone())
3648            .key_resolver(&resolver)
3649            .verify(&xml)
3650            .expect_err("selector lookup must retain and enforce the supplied CRL");
3651        // RFC 10007 section 4: the tracked v3 CA lacks KeyUsage, so CRL
3652        // authorization now fails before its revoked-serial list is consumed.
3653        // https://www.rfc-editor.org/rfc/rfc10007.html#section-4
3654        assert!(matches!(
3655            error,
3656            DsigError::KeyResolution(KeyResolutionError::Chain(
3657                super::super::X509ChainError::InvalidKeyUsage {
3658                    position: 1,
3659                    required: "cRLSign"
3660                }
3661            ))
3662        ));
3663
3664        // Match the exact TBSCertList bytes so earlier certificate-edge
3665        // verification succeeds and the provider rejection occurs at CRL
3666        // authentication itself.
3667        let provider = RejectSecondSha512Provider {
3668            sha512_calls: AtomicUsize::new(0),
3669            verification_calls: AtomicUsize::new(0),
3670            reject_verification_call: None,
3671            rejected_verification_data: Some(crl_signed_data),
3672        };
3673        let error = super::super::VerifyContext::new()
3674            .policy(policy)
3675            .key_resolver(&resolver)
3676            .provider(&provider)
3677            .verify(&xml)
3678            .expect_err("CRL authentication must retain the operation provider");
3679        assert!(matches!(
3680            error,
3681            DsigError::KeyResolution(KeyResolutionError::Chain(
3682                super::super::X509ChainError::Provider(_)
3683            ))
3684        ));
3685    }
3686
3687    #[test]
3688    fn resolver_rejects_revoked_leaf_with_authorized_crl_issuer() {
3689        // Keep revoked-serial enforcement covered independently from the cRLSign authorization
3690        // failure exercised by the legacy fixture above.
3691        let (mut info, root) = authorized_revoked_chain();
3692        let issuer = info.certificates[1].clone();
3693        info.certificates.truncate(1);
3694        info.parsed_certificates.truncate(1);
3695        let key_info = KeyInfo {
3696            sources: vec![KeyInfoSource::X509Data(info)],
3697        };
3698        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3699            lookup_certs: vec![issuer],
3700            trusted_certs: vec![root],
3701            ..KeyResolverConfig::default()
3702        });
3703        let policy = verification_policy_with_trust(crate::policy::KeyTrustPolicy {
3704            check_crls: true,
3705            max_x509_chain_depth: 3,
3706            ..chain_policy_at(
3707                SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1_773_964_800),
3708            )
3709        });
3710
3711        let error = match resolver.resolve_with_policy(
3712            Some(&key_info),
3713            SignatureAlgorithm::EcdsaSha256,
3714            &policy,
3715        ) {
3716            Ok(_) => panic!("an authenticated CRL must reject its revoked leaf"),
3717            Err(error) => error,
3718        };
3719        assert!(matches!(
3720            error,
3721            DsigError::KeyResolution(KeyResolutionError::Chain(
3722                super::super::X509ChainError::Revoked(0)
3723            ))
3724        ));
3725    }
3726
3727    #[test]
3728    fn resolves_each_x509_selector_from_configured_certificates() {
3729        // Every selector form documented by KeyInfo must independently locate
3730        // the same configured RSA certificate without embedded key material.
3731        let selectors = [
3732            "<X509SubjectName>CN=Test Key rsa-2048,O=XML Security Library (http://www.aleksey.com/xmlsec),ST=California,C=US</X509SubjectName>",
3733            "<X509SubjectName>CN=  test   key rsa-2048  ,O=xml security library (HTTP://WWW.ALEKSEY.COM/XMLSEC),ST=california,C=us</X509SubjectName>",
3734            "<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>",
3735            "<X509SKI>bcOXN/nsVl8GatRbcKrPbzIbw0Y=</X509SKI>",
3736        ];
3737        let configured_certificate = certificate_der(include_str!(
3738            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3739        ));
3740
3741        for selector in selectors {
3742            let key_info = format!("<KeyInfo><X509Data>{selector}</X509Data></KeyInfo>");
3743            let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
3744            let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3745                lookup_certs: vec![configured_certificate.clone()],
3746                ..KeyResolverConfig::default()
3747            });
3748            let result = super::super::VerifyContext::new()
3749                .policy(cryptographic_policy())
3750                .key_resolver(&resolver)
3751                .verify(&xml)
3752                .expect("X509 selector should resolve configured certificate");
3753
3754            assert_eq!(result.status, super::super::DsigStatus::Valid);
3755        }
3756    }
3757
3758    #[test]
3759    fn resolves_configured_chain_selectors_across_certificates() {
3760        // Selector categories may identify different members of one configured
3761        // chain; the unique leaf remains the signing certificate.
3762        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>"#;
3763        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3764        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3765            lookup_certs: vec![
3766                certificate_der(include_str!(
3767                    "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3768                )),
3769                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3770            ],
3771            ..KeyResolverConfig::default()
3772        });
3773        let result = super::super::VerifyContext::new()
3774            .policy(cryptographic_policy())
3775            .key_resolver(&resolver)
3776            .verify(&xml)
3777            .expect("selectors across one configured chain should resolve its leaf");
3778
3779        assert_eq!(result.status, super::super::DsigStatus::Valid);
3780    }
3781
3782    #[test]
3783    fn selectors_must_all_match_the_selected_certificate_path() {
3784        // Selector categories may identify different certificates only when
3785        // those certificates belong to the one path chosen for the signer.
3786        let signing_certificate = certificate_der(include_str!(
3787            "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3788        ));
3789        let issuer_certificate =
3790            certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem"));
3791        let unrelated = generated_certificate_params("unrelated selector certificate", false)
3792            .self_signed(
3793                &rcgen::KeyPair::generate().expect("unrelated key generation should succeed"),
3794            )
3795            .expect("unrelated certificate should be self-signable")
3796            .der()
3797            .to_vec();
3798        let digest = crate::provider::default_provider()
3799            .digest(super::super::DigestAlgorithm::Sha256, &unrelated)
3800            .expect("SHA-256 selector digest must be available");
3801        let key_info_xml = format!(
3802            "<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>",
3803            STANDARD.encode(digest)
3804        );
3805        let document = roxmltree::Document::parse(&key_info_xml)
3806            .expect("generated selector KeyInfo must be XML");
3807        let key_info = super::super::parse_key_info(document.root_element())
3808            .expect("generated selector KeyInfo must be structurally valid");
3809        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3810            lookup_certs: vec![signing_certificate, issuer_certificate, unrelated],
3811            ..KeyResolverConfig::default()
3812        });
3813
3814        assert!(
3815            resolver
3816                .resolve(Some(&key_info), SignatureAlgorithm::RsaSha256)
3817                .expect("disjoint selector matches are a key miss")
3818                .is_none()
3819        );
3820    }
3821
3822    #[test]
3823    fn unmatched_x509_selector_does_not_resolve() {
3824        // A selector mismatch must not fall back to arbitrary configured key material.
3825        let key_info = "<KeyInfo><X509Data><X509SubjectName>CN=not-the-signer</X509SubjectName></X509Data></KeyInfo>";
3826        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3827        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3828            lookup_certs: vec![certificate_der(include_str!(
3829                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3830            ))],
3831            ..KeyResolverConfig::default()
3832        });
3833        let result = super::super::VerifyContext::new()
3834            .key_resolver(&resolver)
3835            .verify(&xml)
3836            .expect("an unmatched selector is a key miss, not a parser failure");
3837
3838        assert!(matches!(
3839            result.status,
3840            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
3841        ));
3842    }
3843
3844    #[test]
3845    fn overlapping_trusted_and_lookup_certificate_preserves_trust() {
3846        // One physical certificate appearing in both pools is one candidate;
3847        // deduplication must retain the stronger trusted classification.
3848        let certificate = certificate_der(RSA_4096_CERTIFICATE);
3849        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3850            trusted_certs: vec![certificate.clone()],
3851            lookup_certs: vec![certificate],
3852            ..KeyResolverConfig::default()
3853        });
3854        let result = super::super::VerifyContext::new()
3855            .policy(verification_policy_with_trust(chain_policy_at(
3856                fixture_certificate_time(),
3857            )))
3858            .key_resolver(&resolver)
3859            .verify(&x509_signature_with_leaf_subject())
3860            .expect("trusted/lookup overlap must resolve as one trusted candidate");
3861
3862        assert_eq!(result.status, super::super::DsigStatus::Valid);
3863        let super::super::KeyTrustEvidence::ValidatedX509(evidence) = result.key_trust else {
3864            panic!("successful path validation must retain typed evidence");
3865        };
3866        assert_eq!(evidence.verification_time(), fixture_certificate_time());
3867        assert!(!evidence.revocation_checked());
3868        assert_eq!(evidence.certificate_fingerprints().len(), 1);
3869        assert_eq!(
3870            evidence.anchor_fingerprint(),
3871            &evidence.certificate_fingerprints()[0]
3872        );
3873
3874        // The batch entry point must retain the actual validated path rather
3875        // than reducing trusted/lookup overlap to a boolean or XML claim.
3876        let document = crate::XmlDocument::parse(x509_signature_with_leaf_subject())
3877            .expect("signed certificate selector fixture must parse");
3878        let report = super::super::VerifyContext::new()
3879            .policy(verification_policy_with_trust(chain_policy_at(
3880                fixture_certificate_time(),
3881            )))
3882            .key_resolver(&resolver)
3883            .verify_all(&document)
3884            .expect("batch verification must preserve the validated path");
3885        assert!(
3886            report
3887                .accepted(&document)
3888                .expect("current document acceptance")
3889        );
3890        assert_eq!(
3891            report.signatures()[0]
3892                .result()
3893                .as_ref()
3894                .expect("the individually processed Signature must verify")
3895                .key_trust,
3896            super::super::KeyTrustEvidence::ValidatedX509(evidence)
3897        );
3898    }
3899
3900    #[test]
3901    fn distinct_x509_selector_matches_remain_ambiguous() {
3902        // Deduplication is identity-based, not selector-based: two distinct
3903        // certificates with the same subject remain separate candidates.
3904        let certificate = || {
3905            generated_certificate_params("ambiguous selector", false)
3906                .self_signed(
3907                    &rcgen::KeyPair::generate().expect("test key generation should succeed"),
3908                )
3909                .expect("test certificate should be self-signable")
3910                .der()
3911                .to_vec()
3912        };
3913        let xml = replace_unprefixed_key_info(
3914            X509_DIGEST_SIGNATURE,
3915            "<KeyInfo><X509Data><X509SubjectName>CN=ambiguous selector</X509SubjectName></X509Data></KeyInfo>",
3916        );
3917        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3918            lookup_certs: vec![certificate(), certificate()],
3919            ..KeyResolverConfig::default()
3920        });
3921        let error = super::super::VerifyContext::new()
3922            .key_resolver(&resolver)
3923            .verify(&xml)
3924            .expect_err("distinct selector matches must fail closed");
3925
3926        assert!(matches!(
3927            error,
3928            DsigError::KeyResolution(KeyResolutionError::AmbiguousCertificate)
3929        ));
3930    }
3931
3932    #[test]
3933    fn unsupported_x509_digest_selector_fails_closed() {
3934        // Unknown digest URIs must not be treated as a normal key miss because
3935        // that would silently weaken the caller's explicit selector policy.
3936        let key_info = "<KeyInfo xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\"><X509Data><dsig11:X509Digest Algorithm=\"urn:unsupported\">AQ==</dsig11:X509Digest></X509Data></KeyInfo>";
3937        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
3938        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3939            lookup_certs: vec![certificate_der(include_str!(
3940                "../../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3941            ))],
3942            ..KeyResolverConfig::default()
3943        });
3944        let error = super::super::VerifyContext::new()
3945            .key_resolver(&resolver)
3946            .verify(&xml)
3947            .expect_err("unsupported X509Digest algorithm must fail closed");
3948
3949        assert!(matches!(
3950            error,
3951            DsigError::KeyResolution(KeyResolutionError::UnsupportedDigestAlgorithm(uri))
3952                if uri == "urn:unsupported"
3953        ));
3954    }
3955
3956    #[test]
3957    fn x509_digest_selector_uses_operation_provider() {
3958        // The SHA-512 selector is distinct from the SHA-256 reference digest,
3959        // so only provider-aware key selection can surface this rejection.
3960        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
3961            lookup_certs: vec![certificate_der(RSA_4096_CERTIFICATE)],
3962            trusted_certs: vec![
3963                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
3964                certificate_der(include_str!("../../tests/fixtures/keys/cacert.pem")),
3965            ],
3966            ..KeyResolverConfig::default()
3967        });
3968        let provider = RejectSecondSha512Provider {
3969            sha512_calls: AtomicUsize::new(0),
3970            verification_calls: AtomicUsize::new(0),
3971            reject_verification_call: None,
3972            rejected_verification_data: None,
3973        };
3974        let error = super::super::VerifyContext::new()
3975            .key_resolver(&resolver)
3976            .provider(&provider)
3977            .verify(X509_DIGEST_SIGNATURE)
3978            .expect_err("X509Digest selection must use the operation provider");
3979
3980        assert!(
3981            matches!(
3982                error,
3983                DsigError::Provider(crate::provider::ProviderError::Unsupported {
3984                    operation: crate::provider::ProviderOperation::Digest,
3985                    algorithm: Some(ref uri),
3986                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
3987            ),
3988            "unexpected error: {error:?}"
3989        );
3990    }
3991
3992    #[test]
3993    fn resolves_named_key_end_to_end() {
3994        // KeyName lookup must preserve the same cryptographic result as embedded X509Data.
3995        let xml = replace_key_info(
3996            SIGNED_SAML,
3997            "<ds:KeyInfo><ds:KeyName>idp-signing</ds:KeyName></ds:KeyInfo>",
3998        );
3999        let mut config = KeyResolverConfig::default();
4000        config.named_keys.insert(
4001            "idp-signing".into(),
4002            VerificationKey {
4003                algorithm: SignatureAlgorithm::EcdsaSha256,
4004                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4005                certificate_der: None,
4006                name: Some("idp-signing".into()),
4007            },
4008        );
4009        let resolver = DefaultKeyResolver::new(config);
4010        let result = super::super::VerifyContext::new()
4011            .key_resolver(&resolver)
4012            .verify(&xml)
4013            .expect("named key should resolve");
4014
4015        assert_eq!(result.status, super::super::DsigStatus::Valid);
4016    }
4017
4018    #[test]
4019    fn resolves_der_encoded_key_end_to_end() {
4020        // DSig 1.1 DEREncodedKeyValue must feed the same SPKI verifier path.
4021        let encoded = STANDARD.encode(public_key_der(SAML_PUBLIC_KEY));
4022        let xml = replace_key_info(
4023            SIGNED_SAML,
4024            &format!(
4025                "<ds:KeyInfo><dsig11:DEREncodedKeyValue xmlns:dsig11=\"http://www.w3.org/2009/xmldsig11#\">{encoded}</dsig11:DEREncodedKeyValue></ds:KeyInfo>"
4026            ),
4027        );
4028        let resolver = DefaultKeyResolver::default();
4029        let result = super::super::VerifyContext::new()
4030            .policy(cryptographic_policy())
4031            .key_resolver(&resolver)
4032            .verify(&xml)
4033            .expect("DER key should resolve");
4034
4035        assert_eq!(result.status, super::super::DsigStatus::Valid);
4036    }
4037
4038    #[test]
4039    fn resolves_rsa_key_value_end_to_end() {
4040        // Embedded CryptoBinary parameters must verify the original RSA-2048 donor signature.
4041        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
4042            .expect("fixture must contain an RSA public key");
4043        let (modulus, exponent) = rsa_key_value_parts(&public_key);
4044        let key_info = format!(
4045            "<KeyInfo><KeyValue><RSAKeyValue><Modulus>{}</Modulus><Exponent>{}</Exponent></RSAKeyValue></KeyValue></KeyInfo>",
4046            modulus, exponent,
4047        );
4048        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
4049        let resolver = DefaultKeyResolver::default();
4050        let result = super::super::VerifyContext::new()
4051            .policy(cryptographic_policy())
4052            .key_resolver(&resolver)
4053            .verify(&xml)
4054            .expect("RSAKeyValue should resolve");
4055
4056        assert_eq!(result.status, super::super::DsigStatus::Valid);
4057    }
4058
4059    #[test]
4060    fn rsa_key_value_rejects_legacy_weak_modulus() {
4061        // The secure policy rejects legacy RSA-SHA1 independently of whether
4062        // the capable key came from RSAKeyValue, DER, X.509, or KeyName.
4063        let resolver = DefaultKeyResolver::default();
4064        let error = super::super::VerifyContext::new()
4065            .key_resolver(&resolver)
4066            .verify(LEGACY_RSA_KEY_VALUE_SIGNATURE)
4067            .expect_err("context policy must override permissive resolver defaults");
4068
4069        assert!(matches!(
4070            error,
4071            DsigError::Policy(crate::policy::PolicyViolation::Algorithm {
4072                operation: "verification",
4073                ..
4074            })
4075        ));
4076    }
4077
4078    #[test]
4079    fn operation_policy_rejects_disabled_embedded_key_source() {
4080        // Resolver-owned key material cannot override the operation snapshot's
4081        // decision about which attacker-controlled KeyInfo forms are trusted.
4082        let key_info = KeyInfo {
4083            sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
4084                modulus: vec![0x80; 256],
4085                exponent: vec![1, 0, 1],
4086            })],
4087        };
4088        let mut policy = crate::policy::VerificationPolicy::default();
4089        policy.key_sources.key_value = false;
4090
4091        let error = match DefaultKeyResolver::default().resolve_with_policy(
4092            Some(&key_info),
4093            SignatureAlgorithm::RsaSha256,
4094            &policy,
4095        ) {
4096            Ok(_) => panic!("disabled KeyValue must fail before key construction"),
4097            Err(error) => error,
4098        };
4099
4100        assert!(matches!(
4101            error,
4102            DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
4103                reason: "KeyValue key sources are disabled"
4104            })
4105        ));
4106    }
4107
4108    #[test]
4109    fn operation_policy_preflights_every_key_info_source_before_resolution() {
4110        // A permitted source resolving first must not hide a later source that
4111        // the immutable operation policy rejects.
4112        let mut config = KeyResolverConfig::default();
4113        config.named_keys.insert(
4114            "idp-signing".into(),
4115            VerificationKey {
4116                algorithm: SignatureAlgorithm::EcdsaSha256,
4117                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4118                certificate_der: None,
4119                name: Some("idp-signing".into()),
4120            },
4121        );
4122        let resolver = DefaultKeyResolver::new(config);
4123        let mut policy = crate::policy::VerificationPolicy::default();
4124        policy.key_sources.x509_data = false;
4125
4126        for sources in [
4127            vec![
4128                KeyInfoSource::KeyName("idp-signing".into()),
4129                KeyInfoSource::X509Data(X509DataInfo::default()),
4130            ],
4131            vec![
4132                KeyInfoSource::X509Data(X509DataInfo::default()),
4133                KeyInfoSource::KeyName("idp-signing".into()),
4134            ],
4135        ] {
4136            let error = match resolver.resolve_with_policy(
4137                Some(&KeyInfo { sources }),
4138                SignatureAlgorithm::EcdsaSha256,
4139                &policy,
4140            ) {
4141                Ok(_) => panic!("source order must not hide disabled X509Data"),
4142                Err(error) => error,
4143            };
4144
4145            assert!(matches!(
4146                error,
4147                DsigError::Policy(crate::policy::PolicyViolation::KeyTrust {
4148                    reason: "X509Data key sources are disabled"
4149                })
4150            ));
4151        }
4152    }
4153
4154    #[test]
4155    fn operation_policy_bounds_ordered_key_info_candidates() {
4156        // The candidate ceiling belongs to the complete verification snapshot:
4157        // neither a first source nor fallback to a later source may bypass it.
4158        let mut config = KeyResolverConfig::default();
4159        config.named_keys.insert(
4160            "idp-signing".into(),
4161            VerificationKey {
4162                algorithm: SignatureAlgorithm::EcdsaSha256,
4163                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4164                certificate_der: None,
4165                name: Some("idp-signing".into()),
4166            },
4167        );
4168        let resolver = DefaultKeyResolver::new(config);
4169        let key_info = KeyInfo {
4170            sources: vec![
4171                KeyInfoSource::KeyValue(KeyValueInfo::Ec {
4172                    curve_oid: "1.3.132.0.35".into(),
4173                    public_key: vec![4],
4174                }),
4175                KeyInfoSource::KeyName("idp-signing".into()),
4176            ],
4177        };
4178
4179        for maximum in [0, 1] {
4180            let mut policy = crate::policy::VerificationPolicy::default();
4181            policy.resources.max_key_candidates = maximum;
4182            let error = match resolver.resolve_with_policy(
4183                Some(&key_info),
4184                SignatureAlgorithm::EcdsaSha256,
4185                &policy,
4186            ) {
4187                Ok(_) => panic!("candidate ceiling {maximum} must stop resolution"),
4188                Err(error) => error,
4189            };
4190            assert!(matches!(
4191                error,
4192                DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4193                    resource: crate::policy::resource_name::KEY_CANDIDATES,
4194                    maximum: observed,
4195                    actual,
4196                }) if observed == maximum && actual == maximum + 1
4197            ));
4198        }
4199
4200        let mut policy = crate::policy::VerificationPolicy::default();
4201        policy.resources.max_key_candidates = 2;
4202        assert!(
4203            resolver
4204                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::EcdsaSha256, &policy,)
4205                .expect("two allowed attempts must reach the named key")
4206                .is_some()
4207        );
4208    }
4209
4210    #[test]
4211    fn operation_policy_bounds_configured_x509_selector_candidates() {
4212        // One X509Data selector can fan out across the resolver-owned store.
4213        // Every distinct certificate inspected is candidate work, rather than
4214        // the complete store counting as one KeyInfo source.
4215        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4216            lookup_certs: vec![
4217                certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
4218                certificate_der(RSA_4096_CERTIFICATE),
4219            ],
4220            ..KeyResolverConfig::default()
4221        });
4222        let mut policy = crate::policy::VerificationPolicy::default();
4223        policy.resources.max_key_candidates = 1;
4224
4225        let error = super::super::VerifyContext::new()
4226            .policy(policy)
4227            .key_resolver(&resolver)
4228            .verify(&x509_signature_with_leaf_subject())
4229            .expect_err("the second configured certificate must exceed the candidate budget");
4230
4231        assert!(matches!(
4232            error,
4233            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4234                resource: crate::policy::resource_name::KEY_CANDIDATES,
4235                maximum: 1,
4236                actual: 2,
4237            })
4238        ));
4239    }
4240
4241    #[test]
4242    fn configured_crls_are_bounded_before_der_parsing() {
4243        // Invalid DER must not be parsed when its size or count already violates policy.
4244        let key_info = KeyInfo {
4245            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4246                subject_names: vec!["CN=selected".into()],
4247                ..X509DataInfo::default()
4248            })],
4249        };
4250        let mut policy = crate::policy::VerificationPolicy::default();
4251        policy.key_trust.check_crls = true;
4252        policy.key_trust.verify_x509_chains = true;
4253        policy.resources.max_external_resource_bytes = 4;
4254        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4255            crls: vec![vec![0; 5]],
4256            ..KeyResolverConfig::default()
4257        });
4258        let error = resolver
4259            .resolve_with_policy(Some(&key_info), SignatureAlgorithm::RsaSha256, &policy)
4260            .err()
4261            .expect("oversized CRL must fail before DER parsing");
4262        assert!(
4263            matches!(
4264                error,
4265                DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4266                    resource: crate::policy::resource_name::EXTERNAL_RESOURCE_BYTES,
4267                    ..
4268                })
4269            ),
4270            "{error:?}"
4271        );
4272
4273        policy.resources.max_external_resource_bytes = 4;
4274        policy.resources.max_external_resource_total_bytes = 7;
4275        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4276            crls: vec![vec![0; 4], vec![0; 4]],
4277            ..KeyResolverConfig::default()
4278        });
4279        let error = resolver
4280            .resolve_with_policy(Some(&key_info), SignatureAlgorithm::RsaSha256, &policy)
4281            .err()
4282            .expect("aggregate CRL bytes must fail before DER parsing");
4283        assert!(matches!(
4284            error,
4285            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4286                resource: crate::policy::resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
4287                ..
4288            })
4289        ));
4290
4291        policy.resources.max_external_resource_total_bytes = 8;
4292        policy.resources.max_key_candidates = 1;
4293        let error = resolver
4294            .resolve_with_policy(Some(&key_info), SignatureAlgorithm::RsaSha256, &policy)
4295            .err()
4296            .expect("CRL candidate count must fail before DER parsing");
4297        assert!(matches!(
4298            error,
4299            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4300                resource: crate::policy::resource_name::KEY_CANDIDATES,
4301                ..
4302            })
4303        ));
4304    }
4305
4306    #[test]
4307    fn configured_certificates_and_crls_share_external_byte_budget() {
4308        // A stricter operation policy must account for all resolver-owned
4309        // material on a selector path, even when the resolver was built under
4310        // a broader policy.
4311        let key_info = KeyInfo {
4312            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4313                subject_names: vec!["CN=selected".into()],
4314                ..X509DataInfo::default()
4315            })],
4316        };
4317        let mut policy = crate::policy::VerificationPolicy::default();
4318        policy.key_trust.check_crls = true;
4319        policy.key_trust.verify_x509_chains = true;
4320        policy.resources.max_external_resource_bytes = 8;
4321        policy.resources.max_external_resource_total_bytes = 12;
4322        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4323            trusted_certs: vec![vec![0; 8]],
4324            crls: vec![vec![0; 8]],
4325            ..KeyResolverConfig::default()
4326        });
4327        let error = resolver
4328            .resolve_with_policy(Some(&key_info), SignatureAlgorithm::RsaSha256, &policy)
4329            .err()
4330            .expect("combined external material exceeds the operation limit");
4331        assert!(matches!(
4332            error,
4333            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4334                resource: crate::policy::resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
4335                ..
4336            })
4337        ));
4338    }
4339
4340    #[test]
4341    fn embedded_and_configured_x509_share_one_byte_budget() {
4342        // Both halves fit separately; their combined live material must fail
4343        // before attempting to parse the deliberately invalid certificate DER.
4344        let mut info = KeyInfo {
4345            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4346                certificates: vec![vec![0; 8]],
4347                certificate_chain: vec![0],
4348                crls: vec![vec![0; 2]],
4349                ..X509DataInfo::default()
4350            })],
4351        };
4352        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4353            crls: vec![vec![0; 8]],
4354            ..KeyResolverConfig::default()
4355        });
4356        let mut policy = crate::policy::VerificationPolicy::default();
4357        policy.key_trust.check_crls = true;
4358        policy.key_trust.verify_x509_chains = true;
4359        policy.resources.max_external_resource_total_bytes = 17;
4360        let error = resolver
4361            .resolve_with_policy(Some(&info), SignatureAlgorithm::RsaSha256, &policy)
4362            .err()
4363            .expect("combined material must be rejected");
4364        assert!(
4365            matches!(
4366                error,
4367                DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4368                    resource: crate::policy::resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
4369                    actual: 18,
4370                    ..
4371                })
4372            ),
4373            "{error:?}"
4374        );
4375        policy.resources.max_external_resource_total_bytes = 18;
4376        let error = resolver
4377            .resolve_with_policy(Some(&info), SignatureAlgorithm::RsaSha256, &policy)
4378            .err()
4379            .expect("exact byte allowance reaches DER parsing");
4380        assert!(
4381            matches!(
4382                error,
4383                DsigError::KeyResolution(KeyResolutionError::InvalidCertificate)
4384            ),
4385            "{error:?}"
4386        );
4387        // A prior lookup source may charge configured CRLs once, but cannot
4388        // suppress the combined-byte preflight of a later embedded source.
4389        info.sources.insert(
4390            0,
4391            KeyInfoSource::X509Data(X509DataInfo {
4392                subject_names: vec!["CN=absent".into()],
4393                ..X509DataInfo::default()
4394            }),
4395        );
4396        policy.resources.max_external_resource_total_bytes = 17;
4397        policy.resources.max_key_candidates = 2;
4398        assert!(matches!(
4399            resolver.resolve_with_policy(Some(&info), SignatureAlgorithm::RsaSha256, &policy),
4400            Err(DsigError::Policy(
4401                crate::policy::PolicyViolation::ResourceLimit {
4402                    resource: crate::policy::resource_name::AGGREGATE_EXTERNAL_RESOURCE_BYTES,
4403                    actual: 18,
4404                    ..
4405                }
4406            ))
4407        ));
4408    }
4409
4410    #[test]
4411    fn direct_certificate_does_not_charge_unused_configured_store() {
4412        // A direct embedded certificate bypasses configured lookup material
4413        // when chain verification is disabled.
4414        let certificate = certificate_der(RSA_4096_CERTIFICATE);
4415        let key_info = KeyInfo {
4416            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4417                certificates: vec![certificate],
4418                certificate_chain: vec![0],
4419                subject_names: vec!["CN=unused-selector".into()],
4420                ..X509DataInfo::default()
4421            })],
4422        };
4423        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4424            lookup_certs: vec![vec![0; 4096]],
4425            trusted_certs: vec![vec![0; 4096]],
4426            ..KeyResolverConfig::default()
4427        });
4428        let mut policy = crate::policy::VerificationPolicy::default();
4429        policy.resources.max_external_resource_bytes = 1024;
4430        policy.resources.max_external_resource_total_bytes = 1024;
4431        assert!(
4432            resolver
4433                .resolve_with_policy(Some(&key_info), SignatureAlgorithm::RsaSha256, &policy)
4434                .expect("unused configured certificates are not charged")
4435                .is_some()
4436        );
4437    }
4438
4439    #[test]
4440    fn operation_policy_bounds_embedded_x509_certificate_candidates() {
4441        // Embedded X509Data is also composite key material. Its certificate
4442        // entries must not collapse into one candidate merely because they
4443        // share a single KeyInfo source node.
4444        let key_info = KeyInfo {
4445            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4446                certificates: vec![
4447                    certificate_der(RSA_4096_CERTIFICATE),
4448                    certificate_der(include_str!("../../tests/fixtures/keys/ca2cert.pem")),
4449                ],
4450                certificate_chain: vec![0],
4451                ..X509DataInfo::default()
4452            })],
4453        };
4454        let mut policy = crate::policy::VerificationPolicy::default();
4455        policy.resources.max_key_candidates = 1;
4456
4457        let error = match DefaultKeyResolver::default().resolve_with_policy(
4458            Some(&key_info),
4459            SignatureAlgorithm::RsaSha256,
4460            &policy,
4461        ) {
4462            Ok(_) => panic!("the second embedded certificate must exceed the candidate budget"),
4463            Err(error) => error,
4464        };
4465
4466        assert!(matches!(
4467            error,
4468            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4469                resource: crate::policy::resource_name::KEY_CANDIDATES,
4470                maximum: 1,
4471                actual: 2,
4472            })
4473        ));
4474    }
4475
4476    #[test]
4477    fn operation_policy_charges_duplicate_configured_x509_candidates() {
4478        // Deduplication may avoid repeated parsing, but inspecting a duplicate
4479        // resolver entry still consumes work and must not bypass the budget.
4480        let certificate = certificate_der(RSA_4096_CERTIFICATE);
4481        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4482            lookup_certs: vec![certificate.clone(), certificate],
4483            ..KeyResolverConfig::default()
4484        });
4485        let mut policy = crate::policy::VerificationPolicy::default();
4486        policy.resources.max_key_candidates = 1;
4487
4488        let error = super::super::VerifyContext::new()
4489            .policy(policy)
4490            .key_resolver(&resolver)
4491            .verify(&x509_signature_with_leaf_subject())
4492            .expect_err("the duplicate configured entry must consume candidate work");
4493
4494        assert!(matches!(
4495            error,
4496            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4497                resource: crate::policy::resource_name::KEY_CANDIDATES,
4498                maximum: 1,
4499                actual: 2,
4500            })
4501        ));
4502    }
4503
4504    #[test]
4505    fn operation_policy_charges_duplicate_embedded_x509_candidates() {
4506        // Public callers can construct KeyInfo without passing through parser
4507        // entry limits, so duplicate embedded entries must consume the budget.
4508        let certificate = certificate_der(RSA_4096_CERTIFICATE);
4509        let key_info = KeyInfo {
4510            sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4511                certificates: vec![certificate.clone(), certificate],
4512                certificate_chain: vec![0],
4513                ..X509DataInfo::default()
4514            })],
4515        };
4516        let mut policy = crate::policy::VerificationPolicy::default();
4517        policy.resources.max_key_candidates = 1;
4518
4519        let error = match DefaultKeyResolver::default().resolve_with_policy(
4520            Some(&key_info),
4521            SignatureAlgorithm::RsaSha256,
4522            &policy,
4523        ) {
4524            Ok(_) => panic!("the duplicate embedded entry must consume candidate work"),
4525            Err(error) => error,
4526        };
4527
4528        assert!(matches!(
4529            error,
4530            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4531                resource: crate::policy::resource_name::KEY_CANDIDATES,
4532                maximum: 1,
4533                actual: 2,
4534            })
4535        ));
4536    }
4537
4538    #[test]
4539    fn policy_aware_resolver_rejects_resources_above_hard_ceiling() {
4540        // The resolver is a public policy enforcement boundary in its own
4541        // right; callers must not need VerifyContext to validate the snapshot.
4542        let mut policy = crate::policy::VerificationPolicy::default();
4543        policy.resources.max_key_candidates = usize::MAX;
4544
4545        let error = match DefaultKeyResolver::default().resolve_with_policy(
4546            None,
4547            SignatureAlgorithm::RsaSha256,
4548            &policy,
4549        ) {
4550            Ok(_) => panic!("invalid resource policy must fail before key resolution"),
4551            Err(error) => error,
4552        };
4553
4554        assert!(matches!(
4555            error,
4556            DsigError::Policy(crate::policy::PolicyViolation::ResourceLimit {
4557                resource: crate::policy::resource_name::KEY_CANDIDATES,
4558                actual: usize::MAX,
4559                ..
4560            })
4561        ));
4562    }
4563
4564    #[test]
4565    fn embedded_x509_digest_selection_uses_operation_provider() {
4566        // Embedded certificate selection happens while KeyInfo is parsed, so
4567        // that parser path must retain the verification operation's provider.
4568        let certificate = certificate_der(RSA_4096_CERTIFICATE);
4569        let digest =
4570            super::super::compute_digest(super::super::DigestAlgorithm::Sha512, &certificate);
4571        let xml = format!(
4572            "<KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"><X509Data><X509Certificate>{}</X509Certificate><X509Digest xmlns=\"http://www.w3.org/2009/xmldsig11#\" Algorithm=\"{}\">{}</X509Digest></X509Data></KeyInfo>",
4573            STANDARD.encode(&certificate),
4574            super::super::DigestAlgorithm::Sha512.uri(),
4575            STANDARD.encode(digest),
4576        );
4577        let document = roxmltree::Document::parse(&xml).expect("generated KeyInfo must be XML");
4578        let provider = RejectSecondSha512Provider {
4579            sha512_calls: AtomicUsize::new(1),
4580            verification_calls: AtomicUsize::new(0),
4581            reject_verification_call: None,
4582            rejected_verification_data: None,
4583        };
4584
4585        let error =
4586            super::super::parse::parse_key_info_with_provider(document.root_element(), &provider)
4587                .expect_err("embedded X509Digest selection must use the operation provider");
4588
4589        assert!(
4590            matches!(
4591                error,
4592                ParseError::Provider(crate::provider::ProviderError::Unsupported {
4593                    operation: crate::provider::ProviderOperation::Digest,
4594                    algorithm: Some(ref uri),
4595                }) if uri == super::super::DigestAlgorithm::Sha512.uri()
4596            ),
4597            "unexpected error: {error:?}"
4598        );
4599    }
4600
4601    #[test]
4602    fn generic_key_resolution_keeps_legacy_capability_source_independent() {
4603        let certificate =
4604            include_bytes!("../../tests/fixtures/xmldsig/phaos-xmldsig-three/certs/rsa-cert.der")
4605                .to_vec();
4606        let (_, parsed_certificate) = X509Certificate::from_der(&certificate)
4607            .expect("the Phaos fixture is a DER certificate");
4608        let public_key = parsed_certificate.public_key().raw.to_vec();
4609        let rsa_public_key = rsa::RsaPublicKey::from_public_key_der(&public_key)
4610            .expect("the Phaos certificate contains an RSA public key");
4611        let certificate_metadata = parse_x509_certificate(&certificate)
4612            .expect("the Phaos fixture has supported X.509 metadata");
4613        let named_key = VerificationKey {
4614            algorithm: SignatureAlgorithm::RsaSha1,
4615            public_key_bytes: public_key.clone(),
4616            certificate_der: None,
4617            name: Some("legacy".into()),
4618        };
4619        let key_infos = [
4620            KeyInfo {
4621                sources: vec![KeyInfoSource::KeyName("legacy".into())],
4622            },
4623            KeyInfo {
4624                sources: vec![KeyInfoSource::DerEncodedKeyValue(public_key.clone())],
4625            },
4626            KeyInfo {
4627                sources: vec![KeyInfoSource::KeyValue(KeyValueInfo::Rsa {
4628                    modulus: rsa_public_key.n().to_be_bytes_trimmed_vartime().to_vec(),
4629                    exponent: rsa_public_key.e().to_be_bytes_trimmed_vartime().to_vec(),
4630                })],
4631            },
4632            KeyInfo {
4633                sources: vec![KeyInfoSource::X509Data(X509DataInfo {
4634                    certificates: vec![certificate],
4635                    parsed_certificates: vec![certificate_metadata],
4636                    certificate_chain: vec![0],
4637                    ..X509DataInfo::default()
4638                })],
4639            },
4640        ];
4641        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4642            named_keys: HashMap::from([("legacy".into(), named_key.clone())]),
4643            ..KeyResolverConfig::default()
4644        });
4645        let mut policy = crate::policy::VerificationPolicy::default();
4646        policy.key_trust.rsa_keys.minimum_modulus_bits = 1024;
4647        policy
4648            .key_trust
4649            .allowed_legacy_signature_algorithms
4650            .insert(SignatureAlgorithm::RsaSha1);
4651
4652        for key_info in &key_infos {
4653            let key = resolver
4654                .resolve_with_policy(Some(key_info), SignatureAlgorithm::RsaSha1, &policy)
4655                .expect("the key source is valid")
4656                .expect("key resolution remains independent from operation policy");
4657            assert!(
4658                !key.verify(SignatureAlgorithm::RsaSha1, b"data", &[0; 128])
4659                    .expect("the legacy RSA key is structurally valid")
4660            );
4661        }
4662    }
4663
4664    #[test]
4665    fn rsa_key_value_rejects_ecdsa_signature_method() {
4666        // Embedded RSA parameters must not be relabeled for an ECDSA SignatureMethod.
4667        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
4668            .expect("fixture must contain an RSA public key");
4669        let (modulus, exponent) = rsa_key_value_parts(&public_key);
4670        let key_info = format!(
4671            "<ds:KeyInfo><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>{}</ds:Modulus><ds:Exponent>{}</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo>",
4672            modulus, exponent,
4673        );
4674        let xml = replace_key_info(SIGNED_SAML, &key_info);
4675        let resolver = DefaultKeyResolver::default();
4676        let error = super::super::VerifyContext::new()
4677            .key_resolver(&resolver)
4678            .verify(&xml)
4679            .expect_err("RSAKeyValue must not resolve for ECDSA");
4680
4681        assert!(matches!(
4682            error,
4683            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
4684        ));
4685    }
4686
4687    #[test]
4688    fn resolves_ec_p256_key_value_end_to_end() {
4689        // XMLDSig 1.1 ECKeyValue must verify without a preset key or certificate.
4690        let resolver = DefaultKeyResolver::default();
4691        let result = super::super::VerifyContext::new()
4692            .policy(cryptographic_policy())
4693            .key_resolver(&resolver)
4694            .verify(EC_P256_KEY_VALUE_SIGNATURE)
4695            .expect("P-256 ECKeyValue should resolve");
4696
4697        assert_eq!(result.status, super::super::DsigStatus::Valid);
4698    }
4699
4700    #[test]
4701    fn resolves_ec_p384_key_value_end_to_end() {
4702        // The donor P-384 vector uses NamedCurve + uncompressed PublicKey.
4703        let resolver = DefaultKeyResolver::default();
4704        let result = super::super::VerifyContext::new()
4705            .policy(cryptographic_policy())
4706            .key_resolver(&resolver)
4707            .verify(EC_P384_KEY_VALUE_SIGNATURE)
4708            .expect("P-384 ECKeyValue should resolve");
4709
4710        assert_eq!(result.status, super::super::DsigStatus::Valid);
4711    }
4712
4713    #[test]
4714    fn ec_key_value_ignored_for_rsa_signature_method() {
4715        // Embedded EC key material must not be relabeled for an RSA SignatureMethod.
4716        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>"#;
4717        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, key_info);
4718        let resolver = DefaultKeyResolver::default();
4719        let result = super::super::VerifyContext::new()
4720            .key_resolver(&resolver)
4721            .verify(&xml)
4722            .expect("single incompatible ECKeyValue should be ignored");
4723
4724        assert_eq!(
4725            result.status,
4726            super::super::DsigStatus::Invalid(super::super::FailureReason::KeyNotFound)
4727        );
4728    }
4729
4730    #[test]
4731    fn incompatible_ec_key_value_falls_back_to_later_rsa_key_value() {
4732        // Mixed KeyInfo should keep scanning after an incompatible ECKeyValue source.
4733        let public_key = rsa::RsaPublicKey::from_public_key_pem(RSA_PUBLIC_KEY)
4734            .expect("fixture must contain an RSA public key");
4735        let (modulus, exponent) = rsa_key_value_parts(&public_key);
4736        let key_info = format!(
4737            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>"#,
4738            modulus, exponent,
4739        );
4740        let xml = replace_unprefixed_key_info(RSA_KEY_VALUE_SIGNATURE, &key_info);
4741        let resolver = DefaultKeyResolver::default();
4742        let result = super::super::VerifyContext::new()
4743            .policy(cryptographic_policy())
4744            .key_resolver(&resolver)
4745            .verify(&xml)
4746            .expect("later RSAKeyValue should resolve");
4747
4748        assert_eq!(result.status, super::super::DsigStatus::Valid);
4749    }
4750
4751    #[test]
4752    fn unsupported_ec_key_value_falls_back_to_later_key_name() {
4753        // Unsupported curves are non-fatal so a later compatible source can verify.
4754        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>"#;
4755        let xml = replace_key_info(SIGNED_SAML, key_info);
4756        let mut config = KeyResolverConfig::default();
4757        config.named_keys.insert(
4758            "idp-signing".into(),
4759            VerificationKey {
4760                algorithm: SignatureAlgorithm::EcdsaSha256,
4761                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4762                certificate_der: None,
4763                name: Some("idp-signing".into()),
4764            },
4765        );
4766        let resolver = DefaultKeyResolver::new(config);
4767        let result = super::super::VerifyContext::new()
4768            .key_resolver(&resolver)
4769            .verify(&xml)
4770            .expect("later KeyName should resolve");
4771
4772        assert_eq!(result.status, super::super::DsigStatus::Valid);
4773    }
4774
4775    #[test]
4776    fn invalid_ec_key_value_falls_back_to_later_key_name() {
4777        // Off-curve EC points are typed errors only if no later source can verify.
4778        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>"#;
4779        let xml = replace_key_info(SIGNED_SAML, key_info);
4780        let mut config = KeyResolverConfig::default();
4781        config.named_keys.insert(
4782            "idp-signing".into(),
4783            VerificationKey {
4784                algorithm: SignatureAlgorithm::EcdsaSha256,
4785                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4786                certificate_der: None,
4787                name: Some("idp-signing".into()),
4788            },
4789        );
4790        let resolver = DefaultKeyResolver::new(config);
4791        let result = super::super::VerifyContext::new()
4792            .key_resolver(&resolver)
4793            .verify(&xml)
4794            .expect("later KeyName should resolve after invalid ECKeyValue");
4795
4796        assert_eq!(result.status, super::super::DsigStatus::Valid);
4797    }
4798
4799    #[test]
4800    fn malformed_ec_key_value_falls_back_to_later_key_name() {
4801        // Parse-level EC point errors remain non-fatal while later sources exist.
4802        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>"#;
4803        let xml = replace_key_info(SIGNED_SAML, key_info);
4804        let mut config = KeyResolverConfig::default();
4805        config.named_keys.insert(
4806            "idp-signing".into(),
4807            VerificationKey {
4808                algorithm: SignatureAlgorithm::EcdsaSha256,
4809                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4810                certificate_der: None,
4811                name: Some("idp-signing".into()),
4812            },
4813        );
4814        let resolver = DefaultKeyResolver::new(config);
4815        let result = super::super::VerifyContext::new()
4816            .key_resolver(&resolver)
4817            .verify(&xml)
4818            .expect("later KeyName should resolve after malformed ECKeyValue");
4819
4820        assert_eq!(result.status, super::super::DsigStatus::Valid);
4821    }
4822
4823    #[test]
4824    fn invalid_base64_ec_key_value_falls_back_to_later_key_name() {
4825        // A bad ECKeyValue payload is an unusable source, not a reason to skip
4826        // later ordered KeyInfo sources that can verify the signature.
4827        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>"#;
4828        let xml = replace_key_info(SIGNED_SAML, key_info);
4829        let mut config = KeyResolverConfig::default();
4830        config.named_keys.insert(
4831            "idp-signing".into(),
4832            VerificationKey {
4833                algorithm: SignatureAlgorithm::EcdsaSha256,
4834                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4835                certificate_der: None,
4836                name: Some("idp-signing".into()),
4837            },
4838        );
4839        let resolver = DefaultKeyResolver::new(config);
4840        let result = super::super::VerifyContext::new()
4841            .key_resolver(&resolver)
4842            .verify(&xml)
4843            .expect("later KeyName should resolve after bad ECKeyValue base64");
4844
4845        assert_eq!(result.status, super::super::DsigStatus::Valid);
4846    }
4847
4848    #[test]
4849    fn missing_curve_uri_ec_key_value_falls_back_to_later_key_name() {
4850        // Missing EC curve parameters make only this KeyValue source unusable.
4851        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>"#;
4852        let xml = replace_key_info(SIGNED_SAML, key_info);
4853        let mut config = KeyResolverConfig::default();
4854        config.named_keys.insert(
4855            "idp-signing".into(),
4856            VerificationKey {
4857                algorithm: SignatureAlgorithm::EcdsaSha256,
4858                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4859                certificate_der: None,
4860                name: Some("idp-signing".into()),
4861            },
4862        );
4863        let resolver = DefaultKeyResolver::new(config);
4864        let result = super::super::VerifyContext::new()
4865            .key_resolver(&resolver)
4866            .verify(&xml)
4867            .expect("later KeyName should resolve after missing EC curve URI");
4868
4869        assert_eq!(result.status, super::super::DsigStatus::Valid);
4870    }
4871
4872    #[test]
4873    fn malformed_ec_key_value_children_fall_back_to_later_key_name() {
4874        // An unusable EC source must not prevent later ordered KeyInfo sources
4875        // from resolving, regardless of which required child-shape check fails.
4876        let malformed_ec_key_values = [
4877            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/>"#,
4878            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"/>"#,
4879            r#"<dsig11:NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><dsig11:PublicKey>BA==</dsig11:PublicKey><dsig11:PublicKey>BA==</dsig11:PublicKey>"#,
4880        ];
4881
4882        for malformed_children in malformed_ec_key_values {
4883            let key_info = format!(
4884                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>"#
4885            );
4886            let xml = replace_key_info(SIGNED_SAML, &key_info);
4887            let mut config = KeyResolverConfig::default();
4888            config.named_keys.insert(
4889                "idp-signing".into(),
4890                VerificationKey {
4891                    algorithm: SignatureAlgorithm::EcdsaSha256,
4892                    public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4893                    certificate_der: None,
4894                    name: Some("idp-signing".into()),
4895                },
4896            );
4897            let resolver = DefaultKeyResolver::new(config);
4898            let result = super::super::VerifyContext::new()
4899                .key_resolver(&resolver)
4900                .verify(&xml)
4901                .expect("later KeyName should resolve after malformed EC child shape");
4902
4903            assert_eq!(result.status, super::super::DsigStatus::Valid);
4904        }
4905    }
4906
4907    #[test]
4908    fn supported_ec_curve_does_not_fall_back_to_later_key_name() {
4909        // ECDSA-SHA256 accepts P-384, so this first source is a usable key and
4910        // must not be skipped merely because a later P-256 KeyName happens to
4911        // verify the signature. Verification fails against the selected key.
4912        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>"#;
4913        let xml = replace_key_info(SIGNED_SAML, key_info);
4914        let mut config = KeyResolverConfig::default();
4915        config.named_keys.insert(
4916            "idp-signing".into(),
4917            VerificationKey {
4918                algorithm: SignatureAlgorithm::EcdsaSha256,
4919                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
4920                certificate_der: None,
4921                name: Some("idp-signing".into()),
4922            },
4923        );
4924        let resolver = DefaultKeyResolver::new(config);
4925        let error = super::super::VerifyContext::new()
4926            .policy(cryptographic_policy())
4927            .key_resolver(&resolver)
4928            .verify(&xml)
4929            .expect_err("a usable first key source must not fall through after verification");
4930
4931        assert!(matches!(
4932            error,
4933            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
4934        ));
4935    }
4936
4937    #[test]
4938    fn lone_malformed_ec_key_value_reports_invalid_public_key() {
4939        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>"#;
4940        let xml = replace_key_info(SIGNED_SAML, key_info);
4941        let error = super::super::VerifyContext::new()
4942            .key_resolver(&DefaultKeyResolver::default())
4943            .verify(&xml)
4944            .expect_err("lone malformed ECKeyValue should surface typed key error");
4945
4946        assert!(matches!(
4947            error,
4948            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
4949        ));
4950    }
4951
4952    #[test]
4953    fn lone_supported_ec_curve_reaches_signature_verification() {
4954        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>"#;
4955        let xml = replace_key_info(SIGNED_SAML, key_info);
4956        let error = super::super::VerifyContext::new()
4957            .policy(cryptographic_policy())
4958            .key_resolver(&DefaultKeyResolver::default())
4959            .verify(&xml)
4960            .expect_err("a supported EC curve must reach signature verification");
4961
4962        assert!(matches!(
4963            error,
4964            DsigError::Crypto(super::super::SignatureVerificationError::InvalidSignatureFormat)
4965        ));
4966    }
4967
4968    #[test]
4969    fn chain_verification_rejects_untrusted_embedded_certificate() {
4970        // Enabling chain policy must fail closed when no trust anchor is configured.
4971        let resolver = DefaultKeyResolver::new(KeyResolverConfig {
4972            ..KeyResolverConfig::default()
4973        });
4974        let error = super::super::VerifyContext::new()
4975            .policy(verification_policy_with_trust(chain_policy()))
4976            .key_resolver(&resolver)
4977            .verify(SIGNED_SAML)
4978            .expect_err("untrusted certificate must fail chain validation");
4979
4980        assert!(matches!(
4981            error,
4982            DsigError::KeyResolution(KeyResolutionError::Chain(
4983                super::super::X509ChainError::UntrustedRoot
4984            ))
4985        ));
4986    }
4987
4988    #[test]
4989    fn named_key_algorithm_mismatch_fails_closed() {
4990        // A key registered for RSA must never be attempted for an ECDSA signature.
4991        let xml = replace_key_info(
4992            SIGNED_SAML,
4993            "<ds:KeyInfo><ds:KeyName>wrong-algorithm</ds:KeyName></ds:KeyInfo>",
4994        );
4995        let mut config = KeyResolverConfig::default();
4996        config.named_keys.insert(
4997            "wrong-algorithm".into(),
4998            VerificationKey {
4999                algorithm: SignatureAlgorithm::RsaSha256,
5000                public_key_bytes: public_key_der(SAML_PUBLIC_KEY),
5001                certificate_der: None,
5002                name: Some("wrong-algorithm".into()),
5003            },
5004        );
5005        let resolver = DefaultKeyResolver::new(config);
5006        let error = super::super::VerifyContext::new()
5007            .key_resolver(&resolver)
5008            .verify(&xml)
5009            .expect_err("algorithm mismatch must fail closed");
5010
5011        assert!(matches!(
5012            error,
5013            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
5014        ));
5015    }
5016
5017    #[test]
5018    fn named_key_spki_type_mismatch_fails_during_resolution() {
5019        // The configured algorithm label cannot override the actual SPKI key type.
5020        let xml = replace_key_info(
5021            SIGNED_SAML,
5022            "<ds:KeyInfo><ds:KeyName>mislabeled</ds:KeyName></ds:KeyInfo>",
5023        );
5024        let mut config = KeyResolverConfig::default();
5025        config.named_keys.insert(
5026            "mislabeled".into(),
5027            VerificationKey {
5028                algorithm: SignatureAlgorithm::EcdsaSha256,
5029                public_key_bytes: public_key_der(RSA_PUBLIC_KEY),
5030                certificate_der: None,
5031                name: Some("mislabeled".into()),
5032            },
5033        );
5034        let resolver = DefaultKeyResolver::new(config);
5035        let error = super::super::VerifyContext::new()
5036            .key_resolver(&resolver)
5037            .verify(&xml)
5038            .expect_err("mislabeled named key must fail during resolution");
5039
5040        assert!(matches!(
5041            error,
5042            DsigError::KeyResolution(KeyResolutionError::AlgorithmMismatch)
5043        ));
5044    }
5045
5046    #[test]
5047    fn malformed_named_key_reports_public_key_error() {
5048        // Non-certificate SPKI failures must not be mislabeled as certificate errors.
5049        let xml = replace_key_info(
5050            SIGNED_SAML,
5051            "<ds:KeyInfo><ds:KeyName>malformed</ds:KeyName></ds:KeyInfo>",
5052        );
5053        let mut config = KeyResolverConfig::default();
5054        config.named_keys.insert(
5055            "malformed".into(),
5056            VerificationKey {
5057                algorithm: SignatureAlgorithm::EcdsaSha256,
5058                public_key_bytes: vec![1, 2, 3],
5059                certificate_der: None,
5060                name: Some("malformed".into()),
5061            },
5062        );
5063        let resolver = DefaultKeyResolver::new(config);
5064        let error = super::super::VerifyContext::new()
5065            .key_resolver(&resolver)
5066            .verify(&xml)
5067            .expect_err("malformed named key must fail during resolution");
5068
5069        assert!(matches!(
5070            error,
5071            DsigError::KeyResolution(KeyResolutionError::InvalidPublicKey)
5072        ));
5073    }
5074}