Signature Reuse Risk
In this page, we cover the threat of reusing the paymaster’s signature.
Cross-chain signature replay attack possibility
Like in a VerifyingPaymaster
, when the paymaster includes logic to verify signatures, there are cases where it generates a separate hash using the getHash
function instead of using the userOpHash
passed as a parameter. If the getHash
function in the paymaster does not include the chainId
, there is a risk of the paymaster’s signature being reused across different chains.
**The userOpHash is generated by the Entrypoint and includes the chainId.
Example Code:
src/vulnerableVerifyingPaymaster.sol
function getHash(UserOperation calldata userOp)
public view returns (bytes32) { // @audit change to view
//can't use userOp.hash(), since it contains also the paymasterAndData itself.
return keccak256(abi.encode(
userOp.getSender(),
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas
));
}
Mitigation : Include the
chainId
in thegetHash
function, or use theuserOpHash
passed as a parameter.
src/goodVerifyingPaymaster.sol
function getHash(UserOperation calldata userOp)
public view returns (bytes32) { // @audit change to view
//can't use userOp.hash(), since it contains also the paymasterAndData itself.
return keccak256(abi.encode(
userOp.getSender(),
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas,
+ block.chainid // @audit add chain id
));
}
Signature without Nonce.
Like in a VerifyingPaymaster
, when the paymaster includes logic to verify signatures, there are cases where it generates a separate hash using the getHash
function instead of using the userOpHash
passed as a parameter. If the getHash
function in the paymaster does not include the nonce
, there is a risk of the paymaster’s signature being reused.
** The userOpHash is generated by the Entrypoint and includes the nonce of UserOperation.
If the paymaster’s signature is reused, users could repeatedly submit the same UserOperation, which may drain the paymaster’s funds.
Exmaple Code:
src/vurnerableVerifyingPaymaster.sol
function getHash(UserOperation calldata userOp)
public view returns (bytes32) { // @audit change to view
//can't use userOp.hash(), since it contains also the paymasterAndData itself.
return keccak256(abi.encode(
userOp.getSender(),
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas,
block.chainid
));
}
Mitigation : Include the
nonce
in thegetHash
function, or use theuserOpHash
passed as a parameter.
src/goodVerifyingPaymaster.sol
function getHash(UserOperation calldata userOp)
public view returns (bytes32) { // @audit change to view
//can't use userOp.hash(), since it contains also the paymasterAndData itself.
return keccak256(abi.encode(
userOp.getSender(),
+ userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas,
block.chainid
));
}
Last updated