pub mod cert;
pub mod key;
use websock_proto::{Error, Result};
use cert::SkipServerVerification;
use rustls::client::{ClientConfig, WebPkiServerVerifier};
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
use rustls::server::ServerConfig;
use std::path::Path;
use std::sync::Arc;
pub type TlsServerConfig = rustls::server::ServerConfig;
pub type TlsClientConfig = rustls::client::ClientConfig;
pub fn generate_self_signed_pair_der(
subject_alt_names: Vec<String>,
) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
let cert = rcgen::generate_simple_self_signed(subject_alt_names)
.map_err(|e| Error::Tls(e.to_string()))?;
let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(cert.signing_key.serialize_der()));
let cert_chain = vec![CertificateDer::from(cert.cert)];
Ok((cert_chain, key))
}
pub fn generate_self_signed_pair_pem(
subject_alt_names: Vec<String>,
) -> Result<(Vec<String>, String)> {
let cert = rcgen::generate_simple_self_signed(subject_alt_names)
.map_err(|e| Error::Tls(e.to_string()))?;
let key = cert.signing_key.serialize_pem();
let cert_chain = vec![cert.cert.pem()];
Ok((cert_chain, key))
}
pub fn load_cert(
cert_path: &Path,
key_path: &Path,
) -> Result<(Vec<CertificateDer<'static>>, PrivateKeyDer<'static>)> {
let cert_chain = cert::load_certs(cert_path)?;
let key = key::load_key(key_path)?;
Ok((cert_chain, key))
}
#[derive(Debug, Clone)]
pub struct TlsConfig {
pub client_config: ClientConfig,
pub server_config: ServerConfig,
}
impl TlsConfig {
pub fn with_cert(cert_path: &Path, key_path: &Path) -> Result<Self> {
let client_config = TlsClientConfigBuilder::new_with_native_certs()?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
let server_config = TlsServerConfigBuilder::new_with_cert(cert_path, key_path)?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
Ok(Self {
client_config,
server_config,
})
}
pub fn with_self_signed_certs() -> Result<Self> {
let client_config = TlsClientConfigBuilder::new_with_native_certs()?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
let server_config =
TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
Ok(Self {
client_config,
server_config,
})
}
pub fn new_native_config() -> Result<Self> {
let client_config = TlsClientConfigBuilder::new_with_native_certs()?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
let server_config =
TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
Ok(Self {
client_config,
server_config,
})
}
pub fn new_insecure_config() -> Result<Self> {
let client_config = TlsClientConfigBuilder::new_insecure()?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
let server_config =
TlsServerConfigBuilder::new_with_self_signed_certs(vec!["localhost".into()])?
.with_alpn_protocols(vec![b"h3".to_vec()])
.build();
Ok(Self {
client_config,
server_config,
})
}
}
#[derive(Debug, Clone)]
pub struct TlsServerConfigBuilder {
inner: TlsServerConfig,
}
impl TlsServerConfigBuilder {
pub fn new_insecure(subject_alt_names: Vec<String>) -> Result<Self> {
let (certs, key) = generate_self_signed_pair_der(subject_alt_names)?;
let inner = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| Error::Tls(e.to_string()))?;
Ok(Self { inner })
}
pub fn new_with_cert(cert_path: &Path, key_path: &Path) -> Result<Self> {
let (certs, key) = load_cert(cert_path, key_path)?;
let inner = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| Error::Tls(e.to_string()))?;
Ok(Self { inner })
}
pub fn new_with_self_signed_certs(subject_alt_names: Vec<String>) -> Result<Self> {
Self::new_insecure(subject_alt_names)
}
pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
self.inner.alpn_protocols = protocols;
self
}
pub fn build(self) -> TlsServerConfig {
self.inner
}
}
#[derive(Debug, Clone)]
pub struct TlsClientConfigBuilder {
inner: TlsClientConfig,
}
impl TlsClientConfigBuilder {
pub fn new_insecure() -> Result<Self> {
let inner = ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(SkipServerVerification::new())
.with_no_client_auth();
Ok(Self { inner })
}
pub fn new_with_native_certs() -> Result<Self> {
let native_certs = cert::get_native_certs()?;
let inner = ClientConfig::builder()
.with_root_certificates(native_certs)
.with_no_client_auth();
Ok(Self { inner })
}
pub fn new_with_webpki_verifier(verifier: Arc<WebPkiServerVerifier>) -> Result<Self> {
let inner = ClientConfig::builder()
.with_webpki_verifier(verifier)
.with_no_client_auth();
Ok(Self { inner })
}
pub fn with_alpn_protocols(mut self, protocols: Vec<Vec<u8>>) -> Self {
self.inner.alpn_protocols = protocols;
self
}
pub fn build(self) -> TlsClientConfig {
self.inner
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_self_signed_pair_der() {
let (cert_chain, key) = generate_self_signed_pair_der(vec!["localhost".into()]).unwrap();
let rustls_server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key);
if let Err(e) = rustls_server_config {
panic!("Failed to create ServerConfig: {e}");
}
}
#[test]
fn test_generate_self_signed_pair_pem() {
let (cert_chain, key) = generate_self_signed_pair_pem(vec!["localhost".into()]).unwrap();
let cert_path = Path::new("cert.pem");
let key_path = Path::new("key.pem");
std::fs::write(cert_path, cert_chain.join("\n")).unwrap();
std::fs::write(key_path, key).unwrap();
let (cert_chain, key) = load_cert(cert_path, key_path).unwrap();
let rustls_server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(cert_chain, key);
if let Err(e) = rustls_server_config {
panic!("Failed to create ServerConfig: {e}");
}
std::fs::remove_file(cert_path).unwrap();
std::fs::remove_file(key_path).unwrap();
}
}