Skip to content

fix: F-2026-18827 | [Dual Defense] Normal EVM Outbound Resolution Uses Current TSS Address After Key Rotation - #314

Open
Aman035 wants to merge 1 commit into
audit-fixesfrom
F-2026-18827
Open

fix: F-2026-18827 | [Dual Defense] Normal EVM Outbound Resolution Uses Current TSS Address After Key Rotation#314
Aman035 wants to merge 1 commit into
audit-fixesfrom
F-2026-18827

Conversation

@Aman035

@Aman035 Aman035 commented Aug 19, 2026

Copy link
Copy Markdown
Member

F-2026-18827 Outbound nonce checked against the current TSS after rotation

Issue

  • Nonces are per-EOA. Outbound SigningData carried no key id, so outboundSigner fell back to getTSSAddress() — the live TSS.
  • After a K1→K2 rotation, a still-BROADCASTED K1 outbound gets its nonce compared against K2's unrelated sequence. K2 has advanced → NonceConsumed → failure vote → remint, while the K1 tx can still mine. Double settlement.
  • Fund migration already did this correctly, binding OldTssPubkey. The asymmetry was documented in txresolver/evm.go rather than fixed.

Approach taken

  • Added txflow.RecoverOutboundSigner: ecrecover the signer from the persisted signature + signing hash. Resolver and broadcaster both use it instead of the live TSS address.
  • Recovered rather than persisted, deliberately. The recommendation was to store the pubkey at sign time, but that only populates events signed after deploy — and the at-risk population is exactly the old-key rows already sitting in BROADCASTED when the rotation happens. Recovery covers them with no schema change or migration.
  • Viable because the stored signature is already 65 bytes [r|s|v] — EVM broadcast rejects anything else, and SVM reads signature[64] as the recovery id.
  • Unrecoverable signer → defer, never a failure vote.
  • Removed the now-dead GetTSSAddress config from both packages, so the wrong address is no longer reachable from these paths.

Tests

  • txflow: recovers the signing address, two keys recover to different addresses, and every malformed payload reports failure rather than a wrong address.
  • Regression: signed under K1, live TSS is K2 with its nonce advanced to 42, K1's nonce 5 still free → asserts the nonce is read from K1 and not K2, and the event rewinds to SIGNED instead of fail-voting.
  • Existing fixtures now carry real signatures. They previously used zero-filled bytes, which is why the wrong-key path went unnoticed.
  • Mutation checked: unbinding recovery from the signing hash fails the nonce tests.

Not implemented

Rec 3 (block KEYGEN while old-key rows are pending) is chain-side — rotation is initiated in x/utss, not here.

@Aman035

Aman035 commented Aug 19, 2026

Copy link
Copy Markdown
Member Author

@0xNilesh KEYGEN/rotation: require outbound disabled and no pending/SIGNED/BROADCASTED old-key rows (or refuse current-key flip until drained).

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.

1 participant