grafos_securestore/
manager.rs1extern crate alloc;
4use alloc::boxed::Box;
5use alloc::vec::Vec;
6
7use grafos_locator::locator::MemRegionLocator;
8use zeroize::Zeroize;
9
10use crate::crypto::CryptoBackend;
11use crate::epoch::{EpochId, EpochInfo, EpochStatus};
12use crate::error::SecureStoreError;
13
14pub struct KeyEpochManager {
20 crypto: Box<dyn CryptoBackend>,
21 epochs: Vec<EpochEntry>,
22 next_epoch_id: u64,
23}
24
25struct EpochEntry {
26 info: EpochInfo,
27 key: Vec<u8>,
28}
29
30impl KeyEpochManager {
31 pub fn new(crypto: Box<dyn CryptoBackend>) -> Self {
33 Self {
34 crypto,
35 epochs: Vec::new(),
36 next_epoch_id: 1,
37 }
38 }
39
40 pub fn create_epoch(&mut self, now: u64, ttl_secs: u64) -> Result<EpochId, SecureStoreError> {
46 for entry in &mut self.epochs {
48 if entry.info.status == EpochStatus::Active {
49 entry.info.status = EpochStatus::Rotating;
50 }
51 }
52
53 let epoch_id = EpochId(self.next_epoch_id);
54 self.next_epoch_id += 1;
55
56 let key = self.crypto.generate_key();
57 let key_len = key.len() as u64;
58
59 let key_locator = MemRegionLocator::new(epoch_id.0 as u128, 0, key_len);
62
63 let info = EpochInfo {
64 epoch_id,
65 created_at: now,
66 expires_at: now.saturating_add(ttl_secs),
67 key_locator,
68 status: EpochStatus::Active,
69 };
70
71 self.epochs.push(EpochEntry { info, key });
72
73 Ok(epoch_id)
74 }
75
76 pub fn active_epoch(&self) -> Option<&EpochInfo> {
78 self.epochs
79 .iter()
80 .find(|e| e.info.status == EpochStatus::Active)
81 .map(|e| &e.info)
82 }
83
84 pub fn rotate(&mut self, now: u64, new_ttl_secs: u64) -> Result<EpochId, SecureStoreError> {
89 self.create_epoch(now, new_ttl_secs)
90 }
91
92 pub fn renew_active(&mut self, duration_secs: u64) -> Result<u64, SecureStoreError> {
97 let entry = self
98 .epochs
99 .iter_mut()
100 .find(|e| e.info.status == EpochStatus::Active)
101 .ok_or(SecureStoreError::NoActiveEpoch)?;
102 entry.info.expires_at = entry.info.expires_at.saturating_add(duration_secs);
103 Ok(entry.info.expires_at)
104 }
105
106 pub fn expire_old(&mut self, now: u64) {
112 for entry in &mut self.epochs {
113 if entry.info.status != EpochStatus::Expired && entry.info.expires_at <= now {
114 entry.info.status = EpochStatus::Expired;
115 entry.key.zeroize();
117 }
118 }
119 }
120
121 pub fn get_key(&self, epoch_id: EpochId) -> Result<&[u8], SecureStoreError> {
125 let entry = self
126 .epochs
127 .iter()
128 .find(|e| e.info.epoch_id == epoch_id)
129 .ok_or(SecureStoreError::EpochNotFound(epoch_id))?;
130
131 if entry.info.status == EpochStatus::Expired {
132 return Err(SecureStoreError::EpochExpired(epoch_id));
133 }
134
135 if entry.key.iter().all(|&b| b == 0) {
137 return Err(SecureStoreError::KeyUnavailable(epoch_id));
138 }
139
140 Ok(&entry.key)
141 }
142
143 pub fn get_epoch(&self, epoch_id: EpochId) -> Option<&EpochInfo> {
145 self.epochs
146 .iter()
147 .find(|e| e.info.epoch_id == epoch_id)
148 .map(|e| &e.info)
149 }
150
151 pub fn epoch_count(&self) -> usize {
153 self.epochs.len()
154 }
155
156 pub(crate) fn crypto(&self) -> &dyn CryptoBackend {
158 &*self.crypto
159 }
160}