-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tooling to verify signatures with support for ERC1271 (#2532)
Co-authored-by: Francisco Giordano <[email protected]>
- Loading branch information
Showing
7 changed files
with
149 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
/** | ||
* @dev Interface of the ERC1271 standard signature validation method for | ||
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. | ||
*/ | ||
interface IERC1271 { | ||
/** | ||
* @dev Should return whether the signature provided is valid for the provided data | ||
* @param hash Hash of the data to be signed | ||
* @param signature Signature byte array associated with _data | ||
*/ | ||
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../access/Ownable.sol"; | ||
import "../interfaces/IERC1271.sol"; | ||
import "../utils/cryptography/ECDSA.sol"; | ||
|
||
contract ERC1271WalletMock is Ownable, IERC1271 { | ||
constructor(address originalOwner) { | ||
transferOwnership(originalOwner); | ||
} | ||
|
||
function isValidSignature(bytes32 hash, bytes memory signature) public view override returns (bytes4 magicValue) { | ||
return ECDSA.recover(hash, signature) == owner() ? this.isValidSignature.selector : bytes4(0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "../utils/cryptography/SignatureChecker.sol"; | ||
|
||
contract SignatureCheckerMock { | ||
using SignatureChecker for address; | ||
|
||
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) public view returns (bool) { | ||
return signer.isValidSignatureNow(hash, signature); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.0; | ||
|
||
import "./ECDSA.sol"; | ||
import "../Address.sol"; | ||
import "../../interfaces/IERC1271.sol"; | ||
|
||
/** | ||
* @dev Signature verification helper: Provide a single mechanism to verify both private-key (EOA) ECDSA signature and | ||
* ERC1271 contract sigantures. Using this instead of ECDSA.recover in your contract will make them compatible with | ||
* smart contract wallets such as Argent and Gnosis. | ||
* | ||
* Note: unlike ECDSA signatures, contract signature's are revocable, and the outcome of this function can thus change | ||
* through time. It could return true at block N and false at block N+1 (or the opposite). | ||
*/ | ||
library SignatureChecker { | ||
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { | ||
if (Address.isContract(signer)) { | ||
try IERC1271(signer).isValidSignature(hash, signature) returns (bytes4 magicValue) { | ||
return magicValue == IERC1271(signer).isValidSignature.selector; | ||
} catch { | ||
return false; | ||
} | ||
} else { | ||
return ECDSA.recover(hash, signature) == signer; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
const { toEthSignedMessageHash, fixSignature } = require('../../helpers/sign'); | ||
|
||
const { expect } = require('chai'); | ||
|
||
const SignatureCheckerMock = artifacts.require('SignatureCheckerMock'); | ||
const ERC1271WalletMock = artifacts.require('ERC1271WalletMock'); | ||
|
||
const TEST_MESSAGE = web3.utils.sha3('OpenZeppelin'); | ||
const WRONG_MESSAGE = web3.utils.sha3('Nope'); | ||
|
||
contract('SignatureChecker (ERC1271)', function (accounts) { | ||
const [signer, other] = accounts; | ||
|
||
before('deploying', async function () { | ||
this.signaturechecker = await SignatureCheckerMock.new(); | ||
this.wallet = await ERC1271WalletMock.new(signer); | ||
this.signature = fixSignature(await web3.eth.sign(TEST_MESSAGE, signer)); | ||
}); | ||
|
||
context('EOA account', function () { | ||
it('with matching signer and signature', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
signer, | ||
toEthSignedMessageHash(TEST_MESSAGE), | ||
this.signature, | ||
)).to.equal(true); | ||
}); | ||
|
||
it('with invalid signer', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
other, | ||
toEthSignedMessageHash(TEST_MESSAGE), | ||
this.signature, | ||
)).to.equal(false); | ||
}); | ||
|
||
it('with invalid signature', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
signer, | ||
toEthSignedMessageHash(WRONG_MESSAGE), | ||
this.signature, | ||
)).to.equal(false); | ||
}); | ||
}); | ||
|
||
context('ERC1271 wallet', function () { | ||
it('with matching signer and signature', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
this.wallet.address, | ||
toEthSignedMessageHash(TEST_MESSAGE), | ||
this.signature, | ||
)).to.equal(true); | ||
}); | ||
|
||
it('with invalid signer', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
this.signaturechecker.address, | ||
toEthSignedMessageHash(TEST_MESSAGE), | ||
this.signature, | ||
)).to.equal(false); | ||
}); | ||
|
||
it('with invalid signature', async function () { | ||
expect(await this.signaturechecker.isValidSignatureNow( | ||
this.wallet.address, | ||
toEthSignedMessageHash(WRONG_MESSAGE), | ||
this.signature, | ||
)).to.equal(false); | ||
}); | ||
}); | ||
}); |