From 6439fb4a1d519cc91d0860cc041f7301129c0e73 Mon Sep 17 00:00:00 2001 From: Jiatu Date: Thu, 10 Oct 2024 20:36:50 +0800 Subject: [PATCH 1/7] Fix Safe wallet signature verification --- siwe/siwe.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/siwe/siwe.py b/siwe/siwe.py index c4afab5..31ea8de 100644 --- a/siwe/siwe.py +++ b/siwe/siwe.py @@ -21,7 +21,7 @@ from pydantic_core import core_schema from typing_extensions import Annotated from web3 import HTTPProvider, Web3 -from web3.exceptions import BadFunctionCallOutput +from web3.exceptions import BadFunctionCallOutput, ContractLogicError from .parsed import ABNFParsedMessage, RegExpParsedMessage @@ -327,7 +327,7 @@ def verify( try: address = w3.eth.account.recover_message(message, signature=signature) - except ValueError: + except (ValueError, IndexError): address = None except eth_utils.exceptions.ValidationError: raise InvalidSignature from None @@ -355,7 +355,11 @@ def check_contract_wallet_signature( contract = w3.eth.contract(address=address, abi=EIP1271_CONTRACT_ABI) hash_ = _hash_eip191_message(message) try: - response = contract.caller.isValidSignature(hash_, bytes.fromhex(signature[2:])) + # Signatures returned from Safe wallets are always "0x" and should be + # passed in as-is. + response = contract.caller.isValidSignature( + hash_, signature if signature == "0x" else bytes.fromhex(signature[2:]) + ) return response.hex() == EIP1271_MAGICVALUE - except BadFunctionCallOutput: + except (BadFunctionCallOutput, ContractLogicError): return False From 7f9507bf24915bb35eb0d792960df513b37e2058 Mon Sep 17 00:00:00 2001 From: Jiatu Date: Fri, 11 Oct 2024 20:07:02 +0800 Subject: [PATCH 2/7] Update comment --- siwe/siwe.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/siwe/siwe.py b/siwe/siwe.py index 31ea8de..263e528 100644 --- a/siwe/siwe.py +++ b/siwe/siwe.py @@ -355,8 +355,8 @@ def check_contract_wallet_signature( contract = w3.eth.contract(address=address, abi=EIP1271_CONTRACT_ABI) hash_ = _hash_eip191_message(message) try: - # Signatures returned from Safe wallets are always "0x" and should be - # passed in as-is. + # For message hashes stored on-chain for Safe wallets, the signatures + # are always "0x" and should be passed in as-is. response = contract.caller.isValidSignature( hash_, signature if signature == "0x" else bytes.fromhex(signature[2:]) ) From fec0085a6cc05f717ddda37d855bdfd173d4f460 Mon Sep 17 00:00:00 2001 From: Jiatu Date: Fri, 11 Oct 2024 21:53:27 +0800 Subject: [PATCH 3/7] Add Safe test case --- tests/test_siwe.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/test_siwe.py b/tests/test_siwe.py index 10536f6..21eb137 100644 --- a/tests/test_siwe.py +++ b/tests/test_siwe.py @@ -94,6 +94,13 @@ def test_eip1271_message(self, test_name, test): siwe_message = SiweMessage.from_message(message=test["message"]) siwe_message.verify(test["signature"], provider=provider) + def test_safe_wallet_message(self): + message = "localhost:3000 wants you to sign in with your Ethereum account:\n0x54D97AEa047838CAC7A9C3e452951647f12a440c\n\nPlease sign in to verify your ownership of this wallet\n\nURI: http://localhost:3000\nVersion: 1\nChain ID: 11155111\nNonce: gDj8rv7VVxN\nIssued At: 2024-10-10T08:34:03.152Z\nExpiration Time: 2024-10-13T08:34:03.249112Z" + signature = "0x" + provider = HTTPProvider(endpoint_uri=endpoint_uri) + siwe_message = SiweMessage.from_message(message=message) + siwe_message.verify(signature, provider=provider) + @pytest.mark.parametrize( "provider", [HTTPProvider(endpoint_uri=endpoint_uri), None] ) From bb72e28dab246ad309b25cae9792b27c538dc53b Mon Sep 17 00:00:00 2001 From: Jiatu Date: Fri, 11 Oct 2024 21:54:37 +0800 Subject: [PATCH 4/7] Bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ed05d05..27aaf2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "siwe" -version = "4.3.0" +version = "4.3.1" description = "A Python implementation of Sign-In with Ethereum (EIP-4361)." license = "MIT OR Apache-2.0" authors = [ From a0fa5f987c5bacf0c1d5ff94c6853d3e4e992922 Mon Sep 17 00:00:00 2001 From: Jiatu Date: Fri, 11 Oct 2024 22:00:12 +0800 Subject: [PATCH 5/7] Update RPC node URL --- tests/test_siwe.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_siwe.py b/tests/test_siwe.py index 21eb137..3cb1aa8 100644 --- a/tests/test_siwe.py +++ b/tests/test_siwe.py @@ -97,7 +97,9 @@ def test_eip1271_message(self, test_name, test): def test_safe_wallet_message(self): message = "localhost:3000 wants you to sign in with your Ethereum account:\n0x54D97AEa047838CAC7A9C3e452951647f12a440c\n\nPlease sign in to verify your ownership of this wallet\n\nURI: http://localhost:3000\nVersion: 1\nChain ID: 11155111\nNonce: gDj8rv7VVxN\nIssued At: 2024-10-10T08:34:03.152Z\nExpiration Time: 2024-10-13T08:34:03.249112Z" signature = "0x" - provider = HTTPProvider(endpoint_uri=endpoint_uri) + # Use a Sepolia RPC node since the signature is generated on Sepolia testnet + # instead of mainnet like other EIP-1271 tests. + provider = HTTPProvider(endpoint_uri="https://rpc.sepolia.org") siwe_message = SiweMessage.from_message(message=message) siwe_message.verify(signature, provider=provider) From 519d1a28a6932632ab8b97597bd7634ada6b03db Mon Sep 17 00:00:00 2001 From: Simon Bihel Date: Fri, 11 Oct 2024 16:22:30 +0100 Subject: [PATCH 6/7] Bump minor version instead --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 27aaf2e..a628e56 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "siwe" -version = "4.3.1" +version = "4.4.0" description = "A Python implementation of Sign-In with Ethereum (EIP-4361)." license = "MIT OR Apache-2.0" authors = [ From 7929abbcb1368f50ee7fabd12f7059a3942d73de Mon Sep 17 00:00:00 2001 From: Simon Bihel Date: Fri, 11 Oct 2024 16:23:06 +0100 Subject: [PATCH 7/7] Better handling for test node endpoints --- tests/test_siwe.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/test_siwe.py b/tests/test_siwe.py index 3cb1aa8..df3cbd4 100644 --- a/tests/test_siwe.py +++ b/tests/test_siwe.py @@ -23,10 +23,20 @@ with open(BASE_TESTS + "eip1271.json", "r") as f: verification_eip1271 = decamelize(json.load(fp=f)) +endpoint_uri = "https://cloudflare-eth.com" try: - endpoint_uri = os.environ["WEB3_PROVIDER_URI"] + uri = os.environ["WEB3_PROVIDER_URI"] + if uri != "": + endpoint_uri = uri except KeyError: - endpoint_uri = "https://cloudflare-eth.com" + pass +sepolia_endpoint_uri = "https://rpc.sepolia.org" +try: + uri = os.environ["WEB3_PROVIDER_URI_SEPOLIA"] + if uri != "": + sepolia_endpoint_uri = uri +except KeyError: + pass class TestMessageParsing: @@ -99,7 +109,7 @@ def test_safe_wallet_message(self): signature = "0x" # Use a Sepolia RPC node since the signature is generated on Sepolia testnet # instead of mainnet like other EIP-1271 tests. - provider = HTTPProvider(endpoint_uri="https://rpc.sepolia.org") + provider = HTTPProvider(endpoint_uri=sepolia_endpoint_uri) siwe_message = SiweMessage.from_message(message=message) siwe_message.verify(signature, provider=provider)