From e76e429148a875ebf2dc1cee6b5838822e959334 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 12 Apr 2024 11:09:06 +0200 Subject: [PATCH] Amount-join EIP-712 filtering implementation --- src_features/signMessageEIP712/commands_712.c | 81 ++-- src_features/signMessageEIP712/commands_712.h | 14 - src_features/signMessageEIP712/filtering.c | 392 +++++++++++++----- src_features/signMessageEIP712/filtering.h | 10 +- src_features/signMessageEIP712/ui_logic.c | 134 +++++- src_features/signMessageEIP712/ui_logic.h | 17 +- 6 files changed, 486 insertions(+), 162 deletions(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 2e2f7b7..98661cf 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -14,6 +14,23 @@ #include "filtering.h" #include "common_712.h" #include "common_ui.h" // ui_idle +#include "manage_asset_info.h" + +// APDUs P1 +#define P1_COMPLETE 0x00 +#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_MESSAGE_INFO 0x0F +#define P2_FILT_AMOUNT_JOIN_TOKEN 0xFD +#define P2_FILT_AMOUNT_JOIN_VALUE 0xFE +#define P2_FILT_RAW_FIELD 0xFF /** * Send the response to the previous APDU command @@ -136,39 +153,43 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { bool handle_eip712_filtering(const uint8_t *const apdu_buf) { bool ret = true; bool reply_apdu = true; - e_filtering_type type; if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - ret = false; - } else { - switch (apdu_buf[OFFSET_P2]) { - case P2_FILT_ACTIVATE: - if (!N_storage.verbose_eip712) { - ui_712_set_filtering_mode(EIP712_FILTERING_FULL); - ret = compute_schema_hash(); - } - break; - 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_MESSAGE_INFO) && ret) { - reply_apdu = false; - } - } - break; - default: - PRINTF("Unknown P2 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P2], - apdu_buf[OFFSET_INS]); - apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; - ret = false; - } + return false; + } + if ((apdu_buf[OFFSET_P2] != P2_FILT_ACTIVATE) && + (ui_712_get_filtering_mode() != EIP712_FILTERING_FULL)) { + handle_eip712_return_code(true); + return true; + } + switch (apdu_buf[OFFSET_P2]) { + case P2_FILT_ACTIVATE: + if (!N_storage.verbose_eip712) { + ui_712_set_filtering_mode(EIP712_FILTERING_FULL); + ret = compute_schema_hash(); + } + forget_known_assets(); + break; + case P2_FILT_MESSAGE_INFO: + ret = filtering_message_info(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); + if (ret) { + reply_apdu = false; + } + break; + case P2_FILT_AMOUNT_JOIN_TOKEN: + ret = filtering_amount_join_token(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); + break; + case P2_FILT_AMOUNT_JOIN_VALUE: + ret = filtering_amount_join_value(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); + break; + case P2_FILT_RAW_FIELD: + ret = filtering_raw_field(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); + break; + default: + PRINTF("Unknown P2 0x%x for APDU 0x%x\n", apdu_buf[OFFSET_P2], apdu_buf[OFFSET_INS]); + apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; + ret = false; } if (reply_apdu) { handle_eip712_return_code(ret); diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index e97d77b..dcfcf45 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -6,20 +6,6 @@ #include #include -// APDUs P1 -#define P1_COMPLETE 0x00 -#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_MESSAGE_INFO 0x0F -#define P2_FILT_SHOW_FIELD 0xFF - #define DOMAIN_STRUCT_NAME "EIP712Domain" bool handle_eip712_struct_def(const uint8_t *const apdu_buf); diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 1a24d61..cfd3e72 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -11,12 +11,17 @@ #include "path.h" #include "ui_logic.h" +#define FILT_MAGIC_MESSAGE_INFO 183 +#define FILT_MAGIC_AMOUNT_JOIN_TOKEN 11 +#define FILT_MAGIC_AMOUNT_JOIN_VALUE 22 +#define FILT_MAGIC_RAW_FIELD 72 + /** * Reconstruct the field path and hash it * * @param[in] hash_ctx the hashing context */ -static void hash_filtering_path(cx_hash_t *const hash_ctx) { +static void hash_filtering_path(cx_hash_t *hash_ctx) { const void *field_ptr; const char *key; uint8_t key_len; @@ -45,68 +50,51 @@ static void hash_filtering_path(cx_hash_t *const hash_ctx) { } /** - * Verify the provided signature + * Begin the hashing for signature verification * - * @param[in] dname_length length of provided substitution name - * @param[in] dname provided substitution name - * @param[in] sig_length provided signature length - * @param[in] sig pointer to the provided signature - * @param[in] type the type of filtering - * @return whether the signature verification worked or not + * @param[in] hash_ctx hashing context + * @param[in] magic magic number used in the signature + * @return \ref true */ -static bool verify_filtering_signature(uint8_t dname_length, - const char *const dname, - uint8_t sig_length, - const uint8_t *const sig, - e_filtering_type type) { - uint8_t hash[INT256_LENGTH]; - cx_ecfp_public_key_t verifying_key; - cx_sha256_t hash_ctx; +static bool sig_verif_start(cx_sha256_t *hash_ctx, uint8_t magic) { uint64_t chain_id; - cx_err_t error = CX_INTERNAL_ERROR; - cx_sha256_init(&hash_ctx); + cx_sha256_init(hash_ctx); // Magic number, makes it so a signature of one type can't be used as another - switch (type) { - case FILTERING_SHOW_FIELD: - hash_byte(FILTERING_MAGIC_STRUCT_FIELD, (cx_hash_t *) &hash_ctx); - break; - case FILTERING_PROVIDE_MESSAGE_INFO: - hash_byte(FILTERING_MAGIC_CONTRACT_NAME, (cx_hash_t *) &hash_ctx); - break; - default: - apdu_response_code = APDU_RESPONSE_INVALID_DATA; - PRINTF("Invalid filtering type when verifying signature!\n"); - return false; - } + hash_byte(magic, (cx_hash_t *) hash_ctx); // Chain ID chain_id = __builtin_bswap64(eip712_context->chain_id); - hash_nbytes((uint8_t *) &chain_id, sizeof(chain_id), (cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) &chain_id, sizeof(chain_id), (cx_hash_t *) hash_ctx); // Contract address hash_nbytes(eip712_context->contract_addr, sizeof(eip712_context->contract_addr), - (cx_hash_t *) &hash_ctx); + (cx_hash_t *) hash_ctx); // Schema hash hash_nbytes(eip712_context->schema_hash, sizeof(eip712_context->schema_hash), - (cx_hash_t *) &hash_ctx); + (cx_hash_t *) hash_ctx); + return true; +} - 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 - hash_nbytes((uint8_t *) dname, sizeof(char) * dname_length, (cx_hash_t *) &hash_ctx); +/** + * End the hashing & do the signature verification + * + * @param[in] hash_ctx hashing context + * @param[in] sig signature + * @param[in] sig_length signature length + * @return whether the signature verification worked or not + */ +static bool sig_verif_end(cx_sha256_t *hash_ctx, const uint8_t *sig, uint8_t sig_length) { + uint8_t hash[INT256_LENGTH]; + cx_ecfp_public_key_t verifying_key; + cx_err_t error = CX_INTERNAL_ERROR; // Finalize hash - CX_CHECK(cx_hash_no_throw((cx_hash_t *) &hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); + CX_CHECK(cx_hash_no_throw((cx_hash_t *) hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); CX_CHECK(cx_ecfp_init_public_key_no_throw(CX_CURVE_256K1, LEDGER_SIGNATURE_PUBLIC_KEY, @@ -125,65 +113,281 @@ end: } /** - * Provide filtering information about upcoming struct field + * Check if the given token index is valid * - * @param[in] payload the raw data received - * @param[in] length payload length - * @param[in] type the type of filtering - * @return if everything went well or not + * @param[in] idx token index + * @return whether the index is valid or not */ -bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filtering_type type) { - bool ret = false; - uint8_t dname_len; - const char *dname; +static bool check_token_index(uint8_t idx) { + if (idx >= MAX_ASSETS) { + PRINTF("Error: token index out of range (%u)\n", idx); + return false; + } + if (!tmpCtx.transactionContext.assetSet[idx]) { + PRINTF("Error: token not set (%u)\n", idx); + return false; + } + return true; +} + +/** + * Check if the current element's typename matches the expected one + * + * @param[in] expected the typename we expect + * @return whether it is a match or not + */ +static bool check_typename(const char *expected) { + uint8_t typename_len = 0; + const char *typename; + + typename = get_struct_field_typename(path_get_field(), &typename_len); + if ((typename_len != strlen(expected)) || (strncmp(typename, expected, typename_len) != 0)) { + PRINTF("Error: expected field of type \"%s\" but got \"", expected); + for (int i = 0; i < typename_len; ++i) PRINTF("%c", typename[i]); + PRINTF("\" instead.\n"); + return false; + } + return true; +} + +/** + * Command to give the message information + * + * @param[in] payload the payload to parse + * @param[in] length the payload length + * @return whether it was successful or not + */ +bool filtering_message_info(const uint8_t *payload, uint8_t length) { + uint8_t name_len; + const char *name; + uint8_t filters_count; uint8_t sig_len; const uint8_t *sig; uint8_t offset = 0; - 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_SHOW_FIELD - { - if (path_get_root_type() != ROOT_MESSAGE) { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return false; - } + if (path_get_root_type() != ROOT_DOMAIN) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; } - if (length > 0) { - dname_len = payload[offset++]; - if ((1 + dname_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_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_SHOW_FIELD - { - if (dname_len > 0) // don't substitute for an empty name - { - ui_712_set_title(dname, dname_len); - } - ui_712_flag_field(true, dname_len > 0); - } - } - } - } + + // Parsing + if ((offset + sizeof(name_len)) > length) { + return false; } - return ret; + name_len = payload[offset++]; + if ((offset + name_len) > length) { + return false; + } + name = (char *) &payload[offset]; + offset += name_len; + if ((offset + sizeof(filters_count)) > length) { + return false; + } + filters_count = payload[offset++]; + if ((offset + sizeof(sig_len)) > length) { + return false; + } + sig_len = payload[offset++]; + if ((offset + sig_len) != length) { + return false; + } + sig = &payload[offset]; + + // Verification + cx_sha256_t hash_ctx; + if (!sig_verif_start(&hash_ctx, FILT_MAGIC_MESSAGE_INFO)) { + return false; + } + hash_byte(filters_count, (cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); + if (!sig_verif_end(&hash_ctx, sig, sig_len)) { + return false; + } + + // Handling + ui_712_set_filters_count(filters_count); + if (!N_storage.verbose_eip712) { + ui_712_set_title("Contract", 8); + ui_712_set_value(name, name_len); + ui_712_redraw_generic_step(); + } + return true; +} + +/** + * Command to display a field as an amount-join (token part) + * + * @param[in] payload the payload to parse + * @param[in] length the payload length + * @return whether it was successful or not + */ +bool filtering_amount_join_token(const uint8_t *payload, uint8_t length) { + uint8_t token_idx; + uint8_t sig_len; + const uint8_t *sig; + uint8_t offset = 0; + + if (path_get_root_type() != ROOT_MESSAGE) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + + // Parsing + if ((offset + sizeof(token_idx)) > length) { + return false; + } + token_idx = payload[offset++]; + if ((offset + sizeof(sig_len)) > length) { + return false; + } + sig_len = payload[offset++]; + if ((offset + sig_len) != length) { + return false; + } + sig = &payload[offset]; + + // Verification + cx_sha256_t hash_ctx; + if (!sig_verif_start(&hash_ctx, FILT_MAGIC_AMOUNT_JOIN_TOKEN)) { + return false; + } + hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_byte(token_idx, (cx_hash_t *) &hash_ctx); + if (!sig_verif_end(&hash_ctx, sig, sig_len)) { + return false; + } + + // Handling + if (!check_typename("address") || !check_token_index(token_idx)) { + return false; + } + ui_712_flag_field(false, false, true); + ui_712_token_join_prepare_addr_check(token_idx); + return true; +} + +/** + * Command to display a field as an amount-join (value part) + * + * @param[in] payload the payload to parse + * @param[in] length the payload length + * @return whether it was successful or not + */ +bool filtering_amount_join_value(const uint8_t *payload, uint8_t length) { + uint8_t name_len; + const char *name; + uint8_t token_idx; + uint8_t sig_len; + const uint8_t *sig; + uint8_t offset = 0; + + if (path_get_root_type() != ROOT_MESSAGE) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + + // Parsing + if ((offset + sizeof(name_len)) > length) { + return false; + } + name_len = payload[offset++]; + if ((offset + name_len) > length) { + return false; + } + if (name_len == 0) { + return false; + } + name = (char *) &payload[offset]; + offset += name_len; + if ((offset + sizeof(token_idx)) > length) { + return false; + } + token_idx = payload[offset++]; + if ((offset + sizeof(sig_len)) > length) { + return false; + } + sig_len = payload[offset++]; + if ((offset + sig_len) != length) { + return false; + } + sig = &payload[offset]; + + // Verification + cx_sha256_t hash_ctx; + if (!sig_verif_start(&hash_ctx, FILT_MAGIC_AMOUNT_JOIN_VALUE)) { + return false; + } + hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); + hash_byte(token_idx, (cx_hash_t *) &hash_ctx); + if (!sig_verif_end(&hash_ctx, sig, sig_len)) { + return false; + } + + // Handling + if (!check_typename("uint") || !check_token_index(token_idx)) { + return false; + } + ui_712_flag_field(false, false, true); + ui_712_token_join_prepare_amount(token_idx, name, name_len); + return true; +} + +/** + * Command to display a field raw (without formatting) + * + * @param[in] payload the payload to parse + * @param[in] length the payload length + * @return whether it was successful or not + */ +bool filtering_raw_field(const uint8_t *payload, uint8_t length) { + uint8_t name_len; + const char *name; + uint8_t sig_len; + const uint8_t *sig; + uint8_t offset = 0; + + if (path_get_root_type() != ROOT_MESSAGE) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + + // Parsing + if ((offset + sizeof(name_len)) > length) { + return false; + } + name_len = payload[offset++]; + if ((offset + name_len) > length) { + return false; + } + name = (char *) &payload[offset]; + offset += name_len; + if ((offset + sizeof(sig_len)) > length) { + return false; + } + sig_len = payload[offset++]; + if ((offset + sig_len) != length) { + return false; + } + sig = &payload[offset]; + + // Verification + cx_sha256_t hash_ctx; + if (!sig_verif_start(&hash_ctx, FILT_MAGIC_RAW_FIELD)) { + return false; + } + hash_filtering_path((cx_hash_t *) &hash_ctx); + hash_nbytes((uint8_t *) name, sizeof(char) * name_len, (cx_hash_t *) &hash_ctx); + if (!sig_verif_end(&hash_ctx, sig, sig_len)) { + return false; + } + + // Handling + if (name_len > 0) { // don't substitute for an empty name + ui_712_set_title(name, name_len); + } + ui_712_flag_field(true, name_len > 0, false); + return true; } #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index 3e33b9a..8f0e271 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -6,12 +6,10 @@ #include #include -#define FILTERING_MAGIC_CONTRACT_NAME 0b10110111 // 183 -#define FILTERING_MAGIC_STRUCT_FIELD 0b01001000 // ~183 = 72 - -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); +bool filtering_message_info(const uint8_t *payload, uint8_t length); +bool filtering_amount_join_token(const uint8_t *payload, uint8_t length); +bool filtering_amount_join_value(const uint8_t *payload, uint8_t length); +bool filtering_raw_field(const uint8_t *payload, uint8_t length); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 7f5843c..7c7dff5 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -18,6 +18,44 @@ #include "common_ui.h" #include "uint_common.h" +#define AMOUNT_JOIN_FLAG_TOKEN (1 << 0) +#define AMOUNT_JOIN_FLAG_VALUE (1 << 1) + +typedef struct { + // display name, not NULL-terminated + char name[25]; + uint8_t name_length; + uint8_t value[INT256_LENGTH]; + uint8_t value_length; + // indicates the steps the token join has gone through + uint8_t flags; +} s_amount_join; + +typedef enum { + AMOUNT_JOIN_STATE_TOKEN, + AMOUNT_JOIN_STATE_VALUE, +} e_amount_join_state; + +#define UI_712_FIELD_SHOWN (1 << 0) +#define UI_712_FIELD_NAME_PROVIDED (1 << 1) +#define UI_712_AMOUNT_JOIN (1 << 2) + +typedef struct { + s_amount_join joins[MAX_ASSETS]; + uint8_t idx; + e_amount_join_state state; +} s_amount_context; + +typedef struct { + bool shown; + bool end_reached; + uint8_t filtering_mode; + uint8_t filters_to_process; + uint8_t field_flags; + uint8_t structs_to_review; + s_amount_context amount; +} t_ui_context; + static t_ui_context *ui_ctx = NULL; /** @@ -32,8 +70,7 @@ static bool ui_712_field_shown(void) { if (N_storage.verbose_eip712 || (path_get_root_type() == ROOT_DOMAIN)) { ret = true; } - } else // EIP712_FILTERING_FULL - { + } else { // EIP712_FILTERING_FULL if (ui_ctx->field_flags & UI_712_FIELD_SHOWN) { ret = true; } @@ -103,8 +140,7 @@ void ui_712_set_value(const char *const str, uint8_t length) { * Redraw the dynamic UI step that shows EIP712 information */ void ui_712_redraw_generic_step(void) { - if (!ui_ctx->shown) // Initialize if it is not already - { + if (!ui_ctx->shown) { // Initialize if it is not already ui_712_start(); ui_ctx->shown = true; } else { @@ -334,6 +370,62 @@ static void ui_712_format_uint(const uint8_t *const data, uint8_t length) { } } +/** + * Format given data as an amount with its ticker and value with correct decimals + * + * @return whether it was successful or not + */ +static bool ui_712_format_amount_join(void) { + const tokenDefinition_t *token; + token = &tmpCtx.transactionContext.extraInfo[ui_ctx->amount.idx].token; + + if (!amountToString(ui_ctx->amount.joins[ui_ctx->amount.idx].value, + ui_ctx->amount.joins[ui_ctx->amount.idx].value_length, + token->decimals, + token->ticker, + strings.tmp.tmp, + sizeof(strings.tmp.tmp))) { + return false; + } + ui_ctx->field_flags |= UI_712_FIELD_SHOWN; + ui_712_set_title(ui_ctx->amount.joins[ui_ctx->amount.idx].name, + ui_ctx->amount.joins[ui_ctx->amount.idx].name_length); + explicit_bzero(&ui_ctx->amount.joins[ui_ctx->amount.idx], + sizeof(ui_ctx->amount.joins[ui_ctx->amount.idx])); + return true; +} + +/** + * Update the state of the amount-join + * + * @param[in] data the data that needs formatting + * @param[in] length its length + * @return whether it was successful or not + */ +static bool update_amount_join(const uint8_t *data, uint8_t length) { + tokenDefinition_t *token; + + token = &tmpCtx.transactionContext.extraInfo[ui_ctx->amount.idx].token; + switch (ui_ctx->amount.state) { + case AMOUNT_JOIN_STATE_TOKEN: + if (memcmp(data, token->address, ADDRESS_LENGTH) != 0) { + return false; + } + ui_ctx->amount.joins[ui_ctx->amount.idx].flags |= AMOUNT_JOIN_FLAG_TOKEN; + break; + + case AMOUNT_JOIN_STATE_VALUE: + memcpy(ui_ctx->amount.joins[ui_ctx->amount.idx].value, data, length); + ui_ctx->amount.joins[ui_ctx->amount.idx].value_length = length; + ui_ctx->amount.joins[ui_ctx->amount.idx].flags |= AMOUNT_JOIN_FLAG_VALUE; + break; + + default: + return false; + } + return true; +} + /** * Used to notify of a new field to review in the current struct (key + value) * @@ -392,6 +484,19 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, ui return false; } + if (ui_ctx->field_flags & UI_712_AMOUNT_JOIN) { + if (!update_amount_join(data, length)) { + return false; + } + + if (ui_ctx->amount.joins[ui_ctx->amount.idx].flags == + (AMOUNT_JOIN_FLAG_TOKEN | AMOUNT_JOIN_FLAG_VALUE)) { + if (!ui_712_format_amount_join()) { + return false; + } + } + } + // Check if this field is supposed to be displayed if (ui_712_field_shown()) { ui_712_redraw_generic_step(); @@ -423,6 +528,7 @@ bool ui_712_init(void) { ui_ctx->shown = false; ui_ctx->end_reached = false; ui_ctx->filtering_mode = EIP712_FILTERING_BASIC; + explicit_bzero(&ui_ctx->amount, sizeof(ui_ctx->amount)); } else { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; } @@ -466,14 +572,18 @@ unsigned int ui_712_reject() { * * @param[in] show if this field should be shown on the device * @param[in] name_provided if a substitution name has been provided + * @param[in] token_join if this field is part of a token join */ -void ui_712_flag_field(bool show, bool name_provided) { +void ui_712_flag_field(bool show, bool name_provided, bool token_join) { if (show) { ui_ctx->field_flags |= UI_712_FIELD_SHOWN; } if (name_provided) { ui_ctx->field_flags |= UI_712_FIELD_NAME_PROVIDED; } + if (token_join) { + ui_ctx->field_flags |= UI_712_AMOUNT_JOIN; + } } /** @@ -547,4 +657,18 @@ void ui_712_notify_filter_change(void) { } } +void ui_712_token_join_prepare_addr_check(uint8_t index) { + ui_ctx->amount.idx = index; + ui_ctx->amount.state = AMOUNT_JOIN_STATE_TOKEN; +} + +void ui_712_token_join_prepare_amount(uint8_t index, const char *name, uint8_t name_length) { + uint8_t cpy_len = MIN(sizeof(ui_ctx->amount.joins[index].name), name_length); + + ui_ctx->amount.idx = index; + ui_ctx->amount.state = AMOUNT_JOIN_STATE_VALUE; + memcpy(ui_ctx->amount.joins[index].name, name, cpy_len); + ui_ctx->amount.joins[index].name_length = cpy_len; +} + #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 0b651e6..9f0e85b 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -5,9 +5,7 @@ #include #include "ux.h" - -#define UI_712_FIELD_SHOWN (1 << 0) -#define UI_712_FIELD_NAME_PROVIDED (1 << 1) +#include "uint256.h" typedef enum { EIP712_FILTERING_BASIC, EIP712_FILTERING_FULL } e_eip712_filtering_mode; typedef enum { @@ -16,15 +14,6 @@ typedef enum { EIP712_NO_MORE_FIELD } e_eip712_nfs; // next field state -typedef struct { - bool shown; - bool end_reached; - uint8_t filtering_mode; - uint8_t filters_to_process; - uint8_t field_flags; - uint8_t structs_to_review; -} t_ui_context; - bool ui_712_init(void); void ui_712_deinit(void); e_eip712_nfs ui_712_next_field(void); @@ -37,7 +26,7 @@ void ui_712_set_title(const char *const str, uint8_t length); void ui_712_set_value(const char *const str, uint8_t length); void ui_712_message_hash(void); void ui_712_redraw_generic_step(void); -void ui_712_flag_field(bool show, bool name_provided); +void ui_712_flag_field(bool show, bool name_provided, bool token_join); void ui_712_field_flags_reset(void); void ui_712_finalize_field(void); void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); @@ -46,6 +35,8 @@ 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); +void ui_712_token_join_prepare_addr_check(uint8_t index); +void ui_712_token_join_prepare_amount(uint8_t index, const char *name, uint8_t name_length); #endif // HAVE_EIP712_FULL_SUPPORT