code wiki / (root) / nx_tls13_server_session_run.nx

nx_tls13_server_session_run.nx source

↩ module page · 353 lines · 16325 B

1// nx_tls13_server_session_run.nx -- top-level TLS 1.3 server-side 2// handshake orchestrator. The server-side analogue to 3// nx_tls13_client_session_run. Composes the 10 shipped stones over 4// a connected TCP fd to drive a session from INIT to CONNECTED. 5// 6// Pipeline (client → server arrows + arrowed-out server emits): 7// <-- client ClientHello (TLSPlaintext record) 8// [stone 2] recv_ch INIT -> CH_RECEIVED 9// [stone 3] emit_sh + wrap as TLSPlaintext CH_RECEIVED -> SH_SENT 10// --> server ServerHello (TLSPlaintext) 11// [stone 4] derive_hs_secrets SH_SENT -> EE_SENT 12// [stone 5] derive_traffic (no state change) 13// [stone 6] emit_ee (already TLSCiphertext) EE_SENT -> CERT_SENT 14// --> ChangeCipherSpec compatibility record (skipped per RFC 8446 §D.4) 15// --> EncryptedExtensions 16// [stone 6.5] emit_certificate (state stays CERT_SENT) 17// --> Certificate 18// [stone 7.5] emit_cv CERT_SENT -> CV_SENT 19// --> CertificateVerify 20// [stone 7] emit_sf CV_SENT -> SF_SENT 21// --> ServerFinished 22// <-- client Finished (TLSCiphertext) 23// [stone 8] recv_cf SF_SENT -> CONNECTED 24// [derive_app] (already inside recv_cf per stone 8) 25// 26// Public API: 27// nx_tls13_server_session_run(fd, cert_der, cert_der_len, ecdsa_priv_be) 28// -> *Tls13ServerSession (positive cast to i64) on CONNECTED 29// -> negative -(NX_TLS13_SRUN_*) verdict on failure 30// 31// After this returns OK, the caller uses nx_tls13_server_session_app_send 32// / _app_recv to exchange encrypted application_data records. 33// 34// Per Cardinals 9 (single-responsibility: just drive the handshake; 35// app data is a separate layer), 22 (composition only). 36// 37// license_tier: INDEPENDENT_REDERIVE 38// genealogy_id: international-research-sources/ietf/rfc_8446 39// lineage_id: nishi_tls13_server_session_run_q1 40 41import "nx_syscalls.nx" 42import "nx_tls13.nx" 43import "nx_tls13_record.nx" 44import "nx_tls13_read_record_from_fd.nx" 45import "nx_tls13_transcript.nx" 46import "nx_tls13_server_session.nx" 47import "nx_tls13_server_session_recv_ch.nx" 48import "nx_tls13_server_session_emit_sh.nx" 49import "nx_tls13_server_session_emit_hrr.nx" 50import "nx_tls13_server_session_derive_hs.nx" 51import "nx_tls13_server_session_derive_traffic.nx" 52import "nx_tls13_server_session_emit_ee.nx" 53import "nx_tls13_server_session_emit_certificate.nx" 54import "nx_tls13_server_session_emit_cv.nx" 55import "nx_tls13_server_session_emit_sf.nx" 56import "nx_tls13_server_session_recv_cf.nx" 57 58const NX_TLS13_SRUN_OK: i64 = 1 59const NX_TLS13_SRUN_READ_CH_FAIL: i64 = 2 60const NX_TLS13_SRUN_RECV_CH_FAIL: i64 = 3 61const NX_TLS13_SRUN_EMIT_SH_FAIL: i64 = 4 62const NX_TLS13_SRUN_WRITE_SH_FAIL: i64 = 5 63const NX_TLS13_SRUN_DERIVE_HS_FAIL: i64 = 6 64const NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL: i64 = 7 65const NX_TLS13_SRUN_EMIT_EE_FAIL: i64 = 8 66const NX_TLS13_SRUN_WRITE_EE_FAIL: i64 = 9 67const NX_TLS13_SRUN_EMIT_CERT_FAIL: i64 = 10 68const NX_TLS13_SRUN_WRITE_CERT_FAIL: i64 = 11 69const NX_TLS13_SRUN_EMIT_CV_FAIL: i64 = 12 70const NX_TLS13_SRUN_WRITE_CV_FAIL: i64 = 13 71const NX_TLS13_SRUN_EMIT_SF_FAIL: i64 = 14 72const NX_TLS13_SRUN_WRITE_SF_FAIL: i64 = 15 73const NX_TLS13_SRUN_READ_CF_FAIL: i64 = 16 74const NX_TLS13_SRUN_RECV_CF_FAIL: i64 = 17 75const NX_TLS13_SRUN_EMIT_HRR_FAIL: i64 = 18 76const NX_TLS13_SRUN_WRITE_HRR_FAIL: i64 = 19 77const NX_TLS13_SRUN_VERDICT_N: i64 = 20 78 79const NX_TLS13_SRUN_CH_BUF_BYTES: i64 = 16645 80const NX_TLS13_SRUN_OUT_BUF_BYTES: i64 = 16645 81const NX_TLS13_SRUN_MAX_HS_RECORDS: i64 = 4 82 83func nx_tls13_srun_verdict_is_valid(v: i64) -> i64 { 84 if v < NX_TLS13_SRUN_OK { return 0 } 85 if v >= NX_TLS13_SRUN_VERDICT_N { return 0 } 86 return 1 87} 88 89// Loop writes n bytes to fd via sys_write. Returns 0 OK, -1 fail. 90func _srun_write_n(fd: i64, buf: *u8, n: i64) -> i64 { 91 var off: i64 = 0 92 while off < n { 93 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off) 94 if w <= 0 { return 0 - 1 } 95 off = off + w 96 } 97 return 0 98} 99 100// Read the ClientHello from fd into the session, performing a 101// HelloRetryRequest round trip when the client's first ClientHello 102// carries no X25519 key_share (but does offer X25519 in 103// supported_groups). On return OK the session is in CH_RECEIVED with a 104// usable X25519 share. Returns 0 OK, or a negative -(NX_TLS13_SRUN_*) 105// verdict on failure. Shared by both run paths (ECDSA + Ed25519). 106func _srun_recv_client_hello(fd: i64, s: *Tls13ServerSession) -> i64 { 107 // ---- read ClientHello1 ---- 108 let ch_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES) 109 let ch_total: i64 = nx_tls13_read_record_from_fd(fd, ch_rec, NX_TLS13_SRUN_CH_BUF_BYTES) 110 if ch_total < 0 { return 0 - NX_TLS13_SRUN_READ_CH_FAIL } 111 let r1: i64 = nx_tls13_server_session_recv_ch( 112 s, 113 (ch_rec + NX_TLS13_RECORD_HEADER_LEN), 114 ch_total - NX_TLS13_RECORD_HEADER_LEN) 115 if r1 == NX_TLS13_SSESSION_OK { return 0 } 116 if r1 != NX_TLS13_SSESSION_NEED_HRR { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL } 117 118 // ---- HelloRetryRequest round trip ---- 119 // recv_ch has already fed ClientHello1 into the transcript; rewrite 120 // it to the synthetic message_hash (RFC 8446 §4.4.1), then emit HRR. 121 nx_tls13_transcript_replace_with_hrr(s.transcript) 122 let hrr_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 123 let n_hrr: i64 = nx_tls13_server_session_emit_hrr(s, hrr_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 124 if n_hrr <= 0 { return 0 - NX_TLS13_SRUN_EMIT_HRR_FAIL } 125 if _srun_write_n(fd, hrr_buf, n_hrr) < 0 { return 0 - NX_TLS13_SRUN_WRITE_HRR_FAIL } 126 127 // ---- read ClientHello2, skipping an optional middlebox-compat CCS 128 // (RFC 8446 §D.4: the client MAY send a dummy ChangeCipherSpec 129 // record immediately before its second flight). ---- 130 var loop_count: i64 = 0 131 while loop_count < NX_TLS13_SRUN_MAX_HS_RECORDS { 132 let ch2_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES) 133 let ch2_total: i64 = nx_tls13_read_record_from_fd(fd, ch2_rec, NX_TLS13_SRUN_CH_BUF_BYTES) 134 if ch2_total < 0 { return 0 - NX_TLS13_SRUN_READ_CH_FAIL } 135 if (ch2_rec[0] & 0xff) == 0x14 { 136 loop_count = loop_count + 1 137 } else { 138 let r2: i64 = nx_tls13_server_session_recv_ch( 139 s, 140 (ch2_rec + NX_TLS13_RECORD_HEADER_LEN), 141 ch2_total - NX_TLS13_RECORD_HEADER_LEN) 142 if r2 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL } 143 return 0 144 } 145 } 146 return 0 - NX_TLS13_SRUN_READ_CH_FAIL 147} 148 149// Drive the server-side handshake to CONNECTED. Returns the session 150// as i64 (cast caller-side to *Tls13ServerSession) on success, or a 151// negative -NX_TLS13_SRUN_* verdict on failure. 152// 153// Caller responsibilities: 154// - fd is a connected TCP socket 155// - cert_der points to a DER-encoded server cert (RSA / ECDSA); 156// stone 6.5 wraps it in Certificate handshake message 157// - ecdsa_priv_be is a 32-byte big-endian ECDSA P-256 private scalar 158// used by stone 7.5 to sign the CertificateVerify 159// 160// Allocates one Tls13ServerSession via nx_tls13_server_session_new 161// with caller-supplied srv_rand + srv_x25519_priv. 162func nx_tls13_server_session_run( 163 fd: i64, 164 srv_rand: *u8, srv_x25519_priv: *u8, 165 cert_der: *u8, cert_der_len: i64, 166 ecdsa_priv_be: *u8 167) -> i64 { 168 let s: *Tls13ServerSession = nx_tls13_server_session_new(srv_rand, srv_x25519_priv) 169 if (s as i64) == 0 { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL } 170 171 // ---- Stone 2: read ClientHello (+ HelloRetryRequest if needed) ---- 172 let rch: i64 = _srun_recv_client_hello(fd, s) 173 if rch < 0 { return rch } 174 175 // ---- Stone 3: emit ServerHello + wrap as TLSPlaintext + write ---- 176 let sh_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 177 let n_sh: i64 = nx_tls13_server_session_emit_sh(s, sh_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 178 if n_sh <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SH_FAIL } 179 // emit_sh already returns a complete TLSPlaintext record (5-byte 180 // header backfilled at out[0..4]); write it raw. Wrapping it again 181 // here double-headers the ServerHello -> openssl/browsers reject it 182 // with a fatal unexpected_message alert. 183 if _srun_write_n(fd, sh_buf, n_sh) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SH_FAIL } 184 185 // ---- Stone 4 + 5: derive handshake secrets + traffic keys ---- 186 let r4: i64 = nx_tls13_server_session_derive_hs_secrets(s) 187 if r4 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_HS_FAIL } 188 let r5: i64 = nx_tls13_server_session_derive_traffic(s) 189 if r5 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL } 190 191 // ---- Stone 6: emit EncryptedExtensions (TLSCiphertext already) ---- 192 let ee_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 193 let n_ee: i64 = nx_tls13_server_session_emit_ee(s, ee_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 194 if n_ee <= 0 { return 0 - NX_TLS13_SRUN_EMIT_EE_FAIL } 195 if _srun_write_n(fd, ee_buf, n_ee) < 0 { return 0 - NX_TLS13_SRUN_WRITE_EE_FAIL } 196 197 // ---- Stone 6.5: emit Certificate chain (TLSCiphertext already) ---- 198 // cert_der is a concatenated-DER chain (leaf + intermediates); the 199 // chain emitter splits it into one CertificateEntry per cert so the 200 // client can build a full path to a trusted root. A single-cert 201 // input is byte-identical to the single-cert emitter. 202 let cert_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 203 let n_cert: i64 = nx_tls13_server_session_emit_certificate_chain( 204 s, cert_der, cert_der_len, cert_buf, NX_TLS13_SRUN_OUT_BUF_BYTES 205 ) 206 if n_cert <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CERT_FAIL } 207 if _srun_write_n(fd, cert_buf, n_cert) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CERT_FAIL } 208 209 // ---- Stone 7.5: emit CertificateVerify (TLSCiphertext already) ---- 210 let cv_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 211 let n_cv: i64 = nx_tls13_server_session_emit_cv( 212 s, ecdsa_priv_be, cv_buf, NX_TLS13_SRUN_OUT_BUF_BYTES 213 ) 214 if n_cv <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CV_FAIL } 215 if _srun_write_n(fd, cv_buf, n_cv) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CV_FAIL } 216 217 // ---- Stone 7: emit ServerFinished (TLSCiphertext already) ---- 218 let sf_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 219 let n_sf: i64 = nx_tls13_server_session_emit_sf(s, sf_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 220 if n_sf <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SF_FAIL } 221 if _srun_write_n(fd, sf_buf, n_sf) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SF_FAIL } 222 223 // ---- Stone 8: read ClientFinished record + verify ---- 224 // The client may send a ChangeCipherSpec compat record first 225 // (RFC 8446 §D.4) before the real CF. Loop until we get CF. 226 var loop_count: i64 = 0 227 var keep: i64 = 1 228 while keep == 1 { 229 if loop_count >= NX_TLS13_SRUN_MAX_HS_RECORDS { 230 return 0 - NX_TLS13_SRUN_READ_CF_FAIL 231 } 232 let cf_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES) 233 let cf_total: i64 = nx_tls13_read_record_from_fd( 234 fd, cf_rec, NX_TLS13_SRUN_CH_BUF_BYTES 235 ) 236 if cf_total < 0 { return 0 - NX_TLS13_SRUN_READ_CF_FAIL } 237 238 // ChangeCipherSpec (type 0x14) is harmless legacy noise -- skip. 239 if (cf_rec[0] & 0xff) == 0x14 { 240 loop_count = loop_count + 1 241 } else { 242 let r8: i64 = nx_tls13_server_session_recv_cf(s, cf_rec, cf_total) 243 if r8 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CF_FAIL } 244 keep = 0 245 } 246 } 247 248 return s as i64 249} 250 251// ===== V-HOST-2+1-2 Path B: nx_tls13_server_session_run_ed25519 ================================================= 252// 253// Parallel to nx_tls13_server_session_run; same flow EXCEPT step 7.5 254// uses nx_tls13_server_session_emit_cv_ed25519 with a 32-byte Ed25519 255// priv key (instead of nx_tls13_server_session_emit_cv with ECDSA priv). 256// 257// Caller usage: 258// nx_tls13_server_session_run_ed25519(fd, srv_rand, srv_x25519_priv, 259// cert_der, cert_der_len, 260// ed25519_priv_32) 261// 262// Cert MUST be Ed25519-signed X.509 (per V-HOST-4 sovereign cert gen). 263// V-HOST-4c bin/nx_cert_gen Ed25519 path produces compatible certs. 264// 265// Composes existing primitives: 266// nx_tls13_server_session_new (X25519 for KEX; unchanged) 267// nx_tls13_read_record_from_fd 268// nx_tls13_server_session_recv_ch 269// nx_tls13_server_session_emit_sh 270// nx_tls13_server_session_derive_hs_secrets 271// nx_tls13_server_session_derive_traffic 272// nx_tls13_server_session_emit_ee 273// nx_tls13_server_session_emit_certificate (cert sent unchanged; sig 274// in CV step picks per cert's key type) 275// nx_tls13_server_session_emit_cv_ed25519 (NEW per V-HOST-2+1-1) 276// nx_tls13_server_session_emit_sf 277// nx_tls13_server_session_recv_cf 278 279func nx_tls13_server_session_run_ed25519( 280 fd: i64, 281 srv_rand: *u8, srv_x25519_priv: *u8, 282 cert_der: *u8, cert_der_len: i64, 283 ed25519_priv_32: *u8 284) -> i64 { 285 let s: *Tls13ServerSession = nx_tls13_server_session_new(srv_rand, srv_x25519_priv) 286 if (s as i64) == 0 { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL } 287 288 // ---- Stone 2: read ClientHello (+ HelloRetryRequest if needed) ---- 289 let rch: i64 = _srun_recv_client_hello(fd, s) 290 if rch < 0 { return rch } 291 292 let sh_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 293 let n_sh: i64 = nx_tls13_server_session_emit_sh(s, sh_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 294 if n_sh <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SH_FAIL } 295 if _srun_write_n(fd, sh_buf, n_sh) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SH_FAIL } 296 let r3: i64 = nx_tls13_server_session_derive_hs_secrets(s) 297 if r3 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_HS_FAIL } 298 299 let r4: i64 = nx_tls13_server_session_derive_traffic(s) 300 if r4 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL } 301 302 let ee_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 303 let n_ee: i64 = nx_tls13_server_session_emit_ee(s, ee_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 304 if n_ee <= 0 { return 0 - NX_TLS13_SRUN_EMIT_EE_FAIL } 305 if _srun_write_n(fd, ee_buf, n_ee) < 0 { return 0 - NX_TLS13_SRUN_WRITE_EE_FAIL } 306 307 let cert_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 308 let n_cert: i64 = nx_tls13_server_session_emit_certificate( 309 s, cert_der, cert_der_len, cert_buf, NX_TLS13_SRUN_OUT_BUF_BYTES 310 ) 311 if n_cert <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CERT_FAIL } 312 if _srun_write_n(fd, cert_buf, n_cert) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CERT_FAIL } 313 314 // ---- Stone 7.5 DIFFERENCE: ED25519 sig path ---- 315 let cv_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 316 let n_cv: i64 = nx_tls13_server_session_emit_cv_ed25519( 317 s, ed25519_priv_32, cv_buf, NX_TLS13_SRUN_OUT_BUF_BYTES 318 ) 319 if n_cv <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CV_FAIL } 320 if _srun_write_n(fd, cv_buf, n_cv) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CV_FAIL } 321 322 // ---- Stones 7-8: identical to ECDSA path ---- 323 let sf_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES) 324 let n_sf: i64 = nx_tls13_server_session_emit_sf(s, sf_buf, NX_TLS13_SRUN_OUT_BUF_BYTES) 325 if n_sf <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SF_FAIL } 326 if _srun_write_n(fd, sf_buf, n_sf) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SF_FAIL } 327 328 var loop_count: i64 = 0 329 var keep: i64 = 1 330 while keep == 1 { 331 if loop_count >= NX_TLS13_SRUN_MAX_HS_RECORDS { 332 return 0 - NX_TLS13_SRUN_READ_CF_FAIL 333 } 334 let cf_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES) 335 let cf_total: i64 = nx_tls13_read_record_from_fd( 336 fd, cf_rec, NX_TLS13_SRUN_CH_BUF_BYTES 337 ) 338 if cf_total < 0 { return 0 - NX_TLS13_SRUN_READ_CF_FAIL } 339 if (cf_rec[0] & 0xff) == 0x14 { 340 loop_count = loop_count + 1 341 } else { 342 let r8: i64 = nx_tls13_server_session_recv_cf(s, cf_rec, cf_total) 343 if r8 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CF_FAIL } 344 keep = 0 345 } 346 } 347 348 return s as i64 349} 350 351func main() -> i64 { 352 return 0 353}