nx_tls13_server_recv_client_cert.nx source
↩ module page · 171 lines · 9341 B
1// nx_tls13_server_recv_client_cert.nx -- R7 rung 7b: the server receives the client's Certificate record.
2//
3// In mTLS, after the server's Finished the client sends (encrypted under client_hs_traffic_key): Certificate,
4// CertificateVerify, Finished. This rung handles the first: AEAD-decrypt the client Certificate record (mirror
5// recv_cf's decrypt), confirm it is an HT_CERTIFICATE handshake message, parse out the client's leaf cert DER
6// (via the in-tree nx_tls13_auth parser), and feed the PLAINTEXT into the transcript -- so the subsequent
7// CertificateVerify can be checked against Transcript-Hash(... up to and including this Certificate) per
8// RFC 8446 ยง4.4.3. The CV verification itself is the proven R3 (Ed25519) / R5c (ECDSA) path; this rung only
9// receives + extracts + binds-to-transcript, so it stays purely in the TLS-record closure (no cert-verify
10// imports -> no closure clash). license_tier: ORIGINAL expect_exit: 0
11import "nx_syscalls.nx"
12import "nx_tls13.nx"
13import "nx_tls13_record.nx"
14import "nx_tls13_transcript.nx"
15import "nx_tls13_server_session.nx"
16import "nx_tls13_auth.nx"
17
18const NX_RCC_REC_HEADER: i64 = 5
19const NX_RCC_REC_TAG: i64 = 16
20
21// Receive + decrypt the client Certificate record; copy the leaf cert DER to out_cert; add the plaintext
22// Certificate message to the transcript. Precondition: state == SF_SENT (server has sent its Finished).
23// Returns NX_TLS13_SSESSION_OK on success. State is left unchanged (the run loop sequences cert->cv->finished).
24func nx_tls13_server_recv_client_cert(
25 session: *Tls13ServerSession,
26 record: *u8, record_len: i64,
27 out_cert: *u8, out_cert_cap: i64, out_cert_len: *i64
28) -> i64 {
29 if (session as i64) == 0 { return NX_TLS13_SSESSION_BAD_STATE }
30 if (record as i64) == 0 { return NX_TLS13_SSESSION_BAD_STATE }
31 if session.state != NX_TLS13_SSTATE_SF_SENT { return NX_TLS13_SSESSION_BAD_STATE }
32 if (session.client_hs_traffic_key as i64) == 0 { return NX_TLS13_SSESSION_INTERNAL }
33
34 if record_len < NX_RCC_REC_HEADER + 1 + NX_RCC_REC_TAG { return NX_TLS13_SSESSION_PROTOCOL_ERR }
35 if record[0] != 0x17 { return NX_TLS13_SSESSION_PROTOCOL_ERR } // app_data wrapper
36 let body_len: i64 = ((record[3] as i64) << 8) | (record[4] as i64)
37 if NX_RCC_REC_HEADER + body_len > record_len { return NX_TLS13_SSESSION_PROTOCOL_ERR }
38 let ct_len: i64 = body_len - NX_RCC_REC_TAG
39 if ct_len < 1 { return NX_TLS13_SSESSION_PROTOCOL_ERR }
40 let ct: *u8 = (record as i64 + NX_RCC_REC_HEADER) as *u8
41 let tag: *u8 = (record as i64 + NX_RCC_REC_HEADER + ct_len) as *u8
42
43 let pt: *u8 = sys_mmap(ct_len + 16)
44 let rct: *i64 = sys_mmap(8) as *i64
45 let rlen: *i64 = sys_mmap(8) as *i64
46 let dv: i64 = nx_tls13_record_decrypt_v2(
47 session.cipher_suite,
48 session.client_hs_traffic_key,
49 session.client_hs_iv,
50 session.client_seq,
51 record, ct, ct_len, tag, pt, rct, rlen)
52 if dv != NX_TLS13_REC_VERDICT_OK { return NX_TLS13_SSESSION_PROTOCOL_ERR }
53 if rct[0] != CT_HANDSHAKE { return NX_TLS13_SSESSION_PROTOCOL_ERR }
54 let pt_len: i64 = rlen[0]
55 if pt_len < 4 { return NX_TLS13_SSESSION_PROTOCOL_ERR }
56 if (pt[0] & 0xff) != (HT_CERTIFICATE & 0xff) { return NX_TLS13_SSESSION_PROTOCOL_ERR }
57
58 // Parse the Certificate message -> the leaf cert DER offsets.
59 let cof: *i64 = sys_mmap(8) as *i64; let cln: *i64 = sys_mmap(8) as *i64
60 let crof: *i64 = sys_mmap(8) as *i64; let crln: *i64 = sys_mmap(8) as *i64
61 let exof: *i64 = sys_mmap(8) as *i64; let exln: *i64 = sys_mmap(8) as *i64
62 let chl: *i64 = sys_mmap(8) as *i64
63 if tls13_parse_certificate_chain_first(pt, pt_len, cof, cln, crof, crln, exof, exln, chl) != NX_TLS13_AUTH_VERDICT_OK {
64 return NX_TLS13_SSESSION_PROTOCOL_ERR
65 }
66 if crln[0] > out_cert_cap { return NX_TLS13_SSESSION_BUF_OVERFLOW }
67 var i: i64 = 0
68 while i < crln[0] { out_cert[i] = pt[crof[0] + i]; i = i + 1 }
69 out_cert_len[0] = crln[0]
70
71 // Bind the Certificate into the transcript, advance the client sequence number.
72 nx_tls13_transcript_update(session.transcript, pt, pt_len)
73 session.client_seq = session.client_seq + 1
74 return NX_TLS13_SSESSION_OK
75}
76
77// ===================== in-process gate: encrypt a client Certificate record -> recv -> recover the cert =====================
78func rcc_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
79func rcc_row(name: *u8, ok: i64) -> i64 { if ok == 1 { rcc_w(" PASS " as *u8) } else { rcc_w(" FAIL " as *u8) } rcc_w(name); rcc_w("\n" as *u8); return ok }
80func rcc_u24(b: *u8, o: i64, v: i64) -> i64 { b[o] = ((v >> 16) & 0xff) as u8; b[o+1] = ((v >> 8) & 0xff) as u8; b[o+2] = (v & 0xff) as u8; return o + 3 }
81
82func main() -> i64 {
83 rcc_w("nx_tls13 server recv_client_cert gate (decrypt + parse a client Certificate record; R7 rung 7b)\n" as *u8)
84
85 // a dummy client leaf cert DER (the receive path only extracts bytes; real validity is the R3/R5c verify)
86 let dlen: i64 = 48
87 let dcert: *u8 = sys_mmap(dlen)
88 var d: i64 = 0
89 while d < dlen { dcert[d] = (0x30 + (d % 64)) as u8; d = d + 1 }
90
91 // build the Certificate handshake message:
92 // HT_CERTIFICATE | u24 body | [ctx_len=0x00] [cert_list u24] [u24 cert_len][cert][u16 ext=0x0000]
93 let entry_len: i64 = 3 + dlen + 2
94 let list_len: i64 = entry_len
95 let body_len: i64 = 1 + 3 + list_len
96 let msg: *u8 = sys_mmap(256)
97 msg[0] = HT_CERTIFICATE & 0xff
98 var o: i64 = rcc_u24(msg, 1, body_len) // o = 4
99 msg[o] = 0 as u8; o = o + 1 // certificate_request_context = empty
100 o = rcc_u24(msg, o, list_len) // certificate_list length
101 o = rcc_u24(msg, o, dlen) // CertificateEntry cert_len
102 var c: i64 = 0
103 while c < dlen { msg[o + c] = dcert[c]; c = c + 1 }
104 o = o + dlen
105 msg[o] = 0 as u8; msg[o+1] = 0 as u8; o = o + 2 // entry extensions = empty
106 let msg_len: i64 = o
107
108 // encrypt it as a client handshake record under a fixed client key
109 let key: *u8 = sys_mmap(32); var a: i64 = 0; while a < 32 { key[a] = (0x44 + a) as u8; a = a + 1 }
110 let iv: *u8 = sys_mmap(12); a = 0; while a < 12 { iv[a] = (0x55 + a) as u8; a = a + 1 }
111 let header: *u8 = sys_mmap(5); let ctb: *u8 = sys_mmap(msg_len + 17); let tagb: *u8 = sys_mmap(16)
112 if nx_tls13_record_encrypt_v2(0x1303, key, iv, 0, msg, msg_len, CT_HANDSHAKE, 0, header, ctb, tagb) != NX_TLS13_REC_VERDICT_OK { rcc_w("ENC FAIL\n" as *u8); sys_exit(1) }
113 let ct_len: i64 = msg_len + 1
114 let rec: *u8 = sys_mmap(msg_len + 64); var w: i64 = 0
115 var hi: i64 = 0; while hi < 5 { rec[w + hi] = header[hi]; hi = hi + 1 } w = w + 5
116 var ci: i64 = 0; while ci < ct_len { rec[w + ci] = ctb[ci]; ci = ci + 1 } w = w + ct_len
117 var ti: i64 = 0; while ti < 16 { rec[w + ti] = tagb[ti]; ti = ti + 1 } w = w + 16
118
119 // server session with the matching client key
120 let s: *Tls13ServerSession = sys_mmap(264) as *Tls13ServerSession
121 s.cipher_suite = 0x1303
122 s.client_hs_traffic_key = key
123 s.client_hs_iv = iv
124 s.client_seq = 0
125 s.transcript = nx_tls13_transcript_new()
126 s.state = NX_TLS13_SSTATE_SF_SENT
127
128 let got: *u8 = sys_mmap(256); let glen: *i64 = sys_mmap(8) as *i64
129 let rv: i64 = nx_tls13_server_recv_client_cert(s, rec, w, got, 256, glen)
130 var pass: i64 = 0
131
132 var t1: i64 = 0
133 if rv == NX_TLS13_SSESSION_OK { t1 = 1 }
134 pass = pass + rcc_row("T1 client Certificate record decrypted + parsed" as *u8, t1)
135
136 var t2: i64 = 0
137 if glen[0] == dlen { t2 = 1 }
138 pass = pass + rcc_row("T2 extracted leaf cert is the right length" as *u8, t2)
139
140 var t3: i64 = 1
141 if t2 == 1 { var j: i64 = 0; while j < dlen { if got[j] != dcert[j] { t3 = 0; j = dlen } else { j = j + 1 } } } else { t3 = 0 }
142 pass = pass + rcc_row("T3 extracted leaf cert bytes are byte-identical to what the client sent" as *u8, t3)
143
144 var t4: i64 = 0
145 if s.client_seq == 1 { t4 = 1 }
146 pass = pass + rcc_row("T4 client_seq advanced (record consumed)" as *u8, t4)
147
148 let th: *u8 = sys_mmap(32); nx_tls13_transcript_snapshot(s.transcript, th)
149 var t5: i64 = 0
150 var z: i64 = 0
151 while z < 32 { if (th[z] & 0xff) != 0 { t5 = 1; z = 32 } else { z = z + 1 } }
152 pass = pass + rcc_row("T5 Certificate folded into the transcript (for CertificateVerify)" as *u8, t5)
153
154 // a corrupted record (flip a ciphertext byte) must be rejected (AEAD tag fails)
155 let rec2: *u8 = sys_mmap(msg_len + 64); var q: i64 = 0; while q < w { rec2[q] = rec[q]; q = q + 1 }
156 rec2[7] = (rec2[7] ^ 0x80) as u8
157 let s2: *Tls13ServerSession = sys_mmap(264) as *Tls13ServerSession
158 s2.cipher_suite = 0x1303; s2.client_hs_traffic_key = key; s2.client_hs_iv = iv; s2.client_seq = 0
159 s2.transcript = nx_tls13_transcript_new(); s2.state = NX_TLS13_SSTATE_SF_SENT
160 var t6: i64 = 0
161 if nx_tls13_server_recv_client_cert(s2, rec2, w, got, 256, glen) != NX_TLS13_SSESSION_OK { t6 = 1 }
162 pass = pass + rcc_row("T6 tampered record rejected (AEAD integrity)" as *u8, t6)
163
164 if pass == 6 {
165 rcc_w("NX-TLS13-RECV-CLIENT-CERT GATE GREEN 6/6 (server receives the browser's cert; composes into the run loop)\n" as *u8)
166 sys_exit(0)
167 }
168 rcc_w("NX-TLS13-RECV-CLIENT-CERT GATE RED\n" as *u8)
169 sys_exit(1)
170 return 1
171}