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#[derive(Clone)]
81struct ServerCertData {
82 cert_pem:Vec<u8>,
84 key_pem:Vec<u8>,
86 server_config:Arc<ServerConfig>,
88 info:CertificateInfo,
90 valid_until:DateTime<Utc>,
92}
93
94pub struct CertificateManager {
99 app_id:String,
101 ca_cert:Option<Vec<u8>>,
103 ca_key:Option<Vec<u8>>,
105 server_certs:Arc<RwLock<HashMap<String, ServerCertData>>>,
107}
108
109impl CertificateManager {
110 const KEYRING_SERVICE:&'static str = "CodeEditorLand-TLS";
112 const KEYRING_CA_CERT:&'static str = "ca_certificate";
114 const KEYRING_CA_KEY:&'static str = "ca_private_key";
116 const CA_VALIDITY_DAYS:i64 = 365 * 10;
118 const SERVER_VALIDITY_DAYS:i64 = 365;
120 pub const RENEWAL_THRESHOLD_DAYS:i64 = 30;
122
123 pub async fn new(app_id:&str) -> Result<Self> {
139 Ok(Self {
140 app_id:app_id.to_string(),
141 ca_cert:None,
142 ca_key:None,
143 server_certs:Arc::new(RwLock::new(HashMap::new())),
144 })
145 }
146
147 pub async fn initialize_ca(&mut self) -> Result<()> {
163 if let Some((cert, key)) = self.load_ca_from_keyring()? {
164 dev_log!("security", "loading CA certificate from keyring");
165 self.ca_cert = Some(cert.clone());
166 self.ca_key = Some(key.clone());
167 dev_log!("security", "CA certificate loaded successfully");
168 } else {
169 dev_log!("security", "CA certificate not found in keyring, generating new CA");
170 let (cert, key) = self.generate_ca_cert()?;
171
172 self.save_ca_to_keyring(&cert, &key)?;
174
175 self.ca_cert = Some(cert.clone());
176 self.ca_key = Some(key);
177
178 dev_log!("security", "new CA certificate generated and stored");
179 }
180
181 Ok(())
182 }
183
184 fn generate_ca_cert(&self) -> Result<(Vec<u8>, Vec<u8>)> {
195 dev_log!("security", "generating new CA certificate");
196
197 let key_pair = rcgen::KeyPair::generate()?;
201
202 let mut params = rcgen::CertificateParams::default();
204 params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
205 params.distinguished_name = rcgen::DistinguishedName::new();
206
207 let not_before = rcgen::date_time_ymd(2024, 1, 1);
209 params.not_before = not_before;
210 let expiry_year:i32 = (2024 + Self::CA_VALIDITY_DAYS / 365) as i32;
211 let not_after = rcgen::date_time_ymd(expiry_year, 1, 1);
212 params.not_after = not_after;
213 params.key_usages = vec![
214 rcgen::KeyUsagePurpose::DigitalSignature,
215 rcgen::KeyUsagePurpose::KeyCertSign,
216 rcgen::KeyUsagePurpose::CrlSign,
217 ];
218
219 let cert = params.self_signed(&key_pair)?;
221
222 let cert_pem = cert.pem();
224 let key_pem = key_pair.serialize_pem();
225
226 dev_log!("security", "CA certificate generated successfully");
227
228 Ok((cert_pem.into_bytes(), key_pem.into_bytes()))
229 }
230
231 pub async fn get_server_cert(&self, hostname:&str) -> Result<Arc<ServerConfig>> {
253 {
255 let certs = self.server_certs.read();
256 if let Some(cert_data) = certs.get(hostname) {
257 if !self.should_renew(&cert_data.cert_pem) {
259 dev_log!("security", "using cached server certificate for {}", hostname);
260 return Ok(cert_data.server_config.clone());
261 }
262 drop(certs);
264 }
265 }
266
267 dev_log!("security", "generating server certificate for {}", hostname);
269 let cert_data = self.generate_server_cert(hostname)?;
270
271 {
273 let mut certs = self.server_certs.write();
274 certs.insert(hostname.to_string(), cert_data.clone());
275 }
276
277 Ok(cert_data.server_config)
278 }
279
280 fn generate_server_cert(&self, hostname:&str) -> Result<ServerCertData> {
288 let mut params = rcgen::CertificateParams::default();
290 params.distinguished_name.push(rcgen::DnType::CommonName, hostname);
291
292 let now = chrono::Utc::now();
294 let current_year = 2024; let current_month = 1;
296 let current_day = 1;
297
298 let not_before = rcgen::date_time_ymd(current_year, current_month, current_day);
299 params.not_before = not_before;
300
301 let not_after = rcgen::date_time_ymd(current_year + 1, current_month, current_day);
302 params.not_after = not_after;
303
304 params.key_usages = vec![
309 rcgen::KeyUsagePurpose::DigitalSignature,
310 rcgen::KeyUsagePurpose::KeyEncipherment,
311 ];
312 params.extended_key_usages = vec![
313 rcgen::ExtendedKeyUsagePurpose::ServerAuth,
314 rcgen::ExtendedKeyUsagePurpose::ClientAuth,
315 ];
316
317 let key_pair = rcgen::KeyPair::generate()?;
319 let cert = params.self_signed(&key_pair)?;
321
322 let server_cert_der = cert.der();
325 let server_key_der = key_pair.serialized_der();
326
327 let cert_der:Vec<u8> = server_cert_der.to_vec();
329 let key_der:Vec<u8> = server_key_der.to_vec();
330
331 let cert_der_for_info = cert_der.clone();
333
334 let cert_chain:Vec<CertificateDer<'static>> = vec![CertificateDer::from(cert_der)];
336
337 let private_key_der =
339 PrivatePkcs8KeyDer::try_from(key_der).map_err(|e| anyhow::anyhow!("Failed to parse private key: {}", e))?;
340 let private_key = PrivateKeyDer::Pkcs8(private_key_der);
341
342 let cert_pem:Vec<u8> = Vec::new();
344 let key_pem:Vec<u8> = Vec::new();
345
346 let mut server_config = ServerConfig::builder()
347 .with_no_client_auth()
348 .with_single_cert(cert_chain, private_key)
349 .map_err(|e| anyhow::anyhow!("Failed to create ServerConfig: {}", e))?;
350
351 server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
353
354 let info = self.extract_cert_info(&cert_der_for_info, hostname, true)?;
356 let valid_until = Utc::now() + chrono::Duration::days(Self::SERVER_VALIDITY_DAYS);
357
358 dev_log!(
359 "security",
360 "server certificate generated for {} (valid until {})",
361 hostname,
362 valid_until
363 );
364
365 Ok(ServerCertData { cert_pem, key_pem, server_config:Arc::new(server_config), info, valid_until })
366 }
367
368 fn load_ca_from_keyring(&self) -> Result<Option<(Vec<u8>, Vec<u8>)>> {
372 let keyring_entry_cert =
373 Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_CERT))
374 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
375
376 let keyring_entry_key = Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_KEY))
377 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
378
379 let cert = match keyring_entry_cert.get_password() {
380 Ok(s) => s.into_bytes(),
381 Err(keyring::Error::NoEntry) => return Ok(None),
382 Err(e) => return Err(e.into()),
383 };
384
385 let key = keyring_entry_key
386 .get_password()
387 .map_err(|e| anyhow::anyhow!("Failed to load CA key from keyring: {}", e))?
388 .into_bytes();
389
390 dev_log!("security", "CA certificate loaded from keyring");
391 Ok(Some((cert, key)))
392 }
393
394 fn save_ca_to_keyring(&self, cert:&[u8], key:&[u8]) -> Result<()> {
396 let keyring_entry_cert =
397 Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_CERT))
398 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
399
400 let keyring_entry_key = Entry::new(Self::KEYRING_SERVICE, &format!("{}:{}", self.app_id, Self::KEYRING_CA_KEY))
401 .map_err(|e| anyhow::anyhow!("Failed to create keyring entry: {}", e))?;
402
403 let cert_str = String::from_utf8(cert.to_vec()).map_err(|e| anyhow::anyhow!("Invalid CA cert UTF-8: {}", e))?;
405 let key_str = String::from_utf8(key.to_vec()).map_err(|e| anyhow::anyhow!("Invalid CA key UTF-8: {}", e))?;
406
407 keyring_entry_cert
408 .set_password(&cert_str)
409 .map_err(|e| anyhow::anyhow!("Failed to save CA cert to keyring: {}", e))?;
410
411 keyring_entry_key
412 .set_password(&key_str)
413 .map_err(|e| anyhow::anyhow!("Failed to save CA key to keyring: {}", e))?;
414
415 dev_log!("security", "CA certificate saved to keyring");
416 Ok(())
417 }
418
419 pub fn should_renew(&self, cert_pem:&[u8]) -> bool {
424 if let Ok(result) = self.check_cert_validity(cert_pem) {
425 result.should_renew
426 } else {
427 dev_log!("security", "warn: could not parse certificate validity, forcing renewal");
429 true
430 }
431 }
432
433 pub async fn renew_certificate(&mut self, hostname:&str) -> Result<()> {
451 dev_log!("security", "forcing renewal of certificate for {}", hostname);
452
453 let mut certs = self.server_certs.write();
455 certs.remove(hostname);
456 drop(certs);
457
458 let cert_data = self.generate_server_cert(hostname)?;
460
461 let mut certs = self.server_certs.write();
463 certs.insert(hostname.to_string(), cert_data);
464
465 dev_log!("security", "certificate renewed for {}", hostname);
466 Ok(())
467 }
468
469 pub async fn build_server_config(&self, hostname:&str) -> Result<Arc<ServerConfig>> {
489 self.get_server_cert(hostname).await
490 }
491
492 pub fn get_ca_cert_pem(&self) -> Option<Vec<u8>> { self.ca_cert.clone() }
514
515 pub fn get_server_cert_info(&self, hostname:&str) -> Option<CertificateInfo> {
539 let certs = self.server_certs.read();
540 certs.get(hostname).map(|d| d.info.clone())
541 }
542
543 pub fn get_all_certs(&self) -> HashMap<String, CertificateInfo> {
566 let certs = self.server_certs.read();
567 certs.iter().map(|(k, v)| (k.clone(), v.info.clone())).collect()
568 }
569
570 fn cert_der_to_pem(der:&[u8]) -> Result<Vec<u8>> {
572 let pem = pem::Pem::new("CERTIFICATE".to_string(), der.to_vec());
573 let pem_str = pem::encode(&pem);
574 Ok(pem_str.into_bytes())
575 }
576
577 fn private_key_der_to_pem(der:&[u8]) -> Result<Vec<u8>> {
579 let pem = pem::Pem::new("PRIVATE KEY".to_string(), der.to_vec());
580 let pem_str = pem::encode(&pem);
581 Ok(pem_str.into_bytes())
582 }
583
584 fn pem_to_der(pem:&[u8], label:&str) -> Result<Vec<u8>> {
586 let pem_str = String::from_utf8(pem.to_vec()).map_err(|e| anyhow::anyhow!("Invalid PEM UTF-8: {}", e))?;
587
588 let pem = pem::parse(&pem_str).map_err(|e| anyhow::anyhow!("Failed to parse PEM: {}", e))?;
589
590 if pem.tag() != label {
591 return Err(anyhow::anyhow!("Expected PEM label '{}', found '{}'", label, pem.tag()));
592 }
593
594 Ok(pem.contents().to_vec())
595 }
596
597 fn extract_cert_info(&self, cert_der:&[u8], hostname:&str, is_ca:bool) -> Result<CertificateInfo> {
599 let cert = x509_parser::parse_x509_certificate(cert_der)
601 .map_err(|e| anyhow::anyhow!("Failed to parse certificate: {}", e))?
602 .1;
603
604 let subject = cert.subject().to_string();
605 let issuer = cert.issuer().to_string();
606
607 let valid_from = cert.validity().not_before.to_string();
608 let valid_until = cert.validity().not_after.to_string();
609
610 let mut sans = vec![hostname.to_string(), "127.0.0.1".to_string(), "::1".to_string()];
612 if let Some(ext) = cert
613 .extensions()
614 .iter()
615 .find(|e| e.oid == x509_parser::oid_registry::OID_X509_EXT_SUBJECT_ALT_NAME)
616 {
617 if let x509_parser::extensions::ParsedExtension::SubjectAlternativeName(sans_list) = ext.parsed_extension()
618 {
619 sans = sans_list
620 .general_names
621 .iter()
622 .filter_map(|gn| {
623 match gn {
624 x509_parser::extensions::GeneralName::DNSName(dns) => Some(dns.to_string()),
625 x509_parser::extensions::GeneralName::IPAddress(ip) => {
626 let octets:&[u8] = ip.as_ref();
627 Some(match octets.len() {
628 4 => format!("{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3]),
629 16 => {
630 format!(
631 "::{}:{}:{}:{}:{}",
632 octets[0], octets[1], octets[2], octets[3], octets[4]
633 )
634 },
635 _ => "?".to_string(),
636 })
637 },
638 _ => None,
639 }
640 })
641 .collect();
642 }
643 }
644
645 Ok(CertificateInfo { subject, issuer, valid_from, valid_until, is_self_signed:is_ca, sans })
646 }
647
648 fn check_cert_validity(&self, cert_pem:&[u8]) -> Result<CertValidityResult> {
650 let cert_der = Self::pem_to_der(cert_pem, "CERTIFICATE")?;
651
652 let cert = x509_parser::parse_x509_certificate(&cert_der)
653 .map_err(|e| anyhow::anyhow!("Failed to parse certificate: {}", e))?
654 .1;
655
656 let not_after_chrono = Self::parse_not_after(&cert.validity().not_after)?;
657 let now = chrono::Utc::now();
658
659 let is_valid = now <= not_after_chrono;
660 let days_until_expiry = (not_after_chrono - now).num_days();
661 let should_renew = days_until_expiry <= Self::RENEWAL_THRESHOLD_DAYS;
662
663 Ok(CertValidityResult { is_valid, days_until_expiry, should_renew, not_after:not_after_chrono })
664 }
665
666 fn parse_not_after(not_after:&x509_parser::time::ASN1Time) -> Result<DateTime<Utc>> {
668 let timestamp = Self::not_as_unix_timestamp(not_after)
670 .ok_or_else(|| anyhow::anyhow!("Failed to convert not_after to timestamp"))?;
671
672 DateTime::from_timestamp(timestamp, 0)
673 .ok_or_else(|| anyhow::anyhow!("Invalid timestamp"))
674 .map(|dt| dt.to_utc())
675 }
676
677 fn not_as_unix_timestamp(not_after:&x509_parser::time::ASN1Time) -> Option<i64> {
679 let time_str = not_after.to_string();
682
683 let dt = chrono::NaiveDateTime::parse_from_str(&time_str, "%Y%m%d%H%M%SZ")
686 .or_else(|_| chrono::NaiveDateTime::parse_from_str(&time_str, "%Y%m%d%H%M%S"))
687 .or_else(|_| chrono::NaiveDateTime::parse_from_str(&format!("{}000000", time_str), "%Y%m%d%H%M%S"))
688 .ok()?;
689
690 Some(dt.and_utc().timestamp())
691 }
692}
693
694#[derive(Debug, Clone)]
696struct CertValidityResult {
697 is_valid:bool,
699 days_until_expiry:i64,
701 should_renew:bool,
703 not_after:DateTime<Utc>,
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710
711 #[test]
712 fn test_pem_encoding() {
713 let test_data = b"test certificate data";
714 let pem = CertificateManager::cert_der_to_pem(test_data).unwrap();
715 assert!(String::from_utf8_lossy(&pem).contains("-----BEGIN CERTIFICATE-----"));
716 assert!(String::from_utf8_lossy(&pem).contains("-----END CERTIFICATE-----"));
717
718 let recovered = CertificateManager::pem_to_der(&pem, "CERTIFICATE").unwrap();
719 assert_eq!(recovered, test_data);
720 }
721
722 #[test]
723 fn test_private_key_pem_encoding() {
724 let test_data = b"test private key data";
725 let pem = CertificateManager::private_key_der_to_pem(test_data).unwrap();
726 assert!(String::from_utf8_lossy(&pem).contains("-----BEGIN PRIVATE KEY-----"));
727 assert!(String::from_utf8_lossy(&pem).contains("-----END PRIVATE KEY-----"));
728
729 let recovered = CertificateManager::pem_to_der(&pem, "PRIVATE KEY").unwrap();
730 assert_eq!(recovered, test_data);
731 }
732}