code wiki / (root) / nx_tls13_server_session_emit_certificate.nx

nx_tls13_server_session_emit_certificate.nx source

↩ module page · 278 lines · 10949 B

1// nx_tls13_server_session_emit_certificate.nx -- stone 6.5 (final 2// core TLS server stone). 3// 4// Emits the server Certificate handshake message: caller supplies 5// pre-encoded DER cert bytes, substrate wraps them in the TLS 1.3 6// Certificate structure + AEAD-encrypted record. 7// 8// Per RFC 8446 §4.4.2 Certificate message body: 9// opaque certificate_request_context<0..2^8-1>; // server: empty 10// CertificateEntry certificate_list<0..2^24-1>; 11// 12// CertificateEntry: 13// opaque cert_data<1..2^24-1>; // DER cert 14// Extension extensions<0..2^16-1>; // empty for v1 15// 16// Wire layout (one cert, no extensions): 17// HT_CERTIFICATE (0x0b) (1 byte) 18// u24 body_len (3 bytes) 19// u8 cert_request_ctx_len = 0 (1 byte) 20// u24 cert_list_len (3 bytes) 21// u24 cert_data_len (3 bytes) 22// cert_data (cert_data_len bytes) 23// u16 extensions_len = 0 (2 bytes) 24// 25// Caller responsibility: 26// - Supply a valid DER-encoded X.509 cert (from ACME, self-signed 27// test cert, etc.). Substrate does NOT validate the DER -- it 28// just wraps the bytes. Per [[feedback-no-third-party-trust- 29// native-or-nothing]]: future stone can compose nx_x509 + 30// nx_acme to produce the cert bits-up. 31// 32// Substrate provides: WIRE FORMAT. Operator provides: cert bytes. 33// Together = real-world-client-acceptable server. 34// 35// State machine: requires EE_SENT (set by derive_hs_secrets after 36// emit_ee actually emits). Wait -- the existing flow sets state 37// EE_SENT after derive_hs but PRE-emit_ee; emit_ee then sets 38// CERT_SENT. This stone, IF inserted, would run AFTER emit_ee, so 39// require CERT_SENT and advance to ... a new state. 40// 41// Cleaner design: this stone requires CERT_SENT (set by emit_ee) 42// and advances to CERT_VERIFIED state... but we don't have that 43// state. Pragmatic: require CERT_SENT, advance to CERT_SENT (same 44// state -- the message bytes are written; next state still expects 45// CV). No: that would let emit_cv NOT be called and emit_sf 46// happily proceed from CERT_SENT -- which is what the existing 47// anonymous-stub path does anyway. 48// 49// Cleanest pragmatic path: this stone is the AUTHENTICATED path's 50// "Certificate" message; it runs BETWEEN emit_ee and emit_cv. 51// Require CERT_SENT, advance to CERT_SENT. The state name keeps 52// the existing semantics; the message has just been emitted as 53// well as transcript-updated. emit_cv runs next. 54// 55// license_tier: ORIGINAL 56 57import "nx_syscalls.nx" 58import "nx_tls13.nx" 59import "nx_tls13_record.nx" 60import "nx_tls13_transcript.nx" 61import "nx_tls13_server_session.nx" 62const K_MAGIC_65536: i64 = 65536 63 64func nx_tls13_server_session_emit_certificate( 65 session: *Tls13ServerSession, 66 cert_der: *u8, cert_der_len: i64, 67 out: *u8, out_cap: i64 68) -> i64 { 69 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 70 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 71 if (cert_der as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 72 if cert_der_len < 1 { return 0 - NX_TLS13_SSESSION_INTERNAL } 73 if session.state != NX_TLS13_SSTATE_CERT_SENT { 74 return 0 - NX_TLS13_SSESSION_BAD_STATE 75 } 76 if (session.server_hs_traffic_key as i64) == 0 { 77 return 0 - NX_TLS13_SSESSION_INTERNAL 78 } 79 80 // Compute body sizes per RFC 8446 §4.4.2. 81 // cert_list_len = sum of all CertificateEntry sizes 82 // one CertificateEntry = u24 cert_data_len + cert_data + u16 ext_len 83 // = 3 + cert_der_len + 2 84 let entry_size: i64 = 3 + cert_der_len + 2 85 let cert_list_len: i64 = entry_size 86 // body = u8 ctx_len(0) + u24 cert_list_len + cert_list 87 // = 1 + 3 + cert_list_len 88 let body_len: i64 = 4 + cert_list_len 89 // inner = HT + u24 body_len + body 90 let inner_len: i64 = 4 + body_len 91 92 let max_inner: i64 = K_MAGIC_65536 93 if inner_len > max_inner { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 94 95 let inner: *u8 = sys_mmap(inner_len) 96 inner[0] = HT_CERTIFICATE & 0xff 97 inner[1] = ((body_len >> 16) & 0xff) as u8 98 inner[2] = ((body_len >> 8) & 0xff) as u8 99 inner[3] = ( body_len & 0xff) as u8 100 // cert_request_context_len = 0 101 inner[4] = 0 102 // cert_list_len (u24) 103 inner[5] = ((cert_list_len >> 16) & 0xff) as u8 104 inner[6] = ((cert_list_len >> 8) & 0xff) as u8 105 inner[7] = ( cert_list_len & 0xff) as u8 106 // cert_data_len (u24) 107 inner[8] = ((cert_der_len >> 16) & 0xff) as u8 108 inner[9] = ((cert_der_len >> 8) & 0xff) as u8 109 inner[10] = ( cert_der_len & 0xff) as u8 110 // cert_data 111 var ci: i64 = 0 112 while ci < cert_der_len { 113 inner[11 + ci] = cert_der[ci] 114 ci = ci + 1 115 } 116 // extensions_len = 0 (u16) 117 inner[11 + cert_der_len] = 0 118 inner[12 + cert_der_len] = 0 119 120 // AEAD-encrypt as record under server_hs_* 121 let total_needed: i64 = 5 + inner_len + 1 + 16 122 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 123 let header_buf: *u8 = sys_mmap(5) 124 let ct_buf: *u8 = sys_mmap(inner_len + 17) 125 let tag_buf: *u8 = sys_mmap(16) 126 let rv: i64 = nx_tls13_record_encrypt_v2( 127 session.cipher_suite, 128 session.server_hs_traffic_key, 129 session.server_hs_iv, 130 session.server_seq, 131 inner, inner_len, 132 CT_HANDSHAKE, 0, 133 header_buf, ct_buf, tag_buf) 134 if rv != NX_TLS13_REC_VERDICT_OK { 135 return 0 - NX_TLS13_SSESSION_INTERNAL 136 } 137 138 var w: i64 = 0 139 var hi: i64 = 0 140 while hi < 5 { out[w + hi] = header_buf[hi]; hi = hi + 1 } 141 w = w + 5 142 let ct_len: i64 = inner_len + 1 143 var ti: i64 = 0 144 while ti < ct_len { out[w + ti] = ct_buf[ti]; ti = ti + 1 } 145 w = w + ct_len 146 var tagi: i64 = 0 147 while tagi < 16 { out[w + tagi] = tag_buf[tagi]; tagi = tagi + 1 } 148 w = w + 16 149 150 // Transcript update + seq + state stays CERT_SENT (emit_cv next 151 // expects CERT_SENT; this is the AUTHENTICATED path). 152 nx_tls13_transcript_update(session.transcript, inner, inner_len) 153 session.server_seq = session.server_seq + 1 154 // state unchanged (CERT_SENT) -- next is emit_cv. 155 return w 156} 157 158// ----- chain variant: Certificate from a concatenated-DER chain ----- 159// 160// Same wire structure + encryption as emit_certificate, but accepts ONE 161// OR MORE DER certs concatenated leaf-first (leaf, then the issuing 162// intermediates up to a root the client trusts). Each top-level DER 163// SEQUENCE is split into its own CertificateEntry so clients can build 164// the full trust path. A single-cert input yields byte-identical output 165// to emit_certificate, so callers that pass one leaf are unaffected. 166// 167// chain_der: concatenated DER certs; each begins 0x30 <len>. Lengths are 168// read from the SEQUENCE header (short-form, 0x81, or 0x82 long-form; 169// X.509 certs exceed 255 bytes so 0x82 in practice). 170func nx_tls13_server_session_emit_certificate_chain( 171 session: *Tls13ServerSession, 172 chain_der: *u8, chain_der_len: i64, 173 out: *u8, out_cap: i64 174) -> i64 { 175 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 176 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 177 if (chain_der as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 178 if chain_der_len < 1 { return 0 - NX_TLS13_SSESSION_INTERNAL } 179 if session.state != NX_TLS13_SSTATE_CERT_SENT { 180 return 0 - NX_TLS13_SSESSION_BAD_STATE 181 } 182 if (session.server_hs_traffic_key as i64) == 0 { 183 return 0 - NX_TLS13_SSESSION_INTERNAL 184 } 185 186 // inner = HT(1)+u24 body_len(3)+ctx_len(1)+u24 cert_list_len(3) = 8, 187 // then the cert_list. Each entry adds 5 framing bytes (u24 len + 188 // u16 ext_len=0) over its DER. 189 let inner_cap: i64 = chain_der_len + 64 190 if inner_cap > K_MAGIC_65536 { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 191 let inner: *u8 = sys_mmap(inner_cap) 192 193 var p: i64 = 0 194 var w: i64 = 8 195 while p < chain_der_len { 196 if (((chain_der[p] as i64) & 0xff)) != 0x30 { 197 return 0 - NX_TLS13_SSESSION_INTERNAL 198 } 199 let lb: i64 = (chain_der[p + 1] as i64) & 0xff 200 var clen: i64 = 0 201 var hdr: i64 = 0 202 if lb == 0x82 { 203 clen = (((chain_der[p + 2] as i64) & 0xff) << 8) | ((chain_der[p + 3] as i64) & 0xff) 204 hdr = 4 205 } else { 206 if lb == 0x81 { 207 clen = (chain_der[p + 2] as i64) & 0xff 208 hdr = 3 209 } else { 210 clen = lb 211 hdr = 2 212 } 213 } 214 let cert_total: i64 = hdr + clen 215 if cert_total <= 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 216 if (p + cert_total) > chain_der_len { return 0 - NX_TLS13_SSESSION_INTERNAL } 217 // CertificateEntry: u24 cert_data_len + cert_data + u16 ext_len(0) 218 inner[w] = ((cert_total >> 16) & 0xff) as u8 219 inner[w + 1] = ((cert_total >> 8) & 0xff) as u8 220 inner[w + 2] = ( cert_total & 0xff) as u8 221 w = w + 3 222 var k: i64 = 0 223 while k < cert_total { 224 inner[w + k] = chain_der[p + k] 225 k = k + 1 226 } 227 w = w + cert_total 228 inner[w] = 0 229 inner[w + 1] = 0 230 w = w + 2 231 p = p + cert_total 232 } 233 234 let cert_list_len: i64 = w - 8 235 let body_len: i64 = 4 + cert_list_len 236 let inner_len: i64 = 8 + cert_list_len 237 inner[0] = HT_CERTIFICATE & 0xff 238 inner[1] = ((body_len >> 16) & 0xff) as u8 239 inner[2] = ((body_len >> 8) & 0xff) as u8 240 inner[3] = ( body_len & 0xff) as u8 241 inner[4] = 0 242 inner[5] = ((cert_list_len >> 16) & 0xff) as u8 243 inner[6] = ((cert_list_len >> 8) & 0xff) as u8 244 inner[7] = ( cert_list_len & 0xff) as u8 245 246 let total_needed: i64 = 5 + inner_len + 1 + 16 247 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 248 let header_buf: *u8 = sys_mmap(5) 249 let ct_buf: *u8 = sys_mmap(inner_len + 17) 250 let tag_buf: *u8 = sys_mmap(16) 251 let rv: i64 = nx_tls13_record_encrypt_v2( 252 session.cipher_suite, 253 session.server_hs_traffic_key, 254 session.server_hs_iv, 255 session.server_seq, 256 inner, inner_len, 257 CT_HANDSHAKE, 0, 258 header_buf, ct_buf, tag_buf) 259 if rv != NX_TLS13_REC_VERDICT_OK { 260 return 0 - NX_TLS13_SSESSION_INTERNAL 261 } 262 263 var wout: i64 = 0 264 var hi: i64 = 0 265 while hi < 5 { out[wout + hi] = header_buf[hi]; hi = hi + 1 } 266 wout = wout + 5 267 let ct_len: i64 = inner_len + 1 268 var ti: i64 = 0 269 while ti < ct_len { out[wout + ti] = ct_buf[ti]; ti = ti + 1 } 270 wout = wout + ct_len 271 var tagi: i64 = 0 272 while tagi < 16 { out[wout + tagi] = tag_buf[tagi]; tagi = tagi + 1 } 273 wout = wout + 16 274 275 nx_tls13_transcript_update(session.transcript, inner, inner_len) 276 session.server_seq = session.server_seq + 1 277 return wout 278}