Skip to main content

xml_sec/xmldsig/
keys.rs

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