nx_tls13_server_session_emit_certificate.nx source
↩ module page · 278 lines · 10949 B
1// nx_tls13_server_session_emit_certificate.nx -- stone 6.5 (final
2// core TLS server stone).
3//
4// Emits the server Certificate handshake message: caller supplies
5// pre-encoded DER cert bytes, substrate wraps them in the TLS 1.3
6// Certificate structure + AEAD-encrypted record.
7//
8// Per RFC 8446 §4.4.2 Certificate message body:
9// opaque certificate_request_context<0..2^8-1>; // server: empty
10// CertificateEntry certificate_list<0..2^24-1>;
11//
12// CertificateEntry:
13// opaque cert_data<1..2^24-1>; // DER cert
14// Extension extensions<0..2^16-1>; // empty for v1
15//
16// Wire layout (one cert, no extensions):
17// HT_CERTIFICATE (0x0b) (1 byte)
18// u24 body_len (3 bytes)
19// u8 cert_request_ctx_len = 0 (1 byte)
20// u24 cert_list_len (3 bytes)
21// u24 cert_data_len (3 bytes)
22// cert_data (cert_data_len bytes)
23// u16 extensions_len = 0 (2 bytes)
24//
25// Caller responsibility:
26// - Supply a valid DER-encoded X.509 cert (from ACME, self-signed
27// test cert, etc.). Substrate does NOT validate the DER -- it
28// just wraps the bytes. Per [[feedback-no-third-party-trust-
29// native-or-nothing]]: future stone can compose nx_x509 +
30// nx_acme to produce the cert bits-up.
31//
32// Substrate provides: WIRE FORMAT. Operator provides: cert bytes.
33// Together = real-world-client-acceptable server.
34//
35// State machine: requires EE_SENT (set by derive_hs_secrets after
36// emit_ee actually emits). Wait -- the existing flow sets state
37// EE_SENT after derive_hs but PRE-emit_ee; emit_ee then sets
38// CERT_SENT. This stone, IF inserted, would run AFTER emit_ee, so
39// require CERT_SENT and advance to ... a new state.
40//
41// Cleaner design: this stone requires CERT_SENT (set by emit_ee)
42// and advances to CERT_VERIFIED state... but we don't have that
43// state. Pragmatic: require CERT_SENT, advance to CERT_SENT (same
44// state -- the message bytes are written; next state still expects
45// CV). No: that would let emit_cv NOT be called and emit_sf
46// happily proceed from CERT_SENT -- which is what the existing
47// anonymous-stub path does anyway.
48//
49// Cleanest pragmatic path: this stone is the AUTHENTICATED path's
50// "Certificate" message; it runs BETWEEN emit_ee and emit_cv.
51// Require CERT_SENT, advance to CERT_SENT. The state name keeps
52// the existing semantics; the message has just been emitted as
53// well as transcript-updated. emit_cv runs next.
54//
55// license_tier: ORIGINAL
56
57import "nx_syscalls.nx"
58import "nx_tls13.nx"
59import "nx_tls13_record.nx"
60import "nx_tls13_transcript.nx"
61import "nx_tls13_server_session.nx"
62const K_MAGIC_65536: i64 = 65536
63
64func nx_tls13_server_session_emit_certificate(
65 session: *Tls13ServerSession,
66 cert_der: *u8, cert_der_len: i64,
67 out: *u8, out_cap: i64
68) -> i64 {
69 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
70 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
71 if (cert_der as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL }
72 if cert_der_len < 1 { return 0 - NX_TLS13_SSESSION_INTERNAL }
73 if session.state != NX_TLS13_SSTATE_CERT_SENT {
74 return 0 - NX_TLS13_SSESSION_BAD_STATE
75 }
76 if (session.server_hs_traffic_key as i64) == 0 {
77 return 0 - NX_TLS13_SSESSION_INTERNAL
78 }
79
80 // Compute body sizes per RFC 8446 §4.4.2.
81 // cert_list_len = sum of all CertificateEntry sizes
82 // one CertificateEntry = u24 cert_data_len + cert_data + u16 ext_len
83 // = 3 + cert_der_len + 2
84 let entry_size: i64 = 3 + cert_der_len + 2
85 let cert_list_len: i64 = entry_size
86 // body = u8 ctx_len(0) + u24 cert_list_len + cert_list
87 // = 1 + 3 + cert_list_len
88 let body_len: i64 = 4 + cert_list_len
89 // inner = HT + u24 body_len + body
90 let inner_len: i64 = 4 + body_len
91
92 let max_inner: i64 = K_MAGIC_65536
93 if inner_len > max_inner { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
94
95 let inner: *u8 = sys_mmap(inner_len)
96 inner[0] = HT_CERTIFICATE & 0xff
97 inner[1] = ((body_len >> 16) & 0xff) as u8
98 inner[2] = ((body_len >> 8) & 0xff) as u8
99 inner[3] = ( body_len & 0xff) as u8
100 // cert_request_context_len = 0
101 inner[4] = 0
102 // cert_list_len (u24)
103 inner[5] = ((cert_list_len >> 16) & 0xff) as u8
104 inner[6] = ((cert_list_len >> 8) & 0xff) as u8
105 inner[7] = ( cert_list_len & 0xff) as u8
106 // cert_data_len (u24)
107 inner[8] = ((cert_der_len >> 16) & 0xff) as u8
108 inner[9] = ((cert_der_len >> 8) & 0xff) as u8
109 inner[10] = ( cert_der_len & 0xff) as u8
110 // cert_data
111 var ci: i64 = 0
112 while ci < cert_der_len {
113 inner[11 + ci] = cert_der[ci]
114 ci = ci + 1
115 }
116 // extensions_len = 0 (u16)
117 inner[11 + cert_der_len] = 0
118 inner[12 + cert_der_len] = 0
119
120 // AEAD-encrypt as record under server_hs_*
121 let total_needed: i64 = 5 + inner_len + 1 + 16
122 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
123 let header_buf: *u8 = sys_mmap(5)
124 let ct_buf: *u8 = sys_mmap(inner_len + 17)
125 let tag_buf: *u8 = sys_mmap(16)
126 let rv: i64 = nx_tls13_record_encrypt_v2(
127 session.cipher_suite,
128 session.server_hs_traffic_key,
129 session.server_hs_iv,
130 session.server_seq,
131 inner, inner_len,
132 CT_HANDSHAKE, 0,
133 header_buf, ct_buf, tag_buf)
134 if rv != NX_TLS13_REC_VERDICT_OK {
135 return 0 - NX_TLS13_SSESSION_INTERNAL
136 }
137
138 var w: i64 = 0
139 var hi: i64 = 0
140 while hi < 5 { out[w + hi] = header_buf[hi]; hi = hi + 1 }
141 w = w + 5
142 let ct_len: i64 = inner_len + 1
143 var ti: i64 = 0
144 while ti < ct_len { out[w + ti] = ct_buf[ti]; ti = ti + 1 }
145 w = w + ct_len
146 var tagi: i64 = 0
147 while tagi < 16 { out[w + tagi] = tag_buf[tagi]; tagi = tagi + 1 }
148 w = w + 16
149
150 // Transcript update + seq + state stays CERT_SENT (emit_cv next
151 // expects CERT_SENT; this is the AUTHENTICATED path).
152 nx_tls13_transcript_update(session.transcript, inner, inner_len)
153 session.server_seq = session.server_seq + 1
154 // state unchanged (CERT_SENT) -- next is emit_cv.
155 return w
156}
157
158// ----- chain variant: Certificate from a concatenated-DER chain -----
159//
160// Same wire structure + encryption as emit_certificate, but accepts ONE
161// OR MORE DER certs concatenated leaf-first (leaf, then the issuing
162// intermediates up to a root the client trusts). Each top-level DER
163// SEQUENCE is split into its own CertificateEntry so clients can build
164// the full trust path. A single-cert input yields byte-identical output
165// to emit_certificate, so callers that pass one leaf are unaffected.
166//
167// chain_der: concatenated DER certs; each begins 0x30 <len>. Lengths are
168// read from the SEQUENCE header (short-form, 0x81, or 0x82 long-form;
169// X.509 certs exceed 255 bytes so 0x82 in practice).
170func nx_tls13_server_session_emit_certificate_chain(
171 session: *Tls13ServerSession,
172 chain_der: *u8, chain_der_len: i64,
173 out: *u8, out_cap: i64
174) -> i64 {
175 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
176 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
177 if (chain_der as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL }
178 if chain_der_len < 1 { return 0 - NX_TLS13_SSESSION_INTERNAL }
179 if session.state != NX_TLS13_SSTATE_CERT_SENT {
180 return 0 - NX_TLS13_SSESSION_BAD_STATE
181 }
182 if (session.server_hs_traffic_key as i64) == 0 {
183 return 0 - NX_TLS13_SSESSION_INTERNAL
184 }
185
186 // inner = HT(1)+u24 body_len(3)+ctx_len(1)+u24 cert_list_len(3) = 8,
187 // then the cert_list. Each entry adds 5 framing bytes (u24 len +
188 // u16 ext_len=0) over its DER.
189 let inner_cap: i64 = chain_der_len + 64
190 if inner_cap > K_MAGIC_65536 { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
191 let inner: *u8 = sys_mmap(inner_cap)
192
193 var p: i64 = 0
194 var w: i64 = 8
195 while p < chain_der_len {
196 if (((chain_der[p] as i64) & 0xff)) != 0x30 {
197 return 0 - NX_TLS13_SSESSION_INTERNAL
198 }
199 let lb: i64 = (chain_der[p + 1] as i64) & 0xff
200 var clen: i64 = 0
201 var hdr: i64 = 0
202 if lb == 0x82 {
203 clen = (((chain_der[p + 2] as i64) & 0xff) << 8) | ((chain_der[p + 3] as i64) & 0xff)
204 hdr = 4
205 } else {
206 if lb == 0x81 {
207 clen = (chain_der[p + 2] as i64) & 0xff
208 hdr = 3
209 } else {
210 clen = lb
211 hdr = 2
212 }
213 }
214 let cert_total: i64 = hdr + clen
215 if cert_total <= 0 { return 0 - NX_TLS13_SSESSION_INTERNAL }
216 if (p + cert_total) > chain_der_len { return 0 - NX_TLS13_SSESSION_INTERNAL }
217 // CertificateEntry: u24 cert_data_len + cert_data + u16 ext_len(0)
218 inner[w] = ((cert_total >> 16) & 0xff) as u8
219 inner[w + 1] = ((cert_total >> 8) & 0xff) as u8
220 inner[w + 2] = ( cert_total & 0xff) as u8
221 w = w + 3
222 var k: i64 = 0
223 while k < cert_total {
224 inner[w + k] = chain_der[p + k]
225 k = k + 1
226 }
227 w = w + cert_total
228 inner[w] = 0
229 inner[w + 1] = 0
230 w = w + 2
231 p = p + cert_total
232 }
233
234 let cert_list_len: i64 = w - 8
235 let body_len: i64 = 4 + cert_list_len
236 let inner_len: i64 = 8 + cert_list_len
237 inner[0] = HT_CERTIFICATE & 0xff
238 inner[1] = ((body_len >> 16) & 0xff) as u8
239 inner[2] = ((body_len >> 8) & 0xff) as u8
240 inner[3] = ( body_len & 0xff) as u8
241 inner[4] = 0
242 inner[5] = ((cert_list_len >> 16) & 0xff) as u8
243 inner[6] = ((cert_list_len >> 8) & 0xff) as u8
244 inner[7] = ( cert_list_len & 0xff) as u8
245
246 let total_needed: i64 = 5 + inner_len + 1 + 16
247 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
248 let header_buf: *u8 = sys_mmap(5)
249 let ct_buf: *u8 = sys_mmap(inner_len + 17)
250 let tag_buf: *u8 = sys_mmap(16)
251 let rv: i64 = nx_tls13_record_encrypt_v2(
252 session.cipher_suite,
253 session.server_hs_traffic_key,
254 session.server_hs_iv,
255 session.server_seq,
256 inner, inner_len,
257 CT_HANDSHAKE, 0,
258 header_buf, ct_buf, tag_buf)
259 if rv != NX_TLS13_REC_VERDICT_OK {
260 return 0 - NX_TLS13_SSESSION_INTERNAL
261 }
262
263 var wout: i64 = 0
264 var hi: i64 = 0
265 while hi < 5 { out[wout + hi] = header_buf[hi]; hi = hi + 1 }
266 wout = wout + 5
267 let ct_len: i64 = inner_len + 1
268 var ti: i64 = 0
269 while ti < ct_len { out[wout + ti] = ct_buf[ti]; ti = ti + 1 }
270 wout = wout + ct_len
271 var tagi: i64 = 0
272 while tagi < 16 { out[wout + tagi] = tag_buf[tagi]; tagi = tagi + 1 }
273 wout = wout + 16
274
275 nx_tls13_transcript_update(session.transcript, inner, inner_len)
276 session.server_seq = session.server_seq + 1
277 return wout
278}