Skip to main content

stackforge_core/layer/quic/
builder.rs

1//! QUIC packet builder (RFC 9000 Sections 17.2 and 17.3).
2//!
3//! Provides [`QuicBuilder`] for constructing QUIC packets in byte form.
4//! All encryption / key scheduling is deliberately out of scope here: the
5//! builder emits the plaintext wire bytes only.
6
7use super::headers::QuicPacketType;
8use super::varint;
9
10/// Builder for QUIC packets.
11///
12/// Supports Initial, Handshake (long header) and 1-RTT (short header) packet
13/// construction.
14///
15/// # Example
16///
17/// ```rust
18/// use stackforge_core::layer::quic::builder::QuicBuilder;
19///
20/// let bytes = QuicBuilder::initial()
21///     .dst_conn_id(vec![0x01, 0x02, 0x03, 0x04])
22///     .src_conn_id(vec![0xAA, 0xBB])
23///     .payload(vec![0xDE, 0xAD, 0xBE, 0xEF])
24///     .build();
25/// ```
26#[derive(Debug, Clone)]
27pub struct QuicBuilder {
28    /// Whether this packet uses a long header (false = short/1-RTT).
29    is_long_header: bool,
30    /// Logical packet type.
31    packet_type: QuicPacketType,
32    /// QUIC version (big-endian u32).  Default = 1 (QUIC v1, RFC 9000).
33    version: u32,
34    /// Destination Connection ID.
35    dst_conn_id: Vec<u8>,
36    /// Source Connection ID (long header only).
37    src_conn_id: Vec<u8>,
38    /// Token (Initial packets only, RFC 9000 Section 17.2.2).
39    token: Vec<u8>,
40    /// Decrypted payload bytes (frames).
41    payload: Vec<u8>,
42    /// Packet number (used verbatim; no truncation is applied).
43    packet_number: u32,
44}
45
46impl Default for QuicBuilder {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl QuicBuilder {
53    /// Create a bare builder.  Most callers should use [`initial`], [`handshake`],
54    /// or [`one_rtt`] instead.
55    pub fn new() -> Self {
56        Self {
57            is_long_header: true,
58            packet_type: QuicPacketType::Initial,
59            version: 1,
60            dst_conn_id: Vec::new(),
61            src_conn_id: Vec::new(),
62            token: Vec::new(),
63            payload: Vec::new(),
64            packet_number: 0,
65        }
66    }
67
68    /// Create a builder pre-configured for a QUIC Initial packet (RFC 9000 Section 17.2.2).
69    pub fn initial() -> Self {
70        Self {
71            is_long_header: true,
72            packet_type: QuicPacketType::Initial,
73            version: 1,
74            ..Self::new()
75        }
76    }
77
78    /// Create a builder pre-configured for a QUIC Handshake packet (RFC 9000 Section 17.2.4).
79    pub fn handshake() -> Self {
80        Self {
81            is_long_header: true,
82            packet_type: QuicPacketType::Handshake,
83            version: 1,
84            ..Self::new()
85        }
86    }
87
88    /// Create a builder pre-configured for a QUIC 1-RTT (short header) packet
89    /// (RFC 9000 Section 17.3).
90    pub fn one_rtt() -> Self {
91        Self {
92            is_long_header: false,
93            packet_type: QuicPacketType::OneRtt,
94            version: 1,
95            ..Self::new()
96        }
97    }
98
99    /// Set the destination connection ID.
100    pub fn dst_conn_id(mut self, id: Vec<u8>) -> Self {
101        self.dst_conn_id = id;
102        self
103    }
104
105    /// Set the source connection ID (ignored for short-header packets).
106    pub fn src_conn_id(mut self, id: Vec<u8>) -> Self {
107        self.src_conn_id = id;
108        self
109    }
110
111    /// Set the QUIC version.
112    pub fn version(mut self, v: u32) -> Self {
113        self.version = v;
114        self
115    }
116
117    /// Set the token (Initial packets only).
118    pub fn token(mut self, t: Vec<u8>) -> Self {
119        self.token = t;
120        self
121    }
122
123    /// Set the (plaintext) payload bytes.
124    pub fn payload(mut self, p: Vec<u8>) -> Self {
125        self.payload = p;
126        self
127    }
128
129    /// Set the packet number.
130    pub fn packet_number(mut self, n: u32) -> Self {
131        self.packet_number = n;
132        self
133    }
134
135    /// Build the QUIC packet into a raw byte buffer.
136    ///
137    /// The packet number is encoded in the minimum number of bytes needed
138    /// (1 through 4) according to RFC 9000 Section 17.1.
139    pub fn build(&self) -> Vec<u8> {
140        if self.is_long_header {
141            self.build_long()
142        } else {
143            self.build_short()
144        }
145    }
146
147    // -------------------------------------------------------------------------
148    // Internal helpers
149    // -------------------------------------------------------------------------
150
151    /// Packet-number byte length (1..=4) for the current `packet_number`.
152    fn pn_len(&self) -> usize {
153        if self.packet_number < 0x100 {
154            1
155        } else if self.packet_number < 0x1_0000 {
156            2
157        } else if self.packet_number < 0x100_0000 {
158            3
159        } else {
160            4
161        }
162    }
163
164    /// Encode the packet number into bytes (big-endian, minimal width).
165    fn encode_pn(&self) -> Vec<u8> {
166        let n = self.pn_len();
167        let pn = self.packet_number;
168        match n {
169            1 => vec![pn as u8],
170            2 => (pn as u16).to_be_bytes().to_vec(),
171            3 => {
172                let b = pn.to_be_bytes();
173                vec![b[1], b[2], b[3]]
174            }
175            _ => pn.to_be_bytes().to_vec(),
176        }
177    }
178
179    /// Long-header packet type bits (bits 5-4 of byte 0).
180    fn long_type_bits(&self) -> u8 {
181        match self.packet_type {
182            QuicPacketType::Initial => 0x00,
183            QuicPacketType::ZeroRtt => 0x01,
184            QuicPacketType::Handshake => 0x02,
185            QuicPacketType::Retry => 0x03,
186            // Short header / VersionNeg don't use this path.
187            _ => 0x00,
188        }
189    }
190
191    /// Build a long-header packet (Initial or Handshake).
192    fn build_long(&self) -> Vec<u8> {
193        let pn_len = self.pn_len();
194        let pn_bytes = self.encode_pn();
195
196        // Length field = len(packet_number) + len(payload)
197        let length_val = (pn_len + self.payload.len()) as u64;
198        let length_varint = varint::encode(length_val);
199
200        // --- Byte 0 ---
201        // Header Form (1) = 1
202        // Fixed Bit (1)   = 1
203        // Long Packet Type (2)
204        // Reserved (2)    = 0
205        // Packet Number Length (2) = pn_len - 1
206        let first_byte: u8 = 0x80                        // Header Form = Long
207            | 0x40                      // Fixed Bit
208            | (self.long_type_bits() << 4)
209            | ((pn_len as u8) - 1); // Packet Number Length (0-based)
210
211        let mut out = Vec::new();
212
213        // Byte 0
214        out.push(first_byte);
215
216        // Bytes 1-4: Version
217        out.extend_from_slice(&self.version.to_be_bytes());
218
219        // DCIL + DCID
220        out.push(self.dst_conn_id.len() as u8);
221        out.extend_from_slice(&self.dst_conn_id);
222
223        // SCIL + SCID
224        out.push(self.src_conn_id.len() as u8);
225        out.extend_from_slice(&self.src_conn_id);
226
227        // Token (Initial only)
228        if self.packet_type == QuicPacketType::Initial {
229            out.extend_from_slice(&varint::encode(self.token.len() as u64));
230            out.extend_from_slice(&self.token);
231        }
232
233        // Length (varint)
234        out.extend_from_slice(&length_varint);
235
236        // Packet Number
237        out.extend_from_slice(&pn_bytes);
238
239        // Payload
240        out.extend_from_slice(&self.payload);
241
242        out
243    }
244
245    /// Build a short-header (1-RTT) packet.
246    fn build_short(&self) -> Vec<u8> {
247        let pn_len = self.pn_len();
248        let pn_bytes = self.encode_pn();
249
250        // Byte 0:
251        // Header Form (1) = 0
252        // Fixed Bit (1)   = 1
253        // Spin Bit (1)    = 0
254        // Reserved (2)    = 0
255        // Key Phase (1)   = 0
256        // Packet Number Length (2) = pn_len - 1
257        let first_byte: u8 = 0x40 | ((pn_len as u8) - 1);
258
259        let mut out = Vec::new();
260        out.push(first_byte);
261
262        // Destination Connection ID (0 bytes for simplicity when not set)
263        out.extend_from_slice(&self.dst_conn_id);
264
265        // Packet Number
266        out.extend_from_slice(&pn_bytes);
267
268        // Payload
269        out.extend_from_slice(&self.payload);
270
271        out
272    }
273}
274
275/// Type alias for building QUIC Initial packets — same as `QuicBuilder::initial()`.
276pub type QuicInitialBuilder = QuicBuilder;
277
278#[cfg(test)]
279mod tests {
280    use super::*;
281    use crate::layer::quic::headers::{QuicLongHeader, QuicShortHeader};
282
283    #[test]
284    fn test_build_initial_basic() {
285        let bytes = QuicBuilder::initial()
286            .dst_conn_id(vec![0x01, 0x02, 0x03, 0x04])
287            .src_conn_id(vec![])
288            .payload(vec![0xAA, 0xBB])
289            .build();
290
291        // Must start with long-header first byte
292        assert_eq!(bytes[0] & 0x80, 0x80, "Header Form must be 1 (long)");
293        assert_eq!(bytes[0] & 0x40, 0x40, "Fixed Bit must be 1");
294        // Long packet type bits 5-4 = 00 => Initial
295        assert_eq!((bytes[0] & 0x30) >> 4, 0x00);
296
297        // Version = 1
298        assert_eq!(&bytes[1..5], &[0x00, 0x00, 0x00, 0x01]);
299
300        // DCIL = 4
301        assert_eq!(bytes[5], 4);
302        assert_eq!(&bytes[6..10], &[0x01, 0x02, 0x03, 0x04]);
303
304        // SCIL = 0
305        assert_eq!(bytes[10], 0);
306    }
307
308    #[test]
309    fn test_build_initial_parse_back() {
310        let bytes = QuicBuilder::initial()
311            .dst_conn_id(vec![0xDE, 0xAD, 0xBE, 0xEF])
312            .src_conn_id(vec![0x11, 0x22])
313            .build();
314
315        let hdr = QuicLongHeader::parse(&bytes).unwrap();
316        assert_eq!(hdr.packet_type, QuicPacketType::Initial);
317        assert_eq!(hdr.dst_conn_id, vec![0xDE, 0xAD, 0xBE, 0xEF]);
318        assert_eq!(hdr.src_conn_id, vec![0x11, 0x22]);
319        assert_eq!(hdr.version, 1);
320    }
321
322    #[test]
323    fn test_build_handshake() {
324        let bytes = QuicBuilder::handshake()
325            .dst_conn_id(vec![0x01])
326            .payload(vec![0x06, 0x00, 0x01, 0xFF]) // pretend CRYPTO frame
327            .build();
328
329        assert_eq!(bytes[0] & 0x80, 0x80, "long header");
330        assert_eq!((bytes[0] & 0x30) >> 4, 0x02, "Handshake type bits");
331
332        let hdr = QuicLongHeader::parse(&bytes).unwrap();
333        assert_eq!(hdr.packet_type, QuicPacketType::Handshake);
334    }
335
336    #[test]
337    fn test_build_one_rtt() {
338        let bytes = QuicBuilder::one_rtt()
339            .payload(vec![0x01]) // PING
340            .build();
341
342        // Short header: bit 7 = 0
343        assert_eq!(bytes[0] & 0x80, 0x00, "short header");
344        assert_eq!(bytes[0] & 0x40, 0x40, "Fixed Bit must be 1");
345
346        let hdr = QuicShortHeader::parse(&bytes).unwrap();
347        assert_eq!(hdr.header_len, 1);
348    }
349
350    #[test]
351    fn test_build_with_token() {
352        let bytes = QuicBuilder::initial()
353            .token(vec![0xCA, 0xFE])
354            .payload(vec![])
355            .build();
356
357        // Token len varint should be present after SCID
358        // Minimal: [byte0][ver:4][DCIL=0][SCIL=0][TokenLen=2(varint)][CA][FE][Length][PN]
359        // Token length byte at offset 6 (after byte0 + ver4 + DCIL=0 + SCIL=0 = 7 bytes)
360        // Actually: byte0=1, ver=4, DCIL_byte=1, DCID=0, SCIL_byte=1, SCID=0 => pos=7
361        // Then TokenLen varint (2 => encodes as 0x02) at pos=7
362        assert!(bytes.len() >= 12);
363    }
364
365    #[test]
366    fn test_packet_number_encoding() {
367        // Small packet number => 1 byte
368        let bytes = QuicBuilder::initial().packet_number(0).build();
369        assert_eq!(bytes[0] & 0x03, 0, "pn_len-1 = 0 for 1-byte PN");
370
371        // Large packet number => 4 bytes
372        let bytes = QuicBuilder::initial().packet_number(0x1234_5678).build();
373        assert_eq!(bytes[0] & 0x03, 3, "pn_len-1 = 3 for 4-byte PN");
374    }
375}