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

Fix EIP-712 filtering on empty array #632

Merged
merged 6 commits into from
Sep 11, 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
21 changes: 13 additions & 8 deletions client/src/ledger_app_clients/ethereum/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,25 +146,30 @@ def eip712_sign_legacy(self,
def eip712_filtering_activate(self):
return self._exchange_async(self._cmd_builder.eip712_filtering_activate())

def eip712_filtering_discarded_path(self, path: str):
return self._exchange(self._cmd_builder.eip712_filtering_discarded_path(path))

def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes):
return self._exchange_async(self._cmd_builder.eip712_filtering_message_info(name,
filters_count,
sig))

def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes):
def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes, discarded: bool):
return self._exchange_async(self._cmd_builder.eip712_filtering_amount_join_token(token_idx,
sig))
sig,
discarded))

def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes):
def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes, discarded: bool):
return self._exchange_async(self._cmd_builder.eip712_filtering_amount_join_value(token_idx,
name,
sig))
sig,
discarded))

def eip712_filtering_datetime(self, name: str, sig: bytes):
return self._exchange_async(self._cmd_builder.eip712_filtering_datetime(name, sig))
def eip712_filtering_datetime(self, name: str, sig: bytes, discarded: bool):
return self._exchange_async(self._cmd_builder.eip712_filtering_datetime(name, sig, discarded))

def eip712_filtering_raw(self, name: str, sig: bytes):
return self._exchange_async(self._cmd_builder.eip712_filtering_raw(name, sig))
def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool):
return self._exchange_async(self._cmd_builder.eip712_filtering_raw(name, sig, discarded))

def sign(self,
bip32_path: str,
Expand Down
26 changes: 18 additions & 8 deletions client/src/ledger_app_clients/ethereum/command_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class P2Type(IntEnum):
LEGACY_IMPLEM = 0x00
NEW_IMPLEM = 0x01
FILTERING_ACTIVATE = 0x00
FILTERING_DISCARDED_PATH = 0x01
FILTERING_MESSAGE_INFO = 0x0f
FILTERING_DATETIME = 0xfc
FILTERING_TOKEN_ADDR_CHECK = 0xfd
Expand Down Expand Up @@ -164,6 +165,15 @@ def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes:
data += sig
return data

def eip712_filtering_discarded_path(self, path: str) -> bytes:
data = bytearray()
data.append(len(path))
data += path.encode()
return self._serialize(InsType.EIP712_SEND_FILTERING,
P1Type.COMPLETE_SEND,
P2Type.FILTERING_DISCARDED_PATH,
data)

def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes) -> bytes:
data = bytearray()
data.append(len(name))
Expand All @@ -176,37 +186,37 @@ def eip712_filtering_message_info(self, name: str, filters_count: int, sig: byte
P2Type.FILTERING_MESSAGE_INFO,
data)

def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes) -> bytes:
def eip712_filtering_amount_join_token(self, token_idx: int, sig: bytes, discarded: bool) -> bytes:
data = bytearray()
data.append(token_idx)
data.append(len(sig))
data += sig
return self._serialize(InsType.EIP712_SEND_FILTERING,
P1Type.COMPLETE_SEND,
int(discarded),
P2Type.FILTERING_TOKEN_ADDR_CHECK,
data)

def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes) -> bytes:
def eip712_filtering_amount_join_value(self, token_idx: int, name: str, sig: bytes, discarded: bool) -> bytes:
data = bytearray()
data.append(len(name))
data += name.encode()
data.append(token_idx)
data.append(len(sig))
data += sig
return self._serialize(InsType.EIP712_SEND_FILTERING,
P1Type.COMPLETE_SEND,
int(discarded),
P2Type.FILTERING_AMOUNT_FIELD,
data)

def eip712_filtering_datetime(self, name: str, sig: bytes) -> bytes:
def eip712_filtering_datetime(self, name: str, sig: bytes, discarded: bool) -> bytes:
return self._serialize(InsType.EIP712_SEND_FILTERING,
P1Type.COMPLETE_SEND,
int(discarded),
P2Type.FILTERING_DATETIME,
self._eip712_filtering_send_name(name, sig))

def eip712_filtering_raw(self, name: str, sig: bytes) -> bytes:
def eip712_filtering_raw(self, name: str, sig: bytes, discarded: bool) -> bytes:
return self._serialize(InsType.EIP712_SEND_FILTERING,
P1Type.COMPLETE_SEND,
int(discarded),
P2Type.FILTERING_RAW,
self._eip712_filtering_send_name(name, sig))

Expand Down
80 changes: 41 additions & 39 deletions client/src/ledger_app_clients/ethereum/eip712/InputData.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,32 +194,36 @@ def encode_bytes_dyn(value: str, typesize: int) -> bytes:
encoding_functions[EIP712FieldType.DYN_BYTES] = encode_bytes_dyn


def send_filter(path: str, discarded: bool):
assert path in filtering_paths.keys()

if filtering_paths[path]["type"] == "amount_join_token":
send_filtering_amount_join_token(path, filtering_paths[path]["token"], discarded)
elif filtering_paths[path]["type"] == "amount_join_value":
if "token" in filtering_paths[path].keys():
token = filtering_paths[path]["token"]
else:
# Permit (ERC-2612)
token = 0xff
send_filtering_amount_join_value(path, token, filtering_paths[path]["name"], discarded)
elif filtering_paths[path]["type"] == "datetime":
send_filtering_datetime(path, filtering_paths[path]["name"], discarded)
elif filtering_paths[path]["type"] == "raw":
send_filtering_raw(path, filtering_paths[path]["name"], discarded)
else:
assert False


def send_struct_impl_field(value, field):
# Something wrong happened if this triggers
if isinstance(value, list) or (field["enum"] == EIP712FieldType.CUSTOM):
breakpoint()
assert not isinstance(value, list)
assert field["enum"] != EIP712FieldType.CUSTOM

data = encoding_functions[field["enum"]](value, field["typesize"])

if filtering_paths:
path = ".".join(current_path)
if path in filtering_paths.keys():
if filtering_paths[path]["type"] == "amount_join_token":
send_filtering_amount_join_token(filtering_paths[path]["token"])
elif filtering_paths[path]["type"] == "amount_join_value":
if "token" in filtering_paths[path].keys():
token = filtering_paths[path]["token"]
else:
# Permit (ERC-2612)
token = 0xff
send_filtering_amount_join_value(token,
filtering_paths[path]["name"])
elif filtering_paths[path]["type"] == "datetime":
send_filtering_datetime(filtering_paths[path]["name"])
elif filtering_paths[path]["type"] == "raw":
send_filtering_raw(filtering_paths[path]["name"])
else:
assert False
send_filter(path, False)

with app_client.eip712_send_struct_impl_struct_field(data):
enable_autonext()
Expand All @@ -234,6 +238,12 @@ def evaluate_field(structs, data, field, lvls_left, new_level=True):
if len(array_lvls) > 0 and lvls_left > 0:
with app_client.eip712_send_struct_impl_array(len(data)):
pass
if len(data) == 0:
for path in filtering_paths.keys():
dpath = ".".join(current_path) + ".[]"
if path.startswith(dpath):
app_client.eip712_filtering_discarded_path(path)
send_filter(path, True)
idx = 0
for subdata in data:
current_path.append("[]")
Expand Down Expand Up @@ -295,57 +305,49 @@ def send_filtering_message_info(display_name: str, filters_count: int):
disable_autonext()


def send_filtering_amount_join_token(token_idx: int):
def send_filtering_amount_join_token(path: str, token_idx: int, discarded: bool):
global sig_ctx

path_str = ".".join(current_path)

to_sign = start_signature_payload(sig_ctx, 11)
to_sign += path_str.encode()
to_sign += path.encode()
to_sign.append(token_idx)
sig = keychain.sign_data(keychain.Key.CAL, to_sign)
with app_client.eip712_filtering_amount_join_token(token_idx, sig):
with app_client.eip712_filtering_amount_join_token(token_idx, sig, discarded):
pass


def send_filtering_amount_join_value(token_idx: int, display_name: str):
def send_filtering_amount_join_value(path: str, token_idx: int, display_name: str, discarded: bool):
global sig_ctx

path_str = ".".join(current_path)

to_sign = start_signature_payload(sig_ctx, 22)
to_sign += path_str.encode()
to_sign += path.encode()
to_sign += display_name.encode()
to_sign.append(token_idx)
sig = keychain.sign_data(keychain.Key.CAL, to_sign)
with app_client.eip712_filtering_amount_join_value(token_idx, display_name, sig):
with app_client.eip712_filtering_amount_join_value(token_idx, display_name, sig, discarded):
pass


def send_filtering_datetime(display_name: str):
def send_filtering_datetime(path: str, display_name: str, discarded: bool):
global sig_ctx

path_str = ".".join(current_path)

to_sign = start_signature_payload(sig_ctx, 33)
to_sign += path_str.encode()
to_sign += path.encode()
to_sign += display_name.encode()
sig = keychain.sign_data(keychain.Key.CAL, to_sign)
with app_client.eip712_filtering_datetime(display_name, sig):
with app_client.eip712_filtering_datetime(display_name, sig, discarded):
pass


# ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures
def send_filtering_raw(display_name):
def send_filtering_raw(path: str, display_name: str, discarded: bool):
global sig_ctx

path_str = ".".join(current_path)

to_sign = start_signature_payload(sig_ctx, 72)
to_sign += path_str.encode()
to_sign += path.encode()
to_sign += display_name.encode()
sig = keychain.sign_data(keychain.Key.CAL, to_sign)
with app_client.eip712_filtering_raw(display_name, sig):
with app_client.eip712_filtering_raw(display_name, sig, discarded):
pass


Expand Down
26 changes: 24 additions & 2 deletions doc/ethapp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ Application version 1.9.19 - 2022-05-17
- Add EIP-712 amount & date/time filtering
- PROVIDE ERC 20 TOKEN INFORMATION & PROVIDE NFT INFORMATION now send back the index where the asset has been stored

### 1.12.0
- Add EIP-712 discarded filter path support

## About

This application describes the APDU messages interface to communicate with the Ethereum application.
Expand Down Expand Up @@ -828,6 +831,12 @@ Field substitution will be ignored if the full filtering is not activated.

This command should come before the domain & message implementations. If activated, fields will be by default hidden unless they receive a field name substitution.

##### Discarded filter path

This command gives the app the absolute path of the upcoming filter which will be discarded (because it targets a field within an empty array).

The next filter should be marked as discarded (with P1) to be able to use this given filter path.

##### Message info

This command should come right after the implementation of the domain has been sent with *SEND STRUCT IMPLEMENTATION*, just before sending the message implementation.
Expand Down Expand Up @@ -880,8 +889,11 @@ _Command_
[width="80%"]
|=========================================================================
| *CLA* | *INS* | *P1* | *P2* | *LC* | *Le*
| E0 | 1E | 00
| 00 : activation
| E0 | 1E | 00 : standard

01 : discarded | 00 : activation

01 : discarded filter path

0F : message info

Expand All @@ -901,6 +913,16 @@ _Input data_

None

##### If P2 == discarded filter path

[width="80%"]
|==========================================
| *Description* | *Length (byte)*
| Path length | 1
| Path | variable
|==========================================


##### If P2 == message info

[width="80%"]
Expand Down
2 changes: 1 addition & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ static uint16_t handleApdu(command_t *cmd, uint32_t *flags, uint32_t *tx) {
break;

case INS_EIP712_FILTERING:
sw = handle_eip712_filtering(cmd->p2, cmd->data, cmd->lc, flags);
sw = handle_eip712_filtering(cmd->p1, cmd->p2, cmd->data, cmd->lc, flags);
break;
#endif // HAVE_EIP712_FULL_SUPPORT

Expand Down
Loading
Loading