code wiki / _hdl_build / nx_modauth_arm.nx
nx_modauth_arm.nx source
↩ module page · 88 lines · 4846 B
1// nx_modauth_arm.nx -- PROVISION an admin into the canonical Modern Auth (the arming step that makes
2// "all sites use modern_auth" deployable). File-driven by construction so the operator's passphrase NEVER
3// touches chat / argv / stdout: it is read from a STAGED file, used to register the admin, and the one-time
4// BIP39 24-word recovery mnemonic is written to an out-file for the operator to save then DELETE (it is
5// never echoed). Idempotent: re-running supersedes the admin (additive-only store -- old passphrase dies,
6// new lives; rows 8-9 of nx_modauth_e2e_gate prove this). ONE server-key bundle + ONE store serve every
7// realm; realm+handle decide WHICH site+admin (a status token can't validate a wiki ctx -- realm-scoped hash).
8//
9// argv: [1]=keysfile [2]=storefile [3]=realm [4]=handle [5]=passphrase-file [6]=mnemonic-outfile
10// e.g. nx_modauth_arm /vault/nishi_auth_keys /vault/nishi_auth_store nishi_status_adm elder /tmp/pw /tmp/mn
11// Sovereign: nx_modern_auth_flow + nx_syscalls (no gcc, no 3rd-party). license_tier: ORIGINAL
12import "hub/nx_modern_auth_flow.nx"
13import "nx_syscalls.nx"
14const K_MAGIC_8192: i64 = 8192
15
16func arm_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n }
17func arm_w(s: *u8) -> i64 { sys_write(1, s, arm_len(s)); return 0 }
18
19func main(argc: i64, argv: *i64) -> i64 {
20 if argc < 7 {
21 sys_write(2, "usage: nx_modauth_arm <keysfile> <storefile> <realm> <handle> <passphrase-file> <mnemonic-outfile>\n" as *u8, 98)
22 return 1
23 }
24 let keysfile: *u8 = argv[1] as *u8
25 let storefile: *u8 = argv[2] as *u8
26 let realm: *u8 = argv[3] as *u8
27 let handle: *u8 = argv[4] as *u8
28 let pwfile: *u8 = argv[5] as *u8
29 let mnout: *u8 = argv[6] as *u8
30 let realm_n: i64 = arm_len(realm)
31 let handle_n: i64 = arm_len(handle)
32
33 // ---- read the operator passphrase from the staged file; trim trailing CR/LF (editors add them) ----
34 let pwlen: *i64 = sys_mmap(16) as *i64
35 pwlen[0] = 0
36 let pwbuf: *u8 = sys_read_file(pwfile, pwlen)
37 if (pwbuf as i64) == 0 { arm_w("ARM FAIL: cannot read passphrase file\n" as *u8); return 2 }
38 // strip a leading UTF-8 BOM (EF BB BF) -- editors / shell pipes prepend one and it would
39 // silently become part of the passphrase, so login with the typed passphrase would never match.
40 var pw_start: i64 = 0
41 if pwlen[0] >= 3 {
42 if (pwbuf[0] as i64) == 239 { if (pwbuf[1] as i64) == 187 { if (pwbuf[2] as i64) == 191 { pw_start = 3 } } }
43 }
44 var pw_n: i64 = pwlen[0]
45 var trimming: i64 = 1
46 while trimming == 1 {
47 trimming = 0
48 if pw_n > pw_start {
49 let c: i64 = pwbuf[pw_n - 1] as i64
50 if c == 10 { pw_n = pw_n - 1; trimming = 1 }
51 if c == 13 { pw_n = pw_n - 1; trimming = 1 }
52 }
53 }
54 let pw_ptr: *u8 = ((pwbuf as i64) + pw_start) as *u8
55 let pw_eff: i64 = pw_n - pw_start
56 if pw_eff < 1 { arm_w("ARM FAIL: passphrase file is empty\n" as *u8); return 2 }
57
58 // ---- arm the server-key bundle (idempotent, persists to keysfile) + the realm context ----
59 let oprf_seed: *u8 = sys_mmap(32)
60 let akp: *u8 = sys_mmap(32)
61 let akb: *u8 = sys_mmap(33)
62 let edp: *u8 = sys_mmap(32)
63 let edb: *u8 = sys_mmap(32)
64 if nx_uas_server_keys_load_or_init(keysfile, oprf_seed, akp, akb, edp, edb) != NX_UAS_OK { arm_w("ARM FAIL: server-key bundle\n" as *u8); return 3 }
65 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
66 if nx_auth_context_init(ctx, realm, realm_n, realm, realm_n, storefile as i64, oprf_seed, edp, edb, 900, K_MAGIC_8192, 1, 1, 5, 1) != NX_MAUTH_OK { arm_w("ARM FAIL: context init\n" as *u8); return 4 }
67
68 // ---- register the admin -> one-time BIP39 mnemonic (additive-only; re-run supersedes) ----
69 let mn: *u8 = sys_mmap(512)
70 let mn_n: *i64 = sys_mmap(16) as *i64
71 mn_n[0] = 0
72 if nx_modern_auth_register(ctx, handle, handle_n, pw_ptr, pw_eff, mn, 512, mn_n) != NX_MAUTH_OK { arm_w("ARM FAIL: register\n" as *u8); return 5 }
73 if mn_n[0] < 1 { arm_w("ARM FAIL: empty mnemonic\n" as *u8); return 6 }
74
75 // ---- write the recovery mnemonic to the out-file (0600), never to stdout ----
76 let fd: i64 = sys_openat_wr(mnout, 0x180)
77 if fd < 0 { arm_w("ARM FAIL: cannot open mnemonic out-file\n" as *u8); return 7 }
78 sys_write(fd, mn, mn_n[0])
79 sys_write(fd, "\n" as *u8, 1)
80 sys_close(fd)
81
82 arm_w("ARM OK: admin provisioned for realm '" as *u8)
83 sys_write(1, realm, realm_n)
84 arm_w("'. 24-word BIP39 recovery mnemonic written to the out-file (0600).\n" as *u8)
85 arm_w(" -> SAVE the mnemonic somewhere safe, then DELETE the passphrase file AND the mnemonic out-file.\n" as *u8)
86 arm_w(" -> Login from now on: POST handle+passphrase; the server mints a no-cookie X-Nishi-Session token.\n" as *u8)
87 return 0
88}