code wiki / (root) / nx_tls13_server_session_emit_cv.nx

nx_tls13_server_session_emit_cv.nx source

↩ module page · 347 lines · 12781 B

1// nx_tls13_server_session_emit_cv.nx -- stone 7.5 of TLS server arc. 2// 3// Emits the server CertificateVerify handshake message: ECDSA-P256 4// signature over a transcript-bound context, wrapped as an 5// AEAD-encrypted record. 6// 7// Per RFC 8446 §4.4.3, the signed content is: 8// [0x20 × 64] (octet 0x20 = space, 64 bytes) 9// "TLS 1.3, server CertificateVerify" (33 bytes, the string) 10// 0x00 (separator) 11// transcript_hash (32 bytes, SHA-256) 12// ----- 130 bytes total ----- 13// 14// Signature: ECDSA-P256-SHA256. The signing primitive 15// nx_ecdsa_p256_sign hashes input itself. 16// 17// Wire encoding: 18// HT_CERTIFICATE_VERIFY (0x0f) (1 byte) 19// u24 body_len (3 bytes) 20// u16 signature_scheme = 0x0403 (ecdsa_secp256r1_sha256) 21// u16 sig_len (DER signature length) 22// DER-encoded ECDSA(r, s) (~70-72 bytes) 23// 24// State machine: requires CERT_SENT (i.e. EE has been emitted; the 25// Certificate stone 6.5 is logically before this but is queued, so 26// in v1 the substrate skips cert -- THIS IS HONESTLY ANONYMOUS-CERT 27// STILL). Advances to CV_SENT. emit_sf (patched this commit) now 28// accepts both CERT_SENT and CV_SENT. 29// 30// Honest scope statement: a real client interop'ing with a real 31// public CA chain would expect Certificate to precede this message. 32// This commit ships the SIGNATURE pipeline (key schedule + transcript 33// + ECDSA + DER + AEAD record), composable with stone 6.5 when it 34// ships. The signing primitive is KAT'd against RFC 6979 vectors. 35// 36// license_tier: ORIGINAL 37 38import "nx_syscalls.nx" 39import "nx_u256.nx" 40import "nx_sha256.nx" 41import "nx_ecdsa_p256_sign.nx" 42import "nx_tls13.nx" 43import "nx_tls13_record.nx" 44import "nx_tls13_transcript.nx" 45import "nx_tls13_server_session.nx" 46// V-HOST-2+1 Path B: Ed25519 sig support 47import "nx_ed25519_signature.nx" 48 49const NX_TLS13_SIG_SCHEME_ECDSA_P256_SHA256: i64 = 0x0403 50const NX_TLS13_CV_TRANSCRIPT_LEN: i64 = 32 // SHA-256 hash output 51const NX_TLS13_CV_CONTEXT_LEN: i64 = 130 // 64 + 33 + 1 + 32 52 53// DER-encode an ECDSA-P256 signature (r, s). Returns total bytes 54// written to out. out_cap must be >= 72 for safety. 55func encode_ecdsa_p256_sig_der(r_bytes: *u8, s_bytes: *u8, 56 out: *u8) -> i64 { 57 out[0] = 0x30 // SEQUENCE tag 58 let total_len_pos: i64 = 1 59 var pos: i64 = 2 60 61 // r INTEGER 62 out[pos] = 0x02 63 pos = pos + 1 64 let r_len_pos: i64 = pos 65 pos = pos + 1 66 var r_pad: i64 = 0 67 if ((r_bytes[0] as i64) & 0x80) != 0 { 68 out[pos] = 0 69 pos = pos + 1 70 r_pad = 1 71 } 72 var ri: i64 = 0 73 while ri < 32 { 74 out[pos + ri] = r_bytes[ri] 75 ri = ri + 1 76 } 77 pos = pos + 32 78 out[r_len_pos] = ((32 + r_pad) & 0xff) as u8 79 80 // s INTEGER 81 out[pos] = 0x02 82 pos = pos + 1 83 let s_len_pos: i64 = pos 84 pos = pos + 1 85 var s_pad: i64 = 0 86 if ((s_bytes[0] as i64) & 0x80) != 0 { 87 out[pos] = 0 88 pos = pos + 1 89 s_pad = 1 90 } 91 var si: i64 = 0 92 while si < 32 { 93 out[pos + si] = s_bytes[si] 94 si = si + 1 95 } 96 pos = pos + 32 97 out[s_len_pos] = ((32 + s_pad) & 0xff) as u8 98 99 // Backfill total_len = (pos - 2) (everything after the tag+len pair) 100 out[total_len_pos] = ((pos - 2) & 0xff) as u8 101 return pos 102} 103 104// Build the 130-byte CertificateVerify signed-content context. 105// Lays it into the caller's buffer; returns bytes written (130). 106func build_cv_context(transcript_hash: *u8, out: *u8) -> i64 { 107 var i: i64 = 0 108 while i < 64 { 109 out[i] = 0x20 as u8 // ASCII space 110 i = i + 1 111 } 112 let lbl: *u8 = "TLS 1.3, server CertificateVerify" 113 var j: i64 = 0 114 while j < 33 { 115 out[64 + j] = lbl[j] 116 j = j + 1 117 } 118 out[97] = 0 119 var k: i64 = 0 120 while k < 32 { 121 out[98 + k] = transcript_hash[k] 122 k = k + 1 123 } 124 return 130 125} 126 127// Emit the CertificateVerify handshake message as an AEAD-encrypted 128// record under server_hs_traffic_key + iv. Returns total bytes 129// written or negative -(NX_TLS13_SSESSION_*) verdict. 130// 131// server_ecdsa_priv_be: caller-supplied 32-byte big-endian ECDSA-P256 132// private key. v1 takes it as a param; future 133// stones may store it in the session struct. 134 135func nx_tls13_server_session_emit_cv( 136 session: *Tls13ServerSession, 137 server_ecdsa_priv_be: *u8, 138 out: *u8, out_cap: i64 139) -> i64 { 140 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 141 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 142 if (server_ecdsa_priv_be as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 143 if session.state != NX_TLS13_SSTATE_CERT_SENT { 144 return 0 - NX_TLS13_SSESSION_BAD_STATE 145 } 146 if (session.server_hs_traffic_key as i64) == 0 { 147 return 0 - NX_TLS13_SSESSION_INTERNAL 148 } 149 150 // 1. Transcript snapshot (over CH..EE so far) 151 let th: *u8 = sys_mmap(NX_TLS13_CV_TRANSCRIPT_LEN) 152 let r_th: i64 = nx_tls13_transcript_snapshot(session.transcript, th) 153 if r_th != NX_TLS13_TX_VERDICT_OK { return 0 - NX_TLS13_SSESSION_INTERNAL } 154 155 // 2. Build the 130-byte signed-content context 156 let ctx: *u8 = sys_mmap(NX_TLS13_CV_CONTEXT_LEN) 157 build_cv_context(th, ctx) 158 159 // 3. SHA-256 the context to produce the digest to sign 160 let digest: *u8 = sys_mmap(32) 161 let sha: *Sha256 = sys_mmap(512) as *Sha256 162 sha256_init(sha) 163 sha256_update(sha, ctx, NX_TLS13_CV_CONTEXT_LEN) 164 sha256_final(sha, digest) 165 166 // 4. Load priv key + digest into u256 limbs for signing 167 let priv_limbs: *i64 = u256_alloc() 168 let digest_limbs: *i64 = u256_alloc() 169 u256_load_be(priv_limbs, server_ecdsa_priv_be) 170 u256_load_be(digest_limbs, digest) 171 172 // 5. Sign 173 let r_limbs: *i64 = u256_alloc() 174 let s_limbs: *i64 = u256_alloc() 175 let sv: i64 = nx_ecdsa_p256_sign(priv_limbs, digest_limbs, r_limbs, s_limbs) 176 if sv != NX_ECDSA_SIGN_OK { return 0 - NX_TLS13_SSESSION_INTERNAL } 177 178 // 6. Convert r,s back to big-endian bytes 179 let r_be: *u8 = sys_mmap(32) 180 let s_be: *u8 = sys_mmap(32) 181 u256_store_be(r_be, r_limbs) 182 u256_store_be(s_be, s_limbs) 183 184 // 7. DER-encode r,s 185 let der: *u8 = sys_mmap(80) 186 let der_len: i64 = encode_ecdsa_p256_sig_der(r_be, s_be, der) 187 188 // 8. Build CertificateVerify handshake message 189 // HT(1) + u24_len(3) + sig_scheme(2) + sig_len(2) + DER(der_len) 190 let body_len: i64 = 2 + 2 + der_len // sig_scheme + sig_len + DER 191 let inner_len: i64 = 4 + body_len // HT + u24_len prefix + body 192 let inner: *u8 = sys_mmap(inner_len) 193 inner[0] = HT_CERTIFICATE_VERIFY & 0xff 194 inner[1] = ((body_len >> 16) & 0xff) as u8 195 inner[2] = ((body_len >> 8) & 0xff) as u8 196 inner[3] = ( body_len & 0xff) as u8 197 inner[4] = ((NX_TLS13_SIG_SCHEME_ECDSA_P256_SHA256 >> 8) & 0xff) as u8 198 inner[5] = ( NX_TLS13_SIG_SCHEME_ECDSA_P256_SHA256 & 0xff) as u8 199 inner[6] = ((der_len >> 8) & 0xff) as u8 200 inner[7] = ( der_len & 0xff) as u8 201 var di: i64 = 0 202 while di < der_len { 203 inner[8 + di] = der[di] 204 di = di + 1 205 } 206 207 // 9. AEAD-encrypt as record 208 let total_needed: i64 = 5 + inner_len + 1 + 16 209 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 210 let header_buf: *u8 = sys_mmap(5) 211 let ct_buf: *u8 = sys_mmap(inner_len + 17) 212 let tag_buf: *u8 = sys_mmap(16) 213 let rv: i64 = nx_tls13_record_encrypt_v2( 214 session.cipher_suite, 215 session.server_hs_traffic_key, 216 session.server_hs_iv, 217 session.server_seq, 218 inner, inner_len, 219 CT_HANDSHAKE, 0, 220 header_buf, ct_buf, tag_buf) 221 if rv != NX_TLS13_REC_VERDICT_OK { 222 return 0 - NX_TLS13_SSESSION_INTERNAL 223 } 224 225 var w: i64 = 0 226 var hi: i64 = 0 227 while hi < 5 { out[w + hi] = header_buf[hi]; hi = hi + 1 } 228 w = w + 5 229 let ct_len: i64 = inner_len + 1 230 var ci: i64 = 0 231 while ci < ct_len { out[w + ci] = ct_buf[ci]; ci = ci + 1 } 232 w = w + ct_len 233 var ti: i64 = 0 234 while ti < 16 { out[w + ti] = tag_buf[ti]; ti = ti + 1 } 235 w = w + 16 236 237 // 10. Update transcript with the CV plaintext, advance state + seq 238 nx_tls13_transcript_update(session.transcript, inner, inner_len) 239 session.server_seq = session.server_seq + 1 240 session.state = NX_TLS13_SSTATE_CV_SENT 241 return w 242} 243 244// ===== V-HOST-2+1-1 Path B: Ed25519 CertificateVerify ================================================= 245// 246// Per RFC 8446 §4.2.3 + RFC 8410: signature_scheme ed25519 = 0x0807. 247// Per RFC 8446 §4.4.3: signature is computed over the 130-byte context 248// directly (Ed25519 internally does SHA-512 + scalar mult; no separate 249// outer hash step like ECDSA). 250// 251// Parallel to nx_tls13_server_session_emit_cv (above); ZERO churn to 252// existing function for backward compat. Caller picks which to use 253// based on cert's signing key type. 254// 255// Composes existing primitives: ed25519_sign_full (RFC 8032 §5.1.6) + 256// nx_tls13_transcript_snapshot + build_cv_context + 257// nx_tls13_record_encrypt + transcript_update. 258 259const NX_TLS13_SIG_SCHEME_ED25519: i64 = 0x0807 260 261func nx_tls13_server_session_emit_cv_ed25519( 262 session: *Tls13ServerSession, 263 server_ed25519_priv_32: *u8, 264 out: *u8, out_cap: i64 265) -> i64 { 266 if (session as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 267 if (out as i64) == 0 { return 0 - NX_TLS13_SSESSION_BAD_STATE } 268 if (server_ed25519_priv_32 as i64) == 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 269 if session.state != NX_TLS13_SSTATE_CERT_SENT { 270 return 0 - NX_TLS13_SSESSION_BAD_STATE 271 } 272 if (session.server_hs_traffic_key as i64) == 0 { 273 return 0 - NX_TLS13_SSESSION_INTERNAL 274 } 275 276 // 1. Transcript snapshot (over CH..EE so far) 277 let th: *u8 = sys_mmap(NX_TLS13_CV_TRANSCRIPT_LEN) 278 let r_th: i64 = nx_tls13_transcript_snapshot(session.transcript, th) 279 if r_th != NX_TLS13_TX_VERDICT_OK { return 0 - NX_TLS13_SSESSION_INTERNAL } 280 281 // 2. Build the 130-byte signed-content context 282 let ctx: *u8 = sys_mmap(NX_TLS13_CV_CONTEXT_LEN) 283 build_cv_context(th, ctx) 284 285 // 3. SKIP intermediate hash -- Ed25519 signs the context bytes directly 286 // (RFC 8446 §4.4.3 + RFC 8032 §5.1.6: Ed25519 internally PHF over msg) 287 288 // 4. Sign 130-byte context -> 64-byte sig (R || S) 289 let sig_64: *u8 = sys_mmap(64) 290 let sv: i64 = ed25519_sign_full(server_ed25519_priv_32, ctx, NX_TLS13_CV_CONTEXT_LEN, sig_64) 291 if sv != 0 { return 0 - NX_TLS13_SSESSION_INTERNAL } 292 293 // 5. Build CertificateVerify handshake message 294 // HT(1) + u24_len(3) + sig_scheme(2)=0x0807 + sig_len(2)=64 + sig(64) 295 let body_len: i64 = 2 + 2 + 64 296 let inner_len: i64 = 4 + body_len 297 let inner: *u8 = sys_mmap(inner_len) 298 inner[0] = HT_CERTIFICATE_VERIFY & 0xff 299 inner[1] = ((body_len >> 16) & 0xff) as u8 300 inner[2] = ((body_len >> 8) & 0xff) as u8 301 inner[3] = ( body_len & 0xff) as u8 302 inner[4] = ((NX_TLS13_SIG_SCHEME_ED25519 >> 8) & 0xff) as u8 303 inner[5] = ( NX_TLS13_SIG_SCHEME_ED25519 & 0xff) as u8 304 inner[6] = ((64 >> 8) & 0xff) as u8 305 inner[7] = ( 64 & 0xff) as u8 306 var si: i64 = 0 307 while si < 64 { 308 inner[8 + si] = sig_64[si] 309 si = si + 1 310 } 311 312 // 6. AEAD-encrypt as record (identical to ECDSA path) 313 let total_needed: i64 = 5 + inner_len + 1 + 16 314 if out_cap < total_needed { return 0 - NX_TLS13_SSESSION_BUF_OVERFLOW } 315 let header_buf: *u8 = sys_mmap(5) 316 let ct_buf: *u8 = sys_mmap(inner_len + 17) 317 let tag_buf: *u8 = sys_mmap(16) 318 let rv: i64 = nx_tls13_record_encrypt_v2( 319 session.cipher_suite, 320 session.server_hs_traffic_key, 321 session.server_hs_iv, 322 session.server_seq, 323 inner, inner_len, 324 CT_HANDSHAKE, 0, 325 header_buf, ct_buf, tag_buf) 326 if rv != NX_TLS13_REC_VERDICT_OK { 327 return 0 - NX_TLS13_SSESSION_INTERNAL 328 } 329 330 var w: i64 = 0 331 var hi: i64 = 0 332 while hi < 5 { out[w + hi] = header_buf[hi]; hi = hi + 1 } 333 w = w + 5 334 let ct_len: i64 = inner_len + 1 335 var ci: i64 = 0 336 while ci < ct_len { out[w + ci] = ct_buf[ci]; ci = ci + 1 } 337 w = w + ct_len 338 var ti: i64 = 0 339 while ti < 16 { out[w + ti] = tag_buf[ti]; ti = ti + 1 } 340 w = w + 16 341 342 // 7. Update transcript + advance state + seq 343 nx_tls13_transcript_update(session.transcript, inner, inner_len) 344 session.server_seq = session.server_seq + 1 345 session.state = NX_TLS13_SSTATE_CV_SENT 346 return w 347}