onion

package
v0.13.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

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

View Source
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.

View Source
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

func BuildOnion(route []Hop, delivery []byte, ttl time.Time) (*dmcnpb.OnionPacket, error)

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

type Hop struct {
	PeerID    string
	X25519Pub [32]byte
}

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

type SealedLayer struct {
	EphemeralXPub [32]byte
	Nonce         [12]byte
	Ciphertext    []byte
	Tag           [16]byte
}

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.

func SealLayer

func SealLayer(relayPub [32]byte, plaintext []byte) (*SealedLayer, error)

SealLayer encrypts plaintext to relayPub. A fresh ephemeral X25519 keypair is generated per call (single-use), so layers are unlinkable across hops.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL