Amount-join EIP-712 filtering implementation

This commit is contained in:
Alexandre Paillier
2024-04-12 11:09:06 +02:00
parent c68b4186ed
commit e76e429148
6 changed files with 486 additions and 162 deletions

View File

@@ -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);

View File

@@ -6,20 +6,6 @@
#include <stdbool.h>
#include <stdint.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_SHOW_FIELD 0xFF
#define DOMAIN_STRUCT_NAME "EIP712Domain"
bool handle_eip712_struct_def(const uint8_t *const apdu_buf);

View File

@@ -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

View File

@@ -6,12 +6,10 @@
#include <stdbool.h>
#include <stdint.h>
#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

View File

@@ -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

View File

@@ -5,9 +5,7 @@
#include <stdint.h>
#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