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}