Skip to content

Commit

Permalink
TLS Peer Dependency (#4)
Browse files Browse the repository at this point in the history
* Native TLS Shadowing

Addresses #1 and #2. Provides interfaces without needing to use native-tls
library.
  • Loading branch information
dbcfd authored Apr 13, 2019
1 parent 6156936 commit a58d250
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 224 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
language: rust
rust:
- nightly
- nightly-2019-04-07
sudo: false
cache:
- apt
Expand Down
8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,20 @@ edition = "2018"
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create "v0.1.x" git tag.
version = "0.3.0-alpha.2"
version = "0.3.0-alpha.3"
license = "MIT"
readme = "README.md"
description = """
TLS support for AsyncRead/AsyncWrite using native-tls
"""
authors = ["Danny Browning <bdbrowning2@gmail.com>", "Carl Lerche <me@carllerche.com>"]
categories = ["asynchronous", "network-programming"]
documentation = "https://docs.rs/tls-async/0.3.0-alpha.2/tls_async/"
documentation = "https://docs.rs/tls-async/0.3.0-alpha.3/tls_async/"
repository = "https://github.com/dbcfd/tls-async"

[dependencies]
failure = "0.1"
failure_derive = "0.1"
log = "0.4.1"
native-tls = "0.2"

Expand All @@ -29,7 +31,7 @@ features = ["compat", "io-compat", "std"]

[dev-dependencies]
cfg-if = "0.1"
romio = "0.3.0-alpha.2"
romio = "0.3.0-alpha.3"
tokio = "0.1"

[dev-dependencies.env_logger]
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This is an experimental fork of [tokio-tls](https://github.com/tokio-rs/tokio/tr
An implementation of TLS/SSL streams for [Futures 0.3](https://github.com/rust-lang-nursery/futures-rs) built on top of the [`native-tls`
crate]

[Documentation](https://docs.rs/tls-async/0.3.0-alpha.1/)
[Documentation](https://docs.rs/tls-async/0.3.0-alpha.3/)

[`native-tls` crate]: https://github.com/sfackler/rust-native-tls

Expand All @@ -29,8 +29,7 @@ First, add this to your `Cargo.toml`:

```toml
[dependencies]
native-tls = "0.2"
tls-async = "0.3.0-alpha.1"
tls-async = "0.3.0-alpha.3"
```

Next, add this to your crate:
Expand Down
3 changes: 1 addition & 2 deletions examples/download-rust-lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::net::ToSocketAddrs;

use futures::{FutureExt, TryFutureExt};
use futures::io::{AsyncReadExt, AsyncWriteExt};
use native_tls::TlsConnector;
use romio::TcpStream;
use tls_async::TlsConnector;
use tokio::runtime::Runtime;

fn main() {
Expand All @@ -19,7 +19,6 @@ fn main() {

let socket = await!(TcpStream::connect(&addr)).expect("Could not connect");
let cx = TlsConnector::builder().build().expect("Could not build");
let cx = tls_async::TlsConnector::from(cx);

let mut socket = await!(cx.connect("www.rust-lang.org", socket)).expect("Could not form tls connection");
let _ = await!(socket.write_all(b"\
Expand Down
133 changes: 133 additions & 0 deletions src/acceptor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
use crate::errors::Error;
use crate::pending::PendingTlsStream;
use crate::{Identity, Protocol};

use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite};

/// A builder for `TlsAcceptor`s.
pub struct TlsAcceptorBuilder {
inner: native_tls::TlsAcceptorBuilder,
}

impl TlsAcceptorBuilder {
/// Sets the minimum supported protocol version.
///
/// A value of `None` enables support for the oldest protocols supported by the implementation.
///
/// Defaults to `Some(Protocol::Tlsv10)`.
pub fn min_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsAcceptorBuilder {
self.inner.min_protocol_version(protocol);
self
}

/// Sets the maximum supported protocol version.
///
/// A value of `None` enables support for the newest protocols supported by the implementation.
///
/// Defaults to `None`.
pub fn max_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsAcceptorBuilder {
self.inner.max_protocol_version(protocol);
self
}

/// Creates a new `TlsAcceptor`.
pub fn build(&self) -> Result<TlsAcceptor, Error> {
let acceptor = self.inner.build().map_err(Error::Acceptor)?;
Ok(TlsAcceptor {
inner: acceptor
})
}
}

/// A builder for server-side TLS connections.
///
/// # Examples
///
/// ```rust,no_run
/// #![feature(async_await, await_macro, futures_api)]
/// use futures::StreamExt;
/// use futures::io::AsyncRead;
/// use tls_async::{Identity, TlsAcceptor, TlsStream};
/// use std::fs::File;
/// use std::io::{Read};
/// use romio::{TcpListener, TcpStream};
/// use std::sync::Arc;
/// use std::thread;
///
/// let mut file = File::open("identity.pfx").unwrap();
/// let mut identity = vec![];
/// file.read_to_end(&mut identity).unwrap();
/// let identity = Identity::from_pkcs12(&identity, "hunter2").unwrap();
///
/// let mut listener = TcpListener::bind(&"0.0.0.0:8443".parse().unwrap()).unwrap();
/// let acceptor = TlsAcceptor::new(identity).unwrap();
/// let acceptor = Arc::new(acceptor);
///
/// fn handle_client<S: AsyncRead>(stream: S) {
/// // ...
/// }
///
/// let mut incoming = listener.incoming();
/// # futures::executor::block_on(async {
/// for stream in await!(incoming.next()) {
/// match stream {
/// Ok(stream) => {
/// let acceptor = acceptor.clone();
/// let stream = await!(acceptor.accept(stream)).unwrap();
/// handle_client(stream);
/// }
/// Err(e) => { /* connection failed */ }
/// }
/// }
/// # })
/// ```
#[derive(Clone)]
pub struct TlsAcceptor {
inner: native_tls::TlsAcceptor,
}

impl TlsAcceptor {
/// Creates a acceptor with default settings.
///
/// The identity acts as the server's private key/certificate chain.
pub fn new(identity: Identity) -> Result<TlsAcceptor, Error> {
let native_acceptor = native_tls::TlsAcceptor::new(identity).map_err(Error::Acceptor)?;
Ok(TlsAcceptor {
inner: native_acceptor,
})
}

/// Returns a new builder for a `TlsAcceptor`.
///
/// The identity acts as the server's private key/certificate chain.
pub fn builder(identity: Identity) -> TlsAcceptorBuilder {
let builder = native_tls::TlsAcceptor::builder(identity);
TlsAcceptorBuilder {
inner: builder,
}
}

/// Accepts a new client connection with the provided stream.
///
/// This function will internally call `TlsAcceptor::accept` to connect
/// the stream and returns a future representing the resolution of the
/// connection operation. The returned future will resolve to either
/// `TlsStream<S>` or `Error` depending if it's successful or not.
///
/// This is typically used after a new socket has been accepted from a
/// `TcpListener`. That socket is then passed to this function to perform
/// the server half of accepting a client connection.
pub fn accept<S>(&self, stream: S) -> PendingTlsStream<S>
where S: AsyncRead + AsyncWrite,
{
PendingTlsStream::new(self.inner.accept(stream.compat()))
}
}

impl From<native_tls::TlsAcceptor> for TlsAcceptor {
fn from(inner: native_tls::TlsAcceptor) -> Self {
Self {
inner,
}
}
}
163 changes: 163 additions & 0 deletions src/connector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use crate::errors::Error;
use crate::pending::PendingTlsStream;
use crate::{Certificate, Identity, Protocol};

use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite};

/// A builder for `TlsConnector`s.
pub struct TlsConnectorBuilder {
inner: native_tls::TlsConnectorBuilder,
}

impl TlsConnectorBuilder {
/// Sets the identity to be used for client certificate authentication.
pub fn identity(&mut self, identity: Identity) -> &mut TlsConnectorBuilder {
self.inner.identity(identity);
self
}

/// Sets the minimum supported protocol version.
///
/// A value of `None` enables support for the oldest protocols supported by the implementation.
///
/// Defaults to `Some(Protocol::Tlsv10)`.
pub fn min_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsConnectorBuilder {
self.inner.min_protocol_version(protocol);
self
}

/// Sets the maximum supported protocol version.
///
/// A value of `None` enables support for the newest protocols supported by the implementation.
///
/// Defaults to `None`.
pub fn max_protocol_version(&mut self, protocol: Option<Protocol>) -> &mut TlsConnectorBuilder {
self.inner.max_protocol_version(protocol);
self
}

/// Adds a certificate to the set of roots that the connector will trust.
///
/// The connector will use the system's trust root by default. This method can be used to add
/// to that set when communicating with servers not trusted by the system.
///
/// Defaults to an empty set.
pub fn add_root_certificate(&mut self, cert: Certificate) -> &mut TlsConnectorBuilder {
self.inner.add_root_certificate(cert);
self
}

/// Controls the use of certificate validation.
///
/// Defaults to `false`.
///
/// # Warning
///
/// You should think very carefully before using this method. If invalid certificates are trusted, *any*
/// certificate for *any* site will be trusted for use. This includes expired certificates. This introduces
/// significant vulnerabilities, and should only be used as a last resort.
pub fn danger_accept_invalid_certs(
&mut self,
accept_invalid_certs: bool,
) -> &mut TlsConnectorBuilder {
self.inner.danger_accept_invalid_certs(accept_invalid_certs);
self
}

/// Controls the use of Server Name Indication (SNI).
///
/// Defaults to `true`.
pub fn use_sni(&mut self, use_sni: bool) -> &mut TlsConnectorBuilder {
self.inner.use_sni(use_sni);
self
}

/// Controls the use of hostname verification.
///
/// Defaults to `false`.
///
/// # Warning
///
/// You should think very carefully before using this method. If invalid hostnames are trusted, *any* valid
/// certificate for *any* site will be trusted for use. This introduces significant vulnerabilities, and should
/// only be used as a last resort.
pub fn danger_accept_invalid_hostnames(
&mut self,
accept_invalid_hostnames: bool,
) -> &mut TlsConnectorBuilder {
self.inner.danger_accept_invalid_hostnames(accept_invalid_hostnames);
self
}

/// Creates a new `TlsConnector`.
pub fn build(&self) -> Result<TlsConnector, Error> {
let connector = self.inner.build().map_err(Error::Connector)?;
Ok(TlsConnector {
inner: connector
})
}
}

///
/// # Examples
///
/// ```rust,no_run
/// #![feature(async_await, await_macro, futures_api)]
/// use futures::io::{AsyncReadExt, AsyncWriteExt};
/// use tls_async::TlsConnector;
/// use std::io::{Read, Write};
/// use std::net::ToSocketAddrs;
/// use romio::TcpStream;
///
/// # futures::executor::block_on(async {
/// let connector = TlsConnector::new().unwrap();
///
/// let addr = "google.com:443".to_socket_addrs().unwrap().next().unwrap();
/// let stream = await!(TcpStream::connect(&addr)).unwrap();
/// let mut stream = await!(connector.connect("google.com", stream)).unwrap();
///
/// await!(stream.write_all(b"GET / HTTP/1.0\r\n\r\n")).unwrap();
/// let mut res = vec![];
/// await!(stream.read_to_end(&mut res)).unwrap();
/// println!("{}", String::from_utf8_lossy(&res));
/// # })
/// ```
#[derive(Clone)]
pub struct TlsConnector {
inner: native_tls::TlsConnector,
}

impl TlsConnector {
/// Returns a new connector with default settings.
pub fn new() -> Result<TlsConnector, Error> {
let native_connector = native_tls::TlsConnector::new().map_err(Error::Connector)?;
Ok( TlsConnector {
inner: native_connector,
})
}

/// Returns a new builder for a `TlsConnector`.
pub fn builder() -> TlsConnectorBuilder {
TlsConnectorBuilder {
inner: native_tls::TlsConnector::builder(),
}
}

/// Connects the provided stream with this connector, assuming the provided
/// domain.
///
/// This function will internally call `TlsConnector::connect` to connect
/// the stream and returns a future representing the resolution of the
/// connection operation. The returned future will resolve to either
/// `TlsStream<S>` or `Error` depending if it's successful or not.
///
/// This is typically used for clients who have already established, for
/// example, a TCP connection to a remote server. That stream is then
/// provided here to perform the client half of a connection to a
/// TLS-powered server.
pub fn connect<'a, S>(&'a self, domain: &'a str, stream: S) -> PendingTlsStream<S>
where S: AsyncRead + AsyncWrite,
{
PendingTlsStream::new(self.inner.connect(domain, stream.compat()))
}
}
18 changes: 18 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use failure::Fail;

#[derive(Debug, Fail)]
pub enum Error {
#[fail(display="NativeTls Acceptor Error")]
Acceptor(#[cause] native_tls::Error),
#[fail(display="NativeTls Connector Error")]
Connector(#[cause] native_tls::Error),
#[fail(display="Error during handshake")]
Handshake(#[cause] native_tls::Error),
#[fail(display="NativeTls Error")]
Native(#[cause] native_tls::Error),
#[fail(display="Cannot repeat handshake")]
RepeatedHandshake,
}

unsafe impl Sync for Error {}
unsafe impl Send for Error {}
Loading

0 comments on commit a58d250

Please sign in to comment.