Skip to main content

lion_core/state/
kernel.rs

1// Copyright (C) 2026 HaiyangLi
2// SPDX-License-Identifier: AGPL-3.0-or-later
3//! Lion State Kernel
4//!
5//! Corresponds to: Lion/State/Kernel.lean
6//!
7//! Kernel state for the trusted computing base, including revocation tracking.
8//!
9//! NOTE: Uses Vec<(CapId, Capability)> instead of BTreeMap.
10//! All operations maintain sorted order by CapId for deterministic behavior.
11
12use crate::types::{CapId, CapPayload, Capability, Key, PolicyState, SealedTag, Time};
13
14// ============================================================================
15// KEY STATE (HMAC Key Rotation)
16// ============================================================================
17//
18// Corresponds to Lean: `structure KeyState`
19//
20// Supports HMAC key rotation with grace period for in-flight capabilities.
21
22/// Key state for HMAC key rotation support.
23///
24/// Corresponds to Lean: `structure KeyState`
25///
26/// Design rationale:
27/// - `current`: The active key used for sealing new capabilities
28/// - `epoch`: Monotonically increasing counter identifying the current key
29/// - `previous`: Optional previous key for validating in-flight capabilities
30/// - `previous_epoch`: Epoch of the previous key
31///
32/// The grace period (keeping previous key) allows capabilities that were
33/// sealed just before rotation to still be verified.
34///
35/// SECURITY INVARIANT: epoch is monotonically increasing (never wraps in practice).
36#[derive(Debug, Clone)]
37pub struct KeyState {
38    /// Current active key for sealing
39    ///
40    /// Corresponds to Lean: `current : Key`
41    pub(crate) current: Key,
42
43    /// Current key epoch (monotonically increasing)
44    ///
45    /// Corresponds to Lean: `epoch : Nat`
46    pub(crate) epoch: u64,
47
48    /// Previous key (for grace period verification)
49    ///
50    /// Corresponds to Lean: `previous : Option Key`
51    pub(crate) previous: Option<Key>,
52
53    /// Previous key epoch
54    ///
55    /// Corresponds to Lean: `previousEpoch : Option Nat`
56    pub(crate) previous_epoch: Option<u64>,
57}
58
59impl KeyState {
60    /// Create initial key state with no previous key
61    ///
62    /// Corresponds to Lean: `def KeyState.initial`
63    pub fn initial(key: Key) -> Self {
64        KeyState {
65            current: key,
66            epoch: 0,
67            previous: None,
68            previous_epoch: None,
69        }
70    }
71
72    /// Create empty key state (for default/placeholder purposes)
73    ///
74    /// Corresponds to Lean: `def KeyState.empty`
75    pub fn empty() -> Self {
76        KeyState {
77            current: Key::empty(),
78            epoch: 0,
79            previous: None,
80            previous_epoch: None,
81        }
82    }
83
84    /// Rotate to a new key
85    ///
86    /// Corresponds to Lean: `def KeyState.rotate`
87    ///
88    /// The current key becomes the previous key for grace period verification.
89    /// The epoch is incremented.
90    ///
91    /// SECURITY: Only kernel can trigger rotation (not plugins).
92    /// Uses checked arithmetic -- epoch overflow is a critical error.
93    pub fn rotate(&mut self, new_key: Key) {
94        self.previous = Some(self.current.clone());
95        self.previous_epoch = Some(self.epoch);
96        self.current = new_key;
97        // In practice, u64 epoch overflow is unreachable (>18 quintillion rotations).
98        // We keep checked_add for proof-relevant correctness; panic is acceptable here
99        // because reaching u64::MAX key rotations is physically impossible.
100        self.epoch = self
101            .epoch
102            .checked_add(1)
103            .expect("key epoch overflow (unreachable in practice)");
104    }
105
106    /// Rotate to a new key (immutable version)
107    ///
108    /// Returns a new KeyState with the rotated key.
109    pub fn rotated(&self, new_key: Key) -> Self {
110        KeyState {
111            current: new_key,
112            epoch: self
113                .epoch
114                .checked_add(1)
115                .expect("key epoch overflow (unreachable in practice)"),
116            previous: Some(self.current.clone()),
117            previous_epoch: Some(self.epoch),
118        }
119    }
120
121    /// Get the current key
122    #[inline]
123    pub fn current(&self) -> &Key {
124        &self.current
125    }
126
127    /// Get the current epoch
128    #[inline]
129    pub fn epoch(&self) -> u64 {
130        self.epoch
131    }
132
133    /// Get the previous key (if available)
134    #[inline]
135    pub fn previous(&self) -> Option<&Key> {
136        self.previous.as_ref()
137    }
138
139    /// Get the previous epoch (if available)
140    #[inline]
141    pub fn previous_epoch(&self) -> Option<u64> {
142        self.previous_epoch
143    }
144
145    /// Check if epoch is current or within grace period
146    ///
147    /// Corresponds to Lean: `def KeyState.epochValid`
148    pub fn epoch_valid(&self, cap_epoch: u64) -> bool {
149        // Valid if epoch matches current or is newer
150        if cap_epoch >= self.epoch {
151            return true;
152        }
153        // Or matches previous (grace period)
154        match self.previous_epoch {
155            Some(prev_epoch) => cap_epoch >= prev_epoch,
156            None => false,
157        }
158    }
159
160    /// Verify a capability's seal against the key state
161    ///
162    /// Corresponds to Lean: `def KeyState.verify`
163    ///
164    /// Strategy:
165    /// 1. Try current key first
166    /// 2. Fall back to previous key for in-flight capabilities (grace period)
167    ///
168    /// Returns true if the capability verifies against either key.
169    pub fn verify_seal(&self, payload: &CapPayload, tag: &SealedTag) -> bool {
170        // Try current key
171        if crate::crypto::verify_seal(&self.current, payload, tag) {
172            return true;
173        }
174        // Fall back to previous key if available
175        match &self.previous {
176            Some(prev_key) => crate::crypto::verify_seal(prev_key, payload, tag),
177            None => false,
178        }
179    }
180
181    /// Combined verification: seal AND epoch valid
182    ///
183    /// Corresponds to Lean: `def KeyState.verifyWithEpoch`
184    pub fn verify_with_epoch(&self, payload: &CapPayload, tag: &SealedTag, cap_epoch: u64) -> bool {
185        self.epoch_valid(cap_epoch) && self.verify_seal(payload, tag)
186    }
187
188    /// Clear the previous key (end grace period)
189    ///
190    /// Call this after sufficient time has passed since rotation
191    /// to clear the previous key from memory.
192    pub fn clear_previous(&mut self) {
193        self.previous = None;
194        self.previous_epoch = None;
195    }
196}
197
198impl Default for KeyState {
199    fn default() -> Self {
200        KeyState::empty()
201    }
202}
203
204/// Capability table in kernel
205///
206/// Corresponds to Lean: `abbrev CapTable := Std.HashMap CapId Capability`
207/// INVARIANT: Always sorted by CapId, no duplicate IDs.
208pub type CapTable = Vec<(CapId, Capability)>;
209
210/// Error type for kernel operations
211#[derive(Debug, Clone, PartialEq, Eq)]
212pub enum KernelError {
213    /// Capability not found in the table
214    CapNotFound(CapId),
215    /// Capability ID exhausted (overflow)
216    CapIdExhausted,
217    /// Capability ID collision during insert
218    CapIdCollision(CapId),
219    /// Invalid capability state
220    InvalidCapability(String),
221    /// Counter overflow (time or epoch would exceed u64::MAX)
222    CounterOverflow(&'static str),
223}
224
225impl std::fmt::Display for KernelError {
226    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227        match self {
228            KernelError::CapNotFound(id) => write!(f, "Capability {id} not found"),
229            KernelError::CapIdExhausted => write!(f, "Capability ID space exhausted"),
230            KernelError::CapIdCollision(id) => write!(f, "Capability ID {id} already exists"),
231            KernelError::InvalidCapability(msg) => write!(f, "Invalid capability: {msg}"),
232            KernelError::CounterOverflow(name) => write!(f, "{name} counter overflow"),
233        }
234    }
235}
236
237impl std::error::Error for KernelError {}
238
239/// Children index for O(log n) revocation: maps parent_id -> list of child cap_ids
240///
241/// Corresponds to Lean: `abbrev ChildrenIndex := Std.HashMap CapId (List CapId)`
242/// INVARIANT: Always sorted by parent CapId, no duplicate parent IDs.
243pub type ChildrenIndex = Vec<(CapId, Vec<CapId>)>;
244
245/// Revocation epoch tracking
246///
247/// Corresponds to Lean: `structure RevocationState`
248///
249/// INVARIANTS:
250/// - caps is sorted by CapId, no duplicate IDs
251/// - `caps[i].0 == caps[i].1.id()` for all entries (keys match cap IDs)
252/// - Parent IDs are always less than child IDs (ParentLt)
253/// - Valid capabilities have valid parents (ValidParentPresent)
254/// - children_index is consistent with parent relationships in caps (ChildrenIndexConsistent)
255/// - children_index is complete: every cap with a parent is in the index (ChildrenIndexComplete)
256#[derive(Debug, Clone)]
257pub struct RevocationState {
258    /// Capability table (sorted Vec)
259    ///
260    /// Corresponds to Lean: `caps : CapTable`
261    /// INVARIANT: Sorted by CapId, no duplicates
262    pub(crate) caps: CapTable,
263
264    /// Global revocation epoch
265    ///
266    /// Corresponds to Lean: `epoch : Nat`
267    pub(crate) epoch: u64,
268
269    /// Reverse index for efficient revocation: parent_id -> child cap_ids
270    ///
271    /// Corresponds to Lean: `childrenIndex : ChildrenIndex`
272    /// INVARIANT: Sorted by parent CapId, children lists may be unsorted
273    pub(crate) children_index: ChildrenIndex,
274}
275
276impl RevocationState {
277    /// Create empty revocation state
278    ///
279    /// Corresponds to Lean: `def RevocationState.empty : RevocationState`
280    pub fn empty() -> Self {
281        RevocationState {
282            caps: Vec::new(),
283            epoch: 0,
284            children_index: Vec::new(),
285        }
286    }
287
288    /// Helper: Find index of capability by ID using binary search
289    ///
290    /// Production version: uses binary_search_by_key with closure
291    fn find_index(&self, cap_id: CapId) -> Option<usize> {
292        self.caps
293            .binary_search_by_key(&cap_id, |entry| entry.0)
294            .ok()
295    }
296
297    /// Helper: Find index of capability by ID using binary search
298    ///
299
300    /// Helper: Find insertion point for sorted insert using binary search
301    ///
302    /// Production version: uses binary_search_by_key with closure
303    fn find_insert_pos(&self, cap_id: CapId) -> (usize, bool) {
304        match self.caps.binary_search_by_key(&cap_id, |entry| entry.0) {
305            Ok(pos) => (pos, true),
306            Err(pos) => (pos, false),
307        }
308    }
309
310    /// Helper: Find insertion point for sorted insert using binary search
311    ///
312
313    /// Check if capability exists and is valid
314    ///
315    /// Corresponds to Lean: `def RevocationState.is_valid`
316    pub fn is_valid(&self, cap_id: CapId) -> bool {
317        self.find_index(cap_id)
318            .is_some_and(|idx| self.caps[idx].1.is_valid())
319    }
320
321    /// Get a capability by ID
322    pub fn get(&self, cap_id: CapId) -> Option<&Capability> {
323        self.find_index(cap_id).map(|idx| &self.caps[idx].1)
324    }
325
326    /// Get a mutable reference to a capability by ID
327    #[allow(dead_code)] // Lean correspondence
328    pub(crate) fn get_mut(&mut self, cap_id: CapId) -> Option<&mut Capability> {
329        self.find_index(cap_id).map(|idx| &mut self.caps[idx].1)
330    }
331
332    /// Check if the table contains a capability
333    #[inline]
334    pub fn contains(&self, cap_id: CapId) -> bool {
335        self.find_index(cap_id).is_some()
336    }
337
338    /// Revoke capability by ID (single cap only)
339    ///
340    /// Corresponds to Lean: `def RevocationState.revoke`
341    ///
342    /// Returns the new state with the capability revoked.
343    /// If the capability doesn't exist, returns the state unchanged.
344    pub fn revoke(&self, cap_id: CapId) -> Self {
345        let new_caps = self
346            .caps
347            .iter()
348            .map(|(id, cap)| {
349                if *id == cap_id {
350                    let mut revoked_cap = cap.clone();
351                    revoked_cap.revoke();
352                    (*id, revoked_cap)
353                } else {
354                    (*id, cap.clone())
355                }
356            })
357            .collect();
358        RevocationState {
359            caps: new_caps,
360            epoch: self.epoch,
361            children_index: self.children_index.clone(),
362        }
363    }
364
365    /// Revoke capability by ID (mutating version)
366    ///
367    /// # Errors
368    ///
369    /// Returns `KernelError::CapNotFound` if the capability ID is not in the table.
370    pub fn revoke_mut(&mut self, cap_id: CapId) -> Result<(), KernelError> {
371        match self.find_index(cap_id) {
372            Some(idx) => {
373                self.caps[idx].1.revoke();
374                Ok(())
375            }
376            None => Err(KernelError::CapNotFound(cap_id)),
377        }
378    }
379
380    /// Check if ancestorId is in the parent chain of capId
381    ///
382    /// Corresponds to Lean: `def RevocationState.has_ancestor`
383    ///
384    /// Uses a fuel parameter for termination (ParentLt guarantees termination with fuel = capId).
385    pub fn has_ancestor(&self, cap_id: CapId, ancestor_id: CapId) -> bool {
386        self.has_ancestor_with_fuel(cap_id, ancestor_id, cap_id)
387    }
388
389    /// Internal: has_ancestor with explicit fuel for termination
390    fn has_ancestor_with_fuel(&self, cap_id: CapId, ancestor_id: CapId, fuel: u128) -> bool {
391        if fuel == 0 {
392            return false;
393        }
394
395        match self.get(cap_id) {
396            None => false,
397            Some(cap) => match cap.parent() {
398                None => false,
399                Some(parent_id) => {
400                    if parent_id == ancestor_id {
401                        true
402                    } else {
403                        self.has_ancestor_with_fuel(parent_id, ancestor_id, fuel - 1)
404                    }
405                }
406            },
407        }
408    }
409
410    /// Check if cap_id is in the revoke set (id == cap_id OR has cap_id as ancestor)
411    ///
412    /// Corresponds to Lean: `def RevocationState.in_revoke_set`
413    pub fn in_revoke_set(&self, k: CapId, cap_id: CapId) -> bool {
414        k == cap_id || self.has_ancestor(k, cap_id)
415    }
416
417    /// Revoke capability and all descendants transitively
418    ///
419    /// Corresponds to Lean: `def RevocationState.revoke_transitive`
420    ///
421    /// Sets valid=false on capId and any cap whose parent chain includes capId.
422    pub fn revoke_transitive(&self, cap_id: CapId) -> Self {
423        let new_caps = self
424            .caps
425            .iter()
426            .map(|(k, cap)| {
427                if self.in_revoke_set(*k, cap_id) {
428                    let mut revoked = cap.clone();
429                    revoked.revoke();
430                    (*k, revoked)
431                } else {
432                    (*k, cap.clone())
433                }
434            })
435            .collect();
436        RevocationState {
437            caps: new_caps,
438            epoch: self.epoch,
439            children_index: self.children_index.clone(),
440        }
441    }
442
443    /// Revoke capability and all descendants transitively (mutating version)
444    pub fn revoke_transitive_mut(&mut self, cap_id: CapId) {
445        // Collect indices to revoke (borrow conflict resolved by iterating caps directly)
446        let mut to_revoke = Vec::with_capacity(self.caps.len());
447        to_revoke.extend(
448            self.caps
449                .iter()
450                .enumerate()
451                .filter_map(|(i, (id, _))| self.in_revoke_set(*id, cap_id).then_some(i)),
452        );
453
454        // Then revoke them
455        for idx in to_revoke {
456            self.caps[idx].1.revoke();
457        }
458    }
459
460    /// Insert a capability (returns error on collision)
461    ///
462    /// Corresponds to Lean: `def RevocationState.insert`
463    ///
464    /// SECURITY: This function MUST check for ID collision to maintain
465    /// the uniqueness invariant and prevent capability forgery via replacement.
466    ///
467    /// Also maintains children_index for O(log n) revocation.
468    ///
469    /// # Errors
470    ///
471    /// Returns `KernelError::CapIdCollision` if a capability with the same ID already exists.
472    pub fn insert(&self, cap: Capability) -> Result<Self, KernelError> {
473        let id = cap.id();
474        let parent = cap.parent();
475        let (pos, exists) = self.find_insert_pos(id);
476
477        if exists {
478            return Err(KernelError::CapIdCollision(id));
479        }
480
481        // Build new caps with insertion at position
482        let mut new_caps = Vec::with_capacity(self.caps.len() + 1);
483        new_caps.extend_from_slice(&self.caps[..pos]);
484        new_caps.push((id, cap));
485        new_caps.extend_from_slice(&self.caps[pos..]);
486
487        // Update children_index if cap has a parent
488        let mut new_index = self.children_index.clone();
489        if let Some(parent_id) = parent {
490            Self::add_child_to_index(&mut new_index, parent_id, id);
491        }
492
493        Ok(RevocationState {
494            caps: new_caps,
495            epoch: self.epoch,
496            children_index: new_index,
497        })
498    }
499
500    /// Insert a capability (mutating version)
501    ///
502    /// Also maintains children_index for O(log n) revocation.
503    ///
504    /// # Errors
505    ///
506    /// Returns `KernelError::CapIdCollision` if a capability with the same ID already exists.
507    pub fn insert_mut(&mut self, cap: Capability) -> Result<(), KernelError> {
508        let id = cap.id();
509        let parent = cap.parent();
510        let (pos, exists) = self.find_insert_pos(id);
511
512        if exists {
513            return Err(KernelError::CapIdCollision(id));
514        }
515
516        self.caps.insert(pos, (id, cap));
517
518        // Update children_index if cap has a parent
519        if let Some(parent_id) = parent {
520            Self::add_child_to_index(&mut self.children_index, parent_id, id);
521        }
522
523        Ok(())
524    }
525
526    /// Collect all descendants of cap_id using children_index (BFS traversal)
527    ///
528    /// Corresponds to Lean: `def RevocationState.collectDescendants`
529    ///
530    /// Returns the set of all cap IDs that should be revoked (cap_id + all descendants).
531    ///
532    /// SECURITY: No fuel/iteration limit. The graph is finite and `visited`
533    /// (BTreeSet) guarantees termination by preventing re-visiting nodes.
534    /// Removing the previous 10,000-step hard cap prevents silent truncation
535    /// of the revocation set, which was a security bug: a delegation tree
536    /// with >10,000 reachable nodes would leave descendants valid after revocation.
537    fn collect_descendants(&self, cap_id: CapId) -> Vec<CapId> {
538        let mut queue: Vec<CapId> = Vec::with_capacity(self.caps.len().saturating_add(1));
539        let mut head: usize = 0;
540        let mut visited = std::collections::BTreeSet::new();
541        let mut collected: Vec<CapId> = Vec::new();
542
543        queue.push(cap_id);
544
545        while head < queue.len() {
546            let id = queue[head];
547            head += 1;
548
549            if !visited.insert(id) {
550                continue;
551            }
552
553            collected.push(id);
554            queue.extend(self.get_children(id).iter().copied());
555        }
556
557        collected
558    }
559
560    /// Get children of a capability from the children_index
561    ///
562    /// Production version: uses binary_search_by_key with closure
563    fn get_children(&self, parent_id: CapId) -> &[CapId] {
564        match self
565            .children_index
566            .binary_search_by_key(&parent_id, |entry| entry.0)
567        {
568            Ok(idx) => &self.children_index[idx].1,
569            Err(_) => &[],
570        }
571    }
572
573    /// Get children of a capability from the children_index
574    ///
575
576    /// Add a child to the children_index (maintains sorted order)
577    ///
578    /// Production version: uses binary_search and Vec::insert for efficiency
579    fn add_child_to_index(index: &mut ChildrenIndex, parent_id: CapId, child_id: CapId) {
580        match index.binary_search_by_key(&parent_id, |entry| entry.0) {
581            Ok(idx) => {
582                index[idx].1.push(child_id);
583            }
584            Err(pos) => {
585                index.insert(pos, (parent_id, vec![child_id]));
586            }
587        }
588    }
589
590    /// Add a child to the children_index (maintains sorted order)
591    ///
592    /// Rebuilds the index using only Vec::new/push/len and array indexing.
593    /// Semantically identical to the production version above.
594
595    /// Revoke capability and all descendants transitively (O(k) optimized)
596    ///
597    /// Corresponds to Lean: `def RevocationState.revoke_transitive_fast`
598    ///
599    /// Uses children_index for O(k) traversal where k = number of descendants.
600    pub fn revoke_transitive_fast(&self, cap_id: CapId) -> Self {
601        let mut to_revoke = self.collect_descendants(cap_id);
602        to_revoke.sort_unstable();
603
604        let new_caps = self
605            .caps
606            .iter()
607            .map(|(k, cap)| {
608                if to_revoke.binary_search(k).is_ok() {
609                    let mut revoked = cap.clone();
610                    revoked.revoke();
611                    (*k, revoked)
612                } else {
613                    (*k, cap.clone())
614                }
615            })
616            .collect();
617
618        RevocationState {
619            caps: new_caps,
620            epoch: self.epoch,
621            children_index: self.children_index.clone(),
622        }
623    }
624
625    /// Revoke capability and all descendants transitively (O(k) optimized, mutating)
626    ///
627    /// # Errors
628    ///
629    /// Returns `KernelError::CapNotFound` if the capability does not exist.
630    pub fn revoke_transitive_fast_mut(&mut self, cap_id: CapId) -> Result<(), KernelError> {
631        if self.find_index(cap_id).is_none() {
632            return Err(KernelError::CapNotFound(cap_id));
633        }
634
635        let mut to_revoke = self.collect_descendants(cap_id);
636        to_revoke.sort_unstable();
637
638        for id in to_revoke {
639            if let Some(idx) = self.find_index(id) {
640                self.caps[idx].1.revoke();
641            }
642        }
643
644        Ok(())
645    }
646
647    /// Get the current revocation epoch
648    #[inline]
649    pub fn epoch(&self) -> u64 {
650        self.epoch
651    }
652
653    /// Get the number of capabilities
654    #[inline]
655    pub fn cap_count(&self) -> usize {
656        self.caps.len()
657    }
658
659    /// Get all valid capabilities
660    pub fn valid_caps(&self) -> Vec<(CapId, Capability)> {
661        // Pre-allocate for worst case (all valid); shrink is free for Vec
662        let mut result = Vec::with_capacity(self.caps.len());
663        let mut i = 0;
664        while i < self.caps.len() {
665            if self.caps[i].1.is_valid() {
666                result.push((self.caps[i].0, self.caps[i].1.clone()));
667            }
668            i += 1;
669        }
670        result
671    }
672
673    /// Get all capability IDs
674    pub fn cap_ids(&self) -> Vec<CapId> {
675        let mut ids = Vec::with_capacity(self.caps.len());
676        let mut i = 0;
677        while i < self.caps.len() {
678            ids.push(self.caps[i].0);
679            i += 1;
680        }
681        ids
682    }
683
684    /// Iterate over all capabilities
685    ///
686    /// Returns a slice iterator over (CapId, Capability) pairs.
687    /// This provides compatibility with code that uses .iter() on the caps.
688    #[inline]
689    pub fn iter(&self) -> std::slice::Iter<'_, (CapId, Capability)> {
690        self.caps.iter()
691    }
692
693    /// Check if any valid capability targets the given resource
694    ///
695    /// SECURITY: This check is CRITICAL for apply_free.
696    /// If any valid capability targets an address, freeing that address
697    /// would create a USE-AFTER-FREE vulnerability (dangling capability).
698    ///
699    /// Corresponds to Lean: Memory safety guard in `apply_free`
700    pub fn any_valid_targeting(&self, target: crate::types::ResourceId) -> bool {
701        self.caps
702            .iter()
703            .any(|(_, cap)| cap.is_valid() && cap.target() == target)
704    }
705}
706
707impl Default for RevocationState {
708    fn default() -> Self {
709        RevocationState::empty()
710    }
711}
712
713/// Complete kernel state (TCB)
714///
715/// Corresponds to Lean: `structure KernelState`
716#[derive(Debug, Clone)]
717pub struct KernelState {
718    /// Key state for HMAC sealing with rotation support
719    ///
720    /// Corresponds to Lean: `keyState : KeyState`
721    pub(crate) key_state: KeyState,
722
723    /// Policy state for access control
724    ///
725    /// Corresponds to Lean: `policy : PolicyState`
726    pub(crate) policy: PolicyState,
727
728    /// Revocation tracking
729    ///
730    /// Corresponds to Lean: `revocation : RevocationState`
731    pub(crate) revocation: RevocationState,
732
733    /// Current logical time
734    ///
735    /// Corresponds to Lean: `now : Time`
736    pub(crate) now: Time,
737
738    /// Next capability ID for deterministic allocation
739    ///
740    /// Corresponds to Lean: `nextCapId : CapId`
741    ///
742    /// INVARIANT (FreshIdInvariant):
743    /// - All existing cap IDs are < next_cap_id
744    /// - Allocating a new cap increments this counter
745    /// - This enables constructive proof that fresh IDs exist
746    pub(crate) next_cap_id: CapId,
747}
748
749impl KernelState {
750    /// Create initial kernel state
751    ///
752    /// Corresponds to Lean: `def KernelState.initial (key : Key) (pol : PolicyState) : KernelState`
753    pub fn initial(key: Key, policy: PolicyState) -> Self {
754        KernelState {
755            key_state: KeyState::initial(key),
756            policy,
757            revocation: RevocationState::empty(),
758            now: 0,
759            next_cap_id: 0,
760        }
761    }
762
763    /// Advance kernel time (immutable, checked)
764    ///
765    /// Corresponds to Lean: `def KernelState.tick (ks : KernelState) : KernelState`
766    ///
767    /// # Errors
768    ///
769    /// Returns `KernelError::CounterOverflow` if the time counter would exceed u64::MAX.
770    pub fn tick(&self) -> Result<Self, KernelError> {
771        let new_now = self
772            .now
773            .checked_add(1)
774            .ok_or(KernelError::CounterOverflow("time"))?;
775        Ok(KernelState {
776            key_state: self.key_state.clone(),
777            policy: self.policy.clone(),
778            revocation: self.revocation.clone(),
779            now: new_now,
780            next_cap_id: self.next_cap_id,
781        })
782    }
783
784    /// Rotate HMAC key (kernel-only operation)
785    ///
786    /// Corresponds to Lean: `def KernelState.rotateKey`
787    ///
788    /// The current key becomes the previous key for grace period verification.
789    /// The epoch is incremented.
790    ///
791    /// SECURITY: This operation should only be callable by privileged kernel code.
792    pub fn rotate_key(&mut self, new_key: Key) {
793        self.key_state.rotate(new_key);
794    }
795
796    /// Rotate HMAC key (immutable version)
797    pub fn with_rotated_key(&self, new_key: Key) -> Self {
798        KernelState {
799            key_state: self.key_state.rotated(new_key),
800            policy: self.policy.clone(),
801            revocation: self.revocation.clone(),
802            now: self.now,
803            next_cap_id: self.next_cap_id,
804        }
805    }
806
807    /// Get the current key epoch
808    #[inline]
809    pub fn key_epoch(&self) -> u64 {
810        self.key_state.epoch()
811    }
812
813    /// Get a reference to the key state
814    #[inline]
815    pub fn key_state(&self) -> &KeyState {
816        &self.key_state
817    }
818
819    /// Get a mutable reference to the key state (kernel-internal only)
820    #[inline]
821    #[allow(dead_code)] // Lean correspondence
822    pub(crate) fn key_state_mut(&mut self) -> &mut KeyState {
823        &mut self.key_state
824    }
825
826    /// Verify capability seal using key state (supports rotation)
827    ///
828    /// Corresponds to Lean: `def KernelState.verify_cap_seal`
829    pub fn verify_cap_seal(&self, cap: &Capability) -> bool {
830        self.key_state
831            .verify_with_epoch(&cap.payload(), cap.signature(), cap.epoch())
832    }
833
834    /// Allocate a fresh capability ID
835    ///
836    /// Corresponds to Lean: `def KernelState.alloc_cap_id`
837    ///
838    /// INVARIANT: The returned ID is guaranteed fresh (not in use).
839    ///
840    /// # Errors
841    ///
842    /// Returns `KernelError::CapIdExhausted` if the capability ID counter would overflow.
843    pub fn alloc_cap_id(&mut self) -> Result<CapId, KernelError> {
844        let id = self.next_cap_id;
845        self.next_cap_id = match self.next_cap_id.checked_add(1) {
846            Some(v) => v,
847            None => return Err(KernelError::CapIdExhausted),
848        };
849        Ok(id)
850    }
851
852    /// Get the next capability ID (without allocating)
853    #[inline]
854    pub fn next_cap_id(&self) -> CapId {
855        self.next_cap_id
856    }
857
858    /// Advance kernel time (mutating, checked)
859    ///
860    /// # Errors
861    ///
862    /// Returns `KernelError::CounterOverflow` if the time counter would exceed u64::MAX.
863    pub fn tick_checked(&mut self) -> Result<(), KernelError> {
864        self.now = self
865            .now
866            .checked_add(1)
867            .ok_or(KernelError::CounterOverflow("time"))?;
868        Ok(())
869    }
870
871    /// Check if capability is not revoked
872    ///
873    /// Corresponds to Lean: `def KernelState.cap_not_revoked`
874    pub fn cap_not_revoked(&self, cap: &Capability) -> bool {
875        self.revocation.is_valid(cap.id())
876    }
877
878    /// Get the current time
879    #[inline]
880    pub fn now(&self) -> Time {
881        self.now
882    }
883
884    /// Get a reference to the revocation state
885    #[inline]
886    pub fn revocation(&self) -> &RevocationState {
887        &self.revocation
888    }
889
890    /// Get a mutable reference to the revocation state
891    #[inline]
892    pub(crate) fn revocation_mut(&mut self) -> &mut RevocationState {
893        &mut self.revocation
894    }
895
896    /// Get a reference to the policy state
897    #[inline]
898    pub fn policy(&self) -> &PolicyState {
899        &self.policy
900    }
901
902    /// Get a mutable reference to the policy state
903    #[inline]
904    #[allow(dead_code)] // Lean correspondence
905    pub(crate) fn policy_mut(&mut self) -> &mut PolicyState {
906        &mut self.policy
907    }
908
909    /// Get a reference to the current HMAC key (kernel-internal only)
910    ///
911    /// Backward compatibility: returns the current key from key_state
912    #[inline]
913    #[allow(dead_code)] // Lean correspondence
914    pub(crate) fn hmac_key(&self) -> &Key {
915        self.key_state.current()
916    }
917}
918
919impl Default for KernelState {
920    /// Default kernel state: empty key, deny-all policy, no capabilities, time=0, next_cap_id=0
921    fn default() -> Self {
922        KernelState {
923            key_state: KeyState::empty(),
924            policy: PolicyState::default(),
925            revocation: RevocationState::empty(),
926            now: 0,
927            next_cap_id: 0,
928        }
929    }
930}
931
932// ============================================================================
933// KERNEL STATE CORE
934// ============================================================================
935//
936// These types form the security-critical kernel state that can be machine-verified.
937// PolicyState remains outside KernelStateCore so policy evaluation stays at the
938// shell boundary, but it is now enum-backed and extractable separately.
939//
940// Corresponds to Lean: `Lion/State/KernelStateCore.lean`
941
942use crate::types::{PluginId, ResourceId, SecurityLevel};
943
944/// Plugin security metadata (extractable)
945///
946/// Corresponds to Lean: `structure PluginMeta`
947///
948/// INVARIANT: held_caps is sorted, no duplicates
949#[derive(Debug, Clone, PartialEq, Eq)]
950pub struct PluginMeta {
951    /// Security level of this plugin
952    pub level: SecurityLevel,
953    /// Capabilities held by this plugin (sorted Vec)
954    pub held_caps: Vec<CapId>,
955}
956
957impl PluginMeta {
958    /// Create empty plugin metadata (Public level, no capabilities)
959    ///
960    /// Corresponds to Lean: `def PluginMeta.empty`
961    pub fn empty() -> Self {
962        PluginMeta {
963            level: SecurityLevel::Public,
964            held_caps: Vec::new(),
965        }
966    }
967
968    /// Grant a capability to this plugin (maintains sorted order)
969    ///
970    /// Corresponds to Lean: `def PluginMeta.grant_cap`
971    /// Production version: uses binary_search for efficiency
972    pub fn grant_cap(&mut self, cap_id: CapId) {
973        match self.held_caps.binary_search(&cap_id) {
974            Ok(_) => {} // Already present
975            Err(pos) => self.held_caps.insert(pos, cap_id),
976        }
977    }
978
979    /// Corresponds to Lean: `def PluginMeta.grant_cap`
980    /// Uses while-index loops only.
981
982    /// Revoke a capability from this plugin
983    ///
984    /// Corresponds to Lean: `def PluginMeta.revoke_cap`
985    pub fn revoke_cap(&mut self, cap_id: CapId) {
986        if let Ok(pos) = self.held_caps.binary_search(&cap_id) {
987            self.held_caps.remove(pos);
988        }
989    }
990
991    /// Check if plugin holds a capability
992    ///
993    /// Corresponds to Lean: `def PluginMeta.holds_cap`
994    pub fn holds_cap(&self, cap_id: CapId) -> bool {
995        self.held_caps.binary_search(&cap_id).is_ok()
996    }
997}
998
999impl Default for PluginMeta {
1000    fn default() -> Self {
1001        PluginMeta::empty()
1002    }
1003}
1004
1005/// Resource security metadata (extractable)
1006///
1007/// Corresponds to Lean: `structure ResourceMeta`
1008#[derive(Debug, Clone, PartialEq, Eq)]
1009pub struct ResourceMeta {
1010    /// Security level of this resource
1011    pub level: SecurityLevel,
1012}
1013
1014impl ResourceMeta {
1015    /// Create empty resource metadata (Public level)
1016    ///
1017    /// Corresponds to Lean: `def ResourceMeta.empty`
1018    pub fn empty() -> Self {
1019        ResourceMeta {
1020            level: SecurityLevel::Public,
1021        }
1022    }
1023}
1024
1025impl Default for ResourceMeta {
1026    fn default() -> Self {
1027        ResourceMeta::empty()
1028    }
1029}
1030
1031/// Extractable kernel state core
1032///
1033/// Contains only security-critical state that can be machine-verified.
1034/// PolicyState remains outside this core; policy evaluation happens in the shell.
1035///
1036/// Corresponds to Lean: `structure KernelStateCore`
1037///
1038/// INVARIANTS:
1039/// - plugin_metas is sorted by PluginId, unique keys
1040/// - resource_metas is sorted by ResourceId, unique keys
1041#[derive(Debug, Clone)]
1042pub struct KernelStateCore {
1043    /// Key state for capability sealing (with rotation support)
1044    pub(crate) key_state: KeyState,
1045    /// Capability table with revocation tracking (also owns the revocation epoch)
1046    pub(crate) revocation: RevocationState,
1047    /// Plugin security metadata (sorted Vec)
1048    pub(crate) plugin_metas: Vec<(PluginId, PluginMeta)>,
1049    /// Resource security metadata (sorted Vec)
1050    pub(crate) resource_metas: Vec<(ResourceId, ResourceMeta)>,
1051    /// Current logical time
1052    pub(crate) now: Time,
1053    /// Next capability ID for allocation
1054    #[allow(dead_code)] // Lean correspondence
1055    pub(crate) next_cap_id: CapId,
1056}
1057
1058impl KernelStateCore {
1059    /// Create initial kernel state core
1060    ///
1061    /// Corresponds to Lean: `Inhabited KernelStateCore`
1062    pub fn empty(hmac_key: Key) -> Self {
1063        KernelStateCore {
1064            key_state: KeyState::initial(hmac_key),
1065            revocation: RevocationState::empty(),
1066            plugin_metas: Vec::new(),
1067            resource_metas: Vec::new(),
1068            now: 0,
1069            next_cap_id: 0,
1070        }
1071    }
1072
1073    /// Get a reference to the current HMAC key
1074    ///
1075    /// Backward compatibility accessor
1076    #[inline]
1077    pub fn hmac_key(&self) -> &Key {
1078        self.key_state.current()
1079    }
1080
1081    /// Get a reference to the key state
1082    #[inline]
1083    pub fn key_state(&self) -> &KeyState {
1084        &self.key_state
1085    }
1086
1087    /// Rotate HMAC key (kernel-only operation)
1088    pub fn rotate_key(&mut self, new_key: Key) {
1089        self.key_state.rotate(new_key);
1090    }
1091
1092    /// Verify capability seal using key state (supports rotation)
1093    pub fn verify_cap_seal(&self, cap: &Capability) -> bool {
1094        self.key_state
1095            .verify_with_epoch(&cap.payload(), cap.signature(), cap.epoch())
1096    }
1097
1098    /// Get plugin metadata (returns default if not found)
1099    ///
1100    /// Production version: uses binary_search_by_key with closure
1101    pub fn get_plugin_meta(&self, pid: PluginId) -> PluginMeta {
1102        match self
1103            .plugin_metas
1104            .binary_search_by_key(&pid, |entry| entry.0)
1105        {
1106            Ok(idx) => self.plugin_metas[idx].1.clone(),
1107            Err(_) => PluginMeta::empty(),
1108        }
1109    }
1110
1111    /// Get plugin metadata (returns default if not found)
1112    ///
1113
1114    /// Set plugin metadata (upsert)
1115    ///
1116    /// Production version: uses binary_search for O(log n) lookup.
1117    /// Normalizes held_caps (sort + dedup) to maintain sorted invariant.
1118    pub fn set_plugin_meta(&mut self, pid: PluginId, mut meta: PluginMeta) {
1119        meta.held_caps.sort_unstable();
1120        meta.held_caps.dedup();
1121        match self
1122            .plugin_metas
1123            .binary_search_by_key(&pid, |entry| entry.0)
1124        {
1125            Ok(idx) => {
1126                self.plugin_metas[idx].1 = meta;
1127            }
1128            Err(pos) => {
1129                self.plugin_metas.insert(pos, (pid, meta));
1130            }
1131        }
1132    }
1133
1134    /// rebuilds the vec manually. Normalizes held_caps (sort + dedup).
1135
1136    /// Get resource metadata (returns default if not found)
1137    ///
1138    /// Production version: uses binary_search_by_key with closure
1139    pub fn get_resource_meta(&self, rid: ResourceId) -> ResourceMeta {
1140        match self
1141            .resource_metas
1142            .binary_search_by_key(&rid, |entry| entry.0)
1143        {
1144            Ok(idx) => self.resource_metas[idx].1.clone(),
1145            Err(_) => ResourceMeta::empty(),
1146        }
1147    }
1148
1149    /// Get resource metadata (returns default if not found)
1150    ///
1151
1152    /// Set resource metadata (upsert)
1153    ///
1154    /// Production version: uses binary_search for O(log n) lookup
1155    pub fn set_resource_meta(&mut self, rid: ResourceId, meta: ResourceMeta) {
1156        match self
1157            .resource_metas
1158            .binary_search_by_key(&rid, |entry| entry.0)
1159        {
1160            Ok(idx) => {
1161                self.resource_metas[idx].1 = meta;
1162            }
1163            Err(pos) => {
1164                self.resource_metas.insert(pos, (rid, meta));
1165            }
1166        }
1167    }
1168
1169    /// rebuilds the vec manually
1170
1171    /// Get plugin security level
1172    ///
1173    /// Corresponds to Lean: `def KernelStateCore.plugin_level`
1174    pub fn plugin_level(&self, pid: PluginId) -> SecurityLevel {
1175        self.get_plugin_meta(pid).level
1176    }
1177
1178    /// Get resource security level
1179    ///
1180    /// Corresponds to Lean: `def KernelStateCore.resource_level`
1181    pub fn resource_level(&self, rid: ResourceId) -> SecurityLevel {
1182        self.get_resource_meta(rid).level
1183    }
1184
1185    /// Check if plugin holds capability
1186    ///
1187    /// Corresponds to Lean: `def KernelStateCore.plugin_holds`
1188    pub fn plugin_holds(&self, pid: PluginId, cap_id: CapId) -> bool {
1189        self.plugin_metas
1190            .binary_search_by_key(&pid, |e| e.0)
1191            .ok()
1192            .is_some_and(|i| self.plugin_metas[i].1.holds_cap(cap_id))
1193    }
1194
1195    /// Check if capability is valid
1196    ///
1197    /// Corresponds to Lean: `def KernelStateCore.cap_is_valid`
1198    pub fn cap_is_valid(&self, cap_id: CapId) -> bool {
1199        self.revocation.is_valid(cap_id)
1200    }
1201
1202    /// Get current time
1203    pub fn now(&self) -> Time {
1204        self.now
1205    }
1206
1207    /// Get current revocation epoch (delegates to revocation state -- single source of truth)
1208    pub fn epoch(&self) -> u64 {
1209        self.revocation.epoch()
1210    }
1211}
1212
1213impl Default for KernelStateCore {
1214    fn default() -> Self {
1215        KernelStateCore::empty(Key::empty())
1216    }
1217}
1218
1219/// Kernel dispatch request (extractable)
1220///
1221/// All security-critical mutations go through this enum.
1222/// Policy evaluation happens in the shell BEFORE dispatch.
1223///
1224/// Corresponds to Lean: `inductive KernelRequest`
1225#[derive(Debug, Clone)]
1226pub enum KernelRequest {
1227    /// Delegate a capability to a plugin
1228    /// Pre-condition: Policy already approved by shell
1229    CapDelegate {
1230        /// The new capability to delegate
1231        new_cap: Capability,
1232        /// The plugin receiving the capability
1233        target: PluginId,
1234    },
1235    /// Revoke a capability (and descendants)
1236    CapRevoke {
1237        /// The capability ID to revoke
1238        cap_id: CapId,
1239    },
1240    /// Update plugin security label
1241    SetPluginLevel {
1242        /// The plugin whose level is being changed
1243        plugin_id: PluginId,
1244        /// The new security level to assign
1245        level: SecurityLevel,
1246    },
1247    /// Update resource security label
1248    SetResourceLevel {
1249        /// The resource whose level is being changed
1250        resource_id: ResourceId,
1251        /// The new security level to assign
1252        level: SecurityLevel,
1253    },
1254    /// Advance time
1255    Tick,
1256    /// Rotate HMAC key (kernel-only, privileged operation)
1257    ///
1258    /// SECURITY: Only privileged kernel code should invoke this.
1259    /// The current key becomes the previous key for grace period verification.
1260    RotateKey {
1261        /// The new key to use for sealing capabilities
1262        new_key: Key,
1263    },
1264}
1265
1266/// Kernel dispatch result
1267///
1268/// Corresponds to Lean: `def KernelResult := KernelStateCore ⊕ KernelError`
1269pub type KernelResult = Result<KernelStateCore, KernelError>;
1270
1271/// Kernel dispatch function
1272///
1273/// This is the ONLY entry point for modifying KernelStateCore.
1274/// Policy evaluation happens in shell - this function assumes
1275/// authorization has been verified.
1276///
1277/// Corresponds to Lean: `def kernelDispatch`
1278///
1279/// # Errors
1280///
1281/// Returns `KernelError::CapIdCollision` if a `CapDelegate` request has an ID that already exists.
1282pub fn kernel_dispatch(core: KernelStateCore, req: KernelRequest) -> KernelResult {
1283    match req {
1284        KernelRequest::CapDelegate { new_cap, target } => {
1285            dispatch_cap_delegate(core, new_cap, target)
1286        }
1287        KernelRequest::CapRevoke { cap_id } => dispatch_cap_revoke(core, cap_id),
1288        KernelRequest::SetPluginLevel { plugin_id, level } => {
1289            dispatch_set_plugin_level(core, plugin_id, level)
1290        }
1291        KernelRequest::SetResourceLevel { resource_id, level } => {
1292            dispatch_set_resource_level(core, resource_id, level)
1293        }
1294        KernelRequest::Tick => dispatch_tick(core),
1295        KernelRequest::RotateKey { new_key } => Ok(dispatch_rotate_key(core, new_key)),
1296    }
1297}
1298
1299/// Dispatch: delegate capability
1300///
1301/// Corresponds to Lean: `def KernelDispatch.dispatchCapDelegate`
1302fn dispatch_cap_delegate(
1303    mut core: KernelStateCore,
1304    new_cap: Capability,
1305    target: PluginId,
1306) -> KernelResult {
1307    // Check for ID collision
1308    if core.revocation.contains(new_cap.id()) {
1309        return Err(KernelError::CapIdCollision(new_cap.id()));
1310    }
1311
1312    // Insert capability
1313    let cap_id = new_cap.id();
1314    core.revocation.insert_mut(new_cap)?;
1315
1316    // Grant to target plugin
1317    let mut target_meta = core.get_plugin_meta(target);
1318    target_meta.grant_cap(cap_id);
1319    core.set_plugin_meta(target, target_meta);
1320
1321    Ok(core)
1322}
1323
1324/// Dispatch: revoke capability
1325///
1326/// Corresponds to Lean: `def KernelDispatch.dispatchCapRevoke`
1327#[allow(clippy::unnecessary_wraps)] // Must return KernelResult for dispatch table uniformity
1328fn dispatch_cap_revoke(mut core: KernelStateCore, cap_id: CapId) -> KernelResult {
1329    core.revocation.revoke_transitive_fast_mut(cap_id)?;
1330    Ok(core)
1331}
1332
1333/// Dispatch: set plugin security level
1334///
1335/// Corresponds to Lean: `def KernelDispatch.dispatchSetPluginLevel`
1336#[allow(clippy::unnecessary_wraps)] // Must return KernelResult for dispatch table uniformity
1337fn dispatch_set_plugin_level(
1338    mut core: KernelStateCore,
1339    plugin_id: PluginId,
1340    level: SecurityLevel,
1341) -> KernelResult {
1342    let mut meta = core.get_plugin_meta(plugin_id);
1343    meta.level = level;
1344    core.set_plugin_meta(plugin_id, meta);
1345    Ok(core)
1346}
1347
1348/// Dispatch: set resource security level
1349///
1350/// Corresponds to Lean: `def KernelDispatch.dispatchSetResourceLevel`
1351#[allow(clippy::unnecessary_wraps)] // Must return KernelResult for dispatch table uniformity
1352fn dispatch_set_resource_level(
1353    mut core: KernelStateCore,
1354    resource_id: ResourceId,
1355    level: SecurityLevel,
1356) -> KernelResult {
1357    let mut meta = core.get_resource_meta(resource_id);
1358    meta.level = level;
1359    core.set_resource_meta(resource_id, meta);
1360    Ok(core)
1361}
1362
1363/// Dispatch: tick (advance time)
1364///
1365/// Corresponds to Lean: `def KernelDispatch.dispatchTick`
1366///
1367/// # Errors
1368///
1369/// Returns `KernelError::CounterOverflow` if the time counter would exceed u64::MAX.
1370fn dispatch_tick(mut core: KernelStateCore) -> Result<KernelStateCore, KernelError> {
1371    core.now = core
1372        .now
1373        .checked_add(1)
1374        .ok_or(KernelError::CounterOverflow("time"))?;
1375    Ok(core)
1376}
1377
1378/// Dispatch: rotate HMAC key
1379///
1380/// Corresponds to Lean: `def KernelDispatch.dispatchRotateKey`
1381///
1382/// The current key becomes the previous key for grace period verification.
1383/// Capabilities sealed with the old key remain valid during the grace period.
1384///
1385/// SECURITY: This is a privileged operation. Only kernel-level code
1386/// should be able to invoke key rotation.
1387fn dispatch_rotate_key(mut core: KernelStateCore, new_key: Key) -> KernelStateCore {
1388    core.key_state.rotate(new_key);
1389    core
1390}
1391
1392#[cfg(test)]
1393mod tests {
1394    use super::*;
1395    use crate::types::{Right, Rights, SealedTag};
1396
1397    fn make_test_cap(id: CapId, parent: Option<CapId>) -> Capability {
1398        Capability::new(
1399            id,
1400            1, // holder
1401            1, // target
1402            Rights::singleton(Right::Read),
1403            parent,
1404            0, // epoch
1405            SealedTag::empty(),
1406        )
1407        .expect("valid capability")
1408    }
1409
1410    #[test]
1411    fn test_revocation_state_empty() {
1412        let rs = RevocationState::empty();
1413        assert_eq!(rs.cap_count(), 0);
1414        assert_eq!(rs.epoch(), 0);
1415        assert!(!rs.is_valid(0));
1416    }
1417
1418    #[test]
1419    fn test_revocation_state_insert() {
1420        let mut rs = RevocationState::empty();
1421        let cap = make_test_cap(1, None);
1422        rs.insert_mut(cap).expect("insert should succeed");
1423
1424        assert_eq!(rs.cap_count(), 1);
1425        assert!(rs.contains(1));
1426        assert!(rs.is_valid(1));
1427    }
1428
1429    #[test]
1430    fn test_revocation_state_insert_collision() {
1431        let mut rs = RevocationState::empty();
1432        let cap1 = make_test_cap(1, None);
1433        rs.insert_mut(cap1).expect("first insert should succeed");
1434
1435        let cap2 = make_test_cap(1, None);
1436        let result = rs.insert_mut(cap2);
1437        assert!(matches!(result, Err(KernelError::CapIdCollision(1))));
1438    }
1439
1440    #[test]
1441    fn test_revocation_state_insert_sorted() {
1442        let mut rs = RevocationState::empty();
1443        // Insert out of order
1444        rs.insert_mut(make_test_cap(5, None)).expect("insert 5");
1445        rs.insert_mut(make_test_cap(2, None)).expect("insert 2");
1446        rs.insert_mut(make_test_cap(8, None)).expect("insert 8");
1447        rs.insert_mut(make_test_cap(1, None)).expect("insert 1");
1448
1449        // Verify sorted order
1450        let ids = rs.cap_ids();
1451        assert_eq!(ids, vec![1, 2, 5, 8]);
1452    }
1453
1454    #[test]
1455    fn test_revocation_state_revoke() {
1456        let mut rs = RevocationState::empty();
1457        let cap = make_test_cap(1, None);
1458        rs.insert_mut(cap).expect("insert should succeed");
1459
1460        assert!(rs.is_valid(1));
1461        rs.revoke_mut(1).expect("revoke should succeed");
1462        assert!(!rs.is_valid(1));
1463    }
1464
1465    #[test]
1466    fn test_revocation_state_has_ancestor() {
1467        let mut rs = RevocationState::empty();
1468
1469        // Create parent-child chain: 1 <- 2 <- 3
1470        let cap1 = make_test_cap(1, None);
1471        let cap2 = make_test_cap(2, Some(1));
1472        let cap3 = make_test_cap(3, Some(2));
1473
1474        rs.insert_mut(cap1).expect("insert 1");
1475        rs.insert_mut(cap2).expect("insert 2");
1476        rs.insert_mut(cap3).expect("insert 3");
1477
1478        // Test ancestry
1479        assert!(rs.has_ancestor(2, 1)); // 2's parent is 1
1480        assert!(rs.has_ancestor(3, 2)); // 3's parent is 2
1481        assert!(rs.has_ancestor(3, 1)); // 1 is ancestor of 3
1482
1483        // Negative cases
1484        assert!(!rs.has_ancestor(1, 2)); // 1 has no parent
1485        assert!(!rs.has_ancestor(1, 3)); // 1 has no parent
1486        assert!(!rs.has_ancestor(2, 3)); // 3 is not ancestor of 2
1487    }
1488
1489    #[test]
1490    fn test_revocation_state_revoke_transitive() {
1491        let mut rs = RevocationState::empty();
1492
1493        // Create parent-child chain: 1 <- 2 <- 3
1494        let cap1 = make_test_cap(1, None);
1495        let cap2 = make_test_cap(2, Some(1));
1496        let cap3 = make_test_cap(3, Some(2));
1497
1498        rs.insert_mut(cap1).expect("insert 1");
1499        rs.insert_mut(cap2).expect("insert 2");
1500        rs.insert_mut(cap3).expect("insert 3");
1501
1502        // All should be valid initially
1503        assert!(rs.is_valid(1));
1504        assert!(rs.is_valid(2));
1505        assert!(rs.is_valid(3));
1506
1507        // Revoke 1 transitively - should revoke 1, 2, and 3
1508        rs.revoke_transitive_mut(1);
1509
1510        assert!(!rs.is_valid(1));
1511        assert!(!rs.is_valid(2));
1512        assert!(!rs.is_valid(3));
1513    }
1514
1515    #[test]
1516    fn test_revocation_state_revoke_transitive_partial() {
1517        let mut rs = RevocationState::empty();
1518
1519        // Create parent-child chain: 1 <- 2 <- 3
1520        let cap1 = make_test_cap(1, None);
1521        let cap2 = make_test_cap(2, Some(1));
1522        let cap3 = make_test_cap(3, Some(2));
1523
1524        rs.insert_mut(cap1).expect("insert 1");
1525        rs.insert_mut(cap2).expect("insert 2");
1526        rs.insert_mut(cap3).expect("insert 3");
1527
1528        // Revoke 2 transitively - should revoke 2 and 3, but not 1
1529        rs.revoke_transitive_mut(2);
1530
1531        assert!(rs.is_valid(1)); // Parent still valid
1532        assert!(!rs.is_valid(2)); // Revoked
1533        assert!(!rs.is_valid(3)); // Child of revoked
1534    }
1535
1536    #[test]
1537    fn test_kernel_state_initial() {
1538        let key = Key::empty();
1539        let policy = PolicyState::deny_all();
1540        let ks = KernelState::initial(key, policy);
1541
1542        assert_eq!(ks.now(), 0);
1543        assert_eq!(ks.revocation().cap_count(), 0);
1544    }
1545
1546    #[test]
1547    fn test_kernel_state_tick() {
1548        let mut ks = KernelState::default();
1549        assert_eq!(ks.now(), 0);
1550
1551        ks.tick_checked().expect("tick should succeed");
1552        assert_eq!(ks.now(), 1);
1553
1554        ks.tick_checked().expect("tick should succeed");
1555        assert_eq!(ks.now(), 2);
1556    }
1557
1558    #[test]
1559    fn test_kernel_state_cap_not_revoked() {
1560        let mut ks = KernelState::default();
1561        let cap = make_test_cap(1, None);
1562
1563        // Capability not in kernel -> not valid
1564        assert!(!ks.cap_not_revoked(&cap));
1565
1566        // Insert and check
1567        ks.revocation_mut()
1568            .insert_mut(cap.clone())
1569            .expect("insert should succeed");
1570        assert!(ks.cap_not_revoked(&cap));
1571
1572        // Revoke and check
1573        ks.revocation_mut()
1574            .revoke_mut(1)
1575            .expect("revoke should succeed");
1576        assert!(!ks.cap_not_revoked(&cap));
1577    }
1578
1579    // ============== KEY STATE TESTS ==============
1580
1581    #[test]
1582    fn test_key_state_initial() {
1583        let key = Key::from_bytes([1u8; 32]);
1584        let ks = KeyState::initial(key.clone());
1585
1586        assert_eq!(ks.epoch(), 0);
1587        assert!(ks.previous().is_none());
1588        assert!(ks.previous_epoch().is_none());
1589    }
1590
1591    #[test]
1592    fn test_key_state_rotation() {
1593        let key1 = Key::from_bytes([1u8; 32]);
1594        let key2 = Key::from_bytes([2u8; 32]);
1595        let mut ks = KeyState::initial(key1.clone());
1596
1597        // Rotate to key2
1598        ks.rotate(key2.clone());
1599
1600        assert_eq!(ks.epoch(), 1);
1601        assert!(ks.previous().is_some());
1602        assert_eq!(ks.previous_epoch(), Some(0));
1603        // Current key should be key2
1604        assert_eq!(ks.current().as_bytes(), key2.as_bytes());
1605        // Previous key should be key1
1606        assert_eq!(ks.previous().unwrap().as_bytes(), key1.as_bytes());
1607    }
1608
1609    #[test]
1610    fn test_key_state_epoch_valid() {
1611        let key1 = Key::from_bytes([1u8; 32]);
1612        let key2 = Key::from_bytes([2u8; 32]);
1613        let mut ks = KeyState::initial(key1);
1614
1615        // Initial state: epoch 0
1616        assert!(ks.epoch_valid(0)); // Current epoch valid
1617        assert!(ks.epoch_valid(1)); // Future epoch valid
1618
1619        // After rotation: epoch 1, previous_epoch 0
1620        ks.rotate(key2);
1621        assert!(ks.epoch_valid(1)); // Current epoch valid
1622        assert!(ks.epoch_valid(0)); // Previous epoch (grace period) valid
1623        assert!(ks.epoch_valid(2)); // Future epoch valid
1624    }
1625
1626    #[test]
1627    fn test_key_state_multiple_rotations() {
1628        let key1 = Key::from_bytes([1u8; 32]);
1629        let key2 = Key::from_bytes([2u8; 32]);
1630        let key3 = Key::from_bytes([3u8; 32]);
1631        let mut ks = KeyState::initial(key1.clone());
1632
1633        // First rotation
1634        ks.rotate(key2.clone());
1635        assert_eq!(ks.epoch(), 1);
1636        assert_eq!(ks.previous_epoch(), Some(0));
1637
1638        // Second rotation
1639        ks.rotate(key3.clone());
1640        assert_eq!(ks.epoch(), 2);
1641        assert_eq!(ks.previous_epoch(), Some(1)); // Previous epoch is now 1, not 0
1642                                                  // key2 is now the previous key (key1 is lost)
1643        assert_eq!(ks.previous().unwrap().as_bytes(), key2.as_bytes());
1644    }
1645
1646    #[test]
1647    fn test_key_state_clear_previous() {
1648        let key1 = Key::from_bytes([1u8; 32]);
1649        let key2 = Key::from_bytes([2u8; 32]);
1650        let mut ks = KeyState::initial(key1);
1651
1652        ks.rotate(key2);
1653        assert!(ks.previous().is_some());
1654
1655        ks.clear_previous();
1656        assert!(ks.previous().is_none());
1657        assert!(ks.previous_epoch().is_none());
1658        // Current key should remain
1659        assert_eq!(ks.epoch(), 1);
1660    }
1661
1662    #[test]
1663    fn test_kernel_state_rotate_key() {
1664        let key1 = Key::from_bytes([1u8; 32]);
1665        let key2 = Key::from_bytes([2u8; 32]);
1666        let mut ks = KernelState::initial(key1.clone(), PolicyState::deny_all());
1667
1668        assert_eq!(ks.key_epoch(), 0);
1669
1670        ks.rotate_key(key2.clone());
1671
1672        assert_eq!(ks.key_epoch(), 1);
1673        assert_eq!(ks.hmac_key().as_bytes(), key2.as_bytes());
1674    }
1675
1676    #[test]
1677    fn test_kernel_dispatch_rotate_key() {
1678        let key1 = Key::from_bytes([1u8; 32]);
1679        let key2 = Key::from_bytes([2u8; 32]);
1680        let core = KernelStateCore::empty(key1);
1681
1682        let result = kernel_dispatch(
1683            core,
1684            KernelRequest::RotateKey {
1685                new_key: key2.clone(),
1686            },
1687        );
1688        assert!(result.is_ok());
1689
1690        let new_core = result.unwrap();
1691        assert_eq!(new_core.key_state().epoch(), 1);
1692        assert_eq!(new_core.hmac_key().as_bytes(), key2.as_bytes());
1693    }
1694
1695    // ============== BINARY SEARCH EDGE-CASE TESTS ==============
1696
1697    #[test]
1698    fn test_plugin_meta_grant_revoke_holds_empty() {
1699        let mut pm = PluginMeta::empty();
1700        assert!(!pm.holds_cap(0));
1701        assert!(!pm.holds_cap(u128::MAX));
1702
1703        pm.revoke_cap(42); // no-op on empty
1704        assert!(pm.held_caps.is_empty());
1705    }
1706
1707    #[test]
1708    fn test_plugin_meta_grant_sorted_order() {
1709        let mut pm = PluginMeta::empty();
1710        pm.grant_cap(5);
1711        pm.grant_cap(1);
1712        pm.grant_cap(9);
1713        pm.grant_cap(3);
1714        assert_eq!(pm.held_caps, vec![1, 3, 5, 9]);
1715    }
1716
1717    #[test]
1718    fn test_plugin_meta_grant_dedup() {
1719        let mut pm = PluginMeta::empty();
1720        pm.grant_cap(3);
1721        pm.grant_cap(3);
1722        pm.grant_cap(3);
1723        assert_eq!(pm.held_caps, vec![3]);
1724    }
1725
1726    #[test]
1727    fn test_plugin_meta_revoke_first_middle_last() {
1728        let mut pm = PluginMeta::empty();
1729        pm.grant_cap(1);
1730        pm.grant_cap(5);
1731        pm.grant_cap(9);
1732
1733        pm.revoke_cap(1); // first
1734        assert_eq!(pm.held_caps, vec![5, 9]);
1735
1736        pm.grant_cap(1);
1737        pm.revoke_cap(5); // middle
1738        assert_eq!(pm.held_caps, vec![1, 9]);
1739
1740        pm.grant_cap(5);
1741        pm.revoke_cap(9); // last
1742        assert_eq!(pm.held_caps, vec![1, 5]);
1743    }
1744
1745    #[test]
1746    fn test_set_plugin_meta_normalizes_unsorted_held_caps() {
1747        let mut core = KernelStateCore::empty(Key::empty());
1748        let meta = PluginMeta {
1749            level: SecurityLevel::Internal,
1750            held_caps: vec![9, 3, 5, 3, 1, 5], // unsorted, duplicates
1751        };
1752        core.set_plugin_meta(100, meta);
1753        let stored = core.get_plugin_meta(100);
1754        assert_eq!(stored.held_caps, vec![1, 3, 5, 9]); // sorted + deduped
1755    }
1756
1757    #[test]
1758    fn test_kernel_state_core_plugin_meta_upsert_edge_cases() {
1759        let mut core = KernelStateCore::empty(Key::empty());
1760
1761        // Insert into empty vec
1762        core.set_plugin_meta(
1763            50,
1764            PluginMeta {
1765                level: SecurityLevel::Public,
1766                held_caps: vec![],
1767            },
1768        );
1769        assert_eq!(core.plugin_metas.len(), 1);
1770
1771        // Insert at beginning
1772        core.set_plugin_meta(
1773            10,
1774            PluginMeta {
1775                level: SecurityLevel::Internal,
1776                held_caps: vec![],
1777            },
1778        );
1779        assert_eq!(core.plugin_metas[0].0, 10);
1780
1781        // Insert at end
1782        core.set_plugin_meta(
1783            90,
1784            PluginMeta {
1785                level: SecurityLevel::Secret,
1786                held_caps: vec![],
1787            },
1788        );
1789        assert_eq!(core.plugin_metas[2].0, 90);
1790
1791        // Insert in middle
1792        core.set_plugin_meta(
1793            30,
1794            PluginMeta {
1795                level: SecurityLevel::Confidential,
1796                held_caps: vec![],
1797            },
1798        );
1799        let ids: Vec<u128> = core.plugin_metas.iter().map(|(id, _)| *id).collect();
1800        assert_eq!(ids, vec![10, 30, 50, 90]);
1801
1802        // Update existing (middle)
1803        core.set_plugin_meta(
1804            50,
1805            PluginMeta {
1806                level: SecurityLevel::Secret,
1807                held_caps: vec![1],
1808            },
1809        );
1810        assert_eq!(core.get_plugin_meta(50).level, SecurityLevel::Secret);
1811        assert_eq!(core.plugin_metas.len(), 4); // no growth
1812    }
1813
1814    #[test]
1815    fn test_kernel_state_core_resource_meta_upsert_edge_cases() {
1816        let mut core = KernelStateCore::empty(Key::empty());
1817
1818        // Insert into empty
1819        core.set_resource_meta(
1820            5,
1821            ResourceMeta {
1822                level: SecurityLevel::Public,
1823            },
1824        );
1825        assert_eq!(core.resource_metas.len(), 1);
1826
1827        // Insert at beginning
1828        core.set_resource_meta(
1829            1,
1830            ResourceMeta {
1831                level: SecurityLevel::Internal,
1832            },
1833        );
1834        assert_eq!(core.resource_metas[0].0, 1);
1835
1836        // Update existing
1837        core.set_resource_meta(
1838            5,
1839            ResourceMeta {
1840                level: SecurityLevel::Secret,
1841            },
1842        );
1843        assert_eq!(core.get_resource_meta(5).level, SecurityLevel::Secret);
1844        assert_eq!(core.resource_metas.len(), 2);
1845    }
1846
1847    #[test]
1848    fn test_revocation_state_single_element() {
1849        let mut rs = RevocationState::empty();
1850        rs.insert_mut(make_test_cap(42, None)).unwrap();
1851
1852        assert!(rs.is_valid(42));
1853        assert!(!rs.is_valid(41));
1854        assert!(!rs.is_valid(43));
1855        assert_eq!(rs.cap_count(), 1);
1856    }
1857
1858    #[test]
1859    fn test_revocation_state_insert_at_boundaries() {
1860        let mut rs = RevocationState::empty();
1861        // Insert at end, beginning, middle in that order
1862        rs.insert_mut(make_test_cap(100, None)).unwrap();
1863        rs.insert_mut(make_test_cap(1, None)).unwrap();
1864        rs.insert_mut(make_test_cap(50, None)).unwrap();
1865
1866        let ids = rs.cap_ids();
1867        assert_eq!(ids, vec![1, 50, 100]);
1868    }
1869}