dope-tls 0.1.1

TCP TLS streams for dope runtime, shin-backed
Documentation

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)
    }
}