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}