use memchr::memmem;
pub const MIN_TLS_HANDSHAKE_PATTERN_LEN: usize = 6;
fn is_valid_tls_handshake_prefix(data: &[u8]) -> bool {
if data.is_empty() {
return true; }
if data[0] != 0x16 {
return false;
}
if data.len() >= 2 {
if data[1] != 0x03 {
return false;
}
}
if data.len() >= 3 {
if !(0x01..=0x03).contains(&data[2]) {
return false;
}
}
true
}
pub fn find_tls_handshake_start(data: &[u8]) -> Option<usize> {
if data.len() < MIN_TLS_HANDSHAKE_PATTERN_LEN {
return None;
}
let finder = memmem::Finder::new(&[0x16, 0x03]);
let mut pos = 0;
while let Some(offset) = finder.find(&data[pos..]) {
let absolute_offset = pos + offset;
if absolute_offset + MIN_TLS_HANDSHAKE_PATTERN_LEN > data.len() {
return None;
}
let candidate = &data[absolute_offset..];
if (candidate[2] >= 0x01 && candidate[2] <= 0x03) && (candidate[5] == 0x01 || candidate[5] == 0x02)
{
return Some(absolute_offset);
}
pos = absolute_offset + 1;
}
None
}
pub fn find_potential_tls_suffix_len(data: &[u8]) -> usize {
let len = data.len();
if len == 0 {
return 0;
}
let max_suffix = std::cmp::min(len, 5);
for suffix_len in (1..=max_suffix).rev() {
let suffix = &data[len - suffix_len..];
if is_valid_tls_handshake_prefix(suffix) {
return suffix_len;
}
}
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_find_tls_handshake_clienthello() {
let data = vec![0x16, 0x03, 0x03, 0x00, 0x10, 0x01];
assert_eq!(find_tls_handshake_start(&data), Some(0));
}
#[test]
fn test_find_tls_handshake_serverhello() {
let data = vec![0x16, 0x03, 0x03, 0x00, 0x10, 0x02];
assert_eq!(find_tls_handshake_start(&data), Some(0));
}
#[test]
fn test_find_tls_handshake_with_prefix() {
let mut data = vec![0x00; 26]; data.extend_from_slice(&[0x16, 0x03, 0x01, 0x02, 0x00, 0x01]); assert_eq!(find_tls_handshake_start(&data), Some(26));
}
#[test]
fn test_find_tls_handshake_not_found() {
let data = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
assert_eq!(find_tls_handshake_start(&data), None);
}
#[test]
fn test_find_tls_handshake_too_short() {
let data = vec![0x16, 0x03, 0x03];
assert_eq!(find_tls_handshake_start(&data), None);
}
#[test]
fn test_suffix_no_tls_bytes() {
let data = vec![0x00, 0x01, 0x02, 0x03];
assert_eq!(find_potential_tls_suffix_len(&data), 0);
}
#[test]
fn test_suffix_single_0x16() {
let data = vec![0x00, 0x01, 0x16];
assert_eq!(find_potential_tls_suffix_len(&data), 1);
}
#[test]
fn test_suffix_two_byte_prefix() {
let data = vec![0x00, 0x01, 0x16, 0x03];
assert_eq!(find_potential_tls_suffix_len(&data), 2);
}
#[test]
fn test_suffix_three_byte_prefix() {
let data = vec![0x00, 0x16, 0x03, 0x01];
assert_eq!(find_potential_tls_suffix_len(&data), 3);
}
#[test]
fn test_suffix_valid_five_byte_prefix() {
let data = vec![0x16, 0x03, 0x03, 0xAB, 0x01];
assert_eq!(find_potential_tls_suffix_len(&data), 5);
}
#[test]
fn test_suffix_invalid_minor_version() {
let data = vec![0x16, 0x03, 0x04, 0x00, 0x01];
assert_eq!(find_potential_tls_suffix_len(&data), 0);
}
#[test]
fn test_suffix_large_buffer_no_pattern() {
let data = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
assert_eq!(find_potential_tls_suffix_len(&data), 0);
}
#[test]
fn test_suffix_large_buffer_with_pattern() {
let data = vec![0x00, 0x01, 0x02, 0x03, 0x16, 0x03];
assert_eq!(find_potential_tls_suffix_len(&data), 2);
}
#[test]
fn test_chunked_terminator_not_buffered() {
let data = vec![0x30, 0x0d, 0x0a, 0x0d, 0x0a];
assert_eq!(find_potential_tls_suffix_len(&data), 0);
}
#[test]
fn test_0x16_as_length_byte() {
let data = vec![0x00, 0x16, 0x03, 0x03, 0x16];
assert_eq!(find_potential_tls_suffix_len(&data), 4);
}
#[test]
fn test_0x03_as_length_byte() {
let data = vec![0x00, 0x16, 0x03, 0x01, 0x03];
assert_eq!(find_potential_tls_suffix_len(&data), 4);
}
#[test]
fn test_multiple_0x16_finds_longest_valid() {
let data = vec![0x16, 0x03, 0x03, 0xAB, 0x16, 0x03];
assert_eq!(find_potential_tls_suffix_len(&data), 2);
}
#[test]
fn test_single_0x16_at_end_of_large_buffer() {
let data = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x16];
assert_eq!(find_potential_tls_suffix_len(&data), 1);
}
#[test]
fn test_five_byte_prefix_with_0x16_length() {
let data = vec![0x00, 0x16, 0x03, 0x03, 0x16, 0x16];
assert_eq!(find_potential_tls_suffix_len(&data), 5);
}
#[test]
fn test_empty_buffer() {
let data: Vec<u8> = vec![];
assert_eq!(find_potential_tls_suffix_len(&data), 0);
}
#[test]
fn test_prefix_validation() {
assert!(is_valid_tls_handshake_prefix(&[0x16]));
assert!(is_valid_tls_handshake_prefix(&[0x16, 0x03]));
assert!(is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x01]));
assert!(is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x02]));
assert!(is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x03]));
assert!(is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x03, 0xFF]));
assert!(is_valid_tls_handshake_prefix(&[
0x16, 0x03, 0x03, 0xFF, 0xFF
]));
assert!(!is_valid_tls_handshake_prefix(&[0x17])); assert!(!is_valid_tls_handshake_prefix(&[0x16, 0x04])); assert!(!is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x00])); assert!(!is_valid_tls_handshake_prefix(&[0x16, 0x03, 0x04])); }
}