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}