nx_tls13_server_session_emit_sf.nx source
↩ module page · 161 lines · 5848 B
1// nx_tls13_server_session_emit_sf.nx -- 7th TLS server stone.
2//
3// Emits the server Finished handshake message, AEAD-encrypted as a
4// TLS 1.3 record under server_hs_traffic_key + iv.
5//
6// Per RFC 8446 ยง4.4.4:
7// finished_key = HKDF-Expand-Label(traffic_secret, "finished", "", hash_len)
8// verify_data = HMAC-SHA256(finished_key, transcript_hash)
9//
10// Where transcript_hash is the hash of all handshake messages up to
11// (but not including) this Finished.
12//
13// Inner content (handshake plaintext):
14// HT_FINISHED = 0x14 (1 byte)
15// body_len = 0x000020 (3 bytes, u24 big-endian)
16// verify_data (32 bytes for SHA-256)
17// Total = 36 bytes
18//
19// Record format: same as emit_ee -- header(5) + ct(36+1) + tag(16).
20//
21// Precondition: state == CERT_SENT (set by emit_ee; we're skipping
22// the Certificate + CertificateVerify stones for now -- substrate-
23// honest about a stub server that doesn't authenticate to the
24// client cryptographically yet).
25// Postcondition: state == SF_SENT.
26//
27// SUBSTRATE HONESTY NOTE: a production server MUST emit Certificate
28// + CertificateVerify before Finished, signed with the server's
29// private key. This commit ships Finished EARLY (skipping cert +
30// verify) so the AEAD record + transcript-finished pipeline is
31// exercised end-to-end. A real client would refuse this handshake
32// because the server is anonymous. Stones 6.5 + 7.5 (queued) ship
33// the cert + verify messages.
34//
35// license_tier: ORIGINAL
36
37import "nx_syscalls.nx"
38import "nx_hmac.nx"
39import "nx_tls13.nx"
40import "nx_tls13_kdf.nx"
41import "nx_tls13_record.nx"
42import "nx_tls13_transcript.nx"
43import "nx_tls13_server_session.nx"
44
45const NX_TLS13_SF_VERIFY_LEN: i64 = 32 // SHA-256 HMAC tag
46const NX_TLS13_SF_INNER_LEN: i64 = 36 // HT + u24 + 32-byte verify_data
47const NX_TLS13_SF_REC_HEADER: i64 = 5
48const NX_TLS13_SF_REC_TAG: i64 = 16
49
50func nx_tls13_server_session_emit_sf(
51 session: *Tls13ServerSession,
52 out: *u8, out_cap: i64
53) -> i64 {
54 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
55 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE }
56 // Accept BOTH CERT_SENT (anonymous-server stub path) AND CV_SENT
57 // (authenticated path after emit_cv). Either is a valid state
58 // from which the server emits its Finished.
59 var ok_state: i64 = 0
60 if session.state == NX_TLS13_SSTATE_CERT_SENT { ok_state = 1 }
61 if session.state == NX_TLS13_SSTATE_CV_SENT { ok_state = 1 }
62 if ok_state == 0 {
63 return 0 - NX_TLS13_SSESSION_BAD_STATE
64 }
65 if (session.server_hs_traffic_secret as i64) == 0 {
66 return 0 - NX_TLS13_SSESSION_INTERNAL
67 }
68 if (session.server_hs_traffic_key as i64) == 0 {
69 return 0 - NX_TLS13_SSESSION_INTERNAL
70 }
71
72 // Step 1: finished_key = HKDF-Expand-Label(server_hs_traffic_secret,
73 // "finished", "", 32)
74 let finished_label: *u8 = "finished"
75 let empty: *u8 = sys_mmap(1)
76 let finished_key: *u8 = sys_mmap(NX_TLS13_SF_VERIFY_LEN)
77 let r1: i64 = tls13_hkdf_expand_label(
78 session.server_hs_traffic_secret,
79 finished_label, 8,
80 empty, 0,
81 NX_TLS13_SF_VERIFY_LEN, finished_key)
82 if r1 != NX_TLS13_KDF_VERDICT_OK {
83 return 0 - NX_TLS13_SSESSION_INTERNAL
84 }
85
86 // Step 2: snapshot transcript hash so far
87 // (over ClientHello + ServerHello + EncryptedExtensions
88 // since this commit skips Certificate + CertificateVerify)
89 let th: *u8 = sys_mmap(NX_TLS13_SF_VERIFY_LEN)
90 let r2: i64 = nx_tls13_transcript_snapshot(session.transcript, th)
91 if r2 != NX_TLS13_TX_VERDICT_OK {
92 return 0 - NX_TLS13_SSESSION_INTERNAL
93 }
94
95 // Step 3: verify_data = HMAC-SHA256(finished_key, th)
96 let verify_data: *u8 = sys_mmap(NX_TLS13_SF_VERIFY_LEN)
97 hmac_sha256(finished_key, NX_TLS13_SF_VERIFY_LEN,
98 th, NX_TLS13_SF_VERIFY_LEN,
99 verify_data)
100
101 // Step 4: build Finished plaintext (HT + u24 len + verify_data)
102 let inner: *u8 = sys_mmap(NX_TLS13_SF_INNER_LEN)
103 inner[0] = HT_FINISHED & 0xff
104 inner[1] = 0
105 inner[2] = 0
106 inner[3] = NX_TLS13_SF_VERIFY_LEN & 0xff
107 var c: i64 = 0
108 while c < NX_TLS13_SF_VERIFY_LEN {
109 inner[4 + c] = verify_data[c]
110 c = c + 1
111 }
112
113 // Step 5: AEAD encrypt + write record
114 let total_needed: i64 = NX_TLS13_SF_REC_HEADER + NX_TLS13_SF_INNER_LEN + 1 + NX_TLS13_SF_REC_TAG
115 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW }
116
117 let header_buf: *u8 = sys_mmap(NX_TLS13_SF_REC_HEADER)
118 let ct_len: i64 = NX_TLS13_SF_INNER_LEN + 1
119 let ct_buf: *u8 = sys_mmap(ct_len + 16)
120 let tag_buf: *u8 = sys_mmap(NX_TLS13_SF_REC_TAG)
121
122 let rv: i64 = nx_tls13_record_encrypt_v2(
123 session.cipher_suite,
124 session.server_hs_traffic_key,
125 session.server_hs_iv,
126 session.server_seq,
127 inner, NX_TLS13_SF_INNER_LEN,
128 CT_HANDSHAKE, 0,
129 header_buf, ct_buf, tag_buf)
130 if rv != NX_TLS13_REC_VERDICT_OK {
131 return 0 - NX_TLS13_SSESSION_INTERNAL
132 }
133
134 var w: i64 = 0
135 var i: i64 = 0
136 while i < NX_TLS13_SF_REC_HEADER {
137 out[w + i] = header_buf[i]
138 i = i + 1
139 }
140 w = w + NX_TLS13_SF_REC_HEADER
141 var j: i64 = 0
142 while j < ct_len {
143 out[w + j] = ct_buf[j]
144 j = j + 1
145 }
146 w = w + ct_len
147 var k: i64 = 0
148 while k < NX_TLS13_SF_REC_TAG {
149 out[w + k] = tag_buf[k]
150 k = k + 1
151 }
152 w = w + NX_TLS13_SF_REC_TAG
153
154 // Feed Finished plaintext into transcript (so client_finished
155 // verify can use the same hash).
156 nx_tls13_transcript_update(session.transcript, inner, NX_TLS13_SF_INNER_LEN)
157
158 session.server_seq = session.server_seq + 1
159 session.state = NX_TLS13_SSTATE_SF_SENT
160 return w
161}