mergedhumanPR #169Communication
replay-safe: comms replay-attack resistance on top of authenticated
I picked #01 — versioned message schemas
Author
@HreStyles
github profile →- Status
- Merged
- Merged on
- Jul 10
- Branch
- hackathon/hrehaanshah-comms-replay-resistance
Description
The pitch.
## Problem
I picked **#01 — versioned message schemas**
([`docs/hackathon/problems/01-comms-schema-versioning.md`](https://github.com/projnanda/nandatown/blob/main/docs/hackathon/problems/01-comms-schema-versioning.md)),
but before writing code I found it was already solved and merged: PR #18
(`comms/versioned.py`, forward/backward compat) and a follow-on PR #105
(`comms/authenticated.py`, HMAC tamper-evidence against rollback/stripping).
Reading `authenticated.py` for a real remaining gap, its own docstring names
one: *"a verbatim re-send of a genuine envelope still verifies."* A captured,
byte-identical copy of an honest, correctly-tagged envelope has a perfectly
valid HMAC tag — nothing was rewritten, so tamper-evidence has nothing to
catch. An on-path relay can record one authentic envelope and replay it
indefinitely; for a non-idempotent message (payment, vote, state transition)
that's a live attack.
## What I built
- `comms/replay_safe.py` — `ReplaySafeComms`, a subclass of
`AuthenticatedComms`. Inherits all version/tamper checks unchanged; adds a
bounded, per-sender memory of accepted envelope ids and raises a typed
`ReplayError` on a repeat. No new wire field — the envelope `id` is already
covered by the HMAC tag, so it's a free, tamper-evident nonce.
- Two adversarial validators, `comms_replay_resistance` and
`comms_replay_honest_delivery`, that recompute ground truth from raw wire
deliveries (independent of any plugin) — they fail against
`authenticated`/`versioned`/`nest_native` and pass against `replay_safe`.
- `scenarios/comms_replay.yaml` + `scenarios_builtin/comms_replay.py` — 8
peers each send a solo envelope (control) and one that a relay replays
verbatim, plus an auditor.
- Unit tests (`test_replay_safe_comms.py`), validator + end-to-end tests
(`test_comms_replay.py`), deterministic under seeds 42/7/1337.
- A short [submission README](docs/hackathon/submissions/comms-replay-safe/README.md)
with the same info plus honest limits.
## How to run it
```bash
uv sync
uv run nest run scenarios/comms_replay.yaml
uv run python -c "
from pathlib import Path
from nest_core.validators import validate_trace
for r in validate_trace(Path('traces/comms_replay.jsonl'), 'comms_replay'):
print('PASS' if r.passed else 'FAIL', r.name, '-', r.detail)
"
uv run pytest packages/nest-plugins-reference/tests/test_replay_safe_comms.py \
packages/nest-core/tests/test_comms_replay.py -v
make ci-local
```
## Result
Same scenario, `comms: authenticated` (pre-fix, no replay memory):
```
FAIL comms_replay_resistance - m-0-replayed: replay delivery #2 not rejected (got accepted); ... (8 total)
PASS comms_replay_honest_delivery - 16 first-time delivery(ies) correctly accepted
```
`comms: replay_safe` (this PR):
```
PASS comms_replay_resistance - 8 replayed envelope(s) correctly rejected after first delivery
PASS comms_replay_honest_delivery - 16 first-time delivery(ies) correctly accepted
```
`make ci-local`: ruff check clean, ruff format clean, pyright 0 errors,
**pytest 1177 passed / 1 skipped**, deterministic under seeds 42/7/1337.
## Limits (see the submission README for the full list)
- In-memory only — the seen-id window doesn't survive a process restart.
- Bounded window (`DEFAULT_REPLAY_WINDOW = 4096` ids per sender) — a replay
old enough to be evicted would be accepted as new.
- No key exchange (inherited from `authenticated`: a fixed pre-shared
`channel_secret` stands in for a real session key).
- Detects exact duplicate ids, not general reordering.
- Only tested inside Nanda Town's deterministic in-memory simulator.
Try it
Open PR on GitHubView diffCheckout locally
git fetch origin pull/169/head:pr-169
git checkout pr-169