Skip to content

Commit

Permalink
Merge pull request #35 from jchien14/algo
Browse files Browse the repository at this point in the history
Enable specification of certain cipher suites
  • Loading branch information
sfackler authored Jun 3, 2017
2 parents 1458a2e + 48a883c commit 82c7508
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 7 deletions.
2 changes: 1 addition & 1 deletion security-framework/src/cipher_suite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ macro_rules! make_suites {
($($suite:ident),+) => {
/// Specifies cipher suites
#[allow(non_camel_case_types, missing_docs)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum CipherSuite {
$($suite),+
}
Expand Down
86 changes: 80 additions & 6 deletions security-framework/src/secure_transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,8 @@ pub struct ClientBuilder {
protocol_min: Option<SslProtocol>,
protocol_max: Option<SslProtocol>,
trust_certs_only: bool,
whitelisted_ciphers: Vec<CipherSuite>,
blacklisted_ciphers: Vec<CipherSuite>,
}

impl Default for ClientBuilder {
Expand All @@ -1109,6 +1111,8 @@ impl ClientBuilder {
protocol_min: None,
protocol_max: None,
trust_certs_only: false,
whitelisted_ciphers: Vec::new(),
blacklisted_ciphers: Vec::new(),
}
}

Expand All @@ -1126,6 +1130,18 @@ impl ClientBuilder {
self
}

/// Set a whitelist of enabled ciphers. Any ciphers not whitelisted will be disabled.
pub fn whitelist_ciphers(&mut self, whitelisted_ciphers: &[CipherSuite]) -> &mut Self {
self.whitelisted_ciphers = whitelisted_ciphers.to_owned();
self
}

/// Set a blacklist of disabled ciphers. Blacklisted ciphers will be disabled.
pub fn blacklist_ciphers(&mut self, blacklisted_ciphers: &[CipherSuite]) -> &mut Self {
self.blacklisted_ciphers = blacklisted_ciphers.to_owned();
self
}

/// Use the specified identity as a SSL/TLS client certificate.
pub fn identity(&mut self, identity: &SecIdentity, chain: &[SecCertificate]) -> &mut Self {
self.identity = Some(identity.clone());
Expand Down Expand Up @@ -1198,10 +1214,8 @@ impl ClientBuilder {
self.handshake_inner(None, stream)
}

fn handshake_inner<S>(&self,
domain: Option<&str>,
stream: S)
-> result::Result<SslStream<S>, ClientHandshakeError<S>>

fn ctx_into_stream<S>(&self, domain: Option<&str>, stream: S) -> Result<SslStream<S>>
where S: Read + Write
{
let mut ctx = try!(SslContext::new(ProtocolSide::Client, ConnectionType::Stream));
Expand All @@ -1214,15 +1228,25 @@ impl ClientBuilder {
}
try!(ctx.set_break_on_server_auth(true));
try!(self.configure_protocols(&mut ctx));
try!(self.configure_ciphers(&mut ctx));

let certs = self.certs.clone();
ctx.into_stream(stream)
}

fn handshake_inner<S>(&self,
domain: Option<&str>,
stream: S)
-> result::Result<SslStream<S>, ClientHandshakeError<S>>
where S: Read + Write
{
// the logic for trust validation is in MidHandshakeClientBuilder::connect, so run all
// of the handshake logic through that.
let stream = MidHandshakeSslStream {
stream: try!(ctx.into_stream(stream)),
stream: try!(self.ctx_into_stream(domain, stream)),
error: Error::from(errSecSuccess),
};

let certs = self.certs.clone();
let stream = MidHandshakeClientBuilder {
stream: stream,
domain: domain.map(|s| s.to_string()),
Expand All @@ -1247,6 +1271,22 @@ impl ClientBuilder {
fn configure_protocols(&self, _ctx: &mut SslContext) -> Result<()> {
Ok(())
}

fn configure_ciphers(&self, ctx: &mut SslContext) -> Result<()> {
let mut ciphers = if self.whitelisted_ciphers.is_empty() {
try!(ctx.enabled_ciphers())
}
else {
self.whitelisted_ciphers.clone()
};

if !self.blacklisted_ciphers.is_empty() {
ciphers.retain(|cipher| !self.blacklisted_ciphers.contains(cipher));
}

try!(ctx.set_enabled_ciphers(&ciphers));
Ok(())
}
}

/// A builder type to simplify the creation of server-side `SslStream`s.
Expand Down Expand Up @@ -1371,6 +1411,40 @@ mod test {
assert_eq!(ciphers, p!(ctx.enabled_ciphers()));
}

#[test]
fn test_builder_whitelist_ciphers() {
let stream = p!(TcpStream::connect("google.com:443"));

let ctx = p!(SslContext::new(ProtocolSide::Client, ConnectionType::Stream));
assert!(p!(ctx.enabled_ciphers()).len() > 1);

let ciphers = p!(ctx.enabled_ciphers());
let cipher = ciphers.first().unwrap();
let stream = p!(ClientBuilder::new()
.whitelist_ciphers(&[*cipher])
.ctx_into_stream(None, stream));

assert_eq!(1, p!(stream.context().enabled_ciphers()).len());
}

#[test]
#[cfg_attr(target_os = "ios", ignore)] // FIXME same issue as cipher_configuration
fn test_builder_blacklist_ciphers() {
let stream = p!(TcpStream::connect("google.com:443"));

let ctx = p!(SslContext::new(ProtocolSide::Client, ConnectionType::Stream));
let num = p!(ctx.enabled_ciphers()).len();
assert!(num > 1);

let ciphers = p!(ctx.enabled_ciphers());
let cipher = ciphers.first().unwrap();
let stream = p!(ClientBuilder::new()
.blacklist_ciphers(&[*cipher])
.ctx_into_stream(None, stream));

assert_eq!(num - 1, p!(stream.context().enabled_ciphers()).len());
}

#[test]
fn idle_context_peer_trust() {
let ctx = p!(SslContext::new(ProtocolSide::Server, ConnectionType::Stream));
Expand Down

0 comments on commit 82c7508

Please sign in to comment.