@@ -11,13 +11,238 @@ use self::openssl::ssl::{
1111 SslVerifyMode ,
1212} ;
1313use self :: openssl:: x509:: { store:: X509StoreBuilder , X509VerifyResult , X509 } ;
14+ use std:: borrow;
15+ use std:: collections:: HashSet ;
1416use std:: error;
1517use std:: fmt;
1618use std:: io;
1719use std:: sync:: Once ;
1820
1921use self :: openssl:: pkey:: Private ;
20- use { Protocol , TlsAcceptorBuilder , TlsConnectorBuilder } ;
22+ use {
23+ CipherSuiteSet , Protocol , TlsAcceptorBuilder , TlsBulkEncryptionAlgorithm , TlsConnectorBuilder ,
24+ TlsHashAlgorithm , TlsKeyExchangeAlgorithm , TlsSignatureAlgorithm ,
25+ } ;
26+
27+ const CIPHER_STRING_SUFFIX : & [ & str ] = & [
28+ "!aNULL" ,
29+ "!eNULL" ,
30+ "!IDEA" ,
31+ "!SEED" ,
32+ "!SRP" ,
33+ "!PSK" ,
34+ "@STRENGTH" ,
35+ ] ;
36+
37+ fn cartesian_product (
38+ xs : impl IntoIterator < Item = Vec < & ' static str > > ,
39+ ys : impl IntoIterator < Item = & ' static str > + Clone ,
40+ ) -> Vec < Vec < & ' static str > > {
41+ xs. into_iter ( )
42+ . flat_map ( move |x| ys. clone ( ) . into_iter ( ) . map ( move |y| [ & x, & [ y] [ ..] ] . concat ( ) ) )
43+ . collect ( )
44+ }
45+
46+ /// AES-GCM ciphersuites aren't included in AES128 or AES256. However, specifying `AESGCM` in the
47+ /// cipher string doesn't allow us to specify the bitwidth of the AES cipher used, nor does it
48+ /// allow us to specify the bitwidth of the SHA algorithm.
49+ fn expand_gcm_algorithms ( cipher_suites : & CipherSuiteSet ) -> Vec < & ' static str > {
50+ let first = cipher_suites
51+ . key_exchange
52+ . iter ( )
53+ . flat_map ( |alg| -> & [ & str ] {
54+ match alg {
55+ TlsKeyExchangeAlgorithm :: Dhe => & [
56+ "DHE-RSA-AES128-GCM-SHA256" ,
57+ "DHE-RSA-AES256-GCM-SHA384" ,
58+ "DHE-DSS-AES128-GCM-SHA256" ,
59+ "DHE-DSS-AES256-GCM-SHA384" ,
60+ ] ,
61+ TlsKeyExchangeAlgorithm :: Ecdhe => & [
62+ "ECDHE-RSA-AES128-GCM-SHA256" ,
63+ "ECDHE-RSA-AES256-GCM-SHA384" ,
64+ "ECDHE-ECDSA-AES128-GCM-SHA256" ,
65+ "ECDHE-ECDSA-AES256-GCM-SHA384" ,
66+ ] ,
67+ TlsKeyExchangeAlgorithm :: Rsa => & [ "AES128-GCM-SHA256" , "AES256-GCM-SHA384" ] ,
68+ TlsKeyExchangeAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
69+ }
70+ } )
71+ . copied ( ) ;
72+ let rest: & [ HashSet < _ > ] = & [
73+ cipher_suites
74+ . signature
75+ . iter ( )
76+ . flat_map ( |alg| -> & [ & str ] {
77+ match alg {
78+ TlsSignatureAlgorithm :: Dss => & [
79+ "DH-DSS-AES128-GCM-SHA256" ,
80+ "DH-DSS-AES256-GCM-SHA384" ,
81+ "DHE-DSS-AES128-GCM-SHA256" ,
82+ "DHE-DSS-AES256-GCM-SHA384" ,
83+ ] ,
84+ TlsSignatureAlgorithm :: Ecdsa => & [
85+ "ECDH-ECDSA-AES128-GCM-SHA256" ,
86+ "ECDH-ECDSA-AES256-GCM-SHA384" ,
87+ "ECDHE-ECDSA-AES128-GCM-SHA256" ,
88+ "ECDHE-ECDSA-AES256-GCM-SHA384" ,
89+ ] ,
90+ TlsSignatureAlgorithm :: Rsa => & [
91+ "AES128-GCM-SHA256" ,
92+ "AES256-GCM-SHA384" ,
93+ "DH-RSA-AES128-GCM-SHA256" ,
94+ "DH-RSA-AES256-GCM-SHA384" ,
95+ "DHE-RSA-AES128-GCM-SHA256" ,
96+ "DHE-RSA-AES256-GCM-SHA384" ,
97+ "ECDH-RSA-AES128-GCM-SHA256" ,
98+ "ECDH-RSA-AES256-GCM-SHA384" ,
99+ "ECDHE-RSA-AES128-GCM-SHA256" ,
100+ "ECDHE-RSA-AES256-GCM-SHA384" ,
101+ ] ,
102+ TlsSignatureAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
103+ }
104+ } )
105+ . copied ( )
106+ . collect ( ) ,
107+ cipher_suites
108+ . bulk_encryption
109+ . iter ( )
110+ . flat_map ( |alg| -> & [ & str ] {
111+ match alg {
112+ TlsBulkEncryptionAlgorithm :: Aes128 => & [
113+ "AES128-GCM-SHA256" ,
114+ "DH-RSA-AES128-GCM-SHA256" ,
115+ "DH-DSS-AES128-GCM-SHA256" ,
116+ "DHE-RSA-AES128-GCM-SHA256" ,
117+ "DHE-DSS-AES128-GCM-SHA256" ,
118+ "ECDH-RSA-AES128-GCM-SHA256" ,
119+ "ECDH-ECDSA-AES128-GCM-SHA256" ,
120+ "ECDHE-RSA-AES128-GCM-SHA256" ,
121+ "ECDHE-ECDSA-AES128-GCM-SHA256" ,
122+ ] ,
123+ TlsBulkEncryptionAlgorithm :: Aes256 => & [
124+ "AES256-GCM-SHA384" ,
125+ "DH-RSA-AES256-GCM-SHA384" ,
126+ "DH-DSS-AES256-GCM-SHA384" ,
127+ "DHE-RSA-AES256-GCM-SHA384" ,
128+ "DHE-DSS-AES256-GCM-SHA384" ,
129+ "ECDH-RSA-AES256-GCM-SHA384" ,
130+ "ECDH-ECDSA-AES256-GCM-SHA384" ,
131+ "ECDHE-RSA-AES256-GCM-SHA384" ,
132+ "ECDHE-ECDSA-AES256-GCM-SHA384" ,
133+ ] ,
134+ TlsBulkEncryptionAlgorithm :: Des => & [ ] ,
135+ TlsBulkEncryptionAlgorithm :: Rc2 => & [ ] ,
136+ TlsBulkEncryptionAlgorithm :: Rc4 => & [ ] ,
137+ TlsBulkEncryptionAlgorithm :: TripleDes => & [ ] ,
138+ TlsBulkEncryptionAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
139+ }
140+ } )
141+ . copied ( )
142+ . collect ( ) ,
143+ cipher_suites
144+ . hash
145+ . iter ( )
146+ . flat_map ( |alg| -> & [ & str ] {
147+ match alg {
148+ TlsHashAlgorithm :: Md5 => & [ ] ,
149+ TlsHashAlgorithm :: Sha1 => & [ ] ,
150+ TlsHashAlgorithm :: Sha256 => & [
151+ "AES128-GCM-SHA256" ,
152+ "DH-RSA-AES128-GCM-SHA256" ,
153+ "DH-DSS-AES128-GCM-SHA256" ,
154+ "DHE-RSA-AES128-GCM-SHA256" ,
155+ "DHE-DSS-AES128-GCM-SHA256" ,
156+ "ECDH-RSA-AES128-GCM-SHA256" ,
157+ "ECDH-ECDSA-AES128-GCM-SHA256" ,
158+ "ECDHE-RSA-AES128-GCM-SHA256" ,
159+ "ECDHE-ECDSA-AES128-GCM-SHA256" ,
160+ ] ,
161+ TlsHashAlgorithm :: Sha384 => & [
162+ "AES256-GCM-SHA384" ,
163+ "DH-RSA-AES256-GCM-SHA384" ,
164+ "DH-DSS-AES256-GCM-SHA384" ,
165+ "DHE-RSA-AES256-GCM-SHA384" ,
166+ "DHE-DSS-AES256-GCM-SHA384" ,
167+ "ECDH-RSA-AES256-GCM-SHA384" ,
168+ "ECDH-ECDSA-AES256-GCM-SHA384" ,
169+ "ECDHE-RSA-AES256-GCM-SHA384" ,
170+ "ECDHE-ECDSA-AES256-GCM-SHA384" ,
171+ ] ,
172+ TlsHashAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
173+ }
174+ } )
175+ . copied ( )
176+ . collect ( ) ,
177+ ] ;
178+
179+ first
180+ . filter ( |alg| rest. iter ( ) . all ( |algs| algs. contains ( alg) ) )
181+ . collect ( )
182+ }
183+
184+ fn expand_algorithms ( cipher_suites : & CipherSuiteSet ) -> String {
185+ let mut cipher_suite_strings: Vec < Vec < & ' static str > > = vec ! [ ] ;
186+
187+ cipher_suite_strings. extend ( cipher_suites. key_exchange . iter ( ) . map ( |alg| {
188+ vec ! [ match alg {
189+ TlsKeyExchangeAlgorithm :: Dhe => "DHE" ,
190+ TlsKeyExchangeAlgorithm :: Ecdhe => "ECDHE" ,
191+ TlsKeyExchangeAlgorithm :: Rsa => "kRSA" ,
192+ TlsKeyExchangeAlgorithm :: __NonExhaustive => unreachable!( ) ,
193+ } ]
194+ } ) ) ;
195+
196+ cipher_suite_strings = cartesian_product (
197+ cipher_suite_strings,
198+ cipher_suites. signature . iter ( ) . map ( |alg| match alg {
199+ TlsSignatureAlgorithm :: Dss => "aDSS" ,
200+ TlsSignatureAlgorithm :: Ecdsa => "aECDSA" ,
201+ TlsSignatureAlgorithm :: Rsa => "aRSA" ,
202+ TlsSignatureAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
203+ } ) ,
204+ ) ;
205+ cipher_suite_strings = cartesian_product (
206+ cipher_suite_strings,
207+ cipher_suites. bulk_encryption . iter ( ) . map ( |alg| match alg {
208+ TlsBulkEncryptionAlgorithm :: Aes128 => "AES128" ,
209+ TlsBulkEncryptionAlgorithm :: Aes256 => "AES256" ,
210+ TlsBulkEncryptionAlgorithm :: Des => "DES" ,
211+ TlsBulkEncryptionAlgorithm :: Rc2 => "RC2" ,
212+ TlsBulkEncryptionAlgorithm :: Rc4 => "RC4" ,
213+ TlsBulkEncryptionAlgorithm :: TripleDes => "3DES" ,
214+ TlsBulkEncryptionAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
215+ } ) ,
216+ ) ;
217+ cipher_suite_strings = cartesian_product (
218+ cipher_suite_strings,
219+ cipher_suites. hash . iter ( ) . map ( |alg| match alg {
220+ TlsHashAlgorithm :: Md5 => "MD5" ,
221+ TlsHashAlgorithm :: Sha1 => "SHA1" ,
222+ TlsHashAlgorithm :: Sha256 => "SHA256" ,
223+ TlsHashAlgorithm :: Sha384 => "SHA384" ,
224+ TlsHashAlgorithm :: __NonExhaustive => unreachable ! ( ) ,
225+ } ) ,
226+ ) ;
227+
228+ // GCM first, as `@STRENGTH` sorts purely on bitwidth, and otherwise respects the initial
229+ // ordering. GCM is generally preferred over CBC for performance and security reasons.
230+ expand_gcm_algorithms ( cipher_suites)
231+ . into_iter ( )
232+ . map ( borrow:: Cow :: Borrowed )
233+ . chain (
234+ cipher_suite_strings
235+ . into_iter ( )
236+ . map ( |parts| borrow:: Cow :: Owned ( parts. join ( "+" ) ) ) ,
237+ )
238+ . chain (
239+ CIPHER_STRING_SUFFIX
240+ . iter ( )
241+ . map ( |s| borrow:: Cow :: Borrowed ( * s) ) ,
242+ )
243+ . collect :: < Vec < _ > > ( )
244+ . join ( ":" )
245+ }
21246
22247#[ cfg( have_min_max_version) ]
23248fn supported_protocols (
@@ -262,6 +487,9 @@ impl TlsConnector {
262487 connector. add_extra_chain_cert ( cert. to_owned ( ) ) ?;
263488 }
264489 }
490+ if let Some ( ref cipher_suites) = builder. cipher_suites {
491+ connector. set_cipher_list ( & expand_algorithms ( cipher_suites) ) ?;
492+ }
265493 supported_protocols ( builder. min_protocol , builder. max_protocol , & mut connector) ?;
266494
267495 if builder. disable_built_in_roots {
@@ -452,3 +680,38 @@ impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
452680 self . 0 . flush ( )
453681 }
454682}
683+
684+ #[ cfg( test) ]
685+ mod tests {
686+ use super :: * ;
687+
688+ #[ test]
689+ fn expand_algorithms_basic ( ) {
690+ assert_eq ! (
691+ expand_algorithms( & CipherSuiteSet {
692+ key_exchange: vec![ TlsKeyExchangeAlgorithm :: Dhe , TlsKeyExchangeAlgorithm :: Ecdhe ] ,
693+ signature: vec![ TlsSignatureAlgorithm :: Rsa ] ,
694+ bulk_encryption: vec![
695+ TlsBulkEncryptionAlgorithm :: Aes128 ,
696+ TlsBulkEncryptionAlgorithm :: Aes256
697+ ] ,
698+ hash: vec![ TlsHashAlgorithm :: Sha256 , TlsHashAlgorithm :: Sha384 ] ,
699+ } ) ,
700+ "\
701+ DHE-RSA-AES128-GCM-SHA256:\
702+ DHE-RSA-AES256-GCM-SHA384:\
703+ ECDHE-RSA-AES128-GCM-SHA256:\
704+ ECDHE-RSA-AES256-GCM-SHA384:\
705+ DHE+aRSA+AES128+SHA256:\
706+ DHE+aRSA+AES128+SHA384:\
707+ DHE+aRSA+AES256+SHA256:\
708+ DHE+aRSA+AES256+SHA384:\
709+ ECDHE+aRSA+AES128+SHA256:\
710+ ECDHE+aRSA+AES128+SHA384:\
711+ ECDHE+aRSA+AES256+SHA256:\
712+ ECDHE+aRSA+AES256+SHA384:\
713+ !aNULL:!eNULL:!IDEA:!SEED:!SRP:!PSK:@STRENGTH\
714+ ",
715+ ) ;
716+ }
717+ }
0 commit comments