The `SettlementSignatureVerifier::verifyECDSA()` function fails to ensure the uniqueness of signatures, allowing an attacker to repeatedly use the same valid signature to bypass the `required_validators` threshold and send unauthorized cross-chain messages.
Audit: 2024-08-chakra-findings
Severity: HIGH

# Lines of code

https://github.com/code-423n4/2024-08-chakra/blob/abef77d95866f2fec93270491fc5abc9ab14f86d/solidity/settlement/contracts/SettlementSignatureVerifier.sol#L193-L214


# Vulnerability details

The `ChakraSettlement::receive_cross_chain_msg()` function processes cross-chain messages and relies on the `SettlementSignatureVerifier::verify()` function to validate these messages. The `SettlementSignatureVerifier::verifyECDSA()` function, which is invoked by `verify()`, checks if each `signature` in the provided signatures array is signed by a recognized validator. However, it does not ensure that each signature is unique. The function iterates through the `signatures` array, and once the number of valid signatures meets the `required_validators` threshold, the function returns `true`.

This implementation flaw allows an attacker to reuse the same valid signature multiple times within the `signatures` array to meet the `required_validators` condition. This effectively renders the required_validators parameter meaningless, as the security of the cross-chain message verification process is compromised. The issue is exacerbated by the fact that the `ISettlementHandler(to_handler).receive_cross_chain_msg()` function does not perform additional checks on the message parameters and executes the contents of the `payload` directly.
```js
    // ChakraSettlement::receive_cross_chain_msg()
    function receive_cross_chain_msg(
        uint256 txid,
        string memory from_chain,
        uint256 from_address,
        uint256 from_handler,
        address to_handler,
        PayloadType payload_type,
        bytes calldata payload,
        uint8 sign_type, // validators signature type /  multisig or bls sr25519
        bytes calldata signatures // signature array
    ) external {
        {
            // verify signature
            bytes32 message_hash = keccak256(
                abi.encodePacked(
                    txid,
                    from_chain,
                    from_address,
                    from_handler,
                    to_handler,
                    keccak256(payload)
                )
            );


            require(
@>                signature_verifier.verify(message_hash, signatures, sign_type),
                "Invalid signature"
            );


            require(
                receive_cross_txs[txid].status == CrossChainMsgStatus.Unknow,
                "Invalid transaction status"
            );
        }


        receive_cross_txs[txid] = ReceivedCrossChainTx(
            txid,
            from_chain,
            contract_chain_name,
            from_address,
            from_handler,
            address(this),
            payload,
            CrossChainMsgStatus.Pending
        );


@>        bool result = ISettlementHandler(to_handler).receive_cross_chain_msg(
            txid,
            from_chain,
            from_address,
            from_handler,
            payload_type,
            payload,