Skip to content

Commit

Permalink
feat(s2n-quic-rustls): update rustls from 0.21 to 0.23 (#2143)
Browse files Browse the repository at this point in the history
  • Loading branch information
toidiu authored Mar 22, 2024
1 parent aebd430 commit 15f234c
Show file tree
Hide file tree
Showing 17 changed files with 358 additions and 182 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ jobs:
target: native
env: S2N_QUIC_PLATFORM_FEATURES_OVERRIDE="mtu_disc,pktinfo,tos,socket_msg"
steps:
- uses: ilammy/setup-nasm@v1
- uses: actions/checkout@v4
with:
lfs: true
Expand Down
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
151 changes: 83 additions & 68 deletions examples/rustls-mtls/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
// 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::{
self as s2n_quic_tls_provider,
rustls::rustls::{
// types from the external rustls crate
pki_types::{CertificateDer, PrivateKeyDer},
server::WebPkiClientVerifier,
Error as RustlsError,
RootCertStore,
},
};
use s2n_quic::provider::{tls, tls::rustls::rustls};
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 @@ -40,23 +38,26 @@ 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))
let default_crypto_provider = s2n_quic_tls_provider::rustls::default_crypto_provider()?;
let verifier = WebPkiClientVerifier::builder_with_provider(
Arc::new(self.root_store),
default_crypto_provider.into(),
)
.build()
.unwrap();
let mut cfg = s2n_quic_tls_provider::rustls::server_config_builder()?
.with_client_cert_verifier(verifier)
.with_single_cert(self.my_cert_chain, self.my_private_key)?;

cfg.ignore_client_order = true;
Expand All @@ -66,10 +67,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 = s2n_quic_tls_provider::rustls::client_config_builder()?
.with_root_certificates(self.root_store)
.with_client_auth_cert(self.my_cert_chain, self.my_private_key)?;

Expand All @@ -84,71 +82,88 @@ 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);

Err(RustlsError::General(
"could not load any valid private keys".to_string(),
))
}
9 changes: 9 additions & 0 deletions quic/s2n-quic-core/certs/generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0

# immediately bail if any command fails
set -e

# generate PKCS#1 encoded RSA key (openSSL 1.1.1)
echo "generating PKCS #1 encoded RSA key"
openssl genrsa -f4 -out key_pkcs1.pem 2048
27 changes: 27 additions & 0 deletions quic/s2n-quic-core/certs/key_pkcs1.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEA6mOufxxreztVa/JdBkgADQoPpj5WDmfjnJEU4SOrvKQ3TPAX
ITd1dLMSKRzxIYkUlOS9oNngzxTr7nbgFUtn08sgHfxl6KTbhQgqw9caPXWGtnaQ
YgdNFxfaU03fAhREnnGjmzlpBUFMVZEfa5ETOOGPsLIRrOs8uzBIhk+8IksrNkuX
izuRsQ9UThceDjLxO4wnDC+NRS5P+/rf0VOLfWNnxo+ZOlTsOEflvUkVwkiCQXE/
9ELz3pLlH7vZwefBwv8RiVEVxUiNQUAIp1YudBF2Rfy7OMwL0DKWxpUo6SW3h5uA
HPlf6UEDhfCFszG8R96E4oxe23ISIUWjRSabHQIDAQABAoIBAQCJckDeWy0QC6Aw
9+PjDg+xlcfNhdpzAirwIgeran6H1Yh0PophuSLQdz3cDBO+xaLjGSu2Cm4RIUSl
BuKquhg9k2zXCK49+YadAUgKpbXGeoevseFDCuMC+sLVfOGcV4RRoXP+T3EtWrhH
qR1St7Uc8vCO/iztSNjJMCwnTtSVCn4Mr7j8Tqf8EMoDq9zx4mZaOpeUfcb23Hp7
NpRkNRU5P1RCQFrw5gjp3SwUSzJfzgewovr2U2Je4naNPgE/VjQrSB7WBPDHJ0rO
hiuKHq3e5T3DCL6SAzcwRHS0omB5TQFpt3H3yjmW3D1srUNcF1Ma8namhO8DM3B5
0QORLzUhAoGBAPkmYBvqaOOpSZw2i/ZlR27hA4D/sM5IEBsfwHIZcjFvkbRB1lOe
4OW3WhYYX/2pIHJpqyoZrkI6LYTzbTSHBhSD+Szqr+TJUWEbHjG+pXhrJSuZJNub
Qo4JU5QfbLZ3EXosAyl3Bhh3wmTw+sGqCPmq2EarKquvW9IcQJCauGi5AoGBAPDV
ak80MWvfTnfapZpQzVhli5ZoVPhWk38Fv2WvqeuZ16jjy9Q3kvLHvOXL6RiqGnue
OqWzy+eafeSZ+ZdAmcBeFSqmGuhjjgSCQ765/kFU97ECvm4U/R3bk6h1YKydOJXo
fStO7r8zDYWuulwyfp93c3AEKLqgE7G9S2FY+kuFAoGBAJ4N6TRsXUPndcoClIGn
uEwvtJBWJGyILKd4WhZH7DhORclrZrK/fH5d9WlE50g8zVenjyzzA6gBzjSkVGfZ
LFNBcYY8D3988wI+OMZn4gLlPbxNt6MU+ICwi/PQh5+tI0o0t4DLf/NvrcNpWDoZ
Sv5PkkoqdbRaV06QG6lgbZIJAoGBAL1Znk8NoznEBcbYItH0a67vj9M+zme+chMx
qq1BcuCpQVUpXQ3Kb//wKMWoD+nzfATrgALeHRhIcGj24rUX9oq3rf6tQISsGXHU
FX10cMJfEquak6yrVeNOy4ZsWwj8SAwdEaSmV7H+ashLe3yXutSYLyvIKVmqGDHr
ucOdGJWdAoGACpQMjbj44yWD8W3FE2cC6dwet2gkjWEuZPDcXrmUzAy90fyT+x07
dMqansk4ThlHsfHld+znwJQ20CRCmXWrmVH4vIUNjJK6c+jrvaGYGcoW/bD3Xoeg
qjjmXua7hGVx7wrPIwXfxEG1tJzrDRtEROnCiaeMmzNbVvkQkZTzzkQ=
-----END RSA PRIVATE KEY-----
1 change: 1 addition & 0 deletions quic/s2n-quic-core/src/crypto/tls/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod certificates {
}

pem!(KEY_PEM, "key.pem");
pem!(KEY_PKCS1_PEM, "key_pkcs1.pem");
pem!(CERT_PEM, "cert.pem");
der!(KEY_DER, "key.der");
der!(CERT_DER, "cert.der");
Expand Down
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
70 changes: 54 additions & 16 deletions quic/s2n-quic-qns/src/tls.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use crate::Result;
use crate::{tls::rustls::DisabledVerifier, Result};
use s2n_quic::provider::tls::{self as s2n_quic_tls_provider, rustls::rustls as rustls_crate};
use std::{path::PathBuf, str::FromStr};
use structopt::StructOpt;

Expand Down Expand Up @@ -113,14 +114,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::KeyLogFile;
use std::sync::Arc;

let mut config = ClientConfig::builder()
.with_cipher_suites(rustls::DEFAULT_CIPHERSUITES)
.with_safe_default_kx_groups()
.with_protocol_versions(&[&version::TLS13])?
.with_custom_certificate_verifier(Arc::new(rustls::DisabledVerifier))
let mut config = s2n_quic_tls_provider::rustls::client_config_builder()?
.dangerous()
.with_custom_certificate_verifier(Arc::new(DisabledVerifier))
.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 @@ -265,9 +264,13 @@ pub mod s2n_tls {

pub mod rustls {
use super::*;
use rustls_crate::{
client::danger,
pki_types::{CertificateDer, ServerName, UnixTime},
};
pub use s2n_quic::provider::tls::rustls::{
certificate::{Certificate, IntoCertificate, IntoPrivateKey, PrivateKey},
Client, Server, DEFAULT_CIPHERSUITES,
default_crypto_provider, Client, Server,
};

pub fn ca(ca: Option<&PathBuf>) -> Result<Certificate> {
Expand All @@ -286,19 +289,54 @@ pub mod rustls {
})
}

#[derive(Debug)]
pub struct DisabledVerifier;

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,
&default_crypto_provider()?.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,
&default_crypto_provider()?.signature_verification_algorithms,
)
}

fn supported_verify_schemes(&self) -> Vec<rustls_crate::SignatureScheme> {
default_crypto_provider()
.unwrap()
.signature_verification_algorithms
.supported_schemes()
}
}
}
Expand Down
Loading

0 comments on commit 15f234c

Please sign in to comment.