Skip to main content

pwr_server/
cert.rs

1//! TLS certificate generation for pwr-server.
2//!
3//! Generates self-signed ECDSA P-256 certificates for TLS 1.3.
4//! The certificate fingerprint is printed so the user can pin it
5//! in the client config for MITM protection.
6
7use rcgen::{CertificateParams, DistinguishedName, DnType, KeyPair};
8use std::fs;
9use std::path::Path;
10
11/// Generate a self-signed TLS certificate and private key.
12///
13/// Returns (cert_pem, key_pem, fingerprint_sha256).
14/// The certificate is valid for 365 days from issuance.
15pub fn generate_certificate(
16    common_name: &str,
17) -> Result<(String, String, String), String> {
18    let mut params = CertificateParams::new(vec![common_name.to_string()])
19        .map_err(|e| format!("cert params: {}", e))?;
20
21    // Set distinguished name
22    let mut dn = DistinguishedName::new();
23    dn.push(DnType::CommonName, common_name);
24    params.distinguished_name = dn;
25
26    // Generate ECDSA P-256 key pair
27    let key_pair = KeyPair::generate()
28        .map_err(|e| format!("key gen: {}", e))?;
29
30    let cert = params
31        .self_signed(&key_pair)
32        .map_err(|e| format!("self-sign: {}", e))?;
33
34    let cert_pem = cert.pem();
35    let key_pem = key_pair.serialize_pem();
36
37    // Compute SHA-256 fingerprint for certificate pinning
38    let fingerprint = pwr_core::crypto::sha256_hex(cert_pem.as_bytes());
39
40    Ok((cert_pem, key_pem, fingerprint))
41}
42
43/// Write certificate and key to files with appropriate permissions.
44pub fn save_certificate(
45    cert_path: &Path,
46    key_path: &Path,
47    cert_pem: &str,
48    key_pem: &str,
49) -> Result<(), String> {
50    // Create parent directories
51    if let Some(parent) = cert_path.parent() {
52        fs::create_dir_all(parent)
53            .map_err(|e| format!("mkdir {}: {}", parent.display(), e))?;
54    }
55    if let Some(parent) = key_path.parent() {
56        fs::create_dir_all(parent)
57            .map_err(|e| format!("mkdir {}: {}", parent.display(), e))?;
58    }
59
60    // Write certificate (world-readable)
61    fs::write(cert_path, cert_pem)
62        .map_err(|e| format!("write cert: {}", e))?;
63
64    // Write private key (owner-only)
65    fs::write(key_path, key_pem)
66        .map_err(|e| format!("write key: {}", e))?;
67
68    #[cfg(unix)]
69    {
70        use std::os::unix::fs::PermissionsExt;
71        let mut perms = fs::metadata(key_path)
72            .map_err(|e| format!("stat key: {}", e))?
73            .permissions();
74        perms.set_mode(0o600);
75        fs::set_permissions(key_path, perms)
76            .map_err(|e| format!("chmod key: {}", e))?;
77    }
78
79    Ok(())
80}
81
82/// Generate a fresh server configuration with TLS certificates and PSK.
83pub fn init_server(
84    config_path: &Path,
85    hostname: &str,
86) -> Result<(), String> {
87    use crate::config::{save_config, ServerConfig};
88
89    let (cert_pem, key_pem, fingerprint) = generate_certificate(hostname)?;
90
91    let cert_path = Path::new("/etc/pwr/server.crt");
92    let key_path = Path::new("/etc/pwr/server.key");
93
94    save_certificate(cert_path, key_path, &cert_pem, &key_pem)?;
95
96    // Generate PSK
97    let psk = pwr_core::crypto::generate_psk();
98    let psk_hex = pwr_core::crypto::psk_to_hex(&psk);
99
100    let mut config = ServerConfig::default();
101    config.auth_token = psk_hex.clone();
102    config.tls_cert_path = cert_path.to_path_buf();
103    config.tls_key_path = key_path.to_path_buf();
104
105    save_config(&config, config_path)?;
106
107    println!("Server initialized successfully.");
108    println!("  Config:     {}", config_path.display());
109    println!("  Certificate: {}", cert_path.display());
110    println!("  Private key: {}", key_path.display());
111    println!("  PSK:        {}", psk_hex);
112    println!("  Fingerprint: {}", fingerprint);
113    println!();
114    println!("Copy the PSK to your client config:");
115    println!("  pwr init --server-host {} --psk {}", hostname, psk_hex);
116
117    Ok(())
118}