code wiki / _hdl_build / nx_modauth_arm_vault.nx
nx_modauth_arm_vault.nx source
↩ module page · 111 lines · 5826 B
1// nx_modauth_arm_vault.nx -- PRODUCTION arming: provision an admin with the passphrase sourced from the
2// SEALED VAULT (the documented machine-key vault, the same one nx_aw_push uses for the NAS password), NOT a
3// plaintext file. Flow (mirrors nx_aw_push): run nx_machine_key (writes the machine-derived unseal key to
4// /tmp/nxpass) -> nx_vault open <pw-vault.nv> (decrypts -> /tmp/nxsecret.out, 0600, AES-128-GCM authenticated)
5// -> read the passphrase -> register -> SHRED /tmp/nxsecret.out immediately. The passphrase never persists as
6// plaintext; at rest it lives only inside the AES-GCM-sealed .nv. Idempotent (re-run supersedes).
7//
8// Operator seals their passphrase once (out-of-band): stage it to /tmp/nxsecret.in -> nx_machine_key ->
9// nx_vault seal <pw-vault.nv>. The keys-bundle + store live in the documented secrets dir (~/.nishi/secrets/).
10// MUST run with CWD = the nxc2 repo root (the _offc/ helper paths are relative, like nx_aw_push).
11// argv: [1]=pw-vault.nv [2]=auth-keysfile [3]=auth-storefile [4]=realm [5]=handle [6]=mnemonic-outfile
12// Sovereign: nx_modern_auth_flow + nx_machine_key.elf + nx_vault.elf + nx_syscalls. license_tier: ORIGINAL
13import "hub/nx_modern_auth_flow.nx"
14import "nx_syscalls.nx"
15const AV_MAGIC_8192: i64 = 8192
16
17const AV_NXPASS: *u8 = "/tmp/nxpass"
18const AV_SECRET_OUT: *u8 = "/tmp/nxsecret.out"
19
20func av_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
21func av_w(s: *u8) -> i64 { sys_write(1, s, av_len(s)); return 0 }
22func av_unlink(path: *u8) -> i64 { __syscall(263, AT_FDCWD, path, 0, 0, 0, 0) return 0 }
23
24// fork+exec a Nishi .elf with up to 2 args; returns exit code (mirrors nx_aw_push p_run).
25func av_run(path: *u8, a1: *u8, a2: *u8) -> i64 {
26 let pid: i64 = sys_fork()
27 if pid == 0 {
28 let argv: *i64 = sys_mmap(64) as *i64
29 argv[0] = path as i64
30 var ai: i64 = 1
31 if (a1 as i64) != 0 { argv[ai] = a1 as i64; ai = ai + 1 }
32 if (a2 as i64) != 0 { argv[ai] = a2 as i64; ai = ai + 1 }
33 argv[ai] = 0
34 let envp: *i64 = sys_mmap(16) as *i64
35 envp[0] = "PATH=/usr/bin:/bin" as *u8 as i64
36 envp[1] = 0
37 sys_execve(path, argv, envp)
38 sys_exit(127)
39 }
40 let st: *i64 = sys_mmap(16) as *i64
41 sys_wait4(pid, st, 0)
42 return (st[0] >> 8) & 0xff
43}
44
45func main(argc: i64, argv: *i64) -> i64 {
46 if argc < 7 {
47 sys_write(2, "usage: nx_modauth_arm_vault <pw-vault.nv> <keysfile> <storefile> <realm> <handle> <mnemonic-out>\n" as *u8, 96)
48 return 1
49 }
50 let pwvault: *u8 = argv[1] as *u8
51 let keysfile: *u8 = argv[2] as *u8
52 let storefile: *u8 = argv[3] as *u8
53 let realm: *u8 = argv[4] as *u8
54 let handle: *u8 = argv[5] as *u8
55 let mnout: *u8 = argv[6] as *u8
56 let realm_n: i64 = av_len(realm)
57 let handle_n: i64 = av_len(handle)
58
59 // ---- source the passphrase from the sealed vault (machine-key unseal -> /tmp/nxsecret.out) ----
60 if av_run("_offc/nx_machine_key.elf" as *u8, 0 as *u8, 0 as *u8) != 0 { av_w("ARM FAIL: machine-key (run from nxc2 root)\n" as *u8); av_unlink(AV_NXPASS); return 2 }
61 if av_run("_offc/nx_vault.elf" as *u8, "open" as *u8, pwvault) != 0 { av_w("ARM FAIL: vault open (bad vault / wrong machine)\n" as *u8); av_unlink(AV_NXPASS); return 3 }
62 av_unlink(AV_NXPASS)
63 let pwbox: *i64 = sys_mmap(16) as *i64
64 pwbox[0] = 0
65 let pwbuf: *u8 = sys_read_file(AV_SECRET_OUT, pwbox)
66 if (pwbuf as i64) == 0 { av_w("ARM FAIL: read unsealed secret\n" as *u8); return 4 }
67 var pw_start: i64 = 0
68 if pwbox[0] >= 3 { if (pwbuf[0] as i64) == 239 { if (pwbuf[1] as i64) == 187 { if (pwbuf[2] as i64) == 191 { pw_start = 3 } } } }
69 var pw_n: i64 = pwbox[0]
70 var trimming: i64 = 1
71 while trimming == 1 {
72 trimming = 0
73 if pw_n > pw_start {
74 let c: i64 = pwbuf[pw_n - 1] as i64
75 if c == 10 { pw_n = pw_n - 1; trimming = 1 }
76 if c == 13 { pw_n = pw_n - 1; trimming = 1 }
77 }
78 }
79 let pw_ptr: *u8 = ((pwbuf as i64) + pw_start) as *u8
80 let pw_eff: i64 = pw_n - pw_start
81 if pw_eff < 1 { av_unlink(AV_SECRET_OUT); av_w("ARM FAIL: empty passphrase from vault\n" as *u8); return 4 }
82
83 // ---- arm the server-key bundle + realm context, then register ----
84 let oprf_seed: *u8 = sys_mmap(32)
85 let akp: *u8 = sys_mmap(32)
86 let akb: *u8 = sys_mmap(33)
87 let edp: *u8 = sys_mmap(32)
88 let edb: *u8 = sys_mmap(32)
89 if nx_uas_server_keys_load_or_init(keysfile, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { av_unlink(AV_SECRET_OUT); av_w("ARM FAIL: server-key bundle\n" as *u8); return 5 }
90 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
91 if nx_auth_context_init(ctx, realm, realm_n, realm, realm_n, storefile as i64, oprf_seed, edp, edb, 900, AV_MAGIC_8192, 1, 1, 5, 1) != NX_MAUTH_OK { av_unlink(AV_SECRET_OUT); av_w("ARM FAIL: context init\n" as *u8); return 6 }
92 let mn: *u8 = sys_mmap(512)
93 let mn_n: *i64 = sys_mmap(16) as *i64
94 mn_n[0] = 0
95 let rc: i64 = nx_modern_auth_register(ctx, handle, handle_n, pw_ptr, pw_eff, mn, 512, mn_n)
96 av_unlink(AV_SECRET_OUT)
97 if rc != NX_MAUTH_OK { av_w("ARM FAIL: register\n" as *u8); return 7 }
98 if mn_n[0] < 1 { av_w("ARM FAIL: empty mnemonic\n" as *u8); return 8 }
99
100 let fd: i64 = sys_openat_wr(mnout, 0x180)
101 if fd < 0 { av_w("ARM FAIL: mnemonic out-file\n" as *u8); return 9 }
102 sys_write(fd, mn, mn_n[0])
103 sys_write(fd, "\n" as *u8, 1)
104 sys_close(fd)
105
106 av_w("ARM OK (vault-sourced): admin provisioned for realm '" as *u8)
107 sys_write(1, realm, realm_n)
108 av_w("' -- passphrase read from the AES-GCM-sealed vault, never a plaintext file.\n" as *u8)
109 av_w(" 24-word BIP39 recovery mnemonic written to the out-file (0600) -- SAVE it, then DELETE the out-file.\n" as *u8)
110 return 0
111}