code wiki / (root) / nx_tls13_mtls_loopback_test.nx

nx_tls13_mtls_loopback_test.nx source

↩ module page · 166 lines · 10553 B

1// nx_tls13_mtls_loopback_test.nx -- SOVEREIGN end-to-end MUTUAL-AUTH proof: the capstone of the no-cookie carrier. 2// 3// nx_tls13_loopback_test proves a server-auth-only handshake reaches CONNECTED with both sides agreeing on keys. 4// The R3 client-auth gate proves the CertificateVerify CRYPTO -- but over an ARBITRARY transcript hash. NEITHER 5// proves the load-bearing mTLS property: that a REAL client signature, over the REAL mutually-agreed handshake 6// transcript (INCLUDING the server's CertificateRequest), is verified by the server END-TO-END. 7// 8// This gate closes that last gap. One main() drives BOTH halves of an mTLS 1.3 handshake message-by-message, 9// maintaining two INDEPENDENT transcripts (client + server) exactly as the wire would, then proves: 10// T1 both transcripts AGREE through the client's Certificate (the snapshot the client signs over), 11// T2 the client's REAL Ed25519 CertificateVerify over that snapshot VERIFIES on the server + recovers identity, 12// T3 a downgrade-MITM that STRIPS the CertificateRequest diverges the transcript -> verify FAILS, 13// T4 a single flipped transcript byte -> verify FAILS (bound to THIS handshake), 14// T5 another identity's cert cannot ride the signature (no impersonation), 15// T6 a tampered signature -> verify FAILS. 16// 17// Real R2 CertificateRequest bytes + real R4 minted client cert + real Ed25519 signature + real R3 server verify, 18// bound over a real (sha256) transcript both sides compute independently. No socket, no browser, no external 19// anything -- the codebase's canonical loopback hand-builds its flight the same way. This is the in-process proof 20// that the run loop's mutual-auth LOGIC is correct; the live browser then only tests INTEROP (does Chrome present 21// the cert), not correctness -- the one thing no Nishi component can stand in for. 22// expect_exit: 0 license_tier: ORIGINAL 23import "nx_syscalls.nx" 24import "nx_tls13.nx" 25import "nx_tls13_transcript.nx" 26import "nx_tls13_emit_certificate_request.nx" 27import "nx_tls13_server_clientauth.nx" 28 29func ml_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 30func ml_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 31func ml_row(name: *u8, ok: i64) -> i64 { if ok == 1 { ml_w(" PASS " as *u8) } else { ml_w(" FAIL " as *u8) } ml_w(name); ml_w("\n" as *u8); return ok } 32func ml_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 } 33 34// a representative handshake message: htype(1) + u24 body_len + body filler keyed by `tag`. returns total length. 35func ml_filler_msg(buf: *u8, htype: i64, body_len: i64, tag: i64) -> i64 { 36 buf[0] = htype & 0xff 37 ml_u24(buf, 1, body_len) 38 var i: i64 = 0 39 while i < body_len { buf[4 + i] = ((tag + i) & 0xff) as u8; i = i + 1 } 40 return 4 + body_len 41} 42 43// a real client Certificate handshake message wrapping the leaf cert DER (RFC 8446 4.4.2). returns total length. 44func ml_client_cert_msg(buf: *u8, leaf: *u8, leaf_len: i64) -> i64 { 45 let cert_list_len: i64 = 3 + leaf_len + 2 // cert_len(3) + cert + ext_len(2) 46 let body_len: i64 = 1 + 3 + cert_list_len // ctx_len(1)=0 + cert_list_len(3) + list 47 buf[0] = HT_CERTIFICATE & 0xff 48 ml_u24(buf, 1, body_len) 49 var o: i64 = 4 50 buf[o] = 0 as u8; o = o + 1 // certificate_request_context = empty 51 o = ml_u24(buf, o, cert_list_len) 52 o = ml_u24(buf, o, leaf_len) 53 var i: i64 = 0 54 while i < leaf_len { buf[o + i] = leaf[i]; i = i + 1 } o = o + leaf_len 55 buf[o] = 0 as u8; buf[o+1] = 0 as u8; o = o + 2 // extensions = empty 56 return o 57} 58 59func main() -> i64 { 60 ml_w("nx_tls13 mTLS LOOPBACK gate -- end-to-end mutual auth over a REAL agreed transcript (no socket, no browser)\n" as *u8) 61 62 // ---- client identity: the browser's minted cert from the OPAQUE export_key (R4/R5) ---- 63 let seed: *u8 = sys_mmap(32) 64 var i: i64 = 0 65 while i < 32 { seed[i] = (0x31 + i) as u8; i = i + 1 } 66 let pub: *u8 = sys_mmap(32) 67 let cn: *u8 = "nishi-uid-aabbccddeeff00112233445566" as *u8 68 let leaf: *u8 = sys_mmap(4096) 69 let leaf_len: *i64 = sys_mmap(8) as *i64 70 if ca_mint(seed, pub, cn, ml_len(cn), leaf, 4096, leaf_len) != 0 { ml_w("MINT FAIL\n" as *u8); sys_exit(1) } 71 72 // ---- the handshake flight: representative CH/SH/EE/server-Cert/server-CV/SF + REAL R2 CertReq + REAL client Cert ---- 73 let ch: *u8 = sys_mmap(64); let ch_n: i64 = ml_filler_msg(ch, HT_CLIENT_HELLO, 28, 0x10) 74 let sh: *u8 = sys_mmap(64); let sh_n: i64 = ml_filler_msg(sh, HT_SERVER_HELLO, 24, 0x40) 75 let ee: *u8 = sys_mmap(16); let ee_n: i64 = ml_filler_msg(ee, HT_ENCRYPTED_EXTENSIONS, 2, 0) 76 let schemes: *i64 = sys_mmap(32) as *i64; schemes[0] = SS_ED25519; schemes[1] = SS_ECDSA_SECP256R1_SHA256 77 let cr: *u8 = sys_mmap(64); let cr_n: i64 = tls13_build_certificate_request(cr, 64, schemes, 2) 78 let sc: *u8 = sys_mmap(64); let sc_n: i64 = ml_filler_msg(sc, HT_CERTIFICATE, 24, 0x50) 79 let scv: *u8 = sys_mmap(128); let scv_n: i64 = ml_filler_msg(scv, HT_CERTIFICATE_VERIFY, 68, 0x60) 80 let sf: *u8 = sys_mmap(64); let sf_n: i64 = ml_filler_msg(sf, HT_FINISHED, 32, 0x70) 81 let cc: *u8 = sys_mmap(4096); let cc_n: i64 = ml_client_cert_msg(cc, leaf, leaf_len[0]) 82 83 // ---- two INDEPENDENT transcripts, folded message-by-message exactly as the wire (client + server) ---- 84 let c_tx: *u8 = nx_tls13_transcript_new() 85 let s_tx: *u8 = nx_tls13_transcript_new() 86 nx_tls13_transcript_update(c_tx, ch, ch_n); nx_tls13_transcript_update(s_tx, ch, ch_n) 87 nx_tls13_transcript_update(c_tx, sh, sh_n); nx_tls13_transcript_update(s_tx, sh, sh_n) 88 nx_tls13_transcript_update(c_tx, ee, ee_n); nx_tls13_transcript_update(s_tx, ee, ee_n) 89 nx_tls13_transcript_update(c_tx, cr, cr_n); nx_tls13_transcript_update(s_tx, cr, cr_n) 90 nx_tls13_transcript_update(c_tx, sc, sc_n); nx_tls13_transcript_update(s_tx, sc, sc_n) 91 nx_tls13_transcript_update(c_tx, scv, scv_n); nx_tls13_transcript_update(s_tx, scv, scv_n) 92 nx_tls13_transcript_update(c_tx, sf, sf_n); nx_tls13_transcript_update(s_tx, sf, sf_n) 93 nx_tls13_transcript_update(c_tx, cc, cc_n); nx_tls13_transcript_update(s_tx, cc, cc_n) 94 95 let c_th: *u8 = sys_mmap(32); nx_tls13_transcript_snapshot(c_tx, c_th) 96 let s_th: *u8 = sys_mmap(32); nx_tls13_transcript_snapshot(s_tx, s_th) 97 98 var pass: i64 = 0 99 100 // T1: both sides agree on the mutual-auth transcript THROUGH the client's Certificate (the signed snapshot) 101 var t1: i64 = 1 102 var a: i64 = 0 103 while a < 32 { if (c_th[a] & 0xff) != (s_th[a] & 0xff) { t1 = 0; a = 32 } else { a = a + 1 } } 104 pass = pass + ml_row("T1 client+server transcripts AGREE through the client Certificate" as *u8, t1) 105 106 // ---- the CLIENT signs its CertificateVerify over the REAL agreed transcript (its own snapshot c_th) ---- 107 let content: *u8 = sys_mmap(130) 108 tls13_build_client_cv_content(c_th, content) 109 let cv_sig: *u8 = sys_mmap(64) 110 if ed25519_sign_full(seed, content, 130, cv_sig) != 0 { ml_w("SIGN FAIL\n" as *u8); sys_exit(1) } 111 let idpub: *u8 = sys_mmap(32) 112 113 // T2: the genuine client CertificateVerify VERIFIES on the server over ITS OWN snapshot + recovers identity 114 var t2: i64 = 0 115 if tls13_verify_client_auth_ed25519(leaf, leaf_len[0], NX_CA_SS_ED25519, cv_sig, 64, s_th, idpub) == NX_CA_OK { 116 t2 = 1 117 var j: i64 = 0 118 while j < 32 { if idpub[j] != pub[j] { t2 = 0; j = 32 } else { j = j + 1 } } 119 } 120 pass = pass + ml_row("T2 client signature over the agreed transcript VERIFIES + recovers identity (mutual auth)" as *u8, t2) 121 122 // T3: a downgrade-MITM that STRIPS the CertificateRequest diverges the transcript -> verify FAILS 123 let s_tx2: *u8 = nx_tls13_transcript_new() 124 nx_tls13_transcript_update(s_tx2, ch, ch_n); nx_tls13_transcript_update(s_tx2, sh, sh_n) 125 nx_tls13_transcript_update(s_tx2, ee, ee_n) 126 // (CertificateRequest deliberately NOT folded -- the stripped-mTLS-request downgrade) 127 nx_tls13_transcript_update(s_tx2, sc, sc_n); nx_tls13_transcript_update(s_tx2, scv, scv_n) 128 nx_tls13_transcript_update(s_tx2, sf, sf_n); nx_tls13_transcript_update(s_tx2, cc, cc_n) 129 let s_th2: *u8 = sys_mmap(32); nx_tls13_transcript_snapshot(s_tx2, s_th2) 130 var t3: i64 = 0 131 if tls13_verify_client_auth_ed25519(leaf, leaf_len[0], NX_CA_SS_ED25519, cv_sig, 64, s_th2, idpub) != NX_CA_OK { t3 = 1 } 132 pass = pass + ml_row("T3 stripping CertificateRequest diverges the transcript -> verify FAILS (downgrade caught)" as *u8, t3) 133 134 // T4: a single flipped transcript byte -> verify FAILS (the signature is bound to THIS handshake) 135 let s_th3: *u8 = sys_mmap(32); var b: i64 = 0 136 while b < 32 { s_th3[b] = s_th[b]; b = b + 1 } 137 s_th3[7] = (s_th3[7] ^ 0x01) as u8 138 var t4: i64 = 0 139 if tls13_verify_client_auth_ed25519(leaf, leaf_len[0], NX_CA_SS_ED25519, cv_sig, 64, s_th3, idpub) != NX_CA_OK { t4 = 1 } 140 pass = pass + ml_row("T4 one flipped transcript byte -> verify FAILS (bound to THIS handshake)" as *u8, t4) 141 142 // T5: another identity's cert cannot ride this signature (no impersonation) 143 let seed2: *u8 = sys_mmap(32); var c: i64 = 0 144 while c < 32 { seed2[c] = (0x77 + c) as u8; c = c + 1 } 145 let pub2: *u8 = sys_mmap(32); let leaf2: *u8 = sys_mmap(4096); let leaf2_len: *i64 = sys_mmap(8) as *i64 146 ca_mint(seed2, pub2, "nishi-uid-deadbeefdeadbeefdeadbeef00" as *u8, 36, leaf2, 4096, leaf2_len) 147 var t5: i64 = 0 148 if tls13_verify_client_auth_ed25519(leaf2, leaf2_len[0], NX_CA_SS_ED25519, cv_sig, 64, s_th, idpub) != NX_CA_OK { t5 = 1 } 149 pass = pass + ml_row("T5 another identity's cert cannot ride this signature (no impersonation)" as *u8, t5) 150 151 // T6: a tampered CertificateVerify signature -> verify FAILS 152 let badsig: *u8 = sys_mmap(64); var d: i64 = 0 153 while d < 64 { badsig[d] = cv_sig[d]; d = d + 1 } 154 badsig[5] = (badsig[5] ^ 0x20) as u8 155 var t6: i64 = 0 156 if tls13_verify_client_auth_ed25519(leaf, leaf_len[0], NX_CA_SS_ED25519, badsig, 64, s_th, idpub) != NX_CA_OK { t6 = 1 } 157 pass = pass + ml_row("T6 tampered CertificateVerify signature -> verify FAILS" as *u8, t6) 158 159 if pass == 6 { 160 ml_w("NX-TLS13-MTLS-LOOPBACK GATE GREEN 6/6 (mutual auth verified end-to-end over a real agreed transcript; sovereign, no browser)\n" as *u8) 161 sys_exit(0) 162 } 163 ml_w("NX-TLS13-MTLS-LOOPBACK GATE RED\n" as *u8) 164 sys_exit(1) 165 return 1 166}