use crate::types::{CapId, CapPayload, Capability, Key, PolicyState, SealedTag, Time};
#[derive(Debug, Clone)]
pub struct KeyState {
pub(crate) current: Key,
pub(crate) epoch: u64,
pub(crate) previous: Option<Key>,
pub(crate) previous_epoch: Option<u64>,
}
impl KeyState {
pub fn initial(key: Key) -> Self {
KeyState {
current: key,
epoch: 0,
previous: None,
previous_epoch: None,
}
}
pub fn empty() -> Self {
KeyState {
current: Key::empty(),
epoch: 0,
previous: None,
previous_epoch: None,
}
}
pub fn rotate(&mut self, new_key: Key) {
self.previous = Some(self.current.clone());
self.previous_epoch = Some(self.epoch);
self.current = new_key;
self.epoch = self
.epoch
.checked_add(1)
.expect("key epoch overflow (unreachable in practice)");
}
pub fn rotated(&self, new_key: Key) -> Self {
KeyState {
current: new_key,
epoch: self
.epoch
.checked_add(1)
.expect("key epoch overflow (unreachable in practice)"),
previous: Some(self.current.clone()),
previous_epoch: Some(self.epoch),
}
}
#[inline]
pub fn current(&self) -> &Key {
&self.current
}
#[inline]
pub fn epoch(&self) -> u64 {
self.epoch
}
#[inline]
pub fn previous(&self) -> Option<&Key> {
self.previous.as_ref()
}
#[inline]
pub fn previous_epoch(&self) -> Option<u64> {
self.previous_epoch
}
pub fn epoch_valid(&self, cap_epoch: u64) -> bool {
if cap_epoch >= self.epoch {
return true;
}
match self.previous_epoch {
Some(prev_epoch) => cap_epoch >= prev_epoch,
None => false,
}
}
pub fn verify_seal(&self, payload: &CapPayload, tag: &SealedTag) -> bool {
if crate::crypto::verify_seal(&self.current, payload, tag) {
return true;
}
match &self.previous {
Some(prev_key) => crate::crypto::verify_seal(prev_key, payload, tag),
None => false,
}
}
pub fn verify_with_epoch(&self, payload: &CapPayload, tag: &SealedTag, cap_epoch: u64) -> bool {
self.epoch_valid(cap_epoch) && self.verify_seal(payload, tag)
}
pub fn clear_previous(&mut self) {
self.previous = None;
self.previous_epoch = None;
}
}
impl Default for KeyState {
fn default() -> Self {
KeyState::empty()
}
}
pub type CapTable = Vec<(CapId, Capability)>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KernelError {
CapNotFound(CapId),
CapIdExhausted,
CapIdCollision(CapId),
InvalidCapability(String),
CounterOverflow(&'static str),
}
impl std::fmt::Display for KernelError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
KernelError::CapNotFound(id) => write!(f, "Capability {id} not found"),
KernelError::CapIdExhausted => write!(f, "Capability ID space exhausted"),
KernelError::CapIdCollision(id) => write!(f, "Capability ID {id} already exists"),
KernelError::InvalidCapability(msg) => write!(f, "Invalid capability: {msg}"),
KernelError::CounterOverflow(name) => write!(f, "{name} counter overflow"),
}
}
}
impl std::error::Error for KernelError {}
pub type ChildrenIndex = Vec<(CapId, Vec<CapId>)>;
#[derive(Debug, Clone)]
pub struct RevocationState {
pub(crate) caps: CapTable,
pub(crate) epoch: u64,
pub(crate) children_index: ChildrenIndex,
}
impl RevocationState {
pub fn empty() -> Self {
RevocationState {
caps: Vec::new(),
epoch: 0,
children_index: Vec::new(),
}
}
fn find_index(&self, cap_id: CapId) -> Option<usize> {
self.caps
.binary_search_by_key(&cap_id, |entry| entry.0)
.ok()
}
fn find_insert_pos(&self, cap_id: CapId) -> (usize, bool) {
match self.caps.binary_search_by_key(&cap_id, |entry| entry.0) {
Ok(pos) => (pos, true),
Err(pos) => (pos, false),
}
}
pub fn is_valid(&self, cap_id: CapId) -> bool {
self.find_index(cap_id)
.is_some_and(|idx| self.caps[idx].1.is_valid())
}
pub fn get(&self, cap_id: CapId) -> Option<&Capability> {
self.find_index(cap_id).map(|idx| &self.caps[idx].1)
}
#[allow(dead_code)] pub(crate) fn get_mut(&mut self, cap_id: CapId) -> Option<&mut Capability> {
self.find_index(cap_id).map(|idx| &mut self.caps[idx].1)
}
#[inline]
pub fn contains(&self, cap_id: CapId) -> bool {
self.find_index(cap_id).is_some()
}
pub fn revoke(&self, cap_id: CapId) -> Self {
let new_caps = self
.caps
.iter()
.map(|(id, cap)| {
if *id == cap_id {
let mut revoked_cap = cap.clone();
revoked_cap.revoke();
(*id, revoked_cap)
} else {
(*id, cap.clone())
}
})
.collect();
RevocationState {
caps: new_caps,
epoch: self.epoch,
children_index: self.children_index.clone(),
}
}
pub fn revoke_mut(&mut self, cap_id: CapId) -> Result<(), KernelError> {
match self.find_index(cap_id) {
Some(idx) => {
self.caps[idx].1.revoke();
Ok(())
}
None => Err(KernelError::CapNotFound(cap_id)),
}
}
pub fn has_ancestor(&self, cap_id: CapId, ancestor_id: CapId) -> bool {
self.has_ancestor_with_fuel(cap_id, ancestor_id, cap_id)
}
fn has_ancestor_with_fuel(&self, cap_id: CapId, ancestor_id: CapId, fuel: u128) -> bool {
if fuel == 0 {
return false;
}
match self.get(cap_id) {
None => false,
Some(cap) => match cap.parent() {
None => false,
Some(parent_id) => {
if parent_id == ancestor_id {
true
} else {
self.has_ancestor_with_fuel(parent_id, ancestor_id, fuel - 1)
}
}
},
}
}
pub fn in_revoke_set(&self, k: CapId, cap_id: CapId) -> bool {
k == cap_id || self.has_ancestor(k, cap_id)
}
pub fn revoke_transitive(&self, cap_id: CapId) -> Self {
let new_caps = self
.caps
.iter()
.map(|(k, cap)| {
if self.in_revoke_set(*k, cap_id) {
let mut revoked = cap.clone();
revoked.revoke();
(*k, revoked)
} else {
(*k, cap.clone())
}
})
.collect();
RevocationState {
caps: new_caps,
epoch: self.epoch,
children_index: self.children_index.clone(),
}
}
pub fn revoke_transitive_mut(&mut self, cap_id: CapId) {
let mut to_revoke = Vec::with_capacity(self.caps.len());
to_revoke.extend(
self.caps
.iter()
.enumerate()
.filter_map(|(i, (id, _))| self.in_revoke_set(*id, cap_id).then_some(i)),
);
for idx in to_revoke {
self.caps[idx].1.revoke();
}
}
pub fn insert(&self, cap: Capability) -> Result<Self, KernelError> {
let id = cap.id();
let parent = cap.parent();
let (pos, exists) = self.find_insert_pos(id);
if exists {
return Err(KernelError::CapIdCollision(id));
}
let mut new_caps = Vec::with_capacity(self.caps.len() + 1);
new_caps.extend_from_slice(&self.caps[..pos]);
new_caps.push((id, cap));
new_caps.extend_from_slice(&self.caps[pos..]);
let mut new_index = self.children_index.clone();
if let Some(parent_id) = parent {
Self::add_child_to_index(&mut new_index, parent_id, id);
}
Ok(RevocationState {
caps: new_caps,
epoch: self.epoch,
children_index: new_index,
})
}
pub fn insert_mut(&mut self, cap: Capability) -> Result<(), KernelError> {
let id = cap.id();
let parent = cap.parent();
let (pos, exists) = self.find_insert_pos(id);
if exists {
return Err(KernelError::CapIdCollision(id));
}
self.caps.insert(pos, (id, cap));
if let Some(parent_id) = parent {
Self::add_child_to_index(&mut self.children_index, parent_id, id);
}
Ok(())
}
fn collect_descendants(&self, cap_id: CapId) -> Vec<CapId> {
let mut queue: Vec<CapId> = Vec::with_capacity(self.caps.len().saturating_add(1));
let mut head: usize = 0;
let mut visited = std::collections::BTreeSet::new();
let mut collected: Vec<CapId> = Vec::new();
queue.push(cap_id);
while head < queue.len() {
let id = queue[head];
head += 1;
if !visited.insert(id) {
continue;
}
collected.push(id);
queue.extend(self.get_children(id).iter().copied());
}
collected
}
fn get_children(&self, parent_id: CapId) -> &[CapId] {
match self
.children_index
.binary_search_by_key(&parent_id, |entry| entry.0)
{
Ok(idx) => &self.children_index[idx].1,
Err(_) => &[],
}
}
fn add_child_to_index(index: &mut ChildrenIndex, parent_id: CapId, child_id: CapId) {
match index.binary_search_by_key(&parent_id, |entry| entry.0) {
Ok(idx) => {
index[idx].1.push(child_id);
}
Err(pos) => {
index.insert(pos, (parent_id, vec![child_id]));
}
}
}
pub fn revoke_transitive_fast(&self, cap_id: CapId) -> Self {
let mut to_revoke = self.collect_descendants(cap_id);
to_revoke.sort_unstable();
let new_caps = self
.caps
.iter()
.map(|(k, cap)| {
if to_revoke.binary_search(k).is_ok() {
let mut revoked = cap.clone();
revoked.revoke();
(*k, revoked)
} else {
(*k, cap.clone())
}
})
.collect();
RevocationState {
caps: new_caps,
epoch: self.epoch,
children_index: self.children_index.clone(),
}
}
pub fn revoke_transitive_fast_mut(&mut self, cap_id: CapId) -> Result<(), KernelError> {
if self.find_index(cap_id).is_none() {
return Err(KernelError::CapNotFound(cap_id));
}
let mut to_revoke = self.collect_descendants(cap_id);
to_revoke.sort_unstable();
for id in to_revoke {
if let Some(idx) = self.find_index(id) {
self.caps[idx].1.revoke();
}
}
Ok(())
}
#[inline]
pub fn epoch(&self) -> u64 {
self.epoch
}
#[inline]
pub fn cap_count(&self) -> usize {
self.caps.len()
}
pub fn valid_caps(&self) -> Vec<(CapId, Capability)> {
let mut result = Vec::with_capacity(self.caps.len());
let mut i = 0;
while i < self.caps.len() {
if self.caps[i].1.is_valid() {
result.push((self.caps[i].0, self.caps[i].1.clone()));
}
i += 1;
}
result
}
pub fn cap_ids(&self) -> Vec<CapId> {
let mut ids = Vec::with_capacity(self.caps.len());
let mut i = 0;
while i < self.caps.len() {
ids.push(self.caps[i].0);
i += 1;
}
ids
}
#[inline]
pub fn iter(&self) -> std::slice::Iter<'_, (CapId, Capability)> {
self.caps.iter()
}
pub fn any_valid_targeting(&self, target: crate::types::ResourceId) -> bool {
self.caps
.iter()
.any(|(_, cap)| cap.is_valid() && cap.target() == target)
}
}
impl Default for RevocationState {
fn default() -> Self {
RevocationState::empty()
}
}
#[derive(Debug, Clone)]
pub struct KernelState {
pub(crate) key_state: KeyState,
pub(crate) policy: PolicyState,
pub(crate) revocation: RevocationState,
pub(crate) now: Time,
pub(crate) next_cap_id: CapId,
}
impl KernelState {
pub fn initial(key: Key, policy: PolicyState) -> Self {
KernelState {
key_state: KeyState::initial(key),
policy,
revocation: RevocationState::empty(),
now: 0,
next_cap_id: 0,
}
}
pub fn tick(&self) -> Result<Self, KernelError> {
let new_now = self
.now
.checked_add(1)
.ok_or(KernelError::CounterOverflow("time"))?;
Ok(KernelState {
key_state: self.key_state.clone(),
policy: self.policy.clone(),
revocation: self.revocation.clone(),
now: new_now,
next_cap_id: self.next_cap_id,
})
}
pub fn rotate_key(&mut self, new_key: Key) {
self.key_state.rotate(new_key);
}
pub fn with_rotated_key(&self, new_key: Key) -> Self {
KernelState {
key_state: self.key_state.rotated(new_key),
policy: self.policy.clone(),
revocation: self.revocation.clone(),
now: self.now,
next_cap_id: self.next_cap_id,
}
}
#[inline]
pub fn key_epoch(&self) -> u64 {
self.key_state.epoch()
}
#[inline]
pub fn key_state(&self) -> &KeyState {
&self.key_state
}
#[inline]
#[allow(dead_code)] pub(crate) fn key_state_mut(&mut self) -> &mut KeyState {
&mut self.key_state
}
pub fn verify_cap_seal(&self, cap: &Capability) -> bool {
self.key_state
.verify_with_epoch(&cap.payload(), cap.signature(), cap.epoch())
}
pub fn alloc_cap_id(&mut self) -> Result<CapId, KernelError> {
let id = self.next_cap_id;
self.next_cap_id = match self.next_cap_id.checked_add(1) {
Some(v) => v,
None => return Err(KernelError::CapIdExhausted),
};
Ok(id)
}
#[inline]
pub fn next_cap_id(&self) -> CapId {
self.next_cap_id
}
pub fn tick_checked(&mut self) -> Result<(), KernelError> {
self.now = self
.now
.checked_add(1)
.ok_or(KernelError::CounterOverflow("time"))?;
Ok(())
}
pub fn cap_not_revoked(&self, cap: &Capability) -> bool {
self.revocation.is_valid(cap.id())
}
#[inline]
pub fn now(&self) -> Time {
self.now
}
#[inline]
pub fn revocation(&self) -> &RevocationState {
&self.revocation
}
#[inline]
pub(crate) fn revocation_mut(&mut self) -> &mut RevocationState {
&mut self.revocation
}
#[inline]
pub fn policy(&self) -> &PolicyState {
&self.policy
}
#[inline]
#[allow(dead_code)] pub(crate) fn policy_mut(&mut self) -> &mut PolicyState {
&mut self.policy
}
#[inline]
#[allow(dead_code)] pub(crate) fn hmac_key(&self) -> &Key {
self.key_state.current()
}
}
impl Default for KernelState {
fn default() -> Self {
KernelState {
key_state: KeyState::empty(),
policy: PolicyState::default(),
revocation: RevocationState::empty(),
now: 0,
next_cap_id: 0,
}
}
}
use crate::types::{PluginId, ResourceId, SecurityLevel};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PluginMeta {
pub level: SecurityLevel,
pub held_caps: Vec<CapId>,
}
impl PluginMeta {
pub fn empty() -> Self {
PluginMeta {
level: SecurityLevel::Public,
held_caps: Vec::new(),
}
}
pub fn grant_cap(&mut self, cap_id: CapId) {
match self.held_caps.binary_search(&cap_id) {
Ok(_) => {} Err(pos) => self.held_caps.insert(pos, cap_id),
}
}
pub fn revoke_cap(&mut self, cap_id: CapId) {
if let Ok(pos) = self.held_caps.binary_search(&cap_id) {
self.held_caps.remove(pos);
}
}
pub fn holds_cap(&self, cap_id: CapId) -> bool {
self.held_caps.binary_search(&cap_id).is_ok()
}
}
impl Default for PluginMeta {
fn default() -> Self {
PluginMeta::empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResourceMeta {
pub level: SecurityLevel,
}
impl ResourceMeta {
pub fn empty() -> Self {
ResourceMeta {
level: SecurityLevel::Public,
}
}
}
impl Default for ResourceMeta {
fn default() -> Self {
ResourceMeta::empty()
}
}
#[derive(Debug, Clone)]
pub struct KernelStateCore {
pub(crate) key_state: KeyState,
pub(crate) revocation: RevocationState,
pub(crate) plugin_metas: Vec<(PluginId, PluginMeta)>,
pub(crate) resource_metas: Vec<(ResourceId, ResourceMeta)>,
pub(crate) now: Time,
#[allow(dead_code)] pub(crate) next_cap_id: CapId,
}
impl KernelStateCore {
pub fn empty(hmac_key: Key) -> Self {
KernelStateCore {
key_state: KeyState::initial(hmac_key),
revocation: RevocationState::empty(),
plugin_metas: Vec::new(),
resource_metas: Vec::new(),
now: 0,
next_cap_id: 0,
}
}
#[inline]
pub fn hmac_key(&self) -> &Key {
self.key_state.current()
}
#[inline]
pub fn key_state(&self) -> &KeyState {
&self.key_state
}
pub fn rotate_key(&mut self, new_key: Key) {
self.key_state.rotate(new_key);
}
pub fn verify_cap_seal(&self, cap: &Capability) -> bool {
self.key_state
.verify_with_epoch(&cap.payload(), cap.signature(), cap.epoch())
}
pub fn get_plugin_meta(&self, pid: PluginId) -> PluginMeta {
match self
.plugin_metas
.binary_search_by_key(&pid, |entry| entry.0)
{
Ok(idx) => self.plugin_metas[idx].1.clone(),
Err(_) => PluginMeta::empty(),
}
}
pub fn set_plugin_meta(&mut self, pid: PluginId, mut meta: PluginMeta) {
meta.held_caps.sort_unstable();
meta.held_caps.dedup();
match self
.plugin_metas
.binary_search_by_key(&pid, |entry| entry.0)
{
Ok(idx) => {
self.plugin_metas[idx].1 = meta;
}
Err(pos) => {
self.plugin_metas.insert(pos, (pid, meta));
}
}
}
pub fn get_resource_meta(&self, rid: ResourceId) -> ResourceMeta {
match self
.resource_metas
.binary_search_by_key(&rid, |entry| entry.0)
{
Ok(idx) => self.resource_metas[idx].1.clone(),
Err(_) => ResourceMeta::empty(),
}
}
pub fn set_resource_meta(&mut self, rid: ResourceId, meta: ResourceMeta) {
match self
.resource_metas
.binary_search_by_key(&rid, |entry| entry.0)
{
Ok(idx) => {
self.resource_metas[idx].1 = meta;
}
Err(pos) => {
self.resource_metas.insert(pos, (rid, meta));
}
}
}
pub fn plugin_level(&self, pid: PluginId) -> SecurityLevel {
self.get_plugin_meta(pid).level
}
pub fn resource_level(&self, rid: ResourceId) -> SecurityLevel {
self.get_resource_meta(rid).level
}
pub fn plugin_holds(&self, pid: PluginId, cap_id: CapId) -> bool {
self.plugin_metas
.binary_search_by_key(&pid, |e| e.0)
.ok()
.is_some_and(|i| self.plugin_metas[i].1.holds_cap(cap_id))
}
pub fn cap_is_valid(&self, cap_id: CapId) -> bool {
self.revocation.is_valid(cap_id)
}
pub fn now(&self) -> Time {
self.now
}
pub fn epoch(&self) -> u64 {
self.revocation.epoch()
}
}
impl Default for KernelStateCore {
fn default() -> Self {
KernelStateCore::empty(Key::empty())
}
}
#[derive(Debug, Clone)]
pub enum KernelRequest {
CapDelegate {
new_cap: Capability,
target: PluginId,
},
CapRevoke {
cap_id: CapId,
},
SetPluginLevel {
plugin_id: PluginId,
level: SecurityLevel,
},
SetResourceLevel {
resource_id: ResourceId,
level: SecurityLevel,
},
Tick,
RotateKey {
new_key: Key,
},
}
pub type KernelResult = Result<KernelStateCore, KernelError>;
pub fn kernel_dispatch(core: KernelStateCore, req: KernelRequest) -> KernelResult {
match req {
KernelRequest::CapDelegate { new_cap, target } => {
dispatch_cap_delegate(core, new_cap, target)
}
KernelRequest::CapRevoke { cap_id } => dispatch_cap_revoke(core, cap_id),
KernelRequest::SetPluginLevel { plugin_id, level } => {
dispatch_set_plugin_level(core, plugin_id, level)
}
KernelRequest::SetResourceLevel { resource_id, level } => {
dispatch_set_resource_level(core, resource_id, level)
}
KernelRequest::Tick => dispatch_tick(core),
KernelRequest::RotateKey { new_key } => Ok(dispatch_rotate_key(core, new_key)),
}
}
fn dispatch_cap_delegate(
mut core: KernelStateCore,
new_cap: Capability,
target: PluginId,
) -> KernelResult {
if core.revocation.contains(new_cap.id()) {
return Err(KernelError::CapIdCollision(new_cap.id()));
}
let cap_id = new_cap.id();
core.revocation.insert_mut(new_cap)?;
let mut target_meta = core.get_plugin_meta(target);
target_meta.grant_cap(cap_id);
core.set_plugin_meta(target, target_meta);
Ok(core)
}
#[allow(clippy::unnecessary_wraps)] fn dispatch_cap_revoke(mut core: KernelStateCore, cap_id: CapId) -> KernelResult {
core.revocation.revoke_transitive_fast_mut(cap_id)?;
Ok(core)
}
#[allow(clippy::unnecessary_wraps)] fn dispatch_set_plugin_level(
mut core: KernelStateCore,
plugin_id: PluginId,
level: SecurityLevel,
) -> KernelResult {
let mut meta = core.get_plugin_meta(plugin_id);
meta.level = level;
core.set_plugin_meta(plugin_id, meta);
Ok(core)
}
#[allow(clippy::unnecessary_wraps)] fn dispatch_set_resource_level(
mut core: KernelStateCore,
resource_id: ResourceId,
level: SecurityLevel,
) -> KernelResult {
let mut meta = core.get_resource_meta(resource_id);
meta.level = level;
core.set_resource_meta(resource_id, meta);
Ok(core)
}
fn dispatch_tick(mut core: KernelStateCore) -> Result<KernelStateCore, KernelError> {
core.now = core
.now
.checked_add(1)
.ok_or(KernelError::CounterOverflow("time"))?;
Ok(core)
}
fn dispatch_rotate_key(mut core: KernelStateCore, new_key: Key) -> KernelStateCore {
core.key_state.rotate(new_key);
core
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Right, Rights, SealedTag};
fn make_test_cap(id: CapId, parent: Option<CapId>) -> Capability {
Capability::new(
id,
1, 1, Rights::singleton(Right::Read),
parent,
0, SealedTag::empty(),
)
.expect("valid capability")
}
#[test]
fn test_revocation_state_empty() {
let rs = RevocationState::empty();
assert_eq!(rs.cap_count(), 0);
assert_eq!(rs.epoch(), 0);
assert!(!rs.is_valid(0));
}
#[test]
fn test_revocation_state_insert() {
let mut rs = RevocationState::empty();
let cap = make_test_cap(1, None);
rs.insert_mut(cap).expect("insert should succeed");
assert_eq!(rs.cap_count(), 1);
assert!(rs.contains(1));
assert!(rs.is_valid(1));
}
#[test]
fn test_revocation_state_insert_collision() {
let mut rs = RevocationState::empty();
let cap1 = make_test_cap(1, None);
rs.insert_mut(cap1).expect("first insert should succeed");
let cap2 = make_test_cap(1, None);
let result = rs.insert_mut(cap2);
assert!(matches!(result, Err(KernelError::CapIdCollision(1))));
}
#[test]
fn test_revocation_state_insert_sorted() {
let mut rs = RevocationState::empty();
rs.insert_mut(make_test_cap(5, None)).expect("insert 5");
rs.insert_mut(make_test_cap(2, None)).expect("insert 2");
rs.insert_mut(make_test_cap(8, None)).expect("insert 8");
rs.insert_mut(make_test_cap(1, None)).expect("insert 1");
let ids = rs.cap_ids();
assert_eq!(ids, vec![1, 2, 5, 8]);
}
#[test]
fn test_revocation_state_revoke() {
let mut rs = RevocationState::empty();
let cap = make_test_cap(1, None);
rs.insert_mut(cap).expect("insert should succeed");
assert!(rs.is_valid(1));
rs.revoke_mut(1).expect("revoke should succeed");
assert!(!rs.is_valid(1));
}
#[test]
fn test_revocation_state_has_ancestor() {
let mut rs = RevocationState::empty();
let cap1 = make_test_cap(1, None);
let cap2 = make_test_cap(2, Some(1));
let cap3 = make_test_cap(3, Some(2));
rs.insert_mut(cap1).expect("insert 1");
rs.insert_mut(cap2).expect("insert 2");
rs.insert_mut(cap3).expect("insert 3");
assert!(rs.has_ancestor(2, 1)); assert!(rs.has_ancestor(3, 2)); assert!(rs.has_ancestor(3, 1));
assert!(!rs.has_ancestor(1, 2)); assert!(!rs.has_ancestor(1, 3)); assert!(!rs.has_ancestor(2, 3)); }
#[test]
fn test_revocation_state_revoke_transitive() {
let mut rs = RevocationState::empty();
let cap1 = make_test_cap(1, None);
let cap2 = make_test_cap(2, Some(1));
let cap3 = make_test_cap(3, Some(2));
rs.insert_mut(cap1).expect("insert 1");
rs.insert_mut(cap2).expect("insert 2");
rs.insert_mut(cap3).expect("insert 3");
assert!(rs.is_valid(1));
assert!(rs.is_valid(2));
assert!(rs.is_valid(3));
rs.revoke_transitive_mut(1);
assert!(!rs.is_valid(1));
assert!(!rs.is_valid(2));
assert!(!rs.is_valid(3));
}
#[test]
fn test_revocation_state_revoke_transitive_partial() {
let mut rs = RevocationState::empty();
let cap1 = make_test_cap(1, None);
let cap2 = make_test_cap(2, Some(1));
let cap3 = make_test_cap(3, Some(2));
rs.insert_mut(cap1).expect("insert 1");
rs.insert_mut(cap2).expect("insert 2");
rs.insert_mut(cap3).expect("insert 3");
rs.revoke_transitive_mut(2);
assert!(rs.is_valid(1)); assert!(!rs.is_valid(2)); assert!(!rs.is_valid(3)); }
#[test]
fn test_kernel_state_initial() {
let key = Key::empty();
let policy = PolicyState::deny_all();
let ks = KernelState::initial(key, policy);
assert_eq!(ks.now(), 0);
assert_eq!(ks.revocation().cap_count(), 0);
}
#[test]
fn test_kernel_state_tick() {
let mut ks = KernelState::default();
assert_eq!(ks.now(), 0);
ks.tick_checked().expect("tick should succeed");
assert_eq!(ks.now(), 1);
ks.tick_checked().expect("tick should succeed");
assert_eq!(ks.now(), 2);
}
#[test]
fn test_kernel_state_cap_not_revoked() {
let mut ks = KernelState::default();
let cap = make_test_cap(1, None);
assert!(!ks.cap_not_revoked(&cap));
ks.revocation_mut()
.insert_mut(cap.clone())
.expect("insert should succeed");
assert!(ks.cap_not_revoked(&cap));
ks.revocation_mut()
.revoke_mut(1)
.expect("revoke should succeed");
assert!(!ks.cap_not_revoked(&cap));
}
#[test]
fn test_key_state_initial() {
let key = Key::from_bytes([1u8; 32]);
let ks = KeyState::initial(key.clone());
assert_eq!(ks.epoch(), 0);
assert!(ks.previous().is_none());
assert!(ks.previous_epoch().is_none());
}
#[test]
fn test_key_state_rotation() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let mut ks = KeyState::initial(key1.clone());
ks.rotate(key2.clone());
assert_eq!(ks.epoch(), 1);
assert!(ks.previous().is_some());
assert_eq!(ks.previous_epoch(), Some(0));
assert_eq!(ks.current().as_bytes(), key2.as_bytes());
assert_eq!(ks.previous().unwrap().as_bytes(), key1.as_bytes());
}
#[test]
fn test_key_state_epoch_valid() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let mut ks = KeyState::initial(key1);
assert!(ks.epoch_valid(0)); assert!(ks.epoch_valid(1));
ks.rotate(key2);
assert!(ks.epoch_valid(1)); assert!(ks.epoch_valid(0)); assert!(ks.epoch_valid(2)); }
#[test]
fn test_key_state_multiple_rotations() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let key3 = Key::from_bytes([3u8; 32]);
let mut ks = KeyState::initial(key1.clone());
ks.rotate(key2.clone());
assert_eq!(ks.epoch(), 1);
assert_eq!(ks.previous_epoch(), Some(0));
ks.rotate(key3.clone());
assert_eq!(ks.epoch(), 2);
assert_eq!(ks.previous_epoch(), Some(1)); assert_eq!(ks.previous().unwrap().as_bytes(), key2.as_bytes());
}
#[test]
fn test_key_state_clear_previous() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let mut ks = KeyState::initial(key1);
ks.rotate(key2);
assert!(ks.previous().is_some());
ks.clear_previous();
assert!(ks.previous().is_none());
assert!(ks.previous_epoch().is_none());
assert_eq!(ks.epoch(), 1);
}
#[test]
fn test_kernel_state_rotate_key() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let mut ks = KernelState::initial(key1.clone(), PolicyState::deny_all());
assert_eq!(ks.key_epoch(), 0);
ks.rotate_key(key2.clone());
assert_eq!(ks.key_epoch(), 1);
assert_eq!(ks.hmac_key().as_bytes(), key2.as_bytes());
}
#[test]
fn test_kernel_dispatch_rotate_key() {
let key1 = Key::from_bytes([1u8; 32]);
let key2 = Key::from_bytes([2u8; 32]);
let core = KernelStateCore::empty(key1);
let result = kernel_dispatch(
core,
KernelRequest::RotateKey {
new_key: key2.clone(),
},
);
assert!(result.is_ok());
let new_core = result.unwrap();
assert_eq!(new_core.key_state().epoch(), 1);
assert_eq!(new_core.hmac_key().as_bytes(), key2.as_bytes());
}
#[test]
fn test_plugin_meta_grant_revoke_holds_empty() {
let mut pm = PluginMeta::empty();
assert!(!pm.holds_cap(0));
assert!(!pm.holds_cap(u128::MAX));
pm.revoke_cap(42); assert!(pm.held_caps.is_empty());
}
#[test]
fn test_plugin_meta_grant_sorted_order() {
let mut pm = PluginMeta::empty();
pm.grant_cap(5);
pm.grant_cap(1);
pm.grant_cap(9);
pm.grant_cap(3);
assert_eq!(pm.held_caps, vec![1, 3, 5, 9]);
}
#[test]
fn test_plugin_meta_grant_dedup() {
let mut pm = PluginMeta::empty();
pm.grant_cap(3);
pm.grant_cap(3);
pm.grant_cap(3);
assert_eq!(pm.held_caps, vec![3]);
}
#[test]
fn test_plugin_meta_revoke_first_middle_last() {
let mut pm = PluginMeta::empty();
pm.grant_cap(1);
pm.grant_cap(5);
pm.grant_cap(9);
pm.revoke_cap(1); assert_eq!(pm.held_caps, vec![5, 9]);
pm.grant_cap(1);
pm.revoke_cap(5); assert_eq!(pm.held_caps, vec![1, 9]);
pm.grant_cap(5);
pm.revoke_cap(9); assert_eq!(pm.held_caps, vec![1, 5]);
}
#[test]
fn test_set_plugin_meta_normalizes_unsorted_held_caps() {
let mut core = KernelStateCore::empty(Key::empty());
let meta = PluginMeta {
level: SecurityLevel::Internal,
held_caps: vec![9, 3, 5, 3, 1, 5], };
core.set_plugin_meta(100, meta);
let stored = core.get_plugin_meta(100);
assert_eq!(stored.held_caps, vec![1, 3, 5, 9]); }
#[test]
fn test_kernel_state_core_plugin_meta_upsert_edge_cases() {
let mut core = KernelStateCore::empty(Key::empty());
core.set_plugin_meta(
50,
PluginMeta {
level: SecurityLevel::Public,
held_caps: vec![],
},
);
assert_eq!(core.plugin_metas.len(), 1);
core.set_plugin_meta(
10,
PluginMeta {
level: SecurityLevel::Internal,
held_caps: vec![],
},
);
assert_eq!(core.plugin_metas[0].0, 10);
core.set_plugin_meta(
90,
PluginMeta {
level: SecurityLevel::Secret,
held_caps: vec![],
},
);
assert_eq!(core.plugin_metas[2].0, 90);
core.set_plugin_meta(
30,
PluginMeta {
level: SecurityLevel::Confidential,
held_caps: vec![],
},
);
let ids: Vec<u128> = core.plugin_metas.iter().map(|(id, _)| *id).collect();
assert_eq!(ids, vec![10, 30, 50, 90]);
core.set_plugin_meta(
50,
PluginMeta {
level: SecurityLevel::Secret,
held_caps: vec![1],
},
);
assert_eq!(core.get_plugin_meta(50).level, SecurityLevel::Secret);
assert_eq!(core.plugin_metas.len(), 4); }
#[test]
fn test_kernel_state_core_resource_meta_upsert_edge_cases() {
let mut core = KernelStateCore::empty(Key::empty());
core.set_resource_meta(
5,
ResourceMeta {
level: SecurityLevel::Public,
},
);
assert_eq!(core.resource_metas.len(), 1);
core.set_resource_meta(
1,
ResourceMeta {
level: SecurityLevel::Internal,
},
);
assert_eq!(core.resource_metas[0].0, 1);
core.set_resource_meta(
5,
ResourceMeta {
level: SecurityLevel::Secret,
},
);
assert_eq!(core.get_resource_meta(5).level, SecurityLevel::Secret);
assert_eq!(core.resource_metas.len(), 2);
}
#[test]
fn test_revocation_state_single_element() {
let mut rs = RevocationState::empty();
rs.insert_mut(make_test_cap(42, None)).unwrap();
assert!(rs.is_valid(42));
assert!(!rs.is_valid(41));
assert!(!rs.is_valid(43));
assert_eq!(rs.cap_count(), 1);
}
#[test]
fn test_revocation_state_insert_at_boundaries() {
let mut rs = RevocationState::empty();
rs.insert_mut(make_test_cap(100, None)).unwrap();
rs.insert_mut(make_test_cap(1, None)).unwrap();
rs.insert_mut(make_test_cap(50, None)).unwrap();
let ids = rs.cap_ids();
assert_eq!(ids, vec![1, 50, 100]);
}
}