diff --git a/src/proxy/middle_relay/idle/read.rs b/src/proxy/middle_relay/idle/read.rs index 270f104..80ca0cc 100644 --- a/src/proxy/middle_relay/idle/read.rs +++ b/src/proxy/middle_relay/idle/read.rs @@ -331,7 +331,8 @@ where ) .await?; - // Secure Intermediate: strip validated trailing padding bytes. + // Secure Intermediate strips only non-aligned tail padding; full-word + // padding is indistinguishable from payload in VersionD framing. if proto_tag == ProtoTag::Secure { payload.truncate(secure_payload_len); } diff --git a/src/stream/frame_codec.rs b/src/stream/frame_codec.rs index d0d11b5..cbec951 100644 --- a/src/stream/frame_codec.rs +++ b/src/stream/frame_codec.rs @@ -523,6 +523,16 @@ mod tests { use tokio::io::duplex; use tokio_util::codec::{FramedRead, FramedWrite}; + fn assert_secure_decoded_payload(decoded: &[u8], original: &[u8]) { + assert!(decoded.starts_with(original)); + assert!( + (original.len()..=original.len() + 12).contains(&decoded.len()), + "Secure decoded payload may retain up to 12 bytes of full-word padding, got {}", + decoded.len() + ); + assert_eq!(decoded.len() % 4, 0); + } + #[tokio::test] async fn test_framed_abridged() { let (client, server) = duplex(4096); @@ -565,7 +575,7 @@ mod tests { writer.send(frame).await.unwrap(); let received = reader.next().await.unwrap().unwrap(); - assert_eq!(&received.data[..], &original[..]); + assert_secure_decoded_payload(&received.data, &original); } #[tokio::test] @@ -588,7 +598,11 @@ mod tests { writer.send(frame).await.unwrap(); let received = reader.next().await.unwrap().unwrap(); - assert_eq!(received.data.len(), 8); + if proto_tag == ProtoTag::Secure { + assert_secure_decoded_payload(&received.data, &original); + } else { + assert_eq!(received.data.len(), original.len()); + } } } diff --git a/src/stream/frame_stream.rs b/src/stream/frame_stream.rs index ed84645..61742de 100644 --- a/src/stream/frame_stream.rs +++ b/src/stream/frame_stream.rs @@ -559,6 +559,16 @@ mod tests { use std::sync::Arc; use tokio::io::duplex; + fn assert_secure_decoded_payload(decoded: &[u8], original: &[u8]) { + assert!(decoded.starts_with(original)); + assert!( + (original.len()..=original.len() + 12).contains(&decoded.len()), + "Secure decoded payload may retain up to 12 bytes of full-word padding, got {}", + decoded.len() + ); + assert_eq!(decoded.len() % 4, 0); + } + #[tokio::test] async fn test_abridged_roundtrip() { let (client, server) = duplex(1024); @@ -625,7 +635,7 @@ mod tests { writer.flush().await.unwrap(); let (received, _meta) = reader.read_frame().await.unwrap(); - assert_eq!(received.len(), data.len()); + assert_secure_decoded_payload(&received, &data); } #[tokio::test]