diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 6bbabd1..27bbc7f 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -3,33 +3,18 @@ #include #include #include -#include - -#include "apdu_constants.h" +#include "apdu_constants.h" // APDU response codes #include "eip712.h" -#include "mem.h" -#include "type_hash.h" #include "context.h" -#include "sol_typenames.h" #include "field_hash.h" #include "path.h" -#include "shared_context.h" #include "ui_logic.h" -#include "common_712.h" -#include "path.h" #include "typed_data.h" +#include "schema_hash.h" +#include "filtering.h" +#include "common_712.h" -#ifdef HAVE_EIP712_TESTING_KEY -static const uint8_t EIP712_FEEDER_PUBLIC_KEY[] = { - 0x04, 0x4c, 0xca, 0x8f, 0xad, 0x49, 0x6a, 0xa5, 0x04, 0x0a, 0x00, 0xa7, 0xeb, 0x2f, - 0x5c, 0xc3, 0xb8, 0x53, 0x76, 0xd8, 0x8b, 0xa1, 0x47, 0xa7, 0xd7, 0x05, 0x4a, 0x99, - 0xc6, 0x40, 0x56, 0x18, 0x87, 0xfe, 0x17, 0xa0, 0x96, 0xe3, 0x6c, 0x3b, 0x52, 0x3b, - 0x24, 0x4f, 0x3e, 0x2f, 0xf7, 0xf8, 0x40, 0xae, 0x26, 0xc4, 0xe7, 0x7a, 0xd3, 0xbc, - 0x73, 0x9a, 0xf5, 0xde, 0x6f, 0x2d, 0x77, 0xa7, 0xb6 -}; -#endif // HAVE_EIP712_TESTING_KEY - -void handle_eip712_return_code(bool ret) +static void handle_eip712_return_code(bool ret) { if (ret) { @@ -127,228 +112,6 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) return ret; } -#include "hash_bytes.h" -static bool verify_filtering_signature(uint8_t dname_length, - const char *const dname, - uint8_t sig_length, - const uint8_t *const sig, - uint8_t p1) -{ - 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; - uint64_t chain_id; - - cx_sha256_init(&hash_ctx); - - // Chain ID - chain_id = __builtin_bswap64(chainConfig->chainId); - 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); - - // Schema hash - hash_nbytes(eip712_context->schema_hash, - sizeof(eip712_context->schema_hash), - (cx_hash_t*)&hash_ctx); - - if (p1 == P1_FIELD_NAME) - { - uint8_t depth_count = path_get_depth_count(); - - for (uint8_t i = 0; i < depth_count; ++i) - { - if (i > 0) - { - hash_byte('.', (cx_hash_t*)&hash_ctx); - } - if ((field_ptr = path_get_nth_field(i + 1)) != NULL) - { - if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) - { - // field name - hash_nbytes((uint8_t*)key, key_len, (cx_hash_t*)&hash_ctx); - - // array levels - if (struct_field_is_array(field_ptr)) - { - uint8_t lvl_count; - - get_struct_field_array_lvls_array(field_ptr, &lvl_count); - for (int j = 0; j < lvl_count; ++j) - { - hash_nbytes((uint8_t*)".[]", 3, (cx_hash_t*)&hash_ctx); - } - } - } - } - } - } - - // Display name - hash_nbytes((uint8_t*)dname, - sizeof(char) * dname_length, - (cx_hash_t*)&hash_ctx); - - // Finalize hash - cx_hash((cx_hash_t*)&hash_ctx, - CX_LAST, - NULL, - 0, - hash, - INT256_LENGTH); - - cx_ecfp_init_public_key(CX_CURVE_256K1, -#ifdef HAVE_EIP712_TESTING_KEY - EIP712_FEEDER_PUBLIC_KEY, - sizeof(EIP712_FEEDER_PUBLIC_KEY), -#else - LEDGER_SIGNATURE_PUBLIC_KEY, - sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), -#endif - &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 filtering signature\n"); - return false; -#endif - } - return true; -} - -bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1) -{ - bool ret = false; - uint8_t dname_len; - const char *dname; - uint8_t sig_len; - const uint8_t *sig; - - if (p1 == P1_CONTRACT_NAME) - { - if (path_get_root_type() != ROOT_DOMAIN) - { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return false; - } - } - else // P1_FIELD_NAME - { - if (path_get_root_type() != ROOT_MESSAGE) - { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return false; - } - } - if (length > 0) - { - 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_filtering_signature(dname_len, dname, sig_len, sig, p1))) - { - if (p1 == P1_CONTRACT_NAME) - { - if (!N_storage.verbose_eip712) - { - ui_712_set_title("Contract", 8); - ui_712_set_value(dname, dname_len); - ui_712_redraw_generic_step(); - } - } - else // P1_FIELD_NAME - { - 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); - } - } - } - } - } - return ret; -} - -#include "hash_bytes.h" -#include "format_hash_field_type.h" - -bool compute_schema_hash(void) -{ - const void *struct_ptr; - uint8_t structs_count; - const void *field_ptr; - uint8_t fields_count; - const char *name; - uint8_t name_length; - cx_sha256_t hash_ctx; // sha224 - - cx_sha224_init(&hash_ctx); - - struct_ptr = get_structs_array(eip712_context->structs_array, &structs_count); - hash_byte('{', (cx_hash_t*)&hash_ctx); - while (structs_count-- > 0) - { - name = get_struct_name(struct_ptr, &name_length); - hash_byte('"', (cx_hash_t*)&hash_ctx); - hash_nbytes((uint8_t*)name, name_length, (cx_hash_t*)&hash_ctx); - hash_nbytes((uint8_t*)"\":[", 3, (cx_hash_t*)&hash_ctx); - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - while (fields_count-- > 0) - { - hash_nbytes((uint8_t*)"{\"name\":\"", 9, (cx_hash_t*)&hash_ctx); - name = get_struct_field_keyname(field_ptr, &name_length); - hash_nbytes((uint8_t*)name, name_length, (cx_hash_t*)&hash_ctx); - hash_nbytes((uint8_t*)"\",\"type\":\"", 10, (cx_hash_t*)&hash_ctx); - if (!format_hash_field_type(field_ptr, (cx_hash_t*)&hash_ctx)) - { - return false; - } - hash_nbytes((uint8_t*)"\"}", 2, (cx_hash_t*)&hash_ctx); - if (fields_count > 0) - { - hash_byte(',', (cx_hash_t*)&hash_ctx); - } - field_ptr = get_next_struct_field(field_ptr); - } - hash_byte(']', (cx_hash_t*)&hash_ctx); - if (structs_count > 0) - { - hash_byte(',', (cx_hash_t*)&hash_ctx); - } - struct_ptr = get_next_struct(struct_ptr); - } - hash_byte('}', (cx_hash_t*)&hash_ctx); - - // copy hash into context struct - cx_hash((cx_hash_t*)&hash_ctx, - CX_LAST, - NULL, - 0, - eip712_context->schema_hash, - sizeof(eip712_context->schema_hash)); - return true; -} - bool handle_eip712_filtering(const uint8_t *const apdu_buf) { bool ret = true; diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c new file mode 100644 index 0000000..7f36e50 --- /dev/null +++ b/src_features/signMessageEIP712/filtering.c @@ -0,0 +1,186 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "filtering.h" +#include "hash_bytes.h" +#include "ethUstream.h" // INT256_LENGTH +#include "apdu_constants.h" // APDU return codes +#include "context.h" +#include "eip712.h" +#include "typed_data.h" +#include "path.h" +#include "ui_logic.h" + + +#ifdef HAVE_EIP712_TESTING_KEY +static const uint8_t EIP712_FEEDER_PUBLIC_KEY[] = { + 0x04, 0x4c, 0xca, 0x8f, 0xad, 0x49, 0x6a, 0xa5, 0x04, 0x0a, 0x00, 0xa7, 0xeb, 0x2f, + 0x5c, 0xc3, 0xb8, 0x53, 0x76, 0xd8, 0x8b, 0xa1, 0x47, 0xa7, 0xd7, 0x05, 0x4a, 0x99, + 0xc6, 0x40, 0x56, 0x18, 0x87, 0xfe, 0x17, 0xa0, 0x96, 0xe3, 0x6c, 0x3b, 0x52, 0x3b, + 0x24, 0x4f, 0x3e, 0x2f, 0xf7, 0xf8, 0x40, 0xae, 0x26, 0xc4, 0xe7, 0x7a, 0xd3, 0xbc, + 0x73, 0x9a, 0xf5, 0xde, 0x6f, 0x2d, 0x77, 0xa7, 0xb6 +}; +#endif // HAVE_EIP712_TESTING_KEY + + +static bool verify_filtering_signature(uint8_t dname_length, + const char *const dname, + uint8_t sig_length, + const uint8_t *const sig, + uint8_t p1) +{ + 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; + uint64_t chain_id; + + cx_sha256_init(&hash_ctx); + + // Chain ID + chain_id = __builtin_bswap64(chainConfig->chainId); + 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); + + // Schema hash + hash_nbytes(eip712_context->schema_hash, + sizeof(eip712_context->schema_hash), + (cx_hash_t*)&hash_ctx); + + if (p1 == P1_FIELD_NAME) + { + uint8_t depth_count = path_get_depth_count(); + + for (uint8_t i = 0; i < depth_count; ++i) + { + if (i > 0) + { + hash_byte('.', (cx_hash_t*)&hash_ctx); + } + if ((field_ptr = path_get_nth_field(i + 1)) != NULL) + { + if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) + { + // field name + hash_nbytes((uint8_t*)key, key_len, (cx_hash_t*)&hash_ctx); + + // array levels + if (struct_field_is_array(field_ptr)) + { + uint8_t lvl_count; + + get_struct_field_array_lvls_array(field_ptr, &lvl_count); + for (int j = 0; j < lvl_count; ++j) + { + hash_nbytes((uint8_t*)".[]", 3, (cx_hash_t*)&hash_ctx); + } + } + } + } + } + } + + // Display name + hash_nbytes((uint8_t*)dname, + sizeof(char) * dname_length, + (cx_hash_t*)&hash_ctx); + + // Finalize hash + cx_hash((cx_hash_t*)&hash_ctx, + CX_LAST, + NULL, + 0, + hash, + INT256_LENGTH); + + cx_ecfp_init_public_key(CX_CURVE_256K1, +#ifdef HAVE_EIP712_TESTING_KEY + EIP712_FEEDER_PUBLIC_KEY, + sizeof(EIP712_FEEDER_PUBLIC_KEY), +#else + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), +#endif + &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 filtering signature\n"); + return false; +#endif + } + return true; +} + +bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1) +{ + bool ret = false; + uint8_t dname_len; + const char *dname; + uint8_t sig_len; + const uint8_t *sig; + + if (p1 == P1_CONTRACT_NAME) + { + if (path_get_root_type() != ROOT_DOMAIN) + { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + } + else // P1_FIELD_NAME + { + if (path_get_root_type() != ROOT_MESSAGE) + { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + } + if (length > 0) + { + 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_filtering_signature(dname_len, dname, sig_len, sig, p1))) + { + if (p1 == P1_CONTRACT_NAME) + { + if (!N_storage.verbose_eip712) + { + ui_712_set_title("Contract", 8); + ui_712_set_value(dname, dname_len); + ui_712_redraw_generic_step(); + } + } + else // P1_FIELD_NAME + { + 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); + } + } + } + } + } + return ret; +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h new file mode 100644 index 0000000..e04c6a1 --- /dev/null +++ b/src_features/signMessageEIP712/filtering.h @@ -0,0 +1,13 @@ +#ifndef FILTERING_H_ +#define FILTERING_H_ + +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include +#include + +bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1); + +#endif // HAVE_EIP712_FULL_SUPPORT + +#endif // FILTERING_H_ diff --git a/src_features/signMessageEIP712/schema_hash.c b/src_features/signMessageEIP712/schema_hash.c new file mode 100644 index 0000000..7ebd83c --- /dev/null +++ b/src_features/signMessageEIP712/schema_hash.c @@ -0,0 +1,66 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "schema_hash.h" +#include "hash_bytes.h" +#include "typed_data.h" +#include "format_hash_field_type.h" +#include "context.h" + +bool compute_schema_hash(void) +{ + const void *struct_ptr; + uint8_t structs_count; + const void *field_ptr; + uint8_t fields_count; + const char *name; + uint8_t name_length; + cx_sha256_t hash_ctx; // sha224 + + cx_sha224_init(&hash_ctx); + + struct_ptr = get_structs_array(eip712_context->structs_array, &structs_count); + hash_byte('{', (cx_hash_t*)&hash_ctx); + while (structs_count-- > 0) + { + name = get_struct_name(struct_ptr, &name_length); + hash_byte('"', (cx_hash_t*)&hash_ctx); + hash_nbytes((uint8_t*)name, name_length, (cx_hash_t*)&hash_ctx); + hash_nbytes((uint8_t*)"\":[", 3, (cx_hash_t*)&hash_ctx); + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + while (fields_count-- > 0) + { + hash_nbytes((uint8_t*)"{\"name\":\"", 9, (cx_hash_t*)&hash_ctx); + name = get_struct_field_keyname(field_ptr, &name_length); + hash_nbytes((uint8_t*)name, name_length, (cx_hash_t*)&hash_ctx); + hash_nbytes((uint8_t*)"\",\"type\":\"", 10, (cx_hash_t*)&hash_ctx); + if (!format_hash_field_type(field_ptr, (cx_hash_t*)&hash_ctx)) + { + return false; + } + hash_nbytes((uint8_t*)"\"}", 2, (cx_hash_t*)&hash_ctx); + if (fields_count > 0) + { + hash_byte(',', (cx_hash_t*)&hash_ctx); + } + field_ptr = get_next_struct_field(field_ptr); + } + hash_byte(']', (cx_hash_t*)&hash_ctx); + if (structs_count > 0) + { + hash_byte(',', (cx_hash_t*)&hash_ctx); + } + struct_ptr = get_next_struct(struct_ptr); + } + hash_byte('}', (cx_hash_t*)&hash_ctx); + + // copy hash into context struct + cx_hash((cx_hash_t*)&hash_ctx, + CX_LAST, + NULL, + 0, + eip712_context->schema_hash, + sizeof(eip712_context->schema_hash)); + return true; +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/schema_hash.h b/src_features/signMessageEIP712/schema_hash.h new file mode 100644 index 0000000..9ca1e03 --- /dev/null +++ b/src_features/signMessageEIP712/schema_hash.h @@ -0,0 +1,12 @@ +#ifndef SCHEMA_HASH_H_ +#define SCHEMA_HASH_H_ + +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include + +bool compute_schema_hash(void); + +#endif // HAVE_EIP712_FULL_SUPPORT + +#endif // SCHEMA_HASH_H_