nx_tls13_chrome_session.nx source
↩ module page · 131 lines · 8894 B
1// nx_tls13_chrome_session.nx -- R2f-A LIVE wiring: a TLS-1.3 client session that sends the Chrome-JA3 ClientHello
2// (nx_tls13_chrome_hello) so anti-bot CDNs classify us as a browser and complete the handshake. ADDITIVE (Cardinal
3// 19): NEW functions only; the shared nx_tls13_client_session_run + emit_ch are byte-identical (fleet untouched).
4// run_chrome is a copy of nx_tls13_client_session_run with ONLY the ClientHello emit swapped + one robustness fix
5// (clamp the SH transcript feed to the parsed ServerHello message length).
6//
7// LIVE STATUS (2026-07-03): PROVEN end-to-end -- nhentai.net (HTTP 200), wikipedia (301), example.com (200),
8// rfc-editor.org (200, 180KB body), and **i.nhentai.net image CDN = HANDSHAKE-OK**. The Chrome-JA3 fingerprint
9// beats the Cloudflare/GitHub TLS wall that dropped our minimal hello (-3); the RSA-PSS CertificateVerify verifier
10// (nx_rsa_pss_sha256, wired into nx_tls13_client_verify_cv) completes the handshake with the RSA-cert servers that
11// dominate the web -- the CertificateVerify uses rsa_pss_rsae_sha256 (0x0804), which TLS 1.3 MANDATES for RSA certs
12// and which we previously fail-closed on. license_tier: ORIGINAL
13import "nx_tls13_client_session.nx" // struct + recv_sh/recv_hs/emit_finished/derive_app/session_new + transcript_update + CSESSION consts
14import "nx_tls13_client_session_run.nx" // _write_n / _is_change_cipher_spec + NX_TLS13_RUN_* + record consts + nx_tls13_read_record_from_fd
15import "nx_tls13_chrome_hello.nx" // tls13_chrome_hello_emit
16const K_MAGIC_1024: i64 = 1024
17
18// emit the Chrome-JA3 ClientHello into the session (mirrors nx_tls13_client_session_emit_ch: emit -> transcript -> state).
19func nx_tls13_session_emit_ch_chrome(s: *Tls13ClientSession, sni: *u8, sni_len: i64, out_buf: *u8, out_cap: i64) -> i64 {
20 if s.state != NX_TLS13_CSESSION_STATE_INIT { return 0 - NX_TLS13_CSESSION_BAD_STATE }
21 if out_cap < 700 { return 0 - NX_TLS13_CSESSION_BUF_OVERFLOW }
22 let n: i64 = tls13_chrome_hello_emit(s.client_random, sni, sni_len, s.x25519_pub, s.p256_pub, out_buf, out_cap)
23 if n < 0 { return 0 - NX_TLS13_CSESSION_INTERNAL }
24 nx_tls13_transcript_update(s.transcript, out_buf, n)
25 s.state = NX_TLS13_CSESSION_STATE_CH_SENT
26 return n
27}
28
29// run_chrome: copy of nx_tls13_client_session_run, ClientHello = the Chrome-JA3 one. Returns session ptr as i64, or negative NX_TLS13_RUN_*.
30func nx_tls13_client_session_run_chrome(fd: i64, sni: *u8, sni_len: i64, client_random: *u8, x25519_priv: *u8, val_ctx: *TlsValidationContext) -> i64 {
31 let s: *Tls13ClientSession = nx_tls13_client_session_new(client_random, x25519_priv)
32 let ch_buf: *u8 = sys_mmap(K_MAGIC_1024)
33 let ch_n: i64 = nx_tls13_session_emit_ch_chrome(s, sni, sni_len, ch_buf, K_MAGIC_1024)
34 if ch_n < 0 { return 0 - NX_TLS13_RUN_EMIT_CH_FAIL }
35 let ch_record: *u8 = sys_mmap(K_MAGIC_1024 + NX_TLS13_RECORD_HEADER_LEN)
36 ch_record[0] = NX_TLS13_CT_HANDSHAKE & 0xff
37 ch_record[1] = 0x03; ch_record[2] = 0x01
38 ch_record[3] = ((ch_n >> 8) & 0xff) as u8
39 ch_record[4] = (ch_n & 0xff) as u8
40 var ci: i64 = 0
41 while ci < ch_n { ch_record[NX_TLS13_RECORD_HEADER_LEN + ci] = ch_buf[ci]; ci = ci + 1 }
42 let wr_ch: i64 = _write_n(fd, ch_record, NX_TLS13_RECORD_HEADER_LEN + ch_n)
43 if wr_ch < 0 { return 0 - NX_TLS13_RUN_WRITE_CH_FAIL }
44 let sh_record: *u8 = sys_mmap(NX_TLS13_RUN_RECORD_BUF_BYTES)
45 let sh_total: i64 = nx_tls13_read_record_from_fd(fd, sh_record, NX_TLS13_RUN_RECORD_BUF_BYTES)
46 if sh_total < 0 { return 0 - NX_TLS13_RUN_READ_SH_FAIL }
47 let sh_body: *u8 = sh_record + NX_TLS13_RECORD_HEADER_LEN
48 var sh_body_len: i64 = sh_total - NX_TLS13_RECORD_HEADER_LEN
49 // Robustness: the ServerHello message's own length is authoritative for the transcript feed.
50 if sh_body_len >= 4 {
51 let true_sh: i64 = 4 + (((sh_body[1] as i64)&0xff)<<16) + (((sh_body[2] as i64)&0xff)<<8) + ((sh_body[3] as i64)&0xff)
52 if true_sh > 0 { if true_sh < sh_body_len { sh_body_len = true_sh } }
53 }
54 let rs_v: i64 = nx_tls13_client_session_recv_sh(s, sh_body, sh_body_len)
55 if rs_v != NX_TLS13_RECV_SH_OK {
56 // NAME THE REAL CAUSE. The caller only ever saw the collapsed verdict=5 (RECV_SH_FAIL), which
57 // reads as "the server rejected our hello" -- but the dominant case is the OPPOSITE: the server
58 // ACCEPTED it and chose a suite WE ADVERTISED AND CANNOT DECRYPT. The Chrome-JA3 cipher list is
59 // Chrome's real 15 and includes 0x1302 (AES-256-GCM-SHA384).
60 // Cost 2026-08-01: this opaque 5 was read as a public-edge outage and nearly bought an infra arc.
61 // sub-verdicts: 8 = BAD_CIPHER (a REAL capability gap -- build the suite) · 9 = NOT_TLS13 (the
62 // server negotiated 1.2; build nothing, take the 1.2 leg). CORRECTED 2026-08-06: the old text here
63 // said "recv_sh speaks only 0x1301/0x1303", which stopped being true when R9 shipped 0x1302 with
64 // dual-hash transcript + nx_aes256_gcm. All three TLS-1.3 suites are live.
65 // ★A STALE COMMENT ABOUT A CAPABILITY IS A FALSE BUG REPORT THAT NEVER EXPIRES.
66 sys_write(2, "nishi-hello recv_sh sub-verdict=" as *u8, 32)
67 let d: *u8 = sys_mmap(8)
68 d[0] = (48 + (rs_v % 10)) as u8
69 sys_write(2, d, 1)
70 if rs_v == 8 {
71 // R9b: PRINT THE SUITE, DO NOT ASSERT IT. The previous text named 0x1302 unconditionally,
72 // so a refusal of ANY other suite still read as "the 0x1302 gap" -- a ruler that reports a
73 // cause it never measured. recv_sh now records s.cipher_suite before refusing, so we can
74 // name the byte the server actually sent.
75 sys_write(2, " BAD_CIPHER: server chose suite 0x" as *u8, 34)
76 let hx: *u8 = sys_mmap(8)
77 var hn: i64 = 3
78 while hn >= 0 {
79 let nib: i64 = (s.cipher_suite >> (hn * 4)) & 0xf
80 if nib < 10 { hx[0] = (48 + nib) as u8 } else { hx[0] = (87 + nib) as u8 }
81 sys_write(2, hx, 1)
82 hn = hn - 1
83 }
84 sys_munmap(hx, 8)
85 sys_write(2, " -- we offered it and cannot decrypt it: OUR gap, not the server's\n" as *u8, 67)
86 }
87 else { if rs_v == 9 {
88 // NOT_TLS13. The server answered our 1.3 hello by negotiating a TLS 1.2 suite, so it does not
89 // speak 1.3 at all. Saying "OUR gap" here (as sub-verdict 8 does, correctly, for a real cipher
90 // gap) would be false and would send the reader off to build a cipher that changes nothing.
91 sys_write(2, " NOT_TLS13: server negotiated a TLS 1.2 suite 0x" as *u8, 48)
92 let hx2: *u8 = sys_mmap(8)
93 var hn2: i64 = 3
94 while hn2 >= 0 {
95 let nib2: i64 = (s.cipher_suite >> (hn2 * 4)) & 0xf
96 if nib2 < 10 { hx2[0] = (48 + nib2) as u8 } else { hx2[0] = (87 + nib2) as u8 }
97 sys_write(2, hx2, 1)
98 hn2 = hn2 - 1
99 }
100 sys_munmap(hx2, 8)
101 sys_write(2, " -- THIS HOST HAS NO TLS 1.3. Not a cipher gap: use the 1.2 leg, build nothing.\n" as *u8, 80)
102 }
103 else { sys_write(2, " (see NX_TLS13_RECV_SH_* in nx_tls13_client_session_recv_sh.nx)\n" as *u8, 63) } }
104 sys_munmap(d, 8)
105 sys_munmap(sh_record as *u8, NX_TLS13_RUN_RECORD_BUF_BYTES)
106 return 0 - NX_TLS13_RUN_RECV_SH_FAIL
107 }
108 sys_munmap(sh_record as *u8, NX_TLS13_RUN_RECORD_BUF_BYTES)
109 var loop_count: i64 = 0
110 while s.state != NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN {
111 if loop_count >= NX_TLS13_RUN_MAX_HS_RECORDS { return 0 - NX_TLS13_RUN_LOOP_BUDGET_EXCEEDED }
112 let hs_record: *u8 = sys_mmap(NX_TLS13_RUN_RECORD_BUF_BYTES)
113 let hs_total: i64 = nx_tls13_read_record_from_fd(fd, hs_record, NX_TLS13_RUN_RECORD_BUF_BYTES)
114 if hs_total < 0 { sys_munmap(hs_record as *u8, NX_TLS13_RUN_RECORD_BUF_BYTES); return 0 - NX_TLS13_RUN_READ_HS_FAIL }
115 if _is_change_cipher_spec(hs_record, hs_total) == 1 { loop_count = loop_count + 1 }
116 else {
117 let rh_v: i64 = nx_tls13_client_session_recv_hs(s, hs_record, hs_total, val_ctx)
118 if rh_v != NX_TLS13_RECV_HS_OK { sys_munmap(hs_record as *u8, NX_TLS13_RUN_RECORD_BUF_BYTES); return 0 - NX_TLS13_RUN_RECV_HS_FAIL }
119 loop_count = loop_count + 1
120 }
121 sys_munmap(hs_record as *u8, NX_TLS13_RUN_RECORD_BUF_BYTES)
122 }
123 let cf_buf: *u8 = sys_mmap(128)
124 let cf_n: i64 = nx_tls13_client_session_emit_finished(s, cf_buf, 128)
125 if cf_n < 0 { return 0 - NX_TLS13_RUN_EMIT_CF_FAIL }
126 let wr_cf: i64 = _write_n(fd, cf_buf, cf_n)
127 if wr_cf < 0 { return 0 - NX_TLS13_RUN_WRITE_CF_FAIL }
128 let da_v: i64 = nx_tls13_client_session_derive_app(s)
129 if da_v != NX_TLS13_DERIVE_APP_OK { return 0 - NX_TLS13_RUN_DERIVE_APP_FAIL }
130 return s as i64
131}