veridian_kernel/services/mesh/
identity.rs1#![allow(dead_code)]
8
9use alloc::{collections::BTreeMap, string::String, vec::Vec};
10
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
17pub struct SpiffeId {
18 pub trust_domain: String,
20 pub path: String,
22}
23
24impl SpiffeId {
25 pub fn new(trust_domain: String, path: String) -> Self {
27 SpiffeId { trust_domain, path }
28 }
29
30 pub fn from_k8s(trust_domain: &str, namespace: &str, service_account: &str) -> Self {
32 SpiffeId {
33 trust_domain: String::from(trust_domain),
34 path: alloc::format!("/ns/{}/sa/{}", namespace, service_account),
35 }
36 }
37
38 pub fn uri(&self) -> String {
40 alloc::format!("spiffe://{}{}", self.trust_domain, self.path)
41 }
42
43 pub fn parse(uri: &str) -> Option<Self> {
45 let stripped = uri.strip_prefix("spiffe://")?;
46 let slash_pos = stripped.find('/')?;
47 let trust_domain = String::from(&stripped[..slash_pos]);
48 let path = String::from(&stripped[slash_pos..]);
49 Some(SpiffeId { trust_domain, path })
50 }
51}
52
53#[derive(Debug, Clone)]
59pub struct ServiceIdentity {
60 pub spiffe_id: SpiffeId,
62 pub certificate_data: Vec<u8>,
64 pub private_key_data: Vec<u8>,
66 pub expiry_tick: u64,
68 pub issued_tick: u64,
70 pub serial: u64,
72}
73
74impl ServiceIdentity {
75 pub fn is_expired(&self, current_tick: u64) -> bool {
77 current_tick >= self.expiry_tick
78 }
79
80 pub fn needs_rotation(&self, current_tick: u64) -> bool {
82 let lifetime = self.expiry_tick.saturating_sub(self.issued_tick);
83 let threshold = self.expiry_tick.saturating_sub(lifetime / 5);
84 current_tick >= threshold
85 }
86}
87
88#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum IdentityError {
95 NotFound(String),
97 AlreadyExists(String),
99 CertificateExpired(String),
101 InvalidSpiffeUri(String),
103 CaError(String),
105}
106
107#[derive(Debug)]
113pub struct IdentityProvider {
114 identities: BTreeMap<String, ServiceIdentity>,
116 trust_domain: String,
118 ca_certificate: Vec<u8>,
120 ca_private_key: Vec<u8>,
122 next_serial: u64,
124 cert_lifetime: u64,
126}
127
128impl Default for IdentityProvider {
129 fn default() -> Self {
130 Self::new(String::from("cluster.local"))
131 }
132}
133
134impl IdentityProvider {
135 pub const DEFAULT_CERT_LIFETIME: u64 = 3600;
137
138 pub fn new(trust_domain: String) -> Self {
140 let mut ca_cert = Vec::with_capacity(32);
142 for (i, b) in trust_domain.bytes().enumerate() {
143 ca_cert.push(b.wrapping_add(i as u8));
144 }
145 let ca_key = ca_cert.clone();
146
147 IdentityProvider {
148 identities: BTreeMap::new(),
149 trust_domain,
150 ca_certificate: ca_cert,
151 ca_private_key: ca_key,
152 next_serial: 1,
153 cert_lifetime: Self::DEFAULT_CERT_LIFETIME,
154 }
155 }
156
157 pub fn issue_identity(
159 &mut self,
160 spiffe_id: SpiffeId,
161 current_tick: u64,
162 ) -> Result<&ServiceIdentity, IdentityError> {
163 let uri = spiffe_id.uri();
164 if self.identities.contains_key(&uri) {
165 return Err(IdentityError::AlreadyExists(uri));
166 }
167
168 let serial = self.next_serial;
169 self.next_serial += 1;
170
171 let mut cert_data = Vec::with_capacity(64);
173 cert_data.extend_from_slice(&serial.to_le_bytes());
174 cert_data.extend_from_slice(¤t_tick.to_le_bytes());
175 for b in uri.bytes() {
176 cert_data.push(b);
177 }
178
179 let key_data = cert_data.iter().map(|b| b.wrapping_add(0x42)).collect();
180
181 let identity = ServiceIdentity {
182 spiffe_id,
183 certificate_data: cert_data,
184 private_key_data: key_data,
185 expiry_tick: current_tick + self.cert_lifetime,
186 issued_tick: current_tick,
187 serial,
188 };
189
190 self.identities.insert(uri.clone(), identity);
191 Ok(self.identities.get(&uri).unwrap())
192 }
193
194 pub fn verify_identity(
196 &self,
197 spiffe_uri: &str,
198 current_tick: u64,
199 ) -> Result<bool, IdentityError> {
200 let identity = self
201 .identities
202 .get(spiffe_uri)
203 .ok_or_else(|| IdentityError::NotFound(String::from(spiffe_uri)))?;
204
205 if identity.is_expired(current_tick) {
206 return Err(IdentityError::CertificateExpired(String::from(spiffe_uri)));
207 }
208
209 Ok(true)
210 }
211
212 pub fn rotate_certificate(
214 &mut self,
215 spiffe_uri: &str,
216 current_tick: u64,
217 ) -> Result<&ServiceIdentity, IdentityError> {
218 let identity = self
219 .identities
220 .get(spiffe_uri)
221 .ok_or_else(|| IdentityError::NotFound(String::from(spiffe_uri)))?;
222
223 let spiffe_id = identity.spiffe_id.clone();
224
225 self.identities.remove(spiffe_uri);
227 self.issue_identity(spiffe_id, current_tick)
228 }
229
230 pub fn get_identity(&self, spiffe_uri: &str) -> Option<&ServiceIdentity> {
232 self.identities.get(spiffe_uri)
233 }
234
235 pub fn list_identities(&self) -> Vec<&ServiceIdentity> {
237 self.identities.values().collect()
238 }
239
240 pub fn ca_certificate(&self) -> &[u8] {
242 &self.ca_certificate
243 }
244
245 pub fn trust_domain(&self) -> &str {
247 &self.trust_domain
248 }
249
250 pub fn identity_count(&self) -> usize {
252 self.identities.len()
253 }
254}
255
256#[cfg(test)]
261mod tests {
262 #[allow(unused_imports)]
263 use alloc::string::ToString;
264
265 use super::*;
266
267 fn make_provider() -> IdentityProvider {
268 IdentityProvider::new(String::from("cluster.local"))
269 }
270
271 fn test_spiffe() -> SpiffeId {
272 SpiffeId::from_k8s("cluster.local", "default", "nginx")
273 }
274
275 #[test]
276 fn test_spiffe_uri() {
277 let id = test_spiffe();
278 assert_eq!(id.uri(), "spiffe://cluster.local/ns/default/sa/nginx");
279 }
280
281 #[test]
282 fn test_spiffe_parse() {
283 let uri = "spiffe://cluster.local/ns/default/sa/app";
284 let id = SpiffeId::parse(uri).unwrap();
285 assert_eq!(id.trust_domain, "cluster.local");
286 assert_eq!(id.path, "/ns/default/sa/app");
287 }
288
289 #[test]
290 fn test_issue_identity() {
291 let mut provider = make_provider();
292 let identity = provider.issue_identity(test_spiffe(), 100).unwrap();
293 assert_eq!(identity.issued_tick, 100);
294 assert!(!identity.certificate_data.is_empty());
295 assert_eq!(provider.identity_count(), 1);
296 }
297
298 #[test]
299 fn test_issue_duplicate() {
300 let mut provider = make_provider();
301 provider.issue_identity(test_spiffe(), 100).unwrap();
302 assert!(provider.issue_identity(test_spiffe(), 200).is_err());
303 }
304
305 #[test]
306 fn test_verify_identity() {
307 let mut provider = make_provider();
308 let id = test_spiffe();
309 let uri = id.uri();
310 provider.issue_identity(id, 100).unwrap();
311 assert!(provider.verify_identity(&uri, 200).unwrap());
312 }
313
314 #[test]
315 fn test_verify_expired() {
316 let mut provider = make_provider();
317 let id = test_spiffe();
318 let uri = id.uri();
319 provider.issue_identity(id, 100).unwrap();
320 assert!(provider.verify_identity(&uri, 4000).is_err());
322 }
323
324 #[test]
325 fn test_rotate_certificate() {
326 let mut provider = make_provider();
327 let id = test_spiffe();
328 let uri = id.uri();
329 provider.issue_identity(id, 100).unwrap();
330 let serial_before = provider.get_identity(&uri).unwrap().serial;
331
332 let rotated = provider.rotate_certificate(&uri, 3000).unwrap();
333 assert!(rotated.serial > serial_before);
334 assert_eq!(rotated.issued_tick, 3000);
335 }
336
337 #[test]
338 fn test_needs_rotation() {
339 let identity = ServiceIdentity {
340 spiffe_id: test_spiffe(),
341 certificate_data: Vec::new(),
342 private_key_data: Vec::new(),
343 expiry_tick: 1000,
344 issued_tick: 0,
345 serial: 1,
346 };
347 assert!(identity.needs_rotation(850));
349 assert!(!identity.needs_rotation(500));
351 }
352}