Handshake anti-replay (timestamp) + authenticated endpoint learning #34

Closed
opened 2026-07-06 03:56:23 +00:00 by vxfemboy · 0 comments
vxfemboy commented 2026-07-06 03:56:23 +00:00 (Migrated from github.com)

Context

Milestone 2a (multi-peer data plane, branch feat/multipeer-2a) introduced the in-loop lazy Noise-IK handshake in bin/yipd/src/peer_manager.rs. The whole-branch review surfaced a design-level gap that 2a deliberately defers (handshake hardening is #3 / NAT-roaming is 2b), tracked here.

Problem

The handshake carries no anti-replay token (unlike WireGuard's TAI64N timestamp in the initiation). Two consequences:

  1. Endpoint hijack via replay. handle_handshake_init learns a peer's endpoint from the datagram source (endpoint = src) after admission. An on-path adversary (in-threat-model for a censorship-resistance VPN) who captures one genuine HandshakeInit from configured peer P can replay it from a spoofed source S: start_responder succeeds, recovers P's real static key (so admission passes), and our outbound traffic for P is redirected to S. Admission itself is sound — an unconfigured/forged key allocates nothing — the exposure is specifically trusting an unauthenticated-freshness Init for endpoint learning.

  2. No safe session rebuild on genuine peer restart. Because a replayed old Init is indistinguishable from a genuine re-initiation, an Established responder cannot safely rebuild its session when a peer restarts with a fresh ephemeral (rebuilding on any differing Init would let a replayed old Init tear down a live session — a DoS). 2a works around the loss case by having the initiator retransmit the SAME Init within HANDSHAKE_TOTAL_MS (see the constant's doc comment in peer_manager.rs and commit 9954bfd), which is safe and closes the common wedge — but a genuine peer restart mid-session still cannot be recovered until the responder's own attempt path times out.

Fix (future milestone — handshake hardening / rekey)

  • Add an anti-replay token to the initiation (monotonic timestamp / counter, TAI64N-style), rejecting any Init not strictly newer than the last accepted one from that static key.
  • With anti-replay: (a) only update endpoint from an Init that passes the freshness check; (b) allow an Established responder to rebuild its session on a newer Init (genuine restart/rekey), safely.
  • Revisit by_tag eviction on rebuild (documented on the field) once rebuild becomes reachable.

Scope note

Out of scope for 2a (data plane). Belongs with the session-rekey / PQ-hybrid handshake work and the anti-DPI hardening track. Referenced from the HANDSHAKE_TOTAL_MS doc comment in peer_manager.rs.

## Context Milestone 2a (multi-peer data plane, branch `feat/multipeer-2a`) introduced the in-loop lazy Noise-IK handshake in `bin/yipd/src/peer_manager.rs`. The whole-branch review surfaced a design-level gap that 2a deliberately defers (handshake hardening is #3 / NAT-roaming is 2b), tracked here. ## Problem The handshake carries **no anti-replay token** (unlike WireGuard's TAI64N timestamp in the initiation). Two consequences: 1. **Endpoint hijack via replay.** `handle_handshake_init` learns a peer's endpoint from the datagram source (`endpoint = src`) after admission. An on-path adversary (in-threat-model for a censorship-resistance VPN) who captures one genuine `HandshakeInit` from configured peer P can replay it from a spoofed source S: `start_responder` succeeds, recovers P's real static key (so admission passes), and our outbound traffic for P is redirected to S. Admission itself is sound — an unconfigured/forged key allocates nothing — the exposure is specifically trusting an unauthenticated-freshness `Init` for endpoint learning. 2. **No safe session rebuild on genuine peer restart.** Because a replayed old `Init` is indistinguishable from a genuine re-initiation, an `Established` responder cannot safely rebuild its session when a peer restarts with a fresh ephemeral (rebuilding on any differing `Init` would let a replayed old `Init` tear down a live session — a DoS). 2a works around the loss case by having the initiator retransmit the SAME `Init` within `HANDSHAKE_TOTAL_MS` (see the constant's doc comment in `peer_manager.rs` and commit 9954bfd), which is safe and closes the common wedge — but a genuine peer restart mid-session still cannot be recovered until the responder's own attempt path times out. ## Fix (future milestone — handshake hardening / rekey) - Add an anti-replay token to the initiation (monotonic timestamp / counter, TAI64N-style), rejecting any `Init` not strictly newer than the last accepted one from that static key. - With anti-replay: (a) only update `endpoint` from an `Init` that passes the freshness check; (b) allow an `Established` responder to rebuild its session on a newer `Init` (genuine restart/rekey), safely. - Revisit `by_tag` eviction on rebuild (documented on the field) once rebuild becomes reachable. ## Scope note Out of scope for 2a (data plane). Belongs with the session-rekey / PQ-hybrid handshake work and the anti-DPI hardening track. Referenced from the `HANDSHAKE_TOTAL_MS` doc comment in `peer_manager.rs`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
femboy/yip#34
No description provided.