code wiki / (root) / nx_tls13_emit_certificate_request.nx

nx_tls13_emit_certificate_request.nx source

↩ module page · 193 lines · 9192 B

1// nx_tls13_emit_certificate_request.nx -- TLS 1.3 CertificateRequest (RFC 8446 §4.3.2) emit + parse. 2// 3// mTLS rung R2 of the NO-COOKIE session carrier (operator 2026-06-22: "we use opaque ... not cookies"). 4// The sovereign TLS 1.3 server is server-auth-only today; to authenticate the BROWSER at the TLS layer 5// (so a top-level navigation carries identity with zero cookie/header/JS) the server must REQUEST a client 6// cert. This file is the pure message PRIMITIVE -- bytes in / bytes out, no socket, no AEAD, no session -- 7// so it is gated in-process (sovereignty exemplar). R3 composes the builder into the live server run loop 8// (AEAD-encrypt after EncryptedExtensions + transcript) and adds receive+verify of the client's response. 9// 10// CertificateRequest (RFC 8446 §4.3.2), inside the Handshake wrapper (msg_type=13 + u24 len): 11// opaque certificate_request_context<0..2^8-1> // server-side: empty (1 len byte = 0x00) 12// Extension extensions<2..2^16-1> // MUST contain signature_algorithms (ext type 13) 13// signature_algorithms ext_data: u16 list_len + SignatureScheme[] (2 bytes each). 14// 15// Composes nx_tls13 (HT_/EXT_/SS_ constants + u16/u24 BE writers). license_tier: ORIGINAL expect_exit: 0 16import "nx_syscalls.nx" 17import "nx_tls13.nx" 18 19const NX_CR_OK: i64 = 0 20const NX_CR_BAD_INPUT: i64 = 1 21const NX_CR_TRUNCATED: i64 = 2 22const NX_CR_MAX_SCHEMES: i64 = 16 23 24// Verify the 4-byte Handshake header + bounded body; return body offset (4) or negative. 25func cr_check_hs(buf: *u8, n: i64, expected_type: i64) -> i64 { 26 if n < 4 { return 0 - NX_CR_TRUNCATED } 27 if (buf[0] & 0xff) != expected_type { return 0 - NX_CR_BAD_INPUT } 28 let body_len: i64 = tls_read_u24_be(buf, 1) 29 if 4 + body_len > n { return 0 - NX_CR_TRUNCATED } 30 return 4 31} 32 33// Build a full CertificateRequest handshake message into out[0..]. schemes[] are SignatureScheme codes 34// (e.g. SS_ED25519, SS_ECDSA_SECP256R1_SHA256). Returns total byte length or a negative NX_CR_* code. 35func tls13_build_certificate_request(out: *u8, cap: i64, schemes: *i64, n_schemes: i64) -> i64 { 36 if (out as i64) == 0 { return 0 - NX_CR_BAD_INPUT } 37 if (schemes as i64) == 0 { return 0 - NX_CR_BAD_INPUT } 38 if n_schemes < 1 { return 0 - NX_CR_BAD_INPUT } 39 if n_schemes > NX_CR_MAX_SCHEMES { return 0 - NX_CR_BAD_INPUT } 40 41 let sigalg_list_len: i64 = 2 * n_schemes // SignatureScheme[] bytes 42 let sigalg_ext_data_len: i64 = 2 + sigalg_list_len // u16 list_len + list 43 let sigalg_ext_len: i64 = 4 + sigalg_ext_data_len // ext_type(2) + ext_len(2) + data 44 let ext_total_len: i64 = sigalg_ext_len // exactly one extension 45 let body_len: i64 = 1 + 2 + ext_total_len // ctx_len(1)=0 + ext_total(2) + exts 46 let total: i64 = 4 + body_len // HT(1)+u24(3) + body 47 if cap < total { return 0 - NX_CR_TRUNCATED } 48 49 out[0] = HT_CERTIFICATE_REQUEST & 0xff 50 tls_write_u24_be(out, 1, body_len) 51 var o: i64 = 4 52 out[o] = 0 // certificate_request_context = empty 53 o = o + 1 54 tls_write_u16_be(out, o, ext_total_len); o = o + 2 55 tls_write_u16_be(out, o, EXT_SIGNATURE_ALGORITHMS); o = o + 2 56 tls_write_u16_be(out, o, sigalg_ext_data_len); o = o + 2 57 tls_write_u16_be(out, o, sigalg_list_len); o = o + 2 58 var i: i64 = 0 59 while i < n_schemes { 60 tls_write_u16_be(out, o, schemes[i]); o = o + 2 61 i = i + 1 62 } 63 return o 64} 65 66// Parse a CertificateRequest; expose certificate_request_context + extensions block offsets. OK = NX_CR_OK. 67func tls13_parse_certificate_request(buf: *u8, n: i64, 68 out_ctx_off: *i64, out_ctx_len: *i64, 69 out_exts_off: *i64, out_exts_len: *i64) -> i64 { 70 let bo: i64 = cr_check_hs(buf, n, HT_CERTIFICATE_REQUEST) 71 if bo < 0 { return 0 - NX_CR_BAD_INPUT } 72 var o: i64 = bo 73 if o + 1 > n { return 0 - NX_CR_TRUNCATED } 74 let ctx_len: i64 = buf[o] & 0xff 75 o = o + 1 76 if o + ctx_len > n { return 0 - NX_CR_TRUNCATED } 77 *out_ctx_off = o 78 *out_ctx_len = ctx_len 79 o = o + ctx_len 80 if o + 2 > n { return 0 - NX_CR_TRUNCATED } 81 let ext_len: i64 = tls_read_u16_be(buf, o) 82 o = o + 2 83 if o + ext_len > n { return 0 - NX_CR_TRUNCATED } 84 *out_exts_off = o 85 *out_exts_len = ext_len 86 return NX_CR_OK 87} 88 89// Walk the extensions block for signature_algorithms; write the schemes into out_schemes[0..max]. 90// Returns the count of schemes (>=0), or -1 if the signature_algorithms extension is absent/malformed. 91func tls13_certreq_extract_sigalgs(buf: *u8, exts_off: i64, exts_len: i64, out_schemes: *i64, max: i64) -> i64 { 92 var o: i64 = exts_off 93 let end: i64 = exts_off + exts_len 94 while o + 4 <= end { 95 let et: i64 = tls_read_u16_be(buf, o) 96 let el: i64 = tls_read_u16_be(buf, o + 2) 97 if o + 4 + el > end { return 0 - 1 } 98 if et == EXT_SIGNATURE_ALGORITHMS { 99 if el < 2 { return 0 - 1 } 100 let list_len: i64 = tls_read_u16_be(buf, o + 4) 101 if 2 + list_len > el { return 0 - 1 } 102 var p: i64 = o + 6 103 let pend: i64 = o + 6 + list_len 104 var count: i64 = 0 105 while p + 2 <= pend { 106 if count < max { out_schemes[count] = tls_read_u16_be(buf, p) } 107 count = count + 1 108 p = p + 2 109 } 110 return count 111 } 112 o = o + 4 + el 113 } 114 return 0 - 1 115} 116 117// ===== in-process gate (no socket/timing; sovereignty exemplar) ===== 118func cr_w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 119func cr_row(name: *u8, ok: i64) -> i64 { if ok == 1 { cr_w(" PASS " as *u8) } else { cr_w(" FAIL " as *u8) } cr_w(name); cr_w("\n" as *u8); return ok } 120 121func main() -> i64 { 122 cr_w("nx_tls13 CertificateRequest emit/parse gate (RFC 8446 4.3.2; no-cookie mTLS rung R2)\n" as *u8) 123 let schemes: *i64 = sys_mmap(8 * 4) as *i64 124 schemes[0] = SS_ED25519 125 schemes[1] = SS_ECDSA_SECP256R1_SHA256 126 let msg: *u8 = sys_mmap(256) 127 let total: i64 = tls13_build_certificate_request(msg, 256, schemes, 2) 128 var pass: i64 = 0 129 130 // T1: byte-exact length for a 2-scheme request 131 var t1: i64 = 0 132 if total == 17 { t1 = 1 } 133 pass = pass + cr_row("T1 built length == 17 bytes (2-scheme CertificateRequest)" as *u8, t1) 134 135 // T2: byte-exact KAT of the whole message 136 var t2: i64 = 1 137 if (msg[0] & 0xff) != 13 { t2 = 0 } // HT_CERTIFICATE_REQUEST 138 if (msg[1] & 0xff) != 0 { t2 = 0 } 139 if (msg[2] & 0xff) != 0 { t2 = 0 } 140 if (msg[3] & 0xff) != 13 { t2 = 0 } // body_len = 13 141 if (msg[4] & 0xff) != 0 { t2 = 0 } // certificate_request_context len = 0 142 if (msg[5] & 0xff) != 0 { t2 = 0 } 143 if (msg[6] & 0xff) != 10 { t2 = 0 } // extensions total len = 10 144 if (msg[7] & 0xff) != 0 { t2 = 0 } 145 if (msg[8] & 0xff) != 13 { t2 = 0 } // ext_type = 13 (signature_algorithms) 146 if (msg[9] & 0xff) != 0 { t2 = 0 } 147 if (msg[10] & 0xff) != 6 { t2 = 0 } // ext_data len = 6 148 if (msg[11] & 0xff) != 0 { t2 = 0 } 149 if (msg[12] & 0xff) != 4 { t2 = 0 } // sigalg list len = 4 150 if (msg[13] & 0xff) != 8 { t2 = 0 } // 0x08 151 if (msg[14] & 0xff) != 7 { t2 = 0 } // 0x07 -> Ed25519 (0x0807) 152 if (msg[15] & 0xff) != 4 { t2 = 0 } // 0x04 153 if (msg[16] & 0xff) != 3 { t2 = 0 } // 0x03 -> ECDSA-P256 (0x0403) 154 pass = pass + cr_row("T2 byte-exact KAT (msg_type / body / empty ctx / sig_algs ext / schemes)" as *u8, t2) 155 156 // T3 + T4: parse back, extract schemes (round-trip) 157 let cof: *i64 = sys_mmap(8) as *i64 158 let cln: *i64 = sys_mmap(8) as *i64 159 let eof: *i64 = sys_mmap(8) as *i64 160 let eln: *i64 = sys_mmap(8) as *i64 161 let prc: i64 = tls13_parse_certificate_request(msg, total, cof, cln, eof, eln) 162 var t3: i64 = 0 163 if prc == NX_CR_OK { if cln[0] == 0 { t3 = 1 } } 164 pass = pass + cr_row("T3 parse OK + certificate_request_context empty (server-side)" as *u8, t3) 165 166 let got: *i64 = sys_mmap(8 * 8) as *i64 167 let cnt: i64 = tls13_certreq_extract_sigalgs(msg, eof[0], eln[0], got, 8) 168 var t4: i64 = 0 169 if cnt == 2 { if got[0] == SS_ED25519 { if got[1] == SS_ECDSA_SECP256R1_SHA256 { t4 = 1 } } } 170 pass = pass + cr_row("T4 round-trip: extracted [Ed25519, ECDSA-P256] from signature_algorithms" as *u8, t4) 171 172 // T5: tampered msg_type rejected 173 let bad: *u8 = sys_mmap(256) 174 var bi: i64 = 0 175 while bi < total { bad[bi] = msg[bi]; bi = bi + 1 } 176 bad[0] = 99 as u8 177 var t5: i64 = 0 178 if tls13_parse_certificate_request(bad, total, cof, cln, eof, eln) != NX_CR_OK { t5 = 1 } 179 pass = pass + cr_row("T5 tampered msg_type (99) rejected" as *u8, t5) 180 181 // T6: truncated message rejected 182 var t6: i64 = 0 183 if tls13_parse_certificate_request(msg, total - 1, cof, cln, eof, eln) != NX_CR_OK { t6 = 1 } 184 pass = pass + cr_row("T6 truncated message rejected" as *u8, t6) 185 186 if pass == 6 { 187 cr_w("NX-TLS13-CERTREQ GATE GREEN 6/6 (server can request a client cert; primitive composes into R3)\n" as *u8) 188 sys_exit(0) 189 } 190 cr_w("NX-TLS13-CERTREQ GATE RED\n" as *u8) 191 sys_exit(1) 192 return 1 193}