// SPEC-020: Capability Space (CSpace) Implementation // Component: core/security/cspace // Target: Ground Zero - Phase 1 const std = @import("std"); /// CapType: Closed enumeration of capability types (SPEC-020) pub const CapType = enum(u8) { Null = 0, Entity = 1, // Control over Process/Fiber Channel = 2, // Access to ION Ring Memory = 3, // Access to Physical Frame Interrupt = 4, // IRQ mask/unmask control Time = 5, // Clock/Timer access Entropy = 6, // HWRNG access }; /// Permission flags (SPEC-020) pub const CapPerms = packed struct(u8) { read: bool = false, write: bool = false, execute: bool = false, map: bool = false, delegate: bool = false, revoke: bool = false, copy: bool = false, spawn: bool = false, }; /// Capability structure (32 bytes, cache-line aligned) pub const Capability = packed struct { cap_type: CapType, // 1 byte perms: CapPerms, // 1 byte _reserved: u16, // 2 bytes (alignment) object_id: u64, // 8 bytes (SipHash of resource) bounds_start: u64, // 8 bytes bounds_end: u64, // 8 bytes comptime { if (@sizeOf(Capability) != 32) { @compileError("Capability must be exactly 32 bytes"); } } /// Create a null capability pub fn null_cap() Capability { return .{ .cap_type = .Null, .perms = .{}, ._reserved = 0, .object_id = 0, .bounds_start = 0, .bounds_end = 0, }; } /// Check if capability is null pub fn is_null(self: *const Capability) bool { return self.cap_type == .Null; } /// Validate bounds pub fn check_bounds(self: *const Capability, addr: u64) bool { if (self.is_null()) return false; return addr >= self.bounds_start and addr < self.bounds_end; } /// Check permission pub fn has_perm(self: *const Capability, perm: CapPerms) bool { const self_bits = @as(u8, @bitCast(self.perms)); const perm_bits = @as(u8, @bitCast(perm)); return (self_bits & perm_bits) == perm_bits; } }; /// CSpace: Per-fiber capability table pub const CSPACE_SIZE = 64; // Maximum capabilities per fiber pub const CSpace = struct { slots: [CSPACE_SIZE]Capability, epoch: u32, // For revocation fiber_id: u64, // Owner fiber _padding: u32, // Alignment /// Initialize empty CSpace pub fn init(fiber_id: u64) CSpace { var cs = CSpace{ .slots = undefined, .epoch = 0, .fiber_id = fiber_id, ._padding = 0, }; // Initialize all slots to Null for (&cs.slots) |*slot| { slot.* = Capability.null_cap(); } return cs; } /// Find first empty slot pub fn find_empty_slot(self: *CSpace) ?usize { for (&self.slots, 0..) |*cap, i| { if (cap.is_null()) return i; } return null; } /// Grant capability (insert into CSpace) pub fn grant(self: *CSpace, cap: Capability) !usize { const slot = self.find_empty_slot() orelse return error.CSpaceFull; self.slots[slot] = cap; return slot; } /// Lookup capability by slot index pub fn lookup(self: *const CSpace, slot: usize) ?*const Capability { if (slot >= CSPACE_SIZE) return null; const cap = &self.slots[slot]; if (cap.is_null()) return null; return cap; } /// Revoke capability (set to Null) pub fn revoke(self: *CSpace, slot: usize) void { if (slot >= CSPACE_SIZE) return; self.slots[slot] = Capability.null_cap(); } /// Revoke all capabilities (epoch-based) pub fn revoke_all(self: *CSpace) void { for (&self.slots) |*cap| { cap.* = Capability.null_cap(); } self.epoch +%= 1; } /// Delegate capability (Move or Copy) pub fn delegate( self: *CSpace, slot: usize, target: *CSpace, move: bool, ) !usize { const cap = self.lookup(slot) orelse return error.InvalidCapability; // Check DELEGATE permission if (!cap.has_perm(.{ .delegate = true })) { return error.NotDelegatable; } // Grant to target const new_slot = try target.grant(cap.*); // If move (not copy), revoke from source if (move or !cap.has_perm(.{ .copy = true })) { self.revoke(slot); } return new_slot; } }; /// Global CSpace table (one per fiber) /// This will be integrated with Fiber Control Block in kernel.nim pub const MAX_FIBERS = 16; var global_cspaces: [MAX_FIBERS]CSpace = undefined; var cspaces_initialized: bool = false; /// Initialize global CSpace table pub export fn cspace_init() void { if (cspaces_initialized) return; for (&global_cspaces, 0..) |*cs, i| { cs.* = CSpace.init(i); } cspaces_initialized = true; } /// Get CSpace for fiber pub export fn cspace_get(fiber_id: u64) ?*CSpace { if (!cspaces_initialized) return null; if (fiber_id >= MAX_FIBERS) return null; return &global_cspaces[fiber_id]; } /// Grant capability to fiber (C ABI) pub export fn cspace_grant_cap( fiber_id: u64, cap_type: u8, perms: u8, object_id: u64, bounds_start: u64, bounds_end: u64, ) i32 { const cs = cspace_get(fiber_id) orelse return -1; const cap = Capability{ .cap_type = @enumFromInt(cap_type), .perms = @bitCast(perms), ._reserved = 0, .object_id = object_id, .bounds_start = bounds_start, .bounds_end = bounds_end, }; const slot = cs.grant(cap) catch return -1; return @intCast(slot); } /// Lookup capability (C ABI) pub export fn cspace_lookup(fiber_id: u64, slot: usize) ?*const Capability { const cs = cspace_get(fiber_id) orelse return null; return cs.lookup(slot); } /// Revoke capability (C ABI) pub export fn cspace_revoke(fiber_id: u64, slot: usize) void { const cs = cspace_get(fiber_id) orelse return; cs.revoke(slot); } /// Check capability permission (C ABI) pub export fn cspace_check_perm(fiber_id: u64, slot: usize, perm_bits: u8) bool { const cs = cspace_get(fiber_id) orelse return false; const cap = cs.lookup(slot) orelse return false; const perm: CapPerms = @bitCast(perm_bits); return cap.has_perm(perm); } // Unit tests test "Capability creation and validation" { const cap = Capability{ .cap_type = .Channel, .perms = .{ .read = true, .write = true }, ._reserved = 0, .object_id = 0x1234, .bounds_start = 0x1000, .bounds_end = 0x2000, }; try std.testing.expect(!cap.is_null()); try std.testing.expect(cap.check_bounds(0x1500)); try std.testing.expect(!cap.check_bounds(0x3000)); try std.testing.expect(cap.has_perm(.{ .read = true })); try std.testing.expect(!cap.has_perm(.{ .execute = true })); } test "CSpace operations" { var cs = CSpace.init(42); const cap = Capability{ .cap_type = .Memory, .perms = .{ .read = true, .write = true, .delegate = true }, ._reserved = 0, .object_id = 0xABCD, .bounds_start = 0, .bounds_end = 0x1000, }; // Grant capability const slot = try cs.grant(cap); try std.testing.expect(slot == 0); // Lookup capability const retrieved = cs.lookup(slot).?; try std.testing.expect(retrieved.object_id == 0xABCD); // Revoke capability cs.revoke(slot); try std.testing.expect(cs.lookup(slot) == null); } test "Delegation" { var cs1 = CSpace.init(1); var cs2 = CSpace.init(2); const cap = Capability{ .cap_type = .Channel, .perms = .{ .read = true, .delegate = true, .copy = true }, ._reserved = 0, .object_id = 0x5678, .bounds_start = 0, .bounds_end = 0xFFFF, }; const slot1 = try cs1.grant(cap); // Copy delegation const slot2 = try cs1.delegate(slot1, &cs2, false); // Both should have the capability try std.testing.expect(cs1.lookup(slot1) != null); try std.testing.expect(cs2.lookup(slot2) != null); // Move delegation var cs3 = CSpace.init(3); const slot3 = try cs2.delegate(slot2, &cs3, true); // cs2 should no longer have it try std.testing.expect(cs2.lookup(slot2) == null); try std.testing.expect(cs3.lookup(slot3) != null); }