diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 52de586..5bf1753 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -23,6 +23,7 @@ #define INS_PERFORM_PRIVACY_OPERATION 0x18 #define INS_EIP712_STRUCT_DEF 0x1A #define INS_EIP712_STRUCT_IMPL 0x1C +#define INS_EIP712_FILTERING 0x1E #define P1_CONFIRM 0x01 #define P1_NON_CONFIRM 0x00 #define P2_NO_CHAINCODE 0x00 @@ -172,8 +173,11 @@ void handleStarkwareUnsafeSign(uint8_t p1, #endif +#ifdef HAVE_EIP712_FULL_SUPPORT bool handle_eip712_struct_def(const uint8_t *const apdu_buf); bool handle_eip712_struct_impl(const uint8_t *const apdu_buf); bool handle_eip712_sign(const uint8_t *const apdu_buf); +bool handle_eip712_filtering(const uint8_t *const apdu_buf); +#endif // HAVE_EIP712_FULL_SUPPORT #endif // _APDU_CONSTANTS_H_ diff --git a/src/main.c b/src/main.c index 85c49e2..61baf06 100644 --- a/src/main.c +++ b/src/main.c @@ -717,6 +717,11 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { *flags |= IO_ASYNCH_REPLY; handle_eip712_struct_impl(G_io_apdu_buffer); break; + + case INS_EIP712_FILTERING: + *flags |= IO_ASYNCH_REPLY; + handle_eip712_filtering(G_io_apdu_buffer); + break; #endif // HAVE_EIP712_FULL_SUPPORT #if 0 diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index c219858..1e64090 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -55,7 +55,6 @@ bool eip712_context_init(void) // create len(types) *(eip712_context->structs_array) = 0; - return true; } diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 98a7b05..d0b6964 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -34,11 +34,16 @@ typedef enum // APDUs P1 #define P1_COMPLETE 0x00 #define P1_PARTIAL 0xFF +#define P1_ACTIVATE 0x00 +#define P1_CONTRACT_NAME 0x0F +#define P1_FIELD_NAME 0xFF // APDUs P2 #define P2_NAME 0x00 #define P2_ARRAY 0x0F #define P2_FIELD 0xFF +#define P2_KEY 0x00 +#define P2_VALUE 0xFF // TypeDesc masks #define TYPE_MASK (0xF) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 1be3b8c..4fa4919 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -16,6 +16,7 @@ #include "shared_context.h" #include "ui_logic.h" #include "common_712.h" +#include "path.h" // lib functions @@ -440,6 +441,277 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) return ret; } +static bool verify_contract_name_signature(uint8_t dname_length, + const char *const dname, + uint8_t sig_length, + const uint8_t *const sig) +{ + uint8_t hash[INT256_LENGTH]; + cx_ecfp_public_key_t verifying_key; + cx_sha256_t hash_ctx; + + cx_sha256_init(&hash_ctx); + // Contract address + cx_hash((cx_hash_t*)&hash_ctx, + 0, + eip712_context->contract_addr, + sizeof(eip712_context->contract_addr), + NULL, + 0); + + // Display name length + cx_hash((cx_hash_t*)&hash_ctx, + 0, + &dname_length, + sizeof(dname_length), + NULL, + 0); + + // Display name + cx_hash((cx_hash_t*)&hash_ctx, + 0, + (uint8_t*)dname, + sizeof(char) * dname_length, + NULL, + 0); + + // Finalize hash + cx_hash((cx_hash_t*)&hash_ctx, + CX_LAST, + NULL, + 0, + hash, + sizeof(hash)); + + cx_ecfp_init_public_key(CX_CURVE_256K1, + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), + &verifying_key); + if (!cx_ecdsa_verify(&verifying_key, + CX_LAST, + CX_SHA256, + hash, + sizeof(hash), + sig, + sig_length)) + { +#ifndef HAVE_BYPASS_SIGNATURES + PRINTF("Invalid EIP-712 contract filtering signature\n"); + //return false; // TODO: Uncomment +#endif + } + return true; +} + +static bool verify_field_name_signature(uint8_t dname_length, + const char *const dname, + uint8_t sig_length, + const uint8_t *const sig) +{ + const void *field_ptr; + const char *key; + uint8_t key_len; + uint8_t hash[INT256_LENGTH]; + cx_ecfp_public_key_t verifying_key; + cx_sha256_t hash_ctx; + + cx_sha256_init(&hash_ctx); + // Contract address + cx_hash((cx_hash_t*)&hash_ctx, + 0, + eip712_context->contract_addr, + sizeof(eip712_context->contract_addr), + NULL, + 0); + + if ((field_ptr = path_get_field()) == NULL) + { + return false; + } + if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) + { + return false; + } + + // Key length + cx_hash((cx_hash_t*)&hash_ctx, + 0, + &key_len, + sizeof(key_len), + NULL, + 0); + + // Key + cx_hash((cx_hash_t*)&hash_ctx, + 0, + (uint8_t*)key, + sizeof(char) * key_len, + NULL, + 0); + + // Display name length + cx_hash((cx_hash_t*)&hash_ctx, + 0, + &dname_length, + sizeof(dname_length), + NULL, + 0); + + // Display name + cx_hash((cx_hash_t*)&hash_ctx, + 0, + (uint8_t*)dname, + sizeof(char) * dname_length, + NULL, + 0); + + // Finalize hash + cx_hash((cx_hash_t*)&hash_ctx, + CX_LAST, + NULL, + 0, + hash, + INT256_LENGTH); + + cx_ecfp_init_public_key(CX_CURVE_256K1, + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), + &verifying_key); + if (!cx_ecdsa_verify(&verifying_key, + CX_LAST, + CX_SHA256, + hash, + sizeof(hash), + sig, + sig_length)) + { +#ifndef HAVE_BYPASS_SIGNATURES + PRINTF("Invalid EIP-712 field filtering signature\n"); + //return false; // TODO: Uncomment +#endif + } + return true; +} + +bool provide_contract_name(const uint8_t *const payload, uint8_t length) +{ + bool ret = false; + uint8_t dname_len; + const char *dname; + uint8_t sig_len; + const uint8_t *sig; + + if ((length > 0) && (path_get_root_type() == ROOT_DOMAIN)) + { + dname_len = payload[0]; + if ((1 + dname_len) < length) + { + dname = (char*)&payload[1]; + sig_len = payload[1 + dname_len]; + sig = &payload[1 + dname_len + 1]; + if ((sig_len > 0) && ((1 + dname_len + 1 + sig_len) == length)) + { + if ((ret = verify_contract_name_signature(dname_len, dname, sig_len, sig))) + { + ui_712_set_title("Contract", 8); + ui_712_set_value(dname, dname_len); + ui_712_redraw_generic_step(); + } + } + } + } + return ret; +} + +bool provide_field_name(const uint8_t *const payload, uint8_t length) +{ + bool ret = false; + uint8_t dname_len; + const char *dname; + uint8_t sig_len; + const uint8_t *sig; + bool name_provided = false; + + if ((length > 0) && (path_get_root_type() == ROOT_MESSAGE)) + { + dname_len = payload[0]; + if ((1 + dname_len) < length) + { + dname = (char*)&payload[1]; + sig_len = payload[1 + dname_len]; + sig = &payload[1 + dname_len + 1]; + if ((sig_len > 0) && ((1 + dname_len + 1 + sig_len) == length)) + { + if ((ret = verify_field_name_signature(dname_len, dname, sig_len, sig))) + { + if (dname_len > 0) // don't substitute for an empty name + { + ui_712_set_title(dname, dname_len); + name_provided = true; + } + ret = true; + ui_712_flag_field(true, name_provided); + } + } + } + } + return ret; +} + +bool handle_eip712_filtering(const uint8_t *const apdu_buf) +{ + bool ret = true; + + switch (apdu_buf[OFFSET_P1]) + { + case P1_ACTIVATE: + ui_ctx->filtering_mode = EIP712_FILTERING_FULL; + break; + case P1_CONTRACT_NAME: + if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + { + ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC]); + } + else + { + ret = false; + } + break; + case P1_FIELD_NAME: + if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + { + ret = provide_field_name(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC]); + } + else + { + ret = false; + } + break; + default: + PRINTF("Unknown P1 0x%x for APDU 0x%x\n", + apdu_buf[OFFSET_P1], + apdu_buf[OFFSET_INS]); + ret = false; + } + if (ret) + { + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + } + else + { + G_io_apdu_buffer[0] = 0x6A; + G_io_apdu_buffer[1] = 0x80; + } + if ((apdu_buf[OFFSET_P1] != P1_CONTRACT_NAME) || (ret == false)) + { + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } + return ret; +} + bool handle_eip712_sign(const uint8_t *const apdu_buf) { if (parseBip32(&apdu_buf[OFFSET_CDATA], diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 98025aa..e69706e 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -397,6 +397,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) #ifdef HAVE_EIP712_HALF_BLIND } #endif // HAVE_EIP712_HALF_BLIND + ui_712_field_flags_reset(); return true; } @@ -609,6 +610,7 @@ bool path_advance(void) } while (end_reached); path_update(); + ui_712_field_flags_reset(); return true; } diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index c80e546..daab375 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -17,7 +17,7 @@ #include "uint256.h" // tostring256 && tostring256_signed -static t_ui_context *ui_ctx = NULL; +t_ui_context *ui_ctx = NULL; static void ui_712_set_buf(const char *const src, @@ -144,7 +144,7 @@ void ui_712_message_hash(void) { const char *const title = "Message hash"; - ui_712_set_title(title, strlen(titltitlee)); + ui_712_set_title(title, strlen(title)); snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", @@ -179,7 +179,10 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, // Key if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { - ui_712_set_title(key, key_len); + if (!(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) + { + ui_712_set_title(key, key_len); + } } // Value @@ -305,6 +308,7 @@ bool ui_712_init(void) ui_ctx->shown = false; ui_ctx->end_reached = false; ui_ctx->pos = UI_712_POS_REVIEW; + ui_ctx->filtering_mode = EIP712_FILTERING_BASIC; } return ui_ctx != NULL; } @@ -343,4 +347,21 @@ unsigned int ui_712_reject(const bagl_element_t *e) return 0; } +void ui_712_flag_field(bool show, bool name_provided) +{ + if (show) + { + ui_ctx->field_flags |= UI_712_FIELD_SHOWN; + } + if (name_provided) + { + ui_ctx->field_flags |= UI_712_FIELD_NAME_PROVIDED; + } +} + +void ui_712_field_flags_reset(void) +{ + ui_ctx->field_flags = 0; +} + #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 43889fc..966d81b 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -6,6 +6,15 @@ #include #include "ux.h" +#define UI_712_FIELD_SHOWN (1 << 0) +#define UI_712_FIELD_NAME_PROVIDED (1 << 1) + +typedef enum +{ + EIP712_FILTERING_BASIC, + EIP712_FILTERING_FULL +} e_eip712_filtering_mode; + typedef enum { UI_712_POS_REVIEW, @@ -17,8 +26,12 @@ typedef struct bool shown; bool end_reached; e_ui_position pos; + uint8_t filtering_mode; + uint8_t field_flags; } t_ui_context; +extern t_ui_context *ui_ctx; + bool ui_712_init(void); void ui_712_deinit(void); void ui_712_next_field(void); @@ -33,6 +46,8 @@ void ui_712_set_value(const char *const str, uint8_t length); void ui_712_message_hash(void); #endif // HAVE_EIP712_HALF_BLIND void ui_712_redraw_generic_step(void); +void ui_712_flag_field(bool show, bool name_provided); +void ui_712_field_flags_reset(void); #endif // HAVE_EIP712_FULL_SUPPORT