terrazzo-terminal 0.2.7

A simple web-based terminal emulator built on Terrazzo.
use std::time::Duration;
use std::time::SystemTime;

use nameth::NamedEnumValues as _;
use nameth::nameth;
use openssl::x509::X509;
use trz_gateway_common::security_configuration::SecurityConfig;
use trz_gateway_common::security_configuration::certificate::CertificateConfig as _;
use trz_gateway_common::security_configuration::certificate::cache::CachedCertificate;
use trz_gateway_common::security_configuration::certificate::pem::PemCertificate;
use trz_gateway_common::security_configuration::certificate::pem::PemCertificateError;
use trz_gateway_common::unwrap_infallible::UnwrapInfallible as _;
use trz_gateway_common::x509::PemAsStringError;
use trz_gateway_common::x509::PemString as _;
use trz_gateway_common::x509::ca::MakeCaError;
use trz_gateway_common::x509::ca::make_intermediate;
use trz_gateway_common::x509::cert::MakeCertError;
use trz_gateway_common::x509::cert::make_cert;
use trz_gateway_common::x509::key::MakeKeyError;
use trz_gateway_common::x509::key::make_key;
use trz_gateway_common::x509::name::CertitficateName;
use trz_gateway_common::x509::time::Asn1ToSystemTimeError;
use trz_gateway_common::x509::validity::Validity;
use trz_gateway_common::x509::validity::ValidityError;

use super::root_ca_config::PrivateRootCa;

pub fn make_tls_config(
    root_ca: &PrivateRootCa,
) -> Result<SecurityConfig<PrivateRootCa, CachedCertificate>, TlsConfigError> {
    let root_ca_x509 = root_ca.certificate().unwrap_infallible();
    let validity = root_ca_x509.certificate.as_ref().try_into()?;

    let certificate_key = make_key()?;
    let private_key_pem = certificate_key.private_key_to_pem_pkcs8().pem_string();

    let certificate: X509;
    let intermediates_pem: String;

    if cfg!(target_os = "macos") {
        const TLS_CERTIFICATE_VALIDITY: Duration = Duration::from_secs(3600 * 24 * 90);
        let root_validity: Validity = root_ca_x509.certificate.as_ref().try_into()?;
        let now = SystemTime::now();
        let validity = Validity {
            from: now,
            to: SystemTime::min(root_validity.to, now + TLS_CERTIFICATE_VALIDITY),
        };
        certificate = make_cert(
            (*root_ca_x509).as_ref(),
            CertitficateName {
                organization: Some("Terrazzo"),
                common_name: Some("localhost"),
                ..CertitficateName::default()
            },
            validity,
            &certificate_key
                .public_key_to_pem()
                .pem_string()
                .map_err(TlsConfigError::PublicKeyPem)?,
            vec![],
        )?;

        intermediates_pem = Default::default();
    } else {
        let intermediate = make_intermediate(
            (*root_ca_x509).as_ref(),
            CertitficateName {
                organization: Some("Terrazzo"),
                common_name: Some("Terrazzo Terminal Intermediate CA"),
                ..CertitficateName::default()
            },
            validity,
        )?;
        certificate = make_cert(
            intermediate.as_ref(),
            CertitficateName {
                organization: Some("Terrazzo"),
                common_name: Some("localhost"),
                ..CertitficateName::default()
            },
            validity,
            &certificate_key
                .public_key_to_pem()
                .pem_string()
                .map_err(TlsConfigError::PublicKeyPem)?,
            vec![],
        )?;
        intermediates_pem = {
            let pem = intermediate.certificate.to_pem();
            pem.pem_string().map_err(TlsConfigError::IntermediatesPem)?
        };
    }

    let certificate_pem = {
        let pem = certificate.to_pem();
        pem.pem_string().map_err(TlsConfigError::CertificatePem)?
    };
    Ok(SecurityConfig {
        trusted_store: root_ca.clone(),
        certificate: PemCertificate {
            intermediates_pem,
            certificate_pem,
            private_key_pem: private_key_pem.map_err(TlsConfigError::PrivateKeyPem)?,
        }
        .cache()?,
    })
}

#[nameth]
#[derive(thiserror::Error, Debug)]
pub enum TlsConfigError {
    #[error("[{n}] {0}", n = self.name())]
    ValidityError(#[from] ValidityError<Asn1ToSystemTimeError>),

    #[error("[{n}] {0}", n = self.name())]
    MakeIntermediate(#[from] MakeCaError),

    #[error("[{n}] {0}", n = self.name())]
    MakeKey(#[from] MakeKeyError),

    #[error("[{n}] {0}", n = self.name())]
    MakeCertificate(#[from] MakeCertError),

    #[error("[{n}] {0}", n = self.name())]
    IntermediatesPem(PemAsStringError),

    #[error("[{n}] {0}", n = self.name())]
    CertificatePem(PemAsStringError),

    #[error("[{n}] {0}", n = self.name())]
    PublicKeyPem(PemAsStringError),

    #[error("[{n}] {0}", n = self.name())]
    PrivateKeyPem(PemAsStringError),

    #[error("[{n}] {0}", n = self.name())]
    PemCertificateError(#[from] PemCertificateError),
}