Skip to main content

stackforge_core/layer/tls/
session.rs

1//! TLS Session state management.
2//!
3//! Tracks the state of a TLS connection including version, cipher suite,
4//! randoms, master secret, and derived keys.
5
6#[cfg(feature = "tls")]
7use super::crypto::hash::TlsHash;
8#[cfg(feature = "tls")]
9use super::crypto::suites::{CipherSuite, find_suite};
10
11/// Connection end identifier.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ConnectionEnd {
14    Client,
15    Server,
16}
17
18/// TLS connection state for one direction (read or write).
19#[derive(Debug, Clone)]
20pub struct ConnState {
21    /// Sequence number for MAC computation.
22    pub seq_num: u64,
23    /// Encryption key.
24    pub key: Vec<u8>,
25    /// MAC key (for CBC suites).
26    pub mac_key: Vec<u8>,
27    /// IV (fixed for AEAD, per-record for CBC in TLS 1.0).
28    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    /// Increment the sequence number and return the previous value.
42    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/// TLS session state.
56#[derive(Debug, Clone)]
57pub struct TlsSession {
58    /// Negotiated TLS version.
59    pub version: u16,
60    /// Selected cipher suite ID.
61    pub cipher_suite_id: u16,
62    /// Client random (32 bytes).
63    pub client_random: [u8; 32],
64    /// Server random (32 bytes).
65    pub server_random: [u8; 32],
66    /// Master secret (48 bytes).
67    pub master_secret: Vec<u8>,
68    /// Read (decryption) state.
69    pub read_state: ConnState,
70    /// Write (encryption) state.
71    pub write_state: ConnState,
72    /// Our connection end.
73    pub connection_end: ConnectionEnd,
74    /// Handshake transcript (for verify_data and TLS 1.3).
75    pub transcript: Vec<u8>,
76    /// Whether the handshake is complete.
77    pub handshake_complete: bool,
78    /// Extended master secret enabled.
79    pub extended_master_secret: bool,
80    /// Encrypt-then-MAC enabled.
81    pub encrypt_then_mac: bool,
82}
83
84impl TlsSession {
85    /// Create a new client session.
86    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    /// Create a new server session.
104    pub fn new_server() -> Self {
105        let mut session = Self::new_client();
106        session.connection_end = ConnectionEnd::Server;
107        session
108    }
109
110    /// Append handshake message bytes to the transcript.
111    pub fn update_transcript(&mut self, data: &[u8]) {
112        self.transcript.extend_from_slice(data);
113    }
114
115    /// Derive keys from master secret and install them.
116    #[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    /// Install keys from key block.
142    #[cfg(feature = "tls")]
143    fn install_keys(&mut self, suite: &CipherSuite, key_block: &[u8]) {
144        let mut offset = 0;
145
146        // client_write_MAC_key
147        let client_mac = key_block[offset..offset + suite.mac_len].to_vec();
148        offset += suite.mac_len;
149        // server_write_MAC_key
150        let server_mac = key_block[offset..offset + suite.mac_len].to_vec();
151        offset += suite.mac_len;
152        // client_write_key
153        let client_key = key_block[offset..offset + suite.key_len].to_vec();
154        offset += suite.key_len;
155        // server_write_key
156        let server_key = key_block[offset..offset + suite.key_len].to_vec();
157        offset += suite.key_len;
158        // client_write_IV
159        let client_iv = key_block[offset..offset + suite.iv_len].to_vec();
160        offset += suite.iv_len;
161        // server_write_IV
162        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/// NSS key log file parser.
187///
188/// Parses NSS key log files (SSLKEYLOGFILE format) used by Wireshark
189/// for TLS session decryption.
190#[derive(Debug, Clone)]
191pub struct NssKeyLogEntry {
192    /// Label (e.g., "CLIENT_RANDOM", "CLIENT_HANDSHAKE_TRAFFIC_SECRET").
193    pub label: String,
194    /// Client random or other identifier (hex-encoded in the file, decoded here).
195    pub client_random: Vec<u8>,
196    /// Secret value.
197    pub secret: Vec<u8>,
198}
199
200/// Parse an NSS key log file.
201pub 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
235/// Simple hex decoder.
236fn 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); // odd length
295        assert_eq!(hex_decode("zz"), None); // invalid hex
296    }
297}