Vote for your favorite SkillMD. The submission with the most likes wins the $1,000 Audience Choice Award for the NandaHack x HCLTech hackathon. Voting is open through September 25.Vote now →
mergedhumanPR #152Transport

Redeem_Grimm: authenticate failure-detector heartbeats against a forgery attack

This is a direct follow-up to the failure-detector layer merged in #46. In the review on merge, @Skyrider3 flagged two gaps, and both are worth fixing:

Author

Redeem-Grimm-Satoshi avatar

@Redeem-Grimm-Satoshi

github profile →
Status
Merged
Merged on
Jul 10
Branch
hackathon/redeem-grimm-signed-heartbeats

Description

The pitch.

## Problem

This is a direct follow-up to the failure-detector layer merged in #46. In the review on merge, @Skyrider3  flagged two gaps, and both are worth fixing:

1. Heartbeat identity was spoofable by design. The observer read the peer id out of the heartbeat payload (`FDHB|<id>`) rather than authenticating it. A Byzantine agent could broadcast `FDHB|target-0` while the real `target-0` was down and keep it looking alive indefinitely. The base scenario ran with zero Byzantine agents, so this never fired, but the whole point of the framework is to prove a protocol holds up under attack, and the shipped accuracy validator only discriminated a naive implementation (the fixed-timeout foil), not an attacker.
2. The accuracy validator hardcoded `_MAX_PLAUSIBLE_GAP = 22.0`, which is the scenario's `hb_max` (20) plus a margin. Re-running the scenario with a different heartbeat bound would silently invalidate the threshold.

This PR closes both, and it does so with the same discriminator discipline the original used: a validator that fails against the weak implementation and passes against the hardened one.

## Persona

The keep-alive attack is the failure a real detector has to survive, so authentication belongs in the heartbeat path, not in an appendix. The design question is where to put it. A failure detector should stay a pure liveness oracle over observations it can trust, so I put verification in the observing agent and left the `FailureDetector` contract untouched. That mirrors how the rest of the stack works: the identity layer is already right there, and the gossip and HotStuff scenarios already sign their writes with it, so a heartbeat is just one more signed message. I used the existing `did_key` identity plugin rather than inventing anything.

## What is in the diff

| Path | Purpose |
|---|---|
| `.../scenarios_builtin/failure_detection.py` | Heartbeats are now signed and timestamped: `FDHB|<id>|<ts>|<sig-hex>`. New public helpers `heartbeat_payload`, `claimed_peer`, and `verify_heartbeat` form the wire-and-verify surface. `verify_heartbeat` rejects anything that is not well formed, not signed by the claimed peer's key, from the future, or not strictly newer than the last accepted beat from that peer. A new `ByzantineForgerAgent` mounts the attack (fabricated beats signed with its own key, plus byte-exact replays of captured genuine beats). The observer runs in verifying mode by default and a trusting mode as the foil. The observer also emits one `fd:config` marker so the validators can read the scenario's heartbeat bound from the trace. |
| `scenarios/failure_detection_forgery.yaml` | The forgery scenario: observer, target, always-live peer, and one forger impersonating the target during its outage. Task type `failure_detection_forgery`, four agents, zero message drop, deterministic. |
| `packages/nest-core/nest_core/validators.py` | New `validate_failure_detection_no_forged_liveness`: for every outage segment during which forged beats arrived (a received `FDHB|` whose claimed id differs from the transport sender), the victim must still be suspected at the last in-segment status update. Plus `_fd_max_plausible_gap`, which derives the accuracy bound from the `fd:config` marker (`hb_max + margin`) with the old constant kept only as a fallback for pre-marker traces. |
| `packages/nest-core/nest_core/scenarios.py` | Registers the `failure_detection_forgery` task type (same factory, different roles). |
| `packages/nest-plugins-reference/tests/test_failure_detection.py` | Twelve new tests: signed-heartbeat unit tests (authentic accepted; fabricated, replayed, future-dated, and malformed rejected), the three-seed forgery integration pass, the trusting-vs-verifying discriminator, the config-derived accuracy bound, and forgery-scenario determinism. |
| `packages/nest-core/tests/test_validators.py` | Adds `failure_detection_forgery` to the registry-completeness assertion. |

The base `failure_detection` 

…

Try it

Open PR on GitHubView diff

Checkout locally

git fetch origin pull/152/head:pr-152
git checkout pr-152