trz_gateway_server/server/
root_ca_configuration.rs1use std::io::ErrorKind;
7use std::path::Path;
8
9use nameth::NamedEnumValues as _;
10use nameth::nameth;
11use trz_gateway_common::certificate_info::CertificateError;
12use trz_gateway_common::certificate_info::CertificateInfo;
13use trz_gateway_common::security_configuration::certificate::pem::PemCertificate;
14use trz_gateway_common::x509::PemAsStringError;
15use trz_gateway_common::x509::PemString as _;
16use trz_gateway_common::x509::ca::MakeCaError;
17use trz_gateway_common::x509::ca::make_ca;
18use trz_gateway_common::x509::name::CertitficateName;
19use trz_gateway_common::x509::validity::Validity;
20
21pub fn load_root_ca(
23 name: CertitficateName,
24 root_ca_path: CertificateInfo<impl AsRef<Path>>,
25 default_validity: Validity,
26) -> Result<PemCertificate, RootCaConfigError> {
27 let root_ca_path = root_ca_path.as_ref();
28 match root_ca_path.map(|path| path.exists()) {
29 CertificateInfo {
30 certificate: true,
31 private_key: true,
32 } => {
33 let root_ca = root_ca_path
34 .try_map(std::fs::read_to_string)
35 .map_err(|error| RootCaConfigError::Load(error, format!("{root_ca_path:?}")))?;
36 Ok(root_ca.into())
37 }
38 CertificateInfo {
39 certificate: false,
40 private_key: false,
41 } => {
42 let root_ca = make_ca(name, default_validity).map_err(Box::new)?;
43 let root_ca_pem = CertificateInfo {
44 certificate: root_ca.certificate.to_pem(),
45 private_key: root_ca.private_key.private_key_to_pem_pkcs8(),
46 }
47 .try_map(|maybe_pem| maybe_pem.pem_string())?;
48 let _: CertificateInfo<()> = root_ca_path
49 .zip(root_ca_pem.as_ref())
50 .try_map(write_pem_file)
51 .map_err(|error| RootCaConfigError::Store(error, format!("{root_ca_path:?}")))?;
52
53 #[cfg(unix)]
54 {
55 use std::fs::Permissions;
56 use std::os::unix::fs::PermissionsExt as _;
57 let permissions = Permissions::from_mode(0o600);
58 std::fs::set_permissions(root_ca_path.private_key, permissions)
59 .map_err(RootCaConfigError::SetPrivateKeyFilePermissions)?;
60 }
61
62 Ok(root_ca_pem.into())
63 }
64 CertificateInfo {
65 certificate: root_ca_exists,
66 private_key: private_key_exists,
67 } => {
68 return Err(RootCaConfigError::InconsistentState {
69 root_ca_exists,
70 private_key_exists,
71 });
72 }
73 }
74}
75
76#[nameth]
77#[derive(thiserror::Error, Debug)]
78pub enum RootCaConfigError {
79 #[error("[{n}] Failed to load certificate from '{1}': {0}", n = self.name())]
80 Load(CertificateError<std::io::Error>, String),
81
82 #[error("[{n}] Failed to store certificate into '{1}': {0}", n = self.name())]
83 Store(CertificateError<std::io::Error>, String),
84
85 #[error("[{n}] Inconsistent state: root_ca_exists:{root_ca_exists} private_key_exists:{private_key_exists}", n = self.name())]
86 InconsistentState {
87 root_ca_exists: bool,
88 private_key_exists: bool,
89 },
90
91 #[error("[{n}] {0}", n = self.name())]
92 MakeCa(#[from] Box<MakeCaError>),
93
94 #[error("[{n}] {0}", n = self.name())]
95 PemString(#[from] CertificateError<PemAsStringError>),
96
97 #[cfg(unix)]
98 #[error("[{n}] {0}", n = self.name())]
99 SetPrivateKeyFilePermissions(std::io::Error),
100}
101
102fn write_pem_file((path, pem): (&Path, &str)) -> Result<(), std::io::Error> {
103 let parent_dir = path.parent().ok_or_else(|| {
104 std::io::Error::new(
105 ErrorKind::InvalidInput,
106 format!("Failed to get parent folder of: {path:?}"),
107 )
108 })?;
109 std::fs::create_dir_all(parent_dir)?;
110 std::fs::write(path, pem)
111}