tls-api-openssl 0.12.1

TLS API implementation over openssl crate
Documentation
use openssl::pkcs12::ParsedPkcs12_2;

use tls_api::async_as_sync::AsyncIoAsSyncIo;
use tls_api::spi_acceptor_common;
use tls_api::AsyncSocket;
use tls_api::AsyncSocketBox;
use tls_api::ImplInfo;

use crate::encode_alpn_protos;
use crate::handshake::HandshakeFuture;
use anyhow::Context;
use std::future::Future;

pub struct TlsAcceptorBuilder(pub openssl::ssl::SslAcceptorBuilder);

pub struct TlsAcceptor(pub openssl::ssl::SslAcceptor);

fn to_openssl_pkcs12(pkcs12: &[u8], passphrase: &str) -> anyhow::Result<ParsedPkcs12_2> {
    let pkcs12 = openssl::pkcs12::Pkcs12::from_der(pkcs12)?;
    pkcs12.parse2(passphrase).context("Parse passphrase")
}

impl tls_api::TlsAcceptorBuilder for TlsAcceptorBuilder {
    type Acceptor = TlsAcceptor;

    type Underlying = openssl::ssl::SslAcceptorBuilder;

    fn underlying_mut(&mut self) -> &mut openssl::ssl::SslAcceptorBuilder {
        &mut self.0
    }

    fn set_alpn_protocols(&mut self, protocols: &[&[u8]]) -> anyhow::Result<()> {
        let protocols = encode_alpn_protos(protocols)?;
        self.0
            .set_alpn_select_callback(move |_ssl, client_protocols| {
                match openssl::ssl::select_next_proto(&protocols, client_protocols) {
                    Some(selected) => Ok(selected),
                    None => Err(openssl::ssl::AlpnError::NOACK),
                }
            });
        Ok(())
    }

    fn build(self) -> anyhow::Result<TlsAcceptor> {
        Ok(TlsAcceptor(self.0.build()))
    }
}

impl TlsAcceptorBuilder {
    pub fn builder_mut(&mut self) -> &mut openssl::ssl::SslAcceptorBuilder {
        &mut self.0
    }
}

impl TlsAcceptor {
    fn accept_impl<S>(
        &self,
        stream: S,
    ) -> impl Future<Output = anyhow::Result<crate::TlsStream<S>>> + '_
    where
        S: AsyncSocket,
    {
        HandshakeFuture::Initial(
            move |stream| self.0.accept(stream),
            AsyncIoAsSyncIo::new(stream),
        )
    }
}

impl tls_api::TlsAcceptor for TlsAcceptor {
    type Builder = TlsAcceptorBuilder;

    type Underlying = openssl::ssl::SslAcceptor;
    type TlsStream = crate::TlsStream<AsyncSocketBox>;

    fn underlying_mut(&mut self) -> &mut Self::Underlying {
        &mut self.0
    }

    const IMPLEMENTED: bool = true;
    const SUPPORTS_ALPN: bool = true;
    const SUPPORTS_DER_KEYS: bool = true;
    const SUPPORTS_PKCS12_KEYS: bool = true;

    fn info() -> ImplInfo {
        crate::into()
    }

    fn builder_from_der_key(cert: &[u8], key: &[u8]) -> anyhow::Result<TlsAcceptorBuilder> {
        let cert = openssl::x509::X509::from_der(cert).map_err(anyhow::Error::new)?;
        let pkey = openssl::pkey::PKey::private_key_from_der(key).map_err(anyhow::Error::new)?;

        let mut builder =
            openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls())
                .map_err(anyhow::Error::new)?;

        builder
            .set_certificate(cert.as_ref())
            .map_err(anyhow::Error::new)?;
        builder
            .set_private_key(pkey.as_ref())
            .map_err(anyhow::Error::new)?;

        Ok(TlsAcceptorBuilder(builder))
    }

    fn builder_from_pkcs12(pkcs12: &[u8], passphrase: &str) -> anyhow::Result<TlsAcceptorBuilder> {
        let mut builder =
            openssl::ssl::SslAcceptor::mozilla_intermediate(openssl::ssl::SslMethod::tls())
                .map_err(anyhow::Error::new)?;

        let pkcs12 = to_openssl_pkcs12(pkcs12, passphrase)?;

        if let Some(cert) = &pkcs12.cert {
            builder.set_certificate(cert).map_err(anyhow::Error::new)?;
        }
        if let Some(pkey) = &pkcs12.pkey {
            builder.set_private_key(pkey).map_err(anyhow::Error::new)?;
        }

        if let Some(chain) = pkcs12.ca {
            for x509 in chain {
                builder
                    .add_extra_chain_cert(x509)
                    .map_err(anyhow::Error::new)?;
            }
        }

        Ok(TlsAcceptorBuilder(builder))
    }

    spi_acceptor_common!(crate::TlsStream<S>);
}