Amount-join EIP-712 filtering implementation
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user