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    #[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    /// Increment the sequence number and return the previous value.
43    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/// TLS session state.
57#[derive(Debug, Clone)]
58pub struct TlsSession {
59    /// Negotiated TLS version.
60    pub version: u16,
61    /// Selected cipher suite ID.
62    pub cipher_suite_id: u16,
63    /// Client random (32 bytes).
64    pub client_random: [u8; 32],
65    /// Server random (32 bytes).
66    pub server_random: [u8; 32],
67    /// Master secret (48 bytes).
68    pub master_secret: Vec<u8>,
69    /// Read (decryption) state.
70    pub read_state: ConnState,
71    /// Write (encryption) state.
72    pub write_state: ConnState,
73    /// Our connection end.
74    pub connection_end: ConnectionEnd,
75    /// Handshake transcript (for `verify_data` and TLS 1.3).
76    pub transcript: Vec<u8>,
77    /// Whether the handshake is complete.
78    pub handshake_complete: bool,
79    /// Extended master secret enabled.
80    pub extended_master_secret: bool,
81    /// Encrypt-then-MAC enabled.
82    pub encrypt_then_mac: bool,
83}
84
85impl TlsSession {
86    /// Create a new client session.
87    #[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    /// Create a new server session.
106    #[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    /// Append handshake message bytes to the transcript.
114    pub fn update_transcript(&mut self, data: &[u8]) {
115        self.transcript.extend_from_slice(data);
116    }
117
118    /// Derive keys from master secret and install them.
119    #[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    /// Install keys from key block.
145    #[cfg(feature = "tls")]
146    fn install_keys(&mut self, suite: &CipherSuite, key_block: &[u8]) {
147        let mut offset = 0;
148
149        // client_write_MAC_key
150        let client_mac = key_block[offset..offset + suite.mac_len].to_vec();
151        offset += suite.mac_len;
152        // server_write_MAC_key
153        let server_mac = key_block[offset..offset + suite.mac_len].to_vec();
154        offset += suite.mac_len;
155        // client_write_key
156        let client_key = key_block[offset..offset + suite.key_len].to_vec();
157        offset += suite.key_len;
158        // server_write_key
159        let server_key = key_block[offset..offset + suite.key_len].to_vec();
160        offset += suite.key_len;
161        // client_write_IV
162        let client_iv = key_block[offset..offset + suite.iv_len].to_vec();
163        offset += suite.iv_len;
164        // server_write_IV
165        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/// NSS key log file parser.
190///
191/// Parses NSS key log files (SSLKEYLOGFILE format) used by Wireshark
192/// for TLS session decryption.
193#[derive(Debug, Clone)]
194pub struct NssKeyLogEntry {
195    /// Label (e.g., "`CLIENT_RANDOM`", "`CLIENT_HANDSHAKE_TRAFFIC_SECRET`").
196    pub label: String,
197    /// Client random or other identifier (hex-encoded in the file, decoded here).
198    pub client_random: Vec<u8>,
199    /// Secret value.
200    pub secret: Vec<u8>,
201}
202
203/// Parse an NSS key log file.
204#[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
239/// Simple hex decoder.
240fn 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); // odd length
299        assert_eq!(hex_decode("zz"), None); // invalid hex
300    }
301}