webrtc_sniffer/dtls/
state.rs

1use std::borrow::Cow;
2
3use nom::{multi::many1, IResult, Parser};
4use rand::{rngs::StdRng, SeedableRng};
5
6use crate::dtls::handshake::{HandshakeInner, ServerHello};
7
8use super::{
9    handshake::{Extension, HandshakeMessage},
10    header::{Chunk, ContentType},
11};
12
13pub struct State {
14    rng_seed: [u8; 32],
15    inner: Option<Inner>,
16}
17
18enum Inner {
19    Initial,
20    ClientHello {
21        client_random: [u8; 32],
22    },
23    BothHello(HelloMsgs),
24    ServerKey {
25        hello: HelloMsgs,
26        // <https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8>
27        curve_name: u16,
28        server_pk: Vec<u8>,
29    },
30    BothKey {
31        hello: HelloMsgs,
32        keys: BothKey,
33    },
34}
35
36#[allow(dead_code)]
37struct HelloMsgs {
38    client_random: [u8; 32],
39    server_random: [u8; 32],
40    session_id: Vec<u8>,
41    cipher_suite: u16,
42    extensions: Vec<Extension>,
43}
44
45#[allow(dead_code)]
46struct BothKey {
47    curve_name: u16,
48    server_pk: Vec<u8>,
49    client_pk: Vec<u8>,
50}
51
52impl State {
53    pub fn new(rng_seed: [u8; 32]) -> Self {
54        State {
55            rng_seed,
56            inner: Some(Inner::Initial),
57        }
58    }
59
60    pub fn handle<'d>(&mut self, data: &'d [u8], _incoming: bool) -> IResult<&'d [u8], ()> {
61        let (data, chunks) = many1(Chunk::parse).parse(data)?;
62        for chunk in chunks {
63            log::info!("{chunk}");
64            #[allow(clippy::single_match)]
65            match chunk.ty {
66                ContentType::Handshake => self.handle_handshake(chunk.body),
67                _ => {}
68            }
69        }
70
71        Ok((data, ()))
72    }
73
74    fn handle_handshake(&mut self, msg_bytes: &[u8]) {
75        let Some(state) = self.inner.take() else {
76            log::warn!("ignore datagram, invalid state");
77            return;
78        };
79
80        let mut msg_bytes = Cow::Borrowed(msg_bytes);
81        if let Inner::BothKey { hello, keys } = &state {
82            let bytes = msg_bytes.to_mut();
83            // decrypt
84            let _ = (hello, keys, bytes);
85        }
86        let msg = match HandshakeMessage::parse(&msg_bytes) {
87            Ok((_, msg)) => msg,
88            Err(err) => {
89                log::error!("{err}");
90                return;
91            }
92        };
93
94        let HandshakeMessage {
95            length,
96            message_seq,
97            fragment_offset,
98            fragment_length,
99            inner: msg,
100        } = msg;
101        let _ = message_seq;
102        log::info!("HANDSHAKE: {msg}");
103
104        if fragment_offset != 0 || length != fragment_length {
105            log::error!("collecting fragments is not implemented");
106            self.inner = None;
107            return;
108        }
109
110        let state = match (state, msg) {
111            (Inner::Initial, HandshakeInner::ClientHello(msg)) => {
112                let client_random = msg.random;
113                if msg.cookie.is_empty() {
114                    self.inner = Some(Inner::Initial);
115                    return;
116                }
117
118                use sha2::{
119                    digest::{FixedOutput, Update},
120                    Sha256,
121                };
122                let seed = Sha256::default()
123                    .chain(self.rng_seed)
124                    .chain(&msg.cookie)
125                    .finalize_fixed()
126                    .into();
127                dbg!(format!("{seed:x?}"));
128                let _rng = StdRng::from_seed(seed);
129
130                let _ = (
131                    msg.session_id,
132                    msg.cookie,
133                    msg.cipher_suites,
134                    msg.compression_methods,
135                    msg.extensions,
136                );
137                Inner::ClientHello { client_random }
138            }
139            (Inner::ClientHello { client_random }, HandshakeInner::ServerHello(msg)) => {
140                let ServerHello {
141                    random,
142                    session_id,
143                    cipher_suite,
144                    compression_method,
145                    extensions,
146                } = msg;
147                if compression_method != 0 {
148                    log::error!("compression method {compression_method} is not implemented");
149                    return;
150                }
151                Inner::BothHello(HelloMsgs {
152                    client_random,
153                    server_random: random,
154                    session_id,
155                    cipher_suite,
156                    extensions,
157                })
158            }
159            (Inner::BothHello(hello), HandshakeInner::ServerKeyExchange(msg)) => {
160                // check signature
161                let _ = msg.signature;
162                Inner::ServerKey {
163                    hello,
164                    curve_name: msg.curve_name,
165                    server_pk: msg.public_key,
166                }
167            }
168            (
169                Inner::ServerKey {
170                    hello,
171                    curve_name,
172                    server_pk,
173                },
174                HandshakeInner::ClientKeyExchange(msg),
175            ) => {
176                let keys = BothKey {
177                    curve_name,
178                    server_pk,
179                    client_pk: msg.public_key,
180                };
181                Inner::BothKey { hello, keys }
182            }
183            (state, _) => {
184                log::warn!("ignore handshake msg");
185                state
186            }
187        };
188        self.inner = Some(state);
189    }
190}