diff --git a/Cargo.toml b/Cargo.toml index cab1d47..4193117 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ libc = "0.2" tempfile = "3.1.0" [target.'cfg(target_os = "windows")'.dependencies] -schannel = "0.1.17" +schannel = "0.1.20" [target.'cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))'.dependencies] log = "0.4.5" diff --git a/build.rs b/build.rs index b7a41f4..4f9f63b 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,11 @@ fn main() { if version >= 0x1_01_00_00_0 { println!("cargo:rustc-cfg=have_min_max_version"); } + + // TLS 1.3 requires openssl 1.1.1 + if version >= 0x1_01_01_00_0 { + println!("cargo:rustc-cfg=have_tls13_version"); + } } if let Ok(version) = env::var("DEP_OPENSSL_LIBRESSL_VERSION_NUMBER") { @@ -16,5 +21,10 @@ fn main() { if version >= 0x2_06_01_00_0 { println!("cargo:rustc-cfg=have_min_max_version"); } + + // TLS 1.3 requires libressl 3.4.0 + if version >= 0x3_04_00_00_0 { + println!("cargo:rustc-cfg=have_tls13_version"); + } } } diff --git a/src/imp/openssl.rs b/src/imp/openssl.rs index 8fc4362..b318b7c 100644 --- a/src/imp/openssl.rs +++ b/src/imp/openssl.rs @@ -23,20 +23,24 @@ fn supported_protocols( min: Option, max: Option, ctx: &mut SslContextBuilder, -) -> Result<(), ErrorStack> { +) -> Result<(), Error> { use self::openssl::ssl::SslVersion; - fn cvt(p: Protocol) -> SslVersion { + fn cvt(p: Protocol) -> Result { match p { - Protocol::Sslv3 => SslVersion::SSL3, - Protocol::Tlsv10 => SslVersion::TLS1, - Protocol::Tlsv11 => SslVersion::TLS1_1, - Protocol::Tlsv12 => SslVersion::TLS1_2, + Protocol::Sslv3 => Ok(SslVersion::SSL3), + Protocol::Tlsv10 => Ok(SslVersion::TLS1), + Protocol::Tlsv11 => Ok(SslVersion::TLS1_1), + Protocol::Tlsv12 => Ok(SslVersion::TLS1_2), + #[cfg(have_tls13_version)] + Protocol::Tlsv13 => Ok(SslVersion::TLS1_3), + #[cfg(not(have_tls13_version))] + Protocol::Tlsv13 => Err(Error::UnsupportedTls13) } } - ctx.set_min_proto_version(min.map(cvt))?; - ctx.set_max_proto_version(max.map(cvt))?; + ctx.set_min_proto_version(min.map(cvt).transpose()?)?; + ctx.set_max_proto_version(max.map(cvt).transpose()?)?; Ok(()) } @@ -115,6 +119,7 @@ pub enum Error { Ssl(ssl::Error, X509VerifyResult), EmptyChain, NotPkcs8, + UnsupportedTls13, } impl error::Error for Error { @@ -124,6 +129,7 @@ impl error::Error for Error { Error::Ssl(ref e, _) => error::Error::source(e), Error::EmptyChain => None, Error::NotPkcs8 => None, + Error::UnsupportedTls13 => None, } } } @@ -139,6 +145,7 @@ impl fmt::Display for Error { "at least one certificate must be provided to create an identity" ), Error::NotPkcs8 => write!(fmt, "expected PKCS#8 PEM"), + Error::UnsupportedTls13 => write!(fmt, "TLS version 1.3 not supported"), } } } diff --git a/src/imp/schannel.rs b/src/imp/schannel.rs index 62e5042..faeb5dc 100644 --- a/src/imp/schannel.rs +++ b/src/imp/schannel.rs @@ -19,6 +19,7 @@ static PROTOCOLS: &'static [Protocol] = &[ Protocol::Tls10, Protocol::Tls11, Protocol::Tls12, + Protocol::Tls13, ]; fn convert_protocols(min: Option<::Protocol>, max: Option<::Protocol>) -> &'static [Protocol] { diff --git a/src/imp/security_framework.rs b/src/imp/security_framework.rs index f56a916..b5366ee 100644 --- a/src/imp/security_framework.rs +++ b/src/imp/security_framework.rs @@ -43,12 +43,14 @@ static SET_AT_EXIT: Once = Once::new(); #[cfg(not(target_os = "ios"))] static TEMP_KEYCHAIN: Lazy>> = Lazy::new(|| Mutex::new(None)); -fn convert_protocol(protocol: Protocol) -> SslProtocol { +fn convert_protocol(protocol: Protocol) -> Result { match protocol { - Protocol::Sslv3 => SslProtocol::SSL3, - Protocol::Tlsv10 => SslProtocol::TLS1, - Protocol::Tlsv11 => SslProtocol::TLS11, - Protocol::Tlsv12 => SslProtocol::TLS12, + Protocol::Sslv3 => Ok(SslProtocol::SSL3), + Protocol::Tlsv10 => Ok(SslProtocol::TLS1), + Protocol::Tlsv11 => Ok(SslProtocol::TLS11), + Protocol::Tlsv12 => Ok(SslProtocol::TLS12), + // Not supported in SecureTransport API used in security_framework + Protocol::Tlsv13 => Err(Error(base::Error::from("TLS 1.3 is not supported"))) } } diff --git a/src/lib.rs b/src/lib.rs index 267679d..e10d664 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -322,6 +322,11 @@ pub enum Protocol { Tlsv11, /// The TLS 1.2 protocol. Tlsv12, + /// The TLS 1.3 protocol. + /// + /// Only works on Windows, or with openssl >= 1.1.1 or libressl >= 3.4.0. + /// It will fail at runtime when used in other situations. + Tlsv13, } /// A builder for `TlsConnector`s. diff --git a/src/test.rs b/src/test.rs index c51b0bc..05beb1e 100644 --- a/src/test.rs +++ b/src/test.rs @@ -16,6 +16,26 @@ macro_rules! p { }; } +#[cfg(any(target_os = "windows", have_tls13_version))] +#[test] +fn connect_google_tls13() { + let builder = p!( + TlsConnector::builder() + .min_protocol_version(Some(Protocol::Tlsv13)) + .max_protocol_version(Some(Protocol::Tlsv13)) + .build()); + let s = p!(TcpStream::connect("google.com:443")); + let mut socket = p!(builder.connect("google.com", s)); + + p!(socket.write_all(b"GET / HTTP/1.0\r\n\r\n")); + let mut result = vec![]; + p!(socket.read_to_end(&mut result)); + + println!("{}", String::from_utf8_lossy(&result)); + assert!(result.starts_with(b"HTTP/1.0")); + assert!(result.ends_with(b"\r\n") || result.ends_with(b"")); +} + #[test] fn connect_google() { let builder = p!(TlsConnector::new());