use std::io::{Error as IoError, Result as IoResult};
use tokio_rustls::rustls::RootCertStore;
use tokio_rustls::rustls::pki_types::{CertificateDer, pem::PemObject};
pub(crate) mod config;
pub use config::{Keycert, RustlsConfig, ServerConfig};
mod listener;
pub use listener::{RustlsAcceptor, RustlsListener};
pub(crate) fn read_trust_anchor(trust_anchor: &[u8]) -> IoResult<RootCertStore> {
let certs = CertificateDer::pem_slice_iter(trust_anchor)
.collect::<Result<Vec<_>, _>>()
.map_err(|e| IoError::other(format!("failed to parse PEM: {e}")))?;
let mut store = RootCertStore::empty();
for cert in certs {
store.add(cert).map_err(IoError::other)?;
}
Ok(store)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio_rustls::TlsConnector;
use tokio_rustls::rustls::ClientConfig;
use tokio_rustls::rustls::pki_types::ServerName;
use super::*;
use crate::conn::{Accepted, Acceptor, Listener, TcpListener};
#[tokio::test]
async fn test_rustls_listener() {
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let mut acceptor = TcpListener::new("127.0.0.1:0")
.rustls(RustlsConfig::new(
Keycert::new()
.key_from_path("certs/key.pem")
.unwrap()
.cert_from_path("certs/cert.pem")
.unwrap(),
))
.bind()
.await;
let addr = acceptor.holdings()[0]
.local_addr
.clone()
.into_std()
.unwrap();
tokio::spawn(async move {
let stream = TcpStream::connect(addr).await.unwrap();
let trust_anchor = include_bytes!("../../certs/chain.pem");
let client_config = ClientConfig::builder()
.with_root_certificates(read_trust_anchor(trust_anchor.as_slice()).unwrap())
.with_no_client_auth();
let connector = TlsConnector::from(Arc::new(client_config));
let mut tls_stream = connector
.connect(ServerName::try_from("testserver.com").unwrap(), stream)
.await
.unwrap();
tls_stream.write_i32(518).await.unwrap();
});
let Accepted { mut stream, .. } = acceptor.accept(None).await.unwrap();
assert_eq!(stream.read_i32().await.unwrap(), 518);
}
}