Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 #2200

Merged
merged 3 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading