Part II · Protocol & Services · Chapter 6
Beyond Tor: I2P
Commands only. What each step does, why it is built this way, and the judgment behind it are in the book.
LAB 6.1
An I2P router in the lab
Step 1, Bring up the testnet
# several i2pd routers on an isolated network, with their own netid so they # only ever talk to each other — never the live I2P network $ ./lab up i2p $ ./lab stop i2p # stop — containers kept; ./lab up i2p resumes them $ ./lab down i2p # delete — removes containers (add --volumes to drop the seed)
Expected, the testnet routers start
darkweb-i2p-ff1 started (floodfill) darkweb-i2p-ff2 started (floodfill) darkweb-i2p-r1 started darkweb-i2p-r2 started darkweb-i2p-r3 started i2p testnet: 5 routers, netid 42, isolated network (no route out)
Step 2, Open the router console
# the i2pd web console for router r1, proxied to your host on 7070 $ ./lab i2p console r1 console for darkweb-i2p-r1 → http://127.0.0.1:7070
Step 3, Why that number is the headline
Verify in the Docker host
$ ./lab check 6.1
- The testnet is up and the routers run on an isolated network with a custom netid
- A router console is reachable and reports a converging view of known routers
- At least one router is building or carrying tunnels (it has joined the network)
LAB 6.2
The NetDB and floodfill
Step 1, Read the database that has no owner
# count what this router knows, and pick out the floodfills that hold the DB $ ./lab i2p netdb r1 known routers : 5 floodfills : 2 (ff1, ff2) leaseSets held : 1
Step 2, Publish a record and watch it land
# start a throwaway service on r2; its leaseSet is published into the NetDB $ ./lab i2p publish r2 destination : k6r4...q2wa.b32.i2p leaseSet published; held by floodfills closest to its routing key
Step 3, Eclipse the record
# which floodfills hold that destination's leaseSet — and how hard is it to # become them? the analyzer works over routers you already run. $ ./labs/artifacts/i2p-netdb/eclipse.py --target k6r4...q2wa
Expected, the closest floodfills, and the cost to displace them
routing key (today) : 3f9c...a1 floodfills by XOR distance to the key: ff2 distance 2^249.3 *holds the leaseSet ff1 distance 2^250.1 *holds the leaseSet to eclipse: craft 2 router identities closer than ff2 identities tried : 41067 (grinding SHA-256 for proximity) result : target's lookups would resolve to attacker floodfills note: the routing key rotates daily — grinding must be redone every day
Verify in the Docker host
$ ./lab check 6.2
- The NetDB holds routerInfos and the floodfills are identifiable
- The eclipse analyzer's distance logic self-tests correctly on synthetic hashes
- You can explain what daily key rotation costs an attacker
LAB 6.3
Tunnels, unidirectional
Step 1, Inbound and outbound are different tunnels
$ ./lab i2p tunnels r1
Expected, two sets of tunnels, different routers in each
outbound tunnels (traffic leaving r1): #1 r1 → r3 → ff1 (gateway r1, endpoint ff1) inbound tunnels (traffic arriving at r1): #1 r2 → ff2 → r1 (gateway r2, endpoint r1) note: the outbound and inbound paths share no interior router
Step 2, A round trip is four tunnels
Step 3, What the leaseSet gives away
Verify in the Docker host
$ ./lab check 6.3
- The router has both inbound and outbound tunnels, built through different routers
- A published destination's leaseSet lists its inbound gateways
- You can explain why correlation is harder here and what the leaseSet exposes in exchange
LAB 6.4
Eepsites: access and host
Step 1, Host an eepsite
# point an i2pd server tunnel at a small web server running on r2 $ ./lab i2p eepsite r2 --serve serving on r2:8080 through an i2pd server tunnel destination : k6r4...q2wa.b32.i2p
Step 2, Reach it from another router
# from r1, fetch the eepsite through r1's own HTTP proxy (all in-testnet) $ ./lab i2p fetch r1 k6r4...q2wa.b32.i2p
Expected, the page comes back over I2P
looked up leaseSet for k6r4...q2wa (via floodfill ff2) built client tunnels; fetching... HTTP/1.1 200 OK <title>lab eepsite on r2</title>
Step 3, Naming without DNS
Verify in the Docker host
$ ./lab check 6.4
- An eepsite is served through an i2pd server tunnel and has a b32 destination
- Another router resolves that destination and fetches the page, all in-testnet
- You can explain the addressbook / jump-service model and its chokepoints
LAB 6.5
I2P's threat model vs Tor's
Step 1, Move the adversaries onto I2P
Step 2, Score a concrete role
Worked model, operator of an I2P eepsite (1 low … 5 high)
threat L I E weight vs Tor onion (Lab 5.7) ----------------------------- - - - ------ ---------------------- application leak / canary 4 5 2 40 unchanged — app layer ignores the network operator OPSEC mistake 4 5 2 40 unchanged — persona, stylometry, reuse exit reads destination/content 1 2 1 2 much lower — no exit for in-network svc NetDB floodfill eclipse (6.2) 2 4 3 24 NEW — no Tor equivalent leaseSet gateway harvest (6.3) 3 3 3 27 NEW — inbound edge is published correlation (four-tunnel) 2 4 2 16 harder to mount than Tor's both-ends being a router yourself 3 2 3 18 NEW — you carry traffic by default
Step 3, The headline
Verify in the Docker host
$ ./lab check 6.5
- The I2P threat-model worksheet ships with the shifted and new rows pre-listed
- The testnet the model reasons about is up and its floodfills are present
- You produced a weighted model for a specific I2P activity, not "I2P in general"
LAB 6.6
Both networks, side by side
Step 1, Bring up both
$ ./lab up base # the Tor gateway + workstation from Part I $ ./lab up i2p # the I2P testnet alongside it $ ./lab ps
Step 2, Prove the isolation
# the I2P routers have no clearnet path — this must fail (that's the pass) $ docker exec darkweb-i2p-r1 sh -c 'wget -qO- -T5 https://check.torproject.org' ; echo "exit=$?" exit=1
Step 3, Keep the streams apart
Verify in the Docker host
$ ./lab check 6.6
- Both stacks are running at once
- An I2P router has no clearnet route (the egress test fails, which is the pass)
- The Tor workstation's fail-closed egress lock still holds (Lab 2.7 still passes)
LAB 6.7
A comparison you can defend
Step 1, Lay the two networks side by side
Tor vs I2P, by property (Hyphanet stubbed for Ch. 7)
property Tor I2P
------------------- ------------------------ ------------------------------
trust root 9 signed dir authorities no authority; Kademlia NetDB
→ one hourly consensus → floodfills, daily-rotated keys
path model one bidirectional circuit four unidirectional tunnels
(guard sees both ends) (no interior sees both ends)
who routes volunteers run relays; every router routes by default
clients don't relay
clearnet exit yes — exit nodes; core no by default; in-network first,
use case is reaching it outproxy is a separate choice
hidden services v3 onion addresses eepsites, .b32.i2p destinations
naming onion address = key; addressbook + jump services
no human-name layer (a social trust layer)
key exposure intro points published inbound gateways published in
the leaseSet (ties to eclipse)
biggest lever both-ends correlation; floodfill/NetDB attacks on a
malicious guard+exit specific destination
reach for it when anonymous access to the a self-contained darknet:
clearnet; onion services in-network services, resilience
to authority-based blocking
Step 2, Let the threat model choose
Verify in the Docker host
$ ./lab check 6.7
- The overlay comparison matrix ships, populated for Tor and I2P
- It has a stubbed column ready for Hyphanet (Chapter 7)
- You produced a network choice for a specific task, justified by a property and a threat-model row