Documentation
¶
Overview ¶
Package onion implements DMCN onion routing (SPEC.md §6): per-hop layered encryption (SealLayer/OpenLayer), fixed 3-hop packet build/peel (BuildOnion/PeelOnion), and diversity-aware route selection (SelectRoute).
Privacy properties provided ¶
- Layered confidentiality: each hop learns only its predecessor and successor (entry: sender+hop2; middle: hop1+hop3; exit: hop2+recipient). No single relay links sender to recipient.
- Per-hop unlinkability: every layer uses a fresh ephemeral X25519 key, so a packet looks unrelated across hops.
- Size-class padding: the innermost (delivery) layer is bucketed to a size class (frameLayer, classes 1 KB–36 MB, the top sitting just above the ~35 MB message ceiling), hiding the message size among all same-class messages. Outer (forwarding) layers add only routing overhead — they are NOT re-bucketed — so a 3-hop message is ~3× its size class on the wire (the inherent cost of relaying it three times), not a per-hop bucket multiplication.
- Optional per-hop timing jitter (relay.WithOnionJitter) blurs simple timing correlation.
- Route diversity: distinct peers and (in strict mode) distinct /24 subnets; an optional pinned guard for the entry hop.
Residual gaps (not yet implemented) ¶
- Not Sphinx: packets are nested, so they SHRINK hop-to-hop — a global observer can infer position from the decreasing size. Padding is per-layer bucketing, not constant-size-across-hops. Full fixed-size (Sphinx) packets are future work.
- No mixing/batching: forwarding is synchronous hop-by-hop (the sender blocks on the chain's ACK). Jitter is a weak substitute for a real mix node that batches + reorders; end-to-end latency still correlates send/deliver.
- No cover traffic: there are no dummy packets, so traffic volume is observable.
- Guard persistence: SelectRoute can pin a guard, but persisting/rotating it is the caller's job and is NOT done for the ephemeral CLI (each invocation draws fresh). A long-lived client should hold a guard and rotate it ~monthly.
- Small-network anonymity: Relaxed mode (dev/3-node cluster) drops subnet diversity, so co-located relays share an operator — the structural property holds but the anonymity set is small until the relay set diversifies.
Package onion implements the per-hop encryption primitive for DMCN onion routing: each layer is sealed to a relay's X25519 key with an ephemeral X25519 ECDH → HKDF-SHA256 → AES-256-GCM, the same KEM/DEM scheme the message layer uses to wrap a CEX (internal/core/message wrapCEK). A relay opens its layer with its X25519 private key to recover the next hop + inner payload.
Index ¶
Constants ¶
const DefaultHops = 3
DefaultHops is the fixed onion route length (see SPEC.md §6): entry knows the sender, exit knows the destination, the middle keeps them non-adjacent.
const DeliverHop = "DELIVER"
DeliverHop is the sentinel next_hop value at the innermost layer, telling the final relay to deliver (STORE) rather than forward.
Variables ¶
This section is empty.
Functions ¶
func BuildOnion ¶
BuildOnion layers a delivery payload for the given route, encrypting from the innermost hop outward. route is ordered entry→…→exit; the exit hop performs the final delivery (a marshaled StoreRequest in `delivery`). The returned packet is what the sender submits to route[0]. ttl is the absolute expiry stamped into every layer.
func OpenLayer ¶
func OpenLayer(relayPriv [32]byte, sl *SealedLayer) ([]byte, error)
OpenLayer decrypts a sealed layer with the relay's X25519 private key, recovering the plaintext (the next hop instruction + inner payload). A wrong key or any tampering fails the AEAD authentication.
func PeelOnion ¶
func PeelOnion(relayPriv [32]byte, pkt *dmcnpb.OnionPacket) (*dmcnpb.OnionLayer, error)
PeelOnion opens this relay's layer with its onion X25519 private key, returning the decrypted OnionLayer (next_hop + inner packet or delivery payload).
Types ¶
type Hop ¶
Hop identifies one relay on a route: its peer ID and onion X25519 public key (both from its RelayDescriptor).
func SelectRoute ¶
func SelectRoute(candidates []identity.RelayDescriptor, exitPeerID string, opts RouteOptions) ([]Hop, error)
SelectRoute picks an onion route of opts.Hops relays ending at exitPeerID (the recipient's relay, which performs final delivery). The earlier hops are chosen at random from candidates with distinct peer IDs and — unless Relaxed — distinct /24 subnets. Errors if there aren't enough sufficiently-diverse relays.
type RouteOptions ¶
type RouteOptions struct {
Hops int // 0 ⇒ DefaultHops
// Relaxed drops the subnet-diversity constraint, keeping only distinct peers.
// For small/dev networks (e.g. a 3-node localhost cluster) where every relay
// shares a subnet/operator — the protocol property (middle sees neither end)
// still holds; anonymity strengthens as the relay set diversifies.
Relaxed bool
// Guard, if set to a candidate's peer ID (and not the exit), pins that relay as
// the entry hop. Reusing a stable guard across sends limits a user's exposure to
// ever drawing a malicious entry (Tor's guard rationale). Persisting/rotating
// the guard is the caller's concern (see the package doc — not done for the
// ephemeral CLI; a long-lived client should hold one + rotate ~monthly).
Guard string
}
RouteOptions tunes route selection.
type SealedLayer ¶
SealedLayer is one onion layer: a payload encrypted to a single relay's X25519 key, plus the ephemeral public key the relay needs to derive the shared secret.