1use std::{collections::HashMap, sync::Arc};
52
53use parking_lot::RwLock;
54use anyhow::Result;
55use chrono::{DateTime, Utc};
56use rustls::ServerConfig;
57use rustls_pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
58use keyring::Entry;
59
60use crate::dev_log;
61
62#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
64pub struct CertificateInfo {
65 pub subject:String,
67 pub issuer:String,
69 pub valid_from:String,
71 pub valid_until:String,
73 pub is_self_signed:bool,
75 pub sans:Vec<String>,
77}
78
79#[allow(dead_code)]
81#[derive(Clone)]
82struct ServerCertData {
83 cert_pem:Vec<u8>,
85 key_pem:Vec<u8>,
87 server_config:Arc<ServerConfig>,
89 info:CertificateInfo,
91 valid_until:DateTime<Utc>,
93}
94
95pub struct CertificateManager {
100 app_id:String,
102 ca_cert:Option<Vec<u8>>,
104 ca_key:Option<Vec<u8>>,
106 server_certs:Arc<RwLock<HashMap<String, ServerCertData>>>,
108}
109
110impl CertificateManager {
111 const KEYRING_SERVICE:&'static str = "CodeEditorLand-TLS";
113 const KEYRING_CA_CERT:&'static str = "ca_certificate";
115 const KEYRING_CA_KEY:&'static str = "ca_private_key";
117 const CA_VALIDITY_DAYS:i64 = 365 * 10;
119 const SERVER_VALIDITY_DAYS:i64 = 365;
121 pub const RENEWAL_THRESHOLD_DAYS:i64 = 30;
123
124 pub async fn new(app_id:&str) -> Result<Self> {
140 Ok(Self {
141 app_id:app_id.to_string(),
142 ca_cert:None,
143 ca_key:None,
144 server_certs:Arc::new(RwLock::new(HashMap::new())),
145 })
146 }
147
148 pub async fn initialize_ca(&mut self) -> Result<()> {
164 if let Some((cert, key)) = self.load_ca_from_keyring()? {
165 dev_log!("security", "loading CA certificate from keyring");
166 self.ca_cert = Some(cert.clone());
167 self.ca_key = Some(key.clone());
168 dev_log!("security", "CA certificate loaded successfully");
169 } else {
170 dev_log!("security", "CA certificate not found in keyring, generating new CA");
171 let (cert, key) = self.generate_ca_cert()?;
172
173 self.save_ca_to_keyring(&cert, &key)?;
175
176 self.ca_cert = Some(cert.clone());
177 self.ca_key = Some(key);
178
179 dev_log!("security", "new CA certificate generated and stored");
180 }
181
182 Ok(())
183 }
184
185 fn generate_ca_cert(&self) -> Result<(Vec<u8>, Vec<u8>)> {
196 dev_log!("security", "generating new CA certificate");
197
198 let key_pair = rcgen::KeyPair::generate()?;
202
203 let mut params = rcgen::CertificateParams::default();
205 params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
206 params.distinguished_name = rcgen::DistinguishedName::new();
207
208 let not_before = rcgen::date_time_ymd(2024, 1, 1);
210 params.not_before = not_before;
211 let expiry_year:i32 = (2024 + Self::CA_VALIDITY_DAYS / 365) as i32;
212 let not_after = rcgen::date_time_ymd(expiry_year, 1, 1);
213 params.not_after = not_after;
214 params.key_usages = vec![
215 rcgen::KeyUsagePurpose::DigitalSignature,
216 rcgen::KeyUsagePurpose::KeyCertSign,
217 rcgen::KeyUsagePurpose::CrlSign,
218 ];
219
220 let cert = params.self_signed(&key_pair)?;
222
223 let cert_pem = cert.pem();
225 let key_pem = key_pair.serialize_pem();
226
227 dev_log!("security", "CA certificate generated successfully");
228
229 Ok((cert_pem.into_bytes(), key_pem.into_bytes()))
230 }
231
232 pub async fn get_server_cert(&self, hostname:&str) -> Result<Arc<ServerConfig>> {
254 {
256 let certs = self.server_certs.read();
257 if let Some(cert_data) = certs.get(hostname) {
258 if !self.should_renew(&cert_data.cert_pem) {
260 dev_log!("security", "using cached server certificate for {}", hostname);
261 return Ok(cert_data.server_config.clone());
262 }
263 drop(certs);
265 }
266 }
267
268 dev_log!("security", "generating server certificate for {}", hostname);
270 let cert_data = self.generate_server_cert(hostname)?;
271
272 {
274 let mut certs = self.server_certs.write();
275 certs.insert(hostname.to_string(), cert_data.clone());
276 }
277
278 Ok(cert_data.server_config)
279 }
280
281 fn generate_server_cert(&self, hostname:&str) -> Result<ServerCertData> {
289 let mut params = rcgen::CertificateParams::default();
291 params.distinguished_name.push(rcgen::DnType::CommonName, hostname);
292
293 let now = chrono::Utc::now();
295 let current_year = 2024; let current_month = 1;
297 let current_day = 1;
298
299 let not_before = rcgen::date_time_ymd(current_year, current_month, current_day);
300 params.not_before = not_before;
301
302 let not_after = rcgen::date_time_ymd(current_year + 1, current_month, current_day);
303 params.not_after = not_after;
304
305 params.key_usages = vec![
310 rcgen::KeyUsagePurpose::DigitalSignature,
311 rcgen::KeyUsagePurpose::KeyEncipherment,
312 ];
313 params.extended_key_usages = vec![
314 rcgen::ExtendedKeyUsagePurpose::ServerAuth,
315 rcgen::ExtendedKeyUsagePurpose::ClientAuth,
316 ];
317
318 let key_pair = rcgen::KeyPair::generate()?;
320 let cert = params.self_signed(&key_pair)?;
322
323 let server_cert_der = cert.der();
326 let server_key_der = key_pair.serialized_der();
327
328 let cert_der:Vec<u8> = server_cert_der.to_vec();
330 let key_der:Vec<u8> = server_key_der.to_vec();
331
332 let cert_der_for_info = cert_der.clone();
334
335 let cert_chain:Vec<CertificateDer<'static>> = vec![CertificateDer::from(cert_der)];
337
338 let private_key_der =
340 PrivatePkcs8KeyDer::try_from(key_der).map_err(|e| anyhow::anyhow!("Failed to parse private key: {}", e))?;
341 let private_key = PrivateKeyDer::Pkcs8(private_key_der);
342
343 let cert_pem:Vec<u8> = Vec::new();
345 let key_pem:Vec<u8> = Vec::new();
346
347 let mut server_config = ServerConfig::builder()
348 .with_no_client_auth()
349 .with_single_cert(cert_chain, private_key)
350 .map_err(|e| anyhow::anyhow!("Failed to create ServerConfig: {}", e))?;
351
352 server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
354
355 let info = self.extract_cert_info(&cert_der_for_info, hostname, true)?;
357 let valid_until = Utc::now() + chrono::Duration::days(Self::SERVER_VALIDITY_DAYS);
358
359 dev_log!(
360 "security",
361 "server certificate generated for {} (valid until {})",
362 hostname,
363 valid_until
364 );
365
366 Ok(ServerCertData { cert_pem, key_pem, server_config:Arc::new(server_config), info, valid_until })
367 }
368
369 fn load_ca_from_keyring(&self) -> Result<Option<(Vec<u8>, Vec<u8>)>> {
373 let keyring_entry_cert =
374 Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_CERT))
375 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
376
377 let keyring_entry_key = Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_KEY))
378 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
379
380 let cert = match keyring_entry_cert.get_password() {
381 Ok(s) => s.into_bytes(),
382 Err(keyring::Error::NoEntry) => return Ok(None),
383 Err(e) => return Err(e.into()),
384 };
385
386 let key = keyring_entry_key
387 .get_password()
388 .map_err(|e| anyhow::anyhow!("Failed to load CA key from keyring: {}", e))?
389 .into_bytes();
390
391 dev_log!("security", "CA certificate loaded from keyring");
392 Ok(Some((cert, key)))
393 }
394
395 fn save_ca_to_keyring(&self, cert:&[u8], key:&[u8]) -> Result<()> {
397 let keyring_entry_cert =
398 Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_CERT))
399 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
400
401 let keyring_entry_key = Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_KEY))
402 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
403
404 let cert_str = String::from_utf8(cert.to_vec()).map_err(|e| anyhow::anyhow!("Invalid CA cert UTF-8: {}", e))?;
406 let key_str = String::from_utf8(key.to_vec()).map_err(|e| anyhow::anyhow!("Invalid CA key UTF-8: {}", e))?;
407
408 keyring_entry_cert
409 .set_password(&cert_str)
410 .map_err(|e| anyhow::anyhow!("Failed to save CA cert to keyring: {}", e))?;
411
412 keyring_entry_key
413 .set_password(&key_str)
414 .map_err(|e| anyhow::anyhow!("Failed to save CA key to keyring: {}", e))?;
415
416 dev_log!("security", "CA certificate saved to keyring");
417 Ok(())
418 }
419
420 pub fn should_renew(&self, cert_pem:&[u8]) -> bool {
425 if let Ok(result) = self.check_cert_validity(cert_pem) {
426 result.should_renew
427 } else {
428 dev_log!("security", "warn: could not parse certificate validity, forcing renewal");
430 true
431 }
432 }
433
434 pub async fn renew_certificate(&mut self, hostname:&str) -> Result<()> {
452 dev_log!("security", "forcing renewal of certificate for {}", hostname);
453
454 let mut certs = self.server_certs.write();
456 certs.remove(hostname);
457 drop(certs);
458
459 let cert_data = self.generate_server_cert(hostname)?;
461
462 let mut certs = self.server_certs.write();
464 certs.insert(hostname.to_string(), cert_data);
465
466 dev_log!("security", "certificate renewed for {}", hostname);
467 Ok(())
468 }
469
470 pub async fn build_server_config(&self, hostname:&str) -> Result<Arc<ServerConfig>> {
490 self.get_server_cert(hostname).await
491 }
492
493 pub fn get_ca_cert_pem(&self) -> Option<Vec<u8>> { self.ca_cert.clone() }
515
516 pub fn get_server_cert_info(&self, hostname:&str) -> Option<CertificateInfo> {
540 let certs = self.server_certs.read();
541 certs.get(hostname).map(|d| d.info.clone())
542 }
543
544 pub fn get_all_certs(&self) -> HashMap<String, CertificateInfo> {
567 let certs = self.server_certs.read();
568 certs.iter().map(|(k, v)| (k.clone(), v.info.clone())).collect()
569 }
570
571 #[allow(dead_code)]
573 fn cert_der_to_pem(der:&[u8]) -> Result<Vec<u8>> {
574 let pem = pem::Pem::new("CERTIFICATE".to_string(), der.to_vec());
575 let pem_str = pem::encode(&pem);
576 Ok(pem_str.into_bytes())
577 }
578
579 #[allow(dead_code)]
581 fn private_key_der_to_pem(der:&[u8]) -> Result<Vec<u8>> {
582 let pem = pem::Pem::new("PRIVATE KEY".to_string(), der.to_vec());
583 let pem_str = pem::encode(&pem);
584 Ok(pem_str.into_bytes())
585 }
586
587 fn pem_to_der(pem:&[u8], label:&str) -> Result<Vec<u8>> {
589 let pem_str = String::from_utf8(pem.to_vec()).map_err(|e| anyhow::anyhow!("Invalid PEM UTF-8: {}", e))?;
590
591 let pem = pem::parse(&pem_str).map_err(|e| anyhow::anyhow!("Failed to parse PEM: {}", e))?;
592
593 if pem.tag() != label {
594 return Err(anyhow::anyhow!("Expected PEM label '{}', found '{}'", label, pem.tag()));
595 }
596
597 Ok(pem.contents().to_vec())
598 }
599
600 fn extract_cert_info(&self, cert_der:&[u8], hostname:&str, is_ca:bool) -> Result<CertificateInfo> {
602 let cert = x509_parser::parse_x509_certificate(cert_der)
604 .map_err(|e| anyhow::anyhow!("Failed to parse certificate: {}", e))?
605 .1;
606
607 let subject = cert.subject().to_string();
608 let issuer = cert.issuer().to_string();
609
610 let valid_from = cert.validity().not_before.to_string();
611 let valid_until = cert.validity().not_after.to_string();
612
613 let mut sans = vec![hostname.to_string(), "127.0.0.1".to_string(), "::1".to_string()];
615 if let Some(ext) = cert
616 .extensions()
617 .iter()
618 .find(|e| e.oid == x509_parser::oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME)
619 {
620 if let x509_parser::extensions::ParsedExtension::SubjectAlternativeName(sans_list) = ext.parsed_extension()
621 {
622 sans = sans_list
623 .general_names
624 .iter()
625 .filter_map(|gn| {
626 match gn {
627 x509_parser::extensions::GeneralName::DNSName(dns) => Some(dns.to_string()),
628 x509_parser::extensions::GeneralName::IPAddress(ip) => {
629 let octets:&[u8] = ip.as_ref();
630 Some(match octets.len() {
631 4 => format!("{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]),
632 16 => {
633 format!(
634 "::{}:{}:{}:{}:{}",
635 octets[0], octets[1], octets[2], octets[3], octets[4]
636 )
637 },
638 _ => "?".to_string(),
639 })
640 },
641 _ => None,
642 }
643 })
644 .collect();
645 }
646 }
647
648 Ok(CertificateInfo { subject, issuer, valid_from, valid_until, is_self_signed:is_ca, sans })
649 }
650
651 fn check_cert_validity(&self, cert_pem:&[u8]) -> Result<CertValidityResult> {
653 let cert_der = Self::pem_to_der(cert_pem, "CERTIFICATE")?;
654
655 let cert = x509_parser::parse_x509_certificate(&cert_der)
656 .map_err(|e| anyhow::anyhow!("Failed to parse certificate: {}", e))?
657 .1;
658
659 let not_after_chrono = Self::parse_not_after(&cert.validity().not_after)?;
660 let now = chrono::Utc::now();
661
662 let is_valid = now <= not_after_chrono;
663 let days_until_expiry = (not_after_chrono - now).num_days();
664 let should_renew = days_until_expiry <= Self::RENEWAL_THRESHOLD_DAYS;
665
666 Ok(CertValidityResult { is_valid, days_until_expiry, should_renew, not_after:not_after_chrono })
667 }
668
669 fn parse_not_after(not_after:&x509_parser::time::ASN1Time) -> Result<DateTime<Utc>> {
671 let timestamp = Self::not_as_unix_timestamp(not_after)
673 .ok_or_else(|| anyhow::anyhow!("Failed to convert not_after to timestamp"))?;
674
675 DateTime::from_timestamp(timestamp, 0)
676 .ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))
677 .map(|dt| dt.to_utc())
678 }
679
680 fn not_as_unix_timestamp(not_after:&x509_parser::time::ASN1Time) -> Option<i64> {
682 let time_str = not_after.to_string();
685
686 let dt = chrono::NaiveDateTime::parse_from_str(&time_str, "%Y%m%d%H%M%SZ")
689 .or_else(|_| chrono::NaiveDateTime::parse_from_str(&time_str, "%Y%m%d%H%M%S"))
690 .or_else(|_| chrono::NaiveDateTime::parse_from_str(&format!("{}000000", time_str), "%Y%m%d%H%M%S"))
691 .ok()?;
692
693 Some(dt.and_utc().timestamp())
694 }
695}
696
697#[allow(dead_code)]
699#[derive(Debug, Clone)]
700struct CertValidityResult {
701 is_valid:bool,
703 days_until_expiry:i64,
705 should_renew:bool,
707 not_after:DateTime<Utc>,
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[test]
716 fn test_pem_encoding() {
717 let test_data = b"test certificate data";
718 let pem = CertificateManager::cert_der_to_pem(test_data).unwrap();
719 assert!(String::from_utf8_lossy(&pem).contains("-----BEGIN CERTIFICATE-----"));
720 assert!(String::from_utf8_lossy(&pem).contains("-----END CERTIFICATE-----"));
721
722 let recovered = CertificateManager::pem_to_der(&pem, "CERTIFICATE").unwrap();
723 assert_eq!(recovered, test_data);
724 }
725
726 #[test]
727 fn test_private_key_pem_encoding() {
728 let test_data = b"test private key data";
729 let pem = CertificateManager::private_key_der_to_pem(test_data).unwrap();
730 assert!(String::from_utf8_lossy(&pem).contains("-----BEGIN PRIVATE KEY-----"));
731 assert!(String::from_utf8_lossy(&pem).contains("-----END PRIVATE KEY-----"));
732
733 let recovered = CertificateManager::pem_to_der(&pem, "PRIVATE KEY").unwrap();
734 assert_eq!(recovered, test_data);
735 }
736}