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),
}