Skip to content

Commit

Permalink
feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 (#2200)
Browse files Browse the repository at this point in the history
* Revert "Revert "feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 (#2143)" (#2174)"

This reverts commit 343fa0d.
  • Loading branch information
toidiu committed May 9, 2024
1 parent 286adde commit bd37960
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 174 deletions.
2 changes: 1 addition & 1 deletion examples/rustls-mtls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ authors = ["Rick Richardson <rick.richardson@gmail.com>", "AWS s2n"]
[dependencies]
# Remove the `provider-tls-default` feature and add `provider-tls-rustls` in order to use the rustls backend
s2n-quic = { version = "1", path = "../../quic/s2n-quic", default-features = false, features = ["provider-address-token-default", "provider-tls-rustls", "provider-event-tracing"] }
rustls-pemfile = "1"
rustls-pemfile = "2"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["ansi"] }
Expand Down
165 changes: 94 additions & 71 deletions examples/rustls-mtls/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use rustls::{
cipher_suite, ClientConfig, Error, RootCertStore, ServerConfig, SupportedCipherSuite,
};
use s2n_quic::provider::tls;
use s2n_quic::provider::tls as s2n_quic_tls_provider;
#[allow(deprecated)]
use s2n_quic::provider::tls::rustls::rustls;
use s2n_quic::provider::tls::rustls::rustls::{
self as rustls_crate,
crypto::CryptoProvider,
pki_types::{CertificateDer, PrivateKeyDer},
server::WebPkiClientVerifier,
Error as RustlsError, RootCertStore,
};
use std::{io::Cursor, path::Path, sync::Arc};
use tokio::{fs::File, io::AsyncReadExt};
use tracing::Level;

static PROTOCOL_VERSIONS: &[&rustls::SupportedProtocolVersion] = &[&rustls::version::TLS13];

pub static DEFAULT_CIPHERSUITES: &[SupportedCipherSuite] = &[
cipher_suite::TLS13_AES_128_GCM_SHA256,
cipher_suite::TLS13_AES_256_GCM_SHA384,
cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
];

pub fn initialize_logger(endpoint: &str) {
use std::sync::Once;

Expand All @@ -42,24 +37,36 @@ pub fn initialize_logger(endpoint: &str) {
}

pub struct MtlsProvider {
root_store: rustls::RootCertStore,
my_cert_chain: Vec<rustls::Certificate>,
my_private_key: rustls::PrivateKey,
root_store: RootCertStore,
my_cert_chain: Vec<CertificateDer<'static>>,
my_private_key: PrivateKeyDer<'static>,
}

impl tls::Provider for MtlsProvider {
type Server = tls::rustls::Server;
type Client = tls::rustls::Client;
type Error = rustls::Error;
impl s2n_quic_tls_provider::Provider for MtlsProvider {
type Server = s2n_quic_tls_provider::rustls::Server;
type Client = s2n_quic_tls_provider::rustls::Client;
type Error = RustlsError;

fn start_server(self) -> Result<Self::Server, Self::Error> {
let verifier = rustls::server::AllowAnyAuthenticatedClient::new(self.root_store);
let mut cfg = ServerConfig::builder()
.with_cipher_suites(DEFAULT_CIPHERSUITES)
.with_safe_default_kx_groups()
.with_protocol_versions(PROTOCOL_VERSIONS)?
.with_client_cert_verifier(Arc::new(verifier))
.with_single_cert(self.my_cert_chain, self.my_private_key)?;
#[allow(deprecated)]
let cipher_suites = s2n_quic_tls_provider::rustls::DEFAULT_CIPHERSUITES;
let default_crypto_provider = CryptoProvider {
cipher_suites: cipher_suites.to_vec(),
..rustls_crate::crypto::aws_lc_rs::default_provider()
};

let verifier = WebPkiClientVerifier::builder_with_provider(
Arc::new(self.root_store),
default_crypto_provider.into(),
)
.build()
.unwrap();

let mut cfg = rustls_crate::ServerConfig::builder_with_protocol_versions(&[
&rustls_crate::version::TLS13,
])
.with_client_cert_verifier(verifier)
.with_single_cert(self.my_cert_chain, self.my_private_key)?;

cfg.ignore_client_order = true;
cfg.max_fragment_size = None;
Expand All @@ -68,10 +75,7 @@ impl tls::Provider for MtlsProvider {
}

fn start_client(self) -> Result<Self::Client, Self::Error> {
let mut cfg = ClientConfig::builder()
.with_cipher_suites(DEFAULT_CIPHERSUITES)
.with_safe_default_kx_groups()
.with_protocol_versions(PROTOCOL_VERSIONS)?
let mut cfg = rustls_crate::ClientConfig::builder()
.with_root_certificates(self.root_store)
.with_client_auth_cert(self.my_cert_chain, self.my_private_key)?;

Expand All @@ -86,71 +90,90 @@ impl MtlsProvider {
ca_cert_pem: A,
my_cert_pem: B,
my_key_pem: C,
) -> Result<Self, Error> {
) -> Result<Self, RustlsError> {
let root_store = into_root_store(ca_cert_pem.as_ref()).await?;
let cert_chain = into_certificate(my_cert_pem.as_ref()).await?;
let private_key = into_private_key(my_key_pem.as_ref()).await?;
Ok(MtlsProvider {
root_store,
my_cert_chain: cert_chain.into_iter().map(rustls::Certificate).collect(),
my_private_key: rustls::PrivateKey(private_key),
my_cert_chain: cert_chain.into_iter().map(CertificateDer::from).collect(),
my_private_key: private_key,
})
}
}

async fn into_certificate(path: &Path) -> Result<Vec<Vec<u8>>, Error> {
async fn read_file(path: &Path) -> Result<Vec<u8>, RustlsError> {
let mut f = File::open(path)
.await
.map_err(|e| Error::General(format!("Failed to load file: {}", e)))?;
.map_err(|e| RustlsError::General(format!("Failed to load file: {}", e)))?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)
.await
.map_err(|e| Error::General(format!("Failed to read file: {}", e)))?;
.map_err(|e| RustlsError::General(format!("Failed to read file: {}", e)))?;
Ok(buf)
}

async fn into_certificate(path: &Path) -> Result<Vec<CertificateDer<'static>>, RustlsError> {
let buf = &read_file(path).await?;
let mut cursor = Cursor::new(buf);
let certs = rustls_pemfile::certs(&mut cursor)
.map(|certs| certs.into_iter().collect())
.map_err(|_| Error::General("Could not read certificate".to_string()))?;
Ok(certs)
rustls_pemfile::certs(&mut cursor)
.map(|cert| {
cert.map_err(|_| RustlsError::General("Could not read certificate".to_string()))
})
.collect()
}

async fn into_root_store(path: &Path) -> Result<RootCertStore, Error> {
let ca_certs = into_certificate(path).await?;
async fn into_root_store(path: &Path) -> Result<RootCertStore, RustlsError> {
let ca_certs: Vec<CertificateDer<'static>> = into_certificate(path)
.await
.map(|certs| certs.into_iter().map(CertificateDer::from))?
.collect();
let mut cert_store = RootCertStore::empty();
cert_store.add_parsable_certificates(ca_certs.as_slice());
cert_store.add_parsable_certificates(ca_certs);
Ok(cert_store)
}

async fn into_private_key(path: &Path) -> Result<Vec<u8>, Error> {
let mut f = File::open(path)
.await
.map_err(|e| Error::General(format!("Failed to load file: {}", e)))?;
let mut buf = Vec::new();
f.read_to_end(&mut buf)
.await
.map_err(|e| Error::General(format!("Failed to read file: {}", e)))?;
async fn into_private_key(path: &Path) -> Result<PrivateKeyDer<'static>, RustlsError> {
let buf = &read_file(path).await?;
let mut cursor = Cursor::new(buf);

let parsers = [
rustls_pemfile::rsa_private_keys,
rustls_pemfile::pkcs8_private_keys,
];
for parser in parsers.iter() {
cursor.set_position(0);

match parser(&mut cursor) {
Ok(keys) if keys.is_empty() => continue,
Ok(mut keys) if keys.len() == 1 => return Ok(rustls::PrivateKey(keys.pop().unwrap()).0),
Ok(keys) => {
return Err(Error::General(format!(
"Unexpected number of keys: {} (only 1 supported)",
keys.len()
)));
macro_rules! parse_key {
($parser:ident, $key_type:expr) => {
cursor.set_position(0);

let keys: Result<Vec<_>, RustlsError> = rustls_pemfile::$parser(&mut cursor)
.map(|key| {
key.map_err(|_| {
RustlsError::General("Could not load any private keys".to_string())
})
})
.collect();
match keys {
// try the next parser
Err(_) => (),
// try the next parser
Ok(keys) if keys.is_empty() => (),
Ok(mut keys) if keys.len() == 1 => {
return Ok($key_type(keys.pop().unwrap()));
}
Ok(keys) => {
return Err(RustlsError::General(format!(
"Unexpected number of keys: {} (only 1 supported)",
keys.len()
)));
}
}
// try the next parser
Err(_) => continue,
}
};
}
Err(Error::General(

// attempt to parse PKCS8 encoded key. Returns early if a key is found
parse_key!(pkcs8_private_keys, PrivateKeyDer::Pkcs8);
// attempt to parse RSA key. Returns early if a key is found
parse_key!(rsa_private_keys, PrivateKeyDer::Pkcs1);
// attempt to parse a SEC1-encoded EC key. Returns early if a key is found
parse_key!(ec_private_keys, PrivateKeyDer::Sec1);

Err(RustlsError::General(
"could not load any valid private keys".to_string(),
))
}
2 changes: 0 additions & 2 deletions quic/s2n-quic-qns/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ http = "1.0"
humansize = "2"
lru = "0.10"
rand = "0.8"
# dangerous_configuration is used to allow for cert verification to be disabled for the amplification limit interop test
rustls = { version = "0.21", features = ["dangerous_configuration", "quic"] }
s2n-codec = { path = "../../common/s2n-codec" }
s2n-quic-core = { path = "../s2n-quic-core", features = ["testing"] }
s2n-quic-h3 = { path = "../s2n-quic-h3" }
Expand Down
84 changes: 66 additions & 18 deletions quic/s2n-quic-qns/src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use s2n_quic::provider::tls as s2n_quic_tls_provider;
#[allow(deprecated)]
use s2n_quic::provider::tls::rustls::rustls as rustls_crate;
use std::{path::PathBuf, str::FromStr};
use structopt::StructOpt;

Expand Down Expand Up @@ -112,16 +115,12 @@ impl Client {

pub fn build_rustls(&self, alpns: &[String]) -> Result<rustls::Client> {
let tls = if self.disable_cert_verification {
use ::rustls::{version, ClientConfig, KeyLogFile};
use rustls_crate::{ClientConfig, KeyLogFile};
use std::sync::Arc;

#[allow(deprecated)]
let cipher_suites = rustls::DEFAULT_CIPHERSUITES;
let mut config = ClientConfig::builder()
.with_cipher_suites(cipher_suites)
.with_safe_default_kx_groups()
.with_protocol_versions(&[&version::TLS13])?
.with_custom_certificate_verifier(Arc::new(rustls::DisabledVerifier))
.dangerous()
.with_custom_certificate_verifier(Arc::new(rustls::DisabledVerifier::new()))
.with_no_client_auth();
config.max_fragment_size = None;
config.alpn_protocols = alpns.iter().map(|p| p.as_bytes().to_vec()).collect();
Expand Down Expand Up @@ -268,8 +267,11 @@ pub mod s2n_tls {

pub mod rustls {
use super::*;
#[allow(deprecated)]
pub use s2n_quic::provider::tls::rustls::DEFAULT_CIPHERSUITES;
use rustls_crate::{
client::danger,
crypto::CryptoProvider,
pki_types::{CertificateDer, ServerName, UnixTime},
};
pub use s2n_quic::provider::tls::rustls::{
certificate::{Certificate, IntoCertificate, IntoPrivateKey, PrivateKey},
Client, Server,
Expand All @@ -291,19 +293,65 @@ pub mod rustls {
})
}

pub struct DisabledVerifier;
#[derive(Debug)]
pub struct DisabledVerifier(CryptoProvider);

impl DisabledVerifier {
pub fn new() -> Self {
#[allow(deprecated)]
let cipher_suites = s2n_quic_tls_provider::rustls::DEFAULT_CIPHERSUITES;

let default_crypto_provider = CryptoProvider {
cipher_suites: cipher_suites.to_vec(),
..rustls_crate::crypto::aws_lc_rs::default_provider()
};

DisabledVerifier(default_crypto_provider)
}
}

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

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls_crate::DigitallySignedStruct,
) -> Result<danger::HandshakeSignatureValid, rustls_crate::Error> {
rustls_crate::crypto::verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls_crate::DigitallySignedStruct,
) -> Result<danger::HandshakeSignatureValid, rustls_crate::Error> {
rustls_crate::crypto::verify_tls13_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}

fn supported_verify_schemes(&self) -> Vec<rustls_crate::SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}
}
Expand Down
5 changes: 3 additions & 2 deletions quic/s2n-quic-rustls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ exclude = ["corpus.tar.gz"]

[dependencies]
bytes = { version = "1", default-features = false }
rustls = { version = "0.21", features = ["quic"] }
rustls-pemfile = "1"
# By [default](https://docs.rs/crate/rustls/latest/features) rustls includes the `tls12` feature.
rustls = { version = "0.23", default-features = false, features=["std", "aws-lc-rs", "logging"] }
rustls-pemfile = "2"
s2n-codec = { version = "=0.37.0", path = "../../common/s2n-codec", default-features = false, features = ["alloc"] }
s2n-quic-core = { version = "=0.37.0", path = "../s2n-quic-core", default-features = false, features = ["alloc"] }
s2n-quic-crypto = { version = "=0.37.0", path = "../s2n-quic-crypto", default-features = false }
Expand Down
Loading

0 comments on commit bd37960

Please sign in to comment.