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//! - **PRF Provider** ([`PrfProvider`]): TLS 1.2 PRF for key derivation
23//! - **HMAC Provider** ([`HmacProvider`]): Compute HMAC signatures
24//!
25//! # Using a Custom Provider
26//!
27//! To use a custom crypto provider, create one and pass it to the [`Config`](crate::Config):
28//!
29//! ```
30//! # #[cfg(all(feature = "aws-lc-rs", feature = "rcgen"))]
31//! # fn main() {
32//! use std::sync::Arc;
33//! use std::time::Instant;
34//! use dimpl::{Config, Dtls, certificate};
35//! use dimpl::crypto::aws_lc_rs;
36//!
37//! let cert = certificate::generate_self_signed_certificate().unwrap();
38//! // Use the default aws-lc-rs provider (implicit)
39//! let config = Arc::new(Config::default());
40//!
41//! // Or explicitly set the provider
42//! let config = Arc::new(
43//!     Config::builder()
44//!         .with_crypto_provider(aws_lc_rs::default_provider())
45//!         .build()
46//!         .unwrap()
47//! );
48//!
49//! // Or use your own custom provider
50//! // let config = Arc::new(
51//! //     Config::builder()
52//! //         .with_crypto_provider(my_custom_provider())
53//! //         .build()
54//! //         .unwrap()
55//! // );
56//!
57//! let dtls = Dtls::new_12(config, cert, Instant::now());
58//! # }
59//! # #[cfg(not(all(feature = "aws-lc-rs", feature = "rcgen")))]
60//! # fn main() {}
61//! ```
62//!
63//! # Implementing a Custom Provider
64//!
65//! To implement a custom provider, you need to:
66//!
67//! 1. Implement the required traits for your crypto backend
68//! 2. Create static instances of your implementations
69//! 3. Build a [`CryptoProvider`] struct with references to those statics
70//!
71//! ## Example: Custom Cipher Suite
72//!
73//! ```
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, String> {
83//!         Ok(Self)
84//!     }
85//! }
86//!
87//! impl Cipher for MyCipher {
88//!     fn encrypt(&mut self, _: &mut Buf, _: Aad, _: Nonce) -> Result<(), String> {
89//!         Ok(())
90//!     }
91//!     fn decrypt(&mut self, _: &mut TmpBuf, _: Aad, _: Nonce) -> Result<(), String> {
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>, String> {
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//! - **PRF**: TLS 1.2 PRF (using HMAC-SHA256 or HMAC-SHA384)
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};
153
154/// OID for the P-256 elliptic curve (secp256r1 / prime256v1).
155#[cfg(feature = "_crypto-common")]
156pub const OID_P256: spki::ObjectIdentifier =
157    spki::ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7");
158
159/// OID for the P-384 elliptic curve (secp384r1).
160#[cfg(feature = "_crypto-common")]
161pub const OID_P384: spki::ObjectIdentifier = spki::ObjectIdentifier::new_unwrap("1.3.132.0.34");
162
163// ============================================================================
164// Marker Trait
165// ============================================================================
166
167/// Marker trait for types that are safe to use in crypto provider components.
168///
169/// This trait combines the common bounds required for crypto provider trait objects:
170/// - [`Send`] + [`Sync`]: Thread-safe
171/// - [`Debug`]: Support debugging
172/// - [`UnwindSafe`] + [`RefUnwindSafe`]: Panic-safe
173///
174/// This trait is automatically implemented for all types that satisfy these bounds.
175pub trait CryptoSafe: Send + Sync + Debug + UnwindSafe + RefUnwindSafe {}
176
177/// Blanket implementation: any type satisfying the bounds implements [`CryptoSafe`].
178impl<T: Send + Sync + Debug + UnwindSafe + RefUnwindSafe> CryptoSafe for T {}
179
180// ============================================================================
181// Instance Traits (Level 2 - created by factories)
182// ============================================================================
183
184/// AEAD cipher for in-place encryption/decryption.
185pub trait Cipher: CryptoSafe {
186    /// Encrypt plaintext in-place, appending authentication tag.
187    fn encrypt(&mut self, plaintext: &mut Buf, aad: Aad, nonce: Nonce) -> Result<(), String>;
188
189    /// Decrypt ciphertext in-place, verifying and removing authentication tag.
190    fn decrypt(&mut self, ciphertext: &mut TmpBuf, aad: Aad, nonce: Nonce) -> Result<(), String>;
191}
192
193/// Stateful hash context for incremental hashing.
194pub trait HashContext: CryptoSafe {
195    /// Update the hash with new data.
196    fn update(&mut self, data: &[u8]);
197
198    /// Clone the context and finalize it, writing the hash to `out`.
199    /// The original context can continue to be updated.
200    fn clone_and_finalize(&self, out: &mut Buf);
201}
202
203/// Signing key for generating digital signatures.
204pub trait SigningKey: CryptoSafe {
205    /// Sign data and return the signature.
206    fn sign(&mut self, data: &[u8], out: &mut Buf) -> Result<(), String>;
207
208    /// Signature algorithm used by this key.
209    fn algorithm(&self) -> SignatureAlgorithm;
210
211    /// Default hash algorithm for this key.
212    fn hash_algorithm(&self) -> HashAlgorithm;
213}
214
215/// Active key exchange instance (ephemeral keypair for one handshake).
216pub trait ActiveKeyExchange: CryptoSafe {
217    /// Get the public key for this exchange.
218    fn pub_key(&self) -> &[u8];
219
220    /// Complete exchange with peer's public key, returning shared secret.
221    fn complete(self: Box<Self>, peer_pub: &[u8], out: &mut Buf) -> Result<(), String>;
222
223    /// Get the named group for this exchange.
224    fn group(&self) -> NamedGroup;
225}
226
227// ============================================================================
228// Factory Traits (Level 1 - used by CryptoProvider)
229// ============================================================================
230
231/// Cipher suite support (factory for Cipher instances).
232pub trait SupportedDtls12CipherSuite: CryptoSafe {
233    /// The cipher suite this supports.
234    fn suite(&self) -> Dtls12CipherSuite;
235
236    /// Hash algorithm used by this suite.
237    fn hash_algorithm(&self) -> HashAlgorithm;
238
239    /// Key material lengths: (mac_key_len, enc_key_len, fixed_iv_len).
240    fn key_lengths(&self) -> (usize, usize, usize);
241
242    /// Length in bytes of the per-record explicit nonce (carried in the record body).
243    ///
244    /// AES-GCM suites carry an 8-byte explicit nonce; ChaCha20-Poly1305 carries none.
245    fn explicit_nonce_len(&self) -> usize;
246
247    /// AEAD authentication tag length in bytes.
248    fn tag_len(&self) -> usize;
249
250    /// Create a cipher instance with the given key.
251    fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String>;
252}
253
254/// Key exchange group support (factory for ActiveKeyExchange).
255pub trait SupportedKxGroup: CryptoSafe {
256    /// Named group for this key exchange group.
257    fn name(&self) -> NamedGroup;
258
259    /// Start a new key exchange, generating ephemeral keypair.
260    /// The provided `buf` will be used to store the public key.
261    fn start_exchange(&self, buf: Buf) -> Result<Box<dyn ActiveKeyExchange>, String>;
262}
263
264/// Signature verification against certificates.
265pub trait SignatureVerifier: CryptoSafe {
266    /// Verify a signature on data using a DER-encoded X.509 certificate.
267    fn verify_signature(
268        &self,
269        cert_der: &[u8],
270        data: &[u8],
271        signature: &[u8],
272        hash_alg: HashAlgorithm,
273        sig_alg: SignatureAlgorithm,
274    ) -> Result<(), String>;
275}
276
277/// Allow-list of supported (signature, hash, curve) combinations for
278/// DTLS 1.2 signature verification.
279///
280/// In DTLS 1.2 the hash algorithm and the certificate's curve are
281/// independent choices, so all cross-combinations are valid.
282///
283///  Signature | Hash    | Curve
284/// -----------+---------+-----------
285///  ECDSA     | SHA-256 | P-256
286///  ECDSA     | SHA-256 | P-384
287///  ECDSA     | SHA-384 | P-256
288///  ECDSA     | SHA-384 | P-384
289const SUPPORTED_VERIFY_SCHEMES: &[(SignatureAlgorithm, HashAlgorithm, NamedGroup)] = &[
290    (
291        SignatureAlgorithm::ECDSA,
292        HashAlgorithm::SHA256,
293        NamedGroup::Secp256r1,
294    ),
295    (
296        SignatureAlgorithm::ECDSA,
297        HashAlgorithm::SHA256,
298        NamedGroup::Secp384r1,
299    ),
300    (
301        SignatureAlgorithm::ECDSA,
302        HashAlgorithm::SHA384,
303        NamedGroup::Secp256r1,
304    ),
305    (
306        SignatureAlgorithm::ECDSA,
307        HashAlgorithm::SHA384,
308        NamedGroup::Secp384r1,
309    ),
310];
311
312/// Check that a (signature, hash, curve) combination is in the allow-list.
313pub fn check_verify_scheme(
314    sig_alg: SignatureAlgorithm,
315    hash_alg: HashAlgorithm,
316    group: NamedGroup,
317) -> Result<(), String> {
318    if SUPPORTED_VERIFY_SCHEMES
319        .iter()
320        .any(|(s, h, g)| *s == sig_alg && *h == hash_alg && *g == group)
321    {
322        Ok(())
323    } else {
324        Err(format!(
325            "Unsupported signature verification: {:?} + {:?} + {:?}",
326            sig_alg, hash_alg, group
327        ))
328    }
329}
330
331/// Extract the EC curve ([`NamedGroup`]) from a DER-encoded X.509 certificate.
332///
333/// Used by DTLS 1.3 to verify that the [`SignatureScheme`](crate::types::SignatureScheme)
334/// in `CertificateVerify` is consistent with the peer's certificate key.
335#[cfg(feature = "_crypto-common")]
336pub fn cert_named_group(cert_der: &[u8]) -> Result<NamedGroup, String> {
337    use der::Decode;
338    use spki::ObjectIdentifier;
339    use x509_cert::Certificate as X509Certificate;
340
341    let cert = X509Certificate::from_der(cert_der)
342        .map_err(|e| format!("Failed to parse certificate: {e}"))?;
343    let spki = &cert.tbs_certificate.subject_public_key_info;
344
345    let curve_oid: ObjectIdentifier = spki
346        .algorithm
347        .parameters
348        .as_ref()
349        .ok_or("Missing EC curve parameter in certificate")?
350        .decode_as()
351        .map_err(|_| "Invalid EC curve parameter in certificate".to_string())?;
352
353    match curve_oid {
354        OID_P256 => Ok(NamedGroup::Secp256r1),
355        OID_P384 => Ok(NamedGroup::Secp384r1),
356        _ => Err(format!("Unsupported EC curve: {}", curve_oid)),
357    }
358}
359
360/// Private key parser (factory for SigningKey).
361pub trait KeyProvider: CryptoSafe {
362    /// Parse and load a private key from DER/PEM bytes.
363    fn load_private_key(&self, key_der: &[u8]) -> Result<Box<dyn SigningKey>, String>;
364}
365
366/// Secure random number generator.
367pub trait SecureRandom: CryptoSafe {
368    /// Fill buffer with cryptographically secure random bytes.
369    fn fill(&self, buf: &mut [u8]) -> Result<(), String>;
370}
371
372/// Hash provider (factory for HashContext).
373pub trait HashProvider: CryptoSafe {
374    /// Create a new hash context for the specified algorithm.
375    fn create_hash(&self, algorithm: HashAlgorithm) -> Box<dyn HashContext>;
376}
377
378/// PRF (Pseudo-Random Function) for TLS 1.2 key derivation.
379pub trait PrfProvider: CryptoSafe {
380    /// TLS 1.2 PRF: PRF(secret, label, seed) writing output to `out`.
381    /// Uses `scratch` for temporary concatenation of label+seed.
382    #[allow(clippy::too_many_arguments)]
383    fn prf_tls12(
384        &self,
385        secret: &[u8],
386        label: &str,
387        seed: &[u8],
388        out: &mut Buf,
389        output_len: usize,
390        scratch: &mut Buf,
391        hash: HashAlgorithm,
392    ) -> Result<(), String>;
393}
394
395/// HMAC provider for computing HMAC signatures.
396pub trait HmacProvider: CryptoSafe {
397    /// Compute HMAC-SHA256(key, data) and return the result.
398    fn hmac_sha256(&self, key: &[u8], data: &[u8]) -> Result<[u8; 32], String>;
399}
400
401// ============================================================================
402// DTLS 1.3 Factory Traits
403// ============================================================================
404
405/// Cipher suite support for DTLS 1.3 (factory for Cipher instances).
406///
407/// Unlike DTLS 1.2 cipher suites, TLS 1.3 cipher suites only specify the
408/// AEAD algorithm and hash function. Key exchange is negotiated separately.
409pub trait SupportedDtls13CipherSuite: CryptoSafe {
410    /// The cipher suite this supports.
411    fn suite(&self) -> Dtls13CipherSuite;
412
413    /// Hash algorithm used by this suite.
414    fn hash_algorithm(&self) -> HashAlgorithm;
415
416    /// AEAD key length in bytes.
417    fn key_len(&self) -> usize;
418
419    /// AEAD nonce/IV length in bytes.
420    fn iv_len(&self) -> usize;
421
422    /// AEAD tag length in bytes.
423    fn tag_len(&self) -> usize;
424
425    /// Create a cipher instance with the given key.
426    fn create_cipher(&self, key: &[u8]) -> Result<Box<dyn Cipher>, String>;
427
428    /// Compute a mask for record number encryption (RFC 9147 Section 4.2.3).
429    ///
430    /// The mask is XORed over the sequence number bytes in the header.
431    /// `sample` is the first 16 bytes of the ciphertext.
432    ///
433    /// For AES-based suites: `mask = AES-ECB(sn_key, sample)`.
434    ///
435    /// For ChaCha20-based suites (RFC 9001 Section 5.4.4):
436    /// `counter = sample[0..4]` (LE u32), `nonce = sample[4..16]`,
437    /// `mask = ChaCha20(sn_key, counter, nonce, <zero bytes>)`.
438    fn encrypt_sn(&self, sn_key: &[u8], sample: &[u8; 16]) -> [u8; 16];
439}
440
441/// HKDF provider for TLS 1.3 key derivation (RFC 5869).
442///
443/// TLS 1.3 uses HKDF instead of the TLS 1.2 PRF for all key derivation.
444pub trait HkdfProvider: CryptoSafe {
445    /// HKDF-Extract: Extract a pseudorandom key from input keying material.
446    /// PRK = HKDF-Extract(salt, IKM)
447    fn hkdf_extract(
448        &self,
449        hash: HashAlgorithm,
450        salt: &[u8],
451        ikm: &[u8],
452        out: &mut Buf,
453    ) -> Result<(), String>;
454
455    /// HKDF-Expand: Expand a pseudorandom key to the desired length.
456    /// OKM = HKDF-Expand(PRK, info, L)
457    fn hkdf_expand(
458        &self,
459        hash: HashAlgorithm,
460        prk: &[u8],
461        info: &[u8],
462        out: &mut Buf,
463        output_len: usize,
464    ) -> Result<(), String>;
465
466    /// HKDF-Expand-Label for TLS 1.3 (RFC 8446 Section 7.1).
467    /// Derives key material using the TLS 1.3 label format with "tls13 " prefix.
468    ///
469    /// HkdfLabel = struct {
470    ///     uint16 length;
471    ///     opaque label<7..255> = "tls13 " + Label;
472    ///     opaque context<0..255> = Context;
473    /// }
474    /// OKM = HKDF-Expand(Secret, HkdfLabel, Length)
475    fn hkdf_expand_label(
476        &self,
477        hash: HashAlgorithm,
478        secret: &[u8],
479        label: &[u8],
480        context: &[u8],
481        out: &mut Buf,
482        output_len: usize,
483    ) -> Result<(), String>;
484
485    /// HKDF-Expand-Label for DTLS 1.3 (RFC 9147).
486    /// Derives key material using the DTLS 1.3 label format with "dtls13" prefix.
487    ///
488    /// HkdfLabel = struct {
489    ///     uint16 length;
490    ///     opaque label<6..255> = "dtls13" + Label;
491    ///     opaque context<0..255> = Context;
492    /// }
493    /// OKM = HKDF-Expand(Secret, HkdfLabel, Length)
494    fn hkdf_expand_label_dtls13(
495        &self,
496        hash: HashAlgorithm,
497        secret: &[u8],
498        label: &[u8],
499        context: &[u8],
500        out: &mut Buf,
501        output_len: usize,
502    ) -> Result<(), String>;
503}
504
505// ============================================================================
506// Core Provider Struct
507// ============================================================================
508
509/// Cryptographic provider for DTLS operations.
510///
511/// This struct holds references to all cryptographic components needed
512/// for DTLS. Users can provide custom implementations of each component
513/// to replace the default aws-lc-rs-based provider.
514///
515/// # Version-Specific Components
516///
517/// Some components are version-specific:
518/// - **DTLS 1.2**: Uses `cipher_suites` and `prf_provider`
519/// - **DTLS 1.3**: Uses `dtls13_cipher_suites` and `hkdf_provider`
520///
521/// Shared components like `kx_groups`, `signature_verification`, `key_provider`,
522/// `secure_random`, `hash_provider`, and `hmac_provider` are used by both versions.
523///
524/// # Design
525///
526/// The provider uses static trait object references (`&'static dyn Trait`) which
527/// provides zero runtime overhead for trait dispatch. This design is inspired by
528/// rustls's CryptoProvider and ensures efficient crypto operations.
529///
530/// # Example
531///
532/// ```
533/// # #[cfg(feature = "aws-lc-rs")]
534/// # fn main() {
535/// use dimpl::crypto::{CryptoProvider, aws_lc_rs};
536///
537/// // Use the default provider
538/// let provider = aws_lc_rs::default_provider();
539///
540/// // Or build a custom one (using defaults for demonstration)
541/// let custom_provider = CryptoProvider {
542///     // Shared components
543///     kx_groups: provider.kx_groups,
544///     signature_verification: provider.signature_verification,
545///     key_provider: provider.key_provider,
546///     secure_random: provider.secure_random,
547///     hash_provider: provider.hash_provider,
548///     hmac_provider: provider.hmac_provider,
549///     // DTLS 1.2 components
550///     cipher_suites: provider.cipher_suites,
551///     prf_provider: provider.prf_provider,
552///     // DTLS 1.3 components
553///     dtls13_cipher_suites: provider.dtls13_cipher_suites,
554///     hkdf_provider: provider.hkdf_provider,
555/// };
556/// # }
557/// # #[cfg(not(feature = "aws-lc-rs"))]
558/// # fn main() {}
559/// ```
560#[derive(Debug, Clone)]
561pub struct CryptoProvider {
562    // =========================================================================
563    // Shared components (used by both DTLS 1.2 and DTLS 1.3)
564    // =========================================================================
565    /// Supported key exchange groups (P-256, P-384, X25519).
566    ///
567    /// Used for ECDHE key exchange in both DTLS versions.
568    pub kx_groups: &'static [&'static dyn SupportedKxGroup],
569
570    /// Signature verification for certificates.
571    pub signature_verification: &'static dyn SignatureVerifier,
572
573    /// Key provider for parsing private keys.
574    pub key_provider: &'static dyn KeyProvider,
575
576    /// Secure random number generator.
577    pub secure_random: &'static dyn SecureRandom,
578
579    /// Hash provider for handshake hashing.
580    pub hash_provider: &'static dyn HashProvider,
581
582    /// HMAC provider for computing HMAC signatures.
583    pub hmac_provider: &'static dyn HmacProvider,
584
585    // =========================================================================
586    // DTLS 1.2 specific components
587    // =========================================================================
588    /// Supported DTLS 1.2 cipher suites (for negotiation).
589    ///
590    /// These cipher suites bundle key exchange, authentication, encryption,
591    /// and MAC algorithms together (e.g., TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256).
592    pub cipher_suites: &'static [&'static dyn SupportedDtls12CipherSuite],
593
594    /// PRF for TLS 1.2 key derivation.
595    ///
596    /// The Pseudo-Random Function used for key expansion in DTLS 1.2.
597    pub prf_provider: &'static dyn PrfProvider,
598
599    // =========================================================================
600    // DTLS 1.3 specific components
601    // =========================================================================
602    /// Supported DTLS 1.3 cipher suites (for negotiation).
603    ///
604    /// TLS 1.3 cipher suites only specify the AEAD and hash algorithms
605    /// (e.g., TLS_AES_128_GCM_SHA256). Key exchange is negotiated separately.
606    pub dtls13_cipher_suites: &'static [&'static dyn SupportedDtls13CipherSuite],
607
608    /// HKDF provider for TLS 1.3 key derivation.
609    ///
610    /// TLS 1.3 uses HKDF instead of the TLS 1.2 PRF for all key derivation.
611    pub hkdf_provider: &'static dyn HkdfProvider,
612}
613
614/// Static storage for the default crypto provider.
615///
616/// This is set by `install_default()` and retrieved by `get_default()`.
617static DEFAULT: OnceLock<CryptoProvider> = OnceLock::new();
618
619impl CryptoProvider {
620    /// Install a default crypto provider for the process.
621    ///
622    /// This sets a global default provider that will be used by
623    /// [`Config::builder()`](crate::Config::builder)
624    /// when no explicit provider is specified. This is useful for applications that want
625    /// to override the default provider per process.
626    ///
627    /// # Panics
628    ///
629    /// Panics if called more than once. The default provider can only be set once per process.
630    ///
631    /// # Example
632    ///
633    /// ```
634    /// # #[cfg(feature = "aws-lc-rs")]
635    /// # fn main() {
636    /// use dimpl::crypto::{CryptoProvider, aws_lc_rs};
637    ///
638    /// // Install a default provider (can only be called once per process)
639    /// CryptoProvider::install_default(aws_lc_rs::default_provider());
640    /// # }
641    /// # #[cfg(not(feature = "aws-lc-rs"))]
642    /// # fn main() {}
643    /// ```
644    pub fn install_default(provider: CryptoProvider) {
645        DEFAULT
646            .set(provider)
647            .expect("CryptoProvider::install_default() called more than once");
648    }
649
650    /// Get the default crypto provider, if one has been installed.
651    ///
652    /// Returns `Some(&provider)` if a default provider has been installed via
653    /// [`Self::install_default()`], or `None` if no default provider is available.
654    ///
655    /// This method does not panic. Use [`Config::builder()`](crate::Config::builder) which will handle
656    /// the fallback logic automatically.
657    ///
658    /// # Example
659    ///
660    /// ```
661    /// use dimpl::crypto::CryptoProvider;
662    ///
663    /// if let Some(provider) = CryptoProvider::get_default() {
664    ///     // Use the installed default provider
665    /// }
666    /// ```
667    pub fn get_default() -> Option<&'static CryptoProvider> {
668        DEFAULT.get()
669    }
670}
671
672#[cfg(test)]
673mod tests {
674    use super::*;
675
676    #[test]
677    #[cfg(feature = "rcgen")]
678    fn cert_named_group_p256() {
679        use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P256_SHA256};
680
681        let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap();
682        let params = CertificateParams::new(Vec::<String>::new()).unwrap();
683        let cert = params.self_signed(&key_pair).unwrap();
684
685        let group = cert_named_group(cert.der()).unwrap();
686        assert_eq!(group, NamedGroup::Secp256r1);
687    }
688
689    #[test]
690    #[cfg(feature = "rcgen")]
691    fn cert_named_group_p384() {
692        use rcgen::{CertificateParams, KeyPair, PKCS_ECDSA_P384_SHA384};
693
694        let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
695        let params = CertificateParams::new(Vec::<String>::new()).unwrap();
696        let cert = params.self_signed(&key_pair).unwrap();
697
698        let group = cert_named_group(cert.der()).unwrap();
699        assert_eq!(group, NamedGroup::Secp384r1);
700    }
701
702    #[test]
703    #[cfg(feature = "rcgen")]
704    fn cert_named_group_invalid_der() {
705        let result = cert_named_group(&[0x00, 0x01, 0x02]);
706        assert!(result.is_err());
707    }
708}