Missing signature expiration in EIP-712 mint authorization enables perpetual replay attacks
EIP-712 mint authorisations have no deadline. Once issued, a signature is valid indefinitely until consumed, so revoking an outstanding authorisation is not possible.
Description
The GenesisLicense contract implements EIP-712 signature-based authorization for minting NFTs, but lacks any form of signature expiration mechanism. Once a signature is created, it remains valid indefinitely until used, creating a significant security vulnerability.
In the current implementation, signatures only include the recipient address, token value, and staking pool address:
bytes32 structHash = keccak256(abi.encode(MINT_INFO_TYPEHASH, to_, value_, stakingPool_));
Without an expiration timestamp or block number, these signatures cannot be invalidated except by using them. This creates several security risks:
- Perpetual validity: Signatures remain valid indefinitely, even if protocol conditions change.
- Protocol parameter bypass: Old signatures could authorize mints under conditions that are no longer acceptable.
Impact
If a signing key is rotated or revoked, all previously-issued signatures remain valid. An adversary who obtains an old signature can mint at any time in the future under whatever conditions were valid when it was signed.
Recommendation
- Add expiration parameter. Modify the EIP-712 signature structure to include an expiration timestamp:
// Update typehashbytes32 internal constant MINT_INFO_TYPEHASH = keccak256("Mint(address to,uint256 value,address stakingPool,uint256 deadline)");function mint(address to_,uint256 value_,address stakingPool_,uint256 deadline_,bytes calldata signature_) external nonReentrant {require(block.timestamp <= deadline_, "Signature expired");bytes32 structHash = keccak256(abi.encode(MINT_INFO_TYPEHASH,to_,value_,stakingPool_,deadline_));// verification and minting logic}
- Implement nonce system. As an alternative or additional protection, implement a nonce system where each address has an incrementing nonce that must be included in the signature:
mapping(address => uint256) public nonces;bytes32 structHash = keccak256(abi.encode(MINT_INFO_TYPEHASH,to_,value_,stakingPool_,nonces[to_]));// Increment nonce after usenonces[to_]++;

