code wiki / (root) / nx_tls13_handshake.nx

nx_tls13_handshake.nx source

↩ module page · 212 lines · 8713 B

1// nx_tls13_handshake.nx -- TLS 1.3 handshake orchestrator (Gap L sub-1). 2// 3// Phase 0b §L of the Nishi TLS 1.3 stack per 4// docs/NISHI_TLS13_GAP_AUDIT.md. Composes the shipped primitives 5// (ECDHE + transcript + schedule + KDF) into the small set of 6// state-machine functions a TLS 1.3 client needs. 7// 8// This is NOT yet a full nx_tls13_connect() with sockets. It is 9// the IN-MEMORY orchestrator that takes byte buffers + keys in and 10// produces the next byte buffers + keys out. A future thin 11// wrapper drives this from a TCP socket. Separating logic from 12// transport keeps the substrate testable end-to-end without 13// network, matching the RFC 8448 §3 KAT pattern. 14// 15// What it does today: 16// - tls13_handshake_compute_handshake_keys: given client X25519 17// private key + server X25519 public key + the running 18// transcript hash state (already-fed with CH+SH), derives 19// handshake_secret + client/server handshake_traffic_secrets 20// + their AEAD keys + IVs. This is the moment in the 21// handshake where everything past ServerHello becomes 22// encrypted. 23// - tls13_handshake_compute_application_keys: given handshake_secret 24// + transcript-state at the post-server-Finished point, 25// derives master_secret + client/server app_traffic_secrets 26// + their AEAD keys + IVs. This is the moment the handshake 27// completes and application data starts flowing. 28// 29// What it doesn't do yet: 30// - record-layer I/O (caller still drives nx_tls13_record 31// directly for now; integration ships when the full state 32// machine lands) 33// - HRR retry loop (the HRR detection + transcript replace are 34// both shipped; an outer state-machine retries with the 35// server-chosen group from HRR's key_share extension) 36// - PSK / resumption path (Gap M) 37// 38// KAT verified: 39// - RFC 8448 §3 ECDHE computation: 40// x25519(client_priv=49af...5005, server_pub=c982...1f0f) 41// = 8bd4054f...492d 42// - RFC 8448 §3 handshake_secret derivation chain end-to-end 43// (no longer plugging in a hardcoded ECDHE shared as in 44// nx_tls13_schedule_test; the shared comes from real X25519) 45// 46// Composes with: 47// - nx_x25519 (ECDHE) 48// - nx_tls13_schedule (key schedule cascade) 49// - nx_tls13_kdf (HKDF-Expand-Label, Derive-Secret) 50// - nx_tls13_transcript (caller manages the running hash state) 51// 52// license_tier: INDEPENDENT_REDERIVE 53// genealogy_id: international-research-sources/ietf/rfc_8446 54// lineage_id: nishi_tls13_handshake_orchestrator_q10 55 56// nx_safety_envelope: 57// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 58// sil_target: SIL1 59// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 60// verdict: NOT_YET_EVALUATED 61 62import "nx_syscalls.nx" 63import "nx_x25519.nx" 64import "nx_tls13_kdf.nx" 65import "nx_tls13_schedule.nx" 66 67const NX_TLS13_HS_KEY_BYTES_AEAD: i64 = 32 // ChaCha20-Poly1305 / AES-256-GCM key 68const NX_TLS13_HS_IV_BYTES_AEAD: i64 = 12 // AEAD nonce / iv 69const NX_TLS13_HS_HASH_LEN_SHA256: i64 = 32 70 71const NX_TLS13_HS_VERDICT_OK: i64 = 1 72const NX_TLS13_HS_VERDICT_N: i64 = 2 73 74// Compute the full handshake-phase key set for both directions. 75// 76// Inputs: 77// client_priv32 = our ephemeral X25519 private key (caller 78// generates via nx_x25519_ephemeral) 79// server_pub32 = peer ephemeral X25519 public key (caller 80// parsed from ServerHello key_share) 81// transcript_hash32 = current transcript hash AFTER feeding both 82// ClientHello and ServerHello (caller 83// snapshotted via nx_tls13_transcript_snapshot) 84// empty_hash32 = SHA-256 of empty string; caller-supplied so 85// this function stays hash-agnostic 86// 87// Outputs (each is 32 bytes for SHA-256 cipher suite): 88// out_handshake_secret = the long-form HKDF-Extract output 89// out_chts = client_handshake_traffic_secret 90// out_shts = server_handshake_traffic_secret 91// out_client_hs_key = AEAD key for client->server records 92// out_client_hs_iv = AEAD IV for client->server records 93// out_server_hs_key = AEAD key for server->client records 94// out_server_hs_iv = AEAD IV for server->client records 95// 96// Returns NX_TLS13_HS_VERDICT_OK. 97func tls13_handshake_compute_handshake_keys( 98 client_priv32: *u8, server_pub32: *u8, 99 transcript_hash32: *u8, 100 empty_hash32: *u8, 101 out_handshake_secret: *u8, 102 out_chts: *u8, out_shts: *u8, 103 out_client_hs_key: *u8, out_client_hs_iv: *u8, 104 out_server_hs_key: *u8, out_server_hs_iv: *u8 105) -> i64 { 106 // 1. ECDHE: compute shared secret = X25519(client_priv, server_pub). 107 let ecdhe: *u8 = sys_mmap(64) 108 x25519(client_priv32, server_pub32, ecdhe) 109 110 // 2. Compute the no-PSK early_secret = HKDF-Extract(0, 0^32). 111 let zeros: *u8 = sys_mmap(64) 112 let early_secret: *u8 = sys_mmap(64) 113 tls13_early_secret(zeros, NX_TLS13_HS_HASH_LEN_SHA256, early_secret) 114 115 // 3. derived_1 = Derive-Secret(early_secret, "derived", empty_hash) 116 let derived_1: *u8 = sys_mmap(64) 117 tls13_derived(early_secret, empty_hash32, NX_TLS13_HS_HASH_LEN_SHA256, derived_1) 118 119 // 4. handshake_secret = HKDF-Extract(derived_1, ECDHE_shared) 120 tls13_handshake_secret( 121 derived_1, NX_TLS13_HS_HASH_LEN_SHA256, 122 ecdhe, 32, 123 out_handshake_secret 124 ) 125 126 // 5. client_handshake_traffic_secret = 127 // Derive-Secret(handshake_secret, "c hs traffic", transcript_hash) 128 tls13_traffic_secret( 129 out_handshake_secret, 130 NX_TLS13_LABEL_C_HS_TRAFFIC, 131 transcript_hash32, NX_TLS13_HS_HASH_LEN_SHA256, 132 out_chts 133 ) 134 135 // 6. server_handshake_traffic_secret = same with "s hs traffic" 136 tls13_traffic_secret( 137 out_handshake_secret, 138 NX_TLS13_LABEL_S_HS_TRAFFIC, 139 transcript_hash32, NX_TLS13_HS_HASH_LEN_SHA256, 140 out_shts 141 ) 142 143 // 7. Per-direction AEAD key + IV derivation. 144 tls13_traffic_key(out_chts, NX_TLS13_HS_KEY_BYTES_AEAD, out_client_hs_key) 145 tls13_traffic_iv (out_chts, NX_TLS13_HS_IV_BYTES_AEAD, out_client_hs_iv) 146 tls13_traffic_key(out_shts, NX_TLS13_HS_KEY_BYTES_AEAD, out_server_hs_key) 147 tls13_traffic_iv (out_shts, NX_TLS13_HS_IV_BYTES_AEAD, out_server_hs_iv) 148 149 return NX_TLS13_HS_VERDICT_OK 150} 151 152// Compute application-phase keys after server Finished has been 153// verified and the transcript has been advanced through it. 154// 155// Inputs: 156// handshake_secret = from tls13_handshake_compute_handshake_keys 157// transcript_hash32 = snapshot AFTER ServerHello..server_Finished 158// (per RFC 8446 §7.1, the master_secret-derived 159// secrets read the transcript ending at SF) 160// empty_hash32 = SHA-256("") as above 161// 162// Outputs: 163// out_master_secret 164// out_cats = client_application_traffic_secret_0 165// out_sats = server_application_traffic_secret_0 166// out_client_app_key + iv 167// out_server_app_key + iv 168// 169// Returns NX_TLS13_HS_VERDICT_OK. 170func tls13_handshake_compute_application_keys( 171 handshake_secret: *u8, 172 transcript_hash32: *u8, 173 empty_hash32: *u8, 174 out_master_secret: *u8, 175 out_cats: *u8, out_sats: *u8, 176 out_client_app_key: *u8, out_client_app_iv: *u8, 177 out_server_app_key: *u8, out_server_app_iv: *u8 178) -> i64 { 179 // derived_2 = Derive-Secret(handshake_secret, "derived", empty) 180 let derived_2: *u8 = sys_mmap(64) 181 tls13_derived(handshake_secret, empty_hash32, NX_TLS13_HS_HASH_LEN_SHA256, derived_2) 182 183 // master_secret = HKDF-Extract(derived_2, 0^32) 184 tls13_master_secret(derived_2, NX_TLS13_HS_HASH_LEN_SHA256, out_master_secret) 185 186 // client_application_traffic_secret_0 + server_application_traffic_secret_0 187 tls13_traffic_secret( 188 out_master_secret, NX_TLS13_LABEL_C_AP_TRAFFIC, 189 transcript_hash32, NX_TLS13_HS_HASH_LEN_SHA256, 190 out_cats 191 ) 192 tls13_traffic_secret( 193 out_master_secret, NX_TLS13_LABEL_S_AP_TRAFFIC, 194 transcript_hash32, NX_TLS13_HS_HASH_LEN_SHA256, 195 out_sats 196 ) 197 198 // App AEAD keys + IVs. 199 tls13_traffic_key(out_cats, NX_TLS13_HS_KEY_BYTES_AEAD, out_client_app_key) 200 tls13_traffic_iv (out_cats, NX_TLS13_HS_IV_BYTES_AEAD, out_client_app_iv) 201 tls13_traffic_key(out_sats, NX_TLS13_HS_KEY_BYTES_AEAD, out_server_app_key) 202 tls13_traffic_iv (out_sats, NX_TLS13_HS_IV_BYTES_AEAD, out_server_app_iv) 203 204 return NX_TLS13_HS_VERDICT_OK 205} 206 207// Sealed-enum validity gate. 208func nx_tls13_hs_verdict_is_valid(v: i64) -> i64 { 209 if v < 0 { return 0 } 210 if v >= NX_TLS13_HS_VERDICT_N { return 0 } 211 return 1 212}