Skip to content

Commit

Permalink
add checks to signature recovery
Browse files Browse the repository at this point in the history
  • Loading branch information
gurukamath committed Sep 19, 2024
1 parent c8df873 commit 2624730
Show file tree
Hide file tree
Showing 37 changed files with 171 additions and 41 deletions.
7 changes: 6 additions & 1 deletion 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,7 +418,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

if isinstance(tx, FeeMarketTransaction):
if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
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
7 changes: 6 additions & 1 deletion 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,7 +336,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

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
7 changes: 6 additions & 1 deletion 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,7 +330,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

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
11 changes: 8 additions & 3 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,12 @@ def check_transaction(
if tx.gas > gas_available:
raise InvalidBlock

sender = recover_sender(chain_id, tx)
sender_account = get_account(state, sender)
try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

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 +432,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
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
7 changes: 6 additions & 1 deletion 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,7 +330,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

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
24 changes: 21 additions & 3 deletions src/ethereum/crypto/elliptic_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
import coincurve

from ..base_types import U256, Bytes
from . import InvalidSignature
from .finite_field import Field
from .hash import Hash32

SECP256K1B = 7
SECP256K1P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
SECP256K1N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

F = TypeVar("F", bound=Field)
Expand All @@ -37,16 +40,31 @@ def secp256k1_recover(r: U256, s: U256, v: U256, msg_hash: Hash32) -> Bytes:
public_key : `ethereum.base_types.Bytes`
Recovered public key.
"""
is_square = pow(
pow(r, 3, SECP256K1P) + SECP256K1B, (SECP256K1P - 1) // 2, SECP256K1P
)

if is_square != 1:
raise InvalidSignature("r value is not a square")

r_bytes = r.to_be_bytes32()
s_bytes = s.to_be_bytes32()

signature = bytearray([0] * 65)
signature[32 - len(r_bytes) : 32] = r_bytes
signature[64 - len(s_bytes) : 64] = s_bytes
signature[64] = v
public_key = coincurve.PublicKey.from_signature_and_message(
bytes(signature), msg_hash, hasher=None
)

# If the recovery algorithm returns the point at infinity,
# the signature is considered invalid
# the below function will raise a ValueError.
try:
public_key = coincurve.PublicKey.from_signature_and_message(
bytes(signature), msg_hash, hasher=None
)
except ValueError as e:
raise InvalidSignature from e

public_key = public_key.format(compressed=False)[1:]
return public_key

Expand Down
7 changes: 6 additions & 1 deletion src/ethereum/dao_fork/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,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 @@ -338,7 +339,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(tx)

try:
sender_address = recover_sender(tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

Expand Down
3 changes: 2 additions & 1 deletion src/ethereum/dao_fork/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
7 changes: 6 additions & 1 deletion src/ethereum/frontier/fork.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from dataclasses import dataclass
from typing import List, Optional, Set, Tuple

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 @@ -318,7 +319,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(tx)

try:
sender_address = recover_sender(tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

Expand Down
3 changes: 2 additions & 1 deletion src/ethereum/frontier/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
7 changes: 6 additions & 1 deletion src/ethereum/gray_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,7 +418,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(chain_id, tx)

try:
sender_address = recover_sender(chain_id, tx)
except InvalidSignature as e:
raise InvalidBlock from e

if isinstance(tx, FeeMarketTransaction):
if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
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
7 changes: 6 additions & 1 deletion src/ethereum/homestead/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 @@ -320,7 +321,11 @@ def check_transaction(
"""
if tx.gas > gas_available:
raise InvalidBlock
sender_address = recover_sender(tx)

try:
sender_address = recover_sender(tx)
except InvalidSignature as e:
raise InvalidBlock from e

return sender_address

Expand Down
Loading

0 comments on commit 2624730

Please sign in to comment.