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/// Provider operations used by the XML Security pipelines.
844pub trait CryptoProvider: Send + Sync {
845    /// External execution domain. Different instances of one engine are not interchangeable.
846    fn binding(&self) -> Option<&ProviderBinding> {
847        None
848    }
849
850    /// Recover a candidate without requiring its symmetric value to leave the provider.
851    #[cfg(feature = "xmlenc")]
852    fn recover_content_key(
853        &self,
854        key: &dyn KeyRecoveryKey,
855        parameters: &RsaOaepParameters,
856        _content_algorithm: DataEncryptionAlgorithm,
857        ciphertext: &[u8],
858    ) -> Result<RecoveredContentKey, ProviderError> {
859        self.recover_key(key, parameters, ciphertext)
860            .map(RecoveredContentKey::confirmed)
861    }
862
863    /// Unwrap into an opaque content key. Providers must opt in explicitly.
864    #[cfg(feature = "xmlenc")]
865    fn unwrap_content_key(
866        &self,
867        _key: &dyn KeyUnwrappingKey,
868        algorithm: KeyWrapAlgorithm,
869        _content_algorithm: DataEncryptionAlgorithm,
870        _wrapped: &[u8],
871    ) -> Result<RecoveredContentKey, ProviderError> {
872        Err(ProviderError::Unsupported {
873            operation: ProviderOperation::KeyUnwrap,
874            algorithm: Some(algorithm.uri().into()),
875        })
876    }
877
878    /// Decrypt with an opaque key; capability remains distinct from policy permission.
879    #[cfg(feature = "xmlenc")]
880    fn decrypt_content_key(
881        &self,
882        algorithm: DataEncryptionAlgorithm,
883        _key: &dyn ContentDecryptionKey,
884        _ciphertext: &[u8],
885    ) -> Result<Vec<u8>, ProviderError> {
886        Err(ProviderError::Unsupported {
887            operation: ProviderOperation::Decrypt,
888            algorithm: Some(algorithm.uri().into()),
889        })
890    }
891    /// Stable provider name for diagnostics and capability reporting.
892    fn name(&self) -> &'static str;
893
894    /// Import original PKCS#8 input into this engine without exporting another
895    /// provider's private handle. Policy and input budgets are enforced by the caller.
896    #[cfg(feature = "xmldsig")]
897    fn import_signing_key(
898        &self,
899        algorithm: crate::xmldsig::SignatureAlgorithm,
900        _pkcs8: &[u8],
901    ) -> Result<Box<dyn crate::xmldsig::SigningKey>, crate::xmldsig::SigningKeyError> {
902        Err(ProviderError::Unsupported {
903            operation: ProviderOperation::Sign,
904            algorithm: Some(algorithm.uri().into()),
905        }
906        .into())
907    }
908
909    /// Import original PKCS#8 input into a provider-owned RSA recovery handle.
910    #[cfg(feature = "xmlenc")]
911    fn import_recovery_key(
912        &self,
913        _pkcs8: &[u8],
914    ) -> Result<std::sync::Arc<dyn KeyRecoveryKey>, ProviderError> {
915        Err(ProviderError::Unsupported {
916            operation: ProviderOperation::KeyRecovery,
917            algorithm: None,
918        })
919    }
920
921    /// Return whether this build can dispatch the requested capability.
922    ///
923    /// [`ProviderCapability::Sign`] and [`ProviderCapability::Verify`] describe
924    /// provider dispatch only: the supplied opaque key performs the executable
925    /// algorithm-support check when the operation runs.
926    fn supports(&self, capability: ProviderCapability<'_>) -> bool;
927
928    /// Import a recipient SPKI into the selected engine. The caller reserves
929    /// import work and grants permission; unavailable engines never fall back.
930    #[cfg(feature = "xmldsig")]
931    fn import_encapsulation_key(
932        &self,
933        algorithm: KeyEncapsulationAlgorithm,
934        _spki: &[u8],
935    ) -> Result<std::sync::Arc<dyn KeyEncapsulationKey>, ProviderError> {
936        Err(ProviderError::Unsupported {
937            operation: ProviderOperation::Encapsulate,
938            algorithm: Some(algorithm.uri().into()),
939        })
940    }
941
942    /// Import a recipient PKCS#8 into this engine without exporting another
943    /// provider's private handle. The encoded parameter set must match the request.
944    #[cfg(feature = "xmldsig")]
945    fn import_decapsulation_key(
946        &self,
947        algorithm: KeyEncapsulationAlgorithm,
948        _pkcs8: &[u8],
949    ) -> Result<std::sync::Arc<dyn KeyDecapsulationKey>, ProviderError> {
950        Err(ProviderError::Unsupported {
951            operation: ProviderOperation::Decapsulate,
952            algorithm: Some(algorithm.uri().into()),
953        })
954    }
955
956    /// Fill caller-owned output with cryptographically secure random bytes.
957    fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError>;
958
959    /// Encapsulate through this engine; unsupported engines never delegate to RustCrypto.
960    #[cfg(feature = "xmldsig")]
961    fn encapsulate_key(
962        &self,
963        key: &dyn KeyEncapsulationKey,
964    ) -> Result<EncapsulatedKey, ProviderError> {
965        Err(ProviderError::Unsupported {
966            operation: ProviderOperation::Encapsulate,
967            algorithm: Some(key.algorithm().uri().into()),
968        })
969    }
970
971    /// Decapsulate without revealing implicit-rejection validity.
972    #[cfg(feature = "xmldsig")]
973    fn decapsulate_key(
974        &self,
975        key: &dyn KeyDecapsulationKey,
976        ciphertext: &[u8],
977    ) -> Result<zeroize::Zeroizing<[u8; 32]>, ProviderError> {
978        let _ = ciphertext;
979        Err(ProviderError::Unsupported {
980            operation: ProviderOperation::Decapsulate,
981            algorithm: Some(key.algorithm().uri().into()),
982        })
983    }
984
985    /// Compute a message digest.
986    #[cfg(feature = "xmldsig")]
987    fn digest(&self, algorithm: DigestAlgorithm, data: &[u8]) -> Result<Vec<u8>, ProviderError>;
988
989    /// Sign bytes with an opaque key handle.
990    ///
991    /// Providers that delegate primitive signing to the supplied key must call
992    /// [`crate::xmldsig::SigningKey::sign_with_provider`] so randomized
993    /// primitives consume this provider's randomness.
994    #[cfg(feature = "xmldsig")]
995    fn sign(
996        &self,
997        key: &dyn crate::xmldsig::SigningKey,
998        algorithm: crate::xmldsig::SignatureAlgorithm,
999        data: &[u8],
1000    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError>;
1001
1002    /// Verify bytes with an opaque key handle.
1003    ///
1004    /// The XMLDSig facade validates algorithm- and key-specific signature
1005    /// framing before this provider boundary.
1006    #[cfg(feature = "xmldsig")]
1007    fn verify(
1008        &self,
1009        key: &dyn crate::xmldsig::VerifyingKey,
1010        algorithm: crate::xmldsig::SignatureAlgorithm,
1011        data: &[u8],
1012        signature: &[u8],
1013    ) -> Result<bool, crate::xmldsig::DsigError>;
1014
1015    /// Sign with explicit domain separation; unsupported contexts must not be discarded.
1016    #[cfg(feature = "xmldsig")]
1017    fn sign_with_context(
1018        &self,
1019        key: &dyn crate::xmldsig::SigningKey,
1020        algorithm: crate::xmldsig::SignatureAlgorithm,
1021        context: &crate::xmldsig::SignatureContext,
1022        data: &[u8],
1023    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1024        if !context.as_bytes().is_empty() {
1025            return Err(crate::xmldsig::SigningKeyError::UnsupportedAlgorithm {
1026                uri: algorithm.uri().to_owned(),
1027            });
1028        }
1029        self.sign(key, algorithm, data)
1030    }
1031
1032    /// Verify with explicit domain separation; unsupported contexts fail closed.
1033    #[cfg(feature = "xmldsig")]
1034    fn verify_with_context(
1035        &self,
1036        key: &dyn crate::xmldsig::VerifyingKey,
1037        algorithm: crate::xmldsig::SignatureAlgorithm,
1038        context: &crate::xmldsig::SignatureContext,
1039        data: &[u8],
1040        signature: &[u8],
1041    ) -> Result<bool, crate::xmldsig::DsigError> {
1042        if !context.as_bytes().is_empty() {
1043            return Err(
1044                crate::xmldsig::SignatureVerificationError::UnsupportedAlgorithm {
1045                    uri: algorithm.uri().to_owned(),
1046                }
1047                .into(),
1048            );
1049        }
1050        self.verify(key, algorithm, data, signature)
1051    }
1052
1053    /// Verify once while reporting the actual successful leaf. Providers that
1054    /// cannot identify it retain ordinary verification and report no receipt.
1055    #[cfg(feature = "xmldsig")]
1056    fn verify_with_key_receipt(
1057        &self,
1058        key: &dyn crate::xmldsig::VerifyingKey,
1059        input: SignatureVerificationInput<'_>,
1060        _observer: &mut dyn FnMut(
1061            KeyVerificationEvent<'_>,
1062        ) -> Result<(), crate::xmldsig::DsigError>,
1063    ) -> Result<bool, crate::xmldsig::DsigError> {
1064        self.verify_with_context(
1065            key,
1066            input.algorithm,
1067            input.context,
1068            input.data,
1069            input.signature,
1070        )
1071    }
1072
1073    /// Compare the successful public receipt with original effective PKIX key
1074    /// state. Unknown semantics are Unproven, never a second provider fallback.
1075    #[cfg(feature = "xmldsig")]
1076    fn associate_verification_key(
1077        &self,
1078        _actual: &[u8],
1079        _expected: structured_pkix::path::WorkingKey<'_>,
1080        _charge: &dyn Fn(u64) -> Result<(), structured_asn1::memory::MemoryError>,
1081    ) -> Result<KeyAssociation, crate::xmldsig::DsigError> {
1082        Ok(KeyAssociation::Unproven)
1083    }
1084
1085    /// Verify an X.509 certificate or CRL signature under its issuer SPKI.
1086    #[cfg(feature = "xmldsig")]
1087    fn verify_x509_signature(
1088        &self,
1089        algorithm: X509SignatureAlgorithm,
1090        signed_data: &[u8],
1091        signature: &[u8],
1092        issuer_spki_der: &[u8],
1093    ) -> Result<bool, ProviderError> {
1094        let _ = (signed_data, signature, issuer_spki_der);
1095        Err(ProviderError::Unsupported {
1096            operation: ProviderOperation::VerifyCertificate,
1097            algorithm: algorithm.oid().map(str::to_owned),
1098        })
1099    }
1100
1101    /// Encrypt XMLEnc content bytes, including standard framing.
1102    #[cfg(feature = "xmlenc")]
1103    fn encrypt_data(
1104        &self,
1105        algorithm: DataEncryptionAlgorithm,
1106        key: &[u8],
1107        plaintext: &[u8],
1108    ) -> Result<Vec<u8>, ProviderError>;
1109
1110    /// Decrypt XMLEnc content bytes, including framing validation.
1111    #[cfg(feature = "xmlenc")]
1112    fn decrypt_data(
1113        &self,
1114        algorithm: DataEncryptionAlgorithm,
1115        key: &[u8],
1116        ciphertext: &[u8],
1117    ) -> Result<Vec<u8>, ProviderError>;
1118
1119    /// Encrypt while preserving XML-carried cipher parameters. Providers
1120    /// must implement the profile explicitly, never discard it or fall back.
1121    #[cfg(feature = "xmlenc")]
1122    fn encrypt_data_with_parameters(
1123        &self,
1124        algorithm: DataEncryptionAlgorithm,
1125        key: &[u8],
1126        plaintext: &[u8],
1127        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1128    ) -> Result<Vec<u8>, ProviderError> {
1129        if parameters.is_some() {
1130            return Err(ProviderError::Unsupported {
1131                operation: ProviderOperation::Encrypt,
1132                algorithm: Some(algorithm.uri().into()),
1133            });
1134        }
1135        self.encrypt_data(algorithm, key, plaintext)
1136    }
1137
1138    /// Decrypt with the same explicit parameter contract as encryption.
1139    #[cfg(feature = "xmlenc")]
1140    fn decrypt_data_with_parameters(
1141        &self,
1142        algorithm: DataEncryptionAlgorithm,
1143        key: &[u8],
1144        ciphertext: &[u8],
1145        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1146    ) -> Result<Vec<u8>, ProviderError> {
1147        if parameters.is_some() {
1148            return Err(ProviderError::Unsupported {
1149                operation: ProviderOperation::Decrypt,
1150                algorithm: Some(algorithm.uri().into()),
1151            });
1152        }
1153        self.decrypt_data(algorithm, key, ciphertext)
1154    }
1155
1156    /// Wrap a content key with RFC 3394 AES Key Wrap.
1157    ///
1158    /// Successful output contains the complete RFC 3394 value and is exactly
1159    /// eight bytes longer than `key`. The XMLEnc facade validates that framing
1160    /// before serializing provider output.
1161    #[cfg(feature = "xmlenc")]
1162    fn wrap_key(
1163        &self,
1164        algorithm: KeyWrapAlgorithm,
1165        kek: &[u8],
1166        key: &[u8],
1167    ) -> Result<Vec<u8>, ProviderError>;
1168
1169    /// Unwrap a content key with RFC 3394 AES Key Wrap.
1170    #[cfg(feature = "xmlenc")]
1171    fn unwrap_key(
1172        &self,
1173        algorithm: KeyWrapAlgorithm,
1174        kek: &[u8],
1175        wrapped: &[u8],
1176    ) -> Result<Vec<u8>, ProviderError>;
1177
1178    /// Wrap key bytes using an opaque RSA public-key operation.
1179    #[cfg(feature = "xmlenc")]
1180    fn transport_key(
1181        &self,
1182        key: &dyn KeyTransportKey,
1183        parameters: &RsaOaepParameters,
1184        plaintext: &[u8],
1185    ) -> Result<Vec<u8>, ProviderError>;
1186
1187    /// Recover key bytes using an opaque RSA private-key operation.
1188    #[cfg(feature = "xmlenc")]
1189    fn recover_key(
1190        &self,
1191        key: &dyn KeyRecoveryKey,
1192        parameters: &RsaOaepParameters,
1193        ciphertext: &[u8],
1194    ) -> Result<Vec<u8>, ProviderError>;
1195
1196    /// Transport using RSAES-PKCS1-v1_5; not an OAEP configuration.
1197    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1198    fn transport_pkcs1v15(
1199        &self,
1200        _key: &dyn KeyTransportKey,
1201        _plaintext: &[u8],
1202    ) -> Result<Vec<u8>, ProviderError> {
1203        Err(ProviderError::Unsupported {
1204            operation: ProviderOperation::KeyTransport,
1205            algorithm: Some(
1206                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
1207                    .uri()
1208                    .into(),
1209            ),
1210        })
1211    }
1212
1213    /// Recover a fixed-width content key without exposing padding validity.
1214    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1215    fn recover_pkcs1v15(
1216        &self,
1217        _key: &dyn KeyRecoveryKey,
1218        _ciphertext: &[u8],
1219        _key_len: usize,
1220    ) -> Result<RecoveredContentKey, ProviderError> {
1221        Err(ProviderError::Unsupported {
1222            operation: ProviderOperation::KeyRecovery,
1223            algorithm: Some(
1224                crate::xmlenc::KeyTransportAlgorithm::RsaPkcs1v15
1225                    .uri()
1226                    .into(),
1227            ),
1228        })
1229    }
1230
1231    /// Perform key agreement with an opaque provider-owned private key.
1232    fn agree_key(
1233        &self,
1234        key: &dyn KeyAgreementKey,
1235        parameters: &KeyAgreementParameters<'_>,
1236    ) -> Result<Vec<u8>, ProviderError> {
1237        self.require_capability(ProviderCapability::KeyAgreement(parameters))?;
1238        if let Some(binding) = key.provider_binding()
1239            && !self
1240                .binding()
1241                .is_some_and(|selected| selected.matches(binding))
1242        {
1243            return Err(ProviderError::External(ExternalProviderError::Binding));
1244        }
1245        key.agree(parameters)
1246    }
1247
1248    /// Derive key bytes from caller-owned secret material.
1249    ///
1250    /// Implementations that advertise [`ProviderCapability::Kdf`] must perform
1251    /// the advertised derivation here. This method is required so capability
1252    /// discovery cannot silently inherit a contradictory unsupported default.
1253    fn derive_key(
1254        &self,
1255        parameters: &KdfParameters<'_>,
1256        secret: &[u8],
1257    ) -> Result<Vec<u8>, ProviderError>;
1258
1259    /// Reject an unavailable exact capability without falling back.
1260    fn require_capability(&self, capability: ProviderCapability<'_>) -> Result<(), ProviderError> {
1261        if self.supports(capability) {
1262            Ok(())
1263        } else {
1264            Err(ProviderError::Unsupported {
1265                operation: capability.operation(),
1266                algorithm: capability.algorithm().map(str::to_owned),
1267            })
1268        }
1269    }
1270}
1271
1272/// Pure-Rust provider backed by RustCrypto crates.
1273#[derive(Debug, Clone, Copy, Default)]
1274pub struct RustCryptoProvider;
1275
1276/// Opaque RSA public-key handle for the built-in RustCrypto provider.
1277#[cfg(feature = "xmlenc")]
1278#[derive(Clone)]
1279pub struct RustCryptoRsaPublicKey {
1280    key: rsa::RsaPublicKey,
1281    modulus: Vec<u8>,
1282    exponent: Vec<u8>,
1283}
1284
1285#[cfg(feature = "xmlenc")]
1286impl RustCryptoRsaPublicKey {
1287    /// Wrap an already parsed RustCrypto RSA public key.
1288    #[must_use]
1289    pub fn new(key: rsa::RsaPublicKey) -> Self {
1290        use rsa::traits::PublicKeyParts as _;
1291        let modulus = key.n().to_be_bytes_trimmed_vartime().into_vec();
1292        let exponent = key.e().to_be_bytes_trimmed_vartime().into_vec();
1293        Self {
1294            key,
1295            modulus,
1296            exponent,
1297        }
1298    }
1299}
1300
1301#[cfg(feature = "xmlenc")]
1302impl From<rsa::RsaPublicKey> for RustCryptoRsaPublicKey {
1303    fn from(key: rsa::RsaPublicKey) -> Self {
1304        Self::new(key)
1305    }
1306}
1307
1308#[cfg(feature = "xmlenc")]
1309impl KeyTransportKey for RustCryptoRsaPublicKey {
1310    #[cfg(feature = "legacy-algorithms")]
1311    fn transport_pkcs1v15(
1312        &self,
1313        provider: &dyn CryptoProvider,
1314        plaintext: &[u8],
1315    ) -> Result<Vec<u8>, ProviderError> {
1316        rustcrypto::transport_pkcs1v15(provider, &self.key, plaintext)
1317    }
1318    fn rsa_modulus(&self) -> Cow<'_, [u8]> {
1319        Cow::Borrowed(&self.modulus)
1320    }
1321
1322    fn rsa_exponent(&self) -> Cow<'_, [u8]> {
1323        Cow::Borrowed(&self.exponent)
1324    }
1325
1326    fn transport_with_provider(
1327        &self,
1328        provider: &dyn CryptoProvider,
1329        parameters: &RsaOaepParameters,
1330        plaintext: &[u8],
1331    ) -> Result<Vec<u8>, ProviderError> {
1332        rustcrypto::transport_key(provider, &self.key, parameters, plaintext)
1333    }
1334}
1335
1336#[cfg(feature = "xmlenc")]
1337impl KeyTransportKey for rsa::RsaPublicKey {
1338    #[cfg(feature = "legacy-algorithms")]
1339    fn transport_pkcs1v15(
1340        &self,
1341        provider: &dyn CryptoProvider,
1342        plaintext: &[u8],
1343    ) -> Result<Vec<u8>, ProviderError> {
1344        rustcrypto::transport_pkcs1v15(provider, self, plaintext)
1345    }
1346    fn rsa_modulus(&self) -> Cow<'_, [u8]> {
1347        use rsa::traits::PublicKeyParts as _;
1348        Cow::Owned(self.n().to_be_bytes_trimmed_vartime().into_vec())
1349    }
1350
1351    fn rsa_exponent(&self) -> Cow<'_, [u8]> {
1352        use rsa::traits::PublicKeyParts as _;
1353        Cow::Owned(self.e().to_be_bytes_trimmed_vartime().into_vec())
1354    }
1355
1356    fn transport_with_provider(
1357        &self,
1358        provider: &dyn CryptoProvider,
1359        parameters: &RsaOaepParameters,
1360        plaintext: &[u8],
1361    ) -> Result<Vec<u8>, ProviderError> {
1362        rustcrypto::transport_key(provider, self, parameters, plaintext)
1363    }
1364}
1365
1366/// Opaque RSA private-key handle for the built-in RustCrypto provider.
1367#[cfg(feature = "xmlenc")]
1368#[derive(Clone)]
1369pub struct RustCryptoRsaPrivateKey {
1370    key: rsa::RsaPrivateKey,
1371    ciphertext_len: usize,
1372}
1373
1374#[cfg(feature = "xmlenc")]
1375impl RustCryptoRsaPrivateKey {
1376    /// Wrap an already parsed RustCrypto RSA private key.
1377    #[must_use]
1378    pub fn new(key: rsa::RsaPrivateKey) -> Self {
1379        use rsa::traits::PublicKeyParts as _;
1380        let ciphertext_len = key.size();
1381        Self {
1382            key,
1383            ciphertext_len,
1384        }
1385    }
1386}
1387
1388#[cfg(feature = "xmlenc")]
1389impl From<rsa::RsaPrivateKey> for RustCryptoRsaPrivateKey {
1390    fn from(key: rsa::RsaPrivateKey) -> Self {
1391        Self::new(key)
1392    }
1393}
1394
1395#[cfg(feature = "xmlenc")]
1396impl KeyRecoveryKey for RustCryptoRsaPrivateKey {
1397    #[cfg(feature = "legacy-algorithms")]
1398    fn recover_pkcs1v15(
1399        &self,
1400        provider: &dyn CryptoProvider,
1401        ciphertext: &[u8],
1402        key_len: usize,
1403    ) -> Result<RecoveredContentKey, ProviderError> {
1404        rsa_pkcs1v15::recover(provider, &self.key, ciphertext, key_len)
1405    }
1406    fn rsa_modulus_bits(&self) -> usize {
1407        KeyRecoveryKey::rsa_modulus_bits(&self.key)
1408    }
1409    fn rsa_public_exponent(&self) -> Option<u64> {
1410        KeyRecoveryKey::rsa_public_exponent(&self.key)
1411    }
1412    fn ciphertext_len(&self) -> usize {
1413        self.ciphertext_len
1414    }
1415
1416    fn recover_with_provider(
1417        &self,
1418        provider: &dyn CryptoProvider,
1419        parameters: &RsaOaepParameters,
1420        ciphertext: &[u8],
1421    ) -> Result<Vec<u8>, ProviderError> {
1422        rustcrypto::recover_key(provider, &self.key, parameters, ciphertext)
1423    }
1424}
1425
1426#[cfg(feature = "xmlenc")]
1427impl KeyRecoveryKey for rsa::RsaPrivateKey {
1428    #[cfg(feature = "legacy-algorithms")]
1429    fn recover_pkcs1v15(
1430        &self,
1431        provider: &dyn CryptoProvider,
1432        ciphertext: &[u8],
1433        key_len: usize,
1434    ) -> Result<RecoveredContentKey, ProviderError> {
1435        rsa_pkcs1v15::recover(provider, self, ciphertext, key_len)
1436    }
1437    fn rsa_modulus_bits(&self) -> usize {
1438        use rsa::traits::PublicKeyParts as _;
1439        self.n().bits_vartime() as usize
1440    }
1441    fn rsa_public_exponent(&self) -> Option<u64> {
1442        use rsa::traits::PublicKeyParts as _;
1443        if self.e().bits_vartime() > 64 {
1444            return None;
1445        }
1446        let mut exponent = 0_u64;
1447        let word_bits = crypto_bigint::Word::BITS as usize;
1448        for (index, word) in self.e().as_words().iter().take(64 / word_bits).enumerate() {
1449            #[cfg(target_pointer_width = "64")]
1450            let word = *word;
1451            #[cfg(target_pointer_width = "32")]
1452            let word = u64::from(*word);
1453            exponent |= word << (index * word_bits);
1454        }
1455        Some(exponent)
1456    }
1457    fn ciphertext_len(&self) -> usize {
1458        use rsa::traits::PublicKeyParts as _;
1459        self.size()
1460    }
1461
1462    fn recover_with_provider(
1463        &self,
1464        provider: &dyn CryptoProvider,
1465        parameters: &RsaOaepParameters,
1466        ciphertext: &[u8],
1467    ) -> Result<Vec<u8>, ProviderError> {
1468        rustcrypto::recover_key(provider, self, parameters, ciphertext)
1469    }
1470}
1471
1472/// Process-wide immutable default provider. It contains no mutable state or keys.
1473pub static RUST_CRYPTO_PROVIDER: RustCryptoProvider = RustCryptoProvider;
1474
1475/// Borrow the pure-Rust default provider.
1476#[must_use]
1477pub fn default_provider() -> &'static dyn CryptoProvider {
1478    &RUST_CRYPTO_PROVIDER
1479}
1480
1481/// Adapter used when a RustCrypto primitive requires a fallible RNG object.
1482#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1483pub(crate) struct ProviderRng<'a>(pub(crate) &'a dyn CryptoProvider);
1484
1485#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1486impl TryRng for ProviderRng<'_> {
1487    type Error = ProviderError;
1488
1489    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
1490        let mut bytes = [0_u8; 4];
1491        self.try_fill_bytes(&mut bytes)?;
1492        Ok(u32::from_le_bytes(bytes))
1493    }
1494
1495    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
1496        let mut bytes = [0_u8; 8];
1497        self.try_fill_bytes(&mut bytes)?;
1498        Ok(u64::from_le_bytes(bytes))
1499    }
1500
1501    fn try_fill_bytes(&mut self, output: &mut [u8]) -> Result<(), Self::Error> {
1502        self.0.fill_random(output)
1503    }
1504}
1505
1506#[cfg(any(feature = "xmldsig", feature = "xmlenc"))]
1507impl TryCryptoRng for ProviderRng<'_> {}
1508
1509#[cfg(feature = "xmldsig")]
1510impl RustCryptoProvider {
1511    fn verify_context_leaf(
1512        key: &dyn crate::xmldsig::VerifyingKey,
1513        algorithm: crate::xmldsig::SignatureAlgorithm,
1514        context: &crate::xmldsig::SignatureContext,
1515        data: &[u8],
1516        signature: &[u8],
1517    ) -> Result<bool, crate::xmldsig::DsigError> {
1518        if key.provider_binding().is_some() {
1519            return Err(ProviderError::External(ExternalProviderError::Binding).into());
1520        }
1521        key.verify_with_context(algorithm, context, data, signature)
1522    }
1523}
1524
1525impl CryptoProvider for RustCryptoProvider {
1526    #[cfg(feature = "xmldsig")]
1527    fn verify_with_key_receipt(
1528        &self,
1529        key: &dyn crate::xmldsig::VerifyingKey,
1530        input: SignatureVerificationInput<'_>,
1531        observer: &mut dyn FnMut(KeyVerificationEvent<'_>) -> Result<(), crate::xmldsig::DsigError>,
1532    ) -> Result<bool, crate::xmldsig::DsigError> {
1533        self.require_capability(ProviderCapability::Verify(input.algorithm))?;
1534        key_association::observed_leaf(key, observer, &|key| {
1535            Self::verify_context_leaf(
1536                key,
1537                input.algorithm,
1538                input.context,
1539                input.data,
1540                input.signature,
1541            )
1542        })
1543    }
1544
1545    #[cfg(feature = "xmldsig")]
1546    fn associate_verification_key(
1547        &self,
1548        actual: &[u8],
1549        expected: structured_pkix::path::WorkingKey<'_>,
1550        charge: &dyn Fn(u64) -> Result<(), structured_asn1::memory::MemoryError>,
1551    ) -> Result<KeyAssociation, crate::xmldsig::DsigError> {
1552        key_association::compare_rustcrypto(actual, expected, charge)
1553    }
1554    #[cfg(feature = "xmldsig")]
1555    fn encapsulate_key(
1556        &self,
1557        key: &dyn KeyEncapsulationKey,
1558    ) -> Result<EncapsulatedKey, ProviderError> {
1559        self.require_capability(ProviderCapability::Encapsulate(key.algorithm()))?;
1560        if key.provider_binding().is_some() {
1561            return Err(ProviderError::External(ExternalProviderError::Binding));
1562        }
1563        key.encapsulate_with_provider(self)
1564    }
1565
1566    #[cfg(feature = "xmldsig")]
1567    fn decapsulate_key(
1568        &self,
1569        key: &dyn KeyDecapsulationKey,
1570        ciphertext: &[u8],
1571    ) -> Result<zeroize::Zeroizing<[u8; 32]>, ProviderError> {
1572        self.require_capability(ProviderCapability::Decapsulate(key.algorithm()))?;
1573        if key.provider_binding().is_some() {
1574            return Err(ProviderError::External(ExternalProviderError::Binding));
1575        }
1576        if ciphertext.len() != key.algorithm().ciphertext_len() {
1577            return Err(ProviderError::InvalidInput(
1578                ProviderInputError::MlKemCiphertext,
1579            ));
1580        }
1581        key.decapsulate(ciphertext)
1582    }
1583    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1584    fn transport_pkcs1v15(
1585        &self,
1586        key: &dyn KeyTransportKey,
1587        plaintext: &[u8],
1588    ) -> Result<Vec<u8>, ProviderError> {
1589        self.require_capability(ProviderCapability::Pkcs1v15Transport)?;
1590        key.transport_pkcs1v15(self, plaintext)
1591    }
1592
1593    #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1594    fn recover_pkcs1v15(
1595        &self,
1596        key: &dyn KeyRecoveryKey,
1597        ciphertext: &[u8],
1598        key_len: usize,
1599    ) -> Result<RecoveredContentKey, ProviderError> {
1600        self.require_capability(ProviderCapability::Pkcs1v15Recovery)?;
1601        key.recover_pkcs1v15(self, ciphertext, key_len)
1602    }
1603    fn name(&self) -> &'static str {
1604        "rustcrypto"
1605    }
1606
1607    #[cfg(feature = "experimental-pq")]
1608    fn import_encapsulation_key(
1609        &self,
1610        algorithm: KeyEncapsulationAlgorithm,
1611        spki: &[u8],
1612    ) -> Result<std::sync::Arc<dyn KeyEncapsulationKey>, ProviderError> {
1613        self.require_capability(ProviderCapability::Encapsulate(algorithm))?;
1614        let key = RustCryptoMlKemPublicKey::from_spki_der_with_algorithm(spki, Some(algorithm))?;
1615        Ok(std::sync::Arc::new(key))
1616    }
1617
1618    #[cfg(feature = "experimental-pq")]
1619    fn import_decapsulation_key(
1620        &self,
1621        algorithm: KeyEncapsulationAlgorithm,
1622        pkcs8: &[u8],
1623    ) -> Result<std::sync::Arc<dyn KeyDecapsulationKey>, ProviderError> {
1624        self.require_capability(ProviderCapability::Decapsulate(algorithm))?;
1625        let key = RustCryptoMlKemPrivateKey::from_pkcs8_der_with_algorithm(pkcs8, Some(algorithm))?;
1626        Ok(std::sync::Arc::new(key))
1627    }
1628
1629    #[cfg(feature = "xmlenc")]
1630    fn import_recovery_key(
1631        &self,
1632        pkcs8: &[u8],
1633    ) -> Result<std::sync::Arc<dyn KeyRecoveryKey>, ProviderError> {
1634        use crate::rsa_encoding::RsaPrivateKeyEncoding as _;
1635        let key = rsa::RsaPrivateKey::from_pkcs8_der(pkcs8).map_err(|_| {
1636            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
1637                "RSA recovery key",
1638            ))
1639        })?;
1640        Ok(std::sync::Arc::new(RustCryptoRsaPrivateKey::new(key)))
1641    }
1642
1643    #[cfg(feature = "xmldsig")]
1644    fn import_signing_key(
1645        &self,
1646        algorithm: crate::xmldsig::SignatureAlgorithm,
1647        der: &[u8],
1648    ) -> Result<Box<dyn crate::xmldsig::SigningKey>, crate::xmldsig::SigningKeyError> {
1649        use crate::xmldsig::{SignatureAlgorithm as A, *};
1650        Ok(match algorithm {
1651            #[cfg(feature = "experimental-pq")]
1652            A::PostQuantum(parameter) => {
1653                Box::new(PostQuantumSigningKey::from_pkcs8_der(parameter, der)?)
1654            }
1655            A::Ed25519 | A::Ed25519Ctx | A::Ed25519Ph | A::Ed448 | A::Ed448Ph => {
1656                Box::new(EdDsaSigningKey::from_pkcs8_der(algorithm, der)?)
1657            }
1658            method if method.is_rsa() => Box::new(RsaSigningKey::from_pkcs8_der(der)?),
1659            A::DsaSha1 | A::DsaSha256 => Box::new(DsaSigningKey::from_pkcs8_der(der)?),
1660            method if method.ecdsa_digest().is_some() => {
1661                if let Ok(key) = EcdsaP256SigningKey::from_pkcs8_der(der) {
1662                    Box::new(key)
1663                } else if let Ok(key) = EcdsaP384SigningKey::from_pkcs8_der(der) {
1664                    Box::new(key)
1665                } else {
1666                    Box::new(EcdsaP521SigningKey::from_pkcs8_der(der)?)
1667                }
1668            }
1669            _ => {
1670                return Err(SigningKeyError::UnsupportedAlgorithm {
1671                    uri: algorithm.uri().into(),
1672                });
1673            }
1674        })
1675    }
1676
1677    fn supports(&self, capability: ProviderCapability<'_>) -> bool {
1678        match capability {
1679            #[cfg(feature = "xmldsig")]
1680            ProviderCapability::Encapsulate(_) | ProviderCapability::Decapsulate(_) => {
1681                cfg!(feature = "experimental-pq")
1682            }
1683            #[cfg(feature = "xmldsig")]
1684            ProviderCapability::Digest(_) => true,
1685            #[cfg(feature = "xmldsig")]
1686            // Opaque keys own these primitives and reject unsupported methods
1687            // during dispatch; the provider advertises its dispatch surface.
1688            ProviderCapability::Sign(algorithm) | ProviderCapability::Verify(algorithm) => {
1689                if let Some(parameters) = algorithm.rsa_pss_parameters()
1690                    && parameters.salt_len > i32::MAX as usize
1691                {
1692                    return false;
1693                }
1694                !matches!(
1695                    algorithm,
1696                    crate::xmldsig::SignatureAlgorithm::PostQuantum(_)
1697                ) || cfg!(feature = "experimental-pq")
1698            }
1699            #[cfg(feature = "xmldsig")]
1700            ProviderCapability::VerifyCertificate(algorithm) => {
1701                is_supported_x509_signature(algorithm)
1702            }
1703            #[cfg(feature = "xmlenc")]
1704            ProviderCapability::Encrypt(_) | ProviderCapability::Decrypt(_) => true,
1705            #[cfg(feature = "xmlenc")]
1706            ProviderCapability::KeyWrap(algorithm) | ProviderCapability::KeyUnwrap(algorithm) => {
1707                algorithm.wrapped_len(16).is_some()
1708            }
1709            #[cfg(feature = "xmlenc")]
1710            ProviderCapability::KeyTransport(parameters)
1711            | ProviderCapability::KeyRecovery(parameters) => {
1712                !parameters.algorithm.requires_explicit_permission()
1713                    && parameters.mgf_digest.mgf_uri().is_some()
1714            }
1715            #[cfg(all(feature = "xmlenc", feature = "legacy-algorithms"))]
1716            ProviderCapability::Pkcs1v15Transport | ProviderCapability::Pkcs1v15Recovery => true,
1717            ProviderCapability::Random => true,
1718            ProviderCapability::KeyAgreement(parameters) => {
1719                #[cfg(feature = "xmlenc")]
1720                {
1721                    matches!(
1722                        parameters.algorithm,
1723                        key_establishment::X25519_URI
1724                            | key_establishment::X448_URI
1725                            | key_establishment::ECDH_URI
1726                            | key_establishment::DH_ES_URI
1727                            | key_establishment::DH_URI
1728                    )
1729                }
1730                #[cfg(not(feature = "xmlenc"))]
1731                {
1732                    let _ = parameters;
1733                    false
1734                }
1735            }
1736            ProviderCapability::Kdf(parameters) => {
1737                #[cfg(feature = "xmlenc")]
1738                {
1739                    key_establishment::supports_kdf(parameters)
1740                }
1741                #[cfg(not(feature = "xmlenc"))]
1742                {
1743                    let _ = parameters;
1744                    false
1745                }
1746            }
1747        }
1748    }
1749
1750    fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError> {
1751        SysRng
1752            .try_fill_bytes(output)
1753            .map_err(|error| ProviderError::Random(error.to_string()))
1754    }
1755
1756    fn derive_key(
1757        &self,
1758        parameters: &KdfParameters<'_>,
1759        secret: &[u8],
1760    ) -> Result<Vec<u8>, ProviderError> {
1761        self.require_capability(ProviderCapability::Kdf(parameters))?;
1762        #[cfg(feature = "xmlenc")]
1763        {
1764            key_establishment::derive_key(parameters, secret)
1765        }
1766        #[cfg(not(feature = "xmlenc"))]
1767        {
1768            let _ = secret;
1769            Err(ProviderError::Unsupported {
1770                operation: ProviderOperation::Kdf,
1771                algorithm: Some(parameters.algorithm.to_owned()),
1772            })
1773        }
1774    }
1775
1776    #[cfg(feature = "xmldsig")]
1777    fn digest(&self, algorithm: DigestAlgorithm, data: &[u8]) -> Result<Vec<u8>, ProviderError> {
1778        use sha1::Sha1;
1779        use sha2::{Digest, Sha224, Sha256, Sha384, Sha512};
1780        use sha3::{Sha3_224, Sha3_256, Sha3_384, Sha3_512};
1781        Ok(match algorithm {
1782            #[cfg(feature = "legacy-algorithms")]
1783            DigestAlgorithm::Md5 => md5::Md5::digest(data).to_vec(),
1784            #[cfg(feature = "legacy-algorithms")]
1785            DigestAlgorithm::Ripemd160 => ripemd::Ripemd160::digest(data).to_vec(),
1786            DigestAlgorithm::Sha1 => Sha1::digest(data).to_vec(),
1787            DigestAlgorithm::Sha224 => Sha224::digest(data).to_vec(),
1788            DigestAlgorithm::Sha256 => Sha256::digest(data).to_vec(),
1789            DigestAlgorithm::Sha384 => Sha384::digest(data).to_vec(),
1790            DigestAlgorithm::Sha512 => Sha512::digest(data).to_vec(),
1791            DigestAlgorithm::Sha3_224 => Sha3_224::digest(data).to_vec(),
1792            DigestAlgorithm::Sha3_256 => Sha3_256::digest(data).to_vec(),
1793            DigestAlgorithm::Sha3_384 => Sha3_384::digest(data).to_vec(),
1794            DigestAlgorithm::Sha3_512 => Sha3_512::digest(data).to_vec(),
1795        })
1796    }
1797
1798    #[cfg(feature = "xmldsig")]
1799    fn sign(
1800        &self,
1801        key: &dyn crate::xmldsig::SigningKey,
1802        algorithm: crate::xmldsig::SignatureAlgorithm,
1803        data: &[u8],
1804    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1805        self.require_capability(ProviderCapability::Sign(algorithm))?;
1806        key.sign_with_provider(self, algorithm, data)
1807    }
1808
1809    #[cfg(feature = "xmldsig")]
1810    fn sign_with_context(
1811        &self,
1812        key: &dyn crate::xmldsig::SigningKey,
1813        algorithm: crate::xmldsig::SignatureAlgorithm,
1814        context: &crate::xmldsig::SignatureContext,
1815        data: &[u8],
1816    ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
1817        self.require_capability(ProviderCapability::Sign(algorithm))?;
1818        key.sign_with_provider_context(self, algorithm, context, data)
1819    }
1820
1821    #[cfg(feature = "xmldsig")]
1822    fn verify_with_context(
1823        &self,
1824        key: &dyn crate::xmldsig::VerifyingKey,
1825        algorithm: crate::xmldsig::SignatureAlgorithm,
1826        context: &crate::xmldsig::SignatureContext,
1827        data: &[u8],
1828        signature: &[u8],
1829    ) -> Result<bool, crate::xmldsig::DsigError> {
1830        self.require_capability(ProviderCapability::Verify(algorithm))?;
1831        key_association::observed_leaf(key, &mut |_| Ok(()), &|key| {
1832            Self::verify_context_leaf(key, algorithm, context, data, signature)
1833        })
1834    }
1835
1836    #[cfg(feature = "xmldsig")]
1837    fn verify(
1838        &self,
1839        key: &dyn crate::xmldsig::VerifyingKey,
1840        algorithm: crate::xmldsig::SignatureAlgorithm,
1841        data: &[u8],
1842        signature: &[u8],
1843    ) -> Result<bool, crate::xmldsig::DsigError> {
1844        self.require_capability(ProviderCapability::Verify(algorithm))?;
1845        key_association::observed_leaf(key, &mut |_| Ok(()), &|key| {
1846            if key.provider_binding().is_some() {
1847                return Err(ProviderError::External(ExternalProviderError::Binding).into());
1848            }
1849            key.verify(algorithm, data, signature)
1850        })
1851    }
1852
1853    #[cfg(feature = "xmldsig")]
1854    fn verify_x509_signature(
1855        &self,
1856        algorithm: X509SignatureAlgorithm,
1857        signed_data: &[u8],
1858        signature: &[u8],
1859        issuer_spki_der: &[u8],
1860    ) -> Result<bool, ProviderError> {
1861        rustcrypto_x509::verify_signature(algorithm, signed_data, signature, issuer_spki_der)
1862    }
1863
1864    #[cfg(feature = "xmlenc")]
1865    fn encrypt_data(
1866        &self,
1867        algorithm: DataEncryptionAlgorithm,
1868        key: &[u8],
1869        plaintext: &[u8],
1870    ) -> Result<Vec<u8>, ProviderError> {
1871        rustcrypto::encrypt_data(self, algorithm, key, plaintext)
1872    }
1873
1874    #[cfg(feature = "xmlenc")]
1875    fn decrypt_data(
1876        &self,
1877        algorithm: DataEncryptionAlgorithm,
1878        key: &[u8],
1879        ciphertext: &[u8],
1880    ) -> Result<Vec<u8>, ProviderError> {
1881        rustcrypto::decrypt_data(algorithm, key, ciphertext)
1882    }
1883
1884    #[cfg(feature = "xmlenc")]
1885    fn wrap_key(
1886        &self,
1887        algorithm: KeyWrapAlgorithm,
1888        kek: &[u8],
1889        key: &[u8],
1890    ) -> Result<Vec<u8>, ProviderError> {
1891        rustcrypto::wrap_key(self, algorithm, kek, key)
1892    }
1893
1894    #[cfg(feature = "xmlenc")]
1895    fn encrypt_data_with_parameters(
1896        &self,
1897        algorithm: DataEncryptionAlgorithm,
1898        key: &[u8],
1899        plaintext: &[u8],
1900        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1901    ) -> Result<Vec<u8>, ProviderError> {
1902        match parameters {
1903            Some(parameters) => chacha::encrypt(algorithm, key, plaintext, parameters),
1904            None => self.encrypt_data(algorithm, key, plaintext),
1905        }
1906    }
1907
1908    #[cfg(feature = "xmlenc")]
1909    fn decrypt_data_with_parameters(
1910        &self,
1911        algorithm: DataEncryptionAlgorithm,
1912        key: &[u8],
1913        ciphertext: &[u8],
1914        parameters: Option<crate::xmlenc::ChaChaParametersRef<'_>>,
1915    ) -> Result<Vec<u8>, ProviderError> {
1916        match parameters {
1917            Some(parameters) => chacha::decrypt(algorithm, key, ciphertext, parameters),
1918            None => self.decrypt_data(algorithm, key, ciphertext),
1919        }
1920    }
1921
1922    #[cfg(feature = "xmlenc")]
1923    fn unwrap_key(
1924        &self,
1925        algorithm: KeyWrapAlgorithm,
1926        kek: &[u8],
1927        wrapped: &[u8],
1928    ) -> Result<Vec<u8>, ProviderError> {
1929        rustcrypto::unwrap_key(algorithm, kek, wrapped)
1930    }
1931
1932    #[cfg(feature = "xmlenc")]
1933    fn transport_key(
1934        &self,
1935        key: &dyn KeyTransportKey,
1936        parameters: &RsaOaepParameters,
1937        plaintext: &[u8],
1938    ) -> Result<Vec<u8>, ProviderError> {
1939        self.require_capability(ProviderCapability::KeyTransport(parameters))?;
1940        key.transport_with_provider(self, parameters, plaintext)
1941    }
1942
1943    #[cfg(feature = "xmlenc")]
1944    fn recover_key(
1945        &self,
1946        key: &dyn KeyRecoveryKey,
1947        parameters: &RsaOaepParameters,
1948        ciphertext: &[u8],
1949    ) -> Result<Vec<u8>, ProviderError> {
1950        self.require_capability(ProviderCapability::KeyRecovery(parameters))?;
1951        key.recover_with_provider(self, parameters, ciphertext)
1952    }
1953}
1954
1955#[cfg(feature = "xmldsig")]
1956fn is_supported_x509_signature(algorithm: X509SignatureAlgorithm) -> bool {
1957    match algorithm {
1958        X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1)
1959        | X509SignatureAlgorithm::Ed25519
1960        | X509SignatureAlgorithm::Ed448 => true,
1961        X509SignatureAlgorithm::Ecdsa(digest) => rustcrypto_x509::ecdsa_algorithm(digest).is_some(),
1962        X509SignatureAlgorithm::PostQuantum(_) => cfg!(feature = "experimental-pq"),
1963        X509SignatureAlgorithm::RsaPkcs1v15(digest) => matches!(
1964            digest,
1965            DigestAlgorithm::Sha1
1966                | DigestAlgorithm::Sha224
1967                | DigestAlgorithm::Sha256
1968                | DigestAlgorithm::Sha384
1969                | DigestAlgorithm::Sha512
1970        ),
1971        X509SignatureAlgorithm::RsaPss {
1972            digest, mgf_digest, ..
1973        } => {
1974            matches!(
1975                digest,
1976                DigestAlgorithm::Sha256 | DigestAlgorithm::Sha384 | DigestAlgorithm::Sha512
1977            ) && digest == mgf_digest
1978        }
1979        X509SignatureAlgorithm::Dsa(_) => false,
1980    }
1981}
1982
1983#[cfg(feature = "xmldsig")]
1984pub(crate) mod rustcrypto_x509 {
1985    use crate::rsa_encoding::RsaPublicKeyEncoding as _;
1986    use der::Decode as _;
1987    use dsa::pkcs8::DecodePublicKey as _;
1988    use rsa::{
1989        RsaPublicKey,
1990        pss::{Signature as RsaPssSignature, VerifyingKey as RsaPssVerifyingKey},
1991        traits::PublicKeyParts as _,
1992    };
1993    use sha1::Digest as _;
1994    use sha2::{Sha256, Sha384, Sha512};
1995    use signature::{Verifier as _, hazmat::PrehashVerifier as _};
1996    use x509_parser::prelude::FromDer as _;
1997
1998    use super::{ProviderError, X509SignatureAlgorithm};
1999    use crate::xmldsig::signature::verify_ecdsa_signature_spki_asn1_der;
2000    use crate::xmldsig::{
2001        DigestAlgorithm, DsigError, SignatureAlgorithm, VerificationKey, VerifyingKey as _,
2002    };
2003
2004    pub(super) fn verify_signature(
2005        algorithm: X509SignatureAlgorithm,
2006        signed_data: &[u8],
2007        signature: &[u8],
2008        issuer_spki_der: &[u8],
2009    ) -> Result<bool, ProviderError> {
2010        match algorithm {
2011            X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1) => {
2012                // Certificate signatures are ASN.1 DER integers sized by the
2013                // issuer's q parameter. XMLDSig's fixed 20-byte r||s framing
2014                // applies only to SignatureValue, never to X.509 signatures.
2015                let Ok(key) = crate::xmldsig::signature::decode_dsa_verifying_key(issuer_spki_der)
2016                else {
2017                    return Ok(false);
2018                };
2019                let Ok(signature) = dsa::Signature::from_der(signature) else {
2020                    return Ok(false);
2021                };
2022                let digest = sha1::Sha1::digest(signed_data);
2023                Ok(key.verify_prehash(&digest, &signature).is_ok())
2024            }
2025            X509SignatureAlgorithm::RsaPkcs1v15(digest) => {
2026                let Some(algorithm) = rsa_pkcs1_algorithm(digest) else {
2027                    return unsupported(X509SignatureAlgorithm::RsaPkcs1v15(digest));
2028                };
2029                verify_xml_signature(algorithm, signed_data, signature, issuer_spki_der)
2030            }
2031            X509SignatureAlgorithm::Ecdsa(digest) => {
2032                let Some(algorithm) = ecdsa_algorithm(digest) else {
2033                    return unsupported(X509SignatureAlgorithm::Ecdsa(digest));
2034                };
2035                // RFC 5280 ECDSA certificate signatures are always ASN.1 DER;
2036                // XMLDSig's SignatureValue framing policy is irrelevant here.
2037                match verify_ecdsa_signature_spki_asn1_der(
2038                    algorithm,
2039                    issuer_spki_der,
2040                    signed_data,
2041                    signature,
2042                ) {
2043                    Ok(verified) => Ok(verified),
2044                    Err(_) => Ok(false),
2045                }
2046            }
2047            X509SignatureAlgorithm::RsaPss {
2048                digest,
2049                mgf_digest,
2050                salt_len,
2051            } => {
2052                // RFC 4055 key restrictions are part of signature validity. Check
2053                // them before provider capability so an incompatible key is a
2054                // deterministic non-match even when the requested MGF is unsupported.
2055                let Some(key) = compatible_rsa_pss_public_key_from_spki(issuer_spki_der, algorithm)
2056                else {
2057                    return Ok(false);
2058                };
2059                if digest != mgf_digest {
2060                    return unsupported(algorithm);
2061                }
2062                verify_rsa_pss(digest, salt_len, signed_data, signature, key)
2063            }
2064            X509SignatureAlgorithm::Ed25519 => {
2065                let Ok(key) = ed25519_dalek::VerifyingKey::from_public_key_der(issuer_spki_der)
2066                else {
2067                    return Ok(false);
2068                };
2069                let Ok(signature) = ed25519_dalek::Signature::try_from(signature) else {
2070                    return Ok(false);
2071                };
2072                Ok(key.verify_strict(signed_data, &signature).is_ok())
2073            }
2074            X509SignatureAlgorithm::Ed448 => {
2075                // RFC 8410 sections 3 and 6 select pure Ed448 without context
2076                // or external prehash: https://www.rfc-editor.org/rfc/rfc8410.html#section-6
2077                let Ok(key) = ed448_goldilocks::VerifyingKey::from_public_key_der(issuer_spki_der)
2078                else {
2079                    return Ok(false);
2080                };
2081                let Ok(signature) = ed448_goldilocks::Signature::try_from(signature) else {
2082                    return Ok(false);
2083                };
2084                Ok(key.verify_raw(&signature, signed_data).is_ok())
2085            }
2086            #[cfg(feature = "experimental-pq")]
2087            X509SignatureAlgorithm::PostQuantum(algorithm) => {
2088                // RFC 9881 ยง3 and RFC 9909 ยงยง1, 4 require pure signatures
2089                // over the DER object with an empty context, not an XML context.
2090                // https://www.rfc-editor.org/rfc/rfc9881.html#section-3
2091                // https://www.rfc-editor.org/rfc/rfc9909.html#section-4
2092                Ok(crate::xmldsig::post_quantum::verify(
2093                    algorithm,
2094                    issuer_spki_der,
2095                    &crate::xmldsig::SignatureContext::default(),
2096                    signed_data,
2097                    signature,
2098                )
2099                .unwrap_or(false))
2100            }
2101            _ => unsupported(algorithm),
2102        }
2103    }
2104
2105    fn verify_xml_signature(
2106        algorithm: SignatureAlgorithm,
2107        signed_data: &[u8],
2108        signature: &[u8],
2109        issuer_spki_der: &[u8],
2110    ) -> Result<bool, ProviderError> {
2111        let key = VerificationKey {
2112            algorithm,
2113            public_key_bytes: issuer_spki_der.to_vec(),
2114            certificate_der: None,
2115            name: None,
2116        };
2117        match key.verify(algorithm, signed_data, signature) {
2118            Ok(verified) => Ok(verified),
2119            Err(DsigError::Provider(error)) => Err(error),
2120            Err(_) => Ok(false),
2121        }
2122    }
2123
2124    fn verify_rsa_pss(
2125        digest: DigestAlgorithm,
2126        salt_len: usize,
2127        signed_data: &[u8],
2128        signature: &[u8],
2129        key: RsaPublicKey,
2130    ) -> Result<bool, ProviderError> {
2131        if !rsa_pss_salt_fits_key(&key, digest, salt_len) {
2132            return Ok(false);
2133        }
2134        let Ok(signature) = RsaPssSignature::try_from(signature) else {
2135            return Ok(false);
2136        };
2137        let verified = match digest {
2138            DigestAlgorithm::Sha256 => {
2139                RsaPssVerifyingKey::<Sha256>::new_with_salt_len(key, salt_len)
2140                    .verify(signed_data, &signature)
2141            }
2142            DigestAlgorithm::Sha384 => {
2143                RsaPssVerifyingKey::<Sha384>::new_with_salt_len(key, salt_len)
2144                    .verify(signed_data, &signature)
2145            }
2146            DigestAlgorithm::Sha512 => {
2147                RsaPssVerifyingKey::<Sha512>::new_with_salt_len(key, salt_len)
2148                    .verify(signed_data, &signature)
2149            }
2150            _ => {
2151                return unsupported(X509SignatureAlgorithm::RsaPss {
2152                    digest,
2153                    mgf_digest: digest,
2154                    salt_len,
2155                });
2156            }
2157        };
2158        Ok(verified.is_ok())
2159    }
2160
2161    pub(super) fn rsa_pss_salt_fits_key(
2162        key: &RsaPublicKey,
2163        digest: DigestAlgorithm,
2164        salt_len: usize,
2165    ) -> bool {
2166        let Some(em_bits) = key.n().bits().checked_sub(1) else {
2167            return false;
2168        };
2169        let Ok(em_len) = usize::try_from(em_bits.div_ceil(8)) else {
2170            return false;
2171        };
2172        digest
2173            .output_len()
2174            .checked_add(salt_len)
2175            .and_then(|length| length.checked_add(2))
2176            .is_some_and(|required| required <= em_len)
2177    }
2178
2179    pub(crate) fn compatible_rsa_pss_public_key_from_spki(
2180        spki_der: &[u8],
2181        signature_algorithm: X509SignatureAlgorithm,
2182    ) -> Option<RsaPublicKey> {
2183        let (rest, spki) = x509_parser::x509::SubjectPublicKeyInfo::from_der(spki_der).ok()?;
2184        if !rest.is_empty() {
2185            return None;
2186        }
2187        match spki.algorithm.algorithm.to_id_string().as_str() {
2188            "1.2.840.113549.1.1.1" => RsaPublicKey::from_public_key_der(spki_der).ok(),
2189            "1.2.840.113549.1.1.10" => {
2190                // RFC 4055 section 3.3 applies key restrictions only when
2191                // RSASSA-PSS-params is present in SubjectPublicKeyInfo.
2192                if spki
2193                    .algorithm
2194                    .parameters
2195                    .as_ref()
2196                    .is_some_and(|parameters| {
2197                        !rsa_pss_key_parameters_allow(parameters, signature_algorithm)
2198                    })
2199                {
2200                    return None;
2201                }
2202                RsaPublicKey::from_pkcs1_der(&spki.subject_public_key.data).ok()
2203            }
2204            _ => None,
2205        }
2206    }
2207
2208    pub(crate) fn rsa_pss_key_parameters_allow(
2209        parameters: &x509_parser::asn1_rs::Any<'_>,
2210        signature_algorithm: X509SignatureAlgorithm,
2211    ) -> bool {
2212        let X509SignatureAlgorithm::RsaPss {
2213            digest,
2214            mgf_digest,
2215            salt_len,
2216        } = signature_algorithm
2217        else {
2218            return false;
2219        };
2220        let Ok(parameters) =
2221            x509_parser::signature_algorithm::RsaSsaPssParams::try_from(parameters)
2222        else {
2223            return false;
2224        };
2225        let Ok(mask) = parameters.mask_gen_algorithm() else {
2226            return false;
2227        };
2228        parameters.trailer_field() == 1
2229            && x509_digest_from_oid(&parameters.hash_algorithm_oid().to_id_string()) == Some(digest)
2230            && mask.mgf.to_id_string() == "1.2.840.113549.1.1.8"
2231            && x509_digest_from_oid(&mask.hash.to_id_string()) == Some(mgf_digest)
2232            && usize::try_from(parameters.salt_length()).is_ok_and(|minimum| salt_len >= minimum)
2233    }
2234
2235    fn x509_digest_from_oid(oid: &str) -> Option<DigestAlgorithm> {
2236        match oid {
2237            "1.3.14.3.2.26" => Some(DigestAlgorithm::Sha1),
2238            "2.16.840.1.101.3.4.2.4" => Some(DigestAlgorithm::Sha224),
2239            "2.16.840.1.101.3.4.2.1" => Some(DigestAlgorithm::Sha256),
2240            "2.16.840.1.101.3.4.2.2" => Some(DigestAlgorithm::Sha384),
2241            "2.16.840.1.101.3.4.2.3" => Some(DigestAlgorithm::Sha512),
2242            "2.16.840.1.101.3.4.2.7" => Some(DigestAlgorithm::Sha3_224),
2243            "2.16.840.1.101.3.4.2.8" => Some(DigestAlgorithm::Sha3_256),
2244            "2.16.840.1.101.3.4.2.9" => Some(DigestAlgorithm::Sha3_384),
2245            "2.16.840.1.101.3.4.2.10" => Some(DigestAlgorithm::Sha3_512),
2246            _ => None,
2247        }
2248    }
2249
2250    const fn rsa_pkcs1_algorithm(digest: DigestAlgorithm) -> Option<SignatureAlgorithm> {
2251        match digest {
2252            DigestAlgorithm::Sha1 => Some(SignatureAlgorithm::RsaSha1),
2253            DigestAlgorithm::Sha224 => Some(SignatureAlgorithm::RsaSha224),
2254            DigestAlgorithm::Sha256 => Some(SignatureAlgorithm::RsaSha256),
2255            DigestAlgorithm::Sha384 => Some(SignatureAlgorithm::RsaSha384),
2256            DigestAlgorithm::Sha512 => Some(SignatureAlgorithm::RsaSha512),
2257            _ => None,
2258        }
2259    }
2260
2261    pub(super) const fn ecdsa_algorithm(digest: DigestAlgorithm) -> Option<SignatureAlgorithm> {
2262        match digest {
2263            #[cfg(feature = "legacy-algorithms")]
2264            DigestAlgorithm::Md5 | DigestAlgorithm::Ripemd160 => None,
2265            DigestAlgorithm::Sha1 => Some(SignatureAlgorithm::EcdsaSha1),
2266            DigestAlgorithm::Sha224 => Some(SignatureAlgorithm::EcdsaSha224),
2267            DigestAlgorithm::Sha256 => Some(SignatureAlgorithm::EcdsaSha256),
2268            DigestAlgorithm::Sha384 => Some(SignatureAlgorithm::EcdsaSha384),
2269            DigestAlgorithm::Sha512 => Some(SignatureAlgorithm::EcdsaSha512),
2270            DigestAlgorithm::Sha3_224 => Some(SignatureAlgorithm::EcdsaSha3_224),
2271            DigestAlgorithm::Sha3_256 => Some(SignatureAlgorithm::EcdsaSha3_256),
2272            DigestAlgorithm::Sha3_384 => Some(SignatureAlgorithm::EcdsaSha3_384),
2273            DigestAlgorithm::Sha3_512 => Some(SignatureAlgorithm::EcdsaSha3_512),
2274        }
2275    }
2276
2277    fn unsupported<T>(algorithm: X509SignatureAlgorithm) -> Result<T, ProviderError> {
2278        Err(ProviderError::Unsupported {
2279            operation: super::ProviderOperation::VerifyCertificate,
2280            algorithm: algorithm.oid().map(str::to_owned),
2281        })
2282    }
2283}
2284
2285#[cfg(feature = "xmlenc")]
2286mod rustcrypto {
2287    use crate::rustcrypto_sha3 as sha3;
2288    #[cfg(feature = "legacy-algorithms")]
2289    use aes::Aes192;
2290    use aes::{
2291        Aes128, Aes256,
2292        cipher::{BlockModeDecrypt, BlockModeEncrypt, KeyIvInit, block_padding::NoPadding},
2293    };
2294    use aes_gcm::{
2295        Aes128Gcm, Aes256Gcm, Nonce,
2296        aead::{AeadInOut, KeyInit},
2297    };
2298    use aes_kw::{KwAes128, KwAes256};
2299    use camellia::{Camellia128, Camellia192, Camellia256};
2300    use cbc::{Decryptor, Encryptor};
2301    #[cfg(feature = "legacy-algorithms")]
2302    use des::TdesEde3;
2303    use rsa::{Oaep, traits::PaddingScheme};
2304    use sha1::Sha1;
2305    use sha2::{Sha224, Sha256, Sha384, Sha512};
2306
2307    use super::{CryptoProvider, ProviderError, ProviderInputError};
2308    use crate::xmlenc::{
2309        DataEncryptionAlgorithm, KeyWrapAlgorithm, OaepDigestAlgorithm, RsaOaepParameters,
2310    };
2311
2312    pub(super) fn encrypt_data(
2313        provider: &dyn CryptoProvider,
2314        algorithm: DataEncryptionAlgorithm,
2315        key: &[u8],
2316        plaintext: &[u8],
2317    ) -> Result<Vec<u8>, ProviderError> {
2318        check_key(algorithm.key_len(), key)?;
2319        match algorithm {
2320            #[cfg(feature = "legacy-algorithms")]
2321            DataEncryptionAlgorithm::TripleDesCbc => {
2322                encrypt_cbc::<TdesEde3>(provider, key, plaintext)
2323            }
2324            #[cfg(feature = "legacy-algorithms")]
2325            DataEncryptionAlgorithm::Aes192Cbc => encrypt_cbc::<Aes192>(provider, key, plaintext),
2326            #[cfg(feature = "legacy-algorithms")]
2327            DataEncryptionAlgorithm::Aes192Gcm => encrypt_gcm::<
2328                aes_gcm::AesGcm<Aes192, aes_gcm::aead::consts::U12>,
2329            >(provider, key, plaintext),
2330            DataEncryptionAlgorithm::Aes128Cbc => encrypt_cbc::<Aes128>(provider, key, plaintext),
2331            DataEncryptionAlgorithm::Aes256Cbc => encrypt_cbc::<Aes256>(provider, key, plaintext),
2332            // RFC 9231 ยง2.6.2 uses the AES CBC framing and padding with
2333            // Camellia substituted as the block cipher.
2334            // https://www.rfc-editor.org/rfc/rfc9231.html#section-2.6.2
2335            DataEncryptionAlgorithm::Camellia128Cbc => {
2336                encrypt_cbc::<Camellia128>(provider, key, plaintext)
2337            }
2338            DataEncryptionAlgorithm::Camellia192Cbc => {
2339                encrypt_cbc::<Camellia192>(provider, key, plaintext)
2340            }
2341            DataEncryptionAlgorithm::Camellia256Cbc => {
2342                encrypt_cbc::<Camellia256>(provider, key, plaintext)
2343            }
2344            DataEncryptionAlgorithm::Aes128Gcm => {
2345                encrypt_gcm::<Aes128Gcm>(provider, key, plaintext)
2346            }
2347            DataEncryptionAlgorithm::Aes256Gcm => {
2348                encrypt_gcm::<Aes256Gcm>(provider, key, plaintext)
2349            }
2350            DataEncryptionAlgorithm::ChaCha20 | DataEncryptionAlgorithm::ChaCha20Poly1305 => {
2351                Err(ProviderError::InvalidInput(
2352                    ProviderInputError::PrimitiveInitialization("missing ChaCha parameters"),
2353                ))
2354            }
2355        }
2356    }
2357
2358    pub(super) fn decrypt_data(
2359        algorithm: DataEncryptionAlgorithm,
2360        key: &[u8],
2361        ciphertext: &[u8],
2362    ) -> Result<Vec<u8>, ProviderError> {
2363        check_key(algorithm.key_len(), key)?;
2364        match algorithm {
2365            #[cfg(feature = "legacy-algorithms")]
2366            DataEncryptionAlgorithm::TripleDesCbc => decrypt_cbc::<TdesEde3>(key, ciphertext),
2367            #[cfg(feature = "legacy-algorithms")]
2368            DataEncryptionAlgorithm::Aes192Cbc => decrypt_cbc::<Aes192>(key, ciphertext),
2369            #[cfg(feature = "legacy-algorithms")]
2370            DataEncryptionAlgorithm::Aes192Gcm => {
2371                decrypt_gcm::<aes_gcm::AesGcm<Aes192, aes_gcm::aead::consts::U12>>(key, ciphertext)
2372            }
2373            DataEncryptionAlgorithm::Aes128Cbc => decrypt_cbc::<Aes128>(key, ciphertext),
2374            DataEncryptionAlgorithm::Aes256Cbc => decrypt_cbc::<Aes256>(key, ciphertext),
2375            DataEncryptionAlgorithm::Camellia128Cbc => decrypt_cbc::<Camellia128>(key, ciphertext),
2376            DataEncryptionAlgorithm::Camellia192Cbc => decrypt_cbc::<Camellia192>(key, ciphertext),
2377            DataEncryptionAlgorithm::Camellia256Cbc => decrypt_cbc::<Camellia256>(key, ciphertext),
2378            DataEncryptionAlgorithm::Aes128Gcm => decrypt_gcm::<Aes128Gcm>(key, ciphertext),
2379            DataEncryptionAlgorithm::Aes256Gcm => decrypt_gcm::<Aes256Gcm>(key, ciphertext),
2380            DataEncryptionAlgorithm::ChaCha20 | DataEncryptionAlgorithm::ChaCha20Poly1305 => {
2381                Err(ProviderError::InvalidInput(
2382                    ProviderInputError::PrimitiveInitialization("missing ChaCha parameters"),
2383                ))
2384            }
2385        }
2386    }
2387
2388    fn check_key(expected: usize, key: &[u8]) -> Result<(), ProviderError> {
2389        if key.len() == expected {
2390            Ok(())
2391        } else {
2392            Err(ProviderError::InvalidKeySize {
2393                expected,
2394                actual: key.len(),
2395            })
2396        }
2397    }
2398
2399    fn encrypt_cbc<C>(
2400        provider: &dyn CryptoProvider,
2401        key: &[u8],
2402        plaintext: &[u8],
2403    ) -> Result<Vec<u8>, ProviderError>
2404    where
2405        C: aes::cipher::BlockCipherEncrypt + aes::cipher::KeyInit,
2406    {
2407        let block = C::block_size();
2408        let pad_len = block - (plaintext.len() % block);
2409        let length = plaintext
2410            .len()
2411            .checked_add(pad_len)
2412            .and_then(|len| len.checked_add(block))
2413            .ok_or(ProviderError::InvalidInput(
2414                ProviderInputError::AesCbcFraming,
2415            ))?;
2416        let mut output = zeroize::Zeroizing::new(vec![0_u8; length]);
2417        let (iv, padded) = output.split_at_mut(block);
2418        provider.fill_random(iv)?;
2419        padded[..plaintext.len()].copy_from_slice(plaintext);
2420        if pad_len > 1 {
2421            let last = padded.len() - 1;
2422            provider.fill_random(&mut padded[plaintext.len()..last])?;
2423        }
2424        *padded.last_mut().expect("padding is non-empty") = pad_len as u8;
2425        Encryptor::<C>::new_from_slices(key, iv)
2426            .map_err(|_| {
2427                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-CBC"))
2428            })?
2429            .encrypt_padded::<NoPadding>(padded, plaintext.len() + pad_len)
2430            .map_err(|_| {
2431                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2432                    "AES-CBC padding",
2433                ))
2434            })?;
2435        Ok(core::mem::take(&mut *output))
2436    }
2437
2438    fn decrypt_cbc<C>(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, ProviderError>
2439    where
2440        C: aes::cipher::BlockCipherDecrypt + aes::cipher::KeyInit,
2441    {
2442        let block = C::block_size();
2443        if ciphertext.len() < 2 * block || !(ciphertext.len() - block).is_multiple_of(block) {
2444            return Err(ProviderError::InvalidInput(
2445                ProviderInputError::AesCbcFraming,
2446            ));
2447        }
2448        let (iv, body) = ciphertext.split_at(block);
2449        let mut plaintext = zeroize::Zeroizing::new(body.to_vec());
2450        Decryptor::<C>::new_from_slices(key, iv)
2451            .map_err(|_| {
2452                ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-CBC"))
2453            })?
2454            .decrypt_padded::<NoPadding>(&mut plaintext)
2455            .map_err(|_| ProviderError::InvalidInput(ProviderInputError::AesCbcCiphertext))?;
2456        let pad_len = *plaintext.last().ok_or(ProviderError::InvalidInput(
2457            ProviderInputError::AesCbcCiphertext,
2458        ))?;
2459        let padding_bytes = usize::from(pad_len);
2460        if !(1..=block).contains(&padding_bytes) || padding_bytes > plaintext.len() {
2461            return Err(ProviderError::InvalidInput(
2462                ProviderInputError::AesCbcCiphertext,
2463            ));
2464        }
2465        let length = plaintext.len() - padding_bytes;
2466        plaintext.truncate(length);
2467        Ok(core::mem::take(&mut *plaintext))
2468    }
2469
2470    fn encrypt_gcm<C>(
2471        provider: &dyn CryptoProvider,
2472        key: &[u8],
2473        plaintext: &[u8],
2474    ) -> Result<Vec<u8>, ProviderError>
2475    where
2476        C: AeadInOut + KeyInit,
2477    {
2478        let mut nonce = [0_u8; 12];
2479        provider.fill_random(&mut nonce)?;
2480        let cipher = C::new_from_slice(key).map_err(|_| {
2481            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-GCM"))
2482        })?;
2483        let length = plaintext
2484            .len()
2485            .checked_add(28)
2486            .ok_or(ProviderError::InvalidInput(
2487                ProviderInputError::PrimitiveInitialization("AES-GCM length"),
2488            ))?;
2489        // Encrypt directly in the final nonce-prefixed allocation rather than
2490        // copying an intermediate ciphertext into another document-sized Vec.
2491        let mut output = zeroize::Zeroizing::new(Vec::with_capacity(length));
2492        output.extend_from_slice(&nonce);
2493        output.extend_from_slice(plaintext);
2494        let nonce = Nonce::try_from(nonce.as_slice()).map_err(|_| {
2495            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2496                "AES-GCM nonce",
2497            ))
2498        })?;
2499        let tag = cipher
2500            .encrypt_inout_detached(&nonce, &[], output[12..].as_mut().into())
2501            .map_err(|_| ProviderError::AuthenticationFailed)?;
2502        output.extend_from_slice(&tag);
2503        Ok(core::mem::take(&mut *output))
2504    }
2505
2506    fn decrypt_gcm<C>(key: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, ProviderError>
2507    where
2508        C: AeadInOut + KeyInit,
2509    {
2510        if ciphertext.len() < 28 {
2511            return Err(ProviderError::InvalidInput(
2512                ProviderInputError::AesGcmFraming,
2513            ));
2514        }
2515        let (nonce, body) = ciphertext.split_at(12);
2516        let cipher = C::new_from_slice(key).map_err(|_| {
2517            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization("AES-GCM"))
2518        })?;
2519        let mut plaintext = zeroize::Zeroizing::new(body.to_vec());
2520        let nonce = Nonce::try_from(nonce).map_err(|_| {
2521            ProviderError::InvalidInput(ProviderInputError::PrimitiveInitialization(
2522                "AES-GCM nonce",
2523            ))
2524        })?;
2525        cipher
2526            .decrypt_in_place(&nonce, &[], &mut *plaintext)
2527            .map_err(|_| ProviderError::AuthenticationFailed)?;
2528        Ok(core::mem::take(&mut *plaintext))
2529    }
2530
2531    pub(super) fn wrap_key(
2532        provider: &dyn CryptoProvider,
2533        algorithm: KeyWrapAlgorithm,
2534        kek: &[u8],
2535        key: &[u8],
2536    ) -> Result<Vec<u8>, ProviderError> {
2537        check_key(algorithm.key_len(), kek)?;
2538        if let KeyWrapAlgorithm::Cbc(cipher) = algorithm {
2539            if cipher.cbc_block_len().is_none() {
2540                return Err(ProviderError::InvalidInput(
2541                    ProviderInputError::AesKeyWrapFraming,
2542                ));
2543            }
2544            // XMLEnc 1.1 ยง3.4 defines EncryptedKey as EncryptedType; CBC
2545            // encrypts key bytes using the same padding/IV contract as data.
2546            // https://www.w3.org/TR/xmlenc-core1/#sec-EncryptedKey
2547            return encrypt_data(provider, cipher, kek, key);
2548        }
2549        #[cfg(feature = "legacy-algorithms")]
2550        if algorithm == KeyWrapAlgorithm::TripleDes {
2551            return wrap_des3(provider, kek, key);
2552        }
2553        #[cfg(not(feature = "legacy-algorithms"))]
2554        let _ = provider;
2555        // RFC 3394 ยง2.2.1 requires at least two 64-bit plaintext blocks.
2556        // Reject before allocating: the primitive also accepts the one-block
2557        // degenerate case, which is not this XML Encryption algorithm.
2558        // https://www.rfc-editor.org/rfc/rfc3394.html#section-2.2.1
2559        if key.len() < 16 || !key.len().is_multiple_of(8) {
2560            return Err(ProviderError::InvalidInput(
2561                ProviderInputError::AesKeyWrapFraming,
2562            ));
2563        }
2564        let length = algorithm
2565            .wrapped_len(key.len())
2566            .ok_or(ProviderError::InvalidInput(
2567                ProviderInputError::AesKeyWrapFraming,
2568            ))?;
2569        let mut output = vec![0_u8; length];
2570        // RFC 9231 ยง2.6.3 specifies RFC 3394 with Camellia replacing AES,
2571        // including the same A6 integrity register. Reuse its generic engine.
2572        // https://www.rfc-editor.org/rfc/rfc9231.html#section-2.6.3
2573        macro_rules! camellia_wrap {
2574            ($cipher:ty) => {
2575                aes_kw::AesKw::<$cipher>::new_from_slice(kek)
2576                    .map_err(|_| ProviderError::InvalidKeySize {
2577                        expected: algorithm.key_len(),
2578                        actual: kek.len(),
2579                    })?
2580                    .wrap_key(key, &mut output)
2581            };
2582        }
2583        match algorithm {
2584            KeyWrapAlgorithm::Cbc(_) => unreachable!("CBC wrapping already dispatched"),
2585            KeyWrapAlgorithm::CamelliaKw128 => camellia_wrap!(Camellia128),
2586            KeyWrapAlgorithm::CamelliaKw192 => camellia_wrap!(Camellia192),
2587            KeyWrapAlgorithm::CamelliaKw256 => camellia_wrap!(Camellia256),
2588            #[cfg(feature = "legacy-algorithms")]
2589            KeyWrapAlgorithm::AesKw192 => aes_kw::KwAes192::new_from_slice(kek)
2590                .map_err(|_| ProviderError::InvalidKeySize {
2591                    expected: 24,
2592                    actual: kek.len(),
2593                })?
2594                .wrap_key(key, &mut output),
2595            #[cfg(feature = "legacy-algorithms")]
2596            KeyWrapAlgorithm::TripleDes => unreachable!("CMS wrapping dispatched before AES"),
2597            KeyWrapAlgorithm::AesKw128 => KwAes128::new_from_slice(kek)
2598                .map_err(|_| ProviderError::InvalidKeySize {
2599                    expected: 16,
2600                    actual: kek.len(),
2601                })?
2602                .wrap_key(key, &mut output),
2603            KeyWrapAlgorithm::AesKw256 => KwAes256::new_from_slice(kek)
2604                .map_err(|_| ProviderError::InvalidKeySize {
2605                    expected: 32,
2606                    actual: kek.len(),
2607                })?
2608                .wrap_key(key, &mut output),
2609        }
2610        .map_err(|_| ProviderError::InvalidInput(ProviderInputError::AesKeyWrapFraming))?;
2611        Ok(output)
2612    }
2613
2614    pub(super) fn unwrap_key(
2615        algorithm: KeyWrapAlgorithm,
2616        kek: &[u8],
2617        wrapped: &[u8],
2618    ) -> Result<Vec<u8>, ProviderError> {
2619        check_key(algorithm.key_len(), kek)?;
2620        if let KeyWrapAlgorithm::Cbc(cipher) = algorithm {
2621            if cipher.cbc_block_len().is_none() {
2622                return Err(ProviderError::InvalidInput(
2623                    ProviderInputError::AesKeyWrapFraming,
2624                ));
2625            }
2626            return decrypt_data(cipher, kek, wrapped);
2627        }
2628        #[cfg(feature = "legacy-algorithms")]
2629        if algorithm == KeyWrapAlgorithm::TripleDes {
2630            return unwrap_des3(kek, wrapped);
2631        }
2632        if wrapped.len() < 24 || !wrapped.len().is_multiple_of(8) {
2633            return Err(ProviderError::InvalidInput(
2634                ProviderInputError::AesKeyWrapFraming,
2635            ));
2636        }
2637        let mut output = zeroize::Zeroizing::new(vec![0_u8; wrapped.len() - 8]);
2638        macro_rules! camellia_unwrap {
2639            ($cipher:ty) => {
2640                aes_kw::AesKw::<$cipher>::new_from_slice(kek)
2641                    .map_err(|_| ProviderError::InvalidKeySize {
2642                        expected: algorithm.key_len(),
2643                        actual: kek.len(),
2644                    })?
2645                    .unwrap_key(wrapped, &mut output)
2646            };
2647        }
2648        match algorithm {
2649            KeyWrapAlgorithm::Cbc(_) => unreachable!("CBC unwrapping already dispatched"),
2650            KeyWrapAlgorithm::CamelliaKw128 => camellia_unwrap!(Camellia128),
2651            KeyWrapAlgorithm::CamelliaKw192 => camellia_unwrap!(Camellia192),
2652            KeyWrapAlgorithm::CamelliaKw256 => camellia_unwrap!(Camellia256),
2653            #[cfg(feature = "legacy-algorithms")]
2654            KeyWrapAlgorithm::AesKw192 => aes_kw::KwAes192::new_from_slice(kek)
2655                .map_err(|_| ProviderError::InvalidKeySize {
2656                    expected: 24,
2657                    actual: kek.len(),
2658                })?
2659                .unwrap_key(wrapped, &mut output),
2660            #[cfg(feature = "legacy-algorithms")]
2661            KeyWrapAlgorithm::TripleDes => unreachable!("CMS unwrapping dispatched before AES"),
2662            KeyWrapAlgorithm::AesKw128 => KwAes128::new_from_slice(kek)
2663                .map_err(|_| ProviderError::InvalidKeySize {
2664                    expected: 16,
2665                    actual: kek.len(),
2666                })?
2667                .unwrap_key(wrapped, &mut output),
2668            KeyWrapAlgorithm::AesKw256 => KwAes256::new_from_slice(kek)
2669                .map_err(|_| ProviderError::InvalidKeySize {
2670                    expected: 32,
2671                    actual: kek.len(),
2672                })?
2673                .unwrap_key(wrapped, &mut output),
2674        }
2675        .map_err(|_| ProviderError::AuthenticationFailed)?;
2676        Ok(core::mem::take(&mut *output))
2677    }
2678
2679    #[cfg(feature = "legacy-algorithms")]
2680    const CMS_IV: [u8; 8] = [0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05];
2681
2682    #[cfg(feature = "legacy-algorithms")]
2683    fn wrap_des3(
2684        provider: &dyn CryptoProvider,
2685        kek: &[u8],
2686        key: &[u8],
2687    ) -> Result<Vec<u8>, ProviderError> {
2688        use sha1::Digest as _;
2689        // XMLEnc 1.1 ยง5.7.1 permits other key types in addition to RFC 3217's
2690        // DES CEK. Preserve opaque key octets (especially AES); generated DES
2691        // content keys are normalized to odd parity at their generation boundary.
2692        // https://www.w3.org/TR/xmlenc-core1/#sec-CMS-3DES
2693        if key.len() < 16 || !key.len().is_multiple_of(8) {
2694            return Err(ProviderError::InvalidInput(
2695                ProviderInputError::AesKeyWrapFraming,
2696            ));
2697        }
2698        let length = key
2699            .len()
2700            .checked_add(16)
2701            .ok_or(ProviderError::InvalidInput(
2702                ProviderInputError::AesKeyWrapFraming,
2703            ))?;
2704        let mut output = zeroize::Zeroizing::new(vec![0; length]);
2705        provider.fill_random(&mut output[..8])?;
2706        output[8..8 + key.len()].copy_from_slice(key);
2707        output[8 + key.len()..].copy_from_slice(&Sha1::digest(key)[..8]);
2708        let (iv, body) = output.split_at_mut(8);
2709        let body_len = body.len();
2710        Encryptor::<TdesEde3>::new_from_slices(kek, iv)
2711            .map_err(|_| ProviderError::AuthenticationFailed)?
2712            .encrypt_padded::<NoPadding>(body, body_len)
2713            .map_err(|_| ProviderError::AuthenticationFailed)?;
2714        output.reverse();
2715        Encryptor::<TdesEde3>::new_from_slices(kek, &CMS_IV)
2716            .map_err(|_| ProviderError::AuthenticationFailed)?
2717            .encrypt_padded::<NoPadding>(&mut output, length)
2718            .map_err(|_| ProviderError::AuthenticationFailed)?;
2719        Ok(core::mem::take(&mut *output))
2720    }
2721
2722    #[cfg(feature = "legacy-algorithms")]
2723    fn unwrap_des3(kek: &[u8], wrapped: &[u8]) -> Result<Vec<u8>, ProviderError> {
2724        use sha1::Digest as _;
2725        use subtle::ConstantTimeEq as _;
2726        // RFC 3217 ยง3.2: undo the outer CBC, octet reversal and inner CBC,
2727        // then authenticate the checksum before exposing any recovered bytes.
2728        // https://www.rfc-editor.org/rfc/rfc3217#section-3.2
2729        if wrapped.len() < 32 || !wrapped.len().is_multiple_of(8) {
2730            return Err(ProviderError::InvalidInput(
2731                ProviderInputError::AesKeyWrapFraming,
2732            ));
2733        }
2734        let mut output = zeroize::Zeroizing::new(wrapped.to_vec());
2735        Decryptor::<TdesEde3>::new_from_slices(kek, &CMS_IV)
2736            .map_err(|_| ProviderError::AuthenticationFailed)?
2737            .decrypt_padded::<NoPadding>(&mut output)
2738            .map_err(|_| ProviderError::AuthenticationFailed)?;
2739        output.reverse();
2740        let (iv, body) = output.split_at_mut(8);
2741        Decryptor::<TdesEde3>::new_from_slices(kek, iv)
2742            .map_err(|_| ProviderError::AuthenticationFailed)?
2743            .decrypt_padded::<NoPadding>(body)
2744            .map_err(|_| ProviderError::AuthenticationFailed)?;
2745        let key_len = body.len() - 8;
2746        if !bool::from(Sha1::digest(&body[..key_len])[..8].ct_eq(&body[key_len..])) {
2747            return Err(ProviderError::AuthenticationFailed);
2748        }
2749        output.copy_within(8..8 + key_len, 0);
2750        zeroize::Zeroize::zeroize(&mut output[key_len..]);
2751        output.truncate(key_len);
2752        Ok(core::mem::take(&mut *output))
2753    }
2754
2755    #[cfg(feature = "legacy-algorithms")]
2756    pub(super) fn transport_pkcs1v15(
2757        provider: &dyn CryptoProvider,
2758        key: &rsa::RsaPublicKey,
2759        plaintext: &[u8],
2760    ) -> Result<Vec<u8>, ProviderError> {
2761        rsa::Pkcs1v15Encrypt
2762            .encrypt(&mut super::ProviderRng(provider), key, plaintext)
2763            .map_err(map_rsa_error)
2764    }
2765
2766    pub(super) fn transport_key(
2767        provider: &dyn CryptoProvider,
2768        key: &rsa::RsaPublicKey,
2769        parameters: &RsaOaepParameters,
2770        plaintext: &[u8],
2771    ) -> Result<Vec<u8>, ProviderError> {
2772        if parameters.algorithm.requires_explicit_permission() {
2773            return Err(ProviderError::InvalidInput(
2774                ProviderInputError::PrimitiveInitialization("RSA-OAEP algorithm"),
2775            ));
2776        }
2777        run_oaep(provider, parameters, OaepOperation::Encrypt(key), plaintext)
2778    }
2779
2780    pub(super) fn recover_key(
2781        provider: &dyn CryptoProvider,
2782        key: &rsa::RsaPrivateKey,
2783        parameters: &RsaOaepParameters,
2784        ciphertext: &[u8],
2785    ) -> Result<Vec<u8>, ProviderError> {
2786        if parameters.algorithm.requires_explicit_permission() {
2787            return Err(ProviderError::InvalidInput(
2788                ProviderInputError::PrimitiveInitialization("RSA-OAEP algorithm"),
2789            ));
2790        }
2791        run_oaep(
2792            provider,
2793            parameters,
2794            OaepOperation::Decrypt(key),
2795            ciphertext,
2796        )
2797    }
2798
2799    enum OaepOperation<'a> {
2800        Encrypt(&'a rsa::RsaPublicKey),
2801        Decrypt(&'a rsa::RsaPrivateKey),
2802    }
2803
2804    fn run_oaep(
2805        provider: &dyn CryptoProvider,
2806        parameters: &RsaOaepParameters,
2807        operation: OaepOperation<'_>,
2808        input: &[u8],
2809    ) -> Result<Vec<u8>, ProviderError> {
2810        // RFC 8017 ยง7.1 selects Hash and MGF1's hash independently. One
2811        // dispatch keeps encryption and blinded recovery on the same contract.
2812        // https://www.rfc-editor.org/rfc/rfc8017.html#section-7.1
2813        match parameters.digest {
2814            #[cfg(feature = "legacy-algorithms")]
2815            OaepDigestAlgorithm::Md5 => {
2816                run_oaep_mgf::<md5::Md5>(provider, parameters, operation, input)
2817            }
2818            #[cfg(feature = "legacy-algorithms")]
2819            OaepDigestAlgorithm::Ripemd160 => {
2820                run_oaep_mgf::<ripemd::Ripemd160>(provider, parameters, operation, input)
2821            }
2822            OaepDigestAlgorithm::Sha3_224 => {
2823                run_oaep_mgf::<sha3::Sha3_224>(provider, parameters, operation, input)
2824            }
2825            OaepDigestAlgorithm::Sha3_256 => {
2826                run_oaep_mgf::<sha3::Sha3_256>(provider, parameters, operation, input)
2827            }
2828            OaepDigestAlgorithm::Sha3_384 => {
2829                run_oaep_mgf::<sha3::Sha3_384>(provider, parameters, operation, input)
2830            }
2831            OaepDigestAlgorithm::Sha3_512 => {
2832                run_oaep_mgf::<sha3::Sha3_512>(provider, parameters, operation, input)
2833            }
2834            OaepDigestAlgorithm::Sha1 => {
2835                run_oaep_mgf::<Sha1>(provider, parameters, operation, input)
2836            }
2837            OaepDigestAlgorithm::Sha224 => {
2838                run_oaep_mgf::<Sha224>(provider, parameters, operation, input)
2839            }
2840            OaepDigestAlgorithm::Sha256 => {
2841                run_oaep_mgf::<Sha256>(provider, parameters, operation, input)
2842            }
2843            OaepDigestAlgorithm::Sha384 => {
2844                run_oaep_mgf::<Sha384>(provider, parameters, operation, input)
2845            }
2846            OaepDigestAlgorithm::Sha512 => {
2847                run_oaep_mgf::<Sha512>(provider, parameters, operation, input)
2848            }
2849        }
2850    }
2851
2852    fn run_oaep_mgf<D: sha2::digest::Digest + sha2::digest::FixedOutputReset>(
2853        provider: &dyn CryptoProvider,
2854        parameters: &RsaOaepParameters,
2855        operation: OaepOperation<'_>,
2856        input: &[u8],
2857    ) -> Result<Vec<u8>, ProviderError> {
2858        match parameters.mgf_digest {
2859            OaepDigestAlgorithm::Sha1 => {
2860                run_oaep_primitive::<D, Sha1>(provider, parameters, operation, input)
2861            }
2862            OaepDigestAlgorithm::Sha224 => {
2863                run_oaep_primitive::<D, Sha224>(provider, parameters, operation, input)
2864            }
2865            OaepDigestAlgorithm::Sha256 => {
2866                run_oaep_primitive::<D, Sha256>(provider, parameters, operation, input)
2867            }
2868            OaepDigestAlgorithm::Sha384 => {
2869                run_oaep_primitive::<D, Sha384>(provider, parameters, operation, input)
2870            }
2871            OaepDigestAlgorithm::Sha512 => {
2872                run_oaep_primitive::<D, Sha512>(provider, parameters, operation, input)
2873            }
2874            _ => Err(ProviderError::InvalidInput(
2875                ProviderInputError::PrimitiveInitialization("unregistered XML OAEP mask digest"),
2876            )),
2877        }
2878    }
2879
2880    fn run_oaep_primitive<D, M>(
2881        provider: &dyn CryptoProvider,
2882        parameters: &RsaOaepParameters,
2883        operation: OaepOperation<'_>,
2884        input: &[u8],
2885    ) -> Result<Vec<u8>, ProviderError>
2886    where
2887        D: sha2::digest::Digest + sha2::digest::FixedOutputReset,
2888        M: sha2::digest::Digest + sha2::digest::FixedOutputReset,
2889    {
2890        let mut rng = super::ProviderRng(provider);
2891        let padding = Oaep::<D, M>::new_with_mgf_hash_and_label(parameters.label.clone());
2892        match operation {
2893            OaepOperation::Encrypt(key) => padding.encrypt(&mut rng, key, input),
2894            OaepOperation::Decrypt(key) => padding.decrypt(Some(&mut rng), key, input),
2895        }
2896        .map_err(map_rsa_error)
2897    }
2898
2899    pub(super) fn map_rsa_error(error: rsa::Error) -> ProviderError {
2900        match error {
2901            rsa::Error::Rng => ProviderError::Random("RSA randomness failed".into()),
2902            _ => ProviderError::AuthenticationFailed,
2903        }
2904    }
2905}
2906
2907#[cfg(test)]
2908mod tests {
2909    #[cfg(feature = "xmldsig")]
2910    use std::sync::atomic::AtomicUsize;
2911    use std::sync::atomic::{AtomicBool, Ordering};
2912
2913    use super::*;
2914
2915    #[cfg(feature = "xmlenc")]
2916    #[test]
2917    fn recovered_candidate_equality_includes_every_byte_and_validity() {
2918        // Deduplication must retain distinct candidates, including equal real/
2919        // rejection bytes with different validity. Timing is ensured by ct_eq,
2920        // not by a flaky wall-clock assertion.
2921        let reference = RecoveredContentKey::confirmed(vec![0x52; 32]);
2922        assert!(reference.same_candidate(&RecoveredContentKey::confirmed(vec![0x52; 32])));
2923        for index in 0..32 {
2924            let mut bytes = vec![0x52; 32];
2925            bytes[index] ^= 1;
2926            assert!(!reference.same_candidate(&RecoveredContentKey::confirmed(bytes)));
2927        }
2928        assert!(!reference.same_candidate(&RecoveredContentKey::confirmed(vec![0x52; 16])));
2929        for left in [0, 1] {
2930            for right in [0, 1] {
2931                let mut a = RecoveredContentKey::confirmed(vec![0x52; 32]);
2932                let mut b = RecoveredContentKey::confirmed(vec![0x52; 32]);
2933                a.valid = subtle::Choice::from(left);
2934                b.valid = subtle::Choice::from(right);
2935                assert_eq!(a.same_candidate(&b), left == right);
2936            }
2937        }
2938    }
2939
2940    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
2941    #[test]
2942    fn legacy_ciphers_cover_empty_and_block_boundary_plaintexts() {
2943        // Each cipher's own framing, including DES's 8-byte blocks, must agree
2944        // with the public size preflight for empty and non-aligned messages.
2945        for algorithm in [
2946            DataEncryptionAlgorithm::TripleDesCbc,
2947            DataEncryptionAlgorithm::Aes192Cbc,
2948            DataEncryptionAlgorithm::Aes192Gcm,
2949        ] {
2950            let key = [0x31; 24];
2951            for length in [0, 1, 7, 8, 15, 16, 17, 31, 32, 33] {
2952                let plaintext = vec![0x41; length];
2953                let ciphertext = RUST_CRYPTO_PROVIDER
2954                    .encrypt_data(algorithm, &key, &plaintext)
2955                    .expect("valid legacy encryption must succeed");
2956                assert_eq!(
2957                    Some(ciphertext.len()),
2958                    algorithm.ciphertext_len_for_plaintext(length)
2959                );
2960                assert_eq!(
2961                    RUST_CRYPTO_PROVIDER
2962                        .decrypt_data(algorithm, &key, &ciphertext)
2963                        .expect("matching legacy key must recover plaintext"),
2964                    plaintext
2965                );
2966                assert!(
2967                    RUST_CRYPTO_PROVIDER
2968                        .encrypt_data(algorithm, &key[..23], &plaintext)
2969                        .is_err()
2970                );
2971                assert!(
2972                    RUST_CRYPTO_PROVIDER
2973                        .decrypt_data(algorithm, &key[..23], &ciphertext)
2974                        .is_err()
2975                );
2976                assert!(
2977                    RUST_CRYPTO_PROVIDER
2978                        .decrypt_data(algorithm, &key, &ciphertext[..ciphertext.len() - 1])
2979                        .is_err()
2980                );
2981                if algorithm == DataEncryptionAlgorithm::Aes192Gcm {
2982                    let mut corrupted = ciphertext;
2983                    corrupted[12] ^= 1;
2984                    assert!(matches!(
2985                        RUST_CRYPTO_PROVIDER.decrypt_data(algorithm, &key, &corrupted),
2986                        Err(ProviderError::AuthenticationFailed)
2987                    ));
2988                }
2989            }
2990        }
2991    }
2992
2993    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
2994    #[test]
2995    fn cms_triple_des_unwrap_matches_rfc3217_vector() {
2996        // RFC 3217 ยง3.4 independently validates both CBC passes and reversal;
2997        // a round-trip alone could hide matching errors in wrap and unwrap.
2998        // https://www.rfc-editor.org/rfc/rfc3217.html#section-3.4
2999        let kek = hex_literal::hex!("255e0d1c07b646dfb3134cc843ba8aa71f025b7c0838251f");
3000        let key = hex_literal::hex!("2923bf85e06dd6ae529149f1f1bae9eab3a7da3d860d3e98");
3001        let wrapped = hex_literal::hex!(
3002            "690107618ef092b3b48ca1796b234ae9fa33ebb4159604037db5d6a84eb3aac2768c632775a467d4"
3003        );
3004        assert_eq!(
3005            RUST_CRYPTO_PROVIDER
3006                .unwrap_key(KeyWrapAlgorithm::TripleDes, &kek, &wrapped)
3007                .expect("RFC 3217 vector must authenticate"),
3008            key
3009        );
3010        for offset in 0..wrapped.len() {
3011            let mut corrupted = wrapped;
3012            corrupted[offset] ^= 1;
3013            assert!(matches!(
3014                RUST_CRYPTO_PROVIDER.unwrap_key(KeyWrapAlgorithm::TripleDes, &kek, &corrupted),
3015                Err(ProviderError::AuthenticationFailed)
3016            ));
3017        }
3018    }
3019
3020    #[cfg(all(feature = "legacy-algorithms", feature = "xmlenc"))]
3021    #[test]
3022    fn legacy_wrap_preserves_opaque_key_bytes_and_checks_integrity() {
3023        // XMLEnc permits wrapping non-DES keys; parity normalization must not
3024        // corrupt opaque AES key octets. Each algorithm has its own overhead.
3025        for algorithm in [KeyWrapAlgorithm::AesKw192, KeyWrapAlgorithm::TripleDes] {
3026            let kek = [0x37; 24];
3027            for length in [16, 24, 32] {
3028                let key: Vec<_> = (0..length).map(|byte| byte as u8).collect();
3029                let mut wrapped = RUST_CRYPTO_PROVIDER
3030                    .wrap_key(algorithm, &kek, &key)
3031                    .expect("valid content key must wrap");
3032                assert_eq!(Some(wrapped.len()), algorithm.wrapped_len(key.len()));
3033                assert_eq!(
3034                    RUST_CRYPTO_PROVIDER
3035                        .unwrap_key(algorithm, &kek, &wrapped)
3036                        .expect("matching KEK must recover exact key octets"),
3037                    key
3038                );
3039                assert!(
3040                    RUST_CRYPTO_PROVIDER
3041                        .unwrap_key(algorithm, &[0x38; 24], &wrapped)
3042                        .is_err()
3043                );
3044                wrapped[0] ^= 1;
3045                assert!(
3046                    RUST_CRYPTO_PROVIDER
3047                        .unwrap_key(algorithm, &kek, &wrapped)
3048                        .is_err()
3049                );
3050            }
3051            for length in [0, 1, 8, 15, 17] {
3052                assert!(
3053                    RUST_CRYPTO_PROVIDER
3054                        .wrap_key(algorithm, &kek, &vec![0; length])
3055                        .is_err()
3056                );
3057            }
3058        }
3059    }
3060
3061    #[cfg(all(feature = "legacy-algorithms", feature = "xmldsig"))]
3062    #[test]
3063    fn legacy_digests_match_known_answers() {
3064        // Published empty/abc answers prevent URI wiring from silently choosing
3065        // a different hash with the same output width.
3066        for (algorithm, expected) in [
3067            (
3068                DigestAlgorithm::Md5,
3069                &hex_literal::hex!("900150983cd24fb0d6963f7d28e17f72")[..],
3070            ),
3071            (
3072                DigestAlgorithm::Ripemd160,
3073                &hex_literal::hex!("8eb208f7e05d987a9b044a8e98c6b087f15a0bfc")[..],
3074            ),
3075        ] {
3076            assert_eq!(
3077                RUST_CRYPTO_PROVIDER
3078                    .digest(algorithm, b"abc")
3079                    .expect("compiled digest must be supported"),
3080                expected
3081            );
3082            assert_eq!(DigestAlgorithm::from_uri(algorithm.uri()), Some(algorithm));
3083            assert!(algorithm.requires_explicit_permission());
3084            assert!(!algorithm.signing_allowed());
3085        }
3086    }
3087
3088    #[cfg(feature = "xmldsig")]
3089    struct CountingRandomProvider {
3090        random_calls: AtomicUsize,
3091        reject_digest: Option<DigestAlgorithm>,
3092        extra_digest_byte: bool,
3093        accept_signatures: bool,
3094    }
3095
3096    #[cfg(feature = "xmldsig")]
3097    impl CryptoProvider for CountingRandomProvider {
3098        fn name(&self) -> &'static str {
3099            "counting-random"
3100        }
3101
3102        fn supports(&self, capability: ProviderCapability<'_>) -> bool {
3103            RUST_CRYPTO_PROVIDER.supports(capability)
3104        }
3105
3106        fn fill_random(&self, output: &mut [u8]) -> Result<(), ProviderError> {
3107            self.random_calls.fetch_add(1, Ordering::Relaxed);
3108            RUST_CRYPTO_PROVIDER.fill_random(output)
3109        }
3110
3111        fn derive_key(
3112            &self,
3113            parameters: &KdfParameters<'_>,
3114            secret: &[u8],
3115        ) -> Result<Vec<u8>, ProviderError> {
3116            RUST_CRYPTO_PROVIDER.derive_key(parameters, secret)
3117        }
3118
3119        fn digest(
3120            &self,
3121            algorithm: DigestAlgorithm,
3122            data: &[u8],
3123        ) -> Result<Vec<u8>, ProviderError> {
3124            if self.reject_digest == Some(algorithm) {
3125                return Err(ProviderError::Unsupported {
3126                    operation: ProviderOperation::Digest,
3127                    algorithm: Some(algorithm.uri().to_owned()),
3128                });
3129            }
3130            let mut digest = RUST_CRYPTO_PROVIDER.digest(algorithm, data)?;
3131            if self.extra_digest_byte {
3132                digest.push(0);
3133            }
3134            Ok(digest)
3135        }
3136
3137        fn sign(
3138            &self,
3139            key: &dyn crate::xmldsig::SigningKey,
3140            algorithm: crate::xmldsig::SignatureAlgorithm,
3141            data: &[u8],
3142        ) -> Result<Vec<u8>, crate::xmldsig::SigningKeyError> {
3143            key.sign_with_provider(self, algorithm, data)
3144        }
3145
3146        fn verify(
3147            &self,
3148            key: &dyn crate::xmldsig::VerifyingKey,
3149            algorithm: crate::xmldsig::SignatureAlgorithm,
3150            data: &[u8],
3151            signature: &[u8],
3152        ) -> Result<bool, crate::xmldsig::DsigError> {
3153            if self.accept_signatures {
3154                return Ok(true);
3155            }
3156            RUST_CRYPTO_PROVIDER.verify(key, algorithm, data, signature)
3157        }
3158
3159        #[cfg(feature = "xmlenc")]
3160        fn encrypt_data(
3161            &self,
3162            algorithm: DataEncryptionAlgorithm,
3163            key: &[u8],
3164            plaintext: &[u8],
3165        ) -> Result<Vec<u8>, ProviderError> {
3166            RUST_CRYPTO_PROVIDER.encrypt_data(algorithm, key, plaintext)
3167        }
3168
3169        #[cfg(feature = "xmlenc")]
3170        fn decrypt_data(
3171            &self,
3172            algorithm: DataEncryptionAlgorithm,
3173            key: &[u8],
3174            ciphertext: &[u8],
3175        ) -> Result<Vec<u8>, ProviderError> {
3176            RUST_CRYPTO_PROVIDER.decrypt_data(algorithm, key, ciphertext)
3177        }
3178
3179        #[cfg(feature = "xmlenc")]
3180        fn wrap_key(
3181            &self,
3182            algorithm: KeyWrapAlgorithm,
3183            kek: &[u8],
3184            key: &[u8],
3185        ) -> Result<Vec<u8>, ProviderError> {
3186            RUST_CRYPTO_PROVIDER.wrap_key(algorithm, kek, key)
3187        }
3188
3189        #[cfg(feature = "xmlenc")]
3190        fn unwrap_key(
3191            &self,
3192            algorithm: KeyWrapAlgorithm,
3193            kek: &[u8],
3194            wrapped: &[u8],
3195        ) -> Result<Vec<u8>, ProviderError> {
3196            RUST_CRYPTO_PROVIDER.unwrap_key(algorithm, kek, wrapped)
3197        }
3198
3199        #[cfg(feature = "xmlenc")]
3200        fn transport_key(
3201            &self,
3202            key: &dyn KeyTransportKey,
3203            parameters: &RsaOaepParameters,
3204            plaintext: &[u8],
3205        ) -> Result<Vec<u8>, ProviderError> {
3206            RUST_CRYPTO_PROVIDER.transport_key(key, parameters, plaintext)
3207        }
3208
3209        #[cfg(feature = "xmlenc")]
3210        fn recover_key(
3211            &self,
3212            key: &dyn KeyRecoveryKey,
3213            parameters: &RsaOaepParameters,
3214            ciphertext: &[u8],
3215        ) -> Result<Vec<u8>, ProviderError> {
3216            RUST_CRYPTO_PROVIDER.recover_key(key, parameters, ciphertext)
3217        }
3218    }
3219
3220    #[cfg(feature = "xmldsig")]
3221    #[test]
3222    fn capability_query_is_explicit_about_unimplemented_operations() {
3223        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Digest(DigestAlgorithm::Sha256)));
3224        let agreement = KeyAgreementParameters {
3225            algorithm: "urn:unsupported:agreement",
3226            peer_public_key: &[],
3227        };
3228        assert!(!RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyAgreement(&agreement)));
3229        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Sign(
3230            crate::xmldsig::SignatureAlgorithm::RsaSha1
3231        )));
3232        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::Verify(
3233            crate::xmldsig::SignatureAlgorithm::RsaSha1
3234        )));
3235        for digest in [DigestAlgorithm::Sha1, DigestAlgorithm::Sha512] {
3236            assert!(
3237                RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(
3238                    X509SignatureAlgorithm::Ecdsa(digest)
3239                ))
3240            );
3241        }
3242        // Certificate capabilities must agree with the actual execution
3243        // mapping, not merely with the presence of an ECDSA primitive.
3244        #[cfg(feature = "legacy-algorithms")]
3245        for digest in [DigestAlgorithm::Md5, DigestAlgorithm::Ripemd160] {
3246            assert!(
3247                !RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(
3248                    X509SignatureAlgorithm::Ecdsa(digest)
3249                ))
3250            );
3251        }
3252    }
3253
3254    #[cfg(feature = "xmldsig")]
3255    #[test]
3256    fn x509_digest_key_info_uses_the_selected_provider() {
3257        use crate::xmldsig::{
3258            DigestAlgorithm, KeyInfoWriteError, KeyInfoWriter, RsaSigningKey,
3259            X509DigestKeyInfoWriter,
3260        };
3261
3262        // KeyInfo generation is part of the signing operation's provider
3263        // boundary; a writer must not silently fall back to RustCrypto.
3264        let key = RsaSigningKey::from_pkcs8_pem(include_str!(
3265            "../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3266        ))
3267        .expect("RSA fixture must parse");
3268        let writer = X509DigestKeyInfoWriter::from_pem(
3269            include_str!("../tests/fixtures/keys/rsa/rsa-2048-cert.pem"),
3270            DigestAlgorithm::Sha224,
3271        )
3272        .expect("certificate fixture must parse");
3273        let provider = CountingRandomProvider {
3274            random_calls: AtomicUsize::new(0),
3275            reject_digest: Some(DigestAlgorithm::Sha224),
3276            extra_digest_byte: false,
3277            accept_signatures: false,
3278        };
3279
3280        let error = writer
3281            .write_key_info_with_provider(&key, &provider)
3282            .expect_err("the selected provider must control X509Digest");
3283        assert!(matches!(
3284            error,
3285            KeyInfoWriteError::Provider(ProviderError::Unsupported {
3286                operation: ProviderOperation::Digest,
3287                ..
3288            })
3289        ));
3290    }
3291
3292    #[cfg(feature = "xmldsig")]
3293    #[test]
3294    fn x509_digest_writer_rejects_trailing_certificate_der() {
3295        use crate::xmldsig::{DigestAlgorithm, KeyInfoWriteError, X509DigestKeyInfoWriter};
3296
3297        // The writer retains both digest bytes and the validated signing-key
3298        // identity, so construction must accept exactly one DER certificate.
3299        let (_, certificate) = x509_parser::pem::parse_x509_pem(include_bytes!(
3300            "../tests/fixtures/keys/rsa/rsa-2048-cert.pem"
3301        ))
3302        .expect("certificate fixture must parse");
3303        let mut certificate_der = certificate.contents;
3304        certificate_der.push(0);
3305
3306        assert!(matches!(
3307            X509DigestKeyInfoWriter::from_der(&certificate_der, DigestAlgorithm::Sha256),
3308            Err(KeyInfoWriteError::InvalidCertificateDer)
3309        ));
3310    }
3311
3312    #[cfg(all(feature = "xmldsig", feature = "xmlenc"))]
3313    #[test]
3314    fn capability_queries_include_oaep_and_pss_parameters() {
3315        use crate::xmlenc::{KeyTransportAlgorithm, OaepDigestAlgorithm};
3316
3317        let explicit_legacy = RsaOaepParameters {
3318            algorithm: KeyTransportAlgorithm::RsaOaepMgf1p,
3319            digest: OaepDigestAlgorithm::Sha256,
3320            mgf_digest: OaepDigestAlgorithm::Sha256,
3321            label: Vec::new(),
3322        };
3323        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyTransport(&explicit_legacy)));
3324        let modern =
3325            RsaOaepParameters::xmlenc11(OaepDigestAlgorithm::Sha256, OaepDigestAlgorithm::Sha512)
3326                .label(b"label".to_vec());
3327        assert!(RUST_CRYPTO_PROVIDER.supports(ProviderCapability::KeyTransport(&modern)));
3328
3329        let supported_pss = X509SignatureAlgorithm::RsaPss {
3330            digest: DigestAlgorithm::Sha256,
3331            mgf_digest: DigestAlgorithm::Sha256,
3332            salt_len: 32,
3333        };
3334        assert!(
3335            RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(supported_pss))
3336        );
3337        let unsupported_pss = X509SignatureAlgorithm::RsaPss {
3338            digest: DigestAlgorithm::Sha256,
3339            mgf_digest: DigestAlgorithm::Sha384,
3340            salt_len: 32,
3341        };
3342        assert!(
3343            !RUST_CRYPTO_PROVIDER.supports(ProviderCapability::VerifyCertificate(unsupported_pss))
3344        );
3345    }
3346
3347    struct RecordingAgreementKey(AtomicBool);
3348
3349    impl KeyAgreementKey for RecordingAgreementKey {
3350        fn agree(
3351            &self,
3352            _parameters: &KeyAgreementParameters<'_>,
3353        ) -> Result<Vec<u8>, ProviderError> {
3354            self.0.store(true, Ordering::Relaxed);
3355            Ok(vec![0x42])
3356        }
3357    }
3358
3359    #[test]
3360    fn unsupported_agreement_and_kdf_fail_without_dispatch_or_fallback() {
3361        let agreement = KeyAgreementParameters {
3362            algorithm: "urn:example:agreement",
3363            peer_public_key: b"peer",
3364        };
3365        let key = RecordingAgreementKey(AtomicBool::new(false));
3366        let error = RUST_CRYPTO_PROVIDER
3367            .agree_key(&key, &agreement)
3368            .expect_err("unsupported agreement must fail closed");
3369        assert!(matches!(
3370            error,
3371            ProviderError::Unsupported {
3372                operation: ProviderOperation::KeyAgreement,
3373                algorithm: Some(ref algorithm),
3374            } if algorithm == agreement.algorithm
3375        ));
3376        assert!(!key.0.load(Ordering::Relaxed));
3377
3378        let kdf = KdfParameters {
3379            algorithm: "urn:example:kdf",
3380            digest: Some("urn:example:digest"),
3381            salt: b"salt",
3382            info: KdfContext::Octets(b"info"),
3383            iterations: 1,
3384            output_len: 32,
3385        };
3386        assert!(matches!(
3387            RUST_CRYPTO_PROVIDER.derive_key(&kdf, b"secret"),
3388            Err(ProviderError::Unsupported {
3389                operation: ProviderOperation::Kdf,
3390                algorithm: Some(ref algorithm),
3391            }) if algorithm == kdf.algorithm
3392        ));
3393    }
3394
3395    #[cfg(feature = "xmldsig")]
3396    #[test]
3397    fn rsa_signing_uses_the_selected_providers_randomness() {
3398        use crate::xmldsig::{RsaSigningKey, SignatureAlgorithm};
3399
3400        // RSA PKCS#1 v1.5 uses randomness for blinding even though its wire
3401        // signature is deterministic; the selected provider owns that source.
3402        let key = RsaSigningKey::from_pkcs8_pem(include_str!(
3403            "../tests/fixtures/keys/rsa/rsa-2048-key.pem"
3404        ))
3405        .expect("RSA fixture must parse");
3406        let provider = CountingRandomProvider {
3407            random_calls: AtomicUsize::new(0),
3408            reject_digest: None,
3409            extra_digest_byte: false,
3410            accept_signatures: false,
3411        };
3412
3413        let signature = provider
3414            .sign(&key, SignatureAlgorithm::RsaSha256, b"signed info")
3415            .expect("RSA signing must succeed");
3416
3417        assert!(!signature.is_empty());
3418        assert!(provider.random_calls.load(Ordering::Relaxed) > 0);
3419    }
3420
3421    #[cfg(feature = "xmldsig")]
3422    #[test]
3423    fn ecdsa_signing_uses_the_selected_providers_digest() {
3424        use crate::xmldsig::{
3425            EcdsaP256SigningKey, EcdsaP384SigningKey, SignatureAlgorithm, SigningKeyError,
3426        };
3427
3428        // SignatureMethod chooses the hash independently of the EC key curve.
3429        // Both built-in ECDSA keys must therefore ask the selected provider for
3430        // that digest instead of hashing behind the provider boundary.
3431        let cases: [(
3432            Box<dyn crate::xmldsig::SigningKey>,
3433            SignatureAlgorithm,
3434            DigestAlgorithm,
3435        ); 2] = [
3436            (
3437                Box::new(
3438                    EcdsaP256SigningKey::from_pkcs8_pem(include_str!(
3439                        "../tests/fixtures/keys/ec/ec-prime256v1-key.pem"
3440                    ))
3441                    .expect("P-256 fixture must parse"),
3442                ),
3443                SignatureAlgorithm::EcdsaSha384,
3444                DigestAlgorithm::Sha384,
3445            ),
3446            (
3447                Box::new(
3448                    EcdsaP384SigningKey::from_pkcs8_pem(include_str!(
3449                        "../tests/fixtures/keys/ec/ec-prime384v1-key.pem"
3450                    ))
3451                    .expect("P-384 fixture must parse"),
3452                ),
3453                SignatureAlgorithm::EcdsaSha256,
3454                DigestAlgorithm::Sha256,
3455            ),
3456        ];
3457
3458        for (key, signature_algorithm, digest_algorithm) in cases {
3459            let provider = CountingRandomProvider {
3460                random_calls: AtomicUsize::new(0),
3461                reject_digest: Some(digest_algorithm),
3462                extra_digest_byte: false,
3463                accept_signatures: false,
3464            };
3465            let error = provider
3466                .sign(key.as_ref(), signature_algorithm, b"signed info")
3467                .expect_err("provider digest rejection must stop ECDSA signing");
3468
3469            assert!(matches!(
3470                error,
3471                SigningKeyError::Provider(ProviderError::Unsupported {
3472                    operation: ProviderOperation::Digest,
3473                    algorithm: Some(ref uri),
3474                }) if uri == digest_algorithm.uri()
3475            ));
3476        }
3477    }
3478
3479    #[cfg(feature = "xmldsig")]
3480    #[test]
3481    fn ecdsa_signing_rejects_provider_digests_with_the_wrong_length() {
3482        use crate::xmldsig::{
3483            EcdsaP256SigningKey, EcdsaP384SigningKey, SignatureAlgorithm, SigningKeyError,
3484        };
3485
3486        // Prehash signers may truncate oversized input, so the provider
3487        // boundary must reject it before either curve receives the digest.
3488        let cases: [(
3489            Box<dyn crate::xmldsig::SigningKey>,
3490            SignatureAlgorithm,
3491            usize,
3492        ); 2] = [
3493            (
3494                Box::new(
3495                    EcdsaP256SigningKey::from_pkcs8_pem(include_str!(
3496                        "../tests/fixtures/keys/ec/ec-prime256v1-key.pem"
3497                    ))
3498                    .expect("P-256 fixture must parse"),
3499                ),
3500                SignatureAlgorithm::EcdsaSha256,
3501                32,
3502            ),
3503            (
3504                Box::new(
3505                    EcdsaP384SigningKey::from_pkcs8_pem(include_str!(
3506                        "../tests/fixtures/keys/ec/ec-prime384v1-key.pem"
3507                    ))
3508                    .expect("P-384 fixture must parse"),
3509                ),
3510                SignatureAlgorithm::EcdsaSha384,
3511                48,
3512            ),
3513        ];
3514
3515        for (key, algorithm, expected) in cases {
3516            let provider = CountingRandomProvider {
3517                random_calls: AtomicUsize::new(0),
3518                reject_digest: None,
3519                extra_digest_byte: true,
3520                accept_signatures: false,
3521            };
3522            let error = provider
3523                .sign(key.as_ref(), algorithm, b"signed info")
3524                .expect_err("an oversized provider digest must not reach ECDSA prehash signing");
3525
3526            assert!(matches!(
3527                error,
3528                SigningKeyError::Provider(ProviderError::InvalidOutputSize {
3529                    operation: ProviderOperation::Digest,
3530                    expected: actual_expected,
3531                    actual,
3532                }) if actual_expected == expected && actual == expected + 1
3533            ));
3534        }
3535    }
3536
3537    #[cfg(feature = "xmldsig")]
3538    #[test]
3539    fn verification_facade_rejects_malformed_dsa_before_provider_dispatch() {
3540        use crate::xmldsig::{
3541            DefaultKeyResolver, DsigStatus, FailureReason, SignatureAlgorithm, VerifyContext,
3542        };
3543
3544        let original = include_str!(
3545            "../tests/fixtures/xmldsig/merlin-xmldsig-twenty-three/signature-enveloping-dsa.xml"
3546        );
3547        let value_start = original
3548            .find("<SignatureValue>")
3549            .expect("Merlin fixture must contain SignatureValue")
3550            + "<SignatureValue>".len();
3551        let value_end = original[value_start..]
3552            .find("</SignatureValue>")
3553            .map(|offset| value_start + offset)
3554            .expect("Merlin fixture must close SignatureValue");
3555        let mut malformed = original.to_owned();
3556        malformed.replace_range(value_start..value_end, "AQ==");
3557        let provider = CountingRandomProvider {
3558            random_calls: AtomicUsize::new(0),
3559            reject_digest: None,
3560            extra_digest_byte: false,
3561            accept_signatures: true,
3562        };
3563
3564        let mut policy = crate::policy::VerificationPolicy::default();
3565        policy
3566            .key_trust
3567            .allowed_legacy_signature_algorithms
3568            .insert(SignatureAlgorithm::DsaSha1);
3569        policy.key_trust.dsa_keys.minimum_modulus_bits = 1024;
3570        policy.key_trust.mode = crate::policy::VerificationTrustMode::CryptographicOnly;
3571        let result = VerifyContext::new()
3572            .policy(policy)
3573            .provider(&provider)
3574            .key_resolver(&DefaultKeyResolver::default())
3575            .verify(&malformed)
3576            .expect("malformed framing must be a verification miss");
3577
3578        assert_eq!(
3579            result.status,
3580            DsigStatus::Invalid(FailureReason::SignatureMismatch)
3581        );
3582    }
3583
3584    #[cfg(feature = "xmldsig")]
3585    #[test]
3586    fn rustcrypto_provider_verifies_parameterized_rsa_pss_certificates() {
3587        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3588        use der::{Decode as _, Encode as _};
3589        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3590        use rsa::{RsaPrivateKey, pss::SigningKey as RsaPssSigningKey};
3591        use sha2::Sha256;
3592        use signature::{RandomizedSigner, SignatureEncoding};
3593        use x509_cert::spki::{AlgorithmIdentifierOwned, ObjectIdentifier};
3594
3595        // X.509 RSASSA-PSS carries salt and MGF parameters that cannot be
3596        // represented by the XMLDSig SignatureAlgorithm enum.
3597        let mut rng = ChaCha20Rng::from_seed([0x5a; 32]);
3598        let private_key =
3599            RsaPrivateKey::new(&mut rng, 2048).expect("deterministic RSA key generation");
3600        let public_key = private_key
3601            .to_public_key()
3602            .to_public_key_der()
3603            .expect("RSA public key must encode as SPKI");
3604        let signing_key = RsaPssSigningKey::<Sha256>::new_with_salt_len(private_key, 32);
3605        let signed_data = b"certificate tbs bytes";
3606        let signature = signing_key
3607            .try_sign_with_rng(&mut rng, signed_data)
3608            .expect("RSA-PSS signing must succeed")
3609            .to_vec();
3610
3611        assert!(
3612            RUST_CRYPTO_PROVIDER
3613                .verify_x509_signature(
3614                    X509SignatureAlgorithm::RsaPss {
3615                        digest: DigestAlgorithm::Sha256,
3616                        mgf_digest: DigestAlgorithm::Sha256,
3617                        salt_len: 32,
3618                    },
3619                    signed_data,
3620                    &signature,
3621                    public_key.as_bytes(),
3622                )
3623                .expect("standard RSA-PSS parameters must be supported")
3624        );
3625
3626        let mut parameterless_pss_spki =
3627            x509_cert::SubjectPublicKeyInfo::from_der(public_key.as_bytes())
3628                .expect("RSA SPKI must decode");
3629        parameterless_pss_spki.algorithm = AlgorithmIdentifierOwned {
3630            oid: ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"),
3631            parameters: None,
3632        };
3633        let parameterless_pss_spki = parameterless_pss_spki
3634            .to_der()
3635            .expect("parameterless PSS SPKI must encode");
3636        assert!(
3637            RUST_CRYPTO_PROVIDER
3638                .verify_x509_signature(
3639                    X509SignatureAlgorithm::RsaPss {
3640                        digest: DigestAlgorithm::Sha256,
3641                        mgf_digest: DigestAlgorithm::Sha256,
3642                        salt_len: 32,
3643                    },
3644                    signed_data,
3645                    &signature,
3646                    &parameterless_pss_spki,
3647                )
3648                .expect("parameterless PSS keys impose no signature restrictions")
3649        );
3650
3651        let mut pss_spki = x509_cert::SubjectPublicKeyInfo::from_der(public_key.as_bytes())
3652            .expect("RSA SPKI must decode");
3653        let pss_parameters = der::asn1::Any::from_der(&[
3654            0x30, 0x34, 0xa0, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
3655            0x04, 0x02, 0x01, 0x05, 0x00, 0xa1, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48,
3656            0x86, 0xf7, 0x0d, 0x01, 0x01, 0x08, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
3657            0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa2, 0x03, 0x02, 0x01, 0x20,
3658        ])
3659        .expect("standard SHA-256 PSS parameters must decode");
3660        pss_spki.algorithm = AlgorithmIdentifierOwned {
3661            oid: ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"),
3662            parameters: Some(pss_parameters),
3663        };
3664        let pss_spki = pss_spki.to_der().expect("PSS SPKI must encode");
3665
3666        assert!(
3667            RUST_CRYPTO_PROVIDER
3668                .verify_x509_signature(
3669                    X509SignatureAlgorithm::RsaPss {
3670                        digest: DigestAlgorithm::Sha256,
3671                        mgf_digest: DigestAlgorithm::Sha256,
3672                        salt_len: 32,
3673                    },
3674                    signed_data,
3675                    &signature,
3676                    &pss_spki,
3677                )
3678                .expect("RFC 4055 PSS SubjectPublicKeyInfo must be supported")
3679        );
3680
3681        for incompatible in [
3682            X509SignatureAlgorithm::RsaPss {
3683                digest: DigestAlgorithm::Sha384,
3684                mgf_digest: DigestAlgorithm::Sha256,
3685                salt_len: 32,
3686            },
3687            X509SignatureAlgorithm::RsaPss {
3688                digest: DigestAlgorithm::Sha256,
3689                mgf_digest: DigestAlgorithm::Sha384,
3690                salt_len: 32,
3691            },
3692            X509SignatureAlgorithm::RsaPss {
3693                digest: DigestAlgorithm::Sha256,
3694                mgf_digest: DigestAlgorithm::Sha256,
3695                salt_len: 16,
3696            },
3697        ] {
3698            assert!(
3699                !RUST_CRYPTO_PROVIDER
3700                    .verify_x509_signature(incompatible, signed_data, &signature, &pss_spki,)
3701                    .expect("incompatible PSS key restrictions are invalid, not unsupported")
3702            );
3703        }
3704    }
3705
3706    #[cfg(feature = "xmldsig")]
3707    #[test]
3708    fn rustcrypto_provider_verifies_dsa_certificate_signature_at_q_width() {
3709        use base64::Engine as _;
3710
3711        // OpenSSL-generated L=2048/N=224 DSA material. X.509 carries DER r/s
3712        // integers at q width, not XMLDSig's legacy fixed 20-byte components.
3713        let spki = base64::engine::general_purpose::STANDARD
3714            .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=")
3715            .expect("DSA SPKI fixture must decode");
3716        let signature = base64::engine::general_purpose::STANDARD
3717            .decode("MD0CHQChtB1c+f5BmTJCtT7Gi4cyQiR2igj0znRQYCJ3Ahw4NGg4pL5jgA8Ri07ESV9Yr90WfUmRrbRcnjsY")
3718            .expect("DSA signature fixture must decode");
3719        let message = b"certificate tbs bytes for dsa q-width regression";
3720
3721        assert!(
3722            rustcrypto_x509::verify_signature(
3723                X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1),
3724                message,
3725                &signature,
3726                &spki,
3727            )
3728            .expect("supported DSA-SHA1 certificate signature")
3729        );
3730
3731        let mut tampered = signature;
3732        *tampered.last_mut().expect("DER signature is non-empty") ^= 1;
3733        assert!(
3734            !rustcrypto_x509::verify_signature(
3735                X509SignatureAlgorithm::Dsa(DigestAlgorithm::Sha1),
3736                message,
3737                &tampered,
3738                &spki,
3739            )
3740            .expect("tampered DSA-SHA1 certificate signature is a verification miss")
3741        );
3742    }
3743
3744    #[cfg(feature = "xmldsig")]
3745    #[test]
3746    fn primitive_provider_does_not_embed_rsa_strength_policy() {
3747        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3748        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3749        use rsa::{RsaPrivateKey, pss::SigningKey as RsaPssSigningKey};
3750        use sha2::Sha256;
3751        use signature::{RandomizedSigner, SignatureEncoding};
3752
3753        let mut rng = ChaCha20Rng::from_seed([0x3c; 32]);
3754        let private_key =
3755            RsaPrivateKey::new(&mut rng, 1024).expect("deterministic weak RSA key generation");
3756        let public_key = private_key
3757            .to_public_key()
3758            .to_public_key_der()
3759            .expect("weak RSA public key must encode as SPKI");
3760        let signed_data = b"certificate tbs bytes";
3761        let signature = RsaPssSigningKey::<Sha256>::new_with_salt_len(private_key, 32)
3762            .try_sign_with_rng(&mut rng, signed_data)
3763            .expect("weak RSA-PSS key can still produce a cryptographic signature")
3764            .to_vec();
3765
3766        assert!(
3767            RUST_CRYPTO_PROVIDER
3768                .verify_x509_signature(
3769                    X509SignatureAlgorithm::RsaPss {
3770                        digest: DigestAlgorithm::Sha256,
3771                        mgf_digest: DigestAlgorithm::Sha256,
3772                        salt_len: 32,
3773                    },
3774                    signed_data,
3775                    &signature,
3776                    public_key.as_bytes(),
3777                )
3778                .expect(
3779                    "provider must evaluate structurally valid RSA-PSS independently of policy"
3780                )
3781        );
3782    }
3783
3784    #[cfg(feature = "xmldsig")]
3785    #[test]
3786    fn oversized_rsa_pss_salt_is_a_verification_miss() {
3787        use crate::rsa_encoding::RsaPublicKeyEncoding as _;
3788        use rand_chacha::{ChaCha20Rng, rand_core::SeedableRng};
3789        use rsa::{RsaPrivateKey, traits::PublicKeyParts as _};
3790
3791        // ASN.1 saltLength is attacker-controlled. It must not reach the
3792        // dependency's unchecked hLen + saltLen + 2 arithmetic.
3793        let mut rng = ChaCha20Rng::from_seed([0x55; 32]);
3794        let public_key = RsaPrivateKey::new(&mut rng, 1024)
3795            .expect("deterministic RSA key generation")
3796            .to_public_key();
3797        let spki = public_key
3798            .to_public_key_der()
3799            .expect("RSA public key must encode as SPKI");
3800
3801        assert!(rustcrypto_x509::rsa_pss_salt_fits_key(
3802            &public_key,
3803            DigestAlgorithm::Sha256,
3804            0,
3805        ));
3806        assert!(rustcrypto_x509::rsa_pss_salt_fits_key(
3807            &public_key,
3808            DigestAlgorithm::Sha256,
3809            94,
3810        ));
3811        assert!(!rustcrypto_x509::rsa_pss_salt_fits_key(
3812            &public_key,
3813            DigestAlgorithm::Sha256,
3814            95,
3815        ));
3816
3817        assert!(
3818            !RUST_CRYPTO_PROVIDER
3819                .verify_x509_signature(
3820                    X509SignatureAlgorithm::RsaPss {
3821                        digest: DigestAlgorithm::Sha256,
3822                        mgf_digest: DigestAlgorithm::Sha256,
3823                        salt_len: usize::MAX,
3824                    },
3825                    b"certificate tbs bytes",
3826                    &vec![0_u8; public_key.size()],
3827                    spki.as_bytes(),
3828                )
3829                .expect("oversized PSS salt must fail without panicking")
3830        );
3831    }
3832}