nx_content_get_gate.nx source
↩ module page · 326 lines · 15714 B
1// nx_content_get_gate.nx -- THE GATE FOR THE NAS->LAPTOP DOOR, 2026-09-03.
2//
3// SUBJECT: the BUILT nx_content_get elf, forked end-to-end. Its verbs are the whole contract, so an
4// in-process gate could not test the thing that matters (argv parsing, exit codes, the wire receipt).
5// argv[1] overrides the subject path so nx_gate_bite can hand it a mutant.
6//
7// THE TEETH THAT CARRY THE CLAIM ARE T4/T5 AND T10, and each is a law this estate has been bitten by:
8// T4 IS A FIXTURE ASSERTION, NOT AN OUTCOME. It proves the big fixture actually forced nchunks>1
9// BEFORE T5 judges reassembly. Without it, a subject that fits in one chunk would let T5 pass
10// while the multi-chunk path -- the only path that can corrupt -- was never executed. The
11// single-chunk case looks identical to a working transfer from the outside.
12// T10 IS THE POSITIVE CONTROL FOR THE REFUSALS. T7/T8/T9 all assert a REFUSAL, and a guard that
13// refuses EVERYTHING passes every one of them. T10 demands a legitimate path be ACCEPTED, so
14// the deny-tests can only pass on a guard that actually discriminates.
15//
16// T3 uses the gate's OWN one-shot sha256_digest against the tool's STREAMING sha256_update path --
17// genuinely different code, so it is a cross-check rather than the subject grading itself.
18// T2 re-reads chunk_raw FROM THE PROBE rather than re-deriving it here: re-deriving would install a
19// second ruler that drifts the moment the reserve changes.
20//
21// Teeth, in order:
22// T1 the probe partition RECONCILES: skel+pathcap+digits+shahex == reserve.
23// T2 chunk_raw is positive and an exact multiple of 3 (so base64 emits no interior padding).
24// T3 begin's whole-file digest equals an INDEPENDENTLY computed digest of the same bytes.
25// T4 FIXTURE ASSERTION: the large fixture forces nchunks > 1.
26// T5 multi-chunk reassembly is byte-identical to the original.
27// T6 the partition SUMS: the per-chunk raw sizes total exactly total_bytes.
28// T7 NEG-CONTROL: a path-traversal source is REFUSED (exit 4).
29// T8 NEG-CONTROL: a source outside the allowlist is REFUSED (exit 4).
30// T9 NEG-CONTROL: credential material is REFUSED (exit 4).
31// T10 POSITIVE CONTROL: an allowlisted source is ACCEPTED -- without this T7/T9 pass on a guard
32// that refuses everything.
33// T11 an index past end of file is a RANGE refusal (exit 5), distinct from a source refusal.
34// MEASURED 11/11 GREEN 2026-09-03 on the laptop, bite-proven killed=1 with the source and the
35// deployed subject both restored byte-identical (12665 / 40212 bytes).
36// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
37import "nx_syscalls.nx"
38import "nx_gate_verdict.nx"
39import "nx_tool_run.nx"
40import "nx_base64.nx"
41import "nx_sha256.nx"
42
43// THE SUBJECT PATH IS RESOLVED BY STAT, NEVER ASSUMED. The same build lands at a different relative
44// path depending on the CWD of whoever forks the gate -- the estate measured that hidden variable
45// giving one command opposite effects. So every candidate is probed and the one taken is ANNOUNCED.
46// If none exists the gate ABSTAINS: a fork of a missing elf exits 127, which is indistinguishable
47// from a subject that ran and failed, and convicting a subject you could not reach indicts the wrong
48// thing. "I could not look" is not "it is broken".
49const CGG_SUBJECT: *u8 = "buildroot/_build/nx_content_get.sov.elf" as *u8
50const CGG_SUBJ_B: *u8 = "_build/nx_content_get.sov.elf" as *u8
51const CGG_SUBJ_C: *u8 = "_offc/nx_content_get.elf" as *u8
52const CGG_SUBJ_D: *u8 = "nx_content_get.elf" as *u8
53const CGG_DIR: *u8 = "/tmp/cgget" as *u8
54const CGG_SMALL: *u8 = "/tmp/cgget/small.bin" as *u8
55const CGG_BIG: *u8 = "/tmp/cgget/big.bin" as *u8
56const CGG_CAP: i64 = 220000
57const CGG_DIGEST: i64 = 32
58const CGG_SHAHEX: i64 = 64
59const CGG_SMALL_N: i64 = 4096
60const CGG_BIG_EXTRA: i64 = 3000
61const CGG_MODE: i64 = 420
62
63func cgg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
64
65// find `needle` in buf[0..n); returns index just past it, or -1. Loop exits on a FLAG, never by
66// clobbering the cursor -- the eol-sentinel defect this estate wrote four times in one day.
67func cgg_find(buf: *u8, n: i64, needle: *u8) -> i64 {
68 let m: i64 = cgg_slen(needle)
69 if m == 0 { return 0 - 1 }
70 var i: i64 = 0
71 var hit: i64 = 0 - 1
72 while i + m <= n {
73 var j: i64 = 0
74 var same: i64 = 1
75 while j < m {
76 if buf[i + j] != needle[j] { same = 0; j = m } else { j = j + 1 }
77 }
78 if same == 1 { if hit < 0 { hit = i + m } }
79 i = i + 1
80 }
81 return hit
82}
83func cgg_int_after(buf: *u8, n: i64, key: *u8) -> i64 {
84 let p: i64 = cgg_find(buf, n, key)
85 if p < 0 { return 0 - 1 }
86 var i: i64 = p
87 var v: i64 = 0
88 var got: i64 = 0
89 var go: i64 = 1
90 while go == 1 {
91 if i >= n { go = 0 } else {
92 let c: i64 = buf[i] as i64
93 if c < 48 { go = 0 } else {
94 if c > 57 { go = 0 } else { v = v * 10 + (c - 48); got = 1; i = i + 1 }
95 }
96 }
97 }
98 if got == 0 { return 0 - 1 }
99 return v
100}
101func cgg_hex_into(d: *u8, out: *u8) -> i64 {
102 var i: i64 = 0
103 while i < CGG_DIGEST {
104 let v: i64 = d[i] as i64
105 let hi: i64 = (v >> 4) & 15
106 let lo: i64 = v & 15
107 if hi < 10 { out[i * 2] = (48 + hi) as u8 } else { out[i * 2] = (87 + hi) as u8 }
108 if lo < 10 { out[i * 2 + 1] = (48 + lo) as u8 } else { out[i * 2 + 1] = (87 + lo) as u8 }
109 i = i + 1
110 }
111 out[CGG_SHAHEX] = 0 as u8
112 return 0
113}
114// compare the 64 hex chars at buf[pos..] against `hex`
115func cgg_hex_eq(buf: *u8, n: i64, pos: i64, hex: *u8) -> i64 {
116 if pos < 0 { return 0 }
117 if pos + CGG_SHAHEX > n { return 0 }
118 var i: i64 = 0
119 while i < CGG_SHAHEX {
120 if buf[pos + i] != hex[i] { return 0 }
121 i = i + 1
122 }
123 return 1
124}
125
126func cgg_write_file(path: *u8, buf: *u8, n: i64) -> i64 {
127 let fd: i64 = sys_openat_wr(path, CGG_MODE)
128 if fd < 0 { return 0 - 1 }
129 var off: i64 = 0
130 var go: i64 = 1
131 while go == 1 {
132 if off >= n { go = 0 } else {
133 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off)
134 if w <= 0 { go = 0 } else { off = off + w }
135 }
136 }
137 sys_close(fd)
138 return off
139}
140
141func cgg_exists(path: *u8) -> i64 {
142 let fd: i64 = sys_openat_rd(path)
143 if fd < 0 { return 0 }
144 sys_close(fd)
145 return 1
146}
147func cgg_resolve() -> *u8 {
148 if cgg_exists(CGG_SUBJECT) == 1 { return CGG_SUBJECT }
149 if cgg_exists(CGG_SUBJ_B) == 1 { return CGG_SUBJ_B }
150 if cgg_exists(CGG_SUBJ_C) == 1 { return CGG_SUBJ_C }
151 if cgg_exists(CGG_SUBJ_D) == 1 { return CGG_SUBJ_D }
152 return 0 as *u8
153}
154
155func cgg_run(subj: *u8, a1: *u8, a2: *u8, a3: *u8, out: *u8, ol: *i64) -> i64 {
156 let av: *i64 = sys_mmap(64) as *i64
157 av[0] = subj as i64
158 av[1] = a1 as i64
159 var k: i64 = 2
160 if a2 as i64 != 0 { av[2] = a2 as i64; k = 3 }
161 if a3 as i64 != 0 { av[3] = a3 as i64; k = 4 }
162 av[k] = 0
163 return tr_run_capture(subj, av, out, CGG_CAP, ol)
164}
165
166func main(argc: i64, argv: *i64) -> i64 {
167 let ctr: *i64 = gv_ctr()
168 gv_head("nx_content_get gate -- the NAS->laptop door: the multi-chunk path, the partition, and a guard that must not refuse everything" as *u8)
169
170 var subj: *u8 = cgg_resolve()
171 if argc > 1 { subj = argv[1] as *u8 }
172 if subj as i64 == 0 {
173 gv_need("a-built-nx_content_get-artifact-on-any-known-path" as *u8, 0, ctr)
174 return gv_verdict("content_get" as *u8, ctr, "ABSTAINED: no built subject found on any candidate path, so nothing was measured -- a fork of a missing elf exits 127 and would have convicted a subject that never ran" as *u8)
175 }
176 gv_puts(" subject resolved by stat: " as *u8); gv_puts(subj); gv_puts("\n" as *u8)
177
178 sys_mkdir(CGG_DIR, 0x1ff)
179 let out: *u8 = sys_mmap(CGG_CAP + 16)
180 let ol: *i64 = sys_mmap(16) as *i64
181
182 // ---- T1 the probe partition reconciles ----
183 let rc0: i64 = cgg_run(subj, "probe" as *u8, 0 as *u8, 0 as *u8, out, ol)
184 let n0: i64 = *ol
185 let skel: i64 = cgg_int_after(out, n0, "skel_measured=" as *u8)
186 let pcap: i64 = cgg_int_after(out, n0, "pathcap=" as *u8)
187 let digf: i64 = cgg_int_after(out, n0, "digit_fields=" as *u8)
188 let shex: i64 = cgg_int_after(out, n0, "shahex=" as *u8)
189 let resv: i64 = cgg_int_after(out, n0, "reserve=" as *u8)
190 let craw: i64 = cgg_int_after(out, n0, "chunk_raw=" as *u8)
191 gv_puts(" [T1] skel=" as *u8); gv_num(skel)
192 gv_puts(" pathcap=" as *u8); gv_num(pcap)
193 gv_puts(" digits=" as *u8); gv_num(digf)
194 gv_puts(" shahex=" as *u8); gv_num(shex)
195 gv_puts(" reserve=" as *u8); gv_num(resv)
196 gv_puts(" sum=" as *u8); gv_num(skel + pcap + digf + shex); gv_puts("\n" as *u8)
197 var t1: i64 = 0
198 if resv > 0 { if skel + pcap + digf + shex == resv { t1 = 1 } }
199 gv_check("the-probe-partition-RECONCILES-skel-plus-pathcap-plus-digits-plus-shahex-equals-reserve" as *u8, t1, ctr)
200
201 // ---- T2 chunk_raw positive and a multiple of 3 (read FROM the probe, never re-derived here) ----
202 gv_puts(" [T2] chunk_raw=" as *u8); gv_num(craw)
203 gv_puts(" mod3=" as *u8); if craw > 0 { gv_num(craw % 3) } else { gv_num(0 - 1) }
204 gv_puts("\n" as *u8)
205 var t2: i64 = 0
206 if craw > 0 { if craw % 3 == 0 { t2 = 1 } }
207 gv_check("chunk_raw-is-positive-and-an-exact-multiple-of-3 (so base64 emits no interior padding)" as *u8, t2, ctr)
208
209 // ---- build fixtures ----
210 let sbuf: *u8 = sys_mmap(CGG_SMALL_N + 16)
211 var i: i64 = 0
212 while i < CGG_SMALL_N { sbuf[i] = (65 + (i % 26)) as u8; i = i + 1 }
213 cgg_write_file(CGG_SMALL, sbuf, CGG_SMALL_N)
214
215 var bign: i64 = CGG_SMALL_N
216 if craw > 0 { bign = craw + CGG_BIG_EXTRA }
217 let bbuf: *u8 = sys_mmap(bign + 16)
218 var b: i64 = 0
219 while b < bign { bbuf[b] = ((b * 7 + 13) % 251) as u8; b = b + 1 }
220 cgg_write_file(CGG_BIG, bbuf, bign)
221
222 // ---- T3 begin's digest vs an INDEPENDENT one-shot digest ----
223 let rc1: i64 = cgg_run(subj, "begin" as *u8, CGG_SMALL, 0 as *u8, out, ol)
224 let n1: i64 = *ol
225 let d: *u8 = sys_mmap(CGG_DIGEST + 8)
226 sha256_digest(sbuf, CGG_SMALL_N, d)
227 let myhex: *u8 = sys_mmap(CGG_SHAHEX + 8)
228 cgg_hex_into(d, myhex)
229 let hp: i64 = cgg_find(out, n1, "sha256=" as *u8)
230 let tot1: i64 = cgg_int_after(out, n1, "total_bytes=" as *u8)
231 gv_puts(" [T3] independent=" as *u8); gv_puts(myhex)
232 gv_puts(" total_bytes=" as *u8); gv_num(tot1); gv_puts("\n" as *u8)
233 var t3: i64 = 0
234 if rc1 == 0 { if tot1 == CGG_SMALL_N { if cgg_hex_eq(out, n1, hp, myhex) == 1 { t3 = 1 } } }
235 gv_check("begin-whole-file-digest-equals-an-INDEPENDENTLY-computed-digest (one-shot here vs streaming there)" as *u8, t3, ctr)
236
237 // ---- T4 FIXTURE ASSERTION: the big fixture forces more than one chunk ----
238 let rc2: i64 = cgg_run(subj, "begin" as *u8, CGG_BIG, 0 as *u8, out, ol)
239 let n2: i64 = *ol
240 let nch: i64 = cgg_int_after(out, n2, "nchunks=" as *u8)
241 let totb: i64 = cgg_int_after(out, n2, "total_bytes=" as *u8)
242 sha256_digest(bbuf, bign, d)
243 let bighex: *u8 = sys_mmap(CGG_SHAHEX + 8)
244 cgg_hex_into(d, bighex)
245 gv_puts(" [T4] fixture_bytes=" as *u8); gv_num(bign)
246 gv_puts(" total_bytes=" as *u8); gv_num(totb)
247 gv_puts(" nchunks=" as *u8); gv_num(nch); gv_puts("\n" as *u8)
248 var t4: i64 = 0
249 if nch > 1 { if totb == bign { t4 = 1 } }
250 gv_check("FIXTURE-REACHED-THE-CONDITION-the-large-fixture-forces-nchunks-above-one (without this T5 judges a path that never ran)" as *u8, t4, ctr)
251
252 // ---- T5 + T6 reassemble every chunk, and check the sizes SUM ----
253 let acc: *u8 = sys_mmap(bign + 64)
254 let dec: *u8 = sys_mmap(CGG_CAP + 16)
255 var accn: i64 = 0
256 var sumraw: i64 = 0
257 var ok5: i64 = 1
258 var ix: i64 = 0
259 let ibuf: *u8 = sys_mmap(32)
260 while ix < nch {
261 var m: i64 = ix
262 var q: i64 = 31
263 if m == 0 { q = q - 1; ibuf[q] = 48 as u8 }
264 while m > 0 { q = q - 1; ibuf[q] = (48 + (m % 10)) as u8; m = m / 10 }
265 ibuf[31] = 0 as u8
266 let rcx: i64 = cgg_run(subj, "chunk" as *u8, CGG_BIG, (ibuf as i64 + q) as *u8, out, ol)
267 let nx: i64 = *ol
268 if rcx != 0 { ok5 = 0 } else {
269 let rawn: i64 = cgg_int_after(out, nx, " raw=" as *u8)
270 if rawn > 0 { sumraw = sumraw + rawn }
271 let bp: i64 = cgg_find(out, nx, "b64=" as *u8)
272 if bp < 0 { ok5 = 0 } else {
273 var e: i64 = bp
274 var go: i64 = 1
275 while go == 1 {
276 if e >= nx { go = 0 } else {
277 if out[e] == (10 as u8) { go = 0 } else { e = e + 1 }
278 }
279 }
280 let dn: i64 = b64_decode((out as i64 + bp) as *u8, e - bp, dec)
281 var c: i64 = 0
282 while c < dn { acc[accn + c] = dec[c]; c = c + 1 }
283 accn = accn + dn
284 }
285 }
286 ix = ix + 1
287 }
288 sha256_digest(acc, accn, d)
289 let acchex: *u8 = sys_mmap(CGG_SHAHEX + 8)
290 cgg_hex_into(d, acchex)
291 gv_puts(" [T5] reassembled_bytes=" as *u8); gv_num(accn)
292 gv_puts(" digest=" as *u8); gv_puts(acchex); gv_puts("\n" as *u8)
293 gv_puts(" original_digest=" as *u8); gv_puts(bighex); gv_puts("\n" as *u8)
294 var t5: i64 = 0
295 if ok5 == 1 { if accn == bign { if cgg_hex_eq(acchex, CGG_SHAHEX, 0, bighex) == 1 { t5 = 1 } } }
296 gv_check("multi-chunk-reassembly-is-byte-identical-to-the-original" as *u8, t5, ctr)
297
298 gv_puts(" [T6] sum_of_chunk_raw=" as *u8); gv_num(sumraw)
299 gv_puts(" total_bytes=" as *u8); gv_num(totb); gv_puts("\n" as *u8)
300 gv_check("the-partition-SUMS-per-chunk-raw-sizes-total-exactly-total_bytes" as *u8, (sumraw == totb) as i64, ctr)
301
302 // ---- T7/T8/T9 the refusals ----
303 let rc7: i64 = cgg_run(subj, "begin" as *u8, "buildroot/runtime/../../etc/passwd" as *u8, 0 as *u8, out, ol)
304 gv_puts(" [T7] traversal -> exit=" as *u8); gv_num(rc7); gv_puts("\n" as *u8)
305 gv_check("neg-control-a-path-traversal-source-is-REFUSED" as *u8, (rc7 == 4) as i64, ctr)
306
307 let rc8: i64 = cgg_run(subj, "begin" as *u8, "_offc/nx_cc.elf" as *u8, 0 as *u8, out, ol)
308 gv_puts(" [T8] outside allowlist -> exit=" as *u8); gv_num(rc8); gv_puts("\n" as *u8)
309 gv_check("neg-control-a-source-outside-the-allowlist-is-REFUSED" as *u8, (rc8 == 4) as i64, ctr)
310
311 let rc9: i64 = cgg_run(subj, "begin" as *u8, "knowledge/seat.cap" as *u8, 0 as *u8, out, ol)
312 gv_puts(" [T9] credential material -> exit=" as *u8); gv_num(rc9); gv_puts("\n" as *u8)
313 gv_check("neg-control-credential-material-is-REFUSED-by-name" as *u8, (rc9 == 4) as i64, ctr)
314
315 // ---- T10 THE POSITIVE CONTROL for T7/T8/T9 ----
316 let rc10: i64 = cgg_run(subj, "begin" as *u8, CGG_SMALL, 0 as *u8, out, ol)
317 gv_puts(" [T10] allowlisted source -> exit=" as *u8); gv_num(rc10); gv_puts("\n" as *u8)
318 gv_check("POSITIVE-CONTROL-an-allowlisted-source-is-ACCEPTED (a guard that refused everything would pass T7 T8 and T9)" as *u8, (rc10 == 0) as i64, ctr)
319
320 // ---- T11 range is a DISTINCT refusal from a source refusal ----
321 let rc11: i64 = cgg_run(subj, "chunk" as *u8, CGG_SMALL, "9999" as *u8, out, ol)
322 gv_puts(" [T11] index past end -> exit=" as *u8); gv_num(rc11); gv_puts("\n" as *u8)
323 gv_check("an-index-past-end-of-file-is-a-RANGE-refusal-distinct-from-a-source-refusal" as *u8, (rc11 == 5) as i64, ctr)
324
325 return gv_verdict("content_get" as *u8, ctr, "the NAS->laptop door proven across the multi-chunk boundary its fixture is asserted to reach, with the partition summing, the digest cross-checked against an independent implementation, and every refusal carrying a positive control" as *u8)
326}