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