@@ -11,13 +11,238 @@ use self::openssl::ssl::{
11
11
SslVerifyMode ,
12
12
} ;
13
13
use self :: openssl:: x509:: { store:: X509StoreBuilder , X509VerifyResult , X509 } ;
14
+ use std:: borrow;
15
+ use std:: collections:: HashSet ;
14
16
use std:: error;
15
17
use std:: fmt;
16
18
use std:: io;
17
19
use std:: sync:: Once ;
18
20
19
21
use 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
+ }
21
246
22
247
#[ cfg( have_min_max_version) ]
23
248
fn supported_protocols (
@@ -262,6 +487,9 @@ impl TlsConnector {
262
487
connector. add_extra_chain_cert ( cert. to_owned ( ) ) ?;
263
488
}
264
489
}
490
+ if let Some ( ref cipher_suites) = builder. cipher_suites {
491
+ connector. set_cipher_list ( & expand_algorithms ( cipher_suites) ) ?;
492
+ }
265
493
supported_protocols ( builder. min_protocol , builder. max_protocol , & mut connector) ?;
266
494
267
495
if builder. disable_built_in_roots {
@@ -452,3 +680,38 @@ impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
452
680
self . 0 . flush ( )
453
681
}
454
682
}
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