Skip to content

Commit

Permalink
Merge pull request #1011 from gurukamath/sig-recovery-checks
Browse files Browse the repository at this point in the history
Add checks to signature recovery
  • Loading branch information
gurukamath authored Sep 19, 2024
2 parents 2bb8498 + 05e7756 commit 10125b1
Show file tree
Hide file tree
Showing 38 changed files with 361 additions and 204 deletions.
37 changes: 21 additions & 16 deletions src/ethereum/arrow_glacier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import List, Optional, Set, Tuple, Union

from ethereum.base_types import Bytes0
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.ethash import dataset_size, generate_cache, hashimoto_light
Expand Down Expand Up @@ -417,6 +418,7 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock

sender_address = recover_sender(chain_id, tx)

if isinstance(tx, FeeMarketTransaction):
Expand Down Expand Up @@ -958,26 +960,29 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
try:
if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
r, s, tx.y_parity, signing_hash_2930(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
elif isinstance(tx, FeeMarketTransaction):
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
r, s, tx.y_parity, signing_hash_1559(tx)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_2930(tx)
)
elif isinstance(tx, FeeMarketTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_1559(tx)
)
except InvalidSignature as e:
raise InvalidBlock from e

return Address(keccak256(public_key)[12:32])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Implementation of the ECRECOVER precompiled contract.
"""
from ethereum.base_types import U256
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.utils.byte import left_pad_zero_bytes
Expand Down Expand Up @@ -52,7 +53,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - 27, message_hash)
except ValueError:
except InvalidSignature:
# unable to extract public key
return

Expand Down
33 changes: 19 additions & 14 deletions src/ethereum/berlin/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import List, Optional, Set, Tuple, Union

from ethereum.base_types import Bytes0
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.ethash import dataset_size, generate_cache, hashimoto_light
Expand Down Expand Up @@ -335,6 +336,7 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock

sender_address = recover_sender(chain_id, tx)

return sender_address
Expand Down Expand Up @@ -839,22 +841,25 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
try:
if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
r, s, tx.y_parity, signing_hash_2930(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_2930(tx)
)
except InvalidSignature as e:
raise InvalidBlock from e

return Address(keccak256(public_key)[12:32])

Expand Down
3 changes: 2 additions & 1 deletion src/ethereum/berlin/vm/precompiled_contracts/ecrecover.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Implementation of the ECRECOVER precompiled contract.
"""
from ethereum.base_types import U256
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.utils.byte import left_pad_zero_bytes
Expand Down Expand Up @@ -52,7 +53,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - 27, message_hash)
except ValueError:
except InvalidSignature:
# unable to extract public key
return

Expand Down
24 changes: 16 additions & 8 deletions src/ethereum/byzantium/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import List, Optional, Set, Tuple

from ethereum.base_types import Bytes0
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.ethash import dataset_size, generate_cache, hashimoto_light
Expand Down Expand Up @@ -329,6 +330,7 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock

sender_address = recover_sender(chain_id, tx)

return sender_address
Expand Down Expand Up @@ -811,14 +813,20 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if v == 27 or v == 28:
public_key = secp256k1_recover(r, s, v - 27, signing_hash_pre155(tx))
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
try:
if v == 27 or v == 28:
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
except InvalidSignature as e:
raise InvalidBlock from e

return Address(keccak256(public_key)[12:32])


Expand Down
3 changes: 2 additions & 1 deletion src/ethereum/byzantium/vm/precompiled_contracts/ecrecover.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Implementation of the ECRECOVER precompiled contract.
"""
from ethereum.base_types import U256
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.utils.byte import left_pad_zero_bytes
Expand Down Expand Up @@ -52,7 +53,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - 27, message_hash)
except ValueError:
except InvalidSignature:
# unable to extract public key
return

Expand Down
51 changes: 28 additions & 23 deletions src/ethereum/cancun/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import List, Optional, Tuple, Union

from ethereum.base_types import Bytes0, Bytes32
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.exceptions import InvalidBlock
Expand Down Expand Up @@ -383,8 +384,9 @@ def check_transaction(
if tx.gas > gas_available:
raise InvalidBlock

sender = recover_sender(chain_id, tx)
sender_account = get_account(state, sender)
sender_address = recover_sender(chain_id, tx)

sender_account = get_account(state, sender_address)

if isinstance(tx, (FeeMarketTransaction, BlobTransaction)):
if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
Expand Down Expand Up @@ -427,7 +429,7 @@ def check_transaction(
if sender_account.code != bytearray():
raise InvalidBlock

return sender, effective_gas_price, blob_versioned_hashes
return sender_address, effective_gas_price, blob_versioned_hashes


def make_receipt(
Expand Down Expand Up @@ -953,30 +955,33 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
try:
if isinstance(tx, LegacyTransaction):
v = tx.v
if v == 27 or v == 28:
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
r, s, tx.y_parity, signing_hash_2930(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
elif isinstance(tx, FeeMarketTransaction):
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
r, s, tx.y_parity, signing_hash_1559(tx)
)
elif isinstance(tx, AccessListTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_2930(tx)
)
elif isinstance(tx, FeeMarketTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_1559(tx)
)
elif isinstance(tx, BlobTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_4844(tx)
)
elif isinstance(tx, BlobTransaction):
public_key = secp256k1_recover(
r, s, tx.y_parity, signing_hash_4844(tx)
)
except InvalidSignature as e:
raise InvalidBlock from e

return Address(keccak256(public_key)[12:32])

Expand Down
3 changes: 2 additions & 1 deletion src/ethereum/cancun/vm/precompiled_contracts/ecrecover.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Implementation of the ECRECOVER precompiled contract.
"""
from ethereum.base_types import U256
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.utils.byte import left_pad_zero_bytes
Expand Down Expand Up @@ -52,7 +53,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - 27, message_hash)
except ValueError:
except InvalidSignature:
# unable to extract public key
return

Expand Down
24 changes: 16 additions & 8 deletions src/ethereum/constantinople/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from typing import List, Optional, Set, Tuple

from ethereum.base_types import Bytes0
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.ethash import dataset_size, generate_cache, hashimoto_light
Expand Down Expand Up @@ -329,6 +330,7 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock

sender_address = recover_sender(chain_id, tx)

return sender_address
Expand Down Expand Up @@ -811,14 +813,20 @@ def recover_sender(chain_id: U64, tx: Transaction) -> Address:
if 0 >= s or s > SECP256K1N // 2:
raise InvalidBlock

if v == 27 or v == 28:
public_key = secp256k1_recover(r, s, v - 27, signing_hash_pre155(tx))
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
try:
if v == 27 or v == 28:
public_key = secp256k1_recover(
r, s, v - 27, signing_hash_pre155(tx)
)
else:
if v != 35 + chain_id * 2 and v != 36 + chain_id * 2:
raise InvalidBlock
public_key = secp256k1_recover(
r, s, v - 35 - chain_id * 2, signing_hash_155(tx, chain_id)
)
except InvalidSignature as e:
raise InvalidBlock from e

return Address(keccak256(public_key)[12:32])


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Implementation of the ECRECOVER precompiled contract.
"""
from ethereum.base_types import U256
from ethereum.crypto import InvalidSignature
from ethereum.crypto.elliptic_curve import SECP256K1N, secp256k1_recover
from ethereum.crypto.hash import Hash32, keccak256
from ethereum.utils.byte import left_pad_zero_bytes
Expand Down Expand Up @@ -52,7 +53,7 @@ def ecrecover(evm: Evm) -> None:

try:
public_key = secp256k1_recover(r, s, v - 27, message_hash)
except ValueError:
except InvalidSignature:
# unable to extract public key
return

Expand Down
8 changes: 8 additions & 0 deletions src/ethereum/crypto/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
"""
Cryptographic primitives used in Ethereum.
"""


class InvalidSignature(Exception):
"""
Thrown when a signature is invalid.
"""

pass
Loading

0 comments on commit 10125b1

Please sign in to comment.