code wiki / _hdl_build / nx_coe_frame.nx

nx_coe_frame.nx source

↩ module page · 288 lines · 9724 B

1// nx_coe_frame.nx -- CHAIN-OF-EVIDENCE FRAME (F701 keystone + F702 signature, v1) 2// Emits a 5-pillar evidence frame as an ed25519-SIGNED row on an append-only CID hash-chain, then 3// VERIFIES the whole chain deny-by-default: re-hash every row (integrity) + ed25519_verify every 4// signature (authenticity) + check prev-hash linkage (chain). A forged OR tampered row -> RED. 5// Pillars: provenance | custody | environment | content-CID (immutability) | corroboration (re-hash + sig). 6// v1 signs with a dedicated deterministic CoE key (RFC-8032 ed25519, reproducible; pubkey printed). 7// Honest: production key custody (offline private seed / vault) = F702b. Append-only, never deletes. 8// Composes proven libs: nx_str/nx_syscalls/nx_sha256 + nx_ed25519_signature (via nx_fw_capsule lineage). 9// x86-lane. license_tier: ORIGINAL 10import "nx_str.nx" 11import "nx_itoa_lib.nx" // shared MSB-first emitter (zero-alloc) 12import "nx_syscalls.nx" 13import "nx_sha256.nx" 14import "nx_ed25519_signature.nx" 15const CF_MAGIC_1024: i64 = 1024 16const CF_MAGIC_1500: i64 = 1500 17 18const CF_CAP: i64 = 1048576 19const CF_MAXROWS: i64 = 4096 20 21func cf_puts(s: *u8) -> i64 { sys_write(1, s, nx_str_len(s)); return 0 } 22 23// MIGRATED to the shared emitter (debt 1785563586). The old body mmapped a scratch buffer 24// per call and never freed it. At PAGE granularity that is 4096B leaked PER CALL -- the 25// defect that took 28.5GB of a 36GB host in nx_ts_lumadiff (2MB input, ~3.66M calls). 26// nxi_* is MSB-first, allocates NOTHING, and emits identical bytes including the sign. 27func cf_pi(v: i64) -> i64 { nxi_out(v); return 0 } 28 29func cf_cat(dst: *u8, off: i64, s: *u8) -> i64 { 30 var i: i64 = 0 31 while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } 32 return off + i 33} 34 35func cf_catn(dst: *u8, off: i64, v: i64) -> i64 { 36 let t: *u8 = sys_mmap(32) 37 var m: i64 = v 38 var k: i64 = 0 39 if m == 0 { t[0] = 48 as u8; k = 1 } 40 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 41 var i: i64 = 0 42 while i < k { dst[off+i] = t[k-1-i]; i = i + 1 } 43 return off + k 44} 45 46// n bytes -> 2n lowercase hex into out, NUL-terminated 47func cf_hexn(d: *u8, n: i64, out: *u8) -> i64 { 48 let hx: *u8 = "0123456789abcdef" as *u8 49 var i: i64 = 0 50 while i < n { 51 let b: i64 = d[i] as i64 52 out[i*2] = hx[(b >> 4) & 15] 53 out[i*2+1] = hx[b & 15] 54 i = i + 1 55 } 56 out[n*2] = 0 as u8 57 return n*2 58} 59 60func cf_nib(c: i64) -> i64 { 61 if c >= 97 { return c - 87 } 62 return c - 48 63} 64 65// 2n hex chars in -> n bytes into out 66func cf_unhex(hexin: *u8, hexlen: i64, out: *u8) -> i64 { 67 var i: i64 = 0 68 let n: i64 = hexlen / 2 69 while i < n { 70 let hi: i64 = cf_nib(hexin[i*2] as i64) 71 let lo: i64 = cf_nib(hexin[i*2+1] as i64) 72 out[i] = ((hi * 16) + lo) as u8 73 i = i + 1 74 } 75 return n 76} 77 78func cf_hash_hex(buf: *u8, n: i64, out: *u8) -> i64 { 79 let dg: *u8 = sys_mmap(32) 80 sha256_digest(buf, n, dg) 81 cf_hexn(dg, 32, out) 82 return 64 83} 84 85func cf_eq(a: *u8, b: *u8) -> i64 { 86 var i: i64 = 0 87 while a[i] != (0 as u8) { 88 if a[i] != b[i] { return 0 } 89 i = i + 1 90 } 91 if b[i] != (0 as u8) { return 0 } 92 return 1 93} 94 95func cf_copy(dst: *u8, src: *u8) -> i64 { 96 var i: i64 = 0 97 while src[i] != (0 as u8) { dst[i] = src[i]; i = i + 1 } 98 dst[i] = 0 as u8 99 return i 100} 101 102func cf_line_end(b: *u8, i0: i64, n: i64) -> i64 { 103 var e: i64 = i0 104 while e < n { if b[e] == (10 as u8) { return e } e = e + 1 } 105 return n 106} 107 108func cf_find_tab(b: *u8, i0: i64, e: i64) -> i64 { 109 var j: i64 = i0 110 while j < e { if b[j] == (9 as u8) { return j } j = j + 1 } 111 return e 112} 113 114func cf_slice(b: *u8, i0: i64, e: i64, dst: *u8) -> i64 { 115 var o: i64 = 0 116 var j: i64 = i0 117 while j < e { dst[o] = b[j]; o = o + 1; j = j + 1 } 118 dst[o] = 0 as u8 119 return o 120} 121 122// dedicated CoE signing seed (deterministic v1; distinct from the platform key) 123func cf_coe_seed(out: *u8) -> i64 { 124 var i: i64 = 0 125 while i < 32 { out[i] = ((i * 11 + 29) & 0xff) as u8; i = i + 1 } 126 return 0 127} 128 129// scan plane [0,n): copy last row's frame-hash (field 0) into lastfh (or GENESIS); return row count 130func cf_scan_last(vb: *u8, n: i64, lastfh: *u8) -> i64 { 131 cf_copy(lastfh, "GENESIS" as *u8) 132 var i: i64 = 0 133 var rows: i64 = 0 134 while i < n { 135 let e: i64 = cf_line_end(vb, i, n) 136 if e > i { 137 let t: i64 = cf_find_tab(vb, i, e) 138 if t < e { cf_slice(vb, i, t, lastfh); rows = rows + 1 } 139 } 140 i = e + 1 141 } 142 return rows 143} 144 145func main() -> i64 { 146 cf_puts("=== COE-FRAME v1 (F701+F702): ed25519-signed 5-pillar CID hash-chain ===\n" as *u8) 147 let plane: *u8 = "knowledge/status/coe_signed.log" as *u8 148 149 let seed: *u8 = sys_mmap(32) 150 cf_coe_seed(seed) 151 let pub: *u8 = sys_mmap(32) 152 ed25519_pub_from_priv(seed, pub) 153 let pubhex: *u8 = sys_mmap(72) 154 cf_hexn(pub, 32, pubhex) 155 156 let lenp: *i64 = sys_mmap(16) as *i64 157 lenp[0] = 0 158 let buf: *u8 = sys_read_file(plane, lenp) 159 var n: i64 = lenp[0] 160 if n < 0 { n = 0 } 161 if n > CF_CAP { n = CF_CAP } 162 163 let prevfh: *u8 = sys_mmap(80) 164 let seq: i64 = cf_scan_last(buf, n, prevfh) 165 166 let now: i64 = sys_now_realtime_sec() 167 let pay: *u8 = sys_mmap(256) 168 var po: i64 = 0 169 po = cf_cat(pay, po, "coe-selftest-payload-" as *u8) 170 po = cf_catn(pay, po, now) 171 pay[po] = 0 as u8 172 let cid: *u8 = sys_mmap(80) 173 cf_hash_hex(pay, po, cid) 174 175 // record = prev \t cid \t seq \t now \t pillars ; fh = sha256(record) ; sig = ed25519(record) 176 let rec: *u8 = sys_mmap(CF_MAGIC_1024) 177 var ro: i64 = 0 178 ro = cf_cat(rec, ro, prevfh) 179 rec[ro] = 9 as u8; ro = ro + 1 180 ro = cf_cat(rec, ro, cid) 181 rec[ro] = 9 as u8; ro = ro + 1 182 ro = cf_catn(rec, ro, seq) 183 rec[ro] = 9 as u8; ro = ro + 1 184 ro = cf_catn(rec, ro, now) 185 rec[ro] = 9 as u8; ro = ro + 1 186 ro = cf_cat(rec, ro, "prov=organ:nx_coe_frame;cust=cap;env=nas;corr=rehash+ed25519" as *u8) 187 rec[ro] = 0 as u8 188 189 let fh: *u8 = sys_mmap(80) 190 cf_hash_hex(rec, ro, fh) 191 let sig: *u8 = sys_mmap(64) 192 ed25519_sign_full(seed, rec, ro, sig) 193 let sighex: *u8 = sys_mmap(160) 194 cf_hexn(sig, 64, sighex) 195 196 // line = fh \t sighex \t rec \n 197 let line: *u8 = sys_mmap(CF_MAGIC_1500) 198 var lo: i64 = 0 199 lo = cf_cat(line, lo, fh) 200 line[lo] = 9 as u8; lo = lo + 1 201 lo = cf_cat(line, lo, sighex) 202 line[lo] = 9 as u8; lo = lo + 1 203 lo = cf_cat(line, lo, rec) 204 line[lo] = 10 as u8; lo = lo + 1 205 206 let fd: i64 = sys_openat_append(plane, 0x1a4) 207 if fd < 0 { cf_puts("COE-FRAME RED reason=no-plane\n" as *u8); sys_exit(1); return 1 } 208 sys_write(fd, line, lo) 209 sys_close(fd) 210 cf_puts(" emitted seq=" as *u8); cf_pi(seq) 211 cf_puts(" cid=" as *u8); cf_puts(cid) 212 cf_puts(" frame=" as *u8); cf_puts(fh) 213 cf_puts(" sig=" as *u8); cf_puts(sighex); cf_puts("\n" as *u8) 214 215 // --- VERIFY whole chain: integrity (re-hash) + authenticity (ed25519) + linkage --- 216 lenp[0] = 0 217 let vb: *u8 = sys_read_file(plane, lenp) 218 var vn: i64 = lenp[0] 219 if vn < 0 { vn = 0 } 220 var truncated: i64 = 0 221 if vn > CF_CAP { vn = CF_CAP; truncated = 1 } 222 223 let sfh: *u8 = sys_mmap(80) 224 let rfh: *u8 = sys_mmap(80) 225 let shx: *u8 = sys_mmap(160) 226 let sbytes: *u8 = sys_mmap(64) 227 let pfld: *u8 = sys_mmap(80) 228 let carry: *u8 = sys_mmap(80) 229 cf_copy(carry, "GENESIS" as *u8) 230 var i: i64 = 0 231 var rows: i64 = 0 232 var okhash: i64 = 1 233 var oksig: i64 = 1 234 var oklink: i64 = 1 235 var capped: i64 = 0 236 while i < vn { 237 let e: i64 = cf_line_end(vb, i, vn) 238 if e > i { 239 if rows < CF_MAXROWS { 240 let t1: i64 = cf_find_tab(vb, i, e) 241 if t1 < e { 242 let t2: i64 = cf_find_tab(vb, t1 + 1, e) 243 if t2 < e { 244 cf_slice(vb, i, t1, sfh) 245 cf_slice(vb, t1 + 1, t2, shx) 246 let rs: i64 = t2 + 1 247 let reclen: i64 = e - rs 248 let recp: *u8 = ((vb as i64) + rs) as *u8 249 cf_hash_hex(recp, reclen, rfh) 250 if cf_eq(sfh, rfh) == 0 { okhash = 0 } 251 cf_unhex(shx, 128, sbytes) 252 if ed25519_verify_full(pub, recp, reclen, sbytes) == NX_ED25519_SIG_OK { 253 } else { oksig = 0 } 254 let pt: i64 = cf_find_tab(vb, rs, e) 255 cf_slice(vb, rs, pt, pfld) 256 if cf_eq(pfld, carry) == 0 { oklink = 0 } 257 cf_copy(carry, sfh) 258 rows = rows + 1 259 } 260 } 261 } 262 if rows >= CF_MAXROWS { capped = 1 } 263 } 264 i = e + 1 265 } 266 267 cf_puts(" pubkey=" as *u8); cf_puts(pubhex); cf_puts("\n" as *u8) 268 if okhash == 1 { 269 if oksig == 1 { 270 if oklink == 1 { 271 cf_puts("COE-FRAME GREEN frames=" as *u8); cf_pi(rows) 272 cf_puts(" chain=OK signed=ed25519 sig-verify=OK bytes=" as *u8); cf_pi(vn) 273 cf_puts(" envelope=cap:" as *u8); cf_pi(CF_CAP) 274 cf_puts("/maxrows:" as *u8); cf_pi(CF_MAXROWS) 275 if truncated == 1 { cf_puts(" TRUNCATED" as *u8) } 276 if capped == 1 { cf_puts(" ROWCAP" as *u8) } 277 cf_puts("\n" as *u8) 278 return 0 279 } 280 } 281 } 282 cf_puts("COE-FRAME RED hash=" as *u8); cf_pi(okhash) 283 cf_puts(" sig=" as *u8); cf_pi(oksig) 284 cf_puts(" link=" as *u8); cf_pi(oklink) 285 cf_puts(" frames=" as *u8); cf_pi(rows); cf_puts("\n" as *u8) 286 sys_exit(1) 287 return 1 288}