nx_tls13_server_session_emit_hrr.nx source
↩ module page · 161 lines · 6051 B
1// nx_tls13_server_session_emit_hrr.nx -- server-side HelloRetryRequest
2// emitter (RFC 8446 §4.1.4).
3//
4// HRR is sent when the client's ClientHello did NOT carry a key_share
5// for a group the server supports, but DID list such a group in
6// supported_groups. On the wire HRR is formatted IDENTICALLY to a
7// ServerHello, with two differences:
8// 1. Random is the fixed magic value SHA-256("HelloRetryRequest")
9// (per RFC 8446 §4.1.3) -- supplied by tls13_hrr_magic_random.
10// 2. The key_share extension body is JUST the 2-byte selected_group
11// (NamedGroup) the server wants -- no actual key_exchange entry.
12//
13// This server supports exactly one group (X25519), so HRR always
14// selects NG_X25519; the client then re-sends ClientHello2 with an
15// X25519 key_share and the handshake proceeds normally.
16//
17// Transcript ordering (RFC 8446 §4.4.1, the HRR special case):
18// 1. recv_ch feeds ClientHello1 into the transcript.
19// 2. caller calls nx_tls13_transcript_replace_with_hrr (CH1 ->
20// synthetic message_hash record).
21// 3. THIS step feeds the HRR handshake body into the transcript.
22// 4. recv_ch (CH2) feeds ClientHello2 in.
23// So this emitter updates the transcript with the HRR body and the
24// caller MUST have already done the replace_with_hrr in step 2.
25//
26// State: requires SSTATE_INIT (we are between CH1 and CH2; recv_ch
27// returned NEED_HRR without advancing state). Does NOT advance state
28// -- recv_ch(CH2) advances INIT -> CH_RECEIVED next.
29//
30// Per Cardinals 9 (single-responsibility), 22 (composition only):
31// reuses tls13_ext_emit_supported_versions_server + tls13_hrr_magic_random.
32//
33// license_tier: ORIGINAL
34// genealogy_id: international-research-sources/ietf/rfc_8446
35// lineage_id: nishi_tls13_server_session_emit_hrr_q1
36
37import "nx_syscalls.nx"
38import "nx_tls13.nx"
39import "nx_tls13_hello.nx"
40import "nx_tls13_hrr.nx"
41import "nx_tls13_transcript.nx"
42import "nx_tls13_server_session.nx"
43import "nx_tls13_server_session_emit_sh.nx"
44
45// ----- HRR body emit (handshake header + ServerHello-shaped body) -----
46//
47// Output: total bytes written into out (handshake message, no record
48// header), or negative NX_TLS13_HELLO_VERDICT_* on overflow.
49func tls13_hello_retry_request_emit(
50 cipher_suite: i64,
51 sid_echo: *u8, sid_echo_len: i64,
52 selected_group: i64,
53 out: *u8, out_cap: i64
54) -> i64 {
55 if out_cap < 128 + sid_echo_len { return 0 - NX_TLS13_HELLO_VERDICT_BUF_OVERFLOW }
56 if sid_echo_len < 0 { return 0 - NX_TLS13_HELLO_VERDICT_BAD_HEADER }
57 if sid_echo_len > 32 { return 0 - NX_TLS13_HELLO_VERDICT_BAD_HEADER }
58
59 var o: i64 = 4 // reserve 4 bytes for handshake header
60 tls_write_u16_be(out, o, TLS_LEGACY_VERSION)
61 o = o + 2
62
63 // Random = SHA-256("HelloRetryRequest") magic (RFC 8446 §4.1.3).
64 let magic: *u8 = sys_mmap(64)
65 tls13_hrr_magic_random(magic)
66 var i: i64 = 0
67 while i < 32 {
68 out[o + i] = magic[i]
69 i = i + 1
70 }
71 o = o + 32
72
73 // legacy_session_id_echo (byte-for-byte echo of the client's).
74 out[o] = (sid_echo_len & 0xff) as u8
75 o = o + 1
76 var j: i64 = 0
77 while j < sid_echo_len {
78 out[o + j] = sid_echo[j]
79 j = j + 1
80 }
81 o = o + sid_echo_len
82
83 // cipher_suite (single u16)
84 tls_write_u16_be(out, o, cipher_suite)
85 o = o + 2
86 // legacy_compression_method = 0
87 out[o] = 0
88 o = o + 1
89
90 // extensions list_len placeholder
91 let ext_list_len_off: i64 = o
92 o = o + 2
93 let ext_data_start: i64 = o
94
95 // supported_versions(43) = selected_version 0x0304 (6 bytes)
96 let r1: i64 = tls13_ext_emit_supported_versions_server(out + o, out_cap - o)
97 if r1 < 0 { return r1 }
98 o = o + r1
99
100 // key_share(51) HRR form: ext_data is JUST the selected NamedGroup.
101 // ext_type(2) + ext_data_len(2)=2 + selected_group(2) = 6 bytes.
102 if (out_cap - o) < 6 { return 0 - NX_TLS13_HELLO_VERDICT_BUF_OVERFLOW }
103 tls_write_u16_be(out, o, EXT_KEY_SHARE)
104 tls_write_u16_be(out, o + 2, 2)
105 tls_write_u16_be(out, o + 4, selected_group)
106 o = o + 6
107
108 let ext_data_len: i64 = o - ext_data_start
109 tls_write_u16_be(out, ext_list_len_off, ext_data_len)
110
111 // Backfill handshake header (msg_type = HT_SERVER_HELLO = 2; HRR
112 // shares the ServerHello type, distinguished only by the random).
113 let body_len: i64 = o - 4
114 out[0] = HT_SERVER_HELLO & 0xff
115 tls_write_u24_be(out, 1, body_len)
116 return o
117}
118
119// ----- session orchestrator step -----
120//
121// Emits an HRR as a complete TLSPlaintext record into out (5-byte
122// record header backfilled at out[0..4], like emit_sh), updates the
123// transcript with the HRR handshake body, and returns total bytes
124// (header + body). Does NOT advance state.
125//
126// PRECONDITION: caller has already fed CH1 into the transcript (via
127// recv_ch) and called nx_tls13_transcript_replace_with_hrr.
128func nx_tls13_server_session_emit_hrr(
129 session: *Tls13ServerSession,
130 out: *u8, out_cap: i64
131) -> i64 {
132 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
133 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
134 if session.state != NX_TLS13_SSTATE_INIT {
135 return 0 - NX_TLS13_SSESSION_BAD_STATE
136 }
137 if out_cap < 5 { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
138
139 let n: i64 = tls13_hello_retry_request_emit(
140 NX_TLS13_CS_CHACHA20_POLY1305_SHA256,
141 session.client_session_id, session.client_session_id_len,
142 NG_X25519,
143 out + 5, out_cap - 5)
144 if n < 0 { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
145
146 // TLSPlaintext record header: type(1)=Handshake(22), ver(2)=0x0303, len(2)=n
147 out[0] = 22 as u8
148 out[1] = 0x03 as u8
149 out[2] = 0x03 as u8
150 out[3] = ((n >> 8) & 0xff) as u8
151 out[4] = (n & 0xff) as u8
152
153 // Transcript captures the HRR handshake body (not the record header).
154 nx_tls13_transcript_update(session.transcript, out + 5, n)
155 // State intentionally unchanged (INIT) -- recv_ch(CH2) advances it.
156 return n + 5
157}
158
159func main() -> i64 {
160 return 0
161}