1use std::{path::PathBuf, sync::Arc, time::Duration};
4
5use ndn_packet::Name;
6use ndn_security::SecurityManager;
7use tokio::task::JoinHandle;
8use tracing::{info, warn};
9
10use crate::device::RenewalPolicy;
11
12pub struct RenewalHandle {
14 #[allow(dead_code)]
15 task: JoinHandle<()>,
16}
17
18impl Drop for RenewalHandle {
19 fn drop(&mut self) {
20 self.task.abort();
21 }
22}
23
24pub fn start_renewal(
26 manager: Arc<SecurityManager>,
27 key_name: Name,
28 namespace: Name,
29 policy: &RenewalPolicy,
30 _storage: Option<PathBuf>,
31) -> RenewalHandle {
32 let check_interval = match policy {
33 RenewalPolicy::WhenPercentRemaining(_pct) => {
34 Duration::from_secs(600)
36 }
37 RenewalPolicy::Every(d) => *d,
38 RenewalPolicy::Manual => {
39 return RenewalHandle {
40 task: tokio::spawn(async {}),
41 };
42 }
43 };
44
45 let percent = match policy {
46 RenewalPolicy::WhenPercentRemaining(p) => *p as u64,
47 _ => 20,
48 };
49
50 let task = tokio::spawn(async move {
51 loop {
52 tokio::time::sleep(check_interval).await;
53
54 let should_renew = check_renewal_needed(&manager, &key_name, percent);
55 if should_renew {
56 info!(
57 identity = %namespace,
58 "Certificate approaching expiry, initiating renewal"
59 );
60 warn!(
63 identity = %namespace,
64 "Automatic renewal not yet implemented; please re-enroll manually"
65 );
66 }
67 }
68 });
69
70 RenewalHandle { task }
71}
72
73fn check_renewal_needed(manager: &SecurityManager, key_name: &Name, threshold_pct: u64) -> bool {
74 use std::time::{SystemTime, UNIX_EPOCH};
75 let now_ns = SystemTime::now()
76 .duration_since(UNIX_EPOCH)
77 .unwrap_or_default()
78 .as_nanos() as u64;
79
80 if let Some(cert) = manager
81 .cert_cache()
82 .get(&std::sync::Arc::new(key_name.clone()))
83 {
84 let total = cert.valid_until.saturating_sub(cert.valid_from);
85 let remaining = cert.valid_until.saturating_sub(now_ns);
86 if total == 0 {
87 return false;
88 }
89 let remaining_pct = (remaining * 100) / total;
90 return remaining_pct < threshold_pct;
91 }
92 false
93}