Skip to content

Commit

Permalink
read key adn cert from PEM files for QUIC transport
Browse files Browse the repository at this point in the history
not implemented yet for tls, since native_tls sucks
  • Loading branch information
emillynge committed Jan 17, 2022
1 parent 086ea8f commit 0ccb646
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 34 deletions.
17 changes: 12 additions & 5 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub struct TlsConfig {
pub trusted_root: Option<String>,
pub pkcs12: Option<String>,
pub pkcs12_password: Option<String>,
pub pem_server_key: Option<String>,
pub pem_server_cert: Option<String>,
}

fn default_noise_pattern() -> String {
Expand Down Expand Up @@ -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
Expand Down
104 changes: 78 additions & 26 deletions src/transport/quic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -112,6 +115,75 @@ impl Drop for QuicBiStream {
}
}

async fn read_server_pkcs12(config: &TlsConfig, server_crypto: ConfigBuilder<ServerConfig, WantsServerCert>) -> Result<ServerConfig> {
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<rustls::Certificate> = 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<ServerConfig, WantsServerCert>) -> Result<ServerConfig> {
// 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<u8> = 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<Vec<Vec<u8>>> = 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<rustls::Certificate> = 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;
Expand Down Expand Up @@ -158,33 +230,13 @@ impl Transport for QuicTransport {
}

async fn bind<A: ToSocketAddrs + Send + Sync>(&self, addr: A) -> Result<Self::Acceptor> {
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<rustls::Certificate> = 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();

Expand Down
1 change: 0 additions & 1 deletion src/transport/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ pub struct TlsTransport {
connector: Option<TlsConnector>,
tls_acceptor: Option<TlsAcceptor>,
}

#[async_trait]
impl Transport for TlsTransport {
type Acceptor = TcpListener;
Expand Down
4 changes: 2 additions & 2 deletions tests/for_udp/quic_transport.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 0ccb646

Please sign in to comment.