@@ -11,14 +11,240 @@ 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
21
+ use {
22
+ CipherSuiteSet , Protocol , TlsAcceptorBuilder , TlsBulkEncryptionAlgorithm , TlsConnectorBuilder ,
23
+ TlsHashAlgorithm , TlsKeyExchangeAlgorithm , TlsSignatureAlgorithm ,
24
+ } ;
19
25
use self :: openssl:: pkey:: Private ;
20
26
use { Protocol , TlsAcceptorBuilder , TlsConnectorBuilder } ;
21
27
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
+
22
248
#[ cfg( have_min_max_version) ]
23
249
fn supported_protocols (
24
250
min : Option < Protocol > ,
@@ -262,6 +488,9 @@ impl TlsConnector {
262
488
connector. add_extra_chain_cert ( cert. to_owned ( ) ) ?;
263
489
}
264
490
}
491
+ if let Some ( ref cipher_suites) = builder. cipher_suites {
492
+ connector. set_cipher_list ( & expand_algorithms ( cipher_suites) ) ?;
493
+ }
265
494
supported_protocols ( builder. min_protocol , builder. max_protocol , & mut connector) ?;
266
495
267
496
if builder. disable_built_in_roots {
@@ -452,3 +681,38 @@ impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
452
681
self . 0 . flush ( )
453
682
}
454
683
}
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