1use alloc::vec::Vec;
2use core::mem;
3use core::ops::Range;
4
5use super::buffers::{BufferProgress, Coalescer, Delocator, Locator};
6use crate::error::InvalidMessage;
7use crate::msgs::codec::{Codec, u24};
8use crate::msgs::message::InboundPlainMessage;
9use crate::{ContentType, ProtocolVersion};
10
11#[derive(Debug)]
12pub(crate) struct HandshakeDeframer {
13 /// Spans covering individual handshake payloads, in order of receipt.
14 spans: Vec<FragmentSpan>,
15
16 /// Discard value, tracking the rightmost extent of the last message
17 /// in `spans`.
18 outer_discard: usize,
19}
20
21impl HandshakeDeframer {
22 /// Accepts a message into the deframer.
23 ///
24 /// `containing_buffer` allows mapping the message payload to its position
25 /// in the input buffer, and thereby avoid retaining a borrow on the input
26 /// buffer.
27 ///
28 /// That is required because our processing of handshake messages requires
29 /// them to be contiguous (and avoiding that would mean supporting gather-based
30 /// parsing in a large number of places, including `core`, `webpki`, and the
31 /// `CryptoProvider` interface). `coalesce()` arranges for that to happen, but
32 /// to do so it needs to move the fragments together in the original buffer.
33 /// This would not be possible if the messages were borrowing from that buffer.
34 ///
35 /// `outer_discard` is the rightmost extent of the original message.
36 pub(crate) fn input_message(
37 &mut self,
38 msg: InboundPlainMessage<'_>,
39 containing_buffer: &Locator,
40 outer_discard: usize,
41 ) {
42 debug_assert_eq!(msg.typ, ContentType::Handshake);
43 debug_assert!(containing_buffer.fully_contains(msg.payload));
44 debug_assert!(self.outer_discard <= outer_discard);
45
46 self.outer_discard = outer_discard;
47
48 // if our last span is incomplete, we can blindly add this as a new span --
49 // no need to attempt parsing it with `DissectHandshakeIter`.
50 //
51 // `coalesce()` will later move this new message to be contiguous with
52 // `_last_incomplete`, and reparse the result.
53 //
54 // we cannot merge these processes, because `coalesce` mutates the underlying
55 // buffer, and `msg` borrows it.
56 if let Some(_last_incomplete) = self
57 .spans
58 .last()
59 .filter(|span| !span.is_complete())
60 {
61 self.spans.push(FragmentSpan {
62 version: msg.version,
63 size: None,
64 bounds: containing_buffer.locate(msg.payload),
65 });
66 return;
67 }
68
69 // otherwise, we can expect `msg` to contain a handshake header introducing
70 // a new message (and perhaps several of them.)
71 for span in DissectHandshakeIter::new(msg, containing_buffer) {
72 self.spans.push(span);
73 }
74 }
75
76 /// Returns a `BufferProgress` that skips over unprocessed handshake data.
77 pub(crate) fn progress(&self) -> BufferProgress {
78 BufferProgress::new(self.outer_discard)
79 }
80
81 /// Do we have a message ready? ie, would `iter().next()` return `Some`?
82 pub(crate) fn has_message_ready(&self) -> bool {
83 match self.spans.first() {
84 Some(span) => span.is_complete(),
85 None => false,
86 }
87 }
88
89 /// Do we have any message data, partial or otherwise?
90 pub(crate) fn is_active(&self) -> bool {
91 !self.spans.is_empty()
92 }
93
94 /// We are "aligned" if there is no partial fragment of a handshake
95 /// message.
96 pub(crate) fn is_aligned(&self) -> bool {
97 self.spans
98 .iter()
99 .all(|span| span.is_complete())
100 }
101
102 /// Iterate over the complete messages.
103 pub(crate) fn iter<'a, 'b>(&'a mut self, containing_buffer: &'b [u8]) -> HandshakeIter<'a, 'b> {
104 HandshakeIter {
105 deframer: self,
106 containing_buffer: Delocator::new(containing_buffer),
107 index: 0,
108 }
109 }
110
111 /// Coalesce the handshake portions of the given buffer,
112 /// if needed.
113 ///
114 /// This does nothing if there is nothing to do.
115 ///
116 /// In a normal TLS stream, handshake messages need not be contiguous.
117 /// For example, each handshake message could be delivered in its own
118 /// outer TLS message. This would mean the handshake messages are
119 /// separated by the outer TLS message headers, and likely also
120 /// separated by encryption overhead (any explicit nonce in front,
121 /// any padding and authentication tag afterwards).
122 ///
123 /// For a toy example of one handshake message in two fragments, and:
124 ///
125 /// - the letter `h` for handshake header octets
126 /// - the letter `H` for handshake payload octets
127 /// - the letter `x` for octets in the buffer ignored by this code,
128 ///
129 /// the buffer and `spans` data structure could look like:
130 ///
131 /// ```text
132 /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9
133 /// x x x x x h h h h H H H x x x x x H H H H H H x x x
134 /// '------------' '----------'
135 /// | |
136 /// spans = [ { bounds = (5, 12), |
137 /// size = Some(9), .. }, |
138 /// { bounds = (17, 23), .. } ]
139 /// ```
140 ///
141 /// In this case, `requires_coalesce` returns `Some(0)`. Then
142 /// `coalesce_one` moves the second range leftwards:
143 ///
144 /// ```text
145 /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9
146 /// x x x x x h h h h H H H x x x x x H H H H H H x x x
147 /// '----------'
148 /// ^ '----------'
149 /// | v
150 /// '--<---<--'
151 /// copy_within(from = (17, 23),
152 /// to = (12, 18))
153 /// ```
154 ///
155 /// Leaving the buffer and spans:
156 ///
157 /// ```text
158 /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9
159 /// x x x x x h h h h H H H H H H H H H x x x x x x x x
160 /// '------------------------'
161 /// |
162 /// spans = [ { bounds = (5, 18), size = Some(9), .. } ]
163 /// ```
164 pub(crate) fn coalesce(&mut self, containing_buffer: &mut [u8]) -> Result<(), InvalidMessage> {
165 // Strategy: while there is work to do, scan `spans`
166 // for a pair where the first is not complete. move
167 // the second down towards the first, then reparse the contents.
168 while let Some(i) = self.requires_coalesce() {
169 self.coalesce_one(i, Coalescer::new(containing_buffer));
170 }
171
172 // check resulting spans pass our imposed length limit
173 match self
174 .spans
175 .iter()
176 .any(|span| span.size.unwrap_or_default() > MAX_HANDSHAKE_SIZE)
177 {
178 true => Err(InvalidMessage::HandshakePayloadTooLarge),
179 false => Ok(()),
180 }
181 }
182
183 /// Within `containing_buffer`, move `span[index+1]` to be contiguous
184 /// with `span[index]`.
185 fn coalesce_one(&mut self, index: usize, mut containing_buffer: Coalescer<'_>) {
186 let second = self.spans.remove(index + 1);
187 let mut first = self.spans.remove(index);
188
189 // move the entirety of `second` to be contiguous with `first`
190 let len = second.bounds.len();
191 let target = Range {
192 start: first.bounds.end,
193 end: first.bounds.end + len,
194 };
195
196 containing_buffer.copy_within(second.bounds, target);
197 let delocator = containing_buffer.delocator();
198
199 // now adjust `first` to cover both
200 first.bounds.end += len;
201
202 // finally, attempt to re-dissect `first`
203 let msg = InboundPlainMessage {
204 typ: ContentType::Handshake,
205 version: first.version,
206 payload: delocator.slice_from_range(&first.bounds),
207 };
208
209 for (i, span) in DissectHandshakeIter::new(msg, &delocator.locator()).enumerate() {
210 self.spans.insert(index + i, span);
211 }
212 }
213
214 /// We require coalescing if any span except the last is not complete.
215 ///
216 /// Returns an index into `spans` for the first non-complete span:
217 /// this will never be the last item.
218 fn requires_coalesce(&self) -> Option<usize> {
219 self.spans
220 .split_last()
221 .and_then(|(_last, elements)| {
222 elements
223 .iter()
224 .enumerate()
225 .find_map(|(i, span)| (!span.is_complete()).then_some(i))
226 })
227 }
228}
229
230impl Default for HandshakeDeframer {
231 fn default() -> Self {
232 Self {
233 // capacity: a typical upper limit on handshake messages in
234 // a single flight
235 spans: Vec::with_capacity(16),
236 outer_discard: 0,
237 }
238 }
239}
240
241struct DissectHandshakeIter<'a, 'b> {
242 version: ProtocolVersion,
243 payload: &'b [u8],
244 containing_buffer: &'a Locator,
245}
246
247impl<'a, 'b> DissectHandshakeIter<'a, 'b> {
248 fn new(msg: InboundPlainMessage<'b>, containing_buffer: &'a Locator) -> Self {
249 Self {
250 version: msg.version,
251 payload: msg.payload,
252 containing_buffer,
253 }
254 }
255}
256
257impl Iterator for DissectHandshakeIter<'_, '_> {
258 type Item = FragmentSpan;
259
260 fn next(&mut self) -> Option<Self::Item> {
261 if self.payload.is_empty() {
262 return None;
263 }
264
265 // If there is not enough data to have a header the length is unknown
266 if self.payload.len() < HANDSHAKE_HEADER_LEN {
267 let buf = mem::take(&mut self.payload);
268 let bounds = self.containing_buffer.locate(buf);
269 return Some(FragmentSpan {
270 version: self.version,
271 size: None,
272 bounds: bounds.clone(),
273 });
274 }
275
276 let (header, rest) = mem::take(&mut self.payload).split_at(HANDSHAKE_HEADER_LEN);
277
278 // safety: header[1..] is exactly 3 bytes, so `u24::read_bytes` cannot fail
279 let size = u24::read_bytes(&header[1..])
280 .unwrap()
281 .into();
282
283 let available = if size < rest.len() {
284 self.payload = &rest[size..];
285 size
286 } else {
287 rest.len()
288 };
289
290 let mut bounds = self.containing_buffer.locate(header);
291 bounds.end += available;
292 Some(FragmentSpan {
293 version: self.version,
294 size: Some(size),
295 bounds: bounds.clone(),
296 })
297 }
298}
299
300pub(crate) struct HandshakeIter<'a, 'b> {
301 deframer: &'a mut HandshakeDeframer,
302 containing_buffer: Delocator<'b>,
303 index: usize,
304}
305
306impl<'b> Iterator for HandshakeIter<'_, 'b> {
307 type Item = (InboundPlainMessage<'b>, usize);
308
309 fn next(&mut self) -> Option<Self::Item> {
310 let next_span = self.deframer.spans.get(self.index)?;
311
312 if !next_span.is_complete() {
313 return None;
314 }
315
316 // if this is the last handshake message, then we'll end
317 // up with an empty `spans` and can discard the remainder
318 // of the input buffer.
319 let discard = if self.deframer.spans.len() - 1 == self.index {
320 mem::take(&mut self.deframer.outer_discard)
321 } else {
322 0
323 };
324
325 self.index += 1;
326 Some((
327 InboundPlainMessage {
328 typ: ContentType::Handshake,
329 version: next_span.version,
330 payload: self
331 .containing_buffer
332 .slice_from_range(&next_span.bounds),
333 },
334 discard,
335 ))
336 }
337}
338
339impl Drop for HandshakeIter<'_, '_> {
340 fn drop(&mut self) {
341 self.deframer.spans.drain(..self.index);
342 }
343}
344
345#[derive(Debug)]
346struct FragmentSpan {
347 /// version taken from containing message.
348 version: ProtocolVersion,
349
350 /// size of the handshake message body (excluding header)
351 ///
352 /// `None` means the size is unknown, because `bounds` is not
353 /// large enough to encompass a whole header.
354 size: Option<usize>,
355
356 /// bounds of the handshake message, including header
357 bounds: Range<usize>,
358}
359
360impl FragmentSpan {
361 /// A `FragmentSpan` is "complete" if its size is known, and its
362 /// bounds exactly encompasses one handshake message.
363 fn is_complete(&self) -> bool {
364 match self.size {
365 Some(sz: usize) => sz + HANDSHAKE_HEADER_LEN == self.bounds.len(),
366 None => false,
367 }
368 }
369}
370
371const HANDSHAKE_HEADER_LEN: usize = 1 + 3;
372
373/// TLS allows for handshake messages of up to 16MB. We
374/// restrict that to 64KB to limit potential for denial-of-
375/// service.
376const MAX_HANDSHAKE_SIZE: usize = 0xffff;
377
378#[cfg(test)]
379mod tests {
380 use std::vec;
381
382 use super::*;
383 use crate::msgs::deframer::DeframerIter;
384
385 fn add_bytes(hs: &mut HandshakeDeframer, slice: &[u8], within: &[u8]) {
386 let msg = InboundPlainMessage {
387 typ: ContentType::Handshake,
388 version: ProtocolVersion::TLSv1_3,
389 payload: slice,
390 };
391 let locator = Locator::new(within);
392 let discard = locator.locate(slice).end;
393 hs.input_message(msg, &locator, discard);
394 }
395
396 #[test]
397 fn coalesce() {
398 let mut input = vec![0, 0, 0, 0x21, 0, 0, 0, 0, 0x01, 0xff, 0x00, 0x01];
399 let mut hs = HandshakeDeframer::default();
400
401 add_bytes(&mut hs, &input[3..4], &input);
402 assert_eq!(hs.requires_coalesce(), None);
403 add_bytes(&mut hs, &input[4..6], &input);
404 assert_eq!(hs.requires_coalesce(), Some(0));
405 add_bytes(&mut hs, &input[8..10], &input);
406 assert_eq!(hs.requires_coalesce(), Some(0));
407
408 std::println!("before: {hs:?}");
409 hs.coalesce(&mut input).unwrap();
410 std::println!("after: {hs:?}");
411
412 let (msg, discard) = hs.iter(&input).next().unwrap();
413 std::println!("msg {msg:?} discard {discard:?}");
414 assert_eq!(msg.typ, ContentType::Handshake);
415 assert_eq!(msg.version, ProtocolVersion::TLSv1_3);
416 assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x01, 0xff]);
417
418 input.drain(..discard);
419 assert_eq!(input, &[0, 1]);
420 }
421
422 #[test]
423 fn append() {
424 let mut input = vec![0, 0, 0, 0x21, 0, 0, 5, 0, 0, 1, 2, 3, 4, 5, 0];
425 let mut hs = HandshakeDeframer::default();
426
427 add_bytes(&mut hs, &input[3..7], &input);
428 add_bytes(&mut hs, &input[9..14], &input);
429 assert_eq!(hs.spans.len(), 2);
430
431 hs.coalesce(&mut input).unwrap();
432 assert_eq!(hs.spans.len(), 1);
433
434 let (msg, discard) = std::dbg!(hs.iter(&input).next().unwrap());
435 assert_eq!(msg.typ, ContentType::Handshake);
436 assert_eq!(msg.version, ProtocolVersion::TLSv1_3);
437 assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x05, 1, 2, 3, 4, 5]);
438
439 input.drain(..discard);
440 assert_eq!(input, &[0]);
441 }
442
443 #[test]
444 fn coalesce_rejects_excess_size_message() {
445 const X: u8 = 0xff;
446 let mut input = vec![0x21, 0x01, 0x00, X, 0x00, 0xab, X];
447 let mut hs = HandshakeDeframer::default();
448
449 // split header over multiple messages, which motivates doing
450 // this check in `coalesce()`
451 add_bytes(&mut hs, &input[0..3], &input);
452 add_bytes(&mut hs, &input[4..6], &input);
453
454 assert_eq!(
455 hs.coalesce(&mut input),
456 Err(InvalidMessage::HandshakePayloadTooLarge)
457 );
458 }
459
460 #[test]
461 fn iter_only_returns_full_messages() {
462 let input = [0, 0, 0, 0x21, 0, 0, 1, 0xab, 0x21, 0, 0, 1];
463
464 let mut hs = HandshakeDeframer::default();
465
466 add_bytes(&mut hs, &input[3..8], &input);
467 add_bytes(&mut hs, &input[8..12], &input);
468
469 let mut iter = hs.iter(&input);
470 let (msg, discard) = iter.next().unwrap();
471 assert!(iter.next().is_none());
472
473 assert_eq!(msg.typ, ContentType::Handshake);
474 assert_eq!(msg.version, ProtocolVersion::TLSv1_3);
475 assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x01, 0xab]);
476 assert_eq!(discard, 0);
477 }
478
479 #[test]
480 fn handshake_flight() {
481 // intended to be a realistic example
482 let mut input = include_bytes!("../../testdata/handshake-test.1.bin").to_vec();
483 let locator = Locator::new(&input);
484
485 let mut hs = HandshakeDeframer::default();
486
487 let mut iter = DeframerIter::new(&mut input[..]);
488
489 while let Some(message) = iter.next() {
490 let plain = message.unwrap().into_plain_message();
491 std::println!("message {plain:?}");
492
493 hs.input_message(plain, &locator, iter.bytes_consumed());
494 }
495
496 hs.coalesce(&mut input[..]).unwrap();
497
498 let mut iter = hs.iter(&input[..]);
499 for _ in 0..4 {
500 let (msg, discard) = iter.next().unwrap();
501 assert!(matches!(
502 msg,
503 InboundPlainMessage {
504 typ: ContentType::Handshake,
505 ..
506 }
507 ));
508 assert_eq!(discard, 0);
509 }
510
511 let (msg, discard) = iter.next().unwrap();
512 assert!(matches!(
513 msg,
514 InboundPlainMessage {
515 typ: ContentType::Handshake,
516 ..
517 }
518 ));
519 assert_eq!(discard, 4280);
520 drop(iter);
521
522 input.drain(0..discard);
523 assert!(input.is_empty());
524 }
525}
526