Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add checks to signature recovery #1011

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading