nx_tls13_server_session_run.nx source
↩ module page · 353 lines · 16325 B
1// nx_tls13_server_session_run.nx -- top-level TLS 1.3 server-side
2// handshake orchestrator. The server-side analogue to
3// nx_tls13_client_session_run. Composes the 10 shipped stones over
4// a connected TCP fd to drive a session from INIT to CONNECTED.
5//
6// Pipeline (client → server arrows + arrowed-out server emits):
7// <-- client ClientHello (TLSPlaintext record)
8// [stone 2] recv_ch INIT -> CH_RECEIVED
9// [stone 3] emit_sh + wrap as TLSPlaintext CH_RECEIVED -> SH_SENT
10// --> server ServerHello (TLSPlaintext)
11// [stone 4] derive_hs_secrets SH_SENT -> EE_SENT
12// [stone 5] derive_traffic (no state change)
13// [stone 6] emit_ee (already TLSCiphertext) EE_SENT -> CERT_SENT
14// --> ChangeCipherSpec compatibility record (skipped per RFC 8446 §D.4)
15// --> EncryptedExtensions
16// [stone 6.5] emit_certificate (state stays CERT_SENT)
17// --> Certificate
18// [stone 7.5] emit_cv CERT_SENT -> CV_SENT
19// --> CertificateVerify
20// [stone 7] emit_sf CV_SENT -> SF_SENT
21// --> ServerFinished
22// <-- client Finished (TLSCiphertext)
23// [stone 8] recv_cf SF_SENT -> CONNECTED
24// [derive_app] (already inside recv_cf per stone 8)
25//
26// Public API:
27// nx_tls13_server_session_run(fd, cert_der, cert_der_len, ecdsa_priv_be)
28// -> *Tls13ServerSession (positive cast to i64) on CONNECTED
29// -> negative -(NX_TLS13_SRUN_*) verdict on failure
30//
31// After this returns OK, the caller uses nx_tls13_server_session_app_send
32// / _app_recv to exchange encrypted application_data records.
33//
34// Per Cardinals 9 (single-responsibility: just drive the handshake;
35// app data is a separate layer), 22 (composition only).
36//
37// license_tier: INDEPENDENT_REDERIVE
38// genealogy_id: international-research-sources/ietf/rfc_8446
39// lineage_id: nishi_tls13_server_session_run_q1
40
41import "nx_syscalls.nx"
42import "nx_tls13.nx"
43import "nx_tls13_record.nx"
44import "nx_tls13_read_record_from_fd.nx"
45import "nx_tls13_transcript.nx"
46import "nx_tls13_server_session.nx"
47import "nx_tls13_server_session_recv_ch.nx"
48import "nx_tls13_server_session_emit_sh.nx"
49import "nx_tls13_server_session_emit_hrr.nx"
50import "nx_tls13_server_session_derive_hs.nx"
51import "nx_tls13_server_session_derive_traffic.nx"
52import "nx_tls13_server_session_emit_ee.nx"
53import "nx_tls13_server_session_emit_certificate.nx"
54import "nx_tls13_server_session_emit_cv.nx"
55import "nx_tls13_server_session_emit_sf.nx"
56import "nx_tls13_server_session_recv_cf.nx"
57
58const NX_TLS13_SRUN_OK: i64 = 1
59const NX_TLS13_SRUN_READ_CH_FAIL: i64 = 2
60const NX_TLS13_SRUN_RECV_CH_FAIL: i64 = 3
61const NX_TLS13_SRUN_EMIT_SH_FAIL: i64 = 4
62const NX_TLS13_SRUN_WRITE_SH_FAIL: i64 = 5
63const NX_TLS13_SRUN_DERIVE_HS_FAIL: i64 = 6
64const NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL: i64 = 7
65const NX_TLS13_SRUN_EMIT_EE_FAIL: i64 = 8
66const NX_TLS13_SRUN_WRITE_EE_FAIL: i64 = 9
67const NX_TLS13_SRUN_EMIT_CERT_FAIL: i64 = 10
68const NX_TLS13_SRUN_WRITE_CERT_FAIL: i64 = 11
69const NX_TLS13_SRUN_EMIT_CV_FAIL: i64 = 12
70const NX_TLS13_SRUN_WRITE_CV_FAIL: i64 = 13
71const NX_TLS13_SRUN_EMIT_SF_FAIL: i64 = 14
72const NX_TLS13_SRUN_WRITE_SF_FAIL: i64 = 15
73const NX_TLS13_SRUN_READ_CF_FAIL: i64 = 16
74const NX_TLS13_SRUN_RECV_CF_FAIL: i64 = 17
75const NX_TLS13_SRUN_EMIT_HRR_FAIL: i64 = 18
76const NX_TLS13_SRUN_WRITE_HRR_FAIL: i64 = 19
77const NX_TLS13_SRUN_VERDICT_N: i64 = 20
78
79const NX_TLS13_SRUN_CH_BUF_BYTES: i64 = 16645
80const NX_TLS13_SRUN_OUT_BUF_BYTES: i64 = 16645
81const NX_TLS13_SRUN_MAX_HS_RECORDS: i64 = 4
82
83func nx_tls13_srun_verdict_is_valid(v: i64) -> i64 {
84 if v < NX_TLS13_SRUN_OK { return 0 }
85 if v >= NX_TLS13_SRUN_VERDICT_N { return 0 }
86 return 1
87}
88
89// Loop writes n bytes to fd via sys_write. Returns 0 OK, -1 fail.
90func _srun_write_n(fd: i64, buf: *u8, n: i64) -> i64 {
91 var off: i64 = 0
92 while off < n {
93 let w: i64 = sys_write(fd, (buf as i64 + off) as *u8, n - off)
94 if w <= 0 { return 0 - 1 }
95 off = off + w
96 }
97 return 0
98}
99
100// Read the ClientHello from fd into the session, performing a
101// HelloRetryRequest round trip when the client's first ClientHello
102// carries no X25519 key_share (but does offer X25519 in
103// supported_groups). On return OK the session is in CH_RECEIVED with a
104// usable X25519 share. Returns 0 OK, or a negative -(NX_TLS13_SRUN_*)
105// verdict on failure. Shared by both run paths (ECDSA + Ed25519).
106func _srun_recv_client_hello(fd: i64, s: *Tls13ServerSession) -> i64 {
107 // ---- read ClientHello1 ----
108 let ch_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES)
109 let ch_total: i64 = nx_tls13_read_record_from_fd(fd, ch_rec, NX_TLS13_SRUN_CH_BUF_BYTES)
110 if ch_total < 0 { return 0 - NX_TLS13_SRUN_READ_CH_FAIL }
111 let r1: i64 = nx_tls13_server_session_recv_ch(
112 s,
113 (ch_rec + NX_TLS13_RECORD_HEADER_LEN),
114 ch_total - NX_TLS13_RECORD_HEADER_LEN)
115 if r1 == NX_TLS13_SSESSION_OK { return 0 }
116 if r1 != NX_TLS13_SSESSION_NEED_HRR { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL }
117
118 // ---- HelloRetryRequest round trip ----
119 // recv_ch has already fed ClientHello1 into the transcript; rewrite
120 // it to the synthetic message_hash (RFC 8446 §4.4.1), then emit HRR.
121 nx_tls13_transcript_replace_with_hrr(s.transcript)
122 let hrr_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
123 let n_hrr: i64 = nx_tls13_server_session_emit_hrr(s, hrr_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
124 if n_hrr <= 0 { return 0 - NX_TLS13_SRUN_EMIT_HRR_FAIL }
125 if _srun_write_n(fd, hrr_buf, n_hrr) < 0 { return 0 - NX_TLS13_SRUN_WRITE_HRR_FAIL }
126
127 // ---- read ClientHello2, skipping an optional middlebox-compat CCS
128 // (RFC 8446 §D.4: the client MAY send a dummy ChangeCipherSpec
129 // record immediately before its second flight). ----
130 var loop_count: i64 = 0
131 while loop_count < NX_TLS13_SRUN_MAX_HS_RECORDS {
132 let ch2_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES)
133 let ch2_total: i64 = nx_tls13_read_record_from_fd(fd, ch2_rec, NX_TLS13_SRUN_CH_BUF_BYTES)
134 if ch2_total < 0 { return 0 - NX_TLS13_SRUN_READ_CH_FAIL }
135 if (ch2_rec[0] & 0xff) == 0x14 {
136 loop_count = loop_count + 1
137 } else {
138 let r2: i64 = nx_tls13_server_session_recv_ch(
139 s,
140 (ch2_rec + NX_TLS13_RECORD_HEADER_LEN),
141 ch2_total - NX_TLS13_RECORD_HEADER_LEN)
142 if r2 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL }
143 return 0
144 }
145 }
146 return 0 - NX_TLS13_SRUN_READ_CH_FAIL
147}
148
149// Drive the server-side handshake to CONNECTED. Returns the session
150// as i64 (cast caller-side to *Tls13ServerSession) on success, or a
151// negative -NX_TLS13_SRUN_* verdict on failure.
152//
153// Caller responsibilities:
154// - fd is a connected TCP socket
155// - cert_der points to a DER-encoded server cert (RSA / ECDSA);
156// stone 6.5 wraps it in Certificate handshake message
157// - ecdsa_priv_be is a 32-byte big-endian ECDSA P-256 private scalar
158// used by stone 7.5 to sign the CertificateVerify
159//
160// Allocates one Tls13ServerSession via nx_tls13_server_session_new
161// with caller-supplied srv_rand + srv_x25519_priv.
162func nx_tls13_server_session_run(
163 fd: i64,
164 srv_rand: *u8, srv_x25519_priv: *u8,
165 cert_der: *u8, cert_der_len: i64,
166 ecdsa_priv_be: *u8
167) -> i64 {
168 let s: *Tls13ServerSession = nx_tls13_server_session_new(srv_rand, srv_x25519_priv)
169 if (s as i64) == 0 { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL }
170
171 // ---- Stone 2: read ClientHello (+ HelloRetryRequest if needed) ----
172 let rch: i64 = _srun_recv_client_hello(fd, s)
173 if rch < 0 { return rch }
174
175 // ---- Stone 3: emit ServerHello + wrap as TLSPlaintext + write ----
176 let sh_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
177 let n_sh: i64 = nx_tls13_server_session_emit_sh(s, sh_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
178 if n_sh <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SH_FAIL }
179 // emit_sh already returns a complete TLSPlaintext record (5-byte
180 // header backfilled at out[0..4]); write it raw. Wrapping it again
181 // here double-headers the ServerHello -> openssl/browsers reject it
182 // with a fatal unexpected_message alert.
183 if _srun_write_n(fd, sh_buf, n_sh) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SH_FAIL }
184
185 // ---- Stone 4 + 5: derive handshake secrets + traffic keys ----
186 let r4: i64 = nx_tls13_server_session_derive_hs_secrets(s)
187 if r4 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_HS_FAIL }
188 let r5: i64 = nx_tls13_server_session_derive_traffic(s)
189 if r5 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL }
190
191 // ---- Stone 6: emit EncryptedExtensions (TLSCiphertext already) ----
192 let ee_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
193 let n_ee: i64 = nx_tls13_server_session_emit_ee(s, ee_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
194 if n_ee <= 0 { return 0 - NX_TLS13_SRUN_EMIT_EE_FAIL }
195 if _srun_write_n(fd, ee_buf, n_ee) < 0 { return 0 - NX_TLS13_SRUN_WRITE_EE_FAIL }
196
197 // ---- Stone 6.5: emit Certificate chain (TLSCiphertext already) ----
198 // cert_der is a concatenated-DER chain (leaf + intermediates); the
199 // chain emitter splits it into one CertificateEntry per cert so the
200 // client can build a full path to a trusted root. A single-cert
201 // input is byte-identical to the single-cert emitter.
202 let cert_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
203 let n_cert: i64 = nx_tls13_server_session_emit_certificate_chain(
204 s, cert_der, cert_der_len, cert_buf, NX_TLS13_SRUN_OUT_BUF_BYTES
205 )
206 if n_cert <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CERT_FAIL }
207 if _srun_write_n(fd, cert_buf, n_cert) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CERT_FAIL }
208
209 // ---- Stone 7.5: emit CertificateVerify (TLSCiphertext already) ----
210 let cv_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
211 let n_cv: i64 = nx_tls13_server_session_emit_cv(
212 s, ecdsa_priv_be, cv_buf, NX_TLS13_SRUN_OUT_BUF_BYTES
213 )
214 if n_cv <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CV_FAIL }
215 if _srun_write_n(fd, cv_buf, n_cv) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CV_FAIL }
216
217 // ---- Stone 7: emit ServerFinished (TLSCiphertext already) ----
218 let sf_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
219 let n_sf: i64 = nx_tls13_server_session_emit_sf(s, sf_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
220 if n_sf <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SF_FAIL }
221 if _srun_write_n(fd, sf_buf, n_sf) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SF_FAIL }
222
223 // ---- Stone 8: read ClientFinished record + verify ----
224 // The client may send a ChangeCipherSpec compat record first
225 // (RFC 8446 §D.4) before the real CF. Loop until we get CF.
226 var loop_count: i64 = 0
227 var keep: i64 = 1
228 while keep == 1 {
229 if loop_count >= NX_TLS13_SRUN_MAX_HS_RECORDS {
230 return 0 - NX_TLS13_SRUN_READ_CF_FAIL
231 }
232 let cf_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES)
233 let cf_total: i64 = nx_tls13_read_record_from_fd(
234 fd, cf_rec, NX_TLS13_SRUN_CH_BUF_BYTES
235 )
236 if cf_total < 0 { return 0 - NX_TLS13_SRUN_READ_CF_FAIL }
237
238 // ChangeCipherSpec (type 0x14) is harmless legacy noise -- skip.
239 if (cf_rec[0] & 0xff) == 0x14 {
240 loop_count = loop_count + 1
241 } else {
242 let r8: i64 = nx_tls13_server_session_recv_cf(s, cf_rec, cf_total)
243 if r8 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CF_FAIL }
244 keep = 0
245 }
246 }
247
248 return s as i64
249}
250
251// ===== V-HOST-2+1-2 Path B: nx_tls13_server_session_run_ed25519 =================================================
252//
253// Parallel to nx_tls13_server_session_run; same flow EXCEPT step 7.5
254// uses nx_tls13_server_session_emit_cv_ed25519 with a 32-byte Ed25519
255// priv key (instead of nx_tls13_server_session_emit_cv with ECDSA priv).
256//
257// Caller usage:
258// nx_tls13_server_session_run_ed25519(fd, srv_rand, srv_x25519_priv,
259// cert_der, cert_der_len,
260// ed25519_priv_32)
261//
262// Cert MUST be Ed25519-signed X.509 (per V-HOST-4 sovereign cert gen).
263// V-HOST-4c bin/nx_cert_gen Ed25519 path produces compatible certs.
264//
265// Composes existing primitives:
266// nx_tls13_server_session_new (X25519 for KEX; unchanged)
267// nx_tls13_read_record_from_fd
268// nx_tls13_server_session_recv_ch
269// nx_tls13_server_session_emit_sh
270// nx_tls13_server_session_derive_hs_secrets
271// nx_tls13_server_session_derive_traffic
272// nx_tls13_server_session_emit_ee
273// nx_tls13_server_session_emit_certificate (cert sent unchanged; sig
274// in CV step picks per cert's key type)
275// nx_tls13_server_session_emit_cv_ed25519 (NEW per V-HOST-2+1-1)
276// nx_tls13_server_session_emit_sf
277// nx_tls13_server_session_recv_cf
278
279func nx_tls13_server_session_run_ed25519(
280 fd: i64,
281 srv_rand: *u8, srv_x25519_priv: *u8,
282 cert_der: *u8, cert_der_len: i64,
283 ed25519_priv_32: *u8
284) -> i64 {
285 let s: *Tls13ServerSession = nx_tls13_server_session_new(srv_rand, srv_x25519_priv)
286 if (s as i64) == 0 { return 0 - NX_TLS13_SRUN_RECV_CH_FAIL }
287
288 // ---- Stone 2: read ClientHello (+ HelloRetryRequest if needed) ----
289 let rch: i64 = _srun_recv_client_hello(fd, s)
290 if rch < 0 { return rch }
291
292 let sh_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
293 let n_sh: i64 = nx_tls13_server_session_emit_sh(s, sh_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
294 if n_sh <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SH_FAIL }
295 if _srun_write_n(fd, sh_buf, n_sh) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SH_FAIL }
296 let r3: i64 = nx_tls13_server_session_derive_hs_secrets(s)
297 if r3 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_HS_FAIL }
298
299 let r4: i64 = nx_tls13_server_session_derive_traffic(s)
300 if r4 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_DERIVE_TRAFFIC_FAIL }
301
302 let ee_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
303 let n_ee: i64 = nx_tls13_server_session_emit_ee(s, ee_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
304 if n_ee <= 0 { return 0 - NX_TLS13_SRUN_EMIT_EE_FAIL }
305 if _srun_write_n(fd, ee_buf, n_ee) < 0 { return 0 - NX_TLS13_SRUN_WRITE_EE_FAIL }
306
307 let cert_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
308 let n_cert: i64 = nx_tls13_server_session_emit_certificate(
309 s, cert_der, cert_der_len, cert_buf, NX_TLS13_SRUN_OUT_BUF_BYTES
310 )
311 if n_cert <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CERT_FAIL }
312 if _srun_write_n(fd, cert_buf, n_cert) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CERT_FAIL }
313
314 // ---- Stone 7.5 DIFFERENCE: ED25519 sig path ----
315 let cv_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
316 let n_cv: i64 = nx_tls13_server_session_emit_cv_ed25519(
317 s, ed25519_priv_32, cv_buf, NX_TLS13_SRUN_OUT_BUF_BYTES
318 )
319 if n_cv <= 0 { return 0 - NX_TLS13_SRUN_EMIT_CV_FAIL }
320 if _srun_write_n(fd, cv_buf, n_cv) < 0 { return 0 - NX_TLS13_SRUN_WRITE_CV_FAIL }
321
322 // ---- Stones 7-8: identical to ECDSA path ----
323 let sf_buf: *u8 = sys_mmap(NX_TLS13_SRUN_OUT_BUF_BYTES)
324 let n_sf: i64 = nx_tls13_server_session_emit_sf(s, sf_buf, NX_TLS13_SRUN_OUT_BUF_BYTES)
325 if n_sf <= 0 { return 0 - NX_TLS13_SRUN_EMIT_SF_FAIL }
326 if _srun_write_n(fd, sf_buf, n_sf) < 0 { return 0 - NX_TLS13_SRUN_WRITE_SF_FAIL }
327
328 var loop_count: i64 = 0
329 var keep: i64 = 1
330 while keep == 1 {
331 if loop_count >= NX_TLS13_SRUN_MAX_HS_RECORDS {
332 return 0 - NX_TLS13_SRUN_READ_CF_FAIL
333 }
334 let cf_rec: *u8 = sys_mmap(NX_TLS13_SRUN_CH_BUF_BYTES)
335 let cf_total: i64 = nx_tls13_read_record_from_fd(
336 fd, cf_rec, NX_TLS13_SRUN_CH_BUF_BYTES
337 )
338 if cf_total < 0 { return 0 - NX_TLS13_SRUN_READ_CF_FAIL }
339 if (cf_rec[0] & 0xff) == 0x14 {
340 loop_count = loop_count + 1
341 } else {
342 let r8: i64 = nx_tls13_server_session_recv_cf(s, cf_rec, cf_total)
343 if r8 != NX_TLS13_SSESSION_OK { return 0 - NX_TLS13_SRUN_RECV_CF_FAIL }
344 keep = 0
345 }
346 }
347
348 return s as i64
349}
350
351func main() -> i64 {
352 return 0
353}