Skip to main content

xml_sec/
provider.rs

1//! Provider-neutral cryptographic operations.
2//!
3//! XML parsing and protocol orchestration depend on this contract rather than
4//! concrete cryptographic crates. Secret-bearing keys remain opaque behind
5//! operation-specific handles; this provider owns primitive dispatch and
6//! randomness.
7
8#[cfg(feature = "xmldsig")]
9use crate::rustcrypto_sha3 as sha3;
10
11#[cfg(feature = "aws-lc-fips")]
12mod aws_lc;
13#[cfg(feature = "xmlenc")]
14mod chacha;
15#[cfg(feature = "xmlenc")]
16mod key_establishment;
17#[cfg(feature = "pkcs11")]
18pub mod pkcs11;
19#[cfg(feature = "xmlenc")]
20pub use key_establishment::{
21    EcdhCurve, RustCryptoDhKey, RustCryptoEcdhKey, RustCryptoX448Key, RustCryptoX25519Key,
22};
23#[cfg(feature = "xmldsig")]
24mod key_association;
25#[cfg(feature = "xmldsig")]
26mod key_encapsulation;
27#[cfg(feature = "xmldsig")]
28pub use key_association::{KeyAssociation, KeyVerificationEvent, SignatureVerificationInput};
29#[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
30mod rsa_pkcs1v15;
31#[cfg(feature = "xmldsig")]
32pub(crate) mod rsa_pss;
33#[cfg(feature = "xmldsig")]
34pub use key_encapsulation::{
35    EncapsulatedKey, KeyDecapsulationKey, KeyEncapsulationAlgorithm, KeyEncapsulationKey,
36};
37#[cfg(feature = "experimental-pq")]
38pub use key_encapsulation::{
39    MlKemPrivateKeyEncoding, RustCryptoMlKemPrivateKey, RustCryptoMlKemPublicKey,
40};
41
42/// Secret candidate whose recovery validity is retained until content work ends.
43/// Debug output never exposes key bytes or padding validity.
44#[cfg(feature = "xmlenc")]
45pub struct RecoveredContentKey {
46    bytes: zeroize::Zeroizing<Vec<u8>>,
47    valid: subtle::Choice,
48    implicit_rejection: bool,
49    opaque: Option<std::sync::Arc<dyn ContentDecryptionKey>>,
50}
51
52#[cfg(feature = "xmlenc")]
53impl std::fmt::Debug for RecoveredContentKey {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        f.debug_struct("RecoveredContentKey")
56            .finish_non_exhaustive()
57    }
58}
59
60#[cfg(feature = "xmlenc")]
61impl RecoveredContentKey {
62    /// An explicitly supplied key, or one recovered by an integrity-checking
63    /// mechanism such as OAEP or key wrap. A KEM real-or-rejection secret also
64    /// has no separate recovery-validity result; authenticated content decides
65    /// acceptance, or policy explicitly delegates authentication to the caller.
66    /// This constructor itself proves neither ciphertext integrity nor identity.
67    /// Do not use this for RSA-v1.5 recovery, whose validity must be
68    /// retained separately with `recovery` until content work completes.
69    pub fn confirmed(bytes: Vec<u8>) -> Self {
70        Self {
71            bytes: zeroize::Zeroizing::new(bytes),
72            valid: subtle::Choice::from(1),
73            implicit_rejection: false,
74            opaque: None,
75        }
76    }
77
78    #[cfg(feature = "legacy-algorithms")]
79    /// Preserve a trusted provider's constant-time padding-validation result.
80    /// `bytes` must already contain the fixed-width real-or-fallback candidate;
81    /// the caller must not branch on `valid` during recovery. The enclosing
82    /// content operation consumes this state only after primitive decryption.
83    pub fn recovery(bytes: zeroize::Zeroizing<Vec<u8>>, valid: subtle::Choice) -> Self {
84        Self {
85            bytes,
86            valid,
87            implicit_rejection: true,
88            opaque: None,
89        }
90    }
91
92    /// Retain a non-exportable content key until the content operation ends.
93    pub fn opaque(key: std::sync::Arc<dyn ContentDecryptionKey>) -> Self {
94        Self {
95            bytes: zeroize::Zeroizing::new(Vec::new()),
96            valid: subtle::Choice::from(1),
97            implicit_rejection: false,
98            opaque: Some(key),
99        }
100    }
101
102    /// Public content-key width without exposing the key value.
103    pub fn key_len(&self) -> usize {
104        self.opaque
105            .as_ref()
106            .map_or(self.bytes.len(), |key| key.key_len())
107    }
108
109    pub(crate) fn same_candidate(&self, other: &Self) -> bool {
110        use subtle::ConstantTimeEq;
111
112        match (&self.opaque, &other.opaque) {
113            (Some(a), Some(b)) => std::sync::Arc::ptr_eq(a, b),
114            // Width is public algorithm metadata; secret bytes and recovery
115            // validity must not select an early-exit comparison path.
116            (None, None) => bool::from(
117                self.bytes.as_slice().ct_eq(other.bytes.as_slice())
118                    & self.valid.unwrap_u8().ct_eq(&other.valid.unwrap_u8()),
119            ),
120            _ => false,
121        }
122    }
123
124    pub(crate) fn decrypt(
125        &self,
126        provider: &dyn CryptoProvider,
127        algorithm: DataEncryptionAlgorithm,
128        ciphertext: &[u8],
129        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
130    ) -> Result<Vec<u8>, ProviderError> {
131        match &self.opaque {
132            Some(_) if parameters.is_some() => Err(ProviderError::Unsupported {
133                operation: ProviderOperation::Decrypt,
134                algorithm: Some(algorithm.uri().into()),
135            }),
136            Some(key) => provider.decrypt_content_key(algorithm, key.as_ref(), ciphertext),
137            None => provider.decrypt_data_with_parameters(
138                algorithm,
139                &self.bytes,
140                ciphertext,
141                parameters,
142            ),
143        }
144    }
145
146    #[cfg(test)]
147    pub(crate) fn bytes(&self) -> &[u8] {
148        &self.bytes
149    }
150    pub(crate) fn valid(&self) -> bool {
151        bool::from(self.valid)
152    }
153    /// A nested transport feeds a wrapping operation, not a content cipher.
154    /// Preserve recovery validity across every level until content work ends.
155    pub(crate) fn unwrap_nested(
156        &self,
157        provider: &dyn CryptoProvider,
158        algorithm: KeyWrapAlgorithm,
159        ciphertext: &[u8],
160        output_len: usize,
161    ) -> Result<Self, ProviderError> {
162        if self.opaque.is_some() {
163            return Err(ProviderError::KeyNotExportable);
164        }
165        if self.implicit_rejection {
166            // XMLEnc 1.1 ยง6.1.2: process content even after RSA-v1.5 rejection.
167            // The same protection must survive intermediate wrapping layers.
168            // Branch on the public recovery mechanism, never padding validity.
169            // https://www.w3.org/TR/2013/REC-xmlenc-core1-20130411/#sec-bleichenbacher-attack
170            provider.require_capability(ProviderCapability::Random)?;
171            let mut fallback = zeroize::Zeroizing::new(vec![0; output_len]);
172            provider.fill_random(&mut fallback)?;
173            let mut candidate = match provider.unwrap_key(algorithm, &self.bytes, ciphertext) {
174                Ok(bytes) => zeroize::Zeroizing::new(bytes),
175                Err(ProviderError::AuthenticationFailed) => {
176                    return Ok(Self {
177                        bytes: fallback,
178                        valid: subtle::Choice::from(0),
179                        implicit_rejection: true,
180                        opaque: None,
181                    });
182                }
183                Err(error) => return Err(error),
184            };
185            if candidate.len() != output_len {
186                return Err(ProviderError::InvalidOutputSize {
187                    operation: ProviderOperation::KeyUnwrap,
188                    expected: output_len,
189                    actual: candidate.len(),
190                });
191            }
192            use subtle::ConditionallySelectable;
193            for (byte, random) in candidate.iter_mut().zip(fallback.iter()) {
194                *byte = u8::conditional_select(random, byte, self.valid);
195            }
196            return Ok(Self {
197                bytes: candidate,
198                valid: self.valid,
199                implicit_rejection: true,
200                opaque: None,
201            });
202        }
203        let bytes = provider.unwrap_key(algorithm, &self.bytes, ciphertext)?;
204        Ok(Self {
205            bytes: zeroize::Zeroizing::new(bytes),
206            valid: self.valid,
207            implicit_rejection: false,
208            opaque: None,
209        })
210    }
211    /// Consume a completed recovery outside a content operation. Content
212    /// operations must retain the candidate until after primitive decryption.
213    /// The returned allocation is caller-owned; the caller is responsible for
214    /// zeroizing it when no longer needed (for example with `Zeroizing`).
215    pub fn into_key(mut self) -> Result<Vec<u8>, ProviderError> {
216        if self.opaque.is_some() {
217            return Err(ProviderError::KeyNotExportable);
218        }
219        if !self.valid() {
220            return Err(ProviderError::AuthenticationFailed);
221        }
222        Ok(core::mem::take(&mut *self.bytes))
223    }
224}
225#[cfg(all(feature = "aws-lc-fips", feature = "xmlenc"))]
226pub use aws_lc::AwsLcRsaPrivateKey;
227#[cfg(feature = "aws-lc-fips")]
228pub use aws_lc::{AwsLcFipsProvider, AwsLcSigningKey};
229
230#[cfg(feature = "xmlenc")]
231use std::borrow::Cow;
232
233#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
234use getrandom::rand_core::TryCryptoRng;
235use getrandom::{SysRng, rand_core::TryRng};
236
237#[cfg(feature = "xmldsig")]
238use crate::xmldsig::DigestAlgorithm;
239#[cfg(feature = "xmlenc")]
240use crate::xmlenc::{DataEncryptionAlgorithm, KeyWrapAlgorithm, RsaOaepParameters};
241
242/// A cryptographic operation advertised by a provider.
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
244#[non_exhaustive]
245pub enum ProviderOperation {
246    /// Message digest computation.
247    Digest,
248    /// Public-key signature generation.
249    Sign,
250    /// Public-key signature verification.
251    Verify,
252    /// X.509 certificate or CRL signature verification.
253    VerifyCertificate,
254    /// Authenticated or padded symmetric encryption.
255    Encrypt,
256    /// Authenticated or padded symmetric decryption.
257    Decrypt,
258    /// Symmetric key wrapping.
259    KeyWrap,
260    /// Symmetric key unwrapping.
261    KeyUnwrap,
262    /// Public-key key transport.
263    KeyTransport,
264    /// Private-key recovery of transported key bytes.
265    KeyRecovery,
266    /// Key agreement.
267    KeyAgreement,
268    /// Establish a shared secret with a recipient public key.
269    Encapsulate,
270    /// Recover the shared secret using a recipient private key.
271    Decapsulate,
272    /// Key derivation.
273    Kdf,
274    /// Cryptographically secure random bytes.
275    Random,
276}
277
278/// One exact provider capability, including operation-specific parameters.
279///
280/// Capability discovery describes mechanisms, not policy permission. Callers
281/// must still apply the immutable operation policy before provider dispatch.
282#[derive(Debug, Clone, Copy)]
283#[non_exhaustive]
284pub enum ProviderCapability<'a> {
285    /// Exact KEM parameter set, independent of algorithm permission.
286    #[cfg(feature = "xmldsig")]
287    Encapsulate(KeyEncapsulationAlgorithm),
288    /// Exact KEM parameter set for private-key decapsulation.
289    #[cfg(feature = "xmldsig")]
290    Decapsulate(KeyEncapsulationAlgorithm),
291    /// Message digest computation for an XMLDSig digest method.
292    #[cfg(feature = "xmldsig")]
293    Digest(DigestAlgorithm),
294    /// Provider dispatch for an XMLDSig signing method.
295    ///
296    /// The opaque signing key remains responsible for accepting the method and
297    /// implementing its primitive.
298    #[cfg(feature = "xmldsig")]
299    Sign(crate::xmldsig::SignatureAlgorithm),
300    /// Provider dispatch for an XMLDSig verification method.
301    ///
302    /// The opaque verification key remains responsible for accepting the
303    /// method and implementing its primitive.
304    #[cfg(feature = "xmldsig")]
305    Verify(crate::xmldsig::SignatureAlgorithm),
306    /// X.509 signature verification with complete algorithm parameters.
307    #[cfg(feature = "xmldsig")]
308    VerifyCertificate(X509SignatureAlgorithm),
309    /// XMLEnc content encryption.
310    #[cfg(feature = "xmlenc")]
311    Encrypt(DataEncryptionAlgorithm),
312    /// XMLEnc content decryption.
313    #[cfg(feature = "xmlenc")]
314    Decrypt(DataEncryptionAlgorithm),
315    /// RFC 3394 key wrapping.
316    #[cfg(feature = "xmlenc")]
317    KeyWrap(KeyWrapAlgorithm),
318    /// RFC 3394 key unwrapping.
319    #[cfg(feature = "xmlenc")]
320    KeyUnwrap(KeyWrapAlgorithm),
321    /// RSA-OAEP key transport with complete digest, MGF, and label parameters.
322    #[cfg(feature = "xmlenc")]
323    KeyTransport(&'a RsaOaepParameters),
324    /// RSA-OAEP key recovery with complete digest, MGF, and label parameters.
325    #[cfg(feature = "xmlenc")]
326    KeyRecovery(&'a RsaOaepParameters),
327    /// Historical RSAES-PKCS1-v1_5 transport, without OAEP parameters.
328    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
329    Pkcs1v15Transport,
330    /// Historical RSAES-PKCS1-v1_5 fixed-length content-key recovery.
331    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
332    Pkcs1v15Recovery,
333    /// Provider-defined key agreement identified by its standard URI.
334    KeyAgreement(&'a KeyAgreementParameters<'a>),
335    /// Provider-defined key derivation identified by its standard URI.
336    Kdf(&'a KdfParameters<'a>),
337    /// Cryptographically secure random byte generation.
338    Random,
339}
340
341impl ProviderCapability<'_> {
342    /// Operation category used in diagnostics.
343    #[must_use]
344    pub const fn operation(&self) -> ProviderOperation {
345        match self {
346            #[cfg(feature = "xmldsig")]
347            Self::Encapsulate(_) => ProviderOperation::Encapsulate,
348            #[cfg(feature = "xmldsig")]
349            Self::Decapsulate(_) => ProviderOperation::Decapsulate,
350            #[cfg(feature = "xmldsig")]
351            Self::Digest(_) => ProviderOperation::Digest,
352            #[cfg(feature = "xmldsig")]
353            Self::Sign(_) => ProviderOperation::Sign,
354            #[cfg(feature = "xmldsig")]
355            Self::Verify(_) => ProviderOperation::Verify,
356            #[cfg(feature = "xmldsig")]
357            Self::VerifyCertificate(_) => ProviderOperation::VerifyCertificate,
358            #[cfg(feature = "xmlenc")]
359            Self::Encrypt(_) => ProviderOperation::Encrypt,
360            #[cfg(feature = "xmlenc")]
361            Self::Decrypt(_) => ProviderOperation::Decrypt,
362            #[cfg(feature = "xmlenc")]
363            Self::KeyWrap(_) => ProviderOperation::KeyWrap,
364            #[cfg(feature = "xmlenc")]
365            Self::KeyUnwrap(_) => ProviderOperation::KeyUnwrap,
366            #[cfg(feature = "xmlenc")]
367            Self::KeyTransport(_) => ProviderOperation::KeyTransport,
368            #[cfg(feature = "xmlenc")]
369            Self::KeyRecovery(_) => ProviderOperation::KeyRecovery,
370            #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
371            Self::Pkcs1v15Transport => ProviderOperation::KeyTransport,
372            #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
373            Self::Pkcs1v15Recovery => ProviderOperation::KeyRecovery,
374            Self::KeyAgreement(_) => ProviderOperation::KeyAgreement,
375            Self::Kdf(_) => ProviderOperation::Kdf,
376            Self::Random => ProviderOperation::Random,
377        }
378    }
379
380    /// Standard algorithm identifier used in unsupported-operation errors.
381    #[must_use]
382    pub fn algorithm(&self) -> Option<&str> {
383        match self {
384            #[cfg(feature = "xmldsig")]
385            Self::Encapsulate(algorithm) | Self::Decapsulate(algorithm) => Some(algorithm.uri()),
386            #[cfg(feature = "xmldsig")]
387            Self::Digest(algorithm) => Some(algorithm.uri()),
388            #[cfg(feature = "xmldsig")]
389            Self::Sign(algorithm) | Self::Verify(algorithm) => Some(algorithm.uri()),
390            #[cfg(feature = "xmldsig")]
391            Self::VerifyCertificate(algorithm) => algorithm.oid(),
392            #[cfg(feature = "xmlenc")]
393            Self::Encrypt(algorithm) | Self::Decrypt(algorithm) => Some(algorithm.uri()),
394            #[cfg(feature = "xmlenc")]
395            Self::KeyWrap(algorithm) | Self::KeyUnwrap(algorithm) => Some(algorithm.uri()),
396            #[cfg(feature = "xmlenc")]
397            Self::KeyTransport(parameters) | Self::KeyRecovery(parameters) => {
398                Some(parameters.algorithm.uri())
399            }
400            Self::KeyAgreement(parameters) => Some(parameters.algorithm),
401            #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
402            Self::Pkcs1v15Transport | Self::Pkcs1v15Recovery => {
403                Some(crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15.uri())
404            }
405            Self::Kdf(parameters) => Some(parameters.algorithm),
406            Self::Random => None,
407        }
408    }
409}
410
411/// Provider-neutral parameters for an asymmetric key-agreement operation.
412#[derive(Debug, Clone, Copy, PartialEq, Eq)]
413pub struct KeyAgreementParameters<'a> {
414    /// Standard key-agreement algorithm URI.
415    pub algorithm: &'a str,
416    /// Encoded peer public key in the algorithm's standard wire format.
417    pub peer_public_key: &'a [u8],
418}
419
420/// Provider-neutral context octets or significant bits for key derivation.
421#[derive(Debug, Clone, Copy, PartialEq, Eq)]
422pub enum KdfContext<'a> {
423    /// XMLEnc 1.1 section 5.6.2.2 legacy DH framing. KeySize is derived from
424    /// output_len; callers cannot supply a contradictory decimal key width.
425    LegacyDh {
426        /// URI of the consuming EncryptionMethod, not AgreementMethod.
427        encryption_algorithm: &'a str,
428        /// Decoded KA-Nonce octets, empty when the XML element is absent.
429        nonce: &'a [u8],
430    },
431    /// Complete octets, as used by HKDF info and byte-aligned ConcatKDF.
432    Octets(&'a [u8]),
433    /// An MSB-first bit string. Its last octet must have zero unused low bits;
434    /// the byte slice must have exactly `bit_len.div_ceil(8)` octets.
435    Bits {
436        /// Packed bits, without the XMLEnc attribute's padding-count prefix.
437        bytes: &'a [u8],
438        /// Number of significant bits, not rounded to the next octet.
439        bit_len: usize,
440    },
441}
442
443/// Provider-neutral parameters for a key-derivation operation.
444#[derive(Debug, Clone, Copy, PartialEq, Eq)]
445pub struct KdfParameters<'a> {
446    /// Standard KDF algorithm URI.
447    pub algorithm: &'a str,
448    /// Optional digest or PRF URI selected by the KDF parameters.
449    pub digest: Option<&'a str>,
450    /// Caller-provided salt, when the KDF defines one.
451    pub salt: &'a [u8],
452    /// Algorithm-specific context such as ConcatKDF OtherInfo bits or HKDF info octets.
453    pub info: KdfContext<'a>,
454    /// Policy-validated iteration count for iterative KDFs; zero when not applicable.
455    pub iterations: u64,
456    /// Policy-validated requested output length in bytes.
457    pub output_len: usize,
458}
459
460/// Provider-neutral X.509 certificate and CRL signature parameters.
461#[cfg(feature = "xmldsig")]
462#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
463#[non_exhaustive]
464pub enum X509SignatureAlgorithm {
465    /// DSA with the selected message digest.
466    Dsa(DigestAlgorithm),
467    /// RSASSA-PKCS1-v1_5 with the selected message digest.
468    RsaPkcs1v15(DigestAlgorithm),
469    /// RSASSA-PSS with explicit RFC 4055 parameters.
470    RsaPss {
471        /// Message digest applied to the signed certificate data.
472        digest: DigestAlgorithm,
473        /// Digest used by MGF1.
474        mgf_digest: DigestAlgorithm,
475        /// Salt length in octets.
476        salt_len: usize,
477    },
478    /// ECDSA with the selected message digest; SPKI selects the curve.
479    Ecdsa(DigestAlgorithm),
480    /// Pure Ed25519 as specified by RFC 8410.
481    Ed25519,
482    /// Pure Ed448 as specified by RFC 8410.
483    Ed448,
484    /// Pure ML-DSA or SLH-DSA with the PKIX-mandated empty context.
485    PostQuantum(crate::xmldsig::PqAlgorithm),
486}
487
488#[cfg(feature = "xmldsig")]
489impl X509SignatureAlgorithm {
490    /// Return the assigned AlgorithmIdentifier OID, or `None` for unsupported combinations.
491    #[must_use]
492    pub const fn oid(self) -> Option<&'static str> {
493        Some(match self {
494            #[cfg(feature = "legacy-algorithms")]
495            Self::Dsa(DigestAlgorithm::Md5 | DigestAlgorithm::Ripemd160)
496            | Self::RsaPkcs1v15(DigestAlgorithm::Md5 | DigestAlgorithm::Ripemd160)
497            | Self::Ecdsa(DigestAlgorithm::Md5 | DigestAlgorithm::Ripemd160) => return None,
498            Self::Dsa(DigestAlgorithm::Sha1) => "1.2.840.10040.4.3",
499            Self::Dsa(DigestAlgorithm::Sha224) => "2.16.840.1.101.3.4.3.1",
500            Self::Dsa(DigestAlgorithm::Sha256) => "2.16.840.1.101.3.4.3.2",
501            Self::Dsa(DigestAlgorithm::Sha384) => "2.16.840.1.101.3.4.3.3",
502            Self::Dsa(DigestAlgorithm::Sha512) => "2.16.840.1.101.3.4.3.4",
503            Self::Dsa(DigestAlgorithm::Sha3_224) => "2.16.840.1.101.3.4.3.5",
504            Self::Dsa(DigestAlgorithm::Sha3_256) => "2.16.840.1.101.3.4.3.6",
505            Self::Dsa(DigestAlgorithm::Sha3_384) => "2.16.840.1.101.3.4.3.7",
506            Self::Dsa(DigestAlgorithm::Sha3_512) => "2.16.840.1.101.3.4.3.8",
507            Self::RsaPkcs1v15(DigestAlgorithm::Sha1) => "1.2.840.113549.1.1.5",
508            Self::RsaPkcs1v15(DigestAlgorithm::Sha224) => "1.2.840.113549.1.1.14",
509            Self::RsaPkcs1v15(DigestAlgorithm::Sha256) => "1.2.840.113549.1.1.11",
510            Self::RsaPkcs1v15(DigestAlgorithm::Sha384) => "1.2.840.113549.1.1.12",
511            Self::RsaPkcs1v15(DigestAlgorithm::Sha512) => "1.2.840.113549.1.1.13",
512            Self::RsaPkcs1v15(DigestAlgorithm::Sha3_224) => "2.16.840.1.101.3.4.3.13",
513            Self::RsaPkcs1v15(DigestAlgorithm::Sha3_256) => "2.16.840.1.101.3.4.3.14",
514            Self::RsaPkcs1v15(DigestAlgorithm::Sha3_384) => "2.16.840.1.101.3.4.3.15",
515            Self::RsaPkcs1v15(DigestAlgorithm::Sha3_512) => "2.16.840.1.101.3.4.3.16",
516            Self::RsaPss { .. } => "1.2.840.113549.1.1.10",
517            Self::Ecdsa(DigestAlgorithm::Sha1) => "1.2.840.10045.4.1",
518            Self::Ecdsa(DigestAlgorithm::Sha224) => "1.2.840.10045.4.3.1",
519            Self::Ecdsa(DigestAlgorithm::Sha256) => "1.2.840.10045.4.3.2",
520            Self::Ecdsa(DigestAlgorithm::Sha384) => "1.2.840.10045.4.3.3",
521            Self::Ecdsa(DigestAlgorithm::Sha512) => "1.2.840.10045.4.3.4",
522            Self::Ecdsa(DigestAlgorithm::Sha3_224) => "2.16.840.1.101.3.4.3.9",
523            Self::Ecdsa(DigestAlgorithm::Sha3_256) => "2.16.840.1.101.3.4.3.10",
524            Self::Ecdsa(DigestAlgorithm::Sha3_384) => "2.16.840.1.101.3.4.3.11",
525            Self::Ecdsa(DigestAlgorithm::Sha3_512) => "2.16.840.1.101.3.4.3.12",
526            Self::Ed25519 => "1.3.101.112",
527            Self::Ed448 => "1.3.101.113",
528            Self::PostQuantum(algorithm) => algorithm.oid(),
529        })
530    }
531}
532
533/// Structured invalid-input reasons returned by cryptographic providers.
534#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
535#[non_exhaustive]
536pub enum ProviderInputError {
537    /// Invalid ML-KEM key encoding, parameter set or consistency check.
538    #[error("invalid ML-KEM key material")]
539    MlKemKey,
540    /// Invalid public ciphertext framing, never an implicit-rejection signal.
541    #[error("invalid ML-KEM ciphertext length")]
542    MlKemCiphertext,
543    /// An EC scalar or peer point is not valid for the selected named curve.
544    #[error("invalid ECDH key material")]
545    EcdhKey,
546    /// An operation-owned ephemeral sender key has already been consumed.
547    #[error("ECDH-ES sender key already consumed; supply a fresh key pair")]
548    ConsumedEphemeralKey,
549    /// Finite-field domain, scalar, or peer fails DH validation.
550    #[error("invalid DH key material")]
551    DhKey,
552    /// An HKDF request violates the primitive's parameter or output contract.
553    #[error("invalid HKDF parameters")]
554    HkdfParameters,
555    /// A PBKDF2 request has invalid iterations, length, or unrelated context.
556    #[error("invalid PBKDF2 parameters")]
557    Pbkdf2Parameters,
558    /// A ConcatKDF request violates its bit-string or counter contract.
559    #[error("invalid ConcatKDF parameters")]
560    ConcatKdfParameters,
561    /// A legacy DH KDF request has invalid framing or exceeds its byte counter.
562    #[error("invalid legacy DH KDF parameters")]
563    LegacyDhKdfParameters,
564    /// A primitive rejected a key or IV after its public preconditions were checked.
565    #[error("failed to initialize {0}")]
566    PrimitiveInitialization(&'static str),
567    /// AES-CBC input does not contain an IV followed by complete blocks.
568    #[error("invalid AES-CBC framing")]
569    AesCbcFraming,
570    /// AES-CBC block decryption failed.
571    #[error("invalid AES-CBC ciphertext")]
572    AesCbcCiphertext,
573    /// AES-GCM input does not contain a nonce and authentication tag.
574    #[error("invalid AES-GCM framing")]
575    AesGcmFraming,
576    /// AES key-wrap input or output framing is invalid.
577    #[error("invalid AES key-wrap framing")]
578    AesKeyWrapFraming,
579    /// Legacy compatibility variant retained for downstream construction and matching.
580    ///
581    /// Explicit MGF parameters are valid for both RSA-OAEP URIs, so current providers never
582    /// return this reason.
583    #[deprecated(note = "explicit MGF parameters are supported for both RSA-OAEP URIs")]
584    #[error("legacy RSA-OAEP requires MGF1-SHA1")]
585    LegacyRsaOaepMgf,
586}
587
588/// Failure returned by a cryptographic provider.
589#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
590#[non_exhaustive]
591pub enum ProviderError {
592    /// A secret-bearing external handle cannot be exported as bytes.
593    #[error("key is not exportable")]
594    KeyNotExportable,
595    /// Redacted external-provider failure: never includes credentials or selectors.
596    #[error("external cryptographic provider failed: {0}")]
597    External(ExternalProviderError),
598    /// The selected provider does not implement the operation/parameters.
599    #[error("provider does not support {operation:?} with algorithm {algorithm:?}")]
600    Unsupported {
601        /// Requested operation.
602        operation: ProviderOperation,
603        /// Requested algorithm URI or name.
604        algorithm: Option<String>,
605    },
606    /// A key has the wrong size for the selected algorithm.
607    #[error("invalid key size: expected {expected} bytes, got {actual}")]
608    InvalidKeySize {
609        /// Required key length.
610        expected: usize,
611        /// Supplied key length.
612        actual: usize,
613    },
614    /// A provider reported success but returned bytes that violate the selected
615    /// operation's fixed-size output contract.
616    #[error(
617        "invalid provider output size for {operation:?}: expected {expected} bytes, got {actual}"
618    )]
619    InvalidOutputSize {
620        /// Operation whose output contract was violated.
621        operation: ProviderOperation,
622        /// Exact output length required by the algorithm.
623        expected: usize,
624        /// Actual provider output length.
625        actual: usize,
626    },
627    /// A provider reported success but returned bytes outside the selected
628    /// operation's variable-size output contract.
629    #[error(
630        "invalid provider output size for {operation:?}: expected {minimum}..={maximum} bytes, got {actual}"
631    )]
632    InvalidOutputSizeRange {
633        /// Operation whose output contract was violated.
634        operation: ProviderOperation,
635        /// Smallest output length permitted by the algorithm.
636        minimum: usize,
637        /// Largest output length permitted by the algorithm.
638        maximum: usize,
639        /// Actual provider output length.
640        actual: usize,
641    },
642    /// Input framing, padding, or primitive initialization is invalid.
643    #[error("invalid cryptographic input: {0}")]
644    InvalidInput(ProviderInputError),
645    /// Authenticated decryption or key-wrap integrity validation failed.
646    #[error("cryptographic authentication failed")]
647    AuthenticationFailed,
648    /// Operating-system randomness was unavailable.
649    #[error("operating-system random number generation failed: {0}")]
650    Random(String),
651}
652
653/// Operational failure classes for an explicitly selected external provider.
654#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
655#[non_exhaustive]
656pub enum ExternalProviderError {
657    /// The module, token or device is unavailable.
658    #[error("module or token unavailable")]
659    Unavailable,
660    /// Authentication failed or the operation requires authentication.
661    #[error("authentication failed")]
662    Credentials,
663    /// The selected object does not exist, is ambiguous, or has expired.
664    #[error("key object unavailable or ambiguous")]
665    Object,
666    /// Token key usage does not authorize this primitive.
667    #[error("key usage denied")]
668    Usage,
669    /// The key and selected provider do not share an execution domain.
670    #[error("key provider binding mismatch")]
671    Binding,
672    /// A provider-side operation or synchronization failed.
673    #[error("token operation failed")]
674    Operation,
675}
676
677/// Caller-owned identity for external-provider handles, independent of engine name.
678#[derive(Clone, Default)]
679pub struct ProviderBinding(std::sync::Arc<()>);
680
681impl ProviderBinding {
682    /// Test identity without copying secret or public key material.
683    pub fn matches(&self, other: &Self) -> bool {
684        std::sync::Arc::ptr_eq(&self.0, &other.0)
685    }
686}
687
688/// Non-exportable symmetric key used for content decryption.
689#[cfg(feature = "xmlenc")]
690pub trait ContentDecryptionKey: Send + Sync {
691    /// Exact external domain owning this handle.
692    fn provider_binding(&self) -> &ProviderBinding;
693    /// Public key width in bytes.
694    fn key_len(&self) -> usize;
695    /// Execute within the selected provider's execution domain.
696    fn decrypt_with_provider(
697        &self,
698        provider: &dyn CryptoProvider,
699        algorithm: DataEncryptionAlgorithm,
700        ciphertext: &[u8],
701    ) -> Result<Vec<u8>, ProviderError>;
702}
703
704/// An opaque KEK that unwraps directly into a non-exportable content key.
705#[cfg(feature = "xmlenc")]
706pub trait KeyUnwrappingKey: Send + Sync {
707    /// Exact external domain owning this handle.
708    fn provider_binding(&self) -> &ProviderBinding;
709    /// Unwrap within the selected execution domain without reading CKA_VALUE.
710    fn unwrap_with_provider(
711        &self,
712        provider: &dyn CryptoProvider,
713        algorithm: KeyWrapAlgorithm,
714        content_algorithm: DataEncryptionAlgorithm,
715        wrapped: &[u8],
716    ) -> Result<RecoveredContentKey, ProviderError>;
717}
718
719/// Opaque public-key handle used for asymmetric key transport.
720///
721/// Implementations own their key material and operation. The orchestration
722/// layer can inspect only public RSA components needed for policy validation
723/// and output framing; it cannot recover a backend-specific key object.
724#[cfg(feature = "xmlenc")]
725pub trait KeyTransportKey: Send + Sync {
726    /// External execution domain, or `None` for software-owned public keys.
727    fn provider_binding(&self) -> Option<&ProviderBinding> {
728        None
729    }
730    /// Execute historical transport; unsupported opaque handles never fall back.
731    #[cfg(feature = "legacy-algorithms")]
732    fn transport_pkcs1v15(
733        &self,
734        _provider: &dyn CryptoProvider,
735        _plaintext: &[u8],
736    ) -> Result<Vec<u8>, ProviderError> {
737        Err(ProviderError::Unsupported {
738            operation: ProviderOperation::KeyTransport,
739            algorithm: Some(
740                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
741                    .uri()
742                    .into(),
743            ),
744        })
745    }
746    /// RSA modulus bytes without redundant leading zero octets.
747    ///
748    /// These components must identify the exact key used by
749    /// [`Self::transport_with_provider`]; returning metadata for another key
750    /// would violate the policy boundary.
751    fn rsa_modulus(&self) -> Cow<'_, [u8]>;
752
753    /// RSA public exponent bytes without redundant leading zero octets.
754    fn rsa_exponent(&self) -> Cow<'_, [u8]>;
755
756    /// Execute OAEP key transport using the selected provider's randomness.
757    fn transport_with_provider(
758        &self,
759        provider: &dyn CryptoProvider,
760        parameters: &RsaOaepParameters,
761        plaintext: &[u8],
762    ) -> Result<Vec<u8>, ProviderError>;
763}
764
765/// Opaque private-key handle used to recover transported key bytes.
766///
767/// Private key material never crosses this boundary. The ciphertext size is
768/// public metadata required to reject malformed RSA inputs before dispatch.
769#[cfg(feature = "xmlenc")]
770pub trait KeyRecoveryKey: Send + Sync {
771    /// Recover without exporting a CEK when supported by the execution domain.
772    fn recover_content_with_provider(
773        &self,
774        provider: &dyn CryptoProvider,
775        parameters: &RsaOaepParameters,
776        _content_algorithm: DataEncryptionAlgorithm,
777        ciphertext: &[u8],
778    ) -> Result<RecoveredContentKey, ProviderError> {
779        self.recover_with_provider(provider, parameters, ciphertext)
780            .map(RecoveredContentKey::confirmed)
781    }
782    /// Recover a content key of the caller's already validated algorithm width.
783    #[cfg(feature = "legacy-algorithms")]
784    fn recover_pkcs1v15(
785        &self,
786        _provider: &dyn CryptoProvider,
787        _ciphertext: &[u8],
788        _key_len: usize,
789    ) -> Result<RecoveredContentKey, ProviderError> {
790        Err(ProviderError::Unsupported {
791            operation: ProviderOperation::KeyRecovery,
792            algorithm: Some(
793                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
794                    .uri()
795                    .into(),
796            ),
797        })
798    }
799    /// Native engine binding; an incompatible provider must reject this handle.
800    fn provider_name(&self) -> Option<&'static str> {
801        None
802    }
803    /// Borrow the matching public SPKI when available for certificate binding.
804    fn public_spki(&self) -> Option<&[u8]> {
805        None
806    }
807    /// Exact mathematical bit length of the recovery key's public modulus,
808    /// excluding leading zero padding. Not the rounded ciphertext width.
809    fn rsa_modulus_bits(&self) -> usize;
810
811    /// Public exponent of that same key, or `None` if wider than 64 bits.
812    /// Opaque providers expose public metadata without copying private material.
813    fn rsa_public_exponent(&self) -> Option<u64>;
814
815    /// Exact RSA ciphertext width in bytes for the key used by
816    /// [`Self::recover_with_provider`].
817    fn ciphertext_len(&self) -> usize;
818
819    /// Execute OAEP recovery using the selected provider's randomness.
820    fn recover_with_provider(
821        &self,
822        provider: &dyn CryptoProvider,
823        parameters: &RsaOaepParameters,
824        ciphertext: &[u8],
825    ) -> Result<Vec<u8>, ProviderError>;
826}
827
828/// Opaque private-key handle used for provider-defined key agreement.
829pub trait KeyAgreementKey: Send + Sync {
830    /// Public modulus/subgroup bit widths for finite-field DH. Operations use
831    /// these to recheck key strength under their own policy; no secret is exported.
832    fn dh_domain_bits(&self) -> Option<(usize, usize)> {
833        None
834    }
835    /// External execution domain, when the private key resides outside this process.
836    fn provider_binding(&self) -> Option<&ProviderBinding> {
837        None
838    }
839    /// Derive the raw shared secret for the supplied peer and parameters.
840    fn agree(&self, parameters: &KeyAgreementParameters<'_>) -> Result<Vec<u8>, ProviderError>;
841}
842
843/// Incremental message digest owned by one provider operation.
844///
845/// Input is absorbed in order across any number of [`update`](Self::update)
846/// calls; [`finalize`](Self::finalize) consumes the state. A provider may
847/// serialize its other operations while a state is live, so callers finish a
848/// digest before starting another operation on the same provider.
849///
850/// This is the backend interface of the selected provider (runtime backend,
851/// FIPS selection, token sessions), not a cross-product contract: the shared
852/// streaming digest callbacks of the `signature-primitives` packages are
853/// implemented by adapters on top of it.
854#[cfg(feature = "xmldsig")]
855pub trait DigestState {
856    /// Absorb the next input bytes.
857    fn update(&mut self, data: &[u8]) -> Result<(), ProviderError>;
858
859    /// Return the digest of all absorbed bytes.
860    fn finalize(self: Box<Self>) -> Result<Vec<u8>, ProviderError>;
861}
862
863/// Provider operations used by the XML Security pipelines.
864pub trait CryptoProvider: Send + Sync {
865    /// External execution domain. Different instances of one engine are not interchangeable.
866    fn binding(&self) -> Option<&ProviderBinding> {
867        None
868    }
869
870    /// Recover a candidate without requiring its symmetric value to leave the provider.
871    #[cfg(feature = "xmlenc")]
872    fn recover_content_key(
873        &self,
874        key: &dyn KeyRecoveryKey,
875        parameters: &RsaOaepParameters,
876        _content_algorithm: DataEncryptionAlgorithm,
877        ciphertext: &[u8],
878    ) -> Result<RecoveredContentKey, ProviderError> {
879        self.recover_key(key, parameters, ciphertext)
880            .map(RecoveredContentKey::confirmed)
881    }
882
883    /// Unwrap into an opaque content key. Providers must opt in explicitly.
884    #[cfg(feature = "xmlenc")]
885    fn unwrap_content_key(
886        &self,
887        _key: &dyn KeyUnwrappingKey,
888        algorithm: KeyWrapAlgorithm,
889        _content_algorithm: DataEncryptionAlgorithm,
890        _wrapped: &[u8],
891    ) -> Result<RecoveredContentKey, ProviderError> {
892        Err(ProviderError::Unsupported {
893            operation: ProviderOperation::KeyUnwrap,
894            algorithm: Some(algorithm.uri().into()),
895        })
896    }
897
898    /// Decrypt with an opaque key; capability remains distinct from policy permission.
899    #[cfg(feature = "xmlenc")]
900    fn decrypt_content_key(
901        &self,
902        algorithm: DataEncryptionAlgorithm,
903        _key: &dyn ContentDecryptionKey,
904        _ciphertext: &[u8],
905    ) -> Result<Vec<u8>, ProviderError> {
906        Err(ProviderError::Unsupported {
907            operation: ProviderOperation::Decrypt,
908            algorithm: Some(algorithm.uri().into()),
909        })
910    }
911    /// Stable provider name for diagnostics and capability reporting.
912    fn name(&self) -> &'static str;
913
914    /// Import original PKCS#8 input into this engine without exporting another
915    /// provider's private handle. Policy and input budgets are enforced by the caller.
916    #[cfg(feature = "xmldsig")]
917    fn import_signing_key(
918        &self,
919        algorithm: crate::xmldsig::SignatureAlgorithm,
920        _pkcs8: &[u8],
921    ) -> Result<Box<dyn crate::xmldsig::SigningKey>, crate::xmldsig::SigningKeyError> {
922        Err(ProviderError::Unsupported {
923            operation: ProviderOperation::Sign,
924            algorithm: Some(algorithm.uri().into()),
925        }
926        .into())
927    }
928
929    /// Import original PKCS#8 input into a provider-owned RSA recovery handle.
930    #[cfg(feature = "xmlenc")]
931    fn import_recovery_key(
932        &self,
933        _pkcs8: &[u8],
934    ) -> Result<std::sync::Arc<dyn KeyRecoveryKey>, ProviderError> {
935        Err(ProviderError::Unsupported {
936            operation: ProviderOperation::KeyRecovery,
937            algorithm: None,
938        })
939    }
940
941    /// Return whether this build can dispatch the requested capability.
942    ///
943    /// [`ProviderCapability::Sign`] and [`ProviderCapability::Verify`] describe
944    /// provider dispatch only: the supplied opaque key performs the executable
945    /// algorithm-support check when the operation runs.
946    fn supports(&self, capability: ProviderCapability<'_>) -> bool;
947
948    /// Import a recipient SPKI into the selected engine. The caller reserves
949    /// import work and grants permission; unavailable engines never fall back.
950    #[cfg(feature = "xmldsig")]
951    fn import_encapsulation_key(
952        &self,
953        algorithm: KeyEncapsulationAlgorithm,
954        _spki: &[u8],
955    ) -> Result<std::sync::Arc<dyn KeyEncapsulationKey>, ProviderError> {
956        Err(ProviderError::Unsupported {
957            operation: ProviderOperation::Encapsulate,
958            algorithm: Some(algorithm.uri().into()),
959        })
960    }
961
962    /// Import a recipient PKCS#8 into this engine without exporting another
963    /// provider's private handle. The encoded parameter set must match the request.
964    #[cfg(feature = "xmldsig")]
965    fn import_decapsulation_key(
966        &self,
967        algorithm: KeyEncapsulationAlgorithm,
968        _pkcs8: &[u8],
969    ) -> Result<std::sync::Arc<dyn KeyDecapsulationKey>, ProviderError> {
970        Err(ProviderError::Unsupported {
971            operation: ProviderOperation::Decapsulate,
972            algorithm: Some(algorithm.uri().into()),
973        })
974    }
975
976    /// Fill caller-owned output with cryptographically secure random bytes.
977    fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError>;
978
979    /// Encapsulate through this engine; unsupported engines never delegate to RustCrypto.
980    #[cfg(feature = "xmldsig")]
981    fn encapsulate_key(
982        &self,
983        key: &dyn KeyEncapsulationKey,
984    ) -> Result<EncapsulatedKey, ProviderError> {
985        Err(ProviderError::Unsupported {
986            operation: ProviderOperation::Encapsulate,
987            algorithm: Some(key.algorithm().uri().into()),
988        })
989    }
990
991    /// Decapsulate without revealing implicit-rejection validity.
992    #[cfg(feature = "xmldsig")]
993    fn decapsulate_key(
994        &self,
995        key: &dyn KeyDecapsulationKey,
996        ciphertext: &[u8],
997    ) -> Result<zeroize::Zeroizing<[u8; 32]>, ProviderError> {
998        let _ = ciphertext;
999        Err(ProviderError::Unsupported {
1000            operation: ProviderOperation::Decapsulate,
1001            algorithm: Some(key.algorithm().uri().into()),
1002        })
1003    }
1004
1005    /// Start an incremental message digest; streamed input never needs to be
1006    /// collected in memory first.
1007    #[cfg(feature = "xmldsig")]
1008    fn begin_digest(
1009        &self,
1010        algorithm: DigestAlgorithm,
1011    ) -> Result<Box<dyn DigestState + '_>, ProviderError>;
1012
1013    /// Compute the message digest of one contiguous input.
1014    #[cfg(feature = "xmldsig")]
1015    fn digest(&self, algorithm: DigestAlgorithm, data: &[u8]) -> Result<Vec<u8>, ProviderError> {
1016        let mut state = self.begin_digest(algorithm)?;
1017        state.update(data)?;
1018        state.finalize()
1019    }
1020
1021    /// Sign bytes with an opaque key handle.
1022    ///
1023    /// Providers that delegate primitive signing to the supplied key must call
1024    /// [`crate::xmldsig::SigningKey::sign_with_provider`] so randomized
1025    /// primitives consume this provider's randomness.
1026    #[cfg(feature = "xmldsig")]
1027    fn sign(
1028        &self,
1029        key: &dyn crate::xmldsig::SigningKey,
1030        algorithm: crate::xmldsig::SignatureAlgorithm,
1031        data: &[u8],
1032    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError>;
1033
1034    /// Verify bytes with an opaque key handle.
1035    ///
1036    /// The XMLDSig facade validates algorithm- and key-specific signature
1037    /// framing before this provider boundary.
1038    #[cfg(feature = "xmldsig")]
1039    fn verify(
1040        &self,
1041        key: &dyn crate::xmldsig::VerifyingKey,
1042        algorithm: crate::xmldsig::SignatureAlgorithm,
1043        data: &[u8],
1044        signature: &[u8],
1045    ) -> Result<bool, crate::xmldsig::DsigError>;
1046
1047    /// Sign with explicit domain separation; unsupported contexts must not be discarded.
1048    #[cfg(feature = "xmldsig")]
1049    fn sign_with_context(
1050        &self,
1051        key: &dyn crate::xmldsig::SigningKey,
1052        algorithm: crate::xmldsig::SignatureAlgorithm,
1053        context: &crate::xmldsig::SignatureContext,
1054        data: &[u8],
1055    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1056        if !context.as_bytes().is_empty() {
1057            return Err(crate::xmldsig::SigningKeyError::UnsupportedAlgorithm {
1058                uri: algorithm.uri().to_owned(),
1059            });
1060        }
1061        self.sign(key, algorithm, data)
1062    }
1063
1064    /// Verify with explicit domain separation; unsupported contexts fail closed.
1065    #[cfg(feature = "xmldsig")]
1066    fn verify_with_context(
1067        &self,
1068        key: &dyn crate::xmldsig::VerifyingKey,
1069        algorithm: crate::xmldsig::SignatureAlgorithm,
1070        context: &crate::xmldsig::SignatureContext,
1071        data: &[u8],
1072        signature: &[u8],
1073    ) -> Result<bool, crate::xmldsig::DsigError> {
1074        if !context.as_bytes().is_empty() {
1075            return Err(
1076                crate::xmldsig::SignatureVerificationError::UnsupportedAlgorithm {
1077                    uri: algorithm.uri().to_owned(),
1078                }
1079                .into(),
1080            );
1081        }
1082        self.verify(key, algorithm, data, signature)
1083    }
1084
1085    /// Verify once while reporting the actual successful leaf. Providers that
1086    /// cannot identify it retain ordinary verification and report no receipt.
1087    #[cfg(feature = "xmldsig")]
1088    fn verify_with_key_receipt(
1089        &self,
1090        key: &dyn crate::xmldsig::VerifyingKey,
1091        input: SignatureVerificationInput<'_>,
1092        _observer: &mut dyn FnMut(
1093            KeyVerificationEvent<'_>,
1094        ) -> Result<(), crate::xmldsig::DsigError>,
1095    ) -> Result<bool, crate::xmldsig::DsigError> {
1096        self.verify_with_context(
1097            key,
1098            input.algorithm,
1099            input.context,
1100            input.data,
1101            input.signature,
1102        )
1103    }
1104
1105    /// Compare the successful public receipt with original effective PKIX key
1106    /// state. Unknown semantics are Unproven, never a second provider fallback.
1107    #[cfg(feature = "xmldsig")]
1108    fn associate_verification_key(
1109        &self,
1110        _actual: &[u8],
1111        _expected: structured_pkix::path::WorkingKey<'_>,
1112        _charge: &dyn Fn(u64) -> Result<(), structured_asn1::memory::MemoryError>,
1113    ) -> Result<KeyAssociation, crate::xmldsig::DsigError> {
1114        Ok(KeyAssociation::Unproven)
1115    }
1116
1117    /// Verify an X.509 certificate or CRL signature under its issuer SPKI.
1118    #[cfg(feature = "xmldsig")]
1119    fn verify_x509_signature(
1120        &self,
1121        algorithm: X509SignatureAlgorithm,
1122        signed_data: &[u8],
1123        signature: &[u8],
1124        issuer_spki_der: &[u8],
1125    ) -> Result<bool, ProviderError> {
1126        let _ = (signed_data, signature, issuer_spki_der);
1127        Err(ProviderError::Unsupported {
1128            operation: ProviderOperation::VerifyCertificate,
1129            algorithm: algorithm.oid().map(str::to_owned),
1130        })
1131    }
1132
1133    /// Encrypt XMLEnc content bytes, including standard framing.
1134    #[cfg(feature = "xmlenc")]
1135    fn encrypt_data(
1136        &self,
1137        algorithm: DataEncryptionAlgorithm,
1138        key: &[u8],
1139        plaintext: &[u8],
1140    ) -> Result<Vec<u8>, ProviderError>;
1141
1142    /// Decrypt XMLEnc content bytes, including framing validation.
1143    #[cfg(feature = "xmlenc")]
1144    fn decrypt_data(
1145        &self,
1146        algorithm: DataEncryptionAlgorithm,
1147        key: &[u8],
1148        ciphertext: &[u8],
1149    ) -> Result<Vec<u8>, ProviderError>;
1150
1151    /// Encrypt while preserving XML-carried cipher parameters. Providers
1152    /// must implement the profile explicitly, never discard it or fall back.
1153    #[cfg(feature = "xmlenc")]
1154    fn encrypt_data_with_parameters(
1155        &self,
1156        algorithm: DataEncryptionAlgorithm,
1157        key: &[u8],
1158        plaintext: &[u8],
1159        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1160    ) -> Result<Vec<u8>, ProviderError> {
1161        if parameters.is_some() {
1162            return Err(ProviderError::Unsupported {
1163                operation: ProviderOperation::Encrypt,
1164                algorithm: Some(algorithm.uri().into()),
1165            });
1166        }
1167        self.encrypt_data(algorithm, key, plaintext)
1168    }
1169
1170    /// Decrypt with the same explicit parameter contract as encryption.
1171    #[cfg(feature = "xmlenc")]
1172    fn decrypt_data_with_parameters(
1173        &self,
1174        algorithm: DataEncryptionAlgorithm,
1175        key: &[u8],
1176        ciphertext: &[u8],
1177        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1178    ) -> Result<Vec<u8>, ProviderError> {
1179        if parameters.is_some() {
1180            return Err(ProviderError::Unsupported {
1181                operation: ProviderOperation::Decrypt,
1182                algorithm: Some(algorithm.uri().into()),
1183            });
1184        }
1185        self.decrypt_data(algorithm, key, ciphertext)
1186    }
1187
1188    /// Wrap a content key with RFC 3394 AES Key Wrap.
1189    ///
1190    /// Successful output contains the complete RFC 3394 value and is exactly
1191    /// eight bytes longer than `key`. The XMLEnc facade validates that framing
1192    /// before serializing provider output.
1193    #[cfg(feature = "xmlenc")]
1194    fn wrap_key(
1195        &self,
1196        algorithm: KeyWrapAlgorithm,
1197        kek: &[u8],
1198        key: &[u8],
1199    ) -> Result<Vec<u8>, ProviderError>;
1200
1201    /// Unwrap a content key with RFC 3394 AES Key Wrap.
1202    #[cfg(feature = "xmlenc")]
1203    fn unwrap_key(
1204        &self,
1205        algorithm: KeyWrapAlgorithm,
1206        kek: &[u8],
1207        wrapped: &[u8],
1208    ) -> Result<Vec<u8>, ProviderError>;
1209
1210    /// Wrap key bytes using an opaque RSA public-key operation.
1211    #[cfg(feature = "xmlenc")]
1212    fn transport_key(
1213        &self,
1214        key: &dyn KeyTransportKey,
1215        parameters: &RsaOaepParameters,
1216        plaintext: &[u8],
1217    ) -> Result<Vec<u8>, ProviderError>;
1218
1219    /// Recover key bytes using an opaque RSA private-key operation.
1220    #[cfg(feature = "xmlenc")]
1221    fn recover_key(
1222        &self,
1223        key: &dyn KeyRecoveryKey,
1224        parameters: &RsaOaepParameters,
1225        ciphertext: &[u8],
1226    ) -> Result<Vec<u8>, ProviderError>;
1227
1228    /// Transport using RSAES-PKCS1-v1_5; not an OAEP configuration.
1229    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1230    fn transport_pkcs1v15(
1231        &self,
1232        _key: &dyn KeyTransportKey,
1233        _plaintext: &[u8],
1234    ) -> Result<Vec<u8>, ProviderError> {
1235        Err(ProviderError::Unsupported {
1236            operation: ProviderOperation::KeyTransport,
1237            algorithm: Some(
1238                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
1239                    .uri()
1240                    .into(),
1241            ),
1242        })
1243    }
1244
1245    /// Recover a fixed-width content key without exposing padding validity.
1246    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1247    fn recover_pkcs1v15(
1248        &self,
1249        _key: &dyn KeyRecoveryKey,
1250        _ciphertext: &[u8],
1251        _key_len: usize,
1252    ) -> Result<RecoveredContentKey, ProviderError> {
1253        Err(ProviderError::Unsupported {
1254            operation: ProviderOperation::KeyRecovery,
1255            algorithm: Some(
1256                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
1257                    .uri()
1258                    .into(),
1259            ),
1260        })
1261    }
1262
1263    /// Perform key agreement with an opaque provider-owned private key.
1264    fn agree_key(
1265        &self,
1266        key: &dyn KeyAgreementKey,
1267        parameters: &KeyAgreementParameters<'_>,
1268    ) -> Result<Vec<u8>, ProviderError> {
1269        self.require_capability(ProviderCapability::KeyAgreement(parameters))?;
1270        if let Some(binding) = key.provider_binding()
1271            && !self
1272                .binding()
1273                .is_some_and(|selected| selected.matches(binding))
1274        {
1275            return Err(ProviderError::External(ExternalProviderError::Binding));
1276        }
1277        key.agree(parameters)
1278    }
1279
1280    /// Derive key bytes from caller-owned secret material.
1281    ///
1282    /// Implementations that advertise [`ProviderCapability::Kdf`] must perform
1283    /// the advertised derivation here. This method is required so capability
1284    /// discovery cannot silently inherit a contradictory unsupported default.
1285    fn derive_key(
1286        &self,
1287        parameters: &KdfParameters<'_>,
1288        secret: &[u8],
1289    ) -> Result<Vec<u8>, ProviderError>;
1290
1291    /// Reject an unavailable exact capability without falling back.
1292    fn require_capability(&self, capability: ProviderCapability<'_>) -> Result<(), ProviderError> {
1293        if self.supports(capability) {
1294            Ok(())
1295        } else {
1296            Err(ProviderError::Unsupported {
1297                operation: capability.operation(),
1298                algorithm: capability.algorithm().map(str::to_owned),
1299            })
1300        }
1301    }
1302}
1303
1304/// Pure-Rust provider backed by RustCrypto crates.
1305#[derive(Debug, Clone, Copy, Default)]
1306pub struct RustCryptoProvider;
1307
1308/// Defines the RustCrypto incremental hasher for every compiled algorithm.
1309#[cfg(feature = "xmldsig")]
1310macro_rules! rust_crypto_digest {
1311    ($($(#[$attr:meta])* $variant:ident => $hasher:ty),+ $(,)?) => {
1312        enum RustCryptoDigest {
1313            $($(#[$attr])* $variant($hasher),)+
1314        }
1315
1316        impl RustCryptoDigest {
1317            fn new(algorithm: DigestAlgorithm) -> Self {
1318                use sha2::Digest as _;
1319                match algorithm {
1320                    $($(#[$attr])* DigestAlgorithm::$variant => Self::$variant(<$hasher>::new()),)+
1321                }
1322            }
1323        }
1324
1325        impl DigestState for RustCryptoDigest {
1326            fn update(&mut self, data: &[u8]) -> Result<(), ProviderError> {
1327                use sha2::Digest as _;
1328                match self {
1329                    $($(#[$attr])* Self::$variant(hasher) => hasher.update(data),)+
1330                }
1331                Ok(())
1332            }
1333
1334            fn finalize(self: Box<Self>) -> Result<Vec<u8>, ProviderError> {
1335                use sha2::Digest as _;
1336                Ok(match *self {
1337                    $($(#[$attr])* Self::$variant(hasher) => hasher.finalize().to_vec(),)+
1338                })
1339            }
1340        }
1341    };
1342}
1343
1344#[cfg(feature = "xmldsig")]
1345rust_crypto_digest! {
1346    #[cfg(feature = "legacy-algorithms")]
1347    Md5 => md5::Md5,
1348    #[cfg(feature = "legacy-algorithms")]
1349    Ripemd160 => ripemd::Ripemd160,
1350    Sha1 => sha1::Sha1,
1351    Sha224 => sha2::Sha224,
1352    Sha256 => sha2::Sha256,
1353    Sha384 => sha2::Sha384,
1354    Sha512 => sha2::Sha512,
1355    Sha3_224 => sha3::Sha3_224,
1356    Sha3_256 => sha3::Sha3_256,
1357    Sha3_384 => sha3::Sha3_384,
1358    Sha3_512 => sha3::Sha3_512,
1359}
1360
1361/// Opaque RSA public-key handle for the built-in RustCrypto provider.
1362#[cfg(feature = "xmlenc")]
1363#[derive(Clone)]
1364pub struct RustCryptoRsaPublicKey {
1365    key: rsa::RsaPublicKey,
1366    modulus: Vec<u8>,
1367    exponent: Vec<u8>,
1368}
1369
1370#[cfg(feature = "xmlenc")]
1371impl RustCryptoRsaPublicKey {
1372    /// Wrap an already parsed RustCrypto RSA public key.
1373    #[must_use]
1374    pub fn new(key: rsa::RsaPublicKey) -> Self {
1375        use rsa::traits::PublicKeyParts as _;
1376        let modulus = key.n().to_be_bytes_trimmed_vartime().into_vec();
1377        let exponent = key.e().to_be_bytes_trimmed_vartime().into_vec();
1378        Self {
1379            key,
1380            modulus,
1381            exponent,
1382        }
1383    }
1384}
1385
1386#[cfg(feature = "xmlenc")]
1387impl From<rsa::RsaPublicKey> for RustCryptoRsaPublicKey {
1388    fn from(key: rsa::RsaPublicKey) -> Self {
1389        Self::new(key)
1390    }
1391}
1392
1393#[cfg(feature = "xmlenc")]
1394impl KeyTransportKey for RustCryptoRsaPublicKey {
1395    #[cfg(feature = "legacy-algorithms")]
1396    fn transport_pkcs1v15(
1397        &self,
1398        provider: &dyn CryptoProvider,
1399        plaintext: &[u8],
1400    ) -> Result<Vec<u8>, ProviderError> {
1401        rustcrypto::transport_pkcs1v15(provider, &self.key, plaintext)
1402    }
1403    fn rsa_modulus(&self) -> Cow<'_, [u8]> {
1404        Cow::Borrowed(&self.modulus)
1405    }
1406
1407    fn rsa_exponent(&self) -> Cow<'_, [u8]> {
1408        Cow::Borrowed(&self.exponent)
1409    }
1410
1411    fn transport_with_provider(
1412        &self,
1413        provider: &dyn CryptoProvider,
1414        parameters: &RsaOaepParameters,
1415        plaintext: &[u8],
1416    ) -> Result<Vec<u8>, ProviderError> {
1417        rustcrypto::transport_key(provider, &self.key, parameters, plaintext)
1418    }
1419}
1420
1421#[cfg(feature = "xmlenc")]
1422impl KeyTransportKey for rsa::RsaPublicKey {
1423    #[cfg(feature = "legacy-algorithms")]
1424    fn transport_pkcs1v15(
1425        &self,
1426        provider: &dyn CryptoProvider,
1427        plaintext: &[u8],
1428    ) -> Result<Vec<u8>, ProviderError> {
1429        rustcrypto::transport_pkcs1v15(provider, self, plaintext)
1430    }
1431    fn rsa_modulus(&self) -> Cow<'_, [u8]> {
1432        use rsa::traits::PublicKeyParts as _;
1433        Cow::Owned(self.n().to_be_bytes_trimmed_vartime().into_vec())
1434    }
1435
1436    fn rsa_exponent(&self) -> Cow<'_, [u8]> {
1437        use rsa::traits::PublicKeyParts as _;
1438        Cow::Owned(self.e().to_be_bytes_trimmed_vartime().into_vec())
1439    }
1440
1441    fn transport_with_provider(
1442        &self,
1443        provider: &dyn CryptoProvider,
1444        parameters: &RsaOaepParameters,
1445        plaintext: &[u8],
1446    ) -> Result<Vec<u8>, ProviderError> {
1447        rustcrypto::transport_key(provider, self, parameters, plaintext)
1448    }
1449}
1450
1451/// Opaque RSA private-key handle for the built-in RustCrypto provider.
1452#[cfg(feature = "xmlenc")]
1453#[derive(Clone)]
1454pub struct RustCryptoRsaPrivateKey {
1455    key: rsa::RsaPrivateKey,
1456    ciphertext_len: usize,
1457}
1458
1459#[cfg(feature = "xmlenc")]
1460impl RustCryptoRsaPrivateKey {
1461    /// Wrap an already parsed RustCrypto RSA private key.
1462    #[must_use]
1463    pub fn new(key: rsa::RsaPrivateKey) -> Self {
1464        use rsa::traits::PublicKeyParts as _;
1465        let ciphertext_len = key.size();
1466        Self {
1467            key,
1468            ciphertext_len,
1469        }
1470    }
1471}
1472
1473#[cfg(feature = "xmlenc")]
1474impl From<rsa::RsaPrivateKey> for RustCryptoRsaPrivateKey {
1475    fn from(key: rsa::RsaPrivateKey) -> Self {
1476        Self::new(key)
1477    }
1478}
1479
1480#[cfg(feature = "xmlenc")]
1481impl KeyRecoveryKey for RustCryptoRsaPrivateKey {
1482    #[cfg(feature = "legacy-algorithms")]
1483    fn recover_pkcs1v15(
1484        &self,
1485        provider: &dyn CryptoProvider,
1486        ciphertext: &[u8],
1487        key_len: usize,
1488    ) -> Result<RecoveredContentKey, ProviderError> {
1489        rsa_pkcs1v15::recover(provider, &self.key, ciphertext, key_len)
1490    }
1491    fn rsa_modulus_bits(&self) -> usize {
1492        KeyRecoveryKey::rsa_modulus_bits(&self.key)
1493    }
1494    fn rsa_public_exponent(&self) -> Option<u64> {
1495        KeyRecoveryKey::rsa_public_exponent(&self.key)
1496    }
1497    fn ciphertext_len(&self) -> usize {
1498        self.ciphertext_len
1499    }
1500
1501    fn recover_with_provider(
1502        &self,
1503        provider: &dyn CryptoProvider,
1504        parameters: &RsaOaepParameters,
1505        ciphertext: &[u8],
1506    ) -> Result<Vec<u8>, ProviderError> {
1507        rustcrypto::recover_key(provider, &self.key, parameters, ciphertext)
1508    }
1509}
1510
1511#[cfg(feature = "xmlenc")]
1512impl KeyRecoveryKey for rsa::RsaPrivateKey {
1513    #[cfg(feature = "legacy-algorithms")]
1514    fn recover_pkcs1v15(
1515        &self,
1516        provider: &dyn CryptoProvider,
1517        ciphertext: &[u8],
1518        key_len: usize,
1519    ) -> Result<RecoveredContentKey, ProviderError> {
1520        rsa_pkcs1v15::recover(provider, self, ciphertext, key_len)
1521    }
1522    fn rsa_modulus_bits(&self) -> usize {
1523        use rsa::traits::PublicKeyParts as _;
1524        self.n().bits_vartime() as usize
1525    }
1526    fn rsa_public_exponent(&self) -> Option<u64> {
1527        use rsa::traits::PublicKeyParts as _;
1528        if self.e().bits_vartime() > 64 {
1529            return None;
1530        }
1531        let mut exponent = 0_u64;
1532        let word_bits = crypto_bigint::Word::BITS as usize;
1533        for (index, word) in self.e().as_words().iter().take(64 / word_bits).enumerate() {
1534            #[cfg(target_pointer_width = "64")]
1535            let word = *word;
1536            #[cfg(target_pointer_width = "32")]
1537            let word = u64::from(*word);
1538            exponent |= word << (index * word_bits);
1539        }
1540        Some(exponent)
1541    }
1542    fn ciphertext_len(&self) -> usize {
1543        use rsa::traits::PublicKeyParts as _;
1544        self.size()
1545    }
1546
1547    fn recover_with_provider(
1548        &self,
1549        provider: &dyn CryptoProvider,
1550        parameters: &RsaOaepParameters,
1551        ciphertext: &[u8],
1552    ) -> Result<Vec<u8>, ProviderError> {
1553        rustcrypto::recover_key(provider, self, parameters, ciphertext)
1554    }
1555}
1556
1557/// Process-wide immutable default provider. It contains no mutable state or keys.
1558pub static RUST_CRYPTO_PROVIDER: RustCryptoProvider = RustCryptoProvider;
1559
1560/// Borrow the pure-Rust default provider.
1561#[must_use]
1562pub fn default_provider() -> &'static dyn CryptoProvider {
1563    &RUST_CRYPTO_PROVIDER
1564}
1565
1566/// Adapter used when a RustCrypto primitive requires a fallible RNG object.
1567#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1568pub(crate) struct ProviderRng<'a>(pub(crate) &'a dyn CryptoProvider);
1569
1570#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1571impl TryRng for ProviderRng<'_> {
1572    type Error = ProviderError;
1573
1574    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1575        let mut bytes = [0_u8; 4];
1576        self.try_fill_bytes(&mut bytes)?;
1577        Ok(u32::from_le_bytes(bytes))
1578    }
1579
1580    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1581        let mut bytes = [0_u8; 8];
1582        self.try_fill_bytes(&mut bytes)?;
1583        Ok(u64::from_le_bytes(bytes))
1584    }
1585
1586    fn try_fill_bytes(&mut self, output: &mut [u8]) -> Result<(), Self::Error> {
1587        self.0.fill_random(output)
1588    }
1589}
1590
1591#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1592impl TryCryptoRng for ProviderRng<'_> {}
1593
1594#[cfg(feature = "xmldsig")]
1595impl RustCryptoProvider {
1596    fn verify_context_leaf(
1597        key: &dyn crate::xmldsig::VerifyingKey,
1598        algorithm: crate::xmldsig::SignatureAlgorithm,
1599        context: &crate::xmldsig::SignatureContext,
1600        data: &[u8],
1601        signature: &[u8],
1602    ) -> Result<bool, crate::xmldsig::DsigError> {
1603        if key.provider_binding().is_some() {
1604            return Err(ProviderError::External(ExternalProviderError::Binding).into());
1605        }
1606        key.verify_with_context(algorithm, context, data, signature)
1607    }
1608}
1609
1610impl CryptoProvider for RustCryptoProvider {
1611    #[cfg(feature = "xmldsig")]
1612    fn verify_with_key_receipt(
1613        &self,
1614        key: &dyn crate::xmldsig::VerifyingKey,
1615        input: SignatureVerificationInput<'_>,
1616        observer: &mut dyn FnMut(KeyVerificationEvent<'_>) -> Result<(), crate::xmldsig::DsigError>,
1617    ) -> Result<bool, crate::xmldsig::DsigError> {
1618        self.require_capability(ProviderCapability::Verify(input.algorithm))?;
1619        key_association::observed_leaf(key, observer, &|key| {
1620            Self::verify_context_leaf(
1621                key,
1622                input.algorithm,
1623                input.context,
1624                input.data,
1625                input.signature,
1626            )
1627        })
1628    }
1629
1630    #[cfg(feature = "xmldsig")]
1631    fn associate_verification_key(
1632        &self,
1633        actual: &[u8],
1634        expected: structured_pkix::path::WorkingKey<'_>,
1635        charge: &dyn Fn(u64) -> Result<(), structured_asn1::memory::MemoryError>,
1636    ) -> Result<KeyAssociation, crate::xmldsig::DsigError> {
1637        key_association::compare_rustcrypto(actual, expected, charge)
1638    }
1639    #[cfg(feature = "xmldsig")]
1640    fn encapsulate_key(
1641        &self,
1642        key: &dyn KeyEncapsulationKey,
1643    ) -> Result<EncapsulatedKey, ProviderError> {
1644        self.require_capability(ProviderCapability::Encapsulate(key.algorithm()))?;
1645        if key.provider_binding().is_some() {
1646            return Err(ProviderError::External(ExternalProviderError::Binding));
1647        }
1648        key.encapsulate_with_provider(self)
1649    }
1650
1651    #[cfg(feature = "xmldsig")]
1652    fn decapsulate_key(
1653        &self,
1654        key: &dyn KeyDecapsulationKey,
1655        ciphertext: &[u8],
1656    ) -> Result<zeroize::Zeroizing<[u8; 32]>, ProviderError> {
1657        self.require_capability(ProviderCapability::Decapsulate(key.algorithm()))?;
1658        if key.provider_binding().is_some() {
1659            return Err(ProviderError::External(ExternalProviderError::Binding));
1660        }
1661        if ciphertext.len() != key.algorithm().ciphertext_len() {
1662            return Err(ProviderError::InvalidInput(
1663                ProviderInputError::MlKemCiphertext,
1664            ));
1665        }
1666        key.decapsulate(ciphertext)
1667    }
1668    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1669    fn transport_pkcs1v15(
1670        &self,
1671        key: &dyn KeyTransportKey,
1672        plaintext: &[u8],
1673    ) -> Result<Vec<u8>, ProviderError> {
1674        self.require_capability(ProviderCapability::Pkcs1v15Transport)?;
1675        key.transport_pkcs1v15(self, plaintext)
1676    }
1677
1678    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1679    fn recover_pkcs1v15(
1680        &self,
1681        key: &dyn KeyRecoveryKey,
1682        ciphertext: &[u8],
1683        key_len: usize,
1684    ) -> Result<RecoveredContentKey, ProviderError> {
1685        self.require_capability(ProviderCapability::Pkcs1v15Recovery)?;
1686        key.recover_pkcs1v15(self, ciphertext, key_len)
1687    }
1688    fn name(&self) -> &'static str {
1689        "rustcrypto"
1690    }
1691
1692    #[cfg(feature = "experimental-pq")]
1693    fn import_encapsulation_key(
1694        &self,
1695        algorithm: KeyEncapsulationAlgorithm,
1696        spki: &[u8],
1697    ) -> Result<std::sync::Arc<dyn KeyEncapsulationKey>, ProviderError> {
1698        self.require_capability(ProviderCapability::Encapsulate(algorithm))?;
1699        let key = RustCryptoMlKemPublicKey::from_spki_der_with_algorithm(spki, Some(algorithm))?;
1700        Ok(std::sync::Arc::new(key))
1701    }
1702
1703    #[cfg(feature = "experimental-pq")]
1704    fn import_decapsulation_key(
1705        &self,
1706        algorithm: KeyEncapsulationAlgorithm,
1707        pkcs8: &[u8],
1708    ) -> Result<std::sync::Arc<dyn KeyDecapsulationKey>, ProviderError> {
1709        self.require_capability(ProviderCapability::Decapsulate(algorithm))?;
1710        let key = RustCryptoMlKemPrivateKey::from_pkcs8_der_with_algorithm(pkcs8, Some(algorithm))?;
1711        Ok(std::sync::Arc::new(key))
1712    }
1713
1714    #[cfg(feature = "xmlenc")]
1715    fn import_recovery_key(
1716        &self,
1717        pkcs8: &[u8],
1718    ) -> Result<std::sync::Arc<dyn KeyRecoveryKey>, ProviderError> {
1719        use crate::rsa_encoding::RsaPrivateKeyEncoding as _;
1720        let key = rsa::RsaPrivateKey::from_pkcs8_der(pkcs8).map_err(|_| {
1721            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
1722                "RSA recovery key",
1723            ))
1724        })?;
1725        Ok(std::sync::Arc::new(RustCryptoRsaPrivateKey::new(key)))
1726    }
1727
1728    #[cfg(feature = "xmldsig")]
1729    fn import_signing_key(
1730        &self,
1731        algorithm: crate::xmldsig::SignatureAlgorithm,
1732        der: &[u8],
1733    ) -> Result<Box<dyn crate::xmldsig::SigningKey>, crate::xmldsig::SigningKeyError> {
1734        use crate::xmldsig::{SignatureAlgorithm as A, *};
1735        Ok(match algorithm {
1736            #[cfg(feature = "experimental-pq")]
1737            A::PostQuantum(parameter) => {
1738                Box::new(PostQuantumSigningKey::from_pkcs8_der(parameter, der)?)
1739            }
1740            A::Ed25519 | A::Ed25519Ctx | A::Ed25519Ph | A::Ed448 | A::Ed448Ph => {
1741                Box::new(EdDsaSigningKey::from_pkcs8_der(algorithm, der)?)
1742            }
1743            method if method.is_rsa() => Box::new(RsaSigningKey::from_pkcs8_der(der)?),
1744            A::DsaSha1 | A::DsaSha256 => Box::new(DsaSigningKey::from_pkcs8_der(der)?),
1745            method if method.ecdsa_digest().is_some() => {
1746                if let Ok(key) = EcdsaP256SigningKey::from_pkcs8_der(der) {
1747                    Box::new(key)
1748                } else if let Ok(key) = EcdsaP384SigningKey::from_pkcs8_der(der) {
1749                    Box::new(key)
1750                } else {
1751                    Box::new(EcdsaP521SigningKey::from_pkcs8_der(der)?)
1752                }
1753            }
1754            _ => {
1755                return Err(SigningKeyError::UnsupportedAlgorithm {
1756                    uri: algorithm.uri().into(),
1757                });
1758            }
1759        })
1760    }
1761
1762    fn supports(&self, capability: ProviderCapability<'_>) -> bool {
1763        match capability {
1764            #[cfg(feature = "xmldsig")]
1765            ProviderCapability::Encapsulate(_) | ProviderCapability::Decapsulate(_) => {
1766                cfg!(feature = "experimental-pq")
1767            }
1768            #[cfg(feature = "xmldsig")]
1769            ProviderCapability::Digest(_) => true,
1770            #[cfg(feature = "xmldsig")]
1771            // Opaque keys own these primitives and reject unsupported methods
1772            // during dispatch; the provider advertises its dispatch surface.
1773            ProviderCapability::Sign(algorithm) | ProviderCapability::Verify(algorithm) => {
1774                if let Some(parameters) = algorithm.rsa_pss_parameters()
1775                    && parameters.salt_len > i32::MAX as usize
1776                {
1777                    return false;
1778                }
1779                !matches!(
1780                    algorithm,
1781                    crate::xmldsig::SignatureAlgorithm::PostQuantum(_)
1782                ) || cfg!(feature = "experimental-pq")
1783            }
1784            #[cfg(feature = "xmldsig")]
1785            ProviderCapability::VerifyCertificate(algorithm) => {
1786                is_supported_x509_signature(algorithm)
1787            }
1788            #[cfg(feature = "xmlenc")]
1789            ProviderCapability::Encrypt(_) | ProviderCapability::Decrypt(_) => true,
1790            #[cfg(feature = "xmlenc")]
1791            ProviderCapability::KeyWrap(algorithm) | ProviderCapability::KeyUnwrap(algorithm) => {
1792                algorithm.wrapped_len(16).is_some()
1793            }
1794            #[cfg(feature = "xmlenc")]
1795            ProviderCapability::KeyTransport(parameters)
1796            | ProviderCapability::KeyRecovery(parameters) => {
1797                !parameters.algorithm.requires_explicit_permission()
1798                    && parameters.mgf_digest.mgf_uri().is_some()
1799            }
1800            #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1801            ProviderCapability::Pkcs1v15Transport | ProviderCapability::Pkcs1v15Recovery => true,
1802            ProviderCapability::Random => true,
1803            ProviderCapability::KeyAgreement(parameters) => {
1804                #[cfg(feature = "xmlenc")]
1805                {
1806                    matches!(
1807                        parameters.algorithm,
1808                        key_establishment::X25519_URI
1809                            | key_establishment::X448_URI
1810                            | key_establishment::ECDH_URI
1811                            | key_establishment::DH_ES_URI
1812                            | key_establishment::DH_URI
1813                    )
1814                }
1815                #[cfg(not(feature = "xmlenc"))]
1816                {
1817                    let _ = parameters;
1818                    false
1819                }
1820            }
1821            ProviderCapability::Kdf(parameters) => {
1822                #[cfg(feature = "xmlenc")]
1823                {
1824                    key_establishment::supports_kdf(parameters)
1825                }
1826                #[cfg(not(feature = "xmlenc"))]
1827                {
1828                    let _ = parameters;
1829                    false
1830                }
1831            }
1832        }
1833    }
1834
1835    fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError> {
1836        SysRng
1837            .try_fill_bytes(output)
1838            .map_err(|error| ProviderError::Random(error.to_string()))
1839    }
1840
1841    fn derive_key(
1842        &self,
1843        parameters: &KdfParameters<'_>,
1844        secret: &[u8],
1845    ) -> Result<Vec<u8>, ProviderError> {
1846        self.require_capability(ProviderCapability::Kdf(parameters))?;
1847        #[cfg(feature = "xmlenc")]
1848        {
1849            key_establishment::derive_key(parameters, secret)
1850        }
1851        #[cfg(not(feature = "xmlenc"))]
1852        {
1853            let _ = secret;
1854            Err(ProviderError::Unsupported {
1855                operation: ProviderOperation::Kdf,
1856                algorithm: Some(parameters.algorithm.to_owned()),
1857            })
1858        }
1859    }
1860
1861    #[cfg(feature = "xmldsig")]
1862    fn begin_digest(
1863        &self,
1864        algorithm: DigestAlgorithm,
1865    ) -> Result<Box<dyn DigestState + '_>, ProviderError> {
1866        Ok(Box::new(RustCryptoDigest::new(algorithm)))
1867    }
1868
1869    #[cfg(feature = "xmldsig")]
1870    fn sign(
1871        &self,
1872        key: &dyn crate::xmldsig::SigningKey,
1873        algorithm: crate::xmldsig::SignatureAlgorithm,
1874        data: &[u8],
1875    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1876        self.require_capability(ProviderCapability::Sign(algorithm))?;
1877        key.sign_with_provider(self, algorithm, data)
1878    }
1879
1880    #[cfg(feature = "xmldsig")]
1881    fn sign_with_context(
1882        &self,
1883        key: &dyn crate::xmldsig::SigningKey,
1884        algorithm: crate::xmldsig::SignatureAlgorithm,
1885        context: &crate::xmldsig::SignatureContext,
1886        data: &[u8],
1887    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1888        self.require_capability(ProviderCapability::Sign(algorithm))?;
1889        key.sign_with_provider_context(self, algorithm, context, data)
1890    }
1891
1892    #[cfg(feature = "xmldsig")]
1893    fn verify_with_context(
1894        &self,
1895        key: &dyn crate::xmldsig::VerifyingKey,
1896        algorithm: crate::xmldsig::SignatureAlgorithm,
1897        context: &crate::xmldsig::SignatureContext,
1898        data: &[u8],
1899        signature: &[u8],
1900    ) -> Result<bool, crate::xmldsig::DsigError> {
1901        self.require_capability(ProviderCapability::Verify(algorithm))?;
1902        key_association::observed_leaf(key, &mut |_| Ok(()), &|key| {
1903            Self::verify_context_leaf(key, algorithm, context, data, signature)
1904        })
1905    }
1906
1907    #[cfg(feature = "xmldsig")]
1908    fn verify(
1909        &self,
1910        key: &dyn crate::xmldsig::VerifyingKey,
1911        algorithm: crate::xmldsig::SignatureAlgorithm,
1912        data: &[u8],
1913        signature: &[u8],
1914    ) -> Result<bool, crate::xmldsig::DsigError> {
1915        self.require_capability(ProviderCapability::Verify(algorithm))?;
1916        key_association::observed_leaf(key, &mut |_| Ok(()), &|key| {
1917            if key.provider_binding().is_some() {
1918                return Err(ProviderError::External(ExternalProviderError::Binding).into());
1919            }
1920            key.verify(algorithm, data, signature)
1921        })
1922    }
1923
1924    #[cfg(feature = "xmldsig")]
1925    fn verify_x509_signature(
1926        &self,
1927        algorithm: X509SignatureAlgorithm,
1928        signed_data: &[u8],
1929        signature: &[u8],
1930        issuer_spki_der: &[u8],
1931    ) -> Result<bool, ProviderError> {
1932        rustcrypto_x509::verify_signature(algorithm, signed_data, signature, issuer_spki_der)
1933    }
1934
1935    #[cfg(feature = "xmlenc")]
1936    fn encrypt_data(
1937        &self,
1938        algorithm: DataEncryptionAlgorithm,
1939        key: &[u8],
1940        plaintext: &[u8],
1941    ) -> Result<Vec<u8>, ProviderError> {
1942        rustcrypto::encrypt_data(self, algorithm, key, plaintext)
1943    }
1944
1945    #[cfg(feature = "xmlenc")]
1946    fn decrypt_data(
1947        &self,
1948        algorithm: DataEncryptionAlgorithm,
1949        key: &[u8],
1950        ciphertext: &[u8],
1951    ) -> Result<Vec<u8>, ProviderError> {
1952        rustcrypto::decrypt_data(algorithm, key, ciphertext)
1953    }
1954
1955    #[cfg(feature = "xmlenc")]
1956    fn wrap_key(
1957        &self,
1958        algorithm: KeyWrapAlgorithm,
1959        kek: &[u8],
1960        key: &[u8],
1961    ) -> Result<Vec<u8>, ProviderError> {
1962        rustcrypto::wrap_key(self, algorithm, kek, key)
1963    }
1964
1965    #[cfg(feature = "xmlenc")]
1966    fn encrypt_data_with_parameters(
1967        &self,
1968        algorithm: DataEncryptionAlgorithm,
1969        key: &[u8],
1970        plaintext: &[u8],
1971        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1972    ) -> Result<Vec<u8>, ProviderError> {
1973        match parameters {
1974            Some(parameters) => chacha::encrypt(algorithm, key, plaintext, parameters),
1975            None => self.encrypt_data(algorithm, key, plaintext),
1976        }
1977    }
1978
1979    #[cfg(feature = "xmlenc")]
1980    fn decrypt_data_with_parameters(
1981        &self,
1982        algorithm: DataEncryptionAlgorithm,
1983        key: &[u8],
1984        ciphertext: &[u8],
1985        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1986    ) -> Result<Vec<u8>, ProviderError> {
1987        match parameters {
1988            Some(parameters) => chacha::decrypt(algorithm, key, ciphertext, parameters),
1989            None => self.decrypt_data(algorithm, key, ciphertext),
1990        }
1991    }
1992
1993    #[cfg(feature = "xmlenc")]
1994    fn unwrap_key(
1995        &self,
1996        algorithm: KeyWrapAlgorithm,
1997        kek: &[u8],
1998        wrapped: &[u8],
1999    ) -> Result<Vec<u8>, ProviderError> {
2000        rustcrypto::unwrap_key(algorithm, kek, wrapped)
2001    }
2002
2003    #[cfg(feature = "xmlenc")]
2004    fn transport_key(
2005        &self,
2006        key: &dyn KeyTransportKey,
2007        parameters: &RsaOaepParameters,
2008        plaintext: &[u8],
2009    ) -> Result<Vec<u8>, ProviderError> {
2010        self.require_capability(ProviderCapability::KeyTransport(parameters))?;
2011        key.transport_with_provider(self, parameters, plaintext)
2012    }
2013
2014    #[cfg(feature = "xmlenc")]
2015    fn recover_key(
2016        &self,
2017        key: &dyn KeyRecoveryKey,
2018        parameters: &RsaOaepParameters,
2019        ciphertext: &[u8],
2020    ) -> Result<Vec<u8>, ProviderError> {
2021        self.require_capability(ProviderCapability::KeyRecovery(parameters))?;
2022        key.recover_with_provider(self, parameters, ciphertext)
2023    }
2024}
2025
2026#[cfg(feature = "xmldsig")]
2027fn is_supported_x509_signature(algorithm: X509SignatureAlgorithm) -> bool {
2028    match algorithm {
2029        X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1)
2030        | X509SignatureAlgorithm::Ed25519
2031        | X509SignatureAlgorithm::Ed448 => true,
2032        X509SignatureAlgorithm::Ecdsa(digest) => rustcrypto_x509::ecdsa_algorithm(digest).is_some(),
2033        X509SignatureAlgorithm::PostQuantum(_) => cfg!(feature = "experimental-pq"),
2034        X509SignatureAlgorithm::RsaPkcs1v15(digest) => matches!(
2035            digest,
2036            DigestAlgorithm::Sha1
2037                | DigestAlgorithm::Sha224
2038                | DigestAlgorithm::Sha256
2039                | DigestAlgorithm::Sha384
2040                | DigestAlgorithm::Sha512
2041        ),
2042        X509SignatureAlgorithm::RsaPss {
2043            digest, mgf_digest, ..
2044        } => {
2045            matches!(
2046                digest,
2047                DigestAlgorithm::Sha256 | DigestAlgorithm::Sha384 | DigestAlgorithm::Sha512
2048            ) && digest == mgf_digest
2049        }
2050        X509SignatureAlgorithm::Dsa(_) => false,
2051    }
2052}
2053
2054#[cfg(feature = "xmldsig")]
2055pub(crate) mod rustcrypto_x509 {
2056    use crate::rsa_encoding::RsaPublicKeyEncoding as _;
2057    use der::Decode as _;
2058    use dsa::pkcs8::DecodePublicKey as _;
2059    use rsa::{
2060        RsaPublicKey,
2061        pss::{Signature as RsaPssSignature, VerifyingKey as RsaPssVerifyingKey},
2062        traits::PublicKeyParts as _,
2063    };
2064    use sha1::Digest as _;
2065    use sha2::{Sha256, Sha384, Sha512};
2066    use signature::{Verifier as _, hazmat::PrehashVerifier as _};
2067    use x509_parser::prelude::FromDer as _;
2068
2069    use super::{ProviderError, X509SignatureAlgorithm};
2070    use crate::xmldsig::signature::verify_ecdsa_signature_spki_asn1_der;
2071    use crate::xmldsig::{
2072        DigestAlgorithm, DsigError, SignatureAlgorithm, VerificationKey, VerifyingKey as _,
2073    };
2074
2075    pub(super) fn verify_signature(
2076        algorithm: X509SignatureAlgorithm,
2077        signed_data: &[u8],
2078        signature: &[u8],
2079        issuer_spki_der: &[u8],
2080    ) -> Result<bool, ProviderError> {
2081        match algorithm {
2082            X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1) => {
2083                // Certificate signatures are ASN.1 DER integers sized by the
2084                // issuer's q parameter. XMLDSig's fixed 20-byte r||s framing
2085                // applies only to SignatureValue, never to X.509 signatures.
2086                let Ok(key) = crate::xmldsig::signature::decode_dsa_verifying_key(issuer_spki_der)
2087                else {
2088                    return Ok(false);
2089                };
2090                let Ok(signature) = dsa::Signature::from_der(signature) else {
2091                    return Ok(false);
2092                };
2093                let digest = sha1::Sha1::digest(signed_data);
2094                Ok(key.verify_prehash(&digest, &signature).is_ok())
2095            }
2096            X509SignatureAlgorithm::RsaPkcs1v15(digest) => {
2097                let Some(algorithm) = rsa_pkcs1_algorithm(digest) else {
2098                    return unsupported(X509SignatureAlgorithm::RsaPkcs1v15(digest));
2099                };
2100                verify_xml_signature(algorithm, signed_data, signature, issuer_spki_der)
2101            }
2102            X509SignatureAlgorithm::Ecdsa(digest) => {
2103                let Some(algorithm) = ecdsa_algorithm(digest) else {
2104                    return unsupported(X509SignatureAlgorithm::Ecdsa(digest));
2105                };
2106                // RFC 5280 ECDSA certificate signatures are always ASN.1 DER;
2107                // XMLDSig's SignatureValue framing policy is irrelevant here.
2108                match verify_ecdsa_signature_spki_asn1_der(
2109                    algorithm,
2110                    issuer_spki_der,
2111                    signed_data,
2112                    signature,
2113                ) {
2114                    Ok(verified) => Ok(verified),
2115                    Err(_) => Ok(false),
2116                }
2117            }
2118            X509SignatureAlgorithm::RsaPss {
2119                digest,
2120                mgf_digest,
2121                salt_len,
2122            } => {
2123                // RFC 4055 key restrictions are part of signature validity. Check
2124                // them before provider capability so an incompatible key is a
2125                // deterministic non-match even when the requested MGF is unsupported.
2126                let Some(key) = compatible_rsa_pss_public_key_from_spki(issuer_spki_der, algorithm)
2127                else {
2128                    return Ok(false);
2129                };
2130                if digest != mgf_digest {
2131                    return unsupported(algorithm);
2132                }
2133                verify_rsa_pss(digest, salt_len, signed_data, signature, key)
2134            }
2135            X509SignatureAlgorithm::Ed25519 => {
2136                let Ok(key) = ed25519_dalek::VerifyingKey::from_public_key_der(issuer_spki_der)
2137                else {
2138                    return Ok(false);
2139                };
2140                let Ok(signature) = ed25519_dalek::Signature::try_from(signature) else {
2141                    return Ok(false);
2142                };
2143                Ok(key.verify_strict(signed_data, &signature).is_ok())
2144            }
2145            X509SignatureAlgorithm::Ed448 => {
2146                // RFC 8410 sections 3 and 6 select pure Ed448 without context
2147                // or external prehash: https://www.rfc-editor.org/rfc/rfc8410.html#section-6
2148                let Ok(key) = ed448_goldilocks::VerifyingKey::from_public_key_der(issuer_spki_der)
2149                else {
2150                    return Ok(false);
2151                };
2152                let Ok(signature) = ed448_goldilocks::Signature::try_from(signature) else {
2153                    return Ok(false);
2154                };
2155                Ok(key.verify_raw(&signature, signed_data).is_ok())
2156            }
2157            #[cfg(feature = "experimental-pq")]
2158            X509SignatureAlgorithm::PostQuantum(algorithm) => {
2159                // RFC 9881 ยง3 and RFC 9909 ยงยง1, 4 require pure signatures
2160                // over the DER object with an empty context, not an XML context.
2161                // https://www.rfc-editor.org/rfc/rfc9881.html#section-3
2162                // https://www.rfc-editor.org/rfc/rfc9909.html#section-4
2163                Ok(crate::xmldsig::post_quantum::verify(
2164                    algorithm,
2165                    issuer_spki_der,
2166                    &crate::xmldsig::SignatureContext::default(),
2167                    signed_data,
2168                    signature,
2169                )
2170                .unwrap_or(false))
2171            }
2172            _ => unsupported(algorithm),
2173        }
2174    }
2175
2176    fn verify_xml_signature(
2177        algorithm: SignatureAlgorithm,
2178        signed_data: &[u8],
2179        signature: &[u8],
2180        issuer_spki_der: &[u8],
2181    ) -> Result<bool, ProviderError> {
2182        let key = VerificationKey {
2183            algorithm,
2184            public_key_bytes: issuer_spki_der.to_vec(),
2185            certificate_der: None,
2186            name: None,
2187        };
2188        match key.verify(algorithm, signed_data, signature) {
2189            Ok(verified) => Ok(verified),
2190            Err(DsigError::Provider(error)) => Err(error),
2191            Err(_) => Ok(false),
2192        }
2193    }
2194
2195    fn verify_rsa_pss(
2196        digest: DigestAlgorithm,
2197        salt_len: usize,
2198        signed_data: &[u8],
2199        signature: &[u8],
2200        key: RsaPublicKey,
2201    ) -> Result<bool, ProviderError> {
2202        if !rsa_pss_salt_fits_key(&key, digest, salt_len) {
2203            return Ok(false);
2204        }
2205        let Ok(signature) = RsaPssSignature::try_from(signature) else {
2206            return Ok(false);
2207        };
2208        let verified = match digest {
2209            DigestAlgorithm::Sha256 => {
2210                RsaPssVerifyingKey::<Sha256>::new_with_salt_len(key, salt_len)
2211                    .verify(signed_data, &signature)
2212            }
2213            DigestAlgorithm::Sha384 => {
2214                RsaPssVerifyingKey::<Sha384>::new_with_salt_len(key, salt_len)
2215                    .verify(signed_data, &signature)
2216            }
2217            DigestAlgorithm::Sha512 => {
2218                RsaPssVerifyingKey::<Sha512>::new_with_salt_len(key, salt_len)
2219                    .verify(signed_data, &signature)
2220            }
2221            _ => {
2222                return unsupported(X509SignatureAlgorithm::RsaPss {
2223                    digest,
2224                    mgf_digest: digest,
2225                    salt_len,
2226                });
2227            }
2228        };
2229        Ok(verified.is_ok())
2230    }
2231
2232    pub(super) fn rsa_pss_salt_fits_key(
2233        key: &RsaPublicKey,
2234        digest: DigestAlgorithm,
2235        salt_len: usize,
2236    ) -> bool {
2237        let Some(em_bits) = key.n().bits().checked_sub(1) else {
2238            return false;
2239        };
2240        let Ok(em_len) = usize::try_from(em_bits.div_ceil(8)) else {
2241            return false;
2242        };
2243        digest
2244            .output_len()
2245            .checked_add(salt_len)
2246            .and_then(|length| length.checked_add(2))
2247            .is_some_and(|required| required <= em_len)
2248    }
2249
2250    pub(crate) fn compatible_rsa_pss_public_key_from_spki(
2251        spki_der: &[u8],
2252        signature_algorithm: X509SignatureAlgorithm,
2253    ) -> Option<RsaPublicKey> {
2254        let (rest, spki) = x509_parser::x509::SubjectPublicKeyInfo::from_der(spki_der).ok()?;
2255        if !rest.is_empty() {
2256            return None;
2257        }
2258        match spki.algorithm.algorithm.to_id_string().as_str() {
2259            "1.2.840.113549.1.1.1" => RsaPublicKey::from_public_key_der(spki_der).ok(),
2260            "1.2.840.113549.1.1.10" => {
2261                // RFC 4055 section 3.3 applies key restrictions only when
2262                // RSASSA-PSS-params is present in SubjectPublicKeyInfo.
2263                if spki
2264                    .algorithm
2265                    .parameters
2266                    .as_ref()
2267                    .is_some_and(|parameters| {
2268                        !rsa_pss_key_parameters_allow(parameters, signature_algorithm)
2269                    })
2270                {
2271                    return None;
2272                }
2273                RsaPublicKey::from_pkcs1_der(&spki.subject_public_key.data).ok()
2274            }
2275            _ => None,
2276        }
2277    }
2278
2279    pub(crate) fn rsa_pss_key_parameters_allow(
2280        parameters: &x509_parser::asn1_rs::Any<'_>,
2281        signature_algorithm: X509SignatureAlgorithm,
2282    ) -> bool {
2283        let X509SignatureAlgorithm::RsaPss {
2284            digest,
2285            mgf_digest,
2286            salt_len,
2287        } = signature_algorithm
2288        else {
2289            return false;
2290        };
2291        let Ok(parameters) =
2292            x509_parser::signature_algorithm::RsaSsaPssParams::try_from(parameters)
2293        else {
2294            return false;
2295        };
2296        let Ok(mask) = parameters.mask_gen_algorithm() else {
2297            return false;
2298        };
2299        parameters.trailer_field() == 1
2300            && x509_digest_from_oid(&parameters.hash_algorithm_oid().to_id_string()) == Some(digest)
2301            && mask.mgf.to_id_string() == "1.2.840.113549.1.1.8"
2302            && x509_digest_from_oid(&mask.hash.to_id_string()) == Some(mgf_digest)
2303            && usize::try_from(parameters.salt_length()).is_ok_and(|minimum| salt_len >= minimum)
2304    }
2305
2306    fn x509_digest_from_oid(oid: &str) -> Option<DigestAlgorithm> {
2307        match oid {
2308            "1.3.14.3.2.26" => Some(DigestAlgorithm::Sha1),
2309            "2.16.840.1.101.3.4.2.4" => Some(DigestAlgorithm::Sha224),
2310            "2.16.840.1.101.3.4.2.1" => Some(DigestAlgorithm::Sha256),
2311            "2.16.840.1.101.3.4.2.2" => Some(DigestAlgorithm::Sha384),
2312            "2.16.840.1.101.3.4.2.3" => Some(DigestAlgorithm::Sha512),
2313            "2.16.840.1.101.3.4.2.7" => Some(DigestAlgorithm::Sha3_224),
2314            "2.16.840.1.101.3.4.2.8" => Some(DigestAlgorithm::Sha3_256),
2315            "2.16.840.1.101.3.4.2.9" => Some(DigestAlgorithm::Sha3_384),
2316            "2.16.840.1.101.3.4.2.10" => Some(DigestAlgorithm::Sha3_512),
2317            _ => None,
2318        }
2319    }
2320
2321    const fn rsa_pkcs1_algorithm(digest: DigestAlgorithm) -> Option<SignatureAlgorithm> {
2322        match digest {
2323            DigestAlgorithm::Sha1 => Some(SignatureAlgorithm::RsaSha1),
2324            DigestAlgorithm::Sha224 => Some(SignatureAlgorithm::RsaSha224),
2325            DigestAlgorithm::Sha256 => Some(SignatureAlgorithm::RsaSha256),
2326            DigestAlgorithm::Sha384 => Some(SignatureAlgorithm::RsaSha384),
2327            DigestAlgorithm::Sha512 => Some(SignatureAlgorithm::RsaSha512),
2328            _ => None,
2329        }
2330    }
2331
2332    pub(super) const fn ecdsa_algorithm(digest: DigestAlgorithm) -> Option<SignatureAlgorithm> {
2333        match digest {
2334            #[cfg(feature = "legacy-algorithms")]
2335            DigestAlgorithm::Md5 | DigestAlgorithm::Ripemd160 => None,
2336            DigestAlgorithm::Sha1 => Some(SignatureAlgorithm::EcdsaSha1),
2337            DigestAlgorithm::Sha224 => Some(SignatureAlgorithm::EcdsaSha224),
2338            DigestAlgorithm::Sha256 => Some(SignatureAlgorithm::EcdsaSha256),
2339            DigestAlgorithm::Sha384 => Some(SignatureAlgorithm::EcdsaSha384),
2340            DigestAlgorithm::Sha512 => Some(SignatureAlgorithm::EcdsaSha512),
2341            DigestAlgorithm::Sha3_224 => Some(SignatureAlgorithm::EcdsaSha3_224),
2342            DigestAlgorithm::Sha3_256 => Some(SignatureAlgorithm::EcdsaSha3_256),
2343            DigestAlgorithm::Sha3_384 => Some(SignatureAlgorithm::EcdsaSha3_384),
2344            DigestAlgorithm::Sha3_512 => Some(SignatureAlgorithm::EcdsaSha3_512),
2345        }
2346    }
2347
2348    fn unsupported<T>(algorithm: X509SignatureAlgorithm) -> Result<T, ProviderError> {
2349        Err(ProviderError::Unsupported {
2350            operation: super::ProviderOperation::VerifyCertificate,
2351            algorithm: algorithm.oid().map(str::to_owned),
2352        })
2353    }
2354}
2355
2356#[cfg(feature = "xmlenc")]
2357mod rustcrypto {
2358    use crate::rustcrypto_sha3 as sha3;
2359    #[cfg(feature = "legacy-algorithms")]
2360    use aes::Aes192;
2361    use aes::{
2362        Aes128, Aes256,
2363        cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit, block_padding::NoPadding},
2364    };
2365    use aes_gcm::{
2366        Aes128Gcm, Aes256Gcm, Nonce,
2367        aead::{AeadInOut, KeyInit},
2368    };
2369    use aes_kw::{KwAes128, KwAes256};
2370    use camellia::{Camellia128, Camellia192, Camellia256};
2371    use cbc::{Decryptor, Encryptor};
2372    #[cfg(feature = "legacy-algorithms")]
2373    use des::TdesEde3;
2374    use rsa::{Oaep, traits::PaddingScheme};
2375    use sha1::Sha1;
2376    use sha2::{Sha224, Sha256, Sha384, Sha512};
2377
2378    use super::{CryptoProvider, ProviderError, ProviderInputError};
2379    use crate::xmlenc::{
2380        DataEncryptionAlgorithm, KeyWrapAlgorithm, OaepDigestAlgorithm, RsaOaepParameters,
2381    };
2382
2383    pub(super) fn encrypt_data(
2384        provider: &dyn CryptoProvider,
2385        algorithm: DataEncryptionAlgorithm,
2386        key: &[u8],
2387        plaintext: &[u8],
2388    ) -> Result<Vec<u8>, ProviderError> {
2389        check_key(algorithm.key_len(), key)?;
2390        match algorithm {
2391            #[cfg(feature = "legacy-algorithms")]
2392            DataEncryptionAlgorithm::TripleDesCbc => {
2393                encrypt_cbc::<TdesEde3>(provider, key, plaintext)
2394            }
2395            #[cfg(feature = "legacy-algorithms")]
2396            DataEncryptionAlgorithm::Aes192Cbc => encrypt_cbc::<Aes192>(provider, key, plaintext),
2397            #[cfg(feature = "legacy-algorithms")]
2398            DataEncryptionAlgorithm::Aes192Gcm => encrypt_gcm::<
2399                aes_gcm::AesGcm<Aes192, aes_gcm::aead::consts::U12>,
2400            >(provider, key, plaintext),
2401            DataEncryptionAlgorithm::Aes128Cbc => encrypt_cbc::<Aes128>(provider, key, plaintext),
2402            DataEncryptionAlgorithm::Aes256Cbc => encrypt_cbc::<Aes256>(provider, key, plaintext),
2403            // RFC 9231 ยง2.6.2 uses the AES CBC framing and padding with
2404            // Camellia substituted as the block cipher.
2405            // https://www.rfc-editor.org/rfc/rfc9231.html#section-2.6.2
2406            DataEncryptionAlgorithm::Camellia128Cbc => {
2407                encrypt_cbc::<Camellia128>(provider, key, plaintext)
2408            }
2409            DataEncryptionAlgorithm::Camellia192Cbc => {
2410                encrypt_cbc::<Camellia192>(provider, key, plaintext)
2411            }
2412            DataEncryptionAlgorithm::Camellia256Cbc => {
2413                encrypt_cbc::<Camellia256>(provider, key, plaintext)
2414            }
2415            DataEncryptionAlgorithm::Aes128Gcm => {
2416                encrypt_gcm::<Aes128Gcm>(provider, key, plaintext)
2417            }
2418            DataEncryptionAlgorithm::Aes256Gcm => {
2419                encrypt_gcm::<Aes256Gcm>(provider, key, plaintext)
2420            }
2421            DataEncryptionAlgorithm::ChaCha20 | DataEncryptionAlgorithm::ChaCha20Poly1305 => {
2422                Err(ProviderError::InvalidInput(
2423                    ProviderInputError::PrimitiveInitialization("missing ChaCha parameters"),
2424                ))
2425            }
2426        }
2427    }
2428
2429    pub(super) fn decrypt_data(
2430        algorithm: DataEncryptionAlgorithm,
2431        key: &[u8],
2432        ciphertext: &[u8],
2433    ) -> Result<Vec<u8>, ProviderError> {
2434        check_key(algorithm.key_len(), key)?;
2435        match algorithm {
2436            #[cfg(feature = "legacy-algorithms")]
2437            DataEncryptionAlgorithm::TripleDesCbc => decrypt_cbc::<TdesEde3>(key, ciphertext),
2438            #[cfg(feature = "legacy-algorithms")]
2439            DataEncryptionAlgorithm::Aes192Cbc => decrypt_cbc::<Aes192>(key, ciphertext),
2440            #[cfg(feature = "legacy-algorithms")]
2441            DataEncryptionAlgorithm::Aes192Gcm => {
2442                decrypt_gcm::<aes_gcm::AesGcm<Aes192, aes_gcm::aead::consts::U12>>(key, ciphertext)
2443            }
2444            DataEncryptionAlgorithm::Aes128Cbc => decrypt_cbc::<Aes128>(key, ciphertext),
2445            DataEncryptionAlgorithm::Aes256Cbc => decrypt_cbc::<Aes256>(key, ciphertext),
2446            DataEncryptionAlgorithm::Camellia128Cbc => decrypt_cbc::<Camellia128>(key, ciphertext),
2447            DataEncryptionAlgorithm::Camellia192Cbc => decrypt_cbc::<Camellia192>(key, ciphertext),
2448            DataEncryptionAlgorithm::Camellia256Cbc => decrypt_cbc::<Camellia256>(key, ciphertext),
2449            DataEncryptionAlgorithm::Aes128Gcm => decrypt_gcm::<Aes128Gcm>(key, ciphertext),
2450            DataEncryptionAlgorithm::Aes256Gcm => decrypt_gcm::<Aes256Gcm>(key, ciphertext),
2451            DataEncryptionAlgorithm::ChaCha20 | DataEncryptionAlgorithm::ChaCha20Poly1305 => {
2452                Err(ProviderError::InvalidInput(
2453                    ProviderInputError::PrimitiveInitialization("missing ChaCha parameters"),
2454                ))
2455            }
2456        }
2457    }
2458
2459    fn check_key(expected: usize, key: &[u8]) -> Result<(), ProviderError> {
2460        if key.len() == expected {
2461            Ok(())
2462        } else {
2463            Err(ProviderError::InvalidKeySize {
2464                expected,
2465                actual: key.len(),
2466            })
2467        }
2468    }
2469
2470    fn encrypt_cbc<C>(
2471        provider: &dyn CryptoProvider,
2472        key: &[u8],
2473        plaintext: &[u8],
2474    ) -> Result<Vec<u8>, ProviderError>
2475    where
2476        C: aes::cipher::BlockCipherEncrypt + aes::cipher::KeyInit,
2477    {
2478        let block = C::block_size();
2479        let pad_len = block - (plaintext.len() % block);
2480        let length = plaintext
2481            .len()
2482            .checked_add(pad_len)
2483            .and_then(|len| len.checked_add(block))
2484            .ok_or(ProviderError::InvalidInput(
2485                ProviderInputError::AesCbcFraming,
2486            ))?;
2487        let mut output = zeroize::Zeroizing::new(vec![0_u8; length]);
2488        let (iv, padded) = output.split_at_mut(block);
2489        provider.fill_random(iv)?;
2490        padded[..plaintext.len()].copy_from_slice(plaintext);
2491        if pad_len > 1 {
2492            let last = padded.len() - 1;
2493            provider.fill_random(&mut padded[plaintext.len()..last])?;
2494        }
2495        *padded.last_mut().expect("padding is non-empty") = pad_len as u8;
2496        Encryptor::<C>::new_from_slices(key, iv)
2497            .map_err(|_| {
2498                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-CBC"))
2499            })?
2500            .encrypt_padded::<NoPadding>(padded, plaintext.len() + pad_len)
2501            .map_err(|_| {
2502                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2503                    "AES-CBC padding",
2504                ))
2505            })?;
2506        Ok(core::mem::take(&mut *output))
2507    }
2508
2509    fn decrypt_cbc<C>(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, ProviderError>
2510    where
2511        C: aes::cipher::BlockCipherDecrypt + aes::cipher::KeyInit,
2512    {
2513        let block = C::block_size();
2514        if ciphertext.len() < 2 * block || !(ciphertext.len() - block).is_multiple_of(block) {
2515            return Err(ProviderError::InvalidInput(
2516                ProviderInputError::AesCbcFraming,
2517            ));
2518        }
2519        let (iv, body) = ciphertext.split_at(block);
2520        let mut plaintext = zeroize::Zeroizing::new(body.to_vec());
2521        Decryptor::<C>::new_from_slices(key, iv)
2522            .map_err(|_| {
2523                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-CBC"))
2524            })?
2525            .decrypt_padded::<NoPadding>(&mut plaintext)
2526            .map_err(|_| ProviderError::InvalidInput(ProviderInputError::AesCbcCiphertext))?;
2527        let pad_len = *plaintext.last().ok_or(ProviderError::InvalidInput(
2528            ProviderInputError::AesCbcCiphertext,
2529        ))?;
2530        let padding_bytes = usize::from(pad_len);
2531        if !(1..=block).contains(&padding_bytes) || padding_bytes > plaintext.len() {
2532            return Err(ProviderError::InvalidInput(
2533                ProviderInputError::AesCbcCiphertext,
2534            ));
2535        }
2536        let length = plaintext.len() - padding_bytes;
2537        plaintext.truncate(length);
2538        Ok(core::mem::take(&mut *plaintext))
2539    }
2540
2541    fn encrypt_gcm<C>(
2542        provider: &dyn CryptoProvider,
2543        key: &[u8],
2544        plaintext: &[u8],
2545    ) -> Result<Vec<u8>, ProviderError>
2546    where
2547        C: AeadInOut + KeyInit,
2548    {
2549        let mut nonce = [0_u8; 12];
2550        provider.fill_random(&mut nonce)?;
2551        let cipher = C::new_from_slice(key).map_err(|_| {
2552            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-GCM"))
2553        })?;
2554        let length = plaintext
2555            .len()
2556            .checked_add(28)
2557            .ok_or(ProviderError::InvalidInput(
2558                ProviderInputError::PrimitiveInitialization("AES-GCM length"),
2559            ))?;
2560        // Encrypt directly in the final nonce-prefixed allocation rather than
2561        // copying an intermediate ciphertext into another document-sized Vec.
2562        let mut output = zeroize::Zeroizing::new(Vec::with_capacity(length));
2563        output.extend_from_slice(&nonce);
2564        output.extend_from_slice(plaintext);
2565        let nonce = Nonce::try_from(nonce.as_slice()).map_err(|_| {
2566            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2567                "AES-GCM nonce",
2568            ))
2569        })?;
2570        let tag = cipher
2571            .encrypt_inout_detached(&nonce, &[], output[12..].as_mut().into())
2572            .map_err(|_| ProviderError::AuthenticationFailed)?;
2573        output.extend_from_slice(&tag);
2574        Ok(core::mem::take(&mut *output))
2575    }
2576
2577    fn decrypt_gcm<C>(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, ProviderError>
2578    where
2579        C: AeadInOut + KeyInit,
2580    {
2581        if ciphertext.len() < 28 {
2582            return Err(ProviderError::InvalidInput(
2583                ProviderInputError::AesGcmFraming,
2584            ));
2585        }
2586        let (nonce, body) = ciphertext.split_at(12);
2587        let cipher = C::new_from_slice(key).map_err(|_| {
2588            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-GCM"))
2589        })?;
2590        let mut plaintext = zeroize::Zeroizing::new(body.to_vec());
2591        let nonce = Nonce::try_from(nonce).map_err(|_| {
2592            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2593                "AES-GCM nonce",
2594            ))
2595        })?;
2596        cipher
2597            .decrypt_in_place(&nonce, &[], &mut *plaintext)
2598            .map_err(|_| ProviderError::AuthenticationFailed)?;
2599        Ok(core::mem::take(&mut *plaintext))
2600    }
2601
2602    pub(super) fn wrap_key(
2603        provider: &dyn CryptoProvider,
2604        algorithm: KeyWrapAlgorithm,
2605        kek: &[u8],
2606        key: &[u8],
2607    ) -> Result<Vec<u8>, ProviderError> {
2608        check_key(algorithm.key_len(), kek)?;
2609        if let KeyWrapAlgorithm::Cbc(cipher) = algorithm {
2610            if cipher.cbc_block_len().is_none() {
2611                return Err(ProviderError::InvalidInput(
2612                    ProviderInputError::AesKeyWrapFraming,
2613                ));
2614            }
2615            // XMLEnc 1.1 ยง3.4 defines EncryptedKey as EncryptedType; CBC
2616            // encrypts key bytes using the same padding/IV contract as data.
2617            // https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedKey
2618            return encrypt_data(provider, cipher, kek, key);
2619        }
2620        #[cfg(feature = "legacy-algorithms")]
2621        if algorithm == KeyWrapAlgorithm::TripleDes {
2622            return wrap_des3(provider, kek, key);
2623        }
2624        #[cfg(not(feature = "legacy-algorithms"))]
2625        let _ = provider;
2626        // RFC 3394 ยง2.2.1 requires at least two 64-bit plaintext blocks.
2627        // Reject before allocating: the primitive also accepts the one-block
2628        // degenerate case, which is not this XML Encryption algorithm.
2629        // https://www.rfc-editor.org/rfc/rfc3394.html#section-2.2.1
2630        if key.len() < 16 || !key.len().is_multiple_of(8) {
2631            return Err(ProviderError::InvalidInput(
2632                ProviderInputError::AesKeyWrapFraming,
2633            ));
2634        }
2635        let length = algorithm
2636            .wrapped_len(key.len())
2637            .ok_or(ProviderError::InvalidInput(
2638                ProviderInputError::AesKeyWrapFraming,
2639            ))?;
2640        let mut output = vec![0_u8; length];
2641        // RFC 9231 ยง2.6.3 specifies RFC 3394 with Camellia replacing AES,
2642        // including the same A6 integrity register. Reuse its generic engine.
2643        // https://www.rfc-editor.org/rfc/rfc9231.html#section-2.6.3
2644        macro_rules! camellia_wrap {
2645            ($cipher:ty) => {
2646                aes_kw::AesKw::<$cipher>::new_from_slice(kek)
2647                    .map_err(|_| ProviderError::InvalidKeySize {
2648                        expected: algorithm.key_len(),
2649                        actual: kek.len(),
2650                    })?
2651                    .wrap_key(key, &mut output)
2652            };
2653        }
2654        match algorithm {
2655            KeyWrapAlgorithm::Cbc(_) => unreachable!("CBC wrapping already dispatched"),
2656            KeyWrapAlgorithm::CamelliaKw128 => camellia_wrap!(Camellia128),
2657            KeyWrapAlgorithm::CamelliaKw192 => camellia_wrap!(Camellia192),
2658            KeyWrapAlgorithm::CamelliaKw256 => camellia_wrap!(Camellia256),
2659            #[cfg(feature = "legacy-algorithms")]
2660            KeyWrapAlgorithm::AesKw192 => aes_kw::KwAes192::new_from_slice(kek)
2661                .map_err(|_| ProviderError::InvalidKeySize {
2662                    expected: 24,
2663                    actual: kek.len(),
2664                })?
2665                .wrap_key(key, &mut output),
2666            #[cfg(feature = "legacy-algorithms")]
2667            KeyWrapAlgorithm::TripleDes => unreachable!("CMS wrapping dispatched before AES"),
2668            KeyWrapAlgorithm::AesKw128 => KwAes128::new_from_slice(kek)
2669                .map_err(|_| ProviderError::InvalidKeySize {
2670                    expected: 16,
2671                    actual: kek.len(),
2672                })?
2673                .wrap_key(key, &mut output),
2674            KeyWrapAlgorithm::AesKw256 => KwAes256::new_from_slice(kek)
2675                .map_err(|_| ProviderError::InvalidKeySize {
2676                    expected: 32,
2677                    actual: kek.len(),
2678                })?
2679                .wrap_key(key, &mut output),
2680        }
2681        .map_err(|_| ProviderError::InvalidInput(ProviderInputError::AesKeyWrapFraming))?;
2682        Ok(output)
2683    }
2684
2685    pub(super) fn unwrap_key(
2686        algorithm: KeyWrapAlgorithm,
2687        kek: &[u8],
2688        wrapped: &[u8],
2689    ) -> Result<Vec<u8>, ProviderError> {
2690        check_key(algorithm.key_len(), kek)?;
2691        if let KeyWrapAlgorithm::Cbc(cipher) = algorithm {
2692            if cipher.cbc_block_len().is_none() {
2693                return Err(ProviderError::InvalidInput(
2694                    ProviderInputError::AesKeyWrapFraming,
2695                ));
2696            }
2697            return decrypt_data(cipher, kek, wrapped);
2698        }
2699        #[cfg(feature = "legacy-algorithms")]
2700        if algorithm == KeyWrapAlgorithm::TripleDes {
2701            return unwrap_des3(kek, wrapped);
2702        }
2703        if wrapped.len() < 24 || !wrapped.len().is_multiple_of(8) {
2704            return Err(ProviderError::InvalidInput(
2705                ProviderInputError::AesKeyWrapFraming,
2706            ));
2707        }
2708        let mut output = zeroize::Zeroizing::new(vec![0_u8; wrapped.len() - 8]);
2709        macro_rules! camellia_unwrap {
2710            ($cipher:ty) => {
2711                aes_kw::AesKw::<$cipher>::new_from_slice(kek)
2712                    .map_err(|_| ProviderError::InvalidKeySize {
2713                        expected: algorithm.key_len(),
2714                        actual: kek.len(),
2715                    })?
2716                    .unwrap_key(wrapped, &mut output)
2717            };
2718        }
2719        match algorithm {
2720            KeyWrapAlgorithm::Cbc(_) => unreachable!("CBC unwrapping already dispatched"),
2721            KeyWrapAlgorithm::CamelliaKw128 => camellia_unwrap!(Camellia128),
2722            KeyWrapAlgorithm::CamelliaKw192 => camellia_unwrap!(Camellia192),
2723            KeyWrapAlgorithm::CamelliaKw256 => camellia_unwrap!(Camellia256),
2724            #[cfg(feature = "legacy-algorithms")]
2725            KeyWrapAlgorithm::AesKw192 => aes_kw::KwAes192::new_from_slice(kek)
2726                .map_err(|_| ProviderError::InvalidKeySize {
2727                    expected: 24,
2728                    actual: kek.len(),
2729                })?
2730                .unwrap_key(wrapped, &mut output),
2731            #[cfg(feature = "legacy-algorithms")]
2732            KeyWrapAlgorithm::TripleDes => unreachable!("CMS unwrapping dispatched before AES"),
2733            KeyWrapAlgorithm::AesKw128 => KwAes128::new_from_slice(kek)
2734                .map_err(|_| ProviderError::InvalidKeySize {
2735                    expected: 16,
2736                    actual: kek.len(),
2737                })?
2738                .unwrap_key(wrapped, &mut output),
2739            KeyWrapAlgorithm::AesKw256 => KwAes256::new_from_slice(kek)
2740                .map_err(|_| ProviderError::InvalidKeySize {
2741                    expected: 32,
2742                    actual: kek.len(),
2743                })?
2744                .unwrap_key(wrapped, &mut output),
2745        }
2746        .map_err(|_| ProviderError::AuthenticationFailed)?;
2747        Ok(core::mem::take(&mut *output))
2748    }
2749
2750    #[cfg(feature = "legacy-algorithms")]
2751    const CMS_IV: [u8; 8] = [0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05];
2752
2753    #[cfg(feature = "legacy-algorithms")]
2754    fn wrap_des3(
2755        provider: &dyn CryptoProvider,
2756        kek: &[u8],
2757        key: &[u8],
2758    ) -> Result<Vec<u8>, ProviderError> {
2759        use sha1::Digest as _;
2760        // XMLEnc 1.1 ยง5.7.1 permits other key types in addition to RFC 3217's
2761        // DES CEK. Preserve opaque key octets (especially AES); generated DES
2762        // content keys are normalized to odd parity at their generation boundary.
2763        // https://www.w3.org/TR/xmlenc-core1/#sec-CMS-3DES
2764        if key.len() < 16 || !key.len().is_multiple_of(8) {
2765            return Err(ProviderError::InvalidInput(
2766                ProviderInputError::AesKeyWrapFraming,
2767            ));
2768        }
2769        let length = key
2770            .len()
2771            .checked_add(16)
2772            .ok_or(ProviderError::InvalidInput(
2773                ProviderInputError::AesKeyWrapFraming,
2774            ))?;
2775        let mut output = zeroize::Zeroizing::new(vec![0; length]);
2776        provider.fill_random(&mut output[..8])?;
2777        output[8..8 + key.len()].copy_from_slice(key);
2778        output[8 + key.len()..].copy_from_slice(&Sha1::digest(key)[..8]);
2779        let (iv, body) = output.split_at_mut(8);
2780        let body_len = body.len();
2781        Encryptor::<TdesEde3>::new_from_slices(kek, iv)
2782            .map_err(|_| ProviderError::AuthenticationFailed)?
2783            .encrypt_padded::<NoPadding>(body, body_len)
2784            .map_err(|_| ProviderError::AuthenticationFailed)?;
2785        output.reverse();
2786        Encryptor::<TdesEde3>::new_from_slices(kek, &CMS_IV)
2787            .map_err(|_| ProviderError::AuthenticationFailed)?
2788            .encrypt_padded::<NoPadding>(&mut output, length)
2789            .map_err(|_| ProviderError::AuthenticationFailed)?;
2790        Ok(core::mem::take(&mut *output))
2791    }
2792
2793    #[cfg(feature = "legacy-algorithms")]
2794    fn unwrap_des3(kek: &[u8], wrapped: &[u8]) -> Result<Vec<u8>, ProviderError> {
2795        use sha1::Digest as _;
2796        use subtle::ConstantTimeEq as _;
2797        // RFC 3217 ยง3.2: undo the outer CBC, octet reversal and inner CBC,
2798        // then authenticate the checksum before exposing any recovered bytes.
2799        // https://www.rfc-editor.org/rfc/rfc3217#section-3.2
2800        if wrapped.len() < 32 || !wrapped.len().is_multiple_of(8) {
2801            return Err(ProviderError::InvalidInput(
2802                ProviderInputError::AesKeyWrapFraming,
2803            ));
2804        }
2805        let mut output = zeroize::Zeroizing::new(wrapped.to_vec());
2806        Decryptor::<TdesEde3>::new_from_slices(kek, &CMS_IV)
2807            .map_err(|_| ProviderError::AuthenticationFailed)?
2808            .decrypt_padded::<NoPadding>(&mut output)
2809            .map_err(|_| ProviderError::AuthenticationFailed)?;
2810        output.reverse();
2811        let (iv, body) = output.split_at_mut(8);
2812        Decryptor::<TdesEde3>::new_from_slices(kek, iv)
2813            .map_err(|_| ProviderError::AuthenticationFailed)?
2814            .decrypt_padded::<NoPadding>(body)
2815            .map_err(|_| ProviderError::AuthenticationFailed)?;
2816        let key_len = body.len() - 8;
2817        if !bool::from(Sha1::digest(&body[..key_len])[..8].ct_eq(&body[key_len..])) {
2818            return Err(ProviderError::AuthenticationFailed);
2819        }
2820        output.copy_within(8..8 + key_len, 0);
2821        zeroize::Zeroize::zeroize(&mut output[key_len..]);
2822        output.truncate(key_len);
2823        Ok(core::mem::take(&mut *output))
2824    }
2825
2826    #[cfg(feature = "legacy-algorithms")]
2827    pub(super) fn transport_pkcs1v15(
2828        provider: &dyn CryptoProvider,
2829        key: &rsa::RsaPublicKey,
2830        plaintext: &[u8],
2831    ) -> Result<Vec<u8>, ProviderError> {
2832        rsa::Pkcs1v15Encrypt
2833            .encrypt(&mut super::ProviderRng(provider), key, plaintext)
2834            .map_err(map_rsa_error)
2835    }
2836
2837    pub(super) fn transport_key(
2838        provider: &dyn CryptoProvider,
2839        key: &rsa::RsaPublicKey,
2840        parameters: &RsaOaepParameters,
2841        plaintext: &[u8],
2842    ) -> Result<Vec<u8>, ProviderError> {
2843        if parameters.algorithm.requires_explicit_permission() {
2844            return Err(ProviderError::InvalidInput(
2845                ProviderInputError::PrimitiveInitialization("RSA-OAEP algorithm"),
2846            ));
2847        }
2848        run_oaep(provider, parameters, OaepOperation::Encrypt(key), plaintext)
2849    }
2850
2851    pub(super) fn recover_key(
2852        provider: &dyn CryptoProvider,
2853        key: &rsa::RsaPrivateKey,
2854        parameters: &RsaOaepParameters,
2855        ciphertext: &[u8],
2856    ) -> Result<Vec<u8>, ProviderError> {
2857        if parameters.algorithm.requires_explicit_permission() {
2858            return Err(ProviderError::InvalidInput(
2859                ProviderInputError::PrimitiveInitialization("RSA-OAEP algorithm"),
2860            ));
2861        }
2862        run_oaep(
2863            provider,
2864            parameters,
2865            OaepOperation::Decrypt(key),
2866            ciphertext,
2867        )
2868    }
2869
2870    enum OaepOperation<'a> {
2871        Encrypt(&'a rsa::RsaPublicKey),
2872        Decrypt(&'a rsa::RsaPrivateKey),
2873    }
2874
2875    fn run_oaep(
2876        provider: &dyn CryptoProvider,
2877        parameters: &RsaOaepParameters,
2878        operation: OaepOperation<'_>,
2879        input: &[u8],
2880    ) -> Result<Vec<u8>, ProviderError> {
2881        // RFC 8017 ยง7.1 selects Hash and MGF1's hash independently. One
2882        // dispatch keeps encryption and blinded recovery on the same contract.
2883        // https://www.rfc-editor.org/rfc/rfc8017.html#section-7.1
2884        match parameters.digest {
2885            #[cfg(feature = "legacy-algorithms")]
2886            OaepDigestAlgorithm::Md5 => {
2887                run_oaep_mgf::<md5::Md5>(provider, parameters, operation, input)
2888            }
2889            #[cfg(feature = "legacy-algorithms")]
2890            OaepDigestAlgorithm::Ripemd160 => {
2891                run_oaep_mgf::<ripemd::Ripemd160>(provider, parameters, operation, input)
2892            }
2893            OaepDigestAlgorithm::Sha3_224 => {
2894                run_oaep_mgf::<sha3::Sha3_224>(provider, parameters, operation, input)
2895            }
2896            OaepDigestAlgorithm::Sha3_256 => {
2897                run_oaep_mgf::<sha3::Sha3_256>(provider, parameters, operation, input)
2898            }
2899            OaepDigestAlgorithm::Sha3_384 => {
2900                run_oaep_mgf::<sha3::Sha3_384>(provider, parameters, operation, input)
2901            }
2902            OaepDigestAlgorithm::Sha3_512 => {
2903                run_oaep_mgf::<sha3::Sha3_512>(provider, parameters, operation, input)
2904            }
2905            OaepDigestAlgorithm::Sha1 => {
2906                run_oaep_mgf::<Sha1>(provider, parameters, operation, input)
2907            }
2908            OaepDigestAlgorithm::Sha224 => {
2909                run_oaep_mgf::<Sha224>(provider, parameters, operation, input)
2910            }
2911            OaepDigestAlgorithm::Sha256 => {
2912                run_oaep_mgf::<Sha256>(provider, parameters, operation, input)
2913            }
2914            OaepDigestAlgorithm::Sha384 => {
2915                run_oaep_mgf::<Sha384>(provider, parameters, operation, input)
2916            }
2917            OaepDigestAlgorithm::Sha512 => {
2918                run_oaep_mgf::<Sha512>(provider, parameters, operation, input)
2919            }
2920        }
2921    }
2922
2923    fn run_oaep_mgf<D: sha2::digest::Digest + sha2::digest::FixedOutputReset>(
2924        provider: &dyn CryptoProvider,
2925        parameters: &RsaOaepParameters,
2926        operation: OaepOperation<'_>,
2927        input: &[u8],
2928    ) -> Result<Vec<u8>, ProviderError> {
2929        match parameters.mgf_digest {
2930            OaepDigestAlgorithm::Sha1 => {
2931                run_oaep_primitive::<D, Sha1>(provider, parameters, operation, input)
2932            }
2933            OaepDigestAlgorithm::Sha224 => {
2934                run_oaep_primitive::<D, Sha224>(provider, parameters, operation, input)
2935            }
2936            OaepDigestAlgorithm::Sha256 => {
2937                run_oaep_primitive::<D, Sha256>(provider, parameters, operation, input)
2938            }
2939            OaepDigestAlgorithm::Sha384 => {
2940                run_oaep_primitive::<D, Sha384>(provider, parameters, operation, input)
2941            }
2942            OaepDigestAlgorithm::Sha512 => {
2943                run_oaep_primitive::<D, Sha512>(provider, parameters, operation, input)
2944            }
2945            _ => Err(ProviderError::InvalidInput(
2946                ProviderInputError::PrimitiveInitialization("unregistered XML OAEP mask digest"),
2947            )),
2948        }
2949    }
2950
2951    fn run_oaep_primitive<D, M>(
2952        provider: &dyn CryptoProvider,
2953        parameters: &RsaOaepParameters,
2954        operation: OaepOperation<'_>,
2955        input: &[u8],
2956    ) -> Result<Vec<u8>, ProviderError>
2957    where
2958        D: sha2::digest::Digest + sha2::digest::FixedOutputReset,
2959        M: sha2::digest::Digest + sha2::digest::FixedOutputReset,
2960    {
2961        let mut rng = super::ProviderRng(provider);
2962        let padding = Oaep::<D, M>::new_with_mgf_hash_and_label(parameters.label.clone());
2963        match operation {
2964            OaepOperation::Encrypt(key) => padding.encrypt(&mut rng, key, input),
2965            OaepOperation::Decrypt(key) => padding.decrypt(Some(&mut rng), key, input),
2966        }
2967        .map_err(map_rsa_error)
2968    }
2969
2970    pub(super) fn map_rsa_error(error: rsa::Error) -> ProviderError {
2971        match error {
2972            rsa::Error::Rng => ProviderError::Random("RSA randomness failed".into()),
2973            _ => ProviderError::AuthenticationFailed,
2974        }
2975    }
2976}
2977
2978#[cfg(test)]
2979mod tests {
2980    #[cfg(feature = "xmldsig")]
2981    use std::sync::atomic::AtomicUsize;
2982    use std::sync::atomic::{AtomicBool, Ordering};
2983
2984    use super::*;
2985
2986    #[cfg(feature = "xmlenc")]
2987    #[test]
2988    fn recovered_candidate_equality_includes_every_byte_and_validity() {
2989        // Deduplication must retain distinct candidates, including equal real/
2990        // rejection bytes with different validity. Timing is ensured by ct_eq,
2991        // not by a flaky wall-clock assertion.
2992        let reference = RecoveredContentKey::confirmed(vec![0x52; 32]);
2993        assert!(reference.same_candidate(&RecoveredContentKey::confirmed(vec![0x52; 32])));
2994        for index in 0..32 {
2995            let mut bytes = vec![0x52; 32];
2996            bytes[index] ^= 1;
2997            assert!(!reference.same_candidate(&RecoveredContentKey::confirmed(bytes)));
2998        }
2999        assert!(!reference.same_candidate(&RecoveredContentKey::confirmed(vec![0x52; 16])));
3000        for left in [0, 1] {
3001            for right in [0, 1] {
3002                let mut a = RecoveredContentKey::confirmed(vec![0x52; 32]);
3003                let mut b = RecoveredContentKey::confirmed(vec![0x52; 32]);
3004                a.valid = subtle::Choice::from(left);
3005                b.valid = subtle::Choice::from(right);
3006                assert_eq!(a.same_candidate(&b), left == right);
3007            }
3008        }
3009    }
3010
3011    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
3012    #[test]
3013    fn legacy_ciphers_cover_empty_and_block_boundary_plaintexts() {
3014        // Each cipher's own framing, including DES's 8-byte blocks, must agree
3015        // with the public size preflight for empty and non-aligned messages.
3016        for algorithm in [
3017            DataEncryptionAlgorithm::TripleDesCbc,
3018            DataEncryptionAlgorithm::Aes192Cbc,
3019            DataEncryptionAlgorithm::Aes192Gcm,
3020        ] {
3021            let key = [0x31; 24];
3022            for length in [0, 1, 7, 8, 15, 16, 17, 31, 32, 33] {
3023                let plaintext = vec![0x41; length];
3024                let ciphertext = RUST_CRYPTO_PROVIDER
3025                    .encrypt_data(algorithm, &key, &plaintext)
3026                    .expect("valid legacy encryption must succeed");
3027                assert_eq!(
3028                    Some(ciphertext.len()),
3029                    algorithm.ciphertext_len_for_plaintext(length)
3030                );
3031                assert_eq!(
3032                    RUST_CRYPTO_PROVIDER
3033                        .decrypt_data(algorithm, &key, &ciphertext)
3034                        .expect("matching legacy key must recover plaintext"),
3035                    plaintext
3036                );
3037                assert!(
3038                    RUST_CRYPTO_PROVIDER
3039                        .encrypt_data(algorithm, &key[..23], &plaintext)
3040                        .is_err()
3041                );
3042                assert!(
3043                    RUST_CRYPTO_PROVIDER
3044                        .decrypt_data(algorithm, &key[..23], &ciphertext)
3045                        .is_err()
3046                );
3047                assert!(
3048                    RUST_CRYPTO_PROVIDER
3049                        .decrypt_data(algorithm, &key, &ciphertext[..ciphertext.len() - 1])
3050                        .is_err()
3051                );
3052                if algorithm == DataEncryptionAlgorithm::Aes192Gcm {
3053                    let mut corrupted = ciphertext;
3054                    corrupted[12] ^= 1;
3055                    assert!(matches!(
3056                        RUST_CRYPTO_PROVIDER.decrypt_data(algorithm, &key, &corrupted),
3057                        Err(ProviderError::AuthenticationFailed)
3058                    ));
3059                }
3060            }
3061        }
3062    }
3063
3064    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
3065    #[test]
3066    fn cms_triple_des_unwrap_matches_rfc3217_vector() {
3067        // RFC 3217 ยง3.4 independently validates both CBC passes and reversal;
3068        // a round-trip alone could hide matching errors in wrap and unwrap.
3069        // https://www.rfc-editor.org/rfc/rfc3217.html#section-3.4
3070        let kek = hex_literal::hex!("255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f");
3071        let key = hex_literal::hex!("2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98");
3072        let wrapped = hex_literal::hex!(
3073            "690107618ef092b3b48ca1796b234ae9fa33ebb4159604037db5d6a84eb3aac2768c632775a467d4"
3074        );
3075        assert_eq!(
3076            RUST_CRYPTO_PROVIDER
3077                .unwrap_key(KeyWrapAlgorithm::TripleDes, &kek, &wrapped)
3078                .expect("RFC 3217 vector must authenticate"),
3079            key
3080        );
3081        for offset in 0..wrapped.len() {
3082            let mut corrupted = wrapped;
3083            corrupted[offset] ^= 1;
3084            assert!(matches!(
3085                RUST_CRYPTO_PROVIDER.unwrap_key(KeyWrapAlgorithm::TripleDes, &kek, &corrupted),
3086                Err(ProviderError::AuthenticationFailed)
3087            ));
3088        }
3089    }
3090
3091    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
3092    #[test]
3093    fn legacy_wrap_preserves_opaque_key_bytes_and_checks_integrity() {
3094        // XMLEnc permits wrapping non-DES keys; parity normalization must not
3095        // corrupt opaque AES key octets. Each algorithm has its own overhead.
3096        for algorithm in [KeyWrapAlgorithm::AesKw192, KeyWrapAlgorithm::TripleDes] {
3097            let kek = [0x37; 24];
3098            for length in [16, 24, 32] {
3099                let key: Vec<_> = (0..length).map(|byte| byte as u8).collect();
3100                let mut wrapped = RUST_CRYPTO_PROVIDER
3101                    .wrap_key(algorithm, &kek, &key)
3102                    .expect("valid content key must wrap");
3103                assert_eq!(Some(wrapped.len()), algorithm.wrapped_len(key.len()));
3104                assert_eq!(
3105                    RUST_CRYPTO_PROVIDER
3106                        .unwrap_key(algorithm, &kek, &wrapped)
3107                        .expect("matching KEK must recover exact key octets"),
3108                    key
3109                );
3110                assert!(
3111                    RUST_CRYPTO_PROVIDER
3112                        .unwrap_key(algorithm, &[0x38; 24], &wrapped)
3113                        .is_err()
3114                );
3115                wrapped[0] ^= 1;
3116                assert!(
3117                    RUST_CRYPTO_PROVIDER
3118                        .unwrap_key(algorithm, &kek, &wrapped)
3119                        .is_err()
3120                );
3121            }
3122            for length in [0, 1, 8, 15, 17] {
3123                assert!(
3124                    RUST_CRYPTO_PROVIDER
3125                        .wrap_key(algorithm, &kek, &vec![0; length])
3126                        .is_err()
3127                );
3128            }
3129        }
3130    }
3131
3132    #[cfg(all(feature = "legacy-algorithms", feature = "xmldsig"))]
3133    #[test]
3134    fn legacy_digests_match_known_answers() {
3135        // Published empty/abc answers prevent URI wiring from silently choosing
3136        // a different hash with the same output width.
3137        for (algorithm, expected) in [
3138            (
3139                DigestAlgorithm::Md5,
3140                &hex_literal::hex!("900150983cd24fb0d6963f7d28e17f72")[..],
3141            ),
3142            (
3143                DigestAlgorithm::Ripemd160,
3144                &hex_literal::hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")[..],
3145            ),
3146        ] {
3147            assert_eq!(
3148                RUST_CRYPTO_PROVIDER
3149                    .digest(algorithm, b"abc")
3150                    .expect("compiled digest must be supported"),
3151                expected
3152            );
3153            assert_eq!(DigestAlgorithm::from_uri(algorithm.uri()), Some(algorithm));
3154            assert!(algorithm.requires_explicit_permission());
3155            assert!(!algorithm.signing_allowed());
3156        }
3157    }
3158
3159    /// A provider defect: one byte more than the algorithm's digest width.
3160    #[cfg(feature = "xmldsig")]
3161    struct ExtraByteDigest(Box<dyn DigestState>);
3162
3163    #[cfg(feature = "xmldsig")]
3164    impl DigestState for ExtraByteDigest {
3165        fn update(&mut self, data: &[u8]) -> Result<(), ProviderError> {
3166            self.0.update(data)
3167        }
3168        fn finalize(self: Box<Self>) -> Result<Vec<u8>, ProviderError> {
3169            let mut digest = self.0.finalize()?;
3170            digest.push(0);
3171            Ok(digest)
3172        }
3173    }
3174
3175    #[cfg(feature = "xmldsig")]
3176    struct CountingRandomProvider {
3177        random_calls: AtomicUsize,
3178        reject_digest: Option<DigestAlgorithm>,
3179        extra_digest_byte: bool,
3180        accept_signatures: bool,
3181    }
3182
3183    #[cfg(feature = "xmldsig")]
3184    impl CryptoProvider for CountingRandomProvider {
3185        fn name(&self) -> &'static str {
3186            "counting-random"
3187        }
3188
3189        fn supports(&self, capability: ProviderCapability<'_>) -> bool {
3190            RUST_CRYPTO_PROVIDER.supports(capability)
3191        }
3192
3193        fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError> {
3194            self.random_calls.fetch_add(1, Ordering::Relaxed);
3195            RUST_CRYPTO_PROVIDER.fill_random(output)
3196        }
3197
3198        fn derive_key(
3199            &self,
3200            parameters: &KdfParameters<'_>,
3201            secret: &[u8],
3202        ) -> Result<Vec<u8>, ProviderError> {
3203            RUST_CRYPTO_PROVIDER.derive_key(parameters, secret)
3204        }
3205
3206        fn begin_digest(
3207            &self,
3208            algorithm: DigestAlgorithm,
3209        ) -> Result<Box<dyn DigestState + '_>, ProviderError> {
3210            if self.reject_digest == Some(algorithm) {
3211                return Err(ProviderError::Unsupported {
3212                    operation: ProviderOperation::Digest,
3213                    algorithm: Some(algorithm.uri().to_owned()),
3214                });
3215            }
3216            let state = RUST_CRYPTO_PROVIDER.begin_digest(algorithm)?;
3217            Ok(if self.extra_digest_byte {
3218                Box::new(ExtraByteDigest(state))
3219            } else {
3220                state
3221            })
3222        }
3223
3224        fn sign(
3225            &self,
3226            key: &dyn crate::xmldsig::SigningKey,
3227            algorithm: crate::xmldsig::SignatureAlgorithm,
3228            data: &[u8],
3229        ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
3230            key.sign_with_provider(self, algorithm, data)
3231        }
3232
3233        fn verify(
3234            &self,
3235            key: &dyn crate::xmldsig::VerifyingKey,
3236            algorithm: crate::xmldsig::SignatureAlgorithm,
3237            data: &[u8],
3238            signature: &[u8],
3239        ) -> Result<bool, crate::xmldsig::DsigError> {
3240            if self.accept_signatures {
3241                return Ok(true);
3242            }
3243            RUST_CRYPTO_PROVIDER.verify(key, algorithm, data, signature)
3244        }
3245
3246        #[cfg(feature = "xmlenc")]
3247        fn encrypt_data(
3248            &self,
3249            algorithm: DataEncryptionAlgorithm,
3250            key: &[u8],
3251            plaintext: &[u8],
3252        ) -> Result<Vec<u8>, ProviderError> {
3253            RUST_CRYPTO_PROVIDER.encrypt_data(algorithm, key, plaintext)
3254        }
3255
3256        #[cfg(feature = "xmlenc")]
3257        fn decrypt_data(
3258            &self,
3259            algorithm: DataEncryptionAlgorithm,
3260            key: &[u8],
3261            ciphertext: &[u8],
3262        ) -> Result<Vec<u8>, ProviderError> {
3263            RUST_CRYPTO_PROVIDER.decrypt_data(algorithm, key, ciphertext)
3264        }
3265
3266        #[cfg(feature = "xmlenc")]
3267        fn wrap_key(
3268            &self,
3269            algorithm: KeyWrapAlgorithm,
3270            kek: &[u8],
3271            key: &[u8],
3272        ) -> Result<Vec<u8>, ProviderError> {
3273            RUST_CRYPTO_PROVIDER.wrap_key(algorithm, kek, key)
3274        }
3275
3276        #[cfg(feature = "xmlenc")]
3277        fn unwrap_key(
3278            &self,
3279            algorithm: KeyWrapAlgorithm,
3280            kek: &[u8],
3281            wrapped: &[u8],
3282        ) -> Result<Vec<u8>, ProviderError> {
3283            RUST_CRYPTO_PROVIDER.unwrap_key(algorithm, kek, wrapped)
3284        }
3285
3286        #[cfg(feature = "xmlenc")]
3287        fn transport_key(
3288            &self,
3289            key: &dyn KeyTransportKey,
3290            parameters: &RsaOaepParameters,
3291            plaintext: &[u8],
3292        ) -> Result<Vec<u8>, ProviderError> {
3293            RUST_CRYPTO_PROVIDER.transport_key(key, parameters, plaintext)
3294        }
3295
3296        #[cfg(feature = "xmlenc")]
3297        fn recover_key(
3298            &self,
3299            key: &dyn KeyRecoveryKey,
3300            parameters: &RsaOaepParameters,
3301            ciphertext: &[u8],
3302        ) -> Result<Vec<u8>, ProviderError> {
3303            RUST_CRYPTO_PROVIDER.recover_key(key, parameters, ciphertext)
3304        }
3305    }
3306
3307    #[cfg(feature = "xmldsig")]
3308    #[test]
3309    fn capability_query_is_explicit_about_unimplemented_operations() {
3310        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Digest(DigestAlgorithm::Sha256)));
3311        let agreement = KeyAgreementParameters {
3312            algorithm: "urn:unsupported:agreement",
3313            peer_public_key: &[],
3314        };
3315        assert!(!RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyAgreement(&agreement)));
3316        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Sign(
3317            crate::xmldsig::SignatureAlgorithm::RsaSha1
3318        )));
3319        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Verify(
3320            crate::xmldsig::SignatureAlgorithm::RsaSha1
3321        )));
3322        for digest in [DigestAlgorithm::Sha1, DigestAlgorithm::Sha512] {
3323            assert!(
3324                RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(
3325                    X509SignatureAlgorithm::Ecdsa(digest)
3326                ))
3327            );
3328        }
3329        // Certificate capabilities must agree with the actual execution
3330        // mapping, not merely with the presence of an ECDSA primitive.
3331        #[cfg(feature = "legacy-algorithms")]
3332        for digest in [DigestAlgorithm::Md5, DigestAlgorithm::Ripemd160] {
3333            assert!(
3334                !RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(
3335                    X509SignatureAlgorithm::Ecdsa(digest)
3336                ))
3337            );
3338        }
3339    }
3340
3341    #[cfg(feature = "xmldsig")]
3342    #[test]
3343    fn x509_digest_key_info_uses_the_selected_provider() {
3344        use crate::xmldsig::{
3345            DigestAlgorithm, KeyInfoWriteError, KeyInfoWriter, RsaSigningKey,
3346            X509DigestKeyInfoWriter,
3347        };
3348
3349        // KeyInfo generation is part of the signing operation's provider
3350        // boundary; a writer must not silently fall back to RustCrypto.
3351        let key = RsaSigningKey::from_pkcs8_pem(include_str!(
3352            "../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3353        ))
3354        .expect("RSA fixture must parse");
3355        let writer = X509DigestKeyInfoWriter::from_pem(
3356            include_str!("../tests/fixtures/keys/rsa/rsa-2048-cert.pem"),
3357            DigestAlgorithm::Sha224,
3358        )
3359        .expect("certificate fixture must parse");
3360        let provider = CountingRandomProvider {
3361            random_calls: AtomicUsize::new(0),
3362            reject_digest: Some(DigestAlgorithm::Sha224),
3363            extra_digest_byte: false,
3364            accept_signatures: false,
3365        };
3366
3367        let error = writer
3368            .write_key_info_with_provider(&key, &provider)
3369            .expect_err("the selected provider must control X509Digest");
3370        assert!(matches!(
3371            error,
3372            KeyInfoWriteError::Provider(ProviderError::Unsupported {
3373                operation: ProviderOperation::Digest,
3374                ..
3375            })
3376        ));
3377    }
3378
3379    #[cfg(feature = "xmldsig")]
3380    #[test]
3381    fn x509_digest_writer_rejects_trailing_certificate_der() {
3382        use crate::xmldsig::{DigestAlgorithm, KeyInfoWriteError, X509DigestKeyInfoWriter};
3383
3384        // The writer retains both digest bytes and the validated signing-key
3385        // identity, so construction must accept exactly one DER certificate.
3386        let (_, certificate) = x509_parser::pem::parse_x509_pem(include_bytes!(
3387            "../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3388        ))
3389        .expect("certificate fixture must parse");
3390        let mut certificate_der = certificate.contents;
3391        certificate_der.push(0);
3392
3393        assert!(matches!(
3394            X509DigestKeyInfoWriter::from_der(&certificate_der, DigestAlgorithm::Sha256),
3395            Err(KeyInfoWriteError::InvalidCertificateDer)
3396        ));
3397    }
3398
3399    #[cfg(all(feature = "xmldsig", feature = "xmlenc"))]
3400    #[test]
3401    fn capability_queries_include_oaep_and_pss_parameters() {
3402        use crate::xmlenc::{KeyTransportAlgorithm, OaepDigestAlgorithm};
3403
3404        let explicit_legacy = RsaOaepParameters {
3405            algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
3406            digest: OaepDigestAlgorithm::Sha256,
3407            mgf_digest: OaepDigestAlgorithm::Sha256,
3408            label: Vec::new(),
3409        };
3410        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyTransport(&explicit_legacy)));
3411        let modern =
3412            RsaOaepParameters::xmlenc11(OaepDigestAlgorithm::Sha256, OaepDigestAlgorithm::Sha512)
3413                .label(b"label".to_vec());
3414        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyTransport(&modern)));
3415
3416        let supported_pss = X509SignatureAlgorithm::RsaPss {
3417            digest: DigestAlgorithm::Sha256,
3418            mgf_digest: DigestAlgorithm::Sha256,
3419            salt_len: 32,
3420        };
3421        assert!(
3422            RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(supported_pss))
3423        );
3424        let unsupported_pss = X509SignatureAlgorithm::RsaPss {
3425            digest: DigestAlgorithm::Sha256,
3426            mgf_digest: DigestAlgorithm::Sha384,
3427            salt_len: 32,
3428        };
3429        assert!(
3430            !RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(unsupported_pss))
3431        );
3432    }
3433
3434    struct RecordingAgreementKey(AtomicBool);
3435
3436    impl KeyAgreementKey for RecordingAgreementKey {
3437        fn agree(
3438            &self,
3439            _parameters: &KeyAgreementParameters<'_>,
3440        ) -> Result<Vec<u8>, ProviderError> {
3441            self.0.store(true, Ordering::Relaxed);
3442            Ok(vec![0x42])
3443        }
3444    }
3445
3446    #[test]
3447    fn unsupported_agreement_and_kdf_fail_without_dispatch_or_fallback() {
3448        let agreement = KeyAgreementParameters {
3449            algorithm: "urn:example:agreement",
3450            peer_public_key: b"peer",
3451        };
3452        let key = RecordingAgreementKey(AtomicBool::new(false));
3453        let error = RUST_CRYPTO_PROVIDER
3454            .agree_key(&key, &agreement)
3455            .expect_err("unsupported agreement must fail closed");
3456        assert!(matches!(
3457            error,
3458            ProviderError::Unsupported {
3459                operation: ProviderOperation::KeyAgreement,
3460                algorithm: Some(ref algorithm),
3461            } if algorithm == agreement.algorithm
3462        ));
3463        assert!(!key.0.load(Ordering::Relaxed));
3464
3465        let kdf = KdfParameters {
3466            algorithm: "urn:example:kdf",
3467            digest: Some("urn:example:digest"),
3468            salt: b"salt",
3469            info: KdfContext::Octets(b"info"),
3470            iterations: 1,
3471            output_len: 32,
3472        };
3473        assert!(matches!(
3474            RUST_CRYPTO_PROVIDER.derive_key(&kdf, b"secret"),
3475            Err(ProviderError::Unsupported {
3476                operation: ProviderOperation::Kdf,
3477                algorithm: Some(ref algorithm),
3478            }) if algorithm == kdf.algorithm
3479        ));
3480    }
3481
3482    #[cfg(feature = "xmldsig")]
3483    #[test]
3484    fn rsa_signing_uses_the_selected_providers_randomness() {
3485        use crate::xmldsig::{RsaSigningKey, SignatureAlgorithm};
3486
3487        // RSA PKCS#1 v1.5 uses randomness for blinding even though its wire
3488        // signature is deterministic; the selected provider owns that source.
3489        let key = RsaSigningKey::from_pkcs8_pem(include_str!(
3490            "../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3491        ))
3492        .expect("RSA fixture must parse");
3493        let provider = CountingRandomProvider {
3494            random_calls: AtomicUsize::new(0),
3495            reject_digest: None,
3496            extra_digest_byte: false,
3497            accept_signatures: false,
3498        };
3499
3500        let signature = provider
3501            .sign(&key, SignatureAlgorithm::RsaSha256, b"signed info")
3502            .expect("RSA signing must succeed");
3503
3504        assert!(!signature.is_empty());
3505        assert!(provider.random_calls.load(Ordering::Relaxed) > 0);
3506    }
3507
3508    #[cfg(feature = "xmldsig")]
3509    #[test]
3510    fn ecdsa_signing_uses_the_selected_providers_digest() {
3511        use crate::xmldsig::{
3512            EcdsaP256SigningKey, EcdsaP384SigningKey, SignatureAlgorithm, SigningKeyError,
3513        };
3514
3515        // SignatureMethod chooses the hash independently of the EC key curve.
3516        // Both built-in ECDSA keys must therefore ask the selected provider for
3517        // that digest instead of hashing behind the provider boundary.
3518        let cases: [(
3519            Box<dyn crate::xmldsig::SigningKey>,
3520            SignatureAlgorithm,
3521            DigestAlgorithm,
3522        ); 2] = [
3523            (
3524                Box::new(
3525                    EcdsaP256SigningKey::from_pkcs8_pem(include_str!(
3526                        "../tests/fixtures/keys/ec/ec-prime256v1-key.pem"
3527                    ))
3528                    .expect("P-256 fixture must parse"),
3529                ),
3530                SignatureAlgorithm::EcdsaSha384,
3531                DigestAlgorithm::Sha384,
3532            ),
3533            (
3534                Box::new(
3535                    EcdsaP384SigningKey::from_pkcs8_pem(include_str!(
3536                        "../tests/fixtures/keys/ec/ec-prime384v1-key.pem"
3537                    ))
3538                    .expect("P-384 fixture must parse"),
3539                ),
3540                SignatureAlgorithm::EcdsaSha256,
3541                DigestAlgorithm::Sha256,
3542            ),
3543        ];
3544
3545        for (key, signature_algorithm, digest_algorithm) in cases {
3546            let provider = CountingRandomProvider {
3547                random_calls: AtomicUsize::new(0),
3548                reject_digest: Some(digest_algorithm),
3549                extra_digest_byte: false,
3550                accept_signatures: false,
3551            };
3552            let error = provider
3553                .sign(key.as_ref(), signature_algorithm, b"signed info")
3554                .expect_err("provider digest rejection must stop ECDSA signing");
3555
3556            assert!(matches!(
3557                error,
3558                SigningKeyError::Provider(ProviderError::Unsupported {
3559                    operation: ProviderOperation::Digest,
3560                    algorithm: Some(ref uri),
3561                }) if uri == digest_algorithm.uri()
3562            ));
3563        }
3564    }
3565
3566    #[cfg(feature = "xmldsig")]
3567    #[test]
3568    fn ecdsa_signing_rejects_provider_digests_with_the_wrong_length() {
3569        use crate::xmldsig::{
3570            EcdsaP256SigningKey, EcdsaP384SigningKey, SignatureAlgorithm, SigningKeyError,
3571        };
3572
3573        // Prehash signers may truncate oversized input, so the provider
3574        // boundary must reject it before either curve receives the digest.
3575        let cases: [(
3576            Box<dyn crate::xmldsig::SigningKey>,
3577            SignatureAlgorithm,
3578            usize,
3579        ); 2] = [
3580            (
3581                Box::new(
3582                    EcdsaP256SigningKey::from_pkcs8_pem(include_str!(
3583                        "../tests/fixtures/keys/ec/ec-prime256v1-key.pem"
3584                    ))
3585                    .expect("P-256 fixture must parse"),
3586                ),
3587                SignatureAlgorithm::EcdsaSha256,
3588                32,
3589            ),
3590            (
3591                Box::new(
3592                    EcdsaP384SigningKey::from_pkcs8_pem(include_str!(
3593                        "../tests/fixtures/keys/ec/ec-prime384v1-key.pem"
3594                    ))
3595                    .expect("P-384 fixture must parse"),
3596                ),
3597                SignatureAlgorithm::EcdsaSha384,
3598                48,
3599            ),
3600        ];
3601
3602        for (key, algorithm, expected) in cases {
3603            let provider = CountingRandomProvider {
3604                random_calls: AtomicUsize::new(0),
3605                reject_digest: None,
3606                extra_digest_byte: true,
3607                accept_signatures: false,
3608            };
3609            let error = provider
3610                .sign(key.as_ref(), algorithm, b"signed info")
3611                .expect_err("an oversized provider digest must not reach ECDSA prehash signing");
3612
3613            assert!(matches!(
3614                error,
3615                SigningKeyError::Provider(ProviderError::InvalidOutputSize {
3616                    operation: ProviderOperation::Digest,
3617                    expected: actual_expected,
3618                    actual,
3619                }) if actual_expected == expected && actual == expected + 1
3620            ));
3621        }
3622    }
3623
3624    #[cfg(feature = "xmldsig")]
3625    #[test]
3626    fn verification_facade_rejects_malformed_dsa_before_provider_dispatch() {
3627        use crate::xmldsig::{
3628            DefaultKeyResolver, DsigStatus, FailureReason, SignatureAlgorithm, VerifyContext,
3629        };
3630
3631        let original = include_str!(
3632            "../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/signature-enveloping-dsa.xml"
3633        );
3634        let value_start = original
3635            .find("<SignatureValue>")
3636            .expect("Merlin fixture must contain SignatureValue")
3637            + "<SignatureValue>".len();
3638        let value_end = original[value_start..]
3639            .find("</SignatureValue>")
3640            .map(|offset| value_start + offset)
3641            .expect("Merlin fixture must close SignatureValue");
3642        let mut malformed = original.to_owned();
3643        malformed.replace_range(value_start..value_end, "AQ==");
3644        let provider = CountingRandomProvider {
3645            random_calls: AtomicUsize::new(0),
3646            reject_digest: None,
3647            extra_digest_byte: false,
3648            accept_signatures: true,
3649        };
3650
3651        let mut policy = crate::policy::VerificationPolicy::default();
3652        policy
3653            .key_trust
3654            .allowed_legacy_signature_algorithms
3655            .insert(SignatureAlgorithm::DsaSha1);
3656        policy.key_trust.dsa_keys.minimum_modulus_bits = 1024;
3657        policy.key_trust.mode = crate::policy::VerificationTrustMode::CryptographicOnly;
3658        let result = VerifyContext::new()
3659            .policy(policy)
3660            .provider(&provider)
3661            .key_resolver(&DefaultKeyResolver::default())
3662            .verify(&malformed)
3663            .expect("malformed framing must be a verification miss");
3664
3665        assert_eq!(
3666            result.status,
3667            DsigStatus::Invalid(FailureReason::SignatureMismatch)
3668        );
3669    }
3670
3671    #[cfg(feature = "xmldsig")]
3672    #[test]
3673    fn rustcrypto_provider_verifies_parameterized_rsa_pss_certificates() {
3674        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3675        use der::{Decode as _, Encode as _};
3676        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3677        use rsa::{RsaPrivateKey, pss::SigningKey as RsaPssSigningKey};
3678        use sha2::Sha256;
3679        use signature::{RandomizedSigner, SignatureEncoding};
3680        use x509_cert::spki::{AlgorithmIdentifierOwned, ObjectIdentifier};
3681
3682        // X.509 RSASSA-PSS carries salt and MGF parameters that cannot be
3683        // represented by the XMLDSig SignatureAlgorithm enum.
3684        let mut rng = ChaCha20Rng::from_seed([0x5a; 32]);
3685        let private_key =
3686            RsaPrivateKey::new(&mut rng, 2048).expect("deterministic RSA key generation");
3687        let public_key = private_key
3688            .to_public_key()
3689            .to_public_key_der()
3690            .expect("RSA public key must encode as SPKI");
3691        let signing_key = RsaPssSigningKey::<Sha256>::new_with_salt_len(private_key, 32);
3692        let signed_data = b"certificate tbs bytes";
3693        let signature = signing_key
3694            .try_sign_with_rng(&mut rng, signed_data)
3695            .expect("RSA-PSS signing must succeed")
3696            .to_vec();
3697
3698        assert!(
3699            RUST_CRYPTO_PROVIDER
3700                .verify_x509_signature(
3701                    X509SignatureAlgorithm::RsaPss {
3702                        digest: DigestAlgorithm::Sha256,
3703                        mgf_digest: DigestAlgorithm::Sha256,
3704                        salt_len: 32,
3705                    },
3706                    signed_data,
3707                    &signature,
3708                    public_key.as_bytes(),
3709                )
3710                .expect("standard RSA-PSS parameters must be supported")
3711        );
3712
3713        let mut parameterless_pss_spki =
3714            x509_cert::SubjectPublicKeyInfo::from_der(public_key.as_bytes())
3715                .expect("RSA SPKI must decode");
3716        parameterless_pss_spki.algorithm = AlgorithmIdentifierOwned {
3717            oid: ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"),
3718            parameters: None,
3719        };
3720        let parameterless_pss_spki = parameterless_pss_spki
3721            .to_der()
3722            .expect("parameterless PSS SPKI must encode");
3723        assert!(
3724            RUST_CRYPTO_PROVIDER
3725                .verify_x509_signature(
3726                    X509SignatureAlgorithm::RsaPss {
3727                        digest: DigestAlgorithm::Sha256,
3728                        mgf_digest: DigestAlgorithm::Sha256,
3729                        salt_len: 32,
3730                    },
3731                    signed_data,
3732                    &signature,
3733                    &parameterless_pss_spki,
3734                )
3735                .expect("parameterless PSS keys impose no signature restrictions")
3736        );
3737
3738        let mut pss_spki = x509_cert::SubjectPublicKeyInfo::from_der(public_key.as_bytes())
3739            .expect("RSA SPKI must decode");
3740        let pss_parameters = der::asn1::Any::from_der(&[
3741            0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
3742            0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48,
3743            0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
3744            0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20,
3745        ])
3746        .expect("standard SHA-256 PSS parameters must decode");
3747        pss_spki.algorithm = AlgorithmIdentifierOwned {
3748            oid: ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"),
3749            parameters: Some(pss_parameters),
3750        };
3751        let pss_spki = pss_spki.to_der().expect("PSS SPKI must encode");
3752
3753        assert!(
3754            RUST_CRYPTO_PROVIDER
3755                .verify_x509_signature(
3756                    X509SignatureAlgorithm::RsaPss {
3757                        digest: DigestAlgorithm::Sha256,
3758                        mgf_digest: DigestAlgorithm::Sha256,
3759                        salt_len: 32,
3760                    },
3761                    signed_data,
3762                    &signature,
3763                    &pss_spki,
3764                )
3765                .expect("RFC 4055 PSS SubjectPublicKeyInfo must be supported")
3766        );
3767
3768        for incompatible in [
3769            X509SignatureAlgorithm::RsaPss {
3770                digest: DigestAlgorithm::Sha384,
3771                mgf_digest: DigestAlgorithm::Sha256,
3772                salt_len: 32,
3773            },
3774            X509SignatureAlgorithm::RsaPss {
3775                digest: DigestAlgorithm::Sha256,
3776                mgf_digest: DigestAlgorithm::Sha384,
3777                salt_len: 32,
3778            },
3779            X509SignatureAlgorithm::RsaPss {
3780                digest: DigestAlgorithm::Sha256,
3781                mgf_digest: DigestAlgorithm::Sha256,
3782                salt_len: 16,
3783            },
3784        ] {
3785            assert!(
3786                !RUST_CRYPTO_PROVIDER
3787                    .verify_x509_signature(incompatible, signed_data, &signature, &pss_spki,)
3788                    .expect("incompatible PSS key restrictions are invalid, not unsupported")
3789            );
3790        }
3791    }
3792
3793    #[cfg(feature = "xmldsig")]
3794    #[test]
3795    fn rustcrypto_provider_verifies_dsa_certificate_signature_at_q_width() {
3796        use base64::Engine as _;
3797
3798        // OpenSSL-generated L=2048/N=224 DSA material. X.509 carries DER r/s
3799        // integers at q width, not XMLDSig's legacy fixed 20-byte components.
3800        let spki = base64::engine::general_purpose::STANDARD
3801            .decode("MIIDQzCCAjYGByqGSM44BAEwggIpAoIBAQDEkm7mUEj1dizQRRrcU6ehyhpQ1NAkcKi9XyNcBJDZlyTdVH09XZ04UZNuXAWRL1hEDvDAvFimuwmW7k099j0PRM+WypsfOOgZPJhIVNZu9poTPGINKpbMTXFmR+qhrYM4z+NSKxuUBWZwX5HibBIG5INbx8IDHWAxZqxgHQsebDej1+yZyCTTpmDS9nKGkBRVaxsJgZt958UPNlIz1ECf4n4P4mPLAl7W5xV8VSWMqlXdkOAPbLC/mChjFoCj0jmCQpbcOvd7a6cWhcyhw/yikoVoKEPNWr9xLtdJV37f1/4q/xTvoPKWhMmgMQ/DigUnYgPzmexyS82m5HLZ/vOJAh0A/ckrg9g9PsZesUsH/4bEijeNwWGXB5e+/LCt0QKCAQEAuBGFzyjZEmvbDKbb+8tz+zqw4lK7RGwOjVM3v9xPS6LuG5L1OwCNQcUcVIsU9VxBnEx9oMnl8eVX1nq3kfdiZB2F9ESxwX5FzBt+KLjMOzBa8rPlzVcyCZ3sT3orAQ2D/q7ffDhTCUt+v8UNiAhVbaNnR/vI7AkVoP9crRjpOSV/7b5MGa0BcjIyEzTtqM58wppfSQt8jkj7WT3+Bww/Y9rOtshDE2QosaX/7xoDnzyeZ3amLjTe3/MjBcsKlbK2z4QuaI6xoQBVd/QjP8FjXpZBhXWFIAsOL/sz6uR2Er0ovdX8DBA0EJpuzlTX94Lvf+Eh+5/83ESAm97fk4pnhQOCAQUAAoIBAEwSwKuLFPeR7UJGXkWM9egyYewhqHpIXPBEWOVPqwTw3xLc3EkufpYY9wkhJS08KD+J92jMjm//0bYeVf7fXisc6PHtGY4wx5XBm1g9HKw9lwRjbk7nH495dlZdl0BXHa14TJ8myE2zOM1jsaFyz6jAFTaRnKYIj6WlKOj59d2iAXtLZRme9r+7U4G6zDUkphyIEcIGH4vhb6gm3URr1zAV5kJjTlsPAiqgeH/PgxU52tmvLphJgv/xPxsuX5W0/s7iKbphIb2YWh/gtTWXvRQHiQQ2fCncI3TAMnZ75dBY0gPOVLQJhUyffeRbk9UULux/jc8QBPgKBS7GM5DnNSw=")
3802            .expect("DSA SPKI fixture must decode");
3803        let signature = base64::engine::general_purpose::STANDARD
3804            .decode("MD0CHQChtB1c+f5BmTJCtT7Gi4cyQiR2igj0znRQYCJ3Ahw4NGg4pL5jgA8Ri07ESV9Yr90WfUmRrbRcnjsY")
3805            .expect("DSA signature fixture must decode");
3806        let message = b"certificate tbs bytes for dsa q-width regression";
3807
3808        assert!(
3809            rustcrypto_x509::verify_signature(
3810                X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1),
3811                message,
3812                &signature,
3813                &spki,
3814            )
3815            .expect("supported DSA-SHA1 certificate signature")
3816        );
3817
3818        let mut tampered = signature;
3819        *tampered.last_mut().expect("DER signature is non-empty") ^= 1;
3820        assert!(
3821            !rustcrypto_x509::verify_signature(
3822                X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1),
3823                message,
3824                &tampered,
3825                &spki,
3826            )
3827            .expect("tampered DSA-SHA1 certificate signature is a verification miss")
3828        );
3829    }
3830
3831    #[cfg(feature = "xmldsig")]
3832    #[test]
3833    fn primitive_provider_does_not_embed_rsa_strength_policy() {
3834        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3835        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3836        use rsa::{RsaPrivateKey, pss::SigningKey as RsaPssSigningKey};
3837        use sha2::Sha256;
3838        use signature::{RandomizedSigner, SignatureEncoding};
3839
3840        let mut rng = ChaCha20Rng::from_seed([0x3c; 32]);
3841        let private_key =
3842            RsaPrivateKey::new(&mut rng, 1024).expect("deterministic weak RSA key generation");
3843        let public_key = private_key
3844            .to_public_key()
3845            .to_public_key_der()
3846            .expect("weak RSA public key must encode as SPKI");
3847        let signed_data = b"certificate tbs bytes";
3848        let signature = RsaPssSigningKey::<Sha256>::new_with_salt_len(private_key, 32)
3849            .try_sign_with_rng(&mut rng, signed_data)
3850            .expect("weak RSA-PSS key can still produce a cryptographic signature")
3851            .to_vec();
3852
3853        assert!(
3854            RUST_CRYPTO_PROVIDER
3855                .verify_x509_signature(
3856                    X509SignatureAlgorithm::RsaPss {
3857                        digest: DigestAlgorithm::Sha256,
3858                        mgf_digest: DigestAlgorithm::Sha256,
3859                        salt_len: 32,
3860                    },
3861                    signed_data,
3862                    &signature,
3863                    public_key.as_bytes(),
3864                )
3865                .expect(
3866                    "provider must evaluate structurally valid RSA-PSS independently of policy"
3867                )
3868        );
3869    }
3870
3871    #[cfg(feature = "xmldsig")]
3872    #[test]
3873    fn oversized_rsa_pss_salt_is_a_verification_miss() {
3874        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3875        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3876        use rsa::{RsaPrivateKey, traits::PublicKeyParts as _};
3877
3878        // ASN.1 saltLength is attacker-controlled. It must not reach the
3879        // dependency's unchecked hLen + saltLen + 2 arithmetic.
3880        let mut rng = ChaCha20Rng::from_seed([0x55; 32]);
3881        let public_key = RsaPrivateKey::new(&mut rng, 1024)
3882            .expect("deterministic RSA key generation")
3883            .to_public_key();
3884        let spki = public_key
3885            .to_public_key_der()
3886            .expect("RSA public key must encode as SPKI");
3887
3888        assert!(rustcrypto_x509::rsa_pss_salt_fits_key(
3889            &public_key,
3890            DigestAlgorithm::Sha256,
3891            0,
3892        ));
3893        assert!(rustcrypto_x509::rsa_pss_salt_fits_key(
3894            &public_key,
3895            DigestAlgorithm::Sha256,
3896            94,
3897        ));
3898        assert!(!rustcrypto_x509::rsa_pss_salt_fits_key(
3899            &public_key,
3900            DigestAlgorithm::Sha256,
3901            95,
3902        ));
3903
3904        assert!(
3905            !RUST_CRYPTO_PROVIDER
3906                .verify_x509_signature(
3907                    X509SignatureAlgorithm::RsaPss {
3908                        digest: DigestAlgorithm::Sha256,
3909                        mgf_digest: DigestAlgorithm::Sha256,
3910                        salt_len: usize::MAX,
3911                    },
3912                    b"certificate tbs bytes",
3913                    &vec![0_u8; public_key.size()],
3914                    spki.as_bytes(),
3915                )
3916                .expect("oversized PSS salt must fail without panicking")
3917        );
3918    }
3919}