1#![allow(unexpected_cfgs)]
2#![allow(dead_code)]
9
10#[cfg(feature = "alloc")]
11use alloc::collections::BTreeMap;
12#[cfg(feature = "alloc")]
13use alloc::vec::Vec;
14
15pub const CAP_RIGHT_READ: u32 = 1 << 0;
17pub const CAP_RIGHT_WRITE: u32 = 1 << 1;
18pub const CAP_RIGHT_EXECUTE: u32 = 1 << 2;
19pub const CAP_RIGHT_GRANT: u32 = 1 << 3;
20pub const CAP_RIGHT_REVOKE: u32 = 1 << 4;
21pub const CAP_RIGHT_MAP: u32 = 1 << 5;
22pub const CAP_RIGHT_DERIVE: u32 = 1 << 6;
23pub const CAP_ALL_RIGHTS: u32 = 0x7F;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct CapabilityModel {
28 pub token: u64,
30 pub rights: u32,
32 pub owner: u64,
34 pub generation: u32,
36 pub parent_token: u64,
38 pub address_space: u64,
40}
41
42impl CapabilityModel {
43 pub fn encode(index: u32, rights: u32, generation: u32) -> u64 {
47 let gen = (generation as u64 & 0xFFFF) << 48;
48 let rts = ((rights as u64) & 0xFFFF) << 32;
49 let idx = index as u64;
50 gen | rts | idx
51 }
52
53 pub fn decode(token: u64) -> (u32, u32, u32) {
55 let generation = ((token >> 48) & 0xFFFF) as u32;
56 let rights = ((token >> 32) & 0xFFFF) as u32;
57 let index = (token & 0xFFFF_FFFF) as u32;
58 (index, rights, generation)
59 }
60
61 pub fn derive(
63 &self,
64 child_token: u64,
65 child_rights: u32,
66 child_owner: u64,
67 ) -> Result<CapabilityModel, CapModelError> {
68 if child_rights & !self.rights != 0 {
70 return Err(CapModelError::RightsEscalation);
71 }
72
73 if self.rights & CAP_RIGHT_DERIVE == 0 {
75 return Err(CapModelError::NoDeriveRight);
76 }
77
78 Ok(CapabilityModel {
79 token: child_token,
80 rights: child_rights,
81 owner: child_owner,
82 generation: self.generation,
83 parent_token: self.token,
84 address_space: child_owner, })
86 }
87
88 pub fn has_rights(&self, required: u32) -> bool {
90 self.rights & required == required
91 }
92}
93
94#[derive(Debug, Clone, Default)]
96pub struct CapSpaceModel {
97 #[cfg(feature = "alloc")]
99 capabilities: BTreeMap<u64, CapabilityModel>,
100 #[cfg(feature = "alloc")]
102 children: BTreeMap<u64, Vec<u64>>,
103 #[cfg(feature = "alloc")]
105 revocation_log: Vec<u64>,
106 current_generation: u32,
108 next_token: u64,
110}
111
112#[cfg(feature = "alloc")]
113impl CapSpaceModel {
114 pub fn new() -> Self {
116 Self {
117 capabilities: BTreeMap::new(),
118 children: BTreeMap::new(),
119 revocation_log: Vec::new(),
120 current_generation: 0,
121 next_token: 1, }
123 }
124
125 pub fn create_root(&mut self, rights: u32, owner: u64) -> CapabilityModel {
127 let token = self.next_token;
128 self.next_token += 1;
129
130 let cap = CapabilityModel {
131 token,
132 rights,
133 owner,
134 generation: self.current_generation,
135 parent_token: 0,
136 address_space: owner,
137 };
138
139 self.capabilities.insert(token, cap);
140 cap
141 }
142
143 pub fn derive(
145 &mut self,
146 parent_token: u64,
147 child_rights: u32,
148 child_owner: u64,
149 ) -> Result<CapabilityModel, CapModelError> {
150 let parent = self
151 .capabilities
152 .get(&parent_token)
153 .ok_or(CapModelError::InvalidToken)?;
154
155 let child_token = self.next_token;
156 self.next_token += 1;
157
158 let child = parent.derive(child_token, child_rights, child_owner)?;
159
160 self.capabilities.insert(child_token, child);
161 self.children
162 .entry(parent_token)
163 .or_default()
164 .push(child_token);
165
166 Ok(child)
167 }
168
169 pub fn revoke(&mut self, token: u64) -> Result<usize, CapModelError> {
171 if !self.capabilities.contains_key(&token) {
172 return Err(CapModelError::InvalidToken);
173 }
174
175 let mut revoked = Vec::new();
176 self.collect_descendants(token, &mut revoked);
177 revoked.push(token);
178
179 let count = revoked.len();
180 for t in &revoked {
181 self.capabilities.remove(t);
182 self.children.remove(t);
183 self.revocation_log.push(*t);
184 }
185
186 self.current_generation = self.current_generation.saturating_add(1);
188
189 Ok(count)
190 }
191
192 fn collect_descendants(&self, token: u64, result: &mut Vec<u64>) {
194 if let Some(children) = self.children.get(&token) {
195 for &child in children {
196 result.push(child);
197 self.collect_descendants(child, result);
198 }
199 }
200 }
201
202 pub fn lookup(&self, token: u64) -> Option<&CapabilityModel> {
204 self.capabilities.get(&token)
205 }
206
207 pub fn validate(
209 &self,
210 token: u64,
211 expected_gen: u32,
212 ) -> Result<&CapabilityModel, CapModelError> {
213 let cap = self
214 .capabilities
215 .get(&token)
216 .ok_or(CapModelError::InvalidToken)?;
217 if cap.generation != expected_gen {
218 return Err(CapModelError::StaleGeneration);
219 }
220 Ok(cap)
221 }
222
223 pub fn count(&self) -> usize {
225 self.capabilities.len()
226 }
227
228 pub fn revocation_log(&self) -> &[u64] {
230 &self.revocation_log
231 }
232}
233
234pub struct CapInvariantChecker;
236
237#[cfg(feature = "alloc")]
238impl CapInvariantChecker {
239 pub fn verify_non_forgery(
244 space: &CapSpaceModel,
245 random_token: u64,
246 ) -> Result<(), CapModelError> {
247 if random_token >= space.next_token && space.capabilities.contains_key(&random_token) {
250 return Err(CapModelError::ForgeryDetected);
251 }
252 Ok(())
253 }
254
255 pub fn verify_rights_monotonicity(space: &CapSpaceModel) -> Result<(), CapModelError> {
258 for (_, cap) in space.capabilities.iter() {
259 if cap.parent_token != 0 {
260 if let Some(parent) = space.capabilities.get(&cap.parent_token) {
261 if cap.rights & !parent.rights != 0 {
262 return Err(CapModelError::RightsEscalation);
263 }
264 }
265 }
266 }
267 Ok(())
268 }
269
270 pub fn verify_revocation_completeness(
272 space: &CapSpaceModel,
273 revoked_token: u64,
274 ) -> Result<(), CapModelError> {
275 if space.capabilities.contains_key(&revoked_token) {
277 return Err(CapModelError::RevocationIncomplete);
278 }
279
280 for (_, cap) in space.capabilities.iter() {
282 if cap.parent_token == revoked_token {
283 return Err(CapModelError::OrphanCapability);
284 }
285 }
286
287 Ok(())
288 }
289
290 pub fn verify_generation_integrity(
292 space: &CapSpaceModel,
293 old_gen: u32,
294 ) -> Result<(), CapModelError> {
295 if space.current_generation <= old_gen {
296 return Err(CapModelError::StaleGeneration);
297 }
298 Ok(())
299 }
300}
301
302#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304pub enum CapModelError {
305 InvalidToken,
307 RightsEscalation,
309 NoDeriveRight,
311 StaleGeneration,
313 ForgeryDetected,
315 RevocationIncomplete,
317 OrphanCapability,
319 IsolationBreach,
321}
322
323#[cfg(kani)]
328mod kani_proofs {
329 use super::*;
330
331 #[kani::proof]
333 fn proof_token_encoding_roundtrip() {
334 let index: u32 = kani::any();
335 let rights: u32 = kani::any();
336 let generation: u32 = kani::any();
337
338 kani::assume(rights <= 0xFFFF);
340 kani::assume(generation <= 0xFFFF);
341
342 let encoded = CapabilityModel::encode(index, rights, generation);
343 let (dec_index, dec_rights, dec_gen) = CapabilityModel::decode(encoded);
344
345 assert_eq!(dec_index, index);
346 assert_eq!(dec_rights, rights);
347 assert_eq!(dec_gen, generation);
348 }
349
350 #[kani::proof]
353 fn proof_no_forgery() {
354 let mut space = CapSpaceModel::new();
355 space.create_root(CAP_ALL_RIGHTS, 1);
356
357 let random: u64 = kani::any();
358 kani::assume(random >= space.next_token); assert!(space.lookup(random).is_none());
361 }
362
363 #[kani::proof]
365 fn proof_derivation_subset() {
366 let parent_rights: u32 = kani::any();
367 kani::assume(parent_rights & CAP_RIGHT_DERIVE != 0); kani::assume(parent_rights <= CAP_ALL_RIGHTS);
369
370 let child_rights: u32 = kani::any();
371 kani::assume(child_rights <= CAP_ALL_RIGHTS);
372
373 let parent = CapabilityModel {
374 token: 1,
375 rights: parent_rights,
376 owner: 1,
377 generation: 0,
378 parent_token: 0,
379 address_space: 1,
380 };
381
382 let result = parent.derive(2, child_rights, 2);
383
384 match result {
385 Ok(child) => {
386 assert_eq!(child.rights & !parent.rights, 0);
388 }
389 Err(CapModelError::RightsEscalation) => {
390 assert!(child_rights & !parent_rights != 0);
392 }
393 _ => panic!("Unexpected error"),
394 }
395 }
396
397 #[kani::proof]
399 fn proof_cascading_revocation() {
400 let mut space = CapSpaceModel::new();
401
402 let root = space.create_root(CAP_ALL_RIGHTS, 1);
403 let child = space.derive(root.token, CAP_RIGHT_READ, 2).unwrap();
404 let grandchild = space.derive(child.token, CAP_RIGHT_READ, 3).unwrap();
405
406 let root_token = root.token;
407 let child_token = child.token;
408 let grandchild_token = grandchild.token;
409
410 space.revoke(root_token).unwrap();
411
412 assert!(space.lookup(root_token).is_none());
413 assert!(space.lookup(child_token).is_none());
414 assert!(space.lookup(grandchild_token).is_none());
415 }
416
417 #[kani::proof]
419 fn proof_generation_invalidation() {
420 let mut space = CapSpaceModel::new();
421
422 let cap = space.create_root(CAP_ALL_RIGHTS, 1);
423 let old_gen = cap.generation;
424
425 space.revoke(cap.token).unwrap();
426
427 assert!(space.current_generation > old_gen);
429 }
430
431 #[kani::proof]
433 fn proof_rights_mask_operations() {
434 let r1: u32 = kani::any();
435 let r2: u32 = kani::any();
436 kani::assume(r1 <= CAP_ALL_RIGHTS);
437 kani::assume(r2 <= CAP_ALL_RIGHTS);
438
439 let intersection = r1 & r2;
441 assert_eq!(intersection & !r1, 0);
442 assert_eq!(intersection & !r2, 0);
443
444 let union = r1 | r2;
446 assert_eq!(union & r1, r1);
447 assert_eq!(union & r2, r2);
448
449 let complement = !r1 & CAP_ALL_RIGHTS;
451 assert_eq!(complement & r1, 0);
452 }
453
454 #[kani::proof]
456 fn proof_capability_isolation() {
457 let mut space = CapSpaceModel::new();
458
459 let cap1 = space.create_root(CAP_ALL_RIGHTS, 1);
460 let cap2 = space.create_root(CAP_ALL_RIGHTS, 2);
461
462 assert_eq!(cap1.address_space, 1);
463 assert_eq!(cap2.address_space, 2);
464 assert_ne!(cap1.address_space, cap2.address_space);
465 }
466
467 #[kani::proof]
469 fn proof_revocation_completeness() {
470 let mut space = CapSpaceModel::new();
471
472 let root = space.create_root(CAP_ALL_RIGHTS, 1);
473 let _child = space
474 .derive(root.token, CAP_RIGHT_READ | CAP_RIGHT_DERIVE, 2)
475 .unwrap();
476
477 let root_token = root.token;
478 space.revoke(root_token).unwrap();
479
480 assert!(CapInvariantChecker::verify_revocation_completeness(&space, root_token).is_ok());
482 }
483}
484
485#[cfg(test)]
490mod tests {
491 use super::*;
492
493 #[test]
494 fn test_token_encode_decode() {
495 let encoded = CapabilityModel::encode(42, 0x1F, 3);
496 let (idx, rights, gen) = CapabilityModel::decode(encoded);
497 assert_eq!(idx, 42);
498 assert_eq!(rights, 0x1F);
499 assert_eq!(gen, 3);
500 }
501
502 #[test]
503 fn test_has_rights() {
504 let cap = CapabilityModel {
505 token: 1,
506 rights: CAP_RIGHT_READ | CAP_RIGHT_WRITE,
507 owner: 1,
508 generation: 0,
509 parent_token: 0,
510 address_space: 1,
511 };
512
513 assert!(cap.has_rights(CAP_RIGHT_READ));
514 assert!(cap.has_rights(CAP_RIGHT_WRITE));
515 assert!(cap.has_rights(CAP_RIGHT_READ | CAP_RIGHT_WRITE));
516 assert!(!cap.has_rights(CAP_RIGHT_EXECUTE));
517 }
518
519 #[test]
520 fn test_derive_subset() {
521 let parent = CapabilityModel {
522 token: 1,
523 rights: CAP_RIGHT_READ | CAP_RIGHT_WRITE | CAP_RIGHT_DERIVE,
524 owner: 1,
525 generation: 0,
526 parent_token: 0,
527 address_space: 1,
528 };
529
530 let child = parent.derive(2, CAP_RIGHT_READ, 2).unwrap();
531 assert_eq!(child.rights, CAP_RIGHT_READ);
532 assert_eq!(child.parent_token, 1);
533 }
534
535 #[test]
536 fn test_derive_escalation_fails() {
537 let parent = CapabilityModel {
538 token: 1,
539 rights: CAP_RIGHT_READ | CAP_RIGHT_DERIVE,
540 owner: 1,
541 generation: 0,
542 parent_token: 0,
543 address_space: 1,
544 };
545
546 let result = parent.derive(2, CAP_RIGHT_WRITE, 2);
547 assert_eq!(result, Err(CapModelError::RightsEscalation));
548 }
549
550 #[test]
551 fn test_derive_without_derive_right() {
552 let parent = CapabilityModel {
553 token: 1,
554 rights: CAP_RIGHT_READ,
555 owner: 1,
556 generation: 0,
557 parent_token: 0,
558 address_space: 1,
559 };
560
561 let result = parent.derive(2, CAP_RIGHT_READ, 2);
562 assert_eq!(result, Err(CapModelError::NoDeriveRight));
563 }
564
565 #[cfg(feature = "alloc")]
566 #[test]
567 fn test_capspace_create_and_lookup() {
568 let mut space = CapSpaceModel::new();
569 let cap = space.create_root(CAP_ALL_RIGHTS, 1);
570 assert!(space.lookup(cap.token).is_some());
571 assert_eq!(space.count(), 1);
572 }
573
574 #[cfg(feature = "alloc")]
575 #[test]
576 fn test_capspace_derive() {
577 let mut space = CapSpaceModel::new();
578 let root = space.create_root(CAP_ALL_RIGHTS, 1);
579 let child = space.derive(root.token, CAP_RIGHT_READ, 2).unwrap();
580 assert_eq!(child.rights, CAP_RIGHT_READ);
581 assert_eq!(space.count(), 2);
582 }
583
584 #[cfg(feature = "alloc")]
585 #[test]
586 fn test_cascading_revocation() {
587 let mut space = CapSpaceModel::new();
588 let root = space.create_root(CAP_ALL_RIGHTS, 1);
589 let child = space
590 .derive(root.token, CAP_RIGHT_READ | CAP_RIGHT_DERIVE, 2)
591 .unwrap();
592 let _grandchild = space.derive(child.token, CAP_RIGHT_READ, 3).unwrap();
593
594 assert_eq!(space.count(), 3);
595
596 let revoked = space.revoke(root.token).unwrap();
597 assert_eq!(revoked, 3); assert_eq!(space.count(), 0);
599 }
600
601 #[cfg(feature = "alloc")]
602 #[test]
603 fn test_generation_bump() {
604 let mut space = CapSpaceModel::new();
605 let gen_before = space.current_generation;
606
607 let cap = space.create_root(CAP_ALL_RIGHTS, 1);
608 space.revoke(cap.token).unwrap();
609
610 assert!(space.current_generation > gen_before);
611 }
612
613 #[cfg(feature = "alloc")]
614 #[test]
615 fn test_rights_monotonicity() {
616 let mut space = CapSpaceModel::new();
617 let root = space.create_root(CAP_ALL_RIGHTS, 1);
618 let _child = space.derive(root.token, CAP_RIGHT_READ, 2).unwrap();
619
620 assert!(CapInvariantChecker::verify_rights_monotonicity(&space).is_ok());
621 }
622}