trz_gateway_server/server/
certificate.rs1use 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}