1use std::collections::HashMap;
18use std::sync::{Arc, Mutex};
19
20use crate::bytecode::{Cap, CapId, CapTarget, NativeMask, RevocationCell};
21
22pub use crate::bytecode::CapRights;
23
24use super::error::RuntimeError;
25use super::process::FlowId;
26use super::sync_lock;
27
28const MINT_ATTEMPTS: u32 = 32;
29
30#[derive(Clone, Debug, PartialEq, Eq)]
32pub struct Capability {
33 pub holder: FlowId,
35 pub cap: Cap,
36}
37
38impl Capability {
39 pub fn target(&self) -> Option<FlowId> {
41 self.cap.target.flow_id().map(FlowId)
42 }
43
44 pub fn rights(&self) -> CapRights {
45 self.cap.rights
46 }
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum CapError {
52 Unknown,
53 NotHolder,
54 InsufficientRights,
55 WrongTarget,
56 Unavailable,
58 DuplicateRequestId,
60}
61
62impl std::fmt::Display for CapError {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 match self {
65 CapError::Unknown => f.write_str("unknown or revoked capability"),
66 CapError::NotHolder => f.write_str("calling flow does not hold this capability"),
67 CapError::InsufficientRights => f.write_str("capability lacks required rights"),
68 CapError::WrongTarget => f.write_str("capability target is not a flow"),
69 CapError::Unavailable => f.write_str("capability table unavailable (poisoned lock)"),
70 CapError::DuplicateRequestId => {
71 f.write_str("duplicate in-flight request_id")
72 }
73 }
74 }
75}
76
77impl std::error::Error for CapError {}
78
79impl From<RuntimeError> for CapError {
80 fn from(_err: RuntimeError) -> Self {
81 CapError::Unavailable
82 }
83}
84
85struct CapTableInner {
86 entries: HashMap<CapId, Capability>,
87 flow_cells: HashMap<u64, Arc<RevocationCell>>,
88 reply_index: HashMap<(FlowId, FlowId), CapId>,
93}
94
95pub struct CapTable {
97 inner: Mutex<CapTableInner>,
98 native_cell: Arc<RevocationCell>,
99 scheduler_cell: Arc<RevocationCell>,
100}
101
102impl CapTable {
103 pub fn new() -> Self {
104 Self {
105 inner: Mutex::new(CapTableInner {
106 entries: HashMap::new(),
107 flow_cells: HashMap::new(),
108 reply_index: HashMap::new(),
109 }),
110 native_cell: Arc::new(RevocationCell::new()),
111 scheduler_cell: Arc::new(RevocationCell::new()),
112 }
113 }
114
115 pub fn native_cell(&self) -> Arc<RevocationCell> {
116 Arc::clone(&self.native_cell)
117 }
118
119 fn lock(&self, where_: &'static str) -> Result<std::sync::MutexGuard<'_, CapTableInner>, RuntimeError> {
120 sync_lock::lock(&self.inner, where_)
121 }
122
123 pub fn bind_flow(&self, flow: FlowId) -> Result<Arc<RevocationCell>, RuntimeError> {
125 let mut g = self.lock("CapTable::bind_flow")?;
126 Ok(g.flow_cells
127 .entry(flow.as_u64())
128 .or_insert_with(|| Arc::new(RevocationCell::new()))
129 .clone())
130 }
131
132 pub fn flow_cell(&self, flow: FlowId) -> Result<Option<Arc<RevocationCell>>, RuntimeError> {
133 Ok(self.lock("CapTable::flow_cell")?.flow_cells.get(&flow.as_u64()).cloned())
134 }
135
136 pub fn scheduler_cell(&self) -> Arc<RevocationCell> {
137 Arc::clone(&self.scheduler_cell)
138 }
139
140 fn insert_fresh(
141 table: &mut HashMap<CapId, Capability>,
142 entry: Capability,
143 ) -> Result<CapId, RuntimeError> {
144 for _ in 0..MINT_ATTEMPTS {
145 let id = CapId::random().map_err(|_| RuntimeError::EntropyFailed)?;
146 if id.is_none() || table.contains_key(&id) {
147 continue;
148 }
149 table.insert(id, entry);
150 return Ok(id);
151 }
152 Err(RuntimeError::CapIdCollision)
153 }
154
155 fn insert(&self, holder: FlowId, cap: Cap) -> Result<CapId, RuntimeError> {
156 let mut table = self.lock("CapTable::insert")?;
157 Self::insert_fresh(
158 &mut table.entries,
159 Capability { holder, cap },
160 )
161 }
162
163 pub fn mint(
169 &self,
170 holder: FlowId,
171 target: FlowId,
172 rights: CapRights,
173 ) -> Result<CapId, RuntimeError> {
174 let cell = self.bind_flow(target)?;
175 let cap = Cap::root(CapTarget::Flow(target.as_u64()), rights, None, cell.as_ref());
176 self.grant(holder, cap)
177 }
178
179 pub fn mint_or_reuse(
188 &self,
189 holder: FlowId,
190 target: FlowId,
191 rights: CapRights,
192 ) -> Result<CapId, RuntimeError> {
193 let mut g = self.lock("CapTable::mint_or_reuse")?;
194 if let Some(&id) = g.reply_index.get(&(holder, target)) {
195 if Self::reply_cap_is_live(&g, id, holder, target, rights) {
196 return Ok(id);
197 }
198 g.reply_index.remove(&(holder, target));
199 }
200 let cell = g
201 .flow_cells
202 .entry(target.as_u64())
203 .or_insert_with(|| Arc::new(RevocationCell::new()))
204 .clone();
205 let cap = Cap::root(CapTarget::Flow(target.as_u64()), rights, None, cell.as_ref());
206 let id = Self::insert_fresh(&mut g.entries, Capability { holder, cap })?;
207 g.reply_index.insert((holder, target), id);
208 Ok(id)
209 }
210
211 fn reply_cap_is_live(
212 table: &CapTableInner,
213 id: CapId,
214 holder: FlowId,
215 target: FlowId,
216 rights: CapRights,
217 ) -> bool {
218 let Some(entry) = table.entries.get(&id) else {
219 return false;
220 };
221 if entry.holder != holder {
222 return false;
223 }
224 if entry.cap.target != CapTarget::Flow(target.as_u64()) {
225 return false;
226 }
227 if !entry.cap.rights.contains(rights) {
228 return false;
229 }
230 match table.flow_cells.get(&target.as_u64()) {
231 Some(cell) => entry.cap.is_valid(cell.as_ref()),
232 None => false,
233 }
234 }
235
236 pub fn grant(&self, holder: FlowId, cap: Cap) -> Result<CapId, RuntimeError> {
239 self.insert(holder, cap)
240 }
241
242 pub fn lookup(&self, id: CapId) -> Result<Option<Capability>, RuntimeError> {
244 if id.is_none() {
245 return Ok(None);
246 }
247 Ok(self.lock("CapTable::lookup")?.entries.get(&id).cloned())
248 }
249
250 pub fn resolve(
252 &self,
253 id: CapId,
254 holder: FlowId,
255 required: CapRights,
256 ) -> Result<Capability, CapError> {
257 if id.is_none() {
258 return Err(CapError::Unknown);
259 }
260 let table = self.lock("CapTable::resolve")?;
261 let entry = table.entries.get(&id).cloned().ok_or(CapError::Unknown)?;
262 if entry.holder != holder {
263 return Err(CapError::NotHolder);
264 }
265 if !entry.cap.rights.contains(required) {
266 return Err(CapError::InsufficientRights);
267 }
268 let valid = match entry.cap.target {
269 CapTarget::Flow(fid) => match table.flow_cells.get(&fid) {
270 Some(cell) => entry.cap.is_valid(cell.as_ref()),
271 None => false,
272 },
273 CapTarget::NativeTable => entry.cap.is_valid(self.native_cell.as_ref()),
274 CapTarget::Scheduler => entry.cap.is_valid(self.scheduler_cell.as_ref()),
275 };
276 if !valid {
277 return Err(CapError::Unknown);
278 }
279 Ok(entry)
280 }
281
282 pub fn attenuate(
285 &self,
286 id: CapId,
287 from_holder: FlowId,
288 to_holder: FlowId,
289 want_rights: CapRights,
290 want_native: Option<&NativeMask>,
291 ) -> Result<CapId, CapError> {
292 let src = self.resolve(id, from_holder, CapRights::empty())?;
293 let cell = match src.cap.target {
294 CapTarget::Flow(fid) => self
295 .lock("CapTable::attenuate")?
296 .flow_cells
297 .get(&fid)
298 .cloned()
299 .ok_or(CapError::Unknown)?,
300 CapTarget::NativeTable | CapTarget::Scheduler => {
301 return Err(CapError::WrongTarget);
302 }
303 };
304 let narrowed = match super::delegate::exec_delegate(
305 &src.cap,
306 cell.as_ref(),
307 want_rights,
308 want_native,
309 ) {
310 Ok(c) => c,
311 Err(super::delegate::DelegateError::SourceRevoked) => {
312 return Err(CapError::Unknown)
313 }
314 Err(super::delegate::DelegateError::SourceLacksNative) => {
315 return Err(CapError::InsufficientRights)
316 }
317 };
318 if !narrowed.is_valid(cell.as_ref()) {
319 return Err(CapError::Unknown);
320 }
321 self.insert(to_holder, narrowed).map_err(CapError::from)
322 }
323
324 pub fn delegate(
326 &self,
327 id: CapId,
328 from_holder: FlowId,
329 to_holder: FlowId,
330 ) -> Result<CapId, CapError> {
331 let src = self.resolve(id, from_holder, CapRights::empty())?;
332 self.attenuate(id, from_holder, to_holder, src.cap.rights, src.cap.native_mask.as_ref())
333 }
334
335 pub fn reissue_for(&self, id: CapId, new_holder: FlowId) -> Result<CapId, CapError> {
339 let cap = self.lookup(id)?.ok_or(CapError::Unknown)?;
340 let narrowed = cap.cap.attenuate(cap.cap.rights, cap.cap.native_mask.as_ref());
341 self.insert(new_holder, narrowed).map_err(CapError::from)
342 }
343
344 pub fn revoke_flow(&self, flow: FlowId) -> Result<usize, RuntimeError> {
347 let mut g = self.lock("CapTable::revoke_flow")?;
348 if let Some(cell) = g.flow_cells.get(&flow.as_u64()) {
349 cell.revoke();
350 }
351 g.flow_cells.remove(&flow.as_u64());
352 let before = g.entries.len();
353 let fid = flow.as_u64();
354 g.entries.retain(|_, e| {
355 e.holder != flow && e.cap.target != CapTarget::Flow(fid)
356 });
357 g.reply_index.retain(|(h, t), _| *h != flow && *t != flow);
358 Ok(before - g.entries.len())
359 }
360
361 #[cfg(test)]
362 fn entry_count(&self) -> Result<usize, RuntimeError> {
363 Ok(self.lock("CapTable::entry_count")?.entries.len())
364 }
365
366 #[cfg(test)]
367 fn reply_index_len(&self) -> Result<usize, RuntimeError> {
368 Ok(self.lock("CapTable::reply_index_len")?.reply_index.len())
369 }
370
371 #[cfg(test)]
372 fn reply_index_mentions(&self, flow: FlowId) -> Result<bool, RuntimeError> {
373 Ok(self
374 .lock("CapTable::reply_index_mentions")?
375 .reply_index
376 .keys()
377 .any(|(h, t)| *h == flow || *t == flow))
378 }
379}
380
381impl Default for CapTable {
382 fn default() -> Self {
383 Self::new()
384 }
385}
386
387#[cfg(test)]
388mod tests {
389 use super::*;
390 use crate::scheduler::process::next_flow_id;
391
392 #[test]
393 fn random_ids_are_not_sequential() -> Result<(), Box<dyn std::error::Error>> {
394 let table = CapTable::new();
395 let h = next_flow_id();
396 let t = next_flow_id();
397 let a = table.mint(h, t, CapRights::SEND)?;
398 let b = table.mint(h, t, CapRights::SEND)?;
399 assert_ne!(a, b);
400 assert!(!a.is_none());
401 assert_eq!(
402 table.resolve(CapId::from_raw(1), h, CapRights::SEND),
403 Err(CapError::Unknown)
404 );
405 Ok(())
406 }
407
408 #[test]
409 fn resolve_requires_holder_and_rights() -> Result<(), Box<dyn std::error::Error>> {
410 let table = CapTable::new();
411 let holder = next_flow_id();
412 let other = next_flow_id();
413 let target = next_flow_id();
414 let cap = table.mint(holder, target, CapRights::SEND)?;
415 assert_eq!(
416 table.resolve(cap, holder, CapRights::SEND)?.target(),
417 Some(target)
418 );
419 assert_eq!(
420 table.resolve(cap, other, CapRights::SEND),
421 Err(CapError::NotHolder)
422 );
423 assert_eq!(
424 table.resolve(cap, holder, CapRights::ASK),
425 Err(CapError::InsufficientRights)
426 );
427 Ok(())
428 }
429
430 #[test]
431 fn capability_isolation_is_per_table() -> Result<(), Box<dyn std::error::Error>> {
432 let a = CapTable::new();
433 let b = CapTable::new();
434 let holder = next_flow_id();
435 let target = next_flow_id();
436 let cap = a.mint(holder, target, CapRights::SEND)?;
437 assert_eq!(
438 b.resolve(cap, holder, CapRights::SEND),
439 Err(CapError::Unknown)
440 );
441 Ok(())
442 }
443
444 #[test]
445 fn finalizing_flow_revokes_held_and_targeted_caps() -> Result<(), Box<dyn std::error::Error>> {
446 let table = CapTable::new();
447 let dead = next_flow_id();
448 let alive = next_flow_id();
449 let target = next_flow_id();
450
451 let held_by_dead = table.mint(dead, target, CapRights::SEND)?;
452 let targeting_dead = table.mint(alive, dead, CapRights::SEND)?;
453 let unrelated = table.mint(alive, target, CapRights::SEND)?;
454
455 let removed = table.revoke_flow(dead)?;
456 assert_eq!(removed, 2);
457 assert_eq!(
458 table.resolve(held_by_dead, dead, CapRights::SEND),
459 Err(CapError::Unknown)
460 );
461 assert_eq!(
462 table.resolve(targeting_dead, alive, CapRights::SEND),
463 Err(CapError::Unknown)
464 );
465 assert!(table.resolve(unrelated, alive, CapRights::SEND).is_ok());
466 Ok(())
467 }
468
469 #[test]
470 fn delegate_issues_a_new_id_for_the_child() -> Result<(), Box<dyn std::error::Error>> {
471 let table = CapTable::new();
472 let parent = next_flow_id();
473 let child = next_flow_id();
474 let target = next_flow_id();
475 let original = table.mint(parent, target, CapRights::SEND_ASK)?;
476 let granted = table.delegate(original, parent, child)?;
477 assert_ne!(granted, original);
478 assert_eq!(
479 table.resolve(granted, child, CapRights::SEND)?.target(),
480 Some(target)
481 );
482 assert_eq!(
483 table.resolve(original, child, CapRights::SEND),
484 Err(CapError::NotHolder)
485 );
486 assert!(table.resolve(original, parent, CapRights::ASK).is_ok());
487 Ok(())
488 }
489
490 #[test]
491 fn attenuate_cannot_escalate() -> Result<(), Box<dyn std::error::Error>> {
492 let table = CapTable::new();
493 let parent = next_flow_id();
494 let child = next_flow_id();
495 let target = next_flow_id();
496 let original = table.mint(parent, target, CapRights::SEND)?;
497 let granted = table.attenuate(
498 original,
499 parent,
500 child,
501 CapRights::SEND.union(CapRights::ADMIN),
502 None,
503 )?;
504 let got = table.resolve(granted, child, CapRights::SEND)?;
505 assert!(!got.rights().contains(CapRights::ADMIN));
506 assert!(got.rights().contains(CapRights::SEND));
507 Ok(())
508 }
509
510 #[test]
511 fn mint_or_reuse_is_stable_per_pair() -> Result<(), Box<dyn std::error::Error>> {
512 let table = CapTable::new();
513 let holder = next_flow_id();
514 let target = next_flow_id();
515 let first = table.mint_or_reuse(holder, target, CapRights::SEND)?;
516 for _ in 0..1_000 {
517 let again = table.mint_or_reuse(holder, target, CapRights::SEND)?;
518 assert_eq!(again, first);
519 }
520 assert_eq!(table.entry_count()?, 1);
521 assert_eq!(table.reply_index_len()?, 1);
522 assert!(table.resolve(first, holder, CapRights::SEND).is_ok());
523 Ok(())
524 }
525
526 #[test]
527 fn mint_or_reuse_distinct_pairs_are_independent() -> Result<(), Box<dyn std::error::Error>> {
528 let table = CapTable::new();
529 let a = next_flow_id();
530 let b = next_flow_id();
531 let ab = table.mint_or_reuse(b, a, CapRights::SEND)?;
532 let ba = table.mint_or_reuse(a, b, CapRights::SEND)?;
533 assert_ne!(ab, ba);
534 assert_eq!(table.entry_count()?, 2);
535 assert_eq!(table.reply_index_len()?, 2);
536 Ok(())
537 }
538
539 #[test]
540 fn revoke_flow_clears_reply_index_for_holder_and_target() -> Result<(), Box<dyn std::error::Error>> {
541 let table = CapTable::new();
542 let a = next_flow_id();
543 let b = next_flow_id();
544 let alive = next_flow_id();
545 let ab = table.mint_or_reuse(b, a, CapRights::SEND)?;
546 let ba = table.mint_or_reuse(a, b, CapRights::SEND)?;
547 let kept = table.mint_or_reuse(alive, b, CapRights::SEND)?;
548
549 table.revoke_flow(a)?;
550 assert!(!table.reply_index_mentions(a)?);
551 assert_eq!(
552 table.resolve(ab, b, CapRights::SEND),
553 Err(CapError::Unknown)
554 );
555 assert_eq!(
556 table.resolve(ba, a, CapRights::SEND),
557 Err(CapError::Unknown)
558 );
559 assert!(table.resolve(kept, alive, CapRights::SEND).is_ok());
560 assert!(!table.reply_index_mentions(a)?);
561 assert!(table.reply_index_mentions(b)?);
562
563 table.revoke_flow(b)?;
564 assert!(!table.reply_index_mentions(a)?);
565 assert!(!table.reply_index_mentions(b)?);
566 Ok(())
567 }
568
569 #[test]
570 fn mint_or_reuse_after_revoke_issues_a_fresh_id() -> Result<(), Box<dyn std::error::Error>> {
571 let table = CapTable::new();
572 let holder = next_flow_id();
573 let target = next_flow_id();
574 let old = table.mint_or_reuse(holder, target, CapRights::SEND)?;
575 table.revoke_flow(target)?;
576 assert!(!table.reply_index_mentions(target)?);
577 let fresh = table.mint_or_reuse(holder, target, CapRights::SEND)?;
578 assert_ne!(fresh, old);
579 assert_eq!(
580 table.resolve(old, holder, CapRights::SEND),
581 Err(CapError::Unknown)
582 );
583 assert!(table.resolve(fresh, holder, CapRights::SEND).is_ok());
584 Ok(())
585 }
586}