Omniscia Tokemak Network Audit
Rewards Manual Review Findings
Rewards Manual Review Findings
REW-01M: Improper Cross Chain Replay Attack Protection
Type | Severity | Location |
---|---|---|
Language Specific | Minor | Rewards.sol:L45, L56-L63, L116-L139 |
Description:
The current domainSeparator
of the contract is initialized during the contract's constructor
and thus will reflect an incorrect separator if the blockchain the contract is deployed in forks, permitting cross-chain replay attacks. A protection measure is included in claim
but it is insufficient.
Example:
116function claim(117 Recipient calldata recipient,118 uint8 v,119 bytes32 r,120 bytes32 s // bytes calldata signature121) external {122 address signatureSigner = ecrecover(_hash(recipient), v, r, s);123 require(signatureSigner == rewardsSigner, "Invalid Signature");124 require(recipient.chainId == _getChainID(), "Invalid chainId");125 require(recipient.nonce > accountNonces[recipient.wallet], "Nonce Mismatch"); 126 require(recipient.wallet == msg.sender, "Sender wallet Mismatch");127
128 uint256 claimableAmount = recipient.amount.sub(claimedAmounts[recipient.wallet]);129
130 require(claimableAmount > 0, "Invalid claimable amount");131 require(tokeToken.balanceOf(address(this)) >= claimableAmount, "Insufficient Funds");132
133 accountNonces[recipient.wallet] = recipient.nonce;134 claimedAmounts[recipient.wallet] = claimedAmounts[recipient.wallet].add(claimableAmount);135
136 tokeToken.safeTransfer(recipient.wallet, claimableAmount);137
138 emit Claimed(recipient.nonce, recipient.wallet, claimableAmount);139}
Recommendation:
We advise the protection measure to not rely on the inputs of claim
and instead validate the chain ID that was initially stored during the contract's creation. This can be done by introducing a new variable that holds the said value. Additionally, the draft implementation by OpenZeppelin may also be utilized that is more comprehensive in how cross-chain signatures are handled.
Alleviation:
The Tokemak team stated that the argument based chain ID protection is a sufficient security measure. We should note that we do not believe it to be so given that it can be secured by immutable code, however, the attack vector is minimal if existent given that it requires a lot of rare conditions to materialize and thus we do not believe any action should be carried to remediate this finding.
REW-02M: Non-Standard Nonce System
Type | Severity | Location |
---|---|---|
Logical Fault | Minor | Rewards.sol:L125, L133 |
Description:
The nonce system utilized by the reward contract is non-standard in the sense that it does not sequentially increment a nonce and instead accepts any value greater than the last one which can cause issues if a very high value is specified.
Example:
116function claim(117 Recipient calldata recipient,118 uint8 v,119 bytes32 r,120 bytes32 s // bytes calldata signature121) external {122 address signatureSigner = ecrecover(_hash(recipient), v, r, s);123 require(signatureSigner == rewardsSigner, "Invalid Signature");124 require(recipient.chainId == _getChainID(), "Invalid chainId");125 require(recipient.nonce > accountNonces[recipient.wallet], "Nonce Mismatch"); 126 require(recipient.wallet == msg.sender, "Sender wallet Mismatch");127
128 uint256 claimableAmount = recipient.amount.sub(claimedAmounts[recipient.wallet]);129
130 require(claimableAmount > 0, "Invalid claimable amount");131 require(tokeToken.balanceOf(address(this)) >= claimableAmount, "Insufficient Funds");132
133 accountNonces[recipient.wallet] = recipient.nonce;134 claimedAmounts[recipient.wallet] = claimedAmounts[recipient.wallet].add(claimableAmount);135
136 tokeToken.safeTransfer(recipient.wallet, claimableAmount);137
138 emit Claimed(recipient.nonce, recipient.wallet, claimableAmount);139}
Recommendation:
We advise the code block to instead use a sequentially increasing nonce that is designed as being equal to the recipient.nonce
prior to being incremented, thus cementing a more standard and maintainable nonce system.
Alleviation:
The nonce system was dropped entirely as the system's method of signature formation disallows signatures to be replayed as the balance
utilized is not stored in the contract and is instead carried within the signature payload.
REW-03M: Signature Malleability
Type | Severity | Location |
---|---|---|
Logical Fault | Minor | Rewards.sol:L122 |
Description:
The claim
function utilizes a raw ecrecover
invocation instead of using a well-known package, such as ECDSA, thus permitting invalid values for both the s
and v
values.
Example:
116function claim(117 Recipient calldata recipient,118 uint8 v,119 bytes32 r,120 bytes32 s // bytes calldata signature121) external {122 address signatureSigner = ecrecover(_hash(recipient), v, r, s);123 require(signatureSigner == rewardsSigner, "Invalid Signature");124 require(recipient.chainId == _getChainID(), "Invalid chainId");125 require(recipient.nonce > accountNonces[recipient.wallet], "Nonce Mismatch"); 126 require(recipient.wallet == msg.sender, "Sender wallet Mismatch");127
128 uint256 claimableAmount = recipient.amount.sub(claimedAmounts[recipient.wallet]);129
130 require(claimableAmount > 0, "Invalid claimable amount");131 require(tokeToken.balanceOf(address(this)) >= claimableAmount, "Insufficient Funds");132
133 accountNonces[recipient.wallet] = recipient.nonce;134 claimedAmounts[recipient.wallet] = claimedAmounts[recipient.wallet].add(claimableAmount);135
136 tokeToken.safeTransfer(recipient.wallet, claimableAmount);137
138 emit Claimed(recipient.nonce, recipient.wallet, claimableAmount);139}
Recommendation:
We advise proper sanitization of those values by firstly ensuring s
is less-than-or-equal-to secp256k1n / 2
, or 0x7F..FF5D576E7357A4501DDFE92F46681B20A0
, and secondly ensuring that v
is equal to either 27
or 28
. Values outside of those ranges will cause signatures that can be replayed. The designation of this issue is minor
given that the nonce
system applied protects against replays, however, such signatures should be properly prohibited.
Alleviation:
The recover
function of the ECDSA OpenZeppelin library is now utilized properly enforcing the necessary checks against malleability.