code wiki / (root) / nx_tls13_ext.nx

nx_tls13_ext.nx source

↩ module page · 485 lines · 22198 B

1// nx_tls13_ext.nx -- TLS 1.3 extension emitters + ServerHello parsers. 2// 3// Phase 0b §F.1 of the Nishi TLS 1.3 stack per 4// docs/NISHI_TLS13_GAP_AUDIT.md. The byte-level emit functions 5// for the 5 extensions a minimum-credible TLS 1.3 ClientHello 6// MUST carry, plus parse functions for the two extensions a 7// ServerHello echoes back (supported_versions + key_share). 8// 9// Per RFC 8446 §9.2: any TLS 1.3 ClientHello MUST contain 10// supported_versions, supported_groups, key_share, and 11// signature_algorithms. We add server_name (RFC 6066 §3) so 12// we can talk to TLS-vhosted servers, and we make ALPN 13// trivially extendable. 14// 15// Wire format per extension is: 16// uint16 extension_type 17// uint16 extension_data_length 18// opaque extension_data[extension_data_length] 19// 20// What it does today: 21// Client emit: 22// - server_name (SNI, RFC 6066 §3, host_name only) 23// - supported_versions client-variant TLS 1.3 only (§4.2.1) 24// - supported_groups X25519 + secp256r1 (§4.2.7) 25// - signature_algorithms ed25519 + rsa_pss_rsae_sha256 + 26// rsa_pss_rsae_sha384 + ecdsa_secp256r1_sha256 (§4.2.3) 27// - key_share client X25519 only (§4.2.8) 28// Server parse: 29// - supported_versions server-variant (single u16) (§4.2.1) 30// - key_share server-variant (single KeyShareEntry) (§4.2.8) 31// 32// What it doesn't do yet: 33// - ALPN emit/parse (queued; single-protocol case is trivial, 34// multi-protocol needs a more general list helper) 35// - psk_key_exchange_modes (queued with Gap M resumption) 36// - pre_shared_key (queued with Gap M) 37// - cookie (HRR path; queued with Gap G) 38// - additional cert chain extensions (status_request, SCT) 39// - additional named groups (P-384, P-521, Kyber, X25519+Kyber768) 40// - additional signature schemes (ecdsa_secp384r1_sha384, 41// rsa_pkcs1_sha256 for legacy, ed448) 42// 43// KAT verified: 44// - each emit produces byte-exact bytes per the wire format 45// - parse round-trip: emit then parse recovers the input 46// 47// Composes with: 48// - nx_tls13 (extension type / named group / signature scheme constants) 49// - nx_tls13_hello (queued; composes these into ClientHello) 50// 51// license_tier: INDEPENDENT_REDERIVE 52// genealogy_id: international-research-sources/ietf/rfc_8446 + ietf/rfc_6066 + ietf/rfc_7301 53// lineage_id: nishi_tls13_extensions_q10 54 55// nx_safety_envelope: 56// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 57// sil_target: SIL1 58// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 59// verdict: NOT_YET_EVALUATED 60 61import "nx_syscalls.nx" 62import "nx_tls13.nx" 63const NX_MAGIC_65535: i64 = 65535 64 65// Sealed verdict for emit/parse operations. 66const NX_TLS13_EXT_VERDICT_OK: i64 = 1 67const NX_TLS13_EXT_VERDICT_BUF_OVERFLOW: i64 = 2 68const NX_TLS13_EXT_VERDICT_BAD_FORMAT: i64 = 3 69const NX_TLS13_EXT_VERDICT_TRUNCATED: i64 = 4 70const NX_TLS13_EXT_VERDICT_UNSUPPORTED: i64 = 5 71const NX_TLS13_EXT_VERDICT_N: i64 = 6 72 73// SNI NameType per RFC 6066 §3 (only host_name = 0 is registered). 74const NX_TLS13_SNI_NAME_TYPE_HOST: i64 = 0 75 76// Emit a complete extension into `out`. Caller provides the body via 77// a callback-less pattern: the body emitter is inlined here per 78// extension since NishiLang has no closures yet. 79// 80// Returns total bytes written (4 header + body), or a negative 81// NX_TLS13_EXT_VERDICT_* on error. 82 83// ---- server_name (extension 0) ---- 84// 85// Wire: 86// ext_type(2) + ext_len(2) 87// + ServerNameList_len(2) 88// + name_type(1) + host_name_len(2) + host_name(host_len bytes) 89// 90// Total = 4 + 2 + 1 + 2 + host_len = 9 + host_len bytes. 91func tls13_ext_emit_server_name( 92 host: *u8, host_len: i64, 93 out: *u8, out_cap: i64 94) -> i64 { 95 if host_len < 1 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 96 if host_len > NX_MAGIC_65535 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 97 let total: i64 = 9 + host_len 98 if out_cap < total { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 99 let inner_data_len: i64 = 3 + 2 + host_len // list_len(2) + type(1) + name_len(2) + name 100 let server_name_list_len: i64 = 1 + 2 + host_len 101 tls_write_u16_be(out, 0, EXT_SERVER_NAME) 102 tls_write_u16_be(out, 2, inner_data_len) 103 tls_write_u16_be(out, 4, server_name_list_len) 104 out[6] = NX_TLS13_SNI_NAME_TYPE_HOST & 0xff 105 tls_write_u16_be(out, 7, host_len) 106 var i: i64 = 0 107 while i < host_len { 108 out[9 + i] = host[i] 109 i = i + 1 110 } 111 return total 112} 113 114// ---- supported_versions client (extension 43, TLS 1.3 only) ---- 115// 116// Wire: 117// ext_type(2)=0x002b + ext_len(2)=0x0003 118// + versions_len(1)=0x02 + version(2)=0x0304 119// 120// Always exactly 9 bytes. No caller-supplied data. 121func tls13_ext_emit_supported_versions_tls13(out: *u8, out_cap: i64) -> i64 { 122 // Offer TLS 1.3 AND TLS 1.2 (browser-like). ROOT CAUSE (probed 2026-06-23 vs news.ycombinator.com): a 123 // 1.3-ONLY offer drew a FATAL protocol_version(70) alert from servers whose policy wants 1.2 in the list. 124 // A real 1.3 server still negotiates 1.3 (which we speak) when both are offered; Wikipedia keeps working. 125 // Return value EXACTLY matches the 9 bytes written (the old off-by-2 warning was a return!=written mismatch; 126 // here they agree at 9). 127 if out_cap < 9 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 128 tls_write_u16_be(out, 0, EXT_SUPPORTED_VERSIONS) 129 tls_write_u16_be(out, 2, 5) // ext_data_len = 1 (list len) + 4 (two versions) 130 out[4] = 4 // versions list len = 4 (two versions * 2 bytes) 131 tls_write_u16_be(out, 5, TLS_13_VERSION) // 0x0304 (preferred) 132 tls_write_u16_be(out, 7, 0x0303) // 0x0303 = TLS 1.2 (server still picks 1.3 if it supports it) 133 return 9 134} 135 136// ---- ALPN (extension 16, RFC 7301 + RFC 8446 §4.2) ---- 137// 138// Wire format: 139// ext_type(2)=0x0010 + ext_len(2) 140// + protocol_name_list_len(2) 141// + (protocol_name_len(1) + protocol_name(bytes)) repeated 142// 143// This emit ships the single most common case: advertise "http/1.1" 144// alone. The server selects ONE protocol from the client's list and 145// echoes it back. Single-protocol clients still MUST send the list 146// framing; the protocol_name_list_len wraps the (len + name) pair. 147// 148// Bytes for "http/1.1": 149// protocol_name_len(1) = 8 150// protocol_name(8) = "http/1.1" 151// protocol_name_list_len(2) = 9 (the 1 + 8 above) 152// ext_data_len(2) = 11 (the 2 + 9 above) 153// ext_type(2) + ext_data_len(2) + payload(11) = 15 wire bytes total. 154// 155// Multi-protocol ALPN (e.g. advertising both "h2" and "http/1.1") is 156// queued -- needs a more general list helper that takes a caller- 157// supplied slice of (name, len) pairs. This single-protocol primitive 158// is enough to unblock fetching from public HTTPS servers that don't 159// require HTTP/2 (most servers will fall back to http/1.1 when h2 is 160// not in the ALPN list). 161func tls13_ext_emit_alpn_http11(out: *u8, out_cap: i64) -> i64 { 162 if out_cap < 15 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 163 tls_write_u16_be(out, 0, EXT_APPLICATION_LAYER_PROTOCOL) 164 tls_write_u16_be(out, 2, 11) // ext_data_len = list_len(2) + entry(9) 165 tls_write_u16_be(out, 4, 9) // protocol_name_list_len = name_len(1) + name(8) 166 out[6] = 8 as u8 // protocol_name_len 167 out[7] = 104 as u8 // 'h' 168 out[8] = 116 as u8 // 't' 169 out[9] = 116 as u8 // 't' 170 out[10] = 112 as u8 // 'p' 171 out[11] = 47 as u8 // '/' 172 out[12] = 49 as u8 // '1' 173 out[13] = 46 as u8 // '.' 174 out[14] = 49 as u8 // '1' 175 return 15 176} 177 178// ---- ALPN advertise ["h2","http/1.1"] (extension 16, RFC 7301 + RFC 9113 §3.1) ---- 179// 180// R4-H2-006 (HTTP/2 transport ladder). The multi-protocol sibling of 181// tls13_ext_emit_alpn_http11 (:157): a 2-entry ProtocolNameList in the 182// browser-default order h2-first, http/1.1-fallback. h2c is DEPRECATED and is 183// never advertised (RFC 9113 §3.1). This is the ALPN list a sovereign HTTP/2 184// client sends so the server can negotiate "h2" (and gracefully fall back to 185// "http/1.1" when it cannot). 186// 187// Wire (RFC 7301 §3.1 / RFC 8446 §4.2): 188// ext_type(2)=0x0010 + ext_data_len(2) 189// + protocol_name_list_len(2) 190// + name_len(1)=2 + "h2" (0x68 0x32) 191// + name_len(1)=8 + "http/1.1" (0x68 0x74 0x74 0x70 0x2f 0x31 0x2e 0x31) 192// list_len = (1+2) + (1+8) = 12 193// ext_data_len = list_len(2) + 12 = 14 194// total wire = type(2) + ext_data_len(2) + 14 = 18 bytes. 195// KAT (spec Part A): 00 10 00 0e 00 0c 02 68 32 08 68 74 74 70 2f 31 2e 31 196func tls13_ext_emit_alpn_h2_http11(out: *u8, out_cap: i64) -> i64 { 197 if out_cap < 18 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 198 let h2_len: i64 = 2 199 let http11_len: i64 = 8 200 let list_len: i64 = (1 + h2_len) + (1 + http11_len) // 12 201 let ext_data_len: i64 = 2 + list_len // 14 202 tls_write_u16_be(out, 0, EXT_APPLICATION_LAYER_PROTOCOL) 203 tls_write_u16_be(out, 2, ext_data_len) // 0x000e 204 tls_write_u16_be(out, 4, list_len) // 0x000c 205 // entry 1: "h2" 206 out[6] = h2_len as u8 // 0x02 207 out[7] = 104 as u8 // 'h' 208 out[8] = 50 as u8 // '2' 209 // entry 2: "http/1.1" 210 out[9] = http11_len as u8 // 0x08 211 out[10] = 104 as u8 // 'h' 212 out[11] = 116 as u8 // 't' 213 out[12] = 116 as u8 // 't' 214 out[13] = 112 as u8 // 'p' 215 out[14] = 47 as u8 // '/' 216 out[15] = 49 as u8 // '1' 217 out[16] = 46 as u8 // '.' 218 out[17] = 49 as u8 // '1' 219 return 18 220} 221 222// ---- ALPN advertise ["h2"] ONLY (extension 16, RFC 7301 + RFC 9113 §3.1) ---- 223// 224// R4-H2 capstone (NBC-FETCH-001): the UNAMBIGUOUS-negotiation sibling of 225// tls13_ext_emit_alpn_h2_http11 (:192). Advertising EXACTLY ONE protocol "h2" 226// makes the negotiation a yes/no: a server that supports HTTP/2 selects "h2" 227// (and the connection IS h2 after CONNECTED -- no need to parse the encrypted EE 228// ALPN echo to learn the protocol); a server that does NOT support h2 MUST abort 229// the handshake with a no_application_protocol alert (RFC 7301 §3.2). This is 230// what an h2-ONLY sovereign client sends so "TLS ALPN selects h2" == "handshake 231// succeeded". http/1.1 fallback is deliberately omitted here (use 232// tls13_ext_emit_alpn_h2_http11 when graceful downgrade is wanted). 233// 234// Wire (RFC 7301 §3.1 / RFC 8446 §4.2): 235// ext_type(2)=0x0010 + ext_data_len(2) 236// + protocol_name_list_len(2) 237// + name_len(1)=2 + "h2" (0x68 0x32) 238// list_len = 1 + 2 = 3 ; ext_data_len = 2 + 3 = 5 ; total wire = 4 + 5 = 9. 239// KAT: 00 10 00 05 00 03 02 68 32 240func tls13_ext_emit_alpn_h2_only(out: *u8, out_cap: i64) -> i64 { 241 if out_cap < 9 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 242 let h2_len: i64 = 2 243 let list_len: i64 = 1 + h2_len // 3 244 let ext_data_len: i64 = 2 + list_len // 5 245 tls_write_u16_be(out, 0, EXT_APPLICATION_LAYER_PROTOCOL) 246 tls_write_u16_be(out, 2, ext_data_len) // 0x0005 247 tls_write_u16_be(out, 4, list_len) // 0x0003 248 out[6] = h2_len as u8 // 0x02 249 out[7] = 104 as u8 // 'h' 250 out[8] = 50 as u8 // '2' 251 return 9 252} 253 254// ---- Parse the server-selected ALPN protocol from EncryptedExtensions ---- 255// 256// Per RFC 7301 §3.1 the server echoes the chosen protocol in a 257// ProtocolNameList that MUST contain EXACTLY ONE ProtocolName (TLS 1.3 carries 258// it in EncryptedExtensions, RFC 8446 §4.3.1 -- parse AFTER decrypting EE). 259// 260// Inputs: `ext_data` points PAST the ext_type + ext_data_len pair (i.e. at the 261// 2-byte protocol_name_list_len); `ext_data_len` is that ext_data length. 262// Outputs: *out_ptr = offset (relative to ext_data) of the protocol-name bytes, 263// *out_len = its length. Returns OK or a negative NX_TLS13_EXT_VERDICT_*. 264// 265// Rejections (tamper discipline): 266// - ext_data shorter than the list header -> TRUNCATED 267// - list_len != ext_data_len - 2 (framing mismatch) -> BAD_FORMAT 268// - the single entry does not consume the whole list 269// (i.e. a SECOND ProtocolName follows -> trailing bytes) -> BAD_FORMAT 270// KAT (spec Part A): EE ALPN selecting h2, ext_data = 00 03 02 68 32 (len 5) -> 271// out_ptr -> the "68 32" bytes (offset 3), out_len = 2 ("h2"). 272func tls13_ext_parse_alpn_selected( 273 ext_data: *u8, ext_data_len: i64, 274 out_ptr: *i64, out_len: *i64 275) -> i64 { 276 if ext_data_len < 3 { return 0 - NX_TLS13_EXT_VERDICT_TRUNCATED } 277 let list_len: i64 = tls_read_u16_be(ext_data, 0) 278 // list_len MUST equal everything after the 2-byte list-length field. 279 if list_len != ext_data_len - 2 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 280 if list_len < 1 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 281 let name_len: i64 = ext_data[2] & 0xff 282 // the SINGLE entry (name_len(1) + name) MUST exactly fill the list -- a 283 // second ProtocolName would leave (name_len(1)+name) < list_len, rejected. 284 if 1 + name_len != list_len { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 285 if name_len < 1 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 286 *out_ptr = 3 // offset of the name bytes within ext_data 287 *out_len = name_len 288 return NX_TLS13_EXT_VERDICT_OK 289} 290 291// ---- supported_groups (extension 10) ---- 292// 293// Wire: 294// ext_type(2)=0x000a + ext_len(2) 295// + named_group_list_len(2) 296// + named_group(2) repeated 297// 298// We ship X25519 + secp256r1 = 2 entries = 4 bytes of groups. 299// Total: 4 + 2 + 4 = 10 bytes. 300func tls13_ext_emit_supported_groups(out: *u8, out_cap: i64) -> i64 { 301 if out_cap < 10 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 302 tls_write_u16_be(out, 0, EXT_SUPPORTED_GROUPS) 303 tls_write_u16_be(out, 2, 6) // ext_data_len = 2 (list_len) + 4 (groups) 304 tls_write_u16_be(out, 4, 4) // list_len = 4 bytes (2 entries * 2) 305 tls_write_u16_be(out, 6, NG_X25519) // 0x001d 306 tls_write_u16_be(out, 8, NG_SECP256R1) // 0x0017 307 // Wire bytes: type(2) + ext_data_len(2) + list_len(2) + 2 groups * 2 = 10. 308 // Earlier this function returned 12 (off-by-2) which threw off forward 309 // iteration in tls13_ext_find; see nx_tls13_hello_test assertion #82. 310 return 10 311} 312 313// ---- signature_algorithms (extension 13) ---- 314// 315// Wire: 316// ext_type(2)=0x000d + ext_len(2) 317// + schemes_list_len(2) 318// + scheme(2) repeated 319// 320// We ship 4 schemes: ed25519, ecdsa_secp256r1_sha256, rsa_pss_rsae_sha256, 321// rsa_pss_rsae_sha384 = 8 bytes of schemes. 322// Total: 4 + 2 + 8 = 14 bytes. 323func tls13_ext_emit_signature_algorithms(out: *u8, out_cap: i64) -> i64 { 324 // The 4 we can verify for the handshake CertificateVerify (ECDSA P-256, RSA-PSS 256/384, ed25519) PLUS the 325 // legacy rsa_pkcs1_* algs MANY cert chains/intermediates are signed with. TLS 1.3 forbids rsa_pkcs1 for the 326 // handshake signature, so a compliant server only uses them for the CERT CHAIN (which our X.509 pipeline 327 // validates with standard RSA) -- additive, can't be mis-selected for CertVerify, can't regress the working 328 // Wikipedia handshake. Fixes -4 failures where the server couldn't present a chain our narrow list accepted, 329 // and broadens our ClientHello toward a real browser's set. 330 if out_cap < 20 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 331 tls_write_u16_be(out, 0, EXT_SIGNATURE_ALGORITHMS) 332 tls_write_u16_be(out, 2, 16) // ext_data_len = 2 (list_len) + 14 (7 entries) 333 tls_write_u16_be(out, 4, 14) // list_len = 7 entries * 2 334 tls_write_u16_be(out, 6, SS_ECDSA_SECP256R1_SHA256) // 0x0403 (handshake + cert) 335 tls_write_u16_be(out, 8, SS_RSA_PSS_RSAE_SHA256) // 0x0804 (handshake + cert) 336 tls_write_u16_be(out, 10, SS_RSA_PSS_RSAE_SHA384) // 0x0805 (handshake + cert) 337 tls_write_u16_be(out, 12, SS_ED25519) // 0x0807 (handshake + cert) 338 tls_write_u16_be(out, 14, 0x0401) // rsa_pkcs1_sha256 (CERT CHAIN only) 339 tls_write_u16_be(out, 16, 0x0501) // rsa_pkcs1_sha384 (CERT CHAIN only) 340 tls_write_u16_be(out, 18, 0x0601) // rsa_pkcs1_sha512 (CERT CHAIN only) 341 return 20 342} 343 344// ---- key_share client (extension 51, X25519 only) ---- 345// 346// Wire: 347// ext_type(2)=0x0033 + ext_len(2) 348// + client_shares_len(2) 349// + group(2)=0x001d + key_exchange_len(2)=0x0020 + pubkey(32) 350// 351// Total: 4 + 2 + 4 + 32 = 42 bytes... actually let me recount. 352// ext_type(2) + ext_len(2) + client_shares_len(2) + group(2) + ke_len(2) + ke(32) = 42 bytes. 353// ext_data = client_shares_len(2) + group(2) + ke_len(2) + ke(32) = 38 bytes. 354// client_shares = group(2) + ke_len(2) + ke(32) = 36 bytes. 355func tls13_ext_emit_key_share_x25519( 356 pubkey32: *u8, 357 out: *u8, out_cap: i64 358) -> i64 { 359 if out_cap < 42 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 360 tls_write_u16_be(out, 0, EXT_KEY_SHARE) 361 tls_write_u16_be(out, 2, 38) // ext_data_len 362 tls_write_u16_be(out, 4, 36) // client_shares_len 363 tls_write_u16_be(out, 6, NG_X25519) // group = 0x001d 364 tls_write_u16_be(out, 8, 32) // key_exchange length 365 var i: i64 = 0 366 while i < 32 { 367 out[10 + i] = pubkey32[i] 368 i = i + 1 369 } 370 return 42 371} 372 373// ---- key_share client DUAL (extension 51, X25519 + secp256r1) ---- 374// 375// What real browsers send: one KeyShareEntry per group the client 376// can actually complete, so a server preferring EITHER group picks 377// its share directly and no HelloRetryRequest round-trip is needed 378// (rung B4-P256-KEYSHARE; unblocks the B2 httpbin CH-alert class). 379// 380// Wire: 381// ext_type(2)=0x0033 + ext_len(2)=107 382// + client_shares_len(2)=105 383// + group(2)=0x001d + ke_len(2)=32 + pubkey(32) (X25519, preferred) 384// + group(2)=0x0017 + ke_len(2)=65 + pubkey(65) (secp256r1, 0x04||X||Y) 385// 386// Total: 4 + 2 + 36 + 69 = 111 bytes. 387func tls13_ext_emit_key_share_dual( 388 x25519_pub32: *u8, 389 p256_pub65: *u8, 390 out: *u8, out_cap: i64 391) -> i64 { 392 if out_cap < 111 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 393 tls_write_u16_be(out, 0, EXT_KEY_SHARE) 394 tls_write_u16_be(out, 2, 107) // ext_data_len 395 tls_write_u16_be(out, 4, 105) // client_shares_len 396 tls_write_u16_be(out, 6, NG_X25519) // entry 1: group 0x001d 397 tls_write_u16_be(out, 8, 32) 398 var i: i64 = 0 399 while i < 32 { 400 out[10 + i] = x25519_pub32[i] 401 i = i + 1 402 } 403 tls_write_u16_be(out, 42, NG_SECP256R1) // entry 2: group 0x0017 404 tls_write_u16_be(out, 44, 65) 405 i = 0 406 while i < 65 { 407 out[46 + i] = p256_pub65[i] 408 i = i + 1 409 } 410 return 111 411} 412 413// ---- key_share client P-256 ONLY (extension 51, secp256r1) ---- 414// 415// Census/probe instrument (and future HRR building block): forces 416// the server to complete on secp256r1 or refuse, which is how the 417// B2 row live-proves the P-256 path against an independent peer 418// (servers offered x25519 always prefer it, so the dual share never 419// exercises P-256 live). 420// 421// Wire: ext_type(2)=0x0033 + ext_len(2)=71 + shares_len(2)=69 422// + group(2)=0x0017 + ke_len(2)=65 + pubkey(65) = 75 bytes. 423func tls13_ext_emit_key_share_p256( 424 p256_pub65: *u8, 425 out: *u8, out_cap: i64 426) -> i64 { 427 if out_cap < 75 { return 0 - NX_TLS13_EXT_VERDICT_BUF_OVERFLOW } 428 tls_write_u16_be(out, 0, EXT_KEY_SHARE) 429 tls_write_u16_be(out, 2, 71) // ext_data_len 430 tls_write_u16_be(out, 4, 69) // client_shares_len 431 tls_write_u16_be(out, 6, NG_SECP256R1) // group = 0x0017 432 tls_write_u16_be(out, 8, 65) 433 var i: i64 = 0 434 while i < 65 { 435 out[10 + i] = p256_pub65[i] 436 i = i + 1 437 } 438 return 75 439} 440 441// ---- Parse supported_versions in ServerHello (server-variant). ---- 442// 443// Per RFC 8446 §4.2.1: in ServerHello the supported_versions 444// extension data is just a single ProtocolVersion (no list wrapper). 445// 446// Inputs: pointer to the ext_data bytes (i.e. PAST the ext_type + 447// ext_len pair) and its length. Returns the selected_version, or 448// negative NX_TLS13_EXT_VERDICT_* on error. 449func tls13_ext_parse_supported_versions_server( 450 ext_data: *u8, ext_data_len: i64 451) -> i64 { 452 if ext_data_len != 2 { return 0 - NX_TLS13_EXT_VERDICT_BAD_FORMAT } 453 return tls_read_u16_be(ext_data, 0) 454} 455 456// ---- Parse key_share in ServerHello (single KeyShareEntry). ---- 457// 458// Per RFC 8446 §4.2.8: in ServerHello the key_share extension data 459// is a single KeyShareEntry (group + key_exchange). 460// 461// Inputs: pointer to ext_data + length. Outputs: selected group + 462// pointer to key_exchange bytes within ext_data + length of those 463// bytes. Returns OK or negative verdict. 464func tls13_ext_parse_key_share_server( 465 ext_data: *u8, ext_data_len: i64, 466 out_group: *i64, 467 out_ke_off: *i64, 468 out_ke_len: *i64 469) -> i64 { 470 if ext_data_len < 4 { return 0 - NX_TLS13_EXT_VERDICT_TRUNCATED } 471 let group: i64 = tls_read_u16_be(ext_data, 0) 472 let ke_len: i64 = tls_read_u16_be(ext_data, 2) 473 if 4 + ke_len > ext_data_len { return 0 - NX_TLS13_EXT_VERDICT_TRUNCATED } 474 *out_group = group 475 *out_ke_off = 4 476 *out_ke_len = ke_len 477 return NX_TLS13_EXT_VERDICT_OK 478} 479 480// Sealed-enum validity gate. 481func nx_tls13_ext_verdict_is_valid(v: i64) -> i64 { 482 if v < 0 { return 0 } 483 if v >= NX_TLS13_EXT_VERDICT_N { return 0 } 484 return 1 485}