code wiki / _hdl_build / nx_modauth_e2e_gate.nx
nx_modauth_e2e_gate.nx source
↩ module page · 228 lines · 10933 B
1// nx_modauth_e2e_gate.nx -- ENGINEER gate: the WHOLE modern-auth login stack end to end.
2//
3// Composes the real production path the wiki daemon will call: server key bundle
4// (load-or-init, idempotent) -> nx_modern_auth_register (OPAQUE registration +
5// argon2id KSF + additive-only store) -> nx_modern_auth_login (OPAQUE-3DH KE1/KE2/KE3
6// + ServerFinish + Ed25519 session mint) -> nx_modern_auth_validate_session.
7//
8// 14 rows:
9// 1 keygen idempotent (second load == first) 6 tampered token rejected
10// 2 register OK + 24-word BIP39 mnemonic issued 7 expired token rejected
11// 3 login correct pw -> 152B token 8 re-register supersedes (old pw dies)
12// 4 token validates + user_id_hash matches 9 new pw lives after supersede
13// 5 wrong pw -> WRONG_PASSPHRASE 10 unknown user -> USER_NOT_FOUND
14// 11 recover w/ mnemonic -> OK + NEW rotated mnemonic
15// 12 login with recovered passphrase -> OK (and pre-recover pw dead)
16// 13 recover with the OLD mnemonic after rotation -> RECOVERY_MISMATCH
17// 14 recover with a typo'd mnemonic (checksum) -> RECOVERY_MISMATCH
18//
19// KSF here: argon2id m=8 MiB t=1 (real memory-hard path, gate-speed params).
20// Production realm ctx uses vault-lineage 32 MiB / t=3. Recovery credential = KSF
21// Identity by design (256-bit CSPRNG entropy; no dictionary to defend against).
22// license_tier: ORIGINAL
23import "hub/nx_modern_auth_flow.nx"
24import "nx_syscalls.nx"
25
26func eg_w(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
27func eg_row(idx: i64, ok: i64, name: *u8) -> i64 {
28 if ok == 1 { eg_w("ROW " as *u8) }
29 if ok != 1 { eg_w("FAIL " as *u8) }
30 let d: *u8 = sys_mmap(4)
31 d[0] = (48 + (idx / 10)) as u8
32 d[1] = (48 + (idx % 10)) as u8
33 d[2] = 32 as u8
34 sys_write(1, d, 3)
35 eg_w(name)
36 eg_w("\n" as *u8)
37 if ok == 1 { return 1 }
38 return 0
39}
40func eg_eq(a: *u8, b: *u8, n: i64) -> i64 {
41 var i: i64 = 0
42 while i < n { if (a[i] as i64) != (b[i] as i64) { return 0 } i = i + 1 }
43 return 1
44}
45func eg_trunc(path: *u8) -> i64 {
46 let fd: i64 = sys_openat_wr(path, 0x180)
47 if fd >= 0 { sys_close(fd) }
48 return 0
49}
50
51func main() -> i64 {
52 let store_path: *u8 = "/tmp/nishi_mauth_gate_store.log" as *u8
53 let keys_path: *u8 = "/tmp/nishi_mauth_gate_keys.log" as *u8
54 eg_trunc(store_path)
55 eg_trunc(keys_path)
56 var pass: i64 = 0
57
58 // ---- row 1: server key bundle load-or-init, idempotent ----
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 var r1: i64 = 0
65 if nx_uas_server_keys_load_or_init(keys_path, oprf_seed, akp, akb, edp, edb) == NX_UAS_OK {
66 let o2: *u8 = sys_mmap(32)
67 let a2: *u8 = sys_mmap(32)
68 let b2: *u8 = sys_mmap(33)
69 let e2: *u8 = sys_mmap(32)
70 let f2: *u8 = sys_mmap(32)
71 if nx_uas_server_keys_load_or_init(keys_path, o2, a2, b2, e2, f2) == NX_UAS_OK {
72 if eg_eq(oprf_seed, o2, 32) == 1 { if eg_eq(edp, e2, 32) == 1 { r1 = 1 } }
73 }
74 }
75 pass = pass + eg_row(1, r1, "server-keys load-or-init idempotent" as *u8)
76
77 // ---- auth context (the wiki realm shape) ----
78 let realm: *u8 = "nishi_wiki_admin" as *u8
79 let disp: *u8 = "Nishi wiki admin" as *u8
80 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext
81 let rc_ctx: i64 = nx_auth_context_init(ctx, realm, 16, disp, 16,
82 store_path as i64, oprf_seed, edp, edb,
83 900, 8192, 1, 1, 5, 1)
84 if rc_ctx != NX_MAUTH_OK { eg_w("CTX-INIT FAILED\n" as *u8); sys_exit(1) }
85
86 let handle: *u8 = "elder" as *u8
87 let pw: *u8 = "correct horse battery staple" as *u8
88 let pw_n: i64 = 28
89 let pw2: *u8 = "rotated passphrase 2026" as *u8
90 let pw2_n: i64 = 23
91
92 // ---- row 2: register + 24-word mnemonic issued ----
93 let mn: *u8 = sys_mmap(512)
94 let mn_n: *i64 = sys_mmap(16) as *i64
95 var r2: i64 = 0
96 if nx_modern_auth_register(ctx, handle, 5, pw, pw_n, mn, 512, mn_n) == NX_MAUTH_OK {
97 if mn_n[0] > 0 {
98 // 24 words = 23 spaces
99 var sp: i64 = 0
100 var si: i64 = 0
101 while si < mn_n[0] { if (mn[si] as i64) == 32 { sp = sp + 1 } si = si + 1 }
102 if sp == 23 { r2 = 1 }
103 }
104 }
105 pass = pass + eg_row(2, r2, "register stores record + issues 24-word mnemonic" as *u8)
106
107 // ---- row 3: login correct passphrase ----
108 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES)
109 let tok_n: *i64 = sys_mmap(16) as *i64
110 var r3: i64 = 0
111 if nx_modern_auth_login(ctx, handle, 5, pw, pw_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == NX_MAUTH_OK {
112 if tok_n[0] == NX_MAUTH_SESSION_TOKEN_BYTES { r3 = 1 }
113 }
114 pass = pass + eg_row(3, r3, "login correct pw -> 152B session token" as *u8)
115
116 // ---- row 4: token validates, user hash matches ----
117 let now_s: i64 = sys_now_realtime_sec()
118 let uh: *u8 = sys_mmap(32)
119 let uh_n: *i64 = sys_mmap(16) as *i64
120 var r4: i64 = 0
121 if nx_modern_auth_validate_session(ctx, tok, NX_MAUTH_SESSION_TOKEN_BYTES, now_s, uh, 32, uh_n) == NX_MAUTH_OK {
122 let want_uh: *u8 = sys_mmap(32)
123 nx_ncs_derive_user_id_hash(realm, 16, handle, 5, want_uh)
124 if eg_eq(uh, want_uh, 32) == 1 { r4 = 1 }
125 }
126 pass = pass + eg_row(4, r4, "session validates + user_id_hash matches" as *u8)
127
128 // ---- row 5: wrong passphrase rejected ----
129 let bad_pw: *u8 = "incorrect horse battery stap" as *u8
130 var r5: i64 = 0
131 if nx_modern_auth_login(ctx, handle, 5, bad_pw, pw_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == (0 - NX_MAUTH_WRONG_PASSPHRASE) { r5 = 1 }
132 pass = pass + eg_row(5, r5, "wrong pw -> WRONG_PASSPHRASE" as *u8)
133
134 // ---- row 6: tampered token rejected ----
135 let tok2: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES)
136 let tok2_n: *i64 = sys_mmap(16) as *i64
137 nx_modern_auth_login(ctx, handle, 5, pw, pw_n, tok2, NX_MAUTH_SESSION_TOKEN_BYTES, tok2_n)
138 tok2[100] = ((tok2[100] as i64) ^ 1) as u8
139 var r6: i64 = 0
140 if nx_modern_auth_validate_session(ctx, tok2, NX_MAUTH_SESSION_TOKEN_BYTES, now_s, 0 as *u8, 0, uh_n) == (0 - NX_MAUTH_INVALID_SESSION) { r6 = 1 }
141 pass = pass + eg_row(6, r6, "tampered token -> INVALID_SESSION" as *u8)
142
143 // ---- row 7: expired token rejected ----
144 let tok3: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES)
145 let tok3_n: *i64 = sys_mmap(16) as *i64
146 nx_modern_auth_login(ctx, handle, 5, pw, pw_n, tok3, NX_MAUTH_SESSION_TOKEN_BYTES, tok3_n)
147 var r7: i64 = 0
148 if nx_modern_auth_validate_session(ctx, tok3, NX_MAUTH_SESSION_TOKEN_BYTES, now_s + 901, 0 as *u8, 0, uh_n) == (0 - NX_MAUTH_EXPIRED) { r7 = 1 }
149 pass = pass + eg_row(7, r7, "expired token -> EXPIRED" as *u8)
150
151 // ---- row 8+9: re-register supersedes (additive-only) ----
152 // (also rotates the mnemonic; the post-re-register one is what rows 11-13 use)
153 nx_modern_auth_register(ctx, handle, 5, pw2, pw2_n, mn, 512, mn_n)
154 var r8: i64 = 0
155 if nx_modern_auth_login(ctx, handle, 5, pw, pw_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == (0 - NX_MAUTH_WRONG_PASSPHRASE) { r8 = 1 }
156 pass = pass + eg_row(8, r8, "re-register supersedes: old pw rejected" as *u8)
157 var r9: i64 = 0
158 if nx_modern_auth_login(ctx, handle, 5, pw2, pw2_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == NX_MAUTH_OK { r9 = 1 }
159 pass = pass + eg_row(9, r9, "new pw accepted after supersede" as *u8)
160
161 // ---- row 10: unknown user ----
162 let ghost: *u8 = "ghost" as *u8
163 var r10: i64 = 0
164 if nx_modern_auth_login(ctx, ghost, 5, pw, pw_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == (0 - NX_MAUTH_USER_NOT_FOUND) { r10 = 1 }
165 pass = pass + eg_row(10, r10, "unknown user -> USER_NOT_FOUND" as *u8)
166
167 // ---- row 11: recover with the current mnemonic -> OK + rotated mnemonic ----
168 let pw3: *u8 = "recovered passphrase three" as *u8
169 let pw3_n: i64 = 26
170 let mn2: *u8 = sys_mmap(512)
171 let mn2_n: *i64 = sys_mmap(16) as *i64
172 var r11: i64 = 0
173 if nx_modern_auth_recover(ctx, handle, 5, mn, mn_n[0], pw3, pw3_n, mn2, 512, mn2_n) == NX_MAUTH_OK {
174 if mn2_n[0] > 0 {
175 var same: i64 = 0
176 if mn2_n[0] == mn_n[0] { same = eg_eq(mn2, mn, mn_n[0]) }
177 if same == 0 { r11 = 1 }
178 }
179 }
180 pass = pass + eg_row(11, r11, "recover w/ mnemonic -> new pw set + mnemonic ROTATED" as *u8)
181
182 // ---- row 12: login with recovered passphrase; pre-recover pw dead ----
183 var r12: i64 = 0
184 if nx_modern_auth_login(ctx, handle, 5, pw3, pw3_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == NX_MAUTH_OK {
185 if nx_modern_auth_login(ctx, handle, 5, pw2, pw2_n, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tok_n) == (0 - NX_MAUTH_WRONG_PASSPHRASE) { r12 = 1 }
186 }
187 pass = pass + eg_row(12, r12, "recovered pw lives, pre-recover pw dead" as *u8)
188
189 // ---- row 13: OLD mnemonic after rotation -> RECOVERY_MISMATCH ----
190 var r13: i64 = 0
191 if nx_modern_auth_recover(ctx, handle, 5, mn, mn_n[0], pw3, pw3_n, mn2, 512, mn2_n) == (0 - NX_MAUTH_RECOVERY_MISMATCH) { r13 = 1 }
192 pass = pass + eg_row(13, r13, "old mnemonic after rotation -> RECOVERY_MISMATCH" as *u8)
193
194 // ---- row 14: typo'd mnemonic (bad checksum) -> RECOVERY_MISMATCH ----
195 let mn_typo: *u8 = sys_mmap(512)
196 var ti: i64 = 0
197 while ti < mn2_n[0] { mn_typo[ti] = mn2[ti]; ti = ti + 1 }
198 // overwrite the first word with "zoo " padding-safe: just flip first letter to make
199 // a DIFFERENT valid-or-invalid word start; simplest deterministic typo = swap first
200 // word entirely by writing "zoo" + original tail from the first space.
201 var sp1: i64 = 0
202 var sscan: i64 = 1
203 while sscan == 1 {
204 if sp1 >= mn2_n[0] { sscan = 0 }
205 if sscan == 1 { if (mn_typo[sp1] as i64) == 32 { sscan = 0 } }
206 if sscan == 1 { sp1 = sp1 + 1 }
207 }
208 // build "zoo" || tail
209 let typo2: *u8 = sys_mmap(512)
210 typo2[0] = 0x7A as u8; typo2[1] = 0x6F as u8; typo2[2] = 0x6F as u8
211 var tj: i64 = 0
212 while sp1 + tj < mn2_n[0] { typo2[3 + tj] = mn_typo[sp1 + tj]; tj = tj + 1 }
213 let typo2_n: i64 = 3 + tj
214 var r14: i64 = 0
215 var rc14: i64 = nx_modern_auth_recover(ctx, handle, 5, typo2, typo2_n, pw3, pw3_n, mn2, 512, mn2_n)
216 if rc14 == (0 - NX_MAUTH_RECOVERY_MISMATCH) { r14 = 1 }
217 // (1/256 chance the forged checksum still passes BIP39 but then OPAQUE rejects ->
218 // also RECOVERY_MISMATCH, so the assert is deterministic either way)
219 pass = pass + eg_row(14, r14, "typo'd mnemonic -> RECOVERY_MISMATCH" as *u8)
220
221 if pass == 14 {
222 eg_w("MODAUTH E2E GATE GREEN 14/14 (OPAQUE register/login/RECOVER + BIP39 rotate + argon2id KSF + store + session mint/validate)\n" as *u8)
223 sys_exit(0)
224 }
225 eg_w("MODAUTH E2E GATE RED\n" as *u8)
226 sys_exit(1)
227 return 1
228}