A real, circuit-based mix network for testing anonymous communication designs against 432 configurations of crypto, routing, traffic, and adversary models, genuine OS processes and real sockets standing in for the real network, not a discrete-event approximation of one.
Most anonymity-network research runs on a discrete-event simulator: fast, but every latency number and every crypto cost is a distribution someone had to assume first. The alternative is running the real Tor codebase, where every cost is real, but instrumenting one adversary experiment means patching production software built for a different job. AnonTestLab sits between them: real relay processes, real sockets, a real ECDHE handshake and AEAD encryption per hop, built from the ground up as a research harness where any single dial, cipher, curve, routing strategy, traffic shape, WAN condition, adversary, is one YAML field, and a full run takes seconds.
It's a low-latency, circuit-based mix network: Tor's telescoping onion-routing design, where each relay (a mix node) strips exactly one encryption layer and forwards, rather than the high-latency batch-and-shuffle mixnets built for anonymous email. That's the specific design point every measurement on this page is testing.
Latency, handshake cost, and crypto overhead are numbers someone chose. Change the assumption, get a different answer, not a different measurement.
Every cost is genuine, but a new adversary or a swapped cipher means patching production software that was never built to be an experiment harness.
Real relays, real sockets, real crypto cost, and every axis worth studying is a config field, not a fork.
ExperimentConfigEvery dimension (topology, routing, traffic shape, crypto, WAN conditions, adversary) is one validated, reproducible config object, seeded once.
spawn_relays() forks N real subprocessesEach relay is its own OS process bound to its own loopback address, 127.0.0.1 through 127.0.0.254, not a coroutine pretending to be a host.
build_circuit() telescopes hop by hopA real ephemeral ECDHE handshake and HKDF key derivation per hop, hop-local circuit IDs, independent forward and backward keys for the return path.
Optionally padded to a fixed cell_size, optionally fragmented across cells, optionally slowed by per-node or per-edge WAN conditions.
Measured latency, delivery, and bandwidth feed one or more adversary models; results land in results/<name>/ with a baseline diff if one's set.
anontestlab.emulatorReal subprocesses, real sockets, real crypto cost. Latency and delivery are measured outcomes, never sampled from a distribution someone had to assume first.
crypto_layer.pyThe return path is re-encrypted per hop with its own key, independent from the forward key but derived from the same handshake via HKDF, matching Tor's own directional key separation.
hop_depth.pyFixed-size cells still shrink by a fixed amount per hop position. The hop_depth adversary is a structural attacker that quantifies exactly how recoverable that position is, turning a disclosed simplification into a tested number.
anontestlab.cryptoSix AEAD ciphers across three ECDHE curves, selectable per experiment, for studies that actually compare algorithm choice rather than assuming one.
relay_process.pyPer-node heterogeneous conditions layer with directional per-edge conditions, so a relay's link to one peer can differ from its link to another, not just from other relays.
anontestlab.adversaryObservation, Feature, and Decision are separately swappable stages, so a new correlation statistic or classifier drops in without touching how traffic is observed.
Every scope trim is stated in the open and, where practical, given its own regression test, so "known limitation" means audited, not unexamined.
6 AEAD ciphers × 3 ECDHE curves × 2 routing strategies × 3 traffic distributions × 4 adversary models: 432 combinations, each one a real config field, not a code change.
traffic.*real_rate and cover_rate, each drawn from poisson (default), constant, or pareto for bursty, heavy-tailed arrivals. Real traffic is measured; cover traffic is decoy volume.
cover_behaviour.*drop_probability lets an intermediate mix node silently discard a fraction of cover cells, modeling a relay that doesn't forward every decoy. Real cells are never dropped this way.
routing.*routing_strategy: random or bandwidth_weighted. path_length sets hops per path; num_paths and split_strategy (round_robin or random) spread one session across more than one path.
traffic_shaping.*cell_size pads every cell to one fixed wire size; mode is variable or fixed_rate, a scheduled send cadence. Oversized payloads fragment across multiple cells automatically.
link_conditions.*latency_ms, jitter_ms, loss_probability, bandwidth_kbps, plus heterogeneous (per-node variation) and per_edge (per relay-to-peer variation, directional).
| Cipher | Note |
|---|---|
| none | plaintext passthrough, isolates transport cost |
| aes128gcm | 128-bit key |
| aes256gcm | 256-bit key, default |
| aes256gcmsiv | nonce-misuse resistant |
| aes256ocb3 | faster construction |
| chacha20poly1305 | stream-cipher based |
| Curve | Note |
|---|---|
| x25519 | default, Curve25519 |
| x448 | RFC 7748, larger keys |
| p256 | NIST secp256r1 |
| Adversary | Model | Key metric |
|---|---|---|
| global_observer | Observation → Feature → Decision, AS-level partial visibility | auc, tpr_at_fpr |
| path_compromise | independent-compromise Monte Carlo, no packets move | full_compromise_rate |
| watermark | active delay-pattern injection at hop 1 | watermark_detection_rate |
| hop_depth | structural, quantifies the fixed-size-cell leak | hop_position_accuracy |
A research harness earns trust by stating its scope trims in the open. These are the ones that still stand, each one deliberate:
hop_depth measures.bandwidth_weighted routing skips guard/exit-flag constraints. It weights by node capacity alone, deliberately not replicating Tor's position rules.