code wiki / (root) / nx_tls13_server_session_emit_certreq.nx

nx_tls13_server_session_emit_certreq.nx source

↩ module page · 147 lines · 7229 B

1// nx_tls13_server_session_emit_certreq.nx -- R7 rung 7a: emit CertificateRequest as a real encrypted server 2// handshake step. The on-wire half of mTLS rung R2 (the live terminator sends it; request-NOT-require). 3// 4// Sits between EncryptedExtensions and the server Certificate (RFC 8446 ยง4.3.2 ordering): EE, CertReq, Cert, 5// CertVerify, Finished. Mirrors nx_tls13_server_session_emit_ee exactly: build the CertReq inner bytes (via 6// the R2 builder, advertising Ed25519 0x0807 + ECDSA-P256 0x0403), AEAD-encrypt as a CT_HANDSHAKE record 7// under server_hs_traffic_key, write it, feed the PLAINTEXT into the transcript. State stays CERT_SENT (like 8// emit_certificate), so the existing emit_certificate -> emit_cv -> emit_sf chain is unchanged. 9// 10// REQUEST-NOT-REQUIRE / never-lockout: emitting this only ASKS the client for a cert; a client that presents 11// none still completes the handshake (the run loop's mTLS variant decides whether to require it for ACCESS). 12// license_tier: ORIGINAL expect_exit: 0 13import "nx_syscalls.nx" 14import "nx_tls13.nx" 15import "nx_tls13_record.nx" 16import "nx_tls13_transcript.nx" 17import "nx_tls13_server_session.nx" 18import "nx_tls13_emit_certificate_request.nx" // tls13_build_certificate_request / parse 19 20const NX_CRQ_REC_HEADER: i64 = 5 21const NX_CRQ_REC_TAG: i64 = 16 22 23func nx_tls13_server_session_emit_certreq( 24 session: *Tls13ServerSession, out: *u8, out_cap: i64 25) -> i64 { 26 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 27 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 28 if session.state != NX_TLS13_SSTATE_CERT_SENT { return 0 - NX_TLS13_SSESSION_BAD_STATE } 29 if (session.server_hs_traffic_key as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 30 if (session.server_hs_iv as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 31 32 // Build the CertificateRequest inner handshake bytes (Ed25519 + ECDSA-P256 advertised). 33 let schemes: *i64 = sys_mmap(8 * 4) as *i64 34 schemes[0] = SS_ED25519 35 schemes[1] = SS_ECDSA_SECP256R1_SHA256 36 let inner: *u8 = sys_mmap(64) 37 let inner_len: i64 = tls13_build_certificate_request(inner, 64, schemes, 2) 38 if inner_len < 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 39 40 let total_needed: i64 = NX_CRQ_REC_HEADER + inner_len + 1 + NX_CRQ_REC_TAG 41 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 42 let header_buf: *u8 = sys_mmap(NX_CRQ_REC_HEADER) 43 let ct_buf: *u8 = sys_mmap(inner_len + 17) 44 let tag_buf: *u8 = sys_mmap(NX_CRQ_REC_TAG) 45 let rv: i64 = nx_tls13_record_encrypt_v2( 46 session.cipher_suite, 47 session.server_hs_traffic_key, 48 session.server_hs_iv, 49 session.server_seq, 50 inner, inner_len, 51 CT_HANDSHAKE, 0, 52 header_buf, ct_buf, tag_buf) 53 if rv != NX_TLS13_REC_VERDICT_OK { return 0 - NX_TLS13_SSESSION_INTERNAL } 54 55 var w: i64 = 0 56 var i: i64 = 0 57 while i < NX_CRQ_REC_HEADER { out[w + i] = header_buf[i]; i = i + 1 } 58 w = w + NX_CRQ_REC_HEADER 59 let ct_len: i64 = inner_len + 1 60 var j: i64 = 0 61 while j < ct_len { out[w + j] = ct_buf[j]; j = j + 1 } 62 w = w + ct_len 63 var k: i64 = 0 64 while k < NX_CRQ_REC_TAG { out[w + k] = tag_buf[k]; k = k + 1 } 65 w = w + NX_CRQ_REC_TAG 66 67 // Transcript captures the PLAINTEXT handshake message (not the AEAD record). 68 nx_tls13_transcript_update(session.transcript, inner, inner_len) 69 session.server_seq = session.server_seq + 1 70 return w 71} 72 73// ===================== in-process gate: emit -> decrypt -> parse round-trip ===================== 74func crq_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 75func crq_row(name: *u8, ok: i64) -> i64 { if ok == 1 { crq_w(" PASS " as *u8) } else { crq_w(" FAIL " as *u8) } crq_w(name); crq_w("\n" as *u8); return ok } 76 77func main() -> i64 { 78 crq_w("nx_tls13 server emit_certreq gate (CertReq as an encrypted handshake record; R7 rung 7a)\n" as *u8) 79 // minimal server session with fixed handshake keys (no full ClientHello flow needed for this rung) 80 let s: *Tls13ServerSession = sys_mmap(264) as *Tls13ServerSession 81 s.cipher_suite = 0x1303 // ChaCha20-Poly1305 (32-byte key, 12-byte iv) 82 let key: *u8 = sys_mmap(32); var a: i64 = 0; while a < 32 { key[a] = (0x11 + a) as u8; a = a + 1 } 83 let iv: *u8 = sys_mmap(12); a = 0; while a < 12 { iv[a] = (0x22 + a) as u8; a = a + 1 } 84 s.server_hs_traffic_key = key 85 s.server_hs_iv = iv 86 s.server_seq = 0 87 s.transcript = nx_tls13_transcript_new() 88 s.state = NX_TLS13_SSTATE_CERT_SENT 89 90 let out: *u8 = sys_mmap(256) 91 let n: i64 = nx_tls13_server_session_emit_certreq(s, out, 256) 92 var pass: i64 = 0 93 94 var t1: i64 = 0 95 if n > 0 { t1 = 1 } 96 pass = pass + crq_row("T1 CertReq record emitted (n>0)" as *u8, t1) 97 98 var t2: i64 = 0 99 if (out[0] & 0xff) == 0x17 { t2 = 1 } // TLSCiphertext app_data wrapper 100 pass = pass + crq_row("T2 record is a TLSCiphertext (type 0x17 app_data)" as *u8, t2) 101 102 // decrypt with the same key/iv/seq=0 103 let body_len: i64 = ((out[3] as i64) << 8) | (out[4] as i64) 104 let ct_len: i64 = body_len - NX_CRQ_REC_TAG 105 let ct: *u8 = (out as i64 + NX_CRQ_REC_HEADER) as *u8 106 let tag: *u8 = (out as i64 + NX_CRQ_REC_HEADER + ct_len) as *u8 107 let pt: *u8 = sys_mmap(256) 108 let rct: *i64 = sys_mmap(8) as *i64 109 let rlen: *i64 = sys_mmap(8) as *i64 110 let dv: i64 = nx_tls13_record_decrypt_v2(0x1303, key, iv, 0, out, ct, ct_len, tag, pt, rct, rlen) 111 var t3: i64 = 0 112 if dv == NX_TLS13_REC_VERDICT_OK { if rct[0] == CT_HANDSHAKE { t3 = 1 } } 113 pass = pass + crq_row("T3 record decrypts to a CT_HANDSHAKE message" as *u8, t3) 114 115 // parse the decrypted inner as a CertificateRequest -> schemes [Ed25519, ECDSA-P256] 116 let cof: *i64 = sys_mmap(8) as *i64; let cln: *i64 = sys_mmap(8) as *i64 117 let eof: *i64 = sys_mmap(8) as *i64; let eln: *i64 = sys_mmap(8) as *i64 118 var t4: i64 = 0 119 if t3 == 1 { 120 if tls13_parse_certificate_request(pt, rlen[0], cof, cln, eof, eln) == 0 { 121 let got: *i64 = sys_mmap(8 * 8) as *i64 122 let cnt: i64 = tls13_certreq_extract_sigalgs(pt, eof[0], eln[0], got, 8) 123 if cnt == 2 { if got[0] == SS_ED25519 { if got[1] == SS_ECDSA_SECP256R1_SHA256 { t4 = 1 } } } 124 } 125 } 126 pass = pass + crq_row("T4 decrypted message is a CertReq advertising [Ed25519, ECDSA-P256]" as *u8, t4) 127 128 var t5: i64 = 0 129 if s.server_seq == 1 { t5 = 1 } 130 pass = pass + crq_row("T5 server_seq advanced (record consumes one sequence number)" as *u8, t5) 131 132 // transcript was updated (snapshot is non-zero / changed from fresh) 133 let th: *u8 = sys_mmap(32) 134 nx_tls13_transcript_snapshot(s.transcript, th) 135 var t6: i64 = 0 136 var z: i64 = 0 137 while z < 32 { if (th[z] & 0xff) != 0 { t6 = 1; z = 32 } else { z = z + 1 } } 138 pass = pass + crq_row("T6 transcript updated with the CertReq plaintext" as *u8, t6) 139 140 if pass == 6 { 141 crq_w("NX-TLS13-EMIT-CERTREQ GATE GREEN 6/6 (server sends CertReq on the wire; composes into the run loop)\n" as *u8) 142 sys_exit(0) 143 } 144 crq_w("NX-TLS13-EMIT-CERTREQ GATE RED\n" as *u8) 145 sys_exit(1) 146 return 1 147}