trz_gateway_server/server/
certificate.rs

1use std::sync::Arc;
2use std::time::Duration;
3use std::time::SystemTime;
4
5use axum::Json;
6use axum::http::StatusCode;
7use nameth::NamedEnumValues as _;
8use nameth::NamedType as _;
9use nameth::nameth;
10use openssl::x509::X509Extension;
11use pem::PemError;
12use trz_gateway_common::api::tunnel::GetCertificateRequest;
13use trz_gateway_common::http_error::HttpError;
14use trz_gateway_common::http_error::IsHttpError;
15use trz_gateway_common::x509::PemAsStringError;
16use trz_gateway_common::x509::PemString as _;
17use trz_gateway_common::x509::cert::MakeCertError;
18use trz_gateway_common::x509::cert::make_cert;
19use trz_gateway_common::x509::name::CertitficateName;
20use trz_gateway_common::x509::signed_extension::MakeSignedExtensionError;
21use trz_gateway_common::x509::signed_extension::make_signed_extension;
22use trz_gateway_common::x509::time::Asn1ToSystemTimeError;
23use trz_gateway_common::x509::validity::Validity;
24use trz_gateway_common::x509::validity::ValidityError;
25
26use super::Server;
27use crate::auth_code::AuthCode;
28
29static CERTIFICATE_VALIDITY: Duration = Duration::from_secs(3600 * 24 * 90);
30
31impl Server {
32    pub async fn get_certificate(
33        self: Arc<Self>,
34        Json(request): Json<GetCertificateRequest<AuthCode>>,
35    ) -> Result<String, HttpError<GetCertificateError>> {
36        if !request.auth_code.is_valid() {
37            return Err(GetCertificateError::InvalidAuthCode)?;
38        }
39        Ok(self.make_pem_cert(request)?)
40    }
41
42    fn make_pem_cert(
43        &self,
44        request: GetCertificateRequest<AuthCode>,
45    ) -> Result<String, GetCertificateError> {
46        let mut validity = self.issuer_config.validity;
47        validity.to = SystemTime::min(validity.to, validity.from + CERTIFICATE_VALIDITY);
48        let signed_extension = self.make_signed_extension(&request, validity)?;
49        Ok(self.assemble_pem_cert(request, validity, signed_extension)?)
50    }
51
52    fn make_signed_extension(
53        &self,
54        request: &GetCertificateRequest<AuthCode>,
55        validity: Validity,
56    ) -> Result<X509Extension, GetCertificateError> {
57        Ok(make_signed_extension(
58            &request.name,
59            validity,
60            pem::parse(&request.public_key)
61                .map_err(GetCertificateError::InvalidPublicKeyPem)?
62                .contents(),
63            Some(&self.issuer_config.intermediates),
64            (*self.issuer_config.signer).as_ref(),
65        )?)
66    }
67
68    fn assemble_pem_cert(
69        &self,
70        request: GetCertificateRequest<AuthCode>,
71        validity: Validity,
72        signed_extension: X509Extension,
73    ) -> Result<String, MakePemCertificateError> {
74        let certificate = make_cert(
75            (*self.root_ca).as_ref(),
76            CertitficateName {
77                common_name: Some(&request.name),
78                ..CertitficateName::default()
79            },
80            validity,
81            &request.public_key,
82            vec![signed_extension],
83        )?;
84        Ok(certificate.to_pem().pem_string()?)
85    }
86}
87
88#[nameth]
89#[derive(thiserror::Error, Debug)]
90pub enum GetCertificateError {
91    #[error("[{n}] {t} is invalid", n = self.name(), t = AuthCode::type_name())]
92    InvalidAuthCode,
93
94    #[error("[{n}] {0}", n = self.name())]
95    Validity(#[from] ValidityError<Asn1ToSystemTimeError>),
96
97    #[error("[{n}] {0}", n = self.name())]
98    InvalidPublicKeyPem(PemError),
99
100    #[error("[{n}] {0}", n = self.name())]
101    MakeSignedExtension(#[from] MakeSignedExtensionError),
102
103    #[error("[{n}] {0}", n = self.name())]
104    MakeCert(#[from] MakePemCertificateError),
105}
106
107#[nameth]
108#[derive(thiserror::Error, Debug)]
109pub enum MakePemCertificateError {
110    #[error("[{n}] {0}", n = self.name())]
111    MakeCert(#[from] MakeCertError),
112
113    #[error("[{n}] Failed to convert certificate to PEM: {0}", n = self.name())]
114    PemString(#[from] PemAsStringError),
115}
116
117impl IsHttpError for GetCertificateError {
118    fn status_code(&self) -> StatusCode {
119        match self {
120            GetCertificateError::InvalidAuthCode => StatusCode::FORBIDDEN,
121            GetCertificateError::MakeCert(error) => error.status_code(),
122            GetCertificateError::Validity { .. } => StatusCode::INTERNAL_SERVER_ERROR,
123            GetCertificateError::InvalidPublicKeyPem { .. } => StatusCode::BAD_REQUEST,
124            GetCertificateError::MakeSignedExtension { .. } => StatusCode::INTERNAL_SERVER_ERROR,
125        }
126    }
127}
128
129impl IsHttpError for MakePemCertificateError {
130    fn status_code(&self) -> StatusCode {
131        match self {
132            MakePemCertificateError::MakeCert(error) => error.status_code(),
133            MakePemCertificateError::PemString { .. } => StatusCode::INTERNAL_SERVER_ERROR,
134        }
135    }
136}