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 }