galacticcouncil / galacticcouncil/hydration-node

pallet-signet: respond/respond_error/respond_bidirectional are unauthenticated and unvalidated

Open
#1,494 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
208
Forks
109
Avg merge
6d 3h
Merged PRs (30d)
3

Description

## Summary

The three response extrinsics in `pallet-signet` — `respond` (call_index 4), `respond_error` (5), and `respond_bidirectional` (6) — perform **no authorization, no pause check, and no validation**. Each only calls `ensure_signed(origin)` and then re-emits the caller's raw input as an event. Any signed account can emit arbitrary signing responses for any `request_id`.

Surfaced while adding integration-test coverage for the pallet (`integration-tests/src/signet.rs`, PR #1490).

## Details

`pallets/signet/src/lib.rs` (lines ~391–449):

- **No authority gate.** `sign`/`sign_bidirectional`, `set_config`, `pause`/`unpause`, and `withdraw_funds` are gated (signed requester or `UpdateOrigin`). The `respond*` calls are gated only by `ensure_signed` — there is no check that the responder is an authorized MPC signer / relayer.
- **No pause check.** `sign` and `sign_bidirectional` both `ensure!(!config.paused, Error::Paused)`. The `respond*` calls have no such check, so responses keep flowing even while the pallet is paused.
- **No request validation.** `request_id` is not checked against any stored pending request — arbitrary ids are accepted.
- **No signature verification.** The submitted `Signature` / `serialized_output` are emitted verbatim; nothing verifies they are well-formed or authentic.

`respond` also emits `request_ids.len()` events after only checking `request_ids.len() == signatures.len()` — unbounded-ish work is bounded by `MAX_BATCH_SIZE`, but it is callable by anyone.

## Impact

Depends on the off-chain trust model. If relayers, the MPC network, indexers, or dApps treat `SignatureResponded` / `SignatureError` / `RespondBidirectionalEvent` as authoritative:

- **Response spoofing** — anyone can emit a `RespondBidirectionalEvent` with a forged `serialized_output`/`signature` for a real `request_id`.
- **Griefing** — anyone can emit `SignatureError` for a live `request_id`, or flood spurious `SignatureResponded` events.

If consumers already independently verify signatures against the expected MPC key off-chain, on-chain impact is limited to event noise — but the pallet then provides no on-chain guarantee about who responded.

## Suggested direction

- Gate `respond*` on an authorized-responder origin/set (or `UpdateOrigin`), and/or verify the submitted signature against the configured signing key.
- Apply the same `paused` check used by `sign`/`sign_bidirectional`.
- Consider validating `request_id` against stored pending requests.

## Notes

- Not fixed here; PR #1490 only adds tests that assert current behavior (permissionless event emission) so a future fix has a baseline.
- `build_evm_tx`'s error paths (`DataTooLong`, `InvalidAddress`, `InvalidGasPrice`) are in a public helper, not an extrinsic, so they aren't reachable via dispatch and aren't covered.

Contributor guide

Open the contributing guide

Research direction

Read pallets/signet/src/lib.rs around lines 391–449 and compare respond, respond_error, and respond_bidirectional with the gated sign and pause paths. Review integration-tests/src/signet.rs and PR #1490 for the current behavior baseline. Done should include a decided authorization, pause, request-validation, and signature-verification policy, with tests covering the response extrinsics.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
authorization, blockchain, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.