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