code wiki / _hdl_build / nx_evattest_cli.nx
nx_evattest_cli.nx source
↩ module page · 140 lines · 5836 B
1// nx_evattest_cli.nx -- the OPERATOR's tool for the human leg of the evidence law.
2//
3// The evidence law requires a human attestation before any domain can read PROVEN, and nx_evattest verifies
4// that attestation cryptographically. This is the other half: how a human actually mints one.
5//
6// ★THIS TOOL EXISTS SO THE OPERATOR CAN SIGN. It is deliberately NOT wired into any automated path, any
7// beat, or any agent workflow. An agent running `sign` against a human-role key would be certifying its own
8// work -- the precise thing require_human forbids -- and the verifier's role check is what makes that
9// forgery detectable rather than merely discouraged. Keep the human key somewhere the automation does not
10// operate, and never register a key the automation holds as role=human.
11//
12// VERBS
13// keygen <keyfile> -- 32 CSPRNG bytes -> keyfile (0600); prints ONLY the public key
14// pub <keyfile> -- print the public key hex for the registry
15// sign <keyfile> <class> <verdict> <scope> <signer>
16// -- print a complete signed attestation row on stdout
17//
18// The signed region is the CLAIM PREFIX (up to " pub="), so class/verdict/scope/signer/epoch are all
19// immutable under the signature. Redirect the row into knowledge/status/evclass_<domain>.conf.
20// license_tier: ORIGINAL expect_exit: 2
21import "nx_syscalls.nx"
22import "nx_evattest.nx"
23const K_MAGIC_8192: i64 = 8192
24
25func w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
26func hexw(src: *u8, n: i64) -> i64 {
27 let hexd: *u8 = "0123456789abcdef"
28 let o: *u8 = sys_mmap(n*2 + 8)
29 var i: i64 = 0
30 while i < n {
31 let b: i64 = src[i] as i64
32 o[i*2] = hexd[(b >> 4) & 15]
33 o[i*2+1] = hexd[b & 15]
34 i = i + 1
35 }
36 sys_write(1, o, n*2)
37 return 0
38}
39func catz(dst: *u8, off: i64, s: *u8) -> i64 {
40 var o: i64 = off
41 var i: i64 = 0
42 while s[i] != (0 as u8) { dst[o] = s[i]; o = o + 1; i = i + 1 }
43 return o
44}
45func catn(dst: *u8, off: i64, v: i64) -> i64 {
46 let t: *u8 = sys_mmap(28)
47 var m: i64 = v
48 var k: i64 = 0
49 if m == 0 { t[0] = 48 as u8; k = 1 }
50 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
51 var o: i64 = off
52 var z: i64 = k - 1
53 while z >= 0 { dst[o] = t[z]; o = o + 1; z = z - 1 }
54 return o
55}
56func readkey(path: *u8, out: *u8) -> i64 {
57 let fd: i64 = sys_openat_rd(path)
58 if fd < 0 { return 0 }
59 let n: i64 = sys_read(fd, out, 32)
60 sys_close(fd)
61 if n != 32 { return 0 }
62 return 1
63}
64func streq(a: *u8, b: *u8) -> i64 { var i: i64=0; while a[i]!=(0 as u8){ if a[i]!=b[i]{return 0} i=i+1 } if b[i]!=(0 as u8){return 0} return 1 }
65
66func main(argc: i64, argv: *i64) -> i64 {
67 if argc < 3 {
68 w("usage:\n nx_evattest_cli keygen <keyfile>\n nx_evattest_cli pub <keyfile>\n nx_evattest_cli sign <keyfile> <class> <verdict> <scope> <signer>\n" as *u8)
69 w("\nclass = human|llm|mech verdict = pass|fail\n" as *u8)
70 w("The human key belongs with the OPERATOR, not in any automated path.\n" as *u8)
71 sys_exit(2); return 2
72 }
73 let verb: *u8 = argv[1] as *u8
74 let kf: *u8 = argv[2] as *u8
75 let priv: *u8 = sys_mmap(32)
76 let pub: *u8 = sys_mmap(32)
77
78 if streq(verb, "keygen" as *u8) == 1 {
79 // Refuse to clobber an existing key: silently rotating a signing key invalidates every attestation
80 // it ever made, and doing that by accident is unrecoverable.
81 let ex: i64 = sys_openat_rd(kf)
82 if ex >= 0 { sys_close(ex); w("REFUSED: keyfile already exists -- refusing to overwrite a signing key\n" as *u8); sys_exit(4); return 4 }
83 let rfd: i64 = sys_openat_rd("/dev/urandom\x00" as *u8)
84 if rfd < 0 { w("cannot open /dev/urandom\n" as *u8); sys_exit(3); return 3 }
85 let got: i64 = sys_read(rfd, priv, 32)
86 sys_close(rfd)
87 if got != 32 { w("short read from /dev/urandom\n" as *u8); sys_exit(3); return 3 }
88 let fd: i64 = sys_openat_wr(kf, 0x180) // 0600 -- owner only
89 if fd < 0 { w("cannot write keyfile\n" as *u8); sys_exit(3); return 3 }
90 sys_write(fd, priv, 32)
91 sys_close(fd)
92 ed25519_pub_from_priv(priv, pub)
93 w("KEYGEN OK (private key written 0600; it is NEVER printed)\nregistry line -> role=human pub=" as *u8)
94 hexw(pub, 32)
95 w(" name=<your-name>\n" as *u8)
96 sys_exit(0); return 0
97 }
98
99 if readkey(kf, priv) == 0 { w("cannot read a 32-byte keyfile\n" as *u8); sys_exit(3); return 3 }
100 ed25519_pub_from_priv(priv, pub)
101
102 if streq(verb, "pub" as *u8) == 1 {
103 hexw(pub, 32)
104 w("\n" as *u8)
105 sys_exit(0); return 0
106 }
107
108 if streq(verb, "sign" as *u8) == 1 {
109 if argc < 7 { w("sign needs: <keyfile> <class> <verdict> <scope> <signer>\n" as *u8); sys_exit(2); return 2 }
110 let cls: *u8 = argv[3] as *u8
111 let vrd: *u8 = argv[4] as *u8
112 let scope: *u8 = argv[5] as *u8
113 let signer: *u8 = argv[6] as *u8
114 let row: *u8 = sys_mmap(K_MAGIC_8192)
115 var r: i64 = 0
116 r = catz(row, r, "class=" as *u8)
117 r = catz(row, r, cls)
118 r = catz(row, r, " verdict=" as *u8)
119 r = catz(row, r, vrd)
120 r = catz(row, r, " scope=" as *u8)
121 r = catz(row, r, scope)
122 r = catz(row, r, " signer=" as *u8)
123 r = catz(row, r, signer)
124 r = catz(row, r, " epoch=" as *u8)
125 r = catn(row, r, sys_now_realtime_sec())
126 let sig: *u8 = sys_mmap(64)
127 ed25519_sign_full(priv, row, r, sig)
128 sys_write(1, row, r)
129 w(" pub=" as *u8)
130 hexw(pub, 32)
131 w(" sig=" as *u8)
132 hexw(sig, 64)
133 w("\n" as *u8)
134 sys_exit(0); return 0
135 }
136
137 w("unknown verb\n" as *u8)
138 sys_exit(2)
139 return 2
140}