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