edgedb-client 0.3.0

EdgeDB database client implementation based on async-std.
Documentation
use std::convert::{TryFrom, TryInto};
use std::io;
use std::sync::Arc;
use std::time::SystemTime;

use anyhow::Context as _;
use rustls::{Certificate, ServerName};
use rustls::client::{ServerCertVerifier, ServerCertVerified};
use tls_api::{TlsConnector as _, TlsConnectorBuilder as _};
use tls_api::{TlsConnectorBox};
use tls_api_rustls::{TlsConnector};
use webpki::{SignatureAlgorithm};


static SIG_ALGS: &[&SignatureAlgorithm] = &[
    &webpki::ECDSA_P256_SHA256,
    &webpki::ECDSA_P256_SHA384,
    &webpki::ECDSA_P384_SHA256,
    &webpki::ECDSA_P384_SHA384,
    &webpki::ED25519,
    &webpki::RSA_PKCS1_2048_8192_SHA256,
    &webpki::RSA_PKCS1_2048_8192_SHA384,
    &webpki::RSA_PKCS1_2048_8192_SHA512,
    &webpki::RSA_PKCS1_3072_8192_SHA384,
];

pub struct NullVerifier;

pub struct NoHostnameVerifier {
    trust_anchors: Vec<OwnedTrustAnchor>,
}

pub struct OwnedTrustAnchor {
    subject: Vec<u8>,
    spki: Vec<u8>,
    name_constraints: Option<Vec<u8>>,
}

impl NoHostnameVerifier {
    pub fn new(trust_anchors: Vec<OwnedTrustAnchor>) -> Self {
        NoHostnameVerifier {
            trust_anchors
        }
    }
}

impl ServerCertVerifier for NoHostnameVerifier {
    fn verify_server_cert(&self,
        end_entity: &Certificate,
        intermediates: &[Certificate],
        _server_name: &ServerName,
        _scts: &mut dyn Iterator<Item = &[u8]>,
        _ocsp_response: &[u8],
        now: SystemTime
    ) -> Result<ServerCertVerified, rustls::Error> {
        let webpki_now = webpki::Time::try_from(now)
            .map_err(|_| rustls::Error::FailedToGetCurrentTime)?;
        let end_entity: webpki::EndEntityCert = end_entity.0[..].try_into()
            .map_err(|e| {
                log::warn!("Could not parse TLS certificate {:#}", e);
                pki_error(e)
            })?;
        let trust_roots = self.trust_anchors.iter()
            .map(Into::into)
            .collect::<Vec<_>>();
        let chain = intermediates.iter()
            .map(|c| c.as_ref())
            .collect::<Vec<_>>();
        end_entity.verify_is_valid_tls_server_cert(
            &SIG_ALGS,
            &webpki::TlsServerTrustAnchors(&trust_roots),
            &chain,
            webpki_now,
        ).map_err(pki_error)?;
        Ok(ServerCertVerified::assertion())
    }
}

impl ServerCertVerifier for NullVerifier {
    fn verify_server_cert(&self,
        _end_entity: &Certificate,
        _intermediates: &[Certificate],
        _server_name: &ServerName,
        _scts: &mut dyn Iterator<Item = &[u8]>,
        _ocsp_response: &[u8],
        _now: SystemTime
    ) -> Result<ServerCertVerified, rustls::Error> {
        Ok(ServerCertVerified::assertion())
    }
}

pub fn connector(
    cert_verifier: Arc<dyn ServerCertVerifier>,
) -> anyhow::Result<TlsConnectorBox>
{
    let mut builder = TlsConnector::builder()?;
    builder.config.dangerous().set_certificate_verifier(cert_verifier);
    builder.set_alpn_protocols(&[b"edgedb-binary"])?;
    let connector = builder.build()?.into_dyn();
    Ok(connector)
}

impl From<webpki::TrustAnchor<'_>> for OwnedTrustAnchor {
    fn from(src: webpki::TrustAnchor) -> OwnedTrustAnchor {
        OwnedTrustAnchor {
            subject: src.subject.into(),
            spki: src.spki.into(),
            name_constraints: src.name_constraints.map(|b| b.into()),
        }
    }
}

impl<'a> Into<webpki::TrustAnchor<'a>> for &'a OwnedTrustAnchor {
    fn into(self) -> webpki::TrustAnchor<'a> {
        webpki::TrustAnchor {
            subject: &self.subject,
            spki: &self.spki,
            name_constraints: self.name_constraints.as_deref(),
        }
    }
}

impl Into<rustls::OwnedTrustAnchor> for OwnedTrustAnchor {
    fn into(self) -> rustls::OwnedTrustAnchor {
        rustls::OwnedTrustAnchor::from_subject_spki_name_constraints(
            self.subject,
            self.spki,
            self.name_constraints,
        )
    }
}

impl OwnedTrustAnchor {
    pub fn read_all(data: &str) -> anyhow::Result<Vec<OwnedTrustAnchor>> {
        let mut result = Vec::new();
        let open_data = rustls_pemfile::read_all(&mut io::Cursor::new(data))
            .context("error reading PEM data")?;
        for item in open_data {
            match item {
                rustls_pemfile::Item::X509Certificate(data) => {
                    result.push(
                        webpki::TrustAnchor::try_from_cert_der(&data)
                        .context("certificate data found, \
                                 but trust anchor is invalid")?
                        .into()
                    );
                }
                | rustls_pemfile::Item::RSAKey(_)
                | rustls_pemfile::Item::PKCS8Key(_)
                | rustls_pemfile::Item::ECKey(_)
                => {
                    log::debug!("Skipping private key in cert data");
                }
                _ => {
                    log::debug!("Skipping unknown item cert data");
                }
            }
        }
        Ok(result)
    }
}

fn pki_error(error: webpki::Error) -> rustls::Error {
    use webpki::Error::*;
    match error {
        BadDer | BadDerTime => rustls::Error::InvalidCertificateEncoding,
        InvalidSignatureForPublicKey
            => rustls::Error::InvalidCertificateSignature,
        UnsupportedSignatureAlgorithm
        | UnsupportedSignatureAlgorithmForPublicKey
        => rustls::Error::InvalidCertificateSignatureType,
        e => {
            rustls::Error::InvalidCertificateData(
                format!("invalid peer certificate: {}", e))
        }
    }
}