code wiki / (root) / nx_tls13_ch_compat_test.nx

nx_tls13_ch_compat_test.nx source

↩ module page · 399 lines · 18930 B

1// nx_tls13_ch_compat_test.nx -- B2-HTTPBIN-CH gate row (browser arc). 2// 3// The B2 defect was "httpbin.org answers our ClientHello with an 4// alert". ROOT CAUSE (oracle-measured 2026-06-10, openssl census): 5// httpbin.org does NOT speak TLS 1.3 at all (-tls1_3 -> no cipher; 6// -tls1_2 -> ECDHE-RSA-AES128-GCM-SHA256, P-256 temp key). Its ELB 7// answers a 1.3-only CH with warning close_notify (AL=1 AD=0). Our 8// CH was never malformed -- the CH-compat suspicion is REFUTED. 9// TLS 1.2 client support = separate named rung (not B2's bar). 10// 11// So this row proves the CH-compat claim with two live legs: 12// 13// LEG A (httpbin census, live-updating): send the PRODUCT dual- 14// share CH to httpbin.org and classify the response: 15// - warning/any close_notify (AD=0) or protocol_version (AD=70) 16// alert, or a TLS 1.2 ServerHello (no supported_versions ext) 17// -> the measured TLS-1.2-only signature -> leg PASS 18// - a TLS 1.3 ServerHello -> httpbin enabled 1.3: the leg now 19// DEMANDS the full handshake to WAIT_CLIENT_FIN 20// - any other alert (decode_error 50, illegal_parameter 47, 21// handshake_failure 40) -> a REAL CH problem -> leg FAIL 22// 23// LEG B (P-256 live proof): secp256r1-ONLY CH to github.com 24// (census: completes TLS 1.3 + P-256 + TLS_AES_128_GCM_SHA256). 25// Server MUST take our P-256 share (GR=23 asserted) and the FULL 26// encrypted handshake walks to WAIT_CLIENT_FIN -- the live E2E 27// proof of rung B4's path against an independent implementation, 28// which dual-share connects can never exercise (servers prefer 29// x25519 when offered). 30// 31// Exit 0 iff BOTH legs pass. Markers: L1=/L2= leg verdicts, 32// AL=/AD= alert level/desc, CS=/GR= server cipher + group pick, 33// RC=/CT=/DV=/MT=/DW=/ST= per-record walk (B1 probe pattern). 34// Prereq: /tmp/mozilla_certdata.txt staged (gate self-stages). 35// 36// expect_exit: 0 37// license_tier: ORIGINAL 38 39import "nx_syscalls.nx" 40import "nx_https_url_for_fetch.nx" 41import "nx_https_url_connect.nx" 42import "nx_x509_trust_store.nx" 43import "nx_trust_store_load_from_certdata.nx" 44import "nx_https_cert_pipeline.nx" 45import "nx_tls13_client_session_recv_sh.nx" 46import "nx_tls13_client_session_recv_hs.nx" 47import "nx_tls13_read_record_from_fd.nx" 48 49func cc_dec(label0: i64, label1: i64, v: i64) -> i64 { 50 let lab: *u8 = sys_mmap(8) 51 lab[0] = label0 as u8; lab[1] = label1 as u8; lab[2] = 0x3D 52 sys_write(1, lab, 3) 53 var av: i64 = v 54 if av < 0 { 55 let neg: *u8 = sys_mmap(8); neg[0] = 0x2D; sys_write(1, neg, 1) 56 av = 0 - av 57 } 58 if av == 0 { 59 let z: *u8 = sys_mmap(8); z[0] = 0x30; sys_write(1, z, 1) 60 } 61 if av > 0 { 62 let buf: *u8 = sys_mmap(32) 63 var pos: i64 = 0 64 var x: i64 = av 65 while x > 0 { buf[pos] = (0x30 + (x % 10)) as u8; x = x / 10; pos = pos + 1 } 66 let out: *u8 = sys_mmap(32) 67 var oi: i64 = 0 68 while oi < pos { out[oi] = buf[pos - 1 - oi]; oi = oi + 1 } 69 sys_write(1, out, pos) 70 } 71 let nl: *u8 = sys_mmap(8); nl[0] = 0x0A; sys_write(1, nl, 1) 72 return 0 73} 74 75// Print CS=/GR= from a ServerHello body; returns chosen group, 76// negative if parse/find failed. Read-only census. 77func cc_sh_group_census(sh_body: *u8, sh_len: i64) -> i64 { 78 let p_lv: *i64 = sys_mmap(16) as *i64 79 let p_ro: *i64 = sys_mmap(16) as *i64 80 let p_cs: *i64 = sys_mmap(16) as *i64 81 let p_eo: *i64 = sys_mmap(16) as *i64 82 let p_el: *i64 = sys_mmap(16) as *i64 83 if tls13_server_hello_parse(sh_body, sh_len, p_lv, p_ro, p_cs, p_eo, p_el) != NX_TLS13_HELLO_VERDICT_OK { 84 return 0 - 1 85 } 86 cc_dec(0x43, 0x53, *p_cs) // CS= 87 let p_off: *i64 = sys_mmap(16) as *i64 88 let p_len: *i64 = sys_mmap(16) as *i64 89 if tls13_ext_find(sh_body + *p_eo, *p_el, EXT_KEY_SHARE, p_off, p_len) != NX_TLS13_HELLO_VERDICT_OK { 90 return 0 - 2 91 } 92 let d: i64 = *p_eo + *p_off 93 if *p_len < 2 { return 0 - 3 } 94 let group: i64 = ((sh_body[d] & 0xff) << 8) | (sh_body[d + 1] & 0xff) 95 cc_dec(0x47, 0x52, group) // GR= 96 return group 97} 98 99// Is this ServerHello a TLS 1.3 negotiation? RFC 8446: a 1.3 SH 100// carries supported_versions selecting 0x0304; a 1.2 server's SH 101// has no such extension. Returns 1 / 0 / negative on parse fail. 102func cc_sh_is_tls13(sh_body: *u8, sh_len: i64) -> i64 { 103 let p_lv: *i64 = sys_mmap(16) as *i64 104 let p_ro: *i64 = sys_mmap(16) as *i64 105 let p_cs: *i64 = sys_mmap(16) as *i64 106 let p_eo: *i64 = sys_mmap(16) as *i64 107 let p_el: *i64 = sys_mmap(16) as *i64 108 if tls13_server_hello_parse(sh_body, sh_len, p_lv, p_ro, p_cs, p_eo, p_el) != NX_TLS13_HELLO_VERDICT_OK { 109 return 0 - 1 110 } 111 let p_off: *i64 = sys_mmap(16) as *i64 112 let p_len: *i64 = sys_mmap(16) as *i64 113 if tls13_ext_find(sh_body + *p_eo, *p_el, EXT_SUPPORTED_VERSIONS, p_off, p_len) != NX_TLS13_HELLO_VERDICT_OK { 114 return 0 115 } 116 let sv: i64 = tls13_ext_parse_supported_versions_server(sh_body + *p_eo + *p_off, *p_len) 117 if sv == TLS_13_VERSION { return 1 } 118 return 0 119} 120 121// Connect to url (host_len chars of host). Returns fd or negative. 122func cc_connect(url: *u8, host_len: i64, out_host: *i64) -> i64 { 123 let url_p: *NxUrl = nx_url_new() 124 let target_raw: *u8 = sys_mmap(32) 125 let target: *NxHttpsTarget = target_raw as *NxHttpsTarget 126 target.url = url_p 127 target.port = 0 128 if nx_https_url_for_fetch(url, target) != NX_HTTPS_URL_OK { return 0 - 90 } 129 let fd_p: *i64 = sys_mmap(16) as *i64 130 let ucv: i64 = nx_https_url_connect(target, url, 1781100001, fd_p) 131 cc_dec(0x55, 0x43, ucv) // UC= 132 if ucv != NX_HTTPS_CONNECT_OK { return 0 - 91 } 133 let fd: i64 = *fd_p 134 sys_set_socket_timeout(fd, 10) 135 // Flattened on purpose: the chained (url + target.url.host_off) 136 // as i64 store shape segfaulted on this lane (expression-shape 137 // miscompile class; same family as the 4-deep nested-if landmine). 138 let u: *NxUrl = target.url 139 let hoff: i64 = u.host_off 140 let hp: *u8 = url + hoff 141 *out_host = hp as i64 142 return fd 143} 144 145// Wrap CH bytes in a plaintext handshake record and send. 146func cc_send_ch(fd: i64, ch_buf: *u8, ch_n: i64) -> i64 { 147 let rec: *u8 = sys_mmap(1024 + NX_TLS13_RECORD_HEADER_LEN) 148 rec[0] = (NX_TLS13_CT_HANDSHAKE & 0xff) as u8 149 rec[1] = 0x03 as u8; rec[2] = 0x01 as u8 150 rec[3] = ((ch_n >> 8) & 0xff) as u8 151 rec[4] = (ch_n & 0xff) as u8 152 var ci: i64 = 0 153 while ci < ch_n { rec[NX_TLS13_RECORD_HEADER_LEN + ci] = ch_buf[ci]; ci = ci + 1 } 154 var sent: i64 = 0 155 let want: i64 = NX_TLS13_RECORD_HEADER_LEN + ch_n 156 while sent < want { 157 let w: i64 = sys_write(fd, rec + sent, want - sent) 158 if w <= 0 { cc_dec(0x57, 0x52, w); return 0 - 1 } // WR= 159 sent = sent + w 160 } 161 return 0 162} 163 164// Encrypted-handshake walk (B1 probe pattern): drives the session 165// from WAIT_EE to WAIT_CLIENT_FIN. Returns 0 on verified server 166// Finished, else a fail code (20-91). 167func cc_walk(s: *Tls13ClientSession, fd: i64, val_ctx: *TlsValidationContext) -> i64 { 168 var recno: i64 = 0 169 var fail: i64 = 0 170 var running: i64 = 1 171 while running == 1 { 172 if s.state == NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN { running = 0 } 173 if recno >= 16 { running = 0; if fail == 0 { fail = 90 } } 174 if running == 1 { 175 let hs_record: *u8 = sys_mmap(17000) 176 let hs_total: i64 = nx_tls13_read_record_from_fd(fd, hs_record, 17000) 177 cc_dec(0x52, 0x43, recno) // RC= 178 cc_dec(0x54, 0x4C, hs_total) // TL= 179 if hs_total < 0 { fail = 20; running = 0 } 180 if running == 1 { 181 let oct: i64 = hs_record[0] & 0xff 182 cc_dec(0x43, 0x54, oct) // CT= 183 var skip: i64 = 0 184 if oct == 20 { skip = 1 } 185 if skip == 0 { 186 let header: *u8 = hs_record 187 let ctxt: *u8 = hs_record + NX_TLS13_RECORD_HEADER_LEN 188 let ct_len: i64 = hs_total - NX_TLS13_RECORD_HEADER_LEN - NX_TLS13_RECORD_TAG_LEN 189 let tag: *u8 = hs_record + hs_total - NX_TLS13_RECORD_TAG_LEN 190 let inner: *u8 = sys_mmap(ct_len + 16) 191 let real_ct_p: *i64 = sys_mmap(16) as *i64 192 let real_len_p: *i64 = sys_mmap(16) as *i64 193 let dv: i64 = nx_tls13_record_decrypt_v2( 194 s.cipher_suite, s.server_hs_traffic_key, s.server_hs_iv, 195 s.server_seq, header, ctxt, ct_len, tag, 196 inner, real_ct_p, real_len_p) 197 s.server_seq = s.server_seq + 1 198 cc_dec(0x44, 0x56, dv) // DV= 199 if dv != NX_TLS13_REC_VERDICT_OK { fail = 30; running = 0 } 200 if running == 1 { 201 cc_dec(0x49, 0x43, *real_ct_p) // IC= 202 if *real_ct_p == 21 { 203 if *real_len_p >= 2 { cc_dec(0x41, 0x44, inner[1] & 0xff) } // AD= 204 fail = 31; running = 0 205 } 206 if running == 1 { if *real_ct_p != NX_TLS13_CT_HANDSHAKE { fail = 32; running = 0 } } 207 } 208 if running == 1 { 209 let plen: i64 = *real_len_p 210 cc_dec(0x50, 0x4C, plen) // PL= 211 var off: i64 = 0 212 let new_state_p: *i64 = sys_mmap(16) as *i64 213 while off < plen { 214 if off + 4 > plen { fail = 33; off = plen } 215 if fail == 0 { 216 let mt: i64 = inner[off] & 0xff 217 let body_len: i64 = ((inner[off + 1] & 0xff) << 16) | ((inner[off + 2] & 0xff) << 8) | (inner[off + 3] & 0xff) 218 let msg_len: i64 = 4 + body_len 219 cc_dec(0x4D, 0x54, mt) // MT= 220 if off + msg_len > plen { fail = 34; off = plen } 221 if fail == 0 { 222 let cstate: i64 = s.state - NX_TLS13_CSESSION_STATE_WAIT_EE + NX_TLS13_CSTATE_WAIT_EE 223 let dw: i64 = tls13_client_dispatch_with_validation( 224 cstate, inner + off, msg_len, 225 s.server_hs_traffic_secret, s.transcript, 226 new_state_p, val_ctx) 227 cc_dec(0x44, 0x57, dw) // DW= 228 if dw != NX_TLS13_DWV_OK { fail = 40 + dw; off = plen } 229 if fail == 0 { 230 if *new_state_p == NX_TLS13_CSTATE_WAIT_EE { s.state = NX_TLS13_CSESSION_STATE_WAIT_EE } 231 if *new_state_p == NX_TLS13_CSTATE_WAIT_CERT { s.state = NX_TLS13_CSESSION_STATE_WAIT_CERT } 232 if *new_state_p == NX_TLS13_CSTATE_WAIT_CV { s.state = NX_TLS13_CSESSION_STATE_WAIT_CV } 233 if *new_state_p == NX_TLS13_CSTATE_WAIT_SF { s.state = NX_TLS13_CSESSION_STATE_WAIT_SF } 234 if *new_state_p == NX_TLS13_CSTATE_CONNECTED { s.state = NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN } 235 cc_dec(0x53, 0x54, s.state) // ST= 236 off = off + msg_len 237 } 238 } 239 } 240 } 241 if fail != 0 { running = 0 } 242 } 243 } 244 } 245 recno = recno + 1 246 } 247 } 248 if fail == 0 { 249 if s.state != NX_TLS13_CSESSION_STATE_WAIT_CLIENT_FIN { fail = 91 } 250 } 251 return fail 252} 253 254// LEG A: httpbin census with the PRODUCT dual-share CH. 255// Returns 0 = PASS (TLS-1.2-only signature confirmed, OR full 1.3 256// handshake if they enabled it), nonzero = honest fail code. 257func cc_leg_httpbin(store: *TrustStore, epoch: i64) -> i64 { 258 let url: *u8 = "https://httpbin.org/\x00" 259 let host_len: i64 = 11 260 let host_p: *i64 = sys_mmap(16) as *i64 261 let fd: i64 = cc_connect(url, host_len, host_p) 262 if fd < 0 { return 13 } 263 // Flattened: `*host_p as *u8` in one expression miscompiles on 264 // this lane (deref+ptr-cast shape; same family as the 4-deep 265 // nested-if landmine). Two statements compile correctly. 266 let hv: i64 = host_p[0] 267 let host: *u8 = hv as *u8 268 269 let cr: *u8 = sys_mmap(32) 270 let priv: *u8 = sys_mmap(32) 271 var i: i64 = 0 272 while i < 32 { cr[i] = (0xC0 + i) as u8; priv[i] = (0xA0 + i) as u8; i = i + 1 } 273 let s: *Tls13ClientSession = nx_tls13_client_session_new(cr, priv) 274 let ch_buf: *u8 = sys_mmap(1024) 275 let ch_n: i64 = nx_tls13_client_session_emit_ch(s, host, host_len, ch_buf, 1024) 276 cc_dec(0x43, 0x48, ch_n) // CH= 277 if ch_n < 0 { sys_close(fd); return 14 } 278 if cc_send_ch(fd, ch_buf, ch_n) != 0 { sys_close(fd); return 15 } 279 280 let sh_record: *u8 = sys_mmap(16700) 281 let sh_total: i64 = nx_tls13_read_record_from_fd(fd, sh_record, 16700) 282 if sh_total < 0 { sys_close(fd); cc_dec(0x52, 0x44, sh_total); return 16 } // RD= 283 284 // Plaintext alert at the CH boundary: classify. 285 if (sh_record[0] & 0xff) == 21 { 286 let al: i64 = sh_record[NX_TLS13_RECORD_HEADER_LEN] & 0xff 287 let ad: i64 = sh_record[NX_TLS13_RECORD_HEADER_LEN + 1] & 0xff 288 cc_dec(0x41, 0x4C, al) // AL= 289 cc_dec(0x41, 0x44, ad) // AD= 290 sys_close(fd) 291 if ad == 0 { return 0 } // close_notify = no-1.3 signature (measured 2026-06-10) 292 if ad == 70 { return 0 } // protocol_version = no-1.3, also honest 293 return 18 // any other alert = REAL CH problem 294 } 295 296 let sh_body: *u8 = sh_record + NX_TLS13_RECORD_HEADER_LEN 297 let sh_body_len: i64 = sh_total - NX_TLS13_RECORD_HEADER_LEN 298 let is13: i64 = cc_sh_is_tls13(sh_body, sh_body_len) 299 cc_dec(0x56, 0x33, is13) // V3= (1 = TLS 1.3 SH) 300 if is13 == 0 { 301 // TLS 1.2 ServerHello = the no-1.3 signature, leg PASS. 302 sys_close(fd) 303 return 0 304 } 305 if is13 < 0 { sys_close(fd); return 19 } 306 307 // httpbin speaks TLS 1.3 now: the leg DEMANDS the full handshake. 308 cc_sh_group_census(sh_body, sh_body_len) 309 let rv: i64 = nx_tls13_client_session_recv_sh(s, sh_body, sh_body_len) 310 cc_dec(0x52, 0x56, rv) // RV= 311 if rv != NX_TLS13_RECV_SH_OK { sys_close(fd); return 17 } 312 let val_ctx_raw: *u8 = sys_mmap(64) 313 let val_ctx: *TlsValidationContext = val_ctx_raw as *TlsValidationContext 314 val_ctx.store = store 315 val_ctx.sni_host = host 316 val_ctx.sni_host_len = host_len 317 val_ctx.now_epoch = epoch 318 let wf: i64 = cc_walk(s, fd, val_ctx) 319 sys_close(fd) 320 return wf 321} 322 323// LEG B: live P-256 proof -- secp256r1-ONLY CH to github.com, 324// full encrypted handshake to verified server Finished. 325func cc_leg_p256(store: *TrustStore, epoch: i64) -> i64 { 326 let url: *u8 = "https://github.com/\x00" 327 let host_len: i64 = 10 328 let host_p: *i64 = sys_mmap(16) as *i64 329 let fd: i64 = cc_connect(url, host_len, host_p) 330 if fd < 0 { return 23 } 331 // Flattened: `*host_p as *u8` in one expression miscompiles on 332 // this lane (deref+ptr-cast shape; see leg A + landmine note). 333 let hv: i64 = host_p[0] 334 let host: *u8 = hv as *u8 335 336 let cr: *u8 = sys_mmap(32) 337 let priv: *u8 = sys_mmap(32) 338 var i: i64 = 0 339 while i < 32 { cr[i] = (0xD0 + i) as u8; priv[i] = (0x90 + i) as u8; i = i + 1 } 340 let s: *Tls13ClientSession = nx_tls13_client_session_new(cr, priv) 341 342 // P-256-only CH: emit2 with null x25519 share, then mirror 343 // emit_ch's transcript + state steps (the session emit keeps 344 // the product dual-share contract; this is the census mode). 345 let ch_buf: *u8 = sys_mmap(1024) 346 let ch_n: i64 = tls13_client_hello_emit2( 347 cr, host, host_len, 0 as *u8, s.p256_pub, ch_buf, 1024 348 ) 349 cc_dec(0x43, 0x48, ch_n) // CH= 350 if ch_n < 0 { sys_close(fd); return 24 } 351 nx_tls13_transcript_update(s.transcript, ch_buf, ch_n) 352 s.state = NX_TLS13_CSESSION_STATE_CH_SENT 353 if cc_send_ch(fd, ch_buf, ch_n) != 0 { sys_close(fd); return 25 } 354 355 let sh_record: *u8 = sys_mmap(16700) 356 let sh_total: i64 = nx_tls13_read_record_from_fd(fd, sh_record, 16700) 357 if sh_total < 0 { sys_close(fd); cc_dec(0x52, 0x44, sh_total); return 26 } // RD= 358 if (sh_record[0] & 0xff) == 21 { 359 cc_dec(0x41, 0x4C, sh_record[NX_TLS13_RECORD_HEADER_LEN] & 0xff) // AL= 360 cc_dec(0x41, 0x44, sh_record[NX_TLS13_RECORD_HEADER_LEN + 1] & 0xff) // AD= 361 sys_close(fd) 362 return 28 363 } 364 let sh_body: *u8 = sh_record + NX_TLS13_RECORD_HEADER_LEN 365 let sh_body_len: i64 = sh_total - NX_TLS13_RECORD_HEADER_LEN 366 let gr: i64 = cc_sh_group_census(sh_body, sh_body_len) 367 // The whole point of leg B: server took the secp256r1 share. 368 if gr != 23 { sys_close(fd); return 29 } 369 let rv: i64 = nx_tls13_client_session_recv_sh(s, sh_body, sh_body_len) 370 cc_dec(0x52, 0x56, rv) // RV= 371 if rv != NX_TLS13_RECV_SH_OK { sys_close(fd); return 27 } 372 373 let val_ctx_raw: *u8 = sys_mmap(64) 374 let val_ctx: *TlsValidationContext = val_ctx_raw as *TlsValidationContext 375 val_ctx.store = store 376 val_ctx.sni_host = host 377 val_ctx.sni_host_len = host_len 378 val_ctx.now_epoch = epoch 379 let wf: i64 = cc_walk(s, fd, val_ctx) 380 sys_close(fd) 381 return wf 382} 383 384func main() -> i64 { 385 let epoch: i64 = sys_now_realtime_sec() // real clock (B1 lesson) 386 let r: i64 = nx_trust_store_load_from_certdata("/tmp/mozilla_certdata.txt\x00" as *u8, 300, 4194304) 387 if r <= 0 { cc_dec(0x4C, 0x4F, r); return 11 } // LO= 388 let store: *TrustStore = r as *TrustStore 389 cc_dec(0x43, 0x41, trust_store_count(store)) // CA= 390 391 let la: i64 = cc_leg_httpbin(store, epoch) 392 cc_dec(0x4C, 0x31, la) // L1= 393 let lb: i64 = cc_leg_p256(store, epoch) 394 cc_dec(0x4C, 0x32, lb) // L2= 395 396 if la != 0 { return la } 397 if lb != 0 { return 100 + lb } 398 return 0 399}