code wiki / _hdl_build / nx_audit_log.nx
nx_audit_log.nx source
↩ module page · 143 lines · 7659 B
1// nx_audit_log.nx -- TAMPER-EVIDENT audit log for secret access (a capability real vaults have).
2// Each entry is HASH-CHAINED: line_hash = SHA-256(prev_hash || epoch || op || name || outcome). Any
3// deletion, reordering, or edit of a past entry breaks the chain at that point -- detectable by the
4// verifier without a separate signature. Modes:
5// append <op> <name> <outcome> -- append a chained entry to knowledge/status/secret_audit.log
6// verify -- recompute the whole chain; report the first break (or OK)
7// Uses the team's WORKING SHA-256 (no broken deps). license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_sha256.nx"
10const K_MAGIC_1048592: i64 = 1048592
11const K_MAGIC_1048576: i64 = 1048576
12const K_MAGIC_1024: i64 = 1024
13func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
14func _pn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
15func al_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
16func al_hex(dig: *u8, out: *u8) -> i64 {
17 let hexd: *u8 = "0123456789abcdef" as *u8
18 var i: i64 = 0
19 while i < 32 { out[i*2] = hexd[(dig[i] as i64 >> 4) & 15]; out[i*2+1] = hexd[dig[i] as i64 & 15]; i = i + 1 }
20 return 64
21}
22func al_read(path: *u8, out: *u8, cap: i64, lb: *i64) -> i64 {
23 let fd: i64 = sys_openat_rd(path)
24 if fd < 0 { lb[0] = 0; return 0 - 1 }
25 var total: i64 = 0
26 var go: i64 = 1
27 while go == 1 { let base: i64 = out as i64; let r: i64 = sys_read(fd, (base + total) as *u8, cap - total); if r <= 0 { go = 0 } else { total = total + r } if total >= cap { go = 0 } }
28 sys_close(fd)
29 lb[0] = total
30 return total
31}
32// compute the chain hash for one record: SHA-256(prevhex[64] || payload)
33func al_chain(prevhex: *u8, payload: *u8, plen: i64, outhex: *u8) -> i64 {
34 let dig: *u8 = sys_mmap(32)
35 var c: Sha256
36 sha256_init(&c)
37 sha256_update(&c, prevhex, 64)
38 sha256_update(&c, payload, plen)
39 sha256_final(&c, dig)
40 al_hex(dig, outhex)
41 return 0
42}
43// build the canonical payload "epoch|op|name|outcome" into buf; return length
44func al_payload(buf: *u8, epoch: i64, op: *u8, name: *u8, outcome: *u8) -> i64 {
45 var o: i64 = 0
46 var e: i64 = epoch
47 let t: *u8 = sys_mmap(28); var k: i64 = 0
48 if e == 0 { t[0] = 48 as u8; k = 1 }
49 while e > 0 { t[k] = (48 + (e % 10)) as u8; e = e / 10; k = k + 1 }
50 var i: i64 = 0; while i < k { buf[o] = t[k-1-i]; o = o + 1; i = i + 1 }
51 buf[o] = 124 as u8; o = o + 1
52 i = 0; while op[i] != (0 as u8) { buf[o] = op[i]; o = o + 1; i = i + 1 }
53 buf[o] = 124 as u8; o = o + 1
54 i = 0; while name[i] != (0 as u8) { buf[o] = name[i]; o = o + 1; i = i + 1 }
55 buf[o] = 124 as u8; o = o + 1
56 i = 0; while outcome[i] != (0 as u8) { buf[o] = outcome[i]; o = o + 1; i = i + 1 }
57 return o
58}
59// extract the last "hash=<64hex>" from the log into outhex; genesis (all-zero) if none
60func al_last_hash(buf: *u8, n: i64, outhex: *u8) -> i64 {
61 var i: i64 = 0; while i < 64 { outhex[i] = 48 as u8; i = i + 1 } // genesis = "000...0"
62 var found: i64 = 0
63 i = 0
64 while i + 5 <= n {
65 if buf[i]==(104 as u8) { if buf[i+1]==(97 as u8) { if buf[i+2]==(115 as u8) { if buf[i+3]==(104 as u8) { if buf[i+4]==(61 as u8) {
66 var j: i64 = 0
67 while j < 64 { if i+5+j < n { outhex[j] = buf[i+5+j] } j = j + 1 }
68 found = 1
69 } } } } }
70 i = i + 1
71 }
72 return found
73}
74func main(argc: i64, argv: *i64) -> i64 {
75 if argc < 2 { _p("usage: nx_audit_log <append <op> <name> <outcome>|verify>\n" as *u8); sys_exit(2); return 2 }
76 let mode: *u8 = argv[1] as *u8
77 let path: *u8 = "knowledge/status/secret_audit.log" as *u8
78 let buf: *u8 = sys_mmap(K_MAGIC_1048592)
79 let lb: *i64 = sys_mmap(16) as *i64
80 if mode[0] == (97 as u8) { // 'a' append
81 if argc < 5 { _p(" append needs <op> <name> <outcome>\n" as *u8); sys_exit(2); return 2 }
82 al_read(path, buf, K_MAGIC_1048576, lb)
83 let prevhex: *u8 = sys_mmap(128)
84 al_last_hash(buf, lb[0], prevhex)
85 let payload: *u8 = sys_mmap(K_MAGIC_1024)
86 let plen: i64 = al_payload(payload, sys_now_realtime_sec(), argv[2] as *u8, argv[3] as *u8, argv[4] as *u8)
87 let curhex: *u8 = sys_mmap(128)
88 al_chain(prevhex, payload, plen, curhex)
89 let fd: i64 = sys_openat_append(path, 0x1a4)
90 if fd < 0 { _p(" audit append failed\n" as *u8); sys_exit(1); return 1 }
91 sys_write(fd, "rec ", 4)
92 sys_write(fd, payload, plen)
93 sys_write(fd, " hash=", 6)
94 sys_write(fd, curhex, 64)
95 sys_write(fd, "\n", 1)
96 sys_close(fd)
97 _p(" AUDIT appended (chained): " as *u8); _p(argv[2] as *u8); _p(" " as *u8); _p(argv[3] as *u8); _p(" -> hash linked\n" as *u8)
98 sys_exit(0); return 0
99 }
100 if mode[0] == (118 as u8) { // 'v' verify -- recompute the chain line by line
101 let n: i64 = al_read(path, buf, K_MAGIC_1048576, lb)
102 if n <= 0 { _p(" AUDIT VERIFY: empty/absent log (vacuously OK)\n" as *u8); sys_exit(0); return 0 }
103 let prevhex: *u8 = sys_mmap(128)
104 var i: i64 = 0; while i < 64 { prevhex[i] = 48 as u8; i = i + 1 }
105 var lineno: i64 = 0
106 var ok: i64 = 1
107 var pos: i64 = 0
108 while pos < n {
109 // line = buf[pos .. eol)
110 var eol: i64 = pos
111 while eol < n { if buf[eol] == (10 as u8) { break } eol = eol + 1 }
112 // require prefix "rec " and find " hash="
113 if buf[pos]==(114 as u8) { if buf[pos+1]==(101 as u8) { if buf[pos+2]==(99 as u8) { if buf[pos+3]==(32 as u8) {
114 lineno = lineno + 1
115 let pstart: i64 = pos + 4
116 // find " hash=" within [pstart, eol)
117 var hp: i64 = pstart
118 var hfound: i64 = 0 - 1
119 while hp + 6 <= eol { if buf[hp]==(32 as u8) { if buf[hp+1]==(104 as u8) { if buf[hp+2]==(97 as u8) { if buf[hp+3]==(115 as u8) { if buf[hp+4]==(104 as u8) { if buf[hp+5]==(61 as u8) { hfound = hp } } } } } } if hfound >= 0 { hp = eol } else { hp = hp + 1 } }
120 if hfound < 0 { ok = 0; _p(" AUDIT BREAK at line " as *u8); _pn(lineno); _p(": malformed (no hash)\n" as *u8); pos = n }
121 else {
122 let payload: *u8 = sys_mmap(K_MAGIC_1024)
123 let plen: i64 = hfound - pstart
124 var c2: i64 = 0; while c2 < plen { payload[c2] = buf[pstart + c2]; c2 = c2 + 1 }
125 let want: *u8 = sys_mmap(128)
126 al_chain(prevhex, payload, plen, want)
127 let stored: i64 = hfound + 6
128 var same: i64 = 1
129 var d: i64 = 0
130 while d < 64 { if buf[stored + d] != want[d] { same = 0; d = 64 } else { d = d + 1 } }
131 if same == 0 { ok = 0; _p(" AUDIT BREAK at line " as *u8); _pn(lineno); _p(": hash mismatch (tampered/reordered/deleted)\n" as *u8); pos = n }
132 else { var w: i64 = 0; while w < 64 { prevhex[w] = buf[stored + w]; w = w + 1 } }
133 }
134 } } } }
135 if pos < n { pos = eol + 1 }
136 }
137 if ok == 1 { _p(" AUDIT VERIFY: chain INTACT over " as *u8); _pn(lineno); _p(" entries (no tampering)\n" as *u8); sys_exit(0); return 0 }
138 sys_exit(1); return 1
139 }
140 _p(" unknown mode\n" as *u8)
141 sys_exit(2)
142 return 2
143}