Skip to content

Commit

Permalink
Merge pull request #20 from maybenot-io/workspace
Browse files Browse the repository at this point in the history
maybenot workspace
  • Loading branch information
pylls authored May 6, 2024
2 parents 2645c85 + 39c0a5e commit d7c1a3e
Show file tree
Hide file tree
Showing 26 changed files with 24,947 additions and 112 deletions.
42 changes: 28 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
[package]
name = "maybenot"
version = "1.1.1"
[workspace.package]
edition = "2021"
authors = ["Tobias Pulls <[email protected]>", "Ethan Witwer <[email protected]>"]
license = "MIT OR Apache-2.0"
homepage = "https://maybenot.io"
description = "A framework for traffic analysis defenses"
keywords = [ "anonymity", "correlation", "fingerprinting", "privacy", "security" ]
categories = [ "cryptography", "network-programming" ]
repository = "https://github.com/maybenot-io/maybenot"

[dependencies]
rand="0.8.5"
byteorder="1.4.3"
simple-error = "0.3.0"
hex = "0.4.3"
libflate = "2.0.0"
ring = "0.16.20"
rand_distr = "0.4.3"
serde = {version = "1.0.185", features = ["derive"]}
[workspace]
resolver = "2"
# Please keep this list topologically sorted by dependency relation, so that
# every crate appears _before_ any other crate that depends on it.
members = [
# lib for the Maybenot framework
"crates/maybenot",
# simulator for the Maybenot framework
"crates/maybenot-simulator",
]

# Keep all lints in sync with `test/Cargo.toml`
[workspace.lints.rust]
# Security
non_ascii_idents = "forbid"

# Deny old style Rust
rust_2018_idioms = "deny"
macro_use_extern_crate = "deny"
absolute_paths_not_starting_with_crate = "deny"

# Easy to read style and opinionated best practices
explicit_outlives_requirements = "warn"
missing_abi = "deny"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
single_use_lifetimes = "warn"
95 changes: 10 additions & 85 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,97 +1,22 @@
# Maybenot 🤔

# Maybenot
Maybenot is a framework for traffic analysis defenses that hide patterns in
encrypted communication. Its goal is to increase the uncertainty of network
attackers, hence its logo 🤔 - the thinking face emoji (U+1F914).

[![Crates.io][crates-badge]][crates-url]
[![Documentation][docs-badge]][docs-url]
[![Build Status][tests-badge]][tests-url]
[![MIT OR Apache-2.0][license-badge]][license-url]

[crates-badge]: https://img.shields.io/crates/v/maybenot.svg
[crates-url]: https://crates.io/crates/maybenot
[docs-badge]: https://docs.rs/maybenot/badge.svg
[docs-url]: https://docs.rs/maybenot
[tests-badge]: https://github.com/maybenot-io/maybenot/actions/workflows/tests.yml/badge.svg
[tests-url]: https://github.com/maybenot-io/maybenot/actions
[license-badge]: https://img.shields.io/crates/l/maybenot
[license-url]: https://github.com/maybenot-io/maybenot/

Consider encrypted communication protocols such as TLS, QUIC, WireGuard, or Tor.
While the connections are encrypted, *patterns* in the encrypted communication
may still leak information about the communicated plaintext. Maybenot is a
framework for creating defenses that hide such patterns.

To simulate defenses based on Maybenot, see the [Maybenot
simulator](https://github.com/maybenot-io/maybenot-simulator/).

## Design
An instance of Maybenot repeatedly takes as *input* one or more *events*
describing the encrypted traffic going over an encrypted channel, and produces
as *output* zero or more *scheduled actions*, such as to inject *padding*
traffic or *block* outgoing traffic. One or more *state machines* determine what
actions to take based on events. State machines have a lightweight runtime and
are subject to *limits* on the amount of padding a blocking they can schedule.

<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/maybenot-io/maybenot/main/overview-dark.svg">
<img alt="design overview" src="https://raw.githubusercontent.com/maybenot-io/maybenot/main/overview-light.svg">
</picture>
</p>

Integration with an encrypted communication protocol is done by reporting events
and executing scheduled actions. Maybenot does not specify the specific async
runtime or how to keep time for sake of ease of integration.

## Example usage
```rust,no_run
use maybenot::{
framework::{Action, Framework, TriggerEvent},
machine::Machine,
};
use std::{str::FromStr, time::Instant};
// deserialize state machine from string
let s = "789cedca2101000000c230e85f1a8387009f9e351d051503ca0003";
let m = vec![Machine::from_str(s).unwrap()];
// create framework instance
let mut f = Framework::new(&m, 0.0, 0.0, 1420, Instant::now()).unwrap();
loop {
// collect one or more events
let events = [TriggerEvent::NonPaddingSent { bytes_sent: 1420 }];
## Workspace structure
The Maybenot workspace consists of the following crates:
- [maybenot](crates/maybenot): The core framework for creating defenses.
- [maybenot-simulator](crates/maybenot-simulator): A simulator for testing
defenses.

// trigger events, schedule actions, at most one per machine
for action in f.trigger_events(&events, Instant::now()) {
match action {
Action::Cancel { machine: MachineId } => {
// if any scheduled action for this machine, cancel it
}
Action::InjectPadding {
timeout: Duration,
size: u16,
bypass: bool,
replace: bool,
machine: MachineId,
} => {
// schedule padding of a specific size after timeout
}
Action::BlockOutgoing {
timeout: Duration,
duration: Duration,
bypass: bool,
replace: bool,
machine: MachineId,
} => {
// schedule blocking of outgoing traffic for duration after timeout
}
}
}
}
```
More crates are in the process of being added to the workspace. This happens in
parallel with the development of v2 of the framework, so sorry if it's a bit
messy for now. We aim for a clean slate once v2 is done.

## More details
See the [paper](https://doi.org/10.1145/3603216.3624953) and
Expand Down Expand Up @@ -124,4 +49,4 @@ Wang from 2006.
## Sponsorship
Made possible with support from [Mullvad VPN](https://mullvad.net/), the
[Swedish Internet Foundation](https://internetstiftelsen.se/en/), and the
[Knowledge Foundation of Sweden](https://www.kks.se/en/start-en/).
[Knowledge Foundation of Sweden](https://www.kks.se/en/start-en/).
16 changes: 16 additions & 0 deletions crates/maybenot-simulator/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

Manually generated changelog, for now. We follow semantic versioning.

## 1.1.1 - 2024-04-08
- Update to Maybenot v1.1.0.

## 1.1.0 - 2024-04-05
- Support for integration delays.
- Light networking refactor.

## 1.0.1 - 2023-11-24
- Minor README update.

## 1.0.0 - 2023-11-24
- Initial public release of the Maybenot simulator.
28 changes: 28 additions & 0 deletions crates/maybenot-simulator/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "maybenot-simulator"
version = "1.1.1"
description = "A simulator for the Maybenot framework"
authors = ["Tobias Pulls <[email protected]>"]
edition.workspace = true
license.workspace = true
homepage.workspace = true
keywords.workspace = true
categories.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
priority-queue = "1.3.2"
# defaults to relative path in workspace
maybenot = { path = "../maybenot/" }
log = "0.4.20"
test-log = "0.2.12"
fastrand = "2.0.0"
serde = "1.0.193"
rand = "0.8.5"
serde_json = "1.0.108"

[dev-dependencies]
env_logger = "0.10.1"
156 changes: 156 additions & 0 deletions crates/maybenot-simulator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# The Maybenot Simulator

A simulator for the [Maybenot
framework](https://github.com/maybenot-io/maybenot/).

[![Crates.io][crates-badge]][crates-url]
[![Documentation][docs-badge]][docs-url]
[![Build Status][tests-badge]][tests-url]
[![MIT OR Apache-2.0][license-badge]][license-url]

[crates-badge]: https://img.shields.io/crates/v/maybenot-simulator.svg
[crates-url]: https://crates.io/crates/maybenot-simulator
[docs-badge]: https://docs.rs/maybenot-simulator/badge.svg
[docs-url]: https://docs.rs/maybenot-simulator
[tests-badge]: https://github.com/maybenot-io/maybenot-simulator/actions/workflows/tests.yml/badge.svg
[tests-url]: https://github.com/maybenot-io/maybenot-simulator/actions
[license-badge]: https://img.shields.io/crates/l/maybenot-simulator
[license-url]: https://github.com/maybenot-io/maybenot-simulator/

## Example Usage
See [cargo docs][docs-url] for details on the API. The following is a simple
example of how to use the simulator:

```rust
use maybenot::{framework::TriggerEvent, machine::Machine};
use maybenot_simulator::{parse_trace, sim};
use std::{str::FromStr, time::Duration};

// A trace of ten packets from the client's perspective when visiting
// google.com over WireGuard. The format is: "time,direction,size\n". The
// direction is either "s" (sent) or "r" (received). The time is in
// nanoseconds since the start of the trace. The size is in bytes.
let raw_trace = "0,s,52
19714282,r,52
183976147,s,52
243699564,r,52
1696037773,s,40
2047985926,s,52
2055955094,r,52
9401039609,s,73
9401094589,s,73
9420892765,r,191";

// The delay between client and server. This is for the simulation of the
// network between the client and server
let delay = Duration::from_millis(10);

// Parse the raw trace into a queue of events for the simulator. This uses
// the delay to generate a queue of events at the client and server in such
// a way that the client is ensured to get the packets in the same order and
// at the same time as in the raw trace.
let mut input_trace = parse_trace(raw_trace, delay);

// A simple machine that sends one padding packet of 1000 bytes 20
// milliseconds after the first NonPaddingSent is sent.
let m = "789cedcfc10900200805506d82b6688c1caf5bc3b54823f4a1a2a453b7021ff8ff49\
41261f685323426187f8d3f9cceb18039205b9facab8914adf9d6d9406142f07f0";
let m = Machine::from_str(m).unwrap();

// Run the simulator with the machine at the client. Run the simulation up
// until 100 packets have been recorded (total, client and server).
let trace = sim(&[m], &[], &mut input_trace, delay, 100, true);

// print packets from the client's perspective
let starting_time = trace[0].time;
trace
.into_iter()
.filter(|p| p.client)
.for_each(|p| match p.event {
TriggerEvent::NonPaddingSent { bytes_sent } => {
println!(
"sent {} bytes at {} ms",
bytes_sent,
(p.time - starting_time).as_millis()
);
}
TriggerEvent::PaddingSent { bytes_sent, .. } => {
println!(
"sent {} bytes of padding at {} ms",
bytes_sent,
(p.time - starting_time).as_millis()
);
}
TriggerEvent::NonPaddingRecv { bytes_recv } => {
println!(
"received {} bytes at {} ms",
bytes_recv,
(p.time - starting_time).as_millis()
);
}
TriggerEvent::PaddingRecv { bytes_recv, .. } => {
println!(
"received {} bytes of padding at {} ms",
bytes_recv,
(p.time - starting_time).as_millis()
);
}
_ => {}
});
```

Produces the following output:

```
sent 52 bytes at 0 ms
received 52 bytes at 19 ms
sent 1000 bytes of padding at 20 ms
sent 52 bytes at 183 ms
received 52 bytes at 243 ms
sent 40 bytes at 1696 ms
sent 52 bytes at 2047 ms
received 52 bytes at 2055 ms
sent 73 bytes at 9401 ms
sent 73 bytes at 9401 ms
received 191 bytes at 9420 ms
```

## Key Limitations
This is a prototype simulator, and as such, it has a number of limitations. For
one, it is a simulator! We are simulating the integration with the
application/destination using the framework and the network between the client
and server. We have a *sim2real* problem.

In terms of networking, the relevant code for the simulator is in
`src/network.rs`. It is very crude: we use a fixed static delay. This should be
improved and evaluated against real-world network experiments. The goal of the
simulator is not necessarily to be a perfect simulator, but a useful simulator
for making different kinds of traffic analysis defenses.

There are also fundamental issues with simulating blocking actions of machines.
Because the simulator takes as input a base network trace of encrypted network
traffic, we do not know any semantics or inter-dependencies between the packets
in the encrypted trace. As a result, we cannot properly simulate blocking
actions. For example, if a machine blocks a packet, we cannot know if the
blocked packet contains a request for a resource that leads to a response
contained in the following received packets. The simulator will happily still
receive the resource in the encrypted network trace. Here be dragons.

## Rich Debug Output
The simulator can be run with the `RUST_LOG=debug` environment variable set to
get rich debug output. For example, to run the integration test
`test_bypass_machine`with debug output, run the following command:

```
RUST_LOG=debug cargo test test_bypass_machine
```

## Contributing
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as MIT or Apache-2.0, without any additional terms or conditions.

## Sponsorship
Made possible with support from [Mullvad VPN](https://mullvad.net/), the
[Swedish Internet Foundation](https://internetstiftelsen.se/en/), and the
[Knowledge Foundation of Sweden](https://www.kks.se/en/start-en/).
Loading

0 comments on commit d7c1a3e

Please sign in to comment.