code wiki / (root) / nx_tls12_client_session.nx

nx_tls12_client_session.nx source

↩ module page · 603 lines · 29472 B

1// nx_tls12_client_session.nx -- PHASE 2 of the sovereign TLS 1.2 client: 2// the HANDSHAKE STATE MACHINE that completes a full TLS 1.2 handshake 3// (ECDHE-RSA-AES128-GCM-SHA256) and establishes an encrypted session. 4// 5// WHY: api.porkbun.com (and every Porkbun endpoint) negotiates TLS 1.2 6// MAXIMUM; our nx_tls13_* client is 1.3-only and gets a protocol_version 7// alert. This organ drives the 1.2 handshake to CONNECTED so the Porkbun 8// API (nx_porkbun_set_txt -> ACME wildcard renew) works. Phase 3 wires 9// this into nx_acme_http + the live Porkbun e2e. 10// 11// This is ORCHESTRATION of already-shipped, already-gated primitives 12// (verify-don't-rebuild) -- no new crypto: 13// tls12_client_hello_emit (nx_tls12_hello.nx) flight 1 14// tls12_parse_server_hello (nx_tls12_parse.nx) SH 15// tls12_parse_server_key_exchange(nx_tls12_parse.nx) SKE 16// nx_tls13_client_validate_certificate (the SAME cert-chain cert 17// bridge the 1.3 client uses) -- fed a 1.2->1.3 cert-msg 18// transform built here so the whole RFC-5280 path-validation 19// pipeline + Mozilla trust store is reused unchanged. 20// nx_x509_pubkey_extract_rsa (nx_x509_pubkey_rsa.nx) leaf key 21// nx_tls12_ske_verify (nx_tls12_ske_verify.nx) SKE sig 22// p256_ecdh_derive_priv/pub/shared (nx_p256_ecdh.nx) ECDHE 23// tls12_derive_master_secret / _key_block / _split_keys / 24// _finished_verify_data (nx_tls12_keys.nx) schedule 25// tls12_record_seal / _open (nx_tls12_record.nx) AEAD 26// sha256_digest (nx_sha256.nx) transcript 27// nx_tls13_read_record_from_fd (read framer, reused) IO 28// 29// Pipeline (RFC 5246 §7.3, full ECDHE-RSA handshake): 30// 1. Send ClientHello; start a running SHA-256 transcript over every 31// handshake message (4-byte type+len header + body, NO 5-byte 32// record header), in send/recv order. 33// 2. Recv+reassemble ServerHello / Certificate / ServerKeyExchange / 34// ServerHelloDone; append each to the transcript. 35// 3. VALIDATE the certificate chain to the trust store + SNI host 36// (reused bridge). FAIL CLOSED. 37// 4. VERIFY the ServerKeyExchange RSA signature under the leaf key 38// over client_random||server_random||ECDHE_params. FAIL CLOSED. 39// 5. ECDHE: derive client ephemeral P-256 key; pre_master = ECDH x. 40// 6. master = derive_master_secret; key_block; split to 41// c_key/s_key/c_salt/s_salt. 42// 7. Send ClientKeyExchange (msg type 16, 1-byte-len client point); 43// append to transcript. Send ChangeCipherSpec (record type 20 -- 44// NOT a handshake msg, NOT in the transcript). 45// 8. client Finished = finished_verify_data(master,"client finished", 46// SHA256(transcript)); msg type 20 (0x14)+12-byte body; ENCRYPT 47// via tls12_record_seal (client key/salt, seq 0, ctype 22); send. 48// Then append the PLAINTEXT Finished to the transcript. 49// 9. Recv server ChangeCipherSpec (type 20) then the server's 50// encrypted Finished (type 22): tls12_record_open (server key/salt, 51// seq 0), constant-time-VERIFY verify_data == finished_verify_data( 52// master,"server finished",SHA256(transcript incl client Finished)). 53// FAIL CLOSED on mismatch. 54// 10. CONNECTED: app-data send/recv with independent client/server 55// sequence counters (start at 1 -- Finished consumed seq 0). 56// 57// SECURITY (this is a real handshake -- forged-accept = MITM): 58// Cert chain validation, SKE signature verify, and BOTH Finished MACs 59// are MANDATORY and FAIL CLOSED -- no session is established if any 60// check fails. The Finished verify_data and the GCM tag (the record 61// layer) are compared in constant time. 62// 63// license_tier: INDEPENDENT_REDERIVE 64// genealogy_id: international-research-sources/ietf/rfc_5246 + rfc_5280 + rfc_8422 65// lineage_id: nishi_tls12_client_session_q2 66// 67// nx_safety_envelope: 68// intended_use: "sovereign TLS 1.2 client handshake to CONNECTED + 69// encrypted app-data session (server authentication)" 70// sil_target: SIL3 (authentication; a forged-accept = MITM) 71// evidence: [composed_from_gated_phase1_primitives, 72// live_handshake_api_porkbun_com_gate, 73// neg_control_empty_trust_store_REJECTED, 74// neg_control_tampered_ske_sig_REJECTED, 75// neg_control_tampered_server_finished_REJECTED] 76// verdict: NOT_YET_EVALUATED 77 78import "nx_syscalls.nx" 79import "nx_sha256.nx" 80import "nx_u2048.nx" 81import "nx_u256.nx" // u256_alloc -> EC P-256 pubkey limbs for the ECDHE-ECDSA branch 82import "nx_x509.nx" 83import "nx_x509_pubkey_rsa.nx" 84import "nx_x509_pubkey_ec.nx" // nx_x509_pubkey_extract_ec_p256 (leaf EC key for ECDHE-ECDSA) 85import "nx_tls12_hello.nx" 86import "nx_tls12_parse.nx" 87import "nx_tls12_keys.nx" 88import "nx_tls12_record.nx" 89import "nx_tls12_ske_verify.nx" 90import "nx_p256_ecdh.nx" 91import "nx_tls13_client_validate_certificate.nx" 92import "nx_tls13_read_record_from_fd.nx" 93const NX_MAGIC_65536: i64 = 65536 94const NX_MAGIC_2048: i64 = 2048 95const NX_MAGIC_131072: i64 = 131072 96const NX_MAGIC_32768: i64 = 32768 97const NX_MAGIC_1024: i64 = 1024 98 99// ---- Sealed verdict enum (the NEGATIVE codes returned by run) -------- 100const NX_TLS12_SESS_OK: i64 = 1 101const NX_TLS12_SESS_EMIT_CH: i64 = 2 // ClientHello emit failed 102const NX_TLS12_SESS_WRITE_FAIL: i64 = 3 // sys_write failed 103const NX_TLS12_SESS_READ_FLIGHT: i64 = 4 // could not read server flight 1 104const NX_TLS12_SESS_ALERT: i64 = 5 // server sent a TLS alert 105const NX_TLS12_SESS_UNEXPECTED: i64 = 6 // unexpected record content-type 106const NX_TLS12_SESS_INCOMPLETE_FLIGHT: i64 = 7 // missing SH/Cert/SKE/SHD 107const NX_TLS12_SESS_UNSUPPORTED_CIPHER: i64 = 8 // server picked a suite we don't complete 108const NX_TLS12_SESS_CERT_FAIL: i64 = 9 // chain/host/trust validation failed 109const NX_TLS12_SESS_LEAF_PARSE: i64 = 10 // leaf DER parse failed 110const NX_TLS12_SESS_LEAF_PUBKEY: i64 = 11 // leaf RSA pubkey extract failed 111const NX_TLS12_SESS_SKE_PARSE: i64 = 12 // SKE wire parse failed 112const NX_TLS12_SESS_SKE_VERIFY: i64 = 13 // SKE RSA signature verify failed 113const NX_TLS12_SESS_ECDH: i64 = 14 // ECDHE key-agreement failed 114const NX_TLS12_SESS_READ_SF: i64 = 15 // could not read server flight 2 115const NX_TLS12_SESS_SF_OPEN: i64 = 16 // server Finished record GCM open failed 116const NX_TLS12_SESS_SF_FORMAT: i64 = 17 // server Finished plaintext malformed 117const NX_TLS12_SESS_SF_VERIFY: i64 = 18 // server Finished verify_data mismatch 118const NX_TLS12_SESS_CERT_MSG_BAD: i64 = 19 // 1.2 Certificate body malformed 119// app-data verdicts 120const NX_TLS12_SESS_BAD_STATE: i64 = 20 // session not CONNECTED 121const NX_TLS12_SESS_EOF: i64 = 21 // peer closed the TCP connection 122const NX_TLS12_SESS_READ_FAIL: i64 = 22 // record read error 123const NX_TLS12_SESS_DECRYPT_FAIL: i64 = 23 // record AEAD open failed 124const NX_TLS12_SESS_OVERFLOW: i64 = 24 // record exceeds caller buffer 125const NX_TLS12_SESS_VERDICT_N: i64 = 25 126 127func nx_tls12_sess_verdict_is_valid(v: i64) -> i64 { 128 if v < NX_TLS12_SESS_OK { return 0 } 129 if v >= NX_TLS12_SESS_VERDICT_N { return 0 } 130 return 1 131} 132 133// The only session state we expose: a fully-established connection. 134const NX_TLS12_SESS_STATE_CONNECTED: i64 = 1 135 136// The cipher suites this handshake completes end-to-end (both = P-256 ECDHE + AES-128-GCM + SHA-256 PRF; 137// they differ ONLY in the leaf-cert key type used to verify the ServerKeyExchange signature: RSA vs ECDSA). 138const NX_TLS12_SUITE_ECDHE_RSA_AES128_GCM_SHA256: i64 = 0xC02F 139const NX_TLS12_SUITE_ECDHE_ECDSA_AES128_GCM_SHA256: i64 = 0xC02B 140 141// Read budgets (defensive: an evil server can't feed us forever). 142const NX_TLS12_SESS_MAX_FLIGHT_RECORDS: i64 = 24 143const NX_TLS12_SESS_MAX_SF_RECORDS: i64 = 8 144const NX_TLS12_SESS_RECORD_BUF: i64 = 20000 145 146// A connected TLS 1.2 client session (mirrors Tls13ClientSession shape). 147struct Tls12ClientSession { 148 fd: i64, 149 cipher_suite: i64, 150 state: i64, 151 client_key: *u8, // 16-byte AES-128 client write key 152 server_key: *u8, // 16-byte AES-128 server write key 153 client_salt: *u8, // 4-byte client write IV (GCM salt) 154 server_salt: *u8, // 4-byte server write IV (GCM salt) 155 client_seq: i64, // next record sequence we will SEND (app data starts at 1) 156 server_seq: i64, // next record sequence we will RECEIVE 157 master: *u8, // 48-byte master secret (kept for renegotiation/debug) 158} 159 160// ---- small helpers --------------------------------------------------- 161 162static _T12S_PN_BUF: i64 163func _t12s_pn2(c0: i64, c1: i64, v: i64) -> i64 { 164 let lab: *u8 = sys_mmap(8) 165 lab[0] = c0 as u8; lab[1] = c1 as u8; lab[2] = 0x3D as u8 166 sys_write(2, lab, 3) 167 if _T12S_PN_BUF == 0 { _T12S_PN_BUF = sys_mmap(32) as i64 } 168 let b: *u8 = _T12S_PN_BUF as *u8 169 var m: i64 = v 170 if m < 0 { sys_write(2, "-" as *u8, 1); m = 0 - m } 171 var i: i64 = 22 172 if m == 0 { b[i] = 0x30 as u8; i = i - 1 } 173 else { while m > 0 { b[i] = (0x30 + (m - (m/10)*10)) as u8; m = m / 10; i = i - 1 } } 174 sys_write(2, ((b as i64) + i + 1) as *u8, 22 - i) 175 sys_write(2, "\n" as *u8, 1) 176 return 0 177} 178 179// Write exactly n bytes to fd; 0 on success, -1 on any sys_write error. 180func _t12s_write_n(fd: i64, buf: *u8, n: i64) -> i64 { 181 var off: i64 = 0 182 while off < n { 183 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off) 184 if w <= 0 { return 0 - 1 } 185 off = off + w 186 } 187 return 0 188} 189 190// Frame [ctype][0x03 0x03][len BE 2][payload] and write it. 0 ok / -1 fail. 191func _t12s_send_record(fd: i64, ctype: i64, payload: *u8, plen: i64) -> i64 { 192 let rec: *u8 = sys_mmap(plen + 16) 193 rec[0] = ctype as u8 194 rec[1] = 0x03 as u8 195 rec[2] = 0x03 as u8 196 rec[3] = ((plen >> 8) & 0xff) as u8 197 rec[4] = (plen & 0xff) as u8 198 var i: i64 = 0 199 while i < plen { rec[5 + i] = payload[i]; i = i + 1 } 200 return _t12s_write_n(fd, rec, 5 + plen) 201} 202 203// 64-bit sequence number -> 8-byte big-endian explicit nonce / AAD field. 204func _t12s_seq8(seq: i64, out8: *u8) -> i64 { 205 var i: i64 = 0 206 while i < 8 { 207 out8[7 - i] = ((seq >> (i * 8)) & 0xff) as u8 208 i = i + 1 209 } 210 return 0 211} 212 213// Constant-time equality over n bytes: 1 iff equal, 0 otherwise. 214// (No early exit -- timing must not leak how many bytes matched.) 215func _t12s_ct_eq(a: *u8, b: *u8, n: i64) -> i64 { 216 var diff: i64 = 0 217 var i: i64 = 0 218 while i < n { 219 diff = diff | ((a[i] as i64) ^ (b[i] as i64)) 220 i = i + 1 221 } 222 if diff == 0 { return 1 } 223 return 0 224} 225 226// Transform a TLS 1.2 Certificate-message BODY (RFC 5246 §7.4.2: 227// cert_list_len(3) || repeated[ cert_len(3) || DER ] ) 228// into a TLS 1.3 Certificate handshake MESSAGE (RFC 8446 §4.4.2: 229// type(1)=11 || body_len(3) || ctx_len(1)=0 || list_len(3) || 230// repeated[ cert_len(3) || DER || ext_len(2)=0 ] ) 231// so the SHIPPED 1.3 cert pipeline (tls13_parse_certificate_chain_all 232// + nx_https_cert_pipeline_verify_with_store) validates it unchanged. 233// Returns the 1.3 message length, or negative on malformed input. 234func _t12s_certmsg_to_tls13(cl: *u8, cl_len: i64, out: *u8, out_cap: i64) -> i64 { 235 if cl_len < 3 { return 0 - 1 } 236 let list_len: i64 = ((cl[0] as i64) << 16) | ((cl[1] as i64) << 8) | (cl[2] as i64) 237 if 3 + list_len > cl_len { return 0 - 1 } 238 239 var o: i64 = 8 // entries start after hdr(4)+ctx(1)+listlen(3) 240 var p: i64 = 3 // read cursor (past the 1.2 list length) 241 let end: i64 = 3 + list_len 242 while p < end { 243 if p + 3 > cl_len { return 0 - 1 } 244 let clen: i64 = ((cl[p] as i64) << 16) | ((cl[p+1] as i64) << 8) | (cl[p+2] as i64) 245 if clen < 1 { return 0 - 1 } 246 if p + 3 + clen > cl_len { return 0 - 1 } 247 if o + 3 + clen + 2 > out_cap { return 0 - 1 } 248 out[o] = cl[p] 249 out[o+1] = cl[p+1] 250 out[o+2] = cl[p+2] 251 o = o + 3 252 var i: i64 = 0 253 while i < clen { out[o + i] = cl[p + 3 + i]; i = i + 1 } 254 o = o + clen 255 out[o] = 0 as u8 // CertificateEntry extensions length = 0 256 out[o+1] = 0 as u8 257 o = o + 2 258 p = p + 3 + clen 259 } 260 261 let entries_len: i64 = o - 8 262 out[4] = 0 as u8 // certificate_request_context length 263 out[5] = ((entries_len >> 16) & 0xff) as u8 // 1.3 certificate_list length 264 out[6] = ((entries_len >> 8) & 0xff) as u8 265 out[7] = (entries_len & 0xff) as u8 266 let body_len: i64 = o - 4 267 out[0] = 11 as u8 // HandshakeType certificate(11) 268 out[1] = ((body_len >> 16) & 0xff) as u8 269 out[2] = ((body_len >> 8) & 0xff) as u8 270 out[3] = (body_len & 0xff) as u8 271 return o 272} 273 274// --------------------------------------------------------------------- 275// THE handshake orchestrator core. 276// 277// Returns POSITIVE pointer-as-i64 to the connected *Tls12ClientSession 278// on success; NEGATIVE -NX_TLS12_SESS_* verdict on any failure (every 279// security check FAILS CLOSED). 280// 281// fault_inject is TEST-ONLY (production callers pass 0 via the public 282// wrapper below). Non-zero deliberately corrupts ONE security-critical 283// value mid-handshake so a gate can prove the live path FAILS CLOSED: 284// 1 = flip a byte of the RECEIVED server Finished verify_data 285// 2 = flip a byte of the SKE signature before verification 286// --------------------------------------------------------------------- 287func _nx_tls12_run_core( 288 fd: i64, 289 sni: *u8, sni_len: i64, 290 client_random: *u8, // 32 caller-supplied random bytes 291 ephem_seed: *u8, // 32 bytes -> domain-separated P-256 ephemeral 292 val_ctx: *TlsValidationContext, 293 fault_inject: i64 294) -> i64 { 295 // Running handshake transcript (message headers + bodies, in order). 296 let tr: *u8 = sys_mmap(NX_MAGIC_65536) 297 var tr_len: i64 = 0 298 299 // ---- Step 1: emit + send ClientHello ---- 300 let ch: *u8 = sys_mmap(NX_MAGIC_2048) 301 let ch_n: i64 = tls12_client_hello_emit(client_random, sni, sni_len, ch, NX_MAGIC_2048) 302 if ch_n < 0 { return 0 - NX_TLS12_SESS_EMIT_CH } 303 var i: i64 = 0 304 while i < ch_n { tr[tr_len + i] = ch[i]; i = i + 1 } 305 tr_len = tr_len + ch_n 306 if _t12s_send_record(fd, 22, ch, ch_n) < 0 { return 0 - NX_TLS12_SESS_WRITE_FAIL } 307 308 // ---- Step 2: read + reassemble server flight 1 to ServerHelloDone ---- 309 let resp: *u8 = sys_mmap(NX_TLS12_SESS_RECORD_BUF) 310 let hs: *u8 = sys_mmap(NX_MAGIC_131072) 311 var hlen: i64 = 0 312 var hpos: i64 = 0 313 let server_random: *u8 = sys_mmap(64) 314 let cipher_p: *i64 = sys_mmap(16) as *i64 315 var got_sh: i64 = 0 316 var got_cert: i64 = 0 317 var got_skx: i64 = 0 318 var got_done: i64 = 0 319 var cert_body: *u8 = 0 as *u8 320 var cert_body_len: i64 = 0 321 var skx_body: *u8 = 0 as *u8 322 var skx_len: i64 = 0 323 var recs: i64 = 0 324 while got_done == 0 { 325 if recs >= NX_TLS12_SESS_MAX_FLIGHT_RECORDS { return 0 - NX_TLS12_SESS_READ_FLIGHT } 326 let rt: i64 = nx_tls13_read_record_from_fd(fd, resp, NX_TLS12_SESS_RECORD_BUF) 327 if rt < 5 { return 0 - NX_TLS12_SESS_READ_FLIGHT } 328 recs = recs + 1 329 let ct: i64 = resp[0] as i64 330 if ct == 21 { _t12s_pn2(0x41, 0x4C, resp[6] as i64); return 0 - NX_TLS12_SESS_ALERT } 331 if ct != 22 { return 0 - NX_TLS12_SESS_UNEXPECTED } 332 // accumulate handshake bytes 333 var bi: i64 = 5 334 while bi < rt { hs[hlen] = resp[bi]; hlen = hlen + 1; bi = bi + 1 } 335 // parse every complete handshake message currently buffered 336 var more: i64 = 1 337 while more == 1 { 338 if hpos + 4 > hlen { more = 0 } 339 else { 340 let mtype: i64 = hs[hpos] as i64 341 let mlen: i64 = ((hs[hpos+1] as i64) << 16) | ((hs[hpos+2] as i64) << 8) | (hs[hpos+3] as i64) 342 if hpos + 4 + mlen > hlen { more = 0 } 343 else { 344 let body: *u8 = (hs as i64 + hpos + 4) as *u8 345 // append the WHOLE message (header+body) to the transcript 346 var ci: i64 = 0 347 while ci < 4 + mlen { tr[tr_len] = hs[hpos + ci]; tr_len = tr_len + 1; ci = ci + 1 } 348 if mtype == 2 { if tls12_parse_server_hello(body, mlen, server_random, cipher_p) == 1 { got_sh = 1 } } 349 if mtype == 11 { cert_body = body; cert_body_len = mlen; got_cert = 1 } 350 if mtype == 12 { skx_body = body; skx_len = mlen; got_skx = 1 } 351 if mtype == 14 { got_done = 1 } 352 hpos = hpos + 4 + mlen 353 } 354 } 355 } 356 } 357 if got_sh == 0 { return 0 - NX_TLS12_SESS_INCOMPLETE_FLIGHT } 358 if got_cert == 0 { return 0 - NX_TLS12_SESS_INCOMPLETE_FLIGHT } 359 if got_skx == 0 { return 0 - NX_TLS12_SESS_INCOMPLETE_FLIGHT } 360 _t12s_pn2(0x43, 0x49, cipher_p[0]) // CI= negotiated cipher 361 var is_ecdsa: i64 = 0 362 if cipher_p[0] == NX_TLS12_SUITE_ECDHE_ECDSA_AES128_GCM_SHA256 { is_ecdsa = 1 } 363 if cipher_p[0] != NX_TLS12_SUITE_ECDHE_RSA_AES128_GCM_SHA256 { 364 if is_ecdsa == 0 { return 0 - NX_TLS12_SESS_UNSUPPORTED_CIPHER } 365 } 366 367 // ---- Step 3: VALIDATE the certificate chain (reused 1.3 bridge) ---- 368 // Transform the 1.2 cert-list into a 1.3 Certificate message and feed 369 // it to the SAME validator the 1.3 client uses (full RFC-5280 path 370 // validation + SNI host match + Mozilla trust anchor resolution). 371 let synth: *u8 = sys_mmap(NX_MAGIC_32768) 372 let synth_n: i64 = _t12s_certmsg_to_tls13(cert_body, cert_body_len, synth, NX_MAGIC_32768) 373 if synth_n < 0 { return 0 - NX_TLS12_SESS_CERT_MSG_BAD } 374 let cv: i64 = nx_tls13_client_validate_certificate(synth, synth_n, val_ctx) 375 _t12s_pn2(0x43, 0x56, cv) // CV= cert-validation verdict 376 if cv != NX_TLS13_CLIENT_CV_OK { return 0 - NX_TLS12_SESS_CERT_FAIL } 377 378 // ---- Step 4: VERIFY the ServerKeyExchange signature (leaf key: RSA or ECDSA) ---- 379 // Extract the leaf pubkey straight from the 1.2 cert-list (leaf is the first 380 // cert: skip list_len(3) + cert0_len(3)). The key TYPE follows the negotiated 381 // cipher (ECDHE-RSA -> RSA leaf; ECDHE-ECDSA -> EC P-256 leaf). 382 if cert_body_len < 6 { return 0 - NX_TLS12_SESS_CERT_MSG_BAD } 383 let leaf_len: i64 = ((cert_body[3] as i64) << 16) | ((cert_body[4] as i64) << 8) | (cert_body[5] as i64) 384 if 6 + leaf_len > cert_body_len { return 0 - NX_TLS12_SESS_CERT_MSG_BAD } 385 let leaf_der: *u8 = (cert_body as i64 + 6) as *u8 386 let leaf_cert: *X509Cert = sys_mmap(256) as *X509Cert 387 if x509_parse(leaf_der, leaf_len, leaf_cert) < 0 { return 0 - NX_TLS12_SESS_LEAF_PARSE } 388 389 // SKE wire parse (common to both key types): curve + server ephemeral point + sig_alg + signature. 390 let curve_p: *i64 = sys_mmap(16) as *i64 391 let spub: *u8 = sys_mmap(256) 392 let spublen_p: *i64 = sys_mmap(16) as *i64 393 let sigalg_p: *i64 = sys_mmap(16) as *i64 394 let sig: *u8 = sys_mmap(NX_MAGIC_1024) 395 let siglen_p: *i64 = sys_mmap(16) as *i64 396 if tls12_parse_server_key_exchange(skx_body, skx_len, curve_p, spub, spublen_p, sigalg_p, sig, siglen_p) != 1 { 397 return 0 - NX_TLS12_SESS_SKE_PARSE 398 } 399 // TEST-ONLY fault: corrupt the signature so verify MUST reject. 400 if fault_inject == 2 { sig[10] = (sig[10] ^ 0x01) as u8 } 401 // ECDHE_params = curve_type(1)+named_curve(2)+point_len(1)+point. 402 let params_len: i64 = 4 + spublen_p[0] 403 var skev: i64 = 0 404 if is_ecdsa == 1 { 405 // ECDHE-ECDSA: extract the leaf EC P-256 key, verify the DER ECDSA(SHA-256) SKE signature. 406 let ecx: *i64 = u256_alloc() 407 let ecy: *i64 = u256_alloc() 408 if nx_x509_pubkey_extract_ec_p256(leaf_der, leaf_cert, ecx, ecy) != NX_X509_PUBKEY_EC_OK { 409 return 0 - NX_TLS12_SESS_LEAF_PUBKEY 410 } 411 skev = nx_tls12_ske_verify_ecdsa(client_random, server_random, skx_body, params_len, sig, siglen_p[0], ecx, ecy) 412 } else { 413 // ECDHE-RSA: extract the leaf RSA key, verify the PKCS1-v1.5(SHA-256) SKE signature. 414 let pub_n: *i64 = u2048_alloc() 415 let pub_e_p: *i64 = sys_mmap(16) as *i64 416 if nx_x509_pubkey_extract_rsa(leaf_der, leaf_cert, pub_n, pub_e_p) != NX_X509_PUBKEY_RSA_OK { 417 return 0 - NX_TLS12_SESS_LEAF_PUBKEY 418 } 419 skev = nx_tls12_ske_verify(client_random, server_random, skx_body, params_len, sig, siglen_p[0], pub_n, pub_e_p[0]) 420 } 421 _t12s_pn2(0x53, 0x4B, skev) // SK= SKE verify verdict 422 if skev != NX_TLS12_SKE_OK { return 0 - NX_TLS12_SESS_SKE_VERIFY } 423 424 // ---- Step 5: ECDHE -- client ephemeral key + shared pre_master ---- 425 let cpriv: *u8 = sys_mmap(32) 426 if p256_ecdh_derive_priv(ephem_seed, cpriv) != NX_P256_ECDH_OK { return 0 - NX_TLS12_SESS_ECDH } 427 let pre_master: *u8 = sys_mmap(32) 428 if p256_ecdh_shared(cpriv, spub, spublen_p[0], pre_master) != NX_P256_ECDH_OK { return 0 - NX_TLS12_SESS_ECDH } 429 let cpub: *u8 = sys_mmap(65) 430 if p256_ecdh_pub(cpriv, cpub) != NX_P256_ECDH_OK { return 0 - NX_TLS12_SESS_ECDH } 431 432 // ---- Step 6: key schedule ---- 433 let master: *u8 = sys_mmap(48) 434 tls12_derive_master_secret(pre_master, 32, client_random, server_random, master) 435 let kb: *u8 = sys_mmap(64) 436 tls12_derive_key_block(master, server_random, client_random, kb, 40) 437 let c_key: *u8 = sys_mmap(16) 438 let s_key: *u8 = sys_mmap(16) 439 let c_salt: *u8 = sys_mmap(4) 440 let s_salt: *u8 = sys_mmap(4) 441 tls12_split_keys_aes128gcm(kb, c_key, s_key, c_salt, s_salt) 442 443 // ---- Step 7: ClientKeyExchange (msg 16) + ChangeCipherSpec (rec 20) ---- 444 let cke: *u8 = sys_mmap(80) 445 cke[0] = 0x10 as u8 // HandshakeType client_key_exchange 446 cke[1] = 0 as u8; cke[2] = 0 as u8; cke[3] = 66 as u8 // body length = 1 + 65 447 cke[4] = 65 as u8 // ECDH point length 448 i = 0 449 while i < 65 { cke[5 + i] = cpub[i]; i = i + 1 } 450 let cke_msg_len: i64 = 70 // 4 hdr + 1 + 65 451 i = 0 452 while i < cke_msg_len { tr[tr_len] = cke[i]; tr_len = tr_len + 1; i = i + 1 } 453 if _t12s_send_record(fd, 22, cke, cke_msg_len) < 0 { return 0 - NX_TLS12_SESS_WRITE_FAIL } 454 455 let ccs: *u8 = sys_mmap(8) 456 ccs[0] = 1 as u8 // ChangeCipherSpec message 457 if _t12s_send_record(fd, 20, ccs, 1) < 0 { return 0 - NX_TLS12_SESS_WRITE_FAIL } 458 459 // ---- Step 8: client Finished (encrypted, seq 0) ---- 460 let th: *u8 = sys_mmap(32) 461 sha256_digest(tr, tr_len, th) // SHA256(CH..CKE) 462 let cvd: *u8 = sys_mmap(16) 463 tls12_finished_verify_data(master, "client finished" as *u8, 15, th, cvd) 464 let fin: *u8 = sys_mmap(32) 465 fin[0] = 0x14 as u8 // HandshakeType finished 466 fin[1] = 0 as u8; fin[2] = 0 as u8; fin[3] = 12 as u8 467 i = 0 468 while i < 12 { fin[4 + i] = cvd[i]; i = i + 1 } 469 let fin_msg_len: i64 = 16 470 let seq0: *u8 = sys_mmap(8); _t12s_seq8(0, seq0) 471 let sealed: *u8 = sys_mmap(64) 472 let sealed_n: i64 = tls12_record_seal(c_key, c_salt, seq0, 22, fin, fin_msg_len, sealed) 473 if _t12s_send_record(fd, 22, sealed, sealed_n) < 0 { return 0 - NX_TLS12_SESS_WRITE_FAIL } 474 // add the PLAINTEXT Finished to the transcript (for the server Finished hash) 475 i = 0 476 while i < fin_msg_len { tr[tr_len] = fin[i]; tr_len = tr_len + 1; i = i + 1 } 477 478 // ---- Step 9: server ChangeCipherSpec + encrypted Finished ---- 479 var sf_done: i64 = 0 480 var rc2: i64 = 0 481 while sf_done == 0 { 482 if rc2 >= NX_TLS12_SESS_MAX_SF_RECORDS { return 0 - NX_TLS12_SESS_READ_SF } 483 let rt: i64 = nx_tls13_read_record_from_fd(fd, resp, NX_TLS12_SESS_RECORD_BUF) 484 if rt < 5 { return 0 - NX_TLS12_SESS_READ_SF } 485 rc2 = rc2 + 1 486 let ct: i64 = resp[0] as i64 487 if ct == 20 { 488 // ChangeCipherSpec -- not a handshake message, just advance. 489 } else { 490 if ct == 22 { 491 let payload: *u8 = (resp as i64 + 5) as *u8 492 let plen: i64 = rt - 5 493 let sfpt: *u8 = sys_mmap(64) 494 let sseq0: *u8 = sys_mmap(8); _t12s_seq8(0, sseq0) 495 let ptlen: i64 = tls12_record_open(s_key, s_salt, sseq0, 22, payload, plen, sfpt) 496 if ptlen < 0 { return 0 - NX_TLS12_SESS_SF_OPEN } 497 if ptlen != 16 { return 0 - NX_TLS12_SESS_SF_FORMAT } 498 if sfpt[0] != (0x14 as u8) { return 0 - NX_TLS12_SESS_SF_FORMAT } 499 let recv_vd: *u8 = (sfpt as i64 + 4) as *u8 500 // TEST-ONLY fault: corrupt the received verify_data. 501 if fault_inject == 1 { recv_vd[0] = (recv_vd[0] ^ 0x01) as u8 } 502 let th2: *u8 = sys_mmap(32) 503 sha256_digest(tr, tr_len, th2) // SHA256(CH..client Finished) 504 let svd: *u8 = sys_mmap(16) 505 tls12_finished_verify_data(master, "server finished" as *u8, 15, th2, svd) 506 if _t12s_ct_eq(recv_vd, svd, 12) != 1 { return 0 - NX_TLS12_SESS_SF_VERIFY } 507 sf_done = 1 508 } else { 509 if ct == 21 { return 0 - NX_TLS12_SESS_ALERT } 510 return 0 - NX_TLS12_SESS_UNEXPECTED 511 } 512 } 513 } 514 _t12s_pn2(0x46, 0x48, 1) // FH= both Finished verified 515 516 // ---- Step 10: session established ---- 517 let s: *Tls12ClientSession = sys_mmap(128) as *Tls12ClientSession 518 s.fd = fd 519 s.cipher_suite = cipher_p[0] 520 s.state = NX_TLS12_SESS_STATE_CONNECTED 521 s.client_key = c_key 522 s.server_key = s_key 523 s.client_salt = c_salt 524 s.server_salt = s_salt 525 s.client_seq = 1 // Finished used seq 0 526 s.server_seq = 1 527 s.master = master 528 return s as i64 529} 530 531// PUBLIC API (production): drive the handshake with NO fault injection. 532// Returns POSITIVE *Tls12ClientSession-as-i64 | NEGATIVE -NX_TLS12_SESS_*. 533func nx_tls12_client_session_run( 534 fd: i64, 535 sni: *u8, sni_len: i64, 536 client_random: *u8, 537 ephem_seed: *u8, 538 val_ctx: *TlsValidationContext 539) -> i64 { 540 return _nx_tls12_run_core(fd, sni, sni_len, client_random, ephem_seed, val_ctx, 0) 541} 542 543// TEST-ONLY variant: identical, but `fault` deliberately corrupts a 544// security-critical value so gates can prove the handshake FAILS CLOSED. 545// Production code MUST use nx_tls12_client_session_run (fault = 0). 546func nx_tls12_client_session_run_faulted( 547 fd: i64, 548 sni: *u8, sni_len: i64, 549 client_random: *u8, 550 ephem_seed: *u8, 551 val_ctx: *TlsValidationContext, 552 fault: i64 553) -> i64 { 554 return _nx_tls12_run_core(fd, sni, sni_len, client_random, ephem_seed, val_ctx, fault) 555} 556 557// ---- application-data over the established session -------------------- 558 559// Encrypt + send `data[0..len)` as ONE application_data record (type 23) 560// under the client write key, incrementing the client sequence. 0 ok / 561// NEGATIVE -NX_TLS12_SESS_* on failure. 562func nx_tls12_session_send(s: *Tls12ClientSession, fd: i64, data: *u8, len: i64) -> i64 { 563 if s.state != NX_TLS12_SESS_STATE_CONNECTED { return 0 - NX_TLS12_SESS_BAD_STATE } 564 let seq: *u8 = sys_mmap(8); _t12s_seq8(s.client_seq, seq) 565 let out: *u8 = sys_mmap(len + 64) 566 let n: i64 = tls12_record_seal(s.client_key, s.client_salt, seq, 23, data, len, out) 567 s.client_seq = s.client_seq + 1 568 if _t12s_send_record(fd, 23, out, n) < 0 { return 0 - NX_TLS12_SESS_WRITE_FAIL } 569 return 0 570} 571 572// Read + decrypt ONE record under the server write key, incrementing the 573// server sequence. On success returns the plaintext length into `out` 574// and writes the record content-type to out_ctype[0] (23 app-data, 21 575// alert, 22 handshake). NEGATIVE -NX_TLS12_SESS_EOF on TCP close, or 576// another -NX_TLS12_SESS_* verdict on error. 577func nx_tls12_session_recv(s: *Tls12ClientSession, fd: i64, out: *u8, out_cap: i64, out_ctype: *i64) -> i64 { 578 if s.state != NX_TLS12_SESS_STATE_CONNECTED { return 0 - NX_TLS12_SESS_BAD_STATE } 579 let resp: *u8 = sys_mmap(NX_TLS12_SESS_RECORD_BUF) 580 let rt: i64 = nx_tls13_read_record_from_fd(fd, resp, NX_TLS12_SESS_RECORD_BUF) 581 if rt < 0 { 582 let nv: i64 = 0 - rt 583 if nv == NX_TLS13_READ_REC_EOF { return 0 - NX_TLS12_SESS_EOF } 584 if nv == NX_TLS13_READ_REC_PAYLOAD_EOF { return 0 - NX_TLS12_SESS_EOF } 585 return 0 - NX_TLS12_SESS_READ_FAIL 586 } 587 if rt < 5 { return 0 - NX_TLS12_SESS_EOF } 588 let ct: i64 = resp[0] as i64 589 let payload: *u8 = (resp as i64 + 5) as *u8 590 let plen: i64 = rt - 5 591 if plen > out_cap { return 0 - NX_TLS12_SESS_OVERFLOW } 592 let seq: *u8 = sys_mmap(8); _t12s_seq8(s.server_seq, seq) 593 let ptlen: i64 = tls12_record_open(s.server_key, s.server_salt, seq, ct, payload, plen, out) 594 s.server_seq = s.server_seq + 1 595 if ptlen < 0 { return 0 - NX_TLS12_SESS_DECRYPT_FAIL } 596 out_ctype[0] = ct 597 return ptlen 598} 599 600// Compile-only smoke. Real proof in nx_tls12_handshake_gate.nx (LIVE). 601func main() -> i64 { 602 return 0 603}