Skip to content

Commit f2be4ea

Browse files
committed
Support cipher suite selection
1 parent 97b77f4 commit f2be4ea

File tree

6 files changed

+890
-5
lines changed

6 files changed

+890
-5
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ libc = "0.2"
1818
tempfile = "3.0"
1919

2020
[target.'cfg(target_os = "windows")'.dependencies]
21-
schannel = "0.1.16"
21+
schannel = "0.1.19"
2222

2323
[target.'cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))'.dependencies]
2424
log = "0.4.5"

src/imp/openssl.rs

+114-1
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,93 @@ use self::openssl::ssl::{
1111
SslVerifyMode,
1212
};
1313
use self::openssl::x509::{X509, store::X509StoreBuilder, X509VerifyResult};
14+
use std::borrow;
1415
use std::error;
1516
use std::fmt;
1617
use std::io;
1718
use std::sync::Once;
1819

19-
use {Protocol, TlsAcceptorBuilder, TlsConnectorBuilder};
20+
use {
21+
CipherSuiteSet, Protocol, TlsAcceptorBuilder, TlsBulkEncryptionAlgorithm, TlsConnectorBuilder,
22+
TlsHashAlgorithm, TlsKeyExchangeAlgorithm, TlsSignatureAlgorithm,
23+
};
2024
use self::openssl::pkey::Private;
2125

26+
const CIPHER_STRING_SUFFIX: &[&str] = &[
27+
"!aNULL",
28+
"!eNULL",
29+
"!IDEA",
30+
"!SEED",
31+
"!SRP",
32+
"!PSK",
33+
"@STRENGTH",
34+
];
35+
36+
fn cartesian_product(
37+
xs: impl IntoIterator<Item = Vec<&'static str>>,
38+
ys: impl IntoIterator<Item = &'static str> + Clone,
39+
) -> Vec<Vec<&'static str>> {
40+
xs.into_iter()
41+
.flat_map(move |x| ys.clone().into_iter().map(move |y| [&x, &[y][..]].concat()))
42+
.collect()
43+
}
44+
45+
fn expand_algorithms(cipher_suites: &CipherSuiteSet) -> String {
46+
let mut cipher_suite_strings: Vec<Vec<&'static str>> = vec![];
47+
48+
cipher_suite_strings.extend(cipher_suites.key_exchange.iter().map(|alg| {
49+
vec![match alg {
50+
TlsKeyExchangeAlgorithm::Dhe => "DHE",
51+
TlsKeyExchangeAlgorithm::Ecdhe => "ECDHE",
52+
TlsKeyExchangeAlgorithm::Rsa => "kRSA",
53+
TlsKeyExchangeAlgorithm::__NonExhaustive => unreachable!(),
54+
}]
55+
}));
56+
57+
cipher_suite_strings = cartesian_product(
58+
cipher_suite_strings,
59+
cipher_suites.signature.iter().map(|alg| match alg {
60+
TlsSignatureAlgorithm::Dss => "aDSS",
61+
TlsSignatureAlgorithm::Ecdsa => "aECDSA",
62+
TlsSignatureAlgorithm::Rsa => "aRSA",
63+
TlsSignatureAlgorithm::__NonExhaustive => unreachable!(),
64+
}),
65+
);
66+
cipher_suite_strings = cartesian_product(
67+
cipher_suite_strings,
68+
cipher_suites.bulk_encryption.iter().map(|alg| match alg {
69+
TlsBulkEncryptionAlgorithm::Aes128 => "AES128",
70+
TlsBulkEncryptionAlgorithm::Aes256 => "AES256",
71+
TlsBulkEncryptionAlgorithm::Des => "DES",
72+
TlsBulkEncryptionAlgorithm::Rc2 => "RC2",
73+
TlsBulkEncryptionAlgorithm::Rc4 => "RC4",
74+
TlsBulkEncryptionAlgorithm::TripleDes => "3DES",
75+
TlsBulkEncryptionAlgorithm::__NonExhaustive => unreachable!(),
76+
}),
77+
);
78+
cipher_suite_strings = cartesian_product(
79+
cipher_suite_strings,
80+
cipher_suites.hash.iter().map(|alg| match alg {
81+
TlsHashAlgorithm::Md5 => "MD5",
82+
TlsHashAlgorithm::Sha1 => "SHA1",
83+
TlsHashAlgorithm::Sha256 => "SHA256",
84+
TlsHashAlgorithm::Sha384 => "SHA384",
85+
TlsHashAlgorithm::__NonExhaustive => unreachable!(),
86+
}),
87+
);
88+
89+
cipher_suite_strings
90+
.into_iter()
91+
.map(move |parts| borrow::Cow::Owned(parts.join("+")))
92+
.chain(
93+
CIPHER_STRING_SUFFIX
94+
.iter()
95+
.map(|s| borrow::Cow::Borrowed(*s)),
96+
)
97+
.collect::<Vec<_>>()
98+
.join(":")
99+
}
100+
22101
#[cfg(have_min_max_version)]
23102
fn supported_protocols(
24103
min: Option<Protocol>,
@@ -262,6 +341,9 @@ impl TlsConnector {
262341
connector.add_extra_chain_cert(cert.to_owned())?;
263342
}
264343
}
344+
if let Some(ref cipher_suites) = builder.cipher_suites {
345+
connector.set_cipher_list(&expand_algorithms(cipher_suites))?;
346+
}
265347
supported_protocols(builder.min_protocol, builder.max_protocol, &mut connector)?;
266348

267349
if builder.disable_built_in_roots {
@@ -412,3 +494,34 @@ impl<S: io::Read + io::Write> io::Write for TlsStream<S> {
412494
self.0.flush()
413495
}
414496
}
497+
498+
#[cfg(test)]
499+
mod tests {
500+
use super::*;
501+
502+
#[test]
503+
fn expand_algorithms_basic() {
504+
assert_eq!(
505+
expand_algorithms(&CipherSuiteSet {
506+
key_exchange: vec![TlsKeyExchangeAlgorithm::Dhe, TlsKeyExchangeAlgorithm::Ecdhe],
507+
signature: vec![TlsSignatureAlgorithm::Rsa],
508+
bulk_encryption: vec![
509+
TlsBulkEncryptionAlgorithm::Aes128,
510+
TlsBulkEncryptionAlgorithm::Aes256
511+
],
512+
hash: vec![TlsHashAlgorithm::Sha256, TlsHashAlgorithm::Sha384],
513+
}),
514+
"\
515+
DHE+aRSA+AES128+SHA256:\
516+
DHE+aRSA+AES128+SHA384:\
517+
DHE+aRSA+AES256+SHA256:\
518+
DHE+aRSA+AES256+SHA384:\
519+
ECDHE+aRSA+AES128+SHA256:\
520+
ECDHE+aRSA+AES128+SHA384:\
521+
ECDHE+aRSA+AES256+SHA256:\
522+
ECDHE+aRSA+AES256+SHA384:\
523+
!aNULL:!eNULL:!IDEA:!SEED:!SRP:!PSK:@STRENGTH\
524+
",
525+
);
526+
}
527+
}

src/imp/schannel.rs

+82-2
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,86 @@ extern crate schannel;
22

33
use self::schannel::cert_context::{CertContext, HashAlgorithm};
44
use self::schannel::cert_store::{CertAdd, CertStore, Memory, PfxImportOptions};
5-
use self::schannel::schannel_cred::{Direction, Protocol, SchannelCred};
5+
use self::schannel::schannel_cred::{Algorithm, Direction, Protocol, SchannelCred};
66
use self::schannel::tls_stream;
77
use std::error;
88
use std::fmt;
99
use std::io;
1010
use std::str;
1111

12-
use {TlsAcceptorBuilder, TlsConnectorBuilder};
12+
use {
13+
CipherSuiteSet, TlsAcceptorBuilder, TlsBulkEncryptionAlgorithm, TlsConnectorBuilder,
14+
TlsHashAlgorithm, TlsKeyExchangeAlgorithm, TlsSignatureAlgorithm,
15+
};
16+
17+
impl From<TlsKeyExchangeAlgorithm> for Algorithm {
18+
fn from(other: TlsKeyExchangeAlgorithm) -> Self {
19+
match other {
20+
TlsKeyExchangeAlgorithm::Dhe => Algorithm::DhEphem,
21+
TlsKeyExchangeAlgorithm::Ecdhe => Algorithm::EcdhEphem,
22+
TlsKeyExchangeAlgorithm::Rsa => Algorithm::RsaKeyx,
23+
TlsKeyExchangeAlgorithm::__NonExhaustive => unreachable!(),
24+
}
25+
}
26+
}
27+
28+
impl From<TlsSignatureAlgorithm> for Algorithm {
29+
fn from(other: TlsSignatureAlgorithm) -> Self {
30+
match other {
31+
TlsSignatureAlgorithm::Dss => Algorithm::DssSign,
32+
TlsSignatureAlgorithm::Ecdsa => Algorithm::Ecdsa,
33+
TlsSignatureAlgorithm::Rsa => Algorithm::RsaSign,
34+
TlsSignatureAlgorithm::__NonExhaustive => unreachable!(),
35+
}
36+
}
37+
}
38+
39+
impl From<TlsBulkEncryptionAlgorithm> for Algorithm {
40+
fn from(other: TlsBulkEncryptionAlgorithm) -> Self {
41+
match other {
42+
TlsBulkEncryptionAlgorithm::Aes128 => Algorithm::Aes128,
43+
TlsBulkEncryptionAlgorithm::Aes256 => Algorithm::Aes256,
44+
TlsBulkEncryptionAlgorithm::Des => Algorithm::Des,
45+
TlsBulkEncryptionAlgorithm::Rc2 => Algorithm::Rc2,
46+
TlsBulkEncryptionAlgorithm::Rc4 => Algorithm::Rc4,
47+
TlsBulkEncryptionAlgorithm::TripleDes => Algorithm::TripleDes,
48+
TlsBulkEncryptionAlgorithm::__NonExhaustive => unreachable!(),
49+
}
50+
}
51+
}
52+
53+
impl From<TlsHashAlgorithm> for Algorithm {
54+
fn from(other: TlsHashAlgorithm) -> Self {
55+
match other {
56+
TlsHashAlgorithm::Md5 => Algorithm::Md5,
57+
TlsHashAlgorithm::Sha1 => Algorithm::Sha1,
58+
TlsHashAlgorithm::Sha256 => Algorithm::Sha256,
59+
TlsHashAlgorithm::Sha384 => Algorithm::Sha384,
60+
TlsHashAlgorithm::__NonExhaustive => unreachable!(),
61+
}
62+
}
63+
}
64+
65+
fn expand_algorithms(cipher_suites: &CipherSuiteSet) -> Vec<Algorithm> {
66+
let mut ret = vec![];
67+
ret.extend(
68+
cipher_suites
69+
.key_exchange
70+
.iter()
71+
.copied()
72+
.map(Algorithm::from),
73+
);
74+
ret.extend(cipher_suites.signature.iter().copied().map(Algorithm::from));
75+
ret.extend(
76+
cipher_suites
77+
.bulk_encryption
78+
.iter()
79+
.copied()
80+
.map(Algorithm::from),
81+
);
82+
ret.extend(cipher_suites.hash.iter().copied().map(Algorithm::from));
83+
ret
84+
}
1385

1486
const SEC_E_NO_CREDENTIALS: u32 = 0x8009030E;
1587

@@ -186,6 +258,7 @@ pub struct TlsConnector {
186258
accept_invalid_hostnames: bool,
187259
accept_invalid_certs: bool,
188260
disable_built_in_roots: bool,
261+
supported_algorithms: Vec<Algorithm>,
189262
}
190263

191264
impl TlsConnector {
@@ -205,6 +278,10 @@ impl TlsConnector {
205278
accept_invalid_hostnames: builder.accept_invalid_hostnames,
206279
accept_invalid_certs: builder.accept_invalid_certs,
207280
disable_built_in_roots: builder.disable_built_in_roots,
281+
supported_algorithms: match &builder.cipher_suites {
282+
Some(cipher_suites) => expand_algorithms(cipher_suites),
283+
None => vec![],
284+
},
208285
})
209286
}
210287

@@ -217,6 +294,9 @@ impl TlsConnector {
217294
if let Some(cert) = self.cert.as_ref() {
218295
builder.cert(cert.clone());
219296
}
297+
if !self.supported_algorithms.is_empty() {
298+
builder.supported_algorithms(&self.supported_algorithms);
299+
}
220300
let cred = builder.acquire(Direction::Outbound)?;
221301
let mut builder = tls_stream::Builder::new();
222302
builder

0 commit comments

Comments
 (0)