code wiki / _hdl_build / nx_attest_ceremony.nx
nx_attest_ceremony.nx source
↩ module page · 275 lines · 17087 B
1// nx_attest_ceremony.nx -- the OPERATOR-SIDE key ceremony for the attestation plane, PROVEN against
2// the real verifier before any real key exists.
3//
4// WHY: PROVEN=0/41 is bound on knowledge/attest_keys.conf (operator-held Ed25519 key). The recipe
5// existed only as PROSE derived from reading nx_evattest.nx -- and an untested recipe handed to the
6// operator is litter: if the claim-prefix boundary is off by one byte, every genuine attestation
7// reads BAD-SIGNATURE while all refusal paths still pass (nx_evattest.nx:136 warns exactly this).
8// This organ makes the ceremony EXECUTABLE and proves it byte-exact with a THROWAWAY key that is
9// never registered anywhere real -- the real key stays with the operator, never in an agent session.
10//
11// THE LINE THIS TOOL DOES NOT CROSS: it contains no path that writes knowledge/attest_keys.conf.
12// Registration is the operator's act (the whole clause, per nx_evattest: "Minting a human
13// attestation is an act the operator performs with a key the operator holds"). This tool only makes
14// that act one command and PROVES the output verifies.
15//
16// nx_attest_ceremony keygen <priv_out> -- 32B seed from /dev/urandom -> file; prints pub=<64hex>
17// (pub is PUBLIC; the priv file never prints)
18// nx_attest_ceremony sign <priv_file> <row_file> -- signs the row file's CLAIM PREFIX (trailing newline
19// stripped), prints the full signed row
20// nx_attest_ceremony -- GATE: 6 teeth against the REAL at_verify_row
21// license_tier: ORIGINAL layer: evidence module: nishi-core.evidence.attest_ceremony
22import "nx_evattest.nx"
23const K_MAGIC_8192: i64 = 8192
24const K_MAGIC_8000: i64 = 8000
25const K_MAGIC_65536: i64 = 65536
26const K_MAGIC_65000: i64 = 65000
27const K_MAGIC_4096: i64 = 4096
28const K_MAGIC_4000: i64 = 4000
29
30func ac_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
31func ac_slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n }
32func ac_hex(b: *u8, n: i64, out: *u8) -> i64 {
33 var i: i64 = 0
34 while i < n {
35 let v: i64 = b[i] as i64
36 let hi: i64 = (v >> 4) & 15
37 let lo: i64 = v & 15
38 if hi < 10 { out[i*2] = (48+hi) as u8 } else { out[i*2] = (87+hi) as u8 }
39 if lo < 10 { out[i*2+1] = (48+lo) as u8 } else { out[i*2+1] = (87+lo) as u8 }
40 i = i + 1
41 }
42 out[n*2] = 0 as u8
43 return n*2
44}
45// substring search within a row window (mirrors ss_row_has / at_find semantics)
46func ac_has(b: *u8, s: i64, e: i64, pat: *u8) -> i64 {
47 var pl: i64 = 0
48 while pat[pl] != (0 as u8) { pl = pl + 1 }
49 if pl == 0 { return 0 }
50 var i: i64 = s
51 while i + pl <= e {
52 var k: i64 = 0
53 var ok: i64 = 1
54 while k < pl { if b[i+k] != pat[k] { ok = 0; k = pl } else { k = k + 1 } }
55 if ok == 1 { return 1 }
56 i = i + 1
57 }
58 return 0
59}
60func ac_n(v: i64) -> i64 {
61 let t: *u8 = sys_mmap(64)
62 let o: *u8 = sys_mmap(64)
63 var m: i64=v; var k: i64=0
64 if m==0 { t[0]=48 as u8; k=1 }
65 while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
66 var i: i64=0; while i<k { o[i]=t[k-1-i]; i=i+1 }
67 sys_write(1,o,k)
68 sys_munmap(t,64); sys_munmap(o,64)
69 return 0
70}
71func ac_cat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){ dst[o]=s[i]; o=o+1; i=i+1 } return o }
72
73// 32 random bytes from the kernel CSPRNG. REFUSES on short read -- a partially random key is worse
74// than no key, and /dev/urandom short-reads are exactly the kind of silent failure this plane hates.
75func ac_random32(out: *u8) -> i64 {
76 let fd: i64 = sys_openat_rd("/dev/urandom" as *u8)
77 if fd < 0 { return 0 }
78 let n: i64 = sys_read(fd, out, 32)
79 sys_close(fd)
80 if n != 32 { return 0 }
81 return 1
82}
83
84// Sign `row` (claim prefix, exact bytes) with priv; append " pub=<64hex> sig=<128hex>" into out.
85// Returns total row length, or -1.
86func ac_sign_row(priv: *u8, row: *u8, rowlen: i64, out: *u8) -> i64 {
87 let pub: *u8 = sys_mmap(32)
88 if ed25519_pub_from_priv(priv, pub) != 0 { return 0 - 1 }
89 let sig: *u8 = sys_mmap(64)
90 if ed25519_sign_full(priv, row, rowlen, sig) != 0 { return 0 - 1 }
91 var o: i64 = 0
92 var i: i64 = 0
93 while i < rowlen { out[o] = row[i]; o = o + 1; i = i + 1 }
94 o = ac_cat(out, o, " pub=" as *u8)
95 let ph: *u8 = sys_mmap(80); ac_hex(pub, 32, ph); o = ac_cat(out, o, ph)
96 o = ac_cat(out, o, " sig=" as *u8)
97 let sh: *u8 = sys_mmap(160); ac_hex(sig, 64, sh); o = ac_cat(out, o, sh)
98 out[o] = 0 as u8
99 return o
100}
101
102func main(argc: i64, argv: *i64) -> i64 {
103 if argc >= 2 {
104 let mode: *u8 = argv[1] as *u8
105 // ---- keygen <priv_out> ----
106 if mode[0] == (107 as u8) {
107 if argc < 3 { ac_p("usage: nx_attest_ceremony keygen <priv_out>\n" as *u8); sys_exit(2); return 2 }
108 let priv: *u8 = sys_mmap(32)
109 if ac_random32(priv) != 1 { ac_p("REFUSED: /dev/urandom short read -- no key written\n" as *u8); sys_exit(3); return 3 }
110 let fd: i64 = sys_openat_wr(argv[2] as *u8, 0x180) // 0600: owner rw, nobody else
111 if fd < 0 { ac_p("REFUSED: cannot write priv file\n" as *u8); sys_exit(4); return 4 }
112 sys_write(fd, priv, 32)
113 sys_close(fd)
114 let pub: *u8 = sys_mmap(32)
115 ed25519_pub_from_priv(priv, pub)
116 let ph: *u8 = sys_mmap(80); ac_hex(pub, 32, ph)
117 ac_p("priv written (32B raw, 0600). REGISTRY LINE (public -- paste into knowledge/attest_keys.conf):\n" as *u8)
118 ac_p("role=human pub=" as *u8); ac_p(ph); ac_p(" name=operator\n" as *u8)
119 sys_exit(0); return 0
120 }
121 // ---- check <row_file> <registry_file> -- verify a LIVE row against a LIVE registry ----
122 // Added 2026-08-01: six hypotheses for "the signed oracle row does not count" were eliminated by
123 // measurement (require_human, join key, stamp cache, enum mismatch, binary drift, class-load skip)
124 // and the survivor is at_verify_row's verdict on the row itself. REASONING ABOUT A REFUSAL CODE IS
125 // NOT READING ONE -- this prints the code the board actually computes, from the same function.
126 if mode[0] == (99 as u8) {
127 if argc < 4 { ac_p("usage: nx_attest_ceremony check <row_file> <registry_file>\n" as *u8); sys_exit(2); return 2 }
128 let rb: *u8 = sys_mmap(K_MAGIC_8192)
129 let rf2: i64 = sys_openat_rd(argv[2] as *u8)
130 if rf2 < 0 { ac_p("REFUSED: cannot read row file\n" as *u8); sys_exit(4); return 4 }
131 var rn2: i64 = sys_read(rf2, rb, K_MAGIC_8000)
132 sys_close(rf2)
133 while rn2 > 0 { if rb[rn2-1] == (10 as u8) { rn2 = rn2 - 1 } else { if rb[rn2-1] == (13 as u8) { rn2 = rn2 - 1 } else { break } } }
134 let cb: *u8 = sys_mmap(K_MAGIC_65536)
135 let cf: i64 = sys_openat_rd(argv[3] as *u8)
136 if cf < 0 { ac_p("REFUSED: cannot read registry file\n" as *u8); sys_exit(4); return 4 }
137 let cn2: i64 = sys_read(cf, cb, K_MAGIC_65000)
138 sys_close(cf)
139 ac_p("row_bytes=" as *u8); ac_n(rn2); ac_p(" registry_bytes=" as *u8); ac_n(cn2); ac_p("\n" as *u8)
140 var cl: i64 = 0
141 if ac_has(rb, 0, rn2, "class=human" as *u8) == 1 { cl = AT_CLASS_HUMAN }
142 if ac_has(rb, 0, rn2, "class=llm" as *u8) == 1 { cl = AT_CLASS_LLM }
143 if ac_has(rb, 0, rn2, "class=mech" as *u8) == 1 { cl = AT_CLASS_MECH }
144 if ac_has(rb, 0, rn2, "class=oracle" as *u8) == 1 { cl = AT_CLASS_ORACLE }
145 // 2026-08-03: the parser predated the EXPERIENTIAL class -- a supervisor evclass row checked
146 // as parsed_class=0 and returned OK WITHOUT exercising the ran=/witness=/observed= demands
147 // (T7/T8 bind only under the right enum). A checker that verifies under the wrong class
148 // proves less than it prints.
149 if ac_has(rb, 0, rn2, "class=experiential" as *u8) == 1 { cl = AT_CLASS_EXPERIENTIAL }
150 ac_p("parsed_class=" as *u8); ac_n(cl); ac_p("\n" as *u8)
151 let rc2: i64 = at_verify_row(rb, 0, rn2, cl, cb, cn2)
152 ac_p("at_verify_row -> " as *u8); sys_write(1, at_err_name(rc2), ac_slen(at_err_name(rc2)))
153 ac_p(" (rc=" as *u8); ac_n(rc2); ac_p(")\n" as *u8)
154 if rc2 == AT_OK { sys_exit(0); return 0 }
155 sys_exit(1); return 1
156 }
157 // ---- sign <priv_file> <row_file> ----
158 if mode[0] == (115 as u8) {
159 if argc < 4 { ac_p("usage: nx_attest_ceremony sign <priv_file> <row_file>\n" as *u8); sys_exit(2); return 2 }
160 let priv: *u8 = sys_mmap(32)
161 let pf: i64 = sys_openat_rd(argv[2] as *u8)
162 if pf < 0 { ac_p("REFUSED: cannot read priv file\n" as *u8); sys_exit(4); return 4 }
163 if sys_read(pf, priv, 32) != 32 { sys_close(pf); ac_p("REFUSED: priv file is not 32 raw bytes\n" as *u8); sys_exit(4); return 4 }
164 sys_close(pf)
165 let row: *u8 = sys_mmap(K_MAGIC_4096)
166 let rf: i64 = sys_openat_rd(argv[3] as *u8)
167 if rf < 0 { ac_p("REFUSED: cannot read row file\n" as *u8); sys_exit(4); return 4 }
168 var rn: i64 = sys_read(rf, row, K_MAGIC_4000)
169 sys_close(rf)
170 while rn > 0 { if row[rn-1] == (10 as u8) { rn = rn - 1 } else { if row[rn-1] == (13 as u8) { rn = rn - 1 } else { break } } }
171 if rn <= 0 { ac_p("REFUSED: empty row\n" as *u8); sys_exit(4); return 4 }
172 let out: *u8 = sys_mmap(K_MAGIC_8192)
173 let on: i64 = ac_sign_row(priv, row, rn, out)
174 if on < 0 { ac_p("REFUSED: sign failed\n" as *u8); sys_exit(5); return 5 }
175 sys_write(1, out, on)
176 ac_p("\n" as *u8)
177 sys_exit(0); return 0
178 }
179 }
180
181 // ---- GATE: the whole ceremony against the REAL verifier, THROWAWAY key ----
182 var red: i64 = 0
183 let priv: *u8 = sys_mmap(32)
184 if ac_random32(priv) != 1 { ac_p("[RED] no CSPRNG -- cannot gate\n" as *u8); sys_exit(1); return 1 }
185 let pub: *u8 = sys_mmap(32)
186 ed25519_pub_from_priv(priv, pub)
187 let ph: *u8 = sys_mmap(80); ac_hex(pub, 32, ph)
188
189 // registry buffers (in-memory; NOTHING written to knowledge/)
190 let reg_h: *u8 = sys_mmap(256)
191 var rhn: i64 = ac_cat(reg_h, 0, "role=human pub=" as *u8); rhn = ac_cat(reg_h, rhn, ph); rhn = ac_cat(reg_h, rhn, " name=gate-throwaway\n" as *u8)
192 let reg_m: *u8 = sys_mmap(256)
193 var rmn: i64 = ac_cat(reg_m, 0, "role=machine pub=" as *u8); rmn = ac_cat(reg_m, rmn, ph); rmn = ac_cat(reg_m, rmn, " name=gate-throwaway\n" as *u8)
194 let reg_none: *u8 = sys_mmap(64)
195 var rnn: i64 = ac_cat(reg_none, 0, "# empty registry\n" as *u8)
196
197 // T1: signed human row + human-role registry -> AT_OK
198 let claim: *u8 = "class=human verdict=pass scope=gate-selftest signer=gate epoch=1785623000" as *u8
199 let rowbuf: *u8 = sys_mmap(K_MAGIC_8192)
200 let rl: i64 = ac_sign_row(priv, claim, ac_slen(claim), rowbuf)
201 var rc: i64 = at_verify_row(rowbuf, 0, rl, AT_CLASS_HUMAN, reg_h, rhn)
202 ac_p("T1 signed human row vs human-role registry: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
203 if rc == AT_OK { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED]\n" as *u8); red = red + 1 }
204
205 // T2 NEG: tamper ONE claim byte after signing -> BADSIG
206 let tam: *u8 = sys_mmap(K_MAGIC_8192)
207 var ti: i64 = 0; while ti < rl { tam[ti] = rowbuf[ti]; ti = ti + 1 }
208 tam[20] = (tam[20] as i64 + 1) as u8 // inside "verdict=pass"
209 rc = at_verify_row(tam, 0, rl, AT_CLASS_HUMAN, reg_h, rhn)
210 ac_p("T2 NEG tampered claim byte: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
211 if rc == AT_ERR_BADSIG { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] tamper not caught\n" as *u8); red = red + 1 }
212
213 // T3 NEG: valid signature, key NOT in registry -> UNKNOWNKEY
214 rc = at_verify_row(rowbuf, 0, rl, AT_CLASS_HUMAN, reg_none, rnn)
215 ac_p("T3 NEG unregistered key: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
216 if rc == AT_ERR_UNKNOWNKEY { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] stranger's valid sig accepted\n" as *u8); red = red + 1 }
217
218 // T4 NEG: machine-role key signing a HUMAN row -> ROLE refused (the whole clause)
219 rc = at_verify_row(rowbuf, 0, rl, AT_CLASS_HUMAN, reg_m, rmn)
220 ac_p("T4 NEG machine key on human row: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
221 if rc == AT_ERR_ROLE { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] role separation broken\n" as *u8); red = red + 1 }
222
223 // T5 NEG: oracle row WITHOUT refdig -> NOTDERIVED even with a valid registered signature
224 let oc1: *u8 = "class=oracle verdict=pass scope=x ref=RFC-9807 gate=nx_opaque_rfc_kat signer=m epoch=1785623000" as *u8
225 let ob1: *u8 = sys_mmap(K_MAGIC_8192)
226 let ol1: i64 = ac_sign_row(priv, oc1, ac_slen(oc1), ob1)
227 rc = at_verify_row(ob1, 0, ol1, AT_CLASS_ORACLE, reg_m, rmn)
228 ac_p("T5 NEG oracle row missing refdig=: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
229 if rc == AT_ERR_NOTDERIVED { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] underived oracle accepted\n" as *u8); red = red + 1 }
230
231 // T6: full oracle row (ref+refdig+gate), machine key -> AT_OK (independence lives in the reference)
232 let oc2: *u8 = "class=oracle verdict=pass scope=x ref=RFC-9807 refdig=06f941b4d7307a29 gate=nx_opaque_rfc_kat signer=m epoch=1785623000" as *u8
233 let ob2: *u8 = sys_mmap(K_MAGIC_8192)
234 let ol2: i64 = ac_sign_row(priv, oc2, ac_slen(oc2), ob2)
235 rc = at_verify_row(ob2, 0, ol2, AT_CLASS_ORACLE, reg_m, rmn)
236 ac_p("T6 derived oracle row, machine key: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
237 if rc == AT_OK { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED]\n" as *u8); red = red + 1 }
238
239 // T7 NEG: EXPERIENTIAL row with no witness= -> NOTDERIVED. THE LOAD-BEARING NEGATIVE CONTROL for the
240 // new class: without it, anything holding a machine key could mint "the world responded" over a gate
241 // that merely returned 0 -- which would make this class strictly WEAKER than the signature it replaces.
242 let ec1: *u8 = "class=experiential verdict=pass scope=dht-announce ran=nx_dht_announce observed=acked=8 signer=m epoch=1785623000" as *u8
243 let eb1: *u8 = sys_mmap(K_MAGIC_8192)
244 let el1: i64 = ac_sign_row(priv, ec1, ac_slen(ec1), eb1)
245 rc = at_verify_row(eb1, 0, el1, AT_CLASS_EXPERIENTIAL, reg_m, rmn)
246 ac_p("T7 NEG experiential row missing witness=: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
247 if rc == AT_ERR_NOTDERIVED { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] witnessless experiential accepted\n" as *u8); red = red + 1 }
248
249 // T8 NEG: experiential row missing observed= -> NOTDERIVED. "It worked" is a summary, not a measurement.
250 let ec2: *u8 = "class=experiential verdict=pass scope=dht-announce ran=nx_dht_announce witness=public-dht-peers signer=m epoch=1785623000" as *u8
251 let eb2: *u8 = sys_mmap(K_MAGIC_8192)
252 let el2: i64 = ac_sign_row(priv, ec2, ac_slen(ec2), eb2)
253 rc = at_verify_row(eb2, 0, el2, AT_CLASS_EXPERIENTIAL, reg_m, rmn)
254 ac_p("T8 NEG experiential row missing observed=: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
255 if rc == AT_ERR_NOTDERIVED { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] unmeasured experiential accepted\n" as *u8); red = red + 1 }
256
257 // T9: full experiential row (ran+witness+observed), machine key -> AT_OK. Independence lives in the
258 // RESPONDENT: strangers' DHT nodes ACKed us; no signature could manufacture that.
259 let ec3: *u8 = "class=experiential verdict=pass scope=dht-announce ran=nx_dht_announce witness=public-dht-peers observed=acked=8 signer=m epoch=1785623000" as *u8
260 let eb3: *u8 = sys_mmap(K_MAGIC_8192)
261 let el3: i64 = ac_sign_row(priv, ec3, ac_slen(ec3), eb3)
262 rc = at_verify_row(eb3, 0, el3, AT_CLASS_EXPERIENTIAL, reg_m, rmn)
263 ac_p("T9 full experiential row, machine key: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
264 if rc == AT_OK { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED]\n" as *u8); red = red + 1 }
265
266 // T10 NEG: an UNREGISTERED key cannot mint an experiential row either -- the class is machine-signable,
267 // NOT unsigned-writable. Guards the obvious bypass: anything able to write knowledge/status/.
268 rc = at_verify_row(eb3, 0, el3, AT_CLASS_EXPERIENTIAL, reg_none, rnn)
269 ac_p("T10 NEG experiential, unregistered key: rc=" as *u8); sys_write(1, at_err_name(rc), ac_slen(at_err_name(rc)))
270 if rc == AT_ERR_UNKNOWNKEY { ac_p(" GREEN\n" as *u8) } else { ac_p(" [RED] unregistered experiential accepted\n" as *u8); red = red + 1 }
271
272 if red == 0 { ac_p("NX-ATTEST-CEREMONY 10/10 GREEN -- the recipe verifies against the REAL at_verify_row\n" as *u8); sys_exit(0); return 0 }
273 ac_p("NX-ATTEST-CEREMONY RED\n" as *u8)
274 sys_exit(1); return 1
275}