Skip to main content

dimpl/crypto/
provider.rs

1//! Cryptographic provider traits for pluggable crypto backends.
2//!
3//! This module defines the trait-based interface for cryptographic operations
4//! in dimpl, allowing users to provide custom crypto implementations.
5//!
6//! # Overview
7//!
8//! The crypto provider system is inspired by rustls's design and uses a component-based
9//! approach where the [`CryptoProvider`] struct holds static references to various
10//! trait objects, each representing a specific cryptographic capability.
11//!
12//! # Architecture
13//!
14//! The provider system is organized into these main components:
15//!
16//! - **Cipher Suites** ([`SupportedDtls12CipherSuite`]): Factory for AEAD ciphers
17//! - **Key Exchange Groups** ([`SupportedKxGroup`]): Factory for ECDHE key exchanges
18//! - **Signature Verification** ([`SignatureVerifier`]): Verify signatures in certificates
19//! - **Key Provider** ([`KeyProvider`]): Parse and load private keys
20//! - **Secure Random** ([`SecureRandom`]): Cryptographically secure RNG
21//! - **Hash Provider** ([`HashProvider`]): Factory for hash contexts
22//! - **HMAC Provider** ([`HmacProvider`]): Compute HMAC signatures (also drives PRF and HKDF)
23//!
24//! # Using a Custom Provider
25//!
26//! To use a custom crypto provider, create one and pass it to the [`Config`](crate::Config):
27//!
28//! ```
29//! # #[cfg(all(feature = "aws-lc-rs", feature = "rcgen"))]
30//! # fn main() {
31//! use std::sync::Arc;
32//! use std::time::Instant;
33//! use dimpl::{Config, Dtls, certificate};
34//! use dimpl::crypto::aws_lc_rs;
35//!
36//! let cert = certificate::generate_self_signed_certificate().unwrap();
37//! // Use the default aws-lc-rs provider (implicit)
38//! let config = Arc::new(Config::default());
39//!
40//! // Or explicitly set the provider
41//! let config = Arc::new(
42//!     Config::builder()
43//!         .with_crypto_provider(aws_lc_rs::default_provider())
44//!         .build()
45//!         .unwrap()
46//! );
47//!
48//! // Or use your own custom provider
49//! // let config = Arc::new(
50//! //     Config::builder()
51//! //         .with_crypto_provider(my_custom_provider())
52//! //         .build()
53//! //         .unwrap()
54//! // );
55//!
56//! let dtls = Dtls::new_12(config, cert, Instant::now());
57//! # }
58//! # #[cfg(not(all(feature = "aws-lc-rs", feature = "rcgen")))]
59//! # fn main() {}
60//! ```
61//!
62//! # Implementing a Custom Provider
63//!
64//! To implement a custom provider, you need to:
65//!
66//! 1. Implement the required traits for your crypto backend
67//! 2. Create static instances of your implementations
68//! 3. Build a [`CryptoProvider`] struct with references to those statics
69//!
70//! ## Example: Custom Cipher Suite
71//!
72//! ```
73//! use dimpl::CryptoError;
74//! use dimpl::crypto::{SupportedDtls12CipherSuite, Cipher, Dtls12CipherSuite, HashAlgorithm};
75//! use dimpl::crypto::{Buf, TmpBuf};
76//! use dimpl::crypto::{Aad, Nonce};
77//!
78//! #[derive(Debug)]
79//! struct MyCipher;
80//!
81//! impl MyCipher {
82//!     fn new(_key: &[u8]) -> Result<Self, CryptoError> {
83//!         Ok(Self)
84//!     }
85//! }
86//!
87//! impl Cipher for MyCipher {
88//!     fn encrypt(&mut self, _: &mut Buf, _: Aad, _: Nonce) -> Result<(), CryptoError> {
89//!         Ok(())
90//!     }
91//!     fn decrypt(&mut self, _: &mut TmpBuf, _: Aad, _: Nonce) -> Result<(), CryptoError> {
92//!         Ok(())
93//!     }
94//! }
95//!
96//! #[derive(Debug)]
97//! struct MyDtls12CipherSuite;
98//!
99//! impl SupportedDtls12CipherSuite for MyDtls12CipherSuite {
100//!     fn suite(&self) -> Dtls12CipherSuite {
101//!         Dtls12CipherSuite::ECDHE_ECDSA_AES128_GCM_SHA256
102//!     }
103//!
104//!     fn hash_algorithm(&self) -> HashAlgorithm {
105//!         HashAlgorithm::SHA256
106//!     }
107//!
108//!     fn key_lengths(&self) -> (usize, usize, usize) {
109//!         (0, 16, 4) // (mac_key_len, enc_key_len, fixed_iv_len)
110//!     }
111//!
112//!     fn explicit_nonce_len(&self) -> usize {
113//!         8 // AES-GCM: 8-byte explicit nonce per record
114//!     }
115//!
116//!     fn tag_len(&self) -> usize {
117//!         16 // 128-bit authentication tag
118//!     }
119//!
120//!     fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, CryptoError> {
121//!         // Create your cipher implementation here
122//!         Ok(Box::new(MyCipher::new(key)?))
123//!     }
124//! }
125//!
126//! static MY_CIPHER_SUITE: MyDtls12CipherSuite = MyDtls12CipherSuite;
127//! static ALL_CIPHER_SUITES: &[&dyn SupportedDtls12CipherSuite] = &[&MY_CIPHER_SUITE];
128//! ```
129//!
130//! # Requirements
131//!
132//! For DTLS 1.2, implementations must support:
133//!
134//! - **Cipher suites**: ECDHE_ECDSA with AES-128-GCM, AES-256-GCM, or CHACHA20_POLY1305
135//! - **Key exchange**: ECDHE with X25519, P-256, or P-384 curves
136//! - **Signatures**: ECDSA with P-256/SHA-256 or P-384/SHA-384
137//! - **Hash**: SHA-256 and SHA-384
138//! - **HMAC**: HMAC-SHA256 and HMAC-SHA384 (used for PRF, HKDF, and cookies)
139//!
140//! # Thread Safety
141//!
142//! All provider traits require `Send + Sync + UnwindSafe + RefUnwindSafe` to ensure
143//! safe usage across threads and panic boundaries.
144
145use std::fmt::Debug;
146use std::panic::{RefUnwindSafe, UnwindSafe};
147use std::sync::OnceLock;
148
149use crate::buffer::{Buf, TmpBuf};
150use crate::crypto::{Aad, Nonce};
151use crate::dtls12::message::Dtls12CipherSuite;
152use crate::types::{Dtls13CipherSuite, HashAlgorithm, NamedGroup, SignatureAlgorithm};
153use crate::{CertificateError, CryptoError};
154
155/// OID for the P-256 elliptic curve (secp256r1 / prime256v1).
156#[cfg(feature = "_crypto-common")]
157pub const OID_P256: spki::ObjectIdentifier =
158    spki::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
159
160/// OID for the P-384 elliptic curve (secp384r1).
161#[cfg(feature = "_crypto-common")]
162pub const OID_P384: spki::ObjectIdentifier = spki::ObjectIdentifier::new_unwrap("1.3.132.0.34");
163
164// ============================================================================
165// Marker Trait
166// ============================================================================
167
168/// Marker trait for types that are safe to use in crypto provider components.
169///
170/// This trait combines the common bounds required for crypto provider trait objects:
171/// - [`Send`] + [`Sync`]: Thread-safe
172/// - [`Debug`]: Support debugging
173/// - [`UnwindSafe`] + [`RefUnwindSafe`]: Panic-safe
174///
175/// This trait is automatically implemented for all types that satisfy these bounds.
176pub trait CryptoSafe: Send + Sync + Debug + UnwindSafe + RefUnwindSafe {}
177
178/// Blanket implementation: any type satisfying the bounds implements [`CryptoSafe`].
179impl<T: Send + Sync + Debug + UnwindSafe + RefUnwindSafe> CryptoSafe for T {}
180
181// ============================================================================
182// Instance Traits (Level 2 - created by factories)
183// ============================================================================
184
185/// AEAD cipher for in-place encryption/decryption.
186pub trait Cipher: CryptoSafe {
187    /// Encrypt plaintext in-place, appending authentication tag.
188    fn encrypt(&mut self, plaintext: &mut Buf, aad: Aad, nonce: Nonce) -> Result<(), CryptoError>;
189
190    /// Decrypt ciphertext in-place, verifying and removing authentication tag.
191    fn decrypt(
192        &mut self,
193        ciphertext: &mut TmpBuf,
194        aad: Aad,
195        nonce: Nonce,
196    ) -> Result<(), CryptoError>;
197}
198
199/// Stateful hash context for incremental hashing.
200pub trait HashContext: CryptoSafe {
201    /// Update the hash with new data.
202    fn update(&mut self, data: &[u8]);
203
204    /// Clone the context and finalize it, writing the hash to `out`.
205    /// The original context can continue to be updated.
206    fn clone_and_finalize(&self, out: &mut Buf);
207}
208
209/// Signing key for generating digital signatures.
210pub trait SigningKey: CryptoSafe {
211    /// Sign data using the specified hash algorithm and return the signature.
212    fn sign(
213        &mut self,
214        data: &[u8],
215        hash_alg: HashAlgorithm,
216        out: &mut Buf,
217    ) -> Result<(), CryptoError>;
218
219    /// Signature algorithm used by this key.
220    fn algorithm(&self) -> SignatureAlgorithm;
221
222    /// Default hash algorithm for this key.
223    fn hash_algorithm(&self) -> HashAlgorithm;
224
225    /// Hash algorithms this key can sign with.
226    ///
227    /// Used during negotiation to intersect with the peer's offered
228    /// algorithms. Backends that lock the hash at key-load time (e.g.
229    /// aws-lc-rs) return only the locked hash; backends that support
230    /// arbitrary prehash signing (e.g. RustCrypto) may return several.
231    fn supported_hash_algorithms(&self) -> &[HashAlgorithm];
232}
233
234/// Active key exchange instance (ephemeral keypair for one handshake).
235pub trait ActiveKeyExchange: CryptoSafe {
236    /// Get the public key for this exchange.
237    fn pub_key(&self) -> &[u8];
238
239    /// Complete exchange with peer's public key, returning shared secret.
240    fn complete(self: Box<Self>, peer_pub: &[u8], out: &mut Buf) -> Result<(), CryptoError>;
241
242    /// Get the named group for this exchange.
243    fn group(&self) -> NamedGroup;
244}
245
246// ============================================================================
247// Factory Traits (Level 1 - used by CryptoProvider)
248// ============================================================================
249
250/// Cipher suite support (factory for Cipher instances).
251pub trait SupportedDtls12CipherSuite: CryptoSafe {
252    /// The cipher suite this supports.
253    fn suite(&self) -> Dtls12CipherSuite;
254
255    /// Hash algorithm used by this suite.
256    fn hash_algorithm(&self) -> HashAlgorithm;
257
258    /// Key material lengths: (mac_key_len, enc_key_len, fixed_iv_len).
259    fn key_lengths(&self) -> (usize, usize, usize);
260
261    /// Length in bytes of the per-record explicit nonce (carried in the record body).
262    ///
263    /// AES-GCM suites carry an 8-byte explicit nonce; ChaCha20-Poly1305 carries none.
264    fn explicit_nonce_len(&self) -> usize;
265
266    /// AEAD authentication tag length in bytes.
267    fn tag_len(&self) -> usize;
268
269    /// Minimum length, in bytes, of a protected record's encrypted fragment.
270    ///
271    /// For AEAD suites this equals explicit nonce + authentication tag; a CBC
272    /// suite would override this to `IV + MAC + 1` (one padding byte). Records
273    /// shorter than this cannot be valid regardless of cipher mode and are
274    /// rejected at the record boundary.
275    fn min_protected_fragment_len(&self) -> usize {
276        self.explicit_nonce_len() + self.tag_len()
277    }
278
279    /// Create a cipher instance with the given key.
280    fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, CryptoError>;
281}
282
283/// Key exchange group support (factory for ActiveKeyExchange).
284pub trait SupportedKxGroup: CryptoSafe {
285    /// Named group for this key exchange group.
286    fn name(&self) -> NamedGroup;
287
288    /// Start a new key exchange, generating ephemeral keypair.
289    /// The provided `buf` will be used to store the public key.
290    fn start_exchange(&self, buf: Buf) -> Result<Box<dyn ActiveKeyExchange>, CryptoError>;
291}
292
293/// Signature verification against certificates.
294pub trait SignatureVerifier: CryptoSafe {
295    /// Verify a signature on data using a DER-encoded X.509 certificate.
296    fn verify_signature(
297        &self,
298        cert_der: &[u8],
299        data: &[u8],
300        signature: &[u8],
301        hash_alg: HashAlgorithm,
302        sig_alg: SignatureAlgorithm,
303    ) -> Result<(), CryptoError>;
304}
305
306/// Allow-list of supported (signature, hash, curve) combinations for
307/// DTLS 1.2 signature verification.
308///
309/// In DTLS 1.2 the hash algorithm and the certificate's curve are
310/// independent choices, so all cross-combinations are valid.
311///
312///  Signature | Hash    | Curve
313/// -----------+---------+-----------
314///  ECDSA     | SHA-256 | P-256
315///  ECDSA     | SHA-256 | P-384
316///  ECDSA     | SHA-384 | P-256
317///  ECDSA     | SHA-384 | P-384
318const SUPPORTED_VERIFY_SCHEMES: &[(SignatureAlgorithm, HashAlgorithm, NamedGroup)] = &[
319    (
320        SignatureAlgorithm::ECDSA,
321        HashAlgorithm::SHA256,
322        NamedGroup::Secp256r1,
323    ),
324    (
325        SignatureAlgorithm::ECDSA,
326        HashAlgorithm::SHA256,
327        NamedGroup::Secp384r1,
328    ),
329    (
330        SignatureAlgorithm::ECDSA,
331        HashAlgorithm::SHA384,
332        NamedGroup::Secp256r1,
333    ),
334    (
335        SignatureAlgorithm::ECDSA,
336        HashAlgorithm::SHA384,
337        NamedGroup::Secp384r1,
338    ),
339];
340
341/// Check that a (signature, hash, curve) combination is in the allow-list.
342pub fn check_verify_scheme(
343    sig_alg: SignatureAlgorithm,
344    hash_alg: HashAlgorithm,
345    group: NamedGroup,
346) -> Result<(), CryptoError> {
347    if SUPPORTED_VERIFY_SCHEMES
348        .iter()
349        .any(|(s, h, g)| *s == sig_alg && *h == hash_alg && *g == group)
350    {
351        Ok(())
352    } else {
353        Err(CryptoError::UnsupportedSignatureVerification {
354            signature: sig_alg,
355            hash: hash_alg,
356            group,
357        })
358    }
359}
360
361/// Extract the EC curve ([`NamedGroup`]) from a DER-encoded X.509 certificate.
362///
363/// Used by DTLS 1.3 to verify that the [`SignatureScheme`](crate::types::SignatureScheme)
364/// in `CertificateVerify` is consistent with the peer's certificate key.
365#[cfg(feature = "_crypto-common")]
366pub fn cert_named_group(cert_der: &[u8]) -> Result<NamedGroup, CertificateError> {
367    use der::Decode;
368    use spki::ObjectIdentifier;
369    use x509_cert::Certificate as X509Certificate;
370
371    let cert = X509Certificate::from_der(cert_der).map_err(|_| CertificateError::ParseFailed)?;
372    let spki = &cert.tbs_certificate.subject_public_key_info;
373
374    let curve_oid: ObjectIdentifier = spki
375        .algorithm
376        .parameters
377        .as_ref()
378        .ok_or(CertificateError::MissingEcCurveParameter)?
379        .decode_as()
380        .map_err(|_| CertificateError::InvalidEcCurveParameter)?;
381
382    match curve_oid {
383        OID_P256 => Ok(NamedGroup::Secp256r1),
384        OID_P384 => Ok(NamedGroup::Secp384r1),
385        _ => Err(CertificateError::UnsupportedEcCurve),
386    }
387}
388
389/// Private key parser (factory for SigningKey).
390pub trait KeyProvider: CryptoSafe {
391    /// Parse and load a private key from DER/PEM bytes.
392    fn load_private_key(&self, key_der: &[u8]) -> Result<Box<dyn SigningKey>, CryptoError>;
393}
394
395/// Secure random number generator.
396pub trait SecureRandom: CryptoSafe {
397    /// Fill buffer with cryptographically secure random bytes.
398    fn fill(&self, buf: &mut [u8]) -> Result<(), CryptoError>;
399}
400
401/// Hash provider (factory for HashContext).
402pub trait HashProvider: CryptoSafe {
403    /// Create a new hash context for the specified algorithm.
404    fn create_hash(&self, algorithm: HashAlgorithm) -> Box<dyn HashContext>;
405}
406
407/// HMAC provider for computing HMAC signatures.
408pub trait HmacProvider: CryptoSafe {
409    /// Compute HMAC-SHA256(key, data) and return the result.
410    fn hmac_sha256(&self, key: &[u8], data: &[u8]) -> Result<[u8; 32], CryptoError> {
411        let mut out = [0u8; 32];
412        self.hmac(HashAlgorithm::SHA256, key, data, &mut out)?;
413        Ok(out)
414    }
415
416    /// Compute HMAC for the given hash algorithm, writing the result to `out`.
417    ///
418    /// Returns the number of bytes written.
419    fn hmac(
420        &self,
421        hash: HashAlgorithm,
422        key: &[u8],
423        data: &[u8],
424        out: &mut [u8],
425    ) -> Result<usize, CryptoError>;
426}
427
428// ============================================================================
429// DTLS 1.3 Factory Traits
430// ============================================================================
431
432/// Cipher suite support for DTLS 1.3 (factory for Cipher instances).
433///
434/// Unlike DTLS 1.2 cipher suites, TLS 1.3 cipher suites only specify the
435/// AEAD algorithm and hash function. Key exchange is negotiated separately.
436pub trait SupportedDtls13CipherSuite: CryptoSafe {
437    /// The cipher suite this supports.
438    fn suite(&self) -> Dtls13CipherSuite;
439
440    /// Hash algorithm used by this suite.
441    fn hash_algorithm(&self) -> HashAlgorithm;
442
443    /// AEAD key length in bytes.
444    fn key_len(&self) -> usize;
445
446    /// AEAD nonce/IV length in bytes.
447    fn iv_len(&self) -> usize;
448
449    /// AEAD tag length in bytes.
450    fn tag_len(&self) -> usize;
451
452    /// Minimum length, in bytes, of a protected record's encrypted fragment.
453    /// DTLS 1.3 has no explicit nonce in the record, so this equals
454    /// [`Self::tag_len`]. Records shorter than this cannot hold a valid
455    /// ciphertext + tag and are rejected at the record boundary.
456    fn min_protected_fragment_len(&self) -> usize {
457        self.tag_len()
458    }
459
460    /// Create a cipher instance with the given key.
461    fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, CryptoError>;
462
463    /// Compute a mask for record number encryption (RFC 9147 Section 4.2.3).
464    ///
465    /// The mask is XORed over the sequence number bytes in the header.
466    /// `sample` is the first 16 bytes of the ciphertext.
467    ///
468    /// For AES-based suites: `mask = AES-ECB(sn_key, sample)`.
469    ///
470    /// For ChaCha20-based suites (RFC 9001 Section 5.4.4):
471    /// `counter = sample[0..4]` (LE u32), `nonce = sample[4..16]`,
472    /// `mask = ChaCha20(sn_key, counter, nonce, <zero bytes>)`.
473    fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16];
474}
475
476// ============================================================================
477// Core Provider Struct
478// ============================================================================
479
480/// Cryptographic provider for DTLS operations.
481///
482/// This struct holds references to all cryptographic components needed
483/// for DTLS. Users can provide custom implementations of each component
484/// to replace the default aws-lc-rs-based provider.
485///
486/// # Version-Specific Components
487///
488/// Shared components like `kx_groups`, `signature_verification`, `key_provider`,
489/// `secure_random`, `hash_provider`, and `hmac_provider` are used by both versions.
490/// PRF (TLS 1.2) and HKDF (TLS 1.3) key derivation are built generically on top
491/// of `hmac_provider` — see the [`prf_hkdf`](super::prf_hkdf) module.
492///
493/// # Design
494///
495/// The provider uses static trait object references (`&'static dyn Trait`) which
496/// provides zero runtime overhead for trait dispatch. This design is inspired by
497/// rustls's CryptoProvider and ensures efficient crypto operations.
498///
499/// # Example
500///
501/// ```
502/// # #[cfg(feature = "aws-lc-rs")]
503/// # fn main() {
504/// use dimpl::crypto::{CryptoProvider, aws_lc_rs};
505///
506/// // Use the default provider
507/// let provider = aws_lc_rs::default_provider();
508///
509/// // Or build a custom one (using defaults for demonstration)
510/// let custom_provider = CryptoProvider {
511///     // Shared components
512///     kx_groups: provider.kx_groups,
513///     signature_verification: provider.signature_verification,
514///     key_provider: provider.key_provider,
515///     secure_random: provider.secure_random,
516///     hash_provider: provider.hash_provider,
517///     hmac_provider: provider.hmac_provider,
518///     // DTLS 1.2 components
519///     cipher_suites: provider.cipher_suites,
520///     // DTLS 1.3 components
521///     dtls13_cipher_suites: provider.dtls13_cipher_suites,
522/// };
523/// # }
524/// # #[cfg(not(feature = "aws-lc-rs"))]
525/// # fn main() {}
526/// ```
527#[derive(Debug, Clone)]
528pub struct CryptoProvider {
529    // =========================================================================
530    // Shared components (used by both DTLS 1.2 and DTLS 1.3)
531    // =========================================================================
532    /// Supported key exchange groups (P-256, P-384, X25519).
533    ///
534    /// Used for ECDHE key exchange in both DTLS versions.
535    pub kx_groups: &'static [&'static dyn SupportedKxGroup],
536
537    /// Signature verification for certificates.
538    pub signature_verification: &'static dyn SignatureVerifier,
539
540    /// Key provider for parsing private keys.
541    pub key_provider: &'static dyn KeyProvider,
542
543    /// Secure random number generator.
544    pub secure_random: &'static dyn SecureRandom,
545
546    /// Hash provider for handshake hashing.
547    pub hash_provider: &'static dyn HashProvider,
548
549    /// HMAC provider for computing HMAC signatures.
550    pub hmac_provider: &'static dyn HmacProvider,
551
552    // =========================================================================
553    // DTLS 1.2 specific components
554    // =========================================================================
555    /// Supported DTLS 1.2 cipher suites (for negotiation).
556    ///
557    /// These cipher suites bundle key exchange, authentication, encryption,
558    /// and MAC algorithms together (e.g., TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
559    pub cipher_suites: &'static [&'static dyn SupportedDtls12CipherSuite],
560
561    // =========================================================================
562    // DTLS 1.3 specific components
563    // =========================================================================
564    /// Supported DTLS 1.3 cipher suites (for negotiation).
565    ///
566    /// TLS 1.3 cipher suites only specify the AEAD and hash algorithms
567    /// (e.g., TLS_AES_128_GCM_SHA256). Key exchange is negotiated separately.
568    pub dtls13_cipher_suites: &'static [&'static dyn SupportedDtls13CipherSuite],
569}
570
571/// Static storage for the default crypto provider.
572///
573/// This is set by `install_default()` and retrieved by `get_default()`.
574static DEFAULT: OnceLock<CryptoProvider> = OnceLock::new();
575
576impl CryptoProvider {
577    /// Install a default crypto provider for the process.
578    ///
579    /// This sets a global default provider that will be used by
580    /// [`Config::builder()`](crate::Config::builder)
581    /// when no explicit provider is specified. This is useful for applications that want
582    /// to override the default provider per process.
583    ///
584    /// # Panics
585    ///
586    /// Panics if called more than once. The default provider can only be set once per process.
587    ///
588    /// # Example
589    ///
590    /// ```
591    /// # #[cfg(feature = "aws-lc-rs")]
592    /// # fn main() {
593    /// use dimpl::crypto::{CryptoProvider, aws_lc_rs};
594    ///
595    /// // Install a default provider (can only be called once per process)
596    /// CryptoProvider::install_default(aws_lc_rs::default_provider());
597    /// # }
598    /// # #[cfg(not(feature = "aws-lc-rs"))]
599    /// # fn main() {}
600    /// ```
601    pub fn install_default(provider: CryptoProvider) {
602        DEFAULT
603            .set(provider)
604            .expect("CryptoProvider::install_default() called more than once");
605    }
606
607    /// Get the default crypto provider, if one has been installed.
608    ///
609    /// Returns `Some(&provider)` if a default provider has been installed via
610    /// [`Self::install_default()`], or `None` if no default provider is available.
611    ///
612    /// This method does not panic. Use [`Config::builder()`](crate::Config::builder) which will handle
613    /// the fallback logic automatically.
614    ///
615    /// # Example
616    ///
617    /// ```
618    /// use dimpl::crypto::CryptoProvider;
619    ///
620    /// if let Some(provider) = CryptoProvider::get_default() {
621    ///     // Use the installed default provider
622    /// }
623    /// ```
624    pub fn get_default() -> Option<&'static CryptoProvider> {
625        DEFAULT.get()
626    }
627}
628
629#[cfg(test)]
630mod tests {
631    use super::*;
632
633    #[test]
634    #[cfg(feature = "rcgen")]
635    fn cert_named_group_p256() {
636        use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P256_SHA256};
637
638        let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap();
639        let params = CertificateParams::new(Vec::<String>::new()).unwrap();
640        let cert = params.self_signed(&key_pair).unwrap();
641
642        let group = cert_named_group(cert.der()).unwrap();
643        assert_eq!(group, NamedGroup::Secp256r1);
644    }
645
646    #[test]
647    #[cfg(feature = "rcgen")]
648    fn cert_named_group_p384() {
649        use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P384_SHA384};
650
651        let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
652        let params = CertificateParams::new(Vec::<String>::new()).unwrap();
653        let cert = params.self_signed(&key_pair).unwrap();
654
655        let group = cert_named_group(cert.der()).unwrap();
656        assert_eq!(group, NamedGroup::Secp384r1);
657    }
658
659    #[test]
660    #[cfg(feature = "rcgen")]
661    fn cert_named_group_invalid_der() {
662        let result = cert_named_group(&[0x00, 0x01, 0x02]);
663        assert!(result.is_err());
664    }
665}