Skip to content

Commit 672fc7c

Browse files
Fix EIP-712 & GCS signed integer formatting
* Add support for non-power of 2 type sizes * Commonize the formatting between EIP-712 & GCS
1 parent af9cb39 commit 672fc7c

4 files changed

Lines changed: 74 additions & 91 deletions

File tree

src/features/generic_tx_parser/gtp_param_raw.c

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#include "gtp_param_raw.h"
44
#include "gtp_field.h"
55
#include "uint256.h"
6-
#include "read.h"
76
#include "gtp_field_table.h"
87
#include "utils.h"
98
#include "shared_context.h"
@@ -111,47 +110,7 @@ bool format_uint(const s_field *field,
111110
}
112111

113112
bool format_int(const s_value *def, const s_parsed_value *value, char *buf, size_t buf_size) {
114-
uint8_t tmp[INT256_LENGTH];
115-
bool ret;
116-
union {
117-
uint256_t value256;
118-
uint128_t value128;
119-
int64_t value64;
120-
int32_t value32;
121-
int16_t value16;
122-
int8_t value8;
123-
} uv;
124-
125-
buf_shrink_expand(value->ptr, value->length, tmp, def->type_size);
126-
switch (def->type_size * 8) {
127-
case 256:
128-
convertUint256BE(tmp, def->type_size, &uv.value256);
129-
ret = tostring256_signed(&uv.value256, 10, buf, buf_size);
130-
break;
131-
case 128:
132-
convertUint128BE(tmp, def->type_size, &uv.value128);
133-
ret = tostring128_signed(&uv.value128, 10, buf, buf_size);
134-
break;
135-
case 64:
136-
uv.value64 = read_u64_be(tmp, 0);
137-
ret = format_i64(buf, buf_size, uv.value64);
138-
break;
139-
case 32:
140-
uv.value32 = read_u32_be(tmp, 0);
141-
ret = format_i64(buf, buf_size, (int64_t) uv.value32);
142-
break;
143-
case 16:
144-
uv.value16 = read_u16_be(tmp, 0);
145-
ret = format_i64(buf, buf_size, (int64_t) uv.value16);
146-
break;
147-
case 8:
148-
uv.value8 = value->ptr[0];
149-
ret = format_i64(buf, buf_size, (int64_t) uv.value8);
150-
break;
151-
default:
152-
ret = false;
153-
}
154-
return ret;
113+
return format_signed_int_be(value->ptr, value->length, def->type_size, buf, buf_size);
155114
}
156115

157116
/**

src/features/sign_message_eip712/ui_logic.c

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "app_mem_utils.h"
33
#include "mem_utils.h"
44
#include "os_io.h"
5+
#include "format.h"
56
#include "common_utils.h" // uint256_to_decimal
67
#include "common_712.h"
78
#include "context_712.h" // eip712_context_deinit
@@ -468,62 +469,18 @@ static bool ui_712_format_int(const uint8_t *data,
468469
uint8_t length,
469470
bool first,
470471
const s_struct_712_field *field_ptr) {
471-
uint256_t value256;
472-
uint128_t value128;
473-
int32_t value32;
474-
int16_t value16;
475-
int8_t value8;
476-
uint8_t tmp[sizeof(int32_t)] = {0};
477-
478472
// no reason for an integer to be received over multiple chunks
479473
if (!first) {
480474
return false;
481475
}
482-
if (length < 1) {
476+
if (!format_signed_int_be(data,
477+
length,
478+
field_ptr->type_size,
479+
strings.tmp.tmp,
480+
sizeof(strings.tmp.tmp))) {
483481
apdu_response_code = SWO_INCORRECT_DATA;
484482
return false;
485483
}
486-
if (length > field_ptr->type_size) {
487-
apdu_response_code = SWO_INCORRECT_DATA;
488-
return false;
489-
}
490-
491-
switch (field_ptr->type_size * 8) {
492-
case 256:
493-
convertUint256BE(data, length, &value256);
494-
tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
495-
break;
496-
case 128:
497-
convertUint128BE(data, length, &value128);
498-
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
499-
break;
500-
case 64:
501-
convertUint64BEto128(data, length, &value128);
502-
tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp));
503-
break;
504-
case 32:
505-
buf_shrink_expand(data, length, tmp, sizeof(int32_t));
506-
value32 = (int32_t) read_u32_be(tmp, 0);
507-
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", value32);
508-
break;
509-
case 16:
510-
buf_shrink_expand(data, length, tmp, sizeof(int16_t));
511-
value16 = (int16_t) read_u16_be(tmp, 0);
512-
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", value16);
513-
break;
514-
case 8:
515-
if (length != sizeof(int8_t)) {
516-
apdu_response_code = SWO_INCORRECT_DATA;
517-
return false;
518-
}
519-
value8 = (int8_t) data[0];
520-
snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", value8);
521-
break;
522-
default:
523-
PRINTF("Unhandled field typesize\n");
524-
apdu_response_code = SWO_INCORRECT_DATA;
525-
return false;
526-
}
527484
return true;
528485
}
529486

src/utils.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,67 @@
11
#include <ctype.h>
22
#include <string.h>
33
#include "utils.h"
4+
#include "common_utils.h"
5+
#include "uint128.h"
6+
#include "uint256.h"
7+
#include "format.h"
8+
#include "read.h"
9+
10+
/**
11+
* @brief Format a big-endian signed integer as a decimal string
12+
*
13+
* Mirrors the ABI encode_int() sign convention: a value is treated as negative
14+
* only when it arrives at full type width and has its MSB set. Shorter values
15+
* are always treated as non-negative and formatted via uint256_to_decimal.
16+
*
17+
* @param[in] data Raw big-endian bytes of the value
18+
* @param[in] length Number of bytes in data (must be 1..type_size)
19+
* @param[in] type_size Declared byte width of the integer type (e.g. 32 for int256)
20+
* @param[out] buf Output string buffer
21+
* @param[in] buf_size Size of buf
22+
* @return true on success, false on invalid input or formatting failure
23+
*/
24+
bool format_signed_int_be(const uint8_t *data,
25+
uint8_t length,
26+
uint8_t type_size,
27+
char *buf,
28+
size_t buf_size) {
29+
uint8_t tmp[INT256_LENGTH];
30+
union {
31+
uint256_t value256;
32+
uint128_t value128;
33+
int64_t value64;
34+
} uv;
35+
36+
if ((length == 0) || (length > type_size)) {
37+
return false;
38+
}
39+
40+
// Positive if truncated or MSB not set — treat as unsigned
41+
if ((length != type_size) || ((data[0] & 0x80) == 0)) {
42+
return uint256_to_decimal(data, length, buf, buf_size);
43+
}
44+
45+
// Negative: sign-extend with 0xFF into the smallest fitting container
46+
if (type_size <= sizeof(uv.value64)) {
47+
memset(tmp, 0xFF, sizeof(uv.value64) - length);
48+
memcpy(tmp + sizeof(uv.value64) - length, data, length);
49+
uv.value64 = (int64_t) read_u64_be(tmp, 0);
50+
return format_i64(buf, buf_size, uv.value64);
51+
} else if (type_size <= sizeof(uv.value128)) {
52+
memset(tmp, 0xFF, sizeof(uv.value128) - length);
53+
memcpy(tmp + sizeof(uv.value128) - length, data, length);
54+
convertUint128BE(tmp, sizeof(uv.value128), &uv.value128);
55+
return tostring128_signed(&uv.value128, 10, buf, buf_size);
56+
} else if (type_size <= sizeof(uv.value256)) {
57+
memset(tmp, 0xFF, sizeof(uv.value256) - length);
58+
memcpy(tmp + sizeof(uv.value256) - length, data, length);
59+
convertUint256BE(tmp, sizeof(uv.value256), &uv.value256);
60+
return tostring256_signed(&uv.value256, 10, buf, buf_size);
61+
}
62+
PRINTF("Error: wrong int typesize (%u bytes)\n", type_size);
63+
return false;
64+
}
465

566
/**
667
* @brief Shrinks or expands a buffer to fit a destination size

src/utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22

33
#include <stdint.h>
44
#include <stdbool.h>
5+
#include <stddef.h>
56

67
#define SET_BIT(a) (1 << a)
78

89
void buf_shrink_expand(const uint8_t *src, size_t src_size, uint8_t *dst, size_t dst_size);
910
void str_cpy_explicit_trunc(const char *src, size_t src_size, char *dst, size_t dst_size);
1011
void reverseString(char *const str, uint32_t length);
12+
bool format_signed_int_be(const uint8_t *data,
13+
uint8_t length,
14+
uint8_t type_size,
15+
char *buf,
16+
size_t buf_size);

0 commit comments

Comments
 (0)