gateryx 0.1.6

Secure HTTP gateway for IoT and Industrial applications
Documentation
use crate::{Result, error::ConfigCheckIssue, setup::generate_test_x509_pair};
use std::{
    path::{Path, PathBuf},
    sync::Arc,
};

use serde::Deserialize;
use tracing::warn;
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};

type TlsResult<T> = std::result::Result<T, rustls::Error>;

#[derive(Deserialize, Copy, Clone)]
pub enum Protocol {
    #[serde(rename = "tls1.2")]
    TLSv1_2,
    #[serde(rename = "tls1.3")]
    TLSv1_3,
}

impl Protocol {
    pub fn as_supported_rustls_version(self) -> &'static rustls::SupportedProtocolVersion {
        match self {
            Protocol::TLSv1_2 => &rustls::version::TLS12,
            Protocol::TLSv1_3 => &rustls::version::TLS13,
        }
    }
}

pub fn default_protocols() -> Vec<Protocol> {
    vec![Protocol::TLSv1_2, Protocol::TLSv1_3]
}

#[derive(Deserialize, Clone, Zeroize, ZeroizeOnDrop)]
#[serde(deny_unknown_fields)]
pub struct Config {
    #[zeroize(skip)]
    cert: PathBuf,
    #[zeroize(skip)]
    key: PathBuf,
    #[serde(skip)]
    #[zeroize(skip)]
    pub cert_buf: Arc<parking_lot::Mutex<Zeroizing<Vec<u8>>>>,
    #[serde(skip)]
    #[zeroize(skip)]
    pub key_buf: Arc<parking_lot::Mutex<Zeroizing<Vec<u8>>>>,
    #[serde(default = "default_protocols")]
    #[zeroize(skip)]
    pub protocols: Vec<Protocol>,
}

impl Config {
    pub fn load_files(&mut self) -> Result<()> {
        if !self.cert.exists() && !self.key.exists() {
            warn!(
                cert = %self.cert.display(),
                key = %self.key.display(),
                "Cert/key files do not exists, generating self-signed certificate");
            generate_test_x509_pair(&self.cert, &self.key)?;
        }
        *self.cert_buf.lock() = Zeroizing::new(fs_err::read(&self.cert)?);
        *self.key_buf.lock() = Zeroizing::new(fs_err::read(&self.key)?);
        Ok(())
    }
    pub fn canonicalize_path(&mut self, work_dir: &Path) {
        if !self.cert.is_absolute() {
            self.cert = work_dir.join(&self.cert);
        }
        if !self.key.is_absolute() {
            self.key = work_dir.join(&self.key);
        }
    }
    pub fn check(&self, config_dir: &Path) -> Vec<ConfigCheckIssue> {
        let mut issues = Vec::new();
        let cert_path = if self.cert.is_absolute() {
            self.cert.clone()
        } else {
            config_dir.join(&self.cert)
        };
        let key_path = if self.key.is_absolute() {
            self.key.clone()
        } else {
            config_dir.join(&self.key)
        };
        if !cert_path.exists() && !key_path.exists() {
            issues.push(ConfigCheckIssue::Warning(format!(
                "TLS certificate and key paths do not exist: {}, {}, a new self-signed will be generated",
                cert_path.display(),
                key_path.display()
            )));
            return issues;
        }
        if !cert_path.exists() {
            issues.push(ConfigCheckIssue::Error(format!(
                "TLS certificate path does not exist: {}",
                cert_path.display()
            )));
        }
        if !key_path.exists() {
            issues.push(ConfigCheckIssue::Error(format!(
                "TLS key path does not exist: {}",
                key_path.display()
            )));
        }
        issues
    }
}

#[derive(Debug)]
pub struct NoCertVerifier {}

impl rustls::client::danger::ServerCertVerifier for NoCertVerifier {
    fn verify_server_cert(
        &self,
        _end_entity: &rustls::pki_types::CertificateDer<'_>,
        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
        _server_name: &rustls::pki_types::ServerName<'_>,
        _ocsp_response: &[u8],
        _now: rustls::pki_types::UnixTime,
    ) -> TlsResult<rustls::client::danger::ServerCertVerified> {
        Ok(rustls::client::danger::ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> TlsResult<rustls::client::danger::HandshakeSignatureValid> {
        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> TlsResult<rustls::client::danger::HandshakeSignatureValid> {
        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
        vec![
            rustls::SignatureScheme::RSA_PKCS1_SHA256,
            rustls::SignatureScheme::ECDSA_NISTP256_SHA256,
            rustls::SignatureScheme::RSA_PKCS1_SHA384,
            rustls::SignatureScheme::ECDSA_NISTP384_SHA384,
            rustls::SignatureScheme::RSA_PKCS1_SHA512,
            rustls::SignatureScheme::ECDSA_NISTP521_SHA512,
            rustls::SignatureScheme::RSA_PSS_SHA256,
            rustls::SignatureScheme::RSA_PSS_SHA384,
            rustls::SignatureScheme::RSA_PSS_SHA512,
            rustls::SignatureScheme::ED25519,
            rustls::SignatureScheme::ED448,
            rustls::SignatureScheme::ML_DSA_44,
            rustls::SignatureScheme::ML_DSA_65,
            rustls::SignatureScheme::ML_DSA_87,
        ]
    }
}