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.nonce
will never be repeated across the Editions - Use
abi.encodePacked(editionId,edition.nonce, block.chainid)
instead ofabi.encodePacked(editionId + edition.nonce, block.chainid)