Status: Accepted
Date: 2026-04-14
Commit: 86e2f2b
The initial aggregation subsystem (ADR 020) landed as a working multi-party protocol: cohort formation, update collection, aggregation, validation, MuSig2 signing. Getting it to pass happy-path scenarios came first.
The next step was identifying and mitigating the adversarial-scenario classes a real deployment would face: malicious participants, confused service operators, replay attacks, resource-exhaustion by unbounded message sizes, stale cohorts hanging open forever, etc. This ADR captures the named threat classes the hardening commit addressed and the mitigations chosen.
The regression-test suite at packages/method/tests/aggregation-security.spec.ts pins mitigations for each of the following:
Mitigation: Service state tracks accepted participants; re-opt-ins are idempotent: the cohort key for that DID remains the originally accepted one.
SUBMIT_UPDATE with arbitrarily large payload, forcing the service to canonicalize, hash, and aggregate a megabyte-scale object. DoS.Mitigation: maxUpdateSizeBytes enforced in AggregationService.#handleSubmitUpdate before any expensive processing. Oversized updates are dropped with a diagnostic.
Mitigation: AGGREGATION_WIRE_VERSION = 1 is checked on every message at ingestion. Mismatched messages are rejected, not silently processed as a possibly-incompatible shape.
Failed cleanly, not partially complete.Mitigation: Cohort TTL causes timed cohorts to transition to Failed and emit cohort-failed on the runner. A single validation-ack with approved: false transitions the phase to Failed on both sides.
Mitigation:
BeaconSigningSession.generatePartialSignature() clears the stored secret nonce immediately after signing.generateFinalSignature() validates every partial signature against the expected per-key aggregation before combining; the first bad partial throws BAD_PARTIAL_SIG.addNonceContribution() and addPartialSignature() are phase-gated; out-of-phase calls throw.Adopt the mitigations above. Encode each as a regression test in aggregation-security.spec.ts. The test suite is the durable contract: if any mitigation regresses, a named test fails with the associated threat label.
Runner-side behavior: on every rejection, emit a diagnostic event (error, cohort-failed) with enough context to debug.
Positive
Negative
maxUpdateSizeBytes cap is a protocol parameter that interoperability-conscious implementations must agree on. Currently embedded in the service; a future protocol version may surface it.Explicitly not covered here
packages/method/tests/aggregation-security.spec.ts: durable regression contract.packages/method/src/core/aggregation/service.ts: most mitigations live here.packages/method/src/core/aggregation/signing-session.ts: MuSig2 hygiene.