From d45f082691abae2410a3102739fdd22db08f2dbd Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Sat, 8 Jun 2024 15:55:07 -0700 Subject: [PATCH] test: add pcap testing crate --- .github/workflows/ci_rust.yml | 27 ++++ tests/pcap/.gitignore | 2 + tests/pcap/Cargo.toml | 15 +++ tests/pcap/data/fragmented_ch.pcap | Bin 0 -> 19578 bytes tests/pcap/data/multiple_hellos.pcap | Bin 0 -> 16329 bytes tests/pcap/data/tls12.pcap | Bin 0 -> 2575 bytes tests/pcap/data/tls13.pcap | Bin 0 -> 3162 bytes tests/pcap/src/capture.rs | 42 +++++++ tests/pcap/src/client_hello.rs | 83 +++++++++++++ tests/pcap/src/handshake_message.rs | 171 ++++++++++++++++++++++++++ tests/pcap/src/lib.rs | 3 + tests/pcap/tests/s2n_client_hellos.rs | 56 +++++++++ 12 files changed, 399 insertions(+) create mode 100644 tests/pcap/.gitignore create mode 100644 tests/pcap/Cargo.toml create mode 100644 tests/pcap/data/fragmented_ch.pcap create mode 100644 tests/pcap/data/multiple_hellos.pcap create mode 100644 tests/pcap/data/tls12.pcap create mode 100644 tests/pcap/data/tls13.pcap create mode 100644 tests/pcap/src/capture.rs create mode 100644 tests/pcap/src/client_hello.rs create mode 100644 tests/pcap/src/handshake_message.rs create mode 100644 tests/pcap/src/lib.rs create mode 100644 tests/pcap/tests/s2n_client_hellos.rs diff --git a/.github/workflows/ci_rust.yml b/.github/workflows/ci_rust.yml index 39191005f66..607eb12e265 100644 --- a/.github/workflows/ci_rust.yml +++ b/.github/workflows/ci_rust.yml @@ -14,6 +14,7 @@ env: RUST_NIGHTLY_TOOLCHAIN: nightly-2024-01-01 ROOT_PATH: bindings/rust EXAMPLE_WORKSPACE: bindings/rust-examples + PCAP_TEST_PATH: tests/pcap jobs: generate: @@ -256,3 +257,29 @@ jobs: - name: Check MSRV of s2n-tokio run: grep "rust-version = \"$(cat ${{env.ROOT_PATH}}/rust-toolchain)\"" ${{env.ROOT_PATH}}/s2n-tls-tokio/Cargo.toml + pcaps: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Install Rust toolchain + id: toolchain + run: | + rustup toolchain install stable --component clippy + rustup override set stable + + - name: Generate bindings + working-directory: ${{env.ROOT_PATH}} + run: ./generate.sh --skip-tests + + - name: Run lints + working-directory: ${{env.PCAP_TEST_PATH}} + run: | + cargo fmt --all -- --check + cargo clippy --all-targets -- -D warnings + + - name: Run tests + working-directory: ${{env.PCAP_TEST_PATH}} + run: cargo test diff --git a/tests/pcap/.gitignore b/tests/pcap/.gitignore new file mode 100644 index 00000000000..2c96eb1b651 --- /dev/null +++ b/tests/pcap/.gitignore @@ -0,0 +1,2 @@ +target/ +Cargo.lock diff --git a/tests/pcap/Cargo.toml b/tests/pcap/Cargo.toml new file mode 100644 index 00000000000..bc5aa1bbc2a --- /dev/null +++ b/tests/pcap/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "pcap" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +anyhow = "1.0.86" +hex = "0.4.3" +rtshark = "2.7.1" + +[dev-dependencies] +# We want to test against the latest, local version of s2n +s2n-tls-sys = { path = "../../bindings/rust/s2n-tls-sys" } +s2n-tls = { path = "../../bindings/rust/s2n-tls", features = ["unstable-fingerprint"] } diff --git a/tests/pcap/data/fragmented_ch.pcap b/tests/pcap/data/fragmented_ch.pcap new file mode 100644 index 0000000000000000000000000000000000000000..d5518d8366603055dbf1342a212f23e9a8a9cbe7 GIT binary patch literal 19578 zcmeI4eLR!vAIGn49%fNWXNVom(>U^2sYxhm&P2^BMWK_Lqd6QT{9IZdUNhu?iq>#QfgfBN&fUaxDf+h*_kbHDH1-+!*}>v~XE zaRLiuz`sKVV30rbhrPBldli8Va*R&t5ijtTxnB$LGwr0`MUK%gN)OD%k^NkN2O1QF zHX^44=*Xw&lpc7n0sw1nrya19^M4qHP|$hZTgxt(LFq&PGY>n#8#e<0D>K29!Qu&d zCz{`*?_+Q{d6bNopNb3sO17?+khSctdtb@Q4@Kzxd9x;Ne@;f}60#aXUXGjzozg>` zc>%XeoViUx{*bvJq3?=T#)5GJBqL@rFG@wTnkguOCt5%}KPPH6bGuQZi|tp5YFI$J z`H?t^5{WpBRu%@>#^P{f+YQ04a`w)DW?BRR;BhiI91e@a00MH0lJ|#&<1ny3U~s38eRr3ii&TqasPmgggDueK#Lc5 zBz)OFFe>gJ_-_Ds)N@ z@s0**++LLEA1NVD_7AYMCH({A`##x!0PPBJc^B5HnFtq>+v4De;xKaY5z9ryy zWhF2l+3y0K(gPJBqbHn?5?AJam54M@VCQ@kFpjol5OFvVfdwEG*{*;deP->m65=1$ z#mlhCtiJU7Nka}r{2=$RW_hb|wUhsM0|pgjO9K;RX9X-y2H8eIyorL+s_@q6N|skT zHMouuy&cgfd@E*4lFUfyUNs6P<9fZi;qwvTrYC@kLfdwM7(w`ym>b%FU` z*N1SxFVao}OM2FzUij0h@CVO1^?dq^Fk1F+g2g5c?dn=xT8uSYPuqnO6CDd0YjYhZ zaIG)Bo;h#VZOR>2cN26k`71?N;L~%D?sn3OcrKwb(Be^Q?_$4K^Opn{urHq(a%Xi9 zr$*|95J#{ccODJbtgbk8NyIBMwOClzNj2X5yW$4r_z3SuDu$i?Juhsc7~8wcSf#IS zWbW`@Sg>v^guEZu=5@N4e3;Spc=|@g83zo;6c!#mxWlExcYvBQ-Gt^7&X~P4O~oeF zvZ85oGLt5dj~!GMBnfk+`f#b$V6Bi2YN?RS``Zn$1_1^uENr3(D6q7qRyT`s_BI zfA(DTp+z+pXm1oZsxlpt{wycxV3p$1EoyVpPKBLa;>=TG-&Q&H!h`DUdVm!!Vh<_3 zv=i+)b5YQIw9GX^y`_1Wr6SD#>7?h_E#Z|_`3Fznnq31rWA-vR+$R_D%P@7v&5k_^ zsdf_HetcH`W{#V};eLHmN^SbXy)zCGPrXeyYr?i2Ww)o@TgUxdgS1&G!viNz;t|L* z2J1>$zs^fP>J#axIkPNVnbx1OZCmx&V3mNu?Kt5XaaFlA)t468r?d5N)Q+Rk-Sw#_ zgPdpg^fw$A0gE{vvWBE~BpBk{7F~|=KFx=c!Ih16XQ+%HLoLxLX_CbHEF-6M5 zSqHIy{`j-jYHA{}*63_zGk=!ekL0#@-C&RB1o2(QJ^mX0-SttJp9qziyGge$8kXj* z)w%jZXkFhhmX-mMMi_>g3-UY-PABV(6Kve(Sw-@7Dd6nq>4K&5V5#{!Ee+9j9Ve&-w0Sx(!?o`dUptyK zLXs3Ewit--Lx59h%=4z;yfvK@XZDe)ip)XdG;L=E4X^eJ?zNRAT05Nr(yJH$Eo^P1 zRLsxyJ{NQ~&`B5lrb=>5PKdc6ZQRHdqsm}k8`bWAfo7DiR03qAn4(j9pcyeL!i8wY zpGzefrLLM6jbxmV|1#ra-s_y#n(jNpS1oYOxT8$|Ct@vQUj3pcsk-4?F69+usUGH= z@RXCsk^{3nJw≫@wK@ncHW@HI8@O3-rTcOPk5xp3|fh@H1BVm!Rd1$gpn`&9Xhyt^r6=3<^1Hpq(l+3&e4Es zHlV%h?SItAmiqW@JGJduN{)FZ@BEe@LRHJlS_;%!Ly`@YuO72*V5!vwvm@%~948Y5 zQC;qpW)J99RFl$@Fj)wE9y9OP$r40y&ZANIN}SU;TRbX2j5|+Oyl-d)P$hd zMr=~M7imfF+P+48hYqYQC7&IQa~I|Zb^kDt$5zmO6U#T-L@y&}ZsglHWC$|bDX}8g zQ;*k=24?Qi{Aa^7-=nVC!rjCJL)fJEt@B2Y1elX6jJ(bK?{<-@sxSEzl&~6Nn5Oc> zzul-V+WOSta)?dXt48&5mtWSbIQ{r}#_DCxBw_48$bA=K;FHD*7xj5qk3Gu^|ie{ule}fo0B~iFooH0>CoP4f|DM&{C*RJ}1 DaYff5 literal 0 HcmV?d00001 diff --git a/tests/pcap/data/multiple_hellos.pcap b/tests/pcap/data/multiple_hellos.pcap new file mode 100644 index 0000000000000000000000000000000000000000..dd124cbe3ea0c7310d382c6fc02413811a63e1c6 GIT binary patch literal 16329 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 literal 0 HcmV?d00001 diff --git a/tests/pcap/data/tls12.pcap b/tests/pcap/data/tls12.pcap new file mode 100644 index 0000000000000000000000000000000000000000..41b61cae468feeca458e4d9d1d8fe13e766e1697 GIT binary patch literal 2575 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 g6Gf321=b{vcdW!Xyh{AG>P2|foj Vec { + let mut packets = Vec::new(); + while let Ok(Some(packet)) = tshark.read() { + packets.push(packet) + } + packets +} + +pub(crate) fn get_metadata<'a>(packet: &'a Packet, key: &'a str) -> Option<&'a str> { + let (layer_name, _) = key.split_once('.').expect("key is layer"); + packet + .layer_name(layer_name) + .and_then(|layer| layer.metadata(key)) + .map(Metadata::value) +} + +pub(crate) fn get_all_metadata<'a>(packet: &'a Packet, key: &'a str) -> Vec<&'a str> { + let (layer_name, _) = key.split_once('.').expect("key is layer"); + if let Some(layer) = packet.layer_name(layer_name) { + layer + .iter() + .filter(|metadata| metadata.name() == key) + .map(Metadata::value) + .collect() + } else { + Vec::new() + } +} + +pub fn all_pcaps() -> impl Iterator { + std::fs::read_dir("data") + .expect("Missing test pcap file") + .filter_map(Result::ok) + .filter_map(|entry| { + let path = entry.path(); + path.to_str().map(std::string::ToString::to_string) + }) +} diff --git a/tests/pcap/src/client_hello.rs b/tests/pcap/src/client_hello.rs new file mode 100644 index 00000000000..f8d9b835b2c --- /dev/null +++ b/tests/pcap/src/client_hello.rs @@ -0,0 +1,83 @@ +use crate::handshake_message::Builder as MessageBuilder; +use crate::handshake_message::Message; +use anyhow::*; +use std::option::Option; + +use crate::capture::*; + +#[derive(Debug, Clone, Default)] +pub struct ClientHello { + pub message: Message, + pub ja3_hash: Option, + pub ja3_str: Option, +} + +impl ClientHello { + const JA3_HASH: &'static str = "tls.handshake.ja3"; + const JA3_STR: &'static str = "tls.handshake.ja3_full"; + + fn from_message(message: Message) -> Self { + let packet = &message.packet; + let ja3_hash = get_metadata(packet, Self::JA3_HASH).map(str::to_string); + let ja3_str = get_metadata(packet, Self::JA3_STR).map(str::to_string); + Self { + message, + ja3_hash, + ja3_str, + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct Builder(MessageBuilder); + +impl Builder { + pub fn inner(&mut self) -> &mut MessageBuilder { + &mut self.0 + } + + pub fn build(mut self) -> Result> { + self.0.set_type(1); + + let mut client_hellos = Vec::new(); + for message in self.0.build()? { + client_hellos.push(ClientHello::from_message(message)); + } + Ok(client_hellos) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn multiple_hellos() -> Result<()> { + let mut builder = Builder::default(); + builder + .inner() + .set_capture_file("data/multiple_hellos.pcap"); + let hellos = builder.build()?; + assert_eq!(hellos.len(), 5); + Ok(()) + } + + #[test] + fn from_pcaps() -> Result<()> { + let pcaps = all_pcaps(); + + for pcap in pcaps { + let mut builder = Builder::default(); + builder.inner().set_capture_file(&pcap); + let hellos = builder.build().unwrap(); + + assert!(!hellos.is_empty()); + for hello in hellos { + assert!(hello.ja3_hash.is_some()); + assert!(hello.ja3_str.is_some()); + } + } + + Ok(()) + } +} diff --git a/tests/pcap/src/handshake_message.rs b/tests/pcap/src/handshake_message.rs new file mode 100644 index 00000000000..30bae8dc4e9 --- /dev/null +++ b/tests/pcap/src/handshake_message.rs @@ -0,0 +1,171 @@ +use anyhow::*; +use rtshark::Packet; +use rtshark::RTSharkBuilder; +use std::collections::HashSet; + +use crate::capture::*; + +#[derive(Debug, Clone, Default)] +pub struct Message { + pub message_type: String, + pub packet: Packet, + pub payloads: Vec, + pub frame_num: String, +} + +impl Message { + const FRAGMENT: &'static str = "tls.handshake.fragment"; + const FRAGMENTS_COUNT: &'static str = "tls.handshake.fragment.count"; + + pub fn to_bytes(&self) -> Result> { + let mut bytes = Vec::new(); + for payload in &self.payloads { + let hex = payload.replace(':', ""); + bytes.extend(&hex::decode(&hex)?[5..]); + } + Ok(bytes) + } + + fn from_packet(packet: Packet, frame_num: String, tcp_payloads: &Vec) -> Result { + let message_type = get_metadata(&packet, Builder::MESSAGE_TYPE) + .context("Missing handshake message type")? + .to_string(); + + let mut payload_frames = get_all_metadata(&packet, Self::FRAGMENT); + if payload_frames.is_empty() { + payload_frames.push(&frame_num); + } + let payload_frames: HashSet<&str> = HashSet::from_iter(payload_frames); + + let mut payloads = Vec::new(); + for packet in tcp_payloads { + if let Some(frame) = get_metadata(packet, Builder::FRAME_NUM) { + if payload_frames.contains(frame) { + let payload = get_metadata(packet, Builder::TCP_PAYLOAD) + .context("Missing tcp payload")?; + payloads.push(payload.to_string()); + } + } + } + + let count = get_metadata(&packet, Self::FRAGMENTS_COUNT).unwrap_or("1"); + if count != payloads.len().to_string() { + bail!("Unable to find all tcp payloads for tls message") + } + + Ok(Self { + message_type, + packet, + payloads, + frame_num, + }) + } +} + +#[derive(Debug, Clone, Default)] +pub struct Builder { + message_type: Option, + capture_file: Option, +} + +impl Builder { + const TCP_PAYLOAD: &'static str = "tcp.payload"; + const FRAME_NUM: &'static str = "frame.number"; + const MESSAGE_TYPE: &'static str = "tls.handshake.type"; + + pub(crate) fn set_type(&mut self, message_type: u16) -> &mut Self { + self.message_type = Some(message_type); + self + } + + pub fn set_capture_file(&mut self, file: &str) -> &mut Self { + self.capture_file = Some(file.to_string()); + self + } + + fn build_from_capture(self, capture: rtshark::RTSharkBuilderReady) -> Result> { + let tcp_capture = capture + .display_filter(Self::TCP_PAYLOAD) + .metadata_whitelist(Self::TCP_PAYLOAD) + .metadata_whitelist(Self::FRAME_NUM) + .spawn()?; + let payloads = read_all(tcp_capture); + + let filter = if let Some(message_type) = self.message_type { + format!("{} == {}", Self::MESSAGE_TYPE, message_type) + } else { + Self::MESSAGE_TYPE.to_string() + }; + let message_capture = capture.display_filter(&filter).spawn()?; + + let mut messages = Vec::new(); + for packet in read_all(message_capture) { + let frame_num = get_metadata(&packet, Builder::FRAME_NUM) + .context("Missing frame number")? + .to_string(); + + let context_msg = format!("Failed to parse frame {}", &frame_num); + let message = + Message::from_packet(packet, frame_num, &payloads).context(context_msg)?; + + messages.push(message); + } + Ok(messages) + } + + pub(crate) fn build(mut self) -> Result> { + let file = self + .capture_file + .take() + .context("No capture file provided")?; + let capture = RTSharkBuilder::builder().input_path(&file); + self.build_from_capture(capture) + .with_context(|| format!("Failed to parse capture file {}", &file)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fragmentation() -> Result<()> { + let mut builder = Builder::default(); + builder.set_capture_file("data/fragmented_ch.pcap"); + let messages = builder.build()?; + + let first = messages.first().unwrap(); + assert_eq!(first.payloads.len(), 3); + let bytes = first.to_bytes()?; + // Correct length read from wireshark + assert_eq!(bytes.len(), 16262); + + Ok(()) + } + + #[test] + fn multiple_handshakes() -> Result<()> { + let mut builder = Builder::default(); + builder.set_capture_file("data/multiple_hellos.pcap"); + let messages = builder.build()?; + let count = messages.iter().filter(|m| m.message_type == "1").count(); + assert_eq!(count, 5); + Ok(()) + } + + #[test] + fn from_pcaps() -> Result<()> { + let pcaps = all_pcaps(); + + for pcap in pcaps { + let pcap: String = pcap; + + let mut builder = Builder::default(); + builder.set_capture_file(&pcap); + let messages = builder.build().unwrap(); + assert!(!messages.is_empty()) + } + + Ok(()) + } +} diff --git a/tests/pcap/src/lib.rs b/tests/pcap/src/lib.rs new file mode 100644 index 00000000000..cc08faeb16f --- /dev/null +++ b/tests/pcap/src/lib.rs @@ -0,0 +1,3 @@ +pub mod capture; +pub mod client_hello; +pub mod handshake_message; diff --git a/tests/pcap/tests/s2n_client_hellos.rs b/tests/pcap/tests/s2n_client_hellos.rs new file mode 100644 index 00000000000..6bf3094f742 --- /dev/null +++ b/tests/pcap/tests/s2n_client_hellos.rs @@ -0,0 +1,56 @@ +use anyhow::*; +use pcap::capture::all_pcaps; +use pcap::client_hello::{Builder as PcapBuilder, ClientHello as PcapHello}; +use s2n_tls::client_hello::{ClientHello as S2NHello, FingerprintType}; + +fn get_s2n_hello(pcap_hello: &PcapHello) -> Result> { + let bytes = pcap_hello.message.to_bytes()?; + Ok(S2NHello::parse_client_hello(&bytes)?) +} + +fn test_all_client_hellos(test_fn: F) -> Result<()> +where + F: FnOnce(PcapHello, Box) -> Result<()> + Copy, +{ + let pcaps = all_pcaps(); + for pcap in pcaps { + let mut builder = PcapBuilder::default(); + builder.inner().set_capture_file(&pcap); + let hellos = builder.build()?; + + for hello in hellos { + println!( + "Testing ClientHello found in frame {} in {}", + hello.message.frame_num, pcap + ); + let s2n_hello = get_s2n_hello(&hello).context("s2n failed to parse ClientHello")?; + test_fn(hello, s2n_hello)?; + } + } + Ok(()) +} + +#[test] +fn parsing() -> Result<()> { + test_all_client_hellos(|_, _| Ok(())) +} + +#[test] +fn ja3_fingerprints() -> Result<()> { + test_all_client_hellos(|pcap_hello, s2n_hello| { + let mut s2n_ja3_hash = Vec::new(); + s2n_hello + .fingerprint_hash(FingerprintType::JA3, &mut s2n_ja3_hash) + .context("s2n failed to calculate ja3 hash")?; + let s2n_ja3_hash = hex::encode(s2n_ja3_hash); + + let mut s2n_ja3_str = String::with_capacity(1000); + s2n_hello + .fingerprint_string(FingerprintType::JA3, &mut s2n_ja3_str) + .context("s2n failed to calculate ja3 string")?; + + assert_eq!(pcap_hello.ja3_hash, Some(s2n_ja3_hash)); + assert_eq!(pcap_hello.ja3_str, Some(s2n_ja3_str)); + Ok(()) + }) +}