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