picodata-driver 3.0.0

A native asyncronous cluster-aware Picodata client
Documentation
#[cfg(feature = "tls")]
use std::sync::Arc;

#[cfg(feature = "tls")]
use rustls::ClientConfig;
#[cfg(feature = "tls")]
use rustls::sign::{CertifiedKey, SingleCertAndKey};
#[cfg(feature = "tls")]
use rustls_pemfile::{certs, pkcs8_private_keys};
#[cfg(feature = "tls")]
use rustls_pki_types::PrivateKeyDer;
#[cfg(feature = "tls")]
use tokio_postgres_rustls::MakeRustlsConnect;

#[cfg(feature = "tls")]
use crate::{config::Config, error::Error};

/// Returns a TLS connector if client cert/key are configured.
/// Returns `Ok(None)` when TLS is not configured.
///
/// # Errors
///
/// Returns [`Error::IO`] if cert/key files cannot be read.
#[cfg(feature = "tls")]
pub(crate) fn connector(cfg: &Config) -> Result<Option<MakeRustlsConnect>, Error> {
    let root_store = if let Some(ca_file) = cfg.ssl_ca_file.as_ref() {
        let cert_file = std::fs::File::open(ca_file)?;
        let mut buf = std::io::BufReader::new(cert_file);
        let mut root_store = rustls::RootCertStore::empty();
        for cert in rustls_pemfile::certs(&mut buf) {
            root_store
                .add(cert.expect("CA cert should be valid PEM"))
                .expect("CA cert should be valid X.509");
        }
        root_store
    } else {
        rustls::RootCertStore {
            roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
        }
    };

    let (Some(cert_file), Some(key_file)) = (cfg.ssl_cert_file.as_ref(), cfg.ssl_key_file.as_ref())
    else {
        return Ok(None);
    };

    let cert_file = std::fs::File::open(cert_file)?;
    let mut buf = std::io::BufReader::new(cert_file);
    let mut client_chain = vec![];
    for item in certs(&mut buf) {
        client_chain.push(item.expect("getting cert should not fail"));
    }

    let key_file = std::fs::File::open(key_file)?;
    let mut buf = std::io::BufReader::new(key_file);
    let mut keys = pkcs8_private_keys(&mut buf);
    let client_key_der = PrivateKeyDer::from(
        keys.next()
            .expect("getting key should not fail")
            .expect("getting key should not fail"),
    );

    let builder = ClientConfig::builder().with_root_certificates(root_store);
    let provider = builder.crypto_provider();
    let certified_key = CertifiedKey::from_der(client_chain, client_key_der, provider)
        .expect("creating certified key should not fail");
    let resolver: SingleCertAndKey = SingleCertAndKey::from(Arc::new(certified_key));
    let tls_config = builder.with_client_cert_resolver(Arc::new(resolver));

    Ok(Some(MakeRustlsConnect::new(tls_config)))
}