code wiki / _hdl_build / nx_vault_v3_gate.nx
nx_vault_v3_gate.nx source
↩ module page · 147 lines · 6831 B
1// nx_vault_v3_gate.nx -- re-runnable GATE for the v3 vault format (Engineer verb: VERIFY).
2// Tests the INSTALLED _offc/nx_vault.elf (install-then-gate; rollback bak kept).
3// K1 v3 seal/open round-trip byte-exact
4// K2 re-seal SAME path + SAME value -> vault FILES DIFFER (random salt+iv embedded).
5// This is THE rotation-safety property: under v2's path-derived iv, re-sealing was
6// AES-GCM nonce reuse. K2 failing = rotation is forbidden again.
7// K3 tampered ciphertext byte -> open FAILS CLOSED
8// K4 legacy v2 vault (sealed by _offc/nx_vault.elf.bak-pre-v3-20260610) still OPENS
9// K5 LIVE prod back-compat: nx_secret_cli get nas exits 0 (real v2 nas.nv on this machine)
10// Exit 0 only on 5/5. license_tier: ORIGINAL
11import "nx_syscalls.nx"
12
13const AT_FDCWD: i64 = 0 - 100
14func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
15func _pn(v: i64) -> i64 { let bb: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m;sys_write(1,"-" as *u8,1)}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=48+(m%10);m=m/10;k=k+1}; var i: i64=0; while i<k{bb[i]=t[k-1-i];i=i+1}; sys_write(1,bb,k); return 0 }
16func vg_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
17func vg_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
18
19func vg_read(path: *u8, buf: *u8, cap: i64) -> i64 {
20 let fd: i64 = sys_openat_rd(path)
21 if fd < 0 { return 0 - 1 }
22 var n: i64 = 0
23 var go: i64 = 1
24 while go == 1 {
25 let base: i64 = buf as i64
26 let r: i64 = sys_read(fd, (base + n) as *u8, cap - n)
27 if r <= 0 { go = 0 } else { n = n + r }
28 if n >= cap { go = 0 }
29 }
30 sys_close(fd)
31 return n
32}
33
34func vg_write(path: *u8, buf: *u8, n: i64) -> i64 {
35 let fd: i64 = sys_openat_wr(path, 0x180)
36 if fd < 0 { return 0 - 1 }
37 sys_write(fd, buf, n)
38 sys_close(fd)
39 return 0
40}
41
42// run child with 2 args, muted stdout; RAW wait4 judgment
43func vg_run2(path: *u8, a1: *u8, a2: *u8) -> i64 {
44 let pid: i64 = sys_fork()
45 if pid == 0 {
46 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
47 if dn >= 0 { sys_dup3(dn, 1, 0) }
48 let argv: *i64 = sys_mmap(64) as *i64
49 argv[0] = path as i64
50 var ai: i64 = 1
51 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 }
52 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 }
53 argv[ai] = 0
54 let envp: *i64 = sys_mmap(16) as *i64
55 envp[0] = 0
56 sys_execve(path, argv, envp)
57 sys_exit(127)
58 }
59 let st: *i64 = sys_mmap(16) as *i64
60 sys_wait4(pid, st, 0)
61 let raw: i64 = st[0]
62 if (raw & 0x7f) != 0 { return 128 + (raw & 0x7f) }
63 return (raw >> 8) & 0xff
64}
65
66func vg_eq(a: *u8, b: *u8, n: i64) -> i64 {
67 var i: i64 = 0
68 while i < n { if a[i] != b[i] { return 0 } i = i + 1 }
69 return 1
70}
71
72func main() -> i64 {
73 _p("=== VAULT V3 GATE: random-iv seal format + legacy back-compat (verb: VERIFY) ===\n" as *u8)
74 let vault: *u8 = "_offc/nx_vault.elf" as *u8
75 let vaultv2: *u8 = "_offc/nx_vault.elf.bak-pre-v3-20260610" as *u8
76 let vp: *u8 = "/tmp/gate_v3.nv" as *u8
77 var pass: i64 = 0
78
79 // fixed test passphrase + value (gate-local; never a real secret)
80 vg_write("/tmp/nxpass" as *u8, "gate-test-pass" as *u8, 14)
81 vg_write("/tmp/nxsecret.in" as *u8, "gate-secret-value-123" as *u8, 21)
82
83 // K1: v3 seal/open round-trip
84 vg_unlink(vp)
85 let s1: i64 = vg_run2(vault, "seal" as *u8, vp)
86 vg_unlink("/tmp/nxsecret.out" as *u8)
87 let o1: i64 = vg_run2(vault, "open" as *u8, vp)
88 let out: *u8 = sys_mmap(4096)
89 let on: i64 = vg_read("/tmp/nxsecret.out" as *u8, out, 4096)
90 var ok1: i64 = 0
91 if s1 == 0 { if o1 == 0 { if on == 21 { if vg_eq(out, "gate-secret-value-123" as *u8, 21) == 1 { ok1 = 1 } } } }
92 if ok1 == 1 { pass = pass + 1; _p("K1 v3-roundtrip OK\n" as *u8) }
93 else { _p("K1 FAIL seal=" as *u8); _pn(s1); _p(" open=" as *u8); _pn(o1); _p(" n=" as *u8); _pn(on); _p("\n" as *u8) }
94
95 // K2: re-seal same path+value -> files differ (the rotation-safety property)
96 let f1: *u8 = sys_mmap(65536)
97 let n1: i64 = vg_read(vp, f1, 65536)
98 vg_write("/tmp/nxpass" as *u8, "gate-test-pass" as *u8, 14)
99 vg_write("/tmp/nxsecret.in" as *u8, "gate-secret-value-123" as *u8, 21)
100 vg_run2(vault, "seal" as *u8, vp)
101 let f2: *u8 = sys_mmap(65536)
102 let n2: i64 = vg_read(vp, f2, 65536)
103 var ok2: i64 = 0
104 if n1 > 40 { if n1 == n2 { if vg_eq(f1, f2, n1) == 0 { ok2 = 1 } } }
105 if ok2 == 1 { pass = pass + 1; _p("K2 reseal-distinct-ciphertext OK (nonce-reuse FORBIDDEN-and-prevented)\n" as *u8) }
106 else { _p("K2 FAIL n1=" as *u8); _pn(n1); _p(" n2=" as *u8); _pn(n2); _p("\n" as *u8) }
107
108 // K3: tamper one ciphertext byte -> open fails closed
109 f2[44] = (f2[44] as i64 ^ 0x5a) as u8
110 vg_write(vp, f2, n2)
111 vg_write("/tmp/nxpass" as *u8, "gate-test-pass" as *u8, 14)
112 let o3: i64 = vg_run2(vault, "open" as *u8, vp)
113 if o3 != 0 { pass = pass + 1; _p("K3 tamper-fails-closed OK\n" as *u8) }
114 else { _p("K3 FAIL tampered vault OPENED\n" as *u8) }
115
116 // K4: legacy v2 vault (sealed by the pre-v3 elf) opens under the new elf
117 vg_unlink(vp)
118 vg_write("/tmp/nxpass" as *u8, "gate-test-pass" as *u8, 14)
119 vg_write("/tmp/nxsecret.in" as *u8, "gate-secret-value-123" as *u8, 21)
120 let s4: i64 = vg_run2(vaultv2, "seal" as *u8, vp)
121 vg_unlink("/tmp/nxsecret.out" as *u8)
122 vg_write("/tmp/nxpass" as *u8, "gate-test-pass" as *u8, 14)
123 let o4: i64 = vg_run2(vault, "open" as *u8, vp)
124 let on4: i64 = vg_read("/tmp/nxsecret.out" as *u8, out, 4096)
125 var ok4: i64 = 0
126 if s4 == 0 { if o4 == 0 { if on4 == 21 { if vg_eq(out, "gate-secret-value-123" as *u8, 21) == 1 { ok4 = 1 } } } }
127 if ok4 == 1 { pass = pass + 1; _p("K4 legacy-v2-opens OK\n" as *u8) }
128 else { _p("K4 FAIL v2seal=" as *u8); _pn(s4); _p(" v3open=" as *u8); _pn(o4); _p("\n" as *u8) }
129
130 // cleanup gate ephemerals BEFORE the live check (secret_cli manages its own)
131 vg_unlink(vp)
132 vg_unlink("/tmp/nxpass" as *u8)
133 vg_unlink("/tmp/nxsecret.in" as *u8)
134 vg_unlink("/tmp/nxsecret.out" as *u8)
135
136 // K5: LIVE prod back-compat -- the real nas.nv (v2) still opens through the full stack
137 let o5: i64 = vg_run2("_offc/nx_secret_cli.elf" as *u8, "get" as *u8, "nas" as *u8)
138 vg_unlink("/tmp/nxsecret.out" as *u8)
139 if o5 == 0 { pass = pass + 1; _p("K5 live-nas-get OK\n" as *u8) }
140 else { _p("K5 FAIL rc=" as *u8); _pn(o5); _p("\n" as *u8) }
141
142 _p("GATE-VERDICT vault_v3 " as *u8); _pn(pass); _p("/5" as *u8)
143 if pass == 5 { _p(" GREEN\n" as *u8); sys_exit(0); return 0 }
144 _p(" RED (rollback: cp _offc/nx_vault.elf.bak-pre-v3-20260610 _offc/nx_vault.elf)\n" as *u8)
145 sys_exit(1)
146 return 1
147}