code wiki / _hdl_build / nx_secret_cli.nx
nx_secret_cli.nx source
↩ module page · 112 lines · 6150 B
1// nx_secret_cli.nx -- the operator-facing SECRET STORE CLI (hashicorp-vault-class UX, sovereign).
2// nx_secret put <name> -- seal the value in /tmp/nxsecret.in -> ~/.nishi/secrets/<name>.nv
3// nx_secret get <name> -- open ~/.nishi/secrets/<name>.nv -> /tmp/nxsecret.out (0600, authenticated)
4// Composes the WORKING crypto: nx_machine_key (machine-bound passphrase) + nx_vault (AES-128-GCM,
5// per-name IV, fails closed). The machine key is derived fresh and the /tmp/nxpass ephemeral is
6// SHREDDED after each call -- the operator never types or stores a passphrase. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8const AT_MAGIC_65536: i64 = 65536
9const AT_FDCWD: i64 = 0 - 100
10func _p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
11func _pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0} let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48 as u8;k=1} while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1} sys_write(1,b,k); return 0 }
12// list secret NAMES as JSON (names are plaintext by vault design -- no unseal, no values ever). *.nv -> name.
13func sc_list_json(dir: *u8) -> i64 {
14 let fd: i64 = sys_openat_rd(dir)
15 _p("{\"vault\":\"~/.nishi/secrets\",\"secrets\":[" as *u8)
16 if fd < 0 { _p("],\"count\":0,\"note\":\"no-vault-dir\"}\n" as *u8); return 0 }
17 let dbuf: *u8 = sys_mmap(AT_MAGIC_65536)
18 var cnt: i64 = 0
19 var run: i64 = 1
20 while run == 1 {
21 let n: i64 = sys_getdents64(fd, dbuf, AT_MAGIC_65536)
22 if n <= 0 { run = 0 } else {
23 var off: i64 = 0
24 while off < n {
25 let rec: *u8 = ((dbuf as i64 + off) as *u8)
26 let reclen: i64 = dirent_reclen(rec)
27 if reclen <= 0 { off = n } else {
28 let name: *u8 = dirent_name(rec)
29 var ln: i64 = 0
30 while name[ln]!=(0 as u8) { ln = ln + 1 }
31 if ln > 3 { if name[ln-3]==(46 as u8) { if name[ln-2]==(110 as u8) { if name[ln-1]==(118 as u8) {
32 if cnt > 0 { _p("," as *u8) }
33 _p("\"" as *u8); sys_write(1, name, ln-3); _p("\"" as *u8)
34 cnt = cnt + 1
35 } } } }
36 off = off + reclen
37 }
38 }
39 }
40 }
41 sys_close(fd)
42 _p("],\"count\":" as *u8); _pn(cnt); _p("}\n" as *u8)
43 return cnt
44}
45func sc_run(path: *u8, a1: *u8, a2: *u8) -> i64 {
46 let pid: i64 = sys_fork()
47 if pid == 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 return (st[0] >> 8) & 0xff
62}
63func sc_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
64// append a tamper-evident audit entry (best-effort; absence of the audit binary never blocks access)
65func sc_audit(op: *u8, name: *u8, outcome: *u8) -> i64 {
66 let path: *u8 = "_offc/nx_audit_log.elf" as *u8
67 let pid: i64 = sys_fork()
68 if pid == 0 {
69 let argv: *i64 = sys_mmap(64) as *i64
70 argv[0] = path as i64; argv[1] = "append" as *u8 as i64; argv[2] = op as i64
71 argv[3] = name as i64; argv[4] = outcome as i64; argv[5] = 0
72 let envp: *i64 = sys_mmap(16) as *i64; envp[0] = 0
73 sys_execve(path, argv, envp)
74 sys_exit(127)
75 }
76 let st: *i64 = sys_mmap(16) as *i64
77 sys_wait4(pid, st, 0)
78 return 0
79}
80func sc_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64 = 0; while s[i] != (0 as u8) { dst[off+i] = s[i]; i = i + 1 } return off + i }
81func main(argc: i64, argv: *i64) -> i64 {
82 if argc < 2 { _p("usage: nx_secret <put|get|list> [name]\n" as *u8); sys_exit(2); return 2 }
83 let mode: *u8 = argv[1] as *u8
84 // 'l' list -- names only, no key, no name arg (the portal inventory; safe to expose)
85 if mode[0] == (108 as u8) { sc_list_json("/home/elderwesto/.nishi/secrets" as *u8); sys_exit(0); return 0 }
86 if argc < 3 { _p("usage: nx_secret <put|get|list> [name]\n" as *u8); sys_exit(2); return 2 }
87 let name: *u8 = argv[2] as *u8
88 // compose ~/.nishi/secrets/<name>.nv (HOME hardcoded to the operator's account, per Warden custody)
89 let vpath: *u8 = sys_mmap(512)
90 var o: i64 = 0
91 o = sc_cat(vpath, o, "/home/elderwesto/.nishi/secrets/" as *u8)
92 o = sc_cat(vpath, o, name)
93 o = sc_cat(vpath, o, ".nv" as *u8)
94 vpath[o] = 0 as u8
95 // derive the machine-bound passphrase (writes /tmp/nxpass). DURABLE binary in _offc --
96 // /tmp is tmpfs and gets wiped between sessions, which left this CLI dead at runtime.
97 if sc_run("_offc/nx_machine_key.elf" as *u8, 0 as *u8, 0 as *u8) != 0 { _p(" machine-key derive failed\n" as *u8); sc_unlink("/tmp/nxpass" as *u8); sys_exit(1); return 1 }
98 var rc: i64 = 1
99 if mode[0] == (112 as u8) { // 'p' put
100 rc = sc_run("_offc/nx_vault.elf" as *u8, "seal" as *u8, vpath)
101 if rc == 0 { _p(" put OK: " as *u8); _p(name); _p(" sealed (encrypted at rest, per-name IV)\n" as *u8); sc_audit("put" as *u8, name, "success" as *u8) }
102 else { sc_audit("put" as *u8, name, "fail" as *u8) }
103 } else { if mode[0] == (103 as u8) { // 'g' get
104 rc = sc_run("_offc/nx_vault.elf" as *u8, "open" as *u8, vpath)
105 if rc == 0 { _p(" get OK: " as *u8); _p(name); _p(" -> /tmp/nxsecret.out (0600, authenticated)\n" as *u8); sc_audit("get" as *u8, name, "success" as *u8) }
106 else { _p(" get FAILED (no such secret, wrong machine, or tampered) -- fails closed\n" as *u8); sc_audit("get" as *u8, name, "fail" as *u8) }
107 } else { _p(" unknown mode (use put|get)\n" as *u8); rc = 2 } }
108 // SHRED the passphrase ephemeral (the secret value ephemerals are the caller's to manage)
109 sc_unlink("/tmp/nxpass" as *u8)
110 sys_exit(rc)
111 return rc
112}