EIP712 Filtering count checking
This commit is contained in:
@@ -810,22 +810,21 @@ Full filtering is disabled by default and has to be changed with this APDU (defa
|
||||
|
||||
Field substitution will be ignored if the full filtering is not activated.
|
||||
|
||||
If activated, fields will be by default hidden unless they receive a field name substitution.
|
||||
This command should come before the domain & message implementations. If activated, fields will be by default hidden unless they receive a field name substitution.
|
||||
|
||||
##### Contract name substitution
|
||||
##### Message info
|
||||
|
||||
Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*.
|
||||
Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation.
|
||||
This command should come right after the implementation of the domain has been sent with *SEND STRUCT IMPLEMENTATION*, just before sending the message implementation.
|
||||
The first byte is used so that a signature of one type cannot be valid as another type.
|
||||
|
||||
The signature is computed on :
|
||||
|
||||
183 || chain ID (BE) || contract address || schema hash || display name
|
||||
183 || chain ID (BE) || contract address || schema hash || filters count || display name
|
||||
|
||||
|
||||
##### Field name substitution
|
||||
##### Show field
|
||||
|
||||
Name substitution commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones).
|
||||
These commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones).
|
||||
The first byte is used so that a signature of one type cannot be valid as another type.
|
||||
|
||||
The signature is computed on :
|
||||
@@ -842,19 +841,31 @@ _Command_
|
||||
| E0 | 1E | 00
|
||||
| 00 : activate
|
||||
|
||||
0F : contract name
|
||||
0F : message info
|
||||
|
||||
FF : field name
|
||||
FF : show field
|
||||
| variable | variable
|
||||
|=========================================================================
|
||||
|
||||
_Input data_
|
||||
|
||||
##### If P1 == activate
|
||||
##### If P2 == activate
|
||||
|
||||
None
|
||||
|
||||
##### If P1 == contract name OR P1 == field name
|
||||
##### If P2 == message info
|
||||
|
||||
[width="80%"]
|
||||
|==========================================
|
||||
| *Description* | *Length (byte)*
|
||||
| Display name length | 1
|
||||
| Display name | variable
|
||||
| Filters count | 1
|
||||
| Signature length | 1
|
||||
| Signature | variable
|
||||
|==========================================
|
||||
|
||||
##### If P2 == show field
|
||||
|
||||
[width="80%"]
|
||||
|==========================================
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
#define APDU_RESPONSE_INVALID_INS 0x6d00
|
||||
#define APDU_RESPONSE_INVALID_P1_P2 0x6b00
|
||||
#define APDU_RESPONSE_CONDITION_NOT_SATISFIED 0x6985
|
||||
#define APDU_RESPONSE_REF_DATA_NOT_USABLE 0x6a88
|
||||
#define APDU_RESPONSE_REF_DATA_NOT_FOUND 0x6a88
|
||||
|
||||
#ifdef HAVE_STARKWARE
|
||||
|
||||
|
||||
@@ -139,14 +139,15 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) {
|
||||
ret = compute_schema_hash();
|
||||
}
|
||||
break;
|
||||
case P2_FILT_CONTRACT_NAME:
|
||||
case P2_FILT_FIELD_NAME:
|
||||
type = (apdu_buf[OFFSET_P2] == P2_FILT_CONTRACT_NAME) ? FILTERING_CONTRACT_NAME
|
||||
: FILTERING_STRUCT_FIELD;
|
||||
case P2_FILT_MESSAGE_INFO:
|
||||
case P2_FILT_SHOW_FIELD:
|
||||
type = (apdu_buf[OFFSET_P2] == P2_FILT_MESSAGE_INFO)
|
||||
? FILTERING_PROVIDE_MESSAGE_INFO
|
||||
: FILTERING_SHOW_FIELD;
|
||||
if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) {
|
||||
ret =
|
||||
provide_filtering_info(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC], type);
|
||||
if ((apdu_buf[OFFSET_P2] == P2_FILT_CONTRACT_NAME) && ret) {
|
||||
if ((apdu_buf[OFFSET_P2] == P2_FILT_MESSAGE_INFO) && ret) {
|
||||
reply_apdu = false;
|
||||
}
|
||||
}
|
||||
@@ -177,6 +178,10 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) {
|
||||
|
||||
if (eip712_context == NULL) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
} else if ((ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) &&
|
||||
(ui_712_remaining_filters() != 0)) {
|
||||
PRINTF("%d EIP712 filters are missing\n", ui_712_remaining_filters());
|
||||
apdu_response_code = APDU_RESPONSE_REF_DATA_NOT_FOUND;
|
||||
} else if (parseBip32(&apdu_buf[OFFSET_CDATA], &length, &tmpCtx.messageSigningContext.bip32) !=
|
||||
NULL) {
|
||||
if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) {
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
#define P1_PARTIAL 0xFF
|
||||
|
||||
// APDUs P2
|
||||
#define P2_DEF_NAME 0x00
|
||||
#define P2_DEF_FIELD 0xFF
|
||||
#define P2_IMPL_NAME P2_DEF_NAME
|
||||
#define P2_IMPL_ARRAY 0x0F
|
||||
#define P2_IMPL_FIELD P2_DEF_FIELD
|
||||
#define P2_FILT_ACTIVATE 0x00
|
||||
#define P2_FILT_CONTRACT_NAME 0x0F
|
||||
#define P2_FILT_FIELD_NAME 0xFF
|
||||
#define P2_DEF_NAME 0x00
|
||||
#define P2_DEF_FIELD 0xFF
|
||||
#define P2_IMPL_NAME P2_DEF_NAME
|
||||
#define P2_IMPL_ARRAY 0x0F
|
||||
#define P2_IMPL_FIELD P2_DEF_FIELD
|
||||
#define P2_FILT_ACTIVATE 0x00
|
||||
#define P2_FILT_MESSAGE_INFO 0x0F
|
||||
#define P2_FILT_SHOW_FIELD 0xFF
|
||||
|
||||
#define DOMAIN_STRUCT_NAME "EIP712Domain"
|
||||
|
||||
|
||||
@@ -67,10 +67,10 @@ static bool verify_filtering_signature(uint8_t dname_length,
|
||||
|
||||
// Magic number, makes it so a signature of one type can't be used as another
|
||||
switch (type) {
|
||||
case FILTERING_STRUCT_FIELD:
|
||||
case FILTERING_SHOW_FIELD:
|
||||
hash_byte(FILTERING_MAGIC_STRUCT_FIELD, (cx_hash_t *) &hash_ctx);
|
||||
break;
|
||||
case FILTERING_CONTRACT_NAME:
|
||||
case FILTERING_PROVIDE_MESSAGE_INFO:
|
||||
hash_byte(FILTERING_MAGIC_CONTRACT_NAME, (cx_hash_t *) &hash_ctx);
|
||||
break;
|
||||
default:
|
||||
@@ -93,8 +93,11 @@ static bool verify_filtering_signature(uint8_t dname_length,
|
||||
sizeof(eip712_context->schema_hash),
|
||||
(cx_hash_t *) &hash_ctx);
|
||||
|
||||
if (type == FILTERING_STRUCT_FIELD) {
|
||||
if (type == FILTERING_SHOW_FIELD) {
|
||||
hash_filtering_path((cx_hash_t *) &hash_ctx);
|
||||
} else // FILTERING_PROVIDE_MESSAGE_INFO
|
||||
{
|
||||
hash_byte(ui_712_remaining_filters(), (cx_hash_t *) &hash_ctx);
|
||||
}
|
||||
|
||||
// Display name
|
||||
@@ -131,13 +134,14 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filt
|
||||
const char *dname;
|
||||
uint8_t sig_len;
|
||||
const uint8_t *sig;
|
||||
uint8_t offset = 0;
|
||||
|
||||
if (type == FILTERING_CONTRACT_NAME) {
|
||||
if (type == FILTERING_PROVIDE_MESSAGE_INFO) {
|
||||
if (path_get_root_type() != ROOT_DOMAIN) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
return false;
|
||||
}
|
||||
} else // FILTERING_STRUCT_FIELD
|
||||
} else // FILTERING_SHOW_FIELD
|
||||
{
|
||||
if (path_get_root_type() != ROOT_MESSAGE) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
@@ -145,20 +149,25 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filt
|
||||
}
|
||||
}
|
||||
if (length > 0) {
|
||||
dname_len = payload[0];
|
||||
dname_len = payload[offset++];
|
||||
if ((1 + dname_len) < length) {
|
||||
dname = (char *) &payload[1];
|
||||
sig_len = payload[1 + dname_len];
|
||||
sig = &payload[1 + dname_len + 1];
|
||||
if ((sig_len > 0) && ((1 + dname_len + 1 + sig_len) == length)) {
|
||||
dname = (char *) &payload[offset];
|
||||
offset += dname_len;
|
||||
if (type == FILTERING_PROVIDE_MESSAGE_INFO) {
|
||||
ui_712_set_filters_count(payload[offset++]);
|
||||
}
|
||||
sig_len = payload[offset++];
|
||||
sig = &payload[offset];
|
||||
offset += sig_len;
|
||||
if ((sig_len > 0) && (offset == length)) {
|
||||
if ((ret = verify_filtering_signature(dname_len, dname, sig_len, sig, type))) {
|
||||
if (type == FILTERING_CONTRACT_NAME) {
|
||||
if (type == FILTERING_PROVIDE_MESSAGE_INFO) {
|
||||
if (!N_storage.verbose_eip712) {
|
||||
ui_712_set_title("Contract", 8);
|
||||
ui_712_set_value(dname, dname_len);
|
||||
ui_712_redraw_generic_step();
|
||||
}
|
||||
} else // FILTERING_STRUCT_FIELD
|
||||
} else // FILTERING_SHOW_FIELD
|
||||
{
|
||||
if (dname_len > 0) // don't substitute for an empty name
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#define FILTERING_MAGIC_CONTRACT_NAME 0b10110111 // 183
|
||||
#define FILTERING_MAGIC_STRUCT_FIELD 0b01001000 // ~183 = 72
|
||||
|
||||
typedef enum { FILTERING_CONTRACT_NAME, FILTERING_STRUCT_FIELD } e_filtering_type;
|
||||
typedef enum { FILTERING_PROVIDE_MESSAGE_INFO, FILTERING_SHOW_FIELD } e_filtering_type;
|
||||
|
||||
bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filtering_type type);
|
||||
|
||||
|
||||
@@ -486,6 +486,7 @@ static bool path_advance_in_struct(void) {
|
||||
}
|
||||
if (path_struct->depth_count > 0) {
|
||||
*depth += 1;
|
||||
ui_712_notify_filter_change();
|
||||
end_reached = (*depth == fields_count);
|
||||
}
|
||||
if (end_reached) {
|
||||
|
||||
@@ -503,6 +503,24 @@ e_eip712_filtering_mode ui_712_get_filtering_mode(void) {
|
||||
return ui_ctx->filtering_mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the number of filters this message should process
|
||||
*
|
||||
* @param[in] count number of filters
|
||||
*/
|
||||
void ui_712_set_filters_count(uint8_t count) {
|
||||
ui_ctx->filters_to_process = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of filters left to process
|
||||
*
|
||||
* @return number of filters
|
||||
*/
|
||||
uint8_t ui_712_remaining_filters(void) {
|
||||
return ui_ctx->filters_to_process;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all the UI struct field flags
|
||||
*/
|
||||
@@ -521,4 +539,21 @@ void ui_712_queue_struct_to_review(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify of a filter change from a path advance
|
||||
*
|
||||
* This function figures out by itself if there is anything to do
|
||||
*/
|
||||
void ui_712_notify_filter_change(void) {
|
||||
if (path_get_root_type() == ROOT_MESSAGE) {
|
||||
if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) {
|
||||
if (ui_ctx->filters_to_process > 0) {
|
||||
if (ui_ctx->field_flags & UI_712_FIELD_SHOWN) {
|
||||
ui_ctx->filters_to_process -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_EIP712_FULL_SUPPORT
|
||||
|
||||
@@ -18,6 +18,7 @@ typedef struct {
|
||||
bool end_reached;
|
||||
e_ui_position pos;
|
||||
uint8_t filtering_mode;
|
||||
uint8_t filters_to_process;
|
||||
uint8_t field_flags;
|
||||
uint8_t structs_to_review;
|
||||
} t_ui_context;
|
||||
@@ -39,7 +40,10 @@ void ui_712_field_flags_reset(void);
|
||||
void ui_712_finalize_field(void);
|
||||
void ui_712_set_filtering_mode(e_eip712_filtering_mode mode);
|
||||
e_eip712_filtering_mode ui_712_get_filtering_mode(void);
|
||||
void ui_712_set_filters_count(uint8_t count);
|
||||
uint8_t ui_712_remaining_filters(void);
|
||||
void ui_712_queue_struct_to_review(void);
|
||||
void ui_712_notify_filter_change(void);
|
||||
|
||||
#endif // HAVE_EIP712_FULL_SUPPORT
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ def send_struct_impl_field(value, field):
|
||||
if filtering_paths:
|
||||
path = ".".join(current_path)
|
||||
if path in filtering_paths.keys():
|
||||
send_filtering_field_name(filtering_paths[path])
|
||||
send_filtering_show_field(filtering_paths[path])
|
||||
|
||||
app_client.eip712_send_struct_impl_struct_field(data)
|
||||
|
||||
@@ -242,37 +242,38 @@ def send_struct_impl(structs, data, structname):
|
||||
return True
|
||||
|
||||
# ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures
|
||||
def send_filtering_contract_name(display_name: str):
|
||||
def send_filtering_message_info(display_name: str, filters_count: int):
|
||||
global sig_ctx
|
||||
|
||||
msg = bytearray()
|
||||
msg.append(183)
|
||||
msg += sig_ctx["chainid"]
|
||||
msg += sig_ctx["caddr"]
|
||||
msg += sig_ctx["schema_hash"]
|
||||
to_sign = bytearray()
|
||||
to_sign.append(183)
|
||||
to_sign += sig_ctx["chainid"]
|
||||
to_sign += sig_ctx["caddr"]
|
||||
to_sign += sig_ctx["schema_hash"]
|
||||
to_sign.append(filters_count)
|
||||
for char in display_name:
|
||||
msg.append(ord(char))
|
||||
to_sign.append(ord(char))
|
||||
|
||||
sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der)
|
||||
app_client.eip712_filtering_send_contract_name(display_name, sig)
|
||||
sig = sig_ctx["key"].sign_deterministic(to_sign, sigencode=sigencode_der)
|
||||
app_client.eip712_filtering_message_info(display_name, filters_count, sig)
|
||||
|
||||
# ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures
|
||||
def send_filtering_field_name(display_name):
|
||||
def send_filtering_show_field(display_name):
|
||||
global sig_ctx
|
||||
|
||||
path_str = ".".join(current_path)
|
||||
|
||||
msg = bytearray()
|
||||
msg.append(72)
|
||||
msg += sig_ctx["chainid"]
|
||||
msg += sig_ctx["caddr"]
|
||||
msg += sig_ctx["schema_hash"]
|
||||
to_sign = bytearray()
|
||||
to_sign.append(72)
|
||||
to_sign += sig_ctx["chainid"]
|
||||
to_sign += sig_ctx["caddr"]
|
||||
to_sign += sig_ctx["schema_hash"]
|
||||
for char in path_str:
|
||||
msg.append(ord(char))
|
||||
to_sign.append(ord(char))
|
||||
for char in display_name:
|
||||
msg.append(ord(char))
|
||||
sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der)
|
||||
app_client.eip712_filtering_send_field_name(display_name, sig)
|
||||
to_sign.append(ord(char))
|
||||
sig = sig_ctx["key"].sign_deterministic(to_sign, sigencode=sigencode_der)
|
||||
app_client.eip712_filtering_show_field(display_name, sig)
|
||||
|
||||
def read_filtering_file(domain, message, filtering_file_path):
|
||||
data_json = None
|
||||
@@ -349,9 +350,9 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_p
|
||||
|
||||
if filtering_file_path:
|
||||
if filtr and "name" in filtr:
|
||||
send_filtering_contract_name(filtr["name"])
|
||||
send_filtering_message_info(filtr["name"], len(filtering_paths))
|
||||
else:
|
||||
send_filtering_contract_name(domain["name"])
|
||||
send_filtering_message_info(domain["name"], len(filtering_paths))
|
||||
|
||||
# send message implementation
|
||||
app_client.eip712_send_struct_impl_root_struct(message_typename)
|
||||
|
||||
@@ -144,13 +144,13 @@ class EthereumClient:
|
||||
self._eip712_filtering = True
|
||||
assert self._recv().status == 0x9000
|
||||
|
||||
def eip712_filtering_send_contract_name(self, name: str, sig: bytes):
|
||||
with self._send(self._cmd_builder.eip712_filtering_send_contract_name(name, sig)):
|
||||
def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes):
|
||||
with self._send(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)):
|
||||
self._enable_click_until_response()
|
||||
self._disable_click_until_response()
|
||||
assert self._recv().status == 0x9000
|
||||
|
||||
def eip712_filtering_send_field_name(self, name: str, sig: bytes):
|
||||
with self._send(self._cmd_builder.eip712_filtering_send_field_name(name, sig)):
|
||||
def eip712_filtering_show_field(self, name: str, sig: bytes):
|
||||
with self._send(self._cmd_builder.eip712_filtering_show_field(name, sig)):
|
||||
pass
|
||||
assert self._recv().status == 0x9000
|
||||
|
||||
@@ -151,13 +151,19 @@ class EthereumCmdBuilder:
|
||||
data += sig
|
||||
return data
|
||||
|
||||
def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes:
|
||||
def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes) -> bytes:
|
||||
data = bytearray()
|
||||
data.append(len(name))
|
||||
data += self._string_to_bytes(name)
|
||||
data.append(filters_count)
|
||||
data.append(len(sig))
|
||||
data += sig
|
||||
return self._serialize(InsType.EIP712_SEND_FILTERING,
|
||||
P1Type.COMPLETE_SEND,
|
||||
P2Type.FILTERING_CONTRACT_NAME,
|
||||
self._eip712_filtering_send_name(name, sig))
|
||||
data)
|
||||
|
||||
def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes:
|
||||
def eip712_filtering_show_field(self, name: str, sig: bytes) -> bytes:
|
||||
return self._serialize(InsType.EIP712_SEND_FILTERING,
|
||||
P1Type.COMPLETE_SEND,
|
||||
P2Type.FILTERING_FIELD_NAME,
|
||||
|
||||
Reference in New Issue
Block a user