Skip to content

add subnet replay protection by binding subnet id to client messages - #7

Open
noot wants to merge 2 commits into
mainfrom
noot/subnet-replay-protection
Open

noot wants to merge 2 commits into
mainfrom
noot/subnet-replay-protection

Conversation

@noot

@noot noot commented Oct 23, 2026

Copy link
Copy Markdown

Every subnet has the same relays and the same setup seed, so a client's signed round messages were accepted on any subnet. A relay leading one subnet could replay a client's message into another subnet in the same round. Only that client would then appear in both subnets' client sets, which links the client to its payload. Since each relay is on every subet, it takes only one evil relay to deanonymize a member.

Panetiere and Scheduled Panetiere

  • The session ID now includes the subnet, and every client signature covers the session ID.
  • Relays check the client's post signature before admitting the client, so a replayed or forged post can't take an admission slot.

ADCNet and Scheduled ADCNet

  • Each client message carries a second signature over the subnet and a hash of the message. The leader, the aggregators and the Scheduled ADCNet relays check it.
  • In single-round ADCNet, the blinding keys are derived per subnet. A contribution forwarded by a dishonest aggregator therefore won't decode on another subnet. Scheduled ADCNet doesn't get this yet because it needs a change in adcnet-rs.

Tests

  • Each check has unit tests showing that a message from another subnet is rejected and the same message is accepted on its own subnet.
  • An end-to-end test has a relay forward contributions between two subnets' leaders, and checks that the payload is delivered only once.

Breaking change: the wire format and signatures change, so all nodes and the mobile app must upgrade together.

noot added 2 commits October 22, 2026 17:59
All subnets share the same relays and setup seed, so a client's signed
round messages were valid on every subnet. A relay leading one subnet
could replay a client's round into another subnet in the same round and
link the client to its payload through the two client sets.

The session id is now derived from a per-subnet scope, and every client
signature covers the session id. Relays verify the client's post
signature before admitting it, so a replayed or forged post can no
longer take an admission slot.
Client contributions now carry a signature over the subnet and a digest
of the message, checked by the leader, aggregators, and scheduled
relays. Single-round ADCNet also derives its blinding keys per subnet,
so a contribution forwarded by a dishonest aggregator still does not
decode on another subnet.

Adds an end-to-end test where a relay forwards contributions between
two subnets' leaders and the payload must be delivered only once.
@noot
noot requested a balanced review from Copilot October 23, 2026 18:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Aggregated-round replay remains possible through unscoped relay shares, and Panetiere still consumes admission capacity before validating inner posts.

2 open issues
What changed in this PR

This PR adds subnet-specific authentication to client messages across Panetiere and ADCNet to prevent cross-subnet replay. The wire-format and signature changes require a coordinated upgrade.

Changes:

  • Bind Panetiere sessions and client signatures to the subnet.
  • Add subnet-binding signatures to ADCNet client messages and per-subnet blinding keys to single-round ADCNet.
  • Update session construction and add replay-rejection tests.
File Description
crates/​anymone-core/​tests/​session_scheduledadcnet.rs Supply subnet IDs in session tests.
crates/​anymone-core/​tests/​session_adcnet.rs Update ADCNet test constructors.
crates/​anymone-core/​tests/​session_adcnet_aggregated.rs Update client, relay, and aggregator constructors.
crates/​anymone-core/​tests/​scheduler_core.rs Supply subnet IDs in scheduler fixtures.
crates/​anymone-core/​tests/​adcnet_cross_subnet_replay.rs Add an end-to-end contribution replay test.
crates/​anymone-core/​src/​remote_session.rs Configure subnet scope for remote clients.
crates/​anymone-core/​src/​panetiere.rs Scope session IDs and client signatures; add rejection coverage.
crates/​anymone-core/​src/​panetiere_scheduled.rs Apply subnet scope to scheduled messages and reservations.
crates/​anymone-core/​src/​adcnet.rs Add client bindings, scoped blinding keys, and unit tests.

Review effort: Balanced

/// Blinding pads derive from the relays' ECDH secrets, which every subnet
/// shares; keying them per subnet keeps a contribution that is replayed into
/// another subnet from unblinding there.
fn subnet_shared_key(shared: &SharedKey, subnet: SubnetId) -> SharedKey {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#8

signer,
&signature,
&client_public_signing_bytes(round, client_id, &entry),
&client_public_signing_bytes(&sid, client_id, &entry),
@noot
noot marked this pull request as ready for review October 23, 2026 21:35
@noot
noot added this pull request to stack #9 October 26, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants