# Signatures use only tx ID instead of entire digest

Connext fulfill/cancel signature checks signed only the transaction ID plus relayer fee, allowing replay of older signatures with different txData sharing the same tx ID.

- Payout: Audit contest
- Severity: medium
- Program: Connext
- Found by: cmichel
- Disclosed: 2021-08-30
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2021-07-connext-findings/issues/54
- Library page: https://bugbountylibrary.com/r/c4%3A2021-07-connext%3AM-01
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:business-logic vuln:signature-replay

**Target:** Connext cross-chain transaction signature verification (recoverFulfillSignature/recoverCancelSignature)
**Root cause:** Signature digest covers only transactionId and relayer fee rather than hashInvariantTransactionData(txData), so a signature is not bound to the full transaction data and can be replayed against different txData sharing the same tx ID.
**Steps:**
1. Read `recoverFulfillSignature()` and `recoverCancelSignature()`; observe the signed digest contains only tx ID + relayer fee, not `hashInvariantTransactionData(txData)`.
2. Note the L306 comment indicating the signature should be over the full invariant digest.
3. Reuse a prior signature with an older relayer fee against a transaction with the same tx ID but different txData (preparedBlockNumber > 0).
4. Observe the signature/address check passes and the older signature is replayed.
**How found:** Code review of the signature recovery functions, comparing the actual signed digest against the intended digest described in the L306 comment and the open question noted at L12.
**Exploitation:** An attacker or user replays an older signature (with older relayer fee) against a transaction sharing the same transactionId but different txData, since the digest does not bind the full transaction data.
**Insight:** When a signature is meant to authorize a struct, verify the signed digest actually hashes the full struct, not just an ID field; comments describing intended digest are a good diff target against implementation.

