_assertValidSignature
is not compatible with EIP-1271
#176
Labels
bug
Something isn't working
disagree with severity
Sponsor confirms validity, but disagrees with warden’s risk assessment (sponsor explain in comments)
duplicate
This issue or pull request already exists
QA (Quality Assurance)
Assets are not at risk. State handling, function incorrect as to spec, issues with clarity, syntax
sponsor confirmed
Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity")
Lines of code
https://github.com/code-423n4/2022-05-opensea-seaport/blob/main/contracts/lib/SignatureVerification.sol#L78
https://github.com/code-423n4/2022-05-opensea-seaport/blob/main/contracts/lib/SignatureVerification.soll#L91
https://github.com/code-423n4/2022-05-opensea-seaport/blob/main/contracts/lib/SignatureVerification.sol#L94
Vulnerability details
Impact
TLDR: Seaport signature validation is not 100% compatible with Gnosis Safe.
The way this function is structured can prevent valid EIP-1271 signatures from validating.
EIP-1271 doesn't mandate any kind of structure, but leaves it completely open for the future. It is ought to be forward-compatible with any kind of signature scheme, but is also utilised by contract wallets (such as Gnosis, Argent, etc.)
There are two different cases of errors here:
if (v != 27 && v != 28)
in SignatureVerification.sol#L78ecrecover(...)
in SignatureVerification.sol#L91Imagine a 64 or 65 byte long signature scheme which fails on one of these conditions, but would be accepted by the validating contract.
Case 1
The first case is curious, because it assumes that no EIP-1271 compatible scheme uses 65-bytes with special values for
v
. This assumptions is wrong, because Gnosis Safe has three special cases (v=0
,v=1
,v>30
) potentially affected by this.In the v=1 case the signature format is 65-bytes (same order as here), where
r={address}
,s={dirty}
,v=1
and it will look up an internal map for hashes against the address. The same problem applies to the v>30 case. Likely it doesn't to the v=0 case.The Safe code cannot be reached because of this revert here.
P.S. It is not well known Safe's support this, because it is hidden in a helper since 1.3.0, but confirmed by the authors.
Case 2
The second case is more about forward compatibility.
ecrecover
has certain restrictions, and the cases it returns 0 are:r
ands
must not exceed 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141v
must be 27 or 28If we craft a 65-byte scheme, the following hex string represents the maximum number of bits which can be set, before triggering a failure:
fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641411b
fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd03641411c
A future signature scheme could break this assumption, and the fallback-to-1271 branch cannot be entered due to the
if (recoveredSigner == address(0))
check at SignatureVerification.sol#L94.Proof of Concept
Context: SignatureVerification.sol#L34
Tools Used
Manual review
Recommended Mitigation Steps
extcodesize
check and performing EIP-1271 validation, otherwise falling back toecrecover
.ecrecover
failure/mismatch.The text was updated successfully, but these errors were encountered: