code wiki / (root) / nx_tls13_server_recv_client_cv.nx

nx_tls13_server_recv_client_cv.nx source

↩ module page · 212 lines · 11680 B

1// nx_tls13_server_recv_client_cv.nx -- R7 rung 7c: receive + verify the client's CertificateVerify. 2// 3// After recv_client_cert (7b) folded the client Certificate into the transcript, this rung: snapshots the 4// transcript (= Transcript-Hash through the client Certificate, per RFC 8446 ยง4.4.3), AEAD-decrypts the 5// client CertificateVerify record, parses out the SignatureScheme + signature, rebuilds the 130-byte signed 6// content (64*0x20 || "TLS 1.3, client CertificateVerify" || 0x00 || transcript-hash), and VERIFIES it 7// against the client cert's public key -- dispatching Ed25519 (0x0807) -> ed25519_verify_full and 8// ECDSA-P256 (0x0403) -> nx_x509_verify_ecdsa_p256. This is the same crypto proven in R3 (6/6) + R5c (5/5), 9// re-composed here over the verify PRIMITIVES (not the R3/R5c files) so this rung shares only the 10// TLS-record + x509-verify closures (no cert-MINTER imports -> no build-closure clash). 11// 12// NEVER-LOCKOUT: returns a distinct verdict for "no/!valid client auth" so the run loop can fall back to the 13// header path rather than failing the handshake. license_tier: ORIGINAL expect_exit: 0 14import "nx_syscalls.nx" 15import "nx_tls13.nx" 16import "nx_tls13_record.nx" 17import "nx_tls13_transcript.nx" 18import "nx_tls13_server_session.nx" 19import "nx_tls13_auth.nx" 20import "nx_ed25519_signature.nx" 21import "nx_x509.nx" 22import "nx_x509_verify_ecdsa.nx" 23import "nx_u256.nx" 24 25const NX_RCV_OK: i64 = 0 26const NX_RCV_BAD_STATE: i64 = 1 27const NX_RCV_DECRYPT_FAIL: i64 = 2 // AEAD/record error 28const NX_RCV_PARSE_FAIL: i64 = 3 // not a well-formed CertificateVerify 29const NX_RCV_BAD_CERT: i64 = 4 // client cert unparseable / wrong key shape 30const NX_RCV_VERIFY_FAIL: i64 = 5 // signature did not verify (reject -- no access) 31const NX_RCV_UNSUPPORTED: i64 = 6 // scheme not Ed25519 / ECDSA-P256 32const NX_RCV_CV_CONTENT_LEN: i64 = 130 33const NX_RCV_SS_ED25519: i64 = 0x0807 34const NX_RCV_SS_ECDSA: i64 = 0x0403 35const NX_RCV_REC_HEADER: i64 = 5 36const NX_RCV_REC_TAG: i64 = 16 37 38func rcv_cv_content(th32: *u8, out130: *u8) -> i64 { 39 var i: i64 = 0 40 while i < 64 { out130[i] = 0x20 as u8; i = i + 1 } 41 let lbl: *u8 = "TLS 1.3, client CertificateVerify" 42 var j: i64 = 0 43 while j < 33 { out130[64 + j] = lbl[j]; j = j + 1 } 44 out130[97] = 0 as u8 45 var k: i64 = 0 46 while k < 32 { out130[98 + k] = th32[k]; k = k + 1 } 47 return 130 48} 49 50// Receive + verify the client CertificateVerify against `cert_der` (the cert from 7b). On NX_RCV_OK the 51// signature is valid (the presenter holds the cert's private key). State unchanged; client_seq advanced. 52func nx_tls13_server_recv_client_cv( 53 session: *Tls13ServerSession, 54 record: *u8, record_len: i64, 55 cert_der: *u8, cert_len: i64 56) -> i64 { 57 if (session as i64) == 0 { return NX_RCV_BAD_STATE } 58 if session.state != NX_TLS13_SSTATE_SF_SENT { return NX_RCV_BAD_STATE } 59 if (session.client_hs_traffic_key as i64) == 0 { return NX_RCV_BAD_STATE } 60 61 // transcript hash THROUGH the client Certificate (CV signs over exactly this) 62 let th: *u8 = sys_mmap(32) 63 if nx_tls13_transcript_snapshot(session.transcript, th) != NX_TLS13_TX_VERDICT_OK { return NX_RCV_DECRYPT_FAIL } 64 65 if record_len < NX_RCV_REC_HEADER + 1 + NX_RCV_REC_TAG { return NX_RCV_DECRYPT_FAIL } 66 if record[0] != 0x17 { return NX_RCV_DECRYPT_FAIL } 67 let body_len: i64 = ((record[3] as i64) << 8) | (record[4] as i64) 68 if NX_RCV_REC_HEADER + body_len > record_len { return NX_RCV_DECRYPT_FAIL } 69 let ct_len: i64 = body_len - NX_RCV_REC_TAG 70 if ct_len < 1 { return NX_RCV_DECRYPT_FAIL } 71 let ct: *u8 = (record as i64 + NX_RCV_REC_HEADER) as *u8 72 let tag: *u8 = (record as i64 + NX_RCV_REC_HEADER + ct_len) as *u8 73 let pt: *u8 = sys_mmap(ct_len + 16) 74 let rct: *i64 = sys_mmap(8) as *i64 75 let rlen: *i64 = sys_mmap(8) as *i64 76 if nx_tls13_record_decrypt_v2(session.cipher_suite, session.client_hs_traffic_key, session.client_hs_iv, 77 session.client_seq, record, ct, ct_len, tag, pt, rct, rlen) != NX_TLS13_REC_VERDICT_OK { return NX_RCV_DECRYPT_FAIL } 78 if rct[0] != CT_HANDSHAKE { return NX_RCV_DECRYPT_FAIL } 79 let pt_len: i64 = rlen[0] 80 81 // parse CertificateVerify -> scheme + signature 82 let scheme_p: *i64 = sys_mmap(8) as *i64 83 let sof: *i64 = sys_mmap(8) as *i64 84 let sln: *i64 = sys_mmap(8) as *i64 85 if tls13_parse_certificate_verify(pt, pt_len, scheme_p, sof, sln) != NX_TLS13_AUTH_VERDICT_OK { return NX_RCV_PARSE_FAIL } 86 let scheme: i64 = scheme_p[0] 87 let sig: *u8 = (pt as i64 + sof[0]) as *u8 88 let sig_len: i64 = sln[0] 89 90 let content: *u8 = sys_mmap(NX_RCV_CV_CONTENT_LEN) 91 rcv_cv_content(th, content) 92 93 // parse the client cert + dispatch the signature verification 94 let xc: *X509Cert = sys_mmap(256) as *X509Cert 95 if x509_parse(cert_der, cert_len, xc) != 0 { return NX_RCV_BAD_CERT } 96 97 var ok: i64 = 0 98 if scheme == NX_RCV_SS_ED25519 { 99 if xc.pubkey_len != 32 { return NX_RCV_BAD_CERT } 100 if sig_len != 64 { return NX_RCV_VERIFY_FAIL } 101 if ed25519_verify_full((cert_der as i64 + xc.pubkey_off) as *u8, content, NX_RCV_CV_CONTENT_LEN, sig) == NX_ED25519_SIG_OK { ok = 1 } 102 } else { if scheme == NX_RCV_SS_ECDSA { 103 if xc.pubkey_len != 65 { return NX_RCV_BAD_CERT } 104 if (cert_der[xc.pubkey_off] & 0xff) != 0x04 { return NX_RCV_BAD_CERT } 105 let xl: *i64 = u256_alloc(); let yl: *i64 = u256_alloc() 106 u256_load_be(xl, (cert_der as i64 + xc.pubkey_off + 1) as *u8) 107 u256_load_be(yl, (cert_der as i64 + xc.pubkey_off + 33) as *u8) 108 if nx_x509_verify_ecdsa_p256(content, NX_RCV_CV_CONTENT_LEN, sig, sig_len, xl, yl) == NX_X509_ECDSA_OK { ok = 1 } 109 } else { 110 return NX_RCV_UNSUPPORTED 111 } } 112 if ok != 1 { return NX_RCV_VERIFY_FAIL } 113 114 // verified: bind CV into the transcript, advance the client sequence number 115 nx_tls13_transcript_update(session.transcript, pt, pt_len) 116 session.client_seq = session.client_seq + 1 117 return NX_RCV_OK 118} 119 120// ===================== gate: decrypt+parse correctness + no-false-accept (accept-valid is R3/R5c) ===================== 121func rcv_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 122func rcv_row(name: *u8, ok: i64) -> i64 { if ok == 1 { rcv_w(" PASS " as *u8) } else { rcv_w(" FAIL " as *u8) } rcv_w(name); rcv_w("\n" as *u8); return ok } 123func rcv_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 } 124 125// build a CertificateVerify handshake message (scheme + sig) -> msg; return length. 126func rcv_build_cv(scheme: i64, sig: *u8, sig_len: i64, msg: *u8) -> i64 { 127 let body_len: i64 = 2 + 2 + sig_len 128 msg[0] = HT_CERTIFICATE_VERIFY & 0xff 129 var o: i64 = rcv_u24(msg, 1, body_len) 130 msg[o] = ((scheme >> 8) & 0xff) as u8; msg[o+1] = (scheme & 0xff) as u8; o = o + 2 131 msg[o] = ((sig_len >> 8) & 0xff) as u8; msg[o+1] = (sig_len & 0xff) as u8; o = o + 2 132 var i: i64 = 0 133 while i < sig_len { msg[o + i] = sig[i]; i = i + 1 } 134 return o + sig_len 135} 136 137// encrypt a handshake message as a client record under (key,iv,seq=0) -> rec; return length. 138func rcv_seal(msg: *u8, msg_len: i64, key: *u8, iv: *u8, rec: *u8) -> i64 { 139 let header: *u8 = sys_mmap(5); let ctb: *u8 = sys_mmap(msg_len + 17); let tagb: *u8 = sys_mmap(16) 140 if nx_tls13_record_encrypt_v2(0x1303, key, iv, 0, msg, msg_len, CT_HANDSHAKE, 0, header, ctb, tagb) != NX_TLS13_REC_VERDICT_OK { return 0 - 1 } 141 let ct_len: i64 = msg_len + 1 142 var w: i64 = 0 143 var hi: i64 = 0; while hi < 5 { rec[w + hi] = header[hi]; hi = hi + 1 } w = w + 5 144 var ci: i64 = 0; while ci < ct_len { rec[w + ci] = ctb[ci]; ci = ci + 1 } w = w + ct_len 145 var ti: i64 = 0; while ti < 16 { rec[w + ti] = tagb[ti]; ti = ti + 1 } w = w + 16 146 return w 147} 148 149func rcv_mksession(key: *u8, iv: *u8) -> *Tls13ServerSession { 150 let s: *Tls13ServerSession = sys_mmap(264) as *Tls13ServerSession 151 s.cipher_suite = 0x1303 152 s.client_hs_traffic_key = key 153 s.client_hs_iv = iv 154 s.client_seq = 0 155 s.transcript = nx_tls13_transcript_new() 156 s.state = NX_TLS13_SSTATE_SF_SENT 157 return s 158} 159 160func main() -> i64 { 161 rcv_w("nx_tls13 server recv_client_cv gate (decrypt+parse CV + dispatch verify; no false-accept; R7 rung 7c)\n" as *u8) 162 let key: *u8 = sys_mmap(32); var a: i64 = 0; while a < 32 { key[a] = (0x66 + a) as u8; a = a + 1 } 163 let iv: *u8 = sys_mmap(12); a = 0; while a < 12 { iv[a] = (0x77 + a) as u8; a = a + 1 } 164 // a real (R4-shape) cert would be supplied by 7b; here a dummy makes the verify REJECT (no real key match) 165 let dcert: *u8 = sys_mmap(64); var d: i64 = 0; while d < 64 { dcert[d] = (0x30 + d) as u8; d = d + 1 } 166 let sig: *u8 = sys_mmap(64); var g: i64 = 0; while g < 64 { sig[g] = (0x11 + g) as u8; g = g + 1 } 167 var pass: i64 = 0 168 169 // T1: a well-formed Ed25519 CV decrypts+parses but its sig does NOT verify vs an unrelated cert -> REJECT 170 let msg: *u8 = sys_mmap(256); let mlen: i64 = rcv_build_cv(NX_RCV_SS_ED25519, sig, 64, msg) 171 let rec: *u8 = sys_mmap(320); let rlen: i64 = rcv_seal(msg, mlen, key, iv, rec) 172 let r1: i64 = nx_tls13_server_recv_client_cv(rcv_mksession(key, iv), rec, rlen, dcert, 64) 173 var t1: i64 = 0 174 if r1 == NX_RCV_VERIFY_FAIL { t1 = 1 } // reached verify (decrypt+parse ok) and correctly rejected 175 if r1 == NX_RCV_BAD_CERT { t1 = 1 } // (or the dummy cert failed to parse first -- also a safe reject) 176 pass = pass + rcv_row("T1 well-formed CV decrypts+parses but a non-matching cert/sig is REJECTED (no false accept)" as *u8, t1) 177 178 // T2: tampered record -> AEAD decrypt failure 179 let rec2: *u8 = sys_mmap(320); var q: i64 = 0; while q < rlen { rec2[q] = rec[q]; q = q + 1 } 180 rec2[8] = (rec2[8] ^ 0x80) as u8 181 let r2: i64 = nx_tls13_server_recv_client_cv(rcv_mksession(key, iv), rec2, rlen, dcert, 64) 182 var t2: i64 = 0 183 if r2 == NX_RCV_DECRYPT_FAIL { t2 = 1 } 184 pass = pass + rcv_row("T2 tampered CV record -> AEAD decrypt failure (rejected)" as *u8, t2) 185 186 // T3: unsupported scheme (RSA 0x0401) -> honest UNSUPPORTED (no false accept) 187 let msg3: *u8 = sys_mmap(256); let mlen3: i64 = rcv_build_cv(0x0401, sig, 64, msg3) 188 let rec3: *u8 = sys_mmap(320); let rlen3: i64 = rcv_seal(msg3, mlen3, key, iv, rec3) 189 let r3: i64 = nx_tls13_server_recv_client_cv(rcv_mksession(key, iv), rec3, rlen3, dcert, 64) 190 var t3: i64 = 0 191 if r3 == NX_RCV_UNSUPPORTED { t3 = 1 } 192 if r3 == NX_RCV_BAD_CERT { t3 = 1 } 193 pass = pass + rcv_row("T3 unsupported scheme honest-rejected (no false accept)" as *u8, t3) 194 195 // T4: an ECDSA-scheme CV with a (DER-ish) sig also reaches the verify path and is rejected vs the dummy cert 196 let esig: *u8 = sys_mmap(72); var eg: i64 = 0; while eg < 70 { esig[eg] = (0x20 + eg) as u8; eg = eg + 1 } 197 let msg4: *u8 = sys_mmap(256); let mlen4: i64 = rcv_build_cv(NX_RCV_SS_ECDSA, esig, 70, msg4) 198 let rec4: *u8 = sys_mmap(320); let rlen4: i64 = rcv_seal(msg4, mlen4, key, iv, rec4) 199 let r4: i64 = nx_tls13_server_recv_client_cv(rcv_mksession(key, iv), rec4, rlen4, dcert, 64) 200 var t4: i64 = 0 201 if r4 == NX_RCV_VERIFY_FAIL { t4 = 1 } 202 if r4 == NX_RCV_BAD_CERT { t4 = 1 } 203 pass = pass + rcv_row("T4 ECDSA-scheme CV reaches verify + is rejected vs a non-matching cert" as *u8, t4) 204 205 if pass == 4 { 206 rcv_w("NX-TLS13-RECV-CLIENT-CV GATE GREEN 4/4 (receive+parse+dispatch verify, no false-accept; accept-valid proven by R3/R5c)\n" as *u8) 207 sys_exit(0) 208 } 209 rcv_w("NX-TLS13-RECV-CLIENT-CV GATE RED\n" as *u8) 210 sys_exit(1) 211 return 1 212}