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}