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}