nx_tls13_sh_probe_test.nx source
↩ module page · 252 lines · 10746 B
1// nx_tls13_sh_probe_test.nx -- ServerHello DIAGNOSTIC PROBE.
2//
3// The browser's arbitrary-host gap (RUN verdict -5 = RECV_SH_FAIL)
4// swallows the INNER recv_sh cause. This probe surfaces it, per
5// host, from a data-driven host table:
6//
7// HOST <name>
8// CT= first record content type (22 handshake / 21 alert)
9// AD= alert description byte (only when CT=21)
10// PV= tls13_server_hello_parse verdict (only when != OK)
11// LV= legacy_version (771 = 0x0303)
12// CS= chosen cipher (4865=AES128GCM 4866=AES256GCM 4867=CHACHA)
13// SV= supported_versions value (772 = 0x0304 TLS1.3; 0 = absent
14// = server negotiated TLS 1.2 path)
15// GR= key_share group (29 x25519 / 23 secp256r1 / -1 absent)
16// HR= 1 if SH.random == the RFC 8446 HelloRetryRequest sentinel
17// RV= inner nx_tls13_client_session_recv_sh verdict (1 = OK)
18//
19// Exit 0 always -- this is a DIAGNOSIS instrument, not a gate; the
20// browser gate consumes its findings. Bounded reads, 5s socket
21// timeout (KNOWN_LESSONS bounded-parsing discipline).
22//
23// expect_exit: 0
24// license_tier: ORIGINAL
25
26import "nx_syscalls.nx"
27import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host
28import "nx_dns_resolve_a_record.nx"
29import "nx_https_url_connect.nx"
30import "nx_tls13.nx"
31import "nx_tls13_record.nx"
32import "nx_tls13_hello.nx"
33import "nx_tls13_client_session.nx"
34import "nx_tls13_client_session_recv_sh.nx"
35import "nx_tls13_read_record_from_fd.nx"
36
37// Print 2-char label '=' signed-decimal '\n' to stdout.
38func probe_dec(label0: i64, label1: i64, v: i64) -> i64 {
39 let lab: *u8 = sys_mmap(8)
40 lab[0] = label0 as u8; lab[1] = label1 as u8; lab[2] = 0x3D
41 sys_write(1, lab, 3)
42 var av: i64 = v
43 if av < 0 {
44 let neg: *u8 = sys_mmap(8); neg[0] = 0x2D; sys_write(1, neg, 1)
45 av = 0 - av
46 }
47 if av == 0 {
48 let z: *u8 = sys_mmap(8); z[0] = 0x30; sys_write(1, z, 1)
49 }
50 if av > 0 {
51 let buf: *u8 = sys_mmap(32)
52 var pos: i64 = 0
53 var x: i64 = av
54 while x > 0 {
55 buf[pos] = (0x30 + (x % 10)) as u8
56 x = x / 10
57 pos = pos + 1
58 }
59 let out: *u8 = sys_mmap(32)
60 var oi: i64 = 0
61 while oi < pos {
62 out[oi] = buf[pos - 1 - oi]
63 oi = oi + 1
64 }
65 sys_write(1, out, pos)
66 }
67 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A; sys_write(1, nl, 1)
68 return 0
69}
70
71// RFC 8446 section 4.1.3 HelloRetryRequest sentinel Random.
72func probe_hrr_sentinel(out32: *u8) -> i64 {
73 out32[0]=0xCF as u8; out32[1]=0x21 as u8; out32[2]=0xAD as u8; out32[3]=0x74 as u8
74 out32[4]=0xE5 as u8; out32[5]=0x9A as u8; out32[6]=0x61 as u8; out32[7]=0x11 as u8
75 out32[8]=0xBE as u8; out32[9]=0x1D as u8; out32[10]=0x8C as u8; out32[11]=0x02 as u8
76 out32[12]=0x1E as u8; out32[13]=0x65 as u8; out32[14]=0xB8 as u8; out32[15]=0x91 as u8
77 out32[16]=0xC2 as u8; out32[17]=0xA2 as u8; out32[18]=0x11 as u8; out32[19]=0x16 as u8
78 out32[20]=0x7A as u8; out32[21]=0xBB as u8; out32[22]=0x8C as u8; out32[23]=0x5E as u8
79 out32[24]=0x07 as u8; out32[25]=0x9E as u8; out32[26]=0x09 as u8; out32[27]=0xE2 as u8
80 out32[28]=0xC8 as u8; out32[29]=0xA8 as u8; out32[30]=0x33 as u8; out32[31]=0x9C as u8
81 return 0
82}
83
84// Probe one host: full diagnosis line set. Never aborts the run.
85func probe_host(host: *u8, host_len: i64, seed: i64) -> i64 {
86 let hdr: *u8 = sys_mmap(8)
87 hdr[0] = 0x48; hdr[1] = 0x4F; hdr[2] = 0x53; hdr[3] = 0x54; hdr[4] = 0x20 // "HOST "
88 sys_write(1, hdr, 5)
89 sys_write(1, host, host_len)
90 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A; sys_write(1, nl, 1)
91
92 // ---- DNS ----
93 let dns: *DnsResolveResult = nx_dns_resolve_default(host, host_len, seed)
94 if dns.verdict != NX_DNS_R_OK { probe_dec(0x44, 0x4E, dns.verdict); return 1 } // DN=
95 if dns.ipv4_packed == 0 { probe_dec(0x44, 0x4E, 0 - 99); return 1 }
96
97 // ---- TCP 443 ----
98 let fd: i64 = sys_socket(NX_HTTPS_AF_INET, NX_HTTPS_SOCK_STREAM, 0)
99 if fd < 0 { probe_dec(0x54, 0x43, fd); return 1 } // TC=
100 sys_set_socket_timeout(fd, 5)
101 let sa: *u8 = sys_mmap(16)
102 nx_https_build_sockaddr(sa, dns.ipv4_packed, 443)
103 if nx_connect_bounded(fd, sa, 16, NX_CONN_DEFAULT_MS) < 0 { sys_close(fd); probe_dec(0x54, 0x43, 0 - 1); return 1 }
104
105 // ---- Session + ClientHello ----
106 let cr: *u8 = sys_mmap(32)
107 let pk: *u8 = sys_mmap(32)
108 var i: i64 = 0
109 while i < 32 {
110 cr[i] = (((seed * 31) + (i * 17)) & 0xff) as u8
111 pk[i] = (((seed * 131) + (i * 29) + 7) & 0xff) as u8
112 i = i + 1
113 }
114 let s: *Tls13ClientSession = nx_tls13_client_session_new(cr, pk)
115 let ch_buf: *u8 = sys_mmap(1024)
116 let ch_n: i64 = nx_tls13_client_session_emit_ch(s, host, host_len, ch_buf, 1024)
117 if ch_n < 0 { sys_close(fd); probe_dec(0x43, 0x48, ch_n); return 1 } // CH=
118
119 let rec: *u8 = sys_mmap(1024 + NX_TLS13_RECORD_HEADER_LEN)
120 rec[0] = (NX_TLS13_CT_HANDSHAKE & 0xff) as u8
121 rec[1] = 0x03 as u8; rec[2] = 0x01 as u8
122 rec[3] = ((ch_n >> 8) & 0xff) as u8
123 rec[4] = (ch_n & 0xff) as u8
124 var ci: i64 = 0
125 while ci < ch_n {
126 rec[NX_TLS13_RECORD_HEADER_LEN + ci] = ch_buf[ci]
127 ci = ci + 1
128 }
129 var sent: i64 = 0
130 let want: i64 = NX_TLS13_RECORD_HEADER_LEN + ch_n
131 while sent < want {
132 let w: i64 = sys_write(fd, rec + sent, want - sent)
133 if w <= 0 { sys_close(fd); probe_dec(0x57, 0x52, w); return 1 } // WR=
134 sent = sent + w
135 }
136
137 // ---- First server record ----
138 let sh_record: *u8 = sys_mmap(16700)
139 let sh_total: i64 = nx_tls13_read_record_from_fd(fd, sh_record, 16700)
140 sys_close(fd)
141 if sh_total < 0 { probe_dec(0x52, 0x44, sh_total); return 1 } // RD=
142
143 let ct: i64 = sh_record[0] & 0xff
144 probe_dec(0x43, 0x54, ct) // CT=
145 let body: *u8 = sh_record + NX_TLS13_RECORD_HEADER_LEN
146 let body_len: i64 = sh_total - NX_TLS13_RECORD_HEADER_LEN
147 if ct == 21 {
148 if body_len >= 2 { probe_dec(0x41, 0x44, body[1] & 0xff) } // AD=
149 return 1
150 }
151 if ct != 22 { return 1 }
152
153 // ---- Parse SH ----
154 let p_lv: *i64 = sys_mmap(16) as *i64
155 let p_ro: *i64 = sys_mmap(16) as *i64
156 let p_cs: *i64 = sys_mmap(16) as *i64
157 let p_eo: *i64 = sys_mmap(16) as *i64
158 let p_el: *i64 = sys_mmap(16) as *i64
159 let pv: i64 = tls13_server_hello_parse(body, body_len, p_lv, p_ro, p_cs, p_eo, p_el)
160 if pv != NX_TLS13_HELLO_VERDICT_OK { probe_dec(0x50, 0x56, pv); return 1 } // PV=
161 probe_dec(0x4C, 0x56, *p_lv) // LV=
162 probe_dec(0x43, 0x53, *p_cs) // CS=
163
164 // ---- supported_versions (absent => TLS 1.2 negotiation) ----
165 let p_off: *i64 = sys_mmap(16) as *i64
166 let p_len: *i64 = sys_mmap(16) as *i64
167 var sv: i64 = 0
168 let sv_v: i64 = tls13_ext_find(body + *p_eo, *p_el, EXT_SUPPORTED_VERSIONS, p_off, p_len)
169 if sv_v == NX_TLS13_HELLO_VERDICT_OK {
170 if *p_len >= 2 {
171 let svb: *u8 = body + *p_eo + *p_off
172 sv = ((svb[0] & 0xff) << 8) | (svb[1] & 0xff)
173 }
174 }
175 probe_dec(0x53, 0x56, sv) // SV=
176
177 // ---- key_share group ----
178 var grp: i64 = 0 - 1
179 let ks_v: i64 = tls13_ext_find(body + *p_eo, *p_el, EXT_KEY_SHARE, p_off, p_len)
180 if ks_v == NX_TLS13_HELLO_VERDICT_OK {
181 if *p_len >= 2 {
182 let ksb: *u8 = body + *p_eo + *p_off
183 grp = ((ksb[0] & 0xff) << 8) | (ksb[1] & 0xff)
184 }
185 }
186 probe_dec(0x47, 0x52, grp) // GR=
187
188 // ---- HRR sentinel ----
189 let sentinel: *u8 = sys_mmap(32)
190 probe_hrr_sentinel(sentinel)
191 var hrr: i64 = 1
192 var hi: i64 = 0
193 while hi < 32 {
194 if (body[*p_ro + hi] & 0xff) != (sentinel[hi] & 0xff) { hrr = 0 }
195 hi = hi + 1
196 }
197 probe_dec(0x48, 0x52, hrr) // HR=
198
199 // ---- Step-localize the recv_sh cascade (crash diagnosis) ----
200 // Replicates recv_sh's internals with KD= markers so a fault
201 // names its step instead of dying silently.
202 if grp == 29 {
203 let ksb2: *u8 = body + *p_eo + *p_off
204 let spub: *u8 = sys_mmap(32)
205 var si: i64 = 0
206 while si < 32 { spub[si] = ksb2[4 + si]; si = si + 1 }
207 let shared: *u8 = sys_mmap(32)
208 let xv: i64 = x25519(s.x25519_priv, spub, shared)
209 probe_dec(0x58, 0x56, xv) // XV=
210 let zeros: *u8 = sys_mmap(64)
211 let early: *u8 = sys_mmap(32)
212 tls13_early_secret(zeros, TLS13_HASH_LEN_SHA256, early)
213 probe_dec(0x4B, 0x44, 1) // KD=1
214 let empty_hash: *u8 = sys_mmap(32)
215 sha256_digest(zeros, 0, empty_hash)
216 probe_dec(0x4B, 0x44, 2)
217 let derived_1: *u8 = sys_mmap(32)
218 tls13_derived(early, empty_hash, TLS13_HASH_LEN_SHA256, derived_1)
219 probe_dec(0x4B, 0x44, 3)
220 tls13_handshake_secret(derived_1, TLS13_HASH_LEN_SHA256, shared, 32, s.handshake_secret)
221 probe_dec(0x4B, 0x44, 4)
222 nx_tls13_transcript_update(s.transcript, body, body_len)
223 let h1: *u8 = sys_mmap(32)
224 nx_tls13_transcript_snapshot(s.transcript, h1)
225 probe_dec(0x4B, 0x44, 5)
226 tls13_traffic_secret(s.handshake_secret, NX_TLS13_LABEL_C_HS_TRAFFIC,
227 h1, TLS13_HASH_LEN_SHA256, s.client_hs_traffic_secret)
228 tls13_traffic_secret(s.handshake_secret, NX_TLS13_LABEL_S_HS_TRAFFIC,
229 h1, TLS13_HASH_LEN_SHA256, s.server_hs_traffic_secret)
230 probe_dec(0x4B, 0x44, 6)
231 tls13_traffic_key(s.client_hs_traffic_secret, 16, s.client_hs_traffic_key)
232 tls13_traffic_iv(s.client_hs_traffic_secret, 12, s.client_hs_iv)
233 probe_dec(0x4B, 0x44, 7)
234 }
235
236 // ---- Inner recv_sh verdict ----
237 let rv: i64 = nx_tls13_client_session_recv_sh(s, body, body_len)
238 probe_dec(0x52, 0x56, rv) // RV=
239 return 0
240}
241
242func main() -> i64 {
243 probe_host("example.com\x00", 11, 1779290001)
244 probe_host("httpbin.org\x00", 11, 1779290002)
245 probe_host("en.wikipedia.org\x00", 16, 1779290003)
246 probe_host("github.com\x00", 10, 1779290004)
247 probe_host("www.google.com\x00", 14, 1779290005)
248 probe_host("nishifamily.com\x00", 15, 1779290006)
249 let done: *u8 = "SH_PROBE_DONE\n\x00"
250 sys_write(1, done, 14)
251 return 0
252}