code wiki / _hdl_build / nx_apistack_audit.nx

nx_apistack_audit.nx source

↩ module page · 104 lines · 4456 B

1// nx_apistack_audit.nx -- CAP-API-AUDIT: the control-plane must-have -- a who/what/when trail for every privileged 2// /api action (deploy/rollback/reconcile/restart/upload). Append-only AND HASH-CHAINED: each record's chain hash 3// folds the PREVIOUS record's hash + this record's payload, so altering or deleting any past record breaks the chain 4// and au_verify detects it. Tamper-EVIDENT by construction (the sovereign exceed over a mutable log file). Pure 5// integer FNV-1a -> deterministic, reproducible. Record: "<hex16 chain>\t<action>\t<actor>\t<result>\n". 6// license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_site_lock_lib.nx" 9const AU_MAGIC_1048576: i64 = 1048576 10 11const AU_FNV_OFF: i64 = 0 - 3750763034362895579 // 0xcbf29ce484222325 12const AU_FNV_PRIME: i64 = 1099511628211 13 14// chain hash = FNV-1a over (prev's 8 bytes) then (payload bytes). 15func au_fnv_chain(prev: i64, payload: *u8, n: i64) -> i64 { 16 var h: i64 = AU_FNV_OFF 17 var b: i64 = 0 18 while b < 8 { h = (h ^ ((prev >> (b * 8)) & 0xff)) * AU_FNV_PRIME; b = b + 1 } 19 var i: i64 = 0 20 while i < n { h = (h ^ ((payload[i] as i64) & 0xff)) * AU_FNV_PRIME; i = i + 1 } 21 return h 22} 23func au_hex16(h: i64, out: *u8) -> i64 { 24 let hx: *u8 = "0123456789abcdef" as *u8 25 var k: i64 = 0 26 while k < 16 { out[k] = hx[(h >> (60 - 4 * k)) & 0xf]; k = k + 1 } 27 return 16 28} 29func au_hexparse(s: *u8) -> i64 { 30 var h: i64 = 0; var k: i64 = 0 31 while k < 16 { 32 let c: i64 = (s[k] as i64) & 0xff 33 var v: i64 = 0 34 if c >= 48 { if c <= 57 { v = c - 48 } } 35 if c >= 97 { if c <= 102 { v = c - 97 + 10 } } 36 h = (h << 4) | v 37 k = k + 1 38 } 39 return h 40} 41func au_read_file(path: *u8, out: *u8, cap: i64) -> i64 { 42 let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } 43 var total: i64 = 0; var go: i64 = 1 44 while go == 1 { 45 let tail: *u8 = (out as i64 + total) as *u8 46 let nr: i64 = sys_read(fd, tail, cap - total) 47 if nr <= 0 { go = 0 } 48 if nr > 0 { total = total + nr } 49 if total >= cap { go = 0 } 50 } 51 sys_close(fd); return total 52} 53// chain hash of the last non-empty record (field 0), 0 if the log is empty. 54func au_last_chain(logpath: *u8) -> i64 { 55 let buf: *u8 = sys_mmap(AU_MAGIC_1048576); let n: i64 = au_read_file(logpath, buf, AU_MAGIC_1048576) 56 if n <= 0 { return 0 } 57 var ls: i64 = 0; var last_ls: i64 = 0 - 1 58 while ls < n { 59 let le: i64 = slk_line_end(buf, n, ls) 60 if le > ls { if buf[ls] != (35 as u8) { last_ls = ls } } 61 ls = le + 1 62 } 63 if last_ls < 0 { return 0 } 64 let le2: i64 = slk_line_end(buf, n, last_ls) 65 let fs: *i64 = sys_mmap(8); let fe: *i64 = sys_mmap(8) 66 if slk_field(buf, last_ls, le2, 0, fs, fe) == 1 { return au_hexparse(slk_at(buf, fs[0])) } 67 return 0 68} 69// append an audit record. payload = "action\tactor\tresult". Returns the new chain hash (0 on open fail). 70func au_append(logpath: *u8, payload: *u8, payload_n: i64) -> i64 { 71 let prev: i64 = au_last_chain(logpath) 72 let h: i64 = au_fnv_chain(prev, payload, payload_n) 73 let hexb: *u8 = sys_mmap(24); au_hex16(h, hexb) 74 let fd: i64 = sys_openat_append(logpath, 0x1a4) 75 if fd < 0 { return 0 } 76 sys_write(fd, hexb, 16); sys_write(fd, "\t" as *u8, 1) 77 sys_write(fd, payload, payload_n); sys_write(fd, "\n" as *u8, 1) 78 sys_close(fd) 79 return h 80} 81// verify the whole chain: recompute each record's hash from the running prev. 1 = intact, 0 = TAMPERED. 82func au_verify(logpath: *u8) -> i64 { 83 let buf: *u8 = sys_mmap(AU_MAGIC_1048576); let n: i64 = au_read_file(logpath, buf, AU_MAGIC_1048576) 84 let fs: *i64 = sys_mmap(8); let fe: *i64 = sys_mmap(8) 85 var ls: i64 = 0; var prev: i64 = 0 86 while ls < n { 87 let le: i64 = slk_line_end(buf, n, ls) 88 if le > ls { if buf[ls] != (35 as u8) { 89 if slk_field(buf, ls, le, 0, fs, fe) == 1 { 90 let stored: i64 = au_hexparse(slk_at(buf, fs[0])) 91 let fs1: *i64 = sys_mmap(8); let fe1: *i64 = sys_mmap(8) 92 if slk_field(buf, ls, le, 1, fs1, fe1) == 1 { 93 let payload: *u8 = slk_at(buf, fs1[0]) 94 let payload_n: i64 = le - fs1[0] 95 let h: i64 = au_fnv_chain(prev, payload, payload_n) 96 if h != stored { return 0 } 97 prev = stored 98 } 99 } 100 } } 101 ls = le + 1 102 } 103 return 1 104}