diff --git a/src/config.rs b/src/config.rs index e8b671d8..2ed21667 100644 --- a/src/config.rs +++ b/src/config.rs @@ -83,6 +83,8 @@ pub struct TlsConfig { pub trusted_root: Option, pub pkcs12: Option, pub pkcs12_password: Option, + pub pem_server_key: Option, + pub pem_server_cert: Option, } fn default_noise_pattern() -> String { @@ -191,11 +193,16 @@ impl Config { fn validate_tls_config(tls_config: &TlsConfig, is_server: bool) -> Result<()>{ if is_server { - tls_config - .pkcs12 - .as_ref() - .and(tls_config.pkcs12_password.as_ref()) - .ok_or(anyhow!("Missing `pkcs12` or `pkcs12_password`"))?; + if tls_config.pem_server_key.is_some() { + tls_config.pem_server_cert.as_ref().ok_or( + anyhow!("`pem_server_key` provided but `pem_server_cert` is missing"))?; + } else { + tls_config + .pkcs12 + .as_ref() + .and(tls_config.pkcs12_password.as_ref()) + .ok_or(anyhow!("Missing `pkcs12` or `pkcs12_password`"))?; + } } else { tls_config .trusted_root diff --git a/src/transport/quic.rs b/src/transport/quic.rs index f4126e7c..c6ea8760 100644 --- a/src/transport/quic.rs +++ b/src/transport/quic.rs @@ -14,6 +14,9 @@ use async_trait::async_trait; use futures_util::{StreamExt}; use p12::PFX; use quinn::{Connecting, Connection, Endpoint, EndpointConfig, Incoming}; +use rustls::{ConfigBuilder, ServerConfig}; +use rustls_pemfile::{Item, read_one, read_all}; +use rustls::server::WantsServerCert; use tokio::fs; use tokio::io::{AsyncWrite, ReadBuf}; use tokio::net::{ToSocketAddrs, UdpSocket}; @@ -112,6 +115,75 @@ impl Drop for QuicBiStream { } } +async fn read_server_pkcs12(config: &TlsConfig, server_crypto: ConfigBuilder) -> Result { + let buf = fs::read(config.pkcs12.as_ref() + .with_context(|| "Config `quic.pkcs12` was not provided")?) + .await + .with_context(|| "Failed to read the `quic.pkcs12`")?; + + let pfx = PFX::parse(buf.as_slice()).with_context(|| "Failed to parse `quic.pkcs12`")?; + + let keys = pfx.key_bags(config.pkcs12_password.as_ref() + .with_context(|| "Expected `quic.pkcs12_password` value to be set")?) + .with_context(|| "Could not decrypt `quic.pkcs12 with `quic.pkcs12_password`")?; + + let certs = pfx.cert_bags(config.pkcs12_password.as_ref() + .with_context(|| "Config `quic.pkcs12_password` was not provided")?) + .with_context(|| "Could not decrypt `quic.pkcs12` using `quic.pkcs12_password`")?; + + let chain: Vec = certs + .into_iter() + .map(|cert_bytes| rustls::Certificate(cert_bytes)) + .collect(); + let key = rustls::PrivateKey(keys.into_iter().next() + .with_context(|| "No keys found in `quic.pkcs12`")?); + server_crypto.with_single_cert(chain, key) + .with_context(|| "Server keys invalid") +} + +async fn read_server_pem(config: &TlsConfig, server_crypto: ConfigBuilder) -> Result { + // unwrap, since caller should have checked that pem_server_key exists + let buf = fs::read(config.pem_server_key.as_ref().unwrap()) + .await + .with_context(|| "Failed to read the `quic.pem_server_key`")?; + let mut bufr = std::io::BufReader::new(buf.as_slice()); + let key: Vec = match read_one(&mut bufr).with_context(|| "Could not parse `quic.pem_server_key`")? { + None => {Err(anyhow!("`quic.pem_server_key` contained no keys"))} + Some(item) => { match item { + Item::X509Certificate(_) => { Err(anyhow!("`quic.pem_server_key` should contain keys, not certificates"))} + Item::RSAKey(der_key) => {Ok(der_key)} + Item::PKCS8Key(der_key) => {Ok(der_key)} + }}}?; + + let buf = fs::read(config.pem_server_cert.as_ref() + .with_context(|| "`quic.pem_server_key` was provided, yet `quic.pem_server_cert` is missing")?) + .await + .with_context(|| "Failed to read the `quic.pem_server_cert`")?; + + let mut bufr = std::io::BufReader::new(buf.as_slice()); + let certs: Result>> = read_all(&mut bufr).with_context(|| "Could not parse `quic.pem_server_cert`")? + .into_iter().map(|item| match item { + Item::X509Certificate(der_cert) => { Ok(der_cert)} + Item::RSAKey(_) | Item::PKCS8Key(_) => { + Err(anyhow!("`quic.pem_server_cert` should contain certificates, not keys"))} + }) + .collect(); + + let certs = certs?; + if certs.is_empty() { + return Err(anyhow!("`quic.pem_server_cert` contained no certs")); + } + + let chain: Vec = certs + .into_iter() + .map(|cert_bytes| rustls::Certificate(cert_bytes)) + .collect(); + let key = rustls::PrivateKey(key); + + server_crypto.with_single_cert(chain, key) + .with_context(|| "Server keys invalid") +} + #[async_trait] impl Transport for QuicTransport { type Acceptor = QuicAcceptor; @@ -158,33 +230,13 @@ impl Transport for QuicTransport { } async fn bind(&self, addr: A) -> Result { - let buf = fs::read(self.config.pkcs12.as_ref() - .with_context(|| "Config `quic.pkcs12` was not provided")?) - .await - .with_context(|| "Failed to read the `quic.pkcs12`")?; - - let pfx = PFX::parse(buf.as_slice()).with_context(|| "Failed to parse `quic.pkcs12`")?; - - let keys = pfx.key_bags(self.config.pkcs12_password.as_ref() - .with_context(|| "Expected `quic.pkcs12_password` value to be set")?) - .with_context(|| "Could not decrypt `quic.pkcs12 with `quic.pkcs12_password`")?; - - let certs = pfx.cert_bags(self.config.pkcs12_password.as_ref() - .with_context(|| "Config `quic.pkcs12_password` was not provided")?) - .with_context(|| "Could not decrypt `quic.pkcs12` using `quic.pkcs12_password`")?; - - let chain: Vec = certs - .into_iter() - .map(|cert_bytes| rustls::Certificate(cert_bytes)) - .collect(); - let key = rustls::PrivateKey(keys.into_iter().next() - .with_context(|| "No keys found in `quic.pkcs12`")?); - - let mut server_crypto = rustls::ServerConfig::builder() + let server_crypto = rustls::ServerConfig::builder() .with_safe_defaults() - .with_no_client_auth() - .with_single_cert(chain, key) - .with_context(|| "Server keys invalid")?; + .with_no_client_auth(); + let mut server_crypto = match self.config.pem_server_key { + None => {read_server_pkcs12(&self.config, server_crypto).await?} + Some(_) => {read_server_pem(&self.config, server_crypto).await?} + }; server_crypto.alpn_protocols = ALPN_QUIC_TUNNEL.iter().map(|&x| x.into()).collect(); diff --git a/src/transport/tls.rs b/src/transport/tls.rs index b08b2e59..8d74fe38 100644 --- a/src/transport/tls.rs +++ b/src/transport/tls.rs @@ -16,7 +16,6 @@ pub struct TlsTransport { connector: Option, tls_acceptor: Option, } - #[async_trait] impl Transport for TlsTransport { type Acceptor = TcpListener; diff --git a/tests/for_udp/quic_transport.toml b/tests/for_udp/quic_transport.toml index daa11cd9..0fc77a7c 100644 --- a/tests/for_udp/quic_transport.toml +++ b/tests/for_udp/quic_transport.toml @@ -22,8 +22,8 @@ default_token = "default_token_if_not_specify" [server.transport] type = "quic" [server.transport.quic] -pkcs12 = "examples/quic/test_server.pfx" -pkcs12_password = "1234" +pem_server_key = "examples/quic/test_server.key.pem" +pem_server_cert = "examples/quic/test_server.cert.pem" [server.services.echo] type = "udp"