code wiki / (root) / nx_login_e2e_probe.nx

nx_login_e2e_probe.nx source

↩ module page · 183 lines · 11070 B

1// nx_login_e2e_probe.nx -- SOVEREIGN end-to-end auth proof (operator: "prove it, no bullshit loop"). Our 2// nx_https_fetch_follow is GET-only; this adds the missing rung: a TLS-1.3 client that POSTs (register/login) 3// and sets X-Nishi-Session (validate at the gated /wiki). It PROVES the wiki-gateway key+realm fix: a token 4// minted by :9091 must validate at :18791 and return GATED CONTENT, not the login/bootstrap. Flow against the 5// LIVE site: POST /register (throwaway handle) -> POST /login -> token -> GET /wiki/creation WITH the token. 6// Reuses the proven connect+handshake (nx_https_url_*, nx_tls13_client_session_run) + the record encrypt/read 7// path lifted from nx_https_get_complete (only the request is pre-built instead of a baked GET). A CLIENT that 8// runs+exits (dev-spoke querying the hub) -- NOT a daemon. license_tier: ORIGINAL expect_exit: 0 9import "nx_syscalls.nx" 10import "nx_x509_trust_store.nx" 11import "nx_trust_store_load_from_certdata.nx" 12import "nx_tls13_client_validate_certificate.nx" 13import "nx_tls13_client_session_run.nx" 14import "nx_https_url_for_fetch.nx" 15import "nx_https_url_connect.nx" 16import "nx_http_response_parse.nx" 17import "nx_tls13.nx" 18import "nx_tls13_record.nx" 19import "nx_tls13_read_record_from_fd.nx" 20import "nx_tls13_client_session.nx" 21import "nx_chacha20_poly1305.nx" 22import "nx_u256.nx" // nx_scratch_save/restore -- free the crypto scratch between connections (else the 2nd+ handshake's cert-validation slows to ~30s -> server drops the socket -> SIGPIPE) 23const K_MAGIC_16645: i64 = 16645 24const K_MAGIC_4194304: i64 = 4194304 25const K_MAGIC_8192: i64 = 8192 26const K_MAGIC_262144: i64 = 262144 27const K_MAGIC_1024: i64 = 1024 28 29const HOST: *u8 = "nishifamily.com" 30const HOSTLEN: i64 = 15 31 32func ep(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 33func epn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0 {b[0]=45;sys_write(1,b,1);x=0-x}; if x==0 {b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0 {d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0 {b[i]=(48+(y%10)) as u8; y=y/10; i=i-1} sys_write(1,b,d); return 0 } 34func ecat(dst: *u8, off: i64, s: *u8) -> i64 { var o: i64=off; var i: i64=0; while s[i]!=(0 as u8){dst[o]=s[i]; o=o+1; i=i+1} return o } 35func eslen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } 36func econtains(buf: *u8, n: i64, needle: *u8) -> i64 { 37 var nl: i64=0; while needle[nl]!=(0 as u8){nl=nl+1} 38 if nl==0 { return 0 } 39 var i: i64=0 40 while i+nl<=n { var j: i64=0; var ok: i64=1; while j<nl { if buf[i+j]!=needle[j] { ok=0; j=nl } else { j=j+1 } } if ok==1 { return 1 } i=i+1 } 41 return 0 42} 43 44// write exactly n bytes to fd (loop) 45func ewrite_n(fd: i64, buf: *u8, n: i64) -> i64 { var off: i64=0; while off<n { let w: i64=sys_write(fd, ((buf as i64)+off) as *u8, n-off); if w<=0 { return 0-1 } off=off+w } return 0 } 46 47// Send a PRE-BUILT request over a connected session; drain the full response into out. Returns bytes (or <0). 48// (record encrypt/send + decrypt/read loop lifted from nx_https_get_complete; timing prints dropped.) 49func e2e_send(s: *Tls13ClientSession, fd: i64, req: *u8, req_len: i64, out: *u8, out_cap: i64) -> i64 { 50 if s.state != NX_TLS13_CSESSION_STATE_CONNECTED { return 0-1 } 51 let rec_buf: *u8 = sys_mmap(req_len + 64) 52 let header_out: *u8 = rec_buf 53 let ct_out: *u8 = ((rec_buf as i64) + NX_TLS13_RECORD_HEADER_LEN) as *u8 54 let tag_out: *u8 = ((rec_buf as i64) + NX_TLS13_RECORD_HEADER_LEN + req_len + 1) as *u8 55 let enc_v: i64 = nx_tls13_record_encrypt_v2(s.cipher_suite, s.client_app_traffic_key, s.client_app_iv, s.client_app_seq, req, req_len, NX_TLS13_CT_APPLICATION_DATA, 0, header_out, ct_out, tag_out) 56 s.client_app_seq = s.client_app_seq + 1 57 if enc_v != NX_TLS13_REC_VERDICT_OK { return 0-2 } 58 let total: i64 = NX_TLS13_RECORD_HEADER_LEN + req_len + 1 + NX_TLS13_RECORD_TAG_LEN 59 if ewrite_n(fd, rec_buf, total) < 0 { return 0-3 } 60 var acc: i64 = 0 61 let rec_in: *u8 = sys_mmap(K_MAGIC_16645) 62 let plain: *u8 = sys_mmap(K_MAGIC_16645) 63 let ct_p: *i64 = sys_mmap(16) as *i64 64 let len_p: *i64 = sys_mmap(16) as *i64 65 while acc < out_cap { 66 let rin: i64 = nx_tls13_read_record_from_fd(fd, rec_in, K_MAGIC_16645) 67 if rin < 0 { return acc } 68 let ctlen: i64 = rin - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN 69 let rin_ct: *u8 = ((rec_in as i64) + NX_TLS13_RECORD_HEADER_LEN) as *u8 70 let rin_tag: *u8 = ((rec_in as i64) + rin - NX_TLS13_RECORD_TAG_LEN) as *u8 71 let dv: i64 = nx_tls13_record_decrypt_v2(s.cipher_suite, s.server_app_traffic_key, s.server_app_iv, s.server_app_seq, rec_in, rin_ct, ctlen, rin_tag, plain, ct_p, len_p) 72 s.server_app_seq = s.server_app_seq + 1 73 if dv != NX_TLS13_REC_VERDICT_OK { return acc } 74 if ct_p[0] == NX_TLS13_CT_ALERT { return acc } 75 if ct_p[0] == NX_TLS13_CT_APPLICATION_DATA { 76 let tc: i64 = len_p[0] 77 if acc + tc > out_cap { return acc } 78 var i: i64 = 0 79 while i < tc { out[acc+i] = plain[i]; i=i+1 } 80 acc = acc + tc 81 } 82 } 83 return acc 84} 85 86// connect + TLS handshake to nishifamily.com:443, send req, read response into out. Returns bytes (<0 on fail). 87func e2e_req(store: *TrustStore, req: *u8, req_len: i64, out: *u8, out_cap: i64) -> i64 { 88 let _sm: i64 = nx_scratch_save() 89 let urlbuf: *u8 = sys_mmap(256) 90 let _u: i64 = ecat(urlbuf, 0, "https://nishifamily.com/" as *u8); urlbuf[_u] = 0 as u8 91 let target_raw: *u8 = sys_mmap(64) 92 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget 93 target.url = nx_url_new(); target.port = 0 94 if nx_https_url_for_fetch(urlbuf, target) != NX_HTTPS_URL_OK { return 0-10 } 95 let fd_p: *i64 = sys_mmap(16) as *i64 96 if nx_https_url_connect(target, urlbuf, sys_now_realtime_sec(), fd_p) != NX_HTTPS_CONNECT_OK { return 0-11 } 97 let fd: i64 = fd_p[0] 98 let cr: *u8 = sys_mmap(32); let priv: *u8 = sys_mmap(32) 99 var i: i64 = 0; while i < 32 { cr[i]=(0xC0+i) as u8; priv[i]=(0xA0+i) as u8; i=i+1 } 100 let vc_raw: *u8 = sys_mmap(64) 101 let vc: *TlsValidationContext = vc_raw as *TlsValidationContext 102 vc.store = store 103 vc.sni_host = ((urlbuf as i64) + target.url.host_off) as *u8 104 vc.sni_host_len = target.url.host_len 105 vc.now_epoch = sys_now_realtime_sec() 106 let sr: i64 = nx_tls13_client_session_run(fd, ((urlbuf as i64)+target.url.host_off) as *u8, target.url.host_len, cr, priv, vc) 107 if sr <= 0 { sys_close(fd); return 0-12 } 108 let session: *Tls13ClientSession = sr as *Tls13ClientSession 109 let n: i64 = e2e_send(session, fd, req, req_len, out, out_cap) 110 sys_close(fd) 111 nx_scratch_restore(_sm) 112 return n 113} 114 115// POST <path> with form body. Returns request length. 116func build_post(path: *u8, body: *u8, out: *u8) -> i64 { 117 let bl: i64 = eslen(body) 118 var o: i64 = 0 119 o = ecat(out, o, "POST " as *u8); o = ecat(out, o, path); o = ecat(out, o, " HTTP/1.1\r\nHost: nishifamily.com\r\nContent-Type: application/x-www-form-urlencoded\r\nContent-Length: " as *u8) 120 // itoa bl 121 if bl==0 { out[o]=48; o=o+1 } else { let t: *u8=sys_mmap(24); var m: i64=bl; var k: i64=0; while m>0 {t[k]=(48+(m%10)) as u8; m=m/10; k=k+1} var q: i64=k-1; while q>=0 {out[o]=t[q]; o=o+1; q=q-1} } 122 o = ecat(out, o, "\r\nConnection: close\r\n\r\n" as *u8) 123 o = ecat(out, o, body) 124 return o 125} 126// GET <path> with X-Nishi-Session + a browser-like Accept (so the sites daemon proxies, not bootstraps). 127func build_get_auth(path: *u8, tok: *u8, out: *u8) -> i64 { 128 var o: i64 = 0 129 o = ecat(out, o, "GET " as *u8); o = ecat(out, o, path); o = ecat(out, o, " HTTP/1.1\r\nHost: nishifamily.com\r\nAccept: */*\r\nX-Nishi-Session: " as *u8) 130 o = ecat(out, o, tok) 131 o = ecat(out, o, "\r\nConnection: close\r\n\r\n" as *u8) 132 return o 133} 134// extract token value from a JSON body {"token":"<...>"} into out (NUL-term). Returns length (0 = none). 135func extract_token(buf: *u8, n: i64, out: *u8) -> i64 { 136 let pat: *u8 = "\"token\":\"" as *u8; let pl: i64 = 9 137 var i: i64 = 0; var start: i64 = 0-1 138 while i+pl<=n { var j: i64=0; var ok: i64=1; while j<pl { if buf[i+j]!=pat[j] { ok=0; j=pl } else { j=j+1 } } if ok==1 { start=i+pl; i=n } else { i=i+1 } } 139 if start<0 { return 0 } 140 var k: i64=0; var p: i64=start 141 while p<n { if buf[p]==(34 as u8) { break } out[k]=buf[p]; k=k+1; p=p+1 } 142 out[k]=0 as u8; return k 143} 144 145func main() -> i64 { 146 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304) 147 if r <= 0 { ep("certdata load failed (run from nxc2 dir)\n" as *u8); sys_exit(1); return 1 } 148 let store: *TrustStore = r as *TrustStore 149 let req: *u8 = sys_mmap(K_MAGIC_8192) 150 let out: *u8 = sys_mmap(K_MAGIC_262144) 151 152 ep("==================== LIVE AUTH E2E (sovereign TLS-1.3 POST client) ====================\n" as *u8) 153 let creds: *u8 = "handle=zz_e2e_keytest&pw=KeyTest_zz_9931x" as *u8 154 155 // 1) REGISTER (throwaway). Idempotent: 'already' on re-run is fine -- login still works. 156 var rl: i64 = build_post("/register" as *u8, creds, req) 157 var n: i64 = e2e_req(store, req, rl, out, K_MAGIC_262144) 158 ep("[register] bytes="); epn(n); ep(" ok="); epn(econtains(out, n, "mnemonic" as *u8)); ep(" already="); epn(econtains(out, n, "already" as *u8)); ep("\n" as *u8) 159 160 // 2) LOGIN -> token 161 rl = build_post("/login" as *u8, creds, req) 162 n = e2e_req(store, req, rl, out, K_MAGIC_262144) 163 let tok: *u8 = sys_mmap(K_MAGIC_1024) 164 let tn: i64 = extract_token(out, n, tok) 165 ep("[login] bytes="); epn(n); ep(" got-token="); epn(tn); ep("\n" as *u8) 166 if tn <= 0 { ep("LOGIN FAILED (no token) -- body:\n" as *u8); var dn: i64=n; if dn>400 {dn=400} sys_write(1,out,dn); ep("\nVERDICT: RED\n" as *u8); sys_exit(1); return 1 } 167 168 // 3) VALIDATE the :9091-minted token at the :18791-gated wiki 169 rl = build_get_auth("/wiki/creation.html" as *u8, tok, req) 170 n = e2e_req(store, req, rl, out, K_MAGIC_262144) 171 var is_tree: i64 = 0; if econtains(out, n, "family tree" as *u8) == 1 { if econtains(out, n, "Creation" as *u8) == 1 { is_tree = 1 } } 172 var is_login: i64 = 0; if econtains(out, n, "OPAQUE login" as *u8) == 1 { is_login = 1 } 173 var is_boot: i64 = 0; if econtains(out, n, "X-Nishi-Session" as *u8) == 1 { if econtains(out, n, "/login?return=" as *u8) == 1 { is_boot = 1 } } 174 ep("[wiki w/ token] bytes="); epn(n); ep(" is-tree="); epn(is_tree); ep(" is-login-reject="); epn(is_login); ep(" is-bootstrap="); epn(is_boot); ep("\n" as *u8) 175 176 if is_tree == 1 { 177 ep("VERDICT: GREEN -- a :9091-minted token VALIDATED at the :18791 wiki gateway and returned the GATED Creation tree. The key+realm fix is PROVEN end-to-end (login -> gated content).\n" as *u8) 178 sys_exit(0); return 0 179 } 180 ep("VERDICT: RED -- token did NOT yield gated content (see flags above). body head:\n" as *u8) 181 var dn2: i64=n; if dn2>500 {dn2=500} sys_write(1,out,dn2); ep("\n" as *u8) 182 sys_exit(1); return 1 183}