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}