code wiki / (root) / nx_ws_client_gate.nx

nx_ws_client_gate.nx source

↩ module page · 150 lines · 7998 B

1// nx_ws_client_gate.nx -- THE GATE FOR THE RFC 6455 CLIENT HALF, 2026-09-03. 2// 3// SUBJECT: nx_ws_build_frame_masked / nx_ws_parse_frame_server / nx_ws_verify_accept in-process, AND their 4// interoperation with the incumbent nx_websocket.nx. 5// 6// THE TOOTH THAT CARRIES THE CLAIM IS T2, AND IT NEEDS NO FIXTURE OF MINE. A frame built by the new CLIENT 7// builder is handed to the estate's EXISTING SERVER PARSER, untouched, and must come back byte-for-byte. 8// That is real interop against code I did not write and cannot bend: if my masking were wrong in any bit, 9// the incumbent's unmask would produce different bytes and the tooth would fail. 10// 11// T5 IS THE REGRESSION GUARD. The incumbent must still REFUSE an unmasked frame exactly as before. This 12// file is a sibling, not a widening, and the video /signal lane depends on that rule holding. 13// 14// Teeth, in order: 15// T1 a masked frame sets the MASK bit and does NOT leave the payload in clear. 16// T2 INTEROP: client builder -> INCUMBENT server parser recovers the payload byte-for-byte. 17// T3 the reverse direction: incumbent unmasked builder -> new client parser recovers the payload. 18// T4 NEG-CONTROL: the client parser REFUSES a masked frame (a server must not mask). 19// T5 REGRESSION: the incumbent parser still REFUSES an unmasked frame -- unchanged by this file. 20// T6 a payload crossing the 126-byte boundary round-trips (the extended-length path, not just the short). 21// T7 NEG-CONTROL: an undersized output buffer REFUSES instead of writing past it. 22// T8 accept-key verification MATCHES for the key we sent, composing the incumbent's derivation. 23// T9 NEG-CONTROL: a wrong accept value FAILS verification -- without this, T8 could pass on a stub. 24// MEASURED 9/9 GREEN 2026-09-03: 39-byte masked frame round-tripped through the incumbent parser (opcode 1, 25// 33 payload), 308-byte extended-length frame likewise, both refusals firing at -5, buffer refusal at -3. 26// license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0 27import "nx_syscalls_x86_64.nx" 28import "nx_gate_verdict.nx" 29import "nx_ws_client.nx" 30 31const WG_CAP: i64 = 4096 32const WG_SLOT: i64 = 8 33const WG_LONG: i64 = 300 34 35func wg_eq(a: *u8, b: *u8, n: i64) -> i64 { 36 var i: i64 = 0 37 while i < n { 38 if a[i] != b[i] { return 0 } 39 i = i + 1 40 } 41 return 1 42} 43 44func main(argc: i64, argv: *i64) -> i64 { 45 let ctr: *i64 = gv_ctr() 46 gv_head("nx_ws_client gate -- the RFC 6455 client half, proven against the estate's own server parser" as *u8) 47 48 let msg: *u8 = "{\"id\":1,\"method\":\"Page.navigate\"}" as *u8 49 var mlen: i64 = 0 50 while msg[mlen] != (0 as u8) { mlen = mlen + 1 } 51 52 let mask: *u8 = sys_mmap(WSC_MASK_BYTES) as *u8 53 mask[0] = 0x37 as u8 54 mask[1] = 0xfa as u8 55 mask[2] = 0x21 as u8 56 mask[3] = 0x3d as u8 57 58 let buf: *u8 = sys_mmap(WG_CAP) as *u8 59 let n: i64 = nx_ws_build_frame_masked(buf, WG_CAP, NX_WS_OPCODE_TEXT, msg, mlen, mask) 60 gv_puts(" [T1] masked frame bytes=" as *u8); gv_num(n); gv_puts(" b1=" as *u8); gv_num(buf[1] as i64); gv_puts("\n" as *u8) 61 var t1: i64 = 0 62 if n > 0 { 63 if (buf[1] & NX_WS_MASK_BIT) != 0 { 64 var differs: i64 = 0 65 var i: i64 = 0 66 while i < mlen { 67 if buf[2 + WSC_MASK_BYTES + i] != msg[i] { differs = 1 } 68 i = i + 1 69 } 70 if differs == 1 { t1 = 1 } 71 } 72 } 73 gv_check("a-masked-frame-sets-the-MASK-bit-and-does-not-leave-the-payload-in-clear" as *u8, t1, ctr) 74 75 let op: *i64 = sys_mmap(WG_SLOT) as *i64 76 let poff: *i64 = sys_mmap(WG_SLOT) as *i64 77 let plen: *i64 = sys_mmap(WG_SLOT) as *i64 78 let consumed: i64 = nx_ws_parse_frame_inplace(buf, n, op, poff, plen) 79 gv_puts(" [T2] incumbent parser consumed=" as *u8); gv_num(consumed); gv_puts(" opcode=" as *u8); gv_num(*op); gv_puts(" plen=" as *u8); gv_num(*plen); gv_puts("\n" as *u8) 80 var t2: i64 = 0 81 if consumed == n { 82 if *op == NX_WS_OPCODE_TEXT { 83 if *plen == mlen { 84 if wg_eq((buf as i64 + *poff) as *u8, msg, mlen) == 1 { t2 = 1 } 85 } 86 } 87 } 88 gv_check("INTEROP-a-frame-built-by-the-client-half-is-recovered-byte-for-byte-by-the-INCUMBENT-server-parser" as *u8, t2, ctr) 89 90 let sbuf: *u8 = sys_mmap(WG_CAP) as *u8 91 let sn: i64 = nx_ws_build_frame(sbuf, WG_CAP, NX_WS_OPCODE_TEXT, msg, mlen) 92 let sc: i64 = nx_ws_parse_frame_server(sbuf, sn, op, poff, plen) 93 gv_puts(" [T3] server->client consumed=" as *u8); gv_num(sc); gv_puts(" plen=" as *u8); gv_num(*plen); gv_puts("\n" as *u8) 94 var t3: i64 = 0 95 if sc == sn { if *plen == mlen { if wg_eq((sbuf as i64 + *poff) as *u8, msg, mlen) == 1 { t3 = 1 } } } 96 gv_check("the-reverse-direction-incumbent-unmasked-builder-to-new-client-parser-recovers-the-payload" as *u8, t3, ctr) 97 98 let r4: i64 = nx_ws_parse_frame_server(buf, n, op, poff, plen) 99 gv_puts(" [T4] client parser on a MASKED frame -> " as *u8); gv_num(r4); gv_puts("\n" as *u8) 100 gv_check("neg-control-the-client-parser-REFUSES-a-masked-frame (a conforming server never masks)" as *u8, (r4 < 0) as i64, ctr) 101 102 let r5: i64 = nx_ws_parse_frame_inplace(sbuf, sn, op, poff, plen) 103 gv_puts(" [T5] incumbent parser on an UNMASKED frame -> " as *u8); gv_num(r5); gv_puts("\n" as *u8) 104 gv_check("regression-the-incumbent-parser-still-refuses-an-unmasked-frame-unchanged-by-this-file" as *u8, (r5 < 0) as i64, ctr) 105 106 let big: *u8 = sys_mmap(WG_LONG) as *u8 107 var b: i64 = 0 108 while b < WG_LONG { 109 big[b] = (65 + (b % 26)) as u8 110 b = b + 1 111 } 112 let bbuf: *u8 = sys_mmap(WG_CAP) as *u8 113 let bn: i64 = nx_ws_build_frame_masked(bbuf, WG_CAP, NX_WS_OPCODE_TEXT, big, WG_LONG, mask) 114 let bc: i64 = nx_ws_parse_frame_inplace(bbuf, bn, op, poff, plen) 115 gv_puts(" [T6] 300-byte payload built=" as *u8); gv_num(bn); gv_puts(" consumed=" as *u8); gv_num(bc); gv_puts(" plen=" as *u8); gv_num(*plen); gv_puts("\n" as *u8) 116 var t6: i64 = 0 117 if bc == bn { if *plen == WG_LONG { if wg_eq((bbuf as i64 + *poff) as *u8, big, WG_LONG) == 1 { t6 = 1 } } } 118 gv_check("a-payload-crossing-the-126-byte-boundary-round-trips-through-the-extended-length-path" as *u8, t6, ctr) 119 120 let tiny: *u8 = sys_mmap(WG_CAP) as *u8 121 let r7: i64 = nx_ws_build_frame_masked(tiny, 4, NX_WS_OPCODE_TEXT, msg, mlen, mask) 122 gv_puts(" [T7] undersized buffer -> " as *u8); gv_num(r7); gv_puts("\n" as *u8) 123 gv_check("neg-control-an-undersized-output-buffer-REFUSES-instead-of-writing-past-it" as *u8, (r7 < 0) as i64, ctr) 124 125 let ckey: *u8 = "dGhlIHNhbXBsZSBub25jZQ==" as *u8 126 var klen: i64 = 0 127 while ckey[klen] != (0 as u8) { klen = klen + 1 } 128 let derived: *u8 = sys_mmap(WG_CAP) as *u8 129 let dv: i64 = nx_ws_accept_key(ckey, klen, derived) 130 let dn: i64 = WSC_ACCEPT_LEN 131 let scratch: *u8 = sys_mmap(WG_CAP) as *u8 132 let ok8: i64 = nx_ws_verify_accept(ckey, klen, derived, dn, scratch) 133 gv_puts(" [T8] accept_key verdict=" as *u8); gv_num(dv); gv_puts(" verify=" as *u8); gv_num(ok8); gv_puts("\n" as *u8) 134 var t8: i64 = 0 135 if dv == NX_WS_VERDICT_OK { if ok8 == 1 { t8 = 1 } } 136 gv_check("accept-key-verification-matches-for-the-key-we-sent (composing the incumbent derivation)" as *u8, t8, ctr) 137 138 let wrong: *u8 = sys_mmap(WG_CAP) as *u8 139 var w: i64 = 0 140 while w < dn { 141 wrong[w] = derived[w] 142 w = w + 1 143 } 144 wrong[0] = (derived[0] + 1) as u8 145 let ok9: i64 = nx_ws_verify_accept(ckey, klen, wrong, dn, scratch) 146 gv_puts(" [T9] one byte flipped -> verify=" as *u8); gv_num(ok9); gv_puts("\n" as *u8) 147 gv_check("neg-control-a-wrong-accept-value-FAILS-verification (without this T8 could pass on a stub)" as *u8, (ok9 == 0) as i64, ctr) 148 149 return gv_verdict("ws_client" as *u8, ctr, "the RFC 6455 client half proven by interop against the estate's own server parser in both directions, with the incumbent's rule unchanged and the accept-key check biting" as *u8) 150}