code wiki / _hdl_build / nx_nojs_session_gate.nx

nx_nojs_session_gate.nx source

↩ module page · 123 lines · 7839 B

1import "nx_gate_base.nx" 2// nx_nojs_session_gate.nx -- SOVEREIGN in-process referee for the NISHI-FIRST no-cookie ?s= query-session 3// (the shared nx_site_auth primitive docportal/mail/siteedit all depend on). Arms a real Modern-Auth realm, 4// mints a real OPAQUE session token, then proves the query-session round-trip + the docportal no-JS wiring: 5// N1 ROUND-TRIP : token -> nx_sa_tok_urlenc -> ?s= -> nx_sa_qs_raw -> pct_decode -> b64_decode == token 6// N2 VALIDATE : nx_sa_validate_qs(ctx, "/admin/ui?s=<enc>") == NX_MAUTH_OK (same Ed25519 validator) 7// N3 NEG TAMPER : flip one byte of the token in ?s= -> validate DENIES (no forgery) 8// N4 UI PAGE : dad_handle GET /admin/ui?s=<enc> -> 200 + the authed no-JS page 9// N5 LOGIN->HTML : dad_handle POST /admin/login (ui=1) -> 200 HTML landing carrying /admin/ui?s= 10// GREEN iff 5/5. Proves NO Set-Cookie is needed for a full no-JS session (nishi-first). license_tier: ORIGINAL 11import "nx_docportal_admin_daemon.nx" 12import "hub/nx_modern_auth_flow.nx" 13import "nx_syscalls.nx" 14 15func grow(name: *u8, ok: i64) -> i64 { if ok==1 { gw(" PASS " as *u8) } else { gw(" FAIL " as *u8) } gw(name); gw(" 16" as *u8); return ok } 17func gln(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 18func gcat(d: *u8, o: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){d[o+i]=s[i];i=i+1} return o+i } 19func gcontains(hay: *u8, n: i64, needle: *u8) -> i64 { 20 let nn: i64 = gln(needle) 21 var i: i64 = 0 22 while i + nn <= n { var m: i64=1; var j: i64=0; while j<nn { if (hay[i+j] as i64)!=(needle[j] as i64) {m=0;j=nn} else {j=j+1} } if m==1 {return 1} i=i+1 } 23 return 0 24} 25func gmemeq(a: *u8, b: *u8, n: i64) -> i64 { var i: i64=0; while i<n { if a[i]!=b[i] {return 0} i=i+1 } return 1 } 26func gcell(ok: i64, id: *u8, txt: *u8) -> i64 { if ok==1 {gw(" PASS " as *u8)} else {gw(" FAIL " as *u8)} gw(id); gw(" " as *u8); gw(txt); gw("\n" as *u8); return ok } 27 28func main() -> i64 { 29 gw("=== NX-NOJS-SESSION-GATE -- the shared no-cookie ?s= query-session (nishi-first) ===\n" as *u8) 30 let store_path: *u8 = "/tmp/nx_nojs_store.log" as *u8 31 let keys_path: *u8 = "/tmp/nx_nojs_keys.log" as *u8 32 let f1: i64 = sys_openat_wr(store_path, 0x180); if f1>=0 {sys_close(f1)} 33 let f2: i64 = sys_openat_wr(keys_path, 0x180); if f2>=0 {sys_close(f2)} 34 35 let oprf: *u8 = sys_mmap(32); let akp: *u8 = sys_mmap(32); let akb: *u8 = sys_mmap(33) 36 let edp: *u8 = sys_mmap(32); let edb: *u8 = sys_mmap(32) 37 if nx_uas_server_keys_load_or_init(keys_path, oprf, akp, akb, edp, edb) != NX_UAS_OK { gw("KEYS FAIL\n" as *u8); sys_exit(1) } 38 let realm: *u8 = "nishi_docportal" as *u8 39 let rn: i64 = gln(realm) 40 let ctx: *NxAuthContext = sys_mmap(256) as *NxAuthContext 41 if nx_auth_context_init(ctx, realm, rn, realm, rn, store_path as i64, oprf, edp, edb, 900, 8192, 1, 1, 5, 1) != NX_MAUTH_OK { gw("CTX FAIL\n" as *u8); sys_exit(1) } 42 let mn: *u8 = sys_mmap(512); let mnn: *i64 = sys_mmap(16) as *i64 43 if nx_modern_auth_register(ctx, "elder" as *u8, 5, "correct horse battery staple" as *u8, 28, mn, 512, mnn) != NX_MAUTH_OK { gw("REGISTER FAIL\n" as *u8); sys_exit(1) } 44 // mint a real session token 45 let tok: *u8 = sys_mmap(NX_MAUTH_SESSION_TOKEN_BYTES) 46 let tokn: *i64 = sys_mmap(8) as *i64 47 tokn[0] = 0 48 if nx_modern_auth_login(ctx, "elder" as *u8, 5, "correct horse battery staple" as *u8, 28, tok, NX_MAUTH_SESSION_TOKEN_BYTES, tokn) != NX_MAUTH_OK { gw("LOGIN FAIL\n" as *u8); sys_exit(1) } 49 let b64: *u8 = sys_mmap(256) 50 let b64n: i64 = b64_encode(tok, NX_MAUTH_SESSION_TOKEN_BYTES, b64) 51 52 // build /admin/ui?s=<urlenc(b64)> 53 let path: *u8 = sys_mmap(1024) 54 var po: i64 = gcat(path, 0, "/admin/ui?s=" as *u8) 55 po = nx_sa_tok_urlenc(b64, b64n, path, po) 56 let pn: i64 = po 57 let now: i64 = sys_now_realtime_sec() 58 var pass: i64 = 0 59 60 // N1 round-trip 61 let voff: *i64 = sys_mmap(8) as *i64 62 let rawlen: i64 = nx_sa_qs_raw(path, pn, voff) 63 let raw: *u8 = ((path as i64) + voff[0]) as *u8 64 let dec: *u8 = sys_mmap(512) 65 let dn: i64 = nx_sa_pct_decode(raw, rawlen, dec, 511) 66 let tok2: *u8 = sys_mmap(256) 67 let t2n: i64 = b64_decode(dec, dn, tok2) 68 var n1: i64 = 0 69 if t2n == NX_MAUTH_SESSION_TOKEN_BYTES { if gmemeq(tok, tok2, NX_MAUTH_SESSION_TOKEN_BYTES) == 1 { n1 = 1 } } 70 pass = pass + gcell(n1, "N1" as *u8, "token -> tok_urlenc -> qs_raw -> pct_decode -> b64_decode == token (exact round-trip)" as *u8) 71 72 // N2 validate 73 let hb: *u8 = sys_mmap(64); let hbn: *i64 = sys_mmap(8) as *i64 74 let v2: i64 = nx_sa_validate_qs(ctx, path, pn, now, hb, 64, hbn) 75 var n2: i64 = 0 76 if v2 == NX_MAUTH_OK { n2 = 1 } 77 pass = pass + gcell(n2, "N2" as *u8, "nx_sa_validate_qs(?s=<enc>) == NX_MAUTH_OK (same Ed25519 validator as the header path)" as *u8) 78 79 // N3 NEG tamper -- flip a byte inside the token value 80 let tpath: *u8 = sys_mmap(1024) 81 var ti: i64 = 0 82 while ti < pn { tpath[ti] = path[ti]; ti = ti + 1 } 83 let flip: i64 = voff[0] + 4 84 if (tpath[flip] as i64) == 65 { tpath[flip] = 66 as u8 } else { tpath[flip] = 65 as u8 } 85 let hb3: *u8 = sys_mmap(64); let hbn3: *i64 = sys_mmap(8) as *i64 86 let v3: i64 = nx_sa_validate_qs(ctx, tpath, pn, now, hb3, 64, hbn3) 87 var n3: i64 = 0 88 if v3 != NX_MAUTH_OK { n3 = 1 } 89 pass = pass + gcell(n3, "N3" as *u8, "flip one token byte -> validate DENIES (no forgery; tamper-evident)" as *u8) 90 91 // N4 UI page through the REAL router 92 let req: *u8 = sys_mmap(2048) 93 var rq: i64 = gcat(req, 0, "GET " as *u8) 94 rq = gcat_ncopy(req, rq, path, pn) 95 rq = gcat(req, rq, " HTTP/1.1\r\nHost: admin.andelinwest.com\r\n\r\n" as *u8) 96 let out: *u8 = sys_mmap(DAD_OUTCAP) 97 let o4: i64 = dad_handle(ctx, req, rq, out) 98 var n4: i64 = 0 99 if gcontains(out, o4, "200 OK" as *u8) == 1 { if gcontains(out, o4, "document portal" as *u8) == 1 { n4 = 1 } } 100 pass = pass + gcell(n4, "N4" as *u8, "dad_handle GET /admin/ui?s=<enc> -> 200 + the authed no-JS page" as *u8) 101 102 // N5 login (ui=1) -> HTML landing carrying /admin/ui?s= 103 let req5: *u8 = sys_mmap(2048) 104 let bodyf: *u8 = "ui=1&handle=elder&passphrase=correct%20horse%20battery%20staple" as *u8 105 let bfn: i64 = gln(bodyf) 106 var r5: i64 = gcat(req5, 0, "POST /admin/login HTTP/1.1\r\nHost: admin.andelinwest.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " as *u8) 107 r5 = gcatn_i(req5, r5, bfn) 108 r5 = gcat(req5, r5, "\r\n\r\n" as *u8) 109 r5 = gcat_ncopy(req5, r5, bodyf, bfn) 110 let out5: *u8 = sys_mmap(DAD_OUTCAP) 111 let o5: i64 = dad_handle(ctx, req5, r5, out5) 112 var n5: i64 = 0 113 if gcontains(out5, o5, "200 OK" as *u8) == 1 { if gcontains(out5, o5, "/admin/ui?s=" as *u8) == 1 { n5 = 1 } } 114 pass = pass + gcell(n5, "N5" as *u8, "POST /admin/login ui=1 -> 200 HTML landing carrying /admin/ui?s= (no Set-Cookie)" as *u8) 115 116 gw("NX-NOJS-SESSION-GATE pass=" as *u8); gw_n(pass); gw("/5 verdict=" as *u8) 117 if pass == 5 { gw("GREEN (nishi-first no-cookie ?s= session proven end-to-end; the shared primitive mail/siteedit reuse)\n" as *u8); sys_exit(0); return 0 } 118 gw("RED\n" as *u8); sys_exit(1) 119 return 1 120} 121func gw_n(v: i64) -> i64 { var m: i64=v; if m<0 {gw("-" as *u8);m=0-m} let t: *u8=sys_mmap(24); 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} let o: *u8=sys_mmap(24); var i: i64=0; while i<k {o[i]=t[k-1-i];i=i+1} sys_write(1,o,k); return 0 } 122func gcat_ncopy(d: *u8, o: i64, s: *u8, n: i64) -> i64 { var i: i64=0; while i<n {d[o+i]=s[i];i=i+1} return o+n } 123func gcatn_i(d: *u8, o: i64, v: i64) -> i64 { var m: i64=v; if m==0 {d[o]=48 as u8; return o+1} let t: *u8=sys_mmap(24); var k: i64=0; while m>0 {t[k]=(48+(m%10)) as u8;m=m/10;k=k+1} var i: i64=0; while i<k {d[o+i]=t[k-1-i];i=i+1} return o+k }