Edition facet signature is replayable#
Medium Risk
Fixed
Fixed according to the recommendation.
In EditionFacet.sol, signature can be replayed in certain cases.
function _requireSignature(
EditionStorage.Edition storage edition,
uint256 editionId,
bytes calldata signature
) internal view {
require(
keccak256(
abi.encodePacked(editionId + edition.nonce, block.chainid)
).toEthSignedMessageHash().recover(signature) == edition.signer,
"Invalid signature"
);
}
editionId is incremented at each new edition. edition.nonce starts at zero for each new edition and can be incremented to invalidate a signature.
As the data used to make the hash is abi.encodePacked(editionId + edition.nonce, block.chainid), some combinations of editionId and edition.nonce will recover to the same signer.
For example editionId = 0 and edition.nonce = 1,
will be replayable if editionId = 1 and edition.nonce = 0, because the 0 + 1 == 1 + 0. So the signature could be replayed with the other combination.
A replay of the transaction on another Edition means someone could give themselves permission to mint illegitimately.
Recommendation#
I see two options:
- Use a global nonce instead of a per Edition nonce, so that
editionId + edition.noncewill never be repeated across the Editions - Use
abi.encodePacked(editionId,edition.nonce, block.chainid)instead ofabi.encodePacked(editionId + edition.nonce, block.chainid)