use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use rustls::pki_types::pem::PemObject;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
use super::auth::{Authority, ClientIdentity};
use crate::error::{StreamError, StreamResult};
use crate::protocol::ALPN;
pub const MAX_CONCURRENT_REQUESTS: u32 = 8;
pub const IDLE_TIMEOUT: Duration = Duration::from_secs(30);
pub const KEEP_ALIVE: Duration = Duration::from_secs(10);
fn server_transport() -> quinn::TransportConfig {
let mut t = quinn::TransportConfig::default();
t.max_concurrent_bidi_streams(quinn::VarInt::from_u32(MAX_CONCURRENT_REQUESTS + 1))
.max_concurrent_uni_streams(quinn::VarInt::from_u32(0))
.max_idle_timeout(Some(quinn::IdleTimeout::try_from(IDLE_TIMEOUT).expect("idle timeout fits QUIC's range")));
t
}
fn client_transport() -> quinn::TransportConfig {
let mut t = quinn::TransportConfig::default();
t.keep_alive_interval(Some(KEEP_ALIVE)).max_idle_timeout(Some(quinn::IdleTimeout::try_from(IDLE_TIMEOUT).expect("idle timeout fits QUIC's range")));
t
}
#[derive(Debug, Clone)]
pub enum ServerIdentity {
SelfSigned { names: Vec<String> },
Files { certificate_chain: PathBuf, private_key: PathBuf },
}
#[derive(Debug, Clone)]
pub enum ServerTrust {
Certificates(Vec<Vec<u8>>),
DangerAcceptAnyCertificate,
}
impl ServerTrust {
pub fn from_file(path: &std::path::Path) -> StreamResult<Self> {
let bytes = std::fs::read(path)?;
let pem: Vec<Vec<u8>> = CertificateDer::pem_slice_iter(&bytes).filter_map(Result::ok).map(|c| c.to_vec()).collect();
Ok(Self::Certificates(if pem.is_empty() { vec![bytes] } else { pem }))
}
}
pub struct ServerTls {
pub config: quinn::ServerConfig,
pub certificate: Vec<u8>,
}
fn tls_error(e: impl std::fmt::Display) -> StreamError {
StreamError::Tls(e.to_string())
}
impl ServerTrust {
pub fn from_identity(identity: &ClientIdentity) -> Self {
Self::Certificates(identity.authorities.clone())
}
}
pub fn server_config(identity: &ServerIdentity) -> StreamResult<ServerTls> {
let (chain, key) = identity_chain(identity)?;
let certificate = chain[0].to_vec();
let crypto = rustls::ServerConfig::builder().with_no_client_auth().with_single_cert(chain, key).map_err(tls_error)?;
finish_server(crypto, certificate)
}
pub fn server_config_with_clients(authority: &Authority, identity: &ServerIdentity, anonymous: bool) -> StreamResult<ServerTls> {
let (chain, key, certificate) = match identity {
ServerIdentity::Files { .. } => {
let (chain, key) = identity_chain(identity)?;
let first = chain[0].to_vec();
(chain, key, first)
}
ServerIdentity::SelfSigned { names } => {
let (chain, key) = authority.server_certificate(names)?;
(chain, key, authority.ca_certificate().to_vec())
}
};
let mut roots = rustls::RootCertStore::empty();
roots.add(CertificateDer::from(authority.ca_certificate().to_vec())).map_err(tls_error)?;
let mut verifier = rustls::server::WebPkiClientVerifier::builder(Arc::new(roots));
if anonymous {
verifier = verifier.allow_unauthenticated();
}
let verifier = verifier.build().map_err(tls_error)?;
let crypto = rustls::ServerConfig::builder().with_client_cert_verifier(verifier).with_single_cert(chain, key).map_err(tls_error)?;
finish_server(crypto, certificate)
}
fn finish_server(mut crypto: rustls::ServerConfig, certificate: Vec<u8>) -> StreamResult<ServerTls> {
crypto.alpn_protocols = vec![ALPN.to_vec()];
let quic = quinn::crypto::rustls::QuicServerConfig::try_from(crypto).map_err(tls_error)?;
let mut config = quinn::ServerConfig::with_crypto(Arc::new(quic));
config.transport_config(Arc::new(server_transport()));
Ok(ServerTls { config, certificate })
}
fn identity_chain(identity: &ServerIdentity) -> StreamResult<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
Ok(match identity {
ServerIdentity::SelfSigned { names } => {
let generated = rcgen::generate_simple_self_signed(names.clone()).map_err(tls_error)?;
let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(generated.signing_key.serialize_der()));
(vec![generated.cert.der().clone()], key)
}
ServerIdentity::Files { certificate_chain, private_key } => {
let chain = CertificateDer::pem_file_iter(certificate_chain).map_err(tls_error)?.collect::<Result<Vec<_>, _>>().map_err(tls_error)?;
if chain.is_empty() {
return Err(StreamError::Tls(format!("no certificate in {}", certificate_chain.display())));
}
(chain, PrivateKeyDer::from_pem_file(private_key).map_err(tls_error)?)
}
})
}
pub fn client_config(trust: &ServerTrust) -> StreamResult<quinn::ClientConfig> {
client_config_with(trust, None)
}
pub fn client_config_with(trust: &ServerTrust, identity: Option<&ClientIdentity>) -> StreamResult<quinn::ClientConfig> {
let builder = match trust {
ServerTrust::Certificates(certificates) => {
let mut roots = rustls::RootCertStore::empty();
for der in certificates {
roots.add(CertificateDer::from(der.clone())).map_err(tls_error)?;
}
rustls::ClientConfig::builder().with_root_certificates(roots)
}
ServerTrust::DangerAcceptAnyCertificate => rustls::ClientConfig::builder().dangerous().with_custom_certificate_verifier(Arc::new(AcceptAnyCertificate)),
};
let mut crypto = match identity {
Some(id) => builder
.with_client_auth_cert(vec![CertificateDer::from(id.certificate.clone())], PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(id.private_key.clone())))
.map_err(tls_error)?,
None => builder.with_no_client_auth(),
};
crypto.alpn_protocols = vec![ALPN.to_vec()];
let quic = quinn::crypto::rustls::QuicClientConfig::try_from(crypto).map_err(tls_error)?;
let mut config = quinn::ClientConfig::new(Arc::new(quic));
config.transport_config(Arc::new(client_transport()));
Ok(config)
}
#[derive(Debug)]
struct AcceptAnyCertificate;
impl rustls::client::danger::ServerCertVerifier for AcceptAnyCertificate {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &rustls::pki_types::ServerName<'_>,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
Ok(rustls::client::danger::ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls12_signature(message, cert, dss, &rustls::crypto::ring::default_provider().signature_verification_algorithms)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
rustls::crypto::verify_tls13_signature(message, cert, dss, &rustls::crypto::ring::default_provider().signature_verification_algorithms)
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
rustls::crypto::ring::default_provider().signature_verification_algorithms.supported_schemes()
}
}