code wiki / (root) / nx_tls13_server_session_emit_hrr.nx

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}