code wiki / (root) / nx_tls13_server_recv_client_cert.nx

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}