Skip to Content
3-Hop Relay Routing

3-Hop Relay Routing

Technical specification of anonymous message routing in Zentalk.

Overview

Zentalk implements its own 3-hop relay routing system (not Tor) to prevent network observers and servers from correlating senders with recipients. Messages travel through 3 relay nodes, with each hop encrypted using a separate key. This is a native Go implementation optimized for Zentalk’s architecture.

Not Tor: While conceptually similar to onion routing, Zentalk uses its own relay infrastructure with RSA-4096 layer encryption, integrated with E2EE (Double Ratchet), stealth addresses, and traffic padding. Unlike Tor, the exit relay delivers to a stealth address, not cleartext.

Circuit Architecture

3-Hop Design

NodePositionKnowledge
Guard1st hopKnows client IP, not destination
Middle2nd hopKnows neither client nor destination
Exit3rd hopKnows destination, not client

No single node can correlate sender with recipient.

Circuit Building

Node Selection

StepActionCriteria
1Select GuardFrom trusted guard set, geographic diversity
2Select MiddleRandom, exclude Guard’s family
3Select ExitRandom, exclude Guard/Middle families

Guard Node Persistence

PropertyValue
Guard set size3 nodes
Guard rotation90 days
SelectionWeighted by bandwidth/uptime
FallbackSecondary guard if primary fails

Rationale: Long-term guards prevent an adversary from observing many different entry points.

Family Exclusion

Nodes operated by the same entity are grouped into “families”:

If Guard in Family A: - Middle must NOT be in Family A - Exit must NOT be in Family A Prevents: Same operator controlling multiple hops

Encryption Layers

Layer Construction

Messages are encrypted in reverse order (exit → middle → guard):

1. Start with E2EE payload: M 2. Exit layer: C3 = AES-256-GCM(Key_exit, M || "exit_addr") 3. Middle layer: C2 = AES-256-GCM(Key_middle, C3 || "middle_addr") 4. Guard layer: C1 = AES-256-GCM(Key_guard, C2 || "guard_addr") 5. Send C1 to Guard

Per-Hop Key Establishment

Each hop requires a separate key exchange:

Circuit Extension Protocol: 1. Client → Guard: - CREATE cell with X25519 ephemeral - Guard responds with X25519 + derived key 2. Client → Guard → Middle: - EXTEND cell (encrypted for Guard) - Guard relays CREATE to Middle - Middle responds, relayed back 3. Client → Guard → Middle → Exit: - EXTEND cell (encrypted for Guard, Middle) - Establish key with Exit

Key Derivation

For each hop: DH_output = X25519(client_ephemeral, node_public) Key_forward, Key_backward = HKDF(DH_output, "ZentalkCircuit")

Message Format

Onion Cell Structure

┌─────────────────────────────────────────┐ │ Circuit ID (4 bytes) │ ├─────────────────────────────────────────┤ │ Cell Command (1 byte) │ ├─────────────────────────────────────────┤ │ Encrypted Payload (509 bytes, padded) │ ├─────────────────────────────────────────┤ │ Nonce (12 bytes) │ ├─────────────────────────────────────────┤ │ Authentication Tag (16 bytes) │ └─────────────────────────────────────────┘ Total: 542 bytes (fixed size)

Cell Commands

CommandCodePurpose
CREATE0x01Establish new circuit
CREATED0x02Circuit established
EXTEND0x03Extend circuit to next hop
EXTENDED0x04Extension successful
RELAY0x05Relay data through circuit
DESTROY0x06Tear down circuit
PADDING0x07Dummy traffic

Traffic Analysis Resistance

Fixed Cell Size

All cells are exactly 542 bytes:

Content SizePadding Added
100 bytes409 bytes
300 bytes209 bytes
509 bytes0 bytes

Constant-Rate Traffic

Traffic Pattern: ├── Real messages (when available) └── PADDING cells (when idle) Rate: 1 cell per 100ms (configurable)

Prevents: Timing correlation between send and receive.

Traffic Padding Parameters

ParameterValue
Padding modeCover traffic
Cell rate10 cells/second
Burst handlingQueue and send at constant rate
Idle padding50% of normal rate

Node Discovery

DHT-Based Discovery

1. Client queries DHT for relay nodes 2. Receives signed node descriptors 3. Verifies signatures against known keys 4. Caches node list locally

Node Descriptor

FieldSizePurpose
Node ID32 bytesEd25519 public key
AddressesVariableIP:port pairs
Bandwidth4 bytesSelf-reported capacity
Uptime4 bytesTime since last restart
Signature64 bytesEd25519 signature
Timestamp8 bytesDescriptor creation time

Node Selection Weights

Weight = Bandwidth × Uptime_factor × Diversity_factor Where: Uptime_factor = min(uptime_days / 30, 1.0) Diversity_factor = 1.0 if geographically diverse, 0.5 otherwise

Circuit Lifecycle

States

StateDescription
BUILDINGCREATE/EXTEND in progress
READYAll hops established
ACTIVECarrying traffic
CLOSINGDESTROY sent
CLOSEDResources released

Timeouts

PhaseTimeout
Circuit build30 seconds
Idle circuit10 minutes
Message relay60 seconds
Total circuit lifetime24 hours

Circuit Rotation

1. Build new circuit in background 2. Continue using old circuit 3. When new circuit ready: - Migrate new messages to new circuit - Let old circuit drain 4. Destroy old circuit

Security Properties

PropertyMechanism
Sender anonymityGuard node hides client IP
Recipient anonymityExit node hides destination
UnlinkabilityNo node sees both endpoints
Forward secrecyPer-circuit ephemeral keys
Replay protectionNonces, circuit IDs

Attack Mitigations

End-to-End Correlation

AttackMitigation
Timing analysisConstant-rate traffic
Volume analysisFixed cell size
Pattern analysisTraffic padding

Node Compromise

AttackMitigation
Single node3-hop design, limited knowledge
Two colluding nodesProbabilistic, family exclusion
All three nodesStatistical detection (anomalous)

Sybil Attack

AttackMitigation
Fake nodesProof-of-stake requirement
Bandwidth lyingMeasurement by other nodes
Eclipse attackMultiple bootstrap sources

Performance

MetricValue
Latency overhead~100-300ms
Bandwidth overhead~10% (headers + padding)
Circuit build time500ms-2s
Max throughputLimited by slowest hop

Configuration

OptionDefaultRange
Hop count33 (fixed)
Guard count31-5
Padding rate10/s1-20/s
Circuit lifetime24h1h-48h
Last updated on