use std::time::{SystemTime, UNIX_EPOCH};
use dope_futures::{AsyncRead, AsyncWrite};
use shin::client::{Config, OwnedTrustAnchor, Verifier};
use crate::roots;
use crate::state::{TlsError, TlsState};
use crate::future::stream::TlsStream;
#[derive(Clone)]
pub struct TlsConnector {
kind: Kind,
}
#[derive(Clone)]
enum Kind {
Ready(Config),
PendingX509 {
anchors: Vec<OwnedTrustAnchor>,
transport_params: Vec<u8>,
alpn_protocols: Vec<Vec<u8>>,
},
}
impl TlsConnector {
pub fn new(config: Config) -> Self {
Self {
kind: Kind::Ready(config),
}
}
pub fn new_x509(
anchors: Vec<OwnedTrustAnchor>,
hostname: Vec<u8>,
now_seconds: u64,
transport_params: Vec<u8>,
) -> Self {
Self {
kind: Kind::Ready(Self::make_config(
Verifier::X509 {
anchors,
hostname,
now_seconds,
},
transport_params,
Vec::new(),
)),
}
}
pub fn new_rpk(expected_pubkey: [u8; 32], transport_params: Vec<u8>) -> Self {
Self {
kind: Kind::Ready(Self::make_config(
Verifier::RawPublicKey { expected_pubkey },
transport_params,
Vec::new(),
)),
}
}
pub fn from_webpki_roots() -> Self {
Self::from_anchors(roots::WebpkiRoots::anchors())
}
pub fn from_anchors(anchors: Vec<OwnedTrustAnchor>) -> Self {
Self {
kind: Kind::PendingX509 {
anchors,
transport_params: Vec::new(),
alpn_protocols: Vec::new(),
},
}
}
pub fn with_alpn(mut self, protocols: Vec<Vec<u8>>) -> Self {
match &mut self.kind {
Kind::Ready(cfg) => cfg.alpn_protocols = protocols,
Kind::PendingX509 { alpn_protocols, .. } => *alpn_protocols = protocols,
}
self
}
pub async fn connect<IO>(&self, io: IO) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite,
{
let config = match &self.kind {
Kind::Ready(cfg) => cfg.clone(),
Kind::PendingX509 { .. } => return Err(TlsError::Other),
};
Self::drive(config, io).await
}
pub async fn connect_hostname<IO>(
&self,
hostname: &str,
io: IO,
) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite,
{
let config = match &self.kind {
Kind::Ready(cfg) => cfg.clone(),
Kind::PendingX509 {
anchors,
transport_params,
alpn_protocols,
} => Self::make_config(
Verifier::X509 {
anchors: anchors.clone(),
hostname: hostname.as_bytes().to_vec(),
now_seconds: Self::now_seconds(),
},
transport_params.clone(),
alpn_protocols.clone(),
),
};
Self::drive(config, io).await
}
async fn drive<IO>(config: Config, io: IO) -> Result<TlsStream<IO>, TlsError>
where
IO: AsyncRead + AsyncWrite,
{
let state = TlsState::new_client(config)?;
let mut stream = TlsStream::new(io, state);
stream.handshake().await?;
Ok(stream)
}
fn make_config(
verifier: Verifier,
transport_params: Vec<u8>,
alpn_protocols: Vec<Vec<u8>>,
) -> Config {
Config {
verifier,
transport_params,
alpn_protocols,
resumption: None,
enable_early_data: false,
}
}
fn now_seconds() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0)
}
}