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