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}