code wiki / (root) / nx_tls13_server_session_emit_sf.nx

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}