Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/chaff-machines/src/constant.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! An attempted implementation of a machine that tries to make packets flow at a constant rate.
//! A machine that sends bursts at a constant rate.

#![cfg(not(tarpaulin_include))]

Expand Down
1 change: 1 addition & 0 deletions crates/chaff-machines/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pub mod constant;
pub mod test;
pub mod wtf_pad_lite;
100 changes: 100 additions & 0 deletions crates/chaff-machines/src/wtf_pad_lite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! The "WTF-PAD lite" defence. Simplified version of WTF-PAD for demonstration purposes.
//!
//! This defence does not do both burst and gap level padding, has different stop conditions, and a
//! different way of sampling distributions.

#![cfg(not(tarpaulin_include))]

use chaff::{
action::{FrameworkAction, IntegratorAction},
distr::Distr,
event::Event,
machine,
machine::Machine,
};

/// Constructs a "WTF-PAD lite" defence. A simplified version of WTF-PAD for demonstration purposes.
///
/// This defence does not do both burst and gap level padding, has different stop conditions, and a
/// different way of sampling distributions.
/// # Panics
///
/// Shouldn't panic unless modified.
#[expect(clippy::expect_used)]
pub fn construct(
delay_distr: impl Into<Distr> + Copy,
timeout: impl Into<Distr> + Copy,
) -> Machine {
machine! {
queues: [None, None],
budget: Absolute(500),
state init {
transitions: [
Event::SendNormal => active,
Event::ReceiveNormal => active,
]
},
state active {
actions: [
FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
delay_distr.into()
),
FrameworkAction::schedule(
IntegratorAction::ReleaseBlock,
1,
timeout.into()
),
],
transitions: [
// if we send or receive anything, reset the timer and note
// that we received real traffic
Event::SendNormal => active_real,
Event::ReceiveNormal => active_real,

// if we haven't sent or received anything, then a decoy
// must have been sent, so reset the delay timer
Event::QueuePopped(0) => active,

// if the timeout happens, safely exit.
Event::QueuePopped(1) => end,

// if we exhaust the budget, safely exit
Event::MachineBudgetExhausted => end,
],
},
state active_real {
actions: [
FrameworkAction::CancelAll,
FrameworkAction::schedule(
IntegratorAction::SendDecoy,
0,
delay_distr.into()
),
FrameworkAction::schedule(
IntegratorAction::ReleaseBlock,
1,
timeout.into()
),
],
transitions: [
// if we send or receive anything, reset the timer and
// note that we received real traffic
Event::SendNormal => active_real,
Event::ReceiveNormal => active_real,

// if we haven't sent or received anything, then a decoy
// must have been sent, so reset the timer in active
Event::QueuePopped(0) => active,

Event::QueuePopped(1) => end,
Event::MachineBudgetExhausted => end,
],
},
state end {
actions: [FrameworkAction::CancelAll],
}
}
.expect("preconstructed machine should be valid")
}
54 changes: 51 additions & 3 deletions crates/chaff-sim/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,13 +960,18 @@ mod tests {

#[cfg(test)]
mod machines {
use std::time::Duration;

use super::Simulator;
use chaff::framework::Framework;
use chaff::{
distr::{Distr, DistrKind},
framework::Framework,
};
use chaff_capture::trace::{Direction, Trace};
use chaff_machines::constant;
use chaff_machines::{constant, wtf_pad_lite};

#[test]
pub fn test_constant_machine() {
pub fn test_constant() {
let machine = constant::construct();
let framework = Framework::new(machine, rand::rng());
let trace = Trace::new(
Expand All @@ -989,5 +994,48 @@ mod tests {
);
assert!(out.len() < 35, "len: {}", out.len());
}

#[test]
pub fn test_wtf_pad_lite() {
let delay_distr: Distr = DistrKind::Normal {
mean: 0.015,
std_dev: 0.05,
}
.try_into()
.unwrap();

let timeout: Distr = Duration::from_secs_f64(0.4).try_into().unwrap();

let machine = wtf_pad_lite::construct(delay_distr, timeout);
let framework = Framework::new(machine, rand::rng());
let trace = Trace::new(
[Direction::Send; 7],
[0, 123, 100_000, 200_000, 104, 204_213, 1_000_000],
[0; 7],
);
let mut simulator = Simulator::with(framework, trace, rand::rng());
let (out, _) = simulator.run();

assert!(out.len() < 75, "len: {}", out.len());
}

#[test]
pub fn test_wtf_pad_lite_constant() {
let delay_distr: Distr = Duration::from_millis(20).try_into().unwrap();
let timeout: Distr = Duration::from_secs_f64(0.4).try_into().unwrap();

let machine = wtf_pad_lite::construct(delay_distr, timeout);
let framework = Framework::new(machine, rand::rng());
let trace = Trace::new(
[Direction::Send; 7],
[0, 123, 100_000, 200_000, 104, 204_213, 399_999],
[0; 7],
);
let mut simulator = Simulator::with(framework, trace, rand::rng());
let (out, _) = simulator.run();

assert_eq!(out.len(), 81);
assert!(!out.timing_deltas().iter().any(|delta| *delta > 20_000));
}
}
}
15 changes: 13 additions & 2 deletions crates/chaff/src/distr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,19 @@ fn maybe_clamp(val: f64, min: Option<f64>, max: Option<f64>) -> f64 {

impl Distribution<Duration> for Distr {
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Duration {
fn round_to_micros(dur: Duration) -> Duration {
let nanos = dur.subsec_nanos();
let remainder = nanos % 1_000;

if remainder == 0 {
dur
} else {
dur + Duration::from_nanos(1_000 - u64::from(remainder))
}
}

#[expect(clippy::cast_precision_loss)]
Duration::from_secs_f64(
round_to_micros(Duration::from_secs_f64(
maybe_clamp(
self.offset
+ match self.distr {
Expand All @@ -318,7 +329,7 @@ impl Distribution<Duration> for Distr {
self.max,
)
.max(0.0),
)
))
}
}

Expand Down
1 change: 1 addition & 0 deletions crates/chaff/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ impl borsh::BorshDeserialize for Machine {
borsh::BorshDeserialize::deserialize_reader(reader)?;

Self::validate(&states, queues.len(), budget).map_err(|err| {
// TODO: coverage for this was broken while making fixes before deadline. fix coverage.
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("{err:?}"))
})?;

Expand Down
Loading