feat(cli): v3.2 multi-hop — per-hop cert, cell padding, 3-hop, CIDR whitelist
Closes the v3.1 unlinkability gap and resists volume/timing correlation:
1) Per-hop client cert (identity-unlinkable hops). [[client.circuit.hops]]
now accepts {addr, cert_path, key_path, [server_name]} per hop — each
hop sees a different CN, so a relay and an exit cannot correlate the
same client by certificate. Old flat `hops = ["ip:port"]` form still
parses (serde untagged enum) and falls back to [pki] cert/key.
`aura provision-client --circuit-hops N` mints N fresh UUIDv4 certs.
2) Cell padding. CellPaddingConn wrapper pads every outgoing packet to a
fixed size (default 1280 bytes; `cell_size = N` configurable) before
it hits the inner AEAD. Format: u16_be(real_len) || pkt || zero_pad.
On-wire sizes become constant -> defeats volume/timing fingerprints.
Opt-in via [client.circuit] cell_padding = true and the mirror
[server] cell_padding_for_circuit_clients = true.
3) 3-hop support. dial_circuit now accepts N >= 2 hops; iterative
ExtendBridge nests N-1 forwarders and N handshakes. Client owns the
full chain via CircuitConnection (forwarders abort on drop).
New integration test multihop_v3_2_three_hops_end_to_end runs three
in-process actors (A relay -> B relay -> C exit) on loopback and
verifies peer_id == C's CN.
4) CIDR whitelist. [server.relay] allow_extend_to entries accept
"10.0.0.0/24" (subnet, any port), "10.0.0.0/24:443" (subnet + port),
"[2001:db8::/32]:443" (IPv6 with port), as well as exact IP:port.
Empty list keeps the v3.1 open-relay (warn).
19 new tests; workspace 276 passed (+19), clippy -D warnings clean, fmt clean.
257 baseline tests untouched; all v2 / v3.1 / LE configs work as before.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+40
-11
@@ -127,17 +127,46 @@ enabled = false
|
||||
mean_interval_ms = 500
|
||||
jitter = 0.5
|
||||
|
||||
# v3.1 multi-hop / onion routing: dial through an entry-relay before reaching the exit-server.
|
||||
# When `enabled = true`, the client opens an OUTER Aura UDP connection to `hops[0]` (the relay),
|
||||
# sends one ExtendBridge envelope describing `hops[1]` (the exit), waits for CircuitReady, and
|
||||
# then runs an INNER Aura handshake addressed to the exit through that relay — two AEAD layers
|
||||
# per packet, the exit knows the client's CN but not the source IP, the relay knows the source
|
||||
# IP but not the destination nor a single plaintext byte. Exactly two hops are required in
|
||||
# v3.1; configure the relay-server with [server.relay] enabled = true and
|
||||
# allow_extend_to = ["<this client's exit IP:port>"].
|
||||
# v3.1 / v3.2 multi-hop / onion routing: dial through 1 or 2 intermediate hops before reaching
|
||||
# the exit-server. When `enabled = true`, the client opens an OUTER Aura UDP connection to
|
||||
# `hops[0]` (the entry-relay), sends one ExtendBridge envelope describing the next hop, waits for
|
||||
# CircuitReady, then either dials the exit directly (2-hop) or repeats the ExtendBridge dance
|
||||
# through a middle relay (3-hop). The innermost handshake authenticates the EXIT's cert opaquely
|
||||
# — every relay sees only the next-hop address and AEAD ciphertext.
|
||||
#
|
||||
# Omitting the section (or `enabled = false`) keeps the v2 single-hop dial path intact —
|
||||
# [client] server_addr / [transport] order rules apply as before.
|
||||
# v3.2 adds:
|
||||
# * per-hop client certificates (the entry-relay and the exit see DIFFERENT CNs — they cannot
|
||||
# link the two handshakes by identity), and
|
||||
# * cell padding (every packet is padded to a constant `cell_size` bytes before sending — the
|
||||
# exit MUST also enable `[server] cell_padding_for_circuit_clients = true` to decode), and
|
||||
# * 3-hop support (just add a third [[client.circuit.hops]] table).
|
||||
#
|
||||
# Omitting the section (or `enabled = false`) keeps the v2 single-hop dial path intact.
|
||||
#
|
||||
# --- v3.1 FLAT FORM (back-compat) — every hop uses the [pki] cert/key above (NOT unlinkable):
|
||||
# [client.circuit]
|
||||
# enabled = true
|
||||
# hops = ["198.51.100.5:443", "203.0.113.10:443"] # [entry_relay, exit_server] — literal IP:port
|
||||
# hops = ["198.51.100.5:443", "203.0.113.10:443"]
|
||||
#
|
||||
# --- v3.2 PER-HOP FORM — each hop has its own cert/key (identity-unlinkable):
|
||||
# [client.circuit]
|
||||
# enabled = true
|
||||
# cell_padding = true
|
||||
# cell_size = 1280
|
||||
#
|
||||
# [[client.circuit.hops]]
|
||||
# addr = "198.51.100.5:443"
|
||||
# cert_path = "~/.config/aura/circuit/entry.crt"
|
||||
# key_path = "~/.config/aura/circuit/entry.key"
|
||||
#
|
||||
# [[client.circuit.hops]] # OPTIONAL middle hop for a 3-hop circuit
|
||||
# addr = "198.51.100.99:443"
|
||||
# cert_path = "~/.config/aura/circuit/middle.crt"
|
||||
# key_path = "~/.config/aura/circuit/middle.key"
|
||||
#
|
||||
# [[client.circuit.hops]]
|
||||
# addr = "203.0.113.10:443"
|
||||
# cert_path = "~/.config/aura/circuit/exit.crt"
|
||||
# key_path = "~/.config/aura/circuit/exit.key"
|
||||
#
|
||||
# Generate per-hop certs in one command: `aura provision-client --circuit-hops 3 ...`
|
||||
|
||||
Reference in New Issue
Block a user