stackforge_core/layer/tls/
session.rs1#[cfg(feature = "tls")]
7use super::crypto::hash::TlsHash;
8#[cfg(feature = "tls")]
9use super::crypto::suites::{CipherSuite, find_suite};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ConnectionEnd {
14 Client,
15 Server,
16}
17
18#[derive(Debug, Clone)]
20pub struct ConnState {
21 pub seq_num: u64,
23 pub key: Vec<u8>,
25 pub mac_key: Vec<u8>,
27 pub iv: Vec<u8>,
29}
30
31impl ConnState {
32 #[must_use]
33 pub fn new() -> Self {
34 Self {
35 seq_num: 0,
36 key: Vec::new(),
37 mac_key: Vec::new(),
38 iv: Vec::new(),
39 }
40 }
41
42 pub fn next_seq_num(&mut self) -> u64 {
44 let n = self.seq_num;
45 self.seq_num += 1;
46 n
47 }
48}
49
50impl Default for ConnState {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct TlsSession {
59 pub version: u16,
61 pub cipher_suite_id: u16,
63 pub client_random: [u8; 32],
65 pub server_random: [u8; 32],
67 pub master_secret: Vec<u8>,
69 pub read_state: ConnState,
71 pub write_state: ConnState,
73 pub connection_end: ConnectionEnd,
75 pub transcript: Vec<u8>,
77 pub handshake_complete: bool,
79 pub extended_master_secret: bool,
81 pub encrypt_then_mac: bool,
83}
84
85impl TlsSession {
86 #[must_use]
88 pub fn new_client() -> Self {
89 Self {
90 version: 0x0303,
91 cipher_suite_id: 0,
92 client_random: [0u8; 32],
93 server_random: [0u8; 32],
94 master_secret: Vec::new(),
95 read_state: ConnState::new(),
96 write_state: ConnState::new(),
97 connection_end: ConnectionEnd::Client,
98 transcript: Vec::new(),
99 handshake_complete: false,
100 extended_master_secret: false,
101 encrypt_then_mac: false,
102 }
103 }
104
105 #[must_use]
107 pub fn new_server() -> Self {
108 let mut session = Self::new_client();
109 session.connection_end = ConnectionEnd::Server;
110 session
111 }
112
113 pub fn update_transcript(&mut self, data: &[u8]) {
115 self.transcript.extend_from_slice(data);
116 }
117
118 #[cfg(feature = "tls")]
120 pub fn derive_keys(&mut self) {
121 let suite = match find_suite(self.cipher_suite_id) {
122 Some(s) => s,
123 None => return,
124 };
125
126 let hash = match suite.hash {
127 super::crypto::suites::SuiteHash::Sha256 => TlsHash::Sha256,
128 super::crypto::suites::SuiteHash::Sha384 => TlsHash::Sha384,
129 _ => TlsHash::Sha256,
130 };
131
132 let prf = super::crypto::prf::Prf::new(hash, self.version);
133 let key_block_len = suite.key_block_len();
134 let key_block = prf.derive_key_block(
135 &self.master_secret,
136 &self.server_random,
137 &self.client_random,
138 key_block_len,
139 );
140
141 self.install_keys(suite, &key_block);
142 }
143
144 #[cfg(feature = "tls")]
146 fn install_keys(&mut self, suite: &CipherSuite, key_block: &[u8]) {
147 let mut offset = 0;
148
149 let client_mac = key_block[offset..offset + suite.mac_len].to_vec();
151 offset += suite.mac_len;
152 let server_mac = key_block[offset..offset + suite.mac_len].to_vec();
154 offset += suite.mac_len;
155 let client_key = key_block[offset..offset + suite.key_len].to_vec();
157 offset += suite.key_len;
158 let server_key = key_block[offset..offset + suite.key_len].to_vec();
160 offset += suite.key_len;
161 let client_iv = key_block[offset..offset + suite.iv_len].to_vec();
163 offset += suite.iv_len;
164 let server_iv = key_block[offset..offset + suite.iv_len].to_vec();
166 let _ = offset + suite.iv_len;
167
168 match self.connection_end {
169 ConnectionEnd::Client => {
170 self.write_state.key = client_key;
171 self.write_state.mac_key = client_mac;
172 self.write_state.iv = client_iv;
173 self.read_state.key = server_key;
174 self.read_state.mac_key = server_mac;
175 self.read_state.iv = server_iv;
176 },
177 ConnectionEnd::Server => {
178 self.read_state.key = client_key;
179 self.read_state.mac_key = client_mac;
180 self.read_state.iv = client_iv;
181 self.write_state.key = server_key;
182 self.write_state.mac_key = server_mac;
183 self.write_state.iv = server_iv;
184 },
185 }
186 }
187}
188
189#[derive(Debug, Clone)]
194pub struct NssKeyLogEntry {
195 pub label: String,
197 pub client_random: Vec<u8>,
199 pub secret: Vec<u8>,
201}
202
203#[must_use]
205pub fn parse_nss_key_log(content: &str) -> Vec<NssKeyLogEntry> {
206 let mut entries = Vec::new();
207
208 for line in content.lines() {
209 let line = line.trim();
210 if line.is_empty() || line.starts_with('#') {
211 continue;
212 }
213
214 let parts: Vec<&str> = line.splitn(3, ' ').collect();
215 if parts.len() != 3 {
216 continue;
217 }
218
219 let label = parts[0].to_string();
220 let client_random = match hex_decode(parts[1]) {
221 Some(v) => v,
222 None => continue,
223 };
224 let secret = match hex_decode(parts[2]) {
225 Some(v) => v,
226 None => continue,
227 };
228
229 entries.push(NssKeyLogEntry {
230 label,
231 client_random,
232 secret,
233 });
234 }
235
236 entries
237}
238
239fn hex_decode(s: &str) -> Option<Vec<u8>> {
241 if !s.len().is_multiple_of(2) {
242 return None;
243 }
244 let mut result = Vec::with_capacity(s.len() / 2);
245 for i in (0..s.len()).step_by(2) {
246 let byte = u8::from_str_radix(&s[i..i + 2], 16).ok()?;
247 result.push(byte);
248 }
249 Some(result)
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn test_session_creation() {
258 let session = TlsSession::new_client();
259 assert_eq!(session.connection_end, ConnectionEnd::Client);
260 assert_eq!(session.version, 0x0303);
261 assert!(!session.handshake_complete);
262 }
263
264 #[test]
265 fn test_conn_state_seq_num() {
266 let mut state = ConnState::new();
267 assert_eq!(state.next_seq_num(), 0);
268 assert_eq!(state.next_seq_num(), 1);
269 assert_eq!(state.next_seq_num(), 2);
270 }
271
272 #[test]
273 fn test_transcript() {
274 let mut session = TlsSession::new_client();
275 session.update_transcript(b"hello");
276 session.update_transcript(b" world");
277 assert_eq!(session.transcript, b"hello world");
278 }
279
280 #[test]
281 fn test_parse_nss_key_log() {
282 let content = "# SSL/TLS secrets log file
283CLIENT_RANDOM 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 aabbccdd
284CLIENT_HANDSHAKE_TRAFFIC_SECRET 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 eeff0011
285";
286 let entries = parse_nss_key_log(content);
287 assert_eq!(entries.len(), 2);
288 assert_eq!(entries[0].label, "CLIENT_RANDOM");
289 assert_eq!(entries[0].client_random.len(), 32);
290 assert_eq!(entries[0].secret, vec![0xaa, 0xbb, 0xcc, 0xdd]);
291 assert_eq!(entries[1].label, "CLIENT_HANDSHAKE_TRAFFIC_SECRET");
292 }
293
294 #[test]
295 fn test_hex_decode() {
296 assert_eq!(hex_decode("aabb"), Some(vec![0xaa, 0xbb]));
297 assert_eq!(hex_decode("0102"), Some(vec![0x01, 0x02]));
298 assert_eq!(hex_decode("abc"), None); assert_eq!(hex_decode("zz"), None); }
301}