code wiki / (root) / nx_https_get_happy_test.nx

nx_https_get_happy_test.nx source

↩ module page · 657 lines · 24381 B

1// nx_https_get_happy_test.nx -- REAL fork+child happy-path KAT 2// for the nx_https_get / nx_tls13_client_session_run state machine 3// over TCP loopback. 4// 5// Closes the verdict-only gap explicitly named in the 6// 2026-05-19 PM honest verification audit 7// (NISHI_HTTPS_ARC_STATUS_2026_05_19.md): 8// 9// "nx_https_get (commit 72771424): only tests BAD_URL paths. 10// The full handshake + GET round-trip against a real or 11// loopback HTTPS server is NOT yet a KAT. The lower wire 12// layer IS verified via the pre-existing nx_https_loopback_test 13// (fork+child TCP loopback running both TLS endpoints in 14// NishiLang), but that test bypasses the Tls13ClientSession 15// state machine wrapper..." 16// 17// This file builds the loopback test that DOES exercise the 18// session state machine wrapper. Architecture: 19// 20// Parent process (CLIENT): 21// - socket+bind+listen on 127.0.0.1:19454 22// - accept(); pass accepted fd to nx_tls13_client_session_run 23// - assert session_run returns POSITIVE pointer (= state 24// reached CONNECTED, all 4 dispatched recv_hs verdicts OK, 25// app keys derived) 26// 27// Child process (SERVER): 28// - socket+connect to loopback port 29// - read ClientHello as REAL TLS record (5-byte header) 30// - emit ServerHello as REAL TLS record (5-byte header) 31// - derive handshake keys 32// - emit AEAD-encrypted EE + Cert + CV + SF records 33// - read AEAD-encrypted CF record 34// 35// The CERT carried in the Certificate message is a REAL 36// 197-byte minimal v1 Ed25519 cert with SAN entry "localhost" 37// matching the SNI hostname used by the client. The cert's 38// outer sig is a REAL Ed25519 signature over its own TBS bytes 39// (now that SHA-512 K constants are fixed -- commit c9ffedfd -- 40// Ed25519 sign produces canonical FIPS-compliant output). 41// 42// The cert is added to the client's TrustStore as its own 43// trust anchor (self-signed -> leaf is anchor). Cert chain 44// validation passes: 45// - Leaf SAN matches SNI hostname ("localhost") 46// - Validity period [2023..2026] covers now_epoch (2024-06-15) 47// - Self-signed: leaf verifies under leaf's own pub (the 48// pipeline walks: leaf's issuer-DN matches anchor's 49// subject-DN -> verify leaf sig under anchor's pubkey -> 50// ed25519_verify_full passes because SHA-512 now correct) 51// 52// The CV signature can stay garbage bytes because CV sig 53// verification is still STUBBED in 54// nx_tls13_client_dispatch_with_validation (the dispatcher only 55// validates the Cert message, not CV). This is documented in 56// nx_tls13_client.nx line 170-171 + the dispatcher wrapper. 57// The KAT exercises everything EXCEPT CV sig verification. 58// 59// The SF (server Finished) MUST have a correct HMAC -- the 60// client dispatcher's WAIT_SF branch checks it via 61// nx_tls13_finished_compute. We compute the MAC at test time. 62// 63// expect_exit: 0 64// license_tier: ORIGINAL 65 66import "nx_syscalls.nx" 67import "nx_connect.nx" // bounded connect: a raw sys_connect hangs ~127s on a black-holed host 68import "nx_sha256.nx" 69import "nx_sha512.nx" 70import "nx_ed25519.nx" 71import "nx_ed25519_signature.nx" 72import "nx_ed25519_scalar.nx" 73import "nx_ed25519_arith.nx" 74import "nx_x25519.nx" 75import "nx_x509.nx" 76import "nx_x509_trust_store.nx" 77import "nx_tls13.nx" 78import "nx_tls13_ext.nx" 79import "nx_tls13_hello.nx" 80import "nx_tls13_finished.nx" 81import "nx_tls13_transcript.nx" 82import "nx_tls13_handshake.nx" 83import "nx_tls13_schedule.nx" 84import "nx_tls13_record.nx" 85import "nx_tls13_server.nx" 86import "nx_tls13_client_validate_certificate.nx" 87import "nx_tls13_client_session_run.nx" 88 89const HPORT: i64 = 19454 90const NOW_EPOCH: i64 = 1718452800 // 2024-06-15 within cert validity 91 92// --------------------------------------------------------------- 93// TCP + framing helpers 94// --------------------------------------------------------------- 95 96func hbuild_addr(out: *u8, port: i64) -> i64 { 97 out[0] = 2; out[1] = 0 98 out[2] = (port >> 8) & 0xff 99 out[3] = port & 0xff 100 out[4] = 127; out[5] = 0; out[6] = 0; out[7] = 1 101 var i: i64 = 8 102 while i < 16 { out[i] = 0; i = i + 1 } 103 return 16 104} 105 106func hread_n(fd: i64, buf: *u8, n: i64) -> i64 { 107 var got: i64 = 0 108 while got < n { 109 let r: i64 = sys_read(fd, buf + got, n - got) 110 if r <= 0 { return 0 - 1 } 111 got = got + r 112 } 113 return got 114} 115 116func hwrite_n(fd: i64, buf: *u8, n: i64) -> i64 { 117 var off: i64 = 0 118 while off < n { 119 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off) 120 if w <= 0 { return 0 - 1 } 121 off = off + w 122 } 123 return 0 124} 125 126// Wrap handshake body bytes in a TLS plaintext record (type=22, 127// version=0x0303) and send to fd. 128func send_tls_plain_record(fd: i64, body: *u8, body_len: i64) -> i64 { 129 let hdr: *u8 = sys_mmap(16) 130 hdr[0] = NX_TLS13_CT_HANDSHAKE & 0xff 131 hdr[1] = 0x03 132 hdr[2] = 0x03 133 hdr[3] = (body_len >> 8) & 0xff 134 hdr[4] = body_len & 0xff 135 if hwrite_n(fd, hdr, 5) < 0 { return 0 - 1 } 136 if hwrite_n(fd, body, body_len) < 0 { return 0 - 2 } 137 return 0 138} 139 140// Read a full TLS plaintext record from fd; returns body length, 141// sets *out_body_off = 5 (skip header). out_buf must be sized 142// for the full record (header + body). 143func recv_tls_plain_record(fd: i64, out_buf: *u8, out_cap: i64) -> i64 { 144 if out_cap < 5 { return 0 - 1 } 145 if hread_n(fd, out_buf, 5) < 0 { return 0 - 2 } 146 let body_len: i64 = ((out_buf[3] & 0xff) << 8) | (out_buf[4] & 0xff) 147 if 5 + body_len > out_cap { return 0 - 3 } 148 if hread_n(fd, out_buf + 5, body_len) < 0 { return 0 - 4 } 149 return body_len 150} 151 152// AEAD record send (inner type 22 = handshake-after-keys). 153func send_aead_record(fd: i64, key: *u8, iv: *u8, seq: i64, 154 body: *u8, body_len: i64, inner_type: i64) -> i64 { 155 let hdr: *u8 = sys_mmap(16) 156 let ct: *u8 = sys_mmap(body_len + 64) 157 let tag: *u8 = sys_mmap(16) 158 let v: i64 = nx_tls13_record_encrypt( 159 key, iv, seq, body, body_len, inner_type, 0, hdr, ct, tag 160 ) 161 if v != NX_TLS13_REC_VERDICT_OK { return 0 - 1 } 162 if hwrite_n(fd, hdr, 5) < 0 { return 0 - 2 } 163 if hwrite_n(fd, ct, body_len + 1) < 0 { return 0 - 3 } 164 if hwrite_n(fd, tag, 16) < 0 { return 0 - 4 } 165 return 0 166} 167 168// AEAD record recv. Returns decrypted plaintext length. 169func recv_aead_record(fd: i64, key: *u8, iv: *u8, seq: i64, 170 out: *u8, out_cap: i64) -> i64 { 171 let hdr: *u8 = sys_mmap(16) 172 if hread_n(fd, hdr, 5) < 0 { return 0 - 1 } 173 let total: i64 = ((hdr[3] & 0xff) << 8) | (hdr[4] & 0xff) 174 if total < 17 { return 0 - 2 } 175 let ct_len: i64 = total - 16 176 let buf: *u8 = sys_mmap(total + 16) 177 if hread_n(fd, buf, total) < 0 { return 0 - 3 } 178 let tag: *u8 = sys_mmap(16) 179 var ti: i64 = 0 180 while ti < 16 { tag[ti] = buf[ct_len + ti]; ti = ti + 1 } 181 let rct: *i64 = sys_mmap(8) as *i64 182 let rln: *i64 = sys_mmap(8) as *i64 183 let v: i64 = nx_tls13_record_decrypt( 184 key, iv, seq, hdr, buf, ct_len, tag, out, rct, rln 185 ) 186 if v != NX_TLS13_REC_VERDICT_OK { return 0 - 4 } 187 return rln[0] 188} 189 190// --------------------------------------------------------------- 191// Ed25519 helpers: derive pub from priv via real arithmetic 192// --------------------------------------------------------------- 193 194// Compute s_scalar = SHA-512(priv)[0..32] clamped. 195func derive_s_scalar(priv: *u8, s_out: *u8) -> i64 { 196 let h: *u8 = sys_mmap(64) 197 ed25519_sha512(priv, 32, h) 198 var i: i64 = 0 199 while i < 32 { s_out[i] = h[i]; i = i + 1 } 200 s_out[0] = s_out[0] & 0xf8 201 s_out[31] = (s_out[31] & 0x7f) | 0x40 202 return 0 203} 204 205// Compute pub = compress(s_scalar * G). 206func derive_pub_from_priv(priv: *u8, pub_out: *u8) -> i64 { 207 let s: *u8 = sys_mmap(32) 208 derive_s_scalar(priv, s) 209 let bp: *GeP3 = ge_p3_alloc() 210 ed25519_basepoint_p3(bp) 211 let A: *GeP3 = ge_p3_alloc() 212 ge_scalar_mul(A, s, bp) 213 ge_p3_compress(pub_out, A) 214 return 0 215} 216 217// --------------------------------------------------------------- 218// Build a real Ed25519-signed 197-byte v1 cert with SAN 219// --------------------------------------------------------------- 220 221func emit_ed25519_algid(buf: *u8, off: i64) -> i64 { 222 buf[off + 0] = 0x30 as u8 223 buf[off + 1] = 5 as u8 224 buf[off + 2] = 0x06 as u8 225 buf[off + 3] = 3 as u8 226 buf[off + 4] = 0x2B as u8 227 buf[off + 5] = 0x65 as u8 228 buf[off + 6] = 0x70 as u8 229 return 7 230} 231 232func emit_utctime(buf: *u8, off: i64, 233 yyyy: i64, mo: i64, d: i64, 234 h: i64, mi: i64, s: i64) -> i64 { 235 buf[off + 0] = 0x17 as u8 236 buf[off + 1] = 13 as u8 237 let yy: i64 = yyyy - 2000 238 buf[off + 2] = (0x30 + (yy / 10)) as u8 239 buf[off + 3] = (0x30 + (yy % 10)) as u8 240 buf[off + 4] = (0x30 + (mo / 10)) as u8 241 buf[off + 5] = (0x30 + (mo % 10)) as u8 242 buf[off + 6] = (0x30 + (d / 10)) as u8 243 buf[off + 7] = (0x30 + (d % 10)) as u8 244 buf[off + 8] = (0x30 + (h / 10)) as u8 245 buf[off + 9] = (0x30 + (h % 10)) as u8 246 buf[off + 10] = (0x30 + (mi / 10)) as u8 247 buf[off + 11] = (0x30 + (mi % 10)) as u8 248 buf[off + 12] = (0x30 + (s / 10)) as u8 249 buf[off + 13] = (0x30 + (s % 10)) as u8 250 buf[off + 14] = 0x5A as u8 251 return 15 252} 253 254func emit_dnsname(buf: *u8, off: i64, name: *u8, name_len: i64) -> i64 { 255 buf[off] = 0x82 as u8 256 buf[off + 1] = name_len as u8 257 var i: i64 = 0 258 while i < name_len { 259 buf[off + 2 + i] = name[i] 260 i = i + 1 261 } 262 return 2 + name_len 263} 264 265// Build cert with SAN "localhost" (9 chars) + Ed25519 SPKI from 266// given pub bytes. Returns total cert length. TBS is at offset 3 267// (after outer SEQUENCE header) length TBS_TOTAL = 118. Caller 268// must sign tbs bytes + write into the cert at the sig position 269// (offset 3 + TBS_TOTAL + 7 alg id + 3 BIT STRING header). 270func build_cert_with_pub(buf: *u8, pub: *u8) -> i64 { 271 let SAN_INNER_LEN: i64 = 11 // 1 dNSName entry "localhost" 272 let SAN_SEQ_TOTAL: i64 = 2 + SAN_INNER_LEN // 13 273 let OS_TOTAL: i64 = 2 + SAN_SEQ_TOTAL // 15 274 let OID_TOTAL: i64 = 5 275 let EXT_BODY: i64 = OID_TOTAL + OS_TOTAL // 20 276 let EXT_TOTAL: i64 = 2 + EXT_BODY // 22 277 let EXTS_BODY: i64 = EXT_TOTAL // 22 278 let EXTS_TOTAL: i64 = 2 + EXTS_BODY // 24 279 let EXP_WRAP_TOTAL: i64 = 2 + EXTS_TOTAL // 26 280 281 let VAL_BODY: i64 = 30 282 let VAL_TOTAL: i64 = 2 + VAL_BODY 283 284 let SPKI_BODY: i64 = 7 + 35 285 let SPKI_TOTAL: i64 = 2 + SPKI_BODY 286 287 let TBS_BODY: i64 = 3 + 7 + 2 + VAL_TOTAL + 2 + SPKI_TOTAL + EXP_WRAP_TOTAL // 116 288 let TBS_TOTAL: i64 = 2 + TBS_BODY 289 290 let SIG_TOTAL: i64 = 67 291 let OUTER_BODY: i64 = TBS_TOTAL + 7 + SIG_TOTAL 292 let OUTER_TOTAL: i64 = 3 + OUTER_BODY 293 294 var o: i64 = 0 295 buf[o] = 0x30 as u8; o = o + 1 296 buf[o] = 0x81 as u8; o = o + 1 297 buf[o] = OUTER_BODY as u8; o = o + 1 298 299 let tbs_start: i64 = o 300 buf[o] = 0x30 as u8; o = o + 1 301 buf[o] = TBS_BODY as u8; o = o + 1 302 303 buf[o] = 0x02 as u8; buf[o+1] = 0x01 as u8; buf[o+2] = 0x01 as u8; o = o + 3 304 o = o + emit_ed25519_algid(buf, o) 305 buf[o] = 0x30 as u8; buf[o+1] = 0x00 as u8; o = o + 2 306 buf[o] = 0x30 as u8; o = o + 1 307 buf[o] = VAL_BODY as u8; o = o + 1 308 o = o + emit_utctime(buf, o, 2023, 1, 1, 0, 0, 0) 309 o = o + emit_utctime(buf, o, 2026, 1, 1, 0, 0, 0) 310 buf[o] = 0x30 as u8; buf[o+1] = 0x00 as u8; o = o + 2 311 buf[o] = 0x30 as u8; o = o + 1 312 buf[o] = SPKI_BODY as u8; o = o + 1 313 o = o + emit_ed25519_algid(buf, o) 314 buf[o] = 0x03 as u8; o = o + 1 // BIT STRING tag 315 buf[o] = 0x21 as u8; o = o + 1 // length 33 316 buf[o] = 0x00 as u8; o = o + 1 // unused bits 317 var ki: i64 = 0 318 while ki < 32 { buf[o + ki] = pub[ki]; ki = ki + 1 } 319 o = o + 32 320 321 buf[o] = 0xA3 as u8; o = o + 1 322 buf[o] = EXTS_TOTAL as u8; o = o + 1 323 buf[o] = 0x30 as u8; o = o + 1 324 buf[o] = EXTS_BODY as u8; o = o + 1 325 buf[o] = 0x30 as u8; o = o + 1 326 buf[o] = EXT_BODY as u8; o = o + 1 327 buf[o] = 0x06 as u8; buf[o+1] = 0x03 as u8; buf[o+2] = 0x55 as u8 328 buf[o+3] = 0x1D as u8; buf[o+4] = 0x11 as u8; o = o + 5 329 buf[o] = 0x04 as u8; o = o + 1 330 buf[o] = SAN_SEQ_TOTAL as u8; o = o + 1 331 buf[o] = 0x30 as u8; o = o + 1 332 buf[o] = SAN_INNER_LEN as u8; o = o + 1 333 let host: *u8 = sys_mmap(16) 334 host[0]=0x6c; host[1]=0x6f; host[2]=0x63; host[3]=0x61 335 host[4]=0x6c; host[5]=0x68; host[6]=0x6f; host[7]=0x73 336 host[8]=0x74 // "localhost" 337 o = o + emit_dnsname(buf, o, host, 9) 338 339 let tbs_end: i64 = o 340 341 o = o + emit_ed25519_algid(buf, o) 342 343 // signatureValue BIT STRING wrapper, sig bytes filled by caller 344 buf[o] = 0x03 as u8; o = o + 1 345 buf[o] = 0x41 as u8; o = o + 1 346 buf[o] = 0x00 as u8; o = o + 1 347 // 64 bytes placeholder for the sig (caller fills) 348 var si: i64 = 0 349 while si < 64 { buf[o + si] = 0 as u8; si = si + 1 } 350 o = o + 64 351 352 return o 353} 354 355// Sign TBS bytes at cert[3..3+TBS_TOTAL] and place sig at the 356// end (the 64 bytes after the BIT STRING wrapper). 357// TBS_TOTAL is the outer SEQUENCE of the TBS including its 2-byte 358// header. The TBS body bytes signed by Ed25519 are exactly those 359// outer bytes (DER's "tbsCertificate" SEQUENCE). 360func sign_cert(buf: *u8, priv: *u8, cert_len: i64) -> i64 { 361 let TBS_TOTAL: i64 = 118 362 let tbs_off: i64 = 3 363 let sig_off: i64 = cert_len - 64 // last 64 bytes = sig 364 let sig: *u8 = sys_mmap(64) 365 ed25519_sign_full(priv, buf + tbs_off, TBS_TOTAL, sig) 366 var i: i64 = 0 367 while i < 64 { 368 buf[sig_off + i] = sig[i] 369 i = i + 1 370 } 371 return 0 372} 373 374// --------------------------------------------------------------- 375// CLIENT entry point: hand to nx_tls13_client_session_run 376// --------------------------------------------------------------- 377 378func client_session_run(cfd: i64, cert_for_trust: *u8, cert_len: i64) -> i64 { 379 // Build TrustStore with our self-signed cert as anchor 380 let store: *TrustStore = trust_store_alloc(4) 381 let anchor: *X509Cert = sys_mmap(256) as *X509Cert 382 if x509_parse(cert_for_trust, cert_len, anchor) < 0 { return 200 } 383 if trust_store_add(store, cert_for_trust, anchor) != NX_TRUST_STORE_OK { 384 return 201 385 } 386 387 // Build val_ctx: sni="localhost", now within cert validity 388 let val_ctx: *TlsValidationContext = sys_mmap(64) as *TlsValidationContext 389 let host: *u8 = sys_mmap(16) 390 host[0]=0x6c; host[1]=0x6f; host[2]=0x63; host[3]=0x61 391 host[4]=0x6c; host[5]=0x68; host[6]=0x6f; host[7]=0x73 392 host[8]=0x74 393 val_ctx.store = store 394 val_ctx.sni_host = host 395 val_ctx.sni_host_len = 9 396 val_ctx.now_epoch = NOW_EPOCH 397 398 // Client entropy: deterministic test vectors 399 let cr: *u8 = sys_mmap(32) 400 var i: i64 = 0 401 while i < 32 { cr[i] = (0xC0 + i) as u8; i = i + 1 } 402 let priv: *u8 = sys_mmap(32) 403 i = 0 404 while i < 32 { priv[i] = (0xA0 + i) as u8; i = i + 1 } 405 406 let r: i64 = nx_tls13_client_session_run( 407 cfd, host, 9, cr, priv, val_ctx 408 ) 409 if r <= 0 { return 0 - r } 410 let s: *Tls13ClientSession = r as *Tls13ClientSession 411 if s.state != NX_TLS13_CSESSION_STATE_CONNECTED { return 210 } 412 return 0 413} 414 415// --------------------------------------------------------------- 416// SERVER side: emit real TLS records throughout 417// --------------------------------------------------------------- 418 419// Parse CH and extract client X25519 pubkey + session_id offset+len. 420func server_parse_ch(ch: *u8, ch_len: i64, 421 sid_o: *i64, sid_l: *i64, pub_out: *i64) -> i64 { 422 let p_ro: *i64 = sys_mmap(16) as *i64 423 let p_eo: *i64 = sys_mmap(16) as *i64 424 let p_el: *i64 = sys_mmap(16) as *i64 425 let pv: i64 = tls13_server_parse_client_hello( 426 ch, ch_len, p_ro, sid_o, sid_l, p_eo, p_el 427 ) 428 if pv != NX_TLS13_SERVER_VERDICT_OK { return pv } 429 let ks_off: *i64 = sys_mmap(16) as *i64 430 let ks_len: *i64 = sys_mmap(16) as *i64 431 let fv: i64 = tls13_ext_find( 432 ch + p_eo[0], p_el[0], EXT_KEY_SHARE, ks_off, ks_len 433 ) 434 if fv != NX_TLS13_HELLO_VERDICT_OK { return fv } 435 // SH-style key_share parsing differs from CH: CH wraps the 436 // KeyShareEntry list in a 2-byte client_shares length prefix. 437 // CH KeyShareEntry: group(2) + ke_len(2) + ke(32) 438 let ke_off: i64 = p_eo[0] + ks_off[0] + 2 + 4 439 pub_out[0] = (ch as i64) + ke_off 440 return 0 441} 442 443// Derive handshake-traffic keys after CH+SH transcript snapshot. 444func server_derive_keys(s_priv: *u8, c_pub: *u8, th: *u8, eh: *u8, 445 chts_out: *u8, shts_out: *u8, 446 ck_out: *u8, civ_out: *u8, 447 sk_out: *u8, siv_out: *u8) -> i64 { 448 let hs: *u8 = sys_mmap(64) 449 tls13_handshake_compute_handshake_keys( 450 s_priv, c_pub, th, eh, 451 hs, chts_out, shts_out, 452 ck_out, civ_out, sk_out, siv_out 453 ) 454 return 0 455} 456 457// Build a valid CV message (type=15) with garbage sig content 458// (CV verification is stubbed in client dispatcher). 459// handshake_type(1) + length(3) + scheme(2) + sig_len(2) + sig(64) 460// body length = 2 + 2 + 64 = 68 461func build_cv(out: *u8) -> i64 { 462 out[0] = 0x0F // HT_CERTIFICATE_VERIFY 463 out[1] = 0; out[2] = 0; out[3] = 68 464 out[4] = 0x08; out[5] = 0x07 // ed25519 SigScheme 465 out[6] = 0; out[7] = 64 466 var i: i64 = 0 467 while i < 64 { out[8 + i] = 0x99 as u8; i = i + 1 } 468 return 72 469} 470 471// Wrap our 197-byte cert in a TLS 1.3 Certificate handshake message: 472// handshake_type(1) + length(3) + cert_request_context_len(1) + 473// cert_request_context(0) + cert_chain_length(3) + 474// for each cert: cert_len(3) + cert_data + extensions_len(2) + extensions(0) 475// 476// Inner: 1(ctx_len) + 0 + 3(chain_len) + 3(cert_len) + cert_data + 477// 2(ext_len) + 0 = 9 + cert_len 478// Total body = inner + 0 (no extra wrapping) 479func build_cert_msg(out: *u8, cert: *u8, cert_len: i64) -> i64 { 480 let inner_len: i64 = 9 + cert_len 481 out[0] = 0x0B // HT_CERTIFICATE 482 out[1] = (inner_len >> 16) & 0xff 483 out[2] = (inner_len >> 8) & 0xff 484 out[3] = inner_len & 0xff 485 out[4] = 0 // ctx_len = 0 486 // cert_chain_length = 3 + cert_len + 2 (cert + ext_len) 487 let chain_len: i64 = 3 + cert_len + 2 488 out[5] = (chain_len >> 16) & 0xff 489 out[6] = (chain_len >> 8) & 0xff 490 out[7] = chain_len & 0xff 491 out[8] = (cert_len >> 16) & 0xff 492 out[9] = (cert_len >> 8) & 0xff 493 out[10] = cert_len & 0xff 494 var i: i64 = 0 495 while i < cert_len { out[11 + i] = cert[i]; i = i + 1 } 496 out[11 + cert_len] = 0 // ext_len = 0 497 out[11 + cert_len + 1] = 0 498 return 4 + inner_len 499} 500 501// EncryptedExtensions handshake message: type 8, body 2 bytes (empty ext list) 502func build_ee(out: *u8) -> i64 { 503 out[0] = 0x08 // HT_ENCRYPTED_EXTENSIONS 504 out[1] = 0; out[2] = 0; out[3] = 2 505 out[4] = 0; out[5] = 0 506 return 6 507} 508 509// Compute the server Finished handshake message. 510func build_sf(tx: *u8, shts: *u8, out: *u8) -> i64 { 511 let pre: *u8 = sys_mmap(64) 512 nx_tls13_transcript_snapshot(tx, pre) 513 let fk: *u8 = sys_mmap(64) 514 tls13_finished_key(shts, 32, fk) 515 let mac: *u8 = sys_mmap(64) 516 nx_tls13_finished_compute(fk, 32, pre, 32, mac) 517 out[0] = 0x14 // HT_FINISHED 518 out[1] = 0; out[2] = 0; out[3] = 32 519 var i: i64 = 0 520 while i < 32 { out[4 + i] = mac[i]; i = i + 1 } 521 return 36 522} 523 524func server_run(sfd: i64, cert_for_msg: *u8, cert_len: i64) -> i64 { 525 // ---- 1. Read ClientHello as real TLS record ---- 526 let ch_rec: *u8 = sys_mmap(2048) 527 let ch_body_len: i64 = recv_tls_plain_record(sfd, ch_rec, 2048) 528 if ch_body_len < 0 { return 100 + (0 - ch_body_len) } 529 let ch_body: *u8 = ch_rec + 5 530 531 let sid_o: *i64 = sys_mmap(16) as *i64 532 let sid_l: *i64 = sys_mmap(16) as *i64 533 let c_pub_slot: *i64 = sys_mmap(8) as *i64 534 if server_parse_ch(ch_body, ch_body_len, sid_o, sid_l, c_pub_slot) != 0 { 535 return 110 536 } 537 let c_pub: *u8 = c_pub_slot[0] as *u8 538 539 // ---- 2. Generate server X25519 keypair + emit SH ---- 540 let s_priv: *u8 = sys_mmap(32) 541 var pi: i64 = 0 542 while pi < 32 { s_priv[pi] = (0x80 + pi) as u8; pi = pi + 1 } 543 let s_pub: *u8 = sys_mmap(32) 544 let base: *u8 = sys_mmap(32) 545 base[0] = 9 546 var bi: i64 = 1 547 while bi < 32 { base[bi] = 0; bi = bi + 1 } 548 x25519(s_priv, base, s_pub) 549 550 let s_random: *u8 = sys_mmap(32) 551 var ri: i64 = 0 552 while ri < 32 { s_random[ri] = (0x70 + ri) as u8; ri = ri + 1 } 553 let sh: *u8 = sys_mmap(512) 554 let sh_len: i64 = tls13_server_emit_server_hello( 555 s_random, ch_body + sid_o[0], sid_l[0], s_pub, sh, 512 556 ) 557 if sh_len < 0 { return 120 } 558 if send_tls_plain_record(sfd, sh, sh_len) != 0 { return 121 } 559 560 // ---- 3. Build transcript hash CH || SH ---- 561 let tx: *u8 = nx_tls13_transcript_new() 562 nx_tls13_transcript_update(tx, ch_body, ch_body_len) 563 nx_tls13_transcript_update(tx, sh, sh_len) 564 let th: *u8 = sys_mmap(64) 565 nx_tls13_transcript_snapshot(tx, th) 566 567 // ---- 4. Derive handshake-traffic keys ---- 568 let eh: *u8 = sys_mmap(64) 569 let empty: *u8 = sys_mmap(8) 570 sha256_digest(empty, 0, eh) 571 572 let chts: *u8 = sys_mmap(64) 573 let shts: *u8 = sys_mmap(64) 574 let ck: *u8 = sys_mmap(64) 575 let civ: *u8 = sys_mmap(64) 576 let sk: *u8 = sys_mmap(64) 577 let siv: *u8 = sys_mmap(64) 578 server_derive_keys(s_priv, c_pub, th, eh, chts, shts, ck, civ, sk, siv) 579 580 // ---- 5. Send AEAD-encrypted EE + Cert + CV + SF ---- 581 let ee: *u8 = sys_mmap(64) 582 let ee_len: i64 = build_ee(ee) 583 nx_tls13_transcript_update(tx, ee, ee_len) 584 if send_aead_record(sfd, sk, siv, 0, ee, ee_len, 22) != 0 { return 130 } 585 586 let cert_msg: *u8 = sys_mmap(512) 587 let cert_msg_len: i64 = build_cert_msg(cert_msg, cert_for_msg, cert_len) 588 nx_tls13_transcript_update(tx, cert_msg, cert_msg_len) 589 if send_aead_record(sfd, sk, siv, 1, cert_msg, cert_msg_len, 22) != 0 { return 131 } 590 591 let cv: *u8 = sys_mmap(128) 592 let cv_len: i64 = build_cv(cv) 593 nx_tls13_transcript_update(tx, cv, cv_len) 594 if send_aead_record(sfd, sk, siv, 2, cv, cv_len, 22) != 0 { return 132 } 595 596 let sf: *u8 = sys_mmap(64) 597 let sf_len: i64 = build_sf(tx, shts, sf) 598 nx_tls13_transcript_update(tx, sf, sf_len) 599 if send_aead_record(sfd, sk, siv, 3, sf, sf_len, 22) != 0 { return 133 } 600 601 // ---- 6. Read encrypted CF record (client's Finished) ---- 602 let cf: *u8 = sys_mmap(64) 603 let cf_n: i64 = recv_aead_record(sfd, ck, civ, 0, cf, 64) 604 if cf_n < 0 { return 140 + (0 - cf_n) } 605 if cf_n != 36 { return 150 } 606 if (cf[0] & 0xff) != 0x14 { return 151 } // expect HT_FINISHED 607 608 return 0 609} 610 611// --------------------------------------------------------------- 612// Main: fork + child 613// --------------------------------------------------------------- 614 615func main() -> i64 { 616 // ---- Prepare cert + key shared by both processes ---- 617 let cert_priv: *u8 = sys_mmap(32) 618 var ki: i64 = 0 619 while ki < 32 { cert_priv[ki] = (0x10 + ki) as u8; ki = ki + 1 } 620 let cert_pub: *u8 = sys_mmap(32) 621 derive_pub_from_priv(cert_priv, cert_pub) 622 623 let cert: *u8 = sys_mmap(512) 624 let cert_len: i64 = build_cert_with_pub(cert, cert_pub) 625 sign_cert(cert, cert_priv, cert_len) 626 627 // ---- Socket setup ---- 628 let addr: *u8 = sys_mmap(16) 629 hbuild_addr(addr, HPORT) 630 let lfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 631 if lfd < 0 { return 1 } 632 if sys_bind(lfd, addr, 16) < 0 { return 2 } 633 if sys_listen(lfd, 4) < 0 { return 3 } 634 635 let pid: i64 = sys_fork() 636 if pid < 0 { return 4 } 637 638 if pid == 0 { 639 // CHILD = server 640 var spin: i64 = 0 641 while spin < 200000 { spin = spin + 1 } 642 let cfd: i64 = sys_socket(AF_INET, SOCK_STREAM, 0) 643 if cfd < 0 { return 5 } 644 if nx_connect_bounded(cfd, addr, 16, NX_CONN_DEFAULT_MS) < 0 { return 6 } 645 return server_run(cfd, cert, cert_len) 646 } 647 648 // PARENT = client 649 let sfd: i64 = sys_accept(lfd) 650 if sfd < 0 { return 7 } 651 let cr: i64 = client_session_run(sfd, cert, cert_len) 652 if cr != 0 { return cr } 653 654 let status: *i64 = sys_mmap(8) as *i64 655 sys_wait4(pid, status, 0) 656 return wait_exit_code(*status) 657}