retina_core/protocols/stream/quic/
parser.rs1use crate::protocols::stream::quic::crypto::calc_init_keys;
6use crate::protocols::stream::quic::frame::QuicFrame;
7use crate::protocols::stream::quic::header::{
8 LongHeaderPacketType, QuicLongHeader, QuicShortHeader,
9};
10use crate::protocols::stream::quic::{QuicError, QuicPacket};
11use crate::protocols::stream::tls::Tls;
12use crate::protocols::stream::{
13 ConnParsable, ConnState, L4Pdu, ParseResult, ProbeResult, Session, SessionData,
14};
15use byteorder::{BigEndian, ByteOrder};
16use std::collections::HashSet;
17use tls_parser::parse_tls_message_handshake;
18
19use super::QuicConn;
20
21#[derive(Debug)]
22pub struct QuicParser {
23 sessions: Vec<QuicConn>,
28}
29
30impl Default for QuicParser {
31 fn default() -> Self {
32 QuicParser {
33 sessions: vec![QuicConn::new()],
34 }
35 }
36}
37
38impl ConnParsable for QuicParser {
39 fn parse(&mut self, pdu: &L4Pdu) -> ParseResult {
40 let offset = pdu.offset();
41 let length = pdu.length();
42 if length == 0 {
43 return ParseResult::Skipped;
44 }
45
46 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
47 if !self.sessions.is_empty() {
48 return self.sessions[0].parse_packet(data, pdu.dir);
49 }
50 ParseResult::Skipped
51 } else {
52 log::warn!("Malformed packet on parse");
53 ParseResult::Skipped
54 }
55 }
56
57 fn probe(&self, pdu: &L4Pdu) -> ProbeResult {
58 if pdu.length() < 5 {
59 return ProbeResult::Unsure;
60 }
61
62 let offset = pdu.offset();
63 let length = pdu.length();
64
65 if let Ok(data) = (pdu.mbuf).get_data_slice(offset, length) {
66 if (data[0] & 0x40) == 0 {
68 return ProbeResult::NotForUs;
69 }
70
71 if (data[0] & 0x80) != 0 {
72 if data.len() < 6 {
74 return ProbeResult::Unsure;
75 }
76
77 let version = ((data[1] as u32) << 24)
79 | ((data[2] as u32) << 16)
80 | ((data[3] as u32) << 8)
81 | (data[4] as u32);
82 match QuicVersion::from_u32(version) {
83 QuicVersion::Unknown => ProbeResult::NotForUs,
84 _ => ProbeResult::Certain,
85 }
86 } else {
87 ProbeResult::Unsure
88 }
89 } else {
90 log::warn!("Malformed packet");
91 ProbeResult::Error
92 }
93 }
94
95 fn remove_session(&mut self, session_id: usize) -> Option<Session> {
96 self.sessions.pop().map(|quic| Session {
97 data: SessionData::Quic(Box::new(quic)),
98 id: session_id,
99 })
100 }
101
102 fn drain_sessions(&mut self) -> Vec<Session> {
103 self.sessions
104 .drain(..)
105 .map(|quic| Session {
106 data: SessionData::Quic(Box::new(quic)),
107 id: 0,
108 })
109 .collect()
110 }
111
112 fn session_match_state(&self) -> ConnState {
113 ConnState::Parsing
114 }
115 fn session_nomatch_state(&self) -> ConnState {
116 ConnState::Parsing
117 }
118}
119
120#[derive(Debug, PartialEq, Eq, Hash)]
122#[repr(u32)]
123pub enum QuicVersion {
124 ReservedNegotiation = 0x00000000,
125 Rfc9000 = 0x00000001, Rfc9369 = 0x6b3343cf, Draft27 = 0xff00001b, Draft28 = 0xff00001c, Draft29 = 0xff00001d, Mvfst27 = 0xfaceb002, Unknown,
132}
133
134impl QuicVersion {
135 pub fn from_u32(version: u32) -> Self {
136 match version {
137 0x00000000 => QuicVersion::ReservedNegotiation,
138 0x00000001 => QuicVersion::Rfc9000,
139 0x6b3343cf => QuicVersion::Rfc9369,
140 0xff00001b => QuicVersion::Draft27,
141 0xff00001c => QuicVersion::Draft28,
142 0xff00001d => QuicVersion::Draft29,
143 0xfaceb002 => QuicVersion::Mvfst27,
144 _ => QuicVersion::Unknown,
145 }
146 }
147}
148
149impl QuicPacket {
150 pub fn vec_u8_to_hex_string(vec: &[u8]) -> String {
152 vec.iter()
153 .map(|&byte| format!("{:02x}", byte))
154 .collect::<Vec<String>>()
155 .join("")
156 }
157
158 pub fn get_var_len(a: u8) -> Result<usize, QuicError> {
161 let two_msb = a >> 6;
162 match two_msb {
163 0b00 => Ok(1),
164 0b01 => Ok(2),
165 0b10 => Ok(4),
166 0b11 => Ok(8),
167 _ => Err(QuicError::UnsupportedVarLen),
168 }
169 }
170
171 pub fn slice_to_u64(data: &[u8]) -> Result<u64, QuicError> {
173 if data.len() > 8 {
174 return Err(QuicError::UnsupportedVarLen);
175 }
176
177 let mut result: u64 = 0;
178 for &byte in data {
179 result = (result << 8) | u64::from(byte);
180 }
181 result &= !(0b11 << ((data.len() * 8) - 2)); Ok(result)
183 }
184
185 pub fn access_data(data: &[u8], start: usize, end: usize) -> Result<&[u8], QuicError> {
186 if end < start {
187 return Err(QuicError::InvalidDataIndices);
188 }
189 if data.len() < end {
190 return Err(QuicError::PacketTooShort);
191 }
192 Ok(&data[start..end])
193 }
194
195 pub fn parse_from(
197 conn: &mut QuicConn,
198 data: &[u8],
199 mut offset: usize,
200 dir: bool,
201 ) -> Result<(QuicPacket, usize), QuicError> {
202 let packet_header_byte = QuicPacket::access_data(data, offset, offset + 1)?[0];
203 offset += 1;
204 if (packet_header_byte & 0x40) == 0 {
206 return Err(QuicError::FixedBitNotSet);
207 }
208 if (packet_header_byte & 0x80) != 0 {
210 let packet_type = LongHeaderPacketType::from_u8((packet_header_byte & 0x30) >> 4)?;
213 let type_specific = packet_header_byte & 0x0F; let version_bytes = QuicPacket::access_data(data, offset, offset + 4)?;
216 let version = ((version_bytes[0] as u32) << 24)
217 | ((version_bytes[1] as u32) << 16)
218 | ((version_bytes[2] as u32) << 8)
219 | (version_bytes[3] as u32);
220 if QuicVersion::from_u32(version) == QuicVersion::Unknown {
221 return Err(QuicError::UnknownVersion);
222 }
223 offset += 4;
224 let dcid_len = QuicPacket::access_data(data, offset, offset + 1)?[0];
226 offset += 1;
227 let dcid_bytes = QuicPacket::access_data(data, offset, offset + dcid_len as usize)?;
228 let dcid = QuicPacket::vec_u8_to_hex_string(dcid_bytes);
229 if dcid_len > 0 && !conn.cids.contains(&dcid) {
230 conn.cids.insert(dcid.clone());
231 }
232 offset += dcid_len as usize;
233 let scid_len = QuicPacket::access_data(data, offset, offset + 1)?[0];
235 offset += 1;
236 let scid_bytes = QuicPacket::access_data(data, offset, offset + scid_len as usize)?;
237 let scid = QuicPacket::vec_u8_to_hex_string(scid_bytes);
238 if scid_len > 0 && !conn.cids.contains(&scid) {
239 conn.cids.insert(scid.clone());
240 }
241 offset += scid_len as usize;
242
243 let token_len;
244 let token;
245 let packet_len;
246 let retry_tag;
247 let decrypted_payload;
248 match packet_type {
250 LongHeaderPacketType::Initial => {
251 retry_tag = None;
252 let token_len_len = QuicPacket::get_var_len(
254 QuicPacket::access_data(data, offset, offset + 1)?[0],
255 )?;
256 let token_len_bytes =
257 QuicPacket::access_data(data, offset, offset + token_len_len)?;
258 token_len = Some(QuicPacket::slice_to_u64(token_len_bytes)?);
259 offset += token_len_len;
260 let token_bytes = QuicPacket::access_data(
261 data,
262 offset,
263 offset + token_len.unwrap() as usize,
264 )?;
265 token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes));
266 offset += token_len.unwrap() as usize;
267 let packet_len_len = QuicPacket::get_var_len(
269 QuicPacket::access_data(data, offset, offset + 1)?[0],
270 )?;
271 let packet_len_bytes =
272 QuicPacket::access_data(data, offset, offset + packet_len_len)?;
273 packet_len = Some(QuicPacket::slice_to_u64(packet_len_bytes)?);
274 offset += packet_len_len;
275 if conn.client_opener.is_none() {
276 let [client_opener, server_opener] = calc_init_keys(dcid_bytes, version)?;
278 conn.client_opener = Some(client_opener);
279 conn.server_opener = Some(server_opener);
280 }
281 let sample_len = conn.client_opener.as_ref().unwrap().sample_len();
283 let hp_sample =
284 QuicPacket::access_data(data, offset + 4, offset + 4 + sample_len)?;
285 let mask = if dir {
286 conn.client_opener.as_ref().unwrap().new_mask(hp_sample)?
287 } else {
288 conn.server_opener.as_ref().unwrap().new_mask(hp_sample)?
289 };
290 let unprotected_header = packet_header_byte ^ (mask[0] & 0b00001111);
292 if (unprotected_header >> 2) & 0b00000011 != 0 {
293 return Err(QuicError::FailedHeaderProtection);
294 }
295 let packet_num_len = ((unprotected_header & 0b00000011) + 1) as usize;
297 let packet_number_bytes =
298 QuicPacket::access_data(data, offset, offset + packet_num_len)?;
299 let mut packet_number = vec![0; 4 - packet_num_len];
300 for i in 0..packet_num_len {
301 packet_number.push(packet_number_bytes[i] ^ mask[i + 1]);
302 }
303
304 let initial_packet_number_bytes = &packet_number[4 - packet_num_len..];
305 let packet_number_int = BigEndian::read_i32(&packet_number);
306 offset += packet_num_len;
307 let tag_len = conn.client_opener.as_ref().unwrap().alg().tag_len();
309 if (packet_len.unwrap() as usize) < (tag_len + packet_num_len) {
310 return Err(QuicError::PacketTooShort);
311 }
312 let cipher_text_len = packet_len.unwrap() as usize - tag_len - packet_num_len;
313 let mut encrypted_payload =
314 QuicPacket::access_data(data, offset, offset + cipher_text_len)?.to_vec();
315 offset += cipher_text_len;
316 let tag = QuicPacket::access_data(data, offset, offset + tag_len)?;
318 offset += tag_len;
319 let mut ad = Vec::new();
321 ad.append(&mut [unprotected_header].to_vec());
322 ad.append(&mut version_bytes.to_vec());
323 ad.append(&mut [dcid_len].to_vec());
324 ad.append(&mut dcid_bytes.to_vec());
325 ad.append(&mut [scid_len].to_vec());
326 ad.append(&mut scid_bytes.to_vec());
327 ad.append(&mut token_len_bytes.to_vec());
328 ad.append(&mut token_bytes.to_vec());
329 ad.append(&mut packet_len_bytes.to_vec());
330 ad.append(&mut initial_packet_number_bytes.to_vec());
331 if dir {
333 decrypted_payload =
334 Some(conn.client_opener.as_ref().unwrap().open_with_u64_counter(
335 packet_number_int as u64,
336 &ad,
337 &mut encrypted_payload,
338 tag,
339 )?);
340 } else {
341 decrypted_payload =
342 Some(conn.server_opener.as_ref().unwrap().open_with_u64_counter(
343 packet_number_int as u64,
344 &ad,
345 &mut encrypted_payload,
346 tag,
347 )?);
348 }
349 }
350 LongHeaderPacketType::ZeroRTT | LongHeaderPacketType::Handshake => {
351 token_len = None;
352 token = None;
353 retry_tag = None;
354 decrypted_payload = None;
355 let packet_len_len = QuicPacket::get_var_len(
357 QuicPacket::access_data(data, offset, offset + 1)?[0],
358 )?;
359 packet_len = Some(QuicPacket::slice_to_u64(QuicPacket::access_data(
360 data,
361 offset,
362 offset + packet_len_len,
363 )?)?);
364 offset += packet_len_len;
365 offset += packet_len.unwrap() as usize;
366 }
367 LongHeaderPacketType::Retry => {
368 packet_len = None;
369 decrypted_payload = None;
370 if data.len() > (offset + 16) {
371 token_len = Some((data.len() - offset - 16) as u64);
372 } else {
373 return Err(QuicError::PacketTooShort);
374 }
375 let token_bytes = QuicPacket::access_data(
377 data,
378 offset,
379 offset + token_len.unwrap() as usize,
380 )?;
381 token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes));
382 offset += token_len.unwrap() as usize;
383 let retry_tag_bytes = QuicPacket::access_data(data, offset, offset + 16)?;
385 retry_tag = Some(QuicPacket::vec_u8_to_hex_string(retry_tag_bytes));
386 offset += 16;
387 }
388 }
389
390 let mut frames: Option<Vec<QuicFrame>> = None;
391 let crypto_buffer: &mut Vec<u8> = if dir {
393 conn.client_buffer.as_mut()
394 } else {
395 conn.server_buffer.as_mut()
396 };
397 if let Some(frame_bytes) = decrypted_payload {
399 let (q_frames, mut crypto_bytes) =
402 QuicFrame::parse_frames(&frame_bytes, crypto_buffer.len())?;
403 frames = Some(q_frames);
404 if !crypto_bytes.is_empty() {
405 crypto_buffer.append(&mut crypto_bytes);
406 match parse_tls_message_handshake(crypto_buffer) {
411 Ok((_, msg)) => {
412 conn.tls.parse_message_level(&msg, dir);
413 crypto_buffer.clear();
414 }
415 Err(_) => return Err(QuicError::TlsParseFail),
416 }
417 }
418 }
419
420 Ok((
421 QuicPacket {
422 payload_bytes_count: packet_len,
423 short_header: None,
424 long_header: Some(QuicLongHeader {
425 packet_type,
426 type_specific,
427 version,
428 dcid_len,
429 dcid,
430 scid_len,
431 scid,
432 token_len,
433 token,
434 retry_tag,
435 }),
436 frames,
437 },
438 offset,
439 ))
440 } else {
441 let mut dcid_len = 20;
443 if data.len() < 1 + dcid_len {
444 dcid_len = data.len() - 1;
445 }
446 let dcid_hex = QuicPacket::vec_u8_to_hex_string(QuicPacket::access_data(
448 data,
449 offset,
450 offset + dcid_len,
451 )?);
452 let mut dcid = None;
453 for cid in &conn.cids {
454 if dcid_hex.starts_with(cid) {
455 dcid_len = cid.chars().count() / 2;
456 dcid = Some(cid.clone());
457 }
458 }
459 offset += dcid_len;
460 let payload_bytes_count = (data.len() - offset) as u64;
462 offset += payload_bytes_count as usize;
463 Ok((
464 QuicPacket {
465 short_header: Some(QuicShortHeader { dcid }),
466 long_header: None,
467 payload_bytes_count: Some(payload_bytes_count),
468 frames: None,
469 },
470 offset,
471 ))
472 }
473 }
474}
475
476impl QuicConn {
477 pub(crate) fn new() -> QuicConn {
478 QuicConn {
479 packets: Vec::new(),
480 cids: HashSet::new(),
481 tls: Tls::new(),
482 client_opener: None,
483 server_opener: None,
484 client_buffer: Vec::new(),
485 server_buffer: Vec::new(),
486 }
487 }
488
489 fn parse_packet(&mut self, data: &[u8], direction: bool) -> ParseResult {
490 let mut offset = 0;
491 while data.len() > offset {
495 if let Ok((quic, off)) = QuicPacket::parse_from(self, data, offset, direction) {
496 self.packets.push(quic);
497 offset = off;
498 } else {
499 return ParseResult::Skipped;
500 }
501 }
502 if self
503 .packets
504 .last()
505 .is_some_and(|p| p.short_header.is_some())
506 {
507 return ParseResult::Done(0);
508 }
509 ParseResult::Continue(0)
510 }
511}