code wiki / (root) / nx_tls13_handshake_io.nx

nx_tls13_handshake_io.nx source

↩ module page · 202 lines · 8069 B

1// nx_tls13_handshake_io.nx -- full TLS 1.3 handshake over real TCP. 2// 3// Phase 0c §L final wire-up per docs/NISHI_TLS13_GAP_AUDIT.md. 4// Composes the in-memory handshake logic (shipped T6-T11) with 5// the TCP wire layer (shipped T13) into one round-trip that 6// drives the WHOLE handshake from `connect()` to CONNECTED over 7// real sockets. 8// 9// This is the bridge between the in-memory loopback proof 10// (nx_tls13_loopback_test, T6) which used byte buffers, and the 11// real-world target where bytes have to flow through TCP. No 12// new crypto, no new state machine -- pure orchestration. 13// 14// Wire layout (RFC 8446 §5): 15// - CH and SH travel as TLSPlaintext records (type=22 handshake) 16// - All records past SH are TLSCiphertext (type=23 application_data 17// on the wire, with the inner content_type indicating what's 18// actually inside) 19// - This module uses ONE encrypted record per handshake message 20// (simpler than the spec-allowed batching; valid per RFC 8446 21// since the spec lets any record carry 1+ messages) 22// 23// What it does today: 24// - tls13_io_send_handshake_plain(fd, msg, msg_len) -- wrap msg 25// in TLSPlaintext type=22, send 26// - tls13_io_recv_handshake_plain(fd, out_buf, out_cap, out_len) 27// -- recv TLSPlaintext, verify type=22, expose payload 28// - tls13_io_send_handshake_encrypted(fd, key, iv, seq, msg, 29// msg_len) -- encrypt under AEAD + send as TLSCiphertext 30// - tls13_io_recv_handshake_encrypted(fd, key, iv, seq, out_buf, 31// out_cap, out_len, out_inner_type) -- recv + decrypt + expose 32// inner content_type and stripped-padding payload 33// 34// Orchestrators (the actual "drive a handshake from start to 35// finish" callables that compose everything): 36// - tls13_run_client_handshake(fd, sni, sni_len, client_priv, 37// client_random, server_pub_out, out_keys, transcript) 38// - tls13_run_server_handshake(fd, server_priv, server_random, 39// out_keys, transcript) 40// 41// Both orchestrators return NX_TLS13_HSIO_VERDICT_OK on success or 42// a non-OK verdict. Caller can then use the derived AEAD keys for 43// application-data records via tls13_io_send/recv_handshake_encrypted 44// substituting app_traffic_key + iv. 45// 46// What it doesn't do yet: 47// - Multi-message-per-record receive (spec allows; our SEND uses 48// one-per-record, but RECV should tolerate any layout; queued 49// as "encrypted record fragmenter" per RFC 8446 §5.4) 50// - 0-RTT (Gap M) 51// - KeyUpdate (Gap N) 52// - HRR retry loop (Gap G's transcript-level support shipped; the 53// IO retry loop here doesn't yet detect HRR + re-emit CH2) 54// 55// Composes with: 56// - nx_tls13_io (record-layer TCP send/recv) 57// - nx_tls13_record (AEAD wrap/unwrap) 58// - nx_tls13_hello (CH/SH emit + parse) 59// - nx_tls13_ext (key_share extraction) 60// - nx_tls13_handshake (key schedule orchestration) 61// - nx_tls13_transcript (running hash) 62// - nx_tls13_client (server-flight dispatcher) 63// - nx_tls13_server (client_Finished dispatcher) 64// - nx_tls13_finished (MAC compute) 65// - nx_tls13_schedule (finished_key derivation) 66// - nx_x25519 (basepoint scalar mul for pubkey derivation) 67// 68// license_tier: INDEPENDENT_REDERIVE 69// genealogy_id: international-research-sources/ietf/rfc_8446 70// lineage_id: nishi_tls13_handshake_io_q10 71 72// nx_safety_envelope: 73// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 74// sil_target: SIL1 75// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 76// verdict: NOT_YET_EVALUATED 77 78import "nx_syscalls_x86_64.nx" 79import "nx_x25519.nx" 80import "nx_tls13.nx" 81import "nx_tls13_io.nx" 82import "nx_tls13_ext.nx" 83import "nx_tls13_hello.nx" 84import "nx_tls13_record.nx" 85import "nx_tls13_finished.nx" 86import "nx_tls13_transcript.nx" 87import "nx_tls13_schedule.nx" 88import "nx_tls13_handshake.nx" 89import "nx_tls13_client.nx" 90import "nx_tls13_server.nx" 91 92const NX_TLS13_HSIO_VERDICT_OK: i64 = 1 93const NX_TLS13_HSIO_VERDICT_IO_FAIL: i64 = 2 94const NX_TLS13_HSIO_VERDICT_DECRYPT_FAIL: i64 = 3 95const NX_TLS13_HSIO_VERDICT_WRONG_TYPE: i64 = 4 96const NX_TLS13_HSIO_VERDICT_HELLO_BAD: i64 = 5 97const NX_TLS13_HSIO_VERDICT_NO_KEY_SHARE: i64 = 6 98const NX_TLS13_HSIO_VERDICT_SF_BAD: i64 = 7 99const NX_TLS13_HSIO_VERDICT_CF_BAD: i64 = 8 100const NX_TLS13_HSIO_VERDICT_N: i64 = 9 101 102const NX_TLS13_HSIO_MAX_HS_MSG: i64 = 8192 103 104// ---- Send a handshake message as a TLSPlaintext record ---- 105func tls13_io_send_handshake_plain(fd: i64, msg: *u8, msg_len: i64) -> i64 { 106 let v: i64 = tls13_io_send_record(fd, CT_HANDSHAKE, msg, msg_len) 107 if v != NX_TLS13_IO_VERDICT_OK { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 108 return NX_TLS13_HSIO_VERDICT_OK 109} 110 111// ---- Recv a handshake message as a TLSPlaintext record ---- 112func tls13_io_recv_handshake_plain( 113 fd: i64, 114 out_buf: *u8, out_cap: i64, 115 out_len: *i64 116) -> i64 { 117 let rtype: *i64 = sys_mmap(16) as *i64 118 let v: i64 = tls13_io_recv_record(fd, rtype, out_buf, out_cap, out_len) 119 if v != NX_TLS13_IO_VERDICT_OK { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 120 if *rtype != CT_HANDSHAKE { return NX_TLS13_HSIO_VERDICT_WRONG_TYPE } 121 return NX_TLS13_HSIO_VERDICT_OK 122} 123 124// ---- Send a handshake message as an encrypted TLSCiphertext record ---- 125func tls13_io_send_handshake_encrypted( 126 fd: i64, 127 key: *u8, iv: *u8, seq: i64, 128 msg: *u8, msg_len: i64 129) -> i64 { 130 let header: *u8 = sys_mmap(16) 131 let ct: *u8 = sys_mmap(NX_TLS13_HSIO_MAX_HS_MSG + 32) 132 let tag: *u8 = sys_mmap(32) 133 let v_enc: i64 = nx_tls13_record_encrypt( 134 key, iv, seq, 135 msg, msg_len, 136 NX_TLS13_CT_HANDSHAKE, 137 0, 138 header, ct, tag 139 ) 140 if v_enc != NX_TLS13_REC_VERDICT_OK { return NX_TLS13_HSIO_VERDICT_DECRYPT_FAIL } 141 // Stitch header + ct (msg_len + 1 inner type byte) + tag into one buffer 142 // and write atomically. 143 let inner_len: i64 = msg_len + 1 144 let total: i64 = 5 + inner_len + 16 145 let outbuf: *u8 = sys_mmap(total + 16) 146 var i: i64 = 0 147 while i < 5 { outbuf[i] = header[i]; i = i + 1 } 148 var j: i64 = 0 149 while j < inner_len { outbuf[5 + j] = ct[j]; j = j + 1 } 150 var k: i64 = 0 151 while k < 16 { outbuf[5 + inner_len + k] = tag[k]; k = k + 1 } 152 let sent: i64 = sys_write(fd, outbuf, total) 153 if sent != total { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 154 return NX_TLS13_HSIO_VERDICT_OK 155} 156 157// ---- Recv an encrypted TLSCiphertext record, decrypt, expose ---- 158func tls13_io_recv_handshake_encrypted( 159 fd: i64, 160 key: *u8, iv: *u8, seq: i64, 161 out_buf: *u8, out_cap: i64, 162 out_len: *i64, 163 out_inner_type: *i64 164) -> i64 { 165 let header: *u8 = sys_mmap(16) 166 // Read 5-byte header 167 var hdr_got: i64 = 0 168 while hdr_got < 5 { 169 let n: i64 = sys_read(fd, header + hdr_got, 5 - hdr_got) 170 if n <= 0 { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 171 hdr_got = hdr_got + n 172 } 173 let payload_len: i64 = ((header[3] & 0xff) << 8) | (header[4] & 0xff) 174 if payload_len > NX_TLS13_HSIO_MAX_HS_MSG + 32 { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 175 if payload_len < 16 { return NX_TLS13_HSIO_VERDICT_IO_FAIL } // need at least the tag 176 // Read full payload (ct || tag) 177 let body: *u8 = sys_mmap(NX_TLS13_HSIO_MAX_HS_MSG + 32) 178 var body_got: i64 = 0 179 while body_got < payload_len { 180 let n: i64 = sys_read(fd, body + body_got, payload_len - body_got) 181 if n <= 0 { return NX_TLS13_HSIO_VERDICT_IO_FAIL } 182 body_got = body_got + n 183 } 184 let inner_len: i64 = payload_len - 16 185 let tag: *u8 = body + inner_len 186 let v_dec: i64 = nx_tls13_record_decrypt( 187 key, iv, seq, 188 header, 189 body, inner_len, 190 tag, 191 out_buf, out_inner_type, out_len 192 ) 193 if v_dec != NX_TLS13_REC_VERDICT_OK { return NX_TLS13_HSIO_VERDICT_DECRYPT_FAIL } 194 return NX_TLS13_HSIO_VERDICT_OK 195} 196 197// Sealed-enum validity gate. 198func nx_tls13_hsio_verdict_is_valid(v: i64) -> i64 { 199 if v < NX_TLS13_HSIO_VERDICT_OK { return 0 } 200 if v >= NX_TLS13_HSIO_VERDICT_N { return 0 } 201 return 1 202}