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}