1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2024-11-26 09:28:13 +00:00

refactor(core) allow recursion in ethereum sign_typed_data

This commit is contained in:
Jan Hnatek 2024-09-05 11:38:25 +02:00
parent 777ad11bec
commit d98b5fe064

View File

@ -252,8 +252,6 @@ class TypedDataEnvelope:
i.e. the concatenation of the encoded member values in the order that they appear in the type. i.e. the concatenation of the encoded member values in the order that they appear in the type.
Each encoded member value is exactly 32-byte long. Each encoded member value is exactly 32-byte long.
""" """
from .layout import confirm_typed_value, should_show_array
type_members = self.types[primary_type].members type_members = self.types[primary_type].members
member_value_path = member_path + [0] member_value_path = member_path + [0]
current_parent_objects = parent_objects + [""] current_parent_objects = parent_objects + [""]
@ -265,96 +263,165 @@ class TypedDataEnvelope:
# Arrays and structs need special recursive handling # Arrays and structs need special recursive handling
if field_type.data_type == EthereumDataType.STRUCT: if field_type.data_type == EthereumDataType.STRUCT:
assert field_type.struct_name is not None # validate_field_type assert field_type.struct_name is not None # validate_field_type
struct_name = field_type.struct_name
current_parent_objects[-1] = field_name current_parent_objects[-1] = field_name
if show_data: await self.encode_struct(
show_struct = await should_show_struct( w,
struct_name, # description field_type.struct_name,
self.types[struct_name].members, # data_members
".".join(current_parent_objects), # title
)
else:
show_struct = False
res = await self.hash_struct(
struct_name,
member_value_path, member_value_path,
show_struct, show_data,
current_parent_objects,
False,
)
elif field_type.data_type == EthereumDataType.ARRAY:
await self.encode_array(
w,
field_name,
field_type,
member_value_path,
show_data,
parent_objects,
current_parent_objects, current_parent_objects,
) )
w.extend(res)
elif field_type.data_type == EthereumDataType.ARRAY:
# Getting the length of the array first, if not fixed
if field_type.size is None:
array_size = await _get_array_size(member_value_path)
else:
array_size = field_type.size
assert field_type.entry_type is not None # validate_field_type
entry_type = field_type.entry_type
current_parent_objects[-1] = field_name
if show_data:
show_array = await should_show_array(
current_parent_objects,
get_type_name(entry_type),
array_size,
)
else:
show_array = False
arr_w = get_hash_writer()
el_member_path = member_value_path + [0]
for i in range(array_size):
el_member_path[-1] = i
# TODO: we do not support arrays of arrays, check if we should
if entry_type.data_type == EthereumDataType.STRUCT:
assert entry_type.struct_name is not None # validate_field_type
struct_name = entry_type.struct_name
# Metamask V4 implementation has a bug, that causes the
# behavior of structs in array be different from SPEC
# Explanation at https://github.com/MetaMask/eth-sig-util/pull/107
# encode_data() is the way to process structs in arrays, but
# Metamask V4 is using hash_struct() even in this case
if self.metamask_v4_compat:
res = await self.hash_struct(
struct_name,
el_member_path,
show_array,
current_parent_objects,
)
arr_w.extend(res)
else:
await self.get_and_encode_data(
arr_w,
struct_name,
el_member_path,
show_array,
current_parent_objects,
)
else:
value = await get_value(entry_type, el_member_path)
encode_field(arr_w, entry_type, value)
if show_array:
await confirm_typed_value(
field_name,
value,
parent_objects,
entry_type,
i,
)
w.extend(arr_w.get_digest())
else: else:
value = await get_value(field_type, member_value_path) await self.encode_nonref(
encode_field(w, field_type, value) w,
if show_data: field_name,
await confirm_typed_value( field_type,
field_name, member_value_path,
value, show_data,
parent_objects, parent_objects,
field_type, )
)
async def encode_struct(
self,
w: HashWriter,
struct_name: str,
field_value_path: list[int],
show_data: bool,
parent_objects: list[str],
is_array_member: bool,
):
"""Encode a struct field."""
if show_data:
show_struct = await should_show_struct(
struct_name, # description
self.types[struct_name].members, # data_members
".".join(parent_objects), # title
)
else:
show_struct = False
# Metamask V4 implementation has a bug, that causes the
# behavior of structs in array be different from SPEC
# Explanation at https://github.com/MetaMask/eth-sig-util/pull/107
# encode_data() is the way to process structs in arrays, but
# Metamask V4 is using hash_struct() even in this case
if not is_array_member or self.metamask_v4_compat:
res = await self.hash_struct(
struct_name,
field_value_path,
show_struct,
parent_objects,
)
w.extend(res)
else:
await self.get_and_encode_data(
w,
struct_name,
field_value_path,
show_struct,
parent_objects,
)
async def encode_array(
self,
w: HashWriter,
array_name: str,
array_field_type: EthereumFieldType,
array_value_path: list[int],
show_data: bool,
parent_objects: list[str],
current_parent_objects: list[str],
):
"""Encode an array field."""
from .layout import should_show_array
# Get the length of the array first, if not fixed
if array_field_type.size is None:
array_size = await _get_array_size(array_value_path)
else:
array_size = array_field_type.size
assert array_field_type.entry_type is not None # validate_field_type
entry_type = array_field_type.entry_type
current_parent_objects[-1] = array_name
if show_data:
show_array = await should_show_array(
current_parent_objects,
get_type_name(entry_type),
array_size,
)
else:
show_array = False
arr_w = get_hash_writer()
field_member_path = array_value_path + [0]
for i in range(array_size):
field_member_path[-1] = i
# TODO: we do not support arrays of arrays, check if we should
if entry_type.data_type == EthereumDataType.STRUCT:
assert entry_type.struct_name is not None # validate_field_type
await self.encode_struct(
arr_w,
entry_type.struct_name,
field_member_path,
show_array,
current_parent_objects,
is_array_member=True,
)
else:
await self.encode_nonref(
arr_w,
array_name,
entry_type,
field_member_path,
show_array,
parent_objects,
i,
)
w.extend(arr_w.get_digest())
async def encode_nonref(
self,
w: HashWriter,
field_name: str,
field_type: EthereumFieldType,
field_value_path: list[int],
show_data: bool,
parent_objects: list[str],
array_index: int | None = None,
):
"""Encode a non-reference field."""
from .layout import confirm_typed_value
value = await get_value(field_type, field_value_path)
encode_field(w, field_type, value)
if show_data:
await confirm_typed_value(
field_name,
value,
parent_objects,
field_type,
array_index,
)
def encode_field( def encode_field(