code wiki / _hdl_build / nx_extvec_authentic_gate.nx
nx_extvec_authentic_gate.nx source
↩ module page · 351 lines · 16626 B
1// nx_extvec_authentic_gate.nx -- THE GUARD THAT WOULD HAVE CAUGHT IT ON DAY ONE, PLUS THE LEDGER.
2//
3// ★★★★★ THE LAW THIS GATE ENFORCES:
4// A DIGEST PIN PROVES INTEGRITY-SINCE-FETCH, NOT AUTHENTICITY-AT-SOURCE.
5// Every external-vector gate pins its source document to a SHA-256 computed in-process at the socket and
6// treats "PIN OK" as if it established that the bytes ARE the authority's document. It does not. A pin
7// proves only that the bytes have not changed since we hashed them. A corrupted, truncated, or -- as
8// actually happened on 2026-07-31 -- a fetch that stored the raw HTTP WIRE TRANSCRIPT instead of the
9// document, all pin exactly as cleanly as a good one. The pin is self-consistent BY CONSTRUCTION, so no
10// amount of re-checking the pin can ever reveal the fault. ★INDEPENDENCE NEEDS A SECOND STACK, NOT A
11// SECOND HASH.
12//
13// ⚠THE DEFECT THIS EXISTS TO PREVENT: knowledge/extvec/rfc2202.txt began
14// HTTP/1.1 200 OK\r\nDate: ...\r\nTransfer-Encoding: chunked\r\n ... \r\n\r\n2ea6\r\n
15// -- 707 bytes of response headers plus chunk framing -- because nx_vecfetch called the raw nx_https_get,
16// which BY CONTRACT returns the entire response, and never stripped headers or de-chunked. All 14 pinned
17// "authority documents" were transcripts. The shared decoder (nx_fetch_staged / fu_dechunk in
18// nx_fetch_unit.nx) already existed and was not adopted. ★THE ADOPTION GAP, NOT A MISSING PRIMITIVE.
19//
20// TWO INDEPENDENT CHECKS, AND THEY FAIL FOR DIFFERENT REASONS -- THAT IS THE POINT:
21// A. SHAPE -- is this file a document rather than a transcript? (content predicate, no external input)
22// B. LEDGER -- does an independent stack agree on this exact file's digest? (corroboration, external)
23// A catches the accident. B catches the substitution. A file can pass A and fail B (a DIFFERENT but
24// well-formed document swapped in), or pass B and fail A (both stacks fetched the same transcript).
25// ★A SINGLE CHECK THAT COULD HAVE CAUGHT BOTH WOULD HAVE CAUGHT NEITHER CLEANLY.
26//
27// ⚠HONEST LIMIT OF THE LEDGER, STATED SO NOBODY OVER-TRUSTS IT: CORROBORATION.tsv is a plain file this
28// agent can write. It raises the bar -- swapping a document now requires forging a matching "independent"
29// column, at which point that column is no longer independent and the forgery is recorded in-tree rather
30// than invisible -- but it is NOT a transparency log. The July-2026 answer to that is Sigstore/Fulcio/Rekor
31// or in-toto/DSSE: an append-only log held by a party we cannot edit. Nishi already has nx_coe_dsse /
32// nx_coe_intoto / nx_coe_witness and this lane has NOT adopted them. That is the next rung, not this one.
33// license_tier: ORIGINAL expect_exit: 0
34import "nx_syscalls.nx"
35import "nx_sha256_wasm.nx"
36import "nx_gate_verdict.nx" // D001 base class: gv_check/gv_verdict -- canonical verdict LAST + journal frame
37
38func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
39func wb(b: *u8, n: i64) -> i64 { sys_write(1, b, n); return 0 }
40
41func nn(v: i64) -> i64 {
42 var m: i64 = v
43 if m < 0 { w("-" as *u8); m = 0 - m }
44 let t: *u8 = sys_mmap(32)
45 var k: i64 = 0
46 if m == 0 { t[0] = 48 as u8; k = 1 }
47 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
48 let b: *u8 = sys_mmap(32)
49 var j: i64 = 0
50 while j < k { b[j] = t[k - 1 - j]; j = j + 1 }
51 sys_write(1, b, k)
52 return 0
53}
54
55func hexnib(v: i64) -> i64 { if v < 10 { return 48 + v } return 87 + v }
56
57func starts(b: *u8, n: i64, at: i64, s: *u8) -> i64 {
58 var i: i64 = 0
59 while s[i] != (0 as u8) {
60 if at + i >= n { return 0 }
61 if b[at + i] != s[i] { return 0 }
62 i = i + 1
63 }
64 return 1
65}
66
67func findfrom(b: *u8, n: i64, s: *u8, from: i64) -> i64 {
68 var p: i64 = from
69 while p < n { if starts(b, n, p, s) == 1 { return p } p = p + 1 }
70 return 0 - 1
71}
72
73// Case-insensitive search bounded to the first `lim` bytes. Header names are case-insensitive per RFC 9110,
74// so a case-sensitive probe for "Transfer-Encoding" would miss "transfer-encoding" and pass a contaminated
75// file. ★A CHECK THAT ONLY CATCHES THE SPELLING YOU SAW IS NOT A CHECK.
76func lc(c: i64) -> i64 { if c >= 65 { if c <= 90 { return c + 32 } } return c }
77func find_ci(b: *u8, n: i64, lim: i64, s: *u8) -> i64 {
78 var sl: i64 = 0
79 while s[sl] != (0 as u8) { sl = sl + 1 }
80 if sl == 0 { return 0 - 1 }
81 var top: i64 = lim
82 if top > n { top = n }
83 var i: i64 = 0
84 while i + sl <= top {
85 var j: i64 = 0
86 var ok: i64 = 1
87 while j < sl { if lc(b[i + j] as i64) != lc(s[j] as i64) { ok = 0; j = sl } else { j = j + 1 } }
88 if ok == 1 { return i }
89 i = i + 1
90 }
91 return 0 - 1
92}
93
94func count_byte(b: *u8, n: i64, v: i64) -> i64 {
95 var i: i64 = 0
96 var c: i64 = 0
97 while i < n { if (b[i] as i64) == v { c = c + 1 } i = i + 1 }
98 return c
99}
100
101func mkpath(name: *u8, out: *u8) -> i64 {
102 let pre: *u8 = "knowledge/extvec/" as *u8
103 var o: i64 = 0
104 var i: i64 = 0
105 while pre[i] != (0 as u8) { out[o] = pre[i]; o = o + 1; i = i + 1 }
106 i = 0
107 while name[i] != (0 as u8) { out[o] = name[i]; o = o + 1; i = i + 1 }
108 let suf: *u8 = ".txt" as *u8
109 i = 0
110 while suf[i] != (0 as u8) { out[o] = suf[i]; o = o + 1; i = i + 1 }
111 out[o] = 0 as u8
112 return o
113}
114
115// ---- CHECK A: SHAPE ----
116const F_OK: i64 = 0
117const F_STATUSLINE: i64 = 1
118const F_HDR_TE: i64 = 2
119const F_HDR_CL: i64 = 3
120const F_HDR_BREAK: i64 = 4
121const F_CR: i64 = 5
122
123func transcript_fault(b: *u8, n: i64) -> i64 {
124 if n <= 0 { return F_STATUSLINE }
125 if starts(b, n, 0, "HTTP/" as *u8) == 1 { return F_STATUSLINE }
126 // Bounded to 2048 bytes: these words legitimately appear in the BODY of HTTP-related RFCs (RFC 9112
127 // defines chunked encoding and quotes these very strings), so an unbounded search would convict correct
128 // documents. ★BOUND THE PROBE TO THE REGION WHERE THE SIGNATURE IS DIAGNOSTIC, OR IT CONVICTS THE INNOCENT.
129 if find_ci(b, n, 2048, "transfer-encoding:" as *u8) >= 0 { return F_HDR_TE }
130 if find_ci(b, n, 2048, "content-length:" as *u8) >= 0 { return F_HDR_CL }
131 if find_ci(b, n, 2048, "\r\n\r\n" as *u8) >= 0 { return F_HDR_BREAK }
132 if count_byte(b, n, 13) > 0 { return F_CR }
133 return F_OK
134}
135
136func faultname(f: i64) -> i64 {
137 if f == F_STATUSLINE { w("HTTP status line at offset 0 -- STORED TRANSCRIPT, not document" as *u8) }
138 if f == F_HDR_TE { w("Transfer-Encoding header in preamble -- chunk framing interleaved" as *u8) }
139 if f == F_HDR_CL { w("Content-Length header in preamble -- response headers retained" as *u8) }
140 if f == F_HDR_BREAK { w("CRLFCRLF header/body break in preamble" as *u8) }
141 if f == F_CR { w("carriage returns present -- rfc-editor serves LF-only; bytes were re-framed" as *u8) }
142 if f == F_OK { w("clean" as *u8) }
143 return 0
144}
145
146// ---- CHECK B: LEDGER ----
147const L_OK: i64 = 0
148const L_NOROW: i64 = 1
149const L_SOVMISS: i64 = 2
150const L_INDEPMISS: i64 = 3
151const L_NOTAGREED: i64 = 4
152const L_SHORT: i64 = 5
153
154// Row shape: name \t bytes \t sovereign_sha256 \t independent_sha256 \t verdict \t date
155// The row is located by "\n<name>\t" -- the TAB matters. Anchoring on the bare name would let "rfc321"
156// match inside "rfc3210". ★A PREFIX MATCH IS NOT A TOKEN MATCH (third time this workstream).
157func ledger_check(led: *u8, ln: i64, name: *u8, filedig: *u8) -> i64 {
158 if ln <= 0 { return L_NOROW }
159 let key: *u8 = sys_mmap(64)
160 var o: i64 = 0
161 key[o] = 10 as u8
162 o = 1
163 var i: i64 = 0
164 while name[i] != (0 as u8) { key[o] = name[i]; o = o + 1; i = i + 1 }
165 key[o] = 9 as u8
166 key[o + 1] = 0 as u8
167 let at: i64 = findfrom(led, ln, key, 0)
168 if at < 0 { return L_NOROW }
169 // advance past name and TAB, then past the bytes field and its TAB
170 var p: i64 = at + o + 1
171 var tabs: i64 = 0
172 while tabs < 1 {
173 if p >= ln { return L_SHORT }
174 if (led[p] as i64) == 9 { tabs = tabs + 1 }
175 p = p + 1
176 }
177 // p now at sovereign digest
178 if p + 64 > ln { return L_SHORT }
179 var k: i64 = 0
180 while k < 64 { if lc(led[p + k] as i64) != (filedig[k] as i64) { return L_SOVMISS } k = k + 1 }
181 p = p + 64
182 if p >= ln { return L_SHORT }
183 if (led[p] as i64) != 9 { return L_SHORT }
184 p = p + 1
185 // independent digest -- THE WHOLE POINT: it must equal the bytes we actually hold.
186 if p + 64 > ln { return L_SHORT }
187 k = 0
188 while k < 64 { if lc(led[p + k] as i64) != (filedig[k] as i64) { return L_INDEPMISS } k = k + 1 }
189 p = p + 64
190 if p >= ln { return L_SHORT }
191 if (led[p] as i64) != 9 { return L_SHORT }
192 p = p + 1
193 if starts(led, ln, p, "AGREED" as *u8) == 0 { return L_NOTAGREED }
194 return L_OK
195}
196
197func ledgername(f: i64) -> i64 {
198 if f == L_NOROW { w("NO LEDGER ROW -- document is UNCORROBORATED" as *u8) }
199 if f == L_SOVMISS { w("ledger sovereign digest does not match these bytes -- file changed since acquisition" as *u8) }
200 if f == L_INDEPMISS { w("INDEPENDENT digest does not match these bytes -- the second stack saw something else" as *u8) }
201 if f == L_NOTAGREED { w("ledger verdict is not AGREED" as *u8) }
202 if f == L_SHORT { w("ledger row malformed/truncated" as *u8) }
203 if f == L_OK { w("corroborated" as *u8) }
204 return 0
205}
206
207func hexdig(b: *u8, n: i64, out: *u8) -> i64 {
208 let ctx: *u8 = sys_mmap(1024)
209 let dg: *u8 = sys_mmap(64)
210 nx_sha256_one_shot(b, n, ctx, dg)
211 var i: i64 = 0
212 while i < 32 {
213 out[i * 2] = hexnib(((dg[i] as i64) / 16) & 15) as u8
214 out[i * 2 + 1] = hexnib((dg[i] as i64) & 15) as u8
215 i = i + 1
216 }
217 out[64] = 0 as u8
218 return 0
219}
220
221func main() -> i64 {
222 w("nx_extvec_authentic_gate -- are the pinned 'authority documents' actually documents, and corroborated?\n" as *u8)
223 w(" law: A DIGEST PIN PROVES INTEGRITY-SINCE-FETCH, NOT AUTHENTICITY-AT-SOURCE.\n\n" as *u8)
224
225 let pathbuf: *u8 = sys_mmap(512)
226 let lp: *i64 = sys_mmap(16) as *i64
227 let dhex: *u8 = sys_mmap(80)
228
229 // load the corroboration ledger once
230 let ctr: *i64 = gv_ctr()
231 let llp: *i64 = sys_mmap(16) as *i64
232 llp[0] = 0
233 let led: *u8 = sys_read_file("knowledge/extvec/CORROBORATION.tsv\x00" as *u8, llp)
234 // Ledger absence is a FAILING state, not a SKIP: the property under test IS "corroborated", and a
235 // missing ledger makes every document uncorroborated by definition.
236 if gv_check("T-LEDGER corroboration ledger present (knowledge/extvec/CORROBORATION.tsv)" as *u8, llp[0] > 0, ctr) == 0 {
237 return gv_verdict("EXTVEC-AUTHENTIC-GATE" as *u8, ctr, "" as *u8)
238 }
239 w(" ledger loaded: " as *u8); nn(llp[0]); w(" bytes\n\n" as *u8)
240
241 var clean: i64 = 0
242 var contam: i64 = 0
243 var missing: i64 = 0
244 var uncorro: i64 = 0
245
246 // Declared explicitly: a gate that discovers its own subjects can be silently starved to zero and still
247 // print GREEN. ★A GATE WITH NO DECLARED SUBJECT COUNT CANNOT TELL "ALL PASSED" FROM "NOTHING RAN".
248 let names: *i64 = sys_mmap(256) as *i64
249 names[0] = "rfc1321" as i64
250 names[1] = "rfc2202" as i64
251 names[2] = "rfc3174" as i64
252 names[3] = "rfc3602" as i64
253 names[4] = "rfc4231" as i64
254 names[5] = "rfc4648" as i64
255 names[6] = "rfc5869" as i64
256 names[7] = "rfc6234" as i64
257 names[8] = "rfc7693" as i64
258 names[9] = "rfc7748" as i64
259 names[10] = "rfc7914" as i64
260 names[11] = "rfc8032" as i64
261 names[12] = "rfc8439" as i64
262 names[13] = "rfc9106" as i64
263 // rfc6070 (PKCS#5 PBKDF2 test vectors) -- the FIRST document acquired through the REPAIRED pipeline:
264 // status-checked, envelope stripped (720B), de-chunked, hashed as a document, and corroborated against
265 // an independent stack BEFORE being written. Added here the moment it landed: ★A DOCUMENT THAT IS NOT
266 // IN THE SUBJECT LIST IS NOT GUARDED, AND AN UNGUARDED DOCUMENT SILENTLY DRIFTS.
267 names[14] = "rfc6070" as i64
268 // rfc4226 (HOTP) + rfc6238 (TOTP): present in knowledge/extvec/ since the 2FA sweep but never
269 // DECLARED here -- and an unguarded document silently drifts (this gate's own law above). Added
270 // 2026-08-17 alongside rfc6234/rfc9106 finally being fetched (declared but absent on disk).
271 names[15] = "rfc4226" as i64
272 names[16] = "rfc6238" as i64
273 let NDOC: i64 = 17
274
275 var i: i64 = 0
276 while i < NDOC {
277 let nm: *u8 = names[i] as *u8
278 mkpath(nm, pathbuf)
279 lp[0] = 0
280 let b: *u8 = sys_read_file(pathbuf, lp)
281 if lp[0] <= 0 {
282 w(" MISSING " as *u8); w(nm); w(" (file absent -- cannot grade)\n" as *u8)
283 missing = missing + 1
284 } else {
285 let f: i64 = transcript_fault(b, lp[0])
286 hexdig(b, lp[0], dhex)
287 let lf: i64 = ledger_check(led, llp[0], nm, dhex)
288 if f != F_OK {
289 w(" CONTAM " as *u8); w(nm); w(" fault=" as *u8); faultname(f); w("\n" as *u8)
290 contam = contam + 1
291 } else {
292 if lf != L_OK {
293 w(" UNCORRO " as *u8); w(nm); w(" " as *u8); ledgername(lf); w("\n" as *u8)
294 uncorro = uncorro + 1
295 } else {
296 w(" OK " as *u8); w(nm); w(" bytes=" as *u8); nn(lp[0])
297 w(" shape=document corroboration=AGREED\n" as *u8)
298 clean = clean + 1
299 }
300 }
301 }
302 i = i + 1
303 }
304
305 // ---- NON-VACUITY: BOTH checks must be shown able to FAIL, and able NOT to false-positive. ----
306 // A guard never shown to reject anything is indistinguishable from a guard that rejects nothing.
307 w("\n -- non-vacuity: each check must discriminate, in both directions --\n" as *u8)
308
309 let bad: *u8 = "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n2ea6\r\nNetwork Working Group\x00" as *u8
310 var bl: i64 = 0
311 while bad[bl] != (0 as u8) { bl = bl + 1 }
312 gv_check("neg-control-shape: synthetic wire transcript REJECTED (a blind predicate would accept it)" as *u8, transcript_fault(bad, bl) != F_OK, ctr)
313
314 let good: *u8 = "Network Working Group P. Cheng\nRequest for Comments: 2202\n\n test_case = 1\n\x00" as *u8
315 var gl: i64 = 0
316 while good[gl] != (0 as u8) { gl = gl + 1 }
317 gv_check("shape positive-control: synthetic document ACCEPTED, no false positive" as *u8, transcript_fault(good, gl) == F_OK, ctr)
318
319 // prose mention past the bound must not trip the preamble probes
320 let pad: *u8 = sys_mmap(8192)
321 var p2: i64 = 0
322 while p2 < 3000 { pad[p2] = 32 as u8; p2 = p2 + 1 }
323 let tail: *u8 = "Transfer-Encoding: chunked\x00" as *u8
324 var q: i64 = 0
325 while tail[q] != (0 as u8) { pad[3000 + q] = tail[q]; q = q + 1 }
326 gv_check("shape bound: body-legitimate 'Transfer-Encoding:' past the preamble NOT convicted" as *u8, transcript_fault(pad, 3000 + q) == F_OK, ctr)
327
328 // LEDGER must reject an unknown name, and must reject a KNOWN name whose bytes changed.
329 gv_check("neg-control-ledger: unknown document name REJECTED, no row" as *u8, ledger_check(led, llp[0], "rfc0000" as *u8, dhex) == L_NOROW, ctr)
330
331 let fake: *u8 = sys_mmap(80)
332 var z: i64 = 0
333 while z < 64 { fake[z] = 97 as u8; z = z + 1 }
334 fake[64] = 0 as u8
335 gv_check("neg-control-ledger: known name with WRONG bytes REJECTED (a decorative ledger accepts)" as *u8, ledger_check(led, llp[0], "rfc2202" as *u8, fake) == L_SOVMISS, ctr)
336
337 w("\n ref=CABF-MPIC+SIGSTORE-REKOR gate=nx_extvec_authentic_gate\n" as *u8)
338 w(" SCOPE: shape-check proves absence of ONE observed contamination class; ledger proves a second\n" as *u8)
339 w(" stack agreed on THESE EXACT BYTES. The ledger is agent-writable and is NOT a transparency\n" as *u8)
340 w(" log -- Sigstore/Rekor or in-toto/DSSE is the next rung, and is NOT claimed here.\n" as *u8)
341 w("nx_extvec_authentic_gate: docs=" as *u8); nn(NDOC)
342 w(" ok=" as *u8); nn(clean)
343 w(" CONTAMINATED=" as *u8); nn(contam)
344 w(" UNCORROBORATED=" as *u8); nn(uncorro)
345 w(" missing=" as *u8); nn(missing)
346 w("\n" as *u8)
347 gv_check("T-DOCS every declared subject document present on disk" as *u8, missing == 0, ctr)
348 gv_check("T-SHAPE zero pinned documents carrying wire-transcript contamination" as *u8, contam == 0, ctr)
349 gv_check("T-CORRO zero documents lacking two-stack corroboration" as *u8, uncorro == 0, ctr)
350 return gv_verdict("EXTVEC-AUTHENTIC-GATE" as *u8, ctr, "" as *u8)
351}