webrtc_sniffer/dtls/
state.rs1use 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 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 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 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}