Melted Carrot Swan
Medium
Let’s note that CLAIM_TYPEHASH uses two arrays address[] projectTokenProxyWallets, uint256[] tokenAmountsToClaim.
When validating a signature, these values are encoded as follows.
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
CLAIM_TYPEHASH,
_params.kycAddress,
_params.projectTokenAddress,
_params.projectTokenProxyWallets, //@audit this
_params.tokenAmountsToClaim, //@audit this
_params.nonce,
_params.deadline
)
)
)
);
Now let's look at the EIP-712 specification and how it specifies to handle arrays.
The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents (i.e. the encoding of SomeType[5] is identical to that of a struct containing five members of type SomeType).
It can be seen that the protocol incorrectly handles signature validation arrays, and therefore does not conform to the EIP-712 standard, which will cause signatures generated using libraries that conform to the standard to be invalid.
The code does not handle arrya correctly when creating a digest. It simply inserts arrays into abi.encode as if they were atomic types, but this is incorrect. Let's look at how EIP-712 spec requires non atomic type to be handled.
Link to this part of EIP-712 - Definition of encodeData
1)Dynamic Values
The dynamic values bytes and string are encoded as a keccak256 hash of their contents.
- Array Values
The array values are encoded as the keccak256 hash of the concatenated encodeData of their contents
There is no specific attack path, any library that creates signatures according to the EIP-712 specification will not pass validation in the contract due to incorrect validation.
2 impacts
- due to wrong type hash computation leading to wrong digest validation in the signature validator, the signatures might fail.
- breaking the EIP712 mentioned in
readme
where it strictly complains. The array types should not be used as atomic types
No response
Hash both of the arrays like this
function hashArray(uint256[] memory array) internal returns (bytes32) {
bytes32[] memory hashedElements;
for (uint256 i = 0; i < array.length; i++) {
hashedElements[i] = keccak256(abi.encodePacked(array[i]));
}
return keccak256(abi.encodePacked(hashedElements));
}