use crate::derivation::DerivationTree;
use crate::error::{CapError, CapResult, ProofError};
use crate::grant::{validate_grant, GrantPolicy};
use crate::revoke::{revoke_capability, RevokeResult};
use crate::table::CapabilityTable;
use crate::verify::{PolicyContext, ProofVerifier};
use crate::DEFAULT_CAP_TABLE_CAPACITY;
use rvm_types::{CapRights, CapToken, CapType, PartitionId};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CapManagerConfig {
pub max_delegation_depth: u8,
pub track_derivation: bool,
pub initial_epoch: u32,
}
impl CapManagerConfig {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
max_delegation_depth: crate::DEFAULT_MAX_DELEGATION_DEPTH,
track_derivation: true,
initial_epoch: 0,
}
}
#[inline]
#[must_use]
pub const fn with_max_depth(mut self, depth: u8) -> Self {
self.max_delegation_depth = depth;
self
}
}
impl Default for CapManagerConfig {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ManagerStats {
pub caps_created: u64,
pub caps_granted: u64,
pub caps_revoked: u64,
pub revoke_operations: u64,
pub max_depth_reached: u8,
}
pub struct CapabilityManager<const N: usize = DEFAULT_CAP_TABLE_CAPACITY> {
table: CapabilityTable<N>,
derivation: DerivationTree<N>,
verifier: ProofVerifier<N>,
config: CapManagerConfig,
grant_policy: GrantPolicy,
epoch: u32,
next_id: u64,
stats: ManagerStats,
}
impl<const N: usize> CapabilityManager<N> {
#[must_use]
pub const fn new(config: CapManagerConfig) -> Self {
Self {
table: CapabilityTable::new(),
derivation: DerivationTree::new(),
verifier: ProofVerifier::new(config.initial_epoch),
grant_policy: GrantPolicy {
max_depth: config.max_delegation_depth,
allow_grant_once: true,
},
epoch: config.initial_epoch,
next_id: 1,
config,
stats: ManagerStats {
caps_created: 0,
caps_granted: 0,
caps_revoked: 0,
revoke_operations: 0,
max_depth_reached: 0,
},
}
}
#[must_use]
pub const fn with_defaults() -> Self {
Self::new(CapManagerConfig::new())
}
#[inline]
#[must_use]
pub const fn config(&self) -> &CapManagerConfig {
&self.config
}
#[inline]
#[must_use]
pub const fn stats(&self) -> &ManagerStats {
&self.stats
}
#[inline]
#[must_use]
pub const fn epoch(&self) -> u32 {
self.epoch
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.table.len()
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.table.is_empty()
}
pub fn increment_epoch(&mut self) {
self.epoch = self.epoch.wrapping_add(1);
self.verifier.set_epoch(self.epoch);
}
pub fn create_root_capability(
&mut self,
cap_type: CapType,
rights: CapRights,
badge: u64,
owner: PartitionId,
) -> CapResult<(u32, u32)> {
self.create_root_capability_inner(cap_type, rights, badge, owner)
}
pub fn create_root_capability_checked(
&mut self,
cap_type: CapType,
rights: CapRights,
badge: u64,
owner: PartitionId,
caller_id: PartitionId,
) -> CapResult<(u32, u32)> {
if !caller_id.is_hypervisor() {
return Err(CapError::GrantNotPermitted);
}
self.create_root_capability_inner(cap_type, rights, badge, owner)
}
fn create_root_capability_inner(
&mut self,
cap_type: CapType,
rights: CapRights,
badge: u64,
owner: PartitionId,
) -> CapResult<(u32, u32)> {
let id = self.next_id;
self.next_id = self.next_id.checked_add(1).ok_or(CapError::TableFull)?;
let token = CapToken::new(id, cap_type, rights, self.epoch);
let (index, generation) = self.table.insert_root(token, owner, badge)?;
if self.config.track_derivation {
self.derivation.add_root(index, u64::from(self.epoch))?;
}
self.stats.caps_created = self.stats.caps_created.wrapping_add(1);
Ok((index, generation))
}
pub fn grant(
&mut self,
source_index: u32,
source_generation: u32,
requested_rights: CapRights,
badge: u64,
target_owner: PartitionId,
) -> CapResult<(u32, u32)> {
self.grant_with_caller(
source_index,
source_generation,
requested_rights,
badge,
target_owner,
None,
)
}
pub fn grant_checked(
&mut self,
source_index: u32,
source_generation: u32,
requested_rights: CapRights,
badge: u64,
target_owner: PartitionId,
caller_id: PartitionId,
) -> CapResult<(u32, u32)> {
self.grant_with_caller(
source_index,
source_generation,
requested_rights,
badge,
target_owner,
Some(caller_id),
)
}
fn grant_with_caller(
&mut self,
source_index: u32,
source_generation: u32,
requested_rights: CapRights,
badge: u64,
target_owner: PartitionId,
caller_id: Option<PartitionId>,
) -> CapResult<(u32, u32)> {
let source_slot = self.table.lookup(source_index, source_generation)?;
let source_copy = *source_slot;
if let Some(caller) = caller_id {
if source_copy.owner != caller {
return Err(CapError::GrantNotPermitted);
}
}
let id = self.next_id;
self.next_id = self.next_id.checked_add(1).ok_or(CapError::TableFull)?;
let (derived_token, depth, consume_grant_once) = validate_grant(
&source_copy,
requested_rights,
id,
badge,
self.epoch,
self.grant_policy,
)?;
let (child_index, child_generation) =
self.table
.insert_derived(derived_token, target_owner, depth, source_index, badge)?;
if self.config.track_derivation {
if let Err(e) =
self.derivation
.add_child(source_index, child_index, depth, u64::from(self.epoch))
{
self.table.force_invalidate(child_index);
return Err(e);
}
}
if consume_grant_once {
if let Ok(slot) = self.table.lookup_mut(source_index, source_generation) {
let new_rights = slot.token.rights().difference(CapRights::GRANT_ONCE);
slot.token = CapToken::new(
slot.token.id(),
slot.token.cap_type(),
new_rights,
slot.token.epoch(),
);
}
}
self.stats.caps_granted = self.stats.caps_granted.wrapping_add(1);
if depth > self.stats.max_depth_reached {
self.stats.max_depth_reached = depth;
}
Ok((child_index, child_generation))
}
pub fn revoke(&mut self, index: u32, generation: u32) -> CapResult<RevokeResult> {
let result = revoke_capability(&mut self.table, &mut self.derivation, index, generation)?;
self.stats.caps_revoked = self
.stats
.caps_revoked
.wrapping_add(result.revoked_count as u64);
self.stats.revoke_operations = self.stats.revoke_operations.wrapping_add(1);
Ok(result)
}
pub fn verify_p1(
&self,
cap_index: u32,
cap_generation: u32,
required_rights: CapRights,
) -> Result<(), ProofError> {
self.verifier
.verify_p1(&self.table, cap_index, cap_generation, required_rights)
}
pub fn verify_p2(
&mut self,
cap_index: u32,
cap_generation: u32,
ctx: &PolicyContext,
) -> Result<(), ProofError> {
self.verifier.verify_p2(
&self.table,
&self.derivation,
cap_index,
cap_generation,
ctx,
)
}
pub fn verify_p3(
&self,
cap_index: u32,
cap_generation: u32,
max_depth: u8,
) -> Result<(), ProofError> {
self.verifier.verify_p3(
&self.table,
&self.derivation,
cap_index,
cap_generation,
max_depth,
)
}
#[must_use]
pub fn table(&self) -> &CapabilityTable<N> {
&self.table
}
}
impl<const N: usize> Default for CapabilityManager<N> {
fn default() -> Self {
Self::with_defaults()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::CapError;
fn all_rights() -> CapRights {
CapRights::READ
.union(CapRights::WRITE)
.union(CapRights::EXECUTE)
.union(CapRights::GRANT)
.union(CapRights::REVOKE)
}
#[test]
fn test_create_root_capability() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let (idx, gen) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
assert_eq!(mgr.len(), 1);
assert!(mgr.table().lookup(idx, gen).is_ok());
assert_eq!(mgr.stats().caps_created, 1);
}
#[test]
fn test_grant_and_verify() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let target = PartitionId::new(2);
let (root_idx, root_gen) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
let (child_idx, child_gen) = mgr
.grant(root_idx, root_gen, CapRights::READ, 42, target)
.unwrap();
assert_eq!(mgr.len(), 2);
let child = mgr.table().lookup(child_idx, child_gen).unwrap();
assert_eq!(child.token.rights(), CapRights::READ);
assert_eq!(child.depth, 1);
}
#[test]
fn test_revoke_propagation() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let target = PartitionId::new(2);
let (root_idx, root_gen) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
let (c1_idx, c1_gen) = mgr
.grant(
root_idx,
root_gen,
CapRights::READ.union(CapRights::GRANT),
1,
target,
)
.unwrap();
let _ = mgr
.grant(c1_idx, c1_gen, CapRights::READ, 2, target)
.unwrap();
assert_eq!(mgr.len(), 3);
let result = mgr.revoke(root_idx, root_gen).unwrap();
assert_eq!(result.revoked_count, 3);
}
#[test]
fn test_delegation_depth_limit() {
let config = CapManagerConfig::new().with_max_depth(2);
let mut mgr = CapabilityManager::<64>::new(config);
let owner = PartitionId::new(1);
let (i0, g0) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
let (i1, g1) = mgr.grant(i0, g0, all_rights(), 1, owner).unwrap();
let (i2, g2) = mgr.grant(i1, g1, all_rights(), 2, owner).unwrap();
let result = mgr.grant(i2, g2, CapRights::READ, 3, owner);
assert_eq!(result, Err(CapError::DelegationDepthExceeded));
}
#[test]
fn test_epoch_invalidation() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let (idx, gen) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
assert!(mgr.verify_p1(idx, gen, CapRights::READ).is_ok());
mgr.increment_epoch();
assert_eq!(
mgr.verify_p1(idx, gen, CapRights::READ),
Err(ProofError::StaleCapability)
);
}
#[test]
fn test_p3_root_capability_passes() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let (idx, gen) = mgr
.create_root_capability(CapType::Region, all_rights(), 0, owner)
.unwrap();
assert!(mgr.verify_p3(idx, gen, 8).is_ok());
}
#[test]
fn test_p3_nonexistent_fails() {
let mgr = CapabilityManager::<64>::with_defaults();
assert_eq!(
mgr.verify_p3(99, 0, 8),
Err(ProofError::DerivationChainBroken),
);
}
#[test]
fn test_create_root_checked_hypervisor_allowed() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let result = mgr.create_root_capability_checked(
CapType::Region,
all_rights(),
0,
owner,
PartitionId::hypervisor(),
);
assert!(result.is_ok());
}
#[test]
fn test_create_root_checked_non_hypervisor_denied() {
let mut mgr = CapabilityManager::<64>::with_defaults();
let owner = PartitionId::new(1);
let result = mgr.create_root_capability_checked(
CapType::Region,
all_rights(),
0,
owner,
PartitionId::new(1), );
assert_eq!(result, Err(CapError::GrantNotPermitted));
}
}