code wiki / (root) / nx_tls12_hello.nx

nx_tls12_hello.nx source

↩ module page · 93 lines · 4671 B

1// nx_tls12_hello.nx -- TLS 1.2 ClientHello emitter (rung 1 of the sovereign TLS 1.2 client). 2// 3// ROOT CAUSE (probed 2026-06-23, news.ycombinator.com): some endpoints are TLS 1.2-ONLY and reject our 4// 1.3-only ClientHello with a fatal protocol_version(70) alert; after we offered 1.2 they want a 1.2 cipher 5// we didn't send. This module emits a real 1.2 ClientHello so we can negotiate 1.2 with those servers -- 6// the "access everywhere" unlock (the crypto primitives + nx_tls12_prf already exist; this wires the protocol). 7// 8// Offered cipher suites = the ones we can actually COMPLETE (AEAD + SHA256 PRF = nx_tls12_prf_sha256): 9// 0xC02B ECDHE-ECDSA-AES128-GCM-SHA256 10// 0xC02F ECDHE-RSA-AES128-GCM-SHA256 11// 0xCCA9 ECDHE-ECDSA-CHACHA20-POLY1305-SHA256 12// 0xCCA8 ECDHE-RSA-CHACHA20-POLY1305-SHA256 13// (AES-256-GCM-SHA384 omitted: needs the P_SHA384 PRF/key-schedule, a later rung.) 14// 15// Extensions reuse the version-agnostic emitters from nx_tls13_ext.nx (server_name, supported_groups, 16// signature_algorithms) + a 1.2-needed ec_point_formats. license_tier: ORIGINAL 17import "nx_syscalls.nx" 18import "nx_tls13_ext.nx" 19 20// supported_groups (ext 0x000A) offering secp256r1 (0x0017) ONLY. The shared 1.3 emitter offers X25519 21// first, but this 1.2 client completes ECDHE over P-256 ONLY (nx_p256_ecdh) -- if a modern server picks 22// X25519 for the ECDHE the handshake fails at key-agreement (verdict 14). Offering just P-256 FORCES the 23// server onto the curve we can complete. All TLS servers support secp256r1, so reach is unaffected. 24func tls12_ext_emit_supported_groups_p256(out: *u8, out_cap: i64) -> i64 { 25 if out_cap < 8 { return 0 - 1 } 26 tls_write_u16_be(out, 0, 0x000A) // supported_groups 27 tls_write_u16_be(out, 2, 4) // ext_data_len = 2 (list_len) + 2 (one group) 28 tls_write_u16_be(out, 4, 2) // list_len = 2 bytes (1 entry) 29 tls_write_u16_be(out, 6, 0x0017) // secp256r1 30 return 8 31} 32// ec_point_formats (ext 0x000B): advertise "uncompressed" (0) -- required by many TLS 1.2 ECDHE servers. 33func tls12_ext_emit_ec_point_formats(out: *u8, out_cap: i64) -> i64 { 34 if out_cap < 6 { return 0 - 1 } 35 tls_write_u16_be(out, 0, 0x000B) // ec_point_formats 36 tls_write_u16_be(out, 2, 2) // ext_data_len = 1 (list len) + 1 (format) 37 out[4] = 1 as u8 // formats list length = 1 38 out[5] = 0 as u8 // format 0 = uncompressed 39 return 6 40} 41 42// Emit a TLS 1.2 ClientHello body (handshake message: type 1 + 3-byte len + body) into `out`. 43// Returns total bytes written, or negative on buffer overflow. 44func tls12_client_hello_emit(random32: *u8, sni: *u8, sni_len: i64, out: *u8, out_cap: i64) -> i64 { 45 if out_cap < 300 + sni_len { return 0 - 1 } 46 if sni_len < 1 { return 0 - 2 } 47 48 var o: i64 = 4 // reserve 4 bytes for the handshake header (type + 24-bit len) 49 tls_write_u16_be(out, o, 0x0303) // client_version = TLS 1.2 50 o = o + 2 51 var i: i64 = 0 52 while i < 32 { out[o + i] = random32[i]; i = i + 1 } 53 o = o + 32 54 out[o] = 0 as u8 // legacy_session_id: empty 55 o = o + 1 56 57 // cipher_suites: list_len(2) + 4 suites * 2 bytes 58 tls_write_u16_be(out, o, 8); o = o + 2 59 tls_write_u16_be(out, o, 0xC02B); o = o + 2 60 tls_write_u16_be(out, o, 0xC02F); o = o + 2 61 tls_write_u16_be(out, o, 0xCCA9); o = o + 2 62 tls_write_u16_be(out, o, 0xCCA8); o = o + 2 63 64 // legacy_compression_methods = { null } 65 out[o] = 1 as u8; o = o + 1 66 out[o] = 0 as u8; o = o + 1 67 68 // extensions: reserve 2 bytes for the list length, fill after 69 let ext_len_off: i64 = o 70 o = o + 2 71 let ext_start: i64 = o 72 let r1: i64 = tls13_ext_emit_server_name(sni, sni_len, (out as i64 + o) as *u8, out_cap - o) 73 if r1 < 0 { return 0 - 3 } 74 o = o + r1 75 let r2: i64 = tls12_ext_emit_supported_groups_p256((out as i64 + o) as *u8, out_cap - o) 76 if r2 < 0 { return 0 - 4 } 77 o = o + r2 78 let r3: i64 = tls12_ext_emit_ec_point_formats((out as i64 + o) as *u8, out_cap - o) 79 if r3 < 0 { return 0 - 5 } 80 o = o + r3 81 let r4: i64 = tls13_ext_emit_signature_algorithms((out as i64 + o) as *u8, out_cap - o) 82 if r4 < 0 { return 0 - 6 } 83 o = o + r4 84 tls_write_u16_be(out, ext_len_off, o - ext_start) 85 86 // handshake header: msg_type(1) = ClientHello(1) + length(3) = body bytes after the header 87 out[0] = 1 as u8 88 let body: i64 = o - 4 89 out[1] = ((body >> 16) & 0xff) as u8 90 out[2] = ((body >> 8) & 0xff) as u8 91 out[3] = (body & 0xff) as u8 92 return o 93}