Merge pull request #593 from LedgerHQ/feat/apa/eip712_buffer_pairs_to_fill_screen
EIP-712 buffer pairs to fill screen
@@ -23,6 +23,7 @@ def default_handler():
|
||||
|
||||
|
||||
autonext_handler: Callable = default_handler
|
||||
is_golden_run: bool
|
||||
|
||||
|
||||
# From a string typename, extract the type and all the array depth
|
||||
@@ -119,7 +120,7 @@ def send_struct_def_field(typename, keyname):
|
||||
return (typename, type_enum, typesize, array_lvls)
|
||||
|
||||
|
||||
def encode_integer(value: Union[str | int], typesize: int) -> bytes:
|
||||
def encode_integer(value: Union[str, int], typesize: int) -> bytes:
|
||||
# Some are already represented as integers in the JSON, but most as strings
|
||||
if isinstance(value, str):
|
||||
value = int(value, 0)
|
||||
@@ -394,6 +395,12 @@ def enable_autonext():
|
||||
delay = 1/3
|
||||
else:
|
||||
delay = 1/4
|
||||
|
||||
# golden run has to be slower to make sure we take good snapshots
|
||||
# and not processing/loading screens
|
||||
if is_golden_run:
|
||||
delay *= 3
|
||||
|
||||
signal.setitimer(signal.ITIMER_REAL, delay, delay)
|
||||
|
||||
|
||||
@@ -404,10 +411,12 @@ def disable_autonext():
|
||||
def process_data(aclient: EthAppClient,
|
||||
data_json: dict,
|
||||
filters: Optional[dict] = None,
|
||||
autonext: Optional[Callable] = None) -> bool:
|
||||
autonext: Optional[Callable] = None,
|
||||
golden_run: bool = False) -> bool:
|
||||
global sig_ctx
|
||||
global app_client
|
||||
global autonext_handler
|
||||
global is_golden_run
|
||||
|
||||
# deepcopy because this function modifies the dict
|
||||
data_json = copy.deepcopy(data_json)
|
||||
@@ -422,6 +431,8 @@ def process_data(aclient: EthAppClient,
|
||||
autonext_handler = autonext
|
||||
signal.signal(signal.SIGALRM, next_timeout)
|
||||
|
||||
is_golden_run = golden_run
|
||||
|
||||
if filters:
|
||||
init_signature_context(types, domain)
|
||||
|
||||
|
||||
@@ -138,8 +138,12 @@ typedef struct txStringProperties_s {
|
||||
#ifdef TARGET_NANOS
|
||||
#define SHARED_CTX_FIELD_1_SIZE 100
|
||||
#else
|
||||
#ifdef SCREEN_SIZE_WALLET
|
||||
#define SHARED_CTX_FIELD_1_SIZE 380
|
||||
#else
|
||||
#define SHARED_CTX_FIELD_1_SIZE 256
|
||||
#endif
|
||||
#endif
|
||||
#define SHARED_CTX_FIELD_2_SIZE 40
|
||||
|
||||
typedef struct strDataTmp_s {
|
||||
|
||||
@@ -61,7 +61,6 @@ static const uint8_t *field_hash_prepare(const void *const field_ptr,
|
||||
fh->state = FHS_WAITING_FOR_MORE;
|
||||
if (IS_DYN(field_type)) {
|
||||
CX_CHECK(cx_keccak_init_no_throw(&global_sha3, 256));
|
||||
ui_712_new_field(field_ptr, data, *data_length);
|
||||
}
|
||||
return data;
|
||||
end:
|
||||
@@ -106,11 +105,6 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr,
|
||||
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
|
||||
PRINTF("Unknown solidity type!\n");
|
||||
}
|
||||
|
||||
if (value == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
ui_712_new_field(field_ptr, data, data_length);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -245,6 +239,7 @@ static bool field_hash_finalize(const void *const field_ptr,
|
||||
bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) {
|
||||
const void *field_ptr;
|
||||
e_type field_type;
|
||||
bool first = fh->state == FHS_IDLE;
|
||||
|
||||
if ((fh == NULL) || ((field_ptr = path_get_field()) == NULL)) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
@@ -252,8 +247,11 @@ bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) {
|
||||
}
|
||||
|
||||
field_type = struct_field_type(field_ptr);
|
||||
if (fh->state == FHS_IDLE) // first packet for this frame
|
||||
{
|
||||
// first packet for this frame
|
||||
if (first) {
|
||||
if (!ui_712_show_raw_key(field_ptr)) {
|
||||
return false;
|
||||
}
|
||||
if (data_length < 2) {
|
||||
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
|
||||
return false;
|
||||
@@ -270,6 +268,9 @@ bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) {
|
||||
if (IS_DYN(field_type)) {
|
||||
hash_nbytes(data, data_length, (cx_hash_t *) &global_sha3);
|
||||
}
|
||||
if (!ui_712_feed_to_display(field_ptr, data, data_length, first, fh->remaining_size == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (fh->remaining_size == 0) {
|
||||
if (partial) // only makes sense if marked as complete
|
||||
{
|
||||
@@ -287,7 +288,6 @@ bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) {
|
||||
}
|
||||
handle_eip712_return_code(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ typedef struct {
|
||||
uint8_t field_flags;
|
||||
uint8_t structs_to_review;
|
||||
s_amount_context amount;
|
||||
#ifdef SCREEN_SIZE_WALLET
|
||||
char ui_pairs_buffer[(SHARED_CTX_FIELD_1_SIZE + SHARED_CTX_FIELD_2_SIZE) * 2];
|
||||
#endif
|
||||
} t_ui_context;
|
||||
|
||||
static t_ui_context *ui_ctx = NULL;
|
||||
@@ -89,9 +92,9 @@ static bool ui_712_field_shown(void) {
|
||||
* @param[in] dst_length destination buffer length
|
||||
* @param[in] explicit_trunc if truncation should be explicitly shown
|
||||
*/
|
||||
static void ui_712_set_buf(const char *const src,
|
||||
static void ui_712_set_buf(const char *src,
|
||||
size_t src_length,
|
||||
char *const dst,
|
||||
char *dst,
|
||||
size_t dst_length,
|
||||
bool explicit_trunc) {
|
||||
uint8_t cpy_length;
|
||||
@@ -124,7 +127,7 @@ void ui_712_finalize_field(void) {
|
||||
* @param[in] str the new title
|
||||
* @param[in] length its length
|
||||
*/
|
||||
void ui_712_set_title(const char *const str, uint8_t length) {
|
||||
void ui_712_set_title(const char *str, size_t length) {
|
||||
ui_712_set_buf(str, length, strings.tmp.tmp2, sizeof(strings.tmp.tmp2), false);
|
||||
}
|
||||
|
||||
@@ -134,7 +137,7 @@ void ui_712_set_title(const char *const str, uint8_t length) {
|
||||
* @param[in] str the new value
|
||||
* @param[in] length its length
|
||||
*/
|
||||
void ui_712_set_value(const char *const str, uint8_t length) {
|
||||
void ui_712_set_value(const char *str, size_t length) {
|
||||
ui_712_set_buf(str, length, strings.tmp.tmp, sizeof(strings.tmp.tmp), true);
|
||||
}
|
||||
|
||||
@@ -170,6 +173,9 @@ e_eip712_nfs ui_712_next_field(void) {
|
||||
} else if (!ui_ctx->end_reached) {
|
||||
handle_eip712_return_code(true);
|
||||
state = EIP712_FIELD_INCOMING;
|
||||
// So that later when we append to them, we start from an empty string
|
||||
explicit_bzero(strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
explicit_bzero(strings.tmp.tmp2, sizeof(strings.tmp.tmp2));
|
||||
}
|
||||
}
|
||||
return state;
|
||||
@@ -180,10 +186,10 @@ e_eip712_nfs ui_712_next_field(void) {
|
||||
*
|
||||
* @param[in] struct_ptr pointer to the structure to be shown
|
||||
*/
|
||||
void ui_712_review_struct(const void *const struct_ptr) {
|
||||
void ui_712_review_struct(const void *struct_ptr) {
|
||||
const char *struct_name;
|
||||
uint8_t struct_name_length;
|
||||
const char *const title = "Review struct";
|
||||
const char *title = "Review struct";
|
||||
|
||||
if (ui_ctx == NULL) {
|
||||
return;
|
||||
@@ -200,13 +206,14 @@ void ui_712_review_struct(const void *const struct_ptr) {
|
||||
* Show the hash of the message on the generic UI step
|
||||
*/
|
||||
void ui_712_message_hash(void) {
|
||||
const char *const title = "Message hash";
|
||||
const char *title = "Message hash";
|
||||
|
||||
ui_712_set_title(title, strlen(title));
|
||||
array_bytes_string(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
tmpCtx.messageSigningContext712.messageHash,
|
||||
KECCAK256_HASH_BYTESIZE);
|
||||
ui_ctx->end_reached = true;
|
||||
ui_712_redraw_generic_step();
|
||||
}
|
||||
|
||||
@@ -215,10 +222,16 @@ void ui_712_message_hash(void) {
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] last if this is the last chunk
|
||||
*/
|
||||
static void ui_712_format_str(const uint8_t *const data, uint8_t length) {
|
||||
if (ui_712_field_shown()) {
|
||||
ui_712_set_value((char *) data, length);
|
||||
static void ui_712_format_str(const uint8_t *data, uint8_t length, bool last) {
|
||||
size_t max_len = sizeof(strings.tmp.tmp) - 1;
|
||||
size_t cur_len = strlen(strings.tmp.tmp);
|
||||
|
||||
memcpy(strings.tmp.tmp + cur_len, data, MIN(max_len - cur_len, length));
|
||||
// truncated
|
||||
if (last && ((max_len - cur_len) < length)) {
|
||||
memcpy(strings.tmp.tmp + max_len - 3, "...", 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,20 +240,23 @@ static void ui_712_format_str(const uint8_t *const data, uint8_t length) {
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @return if the formatting was successful
|
||||
*/
|
||||
static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) {
|
||||
static bool ui_712_format_addr(const uint8_t *data, uint8_t length, bool first) {
|
||||
// no reason for an address to be received over multiple chunks
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
if (length != ADDRESS_LENGTH) {
|
||||
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
|
||||
return false;
|
||||
}
|
||||
if (ui_712_field_shown()) {
|
||||
if (!getEthDisplayableAddress((uint8_t *) data,
|
||||
strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
chainConfig->chainId)) {
|
||||
THROW(APDU_RESPONSE_ERROR_NO_INFO);
|
||||
}
|
||||
if (!getEthDisplayableAddress((uint8_t *) data,
|
||||
strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
chainConfig->chainId)) {
|
||||
THROW(APDU_RESPONSE_ERROR_NO_INFO);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -250,21 +266,25 @@ static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) {
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @return if the formatting was successful
|
||||
*/
|
||||
static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) {
|
||||
const char *const true_str = "true";
|
||||
const char *const false_str = "false";
|
||||
static bool ui_712_format_bool(const uint8_t *data, uint8_t length, bool first) {
|
||||
size_t max_len = sizeof(strings.tmp.tmp) - 1;
|
||||
const char *true_str = "true";
|
||||
const char *false_str = "false";
|
||||
const char *str;
|
||||
|
||||
// no reason for a boolean to be received over multiple chunks
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
if (length != 1) {
|
||||
apdu_response_code = APDU_RESPONSE_INVALID_DATA;
|
||||
return false;
|
||||
}
|
||||
str = *data ? true_str : false_str;
|
||||
if (ui_712_field_shown()) {
|
||||
ui_712_set_value(str, strlen(str));
|
||||
}
|
||||
memcpy(strings.tmp.tmp, str, MIN(max_len, strlen(str)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -273,17 +293,29 @@ static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) {
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @param[in] last if this is the last chunk
|
||||
* @return if the formatting was successful
|
||||
*/
|
||||
static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) {
|
||||
if (ui_712_field_shown()) {
|
||||
array_bytes_string(strings.tmp.tmp, sizeof(strings.tmp.tmp), data, length);
|
||||
// +2 for the "0x"
|
||||
// x2 for each byte value is represented by 2 ASCII characters
|
||||
if ((2 + (length * 2)) > (sizeof(strings.tmp.tmp) - 1)) {
|
||||
strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - 3] = '\0';
|
||||
strlcat(strings.tmp.tmp, "...", sizeof(strings.tmp.tmp));
|
||||
}
|
||||
static bool ui_712_format_bytes(const uint8_t *data, uint8_t length, bool first, bool last) {
|
||||
size_t max_len = sizeof(strings.tmp.tmp) - 1;
|
||||
size_t cur_len = strlen(strings.tmp.tmp);
|
||||
|
||||
if (first) {
|
||||
memcpy(strings.tmp.tmp, "0x", MIN(max_len, 2));
|
||||
cur_len += 2;
|
||||
}
|
||||
if (format_hex(data,
|
||||
MIN((max_len - cur_len) / 2, length),
|
||||
strings.tmp.tmp + cur_len,
|
||||
max_len + 1 - cur_len) < 0) {
|
||||
return false;
|
||||
}
|
||||
// truncated
|
||||
if (last && (((max_len - cur_len) / 2) < length)) {
|
||||
memcpy(strings.tmp.tmp + max_len - 3, "...", 3);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,63 +323,58 @@ static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) {
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @param[in] field_ptr pointer to the EIP-712 field
|
||||
* @return if the formatting was successful
|
||||
*/
|
||||
static bool ui_712_format_int(const uint8_t *const data,
|
||||
static bool ui_712_format_int(const uint8_t *data,
|
||||
uint8_t length,
|
||||
const void *const field_ptr) {
|
||||
bool first,
|
||||
const void *field_ptr) {
|
||||
uint256_t value256;
|
||||
uint128_t value128;
|
||||
int32_t value32;
|
||||
int16_t value16;
|
||||
|
||||
// no reason for an integer to be received over multiple chunks
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
switch (get_struct_field_typesize(field_ptr) * 8) {
|
||||
case 256:
|
||||
convertUint256BE(data, length, &value256);
|
||||
if (ui_712_field_shown()) {
|
||||
tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
}
|
||||
tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
break;
|
||||
case 128:
|
||||
convertUint128BE(data, length, &value128);
|
||||
if (ui_712_field_shown()) {
|
||||
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
}
|
||||
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
break;
|
||||
case 64:
|
||||
convertUint64BEto128(data, length, &value128);
|
||||
if (ui_712_field_shown()) {
|
||||
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
}
|
||||
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
break;
|
||||
case 32:
|
||||
value32 = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
((uint8_t *) &value32)[length - 1 - i] = data[i];
|
||||
}
|
||||
if (ui_712_field_shown()) {
|
||||
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", value32);
|
||||
}
|
||||
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", value32);
|
||||
break;
|
||||
case 16:
|
||||
value16 = 0;
|
||||
for (int i = 0; i < length; ++i) {
|
||||
((uint8_t *) &value16)[length - 1 - i] = data[i];
|
||||
}
|
||||
if (ui_712_field_shown()) {
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"%d",
|
||||
value16); // expanded to 32 bits
|
||||
}
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"%d",
|
||||
value16); // expanded to 32 bits
|
||||
break;
|
||||
case 8:
|
||||
if (ui_712_field_shown()) {
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"%d",
|
||||
((int8_t *) data)[0]); // expanded to 32 bits
|
||||
}
|
||||
snprintf(strings.tmp.tmp,
|
||||
sizeof(strings.tmp.tmp),
|
||||
"%d",
|
||||
((int8_t *) data)[0]); // expanded to 32 bits
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unhandled field typesize\n");
|
||||
@@ -362,14 +389,19 @@ static bool ui_712_format_int(const uint8_t *const data,
|
||||
*
|
||||
* @param[in] data the data that needs formatting
|
||||
* @param[in] length its length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @return if the formatting was successful
|
||||
*/
|
||||
static void ui_712_format_uint(const uint8_t *const data, uint8_t length) {
|
||||
static bool ui_712_format_uint(const uint8_t *data, uint8_t length, bool first) {
|
||||
uint256_t value256;
|
||||
|
||||
convertUint256BE(data, length, &value256);
|
||||
if (ui_712_field_shown()) {
|
||||
tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
// no reason for an integer to be received over multiple chunks
|
||||
if (!first) {
|
||||
return false;
|
||||
}
|
||||
convertUint256BE(data, length, &value256);
|
||||
tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -472,61 +504,63 @@ static bool ui_712_format_datetime(const uint8_t *data, uint8_t length) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to notify of a new field to review in the current struct (key + value)
|
||||
* Formats and feeds the given input data to the display buffers
|
||||
*
|
||||
* @param[in] field_ptr pointer to the new struct field
|
||||
* @param[in] data pointer to the field's raw value
|
||||
* @param[in] length field's raw value byte-length
|
||||
* @param[in] first if this is the first chunk
|
||||
* @param[in] last if this is the last chunk
|
||||
*/
|
||||
bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length) {
|
||||
const char *key;
|
||||
uint8_t key_len;
|
||||
|
||||
bool ui_712_feed_to_display(const void *field_ptr,
|
||||
const uint8_t *data,
|
||||
uint8_t length,
|
||||
bool first,
|
||||
bool last) {
|
||||
if (ui_ctx == NULL) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Key
|
||||
if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
if (first && (strlen(strings.tmp.tmp) > 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui_712_field_shown() && !(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) {
|
||||
ui_712_set_title(key, key_len);
|
||||
}
|
||||
|
||||
// Value
|
||||
switch (struct_field_type(field_ptr)) {
|
||||
case TYPE_SOL_STRING:
|
||||
ui_712_format_str(data, length);
|
||||
break;
|
||||
case TYPE_SOL_ADDRESS:
|
||||
if (ui_712_format_addr(data, length) == false) {
|
||||
if (ui_712_field_shown()) {
|
||||
switch (struct_field_type(field_ptr)) {
|
||||
case TYPE_SOL_STRING:
|
||||
ui_712_format_str(data, length, last);
|
||||
break;
|
||||
case TYPE_SOL_ADDRESS:
|
||||
if (ui_712_format_addr(data, length, first) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_BOOL:
|
||||
if (ui_712_format_bool(data, length, first) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_BYTES_FIX:
|
||||
case TYPE_SOL_BYTES_DYN:
|
||||
if (ui_712_format_bytes(data, length, first, last) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_INT:
|
||||
if (ui_712_format_int(data, length, first, field_ptr) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_UINT:
|
||||
if (ui_712_format_uint(data, length, first) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unhandled type\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_BOOL:
|
||||
if (ui_712_format_bool(data, length) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_BYTES_FIX:
|
||||
case TYPE_SOL_BYTES_DYN:
|
||||
ui_712_format_bytes(data, length);
|
||||
break;
|
||||
case TYPE_SOL_INT:
|
||||
if (ui_712_format_int(data, length, field_ptr) == false) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case TYPE_SOL_UINT:
|
||||
ui_712_format_uint(data, length);
|
||||
break;
|
||||
default:
|
||||
PRINTF("Unhandled type\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ui_ctx->field_flags & UI_712_AMOUNT_JOIN) {
|
||||
@@ -549,7 +583,7 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, ui
|
||||
}
|
||||
|
||||
// Check if this field is supposed to be displayed
|
||||
if (ui_712_field_shown()) {
|
||||
if (last && ui_712_field_shown()) {
|
||||
ui_712_redraw_generic_step();
|
||||
}
|
||||
return true;
|
||||
@@ -564,9 +598,9 @@ void ui_712_end_sign(void) {
|
||||
apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED;
|
||||
return;
|
||||
}
|
||||
ui_ctx->end_reached = true;
|
||||
|
||||
if (N_storage.verbose_eip712 || (ui_ctx->filtering_mode == EIP712_FILTERING_FULL)) {
|
||||
ui_ctx->end_reached = true;
|
||||
ui_712_switch_to_sign();
|
||||
}
|
||||
}
|
||||
@@ -580,6 +614,8 @@ bool ui_712_init(void) {
|
||||
ui_ctx->end_reached = false;
|
||||
ui_ctx->filtering_mode = EIP712_FILTERING_BASIC;
|
||||
explicit_bzero(&ui_ctx->amount, sizeof(ui_ctx->amount));
|
||||
explicit_bzero(strings.tmp.tmp, sizeof(strings.tmp.tmp));
|
||||
explicit_bzero(strings.tmp.tmp2, sizeof(strings.tmp.tmp2));
|
||||
} else {
|
||||
apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY;
|
||||
}
|
||||
@@ -726,4 +762,37 @@ void ui_712_token_join_prepare_amount(uint8_t index, const char *name, uint8_t n
|
||||
ui_ctx->amount.joins[index].name_length = cpy_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set UI pair key to the raw JSON key
|
||||
*
|
||||
* @param[in] field_ptr pointer to the field
|
||||
* @return whether it was successful or not
|
||||
*/
|
||||
bool ui_712_show_raw_key(const void *field_ptr) {
|
||||
const char *key;
|
||||
uint8_t key_len;
|
||||
|
||||
if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ui_712_field_shown() && !(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) {
|
||||
ui_712_set_title(key, key_len);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef SCREEN_SIZE_WALLET
|
||||
/*
|
||||
* Get UI pairs buffer
|
||||
*
|
||||
* @param[out] size buffer size
|
||||
* @return pointer to the buffer
|
||||
*/
|
||||
char *get_ui_pairs_buffer(size_t *size) {
|
||||
*size = sizeof(ui_ctx->ui_pairs_buffer);
|
||||
return ui_ctx->ui_pairs_buffer;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAVE_EIP712_FULL_SUPPORT
|
||||
|
||||
@@ -18,12 +18,16 @@ bool ui_712_init(void);
|
||||
void ui_712_deinit(void);
|
||||
e_eip712_nfs ui_712_next_field(void);
|
||||
void ui_712_review_struct(const void *const struct_ptr);
|
||||
bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length);
|
||||
bool ui_712_feed_to_display(const void *field_ptr,
|
||||
const uint8_t *data,
|
||||
uint8_t length,
|
||||
bool first,
|
||||
bool last);
|
||||
void ui_712_end_sign(void);
|
||||
unsigned int ui_712_approve();
|
||||
unsigned int ui_712_reject();
|
||||
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_set_title(const char *str, size_t length);
|
||||
void ui_712_set_value(const char *str, size_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, bool token_join, bool datetime);
|
||||
@@ -38,6 +42,10 @@ 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);
|
||||
void amount_join_set_token_received(void);
|
||||
bool ui_712_show_raw_key(const void *field_ptr);
|
||||
#ifdef SCREEN_SIZE_WALLET
|
||||
char *get_ui_pairs_buffer(size_t *size);
|
||||
#endif
|
||||
|
||||
#endif // HAVE_EIP712_FULL_SUPPORT
|
||||
|
||||
|
||||
@@ -7,11 +7,28 @@
|
||||
#include "common_712.h"
|
||||
#include "nbgl_use_case.h"
|
||||
#include "ui_message_signing.h"
|
||||
#include "ledger_assert.h"
|
||||
|
||||
static nbgl_contentTagValue_t pair;
|
||||
static nbgl_contentTagValue_t pairs[6];
|
||||
static nbgl_contentTagValueList_t pairs_list;
|
||||
static uint8_t pair_idx;
|
||||
static size_t buf_idx;
|
||||
|
||||
static void message_progress(bool confirm) {
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t shift_off;
|
||||
|
||||
if (pairs_list.nbPairs < pair_idx) {
|
||||
buf = get_ui_pairs_buffer(&buf_size);
|
||||
memmove(&pairs[0], &pairs[pairs_list.nbPairs], sizeof(pairs[0]));
|
||||
memmove(buf, pairs[0].item, (buf + buf_idx) - pairs[0].item);
|
||||
shift_off = pairs[0].item - buf;
|
||||
buf_idx -= shift_off;
|
||||
pairs[0].value -= shift_off;
|
||||
pairs[0].item = buf;
|
||||
pair_idx = 1;
|
||||
}
|
||||
if (confirm) {
|
||||
if (ui_712_next_field() == EIP712_NO_MORE_FIELD) {
|
||||
ui_712_switch_to_sign();
|
||||
@@ -22,22 +39,40 @@ static void message_progress(bool confirm) {
|
||||
}
|
||||
|
||||
static void message_update(bool confirm) {
|
||||
if (confirm) {
|
||||
explicit_bzero(&pair, sizeof(pair));
|
||||
explicit_bzero(&pairs_list, sizeof(pairs_list));
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
size_t buf_off;
|
||||
bool flag;
|
||||
|
||||
pair.item = strings.tmp.tmp2;
|
||||
pair.value = strings.tmp.tmp;
|
||||
pairs_list.nbPairs = 1;
|
||||
pairs_list.pairs = &pair;
|
||||
pairs_list.wrapping = false;
|
||||
nbgl_useCaseReviewStreamingContinue(&pairs_list, message_progress);
|
||||
buf = get_ui_pairs_buffer(&buf_size);
|
||||
if (confirm) {
|
||||
buf_off = strlen(strings.tmp.tmp2) + 1;
|
||||
LEDGER_ASSERT((buf_idx + buf_off) < buf_size, "UI pairs buffer overflow");
|
||||
pairs[pair_idx].item = memmove(buf + buf_idx, strings.tmp.tmp2, buf_off);
|
||||
buf_idx += buf_off;
|
||||
buf_off = strlen(strings.tmp.tmp) + 1;
|
||||
LEDGER_ASSERT((buf_idx + buf_off) < buf_size, "UI pairs buffer overflow");
|
||||
pairs[pair_idx].value = memmove(buf + buf_idx, strings.tmp.tmp, buf_off);
|
||||
buf_idx += buf_off;
|
||||
pair_idx += 1;
|
||||
pairs_list.nbPairs = nbgl_useCaseGetNbTagValuesInPage(pair_idx, &pairs_list, 0, &flag);
|
||||
if (pairs_list.nbPairs < pair_idx) {
|
||||
nbgl_useCaseReviewStreamingContinue(&pairs_list, message_progress);
|
||||
} else {
|
||||
message_progress(true);
|
||||
}
|
||||
} else {
|
||||
ui_typed_message_review_choice(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_712_start(void) {
|
||||
explicit_bzero(&pairs, sizeof(pairs));
|
||||
explicit_bzero(&pairs_list, sizeof(pairs_list));
|
||||
pairs_list.pairs = pairs;
|
||||
pair_idx = 0;
|
||||
buf_idx = 0;
|
||||
|
||||
nbgl_useCaseReviewStreamingStart(TYPE_MESSAGE,
|
||||
&C_Review_64px,
|
||||
TEXT_REVIEW_EIP712,
|
||||
@@ -50,7 +85,13 @@ void ui_712_switch_to_message(void) {
|
||||
}
|
||||
|
||||
void ui_712_switch_to_sign(void) {
|
||||
nbgl_useCaseReviewStreamingFinish(TEXT_SIGN_EIP712, ui_typed_message_review_choice);
|
||||
if (pair_idx > 0) {
|
||||
pairs_list.nbPairs = pair_idx;
|
||||
pair_idx = 0;
|
||||
nbgl_useCaseReviewStreamingContinue(&pairs_list, message_progress);
|
||||
} else {
|
||||
nbgl_useCaseReviewStreamingFinish(TEXT_SIGN_EIP712, ui_typed_message_review_choice);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAVE_EIP712_FULL_SUPPORT
|
||||
|
||||
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
@@ -112,11 +112,13 @@ def eip712_new_common(firmware: Firmware,
|
||||
app_client: EthAppClient,
|
||||
json_data: dict,
|
||||
filters: Optional[dict],
|
||||
verbose: bool):
|
||||
verbose: bool,
|
||||
golden_run: bool):
|
||||
assert InputData.process_data(app_client,
|
||||
json_data,
|
||||
filters,
|
||||
partial(autonext, firmware, navigator, default_screenshot_path))
|
||||
partial(autonext, firmware, navigator, default_screenshot_path),
|
||||
golden_run)
|
||||
with app_client.eip712_sign_new(BIP32_PATH):
|
||||
moves = []
|
||||
if firmware.device.startswith("nano"):
|
||||
@@ -126,15 +128,13 @@ def eip712_new_common(firmware: Firmware,
|
||||
moves += [NavInsID.BOTH_CLICK]
|
||||
else:
|
||||
# need to skip the message hash
|
||||
if not verbose and filters is None:
|
||||
moves += [NavInsID.USE_CASE_REVIEW_TAP]
|
||||
moves += [NavInsID.USE_CASE_REVIEW_TAP]
|
||||
moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
|
||||
if SNAPS_CONFIG is not None:
|
||||
navigator.navigate_and_compare(default_screenshot_path,
|
||||
SNAPS_CONFIG.test_name,
|
||||
moves,
|
||||
snap_start_idx=SNAPS_CONFIG.idx)
|
||||
SNAPS_CONFIG.idx += 1
|
||||
else:
|
||||
navigator.navigate(moves)
|
||||
return ResponseParser.signature(app_client.response().data)
|
||||
@@ -173,7 +173,8 @@ def test_eip712_new(firmware: Firmware,
|
||||
app_client,
|
||||
data,
|
||||
filters,
|
||||
verbose)
|
||||
verbose,
|
||||
False)
|
||||
|
||||
recovered_addr = recover_message(data, vrs)
|
||||
|
||||
@@ -340,7 +341,8 @@ def test_eip712_advanced_filtering(firmware: Firmware,
|
||||
navigator: Navigator,
|
||||
default_screenshot_path: Path,
|
||||
test_name: str,
|
||||
data_set: DataSet):
|
||||
data_set: DataSet,
|
||||
golden_run: bool):
|
||||
global SNAPS_CONFIG
|
||||
|
||||
app_client = EthAppClient(backend)
|
||||
@@ -355,7 +357,8 @@ def test_eip712_advanced_filtering(firmware: Firmware,
|
||||
app_client,
|
||||
data_set.data,
|
||||
data_set.filters,
|
||||
False)
|
||||
False,
|
||||
golden_run)
|
||||
|
||||
# verify signature
|
||||
addr = recover_message(data_set.data, vrs)
|
||||
|
||||