11use std:: time:: Duration ;
22
3- use crate :: crypto:: CryptoProvider ;
3+ use crate :: crypto:: { CryptoProvider , SupportedDtls12CipherSuite } ;
4+ use crate :: crypto:: { SupportedDtls13CipherSuite , SupportedKxGroup } ;
5+ use crate :: dtls12:: message:: Dtls12CipherSuite ;
6+ use crate :: types:: { Dtls13CipherSuite , NamedGroup } ;
47use crate :: Error ;
58
69#[ cfg( feature = "aws-lc-rs" ) ]
@@ -25,6 +28,9 @@ pub struct Config {
2528 crypto_provider : CryptoProvider ,
2629 rng_seed : Option < u64 > ,
2730 aead_encryption_limit : u64 ,
31+ dtls12_cipher_suites : Option < Vec < Dtls12CipherSuite > > ,
32+ dtls13_cipher_suites : Option < Vec < Dtls13CipherSuite > > ,
33+ kx_groups : Option < Vec < NamedGroup > > ,
2834}
2935
3036impl Config {
@@ -42,6 +48,9 @@ impl Config {
4248 crypto_provider : None ,
4349 rng_seed : None ,
4450 aead_encryption_limit : 1 << 23 ,
51+ dtls12_cipher_suites : None ,
52+ dtls13_cipher_suites : None ,
53+ kx_groups : None ,
4554 }
4655 }
4756
@@ -138,6 +147,66 @@ impl Config {
138147 pub fn aead_encryption_limit ( & self ) -> u64 {
139148 self . aead_encryption_limit
140149 }
150+
151+ /// Allowed DTLS 1.2 cipher suites, filtered by the config's allow-list.
152+ ///
153+ /// Returns all provider-supported DTLS 1.2 cipher suites when no filter
154+ /// is set. When a filter is set via the builder's `dtls12_cipher_suites`
155+ /// method, only suites in both the provider and the filter are returned.
156+ pub fn dtls12_cipher_suites (
157+ & self ,
158+ ) -> impl Iterator < Item = & ' static dyn SupportedDtls12CipherSuite > + ' _ {
159+ let filter = self . dtls12_cipher_suites . as_ref ( ) ;
160+ self . crypto_provider
161+ . supported_cipher_suites ( )
162+ . filter ( move |cs| match filter {
163+ Some ( list) => list. contains ( & cs. suite ( ) ) ,
164+ None => true ,
165+ } )
166+ }
167+
168+ /// Allowed DTLS 1.3 cipher suites, filtered by the config's allow-list.
169+ ///
170+ /// Returns all provider DTLS 1.3 cipher suites when no filter is set.
171+ /// When a filter is set via the builder's `dtls13_cipher_suites` method,
172+ /// only suites in both the provider and the filter are returned.
173+ pub fn dtls13_cipher_suites (
174+ & self ,
175+ ) -> impl Iterator < Item = & ' static dyn SupportedDtls13CipherSuite > + ' _ {
176+ let filter = self . dtls13_cipher_suites . as_ref ( ) ;
177+ self . crypto_provider
178+ . dtls13_cipher_suites
179+ . iter ( )
180+ . copied ( )
181+ . filter ( move |cs| match filter {
182+ Some ( list) => list. contains ( & cs. suite ( ) ) ,
183+ None => true ,
184+ } )
185+ }
186+
187+ /// Allowed key exchange groups, filtered by the config's allow-list.
188+ ///
189+ /// Returns all provider-supported key exchange groups when no filter
190+ /// is set. When a filter is set via the builder's `kx_groups` method,
191+ /// only groups in both the provider and the filter are returned.
192+ pub fn kx_groups ( & self ) -> impl Iterator < Item = & ' static dyn SupportedKxGroup > + ' _ {
193+ let filter = self . kx_groups . as_ref ( ) ;
194+ self . crypto_provider
195+ . supported_kx_groups ( )
196+ . filter ( move |kx| match filter {
197+ Some ( list) => list. contains ( & kx. name ( ) ) ,
198+ None => true ,
199+ } )
200+ }
201+
202+ /// Allowed key exchange groups for DTLS 1.2.
203+ ///
204+ /// Like [`kx_groups`](Self::kx_groups) but additionally restricted to
205+ /// groups that DTLS 1.2 supports (currently P-256 and P-384).
206+ pub fn dtls12_kx_groups ( & self ) -> impl Iterator < Item = & ' static dyn SupportedKxGroup > + ' _ {
207+ self . kx_groups ( )
208+ . filter ( |kx| matches ! ( kx. name( ) , NamedGroup :: Secp256r1 | NamedGroup :: Secp384r1 ) )
209+ }
141210}
142211
143212/// Builder for [`Config`]. See each setter for defaults.
@@ -153,6 +222,9 @@ pub struct ConfigBuilder {
153222 crypto_provider : Option < CryptoProvider > ,
154223 rng_seed : Option < u64 > ,
155224 aead_encryption_limit : u64 ,
225+ dtls12_cipher_suites : Option < Vec < Dtls12CipherSuite > > ,
226+ dtls13_cipher_suites : Option < Vec < Dtls13CipherSuite > > ,
227+ kx_groups : Option < Vec < NamedGroup > > ,
156228}
157229
158230impl ConfigBuilder {
@@ -261,6 +333,41 @@ impl ConfigBuilder {
261333 self
262334 }
263335
336+ /// Restrict which DTLS 1.2 cipher suites are offered and accepted.
337+ ///
338+ /// Only cipher suites present in both this list and the provider will
339+ /// be used. Passing an empty slice disables DTLS 1.2 (as long as
340+ /// DTLS 1.3 suites remain).
341+ ///
342+ /// By default all provider-supported DTLS 1.2 cipher suites are used.
343+ pub fn dtls12_cipher_suites ( mut self , suites : & [ Dtls12CipherSuite ] ) -> Self {
344+ self . dtls12_cipher_suites = Some ( suites. to_vec ( ) ) ;
345+ self
346+ }
347+
348+ /// Restrict which DTLS 1.3 cipher suites are offered and accepted.
349+ ///
350+ /// Only cipher suites present in both this list and the provider will
351+ /// be used. Passing an empty slice disables DTLS 1.3 (as long as
352+ /// DTLS 1.2 suites remain).
353+ ///
354+ /// By default all provider DTLS 1.3 cipher suites are used.
355+ pub fn dtls13_cipher_suites ( mut self , suites : & [ Dtls13CipherSuite ] ) -> Self {
356+ self . dtls13_cipher_suites = Some ( suites. to_vec ( ) ) ;
357+ self
358+ }
359+
360+ /// Restrict which key exchange groups are offered and accepted.
361+ ///
362+ /// Only groups present in both this list and the provider will be
363+ /// used. Order determines preference (first = most preferred).
364+ ///
365+ /// By default all provider-supported key exchange groups are used.
366+ pub fn kx_groups ( mut self , groups : & [ NamedGroup ] ) -> Self {
367+ self . kx_groups = Some ( groups. to_vec ( ) ) ;
368+ self
369+ }
370+
264371 /// Build the configuration.
265372 ///
266373 /// This validates the crypto provider before returning the configuration.
@@ -307,6 +414,63 @@ impl ConfigBuilder {
307414 ) ) ;
308415 }
309416
417+ // Validate cipher suite filters: at least one version must have suites
418+ let dtls12_count = {
419+ let all = crypto_provider. supported_cipher_suites ( ) ;
420+ match & self . dtls12_cipher_suites {
421+ Some ( list) => all. filter ( |cs| list. contains ( & cs. suite ( ) ) ) . count ( ) ,
422+ None => all. count ( ) ,
423+ }
424+ } ;
425+ let dtls13_count = {
426+ let all = crypto_provider. dtls13_cipher_suites . iter ( ) ;
427+ match & self . dtls13_cipher_suites {
428+ Some ( list) => all. filter ( |cs| list. contains ( & cs. suite ( ) ) ) . count ( ) ,
429+ None => all. count ( ) ,
430+ }
431+ } ;
432+ if dtls12_count + dtls13_count == 0 {
433+ return Err ( Error :: ConfigError (
434+ "No cipher suites remain after filtering. \
435+ At least one DTLS 1.2 or DTLS 1.3 cipher suite must be available."
436+ . to_string ( ) ,
437+ ) ) ;
438+ }
439+
440+ // Validate kx_groups filter: each enabled version needs compatible groups
441+ let filtered_kx = |kx : & & ' static dyn SupportedKxGroup | -> bool {
442+ match & self . kx_groups {
443+ Some ( list) => list. contains ( & kx. name ( ) ) ,
444+ None => true ,
445+ }
446+ } ;
447+ if dtls12_count > 0 {
448+ let dtls12_kx_count = crypto_provider
449+ . supported_dtls12_kx_groups ( )
450+ . filter ( |kx| filtered_kx ( kx) )
451+ . count ( ) ;
452+ if dtls12_kx_count == 0 {
453+ return Err ( Error :: ConfigError (
454+ "DTLS 1.2 cipher suites are enabled but no compatible key exchange \
455+ groups remain after filtering. DTLS 1.2 requires P-256 or P-384."
456+ . to_string ( ) ,
457+ ) ) ;
458+ }
459+ }
460+ if dtls13_count > 0 {
461+ let kx_count = crypto_provider
462+ . supported_kx_groups ( )
463+ . filter ( |kx| filtered_kx ( kx) )
464+ . count ( ) ;
465+ if kx_count == 0 {
466+ return Err ( Error :: ConfigError (
467+ "DTLS 1.3 cipher suites are enabled but no key exchange groups \
468+ remain after filtering."
469+ . to_string ( ) ,
470+ ) ) ;
471+ }
472+ }
473+
310474 Ok ( Config {
311475 mtu : self . mtu ,
312476 max_queue_rx : self . max_queue_rx ,
@@ -319,6 +483,9 @@ impl ConfigBuilder {
319483 crypto_provider,
320484 rng_seed : self . rng_seed ,
321485 aead_encryption_limit : self . aead_encryption_limit ,
486+ dtls12_cipher_suites : self . dtls12_cipher_suites ,
487+ dtls13_cipher_suites : self . dtls13_cipher_suites ,
488+ kx_groups : self . kx_groups ,
322489 } )
323490 }
324491}
@@ -384,4 +551,158 @@ mod tests {
384551 . build ( )
385552 . expect ( "aead_encryption_limit 1 should be accepted" ) ;
386553 }
554+
555+ #[ test]
556+ fn filter_dtls12_cipher_suite ( ) {
557+ let config = Config :: builder ( )
558+ . dtls12_cipher_suites ( & [ Dtls12CipherSuite :: ECDHE_ECDSA_AES128_GCM_SHA256 ] )
559+ . build ( )
560+ . expect ( "should accept single DTLS 1.2 suite" ) ;
561+ let suites: Vec < _ > = config. dtls12_cipher_suites ( ) . map ( |cs| cs. suite ( ) ) . collect ( ) ;
562+ assert_eq ! ( suites, & [ Dtls12CipherSuite :: ECDHE_ECDSA_AES128_GCM_SHA256 ] ) ;
563+ }
564+
565+ #[ test]
566+ fn filter_dtls13_cipher_suite ( ) {
567+ let config = Config :: builder ( )
568+ . dtls13_cipher_suites ( & [ Dtls13CipherSuite :: AES_256_GCM_SHA384 ] )
569+ . build ( )
570+ . expect ( "should accept single DTLS 1.3 suite" ) ;
571+ let suites: Vec < _ > = config. dtls13_cipher_suites ( ) . map ( |cs| cs. suite ( ) ) . collect ( ) ;
572+ assert_eq ! ( suites, & [ Dtls13CipherSuite :: AES_256_GCM_SHA384 ] ) ;
573+ }
574+
575+ #[ test]
576+ fn filter_kx_groups ( ) {
577+ let config = Config :: builder ( )
578+ . kx_groups ( & [ NamedGroup :: Secp256r1 ] )
579+ . build ( )
580+ . expect ( "should accept single kx group" ) ;
581+ let groups: Vec < _ > = config. kx_groups ( ) . map ( |g| g. name ( ) ) . collect ( ) ;
582+ assert_eq ! ( groups, & [ NamedGroup :: Secp256r1 ] ) ;
583+ }
584+
585+ #[ test]
586+ fn empty_dtls12_filter_disables_version ( ) {
587+ let config = Config :: builder ( )
588+ . dtls12_cipher_suites ( & [ ] )
589+ . build ( )
590+ . expect ( "should accept empty DTLS 1.2 when 1.3 has suites" ) ;
591+ assert_eq ! ( config. dtls12_cipher_suites( ) . count( ) , 0 ) ;
592+ assert ! ( config. dtls13_cipher_suites( ) . count( ) > 0 ) ;
593+ }
594+
595+ #[ test]
596+ fn empty_dtls13_filter_disables_version ( ) {
597+ let config = Config :: builder ( )
598+ . dtls13_cipher_suites ( & [ ] )
599+ . build ( )
600+ . expect ( "should accept empty DTLS 1.3 when 1.2 has suites" ) ;
601+ assert ! ( config. dtls12_cipher_suites( ) . count( ) > 0 ) ;
602+ assert_eq ! ( config. dtls13_cipher_suites( ) . count( ) , 0 ) ;
603+ }
604+
605+ #[ test]
606+ fn both_empty_filters_rejected ( ) {
607+ match Config :: builder ( )
608+ . dtls12_cipher_suites ( & [ ] )
609+ . dtls13_cipher_suites ( & [ ] )
610+ . build ( )
611+ {
612+ Err ( Error :: ConfigError ( msg) ) => {
613+ assert ! (
614+ msg. contains( "No cipher suites" ) ,
615+ "error should mention cipher suites: {msg}"
616+ )
617+ }
618+ Err ( other) => panic ! ( "expected ConfigError, got: {other:?}" ) ,
619+ Ok ( _) => panic ! ( "expected error when both versions are empty" ) ,
620+ }
621+ }
622+
623+ #[ test]
624+ fn empty_kx_groups_filter_rejected ( ) {
625+ match Config :: builder ( ) . kx_groups ( & [ ] ) . build ( ) {
626+ Err ( Error :: ConfigError ( msg) ) => {
627+ assert ! (
628+ msg. contains( "key exchange" ) ,
629+ "error should mention key exchange: {msg}"
630+ )
631+ }
632+ Err ( other) => panic ! ( "expected ConfigError, got: {other:?}" ) ,
633+ Ok ( _) => panic ! ( "expected error for empty kx groups" ) ,
634+ }
635+ }
636+
637+ #[ test]
638+ fn x25519_only_rejected_for_dtls12 ( ) {
639+ // X25519 is not yet supported for DTLS 1.2, so filtering to X25519-only
640+ // while DTLS 1.2 suites are enabled should fail.
641+ match Config :: builder ( )
642+ . dtls13_cipher_suites ( & [ ] )
643+ . kx_groups ( & [ NamedGroup :: X25519 ] )
644+ . build ( )
645+ {
646+ Err ( Error :: ConfigError ( msg) ) => {
647+ assert ! (
648+ msg. contains( "DTLS 1.2" ) && msg. contains( "P-256 or P-384" ) ,
649+ "error should mention DTLS 1.2 and required groups: {msg}"
650+ )
651+ }
652+ Err ( other) => panic ! ( "expected ConfigError, got: {other:?}" ) ,
653+ Ok ( _) => panic ! ( "expected error for X25519-only with DTLS 1.2" ) ,
654+ }
655+ }
656+
657+ #[ test]
658+ fn x25519_only_accepted_for_dtls13_only ( ) {
659+ // X25519-only is fine when DTLS 1.2 is disabled.
660+ let config = Config :: builder ( )
661+ . dtls12_cipher_suites ( & [ ] )
662+ . kx_groups ( & [ NamedGroup :: X25519 ] )
663+ . build ( )
664+ . expect ( "X25519-only should be accepted for DTLS 1.3-only config" ) ;
665+ let groups: Vec < _ > = config. kx_groups ( ) . map ( |g| g. name ( ) ) . collect ( ) ;
666+ assert_eq ! ( groups, & [ NamedGroup :: X25519 ] ) ;
667+ }
668+
669+ #[ test]
670+ fn dtls12_kx_groups_excludes_x25519 ( ) {
671+ let config = Config :: default ( ) ;
672+ let dtls12_groups: Vec < _ > = config. dtls12_kx_groups ( ) . map ( |g| g. name ( ) ) . collect ( ) ;
673+ assert ! ( !dtls12_groups. contains( & NamedGroup :: X25519 ) ) ;
674+ assert ! ( dtls12_groups. contains( & NamedGroup :: Secp256r1 ) ) ;
675+ }
676+
677+ #[ test]
678+ fn no_filter_returns_all ( ) {
679+ let config = Config :: default ( ) ;
680+ // Default provider should have at least 2 DTLS 1.2 and 2 DTLS 1.3 suites
681+ assert ! ( config. dtls12_cipher_suites( ) . count( ) >= 2 ) ;
682+ assert ! ( config. dtls13_cipher_suites( ) . count( ) >= 2 ) ;
683+ assert ! ( config. kx_groups( ) . count( ) >= 2 ) ;
684+ }
685+
686+ #[ test]
687+ fn filter_with_explicit_provider ( ) {
688+ #[ cfg( feature = "aws-lc-rs" ) ]
689+ {
690+ let config = Config :: builder ( )
691+ . with_crypto_provider ( aws_lc_rs:: default_provider ( ) )
692+ . dtls12_cipher_suites ( & [ Dtls12CipherSuite :: ECDHE_ECDSA_AES256_GCM_SHA384 ] )
693+ . dtls13_cipher_suites ( & [ Dtls13CipherSuite :: AES_128_GCM_SHA256 ] )
694+ . kx_groups ( & [ NamedGroup :: X25519 , NamedGroup :: Secp256r1 ] )
695+ . build ( )
696+ . expect ( "should accept filtered config with explicit provider" ) ;
697+ let suites12: Vec < _ > = config. dtls12_cipher_suites ( ) . map ( |cs| cs. suite ( ) ) . collect ( ) ;
698+ assert_eq ! (
699+ suites12,
700+ & [ Dtls12CipherSuite :: ECDHE_ECDSA_AES256_GCM_SHA384 ]
701+ ) ;
702+ let suites13: Vec < _ > = config. dtls13_cipher_suites ( ) . map ( |cs| cs. suite ( ) ) . collect ( ) ;
703+ assert_eq ! ( suites13, & [ Dtls13CipherSuite :: AES_128_GCM_SHA256 ] ) ;
704+ let groups: Vec < _ > = config. kx_groups ( ) . map ( |g| g. name ( ) ) . collect ( ) ;
705+ assert_eq ! ( groups, & [ NamedGroup :: X25519 , NamedGroup :: Secp256r1 ] ) ;
706+ }
707+ }
387708}
0 commit comments