nx_tls13_recv_hs_probe_test.nx source
↩ module page · 272 lines · 13933 B
1// nx_tls13_recv_hs_probe_test.nx -- B1 instrument (pm_plan arc=BROWSER
2// step=B1-RECVHS-EXAMPLE): example.com handshakes to recv_sh GREEN (sh-probe
3// RV=1) but session_run dies SR=-7 (RUN_RECV_HS_FAIL) in the ENCRYPTED
4// handshake -- recv_hs collapses every dispatcher failure to DISPATCH_FAIL,
5// hiding the locus. This probe replicates recv_hs's walk per record with
6// step markers (the KD= cascade pattern that localized the tail-call
7// miscompile), composing the shipped primitives UNCHANGED:
8//
9// RC=/CT=/TL= record number, outer ContentType, total bytes
10// DV=/IC=/PL= record-decrypt verdict, inner ContentType, plaintext len
11// MT=/ML= each handshake message type + body length
12// PV= at WAIT_CERT: the cert pipeline's EXACT verdict
13// (2..12 = msg-bad/parse/validity/SAN/DN/SIG/anchor)
14// CN=/XV=/SA=/SS=/PA= per served cert: index, x509_parse rc, sig-alg id,
15// sig-alg SUPPORTED flag, pubkey-alg id (the P-384/RSA-PSS
16// suspicion becomes a measured fact here)
17// DW=/ST= dispatcher verdict, new session state
18//
19// Exit 0 iff the handshake reaches WAIT_CLIENT_FIN (server Finished
20// verified) -- the bar for landing this module as the B1 gate row.
21// Until the fix, the nonzero exit + markers ARE the Doctor's input.
22// Prereq: /tmp/mozilla_certdata.txt staged (browser gate self-stages it).
23//
24// expect_exit: 0 (once B1 is healed; today it names the defect)
25// license_tier: ORIGINAL
26
27import "nx_syscalls.nx"
28import "nx_https_url_for_fetch.nx"
29import "nx_https_url_connect.nx"
30import "nx_x509_trust_store.nx"
31import "nx_trust_store_load_from_certdata.nx"
32import "nx_https_cert_pipeline.nx"
33import "nx_x509.nx"
34import "nx_x509_sig_alg.nx"
35import "nx_x509_pubkey_alg.nx"
36import "nx_tls13_client_session_recv_sh.nx"
37import "nx_tls13_client_session_recv_hs.nx"
38import "nx_tls13_read_record_from_fd.nx"
39
40func rp_dec(label0: i64, label1: i64, v: i64) -> i64 {
41 let lab: *u8 = sys_mmap(8)
42 lab[0] = label0 as u8; lab[1] = label1 as u8; lab[2] = 0x3D
43 sys_write(1, lab, 3)
44 var av: i64 = v
45 if av < 0 {
46 let neg: *u8 = sys_mmap(8); neg[0] = 0x2D; sys_write(1, neg, 1)
47 av = 0 - av
48 }
49 if av == 0 {
50 let z: *u8 = sys_mmap(8); z[0] = 0x30; sys_write(1, z, 1)
51 }
52 if av > 0 {
53 let buf: *u8 = sys_mmap(32)
54 var pos: i64 = 0
55 var x: i64 = av
56 while x > 0 { buf[pos] = (0x30 + (x % 10)) as u8; x = x / 10; pos = pos + 1 }
57 let out: *u8 = sys_mmap(32)
58 var oi: i64 = 0
59 while oi < pos { out[oi] = buf[pos - 1 - oi]; oi = oi + 1 }
60 sys_write(1, out, pos)
61 }
62 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A; sys_write(1, nl, 1)
63 return 0
64}
65
66// Census of the served Certificate message: per cert print index,
67// parse rc, sig-alg id (+supported), pubkey-alg id. Read-only --
68// never touches session/transcript state.
69func rp_chain_census(msg: *u8, msg_len: i64) -> i64 {
70 if msg_len < 8 { return 0 - 1 }
71 let ctx_len: i64 = msg[4] & 0xff
72 var p: i64 = 5 + ctx_len
73 if p + 3 > msg_len { return 0 - 2 }
74 let list_len: i64 = ((msg[p] & 0xff) << 16) | ((msg[p + 1] & 0xff) << 8) | (msg[p + 2] & 0xff)
75 p = p + 3
76 var endp: i64 = p + list_len
77 if endp > msg_len { endp = msg_len }
78 var idx: i64 = 0
79 while p + 3 <= endp {
80 let clen: i64 = ((msg[p] & 0xff) << 16) | ((msg[p + 1] & 0xff) << 8) | (msg[p + 2] & 0xff)
81 if p + 3 + clen > endp { return 0 - 3 }
82 let der: *u8 = msg + p + 3
83 rp_dec(0x43, 0x4E, idx) // CN=
84 let cert_raw: *u8 = sys_mmap(512)
85 let cert: *X509Cert = cert_raw as *X509Cert
86 let xv: i64 = x509_parse(der, clen, cert)
87 rp_dec(0x58, 0x56, xv) // XV=
88 if xv == 0 {
89 let sa: i64 = nx_x509_sig_alg_identify(der, cert.sig_alg_off, cert.sig_alg_len)
90 rp_dec(0x53, 0x41, sa) // SA=
91 rp_dec(0x53, 0x53, nx_x509_sig_alg_is_supported(sa)) // SS=
92 let pa: i64 = nx_x509_pubkey_alg_identify(der, cert.pubkey_alg_off, cert.pubkey_alg_len)
93 rp_dec(0x50, 0x41, pa) // PA=
94 }
95 p = p + 3 + clen
96 if p + 2 > endp { return idx + 1 }
97 let ext_len: i64 = ((msg[p] & 0xff) << 8) | (msg[p + 1] & 0xff)
98 p = p + 2 + ext_len
99 idx = idx + 1
100 }
101 return idx
102}
103
104func main(argc: i64, argv: *i64) -> i64 {
105 var url: *u8 = "https://example.com/\x00"
106 if argc >= 2 { url = argv[1] as *u8 }
107 let epoch: i64 = sys_now_realtime_sec() // REAL clock (B1 root cause was a rotted hardcoded epoch)
108
109 // ---- trust store (real Mozilla bundle, gate-staged) ----
110 let r: i64 = nx_trust_store_load_from_certdata("/tmp/mozilla_certdata.txt\x00" as *u8, 300, 4194304)
111 if r <= 0 { rp_dec(0x4C, 0x4F, r); return 11 } // LO=
112 let store: *TrustStore = r as *TrustStore
113 rp_dec(0x43, 0x41, trust_store_count(store)) // CA=
114
115 // ---- connect ----
116 let url_p: *NxUrl = nx_url_new()
117 let target_raw: *u8 = sys_mmap(32)
118 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget
119 target.url = url_p
120 target.port = 0
121 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { return 12 }
122 let host_len: i64 = target.url.host_len
123 let fd_p: *i64 = sys_mmap(16) as *i64
124 let ucv: i64 = nx_https_url_connect(target, url, 1781100001, fd_p)
125 rp_dec(0x55, 0x43, ucv) // UC=
126 if ucv != NX_HTTPS_CONNECT_OK { return 13 }
127 let fd: i64 = *fd_p
128 sys_set_socket_timeout(fd, 10)
129 let host: *u8 = url + target.url.host_off
130
131 // ---- validation context ----
132 let val_ctx_raw: *u8 = sys_mmap(64)
133 let val_ctx: *TlsValidationContext = val_ctx_raw as *TlsValidationContext
134 val_ctx.store = store
135 val_ctx.sni_host = host
136 val_ctx.sni_host_len = host_len
137 val_ctx.now_epoch = epoch
138
139 // ---- session + ClientHello ----
140 let cr: *u8 = sys_mmap(32)
141 let priv: *u8 = sys_mmap(32)
142 var i: i64 = 0
143 while i < 32 { cr[i] = (0xC0 + i) as u8; priv[i] = (0xA0 + i) as u8; i = i + 1 }
144 let s: *Tls13ClientSession = nx_tls13_client_session_new(cr, priv)
145 let ch_buf: *u8 = sys_mmap(1024)
146 let ch_n: i64 = nx_tls13_client_session_emit_ch(s, host, host_len, ch_buf, 1024)
147 rp_dec(0x43, 0x48, ch_n) // CH=
148 if ch_n < 0 { sys_close(fd); return 14 }
149
150 let rec: *u8 = sys_mmap(1024 + NX_TLS13_RECORD_HEADER_LEN)
151 rec[0] = (NX_TLS13_CT_HANDSHAKE & 0xff) as u8
152 rec[1] = 0x03 as u8; rec[2] = 0x01 as u8
153 rec[3] = ((ch_n >> 8) & 0xff) as u8
154 rec[4] = (ch_n & 0xff) as u8
155 var ci: i64 = 0
156 while ci < ch_n { rec[NX_TLS13_RECORD_HEADER_LEN + ci] = ch_buf[ci]; ci = ci + 1 }
157 var sent: i64 = 0
158 let want: i64 = NX_TLS13_RECORD_HEADER_LEN + ch_n
159 while sent < want {
160 let w: i64 = sys_write(fd, rec + sent, want - sent)
161 if w <= 0 { sys_close(fd); rp_dec(0x57, 0x52, w); return 15 } // WR=
162 sent = sent + w
163 }
164
165 // ---- ServerHello ----
166 let sh_record: *u8 = sys_mmap(16700)
167 let sh_total: i64 = nx_tls13_read_record_from_fd(fd, sh_record, 16700)
168 if sh_total < 0 { sys_close(fd); rp_dec(0x52, 0x44, sh_total); return 16 } // RD=
169 let sh_body: *u8 = sh_record + NX_TLS13_RECORD_HEADER_LEN
170 let sh_body_len: i64 = sh_total - NX_TLS13_RECORD_HEADER_LEN
171 rp_dec(0x43, 0x54, sh_record[0] & 0xff) // CT= outer record ContentType (22=handshake, 21=ALERT)
172 rp_dec(0x42, 0x35, sh_record[5] & 0xff) // B5= first body byte (handshake type 2=SH; or alert level)
173 rp_dec(0x42, 0x36, sh_record[6] & 0xff) // B6= second body byte (SH legacy_version hi; or alert DESCRIPTION)
174 rp_dec(0x54, 0x4c, sh_total) // TL= total record bytes
175 let rv: i64 = nx_tls13_client_session_recv_sh(s, sh_body, sh_body_len)
176 rp_dec(0x52, 0x56, rv) // RV=
177 if rv != NX_TLS13_RECV_SH_OK { sys_close(fd); return 17 }
178
179 // ---- instrumented encrypted-handshake walk (recv_hs mirrored) ----
180 var recno: i64 = 0
181 var fail: i64 = 0
182 var running: i64 = 1
183 while running == 1 {
184 if s.state == NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN { running = 0 }
185 if recno >= 16 { running = 0; if fail == 0 { fail = 90 } }
186 if running == 1 {
187 let hs_record: *u8 = sys_mmap(17000)
188 let hs_total: i64 = nx_tls13_read_record_from_fd(fd, hs_record, 17000)
189 rp_dec(0x52, 0x43, recno) // RC=
190 rp_dec(0x54, 0x4C, hs_total) // TL=
191 if hs_total < 0 { fail = 20; running = 0 }
192 if running == 1 {
193 let oct: i64 = hs_record[0] & 0xff
194 rp_dec(0x43, 0x54, oct) // CT=
195 var skip: i64 = 0
196 if oct == 20 { skip = 1 }
197 if skip == 0 {
198 let header: *u8 = hs_record
199 let ctxt: *u8 = hs_record + NX_TLS13_RECORD_HEADER_LEN
200 let ct_len: i64 = hs_total - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN
201 let tag: *u8 = hs_record + hs_total - NX_TLS13_RECORD_TAG_LEN
202 let inner: *u8 = sys_mmap(ct_len + 16)
203 let real_ct_p: *i64 = sys_mmap(16) as *i64
204 let real_len_p: *i64 = sys_mmap(16) as *i64
205 let dv: i64 = nx_tls13_record_decrypt_v2(
206 s.cipher_suite, s.server_hs_traffic_key, s.server_hs_iv,
207 s.server_seq, header, ctxt, ct_len, tag,
208 inner, real_ct_p, real_len_p)
209 s.server_seq = s.server_seq + 1
210 rp_dec(0x44, 0x56, dv) // DV=
211 if dv != NX_TLS13_REC_VERDICT_OK { fail = 30; running = 0 }
212 if running == 1 {
213 rp_dec(0x49, 0x43, *real_ct_p) // IC=
214 if *real_ct_p == 21 {
215 if *real_len_p >= 2 { rp_dec(0x41, 0x44, inner[1] & 0xff) } // AD=
216 fail = 31; running = 0
217 }
218 if running == 1 { if *real_ct_p != NX_TLS13_CT_HANDSHAKE { fail = 32; running = 0 } }
219 }
220 if running == 1 {
221 let plen: i64 = *real_len_p
222 rp_dec(0x50, 0x4C, plen) // PL=
223 var off: i64 = 0
224 let new_state_p: *i64 = sys_mmap(16) as *i64
225 while off < plen {
226 if off + 4 > plen { fail = 33; off = plen }
227 if fail == 0 {
228 let mt: i64 = inner[off] & 0xff
229 let body_len: i64 = ((inner[off + 1] & 0xff) << 16) | ((inner[off + 2] & 0xff) << 8) | (inner[off + 3] & 0xff)
230 let msg_len: i64 = 4 + body_len
231 rp_dec(0x4D, 0x54, mt) // MT=
232 rp_dec(0x4D, 0x4C, body_len) // ML=
233 if off + msg_len > plen { fail = 34; off = plen }
234 if fail == 0 {
235 let cstate: i64 = s.state - NX_TLS13_CSESSION_STATE_WAIT_EE + NX_TLS13_CSTATE_WAIT_EE
236 if mt == 11 {
237 let pv: i64 = nx_https_cert_pipeline_verify_with_store(
238 inner + off, msg_len, host, host_len, epoch, store)
239 rp_dec(0x50, 0x56, pv) // PV=
240 rp_chain_census(inner + off, msg_len)
241 }
242 let dw: i64 = tls13_client_dispatch_with_validation(
243 cstate, inner + off, msg_len,
244 s.server_hs_traffic_secret, s.transcript,
245 new_state_p, val_ctx)
246 rp_dec(0x44, 0x57, dw) // DW=
247 if dw != NX_TLS13_DWV_OK { fail = 40 + dw; off = plen }
248 if fail == 0 {
249 if *new_state_p == NX_TLS13_CSTATE_WAIT_EE { s.state = NX_TLS13_CSESSION_STATE_WAIT_EE }
250 if *new_state_p == NX_TLS13_CSTATE_WAIT_CERT { s.state = NX_TLS13_CSESSION_STATE_WAIT_CERT }
251 if *new_state_p == NX_TLS13_CSTATE_WAIT_CV { s.state = NX_TLS13_CSESSION_STATE_WAIT_CV }
252 if *new_state_p == NX_TLS13_CSTATE_WAIT_SF { s.state = NX_TLS13_CSESSION_STATE_WAIT_SF }
253 if *new_state_p == NX_TLS13_CSTATE_CONNECTED { s.state = NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN }
254 rp_dec(0x53, 0x54, s.state) // ST=
255 off = off + msg_len
256 }
257 }
258 }
259 }
260 if fail != 0 { running = 0 }
261 }
262 }
263 }
264 recno = recno + 1
265 }
266 }
267 sys_close(fd)
268 rp_dec(0x46, 0x58, fail) // FX=
269 if fail == 0 { if s.state == NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN { return 0 } }
270 if fail == 0 { return 91 }
271 return fail
272}