Skip to content

Commit

Permalink
feat: add rustls v0.22 support
Browse files Browse the repository at this point in the history
  • Loading branch information
robjtede committed Dec 6, 2023
1 parent 9edc0b3 commit 4d7b9d2
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 40 deletions.
2 changes: 2 additions & 0 deletions actix-tls/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Support Rustls v0.22.
- Added `{accept, connect}::rustls_0_22` modules.
- Add `rustls-0_21-native-roots` and `rustls-0_20-native-roots` crate features which utilize the `rustls-native-certs` crate to enable a `native_roots_cert_store()` functions in each rustls-based `connect` module.
- Implement `Host` for `http::Uri` (`http` crate version `1`).

Expand Down
28 changes: 19 additions & 9 deletions actix-tls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,16 @@ rustls = ["rustls-0_20"]
# use rustls v0.20 impls
rustls-0_20 = ["rustls-0_20-webpki-roots"]
rustls-0_20-webpki-roots = ["tokio-rustls-023", "webpki-roots-022"]
rustls-0_20-native-roots = ["tokio-rustls-023", "dep:rustls-native-certs"]
rustls-0_20-native-roots = ["tokio-rustls-023", "dep:rustls-native-certs-06"]

# use rustls v0.21 impls
rustls-0_21 = ["rustls-0_21-webpki-roots"]
rustls-0_21-webpki-roots = ["tokio-rustls-024", "webpki-roots-025"]
rustls-0_21-native-roots = ["tokio-rustls-024", "dep:rustls-native-certs"]
rustls-0_21-native-roots = ["tokio-rustls-024", "dep:rustls-native-certs-06"]

# use rustls v0.22 impls
rustls-0_22-webpki-roots = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1", "dep:webpki-roots-026"]
rustls-0_22-native-roots = ["dep:tokio-rustls-025", "dep:rustls-pki-types-1", "dep:rustls-native-certs-07"]

# use native-tls impls
native-tls = ["tokio-native-tls"]
Expand Down Expand Up @@ -87,13 +91,19 @@ tokio-rustls-023 = { package = "tokio-rustls", version = "0.23", optional = true
webpki-roots-022 = { package = "webpki-roots", version = "0.22", optional = true }

# rustls v0.21
rustls-021 = { package = "rustls", version = "0.21.6" }
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4" }
rustls-021 = { package = "rustls", version = "0.21.6", optional = true }
rustls-webpki-0101 = { package = "rustls-webpki", version = "0.101.4", optional = true }
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", optional = true }
webpki-roots-025 = { package = "webpki-roots", version = "0.25", optional = true }

# native root certificates for both rustls impls
rustls-native-certs = { version = "0.6", optional = true }
# rustls v0.22
rustls-pki-types-1 = { package = "rustls-pki-types", version = "1", optional = true }
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25", optional = true }
webpki-roots-026 = { package = "webpki-roots", version = "0.26", optional = true }

# native root certificates for rustls impls
rustls-native-certs-06 = { package = "rustls-native-certs", version = "0.6", optional = true }
rustls-native-certs-07 = { package = "rustls-native-certs", version = "0.7", optional = true }

# native-tls
tokio-native-tls = { version = "0.3", optional = true }
Expand All @@ -106,10 +116,10 @@ bytes = "1"
env_logger = "0.10"
futures-util = { version = "0.3.17", default-features = false, features = ["sink"] }
rcgen = "0.11"
rustls-pemfile = "1"
tokio-rustls-024 = { package = "tokio-rustls", version = "0.24", features = ["dangerous_configuration"] }
rustls-pemfile = "2"
tokio-rustls-025 = { package = "tokio-rustls", version = "0.25" }
trust-dns-resolver = "0.23"

[[example]]
name = "accept-rustls"
required-features = ["accept", "rustls-0_21"]
required-features = ["accept", "rustls-0_22"]
6 changes: 6 additions & 0 deletions actix-tls/src/connect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ pub use rustls_0_20 as rustls;
))]
pub mod rustls_0_21;

#[cfg(any(
feature = "rustls-0_22-webpki-roots",
feature = "rustls-0_22-native-roots",
))]
pub mod rustls_0_22;

#[cfg(feature = "native-tls")]
pub mod native_tls;

Expand Down
2 changes: 1 addition & 1 deletion actix-tls/src/connect/rustls_0_20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub mod reexports {
pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
let mut root_certs = RootCertStore::empty();

for cert in rustls_native_certs::load_native_certs()? {
for cert in rustls_native_certs_06::load_native_certs()? {
root_certs
.add(&tokio_rustls_023::rustls::Certificate(cert.0))
.unwrap();
Expand Down
2 changes: 1 addition & 1 deletion actix-tls/src/connect/rustls_0_21.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub mod reexports {
pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
let mut root_certs = RootCertStore::empty();

for cert in rustls_native_certs::load_native_certs()? {
for cert in rustls_native_certs_06::load_native_certs()? {
root_certs
.add(&tokio_rustls_024::rustls::Certificate(cert.0))
.unwrap();
Expand Down
162 changes: 162 additions & 0 deletions actix-tls/src/connect/rustls_0_22.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//! Rustls based connector service.
//!
//! See [`TlsConnector`] for main connector service factory docs.
use std::{
future::Future,
io,
pin::Pin,
sync::Arc,
task::{Context, Poll},
};

use actix_rt::net::ActixStream;
use actix_service::{Service, ServiceFactory};
use actix_utils::future::{ok, Ready};
use futures_core::ready;
use rustls_pki_types_1::ServerName;
use tokio_rustls::{
client::TlsStream as AsyncTlsStream,
rustls::{ClientConfig, RootCertStore},
Connect as RustlsConnect, TlsConnector as RustlsTlsConnector,
};
use tokio_rustls_025 as tokio_rustls;

use crate::connect::{Connection, Host};

pub mod reexports {
//! Re-exports from the `rustls` v0.22 ecosystem that are useful for connectors.
pub use tokio_rustls_025::{client::TlsStream as AsyncTlsStream, rustls::ClientConfig};
#[cfg(feature = "rustls-0_22-webpki-roots")]
pub use webpki_roots_025::TLS_SERVER_ROOTS;

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Linux / msrv

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Linux / stable

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / macOS / msrv

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / macOS / stable

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Windows / msrv

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Windows / stable

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Windows (32-bit) / msrv

unresolved import `webpki_roots_025`

Check failure on line 32 in actix-tls/src/connect/rustls_0_22.rs

View workflow job for this annotation

GitHub Actions / Windows (32-bit) / stable

unresolved import `webpki_roots_025`
}

/// Returns root certificates via `rustls-native-certs` crate as a rustls certificate store.
///
/// See [`rustls_native_certs::load_native_certs()`] for more info on behavior and errors.
#[cfg(feature = "rustls-0_22-native-roots")]
pub fn native_roots_cert_store() -> io::Result<RootCertStore> {
let mut root_certs = RootCertStore::empty();

for cert in rustls_native_certs_07::load_native_certs()? {
root_certs.add(cert).unwrap();
}

Ok(root_certs)
}

/// Returns standard root certificates from `webpki-roots` crate as a rustls certificate store.
#[cfg(feature = "rustls-0_22-webpki-roots")]
pub fn webpki_roots_cert_store() -> RootCertStore {
let mut root_certs = RootCertStore::empty();
root_certs.extend(webpki_roots_026::TLS_SERVER_ROOTS.to_owned());
root_certs
}

/// Connector service factory using `rustls`.
#[derive(Clone)]
pub struct TlsConnector {
connector: Arc<ClientConfig>,
}

impl TlsConnector {
/// Constructs new connector service factory from a `rustls` client configuration.
pub fn new(connector: Arc<ClientConfig>) -> Self {
TlsConnector { connector }
}

/// Constructs new connector service from a `rustls` client configuration.
pub fn service(connector: Arc<ClientConfig>) -> TlsConnectorService {
TlsConnectorService { connector }
}
}

impl<R, IO> ServiceFactory<Connection<R, IO>> for TlsConnector
where
R: Host,
IO: ActixStream + 'static,
{
type Response = Connection<R, AsyncTlsStream<IO>>;
type Error = io::Error;
type Config = ();
type Service = TlsConnectorService;
type InitError = ();
type Future = Ready<Result<Self::Service, Self::InitError>>;

fn new_service(&self, _: ()) -> Self::Future {
ok(TlsConnectorService {
connector: self.connector.clone(),
})
}
}

/// Connector service using `rustls`.
#[derive(Clone)]
pub struct TlsConnectorService {
connector: Arc<ClientConfig>,
}

impl<R, IO> Service<Connection<R, IO>> for TlsConnectorService
where
R: Host,
IO: ActixStream,
{
type Response = Connection<R, AsyncTlsStream<IO>>;
type Error = io::Error;
type Future = ConnectFut<R, IO>;

actix_service::always_ready!();

fn call(&self, connection: Connection<R, IO>) -> Self::Future {
tracing::trace!("TLS handshake start for: {:?}", connection.hostname());
let (stream, conn) = connection.replace_io(());

match ServerName::try_from(conn.hostname()) {
Ok(host) => ConnectFut::Future {
connect: RustlsTlsConnector::from(Arc::clone(&self.connector))
.connect(host.to_owned(), stream),
connection: Some(conn),
},
Err(_) => ConnectFut::InvalidServerName,
}
}
}

/// Connect future for Rustls service.
#[doc(hidden)]
#[allow(clippy::large_enum_variant)]
pub enum ConnectFut<R, IO> {
InvalidServerName,
Future {
connect: RustlsConnect<IO>,
connection: Option<Connection<R, ()>>,
},
}

impl<R, IO> Future for ConnectFut<R, IO>
where
R: Host,
IO: ActixStream,
{
type Output = io::Result<Connection<R, AsyncTlsStream<IO>>>;

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
Self::InvalidServerName => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"connection parameters specified invalid server name",
))),

Self::Future {
connect,
connection,
} => {
let stream = ready!(Pin::new(connect).poll(cx))?;
let connection = connection.take().unwrap();
tracing::trace!("TLS handshake success: {:?}", connection.hostname());
Poll::Ready(Ok(connection.replace_io(stream).1))
}
}
}
}
61 changes: 39 additions & 22 deletions actix-tls/tests/accept-openssl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
#![cfg(all(
feature = "accept",
feature = "connect",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "openssl"
))]

use std::{convert::TryFrom, io::Write, sync::Arc};
use std::{io::Write as _, sync::Arc};

use actix_rt::net::TcpStream;
use actix_server::TestServer;
use actix_service::ServiceFactoryExt as _;
use actix_tls::accept::openssl::{Acceptor, TlsStream};
use actix_utils::future::ok;
use tokio_rustls::rustls::{Certificate, ClientConfig, RootCertStore, ServerName};
use tokio_rustls_024 as tokio_rustls;
use rustls_pki_types_1::ServerName;
use tokio_rustls::rustls::{ClientConfig, RootCertStore};
use tokio_rustls_025 as tokio_rustls;

fn new_cert_and_key() -> (String, String) {
let cert =
Expand Down Expand Up @@ -48,36 +49,52 @@ fn openssl_acceptor(cert: String, key: String) -> tls_openssl::ssl::SslAcceptor

#[allow(dead_code)]
mod danger {
use std::time::SystemTime;

use tokio_rustls_024::rustls::{
self,
client::{ServerCertVerified, ServerCertVerifier},
};

use super::*;
use tokio_rustls_025::rustls;

#[derive(Debug)]
pub struct NoCertificateVerification;

impl ServerCertVerifier for NoCertificateVerification {
impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &Certificate,
_intermediates: &[Certificate],
_server_name: &ServerName,
_scts: &mut dyn Iterator<Item = &[u8]>,
_ocsp_response: &[u8],
_now: SystemTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
end_entity: &rustls_pki_types_1::CertificateDer::CertificateDer<'_>,
intermediates: &[rustls_pki_types_1::CertificateDer::CertificateDer<'_>],
server_name: &rustls_pki_types_1::CertificateDer::ServerName<'_>,
ocsp_response: &[u8],
now: rustls_pki_types_1::CertificateDer::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls_pki_types_1::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls_pki_types_1::CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}

fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
}

#[allow(dead_code)]
fn rustls_connector(_cert: String, _key: String) -> ClientConfig {
let mut config = ClientConfig::builder()
.with_safe_defaults()
.with_root_certificates(RootCertStore::empty())
.with_no_client_auth();

Expand Down
8 changes: 5 additions & 3 deletions actix-tls/tests/accept-rustls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#![cfg(all(
feature = "accept",
feature = "connect",
feature = "rustls-0_21",
feature = "rustls-0_22",
feature = "openssl"
))]

Expand Down Expand Up @@ -41,8 +41,10 @@ fn rustls_server_config(cert: String, key: String) -> rustls::ServerConfig {
let cert = &mut BufReader::new(cert.as_bytes());
let key = &mut BufReader::new(key.as_bytes());

let cert_chain = certs(cert).unwrap().into_iter().map(Certificate).collect();
let mut keys = pkcs8_private_keys(key).unwrap();
let cert_chain = certs(cert).collect::<Result<Vec<_>, _>>().unwrap();
let mut keys = pkcs8_private_keys(key)
.collect::<Result<Vec<_>, _>>()
.unwrap();

let mut config = ServerConfig::builder()
.with_safe_defaults()
Expand Down
Loading

0 comments on commit 4d7b9d2

Please sign in to comment.