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