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}