brass_aphid_wire_decryption/decryption/
stream_decrypter.rs

1use crate::decryption::{
2    key_manager::KeyManager, tls_stream::TlsStream, transcript::Transcript, Mode,
3};
4use brass_aphid_wire_messages::{
5    iana::{self, Protocol},
6    protocol::{
7        content_value::{ContentValue, HandshakeMessageValue},
8        ServerHelloConfusionMode,
9    },
10};
11use std::{
12    path::PathBuf,
13    sync::{Arc, Mutex},
14};
15
16/// The conversation state tracks the context necessary to decrypt the connection.
17///
18/// For example, we need to know the cipher to know how to decrypt encrypted records.
19#[derive(Debug, Default, Clone)]
20pub struct ConversationState {
21    pub client_random: Option<Vec<u8>>,
22    pub selected_protocol: Option<Protocol>,
23    pub selected_cipher: Option<iana::Cipher>,
24}
25
26#[derive(Debug)]
27pub struct StreamDecrypter {
28    pub state: ConversationState,
29    /// The key_manager which holds all of the key log entries from the connection.
30    ///
31    /// The Key Manager is associated with a single config, so secrets must be retrieved
32    /// with the connection ID (client hello random).
33    key_manager: KeyManager,
34    pub transcript: Arc<Mutex<Vec<(Mode, ContentValue)>>>,
35    pub client_stream: TlsStream,
36    pub server_stream: TlsStream,
37}
38
39impl StreamDecrypter {
40    pub fn new(key_manager: KeyManager) -> Self {
41        Self {
42            state: ConversationState::default(),
43            key_manager,
44            transcript: Default::default(),
45            client_stream: TlsStream::new(Mode::Client),
46            server_stream: TlsStream::new(Mode::Server),
47        }
48    }
49
50    /// Record a transmitted bytes.
51    ///
52    /// Received bytes should be recorded as transmissions of the peer.
53    ///
54    ///
55    pub fn record_tx(&mut self, data: &[u8], sender: Mode) {
56        match sender {
57            Mode::Client => self.client_stream.feed_bytes(data),
58            Mode::Server => self.server_stream.feed_bytes(data),
59        };
60    }
61
62    pub fn transcript(&self) -> Transcript {
63        Transcript {
64            record_transcript: Default::default(),
65            content_transcript: Mutex::new(self.transcript.lock().unwrap().clone()),
66        }
67    }
68
69    pub fn dump_transcript(&self, file: &PathBuf) {
70        let transcript = format!("{:#?}", self.transcript);
71        std::fs::write(file, transcript).unwrap();
72    }
73
74    pub fn assemble_records(&mut self, mode: Mode) -> std::io::Result<()> {
75        // TODO: error handling. We currently assume that all errors are just because
76        // there isn't enough data. Which will not be true into the future. Also
77        // should think more about the "not enough data" error.
78
79        /* no op */
80
81        Ok(())
82    }
83
84    /// decrypt the records sent by `mode`.
85    pub fn decrypt_records(&mut self, mode: Mode) -> std::io::Result<()> {
86        let content = match mode {
87            Mode::Client => self
88                .client_stream
89                .digest_bytes(&mut self.state, &self.key_manager),
90            Mode::Server => self
91                .server_stream
92                .digest_bytes(&mut self.state, &self.key_manager),
93        }?;
94
95        // if the server sent a hello retry, we need to let the client stream know
96        // that it should move the key space forwards
97        // TODO: could we remove the context from this, and just peek at the records
98        // in the client
99        let hello_retry = content.iter().any(|content| {
100            matches!(
101                content,
102                ContentValue::Handshake(HandshakeMessageValue::ServerHelloConfusion(
103                    ServerHelloConfusionMode::HelloRetryRequest(_)
104                ))
105            )
106        });
107
108        if hello_retry {
109            // hello retry is indicated by the server hello
110            debug_assert_eq!(mode, Mode::Server);
111            self.client_stream.suppress_next_key_state();
112        }
113
114        self.transcript
115            .lock()
116            .unwrap()
117            .extend(content.into_iter().map(|content| (mode, content)));
118        Ok(())
119    }
120}