From d0a21a307695a5fcbc22944d5e40a24ca642de91 Mon Sep 17 00:00:00 2001 From: Martin Algesten Date: Mon, 22 Jun 2026 21:43:29 +0200 Subject: [PATCH] Fix handshake defragment sequence grouping Summary - Stop DTLS 1.2 and 1.3 handshake defragmentation when the next fragment belongs to a different message sequence. - Add cross-sequence regression coverage and keep the DTLS 1.2 test fragment helper on the original sequence. --- src/dtls12/message/handshake.rs | 44 +++++++++++++++++++++++++++++++-- src/dtls13/message/handshake.rs | 42 ++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/src/dtls12/message/handshake.rs b/src/dtls12/message/handshake.rs index ae6a2725..c6b409ea 100644 --- a/src/dtls12/message/handshake.rs +++ b/src/dtls12/message/handshake.rs @@ -168,7 +168,9 @@ impl Handshake { buffer.extend_from_slice(&first_buffer[range.clone()]); for (handshake, source_buf) in iter { - if handshake.header.msg_type != first_handshake.header.msg_type { + if handshake.header.msg_type != first_handshake.header.msg_type + || handshake.header.message_seq != first_handshake.header.message_seq + { break; } @@ -284,7 +286,7 @@ impl Handshake { let mut fragment = to_clone.do_clone(); fragment.header.fragment_offset = offset as u32; fragment.header.fragment_length = fragment_length; - fragment.header.message_seq = to_clone.header.message_seq + i as u16; + fragment.header.message_seq = to_clone.header.message_seq; fragment.body = Body::Fragment(fragment_range); fragment @@ -768,6 +770,44 @@ mod tests { assert!(transcript.is_empty()); } + #[test] + fn defragment_stops_at_cross_sequence_fragment() { + let body = &MESSAGE[12..]; + let mut source = body.to_vec(); + source.push(0); + + let handshake = Handshake::new( + MessageType::ClientHello, + body.len() as u32, + 0, + 0, + body.len() as u32, + Body::Fragment(0..body.len()), + ); + let decoy = Handshake::new( + MessageType::ClientHello, + body.len() as u32 + 1, + 1, + body.len() as u32, + 1, + Body::Fragment(body.len()..body.len() + 1), + ); + + let mut defragmented_buffer = Buf::new(); + let defragmented_handshake = Handshake::defragment( + [(&handshake, source.as_slice()), (&decoy, source.as_slice())].into_iter(), + &mut defragmented_buffer, + None, + None, + ) + .unwrap(); + + assert_eq!(defragmented_handshake.header.message_seq, 0); + assert_eq!(&defragmented_buffer[..body.len()], body); + assert!(handshake.is_handled()); + assert!(!decoy.is_handled()); + } + #[test] fn known_body_rejects_trailing_bytes() { let body = [0]; diff --git a/src/dtls13/message/handshake.rs b/src/dtls13/message/handshake.rs index 1ddfb1cc..77f43ff4 100644 --- a/src/dtls13/message/handshake.rs +++ b/src/dtls13/message/handshake.rs @@ -182,7 +182,9 @@ impl Handshake { first_handshake.header.fragment_offset + first_handshake.header.fragment_length; for (handshake, source_buf) in iter { - if handshake.header.msg_type != first_handshake.header.msg_type { + if handshake.header.msg_type != first_handshake.header.msg_type + || handshake.header.message_seq != first_handshake.header.message_seq + { break; } @@ -684,6 +686,44 @@ mod tests { assert!(transcript.is_empty()); } + #[test] + fn defragment_stops_at_cross_sequence_fragment() { + let body = &MESSAGE[12..]; + let mut source = body.to_vec(); + source.push(0); + + let handshake = Handshake::new( + MessageType::ClientHello, + body.len() as u32, + 0, + 0, + body.len() as u32, + Body::Fragment(0..body.len()), + ); + let decoy = Handshake::new( + MessageType::ClientHello, + body.len() as u32 + 1, + 1, + body.len() as u32, + 1, + Body::Fragment(body.len()..body.len() + 1), + ); + + let mut defragmented_buffer = Buf::new(); + let defragmented_handshake = Handshake::defragment( + [(&handshake, source.as_slice()), (&decoy, source.as_slice())].into_iter(), + &mut defragmented_buffer, + None, + None, + ) + .unwrap(); + + assert_eq!(defragmented_handshake.header.message_seq, 0); + assert_eq!(&defragmented_buffer[..body.len()], body); + assert!(handshake.is_handled()); + assert!(!decoy.is_handled()); + } + #[test] fn known_body_rejects_trailing_bytes() { let source = [0, 0, 0];