A general-purpose encrypted communication tool inspired by the foundational
Hiding Routing Information
paper (Goldschlag, Reed & Syverson, 1996)
Onion Router is a secure desktop application that demonstrates onion routing โ the anonymous-communication architecture that later evolved into Tor. It wraps messages in multiple layers of 1,024-bit RSA + AES-256-CBC encryption, routes them hop-by-hop through a simulated network, and peels one cryptographic layer at each node โ ensuring that no single node ever learns both the sender and the recipient.
The application is built across three languages, each handling a distinct layer of the security stack:
| Layer | Language | Artifact | Responsibility |
|---|---|---|---|
| ๐ Cryptographic Core | C (OpenSSL) | libonion_crypto.so |
RSA-1024, AES-256-CBC, SHA-256, CSPRNG; fixed-size onion construction & peeling |
| ๐ Routing Engine | C++17 | libonion_engine.so |
5-node network, virtual circuit management, replay detection, trace logging |
| ๐ฅ๏ธ User Interface | Python 3 + GTK 3 | main.py |
Animated network visualization, message console, real-time encryption log |
โ ๏ธ Disclaimer: This is an educational and demonstrative implementation faithful to the 1996 paper. It runs entirely on a single machine and is not a hardened anonymity system โ do not use it to protect real-world traffic.
- Overview
- Features
- Architecture Deep Dive
- System Requirements
- Installation & Build
- Usage Guide
- Project Structure
- Component Integration
- Testing
- Security Notes & Limitations
- Academic Reference
- License
- Layered onion encryption โ each hop adds/removes a cryptographic layer using RSA-1024 + AES-256-CBC, exactly as the paper describes
- Fixed-size 4,096-byte onions โ random padding ensures that onion size never reveals the number of remaining hops
- Bidirectional virtual circuits โ forward and backward symmetric key pairs enable anonymous two-way communication
- Replay detection โ SHA-256 fingerprinting rejects duplicate onions, preventing replay attacks
- Expiration enforcement โ onions carry timestamps and are automatically rejected after their TTL expires
- Real-time visualization โ Cairo-rendered animated network view shows the onion traveling hop-by-hop with pulsing nodes
- Color-coded encryption log โ every cryptographic operation (
BUILD,PEEL,FORWARD,CRYPT_FWD/BWD,DELIVER,REPLAY) is logged in real time - Node key inspection โ examine the RSA-1024 public key of any node in the network
- Dark security-console aesthetic โ Adwaita-dark theme with terminal-green accents
Every onion is a fixed 4,096-byte blob, so its size never leaks how many hops remain (the paper's constant-size requirement). Each layer is structured as:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ONION LAYER (4096 bytes) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 128-byte RSA-1024 โ 3968-byte AES-256-CBC region โ
โ encrypted block โ โ
โ โ โ โโโโโโโโโโโฌโโโโโโโโโโโฌโโโโโโโโโโโโโโโ โ
โ โ โ โ Header โ Inner โ Random โ โ
โ โผ โ โ (route โ Onion โ Padding โ โ
โ Wraps a 48-byte โ โ info) โ (next โ (constant โ โ
โ session secret: โ โ โ layer) โ size) โ โ
โ โโโโโโโโโโโโโโโโโโโโ โ โโโโโโโโโโโดโโโโโโโโโโโดโโโโโโโโโโโโโโโ โ
โ โ 32-byte AES key โ โ โ
โ โ 16-byte IV โ โ โ
โ โโโโโโโโโโโโโโโโโโโโ โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The header carries everything a node needs and nothing it does not:
| Field | Description |
|---|---|
exp_time |
Expiration timestamp โ onions are rejected after this time |
next_hop |
ID of the next routing node (or NULL if this is the final destination) |
Ff, Kf |
Forward stream cryptographic function and symmetric key |
Fb, Kb |
Backward stream cryptographic function and symmetric key |
When a routing node receives an onion, it performs these operations in sequence:
Encrypted Onion Routing Node X Smaller Onion
โโโโโโโ โโโโโโโ
โโโโโโโ โโโบ 1. RSA-decrypt outer block โโโโโ โ
โโโโโโโ 2. AES-decrypt body with โโโโโ โ
โโโโโโโ recovered session key โโโโโ โ
โโโโโโโ 3. Check expiration time โ โ + random padding
โโโโโโโ 4. Reject replays (SHA-256 DB) โ โ โ back to 4096B
โโโโโโโ 5. Store circuit state โโโโโโโ
โโโโโโโ 6. Forward re-padded onion โโโโโโโบ to next hop
A node learns only its immediate predecessor and successor โ never the full route, the total number of hops, or its position in the chain.
Once the onion has traversed the route and each node has stored its circuit state, the hops form a virtual circuit:
โโโโโโโโโโโโโ forward keys โโโโโโโโโ forward keys โโโโโโโโโโโโโ
โ Initiator โ โโโโโโโโโโโโโโโโโโโโบ โ Node โ โโโโโโโโโโโโโโโโโโโโบ โ Responder โ
โ Proxy โ โโโโโโโโโโโโโโโโโโโโ โ (รN) โ โโโโโโโโโโโโโโโโโโโโ โ Proxy โ
โโโโโโโโโโโโโ backward keys โโโโโโโโโ backward keys โโโโโโโโโโโโโ
- Forward direction: data is pre-crypted by the initiator's proxy (applying inverses of all forward keys, innermost first) so that each hop peels exactly one layer
- Backward direction: the responder's proxy crypts the reply once, and each intermediate node adds another layer; the initiator strips them all
The demo builds a fixed five-node topology (as labelled in the paper's Figure 1):
X โโโโโโโ U
โฑ โฒ โฑ โฒ
W โฒ โฑ โฒ
โฒ โฒ โฑ โฒ
โฒ Y โโโโโโโ Z
โฒ โฑ
โผ
(W, Z = Proxy/Routing Nodes)
(X, Y, U = Routing Nodes)
| Node | Role | Description |
|---|---|---|
| W | Proxy/Routing | Initiator's entry point โ constructs onions, manages the forward/backward stream |
| X | Routing | Intermediate relay node |
| Y | Routing | Intermediate relay node |
| Z | Proxy/Routing | Responder's exit point โ delivers plaintext to the destination |
| U | Routing | Intermediate relay node |
| Component | Requirement |
|---|---|
| OS | Linux (Ubuntu 20.04+ recommended) |
| C Compiler | GCC with C11 support |
| C++ Compiler | G++ with C++17 support |
| Build System | CMake โฅ 3.15 (fallback compiler invocation in run.sh) |
| Crypto Library | OpenSSL development headers (libssl-dev) |
| GUI Toolkit | GTK 3 with PyGObject (python3-gi, python3-gi-cairo) |
| Python | Python 3.8+ |
sudo apt-get update && sudo apt-get install -y \
build-essential cmake libssl-dev \
python3-gi python3-gi-cairo gir1.2-gtk-3.0 \
libgirepository1.0-dev pkg-config๐ก Python interpreter note: The GUI needs a Python that can
import gi(PyGObject). On many systems, the distro interpreter/usr/bin/python3has this, whilepyenv/condaPythons may not.run.shautomatically probes for a GTK-capable interpreter; you can force one with:ONION_PYTHON=/usr/bin/python3 ./run.sh
git clone https://gitlab.com/BASSCIOP/onion-router.git
cd onion-router
./run.shThis builds both native shared libraries and launches the GTK GUI.
# Build + launch GUI (default)
./run.sh
# Build + run C/C++ test suites only
./run.sh --test
# Skip build, launch GUI with existing libraries
./run.sh --no-buildmake # Configure (CMake) + build shared libraries and tests
make test # Build, then run C and C++ test suites
make run # Build, then launch the GUI
make clean # Remove all build artifactscmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
ctest --test-dir build --output-on-failureBuild artifacts are placed in:
build/lib/โlibonion_crypto.so,libonion_engine.sobuild/bin/โtest_crypto,test_engine
Select a Source (proxy) and Destination from the dropdown menus. Optionally specify intermediate Hops (e.g., X,Y) and set a Circuit TTL in seconds.
Click Build Circuit to watch the initiator's proxy wrap one RSA+AES layer per hop. The network view animates the onion traveling through nodes, and the encryption log fills with color-coded events.
Type a message and click Send Through Onion. The message is delivered over the forward stream to the exit node, and an acknowledgement returns via the backward stream.
Click Replay CREATE to re-inject the original onion and observe a node detecting and rejecting the replay from its seen-onion hash table โ demonstrating the paper's anti-replay mechanism.
Click Destroy to tear down the virtual circuit, propagating DESTROY commands through all participating nodes.
Click the Node Keys button in the header bar to view the RSA-1024 public key (PEM format) of any node in the network.
| Tag | Color | Meaning |
|---|---|---|
BUILD |
๐ข Green | Proxy wraps a layer (RSA + AES) |
PEEL |
๐ต Cyan | Node removes its encryption layer |
FORWARD |
๐ก Yellow | Onion re-padded and relayed to next hop |
CIRCUIT |
๐ฃ Purple | Circuit state stored in node's table |
CRYPT_FWD/BWD |
๐ท Teal | Stream (de)cipher applied per hop |
DELIVER |
๐ข Bright Green | Payload delivered / recovered |
REPLAY |
๐ด Red | Replayed onion detected and rejected |
EXPIRE |
๐ Orange | Expired onion dropped |
ERROR |
๐ด Red | Operation failed |
onion_router_app/
โ
โโโ ๐ src/
โ โโโ ๐ crypto/ # โโ C Cryptographic Core โโ
โ โ โโโ onion_crypto.h # RSA-1024, AES-256-CBC, SHA-256, CSPRNG
โ โ โโโ onion_crypto.c # (272 lines) key gen, encrypt, decrypt, hash
โ โ โโโ onion_builder.h # Onion build/peel interface + constants
โ โ โโโ onion_builder.c # (298 lines) layered onion construction
โ โ
โ โโโ ๐ engine/ # โโ C++17 Routing Engine โโ
โ โ โโโ routing_node.hpp/.cpp # Node: peel onion, verify expiry, replay-check
โ โ โโโ circuit_manager.hpp/.cpp # Circuit table: ID โ {connections, keys}
โ โ โโโ onion_router.hpp/.cpp # 5-node network + extern "C" ABI for ctypes
โ โ
โ โโโ ๐ gui/ # โโ Python / GTK 3 Front-End โโ
โ โโโ main.py # Application entry point (Gtk.Application)
โ โโโ app_window.py # Main window, dark theme, event orchestration
โ โโโ network_view.py # Cairo-rendered animated network graph
โ โโโ message_panel.py # Message compose/send + color-coded log
โ โโโ crypto_utils.py # ctypes bindings to both .so libraries
โ
โโโ ๐ tests/
โ โโโ test_crypto.c # C unit tests for crypto primitives
โ โโโ test_engine.cpp # C++ unit tests for routing engine
โ โโโ test_gui_smoke.py # Headless (xvfb) GUI integration test
โ
โโโ ๐ docs/
โ โโโ screenshot.png # Application screenshot
โ
โโโ CMakeLists.txt # CMake build configuration
โโโ Makefile # Convenience wrapper around CMake
โโโ run.sh # Build + launch script with auto-detection
โโโ requirements.txt # Python dependencies (PyGObject, pycairo)
โโโ LICENSE # MIT License
โโโ README.md # This file
Codebase: ~3,800 lines across 18 source files (806 C, 1,158 C++, 1,596 Python, 254 test code).
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Python / GTK 3 GUI โ
โ main.py โ app_window.py โ network_view.py โ
โ message_panel.py โ
โ โ โ
โ ctypes (FFI) โ
โ โ โ
โ โผ โ
โ crypto_utils.OnionEngine โ
โ (Python wrapper for the C ABI) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ libonion_engine.so (C++17) โ
โ โ
โ OnionRouter โโ RoutingNode โโ CircuitManager โ
โ โ โ
โ โ extern "C" ABI: orouter_create(), orouter_send() โ
โ โ orouter_reply(), orouter_destroy(), etc. โ
โ โ โ
โ โ Every operation emits a JSON trace for the GUI โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ libonion_crypto.so (C) โ โ
โ โ โ โ
โ โ RSA-1024 โ AES-256-CBC โ SHA-256 โ โ
โ โ CSPRNG โ Onion Build โ Padding โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ (OpenSSL 3.x EVP API) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The engine exposes a small extern "C" ABI (orouter_*) that the Python OnionEngine class wraps. Every operation produces a JSON trace โ a list of steps ({step, type, node, circuit_id, layer, bytes, detail}) โ which the GUI replays to animate the network and populate the encryption log.
The project includes three test suites:
| Test | Type | What It Validates |
|---|---|---|
test_crypto |
C unit test | RSA-1024 keygen/encrypt/decrypt, AES-256-CBC round-trip, SHA-256 digests, onion build/peel integrity, constant-size padding |
test_engine |
C++ unit test | Node creation, circuit table management, full onion routing through 5-node network, replay detection, expiration enforcement |
test_gui_smoke |
Python integration | Headless GTK window instantiation, engine loading, circuit build, message send, replay rejection, destroy โ all via xvfb-run |
Run all tests:
# Via run.sh
./run.sh --test
# Via Make
make test
# Via CTest
ctest --test-dir build --output-on-failure| Aspect | Status |
|---|---|
| Key management | Keys are generated fresh in-process each run; no persistent key distribution or PKI |
| RSA-1024 | Used to match the era of the 1996 paper; not adequate for real-world confidentiality today (minimum RSA-2048 recommended) |
| Network simulation | Everything runs in a single process on one host โ there is no real network transport |
| Traffic analysis | No traffic padding, no dummy messages, no latency smoothing โ a global passive adversary would trivially correlate traffic |
| Forward secrecy | Not implemented โ compromise of a node's long-term RSA key exposes past sessions |
| Side channels | No timing attack mitigations or constant-time comparisons beyond what OpenSSL provides |
This is a teaching tool, not a production anonymity network. For real-world anonymous communication, use Tor.
D. M. Goldschlag, M. G. Reed, P. F. Syverson.
"Hiding Routing Information"
Workshop on Information Hiding, Cambridge, UK, May 1996.
In: R. Anderson (Ed.), Information Hiding, LNCS 1174, pp. 137โ150, Springer-Verlag.
๐ Full Paper (PDF)
This paper introduced the concept of Onion Routing โ the layered encryption approach that directly led to the development of Tor in 2002 and remains the foundation of anonymous communication on the Internet today.
MIT License
Copyright (c) 2026 ROBERT C BASSยฎ
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
See LICENSE for the full text.
Built with ๐ง by ROBERT C BASSยฎ โ Inspired by the pioneers of anonymous communication
