ndn_identity/
renewal.rs

1//! Background certificate renewal task.
2
3use 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
12/// Handle to the background renewal task.
13pub 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
24/// Spawn a background renewal task.
25pub 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            // Check every 10 minutes; actual renewal decision is cert-lifetime based.
35            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                // In a full implementation, this would run a new NDNCERT exchange.
61                // For v1 we log the need and allow the operator to handle it.
62                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}