nx_tls13_server_session_derive_hs.nx source
↩ module page · 144 lines · 5540 B
1// nx_tls13_server_session_derive_hs.nx -- 4th stone of the TLS 1.3
2// server handshake arc.
3//
4// After emit_sh, both sides compute the handshake-phase keying
5// material from the X25519 ECDHE shared secret + HKDF chain. Per
6// RFC 8446 ยง7.1:
7//
8// Early-Secret = HKDF-Extract(0, IKM=PSK_or_0)
9// empty_derived = Derive-Secret(Early-Secret, "derived", "")
10// Handshake-Secret = HKDF-Extract(salt=empty_derived, IKM=ECDHE)
11//
12// This commit ships handshake_secret derivation. Traffic secrets
13// (client_hs_traffic_secret / server_hs_traffic_secret) + traffic
14// keys (key + iv) ship in the next stones -- each composes a
15// Derive-Secret + HKDF-Expand-Label step against this commit's
16// handshake_secret.
17//
18// State machine: requires SH_SENT; advances state to EE_SENT
19// (NOTE: the message isn't actually emitted yet; the state
20// signifies "ready to emit EE because keys are derived"). v2
21// will introduce an HS_DERIVED intermediate state when the full
22// emit_ee_cert arc lands.
23//
24// license_tier: ORIGINAL
25
26import "nx_syscalls.nx"
27import "nx_x25519_ephemeral.nx"
28import "nx_hkdf.nx"
29import "nx_tls13.nx"
30import "nx_tls13_kdf.nx"
31import "nx_tls13_transcript.nx"
32import "nx_tls13_server_session.nx"
33import "nx_p256_ecdh.nx"
34
35const NX_TLS13_HS_HASH_LEN_SHA256: i64 = 32
36
37// Returns NX_TLS13_SSESSION_OK on success.
38
39func nx_tls13_server_session_derive_hs_secrets(
40 session: *Tls13ServerSession
41) -> i64 {
42 if (session as i64) == 0 { return NX_TLS13_SSESSION_BAD_STATE }
43 if session.state != NX_TLS13_SSTATE_SH_SENT {
44 return NX_TLS13_SSESSION_BAD_STATE
45 }
46 // Step 1: ECDHE shared secret per the negotiated group (rung B4
47 // server side: kex_group 23 = secp256r1 fallback chosen by
48 // recv_ch when the client offered no x25519 share).
49 let ecdhe: *u8 = sys_mmap(32)
50 if session.kex_group == 23 {
51 if (session.client_p256_pub as i64) == 0 {
52 return NX_TLS13_SSESSION_PROTOCOL_ERR
53 }
54 // p256_ecdh_shared validates format + on-curve (boundary:
55 // the share arrived off the wire).
56 let pcv: i64 = p256_ecdh_shared(session.p256_priv,
57 session.client_p256_pub, 65,
58 ecdhe)
59 if pcv == NX_P256_ECDH_BAD_POINT { return NX_TLS13_SSESSION_PROTOCOL_ERR }
60 if pcv != NX_P256_ECDH_OK { return NX_TLS13_SSESSION_INTERNAL }
61 }
62 if session.kex_group != 23 {
63 if (session.client_x25519_pub as i64) == 0 {
64 return NX_TLS13_SSESSION_PROTOCOL_ERR
65 }
66 let ecv: i64 = x25519_shared_secret(session.x25519_priv,
67 session.client_x25519_pub,
68 ecdhe)
69 if ecv != 0 { return NX_TLS13_SSESSION_INTERNAL }
70 }
71
72 // Step 2: Early-Secret = HKDF-Extract(salt=0, IKM=32 zero bytes)
73 // No PSK in our v1 server, so IKM is all zeros.
74 let zero_salt: *u8 = sys_mmap(32)
75 let zero_ikm: *u8 = sys_mmap(32)
76 // sys_mmap returns zero-initialised memory; no explicit zero needed.
77 let early_secret: *u8 = sys_mmap(32)
78 let r1: i64 = hkdf_extract(zero_salt, 32, zero_ikm, 32, early_secret)
79 if r1 != 0 { return NX_TLS13_SSESSION_INTERNAL }
80
81 // Step 3: empty_derived = Derive-Secret(early_secret, "derived", "")
82 // Derive-Secret(s, l, "") = HKDF-Expand-Label(s, l, hash(""), hash_len)
83 // where hash("") for SHA-256 is the well-known 32-byte digest:
84 // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
85 let empty_hash: *u8 = sys_mmap(32)
86 empty_hash[0] = 0xe3 as u8
87 empty_hash[1] = 0xb0 as u8
88 empty_hash[2] = 0xc4 as u8
89 empty_hash[3] = 0x42 as u8
90 empty_hash[4] = 0x98 as u8
91 empty_hash[5] = 0xfc as u8
92 empty_hash[6] = 0x1c as u8
93 empty_hash[7] = 0x14 as u8
94 empty_hash[8] = 0x9a as u8
95 empty_hash[9] = 0xfb as u8
96 empty_hash[10] = 0xf4 as u8
97 empty_hash[11] = 0xc8 as u8
98 empty_hash[12] = 0x99 as u8
99 empty_hash[13] = 0x6f as u8
100 empty_hash[14] = 0xb9 as u8
101 empty_hash[15] = 0x24 as u8
102 empty_hash[16] = 0x27 as u8
103 empty_hash[17] = 0xae as u8
104 empty_hash[18] = 0x41 as u8
105 empty_hash[19] = 0xe4 as u8
106 empty_hash[20] = 0x64 as u8
107 empty_hash[21] = 0x9b as u8
108 empty_hash[22] = 0x93 as u8
109 empty_hash[23] = 0x4c as u8
110 empty_hash[24] = 0xa4 as u8
111 empty_hash[25] = 0x95 as u8
112 empty_hash[26] = 0x99 as u8
113 empty_hash[27] = 0x1b as u8
114 empty_hash[28] = 0x78 as u8
115 empty_hash[29] = 0x52 as u8
116 empty_hash[30] = 0xb8 as u8
117 empty_hash[31] = 0x55 as u8
118
119 let derived_label: *u8 = "derived"
120 let empty_derived: *u8 = sys_mmap(32)
121 let r2: i64 = tls13_hkdf_expand_label(
122 early_secret,
123 derived_label, 7,
124 empty_hash, 32,
125 32,
126 empty_derived)
127 if r2 != NX_TLS13_KDF_VERDICT_OK {
128 return NX_TLS13_SSESSION_INTERNAL
129 }
130
131 // Step 4: Handshake-Secret = HKDF-Extract(salt=empty_derived, IKM=ECDHE)
132 let handshake_secret: *u8 = sys_mmap(32)
133 let r3: i64 = hkdf_extract(empty_derived, 32, ecdhe, 32, handshake_secret)
134 if r3 != 0 { return NX_TLS13_SSESSION_INTERNAL }
135
136 session.handshake_secret = handshake_secret
137
138 // State advance: SH_SENT -> EE_SENT (placeholder; EE message
139 // not actually emitted yet -- the EE bytes are queued for the
140 // next stone and the state will be the precondition there).
141 session.state = NX_TLS13_SSTATE_EE_SENT
142
143 return NX_TLS13_SSESSION_OK
144}