From 895c37e1fa2812aba974fbe08b2e592e1fe4a242 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 19 Jul 2022 11:42:25 +0200 Subject: [PATCH 001/184] Change apdu payload length to proper size (8 bits) --- src/apdu_constants.h | 32 +++++++++---------- src/main.c | 2 +- src/shared_context.h | 2 +- .../cmd_getAppConfiguration.c | 2 +- .../getEth2PublicKey/cmd_getEth2PublicKey.c | 2 +- src_features/getPublicKey/cmd_getPublicKey.c | 2 +- .../cmd_performPrivacyOperation.c | 2 +- .../cmd_provideTokenInfo.c | 9 +++--- .../cmd_provideNFTInfo.c | 2 +- .../setExternalPlugin/cmd_setExternalPlugin.c | 4 +-- src_features/setPlugin/cmd_setPlugin.c | 2 +- src_features/signMessage/cmd_signMessage.c | 2 +- src_features/signTx/cmd_signTx.c | 2 +- .../cmd_stark_getPublicKey.c | 2 +- .../cmd_stark_provideQuantum.c | 2 +- src_features/stark_sign/cmd_stark_sign.c | 2 +- .../stark_unsafe_sign/cmd_stark_unsafe_sign.c | 2 +- 17 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 1937075..3062674 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -61,37 +61,37 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, - const uint8_t *dataBuffer, - uint16_t dataLength, + const uint8_t *workBuffer, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleProvideNFTInformation(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleSign(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleGetAppConfiguration(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleSignPersonalMessage(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleSignEIP712Message(uint8_t p1, @@ -104,21 +104,21 @@ void handleSignEIP712Message(uint8_t p1, void handleSetExternalPlugin(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleSetPlugin(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handlePerformPrivacyOperation(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); @@ -127,13 +127,13 @@ void handlePerformPrivacyOperation(uint8_t p1, void handleGetEth2PublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleSetEth2WinthdrawalIndex(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); @@ -144,25 +144,25 @@ void handleSetEth2WinthdrawalIndex(uint8_t p1, void handleStarkwareGetPublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleStarkwareSignMessage(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleStarkwareProvideQuantum(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); void handleStarkwareUnsafeSign(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); diff --git a/src/main.c b/src/main.c index b93a70a..d34d5c1 100644 --- a/src/main.c +++ b/src/main.c @@ -480,7 +480,7 @@ void handleGetWalletId(volatile unsigned int *tx) { #endif // HAVE_WALLET_ID_SDK -const uint8_t *parseBip32(const uint8_t *dataBuffer, uint16_t *dataLength, bip32_path_t *bip32) { +const uint8_t *parseBip32(const uint8_t *dataBuffer, uint8_t *dataLength, bip32_path_t *bip32) { if (*dataLength < 1) { PRINTF("Invalid data\n"); return NULL; diff --git a/src/shared_context.h b/src/shared_context.h index 424c0fa..4dc68f7 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -219,6 +219,6 @@ extern uint32_t eth2WithdrawalIndex; #endif void reset_app_context(void); -const uint8_t *parseBip32(const uint8_t *, uint16_t *, bip32_path_t *); +const uint8_t *parseBip32(const uint8_t *, uint8_t *, bip32_path_t *); #endif // _SHARED_CONTEXT_H_ diff --git a/src_features/getAppConfiguration/cmd_getAppConfiguration.c b/src_features/getAppConfiguration/cmd_getAppConfiguration.c index e9e09fc..e850774 100644 --- a/src_features/getAppConfiguration/cmd_getAppConfiguration.c +++ b/src_features/getAppConfiguration/cmd_getAppConfiguration.c @@ -6,7 +6,7 @@ void handleGetAppConfiguration(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(p1); diff --git a/src_features/getEth2PublicKey/cmd_getEth2PublicKey.c b/src_features/getEth2PublicKey/cmd_getEth2PublicKey.c index cccd0eb..32964f3 100644 --- a/src_features/getEth2PublicKey/cmd_getEth2PublicKey.c +++ b/src_features/getEth2PublicKey/cmd_getEth2PublicKey.c @@ -43,7 +43,7 @@ void getEth2PublicKey(uint32_t *bip32Path, uint8_t bip32PathLength, uint8_t *out void handleGetEth2PublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { bip32_path_t bip32; diff --git a/src_features/getPublicKey/cmd_getPublicKey.c b/src_features/getPublicKey/cmd_getPublicKey.c index 244dbb7..339f0ce 100644 --- a/src_features/getPublicKey/cmd_getPublicKey.c +++ b/src_features/getPublicKey/cmd_getPublicKey.c @@ -8,7 +8,7 @@ void handleGetPublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { uint8_t privateKeyData[INT256_LENGTH]; diff --git a/src_features/performPrivacyOperation/cmd_performPrivacyOperation.c b/src_features/performPrivacyOperation/cmd_performPrivacyOperation.c index c41b122..bd5933b 100644 --- a/src_features/performPrivacyOperation/cmd_performPrivacyOperation.c +++ b/src_features/performPrivacyOperation/cmd_performPrivacyOperation.c @@ -26,7 +26,7 @@ void decodeScalar(const uint8_t *scalarIn, uint8_t *scalarOut) { void handlePerformPrivacyOperation(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { uint8_t privateKeyData[INT256_LENGTH]; diff --git a/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c index 325cbb9..fa1bd19 100644 --- a/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c +++ b/src_features/provideErc20TokenInformation/cmd_provideTokenInfo.c @@ -7,8 +7,8 @@ void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, - uint8_t *workBuffer, - uint16_t dataLength, + const uint8_t *workBuffer, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(p1); @@ -102,12 +102,13 @@ void handleProvideErc20TokenInformation(uint8_t p1, void handleProvideErc20TokenInformation(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, - __attribute__((unused)) unsigned int *tx) { + unsigned int *tx) { UNUSED(p1); UNUSED(p2); UNUSED(flags); + UNUSED(tx); uint32_t offset = 0; uint8_t tickerLength; uint32_t chainId; diff --git a/src_features/provideNFTInformation/cmd_provideNFTInfo.c b/src_features/provideNFTInformation/cmd_provideNFTInfo.c index 8d373ba..f8b90c0 100644 --- a/src_features/provideNFTInformation/cmd_provideNFTInfo.c +++ b/src_features/provideNFTInformation/cmd_provideNFTInfo.c @@ -54,7 +54,7 @@ typedef bool verificationAlgo(const cx_ecfp_public_key_t *, void handleProvideNFTInformation(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(p1); diff --git a/src_features/setExternalPlugin/cmd_setExternalPlugin.c b/src_features/setExternalPlugin/cmd_setExternalPlugin.c index fb66f12..b5ea1ac 100644 --- a/src_features/setExternalPlugin/cmd_setExternalPlugin.c +++ b/src_features/setExternalPlugin/cmd_setExternalPlugin.c @@ -8,7 +8,7 @@ void handleSetExternalPlugin(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(p1); @@ -90,4 +90,4 @@ void handleSetExternalPlugin(uint8_t p1, G_io_apdu_buffer[(*tx)++] = 0x90; G_io_apdu_buffer[(*tx)++] = 0x00; -} \ No newline at end of file +} diff --git a/src_features/setPlugin/cmd_setPlugin.c b/src_features/setPlugin/cmd_setPlugin.c index 68b5e76..850eed5 100644 --- a/src_features/setPlugin/cmd_setPlugin.c +++ b/src_features/setPlugin/cmd_setPlugin.c @@ -87,7 +87,7 @@ static pluginType_t getPluginType(char *pluginName, uint8_t pluginNameLength) { void handleSetPlugin(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(p1); diff --git a/src_features/signMessage/cmd_signMessage.c b/src_features/signMessage/cmd_signMessage.c index 8389548..c344f92 100644 --- a/src_features/signMessage/cmd_signMessage.c +++ b/src_features/signMessage/cmd_signMessage.c @@ -114,7 +114,7 @@ static void feed_value_str(const uint8_t *const data, size_t length, bool is_asc void handleSignPersonalMessage(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(tx); diff --git a/src_features/signTx/cmd_signTx.c b/src_features/signTx/cmd_signTx.c index f7ca07c..e14751a 100644 --- a/src_features/signTx/cmd_signTx.c +++ b/src_features/signTx/cmd_signTx.c @@ -7,7 +7,7 @@ void handleSign(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { UNUSED(tx); diff --git a/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c b/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c index 6e9efbf..4ec975c 100644 --- a/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c +++ b/src_features/stark_getPublicKey/cmd_stark_getPublicKey.c @@ -9,7 +9,7 @@ void handleStarkwareGetPublicKey(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { bip32_path_t bip32; diff --git a/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c index 89c68da..5f91945 100644 --- a/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c +++ b/src_features/stark_provideQuantum/cmd_stark_provideQuantum.c @@ -8,7 +8,7 @@ void handleStarkwareProvideQuantum(uint8_t p1, __attribute__((unused)) uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, __attribute__((unused)) unsigned int *flags, __attribute__((unused)) unsigned int *tx) { size_t i = 0; diff --git a/src_features/stark_sign/cmd_stark_sign.c b/src_features/stark_sign/cmd_stark_sign.c index 6d08635..2021176 100644 --- a/src_features/stark_sign/cmd_stark_sign.c +++ b/src_features/stark_sign/cmd_stark_sign.c @@ -15,7 +15,7 @@ void handleStarkwareSignMessage(uint8_t p1, uint8_t p2, uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, __attribute__((unused)) unsigned int *tx) { uint8_t privateKeyData[INT256_LENGTH]; diff --git a/src_features/stark_unsafe_sign/cmd_stark_unsafe_sign.c b/src_features/stark_unsafe_sign/cmd_stark_unsafe_sign.c index 33e3cad..6067648 100644 --- a/src_features/stark_unsafe_sign/cmd_stark_unsafe_sign.c +++ b/src_features/stark_unsafe_sign/cmd_stark_unsafe_sign.c @@ -9,7 +9,7 @@ void handleStarkwareUnsafeSign(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, __attribute__((unused)) unsigned int *tx) { uint8_t privateKeyData[INT256_LENGTH]; From b25eaa1b48b038d93c3206fd9509cfe490c4db3b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 3 May 2022 09:33:46 +0200 Subject: [PATCH 002/184] Renamed the existing EIP712 implementation folder --- src/apdu_constants.h | 12 ++++++------ src/main.c | 12 ++++++------ .../cmd_signMessage712.c | 14 ++++++++------ .../ui_common_signMessage712.c | 0 .../ui_flow_signMessage712.c | 0 5 files changed, 20 insertions(+), 18 deletions(-) rename src_features/{signMessageEIP712 => signMessageEIP712_v0}/cmd_signMessage712.c (70%) rename src_features/{signMessageEIP712 => signMessageEIP712_v0}/ui_common_signMessage712.c (100%) rename src_features/{signMessageEIP712 => signMessageEIP712_v0}/ui_flow_signMessage712.c (100%) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 3062674..1626178 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -94,12 +94,12 @@ void handleSignPersonalMessage(uint8_t p1, uint8_t dataLength, unsigned int *flags, unsigned int *tx); -void handleSignEIP712Message(uint8_t p1, - uint8_t p2, - const uint8_t *dataBuffer, - uint16_t dataLength, - unsigned int *flags, - unsigned int *tx); +void handleSignEIP712Message_v0(uint8_t p1, + uint8_t p2, + const uint8_t *dataBuffer, + uint16_t dataLength, + unsigned int *flags, + unsigned int *tx); void handleSetExternalPlugin(uint8_t p1, uint8_t p2, diff --git a/src/main.c b/src/main.c index d34d5c1..a02a08a 100644 --- a/src/main.c +++ b/src/main.c @@ -663,12 +663,12 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { case INS_SIGN_EIP_712_MESSAGE: memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); - handleSignEIP712Message(G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - flags, - tx); + handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + flags, + tx); break; #ifdef HAVE_ETH2 diff --git a/src_features/signMessageEIP712/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c similarity index 70% rename from src_features/signMessageEIP712/cmd_signMessage712.c rename to src_features/signMessageEIP712_v0/cmd_signMessage712.c index d12356c..cd32dad 100644 --- a/src_features/signMessageEIP712/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -3,12 +3,14 @@ #include "utils.h" #include "ui_flow.h" -void handleSignEIP712Message(uint8_t p1, - uint8_t p2, - const uint8_t *workBuffer, - uint16_t dataLength, - unsigned int *flags, - unsigned int *tx) { +void handleSignEIP712Message_v0(uint8_t p1, + uint8_t p2, + const uint8_t *workBuffer, + uint16_t dataLength, + unsigned int *flags, + unsigned int *tx) { + uint8_t i; + UNUSED(tx); if ((p1 != 00) || (p2 != 00)) { THROW(0x6B00); diff --git a/src_features/signMessageEIP712/ui_common_signMessage712.c b/src_features/signMessageEIP712_v0/ui_common_signMessage712.c similarity index 100% rename from src_features/signMessageEIP712/ui_common_signMessage712.c rename to src_features/signMessageEIP712_v0/ui_common_signMessage712.c diff --git a/src_features/signMessageEIP712/ui_flow_signMessage712.c b/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c similarity index 100% rename from src_features/signMessageEIP712/ui_flow_signMessage712.c rename to src_features/signMessageEIP712_v0/ui_flow_signMessage712.c From 1bc86754733c9c86518b71b8014356048ebeba09 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 4 Mar 2022 18:15:37 +0100 Subject: [PATCH 003/184] Initial version --- src_features/signMessageEIP712/entrypoint.c | 269 ++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src_features/signMessageEIP712/entrypoint.c diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c new file mode 100644 index 0000000..5fc05b5 --- /dev/null +++ b/src_features/signMessageEIP712/entrypoint.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include +#include + + +enum { + OFFSET_CLA = 0, + OFFSET_INS, + OFFSET_P1, + OFFSET_P2, + OFFSET_LC, + OFFSET_DATA +}; + +typedef enum +{ + // contract defined struct + TYPE_CUSTOM = 0, + // native types + TYPE_SOLIDITY_INT, + TYPE_SOLIDITY_UINT, + TYPE_SOLIDITY_ADDRESS, + TYPE_SOLIDITY_BOOL, + TYPE_SOLIDITY_STRING, + TYPE_SOLIDITY_BYTE, + TYPE_SOLIDITY_BYTES_FIX, + TYPE_SOLIDITY_BYTES_DYN +} e_type; + + +// APDUs INS +#define INS_STRUCT_DEF 0x18 +#define INS_STRUCT_IMPL 0x1A + +// TypeDesc masks +#define TYPE_MASK (0xF) +#define ARRAY_MASK (1 << 7) +#define TYPESIZE_MASK (1 << 6) + + +#define SIZE_MEM_BUFFER 1024 +uint8_t mem_buffer[SIZE_MEM_BUFFER]; +uint16_t mem_idx; + +uint8_t *structs_array; +uint8_t *current_struct_fields_array; + + + +bool set_struct_name(uint8_t *data) +{ + // increment number of structs + *structs_array += 1; + + // copy length + mem_buffer[mem_idx++] = data[OFFSET_LC]; + + // copy name + memmove(&mem_buffer[mem_idx], &data[OFFSET_DATA], data[OFFSET_LC]); + mem_idx += data[OFFSET_LC]; + + // initialize number of fields + current_struct_fields_array = &mem_buffer[mem_idx]; + mem_buffer[mem_idx++] = 0; + return true; +} + +bool set_struct_field(uint8_t *data) +{ + uint8_t data_idx = OFFSET_DATA; + uint8_t type_desc, len; + + // increment number of struct fields + *current_struct_fields_array += 1; + + // copy TypeDesc + type_desc = data[data_idx++]; + mem_buffer[mem_idx++] = type_desc; + + // check TypeSize flag in TypeDesc + if (type_desc & TYPESIZE_MASK) + { + // copy TypeSize + mem_buffer[mem_idx++] = data[data_idx++]; + } + if ((type_desc & TYPE_MASK) == TYPE_CUSTOM) + { + len = data[data_idx++]; + // copy custom struct name length + mem_buffer[mem_idx++] = len; + // copy name + memmove(&mem_buffer[mem_idx], &data[data_idx], len); + mem_idx += len; + data_idx += len; + } + + // copy length + len = data[data_idx++]; + mem_buffer[mem_idx++] = len; + + // copy name + memmove(&mem_buffer[mem_idx], &data[data_idx], len); + mem_idx += len; + return true; +} + +void dump_mem(void) +{ + uint8_t *mem = structs_array + 1; + uint8_t type_desc; + uint8_t fields_count; + + for (int i = 0; i < *structs_array; ++i) + { + fwrite(mem + 1, *mem, sizeof(*mem), stdout); + printf("("); + mem += (1 + *mem); + fields_count = *mem; + mem += 1; + for (int y = 0; y < fields_count; ++y) + { + if (y > 0) printf(","); + type_desc = *mem; + mem += 1; + switch (type_desc & TYPE_MASK) + { + case TYPE_CUSTOM: + fwrite(mem + 1, *mem, sizeof(*mem), stdout); + mem += (1 + *mem); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + case TYPE_SOLIDITY_UINT: + printf("u"); + case TYPE_SOLIDITY_INT: + printf("int"); + if (type_desc & TYPESIZE_MASK) + { + printf("%d", (*mem * 8)); + mem += 1; + } + break; + case TYPE_SOLIDITY_ADDRESS: + printf("address"); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + case TYPE_SOLIDITY_BOOL: + printf("bool"); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + case TYPE_SOLIDITY_STRING: + printf("string"); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + case TYPE_SOLIDITY_BYTE: + printf("byte"); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + case TYPE_SOLIDITY_BYTES_FIX: + printf("bytes"); + if (type_desc & TYPESIZE_MASK) + { + printf("%d", *mem); + mem += 1; + } + break; + case TYPE_SOLIDITY_BYTES_DYN: + printf("bytes"); + if (type_desc & TYPESIZE_MASK) mem += 1; + break; + default: + // should not be in here :^) + break; + } + if (type_desc & ARRAY_MASK) printf("[]"); + printf(" "); + fwrite(mem + 1, *mem, sizeof(*mem), stdout); + mem += (1 + *mem); + } + printf(")\n"); + } +} + +bool handle_apdu(uint8_t *data) +{ + switch (data[OFFSET_INS]) + { + case INS_STRUCT_DEF: + switch (data[OFFSET_P2]) + { + case 0x00: + set_struct_name(data); + break; + case 0xFF: + set_struct_field(data); + break; + default: + printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); + return false; + } + break; + case INS_STRUCT_IMPL: + break; + default: + printf("Unrecognized APDU"); + return false; + } + return true; +} + +void init_heap(void) +{ + // init global variables + mem_idx = 0; + + // set types pointer + structs_array = &mem_buffer[mem_idx]; + + // create len(types) + mem_buffer[mem_idx++] = 0; +} + +int main(void) +{ + uint8_t buf[256]; + uint8_t idx; + int state; + uint8_t payload_size; + + init_heap(); + + state = OFFSET_CLA; + idx = 0; + while (true) + { + if (fread(&buf[idx], sizeof(buf[0]), 1, stdin) == 0) break; + switch (state) + { + case OFFSET_CLA: + case OFFSET_INS: + case OFFSET_P1: + case OFFSET_P2: + state += 1; + idx += 1; + break; + case OFFSET_LC: + payload_size = buf[idx]; + state = OFFSET_DATA; + idx += 1; + break; + case OFFSET_DATA: + if (--payload_size == 0) + { + handle_apdu(buf); + state = OFFSET_CLA; + idx = 0; + } + else idx += 1; + break; + default: + printf("Unexpected APDU state!\n"); + return EXIT_FAILURE; + } + } + dump_mem(); + printf("\n%d bytes used in RAM\n", mem_idx); + return EXIT_SUCCESS; +} From 3b6c32d707b32e8ce1b1957513efa6106afd2b4f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 14 Mar 2022 17:29:48 +0100 Subject: [PATCH 004/184] Updated C code with new array handling --- src_features/signMessageEIP712/entrypoint.c | 82 ++++++++++++++++----- 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 5fc05b5..9858363 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -19,16 +19,23 @@ typedef enum // contract defined struct TYPE_CUSTOM = 0, // native types - TYPE_SOLIDITY_INT, - TYPE_SOLIDITY_UINT, - TYPE_SOLIDITY_ADDRESS, - TYPE_SOLIDITY_BOOL, - TYPE_SOLIDITY_STRING, - TYPE_SOLIDITY_BYTE, - TYPE_SOLIDITY_BYTES_FIX, - TYPE_SOLIDITY_BYTES_DYN + TYPE_SOL_INT, + TYPE_SOL_UINT, + TYPE_SOL_ADDRESS, + TYPE_SOL_BOOL, + TYPE_SOL_STRING, + TYPE_SOL_BYTE, + TYPE_SOL_BYTES_FIX, + TYPE_SOL_BYTES_DYN, + TYPES_COUNT } e_type; +typedef enum +{ + ARRAY_DYNAMIC = 0, + ARRAY_FIXED_SIZE +} e_array_type; + // APDUs INS #define INS_STRUCT_DEF 0x18 @@ -85,7 +92,7 @@ bool set_struct_field(uint8_t *data) // copy TypeSize mem_buffer[mem_idx++] = data[data_idx++]; } - if ((type_desc & TYPE_MASK) == TYPE_CUSTOM) + else if ((type_desc & TYPE_MASK) == TYPE_CUSTOM) { len = data[data_idx++]; // copy custom struct name length @@ -95,6 +102,26 @@ bool set_struct_field(uint8_t *data) mem_idx += len; data_idx += len; } + if (type_desc & ARRAY_MASK) + { + len = data[data_idx++]; + mem_buffer[mem_idx++] = len; + while (len-- > 0) + { + mem_buffer[mem_idx++] = data[data_idx]; + switch (data[data_idx++]) + { + case ARRAY_DYNAMIC: // nothing to do + break; + case ARRAY_FIXED_SIZE: + mem_buffer[mem_idx++] = data[data_idx++]; + break; + default: + // should not be in here :^) + break; + } + } + } // copy length len = data[data_idx++]; @@ -111,6 +138,7 @@ void dump_mem(void) uint8_t *mem = structs_array + 1; uint8_t type_desc; uint8_t fields_count; + uint8_t array_depth; for (int i = 0; i < *structs_array; ++i) { @@ -131,9 +159,9 @@ void dump_mem(void) mem += (1 + *mem); if (type_desc & TYPESIZE_MASK) mem += 1; break; - case TYPE_SOLIDITY_UINT: + case TYPE_SOL_UINT: printf("u"); - case TYPE_SOLIDITY_INT: + case TYPE_SOL_INT: printf("int"); if (type_desc & TYPESIZE_MASK) { @@ -141,23 +169,23 @@ void dump_mem(void) mem += 1; } break; - case TYPE_SOLIDITY_ADDRESS: + case TYPE_SOL_ADDRESS: printf("address"); if (type_desc & TYPESIZE_MASK) mem += 1; break; - case TYPE_SOLIDITY_BOOL: + case TYPE_SOL_BOOL: printf("bool"); if (type_desc & TYPESIZE_MASK) mem += 1; break; - case TYPE_SOLIDITY_STRING: + case TYPE_SOL_STRING: printf("string"); if (type_desc & TYPESIZE_MASK) mem += 1; break; - case TYPE_SOLIDITY_BYTE: + case TYPE_SOL_BYTE: printf("byte"); if (type_desc & TYPESIZE_MASK) mem += 1; break; - case TYPE_SOLIDITY_BYTES_FIX: + case TYPE_SOL_BYTES_FIX: printf("bytes"); if (type_desc & TYPESIZE_MASK) { @@ -165,7 +193,7 @@ void dump_mem(void) mem += 1; } break; - case TYPE_SOLIDITY_BYTES_DYN: + case TYPE_SOL_BYTES_DYN: printf("bytes"); if (type_desc & TYPESIZE_MASK) mem += 1; break; @@ -173,7 +201,25 @@ void dump_mem(void) // should not be in here :^) break; } - if (type_desc & ARRAY_MASK) printf("[]"); + if (type_desc & ARRAY_MASK) + { + array_depth = *mem++; + while (array_depth-- > 0) + { + switch (*mem++) + { + case ARRAY_DYNAMIC: + printf("[]"); + break; + case ARRAY_FIXED_SIZE: + printf("[%u]", *mem++); + break; + default: + // should not be in here :^) + break; + } + } + } printf(" "); fwrite(mem + 1, *mem, sizeof(*mem), stdout); mem += (1 + *mem); From c3dc0c18ffdb09a0ef1c7b7de769bbc18e0225d1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 16 Mar 2022 11:59:33 +0100 Subject: [PATCH 005/184] Refactoring; added some utility functions to navigate the type definitions --- src_features/signMessageEIP712/entrypoint.c | 408 ++++++++++++++++---- 1 file changed, 339 insertions(+), 69 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 9858363..a4cf069 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -51,9 +51,242 @@ typedef enum uint8_t mem_buffer[SIZE_MEM_BUFFER]; uint16_t mem_idx; +uint8_t *typename_matcher_array; +uint8_t *typenames_array; uint8_t *structs_array; uint8_t *current_struct_fields_array; +// Typename matcher masks +#define TYPENAME_MATCHER_ENUM(m) ((m >> 4) & 0xF) +#define TYPENAME_MATCHER_IDX(m) (m & 0xF) + +// lib functions + +const uint8_t *get_array_in_mem(const uint8_t *ptr, uint8_t *const array_size) +{ + *array_size = *ptr; + return (ptr + 1); +} + +static inline const uint8_t *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) +{ + return get_array_in_mem(ptr, string_length); +} + +// ptr must point to the beginning of a struct field +uint8_t get_struct_field_typedesc(const uint8_t *ptr) +{ + return *ptr; +} + +// ptr must point to the beginning of a struct field +bool struct_field_is_array(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & ARRAY_MASK); +} + +// ptr must point to the beginning of a struct field +bool struct_field_has_typesize(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); +} + +// ptr must point to the beginning of a struct field +e_type struct_field_type(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & TYPE_MASK); +} + +// ptr must point to the beginning of a struct field +// TODO: Extra check inside or not +uint8_t get_struct_field_typesize(const uint8_t *ptr) +{ + return *(ptr + 1); +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_custom_typename(const uint8_t *ptr, + uint8_t *const length) +{ + ptr += 1; // skip TypeDesc + return get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_sol_typename(const uint8_t *ptr, + uint8_t *const length) +{ + e_type field_type; + + uint8_t matcher_count; + const uint8_t *matcher; + uint8_t matcher_idx; + uint8_t typenames_count; + const uint8_t *typename_ptr; + + field_type = struct_field_type(ptr); + matcher = get_array_in_mem(typename_matcher_array, &matcher_count); + while (matcher_count-- > 0) + { + if (TYPENAME_MATCHER_ENUM(*matcher) == field_type) + { + matcher_idx = TYPENAME_MATCHER_IDX(*matcher); + typename_ptr = get_array_in_mem(typenames_array, &typenames_count); + // if the index, somehow, can't fit in the typenames array + if ((matcher_idx + 1) > typenames_count) return NULL; + while (typenames_count-- > 0) + { + typename_ptr = get_string_in_mem(typename_ptr, length); + if (matcher_idx-- == 0) + { + return typename_ptr; + } + typename_ptr += *length; // skip this typename, get next one + } + return NULL; // Not found + } + matcher += 1; // get next one + } + return NULL; // Not found +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_typename(const uint8_t *ptr, + uint8_t *const length) +{ + if (struct_field_type(ptr) == TYPE_CUSTOM) + { + return get_struct_field_custom_typename(ptr, length); + } + return get_struct_field_sol_typename(ptr, length); +} + +// ptr must point to the beginning of a depth level +e_array_type struct_field_array_depth(const uint8_t *ptr, + uint8_t *const array_size) +{ + if (*ptr == ARRAY_FIXED_SIZE) + { + *array_size = *(ptr + 1); + } + return *ptr; +} + +// ptr must point to the beginning of a struct field level +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) +{ + switch (*ptr) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + ptr += 1; + break; + default: + // should not be in here :^) + break; + } + return ptr + 1; +} + +// Skips TypeDesc and TypeSize/Length+TypeName +// Came to be since it is used in multiple functions +// TODO: Find better name +const uint8_t *struct_field_half_skip(const uint8_t *ptr) +{ + const uint8_t *field_ptr; + uint8_t size; + + field_ptr = ptr; + ptr += 1; // skip TypeDesc + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + get_string_in_mem(ptr, &size); + ptr += (1 + size); // skip typename + } + else if (struct_field_has_typesize(field_ptr)) + { + ptr += 1; // skip TypeSize + } + return ptr; +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, + uint8_t *const length) +{ + ptr = struct_field_half_skip(ptr); + return get_array_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_keyname(const uint8_t *ptr, + uint8_t *const length) +{ + const uint8_t *field_ptr; + uint8_t size; + + field_ptr = ptr; + ptr = struct_field_half_skip(ptr); + if (struct_field_is_array(field_ptr)) + { + ptr = get_array_in_mem(ptr, &size); + while (size-- > 0) + { + ptr = get_next_struct_field_array_lvl(ptr); + } + } + return get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_next_struct_field(const uint8_t *ptr) +{ + uint8_t length; + + ptr = get_struct_field_keyname(ptr, &length); + return (ptr + length); +} + +// ptr must point to the beginning of a struct +const uint8_t *get_struct_name(const uint8_t *ptr, uint8_t *const length) +{ + return get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct +const uint8_t *get_struct_fields_array(const uint8_t *ptr, + uint8_t *const length) +{ + uint8_t name_length; + + get_struct_name(ptr, &name_length); + ptr += (1 + name_length); // skip length + return get_array_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct +const uint8_t *get_next_struct(const uint8_t *ptr) +{ + uint8_t fields_count; + + ptr = get_struct_fields_array(ptr, &fields_count); + while (fields_count-- > 0) + { + ptr = get_next_struct_field(ptr); + } + return ptr; +} + +// ptr must point to the size of the structs array +const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) +{ + return get_array_in_mem(ptr, length); +} +// + + + bool set_struct_name(uint8_t *data) @@ -133,99 +366,85 @@ bool set_struct_field(uint8_t *data) return true; } + void dump_mem(void) { - uint8_t *mem = structs_array + 1; - uint8_t type_desc; + uint8_t structs_count; + const uint8_t *struct_ptr; + // uint8_t fields_count; - uint8_t array_depth; + const uint8_t *field_ptr; + // + uint8_t lvls_count; + const uint8_t *lvl_ptr; - for (int i = 0; i < *structs_array; ++i) + const uint8_t *name; + uint8_t name_length; + // + uint8_t array_size; + uint8_t byte_size; + + + struct_ptr = get_structs_array(structs_array, &structs_count); + while (structs_count-- > 0) { - fwrite(mem + 1, *mem, sizeof(*mem), stdout); + name = get_struct_name(struct_ptr, &name_length); + fwrite(name, sizeof(*name), name_length, stdout); printf("("); - mem += (1 + *mem); - fields_count = *mem; - mem += 1; - for (int y = 0; y < fields_count; ++y) + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (int idx = 0; idx < fields_count; ++idx) { - if (y > 0) printf(","); - type_desc = *mem; - mem += 1; - switch (type_desc & TYPE_MASK) + if (idx > 0) printf(","); + name = get_struct_field_typename(field_ptr, &name_length); + fwrite(name, sizeof(*name), name_length, stdout); + if (struct_field_has_typesize(field_ptr)) { - case TYPE_CUSTOM: - fwrite(mem + 1, *mem, sizeof(*mem), stdout); - mem += (1 + *mem); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - case TYPE_SOL_UINT: - printf("u"); - case TYPE_SOL_INT: - printf("int"); - if (type_desc & TYPESIZE_MASK) - { - printf("%d", (*mem * 8)); - mem += 1; - } - break; - case TYPE_SOL_ADDRESS: - printf("address"); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - case TYPE_SOL_BOOL: - printf("bool"); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - case TYPE_SOL_STRING: - printf("string"); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - case TYPE_SOL_BYTE: - printf("byte"); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - case TYPE_SOL_BYTES_FIX: - printf("bytes"); - if (type_desc & TYPESIZE_MASK) - { - printf("%d", *mem); - mem += 1; - } - break; - case TYPE_SOL_BYTES_DYN: - printf("bytes"); - if (type_desc & TYPESIZE_MASK) mem += 1; - break; - default: - // should not be in here :^) - break; - } - if (type_desc & ARRAY_MASK) - { - array_depth = *mem++; - while (array_depth-- > 0) + byte_size = get_struct_field_typesize(field_ptr); + switch(struct_field_type(field_ptr)) { - switch (*mem++) + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + printf("%u", (byte_size * 8)); + break; + case TYPE_SOL_BYTES_FIX: + printf("%u", byte_size); + break; + default: + // should not be in here :^) + break; + } + } + if (struct_field_is_array(field_ptr)) + { + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + while (lvls_count-- > 0) + { + printf("["); + switch (struct_field_array_depth(lvl_ptr, &array_size)) { case ARRAY_DYNAMIC: - printf("[]"); break; case ARRAY_FIXED_SIZE: - printf("[%u]", *mem++); + printf("%u", array_size); break; default: // should not be in here :^) break; } + printf("]"); + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); } } printf(" "); - fwrite(mem + 1, *mem, sizeof(*mem), stdout); - mem += (1 + *mem); + name = get_struct_field_keyname(field_ptr, &name_length); + fwrite(name, sizeof(*name), name_length, stdout); + + field_ptr = get_next_struct_field(field_ptr); } printf(")\n"); + struct_ptr = get_next_struct(struct_ptr); } + return; } bool handle_apdu(uint8_t *data) @@ -255,11 +474,62 @@ bool handle_apdu(uint8_t *data) return true; } +bool init_typenames(void) +{ + char *typenames_str[] = { + "int", + "uint", + "address", + "bool", + "string", + "byte", + "bytes" + }; + // table to match type enums to indexes in the typenames string array + int enum_to_str[][2] = { + { TYPE_SOL_INT, 0 }, + { TYPE_SOL_UINT, 1}, + { TYPE_SOL_ADDRESS, 2 }, + { TYPE_SOL_BOOL, 3 }, + { TYPE_SOL_STRING, 4 }, + { TYPE_SOL_BYTE, 5 }, + { TYPE_SOL_BYTES_FIX, 6 }, + { TYPE_SOL_BYTES_DYN, 6 } + }; + + typename_matcher_array = &mem_buffer[mem_idx++]; + *typename_matcher_array = 0; + // set matchers + for (size_t i = 0; i < (sizeof(enum_to_str) / sizeof(enum_to_str[0])); ++i) + { + mem_buffer[mem_idx++] = ((enum_to_str[i][0] << 4) | (enum_to_str[i][1])); + // increment array size + *typename_matcher_array += 1; + } + + typenames_array = &mem_buffer[mem_idx++]; + *typenames_array = 0; + // set typenames + for (size_t i = 0; i < (sizeof(typenames_str) / sizeof(typenames_str[0])); ++i) + { + mem_buffer[mem_idx++] = strlen(typenames_str[i]); // length + // copy typename + memcpy(&mem_buffer[mem_idx], typenames_str[i], mem_buffer[mem_idx - 1]); + // increment mem idx by typename length + mem_idx += mem_buffer[mem_idx - 1]; + // increment array size + *typenames_array += 1; + } + return true; +} + void init_heap(void) { // init global variables mem_idx = 0; + init_typenames(); + // set types pointer structs_array = &mem_buffer[mem_idx]; From 545bc439fe93e49790c6a420ca1e0fe220123efd Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 16 Mar 2022 14:41:46 +0100 Subject: [PATCH 006/184] Better implementation of enum(s)->typename memory structure --- src_features/signMessageEIP712/entrypoint.c | 99 +++++++++++---------- 1 file changed, 53 insertions(+), 46 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index a4cf069..bcba3fe 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -46,22 +46,19 @@ typedef enum #define ARRAY_MASK (1 << 7) #define TYPESIZE_MASK (1 << 6) +// Solidity typename mask +#define TYPENAME_MORE_TYPE (1 << 7) +#define TYPENAME_ENUM (0xF) #define SIZE_MEM_BUFFER 1024 uint8_t mem_buffer[SIZE_MEM_BUFFER]; uint16_t mem_idx; -uint8_t *typename_matcher_array; uint8_t *typenames_array; uint8_t *structs_array; uint8_t *current_struct_fields_array; -// Typename matcher masks -#define TYPENAME_MATCHER_ENUM(m) ((m >> 4) & 0xF) -#define TYPENAME_MATCHER_IDX(m) (m & 0xF) - // lib functions - const uint8_t *get_array_in_mem(const uint8_t *ptr, uint8_t *const array_size) { *array_size = *ptr; @@ -117,35 +114,30 @@ const uint8_t *get_struct_field_sol_typename(const uint8_t *ptr, uint8_t *const length) { e_type field_type; - - uint8_t matcher_count; - const uint8_t *matcher; - uint8_t matcher_idx; - uint8_t typenames_count; const uint8_t *typename_ptr; + uint8_t typenames_count; + bool more_type; + bool typename_found; field_type = struct_field_type(ptr); - matcher = get_array_in_mem(typename_matcher_array, &matcher_count); - while (matcher_count-- > 0) + typename_ptr = get_array_in_mem(typenames_array, &typenames_count); + typename_found = false; + while (typenames_count-- > 0) { - if (TYPENAME_MATCHER_ENUM(*matcher) == field_type) + more_type = true; + while (more_type) { - matcher_idx = TYPENAME_MATCHER_IDX(*matcher); - typename_ptr = get_array_in_mem(typenames_array, &typenames_count); - // if the index, somehow, can't fit in the typenames array - if ((matcher_idx + 1) > typenames_count) return NULL; - while (typenames_count-- > 0) + more_type = *typename_ptr & TYPENAME_MORE_TYPE; + e_type type_enum = *typename_ptr & TYPENAME_ENUM; + if (type_enum == field_type) { - typename_ptr = get_string_in_mem(typename_ptr, length); - if (matcher_idx-- == 0) - { - return typename_ptr; - } - typename_ptr += *length; // skip this typename, get next one + typename_found = true; } - return NULL; // Not found + typename_ptr += 1; } - matcher += 1; // get next one + typename_ptr = get_string_in_mem(typename_ptr, length); + if (typename_found) return typename_ptr; + typename_ptr += *length; } return NULL; // Not found } @@ -485,8 +477,7 @@ bool init_typenames(void) "byte", "bytes" }; - // table to match type enums to indexes in the typenames string array - int enum_to_str[][2] = { + int enum_to_idx[][2] = { { TYPE_SOL_INT, 0 }, { TYPE_SOL_UINT, 1}, { TYPE_SOL_ADDRESS, 2 }, @@ -496,27 +487,43 @@ bool init_typenames(void) { TYPE_SOL_BYTES_FIX, 6 }, { TYPE_SOL_BYTES_DYN, 6 } }; - - typename_matcher_array = &mem_buffer[mem_idx++]; - *typename_matcher_array = 0; - // set matchers - for (size_t i = 0; i < (sizeof(enum_to_str) / sizeof(enum_to_str[0])); ++i) - { - mem_buffer[mem_idx++] = ((enum_to_str[i][0] << 4) | (enum_to_str[i][1])); - // increment array size - *typename_matcher_array += 1; - } + bool first_match; typenames_array = &mem_buffer[mem_idx++]; *typenames_array = 0; - // set typenames - for (size_t i = 0; i < (sizeof(typenames_str) / sizeof(typenames_str[0])); ++i) + // loop over typenames + for (size_t s_idx = 0; + s_idx < (sizeof(typenames_str) / sizeof(typenames_str[0])); + ++s_idx) { - mem_buffer[mem_idx++] = strlen(typenames_str[i]); // length - // copy typename - memcpy(&mem_buffer[mem_idx], typenames_str[i], mem_buffer[mem_idx - 1]); - // increment mem idx by typename length - mem_idx += mem_buffer[mem_idx - 1]; + first_match = true; + // loop over enum/typename pairs + for (size_t e_idx = 0; + e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[0])); + ++e_idx) + { + if (s_idx == (size_t)enum_to_idx[e_idx][1]) // match + { + mem_buffer[mem_idx] = enum_to_idx[e_idx][0]; + if (!first_match) // in case of a previous match, mark it + { + mem_buffer[mem_idx - 1] |= TYPENAME_MORE_TYPE; + } + mem_idx += 1; + first_match = false; + } + } + + if (!first_match) // if at least one match was found + { + mem_buffer[mem_idx++] = strlen(typenames_str[s_idx]); + // copy typename + memcpy(&mem_buffer[mem_idx], + typenames_str[s_idx], + mem_buffer[mem_idx - 1]); + // increment mem idx by typename length + mem_idx += mem_buffer[mem_idx - 1]; + } // increment array size *typenames_array += 1; } From 024691eb642c772f90abd66b32c204e428699c48 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 16 Mar 2022 15:26:28 +0100 Subject: [PATCH 007/184] Fix warning in release mode --- src_features/signMessageEIP712/entrypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index bcba3fe..06dfc88 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -549,7 +549,7 @@ int main(void) uint8_t buf[256]; uint8_t idx; int state; - uint8_t payload_size; + uint8_t payload_size = 0; init_heap(); From 63f454b29ad60ddc966f8babaefc1f968db1ec54 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 17 Mar 2022 10:18:29 +0100 Subject: [PATCH 008/184] Fixed ram usage message --- src_features/signMessageEIP712/entrypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 06dfc88..241e073 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -587,6 +587,6 @@ int main(void) } } dump_mem(); - printf("\n%d bytes used in RAM\n", mem_idx); + printf("\n%d bytes used in RAM\n", (mem_idx + 1)); return EXIT_SUCCESS; } From 5438b39eb25c346e91095ac3737ef6b0971a0012 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 21 Mar 2022 14:40:46 +0100 Subject: [PATCH 009/184] Fixes the index overflow, since header + data could be longer than 0xFF --- src_features/signMessageEIP712/entrypoint.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 241e073..da86552 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -41,6 +41,15 @@ typedef enum #define INS_STRUCT_DEF 0x18 #define INS_STRUCT_IMPL 0x1A +// APDUs P1 +#define P1_COMPLETE 0x00 +#define P1_PARTIAL 0xFF + +// APDUs P2 +#define P2_NAME 0x00 +#define P2_ARRAY 0x0F +#define P2_FIELD 0xFF + // TypeDesc masks #define TYPE_MASK (0xF) #define ARRAY_MASK (1 << 7) @@ -396,6 +405,7 @@ void dump_mem(void) { case TYPE_SOL_INT: case TYPE_SOL_UINT: + // bytes -> bits printf("%u", (byte_size * 8)); break; case TYPE_SOL_BYTES_FIX: @@ -446,10 +456,10 @@ bool handle_apdu(uint8_t *data) case INS_STRUCT_DEF: switch (data[OFFSET_P2]) { - case 0x00: + case P2_NAME: set_struct_name(data); break; - case 0xFF: + case P2_FIELD: set_struct_field(data); break; default: @@ -547,7 +557,7 @@ void init_heap(void) int main(void) { uint8_t buf[256]; - uint8_t idx; + uint16_t idx; int state; uint8_t payload_size = 0; @@ -555,9 +565,8 @@ int main(void) state = OFFSET_CLA; idx = 0; - while (true) + while (fread(&buf[idx], sizeof(buf[idx]), 1, stdin) > 0) { - if (fread(&buf[idx], sizeof(buf[0]), 1, stdin) == 0) break; switch (state) { case OFFSET_CLA: @@ -575,7 +584,7 @@ int main(void) case OFFSET_DATA: if (--payload_size == 0) { - handle_apdu(buf); + if (!handle_apdu(buf)) return false; state = OFFSET_CLA; idx = 0; } From 3fda01066706245df9cf9878ed60889e4748e75d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 14:41:00 +0200 Subject: [PATCH 010/184] Add memory functions --- src_features/signMessageEIP712/mem.c | 58 ++++++++++++++++++++++++++++ src_features/signMessageEIP712/mem.h | 13 +++++++ 2 files changed, 71 insertions(+) create mode 100644 src_features/signMessageEIP712/mem.c create mode 100644 src_features/signMessageEIP712/mem.h diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c new file mode 100644 index 0000000..0677783 --- /dev/null +++ b/src_features/signMessageEIP712/mem.c @@ -0,0 +1,58 @@ +#include +#include "mem.h" + +static uint8_t mem_buffer[SIZE_MEM_BUFFER]; +static size_t mem_idx; + +/** + * Initializes the memory buffer index + */ +void mem_init(void) +{ + mem_idx = 0; +} + +/** + * Resets the memory buffer index + */ +void mem_reset(void) +{ + mem_init(); +} + +/** + * Allocates a chunk of the memory buffer of a given size. + * Checks to see if there are enough space left in the memory buffer, returns + * the current location in the memory buffer and moves the index accordingly. + * + * @param[in] size Requested allocation size in bytes + * + * @return Allocated memory pointer; \ref NULL if not enough space left. + */ +void *mem_alloc(size_t size) +{ + if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded + { + return NULL; + } + mem_idx += size; + return &mem_buffer[mem_idx - size]; +} + + +/** + * De-allocates a chunk of memory buffer by a given size. + * + * @param[in] size Requested deallocation size in bytes + */ +void mem_dealloc(size_t size) +{ + if (size > mem_idx) // More than is already allocated + { + mem_idx = 0; + } + else + { + mem_idx -= size; + } +} diff --git a/src_features/signMessageEIP712/mem.h b/src_features/signMessageEIP712/mem.h new file mode 100644 index 0000000..2a7b179 --- /dev/null +++ b/src_features/signMessageEIP712/mem.h @@ -0,0 +1,13 @@ +#ifndef MEM_H_ +#define MEM_H_ + +#include + +#define SIZE_MEM_BUFFER 1024 + +void mem_init(void); +void mem_reset(void); +void *mem_alloc(size_t size); +void mem_dealloc(size_t size); + +#endif // MEM_H_ From e3a8e4ccd218953c70f7c279e4fb8ba9b92b7203 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:28:18 +0200 Subject: [PATCH 011/184] Big Refactoring - moved some things in header files - now uses memory allocation functions with automatic checks, small abstraction layer --- src_features/signMessageEIP712/eip712.h | 88 +++++ src_features/signMessageEIP712/entrypoint.c | 368 +++++++++++--------- 2 files changed, 284 insertions(+), 172 deletions(-) create mode 100644 src_features/signMessageEIP712/eip712.h diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h new file mode 100644 index 0000000..f700af3 --- /dev/null +++ b/src_features/signMessageEIP712/eip712.h @@ -0,0 +1,88 @@ +#ifndef EIP712_H_ +#define EIP712_H_ + +#include + +enum { + OFFSET_CLA = 0, + OFFSET_INS, + OFFSET_P1, + OFFSET_P2, + OFFSET_LC, + OFFSET_DATA +}; + +typedef enum +{ + // contract defined struct + TYPE_CUSTOM = 0, + // native types + TYPE_SOL_INT, + TYPE_SOL_UINT, + TYPE_SOL_ADDRESS, + TYPE_SOL_BOOL, + TYPE_SOL_STRING, + TYPE_SOL_BYTE, + TYPE_SOL_BYTES_FIX, + TYPE_SOL_BYTES_DYN, + TYPES_COUNT +} e_type; + +typedef enum +{ + ARRAY_DYNAMIC = 0, + ARRAY_FIXED_SIZE +} e_array_type; + +typedef enum +{ + IDX_ENUM = 0, + IDX_STR_IDX, + IDX_COUNT +} t_typename_matcher_idx; + +// APDUs INS +#define INS_STRUCT_DEF 0x18 +#define INS_STRUCT_IMPL 0x1A + +// APDUs P1 +#define P1_COMPLETE 0x00 +#define P1_PARTIAL 0xFF + +// APDUs P2 +#define P2_NAME 0x00 +#define P2_ARRAY 0x0F +#define P2_FIELD 0xFF + +// TypeDesc masks +#define TYPE_MASK (0xF) +#define ARRAY_MASK (1 << 7) +#define TYPESIZE_MASK (1 << 6) +#define TYPENAME_ENUM (0xF) + +// Solidity typenames array mask +#define TYPENAME_MORE_TYPE (1 << 7) // For custom typename + +typedef struct +{ + uint16_t length; + char *str; +} t_string; + +typedef struct +{ + uint16_t size; + uint8_t *ptr; +} t_array; + +typedef struct +{ + t_string type; + t_string key; + uint8_t bytesize; + t_array array_levels; +} t_struct_field; + +typedef void (*field_callback)(t_struct_field *field, uint8_t index); + +#endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index da86552..89cce9d 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -4,68 +4,13 @@ #include #include - -enum { - OFFSET_CLA = 0, - OFFSET_INS, - OFFSET_P1, - OFFSET_P2, - OFFSET_LC, - OFFSET_DATA -}; - -typedef enum -{ - // contract defined struct - TYPE_CUSTOM = 0, - // native types - TYPE_SOL_INT, - TYPE_SOL_UINT, - TYPE_SOL_ADDRESS, - TYPE_SOL_BOOL, - TYPE_SOL_STRING, - TYPE_SOL_BYTE, - TYPE_SOL_BYTES_FIX, - TYPE_SOL_BYTES_DYN, - TYPES_COUNT -} e_type; - -typedef enum -{ - ARRAY_DYNAMIC = 0, - ARRAY_FIXED_SIZE -} e_array_type; +#include "eip712.h" +#include "mem.h" -// APDUs INS -#define INS_STRUCT_DEF 0x18 -#define INS_STRUCT_IMPL 0x1A - -// APDUs P1 -#define P1_COMPLETE 0x00 -#define P1_PARTIAL 0xFF - -// APDUs P2 -#define P2_NAME 0x00 -#define P2_ARRAY 0x0F -#define P2_FIELD 0xFF - -// TypeDesc masks -#define TYPE_MASK (0xF) -#define ARRAY_MASK (1 << 7) -#define TYPESIZE_MASK (1 << 6) - -// Solidity typename mask -#define TYPENAME_MORE_TYPE (1 << 7) -#define TYPENAME_ENUM (0xF) - -#define SIZE_MEM_BUFFER 1024 -uint8_t mem_buffer[SIZE_MEM_BUFFER]; -uint16_t mem_idx; - -uint8_t *typenames_array; -uint8_t *structs_array; -uint8_t *current_struct_fields_array; +static uint8_t *typenames_array; +static uint8_t *structs_array; +static uint8_t *current_struct_fields_array; // lib functions const uint8_t *get_array_in_mem(const uint8_t *ptr, uint8_t *const array_size) @@ -80,25 +25,25 @@ static inline const uint8_t *get_string_in_mem(const uint8_t *ptr, uint8_t *cons } // ptr must point to the beginning of a struct field -uint8_t get_struct_field_typedesc(const uint8_t *ptr) +static inline uint8_t get_struct_field_typedesc(const uint8_t *ptr) { return *ptr; } // ptr must point to the beginning of a struct field -bool struct_field_is_array(const uint8_t *ptr) +static inline bool struct_field_is_array(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & ARRAY_MASK); } // ptr must point to the beginning of a struct field -bool struct_field_has_typesize(const uint8_t *ptr) +static inline bool struct_field_has_typesize(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); } // ptr must point to the beginning of a struct field -e_type struct_field_type(const uint8_t *ptr) +static inline e_type struct_field_type(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & TYPE_MASK); } @@ -284,31 +229,55 @@ const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) { return get_array_in_mem(ptr, length); } + +// Finds struct with a given name +const uint8_t *get_structn(const uint8_t *const ptr, + const uint8_t *const name_ptr, + uint8_t name_length) +{ + uint8_t structs_count; + const uint8_t *struct_ptr; + const uint8_t *name; + uint8_t length; + + struct_ptr = get_structs_array(ptr, &structs_count); + while (structs_count-- > 0) + { + name = get_struct_name(struct_ptr, &length); + if ((name_length == length) && (memcmp(name, name_ptr, length) == 0)) + { + return struct_ptr; + } + struct_ptr = get_next_struct(struct_ptr); + } + return NULL; +} + +static inline const uint8_t *get_struct(const uint8_t *const ptr, + const char *const name_ptr) +{ + return get_structn(ptr, (uint8_t*)name_ptr, strlen(name_ptr)); +} // - - - - -bool set_struct_name(uint8_t *data) +bool set_struct_name(const uint8_t *const data) { // increment number of structs *structs_array += 1; // copy length - mem_buffer[mem_idx++] = data[OFFSET_LC]; + *(uint8_t*)mem_alloc(1) = data[OFFSET_LC]; // copy name - memmove(&mem_buffer[mem_idx], &data[OFFSET_DATA], data[OFFSET_LC]); - mem_idx += data[OFFSET_LC]; + memmove(mem_alloc(data[OFFSET_LC]), &data[OFFSET_DATA], data[OFFSET_LC]); // initialize number of fields - current_struct_fields_array = &mem_buffer[mem_idx]; - mem_buffer[mem_idx++] = 0; + current_struct_fields_array = mem_alloc(1); + *current_struct_fields_array = 0; return true; } -bool set_struct_field(uint8_t *data) +bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_DATA; uint8_t type_desc, len; @@ -318,37 +287,36 @@ bool set_struct_field(uint8_t *data) // copy TypeDesc type_desc = data[data_idx++]; - mem_buffer[mem_idx++] = type_desc; + *(uint8_t*)mem_alloc(1) = type_desc; // check TypeSize flag in TypeDesc if (type_desc & TYPESIZE_MASK) { // copy TypeSize - mem_buffer[mem_idx++] = data[data_idx++]; + *(uint8_t*)mem_alloc(1) = data[data_idx++]; } else if ((type_desc & TYPE_MASK) == TYPE_CUSTOM) { len = data[data_idx++]; // copy custom struct name length - mem_buffer[mem_idx++] = len; + *(uint8_t*)mem_alloc(1) = len; // copy name - memmove(&mem_buffer[mem_idx], &data[data_idx], len); - mem_idx += len; + memmove(mem_alloc(len), &data[data_idx], len); data_idx += len; } if (type_desc & ARRAY_MASK) { len = data[data_idx++]; - mem_buffer[mem_idx++] = len; + *(uint8_t*)mem_alloc(1) = len; while (len-- > 0) { - mem_buffer[mem_idx++] = data[data_idx]; + *(uint8_t*)mem_alloc(1) = data[data_idx]; switch (data[data_idx++]) { case ARRAY_DYNAMIC: // nothing to do break; case ARRAY_FIXED_SIZE: - mem_buffer[mem_idx++] = data[data_idx++]; + *(uint8_t*)mem_alloc(1) = data[data_idx++]; break; default: // should not be in here :^) @@ -359,97 +327,142 @@ bool set_struct_field(uint8_t *data) // copy length len = data[data_idx++]; - mem_buffer[mem_idx++] = len; + *(uint8_t*)mem_alloc(1) = len; // copy name - memmove(&mem_buffer[mem_idx], &data[data_idx], len); - mem_idx += len; + memmove(mem_alloc(len), &data[data_idx], len); return true; } - -void dump_mem(void) +void get_struct_type_string(const uint8_t *const ptr, + const uint8_t *const struct_name, + uint8_t struct_name_length) { - uint8_t structs_count; - const uint8_t *struct_ptr; - // - uint8_t fields_count; + const uint8_t *const struct_ptr = get_structn(ptr, + struct_name, + struct_name_length); + uint16_t *typestr_length; + char *typestr; const uint8_t *field_ptr; - // + uint8_t fields_count; + const uint8_t *name; + uint8_t length; + uint16_t field_size; uint8_t lvls_count; const uint8_t *lvl_ptr; - - const uint8_t *name; - uint8_t name_length; - // uint8_t array_size; - uint8_t byte_size; + uint8_t formatted_length; + // set length + typestr_length = mem_alloc(sizeof(uint16_t)); + *typestr_length = 0; - struct_ptr = get_structs_array(structs_array, &structs_count); - while (structs_count-- > 0) + // add name + typestr = memmove(mem_alloc(struct_name_length), struct_name, struct_name_length); + *typestr_length += struct_name_length; + + *(char*)mem_alloc(sizeof(char)) = '('; + *typestr_length += 1; + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) { - name = get_struct_name(struct_ptr, &name_length); - fwrite(name, sizeof(*name), name_length, stdout); - printf("("); - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (int idx = 0; idx < fields_count; ++idx) + if (idx > 0) { - if (idx > 0) printf(","); - name = get_struct_field_typename(field_ptr, &name_length); - fwrite(name, sizeof(*name), name_length, stdout); - if (struct_field_has_typesize(field_ptr)) + *(char*)mem_alloc(sizeof(char)) = ','; + *typestr_length += 1; + } + + name = get_struct_field_typename(field_ptr, &length); + + memmove(mem_alloc(sizeof(char) * length), name, length); + *typestr_length += length; + + if (struct_field_has_typesize(field_ptr)) + { + field_size = get_struct_field_typesize(field_ptr); + switch (struct_field_type(field_ptr)) { - byte_size = get_struct_field_typesize(field_ptr); - switch(struct_field_type(field_ptr)) + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + field_size *= 8; // bytes -> bits + break; + case TYPE_SOL_BYTES_FIX: + break; + default: + // should not be in here :^) + break; + } + // max value = 256, 3 characters max + formatted_length = sprintf(mem_alloc(sizeof(char) * 3), "%u", field_size); + mem_dealloc(3 - formatted_length); // in case it used less + *typestr_length += formatted_length; + } + + if (struct_field_is_array(field_ptr)) + { + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + while (lvls_count-- > 0) + { + *(char*)mem_alloc(sizeof(char)) = '['; + *typestr_length += 1; + switch (struct_field_array_depth(lvl_ptr, &array_size)) { - case TYPE_SOL_INT: - case TYPE_SOL_UINT: - // bytes -> bits - printf("%u", (byte_size * 8)); + case ARRAY_DYNAMIC: break; - case TYPE_SOL_BYTES_FIX: - printf("%u", byte_size); + case ARRAY_FIXED_SIZE: + // max value = 255, 3 characters max + formatted_length = sprintf(mem_alloc(sizeof(char) * 3), "%u", array_size); + mem_dealloc(3 - formatted_length); + *typestr_length += formatted_length; break; default: // should not be in here :^) break; } + *(char*)mem_alloc(sizeof(char)) = ']'; + *typestr_length += 1; + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); } - if (struct_field_is_array(field_ptr)) - { - lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - while (lvls_count-- > 0) - { - printf("["); - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - printf("%u", array_size); - break; - default: - // should not be in here :^) - break; - } - printf("]"); - lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); - } - } - printf(" "); - name = get_struct_field_keyname(field_ptr, &name_length); - fwrite(name, sizeof(*name), name_length, stdout); - - field_ptr = get_next_struct_field(field_ptr); } - printf(")\n"); - struct_ptr = get_next_struct(struct_ptr); + *(char*)mem_alloc(sizeof(char)) = ' '; + *typestr_length += 1; + name = get_struct_field_keyname(field_ptr, &length); + + memmove(mem_alloc(sizeof(char) * length), name, length); + *typestr_length += length; + + field_ptr = get_next_struct_field(field_ptr); } - return; + *(char*)mem_alloc(sizeof(char)) = ')'; + *typestr_length += 1; + + // FIXME: DBG + fwrite(typestr, sizeof(char), *typestr_length, stdout); + printf("\n"); } -bool handle_apdu(uint8_t *data) +void get_type_hash(const uint8_t *const ptr, + const uint8_t *const struct_name, + uint8_t struct_name_length) +{ + void *mem_loc_bak; + uint16_t str_length; + + // backup the memory location + mem_loc_bak = mem_alloc(0); + + // get list of structs (own + dependencies), properly ordered + + // loop over each struct and generate string + str_length = 0; + get_struct_type_string(ptr, struct_name, struct_name_length); + + // restore the memory location + mem_dealloc(mem_alloc(0) - mem_loc_bak); +} + +bool handle_apdu(const uint8_t *const data) { switch (data[OFFSET_INS]) { @@ -468,9 +481,22 @@ bool handle_apdu(uint8_t *data) } break; case INS_STRUCT_IMPL: + switch (data[OFFSET_P2]) + { + case P2_NAME: + get_type_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); + break; + case P2_FIELD: + break; + case P2_ARRAY: + break; + default: + printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); + return false; + } break; default: - printf("Unrecognized APDU"); + printf("Unrecognized APDU (0x%.02x)\n", data[OFFSET_INS]); return false; } return true; @@ -478,7 +504,7 @@ bool handle_apdu(uint8_t *data) bool init_typenames(void) { - char *typenames_str[] = { + const char *const typenames_str[] = { "int", "uint", "address", @@ -487,7 +513,7 @@ bool init_typenames(void) "byte", "bytes" }; - int enum_to_idx[][2] = { + const int enum_to_idx[][IDX_COUNT] = { { TYPE_SOL_INT, 0 }, { TYPE_SOL_UINT, 1}, { TYPE_SOL_ADDRESS, 2 }, @@ -497,42 +523,41 @@ bool init_typenames(void) { TYPE_SOL_BYTES_FIX, 6 }, { TYPE_SOL_BYTES_DYN, 6 } }; - bool first_match; + uint8_t *previous_match; + uint8_t typename_len; - typenames_array = &mem_buffer[mem_idx++]; + typenames_array = mem_alloc(1); *typenames_array = 0; // loop over typenames for (size_t s_idx = 0; - s_idx < (sizeof(typenames_str) / sizeof(typenames_str[0])); + s_idx < (sizeof(typenames_str) / sizeof(typenames_str[IDX_ENUM])); ++s_idx) { - first_match = true; + previous_match = NULL; // loop over enum/typename pairs for (size_t e_idx = 0; - e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[0])); + e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[IDX_ENUM])); ++e_idx) { - if (s_idx == (size_t)enum_to_idx[e_idx][1]) // match + if (s_idx == (size_t)enum_to_idx[e_idx][IDX_STR_IDX]) // match { - mem_buffer[mem_idx] = enum_to_idx[e_idx][0]; - if (!first_match) // in case of a previous match, mark it + if (previous_match) // in case of a previous match, mark it { - mem_buffer[mem_idx - 1] |= TYPENAME_MORE_TYPE; + *previous_match |= TYPENAME_MORE_TYPE; } - mem_idx += 1; - first_match = false; + previous_match = mem_alloc(1); + *previous_match = enum_to_idx[e_idx][IDX_ENUM]; } } - if (!first_match) // if at least one match was found + if (previous_match) // if at least one match was found { - mem_buffer[mem_idx++] = strlen(typenames_str[s_idx]); + typename_len = strlen(typenames_str[s_idx]); + *(uint8_t*)mem_alloc(1) = typename_len; // copy typename - memcpy(&mem_buffer[mem_idx], + memcpy(mem_alloc(typename_len), typenames_str[s_idx], - mem_buffer[mem_idx - 1]); - // increment mem idx by typename length - mem_idx += mem_buffer[mem_idx - 1]; + typename_len); } // increment array size *typenames_array += 1; @@ -543,15 +568,15 @@ bool init_typenames(void) void init_heap(void) { // init global variables - mem_idx = 0; + init_mem(); init_typenames(); // set types pointer - structs_array = &mem_buffer[mem_idx]; + structs_array = mem_alloc(1); // create len(types) - mem_buffer[mem_idx++] = 0; + *structs_array = 0; } int main(void) @@ -595,7 +620,6 @@ int main(void) return EXIT_FAILURE; } } - dump_mem(); - printf("\n%d bytes used in RAM\n", (mem_idx + 1)); + //printf("\n%d bytes used in RAM\n", (mem_idx + 1)); return EXIT_SUCCESS; } From 420afdf958a4e3259ebed787520e72d7b22c0ebf Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 24 Mar 2022 15:19:41 +0100 Subject: [PATCH 012/184] Small function to make formatting into memory of uint easier --- src_features/signMessageEIP712/entrypoint.c | 31 ++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 89cce9d..c70c56b 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -334,6 +334,28 @@ bool set_struct_field(const uint8_t *const data) return true; } +/** + * Format an unsigned number up to 32-bit into memory into an ASCII string. + * + * @param[in] value Value to write in memory + * @param[in] max_chars Maximum number of characters that could be written + * + * @return how many characters have been written in memory + */ +uint8_t format_uint_into_mem(uint32_t value, uint8_t max_chars) +{ + char *ptr; + uint8_t written_chars; + + if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) + { + return 0; + } + written_chars = sprintf(ptr, "%u", value); + mem_dealloc(max_chars - written_chars); // in case it ended up being less + return written_chars; +} + void get_struct_type_string(const uint8_t *const ptr, const uint8_t *const struct_name, uint8_t struct_name_length) @@ -351,7 +373,6 @@ void get_struct_type_string(const uint8_t *const ptr, uint8_t lvls_count; const uint8_t *lvl_ptr; uint8_t array_size; - uint8_t formatted_length; // set length typestr_length = mem_alloc(sizeof(uint16_t)); @@ -394,9 +415,7 @@ void get_struct_type_string(const uint8_t *const ptr, break; } // max value = 256, 3 characters max - formatted_length = sprintf(mem_alloc(sizeof(char) * 3), "%u", field_size); - mem_dealloc(3 - formatted_length); // in case it used less - *typestr_length += formatted_length; + *typestr_length += format_uint_into_mem(field_size, 3); } if (struct_field_is_array(field_ptr)) @@ -412,9 +431,7 @@ void get_struct_type_string(const uint8_t *const ptr, break; case ARRAY_FIXED_SIZE: // max value = 255, 3 characters max - formatted_length = sprintf(mem_alloc(sizeof(char) * 3), "%u", array_size); - mem_dealloc(3 - formatted_length); - *typestr_length += formatted_length; + *typestr_length += format_uint_into_mem(array_size, 3); break; default: // should not be in here :^) From 161919c0120999d6a740a52ebcdbea275f6a3e8d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 24 Mar 2022 15:44:18 +0100 Subject: [PATCH 013/184] Simpler computing the string's length in memory --- src_features/signMessageEIP712/entrypoint.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index c70c56b..c15872d 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -376,14 +376,11 @@ void get_struct_type_string(const uint8_t *const ptr, // set length typestr_length = mem_alloc(sizeof(uint16_t)); - *typestr_length = 0; // add name typestr = memmove(mem_alloc(struct_name_length), struct_name, struct_name_length); - *typestr_length += struct_name_length; *(char*)mem_alloc(sizeof(char)) = '('; - *typestr_length += 1; field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) @@ -391,13 +388,11 @@ void get_struct_type_string(const uint8_t *const ptr, if (idx > 0) { *(char*)mem_alloc(sizeof(char)) = ','; - *typestr_length += 1; } name = get_struct_field_typename(field_ptr, &length); memmove(mem_alloc(sizeof(char) * length), name, length); - *typestr_length += length; if (struct_field_has_typesize(field_ptr)) { @@ -415,7 +410,7 @@ void get_struct_type_string(const uint8_t *const ptr, break; } // max value = 256, 3 characters max - *typestr_length += format_uint_into_mem(field_size, 3); + format_uint_into_mem(field_size, 3); } if (struct_field_is_array(field_ptr)) @@ -438,21 +433,22 @@ void get_struct_type_string(const uint8_t *const ptr, break; } *(char*)mem_alloc(sizeof(char)) = ']'; - *typestr_length += 1; lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); } } *(char*)mem_alloc(sizeof(char)) = ' '; - *typestr_length += 1; name = get_struct_field_keyname(field_ptr, &length); memmove(mem_alloc(sizeof(char) * length), name, length); - *typestr_length += length; field_ptr = get_next_struct_field(field_ptr); } *(char*)mem_alloc(sizeof(char)) = ')'; - *typestr_length += 1; + + // - 2 since : + // * typestr_length is one byte before the start of the string + // * mem_alloc's return will point to one byte after the end of the string + *typestr_length = (mem_alloc(0) - (void*)typestr_length - 2); // FIXME: DBG fwrite(typestr, sizeof(char), *typestr_length, stdout); From e57bc93c6976db400b004d38da1e2a6eeec25dad Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 24 Mar 2022 18:52:45 +0100 Subject: [PATCH 014/184] Fix stack overflow with long APDUs --- src_features/signMessageEIP712/entrypoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index c15872d..835d7e7 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -594,7 +594,7 @@ void init_heap(void) int main(void) { - uint8_t buf[256]; + uint8_t buf[260]; // 4 bytes APDU header + 256 bytes payload uint16_t idx; int state; uint8_t payload_size = 0; From 59490beff941a96d7f1116566b11fef093319846 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 28 Mar 2022 11:44:00 +0200 Subject: [PATCH 015/184] Small type-enforcing refactoring + now properly gets typestrings --- src_features/signMessageEIP712/eip712.h | 3 + src_features/signMessageEIP712/entrypoint.c | 212 +++++++++++++++----- 2 files changed, 162 insertions(+), 53 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index f700af3..60eb7f9 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -41,6 +41,9 @@ typedef enum IDX_COUNT } t_typename_matcher_idx; +#define MIN(a,b) ((a > b) ? b : a) +#define MAX(a,b) ((a > b) ? a : b) + // APDUs INS #define INS_STRUCT_DEF 0x18 #define INS_STRUCT_IMPL 0x1A diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 835d7e7..f65e11f 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -13,15 +13,15 @@ static uint8_t *structs_array; static uint8_t *current_struct_fields_array; // lib functions -const uint8_t *get_array_in_mem(const uint8_t *ptr, uint8_t *const array_size) +const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { - *array_size = *ptr; + *array_size = *(uint8_t*)ptr; return (ptr + 1); } -static inline const uint8_t *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) +static inline const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) { - return get_array_in_mem(ptr, string_length); + return (char*)get_array_in_mem(ptr, string_length); } // ptr must point to the beginning of a struct field @@ -56,7 +56,7 @@ uint8_t get_struct_field_typesize(const uint8_t *ptr) } // ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_custom_typename(const uint8_t *ptr, +const char *get_struct_field_custom_typename(const uint8_t *ptr, uint8_t *const length) { ptr += 1; // skip TypeDesc @@ -64,7 +64,7 @@ const uint8_t *get_struct_field_custom_typename(const uint8_t *ptr, } // ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_sol_typename(const uint8_t *ptr, +const char *get_struct_field_sol_typename(const uint8_t *ptr, uint8_t *const length) { e_type field_type; @@ -89,15 +89,15 @@ const uint8_t *get_struct_field_sol_typename(const uint8_t *ptr, } typename_ptr += 1; } - typename_ptr = get_string_in_mem(typename_ptr, length); - if (typename_found) return typename_ptr; + typename_ptr = (uint8_t*)get_string_in_mem(typename_ptr, length); + if (typename_found) return (char*)typename_ptr; typename_ptr += *length; } return NULL; // Not found } // ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_typename(const uint8_t *ptr, +const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length) { if (struct_field_type(ptr) == TYPE_CUSTOM) @@ -166,7 +166,7 @@ const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, } // ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_keyname(const uint8_t *ptr, +const char *get_struct_field_keyname(const uint8_t *ptr, uint8_t *const length) { const uint8_t *field_ptr; @@ -186,18 +186,18 @@ const uint8_t *get_struct_field_keyname(const uint8_t *ptr, } // ptr must point to the beginning of a struct field -const uint8_t *get_next_struct_field(const uint8_t *ptr) +const uint8_t *get_next_struct_field(const void *ptr) { uint8_t length; - ptr = get_struct_field_keyname(ptr, &length); + ptr = (uint8_t*)get_struct_field_keyname(ptr, &length); return (ptr + length); } // ptr must point to the beginning of a struct -const uint8_t *get_struct_name(const uint8_t *ptr, uint8_t *const length) +const char *get_struct_name(const uint8_t *ptr, uint8_t *const length) { - return get_string_in_mem(ptr, length); + return (char*)get_string_in_mem(ptr, length); } // ptr must point to the beginning of a struct @@ -232,12 +232,12 @@ const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) // Finds struct with a given name const uint8_t *get_structn(const uint8_t *const ptr, - const uint8_t *const name_ptr, - uint8_t name_length) + const char *const name_ptr, + const uint8_t name_length) { uint8_t structs_count; const uint8_t *struct_ptr; - const uint8_t *name; + const char *name; uint8_t length; struct_ptr = get_structs_array(ptr, &structs_count); @@ -256,7 +256,7 @@ const uint8_t *get_structn(const uint8_t *const ptr, static inline const uint8_t *get_struct(const uint8_t *const ptr, const char *const name_ptr) { - return get_structn(ptr, (uint8_t*)name_ptr, strlen(name_ptr)); + return get_structn(ptr, name_ptr, strlen(name_ptr)); } // @@ -342,7 +342,7 @@ bool set_struct_field(const uint8_t *const data) * * @return how many characters have been written in memory */ -uint8_t format_uint_into_mem(uint32_t value, uint8_t max_chars) +uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) { char *ptr; uint8_t written_chars; @@ -356,29 +356,30 @@ uint8_t format_uint_into_mem(uint32_t value, uint8_t max_chars) return written_chars; } -void get_struct_type_string(const uint8_t *const ptr, - const uint8_t *const struct_name, - uint8_t struct_name_length) +/** + * + * + * @param[in] struct_ptr pointer to the structure we want the typestring of + * @param[in] str_length length of the formatted string in memory + * @return pointer of the string in memory, \ref NULL in case of an error + */ +const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *const str_length) { - const uint8_t *const struct_ptr = get_structn(ptr, - struct_name, - struct_name_length); - uint16_t *typestr_length; - char *typestr; + const char *str_start; + const char *struct_name; + uint8_t struct_name_length; const uint8_t *field_ptr; uint8_t fields_count; - const uint8_t *name; + const char *name; uint8_t length; uint16_t field_size; uint8_t lvls_count; const uint8_t *lvl_ptr; uint8_t array_size; - // set length - typestr_length = mem_alloc(sizeof(uint16_t)); - // add name - typestr = memmove(mem_alloc(struct_name_length), struct_name, struct_name_length); + struct_name = get_struct_name(struct_ptr, &struct_name_length); + str_start = memmove(mem_alloc(struct_name_length), struct_name, struct_name_length); *(char*)mem_alloc(sizeof(char)) = '('; @@ -419,14 +420,13 @@ void get_struct_type_string(const uint8_t *const ptr, while (lvls_count-- > 0) { *(char*)mem_alloc(sizeof(char)) = '['; - *typestr_length += 1; switch (struct_field_array_depth(lvl_ptr, &array_size)) { case ARRAY_DYNAMIC: break; case ARRAY_FIXED_SIZE: // max value = 255, 3 characters max - *typestr_length += format_uint_into_mem(array_size, 3); + format_uint_into_mem(array_size, 3); break; default: // should not be in here :^) @@ -445,31 +445,137 @@ void get_struct_type_string(const uint8_t *const ptr, } *(char*)mem_alloc(sizeof(char)) = ')'; - // - 2 since : - // * typestr_length is one byte before the start of the string - // * mem_alloc's return will point to one byte after the end of the string - *typestr_length = (mem_alloc(0) - (void*)typestr_length - 2); - - // FIXME: DBG - fwrite(typestr, sizeof(char), *typestr_length, stdout); - printf("\n"); + *str_length = ((char*)mem_alloc(0) - str_start); + return str_start; } -void get_type_hash(const uint8_t *const ptr, - const uint8_t *const struct_name, - uint8_t struct_name_length) +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] deps_count count of how many struct dependencies pointers + * @param[in,out] dep pointer to the first dependency pointer + */ +void sort_dependencies(const uint8_t *const deps_count, + void **dep) { - void *mem_loc_bak; - uint16_t str_length; + bool changed = false; + void *tmp_ptr; + const char *name1, *name2; + uint8_t namelen1, namelen2; + int str_cmp_result; - // backup the memory location - mem_loc_bak = mem_alloc(0); + for (size_t idx = 0; (idx + 1) < *deps_count; ++idx) + { + name1 = get_struct_name(*(dep + idx), &namelen1); + name2 = get_struct_name(*(dep + idx + 1), &namelen2); + str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); + if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) + { + tmp_ptr = *(dep + idx); + *(dep + idx) = *(dep + idx + 1); + *(dep + idx + 1) = tmp_ptr; + + changed = true; + } + } + // recurse until it is sorted + if (changed) + { + sort_dependencies(deps_count, dep); + } +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[out] deps_count count of how many struct dependencie pointers + * @param[in] dep pointer to the first dependency pointer + * @param[in] struct_ptr pointer to the struct we are getting the dependencies of + */ +void get_struct_dependencies(const void *const structs_array, + uint8_t *const deps_count, + void **dep, + const void *const struct_ptr) +{ + uint8_t fields_count; + const void *field_ptr; + const char *arg_structname; + uint8_t arg_structname_length; + const void *arg_struct_ptr; + size_t dep_idx; + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) + { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + // get struct name + arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); + // from its name, get the pointer to its definition + arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); + + // check if it is not already present in the dependencies array + for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) + { + // it's a match! + if (*(dep + dep_idx) == arg_struct_ptr) + { + break; + } + } + // if it's not present in the array, add it and recurse into it + if (dep_idx == *deps_count) + { + *(const void**)mem_alloc(sizeof(void*)) = arg_struct_ptr; + *deps_count += 1; + get_struct_dependencies(structs_array, deps_count, dep, arg_struct_ptr); + } + } + field_ptr = get_next_struct_field(field_ptr); + } +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] struct_name name of the given struct + * @param[in] struct_name_length length of the name of the given struct + */ +void get_type_hash(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length) +{ + const void *const struct_ptr = get_structn(structs_array, + struct_name, + struct_name_length); + const void *const mem_loc_bak = mem_alloc(0); // backup the memory location + uint8_t *const deps_count = mem_alloc(sizeof(uint8_t)); + void **dep; + uint16_t total_length = 0; + uint16_t length; + const char *typestr; + + *deps_count = 0; // get list of structs (own + dependencies), properly ordered - + dep = (void**)(deps_count + 1); // get first elem + get_struct_dependencies(structs_array, deps_count, dep, struct_ptr); + sort_dependencies(deps_count, dep); + typestr = get_struct_type_string(struct_ptr, &length); + total_length += length; // loop over each struct and generate string - str_length = 0; - get_struct_type_string(ptr, struct_name, struct_name_length); + for (int idx = 0; idx < *deps_count; ++idx) + { + get_struct_type_string(*dep, &length); + total_length += length; + dep += 1; + } + + fwrite(typestr, sizeof(char), total_length, stdout); + printf("\n"); // restore the memory location mem_dealloc(mem_alloc(0) - mem_loc_bak); @@ -497,7 +603,7 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: - get_type_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); + get_type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: break; From 16081c38a9e18a0cf35ed3fff6a46650db7f06e4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 3 May 2022 15:41:58 +0200 Subject: [PATCH 016/184] Added some memory related debug features --- src_features/signMessageEIP712/entrypoint.c | 4 +++- src_features/signMessageEIP712/mem.c | 23 ++++++++++++++++++++- src_features/signMessageEIP712/mem.h | 6 ++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index f65e11f..ccd2b42 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -739,6 +739,8 @@ int main(void) return EXIT_FAILURE; } } - //printf("\n%d bytes used in RAM\n", (mem_idx + 1)); +#ifdef DEBUG + printf("\n%lu bytes used in RAM\n", (mem_max + 1)); +#endif return EXIT_SUCCESS; } diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index 0677783..17b98b2 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -1,8 +1,18 @@ #include +#ifdef DEBUG +#include +#endif #include "mem.h" + +#define SIZE_MEM_BUFFER 1024 + static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; +#ifdef DEBUG +size_t mem_max; +#endif + /** * Initializes the memory buffer index @@ -10,6 +20,9 @@ static size_t mem_idx; void mem_init(void) { mem_idx = 0; +#ifdef DEBUG + mem_max = 0; +#endif } /** @@ -26,16 +39,24 @@ void mem_reset(void) * the current location in the memory buffer and moves the index accordingly. * * @param[in] size Requested allocation size in bytes - * * @return Allocated memory pointer; \ref NULL if not enough space left. */ void *mem_alloc(size_t size) { if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded { +#ifdef DEBUG + printf("Memory exhausted!\n"); +#endif return NULL; } mem_idx += size; +#ifdef DEBUG + if (mem_idx > mem_max) + { + mem_max = mem_idx; + } +#endif return &mem_buffer[mem_idx - size]; } diff --git a/src_features/signMessageEIP712/mem.h b/src_features/signMessageEIP712/mem.h index 2a7b179..d91f57f 100644 --- a/src_features/signMessageEIP712/mem.h +++ b/src_features/signMessageEIP712/mem.h @@ -3,11 +3,13 @@ #include -#define SIZE_MEM_BUFFER 1024 - void mem_init(void); void mem_reset(void); void *mem_alloc(size_t size); void mem_dealloc(size_t size); +#ifdef DEBUG +extern size_t mem_max; +#endif + #endif // MEM_H_ From e3dfd787b5afaac84404b6e1c1e3e51c8c9bf18c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 28 Mar 2022 14:31:02 +0200 Subject: [PATCH 017/184] Memory allocation checking --- src_features/signMessageEIP712/eip712.h | 2 - src_features/signMessageEIP712/entrypoint.c | 239 +++++++++++++++----- 2 files changed, 183 insertions(+), 58 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 60eb7f9..52ca784 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -86,6 +86,4 @@ typedef struct t_array array_levels; } t_struct_field; -typedef void (*field_callback)(t_struct_field *field, uint8_t index); - #endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index ccd2b42..f8f395d 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -262,17 +262,31 @@ static inline const uint8_t *get_struct(const uint8_t *const ptr, bool set_struct_name(const uint8_t *const data) { + uint8_t *length_ptr; + char *name_ptr; + // increment number of structs *structs_array += 1; // copy length - *(uint8_t*)mem_alloc(1) = data[OFFSET_LC]; + if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *length_ptr = data[OFFSET_LC]; // copy name - memmove(mem_alloc(data[OFFSET_LC]), &data[OFFSET_DATA], data[OFFSET_LC]); + if ((name_ptr = mem_alloc(sizeof(char) * data[OFFSET_LC])) == NULL) + { + return false; + } + memmove(name_ptr, &data[OFFSET_DATA], data[OFFSET_LC]); // initialize number of fields - current_struct_fields_array = mem_alloc(1); + if ((current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } *current_struct_fields_array = 0; return true; } @@ -280,43 +294,77 @@ bool set_struct_name(const uint8_t *const data) bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_DATA; - uint8_t type_desc, len; + uint8_t *type_desc_ptr; + uint8_t *type_size_ptr; + uint8_t *typename_len_ptr; + char *typename; + uint8_t *array_levels_count; + e_array_type *array_level; + uint8_t *array_level_size; + uint8_t *fieldname_len_ptr; + char *fieldname_ptr; // increment number of struct fields *current_struct_fields_array += 1; // copy TypeDesc - type_desc = data[data_idx++]; - *(uint8_t*)mem_alloc(1) = type_desc; + if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *type_desc_ptr = data[data_idx++]; // check TypeSize flag in TypeDesc - if (type_desc & TYPESIZE_MASK) + if (*type_desc_ptr & TYPESIZE_MASK) { // copy TypeSize - *(uint8_t*)mem_alloc(1) = data[data_idx++]; - } - else if ((type_desc & TYPE_MASK) == TYPE_CUSTOM) - { - len = data[data_idx++]; - // copy custom struct name length - *(uint8_t*)mem_alloc(1) = len; - // copy name - memmove(mem_alloc(len), &data[data_idx], len); - data_idx += len; - } - if (type_desc & ARRAY_MASK) - { - len = data[data_idx++]; - *(uint8_t*)mem_alloc(1) = len; - while (len-- > 0) + if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { - *(uint8_t*)mem_alloc(1) = data[data_idx]; - switch (data[data_idx++]) + return false; + } + *type_size_ptr = data[data_idx++]; + } + else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) + { + // copy custom struct name length + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *typename_len_ptr = data[data_idx++]; + + // copy name + if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) + { + return false; + } + memmove(typename, &data[data_idx], *typename_len_ptr); + data_idx += *typename_len_ptr; + } + if (*type_desc_ptr & ARRAY_MASK) + { + if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *array_levels_count = data[data_idx++]; + for (int idx = 0; idx < *array_levels_count; ++idx) + { + if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *array_level = data[data_idx++]; + switch (*array_level) { case ARRAY_DYNAMIC: // nothing to do break; case ARRAY_FIXED_SIZE: - *(uint8_t*)mem_alloc(1) = data[data_idx++]; + if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *array_level_size = data[data_idx++]; break; default: // should not be in here :^) @@ -326,11 +374,18 @@ bool set_struct_field(const uint8_t *const data) } // copy length - len = data[data_idx++]; - *(uint8_t*)mem_alloc(1) = len; + if ((fieldname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *fieldname_len_ptr = data[data_idx++]; // copy name - memmove(mem_alloc(len), &data[data_idx], len); + if ((fieldname_ptr = mem_alloc(sizeof(char) * *fieldname_len_ptr)) == NULL) + { + return false; + } + memmove(fieldname_ptr, &data[data_idx], *fieldname_len_ptr); return true; } @@ -340,7 +395,7 @@ bool set_struct_field(const uint8_t *const data) * @param[in] value Value to write in memory * @param[in] max_chars Maximum number of characters that could be written * - * @return how many characters have been written in memory + * @return how many characters have been written in memory, 0 in case of an allocation error */ uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) { @@ -376,24 +431,42 @@ const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *co uint8_t lvls_count; const uint8_t *lvl_ptr; uint8_t array_size; + char *char_ptr; + char *name_ptr; // add name struct_name = get_struct_name(struct_ptr, &struct_name_length); - str_start = memmove(mem_alloc(struct_name_length), struct_name, struct_name_length); + if ((name_ptr = mem_alloc(sizeof(char) * struct_name_length)) == NULL) + { + return NULL; + } + str_start = memmove(name_ptr, struct_name, struct_name_length); - *(char*)mem_alloc(sizeof(char)) = '('; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = '('; field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) { if (idx > 0) { - *(char*)mem_alloc(sizeof(char)) = ','; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ','; } name = get_struct_field_typename(field_ptr, &length); - memmove(mem_alloc(sizeof(char) * length), name, length); + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + { + return NULL; + } + memmove(name_ptr, name, length); if (struct_field_has_typesize(field_ptr)) { @@ -419,7 +492,11 @@ const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *co lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); while (lvls_count-- > 0) { - *(char*)mem_alloc(sizeof(char)) = '['; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = '['; switch (struct_field_array_depth(lvl_ptr, &array_size)) { case ARRAY_DYNAMIC: @@ -432,18 +509,34 @@ const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *co // should not be in here :^) break; } - *(char*)mem_alloc(sizeof(char)) = ']'; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ']'; lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); } } - *(char*)mem_alloc(sizeof(char)) = ' '; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ' '; name = get_struct_field_keyname(field_ptr, &length); - memmove(mem_alloc(sizeof(char) * length), name, length); + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + { + return NULL; + } + memmove(name_ptr, name, length); field_ptr = get_next_struct_field(field_ptr); } - *(char*)mem_alloc(sizeof(char)) = ')'; + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ')'; *str_length = ((char*)mem_alloc(0) - str_start); return str_start; @@ -494,8 +587,9 @@ void sort_dependencies(const uint8_t *const deps_count, * @param[out] deps_count count of how many struct dependencie pointers * @param[in] dep pointer to the first dependency pointer * @param[in] struct_ptr pointer to the struct we are getting the dependencies of + * @return \ref false in case of a memory allocation error, \ref true otherwise */ -void get_struct_dependencies(const void *const structs_array, +bool get_struct_dependencies(const void *const structs_array, uint8_t *const deps_count, void **dep, const void *const struct_ptr) @@ -506,6 +600,7 @@ void get_struct_dependencies(const void *const structs_array, uint8_t arg_structname_length; const void *arg_struct_ptr; size_t dep_idx; + const void **new_dep; field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) @@ -529,13 +624,18 @@ void get_struct_dependencies(const void *const structs_array, // if it's not present in the array, add it and recurse into it if (dep_idx == *deps_count) { - *(const void**)mem_alloc(sizeof(void*)) = arg_struct_ptr; + if ((new_dep = mem_alloc(sizeof(void*))) == NULL) + { + return false; + } + *new_dep = arg_struct_ptr; *deps_count += 1; get_struct_dependencies(structs_array, deps_count, dep, arg_struct_ptr); } } field_ptr = get_next_struct_field(field_ptr); } + return true; } /** @@ -544,8 +644,9 @@ void get_struct_dependencies(const void *const structs_array, * @param[in] structs_array pointer to structs array * @param[in] struct_name name of the given struct * @param[in] struct_name_length length of the name of the given struct + * @return \ref false in case of a memory allocation error, \ref true otherwise */ -void get_type_hash(const void *const structs_array, +bool get_type_hash(const void *const structs_array, const char *const struct_name, const uint8_t struct_name_length) { @@ -553,16 +654,23 @@ void get_type_hash(const void *const structs_array, struct_name, struct_name_length); const void *const mem_loc_bak = mem_alloc(0); // backup the memory location - uint8_t *const deps_count = mem_alloc(sizeof(uint8_t)); + uint8_t *deps_count; void **dep; uint16_t total_length = 0; uint16_t length; const char *typestr; + if ((deps_count = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } *deps_count = 0; // get list of structs (own + dependencies), properly ordered dep = (void**)(deps_count + 1); // get first elem - get_struct_dependencies(structs_array, deps_count, dep, struct_ptr); + if (get_struct_dependencies(structs_array, deps_count, dep, struct_ptr) == false) + { + return false; + } sort_dependencies(deps_count, dep); typestr = get_struct_type_string(struct_ptr, &length); total_length += length; @@ -579,6 +687,7 @@ void get_type_hash(const void *const structs_array, // restore the memory location mem_dealloc(mem_alloc(0) - mem_loc_bak); + return true; } bool handle_apdu(const uint8_t *const data) @@ -643,9 +752,13 @@ bool init_typenames(void) { TYPE_SOL_BYTES_DYN, 6 } }; uint8_t *previous_match; - uint8_t typename_len; + uint8_t *typename_len_ptr; + char *typename_ptr; - typenames_array = mem_alloc(1); + if ((typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } *typenames_array = 0; // loop over typenames for (size_t s_idx = 0; @@ -664,19 +777,30 @@ bool init_typenames(void) { *previous_match |= TYPENAME_MORE_TYPE; } - previous_match = mem_alloc(1); + if ((previous_match = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } *previous_match = enum_to_idx[e_idx][IDX_ENUM]; } } if (previous_match) // if at least one match was found { - typename_len = strlen(typenames_str[s_idx]); - *(uint8_t*)mem_alloc(1) = typename_len; + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + // get pointer to the allocated space just above + *typename_len_ptr = strlen(typenames_str[s_idx]); + + + if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) + { + return false; + } // copy typename - memcpy(mem_alloc(typename_len), - typenames_str[s_idx], - typename_len); + memcpy(typename_ptr, typenames_str[s_idx], *typename_len_ptr); } // increment array size *typenames_array += 1; @@ -684,18 +808,21 @@ bool init_typenames(void) return true; } -void init_heap(void) +bool init_eip712_context(void) { // init global variables init_mem(); - init_typenames(); + if (init_typenames() == false) + return false; // set types pointer - structs_array = mem_alloc(1); + if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + return false; // create len(types) *structs_array = 0; + return true; } int main(void) @@ -705,7 +832,7 @@ int main(void) int state; uint8_t payload_size = 0; - init_heap(); + init_eip712_context(); state = OFFSET_CLA; idx = 0; From 5889da054e2188b4eb737396ea90a5d46cc37ab2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:28:35 +0200 Subject: [PATCH 018/184] Extracted the encodeType and typeHash functions from the main file --- src_features/signMessageEIP712/eip712.h | 30 ++ src_features/signMessageEIP712/encode_type.c | 304 ++++++++++++++++++ src_features/signMessageEIP712/encode_type.h | 11 + src_features/signMessageEIP712/entrypoint.c | 313 +------------------ src_features/signMessageEIP712/type_hash.c | 31 ++ src_features/signMessageEIP712/type_hash.h | 10 + 6 files changed, 393 insertions(+), 306 deletions(-) create mode 100644 src_features/signMessageEIP712/encode_type.c create mode 100644 src_features/signMessageEIP712/encode_type.h create mode 100644 src_features/signMessageEIP712/type_hash.c create mode 100644 src_features/signMessageEIP712/type_hash.h diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 52ca784..e7e1cae 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -1,6 +1,7 @@ #ifndef EIP712_H_ #define EIP712_H_ +#include #include enum { @@ -66,6 +67,8 @@ typedef enum // Solidity typenames array mask #define TYPENAME_MORE_TYPE (1 << 7) // For custom typename +#define KECCAK256_HASH_LENGTH 32 + typedef struct { uint16_t length; @@ -86,4 +89,31 @@ typedef struct t_array array_levels; } t_struct_field; + + +// TODO: Move these into a new file +const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); +const uint8_t *get_struct_fields_array(const uint8_t *ptr, + uint8_t *const length); +const char *get_struct_field_typename(const uint8_t *ptr, + uint8_t *const length); +bool struct_field_has_typesize(const uint8_t *ptr); +uint8_t get_struct_field_typesize(const uint8_t *ptr); +bool struct_field_is_array(const uint8_t *ptr); +e_type struct_field_type(const uint8_t *ptr); +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, + uint8_t *const length); +e_array_type struct_field_array_depth(const uint8_t *ptr, + uint8_t *const array_size); +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); +const char *get_struct_field_typename(const uint8_t *ptr, + uint8_t *const length); +const char *get_struct_field_keyname(const uint8_t *ptr, + uint8_t *const length); +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); +const uint8_t *get_next_struct_field(const void *ptr); +const uint8_t *get_structn(const uint8_t *const ptr, + const char *const name_ptr, + const uint8_t name_length); + #endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c new file mode 100644 index 0000000..057cc91 --- /dev/null +++ b/src_features/signMessageEIP712/encode_type.c @@ -0,0 +1,304 @@ +#include +#include +#include +#include +#include "mem.h" +#include "eip712.h" +#include "encode_type.h" + +/** + * Format an unsigned number up to 32-bit into memory into an ASCII string. + * + * @param[in] value Value to write in memory + * @param[in] max_chars Maximum number of characters that could be written + * + * @return how many characters have been written in memory, 0 in case of an allocation error + */ +static uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) +{ + char *ptr; + uint8_t written_chars; + + if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) + { + return 0; + } + written_chars = sprintf(ptr, "%u", value); + mem_dealloc(max_chars - written_chars); // in case it ended up being less + return written_chars; +} + +/** + * + * + * @param[in] struct_ptr pointer to the structure we want the typestring of + * @param[in] str_length length of the formatted string in memory + * @return pointer of the string in memory, \ref NULL in case of an error + */ +static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *const str_length) +{ + const char *str_start; + const char *struct_name; + uint8_t struct_name_length; + const uint8_t *field_ptr; + uint8_t fields_count; + const char *name; + uint8_t length; + uint16_t field_size; + uint8_t lvls_count; + const uint8_t *lvl_ptr; + uint8_t array_size; + char *char_ptr; + char *name_ptr; + + // add name + struct_name = get_struct_name(struct_ptr, &struct_name_length); + if ((name_ptr = mem_alloc(sizeof(char) * struct_name_length)) == NULL) + { + return NULL; + } + str_start = memmove(name_ptr, struct_name, struct_name_length); + + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = '('; + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) + { + if (idx > 0) + { + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ','; + } + + name = get_struct_field_typename(field_ptr, &length); + + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + { + return NULL; + } + memmove(name_ptr, name, length); + + if (struct_field_has_typesize(field_ptr)) + { + field_size = get_struct_field_typesize(field_ptr); + switch (struct_field_type(field_ptr)) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + field_size *= 8; // bytes -> bits + break; + case TYPE_SOL_BYTES_FIX: + break; + default: + // should not be in here :^) + break; + } + // max value = 256, 3 characters max + format_uint_into_mem(field_size, 3); + } + + if (struct_field_is_array(field_ptr)) + { + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + while (lvls_count-- > 0) + { + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = '['; + switch (struct_field_array_depth(lvl_ptr, &array_size)) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + // max value = 255, 3 characters max + format_uint_into_mem(array_size, 3); + break; + default: + // should not be in here :^) + break; + } + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ']'; + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); + } + } + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ' '; + name = get_struct_field_keyname(field_ptr, &length); + + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + { + return NULL; + } + memmove(name_ptr, name, length); + + field_ptr = get_next_struct_field(field_ptr); + } + if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + { + return NULL; + } + *char_ptr = ')'; + + *str_length = ((char*)mem_alloc(0) - str_start); + return str_start; +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] deps_count count of how many struct dependencies pointers + * @param[in,out] dep pointer to the first dependency pointer + */ +static void sort_dependencies(const uint8_t *const deps_count, + void **dep) +{ + bool changed = false; + void *tmp_ptr; + const char *name1, *name2; + uint8_t namelen1, namelen2; + int str_cmp_result; + + for (size_t idx = 0; (idx + 1) < *deps_count; ++idx) + { + name1 = get_struct_name(*(dep + idx), &namelen1); + name2 = get_struct_name(*(dep + idx + 1), &namelen2); + + str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); + if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) + { + tmp_ptr = *(dep + idx); + *(dep + idx) = *(dep + idx + 1); + *(dep + idx + 1) = tmp_ptr; + + changed = true; + } + } + // recurse until it is sorted + if (changed) + { + sort_dependencies(deps_count, dep); + } +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[out] deps_count count of how many struct dependencie pointers + * @param[in] dep pointer to the first dependency pointer + * @param[in] struct_ptr pointer to the struct we are getting the dependencies of + * @return \ref false in case of a memory allocation error, \ref true otherwise + */ +static bool get_struct_dependencies(const void *const structs_array, + uint8_t *const deps_count, + void **dep, + const void *const struct_ptr) +{ + uint8_t fields_count; + const void *field_ptr; + const char *arg_structname; + uint8_t arg_structname_length; + const void *arg_struct_ptr; + size_t dep_idx; + const void **new_dep; + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) + { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + // get struct name + arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); + // from its name, get the pointer to its definition + arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); + + // check if it is not already present in the dependencies array + for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) + { + // it's a match! + if (*(dep + dep_idx) == arg_struct_ptr) + { + break; + } + } + // if it's not present in the array, add it and recurse into it + if (dep_idx == *deps_count) + { + if ((new_dep = mem_alloc(sizeof(void*))) == NULL) + { + return false; + } + *new_dep = arg_struct_ptr; + *deps_count += 1; + get_struct_dependencies(structs_array, deps_count, dep, arg_struct_ptr); + } + } + field_ptr = get_next_struct_field(field_ptr); + } + return true; +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] struct_name name of the given struct + * @param[in] struct_name_length length of the name of the given struct + * @param[out] encoded_length length of the returned string + * @return pointer to encoded string or \ref NULL in case of a memory allocation error + */ +const char *encode_type(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length, + uint16_t *const encoded_length) +{ + const void *const struct_ptr = get_structn(structs_array, + struct_name, + struct_name_length); + uint8_t *deps_count; + void **dep; + uint16_t length; + const char *typestr; + + *encoded_length = 0; + if ((deps_count = mem_alloc(sizeof(uint8_t))) == NULL) + { + return NULL;//false; + } + *deps_count = 0; + // get list of structs (own + dependencies), properly ordered + dep = (void**)(deps_count + 1); // get first elem + if (get_struct_dependencies(structs_array, deps_count, dep, struct_ptr) == false) + { + return NULL;//false; + } + sort_dependencies(deps_count, dep); + typestr = get_struct_type_string(struct_ptr, &length); + *encoded_length += length; + // loop over each struct and generate string + for (int idx = 0; idx < *deps_count; ++idx) + { + get_struct_type_string(*dep, &length); + *encoded_length += length; + dep += 1; + } + + return typestr;//true; +} diff --git a/src_features/signMessageEIP712/encode_type.h b/src_features/signMessageEIP712/encode_type.h new file mode 100644 index 0000000..f7ed216 --- /dev/null +++ b/src_features/signMessageEIP712/encode_type.h @@ -0,0 +1,11 @@ +#ifndef ENCODE_TYPE_H_ +#define ENCODE_TYPE_H_ + +#include + +const char *encode_type(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length, + uint16_t *const encoded_length); + +#endif // ENCODE_TYPE_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index f8f395d..baf9421 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -6,6 +6,7 @@ #include "eip712.h" #include "mem.h" +#include "type_hash.h" static uint8_t *typenames_array; @@ -31,19 +32,19 @@ static inline uint8_t get_struct_field_typedesc(const uint8_t *ptr) } // ptr must point to the beginning of a struct field -static inline bool struct_field_is_array(const uint8_t *ptr) +bool struct_field_is_array(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & ARRAY_MASK); } // ptr must point to the beginning of a struct field -static inline bool struct_field_has_typesize(const uint8_t *ptr) +bool struct_field_has_typesize(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); } // ptr must point to the beginning of a struct field -static inline e_type struct_field_type(const uint8_t *ptr) +e_type struct_field_type(const uint8_t *ptr) { return (get_struct_field_typedesc(ptr) & TYPE_MASK); } @@ -98,7 +99,7 @@ const char *get_struct_field_sol_typename(const uint8_t *ptr, // ptr must point to the beginning of a struct field const char *get_struct_field_typename(const uint8_t *ptr, - uint8_t *const length) + uint8_t *const length) { if (struct_field_type(ptr) == TYPE_CUSTOM) { @@ -167,7 +168,7 @@ const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, // ptr must point to the beginning of a struct field const char *get_struct_field_keyname(const uint8_t *ptr, - uint8_t *const length) + uint8_t *const length) { const uint8_t *field_ptr; uint8_t size; @@ -389,306 +390,6 @@ bool set_struct_field(const uint8_t *const data) return true; } -/** - * Format an unsigned number up to 32-bit into memory into an ASCII string. - * - * @param[in] value Value to write in memory - * @param[in] max_chars Maximum number of characters that could be written - * - * @return how many characters have been written in memory, 0 in case of an allocation error - */ -uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) -{ - char *ptr; - uint8_t written_chars; - - if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) - { - return 0; - } - written_chars = sprintf(ptr, "%u", value); - mem_dealloc(max_chars - written_chars); // in case it ended up being less - return written_chars; -} - -/** - * - * - * @param[in] struct_ptr pointer to the structure we want the typestring of - * @param[in] str_length length of the formatted string in memory - * @return pointer of the string in memory, \ref NULL in case of an error - */ -const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *const str_length) -{ - const char *str_start; - const char *struct_name; - uint8_t struct_name_length; - const uint8_t *field_ptr; - uint8_t fields_count; - const char *name; - uint8_t length; - uint16_t field_size; - uint8_t lvls_count; - const uint8_t *lvl_ptr; - uint8_t array_size; - char *char_ptr; - char *name_ptr; - - // add name - struct_name = get_struct_name(struct_ptr, &struct_name_length); - if ((name_ptr = mem_alloc(sizeof(char) * struct_name_length)) == NULL) - { - return NULL; - } - str_start = memmove(name_ptr, struct_name, struct_name_length); - - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = '('; - - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { - if (idx > 0) - { - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = ','; - } - - name = get_struct_field_typename(field_ptr, &length); - - if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) - { - return NULL; - } - memmove(name_ptr, name, length); - - if (struct_field_has_typesize(field_ptr)) - { - field_size = get_struct_field_typesize(field_ptr); - switch (struct_field_type(field_ptr)) - { - case TYPE_SOL_INT: - case TYPE_SOL_UINT: - field_size *= 8; // bytes -> bits - break; - case TYPE_SOL_BYTES_FIX: - break; - default: - // should not be in here :^) - break; - } - // max value = 256, 3 characters max - format_uint_into_mem(field_size, 3); - } - - if (struct_field_is_array(field_ptr)) - { - lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - while (lvls_count-- > 0) - { - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = '['; - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - // max value = 255, 3 characters max - format_uint_into_mem(array_size, 3); - break; - default: - // should not be in here :^) - break; - } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = ']'; - lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); - } - } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = ' '; - name = get_struct_field_keyname(field_ptr, &length); - - if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) - { - return NULL; - } - memmove(name_ptr, name, length); - - field_ptr = get_next_struct_field(field_ptr); - } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) - { - return NULL; - } - *char_ptr = ')'; - - *str_length = ((char*)mem_alloc(0) - str_start); - return str_start; -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[in] deps_count count of how many struct dependencies pointers - * @param[in,out] dep pointer to the first dependency pointer - */ -void sort_dependencies(const uint8_t *const deps_count, - void **dep) -{ - bool changed = false; - void *tmp_ptr; - const char *name1, *name2; - uint8_t namelen1, namelen2; - int str_cmp_result; - - for (size_t idx = 0; (idx + 1) < *deps_count; ++idx) - { - name1 = get_struct_name(*(dep + idx), &namelen1); - name2 = get_struct_name(*(dep + idx + 1), &namelen2); - - str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); - if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) - { - tmp_ptr = *(dep + idx); - *(dep + idx) = *(dep + idx + 1); - *(dep + idx + 1) = tmp_ptr; - - changed = true; - } - } - // recurse until it is sorted - if (changed) - { - sort_dependencies(deps_count, dep); - } -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[out] deps_count count of how many struct dependencie pointers - * @param[in] dep pointer to the first dependency pointer - * @param[in] struct_ptr pointer to the struct we are getting the dependencies of - * @return \ref false in case of a memory allocation error, \ref true otherwise - */ -bool get_struct_dependencies(const void *const structs_array, - uint8_t *const deps_count, - void **dep, - const void *const struct_ptr) -{ - uint8_t fields_count; - const void *field_ptr; - const char *arg_structname; - uint8_t arg_structname_length; - const void *arg_struct_ptr; - size_t dep_idx; - const void **new_dep; - - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { - // get struct name - arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); - // from its name, get the pointer to its definition - arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); - - // check if it is not already present in the dependencies array - for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) - { - // it's a match! - if (*(dep + dep_idx) == arg_struct_ptr) - { - break; - } - } - // if it's not present in the array, add it and recurse into it - if (dep_idx == *deps_count) - { - if ((new_dep = mem_alloc(sizeof(void*))) == NULL) - { - return false; - } - *new_dep = arg_struct_ptr; - *deps_count += 1; - get_struct_dependencies(structs_array, deps_count, dep, arg_struct_ptr); - } - } - field_ptr = get_next_struct_field(field_ptr); - } - return true; -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[in] struct_name name of the given struct - * @param[in] struct_name_length length of the name of the given struct - * @return \ref false in case of a memory allocation error, \ref true otherwise - */ -bool get_type_hash(const void *const structs_array, - const char *const struct_name, - const uint8_t struct_name_length) -{ - const void *const struct_ptr = get_structn(structs_array, - struct_name, - struct_name_length); - const void *const mem_loc_bak = mem_alloc(0); // backup the memory location - uint8_t *deps_count; - void **dep; - uint16_t total_length = 0; - uint16_t length; - const char *typestr; - - if ((deps_count = mem_alloc(sizeof(uint8_t))) == NULL) - { - return false; - } - *deps_count = 0; - // get list of structs (own + dependencies), properly ordered - dep = (void**)(deps_count + 1); // get first elem - if (get_struct_dependencies(structs_array, deps_count, dep, struct_ptr) == false) - { - return false; - } - sort_dependencies(deps_count, dep); - typestr = get_struct_type_string(struct_ptr, &length); - total_length += length; - // loop over each struct and generate string - for (int idx = 0; idx < *deps_count; ++idx) - { - get_struct_type_string(*dep, &length); - total_length += length; - dep += 1; - } - - fwrite(typestr, sizeof(char), total_length, stdout); - printf("\n"); - - // restore the memory location - mem_dealloc(mem_alloc(0) - mem_loc_bak); - return true; -} bool handle_apdu(const uint8_t *const data) { @@ -712,7 +413,7 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: - get_type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); + type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: break; diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c new file mode 100644 index 0000000..e3069ce --- /dev/null +++ b/src_features/signMessageEIP712/type_hash.c @@ -0,0 +1,31 @@ +#include +#include +#include "eip712.h" +#include "mem.h" +#include "encode_type.h" +#include "type_hash.h" + +const uint8_t *type_hash(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length) +{ + const void *const mem_loc_bak = mem_alloc(0); // backup the memory location + const char *typestr; + uint16_t length; + + typestr = encode_type(structs_array, struct_name, struct_name_length, &length); + if (typestr == NULL) + { + return NULL; + } + +#ifdef DEBUG + fwrite(typestr, sizeof(char), length, stdout); + printf("\n"); +#endif + + // restore the memory location + mem_dealloc(mem_alloc(0) - mem_loc_bak); + + return NULL; +} diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h new file mode 100644 index 0000000..e40ba7a --- /dev/null +++ b/src_features/signMessageEIP712/type_hash.h @@ -0,0 +1,10 @@ +#ifndef TYPE_HASH_H_ +#define TYPE_HASH_H_ + +#include + +const uint8_t *type_hash(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length); + +#endif // TYPE_HASH_H_ From b2fe2f26ba2d8a2800bcaa3de979aa04bc43cb4a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:32:45 +0200 Subject: [PATCH 019/184] Implements the hashing part of typeHash --- src_features/signMessageEIP712/type_hash.c | 28 +++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index e3069ce..ee471fc 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -4,6 +4,7 @@ #include "mem.h" #include "encode_type.h" #include "type_hash.h" +#include "sha3.h" const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, @@ -12,12 +13,19 @@ const uint8_t *type_hash(const void *const structs_array, const void *const mem_loc_bak = mem_alloc(0); // backup the memory location const char *typestr; uint16_t length; + sha3_context ctx; + const uint8_t *hash; + uint8_t *hash_ptr; typestr = encode_type(structs_array, struct_name, struct_name_length, &length); if (typestr == NULL) { return NULL; } + sha3_Init256(&ctx); + sha3_SetFlags(&ctx, SHA3_FLAGS_KECCAK); + sha3_Update(&ctx, typestr, length); + hash = sha3_Finalize(&ctx); #ifdef DEBUG fwrite(typestr, sizeof(char), length, stdout); @@ -27,5 +35,23 @@ const uint8_t *type_hash(const void *const structs_array, // restore the memory location mem_dealloc(mem_alloc(0) - mem_loc_bak); - return NULL; + // copy hash into memory + if ((hash_ptr = mem_alloc(KECCAK256_HASH_LENGTH)) == NULL) + { + return NULL; + } +#ifdef DEBUG + printf("-> 0x"); +#endif + for (int idx = 0; idx < KECCAK256_HASH_LENGTH; ++idx) + { + hash_ptr[idx] = hash[idx]; +#ifdef DEBUG + printf("%.02x", hash[idx]); +#endif + } +#ifdef DEBUG + printf("\n"); +#endif + return hash_ptr; } From 54fe26a30174a1ac6626a66be169b6a83ec922ec Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:28:50 +0200 Subject: [PATCH 020/184] Extracted the context initialization from the main source file --- src_features/signMessageEIP712/context.c | 31 ++++ src_features/signMessageEIP712/context.h | 13 ++ src_features/signMessageEIP712/eip712.h | 13 +- src_features/signMessageEIP712/entrypoint.c | 136 +----------------- .../signMessageEIP712/sol_typenames.c | 132 +++++++++++++++++ .../signMessageEIP712/sol_typenames.h | 12 ++ 6 files changed, 193 insertions(+), 144 deletions(-) create mode 100644 src_features/signMessageEIP712/context.c create mode 100644 src_features/signMessageEIP712/context.h create mode 100644 src_features/signMessageEIP712/sol_typenames.c create mode 100644 src_features/signMessageEIP712/sol_typenames.h diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c new file mode 100644 index 0000000..6db0e0d --- /dev/null +++ b/src_features/signMessageEIP712/context.c @@ -0,0 +1,31 @@ +#include +#include +#include "context.h" +#include "eip712.h" +#include "mem.h" +#include "sol_typenames.h" + +uint8_t *typenames_array; +uint8_t *structs_array; +uint8_t *current_struct_fields_array; + +/** + * + * @return a boolean indicating if the initialization was successful or not + */ +bool init_eip712_context(void) +{ + // init global variables + mem_init(); + + if (init_sol_typenames() == false) + return false; + + // set types pointer + if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + return false; + + // create len(types) + *structs_array = 0; + return true; +} diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h new file mode 100644 index 0000000..7c4520b --- /dev/null +++ b/src_features/signMessageEIP712/context.h @@ -0,0 +1,13 @@ +#ifndef EIP712_CTX_H_ +#define EIP712_CTX_H_ + +#include + + +extern uint8_t *typenames_array; +extern uint8_t *structs_array; +extern uint8_t *current_struct_fields_array; + +bool init_eip712_context(void); + +#endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index e7e1cae..6d51647 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -35,13 +35,6 @@ typedef enum ARRAY_FIXED_SIZE } e_array_type; -typedef enum -{ - IDX_ENUM = 0, - IDX_STR_IDX, - IDX_COUNT -} t_typename_matcher_idx; - #define MIN(a,b) ((a > b) ? b : a) #define MAX(a,b) ((a > b) ? a : b) @@ -64,9 +57,6 @@ typedef enum #define TYPESIZE_MASK (1 << 6) #define TYPENAME_ENUM (0xF) -// Solidity typenames array mask -#define TYPENAME_MORE_TYPE (1 << 7) // For custom typename - #define KECCAK256_HASH_LENGTH 32 typedef struct @@ -90,7 +80,6 @@ typedef struct } t_struct_field; - // TODO: Move these into a new file const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_struct_fields_array(const uint8_t *ptr, @@ -115,5 +104,7 @@ const uint8_t *get_next_struct_field(const void *ptr); const uint8_t *get_structn(const uint8_t *const ptr, const char *const name_ptr, const uint8_t name_length); +const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); +const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); #endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index baf9421..9e85e40 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -7,12 +7,10 @@ #include "eip712.h" #include "mem.h" #include "type_hash.h" +#include "context.h" +#include "sol_typenames.h" -static uint8_t *typenames_array; -static uint8_t *structs_array; -static uint8_t *current_struct_fields_array; - // lib functions const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { @@ -20,7 +18,7 @@ const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) return (ptr + 1); } -static inline const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) +const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) { return (char*)get_array_in_mem(ptr, string_length); } @@ -64,39 +62,6 @@ const char *get_struct_field_custom_typename(const uint8_t *ptr, return get_string_in_mem(ptr, length); } -// ptr must point to the beginning of a struct field -const char *get_struct_field_sol_typename(const uint8_t *ptr, - uint8_t *const length) -{ - e_type field_type; - const uint8_t *typename_ptr; - uint8_t typenames_count; - bool more_type; - bool typename_found; - - field_type = struct_field_type(ptr); - typename_ptr = get_array_in_mem(typenames_array, &typenames_count); - typename_found = false; - while (typenames_count-- > 0) - { - more_type = true; - while (more_type) - { - more_type = *typename_ptr & TYPENAME_MORE_TYPE; - e_type type_enum = *typename_ptr & TYPENAME_ENUM; - if (type_enum == field_type) - { - typename_found = true; - } - typename_ptr += 1; - } - typename_ptr = (uint8_t*)get_string_in_mem(typename_ptr, length); - if (typename_found) return (char*)typename_ptr; - typename_ptr += *length; - } - return NULL; // Not found -} - // ptr must point to the beginning of a struct field const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length) @@ -431,101 +396,6 @@ bool handle_apdu(const uint8_t *const data) return true; } -bool init_typenames(void) -{ - const char *const typenames_str[] = { - "int", - "uint", - "address", - "bool", - "string", - "byte", - "bytes" - }; - const int enum_to_idx[][IDX_COUNT] = { - { TYPE_SOL_INT, 0 }, - { TYPE_SOL_UINT, 1}, - { TYPE_SOL_ADDRESS, 2 }, - { TYPE_SOL_BOOL, 3 }, - { TYPE_SOL_STRING, 4 }, - { TYPE_SOL_BYTE, 5 }, - { TYPE_SOL_BYTES_FIX, 6 }, - { TYPE_SOL_BYTES_DYN, 6 } - }; - uint8_t *previous_match; - uint8_t *typename_len_ptr; - char *typename_ptr; - - if ((typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) - { - return false; - } - *typenames_array = 0; - // loop over typenames - for (size_t s_idx = 0; - s_idx < (sizeof(typenames_str) / sizeof(typenames_str[IDX_ENUM])); - ++s_idx) - { - previous_match = NULL; - // loop over enum/typename pairs - for (size_t e_idx = 0; - e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[IDX_ENUM])); - ++e_idx) - { - if (s_idx == (size_t)enum_to_idx[e_idx][IDX_STR_IDX]) // match - { - if (previous_match) // in case of a previous match, mark it - { - *previous_match |= TYPENAME_MORE_TYPE; - } - if ((previous_match = mem_alloc(sizeof(uint8_t))) == NULL) - { - return false; - } - *previous_match = enum_to_idx[e_idx][IDX_ENUM]; - } - } - - if (previous_match) // if at least one match was found - { - if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - return false; - } - // get pointer to the allocated space just above - *typename_len_ptr = strlen(typenames_str[s_idx]); - - - if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) - { - return false; - } - // copy typename - memcpy(typename_ptr, typenames_str[s_idx], *typename_len_ptr); - } - // increment array size - *typenames_array += 1; - } - return true; -} - -bool init_eip712_context(void) -{ - // init global variables - init_mem(); - - if (init_typenames() == false) - return false; - - // set types pointer - if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) - return false; - - // create len(types) - *structs_array = 0; - return true; -} - int main(void) { uint8_t buf[260]; // 4 bytes APDU header + 256 bytes payload diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c new file mode 100644 index 0000000..018c263 --- /dev/null +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -0,0 +1,132 @@ +#include +#include +#include "sol_typenames.h" +#include "eip712.h" +#include "context.h" +#include "mem.h" + +// Bit indicating they are more types associated to this typename +#define TYPENAME_MORE_TYPE (1 << 7) + +enum +{ + IDX_ENUM = 0, + IDX_STR_IDX, + IDX_COUNT +}; + +bool init_sol_typenames(void) +{ + const char *const typenames_str[] = { + "int", // 0 + "uint", // 1 + "address", // 2 + "bool", // 3 + "string", // 4 + "byte", // 5 + "bytes" // 6 + }; + const uint8_t enum_to_idx[][IDX_COUNT] = { + { TYPE_SOL_INT, 0 }, + { TYPE_SOL_UINT, 1 }, + { TYPE_SOL_ADDRESS, 2 }, + { TYPE_SOL_BOOL, 3 }, + { TYPE_SOL_STRING, 4 }, + { TYPE_SOL_BYTE, 5 }, + { TYPE_SOL_BYTES_FIX, 6 }, + { TYPE_SOL_BYTES_DYN, 6 } + }; + uint8_t *previous_match; + uint8_t *typename_len_ptr; + char *typename_ptr; + + if ((typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *typenames_array = 0; + // loop over typenames + for (size_t s_idx = 0; + s_idx < (sizeof(typenames_str) / sizeof(typenames_str[IDX_ENUM])); + ++s_idx) + { + previous_match = NULL; + // loop over enum/typename pairs + for (size_t e_idx = 0; + e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[IDX_ENUM])); + ++e_idx) + { + if (s_idx == (size_t)enum_to_idx[e_idx][IDX_STR_IDX]) // match + { + if (previous_match) // in case of a previous match, mark it + { + *previous_match |= TYPENAME_MORE_TYPE; + } + if ((previous_match = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *previous_match = enum_to_idx[e_idx][IDX_ENUM]; + } + } + + if (previous_match) // if at least one match was found + { + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + // get pointer to the allocated space just above + *typename_len_ptr = strlen(typenames_str[s_idx]); + + + if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) + { + return false; + } + // copy typename + memcpy(typename_ptr, typenames_str[s_idx], *typename_len_ptr); + } + // increment array size + *typenames_array += 1; + } + return true; +} + +/** + * + * @param[in] field_ptr pointer to a struct field + * @param[out] length length of the returned typename + * @return typename or \ref NULL in case it wasn't found + */ +const char *get_struct_field_sol_typename(const uint8_t *field_ptr, + uint8_t *const length) +{ + e_type field_type; + const uint8_t *typename_ptr; + uint8_t typenames_count; + bool more_type; + bool typename_found; + + field_type = struct_field_type(field_ptr); + typename_ptr = get_array_in_mem(typenames_array, &typenames_count); + typename_found = false; + while (typenames_count-- > 0) + { + more_type = true; + while (more_type) + { + more_type = *typename_ptr & TYPENAME_MORE_TYPE; + e_type type_enum = *typename_ptr & TYPENAME_ENUM; + if (type_enum == field_type) + { + typename_found = true; + } + typename_ptr += 1; + } + typename_ptr = (uint8_t*)get_string_in_mem(typename_ptr, length); + if (typename_found) return (char*)typename_ptr; + typename_ptr += *length; + } + return NULL; // Not found +} diff --git a/src_features/signMessageEIP712/sol_typenames.h b/src_features/signMessageEIP712/sol_typenames.h new file mode 100644 index 0000000..b1e2a67 --- /dev/null +++ b/src_features/signMessageEIP712/sol_typenames.h @@ -0,0 +1,12 @@ +#ifndef SOL_TYPENAMES_H_ +#define SOL_TYPENAMES_H_ + +#include +#include + +bool init_sol_typenames(void); + +const char *get_struct_field_sol_typename(const uint8_t *ptr, + uint8_t *const length); + +#endif // SOL_TYPENAMES_H_ From 2584490afa95adab138c811f9b8d2918fb3d53ff Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 29 Mar 2022 18:29:36 +0200 Subject: [PATCH 021/184] Added array size macro --- src_features/signMessageEIP712/eip712.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 6d51647..b25790f 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -59,6 +59,8 @@ typedef enum #define KECCAK256_HASH_LENGTH 32 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + typedef struct { uint16_t length; From 547846f56d3718480b77b3ba9f595e1f30965f95 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 29 Mar 2022 18:30:08 +0200 Subject: [PATCH 022/184] Small code cleaning in the init of solidity typenames --- .../signMessageEIP712/sol_typenames.c | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 018c263..09f8d2c 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -15,9 +15,32 @@ enum IDX_COUNT }; -bool init_sol_typenames(void) +static bool find_enum_matches(const uint8_t (*enum_to_idx)[TYPES_COUNT - 1][IDX_COUNT], uint8_t s_idx) { - const char *const typenames_str[] = { + uint8_t *enum_match = NULL; + + // loop over enum/typename pairs + for (uint8_t e_idx = 0; e_idx < ARRAY_SIZE(*enum_to_idx); ++e_idx) + { + if (s_idx == (*enum_to_idx)[e_idx][IDX_STR_IDX]) // match + { + if (enum_match != NULL) // in case of a previous match, mark it + { + *enum_match |= TYPENAME_MORE_TYPE; + } + if ((enum_match = mem_alloc(sizeof(uint8_t))) == NULL) + { + return false; + } + *enum_match = (*enum_to_idx)[e_idx][IDX_ENUM]; + } + } + return (enum_match != NULL); +} + +bool init_sol_typenames(void) +{ + const char *const typenames[] = { "int", // 0 "uint", // 1 "address", // 2 @@ -26,7 +49,8 @@ bool init_sol_typenames(void) "byte", // 5 "bytes" // 6 }; - const uint8_t enum_to_idx[][IDX_COUNT] = { + // \ref TYPES_COUNT - 1 since we don't include \ref TYPE_CUSTOM + const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT] = { { TYPE_SOL_INT, 0 }, { TYPE_SOL_UINT, 1 }, { TYPE_SOL_ADDRESS, 2 }, @@ -36,7 +60,6 @@ bool init_sol_typenames(void) { TYPE_SOL_BYTES_FIX, 6 }, { TYPE_SOL_BYTES_DYN, 6 } }; - uint8_t *previous_match; uint8_t *typename_len_ptr; char *typename_ptr; @@ -46,46 +69,24 @@ bool init_sol_typenames(void) } *typenames_array = 0; // loop over typenames - for (size_t s_idx = 0; - s_idx < (sizeof(typenames_str) / sizeof(typenames_str[IDX_ENUM])); - ++s_idx) + for (uint8_t s_idx = 0; s_idx < ARRAY_SIZE(typenames); ++s_idx) { - previous_match = NULL; - // loop over enum/typename pairs - for (size_t e_idx = 0; - e_idx < (sizeof(enum_to_idx) / sizeof(enum_to_idx[IDX_ENUM])); - ++e_idx) - { - if (s_idx == (size_t)enum_to_idx[e_idx][IDX_STR_IDX]) // match - { - if (previous_match) // in case of a previous match, mark it - { - *previous_match |= TYPENAME_MORE_TYPE; - } - if ((previous_match = mem_alloc(sizeof(uint8_t))) == NULL) - { - return false; - } - *previous_match = enum_to_idx[e_idx][IDX_ENUM]; - } - } - - if (previous_match) // if at least one match was found + // if at least one match was found + if (find_enum_matches(&enum_to_idx, s_idx)) { if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } // get pointer to the allocated space just above - *typename_len_ptr = strlen(typenames_str[s_idx]); - + *typename_len_ptr = strlen(typenames[s_idx]); if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { return false; } // copy typename - memcpy(typename_ptr, typenames_str[s_idx], *typename_len_ptr); + memcpy(typename_ptr, typenames[s_idx], *typename_len_ptr); } // increment array size *typenames_array += 1; From c30b3aceecd72917b17278dea3334fd422273c5e Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:29:02 +0200 Subject: [PATCH 023/184] Added memory allocation utility functions --- src_features/signMessageEIP712/encode_type.c | 55 +++++--------------- src_features/signMessageEIP712/mem_utils.c | 43 +++++++++++++++ src_features/signMessageEIP712/mem_utils.h | 11 ++++ 3 files changed, 66 insertions(+), 43 deletions(-) create mode 100644 src_features/signMessageEIP712/mem_utils.c create mode 100644 src_features/signMessageEIP712/mem_utils.h diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c index 057cc91..7554fe4 100644 --- a/src_features/signMessageEIP712/encode_type.c +++ b/src_features/signMessageEIP712/encode_type.c @@ -3,30 +3,10 @@ #include #include #include "mem.h" +#include "mem_utils.h" #include "eip712.h" #include "encode_type.h" -/** - * Format an unsigned number up to 32-bit into memory into an ASCII string. - * - * @param[in] value Value to write in memory - * @param[in] max_chars Maximum number of characters that could be written - * - * @return how many characters have been written in memory, 0 in case of an allocation error - */ -static uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) -{ - char *ptr; - uint8_t written_chars; - - if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) - { - return 0; - } - written_chars = sprintf(ptr, "%u", value); - mem_dealloc(max_chars - written_chars); // in case it ended up being less - return written_chars; -} /** * @@ -48,42 +28,35 @@ static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint1 uint8_t lvls_count; const uint8_t *lvl_ptr; uint8_t array_size; - char *char_ptr; - char *name_ptr; // add name struct_name = get_struct_name(struct_ptr, &struct_name_length); - if ((name_ptr = mem_alloc(sizeof(char) * struct_name_length)) == NULL) + if ((str_start = alloc_and_copy(struct_name, struct_name_length)) == NULL) { return NULL; } - str_start = memmove(name_ptr, struct_name, struct_name_length); - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char('(') == NULL) { return NULL; } - *char_ptr = '('; field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) { if (idx > 0) { - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char(',') == NULL) { return NULL; } - *char_ptr = ','; } name = get_struct_field_typename(field_ptr, &length); - - if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + if (alloc_and_copy(name, length) == NULL) { return NULL; } - memmove(name_ptr, name, length); if (struct_field_has_typesize(field_ptr)) { @@ -109,11 +82,11 @@ static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint1 lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); while (lvls_count-- > 0) { - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char('[') == NULL) { return NULL; } - *char_ptr = '['; + switch (struct_field_array_depth(lvl_ptr, &array_size)) { case ARRAY_DYNAMIC: @@ -126,34 +99,30 @@ static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint1 // should not be in here :^) break; } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char(']') == NULL) { return NULL; } - *char_ptr = ']'; lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); } } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char(' ') == NULL) { return NULL; } - *char_ptr = ' '; - name = get_struct_field_keyname(field_ptr, &length); - if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) + name = get_struct_field_keyname(field_ptr, &length); + if (alloc_and_copy(name, length) == NULL) { return NULL; } - memmove(name_ptr, name, length); field_ptr = get_next_struct_field(field_ptr); } - if ((char_ptr = mem_alloc(sizeof(char))) == NULL) + if (alloc_and_copy_char(')') == NULL) { return NULL; } - *char_ptr = ')'; *str_length = ((char*)mem_alloc(0) - str_start); return str_start; diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c new file mode 100644 index 0000000..4b44214 --- /dev/null +++ b/src_features/signMessageEIP712/mem_utils.c @@ -0,0 +1,43 @@ +#include +#include +#include +#include "mem.h" +#include "mem_utils.h" + +void *alloc_and_copy(const void *data, size_t size) +{ + void *mem_ptr; + + if ((mem_ptr = mem_alloc(size)) != NULL) + { + memmove(mem_ptr, data, size); + } + return mem_ptr; +} + +char *alloc_and_copy_char(char c) +{ + return alloc_and_copy(&c, sizeof(char)); +} + +/** + * Format an unsigned number up to 32-bit into memory into an ASCII string. + * + * @param[in] value Value to write in memory + * @param[in] max_chars Maximum number of characters that could be written + * + * @return how many characters have been written in memory, 0 in case of an allocation error + */ +uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) +{ + char *ptr; + uint8_t written_chars; + + if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) + { + return 0; + } + written_chars = sprintf(ptr, "%u", value); + mem_dealloc(max_chars - written_chars); // in case it ended up being less + return written_chars; +} diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h new file mode 100644 index 0000000..8488d11 --- /dev/null +++ b/src_features/signMessageEIP712/mem_utils.h @@ -0,0 +1,11 @@ +#ifndef MEM_UTILS_H_ +#define MEM_UTILS_H_ + +#include +#include + +char *alloc_and_copy_char(char c); +void *alloc_and_copy(const void *data, size_t size); +uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars); + +#endif // MEM_UTILS_H_ From 9ca9caf410077e550f51b122cc9f433b42901870 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 30 Mar 2022 17:43:41 +0200 Subject: [PATCH 024/184] encodeType reformatting / code cleanup --- src_features/signMessageEIP712/encode_type.c | 182 +++++++++++-------- 1 file changed, 109 insertions(+), 73 deletions(-) diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c index 7554fe4..42ab111 100644 --- a/src_features/signMessageEIP712/encode_type.c +++ b/src_features/signMessageEIP712/encode_type.c @@ -7,35 +7,127 @@ #include "eip712.h" #include "encode_type.h" +/** + * + * @param[in] lvl_ptr pointer to the first array level of a struct field + * @param[in] lvls_count the number of array levels the struct field contains + * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) + */ +static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t lvls_count) +{ + uint8_t array_size; + + while (lvls_count-- > 0) + { + if (alloc_and_copy_char('[') == NULL) + { + return false; + } + + switch (struct_field_array_depth(lvl_ptr, &array_size)) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + // max value = 255, 3 characters max + format_uint_into_mem(array_size, 3); + break; + default: + // should not be in here :^) + break; + } + if (alloc_and_copy_char(']') == NULL) + { + return false; + } + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); + } + return true; +} /** * + * @param[in] field_ptr pointer to the struct field + * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) + */ +static bool format_field_string(const void *field_ptr) +{ + const char *name; + uint8_t length; + uint16_t field_size; + uint8_t lvls_count; + const uint8_t *lvl_ptr; + + // field type name + name = get_struct_field_typename(field_ptr, &length); + if (alloc_and_copy(name, length) == NULL) + { + return false; + } + + // field type size + if (struct_field_has_typesize(field_ptr)) + { + field_size = get_struct_field_typesize(field_ptr); + switch (struct_field_type(field_ptr)) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + field_size *= 8; // bytes -> bits + break; + case TYPE_SOL_BYTES_FIX: + break; + default: + // should not be in here :^) + break; + } + // max value = 256, 3 characters max + format_uint_into_mem(field_size, 3); + } + + // field type array levels + if (struct_field_is_array(field_ptr)) + { + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + format_field_type_array_levels_string(lvl_ptr, lvls_count); + } + // space between field type name and field name + if (alloc_and_copy_char(' ') == NULL) + { + return false; + } + + // field name + name = get_struct_field_keyname(field_ptr, &length); + if (alloc_and_copy(name, length) == NULL) + { + return false; + } + return true; +} + +/** * * @param[in] struct_ptr pointer to the structure we want the typestring of * @param[in] str_length length of the formatted string in memory * @return pointer of the string in memory, \ref NULL in case of an error */ -static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint16_t *const str_length) +static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_t *const str_length) { const char *str_start; const char *struct_name; uint8_t struct_name_length; const uint8_t *field_ptr; uint8_t fields_count; - const char *name; - uint8_t length; - uint16_t field_size; - uint8_t lvls_count; - const uint8_t *lvl_ptr; - uint8_t array_size; - // add name + // struct name struct_name = get_struct_name(struct_ptr, &struct_name_length); if ((str_start = alloc_and_copy(struct_name, struct_name_length)) == NULL) { return NULL; } + // opening struct parenthese if (alloc_and_copy_char('(') == NULL) { return NULL; @@ -44,6 +136,7 @@ static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint1 field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) { + // comma separating struct fields if (idx > 0) { if (alloc_and_copy_char(',') == NULL) @@ -52,78 +145,21 @@ static const char *get_struct_type_string(const uint8_t *const struct_ptr, uint1 } } - name = get_struct_field_typename(field_ptr, &length); - if (alloc_and_copy(name, length) == NULL) + if (format_field_string(field_ptr) == false) { return NULL; } - if (struct_field_has_typesize(field_ptr)) - { - field_size = get_struct_field_typesize(field_ptr); - switch (struct_field_type(field_ptr)) - { - case TYPE_SOL_INT: - case TYPE_SOL_UINT: - field_size *= 8; // bytes -> bits - break; - case TYPE_SOL_BYTES_FIX: - break; - default: - // should not be in here :^) - break; - } - // max value = 256, 3 characters max - format_uint_into_mem(field_size, 3); - } - - if (struct_field_is_array(field_ptr)) - { - lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - while (lvls_count-- > 0) - { - if (alloc_and_copy_char('[') == NULL) - { - return NULL; - } - - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - // max value = 255, 3 characters max - format_uint_into_mem(array_size, 3); - break; - default: - // should not be in here :^) - break; - } - if (alloc_and_copy_char(']') == NULL) - { - return NULL; - } - lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); - } - } - if (alloc_and_copy_char(' ') == NULL) - { - return NULL; - } - - name = get_struct_field_keyname(field_ptr, &length); - if (alloc_and_copy(name, length) == NULL) - { - return NULL; - } field_ptr = get_next_struct_field(field_ptr); } + // closing struct parenthese if (alloc_and_copy_char(')') == NULL) { return NULL; } + // compute the length *str_length = ((char*)mem_alloc(0) - str_start); return str_start; } @@ -249,25 +285,25 @@ const char *encode_type(const void *const structs_array, *encoded_length = 0; if ((deps_count = mem_alloc(sizeof(uint8_t))) == NULL) { - return NULL;//false; + return NULL; } *deps_count = 0; // get list of structs (own + dependencies), properly ordered dep = (void**)(deps_count + 1); // get first elem if (get_struct_dependencies(structs_array, deps_count, dep, struct_ptr) == false) { - return NULL;//false; + return NULL; } sort_dependencies(deps_count, dep); - typestr = get_struct_type_string(struct_ptr, &length); + typestr = format_struct_string(struct_ptr, &length); *encoded_length += length; // loop over each struct and generate string for (int idx = 0; idx < *deps_count; ++idx) { - get_struct_type_string(*dep, &length); + format_struct_string(*dep, &length); *encoded_length += length; dep += 1; } - return typestr;//true; + return typestr; } From 1375571957d64836c68df5c5d89ee41f987c0ae9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 30 Mar 2022 17:52:41 +0200 Subject: [PATCH 025/184] Unify mem utils function names --- src_features/signMessageEIP712/encode_type.c | 22 ++++++++++---------- src_features/signMessageEIP712/mem_utils.c | 8 +++---- src_features/signMessageEIP712/mem_utils.h | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c index 42ab111..3a229b5 100644 --- a/src_features/signMessageEIP712/encode_type.c +++ b/src_features/signMessageEIP712/encode_type.c @@ -19,7 +19,7 @@ static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t l while (lvls_count-- > 0) { - if (alloc_and_copy_char('[') == NULL) + if (mem_alloc_and_copy_char('[') == NULL) { return false; } @@ -30,13 +30,13 @@ static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t l break; case ARRAY_FIXED_SIZE: // max value = 255, 3 characters max - format_uint_into_mem(array_size, 3); + mem_alloc_and_format_uint(array_size, 3); break; default: // should not be in here :^) break; } - if (alloc_and_copy_char(']') == NULL) + if (mem_alloc_and_copy_char(']') == NULL) { return false; } @@ -60,7 +60,7 @@ static bool format_field_string(const void *field_ptr) // field type name name = get_struct_field_typename(field_ptr, &length); - if (alloc_and_copy(name, length) == NULL) + if (mem_alloc_and_copy(name, length) == NULL) { return false; } @@ -82,7 +82,7 @@ static bool format_field_string(const void *field_ptr) break; } // max value = 256, 3 characters max - format_uint_into_mem(field_size, 3); + mem_alloc_and_format_uint(field_size, 3); } // field type array levels @@ -92,14 +92,14 @@ static bool format_field_string(const void *field_ptr) format_field_type_array_levels_string(lvl_ptr, lvls_count); } // space between field type name and field name - if (alloc_and_copy_char(' ') == NULL) + if (mem_alloc_and_copy_char(' ') == NULL) { return false; } // field name name = get_struct_field_keyname(field_ptr, &length); - if (alloc_and_copy(name, length) == NULL) + if (mem_alloc_and_copy(name, length) == NULL) { return false; } @@ -122,13 +122,13 @@ static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_ // struct name struct_name = get_struct_name(struct_ptr, &struct_name_length); - if ((str_start = alloc_and_copy(struct_name, struct_name_length)) == NULL) + if ((str_start = mem_alloc_and_copy(struct_name, struct_name_length)) == NULL) { return NULL; } // opening struct parenthese - if (alloc_and_copy_char('(') == NULL) + if (mem_alloc_and_copy_char('(') == NULL) { return NULL; } @@ -139,7 +139,7 @@ static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_ // comma separating struct fields if (idx > 0) { - if (alloc_and_copy_char(',') == NULL) + if (mem_alloc_and_copy_char(',') == NULL) { return NULL; } @@ -154,7 +154,7 @@ static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_ field_ptr = get_next_struct_field(field_ptr); } // closing struct parenthese - if (alloc_and_copy_char(')') == NULL) + if (mem_alloc_and_copy_char(')') == NULL) { return NULL; } diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index 4b44214..0220fb0 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -4,7 +4,7 @@ #include "mem.h" #include "mem_utils.h" -void *alloc_and_copy(const void *data, size_t size) +void *mem_alloc_and_copy(const void *data, size_t size) { void *mem_ptr; @@ -15,9 +15,9 @@ void *alloc_and_copy(const void *data, size_t size) return mem_ptr; } -char *alloc_and_copy_char(char c) +char *mem_alloc_and_copy_char(char c) { - return alloc_and_copy(&c, sizeof(char)); + return mem_alloc_and_copy(&c, sizeof(char)); } /** @@ -28,7 +28,7 @@ char *alloc_and_copy_char(char c) * * @return how many characters have been written in memory, 0 in case of an allocation error */ -uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars) +uint8_t mem_alloc_and_format_uint(uint32_t value, const uint8_t max_chars) { char *ptr; uint8_t written_chars; diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h index 8488d11..dad38d6 100644 --- a/src_features/signMessageEIP712/mem_utils.h +++ b/src_features/signMessageEIP712/mem_utils.h @@ -4,8 +4,8 @@ #include #include -char *alloc_and_copy_char(char c); -void *alloc_and_copy(const void *data, size_t size); -uint8_t format_uint_into_mem(uint32_t value, const uint8_t max_chars); +char *mem_alloc_and_copy_char(char c); +void *mem_alloc_and_copy(const void *data, size_t size); +uint8_t mem_alloc_and_format_uint(uint32_t value, const uint8_t max_chars); #endif // MEM_UTILS_H_ From 3d9089b395fb7e00b32c581bc3aea439bbf6a53e Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:29:19 +0200 Subject: [PATCH 026/184] New functions that emulate the device's hashing functions --- src_features/signMessageEIP712/hash_wrap.c | 33 ++++++++++++++++++++++ src_features/signMessageEIP712/hash_wrap.h | 17 +++++++++++ 2 files changed, 50 insertions(+) create mode 100644 src_features/signMessageEIP712/hash_wrap.c create mode 100644 src_features/signMessageEIP712/hash_wrap.h diff --git a/src_features/signMessageEIP712/hash_wrap.c b/src_features/signMessageEIP712/hash_wrap.c new file mode 100644 index 0000000..be54c26 --- /dev/null +++ b/src_features/signMessageEIP712/hash_wrap.c @@ -0,0 +1,33 @@ +#include +#include +#include "hash_wrap.h" + +cx_sha3_t global_sha3; + +int cx_keccak_init(cx_hash_t *hash, size_t size) +{ + sha3_context *ctx = (sha3_context*)hash; + + (void)size; + sha3_Init256(ctx); + sha3_SetFlags(ctx, SHA3_FLAGS_KECCAK); + return 0; +} + +int cx_hash(cx_hash_t *hash, int mode, const unsigned char *in, + unsigned int len, unsigned char *out, unsigned int out_len) +{ + sha3_context *ctx = (sha3_context*)hash; + const uint8_t *result; + + sha3_Update(ctx, in, len); + if (mode == CX_LAST) + { + result = sha3_Finalize(ctx); + if (out != NULL) + { + memmove(out, result, out_len); + } + } + return out_len; +} diff --git a/src_features/signMessageEIP712/hash_wrap.h b/src_features/signMessageEIP712/hash_wrap.h new file mode 100644 index 0000000..5910322 --- /dev/null +++ b/src_features/signMessageEIP712/hash_wrap.h @@ -0,0 +1,17 @@ +#ifndef HASH_WRAP_H_ +#define HASH_WRAP_H_ + +#include +#include "sha3.h" + +#define CX_LAST 1 + +typedef sha3_context cx_sha3_t; +typedef struct {} cx_hash_t; + +extern cx_sha3_t global_sha3; + +int cx_keccak_init(cx_hash_t *hash, size_t size); +int cx_hash(cx_hash_t *hash, int mode, const unsigned char *in, + unsigned int len, unsigned char *out, unsigned int out_len); +#endif // HASH_WRAP_H_ From c53a4299a3b516dc0ee7f4f0c3a33e0ad578005f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Mar 2022 17:08:44 +0200 Subject: [PATCH 027/184] TypeHash function now uses the device's hashing functions --- src_features/signMessageEIP712/type_hash.c | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index ee471fc..1069bf5 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -4,7 +4,7 @@ #include "mem.h" #include "encode_type.h" #include "type_hash.h" -#include "sha3.h" +#include "hash_wrap.h" const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, @@ -13,8 +13,6 @@ const uint8_t *type_hash(const void *const structs_array, const void *const mem_loc_bak = mem_alloc(0); // backup the memory location const char *typestr; uint16_t length; - sha3_context ctx; - const uint8_t *hash; uint8_t *hash_ptr; typestr = encode_type(structs_array, struct_name, struct_name_length, &length); @@ -22,12 +20,16 @@ const uint8_t *type_hash(const void *const structs_array, { return NULL; } - sha3_Init256(&ctx); - sha3_SetFlags(&ctx, SHA3_FLAGS_KECCAK); - sha3_Update(&ctx, typestr, length); - hash = sha3_Finalize(&ctx); + cx_keccak_init((cx_hash_t*)&global_sha3, 256); + cx_hash((cx_hash_t*)&global_sha3, + 0, + (uint8_t*)typestr, + length, + NULL, + 0); #ifdef DEBUG + // Print type string fwrite(typestr, sizeof(char), length, stdout); printf("\n"); #endif @@ -40,17 +42,19 @@ const uint8_t *type_hash(const void *const structs_array, { return NULL; } + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + hash_ptr, + KECCAK256_HASH_LENGTH); #ifdef DEBUG + // print computed hash printf("-> 0x"); -#endif for (int idx = 0; idx < KECCAK256_HASH_LENGTH; ++idx) { - hash_ptr[idx] = hash[idx]; -#ifdef DEBUG - printf("%.02x", hash[idx]); -#endif + printf("%.02x", hash_ptr[idx]); } -#ifdef DEBUG printf("\n"); #endif return hash_ptr; From 223aa10a582c676a6ffd90ffe165f16da691c5dc Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 31 Mar 2022 17:11:46 +0200 Subject: [PATCH 028/184] Better macro naming for hash length --- src_features/signMessageEIP712/eip712.h | 2 +- src_features/signMessageEIP712/type_hash.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index b25790f..41c54f9 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -57,7 +57,7 @@ typedef enum #define TYPESIZE_MASK (1 << 6) #define TYPENAME_ENUM (0xF) -#define KECCAK256_HASH_LENGTH 32 +#define KECCAK256_HASH_BYTESIZE 32 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 1069bf5..bd5680e 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -38,7 +38,7 @@ const uint8_t *type_hash(const void *const structs_array, mem_dealloc(mem_alloc(0) - mem_loc_bak); // copy hash into memory - if ((hash_ptr = mem_alloc(KECCAK256_HASH_LENGTH)) == NULL) + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { return NULL; } @@ -47,11 +47,11 @@ const uint8_t *type_hash(const void *const structs_array, NULL, 0, hash_ptr, - KECCAK256_HASH_LENGTH); + KECCAK256_HASH_BYTESIZE); #ifdef DEBUG // print computed hash printf("-> 0x"); - for (int idx = 0; idx < KECCAK256_HASH_LENGTH; ++idx) + for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { printf("%.02x", hash_ptr[idx]); } From 9521b36de32bf8d683405bb6a4a4cc221b7b669c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 4 Apr 2022 12:03:37 +0200 Subject: [PATCH 029/184] Now prefixes the type hash with a marker --- src_features/signMessageEIP712/eip712.h | 7 +++++++ src_features/signMessageEIP712/type_hash.c | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 41c54f9..26ed8d1 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -57,6 +57,13 @@ typedef enum #define TYPESIZE_MASK (1 << 6) #define TYPENAME_ENUM (0xF) +typedef enum +{ + EIP712_TYPE_HASH, + EIP712_FIELD_HASH, + EIP712_STRUCT_HASH +} e_eip712_hash_type; + #define KECCAK256_HASH_BYTESIZE 32 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index bd5680e..3ccad3c 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -37,23 +37,27 @@ const uint8_t *type_hash(const void *const structs_array, // restore the memory location mem_dealloc(mem_alloc(0) - mem_loc_bak); - // copy hash into memory - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE + 1)) == NULL) { return NULL; } + + // set TypeHash marker + *hash_ptr = EIP712_TYPE_HASH; + + // copy hash into memory cx_hash((cx_hash_t*)&global_sha3, CX_LAST, NULL, 0, - hash_ptr, + hash_ptr + 1, KECCAK256_HASH_BYTESIZE); #ifdef DEBUG // print computed hash printf("-> 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { - printf("%.02x", hash_ptr[idx]); + printf("%.02x", (hash_ptr + 1)[idx]); } printf("\n"); #endif From fd31def094d2fef399fca55eb45a17216493115c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:35:31 +0200 Subject: [PATCH 030/184] Update app context wrapping --- src_features/signMessageEIP712/hash_wrap.c | 33 ---------------------- src_features/signMessageEIP712/hash_wrap.h | 17 ----------- src_features/signMessageEIP712/type_hash.c | 2 +- 3 files changed, 1 insertion(+), 51 deletions(-) delete mode 100644 src_features/signMessageEIP712/hash_wrap.c delete mode 100644 src_features/signMessageEIP712/hash_wrap.h diff --git a/src_features/signMessageEIP712/hash_wrap.c b/src_features/signMessageEIP712/hash_wrap.c deleted file mode 100644 index be54c26..0000000 --- a/src_features/signMessageEIP712/hash_wrap.c +++ /dev/null @@ -1,33 +0,0 @@ -#include -#include -#include "hash_wrap.h" - -cx_sha3_t global_sha3; - -int cx_keccak_init(cx_hash_t *hash, size_t size) -{ - sha3_context *ctx = (sha3_context*)hash; - - (void)size; - sha3_Init256(ctx); - sha3_SetFlags(ctx, SHA3_FLAGS_KECCAK); - return 0; -} - -int cx_hash(cx_hash_t *hash, int mode, const unsigned char *in, - unsigned int len, unsigned char *out, unsigned int out_len) -{ - sha3_context *ctx = (sha3_context*)hash; - const uint8_t *result; - - sha3_Update(ctx, in, len); - if (mode == CX_LAST) - { - result = sha3_Finalize(ctx); - if (out != NULL) - { - memmove(out, result, out_len); - } - } - return out_len; -} diff --git a/src_features/signMessageEIP712/hash_wrap.h b/src_features/signMessageEIP712/hash_wrap.h deleted file mode 100644 index 5910322..0000000 --- a/src_features/signMessageEIP712/hash_wrap.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef HASH_WRAP_H_ -#define HASH_WRAP_H_ - -#include -#include "sha3.h" - -#define CX_LAST 1 - -typedef sha3_context cx_sha3_t; -typedef struct {} cx_hash_t; - -extern cx_sha3_t global_sha3; - -int cx_keccak_init(cx_hash_t *hash, size_t size); -int cx_hash(cx_hash_t *hash, int mode, const unsigned char *in, - unsigned int len, unsigned char *out, unsigned int out_len); -#endif // HASH_WRAP_H_ diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 3ccad3c..fdd46c7 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -4,7 +4,7 @@ #include "mem.h" #include "encode_type.h" #include "type_hash.h" -#include "hash_wrap.h" +#include "shared_context.h" const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, From 7e35b96ec0982a9f0fea4a93c08d6303967d1a2b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:30:14 +0200 Subject: [PATCH 031/184] WIP field hashing --- src_features/signMessageEIP712/encode_field.c | 129 ++++++++++++++++++ src_features/signMessageEIP712/encode_field.h | 15 ++ src_features/signMessageEIP712/entrypoint.c | 2 + src_features/signMessageEIP712/field_hash.c | 14 ++ src_features/signMessageEIP712/field_hash.h | 9 ++ 5 files changed, 169 insertions(+) create mode 100644 src_features/signMessageEIP712/encode_field.c create mode 100644 src_features/signMessageEIP712/encode_field.h create mode 100644 src_features/signMessageEIP712/field_hash.c create mode 100644 src_features/signMessageEIP712/field_hash.h diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c new file mode 100644 index 0000000..e075f4d --- /dev/null +++ b/src_features/signMessageEIP712/encode_field.c @@ -0,0 +1,129 @@ +#include +#include +#include "encode_field.h" +#include "mem.h" +#include "eip712.h" +#include "shared_context.h" + + +/** + * Hash field value + * + * @param[in] value pointer to value + * @param[in] length its bytelength + * @param[in] dealloc if the value length should be deallocated from the memory + */ +static void *hash_field_value(const void *const value, uint16_t length, bool dealloc) +{ + uint8_t *hash_ptr = NULL; + + if (value != NULL) + { + cx_keccak_init((cx_hash_t*)&global_sha3, 256); + cx_hash((cx_hash_t*)&global_sha3, + 0, + (uint8_t*)value, + length, + NULL, + 0); + + if (dealloc) + { + // restore the memory location + mem_dealloc(length); + } + + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + { + return NULL; + } + + // copy hash into memory + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + hash_ptr, + KECCAK256_HASH_BYTESIZE); + } + return hash_ptr; +} + +/** + * Encode an integer and hash it + * + * @param[in] value pointer to the "packed" integer received + * @param[in] length its byte-length + * @return the encoded (hashed) value + */ +void *encode_integer(const uint8_t *const value, uint16_t length) +{ + uint8_t *padded_value; + + // 0-pad the value to 32 bytes + if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) + { + explicit_bzero(padded_value, EIP_712_ENCODED_FIELD_LENGTH - length); + for (uint8_t idx = 0; idx < length; ++idx) + { + padded_value[EIP_712_ENCODED_FIELD_LENGTH - (length - idx)] = value[idx]; + } + } + return hash_field_value(padded_value, EIP_712_ENCODED_FIELD_LENGTH, true); +} + +/** + * Encode a string and hash it + * + * @param[in] value pointer to the string received + * @param[in] length its byte-length + * @return the encoded (hashed) value + */ +void *encode_string(const char *const value, uint16_t length) +{ + return hash_field_value(value, length, false); +} + +/** + * Encode a boolean and hash it + * + * @param[in] value pointer to the boolean received + * @param[in] length its byte-length + * @return the encoded (hashed) value + */ +void *encode_bool(const bool *const value, uint16_t length) +{ + if (length != 1) + { + return NULL; + } + return encode_integer((uint8_t*)value, length); +} + +/** + * Encode an address and hash it + * + * @param[in] value pointer to the address received + * @param[in] length its byte-length + * @return the encoded (hashed) value + */ +void *encode_address(const uint8_t *const value, uint16_t length) +{ + if (length != ADDRESS_LENGTH) + { + return NULL; + } + return encode_integer(value, length); +} + +/** + * Encode bytes and hash it + * + * @param[in] value pointer to the bytes received + * @param[in] length its byte-length + * @return the encoded (hashed) value + */ +void *encode_bytes(const uint8_t *const value, uint16_t length) +{ + return hash_field_value(value, length, false); +} diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h new file mode 100644 index 0000000..eea2429 --- /dev/null +++ b/src_features/signMessageEIP712/encode_field.h @@ -0,0 +1,15 @@ +#ifndef ENCODE_FIELD_H_ +#define ENCODE_FIELD_H_ + +#include +#include + +#define EIP_712_ENCODED_FIELD_LENGTH 32 + +void *encode_integer(const uint8_t *const value, uint16_t length); +void *encode_string(const char *const value, uint16_t length); +void *encode_bool(const bool *const value, uint16_t length); +void *encode_address(const uint8_t *const value, uint16_t length); +void *encode_bytes(const uint8_t *const value, uint16_t length); + +#endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 9e85e40..f265ad7 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -9,6 +9,7 @@ #include "type_hash.h" #include "context.h" #include "sol_typenames.h" +#include "field_hash.h" // lib functions @@ -381,6 +382,7 @@ bool handle_apdu(const uint8_t *const data) type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: + field_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_ARRAY: break; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c new file mode 100644 index 0000000..db7b38f --- /dev/null +++ b/src_features/signMessageEIP712/field_hash.c @@ -0,0 +1,14 @@ +#include +#include "field_hash.h" +#include "encode_field.h" + +const uint8_t *field_hash(const void *const structs_array, + const uint8_t *const data, + const uint8_t data_length) +{ + (void)structs_array; + (void)data; + (void)data_length; + encode_integer(data, data_length); + return NULL; +} diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h new file mode 100644 index 0000000..9b2a2ac --- /dev/null +++ b/src_features/signMessageEIP712/field_hash.h @@ -0,0 +1,9 @@ +#ifndef FIELD_HASH_H_ +#define FIELD_HASH_H_ + +#include + +const uint8_t *field_hash(const void *const structs_array, + const uint8_t *const data, + const uint8_t data_length); +#endif // FIELD_HASH_H_ From 0e386a420452db5d225d803c7328bfbbe25aae17 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:30:26 +0200 Subject: [PATCH 032/184] Now initializes a path in memory --- src_features/signMessageEIP712/context.c | 8 +++++++ src_features/signMessageEIP712/encode_field.c | 8 +++++-- src_features/signMessageEIP712/entrypoint.c | 1 + src_features/signMessageEIP712/field_hash.c | 2 ++ src_features/signMessageEIP712/path.c | 22 +++++++++++++++++++ src_features/signMessageEIP712/path.h | 12 ++++++++++ 6 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 src_features/signMessageEIP712/path.c create mode 100644 src_features/signMessageEIP712/path.h diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 6db0e0d..b76a3eb 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -4,6 +4,7 @@ #include "eip712.h" #include "mem.h" #include "sol_typenames.h" +#include "path.h" uint8_t *typenames_array; uint8_t *structs_array; @@ -21,9 +22,16 @@ bool init_eip712_context(void) if (init_sol_typenames() == false) return false; + if (init_path() == false) + { + return false; + } + // set types pointer if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + { return false; + } // create len(types) *structs_array = 0; diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index e075f4d..0f670d2 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -60,6 +60,10 @@ void *encode_integer(const uint8_t *const value, uint16_t length) { uint8_t *padded_value; + if (length > 32) // sanity check + { + return NULL; + } // 0-pad the value to 32 bytes if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) { @@ -93,7 +97,7 @@ void *encode_string(const char *const value, uint16_t length) */ void *encode_bool(const bool *const value, uint16_t length) { - if (length != 1) + if (length != 1) // sanity check { return NULL; } @@ -109,7 +113,7 @@ void *encode_bool(const bool *const value, uint16_t length) */ void *encode_address(const uint8_t *const value, uint16_t length) { - if (length != ADDRESS_LENGTH) + if (length != ADDRESS_LENGTH) // sanity check { return NULL; } diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index f265ad7..08751cc 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -258,6 +258,7 @@ bool set_struct_name(const uint8_t *const data) return true; } +// TODO: Split this function bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_DATA; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index db7b38f..f4aee73 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -9,6 +9,8 @@ const uint8_t *field_hash(const void *const structs_array, (void)structs_array; (void)data; (void)data_length; + // get field by path encode_integer(data, data_length); + // path += 1 return NULL; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c new file mode 100644 index 0000000..dc7368d --- /dev/null +++ b/src_features/signMessageEIP712/path.c @@ -0,0 +1,22 @@ +#include +#include +#include "path.h" +#include "mem.h" +#include "context.h" + +uint8_t *path_indexes; + +/** + * Allocates the the path indexes in memory and sets them all to 0 with a count of 1. + */ +bool init_path(void) +{ + // + 1 for the used index count + if ((path_indexes = mem_alloc(sizeof(uint8_t) * (MAX_PATH_DEPTH + 1))) != NULL) + { + // set all to 0 + explicit_bzero(path_indexes + 1, sizeof(uint8_t) * MAX_PATH_DEPTH); + *path_indexes = 1; // init count at 1, so the default path will be 0 + } + return path_indexes != NULL; +} diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h new file mode 100644 index 0000000..8eda5ba --- /dev/null +++ b/src_features/signMessageEIP712/path.h @@ -0,0 +1,12 @@ +#ifndef PATH_H_ +#define PATH_H_ + +#include + +#define MAX_PATH_DEPTH 16 + +extern uint8_t *path_indexes; + +bool init_path(void); + +#endif // PATH_H_ From 45dfd5d6a6f4b1e43a349971693a4c72889e26f8 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 8 Apr 2022 18:55:56 +0200 Subject: [PATCH 033/184] Proper path implementation with array support --- src_features/signMessageEIP712/context.c | 4 +- src_features/signMessageEIP712/eip712.h | 1 - src_features/signMessageEIP712/entrypoint.c | 4 + src_features/signMessageEIP712/field_hash.c | 24 +- src_features/signMessageEIP712/path.c | 402 +++++++++++++++++++- src_features/signMessageEIP712/path.h | 23 +- 6 files changed, 443 insertions(+), 15 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index b76a3eb..56e5d36 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -20,9 +20,11 @@ bool init_eip712_context(void) mem_init(); if (init_sol_typenames() == false) + { return false; + } - if (init_path() == false) + if (path_init() == false) { return false; } diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 26ed8d1..c77b182 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -108,7 +108,6 @@ const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length); const char *get_struct_field_keyname(const uint8_t *ptr, uint8_t *const length); -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); const uint8_t *get_next_struct_field(const void *ptr); const uint8_t *get_structn(const uint8_t *const ptr, const char *const name_ptr, diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 08751cc..40d42c9 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -10,6 +10,7 @@ #include "context.h" #include "sol_typenames.h" #include "field_hash.h" +#include "path.h" // lib functions @@ -380,12 +381,15 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: + // set root type + path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: field_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_ARRAY: + path_new_array_depth(data[OFFSET_DATA]); break; default: printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index f4aee73..5cbf791 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -1,16 +1,36 @@ +#include #include #include "field_hash.h" #include "encode_field.h" +#include "path.h" +#include "eip712.h" const uint8_t *field_hash(const void *const structs_array, const uint8_t *const data, const uint8_t data_length) { + const char *type; + uint8_t typelen; + const char *key; + uint8_t keylen; + const void *field; + (void)structs_array; (void)data; (void)data_length; // get field by path - encode_integer(data, data_length); - // path += 1 + //encode_integer(data, data_length); + field = path_get_field(); + if (field != NULL) + { + printf("==> "); + type = get_struct_field_typename(field, &typelen); + fwrite(type, sizeof(char), typelen, stdout); + printf(" "); + key = get_struct_field_keyname(field, &keylen); + fwrite(key, sizeof(char), keylen, stdout); + printf("\n"); + path_advance(); + } return NULL; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index dc7368d..7427896 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -1,22 +1,406 @@ #include #include +#include #include "path.h" #include "mem.h" #include "context.h" +#include "eip712.h" -uint8_t *path_indexes; +static s_path *path_struct = NULL; /** - * Allocates the the path indexes in memory and sets them all to 0 with a count of 1. + * Get the field pointer to by the first N depths of the path. + * + * @param[out] fields_count_ptr the number of fields in the last evaluated depth + * @param[in] depth_count the number of depths to evaluate (N) + * @return the feld which the first Nth depths points to */ -bool init_path(void) +static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, + uint8_t depth_count) { - // + 1 for the used index count - if ((path_indexes = mem_alloc(sizeof(uint8_t) * (MAX_PATH_DEPTH + 1))) != NULL) + const void *struct_ptr = path_struct->root_struct; + const void *field_ptr = NULL; + const char *typename; + uint8_t length; + uint8_t fields_count; + + if (depth_count > path_struct->depth_count) // sanity check { - // set all to 0 - explicit_bzero(path_indexes + 1, sizeof(uint8_t) * MAX_PATH_DEPTH); - *path_indexes = 1; // init count at 1, so the default path will be 0 + return NULL; } - return path_indexes != NULL; + for (uint8_t depth = 0; depth < depth_count; ++depth) + { + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + + if (fields_count_ptr != NULL) + { + *fields_count_ptr = fields_count; + } + // check if the index at this depth makes sense + if (path_struct->depths[depth] > fields_count) + { + return NULL; + } + + for (uint8_t index = 0; index < path_struct->depths[depth]; ++index) + { + field_ptr = get_next_struct_field(field_ptr); + } + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + typename = get_struct_field_typename(field_ptr, &length); + if ((struct_ptr = get_structn(structs_array, typename, length)) == NULL) + { + return NULL; + } + } + } + return field_ptr; +} + +/** + * Get the element the path is pointing to. (internal) + * + * @param[out] the number of fields in the depth of the returned field + * @return the field which the path points to + */ +static inline const void *get_field_from_path(uint8_t *const fields_count) +{ + return get_nth_field_from_path(fields_count, path_struct->depth_count); +} + +/** + * Get the element the path is pointing to. (public facing) + * + * @return the field which the path points to + */ +const void *path_get_field(void) +{ + return get_field_from_path(NULL); +} + +/** + * Go down (add) a depth level. + * + * @return whether the push was succesful + */ +static bool path_depth_list_push(void) +{ + if (path_struct->depth_count == MAX_PATH_DEPTH) + { + return false; + } + path_struct->depths[path_struct->depth_count] = 0; + path_struct->depth_count += 1; + return true; +} + +/** + * Go up (remove) a depth level. + * + * @return whether the pop was successful + */ +static bool path_depth_list_pop(void) +{ + if (path_struct->depth_count == 0) + { + return false; + } + path_struct->depth_count -= 1; + return true; +} + +/** + * Go down (add) an array depth level. + * + * @param[in] path_idx the index in the path list + * @param[in] the number of elements contained in that depth + * @return whether the push was successful + */ +static bool array_depth_list_push(uint8_t path_idx, uint8_t size) +{ + s_array_depth *arr = &path_struct->array_depths[path_struct->array_depth_count]; + + if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) + { + return false; + } + arr->path_index = path_idx; + arr->size = size; + path_struct->array_depth_count += 1; + return true; +} + +/** + * Go up (remove) an array depth level. + * + * @return whether the pop was successful + */ +static bool array_depth_list_pop(void) +{ + if (path_struct->array_depth_count == 0) + { + return false; + } + path_struct->array_depth_count -= 1; + return true; +} + +/** + * Updates the path so that it doesn't point to a struct-type field, but rather + * only to actual fields. + * + * @return whether the path update worked or not + */ +static bool path_update(void) +{ + uint8_t fields_count; + const void *struct_ptr = path_struct->root_struct; + const void *field_ptr; + const char *typename; + uint8_t typename_len; + + if ((field_ptr = get_field_from_path(NULL)) == NULL) + { + return false; + } + while (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + // TODO: calculate the type hash here + typename = get_struct_field_typename(field_ptr, &typename_len); + if ((struct_ptr = get_structn(structs_array, typename, typename_len)) == NULL) + { + return false; + } + if ((field_ptr = get_struct_fields_array(struct_ptr, &fields_count)) == NULL) + { + return false; + } + path_depth_list_push(); + } + return true; +} + +/** + * Set a new struct as the path root type + * + * @param[in] struct_name the root struct name + * @param[in] name_length the root struct name length + * @return boolean indicating if it was successful or not + */ +bool path_set_root(const char *const struct_name, uint8_t name_length) +{ + if (path_struct == NULL) + { + return false; + } + + path_struct->root_struct = get_structn(structs_array, struct_name, name_length); + + if (path_struct->root_struct == NULL) + { + return false; + } + + // init depth, at 0 : empty path + path_struct->depth_count = 0; + path_depth_list_push(); + + // init array levels at 0 + path_struct->array_depth_count = 0; + + // because the first field could be a struct type + path_update(); + return true; +} + +/** + * Checks the new array depth and adds it to the list + * + * @param[in] depth pointer to the array depth definition + * @param[in] total_count number of array depth contained down to this array depth + * @param[in] pidx path index + * @param[in] size requested array depth size + * @return whether the checks and add were successful or not + */ +static bool check_and_add_array_depth(const void *depth, + uint8_t total_count, + uint8_t pidx, + uint8_t size) +{ + uint8_t expected_size; + uint8_t arr_idx = (total_count - path_struct->array_depth_count) - 1; + e_array_type expected_type; + + // we skip index 0, since we already have it + for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) + { + if ((depth = get_next_struct_field_array_lvl(depth)) == NULL) + { + return false; + } + } + expected_type = struct_field_array_depth(depth, &expected_size); + if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) + { + printf("Unexpected array depth size. (expected %d, got %d)\n", + expected_size, size); + return false; + } + // add it + if (!array_depth_list_push(pidx, size)) + { + return false; + } + return true; +} + +/** + * Add a new array depth with a given size (number of elements). + * + * @return whether the add was successful or not + */ +bool path_new_array_depth(uint8_t size) +{ + const void *field_ptr, *depth; + uint8_t depth_count; + uint8_t total_count = 0; + uint8_t pidx; + + if (path_struct == NULL) // sanity check + { + printf("NULL struct check failed\n"); + return false; + } + + for (pidx = 0; pidx < path_struct->depth_count; ++pidx) + { + if ((field_ptr = get_nth_field_from_path(NULL, pidx + 1)) == NULL) + { + return false; + } + if (struct_field_is_array(field_ptr)) + { + if ((depth = get_struct_field_array_lvls_array(field_ptr, &depth_count)) == NULL) + { + return false; + } + total_count += depth_count; + if (total_count > path_struct->array_depth_count) + { + if (!check_and_add_array_depth(depth, total_count, pidx, size)) + { + return false; + } + break; + } + } + } + + if (pidx == path_struct->depth_count) + { + printf("Did not find a matching array type.\n"); + return false; + } + return true; +} + +/** + * Advance within the struct that contains the field the path points to. + * + * @return whether the end of the struct has been reached. + */ +static bool path_advance_in_struct(void) +{ + bool end_reached = true; + uint8_t *depth = &path_struct->depths[path_struct->depth_count - 1]; + uint8_t fields_count; + + if ((get_field_from_path(&fields_count)) == NULL) + { + return false; + } + if (path_struct->depth_count > 0) + { + *depth += 1; + end_reached = (*depth == fields_count); + } + if (end_reached) + { + path_depth_list_pop(); + } + return end_reached; +} + +/** + * Advance within the array levels of the current field the path points to. + * + * @return whether the end of the array levels has been reached. + */ +static bool path_advance_in_array(void) +{ + bool end_reached; + s_array_depth *arr_depth; + + do + { + end_reached = false; + arr_depth = &path_struct->array_depths[path_struct->array_depth_count - 1]; + + if ((path_struct->array_depth_count > 0) && + (arr_depth->path_index == (path_struct->depth_count - 1))) + { + arr_depth->size -= 1; + if (arr_depth->size == 0) + { + array_depth_list_pop(); + end_reached = true; + } + else + { + return false; + } + } + } + while (end_reached); + return true; +} + +/** + * Updates the path to point to the next field in order (DFS). + * + * @return whether the advancement was successful or not + */ +bool path_advance(void) +{ + bool end_reached; + + do + { + if (path_advance_in_array()) + { + end_reached = path_advance_in_struct(); + } + else + { + end_reached = false; + } + } + while (end_reached); + path_update(); + return true; +} + +/** + * Allocates the the path indexes in memory and sets it with a depth of 0. + * + * @return whether the memory allocation were successful. + */ +bool path_init(void) +{ + if (path_struct == NULL) + { + path_struct = mem_alloc(sizeof(*path_struct)); + } + return path_struct != NULL; } diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 8eda5ba..3be4c5f 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -1,12 +1,31 @@ #ifndef PATH_H_ #define PATH_H_ +#include #include #define MAX_PATH_DEPTH 16 +#define MAX_ARRAY_DEPTH 4 -extern uint8_t *path_indexes; +typedef struct __attribute__((packed)) +{ + uint8_t path_index; + uint8_t size; +} s_array_depth; -bool init_path(void); +typedef struct __attribute__((packed)) +{ + const void *root_struct; + uint8_t depth_count; + uint8_t depths[MAX_PATH_DEPTH]; + uint8_t array_depth_count; + s_array_depth array_depths[MAX_ARRAY_DEPTH]; +} s_path; + +bool path_set_root(const char *const struct_name, uint8_t length); +const void *path_get_field(void); +bool path_advance(void); +bool path_init(void); +bool path_new_array_depth(uint8_t size); #endif // PATH_H_ From 477046bd43250bad25157ae91d06f878dcad765a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 13 Apr 2022 17:02:33 +0200 Subject: [PATCH 034/184] Removed unused byte type --- src_features/signMessageEIP712/eip712.h | 22 ------------------- .../signMessageEIP712/sol_typenames.c | 8 +++---- 2 files changed, 3 insertions(+), 27 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index c77b182..7b17f8f 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -23,7 +23,6 @@ typedef enum TYPE_SOL_ADDRESS, TYPE_SOL_BOOL, TYPE_SOL_STRING, - TYPE_SOL_BYTE, TYPE_SOL_BYTES_FIX, TYPE_SOL_BYTES_DYN, TYPES_COUNT @@ -68,27 +67,6 @@ typedef enum #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) -typedef struct -{ - uint16_t length; - char *str; -} t_string; - -typedef struct -{ - uint16_t size; - uint8_t *ptr; -} t_array; - -typedef struct -{ - t_string type; - t_string key; - uint8_t bytesize; - t_array array_levels; -} t_struct_field; - - // TODO: Move these into a new file const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_struct_fields_array(const uint8_t *ptr, diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 09f8d2c..25c704f 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -46,8 +46,7 @@ bool init_sol_typenames(void) "address", // 2 "bool", // 3 "string", // 4 - "byte", // 5 - "bytes" // 6 + "bytes" // 5 }; // \ref TYPES_COUNT - 1 since we don't include \ref TYPE_CUSTOM const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT] = { @@ -56,9 +55,8 @@ bool init_sol_typenames(void) { TYPE_SOL_ADDRESS, 2 }, { TYPE_SOL_BOOL, 3 }, { TYPE_SOL_STRING, 4 }, - { TYPE_SOL_BYTE, 5 }, - { TYPE_SOL_BYTES_FIX, 6 }, - { TYPE_SOL_BYTES_DYN, 6 } + { TYPE_SOL_BYTES_FIX, 5 }, + { TYPE_SOL_BYTES_DYN, 5 } }; uint8_t *typename_len_ptr; char *typename_ptr; From 0e9cd91ecc95d8c54d7ec03a2d503a98499de190 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 14 Apr 2022 15:25:38 +0200 Subject: [PATCH 035/184] Removed structure packing, prevents unaligned access --- src_features/signMessageEIP712/path.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 3be4c5f..f9e1885 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -7,19 +7,19 @@ #define MAX_PATH_DEPTH 16 #define MAX_ARRAY_DEPTH 4 -typedef struct __attribute__((packed)) +typedef struct { uint8_t path_index; uint8_t size; } s_array_depth; -typedef struct __attribute__((packed)) +typedef struct { - const void *root_struct; uint8_t depth_count; uint8_t depths[MAX_PATH_DEPTH]; uint8_t array_depth_count; s_array_depth array_depths[MAX_ARRAY_DEPTH]; + const void *root_struct; } s_path; bool path_set_root(const char *const struct_name, uint8_t length); From a3f5eee116213d05fc3e3283b00a38d1646ea5fa Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 14 Apr 2022 18:22:35 +0200 Subject: [PATCH 036/184] WIP field hashing support --- src_features/signMessageEIP712/encode_field.c | 84 ++--------- src_features/signMessageEIP712/encode_field.h | 8 +- src_features/signMessageEIP712/entrypoint.c | 6 +- src_features/signMessageEIP712/field_hash.c | 132 ++++++++++++++++-- src_features/signMessageEIP712/field_hash.h | 21 ++- 5 files changed, 160 insertions(+), 91 deletions(-) diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 0f670d2..7eeb4b8 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -7,56 +7,13 @@ /** - * Hash field value + * Encode a field value to 32 bytes * - * @param[in] value pointer to value - * @param[in] length its bytelength - * @param[in] dealloc if the value length should be deallocated from the memory + * @param[in] value field value to encode + * @param[in] length field length before encoding + * @return encoded field value */ -static void *hash_field_value(const void *const value, uint16_t length, bool dealloc) -{ - uint8_t *hash_ptr = NULL; - - if (value != NULL) - { - cx_keccak_init((cx_hash_t*)&global_sha3, 256); - cx_hash((cx_hash_t*)&global_sha3, - 0, - (uint8_t*)value, - length, - NULL, - 0); - - if (dealloc) - { - // restore the memory location - mem_dealloc(length); - } - - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) - { - return NULL; - } - - // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, - 0, - hash_ptr, - KECCAK256_HASH_BYTESIZE); - } - return hash_ptr; -} - -/** - * Encode an integer and hash it - * - * @param[in] value pointer to the "packed" integer received - * @param[in] length its byte-length - * @return the encoded (hashed) value - */ -void *encode_integer(const uint8_t *const value, uint16_t length) +static void *field_encode(const uint8_t *const value, uint8_t length) { uint8_t *padded_value; @@ -73,29 +30,30 @@ void *encode_integer(const uint8_t *const value, uint16_t length) padded_value[EIP_712_ENCODED_FIELD_LENGTH - (length - idx)] = value[idx]; } } - return hash_field_value(padded_value, EIP_712_ENCODED_FIELD_LENGTH, true); + return padded_value; } /** - * Encode a string and hash it + * Encode an integer * - * @param[in] value pointer to the string received + * @param[in] value pointer to the "packed" integer received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_string(const char *const value, uint16_t length) +void *encode_integer(const uint8_t *const value, uint8_t length) { - return hash_field_value(value, length, false); + // no length check here since it will be checked by field_encode + return field_encode(value, length); } /** - * Encode a boolean and hash it + * Encode a boolean * * @param[in] value pointer to the boolean received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_bool(const bool *const value, uint16_t length) +void *encode_boolean(const bool *const value, uint8_t length) { if (length != 1) // sanity check { @@ -105,13 +63,13 @@ void *encode_bool(const bool *const value, uint16_t length) } /** - * Encode an address and hash it + * Encode an address * * @param[in] value pointer to the address received * @param[in] length its byte-length * @return the encoded (hashed) value */ -void *encode_address(const uint8_t *const value, uint16_t length) +void *encode_address(const uint8_t *const value, uint8_t length) { if (length != ADDRESS_LENGTH) // sanity check { @@ -119,15 +77,3 @@ void *encode_address(const uint8_t *const value, uint16_t length) } return encode_integer(value, length); } - -/** - * Encode bytes and hash it - * - * @param[in] value pointer to the bytes received - * @param[in] length its byte-length - * @return the encoded (hashed) value - */ -void *encode_bytes(const uint8_t *const value, uint16_t length) -{ - return hash_field_value(value, length, false); -} diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index eea2429..bcdaf2a 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -6,10 +6,8 @@ #define EIP_712_ENCODED_FIELD_LENGTH 32 -void *encode_integer(const uint8_t *const value, uint16_t length); -void *encode_string(const char *const value, uint16_t length); -void *encode_bool(const bool *const value, uint16_t length); -void *encode_address(const uint8_t *const value, uint16_t length); -void *encode_bytes(const uint8_t *const value, uint16_t length); +void *encode_integer(const uint8_t *const value, uint8_t length); +void *encode_boolean(const bool *const value, uint8_t length); +void *encode_address(const uint8_t *const value, uint8_t length); #endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 40d42c9..7444d7e 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -386,7 +386,11 @@ bool handle_apdu(const uint8_t *const data) type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: - field_hash(structs_array, &data[OFFSET_DATA], data[OFFSET_LC]); + if ((data[OFFSET_P1] != P1_COMPLETE) && (data[OFFSET_P1] != P1_PARTIAL)) + { + return false; + } + field_hash(&data[OFFSET_DATA], data[OFFSET_LC], data[OFFSET_P1] == P1_PARTIAL); break; case P2_ARRAY: path_new_array_depth(data[OFFSET_DATA]); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 5cbf791..24361b7 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -3,34 +3,140 @@ #include "field_hash.h" #include "encode_field.h" #include "path.h" +#include "mem.h" #include "eip712.h" +#include "shared_context.h" -const uint8_t *field_hash(const void *const structs_array, - const uint8_t *const data, - const uint8_t data_length) +static s_field_hashing *fh = NULL; + +static bool field_hash_init(void) { + if (fh == NULL) + { + if ((fh = mem_alloc(sizeof(*fh))) == NULL) + { + return false; + } + fh->state = FHS_IDLE; + } + return true; +} + +const uint8_t *field_hash(const uint8_t *data, + uint8_t data_length, + bool partial) +{ + const void *field_ptr; const char *type; uint8_t typelen; const char *key; uint8_t keylen; - const void *field; + e_type field_type; + uint8_t *hash_ptr = NULL; - (void)structs_array; (void)data; - (void)data_length; - // get field by path - //encode_integer(data, data_length); - field = path_get_field(); - if (field != NULL) + if ((fh == NULL) && (field_hash_init() == false)) { + return NULL; + } + // get field by path + if ((field_ptr = path_get_field()) == NULL) + { + return NULL; + } + field_type = struct_field_type(field_ptr); + if (fh->state == FHS_IDLE) // first packet for this frame + { + fh->remaining_size = (data[0] << 8) | data[1]; // network byte order + data += sizeof(uint16_t); + data_length -= sizeof(uint16_t); + fh->state = FHS_WAITING_FOR_MORE; + cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + } + fh->remaining_size -= data_length; + // if a dynamic type -> continue progressive hash + if (IS_DYN(field_type)) + { + cx_hash((cx_hash_t*)&global_sha3, + 0, + data, + data_length, + NULL, + 0); + } + if (fh->remaining_size == 0) + { + if (partial) // only makes sense if marked as complete + { + return NULL; + } printf("==> "); - type = get_struct_field_typename(field, &typelen); + type = get_struct_field_typename(field_ptr, &typelen); fwrite(type, sizeof(char), typelen, stdout); printf(" "); - key = get_struct_field_keyname(field, &keylen); + key = get_struct_field_keyname(field_ptr, &keylen); fwrite(key, sizeof(char), keylen, stdout); printf("\n"); + + uint8_t *value = NULL; + + if (!IS_DYN(field_type)) + { + switch (field_type) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + case TYPE_SOL_BYTES_FIX: + value = encode_integer(data, data_length); + break; + case TYPE_SOL_ADDRESS: + value = encode_address(data, data_length); + break; + case TYPE_SOL_BOOL: + value = encode_boolean((bool*)data, data_length); + break; + case TYPE_CUSTOM: + default: + printf("Unknown solidity type!\n"); + return NULL; + } + + if (value == NULL) + { + return NULL; + } + cx_hash((cx_hash_t*)&global_sha3, + 0, + (uint8_t*)value, + EIP_712_ENCODED_FIELD_LENGTH, + NULL, + 0); + + // restore the memory location + mem_dealloc(EIP_712_ENCODED_FIELD_LENGTH); + } + + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + { + return NULL; + } + // copy hash into memory + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + hash_ptr, + KECCAK256_HASH_BYTESIZE); + path_advance(); + fh->state = FHS_IDLE; } - return NULL; + else + { + if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial + { + return NULL; + } + } + return hash_ptr; } diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index 9b2a2ac..251f5a7 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -2,8 +2,23 @@ #define FIELD_HASH_H_ #include +#include -const uint8_t *field_hash(const void *const structs_array, - const uint8_t *const data, - const uint8_t data_length); +#define IS_DYN(type) (((type) == TYPE_SOL_STRING) || ((type) == TYPE_SOL_BYTES_DYN)) + +typedef enum +{ + FHS_IDLE, + FHS_WAITING_FOR_MORE +} e_field_hashing_state; + +typedef struct +{ + uint16_t remaining_size; + uint8_t state; // e_field_hashing_state +} s_field_hashing; + +const uint8_t *field_hash(const uint8_t *data, + uint8_t data_length, + bool partial); #endif // FIELD_HASH_H_ From 149e20cd11fdc83b34ee23738cfe99d330d15d31 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 26 Apr 2022 15:08:59 +0200 Subject: [PATCH 037/184] Typehash refactoring --- src_features/signMessageEIP712/encode_type.c | 72 +++++++++----------- src_features/signMessageEIP712/entrypoint.c | 2 +- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c index 3a229b5..e65a664 100644 --- a/src_features/signMessageEIP712/encode_type.c +++ b/src_features/signMessageEIP712/encode_type.c @@ -169,37 +169,37 @@ static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_ * * @param[in] structs_array pointer to structs array * @param[in] deps_count count of how many struct dependencies pointers - * @param[in,out] dep pointer to the first dependency pointer + * @param[in,out] deps pointer to the first dependency pointer */ -static void sort_dependencies(const uint8_t *const deps_count, - void **dep) +static void sort_dependencies(uint8_t deps_count, + void **deps) { - bool changed = false; + bool changed; void *tmp_ptr; const char *name1, *name2; uint8_t namelen1, namelen2; int str_cmp_result; - for (size_t idx = 0; (idx + 1) < *deps_count; ++idx) + do { - name1 = get_struct_name(*(dep + idx), &namelen1); - name2 = get_struct_name(*(dep + idx + 1), &namelen2); - - str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); - if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) + changed = false; + for (size_t idx = 0; (idx + 1) < deps_count; ++idx) { - tmp_ptr = *(dep + idx); - *(dep + idx) = *(dep + idx + 1); - *(dep + idx + 1) = tmp_ptr; + name1 = get_struct_name(*(deps + idx), &namelen1); + name2 = get_struct_name(*(deps + idx + 1), &namelen2); - changed = true; + str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); + if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) + { + tmp_ptr = *(deps + idx); + *(deps + idx) = *(deps + idx + 1); + *(deps + idx + 1) = tmp_ptr; + + changed = true; + } } } - // recurse until it is sorted - if (changed) - { - sort_dependencies(deps_count, dep); - } + while (changed); } /** @@ -207,14 +207,14 @@ static void sort_dependencies(const uint8_t *const deps_count, * * @param[in] structs_array pointer to structs array * @param[out] deps_count count of how many struct dependencie pointers - * @param[in] dep pointer to the first dependency pointer + * @param[in] deps pointer to the first dependency pointer * @param[in] struct_ptr pointer to the struct we are getting the dependencies of * @return \ref false in case of a memory allocation error, \ref true otherwise */ static bool get_struct_dependencies(const void *const structs_array, - uint8_t *const deps_count, - void **dep, - const void *const struct_ptr) + uint8_t *const deps_count, + void *const *const deps, + const void *const struct_ptr) { uint8_t fields_count; const void *field_ptr; @@ -238,7 +238,7 @@ static bool get_struct_dependencies(const void *const structs_array, for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) { // it's a match! - if (*(dep + dep_idx) == arg_struct_ptr) + if (*(deps + dep_idx) == arg_struct_ptr) { break; } @@ -252,7 +252,7 @@ static bool get_struct_dependencies(const void *const structs_array, } *new_dep = arg_struct_ptr; *deps_count += 1; - get_struct_dependencies(structs_array, deps_count, dep, arg_struct_ptr); + get_struct_dependencies(structs_array, deps_count, deps, arg_struct_ptr); } } field_ptr = get_next_struct_field(field_ptr); @@ -277,32 +277,28 @@ const char *encode_type(const void *const structs_array, const void *const struct_ptr = get_structn(structs_array, struct_name, struct_name_length); - uint8_t *deps_count; - void **dep; + uint8_t deps_count; + void **deps; uint16_t length; const char *typestr; *encoded_length = 0; - if ((deps_count = mem_alloc(sizeof(uint8_t))) == NULL) - { - return NULL; - } - *deps_count = 0; + deps_count = 0; // get list of structs (own + dependencies), properly ordered - dep = (void**)(deps_count + 1); // get first elem - if (get_struct_dependencies(structs_array, deps_count, dep, struct_ptr) == false) + deps = mem_alloc(0); // get where the first elem will be + if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) { return NULL; } - sort_dependencies(deps_count, dep); + sort_dependencies(deps_count, deps); typestr = format_struct_string(struct_ptr, &length); *encoded_length += length; // loop over each struct and generate string - for (int idx = 0; idx < *deps_count; ++idx) + for (int idx = 0; idx < deps_count; ++idx) { - format_struct_string(*dep, &length); + format_struct_string(*deps, &length); *encoded_length += length; - dep += 1; + deps += 1; } return typestr; diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 7444d7e..25b67e5 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -381,9 +381,9 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: + type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); // set root type path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); - type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; case P2_FIELD: if ((data[OFFSET_P1] != P1_COMPLETE) && (data[OFFSET_P1] != P1_PARTIAL)) From 01ba4fb41ffbb6a5de7ca2637e5d06d439ba3ebe Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 26 Apr 2022 18:15:53 +0200 Subject: [PATCH 038/184] Small refactoring to the format uint memory utils function --- src_features/signMessageEIP712/encode_type.c | 6 ++-- src_features/signMessageEIP712/encode_type.h | 1 + src_features/signMessageEIP712/mem_utils.c | 32 ++++++++++++++------ src_features/signMessageEIP712/mem_utils.h | 7 +++-- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c index e65a664..1422407 100644 --- a/src_features/signMessageEIP712/encode_type.c +++ b/src_features/signMessageEIP712/encode_type.c @@ -29,8 +29,7 @@ static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t l case ARRAY_DYNAMIC: break; case ARRAY_FIXED_SIZE: - // max value = 255, 3 characters max - mem_alloc_and_format_uint(array_size, 3); + mem_alloc_and_format_uint(array_size, NULL); break; default: // should not be in here :^) @@ -81,8 +80,7 @@ static bool format_field_string(const void *field_ptr) // should not be in here :^) break; } - // max value = 256, 3 characters max - mem_alloc_and_format_uint(field_size, 3); + mem_alloc_and_format_uint(field_size, NULL); } // field type array levels diff --git a/src_features/signMessageEIP712/encode_type.h b/src_features/signMessageEIP712/encode_type.h index f7ed216..9271cd4 100644 --- a/src_features/signMessageEIP712/encode_type.h +++ b/src_features/signMessageEIP712/encode_type.h @@ -2,6 +2,7 @@ #define ENCODE_TYPE_H_ #include +#include const char *encode_type(const void *const structs_array, const char *const struct_name, diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index 0220fb0..0c5583f 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -24,20 +24,34 @@ char *mem_alloc_and_copy_char(char c) * Format an unsigned number up to 32-bit into memory into an ASCII string. * * @param[in] value Value to write in memory - * @param[in] max_chars Maximum number of characters that could be written + * @param[out] length number of characters written to memory * - * @return how many characters have been written in memory, 0 in case of an allocation error + * @return pointer to memory area or \ref NULL if the allocated failed */ -uint8_t mem_alloc_and_format_uint(uint32_t value, const uint8_t max_chars) +char *mem_alloc_and_format_uint(uint32_t value, + uint8_t *const length) { - char *ptr; - uint8_t written_chars; + char *mem_ptr; + uint32_t value_copy; + uint8_t size; - if ((ptr = mem_alloc(sizeof(char) * max_chars)) == NULL) + size = 1; // minimum size, even if 0 + value_copy = value; + while (value_copy >= 10) + { + value_copy /= 10; + size += 1; + } + // +1 for the null character + if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1))) == NULL) { return 0; } - written_chars = sprintf(ptr, "%u", value); - mem_dealloc(max_chars - written_chars); // in case it ended up being less - return written_chars; + snprintf(mem_ptr, (size + 1), "%u", value); + mem_dealloc(sizeof(char)); // to skip the null character + if (length != NULL) + { + *length = size; + } + return mem_ptr; } diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h index dad38d6..a86684c 100644 --- a/src_features/signMessageEIP712/mem_utils.h +++ b/src_features/signMessageEIP712/mem_utils.h @@ -4,8 +4,9 @@ #include #include -char *mem_alloc_and_copy_char(char c); -void *mem_alloc_and_copy(const void *data, size_t size); -uint8_t mem_alloc_and_format_uint(uint32_t value, const uint8_t max_chars); +char *mem_alloc_and_copy_char(char c); +void *mem_alloc_and_copy(const void *data, size_t size); +char *mem_alloc_and_format_uint(uint32_t value, + uint8_t *const written_chars); #endif // MEM_UTILS_H_ From 854791324adf23bd630b802fb570b6e591a47415 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 27 Apr 2022 15:30:29 +0200 Subject: [PATCH 039/184] Doubled the available RAM size --- src_features/signMessageEIP712/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index 17b98b2..f3ed4aa 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -5,7 +5,7 @@ #include "mem.h" -#define SIZE_MEM_BUFFER 1024 +#define SIZE_MEM_BUFFER 2048 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; From 83dda443f4cbfdb9e02c6580133562c77c71a886 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 15:30:41 +0200 Subject: [PATCH 040/184] Refactor type hash function to use less RAM --- src_features/signMessageEIP712/encode_type.c | 303 ----------------- src_features/signMessageEIP712/encode_type.h | 12 - src_features/signMessageEIP712/entrypoint.c | 2 +- src_features/signMessageEIP712/type_hash.c | 329 +++++++++++++++++-- src_features/signMessageEIP712/type_hash.h | 3 +- 5 files changed, 301 insertions(+), 348 deletions(-) delete mode 100644 src_features/signMessageEIP712/encode_type.c delete mode 100644 src_features/signMessageEIP712/encode_type.h diff --git a/src_features/signMessageEIP712/encode_type.c b/src_features/signMessageEIP712/encode_type.c deleted file mode 100644 index 1422407..0000000 --- a/src_features/signMessageEIP712/encode_type.c +++ /dev/null @@ -1,303 +0,0 @@ -#include -#include -#include -#include -#include "mem.h" -#include "mem_utils.h" -#include "eip712.h" -#include "encode_type.h" - -/** - * - * @param[in] lvl_ptr pointer to the first array level of a struct field - * @param[in] lvls_count the number of array levels the struct field contains - * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) - */ -static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t lvls_count) -{ - uint8_t array_size; - - while (lvls_count-- > 0) - { - if (mem_alloc_and_copy_char('[') == NULL) - { - return false; - } - - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - mem_alloc_and_format_uint(array_size, NULL); - break; - default: - // should not be in here :^) - break; - } - if (mem_alloc_and_copy_char(']') == NULL) - { - return false; - } - lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); - } - return true; -} - -/** - * - * @param[in] field_ptr pointer to the struct field - * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) - */ -static bool format_field_string(const void *field_ptr) -{ - const char *name; - uint8_t length; - uint16_t field_size; - uint8_t lvls_count; - const uint8_t *lvl_ptr; - - // field type name - name = get_struct_field_typename(field_ptr, &length); - if (mem_alloc_and_copy(name, length) == NULL) - { - return false; - } - - // field type size - if (struct_field_has_typesize(field_ptr)) - { - field_size = get_struct_field_typesize(field_ptr); - switch (struct_field_type(field_ptr)) - { - case TYPE_SOL_INT: - case TYPE_SOL_UINT: - field_size *= 8; // bytes -> bits - break; - case TYPE_SOL_BYTES_FIX: - break; - default: - // should not be in here :^) - break; - } - mem_alloc_and_format_uint(field_size, NULL); - } - - // field type array levels - if (struct_field_is_array(field_ptr)) - { - lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - format_field_type_array_levels_string(lvl_ptr, lvls_count); - } - // space between field type name and field name - if (mem_alloc_and_copy_char(' ') == NULL) - { - return false; - } - - // field name - name = get_struct_field_keyname(field_ptr, &length); - if (mem_alloc_and_copy(name, length) == NULL) - { - return false; - } - return true; -} - -/** - * - * @param[in] struct_ptr pointer to the structure we want the typestring of - * @param[in] str_length length of the formatted string in memory - * @return pointer of the string in memory, \ref NULL in case of an error - */ -static const char *format_struct_string(const uint8_t *const struct_ptr, uint16_t *const str_length) -{ - const char *str_start; - const char *struct_name; - uint8_t struct_name_length; - const uint8_t *field_ptr; - uint8_t fields_count; - - // struct name - struct_name = get_struct_name(struct_ptr, &struct_name_length); - if ((str_start = mem_alloc_and_copy(struct_name, struct_name_length)) == NULL) - { - return NULL; - } - - // opening struct parenthese - if (mem_alloc_and_copy_char('(') == NULL) - { - return NULL; - } - - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { - // comma separating struct fields - if (idx > 0) - { - if (mem_alloc_and_copy_char(',') == NULL) - { - return NULL; - } - } - - if (format_field_string(field_ptr) == false) - { - return NULL; - } - - - field_ptr = get_next_struct_field(field_ptr); - } - // closing struct parenthese - if (mem_alloc_and_copy_char(')') == NULL) - { - return NULL; - } - - // compute the length - *str_length = ((char*)mem_alloc(0) - str_start); - return str_start; -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[in] deps_count count of how many struct dependencies pointers - * @param[in,out] deps pointer to the first dependency pointer - */ -static void sort_dependencies(uint8_t deps_count, - void **deps) -{ - bool changed; - void *tmp_ptr; - const char *name1, *name2; - uint8_t namelen1, namelen2; - int str_cmp_result; - - do - { - changed = false; - for (size_t idx = 0; (idx + 1) < deps_count; ++idx) - { - name1 = get_struct_name(*(deps + idx), &namelen1); - name2 = get_struct_name(*(deps + idx + 1), &namelen2); - - str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); - if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) - { - tmp_ptr = *(deps + idx); - *(deps + idx) = *(deps + idx + 1); - *(deps + idx + 1) = tmp_ptr; - - changed = true; - } - } - } - while (changed); -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[out] deps_count count of how many struct dependencie pointers - * @param[in] deps pointer to the first dependency pointer - * @param[in] struct_ptr pointer to the struct we are getting the dependencies of - * @return \ref false in case of a memory allocation error, \ref true otherwise - */ -static bool get_struct_dependencies(const void *const structs_array, - uint8_t *const deps_count, - void *const *const deps, - const void *const struct_ptr) -{ - uint8_t fields_count; - const void *field_ptr; - const char *arg_structname; - uint8_t arg_structname_length; - const void *arg_struct_ptr; - size_t dep_idx; - const void **new_dep; - - field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { - // get struct name - arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); - // from its name, get the pointer to its definition - arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); - - // check if it is not already present in the dependencies array - for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) - { - // it's a match! - if (*(deps + dep_idx) == arg_struct_ptr) - { - break; - } - } - // if it's not present in the array, add it and recurse into it - if (dep_idx == *deps_count) - { - if ((new_dep = mem_alloc(sizeof(void*))) == NULL) - { - return false; - } - *new_dep = arg_struct_ptr; - *deps_count += 1; - get_struct_dependencies(structs_array, deps_count, deps, arg_struct_ptr); - } - } - field_ptr = get_next_struct_field(field_ptr); - } - return true; -} - -/** - * - * - * @param[in] structs_array pointer to structs array - * @param[in] struct_name name of the given struct - * @param[in] struct_name_length length of the name of the given struct - * @param[out] encoded_length length of the returned string - * @return pointer to encoded string or \ref NULL in case of a memory allocation error - */ -const char *encode_type(const void *const structs_array, - const char *const struct_name, - const uint8_t struct_name_length, - uint16_t *const encoded_length) -{ - const void *const struct_ptr = get_structn(structs_array, - struct_name, - struct_name_length); - uint8_t deps_count; - void **deps; - uint16_t length; - const char *typestr; - - *encoded_length = 0; - deps_count = 0; - // get list of structs (own + dependencies), properly ordered - deps = mem_alloc(0); // get where the first elem will be - if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) - { - return NULL; - } - sort_dependencies(deps_count, deps); - typestr = format_struct_string(struct_ptr, &length); - *encoded_length += length; - // loop over each struct and generate string - for (int idx = 0; idx < deps_count; ++idx) - { - format_struct_string(*deps, &length); - *encoded_length += length; - deps += 1; - } - - return typestr; -} diff --git a/src_features/signMessageEIP712/encode_type.h b/src_features/signMessageEIP712/encode_type.h deleted file mode 100644 index 9271cd4..0000000 --- a/src_features/signMessageEIP712/encode_type.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef ENCODE_TYPE_H_ -#define ENCODE_TYPE_H_ - -#include -#include - -const char *encode_type(const void *const structs_array, - const char *const struct_name, - const uint8_t struct_name_length, - uint16_t *const encoded_length); - -#endif // ENCODE_TYPE_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 25b67e5..332fa64 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -381,7 +381,7 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: - type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC]); + type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC], true); // set root type path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index fdd46c7..eda0f75 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -1,63 +1,330 @@ #include +#include +#include #include -#include "eip712.h" #include "mem.h" -#include "encode_type.h" +#include "mem_utils.h" +#include "eip712.h" #include "type_hash.h" #include "shared_context.h" -const uint8_t *type_hash(const void *const structs_array, - const char *const struct_name, - const uint8_t struct_name_length) +static inline void hash_nbytes(const uint8_t *b, uint8_t n) { - const void *const mem_loc_bak = mem_alloc(0); // backup the memory location - const char *typestr; - uint16_t length; - uint8_t *hash_ptr; - - typestr = encode_type(structs_array, struct_name, struct_name_length, &length); - if (typestr == NULL) +#ifdef DEBUG + for (int i = 0; i < n; ++i) { - return NULL; + printf("%c", b[i]); } - cx_keccak_init((cx_hash_t*)&global_sha3, 256); +#endif cx_hash((cx_hash_t*)&global_sha3, 0, - (uint8_t*)typestr, - length, + b, + n, NULL, 0); +} -#ifdef DEBUG - // Print type string - fwrite(typestr, sizeof(char), length, stdout); - printf("\n"); -#endif +static inline void hash_byte(uint8_t b) +{ + hash_nbytes(&b, 1); +} - // restore the memory location - mem_dealloc(mem_alloc(0) - mem_loc_bak); +/** + * + * @param[in] lvl_ptr pointer to the first array level of a struct field + * @param[in] lvls_count the number of array levels the struct field contains + * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) + */ +static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t lvls_count) +{ + uint8_t array_size; + char *uint_str_ptr; + uint8_t uint_str_len; - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE + 1)) == NULL) + while (lvls_count-- > 0) + { + hash_byte('['); + + switch (struct_field_array_depth(lvl_ptr, &array_size)) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len); + hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len); + mem_dealloc(uint_str_len); + break; + default: + // should not be in here :^) + break; + } + hash_byte(']'); + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); + } + return true; +} + +/** + * + * @param[in] field_ptr pointer to the struct field + * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) + */ +static bool encode_and_hash_field(const void *field_ptr) +{ + const char *name; + uint8_t length; + uint16_t field_size; + uint8_t lvls_count; + const uint8_t *lvl_ptr; + char *uint_str_ptr; + uint8_t uint_str_len; + + // field type name + name = get_struct_field_typename(field_ptr, &length); + hash_nbytes((uint8_t*)name, length); + + // field type size + if (struct_field_has_typesize(field_ptr)) + { + field_size = get_struct_field_typesize(field_ptr); + switch (struct_field_type(field_ptr)) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + field_size *= 8; // bytes -> bits + break; + case TYPE_SOL_BYTES_FIX: + break; + default: + // should not be in here :^) + break; + } + uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len); + hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len); + mem_dealloc(uint_str_len); + } + + // field type array levels + if (struct_field_is_array(field_ptr)) + { + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + format_field_type_array_levels_string(lvl_ptr, lvls_count); + } + // space between field type name and field name + hash_byte(' '); + + // field name + name = get_struct_field_keyname(field_ptr, &length); + hash_nbytes((uint8_t*)name, length); + return true; +} + +/** + * + * @param[in] struct_ptr pointer to the structure we want the typestring of + * @param[in] str_length length of the formatted string in memory + * @return pointer of the string in memory, \ref NULL in case of an error + */ +static bool encode_and_hash_type(const uint8_t *const struct_ptr) +{ + const char *struct_name; + uint8_t struct_name_length; + const uint8_t *field_ptr; + uint8_t fields_count; + + // struct name + struct_name = get_struct_name(struct_ptr, &struct_name_length); + hash_nbytes((uint8_t*)struct_name, struct_name_length); + + // opening struct parenthese + hash_byte('('); + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) + { + // comma separating struct fields + if (idx > 0) + { + hash_byte(','); + } + + if (encode_and_hash_field(field_ptr) == false) + { + return NULL; + } + + field_ptr = get_next_struct_field(field_ptr); + } + // closing struct parenthese + hash_byte(')'); + + return true; +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] deps_count count of how many struct dependencies pointers + * @param[in,out] deps pointer to the first dependency pointer + */ +static void sort_dependencies(uint8_t deps_count, + void **deps) +{ + bool changed; + void *tmp_ptr; + const char *name1, *name2; + uint8_t namelen1, namelen2; + int str_cmp_result; + + do + { + changed = false; + for (size_t idx = 0; (idx + 1) < deps_count; ++idx) + { + name1 = get_struct_name(*(deps + idx), &namelen1); + name2 = get_struct_name(*(deps + idx + 1), &namelen2); + + str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); + if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) + { + tmp_ptr = *(deps + idx); + *(deps + idx) = *(deps + idx + 1); + *(deps + idx + 1) = tmp_ptr; + + changed = true; + } + } + } + while (changed); +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[out] deps_count count of how many struct dependencie pointers + * @param[in] deps pointer to the first dependency pointer + * @param[in] struct_ptr pointer to the struct we are getting the dependencies of + * @return \ref false in case of a memory allocation error, \ref true otherwise + */ +static bool get_struct_dependencies(const void *const structs_array, + uint8_t *const deps_count, + void *const *const deps, + const void *const struct_ptr) +{ + uint8_t fields_count; + const void *field_ptr; + const char *arg_structname; + uint8_t arg_structname_length; + const void *arg_struct_ptr; + size_t dep_idx; + const void **new_dep; + + field_ptr = get_struct_fields_array(struct_ptr, &fields_count); + for (uint8_t idx = 0; idx < fields_count; ++idx) + { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + // get struct name + arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); + // from its name, get the pointer to its definition + arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); + + // check if it is not already present in the dependencies array + for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) + { + // it's a match! + if (*(deps + dep_idx) == arg_struct_ptr) + { + break; + } + } + // if it's not present in the array, add it and recurse into it + if (dep_idx == *deps_count) + { + if ((new_dep = mem_alloc(sizeof(void*))) == NULL) + { + return false; + } + *new_dep = arg_struct_ptr; + *deps_count += 1; + get_struct_dependencies(structs_array, deps_count, deps, arg_struct_ptr); + } + } + field_ptr = get_next_struct_field(field_ptr); + } + return true; +} + +/** + * + * + * @param[in] structs_array pointer to structs array + * @param[in] struct_name name of the given struct + * @param[in] struct_name_length length of the name of the given struct + * @param[in] with_deps if hashed typestring should include struct dependencies + * @return pointer to encoded string or \ref NULL in case of a memory allocation error + */ +const uint8_t *type_hash(const void *const structs_array, + const char *const struct_name, + const uint8_t struct_name_length, + bool with_deps) +{ + const void *const struct_ptr = get_structn(structs_array, + struct_name, + struct_name_length); + uint8_t deps_count; + void **deps; + uint8_t *hash_ptr; + + cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + if (with_deps) + { + deps_count = 0; + // get list of structs (own + dependencies), properly ordered + deps = mem_alloc(0); // get where the first elem will be + if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) + { + return NULL; + } + sort_dependencies(deps_count, deps); + } + if (encode_and_hash_type(struct_ptr) == false) + { + return NULL; + } + if (with_deps) + { + // loop over each struct and generate string + for (int idx = 0; idx < deps_count; ++idx) + { + encode_and_hash_type(*deps); + deps += 1; + } + mem_dealloc(sizeof(void*) * deps_count); + } +#ifdef DEBUG + printf("\n"); +#endif + // End progressive hashing + if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { return NULL; } - - // set TypeHash marker - *hash_ptr = EIP712_TYPE_HASH; - // copy hash into memory cx_hash((cx_hash_t*)&global_sha3, CX_LAST, NULL, 0, - hash_ptr + 1, + hash_ptr, KECCAK256_HASH_BYTESIZE); #ifdef DEBUG // print computed hash - printf("-> 0x"); + printf("new -> 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { - printf("%.02x", (hash_ptr + 1)[idx]); + printf("%.02x", hash_ptr[idx]); } printf("\n"); #endif diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index e40ba7a..bd7cbef 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -5,6 +5,7 @@ const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, - const uint8_t struct_name_length); + const uint8_t struct_name_length, + bool with_deps); #endif // TYPE_HASH_H_ From a2a72edf830d11dd770fc1d4c4fb199d5bc9e3eb Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 28 Apr 2022 16:47:48 +0200 Subject: [PATCH 041/184] Now initializes field hash struct in eip712 context --- src_features/signMessageEIP712/context.c | 6 ++++++ src_features/signMessageEIP712/field_hash.c | 4 ++-- src_features/signMessageEIP712/field_hash.h | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 56e5d36..de53326 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -5,6 +5,7 @@ #include "mem.h" #include "sol_typenames.h" #include "path.h" +#include "field_hash.h" uint8_t *typenames_array; uint8_t *structs_array; @@ -29,6 +30,11 @@ bool init_eip712_context(void) return false; } + if (field_hash_init() == false) + { + return false; + } + // set types pointer if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) { diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 24361b7..285be28 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -9,7 +9,7 @@ static s_field_hashing *fh = NULL; -static bool field_hash_init(void) +bool field_hash_init(void) { if (fh == NULL) { @@ -35,7 +35,7 @@ const uint8_t *field_hash(const uint8_t *data, uint8_t *hash_ptr = NULL; (void)data; - if ((fh == NULL) && (field_hash_init() == false)) + if (fh == NULL) { return NULL; } diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index 251f5a7..1d0e276 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -18,6 +18,7 @@ typedef struct uint8_t state; // e_field_hashing_state } s_field_hashing; +bool field_hash_init(void); const uint8_t *field_hash(const uint8_t *data, uint8_t data_length, bool partial); From 6d86a5de7706cbd77256dbaa64921708b6534289 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 11:04:59 +0200 Subject: [PATCH 042/184] Global hash making --- src_features/signMessageEIP712/entrypoint.c | 4 +- src_features/signMessageEIP712/field_hash.c | 60 +++++---- src_features/signMessageEIP712/mem.c | 2 +- src_features/signMessageEIP712/path.c | 138 +++++++++++++++++++- 4 files changed, 176 insertions(+), 28 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 332fa64..5aff3e2 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -260,6 +260,7 @@ bool set_struct_name(const uint8_t *const data) } // TODO: Split this function +// TODO: Handle partial sends bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_DATA; @@ -381,7 +382,6 @@ bool handle_apdu(const uint8_t *const data) switch (data[OFFSET_P2]) { case P2_NAME: - type_hash(structs_array, (char*)&data[OFFSET_DATA], data[OFFSET_LC], true); // set root type path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); break; @@ -449,7 +449,7 @@ int main(void) } } #ifdef DEBUG - printf("\n%lu bytes used in RAM\n", (mem_max + 1)); + printf("%lu bytes used in RAM\n", (mem_max + 1)); #endif return EXIT_SUCCESS; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 285be28..1d5c365 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -32,7 +32,8 @@ const uint8_t *field_hash(const uint8_t *data, const char *key; uint8_t keylen; e_type field_type; - uint8_t *hash_ptr = NULL; + uint8_t *value = NULL; + (void)data; if (fh == NULL) @@ -51,7 +52,10 @@ const uint8_t *field_hash(const uint8_t *data, data += sizeof(uint16_t); data_length -= sizeof(uint16_t); fh->state = FHS_WAITING_FOR_MORE; - cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + if (IS_DYN(field_type)) + { + cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + } } fh->remaining_size -= data_length; // if a dynamic type -> continue progressive hash @@ -78,8 +82,6 @@ const uint8_t *field_hash(const uint8_t *data, fwrite(key, sizeof(char), keylen, stdout); printf("\n"); - uint8_t *value = NULL; - if (!IS_DYN(field_type)) { switch (field_type) @@ -105,28 +107,39 @@ const uint8_t *field_hash(const uint8_t *data, { return NULL; } - cx_hash((cx_hash_t*)&global_sha3, - 0, - (uint8_t*)value, - EIP_712_ENCODED_FIELD_LENGTH, - NULL, - 0); - - // restore the memory location - mem_dealloc(EIP_712_ENCODED_FIELD_LENGTH); } - - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + else { - return NULL; + if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + { + return NULL; + } + // copy hash into memory + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + value, + KECCAK256_HASH_BYTESIZE); } - // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, + + // TODO: Move elsewhere + uint8_t len = IS_DYN(field_type) ? + KECCAK256_HASH_BYTESIZE : + EIP_712_ENCODED_FIELD_LENGTH; + // last thing in mem is the hash of the previous field + // and just before it is the current hash context + cx_sha3_t *hash_ctx = (cx_sha3_t*)(value - sizeof(cx_sha3_t)); + // start the progressive hash on it + cx_hash((cx_hash_t*)hash_ctx, 0, - hash_ptr, - KECCAK256_HASH_BYTESIZE); + value, + len, + NULL, + 0); + // deallocate it + mem_dealloc(len); + printf("FEED %d\n", len); path_advance(); fh->state = FHS_IDLE; @@ -138,5 +151,6 @@ const uint8_t *field_hash(const uint8_t *data, return NULL; } } - return hash_ptr; + + return value; } diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index f3ed4aa..655d84a 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -5,7 +5,7 @@ #include "mem.h" -#define SIZE_MEM_BUFFER 2048 +#define SIZE_MEM_BUFFER 8192 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 7427896..9dbabfa 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -5,6 +5,8 @@ #include "mem.h" #include "context.h" #include "eip712.h" +#include "type_hash.h" +#include "shared_context.h" static s_path *path_struct = NULL; @@ -107,6 +109,43 @@ static bool path_depth_list_pop(void) return false; } path_struct->depth_count -= 1; + + // TODO: Move elsewhere + uint8_t shash[KECCAK256_HASH_BYTESIZE]; + cx_sha3_t *hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); + // finalize hash + cx_hash((cx_hash_t*)hash_ctx, + CX_LAST, + NULL, + 0, + &shash[0], + KECCAK256_HASH_BYTESIZE); + mem_dealloc(sizeof(cx_sha3_t)); // remove hash context +#ifdef DEBUG + // print computed hash + printf("SHASH POP 0x"); + for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) + { + printf("%.02x", shash[idx]); + } + printf("\n"); +#endif + if (path_struct->depth_count > 0) + { + hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); // previous one + // continue progressive hash with the array hash + cx_hash((cx_hash_t*)hash_ctx, + 0, + &shash[0], + KECCAK256_HASH_BYTESIZE, + NULL, + 0); + } + else + { + printf("\n"); + } + return true; } @@ -125,6 +164,7 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) { return false; } + arr->path_index = path_idx; arr->size = size; path_struct->array_depth_count += 1; @@ -142,6 +182,28 @@ static bool array_depth_list_pop(void) { return false; } + + // TODO: Move elsewhere + uint8_t ahash[KECCAK256_HASH_BYTESIZE]; + cx_sha3_t *hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); + // finalize hash + cx_hash((cx_hash_t*)hash_ctx, + CX_LAST, + NULL, + 0, + &ahash[0], + KECCAK256_HASH_BYTESIZE); + mem_dealloc(sizeof(cx_sha3_t)); // remove hash context + hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); // previous one + // continue progressive hash with the array hash + cx_hash((cx_hash_t*)hash_ctx, + 0, + &ahash[0], + KECCAK256_HASH_BYTESIZE, + NULL, + 0); + printf("AHASH POP\n"); + path_struct->array_depth_count -= 1; return true; } @@ -166,7 +228,6 @@ static bool path_update(void) } while (struct_field_type(field_ptr) == TYPE_CUSTOM) { - // TODO: calculate the type hash here typename = get_struct_field_typename(field_ptr, &typename_len); if ((struct_ptr = get_structn(structs_array, typename, typename_len)) == NULL) { @@ -176,6 +237,33 @@ static bool path_update(void) { return false; } + + // TODO: Move elsewhere + cx_sha3_t *hash_ctx; + const uint8_t *thash_ptr; + + // allocate new hash context + if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + { + return false; + } + cx_keccak_init((cx_hash_t*)hash_ctx, 256); // initialize it + // get the struct typehash + if ((thash_ptr = type_hash(structs_array, typename, typename_len, true)) == NULL) + { + return false; + } + // start the progressive hash on it + cx_hash((cx_hash_t*)hash_ctx, + 0, + thash_ptr, + KECCAK256_HASH_BYTESIZE, + NULL, + 0); + // deallocate it + mem_dealloc(KECCAK256_HASH_BYTESIZE); + printf("SHASH PUSH w/o deps %p\n", hash_ctx); + path_depth_list_push(); } return true; @@ -202,6 +290,30 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } + // TODO: Move elsewhere + cx_sha3_t *hash_ctx; + const uint8_t *thash_ptr; + if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + { + return false; + } + cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash + if ((thash_ptr = type_hash(structs_array, struct_name, name_length, true)) == NULL) + { + return false; + } + // start the progressive hash on it + cx_hash((cx_hash_t*)hash_ctx, + 0, + thash_ptr, + KECCAK256_HASH_BYTESIZE, + NULL, + 0); + // deallocate it + mem_dealloc(KECCAK256_HASH_BYTESIZE); + printf("SHASH PUSH w/ deps %p\n", hash_ctx); + // + // init depth, at 0 : empty path path_struct->depth_count = 0; path_depth_list_push(); @@ -262,7 +374,8 @@ static bool check_and_add_array_depth(const void *depth, */ bool path_new_array_depth(uint8_t size) { - const void *field_ptr, *depth; + const void *field_ptr = NULL; + const void *depth = NULL; uint8_t depth_count; uint8_t total_count = 0; uint8_t pidx; @@ -302,6 +415,27 @@ bool path_new_array_depth(uint8_t size) printf("Did not find a matching array type.\n"); return false; } + // TODO: Move elsewhere + cx_sha3_t *hash_ctx; + if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + { + return false; + } + printf("AHASH PUSH %p", hash_ctx); + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + cx_sha3_t *old_ctx = (void*)hash_ctx - sizeof(cx_sha3_t); + + memcpy(hash_ctx, old_ctx, sizeof(cx_sha3_t)); + cx_keccak_init((cx_hash_t*)old_ctx, 256); // init hash + printf(" (switched)"); + } + else // solidity type + { + cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash + } + printf("\n"); + return true; } From 09697f718a05977ea7001d066f9b4d65e4e83ea0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 11:05:25 +0200 Subject: [PATCH 043/184] Fix dynamic bytes type encoding --- src_features/signMessageEIP712/encode_field.c | 47 +++++++++++++++---- src_features/signMessageEIP712/encode_field.h | 1 + src_features/signMessageEIP712/field_hash.c | 4 +- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 7eeb4b8..fea2a26 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -5,17 +5,23 @@ #include "eip712.h" #include "shared_context.h" +typedef enum +{ + MSB, + LSB +} e_padding_type; /** - * Encode a field value to 32 bytes + * Encode a field value to 32 bytes (0 padded) * * @param[in] value field value to encode * @param[in] length field length before encoding * @return encoded field value */ -static void *field_encode(const uint8_t *const value, uint8_t length) +static void *field_encode(const uint8_t *const value, uint8_t length, e_padding_type ptype) { uint8_t *padded_value; + uint8_t start_idx; if (length > 32) // sanity check { @@ -24,10 +30,22 @@ static void *field_encode(const uint8_t *const value, uint8_t length) // 0-pad the value to 32 bytes if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) { - explicit_bzero(padded_value, EIP_712_ENCODED_FIELD_LENGTH - length); + switch (ptype) + { + case MSB: + explicit_bzero(padded_value, EIP_712_ENCODED_FIELD_LENGTH - length); + start_idx = EIP_712_ENCODED_FIELD_LENGTH - length; + break; + case LSB: + explicit_bzero(padded_value + length, EIP_712_ENCODED_FIELD_LENGTH - length); + start_idx = 0; + break; + default: + return NULL; // should not be here + } for (uint8_t idx = 0; idx < length; ++idx) { - padded_value[EIP_712_ENCODED_FIELD_LENGTH - (length - idx)] = value[idx]; + padded_value[start_idx + idx] = value[idx]; } } return padded_value; @@ -38,12 +56,25 @@ static void *field_encode(const uint8_t *const value, uint8_t length) * * @param[in] value pointer to the "packed" integer received * @param[in] length its byte-length - * @return the encoded (hashed) value + * @return the encoded value */ void *encode_integer(const uint8_t *const value, uint8_t length) { // no length check here since it will be checked by field_encode - return field_encode(value, length); + return field_encode(value, length, MSB); +} + +/** + * Encode a fixed-size byte array + * + * @param[in] value pointer to the "packed" bytes array + * @param[in] length its byte-length + * @return the encoded value + */ +void *encode_bytes(const uint8_t *const value, uint8_t length) +{ + // no length check here since it will be checked by field_encode + return field_encode(value, length, LSB); } /** @@ -51,7 +82,7 @@ void *encode_integer(const uint8_t *const value, uint8_t length) * * @param[in] value pointer to the boolean received * @param[in] length its byte-length - * @return the encoded (hashed) value + * @return the encoded value */ void *encode_boolean(const bool *const value, uint8_t length) { @@ -67,7 +98,7 @@ void *encode_boolean(const bool *const value, uint8_t length) * * @param[in] value pointer to the address received * @param[in] length its byte-length - * @return the encoded (hashed) value + * @return the encoded value */ void *encode_address(const uint8_t *const value, uint8_t length) { diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index bcdaf2a..acc925c 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -9,5 +9,6 @@ void *encode_integer(const uint8_t *const value, uint8_t length); void *encode_boolean(const bool *const value, uint8_t length); void *encode_address(const uint8_t *const value, uint8_t length); +void *encode_bytes(const uint8_t *const value, uint8_t length); #endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 1d5c365..0996118 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -88,9 +88,11 @@ const uint8_t *field_hash(const uint8_t *data, { case TYPE_SOL_INT: case TYPE_SOL_UINT: - case TYPE_SOL_BYTES_FIX: value = encode_integer(data, data_length); break; + case TYPE_SOL_BYTES_FIX: + value = encode_bytes(data, data_length); + break; case TYPE_SOL_ADDRESS: value = encode_address(data, data_length); break; From 6c14bfa476febf572ec4816f54642049041341ed Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 17:11:28 +0200 Subject: [PATCH 044/184] Now wraps the printf calls in a macro. SDK-like --- src_features/signMessageEIP712/entrypoint.c | 12 ++++----- src_features/signMessageEIP712/field_hash.c | 11 ++++----- src_features/signMessageEIP712/mem.c | 6 ++--- src_features/signMessageEIP712/path.c | 27 ++++++++++----------- src_features/signMessageEIP712/type_hash.c | 11 ++++----- 5 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 5aff3e2..1fddbee 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -11,6 +10,7 @@ #include "sol_typenames.h" #include "field_hash.h" #include "path.h" +#include "shared_context.h" // lib functions @@ -374,7 +374,7 @@ bool handle_apdu(const uint8_t *const data) set_struct_field(data); break; default: - printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); + PRINTF("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); return false; } break; @@ -396,12 +396,12 @@ bool handle_apdu(const uint8_t *const data) path_new_array_depth(data[OFFSET_DATA]); break; default: - printf("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); + PRINTF("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); return false; } break; default: - printf("Unrecognized APDU (0x%.02x)\n", data[OFFSET_INS]); + PRINTF("Unrecognized APDU (0x%.02x)\n", data[OFFSET_INS]); return false; } return true; @@ -444,12 +444,12 @@ int main(void) else idx += 1; break; default: - printf("Unexpected APDU state!\n"); + PRINTF("Unexpected APDU state!\n"); return EXIT_FAILURE; } } #ifdef DEBUG - printf("%lu bytes used in RAM\n", (mem_max + 1)); + PRINTF("%lu bytes used in RAM\n", (mem_max + 1)); #endif return EXIT_SUCCESS; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 0996118..25e7dbf 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -1,4 +1,3 @@ -#include #include #include "field_hash.h" #include "encode_field.h" @@ -74,13 +73,13 @@ const uint8_t *field_hash(const uint8_t *data, { return NULL; } - printf("==> "); + PRINTF("==> "); type = get_struct_field_typename(field_ptr, &typelen); fwrite(type, sizeof(char), typelen, stdout); - printf(" "); + PRINTF(" "); key = get_struct_field_keyname(field_ptr, &keylen); fwrite(key, sizeof(char), keylen, stdout); - printf("\n"); + PRINTF("\n"); if (!IS_DYN(field_type)) { @@ -101,7 +100,7 @@ const uint8_t *field_hash(const uint8_t *data, break; case TYPE_CUSTOM: default: - printf("Unknown solidity type!\n"); + PRINTF("Unknown solidity type!\n"); return NULL; } @@ -141,7 +140,7 @@ const uint8_t *field_hash(const uint8_t *data, 0); // deallocate it mem_dealloc(len); - printf("FEED %d\n", len); + PRINTF("FEED %d\n", len); path_advance(); fh->state = FHS_IDLE; diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index 655d84a..799be1f 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -1,8 +1,6 @@ #include -#ifdef DEBUG -#include -#endif #include "mem.h" +#include "shared_context.h" #define SIZE_MEM_BUFFER 8192 @@ -46,7 +44,7 @@ void *mem_alloc(size_t size) if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded { #ifdef DEBUG - printf("Memory exhausted!\n"); + PRINTF("Memory exhausted!\n"); #endif return NULL; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 9dbabfa..c681193 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -1,6 +1,5 @@ #include #include -#include #include "path.h" #include "mem.h" #include "context.h" @@ -123,12 +122,12 @@ static bool path_depth_list_pop(void) mem_dealloc(sizeof(cx_sha3_t)); // remove hash context #ifdef DEBUG // print computed hash - printf("SHASH POP 0x"); + PRINTF("SHASH POP 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { - printf("%.02x", shash[idx]); + PRINTF("%.02x", shash[idx]); } - printf("\n"); + PRINTF("\n"); #endif if (path_struct->depth_count > 0) { @@ -143,7 +142,7 @@ static bool path_depth_list_pop(void) } else { - printf("\n"); + PRINTF("\n"); } return true; @@ -202,7 +201,7 @@ static bool array_depth_list_pop(void) KECCAK256_HASH_BYTESIZE, NULL, 0); - printf("AHASH POP\n"); + PRINTF("AHASH POP\n"); path_struct->array_depth_count -= 1; return true; @@ -262,7 +261,7 @@ static bool path_update(void) 0); // deallocate it mem_dealloc(KECCAK256_HASH_BYTESIZE); - printf("SHASH PUSH w/o deps %p\n", hash_ctx); + PRINTF("SHASH PUSH w/o deps %p\n", hash_ctx); path_depth_list_push(); } @@ -311,7 +310,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) 0); // deallocate it mem_dealloc(KECCAK256_HASH_BYTESIZE); - printf("SHASH PUSH w/ deps %p\n", hash_ctx); + PRINTF("SHASH PUSH w/ deps %p\n", hash_ctx); // // init depth, at 0 : empty path @@ -355,7 +354,7 @@ static bool check_and_add_array_depth(const void *depth, expected_type = struct_field_array_depth(depth, &expected_size); if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) { - printf("Unexpected array depth size. (expected %d, got %d)\n", + PRINTF("Unexpected array depth size. (expected %d, got %d)\n", expected_size, size); return false; } @@ -382,7 +381,7 @@ bool path_new_array_depth(uint8_t size) if (path_struct == NULL) // sanity check { - printf("NULL struct check failed\n"); + PRINTF("NULL struct check failed\n"); return false; } @@ -412,7 +411,7 @@ bool path_new_array_depth(uint8_t size) if (pidx == path_struct->depth_count) { - printf("Did not find a matching array type.\n"); + PRINTF("Did not find a matching array type.\n"); return false; } // TODO: Move elsewhere @@ -421,20 +420,20 @@ bool path_new_array_depth(uint8_t size) { return false; } - printf("AHASH PUSH %p", hash_ctx); + PRINTF("AHASH PUSH %p", hash_ctx); if (struct_field_type(field_ptr) == TYPE_CUSTOM) { cx_sha3_t *old_ctx = (void*)hash_ctx - sizeof(cx_sha3_t); memcpy(hash_ctx, old_ctx, sizeof(cx_sha3_t)); cx_keccak_init((cx_hash_t*)old_ctx, 256); // init hash - printf(" (switched)"); + PRINTF(" (switched)"); } else // solidity type { cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash } - printf("\n"); + PRINTF("\n"); return true; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index eda0f75..4c6e6ec 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -1,7 +1,6 @@ #include #include #include -#include #include "mem.h" #include "mem_utils.h" #include "eip712.h" @@ -13,7 +12,7 @@ static inline void hash_nbytes(const uint8_t *b, uint8_t n) #ifdef DEBUG for (int i = 0; i < n; ++i) { - printf("%c", b[i]); + PRINTF("%c", b[i]); } #endif cx_hash((cx_hash_t*)&global_sha3, @@ -305,7 +304,7 @@ const uint8_t *type_hash(const void *const structs_array, mem_dealloc(sizeof(void*) * deps_count); } #ifdef DEBUG - printf("\n"); + PRINTF("\n"); #endif // End progressive hashing if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) @@ -321,12 +320,12 @@ const uint8_t *type_hash(const void *const structs_array, KECCAK256_HASH_BYTESIZE); #ifdef DEBUG // print computed hash - printf("new -> 0x"); + PRINTF("new -> 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { - printf("%.02x", hash_ptr[idx]); + PRINTF("%.02x", hash_ptr[idx]); } - printf("\n"); + PRINTF("\n"); #endif return hash_ptr; } From ad030cdb6a04ac38b2597ce53f10f5053d59c463 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 17:27:24 +0200 Subject: [PATCH 045/184] Make the dependencies mandatory in the typehash function --- src_features/signMessageEIP712/path.c | 4 +-- src_features/signMessageEIP712/type_hash.c | 32 ++++++++-------------- src_features/signMessageEIP712/type_hash.h | 3 +- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index c681193..8da2b9c 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -248,7 +248,7 @@ static bool path_update(void) } cx_keccak_init((cx_hash_t*)hash_ctx, 256); // initialize it // get the struct typehash - if ((thash_ptr = type_hash(structs_array, typename, typename_len, true)) == NULL) + if ((thash_ptr = type_hash(structs_array, typename, typename_len)) == NULL) { return false; } @@ -297,7 +297,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash - if ((thash_ptr = type_hash(structs_array, struct_name, name_length, true)) == NULL) + if ((thash_ptr = type_hash(structs_array, struct_name, name_length)) == NULL) { return false; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 4c6e6ec..2eeaf01 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -267,42 +267,34 @@ static bool get_struct_dependencies(const void *const structs_array, */ const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, - const uint8_t struct_name_length, - bool with_deps) + const uint8_t struct_name_length) { const void *const struct_ptr = get_structn(structs_array, struct_name, struct_name_length); - uint8_t deps_count; + uint8_t deps_count = 0; void **deps; uint8_t *hash_ptr; cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash - if (with_deps) + // get list of structs (own + dependencies), properly ordered + deps = mem_alloc(0); // get where the first elem will be + if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) { - deps_count = 0; - // get list of structs (own + dependencies), properly ordered - deps = mem_alloc(0); // get where the first elem will be - if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) - { - return NULL; - } - sort_dependencies(deps_count, deps); + return NULL; } + sort_dependencies(deps_count, deps); if (encode_and_hash_type(struct_ptr) == false) { return NULL; } - if (with_deps) + // loop over each struct and generate string + for (int idx = 0; idx < deps_count; ++idx) { - // loop over each struct and generate string - for (int idx = 0; idx < deps_count; ++idx) - { - encode_and_hash_type(*deps); - deps += 1; - } - mem_dealloc(sizeof(void*) * deps_count); + encode_and_hash_type(*deps); + deps += 1; } + mem_dealloc(sizeof(void*) * deps_count); #ifdef DEBUG PRINTF("\n"); #endif diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index bd7cbef..e40ba7a 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -5,7 +5,6 @@ const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, - const uint8_t struct_name_length, - bool with_deps); + const uint8_t struct_name_length); #endif // TYPE_HASH_H_ From c689b68f76061b515fcfbdb42f0883e231e0ceae Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 17:32:00 +0200 Subject: [PATCH 046/184] Remove some now useless debug outputs --- src_features/signMessageEIP712/field_hash.c | 11 ++++++---- src_features/signMessageEIP712/path.c | 24 +++++++-------------- src_features/signMessageEIP712/type_hash.c | 9 -------- 3 files changed, 15 insertions(+), 29 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 25e7dbf..54f93b6 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -26,12 +26,14 @@ const uint8_t *field_hash(const uint8_t *data, bool partial) { const void *field_ptr; + e_type field_type; + uint8_t *value = NULL; +#ifdef DEBUG const char *type; uint8_t typelen; const char *key; uint8_t keylen; - e_type field_type; - uint8_t *value = NULL; +#endif (void)data; @@ -73,13 +75,15 @@ const uint8_t *field_hash(const uint8_t *data, { return NULL; } - PRINTF("==> "); +#ifdef DEBUG + PRINTF("=> "); type = get_struct_field_typename(field_ptr, &typelen); fwrite(type, sizeof(char), typelen, stdout); PRINTF(" "); key = get_struct_field_keyname(field_ptr, &keylen); fwrite(key, sizeof(char), keylen, stdout); PRINTF("\n"); +#endif if (!IS_DYN(field_type)) { @@ -140,7 +144,6 @@ const uint8_t *field_hash(const uint8_t *data, 0); // deallocate it mem_dealloc(len); - PRINTF("FEED %d\n", len); path_advance(); fh->state = FHS_IDLE; diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 8da2b9c..54641db 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -120,15 +120,6 @@ static bool path_depth_list_pop(void) &shash[0], KECCAK256_HASH_BYTESIZE); mem_dealloc(sizeof(cx_sha3_t)); // remove hash context -#ifdef DEBUG - // print computed hash - PRINTF("SHASH POP 0x"); - for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) - { - PRINTF("%.02x", shash[idx]); - } - PRINTF("\n"); -#endif if (path_struct->depth_count > 0) { hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); // previous one @@ -140,10 +131,17 @@ static bool path_depth_list_pop(void) NULL, 0); } +#ifdef DEBUG else { - PRINTF("\n"); + PRINTF("Hash = 0x"); + for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) + { + PRINTF("%.02x", shash[idx]); + } + PRINTF("\n\n"); } +#endif return true; } @@ -201,7 +199,6 @@ static bool array_depth_list_pop(void) KECCAK256_HASH_BYTESIZE, NULL, 0); - PRINTF("AHASH POP\n"); path_struct->array_depth_count -= 1; return true; @@ -261,7 +258,6 @@ static bool path_update(void) 0); // deallocate it mem_dealloc(KECCAK256_HASH_BYTESIZE); - PRINTF("SHASH PUSH w/o deps %p\n", hash_ctx); path_depth_list_push(); } @@ -310,7 +306,6 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) 0); // deallocate it mem_dealloc(KECCAK256_HASH_BYTESIZE); - PRINTF("SHASH PUSH w/ deps %p\n", hash_ctx); // // init depth, at 0 : empty path @@ -420,20 +415,17 @@ bool path_new_array_depth(uint8_t size) { return false; } - PRINTF("AHASH PUSH %p", hash_ctx); if (struct_field_type(field_ptr) == TYPE_CUSTOM) { cx_sha3_t *old_ctx = (void*)hash_ctx - sizeof(cx_sha3_t); memcpy(hash_ctx, old_ctx, sizeof(cx_sha3_t)); cx_keccak_init((cx_hash_t*)old_ctx, 256); // init hash - PRINTF(" (switched)"); } else // solidity type { cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash } - PRINTF("\n"); return true; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 2eeaf01..37708b7 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -310,14 +310,5 @@ const uint8_t *type_hash(const void *const structs_array, 0, hash_ptr, KECCAK256_HASH_BYTESIZE); -#ifdef DEBUG - // print computed hash - PRINTF("new -> 0x"); - for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) - { - PRINTF("%.02x", hash_ptr[idx]); - } - PRINTF("\n"); -#endif return hash_ptr; } From d333b3a43342f7ff666a787b38cc33df97806c9c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 2 May 2022 18:28:02 +0200 Subject: [PATCH 047/184] Fixed compilation errors/warnings --- src_features/signMessageEIP712/eip712.h | 3 --- src_features/signMessageEIP712/entrypoint.c | 2 ++ src_features/signMessageEIP712/field_hash.c | 2 +- src_features/signMessageEIP712/path.c | 8 ++++---- src_features/signMessageEIP712/type_hash.c | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 7b17f8f..7846841 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -34,9 +34,6 @@ typedef enum ARRAY_FIXED_SIZE } e_array_type; -#define MIN(a,b) ((a > b) ? b : a) -#define MAX(a,b) ((a > b) ? a : b) - // APDUs INS #define INS_STRUCT_DEF 0x18 #define INS_STRUCT_IMPL 0x1A diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 1fddbee..6cb7504 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -407,6 +407,7 @@ bool handle_apdu(const uint8_t *const data) return true; } +#if 0 int main(void) { uint8_t buf[260]; // 4 bytes APDU header + 256 bytes payload @@ -453,3 +454,4 @@ int main(void) #endif return EXIT_SUCCESS; } +#endif diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 54f93b6..6221631 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -55,7 +55,7 @@ const uint8_t *field_hash(const uint8_t *data, fh->state = FHS_WAITING_FOR_MORE; if (IS_DYN(field_type)) { - cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + cx_keccak_init(&global_sha3, 256); // init hash } } fh->remaining_size -= data_length; diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 54641db..fd8c54e 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -243,7 +243,7 @@ static bool path_update(void) { return false; } - cx_keccak_init((cx_hash_t*)hash_ctx, 256); // initialize it + cx_keccak_init(hash_ctx, 256); // initialize it // get the struct typehash if ((thash_ptr = type_hash(structs_array, typename, typename_len)) == NULL) { @@ -292,7 +292,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) { return false; } - cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash + cx_keccak_init(hash_ctx, 256); // init hash if ((thash_ptr = type_hash(structs_array, struct_name, name_length)) == NULL) { return false; @@ -420,11 +420,11 @@ bool path_new_array_depth(uint8_t size) cx_sha3_t *old_ctx = (void*)hash_ctx - sizeof(cx_sha3_t); memcpy(hash_ctx, old_ctx, sizeof(cx_sha3_t)); - cx_keccak_init((cx_hash_t*)old_ctx, 256); // init hash + cx_keccak_init(old_ctx, 256); // init hash } else // solidity type { - cx_keccak_init((cx_hash_t*)hash_ctx, 256); // init hash + cx_keccak_init(hash_ctx, 256); // init hash } return true; diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 37708b7..8c1b0b4 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -276,7 +276,7 @@ const uint8_t *type_hash(const void *const structs_array, void **deps; uint8_t *hash_ptr; - cx_keccak_init((cx_hash_t*)&global_sha3, 256); // init hash + cx_keccak_init(&global_sha3, 256); // init hash // get list of structs (own + dependencies), properly ordered deps = mem_alloc(0); // get where the first elem will be if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) From e070c379da6a5d0ccf70118066512a279c2edddf Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 12 May 2022 17:27:51 +0200 Subject: [PATCH 048/184] Removed old code main function & apdu handling function --- src_features/signMessageEIP712/entrypoint.c | 49 --------------------- 1 file changed, 49 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 6cb7504..269f2e9 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -406,52 +406,3 @@ bool handle_apdu(const uint8_t *const data) } return true; } - -#if 0 -int main(void) -{ - uint8_t buf[260]; // 4 bytes APDU header + 256 bytes payload - uint16_t idx; - int state; - uint8_t payload_size = 0; - - init_eip712_context(); - - state = OFFSET_CLA; - idx = 0; - while (fread(&buf[idx], sizeof(buf[idx]), 1, stdin) > 0) - { - switch (state) - { - case OFFSET_CLA: - case OFFSET_INS: - case OFFSET_P1: - case OFFSET_P2: - state += 1; - idx += 1; - break; - case OFFSET_LC: - payload_size = buf[idx]; - state = OFFSET_DATA; - idx += 1; - break; - case OFFSET_DATA: - if (--payload_size == 0) - { - if (!handle_apdu(buf)) return false; - state = OFFSET_CLA; - idx = 0; - } - else idx += 1; - break; - default: - PRINTF("Unexpected APDU state!\n"); - return EXIT_FAILURE; - } - } -#ifdef DEBUG - PRINTF("%lu bytes used in RAM\n", (mem_max + 1)); -#endif - return EXIT_SUCCESS; -} -#endif From 9f8b2da74cff86489c92e391fb04be4230194150 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 12 May 2022 17:28:40 +0200 Subject: [PATCH 049/184] WIP integration in app --- src/apdu_constants.h | 5 ++ src/main.c | 10 +++ src_features/signMessageEIP712/entrypoint.c | 80 +++++++++++---------- 3 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 1626178..a9cceb4 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -21,6 +21,8 @@ #define INS_PROVIDE_NFT_INFORMATION 0x14 #define INS_SET_PLUGIN 0x16 #define INS_PERFORM_PRIVACY_OPERATION 0x18 +#define INS_EIP712_STRUCT_DEF 0x1A +#define INS_EIP712_STRUCT_IMPL 0x1C #define P1_CONFIRM 0x01 #define P1_NON_CONFIRM 0x00 #define P2_NO_CHAINCODE 0x00 @@ -168,4 +170,7 @@ void handleStarkwareUnsafeSign(uint8_t p1, #endif +bool handle_eip712_struct_def(const uint8_t *const apdu_buf); +bool handle_eip712_struct_impl(const uint8_t *const apdu_buf); + #endif // _APDU_CONSTANTS_H_ diff --git a/src/main.c b/src/main.c index a02a08a..279a9ac 100644 --- a/src/main.c +++ b/src/main.c @@ -29,6 +29,7 @@ #include "handle_swap_sign_transaction.h" #include "handle_get_printable_amount.h" #include "handle_check_address.h" +#include "mem.h" #ifdef HAVE_STARKWARE #include "stark_crypto.h" @@ -694,6 +695,14 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { #endif + case INS_EIP712_STRUCT_DEF: + handle_eip712_struct_def(G_io_apdu_buffer); + break; + + case INS_EIP712_STRUCT_IMPL: + handle_eip712_struct_impl(G_io_apdu_buffer); + break; + #if 0 case 0xFF: // return to dashboard goto return_to_dashboard; @@ -884,6 +893,7 @@ void coin_main(chain_config_t *coin_config) { } reset_app_context(); tmpCtx.transactionContext.currentItemIndex = 0; + mem_init(); for (;;) { UX_INIT(); diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 269f2e9..d52be1b 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -360,49 +360,51 @@ bool set_struct_field(const uint8_t *const data) } -bool handle_apdu(const uint8_t *const data) +bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { - switch (data[OFFSET_INS]) + bool ret = true; + + switch (apdu_buf[OFFSET_P2]) { - case INS_STRUCT_DEF: - switch (data[OFFSET_P2]) - { - case P2_NAME: - set_struct_name(data); - break; - case P2_FIELD: - set_struct_field(data); - break; - default: - PRINTF("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); - return false; - } + case P2_NAME: + ret = set_struct_name(apdu_buf); break; - case INS_STRUCT_IMPL: - switch (data[OFFSET_P2]) - { - case P2_NAME: - // set root type - path_set_root((char*)&data[OFFSET_DATA], data[OFFSET_LC]); - break; - case P2_FIELD: - if ((data[OFFSET_P1] != P1_COMPLETE) && (data[OFFSET_P1] != P1_PARTIAL)) - { - return false; - } - field_hash(&data[OFFSET_DATA], data[OFFSET_LC], data[OFFSET_P1] == P1_PARTIAL); - break; - case P2_ARRAY: - path_new_array_depth(data[OFFSET_DATA]); - break; - default: - PRINTF("Unknown P2 0x%x for APDU 0x%x\n", data[OFFSET_P2], data[OFFSET_INS]); - return false; - } + case P2_FIELD: + ret = set_struct_field(apdu_buf); break; default: - PRINTF("Unrecognized APDU (0x%.02x)\n", data[OFFSET_INS]); - return false; + PRINTF("Unknown P2 0x%x for APDU 0x%x\n", + apdu_buf[OFFSET_P2], + apdu_buf[OFFSET_INS]); + ret = false; } - return true; + return ret; +} + +bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) +{ + bool ret; + + switch (apdu_buf[OFFSET_P2]) + { + case P2_NAME: + // set root type + ret = path_set_root((char*)&apdu_buf[OFFSET_DATA], + apdu_buf[OFFSET_LC]); + break; + case P2_FIELD: + ret = field_hash(&apdu_buf[OFFSET_DATA], + apdu_buf[OFFSET_LC], + apdu_buf[OFFSET_P1] != P1_COMPLETE); + break; + case P2_ARRAY: + ret = path_new_array_depth(apdu_buf[OFFSET_DATA]); + break; + default: + PRINTF("Unknown P2 0x%x for APDU 0x%x\n", + apdu_buf[OFFSET_P2], + apdu_buf[OFFSET_INS]); + ret = false; + } + return ret; } From faae5a909c9f39199962aa7822a9517ae379b6d1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 11:37:16 +0200 Subject: [PATCH 050/184] Fix PIC crashes --- src_features/signMessageEIP712/sol_typenames.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 25c704f..11a24ae 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -4,6 +4,7 @@ #include "eip712.h" #include "context.h" #include "mem.h" +#include "os_pic.h" // Bit indicating they are more types associated to this typename #define TYPENAME_MORE_TYPE (1 << 7) @@ -77,14 +78,14 @@ bool init_sol_typenames(void) return false; } // get pointer to the allocated space just above - *typename_len_ptr = strlen(typenames[s_idx]); + *typename_len_ptr = strlen(PIC(typenames[s_idx])); if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { return false; } // copy typename - memcpy(typename_ptr, typenames[s_idx], *typename_len_ptr); + memcpy(typename_ptr, PIC(typenames[s_idx]), *typename_len_ptr); } // increment array size *typenames_array += 1; From 4442ba5716536f462516d52d735df8f56482564c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 11:31:44 +0200 Subject: [PATCH 051/184] Now responds to EIP712 APDUs --- src_features/signMessageEIP712/entrypoint.c | 26 +++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index d52be1b..fd0b7e9 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -378,6 +378,19 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) 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; + } + //*flags |= IO_ASYNCH_REPLY; + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return ret; } @@ -406,5 +419,18 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) 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; + } + //*flags |= IO_ASYNCH_REPLY; + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return ret; } From b6e4f887cb4af162a9048fd02899c1762a0066f2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 11:27:51 +0200 Subject: [PATCH 052/184] Fixes printf formatting issues caused by the toolchain --- src_features/signMessageEIP712/mem_utils.c | 16 ++++++++-------- src_features/signMessageEIP712/path.c | 4 +++- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index 0c5583f..d5d85e1 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -43,15 +43,15 @@ char *mem_alloc_and_format_uint(uint32_t value, size += 1; } // +1 for the null character - if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1))) == NULL) + if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1)))) { - return 0; - } - snprintf(mem_ptr, (size + 1), "%u", value); - mem_dealloc(sizeof(char)); // to skip the null character - if (length != NULL) - { - *length = size; + // should be using %u, but not supported by toolchain + snprintf(mem_ptr, (size + 1), "%d", value); + mem_dealloc(sizeof(char)); // to skip the null character + if (length != NULL) + { + *length = size; + } } return mem_ptr; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index fd8c54e..8a8ec1f 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -137,7 +137,9 @@ static bool path_depth_list_pop(void) PRINTF("Hash = 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { - PRINTF("%.02x", shash[idx]); + // manual 0 padding, %.02x not supported by toolchain + if (shash[idx] < 0x10) PRINTF("0"); + PRINTF("%x", shash[idx]); } PRINTF("\n\n"); } From 3095d54394c21da16d8018c4810ed3e27c6bc2c4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 11:43:20 +0200 Subject: [PATCH 053/184] Now initializes the eip712 context --- src/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 279a9ac..fdc82bf 100644 --- a/src/main.c +++ b/src/main.c @@ -29,7 +29,7 @@ #include "handle_swap_sign_transaction.h" #include "handle_get_printable_amount.h" #include "handle_check_address.h" -#include "mem.h" +#include "context.h" #ifdef HAVE_STARKWARE #include "stark_crypto.h" @@ -893,7 +893,7 @@ void coin_main(chain_config_t *coin_config) { } reset_app_context(); tmpCtx.transactionContext.currentItemIndex = 0; - mem_init(); + init_eip712_context(); for (;;) { UX_INIT(); From 516682b206929ebe4f44f217ed497c9002d6743d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 11:54:22 +0200 Subject: [PATCH 054/184] Fix impl APDU wrongly returning 0x6a80 --- src_features/signMessageEIP712/entrypoint.c | 2 +- src_features/signMessageEIP712/field_hash.c | 22 ++++++++++----------- src_features/signMessageEIP712/field_hash.h | 6 +++--- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index fd0b7e9..20395ea 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -396,7 +396,7 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { - bool ret; + bool ret = true; switch (apdu_buf[OFFSET_P2]) { diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 6221631..54bc389 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -21,9 +21,9 @@ bool field_hash_init(void) return true; } -const uint8_t *field_hash(const uint8_t *data, - uint8_t data_length, - bool partial) +bool field_hash(const uint8_t *data, + uint8_t data_length, + bool partial) { const void *field_ptr; e_type field_type; @@ -39,12 +39,12 @@ const uint8_t *field_hash(const uint8_t *data, (void)data; if (fh == NULL) { - return NULL; + return false; } // get field by path if ((field_ptr = path_get_field()) == NULL) { - return NULL; + return false; } field_type = struct_field_type(field_ptr); if (fh->state == FHS_IDLE) // first packet for this frame @@ -73,7 +73,7 @@ const uint8_t *field_hash(const uint8_t *data, { if (partial) // only makes sense if marked as complete { - return NULL; + return false; } #ifdef DEBUG PRINTF("=> "); @@ -105,19 +105,19 @@ const uint8_t *field_hash(const uint8_t *data, case TYPE_CUSTOM: default: PRINTF("Unknown solidity type!\n"); - return NULL; + return false; } if (value == NULL) { - return NULL; + return false; } } else { if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { - return NULL; + return false; } // copy hash into memory cx_hash((cx_hash_t*)&global_sha3, @@ -152,9 +152,9 @@ const uint8_t *field_hash(const uint8_t *data, { if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial { - return NULL; + return false; } } - return value; + return true; } diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index 1d0e276..ca08aff 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -19,7 +19,7 @@ typedef struct } s_field_hashing; bool field_hash_init(void); -const uint8_t *field_hash(const uint8_t *data, - uint8_t data_length, - bool partial); +bool field_hash(const uint8_t *data, + uint8_t data_length, + bool partial); #endif // FIELD_HASH_H_ From 90dfa74538b5d2ccdb519bace02b64628cbc7e2f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 17:16:25 +0200 Subject: [PATCH 055/184] Emulates old behaviour with new APDUs but computed all on-device --- src_features/signMessageEIP712/path.c | 18 ++++++++++++++++-- .../signMessageEIP712_v0/cmd_signMessage712.c | 15 ++++++++++----- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 8a8ec1f..00acffd 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -6,6 +6,7 @@ #include "eip712.h" #include "type_hash.h" #include "shared_context.h" +#include "ethUtils.h" static s_path *path_struct = NULL; @@ -131,9 +132,22 @@ static bool path_depth_list_pop(void) NULL, 0); } -#ifdef DEBUG else { + if (allzeroes(tmpCtx.messageSigningContext712.domainHash, KECCAK256_HASH_BYTESIZE)) + { + memcpy(tmpCtx.messageSigningContext712.domainHash, + shash, + KECCAK256_HASH_BYTESIZE); + } + else + { + memcpy(tmpCtx.messageSigningContext712.messageHash, + shash, + KECCAK256_HASH_BYTESIZE); + mem_reset(); + } +#ifdef DEBUG PRINTF("Hash = 0x"); for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) { @@ -142,8 +156,8 @@ static bool path_depth_list_pop(void) PRINTF("%x", shash[idx]); } PRINTF("\n\n"); - } #endif + } return true; } diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index cd32dad..3834711 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -13,7 +13,7 @@ void handleSignEIP712Message_v0(uint8_t p1, UNUSED(tx); if ((p1 != 00) || (p2 != 00)) { - THROW(0x6B00); + //THROW(0x6B00); // TODO: TMP } if (appState != APP_STATE_IDLE) { reset_app_context(); @@ -21,12 +21,17 @@ void handleSignEIP712Message_v0(uint8_t p1, workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32); - if (workBuffer == NULL || dataLength < 32 + 32) { + if (workBuffer == NULL) { THROW(0x6a80); } - - memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); - memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); + if (p2 == 0) // TODO: TMP + { + if (dataLength < (32 + 32)) { + THROW(0x6a80); + } + memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); + memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); + } #ifdef NO_CONSENT io_seproxyhal_touch_signMessage_ok(NULL); From d43849d8527e3dfb685e6109ad5825182d8e069b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 4 May 2022 17:16:46 +0200 Subject: [PATCH 056/184] Sets the memory buffer to 5K --- src_features/signMessageEIP712/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index 799be1f..e0c61c9 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -3,7 +3,7 @@ #include "shared_context.h" -#define SIZE_MEM_BUFFER 8192 +#define SIZE_MEM_BUFFER 5120 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; From 5c00a5c27bf011f3473061b8a268f2b5c1225e39 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 5 May 2022 15:21:39 +0200 Subject: [PATCH 057/184] Structs alignment in memory, fixes device freezes/crashes --- src_features/signMessageEIP712/field_hash.c | 4 +-- src_features/signMessageEIP712/mem_utils.c | 28 ++++++++++++++++++--- src_features/signMessageEIP712/mem_utils.h | 6 +++-- src_features/signMessageEIP712/path.c | 12 +++++---- src_features/signMessageEIP712/type_hash.c | 8 ++++-- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 54bc389..97f326a 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -3,6 +3,7 @@ #include "encode_field.h" #include "path.h" #include "mem.h" +#include "mem_utils.h" #include "eip712.h" #include "shared_context.h" @@ -12,7 +13,7 @@ bool field_hash_init(void) { if (fh == NULL) { - if ((fh = mem_alloc(sizeof(*fh))) == NULL) + if ((fh = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*fh), *fh)) == NULL) { return false; } @@ -36,7 +37,6 @@ bool field_hash(const uint8_t *data, #endif - (void)data; if (fh == NULL) { return false; diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index d5d85e1..d8b5cf8 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -26,10 +26,9 @@ char *mem_alloc_and_copy_char(char c) * @param[in] value Value to write in memory * @param[out] length number of characters written to memory * - * @return pointer to memory area or \ref NULL if the allocated failed + * @return pointer to memory area or \ref NULL if the allocation failed */ -char *mem_alloc_and_format_uint(uint32_t value, - uint8_t *const length) +char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const length) { char *mem_ptr; uint32_t value_copy; @@ -55,3 +54,26 @@ char *mem_alloc_and_format_uint(uint32_t value, } return mem_ptr; } + +/** + * Allocate and align, required when dealing with pointers of multi-bytes data + * like structures that will be dereferenced at runtime. + * + * @param[in] size the size of the data we want to allocate in memory + * @param[in] alignment the byte alignment needed + * + * @return pointer to the memory area, \ref NULL if the allocation failed + */ +void *mem_alloc_and_align(size_t size, size_t alignment) +{ + uint8_t align_diff = (uintptr_t)mem_alloc(0) % alignment; + + if (align_diff > 0) // alignment needed + { + if (mem_alloc(alignment - align_diff) == NULL) + { + return NULL; + } + } + return mem_alloc(size); +} diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h index a86684c..8a89c5b 100644 --- a/src_features/signMessageEIP712/mem_utils.h +++ b/src_features/signMessageEIP712/mem_utils.h @@ -4,9 +4,11 @@ #include #include +#define MEM_ALLOC_AND_ALIGN_TO_TYPE(size, type) (mem_alloc_and_align(size, __alignof__(type))) + char *mem_alloc_and_copy_char(char c); void *mem_alloc_and_copy(const void *data, size_t size); -char *mem_alloc_and_format_uint(uint32_t value, - uint8_t *const written_chars); +char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); +void *mem_alloc_and_align(size_t size, size_t alignment); #endif // MEM_UTILS_H_ diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 00acffd..29cc696 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -7,6 +7,7 @@ #include "type_hash.h" #include "shared_context.h" #include "ethUtils.h" +#include "mem_utils.h" static s_path *path_struct = NULL; @@ -255,7 +256,7 @@ static bool path_update(void) const uint8_t *thash_ptr; // allocate new hash context - if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + if ((hash_ctx = mem_alloc(sizeof(*hash_ctx))) == NULL) { return false; } @@ -304,7 +305,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // TODO: Move elsewhere cx_sha3_t *hash_ctx; const uint8_t *thash_ptr; - if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*hash_ctx), *hash_ctx)) == NULL) { return false; } @@ -427,7 +428,8 @@ bool path_new_array_depth(uint8_t size) } // TODO: Move elsewhere cx_sha3_t *hash_ctx; - if ((hash_ctx = mem_alloc(sizeof(cx_sha3_t))) == NULL) + // memory address not aligned, padd it + if ((hash_ctx = mem_alloc(sizeof(*hash_ctx))) == NULL) { return false; } @@ -533,7 +535,7 @@ bool path_advance(void) } /** - * Allocates the the path indexes in memory and sets it with a depth of 0. + * Allocates the path indexes in memory and sets it with a depth of 0. * * @return whether the memory allocation were successful. */ @@ -541,7 +543,7 @@ bool path_init(void) { if (path_struct == NULL) { - path_struct = mem_alloc(sizeof(*path_struct)); + path_struct = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*path_struct), *path_struct); } return path_struct != NULL; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 8c1b0b4..203608c 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -275,10 +275,14 @@ const uint8_t *type_hash(const void *const structs_array, uint8_t deps_count = 0; void **deps; uint8_t *hash_ptr; + void *mem_loc_bak = mem_alloc(0); cx_keccak_init(&global_sha3, 256); // init hash // get list of structs (own + dependencies), properly ordered - deps = mem_alloc(0); // get where the first elem will be + if ((deps = MEM_ALLOC_AND_ALIGN_TO_TYPE(0, *deps)) == NULL) + { + return NULL; + } if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) { return NULL; @@ -294,7 +298,7 @@ const uint8_t *type_hash(const void *const structs_array, encode_and_hash_type(*deps); deps += 1; } - mem_dealloc(sizeof(void*) * deps_count); + mem_dealloc(mem_alloc(0) - mem_loc_bak); #ifdef DEBUG PRINTF("\n"); #endif From b8424d1a21788e3f64f997dcb63220120dda26ef Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 6 May 2022 18:14:04 +0200 Subject: [PATCH 058/184] Now handles domain & message struct not based on order but on struct name --- src_features/signMessageEIP712/eip712.h | 3 +++ src_features/signMessageEIP712/path.c | 35 +++++++++++++++++-------- src_features/signMessageEIP712/path.h | 7 +++++ 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 7846841..75f63f9 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -64,6 +64,9 @@ typedef enum #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define DOMAIN_STRUCT_NAME "EIP712Domain" +#define DOMAIN_STRUCT_NAME_LENGTH 12 + // TODO: Move these into a new file const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_struct_fields_array(const uint8_t *ptr, diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 29cc696..59a8223 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -135,18 +135,21 @@ static bool path_depth_list_pop(void) } else { - if (allzeroes(tmpCtx.messageSigningContext712.domainHash, KECCAK256_HASH_BYTESIZE)) + switch (path_struct->root_type) { - memcpy(tmpCtx.messageSigningContext712.domainHash, - shash, - KECCAK256_HASH_BYTESIZE); - } - else - { - memcpy(tmpCtx.messageSigningContext712.messageHash, - shash, - KECCAK256_HASH_BYTESIZE); - mem_reset(); + case ROOT_DOMAIN: + memcpy(tmpCtx.messageSigningContext712.domainHash, + shash, + KECCAK256_HASH_BYTESIZE); + break; + case ROOT_MESSAGE: + memcpy(tmpCtx.messageSigningContext712.messageHash, + shash, + KECCAK256_HASH_BYTESIZE); + mem_reset(); + break; + default: + break; } #ifdef DEBUG PRINTF("Hash = 0x"); @@ -332,6 +335,16 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // init array levels at 0 path_struct->array_depth_count = 0; + if ((name_length == DOMAIN_STRUCT_NAME_LENGTH) && + (strncmp(struct_name, DOMAIN_STRUCT_NAME, DOMAIN_STRUCT_NAME_LENGTH) == 0)) + { + path_struct->root_type = ROOT_DOMAIN; + } + else + { + path_struct->root_type = ROOT_MESSAGE; + } + // because the first field could be a struct type path_update(); return true; diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index f9e1885..ffec68f 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -13,6 +13,12 @@ typedef struct uint8_t size; } s_array_depth; +typedef enum +{ + ROOT_DOMAIN, + ROOT_MESSAGE +} e_root_type; + typedef struct { uint8_t depth_count; @@ -20,6 +26,7 @@ typedef struct uint8_t array_depth_count; s_array_depth array_depths[MAX_ARRAY_DEPTH]; const void *root_struct; + e_root_type root_type; } s_path; bool path_set_root(const char *const struct_name, uint8_t length); From 9f29a7caa23ead46432cbcbdb8f2bf00b5fa7de0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 12 May 2022 17:29:35 +0200 Subject: [PATCH 059/184] UI handling WIP --- src/apdu_constants.h | 1 + src/main.c | 23 ++-- src_features/signMessageEIP712/context.c | 8 ++ src_features/signMessageEIP712/entrypoint.c | 35 ++++-- src_features/signMessageEIP712/field_hash.c | 5 + src_features/signMessageEIP712/path.c | 13 ++ src_features/signMessageEIP712/ui_flow_712.c | 53 +++++++++ src_features/signMessageEIP712/ui_flow_712.h | 8 ++ src_features/signMessageEIP712/ui_logic.c | 112 ++++++++++++++++++ src_features/signMessageEIP712/ui_logic.h | 25 ++++ .../signMessageEIP712_v0/cmd_signMessage712.c | 14 +-- 11 files changed, 271 insertions(+), 26 deletions(-) create mode 100644 src_features/signMessageEIP712/ui_flow_712.c create mode 100644 src_features/signMessageEIP712/ui_flow_712.h create mode 100644 src_features/signMessageEIP712/ui_logic.c create mode 100644 src_features/signMessageEIP712/ui_logic.h diff --git a/src/apdu_constants.h b/src/apdu_constants.h index a9cceb4..a5934ce 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -172,5 +172,6 @@ void handleStarkwareUnsafeSign(uint8_t p1, 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); #endif // _APDU_CONSTANTS_H_ diff --git a/src/main.c b/src/main.c index fdc82bf..5af4e52 100644 --- a/src/main.c +++ b/src/main.c @@ -663,13 +663,20 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { break; case INS_SIGN_EIP_712_MESSAGE: - memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); - handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - flags, - tx); + if (G_io_apdu_buffer[OFFSET_P2] == 0) + { + memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); + handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + flags, + tx); + } + else + { + handle_eip712_sign(G_io_apdu_buffer); + } break; #ifdef HAVE_ETH2 @@ -696,10 +703,12 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { #endif case INS_EIP712_STRUCT_DEF: + *flags |= IO_ASYNCH_REPLY; handle_eip712_struct_def(G_io_apdu_buffer); break; case INS_EIP712_STRUCT_IMPL: + *flags |= IO_ASYNCH_REPLY; handle_eip712_struct_impl(G_io_apdu_buffer); break; diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index de53326..3a08d04 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -6,6 +6,7 @@ #include "sol_typenames.h" #include "path.h" #include "field_hash.h" +#include "ui_logic.h" uint8_t *typenames_array; uint8_t *structs_array; @@ -35,6 +36,11 @@ bool init_eip712_context(void) return false; } + if (ui_712_init() == false) + { + return false; + } + // set types pointer if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) { @@ -45,3 +51,5 @@ bool init_eip712_context(void) *structs_array = 0; return true; } + +// TODO: Make a deinit function diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 20395ea..5e00b87 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -11,6 +11,7 @@ #include "field_hash.h" #include "path.h" #include "shared_context.h" +#include "ui_logic.h" // lib functions @@ -419,18 +420,34 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) apdu_buf[OFFSET_INS]); ret = false; } - if (ret) - { - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - } - else + if (!ret) { + // Send back the response, do not restart the event loop G_io_apdu_buffer[0] = 0x6A; G_io_apdu_buffer[1] = 0x80; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } - //*flags |= IO_ASYNCH_REPLY; - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return ret; } + +bool handle_eip712_sign(const uint8_t *const apdu_buf) +{ + uint8_t i; + + if (apdu_buf[OFFSET_LC] < 1) { + PRINTF("Invalid data\n"); + THROW(0x6a80); + } + tmpCtx.messageSigningContext712.pathLength = apdu_buf[OFFSET_DATA]; + if ((tmpCtx.messageSigningContext712.pathLength < 0x01) || + (tmpCtx.messageSigningContext712.pathLength > MAX_BIP32_PATH)) { + PRINTF("Invalid path\n"); + THROW(0x6a80); + } + for (i = 0; i < tmpCtx.messageSigningContext712.pathLength; i++) { + tmpCtx.messageSigningContext712.bip32Path[i] = U4BE(apdu_buf + OFFSET_LC + 1 + (i * 4), 0); + } + + ui_712_end_sign(); + return true; +} diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 97f326a..8637adf 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -6,6 +6,7 @@ #include "mem_utils.h" #include "eip712.h" #include "shared_context.h" +#include "ui_logic.h" static s_field_hashing *fh = NULL; @@ -145,6 +146,7 @@ bool field_hash(const uint8_t *data, // deallocate it mem_dealloc(len); + ui_712_new_field(field_ptr, data, data_length); path_advance(); fh->state = FHS_IDLE; } @@ -154,6 +156,9 @@ bool field_hash(const uint8_t *data, { return false; } + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } return true; diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 59a8223..fb87573 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -8,6 +8,7 @@ #include "shared_context.h" #include "ethUtils.h" #include "mem_utils.h" +#include "ui_logic.h" static s_path *path_struct = NULL; @@ -295,6 +296,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) { if (path_struct == NULL) { + PRINTF("NULL check failed!\n"); return false; } @@ -302,6 +304,12 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) if (path_struct->root_struct == NULL) { + PRINTF("Struct name not found ("); + for (int i = 0; i < name_length; ++i) + { + PRINTF("%c", struct_name[i]); + } + PRINTF(")!\n"); return false; } @@ -315,6 +323,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) cx_keccak_init(hash_ctx, 256); // init hash if ((thash_ptr = type_hash(structs_array, struct_name, name_length)) == NULL) { + PRINTF("Memory allocation failed!\n"); return false; } // start the progressive hash on it @@ -347,6 +356,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // because the first field could be a struct type path_update(); + ui_712_new_root_struct(path_struct->root_struct); return true; } @@ -458,6 +468,9 @@ bool path_new_array_depth(uint8_t size) cx_keccak_init(hash_ctx, 256); // init hash } + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return true; } diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_features/signMessageEIP712/ui_flow_712.c new file mode 100644 index 0000000..6db752b --- /dev/null +++ b/src_features/signMessageEIP712/ui_flow_712.c @@ -0,0 +1,53 @@ +#include "ui_flow_712.h" +#include "ui_logic.h" +#include "shared_context.h" // strings + +// clang-format off +UX_STEP_NOCB( + ux_712_step_review, + pnn, + { + &C_icon_eye, + "Review", + "typed message", + }); +UX_STEP_NOCB( + ux_712_step_dynamic, + bnnn_paging, + { + .title = strings.tmp.tmp2, + .text = strings.tmp.tmp, + } +); +UX_STEP_INIT( + ux_712_step_dummy, + NULL, + NULL, + { + ui_712_next_field(); + } +); +UX_STEP_CB( + ux_712_step_approve, + pb, + NULL,//io_seproxyhal_touch_signMessage712_ok(NULL), + { + &C_icon_validate_14, + "Approve", + }); +UX_STEP_CB( + ux_712_step_reject, + pb, + NULL,//io_seproxyhal_touch_signMessage712_cancel(NULL), + { + &C_icon_crossmark, + "Reject", + }); +// clang-format on + +UX_FLOW(ux_712_flow, + &ux_712_step_review, + &ux_712_step_dynamic, + &ux_712_step_dummy, + &ux_712_step_approve, + &ux_712_step_reject); diff --git a/src_features/signMessageEIP712/ui_flow_712.h b/src_features/signMessageEIP712/ui_flow_712.h new file mode 100644 index 0000000..46f3120 --- /dev/null +++ b/src_features/signMessageEIP712/ui_flow_712.h @@ -0,0 +1,8 @@ +#ifndef UI_FLOW_712_H_ +#define UI_FLOW_712_H_ + +#include "ux_flow_engine.h" + +extern const ux_flow_step_t* const ux_712_flow[]; + +#endif // UI_FLOW_712_H_ diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c new file mode 100644 index 0000000..3bc1704 --- /dev/null +++ b/src_features/signMessageEIP712/ui_logic.c @@ -0,0 +1,112 @@ +#include +#include +#include "ui_logic.h" +#include "mem.h" +#include "mem_utils.h" +#include "os_io.h" +#include "ux_flow_engine.h" +#include "ui_flow_712.h" +#include "shared_context.h" +#include "eip712.h" // get_struct_name + +static t_ui_context *ui_ctx = NULL; + +/** + * Called on the intermediate dummy screen between the dynamic step + * && the approve/reject screen + */ +void ui_712_next_field(void) +{ + if (!ui_ctx->end_reached) + { + // reply to previous APDU + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } + else + { + if (ui_ctx->pos == UI_712_POS_REVIEW) + { + ux_flow_next(); + ui_ctx->pos = UI_712_POS_END; + } + else + { + ux_flow_prev(); + ui_ctx->pos = UI_712_POS_REVIEW; + } + } +} + +/** + * Used to notify of a new struct to review (domain or message) + */ +void ui_712_new_root_struct(const void *const struct_ptr) +{ + strcpy(strings.tmp.tmp2, "Review struct"); + const char *struct_name; + uint8_t struct_name_length; + if ((struct_name = get_struct_name(struct_ptr, &struct_name_length)) != NULL) + { + strncpy(strings.tmp.tmp, struct_name, struct_name_length); + strings.tmp.tmp[struct_name_length] = '\0'; + } + if (!ui_ctx->shown) + { + ux_flow_init(0, ux_712_flow, NULL); + ui_ctx->shown = true; + } + else + { + ux_flow_prev(); + } +} + +/** + * Used to notify of a new field to review in the current struct (key + value) + * + * @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 + */ +void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length) +{ + const char *key; + uint8_t key_len; + + if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) + { + strncpy(strings.tmp.tmp2, key, MIN(key_len, sizeof(strings.tmp.tmp2) - 1)); + strings.tmp.tmp2[key_len] = '\0'; + } + // TODO: Encode data as string based on data type + (void)data; + (void)length; + strcpy(strings.tmp.tmp, "Field value"); + ux_flow_prev(); +} + +/** + * Used to signal that we are done with reviewing the structs and we can now have + * the option to approve or reject the signature + */ +void ui_712_end_sign(void) +{ + ui_ctx->end_reached = true; + ui_712_next_field(); +} + +/** + * Initializes the UI context structure in memory + */ +bool ui_712_init(void) +{ + if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*ui_ctx), *ui_ctx))) + { + ui_ctx->shown = false; + ui_ctx->end_reached = false; + ui_ctx->pos = UI_712_POS_REVIEW; + } + return ui_ctx != NULL; +} diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h new file mode 100644 index 0000000..7e6e82e --- /dev/null +++ b/src_features/signMessageEIP712/ui_logic.h @@ -0,0 +1,25 @@ +#ifndef UI_LOGIC_712_H_ +#define UI_LOGIC_712_H_ + +#include + +typedef enum +{ + UI_712_POS_REVIEW, + UI_712_POS_END +} e_ui_position; + +typedef struct +{ + bool shown; + bool end_reached; + e_ui_position pos; +} t_ui_context; + +bool ui_712_init(void); +void ui_712_next_field(void); +void ui_712_new_root_struct(const void *const struct_ptr); +void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); +void ui_712_end_sign(void); + +#endif // UI_LOGIC_712_H_ diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index 3834711..6abd4f7 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -13,7 +13,7 @@ void handleSignEIP712Message_v0(uint8_t p1, UNUSED(tx); if ((p1 != 00) || (p2 != 00)) { - //THROW(0x6B00); // TODO: TMP + THROW(0x6B00); } if (appState != APP_STATE_IDLE) { reset_app_context(); @@ -21,17 +21,11 @@ void handleSignEIP712Message_v0(uint8_t p1, workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32); - if (workBuffer == NULL) { + if ((workBuffer == NULL) || (dataLength < (32 + 32))) { THROW(0x6a80); } - if (p2 == 0) // TODO: TMP - { - if (dataLength < (32 + 32)) { - THROW(0x6a80); - } - memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); - memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); - } + memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); + memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); #ifdef NO_CONSENT io_seproxyhal_touch_signMessage_ok(NULL); From 9cd39e2c024c13b84c6f09a7adccf695174afb59 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 12 May 2022 17:30:04 +0200 Subject: [PATCH 060/184] Now uses some of the old EIP712 code as common code between the two implementations + approve/reject button now works --- src/apdu_constants.h | 16 ++++++---- src/main.c | 1 + src/ui_callbacks.h | 2 -- src_features/signMessageEIP712/eip712.h | 9 ------ src_features/signMessageEIP712/entrypoint.c | 32 +++++++------------ src_features/signMessageEIP712/ui_flow_712.c | 5 +-- .../common_712.c} | 20 ++++++++---- .../signMessageEIP712_common/common_712.h | 10 ++++++ .../signMessageEIP712_v0/cmd_signMessage712.c | 16 ++++++---- .../ui_flow_signMessage712.c | 5 +-- 10 files changed, 61 insertions(+), 55 deletions(-) rename src_features/{signMessageEIP712_v0/ui_common_signMessage712.c => signMessageEIP712_common/common_712.c} (86%) create mode 100644 src_features/signMessageEIP712_common/common_712.h diff --git a/src/apdu_constants.h b/src/apdu_constants.h index a5934ce..52de586 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -53,12 +53,14 @@ #endif -#define OFFSET_CLA 0 -#define OFFSET_INS 1 -#define OFFSET_P1 2 -#define OFFSET_P2 3 -#define OFFSET_LC 4 -#define OFFSET_CDATA 5 +enum { + OFFSET_CLA = 0, + OFFSET_INS, + OFFSET_P1, + OFFSET_P2, + OFFSET_LC, + OFFSET_CDATA +}; void handleGetPublicKey(uint8_t p1, uint8_t p2, @@ -99,7 +101,7 @@ void handleSignPersonalMessage(uint8_t p1, void handleSignEIP712Message_v0(uint8_t p1, uint8_t p2, const uint8_t *dataBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx); diff --git a/src/main.c b/src/main.c index 5af4e52..bd8e67b 100644 --- a/src/main.c +++ b/src/main.c @@ -675,6 +675,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { } else { + *flags |= IO_ASYNCH_REPLY; handle_eip712_sign(G_io_apdu_buffer); } break; diff --git a/src/ui_callbacks.h b/src/ui_callbacks.h index 2d7ff98..f1b1339 100644 --- a/src/ui_callbacks.h +++ b/src/ui_callbacks.h @@ -14,8 +14,6 @@ unsigned int io_seproxyhal_touch_signMessage_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_signMessage_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_data_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_data_cancel(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_signMessage712_v0_ok(const bagl_element_t *e); -unsigned int io_seproxyhal_touch_signMessage712_v0_cancel(const bagl_element_t *e); unsigned int io_seproxyhal_touch_eth2_address_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_privacy_ok(const bagl_element_t *e); unsigned int io_seproxyhal_touch_privacy_cancel(const bagl_element_t *e); diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 75f63f9..70a5ffe 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -4,15 +4,6 @@ #include #include -enum { - OFFSET_CLA = 0, - OFFSET_INS, - OFFSET_P1, - OFFSET_P2, - OFFSET_LC, - OFFSET_DATA -}; - typedef enum { // contract defined struct diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 5e00b87..368deb3 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -3,6 +3,7 @@ #include #include +#include "apdu_constants.h" #include "eip712.h" #include "mem.h" #include "type_hash.h" @@ -12,6 +13,7 @@ #include "path.h" #include "shared_context.h" #include "ui_logic.h" +#include "common_712.h" // lib functions @@ -249,7 +251,7 @@ bool set_struct_name(const uint8_t *const data) { return false; } - memmove(name_ptr, &data[OFFSET_DATA], data[OFFSET_LC]); + memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); // initialize number of fields if ((current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) @@ -264,7 +266,7 @@ bool set_struct_name(const uint8_t *const data) // TODO: Handle partial sends bool set_struct_field(const uint8_t *const data) { - uint8_t data_idx = OFFSET_DATA; + uint8_t data_idx = OFFSET_CDATA; uint8_t *type_desc_ptr; uint8_t *type_size_ptr; uint8_t *typename_len_ptr; @@ -403,16 +405,16 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { case P2_NAME: // set root type - ret = path_set_root((char*)&apdu_buf[OFFSET_DATA], + ret = path_set_root((char*)&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); break; case P2_FIELD: - ret = field_hash(&apdu_buf[OFFSET_DATA], + ret = field_hash(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC], apdu_buf[OFFSET_P1] != P1_COMPLETE); break; case P2_ARRAY: - ret = path_new_array_depth(apdu_buf[OFFSET_DATA]); + ret = path_new_array_depth(apdu_buf[OFFSET_CDATA]); break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", @@ -432,22 +434,12 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) bool handle_eip712_sign(const uint8_t *const apdu_buf) { - uint8_t i; - - if (apdu_buf[OFFSET_LC] < 1) { - PRINTF("Invalid data\n"); - THROW(0x6a80); + if (parseBip32(&apdu_buf[OFFSET_CDATA], + &apdu_buf[OFFSET_LC], + &tmpCtx.messageSigningContext.bip32) == NULL) + { + return false; } - tmpCtx.messageSigningContext712.pathLength = apdu_buf[OFFSET_DATA]; - if ((tmpCtx.messageSigningContext712.pathLength < 0x01) || - (tmpCtx.messageSigningContext712.pathLength > MAX_BIP32_PATH)) { - PRINTF("Invalid path\n"); - THROW(0x6a80); - } - for (i = 0; i < tmpCtx.messageSigningContext712.pathLength; i++) { - tmpCtx.messageSigningContext712.bip32Path[i] = U4BE(apdu_buf + OFFSET_LC + 1 + (i * 4), 0); - } - ui_712_end_sign(); return true; } diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_features/signMessageEIP712/ui_flow_712.c index 6db752b..7563407 100644 --- a/src_features/signMessageEIP712/ui_flow_712.c +++ b/src_features/signMessageEIP712/ui_flow_712.c @@ -1,6 +1,7 @@ #include "ui_flow_712.h" #include "ui_logic.h" #include "shared_context.h" // strings +#include "common_712.h" // clang-format off UX_STEP_NOCB( @@ -30,7 +31,7 @@ UX_STEP_INIT( UX_STEP_CB( ux_712_step_approve, pb, - NULL,//io_seproxyhal_touch_signMessage712_ok(NULL), + ui_712_approve_cb(NULL), { &C_icon_validate_14, "Approve", @@ -38,7 +39,7 @@ UX_STEP_CB( UX_STEP_CB( ux_712_step_reject, pb, - NULL,//io_seproxyhal_touch_signMessage712_cancel(NULL), + ui_712_reject_cb(NULL), { &C_icon_crossmark, "Reject", diff --git a/src_features/signMessageEIP712_v0/ui_common_signMessage712.c b/src_features/signMessageEIP712_common/common_712.c similarity index 86% rename from src_features/signMessageEIP712_v0/ui_common_signMessage712.c rename to src_features/signMessageEIP712_common/common_712.c index e7dea8f..93ba048 100644 --- a/src_features/signMessageEIP712_v0/ui_common_signMessage712.c +++ b/src_features/signMessageEIP712_common/common_712.c @@ -1,16 +1,19 @@ -#include "os_io_seproxyhal.h" #include "shared_context.h" +#include "os_io_seproxyhal.h" #include "ui_callbacks.h" +#include "common_712.h" static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01}; -unsigned int io_seproxyhal_touch_signMessage712_v0_ok(__attribute__((unused)) - const bagl_element_t *e) { +unsigned int ui_712_approve_cb(const bagl_element_t *e) +{ uint8_t privateKeyData[INT256_LENGTH]; uint8_t hash[INT256_LENGTH]; uint8_t signature[100]; cx_ecfp_private_key_t privateKey; uint32_t tx = 0; + + (void)e; io_seproxyhal_io_heartbeat(); cx_keccak_init(&global_sha3, 256); cx_hash((cx_hash_t *) &global_sha3, @@ -53,10 +56,12 @@ unsigned int io_seproxyhal_touch_signMessage712_v0_ok(__attribute__((unused)) &info); explicit_bzero(&privateKey, sizeof(privateKey)); G_io_apdu_buffer[0] = 27; - if (info & CX_ECCINFO_PARITY_ODD) { + if (info & CX_ECCINFO_PARITY_ODD) + { G_io_apdu_buffer[0]++; } - if (info & CX_ECCINFO_xGTn) { + if (info & CX_ECCINFO_xGTn) + { G_io_apdu_buffer[0] += 2; } format_signature_out(signature); @@ -71,8 +76,9 @@ unsigned int io_seproxyhal_touch_signMessage712_v0_ok(__attribute__((unused)) return 0; // do not redraw the widget } -unsigned int io_seproxyhal_touch_signMessage712_v0_cancel(__attribute__((unused)) - const bagl_element_t *e) { +unsigned int ui_712_reject_cb(const bagl_element_t *e) +{ + (void)e; reset_app_context(); G_io_apdu_buffer[0] = 0x69; G_io_apdu_buffer[1] = 0x85; diff --git a/src_features/signMessageEIP712_common/common_712.h b/src_features/signMessageEIP712_common/common_712.h new file mode 100644 index 0000000..8e35b15 --- /dev/null +++ b/src_features/signMessageEIP712_common/common_712.h @@ -0,0 +1,10 @@ +#ifndef COMMON_EIP712_H_ +#define COMMON_EIP712_H_ + +#include +#include "ux.h" + +unsigned int ui_712_approve_cb(const bagl_element_t *e); +unsigned int ui_712_reject_cb(const bagl_element_t *e); + +#endif // COMMON_EIP712_H_ diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index 6abd4f7..09e3b30 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -2,15 +2,15 @@ #include "apdu_constants.h" #include "utils.h" #include "ui_flow.h" +#include "eip712.h" +#include "common_712.h" void handleSignEIP712Message_v0(uint8_t p1, uint8_t p2, const uint8_t *workBuffer, - uint16_t dataLength, + uint8_t dataLength, unsigned int *flags, unsigned int *tx) { - uint8_t i; - UNUSED(tx); if ((p1 != 00) || (p2 != 00)) { THROW(0x6B00); @@ -21,11 +21,15 @@ void handleSignEIP712Message_v0(uint8_t p1, workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32); - if ((workBuffer == NULL) || (dataLength < (32 + 32))) { + if ((workBuffer == NULL) || (dataLength < (KECCAK256_HASH_BYTESIZE * 2))) { THROW(0x6a80); } - memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, 32); - memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + 32, 32); + memmove(tmpCtx.messageSigningContext712.domainHash, + workBuffer, + KECCAK256_HASH_BYTESIZE); + memmove(tmpCtx.messageSigningContext712.messageHash, + workBuffer + KECCAK256_HASH_BYTESIZE, + KECCAK256_HASH_BYTESIZE); #ifdef NO_CONSENT io_seproxyhal_touch_signMessage_ok(NULL); diff --git a/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c b/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c index ac239d7..02e9e14 100644 --- a/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c +++ b/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c @@ -1,5 +1,6 @@ #include "shared_context.h" #include "ui_callbacks.h" +#include "common_712.h" void prepare_domain_hash_v0() { snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, tmpCtx.messageSigningContext712.domainHash); @@ -37,7 +38,7 @@ UX_STEP_NOCB_INIT( UX_STEP_CB( ux_sign_712_v0_flow_4_step, pbb, - io_seproxyhal_touch_signMessage712_v0_ok(NULL), + ui_712_approve_cb(NULL), { &C_icon_validate_14, "Sign", @@ -46,7 +47,7 @@ UX_STEP_CB( UX_STEP_CB( ux_sign_712_v0_flow_5_step, pbb, - io_seproxyhal_touch_signMessage712_v0_cancel(NULL), + ui_712_reject_cb(NULL), { &C_icon_crossmark, "Cancel", From 2badb7b5b2170ed528fef414863ec34e029e2222 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 11 May 2022 11:39:21 +0200 Subject: [PATCH 061/184] Now displays the field values Show ints as uints for now --- src_features/signMessageEIP712/field_hash.c | 3 +- src_features/signMessageEIP712/ui_logic.c | 57 +++++++++++++++++++-- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 8637adf..f8940d5 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -57,6 +57,7 @@ bool field_hash(const uint8_t *data, if (IS_DYN(field_type)) { cx_keccak_init(&global_sha3, 256); // init hash + ui_712_new_field(field_ptr, data, data_length); } } fh->remaining_size -= data_length; @@ -113,6 +114,7 @@ bool field_hash(const uint8_t *data, { return false; } + ui_712_new_field(field_ptr, data, data_length); } else { @@ -146,7 +148,6 @@ bool field_hash(const uint8_t *data, // deallocate it mem_dealloc(len); - ui_712_new_field(field_ptr, data, data_length); path_advance(); fh->state = FHS_IDLE; } diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 3bc1704..d03cd1a 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -8,6 +8,8 @@ #include "ui_flow_712.h" #include "shared_context.h" #include "eip712.h" // get_struct_name +#include "ethUtils.h" // getEthDisplayableAddress +#include "utils.h" // uint256_to_decimal static t_ui_context *ui_ctx = NULL; @@ -75,15 +77,62 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, const char *key; uint8_t key_len; + // Key if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { strncpy(strings.tmp.tmp2, key, MIN(key_len, sizeof(strings.tmp.tmp2) - 1)); strings.tmp.tmp2[key_len] = '\0'; } - // TODO: Encode data as string based on data type - (void)data; - (void)length; - strcpy(strings.tmp.tmp, "Field value"); + + // Value + strings.tmp.tmp[0] = '\0'; // empty string + switch (struct_field_type(field_ptr)) + { + case TYPE_SOL_STRING: + strncat(strings.tmp.tmp, (char*)data, sizeof(strings.tmp.tmp) - 1); + if (length > (sizeof(strings.tmp.tmp) - 1)) + { + strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - 3] = '\0'; + strcat(strings.tmp.tmp, "..."); + } + else + { + strings.tmp.tmp[length] = '\0'; + } + break; + case TYPE_SOL_ADDRESS: + getEthDisplayableAddress((uint8_t*)data, + strings.tmp.tmp, + sizeof(strings.tmp.tmp), + &global_sha3, + chainConfig->chainId); + break; + case TYPE_SOL_BOOL: + strcpy(strings.tmp.tmp, (*data == false) ? "false" : "true"); + break; + case TYPE_SOL_BYTES_FIX: + case TYPE_SOL_BYTES_DYN: + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + length, + data); + // +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'; + strcat(strings.tmp.tmp, "..."); + } + break; + // TODO: signed integers should be handled differently + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + uint256_to_decimal(data, length, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; + default: + PRINTF("Unhandled type\n"); + } ux_flow_prev(); } From 0525f7cda81808f48675a8334cba9c4f8fad28ba Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 11 May 2022 11:40:15 +0200 Subject: [PATCH 062/184] Fixes bad multi-screen value behaviour --- src_features/signMessageEIP712/ui_logic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index d03cd1a..23d4ed9 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -133,7 +133,13 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, default: PRINTF("Unhandled type\n"); } - ux_flow_prev(); + + // not pretty, manually changes the internal state of the UX flow + // so that we always land on the first screen of a paging step without any visible + // screen glitching (quick screen switching) + G_ux.flow_stack[G_ux.stack_count - 1].index = 0; + // since the flow now thinks we are displaying the first step, do next + ux_flow_next(); } /** From c0eaf8d1066605892502698fbdedf9b9114dc92c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 12 May 2022 17:30:26 +0200 Subject: [PATCH 063/184] Proper cleanup after EIP712 message --- src/main.c | 1 - src_features/signMessageEIP712/context.c | 17 ++++++-- src_features/signMessageEIP712/context.h | 5 ++- src_features/signMessageEIP712/entrypoint.c | 8 +++- src_features/signMessageEIP712/field_hash.c | 8 +++- src_features/signMessageEIP712/field_hash.h | 1 + src_features/signMessageEIP712/path.c | 6 ++- src_features/signMessageEIP712/path.h | 1 + .../signMessageEIP712/sol_typenames.c | 2 +- .../signMessageEIP712/sol_typenames.h | 2 +- src_features/signMessageEIP712/ui_flow_712.c | 5 +-- src_features/signMessageEIP712/ui_logic.c | 41 +++++++++++++++++++ src_features/signMessageEIP712/ui_logic.h | 4 ++ 13 files changed, 88 insertions(+), 13 deletions(-) diff --git a/src/main.c b/src/main.c index bd8e67b..09f8eb2 100644 --- a/src/main.c +++ b/src/main.c @@ -903,7 +903,6 @@ void coin_main(chain_config_t *coin_config) { } reset_app_context(); tmpCtx.transactionContext.currentItemIndex = 0; - init_eip712_context(); for (;;) { UX_INIT(); diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 3a08d04..dbea2d6 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -11,17 +11,18 @@ uint8_t *typenames_array; uint8_t *structs_array; uint8_t *current_struct_fields_array; +bool eip712_context_initialized = false; /** * * @return a boolean indicating if the initialization was successful or not */ -bool init_eip712_context(void) +bool eip712_context_init(void) { // init global variables mem_init(); - if (init_sol_typenames() == false) + if (sol_typenames_init() == false) { return false; } @@ -49,7 +50,17 @@ bool init_eip712_context(void) // create len(types) *structs_array = 0; + + eip712_context_initialized = true; + return true; } -// TODO: Make a deinit function +void eip712_context_deinit(void) +{ + path_deinit(); + field_hash_deinit(); + ui_712_deinit(); + mem_reset(); + eip712_context_initialized = false; +} diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 7c4520b..82aff85 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -8,6 +8,9 @@ extern uint8_t *typenames_array; extern uint8_t *structs_array; extern uint8_t *current_struct_fields_array; -bool init_eip712_context(void); +bool eip712_context_init(void); +void eip712_context_deinit(void); + +extern bool eip712_context_initialized; #endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 368deb3..5b67a4f 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -367,6 +367,13 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { bool ret = true; + if (!eip712_context_initialized) + { + if (!eip712_context_init()) + { + return false; + } + } switch (apdu_buf[OFFSET_P2]) { case P2_NAME: @@ -391,7 +398,6 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) G_io_apdu_buffer[0] = 0x6A; G_io_apdu_buffer[1] = 0x80; } - //*flags |= IO_ASYNCH_REPLY; // Send back the response, do not restart the event loop io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return ret; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index f8940d5..c8c1fb1 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -10,7 +10,8 @@ static s_field_hashing *fh = NULL; -bool field_hash_init(void) + +bool field_hash_init(void) { if (fh == NULL) { @@ -23,6 +24,11 @@ bool field_hash_init(void) return true; } +void field_hash_deinit(void) +{ + fh = NULL; +} + bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index ca08aff..6a3fb4a 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -19,6 +19,7 @@ typedef struct } s_field_hashing; bool field_hash_init(void); +void field_hash_deinit(void); bool field_hash(const uint8_t *data, uint8_t data_length, bool partial); diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index fb87573..9cf4116 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -147,7 +147,6 @@ static bool path_depth_list_pop(void) memcpy(tmpCtx.messageSigningContext712.messageHash, shash, KECCAK256_HASH_BYTESIZE); - mem_reset(); break; default: break; @@ -573,3 +572,8 @@ bool path_init(void) } return path_struct != NULL; } + +void path_deinit(void) +{ + path_struct = NULL; +} diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index ffec68f..030e369 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -33,6 +33,7 @@ bool path_set_root(const char *const struct_name, uint8_t length); const void *path_get_field(void); bool path_advance(void); bool path_init(void); +void path_deinit(void); bool path_new_array_depth(uint8_t size); #endif // PATH_H_ diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 11a24ae..f4fc562 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -39,7 +39,7 @@ static bool find_enum_matches(const uint8_t (*enum_to_idx)[TYPES_COUNT - 1][IDX_ return (enum_match != NULL); } -bool init_sol_typenames(void) +bool sol_typenames_init(void) { const char *const typenames[] = { "int", // 0 diff --git a/src_features/signMessageEIP712/sol_typenames.h b/src_features/signMessageEIP712/sol_typenames.h index b1e2a67..bb8e259 100644 --- a/src_features/signMessageEIP712/sol_typenames.h +++ b/src_features/signMessageEIP712/sol_typenames.h @@ -4,7 +4,7 @@ #include #include -bool init_sol_typenames(void); +bool sol_typenames_init(void); const char *get_struct_field_sol_typename(const uint8_t *ptr, uint8_t *const length); diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_features/signMessageEIP712/ui_flow_712.c index 7563407..37d97f1 100644 --- a/src_features/signMessageEIP712/ui_flow_712.c +++ b/src_features/signMessageEIP712/ui_flow_712.c @@ -1,7 +1,6 @@ #include "ui_flow_712.h" #include "ui_logic.h" #include "shared_context.h" // strings -#include "common_712.h" // clang-format off UX_STEP_NOCB( @@ -31,7 +30,7 @@ UX_STEP_INIT( UX_STEP_CB( ux_712_step_approve, pb, - ui_712_approve_cb(NULL), + ui_712_approve(NULL), { &C_icon_validate_14, "Approve", @@ -39,7 +38,7 @@ UX_STEP_CB( UX_STEP_CB( ux_712_step_reject, pb, - ui_712_reject_cb(NULL), + ui_712_reject(NULL), { &C_icon_crossmark, "Reject", diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 23d4ed9..d52eaf5 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -10,9 +10,14 @@ #include "eip712.h" // get_struct_name #include "ethUtils.h" // getEthDisplayableAddress #include "utils.h" // uint256_to_decimal +#include "common_712.h" +#include "context.h" // eip712_context_deinit + static t_ui_context *ui_ctx = NULL; + + /** * Called on the intermediate dummy screen between the dynamic step * && the approve/reject screen @@ -43,6 +48,8 @@ void ui_712_next_field(void) /** * Used to notify of a new struct to review (domain or message) + * + * @param[in] struct_ptr pointer to the structure */ void ui_712_new_root_struct(const void *const struct_ptr) { @@ -165,3 +172,37 @@ bool ui_712_init(void) } return ui_ctx != NULL; } + +/** + * Deinit function that simply unsets the struct pointer to NULL + */ +void ui_712_deinit(void) +{ + ui_ctx = NULL; +} + +/** + * Approve button handling, calls the common handler function then + * deinitializes the EIP712 context altogether. + * @param[in] e unused here, just needed to match the UI function signature + * @return unused here, just needed to match the UI function signature + */ +unsigned int ui_712_approve(const bagl_element_t *e) +{ + ui_712_approve_cb(e); + eip712_context_deinit(); + return 0; +} + +/** + * Reject button handling, calls the common handler function then + * deinitializes the EIP712 context altogether. + * @param[in] e unused here, just needed to match the UI function signature + * @return unused here, just needed to match the UI function signature + */ +unsigned int ui_712_reject(const bagl_element_t *e) +{ + ui_712_reject_cb(e); + eip712_context_deinit(); + return 0; +} diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 7e6e82e..c313a9d 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -2,6 +2,7 @@ #define UI_LOGIC_712_H_ #include +#include "ux.h" typedef enum { @@ -17,9 +18,12 @@ typedef struct } t_ui_context; bool ui_712_init(void); +void ui_712_deinit(void); void ui_712_next_field(void); void ui_712_new_root_struct(const void *const struct_ptr); void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); void ui_712_end_sign(void); +unsigned int ui_712_approve(const bagl_element_t *e); +unsigned int ui_712_reject(const bagl_element_t *e); #endif // UI_LOGIC_712_H_ From 59dd149534acbd74687c48cf53dd1354e5ec8ebe Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 13 May 2022 11:57:33 +0200 Subject: [PATCH 064/184] Added some NULL checks for global structure pointers --- src_features/signMessageEIP712/path.c | 49 ++++++++++++++++++++--- src_features/signMessageEIP712/ui_logic.c | 16 ++++++++ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 9cf4116..de8634b 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -28,6 +28,10 @@ static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, uint8_t length; uint8_t fields_count; + if (path_struct == NULL) + { + return NULL; + } if (depth_count > path_struct->depth_count) // sanity check { return NULL; @@ -90,6 +94,10 @@ const void *path_get_field(void) */ static bool path_depth_list_push(void) { + if (path_struct == NULL) + { + return false; + } if (path_struct->depth_count == MAX_PATH_DEPTH) { return false; @@ -106,6 +114,10 @@ static bool path_depth_list_push(void) */ static bool path_depth_list_pop(void) { + if (path_struct == NULL) + { + return false; + } if (path_struct->depth_count == 0) { return false; @@ -175,13 +187,18 @@ static bool path_depth_list_pop(void) */ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) { - s_array_depth *arr = &path_struct->array_depths[path_struct->array_depth_count]; + s_array_depth *arr; + if (path_struct == NULL) + { + return false; + } if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) { return false; } + arr = &path_struct->array_depths[path_struct->array_depth_count]; arr->path_index = path_idx; arr->size = size; path_struct->array_depth_count += 1; @@ -195,6 +212,10 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) */ static bool array_depth_list_pop(void) { + if (path_struct == NULL) + { + return false; + } if (path_struct->array_depth_count == 0) { return false; @@ -233,15 +254,20 @@ static bool array_depth_list_pop(void) static bool path_update(void) { uint8_t fields_count; - const void *struct_ptr = path_struct->root_struct; + const void *struct_ptr; const void *field_ptr; const char *typename; uint8_t typename_len; + if (path_struct == NULL) + { + return false; + } if ((field_ptr = get_field_from_path(NULL)) == NULL) { return false; } + struct_ptr = path_struct->root_struct; while (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &typename_len); @@ -295,7 +321,6 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) { if (path_struct == NULL) { - PRINTF("NULL check failed!\n"); return false; } @@ -374,9 +399,14 @@ static bool check_and_add_array_depth(const void *depth, uint8_t size) { uint8_t expected_size; - uint8_t arr_idx = (total_count - path_struct->array_depth_count) - 1; + uint8_t arr_idx; e_array_type expected_type; + if (path_struct == NULL) + { + return false; + } + arr_idx = (total_count - path_struct->array_depth_count) - 1; // we skip index 0, since we already have it for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) { @@ -413,9 +443,8 @@ bool path_new_array_depth(uint8_t size) uint8_t total_count = 0; uint8_t pidx; - if (path_struct == NULL) // sanity check + if (path_struct == NULL) { - PRINTF("NULL struct check failed\n"); return false; } @@ -484,6 +513,10 @@ static bool path_advance_in_struct(void) uint8_t *depth = &path_struct->depths[path_struct->depth_count - 1]; uint8_t fields_count; + if (path_struct == NULL) + { + return false; + } if ((get_field_from_path(&fields_count)) == NULL) { return false; @@ -510,6 +543,10 @@ static bool path_advance_in_array(void) bool end_reached; s_array_depth *arr_depth; + if (path_struct == NULL) + { + return false; + } do { end_reached = false; diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index d52eaf5..993e0ff 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -24,6 +24,10 @@ static t_ui_context *ui_ctx = NULL; */ void ui_712_next_field(void) { + if (ui_ctx == NULL) + { + return; + } if (!ui_ctx->end_reached) { // reply to previous APDU @@ -53,6 +57,10 @@ void ui_712_next_field(void) */ void ui_712_new_root_struct(const void *const struct_ptr) { + if (ui_ctx == NULL) + { + return; + } strcpy(strings.tmp.tmp2, "Review struct"); const char *struct_name; uint8_t struct_name_length; @@ -84,6 +92,10 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, const char *key; uint8_t key_len; + if (ui_ctx == NULL) + { + return; + } // Key if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { @@ -155,6 +167,10 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, */ void ui_712_end_sign(void) { + if (ui_ctx == NULL) + { + return; + } ui_ctx->end_reached = true; ui_712_next_field(); } From b9820774baae5a69dd3d9f047b9a1a90878ee6af Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 13 May 2022 15:03:10 +0200 Subject: [PATCH 065/184] Replaced the debug printf in the EIP712 signature function by two more relevant ones --- src_features/signMessageEIP712_common/common_712.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712_common/common_712.c b/src_features/signMessageEIP712_common/common_712.c index 93ba048..a2bde33 100644 --- a/src_features/signMessageEIP712_common/common_712.c +++ b/src_features/signMessageEIP712_common/common_712.c @@ -34,7 +34,8 @@ unsigned int ui_712_approve_cb(const bagl_element_t *e) sizeof(tmpCtx.messageSigningContext712.messageHash), hash, sizeof(hash)); - PRINTF("EIP712 hash to sign %.*H\n", 32, hash); + PRINTF("EIP712 Domain hash 0x%.*h\n", 32, tmpCtx.messageSigningContext712.domainHash); + PRINTF("EIP712 Message hash 0x%.*h\n", 32, tmpCtx.messageSigningContext712.messageHash); io_seproxyhal_io_heartbeat(); os_perso_derive_node_bip32(CX_CURVE_256K1, tmpCtx.messageSigningContext712.bip32.path, From 2fa6379470b0dadefc519910193d9c062d8e0dd9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 May 2022 10:59:20 +0200 Subject: [PATCH 066/184] Now only compiles full EIP-712 support for capable devices --- Makefile | 5 +++++ src/main.c | 6 ++++++ src_common/ethUtils.h | 2 ++ src_features/signMessageEIP712/context.c | 4 ++++ src_features/signMessageEIP712/context.h | 5 ++++- src_features/signMessageEIP712/eip712.h | 6 ++++-- src_features/signMessageEIP712/encode_field.c | 4 ++++ src_features/signMessageEIP712/encode_field.h | 4 ++++ src_features/signMessageEIP712/entrypoint.c | 5 +++++ src_features/signMessageEIP712/field_hash.c | 5 +++++ src_features/signMessageEIP712/field_hash.h | 5 +++++ src_features/signMessageEIP712/mem.c | 5 ++++- src_features/signMessageEIP712/mem.h | 4 ++++ src_features/signMessageEIP712/mem_utils.c | 4 ++++ src_features/signMessageEIP712/mem_utils.h | 4 ++++ src_features/signMessageEIP712/path.c | 4 ++++ src_features/signMessageEIP712/path.h | 4 ++++ src_features/signMessageEIP712/sol_typenames.c | 4 ++++ src_features/signMessageEIP712/sol_typenames.h | 4 ++++ src_features/signMessageEIP712/type_hash.c | 5 +++++ src_features/signMessageEIP712/type_hash.h | 4 ++++ src_features/signMessageEIP712/ui_flow_712.c | 4 ++++ src_features/signMessageEIP712/ui_flow_712.h | 4 ++++ src_features/signMessageEIP712/ui_logic.c | 4 ++++ src_features/signMessageEIP712/ui_logic.h | 4 ++++ src_features/signMessageEIP712_v0/cmd_signMessage712.c | 6 ++++-- 26 files changed, 109 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 05646f3..872bc15 100644 --- a/Makefile +++ b/Makefile @@ -137,6 +137,11 @@ DEFINES += HAVE_NFT_TESTING_KEY endif endif +# EIP-712 +ifneq ($(TARGET_NAME),TARGET_NANOS) +DEFINES += HAVE_EIP712_FULL_SUPPORT +endif + # Enabling debug PRINTF DEBUG:=0 ifneq ($(DEBUG),0) diff --git a/src/main.c b/src/main.c index 09f8eb2..85c49e2 100644 --- a/src/main.c +++ b/src/main.c @@ -675,8 +675,12 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { } else { +#ifdef HAVE_EIP712_FULL_SUPPORT *flags |= IO_ASYNCH_REPLY; handle_eip712_sign(G_io_apdu_buffer); +#else + THROW(0x6B00); +#endif // HAVE_EIP712_FULL_SUPPORT } break; @@ -703,6 +707,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { #endif +#ifdef HAVE_EIP712_FULL_SUPPORT case INS_EIP712_STRUCT_DEF: *flags |= IO_ASYNCH_REPLY; handle_eip712_struct_def(G_io_apdu_buffer); @@ -712,6 +717,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { *flags |= IO_ASYNCH_REPLY; handle_eip712_struct_impl(G_io_apdu_buffer); break; +#endif // HAVE_EIP712_FULL_SUPPORT #if 0 case 0xFF: // return to dashboard diff --git a/src_common/ethUtils.h b/src_common/ethUtils.h index 3c147cc..0755b13 100644 --- a/src_common/ethUtils.h +++ b/src_common/ethUtils.h @@ -23,6 +23,8 @@ #include "cx.h" #include "chainConfig.h" +#define KECCAK256_HASH_BYTESIZE 32 + /** * @brief Decode an RLP encoded field - see * https://github.com/ethereum/wiki/wiki/RLP diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index dbea2d6..27a937e 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include "context.h" @@ -64,3 +66,5 @@ void eip712_context_deinit(void) mem_reset(); eip712_context_initialized = false; } + +#endif diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 82aff85..a85f56a 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -1,8 +1,9 @@ #ifndef EIP712_CTX_H_ #define EIP712_CTX_H_ -#include +#ifdef HAVE_EIP712_FULL_SUPPORT +#include extern uint8_t *typenames_array; extern uint8_t *structs_array; @@ -13,4 +14,6 @@ void eip712_context_deinit(void); extern bool eip712_context_initialized; +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index 70a5ffe..98a7b05 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -1,6 +1,8 @@ #ifndef EIP712_H_ #define EIP712_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -51,8 +53,6 @@ typedef enum EIP712_STRUCT_HASH } e_eip712_hash_type; -#define KECCAK256_HASH_BYTESIZE 32 - #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define DOMAIN_STRUCT_NAME "EIP712Domain" @@ -84,4 +84,6 @@ const uint8_t *get_structn(const uint8_t *const ptr, const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index fea2a26..4702fa4 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include "encode_field.h" @@ -108,3 +110,5 @@ void *encode_address(const uint8_t *const value, uint8_t length) } return encode_integer(value, length); } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index acc925c..6bb58e4 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -1,6 +1,8 @@ #ifndef ENCODE_FIELD_H_ #define ENCODE_FIELD_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -11,4 +13,6 @@ void *encode_boolean(const bool *const value, uint8_t length); void *encode_address(const uint8_t *const value, uint8_t length); void *encode_bytes(const uint8_t *const value, uint8_t length); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 5b67a4f..e744ff1 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include @@ -449,3 +451,6 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) ui_712_end_sign(); return true; } + + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index c8c1fb1..ef019ac 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include "field_hash.h" #include "encode_field.h" @@ -7,6 +9,7 @@ #include "eip712.h" #include "shared_context.h" #include "ui_logic.h" +#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE static s_field_hashing *fh = NULL; @@ -170,3 +173,5 @@ bool field_hash(const uint8_t *data, return true; } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index 6a3fb4a..f184792 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -1,6 +1,8 @@ #ifndef FIELD_HASH_H_ #define FIELD_HASH_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -23,4 +25,7 @@ void field_hash_deinit(void); bool field_hash(const uint8_t *data, uint8_t data_length, bool partial); + +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // FIELD_HASH_H_ diff --git a/src_features/signMessageEIP712/mem.c b/src_features/signMessageEIP712/mem.c index e0c61c9..e4c01c4 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_features/signMessageEIP712/mem.c @@ -1,8 +1,9 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include "mem.h" #include "shared_context.h" - #define SIZE_MEM_BUFFER 5120 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; @@ -75,3 +76,5 @@ void mem_dealloc(size_t size) mem_idx -= size; } } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/mem.h b/src_features/signMessageEIP712/mem.h index d91f57f..0cb9aca 100644 --- a/src_features/signMessageEIP712/mem.h +++ b/src_features/signMessageEIP712/mem.h @@ -1,6 +1,8 @@ #ifndef MEM_H_ #define MEM_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include void mem_init(void); @@ -12,4 +14,6 @@ void mem_dealloc(size_t size); extern size_t mem_max; #endif +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // MEM_H_ diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index d8b5cf8..09d98cd 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include @@ -77,3 +79,5 @@ void *mem_alloc_and_align(size_t size, size_t alignment) } return mem_alloc(size); } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h index 8a89c5b..f339a92 100644 --- a/src_features/signMessageEIP712/mem_utils.h +++ b/src_features/signMessageEIP712/mem_utils.h @@ -1,6 +1,8 @@ #ifndef MEM_UTILS_H_ #define MEM_UTILS_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -11,4 +13,6 @@ void *mem_alloc_and_copy(const void *data, size_t size); char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); void *mem_alloc_and_align(size_t size, size_t alignment); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // MEM_UTILS_H_ diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index de8634b..28fd41b 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include "path.h" @@ -614,3 +616,5 @@ void path_deinit(void) { path_struct = NULL; } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 030e369..b8e0ad6 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -1,6 +1,8 @@ #ifndef PATH_H_ #define PATH_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -36,4 +38,6 @@ bool path_init(void); void path_deinit(void); bool path_new_array_depth(uint8_t size); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // PATH_H_ diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index f4fc562..b9cbf53 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include "sol_typenames.h" @@ -130,3 +132,5 @@ const char *get_struct_field_sol_typename(const uint8_t *field_ptr, } return NULL; // Not found } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/sol_typenames.h b/src_features/signMessageEIP712/sol_typenames.h index bb8e259..77f744c 100644 --- a/src_features/signMessageEIP712/sol_typenames.h +++ b/src_features/signMessageEIP712/sol_typenames.h @@ -1,6 +1,8 @@ #ifndef SOL_TYPENAMES_H_ #define SOL_TYPENAMES_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include @@ -9,4 +11,6 @@ bool sol_typenames_init(void); const char *get_struct_field_sol_typename(const uint8_t *ptr, uint8_t *const length); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // SOL_TYPENAMES_H_ diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 203608c..f9b28ca 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include @@ -6,6 +8,7 @@ #include "eip712.h" #include "type_hash.h" #include "shared_context.h" +#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE static inline void hash_nbytes(const uint8_t *b, uint8_t n) { @@ -316,3 +319,5 @@ const uint8_t *type_hash(const void *const structs_array, KECCAK256_HASH_BYTESIZE); return hash_ptr; } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index e40ba7a..7fe8bd2 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -1,10 +1,14 @@ #ifndef TYPE_HASH_H_ #define TYPE_HASH_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include const uint8_t *type_hash(const void *const structs_array, const char *const struct_name, const uint8_t struct_name_length); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // TYPE_HASH_H_ diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_features/signMessageEIP712/ui_flow_712.c index 37d97f1..7d06bd6 100644 --- a/src_features/signMessageEIP712/ui_flow_712.c +++ b/src_features/signMessageEIP712/ui_flow_712.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include "ui_flow_712.h" #include "ui_logic.h" #include "shared_context.h" // strings @@ -51,3 +53,5 @@ UX_FLOW(ux_712_flow, &ux_712_step_dummy, &ux_712_step_approve, &ux_712_step_reject); + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_flow_712.h b/src_features/signMessageEIP712/ui_flow_712.h index 46f3120..3ace335 100644 --- a/src_features/signMessageEIP712/ui_flow_712.h +++ b/src_features/signMessageEIP712/ui_flow_712.h @@ -1,8 +1,12 @@ #ifndef UI_FLOW_712_H_ #define UI_FLOW_712_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include "ux_flow_engine.h" extern const ux_flow_step_t* const ux_712_flow[]; +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // UI_FLOW_712_H_ diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 993e0ff..e6048ce 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -1,3 +1,5 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include #include "ui_logic.h" @@ -222,3 +224,5 @@ unsigned int ui_712_reject(const bagl_element_t *e) eip712_context_deinit(); return 0; } + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index c313a9d..acfb52c 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -1,6 +1,8 @@ #ifndef UI_LOGIC_712_H_ #define UI_LOGIC_712_H_ +#ifdef HAVE_EIP712_FULL_SUPPORT + #include #include "ux.h" @@ -26,4 +28,6 @@ void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); +#endif // HAVE_EIP712_FULL_SUPPORT + #endif // UI_LOGIC_712_H_ diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index 09e3b30..6865b3a 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -4,6 +4,7 @@ #include "ui_flow.h" #include "eip712.h" #include "common_712.h" +#include "ethUtils.h" void handleSignEIP712Message_v0(uint8_t p1, uint8_t p2, @@ -11,8 +12,9 @@ void handleSignEIP712Message_v0(uint8_t p1, uint8_t dataLength, unsigned int *flags, unsigned int *tx) { - UNUSED(tx); - if ((p1 != 00) || (p2 != 00)) { + (void)tx; + (void)p2; + if (p1 != 00) { THROW(0x6B00); } if (appState != APP_STATE_IDLE) { From a0551e822621ce1f2b8ac371e8a24d35a7e7a87d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 May 2022 15:15:00 +0200 Subject: [PATCH 067/184] Added a function to check if we are in the domain or the message struct --- src_features/signMessageEIP712/path.c | 9 +++++++++ src_features/signMessageEIP712/path.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 28fd41b..1b199a2 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -598,6 +598,15 @@ bool path_advance(void) return true; } +e_root_type path_get_root_type(void) +{ + if (path_struct == NULL) + { + return ROOT_DOMAIN; + } + return path_struct->root_type; +} + /** * Allocates the path indexes in memory and sets it with a depth of 0. * diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index b8e0ad6..e5a05c1 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -37,6 +37,7 @@ bool path_advance(void); bool path_init(void); void path_deinit(void); bool path_new_array_depth(uint8_t size); +e_root_type path_get_root_type(void); #endif // HAVE_EIP712_FULL_SUPPORT From 239da44d0e63ab436249308a8ef928b75bd427e4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 16 May 2022 16:06:52 +0200 Subject: [PATCH 068/184] Added a new temporary EIP-712 half-bind mode --- Makefile | 1 + src_features/signMessageEIP712/entrypoint.c | 3 +++ src_features/signMessageEIP712/field_hash.c | 23 +++++++++++++++++++ src_features/signMessageEIP712/path.c | 14 +++++++++++ src_features/signMessageEIP712/ui_logic.c | 19 +++++++++++++++ src_features/signMessageEIP712/ui_logic.h | 3 +++ .../ui_flow_signMessage712.c | 13 +++++++++-- 7 files changed, 74 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 872bc15..d2c0b32 100644 --- a/Makefile +++ b/Makefile @@ -140,6 +140,7 @@ endif # EIP-712 ifneq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_EIP712_FULL_SUPPORT +#DEFINES += HAVE_EIP712_HALF_BLIND # temporary endif # Enabling debug PRINTF diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index e744ff1..cb4eb34 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -448,6 +448,9 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) { return false; } +#ifdef HAVE_EIP712_HALF_BLIND + ui_712_message_hash(); +#endif // HAVE_EIP712_HALF_BLIND ui_712_end_sign(); return true; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index ef019ac..6dde239 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -66,7 +66,14 @@ bool field_hash(const uint8_t *data, if (IS_DYN(field_type)) { cx_keccak_init(&global_sha3, 256); // init hash +#ifdef HAVE_EIP712_HALF_BLIND + if (path_get_root_type() == ROOT_DOMAIN) + { +#endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); +#ifdef HAVE_EIP712_HALF_BLIND + } +#endif // HAVE_EIP712_HALF_BLIND } } fh->remaining_size -= data_length; @@ -123,7 +130,14 @@ bool field_hash(const uint8_t *data, { return false; } +#ifdef HAVE_EIP712_HALF_BLIND + if (path_get_root_type() == ROOT_DOMAIN) + { +#endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); +#ifdef HAVE_EIP712_HALF_BLIND + } +#endif // HAVE_EIP712_HALF_BLIND } else { @@ -159,6 +173,15 @@ bool field_hash(const uint8_t *data, path_advance(); fh->state = FHS_IDLE; +#ifdef HAVE_EIP712_HALF_BLIND + if (path_get_root_type() == ROOT_MESSAGE) + { + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } +#endif // HAVE_EIP712_HALF_BLIND + } else { diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 1b199a2..7638d31 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -382,7 +382,21 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // because the first field could be a struct type path_update(); +#ifdef HAVE_EIP712_HALF_BLIND + // Only show field for the domain + if (path_get_root_type() == ROOT_MESSAGE) + { + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } + else + { +#endif // HAVE_EIP712_HALF_BLIND ui_712_new_root_struct(path_struct->root_struct); +#ifdef HAVE_EIP712_HALF_BLIND + } +#endif // HAVE_EIP712_HALF_BLIND return true; } diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index e6048ce..67869bc 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -63,6 +63,7 @@ void ui_712_new_root_struct(const void *const struct_ptr) { return; } + strcpy(strings.tmp.tmp2, "Review struct"); const char *struct_name; uint8_t struct_name_length; @@ -82,6 +83,20 @@ void ui_712_new_root_struct(const void *const struct_ptr) } } +#ifdef HAVE_EIP712_HALF_BLIND +void ui_712_message_hash(void) +{ + strcpy(strings.tmp.tmp2, "Message hash"); + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + KECCAK256_HASH_BYTESIZE, + tmpCtx.messageSigningContext712.messageHash); + G_ux.flow_stack[G_ux.stack_count - 1].index = 0; + ux_flow_next(); +} +#endif // HAVE_EIP712_HALF_BLIND + /** * Used to notify of a new field to review in the current struct (key + value) * @@ -98,6 +113,7 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, { return; } + // Key if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { @@ -174,7 +190,10 @@ void ui_712_end_sign(void) return; } ui_ctx->end_reached = true; + +#ifndef HAVE_EIP712_HALF_BLIND ui_712_next_field(); +#endif // HAVE_EIP712_HALF_BLIND } /** diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index acfb52c..0458256 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -27,6 +27,9 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, ui void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); +#ifdef HAVE_EIP712_HALF_BLIND +void ui_712_message_hash(void); +#endif // HAVE_EIP712_HALF_BLIND #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c b/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c index 02e9e14..230852e 100644 --- a/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c +++ b/src_features/signMessageEIP712_v0/ui_flow_signMessage712.c @@ -1,13 +1,22 @@ #include "shared_context.h" #include "ui_callbacks.h" #include "common_712.h" +#include "ethUtils.h" void prepare_domain_hash_v0() { - snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, tmpCtx.messageSigningContext712.domainHash); + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + KECCAK256_HASH_BYTESIZE, + tmpCtx.messageSigningContext712.domainHash); } void prepare_message_hash_v0() { - snprintf(strings.tmp.tmp, 70, "0x%.*H", 32, tmpCtx.messageSigningContext712.messageHash); + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + KECCAK256_HASH_BYTESIZE, + tmpCtx.messageSigningContext712.messageHash); } // clang-format off From f0d6a2bba19e15570b45915ce6572ba60211d445 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 18 May 2022 15:00:43 +0200 Subject: [PATCH 069/184] Now filters out all fields but the name in the EIP712Domain in the half-blind mode --- src_features/signMessageEIP712/field_hash.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 6dde239..da1a997 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -56,6 +56,10 @@ bool field_hash(const uint8_t *data, { return false; } +#ifdef HAVE_EIP712_HALF_BLIND + uint8_t keylen; + const char *key = get_struct_field_keyname(field_ptr, &keylen); +#endif // HAVE_EIP712_HALF_BLIND field_type = struct_field_type(field_ptr); if (fh->state == FHS_IDLE) // first packet for this frame { @@ -69,9 +73,12 @@ bool field_hash(const uint8_t *data, #ifdef HAVE_EIP712_HALF_BLIND if (path_get_root_type() == ROOT_DOMAIN) { + if ((keylen == 4) && (strncmp(key, "name", keylen) == 0)) + { #endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); #ifdef HAVE_EIP712_HALF_BLIND + } } #endif // HAVE_EIP712_HALF_BLIND } @@ -133,9 +140,14 @@ bool field_hash(const uint8_t *data, #ifdef HAVE_EIP712_HALF_BLIND if (path_get_root_type() == ROOT_DOMAIN) { + uint8_t keylen; + const char *key = get_struct_field_keyname(field_ptr, &keylen); + if ((keylen == 4) && (strncmp(key, "name", keylen) == 0)) + { #endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); #ifdef HAVE_EIP712_HALF_BLIND + } } #endif // HAVE_EIP712_HALF_BLIND } @@ -174,7 +186,8 @@ bool field_hash(const uint8_t *data, path_advance(); fh->state = FHS_IDLE; #ifdef HAVE_EIP712_HALF_BLIND - if (path_get_root_type() == ROOT_MESSAGE) + if ((path_get_root_type() == ROOT_MESSAGE) || + ((keylen != 4) || (strncmp(key, "name", keylen) != 0))) { G_io_apdu_buffer[0] = 0x90; G_io_apdu_buffer[1] = 0x00; From b2fc45e295db7a8e797e4a279c26b5fe80fad34a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 17 May 2022 11:35:03 +0200 Subject: [PATCH 070/184] Reordered the APDUs by INS values in the doc Also changed from CRLF encoding to simple LF --- doc/ethapp.asc | 1363 ++++++++++++++++++++++++------------------------ 1 file changed, 684 insertions(+), 679 deletions(-) diff --git a/doc/ethapp.asc b/doc/ethapp.asc index 839e70b..9f2e419 100644 --- a/doc/ethapp.asc +++ b/doc/ethapp.asc @@ -1,679 +1,684 @@ -Ethereum application : Common Technical Specifications -======================================================= -Ledger Firmware Team -Application version 1.9.18 - 29th of January 2022 - -## 1.0 - - Initial release - -## 1.1 - - Add GET APP CONFIGURATION - - Add an option to return the chain code in GET ETH PUBLIC ADDRESS - -## 1.2 - - Add SIGN ETH PERSONAL MESSAGE - -## 1.1.10 - - Add PROVIDE ERC 20 TOKEN INFORMATION - -## 1.5.0 - - Add SIGN ETH EIP 712 - - Add GET ETH2 PUBLIC KEY - -## 1.7.6 - - Add SET EXTERNAL PLUGIN - -## 1.9.13 - - Add SET PLUGIN - -## 1.9.17 - - Add PERFORM PRIVACY OPERATION - -## About - -This application describes the APDU messages interface to communicate with the Ethereum application. - -The application covers the following functionalities : - - - Retrieve a public Ethereum address given a BIP 32 path - - Sign a basic Ethereum transaction given a BIP 32 path - - Provide callbacks to validate the data associated to an Ethereum transaction - -The application interface can be accessed over HID or BLE - -## General purpose APDUs - -### GET ETH PUBLIC ADDRESS - -#### Description - -This command returns the public key and Ethereum address for the given BIP 32 path. - -The address can be optionally checked on the device before being returned. - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 02 | 00 : return address - - 01 : display address and confirm before returning - | 00 : do not return the chain code - - 01 : return the chain code | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Public Key length | 1 -| Uncompressed Public Key | var -| Ethereum address length | 1 -| Ethereum address | var -| Chain code if requested | 32 -|============================================================================================================================== - - -### SIGN ETH TRANSACTION - -#### Description - -https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md - -This command signs an Ethereum transaction after having the user validate the following parameters - - - Gas price - - Gas limit - - Recipient address - - Value - -The input data is the RLP encoded transaction (as per https://github.com/ethereum/pyethereum/blob/develop/ethereum/transactions.py#L22), without v/r/s present, streamed to the device in 255 bytes maximum data chunks. - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 04 | 00 : first transaction data block - - 80 : subsequent transaction data block - | 00 | variable | variable -|============================================================================================================================== - -'Input data (first transaction data block)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| RLP transaction chunk | variable -|============================================================================================================================== - -'Input data (other transaction data block)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| RLP transaction chunk | variable -|============================================================================================================================== - - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| v | 1 -| r | 32 -| s | 32 -|============================================================================================================================== - - - -### SIGN ETH PERSONAL MESSAGE - -#### Description - -This command signs an Ethereum message following the personal_sign specification (https://github.com/ethereum/go-ethereum/pull/2940) after having the user validate the SHA-256 hash of the message being signed. - -This command has been supported since firmware version 1.0.8 - -The input data is the message to sign, streamed to the device in 255 bytes maximum data chunks - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 08 | 00 : first message data block - - 80 : subsequent message data block - | 00 | variable | variable -|============================================================================================================================== - -'Input data (first message data block)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Message length | 4 -| Message chunk | variable -|============================================================================================================================== - -'Input data (other transaction data block)' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Message chunk | variable -|============================================================================================================================== - - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| v | 1 -| r | 32 -| s | 32 -|============================================================================================================================== - - -### PROVIDE ERC 20 TOKEN INFORMATION - -#### Description - -This commands provides a trusted description of an ERC 20 token to associate a contract address with a ticker and number of decimals. - -It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper token information to the user if necessary, as marked in GET APP CONFIGURATION flags. - -The signature is computed on - -ticker || address || number of decimals (uint4be) || chainId (uint4be) - -signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f0976b22d3388a9a5e446ac62d25cf725b62a2555b2dd464a4da0ab2f4d506820543af1d242470b1b1a969a27578f353 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 0A | 00 | 00 | variable | 00 -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Length of ERC 20 ticker | 1 -| ERC 20 ticker | variable -| ERC 20 contract address | 20 -| Number of decimals (big endian encoded) | 4 -| Chain ID (big endian encoded) | 4 -| Token information signature | variable -|============================================================================================================================== - -'Output data' - -None - -### PROVIDE NFT INFORMATION - -#### Description - -This commands provides a trusted description of an NFT to associate a contract address with a collectionName. - -It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper nft information to the user if necessary, as marked in GET APP CONFIGURATION flags. - -The signature is computed on: - -type || version || len(collectionName) || collectionName || address || chainId || keyId || algorithmId - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 14 | 00 | 00 | variable | 00 -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Type | 1 -| Version | 1 -| Collection Name Length | 1 -| Collection Name | variable -| Address | 20 -| Chain ID | 8 -| KeyID | 1 -| Algorithm ID | 1 -| Signature Length | 1 -| Signature | variable -|============================================================================================================================== - -'Output data' - -None - - -### SET EXTERNAL PLUGIN - -#### Description - -This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. - -It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. - -The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise. - -The signature is computed on - -len(pluginName) || pluginName || contractAddress || methodSelector - -signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f0976b22d3388a9a5e446ac62d25cf725b62a2555b2dd464a4da0ab2f4d506820543af1d242470b1b1a969a27578f353 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 12 | 00 | 00 | variable | 00 -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Length of plugin name | 1 -| plugin name | variable -| contract address | 20 -| method selector | 4 -| signature | variable -|============================================================================================================================== - -'Output data' - -None - -### SET PLUGIN - -#### Description - -This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. - -It can be used to set both internal and external plugins. - -It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. - -The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise. - -The plugin names `ERC20`, `ERC721` and `ERC1155` are reserved. Additional plugin names might be added to this list in the future. - -The signature is computed on - -type || version || len(pluginName) || pluginName || address || selector || chainId || keyId || algorithmId || len(signature) || signature - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 16 | 00 | 00 | variable | 00 -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Type | 1 -| Version | 1 -| Plugin Name Length | 1 -| Plugin Name | variable -| Address | 20 -| Selector | 4 -| Chain ID | 8 -| KeyID | 1 -| Algorithm ID | 1 -| Signature Length | 1 -| Signature | variable -|============================================================================================================================== - -'Output data' - -None - -### PERFORM PRIVACY OPERATION - -#### Description - -This command performs privacy operations as defined in EIP 1024 (https://ethereum-magicians.org/t/eip-1024-cross-client-encrypt-decrypt/505) - -It can return the public encryption key on Curve25519 for a given Ethereum account or the shared secret (generated by the scalar multiplication of the remote public key by the account private key on Curve25519) used to decrypt private data encrypted for a given Ethereum account - -All data can be optionally checked on the device before being returned. - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 18 | 00 : return data - - 01 : display data and confirm before returning - | 00 : return the public encryption key - - 01 : return the shared secret | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Third party public key on Curve25519, if returning the shared secret | 32 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Public encryption key or shared secret | 32 -|============================================================================================================================== - - -### GET APP CONFIGURATION - -#### Description - -This command returns specific application configuration - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 06 | 00 | 00 | 00 | 04 -|============================================================================================================================== - -'Input data' - -None - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Flags - 0x01 : arbitrary data signature enabled by user - - 0x02 : ERC 20 Token information needs to be provided externally - | 01 -| Application major version | 01 -| Application minor version | 01 -| Application patch version | 01 -|============================================================================================================================== - -### SIGN ETH EIP 712 - -#### Description - -This command signs an Ethereum message following the EIP 712 specification (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) - -For implementation version 0, the domain hash and message hash are provided to the device, which displays them and returns the signature - -This command has been supported since firmware version 1.5.0 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 0C | 00 - | implementation version : 00 | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -| Domain hash | 32 -| Message hash | 32 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| v | 1 -| r | 32 -| s | 32 -|============================================================================================================================== - -### GET ETH2 PUBLIC KEY - -#### Description - -This command returns an Ethereum 2 BLS12-381 public key derived following EIP 2333 specification (https://eips.ethereum.org/EIPS/eip-2333) - -This command has been supported since firmware version 1.6.0 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 0E | 00 : return public key - - 01 : display public key and confirm before returning - | 00 | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Number of BIP 32 derivations to perform (max 10) | 1 -| First derivation index (big endian) | 4 -| ... | 4 -| Last derivation index (big endian) | 4 -|============================================================================================================================== - -'Output data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Public key | 48 -|============================================================================================================================== - -### SET ETH2 WITHDRAWAL INDEX - -#### Description - -This command sets the index of the Withdrawal key used as withdrawal credentials in an ETH2 deposit contract call signature. The path of the Withdrawal key is defined as m/12381/3600/index/0 according to EIP 2334 (https://eips.ethereum.org/EIPS/eip-2334) - -The default index used is 0 if this method isn't called before the deposit contract transaction is sent to the device to be signed - -This command has been supported since firmware version 1.5.0 - -#### Coding - -'Command' - -[width="80%"] -|============================================================================================================================== -| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* -| E0 | 10 | 00 - | 00 | variable | variable -|============================================================================================================================== - -'Input data' - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Withdrawal key index (big endian) | 4 -|============================================================================================================================== - -'Output data' - -None - -## Transport protocol - -### General transport description - -Ledger APDUs requests and responses are encapsulated using a flexible protocol allowing to fragment large payloads over different underlying transport mechanisms. - -The common transport header is defined as follows : - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| Communication channel ID (big endian) | 2 -| Command tag | 1 -| Packet sequence index (big endian) | 2 -| Payload | var -|============================================================================================================================== - -The Communication channel ID allows commands multiplexing over the same physical link. It is not used for the time being, and should be set to 0101 to avoid compatibility issues with implementations ignoring a leading 00 byte. - -The Command tag describes the message content. Use TAG_APDU (0x05) for standard APDU payloads, or TAG_PING (0x02) for a simple link test. - -The Packet sequence index describes the current sequence for fragmented payloads. The first fragment index is 0x00. - -### APDU Command payload encoding - -APDU Command payloads are encoded as follows : - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| APDU length (big endian) | 2 -| APDU CLA | 1 -| APDU INS | 1 -| APDU P1 | 1 -| APDU P2 | 1 -| APDU length | 1 -| Optional APDU data | var -|============================================================================================================================== - -APDU payload is encoded according to the APDU case - -[width="80%"] -|======================================================================================= -| Case Number | *Lc* | *Le* | Case description -| 1 | 0 | 0 | No data in either direction - L is set to 00 -| 2 | 0 | !0 | Input Data present, no Output Data - L is set to Lc -| 3 | !0 | 0 | Output Data present, no Input Data - L is set to Le -| 4 | !0 | !0 | Both Input and Output Data are present - L is set to Lc -|======================================================================================= - -### APDU Response payload encoding - -APDU Response payloads are encoded as follows : - -[width="80%"] -|============================================================================================================================== -| *Description* | *Length* -| APDU response length (big endian) | 2 -| APDU response data and Status Word | var -|============================================================================================================================== - -### USB mapping - -Messages are exchanged with the dongle over HID endpoints over interrupt transfers, with each chunk being 64 bytes long. The HID Report ID is ignored. - -### BLE mapping - -A similar encoding is used over BLE, without the Communication channel ID. - -The application acts as a GATT server defining service UUID D973F2E0-B19E-11E2-9E96-0800200C9A66 - -When using this service, the client sends requests to the characteristic D973F2E2-B19E-11E2-9E96-0800200C9A66, and gets notified on the characteristic D973F2E1-B19E-11E2-9E96-0800200C9A66 after registering for it. - -Requests are encoded using the standard BLE 20 bytes MTU size - -## Status Words - -The following standard Status Words are returned for all APDUs - some specific Status Words can be used for specific commands and are mentioned in the command description. - -'Status Words' - -[width="80%"] -|=============================================================================================== -| *SW* | *Description* -| 6501 | TransactionType not supported -| 6502 | Output buffer too small for chainId conversion -| 6503 | Plugin error -| 6504 | Failed to convert from int256 -| 6700 | Incorrect length -| 6982 | Security status not satisfied (Canceled by user) -| 6A80 | Invalid data -| 6B00 | Incorrect parameter P1 or P2 -| 6Fxx | Technical problem (Internal error, please report) -| 9000 | Normal ending of the command -|=============================================================================================== +Ethereum application : Common Technical Specifications +======================================================= +Ledger Firmware Team +Application version 1.9.18 - 29th of January 2022 + +## 1.0 + - Initial release + +## 1.1 + - Add GET APP CONFIGURATION + - Add an option to return the chain code in GET ETH PUBLIC ADDRESS + +## 1.2 + - Add SIGN ETH PERSONAL MESSAGE + +## 1.1.10 + - Add PROVIDE ERC 20 TOKEN INFORMATION + +## 1.5.0 + - Add SIGN ETH EIP 712 + - Add GET ETH2 PUBLIC KEY + +## 1.7.6 + - Add SET EXTERNAL PLUGIN + +## 1.9.13 + - Add SET PLUGIN + +## 1.9.17 + - Add PERFORM PRIVACY OPERATION + +## About + +This application describes the APDU messages interface to communicate with the Ethereum application. + +The application covers the following functionalities : + + - Retrieve a public Ethereum address given a BIP 32 path + - Sign a basic Ethereum transaction given a BIP 32 path + - Provide callbacks to validate the data associated to an Ethereum transaction + +The application interface can be accessed over HID or BLE + +## General purpose APDUs + +### GET ETH PUBLIC ADDRESS + +#### Description + +This command returns the public key and Ethereum address for the given BIP 32 path. + +The address can be optionally checked on the device before being returned. + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 02 | 00 : return address + + 01 : display address and confirm before returning + | 00 : do not return the chain code + + 01 : return the chain code | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +|============================================================================================================================== + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Public Key length | 1 +| Uncompressed Public Key | var +| Ethereum address length | 1 +| Ethereum address | var +| Chain code if requested | 32 +|============================================================================================================================== + + +### SIGN ETH TRANSACTION + +#### Description + +https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md + +This command signs an Ethereum transaction after having the user validate the following parameters + + - Gas price + - Gas limit + - Recipient address + - Value + +The input data is the RLP encoded transaction (as per https://github.com/ethereum/pyethereum/blob/develop/ethereum/transactions.py#L22), without v/r/s present, streamed to the device in 255 bytes maximum data chunks. + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 04 | 00 : first transaction data block + + 80 : subsequent transaction data block + | 00 | variable | variable +|============================================================================================================================== + +'Input data (first transaction data block)' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| RLP transaction chunk | variable +|============================================================================================================================== + +'Input data (other transaction data block)' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| RLP transaction chunk | variable +|============================================================================================================================== + + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| v | 1 +| r | 32 +| s | 32 +|============================================================================================================================== + + +### GET APP CONFIGURATION + +#### Description + +This command returns specific application configuration + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 06 | 00 | 00 | 00 | 04 +|============================================================================================================================== + +'Input data' + +None + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Flags + 0x01 : arbitrary data signature enabled by user + + 0x02 : ERC 20 Token information needs to be provided externally + | 01 +| Application major version | 01 +| Application minor version | 01 +| Application patch version | 01 +|============================================================================================================================== + + +### SIGN ETH PERSONAL MESSAGE + +#### Description + +This command signs an Ethereum message following the personal_sign specification (https://github.com/ethereum/go-ethereum/pull/2940) after having the user validate the SHA-256 hash of the message being signed. + +This command has been supported since firmware version 1.0.8 + +The input data is the message to sign, streamed to the device in 255 bytes maximum data chunks + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 08 | 00 : first message data block + + 80 : subsequent message data block + | 00 | variable | variable +|============================================================================================================================== + +'Input data (first message data block)' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| Message length | 4 +| Message chunk | variable +|============================================================================================================================== + +'Input data (other transaction data block)' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Message chunk | variable +|============================================================================================================================== + + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| v | 1 +| r | 32 +| s | 32 +|============================================================================================================================== + + +### PROVIDE ERC 20 TOKEN INFORMATION + +#### Description + +This commands provides a trusted description of an ERC 20 token to associate a contract address with a ticker and number of decimals. + +It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper token information to the user if necessary, as marked in GET APP CONFIGURATION flags. + +The signature is computed on + +ticker || address || number of decimals (uint4be) || chainId (uint4be) + +signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f0976b22d3388a9a5e446ac62d25cf725b62a2555b2dd464a4da0ab2f4d506820543af1d242470b1b1a969a27578f353 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 0A | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Length of ERC 20 ticker | 1 +| ERC 20 ticker | variable +| ERC 20 contract address | 20 +| Number of decimals (big endian encoded) | 4 +| Chain ID (big endian encoded) | 4 +| Token information signature | variable +|============================================================================================================================== + +'Output data' + +None + + +### SIGN ETH EIP 712 + +#### Description + +This command signs an Ethereum message following the EIP 712 specification (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) + +For implementation version 0, the domain hash and message hash are provided to the device, which displays them and returns the signature + +This command has been supported since firmware version 1.5.0 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 0C | 00 + | implementation version : 00 | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| Domain hash | 32 +| Message hash | 32 +|============================================================================================================================== + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| v | 1 +| r | 32 +| s | 32 +|============================================================================================================================== + + +### GET ETH2 PUBLIC KEY + +#### Description + +This command returns an Ethereum 2 BLS12-381 public key derived following EIP 2333 specification (https://eips.ethereum.org/EIPS/eip-2333) + +This command has been supported since firmware version 1.6.0 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 0E | 00 : return public key + + 01 : display public key and confirm before returning + | 00 | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +|============================================================================================================================== + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Public key | 48 +|============================================================================================================================== + + +### SET ETH2 WITHDRAWAL INDEX + +#### Description + +This command sets the index of the Withdrawal key used as withdrawal credentials in an ETH2 deposit contract call signature. The path of the Withdrawal key is defined as m/12381/3600/index/0 according to EIP 2334 (https://eips.ethereum.org/EIPS/eip-2334) + +The default index used is 0 if this method isn't called before the deposit contract transaction is sent to the device to be signed + +This command has been supported since firmware version 1.5.0 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 10 | 00 + | 00 | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Withdrawal key index (big endian) | 4 +|============================================================================================================================== + +'Output data' + +None + + +### SET EXTERNAL PLUGIN + +#### Description + +This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. + +It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. + +The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise. + +The signature is computed on + +len(pluginName) || pluginName || contractAddress || methodSelector + +signed by the following secp256k1 public key 0482bbf2f34f367b2e5bc21847b6566f21f0976b22d3388a9a5e446ac62d25cf725b62a2555b2dd464a4da0ab2f4d506820543af1d242470b1b1a969a27578f353 + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 12 | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Length of plugin name | 1 +| plugin name | variable +| contract address | 20 +| method selector | 4 +| signature | variable +|============================================================================================================================== + +'Output data' + +None + + +### PROVIDE NFT INFORMATION + +#### Description + +This commands provides a trusted description of an NFT to associate a contract address with a collectionName. + +It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper nft information to the user if necessary, as marked in GET APP CONFIGURATION flags. + +The signature is computed on: + +type || version || len(collectionName) || collectionName || address || chainId || keyId || algorithmId + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 14 | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Type | 1 +| Version | 1 +| Collection Name Length | 1 +| Collection Name | variable +| Address | 20 +| Chain ID | 8 +| KeyID | 1 +| Algorithm ID | 1 +| Signature Length | 1 +| Signature | variable +|============================================================================================================================== + +'Output data' + +None + + +### SET PLUGIN + +#### Description + +This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. + +It can be used to set both internal and external plugins. + +It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. + +The function returns an error sw (0x6984) if the plugin requested is not installed on the device, 0x9000 otherwise. + +The plugin names `ERC20`, `ERC721` and `ERC1155` are reserved. Additional plugin names might be added to this list in the future. + +The signature is computed on + +type || version || len(pluginName) || pluginName || address || selector || chainId || keyId || algorithmId || len(signature) || signature + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 16 | 00 | 00 | variable | 00 +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Type | 1 +| Version | 1 +| Plugin Name Length | 1 +| Plugin Name | variable +| Address | 20 +| Selector | 4 +| Chain ID | 8 +| KeyID | 1 +| Algorithm ID | 1 +| Signature Length | 1 +| Signature | variable +|============================================================================================================================== + +'Output data' + +None + +### PERFORM PRIVACY OPERATION + +#### Description + +This command performs privacy operations as defined in EIP 1024 (https://ethereum-magicians.org/t/eip-1024-cross-client-encrypt-decrypt/505) + +It can return the public encryption key on Curve25519 for a given Ethereum account or the shared secret (generated by the scalar multiplication of the remote public key by the account private key on Curve25519) used to decrypt private data encrypted for a given Ethereum account + +All data can be optionally checked on the device before being returned. + +#### Coding + +'Command' + +[width="80%"] +|============================================================================================================================== +| *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* +| E0 | 18 | 00 : return data + + 01 : display data and confirm before returning + | 00 : return the public encryption key + + 01 : return the shared secret | variable | variable +|============================================================================================================================== + +'Input data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Number of BIP 32 derivations to perform (max 10) | 1 +| First derivation index (big endian) | 4 +| ... | 4 +| Last derivation index (big endian) | 4 +| Third party public key on Curve25519, if returning the shared secret | 32 +|============================================================================================================================== + +'Output data' + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Public encryption key or shared secret | 32 +|============================================================================================================================== + + +## Transport protocol + +### General transport description + +Ledger APDUs requests and responses are encapsulated using a flexible protocol allowing to fragment large payloads over different underlying transport mechanisms. + +The common transport header is defined as follows : + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| Communication channel ID (big endian) | 2 +| Command tag | 1 +| Packet sequence index (big endian) | 2 +| Payload | var +|============================================================================================================================== + +The Communication channel ID allows commands multiplexing over the same physical link. It is not used for the time being, and should be set to 0101 to avoid compatibility issues with implementations ignoring a leading 00 byte. + +The Command tag describes the message content. Use TAG_APDU (0x05) for standard APDU payloads, or TAG_PING (0x02) for a simple link test. + +The Packet sequence index describes the current sequence for fragmented payloads. The first fragment index is 0x00. + +### APDU Command payload encoding + +APDU Command payloads are encoded as follows : + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| APDU length (big endian) | 2 +| APDU CLA | 1 +| APDU INS | 1 +| APDU P1 | 1 +| APDU P2 | 1 +| APDU length | 1 +| Optional APDU data | var +|============================================================================================================================== + +APDU payload is encoded according to the APDU case + +[width="80%"] +|======================================================================================= +| Case Number | *Lc* | *Le* | Case description +| 1 | 0 | 0 | No data in either direction - L is set to 00 +| 2 | 0 | !0 | Input Data present, no Output Data - L is set to Lc +| 3 | !0 | 0 | Output Data present, no Input Data - L is set to Le +| 4 | !0 | !0 | Both Input and Output Data are present - L is set to Lc +|======================================================================================= + +### APDU Response payload encoding + +APDU Response payloads are encoded as follows : + +[width="80%"] +|============================================================================================================================== +| *Description* | *Length* +| APDU response length (big endian) | 2 +| APDU response data and Status Word | var +|============================================================================================================================== + +### USB mapping + +Messages are exchanged with the dongle over HID endpoints over interrupt transfers, with each chunk being 64 bytes long. The HID Report ID is ignored. + +### BLE mapping + +A similar encoding is used over BLE, without the Communication channel ID. + +The application acts as a GATT server defining service UUID D973F2E0-B19E-11E2-9E96-0800200C9A66 + +When using this service, the client sends requests to the characteristic D973F2E2-B19E-11E2-9E96-0800200C9A66, and gets notified on the characteristic D973F2E1-B19E-11E2-9E96-0800200C9A66 after registering for it. + +Requests are encoded using the standard BLE 20 bytes MTU size + +## Status Words + +The following standard Status Words are returned for all APDUs - some specific Status Words can be used for specific commands and are mentioned in the command description. + +'Status Words' + +[width="80%"] +|=============================================================================================== +| *SW* | *Description* +| 6501 | TransactionType not supported +| 6502 | Output buffer too small for chainId conversion +| 6503 | Plugin error +| 6504 | Failed to convert from int256 +| 6700 | Incorrect length +| 6982 | Security status not satisfied (Canceled by user) +| 6A80 | Invalid data +| 6B00 | Incorrect parameter P1 or P2 +| 6Fxx | Technical problem (Internal error, please report) +| 9000 | Normal ending of the command +|=============================================================================================== From 30f8c50e3f2f03811af3f4820eb5ba6655f9b066 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 17 May 2022 13:58:28 +0200 Subject: [PATCH 071/184] Renamed doc files to their recommended extension https://asciidoctor.org/docs/asciidoc-recommended-practices/#document-extension --- doc/{eth_contract_support_embedded.asc => .adoc} | 0 ...eth_starkware_extensions.asc => eth_starkware_extensions.adoc} | 0 doc/{ethapp.asc => ethapp.adoc} | 0 doc/{ethapp_plugins.asc => ethapp_plugins.adoc} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename doc/{eth_contract_support_embedded.asc => .adoc} (100%) rename doc/{eth_starkware_extensions.asc => eth_starkware_extensions.adoc} (100%) rename doc/{ethapp.asc => ethapp.adoc} (100%) rename doc/{ethapp_plugins.asc => ethapp_plugins.adoc} (100%) diff --git a/doc/eth_contract_support_embedded.asc b/doc/.adoc similarity index 100% rename from doc/eth_contract_support_embedded.asc rename to doc/.adoc diff --git a/doc/eth_starkware_extensions.asc b/doc/eth_starkware_extensions.adoc similarity index 100% rename from doc/eth_starkware_extensions.asc rename to doc/eth_starkware_extensions.adoc diff --git a/doc/ethapp.asc b/doc/ethapp.adoc similarity index 100% rename from doc/ethapp.asc rename to doc/ethapp.adoc diff --git a/doc/ethapp_plugins.asc b/doc/ethapp_plugins.adoc similarity index 100% rename from doc/ethapp_plugins.asc rename to doc/ethapp_plugins.adoc From 3674212016e5c2405b620e4af5d8e330bdef9238 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 18 May 2022 14:24:26 +0200 Subject: [PATCH 072/184] New EIP712 APDUs added to the documentation --- doc/ethapp.adoc | 223 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 210 insertions(+), 13 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 9f2e419..7b917f5 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -1,34 +1,43 @@ Ethereum application : Common Technical Specifications ======================================================= Ledger Firmware Team -Application version 1.9.18 - 29th of January 2022 +Application version 1.9.19 - 2022-05-17 -## 1.0 +## Version history + +### 1.0 - Initial release -## 1.1 +### 1.1 - Add GET APP CONFIGURATION - Add an option to return the chain code in GET ETH PUBLIC ADDRESS -## 1.2 +### 1.2 - Add SIGN ETH PERSONAL MESSAGE -## 1.1.10 +### 1.1.10 - Add PROVIDE ERC 20 TOKEN INFORMATION -## 1.5.0 +### 1.5.0 - Add SIGN ETH EIP 712 - Add GET ETH2 PUBLIC KEY -## 1.7.6 +### 1.7.6 - Add SET EXTERNAL PLUGIN -## 1.9.13 +### 1.9.13 - Add SET PLUGIN -## 1.9.17 +### 1.9.17 + - Add PROVIDE NFT INFORMATION + +### 1.9.18 - Add PERFORM PRIVACY OPERATION +### 1.9.19 + - Add EIP712 STRUCT DEFINITION & EIP712 STRUCT IMPLEMENTATION + - Update to SIGN ETH EIP712 + ## About This application describes the APDU messages interface to communicate with the Ethereum application. @@ -292,7 +301,11 @@ This command signs an Ethereum message following the EIP 712 specification (http For implementation version 0, the domain hash and message hash are provided to the device, which displays them and returns the signature -This command has been supported since firmware version 1.5.0 +This command has been supported since app version 1.5.0 + +The full implementation uses all the JSON data and does all the hashing on the +device, it has been supported since app version 1.9.19. This command should come +last, after all the EIP712 SEND STRUCT DEFINITION & SEND STRUCT IMPLEMENTATION. #### Coding @@ -302,7 +315,11 @@ This command has been supported since firmware version 1.5.0 |============================================================================================================================== | *CLA* | *INS* | *P1* | *P2* | *Lc* | *Le* | E0 | 0C | 00 - | implementation version : 00 | variable | variable + | 00: v0 implementation + + 01: full implementation + | variable + | variable |============================================================================================================================== 'Input data' @@ -314,8 +331,8 @@ This command has been supported since firmware version 1.5.0 | First derivation index (big endian) | 4 | ... | 4 | Last derivation index (big endian) | 4 -| Domain hash | 32 -| Message hash | 32 +| Domain hash *(only for v0)* | 32 +| Message hash *(only for v0)* | 32 |============================================================================================================================== 'Output data' @@ -587,6 +604,186 @@ All data can be optionally checked on the device before being returned. |============================================================================================================================== +### EIP712 SEND STRUCT DEFINITION + +#### Description + +This command sends the message definition with all its types. + +These commands should come before the EIP712 SEND STRUCT IMPLEMENTATION ones. + +#### Coding + +_Command_ + +[width="80%"] +|========================================================================= +| *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* +| E0 | 1A | 00 : complete send + + 01 : partial send, more to come + | 00 : struct name + + FF : struct field + | variable + | variable +|========================================================================= + +_Input data_ + +##### If P2 == struct name + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Name | LC +|========================================== + +##### If P2 == struct field + +:check_y: ✅ +:check_n: ❌ + +[width="80%"] +|====================================================================== +| *Description* | *Length (byte)* | *Mandatory* +| TypeDesc (type description) | 1 | {check_y} +| TypeSize (type byte size) | 1 | {check_n} +| ArrayLevelCount | 1 | {check_n} +| ArrayLevels | variable | {check_n} +| KeyLength | 1 | {check_y} +| Key | variable | {check_y} +|====================================================================== + +###### TypeDesc + +From MSB to LSB: + +[width="80%"] +|============================================================= +| *Description* | *Length (bit)* +| TypeArray (is it an array?) | 1 +| TypeSize (is a type size specified?) | 1 +| Unused | 2 +| Type | 4 +|============================================================= + +How to interpret Type from its value : + +[width="40%"] +|=========================================== +| *Value* | *Type* +| 0 | custom (struct type) +| 1 | int +| 2 | uint +| 3 | address +| 4 | bool +| 5 | string +| 6 | fixed-sized bytes +| 7 | dynamic-sized bytes +|=========================================== + +###### TypeSize + +_Only present if the TypeSize bit is set in TypeDesc._ + +Indicates the byte size of the field. (Ex: 8 for an int64) + + +###### ArrayLevelCount + +_Only present if the TypeArray bit is set in TypeDesc._ + +Indicates how many array levels that field has (Ex: 3 for int16[2][][4]). + +###### ArrayLevels + +_Only present if the TypeArray bit is set in TypeDesc._ + +Types of array level: + +[width="40%"] +|================================ +| *Byte value* | *Type* +| 0 | Dynamic sized (type[]) +| 1 | Fixed size (type[N]) +|================================ + +Each fixed-sized array level is followed by a byte indicating its size (number of elements). + + +_Output data_ + +None + + +### EIP712 SEND STRUCT IMPLEMENTATION + +#### Description + +This command sends the message implementation with all its values. + +These commands should come after the EIP712 SEND STRUCT DEFINITION ones. + +#### Coding + +_Command_ + +[width="80%"] +|========================================================================= +| *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* +| E0 | 1C | 00 : complete send + + 01 : partial send, more to come + | 00 : root struct + + 0F : array + + FF : struct field + | variable + | variable +|========================================================================= + +_Input data_ + +##### If P2 == root struct + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Name | LC +|========================================== + +Sets the name of the upcoming root structure all the following fields will be apart +of until we set another root structure. + +##### If P2 == array + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Array size | 1 +|========================================== + +Sets the size of the upcoming array the following N fields will be apart of. + +##### If P2 == struct field + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Value length | 2 (BE) +| Value | variable +|========================================== + +Sets the raw value of the next field in order in the current root structure. +Raw as in, an integer in the JSON file represented as "128" would only be 1 byte long (0x80) +instead of 3 as an array of ASCII characters. same for addresses and so on. + + +_Output data_ + +None + + ## Transport protocol ### General transport description From 31aff6a95f16f15c021c82c8c7bdf0410845eb5a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 31 May 2022 16:31:39 +0200 Subject: [PATCH 073/184] Removed unused custom strchr function --- src/utils.c | 11 ----------- src/utils.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/src/utils.c b/src/utils.c index 7a5627f..791b015 100644 --- a/src/utils.c +++ b/src/utils.c @@ -43,17 +43,6 @@ void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target) { readu256BE(tmp, target); } -int local_strchr(char *string, char ch) { - unsigned int length = strlen(string); - unsigned int i; - for (i = 0; i < length; i++) { - if (string[i] == ch) { - return i; - } - } - return -1; -} - uint64_t u64_from_BE(const uint8_t *in, uint8_t size) { uint8_t i = 0; uint64_t res = 0; diff --git a/src/utils.h b/src/utils.h index bd52398..306c27b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,8 +26,6 @@ void array_hexstr(char* strbuf, const void* bin, unsigned int len); void convertUint256BE(uint8_t* data, uint32_t length, uint256_t* target); -int local_strchr(char* string, char ch); - uint64_t u64_from_BE(const uint8_t* in, uint8_t size); bool uint256_to_decimal(const uint8_t* value, size_t value_len, char* out, size_t out_len); From 25b9caf2d5c1a4722a3cfbc9d9ac78b2e71de43d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 31 May 2022 17:30:02 +0200 Subject: [PATCH 074/184] Typo fix in APDU doc --- doc/ethapp.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 7b917f5..6a61197 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -255,7 +255,7 @@ The input data is the message to sign, streamed to the device in 255 bytes maxim #### Description -This commands provides a trusted description of an ERC 20 token to associate a contract address with a ticker and number of decimals. +This command provides a trusted description of an ERC 20 token to associate a contract address with a ticker and number of decimals. It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper token information to the user if necessary, as marked in GET APP CONFIGURATION flags. @@ -425,7 +425,7 @@ None #### Description -This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. +This command provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. It shall be run immediately before performing a transaction involving a contract supported by this plugin to display the proper information to the user if necessary. @@ -468,7 +468,7 @@ None #### Description -This commands provides a trusted description of an NFT to associate a contract address with a collectionName. +This command provides a trusted description of an NFT to associate a contract address with a collectionName. It shall be run immediately before performing a transaction involving a contract calling this contract address to display the proper nft information to the user if necessary, as marked in GET APP CONFIGURATION flags. @@ -512,7 +512,7 @@ None #### Description -This commands provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. +This command provides the name of a trusted binding of a plugin with a contract address and a supported method selector. This plugin will be called to interpret contract data in the following transaction signing command. It can be used to set both internal and external plugins. From f458fcd6ac2e45c50d579a179fef26e589d43e8d Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 6 Jun 2022 16:40:01 +0200 Subject: [PATCH 075/184] Better naming for uint256 type functions --- src_common/uint256.c | 16 ++++++++-------- src_common/uint256.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src_common/uint256.c b/src_common/uint256.c index 22b738e..5accf5e 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -249,23 +249,23 @@ void add256(uint256_t *number1, uint256_t *number2, uint256_t *target) { add128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void minus128(uint128_t *number1, uint128_t *number2, uint128_t *target) { +void sub128(uint128_t *number1, uint128_t *number2, uint128_t *target) { UPPER_P(target) = UPPER_P(number1) - UPPER_P(number2) - ((LOWER_P(number1) - LOWER_P(number2)) > LOWER_P(number1)); LOWER_P(target) = LOWER_P(number1) - LOWER_P(number2); } -void minus256(uint256_t *number1, uint256_t *number2, uint256_t *target) { +void sub256(uint256_t *number1, uint256_t *number2, uint256_t *target) { uint128_t tmp; - minus128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); - minus128(&LOWER_P(number1), &LOWER_P(number2), &tmp); + sub128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); + sub128(&LOWER_P(number1), &LOWER_P(number2), &tmp); if (gt128(&tmp, &LOWER_P(number1))) { uint128_t one; UPPER(one) = 0; LOWER(one) = 1; - minus128(&UPPER_P(target), &one, &UPPER_P(target)); + sub128(&UPPER_P(target), &one, &UPPER_P(target)); } - minus128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); + sub128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } void or128(uint128_t *number1, uint128_t *number2, uint128_t *target) { @@ -379,7 +379,7 @@ void divmod128(uint128_t *l, uint128_t *r, uint128_t *retDiv, uint128_t *retMod) } while (gte128(&resMod, r)) { if (gte128(&resMod, ©d)) { - minus128(&resMod, ©d, &resMod); + sub128(&resMod, ©d, &resMod); or128(&resDiv, &adder, &resDiv); } shiftr128(©d, 1, ©d); @@ -411,7 +411,7 @@ void divmod256(uint256_t *l, uint256_t *r, uint256_t *retDiv, uint256_t *retMod) } while (gte256(&resMod, r)) { if (gte256(&resMod, ©d)) { - minus256(&resMod, ©d, &resMod); + sub256(&resMod, ©d, &resMod); or256(&resDiv, &adder, &resDiv); } shiftr256(©d, 1, ©d); diff --git a/src_common/uint256.h b/src_common/uint256.h index 5683c3e..ebf6da3 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -62,8 +62,8 @@ bool gte128(uint128_t *number1, uint128_t *number2); bool gte256(uint256_t *number1, uint256_t *number2); void add128(uint128_t *number1, uint128_t *number2, uint128_t *target); void add256(uint256_t *number1, uint256_t *number2, uint256_t *target); -void minus128(uint128_t *number1, uint128_t *number2, uint128_t *target); -void minus256(uint256_t *number1, uint256_t *number2, uint256_t *target); +void sub128(uint128_t *number1, uint128_t *number2, uint128_t *target); +void sub256(uint256_t *number1, uint256_t *number2, uint256_t *target); void or128(uint128_t *number1, uint128_t *number2, uint128_t *target); void or256(uint256_t *number1, uint256_t *number2, uint256_t *target); void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target); From 50225d72d077c1c6e1bfad5f6c0dcd1aa19cd6a3 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 6 Jun 2022 18:35:55 +0200 Subject: [PATCH 076/184] Some const correctness in the uint256 functions --- src_common/uint256.c | 98 ++++++++++++++++++++++++++++---------------- src_common/uint256.h | 85 +++++++++++++++++++++++--------------- 2 files changed, 115 insertions(+), 68 deletions(-) diff --git a/src_common/uint256.c b/src_common/uint256.c index 5accf5e..771936f 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -24,52 +24,52 @@ static const char HEXDIGITS[] = "0123456789abcdef"; -static uint64_t readUint64BE(uint8_t *buffer) { +static uint64_t readUint64BE(const uint8_t *const buffer) { return (((uint64_t) buffer[0]) << 56) | (((uint64_t) buffer[1]) << 48) | (((uint64_t) buffer[2]) << 40) | (((uint64_t) buffer[3]) << 32) | (((uint64_t) buffer[4]) << 24) | (((uint64_t) buffer[5]) << 16) | (((uint64_t) buffer[6]) << 8) | (((uint64_t) buffer[7])); } -void readu128BE(uint8_t *buffer, uint128_t *target) { +void readu128BE(const uint8_t *const buffer, uint128_t *target) { UPPER_P(target) = readUint64BE(buffer); LOWER_P(target) = readUint64BE(buffer + 8); } -void readu256BE(uint8_t *buffer, uint256_t *target) { +void readu256BE(const uint8_t *const buffer, uint256_t *target) { readu128BE(buffer, &UPPER_P(target)); readu128BE(buffer + 16, &LOWER_P(target)); } -bool zero128(uint128_t *number) { +bool zero128(const uint128_t *const number) { return ((LOWER_P(number) == 0) && (UPPER_P(number) == 0)); } -bool zero256(uint256_t *number) { +bool zero256(const uint256_t *const number) { return (zero128(&LOWER_P(number)) && zero128(&UPPER_P(number))); } -void copy128(uint128_t *target, uint128_t *number) { +void copy128(uint128_t *const target, const uint128_t *const number) { UPPER_P(target) = UPPER_P(number); LOWER_P(target) = LOWER_P(number); } -void copy256(uint256_t *target, uint256_t *number) { +void copy256(uint256_t *const target, const uint256_t *const number) { copy128(&UPPER_P(target), &UPPER_P(number)); copy128(&LOWER_P(target), &LOWER_P(number)); } -void clear128(uint128_t *target) { +void clear128(uint128_t *const target) { UPPER_P(target) = 0; LOWER_P(target) = 0; } -void clear256(uint256_t *target) { +void clear256(uint256_t *const target) { clear128(&UPPER_P(target)); clear128(&LOWER_P(target)); } -void shiftl128(uint128_t *number, uint32_t value, uint128_t *target) { +void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target) { if (value >= 128) { clear128(target); } else if (value == 64) { @@ -88,7 +88,7 @@ void shiftl128(uint128_t *number, uint32_t value, uint128_t *target) { } } -void shiftl256(uint256_t *number, uint32_t value, uint256_t *target) { +void shiftl256(const uint256_t *const number, uint32_t value, uint256_t *const target) { if (value >= 256) { clear256(target); } else if (value == 128) { @@ -113,7 +113,7 @@ void shiftl256(uint256_t *number, uint32_t value, uint256_t *target) { } } -void shiftr128(uint128_t *number, uint32_t value, uint128_t *target) { +void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target) { if (value >= 128) { clear128(target); } else if (value == 64) { @@ -134,7 +134,7 @@ void shiftr128(uint128_t *number, uint32_t value, uint128_t *target) { } } -void shiftr256(uint256_t *number, uint32_t value, uint256_t *target) { +void shiftr256(const uint256_t *const number, uint32_t value, uint256_t *const target) { if (value >= 256) { clear256(target); } else if (value == 128) { @@ -159,7 +159,7 @@ void shiftr256(uint256_t *number, uint32_t value, uint256_t *target) { } } -uint32_t bits128(uint128_t *number) { +uint32_t bits128(const uint128_t *const number) { uint32_t result = 0; if (UPPER_P(number)) { result = 64; @@ -178,7 +178,7 @@ uint32_t bits128(uint128_t *number) { return result; } -uint32_t bits256(uint256_t *number) { +uint32_t bits256(const uint256_t *const number) { uint32_t result = 0; if (!zero128(&UPPER_P(number))) { result = 128; @@ -199,44 +199,48 @@ uint32_t bits256(uint256_t *number) { return result; } -bool equal128(uint128_t *number1, uint128_t *number2) { +bool equal128(const uint128_t *const number1, const uint128_t *const number2) { return (UPPER_P(number1) == UPPER_P(number2)) && (LOWER_P(number1) == LOWER_P(number2)); } -bool equal256(uint256_t *number1, uint256_t *number2) { +bool equal256(const uint256_t *const number1, const uint256_t *const number2) { return (equal128(&UPPER_P(number1), &UPPER_P(number2)) && equal128(&LOWER_P(number1), &LOWER_P(number2))); } -bool gt128(uint128_t *number1, uint128_t *number2) { +bool gt128(const uint128_t *const number1, const uint128_t *const number2) { if (UPPER_P(number1) == UPPER_P(number2)) { return (LOWER_P(number1) > LOWER_P(number2)); } return (UPPER_P(number1) > UPPER_P(number2)); } -bool gt256(uint256_t *number1, uint256_t *number2) { +bool gt256(const uint256_t *const number1, const uint256_t *const number2) { if (equal128(&UPPER_P(number1), &UPPER_P(number2))) { return gt128(&LOWER_P(number1), &LOWER_P(number2)); } return gt128(&UPPER_P(number1), &UPPER_P(number2)); } -bool gte128(uint128_t *number1, uint128_t *number2) { +bool gte128(const uint128_t *const number1, const uint128_t *const number2) { return gt128(number1, number2) || equal128(number1, number2); } -bool gte256(uint256_t *number1, uint256_t *number2) { +bool gte256(const uint256_t *const number1, const uint256_t *const number2) { return gt256(number1, number2) || equal256(number1, number2); } -void add128(uint128_t *number1, uint128_t *number2, uint128_t *target) { +void add128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { UPPER_P(target) = UPPER_P(number1) + UPPER_P(number2) + ((LOWER_P(number1) + LOWER_P(number2)) < LOWER_P(number1)); LOWER_P(target) = LOWER_P(number1) + LOWER_P(number2); } -void add256(uint256_t *number1, uint256_t *number2, uint256_t *target) { +void add256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target) { uint128_t tmp; add128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); add128(&LOWER_P(number1), &LOWER_P(number2), &tmp); @@ -249,13 +253,17 @@ void add256(uint256_t *number1, uint256_t *number2, uint256_t *target) { add128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void sub128(uint128_t *number1, uint128_t *number2, uint128_t *target) { +void sub128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { UPPER_P(target) = UPPER_P(number1) - UPPER_P(number2) - ((LOWER_P(number1) - LOWER_P(number2)) > LOWER_P(number1)); LOWER_P(target) = LOWER_P(number1) - LOWER_P(number2); } -void sub256(uint256_t *number1, uint256_t *number2, uint256_t *target) { +void sub256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target) { uint128_t tmp; sub128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); sub128(&LOWER_P(number1), &LOWER_P(number2), &tmp); @@ -268,17 +276,23 @@ void sub256(uint256_t *number1, uint256_t *number2, uint256_t *target) { sub128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void or128(uint128_t *number1, uint128_t *number2, uint128_t *target) { +void or128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { UPPER_P(target) = UPPER_P(number1) | UPPER_P(number2); LOWER_P(target) = LOWER_P(number1) | LOWER_P(number2); } -void or256(uint256_t *number1, uint256_t *number2, uint256_t *target) { +void or256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target) { or128(&UPPER_P(number1), &UPPER_P(number2), &UPPER_P(target)); or128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target) { +void mul128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { uint64_t top[4] = {UPPER_P(number1) >> 32, UPPER_P(number1) & 0xffffffff, LOWER_P(number1) >> 32, @@ -323,7 +337,7 @@ void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target) { add128(&tmp, &tmp2, target); } -void write_u64_be(uint8_t *buffer, uint64_t value) { +void write_u64_be(uint8_t *const buffer, uint64_t value) { buffer[0] = ((value >> 56) & 0xff); buffer[1] = ((value >> 48) & 0xff); buffer[2] = ((value >> 40) & 0xff); @@ -334,7 +348,7 @@ void write_u64_be(uint8_t *buffer, uint64_t value) { buffer[7] = (value & 0xff); } -void read_u64_be(uint8_t *in, uint64_t *out) { +void read_u64_be(const uint8_t *const in, uint64_t *const out) { uint8_t *out_ptr = (uint8_t *) out; *out_ptr++ = in[7]; *out_ptr++ = in[6]; @@ -346,7 +360,9 @@ void read_u64_be(uint8_t *in, uint64_t *out) { *out_ptr = in[0]; } -void mul256(uint256_t *number1, uint256_t *number2, uint256_t *target) { +void mul256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target) { uint8_t num1[INT256_LENGTH], num2[INT256_LENGTH], result[INT256_LENGTH * 2]; memset(&result, 0, sizeof(result)); for (uint8_t i = 0; i < 4; i++) { @@ -359,7 +375,10 @@ void mul256(uint256_t *number1, uint256_t *number2, uint256_t *target) { } } -void divmod128(uint128_t *l, uint128_t *r, uint128_t *retDiv, uint128_t *retMod) { +void divmod128(const uint128_t *const l, + const uint128_t *const r, + uint128_t *const retDiv, + uint128_t *const retMod) { uint128_t copyd, adder, resDiv, resMod; uint128_t one; UPPER(one) = 0; @@ -390,7 +409,10 @@ void divmod128(uint128_t *l, uint128_t *r, uint128_t *retDiv, uint128_t *retMod) } } -void divmod256(uint256_t *l, uint256_t *r, uint256_t *retDiv, uint256_t *retMod) { +void divmod256(const uint256_t *const l, + const uint256_t *const r, + uint256_t *const retDiv, + uint256_t *const retMod) { uint256_t copyd, adder, resDiv, resMod; uint256_t one; clear256(&one); @@ -432,7 +454,10 @@ static void reverseString(char *str, uint32_t length) { } } -bool tostring128(uint128_t *number, uint32_t baseParam, char *out, uint32_t outLength) { +bool tostring128(const uint128_t *const number, + uint32_t baseParam, + char *const out, + uint32_t outLength) { uint128_t rDiv; uint128_t rMod; uint128_t base; @@ -456,7 +481,10 @@ bool tostring128(uint128_t *number, uint32_t baseParam, char *out, uint32_t outL return true; } -bool tostring256(uint256_t *number, uint32_t baseParam, char *out, uint32_t outLength) { +bool tostring256(const uint256_t *const number, + uint32_t baseParam, + char *const out, + uint32_t outLength) { uint256_t rDiv; uint256_t rMod; uint256_t base; diff --git a/src_common/uint256.h b/src_common/uint256.h index ebf6da3..837bfe4 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -39,38 +39,57 @@ typedef struct uint256_t { #define UPPER(x) x.elements[0] #define LOWER(x) x.elements[1] -void readu128BE(uint8_t *buffer, uint128_t *target); -void readu256BE(uint8_t *buffer, uint256_t *target); -void write_u64_be(uint8_t *buffer, uint64_t value); -bool zero128(uint128_t *number); -bool zero256(uint256_t *number); -void copy128(uint128_t *target, uint128_t *number); -void copy256(uint256_t *target, uint256_t *number); -void clear128(uint128_t *target); -void clear256(uint256_t *target); -void shiftl128(uint128_t *number, uint32_t value, uint128_t *target); -void shiftr128(uint128_t *number, uint32_t value, uint128_t *target); -void shiftl256(uint256_t *number, uint32_t value, uint256_t *target); -void shiftr256(uint256_t *number, uint32_t value, uint256_t *target); -uint32_t bits128(uint128_t *number); -uint32_t bits256(uint256_t *number); -bool equal128(uint128_t *number1, uint128_t *number2); -bool equal256(uint256_t *number1, uint256_t *number2); -bool gt128(uint128_t *number1, uint128_t *number2); -bool gt256(uint256_t *number1, uint256_t *number2); -bool gte128(uint128_t *number1, uint128_t *number2); -bool gte256(uint256_t *number1, uint256_t *number2); -void add128(uint128_t *number1, uint128_t *number2, uint128_t *target); -void add256(uint256_t *number1, uint256_t *number2, uint256_t *target); -void sub128(uint128_t *number1, uint128_t *number2, uint128_t *target); -void sub256(uint256_t *number1, uint256_t *number2, uint256_t *target); -void or128(uint128_t *number1, uint128_t *number2, uint128_t *target); -void or256(uint256_t *number1, uint256_t *number2, uint256_t *target); -void mul128(uint128_t *number1, uint128_t *number2, uint128_t *target); -void mul256(uint256_t *number1, uint256_t *number2, uint256_t *target); -void divmod128(uint128_t *l, uint128_t *r, uint128_t *div, uint128_t *mod); -void divmod256(uint256_t *l, uint256_t *r, uint256_t *div, uint256_t *mod); -bool tostring128(uint128_t *number, uint32_t base, char *out, uint32_t outLength); -bool tostring256(uint256_t *number, uint32_t base, char *out, uint32_t outLength); +void readu128BE(const uint8_t *const buffer, uint128_t *target); +void readu256BE(const uint8_t *const buffer, uint256_t *target); +void write_u64_be(uint8_t *const buffer, uint64_t value); +void read_u64_be(const uint8_t *const in, uint64_t *const out); +bool zero128(const uint128_t *const number); +bool zero256(const uint256_t *const number); +void copy128(uint128_t *const target, const uint128_t *const number); +void copy256(uint256_t *const target, const uint256_t *const number); +void clear128(uint128_t *const target); +void clear256(uint256_t *const target); +void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target); +void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target); +void shiftl256(const uint256_t *const number, uint32_t value, uint256_t *const target); +void shiftr256(const uint256_t *const number, uint32_t value, uint256_t *const target); +uint32_t bits128(const uint128_t *const number); +uint32_t bits256(const uint256_t *const number); +bool equal128(const uint128_t *const number1, const uint128_t *const number2); +bool equal256(const uint256_t *const number1, const uint256_t *const number2); +bool gt128(const uint128_t *const number1, const uint128_t *const number2); +bool gt256(const uint256_t *const number1, const uint256_t *const number2); +bool gte128(const uint128_t *const number1, const uint128_t *const number2); +bool gte256(const uint256_t *const number1, const uint256_t *const number2); +void add128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void add256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target); +void sub128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void sub256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target); +void or128(const uint128_t *const number1, const uint128_t *const number2, uint128_t *const target); +void or256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target); +void mul128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void mul256(const uint256_t *const number1, + const uint256_t *const number2, + uint256_t *const target); +void divmod128(const uint128_t *const l, + const uint128_t *const r, + uint128_t *const div, + uint128_t *const mod); +void divmod256(const uint256_t *const l, + const uint256_t *const r, + uint256_t *const div, + uint256_t *const mod); +bool tostring128(const uint128_t *const number, uint32_t base, char *const out, uint32_t outLength); +bool tostring256(const uint256_t *const number, uint32_t base, char *const out, uint32_t outLength); #endif // _UINT256_H_ From 59d8ace99ec640d48ae160451ad08f52c70a1b0c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 7 Jun 2022 11:54:54 +0200 Subject: [PATCH 077/184] Split the uint256 lib into multiple more meaningful files --- src/stark_utils.c | 1 + src_common/uint128.c | 247 +++++++++++++++++++++++++++++++++++ src_common/uint128.h | 56 ++++++++ src_common/uint256.c | 271 +-------------------------------------- src_common/uint256.h | 42 +----- src_common/uint_common.c | 62 +++++++++ src_common/uint_common.h | 37 ++++++ 7 files changed, 408 insertions(+), 308 deletions(-) create mode 100644 src_common/uint128.c create mode 100644 src_common/uint128.h create mode 100644 src_common/uint_common.c create mode 100644 src_common/uint_common.h diff --git a/src/stark_utils.c b/src/stark_utils.c index 4df86e9..a585e03 100644 --- a/src/stark_utils.c +++ b/src/stark_utils.c @@ -4,6 +4,7 @@ #include "shared_context.h" #include "ethUtils.h" #include "uint256.h" +#include "uint_common.h" #include "os_io_seproxyhal.h" diff --git a/src_common/uint128.c b/src_common/uint128.c new file mode 100644 index 0000000..e286412 --- /dev/null +++ b/src_common/uint128.c @@ -0,0 +1,247 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2016-2019 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +// Adapted from https://github.com/calccrypto/uint256_t + +#include +#include +#include "uint128.h" +#include "uint_common.h" + +void readu128BE(const uint8_t *const buffer, uint128_t *const target) { + UPPER_P(target) = readUint64BE(buffer); + LOWER_P(target) = readUint64BE(buffer + 8); +} + +bool zero128(const uint128_t *const number) { + return ((LOWER_P(number) == 0) && (UPPER_P(number) == 0)); +} + +void copy128(uint128_t *const target, const uint128_t *const number) { + UPPER_P(target) = UPPER_P(number); + LOWER_P(target) = LOWER_P(number); +} + +void clear128(uint128_t *const target) { + UPPER_P(target) = 0; + LOWER_P(target) = 0; +} + +void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target) { + if (value >= 128) { + clear128(target); + } else if (value == 64) { + UPPER_P(target) = LOWER_P(number); + LOWER_P(target) = 0; + } else if (value == 0) { + copy128(target, number); + } else if (value < 64) { + UPPER_P(target) = (UPPER_P(number) << value) + (LOWER_P(number) >> (64 - value)); + LOWER_P(target) = (LOWER_P(number) << value); + } else if ((128 > value) && (value > 64)) { + UPPER_P(target) = LOWER_P(number) << (value - 64); + LOWER_P(target) = 0; + } else { + clear128(target); + } +} + +void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target) { + if (value >= 128) { + clear128(target); + } else if (value == 64) { + UPPER_P(target) = 0; + LOWER_P(target) = UPPER_P(number); + } else if (value == 0) { + copy128(target, number); + } else if (value < 64) { + uint128_t result; + UPPER(result) = UPPER_P(number) >> value; + LOWER(result) = (UPPER_P(number) << (64 - value)) + (LOWER_P(number) >> value); + copy128(target, &result); + } else if ((128 > value) && (value > 64)) { + LOWER_P(target) = UPPER_P(number) >> (value - 64); + UPPER_P(target) = 0; + } else { + clear128(target); + } +} + +uint32_t bits128(const uint128_t *const number) { + uint32_t result = 0; + if (UPPER_P(number)) { + result = 64; + uint64_t up = UPPER_P(number); + while (up) { + up >>= 1; + result++; + } + } else { + uint64_t low = LOWER_P(number); + while (low) { + low >>= 1; + result++; + } + } + return result; +} + +bool equal128(const uint128_t *const number1, const uint128_t *const number2) { + return (UPPER_P(number1) == UPPER_P(number2)) && (LOWER_P(number1) == LOWER_P(number2)); +} + +bool gt128(const uint128_t *const number1, const uint128_t *const number2) { + if (UPPER_P(number1) == UPPER_P(number2)) { + return (LOWER_P(number1) > LOWER_P(number2)); + } + return (UPPER_P(number1) > UPPER_P(number2)); +} + +bool gte128(const uint128_t *const number1, const uint128_t *const number2) { + return gt128(number1, number2) || equal128(number1, number2); +} + +void add128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { + UPPER_P(target) = UPPER_P(number1) + UPPER_P(number2) + + ((LOWER_P(number1) + LOWER_P(number2)) < LOWER_P(number1)); + LOWER_P(target) = LOWER_P(number1) + LOWER_P(number2); +} + +void sub128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { + UPPER_P(target) = UPPER_P(number1) - UPPER_P(number2) - + ((LOWER_P(number1) - LOWER_P(number2)) > LOWER_P(number1)); + LOWER_P(target) = LOWER_P(number1) - LOWER_P(number2); +} + +void or128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { + UPPER_P(target) = UPPER_P(number1) | UPPER_P(number2); + LOWER_P(target) = LOWER_P(number1) | LOWER_P(number2); +} + +void mul128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target) { + uint64_t top[4] = {UPPER_P(number1) >> 32, + UPPER_P(number1) & 0xffffffff, + LOWER_P(number1) >> 32, + LOWER_P(number1) & 0xffffffff}; + uint64_t bottom[4] = {UPPER_P(number2) >> 32, + UPPER_P(number2) & 0xffffffff, + LOWER_P(number2) >> 32, + LOWER_P(number2) & 0xffffffff}; + uint64_t products[4][4]; + uint128_t tmp, tmp2; + + for (int y = 3; y > -1; y--) { + for (int x = 3; x > -1; x--) { + products[3 - x][y] = top[x] * bottom[y]; + } + } + + uint64_t fourth32 = products[0][3] & 0xffffffff; + uint64_t third32 = (products[0][2] & 0xffffffff) + (products[0][3] >> 32); + uint64_t second32 = (products[0][1] & 0xffffffff) + (products[0][2] >> 32); + uint64_t first32 = (products[0][0] & 0xffffffff) + (products[0][1] >> 32); + + third32 += products[1][3] & 0xffffffff; + second32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32); + first32 += (products[1][1] & 0xffffffff) + (products[1][2] >> 32); + + second32 += products[2][3] & 0xffffffff; + first32 += (products[2][2] & 0xffffffff) + (products[2][3] >> 32); + + first32 += products[3][3] & 0xffffffff; + + UPPER(tmp) = first32 << 32; + LOWER(tmp) = 0; + UPPER(tmp2) = third32 >> 32; + LOWER(tmp2) = third32 << 32; + add128(&tmp, &tmp2, target); + UPPER(tmp) = second32; + LOWER(tmp) = 0; + add128(&tmp, target, &tmp2); + UPPER(tmp) = 0; + LOWER(tmp) = fourth32; + add128(&tmp, &tmp2, target); +} + +void divmod128(const uint128_t *const l, + const uint128_t *const r, + uint128_t *const retDiv, + uint128_t *const retMod) { + uint128_t copyd, adder, resDiv, resMod; + uint128_t one; + UPPER(one) = 0; + LOWER(one) = 1; + uint32_t diffBits = bits128(l) - bits128(r); + clear128(&resDiv); + copy128(&resMod, l); + if (gt128(r, l)) { + copy128(retMod, l); + clear128(retDiv); + } else { + shiftl128(r, diffBits, ©d); + shiftl128(&one, diffBits, &adder); + if (gt128(©d, &resMod)) { + shiftr128(©d, 1, ©d); + shiftr128(&adder, 1, &adder); + } + while (gte128(&resMod, r)) { + if (gte128(&resMod, ©d)) { + sub128(&resMod, ©d, &resMod); + or128(&resDiv, &adder, &resDiv); + } + shiftr128(©d, 1, ©d); + shiftr128(&adder, 1, &adder); + } + copy128(retDiv, &resDiv); + copy128(retMod, &resMod); + } +} + +bool tostring128(const uint128_t *const number, + uint32_t baseParam, + char *const out, + uint32_t outLength) { + uint128_t rDiv; + uint128_t rMod; + uint128_t base; + copy128(&rDiv, number); + clear128(&rMod); + clear128(&base); + LOWER(base) = baseParam; + uint32_t offset = 0; + if ((baseParam < 2) || (baseParam > 16)) { + return false; + } + do { + if (offset > (outLength - 1)) { + return false; + } + divmod128(&rDiv, &base, &rDiv, &rMod); + out[offset++] = HEXDIGITS[(uint8_t) LOWER(rMod)]; + } while (!zero128(&rDiv)); + out[offset] = '\0'; + reverseString(out, offset); + return true; +} diff --git a/src_common/uint128.h b/src_common/uint128.h new file mode 100644 index 0000000..8f76504 --- /dev/null +++ b/src_common/uint128.h @@ -0,0 +1,56 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2016-2019 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +// Adapted from https://github.com/calccrypto/uint256_t + +#ifndef _UINT128_H_ +#define _UINT128_H_ + +#include +#include + +typedef struct uint128_t { + uint64_t elements[2]; +} uint128_t; + +void readu128BE(const uint8_t *const buffer, uint128_t *const target); +bool zero128(const uint128_t *const number); +void copy128(uint128_t *const target, const uint128_t *const number); +void clear128(uint128_t *const target); +void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target); +void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target); +uint32_t bits128(const uint128_t *const number); +bool equal128(const uint128_t *const number1, const uint128_t *const number2); +bool gt128(const uint128_t *const number1, const uint128_t *const number2); +bool gte128(const uint128_t *const number1, const uint128_t *const number2); +void add128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void sub128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void or128(const uint128_t *const number1, const uint128_t *const number2, uint128_t *const target); +void mul128(const uint128_t *const number1, + const uint128_t *const number2, + uint128_t *const target); +void divmod128(const uint128_t *const l, + const uint128_t *const r, + uint128_t *const div, + uint128_t *const mod); +bool tostring128(const uint128_t *const number, uint32_t base, char *const out, uint32_t outLength); + +#endif // _UINT128_H_ diff --git a/src_common/uint256.c b/src_common/uint256.c index 771936f..e8698ee 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -19,75 +19,29 @@ #include #include - +#include "ethUstream.h" // INT256_LENGTH #include "uint256.h" +#include "uint_common.h" -static const char HEXDIGITS[] = "0123456789abcdef"; - -static uint64_t readUint64BE(const uint8_t *const buffer) { - return (((uint64_t) buffer[0]) << 56) | (((uint64_t) buffer[1]) << 48) | - (((uint64_t) buffer[2]) << 40) | (((uint64_t) buffer[3]) << 32) | - (((uint64_t) buffer[4]) << 24) | (((uint64_t) buffer[5]) << 16) | - (((uint64_t) buffer[6]) << 8) | (((uint64_t) buffer[7])); -} - -void readu128BE(const uint8_t *const buffer, uint128_t *target) { - UPPER_P(target) = readUint64BE(buffer); - LOWER_P(target) = readUint64BE(buffer + 8); -} - -void readu256BE(const uint8_t *const buffer, uint256_t *target) { +void readu256BE(const uint8_t *const buffer, uint256_t *const target) { readu128BE(buffer, &UPPER_P(target)); readu128BE(buffer + 16, &LOWER_P(target)); } -bool zero128(const uint128_t *const number) { - return ((LOWER_P(number) == 0) && (UPPER_P(number) == 0)); -} - bool zero256(const uint256_t *const number) { return (zero128(&LOWER_P(number)) && zero128(&UPPER_P(number))); } -void copy128(uint128_t *const target, const uint128_t *const number) { - UPPER_P(target) = UPPER_P(number); - LOWER_P(target) = LOWER_P(number); -} - void copy256(uint256_t *const target, const uint256_t *const number) { copy128(&UPPER_P(target), &UPPER_P(number)); copy128(&LOWER_P(target), &LOWER_P(number)); } -void clear128(uint128_t *const target) { - UPPER_P(target) = 0; - LOWER_P(target) = 0; -} - void clear256(uint256_t *const target) { clear128(&UPPER_P(target)); clear128(&LOWER_P(target)); } -void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target) { - if (value >= 128) { - clear128(target); - } else if (value == 64) { - UPPER_P(target) = LOWER_P(number); - LOWER_P(target) = 0; - } else if (value == 0) { - copy128(target, number); - } else if (value < 64) { - UPPER_P(target) = (UPPER_P(number) << value) + (LOWER_P(number) >> (64 - value)); - LOWER_P(target) = (LOWER_P(number) << value); - } else if ((128 > value) && (value > 64)) { - UPPER_P(target) = LOWER_P(number) << (value - 64); - LOWER_P(target) = 0; - } else { - clear128(target); - } -} - void shiftl256(const uint256_t *const number, uint32_t value, uint256_t *const target) { if (value >= 256) { clear256(target); @@ -113,27 +67,6 @@ void shiftl256(const uint256_t *const number, uint32_t value, uint256_t *const t } } -void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target) { - if (value >= 128) { - clear128(target); - } else if (value == 64) { - UPPER_P(target) = 0; - LOWER_P(target) = UPPER_P(number); - } else if (value == 0) { - copy128(target, number); - } else if (value < 64) { - uint128_t result; - UPPER(result) = UPPER_P(number) >> value; - LOWER(result) = (UPPER_P(number) << (64 - value)) + (LOWER_P(number) >> value); - copy128(target, &result); - } else if ((128 > value) && (value > 64)) { - LOWER_P(target) = UPPER_P(number) >> (value - 64); - UPPER_P(target) = 0; - } else { - clear128(target); - } -} - void shiftr256(const uint256_t *const number, uint32_t value, uint256_t *const target) { if (value >= 256) { clear256(target); @@ -159,25 +92,6 @@ void shiftr256(const uint256_t *const number, uint32_t value, uint256_t *const t } } -uint32_t bits128(const uint128_t *const number) { - uint32_t result = 0; - if (UPPER_P(number)) { - result = 64; - uint64_t up = UPPER_P(number); - while (up) { - up >>= 1; - result++; - } - } else { - uint64_t low = LOWER_P(number); - while (low) { - low >>= 1; - result++; - } - } - return result; -} - uint32_t bits256(const uint256_t *const number) { uint32_t result = 0; if (!zero128(&UPPER_P(number))) { @@ -199,22 +113,11 @@ uint32_t bits256(const uint256_t *const number) { return result; } -bool equal128(const uint128_t *const number1, const uint128_t *const number2) { - return (UPPER_P(number1) == UPPER_P(number2)) && (LOWER_P(number1) == LOWER_P(number2)); -} - bool equal256(const uint256_t *const number1, const uint256_t *const number2) { return (equal128(&UPPER_P(number1), &UPPER_P(number2)) && equal128(&LOWER_P(number1), &LOWER_P(number2))); } -bool gt128(const uint128_t *const number1, const uint128_t *const number2) { - if (UPPER_P(number1) == UPPER_P(number2)) { - return (LOWER_P(number1) > LOWER_P(number2)); - } - return (UPPER_P(number1) > UPPER_P(number2)); -} - bool gt256(const uint256_t *const number1, const uint256_t *const number2) { if (equal128(&UPPER_P(number1), &UPPER_P(number2))) { return gt128(&LOWER_P(number1), &LOWER_P(number2)); @@ -222,22 +125,10 @@ bool gt256(const uint256_t *const number1, const uint256_t *const number2) { return gt128(&UPPER_P(number1), &UPPER_P(number2)); } -bool gte128(const uint128_t *const number1, const uint128_t *const number2) { - return gt128(number1, number2) || equal128(number1, number2); -} - bool gte256(const uint256_t *const number1, const uint256_t *const number2) { return gt256(number1, number2) || equal256(number1, number2); } -void add128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target) { - UPPER_P(target) = UPPER_P(number1) + UPPER_P(number2) + - ((LOWER_P(number1) + LOWER_P(number2)) < LOWER_P(number1)); - LOWER_P(target) = LOWER_P(number1) + LOWER_P(number2); -} - void add256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target) { @@ -253,14 +144,6 @@ void add256(const uint256_t *const number1, add128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void sub128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target) { - UPPER_P(target) = UPPER_P(number1) - UPPER_P(number2) - - ((LOWER_P(number1) - LOWER_P(number2)) > LOWER_P(number1)); - LOWER_P(target) = LOWER_P(number1) - LOWER_P(number2); -} - void sub256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target) { @@ -276,13 +159,6 @@ void sub256(const uint256_t *const number1, sub128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void or128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target) { - UPPER_P(target) = UPPER_P(number1) | UPPER_P(number2); - LOWER_P(target) = LOWER_P(number1) | LOWER_P(number2); -} - void or256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target) { @@ -290,76 +166,6 @@ void or256(const uint256_t *const number1, or128(&LOWER_P(number1), &LOWER_P(number2), &LOWER_P(target)); } -void mul128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target) { - uint64_t top[4] = {UPPER_P(number1) >> 32, - UPPER_P(number1) & 0xffffffff, - LOWER_P(number1) >> 32, - LOWER_P(number1) & 0xffffffff}; - uint64_t bottom[4] = {UPPER_P(number2) >> 32, - UPPER_P(number2) & 0xffffffff, - LOWER_P(number2) >> 32, - LOWER_P(number2) & 0xffffffff}; - uint64_t products[4][4]; - uint128_t tmp, tmp2; - - for (int y = 3; y > -1; y--) { - for (int x = 3; x > -1; x--) { - products[3 - x][y] = top[x] * bottom[y]; - } - } - - uint64_t fourth32 = products[0][3] & 0xffffffff; - uint64_t third32 = (products[0][2] & 0xffffffff) + (products[0][3] >> 32); - uint64_t second32 = (products[0][1] & 0xffffffff) + (products[0][2] >> 32); - uint64_t first32 = (products[0][0] & 0xffffffff) + (products[0][1] >> 32); - - third32 += products[1][3] & 0xffffffff; - second32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32); - first32 += (products[1][1] & 0xffffffff) + (products[1][2] >> 32); - - second32 += products[2][3] & 0xffffffff; - first32 += (products[2][2] & 0xffffffff) + (products[2][3] >> 32); - - first32 += products[3][3] & 0xffffffff; - - UPPER(tmp) = first32 << 32; - LOWER(tmp) = 0; - UPPER(tmp2) = third32 >> 32; - LOWER(tmp2) = third32 << 32; - add128(&tmp, &tmp2, target); - UPPER(tmp) = second32; - LOWER(tmp) = 0; - add128(&tmp, target, &tmp2); - UPPER(tmp) = 0; - LOWER(tmp) = fourth32; - add128(&tmp, &tmp2, target); -} - -void write_u64_be(uint8_t *const buffer, uint64_t value) { - buffer[0] = ((value >> 56) & 0xff); - buffer[1] = ((value >> 48) & 0xff); - buffer[2] = ((value >> 40) & 0xff); - buffer[3] = ((value >> 32) & 0xff); - buffer[4] = ((value >> 24) & 0xff); - buffer[5] = ((value >> 16) & 0xff); - buffer[6] = ((value >> 8) & 0xff); - buffer[7] = (value & 0xff); -} - -void read_u64_be(const uint8_t *const in, uint64_t *const out) { - uint8_t *out_ptr = (uint8_t *) out; - *out_ptr++ = in[7]; - *out_ptr++ = in[6]; - *out_ptr++ = in[5]; - *out_ptr++ = in[4]; - *out_ptr++ = in[3]; - *out_ptr++ = in[2]; - *out_ptr++ = in[1]; - *out_ptr = in[0]; -} - void mul256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target) { @@ -375,40 +181,6 @@ void mul256(const uint256_t *const number1, } } -void divmod128(const uint128_t *const l, - const uint128_t *const r, - uint128_t *const retDiv, - uint128_t *const retMod) { - uint128_t copyd, adder, resDiv, resMod; - uint128_t one; - UPPER(one) = 0; - LOWER(one) = 1; - uint32_t diffBits = bits128(l) - bits128(r); - clear128(&resDiv); - copy128(&resMod, l); - if (gt128(r, l)) { - copy128(retMod, l); - clear128(retDiv); - } else { - shiftl128(r, diffBits, ©d); - shiftl128(&one, diffBits, &adder); - if (gt128(©d, &resMod)) { - shiftr128(©d, 1, ©d); - shiftr128(&adder, 1, &adder); - } - while (gte128(&resMod, r)) { - if (gte128(&resMod, ©d)) { - sub128(&resMod, ©d, &resMod); - or128(&resDiv, &adder, &resDiv); - } - shiftr128(©d, 1, ©d); - shiftr128(&adder, 1, &adder); - } - copy128(retDiv, &resDiv); - copy128(retMod, &resMod); - } -} - void divmod256(const uint256_t *const l, const uint256_t *const r, uint256_t *const retDiv, @@ -444,43 +216,6 @@ void divmod256(const uint256_t *const l, } } -static void reverseString(char *str, uint32_t length) { - uint32_t i, j; - for (i = 0, j = length - 1; i < j; i++, j--) { - uint8_t c; - c = str[i]; - str[i] = str[j]; - str[j] = c; - } -} - -bool tostring128(const uint128_t *const number, - uint32_t baseParam, - char *const out, - uint32_t outLength) { - uint128_t rDiv; - uint128_t rMod; - uint128_t base; - copy128(&rDiv, number); - clear128(&rMod); - clear128(&base); - LOWER(base) = baseParam; - uint32_t offset = 0; - if ((baseParam < 2) || (baseParam > 16)) { - return false; - } - do { - if (offset > (outLength - 1)) { - return false; - } - divmod128(&rDiv, &base, &rDiv, &rMod); - out[offset++] = HEXDIGITS[(uint8_t) LOWER(rMod)]; - } while (!zero128(&rDiv)); - out[offset] = '\0'; - reverseString(out, offset); - return true; -} - bool tostring256(const uint256_t *const number, uint32_t baseParam, char *const out, diff --git a/src_common/uint256.h b/src_common/uint256.h index 837bfe4..dfc97f7 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -22,74 +22,36 @@ #include #include -#include "os.h" -#include "cx.h" -#include "ethUstream.h" - -typedef struct uint128_t { - uint64_t elements[2]; -} uint128_t; +#include "uint128.h" typedef struct uint256_t { uint128_t elements[2]; } uint256_t; -#define UPPER_P(x) x->elements[0] -#define LOWER_P(x) x->elements[1] -#define UPPER(x) x.elements[0] -#define LOWER(x) x.elements[1] - -void readu128BE(const uint8_t *const buffer, uint128_t *target); -void readu256BE(const uint8_t *const buffer, uint256_t *target); -void write_u64_be(uint8_t *const buffer, uint64_t value); -void read_u64_be(const uint8_t *const in, uint64_t *const out); -bool zero128(const uint128_t *const number); +void readu256BE(const uint8_t *const buffer, uint256_t *const target); bool zero256(const uint256_t *const number); -void copy128(uint128_t *const target, const uint128_t *const number); void copy256(uint256_t *const target, const uint256_t *const number); -void clear128(uint128_t *const target); void clear256(uint256_t *const target); -void shiftl128(const uint128_t *const number, uint32_t value, uint128_t *const target); -void shiftr128(const uint128_t *const number, uint32_t value, uint128_t *const target); void shiftl256(const uint256_t *const number, uint32_t value, uint256_t *const target); void shiftr256(const uint256_t *const number, uint32_t value, uint256_t *const target); -uint32_t bits128(const uint128_t *const number); uint32_t bits256(const uint256_t *const number); -bool equal128(const uint128_t *const number1, const uint128_t *const number2); bool equal256(const uint256_t *const number1, const uint256_t *const number2); -bool gt128(const uint128_t *const number1, const uint128_t *const number2); bool gt256(const uint256_t *const number1, const uint256_t *const number2); -bool gte128(const uint128_t *const number1, const uint128_t *const number2); bool gte256(const uint256_t *const number1, const uint256_t *const number2); -void add128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target); void add256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target); -void sub128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target); void sub256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target); -void or128(const uint128_t *const number1, const uint128_t *const number2, uint128_t *const target); void or256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target); -void mul128(const uint128_t *const number1, - const uint128_t *const number2, - uint128_t *const target); void mul256(const uint256_t *const number1, const uint256_t *const number2, uint256_t *const target); -void divmod128(const uint128_t *const l, - const uint128_t *const r, - uint128_t *const div, - uint128_t *const mod); void divmod256(const uint256_t *const l, const uint256_t *const r, uint256_t *const div, uint256_t *const mod); -bool tostring128(const uint128_t *const number, uint32_t base, char *const out, uint32_t outLength); bool tostring256(const uint256_t *const number, uint32_t base, char *const out, uint32_t outLength); #endif // _UINT256_H_ diff --git a/src_common/uint_common.c b/src_common/uint_common.c new file mode 100644 index 0000000..237641c --- /dev/null +++ b/src_common/uint_common.c @@ -0,0 +1,62 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2016-2019 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +// Adapted from https://github.com/calccrypto/uint256_t + +#include "uint_common.h" + +const char HEXDIGITS[] = "0123456789abcdef"; + +void write_u64_be(uint8_t *const buffer, uint64_t value) { + buffer[0] = ((value >> 56) & 0xff); + buffer[1] = ((value >> 48) & 0xff); + buffer[2] = ((value >> 40) & 0xff); + buffer[3] = ((value >> 32) & 0xff); + buffer[4] = ((value >> 24) & 0xff); + buffer[5] = ((value >> 16) & 0xff); + buffer[6] = ((value >> 8) & 0xff); + buffer[7] = (value & 0xff); +} + +void read_u64_be(const uint8_t *const in, uint64_t *const out) { + uint8_t *out_ptr = (uint8_t *) out; + *out_ptr++ = in[7]; + *out_ptr++ = in[6]; + *out_ptr++ = in[5]; + *out_ptr++ = in[4]; + *out_ptr++ = in[3]; + *out_ptr++ = in[2]; + *out_ptr++ = in[1]; + *out_ptr = in[0]; +} + +uint64_t readUint64BE(const uint8_t *const buffer) { + return (((uint64_t) buffer[0]) << 56) | (((uint64_t) buffer[1]) << 48) | + (((uint64_t) buffer[2]) << 40) | (((uint64_t) buffer[3]) << 32) | + (((uint64_t) buffer[4]) << 24) | (((uint64_t) buffer[5]) << 16) | + (((uint64_t) buffer[6]) << 8) | (((uint64_t) buffer[7])); +} + +void reverseString(char *const str, uint32_t length) { + uint32_t i, j; + for (i = 0, j = length - 1; i < j; i++, j--) { + char c; + c = str[i]; + str[i] = str[j]; + str[j] = c; + } +} diff --git a/src_common/uint_common.h b/src_common/uint_common.h new file mode 100644 index 0000000..3f2fec1 --- /dev/null +++ b/src_common/uint_common.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Ledger Ethereum App + * (c) 2016-2019 Ledger + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ********************************************************************************/ + +// Adapted from https://github.com/calccrypto/uint256_t + +#ifndef _UINT_COMMON_H_ +#define _UINT_COMMON_H_ + +#include + +#define UPPER_P(x) x->elements[0] +#define LOWER_P(x) x->elements[1] +#define UPPER(x) x.elements[0] +#define LOWER(x) x.elements[1] + +extern const char HEXDIGITS[]; + +void write_u64_be(uint8_t *const buffer, uint64_t value); +void read_u64_be(const uint8_t *const in, uint64_t *const out); +uint64_t readUint64BE(const uint8_t *const buffer); +void reverseString(char *const str, uint32_t length); + +#endif //_UINT_COMMON_H_ From f99804de08c64531ea6fcd508e1cf32ac237d0b2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 6 Jun 2022 18:36:20 +0200 Subject: [PATCH 078/184] Added a signed int256 formatting function --- src_common/uint128.c | 39 +++++++++++++++++++++++++++++++++++++++ src_common/uint128.h | 4 ++++ src_common/uint256.c | 39 +++++++++++++++++++++++++++++++++++++++ src_common/uint256.h | 5 +++++ 4 files changed, 87 insertions(+) diff --git a/src_common/uint128.c b/src_common/uint128.c index e286412..1962cc8 100644 --- a/src_common/uint128.c +++ b/src_common/uint128.c @@ -245,3 +245,42 @@ bool tostring128(const uint128_t *const number, reverseString(out, offset); return true; } + +/** + * Format a uint128_t into a string as a signed integer + * + * @param[in] number the number to format + * @param[in] base the radix used in formatting + * @param[out] out the output buffer + * @param[in] out_length the length of the output buffer + * @return whether the formatting was successful or not + */ +bool tostring128_signed(const uint128_t *const number, + uint32_t base, + char *const out, + uint32_t out_length) { + uint128_t max_unsigned_val; + uint128_t max_signed_val; + uint128_t one_val; + uint128_t two_val; + uint128_t tmp; + + // showing negative numbers only really makes sense in base 10 + if (base == 10) { + explicit_bzero(&one_val, sizeof(one_val)); + LOWER(one_val) = 1; + explicit_bzero(&two_val, sizeof(two_val)); + LOWER(two_val) = 2; + + memset(&max_unsigned_val, 0xFF, sizeof(max_unsigned_val)); + divmod128(&max_unsigned_val, &two_val, &max_signed_val, &tmp); + if (gt128(number, &max_signed_val)) // negative value + { + sub128(&max_unsigned_val, number, &tmp); + add128(&tmp, &one_val, &tmp); + out[0] = '-'; + return tostring128(&tmp, base, out + 1, out_length - 1); + } + } + return tostring128(number, base, out, out_length); // positive value +} diff --git a/src_common/uint128.h b/src_common/uint128.h index 8f76504..e1166c2 100644 --- a/src_common/uint128.h +++ b/src_common/uint128.h @@ -52,5 +52,9 @@ void divmod128(const uint128_t *const l, uint128_t *const div, uint128_t *const mod); bool tostring128(const uint128_t *const number, uint32_t base, char *const out, uint32_t outLength); +bool tostring128_signed(const uint128_t *const number, + uint32_t base, + char *const out, + uint32_t out_length); #endif // _UINT128_H_ diff --git a/src_common/uint256.c b/src_common/uint256.c index e8698ee..68ac0a3 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -248,3 +248,42 @@ bool tostring256(const uint256_t *const number, reverseString(out, offset); return true; } + +/** + * Format a uint256_t into a string as a signed integer + * + * @param[in] number the number to format + * @param[in] base the radix used in formatting + * @param[out] out the output buffer + * @param[in] out_length the length of the output buffer + * @return whether the formatting was successful or not + */ +bool tostring256_signed(const uint256_t *const number, + uint32_t base, + char *const out, + uint32_t out_length) { + uint256_t max_unsigned_val; + uint256_t max_signed_val; + uint256_t one_val; + uint256_t two_val; + uint256_t tmp; + + // showing negative numbers only really makes sense in base 10 + if (base == 10) { + explicit_bzero(&one_val, sizeof(one_val)); + LOWER(LOWER(one_val)) = 1; + explicit_bzero(&two_val, sizeof(two_val)); + LOWER(LOWER(two_val)) = 2; + + memset(&max_unsigned_val, 0xFF, sizeof(max_unsigned_val)); + divmod256(&max_unsigned_val, &two_val, &max_signed_val, &tmp); + if (gt256(number, &max_signed_val)) // negative value + { + sub256(&max_unsigned_val, number, &tmp); + add256(&tmp, &one_val, &tmp); + out[0] = '-'; + return tostring256(&tmp, base, out + 1, out_length - 1); + } + } + return tostring256(number, base, out, out_length); // positive value +} diff --git a/src_common/uint256.h b/src_common/uint256.h index dfc97f7..351953b 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -53,5 +53,10 @@ void divmod256(const uint256_t *const l, uint256_t *const div, uint256_t *const mod); bool tostring256(const uint256_t *const number, uint32_t base, char *const out, uint32_t outLength); +bool tostring256_signed(const uint256_t *const number, + uint32_t base, + char *const out, + uint32_t out_length); + #endif // _UINT256_H_ From 10fbb8d5bc59f8aaa86c97307c8c93ba23d4368c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 7 Jun 2022 14:44:01 +0200 Subject: [PATCH 079/184] Unify all the hex digits used in the app --- src/utils.c | 7 ++----- src_common/ethUtils.c | 2 -- src_common/uint128.c | 1 + src_common/uint256.c | 3 ++- src_common/uint_common.c | 2 -- src_common/uint_common.h | 2 -- 6 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/utils.c b/src/utils.c index 791b015..f044e98 100644 --- a/src/utils.c +++ b/src/utils.c @@ -24,13 +24,10 @@ #include "tokens.h" #include "utils.h" -static const unsigned char hex_digits[] = - {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - void array_hexstr(char *strbuf, const void *bin, unsigned int len) { while (len--) { - *strbuf++ = hex_digits[((*((char *) bin)) >> 4) & 0xF]; - *strbuf++ = hex_digits[(*((char *) bin)) & 0xF]; + *strbuf++ = HEXDIGITS[((*((char *) bin)) >> 4) & 0xF]; + *strbuf++ = HEXDIGITS[(*((char *) bin)) & 0xF]; bin = (const void *) ((unsigned int) bin + 1); } *strbuf = 0; // EOS diff --git a/src_common/ethUtils.c b/src_common/ethUtils.c index 804837c..4dd0e6a 100644 --- a/src_common/ethUtils.c +++ b/src_common/ethUtils.c @@ -27,8 +27,6 @@ #include #include -#include "os.h" -#include "cx.h" #include "ethUtils.h" #include "chainConfig.h" #include "ethUstream.h" diff --git a/src_common/uint128.c b/src_common/uint128.c index 1962cc8..79cf2ac 100644 --- a/src_common/uint128.c +++ b/src_common/uint128.c @@ -21,6 +21,7 @@ #include #include "uint128.h" #include "uint_common.h" +#include "ethUtils.h" // HEXDIGITS void readu128BE(const uint8_t *const buffer, uint128_t *const target) { UPPER_P(target) = readUint64BE(buffer); diff --git a/src_common/uint256.c b/src_common/uint256.c index 68ac0a3..54b165b 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -19,9 +19,10 @@ #include #include -#include "ethUstream.h" // INT256_LENGTH #include "uint256.h" #include "uint_common.h" +#include "ethUstream.h" // INT256_LENGTH +#include "ethUtils.h" // HEXDIGITS void readu256BE(const uint8_t *const buffer, uint256_t *const target) { readu128BE(buffer, &UPPER_P(target)); diff --git a/src_common/uint_common.c b/src_common/uint_common.c index 237641c..5fe06a7 100644 --- a/src_common/uint_common.c +++ b/src_common/uint_common.c @@ -19,8 +19,6 @@ #include "uint_common.h" -const char HEXDIGITS[] = "0123456789abcdef"; - void write_u64_be(uint8_t *const buffer, uint64_t value) { buffer[0] = ((value >> 56) & 0xff); buffer[1] = ((value >> 48) & 0xff); diff --git a/src_common/uint_common.h b/src_common/uint_common.h index 3f2fec1..4a22171 100644 --- a/src_common/uint_common.h +++ b/src_common/uint_common.h @@ -27,8 +27,6 @@ #define UPPER(x) x.elements[0] #define LOWER(x) x.elements[1] -extern const char HEXDIGITS[]; - void write_u64_be(uint8_t *const buffer, uint64_t value); void read_u64_be(const uint8_t *const in, uint64_t *const out); uint64_t readUint64BE(const uint8_t *const buffer); From f3e307423e7110790b55346116cb9723139e8025 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 7 Jun 2022 15:07:54 +0200 Subject: [PATCH 080/184] EIP712 now displays integers differently if signed or unsigned --- src/utils.c | 2 +- src/utils.h | 2 +- src_features/signMessageEIP712/ui_logic.c | 10 ++++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/utils.c b/src/utils.c index f044e98..be55825 100644 --- a/src/utils.c +++ b/src/utils.c @@ -33,7 +33,7 @@ void array_hexstr(char *strbuf, const void *bin, unsigned int len) { *strbuf = 0; // EOS } -void convertUint256BE(uint8_t *data, uint32_t length, uint256_t *target) { +void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target) { uint8_t tmp[INT256_LENGTH]; memset(tmp, 0, 32); memmove(tmp + 32 - length, data, length); diff --git a/src/utils.h b/src/utils.h index 306c27b..407039b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,7 +24,7 @@ void array_hexstr(char* strbuf, const void* bin, unsigned int len); -void convertUint256BE(uint8_t* data, uint32_t length, uint256_t* target); +void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target); uint64_t u64_from_BE(const uint8_t* in, uint8_t size); diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 67869bc..ece0791 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -14,6 +14,8 @@ #include "utils.h" // uint256_to_decimal #include "common_712.h" #include "context.h" // eip712_context_deinit +#include "uint256.h" // tostring256 +#include "int256.h" // tostring256_s static t_ui_context *ui_ctx = NULL; @@ -108,6 +110,7 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, { const char *key; uint8_t key_len; + uint256_t value256; if (ui_ctx == NULL) { @@ -162,10 +165,13 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, strcat(strings.tmp.tmp, "..."); } break; - // TODO: signed integers should be handled differently case TYPE_SOL_INT: + convertUint256BE(data, length, &value256); + tostring256_s(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; case TYPE_SOL_UINT: - uint256_to_decimal(data, length, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + convertUint256BE(data, length, &value256); + tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); break; default: PRINTF("Unhandled type\n"); From bbcfe5abc3aaca5e7cbdc16546a0539ce19b914e Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 8 Jun 2022 11:38:08 +0200 Subject: [PATCH 081/184] Added a util function to convert from byte buffer to uint128_t --- src/utils.c | 14 ++++++++++++-- src/utils.h | 1 + src_common/ethUstream.h | 1 + 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/utils.c b/src/utils.c index be55825..3fb4cc9 100644 --- a/src/utils.c +++ b/src/utils.c @@ -20,6 +20,7 @@ #include "ethUstream.h" #include "ethUtils.h" +#include "uint128.h" #include "uint256.h" #include "tokens.h" #include "utils.h" @@ -33,10 +34,19 @@ void array_hexstr(char *strbuf, const void *bin, unsigned int len) { *strbuf = 0; // EOS } +void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target) { + uint8_t tmp[INT128_LENGTH]; + + memset(tmp, 0, sizeof(tmp) - length); + memmove(tmp + sizeof(tmp) - length, data, length); + readu128BE(tmp, target); +} + void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target) { uint8_t tmp[INT256_LENGTH]; - memset(tmp, 0, 32); - memmove(tmp + 32 - length, data, length); + + memset(tmp, 0, sizeof(tmp) - length); + memmove(tmp + sizeof(tmp) - length, data, length); readu256BE(tmp, target); } diff --git a/src/utils.h b/src/utils.h index 407039b..ce39d65 100644 --- a/src/utils.h +++ b/src/utils.h @@ -24,6 +24,7 @@ void array_hexstr(char* strbuf, const void* bin, unsigned int len); +void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target); void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target); uint64_t u64_from_BE(const uint8_t* in, uint8_t size); diff --git a/src_common/ethUstream.h b/src_common/ethUstream.h index 4229d40..ed5aea3 100644 --- a/src_common/ethUstream.h +++ b/src_common/ethUstream.h @@ -37,6 +37,7 @@ typedef customStatus_e (*ustreamProcess_t)(struct txContext_t *context); #define TX_FLAG_TYPE 0x01 #define ADDRESS_LENGTH 20 +#define INT128_LENGTH 16 #define INT256_LENGTH 32 // First variant of every Tx enum. From d5603857b3360d41d3bc99b4878fc9e4fb516906 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 8 Jun 2022 18:30:25 +0200 Subject: [PATCH 082/184] Fix UI display of signed negative integers smaller than 256 bits --- src/utils.c | 10 +++++ src/utils.h | 1 + src_features/signMessageEIP712/ui_logic.c | 53 +++++++++++++++++++++-- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/utils.c b/src/utils.c index 3fb4cc9..be03f10 100644 --- a/src/utils.c +++ b/src/utils.c @@ -34,6 +34,16 @@ void array_hexstr(char *strbuf, const void *bin, unsigned int len) { *strbuf = 0; // EOS } +void convertUint64BEto128(const uint8_t *const data, uint32_t length, uint128_t *const target) { + uint8_t tmp[INT128_LENGTH]; + int64_t value; + + value = u64_from_BE(data, length); + memset(tmp, ((value < 0) ? 0xff : 0), sizeof(tmp) - length); + memmove(tmp + sizeof(tmp) - length, data, length); + readu128BE(tmp, target); +} + void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target) { uint8_t tmp[INT128_LENGTH]; diff --git a/src/utils.h b/src/utils.h index ce39d65..3477081 100644 --- a/src/utils.h +++ b/src/utils.h @@ -26,6 +26,7 @@ void array_hexstr(char* strbuf, const void* bin, unsigned int len); void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target); void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target); +void convertUint64BEto128(const uint8_t *const data, uint32_t length, uint128_t *const target); uint64_t u64_from_BE(const uint8_t* in, uint8_t size); diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index ece0791..d811b35 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -14,8 +14,7 @@ #include "utils.h" // uint256_to_decimal #include "common_712.h" #include "context.h" // eip712_context_deinit -#include "uint256.h" // tostring256 -#include "int256.h" // tostring256_s +#include "uint256.h" // tostring256 && tostring256_signed static t_ui_context *ui_ctx = NULL; @@ -111,6 +110,9 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, const char *key; uint8_t key_len; uint256_t value256; + uint128_t value128; + int32_t value32; + int16_t value16; if (ui_ctx == NULL) { @@ -166,8 +168,51 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, } break; case TYPE_SOL_INT: - convertUint256BE(data, length, &value256); - tostring256_s(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + switch (get_struct_field_typesize(field_ptr) * 8) + { + case 256: + convertUint256BE(data, length, &value256); + tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; + case 128: + convertUint128BE(data, length, &value128); + tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; + case 64: + convertUint64BEto128(data, length, &value128); + 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]; + } + 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]; + } + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + value16); // expanded to 32 bits + break; + case 8: + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + ((int8_t*)data)[0]); // expanded to 32 bits + break; + default: + PRINTF("Unhandled field typesize\n"); + } break; case TYPE_SOL_UINT: convertUint256BE(data, length, &value256); From 8ea34f9f008a32b7bbc7beeeaf41919abb834b8f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 8 Jun 2022 18:31:16 +0200 Subject: [PATCH 083/184] Fix hashing of signed negative integers smaller than 256 bits --- src_features/signMessageEIP712/encode_field.c | 55 ++++++++++++++----- src_features/signMessageEIP712/encode_field.h | 3 +- src_features/signMessageEIP712/field_hash.c | 4 +- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 4702fa4..ce78a89 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -14,28 +14,32 @@ typedef enum } e_padding_type; /** - * Encode a field value to 32 bytes (0 padded) + * Encode a field value to 32 bytes (padded) * * @param[in] value field value to encode * @param[in] length field length before encoding + * @param[in] ptype padding direction (LSB vs MSB) + * @param[in] pval value used for padding * @return encoded field value */ -static void *field_encode(const uint8_t *const value, uint8_t length, e_padding_type ptype) +static void *field_encode(const uint8_t *const value, + uint8_t length, + e_padding_type ptype, + uint8_t pval) { uint8_t *padded_value; uint8_t start_idx; - if (length > 32) // sanity check + if (length > EIP_712_ENCODED_FIELD_LENGTH) // sanity check { return NULL; } - // 0-pad the value to 32 bytes if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) { switch (ptype) { case MSB: - explicit_bzero(padded_value, EIP_712_ENCODED_FIELD_LENGTH - length); + memset(padded_value, pval, EIP_712_ENCODED_FIELD_LENGTH - length); start_idx = EIP_712_ENCODED_FIELD_LENGTH - length; break; case LSB: @@ -45,25 +49,46 @@ static void *field_encode(const uint8_t *const value, uint8_t length, e_padding_ default: return NULL; // should not be here } - for (uint8_t idx = 0; idx < length; ++idx) - { - padded_value[start_idx + idx] = value[idx]; - } + memcpy(&padded_value[start_idx], value, length); } return padded_value; } /** - * Encode an integer + * Encode an unsigned integer * * @param[in] value pointer to the "packed" integer received * @param[in] length its byte-length * @return the encoded value */ -void *encode_integer(const uint8_t *const value, uint8_t length) +void *encode_uint(const uint8_t *const value, uint8_t length) { // no length check here since it will be checked by field_encode - return field_encode(value, length, MSB); + return field_encode(value, length, MSB, 0x00); +} + +/** + * Encode a signed integer + * + * @param[in] value pointer to the "packed" integer received + * @param[in] length its byte-length + * @param[in] typesize the type size in bytes + * @return the encoded value + */ +void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize) +{ + uint8_t padding_value; + + if ((length == typesize) && (value[0] & (1 << 7))) // negative number + { + padding_value = 0xFF; + } + else + { + padding_value = 0x00; + } + // no length check here since it will be checked by field_encode + return field_encode(value, length, MSB, padding_value); } /** @@ -76,7 +101,7 @@ void *encode_integer(const uint8_t *const value, uint8_t length) void *encode_bytes(const uint8_t *const value, uint8_t length) { // no length check here since it will be checked by field_encode - return field_encode(value, length, LSB); + return field_encode(value, length, LSB, 0x00); } /** @@ -92,7 +117,7 @@ void *encode_boolean(const bool *const value, uint8_t length) { return NULL; } - return encode_integer((uint8_t*)value, length); + return encode_uint((uint8_t*)value, length); } /** @@ -108,7 +133,7 @@ void *encode_address(const uint8_t *const value, uint8_t length) { return NULL; } - return encode_integer(value, length); + return encode_uint(value, length); } #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index 6bb58e4..a8e99f9 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -8,7 +8,8 @@ #define EIP_712_ENCODED_FIELD_LENGTH 32 -void *encode_integer(const uint8_t *const value, uint8_t length); +void *encode_uint(const uint8_t *const value, uint8_t length); +void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize); void *encode_boolean(const bool *const value, uint8_t length); void *encode_address(const uint8_t *const value, uint8_t length); void *encode_bytes(const uint8_t *const value, uint8_t length); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index da1a997..8e47d4b 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -115,8 +115,10 @@ bool field_hash(const uint8_t *data, switch (field_type) { case TYPE_SOL_INT: + value = encode_int(data, data_length, get_struct_field_typesize(field_ptr)); + break; case TYPE_SOL_UINT: - value = encode_integer(data, data_length); + value = encode_uint(data, data_length); break; case TYPE_SOL_BYTES_FIX: value = encode_bytes(data, data_length); From f480d5091ef1a16a8bf8fc6b58fbaa29fbffa979 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 9 Jun 2022 12:00:42 +0200 Subject: [PATCH 084/184] Now uses an eip712 context struct instead of having multiple global variables => reduced bss footprint --- src_features/signMessageEIP712/context.c | 19 ++++++++++--------- src_features/signMessageEIP712/context.h | 13 ++++++++----- src_features/signMessageEIP712/entrypoint.c | 10 +++++----- src_features/signMessageEIP712/path.c | 10 +++++----- .../signMessageEIP712/sol_typenames.c | 8 ++++---- 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 27a937e..c219858 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -5,15 +5,13 @@ #include "context.h" #include "eip712.h" #include "mem.h" +#include "mem_utils.h" #include "sol_typenames.h" #include "path.h" #include "field_hash.h" #include "ui_logic.h" -uint8_t *typenames_array; -uint8_t *structs_array; -uint8_t *current_struct_fields_array; -bool eip712_context_initialized = false; +s_eip712_context *eip712_context = NULL; /** * @@ -24,6 +22,11 @@ bool eip712_context_init(void) // init global variables mem_init(); + if ((eip712_context = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*eip712_context), *eip712_context)) == NULL) + { + return false; + } + if (sol_typenames_init() == false) { return false; @@ -45,15 +48,13 @@ bool eip712_context_init(void) } // set types pointer - if ((structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + if ((eip712_context->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } // create len(types) - *structs_array = 0; - - eip712_context_initialized = true; + *(eip712_context->structs_array) = 0; return true; } @@ -64,7 +65,7 @@ void eip712_context_deinit(void) field_hash_deinit(); ui_712_deinit(); mem_reset(); - eip712_context_initialized = false; + eip712_context = NULL; } #endif diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index a85f56a..9bb2e0c 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -5,15 +5,18 @@ #include -extern uint8_t *typenames_array; -extern uint8_t *structs_array; -extern uint8_t *current_struct_fields_array; +typedef struct +{ + uint8_t *typenames_array; + uint8_t *structs_array; + uint8_t *current_struct_fields_array; +} s_eip712_context; + +extern s_eip712_context *eip712_context; bool eip712_context_init(void); void eip712_context_deinit(void); -extern bool eip712_context_initialized; - #endif // HAVE_EIP712_FULL_SUPPORT #endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index cb4eb34..1be3b8c 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -239,7 +239,7 @@ bool set_struct_name(const uint8_t *const data) char *name_ptr; // increment number of structs - *structs_array += 1; + *(eip712_context->structs_array) += 1; // copy length if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) @@ -256,11 +256,11 @@ bool set_struct_name(const uint8_t *const data) memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); // initialize number of fields - if ((current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) + if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } - *current_struct_fields_array = 0; + *(eip712_context->current_struct_fields_array) = 0; return true; } @@ -280,7 +280,7 @@ bool set_struct_field(const uint8_t *const data) char *fieldname_ptr; // increment number of struct fields - *current_struct_fields_array += 1; + *(eip712_context->current_struct_fields_array) += 1; // copy TypeDesc if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) @@ -369,7 +369,7 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { bool ret = true; - if (!eip712_context_initialized) + if (eip712_context == NULL) { if (!eip712_context_init()) { diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 7638d31..98025aa 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -59,7 +59,7 @@ static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, if (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &length); - if ((struct_ptr = get_structn(structs_array, typename, length)) == NULL) + if ((struct_ptr = get_structn(eip712_context->structs_array, typename, length)) == NULL) { return NULL; } @@ -273,7 +273,7 @@ static bool path_update(void) while (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &typename_len); - if ((struct_ptr = get_structn(structs_array, typename, typename_len)) == NULL) + if ((struct_ptr = get_structn(eip712_context->structs_array, typename, typename_len)) == NULL) { return false; } @@ -293,7 +293,7 @@ static bool path_update(void) } cx_keccak_init(hash_ctx, 256); // initialize it // get the struct typehash - if ((thash_ptr = type_hash(structs_array, typename, typename_len)) == NULL) + if ((thash_ptr = type_hash(eip712_context->structs_array, typename, typename_len)) == NULL) { return false; } @@ -326,7 +326,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } - path_struct->root_struct = get_structn(structs_array, struct_name, name_length); + path_struct->root_struct = get_structn(eip712_context->structs_array, struct_name, name_length); if (path_struct->root_struct == NULL) { @@ -347,7 +347,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } cx_keccak_init(hash_ctx, 256); // init hash - if ((thash_ptr = type_hash(structs_array, struct_name, name_length)) == NULL) + if ((thash_ptr = type_hash(eip712_context->structs_array, struct_name, name_length)) == NULL) { PRINTF("Memory allocation failed!\n"); return false; diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index b9cbf53..ea1d1f2 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -64,11 +64,11 @@ bool sol_typenames_init(void) uint8_t *typename_len_ptr; char *typename_ptr; - if ((typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) + if ((eip712_context->typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } - *typenames_array = 0; + *(eip712_context->typenames_array) = 0; // loop over typenames for (uint8_t s_idx = 0; s_idx < ARRAY_SIZE(typenames); ++s_idx) { @@ -90,7 +90,7 @@ bool sol_typenames_init(void) memcpy(typename_ptr, PIC(typenames[s_idx]), *typename_len_ptr); } // increment array size - *typenames_array += 1; + *(eip712_context->typenames_array) += 1; } return true; } @@ -111,7 +111,7 @@ const char *get_struct_field_sol_typename(const uint8_t *field_ptr, bool typename_found; field_type = struct_field_type(field_ptr); - typename_ptr = get_array_in_mem(typenames_array, &typenames_count); + typename_ptr = get_array_in_mem(eip712_context->typenames_array, &typenames_count); typename_found = false; while (typenames_count-- > 0) { From 7e63bd84353994fa5bd01366e8ab8b9330d86209 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 9 Jun 2022 15:21:27 +0200 Subject: [PATCH 085/184] Now copies the EIP712 contract address into memory so it can be used later --- src_features/signMessageEIP712/context.h | 2 ++ src_features/signMessageEIP712/field_hash.c | 24 +++++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 9bb2e0c..455e270 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -4,12 +4,14 @@ #ifdef HAVE_EIP712_FULL_SUPPORT #include +#include "ethUstream.h" // ADDRESS_LENGTH typedef struct { uint8_t *typenames_array; uint8_t *structs_array; uint8_t *current_struct_fields_array; + uint8_t contract_addr[ADDRESS_LENGTH]; } s_eip712_context; extern s_eip712_context *eip712_context; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 8e47d4b..19e9d5b 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -10,6 +10,7 @@ #include "shared_context.h" #include "ui_logic.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE +#include "context.h" // contract_addr static s_field_hashing *fh = NULL; @@ -39,14 +40,13 @@ bool field_hash(const uint8_t *data, const void *field_ptr; e_type field_type; uint8_t *value = NULL; + const char *key; + uint8_t keylen; #ifdef DEBUG const char *type; uint8_t typelen; - const char *key; - uint8_t keylen; #endif - if (fh == NULL) { return false; @@ -56,10 +56,7 @@ bool field_hash(const uint8_t *data, { return false; } -#ifdef HAVE_EIP712_HALF_BLIND - uint8_t keylen; - const char *key = get_struct_field_keyname(field_ptr, &keylen); -#endif // HAVE_EIP712_HALF_BLIND + key = get_struct_field_keyname(field_ptr, &keylen); field_type = struct_field_type(field_ptr); if (fh->state == FHS_IDLE) // first packet for this frame { @@ -142,8 +139,6 @@ bool field_hash(const uint8_t *data, #ifdef HAVE_EIP712_HALF_BLIND if (path_get_root_type() == ROOT_DOMAIN) { - uint8_t keylen; - const char *key = get_struct_field_keyname(field_ptr, &keylen); if ((keylen == 4) && (strncmp(key, "name", keylen) == 0)) { #endif // HAVE_EIP712_HALF_BLIND @@ -185,6 +180,17 @@ bool field_hash(const uint8_t *data, // deallocate it mem_dealloc(len); + // copy contract address into context + if ((path_get_root_type() == ROOT_DOMAIN) + && (strncmp(key, "verifyingContract", keylen) == 0)) + { + if (data_length != sizeof(eip712_context->contract_addr)) + { + PRINTF("Unexpected verifyingContract length!\n"); + return false; + } + memcpy(eip712_context->contract_addr, data, data_length); + } path_advance(); fh->state = FHS_IDLE; #ifdef HAVE_EIP712_HALF_BLIND From 9bc3f5255e5b19e66181d5b89ae5786b1589a876 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 31 May 2022 18:10:37 +0200 Subject: [PATCH 086/184] Add documentation on EIP712 filtering APDU --- doc/ethapp.adoc | 74 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 6a61197..fd1a95f 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -776,7 +776,7 @@ Sets the size of the upcoming array the following N fields will be apart of. Sets the raw value of the next field in order in the current root structure. Raw as in, an integer in the JSON file represented as "128" would only be 1 byte long (0x80) -instead of 3 as an array of ASCII characters. same for addresses and so on. +instead of 3 as an array of ASCII characters, same for addresses and so on. _Output data_ @@ -784,6 +784,78 @@ _Output data_ None +### EIP712 FILTERING + +#### Description + +This command provides a trusted way of deciding what information from the JSON data to show and replace some values by more meaningful ones. + +This mode can be overriden by the in-app setting to fully clear-sign EIP-712 messages. + +##### Activation + +Full filtering is disabled by default and has to be changed with this APDU (default behaviour is basic filtering handled by the app itself). + +Field substitution will be ignored if the full filtering is not activated. + +If activated, fields will be by default hidden unless they receive a field name substitution. + +##### Contract name substitution + +Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*. +Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation. + +The signature is computed on : + +contract address || display name length || display name + + +##### Field name substitution + +Name substitution commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones). + +The signature is computed on : + +contract address || json key length || json key || display name length || display name + +#### Coding + +_Command_ + +[width="80%"] +|========================================================================= +| *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* +| E0 | 1E | 00 : activate + + 0F : contract name + + FF : field name + | 00 + | variable | variable +|========================================================================= + +_Input data_ + +##### If P1 == activate + +None + +##### If P1 == contract name OR P1 == field name + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Display name length | 1 +| Display name | variable +| Signature length | 1 +| Signature | variable +|========================================== + +_Output data_ + +None + + ## Transport protocol ### General transport description From 07aca18ab1d5e58b83de342671c107ba4e741a27 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 14 Jun 2022 10:06:52 +0200 Subject: [PATCH 087/184] New EIP-712 UX set title/value functions --- src_features/signMessageEIP712/ui_logic.c | 86 +++++++++++++++++------ src_features/signMessageEIP712/ui_logic.h | 2 + 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index d811b35..1948c2a 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -20,6 +20,51 @@ static t_ui_context *ui_ctx = NULL; +static void ui_712_set_buf(const char *const src, + size_t src_length, + char *const dst, + size_t dst_length, + bool explicit_trunc) +{ + uint8_t cpy_length; + + if (src_length < dst_length) + { + cpy_length = src_length; + } + else + { + cpy_length = dst_length - 1; + } + memcpy(dst, src, cpy_length); + dst[cpy_length] = '\0'; + if (explicit_trunc && (src_length > dst_length)) + { + memcpy(dst + cpy_length - 3, "...", 3); + } +} + +/** + * Set a new title for the EIP-712 generic UX_STEP + * + * @param[in] str the new title + * @param[in] length its length + */ +void ui_712_set_title(const char *const str, uint8_t length) +{ + ui_712_set_buf(str, length, strings.tmp.tmp2, sizeof(strings.tmp.tmp2), false); +} + +/** + * Set a new value for the EIP-712 generic UX_STEP + * + * @param[in] str the new value + * @param[in] length its length + */ +void ui_712_set_value(const char *const str, uint8_t length) +{ + ui_712_set_buf(str, length, strings.tmp.tmp, sizeof(strings.tmp.tmp), true); +} /** * Called on the intermediate dummy screen between the dynamic step @@ -60,18 +105,18 @@ void ui_712_next_field(void) */ void ui_712_new_root_struct(const void *const struct_ptr) { + const char *struct_name; + uint8_t struct_name_length; + const char *const title = "Review struct"; + if (ui_ctx == NULL) { return; } - - strcpy(strings.tmp.tmp2, "Review struct"); - const char *struct_name; - uint8_t struct_name_length; + ui_712_set_title(title, strlen(title)); if ((struct_name = get_struct_name(struct_ptr, &struct_name_length)) != NULL) { - strncpy(strings.tmp.tmp, struct_name, struct_name_length); - strings.tmp.tmp[struct_name_length] = '\0'; + ui_712_set_value(struct_name, struct_name_length); } if (!ui_ctx->shown) { @@ -87,7 +132,9 @@ void ui_712_new_root_struct(const void *const struct_ptr) #ifdef HAVE_EIP712_HALF_BLIND void ui_712_message_hash(void) { - strcpy(strings.tmp.tmp2, "Message hash"); + const char *const title = "Message hash"; + + ui_712_set_title(title, strlen(titltitlee)); snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", @@ -122,25 +169,15 @@ 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) { - strncpy(strings.tmp.tmp2, key, MIN(key_len, sizeof(strings.tmp.tmp2) - 1)); - strings.tmp.tmp2[key_len] = '\0'; + ui_712_set_title(key, key_len); } // Value - strings.tmp.tmp[0] = '\0'; // empty string + ui_712_set_value("", 0); switch (struct_field_type(field_ptr)) { case TYPE_SOL_STRING: - strncat(strings.tmp.tmp, (char*)data, sizeof(strings.tmp.tmp) - 1); - if (length > (sizeof(strings.tmp.tmp) - 1)) - { - strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - 3] = '\0'; - strcat(strings.tmp.tmp, "..."); - } - else - { - strings.tmp.tmp[length] = '\0'; - } + ui_712_set_value((char*)data, length); break; case TYPE_SOL_ADDRESS: getEthDisplayableAddress((uint8_t*)data, @@ -150,7 +187,14 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, chainConfig->chainId); break; case TYPE_SOL_BOOL: - strcpy(strings.tmp.tmp, (*data == false) ? "false" : "true"); + if (*data) + { + ui_712_set_value("true", 4); + } + else + { + ui_712_set_value("false", 5); + } break; case TYPE_SOL_BYTES_FIX: case TYPE_SOL_BYTES_DYN: diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 0458256..8c0ec22 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -27,6 +27,8 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, ui void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); +void ui_712_set_title(const char *const str, uint8_t length); +void ui_712_set_value(const char *const str, uint8_t length); #ifdef HAVE_EIP712_HALF_BLIND void ui_712_message_hash(void); #endif // HAVE_EIP712_HALF_BLIND From 56e0875979784eecd3e0e1645fb0cc8513ae73d4 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 16 Jun 2022 17:52:08 +0200 Subject: [PATCH 088/184] Made the hacky 712 generic step redraw a function --- src_features/signMessageEIP712/ui_logic.c | 18 +++++++++++------- src_features/signMessageEIP712/ui_logic.h | 1 + 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 1948c2a..c80e546 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -66,6 +66,16 @@ void ui_712_set_value(const char *const str, uint8_t length) ui_712_set_buf(str, length, strings.tmp.tmp, sizeof(strings.tmp.tmp), true); } +void ui_712_redraw_generic_step(void) +{ + // not pretty, manually changes the internal state of the UX flow + // so that we always land on the first screen of a paging step without any visible + // screen glitching (quick screen switching) + G_ux.flow_stack[G_ux.stack_count - 1].index = 0; + // since the flow now thinks we are displaying the first step, do next + ux_flow_next(); +} + /** * Called on the intermediate dummy screen between the dynamic step * && the approve/reject screen @@ -265,13 +275,7 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, default: PRINTF("Unhandled type\n"); } - - // not pretty, manually changes the internal state of the UX flow - // so that we always land on the first screen of a paging step without any visible - // screen glitching (quick screen switching) - G_ux.flow_stack[G_ux.stack_count - 1].index = 0; - // since the flow now thinks we are displaying the first step, do next - ux_flow_next(); + ui_712_redraw_generic_step(); } /** diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 8c0ec22..43889fc 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -32,6 +32,7 @@ void ui_712_set_value(const char *const str, uint8_t length); #ifdef HAVE_EIP712_HALF_BLIND void ui_712_message_hash(void); #endif // HAVE_EIP712_HALF_BLIND +void ui_712_redraw_generic_step(void); #endif // HAVE_EIP712_FULL_SUPPORT From a24f621ce3e4fc531f62d0dc450d2bf10a3ab6a6 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 13 Jun 2022 12:50:55 +0200 Subject: [PATCH 089/184] EIP712 filtering, added new APDUs --- src/apdu_constants.h | 4 + src/main.c | 5 + src_features/signMessageEIP712/context.c | 1 - src_features/signMessageEIP712/eip712.h | 5 + src_features/signMessageEIP712/entrypoint.c | 272 ++++++++++++++++++++ src_features/signMessageEIP712/path.c | 2 + src_features/signMessageEIP712/ui_logic.c | 27 +- src_features/signMessageEIP712/ui_logic.h | 15 ++ 8 files changed, 327 insertions(+), 4 deletions(-) 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 From b7a4c0d93b8356cfb177b8da61b383150783f17a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 20 Jun 2022 15:15:58 +0200 Subject: [PATCH 090/184] EIP-712 filtering UI adjustments; Removed half-blind special compilation flag --- Makefile | 1 - src_features/signMessageEIP712/eip712.h | 1 - src_features/signMessageEIP712/entrypoint.c | 58 ++++++++--- src_features/signMessageEIP712/field_hash.c | 31 +----- src_features/signMessageEIP712/path.c | 31 +++--- src_features/signMessageEIP712/path.h | 1 + src_features/signMessageEIP712/ui_logic.c | 103 +++++++++++++++----- src_features/signMessageEIP712/ui_logic.h | 9 +- 8 files changed, 137 insertions(+), 98 deletions(-) diff --git a/Makefile b/Makefile index d2c0b32..872bc15 100644 --- a/Makefile +++ b/Makefile @@ -140,7 +140,6 @@ endif # EIP-712 ifneq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_EIP712_FULL_SUPPORT -#DEFINES += HAVE_EIP712_HALF_BLIND # temporary endif # Enabling debug PRINTF diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index d0b6964..ff68f5b 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -61,7 +61,6 @@ typedef enum #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) #define DOMAIN_STRUCT_NAME "EIP712Domain" -#define DOMAIN_STRUCT_NAME_LENGTH 12 // TODO: Move these into a new file const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 4fa4919..754484c 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -409,21 +409,39 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { bool ret = true; + bool reply_apdu = true; switch (apdu_buf[OFFSET_P2]) { case P2_NAME: // set root type - ret = path_set_root((char*)&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]); + if (path_set_root((char*)&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC])) + { + ui_712_field_flags_reset(); + } + else + { + ret = false; + } break; case P2_FIELD: - ret = field_hash(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC], - apdu_buf[OFFSET_P1] != P1_COMPLETE); + if (field_hash(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC], + apdu_buf[OFFSET_P1] != P1_COMPLETE)) + { + reply_apdu = false; + } + else + { + ret = false; + } break; case P2_ARRAY: - ret = path_new_array_depth(apdu_buf[OFFSET_CDATA]); + if (!path_new_array_depth(apdu_buf[OFFSET_CDATA])) + { + ret = false; + } break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", @@ -431,11 +449,18 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) apdu_buf[OFFSET_INS]); ret = false; } - if (!ret) + if (reply_apdu) { - // Send back the response, do not restart the event loop - G_io_apdu_buffer[0] = 0x6A; - G_io_apdu_buffer[1] = 0x80; + 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; + } io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } return ret; @@ -665,10 +690,10 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) switch (apdu_buf[OFFSET_P1]) { case P1_ACTIVATE: - ui_ctx->filtering_mode = EIP712_FILTERING_FULL; + ui_712_set_filtering_mode(EIP712_FILTERING_FULL); break; case P1_CONTRACT_NAME: - if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); @@ -679,7 +704,7 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) } break; case P1_FIELD_NAME: - if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { ret = provide_field_name(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); @@ -720,9 +745,10 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) { return false; } -#ifdef HAVE_EIP712_HALF_BLIND - ui_712_message_hash(); -#endif // HAVE_EIP712_HALF_BLIND + if (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC) + { + ui_712_message_hash(); + } ui_712_end_sign(); return true; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 19e9d5b..01abe08 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -67,17 +67,7 @@ bool field_hash(const uint8_t *data, if (IS_DYN(field_type)) { cx_keccak_init(&global_sha3, 256); // init hash -#ifdef HAVE_EIP712_HALF_BLIND - if (path_get_root_type() == ROOT_DOMAIN) - { - if ((keylen == 4) && (strncmp(key, "name", keylen) == 0)) - { -#endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); -#ifdef HAVE_EIP712_HALF_BLIND - } - } -#endif // HAVE_EIP712_HALF_BLIND } } fh->remaining_size -= data_length; @@ -136,17 +126,7 @@ bool field_hash(const uint8_t *data, { return false; } -#ifdef HAVE_EIP712_HALF_BLIND - if (path_get_root_type() == ROOT_DOMAIN) - { - if ((keylen == 4) && (strncmp(key, "name", keylen) == 0)) - { -#endif // HAVE_EIP712_HALF_BLIND ui_712_new_field(field_ptr, data, data_length); -#ifdef HAVE_EIP712_HALF_BLIND - } - } -#endif // HAVE_EIP712_HALF_BLIND } else { @@ -193,16 +173,7 @@ bool field_hash(const uint8_t *data, } path_advance(); fh->state = FHS_IDLE; -#ifdef HAVE_EIP712_HALF_BLIND - if ((path_get_root_type() == ROOT_MESSAGE) || - ((keylen != 4) || (strncmp(key, "name", keylen) != 0))) - { - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - } -#endif // HAVE_EIP712_HALF_BLIND - + ui_712_finalize_field(); } else { diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index e69706e..609d6a7 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -10,7 +10,6 @@ #include "shared_context.h" #include "ethUtils.h" #include "mem_utils.h" -#include "ui_logic.h" static s_path *path_struct = NULL; @@ -370,8 +369,8 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // init array levels at 0 path_struct->array_depth_count = 0; - if ((name_length == DOMAIN_STRUCT_NAME_LENGTH) && - (strncmp(struct_name, DOMAIN_STRUCT_NAME, DOMAIN_STRUCT_NAME_LENGTH) == 0)) + if ((name_length == strlen(DOMAIN_STRUCT_NAME)) && + (strncmp(struct_name, DOMAIN_STRUCT_NAME, name_length) == 0)) { path_struct->root_type = ROOT_DOMAIN; } @@ -382,22 +381,6 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // because the first field could be a struct type path_update(); -#ifdef HAVE_EIP712_HALF_BLIND - // Only show field for the domain - if (path_get_root_type() == ROOT_MESSAGE) - { - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - } - else - { -#endif // HAVE_EIP712_HALF_BLIND - ui_712_new_root_struct(path_struct->root_struct); -#ifdef HAVE_EIP712_HALF_BLIND - } -#endif // HAVE_EIP712_HALF_BLIND - ui_712_field_flags_reset(); return true; } @@ -610,7 +593,6 @@ bool path_advance(void) } while (end_reached); path_update(); - ui_712_field_flags_reset(); return true; } @@ -623,6 +605,15 @@ e_root_type path_get_root_type(void) return path_struct->root_type; } +const void *path_get_root(void) +{ + if (path_struct == NULL) + { + return NULL; + } + return path_struct->root_struct; +} + /** * Allocates the path indexes in memory and sets it with a depth of 0. * diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index e5a05c1..ec8413f 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -38,6 +38,7 @@ bool path_init(void); void path_deinit(void); bool path_new_array_depth(uint8_t size); e_root_type path_get_root_type(void); +const void *path_get_root(void); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index daab375..1edda19 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -15,11 +15,38 @@ #include "common_712.h" #include "context.h" // eip712_context_deinit #include "uint256.h" // tostring256 && tostring256_signed +#include "path.h" // path_get_root_type -t_ui_context *ui_ctx = NULL; +static t_ui_context *ui_ctx = NULL; +/** + * Checks on the UI context to determine if the next EIP 712 field should be shown + * + * @return whether the next field should be shown + */ +static bool ui_712_field_shown(void) +{ + bool ret = false; + + if (ui_ctx->filtering_mode == EIP712_FILTERING_BASIC) + { + if (path_get_root_type() == ROOT_DOMAIN) + { + ret = true; + } + } + else // EIP712_FILTERING_FULL + { + if (ui_ctx->field_flags & UI_712_FIELD_SHOWN) + { + ret = true; + } + } + return ret; +} + static void ui_712_set_buf(const char *const src, size_t src_length, char *const dst, @@ -44,6 +71,17 @@ static void ui_712_set_buf(const char *const src, } } +void ui_712_finalize_field(void) +{ + if (!ui_712_field_shown()) + { + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + } + ui_712_field_flags_reset(); +} + /** * Set a new title for the EIP-712 generic UX_STEP * @@ -68,12 +106,20 @@ void ui_712_set_value(const char *const str, uint8_t length) void ui_712_redraw_generic_step(void) { - // not pretty, manually changes the internal state of the UX flow - // so that we always land on the first screen of a paging step without any visible - // screen glitching (quick screen switching) - G_ux.flow_stack[G_ux.stack_count - 1].index = 0; - // since the flow now thinks we are displaying the first step, do next - ux_flow_next(); + if (!ui_ctx->shown) // Initialize if it is not already + { + ux_flow_init(0, ux_712_flow, NULL); + ui_ctx->shown = true; + } + else + { + // not pretty, manually changes the internal state of the UX flow + // so that we always land on the first screen of a paging step without any visible + // screen glitching (quick screen switching) + G_ux.flow_stack[G_ux.stack_count - 1].index = 0; + // since the flow now thinks we are displaying the first step, do next + ux_flow_next(); + } } /** @@ -109,11 +155,11 @@ void ui_712_next_field(void) } /** - * Used to notify of a new struct to review (domain or message) + * Used to notify of a new struct to review * * @param[in] struct_ptr pointer to the structure */ -void ui_712_new_root_struct(const void *const struct_ptr) +void ui_712_new_struct(const void *const struct_ptr) { const char *struct_name; uint8_t struct_name_length; @@ -123,23 +169,15 @@ void ui_712_new_root_struct(const void *const struct_ptr) { return; } + ui_712_set_title(title, strlen(title)); if ((struct_name = get_struct_name(struct_ptr, &struct_name_length)) != NULL) { ui_712_set_value(struct_name, struct_name_length); } - if (!ui_ctx->shown) - { - ux_flow_init(0, ux_712_flow, NULL); - ui_ctx->shown = true; - } - else - { - ux_flow_prev(); - } + ui_712_redraw_generic_step(); } -#ifdef HAVE_EIP712_HALF_BLIND void ui_712_message_hash(void) { const char *const title = "Message hash"; @@ -150,10 +188,8 @@ void ui_712_message_hash(void) "0x%.*H", KECCAK256_HASH_BYTESIZE, tmpCtx.messageSigningContext712.messageHash); - G_ux.flow_stack[G_ux.stack_count - 1].index = 0; - ux_flow_next(); + ui_712_redraw_generic_step(); } -#endif // HAVE_EIP712_HALF_BLIND /** * Used to notify of a new field to review in the current struct (key + value) @@ -176,6 +212,12 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, return; } + // Check if this field is supposed to be displayed + if (!ui_712_field_shown()) + { + return; + } + // Key if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) { @@ -293,9 +335,10 @@ void ui_712_end_sign(void) } ui_ctx->end_reached = true; -#ifndef HAVE_EIP712_HALF_BLIND - ui_712_next_field(); -#endif // HAVE_EIP712_HALF_BLIND + if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + { + ui_712_next_field(); + } } /** @@ -359,6 +402,16 @@ void ui_712_flag_field(bool show, bool name_provided) } } +void ui_712_set_filtering_mode(e_eip712_filtering_mode mode) +{ + ui_ctx->filtering_mode = mode; +} + +e_eip712_filtering_mode ui_712_get_filtering_mode(void) +{ + return ui_ctx->filtering_mode; +} + void ui_712_field_flags_reset(void) { ui_ctx->field_flags = 0; diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 966d81b..277932d 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -30,24 +30,23 @@ typedef struct 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); -void ui_712_new_root_struct(const void *const struct_ptr); +void ui_712_new_struct(const void *const struct_ptr); void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); void ui_712_set_title(const char *const str, uint8_t length); void ui_712_set_value(const char *const str, uint8_t length); -#ifdef HAVE_EIP712_HALF_BLIND 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); +void ui_712_finalize_field(void); +void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); +e_eip712_filtering_mode ui_712_get_filtering_mode(void); #endif // HAVE_EIP712_FULL_SUPPORT From b1f2e933c012c53e6f990076bc8fbe0ae81ea3d1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 23 Jun 2022 10:03:16 +0200 Subject: [PATCH 091/184] Added an EIP712 test key --- Makefile | 2 +- src_features/signMessageEIP712/entrypoint.c | 23 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 872bc15..536c003 100644 --- a/Makefile +++ b/Makefile @@ -139,7 +139,7 @@ endif # EIP-712 ifneq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += HAVE_EIP712_FULL_SUPPORT +DEFINES += HAVE_EIP712_FULL_SUPPORT HAVE_EIP712_TESTING_KEY endif # Enabling debug PRINTF diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 754484c..3c43156 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -18,6 +18,15 @@ #include "common_712.h" #include "path.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 // lib functions const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) @@ -509,8 +518,13 @@ static bool verify_contract_name_signature(uint8_t dname_length, sizeof(hash)); 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, @@ -522,7 +536,7 @@ static bool verify_contract_name_signature(uint8_t dname_length, { #ifndef HAVE_BYPASS_SIGNATURES PRINTF("Invalid EIP-712 contract filtering signature\n"); - //return false; // TODO: Uncomment + return false; #endif } return true; @@ -599,8 +613,13 @@ static bool verify_field_name_signature(uint8_t dname_length, 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, @@ -612,7 +631,7 @@ static bool verify_field_name_signature(uint8_t dname_length, { #ifndef HAVE_BYPASS_SIGNATURES PRINTF("Invalid EIP-712 field filtering signature\n"); - //return false; // TODO: Uncomment + return false; #endif } return true; From 8dcb4bc74f541ee5e952a4a25c2022bf342631f5 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 21 Jun 2022 11:44:15 +0200 Subject: [PATCH 092/184] New EIP-712 verbose in-app setting + small refactoring & naming unification --- src/shared_context.h | 3 ++ src/ui_flow.c | 109 ++++++++++++++++++++++++++++++------------- 2 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/shared_context.h b/src/shared_context.h index 4dc68f7..ef73920 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -27,6 +27,9 @@ typedef struct internalStorage_t { unsigned char dataAllowed; unsigned char contractDetails; unsigned char displayNonce; +#ifdef HAVE_EIP712_FULL_SUPPORT + bool verbose_eip712; +#endif // HAVE_EIP712_FULL_SUPPORT uint8_t initialized; } internalStorage_t; diff --git a/src/ui_flow.c b/src/ui_flow.c index d6c0a16..0e441c4 100644 --- a/src/ui_flow.c +++ b/src/ui_flow.c @@ -5,6 +5,9 @@ void display_settings(const ux_flow_step_t* const start_step); void switch_settings_blind_signing(void); void switch_settings_display_data(void); void switch_settings_display_nonce(void); +#ifdef HAVE_EIP712_FULL_SUPPORT +void switch_settings_verbose_eip712(void); +#endif // HAVE_EIP712_FULL_SUPPORT ////////////////////////////////////////////////////////////////////// // clang-format off @@ -52,7 +55,7 @@ UX_FLOW(ux_idle_flow, // clang-format off UX_STEP_CB( - ux_settings_flow_1_step, + ux_settings_flow_blind_signing_step, bnnn_paging, switch_settings_blind_signing(), { @@ -61,62 +64,76 @@ UX_STEP_CB( }); UX_STEP_CB( - ux_settings_flow_2_step, + ux_settings_flow_display_data_step, bnnn_paging, switch_settings_display_data(), { .title = "Debug data", - .text = strings.common.fullAddress + 12 + .text = strings.common.fullAddress + 9 }); UX_STEP_CB( - ux_settings_flow_3_step, + ux_settings_flow_display_nonce_step, bnnn_paging, switch_settings_display_nonce(), { .title = "Account nonce", - .text = strings.common.fullAddress + 26 + .text = strings.common.fullAddress + 18 }); #else UX_STEP_CB( - ux_settings_flow_1_step, + ux_settings_flow_blind_signing_step, bnnn, switch_settings_blind_signing(), { "Blind signing", - "Enable transaction", + "Transaction", "blind signing", strings.common.fullAddress, }); UX_STEP_CB( - ux_settings_flow_2_step, + ux_settings_flow_display_data_step, bnnn, switch_settings_display_data(), { "Debug data", - "Display contract data", + "Show contract data", "details", - strings.common.fullAddress + 12 + strings.common.fullAddress + 9 }); UX_STEP_CB( - ux_settings_flow_3_step, + ux_settings_flow_display_nonce_step, bnnn, switch_settings_display_nonce(), { "Nonce", - "Display account nonce", + "Show account nonce", "in transactions", - strings.common.fullAddress + 26 + strings.common.fullAddress + 18 }); #endif +#ifdef HAVE_EIP712_FULL_SUPPORT UX_STEP_CB( - ux_settings_flow_4_step, + ux_settings_flow_verbose_eip712_step, + bnnn, + switch_settings_verbose_eip712(), + { + "Verbose EIP-712", + "Ignore filtering &", + "display raw content", + strings.common.fullAddress + 27 + }); +#endif // HAVE_EIP712_FULL_SUPPORT + + +UX_STEP_CB( + ux_settings_flow_back_step, pb, ui_idle(), { @@ -126,40 +143,68 @@ UX_STEP_CB( // clang-format on UX_FLOW(ux_settings_flow, - &ux_settings_flow_1_step, - &ux_settings_flow_2_step, - &ux_settings_flow_3_step, - &ux_settings_flow_4_step); + &ux_settings_flow_blind_signing_step, + &ux_settings_flow_display_data_step, + &ux_settings_flow_display_nonce_step, +#ifdef HAVE_EIP712_FULL_SUPPORT + &ux_settings_flow_verbose_eip712_step, +#endif // HAVE_EIP712_FULL_SUPPORT + &ux_settings_flow_back_step); void display_settings(const ux_flow_step_t* const start_step) { - strlcpy(strings.common.fullAddress, (N_storage.dataAllowed ? "Enabled" : "NOT Enabled"), 12); - strlcpy(strings.common.fullAddress + 12, - (N_storage.contractDetails ? "Displayed" : "NOT Displayed"), - 26 - 12); - strlcpy(strings.common.fullAddress + 26, - (N_storage.displayNonce ? "Displayed" : "NOT Displayed"), - sizeof(strings.common.fullAddress) - 26); + const char *const values[] = { + "Enabled", + "Disabled" + }; + bool settings[] = { + N_storage.dataAllowed, + N_storage.contractDetails, + N_storage.displayNonce, +#ifdef HAVE_EIP712_FULL_SUPPORT + N_storage.verbose_eip712 +#endif // HAVE_EIP712_FULL_SUPPORT + }; + uint8_t offset = 0; + uint8_t increment = MAX(strlen(values[0]), strlen(values[1])) + 1; + + for (unsigned int i = 0; i < (sizeof(settings) / sizeof(settings[0])); ++i) + { + strlcpy(strings.common.fullAddress + offset, + (settings[i] ? values[0] : values[1]), + sizeof(strings.common.fullAddress) - offset); + offset += increment; + } + ux_flow_init(0, ux_settings_flow, start_step); } -void switch_settings_blind_signing() { +void switch_settings_blind_signing(void) { uint8_t value = (N_storage.dataAllowed ? 0 : 1); nvm_write((void*) &N_storage.dataAllowed, (void*) &value, sizeof(uint8_t)); - display_settings(&ux_settings_flow_1_step); + display_settings(&ux_settings_flow_blind_signing_step); } -void switch_settings_display_data() { +void switch_settings_display_data(void) { uint8_t value = (N_storage.contractDetails ? 0 : 1); nvm_write((void*) &N_storage.contractDetails, (void*) &value, sizeof(uint8_t)); - display_settings(&ux_settings_flow_2_step); + display_settings(&ux_settings_flow_display_data_step); } -void switch_settings_display_nonce() { +void switch_settings_display_nonce(void) { uint8_t value = (N_storage.displayNonce ? 0 : 1); nvm_write((void*) &N_storage.displayNonce, (void*) &value, sizeof(uint8_t)); - display_settings(&ux_settings_flow_3_step); + display_settings(&ux_settings_flow_display_nonce_step); } +#ifdef HAVE_EIP712_FULL_SUPPORT +void switch_settings_verbose_eip712(void) +{ + bool value = !N_storage.verbose_eip712; + nvm_write((void*) &N_storage.verbose_eip712, (void*) &value, sizeof(value)); + display_settings(&ux_settings_flow_verbose_eip712_step); +} +#endif // HAVE_EIP712_FULL_SUPPORT + ////////////////////////////////////////////////////////////////////// // clang-format off #if defined(TARGET_NANOS) @@ -184,4 +229,4 @@ UX_STEP_CB( #endif // clang-format on -UX_FLOW(ux_warning_contract_data_flow, &ux_warning_contract_data_step); \ No newline at end of file +UX_FLOW(ux_warning_contract_data_flow, &ux_warning_contract_data_step); From 447e7b92771aedb354b411c576271cd565bcaa17 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 23 Jun 2022 14:39:06 +0200 Subject: [PATCH 093/184] Verbose EIP-712 setting now ignores filtering APDUs --- src_features/signMessageEIP712/entrypoint.c | 53 +++++++++++---------- src_features/signMessageEIP712/ui_logic.c | 4 +- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 3c43156..796234b 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -427,6 +427,11 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) if (path_set_root((char*)&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC])) { + if (N_storage.verbose_eip712) + { + ui_712_new_struct(path_get_root()); + reply_apdu = false; + } ui_712_field_flags_reset(); } else @@ -657,9 +662,12 @@ bool provide_contract_name(const uint8_t *const payload, uint8_t 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(); + if (!N_storage.verbose_eip712) + { + ui_712_set_title("Contract", 8); + ui_712_set_value(dname, dname_len); + ui_712_redraw_generic_step(); + } } } } @@ -705,21 +713,22 @@ bool provide_field_name(const uint8_t *const payload, uint8_t length) bool handle_eip712_filtering(const uint8_t *const apdu_buf) { bool ret = true; + bool reply_apdu = true; switch (apdu_buf[OFFSET_P1]) { case P1_ACTIVATE: - ui_712_set_filtering_mode(EIP712_FILTERING_FULL); + if (!N_storage.verbose_eip712) + { + ui_712_set_filtering_mode(EIP712_FILTERING_FULL); + } break; case P1_CONTRACT_NAME: if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); - } - else - { - ret = false; + reply_apdu = false; } break; case P1_FIELD_NAME: @@ -728,10 +737,6 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) 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", @@ -739,18 +744,18 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) 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)) + if (reply_apdu) { + 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; + } io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } return ret; @@ -764,7 +769,7 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) { return false; } - if (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC) + if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) { ui_712_message_hash(); } diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 1edda19..3316d7a 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -32,7 +32,7 @@ static bool ui_712_field_shown(void) if (ui_ctx->filtering_mode == EIP712_FILTERING_BASIC) { - if (path_get_root_type() == ROOT_DOMAIN) + if (N_storage.verbose_eip712 || (path_get_root_type() == ROOT_DOMAIN)) { ret = true; } @@ -335,7 +335,7 @@ void ui_712_end_sign(void) } ui_ctx->end_reached = true; - if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) + if (N_storage.verbose_eip712 || (ui_ctx->filtering_mode == EIP712_FILTERING_FULL)) { ui_712_next_field(); } From ca37612f614a2016b68e0d361cdce06bf6648cd6 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 24 Jun 2022 11:25:13 +0200 Subject: [PATCH 094/184] Fix io_exchange exception Was trying to reply to EIP712 ARRAY size APDU twice --- src_features/signMessageEIP712/entrypoint.c | 8 +++++--- src_features/signMessageEIP712/path.c | 3 --- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 796234b..640152b 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -726,9 +726,11 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) case P1_CONTRACT_NAME: if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { - ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]); - reply_apdu = false; + if ((ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC]))) + { + reply_apdu = false; + } } break; case P1_FIELD_NAME: diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 609d6a7..485bb97 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -496,9 +496,6 @@ bool path_new_array_depth(uint8_t size) cx_keccak_init(hash_ctx, 256); // init hash } - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); return true; } From 9884fadefcb9400a3fcd8595395c35d6258c11f7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 24 Jun 2022 18:18:09 +0200 Subject: [PATCH 095/184] Updated EIP-712 filtering APDU doc to include the chain ID in the sig --- doc/ethapp.adoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index fd1a95f..6db1f4a 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -804,10 +804,11 @@ If activated, fields will be by default hidden unless they receive a field name Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*. Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation. +The chain ID used for the signature must be 8 bytes wide. The signature is computed on : -contract address || display name length || display name +chain ID (BE) || contract address || display name length || display name ##### Field name substitution @@ -816,7 +817,7 @@ Name substitution commands should come before the corresponding *SEND STRUCT IMP The signature is computed on : -contract address || json key length || json key || display name length || display name +chain ID (BE) || contract address || json key length || json key || display name length || display name #### Coding From d8321d422d10f44b65ba839e954fddf8de4eb311 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 24 Jun 2022 18:12:07 +0200 Subject: [PATCH 096/184] Renamed EIP-712 UI function --- src_features/signMessageEIP712/entrypoint.c | 2 +- src_features/signMessageEIP712/ui_logic.c | 2 +- src_features/signMessageEIP712/ui_logic.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 640152b..ee536b4 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -429,7 +429,7 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { if (N_storage.verbose_eip712) { - ui_712_new_struct(path_get_root()); + ui_712_review_struct(path_get_root()); reply_apdu = false; } ui_712_field_flags_reset(); diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 3316d7a..0aa74c0 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -159,7 +159,7 @@ void ui_712_next_field(void) * * @param[in] struct_ptr pointer to the structure */ -void ui_712_new_struct(const void *const struct_ptr) +void ui_712_review_struct(const void *const struct_ptr) { const char *struct_name; uint8_t struct_name_length; diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 277932d..27a840a 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -33,7 +33,7 @@ typedef struct bool ui_712_init(void); void ui_712_deinit(void); void ui_712_next_field(void); -void ui_712_new_struct(const void *const struct_ptr); +void ui_712_review_struct(const void *const struct_ptr); void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); From 1a4149e836bfb4ed5d69a6148e02a6ae07bbddf3 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 27 Jun 2022 09:53:28 +0200 Subject: [PATCH 097/184] EIP-712 hidden fields now share the shown fields logic --- src_features/signMessageEIP712/ui_logic.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 0aa74c0..8a5b772 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -75,9 +75,7 @@ void ui_712_finalize_field(void) { if (!ui_712_field_shown()) { - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + ui_712_next_field(); } ui_712_field_flags_reset(); } From 032d74e30168cb4e5fe96c0b1224b6ec022b9aa6 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 27 Jun 2022 17:01:07 +0200 Subject: [PATCH 098/184] Now checks the EIP712Domain chain ID --- src_features/signMessageEIP712/field_hash.c | 31 ++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 01abe08..bcd8985 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -11,6 +11,7 @@ #include "ui_logic.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE #include "context.h" // contract_addr +#include "utils.h" // u64_from_BE static s_field_hashing *fh = NULL; @@ -160,16 +161,32 @@ bool field_hash(const uint8_t *data, // deallocate it mem_dealloc(len); - // copy contract address into context - if ((path_get_root_type() == ROOT_DOMAIN) - && (strncmp(key, "verifyingContract", keylen) == 0)) + if (path_get_root_type() == ROOT_DOMAIN) { - if (data_length != sizeof(eip712_context->contract_addr)) + // copy contract address into context + if (strncmp(key, "verifyingContract", keylen) == 0) { - PRINTF("Unexpected verifyingContract length!\n"); - return false; + if (data_length != sizeof(eip712_context->contract_addr)) + { + PRINTF("Unexpected verifyingContract length!\n"); + return false; + } + memcpy(eip712_context->contract_addr, data, data_length); + } + else if (strncmp(key, "chainId", keylen) == 0) + { + uint64_t chainId = u64_from_BE(data, data_length); + + if (chainId != chainConfig->chainId) + { + PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", + sizeof(chainConfig->chainId), + &chainConfig->chainId, + sizeof(chainId), + &chainId); + return false; + } } - memcpy(eip712_context->contract_addr, data, data_length); } path_advance(); fh->state = FHS_IDLE; From 559e729188c294a0cb27f77405ae96dc565a1bca Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 24 Jun 2022 18:12:55 +0200 Subject: [PATCH 099/184] Better UI for verbose EIP-712, now shows the review struct screen even on inner structures --- src_features/signMessageEIP712/field_hash.c | 1 - src_features/signMessageEIP712/path.c | 18 +++++++++++ src_features/signMessageEIP712/path.h | 1 + src_features/signMessageEIP712/ui_logic.c | 36 +++++++++++++++------ src_features/signMessageEIP712/ui_logic.h | 2 ++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index bcd8985..20398d4 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -15,7 +15,6 @@ static s_field_hashing *fh = NULL; - bool field_hash_init(void) { if (fh == NULL) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 485bb97..2639589 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -10,6 +10,7 @@ #include "shared_context.h" #include "ethUtils.h" #include "mem_utils.h" +#include "ui_logic.h" static s_path *path_struct = NULL; @@ -78,6 +79,22 @@ static inline const void *get_field_from_path(uint8_t *const fields_count) return get_nth_field_from_path(fields_count, path_struct->depth_count); } +const void *path_get_nth_struct_to_last(uint8_t n) +{ + const char *typename; + uint8_t typename_len; + const void *field_ptr; + const void *struct_ptr = NULL; + + field_ptr = get_nth_field_from_path(NULL, path_struct->depth_count - n); + if (field_ptr != NULL) + { + typename = get_struct_field_typename(field_ptr, &typename_len); + struct_ptr = get_structn(eip712_context->structs_array, typename, typename_len); + } + return struct_ptr; +} + /** * Get the element the path is pointing to. (public facing) * @@ -306,6 +323,7 @@ static bool path_update(void) // deallocate it mem_dealloc(KECCAK256_HASH_BYTESIZE); + ui_712_queue_struct_to_review(); path_depth_list_push(); } return true; diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index ec8413f..ef43a2c 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -39,6 +39,7 @@ void path_deinit(void); bool path_new_array_depth(uint8_t size); e_root_type path_get_root_type(void); const void *path_get_root(void); +const void *path_get_nth_struct_to_last(uint8_t n); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 8a5b772..a260e31 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -130,24 +130,32 @@ void ui_712_next_field(void) { return; } - if (!ui_ctx->end_reached) + if (ui_ctx->structs_to_review > 0) { - // reply to previous APDU - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + ui_712_review_struct(path_get_nth_struct_to_last(ui_ctx->structs_to_review)); + ui_ctx->structs_to_review -= 1; } else { - if (ui_ctx->pos == UI_712_POS_REVIEW) + if (!ui_ctx->end_reached) { - ux_flow_next(); - ui_ctx->pos = UI_712_POS_END; + // reply to previous APDU + G_io_apdu_buffer[0] = 0x90; + G_io_apdu_buffer[1] = 0x00; + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); } else { - ux_flow_prev(); - ui_ctx->pos = UI_712_POS_REVIEW; + if (ui_ctx->pos == UI_712_POS_REVIEW) + { + ux_flow_next(); + ui_ctx->pos = UI_712_POS_END; + } + else + { + ux_flow_prev(); + ui_ctx->pos = UI_712_POS_REVIEW; + } } } } @@ -415,4 +423,12 @@ void ui_712_field_flags_reset(void) ui_ctx->field_flags = 0; } +void ui_712_queue_struct_to_review(void) +{ + if (N_storage.verbose_eip712) + { + ui_ctx->structs_to_review += 1; + } +} + #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 27a840a..81a2779 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -28,6 +28,7 @@ typedef struct e_ui_position pos; uint8_t filtering_mode; uint8_t field_flags; + uint8_t structs_to_review; } t_ui_context; bool ui_712_init(void); @@ -47,6 +48,7 @@ void ui_712_field_flags_reset(void); void ui_712_finalize_field(void); void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); e_eip712_filtering_mode ui_712_get_filtering_mode(void); +void ui_712_queue_struct_to_review(void); #endif // HAVE_EIP712_FULL_SUPPORT From 47e47dd81873b23e245c828f8bf60634716c35f2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 27 Jun 2022 18:51:57 +0200 Subject: [PATCH 100/184] EIP712 signatures now computed on chain ID --- src_features/signMessageEIP712/entrypoint.c | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index ee536b4..ada54a4 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -488,8 +488,19 @@ static bool verify_contract_name_signature(uint8_t dname_length, 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); + cx_hash((cx_hash_t*)&hash_ctx, + 0, + (uint8_t*)&chain_id, + sizeof(chain_id), + NULL, + 0); + // Contract address cx_hash((cx_hash_t*)&hash_ctx, 0, @@ -558,8 +569,19 @@ static bool verify_field_name_signature(uint8_t dname_length, 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); + cx_hash((cx_hash_t*)&hash_ctx, + 0, + (uint8_t*)&chain_id, + sizeof(chain_id), + NULL, + 0); + // Contract address cx_hash((cx_hash_t*)&hash_ctx, 0, From 1b0003885975bdee7d87fe3ef772b815edab33ab Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 28 Jun 2022 16:49:29 +0200 Subject: [PATCH 101/184] Fixed compilation warning --- src/tokens.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tokens.c b/src/tokens.c index b26771c..44e3cb3 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -19,7 +19,7 @@ #include "tokens.h" -const tokenDefinition_t const TOKENS_EXTRA[NUM_TOKENS_EXTRA] = { +const tokenDefinition_t TOKENS_EXTRA[NUM_TOKENS_EXTRA] = { // Ropsten DeversiFi tokens {{0x4c, 0x5f, 0x66, 0x59, 0x61, 0x97, 0xa8, 0x6f, 0xb3, 0x0a, From 5450f0c46f25a73310180406760e240a35b2e7c8 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 29 Jun 2022 14:08:08 +0200 Subject: [PATCH 102/184] Refactored some type EIP712 formatting and hashing code to re-use it --- .../format_hash_field_type.c | 116 ++++++++++++++++++ .../format_hash_field_type.h | 12 ++ src_features/signMessageEIP712/hash_bytes.c | 33 +++++ src_features/signMessageEIP712/hash_bytes.h | 13 ++ src_features/signMessageEIP712/mem_utils.c | 3 +- src_features/signMessageEIP712/type_hash.c | 114 ++--------------- 6 files changed, 188 insertions(+), 103 deletions(-) create mode 100644 src_features/signMessageEIP712/format_hash_field_type.c create mode 100644 src_features/signMessageEIP712/format_hash_field_type.h create mode 100644 src_features/signMessageEIP712/hash_bytes.c create mode 100644 src_features/signMessageEIP712/hash_bytes.h diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c new file mode 100644 index 0000000..399ef36 --- /dev/null +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -0,0 +1,116 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "format_hash_field_type.h" +#include "mem.h" +#include "mem_utils.h" +#include "eip712.h" +#include "hash_bytes.h" + +/** + * Format & hash a struct field typesize + * + * @param[in] field_ptr pointer to the struct field + * @param[in] hash_ctx pointer to the hashing context + * @return whether the formatting & hashing were successful or not + */ +static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t *hash_ctx) +{ + uint16_t field_size; + char *uint_str_ptr; + uint8_t uint_str_len; + + field_size = get_struct_field_typesize(field_ptr); + switch (struct_field_type(field_ptr)) + { + case TYPE_SOL_INT: + case TYPE_SOL_UINT: + field_size *= 8; // bytes -> bits + break; + case TYPE_SOL_BYTES_FIX: + break; + default: + // should not be in here :^) + return false; + } + uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len); + hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); + mem_dealloc(uint_str_len); + return true; +} + +/** + * Format & hash a struct field array levels + * + * @param[in] field_ptr pointer to the struct field + * @param[in] hash_ctx pointer to the hashing context + * @return whether the formatting & hashing were successful or not + */ +static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_hash_t *hash_ctx) +{ + uint8_t array_size; + char *uint_str_ptr; + uint8_t uint_str_len; + const void *lvl_ptr; + uint8_t lvls_count; + + lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); + while (lvls_count-- > 0) + { + hash_byte('[', hash_ctx); + + switch (struct_field_array_depth(lvl_ptr, &array_size)) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len); + hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); + mem_dealloc(uint_str_len); + break; + default: + // should not be in here :^) + return false; + } + hash_byte(']', hash_ctx); + lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); + } + return true; +} + +/** + * Format & hash a struct field type + * + * @param[in] field_ptr pointer to the struct field + * @param[in] hash_ctx pointer to the hashing context + * @return whether the formatting & hashing were successful or not + */ +bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx) +{ + const char *name; + uint8_t length; + + // field type name + name = get_struct_field_typename(field_ptr, &length); + hash_nbytes((uint8_t*)name, length, hash_ctx); + + // field type size + if (struct_field_has_typesize(field_ptr)) + { + if (!format_hash_field_type_size(field_ptr, hash_ctx)) + { + return false; + } + } + + // field type array levels + if (struct_field_is_array(field_ptr)) + { + if (!format_hash_field_type_array_levels(field_ptr, hash_ctx)) + { + return false; + } + } + return true; +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/format_hash_field_type.h b/src_features/signMessageEIP712/format_hash_field_type.h new file mode 100644 index 0000000..6baea31 --- /dev/null +++ b/src_features/signMessageEIP712/format_hash_field_type.h @@ -0,0 +1,12 @@ +#ifndef FORMAT_HASH_FIELD_TYPE_H_ +#define FORMAT_HASH_FIELD_TYPE_H_ + +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "cx.h" + +bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx); + +#endif // HAVE_EIP712_FULL_SUPPORT + +#endif // FORMAT_HASH_FIELD_TYPE_H_ diff --git a/src_features/signMessageEIP712/hash_bytes.c b/src_features/signMessageEIP712/hash_bytes.c new file mode 100644 index 0000000..0a5ee6a --- /dev/null +++ b/src_features/signMessageEIP712/hash_bytes.c @@ -0,0 +1,33 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "hash_bytes.h" + +/** + * Continue given progressive hash on given bytes + * + * @param[in] bytes_ptr pointer to bytes + * @param[in] n number of bytes to hash + * @param[in] hash_ctx pointer to the hashing context + */ +void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *hash_ctx) +{ + cx_hash(hash_ctx, + 0, + bytes_ptr, + n, + NULL, + 0); +} + +/** + * Continue given progressive hash on given byte + * + * @param[in] byte byte to hash + * @param[in] hash_ctx pointer to the hashing context + */ +void hash_byte(uint8_t byte, cx_hash_t *hash_ctx) +{ + hash_nbytes(&byte, 1, hash_ctx); +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/hash_bytes.h b/src_features/signMessageEIP712/hash_bytes.h new file mode 100644 index 0000000..32e7d11 --- /dev/null +++ b/src_features/signMessageEIP712/hash_bytes.h @@ -0,0 +1,13 @@ +#ifndef HASH_BYTES_H_ +#define HASH_BYTES_H_ + +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include "cx.h" + +void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *hash_ctx); +void hash_byte(uint8_t byte, cx_hash_t *hash_ctx); + +#endif // HAVE_EIP712_FULL_SUPPORT + +#endif // HASH_BYTES_H_ diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_features/signMessageEIP712/mem_utils.c index 09d98cd..e45280f 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_features/signMessageEIP712/mem_utils.c @@ -46,8 +46,7 @@ char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const length) // +1 for the null character if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1)))) { - // should be using %u, but not supported by toolchain - snprintf(mem_ptr, (size + 1), "%d", value); + snprintf(mem_ptr, (size + 1), "%u", value); mem_dealloc(sizeof(char)); // to skip the null character if (length != NULL) { diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index f9b28ca..b234c66 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -9,115 +9,29 @@ #include "type_hash.h" #include "shared_context.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE - -static inline void hash_nbytes(const uint8_t *b, uint8_t n) -{ -#ifdef DEBUG - for (int i = 0; i < n; ++i) - { - PRINTF("%c", b[i]); - } -#endif - cx_hash((cx_hash_t*)&global_sha3, - 0, - b, - n, - NULL, - 0); -} - -static inline void hash_byte(uint8_t b) -{ - hash_nbytes(&b, 1); -} - -/** - * - * @param[in] lvl_ptr pointer to the first array level of a struct field - * @param[in] lvls_count the number of array levels the struct field contains - * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) - */ -static bool format_field_type_array_levels_string(const void *lvl_ptr, uint8_t lvls_count) -{ - uint8_t array_size; - char *uint_str_ptr; - uint8_t uint_str_len; - - while (lvls_count-- > 0) - { - hash_byte('['); - - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len); - hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len); - mem_dealloc(uint_str_len); - break; - default: - // should not be in here :^) - break; - } - hash_byte(']'); - lvl_ptr = get_next_struct_field_array_lvl(lvl_ptr); - } - return true; -} +#include "format_hash_field_type.h" +#include "hash_bytes.h" /** * * @param[in] field_ptr pointer to the struct field * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) */ -static bool encode_and_hash_field(const void *field_ptr) +static bool encode_and_hash_field(const void *const field_ptr) { const char *name; uint8_t length; - uint16_t field_size; - uint8_t lvls_count; - const uint8_t *lvl_ptr; - char *uint_str_ptr; - uint8_t uint_str_len; - // field type name - name = get_struct_field_typename(field_ptr, &length); - hash_nbytes((uint8_t*)name, length); - - // field type size - if (struct_field_has_typesize(field_ptr)) + if (!format_hash_field_type(field_ptr, (cx_hash_t*)&global_sha3)) { - field_size = get_struct_field_typesize(field_ptr); - switch (struct_field_type(field_ptr)) - { - case TYPE_SOL_INT: - case TYPE_SOL_UINT: - field_size *= 8; // bytes -> bits - break; - case TYPE_SOL_BYTES_FIX: - break; - default: - // should not be in here :^) - break; - } - uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len); - hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len); - mem_dealloc(uint_str_len); - } - - // field type array levels - if (struct_field_is_array(field_ptr)) - { - lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - format_field_type_array_levels_string(lvl_ptr, lvls_count); + return false; } // space between field type name and field name - hash_byte(' '); + hash_byte(' ', (cx_hash_t*)&global_sha3); // field name name = get_struct_field_keyname(field_ptr, &length); - hash_nbytes((uint8_t*)name, length); + hash_nbytes((uint8_t*)name, length, (cx_hash_t*)&global_sha3); return true; } @@ -127,7 +41,7 @@ static bool encode_and_hash_field(const void *field_ptr) * @param[in] str_length length of the formatted string in memory * @return pointer of the string in memory, \ref NULL in case of an error */ -static bool encode_and_hash_type(const uint8_t *const struct_ptr) +static bool encode_and_hash_type(const void *const struct_ptr) { const char *struct_name; uint8_t struct_name_length; @@ -136,10 +50,10 @@ static bool encode_and_hash_type(const uint8_t *const struct_ptr) // struct name struct_name = get_struct_name(struct_ptr, &struct_name_length); - hash_nbytes((uint8_t*)struct_name, struct_name_length); + hash_nbytes((uint8_t*)struct_name, struct_name_length, (cx_hash_t*)&global_sha3); // opening struct parenthese - hash_byte('('); + hash_byte('(', (cx_hash_t*)&global_sha3); field_ptr = get_struct_fields_array(struct_ptr, &fields_count); for (uint8_t idx = 0; idx < fields_count; ++idx) @@ -147,7 +61,7 @@ static bool encode_and_hash_type(const uint8_t *const struct_ptr) // comma separating struct fields if (idx > 0) { - hash_byte(','); + hash_byte(',', (cx_hash_t*)&global_sha3); } if (encode_and_hash_field(field_ptr) == false) @@ -158,7 +72,7 @@ static bool encode_and_hash_type(const uint8_t *const struct_ptr) field_ptr = get_next_struct_field(field_ptr); } // closing struct parenthese - hash_byte(')'); + hash_byte(')', (cx_hash_t*)&global_sha3); return true; } @@ -302,9 +216,7 @@ const uint8_t *type_hash(const void *const structs_array, deps += 1; } mem_dealloc(mem_alloc(0) - mem_loc_bak); -#ifdef DEBUG - PRINTF("\n"); -#endif + // End progressive hashing if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { From 188a75ffff05c109c26c9963f3b88c6f2facb061 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 29 Jun 2022 14:51:37 +0200 Subject: [PATCH 103/184] App now computes EIP-712 schema identifier --- src_features/signMessageEIP712/context.h | 1 + src_features/signMessageEIP712/entrypoint.c | 61 +++++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 455e270..3386a6f 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -12,6 +12,7 @@ typedef struct uint8_t *structs_array; uint8_t *current_struct_fields_array; uint8_t contract_addr[ADDRESS_LENGTH]; + uint8_t schema_hash[224 / 8]; } s_eip712_context; extern s_eip712_context *eip712_context; diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index ada54a4..2bf6f18 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -732,6 +732,66 @@ bool provide_field_name(const uint8_t *const payload, uint8_t length) 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; @@ -743,6 +803,7 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) if (!N_storage.verbose_eip712) { ui_712_set_filtering_mode(EIP712_FILTERING_FULL); + ret = compute_schema_hash(); } break; case P1_CONTRACT_NAME: From ee660317aab000bf945b76084ec177a1898e6d73 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 29 Jun 2022 15:08:43 +0200 Subject: [PATCH 104/184] EIP712 signatures now computed on schema hash --- doc/ethapp.adoc | 10 +++++++--- src_features/signMessageEIP712/entrypoint.c | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 6db1f4a..fe5ce5e 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -792,6 +792,11 @@ This command provides a trusted way of deciding what information from the JSON d This mode can be overriden by the in-app setting to fully clear-sign EIP-712 messages. +For the signatures : + +* The chain ID used for the signature must be 8 bytes wide. +* The schema hash = sha224sum of the value of _types_ at the root of the JSON data (stripped of all spaces and newlines) + ##### Activation Full filtering is disabled by default and has to be changed with this APDU (default behaviour is basic filtering handled by the app itself). @@ -804,11 +809,10 @@ If activated, fields will be by default hidden unless they receive a field name Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*. Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation. -The chain ID used for the signature must be 8 bytes wide. The signature is computed on : -chain ID (BE) || contract address || display name length || display name +chain ID (BE) || contract address || schema hash || display name length || display name ##### Field name substitution @@ -817,7 +821,7 @@ Name substitution commands should come before the corresponding *SEND STRUCT IMP The signature is computed on : -chain ID (BE) || contract address || json key length || json key || display name length || display name +chain ID (BE) || contract address || schema hash || json key length || json key || display name length || display name #### Coding diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 2bf6f18..a11d055 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -509,6 +509,14 @@ static bool verify_contract_name_signature(uint8_t dname_length, NULL, 0); + // Schema hash + cx_hash((cx_hash_t*)&hash_ctx, + 0, + eip712_context->schema_hash, + sizeof(eip712_context->schema_hash), + NULL, + 0); + // Display name length cx_hash((cx_hash_t*)&hash_ctx, 0, @@ -590,6 +598,14 @@ static bool verify_field_name_signature(uint8_t dname_length, NULL, 0); + // Schema hash + cx_hash((cx_hash_t*)&hash_ctx, + 0, + eip712_context->schema_hash, + sizeof(eip712_context->schema_hash), + NULL, + 0); + if ((field_ptr = path_get_field()) == NULL) { return false; From 339d24445e9f9cb15aef57295e7d9735f9aff382 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 29 Jun 2022 18:39:47 +0200 Subject: [PATCH 105/184] Merge the duplicated EIP-712 filtering function --- src_features/signMessageEIP712/entrypoint.c | 226 ++++++-------------- 1 file changed, 65 insertions(+), 161 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index a11d055..260b476 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -480,96 +480,11 @@ 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; - uint64_t chain_id; - - cx_sha256_init(&hash_ctx); - - // Chain ID - chain_id = __builtin_bswap64(chainConfig->chainId); - cx_hash((cx_hash_t*)&hash_ctx, - 0, - (uint8_t*)&chain_id, - sizeof(chain_id), - NULL, - 0); - - // Contract address - cx_hash((cx_hash_t*)&hash_ctx, - 0, - eip712_context->contract_addr, - sizeof(eip712_context->contract_addr), - NULL, - 0); - - // Schema hash - cx_hash((cx_hash_t*)&hash_ctx, - 0, - eip712_context->schema_hash, - sizeof(eip712_context->schema_hash), - 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, -#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 contract filtering signature\n"); - return false; -#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) +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; @@ -606,30 +521,33 @@ static bool verify_field_name_signature(uint8_t dname_length, NULL, 0); - if ((field_ptr = path_get_field()) == NULL) + if (p1 == P1_FIELD_NAME) { - return false; - } - if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) - { - return false; - } + 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 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); + // 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, @@ -673,14 +591,14 @@ static bool verify_field_name_signature(uint8_t dname_length, sig_length)) { #ifndef HAVE_BYPASS_SIGNATURES - PRINTF("Invalid EIP-712 field filtering signature\n"); + PRINTF("Invalid EIP-712 filtering signature\n"); return false; #endif } return true; } -bool provide_contract_name(const uint8_t *const payload, uint8_t length) +bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1) { bool ret = false; uint8_t dname_len; @@ -688,41 +606,21 @@ bool provide_contract_name(const uint8_t *const payload, uint8_t length) uint8_t sig_len; const uint8_t *sig; - if ((length > 0) && (path_get_root_type() == ROOT_DOMAIN)) + if (p1 == P1_CONTRACT_NAME) { - dname_len = payload[0]; - if ((1 + dname_len) < length) + if (path_get_root_type() != ROOT_DOMAIN) { - 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))) - { - if (!N_storage.verbose_eip712) - { - ui_712_set_title("Contract", 8); - ui_712_set_value(dname, dname_len); - ui_712_redraw_generic_step(); - } - } - } + return false; } } - 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)) + else // P1_FIELD_NAME + { + if (path_get_root_type() != ROOT_MESSAGE) + { + return false; + } + } + if (length > 0) { dname_len = payload[0]; if ((1 + dname_len) < length) @@ -732,15 +630,25 @@ bool provide_field_name(const uint8_t *const payload, uint8_t length) 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 ((ret = verify_filtering_signature(dname_len, dname, sig_len, sig, p1))) { - if (dname_len > 0) // don't substitute for an empty name + if (p1 == P1_CONTRACT_NAME) { - ui_712_set_title(dname, dname_len); - name_provided = true; + 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); } - ret = true; - ui_712_flag_field(true, name_provided); } } } @@ -823,20 +731,16 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) } break; case P1_CONTRACT_NAME: - if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) - { - if ((ret = provide_contract_name(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]))) - { - reply_apdu = false; - } - } - break; case P1_FIELD_NAME: if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { - ret = provide_field_name(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]); + ret = provide_filtering_info(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC], + apdu_buf[OFFSET_P1]); + if ((apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) && ret) + { + reply_apdu = false; + } } break; default: From 4574c223f2d790e57dbd262d8f40c30d790ae88b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 30 Jun 2022 18:43:01 +0200 Subject: [PATCH 106/184] Clean up mem allocation alignment macro usage + small refactoring of the typehash dependencies search --- src_features/signMessageEIP712/context.c | 2 +- src_features/signMessageEIP712/field_hash.c | 2 +- src_features/signMessageEIP712/mem_utils.h | 2 +- src_features/signMessageEIP712/path.c | 4 +-- src_features/signMessageEIP712/type_hash.c | 37 +++++++++++---------- src_features/signMessageEIP712/ui_logic.c | 2 +- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 1e64090..ce56704 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -22,7 +22,7 @@ bool eip712_context_init(void) // init global variables mem_init(); - if ((eip712_context = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*eip712_context), *eip712_context)) == NULL) + if ((eip712_context = MEM_ALLOC_AND_ALIGN_TYPE(*eip712_context)) == NULL) { return false; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 20398d4..3289ce3 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -19,7 +19,7 @@ bool field_hash_init(void) { if (fh == NULL) { - if ((fh = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*fh), *fh)) == NULL) + if ((fh = MEM_ALLOC_AND_ALIGN_TYPE(*fh)) == NULL) { return false; } diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h index f339a92..3064f9b 100644 --- a/src_features/signMessageEIP712/mem_utils.h +++ b/src_features/signMessageEIP712/mem_utils.h @@ -6,7 +6,7 @@ #include #include -#define MEM_ALLOC_AND_ALIGN_TO_TYPE(size, type) (mem_alloc_and_align(size, __alignof__(type))) +#define MEM_ALLOC_AND_ALIGN_TYPE(type) mem_alloc_and_align(sizeof(type), __alignof__(type)) char *mem_alloc_and_copy_char(char c); void *mem_alloc_and_copy(const void *data, size_t size); diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 2639589..4be0cb3 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -359,7 +359,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) // TODO: Move elsewhere cx_sha3_t *hash_ctx; const uint8_t *thash_ptr; - if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*hash_ctx), *hash_ctx)) == NULL) + if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) { return false; } @@ -638,7 +638,7 @@ bool path_init(void) { if (path_struct == NULL) { - path_struct = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*path_struct), *path_struct); + path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct); } return path_struct != NULL; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index b234c66..fe7ca08 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -85,10 +85,10 @@ static bool encode_and_hash_type(const void *const struct_ptr) * @param[in,out] deps pointer to the first dependency pointer */ static void sort_dependencies(uint8_t deps_count, - void **deps) + const void **deps) { bool changed; - void *tmp_ptr; + const void *tmp_ptr; const char *name1, *name2; uint8_t namelen1, namelen2; int str_cmp_result; @@ -120,13 +120,13 @@ static void sort_dependencies(uint8_t deps_count, * * @param[in] structs_array pointer to structs array * @param[out] deps_count count of how many struct dependencie pointers - * @param[in] deps pointer to the first dependency pointer + * @param[in] first_dep pointer to the first dependency pointer * @param[in] struct_ptr pointer to the struct we are getting the dependencies of * @return \ref false in case of a memory allocation error, \ref true otherwise */ -static bool get_struct_dependencies(const void *const structs_array, +static const void **get_struct_dependencies(const void *const structs_array, uint8_t *const deps_count, - void *const *const deps, + const void **first_dep, const void *const struct_ptr) { uint8_t fields_count; @@ -151,7 +151,7 @@ static bool get_struct_dependencies(const void *const structs_array, for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) { // it's a match! - if (*(deps + dep_idx) == arg_struct_ptr) + if (*(first_dep + dep_idx) == arg_struct_ptr) { break; } @@ -159,18 +159,23 @@ static bool get_struct_dependencies(const void *const structs_array, // if it's not present in the array, add it and recurse into it if (dep_idx == *deps_count) { - if ((new_dep = mem_alloc(sizeof(void*))) == NULL) + *deps_count += 1; + if ((new_dep = MEM_ALLOC_AND_ALIGN_TYPE(void*)) == NULL) { - return false; + return NULL; + } + if (*deps_count == 1) + { + first_dep = new_dep; } *new_dep = arg_struct_ptr; - *deps_count += 1; - get_struct_dependencies(structs_array, deps_count, deps, arg_struct_ptr); + // TODO: Move away from recursive calls + get_struct_dependencies(structs_array, deps_count, first_dep, arg_struct_ptr); } } field_ptr = get_next_struct_field(field_ptr); } - return true; + return first_dep; } /** @@ -190,17 +195,13 @@ const uint8_t *type_hash(const void *const structs_array, struct_name, struct_name_length); uint8_t deps_count = 0; - void **deps; + const void **deps; uint8_t *hash_ptr; void *mem_loc_bak = mem_alloc(0); cx_keccak_init(&global_sha3, 256); // init hash - // get list of structs (own + dependencies), properly ordered - if ((deps = MEM_ALLOC_AND_ALIGN_TO_TYPE(0, *deps)) == NULL) - { - return NULL; - } - if (get_struct_dependencies(structs_array, &deps_count, deps, struct_ptr) == false) + deps = get_struct_dependencies(structs_array, &deps_count, NULL, struct_ptr); + if ((deps_count > 0) && (deps == NULL)) { return NULL; } diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index a260e31..eef3aeb 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -352,7 +352,7 @@ void ui_712_end_sign(void) */ bool ui_712_init(void) { - if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TO_TYPE(sizeof(*ui_ctx), *ui_ctx))) + if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*ui_ctx))) { ui_ctx->shown = false; ui_ctx->end_reached = false; From 01179730a13bfd7f70f5d0f65c16f0cdeda24f20 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 1 Jul 2022 18:23:35 +0200 Subject: [PATCH 107/184] Updated the EIP-712 filtering signature specs; Update the verification implementation to now handle field path intead of only the field key name --- doc/ethapp.adoc | 4 +- src_features/signMessageEIP712/entrypoint.c | 92 +++++++++------------ src_features/signMessageEIP712/path.c | 40 ++++++--- src_features/signMessageEIP712/path.h | 4 +- src_features/signMessageEIP712/ui_logic.c | 2 +- 5 files changed, 71 insertions(+), 71 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index fe5ce5e..33d613f 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -812,7 +812,7 @@ Perfect moment to do it is when the domain implementation has been sent, just be The signature is computed on : -chain ID (BE) || contract address || schema hash || display name length || display name +chain ID (BE) || contract address || schema hash || display name ##### Field name substitution @@ -821,7 +821,7 @@ Name substitution commands should come before the corresponding *SEND STRUCT IMP The signature is computed on : -chain ID (BE) || contract address || schema hash || json key length || json key || display name length || display name +chain ID (BE) || contract address || schema hash || field path || display name #### Coding diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 260b476..f568ac9 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -480,6 +480,7 @@ 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, @@ -498,72 +499,55 @@ static bool verify_filtering_signature(uint8_t dname_length, // Chain ID chain_id = __builtin_bswap64(chainConfig->chainId); - cx_hash((cx_hash_t*)&hash_ctx, - 0, - (uint8_t*)&chain_id, - sizeof(chain_id), - NULL, - 0); + hash_nbytes((uint8_t*)&chain_id, sizeof(chain_id), (cx_hash_t*)&hash_ctx); // Contract address - cx_hash((cx_hash_t*)&hash_ctx, - 0, - eip712_context->contract_addr, - sizeof(eip712_context->contract_addr), - NULL, - 0); + hash_nbytes(eip712_context->contract_addr, + sizeof(eip712_context->contract_addr), + (cx_hash_t*)&hash_ctx); // Schema hash - cx_hash((cx_hash_t*)&hash_ctx, - 0, - eip712_context->schema_hash, - sizeof(eip712_context->schema_hash), - NULL, - 0); + hash_nbytes(eip712_context->schema_hash, + sizeof(eip712_context->schema_hash), + (cx_hash_t*)&hash_ctx); if (p1 == P1_FIELD_NAME) { - if ((field_ptr = path_get_field()) == NULL) - { - return false; - } - if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) - { - return false; - } + uint8_t depth_count = path_get_depth_count(); - // Key length - cx_hash((cx_hash_t*)&hash_ctx, - 0, - &key_len, - sizeof(key_len), - NULL, - 0); + 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); - // Key - cx_hash((cx_hash_t*)&hash_ctx, - 0, - (uint8_t*)key, - sizeof(char) * key_len, - NULL, - 0); + // 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 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); + hash_nbytes((uint8_t*)dname, + sizeof(char) * dname_length, + (cx_hash_t*)&hash_ctx); // Finalize hash cx_hash((cx_hash_t*)&hash_ctx, diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 4be0cb3..4ed05bc 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -18,11 +18,11 @@ static s_path *path_struct = NULL; * Get the field pointer to by the first N depths of the path. * * @param[out] fields_count_ptr the number of fields in the last evaluated depth - * @param[in] depth_count the number of depths to evaluate (N) + * @param[in] n the number of depths to evaluate * @return the feld which the first Nth depths points to */ -static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, - uint8_t depth_count) +static const void *get_nth_field(uint8_t *const fields_count_ptr, + uint8_t n) { const void *struct_ptr = path_struct->root_struct; const void *field_ptr = NULL; @@ -34,11 +34,11 @@ static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, { return NULL; } - if (depth_count > path_struct->depth_count) // sanity check + if (n > path_struct->depth_count) // sanity check { return NULL; } - for (uint8_t depth = 0; depth < depth_count; ++depth) + for (uint8_t depth = 0; depth < n; ++depth) { field_ptr = get_struct_fields_array(struct_ptr, &fields_count); @@ -74,19 +74,24 @@ static const void *get_nth_field_from_path(uint8_t *const fields_count_ptr, * @param[out] the number of fields in the depth of the returned field * @return the field which the path points to */ -static inline const void *get_field_from_path(uint8_t *const fields_count) +static inline const void *get_field(uint8_t *const fields_count) { - return get_nth_field_from_path(fields_count, path_struct->depth_count); + return get_nth_field(fields_count, path_struct->depth_count); } -const void *path_get_nth_struct_to_last(uint8_t n) +const void *path_get_nth_field(uint8_t n) +{ + return get_nth_field(NULL, n); +} + +const void *path_get_nth_field_to_last(uint8_t n) { const char *typename; uint8_t typename_len; const void *field_ptr; const void *struct_ptr = NULL; - field_ptr = get_nth_field_from_path(NULL, path_struct->depth_count - n); + field_ptr = get_nth_field(NULL, path_struct->depth_count - n); if (field_ptr != NULL) { typename = get_struct_field_typename(field_ptr, &typename_len); @@ -102,7 +107,7 @@ const void *path_get_nth_struct_to_last(uint8_t n) */ const void *path_get_field(void) { - return get_field_from_path(NULL); + return get_field(NULL); } /** @@ -281,7 +286,7 @@ static bool path_update(void) { return false; } - if ((field_ptr = get_field_from_path(NULL)) == NULL) + if ((field_ptr = get_field(NULL)) == NULL) { return false; } @@ -468,7 +473,7 @@ bool path_new_array_depth(uint8_t size) for (pidx = 0; pidx < path_struct->depth_count; ++pidx) { - if ((field_ptr = get_nth_field_from_path(NULL, pidx + 1)) == NULL) + if ((field_ptr = get_nth_field(NULL, pidx + 1)) == NULL) { return false; } @@ -532,7 +537,7 @@ static bool path_advance_in_struct(void) { return false; } - if ((get_field_from_path(&fields_count)) == NULL) + if ((get_field(&fields_count)) == NULL) { return false; } @@ -629,6 +634,15 @@ const void *path_get_root(void) return path_struct->root_struct; } +uint8_t path_get_depth_count(void) +{ + if (path_struct == NULL) + { + return 0; + } + return path_struct->depth_count; +} + /** * Allocates the path indexes in memory and sets it with a depth of 0. * diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index ef43a2c..cf21c8c 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -39,7 +39,9 @@ void path_deinit(void); bool path_new_array_depth(uint8_t size); e_root_type path_get_root_type(void); const void *path_get_root(void); -const void *path_get_nth_struct_to_last(uint8_t n); +const void *path_get_nth_field(uint8_t n); +const void *path_get_nth_field_to_last(uint8_t n); +uint8_t path_get_depth_count(void); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index eef3aeb..6fb7827 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -132,7 +132,7 @@ void ui_712_next_field(void) } if (ui_ctx->structs_to_review > 0) { - ui_712_review_struct(path_get_nth_struct_to_last(ui_ctx->structs_to_review)); + ui_712_review_struct(path_get_nth_field_to_last(ui_ctx->structs_to_review)); ui_ctx->structs_to_review -= 1; } else From 12b97d91db2040bfa97ad611ed475455b7884fc2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 4 Jul 2022 15:05:07 +0200 Subject: [PATCH 108/184] Added a global apdu response code variable --- src/apdu_constants.h | 11 +++++++++++ src/main.c | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 5bf1753..aeb2986 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -34,6 +34,15 @@ #define COMMON_CLA 0xB0 #define COMMON_INS_GET_WALLET_ID 0x04 +#define APDU_RESPONSE_OK 0x9000 +#define APDU_RESPONSE_INVALID_DATA 0x6a80 +#define APDU_RESPONSE_INSUFFICIENT_MEMORY 0x6a84 +#define APDU_RESPONSE_INVALID_INS 0x6d00 +#define APDU_RESPONSE_INVALID_P1_P2 0x6b00 +#define APDU_RESPONSE_CONDITION_NOT_SATISFIED 0x6985 +#define APDU_RESPONSE_REF_DATA_NOT_USABLE 0x6a88 + + #ifdef HAVE_STARKWARE #define STARKWARE_CLA 0xF0 @@ -180,4 +189,6 @@ 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 +extern uint16_t apdu_response_code; + #endif // _APDU_CONSTANTS_H_ diff --git a/src/main.c b/src/main.c index 61baf06..708f2bf 100644 --- a/src/main.c +++ b/src/main.c @@ -50,6 +50,7 @@ strings_t strings; cx_sha3_t global_sha3; uint8_t appState; +uint16_t apdu_response_code; bool called_from_swap; pluginType_t pluginType; #ifdef HAVE_STARKWARE @@ -70,7 +71,7 @@ const internalStorage_t N_storage_real; chain_config_t *chainConfig; void reset_app_context() { - // PRINTF("!!RESET_APP_CONTEXT\n"); + //PRINTF("!!RESET_APP_CONTEXT\n"); appState = APP_STATE_IDLE; called_from_swap = false; pluginType = OLD_INTERNAL; From 0100eed1fdec957b0b95d99e374f53a884bf3b5a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 19 Jul 2022 11:04:16 +0200 Subject: [PATCH 109/184] EIP712 code now uses the global apdu response code; error handling improvements --- src_features/signMessageEIP712/context.c | 7 + src_features/signMessageEIP712/encode_field.c | 8 + src_features/signMessageEIP712/entrypoint.c | 261 ++++++++++-------- src_features/signMessageEIP712/field_hash.c | 11 +- .../format_hash_field_type.c | 15 +- src_features/signMessageEIP712/path.c | 14 +- .../signMessageEIP712/sol_typenames.c | 5 + src_features/signMessageEIP712/type_hash.c | 8 +- src_features/signMessageEIP712/ui_logic.c | 5 + 9 files changed, 207 insertions(+), 127 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index ce56704..8d30e20 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -10,6 +10,9 @@ #include "path.h" #include "field_hash.h" #include "ui_logic.h" +#include "apdu_constants.h" // APDU response codes +#include "shared_context.h" // reset_app_context +#include "ui_callbacks.h" // ui_idle s_eip712_context *eip712_context = NULL; @@ -24,6 +27,7 @@ bool eip712_context_init(void) if ((eip712_context = MEM_ALLOC_AND_ALIGN_TYPE(*eip712_context)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -50,6 +54,7 @@ bool eip712_context_init(void) // set types pointer if ((eip712_context->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -65,6 +70,8 @@ void eip712_context_deinit(void) ui_712_deinit(); mem_reset(); eip712_context = NULL; + reset_app_context(); + ui_idle(); } #endif diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index ce78a89..a4d0f2b 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -6,6 +6,7 @@ #include "mem.h" #include "eip712.h" #include "shared_context.h" +#include "apdu_constants.h" // APDU response codes typedef enum { @@ -32,6 +33,7 @@ static void *field_encode(const uint8_t *const value, if (length > EIP_712_ENCODED_FIELD_LENGTH) // sanity check { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; } if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) @@ -51,6 +53,10 @@ static void *field_encode(const uint8_t *const value, } memcpy(&padded_value[start_idx], value, length); } + else + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + } return padded_value; } @@ -115,6 +121,7 @@ void *encode_boolean(const bool *const value, uint8_t length) { if (length != 1) // sanity check { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; } return encode_uint((uint8_t*)value, length); @@ -131,6 +138,7 @@ void *encode_address(const uint8_t *const value, uint8_t length) { if (length != ADDRESS_LENGTH) // sanity check { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; } return encode_uint(value, length); diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index f568ac9..2daf964 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -113,7 +113,8 @@ const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) break; default: // should not be in here :^) - break; + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return NULL; } return ptr + 1; } @@ -254,6 +255,7 @@ bool set_struct_name(const uint8_t *const data) // copy length if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *length_ptr = data[OFFSET_LC]; @@ -261,6 +263,7 @@ bool set_struct_name(const uint8_t *const data) // copy name if ((name_ptr = mem_alloc(sizeof(char) * data[OFFSET_LC])) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); @@ -268,6 +271,7 @@ bool set_struct_name(const uint8_t *const data) // initialize number of fields if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *(eip712_context->current_struct_fields_array) = 0; @@ -295,6 +299,7 @@ bool set_struct_field(const uint8_t *const data) // copy TypeDesc if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *type_desc_ptr = data[data_idx++]; @@ -305,6 +310,7 @@ bool set_struct_field(const uint8_t *const data) // copy TypeSize if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *type_size_ptr = data[data_idx++]; @@ -314,6 +320,7 @@ bool set_struct_field(const uint8_t *const data) // copy custom struct name length if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *typename_len_ptr = data[data_idx++]; @@ -321,6 +328,7 @@ bool set_struct_field(const uint8_t *const data) // copy name if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } memmove(typename, &data[data_idx], *typename_len_ptr); @@ -330,6 +338,7 @@ bool set_struct_field(const uint8_t *const data) { if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_levels_count = data[data_idx++]; @@ -337,6 +346,7 @@ bool set_struct_field(const uint8_t *const data) { if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_level = data[data_idx++]; @@ -347,13 +357,15 @@ bool set_struct_field(const uint8_t *const data) case ARRAY_FIXED_SIZE: if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_level_size = data[data_idx++]; break; default: // should not be in here :^) - break; + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; } } } @@ -361,6 +373,7 @@ bool set_struct_field(const uint8_t *const data) // copy length if ((fieldname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *fieldname_len_ptr = data[data_idx++]; @@ -368,12 +381,29 @@ bool set_struct_field(const uint8_t *const data) // copy name if ((fieldname_ptr = mem_alloc(sizeof(char) * *fieldname_len_ptr)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } memmove(fieldname_ptr, &data[data_idx], *fieldname_len_ptr); return true; } +void handle_eip712_return_code(bool ret) +{ + if (ret) + { + apdu_response_code = APDU_RESPONSE_OK; + } + *(uint16_t*)G_io_apdu_buffer = __builtin_bswap16(apdu_response_code); + + // Send back the response, do not restart the event loop + io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + + if (!ret) + { + eip712_context_deinit(); + } +} bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { @@ -381,101 +411,77 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) if (eip712_context == NULL) { - if (!eip712_context_init()) - { - return false; - } - } - switch (apdu_buf[OFFSET_P2]) - { - case P2_NAME: - ret = set_struct_name(apdu_buf); - break; - case P2_FIELD: - ret = set_struct_field(apdu_buf); - break; - default: - PRINTF("Unknown P2 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P2], - apdu_buf[OFFSET_INS]); - ret = false; + ret = eip712_context_init(); } if (ret) { - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; + switch (apdu_buf[OFFSET_P2]) + { + case P2_NAME: + ret = set_struct_name(apdu_buf); + break; + case P2_FIELD: + ret = set_struct_field(apdu_buf); + 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; + } } - else - { - G_io_apdu_buffer[0] = 0x6A; - G_io_apdu_buffer[1] = 0x80; - } - // Send back the response, do not restart the event loop - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + handle_eip712_return_code(ret); return ret; } bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { - bool ret = true; + bool ret = false; bool reply_apdu = true; - switch (apdu_buf[OFFSET_P2]) + if (eip712_context == NULL) { - case P2_NAME: - // set root type - if (path_set_root((char*)&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC])) - { - if (N_storage.verbose_eip712) + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + else + { + switch (apdu_buf[OFFSET_P2]) + { + case P2_NAME: + // set root type + if ((ret = path_set_root((char*)&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC]))) + { + if (N_storage.verbose_eip712) + { + ui_712_review_struct(path_get_root()); + reply_apdu = false; + } + ui_712_field_flags_reset(); + } + break; + case P2_FIELD: + if ((ret = field_hash(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC], + apdu_buf[OFFSET_P1] != P1_COMPLETE))) { - ui_712_review_struct(path_get_root()); reply_apdu = false; } - ui_712_field_flags_reset(); - } - else - { - ret = false; - } - break; - case P2_FIELD: - if (field_hash(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC], - apdu_buf[OFFSET_P1] != P1_COMPLETE)) - { - reply_apdu = false; - } - else - { - ret = false; - } - break; - case P2_ARRAY: - if (!path_new_array_depth(apdu_buf[OFFSET_CDATA])) - { - ret = false; - } - break; - default: - PRINTF("Unknown P2 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P2], - apdu_buf[OFFSET_INS]); - ret = false; + break; + case P2_ARRAY: + ret = path_new_array_depth(apdu_buf[OFFSET_CDATA]); + 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; + } } if (reply_apdu) { - 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; - } - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + handle_eip712_return_code(ret); } return ret; } @@ -594,6 +600,7 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin { if (path_get_root_type() != ROOT_DOMAIN) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } } @@ -601,6 +608,7 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin { if (path_get_root_type() != ROOT_MESSAGE) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } } @@ -705,65 +713,74 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) bool ret = true; bool reply_apdu = true; - switch (apdu_buf[OFFSET_P1]) + if (eip712_context == NULL) { - case P1_ACTIVATE: - if (!N_storage.verbose_eip712) - { - ui_712_set_filtering_mode(EIP712_FILTERING_FULL); - ret = compute_schema_hash(); - } - break; - case P1_CONTRACT_NAME: - case P1_FIELD_NAME: - if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) - { - ret = provide_filtering_info(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC], - apdu_buf[OFFSET_P1]); - if ((apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) && ret) + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + ret = false; + } + else + { + switch (apdu_buf[OFFSET_P1]) + { + case P1_ACTIVATE: + if (!N_storage.verbose_eip712) { - reply_apdu = false; + ui_712_set_filtering_mode(EIP712_FILTERING_FULL); + ret = compute_schema_hash(); } - } - break; - default: - PRINTF("Unknown P1 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P1], - apdu_buf[OFFSET_INS]); - ret = false; + break; + case P1_CONTRACT_NAME: + case P1_FIELD_NAME: + if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) + { + ret = provide_filtering_info(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC], + apdu_buf[OFFSET_P1]); + if ((apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) && ret) + { + reply_apdu = false; + } + } + break; + default: + PRINTF("Unknown P1 0x%x for APDU 0x%x\n", + apdu_buf[OFFSET_P1], + apdu_buf[OFFSET_INS]); + apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; + ret = false; + } } if (reply_apdu) { - 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; - } - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + handle_eip712_return_code(ret); } return ret; } bool handle_eip712_sign(const uint8_t *const apdu_buf) { - if (parseBip32(&apdu_buf[OFFSET_CDATA], - &apdu_buf[OFFSET_LC], - &tmpCtx.messageSigningContext.bip32) == NULL) + bool ret = false; + + if (eip712_context == NULL) { - return false; + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; } - if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) + else if (parseBip32(&apdu_buf[OFFSET_CDATA], + &apdu_buf[OFFSET_LC], + &tmpCtx.messageSigningContext.bip32) != NULL) { - ui_712_message_hash(); + if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) + { + ui_712_message_hash(); + } + ret = true; + ui_712_end_sign(); } - ui_712_end_sign(); - return true; + if (!ret) + { + handle_eip712_return_code(ret); + } + return ret; } diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 3289ce3..c3a4eae 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -12,6 +12,7 @@ #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE #include "context.h" // contract_addr #include "utils.h" // u64_from_BE +#include "apdu_constants.h" // APDU response codes static s_field_hashing *fh = NULL; @@ -21,6 +22,7 @@ bool field_hash_init(void) { if ((fh = MEM_ALLOC_AND_ALIGN_TYPE(*fh)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } fh->state = FHS_IDLE; @@ -54,13 +56,14 @@ bool field_hash(const uint8_t *data, // get field by path if ((field_ptr = path_get_field()) == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } key = get_struct_field_keyname(field_ptr, &keylen); field_type = struct_field_type(field_ptr); if (fh->state == FHS_IDLE) // first packet for this frame { - fh->remaining_size = (data[0] << 8) | data[1]; // network byte order + fh->remaining_size = __builtin_bswap16(*(uint16_t*)&data[0]); // network byte order data += sizeof(uint16_t); data_length -= sizeof(uint16_t); fh->state = FHS_WAITING_FOR_MORE; @@ -85,6 +88,7 @@ bool field_hash(const uint8_t *data, { if (partial) // only makes sense if marked as complete { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } #ifdef DEBUG @@ -118,6 +122,7 @@ bool field_hash(const uint8_t *data, break; case TYPE_CUSTOM: default: + apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Unknown solidity type!\n"); return false; } @@ -132,6 +137,7 @@ bool field_hash(const uint8_t *data, { if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } // copy hash into memory @@ -167,6 +173,7 @@ bool field_hash(const uint8_t *data, { if (data_length != sizeof(eip712_context->contract_addr)) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Unexpected verifyingContract length!\n"); return false; } @@ -178,6 +185,7 @@ bool field_hash(const uint8_t *data, if (chainId != chainConfig->chainId) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", sizeof(chainConfig->chainId), &chainConfig->chainId, @@ -195,6 +203,7 @@ bool field_hash(const uint8_t *data, { if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } G_io_apdu_buffer[0] = 0x90; diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c index 399ef36..b8a3892 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.c +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -5,6 +5,7 @@ #include "mem_utils.h" #include "eip712.h" #include "hash_bytes.h" +#include "apdu_constants.h" // APDU response codes /** * Format & hash a struct field typesize @@ -30,9 +31,14 @@ static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t * break; default: // should not be in here :^) + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len); + if ((uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); mem_dealloc(uint_str_len); return true; @@ -63,12 +69,17 @@ static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_ case ARRAY_DYNAMIC: break; case ARRAY_FIXED_SIZE: - uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len); + if ((uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); mem_dealloc(uint_str_len); break; default: // should not be in here :^) + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } hash_byte(']', hash_ctx); diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 4ed05bc..e630e23 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -11,6 +11,7 @@ #include "ethUtils.h" #include "mem_utils.h" #include "ui_logic.h" +#include "apdu_constants.h" // APDU response codes static s_path *path_struct = NULL; @@ -214,10 +215,12 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) if (path_struct == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } @@ -345,6 +348,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) { if (path_struct == NULL) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } @@ -358,6 +362,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) PRINTF("%c", struct_name[i]); } PRINTF(")!\n"); + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } @@ -366,12 +371,12 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) const uint8_t *thash_ptr; if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } cx_keccak_init(hash_ctx, 256); // init hash if ((thash_ptr = type_hash(eip712_context->structs_array, struct_name, name_length)) == NULL) { - PRINTF("Memory allocation failed!\n"); return false; } // start the progressive hash on it @@ -427,6 +432,7 @@ static bool check_and_add_array_depth(const void *depth, if (path_struct == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } arr_idx = (total_count - path_struct->array_depth_count) - 1; @@ -441,6 +447,7 @@ static bool check_and_add_array_depth(const void *depth, expected_type = struct_field_array_depth(depth, &expected_size); if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Unexpected array depth size. (expected %d, got %d)\n", expected_size, size); return false; @@ -475,12 +482,14 @@ bool path_new_array_depth(uint8_t size) { if ((field_ptr = get_nth_field(NULL, pidx + 1)) == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } if (struct_field_is_array(field_ptr)) { if ((depth = get_struct_field_array_lvls_array(field_ptr, &depth_count)) == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } total_count += depth_count; @@ -497,6 +506,7 @@ bool path_new_array_depth(uint8_t size) if (pidx == path_struct->depth_count) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Did not find a matching array type.\n"); return false; } @@ -505,6 +515,7 @@ bool path_new_array_depth(uint8_t size) // memory address not aligned, padd it if ((hash_ctx = mem_alloc(sizeof(*hash_ctx))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } if (struct_field_type(field_ptr) == TYPE_CUSTOM) @@ -652,6 +663,7 @@ bool path_init(void) { if (path_struct == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct); } return path_struct != NULL; diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index ea1d1f2..eff24da 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -7,6 +7,7 @@ #include "context.h" #include "mem.h" #include "os_pic.h" +#include "apdu_constants.h" // APDU response codes // Bit indicating they are more types associated to this typename #define TYPENAME_MORE_TYPE (1 << 7) @@ -33,6 +34,7 @@ static bool find_enum_matches(const uint8_t (*enum_to_idx)[TYPES_COUNT - 1][IDX_ } if ((enum_match = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *enum_match = (*enum_to_idx)[e_idx][IDX_ENUM]; @@ -77,6 +79,7 @@ bool sol_typenames_init(void) { if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } // get pointer to the allocated space just above @@ -84,6 +87,7 @@ bool sol_typenames_init(void) if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } // copy typename @@ -130,6 +134,7 @@ const char *get_struct_field_sol_typename(const uint8_t *field_ptr, if (typename_found) return (char*)typename_ptr; typename_ptr += *length; } + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; // Not found } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index fe7ca08..a4ee85b 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -11,6 +11,7 @@ #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE #include "format_hash_field_type.h" #include "hash_bytes.h" +#include "apdu_constants.h" // APDU response codes /** * @@ -162,6 +163,7 @@ static const void **get_struct_dependencies(const void *const structs_array, *deps_count += 1; if ((new_dep = MEM_ALLOC_AND_ALIGN_TYPE(void*)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return NULL; } if (*deps_count == 1) @@ -213,7 +215,10 @@ const uint8_t *type_hash(const void *const structs_array, // loop over each struct and generate string for (int idx = 0; idx < deps_count; ++idx) { - encode_and_hash_type(*deps); + if (encode_and_hash_type(*deps) == false) + { + return NULL; + } deps += 1; } mem_dealloc(mem_alloc(0) - mem_loc_bak); @@ -221,6 +226,7 @@ const uint8_t *type_hash(const void *const structs_array, // End progressive hashing if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return NULL; } // copy hash into memory diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 6fb7827..fa21891 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -16,6 +16,7 @@ #include "context.h" // eip712_context_deinit #include "uint256.h" // tostring256 && tostring256_signed #include "path.h" // path_get_root_type +#include "apdu_constants.h" // APDU response codes static t_ui_context *ui_ctx = NULL; @@ -359,6 +360,10 @@ bool ui_712_init(void) ui_ctx->pos = UI_712_POS_REVIEW; ui_ctx->filtering_mode = EIP712_FILTERING_BASIC; } + else + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + } return ui_ctx != NULL; } From b782a6913712ec291bb04be9e8bb293a6822b32b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 11:41:46 +0200 Subject: [PATCH 110/184] Moved all typed data in RAM related code into its own file --- src_features/signMessageEIP712/eip712.h | 53 --- src_features/signMessageEIP712/entrypoint.c | 361 +------------- src_features/signMessageEIP712/field_hash.c | 1 + .../format_hash_field_type.c | 1 + src_features/signMessageEIP712/path.c | 1 + .../signMessageEIP712/sol_typenames.c | 1 + src_features/signMessageEIP712/type_hash.c | 1 + src_features/signMessageEIP712/typed_data.c | 441 ++++++++++++++++++ src_features/signMessageEIP712/typed_data.h | 69 +++ src_features/signMessageEIP712/ui_logic.c | 1 + 10 files changed, 517 insertions(+), 413 deletions(-) create mode 100644 src_features/signMessageEIP712/typed_data.c create mode 100644 src_features/signMessageEIP712/typed_data.h diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/eip712.h index ff68f5b..7561c03 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/eip712.h @@ -6,27 +6,6 @@ #include #include -typedef enum -{ - // contract defined struct - TYPE_CUSTOM = 0, - // native types - TYPE_SOL_INT, - TYPE_SOL_UINT, - TYPE_SOL_ADDRESS, - TYPE_SOL_BOOL, - TYPE_SOL_STRING, - TYPE_SOL_BYTES_FIX, - TYPE_SOL_BYTES_DYN, - TYPES_COUNT -} e_type; - -typedef enum -{ - ARRAY_DYNAMIC = 0, - ARRAY_FIXED_SIZE -} e_array_type; - // APDUs INS #define INS_STRUCT_DEF 0x18 #define INS_STRUCT_IMPL 0x1A @@ -45,12 +24,6 @@ typedef enum #define P2_KEY 0x00 #define P2_VALUE 0xFF -// TypeDesc masks -#define TYPE_MASK (0xF) -#define ARRAY_MASK (1 << 7) -#define TYPESIZE_MASK (1 << 6) -#define TYPENAME_ENUM (0xF) - typedef enum { EIP712_TYPE_HASH, @@ -62,32 +35,6 @@ typedef enum #define DOMAIN_STRUCT_NAME "EIP712Domain" -// TODO: Move these into a new file -const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); -const uint8_t *get_struct_fields_array(const uint8_t *ptr, - uint8_t *const length); -const char *get_struct_field_typename(const uint8_t *ptr, - uint8_t *const length); -bool struct_field_has_typesize(const uint8_t *ptr); -uint8_t get_struct_field_typesize(const uint8_t *ptr); -bool struct_field_is_array(const uint8_t *ptr); -e_type struct_field_type(const uint8_t *ptr); -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, - uint8_t *const length); -e_array_type struct_field_array_depth(const uint8_t *ptr, - uint8_t *const array_size); -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); -const char *get_struct_field_typename(const uint8_t *ptr, - uint8_t *const length); -const char *get_struct_field_keyname(const uint8_t *ptr, - uint8_t *const length); -const uint8_t *get_next_struct_field(const void *ptr); -const uint8_t *get_structn(const uint8_t *const ptr, - const char *const name_ptr, - const uint8_t name_length); -const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); -const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); - #endif // HAVE_EIP712_FULL_SUPPORT #endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 2daf964..5304391 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -17,6 +17,7 @@ #include "ui_logic.h" #include "common_712.h" #include "path.h" +#include "typed_data.h" #ifdef HAVE_EIP712_TESTING_KEY static const uint8_t EIP712_FEEDER_PUBLIC_KEY[] = { @@ -28,366 +29,6 @@ static const uint8_t EIP712_FEEDER_PUBLIC_KEY[] = { }; #endif // HAVE_EIP712_TESTING_KEY -// lib functions -const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) -{ - *array_size = *(uint8_t*)ptr; - return (ptr + 1); -} - -const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) -{ - return (char*)get_array_in_mem(ptr, string_length); -} - -// ptr must point to the beginning of a struct field -static inline uint8_t get_struct_field_typedesc(const uint8_t *ptr) -{ - return *ptr; -} - -// ptr must point to the beginning of a struct field -bool struct_field_is_array(const uint8_t *ptr) -{ - return (get_struct_field_typedesc(ptr) & ARRAY_MASK); -} - -// ptr must point to the beginning of a struct field -bool struct_field_has_typesize(const uint8_t *ptr) -{ - return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); -} - -// ptr must point to the beginning of a struct field -e_type struct_field_type(const uint8_t *ptr) -{ - return (get_struct_field_typedesc(ptr) & TYPE_MASK); -} - -// ptr must point to the beginning of a struct field -// TODO: Extra check inside or not -uint8_t get_struct_field_typesize(const uint8_t *ptr) -{ - return *(ptr + 1); -} - -// ptr must point to the beginning of a struct field -const char *get_struct_field_custom_typename(const uint8_t *ptr, - uint8_t *const length) -{ - ptr += 1; // skip TypeDesc - return get_string_in_mem(ptr, length); -} - -// ptr must point to the beginning of a struct field -const char *get_struct_field_typename(const uint8_t *ptr, - uint8_t *const length) -{ - if (struct_field_type(ptr) == TYPE_CUSTOM) - { - return get_struct_field_custom_typename(ptr, length); - } - return get_struct_field_sol_typename(ptr, length); -} - -// ptr must point to the beginning of a depth level -e_array_type struct_field_array_depth(const uint8_t *ptr, - uint8_t *const array_size) -{ - if (*ptr == ARRAY_FIXED_SIZE) - { - *array_size = *(ptr + 1); - } - return *ptr; -} - -// ptr must point to the beginning of a struct field level -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) -{ - switch (*ptr) - { - case ARRAY_DYNAMIC: - break; - case ARRAY_FIXED_SIZE: - ptr += 1; - break; - default: - // should not be in here :^) - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return NULL; - } - return ptr + 1; -} - -// Skips TypeDesc and TypeSize/Length+TypeName -// Came to be since it is used in multiple functions -// TODO: Find better name -const uint8_t *struct_field_half_skip(const uint8_t *ptr) -{ - const uint8_t *field_ptr; - uint8_t size; - - field_ptr = ptr; - ptr += 1; // skip TypeDesc - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { - get_string_in_mem(ptr, &size); - ptr += (1 + size); // skip typename - } - else if (struct_field_has_typesize(field_ptr)) - { - ptr += 1; // skip TypeSize - } - return ptr; -} - -// ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, - uint8_t *const length) -{ - ptr = struct_field_half_skip(ptr); - return get_array_in_mem(ptr, length); -} - -// ptr must point to the beginning of a struct field -const char *get_struct_field_keyname(const uint8_t *ptr, - uint8_t *const length) -{ - const uint8_t *field_ptr; - uint8_t size; - - field_ptr = ptr; - ptr = struct_field_half_skip(ptr); - if (struct_field_is_array(field_ptr)) - { - ptr = get_array_in_mem(ptr, &size); - while (size-- > 0) - { - ptr = get_next_struct_field_array_lvl(ptr); - } - } - return get_string_in_mem(ptr, length); -} - -// ptr must point to the beginning of a struct field -const uint8_t *get_next_struct_field(const void *ptr) -{ - uint8_t length; - - ptr = (uint8_t*)get_struct_field_keyname(ptr, &length); - return (ptr + length); -} - -// ptr must point to the beginning of a struct -const char *get_struct_name(const uint8_t *ptr, uint8_t *const length) -{ - return (char*)get_string_in_mem(ptr, length); -} - -// ptr must point to the beginning of a struct -const uint8_t *get_struct_fields_array(const uint8_t *ptr, - uint8_t *const length) -{ - uint8_t name_length; - - get_struct_name(ptr, &name_length); - ptr += (1 + name_length); // skip length - return get_array_in_mem(ptr, length); -} - -// ptr must point to the beginning of a struct -const uint8_t *get_next_struct(const uint8_t *ptr) -{ - uint8_t fields_count; - - ptr = get_struct_fields_array(ptr, &fields_count); - while (fields_count-- > 0) - { - ptr = get_next_struct_field(ptr); - } - return ptr; -} - -// ptr must point to the size of the structs array -const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) -{ - return get_array_in_mem(ptr, length); -} - -// Finds struct with a given name -const uint8_t *get_structn(const uint8_t *const ptr, - const char *const name_ptr, - const uint8_t name_length) -{ - uint8_t structs_count; - const uint8_t *struct_ptr; - const char *name; - uint8_t length; - - struct_ptr = get_structs_array(ptr, &structs_count); - while (structs_count-- > 0) - { - name = get_struct_name(struct_ptr, &length); - if ((name_length == length) && (memcmp(name, name_ptr, length) == 0)) - { - return struct_ptr; - } - struct_ptr = get_next_struct(struct_ptr); - } - return NULL; -} - -static inline const uint8_t *get_struct(const uint8_t *const ptr, - const char *const name_ptr) -{ - return get_structn(ptr, name_ptr, strlen(name_ptr)); -} -// - -bool set_struct_name(const uint8_t *const data) -{ - uint8_t *length_ptr; - char *name_ptr; - - // increment number of structs - *(eip712_context->structs_array) += 1; - - // copy length - if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *length_ptr = data[OFFSET_LC]; - - // copy name - if ((name_ptr = mem_alloc(sizeof(char) * data[OFFSET_LC])) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); - - // initialize number of fields - if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *(eip712_context->current_struct_fields_array) = 0; - return true; -} - -// TODO: Split this function -// TODO: Handle partial sends -bool set_struct_field(const uint8_t *const data) -{ - uint8_t data_idx = OFFSET_CDATA; - uint8_t *type_desc_ptr; - uint8_t *type_size_ptr; - uint8_t *typename_len_ptr; - char *typename; - uint8_t *array_levels_count; - e_array_type *array_level; - uint8_t *array_level_size; - uint8_t *fieldname_len_ptr; - char *fieldname_ptr; - - // increment number of struct fields - *(eip712_context->current_struct_fields_array) += 1; - - // copy TypeDesc - if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *type_desc_ptr = data[data_idx++]; - - // check TypeSize flag in TypeDesc - if (*type_desc_ptr & TYPESIZE_MASK) - { - // copy TypeSize - if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *type_size_ptr = data[data_idx++]; - } - else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) - { - // copy custom struct name length - if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *typename_len_ptr = data[data_idx++]; - - // copy name - if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - memmove(typename, &data[data_idx], *typename_len_ptr); - data_idx += *typename_len_ptr; - } - if (*type_desc_ptr & ARRAY_MASK) - { - if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *array_levels_count = data[data_idx++]; - for (int idx = 0; idx < *array_levels_count; ++idx) - { - if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *array_level = data[data_idx++]; - switch (*array_level) - { - case ARRAY_DYNAMIC: // nothing to do - break; - case ARRAY_FIXED_SIZE: - if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *array_level_size = data[data_idx++]; - break; - default: - // should not be in here :^) - apdu_response_code = APDU_RESPONSE_INVALID_DATA; - return false; - } - } - } - - // copy length - if ((fieldname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *fieldname_len_ptr = data[data_idx++]; - - // copy name - if ((fieldname_ptr = mem_alloc(sizeof(char) * *fieldname_len_ptr)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - memmove(fieldname_ptr, &data[data_idx], *fieldname_len_ptr); - return true; -} - void handle_eip712_return_code(bool ret) { if (ret) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index c3a4eae..8e2a50b 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -13,6 +13,7 @@ #include "context.h" // contract_addr #include "utils.h" // u64_from_BE #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" static s_field_hashing *fh = NULL; diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c index b8a3892..a520abf 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.c +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -6,6 +6,7 @@ #include "eip712.h" #include "hash_bytes.h" #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" /** * Format & hash a struct field typesize diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index e630e23..20c3baf 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -12,6 +12,7 @@ #include "mem_utils.h" #include "ui_logic.h" #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" static s_path *path_struct = NULL; diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index eff24da..6fedb06 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -8,6 +8,7 @@ #include "mem.h" #include "os_pic.h" #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" // Bit indicating they are more types associated to this typename #define TYPENAME_MORE_TYPE (1 << 7) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index a4ee85b..0de3adc 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -12,6 +12,7 @@ #include "format_hash_field_type.h" #include "hash_bytes.h" #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" /** * diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c new file mode 100644 index 0000000..3cd5fd0 --- /dev/null +++ b/src_features/signMessageEIP712/typed_data.c @@ -0,0 +1,441 @@ +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include +#include +#include "typed_data.h" +#include "sol_typenames.h" +#include "apdu_constants.h" // APDU response codes +#include "context.h" +#include "mem.h" + +// lib functions +const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) +{ + if (ptr == NULL) + { + return NULL; + } + if (array_size) + { + *array_size = *(uint8_t*)ptr; + } + return (ptr + 1); +} + +const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) +{ + return (char*)get_array_in_mem(ptr, string_length); +} + +// ptr must point to the beginning of a struct field +static inline uint8_t get_struct_field_typedesc(const uint8_t *ptr) +{ + if (ptr == NULL) + { + return 0; + } + return *ptr; +} + +// ptr must point to the beginning of a struct field +bool struct_field_is_array(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & ARRAY_MASK); +} + +// ptr must point to the beginning of a struct field +bool struct_field_has_typesize(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); +} + +// ptr must point to the beginning of a struct field +e_type struct_field_type(const uint8_t *ptr) +{ + return (get_struct_field_typedesc(ptr) & TYPE_MASK); +} + +// ptr must point to the beginning of a struct field +uint8_t get_struct_field_typesize(const uint8_t *ptr) +{ + if (ptr == NULL) + { + return 0; + } + return *(ptr + 1); +} + +// ptr must point to the beginning of a struct field +const char *get_struct_field_custom_typename(const uint8_t *ptr, + uint8_t *const length) +{ + if (ptr == NULL) + { + return NULL; + } + ptr += 1; // skip TypeDesc + return get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const char *get_struct_field_typename(const uint8_t *ptr, + uint8_t *const length) +{ + if (ptr == NULL) + { + return NULL; + } + if (struct_field_type(ptr) == TYPE_CUSTOM) + { + return get_struct_field_custom_typename(ptr, length); + } + return get_struct_field_sol_typename(ptr, length); +} + +// ptr must point to the beginning of a depth level +e_array_type struct_field_array_depth(const uint8_t *ptr, + uint8_t *const array_size) +{ + if (ptr == NULL) + { + return 0; + } + if (*ptr == ARRAY_FIXED_SIZE) + { + *array_size = *(ptr + 1); + } + return *ptr; +} + +// ptr must point to the beginning of a struct field level +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) +{ + if (ptr == NULL) + { + return NULL; + } + switch (*ptr) + { + case ARRAY_DYNAMIC: + break; + case ARRAY_FIXED_SIZE: + ptr += 1; + break; + default: + // should not be in here :^) + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return NULL; + } + return ptr + 1; +} + +// Skips TypeDesc and TypeSize/Length+TypeName +// Came to be since it is used in multiple functions +// TODO: Find better name +const uint8_t *struct_field_half_skip(const uint8_t *ptr) +{ + const uint8_t *field_ptr; + uint8_t size; + + if (ptr == NULL) + { + return NULL; + } + field_ptr = ptr; + ptr += 1; // skip TypeDesc + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + get_string_in_mem(ptr, &size); + ptr += (1 + size); // skip typename + } + else if (struct_field_has_typesize(field_ptr)) + { + ptr += 1; // skip TypeSize + } + return ptr; +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, + uint8_t *const length) +{ + if (ptr == NULL) + { + return NULL; + } + ptr = struct_field_half_skip(ptr); + return get_array_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const char *get_struct_field_keyname(const uint8_t *ptr, + uint8_t *const length) +{ + const uint8_t *field_ptr; + uint8_t size; + + if (ptr == NULL) + { + return NULL; + } + field_ptr = ptr; + ptr = struct_field_half_skip(ptr); + if (struct_field_is_array(field_ptr)) + { + ptr = get_array_in_mem(ptr, &size); + while (size-- > 0) + { + ptr = get_next_struct_field_array_lvl(ptr); + } + } + return get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct field +const uint8_t *get_next_struct_field(const void *ptr) +{ + uint8_t length; + + if (ptr == NULL) + { + return NULL; + } + ptr = (uint8_t*)get_struct_field_keyname(ptr, &length); + return (ptr + length); +} + +// ptr must point to the beginning of a struct +const char *get_struct_name(const uint8_t *ptr, uint8_t *const length) +{ + if (ptr == NULL) + { + return NULL; + } + return (char*)get_string_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct +const uint8_t *get_struct_fields_array(const uint8_t *ptr, + uint8_t *const length) +{ + uint8_t name_length; + + if (ptr == NULL) + { + return NULL; + } + get_struct_name(ptr, &name_length); + ptr += (1 + name_length); // skip length + return get_array_in_mem(ptr, length); +} + +// ptr must point to the beginning of a struct +const uint8_t *get_next_struct(const uint8_t *ptr) +{ + uint8_t fields_count; + + if (ptr == NULL) + { + return NULL; + } + ptr = get_struct_fields_array(ptr, &fields_count); + while (fields_count-- > 0) + { + ptr = get_next_struct_field(ptr); + } + return ptr; +} + +// ptr must point to the size of the structs array +const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) +{ + return get_array_in_mem(ptr, length); +} + +// Finds struct with a given name +const uint8_t *get_structn(const uint8_t *const ptr, + const char *const name_ptr, + const uint8_t name_length) +{ + uint8_t structs_count; + const uint8_t *struct_ptr; + const char *name; + uint8_t length; + + if ((ptr == NULL) || (name_ptr == NULL)) + { + return NULL; + } + struct_ptr = get_structs_array(ptr, &structs_count); + while (structs_count-- > 0) + { + name = get_struct_name(struct_ptr, &length); + if ((name_length == length) && (memcmp(name, name_ptr, length) == 0)) + { + return struct_ptr; + } + struct_ptr = get_next_struct(struct_ptr); + } + return NULL; +} + +static inline const uint8_t *get_struct(const uint8_t *const ptr, + const char *const name_ptr) +{ + return get_structn(ptr, name_ptr, strlen(name_ptr)); +} +// + +bool set_struct_name(const uint8_t *const data) +{ + uint8_t *length_ptr; + char *name_ptr; + + if ((data == NULL) || (eip712_context == NULL)) + { + return false; + } + // increment number of structs + *(eip712_context->structs_array) += 1; + + // copy length + if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *length_ptr = data[OFFSET_LC]; + + // copy name + if ((name_ptr = mem_alloc(sizeof(char) * data[OFFSET_LC])) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); + + // initialize number of fields + if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *(eip712_context->current_struct_fields_array) = 0; + return true; +} + +// TODO: Split this function +// TODO: Handle partial sends +bool set_struct_field(const uint8_t *const data) +{ + uint8_t data_idx = OFFSET_CDATA; + uint8_t *type_desc_ptr; + uint8_t *type_size_ptr; + uint8_t *typename_len_ptr; + char *typename; + uint8_t *array_levels_count; + e_array_type *array_level; + uint8_t *array_level_size; + uint8_t *fieldname_len_ptr; + char *fieldname_ptr; + + if ((data == NULL) || (eip712_context == NULL)) + { + return false; + } + // increment number of struct fields + *(eip712_context->current_struct_fields_array) += 1; + + // copy TypeDesc + if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *type_desc_ptr = data[data_idx++]; + + // check TypeSize flag in TypeDesc + if (*type_desc_ptr & TYPESIZE_MASK) + { + // copy TypeSize + if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *type_size_ptr = data[data_idx++]; + } + else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) + { + // copy custom struct name length + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *typename_len_ptr = data[data_idx++]; + + // copy name + if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + memmove(typename, &data[data_idx], *typename_len_ptr); + data_idx += *typename_len_ptr; + } + if (*type_desc_ptr & ARRAY_MASK) + { + if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_levels_count = data[data_idx++]; + for (int idx = 0; idx < *array_levels_count; ++idx) + { + if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_level = data[data_idx++]; + switch (*array_level) + { + case ARRAY_DYNAMIC: // nothing to do + break; + case ARRAY_FIXED_SIZE: + if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_level_size = data[data_idx++]; + break; + default: + // should not be in here :^) + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + } + } + + // copy length + if ((fieldname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *fieldname_len_ptr = data[data_idx++]; + + // copy name + if ((fieldname_ptr = mem_alloc(sizeof(char) * *fieldname_len_ptr)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + memmove(fieldname_ptr, &data[data_idx], *fieldname_len_ptr); + return true; +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h new file mode 100644 index 0000000..f367959 --- /dev/null +++ b/src_features/signMessageEIP712/typed_data.h @@ -0,0 +1,69 @@ +#ifndef TYPED_DATA_H_ +#define TYPED_DATA_H_ + +#ifdef HAVE_EIP712_FULL_SUPPORT + +#include +#include + +// TypeDesc masks +#define TYPE_MASK (0xF) +#define ARRAY_MASK (1 << 7) +#define TYPESIZE_MASK (1 << 6) +#define TYPENAME_ENUM (0xF) + + +typedef enum +{ + ARRAY_DYNAMIC = 0, + ARRAY_FIXED_SIZE +} e_array_type; + +typedef enum +{ + // contract defined struct + TYPE_CUSTOM = 0, + // native types + TYPE_SOL_INT, + TYPE_SOL_UINT, + TYPE_SOL_ADDRESS, + TYPE_SOL_BOOL, + TYPE_SOL_STRING, + TYPE_SOL_BYTES_FIX, + TYPE_SOL_BYTES_DYN, + TYPES_COUNT +} e_type; + +const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); +const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); +bool struct_field_is_array(const uint8_t *ptr); +bool struct_field_has_typesize(const uint8_t *ptr); +e_type struct_field_type(const uint8_t *ptr); +uint8_t get_struct_field_typesize(const uint8_t *ptr); +const char *get_struct_field_custom_typename(const uint8_t *ptr, + uint8_t *const length); +const char *get_struct_field_typename(const uint8_t *ptr, + uint8_t *const length); +e_array_type struct_field_array_depth(const uint8_t *ptr, + uint8_t *const array_size); +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); +const uint8_t *struct_field_half_skip(const uint8_t *ptr); +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, + uint8_t *const length); +const char *get_struct_field_keyname(const uint8_t *ptr, + uint8_t *const length); +const uint8_t *get_next_struct_field(const void *ptr); +const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); +const uint8_t *get_struct_fields_array(const uint8_t *ptr, + uint8_t *const length); +const uint8_t *get_next_struct(const uint8_t *ptr); +const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length); +const uint8_t *get_structn(const uint8_t *const ptr, + const char *const name_ptr, + const uint8_t name_length); +bool set_struct_name(const uint8_t *const data); +bool set_struct_field(const uint8_t *const data); + +#endif // HAVE_EIP712_FULL_SUPPORT + +#endif // TYPED_DATA_H_ diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index fa21891..c66bdf2 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -17,6 +17,7 @@ #include "uint256.h" // tostring256 && tostring256_signed #include "path.h" // path_get_root_type #include "apdu_constants.h" // APDU response codes +#include "typed_data.h" static t_ui_context *ui_ctx = NULL; From cd77ca5152146a21cc049759ab18d34290176fbb Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 11:53:22 +0200 Subject: [PATCH 111/184] Small refactoring on EIP712 set_struct_name function --- src_features/signMessageEIP712/entrypoint.c | 2 +- src_features/signMessageEIP712/typed_data.c | 10 +++++----- src_features/signMessageEIP712/typed_data.h | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/entrypoint.c index 5304391..6bbabd1 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/entrypoint.c @@ -59,7 +59,7 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) switch (apdu_buf[OFFSET_P2]) { case P2_NAME: - ret = set_struct_name(apdu_buf); + ret = set_struct_name(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; case P2_FIELD: ret = set_struct_field(apdu_buf); diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 3cd5fd0..d2d05d4 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -286,12 +286,12 @@ static inline const uint8_t *get_struct(const uint8_t *const ptr, } // -bool set_struct_name(const uint8_t *const data) +bool set_struct_name(uint8_t length, const uint8_t *const name) { uint8_t *length_ptr; char *name_ptr; - if ((data == NULL) || (eip712_context == NULL)) + if ((name == NULL) || (eip712_context == NULL)) { return false; } @@ -304,15 +304,15 @@ bool set_struct_name(const uint8_t *const data) apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *length_ptr = data[OFFSET_LC]; + *length_ptr = length; // copy name - if ((name_ptr = mem_alloc(sizeof(char) * data[OFFSET_LC])) == NULL) + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - memmove(name_ptr, &data[OFFSET_CDATA], data[OFFSET_LC]); + memmove(name_ptr, name, length); // initialize number of fields if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index f367959..aaa8dbe 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -61,7 +61,7 @@ const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_structn(const uint8_t *const ptr, const char *const name_ptr, const uint8_t name_length); -bool set_struct_name(const uint8_t *const data); +bool set_struct_name(uint8_t length, const uint8_t *const name); bool set_struct_field(const uint8_t *const data); #endif // HAVE_EIP712_FULL_SUPPORT From 399b865e5cfac02ee74c15c2e18918303ccb15d9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 14:38:04 +0200 Subject: [PATCH 112/184] Moved EIP712 filtering related code into its own file --- src_features/signMessageEIP712/entrypoint.c | 247 +------------------ src_features/signMessageEIP712/filtering.c | 186 ++++++++++++++ src_features/signMessageEIP712/filtering.h | 13 + src_features/signMessageEIP712/schema_hash.c | 66 +++++ src_features/signMessageEIP712/schema_hash.h | 12 + 5 files changed, 282 insertions(+), 242 deletions(-) create mode 100644 src_features/signMessageEIP712/filtering.c create mode 100644 src_features/signMessageEIP712/filtering.h create mode 100644 src_features/signMessageEIP712/schema_hash.c create mode 100644 src_features/signMessageEIP712/schema_hash.h 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_ From 7dad477413649f6fef3e1bc9811ed0810820dea1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 17:18:22 +0200 Subject: [PATCH 113/184] Moved EIP 712 commands handling into their own file & renamed some things --- src/apdu_constants.h | 7 ------- src/main.c | 2 +- src/utils.h | 3 +++ .../{entrypoint.c => commands_712.c} | 5 +++-- .../{eip712.h => commands_712.h} | 18 +++++------------- src_features/signMessageEIP712/context.c | 1 - src_features/signMessageEIP712/encode_field.c | 1 - src_features/signMessageEIP712/field_hash.c | 1 - src_features/signMessageEIP712/filtering.c | 2 +- .../signMessageEIP712/format_hash_field_type.c | 2 +- src_features/signMessageEIP712/path.c | 2 +- src_features/signMessageEIP712/sol_typenames.c | 2 +- src_features/signMessageEIP712/type_hash.c | 1 - src_features/signMessageEIP712/ui_logic.c | 1 - .../signMessageEIP712_v0/cmd_signMessage712.c | 1 - 15 files changed, 16 insertions(+), 33 deletions(-) rename src_features/signMessageEIP712/{entrypoint.c => commands_712.c} (98%) rename src_features/signMessageEIP712/{eip712.h => commands_712.h} (67%) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index aeb2986..5f1cbf4 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -182,13 +182,6 @@ 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 - extern uint16_t apdu_response_code; #endif // _APDU_CONSTANTS_H_ diff --git a/src/main.c b/src/main.c index 708f2bf..85417e3 100644 --- a/src/main.c +++ b/src/main.c @@ -29,7 +29,7 @@ #include "handle_swap_sign_transaction.h" #include "handle_get_printable_amount.h" #include "handle_check_address.h" -#include "context.h" +#include "commands_712.h" #ifdef HAVE_STARKWARE #include "stark_crypto.h" diff --git a/src/utils.h b/src/utils.h index 3477081..3d772b8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -22,6 +22,9 @@ #include "uint256.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + void array_hexstr(char* strbuf, const void* bin, unsigned int len); void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target); diff --git a/src_features/signMessageEIP712/entrypoint.c b/src_features/signMessageEIP712/commands_712.c similarity index 98% rename from src_features/signMessageEIP712/entrypoint.c rename to src_features/signMessageEIP712/commands_712.c index 27bbc7f..22feb20 100644 --- a/src_features/signMessageEIP712/entrypoint.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -3,8 +3,8 @@ #include #include #include +#include "commands_712.h" #include "apdu_constants.h" // APDU response codes -#include "eip712.h" #include "context.h" #include "field_hash.h" #include "path.h" @@ -164,13 +164,14 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) bool handle_eip712_sign(const uint8_t *const apdu_buf) { bool ret = false; + uint8_t length = apdu_buf[OFFSET_LC]; if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; } else if (parseBip32(&apdu_buf[OFFSET_CDATA], - &apdu_buf[OFFSET_LC], + &length, &tmpCtx.messageSigningContext.bip32) != NULL) { if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) diff --git a/src_features/signMessageEIP712/eip712.h b/src_features/signMessageEIP712/commands_712.h similarity index 67% rename from src_features/signMessageEIP712/eip712.h rename to src_features/signMessageEIP712/commands_712.h index 7561c03..2814e29 100644 --- a/src_features/signMessageEIP712/eip712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -6,10 +6,6 @@ #include #include -// APDUs INS -#define INS_STRUCT_DEF 0x18 -#define INS_STRUCT_IMPL 0x1A - // APDUs P1 #define P1_COMPLETE 0x00 #define P1_PARTIAL 0xFF @@ -24,17 +20,13 @@ #define P2_KEY 0x00 #define P2_VALUE 0xFF -typedef enum -{ - EIP712_TYPE_HASH, - EIP712_FIELD_HASH, - EIP712_STRUCT_HASH -} e_eip712_hash_type; - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - #define DOMAIN_STRUCT_NAME "EIP712Domain" +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 // EIP712_H_ diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 8d30e20..8902797 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -3,7 +3,6 @@ #include #include #include "context.h" -#include "eip712.h" #include "mem.h" #include "mem_utils.h" #include "sol_typenames.h" diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index a4d0f2b..a48d0fa 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -4,7 +4,6 @@ #include #include "encode_field.h" #include "mem.h" -#include "eip712.h" #include "shared_context.h" #include "apdu_constants.h" // APDU response codes diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 8e2a50b..6a838ed 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -6,7 +6,6 @@ #include "path.h" #include "mem.h" #include "mem_utils.h" -#include "eip712.h" #include "shared_context.h" #include "ui_logic.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 7f36e50..5050e31 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -5,7 +5,7 @@ #include "ethUstream.h" // INT256_LENGTH #include "apdu_constants.h" // APDU return codes #include "context.h" -#include "eip712.h" +#include "commands_712.h" #include "typed_data.h" #include "path.h" #include "ui_logic.h" diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c index a520abf..7c0a549 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.c +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -3,7 +3,7 @@ #include "format_hash_field_type.h" #include "mem.h" #include "mem_utils.h" -#include "eip712.h" +#include "commands_712.h" #include "hash_bytes.h" #include "apdu_constants.h" // APDU response codes #include "typed_data.h" diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 20c3baf..12d802d 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -5,7 +5,7 @@ #include "path.h" #include "mem.h" #include "context.h" -#include "eip712.h" +#include "commands_712.h" #include "type_hash.h" #include "shared_context.h" #include "ethUtils.h" diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 6fedb06..50b595b 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -3,12 +3,12 @@ #include #include #include "sol_typenames.h" -#include "eip712.h" #include "context.h" #include "mem.h" #include "os_pic.h" #include "apdu_constants.h" // APDU response codes #include "typed_data.h" +#include "utils.h" // ARRAY_SIZE // Bit indicating they are more types associated to this typename #define TYPENAME_MORE_TYPE (1 << 7) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 0de3adc..5f972a1 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -5,7 +5,6 @@ #include #include "mem.h" #include "mem_utils.h" -#include "eip712.h" #include "type_hash.h" #include "shared_context.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index c66bdf2..e6f68aa 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -9,7 +9,6 @@ #include "ux_flow_engine.h" #include "ui_flow_712.h" #include "shared_context.h" -#include "eip712.h" // get_struct_name #include "ethUtils.h" // getEthDisplayableAddress #include "utils.h" // uint256_to_decimal #include "common_712.h" diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index 6865b3a..b878f0a 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -2,7 +2,6 @@ #include "apdu_constants.h" #include "utils.h" #include "ui_flow.h" -#include "eip712.h" #include "common_712.h" #include "ethUtils.h" From a375e5b07d96a67b6672e7c31b1a1c20c73d3bcd Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 17:49:45 +0200 Subject: [PATCH 114/184] Moved the memory allocator and its util functions out of the EIP712 subdirectory and gave it its own compilation flag Also removed dead code --- Makefile | 8 ++++++- .../signMessageEIP712 => src_common}/mem.c | 22 +++---------------- .../signMessageEIP712 => src_common}/mem.h | 8 ++----- .../mem_utils.c | 20 ++--------------- src_common/mem_utils.h | 16 ++++++++++++++ src_features/signMessageEIP712/mem_utils.h | 18 --------------- 6 files changed, 30 insertions(+), 62 deletions(-) rename {src_features/signMessageEIP712 => src_common}/mem.c (78%) rename {src_features/signMessageEIP712 => src_common}/mem.h (62%) rename {src_features/signMessageEIP712 => src_common}/mem_utils.c (81%) create mode 100644 src_common/mem_utils.h delete mode 100644 src_features/signMessageEIP712/mem_utils.h diff --git a/Makefile b/Makefile index 536c003..67efcde 100644 --- a/Makefile +++ b/Makefile @@ -137,9 +137,15 @@ DEFINES += HAVE_NFT_TESTING_KEY endif endif +# Dynamic memory allocator +ifneq ($(TARGET_NAME),TARGET_NANOS) +DEFINES += HAVE_DYN_MEM_ALLOC +endif + # EIP-712 ifneq ($(TARGET_NAME),TARGET_NANOS) -DEFINES += HAVE_EIP712_FULL_SUPPORT HAVE_EIP712_TESTING_KEY +DEFINES += HAVE_EIP712_FULL_SUPPORT +DEFINES += HAVE_EIP712_TESTING_KEY endif # Enabling debug PRINTF diff --git a/src_features/signMessageEIP712/mem.c b/src_common/mem.c similarity index 78% rename from src_features/signMessageEIP712/mem.c rename to src_common/mem.c index e4c01c4..291c4d7 100644 --- a/src_features/signMessageEIP712/mem.c +++ b/src_common/mem.c @@ -1,16 +1,12 @@ -#ifdef HAVE_EIP712_FULL_SUPPORT +#ifdef HAVE_DYN_MEM_ALLOC #include #include "mem.h" -#include "shared_context.h" #define SIZE_MEM_BUFFER 5120 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; -#ifdef DEBUG -size_t mem_max; -#endif /** @@ -19,9 +15,6 @@ size_t mem_max; void mem_init(void) { mem_idx = 0; -#ifdef DEBUG - mem_max = 0; -#endif } /** @@ -34,6 +27,7 @@ void mem_reset(void) /** * Allocates a chunk of the memory buffer of a given size. + * * Checks to see if there are enough space left in the memory buffer, returns * the current location in the memory buffer and moves the index accordingly. * @@ -44,22 +38,12 @@ void *mem_alloc(size_t size) { if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded { -#ifdef DEBUG - PRINTF("Memory exhausted!\n"); -#endif return NULL; } mem_idx += size; -#ifdef DEBUG - if (mem_idx > mem_max) - { - mem_max = mem_idx; - } -#endif return &mem_buffer[mem_idx - size]; } - /** * De-allocates a chunk of memory buffer by a given size. * @@ -77,4 +61,4 @@ void mem_dealloc(size_t size) } } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_DYN_MEM_ALLOC diff --git a/src_features/signMessageEIP712/mem.h b/src_common/mem.h similarity index 62% rename from src_features/signMessageEIP712/mem.h rename to src_common/mem.h index 0cb9aca..e885137 100644 --- a/src_features/signMessageEIP712/mem.h +++ b/src_common/mem.h @@ -1,7 +1,7 @@ #ifndef MEM_H_ #define MEM_H_ -#ifdef HAVE_EIP712_FULL_SUPPORT +#ifdef HAVE_DYN_MEM_ALLOC #include @@ -10,10 +10,6 @@ void mem_reset(void); void *mem_alloc(size_t size); void mem_dealloc(size_t size); -#ifdef DEBUG -extern size_t mem_max; -#endif - -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_DYN_MEM_ALLOC #endif // MEM_H_ diff --git a/src_features/signMessageEIP712/mem_utils.c b/src_common/mem_utils.c similarity index 81% rename from src_features/signMessageEIP712/mem_utils.c rename to src_common/mem_utils.c index e45280f..e49a321 100644 --- a/src_features/signMessageEIP712/mem_utils.c +++ b/src_common/mem_utils.c @@ -1,4 +1,4 @@ -#ifdef HAVE_EIP712_FULL_SUPPORT +#ifdef HAVE_DYN_MEM_ALLOC #include #include @@ -6,22 +6,6 @@ #include "mem.h" #include "mem_utils.h" -void *mem_alloc_and_copy(const void *data, size_t size) -{ - void *mem_ptr; - - if ((mem_ptr = mem_alloc(size)) != NULL) - { - memmove(mem_ptr, data, size); - } - return mem_ptr; -} - -char *mem_alloc_and_copy_char(char c) -{ - return mem_alloc_and_copy(&c, sizeof(char)); -} - /** * Format an unsigned number up to 32-bit into memory into an ASCII string. * @@ -79,4 +63,4 @@ void *mem_alloc_and_align(size_t size, size_t alignment) return mem_alloc(size); } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_DYN_MEM_ALLOC diff --git a/src_common/mem_utils.h b/src_common/mem_utils.h new file mode 100644 index 0000000..1633d97 --- /dev/null +++ b/src_common/mem_utils.h @@ -0,0 +1,16 @@ +#ifndef MEM_UTILS_H_ +#define MEM_UTILS_H_ + +#ifdef HAVE_DYN_MEM_ALLOC + +#include +#include + +#define MEM_ALLOC_AND_ALIGN_TYPE(type) mem_alloc_and_align(sizeof(type), __alignof__(type)) + +char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); +void *mem_alloc_and_align(size_t size, size_t alignment); + +#endif // HAVE_DYN_MEM_ALLOC + +#endif // MEM_UTILS_H_ diff --git a/src_features/signMessageEIP712/mem_utils.h b/src_features/signMessageEIP712/mem_utils.h deleted file mode 100644 index 3064f9b..0000000 --- a/src_features/signMessageEIP712/mem_utils.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MEM_UTILS_H_ -#define MEM_UTILS_H_ - -#ifdef HAVE_EIP712_FULL_SUPPORT - -#include -#include - -#define MEM_ALLOC_AND_ALIGN_TYPE(type) mem_alloc_and_align(sizeof(type), __alignof__(type)) - -char *mem_alloc_and_copy_char(char c); -void *mem_alloc_and_copy(const void *data, size_t size); -char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); -void *mem_alloc_and_align(size_t size, size_t alignment); - -#endif // HAVE_EIP712_FULL_SUPPORT - -#endif // MEM_UTILS_H_ From 47c1f737362d0daf6463a3910fac280bf2a946e7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 5 Jul 2022 18:31:14 +0200 Subject: [PATCH 115/184] Removed unused EIP712 get_struct function --- src_features/signMessageEIP712/typed_data.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index d2d05d4..a3a949e 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -278,12 +278,6 @@ const uint8_t *get_structn(const uint8_t *const ptr, } return NULL; } - -static inline const uint8_t *get_struct(const uint8_t *const ptr, - const char *const name_ptr) -{ - return get_structn(ptr, name_ptr, strlen(name_ptr)); -} // bool set_struct_name(uint8_t length, const uint8_t *const name) From d1d1aa73e6b370603e345ad1bf2b69cca7aca336 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 6 Jul 2022 10:03:42 +0200 Subject: [PATCH 116/184] Made all EIP712 code use the same function for response APDUs --- src_features/signMessageEIP712/commands_712.c | 6 +++--- src_features/signMessageEIP712/commands_712.h | 1 + src_features/signMessageEIP712/field_hash.c | 5 ++--- src_features/signMessageEIP712/ui_logic.c | 6 ++---- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 22feb20..5681767 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -14,9 +14,9 @@ #include "filtering.h" #include "common_712.h" -static void handle_eip712_return_code(bool ret) +void handle_eip712_return_code(bool success) { - if (ret) + if (success) { apdu_response_code = APDU_RESPONSE_OK; } @@ -25,7 +25,7 @@ static void handle_eip712_return_code(bool ret) // Send back the response, do not restart the event loop io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - if (!ret) + if (!success) { eip712_context_deinit(); } diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index 2814e29..33e7509 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -26,6 +26,7 @@ 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); +void handle_eip712_return_code(bool success); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 6a838ed..22b2f9d 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -13,6 +13,7 @@ #include "utils.h" // u64_from_BE #include "apdu_constants.h" // APDU response codes #include "typed_data.h" +#include "commands_712.h" static s_field_hashing *fh = NULL; @@ -206,9 +207,7 @@ bool field_hash(const uint8_t *data, apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + handle_eip712_return_code(true); } return true; diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index e6f68aa..9e0ddcb 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -17,6 +17,7 @@ #include "path.h" // path_get_root_type #include "apdu_constants.h" // APDU response codes #include "typed_data.h" +#include "commands_712.h" static t_ui_context *ui_ctx = NULL; @@ -140,10 +141,7 @@ void ui_712_next_field(void) { if (!ui_ctx->end_reached) { - // reply to previous APDU - G_io_apdu_buffer[0] = 0x90; - G_io_apdu_buffer[1] = 0x00; - io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); + handle_eip712_return_code(true); } else { From aeb8072cf0a11b199cdc87425764239da26193e3 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 6 Jul 2022 15:09:31 +0200 Subject: [PATCH 117/184] Removed solidity typenames from EIP712 context structure --- src_features/signMessageEIP712/context.h | 1 - src_features/signMessageEIP712/sol_typenames.c | 11 ++++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 3386a6f..2df0aef 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -8,7 +8,6 @@ typedef struct { - uint8_t *typenames_array; uint8_t *structs_array; uint8_t *current_struct_fields_array; uint8_t contract_addr[ADDRESS_LENGTH]; diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 50b595b..79b9ee7 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -3,7 +3,6 @@ #include #include #include "sol_typenames.h" -#include "context.h" #include "mem.h" #include "os_pic.h" #include "apdu_constants.h" // APDU response codes @@ -13,6 +12,8 @@ // Bit indicating they are more types associated to this typename #define TYPENAME_MORE_TYPE (1 << 7) +static uint8_t *sol_typenames = NULL; + enum { IDX_ENUM = 0, @@ -67,11 +68,11 @@ bool sol_typenames_init(void) uint8_t *typename_len_ptr; char *typename_ptr; - if ((eip712_context->typenames_array = mem_alloc(sizeof(uint8_t))) == NULL) + if ((sol_typenames = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } - *(eip712_context->typenames_array) = 0; + *(sol_typenames) = 0; // loop over typenames for (uint8_t s_idx = 0; s_idx < ARRAY_SIZE(typenames); ++s_idx) { @@ -95,7 +96,7 @@ bool sol_typenames_init(void) memcpy(typename_ptr, PIC(typenames[s_idx]), *typename_len_ptr); } // increment array size - *(eip712_context->typenames_array) += 1; + *(sol_typenames) += 1; } return true; } @@ -116,7 +117,7 @@ const char *get_struct_field_sol_typename(const uint8_t *field_ptr, bool typename_found; field_type = struct_field_type(field_ptr); - typename_ptr = get_array_in_mem(eip712_context->typenames_array, &typenames_count); + typename_ptr = get_array_in_mem(sol_typenames, &typenames_count); typename_found = false; while (typenames_count-- > 0) { From f0764545aedef0f4dff4687793854c92323b2a77 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 6 Jul 2022 17:51:57 +0200 Subject: [PATCH 118/184] Small simplification/refactoring of EIP712 typed data --- src_features/signMessageEIP712/context.c | 8 ++- src_features/signMessageEIP712/context.h | 2 - src_features/signMessageEIP712/path.c | 12 ++--- src_features/signMessageEIP712/schema_hash.c | 2 +- src_features/signMessageEIP712/type_hash.c | 22 +++----- src_features/signMessageEIP712/type_hash.h | 3 +- src_features/signMessageEIP712/typed_data.c | 54 +++++++++++++++----- src_features/signMessageEIP712/typed_data.h | 13 +++-- 8 files changed, 71 insertions(+), 45 deletions(-) diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 8902797..0318f39 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -9,6 +9,7 @@ #include "path.h" #include "field_hash.h" #include "ui_logic.h" +#include "typed_data.h" #include "apdu_constants.h" // APDU response codes #include "shared_context.h" // reset_app_context #include "ui_callbacks.h" // ui_idle @@ -50,20 +51,17 @@ bool eip712_context_init(void) return false; } - // set types pointer - if ((eip712_context->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + if (typed_data_init() == false) // this needs to be initialized last ! { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - // create len(types) - *(eip712_context->structs_array) = 0; return true; } void eip712_context_deinit(void) { + typed_data_deinit(); path_deinit(); field_hash_deinit(); ui_712_deinit(); diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 2df0aef..0d08c5b 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -8,8 +8,6 @@ typedef struct { - uint8_t *structs_array; - uint8_t *current_struct_fields_array; uint8_t contract_addr[ADDRESS_LENGTH]; uint8_t schema_hash[224 / 8]; } s_eip712_context; diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 12d802d..8d167ff 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -61,7 +61,7 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, if (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &length); - if ((struct_ptr = get_structn(eip712_context->structs_array, typename, length)) == NULL) + if ((struct_ptr = get_structn(typename, length)) == NULL) { return NULL; } @@ -97,7 +97,7 @@ const void *path_get_nth_field_to_last(uint8_t n) if (field_ptr != NULL) { typename = get_struct_field_typename(field_ptr, &typename_len); - struct_ptr = get_structn(eip712_context->structs_array, typename, typename_len); + struct_ptr = get_structn(typename, typename_len); } return struct_ptr; } @@ -298,7 +298,7 @@ static bool path_update(void) while (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &typename_len); - if ((struct_ptr = get_structn(eip712_context->structs_array, typename, typename_len)) == NULL) + if ((struct_ptr = get_structn(typename, typename_len)) == NULL) { return false; } @@ -318,7 +318,7 @@ static bool path_update(void) } cx_keccak_init(hash_ctx, 256); // initialize it // get the struct typehash - if ((thash_ptr = type_hash(eip712_context->structs_array, typename, typename_len)) == NULL) + if ((thash_ptr = type_hash(typename, typename_len)) == NULL) { return false; } @@ -353,7 +353,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } - path_struct->root_struct = get_structn(eip712_context->structs_array, struct_name, name_length); + path_struct->root_struct = get_structn(struct_name, name_length); if (path_struct->root_struct == NULL) { @@ -376,7 +376,7 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } cx_keccak_init(hash_ctx, 256); // init hash - if ((thash_ptr = type_hash(eip712_context->structs_array, struct_name, name_length)) == NULL) + if ((thash_ptr = type_hash(struct_name, name_length)) == NULL) { return false; } diff --git a/src_features/signMessageEIP712/schema_hash.c b/src_features/signMessageEIP712/schema_hash.c index 7ebd83c..004b464 100644 --- a/src_features/signMessageEIP712/schema_hash.c +++ b/src_features/signMessageEIP712/schema_hash.c @@ -18,7 +18,7 @@ bool compute_schema_hash(void) cx_sha224_init(&hash_ctx); - struct_ptr = get_structs_array(eip712_context->structs_array, &structs_count); + struct_ptr = get_structs_array(&structs_count); hash_byte('{', (cx_hash_t*)&hash_ctx); while (structs_count-- > 0) { diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 5f972a1..593fe1d 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -81,7 +81,6 @@ static bool encode_and_hash_type(const void *const struct_ptr) /** * * - * @param[in] structs_array pointer to structs array * @param[in] deps_count count of how many struct dependencies pointers * @param[in,out] deps pointer to the first dependency pointer */ @@ -119,16 +118,14 @@ static void sort_dependencies(uint8_t deps_count, /** * * - * @param[in] structs_array pointer to structs array * @param[out] deps_count count of how many struct dependencie pointers * @param[in] first_dep pointer to the first dependency pointer * @param[in] struct_ptr pointer to the struct we are getting the dependencies of * @return \ref false in case of a memory allocation error, \ref true otherwise */ -static const void **get_struct_dependencies(const void *const structs_array, - uint8_t *const deps_count, - const void **first_dep, - const void *const struct_ptr) +static const void **get_struct_dependencies(uint8_t *const deps_count, + const void **first_dep, + const void *const struct_ptr) { uint8_t fields_count; const void *field_ptr; @@ -146,7 +143,7 @@ static const void **get_struct_dependencies(const void *const structs_array, // get struct name arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); // from its name, get the pointer to its definition - arg_struct_ptr = get_structn(structs_array, arg_structname, arg_structname_length); + arg_struct_ptr = get_structn(arg_structname, arg_structname_length); // check if it is not already present in the dependencies array for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) @@ -172,7 +169,7 @@ static const void **get_struct_dependencies(const void *const structs_array, } *new_dep = arg_struct_ptr; // TODO: Move away from recursive calls - get_struct_dependencies(structs_array, deps_count, first_dep, arg_struct_ptr); + get_struct_dependencies(deps_count, first_dep, arg_struct_ptr); } } field_ptr = get_next_struct_field(field_ptr); @@ -183,18 +180,15 @@ static const void **get_struct_dependencies(const void *const structs_array, /** * * - * @param[in] structs_array pointer to structs array * @param[in] struct_name name of the given struct * @param[in] struct_name_length length of the name of the given struct * @param[in] with_deps if hashed typestring should include struct dependencies * @return pointer to encoded string or \ref NULL in case of a memory allocation error */ -const uint8_t *type_hash(const void *const structs_array, - const char *const struct_name, +const uint8_t *type_hash(const char *const struct_name, const uint8_t struct_name_length) { - const void *const struct_ptr = get_structn(structs_array, - struct_name, + const void *const struct_ptr = get_structn(struct_name, struct_name_length); uint8_t deps_count = 0; const void **deps; @@ -202,7 +196,7 @@ const uint8_t *type_hash(const void *const structs_array, void *mem_loc_bak = mem_alloc(0); cx_keccak_init(&global_sha3, 256); // init hash - deps = get_struct_dependencies(structs_array, &deps_count, NULL, struct_ptr); + deps = get_struct_dependencies(&deps_count, NULL, struct_ptr); if ((deps_count > 0) && (deps == NULL)) { return NULL; diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index 7fe8bd2..abed80a 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -5,8 +5,7 @@ #include -const uint8_t *type_hash(const void *const structs_array, - const char *const struct_name, +const uint8_t *type_hash(const char *const struct_name, const uint8_t struct_name_length); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index a3a949e..bfd9e82 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -7,6 +7,37 @@ #include "apdu_constants.h" // APDU response codes #include "context.h" #include "mem.h" +#include "mem_utils.h" + +static s_typed_data *typed_data = NULL; + + +bool typed_data_init(void) +{ + if (typed_data == NULL) + { + if ((typed_data = MEM_ALLOC_AND_ALIGN_TYPE(*typed_data)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + // set types pointer + if ((typed_data->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + + // create len(types) + *(typed_data->structs_array) = 0; + } + return true; +} + +void typed_data_deinit(void) +{ + typed_data = NULL; +} // lib functions const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) @@ -247,14 +278,13 @@ const uint8_t *get_next_struct(const uint8_t *ptr) } // ptr must point to the size of the structs array -const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length) +const uint8_t *get_structs_array(uint8_t *const length) { - return get_array_in_mem(ptr, length); + return get_array_in_mem(typed_data->structs_array, length); } // Finds struct with a given name -const uint8_t *get_structn(const uint8_t *const ptr, - const char *const name_ptr, +const uint8_t *get_structn(const char *const name_ptr, const uint8_t name_length) { uint8_t structs_count; @@ -262,11 +292,11 @@ const uint8_t *get_structn(const uint8_t *const ptr, const char *name; uint8_t length; - if ((ptr == NULL) || (name_ptr == NULL)) + if (name_ptr == NULL) { return NULL; } - struct_ptr = get_structs_array(ptr, &structs_count); + struct_ptr = get_structs_array(&structs_count); while (structs_count-- > 0) { name = get_struct_name(struct_ptr, &length); @@ -285,12 +315,12 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) uint8_t *length_ptr; char *name_ptr; - if ((name == NULL) || (eip712_context == NULL)) + if ((name == NULL) || (typed_data == NULL)) { return false; } // increment number of structs - *(eip712_context->structs_array) += 1; + *(typed_data->structs_array) += 1; // copy length if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) @@ -309,12 +339,12 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) memmove(name_ptr, name, length); // initialize number of fields - if ((eip712_context->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) + if ((typed_data->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *(eip712_context->current_struct_fields_array) = 0; + *(typed_data->current_struct_fields_array) = 0; return true; } @@ -333,12 +363,12 @@ bool set_struct_field(const uint8_t *const data) uint8_t *fieldname_len_ptr; char *fieldname_ptr; - if ((data == NULL) || (eip712_context == NULL)) + if ((data == NULL) || (typed_data == NULL)) { return false; } // increment number of struct fields - *(eip712_context->current_struct_fields_array) += 1; + *(typed_data->current_struct_fields_array) += 1; // copy TypeDesc if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index aaa8dbe..66df765 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -34,6 +34,12 @@ typedef enum TYPES_COUNT } e_type; +typedef struct +{ + uint8_t *structs_array; + uint8_t *current_struct_fields_array; +} s_typed_data; + const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); bool struct_field_is_array(const uint8_t *ptr); @@ -57,12 +63,13 @@ const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_struct_fields_array(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_next_struct(const uint8_t *ptr); -const uint8_t *get_structs_array(const uint8_t *ptr, uint8_t *const length); -const uint8_t *get_structn(const uint8_t *const ptr, - const char *const name_ptr, +const uint8_t *get_structs_array(uint8_t *const length); +const uint8_t *get_structn(const char *const name_ptr, const uint8_t name_length); bool set_struct_name(uint8_t length, const uint8_t *const name); bool set_struct_field(const uint8_t *const data); +bool typed_data_init(void); +void typed_data_deinit(void); #endif // HAVE_EIP712_FULL_SUPPORT From b35fc03a649a7f4286a2d139772a6e39122d33aa Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 6 Jul 2022 18:04:29 +0200 Subject: [PATCH 119/184] Small simplification/refactoring of EIP712 solidity typenames matching --- src_features/signMessageEIP712/sol_typenames.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 79b9ee7..ec9fa53 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -21,14 +21,14 @@ enum IDX_COUNT }; -static bool find_enum_matches(const uint8_t (*enum_to_idx)[TYPES_COUNT - 1][IDX_COUNT], uint8_t s_idx) +static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT], uint8_t s_idx) { uint8_t *enum_match = NULL; // loop over enum/typename pairs - for (uint8_t e_idx = 0; e_idx < ARRAY_SIZE(*enum_to_idx); ++e_idx) + for (uint8_t e_idx = 0; e_idx < (TYPES_COUNT - 1); ++e_idx) { - if (s_idx == (*enum_to_idx)[e_idx][IDX_STR_IDX]) // match + if (s_idx == enum_to_idx[e_idx][IDX_STR_IDX]) // match { if (enum_match != NULL) // in case of a previous match, mark it { @@ -39,7 +39,7 @@ static bool find_enum_matches(const uint8_t (*enum_to_idx)[TYPES_COUNT - 1][IDX_ apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *enum_match = (*enum_to_idx)[e_idx][IDX_ENUM]; + *enum_match = enum_to_idx[e_idx][IDX_ENUM]; } } return (enum_match != NULL); @@ -77,7 +77,7 @@ bool sol_typenames_init(void) for (uint8_t s_idx = 0; s_idx < ARRAY_SIZE(typenames); ++s_idx) { // if at least one match was found - if (find_enum_matches(&enum_to_idx, s_idx)) + if (find_enum_matches(enum_to_idx, s_idx)) { if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { From 3ed8b3b0efecbcff6be7c1c9f308ad297f79bd5a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 7 Jul 2022 10:20:46 +0200 Subject: [PATCH 120/184] Removed mentions about un-needed multi-APDU EIP712 SEND STRUCT DEFINITION --- doc/ethapp.adoc | 4 +--- src_features/signMessageEIP712/typed_data.c | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 33d613f..f1e5423 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -618,9 +618,7 @@ _Command_ [width="80%"] |========================================================================= | *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* -| E0 | 1A | 00 : complete send - - 01 : partial send, more to come +| E0 | 1A | 00 | 00 : struct name FF : struct field diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index bfd9e82..b819c6e 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -349,7 +349,6 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) } // TODO: Split this function -// TODO: Handle partial sends bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_CDATA; From 04d78eaaa94c6c84b2a66eae33043330ecf170c0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 7 Jul 2022 11:20:42 +0200 Subject: [PATCH 121/184] Refactored/Split up EIP712 set struct field function --- src_features/signMessageEIP712/typed_data.c | 135 ++++++++++++-------- 1 file changed, 82 insertions(+), 53 deletions(-) diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index b819c6e..9bac5f5 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -348,17 +348,89 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) return true; } -// TODO: Split this function +static bool set_struct_field_custom(const uint8_t *const data, uint8_t *data_idx) +{ + uint8_t *typename_len_ptr; + char *typename; + + // copy custom struct name length + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *typename_len_ptr = data[(*data_idx)++]; + + // copy name + if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + memmove(typename, &data[*data_idx], *typename_len_ptr); + *data_idx += *typename_len_ptr; + return true; +} + +static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) +{ + uint8_t *array_levels_count; + e_array_type *array_level; + uint8_t *array_level_size; + + if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_levels_count = data[(*data_idx)++]; + for (int idx = 0; idx < *array_levels_count; ++idx) + { + if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_level = data[(*data_idx)++]; + switch (*array_level) + { + case ARRAY_DYNAMIC: // nothing to do + break; + case ARRAY_FIXED_SIZE: + if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *array_level_size = data[(*data_idx)++]; + break; + default: + // should not be in here :^) + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + } + return true; +} + +static bool set_struct_field_typesize(const uint8_t *const data, uint8_t *data_idx) +{ + uint8_t *type_size_ptr; + + // copy TypeSize + if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *type_size_ptr = data[(*data_idx)++]; + return true; +} + bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_CDATA; uint8_t *type_desc_ptr; - uint8_t *type_size_ptr; - uint8_t *typename_len_ptr; - char *typename; - uint8_t *array_levels_count; - e_array_type *array_level; - uint8_t *array_level_size; uint8_t *fieldname_len_ptr; char *fieldname_ptr; @@ -380,67 +452,24 @@ bool set_struct_field(const uint8_t *const data) // check TypeSize flag in TypeDesc if (*type_desc_ptr & TYPESIZE_MASK) { - // copy TypeSize - if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + if (set_struct_field_typesize(data, &data_idx) == false) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *type_size_ptr = data[data_idx++]; } else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) { - // copy custom struct name length - if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + if (set_struct_field_custom(data, &data_idx) == false) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *typename_len_ptr = data[data_idx++]; - - // copy name - if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - memmove(typename, &data[data_idx], *typename_len_ptr); - data_idx += *typename_len_ptr; } if (*type_desc_ptr & ARRAY_MASK) { - if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) + if (set_struct_field_array(data, &data_idx) == false) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *array_levels_count = data[data_idx++]; - for (int idx = 0; idx < *array_levels_count; ++idx) - { - if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *array_level = data[data_idx++]; - switch (*array_level) - { - case ARRAY_DYNAMIC: // nothing to do - break; - case ARRAY_FIXED_SIZE: - if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - *array_level_size = data[data_idx++]; - break; - default: - // should not be in here :^) - apdu_response_code = APDU_RESPONSE_INVALID_DATA; - return false; - } - } } // copy length From 832bbbf24c436da1304c43f4dcac7556bb70fe3e Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 7 Jul 2022 17:52:01 +0200 Subject: [PATCH 122/184] Cleaned up the EIP712 field hash code --- src_features/signMessageEIP712/field_hash.c | 318 +++++++++++--------- 1 file changed, 184 insertions(+), 134 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 22b2f9d..4b915e5 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -14,6 +14,7 @@ #include "apdu_constants.h" // APDU response codes #include "typed_data.h" #include "commands_712.h" +#include "hash_bytes.h" static s_field_hashing *fh = NULL; @@ -36,54 +37,206 @@ void field_hash_deinit(void) fh = NULL; } +static const uint8_t *field_hash_prepare(const void *const field_ptr, + const uint8_t *data, + uint8_t *data_length) +{ + e_type field_type; + + field_type = struct_field_type(field_ptr); + fh->remaining_size = __builtin_bswap16(*(uint16_t*)&data[0]); // network byte order + data += sizeof(uint16_t); + *data_length -= sizeof(uint16_t); + fh->state = FHS_WAITING_FOR_MORE; + if (IS_DYN(field_type)) + { + cx_keccak_init(&global_sha3, 256); // init hash + ui_712_new_field(field_ptr, data, *data_length); + } + return data; +} + +static const uint8_t *field_hash_finalize_static(const void *const field_ptr, + const uint8_t *const data, + uint8_t data_length) +{ + uint8_t *value = NULL; + e_type field_type; + + field_type = struct_field_type(field_ptr); + switch (field_type) + { + case TYPE_SOL_INT: + value = encode_int(data, data_length, get_struct_field_typesize(field_ptr)); + break; + case TYPE_SOL_UINT: + value = encode_uint(data, data_length); + break; + case TYPE_SOL_BYTES_FIX: + value = encode_bytes(data, data_length); + break; + case TYPE_SOL_ADDRESS: + value = encode_address(data, data_length); + break; + case TYPE_SOL_BOOL: + value = encode_boolean((bool*)data, data_length); + break; + case TYPE_CUSTOM: + default: + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + PRINTF("Unknown solidity type!\n"); + return NULL; + } + + if (value == NULL) + { + return NULL; + } + ui_712_new_field(field_ptr, data, data_length); + return value; +} + +static uint8_t *field_hash_finalize_dynamic(void) +{ + uint8_t *value; + + if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return NULL; + } + // copy hash into memory + cx_hash((cx_hash_t*)&global_sha3, + CX_LAST, + NULL, + 0, + value, + KECCAK256_HASH_BYTESIZE); + return value; +} + +static void field_hash_feed_parent(e_type field_type, const uint8_t *const value) +{ + uint8_t len; + + if (IS_DYN(field_type)) + { + len = KECCAK256_HASH_BYTESIZE; + } + else + { + len = EIP_712_ENCODED_FIELD_LENGTH; + } + + // last thing in mem is the hash of the previous field + // and just before it is the current hash context + cx_sha3_t *hash_ctx = (cx_sha3_t*)(value - sizeof(cx_sha3_t)); + // start the progressive hash on it + hash_nbytes(value, len, (cx_hash_t*)hash_ctx); + // deallocate it + mem_dealloc(len); +} + +static bool field_hash_domain_special_fields(const void *const field_ptr, + const uint8_t *const data, + uint8_t data_length) +{ + const char *key; + uint8_t keylen; + + key = get_struct_field_keyname(field_ptr, &keylen); + // copy contract address into context + if (strncmp(key, "verifyingContract", keylen) == 0) + { + if (data_length != sizeof(eip712_context->contract_addr)) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + PRINTF("Unexpected verifyingContract length!\n"); + return false; + } + memcpy(eip712_context->contract_addr, data, data_length); + } + else if (strncmp(key, "chainId", keylen) == 0) + { + uint64_t chainId = u64_from_BE(data, data_length); + + if (chainId != chainConfig->chainId) + { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", + sizeof(chainConfig->chainId), + &chainConfig->chainId, + sizeof(chainId), + &chainId); + return false; + } + } + return true; +} + +static bool field_hash_finalize(const void *const field_ptr, + const uint8_t *const data, + uint8_t data_length) +{ + const uint8_t *value = NULL; + e_type field_type; + + field_type = struct_field_type(field_ptr); + if (!IS_DYN(field_type)) + { + if ((value = field_hash_finalize_static(field_ptr, + data, + data_length)) == NULL) + { + return false; + } + } + else + { + if ((value = field_hash_finalize_dynamic()) == NULL) + { + return false; + } + } + + field_hash_feed_parent(field_type, value); + + if (path_get_root_type() == ROOT_DOMAIN) + { + if (field_hash_domain_special_fields(field_ptr, data, data_length) == false) + { + return false; + } + } + path_advance(); + fh->state = FHS_IDLE; + ui_712_finalize_field(); + return true; +} + bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) { const void *field_ptr; e_type field_type; - uint8_t *value = NULL; - const char *key; - uint8_t keylen; -#ifdef DEBUG - const char *type; - uint8_t typelen; -#endif - if (fh == NULL) - { - return false; - } - // get field by path - if ((field_ptr = path_get_field()) == NULL) + if ((fh == NULL) || ((field_ptr = path_get_field()) == NULL)) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } - key = get_struct_field_keyname(field_ptr, &keylen); + field_type = struct_field_type(field_ptr); if (fh->state == FHS_IDLE) // first packet for this frame { - fh->remaining_size = __builtin_bswap16(*(uint16_t*)&data[0]); // network byte order - data += sizeof(uint16_t); - data_length -= sizeof(uint16_t); - fh->state = FHS_WAITING_FOR_MORE; - if (IS_DYN(field_type)) - { - cx_keccak_init(&global_sha3, 256); // init hash - ui_712_new_field(field_ptr, data, data_length); - } + data = field_hash_prepare(field_ptr, data, &data_length); } fh->remaining_size -= data_length; // if a dynamic type -> continue progressive hash if (IS_DYN(field_type)) { - cx_hash((cx_hash_t*)&global_sha3, - 0, - data, - data_length, - NULL, - 0); + hash_nbytes(data, data_length, (cx_hash_t*)&global_sha3); } if (fh->remaining_size == 0) { @@ -92,113 +245,10 @@ bool field_hash(const uint8_t *data, apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } -#ifdef DEBUG - PRINTF("=> "); - type = get_struct_field_typename(field_ptr, &typelen); - fwrite(type, sizeof(char), typelen, stdout); - PRINTF(" "); - key = get_struct_field_keyname(field_ptr, &keylen); - fwrite(key, sizeof(char), keylen, stdout); - PRINTF("\n"); -#endif - - if (!IS_DYN(field_type)) + if (field_hash_finalize(field_ptr, data, data_length) == false) { - switch (field_type) - { - case TYPE_SOL_INT: - value = encode_int(data, data_length, get_struct_field_typesize(field_ptr)); - break; - case TYPE_SOL_UINT: - value = encode_uint(data, data_length); - break; - case TYPE_SOL_BYTES_FIX: - value = encode_bytes(data, data_length); - break; - case TYPE_SOL_ADDRESS: - value = encode_address(data, data_length); - break; - case TYPE_SOL_BOOL: - value = encode_boolean((bool*)data, data_length); - break; - case TYPE_CUSTOM: - default: - apdu_response_code = APDU_RESPONSE_INVALID_DATA; - PRINTF("Unknown solidity type!\n"); - return false; - } - - if (value == NULL) - { - return false; - } - ui_712_new_field(field_ptr, data, data_length); + return false; } - else - { - if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, - 0, - value, - KECCAK256_HASH_BYTESIZE); - } - - // TODO: Move elsewhere - uint8_t len = IS_DYN(field_type) ? - KECCAK256_HASH_BYTESIZE : - EIP_712_ENCODED_FIELD_LENGTH; - // last thing in mem is the hash of the previous field - // and just before it is the current hash context - cx_sha3_t *hash_ctx = (cx_sha3_t*)(value - sizeof(cx_sha3_t)); - // start the progressive hash on it - cx_hash((cx_hash_t*)hash_ctx, - 0, - value, - len, - NULL, - 0); - // deallocate it - mem_dealloc(len); - - if (path_get_root_type() == ROOT_DOMAIN) - { - // copy contract address into context - if (strncmp(key, "verifyingContract", keylen) == 0) - { - if (data_length != sizeof(eip712_context->contract_addr)) - { - apdu_response_code = APDU_RESPONSE_INVALID_DATA; - PRINTF("Unexpected verifyingContract length!\n"); - return false; - } - memcpy(eip712_context->contract_addr, data, data_length); - } - else if (strncmp(key, "chainId", keylen) == 0) - { - uint64_t chainId = u64_from_BE(data, data_length); - - if (chainId != chainConfig->chainId) - { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", - sizeof(chainConfig->chainId), - &chainConfig->chainId, - sizeof(chainId), - &chainId); - return false; - } - } - } - path_advance(); - fh->state = FHS_IDLE; - ui_712_finalize_field(); } else { From bce0a3114dc4190fc6e243c60eac84871aacd443 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 8 Jul 2022 15:59:23 +0200 Subject: [PATCH 123/184] WIP path refactoring --- src_features/signMessageEIP712/path.c | 177 +++++++++------------ src_features/signMessageEIP712/type_hash.c | 22 +-- src_features/signMessageEIP712/type_hash.h | 6 +- 3 files changed, 91 insertions(+), 114 deletions(-) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 8d167ff..fae422b 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -132,6 +132,56 @@ static bool path_depth_list_push(void) return true; } +static cx_sha3_t *get_last_hash_ctx(void) +{ + return (cx_sha3_t*)mem_alloc(0) - 1; +} + +static void finalize_hash_depth(uint8_t *hash) +{ + const cx_sha3_t *hash_ctx; + + hash_ctx = get_last_hash_ctx(); + // finalize hash + cx_hash((cx_hash_t*)hash_ctx, + CX_LAST, + NULL, + 0, + hash, + KECCAK256_HASH_BYTESIZE); + mem_dealloc(sizeof(*hash_ctx)); // remove hash context +} + +static void feed_last_hash_depth(uint8_t *hash) +{ + const cx_sha3_t *hash_ctx; + + hash_ctx = get_last_hash_ctx(); + // continue progressive hash with the array hash + cx_hash((cx_hash_t*)hash_ctx, + 0, + hash, + KECCAK256_HASH_BYTESIZE, + NULL, + 0); +} + +static bool push_new_hash_depth(bool init) +{ + cx_sha3_t *hash_ctx; + + // allocate new hash context + if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) + { + return false; + } + if (init) + { + cx_keccak_init(hash_ctx, 256); // initialize it + } + return true; +} + /** * Go up (remove) a depth level. * @@ -139,6 +189,8 @@ static bool path_depth_list_push(void) */ static bool path_depth_list_pop(void) { + uint8_t hash[KECCAK256_HASH_BYTESIZE]; + if (path_struct == NULL) { return false; @@ -149,27 +201,10 @@ static bool path_depth_list_pop(void) } path_struct->depth_count -= 1; - // TODO: Move elsewhere - uint8_t shash[KECCAK256_HASH_BYTESIZE]; - cx_sha3_t *hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); - // finalize hash - cx_hash((cx_hash_t*)hash_ctx, - CX_LAST, - NULL, - 0, - &shash[0], - KECCAK256_HASH_BYTESIZE); - mem_dealloc(sizeof(cx_sha3_t)); // remove hash context + finalize_hash_depth(hash); if (path_struct->depth_count > 0) { - hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); // previous one - // continue progressive hash with the array hash - cx_hash((cx_hash_t*)hash_ctx, - 0, - &shash[0], - KECCAK256_HASH_BYTESIZE, - NULL, - 0); + feed_last_hash_depth(hash); } else { @@ -177,27 +212,17 @@ static bool path_depth_list_pop(void) { case ROOT_DOMAIN: memcpy(tmpCtx.messageSigningContext712.domainHash, - shash, + hash, KECCAK256_HASH_BYTESIZE); break; case ROOT_MESSAGE: memcpy(tmpCtx.messageSigningContext712.messageHash, - shash, + hash, KECCAK256_HASH_BYTESIZE); break; default: break; } -#ifdef DEBUG - PRINTF("Hash = 0x"); - for (int idx = 0; idx < KECCAK256_HASH_BYTESIZE; ++idx) - { - // manual 0 padding, %.02x not supported by toolchain - if (shash[idx] < 0x10) PRINTF("0"); - PRINTF("%x", shash[idx]); - } - PRINTF("\n\n"); -#endif } return true; @@ -239,6 +264,8 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) */ static bool array_depth_list_pop(void) { + uint8_t hash[KECCAK256_HASH_BYTESIZE]; + if (path_struct == NULL) { return false; @@ -248,25 +275,8 @@ static bool array_depth_list_pop(void) return false; } - // TODO: Move elsewhere - uint8_t ahash[KECCAK256_HASH_BYTESIZE]; - cx_sha3_t *hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); - // finalize hash - cx_hash((cx_hash_t*)hash_ctx, - CX_LAST, - NULL, - 0, - &ahash[0], - KECCAK256_HASH_BYTESIZE); - mem_dealloc(sizeof(cx_sha3_t)); // remove hash context - hash_ctx = mem_alloc(0) - sizeof(cx_sha3_t); // previous one - // continue progressive hash with the array hash - cx_hash((cx_hash_t*)hash_ctx, - 0, - &ahash[0], - KECCAK256_HASH_BYTESIZE, - NULL, - 0); + finalize_hash_depth(hash); + feed_last_hash_depth(hash); path_struct->array_depth_count -= 1; return true; @@ -285,6 +295,7 @@ static bool path_update(void) const void *field_ptr; const char *typename; uint8_t typename_len; + uint8_t hash[KECCAK256_HASH_BYTESIZE]; if (path_struct == NULL) { @@ -307,30 +318,16 @@ static bool path_update(void) return false; } - // TODO: Move elsewhere - cx_sha3_t *hash_ctx; - const uint8_t *thash_ptr; - - // allocate new hash context - if ((hash_ctx = mem_alloc(sizeof(*hash_ctx))) == NULL) + if (push_new_hash_depth(true) == false) { return false; } - cx_keccak_init(hash_ctx, 256); // initialize it // get the struct typehash - if ((thash_ptr = type_hash(typename, typename_len)) == NULL) + if (type_hash(typename, typename_len, hash) == false) { return false; } - // start the progressive hash on it - cx_hash((cx_hash_t*)hash_ctx, - 0, - thash_ptr, - KECCAK256_HASH_BYTESIZE, - NULL, - 0); - // deallocate it - mem_dealloc(KECCAK256_HASH_BYTESIZE); + feed_last_hash_depth(hash); ui_712_queue_struct_to_review(); path_depth_list_push(); @@ -347,6 +344,8 @@ static bool path_update(void) */ bool path_set_root(const char *const struct_name, uint8_t name_length) { + uint8_t hash[KECCAK256_HASH_BYTESIZE]; + if (path_struct == NULL) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; @@ -367,28 +366,15 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } - // TODO: Move elsewhere - cx_sha3_t *hash_ctx; - const uint8_t *thash_ptr; - if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return false; - } - cx_keccak_init(hash_ctx, 256); // init hash - if ((thash_ptr = type_hash(struct_name, name_length)) == NULL) + if (push_new_hash_depth(true) == false) { return false; } - // start the progressive hash on it - cx_hash((cx_hash_t*)hash_ctx, - 0, - thash_ptr, - KECCAK256_HASH_BYTESIZE, - NULL, - 0); - // deallocate it - mem_dealloc(KECCAK256_HASH_BYTESIZE); + if (type_hash(struct_name, name_length, hash) == false) + { + return false; + } + feed_last_hash_depth(hash); // // init depth, at 0 : empty path @@ -473,6 +459,7 @@ bool path_new_array_depth(uint8_t size) uint8_t depth_count; uint8_t total_count = 0; uint8_t pidx; + bool is_custom; if (path_struct == NULL) { @@ -511,25 +498,19 @@ bool path_new_array_depth(uint8_t size) PRINTF("Did not find a matching array type.\n"); return false; } - // TODO: Move elsewhere - cx_sha3_t *hash_ctx; - // memory address not aligned, padd it - if ((hash_ctx = mem_alloc(sizeof(*hash_ctx))) == NULL) + is_custom = struct_field_type(field_ptr) == TYPE_CUSTOM; + if (push_new_hash_depth(!is_custom) == false) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - if (struct_field_type(field_ptr) == TYPE_CUSTOM) + if (is_custom) { - cx_sha3_t *old_ctx = (void*)hash_ctx - sizeof(cx_sha3_t); + cx_sha3_t *hash_ctx = get_last_hash_ctx(); + cx_sha3_t *old_ctx = hash_ctx - 1; - memcpy(hash_ctx, old_ctx, sizeof(cx_sha3_t)); + memcpy(hash_ctx, old_ctx, sizeof(*old_ctx)); cx_keccak_init(old_ctx, 256); // init hash } - else // solidity type - { - cx_keccak_init(hash_ctx, 256); // init hash - } return true; } diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 593fe1d..b0f32d6 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -185,52 +185,46 @@ static const void **get_struct_dependencies(uint8_t *const deps_count, * @param[in] with_deps if hashed typestring should include struct dependencies * @return pointer to encoded string or \ref NULL in case of a memory allocation error */ -const uint8_t *type_hash(const char *const struct_name, - const uint8_t struct_name_length) +bool type_hash(const char *const struct_name, + const uint8_t struct_name_length, + uint8_t *hash_buf) { const void *const struct_ptr = get_structn(struct_name, struct_name_length); uint8_t deps_count = 0; const void **deps; - uint8_t *hash_ptr; void *mem_loc_bak = mem_alloc(0); cx_keccak_init(&global_sha3, 256); // init hash deps = get_struct_dependencies(&deps_count, NULL, struct_ptr); if ((deps_count > 0) && (deps == NULL)) { - return NULL; + return false; } sort_dependencies(deps_count, deps); if (encode_and_hash_type(struct_ptr) == false) { - return NULL; + return false; } // loop over each struct and generate string for (int idx = 0; idx < deps_count; ++idx) { if (encode_and_hash_type(*deps) == false) { - return NULL; + return false; } deps += 1; } mem_dealloc(mem_alloc(0) - mem_loc_bak); - // End progressive hashing - if ((hash_ptr = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) - { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - return NULL; - } // copy hash into memory cx_hash((cx_hash_t*)&global_sha3, CX_LAST, NULL, 0, - hash_ptr, + hash_buf, KECCAK256_HASH_BYTESIZE); - return hash_ptr; + return true; } #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index abed80a..0017a13 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -4,9 +4,11 @@ #ifdef HAVE_EIP712_FULL_SUPPORT #include +#include -const uint8_t *type_hash(const char *const struct_name, - const uint8_t struct_name_length); +bool type_hash(const char *const struct_name, + const uint8_t struct_name_length, + uint8_t *hash_buf); #endif // HAVE_EIP712_FULL_SUPPORT From 58d1b40c799cc4019218758892cfa788b58ae16f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 8 Jul 2022 18:06:18 +0200 Subject: [PATCH 124/184] UI 712 logic refactoring --- src_features/signMessageEIP712/ui_logic.c | 181 +++++++++++++--------- src_features/signMessageEIP712/ui_logic.h | 2 +- 2 files changed, 112 insertions(+), 71 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 9e0ddcb..1cb0d16 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -196,6 +196,105 @@ void ui_712_message_hash(void) ui_712_redraw_generic_step(); } +static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) +{ + if (length != 1) + { + return false; + } + if (*data) + { + ui_712_set_value("true", 4); + } + else + { + ui_712_set_value("false", 5); + } + return true; +} + +static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) +{ + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + length, + data); + // +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'; + strcat(strings.tmp.tmp, "..."); + } +} + +static bool ui_712_format_int(const uint8_t *const data, + uint8_t length, + const void *const field_ptr) +{ + uint256_t value256; + uint128_t value128; + int32_t value32; + int16_t value16; + + switch (get_struct_field_typesize(field_ptr) * 8) + { + case 256: + convertUint256BE(data, length, &value256); + tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; + case 128: + convertUint128BE(data, length, &value128); + tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + break; + case 64: + convertUint64BEto128(data, length, &value128); + 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]; + } + 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]; + } + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + value16); // expanded to 32 bits + break; + case 8: + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + ((int8_t*)data)[0]); // expanded to 32 bits + break; + default: + PRINTF("Unhandled field typesize\n"); + return false; + } + return true; +} + +static void ui_712_format_uint(const uint8_t *const data, uint8_t length) +{ + uint256_t value256; + + convertUint256BE(data, length, &value256); + tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); +} + /** * Used to notify of a new field to review in the current struct (key + value) * @@ -203,24 +302,21 @@ void ui_712_message_hash(void) * @param[in] data pointer to the field's raw value * @param[in] length field's raw value byte-length */ -void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length) +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; - uint256_t value256; - uint128_t value128; - int32_t value32; - int16_t value16; if (ui_ctx == NULL) { - return; + return false; } // Check if this field is supposed to be displayed + // TODO: maybe put it after the big switch case so error handling on value happens if (!ui_712_field_shown()) { - return; + return true; } // Key @@ -247,85 +343,30 @@ void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, chainConfig->chainId); break; case TYPE_SOL_BOOL: - if (*data) + if (ui_712_format_bool(data, length) == false) { - ui_712_set_value("true", 4); - } - else - { - ui_712_set_value("false", 5); + return false; } break; case TYPE_SOL_BYTES_FIX: case TYPE_SOL_BYTES_DYN: - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - length, - data); - // +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'; - strcat(strings.tmp.tmp, "..."); - } + ui_712_format_bytes(data, length); break; case TYPE_SOL_INT: - switch (get_struct_field_typesize(field_ptr) * 8) + if (ui_712_format_int(data, length, field_ptr) == false) { - case 256: - convertUint256BE(data, length, &value256); - tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); - break; - case 128: - convertUint128BE(data, length, &value128); - tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); - break; - case 64: - convertUint64BEto128(data, length, &value128); - 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]; - } - 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]; - } - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%d", - value16); // expanded to 32 bits - break; - case 8: - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%d", - ((int8_t*)data)[0]); // expanded to 32 bits - break; - default: - PRINTF("Unhandled field typesize\n"); + return false; } break; case TYPE_SOL_UINT: - convertUint256BE(data, length, &value256); - tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + ui_712_format_uint(data, length); break; default: PRINTF("Unhandled type\n"); + return false; } ui_712_redraw_generic_step(); + return true; } /** diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 81a2779..ef08440 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -35,7 +35,7 @@ bool ui_712_init(void); void ui_712_deinit(void); void ui_712_next_field(void); void ui_712_review_struct(const void *const struct_ptr); -void ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); +bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length); void ui_712_end_sign(void); unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); From cfb45a795b86346fedfed0397a8a390565c1c7fa Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 11 Jul 2022 09:38:20 +0200 Subject: [PATCH 125/184] Better EIP712 field implementation error handling, including hidden ones --- src_features/signMessageEIP712/ui_logic.c | 29 +++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 1cb0d16..f5b19b7 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -130,6 +130,7 @@ void ui_712_next_field(void) { if (ui_ctx == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return; } if (ui_ctx->structs_to_review > 0) @@ -200,6 +201,7 @@ static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) { if (length != 1) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } if (*data) @@ -282,6 +284,7 @@ static bool ui_712_format_int(const uint8_t *const data, break; default: PRINTF("Unhandled field typesize\n"); + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } return true; @@ -309,27 +312,23 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, if (ui_ctx == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } - // Check if this field is supposed to be displayed - // TODO: maybe put it after the big switch case so error handling on value happens - if (!ui_712_field_shown()) + // Key + if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) { - return true; + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; } - // Key - if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) + if (!(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) { - if (!(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) - { - ui_712_set_title(key, key_len); - } + ui_712_set_title(key, key_len); } // Value - ui_712_set_value("", 0); switch (struct_field_type(field_ptr)) { case TYPE_SOL_STRING: @@ -365,7 +364,12 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, PRINTF("Unhandled type\n"); return false; } - ui_712_redraw_generic_step(); + + // Check if this field is supposed to be displayed + if (ui_712_field_shown()) + { + ui_712_redraw_generic_step(); + } return true; } @@ -377,6 +381,7 @@ void ui_712_end_sign(void) { if (ui_ctx == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return; } ui_ctx->end_reached = true; From 1eaba306e1cc426c522a4564ef3922e343ef2ac7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 11 Jul 2022 11:54:37 +0200 Subject: [PATCH 126/184] Add missing information to EIP712 send struct def APDU in doc --- doc/ethapp.adoc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index f1e5423..59c37f5 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -645,11 +645,13 @@ _Input data_ |====================================================================== | *Description* | *Length (byte)* | *Mandatory* | TypeDesc (type description) | 1 | {check_y} -| TypeSize (type byte size) | 1 | {check_n} +| TypeNameLength | 1 | {check_n} +| TypeName | variable | {check_n} +| TypeSize | 1 | {check_n} | ArrayLevelCount | 1 | {check_n} | ArrayLevels | variable | {check_n} -| KeyLength | 1 | {check_y} -| Key | variable | {check_y} +| KeyNameLength | 1 | {check_y} +| KeyName | variable | {check_y} |====================================================================== ###### TypeDesc @@ -680,6 +682,13 @@ How to interpret Type from its value : | 7 | dynamic-sized bytes |=========================================== +###### TypeName + +_Only present if the Type is set to custom._ + +Indicates the name of the struct that will be the type of the field. + + ###### TypeSize _Only present if the TypeSize bit is set in TypeDesc._ From fcc375a9aa490bd705d5c86e5d64be45e59030af Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 11 Jul 2022 14:59:54 +0200 Subject: [PATCH 127/184] Small refactoring to EIP712 typed data functions --- src_features/signMessageEIP712/typed_data.c | 172 ++++++++++++-------- src_features/signMessageEIP712/typed_data.h | 3 + 2 files changed, 109 insertions(+), 66 deletions(-) diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 9bac5f5..d9f52fd 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -40,6 +40,57 @@ void typed_data_deinit(void) } // lib functions +static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, const uint8_t *ptr) +{ + (void)ptr; + return field_ptr + sizeof(typedesc_t); +} + +static const uint8_t *field_skip_typename(const uint8_t *field_ptr, const uint8_t *ptr) +{ + uint8_t size; + + if (struct_field_type(field_ptr) == TYPE_CUSTOM) + { + get_string_in_mem(ptr, &size); + ptr += (sizeof(size) + size); + } + return ptr; +} + +static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, const uint8_t *ptr) +{ + if (struct_field_has_typesize(field_ptr)) + { + ptr += sizeof(typesize_t); + } + return ptr; +} + +static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, const uint8_t *ptr) +{ + uint8_t size; + + if (struct_field_is_array(field_ptr)) + { + ptr = get_array_in_mem(ptr, &size); + while (size-- > 0) + { + ptr = get_next_struct_field_array_lvl(ptr); + } + } + return ptr; +} + +static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, const uint8_t *ptr) +{ + uint8_t size; + + (void)field_ptr; + ptr = get_array_in_mem(ptr, &size); + return ptr + size; +} + const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { if (ptr == NULL) @@ -97,30 +148,32 @@ uint8_t get_struct_field_typesize(const uint8_t *ptr) } // ptr must point to the beginning of a struct field -const char *get_struct_field_custom_typename(const uint8_t *ptr, +const char *get_struct_field_custom_typename(const uint8_t *field_ptr, uint8_t *const length) { - if (ptr == NULL) + const uint8_t *ptr; + + if (field_ptr == NULL) { return NULL; } - ptr += 1; // skip TypeDesc + ptr = field_skip_typedesc(field_ptr, NULL); return get_string_in_mem(ptr, length); } // ptr must point to the beginning of a struct field -const char *get_struct_field_typename(const uint8_t *ptr, +const char *get_struct_field_typename(const uint8_t *field_ptr, uint8_t *const length) { - if (ptr == NULL) + if (field_ptr == NULL) { return NULL; } - if (struct_field_type(ptr) == TYPE_CUSTOM) + if (struct_field_type(field_ptr) == TYPE_CUSTOM) { - return get_struct_field_custom_typename(ptr, length); + return get_struct_field_custom_typename(field_ptr, length); } - return get_struct_field_sol_typename(ptr, length); + return get_struct_field_sol_typename(field_ptr, length); } // ptr must point to the beginning of a depth level @@ -160,116 +213,99 @@ const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) return ptr + 1; } -// Skips TypeDesc and TypeSize/Length+TypeName -// Came to be since it is used in multiple functions -// TODO: Find better name -const uint8_t *struct_field_half_skip(const uint8_t *ptr) -{ - const uint8_t *field_ptr; - uint8_t size; - - if (ptr == NULL) - { - return NULL; - } - field_ptr = ptr; - ptr += 1; // skip TypeDesc - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { - get_string_in_mem(ptr, &size); - ptr += (1 + size); // skip typename - } - else if (struct_field_has_typesize(field_ptr)) - { - ptr += 1; // skip TypeSize - } - return ptr; -} - // ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *field_ptr, uint8_t *const length) { - if (ptr == NULL) + const uint8_t *ptr; + + if (field_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - ptr = struct_field_half_skip(ptr); + ptr = field_skip_typedesc(field_ptr, NULL); + ptr = field_skip_typename(field_ptr, ptr); + ptr = field_skip_typesize(field_ptr, ptr); return get_array_in_mem(ptr, length); } // ptr must point to the beginning of a struct field -const char *get_struct_field_keyname(const uint8_t *ptr, +const char *get_struct_field_keyname(const uint8_t *field_ptr, uint8_t *const length) { - const uint8_t *field_ptr; - uint8_t size; + const uint8_t *ptr; - if (ptr == NULL) + if (field_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - field_ptr = ptr; - ptr = struct_field_half_skip(ptr); - if (struct_field_is_array(field_ptr)) - { - ptr = get_array_in_mem(ptr, &size); - while (size-- > 0) - { - ptr = get_next_struct_field_array_lvl(ptr); - } - } + ptr = field_skip_typedesc(field_ptr, NULL); + ptr = field_skip_typename(field_ptr, ptr); + ptr = field_skip_typesize(field_ptr, ptr); + ptr = field_skip_array_levels(field_ptr, ptr); return get_string_in_mem(ptr, length); } // ptr must point to the beginning of a struct field -const uint8_t *get_next_struct_field(const void *ptr) +const uint8_t *get_next_struct_field(const void *field_ptr) { - uint8_t length; + const void *ptr; - if (ptr == NULL) + if (field_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - ptr = (uint8_t*)get_struct_field_keyname(ptr, &length); - return (ptr + length); + ptr = field_skip_typedesc(field_ptr, NULL); + ptr = field_skip_typename(field_ptr, ptr); + ptr = field_skip_typesize(field_ptr, ptr); + ptr = field_skip_array_levels(field_ptr, ptr); + return field_skip_keyname(field_ptr, ptr); } // ptr must point to the beginning of a struct -const char *get_struct_name(const uint8_t *ptr, uint8_t *const length) +const char *get_struct_name(const uint8_t *struct_ptr, uint8_t *const length) { - if (ptr == NULL) + if (struct_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - return (char*)get_string_in_mem(ptr, length); + return (char*)get_string_in_mem(struct_ptr, length); } // ptr must point to the beginning of a struct -const uint8_t *get_struct_fields_array(const uint8_t *ptr, +const uint8_t *get_struct_fields_array(const uint8_t *struct_ptr, uint8_t *const length) { + const void *ptr; uint8_t name_length; - if (ptr == NULL) + if (struct_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - get_struct_name(ptr, &name_length); - ptr += (1 + name_length); // skip length + ptr = struct_ptr; + get_struct_name(struct_ptr, &name_length); + ptr += (sizeof(name_length) + name_length); // skip length return get_array_in_mem(ptr, length); } // ptr must point to the beginning of a struct -const uint8_t *get_next_struct(const uint8_t *ptr) +const uint8_t *get_next_struct(const uint8_t *struct_ptr) { uint8_t fields_count; + const void *ptr; - if (ptr == NULL) + if (struct_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - ptr = get_struct_fields_array(ptr, &fields_count); + ptr = get_struct_fields_array(struct_ptr, &fields_count); while (fields_count-- > 0) { ptr = get_next_struct_field(ptr); @@ -294,6 +330,7 @@ const uint8_t *get_structn(const char *const name_ptr, if (name_ptr == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } struct_ptr = get_structs_array(&structs_count); @@ -306,6 +343,7 @@ const uint8_t *get_structn(const char *const name_ptr, } struct_ptr = get_next_struct(struct_ptr); } + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } // @@ -317,6 +355,7 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) if ((name == NULL) || (typed_data == NULL)) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } // increment number of structs @@ -436,6 +475,7 @@ bool set_struct_field(const uint8_t *const data) if ((data == NULL) || (typed_data == NULL)) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } // increment number of struct fields diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index 66df765..d8eb1ab 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -40,6 +40,9 @@ typedef struct uint8_t *current_struct_fields_array; } s_typed_data; +typedef uint8_t typedesc_t; +typedef uint8_t typesize_t; + const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); bool struct_field_is_array(const uint8_t *ptr); From 0cfb16759cd9405bf8d6783e165a36c9d0d66d79 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 12 Jul 2022 10:24:46 +0200 Subject: [PATCH 128/184] Add missing APDU return code --- src_features/signMessageEIP712/encode_field.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index a48d0fa..98183b0 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -48,6 +48,7 @@ static void *field_encode(const uint8_t *const value, start_idx = 0; break; default: + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; // should not be here } memcpy(&padded_value[start_idx], value, length); From 0cc75edf699037819331062e7c57143839bb95f2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 11 Jul 2022 17:12:58 +0200 Subject: [PATCH 129/184] EIP712 code Doxygen comments pass --- src_features/signMessage/cmd_signMessage.c | 6 +- src_features/signMessageEIP712/commands_712.c | 38 ++- src_features/signMessageEIP712/context.c | 4 + src_features/signMessageEIP712/field_hash.c | 78 ++++- src_features/signMessageEIP712/filtering.c | 111 ++++--- src_features/signMessageEIP712/filtering.h | 10 +- src_features/signMessageEIP712/hash_bytes.c | 4 +- src_features/signMessageEIP712/path.c | 70 +++- src_features/signMessageEIP712/schema_hash.c | 15 +- .../signMessageEIP712/sol_typenames.c | 25 +- src_features/signMessageEIP712/type_hash.c | 8 +- src_features/signMessageEIP712/typed_data.c | 312 ++++++++++++++---- src_features/signMessageEIP712/typed_data.h | 4 +- src_features/signMessageEIP712/ui_logic.c | 71 +++- 14 files changed, 621 insertions(+), 135 deletions(-) diff --git a/src_features/signMessage/cmd_signMessage.c b/src_features/signMessage/cmd_signMessage.c index c344f92..3940c70 100644 --- a/src_features/signMessage/cmd_signMessage.c +++ b/src_features/signMessage/cmd_signMessage.c @@ -12,7 +12,7 @@ static const char SIGN_MAGIC[] = * Check if a given character is a "special" displayable ASCII character * * @param[in] c character we're checking - * @return wether the character is special or not + * @return whether the character is special or not */ static inline bool is_char_special(char c) { return ((c >= '\b') && (c <= '\r')); @@ -23,7 +23,7 @@ static inline bool is_char_special(char c) { * * @param[in] data the input data * @param[in] the length of the input data - * @return wether the data is fully ASCII or not + * @return whether the data is fully ASCII or not */ static bool is_data_ascii(const uint8_t *const data, size_t length) { for (uint8_t idx = 0; idx < length; ++idx) { @@ -59,7 +59,7 @@ static bool is_value_str_ascii() { * * @param[in] data the input data * @param[in] length the data length - * @param[in] is_ascii wether the data is ASCII or not + * @param[in] is_ascii whether the data is ASCII or not */ static void feed_value_str(const uint8_t *const data, size_t length, bool is_ascii) { uint16_t value_strlen = strlen(strings.tmp.tmp); diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 5681767..008c4bc 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -14,6 +14,14 @@ #include "filtering.h" #include "common_712.h" +/** + * Send the response to the previous APDU command + * + * In case of an error it uses the global variable to retrieve the error code and resets + * the app context + * + * @param[in] success whether the command was successful + */ void handle_eip712_return_code(bool success) { if (success) @@ -31,6 +39,12 @@ void handle_eip712_return_code(bool success) } } +/** + * Process the EIP712 struct definition command + * + * @param[in] apdu_buf the APDU payload + * @return whether the command was successful or not + */ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { bool ret = true; @@ -61,6 +75,12 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) return ret; } +/** + * Process the EIP712 struct implementation command + * + * @param[in] apdu_buf the APDU payload + * @return whether the command was successful or not + */ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { bool ret = false; @@ -112,10 +132,17 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) return ret; } +/** + * Process the EIP712 filtering command + * + * @param[in] apdu_buf the APDU payload + * @return whether the command was successful or not + */ 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) { @@ -135,11 +162,14 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) break; case P1_CONTRACT_NAME: case P1_FIELD_NAME: + type = (apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) + ? FILTERING_CONTRACT_NAME + : FILTERING_STRUCT_FIELD; if (ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) { ret = provide_filtering_info(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC], - apdu_buf[OFFSET_P1]); + type); if ((apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) && ret) { reply_apdu = false; @@ -161,6 +191,12 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) return ret; } +/** + * Process the EIP712 sign command + * + * @param[in] apdu_buf the APDU payload + * @return whether the command was successful or not + */ bool handle_eip712_sign(const uint8_t *const apdu_buf) { bool ret = false; diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 0318f39..b03c554 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -17,6 +17,7 @@ s_eip712_context *eip712_context = NULL; /** + * Initialize the EIP712 context * * @return a boolean indicating if the initialization was successful or not */ @@ -59,6 +60,9 @@ bool eip712_context_init(void) return true; } +/** + * De-initialize the EIP712 context + */ void eip712_context_deinit(void) { typed_data_deinit(); diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 4b915e5..1cec18e 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -18,6 +18,11 @@ static s_field_hashing *fh = NULL; +/** + * Initialize the field hash context + * + * @return whether the initialization was successful or not + */ bool field_hash_init(void) { if (fh == NULL) @@ -32,14 +37,25 @@ bool field_hash_init(void) return true; } +/** + * Deinitialize the field hash context + */ void field_hash_deinit(void) { fh = NULL; } +/** + * Special handling of the first chunk received from a field value + * + * @param[in] field_ptr pointer to the struct field definition + * @param[in] data the field value + * @param[in,out] data_length the value length + * @return the data pointer + */ static const uint8_t *field_hash_prepare(const void *const field_ptr, - const uint8_t *data, - uint8_t *data_length) + const uint8_t *data, + uint8_t *data_length) { e_type field_type; @@ -56,6 +72,16 @@ static const uint8_t *field_hash_prepare(const void *const field_ptr, return data; } +/** + * Finalize static field hash + * + * Encode the field data depending on its type + * + * @param[in] field_ptr pointer to the struct field definition + * @param[in] data the field value + * @param[in] data_length the value length + * @return pointer to the encoded value + */ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, const uint8_t *const data, uint8_t data_length) @@ -85,7 +111,6 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, default: apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Unknown solidity type!\n"); - return NULL; } if (value == NULL) @@ -96,6 +121,13 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, return value; } +/** + * Finalize dynamic field hash + * + * Allocate and hash the data + * + * @return pointer to the hash, \ref NULL if it failed + */ static uint8_t *field_hash_finalize_dynamic(void) { uint8_t *value; @@ -115,7 +147,13 @@ static uint8_t *field_hash_finalize_dynamic(void) return value; } -static void field_hash_feed_parent(e_type field_type, const uint8_t *const value) +/** + * Feed the newly created field hash into the parent struct's progressive hash + * + * @param[in] field_type the struct field's type + * @param[in] hash the field hash + */ +static void field_hash_feed_parent(e_type field_type, const uint8_t *const hash) { uint8_t len; @@ -130,13 +168,23 @@ static void field_hash_feed_parent(e_type field_type, const uint8_t *const value // last thing in mem is the hash of the previous field // and just before it is the current hash context - cx_sha3_t *hash_ctx = (cx_sha3_t*)(value - sizeof(cx_sha3_t)); - // start the progressive hash on it - hash_nbytes(value, len, (cx_hash_t*)hash_ctx); + cx_sha3_t *hash_ctx = (cx_sha3_t*)(hash - sizeof(cx_sha3_t)); + // continue the progressive hash on it + hash_nbytes(hash, len, (cx_hash_t*)hash_ctx); // deallocate it mem_dealloc(len); } +/** + * Special domain fields handling + * + * Do something special for certain EIP712Domain fields + * + * @param[in] field_ptr pointer to the struct field definition + * @param[in] data the field value + * @param[in] data_length the value length + * @return whether an error occured or not + */ static bool field_hash_domain_special_fields(const void *const field_ptr, const uint8_t *const data, uint8_t data_length) @@ -174,6 +222,14 @@ static bool field_hash_domain_special_fields(const void *const field_ptr, return true; } +/** + * Finalize the data hashing + * + * @param[in] field_ptr pointer to the struct field definition + * @param[in] data the field value + * @param[in] data_length the value length + * @return whether an error occured or not + */ static bool field_hash_finalize(const void *const field_ptr, const uint8_t *const data, uint8_t data_length) @@ -214,6 +270,14 @@ static bool field_hash_finalize(const void *const field_ptr, return true; } +/** + * Hash a field value + * + * @param[in] data the field value + * @param[in] data_length the value length + * @param[in] partial whether there is more of that data coming later or not + * @return whether the data hashing was successful or not + */ bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 5050e31..ac283e1 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -22,15 +22,62 @@ static const uint8_t EIP712_FEEDER_PUBLIC_KEY[] = { #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) +/** + * 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) { const void *field_ptr; const char *key; uint8_t key_len; + + for (uint8_t i = 0; i < path_get_depth_count(); ++i) + { + if (i > 0) + { + hash_byte('.', 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, 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, hash_ctx); + } + } + } + } + } +} + +/** + * Verify the provided signature + * + * @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 + */ +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; @@ -52,37 +99,9 @@ static bool verify_filtering_signature(uint8_t dname_length, sizeof(eip712_context->schema_hash), (cx_hash_t*)&hash_ctx); - if (p1 == P1_FIELD_NAME) + if (type == FILTERING_STRUCT_FIELD) { - 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); - } - } - } - } - } + hash_filtering_path((cx_hash_t*)&hash_ctx); } // Display name @@ -123,7 +142,17 @@ static bool verify_filtering_signature(uint8_t dname_length, return true; } -bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1) +/** + * Provide filtering information about upcoming struct field + * + * @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 + */ +bool provide_filtering_info(const uint8_t *const payload, + uint8_t length, + e_filtering_type type) { bool ret = false; uint8_t dname_len; @@ -131,7 +160,7 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin uint8_t sig_len; const uint8_t *sig; - if (p1 == P1_CONTRACT_NAME) + if (type == FILTERING_CONTRACT_NAME) { if (path_get_root_type() != ROOT_DOMAIN) { @@ -139,7 +168,7 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin return false; } } - else // P1_FIELD_NAME + else // FILTERING_STRUCT_FIELD { if (path_get_root_type() != ROOT_MESSAGE) { @@ -157,9 +186,9 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin 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 ((ret = verify_filtering_signature(dname_len, dname, sig_len, sig, type))) { - if (p1 == P1_CONTRACT_NAME) + if (type == FILTERING_CONTRACT_NAME) { if (!N_storage.verbose_eip712) { @@ -168,7 +197,7 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uin ui_712_redraw_generic_step(); } } - else // P1_FIELD_NAME + else // FILTERING_STRUCT_FIELD { if (dname_len > 0) // don't substitute for an empty name { diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index e04c6a1..c914dd7 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -6,7 +6,15 @@ #include #include -bool provide_filtering_info(const uint8_t *const payload, uint8_t length, uint8_t p1); +typedef enum +{ + FILTERING_CONTRACT_NAME, + FILTERING_STRUCT_FIELD +} e_filtering_type; + +bool provide_filtering_info(const uint8_t *const payload, + uint8_t length, + e_filtering_type type); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/hash_bytes.c b/src_features/signMessageEIP712/hash_bytes.c index 0a5ee6a..8b69c94 100644 --- a/src_features/signMessageEIP712/hash_bytes.c +++ b/src_features/signMessageEIP712/hash_bytes.c @@ -9,7 +9,7 @@ * @param[in] n number of bytes to hash * @param[in] hash_ctx pointer to the hashing context */ -void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *hash_ctx) +void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *const hash_ctx) { cx_hash(hash_ctx, 0, @@ -25,7 +25,7 @@ void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *hash_ctx) * @param[in] byte byte to hash * @param[in] hash_ctx pointer to the hashing context */ -void hash_byte(uint8_t byte, cx_hash_t *hash_ctx) +void hash_byte(uint8_t byte, cx_hash_t *const hash_ctx) { hash_nbytes(&byte, 1, hash_ctx); } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index fae422b..746dda0 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -71,7 +71,7 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, } /** - * Get the element the path is pointing to. (internal) + * Get the element the path is pointing to. * * @param[out] the number of fields in the depth of the returned field * @return the field which the path points to @@ -81,11 +81,23 @@ static inline const void *get_field(uint8_t *const fields_count) return get_nth_field(fields_count, path_struct->depth_count); } +/** + * Get Nth struct field from path + * + * @param[in] n nth depth requested + * @return pointer to the matching field, \ref NULL otherwise + */ const void *path_get_nth_field(uint8_t n) { return get_nth_field(NULL, n); } +/** + * Get Nth to last struct field from path + * + * @param[in] n nth to last depth requested + * @return pointer to the matching field, \ref NULL otherwise + */ const void *path_get_nth_field_to_last(uint8_t n) { const char *typename; @@ -103,7 +115,7 @@ const void *path_get_nth_field_to_last(uint8_t n) } /** - * Get the element the path is pointing to. (public facing) + * Get the element the path is pointing to * * @return the field which the path points to */ @@ -132,11 +144,21 @@ static bool path_depth_list_push(void) return true; } +/** + * Get the last hashing context (corresponding to the current path depth) + * + * @return pointer to the hashing context + */ static cx_sha3_t *get_last_hash_ctx(void) { return (cx_sha3_t*)mem_alloc(0) - 1; } +/** + * Finalize the last hashing context + * + * @param[out] hash pointer to buffer where the hash will be stored + */ static void finalize_hash_depth(uint8_t *hash) { const cx_sha3_t *hash_ctx; @@ -152,7 +174,12 @@ static void finalize_hash_depth(uint8_t *hash) mem_dealloc(sizeof(*hash_ctx)); // remove hash context } -static void feed_last_hash_depth(uint8_t *hash) +/** + * Continue last progressive hashing context with given hash + * + * @param[in] hash pointer to given hash + */ +static void feed_last_hash_depth(const uint8_t *const hash) { const cx_sha3_t *hash_ctx; @@ -166,6 +193,12 @@ static void feed_last_hash_depth(uint8_t *hash) 0); } +/** + * Create a new hashing context depth in memory + * + * @param[in] init if the hashing context should be initialized + * @return whether the memory allocation of the hashing context was successful + */ static bool push_new_hash_depth(bool init) { cx_sha3_t *hash_ctx; @@ -450,6 +483,7 @@ static bool check_and_add_array_depth(const void *depth, /** * Add a new array depth with a given size (number of elements). * + * @param[in] size number of elements * @return whether the add was successful or not */ bool path_new_array_depth(uint8_t size) @@ -609,6 +643,11 @@ bool path_advance(void) return true; } +/** + * Get root structure type from path (domain or message) + * + * @return enum representing root type + */ e_root_type path_get_root_type(void) { if (path_struct == NULL) @@ -618,6 +657,11 @@ e_root_type path_get_root_type(void) return path_struct->root_type; } +/** + * Get root structure from path + * + * @return pointer to the root structure definition + */ const void *path_get_root(void) { if (path_struct == NULL) @@ -627,6 +671,11 @@ const void *path_get_root(void) return path_struct->root_struct; } +/** + * Get the current amount of depth + * + * @return depth count + */ uint8_t path_get_depth_count(void) { if (path_struct == NULL) @@ -637,7 +686,7 @@ uint8_t path_get_depth_count(void) } /** - * Allocates the path indexes in memory and sets it with a depth of 0. + * Initialize the path context with its indexes in memory and sets it with a depth of 0. * * @return whether the memory allocation were successful. */ @@ -645,12 +694,21 @@ bool path_init(void) { if (path_struct == NULL) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct); + if ((path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + } + else + { + path_struct->depth_count = 0; + } } return path_struct != NULL; } +/** + * De-initialize the path context + */ void path_deinit(void) { path_struct = NULL; diff --git a/src_features/signMessageEIP712/schema_hash.c b/src_features/signMessageEIP712/schema_hash.c index 004b464..a6cd3c0 100644 --- a/src_features/signMessageEIP712/schema_hash.c +++ b/src_features/signMessageEIP712/schema_hash.c @@ -6,6 +6,19 @@ #include "format_hash_field_type.h" #include "context.h" +// the SDK does not define a SHA-224 type, define it here so it's easier +// to understand in the code +typedef cx_sha256_t cx_sha224_t; + +/** + * Compute the schema hash + * + * The schema hash is the value of the root field "types" in the JSON data, + * stripped of all its spaces and newlines. This function reconstructs the JSON syntax + * from the stored typed data. + * + * @return whether the schema hash was successful or not + */ bool compute_schema_hash(void) { const void *struct_ptr; @@ -14,7 +27,7 @@ bool compute_schema_hash(void) uint8_t fields_count; const char *name; uint8_t name_length; - cx_sha256_t hash_ctx; // sha224 + cx_sha224_t hash_ctx; cx_sha224_init(&hash_ctx); diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index ec9fa53..44d1687 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -21,14 +21,21 @@ enum IDX_COUNT }; -static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT], uint8_t s_idx) +/** + * Find a match between a typename index and all the type enums associated to it + * + * @param[in] enum_to_idx the type enum to typename index table + * @param[in] t_idx typename index + * @return whether at least one match was found + */ +static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT], uint8_t t_idx) { uint8_t *enum_match = NULL; // loop over enum/typename pairs for (uint8_t e_idx = 0; e_idx < (TYPES_COUNT - 1); ++e_idx) { - if (s_idx == enum_to_idx[e_idx][IDX_STR_IDX]) // match + if (t_idx == enum_to_idx[e_idx][IDX_STR_IDX]) // match { if (enum_match != NULL) // in case of a previous match, mark it { @@ -45,6 +52,11 @@ static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COU return (enum_match != NULL); } +/** + * Initialize solidity typenames in memory + * + * @return whether the initialization went well or not + */ bool sol_typenames_init(void) { const char *const typenames[] = { @@ -74,10 +86,10 @@ bool sol_typenames_init(void) } *(sol_typenames) = 0; // loop over typenames - for (uint8_t s_idx = 0; s_idx < ARRAY_SIZE(typenames); ++s_idx) + for (uint8_t t_idx = 0; t_idx < ARRAY_SIZE(typenames); ++t_idx) { // if at least one match was found - if (find_enum_matches(enum_to_idx, s_idx)) + if (find_enum_matches(enum_to_idx, t_idx)) { if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { @@ -85,7 +97,7 @@ bool sol_typenames_init(void) return false; } // get pointer to the allocated space just above - *typename_len_ptr = strlen(PIC(typenames[s_idx])); + *typename_len_ptr = strlen(PIC(typenames[t_idx])); if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { @@ -93,7 +105,7 @@ bool sol_typenames_init(void) return false; } // copy typename - memcpy(typename_ptr, PIC(typenames[s_idx]), *typename_len_ptr); + memcpy(typename_ptr, PIC(typenames[t_idx]), *typename_len_ptr); } // increment array size *(sol_typenames) += 1; @@ -102,6 +114,7 @@ bool sol_typenames_init(void) } /** + * Get typename from a given field * * @param[in] field_ptr pointer to a struct field * @param[out] length length of the returned typename diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index b0f32d6..67e54b8 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -14,6 +14,7 @@ #include "typed_data.h" /** + * Encode & hash the given structure field * * @param[in] field_ptr pointer to the struct field * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) @@ -37,6 +38,7 @@ static bool encode_and_hash_field(const void *const field_ptr) } /** + * Encode & hash the a given structure type * * @param[in] struct_ptr pointer to the structure we want the typestring of * @param[in] str_length length of the formatted string in memory @@ -79,7 +81,7 @@ static bool encode_and_hash_type(const void *const struct_ptr) } /** - * + * Sort the given structs based by alphabetical order * * @param[in] deps_count count of how many struct dependencies pointers * @param[in,out] deps pointer to the first dependency pointer @@ -116,7 +118,7 @@ static void sort_dependencies(uint8_t deps_count, } /** - * + * Find all the dependencies from a given structure * * @param[out] deps_count count of how many struct dependencie pointers * @param[in] first_dep pointer to the first dependency pointer @@ -178,7 +180,7 @@ static const void **get_struct_dependencies(uint8_t *const deps_count, } /** - * + * Encode the structure's type and hash it * * @param[in] struct_name name of the given struct * @param[in] struct_name_length length of the name of the given struct diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index d9f52fd..54c4838 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -11,7 +11,11 @@ static s_typed_data *typed_data = NULL; - +/** + * Initialize the typed data context + * + * @return whether the memory allocation was successful or not + */ bool typed_data_init(void) { if (typed_data == NULL) @@ -39,14 +43,29 @@ void typed_data_deinit(void) typed_data = NULL; } -// lib functions -static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, const uint8_t *ptr) +/** + * Skip TypeDesc from a structure field + * + * @param[in] field_ptr pointer to the beginning of the struct field + * @param[in] ptr pointer to the current location within the struct field + * @return pointer to the data right after + */ +static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, + const uint8_t *ptr) { (void)ptr; return field_ptr + sizeof(typedesc_t); } -static const uint8_t *field_skip_typename(const uint8_t *field_ptr, const uint8_t *ptr) +/** + * Skip the type name from a structure field + * + * @param[in] field_ptr pointer to the beginning of the struct field + * @param[in] ptr pointer to the current location within the struct field + * @return pointer to the data right after + */ +static const uint8_t *field_skip_typename(const uint8_t *field_ptr, + const uint8_t *ptr) { uint8_t size; @@ -58,7 +77,15 @@ static const uint8_t *field_skip_typename(const uint8_t *field_ptr, const uint8_ return ptr; } -static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, const uint8_t *ptr) +/** + * Skip the type size from a structure field + * + * @param[in] field_ptr pointer to the beginning of the struct field + * @param[in] ptr pointer to the current location within the struct field + * @return pointer to the data right after + */ +static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, + const uint8_t *ptr) { if (struct_field_has_typesize(field_ptr)) { @@ -67,7 +94,15 @@ static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, const uint8_ return ptr; } -static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, const uint8_t *ptr) +/** + * Skip the array levels from a structure field + * + * @param[in] field_ptr pointer to the beginning of the struct field + * @param[in] ptr pointer to the current location within the struct field + * @return pointer to the data right after + */ +static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, + const uint8_t *ptr) { uint8_t size; @@ -82,7 +117,15 @@ static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, const ui return ptr; } -static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, const uint8_t *ptr) +/** + * Skip the key name from a structure field + * + * @param[in] field_ptr pointer to the beginning of the struct field + * @param[in] ptr pointer to the current location within the struct field + * @return pointer to the data right after + */ +static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, + const uint8_t *ptr) { uint8_t size; @@ -91,6 +134,13 @@ static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, const uint8_t return ptr + size; } +/** + * Get data pointer & array size from a given pointer + * + * @param[in] ptr given pointer + * @param[out] array_size pointer to array size + * @return pointer to data + */ const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { if (ptr == NULL) @@ -101,53 +151,91 @@ const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { *array_size = *(uint8_t*)ptr; } - return (ptr + 1); + return (ptr + sizeof(*array_size)); } +/** + * Get pointer to beginning of string & its length from a given pointer + * + * @param[in] ptr given pointer + * @param[out] string_length pointer to string length + * @return pointer to beginning of the string + */ const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) { return (char*)get_array_in_mem(ptr, string_length); } -// ptr must point to the beginning of a struct field -static inline uint8_t get_struct_field_typedesc(const uint8_t *ptr) +/** + * Get the TypeDesc from a given struct field pointer + * + * @param[in] field_ptr struct field pointer + * @return TypeDesc + */ +static inline typedesc_t get_struct_field_typedesc(const uint8_t *const field_ptr) { - if (ptr == NULL) + if (field_ptr == NULL) { return 0; } - return *ptr; + return *field_ptr; } -// ptr must point to the beginning of a struct field -bool struct_field_is_array(const uint8_t *ptr) +/** + * Check whether a struct field is an array + * + * @param[in] field_ptr struct field pointer + * @return bool whether it is the case or not + */ +bool struct_field_is_array(const uint8_t *const field_ptr) { - return (get_struct_field_typedesc(ptr) & ARRAY_MASK); + return (get_struct_field_typedesc(field_ptr) & ARRAY_MASK); } -// ptr must point to the beginning of a struct field -bool struct_field_has_typesize(const uint8_t *ptr) +/** + * Check whether a struct field has a type size associated to it + * + * @param[in] field_ptr struct field pointer + * @return bool whether it is the case or not + */ +bool struct_field_has_typesize(const uint8_t *const field_ptr) { - return (get_struct_field_typedesc(ptr) & TYPESIZE_MASK); + return (get_struct_field_typedesc(field_ptr) & TYPESIZE_MASK); } -// ptr must point to the beginning of a struct field -e_type struct_field_type(const uint8_t *ptr) +/** + * Get type from a struct field + * + * @param[in] field_ptr struct field pointer + * @return its type enum + */ +e_type struct_field_type(const uint8_t *const field_ptr) { - return (get_struct_field_typedesc(ptr) & TYPE_MASK); + return (get_struct_field_typedesc(field_ptr) & TYPE_MASK); } -// ptr must point to the beginning of a struct field -uint8_t get_struct_field_typesize(const uint8_t *ptr) +/** + * Get type size from a struct field + * + * @param[in] field_ptr struct field pointer + * @return its type size + */ +uint8_t get_struct_field_typesize(const uint8_t *const field_ptr) { - if (ptr == NULL) + if (field_ptr == NULL) { return 0; } - return *(ptr + 1); + return *field_skip_typedesc(field_ptr, NULL); } -// ptr must point to the beginning of a struct field +/** + * Get custom type name from a struct field + * + * @param[in] field_ptr struct field pointer + * @param[out] length the type name length + * @return type name pointer + */ const char *get_struct_field_custom_typename(const uint8_t *field_ptr, uint8_t *const length) { @@ -161,7 +249,13 @@ const char *get_struct_field_custom_typename(const uint8_t *field_ptr, return get_string_in_mem(ptr, length); } -// ptr must point to the beginning of a struct field +/** + * Get type name from a struct field + * + * @param[in] field_ptr struct field pointer + * @param[out] length the type name length + * @return type name pointer + */ const char *get_struct_field_typename(const uint8_t *field_ptr, uint8_t *const length) { @@ -176,34 +270,51 @@ const char *get_struct_field_typename(const uint8_t *field_ptr, return get_struct_field_sol_typename(field_ptr, length); } -// ptr must point to the beginning of a depth level -e_array_type struct_field_array_depth(const uint8_t *ptr, +/** + * Get array type of a given struct field's array depth + * + * @param[in] array_depth_ptr given array depth + * @param[out] array_size pointer to array size + * @return array type of that depth + */ +e_array_type struct_field_array_depth(const uint8_t *array_depth_ptr, uint8_t *const array_size) { - if (ptr == NULL) + if (array_depth_ptr == NULL) { return 0; } - if (*ptr == ARRAY_FIXED_SIZE) + if (*array_depth_ptr == ARRAY_FIXED_SIZE) { - *array_size = *(ptr + 1); + if (array_size != NULL) + { + *array_size = *(array_depth_ptr + sizeof(uint8_t)); + } } - return *ptr; + return *array_depth_ptr; } -// ptr must point to the beginning of a struct field level -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) +/** + * Get next array depth form a given struct field's array depth + * + * @param[in] array_depth_ptr given array depth + * @return next array depth + */ +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const array_depth_ptr) { - if (ptr == NULL) + const uint8_t *ptr; + + if (array_depth_ptr == NULL) { return NULL; } - switch (*ptr) + switch (*array_depth_ptr) { case ARRAY_DYNAMIC: + ptr = array_depth_ptr; break; case ARRAY_FIXED_SIZE: - ptr += 1; + ptr = array_depth_ptr + 1; break; default: // should not be in here :^) @@ -213,8 +324,14 @@ const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr) return ptr + 1; } -// ptr must point to the beginning of a struct field -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *field_ptr, +/** + * Get the array levels from a given struct field + * + * @param[in] field_ptr given struct field + * @param[out] length number of array levels + * @return pointer to the first array level + */ +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const field_ptr, uint8_t *const length) { const uint8_t *ptr; @@ -230,7 +347,13 @@ const uint8_t *get_struct_field_array_lvls_array(const uint8_t *field_ptr, return get_array_in_mem(ptr, length); } -// ptr must point to the beginning of a struct field +/** + * Get key name from a given struct field + * + * @param[in] field_ptr given struct field + * @param[out] length name length + * @return key name + */ const char *get_struct_field_keyname(const uint8_t *field_ptr, uint8_t *const length) { @@ -248,8 +371,13 @@ const char *get_struct_field_keyname(const uint8_t *field_ptr, return get_string_in_mem(ptr, length); } -// ptr must point to the beginning of a struct field -const uint8_t *get_next_struct_field(const void *field_ptr) +/** + * Get next struct field from a given field + * + * @param[in] field_ptr given struct field + * @return pointer to the next field + */ +const uint8_t *get_next_struct_field(const void *const field_ptr) { const void *ptr; @@ -265,8 +393,14 @@ const uint8_t *get_next_struct_field(const void *field_ptr) return field_skip_keyname(field_ptr, ptr); } -// ptr must point to the beginning of a struct -const char *get_struct_name(const uint8_t *struct_ptr, uint8_t *const length) +/** + * Get name from a given struct + * + * @param[in] struct_ptr given struct + * @param[out] length name length + * @return struct name + */ +const char *get_struct_name(const uint8_t *const struct_ptr, uint8_t *const length) { if (struct_ptr == NULL) { @@ -276,8 +410,14 @@ const char *get_struct_name(const uint8_t *struct_ptr, uint8_t *const length) return (char*)get_string_in_mem(struct_ptr, length); } -// ptr must point to the beginning of a struct -const uint8_t *get_struct_fields_array(const uint8_t *struct_ptr, +/** + * Get struct fields from a given struct + * + * @param[in] struct_ptr given struct + * @param[out] length name length + * @return struct name + */ +const uint8_t *get_struct_fields_array(const uint8_t *const struct_ptr, uint8_t *const length) { const void *ptr; @@ -294,8 +434,13 @@ const uint8_t *get_struct_fields_array(const uint8_t *struct_ptr, return get_array_in_mem(ptr, length); } -// ptr must point to the beginning of a struct -const uint8_t *get_next_struct(const uint8_t *struct_ptr) +/** + * Get next struct from a given struct + * + * @param[in] struct_ptr given struct + * @return pointer to next struct + */ +const uint8_t *get_next_struct(const uint8_t *const struct_ptr) { uint8_t fields_count; const void *ptr; @@ -313,22 +458,33 @@ const uint8_t *get_next_struct(const uint8_t *struct_ptr) return ptr; } -// ptr must point to the size of the structs array +/** + * Get structs array + * + * @param[out] length number of structs + * @return pointer to the first struct + */ const uint8_t *get_structs_array(uint8_t *const length) { return get_array_in_mem(typed_data->structs_array, length); } -// Finds struct with a given name -const uint8_t *get_structn(const char *const name_ptr, - const uint8_t name_length) +/** + * Find struct with a given name + * + * @param[in] name struct name + * @param[in] length name length + * @return pointer to struct + */ +const uint8_t *get_structn(const char *const name, + const uint8_t length) { uint8_t structs_count; const uint8_t *struct_ptr; - const char *name; - uint8_t length; + const char *struct_name; + uint8_t name_length; - if (name_ptr == NULL) + if (name == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; @@ -336,8 +492,8 @@ const uint8_t *get_structn(const char *const name_ptr, struct_ptr = get_structs_array(&structs_count); while (structs_count-- > 0) { - name = get_struct_name(struct_ptr, &length); - if ((name_length == length) && (memcmp(name, name_ptr, length) == 0)) + struct_name = get_struct_name(struct_ptr, &name_length); + if ((length == name_length) && (memcmp(name, struct_name, length) == 0)) { return struct_ptr; } @@ -346,8 +502,14 @@ const uint8_t *get_structn(const char *const name_ptr, apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } -// +/** + * Set struct name + * + * @param[in] length name length + * @param[in] name name + * @return whether it was successful or not + */ bool set_struct_name(uint8_t length, const uint8_t *const name) { uint8_t *length_ptr; @@ -387,7 +549,15 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) return true; } -static bool set_struct_field_custom(const uint8_t *const data, uint8_t *data_idx) +/** + * Set struct field custom typename + * + * @param[in] data the field data + * @param[in] data_idx the data index + * @return whether it was successful or not + */ +static bool set_struct_field_custom_typename(const uint8_t *const data, + uint8_t *data_idx) { uint8_t *typename_len_ptr; char *typename; @@ -411,6 +581,13 @@ static bool set_struct_field_custom(const uint8_t *const data, uint8_t *data_idx return true; } +/** + * Set struct field's array levels + * + * @param[in] data the field data + * @param[in] data_idx the data index + * @return whether it was successful or not + */ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) { uint8_t *array_levels_count; @@ -452,6 +629,13 @@ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) return true; } +/** + * Set struct field's type size + * + * @param[in] data the field data + * @param[in,out] data_idx the data index + * @return whether it was successful or not + */ static bool set_struct_field_typesize(const uint8_t *const data, uint8_t *data_idx) { uint8_t *type_size_ptr; @@ -466,6 +650,12 @@ static bool set_struct_field_typesize(const uint8_t *const data, uint8_t *data_i return true; } +/** + * Set struct field + * + * @param[in] data the field data + * @return whether it was successful or not + */ bool set_struct_field(const uint8_t *const data) { uint8_t data_idx = OFFSET_CDATA; @@ -499,7 +689,7 @@ bool set_struct_field(const uint8_t *const data) } else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) { - if (set_struct_field_custom(data, &data_idx) == false) + if (set_struct_field_custom_typename(data, &data_idx) == false) { return false; } diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index d8eb1ab..06bc7d1 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -55,9 +55,9 @@ const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length); e_array_type struct_field_array_depth(const uint8_t *ptr, uint8_t *const array_size); -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *ptr); +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const ptr); const uint8_t *struct_field_half_skip(const uint8_t *ptr); -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *ptr, +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const ptr, uint8_t *const length); const char *get_struct_field_keyname(const uint8_t *ptr, uint8_t *const length); diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index f5b19b7..54f3c2e 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -49,6 +49,15 @@ static bool ui_712_field_shown(void) return ret; } +/** + * Set UI buffer + * + * @param[in] src source buffer + * @param[in] src_length source buffer size + * @param[in] dst destination buffer + * @param[in] dst_length destination buffer length + * @param[in] explicit_trunc if truncation should be explicitely shown + */ static void ui_712_set_buf(const char *const src, size_t src_length, char *const dst, @@ -73,6 +82,9 @@ static void ui_712_set_buf(const char *const src, } } +/** + * Skip the field if needed and reset its UI flags + */ void ui_712_finalize_field(void) { if (!ui_712_field_shown()) @@ -104,6 +116,9 @@ void ui_712_set_value(const char *const str, uint8_t length) ui_712_set_buf(str, length, strings.tmp.tmp, sizeof(strings.tmp.tmp), true); } +/** + * 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 @@ -163,7 +178,7 @@ void ui_712_next_field(void) /** * Used to notify of a new struct to review * - * @param[in] struct_ptr pointer to the structure + * @param[in] struct_ptr pointer to the structure to be shown */ void ui_712_review_struct(const void *const struct_ptr) { @@ -184,6 +199,9 @@ void ui_712_review_struct(const void *const struct_ptr) ui_712_redraw_generic_step(); } +/** + * Show the hash of the message on the generic UI step + */ void ui_712_message_hash(void) { const char *const title = "Message hash"; @@ -197,6 +215,13 @@ void ui_712_message_hash(void) ui_712_redraw_generic_step(); } +/** + * Format given data as a string representation of a boolean + * + * @param[in] data the data that needs formatting + * @param[in] length its length + * @return if the formatting was successful + */ static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) { if (length != 1) @@ -215,6 +240,12 @@ static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) return true; } +/** + * Format given data as a string representation of bytes + * + * @param[in] data the data that needs formatting + * @param[in] length its length + */ static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) { snprintf(strings.tmp.tmp, @@ -231,6 +262,13 @@ static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) } } +/** + * Format given data as a string representation of an integer + * + * @param[in] data the data that needs formatting + * @param[in] length its length + * @return if the formatting was successful + */ static bool ui_712_format_int(const uint8_t *const data, uint8_t length, const void *const field_ptr) @@ -290,6 +328,12 @@ static bool ui_712_format_int(const uint8_t *const data, return true; } +/** + * Format given data as a string representation of an unsigned integer + * + * @param[in] data the data that needs formatting + * @param[in] length its length + */ static void ui_712_format_uint(const uint8_t *const data, uint8_t length) { uint256_t value256; @@ -435,6 +479,7 @@ unsigned int ui_712_approve(const bagl_element_t *e) /** * Reject button handling, calls the common handler function then * deinitializes the EIP712 context altogether. + * @param[in] e unused here, just needed to match the UI function signature * @return unused here, just needed to match the UI function signature */ @@ -445,6 +490,12 @@ unsigned int ui_712_reject(const bagl_element_t *e) return 0; } +/** + * Set a structure field's UI flags + * + * @param[in] show if this field should be shown on the device + * @param[in] name_provided if a substitution name has been provided + */ void ui_712_flag_field(bool show, bool name_provided) { if (show) @@ -457,21 +508,39 @@ void ui_712_flag_field(bool show, bool name_provided) } } +/** + * Set the UI filtering mode + * + * @param[in] the new filtering mode + */ void ui_712_set_filtering_mode(e_eip712_filtering_mode mode) { ui_ctx->filtering_mode = mode; } +/** + * Get the UI filtering mode + * + * @return current filtering mode + */ e_eip712_filtering_mode ui_712_get_filtering_mode(void) { return ui_ctx->filtering_mode; } +/** + * Reset all the UI struct field flags + */ void ui_712_field_flags_reset(void) { ui_ctx->field_flags = 0; } +/** + * Add a struct to the UI review queue + * + * Makes it so the user will have to go through a "Review struct" screen + */ void ui_712_queue_struct_to_review(void) { if (N_storage.verbose_eip712) From b4fe42cd785e1cf000edcc3d78ee006148f1dbcc Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 13 Jul 2022 18:33:29 +0200 Subject: [PATCH 130/184] EIP712 STRUCT FIELD DEF now checks the APDU payload bounds --- src_features/signMessageEIP712/commands_712.c | 2 +- src_features/signMessageEIP712/typed_data.c | 178 ++++++++++++++---- src_features/signMessageEIP712/typed_data.h | 2 +- 3 files changed, 142 insertions(+), 40 deletions(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 008c4bc..78e6067 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -61,7 +61,7 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) ret = set_struct_name(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; case P2_FIELD: - ret = set_struct_field(apdu_buf); + ret = set_struct_field(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 54c4838..7b10b08 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -14,7 +14,7 @@ static s_typed_data *typed_data = NULL; /** * Initialize the typed data context * - * @return whether the memory allocation was successful or not + * @return whether the memory allocation was successful */ bool typed_data_init(void) { @@ -185,7 +185,7 @@ static inline typedesc_t get_struct_field_typedesc(const uint8_t *const field_pt * Check whether a struct field is an array * * @param[in] field_ptr struct field pointer - * @return bool whether it is the case or not + * @return bool whether it is the case */ bool struct_field_is_array(const uint8_t *const field_ptr) { @@ -196,7 +196,7 @@ bool struct_field_is_array(const uint8_t *const field_ptr) * Check whether a struct field has a type size associated to it * * @param[in] field_ptr struct field pointer - * @return bool whether it is the case or not + * @return bool whether it is the case */ bool struct_field_has_typesize(const uint8_t *const field_ptr) { @@ -508,7 +508,7 @@ const uint8_t *get_structn(const char *const name, * * @param[in] length name length * @param[in] name name - * @return whether it was successful or not + * @return whether it was successful */ bool set_struct_name(uint8_t length, const uint8_t *const name) { @@ -549,20 +549,54 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) return true; } +/** + * Set struct field TypeDesc + * + * @param[in] data the field data + * @param[in] data_idx the data index + * @return pointer to the TypeDesc in memory + */ +static const typedesc_t *set_struct_field_typedesc(const uint8_t *const data, + uint8_t *data_idx, + uint8_t length) +{ + typedesc_t *typedesc_ptr; + + // copy TypeDesc + if ((*data_idx + sizeof(*typedesc_ptr)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + if ((typedesc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return NULL; + } + *typedesc_ptr = data[(*data_idx)++]; + return typedesc_ptr; +} + /** * Set struct field custom typename * * @param[in] data the field data * @param[in] data_idx the data index - * @return whether it was successful or not + * @return whether it was successful */ static bool set_struct_field_custom_typename(const uint8_t *const data, - uint8_t *data_idx) + uint8_t *data_idx, + uint8_t length) { uint8_t *typename_len_ptr; char *typename; // copy custom struct name length + if ((*data_idx + sizeof(*typename_len_ptr)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -571,6 +605,11 @@ static bool set_struct_field_custom_typename(const uint8_t *const data, *typename_len_ptr = data[(*data_idx)++]; // copy name + if ((*data_idx + *typename_len_ptr) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -586,14 +625,21 @@ static bool set_struct_field_custom_typename(const uint8_t *const data, * * @param[in] data the field data * @param[in] data_idx the data index - * @return whether it was successful or not + * @return whether it was successful */ -static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) +static bool set_struct_field_array(const uint8_t *const data, + uint8_t *data_idx, + uint8_t length) { uint8_t *array_levels_count; e_array_type *array_level; uint8_t *array_level_size; + if ((*data_idx + sizeof(*array_levels_count)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -602,6 +648,11 @@ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) *array_levels_count = data[(*data_idx)++]; for (int idx = 0; idx < *array_levels_count; ++idx) { + if ((*data_idx + sizeof(*array_level)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -613,6 +664,11 @@ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) case ARRAY_DYNAMIC: // nothing to do break; case ARRAY_FIXED_SIZE: + if ((*data_idx + sizeof(*array_level_size)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -634,36 +690,90 @@ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx) * * @param[in] data the field data * @param[in,out] data_idx the data index - * @return whether it was successful or not + * @return whether it was successful */ -static bool set_struct_field_typesize(const uint8_t *const data, uint8_t *data_idx) +static bool set_struct_field_typesize(const uint8_t *const data, + uint8_t *data_idx, + uint8_t length) { - uint8_t *type_size_ptr; + uint8_t *typesize_ptr; // copy TypeSize - if ((type_size_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + if ((*data_idx + sizeof(*typesize_ptr)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + if ((typesize_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *type_size_ptr = data[(*data_idx)++]; + *typesize_ptr = data[(*data_idx)++]; + return true; +} + +/** + * Set struct field's key name + * + * @param[in] data the field data + * @param[in,out] data_idx the data index + * @return whether it was successful + */ +static bool set_struct_field_keyname(const uint8_t *const data, + uint8_t *data_idx, + uint8_t length) +{ + uint8_t *keyname_len_ptr; + char *keyname_ptr; + + // copy length + if ((*data_idx + sizeof(*keyname_len_ptr)) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + if ((keyname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + *keyname_len_ptr = data[(*data_idx)++]; + + // copy name + if ((*data_idx + *keyname_len_ptr) > length) // check buffer bound + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + if ((keyname_ptr = mem_alloc(sizeof(char) * *keyname_len_ptr)) == NULL) + { + apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + return false; + } + memmove(keyname_ptr, &data[*data_idx], *keyname_len_ptr); + *data_idx += *keyname_len_ptr; return true; } /** * Set struct field * + * @param[in] length data length * @param[in] data the field data - * @return whether it was successful or not + * @return whether it was successful */ -bool set_struct_field(const uint8_t *const data) +bool set_struct_field(uint8_t length, const uint8_t *const data) { - uint8_t data_idx = OFFSET_CDATA; - uint8_t *type_desc_ptr; - uint8_t *fieldname_len_ptr; - char *fieldname_ptr; + const typedesc_t *typedesc_ptr; + uint8_t data_idx = 0; - if ((data == NULL) || (typed_data == NULL)) + if ((data == NULL) || (length == 0)) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + else if (typed_data == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; @@ -671,52 +781,44 @@ bool set_struct_field(const uint8_t *const data) // increment number of struct fields *(typed_data->current_struct_fields_array) += 1; - // copy TypeDesc - if ((type_desc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + if ((typedesc_ptr = set_struct_field_typedesc(data, &data_idx, length)) == NULL) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *type_desc_ptr = data[data_idx++]; // check TypeSize flag in TypeDesc - if (*type_desc_ptr & TYPESIZE_MASK) + if (*typedesc_ptr & TYPESIZE_MASK) { - if (set_struct_field_typesize(data, &data_idx) == false) + if (set_struct_field_typesize(data, &data_idx, length) == false) { return false; } } - else if ((*type_desc_ptr & TYPE_MASK) == TYPE_CUSTOM) + else if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) { - if (set_struct_field_custom_typename(data, &data_idx) == false) + if (set_struct_field_custom_typename(data, &data_idx, length) == false) { return false; } } - if (*type_desc_ptr & ARRAY_MASK) + if (*typedesc_ptr & ARRAY_MASK) { - if (set_struct_field_array(data, &data_idx) == false) + if (set_struct_field_array(data, &data_idx, length) == false) { return false; } } - // copy length - if ((fieldname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) + if (set_struct_field_keyname(data, &data_idx, length) == false) { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - *fieldname_len_ptr = data[data_idx++]; - // copy name - if ((fieldname_ptr = mem_alloc(sizeof(char) * *fieldname_len_ptr)) == NULL) + if (data_idx != length) // check that there is no more { - apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - memmove(fieldname_ptr, &data[data_idx], *fieldname_len_ptr); return true; } diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index 06bc7d1..f8daa54 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -70,7 +70,7 @@ const uint8_t *get_structs_array(uint8_t *const length); const uint8_t *get_structn(const char *const name_ptr, const uint8_t name_length); bool set_struct_name(uint8_t length, const uint8_t *const name); -bool set_struct_field(const uint8_t *const data); +bool set_struct_field(uint8_t length, const uint8_t *const data); bool typed_data_init(void); void typed_data_deinit(void); From 38f199e46b6cf88d32614bb41d1d4ac2d2e32052 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 13 Jul 2022 18:47:36 +0200 Subject: [PATCH 131/184] Now only writes to EIP-712 UI buffers data that should be show Prevents the case if the last field of a message is supposed to be hidden and after reaching the Approve/Reject screens the user goes back to see the last field, he would see the actual last field that should be hidden instead of the last one he saw beforehand. --- src_features/signMessageEIP712/ui_logic.c | 141 ++++++++++++++++------ 1 file changed, 102 insertions(+), 39 deletions(-) diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 54f3c2e..6440bee 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -215,6 +215,45 @@ void ui_712_message_hash(void) ui_712_redraw_generic_step(); } +/** + * Format a given data as a string + * + * @param[in] data the data that needs formatting + * @param[in] length its length + */ +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); + } +} + +/** + * Format a given data as a string representation of an address + * + * @param[in] data the data that needs formatting + * @param[in] length its length + * @return if the formatting was successful + */ +static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) +{ + if (length != ADDRESS_LENGTH) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + if (ui_712_field_shown()) + { + getEthDisplayableAddress((uint8_t*)data, + strings.tmp.tmp, + sizeof(strings.tmp.tmp), + &global_sha3, + chainConfig->chainId); + } + return true; +} + /** * Format given data as a string representation of a boolean * @@ -224,18 +263,19 @@ void ui_712_message_hash(void) */ 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"; + const char *str; + if (length != 1) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if (*data) + str = *data ? true_str : false_str; + if (ui_712_field_shown()) { - ui_712_set_value("true", 4); - } - else - { - ui_712_set_value("false", 5); + ui_712_set_value(str, strlen(str)); } return true; } @@ -248,17 +288,20 @@ static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) */ static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) { - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - length, - data); - // +2 for the "0x" - // x2 for each byte value is represented by 2 ASCII characters - if ((2 + (length * 2)) > (sizeof(strings.tmp.tmp) - 1)) + if (ui_712_field_shown()) { - strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - 3] = '\0'; - strcat(strings.tmp.tmp, "..."); + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + length, + data); + // +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'; + strcat(strings.tmp.tmp, "..."); + } } } @@ -282,15 +325,24 @@ static bool ui_712_format_int(const uint8_t *const data, { case 256: convertUint256BE(data, length, &value256); - tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + if (ui_712_field_shown()) + { + tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + } break; case 128: convertUint128BE(data, length, &value128); - tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + if (ui_712_field_shown()) + { + tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + } break; case 64: convertUint64BEto128(data, length, &value128); - tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + if (ui_712_field_shown()) + { + tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + } break; case 32: value32 = 0; @@ -298,10 +350,13 @@ static bool ui_712_format_int(const uint8_t *const data, { ((uint8_t*)&value32)[length - 1 - i] = data[i]; } - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%d", - value32); + if (ui_712_field_shown()) + { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + value32); + } break; case 16: value16 = 0; @@ -309,16 +364,22 @@ static bool ui_712_format_int(const uint8_t *const data, { ((uint8_t*)&value16)[length - 1 - i] = data[i]; } - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%d", - value16); // expanded to 32 bits + if (ui_712_field_shown()) + { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + value16); // expanded to 32 bits + } break; case 8: - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "%d", - ((int8_t*)data)[0]); // expanded to 32 bits + if (ui_712_field_shown()) + { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "%d", + ((int8_t*)data)[0]); // expanded to 32 bits + } break; default: PRINTF("Unhandled field typesize\n"); @@ -339,7 +400,10 @@ static void ui_712_format_uint(const uint8_t *const data, uint8_t length) uint256_t value256; convertUint256BE(data, length, &value256); - tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + if (ui_712_field_shown()) + { + tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); + } } /** @@ -367,7 +431,7 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, return false; } - if (!(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) + if (ui_712_field_shown() && !(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) { ui_712_set_title(key, key_len); } @@ -376,14 +440,13 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, switch (struct_field_type(field_ptr)) { case TYPE_SOL_STRING: - ui_712_set_value((char*)data, length); + ui_712_format_str(data, length); break; case TYPE_SOL_ADDRESS: - getEthDisplayableAddress((uint8_t*)data, - strings.tmp.tmp, - sizeof(strings.tmp.tmp), - &global_sha3, - chainConfig->chainId); + if (ui_712_format_addr(data, length) == false) + { + return false; + } break; case TYPE_SOL_BOOL: if (ui_712_format_bool(data, length) == false) From 5e5b3c362101ea306a3b16a5caa778b25caa1864 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 18 Jul 2022 11:31:46 +0200 Subject: [PATCH 132/184] EIP712 STRUCT FIELD IMPL now checks the APDU payload bounds --- src_features/signMessageEIP712/commands_712.c | 3 ++- src_features/signMessageEIP712/field_hash.c | 5 ++++ src_features/signMessageEIP712/path.c | 25 +++++++++++-------- src_features/signMessageEIP712/path.h | 3 ++- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 78e6067..aebc8cb 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -116,7 +116,8 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) } break; case P2_ARRAY: - ret = path_new_array_depth(apdu_buf[OFFSET_CDATA]); + ret = path_new_array_depth(&apdu_buf[OFFSET_CDATA], + apdu_buf[OFFSET_LC]); break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 1cec18e..211d175 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -296,6 +296,11 @@ bool field_hash(const uint8_t *data, { data = field_hash_prepare(field_ptr, data, &data_length); } + if (data_length > fh->remaining_size) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } fh->remaining_size -= data_length; // if a dynamic type -> continue progressive hash if (IS_DYN(field_type)) diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 746dda0..eda4076 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -442,19 +442,14 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) * @return whether the checks and add were successful or not */ static bool check_and_add_array_depth(const void *depth, - uint8_t total_count, - uint8_t pidx, - uint8_t size) + uint8_t total_count, + uint8_t pidx, + uint8_t size) { uint8_t expected_size; uint8_t arr_idx; e_array_type expected_type; - if (path_struct == NULL) - { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return false; - } arr_idx = (total_count - path_struct->array_depth_count) - 1; // we skip index 0, since we already have it for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) @@ -483,10 +478,12 @@ static bool check_and_add_array_depth(const void *depth, /** * Add a new array depth with a given size (number of elements). * - * @param[in] size number of elements + * @param[in] data pointer to the number of elements + * @param[in] length length of data * @return whether the add was successful or not */ -bool path_new_array_depth(uint8_t size) +bool path_new_array_depth(const uint8_t *const data, + uint8_t length) { const void *field_ptr = NULL; const void *depth = NULL; @@ -497,6 +494,12 @@ bool path_new_array_depth(uint8_t size) if (path_struct == NULL) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + else if (length != 1) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } @@ -517,7 +520,7 @@ bool path_new_array_depth(uint8_t size) total_count += depth_count; if (total_count > path_struct->array_depth_count) { - if (!check_and_add_array_depth(depth, total_count, pidx, size)) + if (!check_and_add_array_depth(depth, total_count, pidx, *data)) { return false; } diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index cf21c8c..0efa70a 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -36,7 +36,8 @@ const void *path_get_field(void); bool path_advance(void); bool path_init(void); void path_deinit(void); -bool path_new_array_depth(uint8_t size); +bool path_new_array_depth(const uint8_t *const data, + uint8_t length); e_root_type path_get_root_type(void); const void *path_get_root(void); const void *path_get_nth_field(uint8_t n); From 0cf21cdf73917e90e978842b4482285e838f8580 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 18 Jul 2022 16:18:19 +0200 Subject: [PATCH 133/184] Better EIP-712 array error-handling on APDU parsing --- src_features/signMessageEIP712/typed_data.c | 5 +++++ src_features/signMessageEIP712/typed_data.h | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 7b10b08..1589994 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -659,6 +659,11 @@ static bool set_struct_field_array(const uint8_t *const data, return false; } *array_level = data[(*data_idx)++]; + if (*array_level > ARRAY_TYPES_COUNT) + { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } switch (*array_level) { case ARRAY_DYNAMIC: // nothing to do diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index f8daa54..dd431f3 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -16,7 +16,8 @@ typedef enum { ARRAY_DYNAMIC = 0, - ARRAY_FIXED_SIZE + ARRAY_FIXED_SIZE, + ARRAY_TYPES_COUNT } e_array_type; typedef enum From de9e895ad96b6043df7cd492b86c88c2b4af4d92 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 19 Jul 2022 11:49:18 +0200 Subject: [PATCH 134/184] EIP-712 code linting --- src/apdu_constants.h | 24 +- src/main.c | 13 +- src/shared_context.h | 2 +- src/ui_flow.c | 28 +- src/utils.h | 9 +- src_common/mem.c | 27 +- src_common/mem.h | 12 +- src_common/mem_utils.c | 34 +- src_common/mem_utils.h | 10 +- src_common/uint128.c | 2 +- src_common/uint256.c | 4 +- src_common/uint256.h | 1 - src_common/uint_common.h | 2 +- src_features/signMessageEIP712/commands_712.c | 119 +++---- src_features/signMessageEIP712/commands_712.h | 36 +- src_features/signMessageEIP712/context.c | 29 +- src_features/signMessageEIP712/context.h | 15 +- src_features/signMessageEIP712/encode_field.c | 54 +-- src_features/signMessageEIP712/encode_field.h | 16 +- src_features/signMessageEIP712/field_hash.c | 142 +++----- src_features/signMessageEIP712/field_hash.h | 25 +- src_features/signMessageEIP712/filtering.c | 128 +++---- src_features/signMessageEIP712/filtering.h | 14 +- .../format_hash_field_type.c | 48 +-- .../format_hash_field_type.h | 6 +- src_features/signMessageEIP712/hash_bytes.c | 15 +- src_features/signMessageEIP712/hash_bytes.h | 4 +- src_features/signMessageEIP712/path.c | 336 ++++++------------ src_features/signMessageEIP712/path.h | 35 +- src_features/signMessageEIP712/schema_hash.c | 46 ++- src_features/signMessageEIP712/schema_hash.h | 6 +- .../signMessageEIP712/sol_typenames.c | 82 ++--- .../signMessageEIP712/sol_typenames.h | 7 +- src_features/signMessageEIP712/type_hash.c | 105 ++---- src_features/signMessageEIP712/type_hash.h | 8 +- src_features/signMessageEIP712/typed_data.c | 323 ++++++----------- src_features/signMessageEIP712/typed_data.h | 65 ++-- src_features/signMessageEIP712/ui_flow_712.c | 4 +- src_features/signMessageEIP712/ui_flow_712.h | 4 +- src_features/signMessageEIP712/ui_logic.c | 275 +++++--------- src_features/signMessageEIP712/ui_logic.h | 39 +- .../signMessageEIP712_common/common_712.c | 16 +- .../signMessageEIP712_common/common_712.h | 2 +- .../signMessageEIP712_v0/cmd_signMessage712.c | 8 +- 44 files changed, 778 insertions(+), 1402 deletions(-) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 5f1cbf4..19111b3 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -34,14 +34,13 @@ #define COMMON_CLA 0xB0 #define COMMON_INS_GET_WALLET_ID 0x04 -#define APDU_RESPONSE_OK 0x9000 -#define APDU_RESPONSE_INVALID_DATA 0x6a80 -#define APDU_RESPONSE_INSUFFICIENT_MEMORY 0x6a84 -#define APDU_RESPONSE_INVALID_INS 0x6d00 -#define APDU_RESPONSE_INVALID_P1_P2 0x6b00 -#define APDU_RESPONSE_CONDITION_NOT_SATISFIED 0x6985 -#define APDU_RESPONSE_REF_DATA_NOT_USABLE 0x6a88 - +#define APDU_RESPONSE_OK 0x9000 +#define APDU_RESPONSE_INVALID_DATA 0x6a80 +#define APDU_RESPONSE_INSUFFICIENT_MEMORY 0x6a84 +#define APDU_RESPONSE_INVALID_INS 0x6d00 +#define APDU_RESPONSE_INVALID_P1_P2 0x6b00 +#define APDU_RESPONSE_CONDITION_NOT_SATISFIED 0x6985 +#define APDU_RESPONSE_REF_DATA_NOT_USABLE 0x6a88 #ifdef HAVE_STARKWARE @@ -63,14 +62,7 @@ #endif -enum { - OFFSET_CLA = 0, - OFFSET_INS, - OFFSET_P1, - OFFSET_P2, - OFFSET_LC, - OFFSET_CDATA -}; +enum { OFFSET_CLA = 0, OFFSET_INS, OFFSET_P1, OFFSET_P2, OFFSET_LC, OFFSET_CDATA }; void handleGetPublicKey(uint8_t p1, uint8_t p2, diff --git a/src/main.c b/src/main.c index 85417e3..e9c3505 100644 --- a/src/main.c +++ b/src/main.c @@ -71,7 +71,7 @@ const internalStorage_t N_storage_real; chain_config_t *chainConfig; void reset_app_context() { - //PRINTF("!!RESET_APP_CONTEXT\n"); + // PRINTF("!!RESET_APP_CONTEXT\n"); appState = APP_STATE_IDLE; called_from_swap = false; pluginType = OLD_INTERNAL; @@ -664,8 +664,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { break; case INS_SIGN_EIP_712_MESSAGE: - if (G_io_apdu_buffer[OFFSET_P2] == 0) - { + if (G_io_apdu_buffer[OFFSET_P2] == 0) { memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], G_io_apdu_buffer[OFFSET_P2], @@ -673,15 +672,13 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { G_io_apdu_buffer[OFFSET_LC], flags, tx); - } - else - { + } else { #ifdef HAVE_EIP712_FULL_SUPPORT *flags |= IO_ASYNCH_REPLY; handle_eip712_sign(G_io_apdu_buffer); #else THROW(0x6B00); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT } break; @@ -723,7 +720,7 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { *flags |= IO_ASYNCH_REPLY; handle_eip712_filtering(G_io_apdu_buffer); break; -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT #if 0 case 0xFF: // return to dashboard diff --git a/src/shared_context.h b/src/shared_context.h index ef73920..38fa0da 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -29,7 +29,7 @@ typedef struct internalStorage_t { unsigned char displayNonce; #ifdef HAVE_EIP712_FULL_SUPPORT bool verbose_eip712; -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT uint8_t initialized; } internalStorage_t; diff --git a/src/ui_flow.c b/src/ui_flow.c index 0e441c4..a1bc93a 100644 --- a/src/ui_flow.c +++ b/src/ui_flow.c @@ -7,7 +7,7 @@ void switch_settings_display_data(void); void switch_settings_display_nonce(void); #ifdef HAVE_EIP712_FULL_SUPPORT void switch_settings_verbose_eip712(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT ////////////////////////////////////////////////////////////////////// // clang-format off @@ -148,27 +148,22 @@ UX_FLOW(ux_settings_flow, &ux_settings_flow_display_nonce_step, #ifdef HAVE_EIP712_FULL_SUPPORT &ux_settings_flow_verbose_eip712_step, -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT &ux_settings_flow_back_step); void display_settings(const ux_flow_step_t* const start_step) { - const char *const values[] = { - "Enabled", - "Disabled" - }; - bool settings[] = { - N_storage.dataAllowed, - N_storage.contractDetails, - N_storage.displayNonce, + const char* const values[] = {"Enabled", "Disabled"}; + bool settings[] = {N_storage.dataAllowed, + N_storage.contractDetails, + N_storage.displayNonce, #ifdef HAVE_EIP712_FULL_SUPPORT - N_storage.verbose_eip712 -#endif // HAVE_EIP712_FULL_SUPPORT + N_storage.verbose_eip712 +#endif // HAVE_EIP712_FULL_SUPPORT }; uint8_t offset = 0; uint8_t increment = MAX(strlen(values[0]), strlen(values[1])) + 1; - for (unsigned int i = 0; i < (sizeof(settings) / sizeof(settings[0])); ++i) - { + for (unsigned int i = 0; i < (sizeof(settings) / sizeof(settings[0])); ++i) { strlcpy(strings.common.fullAddress + offset, (settings[i] ? values[0] : values[1]), sizeof(strings.common.fullAddress) - offset); @@ -197,13 +192,12 @@ void switch_settings_display_nonce(void) { } #ifdef HAVE_EIP712_FULL_SUPPORT -void switch_settings_verbose_eip712(void) -{ +void switch_settings_verbose_eip712(void) { bool value = !N_storage.verbose_eip712; nvm_write((void*) &N_storage.verbose_eip712, (void*) &value, sizeof(value)); display_settings(&ux_settings_flow_verbose_eip712_step); } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT ////////////////////////////////////////////////////////////////////// // clang-format off diff --git a/src/utils.h b/src/utils.h index 3d772b8..504c1b1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -22,14 +22,13 @@ #include "uint256.h" - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) void array_hexstr(char* strbuf, const void* bin, unsigned int len); -void convertUint128BE(const uint8_t *const data, uint32_t length, uint128_t *const target); -void convertUint256BE(const uint8_t *const data, uint32_t length, uint256_t *const target); -void convertUint64BEto128(const uint8_t *const data, uint32_t length, uint128_t *const target); +void convertUint128BE(const uint8_t* const data, uint32_t length, uint128_t* const target); +void convertUint256BE(const uint8_t* const data, uint32_t length, uint256_t* const target); +void convertUint64BEto128(const uint8_t* const data, uint32_t length, uint128_t* const target); uint64_t u64_from_BE(const uint8_t* in, uint8_t size); diff --git a/src_common/mem.c b/src_common/mem.c index 291c4d7..7d03b34 100644 --- a/src_common/mem.c +++ b/src_common/mem.c @@ -5,23 +5,20 @@ #define SIZE_MEM_BUFFER 5120 -static uint8_t mem_buffer[SIZE_MEM_BUFFER]; -static size_t mem_idx; - +static uint8_t mem_buffer[SIZE_MEM_BUFFER]; +static size_t mem_idx; /** * Initializes the memory buffer index */ -void mem_init(void) -{ +void mem_init(void) { mem_idx = 0; } /** * Resets the memory buffer index */ -void mem_reset(void) -{ +void mem_reset(void) { mem_init(); } @@ -34,9 +31,8 @@ void mem_reset(void) * @param[in] size Requested allocation size in bytes * @return Allocated memory pointer; \ref NULL if not enough space left. */ -void *mem_alloc(size_t size) -{ - if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded +void *mem_alloc(size_t size) { + if ((mem_idx + size) > SIZE_MEM_BUFFER) // Buffer exceeded { return NULL; } @@ -49,16 +45,13 @@ void *mem_alloc(size_t size) * * @param[in] size Requested deallocation size in bytes */ -void mem_dealloc(size_t size) -{ - if (size > mem_idx) // More than is already allocated +void mem_dealloc(size_t size) { + if (size > mem_idx) // More than is already allocated { mem_idx = 0; - } - else - { + } else { mem_idx -= size; } } -#endif // HAVE_DYN_MEM_ALLOC +#endif // HAVE_DYN_MEM_ALLOC diff --git a/src_common/mem.h b/src_common/mem.h index e885137..eafa797 100644 --- a/src_common/mem.h +++ b/src_common/mem.h @@ -5,11 +5,11 @@ #include -void mem_init(void); -void mem_reset(void); -void *mem_alloc(size_t size); -void mem_dealloc(size_t size); +void mem_init(void); +void mem_reset(void); +void *mem_alloc(size_t size); +void mem_dealloc(size_t size); -#endif // HAVE_DYN_MEM_ALLOC +#endif // HAVE_DYN_MEM_ALLOC -#endif // MEM_H_ +#endif // MEM_H_ diff --git a/src_common/mem_utils.c b/src_common/mem_utils.c index e49a321..b7f58ad 100644 --- a/src_common/mem_utils.c +++ b/src_common/mem_utils.c @@ -14,26 +14,22 @@ * * @return pointer to memory area or \ref NULL if the allocation failed */ -char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const length) -{ - char *mem_ptr; - uint32_t value_copy; - uint8_t size; +char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const length) { + char *mem_ptr; + uint32_t value_copy; + uint8_t size; - size = 1; // minimum size, even if 0 + size = 1; // minimum size, even if 0 value_copy = value; - while (value_copy >= 10) - { + while (value_copy >= 10) { value_copy /= 10; size += 1; } // +1 for the null character - if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1)))) - { + if ((mem_ptr = mem_alloc(sizeof(char) * (size + 1)))) { snprintf(mem_ptr, (size + 1), "%u", value); - mem_dealloc(sizeof(char)); // to skip the null character - if (length != NULL) - { + mem_dealloc(sizeof(char)); // to skip the null character + if (length != NULL) { *length = size; } } @@ -49,18 +45,16 @@ char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const length) * * @return pointer to the memory area, \ref NULL if the allocation failed */ -void *mem_alloc_and_align(size_t size, size_t alignment) -{ - uint8_t align_diff = (uintptr_t)mem_alloc(0) % alignment; +void *mem_alloc_and_align(size_t size, size_t alignment) { + uint8_t align_diff = (uintptr_t) mem_alloc(0) % alignment; - if (align_diff > 0) // alignment needed + if (align_diff > 0) // alignment needed { - if (mem_alloc(alignment - align_diff) == NULL) - { + if (mem_alloc(alignment - align_diff) == NULL) { return NULL; } } return mem_alloc(size); } -#endif // HAVE_DYN_MEM_ALLOC +#endif // HAVE_DYN_MEM_ALLOC diff --git a/src_common/mem_utils.h b/src_common/mem_utils.h index 1633d97..c6440e1 100644 --- a/src_common/mem_utils.h +++ b/src_common/mem_utils.h @@ -6,11 +6,11 @@ #include #include -#define MEM_ALLOC_AND_ALIGN_TYPE(type) mem_alloc_and_align(sizeof(type), __alignof__(type)) +#define MEM_ALLOC_AND_ALIGN_TYPE(type) mem_alloc_and_align(sizeof(type), __alignof__(type)) -char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); -void *mem_alloc_and_align(size_t size, size_t alignment); +char *mem_alloc_and_format_uint(uint32_t value, uint8_t *const written_chars); +void *mem_alloc_and_align(size_t size, size_t alignment); -#endif // HAVE_DYN_MEM_ALLOC +#endif // HAVE_DYN_MEM_ALLOC -#endif // MEM_UTILS_H_ +#endif // MEM_UTILS_H_ diff --git a/src_common/uint128.c b/src_common/uint128.c index 79cf2ac..0b1fe86 100644 --- a/src_common/uint128.c +++ b/src_common/uint128.c @@ -21,7 +21,7 @@ #include #include "uint128.h" #include "uint_common.h" -#include "ethUtils.h" // HEXDIGITS +#include "ethUtils.h" // HEXDIGITS void readu128BE(const uint8_t *const buffer, uint128_t *const target) { UPPER_P(target) = readUint64BE(buffer); diff --git a/src_common/uint256.c b/src_common/uint256.c index 54b165b..9da483d 100644 --- a/src_common/uint256.c +++ b/src_common/uint256.c @@ -21,8 +21,8 @@ #include #include "uint256.h" #include "uint_common.h" -#include "ethUstream.h" // INT256_LENGTH -#include "ethUtils.h" // HEXDIGITS +#include "ethUstream.h" // INT256_LENGTH +#include "ethUtils.h" // HEXDIGITS void readu256BE(const uint8_t *const buffer, uint256_t *const target) { readu128BE(buffer, &UPPER_P(target)); diff --git a/src_common/uint256.h b/src_common/uint256.h index 351953b..97b8cfb 100644 --- a/src_common/uint256.h +++ b/src_common/uint256.h @@ -58,5 +58,4 @@ bool tostring256_signed(const uint256_t *const number, char *const out, uint32_t out_length); - #endif // _UINT256_H_ diff --git a/src_common/uint_common.h b/src_common/uint_common.h index 4a22171..c12d9e8 100644 --- a/src_common/uint_common.h +++ b/src_common/uint_common.h @@ -32,4 +32,4 @@ void read_u64_be(const uint8_t *const in, uint64_t *const out); uint64_t readUint64BE(const uint8_t *const buffer); void reverseString(char *const str, uint32_t length); -#endif //_UINT_COMMON_H_ +#endif //_UINT_COMMON_H_ diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index aebc8cb..deb9bba 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -4,7 +4,7 @@ #include #include #include "commands_712.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "context.h" #include "field_hash.h" #include "path.h" @@ -22,19 +22,16 @@ * * @param[in] success whether the command was successful */ -void handle_eip712_return_code(bool success) -{ - if (success) - { +void handle_eip712_return_code(bool success) { + if (success) { apdu_response_code = APDU_RESPONSE_OK; } - *(uint16_t*)G_io_apdu_buffer = __builtin_bswap16(apdu_response_code); + *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(apdu_response_code); // Send back the response, do not restart the event loop io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); - if (!success) - { + if (!success) { eip712_context_deinit(); } } @@ -45,18 +42,14 @@ void handle_eip712_return_code(bool success) * @param[in] apdu_buf the APDU payload * @return whether the command was successful or not */ -bool handle_eip712_struct_def(const uint8_t *const apdu_buf) -{ +bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { bool ret = true; - if (eip712_context == NULL) - { + if (eip712_context == NULL) { ret = eip712_context_init(); } - if (ret) - { - switch (apdu_buf[OFFSET_P2]) - { + if (ret) { + switch (apdu_buf[OFFSET_P2]) { case P2_NAME: ret = set_struct_name(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; @@ -65,8 +58,8 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P2], - apdu_buf[OFFSET_INS]); + apdu_buf[OFFSET_P2], + apdu_buf[OFFSET_INS]); apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; ret = false; } @@ -81,26 +74,18 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) * @param[in] apdu_buf the APDU payload * @return whether the command was successful or not */ -bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) -{ +bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { bool ret = false; bool reply_apdu = true; - if (eip712_context == NULL) - { + if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - } - else - { - switch (apdu_buf[OFFSET_P2]) - { + } else { + switch (apdu_buf[OFFSET_P2]) { case P2_NAME: // set root type - if ((ret = path_set_root((char*)&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]))) - { - if (N_storage.verbose_eip712) - { + if ((ret = path_set_root((char *) &apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]))) { + if (N_storage.verbose_eip712) { ui_712_review_struct(path_get_root()); reply_apdu = false; } @@ -110,14 +95,12 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) case P2_FIELD: if ((ret = field_hash(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC], - apdu_buf[OFFSET_P1] != P1_COMPLETE))) - { + apdu_buf[OFFSET_P1] != P1_COMPLETE))) { reply_apdu = false; } break; case P2_ARRAY: - ret = path_new_array_depth(&apdu_buf[OFFSET_CDATA], - apdu_buf[OFFSET_LC]); + ret = path_new_array_depth(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); break; default: PRINTF("Unknown P2 0x%x for APDU 0x%x\n", @@ -126,8 +109,7 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; } } - if (reply_apdu) - { + if (reply_apdu) { handle_eip712_return_code(ret); } return ret; @@ -139,54 +121,43 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) * @param[in] apdu_buf the APDU payload * @return whether the command was successful or not */ -bool handle_eip712_filtering(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) - { + if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; ret = false; - } - else - { - switch (apdu_buf[OFFSET_P1]) - { + } else { + switch (apdu_buf[OFFSET_P1]) { case P1_ACTIVATE: - if (!N_storage.verbose_eip712) - { + if (!N_storage.verbose_eip712) { ui_712_set_filtering_mode(EIP712_FILTERING_FULL); ret = compute_schema_hash(); } break; case P1_CONTRACT_NAME: case P1_FIELD_NAME: - type = (apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) - ? FILTERING_CONTRACT_NAME - : FILTERING_STRUCT_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_P1] == P1_CONTRACT_NAME) && ret) - { + type = (apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) ? FILTERING_CONTRACT_NAME + : FILTERING_STRUCT_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_P1] == P1_CONTRACT_NAME) && ret) { reply_apdu = false; } } break; default: PRINTF("Unknown P1 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P1], - apdu_buf[OFFSET_INS]); + apdu_buf[OFFSET_P1], + apdu_buf[OFFSET_INS]); apdu_response_code = APDU_RESPONSE_INVALID_P1_P2; ret = false; } } - if (reply_apdu) - { + if (reply_apdu) { handle_eip712_return_code(ret); } return ret; @@ -198,32 +169,24 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) * @param[in] apdu_buf the APDU payload * @return whether the command was successful or not */ -bool handle_eip712_sign(const uint8_t *const apdu_buf) -{ +bool handle_eip712_sign(const uint8_t *const apdu_buf) { bool ret = false; uint8_t length = apdu_buf[OFFSET_LC]; - if (eip712_context == NULL) - { + if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - } - else if (parseBip32(&apdu_buf[OFFSET_CDATA], - &length, - &tmpCtx.messageSigningContext.bip32) != NULL) - { - if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) - { + } else if (parseBip32(&apdu_buf[OFFSET_CDATA], &length, &tmpCtx.messageSigningContext.bip32) != + NULL) { + if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) { ui_712_message_hash(); } ret = true; ui_712_end_sign(); } - if (!ret) - { + if (!ret) { handle_eip712_return_code(ret); } return ret; } - -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index 33e7509..a5485a0 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -7,27 +7,27 @@ #include // APDUs P1 -#define P1_COMPLETE 0x00 -#define P1_PARTIAL 0xFF -#define P1_ACTIVATE 0x00 -#define P1_CONTRACT_NAME 0x0F -#define P1_FIELD_NAME 0xFF +#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 +#define P2_NAME 0x00 +#define P2_ARRAY 0x0F +#define P2_FIELD 0xFF +#define P2_KEY 0x00 +#define P2_VALUE 0xFF -#define DOMAIN_STRUCT_NAME "EIP712Domain" +#define DOMAIN_STRUCT_NAME "EIP712Domain" -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); -void handle_eip712_return_code(bool success); +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); +void handle_eip712_return_code(bool success); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // EIP712_H_ +#endif // EIP712_H_ diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index b03c554..38730cb 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -10,9 +10,9 @@ #include "field_hash.h" #include "ui_logic.h" #include "typed_data.h" -#include "apdu_constants.h" // APDU response codes -#include "shared_context.h" // reset_app_context -#include "ui_callbacks.h" // ui_idle +#include "apdu_constants.h" // APDU response codes +#include "shared_context.h" // reset_app_context +#include "ui_callbacks.h" // ui_idle s_eip712_context *eip712_context = NULL; @@ -21,38 +21,32 @@ s_eip712_context *eip712_context = NULL; * * @return a boolean indicating if the initialization was successful or not */ -bool eip712_context_init(void) -{ +bool eip712_context_init(void) { // init global variables mem_init(); - if ((eip712_context = MEM_ALLOC_AND_ALIGN_TYPE(*eip712_context)) == NULL) - { + if ((eip712_context = MEM_ALLOC_AND_ALIGN_TYPE(*eip712_context)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - if (sol_typenames_init() == false) - { + if (sol_typenames_init() == false) { return false; } - if (path_init() == false) - { + if (path_init() == false) { return false; } - if (field_hash_init() == false) - { + if (field_hash_init() == false) { return false; } - if (ui_712_init() == false) - { + if (ui_712_init() == false) { return false; } - if (typed_data_init() == false) // this needs to be initialized last ! + if (typed_data_init() == false) // this needs to be initialized last ! { return false; } @@ -63,8 +57,7 @@ bool eip712_context_init(void) /** * De-initialize the EIP712 context */ -void eip712_context_deinit(void) -{ +void eip712_context_deinit(void) { typed_data_deinit(); path_deinit(); field_hash_deinit(); diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 0d08c5b..97e947f 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -4,19 +4,18 @@ #ifdef HAVE_EIP712_FULL_SUPPORT #include -#include "ethUstream.h" // ADDRESS_LENGTH +#include "ethUstream.h" // ADDRESS_LENGTH -typedef struct -{ +typedef struct { uint8_t contract_addr[ADDRESS_LENGTH]; uint8_t schema_hash[224 / 8]; -} s_eip712_context; +} s_eip712_context; extern s_eip712_context *eip712_context; -bool eip712_context_init(void); -void eip712_context_deinit(void); +bool eip712_context_init(void); +void eip712_context_deinit(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // EIP712_CTX_H_ +#endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 98183b0..596315f 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -5,13 +5,9 @@ #include "encode_field.h" #include "mem.h" #include "shared_context.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes -typedef enum -{ - MSB, - LSB -} e_padding_type; +typedef enum { MSB, LSB } e_padding_type; /** * Encode a field value to 32 bytes (padded) @@ -25,20 +21,17 @@ typedef enum static void *field_encode(const uint8_t *const value, uint8_t length, e_padding_type ptype, - uint8_t pval) -{ + uint8_t pval) { uint8_t *padded_value; uint8_t start_idx; - if (length > EIP_712_ENCODED_FIELD_LENGTH) // sanity check + if (length > EIP_712_ENCODED_FIELD_LENGTH) // sanity check { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; } - if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) - { - switch (ptype) - { + if ((padded_value = mem_alloc(EIP_712_ENCODED_FIELD_LENGTH)) != NULL) { + switch (ptype) { case MSB: memset(padded_value, pval, EIP_712_ENCODED_FIELD_LENGTH - length); start_idx = EIP_712_ENCODED_FIELD_LENGTH - length; @@ -49,12 +42,10 @@ static void *field_encode(const uint8_t *const value, break; default: apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return NULL; // should not be here + return NULL; // should not be here } memcpy(&padded_value[start_idx], value, length); - } - else - { + } else { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; } return padded_value; @@ -67,8 +58,7 @@ static void *field_encode(const uint8_t *const value, * @param[in] length its byte-length * @return the encoded value */ -void *encode_uint(const uint8_t *const value, uint8_t length) -{ +void *encode_uint(const uint8_t *const value, uint8_t length) { // no length check here since it will be checked by field_encode return field_encode(value, length, MSB, 0x00); } @@ -81,16 +71,13 @@ void *encode_uint(const uint8_t *const value, uint8_t length) * @param[in] typesize the type size in bytes * @return the encoded value */ -void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize) -{ +void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize) { uint8_t padding_value; - if ((length == typesize) && (value[0] & (1 << 7))) // negative number + if ((length == typesize) && (value[0] & (1 << 7))) // negative number { padding_value = 0xFF; - } - else - { + } else { padding_value = 0x00; } // no length check here since it will be checked by field_encode @@ -104,8 +91,7 @@ void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize * @param[in] length its byte-length * @return the encoded value */ -void *encode_bytes(const uint8_t *const value, uint8_t length) -{ +void *encode_bytes(const uint8_t *const value, uint8_t length) { // no length check here since it will be checked by field_encode return field_encode(value, length, LSB, 0x00); } @@ -117,14 +103,13 @@ void *encode_bytes(const uint8_t *const value, uint8_t length) * @param[in] length its byte-length * @return the encoded value */ -void *encode_boolean(const bool *const value, uint8_t length) -{ - if (length != 1) // sanity check +void *encode_boolean(const bool *const value, uint8_t length) { + if (length != 1) // sanity check { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; } - return encode_uint((uint8_t*)value, length); + return encode_uint((uint8_t *) value, length); } /** @@ -134,9 +119,8 @@ void *encode_boolean(const bool *const value, uint8_t length) * @param[in] length its byte-length * @return the encoded value */ -void *encode_address(const uint8_t *const value, uint8_t length) -{ - if (length != ADDRESS_LENGTH) // sanity check +void *encode_address(const uint8_t *const value, uint8_t length) { + if (length != ADDRESS_LENGTH) // sanity check { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return NULL; @@ -144,4 +128,4 @@ void *encode_address(const uint8_t *const value, uint8_t length) return encode_uint(value, length); } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/encode_field.h b/src_features/signMessageEIP712/encode_field.h index a8e99f9..f3a684a 100644 --- a/src_features/signMessageEIP712/encode_field.h +++ b/src_features/signMessageEIP712/encode_field.h @@ -6,14 +6,14 @@ #include #include -#define EIP_712_ENCODED_FIELD_LENGTH 32 +#define EIP_712_ENCODED_FIELD_LENGTH 32 -void *encode_uint(const uint8_t *const value, uint8_t length); -void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize); -void *encode_boolean(const bool *const value, uint8_t length); -void *encode_address(const uint8_t *const value, uint8_t length); -void *encode_bytes(const uint8_t *const value, uint8_t length); +void *encode_uint(const uint8_t *const value, uint8_t length); +void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize); +void *encode_boolean(const bool *const value, uint8_t length); +void *encode_address(const uint8_t *const value, uint8_t length); +void *encode_bytes(const uint8_t *const value, uint8_t length); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // ENCODE_FIELD_H_ +#endif // ENCODE_FIELD_H_ diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 211d175..b3799d3 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -8,10 +8,10 @@ #include "mem_utils.h" #include "shared_context.h" #include "ui_logic.h" -#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE -#include "context.h" // contract_addr -#include "utils.h" // u64_from_BE -#include "apdu_constants.h" // APDU response codes +#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE +#include "context.h" // contract_addr +#include "utils.h" // u64_from_BE +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" #include "commands_712.h" #include "hash_bytes.h" @@ -23,12 +23,9 @@ static s_field_hashing *fh = NULL; * * @return whether the initialization was successful or not */ -bool field_hash_init(void) -{ - if (fh == NULL) - { - if ((fh = MEM_ALLOC_AND_ALIGN_TYPE(*fh)) == NULL) - { +bool field_hash_init(void) { + if (fh == NULL) { + if ((fh = MEM_ALLOC_AND_ALIGN_TYPE(*fh)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -40,8 +37,7 @@ bool field_hash_init(void) /** * Deinitialize the field hash context */ -void field_hash_deinit(void) -{ +void field_hash_deinit(void) { fh = NULL; } @@ -55,18 +51,16 @@ void field_hash_deinit(void) */ static const uint8_t *field_hash_prepare(const void *const field_ptr, const uint8_t *data, - uint8_t *data_length) -{ + uint8_t *data_length) { e_type field_type; field_type = struct_field_type(field_ptr); - fh->remaining_size = __builtin_bswap16(*(uint16_t*)&data[0]); // network byte order + fh->remaining_size = __builtin_bswap16(*(uint16_t *) &data[0]); // network byte order data += sizeof(uint16_t); *data_length -= sizeof(uint16_t); fh->state = FHS_WAITING_FOR_MORE; - if (IS_DYN(field_type)) - { - cx_keccak_init(&global_sha3, 256); // init hash + if (IS_DYN(field_type)) { + cx_keccak_init(&global_sha3, 256); // init hash ui_712_new_field(field_ptr, data, *data_length); } return data; @@ -84,14 +78,12 @@ static const uint8_t *field_hash_prepare(const void *const field_ptr, */ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, const uint8_t *const data, - uint8_t data_length) -{ + uint8_t data_length) { uint8_t *value = NULL; e_type field_type; field_type = struct_field_type(field_ptr); - switch (field_type) - { + switch (field_type) { case TYPE_SOL_INT: value = encode_int(data, data_length, get_struct_field_typesize(field_ptr)); break; @@ -105,7 +97,7 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, value = encode_address(data, data_length); break; case TYPE_SOL_BOOL: - value = encode_boolean((bool*)data, data_length); + value = encode_boolean((bool *) data, data_length); break; case TYPE_CUSTOM: default: @@ -113,8 +105,7 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, PRINTF("Unknown solidity type!\n"); } - if (value == NULL) - { + if (value == NULL) { return NULL; } ui_712_new_field(field_ptr, data, data_length); @@ -128,22 +119,15 @@ static const uint8_t *field_hash_finalize_static(const void *const field_ptr, * * @return pointer to the hash, \ref NULL if it failed */ -static uint8_t *field_hash_finalize_dynamic(void) -{ +static uint8_t *field_hash_finalize_dynamic(void) { uint8_t *value; - if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) - { + if ((value = mem_alloc(KECCAK256_HASH_BYTESIZE)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return NULL; } // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, - 0, - value, - KECCAK256_HASH_BYTESIZE); + cx_hash((cx_hash_t *) &global_sha3, CX_LAST, NULL, 0, value, KECCAK256_HASH_BYTESIZE); return value; } @@ -153,24 +137,20 @@ static uint8_t *field_hash_finalize_dynamic(void) * @param[in] field_type the struct field's type * @param[in] hash the field hash */ -static void field_hash_feed_parent(e_type field_type, const uint8_t *const hash) -{ +static void field_hash_feed_parent(e_type field_type, const uint8_t *const hash) { uint8_t len; - if (IS_DYN(field_type)) - { + if (IS_DYN(field_type)) { len = KECCAK256_HASH_BYTESIZE; - } - else - { + } else { len = EIP_712_ENCODED_FIELD_LENGTH; } // last thing in mem is the hash of the previous field // and just before it is the current hash context - cx_sha3_t *hash_ctx = (cx_sha3_t*)(hash - sizeof(cx_sha3_t)); + cx_sha3_t *hash_ctx = (cx_sha3_t *) (hash - sizeof(cx_sha3_t)); // continue the progressive hash on it - hash_nbytes(hash, len, (cx_hash_t*)hash_ctx); + hash_nbytes(hash, len, (cx_hash_t *) hash_ctx); // deallocate it mem_dealloc(len); } @@ -187,29 +167,23 @@ static void field_hash_feed_parent(e_type field_type, const uint8_t *const hash) */ static bool field_hash_domain_special_fields(const void *const field_ptr, const uint8_t *const data, - uint8_t data_length) -{ + uint8_t data_length) { const char *key; uint8_t keylen; key = get_struct_field_keyname(field_ptr, &keylen); // copy contract address into context - if (strncmp(key, "verifyingContract", keylen) == 0) - { - if (data_length != sizeof(eip712_context->contract_addr)) - { + if (strncmp(key, "verifyingContract", keylen) == 0) { + if (data_length != sizeof(eip712_context->contract_addr)) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Unexpected verifyingContract length!\n"); return false; } memcpy(eip712_context->contract_addr, data, data_length); - } - else if (strncmp(key, "chainId", keylen) == 0) - { + } else if (strncmp(key, "chainId", keylen) == 0) { uint64_t chainId = u64_from_BE(data, data_length); - if (chainId != chainConfig->chainId) - { + if (chainId != chainConfig->chainId) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", sizeof(chainConfig->chainId), @@ -232,35 +206,25 @@ static bool field_hash_domain_special_fields(const void *const field_ptr, */ static bool field_hash_finalize(const void *const field_ptr, const uint8_t *const data, - uint8_t data_length) -{ + uint8_t data_length) { const uint8_t *value = NULL; e_type field_type; field_type = struct_field_type(field_ptr); - if (!IS_DYN(field_type)) - { - if ((value = field_hash_finalize_static(field_ptr, - data, - data_length)) == NULL) - { + if (!IS_DYN(field_type)) { + if ((value = field_hash_finalize_static(field_ptr, data, data_length)) == NULL) { return false; } - } - else - { - if ((value = field_hash_finalize_dynamic()) == NULL) - { + } else { + if ((value = field_hash_finalize_dynamic()) == NULL) { return false; } } field_hash_feed_parent(field_type, value); - if (path_get_root_type() == ROOT_DOMAIN) - { - if (field_hash_domain_special_fields(field_ptr, data, data_length) == false) - { + if (path_get_root_type() == ROOT_DOMAIN) { + if (field_hash_domain_special_fields(field_ptr, data, data_length) == false) { return false; } } @@ -278,50 +242,40 @@ static bool field_hash_finalize(const void *const field_ptr, * @param[in] partial whether there is more of that data coming later or not * @return whether the data hashing was successful or not */ -bool field_hash(const uint8_t *data, - uint8_t data_length, - bool partial) -{ +bool field_hash(const uint8_t *data, uint8_t data_length, bool partial) { const void *field_ptr; e_type field_type; - if ((fh == NULL) || ((field_ptr = path_get_field()) == NULL)) - { + if ((fh == NULL) || ((field_ptr = path_get_field()) == NULL)) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } field_type = struct_field_type(field_ptr); - if (fh->state == FHS_IDLE) // first packet for this frame + if (fh->state == FHS_IDLE) // first packet for this frame { data = field_hash_prepare(field_ptr, data, &data_length); } - if (data_length > fh->remaining_size) - { + if (data_length > fh->remaining_size) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } fh->remaining_size -= data_length; // if a dynamic type -> continue progressive hash - if (IS_DYN(field_type)) - { - hash_nbytes(data, data_length, (cx_hash_t*)&global_sha3); + if (IS_DYN(field_type)) { + hash_nbytes(data, data_length, (cx_hash_t *) &global_sha3); } - if (fh->remaining_size == 0) - { - if (partial) // only makes sense if marked as complete + if (fh->remaining_size == 0) { + if (partial) // only makes sense if marked as complete { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if (field_hash_finalize(field_ptr, data, data_length) == false) - { + if (field_hash_finalize(field_ptr, data, data_length) == false) { return false; } - } - else - { - if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial + } else { + if (!partial || !IS_DYN(field_type)) // only makes sense if marked as partial { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; @@ -332,4 +286,4 @@ bool field_hash(const uint8_t *data, return true; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/field_hash.h b/src_features/signMessageEIP712/field_hash.h index f184792..448dbd8 100644 --- a/src_features/signMessageEIP712/field_hash.h +++ b/src_features/signMessageEIP712/field_hash.h @@ -6,26 +6,19 @@ #include #include -#define IS_DYN(type) (((type) == TYPE_SOL_STRING) || ((type) == TYPE_SOL_BYTES_DYN)) +#define IS_DYN(type) (((type) == TYPE_SOL_STRING) || ((type) == TYPE_SOL_BYTES_DYN)) -typedef enum -{ - FHS_IDLE, - FHS_WAITING_FOR_MORE -} e_field_hashing_state; +typedef enum { FHS_IDLE, FHS_WAITING_FOR_MORE } e_field_hashing_state; -typedef struct -{ - uint16_t remaining_size; - uint8_t state; // e_field_hashing_state -} s_field_hashing; +typedef struct { + uint16_t remaining_size; + uint8_t state; // e_field_hashing_state +} s_field_hashing; bool field_hash_init(void); void field_hash_deinit(void); -bool field_hash(const uint8_t *data, - uint8_t data_length, - bool partial); +bool field_hash(const uint8_t *data, uint8_t data_length, bool partial); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // FIELD_HASH_H_ +#endif // FIELD_HASH_H_ diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index ac283e1..8b96233 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -2,59 +2,49 @@ #include "filtering.h" #include "hash_bytes.h" -#include "ethUstream.h" // INT256_LENGTH -#include "apdu_constants.h" // APDU return codes +#include "ethUstream.h" // INT256_LENGTH +#include "apdu_constants.h" // APDU return codes #include "context.h" #include "commands_712.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 - + 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 /** * 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 *const hash_ctx) { const void *field_ptr; const char *key; uint8_t key_len; - for (uint8_t i = 0; i < path_get_depth_count(); ++i) - { - if (i > 0) - { + for (uint8_t i = 0; i < path_get_depth_count(); ++i) { + if (i > 0) { hash_byte('.', hash_ctx); } - if ((field_ptr = path_get_nth_field(i + 1)) != NULL) - { - if ((key = get_struct_field_keyname(field_ptr, &key_len)) != NULL) - { + 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, hash_ctx); + hash_nbytes((uint8_t *) key, key_len, hash_ctx); // array levels - if (struct_field_is_array(field_ptr)) - { + 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, hash_ctx); + for (int j = 0; j < lvl_count; ++j) { + hash_nbytes((uint8_t *) ".[]", 3, hash_ctx); } } } @@ -76,8 +66,7 @@ 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) -{ + e_filtering_type type) { uint8_t hash[INT256_LENGTH]; cx_ecfp_public_key_t verifying_key; cx_sha256_t hash_ctx; @@ -87,35 +76,27 @@ static bool verify_filtering_signature(uint8_t dname_length, // Chain ID chain_id = __builtin_bswap64(chainConfig->chainId); - 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); - if (type == FILTERING_STRUCT_FIELD) - { - hash_filtering_path((cx_hash_t*)&hash_ctx); + if (type == FILTERING_STRUCT_FIELD) { + hash_filtering_path((cx_hash_t *) &hash_ctx); } // Display name - hash_nbytes((uint8_t*)dname, - sizeof(char) * dname_length, - (cx_hash_t*)&hash_ctx); + 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_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 @@ -126,14 +107,7 @@ static bool verify_filtering_signature(uint8_t dname_length, sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), #endif &verifying_key); - if (!cx_ecdsa_verify(&verifying_key, - CX_LAST, - CX_SHA256, - hash, - sizeof(hash), - sig, - sig_length)) - { + 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; @@ -150,56 +124,42 @@ static bool verify_filtering_signature(uint8_t dname_length, * @param[in] type the type of filtering * @return if everything went well or not */ -bool provide_filtering_info(const uint8_t *const payload, - uint8_t length, - e_filtering_type type) -{ +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; uint8_t sig_len; const uint8_t *sig; - if (type == FILTERING_CONTRACT_NAME) + if (type == FILTERING_CONTRACT_NAME) { + if (path_get_root_type() != ROOT_DOMAIN) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + } else // FILTERING_STRUCT_FIELD { - if (path_get_root_type() != ROOT_DOMAIN) - { + if (path_get_root_type() != ROOT_MESSAGE) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } } - else // FILTERING_STRUCT_FIELD - { - if (path_get_root_type() != ROOT_MESSAGE) - { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return false; - } - } - if (length > 0) - { + if (length > 0) { dname_len = payload[0]; - if ((1 + dname_len) < length) - { - dname = (char*)&payload[1]; + 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, type))) - { - if (type == FILTERING_CONTRACT_NAME) - { - if (!N_storage.verbose_eip712) - { + if ((sig_len > 0) && ((1 + dname_len + 1 + sig_len) == length)) { + if ((ret = verify_filtering_signature(dname_len, dname, sig_len, sig, type))) { + if (type == FILTERING_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 // FILTERING_STRUCT_FIELD + } else // FILTERING_STRUCT_FIELD { - if (dname_len > 0) // don't substitute for an empty name + if (dname_len > 0) // don't substitute for an empty name { ui_712_set_title(dname, dname_len); } @@ -212,4 +172,4 @@ bool provide_filtering_info(const uint8_t *const payload, return ret; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index c914dd7..541521c 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -6,16 +6,10 @@ #include #include -typedef enum -{ - FILTERING_CONTRACT_NAME, - FILTERING_STRUCT_FIELD -} e_filtering_type; +typedef enum { FILTERING_CONTRACT_NAME, FILTERING_STRUCT_FIELD } e_filtering_type; -bool provide_filtering_info(const uint8_t *const payload, - uint8_t length, - e_filtering_type type); +bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filtering_type type); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // FILTERING_H_ +#endif // FILTERING_H_ diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c index 7c0a549..761f5f3 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.c +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -5,7 +5,7 @@ #include "mem_utils.h" #include "commands_712.h" #include "hash_bytes.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" /** @@ -15,18 +15,16 @@ * @param[in] hash_ctx pointer to the hashing context * @return whether the formatting & hashing were successful or not */ -static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t *hash_ctx) -{ +static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t *hash_ctx) { uint16_t field_size; char *uint_str_ptr; uint8_t uint_str_len; field_size = get_struct_field_typesize(field_ptr); - switch (struct_field_type(field_ptr)) - { + switch (struct_field_type(field_ptr)) { case TYPE_SOL_INT: case TYPE_SOL_UINT: - field_size *= 8; // bytes -> bits + field_size *= 8; // bytes -> bits break; case TYPE_SOL_BYTES_FIX: break; @@ -35,12 +33,11 @@ static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t * apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len)) == NULL) - { + if ((uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); + hash_nbytes((uint8_t *) uint_str_ptr, uint_str_len, hash_ctx); mem_dealloc(uint_str_len); return true; } @@ -52,8 +49,7 @@ static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t * * @param[in] hash_ctx pointer to the hashing context * @return whether the formatting & hashing were successful or not */ -static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_hash_t *hash_ctx) -{ +static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_hash_t *hash_ctx) { uint8_t array_size; char *uint_str_ptr; uint8_t uint_str_len; @@ -61,21 +57,18 @@ static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_ uint8_t lvls_count; lvl_ptr = get_struct_field_array_lvls_array(field_ptr, &lvls_count); - while (lvls_count-- > 0) - { + while (lvls_count-- > 0) { hash_byte('[', hash_ctx); - switch (struct_field_array_depth(lvl_ptr, &array_size)) - { + switch (struct_field_array_depth(lvl_ptr, &array_size)) { case ARRAY_DYNAMIC: break; case ARRAY_FIXED_SIZE: - if ((uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len)) == NULL) - { + if ((uint_str_ptr = mem_alloc_and_format_uint(array_size, &uint_str_len)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } - hash_nbytes((uint8_t*)uint_str_ptr, uint_str_len, hash_ctx); + hash_nbytes((uint8_t *) uint_str_ptr, uint_str_len, hash_ctx); mem_dealloc(uint_str_len); break; default: @@ -96,33 +89,28 @@ static bool format_hash_field_type_array_levels(const void *const field_ptr, cx_ * @param[in] hash_ctx pointer to the hashing context * @return whether the formatting & hashing were successful or not */ -bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx) -{ +bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx) { const char *name; uint8_t length; // field type name name = get_struct_field_typename(field_ptr, &length); - hash_nbytes((uint8_t*)name, length, hash_ctx); + hash_nbytes((uint8_t *) name, length, hash_ctx); // field type size - if (struct_field_has_typesize(field_ptr)) - { - if (!format_hash_field_type_size(field_ptr, hash_ctx)) - { + if (struct_field_has_typesize(field_ptr)) { + if (!format_hash_field_type_size(field_ptr, hash_ctx)) { return false; } } // field type array levels - if (struct_field_is_array(field_ptr)) - { - if (!format_hash_field_type_array_levels(field_ptr, hash_ctx)) - { + if (struct_field_is_array(field_ptr)) { + if (!format_hash_field_type_array_levels(field_ptr, hash_ctx)) { return false; } } return true; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/format_hash_field_type.h b/src_features/signMessageEIP712/format_hash_field_type.h index 6baea31..b6d635f 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.h +++ b/src_features/signMessageEIP712/format_hash_field_type.h @@ -5,8 +5,8 @@ #include "cx.h" -bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx); +bool format_hash_field_type(const void *const field_ptr, cx_hash_t *hash_ctx); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // FORMAT_HASH_FIELD_TYPE_H_ +#endif // FORMAT_HASH_FIELD_TYPE_H_ diff --git a/src_features/signMessageEIP712/hash_bytes.c b/src_features/signMessageEIP712/hash_bytes.c index 8b69c94..6f702a4 100644 --- a/src_features/signMessageEIP712/hash_bytes.c +++ b/src_features/signMessageEIP712/hash_bytes.c @@ -9,14 +9,8 @@ * @param[in] n number of bytes to hash * @param[in] hash_ctx pointer to the hashing context */ -void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *const hash_ctx) -{ - cx_hash(hash_ctx, - 0, - bytes_ptr, - n, - NULL, - 0); +void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *const hash_ctx) { + cx_hash(hash_ctx, 0, bytes_ptr, n, NULL, 0); } /** @@ -25,9 +19,8 @@ void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *const has * @param[in] byte byte to hash * @param[in] hash_ctx pointer to the hashing context */ -void hash_byte(uint8_t byte, cx_hash_t *const hash_ctx) -{ +void hash_byte(uint8_t byte, cx_hash_t *const hash_ctx) { hash_nbytes(&byte, 1, hash_ctx); } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/hash_bytes.h b/src_features/signMessageEIP712/hash_bytes.h index 32e7d11..6c1d329 100644 --- a/src_features/signMessageEIP712/hash_bytes.h +++ b/src_features/signMessageEIP712/hash_bytes.h @@ -8,6 +8,6 @@ void hash_nbytes(const uint8_t *const bytes_ptr, uint8_t n, cx_hash_t *hash_ctx); void hash_byte(uint8_t byte, cx_hash_t *hash_ctx); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // HASH_BYTES_H_ +#endif // HASH_BYTES_H_ diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index eda4076..f3299cc 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -11,7 +11,7 @@ #include "ethUtils.h" #include "mem_utils.h" #include "ui_logic.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" static s_path *path_struct = NULL; @@ -23,46 +23,37 @@ static s_path *path_struct = NULL; * @param[in] n the number of depths to evaluate * @return the feld which the first Nth depths points to */ -static const void *get_nth_field(uint8_t *const fields_count_ptr, - uint8_t n) -{ +static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { const void *struct_ptr = path_struct->root_struct; const void *field_ptr = NULL; const char *typename; uint8_t length; uint8_t fields_count; - if (path_struct == NULL) + if (path_struct == NULL) { + return NULL; + } + if (n > path_struct->depth_count) // sanity check { return NULL; } - if (n > path_struct->depth_count) // sanity check - { - return NULL; - } - for (uint8_t depth = 0; depth < n; ++depth) - { + for (uint8_t depth = 0; depth < n; ++depth) { field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - if (fields_count_ptr != NULL) - { + if (fields_count_ptr != NULL) { *fields_count_ptr = fields_count; } // check if the index at this depth makes sense - if (path_struct->depths[depth] > fields_count) - { + if (path_struct->depths[depth] > fields_count) { return NULL; } - for (uint8_t index = 0; index < path_struct->depths[depth]; ++index) - { + for (uint8_t index = 0; index < path_struct->depths[depth]; ++index) { field_ptr = get_next_struct_field(field_ptr); } - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &length); - if ((struct_ptr = get_structn(typename, length)) == NULL) - { + if ((struct_ptr = get_structn(typename, length)) == NULL) { return NULL; } } @@ -76,8 +67,7 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, * @param[out] the number of fields in the depth of the returned field * @return the field which the path points to */ -static inline const void *get_field(uint8_t *const fields_count) -{ +static inline const void *get_field(uint8_t *const fields_count) { return get_nth_field(fields_count, path_struct->depth_count); } @@ -87,8 +77,7 @@ static inline const void *get_field(uint8_t *const fields_count) * @param[in] n nth depth requested * @return pointer to the matching field, \ref NULL otherwise */ -const void *path_get_nth_field(uint8_t n) -{ +const void *path_get_nth_field(uint8_t n) { return get_nth_field(NULL, n); } @@ -98,16 +87,14 @@ const void *path_get_nth_field(uint8_t n) * @param[in] n nth to last depth requested * @return pointer to the matching field, \ref NULL otherwise */ -const void *path_get_nth_field_to_last(uint8_t n) -{ +const void *path_get_nth_field_to_last(uint8_t n) { const char *typename; uint8_t typename_len; const void *field_ptr; const void *struct_ptr = NULL; field_ptr = get_nth_field(NULL, path_struct->depth_count - n); - if (field_ptr != NULL) - { + if (field_ptr != NULL) { typename = get_struct_field_typename(field_ptr, &typename_len); struct_ptr = get_structn(typename, typename_len); } @@ -119,8 +106,7 @@ const void *path_get_nth_field_to_last(uint8_t n) * * @return the field which the path points to */ -const void *path_get_field(void) -{ +const void *path_get_field(void) { return get_field(NULL); } @@ -129,14 +115,11 @@ const void *path_get_field(void) * * @return whether the push was succesful */ -static bool path_depth_list_push(void) -{ - if (path_struct == NULL) - { +static bool path_depth_list_push(void) { + if (path_struct == NULL) { return false; } - if (path_struct->depth_count == MAX_PATH_DEPTH) - { + if (path_struct->depth_count == MAX_PATH_DEPTH) { return false; } path_struct->depths[path_struct->depth_count] = 0; @@ -149,9 +132,8 @@ static bool path_depth_list_push(void) * * @return pointer to the hashing context */ -static cx_sha3_t *get_last_hash_ctx(void) -{ - return (cx_sha3_t*)mem_alloc(0) - 1; +static cx_sha3_t *get_last_hash_ctx(void) { + return (cx_sha3_t *) mem_alloc(0) - 1; } /** @@ -159,19 +141,13 @@ static cx_sha3_t *get_last_hash_ctx(void) * * @param[out] hash pointer to buffer where the hash will be stored */ -static void finalize_hash_depth(uint8_t *hash) -{ +static void finalize_hash_depth(uint8_t *hash) { const cx_sha3_t *hash_ctx; hash_ctx = get_last_hash_ctx(); // finalize hash - cx_hash((cx_hash_t*)hash_ctx, - CX_LAST, - NULL, - 0, - hash, - KECCAK256_HASH_BYTESIZE); - mem_dealloc(sizeof(*hash_ctx)); // remove hash context + cx_hash((cx_hash_t *) hash_ctx, CX_LAST, NULL, 0, hash, KECCAK256_HASH_BYTESIZE); + mem_dealloc(sizeof(*hash_ctx)); // remove hash context } /** @@ -179,18 +155,12 @@ static void finalize_hash_depth(uint8_t *hash) * * @param[in] hash pointer to given hash */ -static void feed_last_hash_depth(const uint8_t *const hash) -{ +static void feed_last_hash_depth(const uint8_t *const hash) { const cx_sha3_t *hash_ctx; hash_ctx = get_last_hash_ctx(); // continue progressive hash with the array hash - cx_hash((cx_hash_t*)hash_ctx, - 0, - hash, - KECCAK256_HASH_BYTESIZE, - NULL, - 0); + cx_hash((cx_hash_t *) hash_ctx, 0, hash, KECCAK256_HASH_BYTESIZE, NULL, 0); } /** @@ -199,18 +169,15 @@ static void feed_last_hash_depth(const uint8_t *const hash) * @param[in] init if the hashing context should be initialized * @return whether the memory allocation of the hashing context was successful */ -static bool push_new_hash_depth(bool init) -{ +static bool push_new_hash_depth(bool init) { cx_sha3_t *hash_ctx; // allocate new hash context - if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) - { + if ((hash_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*hash_ctx)) == NULL) { return false; } - if (init) - { - cx_keccak_init(hash_ctx, 256); // initialize it + if (init) { + cx_keccak_init(hash_ctx, 256); // initialize it } return true; } @@ -220,38 +187,27 @@ static bool push_new_hash_depth(bool init) * * @return whether the pop was successful */ -static bool path_depth_list_pop(void) -{ +static bool path_depth_list_pop(void) { uint8_t hash[KECCAK256_HASH_BYTESIZE]; - if (path_struct == NULL) - { + if (path_struct == NULL) { return false; } - if (path_struct->depth_count == 0) - { + if (path_struct->depth_count == 0) { return false; } path_struct->depth_count -= 1; finalize_hash_depth(hash); - if (path_struct->depth_count > 0) - { + if (path_struct->depth_count > 0) { feed_last_hash_depth(hash); - } - else - { - switch (path_struct->root_type) - { + } else { + switch (path_struct->root_type) { case ROOT_DOMAIN: - memcpy(tmpCtx.messageSigningContext712.domainHash, - hash, - KECCAK256_HASH_BYTESIZE); + memcpy(tmpCtx.messageSigningContext712.domainHash, hash, KECCAK256_HASH_BYTESIZE); break; case ROOT_MESSAGE: - memcpy(tmpCtx.messageSigningContext712.messageHash, - hash, - KECCAK256_HASH_BYTESIZE); + memcpy(tmpCtx.messageSigningContext712.messageHash, hash, KECCAK256_HASH_BYTESIZE); break; default: break; @@ -268,17 +224,14 @@ static bool path_depth_list_pop(void) * @param[in] the number of elements contained in that depth * @return whether the push was successful */ -static bool array_depth_list_push(uint8_t path_idx, uint8_t size) -{ +static bool array_depth_list_push(uint8_t path_idx, uint8_t size) { s_array_depth *arr; - if (path_struct == NULL) - { + if (path_struct == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } - if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) - { + if (path_struct->array_depth_count == MAX_ARRAY_DEPTH) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } @@ -295,16 +248,13 @@ static bool array_depth_list_push(uint8_t path_idx, uint8_t size) * * @return whether the pop was successful */ -static bool array_depth_list_pop(void) -{ +static bool array_depth_list_pop(void) { uint8_t hash[KECCAK256_HASH_BYTESIZE]; - if (path_struct == NULL) - { + if (path_struct == NULL) { return false; } - if (path_struct->array_depth_count == 0) - { + if (path_struct->array_depth_count == 0) { return false; } @@ -321,8 +271,7 @@ static bool array_depth_list_pop(void) * * @return whether the path update worked or not */ -static bool path_update(void) -{ +static bool path_update(void) { uint8_t fields_count; const void *struct_ptr; const void *field_ptr; @@ -330,34 +279,27 @@ static bool path_update(void) uint8_t typename_len; uint8_t hash[KECCAK256_HASH_BYTESIZE]; - if (path_struct == NULL) - { + if (path_struct == NULL) { return false; } - if ((field_ptr = get_field(NULL)) == NULL) - { + if ((field_ptr = get_field(NULL)) == NULL) { return false; } struct_ptr = path_struct->root_struct; - while (struct_field_type(field_ptr) == TYPE_CUSTOM) - { + while (struct_field_type(field_ptr) == TYPE_CUSTOM) { typename = get_struct_field_typename(field_ptr, &typename_len); - if ((struct_ptr = get_structn(typename, typename_len)) == NULL) - { + if ((struct_ptr = get_structn(typename, typename_len)) == NULL) { return false; } - if ((field_ptr = get_struct_fields_array(struct_ptr, &fields_count)) == NULL) - { + if ((field_ptr = get_struct_fields_array(struct_ptr, &fields_count)) == NULL) { return false; } - if (push_new_hash_depth(true) == false) - { + if (push_new_hash_depth(true) == false) { return false; } // get the struct typehash - if (type_hash(typename, typename_len, hash) == false) - { + if (type_hash(typename, typename_len, hash) == false) { return false; } feed_last_hash_depth(hash); @@ -375,23 +317,19 @@ static bool path_update(void) * @param[in] name_length the root struct name length * @return boolean indicating if it was successful or not */ -bool path_set_root(const char *const struct_name, uint8_t name_length) -{ +bool path_set_root(const char *const struct_name, uint8_t name_length) { uint8_t hash[KECCAK256_HASH_BYTESIZE]; - if (path_struct == NULL) - { + if (path_struct == NULL) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } path_struct->root_struct = get_structn(struct_name, name_length); - if (path_struct->root_struct == NULL) - { + if (path_struct->root_struct == NULL) { PRINTF("Struct name not found ("); - for (int i = 0; i < name_length; ++i) - { + for (int i = 0; i < name_length; ++i) { PRINTF("%c", struct_name[i]); } PRINTF(")!\n"); @@ -399,12 +337,10 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) return false; } - if (push_new_hash_depth(true) == false) - { + if (push_new_hash_depth(true) == false) { return false; } - if (type_hash(struct_name, name_length, hash) == false) - { + if (type_hash(struct_name, name_length, hash) == false) { return false; } feed_last_hash_depth(hash); @@ -418,12 +354,9 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) path_struct->array_depth_count = 0; if ((name_length == strlen(DOMAIN_STRUCT_NAME)) && - (strncmp(struct_name, DOMAIN_STRUCT_NAME, name_length) == 0)) - { + (strncmp(struct_name, DOMAIN_STRUCT_NAME, name_length) == 0)) { path_struct->root_type = ROOT_DOMAIN; - } - else - { + } else { path_struct->root_type = ROOT_MESSAGE; } @@ -444,32 +377,26 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) static bool check_and_add_array_depth(const void *depth, uint8_t total_count, uint8_t pidx, - uint8_t size) -{ + uint8_t size) { uint8_t expected_size; uint8_t arr_idx; e_array_type expected_type; arr_idx = (total_count - path_struct->array_depth_count) - 1; // we skip index 0, since we already have it - for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) - { - if ((depth = get_next_struct_field_array_lvl(depth)) == NULL) - { + for (uint8_t idx = 1; idx < (arr_idx + 1); ++idx) { + if ((depth = get_next_struct_field_array_lvl(depth)) == NULL) { return false; } } expected_type = struct_field_array_depth(depth, &expected_size); - if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) - { + if ((expected_type == ARRAY_FIXED_SIZE) && (expected_size != size)) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; - PRINTF("Unexpected array depth size. (expected %d, got %d)\n", - expected_size, size); + PRINTF("Unexpected array depth size. (expected %d, got %d)\n", expected_size, size); return false; } // add it - if (!array_depth_list_push(pidx, size)) - { + if (!array_depth_list_push(pidx, size)) { return false; } return true; @@ -482,9 +409,7 @@ static bool check_and_add_array_depth(const void *depth, * @param[in] length length of data * @return whether the add was successful or not */ -bool path_new_array_depth(const uint8_t *const data, - uint8_t length) -{ +bool path_new_array_depth(const uint8_t *const data, uint8_t length) { const void *field_ptr = NULL; const void *depth = NULL; uint8_t depth_count; @@ -492,36 +417,27 @@ bool path_new_array_depth(const uint8_t *const data, uint8_t pidx; bool is_custom; - if (path_struct == NULL) - { + if (path_struct == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; - } - else if (length != 1) - { + } else if (length != 1) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - for (pidx = 0; pidx < path_struct->depth_count; ++pidx) - { - if ((field_ptr = get_nth_field(NULL, pidx + 1)) == NULL) - { + for (pidx = 0; pidx < path_struct->depth_count; ++pidx) { + if ((field_ptr = get_nth_field(NULL, pidx + 1)) == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } - if (struct_field_is_array(field_ptr)) - { - if ((depth = get_struct_field_array_lvls_array(field_ptr, &depth_count)) == NULL) - { + if (struct_field_is_array(field_ptr)) { + if ((depth = get_struct_field_array_lvls_array(field_ptr, &depth_count)) == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } total_count += depth_count; - if (total_count > path_struct->array_depth_count) - { - if (!check_and_add_array_depth(depth, total_count, pidx, *data)) - { + if (total_count > path_struct->array_depth_count) { + if (!check_and_add_array_depth(depth, total_count, pidx, *data)) { return false; } break; @@ -529,24 +445,21 @@ bool path_new_array_depth(const uint8_t *const data, } } - if (pidx == path_struct->depth_count) - { + if (pidx == path_struct->depth_count) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; PRINTF("Did not find a matching array type.\n"); return false; } is_custom = struct_field_type(field_ptr) == TYPE_CUSTOM; - if (push_new_hash_depth(!is_custom) == false) - { + if (push_new_hash_depth(!is_custom) == false) { return false; } - if (is_custom) - { + if (is_custom) { cx_sha3_t *hash_ctx = get_last_hash_ctx(); cx_sha3_t *old_ctx = hash_ctx - 1; memcpy(hash_ctx, old_ctx, sizeof(*old_ctx)); - cx_keccak_init(old_ctx, 256); // init hash + cx_keccak_init(old_ctx, 256); // init hash } return true; @@ -557,27 +470,22 @@ bool path_new_array_depth(const uint8_t *const data, * * @return whether the end of the struct has been reached. */ -static bool path_advance_in_struct(void) -{ +static bool path_advance_in_struct(void) { bool end_reached = true; uint8_t *depth = &path_struct->depths[path_struct->depth_count - 1]; uint8_t fields_count; - if (path_struct == NULL) - { + if (path_struct == NULL) { return false; } - if ((get_field(&fields_count)) == NULL) - { + if ((get_field(&fields_count)) == NULL) { return false; } - if (path_struct->depth_count > 0) - { + if (path_struct->depth_count > 0) { *depth += 1; end_reached = (*depth == fields_count); } - if (end_reached) - { + if (end_reached) { path_depth_list_pop(); } return end_reached; @@ -588,36 +496,28 @@ static bool path_advance_in_struct(void) * * @return whether the end of the array levels has been reached. */ -static bool path_advance_in_array(void) -{ +static bool path_advance_in_array(void) { bool end_reached; s_array_depth *arr_depth; - if (path_struct == NULL) - { + if (path_struct == NULL) { return false; } - do - { + do { end_reached = false; arr_depth = &path_struct->array_depths[path_struct->array_depth_count - 1]; if ((path_struct->array_depth_count > 0) && - (arr_depth->path_index == (path_struct->depth_count - 1))) - { + (arr_depth->path_index == (path_struct->depth_count - 1))) { arr_depth->size -= 1; - if (arr_depth->size == 0) - { + if (arr_depth->size == 0) { array_depth_list_pop(); end_reached = true; - } - else - { + } else { return false; } } - } - while (end_reached); + } while (end_reached); return true; } @@ -626,22 +526,16 @@ static bool path_advance_in_array(void) * * @return whether the advancement was successful or not */ -bool path_advance(void) -{ +bool path_advance(void) { bool end_reached; - do - { - if (path_advance_in_array()) - { + do { + if (path_advance_in_array()) { end_reached = path_advance_in_struct(); - } - else - { + } else { end_reached = false; } - } - while (end_reached); + } while (end_reached); path_update(); return true; } @@ -651,10 +545,8 @@ bool path_advance(void) * * @return enum representing root type */ -e_root_type path_get_root_type(void) -{ - if (path_struct == NULL) - { +e_root_type path_get_root_type(void) { + if (path_struct == NULL) { return ROOT_DOMAIN; } return path_struct->root_type; @@ -665,10 +557,8 @@ e_root_type path_get_root_type(void) * * @return pointer to the root structure definition */ -const void *path_get_root(void) -{ - if (path_struct == NULL) - { +const void *path_get_root(void) { + if (path_struct == NULL) { return NULL; } return path_struct->root_struct; @@ -679,10 +569,8 @@ const void *path_get_root(void) * * @return depth count */ -uint8_t path_get_depth_count(void) -{ - if (path_struct == NULL) - { +uint8_t path_get_depth_count(void) { + if (path_struct == NULL) { return 0; } return path_struct->depth_count; @@ -693,16 +581,11 @@ uint8_t path_get_depth_count(void) * * @return whether the memory allocation were successful. */ -bool path_init(void) -{ - if (path_struct == NULL) - { - if ((path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct)) == NULL) - { +bool path_init(void) { + if (path_struct == NULL) { + if ((path_struct = MEM_ALLOC_AND_ALIGN_TYPE(*path_struct)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; - } - else - { + } else { path_struct->depth_count = 0; } } @@ -712,9 +595,8 @@ bool path_init(void) /** * De-initialize the path context */ -void path_deinit(void) -{ +void path_deinit(void) { path_struct = NULL; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 0efa70a..8d97da4 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -9,41 +9,34 @@ #define MAX_PATH_DEPTH 16 #define MAX_ARRAY_DEPTH 4 -typedef struct -{ +typedef struct { uint8_t path_index; uint8_t size; -} s_array_depth; +} s_array_depth; -typedef enum -{ - ROOT_DOMAIN, - ROOT_MESSAGE -} e_root_type; +typedef enum { ROOT_DOMAIN, ROOT_MESSAGE } e_root_type; -typedef struct -{ +typedef struct { uint8_t depth_count; uint8_t depths[MAX_PATH_DEPTH]; uint8_t array_depth_count; s_array_depth array_depths[MAX_ARRAY_DEPTH]; const void *root_struct; e_root_type root_type; -} s_path; +} s_path; -bool path_set_root(const char *const struct_name, uint8_t length); -const void *path_get_field(void); -bool path_advance(void); -bool path_init(void); -void path_deinit(void); -bool path_new_array_depth(const uint8_t *const data, - uint8_t length); +bool path_set_root(const char *const struct_name, uint8_t length); +const void *path_get_field(void); +bool path_advance(void); +bool path_init(void); +void path_deinit(void); +bool path_new_array_depth(const uint8_t *const data, uint8_t length); e_root_type path_get_root_type(void); -const void *path_get_root(void); +const void *path_get_root(void); const void *path_get_nth_field(uint8_t n); const void *path_get_nth_field_to_last(uint8_t n); uint8_t path_get_depth_count(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // PATH_H_ +#endif // PATH_H_ diff --git a/src_features/signMessageEIP712/schema_hash.c b/src_features/signMessageEIP712/schema_hash.c index a6cd3c0..10af1e2 100644 --- a/src_features/signMessageEIP712/schema_hash.c +++ b/src_features/signMessageEIP712/schema_hash.c @@ -19,8 +19,7 @@ typedef cx_sha256_t cx_sha224_t; * * @return whether the schema hash was successful or not */ -bool compute_schema_hash(void) -{ +bool compute_schema_hash(void) { const void *struct_ptr; uint8_t structs_count; const void *field_ptr; @@ -32,42 +31,37 @@ bool compute_schema_hash(void) cx_sha224_init(&hash_ctx); struct_ptr = get_structs_array(&structs_count); - hash_byte('{', (cx_hash_t*)&hash_ctx); - while (structs_count-- > 0) - { + 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); + 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); + 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)) - { + 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); + 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); + 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); + hash_byte('}', (cx_hash_t *) &hash_ctx); // copy hash into context struct - cx_hash((cx_hash_t*)&hash_ctx, + cx_hash((cx_hash_t *) &hash_ctx, CX_LAST, NULL, 0, @@ -76,4 +70,4 @@ bool compute_schema_hash(void) return true; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/schema_hash.h b/src_features/signMessageEIP712/schema_hash.h index 9ca1e03..562d445 100644 --- a/src_features/signMessageEIP712/schema_hash.h +++ b/src_features/signMessageEIP712/schema_hash.h @@ -5,8 +5,8 @@ #include -bool compute_schema_hash(void); +bool compute_schema_hash(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // SCHEMA_HASH_H_ +#endif // SCHEMA_HASH_H_ diff --git a/src_features/signMessageEIP712/sol_typenames.c b/src_features/signMessageEIP712/sol_typenames.c index 44d1687..bf78f26 100644 --- a/src_features/signMessageEIP712/sol_typenames.c +++ b/src_features/signMessageEIP712/sol_typenames.c @@ -5,21 +5,16 @@ #include "sol_typenames.h" #include "mem.h" #include "os_pic.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" -#include "utils.h" // ARRAY_SIZE +#include "utils.h" // ARRAY_SIZE // Bit indicating they are more types associated to this typename -#define TYPENAME_MORE_TYPE (1 << 7) +#define TYPENAME_MORE_TYPE (1 << 7) static uint8_t *sol_typenames = NULL; -enum -{ - IDX_ENUM = 0, - IDX_STR_IDX, - IDX_COUNT -}; +enum { IDX_ENUM = 0, IDX_STR_IDX, IDX_COUNT }; /** * Find a match between a typename index and all the type enums associated to it @@ -28,21 +23,19 @@ enum * @param[in] t_idx typename index * @return whether at least one match was found */ -static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT], uint8_t t_idx) -{ +static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT], + uint8_t t_idx) { uint8_t *enum_match = NULL; // loop over enum/typename pairs - for (uint8_t e_idx = 0; e_idx < (TYPES_COUNT - 1); ++e_idx) - { - if (t_idx == enum_to_idx[e_idx][IDX_STR_IDX]) // match + for (uint8_t e_idx = 0; e_idx < (TYPES_COUNT - 1); ++e_idx) { + if (t_idx == enum_to_idx[e_idx][IDX_STR_IDX]) // match { - if (enum_match != NULL) // in case of a previous match, mark it + if (enum_match != NULL) // in case of a previous match, mark it { *enum_match |= TYPENAME_MORE_TYPE; } - if ((enum_match = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((enum_match = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -57,8 +50,7 @@ static bool find_enum_matches(const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COU * * @return whether the initialization went well or not */ -bool sol_typenames_init(void) -{ +bool sol_typenames_init(void) { const char *const typenames[] = { "int", // 0 "uint", // 1 @@ -68,39 +60,32 @@ bool sol_typenames_init(void) "bytes" // 5 }; // \ref TYPES_COUNT - 1 since we don't include \ref TYPE_CUSTOM - const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT] = { - { TYPE_SOL_INT, 0 }, - { TYPE_SOL_UINT, 1 }, - { TYPE_SOL_ADDRESS, 2 }, - { TYPE_SOL_BOOL, 3 }, - { TYPE_SOL_STRING, 4 }, - { TYPE_SOL_BYTES_FIX, 5 }, - { TYPE_SOL_BYTES_DYN, 5 } - }; + const uint8_t enum_to_idx[TYPES_COUNT - 1][IDX_COUNT] = {{TYPE_SOL_INT, 0}, + {TYPE_SOL_UINT, 1}, + {TYPE_SOL_ADDRESS, 2}, + {TYPE_SOL_BOOL, 3}, + {TYPE_SOL_STRING, 4}, + {TYPE_SOL_BYTES_FIX, 5}, + {TYPE_SOL_BYTES_DYN, 5}}; uint8_t *typename_len_ptr; char *typename_ptr; - if ((sol_typenames = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((sol_typenames = mem_alloc(sizeof(uint8_t))) == NULL) { return false; } *(sol_typenames) = 0; // loop over typenames - for (uint8_t t_idx = 0; t_idx < ARRAY_SIZE(typenames); ++t_idx) - { + for (uint8_t t_idx = 0; t_idx < ARRAY_SIZE(typenames); ++t_idx) { // if at least one match was found - if (find_enum_matches(enum_to_idx, t_idx)) - { - if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if (find_enum_matches(enum_to_idx, t_idx)) { + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } // get pointer to the allocated space just above *typename_len_ptr = strlen(PIC(typenames[t_idx])); - if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) - { + if ((typename_ptr = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -120,9 +105,7 @@ bool sol_typenames_init(void) * @param[out] length length of the returned typename * @return typename or \ref NULL in case it wasn't found */ -const char *get_struct_field_sol_typename(const uint8_t *field_ptr, - uint8_t *const length) -{ +const char *get_struct_field_sol_typename(const uint8_t *field_ptr, uint8_t *const length) { e_type field_type; const uint8_t *typename_ptr; uint8_t typenames_count; @@ -132,25 +115,22 @@ const char *get_struct_field_sol_typename(const uint8_t *field_ptr, field_type = struct_field_type(field_ptr); typename_ptr = get_array_in_mem(sol_typenames, &typenames_count); typename_found = false; - while (typenames_count-- > 0) - { + while (typenames_count-- > 0) { more_type = true; - while (more_type) - { + while (more_type) { more_type = *typename_ptr & TYPENAME_MORE_TYPE; e_type type_enum = *typename_ptr & TYPENAME_ENUM; - if (type_enum == field_type) - { + if (type_enum == field_type) { typename_found = true; } typename_ptr += 1; } - typename_ptr = (uint8_t*)get_string_in_mem(typename_ptr, length); - if (typename_found) return (char*)typename_ptr; + typename_ptr = (uint8_t *) get_string_in_mem(typename_ptr, length); + if (typename_found) return (char *) typename_ptr; typename_ptr += *length; } apdu_response_code = APDU_RESPONSE_INVALID_DATA; - return NULL; // Not found + return NULL; // Not found } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/sol_typenames.h b/src_features/signMessageEIP712/sol_typenames.h index 77f744c..c2229b7 100644 --- a/src_features/signMessageEIP712/sol_typenames.h +++ b/src_features/signMessageEIP712/sol_typenames.h @@ -8,9 +8,8 @@ bool sol_typenames_init(void); -const char *get_struct_field_sol_typename(const uint8_t *ptr, - uint8_t *const length); +const char *get_struct_field_sol_typename(const uint8_t *ptr, uint8_t *const length); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // SOL_TYPENAMES_H_ +#endif // SOL_TYPENAMES_H_ diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index 67e54b8..fcd6f52 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -7,10 +7,10 @@ #include "mem_utils.h" #include "type_hash.h" #include "shared_context.h" -#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE +#include "ethUtils.h" // KECCAK256_HASH_BYTESIZE #include "format_hash_field_type.h" #include "hash_bytes.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" /** @@ -19,21 +19,19 @@ * @param[in] field_ptr pointer to the struct field * @return \ref true it finished correctly, \ref false if it didn't (memory allocation) */ -static bool encode_and_hash_field(const void *const field_ptr) -{ +static bool encode_and_hash_field(const void *const field_ptr) { const char *name; uint8_t length; - if (!format_hash_field_type(field_ptr, (cx_hash_t*)&global_sha3)) - { + if (!format_hash_field_type(field_ptr, (cx_hash_t *) &global_sha3)) { return false; } // space between field type name and field name - hash_byte(' ', (cx_hash_t*)&global_sha3); + hash_byte(' ', (cx_hash_t *) &global_sha3); // field name name = get_struct_field_keyname(field_ptr, &length); - hash_nbytes((uint8_t*)name, length, (cx_hash_t*)&global_sha3); + hash_nbytes((uint8_t *) name, length, (cx_hash_t *) &global_sha3); return true; } @@ -44,8 +42,7 @@ static bool encode_and_hash_field(const void *const field_ptr) * @param[in] str_length length of the formatted string in memory * @return pointer of the string in memory, \ref NULL in case of an error */ -static bool encode_and_hash_type(const void *const struct_ptr) -{ +static bool encode_and_hash_type(const void *const struct_ptr) { const char *struct_name; uint8_t struct_name_length; const uint8_t *field_ptr; @@ -53,29 +50,26 @@ static bool encode_and_hash_type(const void *const struct_ptr) // struct name struct_name = get_struct_name(struct_ptr, &struct_name_length); - hash_nbytes((uint8_t*)struct_name, struct_name_length, (cx_hash_t*)&global_sha3); + hash_nbytes((uint8_t *) struct_name, struct_name_length, (cx_hash_t *) &global_sha3); // opening struct parenthese - hash_byte('(', (cx_hash_t*)&global_sha3); + hash_byte('(', (cx_hash_t *) &global_sha3); field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { + for (uint8_t idx = 0; idx < fields_count; ++idx) { // comma separating struct fields - if (idx > 0) - { - hash_byte(',', (cx_hash_t*)&global_sha3); + if (idx > 0) { + hash_byte(',', (cx_hash_t *) &global_sha3); } - if (encode_and_hash_field(field_ptr) == false) - { + if (encode_and_hash_field(field_ptr) == false) { return NULL; } field_ptr = get_next_struct_field(field_ptr); } // closing struct parenthese - hash_byte(')', (cx_hash_t*)&global_sha3); + hash_byte(')', (cx_hash_t *) &global_sha3); return true; } @@ -86,26 +80,21 @@ static bool encode_and_hash_type(const void *const struct_ptr) * @param[in] deps_count count of how many struct dependencies pointers * @param[in,out] deps pointer to the first dependency pointer */ -static void sort_dependencies(uint8_t deps_count, - const void **deps) -{ +static void sort_dependencies(uint8_t deps_count, const void **deps) { bool changed; const void *tmp_ptr; const char *name1, *name2; uint8_t namelen1, namelen2; int str_cmp_result; - do - { + do { changed = false; - for (size_t idx = 0; (idx + 1) < deps_count; ++idx) - { + for (size_t idx = 0; (idx + 1) < deps_count; ++idx) { name1 = get_struct_name(*(deps + idx), &namelen1); name2 = get_struct_name(*(deps + idx + 1), &namelen2); str_cmp_result = strncmp(name1, name2, MIN(namelen1, namelen2)); - if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) - { + if ((str_cmp_result > 0) || ((str_cmp_result == 0) && (namelen1 > namelen2))) { tmp_ptr = *(deps + idx); *(deps + idx) = *(deps + idx + 1); *(deps + idx + 1) = tmp_ptr; @@ -113,8 +102,7 @@ static void sort_dependencies(uint8_t deps_count, changed = true; } } - } - while (changed); + } while (changed); } /** @@ -127,8 +115,7 @@ static void sort_dependencies(uint8_t deps_count, */ static const void **get_struct_dependencies(uint8_t *const deps_count, const void **first_dep, - const void *const struct_ptr) -{ + const void *const struct_ptr) { uint8_t fields_count; const void *field_ptr; const char *arg_structname; @@ -138,35 +125,28 @@ static const void **get_struct_dependencies(uint8_t *const deps_count, const void **new_dep; field_ptr = get_struct_fields_array(struct_ptr, &fields_count); - for (uint8_t idx = 0; idx < fields_count; ++idx) - { - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { + for (uint8_t idx = 0; idx < fields_count; ++idx) { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) { // get struct name arg_structname = get_struct_field_typename(field_ptr, &arg_structname_length); // from its name, get the pointer to its definition arg_struct_ptr = get_structn(arg_structname, arg_structname_length); // check if it is not already present in the dependencies array - for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) - { + for (dep_idx = 0; dep_idx < *deps_count; ++dep_idx) { // it's a match! - if (*(first_dep + dep_idx) == arg_struct_ptr) - { + if (*(first_dep + dep_idx) == arg_struct_ptr) { break; } } // if it's not present in the array, add it and recurse into it - if (dep_idx == *deps_count) - { + if (dep_idx == *deps_count) { *deps_count += 1; - if ((new_dep = MEM_ALLOC_AND_ALIGN_TYPE(void*)) == NULL) - { + if ((new_dep = MEM_ALLOC_AND_ALIGN_TYPE(void *)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return NULL; } - if (*deps_count == 1) - { + if (*deps_count == 1) { first_dep = new_dep; } *new_dep = arg_struct_ptr; @@ -187,32 +167,24 @@ static const void **get_struct_dependencies(uint8_t *const deps_count, * @param[in] with_deps if hashed typestring should include struct dependencies * @return pointer to encoded string or \ref NULL in case of a memory allocation error */ -bool type_hash(const char *const struct_name, - const uint8_t struct_name_length, - uint8_t *hash_buf) -{ - const void *const struct_ptr = get_structn(struct_name, - struct_name_length); +bool type_hash(const char *const struct_name, const uint8_t struct_name_length, uint8_t *hash_buf) { + const void *const struct_ptr = get_structn(struct_name, struct_name_length); uint8_t deps_count = 0; const void **deps; void *mem_loc_bak = mem_alloc(0); - cx_keccak_init(&global_sha3, 256); // init hash + cx_keccak_init(&global_sha3, 256); // init hash deps = get_struct_dependencies(&deps_count, NULL, struct_ptr); - if ((deps_count > 0) && (deps == NULL)) - { + if ((deps_count > 0) && (deps == NULL)) { return false; } sort_dependencies(deps_count, deps); - if (encode_and_hash_type(struct_ptr) == false) - { + if (encode_and_hash_type(struct_ptr) == false) { return false; } // loop over each struct and generate string - for (int idx = 0; idx < deps_count; ++idx) - { - if (encode_and_hash_type(*deps) == false) - { + for (int idx = 0; idx < deps_count; ++idx) { + if (encode_and_hash_type(*deps) == false) { return false; } deps += 1; @@ -220,13 +192,8 @@ bool type_hash(const char *const struct_name, mem_dealloc(mem_alloc(0) - mem_loc_bak); // copy hash into memory - cx_hash((cx_hash_t*)&global_sha3, - CX_LAST, - NULL, - 0, - hash_buf, - KECCAK256_HASH_BYTESIZE); + cx_hash((cx_hash_t *) &global_sha3, CX_LAST, NULL, 0, hash_buf, KECCAK256_HASH_BYTESIZE); return true; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/type_hash.h b/src_features/signMessageEIP712/type_hash.h index 0017a13..ad6f070 100644 --- a/src_features/signMessageEIP712/type_hash.h +++ b/src_features/signMessageEIP712/type_hash.h @@ -6,10 +6,8 @@ #include #include -bool type_hash(const char *const struct_name, - const uint8_t struct_name_length, - uint8_t *hash_buf); +bool type_hash(const char *const struct_name, const uint8_t struct_name_length, uint8_t *hash_buf); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // TYPE_HASH_H_ +#endif // TYPE_HASH_H_ diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 1589994..e24886a 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -4,7 +4,7 @@ #include #include "typed_data.h" #include "sol_typenames.h" -#include "apdu_constants.h" // APDU response codes +#include "apdu_constants.h" // APDU response codes #include "context.h" #include "mem.h" #include "mem_utils.h" @@ -16,18 +16,14 @@ static s_typed_data *typed_data = NULL; * * @return whether the memory allocation was successful */ -bool typed_data_init(void) -{ - if (typed_data == NULL) - { - if ((typed_data = MEM_ALLOC_AND_ALIGN_TYPE(*typed_data)) == NULL) - { +bool typed_data_init(void) { + if (typed_data == NULL) { + if ((typed_data = MEM_ALLOC_AND_ALIGN_TYPE(*typed_data)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } // set types pointer - if ((typed_data->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((typed_data->structs_array = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -38,8 +34,7 @@ bool typed_data_init(void) return true; } -void typed_data_deinit(void) -{ +void typed_data_deinit(void) { typed_data = NULL; } @@ -50,10 +45,8 @@ void typed_data_deinit(void) * @param[in] ptr pointer to the current location within the struct field * @return pointer to the data right after */ -static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, - const uint8_t *ptr) -{ - (void)ptr; +static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, const uint8_t *ptr) { + (void) ptr; return field_ptr + sizeof(typedesc_t); } @@ -64,13 +57,10 @@ static const uint8_t *field_skip_typedesc(const uint8_t *field_ptr, * @param[in] ptr pointer to the current location within the struct field * @return pointer to the data right after */ -static const uint8_t *field_skip_typename(const uint8_t *field_ptr, - const uint8_t *ptr) -{ +static const uint8_t *field_skip_typename(const uint8_t *field_ptr, const uint8_t *ptr) { uint8_t size; - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) { get_string_in_mem(ptr, &size); ptr += (sizeof(size) + size); } @@ -84,11 +74,8 @@ static const uint8_t *field_skip_typename(const uint8_t *field_ptr, * @param[in] ptr pointer to the current location within the struct field * @return pointer to the data right after */ -static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, - const uint8_t *ptr) -{ - if (struct_field_has_typesize(field_ptr)) - { +static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, const uint8_t *ptr) { + if (struct_field_has_typesize(field_ptr)) { ptr += sizeof(typesize_t); } return ptr; @@ -101,16 +88,12 @@ static const uint8_t *field_skip_typesize(const uint8_t *field_ptr, * @param[in] ptr pointer to the current location within the struct field * @return pointer to the data right after */ -static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, - const uint8_t *ptr) -{ +static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, const uint8_t *ptr) { uint8_t size; - if (struct_field_is_array(field_ptr)) - { + if (struct_field_is_array(field_ptr)) { ptr = get_array_in_mem(ptr, &size); - while (size-- > 0) - { + while (size-- > 0) { ptr = get_next_struct_field_array_lvl(ptr); } } @@ -124,12 +107,10 @@ static const uint8_t *field_skip_array_levels(const uint8_t *field_ptr, * @param[in] ptr pointer to the current location within the struct field * @return pointer to the data right after */ -static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, - const uint8_t *ptr) -{ +static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, const uint8_t *ptr) { uint8_t size; - (void)field_ptr; + (void) field_ptr; ptr = get_array_in_mem(ptr, &size); return ptr + size; } @@ -141,15 +122,12 @@ static const uint8_t *field_skip_keyname(const uint8_t *field_ptr, * @param[out] array_size pointer to array size * @return pointer to data */ -const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) -{ - if (ptr == NULL) - { +const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) { + if (ptr == NULL) { return NULL; } - if (array_size) - { - *array_size = *(uint8_t*)ptr; + if (array_size) { + *array_size = *(uint8_t *) ptr; } return (ptr + sizeof(*array_size)); } @@ -161,9 +139,8 @@ const void *get_array_in_mem(const void *ptr, uint8_t *const array_size) * @param[out] string_length pointer to string length * @return pointer to beginning of the string */ -const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) -{ - return (char*)get_array_in_mem(ptr, string_length); +const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) { + return (char *) get_array_in_mem(ptr, string_length); } /** @@ -172,10 +149,8 @@ const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length) * @param[in] field_ptr struct field pointer * @return TypeDesc */ -static inline typedesc_t get_struct_field_typedesc(const uint8_t *const field_ptr) -{ - if (field_ptr == NULL) - { +static inline typedesc_t get_struct_field_typedesc(const uint8_t *const field_ptr) { + if (field_ptr == NULL) { return 0; } return *field_ptr; @@ -187,8 +162,7 @@ static inline typedesc_t get_struct_field_typedesc(const uint8_t *const field_pt * @param[in] field_ptr struct field pointer * @return bool whether it is the case */ -bool struct_field_is_array(const uint8_t *const field_ptr) -{ +bool struct_field_is_array(const uint8_t *const field_ptr) { return (get_struct_field_typedesc(field_ptr) & ARRAY_MASK); } @@ -198,8 +172,7 @@ bool struct_field_is_array(const uint8_t *const field_ptr) * @param[in] field_ptr struct field pointer * @return bool whether it is the case */ -bool struct_field_has_typesize(const uint8_t *const field_ptr) -{ +bool struct_field_has_typesize(const uint8_t *const field_ptr) { return (get_struct_field_typedesc(field_ptr) & TYPESIZE_MASK); } @@ -209,8 +182,7 @@ bool struct_field_has_typesize(const uint8_t *const field_ptr) * @param[in] field_ptr struct field pointer * @return its type enum */ -e_type struct_field_type(const uint8_t *const field_ptr) -{ +e_type struct_field_type(const uint8_t *const field_ptr) { return (get_struct_field_typedesc(field_ptr) & TYPE_MASK); } @@ -220,10 +192,8 @@ e_type struct_field_type(const uint8_t *const field_ptr) * @param[in] field_ptr struct field pointer * @return its type size */ -uint8_t get_struct_field_typesize(const uint8_t *const field_ptr) -{ - if (field_ptr == NULL) - { +uint8_t get_struct_field_typesize(const uint8_t *const field_ptr) { + if (field_ptr == NULL) { return 0; } return *field_skip_typedesc(field_ptr, NULL); @@ -236,13 +206,10 @@ uint8_t get_struct_field_typesize(const uint8_t *const field_ptr) * @param[out] length the type name length * @return type name pointer */ -const char *get_struct_field_custom_typename(const uint8_t *field_ptr, - uint8_t *const length) -{ +const char *get_struct_field_custom_typename(const uint8_t *field_ptr, uint8_t *const length) { const uint8_t *ptr; - if (field_ptr == NULL) - { + if (field_ptr == NULL) { return NULL; } ptr = field_skip_typedesc(field_ptr, NULL); @@ -256,15 +223,11 @@ const char *get_struct_field_custom_typename(const uint8_t *field_ptr, * @param[out] length the type name length * @return type name pointer */ -const char *get_struct_field_typename(const uint8_t *field_ptr, - uint8_t *const length) -{ - if (field_ptr == NULL) - { +const char *get_struct_field_typename(const uint8_t *field_ptr, uint8_t *const length) { + if (field_ptr == NULL) { return NULL; } - if (struct_field_type(field_ptr) == TYPE_CUSTOM) - { + if (struct_field_type(field_ptr) == TYPE_CUSTOM) { return get_struct_field_custom_typename(field_ptr, length); } return get_struct_field_sol_typename(field_ptr, length); @@ -277,17 +240,12 @@ const char *get_struct_field_typename(const uint8_t *field_ptr, * @param[out] array_size pointer to array size * @return array type of that depth */ -e_array_type struct_field_array_depth(const uint8_t *array_depth_ptr, - uint8_t *const array_size) -{ - if (array_depth_ptr == NULL) - { +e_array_type struct_field_array_depth(const uint8_t *array_depth_ptr, uint8_t *const array_size) { + if (array_depth_ptr == NULL) { return 0; } - if (*array_depth_ptr == ARRAY_FIXED_SIZE) - { - if (array_size != NULL) - { + if (*array_depth_ptr == ARRAY_FIXED_SIZE) { + if (array_size != NULL) { *array_size = *(array_depth_ptr + sizeof(uint8_t)); } } @@ -300,16 +258,13 @@ e_array_type struct_field_array_depth(const uint8_t *array_depth_ptr, * @param[in] array_depth_ptr given array depth * @return next array depth */ -const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const array_depth_ptr) -{ +const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const array_depth_ptr) { const uint8_t *ptr; - if (array_depth_ptr == NULL) - { + if (array_depth_ptr == NULL) { return NULL; } - switch (*array_depth_ptr) - { + switch (*array_depth_ptr) { case ARRAY_DYNAMIC: ptr = array_depth_ptr; break; @@ -332,12 +287,10 @@ const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const array_depth_ * @return pointer to the first array level */ const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const field_ptr, - uint8_t *const length) -{ + uint8_t *const length) { const uint8_t *ptr; - if (field_ptr == NULL) - { + if (field_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } @@ -354,13 +307,10 @@ const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const field_ptr, * @param[out] length name length * @return key name */ -const char *get_struct_field_keyname(const uint8_t *field_ptr, - uint8_t *const length) -{ +const char *get_struct_field_keyname(const uint8_t *field_ptr, uint8_t *const length) { const uint8_t *ptr; - if (field_ptr == NULL) - { + if (field_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } @@ -377,12 +327,10 @@ const char *get_struct_field_keyname(const uint8_t *field_ptr, * @param[in] field_ptr given struct field * @return pointer to the next field */ -const uint8_t *get_next_struct_field(const void *const field_ptr) -{ +const uint8_t *get_next_struct_field(const void *const field_ptr) { const void *ptr; - if (field_ptr == NULL) - { + if (field_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } @@ -400,14 +348,12 @@ const uint8_t *get_next_struct_field(const void *const field_ptr) * @param[out] length name length * @return struct name */ -const char *get_struct_name(const uint8_t *const struct_ptr, uint8_t *const length) -{ - if (struct_ptr == NULL) - { +const char *get_struct_name(const uint8_t *const struct_ptr, uint8_t *const length) { + if (struct_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } - return (char*)get_string_in_mem(struct_ptr, length); + return (char *) get_string_in_mem(struct_ptr, length); } /** @@ -417,20 +363,17 @@ const char *get_struct_name(const uint8_t *const struct_ptr, uint8_t *const leng * @param[out] length name length * @return struct name */ -const uint8_t *get_struct_fields_array(const uint8_t *const struct_ptr, - uint8_t *const length) -{ +const uint8_t *get_struct_fields_array(const uint8_t *const struct_ptr, uint8_t *const length) { const void *ptr; uint8_t name_length; - if (struct_ptr == NULL) - { + if (struct_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } ptr = struct_ptr; get_struct_name(struct_ptr, &name_length); - ptr += (sizeof(name_length) + name_length); // skip length + ptr += (sizeof(name_length) + name_length); // skip length return get_array_in_mem(ptr, length); } @@ -440,19 +383,16 @@ const uint8_t *get_struct_fields_array(const uint8_t *const struct_ptr, * @param[in] struct_ptr given struct * @return pointer to next struct */ -const uint8_t *get_next_struct(const uint8_t *const struct_ptr) -{ +const uint8_t *get_next_struct(const uint8_t *const struct_ptr) { uint8_t fields_count; const void *ptr; - if (struct_ptr == NULL) - { + if (struct_ptr == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } ptr = get_struct_fields_array(struct_ptr, &fields_count); - while (fields_count-- > 0) - { + while (fields_count-- > 0) { ptr = get_next_struct_field(ptr); } return ptr; @@ -464,8 +404,7 @@ const uint8_t *get_next_struct(const uint8_t *const struct_ptr) * @param[out] length number of structs * @return pointer to the first struct */ -const uint8_t *get_structs_array(uint8_t *const length) -{ +const uint8_t *get_structs_array(uint8_t *const length) { return get_array_in_mem(typed_data->structs_array, length); } @@ -476,25 +415,20 @@ const uint8_t *get_structs_array(uint8_t *const length) * @param[in] length name length * @return pointer to struct */ -const uint8_t *get_structn(const char *const name, - const uint8_t length) -{ +const uint8_t *get_structn(const char *const name, const uint8_t length) { uint8_t structs_count; const uint8_t *struct_ptr; const char *struct_name; uint8_t name_length; - if (name == NULL) - { + if (name == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return NULL; } struct_ptr = get_structs_array(&structs_count); - while (structs_count-- > 0) - { + while (structs_count-- > 0) { struct_name = get_struct_name(struct_ptr, &name_length); - if ((length == name_length) && (memcmp(name, struct_name, length) == 0)) - { + if ((length == name_length) && (memcmp(name, struct_name, length) == 0)) { return struct_ptr; } struct_ptr = get_next_struct(struct_ptr); @@ -510,13 +444,11 @@ const uint8_t *get_structn(const char *const name, * @param[in] name name * @return whether it was successful */ -bool set_struct_name(uint8_t length, const uint8_t *const name) -{ +bool set_struct_name(uint8_t length, const uint8_t *const name) { uint8_t *length_ptr; char *name_ptr; - if ((name == NULL) || (typed_data == NULL)) - { + if ((name == NULL) || (typed_data == NULL)) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } @@ -524,24 +456,21 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) *(typed_data->structs_array) += 1; // copy length - if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *length_ptr = length; // copy name - if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) - { + if ((name_ptr = mem_alloc(sizeof(char) * length)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } memmove(name_ptr, name, length); // initialize number of fields - if ((typed_data->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((typed_data->current_struct_fields_array = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -558,18 +487,16 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) */ static const typedesc_t *set_struct_field_typedesc(const uint8_t *const data, uint8_t *data_idx, - uint8_t length) -{ + uint8_t length) { typedesc_t *typedesc_ptr; // copy TypeDesc - if ((*data_idx + sizeof(*typedesc_ptr)) > length) // check buffer bound + if ((*data_idx + sizeof(*typedesc_ptr)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((typedesc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((typedesc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return NULL; } @@ -586,32 +513,29 @@ static const typedesc_t *set_struct_field_typedesc(const uint8_t *const data, */ static bool set_struct_field_custom_typename(const uint8_t *const data, uint8_t *data_idx, - uint8_t length) -{ + uint8_t length) { uint8_t *typename_len_ptr; char *typename; // copy custom struct name length - if ((*data_idx + sizeof(*typename_len_ptr)) > length) // check buffer bound + if ((*data_idx + sizeof(*typename_len_ptr)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((typename_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *typename_len_ptr = data[(*data_idx)++]; // copy name - if ((*data_idx + *typename_len_ptr) > length) // check buffer bound + if ((*data_idx + *typename_len_ptr) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) - { + if ((typename = mem_alloc(sizeof(char) * *typename_len_ptr)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -627,55 +551,46 @@ static bool set_struct_field_custom_typename(const uint8_t *const data, * @param[in] data_idx the data index * @return whether it was successful */ -static bool set_struct_field_array(const uint8_t *const data, - uint8_t *data_idx, - uint8_t length) -{ +static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx, uint8_t length) { uint8_t *array_levels_count; e_array_type *array_level; uint8_t *array_level_size; - if ((*data_idx + sizeof(*array_levels_count)) > length) // check buffer bound + if ((*data_idx + sizeof(*array_levels_count)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((array_levels_count = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_levels_count = data[(*data_idx)++]; - for (int idx = 0; idx < *array_levels_count; ++idx) - { - if ((*data_idx + sizeof(*array_level)) > length) // check buffer bound + for (int idx = 0; idx < *array_levels_count; ++idx) { + if ((*data_idx + sizeof(*array_level)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_level = data[(*data_idx)++]; - if (*array_level > ARRAY_TYPES_COUNT) - { + if (*array_level > ARRAY_TYPES_COUNT) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - switch (*array_level) - { - case ARRAY_DYNAMIC: // nothing to do + switch (*array_level) { + case ARRAY_DYNAMIC: // nothing to do break; case ARRAY_FIXED_SIZE: - if ((*data_idx + sizeof(*array_level_size)) > length) // check buffer bound + if ((*data_idx + sizeof(*array_level_size)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((array_level_size = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -699,18 +614,16 @@ static bool set_struct_field_array(const uint8_t *const data, */ static bool set_struct_field_typesize(const uint8_t *const data, uint8_t *data_idx, - uint8_t length) -{ + uint8_t length) { uint8_t *typesize_ptr; // copy TypeSize - if ((*data_idx + sizeof(*typesize_ptr)) > length) // check buffer bound + if ((*data_idx + sizeof(*typesize_ptr)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((typesize_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((typesize_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -725,34 +638,29 @@ static bool set_struct_field_typesize(const uint8_t *const data, * @param[in,out] data_idx the data index * @return whether it was successful */ -static bool set_struct_field_keyname(const uint8_t *const data, - uint8_t *data_idx, - uint8_t length) -{ +static bool set_struct_field_keyname(const uint8_t *const data, uint8_t *data_idx, uint8_t length) { uint8_t *keyname_len_ptr; char *keyname_ptr; // copy length - if ((*data_idx + sizeof(*keyname_len_ptr)) > length) // check buffer bound + if ((*data_idx + sizeof(*keyname_len_ptr)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((keyname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) - { + if ((keyname_len_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *keyname_len_ptr = data[(*data_idx)++]; // copy name - if ((*data_idx + *keyname_len_ptr) > length) // check buffer bound + if ((*data_idx + *keyname_len_ptr) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((keyname_ptr = mem_alloc(sizeof(char) * *keyname_len_ptr)) == NULL) - { + if ((keyname_ptr = mem_alloc(sizeof(char) * *keyname_len_ptr)) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } @@ -768,58 +676,45 @@ static bool set_struct_field_keyname(const uint8_t *const data, * @param[in] data the field data * @return whether it was successful */ -bool set_struct_field(uint8_t length, const uint8_t *const data) -{ +bool set_struct_field(uint8_t length, const uint8_t *const data) { const typedesc_t *typedesc_ptr; uint8_t data_idx = 0; - if ((data == NULL) || (length == 0)) - { + if ((data == NULL) || (length == 0)) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; - } - else if (typed_data == NULL) - { + } else if (typed_data == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } // increment number of struct fields *(typed_data->current_struct_fields_array) += 1; - if ((typedesc_ptr = set_struct_field_typedesc(data, &data_idx, length)) == NULL) - { + if ((typedesc_ptr = set_struct_field_typedesc(data, &data_idx, length)) == NULL) { return false; } // check TypeSize flag in TypeDesc - if (*typedesc_ptr & TYPESIZE_MASK) - { - if (set_struct_field_typesize(data, &data_idx, length) == false) - { + if (*typedesc_ptr & TYPESIZE_MASK) { + if (set_struct_field_typesize(data, &data_idx, length) == false) { + return false; + } + } else if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) { + if (set_struct_field_custom_typename(data, &data_idx, length) == false) { return false; } } - else if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) - { - if (set_struct_field_custom_typename(data, &data_idx, length) == false) - { - return false; - } - } - if (*typedesc_ptr & ARRAY_MASK) - { - if (set_struct_field_array(data, &data_idx, length) == false) - { + if (*typedesc_ptr & ARRAY_MASK) { + if (set_struct_field_array(data, &data_idx, length) == false) { return false; } } - if (set_struct_field_keyname(data, &data_idx, length) == false) - { + if (set_struct_field_keyname(data, &data_idx, length) == false) { return false; } - if (data_idx != length) // check that there is no more + if (data_idx != length) // check that there is no more { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; @@ -827,4 +722,4 @@ bool set_struct_field(uint8_t length, const uint8_t *const data) return true; } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/typed_data.h b/src_features/signMessageEIP712/typed_data.h index dd431f3..9cfb68e 100644 --- a/src_features/signMessageEIP712/typed_data.h +++ b/src_features/signMessageEIP712/typed_data.h @@ -7,21 +7,14 @@ #include // TypeDesc masks -#define TYPE_MASK (0xF) -#define ARRAY_MASK (1 << 7) -#define TYPESIZE_MASK (1 << 6) -#define TYPENAME_ENUM (0xF) +#define TYPE_MASK (0xF) +#define ARRAY_MASK (1 << 7) +#define TYPESIZE_MASK (1 << 6) +#define TYPENAME_ENUM (0xF) +typedef enum { ARRAY_DYNAMIC = 0, ARRAY_FIXED_SIZE, ARRAY_TYPES_COUNT } e_array_type; -typedef enum -{ - ARRAY_DYNAMIC = 0, - ARRAY_FIXED_SIZE, - ARRAY_TYPES_COUNT -} e_array_type; - -typedef enum -{ +typedef enum { // contract defined struct TYPE_CUSTOM = 0, // native types @@ -33,48 +26,40 @@ typedef enum TYPE_SOL_BYTES_FIX, TYPE_SOL_BYTES_DYN, TYPES_COUNT -} e_type; +} e_type; -typedef struct -{ +typedef struct { uint8_t *structs_array; uint8_t *current_struct_fields_array; -} s_typed_data; +} s_typed_data; typedef uint8_t typedesc_t; typedef uint8_t typesize_t; const void *get_array_in_mem(const void *ptr, uint8_t *const array_size); const char *get_string_in_mem(const uint8_t *ptr, uint8_t *const string_length); -bool struct_field_is_array(const uint8_t *ptr); -bool struct_field_has_typesize(const uint8_t *ptr); -e_type struct_field_type(const uint8_t *ptr); +bool struct_field_is_array(const uint8_t *ptr); +bool struct_field_has_typesize(const uint8_t *ptr); +e_type struct_field_type(const uint8_t *ptr); uint8_t get_struct_field_typesize(const uint8_t *ptr); -const char *get_struct_field_custom_typename(const uint8_t *ptr, - uint8_t *const length); -const char *get_struct_field_typename(const uint8_t *ptr, - uint8_t *const length); -e_array_type struct_field_array_depth(const uint8_t *ptr, - uint8_t *const array_size); +const char *get_struct_field_custom_typename(const uint8_t *ptr, uint8_t *const length); +const char *get_struct_field_typename(const uint8_t *ptr, uint8_t *const length); +e_array_type struct_field_array_depth(const uint8_t *ptr, uint8_t *const array_size); const uint8_t *get_next_struct_field_array_lvl(const uint8_t *const ptr); const uint8_t *struct_field_half_skip(const uint8_t *ptr); -const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const ptr, - uint8_t *const length); -const char *get_struct_field_keyname(const uint8_t *ptr, - uint8_t *const length); +const uint8_t *get_struct_field_array_lvls_array(const uint8_t *const ptr, uint8_t *const length); +const char *get_struct_field_keyname(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_next_struct_field(const void *ptr); const char *get_struct_name(const uint8_t *ptr, uint8_t *const length); -const uint8_t *get_struct_fields_array(const uint8_t *ptr, - uint8_t *const length); +const uint8_t *get_struct_fields_array(const uint8_t *ptr, uint8_t *const length); const uint8_t *get_next_struct(const uint8_t *ptr); const uint8_t *get_structs_array(uint8_t *const length); -const uint8_t *get_structn(const char *const name_ptr, - const uint8_t name_length); -bool set_struct_name(uint8_t length, const uint8_t *const name); -bool set_struct_field(uint8_t length, const uint8_t *const data); -bool typed_data_init(void); -void typed_data_deinit(void); +const uint8_t *get_structn(const char *const name_ptr, const uint8_t name_length); +bool set_struct_name(uint8_t length, const uint8_t *const name); +bool set_struct_field(uint8_t length, const uint8_t *const data); +bool typed_data_init(void); +void typed_data_deinit(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // TYPED_DATA_H_ +#endif // TYPED_DATA_H_ diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_features/signMessageEIP712/ui_flow_712.c index 7d06bd6..182a89a 100644 --- a/src_features/signMessageEIP712/ui_flow_712.c +++ b/src_features/signMessageEIP712/ui_flow_712.c @@ -2,7 +2,7 @@ #include "ui_flow_712.h" #include "ui_logic.h" -#include "shared_context.h" // strings +#include "shared_context.h" // strings // clang-format off UX_STEP_NOCB( @@ -54,4 +54,4 @@ UX_FLOW(ux_712_flow, &ux_712_step_approve, &ux_712_step_reject); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_flow_712.h b/src_features/signMessageEIP712/ui_flow_712.h index 3ace335..25016de 100644 --- a/src_features/signMessageEIP712/ui_flow_712.h +++ b/src_features/signMessageEIP712/ui_flow_712.h @@ -7,6 +7,6 @@ extern const ux_flow_step_t* const ux_712_flow[]; -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // UI_FLOW_712_H_ +#endif // UI_FLOW_712_H_ diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 6440bee..bd054a3 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -9,40 +9,33 @@ #include "ux_flow_engine.h" #include "ui_flow_712.h" #include "shared_context.h" -#include "ethUtils.h" // getEthDisplayableAddress -#include "utils.h" // uint256_to_decimal +#include "ethUtils.h" // getEthDisplayableAddress +#include "utils.h" // uint256_to_decimal #include "common_712.h" -#include "context.h" // eip712_context_deinit -#include "uint256.h" // tostring256 && tostring256_signed -#include "path.h" // path_get_root_type -#include "apdu_constants.h" // APDU response codes +#include "context.h" // eip712_context_deinit +#include "uint256.h" // tostring256 && tostring256_signed +#include "path.h" // path_get_root_type +#include "apdu_constants.h" // APDU response codes #include "typed_data.h" #include "commands_712.h" - static t_ui_context *ui_ctx = NULL; - /** * Checks on the UI context to determine if the next EIP 712 field should be shown * * @return whether the next field should be shown */ -static bool ui_712_field_shown(void) -{ +static bool ui_712_field_shown(void) { bool ret = false; - if (ui_ctx->filtering_mode == EIP712_FILTERING_BASIC) - { - if (N_storage.verbose_eip712 || (path_get_root_type() == ROOT_DOMAIN)) - { + if (ui_ctx->filtering_mode == EIP712_FILTERING_BASIC) { + 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) - { + if (ui_ctx->field_flags & UI_712_FIELD_SHOWN) { ret = true; } } @@ -62,22 +55,17 @@ static void ui_712_set_buf(const char *const src, size_t src_length, char *const dst, size_t dst_length, - bool explicit_trunc) -{ + bool explicit_trunc) { uint8_t cpy_length; - if (src_length < dst_length) - { + if (src_length < dst_length) { cpy_length = src_length; - } - else - { + } else { cpy_length = dst_length - 1; } memcpy(dst, src, cpy_length); dst[cpy_length] = '\0'; - if (explicit_trunc && (src_length > dst_length)) - { + if (explicit_trunc && (src_length > dst_length)) { memcpy(dst + cpy_length - 3, "...", 3); } } @@ -85,10 +73,8 @@ static void ui_712_set_buf(const char *const src, /** * Skip the field if needed and reset its UI flags */ -void ui_712_finalize_field(void) -{ - if (!ui_712_field_shown()) - { +void ui_712_finalize_field(void) { + if (!ui_712_field_shown()) { ui_712_next_field(); } ui_712_field_flags_reset(); @@ -100,8 +86,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 *const str, uint8_t length) { ui_712_set_buf(str, length, strings.tmp.tmp2, sizeof(strings.tmp.tmp2), false); } @@ -111,23 +96,19 @@ 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 *const str, uint8_t length) { ui_712_set_buf(str, length, strings.tmp.tmp, sizeof(strings.tmp.tmp), true); } /** * 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 +void ui_712_redraw_generic_step(void) { + if (!ui_ctx->shown) // Initialize if it is not already { ux_flow_init(0, ux_712_flow, NULL); ui_ctx->shown = true; - } - else - { + } else { // not pretty, manually changes the internal state of the UX flow // so that we always land on the first screen of a paging step without any visible // screen glitching (quick screen switching) @@ -141,33 +122,22 @@ void ui_712_redraw_generic_step(void) * Called on the intermediate dummy screen between the dynamic step * && the approve/reject screen */ -void ui_712_next_field(void) -{ - if (ui_ctx == NULL) - { +void ui_712_next_field(void) { + if (ui_ctx == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return; } - if (ui_ctx->structs_to_review > 0) - { + if (ui_ctx->structs_to_review > 0) { ui_712_review_struct(path_get_nth_field_to_last(ui_ctx->structs_to_review)); ui_ctx->structs_to_review -= 1; - } - else - { - if (!ui_ctx->end_reached) - { + } else { + if (!ui_ctx->end_reached) { handle_eip712_return_code(true); - } - else - { - if (ui_ctx->pos == UI_712_POS_REVIEW) - { + } else { + if (ui_ctx->pos == UI_712_POS_REVIEW) { ux_flow_next(); ui_ctx->pos = UI_712_POS_END; - } - else - { + } else { ux_flow_prev(); ui_ctx->pos = UI_712_POS_REVIEW; } @@ -180,20 +150,17 @@ void 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 *const struct_ptr) { const char *struct_name; uint8_t struct_name_length; const char *const title = "Review struct"; - if (ui_ctx == NULL) - { + if (ui_ctx == NULL) { return; } ui_712_set_title(title, strlen(title)); - if ((struct_name = get_struct_name(struct_ptr, &struct_name_length)) != NULL) - { + if ((struct_name = get_struct_name(struct_ptr, &struct_name_length)) != NULL) { ui_712_set_value(struct_name, struct_name_length); } ui_712_redraw_generic_step(); @@ -202,8 +169,7 @@ 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) -{ +void ui_712_message_hash(void) { const char *const title = "Message hash"; ui_712_set_title(title, strlen(title)); @@ -221,11 +187,9 @@ void ui_712_message_hash(void) * @param[in] data the data that needs formatting * @param[in] length its length */ -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 *const data, uint8_t length) { + if (ui_712_field_shown()) { + ui_712_set_value((char *) data, length); } } @@ -236,16 +200,13 @@ static void ui_712_format_str(const uint8_t *const data, uint8_t length) * @param[in] length its length * @return if the formatting was successful */ -static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) -{ - if (length != ADDRESS_LENGTH) - { +static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) { + if (length != ADDRESS_LENGTH) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if (ui_712_field_shown()) - { - getEthDisplayableAddress((uint8_t*)data, + if (ui_712_field_shown()) { + getEthDisplayableAddress((uint8_t *) data, strings.tmp.tmp, sizeof(strings.tmp.tmp), &global_sha3, @@ -261,20 +222,17 @@ static bool ui_712_format_addr(const uint8_t *const data, uint8_t length) * @param[in] length its length * @return if the formatting was successful */ -static bool ui_712_format_bool(const uint8_t *const data, uint8_t length) -{ +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"; const char *str; - if (length != 1) - { + if (length != 1) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } str = *data ? true_str : false_str; - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { ui_712_set_value(str, strlen(str)); } return true; @@ -286,19 +244,12 @@ 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 */ -static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) -{ - if (ui_712_field_shown()) - { - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - length, - data); +static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) { + if (ui_712_field_shown()) { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", length, data); // +2 for the "0x" // x2 for each byte value is represented by 2 ASCII characters - if ((2 + (length * 2)) > (sizeof(strings.tmp.tmp) - 1)) - { + if ((2 + (length * 2)) > (sizeof(strings.tmp.tmp) - 1)) { strings.tmp.tmp[sizeof(strings.tmp.tmp) - 1 - 3] = '\0'; strcat(strings.tmp.tmp, "..."); } @@ -314,71 +265,58 @@ static void ui_712_format_bytes(const uint8_t *const data, uint8_t length) */ static bool ui_712_format_int(const uint8_t *const data, uint8_t length, - const void *const field_ptr) -{ + const void *const field_ptr) { uint256_t value256; uint128_t value128; int32_t value32; int16_t value16; - switch (get_struct_field_typesize(field_ptr) * 8) - { + switch (get_struct_field_typesize(field_ptr) * 8) { case 256: convertUint256BE(data, length, &value256); - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { tostring256_signed(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); } break; case 128: convertUint128BE(data, length, &value128); - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { tostring128_signed(&value128, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); } break; case 64: convertUint64BEto128(data, length, &value128); - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { 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]; + 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); + if (ui_712_field_shown()) { + 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]; + for (int i = 0; i < length; ++i) { + ((uint8_t *) &value16)[length - 1 - i] = data[i]; } - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", - value16); // expanded to 32 bits + value16); // expanded to 32 bits } break; case 8: - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "%d", - ((int8_t*)data)[0]); // expanded to 32 bits + ((int8_t *) data)[0]); // expanded to 32 bits } break; default: @@ -395,13 +333,11 @@ static bool ui_712_format_int(const uint8_t *const data, * @param[in] data the data that needs formatting * @param[in] length its length */ -static void ui_712_format_uint(const uint8_t *const data, uint8_t length) -{ +static void ui_712_format_uint(const uint8_t *const data, uint8_t length) { uint256_t value256; convertUint256BE(data, length, &value256); - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { tostring256(&value256, 10, strings.tmp.tmp, sizeof(strings.tmp.tmp)); } } @@ -413,44 +349,37 @@ static void ui_712_format_uint(const uint8_t *const data, uint8_t length) * @param[in] data pointer to the field's raw value * @param[in] length field's raw value byte-length */ -bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, uint8_t length) -{ +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; - if (ui_ctx == NULL) - { + 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) - { + if ((key = get_struct_field_keyname(field_ptr, &key_len)) == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } - if (ui_712_field_shown() && !(ui_ctx->field_flags & UI_712_FIELD_NAME_PROVIDED)) - { + 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)) - { + 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_format_addr(data, length) == false) { return false; } break; case TYPE_SOL_BOOL: - if (ui_712_format_bool(data, length) == false) - { + if (ui_712_format_bool(data, length) == false) { return false; } break; @@ -459,8 +388,7 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, ui_712_format_bytes(data, length); break; case TYPE_SOL_INT: - if (ui_712_format_int(data, length, field_ptr) == false) - { + if (ui_712_format_int(data, length, field_ptr) == false) { return false; } break; @@ -473,8 +401,7 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, } // Check if this field is supposed to be displayed - if (ui_712_field_shown()) - { + if (ui_712_field_shown()) { ui_712_redraw_generic_step(); } return true; @@ -484,17 +411,14 @@ bool ui_712_new_field(const void *const field_ptr, const uint8_t *const data, * Used to signal that we are done with reviewing the structs and we can now have * the option to approve or reject the signature */ -void ui_712_end_sign(void) -{ - if (ui_ctx == NULL) - { +void ui_712_end_sign(void) { + if (ui_ctx == NULL) { 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)) - { + if (N_storage.verbose_eip712 || (ui_ctx->filtering_mode == EIP712_FILTERING_FULL)) { ui_712_next_field(); } } @@ -502,17 +426,13 @@ void ui_712_end_sign(void) /** * Initializes the UI context structure in memory */ -bool ui_712_init(void) -{ - if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*ui_ctx))) - { +bool ui_712_init(void) { + if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*ui_ctx))) { ui_ctx->shown = false; ui_ctx->end_reached = false; ui_ctx->pos = UI_712_POS_REVIEW; ui_ctx->filtering_mode = EIP712_FILTERING_BASIC; - } - else - { + } else { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; } return ui_ctx != NULL; @@ -521,8 +441,7 @@ bool ui_712_init(void) /** * Deinit function that simply unsets the struct pointer to NULL */ -void ui_712_deinit(void) -{ +void ui_712_deinit(void) { ui_ctx = NULL; } @@ -532,8 +451,7 @@ void ui_712_deinit(void) * @param[in] e unused here, just needed to match the UI function signature * @return unused here, just needed to match the UI function signature */ -unsigned int ui_712_approve(const bagl_element_t *e) -{ +unsigned int ui_712_approve(const bagl_element_t *e) { ui_712_approve_cb(e); eip712_context_deinit(); return 0; @@ -546,8 +464,7 @@ unsigned int ui_712_approve(const bagl_element_t *e) * @param[in] e unused here, just needed to match the UI function signature * @return unused here, just needed to match the UI function signature */ -unsigned int ui_712_reject(const bagl_element_t *e) -{ +unsigned int ui_712_reject(const bagl_element_t *e) { ui_712_reject_cb(e); eip712_context_deinit(); return 0; @@ -559,14 +476,11 @@ unsigned int ui_712_reject(const bagl_element_t *e) * @param[in] show if this field should be shown on the device * @param[in] name_provided if a substitution name has been provided */ -void ui_712_flag_field(bool show, bool name_provided) -{ - if (show) - { +void ui_712_flag_field(bool show, bool name_provided) { + if (show) { ui_ctx->field_flags |= UI_712_FIELD_SHOWN; } - if (name_provided) - { + if (name_provided) { ui_ctx->field_flags |= UI_712_FIELD_NAME_PROVIDED; } } @@ -576,8 +490,7 @@ void ui_712_flag_field(bool show, bool name_provided) * * @param[in] the new filtering mode */ -void ui_712_set_filtering_mode(e_eip712_filtering_mode mode) -{ +void ui_712_set_filtering_mode(e_eip712_filtering_mode mode) { ui_ctx->filtering_mode = mode; } @@ -586,16 +499,14 @@ void ui_712_set_filtering_mode(e_eip712_filtering_mode mode) * * @return current filtering mode */ -e_eip712_filtering_mode ui_712_get_filtering_mode(void) -{ +e_eip712_filtering_mode ui_712_get_filtering_mode(void) { return ui_ctx->filtering_mode; } /** * Reset all the UI struct field flags */ -void ui_712_field_flags_reset(void) -{ +void ui_712_field_flags_reset(void) { ui_ctx->field_flags = 0; } @@ -604,12 +515,10 @@ void ui_712_field_flags_reset(void) * * Makes it so the user will have to go through a "Review struct" screen */ -void ui_712_queue_struct_to_review(void) -{ - if (N_storage.verbose_eip712) - { +void ui_712_queue_struct_to_review(void) { + if (N_storage.verbose_eip712) { ui_ctx->structs_to_review += 1; } } -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index ef08440..8d3cdf1 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -6,30 +6,21 @@ #include #include "ux.h" -#define UI_712_FIELD_SHOWN (1 << 0) -#define UI_712_FIELD_NAME_PROVIDED (1 << 1) +#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 { EIP712_FILTERING_BASIC, EIP712_FILTERING_FULL } e_eip712_filtering_mode; -typedef enum -{ - UI_712_POS_REVIEW, - UI_712_POS_END -} e_ui_position; +typedef enum { UI_712_POS_REVIEW, UI_712_POS_END } e_ui_position; -typedef struct -{ +typedef struct { bool shown; bool end_reached; e_ui_position pos; uint8_t filtering_mode; uint8_t field_flags; uint8_t structs_to_review; -} t_ui_context; +} t_ui_context; bool ui_712_init(void); void ui_712_deinit(void); @@ -41,15 +32,15 @@ unsigned int ui_712_approve(const bagl_element_t *e); unsigned int ui_712_reject(const bagl_element_t *e); 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_field_flags_reset(void); -void ui_712_finalize_field(void); -void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); +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_field_flags_reset(void); +void ui_712_finalize_field(void); +void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); e_eip712_filtering_mode ui_712_get_filtering_mode(void); -void ui_712_queue_struct_to_review(void); +void ui_712_queue_struct_to_review(void); -#endif // HAVE_EIP712_FULL_SUPPORT +#endif // HAVE_EIP712_FULL_SUPPORT -#endif // UI_LOGIC_712_H_ +#endif // UI_LOGIC_712_H_ diff --git a/src_features/signMessageEIP712_common/common_712.c b/src_features/signMessageEIP712_common/common_712.c index a2bde33..27b345f 100644 --- a/src_features/signMessageEIP712_common/common_712.c +++ b/src_features/signMessageEIP712_common/common_712.c @@ -5,15 +5,14 @@ static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01}; -unsigned int ui_712_approve_cb(const bagl_element_t *e) -{ +unsigned int ui_712_approve_cb(const bagl_element_t *e) { uint8_t privateKeyData[INT256_LENGTH]; uint8_t hash[INT256_LENGTH]; uint8_t signature[100]; cx_ecfp_private_key_t privateKey; uint32_t tx = 0; - (void)e; + (void) e; io_seproxyhal_io_heartbeat(); cx_keccak_init(&global_sha3, 256); cx_hash((cx_hash_t *) &global_sha3, @@ -57,12 +56,10 @@ unsigned int ui_712_approve_cb(const bagl_element_t *e) &info); explicit_bzero(&privateKey, sizeof(privateKey)); G_io_apdu_buffer[0] = 27; - if (info & CX_ECCINFO_PARITY_ODD) - { + if (info & CX_ECCINFO_PARITY_ODD) { G_io_apdu_buffer[0]++; } - if (info & CX_ECCINFO_xGTn) - { + if (info & CX_ECCINFO_xGTn) { G_io_apdu_buffer[0] += 2; } format_signature_out(signature); @@ -77,9 +74,8 @@ unsigned int ui_712_approve_cb(const bagl_element_t *e) return 0; // do not redraw the widget } -unsigned int ui_712_reject_cb(const bagl_element_t *e) -{ - (void)e; +unsigned int ui_712_reject_cb(const bagl_element_t *e) { + (void) e; reset_app_context(); G_io_apdu_buffer[0] = 0x69; G_io_apdu_buffer[1] = 0x85; diff --git a/src_features/signMessageEIP712_common/common_712.h b/src_features/signMessageEIP712_common/common_712.h index 8e35b15..9c279d1 100644 --- a/src_features/signMessageEIP712_common/common_712.h +++ b/src_features/signMessageEIP712_common/common_712.h @@ -7,4 +7,4 @@ unsigned int ui_712_approve_cb(const bagl_element_t *e); unsigned int ui_712_reject_cb(const bagl_element_t *e); -#endif // COMMON_EIP712_H_ +#endif // COMMON_EIP712_H_ diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index b878f0a..8b45d1c 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -11,8 +11,8 @@ void handleSignEIP712Message_v0(uint8_t p1, uint8_t dataLength, unsigned int *flags, unsigned int *tx) { - (void)tx; - (void)p2; + (void) tx; + (void) p2; if (p1 != 00) { THROW(0x6B00); } @@ -25,9 +25,7 @@ void handleSignEIP712Message_v0(uint8_t p1, if ((workBuffer == NULL) || (dataLength < (KECCAK256_HASH_BYTESIZE * 2))) { THROW(0x6a80); } - memmove(tmpCtx.messageSigningContext712.domainHash, - workBuffer, - KECCAK256_HASH_BYTESIZE); + memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, KECCAK256_HASH_BYTESIZE); memmove(tmpCtx.messageSigningContext712.messageHash, workBuffer + KECCAK256_HASH_BYTESIZE, KECCAK256_HASH_BYTESIZE); From ff68de5bb7292ccd1f4d30a94a2a066890516493 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 5 Aug 2022 18:39:56 +0200 Subject: [PATCH 135/184] Fix compound tests --- .../nanos_enable_blind_signing/00003.png | Bin 396 -> 366 bytes .../nanos_enable_blind_signing/00005.png | Bin 434 -> 374 bytes .../nanos_enable_blind_signing/00006.png | Bin 411 -> 356 bytes .../nanox_enable_blind_signing/00003.png | Bin 700 -> 641 bytes .../nanox_enable_blind_signing/00004.png | Bin 659 -> 633 bytes .../nanox_enable_blind_signing/00005.png | Bin 730 -> 667 bytes .../nanox_enable_blind_signing/00006.png | Bin 695 -> 615 bytes .../nanox_enable_blind_signing/00007.png | Bin 305 -> 758 bytes .../nanox_enable_blind_signing/00008.png | Bin 382 -> 305 bytes .../nanox_enable_blind_signing/00009.png | Bin 0 -> 382 bytes tests/zemu/src/blind_compound_deposit.test.js | 7 +++++-- 11 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 tests/zemu/snapshots/nanox_enable_blind_signing/00009.png diff --git a/tests/zemu/snapshots/nanos_enable_blind_signing/00003.png b/tests/zemu/snapshots/nanos_enable_blind_signing/00003.png index c72ea3dfdf38a5551acd739d3eaa356064d87b80..c828b7c1f241c63fdf4b0f185be604960084a529 100644 GIT binary patch delta 339 zcmV-Z0j&Ov1MUKlB!4wYL_t(|ob8!G4ul{KMR79T|G+MEfgud7*a(!?`7bVvz(?D( zKn(!^000=pzWrS3VR0kTY3$J0-z)idKCnBh^CG^J=ij%mtmjQ4K1m%aWApy3RBUC% z>r+z^uh9GxxKOwa6zYlMNky||>cNrs8LED=tmoDJWBli(*?%dhg(CChM8`DdjV>a0 zcy>S}23BFFE>=^?`zj82oG9v9t5}4EO>(-3{2f;-v%ZK#S!odl2@=!GtT!3R)saG9 zq#};AHW~jLXwSggRsR5?Wb= lbI--rm--R_0002M5eJ}E;E<|<=FtEE002ovPDHLkV1l_8qEY|= delta 369 zcmV-%0gnFe0*nKYB!5#$L_t(|ob8#x5`!QNM72}@|ARf)0}ew;2q}ax*nQ){0LSlNK)sumgwiNy% zf;en#GV8xUuM9?iCa!BhW_Ib%QnE!(!ZcfRX}_isF|=%N_phdaXjQ3!T(0R!v)V|< z7{3u?cdCX|b84BiknIeub#dwiQxSBeXok5u6V%zV)U@E1rnpRiZ4fA3`&TBtnJgFq zO4yGx)%Naqr6NXr40r^N5s=OSoe%vVP?@9@UbN_iS>M^b=snZv$Ra^v z5{MxXLJ0X+ru#&_mS4#;>Iqu+i9UBgmm~^Eipr)_L|-bak!N4=D{&R#ds?i?ud!9- ziz{oUc`bhaW3f2Y2-GY9T<9zDT7%Z21>9&B#w?((pdRkXhJON%*n4y!O|oihjax6k zh60Y@+*#kNfMS%B(GLXSu;s54O(a1T3?gl)_om3g4AkiNwRYH;q>E>WcvL_D^Yron zlGpP6IifIIUk@+<)Im>%@Z45JCuX#2eON;%rKP7~}u|002ovPDHLkV1fa2rZxZo delta 408 zcmV;J0cZa90r{|EPBd$4g9C}O?hI9Zm~8x{mB zbe!qFTCD1Y(y5J0kI-RYXIZNMA` zC_#C!?rQ*Ymy35lW)pU)cnvH8HdSVj*oQ{H6wNRLG4l7?c}PrB#WMsn8o)90)bao^ zpX2va1TkA(53m4cgOMy1h179XGRlS)CJsS&kifY}PL(Hn$>&)y8(LRC^#S{#Fjqr1 z$ZD3HRV``@Leu!HgUe=xR1syJpiOR`hN z;OuPY#iW}Dni{IyW+BLsp??!DA@_5pVHn0*d;!d3`VsGM=MUom0000Jg>f10|G-}81+ih2KaoOl=gYDPgThXs z#9|Bp007XLW1l^Pf5xdl%m*tw`#WwZl4-N$qO0N0zSe$?`=<8MmeFa{x8xo5!goNh zNHaxxZmSfsnIG!`pT6dd@mA|*mktgg$1d@AWJ1uJbSGpYkAI8JK9#HPjyrd@vJ4p_!;Vhus?{ zeHvy`Ohw%7D~HAa%ZafXCeKoJ&@#Kei4Il-H^8I65KdPP#{QB7cw01XRN-s4;s2(l@93 zL?M4<;fTFVdz0IBf<@Xg#MYEI90hAu6-HjDZ$MLIsBcC^YN&4v=3pg=LMzEd_LbWs zpIM|i$>(I2xpKCZe4K+1HLM0PqngF+AffOl9Dlx4Df6SPbSY_- z-fj77KVv!-!+CVH9z^I)m?DCvb5K1g``N$)oRt$q|$xl=U=fodo;<0000O7AhSSd8#QPt~yYwM54%LVlMbQ1fr>?<2 zU5Qc%)9V5N00000U^zb;qA^DLF@%r?%bz7uO-s4p_^i2n^rS-wnJ4I_>GxVTYc7fo zV90g@byZrFn)9+twe3Z@^~<|7e~m|}{;$jNJ6Dd|*Gn~4D>`80e7JqU zKi$caw;5l)iBjj2xosp)Oq62SG6tN4N+xW}V;*iU1pojJSr-F5oTtrd#QCf*9yl*o z7-zyX$Enpg>3_&oq(!_9{g`T^k2*D}(|BB(noX7mF;f_WrU*Z%jiXwS{5wBB$eZFT z6Xr|&)FwMF_B}CtIY5r2V!8#6j-X3+o-|)x@~<9iHfzA&!ShZZSE|mJl0v8xAyS%Vh()W zailz1t9tf|%dcv%Yc1&JfOE-LEvSp^FFdo9sPT8l0j$F4Dv}2=nkY zXFj&SyW@0`uS4?6bB&0Qy@T)p9NruH`t{EaFK{*)wlcO zmxMkS4qC42F-A>1NgjO5<;0ET(*Ip>Pr9s00000r2GMQd>@1k04!Dj0000QpzQQBrg4bPv;Qf zNTdCn=K%l!0002MoF5IR-uw8gb1o$2pE<6YmVAfvrDXZjbM2fh?Rp^vj@RF?B@QQi;=e0DOQFPV+b18mh-NkgxGl?0etbcQkFbG+3L}`aLgWQ!M ziUFuSL`|4Qy!QjncSA7%dc;p+Ee3)Mtm<05W(}YrR`1y6A;*eff2pq3iUF9JAKnL+ZC6p(hf|3nOZcmsIABsNd zVMJHKDaCQZ{NWr1M*#mYs|%4urDn-FX|P9EjO$gT2SzcciulN7tYLb5NQ&Tyto96! z0H#1l%;tH);=yE6u$JE2+FWBOF4S>#bn}=rPvw9*8-I?%&w&`M2QgR)dVeM8{go`} zeZF!q;HS%A(*9JWB-Ty}cq7%7^UteCt#c7>c54v+8?Jg9jAQUXgXuIX_o} zv$dcL=A|94CNjx2&Ol8#>O#axlY;~Bk{nuvL=n&PPMR!UglT92xhb6v)F|%ca}bA% z#!i@=y?+Q_z~y&CXHHJ$od5v9=IE{g$s0OxIfp623{TCq{o2^SXpbSjJ@>Hhz7-AV zbWTf#y{W#2s&B-KJ5@oNTITSzR2!k`JSigYsa#=hMk1ft5^37(Dx*aJWCy_}*HWc8 zZv-$gxRxq{krc7#z04i38v(?BE1sSQ(B@hyo*ftf00000a5Mi@;*np7PT_h00000< KMNUMnLSTZA>_EQ& diff --git a/tests/zemu/snapshots/nanox_enable_blind_signing/00004.png b/tests/zemu/snapshots/nanox_enable_blind_signing/00004.png index 8f75e908a9d075ce5e7f39fb222bfbec70bf7d79..fb2843a9d22f9ba531b0ff9249be70917d1bd398 100644 GIT binary patch delta 608 zcmV-m0-ybp1^EP!B!56jL_t(|ob8-jj>8}fMKhz>3Hx7C_A(!lkj2B8V1ps|e6@+W zX>qV?2lcuD000000GQ5?h3I|X^lb9CF|H3vq84I=~KkC)RwRm|#tF^_m>O!(?x2bFphhalTA*wV?w>;=|(u z{_c*Jyv_LZDN3DB;JaFEw zSX>Fy8mHFdq<=@QtSsVf=-b#6E$Y;yPNO(AHJhXkVummVO%eW}HI8~g^6y-{kvGLx zCCs<@sZA0t_Kg_693V&1G2H@3A?T9Dljg%q{?TL2W)Aoryq+@_?%u!$>wyneg4tgQ zW`89MW?$ZHc%jo}FlfIkyaNVx4q!$h#*{pE_OSNU_h_ytId>7dr5L z$C37At?St6Ry_4_(9KJX7b@cee4haAN@W4&z9Vk5b+RPd+h1h2kofiir zG&_{c7Fo}t&M`abOvpo!un*eXYF)DXlgG;nkpFW^7fxoG!HWZg^#1*U|I~t3#A3Xx u0AcHq$p`tsd;{n)TY3Qi000007~&ry#|;}+qUeSI0000g6ZgNQ>}5VuWm!HD6_A*FzPb%w zZHdyN*98Co0001hIX@br_kE{dLkO|3{LGQ+TJiO^YMi z3YN{uY{aTNBY(QE`j4Xe^eK$_fYhdy7Nr~Q_BLyd=sagjC{MWrWgC{#o-iXmR5j^o zL^r{y#Svlta1N6*fPa`Zgh)_nS#nMq?9olejb@}LW-+IU^vGqdV|sij6~Rfe+A}!= zm{mN|xMpiY~kyT_z^DhJeYIDZR22Yj#}_+Td({heU+cd}vh z`I&)!Te3+9`GVIcyRl^tM z$jgT`mzQ%EV=NzMYPISyrvkLOTv9c`DqO+76+p>jrUuoIJbz2F)~^cV3o4MysmxN$ zRzUUW31S0}VN0jRpqt&1u?q^1m$`nAmED(KW-ewfNNM-3Sauf!$~|Cw3>9`ow(!~{ z1<6*sa+ONItQLeo&W^T6$tPHzf)^Oc%wOSDxnh%Y4s%WTNNdLC94Jc=o z2$Ef4xs%pCVI$j)^<$oNF;oO=`(D$ZiT9`hPJ+v%pSk|{N>axNVW^z z5?QL9P~V%X;w?Qb$4E04t@oX*jG(Hjt9;ud4aCWr@_(~w8X4B#24iYie>0c^OW=hp z=@=Pvu$05h`#i5%LF^eJxK{-mwK@gj{l#{HWiJa_IW+IjH9`LvV zSf0Na@tJ)(W&*m|ry$Nm%QcyE>Lxr!E6ZkxyA{CNh)s*$%0yxl1^@s6003{Kcgk|T z24&%}*n2u$!~xZv#(Z_fu1EWDF##-z{1{L8J+~ZZ$rnt&oz`W|ZX#28TU1tPJ`;Xd zKuLPPP(4t6c8{iUzy#1l=G|Yid)`it$%Z;iWKO}w3SjqD`d`5d37as@q<-pL0ssI2 c005xnAID=u<;B813IG5A07*qoM6N<$f|ANHi~s-t delta 706 zcmV;z0zLhk1=6rF;{KQ9Uiy$TIgThILDZT1J++oV z#J_m8ApigX0002s&W}Tklv4g{jInFX+bE(NI<)22EmDl}%0kf|%SkCMe5JotfiLo! z=biK}&)Y1m= zGM25l{3C^b-Yp1$d^@iFlzhU7;k^@*yV^UTpyjDpG{hy@B(4 ztHX64WDImc^?yIJ#181Z3V3+VT{%YXsKz{ zX^N?U#75|uFXW?3jD;2HI?S;V^+%X~?0mlDjH?<)3Yx6Ms+vqq@%L54QSNYdv6&6FU9O zWQtTc`IudTgl1)}vVaEYQmL=o=K(eTw(MWbZ3DnFeHH=?og||TrgB(%pKEISSNPO3 zVLr15?djHE%%bqS9*OkWRaHR~UfmQ*k3#z$*l`3@d43YIr`&W*1Ms=)-sWsgP0KlT z5!RuWY=7sm-3X}WsH%f@rN zG3su;Da^WWrFd$#s|)*$7`^d1gEaGIDs$1sob7hP3W`KC(-hrB=SinOP(KHZfZ#O< zZp?3{dg?{TkAP8`Q=%PtF0-@LP-oro+?%NrM>_!X&kG)2mU*+-5uzYbLI~|86VZGTVAhf z2NN-iz%e7BO-HRad8n#ofs~NTw-Ye{T`v zcak^lmO%&_0FM2|v|84j!%F}F0002M0gx9syvZ%aE(07vw9em@BDes_3tO2?I!=O4 z*BiF|LHI^rk%q6*>Z>1*T-sAaBn+8Da9!{&ya zwBM1jhe=p!r9A_#h)#z0m(g)GyuTR4U?<2zI~h0`F>qytd*g>Neh*i$*b8BuK7V(# zB1X10?O*L@izH!^dL;F+YtuqL$c=jEOm)*6T{!HP=zj{OUDQ+zJT%lPgxi{UNm0-r z_PIC-SrK=})osi}8)7;J!B&m{?o75-I)YCF~b9TM+q)8Y6 z00000SjeBW<@Oqkg}2T82q8s~%?))M%f241BSZkYa8=bVj`FG`dobQuc}e(GE;q`w zi-|h|T1OrVpWD5xa6ka~X7{UmNh41u$Ecx;h0kS#7y-)SWwK47$|TGs&u@(-00000 c003700Ie!W#Y6}|)c^nh07*qoM6N<$f-IH{E&u=k delta 671 zcmV;Q0$}~;1h)l{B!7NML_t(|ob8)il7t`(MVYDDiThs?dzlZbC^`utAdg_*^QCk^ z3?%A>4gmlF0001h<^1gKzy~qL`{25+bb24ZmEYYR_;&twcR(h_$R|FDk7Op6r&@V1 z5i<`Q69IWT%Du^lDp}r;5>kCT9tS}5GO;N+QL&6Mx^n%E(|;5+16P?R;V}XNa~Ahq z$(wddCpaAdj{U_nS*D!BO8@`>006*VWHpq$s}b$R*mRRI_<#&R-@CK_H*OC-Y?C6= zq!xwyyoA)seVQRa6)kziI;9BU2mG{TV`)d}L)AuX z(_T2K&FY=@i+^$U6yFr-9@4^C^gHStfD~NDF=!Iv9jf+E&|UOMjLTP}Ix6BdDewqT zo?66={G>l>o@AlCOXNUYk8HY}aoxXkp{gZ0t<1UZsPCxwnheuQ@y4Hc1Ow27aPJS@ z(VAht+50!+#{k%wl7)bgijy%0Q(56&|3m1%hbvg@1%J0rAHO@=@+rslQGOg$snsIK zicTkALq?6WN73AcE*6RFj^ zfnRPE9tXhAB&V#r+|CqKQX-qy!b(|)>zDj;c8&9-hcEyD006)V4Az2TdDk0%P1MV) zwYKb1^?$yTgDf{HT8g68beL6p)lqA9Q#a#3#Oj+bM!+@KfxA?zUwH#@C&qP9`cEsL z2gITI7>EbFodY5utO|mJ{aQx%t<;x}kANAwR98M`XQ`p5=#<-csZNXlWvwx^%rfJ1 zb<{3ZE|D&tOlNc4CdsoGWM07W58)@2R)hcm02qK3`~ywRm!I-!)Z+jE002ovPDHLk FV1jGbL6`sl diff --git a/tests/zemu/snapshots/nanox_enable_blind_signing/00007.png b/tests/zemu/snapshots/nanox_enable_blind_signing/00007.png index 61861f2998e3ad1281d45331147c852277579e50..5b3eed92be2ac662655d726f7880834b803bd82c 100644 GIT binary patch delta 735 zcmV<50wDdd0`>)vBYy(DNkla(jLmecY{+pzLmu>neks?ArtbM~_!)3{xqliU!*1wnMdkh0eq|~+ zFnhJb3C)0WP?y72ad+wuoSqX~r|b5I6}4rR88&n}WG9=l1SQ-g?KwSSAJR6|3YqTW!whh3N#S6`V;S88DmSIVU&Do1^mw~bUv|z!i~YzRU|CqoNr@d0gb+dq zA;iSuLs)t2Gk@NSdv=0t*yZ%OvK`8)e_3PTb-`}J?X9Y(91KNJQEEREE5UA*S$nB#arWzsuxJDJ3f#>^>D^YEl#@#NHGo~&q6H3?dW|e7qKA7 z0BpThIvMr_6@xke5u@VfSy67zVzyxhOB>E-SaCaIS$}aex0}`S2PNFNogJ9nm&)LG z`Ncb}A=^?N(Er{GihI9z>VF~&`XV^|(d)*u=(?IQSir!^jKNf3?!Bi;F}f|Vw<6w> z1wyy)CM*hMy7ZXTyG)X`E?5S->>umr`!g3pnj~ z@}#RI%0wNk3rO^NCYV2KiGR|ckn|%-Kb{Wa=^&mC z_%B2M6|vfmrw;zF_l|$Bkl;>u{1C5rL@(g9ep~e29A6){#WW*mn_!>pHsveh>zC4u zpcZI-YcKeCavL`NlA?fK`=*ba=_$zy*mY3GYWEB%K>^!eg6-o#{q^0rNA3Us00000 d0Dwt70pO5tV-R0y=BNMw002ovPDHLkV1oAbg3$l~ diff --git a/tests/zemu/snapshots/nanox_enable_blind_signing/00008.png b/tests/zemu/snapshots/nanox_enable_blind_signing/00008.png index a58590b988714545e7960f7f400f360ffc5de41f..61861f2998e3ad1281d45331147c852277579e50 100644 GIT binary patch delta 279 zcmV+y0qFkz0`E?5S->>umr`!g3pnj~ z@}#RI%0wNk3rO^NCYV2KiGR|ckn|%-Kb{Wa=^&mC z_%B2M6|vfmrw;zF_l|$Bkl;>u{1C5rL@(g9ep~e29A6){#WW*mn_!>pHsveh>zC4u zpcZI-YcKeCavL`NlA?fK`=*ba=_$zy*mY3GYWEB%K>^!eg6-o#{q^0rNA3Us00000 d0Dwt70pO5tV-R0y=BNMw002ovPDHLkV1oQ>g4O^4 delta 356 zcmV-q0h|7@0{#M!BYy!!NklIM-mZ9&c>_-hT!sn4N;8=O_gAL6Mjp zfMU+wSsDO|%usAO&O-Ssu~wLfU%a)of4+npwWjGzuga<{K6K&zQ5~Arn0E_+D~?Wa zz#9g@S&PAL%w?gibS9Y?h_XAqqCK-a>`hn>fc|Z0&_0yq<&E=K?XaC3g5-{zk2HS4 z+5Yjb_o?cv>|KD^$lAsAjk5Y(@*V#~rbuHu`7A_H&AxgwHf8y4cdKvg$JbB4n%ez%sZ31aBYd&Oo@(7)_WJ(Znl2o8^PD` zFE`_Y-IS+Kdq0Q2-7r_C=}F(5ev`U`H~)QH|9$1FHE$YjoVax+e0xei%S`SAGlOm! zp7{T3NA-mVOYb&rdU8MdyIHo@@7cE*uj@I+Zu{2i)KMSV-E~o { + let clicks; + // LNS does not have an EIP712 setting + if (model.letter === 'S') clicks = 3; + else clicks = 4; // Enable blind-signing - await sim.navigateAndCompareSnapshots('.', model.name + '_enable_blind_signing', [-2, 0, 0, 3, 0]); + await sim.navigateAndCompareSnapshots('.', model.name + '_enable_blind_signing', [-2, 0, 0, clicks, 0]); const tx = eth.signTransaction( "44'/60'/1'/0/0", @@ -13,7 +17,6 @@ nano_models.forEach(function(model) { ); await waitForAppScreen(sim); - let clicks; if (model.letter === 'S') clicks = 8; else clicks = 6; await sim.navigateAndCompareSnapshots('.', model.name + '_deposit_eth_compound_blind', [clicks, -1, 0]); From b2ec3eff0a52c3baae86d1eb11e146151829f06c Mon Sep 17 00:00:00 2001 From: Jorge Martins <106586648+jmartins-ledger@users.noreply.github.com> Date: Mon, 8 Aug 2022 13:53:41 +0200 Subject: [PATCH 136/184] Security review (#331) * Fix some issues * add typed_data.c changes * Make attribution after the check --- src_common/uint128.c | 5 +++++ src_features/signMessageEIP712/context.c | 3 +++ src_features/signMessageEIP712/context.h | 3 +++ src_features/signMessageEIP712/encode_field.c | 5 +++++ src_features/signMessageEIP712/field_hash.c | 5 +++++ src_features/signMessageEIP712/path.c | 7 +++++-- src_features/signMessageEIP712/typed_data.c | 19 +++++++++++++++++-- 7 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src_common/uint128.c b/src_common/uint128.c index 0b1fe86..0663a24 100644 --- a/src_common/uint128.c +++ b/src_common/uint128.c @@ -242,6 +242,11 @@ bool tostring128(const uint128_t *const number, divmod128(&rDiv, &base, &rDiv, &rMod); out[offset++] = HEXDIGITS[(uint8_t) LOWER(rMod)]; } while (!zero128(&rDiv)); + + if (offset > (outLength - 1)) { + return false; + } + out[offset] = '\0'; reverseString(out, offset); return true; diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 38730cb..2c79644 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -14,6 +14,7 @@ #include "shared_context.h" // reset_app_context #include "ui_callbacks.h" // ui_idle +e_struct_init struct_state = NOT_INITIALIZED; s_eip712_context *eip712_context = NULL; /** @@ -51,6 +52,8 @@ bool eip712_context_init(void) { return false; } + struct_state = NOT_INITIALIZED; + return true; } diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 97e947f..9c2366b 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -16,6 +16,9 @@ extern s_eip712_context *eip712_context; bool eip712_context_init(void); void eip712_context_deinit(void); +typedef enum {NOT_INITIALIZED, INITIALIZED} e_struct_init; +extern e_struct_init struct_state; + #endif // HAVE_EIP712_FULL_SUPPORT #endif // EIP712_CTX_H_ diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 596315f..4ea2411 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -74,6 +74,11 @@ void *encode_uint(const uint8_t *const value, uint8_t length) { void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize) { uint8_t padding_value; + if (length < 1) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return NULL; + } + if ((length == typesize) && (value[0] & (1 << 7))) // negative number { padding_value = 0xFF; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index b3799d3..bd079b4 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -254,6 +254,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 { + if (data_length < 2) { + apdu_response_code = APDU_RESPONSE_INVALID_DATA; + return false; + } + data = field_hash_prepare(field_ptr, data, &data_length); } if (data_length > fh->remaining_size) { diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index f3299cc..b4dd32b 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -21,10 +21,10 @@ static s_path *path_struct = NULL; * * @param[out] fields_count_ptr the number of fields in the last evaluated depth * @param[in] n the number of depths to evaluate - * @return the feld which the first Nth depths points to + * @return the field which the first Nth depths points to */ static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { - const void *struct_ptr = path_struct->root_struct; + const void *struct_ptr = NULL; const void *field_ptr = NULL; const char *typename; uint8_t length; @@ -33,6 +33,9 @@ static const void *get_nth_field(uint8_t *const fields_count_ptr, uint8_t n) { if (path_struct == NULL) { return NULL; } + + struct_ptr = path_struct->root_struct; + if (n > path_struct->depth_count) // sanity check { return NULL; diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index e24886a..a3536fa 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -360,7 +360,7 @@ const char *get_struct_name(const uint8_t *const struct_ptr, uint8_t *const leng * Get struct fields from a given struct * * @param[in] struct_ptr given struct - * @param[out] length name length + * @param[out] length number of fields * @return struct name */ const uint8_t *get_struct_fields_array(const uint8_t *const struct_ptr, uint8_t *const length) { @@ -475,6 +475,8 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) { return false; } *(typed_data->current_struct_fields_array) = 0; + + struct_state = INITIALIZED; return true; } @@ -494,7 +496,7 @@ static const typedesc_t *set_struct_field_typedesc(const uint8_t *const data, if ((*data_idx + sizeof(*typedesc_ptr)) > length) // check buffer bound { apdu_response_code = APDU_RESPONSE_INVALID_DATA; - return false; + return NULL; } if ((typedesc_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; @@ -687,6 +689,11 @@ bool set_struct_field(uint8_t length, const uint8_t *const data) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } + + if (struct_state == NOT_INITIALIZED) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } // increment number of struct fields *(typed_data->current_struct_fields_array) += 1; @@ -696,9 +703,17 @@ bool set_struct_field(uint8_t length, const uint8_t *const data) { // check TypeSize flag in TypeDesc if (*typedesc_ptr & TYPESIZE_MASK) { + + // TYPESIZE and TYPE_CUSTOM are mutually exclusive + if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } + if (set_struct_field_typesize(data, &data_idx, length) == false) { return false; } + } else if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) { if (set_struct_field_custom_typename(data, &data_idx, length) == false) { return false; From 3e3dade0b4bc00e99df7e5118c9bea3946636eb9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 8 Aug 2022 13:55:45 +0200 Subject: [PATCH 137/184] Security review code linting --- src_features/signMessageEIP712/context.h | 2 +- src_features/signMessageEIP712/encode_field.c | 2 +- src_features/signMessageEIP712/typed_data.c | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 9c2366b..5a546fd 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -16,7 +16,7 @@ extern s_eip712_context *eip712_context; bool eip712_context_init(void); void eip712_context_deinit(void); -typedef enum {NOT_INITIALIZED, INITIALIZED} e_struct_init; +typedef enum { NOT_INITIALIZED, INITIALIZED } e_struct_init; extern e_struct_init struct_state; #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/encode_field.c b/src_features/signMessageEIP712/encode_field.c index 4ea2411..2c3339d 100644 --- a/src_features/signMessageEIP712/encode_field.c +++ b/src_features/signMessageEIP712/encode_field.c @@ -76,7 +76,7 @@ void *encode_int(const uint8_t *const value, uint8_t length, uint8_t typesize) { if (length < 1) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; - return NULL; + return NULL; } if ((length == typesize) && (value[0] & (1 << 7))) // negative number diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index a3536fa..ee59e63 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -703,7 +703,6 @@ bool set_struct_field(uint8_t length, const uint8_t *const data) { // check TypeSize flag in TypeDesc if (*typedesc_ptr & TYPESIZE_MASK) { - // TYPESIZE and TYPE_CUSTOM are mutually exclusive if ((*typedesc_ptr & TYPE_MASK) == TYPE_CUSTOM) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; From aef491fdeec5ba7e4a6e20bef5b8d17363644d9f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 26 Jul 2022 11:23:06 +0200 Subject: [PATCH 138/184] Added a ragger ethereum client with EIP712 support --- tests/ragger/.gitignore | 2 + tests/ragger/conftest.py | 50 +++++++ tests/ragger/ethereum_client.py | 232 ++++++++++++++++++++++++++++++++ tests/ragger/requirements.txt | 3 + tests/ragger/test_eip712.py | 22 +++ 5 files changed, 309 insertions(+) create mode 100644 tests/ragger/.gitignore create mode 100644 tests/ragger/conftest.py create mode 100644 tests/ragger/ethereum_client.py create mode 100644 tests/ragger/requirements.txt create mode 100644 tests/ragger/test_eip712.py diff --git a/tests/ragger/.gitignore b/tests/ragger/.gitignore new file mode 100644 index 0000000..93526df --- /dev/null +++ b/tests/ragger/.gitignore @@ -0,0 +1,2 @@ +venv/ +__pycache__/ diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py new file mode 100644 index 0000000..64048dc --- /dev/null +++ b/tests/ragger/conftest.py @@ -0,0 +1,50 @@ +import pytest +from ragger import Firmware +from ragger.backend import SpeculosBackend, LedgerCommBackend, LedgerWalletBackend, BackendInterface +from ethereum_client import EthereumClient + +# This variable is needed for Speculos only (physical tests need the application to be already installed) +APPLICATION = "../../bin/app.elf" +# This variable will be useful in tests to implement different behavior depending on the firmware +NANOX_FIRMWARE = Firmware("nanox", "2.0.2") +NANOS_FIRMWARE = Firmware("nanos", "2.1") +NANOSP_FIRMWARE = Firmware("nanosp", "1.0") + +# adding a pytest CLI option "--backend" +def pytest_addoption(parser): + print(help(parser.addoption)) + parser.addoption("--backend", action="store", default="speculos") + +# accessing the value of the "--backend" option as a fixture +@pytest.fixture(scope="session") +def backend_name(pytestconfig) -> str: + return pytestconfig.getoption("backend") + +# Providing the firmware as a fixture +@pytest.fixture +def firmware() -> Firmware: + return NANOX_FIRMWARE + +# Depending on the "--backend" option value, a different backend is +# instantiated, and the tests will either run on Speculos or on a physical +# device depending on the backend +def create_backend(backend: str, firmware: Firmware) -> BackendInterface: + if backend.lower() == "ledgercomm": + return LedgerCommBackend(firmware, interface="hid") + elif backend.lower() == "ledgerwallet": + return LedgerWalletBackend(firmware) + elif backend.lower() == "speculos": + return SpeculosBackend(APPLICATION, firmware) + else: + raise ValueError(f"Backend '{backend}' is unknown. Valid backends are: {BACKENDS}") + +# This fixture will create and return the backend client +@pytest.fixture +def backend_client(backend_name: str, firmware: Firmware) -> BackendInterface: + with create_backend(backend_name, firmware) as b: + yield b + +# This final fixture will return the properly configured app client, to be used in tests +@pytest.fixture +def app_client(backend_client: BackendInterface) -> EthereumClient: + yield EthereumClient(backend_client) diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py new file mode 100644 index 0000000..fd56ef2 --- /dev/null +++ b/tests/ragger/ethereum_client.py @@ -0,0 +1,232 @@ +from enum import IntEnum, auto +from typing import Iterator +from ragger.backend import BackendInterface +from ragger.utils import RAPDU + +class InsType(IntEnum): + EIP712_SEND_STRUCT_DEF = 0x1a, + EIP712_SEND_STRUCT_IMPL = 0x1c, + EIP712_SEND_FILTERING = 0x1e, + EIP712_SIGN = 0x0c + +class P1Type(IntEnum): + COMPLETE_SEND = 0x00, + PARTIAL_SEND = 0x01 + +class P2Type(IntEnum): + STRUCT_NAME = 0x00, + STRUCT_FIELD = 0xff, + ARRAY = 0x0f, + LEGACY_IMPLEM = 0x00 + NEW_IMPLEM = 0x01, + +class EIP712FieldType: + CUSTOM = 0, + INT = auto(), + UINT = auto(), + ADDRESS = auto(), + BOOL = auto(), + STRING = auto(), + FIXED_BYTES = auto(), + DYN_BYTES = auto() + + +class EthereumClientCmdBuilder: + _CLA: int = 0xE0 + + def _serialize(self, + ins: InsType, + p1: int, + p2: int, + cdata: bytearray = bytearray()) -> bytes: + + header = bytearray() + header.append(self._CLA) + header.append(ins) + header.append(p1) + header.append(p2) + header.append(len(cdata)) + return header + cdata + + def eip712_send_struct_def_struct_name(self, name: str) -> bytes: + data = bytearray() + for char in name: + data.append(ord(char)) + return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_NAME, + data) + + def eip712_send_struct_def_struct_field(self, + field_type: EIP712FieldType, + type_name: str, + type_size: int, + array_levels: [], + key_name: str) -> bytes: + data = bytearray() + typedesc = 0 + typedesc |= (len(array_levels) > 0) << 7 + typedesc |= (type_size > 0) << 6 + typedesc |= field_type + data.append(typedesc) + if field_type == EIP712FieldType.CUSTOM: + data.append(len(type_name)) + for char in type_name: + data.append(ord(char)) + if type_size > 0: + data.append(type_size) + if len(array_levels) > 0: + data.append(len(array_levels)) + for level in array_levels: + data.append(0 if level == None else 1) + if level != None: + data.append(level) + data.append(len(key_name)) + for char in key_name: + data.append(ord(char)) + return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_FIELD, + data) + + def eip712_send_struct_impl_root_struct(self, name: str) -> bytes: + data = bytearray() + for char in name: + data.append(ord(char)) + return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_NAME, + data) + + def eip712_send_struct_impl_array(self, size: int) -> bytes: + data = bytearray() + data.append(size) + return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.ARRAY, + data) + + def eip712_send_struct_impl_struct_field(self, data: bytearray) -> Iterator[bytes]: + while len(data > 0): + yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_FIELD, + data[:0xff]) + data = data[0xff:] + + def _format_bip32(self, bip32, data = bytearray()) -> bytearray: + data.append(len(bip32)) + for idx in bip32: + data.append((idx & 0xff000000) >> 24) + data.append((idx & 0x00ff0000) >> 16) + data.append((idx & 0x0000ff00) >> 8) + data.append((idx & 0x000000ff)) + return data + + def eip712_sign_new(self, bip32) -> bytes: + data = self._format_bip32(bip32) + return self._serialize(InsType.EIP712_SIGN, + P1Type.COMPLETE_SEND, + P2Type.NEW_IMPLEM, + data) + + def eip712_sign_legacy(self, + bip32, + domain_hash: bytes, + message_hash: bytes) -> bytes: + data = self._format_bip32(bip32) + data += domain_hash + data += message_hash + return self._serialize(InsType.EIP712_SIGN, + P1Type.COMPLETE_SEND, + P2Type.LEGACY_IMPLEM, + data) + + +class EthereumResponseParser: + def sign(self, data: bytes): + assert len(data) == (1 + 32 + 32) + + v = data[0:1] + data = data[1:] + + r = data[0:32] + data = data[32:] + + s = data[0:32] + data = data[32:] + + return v, r, s + + +class EthereumClient: + def __init__(self, client: BackendInterface, debug: bool = False): + self._client = client + self._debug = debug + self._cmd_builder = EthereumClientCmdBuilder() + self._resp_parser = EthereumResponseParser() + + def _send(self, payload: bytearray) -> None: + self._client.send_raw(payload) + + def _recv(self) -> RAPDU: + return self._client.receive() + + def eip712_send_struct_def_struct_name(self, name: str): + self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)) + return self._recv() + + def eip712_send_struct_def_struct_field(self, + field_type: EIP712FieldType, + type_name: str, + type_size: int, + array_levels: [], + key_name: str): + self._send(self._cmd_builder.eip712_send_struct_def_struct_field( + field_type, + type_name, + type_size, + array_levels, + key_name)) + return self._recv() + + def eip712_send_struct_impl_root_struct(self, name: str): + self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)) + return self._recv() + + def eip712_send_struct_impl_array(self, size: int): + send._send(self._cmd_builder.eip712_send_struct_impl_array(size)) + return self._recv() + + def eip712_send_struct_impl_struct_field(self, raw_value: bytes): + ret = None + for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field( + InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_FIELD, + data[:0xff]): + self._send(apdu) + ret = self._recv() + return ret + + def eip712_sign_new(self, bip32): + self._send(self._cmd_builder.eip712_sign_new(bip32)) + return self._recv() + + def eip712_sign_legacy(self, + bip32, + domain_hash: bytes, + message_hash: bytes): + self._send(self._cmd_builder.eip712_sign_legacy(bip32, + domain_hash, + message_hash)) + self._client.right_click() # sign typed message screen + for _ in range(2): # two hashes (domain + message) + for _ in range(2): # two screens per hash + self._client.right_click() + self._client.both_click() # approve signature + + resp = self._recv() + + assert resp.status == 0x9000 + return self._resp_parser.sign(resp.data) diff --git a/tests/ragger/requirements.txt b/tests/ragger/requirements.txt new file mode 100644 index 0000000..b85a795 --- /dev/null +++ b/tests/ragger/requirements.txt @@ -0,0 +1,3 @@ +ragger +pytest>=6.1.1,<7.0.0 +ecdsa>=0.16.1,<0.17.0 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py new file mode 100644 index 0000000..c3b4de1 --- /dev/null +++ b/tests/ragger/test_eip712.py @@ -0,0 +1,22 @@ +import os +import fnmatch +from ethereum_client import EthereumClient + +def test_eip712_legacy(app_client: EthereumClient): + bip32 = [ + 0x8000002c, + 0x8000003c, + 0x80000000, + 0, + 0 + ] + + v, r, s = app_client.eip712_sign_legacy( + bip32, + bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'), + bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8') + ) + + assert v == bytes.fromhex("1c") + assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64") + assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") From ef7f2e128a4995c1060f53b563c774a8443dde04 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 26 Jul 2022 16:56:13 +0200 Subject: [PATCH 139/184] Ragger tests now target all devices --- tests/ragger/conftest.py | 27 +++++++++++++++++---------- tests/ragger/ethereum_client.py | 6 +++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py index 64048dc..5f9efae 100644 --- a/tests/ragger/conftest.py +++ b/tests/ragger/conftest.py @@ -1,14 +1,15 @@ import pytest +from pathlib import Path from ragger import Firmware from ragger.backend import SpeculosBackend, LedgerCommBackend, LedgerWalletBackend, BackendInterface from ethereum_client import EthereumClient -# This variable is needed for Speculos only (physical tests need the application to be already installed) -APPLICATION = "../../bin/app.elf" -# This variable will be useful in tests to implement different behavior depending on the firmware -NANOX_FIRMWARE = Firmware("nanox", "2.0.2") -NANOS_FIRMWARE = Firmware("nanos", "2.1") -NANOSP_FIRMWARE = Firmware("nanosp", "1.0") +ELFS_DIR = (Path(__file__).parent.parent / "elfs").resolve() +FWS = [ + Firmware("nanox", "2.0.2"), + Firmware("nanos", "2.1"), + Firmware("nanosp", "1.0.3") +] # adding a pytest CLI option "--backend" def pytest_addoption(parser): @@ -21,9 +22,15 @@ def backend_name(pytestconfig) -> str: return pytestconfig.getoption("backend") # Providing the firmware as a fixture -@pytest.fixture -def firmware() -> Firmware: - return NANOX_FIRMWARE +@pytest.fixture(params=FWS) +def firmware(request) -> Firmware: + return request.param + +def get_elf_path(firmware: Firmware) -> Path: + assert ELFS_DIR.is_dir(), f"{ELFS_DIR} is not a directory" + app = ELFS_DIR / ("app-%s.elf" % firmware.device) + assert app.is_file(), f"{app} must exist" + return app # Depending on the "--backend" option value, a different backend is # instantiated, and the tests will either run on Speculos or on a physical @@ -34,7 +41,7 @@ def create_backend(backend: str, firmware: Firmware) -> BackendInterface: elif backend.lower() == "ledgerwallet": return LedgerWalletBackend(firmware) elif backend.lower() == "speculos": - return SpeculosBackend(APPLICATION, firmware) + return SpeculosBackend(get_elf_path(firmware), firmware) else: raise ValueError(f"Backend '{backend}' is unknown. Valid backends are: {BACKENDS}") diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index fd56ef2..5f1aea9 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -222,7 +222,11 @@ class EthereumClient: message_hash)) self._client.right_click() # sign typed message screen for _ in range(2): # two hashes (domain + message) - for _ in range(2): # two screens per hash + if self._client.firmware.device == "nanos": + screens_per_hash = 4 + else: + screens_per_hash = 2 + for _ in range(screens_per_hash): self._client.right_click() self._client.both_click() # approve signature From fe1326014018c814139390915f80451761e1a479 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 27 Jul 2022 18:12:23 +0200 Subject: [PATCH 140/184] EIP712 feeder script ragger support --- tests/ragger/eip712/InputData.py | 387 ++++++++++++++++++ tests/ragger/eip712/__init__.py | 0 .../input_files/00-simple_mail-test.json | 44 ++ .../01-addresses_array_mail-test.json | 31 ++ .../02-recipients_array_mail-test.json | 55 +++ .../input_files/03-long_string-test.json | 50 +++ .../input_files/04-long_bytes-test.json | 50 +++ .../input_files/05-signed_ints-test.json | 45 ++ .../eip712/input_files/06-boolean-test.json | 25 ++ .../input_files/07-fixed_bytes-test.json | 23 ++ .../eip712/input_files/08-opensea-test.json | 153 +++++++ .../eip712/input_files/09-rarible-test.json | 110 +++++ .../10-multidimensional_arrays-test.json | 92 +++++ .../input_files/11-complex_structs-test.json | 78 ++++ tests/ragger/ethereum_client.py | 38 +- tests/ragger/test_eip712.py | 13 + 16 files changed, 1176 insertions(+), 18 deletions(-) create mode 100644 tests/ragger/eip712/InputData.py create mode 100644 tests/ragger/eip712/__init__.py create mode 100644 tests/ragger/eip712/input_files/00-simple_mail-test.json create mode 100644 tests/ragger/eip712/input_files/01-addresses_array_mail-test.json create mode 100644 tests/ragger/eip712/input_files/02-recipients_array_mail-test.json create mode 100644 tests/ragger/eip712/input_files/03-long_string-test.json create mode 100644 tests/ragger/eip712/input_files/04-long_bytes-test.json create mode 100644 tests/ragger/eip712/input_files/05-signed_ints-test.json create mode 100644 tests/ragger/eip712/input_files/06-boolean-test.json create mode 100644 tests/ragger/eip712/input_files/07-fixed_bytes-test.json create mode 100644 tests/ragger/eip712/input_files/08-opensea-test.json create mode 100644 tests/ragger/eip712/input_files/09-rarible-test.json create mode 100644 tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json create mode 100644 tests/ragger/eip712/input_files/11-complex_structs-test.json diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py new file mode 100644 index 0000000..6971293 --- /dev/null +++ b/tests/ragger/eip712/InputData.py @@ -0,0 +1,387 @@ +#!/usr/bin/env python3 + +import json +import sys +import re +from enum import IntEnum, auto +import hashlib +from ecdsa import SigningKey +from ecdsa.util import sigencode_der +import pdb +from ethereum_client import EthereumClient, EIP712FieldType + +# global variables +app_client: EthereumClient = None +parser = None +trans = None +filtering_paths = None +current_path = list() +sig_ctx = {} + + +class ArrayType(IntEnum): + dynamic = 0 + fixed_size = auto() + + + +# From a string typename, extract the type and all the array depth +# Input = "uint8[2][][4]" | "bool" +# Output = ('uint8', [2, None, 4]) | ('bool', []) +def get_array_levels(typename): + array_lvls = list() + regex = re.compile("(.*)\[([0-9]*)\]$") + + while True: + result = regex.search(typename) + if not result: + break + typename = result.group(1) + + level_size = result.group(2) + if len(level_size) == 0: + level_size = None + else: + level_size = int(level_size) + array_lvls.insert(0, level_size) + return (typename, array_lvls) + + +# From a string typename, extract the type and its size +# Input = "uint64" | "string" +# Output = ('uint', 64) | ('string', None) +def get_typesize(typename): + regex = re.compile("^(\w+?)(\d*)$") + result = regex.search(typename) + typename = result.group(1) + typesize = result.group(2) + if len(typesize) == 0: + typesize = None + else: + typesize = int(typesize) + return (typename, typesize) + + + +def parse_int(typesize): + return (EIP712FieldType.INT, int(typesize / 8)) + +def parse_uint(typesize): + return (EIP712FieldType.UINT, int(typesize / 8)) + +def parse_address(typesize): + return (EIP712FieldType.ADDRESS, None) + +def parse_bool(typesize): + return (EIP712FieldType.BOOL, None) + +def parse_string(typesize): + return (EIP712FieldType.STRING, None) + +def parse_bytes(typesize): + if typesize != None: + return (EIP712FieldType.FIX_BYTES, typesize) + return (EIP712FieldType.DYN_BYTES, None) + +# set functions for each type +parsing_type_functions = {}; +parsing_type_functions["int"] = parse_int +parsing_type_functions["uint"] = parse_uint +parsing_type_functions["address"] = parse_address +parsing_type_functions["bool"] = parse_bool +parsing_type_functions["string"] = parse_string +parsing_type_functions["bytes"] = parse_bytes + + + +def send_struct_def_field(typename, keyname): + type_enum = None + + (typename, array_lvls) = get_array_levels(typename) + (typename, typesize) = get_typesize(typename) + + if typename in parsing_type_functions.keys(): + (type_enum, typesize) = parsing_type_functions[typename](typesize) + else: + type_enum = EIP712FieldType.CUSTOM + typesize = None + + app_client.eip712_send_struct_def_struct_field(type_enum, + typename, + typesize, + array_lvls, + keyname) + return (typename, type_enum, typesize, array_lvls) + + + +def encode_integer(value, typesize): + data = bytearray() + + # Some are already represented as integers in the JSON, but most as strings + if isinstance(value, str): + base = 10 + if value.startswith("0x"): + base = 16 + value = int(value, base) + + if value == 0: + data.append(0) + else: + if value < 0: # negative number, send it as unsigned + mask = 0 + for i in range(typesize): # make a mask as big as the typesize + mask = (mask << 8) | 0xff + value &= mask + while value > 0: + data.append(value & 0xff) + value >>= 8 + data.reverse() + return data + +def encode_int(value, typesize): + return encode_integer(value, typesize) + +def encode_uint(value, typesize): + return encode_integer(value, typesize) + +def encode_hex_string(value, size): + data = bytearray() + value = value[2:] # skip 0x + byte_idx = 0 + while byte_idx < size: + data.append(int(value[(byte_idx * 2):(byte_idx * 2 + 2)], 16)) + byte_idx += 1 + return data + +def encode_address(value, typesize): + return encode_hex_string(value, 20) + +def encode_bool(value, typesize): + return encode_integer(value, typesize) + +def encode_string(value, typesize): + data = bytearray() + for char in value: + data.append(ord(char)) + return data + +def encode_byte(value, typesize): + return bytearray() + +def encode_bytes_fix(value, typesize): + return encode_hex_string(value, typesize) + +def encode_bytes_dyn(value, typesize): + # length of the value string + # - the length of 0x (2) + # / by the length of one byte in a hex string (2) + return encode_hex_string(value, int((len(value) - 2) / 2)) + +# set functions for each type +encoding_functions = {} +encoding_functions[EIP712FieldType.INT] = encode_int +encoding_functions[EIP712FieldType.UINT] = encode_uint +encoding_functions[EIP712FieldType.ADDRESS] = encode_address +encoding_functions[EIP712FieldType.BOOL] = encode_bool +encoding_functions[EIP712FieldType.STRING] = encode_string +encoding_functions[EIP712FieldType.FIX_BYTES] = encode_bytes_fix +encoding_functions[EIP712FieldType.DYN_BYTES] = encode_bytes_dyn + + + +def send_struct_impl_field(value, field): + # Something wrong happened if this triggers + if isinstance(value, list) or (field["enum"] == EIP712FieldType.CUSTOM): + breakpoint() + + data = encoding_functions[field["enum"]](value, field["typesize"]) + + + if False:#args.filtering: + path = ".".join(current_path) + if path in filtering_paths.keys(): + send_filtering_field_name(filtering_paths[path]) + + app_client.eip712_send_struct_impl_struct_field(data) + + + +def evaluate_field(structs, data, field, lvls_left, new_level = True): + array_lvls = field["array_lvls"] + + if new_level: + current_path.append(field["name"]) + if len(array_lvls) > 0 and lvls_left > 0: + app_client.eip712_send_struct_impl_array(len(data)) + idx = 0 + for subdata in data: + current_path.append("[]") + if not evaluate_field(structs, subdata, field, lvls_left - 1, False): + return False + current_path.pop() + idx += 1 + if array_lvls[lvls_left - 1] != None: + if array_lvls[lvls_left - 1] != idx: + print("Mismatch in array size! Got %d, expected %d\n" % + (idx, array_lvls[lvls_left - 1]), + file=sys.stderr) + return False + else: + if field["enum"] == EIP712FieldType.CUSTOM: + if not send_struct_impl(structs, data, field["type"]): + return False + else: + send_struct_impl_field(data, field) + if new_level: + current_path.pop() + return True + + + +def send_struct_impl(structs, data, structname): + # Check if it is a struct we don't known + if structname not in structs.keys(): + return False + + struct = structs[structname] + for f in struct: + if not evaluate_field(structs, data[f["name"]], f, len(f["array_lvls"])): + return False + return True + +def send_sign(): + bip32path = bytearray.fromhex("8000002c8000003c800000000000000000000000") + path_len = bytearray() + path_len.append(int(len(bip32path) / 4)) + #send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path) + print("send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path)") + +def send_filtering_activate(): + #send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray()) + print("send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray())") + +def send_filtering_info(p1, display_name, sig): + payload = bytearray() + payload.append(len(display_name)) + for char in display_name: + payload.append(ord(char)) + payload.append(len(sig)) + payload += sig + #send_apdu(INS_FILTERING, p1, 0x00, payload) + print("send_apdu(INS_FILTERING, p1, 0x00, payload)") + +# ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures +def send_filtering_contract_name(display_name): + global sig_ctx + + msg = bytearray() + msg += sig_ctx["chainid"] + msg += sig_ctx["caddr"] + msg += sig_ctx["schema_hash"] + for char in display_name: + msg.append(ord(char)) + + sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) + send_filtering_info(P1_CONTRACT_NAME, display_name, sig) + +# ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures +def send_filtering_field_name(display_name): + global sig_ctx + + path_str = ".".join(current_path) + + msg = bytearray() + msg += sig_ctx["chainid"] + msg += sig_ctx["caddr"] + msg += sig_ctx["schema_hash"] + for char in path_str: + msg.append(ord(char)) + for char in display_name: + msg.append(ord(char)) + sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) + send_filtering_info(P1_FIELD_NAME, display_name, sig) + +def read_filtering_file(domain, message): + data_json = None + with open("%s-filter.json" % (args.JSON_FILE)) as data: + data_json = json.load(data) + return data_json + +def prepare_filtering(filtr_data, message): + global filtering_paths + + if "fields" in filtr_data: + filtering_paths = filtr_data["fields"] + else: + filtering_paths = {} + +def init_signature_context(types, domain): + global sig_ctx + + with open(args.keypath, "r") as priv_file: + sig_ctx["key"] = SigningKey.from_pem(priv_file.read(), hashlib.sha256) + caddr = domain["verifyingContract"] + if caddr.startswith("0x"): + caddr = caddr[2:] + sig_ctx["caddr"] = bytearray.fromhex(caddr) + chainid = domain["chainId"] + sig_ctx["chainid"] = bytearray() + for i in range(8): + sig_ctx["chainid"].append(chainid & (0xff << (i * 8))) + sig_ctx["chainid"].reverse() + schema_str = json.dumps(types).replace(" ","") + schema_hash = hashlib.sha224(schema_str.encode()) + sig_ctx["schema_hash"] = bytearray.fromhex(schema_hash.hexdigest()) + + return True + return False + +def process_file(aclient: EthereumClient, input_file_path: str, filtering = False) -> bool: + global sig_ctx + global app_client + + app_client = aclient + with open(input_file_path, "r") as data: + data_json = json.load(data) + domain_typename = "EIP712Domain" + message_typename = data_json["primaryType"] + types = data_json["types"] + domain = data_json["domain"] + message = data_json["message"] + + if filtering: + if not init_signature_context(types, domain): + return False + filtr = read_filtering_file(domain, message) + + # send types definition + for key in types.keys(): + app_client.eip712_send_struct_def_struct_name(key) + for f in types[key]: + (f["type"], f["enum"], f["typesize"], f["array_lvls"]) = \ + send_struct_def_field(f["type"], f["name"]) + + if filtering: + send_filtering_activate() + prepare_filtering(filtr, message) + + # send domain implementation + app_client.eip712_send_struct_impl_root_struct(domain_typename) + if not send_struct_impl(types, domain, domain_typename): + return False + + if filtering: + if filtr and "name" in filtr: + send_filtering_contract_name(filtr["name"]) + else: + send_filtering_contract_name(sig_ctx["domain"]["name"]) + + # send message implementation + app_client.eip712_send_struct_impl_root_struct(message_typename) + if not send_struct_impl(types, message, message_typename): + return False + + # sign + send_sign() + return True diff --git a/tests/ragger/eip712/__init__.py b/tests/ragger/eip712/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ragger/eip712/input_files/00-simple_mail-test.json b/tests/ragger/eip712/input_files/00-simple_mail-test.json new file mode 100644 index 0000000..ef5ce4e --- /dev/null +++ b/tests/ragger/eip712/input_files/00-simple_mail-test.json @@ -0,0 +1,44 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person" }, + { "name": "contents", "type": "string" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallets", "type": "address[]" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json b/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json new file mode 100644 index 0000000..e17e626 --- /dev/null +++ b/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json @@ -0,0 +1,31 @@ +{ + "domain": { + "chainId": 1337, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob!", + "from": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "to": [ + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xb1a22cc48f6784f629a994917cd6474923630c48" + ], + "id": 7 + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Mail": [ + { "name": "from", "type": "address" }, + { "name": "to", "type": "address[]" }, + { "name": "contents", "type": "string" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json b/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json new file mode 100644 index 0000000..51e8fef --- /dev/null +++ b/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json @@ -0,0 +1,55 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Alice", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + }, + { + "name": "Bob", + "wallets": [ + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57" + ] + } + ] + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Group": [ + { "name": "name", "type": "string" }, + { "name": "members", "type": "Person[]" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person[]" }, + { "name": "contents", "type": "string" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallets", "type": "address[]" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/03-long_string-test.json b/tests/ragger/eip712/input_files/03-long_string-test.json new file mode 100644 index 0000000..03ee734 --- /dev/null +++ b/tests/ragger/eip712/input_files/03-long_string-test.json @@ -0,0 +1,50 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob! 012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012344444444444444444444123456780123456780123456780123456780123456780123456780123456789999999999999999999999999999990123456789", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ] + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Group": [ + { "name": "name", "type": "string" }, + { "name": "members", "type": "Person[]" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person[]" }, + { "name": "contents", "type": "string" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallets", "type": "address[]" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/04-long_bytes-test.json b/tests/ragger/eip712/input_files/04-long_bytes-test.json new file mode 100644 index 0000000..c7dc888 --- /dev/null +++ b/tests/ragger/eip712/input_files/04-long_bytes-test.json @@ -0,0 +1,50 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "0x1123456789123456789123453678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678991234567892345678991234567892345678991234567892345678991234567892345678991234567892345678912345678923456789", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + } + ] + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Group": [ + { "name": "name", "type": "string" }, + { "name": "members", "type": "Person[]" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "Person[]" }, + { "name": "contents", "type": "bytes" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallets", "type": "address[]" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/05-signed_ints-test.json b/tests/ragger/eip712/input_files/05-signed_ints-test.json new file mode 100644 index 0000000..8a94649 --- /dev/null +++ b/tests/ragger/eip712/input_files/05-signed_ints-test.json @@ -0,0 +1,45 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "neg256" : "-256", + "pos256" : "256", + "neg128" : "-128", + "pos128" : "128", + "neg64" : "-64", + "pos64" : "64", + "neg32" : "-32", + "pos32" : "32", + "neg16" : "-16", + "pos16" : "16", + "neg8" : "-8", + "pos8" : "8" + }, + "primaryType": "Test", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Test": [ + { "name": "neg256", "type": "int256" }, + { "name": "pos256", "type": "int256" }, + { "name": "neg128", "type": "int128" }, + { "name": "pos128", "type": "int128" }, + { "name": "neg64", "type": "int64" }, + { "name": "pos64", "type": "int64" }, + { "name": "neg32", "type": "int32" }, + { "name": "pos32", "type": "int32" }, + { "name": "neg16", "type": "int16" }, + { "name": "pos16", "type": "int16" }, + { "name": "neg8", "type": "int8" }, + { "name": "pos8", "type": "int8" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/06-boolean-test.json b/tests/ragger/eip712/input_files/06-boolean-test.json new file mode 100644 index 0000000..5effe8d --- /dev/null +++ b/tests/ragger/eip712/input_files/06-boolean-test.json @@ -0,0 +1,25 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "Bueno" : true, + "NoBueno": false + }, + "primaryType": "Test", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Test": [ + { "name": "Bueno", "type": "bool" }, + { "name": "NoBueno", "type": "bool" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/07-fixed_bytes-test.json b/tests/ragger/eip712/input_files/07-fixed_bytes-test.json new file mode 100644 index 0000000..cb00886 --- /dev/null +++ b/tests/ragger/eip712/input_files/07-fixed_bytes-test.json @@ -0,0 +1,23 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "val" : "0x973bb640" + }, + "primaryType": "Test", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Test": [ + { "name": "val", "type": "bytes4" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/08-opensea-test.json b/tests/ragger/eip712/input_files/08-opensea-test.json new file mode 100644 index 0000000..35bd77a --- /dev/null +++ b/tests/ragger/eip712/input_files/08-opensea-test.json @@ -0,0 +1,153 @@ +{ + "domain" : { + "chainId" : 1, + "name" : "Wyvern Exchange Contract", + "verifyingContract" : "0x7f268357a8c2552623316e2562d90e642bb538e5", + "version" : "2.3" + }, + "message" : { + "basePrice" : "2000000000000000000", + "calldata" : "0x96809f90000000000000000000000000112f0732e59e7600768dfc35ba744b89f2356cd80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5ebdf2657ffc1fadfd73cf0a8cde95d50b62d3df8c0000000000000700000000320000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000000", + "exchange" : "0x7f268357a8c2552623316e2562d90e642bb538e5", + "expirationTime" : "1646089435", + "extra" : "0", + "feeMethod" : 1, + "feeRecipient" : "0x5b3256965e7c3cf26e11fcaf296dfc8807c01073", + "howToCall" : 1, + "listingTime" : "1645484541", + "maker" : "0x112f0732e59e7600768dfc35ba744b89f2356cd8", + "makerProtocolFee" : "0", + "makerRelayerFee" : "1250", + "nonce" : 0, + "paymentToken" : "0x0000000000000000000000000000000000000000", + "replacementPattern" : "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "saleKind" : 0, + "salt" : "21014297276898013168171430966355369260039074692095359200549020767078729356431", + "side" : 1, + "staticExtradata" : "0x", + "staticTarget" : "0x0000000000000000000000000000000000000000", + "taker" : "0x0000000000000000000000000000000000000000", + "takerProtocolFee" : "0", + "takerRelayerFee" : "0", + "target" : "0xbaf2127b49fc93cbca6269fade0f7f31df4c88a7" + }, + "primaryType" : "Order", + "types" : { + "EIP712Domain" : [ + { + "name" : "name", + "type" : "string" + }, + { + "name" : "version", + "type" : "string" + }, + { + "name" : "chainId", + "type" : "uint256" + }, + { + "name" : "verifyingContract", + "type" : "address" + } + ], + "Order" : [ + { + "name" : "exchange", + "type" : "address" + }, + { + "name" : "maker", + "type" : "address" + }, + { + "name" : "taker", + "type" : "address" + }, + { + "name" : "makerRelayerFee", + "type" : "uint256" + }, + { + "name" : "takerRelayerFee", + "type" : "uint256" + }, + { + "name" : "makerProtocolFee", + "type" : "uint256" + }, + { + "name" : "takerProtocolFee", + "type" : "uint256" + }, + { + "name" : "feeRecipient", + "type" : "address" + }, + { + "name" : "feeMethod", + "type" : "uint8" + }, + { + "name" : "side", + "type" : "uint8" + }, + { + "name" : "saleKind", + "type" : "uint8" + }, + { + "name" : "target", + "type" : "address" + }, + { + "name" : "howToCall", + "type" : "uint8" + }, + { + "name" : "calldata", + "type" : "bytes" + }, + { + "name" : "replacementPattern", + "type" : "bytes" + }, + { + "name" : "staticTarget", + "type" : "address" + }, + { + "name" : "staticExtradata", + "type" : "bytes" + }, + { + "name" : "paymentToken", + "type" : "address" + }, + { + "name" : "basePrice", + "type" : "uint256" + }, + { + "name" : "extra", + "type" : "uint256" + }, + { + "name" : "listingTime", + "type" : "uint256" + }, + { + "name" : "expirationTime", + "type" : "uint256" + }, + { + "name" : "salt", + "type" : "uint256" + }, + { + "name" : "nonce", + "type" : "uint256" + } + ] + } +} diff --git a/tests/ragger/eip712/input_files/09-rarible-test.json b/tests/ragger/eip712/input_files/09-rarible-test.json new file mode 100644 index 0000000..f5284a6 --- /dev/null +++ b/tests/ragger/eip712/input_files/09-rarible-test.json @@ -0,0 +1,110 @@ +{ + "domain" : { + "chainId" : 1, + "name" : "Exchange", + "verifyingContract" : "0x9757f2d2b135150bbeb65308d4a91804107cd8d6", + "version" : "2" + }, + "message" : { + "data" : "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000001cf0df2a5a20cd61d68d4489eebbf85b8d39e18a00000000000000000000000000000000000000000000000000000000000000fa", + "dataType" : "0x23d235ef", + "end" : 0, + "makeAsset" : { + "assetType" : { + "assetClass" : "0x973bb640", + "data" : "0x000000000000000000000000495f947276749ce646f68ac8c248420045cb7b5ebdf2657ffc1fadfd73cf0a8cde95d50b62d3df8c000000000000070000000032" + }, + "value" : "1" + }, + "maker" : "0x112f0732e59e7600768dfc35ba744b89f2356cd8", + "salt" : "0xdbf0f98bc1746711579dcce549a4cc4e866fb71bf2e185bfefbb7d32f325972e", + "start" : 0, + "takeAsset" : { + "assetType" : { + "assetClass" : "0xaaaebeba", + "data" : "0x" + }, + "value" : "2000000000000000000" + }, + "taker" : "0x0000000000000000000000000000000000000000" + }, + "primaryType" : "Order", + "types" : { + "Asset" : [ + { + "name" : "assetType", + "type" : "AssetType" + }, + { + "name" : "value", + "type" : "uint256" + } + ], + "AssetType" : [ + { + "name" : "assetClass", + "type" : "bytes4" + }, + { + "name" : "data", + "type" : "bytes" + } + ], + "EIP712Domain" : [ + { + "name" : "name", + "type" : "string" + }, + { + "name" : "version", + "type" : "string" + }, + { + "name" : "chainId", + "type" : "uint256" + }, + { + "name" : "verifyingContract", + "type" : "address" + } + ], + "Order" : [ + { + "name" : "maker", + "type" : "address" + }, + { + "name" : "makeAsset", + "type" : "Asset" + }, + { + "name" : "taker", + "type" : "address" + }, + { + "name" : "takeAsset", + "type" : "Asset" + }, + { + "name" : "salt", + "type" : "uint256" + }, + { + "name" : "start", + "type" : "uint256" + }, + { + "name" : "end", + "type" : "uint256" + }, + { + "name" : "dataType", + "type" : "bytes4" + }, + { + "name" : "data", + "type" : "bytes" + } + ] + } +} diff --git a/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json b/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json new file mode 100644 index 0000000..92d7963 --- /dev/null +++ b/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json @@ -0,0 +1,92 @@ +{ + "types": { + "EIP712Domain": [ + { + "type": "string", + "name": "name" + } + ], + "LDPSigningRequest": [ + { + "type": "string[3][]", + "name": "document" + }, + { + "type": "string[][4]", + "name": "proof" + }, + { + "type": "uint8[][][][]", + "name": "depthy" + } + ] + }, + "primaryType": "LDPSigningRequest", + "domain": { + "name": "Eip712Method2021" + }, + "message": { + "depthy": [ + [ + [ + [ + "1", + "2" + ], + [ + "3" + ] + ] + ] + ], + "document": [ + [ + "", + "", + "\"did:ethr:0xf7398bacf610bb4e3b567811279fcb3c41919f89\"" + ], + [ + "", + "", + "" + ], + [ + "", + "", + "\"2021-03-04T21:08:22.615Z\"^^" + ], + [ + "", + "", + "" + ], + [ + "", + "", + "" + ] + ], + "proof": [ + [ + "_:c14n0", + "", + "\"2021-03-04T21:08:22.616Z\"^^" + ], + [ + "_:c14n0", + "", + "" + ], + [ + "_:c14n0", + "", + "" + ], + [ + "_:c14n0", + "", + "" + ] + ] + } +} diff --git a/tests/ragger/eip712/input_files/11-complex_structs-test.json b/tests/ragger/eip712/input_files/11-complex_structs-test.json new file mode 100644 index 0000000..3433564 --- /dev/null +++ b/tests/ragger/eip712/input_files/11-complex_structs-test.json @@ -0,0 +1,78 @@ +{ + "domain": { + "chainId": 5, + "name": "Ether Mail", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" + }, + "message": { + "contents": "Hello, Bob!", + "from": { + "name": "Cow", + "wallets": [ + "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826", + "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF" + ] + }, + "to": { + "name": "test list", + "members": [ + { + "name": "Bob", + "wallets": [ + "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + "0xB0B0b0b0b0b0B000000000000000000000000000" + ] + }, + { + "name": "Alice", + "wallets": [ + "0xB0BdaBea57B0BDABeA57b0bdABEA57b0BDabEa57" + ] + } + ] + }, + "attach": { + "list": [ + { + "name": "first", + "size": "100" + }, + { + "name": "second", + "size": "3400" + } + ] + } + }, + "primaryType": "Mail", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } + ], + "Attachment": [ + { "name": "name", "type": "string" }, + { "name": "size", "type": "uint16" } + ], + "Attachments": [ + { "name": "list", "type": "Attachment[]" } + ], + "MailingList": [ + { "name": "name", "type": "string" }, + { "name": "members", "type": "Person[]" } + ], + "Mail": [ + { "name": "from", "type": "Person" }, + { "name": "to", "type": "MailingList" }, + { "name": "contents", "type": "string" }, + { "name": "attach", "type": "Attachments" } + ], + "Person": [ + { "name": "name", "type": "string" }, + { "name": "wallets", "type": "address[]" } + ] + } +} diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index 5f1aea9..db0710b 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -20,14 +20,14 @@ class P2Type(IntEnum): LEGACY_IMPLEM = 0x00 NEW_IMPLEM = 0x01, -class EIP712FieldType: +class EIP712FieldType(IntEnum): CUSTOM = 0, - INT = auto(), - UINT = auto(), - ADDRESS = auto(), - BOOL = auto(), - STRING = auto(), - FIXED_BYTES = auto(), + INT = auto() + UINT = auto() + ADDRESS = auto() + BOOL = auto() + STRING = auto() + FIX_BYTES = auto() DYN_BYTES = auto() @@ -66,14 +66,14 @@ class EthereumClientCmdBuilder: data = bytearray() typedesc = 0 typedesc |= (len(array_levels) > 0) << 7 - typedesc |= (type_size > 0) << 6 + typedesc |= (type_size != None) << 6 typedesc |= field_type data.append(typedesc) if field_type == EIP712FieldType.CUSTOM: data.append(len(type_name)) for char in type_name: data.append(ord(char)) - if type_size > 0: + if type_size != None: data.append(type_size) if len(array_levels) > 0: data.append(len(array_levels)) @@ -107,12 +107,17 @@ class EthereumClientCmdBuilder: data) def eip712_send_struct_impl_struct_field(self, data: bytearray) -> Iterator[bytes]: - while len(data > 0): + # Add a 16-bit integer with the data's byte length (network byte order) + data_w_length = bytearray() + data_w_length.append((len(data) & 0xff00) >> 8) + data_w_length.append(len(data) & 0x00ff) + data_w_length += data + while len(data_w_length) > 0: yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, P1Type.COMPLETE_SEND, P2Type.STRUCT_FIELD, - data[:0xff]) - data = data[0xff:] + data_w_length[:0xff]) + data_w_length = data_w_length[0xff:] def _format_bip32(self, bip32, data = bytearray()) -> bytearray: data.append(len(bip32)) @@ -195,17 +200,14 @@ class EthereumClient: return self._recv() def eip712_send_struct_impl_array(self, size: int): - send._send(self._cmd_builder.eip712_send_struct_impl_array(size)) + self._send(self._cmd_builder.eip712_send_struct_impl_array(size)) return self._recv() def eip712_send_struct_impl_struct_field(self, raw_value: bytes): ret = None - for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field( - InsType.EIP712_SEND_STRUCT_IMPL, - P1Type.COMPLETE_SEND, - P2Type.STRUCT_FIELD, - data[:0xff]): + for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value): self._send(apdu) + # TODO: Do clicks ret = self._recv() return ret diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index c3b4de1..0b06f55 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -1,6 +1,7 @@ import os import fnmatch from ethereum_client import EthereumClient +from eip712 import InputData def test_eip712_legacy(app_client: EthereumClient): bip32 = [ @@ -20,3 +21,15 @@ def test_eip712_legacy(app_client: EthereumClient): assert v == bytes.fromhex("1c") assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64") assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") + + +def test_eip712_new(app_client: EthereumClient): + if app_client._client.firmware.device == "nanos": # not supported + return + + # Loop through JSON files + for file in os.scandir("./eip712/input_files"): + if fnmatch.fnmatch(file, "*-test.json"): + print(file.path) + InputData.process_file(app_client, file.path, False) + assert 1 == 1 From 311c2660845219e351e01e54d7bb47c810eaf3c1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 28 Jul 2022 17:29:10 +0200 Subject: [PATCH 141/184] Fixes problem with field values too long for one APDU --- tests/ragger/eip712/InputData.py | 2 +- tests/ragger/ethereum_client.py | 3 ++- tests/ragger/test_eip712.py | 18 ++++++++++-------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 6971293..7b66801 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -383,5 +383,5 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering = Fals return False # sign - send_sign() + #send_sign() return True diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index db0710b..a69f323 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -113,8 +113,9 @@ class EthereumClientCmdBuilder: data_w_length.append(len(data) & 0x00ff) data_w_length += data while len(data_w_length) > 0: + p1 = P1Type.PARTIAL_SEND if len(data_w_length) > 0xff else P1Type.COMPLETE_SEND yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, - P1Type.COMPLETE_SEND, + p1, P2Type.STRUCT_FIELD, data_w_length[:0xff]) data_w_length = data_w_length[0xff:] diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 0b06f55..9367dea 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -3,15 +3,16 @@ import fnmatch from ethereum_client import EthereumClient from eip712 import InputData -def test_eip712_legacy(app_client: EthereumClient): - bip32 = [ - 0x8000002c, - 0x8000003c, - 0x80000000, - 0, - 0 - ] +bip32 = [ + 0x8000002c, + 0x8000003c, + 0x80000000, + 0, + 0 +] + +def test_eip712_legacy(app_client: EthereumClient): v, r, s = app_client.eip712_sign_legacy( bip32, bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'), @@ -32,4 +33,5 @@ def test_eip712_new(app_client: EthereumClient): if fnmatch.fnmatch(file, "*-test.json"): print(file.path) InputData.process_file(app_client, file.path, False) + app_client.eip712_sign_new(bip32) assert 1 == 1 From 243f33bbb74075e6d4a53b9a27a2fd956b925408 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 1 Aug 2022 11:22:51 +0200 Subject: [PATCH 142/184] Fix initial state of buffer in BIP32 formatting function --- tests/ragger/ethereum_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index a69f323..02a705e 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -120,7 +120,7 @@ class EthereumClientCmdBuilder: data_w_length[:0xff]) data_w_length = data_w_length[0xff:] - def _format_bip32(self, bip32, data = bytearray()) -> bytearray: + def _format_bip32(self, bip32, data: bytearray) -> bytearray: data.append(len(bip32)) for idx in bip32: data.append((idx & 0xff000000) >> 24) @@ -130,7 +130,7 @@ class EthereumClientCmdBuilder: return data def eip712_sign_new(self, bip32) -> bytes: - data = self._format_bip32(bip32) + data = self._format_bip32(bip32, bytearray()) return self._serialize(InsType.EIP712_SIGN, P1Type.COMPLETE_SEND, P2Type.NEW_IMPLEM, @@ -140,7 +140,7 @@ class EthereumClientCmdBuilder: bip32, domain_hash: bytes, message_hash: bytes) -> bytes: - data = self._format_bip32(bip32) + data = self._format_bip32(bip32, bytearray()) data += domain_hash data += message_hash return self._serialize(InsType.EIP712_SIGN, From 341b7c2adf33e039d623610af7cfae1be86fcfaa Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 2 Aug 2022 16:30:10 +0200 Subject: [PATCH 143/184] Modified all the nominal eip712 test input files --- .../input_files/00-simple_mail-test.json | 4 +- .../01-addresses_array_mail-test.json | 4 +- .../02-recipients_array_mail-test.json | 4 +- .../input_files/03-long_string-test.json | 6 +- .../input_files/04-long_bytes-test.json | 6 +- .../input_files/05-signed_ints-test.json | 4 +- .../eip712/input_files/06-boolean-test.json | 4 +- .../input_files/07-fixed_bytes-test.json | 16 +++- .../10-multidimensional_arrays-test.json | 86 ++++--------------- .../input_files/11-complex_structs-test.json | 4 +- 10 files changed, 48 insertions(+), 90 deletions(-) diff --git a/tests/ragger/eip712/input_files/00-simple_mail-test.json b/tests/ragger/eip712/input_files/00-simple_mail-test.json index ef5ce4e..0b4fa66 100644 --- a/tests/ragger/eip712/input_files/00-simple_mail-test.json +++ b/tests/ragger/eip712/input_files/00-simple_mail-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Simple Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, diff --git a/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json b/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json index e17e626..cc54d8c 100644 --- a/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json +++ b/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 1337, - "name": "Ether Mail", + "chainId": 1, + "name": "Addresses Array Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, diff --git a/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json b/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json index 51e8fef..1d27ff0 100644 --- a/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json +++ b/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Recipients Array Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, diff --git a/tests/ragger/eip712/input_files/03-long_string-test.json b/tests/ragger/eip712/input_files/03-long_string-test.json index 03ee734..d286934 100644 --- a/tests/ragger/eip712/input_files/03-long_string-test.json +++ b/tests/ragger/eip712/input_files/03-long_string-test.json @@ -1,12 +1,12 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Long String Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, "message": { - "contents": "Hello, Bob! 012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012312345678012345678012345678012345678012344444444444444444444123456780123456780123456780123456780123456780123456780123456789999999999999999999999999999990123456789", + "contents": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam fermentum interdum tortor, nec elementum enim dignissim ac. Proin at leo sit amet nisl ultrices mollis quis a nunc. Aliquam lobortis a libero non lobortis. Morbi elementum eleifend ante et malesuada. Proin eget fermentum risus. Vestibulum cursus dignissim mollis. In viverra, mi ac accumsan elementum, tellus metus dictum nisi, vel tincidunt odio erat in odio. Aenean nec lorem auctor, tempor ante eget, aliquet purus. Donec fringilla felis iaculis, venenatis ligula egestas, dignissim orci. Aliquam id rhoncus ante, cursus consectetur mauris. Nunc porttitor urna urna, et tristique eros maximus vestibulum. Curabitur pretium a est non porttitor. Cras mollis efficitur sem ut porta. Proin commodo volutpat iaculis. Maecenas ut nulla mi. Aenean ultrices sollicitudin enim, non luctus magna efficitur et. Sed varius sem odio, in sodales erat porta in. Nunc blandit finibus maximus. Mauris nunc tellus, interdum non laoreet sed, aliquet non.", "from": { "name": "Cow", "wallets": [ diff --git a/tests/ragger/eip712/input_files/04-long_bytes-test.json b/tests/ragger/eip712/input_files/04-long_bytes-test.json index c7dc888..5bcfe55 100644 --- a/tests/ragger/eip712/input_files/04-long_bytes-test.json +++ b/tests/ragger/eip712/input_files/04-long_bytes-test.json @@ -1,12 +1,12 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Long Bytes Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, "message": { - "contents": "0x1123456789123456789123453678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678912345678991234567892345678991234567892345678991234567892345678991234567892345678991234567892345678912345678923456789", + "contents": "0x11223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900", "from": { "name": "Cow", "wallets": [ diff --git a/tests/ragger/eip712/input_files/05-signed_ints-test.json b/tests/ragger/eip712/input_files/05-signed_ints-test.json index 8a94649..a6279d6 100644 --- a/tests/ragger/eip712/input_files/05-signed_ints-test.json +++ b/tests/ragger/eip712/input_files/05-signed_ints-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Signed Ints test", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, diff --git a/tests/ragger/eip712/input_files/06-boolean-test.json b/tests/ragger/eip712/input_files/06-boolean-test.json index 5effe8d..6b29025 100644 --- a/tests/ragger/eip712/input_files/06-boolean-test.json +++ b/tests/ragger/eip712/input_files/06-boolean-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Boolean test", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, diff --git a/tests/ragger/eip712/input_files/07-fixed_bytes-test.json b/tests/ragger/eip712/input_files/07-fixed_bytes-test.json index cb00886..bf2db40 100644 --- a/tests/ragger/eip712/input_files/07-fixed_bytes-test.json +++ b/tests/ragger/eip712/input_files/07-fixed_bytes-test.json @@ -1,12 +1,16 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Fixed bytes test", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, "message": { - "val" : "0x973bb640" + "val1": "0xae", + "val4": "0x973bb640", + "val8": "0xac3608fa074a22a0", + "val16": "0x24e62129cc3ed3df6f8f3cd1e95b812a", + "val32": "0xb5d679d10bf948280080e802ce9fde218b0f8c442c47bf4ab05657d8da04d1da" }, "primaryType": "Test", "types": { @@ -17,7 +21,11 @@ { "name": "verifyingContract", "type": "address" } ], "Test": [ - { "name": "val", "type": "bytes4" } + { "name": "val1", "type": "bytes1" }, + { "name": "val4", "type": "bytes4" }, + { "name": "val8", "type": "bytes8" }, + { "name": "val16", "type": "bytes16" }, + { "name": "val32", "type": "bytes32" } ] } } diff --git a/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json b/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json index 92d7963..716193b 100644 --- a/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json +++ b/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json @@ -1,29 +1,21 @@ { "types": { "EIP712Domain": [ - { - "type": "string", - "name": "name" - } + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" }, + { "name": "chainId", "type": "uint256" }, + { "name": "verifyingContract", "type": "address" } ], - "LDPSigningRequest": [ - { - "type": "string[3][]", - "name": "document" - }, - { - "type": "string[][4]", - "name": "proof" - }, - { - "type": "uint8[][][][]", - "name": "depthy" - } + "Test": [ + { "type": "uint8[][][][]", "name": "depthy" } ] }, - "primaryType": "LDPSigningRequest", + "primaryType": "Test", "domain": { - "name": "Eip712Method2021" + "chainId": 1, + "name": "Depth Test", + "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", + "version": "1" }, "message": { "depthy": [ @@ -36,57 +28,15 @@ [ "3" ] + ], + [ + [ + "4", + "5", + "6" + ] ] ] - ], - "document": [ - [ - "", - "", - "\"did:ethr:0xf7398bacf610bb4e3b567811279fcb3c41919f89\"" - ], - [ - "", - "", - "" - ], - [ - "", - "", - "\"2021-03-04T21:08:22.615Z\"^^" - ], - [ - "", - "", - "" - ], - [ - "", - "", - "" - ] - ], - "proof": [ - [ - "_:c14n0", - "", - "\"2021-03-04T21:08:22.616Z\"^^" - ], - [ - "_:c14n0", - "", - "" - ], - [ - "_:c14n0", - "", - "" - ], - [ - "_:c14n0", - "", - "" - ] ] } } diff --git a/tests/ragger/eip712/input_files/11-complex_structs-test.json b/tests/ragger/eip712/input_files/11-complex_structs-test.json index 3433564..a3ad755 100644 --- a/tests/ragger/eip712/input_files/11-complex_structs-test.json +++ b/tests/ragger/eip712/input_files/11-complex_structs-test.json @@ -1,7 +1,7 @@ { "domain": { - "chainId": 5, - "name": "Ether Mail", + "chainId": 1, + "name": "Complex Structs Mail", "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC", "version": "1" }, From ca04b5e21399d1d5dfae466c656960d4b46345e5 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 2 Aug 2022 16:31:11 +0200 Subject: [PATCH 144/184] Now properly handles UI clicks in EIP712 automated tests --- tests/ragger/conftest.py | 4 +- tests/ragger/ethereum_client.py | 101 +++++++++++++++++++++++--------- tests/ragger/test_eip712.py | 36 ++++++++---- 3 files changed, 100 insertions(+), 41 deletions(-) diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py index 5f9efae..09c6227 100644 --- a/tests/ragger/conftest.py +++ b/tests/ragger/conftest.py @@ -6,8 +6,8 @@ from ethereum_client import EthereumClient ELFS_DIR = (Path(__file__).parent.parent / "elfs").resolve() FWS = [ - Firmware("nanox", "2.0.2"), Firmware("nanos", "2.1"), + Firmware("nanox", "2.0.2"), Firmware("nanosp", "1.0.3") ] @@ -54,4 +54,4 @@ def backend_client(backend_name: str, firmware: Firmware) -> BackendInterface: # This final fixture will return the properly configured app client, to be used in tests @pytest.fixture def app_client(backend_client: BackendInterface) -> EthereumClient: - yield EthereumClient(backend_client) + return EthereumClient(backend_client) diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index 02a705e..fc3c4c5 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -1,7 +1,10 @@ +from contextlib import contextmanager +from typing import Generator from enum import IntEnum, auto from typing import Iterator from ragger.backend import BackendInterface from ragger.utils import RAPDU +import signal class InsType(IntEnum): EIP712_SEND_STRUCT_DEF = 0x1a, @@ -164,23 +167,38 @@ class EthereumResponseParser: return v, r, s - class EthereumClient: + _verbose_eip712 = False + def __init__(self, client: BackendInterface, debug: bool = False): self._client = client self._debug = debug self._cmd_builder = EthereumClientCmdBuilder() self._resp_parser = EthereumResponseParser() + self._click_delay = 1/4 + signal.signal(signal.SIGALRM, self._click_signal_timeout) - def _send(self, payload: bytearray) -> None: - self._client.send_raw(payload) + def _send(self, payload: bytearray): + return self._client.exchange_async_raw(payload) def _recv(self) -> RAPDU: - return self._client.receive() + return self._client._last_async_response + + def _click_signal_timeout(self, signum: int, frame): + self._client.right_click() + + def _enable_click_until_response(self): + signal.setitimer(signal.ITIMER_REAL, + self._click_delay, + self._click_delay) + + def _disable_click_until_response(self): + signal.setitimer(signal.ITIMER_REAL, 0, 0) def eip712_send_struct_def_struct_name(self, name: str): - self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)) - return self._recv() + with self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)): + pass + return self._recv().status == 0x9000 def eip712_send_struct_def_struct_field(self, field_type: EIP712FieldType, @@ -188,52 +206,77 @@ class EthereumClient: type_size: int, array_levels: [], key_name: str): - self._send(self._cmd_builder.eip712_send_struct_def_struct_field( + with self._send(self._cmd_builder.eip712_send_struct_def_struct_field( field_type, type_name, type_size, array_levels, - key_name)) + key_name)): + pass return self._recv() def eip712_send_struct_impl_root_struct(self, name: str): - self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)) + with self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)): + self._enable_click_until_response() + self._disable_click_until_response() return self._recv() def eip712_send_struct_impl_array(self, size: int): - self._send(self._cmd_builder.eip712_send_struct_impl_array(size)) + with self._send(self._cmd_builder.eip712_send_struct_impl_array(size)): + pass return self._recv() def eip712_send_struct_impl_struct_field(self, raw_value: bytes): - ret = None for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value): - self._send(apdu) - # TODO: Do clicks - ret = self._recv() - return ret + with self._send(apdu): + self._enable_click_until_response() + self._disable_click_until_response() + assert self._recv().status == 0x9000 def eip712_sign_new(self, bip32): - self._send(self._cmd_builder.eip712_sign_new(bip32)) - return self._recv() + with self._send(self._cmd_builder.eip712_sign_new(bip32)): + if not self._verbose_eip712: # need to skip the message hash + self._client.right_click() + self._client.right_click() + self._client.both_click() # approve signature + resp = self._recv() + assert resp.status == 0x9000 + return self._resp_parser.sign(resp.data) def eip712_sign_legacy(self, bip32, domain_hash: bytes, message_hash: bytes): - self._send(self._cmd_builder.eip712_sign_legacy(bip32, - domain_hash, - message_hash)) - self._client.right_click() # sign typed message screen - for _ in range(2): # two hashes (domain + message) - if self._client.firmware.device == "nanos": - screens_per_hash = 4 - else: - screens_per_hash = 2 - for _ in range(screens_per_hash): - self._client.right_click() - self._client.both_click() # approve signature + with self._send(self._cmd_builder.eip712_sign_legacy(bip32, + domain_hash, + message_hash)): + self._client.right_click() # sign typed message screen + for _ in range(2): # two hashes (domain + message) + if self._client.firmware.device == "nanos": + screens_per_hash = 4 + else: + screens_per_hash = 2 + for _ in range(screens_per_hash): + self._client.right_click() + self._client.both_click() # approve signature resp = self._recv() assert resp.status == 0x9000 return self._resp_parser.sign(resp.data) + + def setting_toggle_verbose_eip712(self): + # Go to settings + self._client.right_click() + self._client.right_click() + self._client.both_click() + # Go to verbose eip712 + self._client.right_click() + self._client.right_click() + self._client.right_click() + self._client.both_click() + # Go back + self._client.right_click() + self._client.both_click() + + self._verbose_eip712 = not self._verbose_eip712 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 9367dea..597989c 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -1,5 +1,7 @@ +import pytest import os import fnmatch +from typing import List from ethereum_client import EthereumClient from eip712 import InputData @@ -12,6 +14,22 @@ bip32 = [ ] +def input_files() -> List[str]: + files = [] + for file in os.scandir("./eip712/input_files"): + if fnmatch.fnmatch(file, "*-test.json"): + files.append(file.path) + return sorted(files) + +@pytest.fixture(params=input_files()) +def input_file(request) -> str: + return request.param + +@pytest.fixture(params=[True, False]) +def verbose(request) -> bool: + return request.param + + def test_eip712_legacy(app_client: EthereumClient): v, r, s = app_client.eip712_sign_legacy( bip32, @@ -24,14 +42,12 @@ def test_eip712_legacy(app_client: EthereumClient): assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") -def test_eip712_new(app_client: EthereumClient): - if app_client._client.firmware.device == "nanos": # not supported - return +def test_eip712_new(app_client: EthereumClient, input_file, verbose): + if app_client._client.firmware.device != "nanos": # not supported + print("=====> %s" % (input_file)) - # Loop through JSON files - for file in os.scandir("./eip712/input_files"): - if fnmatch.fnmatch(file, "*-test.json"): - print(file.path) - InputData.process_file(app_client, file.path, False) - app_client.eip712_sign_new(bip32) - assert 1 == 1 + if verbose: + app_client.setting_toggle_verbose_eip712() + InputData.process_file(app_client, input_file, False) + v, r, s = app_client.eip712_sign_new(bip32) + assert 1 == 1 # TODO: Replace by the actual v,r,s asserts From 5e9b426b3e7a58d0a7c4bec44631f4df7e86fc49 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 3 Aug 2022 11:56:26 +0200 Subject: [PATCH 145/184] EIP-712 tests now have reference response signatures they can check against --- .../eip712/input_files/00-simple_mail.ini | 4 ++++ .../input_files/01-addresses_array_mail.ini | 4 ++++ .../input_files/02-recipients_array_mail.ini | 4 ++++ .../eip712/input_files/03-long_string.ini | 4 ++++ .../eip712/input_files/04-long_bytes.ini | 4 ++++ .../eip712/input_files/05-signed_ints.ini | 4 ++++ .../ragger/eip712/input_files/06-boolean.ini | 4 ++++ .../eip712/input_files/07-fixed_bytes.ini | 4 ++++ .../ragger/eip712/input_files/08-opensea.ini | 4 ++++ .../ragger/eip712/input_files/09-rarible.ini | 4 ++++ .../10-multidimensional_arrays.ini | 4 ++++ .../eip712/input_files/11-complex_structs.ini | 4 ++++ tests/ragger/test_eip712.py | 22 ++++++++++++++++--- 13 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 tests/ragger/eip712/input_files/00-simple_mail.ini create mode 100644 tests/ragger/eip712/input_files/01-addresses_array_mail.ini create mode 100644 tests/ragger/eip712/input_files/02-recipients_array_mail.ini create mode 100644 tests/ragger/eip712/input_files/03-long_string.ini create mode 100644 tests/ragger/eip712/input_files/04-long_bytes.ini create mode 100644 tests/ragger/eip712/input_files/05-signed_ints.ini create mode 100644 tests/ragger/eip712/input_files/06-boolean.ini create mode 100644 tests/ragger/eip712/input_files/07-fixed_bytes.ini create mode 100644 tests/ragger/eip712/input_files/08-opensea.ini create mode 100644 tests/ragger/eip712/input_files/09-rarible.ini create mode 100644 tests/ragger/eip712/input_files/10-multidimensional_arrays.ini create mode 100644 tests/ragger/eip712/input_files/11-complex_structs.ini diff --git a/tests/ragger/eip712/input_files/00-simple_mail.ini b/tests/ragger/eip712/input_files/00-simple_mail.ini new file mode 100644 index 0000000..62d03bd --- /dev/null +++ b/tests/ragger/eip712/input_files/00-simple_mail.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = 23599abd6c4b631e42770c112b5955907fe91339f1ea1e102f7682262ca178b9 +s = 29fc94518588165114b4c4acb4d73e6d028dfb051d90e517b3b4746e04eb0f5f diff --git a/tests/ragger/eip712/input_files/01-addresses_array_mail.ini b/tests/ragger/eip712/input_files/01-addresses_array_mail.ini new file mode 100644 index 0000000..d972658 --- /dev/null +++ b/tests/ragger/eip712/input_files/01-addresses_array_mail.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = 3f084a471e6158bce792287500d62d40061acc1864180ed2da7a704bf3aced0f +s = 3b799ced9e48cda152b4b9a4b7f45e3119dc7acdf16710a73800b4e336fa1b40 diff --git a/tests/ragger/eip712/input_files/02-recipients_array_mail.ini b/tests/ragger/eip712/input_files/02-recipients_array_mail.ini new file mode 100644 index 0000000..8e8dcff --- /dev/null +++ b/tests/ragger/eip712/input_files/02-recipients_array_mail.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = 49dd2aa96d7494e0cd9111f19f87ac50194e4bbc61ea9f4bb86d674da0ae7721 +s = 7a12ddd9083b4caaabd2fb80df6de1d5d926c0e8a73bf371a45e231d409d79d6 diff --git a/tests/ragger/eip712/input_files/03-long_string.ini b/tests/ragger/eip712/input_files/03-long_string.ini new file mode 100644 index 0000000..9b2740a --- /dev/null +++ b/tests/ragger/eip712/input_files/03-long_string.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = b23ffac2cb350fd6e7d06ec4b981fe016d33426d753c870e7e753797cc43bb1f +s = 37948a656fa3403e21956ef10c8d3152f7ce22cc252d958c9f9249435090f426 diff --git a/tests/ragger/eip712/input_files/04-long_bytes.ini b/tests/ragger/eip712/input_files/04-long_bytes.ini new file mode 100644 index 0000000..14018b7 --- /dev/null +++ b/tests/ragger/eip712/input_files/04-long_bytes.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = db18ea1b9757773385138d0802fb2f8107c3e45882962b8e0c6789eccdbfab05 +s = 3d66d4dee47916fb7fec39a538ad8d5e94fbc92f99327410716180ab07591218 diff --git a/tests/ragger/eip712/input_files/05-signed_ints.ini b/tests/ragger/eip712/input_files/05-signed_ints.ini new file mode 100644 index 0000000..754b2b3 --- /dev/null +++ b/tests/ragger/eip712/input_files/05-signed_ints.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = 50fb2861367daf3b5b73cac277e698b27bf7627a462fade1acb5a2ef285ba8ae +s = 131a62515a0a5c4b35c5cb672b46f562151c45508d8efcdf78c4608bc14c5f30 diff --git a/tests/ragger/eip712/input_files/06-boolean.ini b/tests/ragger/eip712/input_files/06-boolean.ini new file mode 100644 index 0000000..e7de2b3 --- /dev/null +++ b/tests/ragger/eip712/input_files/06-boolean.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = 929681d77ed88cd1adef57185a0cd7b3a268aca5d4122b8c0acfd2ce4c0afb18 +s = 5afe8e3004c182b6b02fe7559c26f20f4133ad9b17223658ccd9061b33b021cf diff --git a/tests/ragger/eip712/input_files/07-fixed_bytes.ini b/tests/ragger/eip712/input_files/07-fixed_bytes.ini new file mode 100644 index 0000000..e2f661e --- /dev/null +++ b/tests/ragger/eip712/input_files/07-fixed_bytes.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = e021d88afc50079b0341b01193c4687c47b85bcd6749fe69e0b87521d65a1847 +s = 5b7670d2a67c781a11164920403db0f7707161e81d88226cdbf91298390dfeda diff --git a/tests/ragger/eip712/input_files/08-opensea.ini b/tests/ragger/eip712/input_files/08-opensea.ini new file mode 100644 index 0000000..dfef6bb --- /dev/null +++ b/tests/ragger/eip712/input_files/08-opensea.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = 1539547ae7cf8ebcd3eabfb57cd2b1fb7775ce757c3f4a307c7425d35b7bfff7 +s = 47248cb61e554c1f90af6331d9c9e51cbb8655667514194f509abe097a032319 diff --git a/tests/ragger/eip712/input_files/09-rarible.ini b/tests/ragger/eip712/input_files/09-rarible.ini new file mode 100644 index 0000000..d880f0c --- /dev/null +++ b/tests/ragger/eip712/input_files/09-rarible.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = 341bca1c0dfd805d4befc21500084424dbe559c7aafd78d8fb461c0c76dfea1d +s = 33ebb7b6fe0691961cd8b263faac20ecbbdcaef3febb57eb76614cad629080ea diff --git a/tests/ragger/eip712/input_files/10-multidimensional_arrays.ini b/tests/ragger/eip712/input_files/10-multidimensional_arrays.ini new file mode 100644 index 0000000..63113be --- /dev/null +++ b/tests/ragger/eip712/input_files/10-multidimensional_arrays.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = d11a91bdf7836288818875d046452061d565cc6dc1bf3dd6216ab27ef9a2844f +s = 4f6bda8ac4c39721aff7ae08989897ede9d573085a192d03ab0eb7735d2ef403 diff --git a/tests/ragger/eip712/input_files/11-complex_structs.ini b/tests/ragger/eip712/input_files/11-complex_structs.ini new file mode 100644 index 0000000..ec35ea3 --- /dev/null +++ b/tests/ragger/eip712/input_files/11-complex_structs.ini @@ -0,0 +1,4 @@ +[signature] +v = 1c +r = cce2e63aaac6a5f9a74684d8fdddcbc7f3b27aa17235bfab89226821ead933b6 +s = 3f3c93977abcc3f8cc9a3dc1ecc02dbca14aca1a6ecb2fb6ca3d7c713ace1ec4 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 597989c..e218f5c 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -4,6 +4,8 @@ import fnmatch from typing import List from ethereum_client import EthereumClient from eip712 import InputData +from pathlib import Path +from configparser import ConfigParser bip32 = [ 0x8000002c, @@ -23,7 +25,7 @@ def input_files() -> List[str]: @pytest.fixture(params=input_files()) def input_file(request) -> str: - return request.param + return Path(request.param) @pytest.fixture(params=[True, False]) def verbose(request) -> bool: @@ -42,12 +44,26 @@ def test_eip712_legacy(app_client: EthereumClient): assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") -def test_eip712_new(app_client: EthereumClient, input_file, verbose): +def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose): if app_client._client.firmware.device != "nanos": # not supported print("=====> %s" % (input_file)) + test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1])) + conf_file = "%s.ini" % (test_path) + + config = ConfigParser() + config.read(conf_file) + + # sanity check + assert "signature" in config.sections() + assert "v" in config["signature"] + assert "r" in config["signature"] + assert "s" in config["signature"] if verbose: app_client.setting_toggle_verbose_eip712() InputData.process_file(app_client, input_file, False) v, r, s = app_client.eip712_sign_new(bip32) - assert 1 == 1 # TODO: Replace by the actual v,r,s asserts + + assert v == bytes.fromhex(config["signature"]["v"]) + assert r == bytes.fromhex(config["signature"]["r"]) + assert s == bytes.fromhex(config["signature"]["s"]) From 0d38bea388dfda05da7fefd2ebed839f3853456f Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 4 Aug 2022 17:00:55 +0200 Subject: [PATCH 146/184] Ragger tests - Refactored the handling of the app settings --- tests/ragger/ethereum_client.py | 60 +++++++++++++++++++++++---------- tests/ragger/test_eip712.py | 15 ++++++--- 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index fc3c4c5..81a1af6 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -1,7 +1,6 @@ from contextlib import contextmanager -from typing import Generator from enum import IntEnum, auto -from typing import Iterator +from typing import Iterator, Dict, List from ragger.backend import BackendInterface from ragger.utils import RAPDU import signal @@ -33,6 +32,19 @@ class EIP712FieldType(IntEnum): FIX_BYTES = auto() DYN_BYTES = auto() +class SettingType(IntEnum): + BLIND_SIGNING = 0, + DEBUG_DATA = auto() + NONCE = auto() + VERBOSE_EIP712 = auto() + +class Setting: + devices: List[str] + value: bool + + def __init__(self, d: List[str]): + self.devices = d + class EthereumClientCmdBuilder: _CLA: int = 0xE0 @@ -168,15 +180,30 @@ class EthereumResponseParser: return v, r, s class EthereumClient: - _verbose_eip712 = False + _settings: Dict[SettingType, Setting] = { + SettingType.BLIND_SIGNING: Setting( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.DEBUG_DATA: Setting( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.NONCE: Setting( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.VERBOSE_EIP712: Setting( + [ "nanox", "nanosp" ] + ) + } + _click_delay = 1/4 def __init__(self, client: BackendInterface, debug: bool = False): self._client = client self._debug = debug self._cmd_builder = EthereumClientCmdBuilder() self._resp_parser = EthereumResponseParser() - self._click_delay = 1/4 signal.signal(signal.SIGALRM, self._click_signal_timeout) + for setting in self._settings.values(): + setting.value = False def _send(self, payload: bytearray): return self._client.exchange_async_raw(payload) @@ -235,7 +262,7 @@ class EthereumClient: def eip712_sign_new(self, bip32): with self._send(self._cmd_builder.eip712_sign_new(bip32)): - if not self._verbose_eip712: # need to skip the message hash + if not self._settings[SettingType.VERBOSE_EIP712].value: # need to skip the message hash self._client.right_click() self._client.right_click() self._client.both_click() # approve signature @@ -265,18 +292,17 @@ class EthereumClient: assert resp.status == 0x9000 return self._resp_parser.sign(resp.data) - def setting_toggle_verbose_eip712(self): + def settings_set(self, new_values: Dict[SettingType, bool]): # Go to settings - self._client.right_click() - self._client.right_click() - self._client.both_click() - # Go to verbose eip712 - self._client.right_click() - self._client.right_click() - self._client.right_click() - self._client.both_click() - # Go back - self._client.right_click() + for _ in range(2): + self._client.right_click() self._client.both_click() - self._verbose_eip712 = not self._verbose_eip712 + for enum in self._settings.keys(): + if self._client.firmware.device in self._settings[enum].devices: + if enum in new_values.keys(): + if new_values[enum] != self._settings[enum].value: + self._client.both_click() + self._settings[enum].value = new_values[enum] + self._client.right_click() + self._client.both_click() diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index e218f5c..1645013 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -2,7 +2,7 @@ import pytest import os import fnmatch from typing import List -from ethereum_client import EthereumClient +from ethereum_client import EthereumClient, SettingType from eip712 import InputData from pathlib import Path from configparser import ConfigParser @@ -44,9 +44,9 @@ def test_eip712_legacy(app_client: EthereumClient): assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55") -def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose): - if app_client._client.firmware.device != "nanos": # not supported - print("=====> %s" % (input_file)) +def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, filtering: bool): + print("=====> %s" % (input_file)) + if app_client._client.firmware.device != "nanos": test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1])) conf_file = "%s.ini" % (test_path) @@ -60,10 +60,15 @@ def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose): assert "s" in config["signature"] if verbose: - app_client.setting_toggle_verbose_eip712() + app_client.settings_set({ + SettingType.VERBOSE_EIP712: True + }) + InputData.process_file(app_client, input_file, False) v, r, s = app_client.eip712_sign_new(bip32) assert v == bytes.fromhex(config["signature"]["v"]) assert r == bytes.fromhex(config["signature"]["r"]) assert s == bytes.fromhex(config["signature"]["s"]) + else: + print("Not supported by LNS") From e8f58bdce76dc2f3aefb6849abaf903e324bac02 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 4 Aug 2022 18:25:02 +0200 Subject: [PATCH 147/184] Ragger tests now have EIP721 filtering --- tests/ragger/eip712/InputData.py | 45 ++++++----- .../11-complex_structs-filter.json | 9 +++ tests/ragger/ethereum_client.py | 76 +++++++++++++++---- tests/ragger/test_eip712.py | 31 +++++--- 4 files changed, 116 insertions(+), 45 deletions(-) create mode 100644 tests/ragger/eip712/input_files/11-complex_structs-filter.json diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 7b66801..8f63188 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import os import json import sys import re @@ -9,6 +10,7 @@ from ecdsa import SigningKey from ecdsa.util import sigencode_der import pdb from ethereum_client import EthereumClient, EIP712FieldType +import base64 # global variables app_client: EthereumClient = None @@ -198,7 +200,7 @@ def send_struct_impl_field(value, field): data = encoding_functions[field["enum"]](value, field["typesize"]) - if False:#args.filtering: + if filtering_paths: path = ".".join(current_path) if path in filtering_paths.keys(): send_filtering_field_name(filtering_paths[path]) @@ -257,10 +259,10 @@ def send_sign(): #send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path) print("send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path)") -def send_filtering_activate(): - #send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray()) - print("send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray())") - +#def send_filtering_activate(): +# #send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray()) +# print("send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray())") +# def send_filtering_info(p1, display_name, sig): payload = bytearray() payload.append(len(display_name)) @@ -272,7 +274,7 @@ def send_filtering_info(p1, display_name, sig): print("send_apdu(INS_FILTERING, p1, 0x00, payload)") # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures -def send_filtering_contract_name(display_name): +def send_filtering_contract_name(display_name: str): global sig_ctx msg = bytearray() @@ -283,7 +285,7 @@ def send_filtering_contract_name(display_name): msg.append(ord(char)) sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) - send_filtering_info(P1_CONTRACT_NAME, display_name, sig) + app_client.eip712_filtering_send_contract_name(display_name, sig) # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures def send_filtering_field_name(display_name): @@ -300,11 +302,11 @@ def send_filtering_field_name(display_name): for char in display_name: msg.append(ord(char)) sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) - send_filtering_info(P1_FIELD_NAME, display_name, sig) + app_client.eip712_filtering_send_field_name(display_name, sig) -def read_filtering_file(domain, message): +def read_filtering_file(domain, message, filtering_file_path): data_json = None - with open("%s-filter.json" % (args.JSON_FILE)) as data: + with open(filtering_file_path) as data: data_json = json.load(data) return data_json @@ -319,8 +321,11 @@ def prepare_filtering(filtr_data, message): def init_signature_context(types, domain): global sig_ctx - with open(args.keypath, "r") as priv_file: - sig_ctx["key"] = SigningKey.from_pem(priv_file.read(), hashlib.sha256) + env_key = os.getenv("CAL_SIGNATURE_TEST_KEY") + if env_key: + key = base64.b64decode(env_key).decode() # base 64 string -> decode bytes -> string + print(key) + sig_ctx["key"] = SigningKey.from_pem(key, hashlib.sha256) caddr = domain["verifyingContract"] if caddr.startswith("0x"): caddr = caddr[2:] @@ -337,7 +342,7 @@ def init_signature_context(types, domain): return True return False -def process_file(aclient: EthereumClient, input_file_path: str, filtering = False) -> bool: +def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_path = None) -> bool: global sig_ctx global app_client @@ -350,10 +355,10 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering = Fals domain = data_json["domain"] message = data_json["message"] - if filtering: + if filtering_file_path: if not init_signature_context(types, domain): return False - filtr = read_filtering_file(domain, message) + filtr = read_filtering_file(domain, message, filtering_file_path) # send types definition for key in types.keys(): @@ -362,8 +367,8 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering = Fals (f["type"], f["enum"], f["typesize"], f["array_lvls"]) = \ send_struct_def_field(f["type"], f["name"]) - if filtering: - send_filtering_activate() + if filtering_file_path: + app_client.eip712_filtering_activate() prepare_filtering(filtr, message) # send domain implementation @@ -371,17 +376,15 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering = Fals if not send_struct_impl(types, domain, domain_typename): return False - if filtering: + if filtering_file_path: if filtr and "name" in filtr: send_filtering_contract_name(filtr["name"]) else: - send_filtering_contract_name(sig_ctx["domain"]["name"]) + send_filtering_contract_name(domain["name"]) # send message implementation app_client.eip712_send_struct_impl_root_struct(message_typename) if not send_struct_impl(types, message, message_typename): return False - # sign - #send_sign() return True diff --git a/tests/ragger/eip712/input_files/11-complex_structs-filter.json b/tests/ragger/eip712/input_files/11-complex_structs-filter.json new file mode 100644 index 0000000..a38240d --- /dev/null +++ b/tests/ragger/eip712/input_files/11-complex_structs-filter.json @@ -0,0 +1,9 @@ +{ + "name": "Depthy Test", + "fields": { + "contents": "Message", + "from.name": "Sender", + "to.members.[].name": "Recipient", + "attach.list.[].name": "Attachment" + } +} diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py index 81a1af6..68ce086 100644 --- a/tests/ragger/ethereum_client.py +++ b/tests/ragger/ethereum_client.py @@ -4,6 +4,7 @@ from typing import Iterator, Dict, List from ragger.backend import BackendInterface from ragger.utils import RAPDU import signal +import pdb class InsType(IntEnum): EIP712_SEND_STRUCT_DEF = 0x1a, @@ -13,14 +14,17 @@ class InsType(IntEnum): class P1Type(IntEnum): COMPLETE_SEND = 0x00, - PARTIAL_SEND = 0x01 + PARTIAL_SEND = 0x01, + FILTERING_ACTIVATE = 0x00, + FILTERING_CONTRACT_NAME = 0x0f, + FILTERING_FIELD_NAME = 0xff class P2Type(IntEnum): STRUCT_NAME = 0x00, STRUCT_FIELD = 0xff, ARRAY = 0x0f, LEGACY_IMPLEM = 0x00 - NEW_IMPLEM = 0x01, + NEW_IMPLEM = 0x01 class EIP712FieldType(IntEnum): CUSTOM = 0, @@ -63,14 +67,17 @@ class EthereumClientCmdBuilder: header.append(len(cdata)) return header + cdata - def eip712_send_struct_def_struct_name(self, name: str) -> bytes: + def _string_to_bytes(self, string: str) -> bytes: data = bytearray() - for char in name: + for char in string: data.append(ord(char)) + return data + + def eip712_send_struct_def_struct_name(self, name: str) -> bytes: return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, P1Type.COMPLETE_SEND, P2Type.STRUCT_NAME, - data) + self._string_to_bytes(name)) def eip712_send_struct_def_struct_field(self, field_type: EIP712FieldType, @@ -86,8 +93,7 @@ class EthereumClientCmdBuilder: data.append(typedesc) if field_type == EIP712FieldType.CUSTOM: data.append(len(type_name)) - for char in type_name: - data.append(ord(char)) + data += self._string_to_bytes(type_name) if type_size != None: data.append(type_size) if len(array_levels) > 0: @@ -97,21 +103,17 @@ class EthereumClientCmdBuilder: if level != None: data.append(level) data.append(len(key_name)) - for char in key_name: - data.append(ord(char)) + data += self._string_to_bytes(key_name) return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, P1Type.COMPLETE_SEND, P2Type.STRUCT_FIELD, data) def eip712_send_struct_impl_root_struct(self, name: str) -> bytes: - data = bytearray() - for char in name: - data.append(ord(char)) return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, P1Type.COMPLETE_SEND, P2Type.STRUCT_NAME, - data) + self._string_to_bytes(name)) def eip712_send_struct_impl_array(self, size: int) -> bytes: data = bytearray() @@ -163,6 +165,32 @@ class EthereumClientCmdBuilder: P2Type.LEGACY_IMPLEM, data) + def eip712_filtering_activate(self): + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_ACTIVATE, + 0x00, + bytearray()) + + def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes: + data = bytearray() + data.append(len(name)) + data += self._string_to_bytes(name) + data.append(len(sig)) + data += sig + return data + + def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes: + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_CONTRACT_NAME, + 0x00, + self._eip712_filtering_send_name(name, sig)) + + def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes: + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_FIELD_NAME, + 0x00, + self._eip712_filtering_send_name(name, sig)) + class EthereumResponseParser: def sign(self, data: bytes): @@ -195,6 +223,7 @@ class EthereumClient: ) } _click_delay = 1/4 + _eip712_filtering = False def __init__(self, client: BackendInterface, debug: bool = False): self._client = client @@ -262,7 +291,8 @@ class EthereumClient: def eip712_sign_new(self, bip32): with self._send(self._cmd_builder.eip712_sign_new(bip32)): - if not self._settings[SettingType.VERBOSE_EIP712].value: # need to skip the message hash + if not self._settings[SettingType.VERBOSE_EIP712].value and \ + not self._eip712_filtering: # need to skip the message hash self._client.right_click() self._client.right_click() self._client.both_click() # approve signature @@ -306,3 +336,21 @@ class EthereumClient: self._settings[enum].value = new_values[enum] self._client.right_click() self._client.both_click() + + def eip712_filtering_activate(self): + with self._send(self._cmd_builder.eip712_filtering_activate()): + pass + self._eip712_filtering = True + assert self._recv().status == 0x9000 + + def eip712_filtering_send_contract_name(self, name: str, sig: bytes): + #pdb.set_trace() + with self._send(self._cmd_builder.eip712_filtering_send_contract_name(name, sig)): + self._enable_click_until_response() + self._disable_click_until_response() + assert self._recv().status == 0x9000 + + def eip712_filtering_send_field_name(self, name: str, sig: bytes): + with self._send(self._cmd_builder.eip712_filtering_send_field_name(name, sig)): + pass + assert self._recv().status == 0x9000 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 1645013..f9b4a16 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -31,6 +31,10 @@ def input_file(request) -> str: def verbose(request) -> bool: return request.param +@pytest.fixture(params=[False, True]) +def filtering(request) -> bool: + return request.param + def test_eip712_legacy(app_client: EthereumClient): v, r, s = app_client.eip712_sign_legacy( @@ -49,6 +53,10 @@ def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, if app_client._client.firmware.device != "nanos": test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1])) conf_file = "%s.ini" % (test_path) + filter_file = None + + if filtering: + filter_file = "%s-filter.json" % (test_path) config = ConfigParser() config.read(conf_file) @@ -59,16 +67,19 @@ def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, assert "r" in config["signature"] assert "s" in config["signature"] - if verbose: - app_client.settings_set({ - SettingType.VERBOSE_EIP712: True - }) + if not filtering or Path(filter_file).is_file(): + if verbose: + app_client.settings_set({ + SettingType.VERBOSE_EIP712: True + }) - InputData.process_file(app_client, input_file, False) - v, r, s = app_client.eip712_sign_new(bip32) + assert InputData.process_file(app_client, input_file, filter_file) == True + v, r, s = app_client.eip712_sign_new(bip32) - assert v == bytes.fromhex(config["signature"]["v"]) - assert r == bytes.fromhex(config["signature"]["r"]) - assert s == bytes.fromhex(config["signature"]["s"]) + assert v == bytes.fromhex(config["signature"]["v"]) + assert r == bytes.fromhex(config["signature"]["r"]) + assert s == bytes.fromhex(config["signature"]["s"]) + else: + print("No filter file found, skipping...") else: - print("Not supported by LNS") + print("Not supported by LNS, skipping...") From af277c9c40ee96b135fc389c30e2c7939b0a35f5 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 5 Aug 2022 17:14:50 +0200 Subject: [PATCH 148/184] Removed dead code in EIP712 JSON handling python test code --- tests/ragger/eip712/InputData.py | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 8f63188..62c51c6 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -4,27 +4,19 @@ import os import json import sys import re -from enum import IntEnum, auto import hashlib from ecdsa import SigningKey from ecdsa.util import sigencode_der -import pdb from ethereum_client import EthereumClient, EIP712FieldType import base64 # global variables app_client: EthereumClient = None -parser = None -trans = None filtering_paths = None current_path = list() sig_ctx = {} -class ArrayType(IntEnum): - dynamic = 0 - fixed_size = auto() - # From a string typename, extract the type and all the array depth @@ -168,9 +160,6 @@ def encode_string(value, typesize): data.append(ord(char)) return data -def encode_byte(value, typesize): - return bytearray() - def encode_bytes_fix(value, typesize): return encode_hex_string(value, typesize) @@ -252,27 +241,6 @@ def send_struct_impl(structs, data, structname): return False return True -def send_sign(): - bip32path = bytearray.fromhex("8000002c8000003c800000000000000000000000") - path_len = bytearray() - path_len.append(int(len(bip32path) / 4)) - #send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path) - print("send_apdu(INS_SIGN, 0x00, P2_VERS_NEW, path_len + bip32path)") - -#def send_filtering_activate(): -# #send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray()) -# print("send_apdu(INS_FILTERING, P1_ACTIVATE, 0x00, bytearray())") -# -def send_filtering_info(p1, display_name, sig): - payload = bytearray() - payload.append(len(display_name)) - for char in display_name: - payload.append(ord(char)) - payload.append(len(sig)) - payload += sig - #send_apdu(INS_FILTERING, p1, 0x00, payload) - print("send_apdu(INS_FILTERING, p1, 0x00, payload)") - # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures def send_filtering_contract_name(display_name: str): global sig_ctx From c6ae3480a7b1833f496c65ec5a828142d93fe75c Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 8 Aug 2022 16:36:02 +0200 Subject: [PATCH 149/184] Updated ragger requirements file --- tests/ragger/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ragger/requirements.txt b/tests/ragger/requirements.txt index b85a795..246b518 100644 --- a/tests/ragger/requirements.txt +++ b/tests/ragger/requirements.txt @@ -1,3 +1,3 @@ ragger -pytest>=6.1.1,<7.0.0 -ecdsa>=0.16.1,<0.17.0 +pytest>=7.1.0,<7.2.0 +ecdsa>=0.18.0,<0.19.0 From 3c321a04ea6f700a595f541f540e6ae07bf360e2 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 9 Aug 2022 17:49:05 +0200 Subject: [PATCH 150/184] Split ragger ethereum client into multiple smaller files --- tests/ragger/conftest.py | 2 +- tests/ragger/eip712/InputData.py | 2 +- tests/ragger/ethereum_client.py | 356 ------------------ tests/ragger/ethereum_client/__init__.py | 0 tests/ragger/ethereum_client/client.py | 156 ++++++++ .../ragger/ethereum_client/command_builder.py | 164 ++++++++ tests/ragger/ethereum_client/eip712.py | 11 + .../ragger/ethereum_client/response_parser.py | 14 + tests/ragger/ethereum_client/setting.py | 15 + tests/ragger/test_eip712.py | 2 +- 10 files changed, 363 insertions(+), 359 deletions(-) delete mode 100644 tests/ragger/ethereum_client.py create mode 100644 tests/ragger/ethereum_client/__init__.py create mode 100644 tests/ragger/ethereum_client/client.py create mode 100644 tests/ragger/ethereum_client/command_builder.py create mode 100644 tests/ragger/ethereum_client/eip712.py create mode 100644 tests/ragger/ethereum_client/response_parser.py create mode 100644 tests/ragger/ethereum_client/setting.py diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py index 09c6227..f525cbd 100644 --- a/tests/ragger/conftest.py +++ b/tests/ragger/conftest.py @@ -2,7 +2,7 @@ import pytest from pathlib import Path from ragger import Firmware from ragger.backend import SpeculosBackend, LedgerCommBackend, LedgerWalletBackend, BackendInterface -from ethereum_client import EthereumClient +from ethereum_client.client import EthereumClient ELFS_DIR = (Path(__file__).parent.parent / "elfs").resolve() FWS = [ diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 62c51c6..dda0e37 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -7,7 +7,7 @@ import re import hashlib from ecdsa import SigningKey from ecdsa.util import sigencode_der -from ethereum_client import EthereumClient, EIP712FieldType +from ethereum_client.client import EthereumClient, EIP712FieldType import base64 # global variables diff --git a/tests/ragger/ethereum_client.py b/tests/ragger/ethereum_client.py deleted file mode 100644 index 68ce086..0000000 --- a/tests/ragger/ethereum_client.py +++ /dev/null @@ -1,356 +0,0 @@ -from contextlib import contextmanager -from enum import IntEnum, auto -from typing import Iterator, Dict, List -from ragger.backend import BackendInterface -from ragger.utils import RAPDU -import signal -import pdb - -class InsType(IntEnum): - EIP712_SEND_STRUCT_DEF = 0x1a, - EIP712_SEND_STRUCT_IMPL = 0x1c, - EIP712_SEND_FILTERING = 0x1e, - EIP712_SIGN = 0x0c - -class P1Type(IntEnum): - COMPLETE_SEND = 0x00, - PARTIAL_SEND = 0x01, - FILTERING_ACTIVATE = 0x00, - FILTERING_CONTRACT_NAME = 0x0f, - FILTERING_FIELD_NAME = 0xff - -class P2Type(IntEnum): - STRUCT_NAME = 0x00, - STRUCT_FIELD = 0xff, - ARRAY = 0x0f, - LEGACY_IMPLEM = 0x00 - NEW_IMPLEM = 0x01 - -class EIP712FieldType(IntEnum): - CUSTOM = 0, - INT = auto() - UINT = auto() - ADDRESS = auto() - BOOL = auto() - STRING = auto() - FIX_BYTES = auto() - DYN_BYTES = auto() - -class SettingType(IntEnum): - BLIND_SIGNING = 0, - DEBUG_DATA = auto() - NONCE = auto() - VERBOSE_EIP712 = auto() - -class Setting: - devices: List[str] - value: bool - - def __init__(self, d: List[str]): - self.devices = d - - -class EthereumClientCmdBuilder: - _CLA: int = 0xE0 - - def _serialize(self, - ins: InsType, - p1: int, - p2: int, - cdata: bytearray = bytearray()) -> bytes: - - header = bytearray() - header.append(self._CLA) - header.append(ins) - header.append(p1) - header.append(p2) - header.append(len(cdata)) - return header + cdata - - def _string_to_bytes(self, string: str) -> bytes: - data = bytearray() - for char in string: - data.append(ord(char)) - return data - - def eip712_send_struct_def_struct_name(self, name: str) -> bytes: - return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, - P1Type.COMPLETE_SEND, - P2Type.STRUCT_NAME, - self._string_to_bytes(name)) - - def eip712_send_struct_def_struct_field(self, - field_type: EIP712FieldType, - type_name: str, - type_size: int, - array_levels: [], - key_name: str) -> bytes: - data = bytearray() - typedesc = 0 - typedesc |= (len(array_levels) > 0) << 7 - typedesc |= (type_size != None) << 6 - typedesc |= field_type - data.append(typedesc) - if field_type == EIP712FieldType.CUSTOM: - data.append(len(type_name)) - data += self._string_to_bytes(type_name) - if type_size != None: - data.append(type_size) - if len(array_levels) > 0: - data.append(len(array_levels)) - for level in array_levels: - data.append(0 if level == None else 1) - if level != None: - data.append(level) - data.append(len(key_name)) - data += self._string_to_bytes(key_name) - return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, - P1Type.COMPLETE_SEND, - P2Type.STRUCT_FIELD, - data) - - def eip712_send_struct_impl_root_struct(self, name: str) -> bytes: - return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, - P1Type.COMPLETE_SEND, - P2Type.STRUCT_NAME, - self._string_to_bytes(name)) - - def eip712_send_struct_impl_array(self, size: int) -> bytes: - data = bytearray() - data.append(size) - return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, - P1Type.COMPLETE_SEND, - P2Type.ARRAY, - data) - - def eip712_send_struct_impl_struct_field(self, data: bytearray) -> Iterator[bytes]: - # Add a 16-bit integer with the data's byte length (network byte order) - data_w_length = bytearray() - data_w_length.append((len(data) & 0xff00) >> 8) - data_w_length.append(len(data) & 0x00ff) - data_w_length += data - while len(data_w_length) > 0: - p1 = P1Type.PARTIAL_SEND if len(data_w_length) > 0xff else P1Type.COMPLETE_SEND - yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, - p1, - P2Type.STRUCT_FIELD, - data_w_length[:0xff]) - data_w_length = data_w_length[0xff:] - - def _format_bip32(self, bip32, data: bytearray) -> bytearray: - data.append(len(bip32)) - for idx in bip32: - data.append((idx & 0xff000000) >> 24) - data.append((idx & 0x00ff0000) >> 16) - data.append((idx & 0x0000ff00) >> 8) - data.append((idx & 0x000000ff)) - return data - - def eip712_sign_new(self, bip32) -> bytes: - data = self._format_bip32(bip32, bytearray()) - return self._serialize(InsType.EIP712_SIGN, - P1Type.COMPLETE_SEND, - P2Type.NEW_IMPLEM, - data) - - def eip712_sign_legacy(self, - bip32, - domain_hash: bytes, - message_hash: bytes) -> bytes: - data = self._format_bip32(bip32, bytearray()) - data += domain_hash - data += message_hash - return self._serialize(InsType.EIP712_SIGN, - P1Type.COMPLETE_SEND, - P2Type.LEGACY_IMPLEM, - data) - - def eip712_filtering_activate(self): - return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_ACTIVATE, - 0x00, - bytearray()) - - def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes: - data = bytearray() - data.append(len(name)) - data += self._string_to_bytes(name) - data.append(len(sig)) - data += sig - return data - - def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes: - return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_CONTRACT_NAME, - 0x00, - self._eip712_filtering_send_name(name, sig)) - - def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes: - return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_FIELD_NAME, - 0x00, - self._eip712_filtering_send_name(name, sig)) - - -class EthereumResponseParser: - def sign(self, data: bytes): - assert len(data) == (1 + 32 + 32) - - v = data[0:1] - data = data[1:] - - r = data[0:32] - data = data[32:] - - s = data[0:32] - data = data[32:] - - return v, r, s - -class EthereumClient: - _settings: Dict[SettingType, Setting] = { - SettingType.BLIND_SIGNING: Setting( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.DEBUG_DATA: Setting( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.NONCE: Setting( - [ "nanos", "nanox", "nanosp" ] - ), - SettingType.VERBOSE_EIP712: Setting( - [ "nanox", "nanosp" ] - ) - } - _click_delay = 1/4 - _eip712_filtering = False - - def __init__(self, client: BackendInterface, debug: bool = False): - self._client = client - self._debug = debug - self._cmd_builder = EthereumClientCmdBuilder() - self._resp_parser = EthereumResponseParser() - signal.signal(signal.SIGALRM, self._click_signal_timeout) - for setting in self._settings.values(): - setting.value = False - - def _send(self, payload: bytearray): - return self._client.exchange_async_raw(payload) - - def _recv(self) -> RAPDU: - return self._client._last_async_response - - def _click_signal_timeout(self, signum: int, frame): - self._client.right_click() - - def _enable_click_until_response(self): - signal.setitimer(signal.ITIMER_REAL, - self._click_delay, - self._click_delay) - - def _disable_click_until_response(self): - signal.setitimer(signal.ITIMER_REAL, 0, 0) - - def eip712_send_struct_def_struct_name(self, name: str): - with self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)): - pass - return self._recv().status == 0x9000 - - def eip712_send_struct_def_struct_field(self, - field_type: EIP712FieldType, - type_name: str, - type_size: int, - array_levels: [], - key_name: str): - with self._send(self._cmd_builder.eip712_send_struct_def_struct_field( - field_type, - type_name, - type_size, - array_levels, - key_name)): - pass - return self._recv() - - def eip712_send_struct_impl_root_struct(self, name: str): - with self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)): - self._enable_click_until_response() - self._disable_click_until_response() - return self._recv() - - def eip712_send_struct_impl_array(self, size: int): - with self._send(self._cmd_builder.eip712_send_struct_impl_array(size)): - pass - return self._recv() - - def eip712_send_struct_impl_struct_field(self, raw_value: bytes): - for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value): - with self._send(apdu): - self._enable_click_until_response() - self._disable_click_until_response() - assert self._recv().status == 0x9000 - - def eip712_sign_new(self, bip32): - with self._send(self._cmd_builder.eip712_sign_new(bip32)): - if not self._settings[SettingType.VERBOSE_EIP712].value and \ - not self._eip712_filtering: # need to skip the message hash - self._client.right_click() - self._client.right_click() - self._client.both_click() # approve signature - resp = self._recv() - assert resp.status == 0x9000 - return self._resp_parser.sign(resp.data) - - def eip712_sign_legacy(self, - bip32, - domain_hash: bytes, - message_hash: bytes): - with self._send(self._cmd_builder.eip712_sign_legacy(bip32, - domain_hash, - message_hash)): - self._client.right_click() # sign typed message screen - for _ in range(2): # two hashes (domain + message) - if self._client.firmware.device == "nanos": - screens_per_hash = 4 - else: - screens_per_hash = 2 - for _ in range(screens_per_hash): - self._client.right_click() - self._client.both_click() # approve signature - - resp = self._recv() - - assert resp.status == 0x9000 - return self._resp_parser.sign(resp.data) - - def settings_set(self, new_values: Dict[SettingType, bool]): - # Go to settings - for _ in range(2): - self._client.right_click() - self._client.both_click() - - for enum in self._settings.keys(): - if self._client.firmware.device in self._settings[enum].devices: - if enum in new_values.keys(): - if new_values[enum] != self._settings[enum].value: - self._client.both_click() - self._settings[enum].value = new_values[enum] - self._client.right_click() - self._client.both_click() - - def eip712_filtering_activate(self): - with self._send(self._cmd_builder.eip712_filtering_activate()): - pass - self._eip712_filtering = True - assert self._recv().status == 0x9000 - - def eip712_filtering_send_contract_name(self, name: str, sig: bytes): - #pdb.set_trace() - with self._send(self._cmd_builder.eip712_filtering_send_contract_name(name, sig)): - self._enable_click_until_response() - self._disable_click_until_response() - assert self._recv().status == 0x9000 - - def eip712_filtering_send_field_name(self, name: str, sig: bytes): - with self._send(self._cmd_builder.eip712_filtering_send_field_name(name, sig)): - pass - assert self._recv().status == 0x9000 diff --git a/tests/ragger/ethereum_client/__init__.py b/tests/ragger/ethereum_client/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ragger/ethereum_client/client.py b/tests/ragger/ethereum_client/client.py new file mode 100644 index 0000000..c3588b3 --- /dev/null +++ b/tests/ragger/ethereum_client/client.py @@ -0,0 +1,156 @@ +from enum import IntEnum, auto +from typing import Iterator, Dict, List +from ragger.backend import BackendInterface +from ragger.utils import RAPDU +from ethereum_client.command_builder import EthereumCmdBuilder +from ethereum_client.setting import SettingType, SettingImpl +from ethereum_client.eip712 import EIP712FieldType +from ethereum_client.response_parser import EthereumRespParser +import signal + + +class EthereumClient: + _settings: Dict[SettingType, SettingImpl] = { + SettingType.BLIND_SIGNING: SettingImpl( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.DEBUG_DATA: SettingImpl( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.NONCE: SettingImpl( + [ "nanos", "nanox", "nanosp" ] + ), + SettingType.VERBOSE_EIP712: SettingImpl( + [ "nanox", "nanosp" ] + ) + } + _click_delay = 1/4 + _eip712_filtering = False + + def __init__(self, client: BackendInterface): + self._client = client + self._cmd_builder = EthereumCmdBuilder() + self._resp_parser = EthereumRespParser() + signal.signal(signal.SIGALRM, self._click_signal_timeout) + for setting in self._settings.values(): + setting.value = False + + def _send(self, payload: bytearray): + return self._client.exchange_async_raw(payload) + + def _recv(self) -> RAPDU: + return self._client._last_async_response + + def _click_signal_timeout(self, signum: int, frame): + self._client.right_click() + + def _enable_click_until_response(self): + signal.setitimer(signal.ITIMER_REAL, + self._click_delay, + self._click_delay) + + def _disable_click_until_response(self): + signal.setitimer(signal.ITIMER_REAL, 0, 0) + + def eip712_send_struct_def_struct_name(self, name: str): + with self._send(self._cmd_builder.eip712_send_struct_def_struct_name(name)): + pass + return self._recv().status == 0x9000 + + def eip712_send_struct_def_struct_field(self, + field_type: EIP712FieldType, + type_name: str, + type_size: int, + array_levels: [], + key_name: str): + with self._send(self._cmd_builder.eip712_send_struct_def_struct_field( + field_type, + type_name, + type_size, + array_levels, + key_name)): + pass + return self._recv() + + def eip712_send_struct_impl_root_struct(self, name: str): + with self._send(self._cmd_builder.eip712_send_struct_impl_root_struct(name)): + self._enable_click_until_response() + self._disable_click_until_response() + return self._recv() + + def eip712_send_struct_impl_array(self, size: int): + with self._send(self._cmd_builder.eip712_send_struct_impl_array(size)): + pass + return self._recv() + + def eip712_send_struct_impl_struct_field(self, raw_value: bytes): + for apdu in self._cmd_builder.eip712_send_struct_impl_struct_field(raw_value): + with self._send(apdu): + self._enable_click_until_response() + self._disable_click_until_response() + assert self._recv().status == 0x9000 + + def eip712_sign_new(self, bip32): + with self._send(self._cmd_builder.eip712_sign_new(bip32)): + if not self._settings[SettingType.VERBOSE_EIP712].value and \ + not self._eip712_filtering: # need to skip the message hash + self._client.right_click() + self._client.right_click() + self._client.both_click() # approve signature + resp = self._recv() + assert resp.status == 0x9000 + return self._resp_parser.sign(resp.data) + + def eip712_sign_legacy(self, + bip32, + domain_hash: bytes, + message_hash: bytes): + with self._send(self._cmd_builder.eip712_sign_legacy(bip32, + domain_hash, + message_hash)): + self._client.right_click() # sign typed message screen + for _ in range(2): # two hashes (domain + message) + if self._client.firmware.device == "nanos": + screens_per_hash = 4 + else: + screens_per_hash = 2 + for _ in range(screens_per_hash): + self._client.right_click() + self._client.both_click() # approve signature + + resp = self._recv() + + assert resp.status == 0x9000 + return self._resp_parser.sign(resp.data) + + def settings_set(self, new_values: Dict[SettingType, bool]): + # Go to settings + for _ in range(2): + self._client.right_click() + self._client.both_click() + + for enum in self._settings.keys(): + if self._client.firmware.device in self._settings[enum].devices: + if enum in new_values.keys(): + if new_values[enum] != self._settings[enum].value: + self._client.both_click() + self._settings[enum].value = new_values[enum] + self._client.right_click() + self._client.both_click() + + def eip712_filtering_activate(self): + with self._send(self._cmd_builder.eip712_filtering_activate()): + pass + self._eip712_filtering = True + assert self._recv().status == 0x9000 + + def eip712_filtering_send_contract_name(self, name: str, sig: bytes): + with self._send(self._cmd_builder.eip712_filtering_send_contract_name(name, sig)): + self._enable_click_until_response() + self._disable_click_until_response() + assert self._recv().status == 0x9000 + + def eip712_filtering_send_field_name(self, name: str, sig: bytes): + with self._send(self._cmd_builder.eip712_filtering_send_field_name(name, sig)): + pass + assert self._recv().status == 0x9000 diff --git a/tests/ragger/ethereum_client/command_builder.py b/tests/ragger/ethereum_client/command_builder.py new file mode 100644 index 0000000..ebb92b1 --- /dev/null +++ b/tests/ragger/ethereum_client/command_builder.py @@ -0,0 +1,164 @@ +from enum import IntEnum, auto +from typing import Iterator +from ethereum_client.eip712 import EIP712FieldType + +class InsType(IntEnum): + EIP712_SEND_STRUCT_DEF = 0x1a, + EIP712_SEND_STRUCT_IMPL = 0x1c, + EIP712_SEND_FILTERING = 0x1e, + EIP712_SIGN = 0x0c + +class P1Type(IntEnum): + COMPLETE_SEND = 0x00, + PARTIAL_SEND = 0x01, + FILTERING_ACTIVATE = 0x00, + FILTERING_CONTRACT_NAME = 0x0f, + FILTERING_FIELD_NAME = 0xff + +class P2Type(IntEnum): + STRUCT_NAME = 0x00, + STRUCT_FIELD = 0xff, + ARRAY = 0x0f, + LEGACY_IMPLEM = 0x00 + NEW_IMPLEM = 0x01 + +class EthereumCmdBuilder: + _CLA: int = 0xE0 + + def _serialize(self, + ins: InsType, + p1: int, + p2: int, + cdata: bytearray = bytearray()) -> bytes: + + header = bytearray() + header.append(self._CLA) + header.append(ins) + header.append(p1) + header.append(p2) + header.append(len(cdata)) + return header + cdata + + def _string_to_bytes(self, string: str) -> bytes: + data = bytearray() + for char in string: + data.append(ord(char)) + return data + + def eip712_send_struct_def_struct_name(self, name: str) -> bytes: + return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_NAME, + self._string_to_bytes(name)) + + def eip712_send_struct_def_struct_field(self, + field_type: EIP712FieldType, + type_name: str, + type_size: int, + array_levels: [], + key_name: str) -> bytes: + data = bytearray() + typedesc = 0 + typedesc |= (len(array_levels) > 0) << 7 + typedesc |= (type_size != None) << 6 + typedesc |= field_type + data.append(typedesc) + if field_type == EIP712FieldType.CUSTOM: + data.append(len(type_name)) + data += self._string_to_bytes(type_name) + if type_size != None: + data.append(type_size) + if len(array_levels) > 0: + data.append(len(array_levels)) + for level in array_levels: + data.append(0 if level == None else 1) + if level != None: + data.append(level) + data.append(len(key_name)) + data += self._string_to_bytes(key_name) + return self._serialize(InsType.EIP712_SEND_STRUCT_DEF, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_FIELD, + data) + + def eip712_send_struct_impl_root_struct(self, name: str) -> bytes: + return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.STRUCT_NAME, + self._string_to_bytes(name)) + + def eip712_send_struct_impl_array(self, size: int) -> bytes: + data = bytearray() + data.append(size) + return self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + P1Type.COMPLETE_SEND, + P2Type.ARRAY, + data) + + def eip712_send_struct_impl_struct_field(self, data: bytearray) -> Iterator[bytes]: + # Add a 16-bit integer with the data's byte length (network byte order) + data_w_length = bytearray() + data_w_length.append((len(data) & 0xff00) >> 8) + data_w_length.append(len(data) & 0x00ff) + data_w_length += data + while len(data_w_length) > 0: + p1 = P1Type.PARTIAL_SEND if len(data_w_length) > 0xff else P1Type.COMPLETE_SEND + yield self._serialize(InsType.EIP712_SEND_STRUCT_IMPL, + p1, + P2Type.STRUCT_FIELD, + data_w_length[:0xff]) + data_w_length = data_w_length[0xff:] + + def _format_bip32(self, bip32, data: bytearray) -> bytearray: + data.append(len(bip32)) + for idx in bip32: + data.append((idx & 0xff000000) >> 24) + data.append((idx & 0x00ff0000) >> 16) + data.append((idx & 0x0000ff00) >> 8) + data.append((idx & 0x000000ff)) + return data + + def eip712_sign_new(self, bip32) -> bytes: + data = self._format_bip32(bip32, bytearray()) + return self._serialize(InsType.EIP712_SIGN, + P1Type.COMPLETE_SEND, + P2Type.NEW_IMPLEM, + data) + + def eip712_sign_legacy(self, + bip32, + domain_hash: bytes, + message_hash: bytes) -> bytes: + data = self._format_bip32(bip32, bytearray()) + data += domain_hash + data += message_hash + return self._serialize(InsType.EIP712_SIGN, + P1Type.COMPLETE_SEND, + P2Type.LEGACY_IMPLEM, + data) + + def eip712_filtering_activate(self): + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_ACTIVATE, + 0x00, + bytearray()) + + def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes: + data = bytearray() + data.append(len(name)) + data += self._string_to_bytes(name) + data.append(len(sig)) + data += sig + return data + + def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes: + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_CONTRACT_NAME, + 0x00, + self._eip712_filtering_send_name(name, sig)) + + def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes: + return self._serialize(InsType.EIP712_SEND_FILTERING, + P1Type.FILTERING_FIELD_NAME, + 0x00, + self._eip712_filtering_send_name(name, sig)) diff --git a/tests/ragger/ethereum_client/eip712.py b/tests/ragger/ethereum_client/eip712.py new file mode 100644 index 0000000..3438a1c --- /dev/null +++ b/tests/ragger/ethereum_client/eip712.py @@ -0,0 +1,11 @@ +from enum import IntEnum, auto + +class EIP712FieldType(IntEnum): + CUSTOM = 0, + INT = auto() + UINT = auto() + ADDRESS = auto() + BOOL = auto() + STRING = auto() + FIX_BYTES = auto() + DYN_BYTES = auto() diff --git a/tests/ragger/ethereum_client/response_parser.py b/tests/ragger/ethereum_client/response_parser.py new file mode 100644 index 0000000..681c18d --- /dev/null +++ b/tests/ragger/ethereum_client/response_parser.py @@ -0,0 +1,14 @@ +class EthereumRespParser: + def sign(self, data: bytes): + assert len(data) == (1 + 32 + 32) + + v = data[0:1] + data = data[1:] + + r = data[0:32] + data = data[32:] + + s = data[0:32] + data = data[32:] + + return v, r, s diff --git a/tests/ragger/ethereum_client/setting.py b/tests/ragger/ethereum_client/setting.py new file mode 100644 index 0000000..a965fe3 --- /dev/null +++ b/tests/ragger/ethereum_client/setting.py @@ -0,0 +1,15 @@ +from enum import IntEnum, auto +from typing import List + +class SettingType(IntEnum): + BLIND_SIGNING = 0, + DEBUG_DATA = auto() + NONCE = auto() + VERBOSE_EIP712 = auto() + +class SettingImpl: + devices: List[str] + value: bool + + def __init__(self, devs: List[str]): + self.devices = devs diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index f9b4a16..f2ae413 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -2,7 +2,7 @@ import pytest import os import fnmatch from typing import List -from ethereum_client import EthereumClient, SettingType +from ethereum_client.client import EthereumClient, SettingType from eip712 import InputData from pathlib import Path from configparser import ConfigParser From f27822153704cd68bfe3d2de3eef3f15baa4f7c1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 9 Aug 2022 17:49:47 +0200 Subject: [PATCH 151/184] Add two new EIP712 filters --- .../ragger/eip712/input_files/00-simple_mail-filter.json | 7 +++++++ tests/ragger/eip712/input_files/08-opensea-filter.json | 9 +++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/ragger/eip712/input_files/00-simple_mail-filter.json create mode 100644 tests/ragger/eip712/input_files/08-opensea-filter.json diff --git a/tests/ragger/eip712/input_files/00-simple_mail-filter.json b/tests/ragger/eip712/input_files/00-simple_mail-filter.json new file mode 100644 index 0000000..33399d1 --- /dev/null +++ b/tests/ragger/eip712/input_files/00-simple_mail-filter.json @@ -0,0 +1,7 @@ +{ + "name": "Test JSON", + "fields": { + "from.name": "From", + "to.name" : "To" + } +} diff --git a/tests/ragger/eip712/input_files/08-opensea-filter.json b/tests/ragger/eip712/input_files/08-opensea-filter.json new file mode 100644 index 0000000..6f1d9f5 --- /dev/null +++ b/tests/ragger/eip712/input_files/08-opensea-filter.json @@ -0,0 +1,9 @@ +{ + "name": "OpenSea", + "fields": { + "maker": "Maker", + "taker": "Taker", + "basePrice": "Base Price", + "expirationTime": "Expiration Time" + } +} From 98889e2642bfa45ff6fe2e92f65968fa46c39e52 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 10 Aug 2022 11:47:05 +0200 Subject: [PATCH 152/184] Moved EIP712 filtering argument switch from P1 to P2, more in line with other EIP712 APDUs --- doc/ethapp.adoc | 8 ++--- src_features/signMessageEIP712/commands_712.c | 28 +++++++-------- src_features/signMessageEIP712/commands_712.h | 20 +++++------ tests/ragger/ethereum_client/client.py | 2 +- .../ragger/ethereum_client/command_builder.py | 34 +++++++++---------- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 59c37f5..3aaf738 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -837,12 +837,12 @@ _Command_ [width="80%"] |========================================================================= | *CLA* | *INS* | *P1* | *P2* | *LC* | *Le* -| E0 | 1E | 00 : activate +| E0 | 1E | 00 + | 00 : activate - 0F : contract name + 0F : contract name - FF : field name - | 00 + FF : field name | variable | variable |========================================================================= diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index deb9bba..9d926ba 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -50,10 +50,10 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { } if (ret) { switch (apdu_buf[OFFSET_P2]) { - case P2_NAME: + case P2_DEF_NAME: ret = set_struct_name(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; - case P2_FIELD: + case P2_DEF_FIELD: ret = set_struct_field(apdu_buf[OFFSET_LC], &apdu_buf[OFFSET_CDATA]); break; default: @@ -82,7 +82,7 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; } else { switch (apdu_buf[OFFSET_P2]) { - case P2_NAME: + case P2_IMPL_NAME: // set root type if ((ret = path_set_root((char *) &apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]))) { if (N_storage.verbose_eip712) { @@ -92,14 +92,14 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { ui_712_field_flags_reset(); } break; - case P2_FIELD: + case P2_IMPL_FIELD: if ((ret = field_hash(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC], apdu_buf[OFFSET_P1] != P1_COMPLETE))) { reply_apdu = false; } break; - case P2_ARRAY: + case P2_IMPL_ARRAY: ret = path_new_array_depth(&apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); break; default: @@ -130,28 +130,28 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; ret = false; } else { - switch (apdu_buf[OFFSET_P1]) { - case P1_ACTIVATE: + 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 P1_CONTRACT_NAME: - case P1_FIELD_NAME: - type = (apdu_buf[OFFSET_P1] == P1_CONTRACT_NAME) ? FILTERING_CONTRACT_NAME - : FILTERING_STRUCT_FIELD; + case P2_FILT_CONTRACT_NAME: + case P2_FILT_FIELD_NAME: + type = (apdu_buf[OFFSET_P2] == P2_FILT_CONTRACT_NAME) ? FILTERING_CONTRACT_NAME + : FILTERING_STRUCT_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_P1] == P1_CONTRACT_NAME) && ret) { + if ((apdu_buf[OFFSET_P2] == P2_FILT_CONTRACT_NAME) && ret) { reply_apdu = false; } } break; default: - PRINTF("Unknown P1 0x%x for APDU 0x%x\n", - apdu_buf[OFFSET_P1], + 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; diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index a5485a0..8cfb3a1 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -7,18 +7,18 @@ #include // APDUs P1 -#define P1_COMPLETE 0x00 -#define P1_PARTIAL 0xFF -#define P1_ACTIVATE 0x00 -#define P1_CONTRACT_NAME 0x0F -#define P1_FIELD_NAME 0xFF +#define P1_COMPLETE 0x00 +#define P1_PARTIAL 0xFF // APDUs P2 -#define P2_NAME 0x00 -#define P2_ARRAY 0x0F -#define P2_FIELD 0xFF -#define P2_KEY 0x00 -#define P2_VALUE 0xFF +#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_CONTRACT_NAME 0x0F +#define P2_FILT_FIELD_NAME 0xFF #define DOMAIN_STRUCT_NAME "EIP712Domain" diff --git a/tests/ragger/ethereum_client/client.py b/tests/ragger/ethereum_client/client.py index c3588b3..6ac352e 100644 --- a/tests/ragger/ethereum_client/client.py +++ b/tests/ragger/ethereum_client/client.py @@ -41,7 +41,7 @@ class EthereumClient: def _recv(self) -> RAPDU: return self._client._last_async_response - def _click_signal_timeout(self, signum: int, frame): + def _click_signal_timeout(self, _signum: int, _frame): self._client.right_click() def _enable_click_until_response(self): diff --git a/tests/ragger/ethereum_client/command_builder.py b/tests/ragger/ethereum_client/command_builder.py index ebb92b1..5024a3c 100644 --- a/tests/ragger/ethereum_client/command_builder.py +++ b/tests/ragger/ethereum_client/command_builder.py @@ -3,24 +3,24 @@ from typing import Iterator from ethereum_client.eip712 import EIP712FieldType class InsType(IntEnum): - EIP712_SEND_STRUCT_DEF = 0x1a, - EIP712_SEND_STRUCT_IMPL = 0x1c, - EIP712_SEND_FILTERING = 0x1e, + EIP712_SEND_STRUCT_DEF = 0x1a + EIP712_SEND_STRUCT_IMPL = 0x1c + EIP712_SEND_FILTERING = 0x1e EIP712_SIGN = 0x0c class P1Type(IntEnum): - COMPLETE_SEND = 0x00, - PARTIAL_SEND = 0x01, - FILTERING_ACTIVATE = 0x00, - FILTERING_CONTRACT_NAME = 0x0f, - FILTERING_FIELD_NAME = 0xff + COMPLETE_SEND = 0x00 + PARTIAL_SEND = 0x01 class P2Type(IntEnum): - STRUCT_NAME = 0x00, - STRUCT_FIELD = 0xff, - ARRAY = 0x0f, + STRUCT_NAME = 0x00 + STRUCT_FIELD = 0xff + ARRAY = 0x0f LEGACY_IMPLEM = 0x00 NEW_IMPLEM = 0x01 + FILTERING_ACTIVATE = 0x00 + FILTERING_CONTRACT_NAME = 0x0f + FILTERING_FIELD_NAME = 0xff class EthereumCmdBuilder: _CLA: int = 0xE0 @@ -139,8 +139,8 @@ class EthereumCmdBuilder: def eip712_filtering_activate(self): return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_ACTIVATE, - 0x00, + P1Type.COMPLETE_SEND, + P2Type.FILTERING_ACTIVATE, bytearray()) def _eip712_filtering_send_name(self, name: str, sig: bytes) -> bytes: @@ -153,12 +153,12 @@ class EthereumCmdBuilder: def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes: return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_CONTRACT_NAME, - 0x00, + P1Type.COMPLETE_SEND, + P2Type.FILTERING_CONTRACT_NAME, self._eip712_filtering_send_name(name, sig)) def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes: return self._serialize(InsType.EIP712_SEND_FILTERING, - P1Type.FILTERING_FIELD_NAME, - 0x00, + P1Type.COMPLETE_SEND, + P2Type.FILTERING_FIELD_NAME, self._eip712_filtering_send_name(name, sig)) From ed7b6541c026fdffad69e8f6cfae0c24164eac90 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 10 Aug 2022 15:52:45 +0200 Subject: [PATCH 153/184] EIP712 - Extra fixes from security review --- src_features/signMessageEIP712/typed_data.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index ee59e63..4239f86 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -452,8 +452,13 @@ bool set_struct_name(uint8_t length, const uint8_t *const name) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } + // increment number of structs - *(typed_data->structs_array) += 1; + if ((*(typed_data->structs_array) += 1) == 0) { + PRINTF("EIP712 Structs count overflow!\n"); + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } // copy length if ((length_ptr = mem_alloc(sizeof(uint8_t))) == NULL) { @@ -555,7 +560,7 @@ static bool set_struct_field_custom_typename(const uint8_t *const data, */ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx, uint8_t length) { uint8_t *array_levels_count; - e_array_type *array_level; + uint8_t *array_level; uint8_t *array_level_size; if ((*data_idx + sizeof(*array_levels_count)) > length) // check buffer bound @@ -574,12 +579,12 @@ static bool set_struct_field_array(const uint8_t *const data, uint8_t *data_idx, apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((array_level = mem_alloc(sizeof(uint8_t))) == NULL) { + if ((array_level = mem_alloc(sizeof(*array_level))) == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } *array_level = data[(*data_idx)++]; - if (*array_level > ARRAY_TYPES_COUNT) { + if (*array_level >= ARRAY_TYPES_COUNT) { apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } @@ -694,8 +699,13 @@ bool set_struct_field(uint8_t length, const uint8_t *const data) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; return false; } + // increment number of struct fields - *(typed_data->current_struct_fields_array) += 1; + if ((*(typed_data->current_struct_fields_array) += 1) == 0) { + PRINTF("EIP712 Struct fields count overflow!\n"); + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + return false; + } if ((typedesc_ptr = set_struct_field_typedesc(data, &data_idx, length)) == NULL) { return false; From 770bd9c39d527077ed49b41eb586955281616a30 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 10 Aug 2022 18:31:23 +0200 Subject: [PATCH 154/184] Replaced the signature key used for EIP712 tests by the CAL testing one --- Makefile | 7 ++++++- src/tokens.h | 2 +- src_features/signMessageEIP712/filtering.c | 14 -------------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 67efcde..f818084 100644 --- a/Makefile +++ b/Makefile @@ -145,7 +145,12 @@ endif # EIP-712 ifneq ($(TARGET_NAME),TARGET_NANOS) DEFINES += HAVE_EIP712_FULL_SUPPORT -DEFINES += HAVE_EIP712_TESTING_KEY +endif + +# CryptoAssetsList testing key +CAL_TESTING_KEY:=0 +ifneq ($(CAL_TESTING_KEY),0) +DEFINES += HAVE_CAL_TESTING_KEY endif # Enabling debug PRINTF diff --git a/src/tokens.h b/src/tokens.h index 318edad..a0a8666 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -44,7 +44,7 @@ extern tokenDefinition_t const TOKENS_EXTRA[NUM_TOKENS_EXTRA]; #ifndef HAVE_TOKENS_LIST static const uint8_t LEDGER_SIGNATURE_PUBLIC_KEY[] = { -#ifndef LEDGER_TEST_PUBLIC_KEY +#ifndef HAVE_CAL_TESTING_KEY // production key 2019-01-11 03:07PM (erc20signer) 0x04, 0x5e, 0x6c, 0x10, 0x20, 0xc1, 0x4d, 0xc4, 0x64, 0x42, 0xfe, 0x89, 0xf9, 0x7c, 0x0b, 0x68, 0xcd, 0xb1, 0x59, 0x76, 0xdc, 0x24, 0xf2, 0x4c, 0x31, 0x6e, 0x7b, 0x30, diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 8b96233..7510cd0 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -10,15 +10,6 @@ #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 - /** * Reconstruct the field path and hash it * @@ -99,13 +90,8 @@ static bool verify_filtering_signature(uint8_t dname_length, 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 From 3e896104994feafa95e5dc46ab8028aaff61f034 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 11 Aug 2022 15:50:05 +0200 Subject: [PATCH 155/184] Ragger tests now take CLI arguments for device model and elfs location --- tests/ragger/conftest.py | 45 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/tests/ragger/conftest.py b/tests/ragger/conftest.py index f525cbd..de165db 100644 --- a/tests/ragger/conftest.py +++ b/tests/ragger/conftest.py @@ -4,51 +4,62 @@ from ragger import Firmware from ragger.backend import SpeculosBackend, LedgerCommBackend, LedgerWalletBackend, BackendInterface from ethereum_client.client import EthereumClient -ELFS_DIR = (Path(__file__).parent.parent / "elfs").resolve() FWS = [ Firmware("nanos", "2.1"), Firmware("nanox", "2.0.2"), Firmware("nanosp", "1.0.3") ] -# adding a pytest CLI option "--backend" def pytest_addoption(parser): - print(help(parser.addoption)) parser.addoption("--backend", action="store", default="speculos") + parser.addoption("--path", action="store", default="./elfs") + parser.addoption("--model", action="store", required=True) # accessing the value of the "--backend" option as a fixture -@pytest.fixture(scope="session") -def backend_name(pytestconfig) -> str: +@pytest.fixture +def arg_backend(pytestconfig) -> str: return pytestconfig.getoption("backend") -# Providing the firmware as a fixture -@pytest.fixture(params=FWS) -def firmware(request) -> Firmware: - return request.param +@pytest.fixture +def arg_path(pytestconfig) -> str: + return pytestconfig.getoption("path") -def get_elf_path(firmware: Firmware) -> Path: - assert ELFS_DIR.is_dir(), f"{ELFS_DIR} is not a directory" - app = ELFS_DIR / ("app-%s.elf" % firmware.device) - assert app.is_file(), f"{app} must exist" +@pytest.fixture +def arg_model(pytestconfig) -> str: + return pytestconfig.getoption("model") + +# Providing the firmware as a fixture +@pytest.fixture +def firmware(arg_model: str) -> Firmware: + for fw in FWS: + if fw.device == arg_model: + return fw + raise ValueError("Unknown device model \"%s\"" % (arg_model)) + +def get_elf_path(arg_path: str, firmware: Firmware) -> Path: + elf_dir = Path(arg_path).resolve() + assert elf_dir.is_dir(), ("%s is not a directory" % (arg_path)) + app = elf_dir / ("app-%s.elf" % firmware.device) + assert app.is_file(), ("Firmware %s does not exist !" % (app)) return app # Depending on the "--backend" option value, a different backend is # instantiated, and the tests will either run on Speculos or on a physical # device depending on the backend -def create_backend(backend: str, firmware: Firmware) -> BackendInterface: +def create_backend(backend: str, arg_path: str, firmware: Firmware) -> BackendInterface: if backend.lower() == "ledgercomm": return LedgerCommBackend(firmware, interface="hid") elif backend.lower() == "ledgerwallet": return LedgerWalletBackend(firmware) elif backend.lower() == "speculos": - return SpeculosBackend(get_elf_path(firmware), firmware) + return SpeculosBackend(get_elf_path(arg_path, firmware), firmware) else: raise ValueError(f"Backend '{backend}' is unknown. Valid backends are: {BACKENDS}") # This fixture will create and return the backend client @pytest.fixture -def backend_client(backend_name: str, firmware: Firmware) -> BackendInterface: - with create_backend(backend_name, firmware) as b: +def backend_client(arg_backend: str, arg_path: str, firmware: Firmware) -> BackendInterface: + with create_backend(arg_backend, arg_path, firmware) as b: yield b # This final fixture will return the properly configured app client, to be used in tests From 78172cdd612f1fe34272ad203eb1e983b375f886 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 13 Sep 2022 18:20:19 +0200 Subject: [PATCH 156/184] CI - Changed build workflow to compile release applications instead; remove the artifacts upload since they were not used --- .github/workflows/build-workflow.yml | 40 +++++++--------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-workflow.yml b/.github/workflows/build-workflow.yml index 0c9ef5f..54bd896 100644 --- a/.github/workflows/build-workflow.yml +++ b/.github/workflows/build-workflow.yml @@ -1,7 +1,6 @@ name: Compilation on: - workflow_dispatch: push: branches: - master @@ -9,19 +8,14 @@ on: branches: - master - develop + workflow_dispatch: jobs: - nano_debug_build: - name: Build debug application for NanoS, X and S+ + nano_release_build: + name: Build release application for NanoS, X and S+ strategy: matrix: - include: - - SDK: "$NANOS_SDK" - artifact: nanos - - SDK: "$NANOX_SDK" - artifact: nanox - - SDK: "$NANOSP_SDK" - artifact: nanosp + sdk: ["$NANOS_SDK", "$NANOX_SDK", "$NANOSP_SDK"] runs-on: ubuntu-latest container: image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest @@ -29,28 +23,12 @@ jobs: steps: - name: Clone uses: actions/checkout@v2 - with: - submodules: recursive - - - name: Build an altcoin - run: | - make BOLOS_SDK=${{ matrix.SDK }} DEBUG=1 ALLOW_DATA=1 CHAIN=ethereum_classic - mv bin/app.elf ethereum_classic_${{ matrix.artifact }}.elf - - - name: Upload altcoin binary - uses: actions/upload-artifact@v2 - with: - name: ethereum_classic_${{ matrix.artifact }} - path: ./ethereum_classic_${{ matrix.artifact }}.elf - name: Build Ethereum run: | - make clean - make BOLOS_SDK=${{ matrix.SDK }} DEBUG=1 ALLOW_DATA=1 - mv bin/app.elf ethereum_${{ matrix.artifact }}.elf + make -j BOLOS_SDK=${{ matrix.sdk }} - - name: Upload app binary - uses: actions/upload-artifact@v2 - with: - name: ethereum_${{ matrix.artifact }} - path: ./ethereum_${{ matrix.artifact }}.elf \ No newline at end of file + - name: Build an altcoin + run: | + make clean + make -j BOLOS_SDK=${{ matrix.sdk }} CHAIN=polygon From fd93495096da09db79aa5a571961a87f542ba575 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 11 Aug 2022 13:54:03 +0200 Subject: [PATCH 157/184] CI - Add ragger tests --- .github/workflows/auto-author-assign.yml | 2 +- .github/workflows/ci-workflow.yml | 145 +++++++++++++++++++++-- tests/ragger/eip712/InputData.py | 38 +++--- tests/ragger/requirements.txt | 7 +- 4 files changed, 154 insertions(+), 38 deletions(-) diff --git a/.github/workflows/auto-author-assign.yml b/.github/workflows/auto-author-assign.yml index a19fb99..acd55ab 100644 --- a/.github/workflows/auto-author-assign.yml +++ b/.github/workflows/auto-author-assign.yml @@ -11,4 +11,4 @@ jobs: assign-author: runs-on: ubuntu-latest steps: - - uses: toshimaru/auto-author-assign@v1.4.0 \ No newline at end of file + - uses: toshimaru/auto-author-assign@v1.4.0 diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 85efce8..8a54522 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -1,7 +1,6 @@ name: Tests on: - workflow_dispatch: push: branches: - master @@ -9,12 +8,12 @@ on: branches: - master - develop + workflow_dispatch: jobs: scan-build: name: Clang Static Analyzer runs-on: ubuntu-latest - container: image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest @@ -25,6 +24,7 @@ jobs: run: | make clean scan-build --use-cc=clang -analyze-headers -enable-checker security -enable-checker unix -enable-checker valist -o scan-build --status-bugs make default + - uses: actions/upload-artifact@v2 if: failure() with: @@ -52,19 +52,21 @@ jobs: - name: Upload app binaries uses: actions/upload-artifact@v2 with: - name: e2e_elfs + name: e2e_zemu_elfs path: ./tests/zemu/elfs/ jobs-e2e-zemu-tests: name: E2E Zemu tests needs: [building_for_e2e_zemu_tests] runs-on: ubuntu-latest + steps: - name: Test run: | id echo $HOME echo $DISPLAY + - name: Checkout uses: actions/checkout@v2 @@ -90,7 +92,7 @@ jobs: path: tmp/ - name: Gather elfs - run: cp `find tmp/e2e_elfs/ -name "*.elf"` tests/zemu/elfs/ + run: cp `find tmp/e2e_zemu_elfs/ -name "*.elf"` tests/zemu/elfs/ - name: Run zemu tests run: cd tests/zemu/ && yarn test @@ -112,14 +114,14 @@ jobs: - name: Build testing binaries run: | mkdir tests/speculos/elfs - make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOS_SDK && mv bin/app.elf tests/speculos/elfs/nanos.elf - make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOX_SDK && mv bin/app.elf tests/speculos/elfs/nanox.elf - make clean && make DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOSP_SDK && mv bin/app.elf tests/speculos/elfs/nanosp.elf + make clean && make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOS_SDK && mv bin/app.elf tests/speculos/elfs/nanos.elf + make clean && make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOX_SDK && mv bin/app.elf tests/speculos/elfs/nanox.elf + make clean && make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK=$NANOSP_SDK && mv bin/app.elf tests/speculos/elfs/nanosp.elf - name: Upload app binaries uses: actions/upload-artifact@v2 with: - name: e2e_elfs + name: e2e_speculos_elfs path: ./tests/speculos/elfs @@ -127,8 +129,8 @@ jobs: name: Speculos tests strategy: matrix: - model: ["nanosp", "nanos", "nanox"] - + model: ["nanosp", "nanos", "nanox"] + needs: [building_for_e2e_speculos_tests] runs-on: ubuntu-latest @@ -145,7 +147,7 @@ jobs: path: tmp/ - name: Gather elfs - run: cp `find tmp/e2e_elfs/ -name "*.elf"` tests/speculos/elfs/ + run: cp `find tmp/e2e_speculos_elfs/ -name "*.elf"` tests/speculos/elfs/ - name: Install dependencies run: | @@ -154,6 +156,123 @@ jobs: pip install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt - name: Run speculos tests - run: | + run: | cd tests/speculos - pytest --model ${{ matrix.model }} --path ./elfs/${{ matrix.model }}.elf --display headless \ No newline at end of file + pytest --model ${{ matrix.model }} --path ./elfs/${{ matrix.model }}.elf --display headless + + +# ===================================================== +# RAGGER TESTS +# ===================================================== + + build_ragger_elfs: + name: Building binaries for Ragger tests + runs-on: ubuntu-latest + container: + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Build test binaries + run: | + make -j BOLOS_SDK=$NANOS_SDK CAL_TESTING_KEY=1 + mv bin/app.elf app-nanos.elf + make clean + make -j BOLOS_SDK=$NANOX_SDK CAL_TESTING_KEY=1 + mv bin/app.elf app-nanox.elf + make clean + make -j BOLOS_SDK=$NANOSP_SDK CAL_TESTING_KEY=1 + mv bin/app.elf app-nanosp.elf + + - name: Upload app binaries + uses: actions/upload-artifact@v2 + with: + name: ragger_elfs + path: ./app-*.elf + + create_ragger_env: + name: Cache Ragger environment + runs-on: ubuntu-latest + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: APT update + run: | + sudo apt update + + - name: Create virtual env with dependencies + run: | + cd tests/ragger + python3 -m venv ./venv + . ./venv/bin/activate + pip3 install --extra-index-url https://test.pypi.org/simple/ -r requirements.txt speculos + # Used for the cache key + echo "py_deps=$(pip freeze | md5sum | cut -d' ' -f1)" >> $GITHUB_ENV + + - name: Download QEMU + run: | + sudo apt install --download-only -y qemu-user-static + mkdir -p tests/ragger/packages + cp /var/cache/apt/archives/*.deb tests/ragger/packages/ + # Used for the cache key + echo "deb_deps=$(find /var/cache/apt/archives/ -maxdepth 0 -type f -name '*.deb' | md5sum | cut -d' ' -f 1)" >> $GITHUB_ENV + + - name: Set up cache + uses: actions/cache@v3 + with: + key: ${{ runner.os }}-raggenv-${{ env.py_deps }}-${{ env.deb_deps }} + path: | + tests/ragger/venv/ + tests/ragger/packages/ + outputs: + py_deps: ${{ env.py_deps }} + deb_deps: ${{ env.deb_deps }} + + + jobs-ragger-tests: + name: Ragger tests + strategy: + matrix: + model: ["nanos", "nanox", "nanosp"] + needs: [build_ragger_elfs, create_ragger_env] + runs-on: ubuntu-latest + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Download previously built artifacts + uses: actions/download-artifact@v2 + with: + name: ragger_elfs + path: tmp/ + + - name: Put them where they belong + run: | + mkdir -p tests/ragger/elfs + find tmp/ -type f -name '*.elf' -exec cp {} tests/ragger/elfs/ \; + + - name: Get cached environment + uses: actions/cache@v3 + with: + key: ${{ runner.os }}-raggenv-${{ needs.create_ragger_env.outputs.py_deps }}-${{ needs.create_ragger_env.outputs.deb_deps }} + path: | + tests/ragger/venv/ + tests/ragger/packages/ + + - name: Install QEMU + run: | + sudo mv tests/ragger/packages/*.deb /var/cache/apt/archives/ + sudo apt install -y qemu-user-static + + - name: Run tests + env: + CAL_SIGNATURE_TEST_KEY: ${{ secrets.CAL_SIGNATURE_TEST_KEY }} + run: | + cd tests/ragger + . ./venv/bin/activate + pytest --path ./elfs --model ${{ matrix.model }} diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index dda0e37..92f84f4 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -289,26 +289,21 @@ def prepare_filtering(filtr_data, message): def init_signature_context(types, domain): global sig_ctx - env_key = os.getenv("CAL_SIGNATURE_TEST_KEY") - if env_key: - key = base64.b64decode(env_key).decode() # base 64 string -> decode bytes -> string - print(key) - sig_ctx["key"] = SigningKey.from_pem(key, hashlib.sha256) - caddr = domain["verifyingContract"] - if caddr.startswith("0x"): - caddr = caddr[2:] - sig_ctx["caddr"] = bytearray.fromhex(caddr) - chainid = domain["chainId"] - sig_ctx["chainid"] = bytearray() - for i in range(8): - sig_ctx["chainid"].append(chainid & (0xff << (i * 8))) - sig_ctx["chainid"].reverse() - schema_str = json.dumps(types).replace(" ","") - schema_hash = hashlib.sha224(schema_str.encode()) - sig_ctx["schema_hash"] = bytearray.fromhex(schema_hash.hexdigest()) - - return True - return False + env_key = os.environ["CAL_SIGNATURE_TEST_KEY"] + key = base64.b64decode(env_key).decode() # base 64 string -> decode bytes -> string + sig_ctx["key"] = SigningKey.from_pem(key, hashlib.sha256) + caddr = domain["verifyingContract"] + if caddr.startswith("0x"): + caddr = caddr[2:] + sig_ctx["caddr"] = bytearray.fromhex(caddr) + chainid = domain["chainId"] + sig_ctx["chainid"] = bytearray() + for i in range(8): + sig_ctx["chainid"].append(chainid & (0xff << (i * 8))) + sig_ctx["chainid"].reverse() + schema_str = json.dumps(types).replace(" ","") + schema_hash = hashlib.sha224(schema_str.encode()) + sig_ctx["schema_hash"] = bytearray.fromhex(schema_hash.hexdigest()) def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_path = None) -> bool: global sig_ctx @@ -324,8 +319,7 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_p message = data_json["message"] if filtering_file_path: - if not init_signature_context(types, domain): - return False + init_signature_context(types, domain) filtr = read_filtering_file(domain, message, filtering_file_path) # send types definition diff --git a/tests/ragger/requirements.txt b/tests/ragger/requirements.txt index 246b518..a63552e 100644 --- a/tests/ragger/requirements.txt +++ b/tests/ragger/requirements.txt @@ -1,3 +1,6 @@ +requests>=2.28,<3.0 +click>=8.0,<9.0 # needed by the CI as it installs an older version and breaks dependencies +protobuf==3.20.1 # To fix the protobuf dependency bug ragger -pytest>=7.1.0,<7.2.0 -ecdsa>=0.18.0,<0.19.0 +pytest +ecdsa From 089de9e28f1868bff46fe9d24cd29168603bb977 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 16 Aug 2022 16:19:06 +0200 Subject: [PATCH 158/184] EIP712 tests - Renamed -test.json files into -data.json --- .../{00-simple_mail-test.json => 00-simple_mail-data.json} | 0 ...s_array_mail-test.json => 01-addresses_array_mail-data.json} | 0 ..._array_mail-test.json => 02-recipients_array_mail-data.json} | 0 .../{03-long_string-test.json => 03-long_string-data.json} | 0 .../{04-long_bytes-test.json => 04-long_bytes-data.json} | 0 .../{05-signed_ints-test.json => 05-signed_ints-data.json} | 0 .../input_files/{06-boolean-test.json => 06-boolean-data.json} | 0 .../{07-fixed_bytes-test.json => 07-fixed_bytes-data.json} | 0 .../input_files/{08-opensea-test.json => 08-opensea-data.json} | 0 .../input_files/{09-rarible-test.json => 09-rarible-data.json} | 0 ...al_arrays-test.json => 10-multidimensional_arrays-data.json} | 0 ...1-complex_structs-test.json => 11-complex_structs-data.json} | 0 tests/ragger/test_eip712.py | 2 +- 13 files changed, 1 insertion(+), 1 deletion(-) rename tests/ragger/eip712/input_files/{00-simple_mail-test.json => 00-simple_mail-data.json} (100%) rename tests/ragger/eip712/input_files/{01-addresses_array_mail-test.json => 01-addresses_array_mail-data.json} (100%) rename tests/ragger/eip712/input_files/{02-recipients_array_mail-test.json => 02-recipients_array_mail-data.json} (100%) rename tests/ragger/eip712/input_files/{03-long_string-test.json => 03-long_string-data.json} (100%) rename tests/ragger/eip712/input_files/{04-long_bytes-test.json => 04-long_bytes-data.json} (100%) rename tests/ragger/eip712/input_files/{05-signed_ints-test.json => 05-signed_ints-data.json} (100%) rename tests/ragger/eip712/input_files/{06-boolean-test.json => 06-boolean-data.json} (100%) rename tests/ragger/eip712/input_files/{07-fixed_bytes-test.json => 07-fixed_bytes-data.json} (100%) rename tests/ragger/eip712/input_files/{08-opensea-test.json => 08-opensea-data.json} (100%) rename tests/ragger/eip712/input_files/{09-rarible-test.json => 09-rarible-data.json} (100%) rename tests/ragger/eip712/input_files/{10-multidimensional_arrays-test.json => 10-multidimensional_arrays-data.json} (100%) rename tests/ragger/eip712/input_files/{11-complex_structs-test.json => 11-complex_structs-data.json} (100%) diff --git a/tests/ragger/eip712/input_files/00-simple_mail-test.json b/tests/ragger/eip712/input_files/00-simple_mail-data.json similarity index 100% rename from tests/ragger/eip712/input_files/00-simple_mail-test.json rename to tests/ragger/eip712/input_files/00-simple_mail-data.json diff --git a/tests/ragger/eip712/input_files/01-addresses_array_mail-test.json b/tests/ragger/eip712/input_files/01-addresses_array_mail-data.json similarity index 100% rename from tests/ragger/eip712/input_files/01-addresses_array_mail-test.json rename to tests/ragger/eip712/input_files/01-addresses_array_mail-data.json diff --git a/tests/ragger/eip712/input_files/02-recipients_array_mail-test.json b/tests/ragger/eip712/input_files/02-recipients_array_mail-data.json similarity index 100% rename from tests/ragger/eip712/input_files/02-recipients_array_mail-test.json rename to tests/ragger/eip712/input_files/02-recipients_array_mail-data.json diff --git a/tests/ragger/eip712/input_files/03-long_string-test.json b/tests/ragger/eip712/input_files/03-long_string-data.json similarity index 100% rename from tests/ragger/eip712/input_files/03-long_string-test.json rename to tests/ragger/eip712/input_files/03-long_string-data.json diff --git a/tests/ragger/eip712/input_files/04-long_bytes-test.json b/tests/ragger/eip712/input_files/04-long_bytes-data.json similarity index 100% rename from tests/ragger/eip712/input_files/04-long_bytes-test.json rename to tests/ragger/eip712/input_files/04-long_bytes-data.json diff --git a/tests/ragger/eip712/input_files/05-signed_ints-test.json b/tests/ragger/eip712/input_files/05-signed_ints-data.json similarity index 100% rename from tests/ragger/eip712/input_files/05-signed_ints-test.json rename to tests/ragger/eip712/input_files/05-signed_ints-data.json diff --git a/tests/ragger/eip712/input_files/06-boolean-test.json b/tests/ragger/eip712/input_files/06-boolean-data.json similarity index 100% rename from tests/ragger/eip712/input_files/06-boolean-test.json rename to tests/ragger/eip712/input_files/06-boolean-data.json diff --git a/tests/ragger/eip712/input_files/07-fixed_bytes-test.json b/tests/ragger/eip712/input_files/07-fixed_bytes-data.json similarity index 100% rename from tests/ragger/eip712/input_files/07-fixed_bytes-test.json rename to tests/ragger/eip712/input_files/07-fixed_bytes-data.json diff --git a/tests/ragger/eip712/input_files/08-opensea-test.json b/tests/ragger/eip712/input_files/08-opensea-data.json similarity index 100% rename from tests/ragger/eip712/input_files/08-opensea-test.json rename to tests/ragger/eip712/input_files/08-opensea-data.json diff --git a/tests/ragger/eip712/input_files/09-rarible-test.json b/tests/ragger/eip712/input_files/09-rarible-data.json similarity index 100% rename from tests/ragger/eip712/input_files/09-rarible-test.json rename to tests/ragger/eip712/input_files/09-rarible-data.json diff --git a/tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json b/tests/ragger/eip712/input_files/10-multidimensional_arrays-data.json similarity index 100% rename from tests/ragger/eip712/input_files/10-multidimensional_arrays-test.json rename to tests/ragger/eip712/input_files/10-multidimensional_arrays-data.json diff --git a/tests/ragger/eip712/input_files/11-complex_structs-test.json b/tests/ragger/eip712/input_files/11-complex_structs-data.json similarity index 100% rename from tests/ragger/eip712/input_files/11-complex_structs-test.json rename to tests/ragger/eip712/input_files/11-complex_structs-data.json diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index f2ae413..93bee99 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -19,7 +19,7 @@ bip32 = [ def input_files() -> List[str]: files = [] for file in os.scandir("./eip712/input_files"): - if fnmatch.fnmatch(file, "*-test.json"): + if fnmatch.fnmatch(file, "*-data.json"): files.append(file.path) return sorted(files) From 3ee1fa419aa0578d057adb999c53c784736a2d28 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 16 Aug 2022 18:44:28 +0200 Subject: [PATCH 159/184] Now supports EIP-712 filtering with missing chain id & contract address --- src_features/signMessageEIP712/context.c | 4 ++++ src_features/signMessageEIP712/context.h | 1 + src_features/signMessageEIP712/field_hash.c | 9 ++++---- src_features/signMessageEIP712/filtering.c | 2 +- tests/ragger/eip712/InputData.py | 7 ++++++ .../eip712/input_files/12-sign_in-data.json | 23 +++++++++++++++++++ .../eip712/input_files/12-sign_in-filter.json | 7 ++++++ .../ragger/eip712/input_files/12-sign_in.ini | 4 ++++ tests/ragger/test_eip712.py | 4 ++++ 9 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/ragger/eip712/input_files/12-sign_in-data.json create mode 100644 tests/ragger/eip712/input_files/12-sign_in-filter.json create mode 100644 tests/ragger/eip712/input_files/12-sign_in.ini diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context.c index 2c79644..7b45518 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context.c @@ -52,6 +52,10 @@ bool eip712_context_init(void) { return false; } + // Since they are optional, they might not be provided by the JSON data + explicit_bzero(eip712_context->contract_addr, sizeof(eip712_context->contract_addr)); + eip712_context->chain_id = 0; + struct_state = NOT_INITIALIZED; return true; diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context.h index 5a546fd..d4cc6f5 100644 --- a/src_features/signMessageEIP712/context.h +++ b/src_features/signMessageEIP712/context.h @@ -8,6 +8,7 @@ typedef struct { uint8_t contract_addr[ADDRESS_LENGTH]; + uint64_t chain_id; uint8_t schema_hash[224 / 8]; } s_eip712_context; diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index bd079b4..83aa45f 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -181,15 +181,14 @@ static bool field_hash_domain_special_fields(const void *const field_ptr, } memcpy(eip712_context->contract_addr, data, data_length); } else if (strncmp(key, "chainId", keylen) == 0) { - uint64_t chainId = u64_from_BE(data, data_length); - - if (chainId != chainConfig->chainId) { + eip712_context->chain_id = u64_from_BE(data, data_length); + if ((eip712_context->chain_id != 0) && (eip712_context->chain_id != chainConfig->chainId)) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", sizeof(chainConfig->chainId), &chainConfig->chainId, - sizeof(chainId), - &chainId); + sizeof(eip712_context->chain_id), + &eip712_context->chain_id); return false; } } diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 7510cd0..7984714 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -66,7 +66,7 @@ static bool verify_filtering_signature(uint8_t dname_length, cx_sha256_init(&hash_ctx); // Chain ID - chain_id = __builtin_bswap64(chainConfig->chainId); + chain_id = __builtin_bswap64(eip712_context->chain_id); hash_nbytes((uint8_t *) &chain_id, sizeof(chain_id), (cx_hash_t *) &hash_ctx); // Contract address diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 92f84f4..47c3018 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -286,9 +286,16 @@ def prepare_filtering(filtr_data, message): else: filtering_paths = {} +def handle_optional_domain_values(domain): + if "chainId" not in domain.keys(): + domain["chainId"] = 0 + if "verifyingContract" not in domain.keys(): + domain["verifyingContract"] = "0x0000000000000000000000000000000000000000" + def init_signature_context(types, domain): global sig_ctx + handle_optional_domain_values(domain) env_key = os.environ["CAL_SIGNATURE_TEST_KEY"] key = base64.b64decode(env_key).decode() # base 64 string -> decode bytes -> string sig_ctx["key"] = SigningKey.from_pem(key, hashlib.sha256) diff --git a/tests/ragger/eip712/input_files/12-sign_in-data.json b/tests/ragger/eip712/input_files/12-sign_in-data.json new file mode 100644 index 0000000..1792e88 --- /dev/null +++ b/tests/ragger/eip712/input_files/12-sign_in-data.json @@ -0,0 +1,23 @@ +{ + "domain": { + "name": "Who are You?", + "version": "1" + }, + "message": { + "banner": "Please sign this message with your wallet", + "curDate": 1660659773, + "id": 38 + }, + "primaryType": "Auth", + "types": { + "EIP712Domain": [ + { "name": "name", "type": "string" }, + { "name": "version", "type": "string" } + ], + "Auth": [ + { "name": "banner", "type": "string" }, + { "name": "curDate", "type": "uint32" }, + { "name": "id", "type": "uint8" } + ] + } +} diff --git a/tests/ragger/eip712/input_files/12-sign_in-filter.json b/tests/ragger/eip712/input_files/12-sign_in-filter.json new file mode 100644 index 0000000..69da4f1 --- /dev/null +++ b/tests/ragger/eip712/input_files/12-sign_in-filter.json @@ -0,0 +1,7 @@ +{ + "name": "Ethereum sign-in", + "fields": { + "curDate": "Timestamp", + "id": "Identifier" + } +} diff --git a/tests/ragger/eip712/input_files/12-sign_in.ini b/tests/ragger/eip712/input_files/12-sign_in.ini new file mode 100644 index 0000000..83047ab --- /dev/null +++ b/tests/ragger/eip712/input_files/12-sign_in.ini @@ -0,0 +1,4 @@ +[signature] +v = 1b +r = 7be1671577753c13bfd1da8b234b6df8484daf47351c2366637fd291dd4aa4d9 +s = 1a7ffbb01dc8a64e9ee97d19b8f154e9eecbe0b1bfb9dcfa781a65e474573963 diff --git a/tests/ragger/test_eip712.py b/tests/ragger/test_eip712.py index 93bee99..bb53ba8 100644 --- a/tests/ragger/test_eip712.py +++ b/tests/ragger/test_eip712.py @@ -75,6 +75,10 @@ def test_eip712_new(app_client: EthereumClient, input_file: Path, verbose: bool, assert InputData.process_file(app_client, input_file, filter_file) == True v, r, s = app_client.eip712_sign_new(bip32) + #print("[signature]") + #print("v = %s" % (v.hex())) + #print("r = %s" % (r.hex())) + #print("s = %s" % (s.hex())) assert v == bytes.fromhex(config["signature"]["v"]) assert r == bytes.fromhex(config["signature"]["r"]) From 9e4df4b65548ff1983aecd9b4d467d7ffee600a1 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 17 Aug 2022 10:45:43 +0200 Subject: [PATCH 160/184] Updated EIP712 filtering signature specs to start with a magic number Making it impossible for a signature of one type to be valid as another --- doc/ethapp.adoc | 6 ++++-- src_features/signMessageEIP712/filtering.c | 14 ++++++++++++++ src_features/signMessageEIP712/filtering.h | 3 +++ tests/ragger/eip712/InputData.py | 2 ++ 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 3aaf738..7a30e33 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -816,19 +816,21 @@ If activated, fields will be by default hidden unless they receive a field name Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*. Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation. +The first byte is used so that a signature of one type cannot be valid as another type. The signature is computed on : -chain ID (BE) || contract address || schema hash || display name +183 || chain ID (BE) || contract address || schema hash || display name ##### Field name substitution Name substitution commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones). +The first byte is used so that a signature of one type cannot be valid as another type. The signature is computed on : -chain ID (BE) || contract address || schema hash || field path || display name +72 || chain ID (BE) || contract address || schema hash || field path || display name #### Coding diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 7984714..19eb2f0 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -65,6 +65,20 @@ static bool verify_filtering_signature(uint8_t dname_length, 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_STRUCT_FIELD: + hash_byte(FILTERING_MAGIC_STRUCT_FIELD, (cx_hash_t *) &hash_ctx); + break; + case FILTERING_CONTRACT_NAME: + 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; + } + // Chain ID chain_id = __builtin_bswap64(eip712_context->chain_id); hash_nbytes((uint8_t *) &chain_id, sizeof(chain_id), (cx_hash_t *) &hash_ctx); diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index 541521c..04d20dc 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -6,6 +6,9 @@ #include #include +#define FILTERING_MAGIC_CONTRACT_NAME 0b10110111 // 183 +#define FILTERING_MAGIC_STRUCT_FIELD 0b01001000 // ~183 = 72 + typedef enum { FILTERING_CONTRACT_NAME, FILTERING_STRUCT_FIELD } e_filtering_type; bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filtering_type type); diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 47c3018..359ca69 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -246,6 +246,7 @@ def send_filtering_contract_name(display_name: str): global sig_ctx msg = bytearray() + msg.append(183) msg += sig_ctx["chainid"] msg += sig_ctx["caddr"] msg += sig_ctx["schema_hash"] @@ -262,6 +263,7 @@ def send_filtering_field_name(display_name): path_str = ".".join(current_path) msg = bytearray() + msg.append(72) msg += sig_ctx["chainid"] msg += sig_ctx["caddr"] msg += sig_ctx["schema_hash"] From 3f02bf672c6462bc6e322ae94c080a57672ccbcf Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 19 Aug 2022 12:13:24 +0200 Subject: [PATCH 161/184] Updated linter to clang-format 12.0.1 --- .github/workflows/lint-workflow.yml | 4 ++-- src/eth_plugin_handler.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint-workflow.yml b/.github/workflows/lint-workflow.yml index b9ea676..f74416d 100644 --- a/.github/workflows/lint-workflow.yml +++ b/.github/workflows/lint-workflow.yml @@ -19,8 +19,8 @@ jobs: uses: actions/checkout@v2 - name: Lint - uses: DoozyX/clang-format-lint-action@v0.13 + uses: DoozyX/clang-format-lint-action@v0.14 with: source: "./" extensions: "h,c" - clangFormatVersion: 12.0.0 + clangFormatVersion: 12.0.1 diff --git a/src/eth_plugin_handler.c b/src/eth_plugin_handler.c index d0de69c..e5187ca 100644 --- a/src/eth_plugin_handler.c +++ b/src/eth_plugin_handler.c @@ -113,7 +113,7 @@ static bool eth_plugin_perform_init_old_internal(uint8_t *contractAddress, j++) { if (memcmp(init->selector, (const void *) PIC(selectors[j]), SELECTOR_SIZE) == 0) { if ((INTERNAL_ETH_PLUGINS[i].availableCheck == NULL) || - ((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck)) ()) { + ((PluginAvailableCheck) PIC(INTERNAL_ETH_PLUGINS[i].availableCheck))()) { strlcpy(dataContext.tokenContext.pluginName, INTERNAL_ETH_PLUGINS[i].alias, PLUGIN_ID_LENGTH); From 93b0bb08f72316ac9ceffbc105e74f2e674eef42 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 23 Aug 2022 12:01:32 +0200 Subject: [PATCH 162/184] Better handling of missing EIP721 error codes + add a missing one for EIP712 filtering signature checking --- src/apdu_constants.h | 1 + src_features/signMessageEIP712/commands_712.c | 2 ++ src_features/signMessageEIP712/filtering.c | 1 + 3 files changed, 4 insertions(+) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 19111b3..55ff4c9 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -35,6 +35,7 @@ #define COMMON_INS_GET_WALLET_ID 0x04 #define APDU_RESPONSE_OK 0x9000 +#define APDU_RESPONSE_ERROR_NO_INFO 0x6a00 #define APDU_RESPONSE_INVALID_DATA 0x6a80 #define APDU_RESPONSE_INSUFFICIENT_MEMORY 0x6a84 #define APDU_RESPONSE_INVALID_INS 0x6d00 diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 9d926ba..b51f519 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -25,6 +25,8 @@ void handle_eip712_return_code(bool success) { if (success) { apdu_response_code = APDU_RESPONSE_OK; + } else if (apdu_response_code == APDU_RESPONSE_OK) { // somehow not set + apdu_response_code = APDU_RESPONSE_ERROR_NO_INFO; } *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(apdu_response_code); diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 19eb2f0..71a87cb 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -110,6 +110,7 @@ static bool verify_filtering_signature(uint8_t dname_length, 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"); + apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; #endif } From ed479128fdf8d3ce4beb268ef2340a87d7efbd88 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 19 Aug 2022 18:31:00 +0200 Subject: [PATCH 163/184] EIP712 Filtering count checking --- doc/ethapp.adoc | 33 +++++++++----- src/apdu_constants.h | 2 +- src_features/signMessageEIP712/commands_712.c | 15 ++++--- src_features/signMessageEIP712/commands_712.h | 16 +++---- src_features/signMessageEIP712/filtering.c | 33 +++++++++----- src_features/signMessageEIP712/filtering.h | 2 +- src_features/signMessageEIP712/path.c | 1 + src_features/signMessageEIP712/ui_logic.c | 35 +++++++++++++++ src_features/signMessageEIP712/ui_logic.h | 4 ++ tests/ragger/eip712/InputData.py | 45 ++++++++++--------- tests/ragger/ethereum_client/client.py | 8 ++-- .../ragger/ethereum_client/command_builder.py | 12 +++-- 12 files changed, 139 insertions(+), 67 deletions(-) diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 7a30e33..758e761 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -810,22 +810,21 @@ Full filtering is disabled by default and has to be changed with this APDU (defa Field substitution will be ignored if the full filtering is not activated. -If activated, fields will be by default hidden unless they receive a field name substitution. +This command should come before the domain & message implementations. If activated, fields will be by default hidden unless they receive a field name substitution. -##### Contract name substitution +##### Message info -Name substitution commands should come right after the contract address from the domain has been sent with a *SEND STRUCT IMPLEMENTATION*. -Perfect moment to do it is when the domain implementation has been sent, just before sending the message implementation. +This command should come right after the implementation of the domain has been sent with *SEND STRUCT IMPLEMENTATION*, just before sending the message implementation. The first byte is used so that a signature of one type cannot be valid as another type. The signature is computed on : -183 || chain ID (BE) || contract address || schema hash || display name +183 || chain ID (BE) || contract address || schema hash || filters count || display name -##### Field name substitution +##### Show field -Name substitution commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones). +These commands should come before the corresponding *SEND STRUCT IMPLEMENTATION* and are only usable for message fields (and not domain ones). The first byte is used so that a signature of one type cannot be valid as another type. The signature is computed on : @@ -842,19 +841,31 @@ _Command_ | E0 | 1E | 00 | 00 : activate - 0F : contract name + 0F : message info - FF : field name + FF : show field | variable | variable |========================================================================= _Input data_ -##### If P1 == activate +##### If P2 == activate None -##### If P1 == contract name OR P1 == field name +##### If P2 == message info + +[width="80%"] +|========================================== +| *Description* | *Length (byte)* +| Display name length | 1 +| Display name | variable +| Filters count | 1 +| Signature length | 1 +| Signature | variable +|========================================== + +##### If P2 == show field [width="80%"] |========================================== diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 55ff4c9..234294e 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -41,7 +41,7 @@ #define APDU_RESPONSE_INVALID_INS 0x6d00 #define APDU_RESPONSE_INVALID_P1_P2 0x6b00 #define APDU_RESPONSE_CONDITION_NOT_SATISFIED 0x6985 -#define APDU_RESPONSE_REF_DATA_NOT_USABLE 0x6a88 +#define APDU_RESPONSE_REF_DATA_NOT_FOUND 0x6a88 #ifdef HAVE_STARKWARE diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index b51f519..fe2fb03 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -139,14 +139,15 @@ bool handle_eip712_filtering(const uint8_t *const apdu_buf) { ret = compute_schema_hash(); } break; - case P2_FILT_CONTRACT_NAME: - case P2_FILT_FIELD_NAME: - type = (apdu_buf[OFFSET_P2] == P2_FILT_CONTRACT_NAME) ? FILTERING_CONTRACT_NAME - : FILTERING_STRUCT_FIELD; + 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_CONTRACT_NAME) && ret) { + if ((apdu_buf[OFFSET_P2] == P2_FILT_MESSAGE_INFO) && ret) { reply_apdu = false; } } @@ -177,6 +178,10 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) { if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } else if ((ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) && + (ui_712_remaining_filters() != 0)) { + PRINTF("%d EIP712 filters are missing\n", ui_712_remaining_filters()); + apdu_response_code = APDU_RESPONSE_REF_DATA_NOT_FOUND; } else if (parseBip32(&apdu_buf[OFFSET_CDATA], &length, &tmpCtx.messageSigningContext.bip32) != NULL) { if (!N_storage.verbose_eip712 && (ui_712_get_filtering_mode() == EIP712_FILTERING_BASIC)) { diff --git a/src_features/signMessageEIP712/commands_712.h b/src_features/signMessageEIP712/commands_712.h index 8cfb3a1..e97d77b 100644 --- a/src_features/signMessageEIP712/commands_712.h +++ b/src_features/signMessageEIP712/commands_712.h @@ -11,14 +11,14 @@ #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_CONTRACT_NAME 0x0F -#define P2_FILT_FIELD_NAME 0xFF +#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" diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index 71a87cb..a43c788 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -67,10 +67,10 @@ static bool verify_filtering_signature(uint8_t dname_length, // Magic number, makes it so a signature of one type can't be used as another switch (type) { - case FILTERING_STRUCT_FIELD: + case FILTERING_SHOW_FIELD: hash_byte(FILTERING_MAGIC_STRUCT_FIELD, (cx_hash_t *) &hash_ctx); break; - case FILTERING_CONTRACT_NAME: + case FILTERING_PROVIDE_MESSAGE_INFO: hash_byte(FILTERING_MAGIC_CONTRACT_NAME, (cx_hash_t *) &hash_ctx); break; default: @@ -93,8 +93,11 @@ static bool verify_filtering_signature(uint8_t dname_length, sizeof(eip712_context->schema_hash), (cx_hash_t *) &hash_ctx); - if (type == FILTERING_STRUCT_FIELD) { + 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 @@ -131,13 +134,14 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filt const char *dname; uint8_t sig_len; const uint8_t *sig; + uint8_t offset = 0; - if (type == FILTERING_CONTRACT_NAME) { + 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_STRUCT_FIELD + } else // FILTERING_SHOW_FIELD { if (path_get_root_type() != ROOT_MESSAGE) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; @@ -145,20 +149,25 @@ bool provide_filtering_info(const uint8_t *const payload, uint8_t length, e_filt } } if (length > 0) { - dname_len = payload[0]; + dname_len = payload[offset++]; 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)) { + 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_CONTRACT_NAME) { + 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_STRUCT_FIELD + } else // FILTERING_SHOW_FIELD { if (dname_len > 0) // don't substitute for an empty name { diff --git a/src_features/signMessageEIP712/filtering.h b/src_features/signMessageEIP712/filtering.h index 04d20dc..3e33b9a 100644 --- a/src_features/signMessageEIP712/filtering.h +++ b/src_features/signMessageEIP712/filtering.h @@ -9,7 +9,7 @@ #define FILTERING_MAGIC_CONTRACT_NAME 0b10110111 // 183 #define FILTERING_MAGIC_STRUCT_FIELD 0b01001000 // ~183 = 72 -typedef enum { FILTERING_CONTRACT_NAME, FILTERING_STRUCT_FIELD } e_filtering_type; +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); diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index b4dd32b..aa6fad1 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -486,6 +486,7 @@ static bool path_advance_in_struct(void) { } if (path_struct->depth_count > 0) { *depth += 1; + ui_712_notify_filter_change(); end_reached = (*depth == fields_count); } if (end_reached) { diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index bd054a3..0099936 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -503,6 +503,24 @@ e_eip712_filtering_mode ui_712_get_filtering_mode(void) { return ui_ctx->filtering_mode; } +/** + * Set the number of filters this message should process + * + * @param[in] count number of filters + */ +void ui_712_set_filters_count(uint8_t count) { + ui_ctx->filters_to_process = count; +} + +/** + * Get the number of filters left to process + * + * @return number of filters + */ +uint8_t ui_712_remaining_filters(void) { + return ui_ctx->filters_to_process; +} + /** * Reset all the UI struct field flags */ @@ -521,4 +539,21 @@ void ui_712_queue_struct_to_review(void) { } } +/** + * Notify of a filter change from a path advance + * + * This function figures out by itself if there is anything to do + */ +void ui_712_notify_filter_change(void) { + if (path_get_root_type() == ROOT_MESSAGE) { + if (ui_ctx->filtering_mode == EIP712_FILTERING_FULL) { + if (ui_ctx->filters_to_process > 0) { + if (ui_ctx->field_flags & UI_712_FIELD_SHOWN) { + ui_ctx->filters_to_process -= 1; + } + } + } + } +} + #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 8d3cdf1..47a2b7e 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -18,6 +18,7 @@ typedef struct { bool end_reached; e_ui_position pos; uint8_t filtering_mode; + uint8_t filters_to_process; uint8_t field_flags; uint8_t structs_to_review; } t_ui_context; @@ -39,7 +40,10 @@ void ui_712_field_flags_reset(void); void ui_712_finalize_field(void); void ui_712_set_filtering_mode(e_eip712_filtering_mode mode); e_eip712_filtering_mode ui_712_get_filtering_mode(void); +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); #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/tests/ragger/eip712/InputData.py b/tests/ragger/eip712/InputData.py index 359ca69..89e877c 100644 --- a/tests/ragger/eip712/InputData.py +++ b/tests/ragger/eip712/InputData.py @@ -192,7 +192,7 @@ def send_struct_impl_field(value, field): if filtering_paths: path = ".".join(current_path) if path in filtering_paths.keys(): - send_filtering_field_name(filtering_paths[path]) + send_filtering_show_field(filtering_paths[path]) app_client.eip712_send_struct_impl_struct_field(data) @@ -242,37 +242,38 @@ def send_struct_impl(structs, data, structname): return True # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures -def send_filtering_contract_name(display_name: str): +def send_filtering_message_info(display_name: str, filters_count: int): global sig_ctx - msg = bytearray() - msg.append(183) - msg += sig_ctx["chainid"] - msg += sig_ctx["caddr"] - msg += sig_ctx["schema_hash"] + to_sign = bytearray() + to_sign.append(183) + to_sign += sig_ctx["chainid"] + to_sign += sig_ctx["caddr"] + to_sign += sig_ctx["schema_hash"] + to_sign.append(filters_count) for char in display_name: - msg.append(ord(char)) + to_sign.append(ord(char)) - sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) - app_client.eip712_filtering_send_contract_name(display_name, sig) + sig = sig_ctx["key"].sign_deterministic(to_sign, sigencode=sigencode_der) + app_client.eip712_filtering_message_info(display_name, filters_count, sig) # ledgerjs doesn't actually sign anything, and instead uses already pre-computed signatures -def send_filtering_field_name(display_name): +def send_filtering_show_field(display_name): global sig_ctx path_str = ".".join(current_path) - msg = bytearray() - msg.append(72) - msg += sig_ctx["chainid"] - msg += sig_ctx["caddr"] - msg += sig_ctx["schema_hash"] + to_sign = bytearray() + to_sign.append(72) + to_sign += sig_ctx["chainid"] + to_sign += sig_ctx["caddr"] + to_sign += sig_ctx["schema_hash"] for char in path_str: - msg.append(ord(char)) + to_sign.append(ord(char)) for char in display_name: - msg.append(ord(char)) - sig = sig_ctx["key"].sign_deterministic(msg, sigencode=sigencode_der) - app_client.eip712_filtering_send_field_name(display_name, sig) + to_sign.append(ord(char)) + sig = sig_ctx["key"].sign_deterministic(to_sign, sigencode=sigencode_der) + app_client.eip712_filtering_show_field(display_name, sig) def read_filtering_file(domain, message, filtering_file_path): data_json = None @@ -349,9 +350,9 @@ def process_file(aclient: EthereumClient, input_file_path: str, filtering_file_p if filtering_file_path: if filtr and "name" in filtr: - send_filtering_contract_name(filtr["name"]) + send_filtering_message_info(filtr["name"], len(filtering_paths)) else: - send_filtering_contract_name(domain["name"]) + send_filtering_message_info(domain["name"], len(filtering_paths)) # send message implementation app_client.eip712_send_struct_impl_root_struct(message_typename) diff --git a/tests/ragger/ethereum_client/client.py b/tests/ragger/ethereum_client/client.py index 6ac352e..61b2012 100644 --- a/tests/ragger/ethereum_client/client.py +++ b/tests/ragger/ethereum_client/client.py @@ -144,13 +144,13 @@ class EthereumClient: self._eip712_filtering = True assert self._recv().status == 0x9000 - def eip712_filtering_send_contract_name(self, name: str, sig: bytes): - with self._send(self._cmd_builder.eip712_filtering_send_contract_name(name, sig)): + def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes): + with self._send(self._cmd_builder.eip712_filtering_message_info(name, filters_count, sig)): self._enable_click_until_response() self._disable_click_until_response() assert self._recv().status == 0x9000 - def eip712_filtering_send_field_name(self, name: str, sig: bytes): - with self._send(self._cmd_builder.eip712_filtering_send_field_name(name, sig)): + def eip712_filtering_show_field(self, name: str, sig: bytes): + with self._send(self._cmd_builder.eip712_filtering_show_field(name, sig)): pass assert self._recv().status == 0x9000 diff --git a/tests/ragger/ethereum_client/command_builder.py b/tests/ragger/ethereum_client/command_builder.py index 5024a3c..134405f 100644 --- a/tests/ragger/ethereum_client/command_builder.py +++ b/tests/ragger/ethereum_client/command_builder.py @@ -151,13 +151,19 @@ class EthereumCmdBuilder: data += sig return data - def eip712_filtering_send_contract_name(self, name: str, sig: bytes) -> bytes: + def eip712_filtering_message_info(self, name: str, filters_count: int, sig: bytes) -> bytes: + data = bytearray() + data.append(len(name)) + data += self._string_to_bytes(name) + data.append(filters_count) + data.append(len(sig)) + data += sig return self._serialize(InsType.EIP712_SEND_FILTERING, P1Type.COMPLETE_SEND, P2Type.FILTERING_CONTRACT_NAME, - self._eip712_filtering_send_name(name, sig)) + data) - def eip712_filtering_send_field_name(self, name: str, sig: bytes) -> bytes: + def eip712_filtering_show_field(self, name: str, sig: bytes) -> bytes: return self._serialize(InsType.EIP712_SEND_FILTERING, P1Type.COMPLETE_SEND, P2Type.FILTERING_FIELD_NAME, From a27826dcf6fe825b40270b1e2ce199526cea94dd Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 24 Aug 2022 14:42:27 +0200 Subject: [PATCH 164/184] Fix EIP712 type_hash function description --- src_features/signMessageEIP712/type_hash.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index fcd6f52..c70383d 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -164,8 +164,8 @@ static const void **get_struct_dependencies(uint8_t *const deps_count, * * @param[in] struct_name name of the given struct * @param[in] struct_name_length length of the name of the given struct - * @param[in] with_deps if hashed typestring should include struct dependencies - * @return pointer to encoded string or \ref NULL in case of a memory allocation error + * @param[out] hash_buf buffer containing the resulting type_hash + * @return whether the type_hash was successful or not */ bool type_hash(const char *const struct_name, const uint8_t struct_name_length, uint8_t *hash_buf) { const void *const struct_ptr = get_structn(struct_name, struct_name_length); From 1b46ccb190a5b0b0d79c5f16718a1788c61070f7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 25 Aug 2022 10:26:48 +0200 Subject: [PATCH 165/184] Extract EIP-712 UI code --- .../signMessageEIP712 => src_bagl}/ui_flow_712.c | 0 .../signMessageEIP712 => src_bagl}/ui_flow_712.h | 0 src_features/signMessageEIP712/ui_logic.c | 8 ++++---- src_features/signMessageEIP712/ui_logic.h | 4 ++-- src_features/signMessageEIP712_common/common_712.c | 6 ++---- src_features/signMessageEIP712_common/common_712.h | 4 ++-- 6 files changed, 10 insertions(+), 12 deletions(-) rename {src_features/signMessageEIP712 => src_bagl}/ui_flow_712.c (100%) rename {src_features/signMessageEIP712 => src_bagl}/ui_flow_712.h (100%) diff --git a/src_features/signMessageEIP712/ui_flow_712.c b/src_bagl/ui_flow_712.c similarity index 100% rename from src_features/signMessageEIP712/ui_flow_712.c rename to src_bagl/ui_flow_712.c diff --git a/src_features/signMessageEIP712/ui_flow_712.h b/src_bagl/ui_flow_712.h similarity index 100% rename from src_features/signMessageEIP712/ui_flow_712.h rename to src_bagl/ui_flow_712.h diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 0099936..3c6635a 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -451,8 +451,8 @@ void ui_712_deinit(void) { * @param[in] e unused here, just needed to match the UI function signature * @return unused here, just needed to match the UI function signature */ -unsigned int ui_712_approve(const bagl_element_t *e) { - ui_712_approve_cb(e); +unsigned int ui_712_approve() { + ui_712_approve_cb(); eip712_context_deinit(); return 0; } @@ -464,8 +464,8 @@ unsigned int ui_712_approve(const bagl_element_t *e) { * @param[in] e unused here, just needed to match the UI function signature * @return unused here, just needed to match the UI function signature */ -unsigned int ui_712_reject(const bagl_element_t *e) { - ui_712_reject_cb(e); +unsigned int ui_712_reject() { + ui_712_reject_cb(); eip712_context_deinit(); return 0; } diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 47a2b7e..72224b0 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -29,8 +29,8 @@ void 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); void ui_712_end_sign(void); -unsigned int ui_712_approve(const bagl_element_t *e); -unsigned int ui_712_reject(const bagl_element_t *e); +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_message_hash(void); diff --git a/src_features/signMessageEIP712_common/common_712.c b/src_features/signMessageEIP712_common/common_712.c index 27b345f..4bed719 100644 --- a/src_features/signMessageEIP712_common/common_712.c +++ b/src_features/signMessageEIP712_common/common_712.c @@ -5,14 +5,13 @@ static const uint8_t EIP_712_MAGIC[] = {0x19, 0x01}; -unsigned int ui_712_approve_cb(const bagl_element_t *e) { +unsigned int ui_712_approve_cb() { uint8_t privateKeyData[INT256_LENGTH]; uint8_t hash[INT256_LENGTH]; uint8_t signature[100]; cx_ecfp_private_key_t privateKey; uint32_t tx = 0; - (void) e; io_seproxyhal_io_heartbeat(); cx_keccak_init(&global_sha3, 256); cx_hash((cx_hash_t *) &global_sha3, @@ -74,8 +73,7 @@ unsigned int ui_712_approve_cb(const bagl_element_t *e) { return 0; // do not redraw the widget } -unsigned int ui_712_reject_cb(const bagl_element_t *e) { - (void) e; +unsigned int ui_712_reject_cb() { reset_app_context(); G_io_apdu_buffer[0] = 0x69; G_io_apdu_buffer[1] = 0x85; diff --git a/src_features/signMessageEIP712_common/common_712.h b/src_features/signMessageEIP712_common/common_712.h index 9c279d1..1283a61 100644 --- a/src_features/signMessageEIP712_common/common_712.h +++ b/src_features/signMessageEIP712_common/common_712.h @@ -4,7 +4,7 @@ #include #include "ux.h" -unsigned int ui_712_approve_cb(const bagl_element_t *e); -unsigned int ui_712_reject_cb(const bagl_element_t *e); +unsigned int ui_712_approve_cb(); +unsigned int ui_712_reject_cb(); #endif // COMMON_EIP712_H_ From 5ce56bd53ef862f063631d47b8b447360eefa94a Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 25 Aug 2022 10:54:34 +0200 Subject: [PATCH 166/184] Renamed EIP712 context files to less generic ones --- src_features/signMessageEIP712/commands_712.c | 2 +- src_features/signMessageEIP712/{context.c => context_712.c} | 2 +- src_features/signMessageEIP712/{context.h => context_712.h} | 0 src_features/signMessageEIP712/field_hash.c | 2 +- src_features/signMessageEIP712/filtering.c | 2 +- src_features/signMessageEIP712/path.c | 2 +- src_features/signMessageEIP712/schema_hash.c | 2 +- src_features/signMessageEIP712/typed_data.c | 2 +- src_features/signMessageEIP712/ui_logic.c | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) rename src_features/signMessageEIP712/{context.c => context_712.c} (98%) rename src_features/signMessageEIP712/{context.h => context_712.h} (100%) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index fe2fb03..c1d3a56 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -5,7 +5,7 @@ #include #include "commands_712.h" #include "apdu_constants.h" // APDU response codes -#include "context.h" +#include "context_712.h" #include "field_hash.h" #include "path.h" #include "ui_logic.h" diff --git a/src_features/signMessageEIP712/context.c b/src_features/signMessageEIP712/context_712.c similarity index 98% rename from src_features/signMessageEIP712/context.c rename to src_features/signMessageEIP712/context_712.c index 7b45518..829398c 100644 --- a/src_features/signMessageEIP712/context.c +++ b/src_features/signMessageEIP712/context_712.c @@ -2,7 +2,7 @@ #include #include -#include "context.h" +#include "context_712.h" #include "mem.h" #include "mem_utils.h" #include "sol_typenames.h" diff --git a/src_features/signMessageEIP712/context.h b/src_features/signMessageEIP712/context_712.h similarity index 100% rename from src_features/signMessageEIP712/context.h rename to src_features/signMessageEIP712/context_712.h diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index 83aa45f..f472b6d 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -9,7 +9,7 @@ #include "shared_context.h" #include "ui_logic.h" #include "ethUtils.h" // KECCAK256_HASH_BYTESIZE -#include "context.h" // contract_addr +#include "context_712.h" // contract_addr #include "utils.h" // u64_from_BE #include "apdu_constants.h" // APDU response codes #include "typed_data.h" diff --git a/src_features/signMessageEIP712/filtering.c b/src_features/signMessageEIP712/filtering.c index a43c788..1ba40dc 100644 --- a/src_features/signMessageEIP712/filtering.c +++ b/src_features/signMessageEIP712/filtering.c @@ -4,7 +4,7 @@ #include "hash_bytes.h" #include "ethUstream.h" // INT256_LENGTH #include "apdu_constants.h" // APDU return codes -#include "context.h" +#include "context_712.h" #include "commands_712.h" #include "typed_data.h" #include "path.h" diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index aa6fad1..998296c 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -4,7 +4,7 @@ #include #include "path.h" #include "mem.h" -#include "context.h" +#include "context_712.h" #include "commands_712.h" #include "type_hash.h" #include "shared_context.h" diff --git a/src_features/signMessageEIP712/schema_hash.c b/src_features/signMessageEIP712/schema_hash.c index 10af1e2..e47c2ee 100644 --- a/src_features/signMessageEIP712/schema_hash.c +++ b/src_features/signMessageEIP712/schema_hash.c @@ -4,7 +4,7 @@ #include "hash_bytes.h" #include "typed_data.h" #include "format_hash_field_type.h" -#include "context.h" +#include "context_712.h" // the SDK does not define a SHA-224 type, define it here so it's easier // to understand in the code diff --git a/src_features/signMessageEIP712/typed_data.c b/src_features/signMessageEIP712/typed_data.c index 4239f86..a410ccf 100644 --- a/src_features/signMessageEIP712/typed_data.c +++ b/src_features/signMessageEIP712/typed_data.c @@ -5,7 +5,7 @@ #include "typed_data.h" #include "sol_typenames.h" #include "apdu_constants.h" // APDU response codes -#include "context.h" +#include "context_712.h" #include "mem.h" #include "mem_utils.h" diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 3c6635a..1589b19 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -12,7 +12,7 @@ #include "ethUtils.h" // getEthDisplayableAddress #include "utils.h" // uint256_to_decimal #include "common_712.h" -#include "context.h" // eip712_context_deinit +#include "context_712.h" // eip712_context_deinit #include "uint256.h" // tostring256 && tostring256_signed #include "path.h" // path_get_root_type #include "apdu_constants.h" // APDU response codes From edac57df06a6ae3b89daad37b90e5a798ab6f3db Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 1 Sep 2022 10:18:41 +0200 Subject: [PATCH 167/184] Increased memory allocator pool to 8K --- src_common/mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_common/mem.c b/src_common/mem.c index 7d03b34..02bd6b1 100644 --- a/src_common/mem.c +++ b/src_common/mem.c @@ -3,7 +3,7 @@ #include #include "mem.h" -#define SIZE_MEM_BUFFER 5120 +#define SIZE_MEM_BUFFER 8192 static uint8_t mem_buffer[SIZE_MEM_BUFFER]; static size_t mem_idx; From 35c77ff29c0cdafcc44b76e36e78768e18bf30ea Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 1 Sep 2022 10:19:03 +0200 Subject: [PATCH 168/184] Increased EIP712 max array depth to 8 --- src_features/signMessageEIP712/path.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/path.h b/src_features/signMessageEIP712/path.h index 8d97da4..d03b4c5 100644 --- a/src_features/signMessageEIP712/path.h +++ b/src_features/signMessageEIP712/path.h @@ -7,7 +7,7 @@ #include #define MAX_PATH_DEPTH 16 -#define MAX_ARRAY_DEPTH 4 +#define MAX_ARRAY_DEPTH 8 typedef struct { uint8_t path_index; From 78c0bfe4249249fabf13ca9d13a9caf28cd8dfa7 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Thu, 15 Sep 2022 16:43:39 +0200 Subject: [PATCH 169/184] Removed EIP-712 strict chain ID check --- src_features/signMessageEIP712/field_hash.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src_features/signMessageEIP712/field_hash.c b/src_features/signMessageEIP712/field_hash.c index f472b6d..cc57dae 100644 --- a/src_features/signMessageEIP712/field_hash.c +++ b/src_features/signMessageEIP712/field_hash.c @@ -182,15 +182,6 @@ static bool field_hash_domain_special_fields(const void *const field_ptr, memcpy(eip712_context->contract_addr, data, data_length); } else if (strncmp(key, "chainId", keylen) == 0) { eip712_context->chain_id = u64_from_BE(data, data_length); - if ((eip712_context->chain_id != 0) && (eip712_context->chain_id != chainConfig->chainId)) { - apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - PRINTF("EIP712Domain chain ID mismatch, expected 0x%.*h, got 0x%.*h !\n", - sizeof(chainConfig->chainId), - &chainConfig->chainId, - sizeof(eip712_context->chain_id), - &eip712_context->chain_id); - return false; - } } return true; } From 2dd757786d749cc2ca03125b85544bc1cb3e8d68 Mon Sep 17 00:00:00 2001 From: Clement Bouvet Date: Thu, 13 Oct 2022 12:59:57 +0200 Subject: [PATCH 170/184] commonise plugins --- src_bagl/ui_flow_signTx.c | 1 + src_bagl/ui_plugin.c | 29 +--------------------------- src_bagl/ui_plugin.h | 2 -- src_common/plugins.c | 40 +++++++++++++++++++++++++++++++++++++++ src_common/plugins.h | 11 +++++++++++ 5 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 src_common/plugins.c create mode 100644 src_common/plugins.h diff --git a/src_bagl/ui_flow_signTx.c b/src_bagl/ui_flow_signTx.c index 6d05d6d..574dd9c 100644 --- a/src_bagl/ui_flow_signTx.c +++ b/src_bagl/ui_flow_signTx.c @@ -7,6 +7,7 @@ #include "eth_plugin_handler.h" #include "ui_plugin.h" #include "common_ui.h" +#include "plugins.h" // clang-format off UX_STEP_NOCB( diff --git a/src_bagl/ui_plugin.c b/src_bagl/ui_plugin.c index ed3f52f..c3743fa 100644 --- a/src_bagl/ui_plugin.c +++ b/src_bagl/ui_plugin.c @@ -3,38 +3,11 @@ #include "eth_plugin_handler.h" #include "ui_callbacks.h" #include "ui_plugin.h" +#include "plugins.h" // This function is not exported by the SDK void ux_layout_paging_redisplay_by_addr(unsigned int stack_slot); -void plugin_ui_get_id() { - ethQueryContractID_t pluginQueryContractID; - eth_plugin_prepare_query_contract_ID(&pluginQueryContractID, - strings.common.fullAddress, - sizeof(strings.common.fullAddress), - strings.common.fullAmount, - sizeof(strings.common.fullAmount)); - // Query the original contract for ID if it's not an internal alias - if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, (void *) &pluginQueryContractID)) { - PRINTF("Plugin query contract ID call failed\n"); - io_seproxyhal_touch_tx_cancel(NULL); - } -} - -void plugin_ui_get_item() { - ethQueryContractUI_t pluginQueryContractUI; - eth_plugin_prepare_query_contract_UI(&pluginQueryContractUI, - dataContext.tokenContext.pluginUiCurrentItem, - strings.common.fullAddress, - sizeof(strings.common.fullAddress), - strings.common.fullAmount, - sizeof(strings.common.fullAmount)); - if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_UI, (void *) &pluginQueryContractUI)) { - PRINTF("Plugin query contract UI call failed\n"); - io_seproxyhal_touch_tx_cancel(NULL); - } -} - void display_next_plugin_item(bool entering) { if (entering) { if (dataContext.tokenContext.pluginUiState == PLUGIN_UI_OUTSIDE) { diff --git a/src_bagl/ui_plugin.h b/src_bagl/ui_plugin.h index ae86b6a..2f9fe60 100644 --- a/src_bagl/ui_plugin.h +++ b/src_bagl/ui_plugin.h @@ -1,8 +1,6 @@ #ifndef _UI_PLUGIN_H_ #define _UI_PLUGIN_H_ -void plugin_ui_get_id(); -void plugin_ui_get_item(); void display_next_plugin_item(bool entering); #endif // _UI_PLUGIN_H_ diff --git a/src_common/plugins.c b/src_common/plugins.c new file mode 100644 index 0000000..42c17a9 --- /dev/null +++ b/src_common/plugins.c @@ -0,0 +1,40 @@ +#include "eth_plugin_handler.h" +#include "ui_callbacks.h" + +void plugin_ui_get_id(void) { + ethQueryContractID_t pluginQueryContractID; + eth_plugin_prepare_query_contract_ID(&pluginQueryContractID, + strings.common.fullAddress, + sizeof(strings.common.fullAddress), + strings.common.fullAmount, + sizeof(strings.common.fullAmount)); + // Query the original contract for ID if it's not an internal alias + if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_ID, (void *) &pluginQueryContractID)) { + PRINTF("Plugin query contract ID call failed\n"); + io_seproxyhal_touch_tx_cancel(NULL); + } +} + +void plugin_ui_get_item_internal(char *title_buffer, + size_t title_buffer_size, + char *msg_buffer, + size_t msg_buffer_size) { + ethQueryContractUI_t pluginQueryContractUI; + eth_plugin_prepare_query_contract_UI(&pluginQueryContractUI, + dataContext.tokenContext.pluginUiCurrentItem, + title_buffer, + title_buffer_size, + msg_buffer, + msg_buffer_size); + if (!eth_plugin_call(ETH_PLUGIN_QUERY_CONTRACT_UI, (void *) &pluginQueryContractUI)) { + PRINTF("Plugin query contract UI call failed\n"); + io_seproxyhal_touch_tx_cancel(NULL); + } +} + +void plugin_ui_get_item(void) { + plugin_ui_get_item_internal(strings.common.fullAddress, + sizeof(strings.common.fullAddress), + strings.common.fullAmount, + sizeof(strings.common.fullAmount)); +} \ No newline at end of file diff --git a/src_common/plugins.h b/src_common/plugins.h new file mode 100644 index 0000000..1ccc123 --- /dev/null +++ b/src_common/plugins.h @@ -0,0 +1,11 @@ +#ifndef _PLUGIN_H_ +#define _PLUGIN_H_ + +void plugin_ui_get_id(); +void plugin_ui_get_item(); +void plugin_ui_get_item_internal(uint8_t *title_buffer, + size_t title_buffer_size, + uint8_t *msg_buffer, + size_t msg_buffer_size); + +#endif // _PLUGIN_H_ \ No newline at end of file From 51e4ab2a6f0f4669988de1f3fdbc114846668550 Mon Sep 17 00:00:00 2001 From: Clement Bouvet Date: Fri, 14 Oct 2022 10:35:06 +0200 Subject: [PATCH 171/184] move some stack utility functions to common --- src_bagl/ui_flow_stark_sign.c | 13 +------------ src_common/starkDisplayUtils.c | 17 +++++++++++++++++ src_common/starkDisplayUtils.h | 6 ++++++ 3 files changed, 24 insertions(+), 12 deletions(-) create mode 100644 src_common/starkDisplayUtils.c create mode 100644 src_common/starkDisplayUtils.h diff --git a/src_bagl/ui_flow_stark_sign.c b/src_bagl/ui_flow_stark_sign.c index df3c6a1..548e880 100644 --- a/src_bagl/ui_flow_stark_sign.c +++ b/src_bagl/ui_flow_stark_sign.c @@ -3,18 +3,7 @@ #include "shared_context.h" #include "ui_callbacks.h" #include "ethUtils.h" - -void stark_sign_display_master_account() { - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - 32, - dataContext.starkContext.transferDestination); -} - -void stark_sign_display_condition_fact() { - snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataContext.starkContext.fact); -} +#include "starkDisplayUtils.h" // clang-format off UX_STEP_NOCB(ux_stark_limit_order_1_step, diff --git a/src_common/starkDisplayUtils.c b/src_common/starkDisplayUtils.c new file mode 100644 index 0000000..a59ae99 --- /dev/null +++ b/src_common/starkDisplayUtils.c @@ -0,0 +1,17 @@ +#ifdef HAVE_STARKWARE + +#include "shared_context.h" + +void stark_sign_display_master_account() { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + 32, + dataContext.starkContext.transferDestination); +} + +void stark_sign_display_condition_fact() { + snprintf(strings.tmp.tmp, sizeof(strings.tmp.tmp), "0x%.*H", 32, dataContext.starkContext.fact); +} + +#endif \ No newline at end of file diff --git a/src_common/starkDisplayUtils.h b/src_common/starkDisplayUtils.h new file mode 100644 index 0000000..48399b8 --- /dev/null +++ b/src_common/starkDisplayUtils.h @@ -0,0 +1,6 @@ +#ifdef HAVE_STARKWARE + +void stark_sign_display_master_account(); +void stark_sign_display_condition_fact(); + +#endif \ No newline at end of file From d2659ed33273ea2130e187611eb674ae25e3e641 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 14 Oct 2022 17:40:14 +0200 Subject: [PATCH 172/184] Version bump to 1.10.0 --- CHANGELOG.md | 2 ++ Makefile | 6 +++--- tests/speculos/test_configuration_cmd.py | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79d9d0b..c6ba0c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.10.0](https://github.com/ledgerhq/app-ethereum/compare/1.9.20...1.10.0) - 2022-XX-XX + ## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-10-10 ### Added diff --git a/Makefile b/Makefile index eedf848..b14e367 100644 --- a/Makefile +++ b/Makefile @@ -33,9 +33,9 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'" ################## APPVERSION_M=1 -APPVERSION_N=9 -APPVERSION_P=20 -APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) +APPVERSION_N=10 +APPVERSION_P=0 +APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)-dev APP_LOAD_FLAGS= --appFlags 0xa40 --dep Ethereum:$(APPVERSION) ########################### diff --git a/tests/speculos/test_configuration_cmd.py b/tests/speculos/test_configuration_cmd.py index 6f9e5e0..de41bde 100644 --- a/tests/speculos/test_configuration_cmd.py +++ b/tests/speculos/test_configuration_cmd.py @@ -1,10 +1,10 @@ def test_configuration(cmd): if cmd.model == "nanos": - assert cmd.get_configuration() == (14, 1, 9, 20) - + assert cmd.get_configuration() == (14, 1, 10, 0) + if cmd.model == "nanox": - assert cmd.get_configuration() == (14, 1, 9, 20) + assert cmd.get_configuration() == (14, 1, 10, 0) if cmd.model == "nanosp": - assert cmd.get_configuration() == (14, 1, 9, 20) \ No newline at end of file + assert cmd.get_configuration() == (14, 1, 10, 0) From fe3f61c7a09042a9d2836602184172c217a65023 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 13 Sep 2022 17:32:54 +0200 Subject: [PATCH 173/184] Updated changelog --- CHANGELOG.md | 4 ++++ doc/ethapp.adoc | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6ba0c9..fda0f81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [1.10.0](https://github.com/ledgerhq/app-ethereum/compare/1.9.20...1.10.0) - 2022-XX-XX +### Changed + +- EIP-712 signatures are now computed on-device and display their content (clear-signing) + ## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-10-10 ### Added diff --git a/doc/ethapp.adoc b/doc/ethapp.adoc index 758e761..bc57b3b 100644 --- a/doc/ethapp.adoc +++ b/doc/ethapp.adoc @@ -34,7 +34,7 @@ Application version 1.9.19 - 2022-05-17 ### 1.9.18 - Add PERFORM PRIVACY OPERATION -### 1.9.19 +### 1.10.0 - Add EIP712 STRUCT DEFINITION & EIP712 STRUCT IMPLEMENTATION - Update to SIGN ETH EIP712 From d297a6695168e2f54257283de62f71ae88466eab Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Mon, 3 Oct 2022 13:44:50 +0200 Subject: [PATCH 174/184] Enforce proper memory alignment on APDU buffer for response status words --- src_features/signMessageEIP712/commands_712.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index c1d3a56..dc29b3d 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -28,7 +28,9 @@ void handle_eip712_return_code(bool success) { } else if (apdu_response_code == APDU_RESPONSE_OK) { // somehow not set apdu_response_code = APDU_RESPONSE_ERROR_NO_INFO; } - *(uint16_t *) G_io_apdu_buffer = __builtin_bswap16(apdu_response_code); + + G_io_apdu_buffer[0] = (apdu_response_code >> 8) & 0xff; + G_io_apdu_buffer[1] = apdu_response_code & 0xff; // Send back the response, do not restart the event loop io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, 2); From c803322f3c0c9555f2c52aabcacb4f2b08643ad0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 5 Oct 2022 10:21:52 +0200 Subject: [PATCH 175/184] Review fixes --- src/apdu_constants.h | 2 ++ src/main.c | 29 ++++++++++--------- src/shared_context.h | 2 +- src_bagl/ui_flow.c | 24 ++++++++------- src_common/mem.c | 12 ++++++-- .../signMessageEIP712_v0/cmd_signMessage712.c | 4 +-- 6 files changed, 45 insertions(+), 28 deletions(-) diff --git a/src/apdu_constants.h b/src/apdu_constants.h index 8cf18ce..cab0688 100644 --- a/src/apdu_constants.h +++ b/src/apdu_constants.h @@ -30,6 +30,8 @@ #define P2_CHAINCODE 0x01 #define P1_FIRST 0x00 #define P1_MORE 0x80 +#define P2_EIP712_LEGACY_IMPLEM 0x00 +#define P2_EIP712_FULL_IMPLEM 0x01 #define COMMON_CLA 0xB0 #define COMMON_INS_GET_WALLET_ID 0x04 diff --git a/src/main.c b/src/main.c index cc67580..1828416 100644 --- a/src/main.c +++ b/src/main.c @@ -674,21 +674,24 @@ void handleApdu(unsigned int *flags, unsigned int *tx) { break; case INS_SIGN_EIP_712_MESSAGE: - if (G_io_apdu_buffer[OFFSET_P2] == 0) { - memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); - handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], - G_io_apdu_buffer[OFFSET_P2], - G_io_apdu_buffer + OFFSET_CDATA, - G_io_apdu_buffer[OFFSET_LC], - flags, - tx); - } else { + switch (G_io_apdu_buffer[OFFSET_P2]) { + case P2_EIP712_LEGACY_IMPLEM: + memset(tmpCtx.transactionContext.tokenSet, 0, MAX_ITEMS); + handleSignEIP712Message_v0(G_io_apdu_buffer[OFFSET_P1], + G_io_apdu_buffer[OFFSET_P2], + G_io_apdu_buffer + OFFSET_CDATA, + G_io_apdu_buffer[OFFSET_LC], + flags, + tx); + break; #ifdef HAVE_EIP712_FULL_SUPPORT - *flags |= IO_ASYNCH_REPLY; - handle_eip712_sign(G_io_apdu_buffer); -#else - THROW(0x6B00); + case P2_EIP712_FULL_IMPLEM: + *flags |= IO_ASYNCH_REPLY; + handle_eip712_sign(G_io_apdu_buffer); + break; #endif // HAVE_EIP712_FULL_SUPPORT + default: + THROW(APDU_RESPONSE_INVALID_P1_P2); } break; diff --git a/src/shared_context.h b/src/shared_context.h index 38fa0da..3b3b98b 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -222,6 +222,6 @@ extern uint32_t eth2WithdrawalIndex; #endif void reset_app_context(void); -const uint8_t *parseBip32(const uint8_t *, uint8_t *, bip32_path_t *); +const uint8_t *parseBip32(const uint8_t *dataBuffer, uint8_t *dataLength, bip32_path_t *bip32); #endif // _SHARED_CONTEXT_H_ diff --git a/src_bagl/ui_flow.c b/src_bagl/ui_flow.c index cbb6f25..5f6ed11 100644 --- a/src_bagl/ui_flow.c +++ b/src_bagl/ui_flow.c @@ -1,5 +1,11 @@ #include "shared_context.h" +#include "ui_callbacks.h" #include "common_ui.h" +#include "utils.h" + +#define ENABLED_STR "Enabled" +#define DISABLED_STR "Disabled" +#define BUF_INCREMENT (MAX(strlen(ENABLED_STR), strlen(DISABLED_STR)) + 1) void display_settings(const ux_flow_step_t* const start_step); void switch_settings_blind_signing(void); @@ -69,7 +75,7 @@ UX_STEP_CB( switch_settings_display_data(), { .title = "Debug data", - .text = strings.common.fullAddress + 9 + .text = strings.common.fullAddress + BUF_INCREMENT }); UX_STEP_CB( @@ -78,7 +84,7 @@ UX_STEP_CB( switch_settings_display_nonce(), { .title = "Account nonce", - .text = strings.common.fullAddress + 18 + .text = strings.common.fullAddress + (BUF_INCREMENT * 2) }); #else @@ -102,7 +108,7 @@ UX_STEP_CB( "Debug data", "Show contract data", "details", - strings.common.fullAddress + 9 + strings.common.fullAddress + BUF_INCREMENT }); UX_STEP_CB( @@ -113,7 +119,7 @@ UX_STEP_CB( "Nonce", "Show account nonce", "in transactions", - strings.common.fullAddress + 18 + strings.common.fullAddress + (BUF_INCREMENT * 2) }); #endif @@ -127,7 +133,7 @@ UX_STEP_CB( "Verbose EIP-712", "Ignore filtering &", "display raw content", - strings.common.fullAddress + 27 + strings.common.fullAddress + (BUF_INCREMENT * 3) }); #endif // HAVE_EIP712_FULL_SUPPORT @@ -152,7 +158,6 @@ UX_FLOW(ux_settings_flow, &ux_settings_flow_back_step); void display_settings(const ux_flow_step_t* const start_step) { - const char* const values[] = {"Enabled", "Disabled"}; bool settings[] = {N_storage.dataAllowed, N_storage.contractDetails, N_storage.displayNonce, @@ -161,13 +166,12 @@ void display_settings(const ux_flow_step_t* const start_step) { #endif // HAVE_EIP712_FULL_SUPPORT }; uint8_t offset = 0; - uint8_t increment = MAX(strlen(values[0]), strlen(values[1])) + 1; - for (unsigned int i = 0; i < (sizeof(settings) / sizeof(settings[0])); ++i) { + for (unsigned int i = 0; i < ARRAY_SIZE(settings); ++i) { strlcpy(strings.common.fullAddress + offset, - (settings[i] ? values[0] : values[1]), + (settings[i] ? ENABLED_STR : DISABLED_STR), sizeof(strings.common.fullAddress) - offset); - offset += increment; + offset += BUF_INCREMENT; } ux_flow_init(0, ux_settings_flow, start_step); diff --git a/src_common/mem.c b/src_common/mem.c index 02bd6b1..2234cf4 100644 --- a/src_common/mem.c +++ b/src_common/mem.c @@ -1,3 +1,11 @@ +/** + * Dynamic allocator that uses a fixed-length buffer that is hopefully big enough + * + * The two functions alloc & dealloc use the buffer as a simple stack. + * Especially useful when an unpredictable amount of data will be received and have to be stored + * during the transaction but discarded right after. + */ + #ifdef HAVE_DYN_MEM_ALLOC #include @@ -23,7 +31,7 @@ void mem_reset(void) { } /** - * Allocates a chunk of the memory buffer of a given size. + * Allocates (push) a chunk of the memory buffer of a given size. * * Checks to see if there are enough space left in the memory buffer, returns * the current location in the memory buffer and moves the index accordingly. @@ -41,7 +49,7 @@ void *mem_alloc(size_t size) { } /** - * De-allocates a chunk of memory buffer by a given size. + * De-allocates (pop) a chunk of memory buffer by a given size. * * @param[in] size Requested deallocation size in bytes */ diff --git a/src_features/signMessageEIP712_v0/cmd_signMessage712.c b/src_features/signMessageEIP712_v0/cmd_signMessage712.c index 8b45d1c..539e8f0 100644 --- a/src_features/signMessageEIP712_v0/cmd_signMessage712.c +++ b/src_features/signMessageEIP712_v0/cmd_signMessage712.c @@ -14,7 +14,7 @@ void handleSignEIP712Message_v0(uint8_t p1, (void) tx; (void) p2; if (p1 != 00) { - THROW(0x6B00); + THROW(APDU_RESPONSE_INVALID_P1_P2); } if (appState != APP_STATE_IDLE) { reset_app_context(); @@ -23,7 +23,7 @@ void handleSignEIP712Message_v0(uint8_t p1, workBuffer = parseBip32(workBuffer, &dataLength, &tmpCtx.messageSigningContext.bip32); if ((workBuffer == NULL) || (dataLength < (KECCAK256_HASH_BYTESIZE * 2))) { - THROW(0x6a80); + THROW(APDU_RESPONSE_INVALID_DATA); } memmove(tmpCtx.messageSigningContext712.domainHash, workBuffer, KECCAK256_HASH_BYTESIZE); memmove(tmpCtx.messageSigningContext712.messageHash, From 3e3da4c814beea00f20ef38f55f0f2d7cd83a774 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 4 Oct 2022 10:23:18 +0200 Subject: [PATCH 176/184] Minimize duplicated code in cross devices settings UX flow --- src_bagl/ui_flow.c | 64 +++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/src_bagl/ui_flow.c b/src_bagl/ui_flow.c index 5f6ed11..7cac427 100644 --- a/src_bagl/ui_flow.c +++ b/src_bagl/ui_flow.c @@ -57,73 +57,67 @@ UX_FLOW(ux_idle_flow, &ux_idle_flow_4_step, FLOW_LOOP); -#if defined(TARGET_NANOS) - // clang-format off UX_STEP_CB( ux_settings_flow_blind_signing_step, +#ifdef TARGET_NANOS bnnn_paging, - switch_settings_blind_signing(), - { - .title = "Blind signing", - .text = strings.common.fullAddress, - }); - -UX_STEP_CB( - ux_settings_flow_display_data_step, - bnnn_paging, - switch_settings_display_data(), - { - .title = "Debug data", - .text = strings.common.fullAddress + BUF_INCREMENT - }); - -UX_STEP_CB( - ux_settings_flow_display_nonce_step, - bnnn_paging, - switch_settings_display_nonce(), - { - .title = "Account nonce", - .text = strings.common.fullAddress + (BUF_INCREMENT * 2) - }); - #else - -UX_STEP_CB( - ux_settings_flow_blind_signing_step, bnnn, +#endif switch_settings_blind_signing(), { +#ifdef TARGET_NANOS + .title = "Blind signing", + .text = +#else "Blind signing", "Transaction", "blind signing", - strings.common.fullAddress, +#endif + strings.common.fullAddress }); UX_STEP_CB( ux_settings_flow_display_data_step, +#ifdef TARGET_NANOS + bnnn_paging, +#else bnnn, +#endif switch_settings_display_data(), { +#ifdef TARGET_NANOS + .title = "Debug data", + .text = +#else "Debug data", "Show contract data", "details", +#endif strings.common.fullAddress + BUF_INCREMENT }); - UX_STEP_CB( +UX_STEP_CB( ux_settings_flow_display_nonce_step, +#ifdef TARGET_NANOS + bnnn_paging, +#else bnnn, +#endif switch_settings_display_nonce(), { +#ifdef TARGET_NANOS + .title = "Account nonce", + .text = +#else "Nonce", "Show account nonce", "in transactions", +#endif strings.common.fullAddress + (BUF_INCREMENT * 2) }); -#endif - #ifdef HAVE_EIP712_FULL_SUPPORT UX_STEP_CB( ux_settings_flow_verbose_eip712_step, @@ -205,7 +199,7 @@ void switch_settings_verbose_eip712(void) { ////////////////////////////////////////////////////////////////////// // clang-format off -#if defined(TARGET_NANOS) +#ifdef TARGET_NANOS UX_STEP_CB( ux_warning_contract_data_step, bnnn_paging, @@ -214,7 +208,7 @@ UX_STEP_CB( "Error", "Blind signing must be enabled in Settings", }); -#elif defined(TARGET_NANOX) || defined(TARGET_NANOS2) +#else UX_STEP_CB( ux_warning_contract_data_step, pnn, From 0b288cd924f93da4637212abe29ef7d52b99ada2 Mon Sep 17 00:00:00 2001 From: Jorge Martins <106586648+jmartins-ledger@users.noreply.github.com> Date: Tue, 18 Oct 2022 11:09:00 +0200 Subject: [PATCH 177/184] Eip712 review (#355) * Possible security fix. It is possible to send a new structure definition after sending a structure implementation, which makes the app treat unrestricted data as if it was a well defined structure. This commit tries to fix that behaviour. Once a structure implementation is sent, we consider all structures to be defined and we do not allow new definitions. * Fix previous commit --- src_features/signMessageEIP712/commands_712.c | 5 +++++ src_features/signMessageEIP712/context_712.h | 2 +- src_features/signMessageEIP712/path.c | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index dc29b3d..9945bd8 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -52,6 +52,11 @@ bool handle_eip712_struct_def(const uint8_t *const apdu_buf) { if (eip712_context == NULL) { ret = eip712_context_init(); } + + if (struct_state == DEFINED) { + ret = false; + } + if (ret) { switch (apdu_buf[OFFSET_P2]) { case P2_DEF_NAME: diff --git a/src_features/signMessageEIP712/context_712.h b/src_features/signMessageEIP712/context_712.h index d4cc6f5..2ae5c8d 100644 --- a/src_features/signMessageEIP712/context_712.h +++ b/src_features/signMessageEIP712/context_712.h @@ -17,7 +17,7 @@ extern s_eip712_context *eip712_context; bool eip712_context_init(void); void eip712_context_deinit(void); -typedef enum { NOT_INITIALIZED, INITIALIZED } e_struct_init; +typedef enum { NOT_INITIALIZED, INITIALIZED, DEFINED } e_struct_init; extern e_struct_init struct_state; #endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 998296c..53eddf3 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -363,6 +363,8 @@ bool path_set_root(const char *const struct_name, uint8_t name_length) { path_struct->root_type = ROOT_MESSAGE; } + struct_state = DEFINED; + // because the first field could be a struct type path_update(); return true; From 109dffc70e35f8aca35099aa3c99cd0e8426bcbf Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Tue, 18 Oct 2022 11:48:03 +0200 Subject: [PATCH 178/184] Better context check for EIP712 sign It was possible to define empty structs without any fields and right after, trigger the EIP712 sign UI flow for blank domain & message hashes. Added checks if there is actually anything relevant to sign. --- src_features/signMessageEIP712/commands_712.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 9945bd8..3c0cc9e 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -13,6 +13,7 @@ #include "schema_hash.h" #include "filtering.h" #include "common_712.h" +#include "ethUtils.h" // allzeroes /** * Send the response to the previous APDU command @@ -185,6 +186,14 @@ bool handle_eip712_sign(const uint8_t *const apdu_buf) { if (eip712_context == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; + } + // if the final hashes are still zero or if there are some unimplemented fields + else if (allzeroes(tmpCtx.messageSigningContext712.domainHash, + sizeof(tmpCtx.messageSigningContext712.domainHash)) || + allzeroes(tmpCtx.messageSigningContext712.messageHash, + sizeof(tmpCtx.messageSigningContext712.messageHash)) || + (path_get_field() != NULL)) { + apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; } else if ((ui_712_get_filtering_mode() == EIP712_FILTERING_FULL) && (ui_712_remaining_filters() != 0)) { PRINTF("%d EIP712 filters are missing\n", ui_712_remaining_filters()); From c158c3e5023ce1001217ba8cddd286c66d3f65eb Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 21 Oct 2022 18:00:59 +0200 Subject: [PATCH 179/184] Better split between business logic & UI code for EIP-712 --- src/common_ui.h | 5 + src_bagl/ui_flow_712.c | 57 ----------- src_bagl/ui_flow_712.h | 12 --- src_bagl/ui_flow_signMessage712.c | 117 ++++++++++++---------- src_bagl/ui_flow_signMessage712_v0.c | 72 +++++++++++++ src_features/signMessageEIP712/ui_logic.c | 43 +++----- src_features/signMessageEIP712/ui_logic.h | 5 +- 7 files changed, 160 insertions(+), 151 deletions(-) delete mode 100644 src_bagl/ui_flow_712.c delete mode 100644 src_bagl/ui_flow_712.h create mode 100644 src_bagl/ui_flow_signMessage712_v0.c diff --git a/src/common_ui.h b/src/common_ui.h index 3bca6b9..9b2b0dc 100644 --- a/src/common_ui.h +++ b/src/common_ui.h @@ -25,6 +25,11 @@ void ui_191_switch_to_message_end(void); void ui_191_switch_to_sign(void); void ui_191_switch_to_question(void); +// EIP-712 +void ui_712_start(void); +void ui_712_switch_to_message(void); +void ui_712_switch_to_sign(void); + #include "ui_callbacks.h" #include diff --git a/src_bagl/ui_flow_712.c b/src_bagl/ui_flow_712.c deleted file mode 100644 index 182a89a..0000000 --- a/src_bagl/ui_flow_712.c +++ /dev/null @@ -1,57 +0,0 @@ -#ifdef HAVE_EIP712_FULL_SUPPORT - -#include "ui_flow_712.h" -#include "ui_logic.h" -#include "shared_context.h" // strings - -// clang-format off -UX_STEP_NOCB( - ux_712_step_review, - pnn, - { - &C_icon_eye, - "Review", - "typed message", - }); -UX_STEP_NOCB( - ux_712_step_dynamic, - bnnn_paging, - { - .title = strings.tmp.tmp2, - .text = strings.tmp.tmp, - } -); -UX_STEP_INIT( - ux_712_step_dummy, - NULL, - NULL, - { - ui_712_next_field(); - } -); -UX_STEP_CB( - ux_712_step_approve, - pb, - ui_712_approve(NULL), - { - &C_icon_validate_14, - "Approve", - }); -UX_STEP_CB( - ux_712_step_reject, - pb, - ui_712_reject(NULL), - { - &C_icon_crossmark, - "Reject", - }); -// clang-format on - -UX_FLOW(ux_712_flow, - &ux_712_step_review, - &ux_712_step_dynamic, - &ux_712_step_dummy, - &ux_712_step_approve, - &ux_712_step_reject); - -#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_bagl/ui_flow_712.h b/src_bagl/ui_flow_712.h deleted file mode 100644 index 25016de..0000000 --- a/src_bagl/ui_flow_712.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef UI_FLOW_712_H_ -#define UI_FLOW_712_H_ - -#ifdef HAVE_EIP712_FULL_SUPPORT - -#include "ux_flow_engine.h" - -extern const ux_flow_step_t* const ux_712_flow[]; - -#endif // HAVE_EIP712_FULL_SUPPORT - -#endif // UI_FLOW_712_H_ diff --git a/src_bagl/ui_flow_signMessage712.c b/src_bagl/ui_flow_signMessage712.c index 230852e..8e0ee75 100644 --- a/src_bagl/ui_flow_signMessage712.c +++ b/src_bagl/ui_flow_signMessage712.c @@ -1,72 +1,87 @@ -#include "shared_context.h" -#include "ui_callbacks.h" -#include "common_712.h" -#include "ethUtils.h" +#ifdef HAVE_EIP712_FULL_SUPPORT -void prepare_domain_hash_v0() { - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - KECCAK256_HASH_BYTESIZE, - tmpCtx.messageSigningContext712.domainHash); -} +#include "ui_logic.h" +#include "shared_context.h" // strings -void prepare_message_hash_v0() { - snprintf(strings.tmp.tmp, - sizeof(strings.tmp.tmp), - "0x%.*H", - KECCAK256_HASH_BYTESIZE, - tmpCtx.messageSigningContext712.messageHash); +enum { UI_712_POS_REVIEW, UI_712_POS_END }; +static uint8_t ui_pos; + +static void dummy_cb(void) { + if (!ui_712_next_field()) { + if (ui_pos == UI_712_POS_REVIEW) { + ux_flow_next(); + ui_pos = UI_712_POS_END; + } else // UI_712_POS_END + { + ux_flow_prev(); + ui_pos = UI_712_POS_REVIEW; + } + } } // clang-format off UX_STEP_NOCB( - ux_sign_712_v0_flow_1_step, + ux_712_step_review, pnn, { - &C_icon_certificate, - "Sign", + &C_icon_eye, + "Review", "typed message", }); -UX_STEP_NOCB_INIT( - ux_sign_712_v0_flow_2_step, - bnnn_paging, - prepare_domain_hash_v0(), - { - .title = "Domain hash", +UX_STEP_NOCB( + ux_712_step_dynamic, + bnnn_paging, + { + .title = strings.tmp.tmp2, .text = strings.tmp.tmp, - }); -UX_STEP_NOCB_INIT( - ux_sign_712_v0_flow_3_step, - bnnn_paging, - prepare_message_hash_v0(), - { - .title = "Message hash", - .text = strings.tmp.tmp, - }); + } +); +UX_STEP_INIT( + ux_712_step_dummy, + NULL, + NULL, + { + dummy_cb(); + } +); UX_STEP_CB( - ux_sign_712_v0_flow_4_step, - pbb, - ui_712_approve_cb(NULL), + ux_712_step_approve, + pb, + ui_712_approve(NULL), { &C_icon_validate_14, - "Sign", - "message", + "Approve", }); UX_STEP_CB( - ux_sign_712_v0_flow_5_step, - pbb, - ui_712_reject_cb(NULL), + ux_712_step_reject, + pb, + ui_712_reject(NULL), { &C_icon_crossmark, - "Cancel", - "signature", + "Reject", }); // clang-format on -UX_FLOW(ux_sign_712_v0_flow, - &ux_sign_712_v0_flow_1_step, - &ux_sign_712_v0_flow_2_step, - &ux_sign_712_v0_flow_3_step, - &ux_sign_712_v0_flow_4_step, - &ux_sign_712_v0_flow_5_step); +UX_FLOW(ux_712_flow, + &ux_712_step_review, + &ux_712_step_dynamic, + &ux_712_step_dummy, + &ux_712_step_approve, + &ux_712_step_reject); + +void ui_712_start(void) { + ux_flow_init(0, ux_712_flow, NULL); + ui_pos = UI_712_POS_REVIEW; +} + +void ui_712_switch_to_message(void) { + ux_flow_init(0, ux_712_flow, &ux_712_step_dynamic); + ui_pos = UI_712_POS_REVIEW; +} + +void ui_712_switch_to_sign(void) { + ux_flow_init(0, ux_712_flow, &ux_712_step_approve); + ui_pos = UI_712_POS_END; +} + +#endif // HAVE_EIP712_FULL_SUPPORT diff --git a/src_bagl/ui_flow_signMessage712_v0.c b/src_bagl/ui_flow_signMessage712_v0.c new file mode 100644 index 0000000..230852e --- /dev/null +++ b/src_bagl/ui_flow_signMessage712_v0.c @@ -0,0 +1,72 @@ +#include "shared_context.h" +#include "ui_callbacks.h" +#include "common_712.h" +#include "ethUtils.h" + +void prepare_domain_hash_v0() { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + KECCAK256_HASH_BYTESIZE, + tmpCtx.messageSigningContext712.domainHash); +} + +void prepare_message_hash_v0() { + snprintf(strings.tmp.tmp, + sizeof(strings.tmp.tmp), + "0x%.*H", + KECCAK256_HASH_BYTESIZE, + tmpCtx.messageSigningContext712.messageHash); +} + +// clang-format off +UX_STEP_NOCB( + ux_sign_712_v0_flow_1_step, + pnn, + { + &C_icon_certificate, + "Sign", + "typed message", + }); +UX_STEP_NOCB_INIT( + ux_sign_712_v0_flow_2_step, + bnnn_paging, + prepare_domain_hash_v0(), + { + .title = "Domain hash", + .text = strings.tmp.tmp, + }); +UX_STEP_NOCB_INIT( + ux_sign_712_v0_flow_3_step, + bnnn_paging, + prepare_message_hash_v0(), + { + .title = "Message hash", + .text = strings.tmp.tmp, + }); +UX_STEP_CB( + ux_sign_712_v0_flow_4_step, + pbb, + ui_712_approve_cb(NULL), + { + &C_icon_validate_14, + "Sign", + "message", + }); +UX_STEP_CB( + ux_sign_712_v0_flow_5_step, + pbb, + ui_712_reject_cb(NULL), + { + &C_icon_crossmark, + "Cancel", + "signature", + }); +// clang-format on + +UX_FLOW(ux_sign_712_v0_flow, + &ux_sign_712_v0_flow_1_step, + &ux_sign_712_v0_flow_2_step, + &ux_sign_712_v0_flow_3_step, + &ux_sign_712_v0_flow_4_step, + &ux_sign_712_v0_flow_5_step); diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 1589b19..6243134 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -6,8 +6,6 @@ #include "mem.h" #include "mem_utils.h" #include "os_io.h" -#include "ux_flow_engine.h" -#include "ui_flow_712.h" #include "shared_context.h" #include "ethUtils.h" // getEthDisplayableAddress #include "utils.h" // uint256_to_decimal @@ -18,6 +16,7 @@ #include "apdu_constants.h" // APDU response codes #include "typed_data.h" #include "commands_712.h" +#include "common_ui.h" static t_ui_context *ui_ctx = NULL; @@ -106,43 +105,34 @@ void ui_712_set_value(const char *const str, uint8_t length) { void ui_712_redraw_generic_step(void) { if (!ui_ctx->shown) // Initialize if it is not already { - ux_flow_init(0, ux_712_flow, NULL); + ui_712_start(); ui_ctx->shown = true; } else { - // not pretty, manually changes the internal state of the UX flow - // so that we always land on the first screen of a paging step without any visible - // screen glitching (quick screen switching) - G_ux.flow_stack[G_ux.stack_count - 1].index = 0; - // since the flow now thinks we are displaying the first step, do next - ux_flow_next(); + ui_712_switch_to_message(); } } /** - * Called on the intermediate dummy screen between the dynamic step - * && the approve/reject screen + * Called to fetch the next field if they have not all been processed yet + * + * @return whether there will be a next field */ -void ui_712_next_field(void) { +bool ui_712_next_field(void) { + bool next = false; + if (ui_ctx == NULL) { apdu_response_code = APDU_RESPONSE_CONDITION_NOT_SATISFIED; - return; - } - if (ui_ctx->structs_to_review > 0) { - ui_712_review_struct(path_get_nth_field_to_last(ui_ctx->structs_to_review)); - ui_ctx->structs_to_review -= 1; } else { + if (ui_ctx->structs_to_review > 0) { + ui_712_review_struct(path_get_nth_field_to_last(ui_ctx->structs_to_review)); + ui_ctx->structs_to_review -= 1; + } if (!ui_ctx->end_reached) { handle_eip712_return_code(true); - } else { - if (ui_ctx->pos == UI_712_POS_REVIEW) { - ux_flow_next(); - ui_ctx->pos = UI_712_POS_END; - } else { - ux_flow_prev(); - ui_ctx->pos = UI_712_POS_REVIEW; - } + next = true; } } + return next; } /** @@ -419,7 +409,7 @@ void ui_712_end_sign(void) { ui_ctx->end_reached = true; if (N_storage.verbose_eip712 || (ui_ctx->filtering_mode == EIP712_FILTERING_FULL)) { - ui_712_next_field(); + ui_712_switch_to_sign(); } } @@ -430,7 +420,6 @@ bool ui_712_init(void) { if ((ui_ctx = MEM_ALLOC_AND_ALIGN_TYPE(*ui_ctx))) { ui_ctx->shown = false; ui_ctx->end_reached = false; - ui_ctx->pos = UI_712_POS_REVIEW; ui_ctx->filtering_mode = EIP712_FILTERING_BASIC; } else { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; diff --git a/src_features/signMessageEIP712/ui_logic.h b/src_features/signMessageEIP712/ui_logic.h index 72224b0..7ebb4b6 100644 --- a/src_features/signMessageEIP712/ui_logic.h +++ b/src_features/signMessageEIP712/ui_logic.h @@ -11,12 +11,9 @@ typedef enum { EIP712_FILTERING_BASIC, EIP712_FILTERING_FULL } e_eip712_filtering_mode; -typedef enum { UI_712_POS_REVIEW, UI_712_POS_END } e_ui_position; - typedef struct { bool shown; bool end_reached; - e_ui_position pos; uint8_t filtering_mode; uint8_t filters_to_process; uint8_t field_flags; @@ -25,7 +22,7 @@ typedef struct { bool ui_712_init(void); void ui_712_deinit(void); -void ui_712_next_field(void); +bool 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); void ui_712_end_sign(void); From 4af091f3265ab1c131be371c46ec8accf9fadbf9 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Fri, 21 Oct 2022 18:41:40 +0200 Subject: [PATCH 180/184] Fix CI with newer ragger version --- tests/ragger/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ragger/requirements.txt b/tests/ragger/requirements.txt index a63552e..8836582 100644 --- a/tests/ragger/requirements.txt +++ b/tests/ragger/requirements.txt @@ -1,6 +1,6 @@ requests>=2.28,<3.0 click>=8.0,<9.0 # needed by the CI as it installs an older version and breaks dependencies protobuf==3.20.1 # To fix the protobuf dependency bug -ragger +ragger[speculos] pytest ecdsa From 174d35782f739d2da5beeec2006831fae79b4d30 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 26 Oct 2022 14:15:09 +0200 Subject: [PATCH 181/184] Review fixes --- src_features/signMessageEIP712/commands_712.c | 3 ++- src_features/signMessageEIP712/format_hash_field_type.c | 3 ++- src_features/signMessageEIP712/path.c | 2 +- src_features/signMessageEIP712/type_hash.c | 2 +- src_features/signMessageEIP712/ui_logic.c | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src_features/signMessageEIP712/commands_712.c b/src_features/signMessageEIP712/commands_712.c index 3c0cc9e..7810b93 100644 --- a/src_features/signMessageEIP712/commands_712.c +++ b/src_features/signMessageEIP712/commands_712.c @@ -94,7 +94,8 @@ bool handle_eip712_struct_impl(const uint8_t *const apdu_buf) { switch (apdu_buf[OFFSET_P2]) { case P2_IMPL_NAME: // set root type - if ((ret = path_set_root((char *) &apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]))) { + ret = path_set_root((char *) &apdu_buf[OFFSET_CDATA], apdu_buf[OFFSET_LC]); + if (ret) { if (N_storage.verbose_eip712) { ui_712_review_struct(path_get_root()); reply_apdu = false; diff --git a/src_features/signMessageEIP712/format_hash_field_type.c b/src_features/signMessageEIP712/format_hash_field_type.c index 761f5f3..1760c7d 100644 --- a/src_features/signMessageEIP712/format_hash_field_type.c +++ b/src_features/signMessageEIP712/format_hash_field_type.c @@ -33,7 +33,8 @@ static bool format_hash_field_type_size(const void *const field_ptr, cx_hash_t * apdu_response_code = APDU_RESPONSE_INVALID_DATA; return false; } - if ((uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len)) == NULL) { + uint_str_ptr = mem_alloc_and_format_uint(field_size, &uint_str_len); + if (uint_str_ptr == NULL) { apdu_response_code = APDU_RESPONSE_INSUFFICIENT_MEMORY; return false; } diff --git a/src_features/signMessageEIP712/path.c b/src_features/signMessageEIP712/path.c index 53eddf3..ec95242 100644 --- a/src_features/signMessageEIP712/path.c +++ b/src_features/signMessageEIP712/path.c @@ -136,7 +136,7 @@ static bool path_depth_list_push(void) { * @return pointer to the hashing context */ static cx_sha3_t *get_last_hash_ctx(void) { - return (cx_sha3_t *) mem_alloc(0) - 1; + return ((cx_sha3_t *) mem_alloc(0)) - 1; } /** diff --git a/src_features/signMessageEIP712/type_hash.c b/src_features/signMessageEIP712/type_hash.c index c70383d..1e8da4b 100644 --- a/src_features/signMessageEIP712/type_hash.c +++ b/src_features/signMessageEIP712/type_hash.c @@ -111,7 +111,7 @@ static void sort_dependencies(uint8_t deps_count, const void **deps) { * @param[out] deps_count count of how many struct dependencie pointers * @param[in] first_dep pointer to the first dependency pointer * @param[in] struct_ptr pointer to the struct we are getting the dependencies of - * @return \ref false in case of a memory allocation error, \ref true otherwise + * @return pointer to the first found dependency, \ref NULL otherwise */ static const void **get_struct_dependencies(uint8_t *const deps_count, const void **first_dep, diff --git a/src_features/signMessageEIP712/ui_logic.c b/src_features/signMessageEIP712/ui_logic.c index 6243134..698e5e1 100644 --- a/src_features/signMessageEIP712/ui_logic.c +++ b/src_features/signMessageEIP712/ui_logic.c @@ -64,7 +64,7 @@ static void ui_712_set_buf(const char *const src, } memcpy(dst, src, cpy_length); dst[cpy_length] = '\0'; - if (explicit_trunc && (src_length > dst_length)) { + if (explicit_trunc && (cpy_length < src_length)) { memcpy(dst + cpy_length - 3, "...", 3); } } From 314eba1a643ecf75dfe01d7b3ce0eaa9bed8a1e0 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 26 Oct 2022 15:47:53 +0200 Subject: [PATCH 182/184] CI fix Needed by the update of the app-builder container --- tests/zemu/build_local_test_elfs.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/zemu/build_local_test_elfs.sh b/tests/zemu/build_local_test_elfs.sh index b040891..c6b0d0e 100755 --- a/tests/zemu/build_local_test_elfs.sh +++ b/tests/zemu/build_local_test_elfs.sh @@ -2,7 +2,7 @@ set -e -TESTS_FULL_PATH=$(dirname "$(realpath "$0")") +TESTS_PATH=$(dirname "$(realpath "$0")") # FILL THESE WITH YOUR OWN SDKs PATHS # NANOS_SDK= @@ -17,11 +17,10 @@ NANO_SDKS=("$NANOS_SDK" "$NANOX_SDK") FILE_SUFFIXES=("nanos" "nanox") # move to the tests directory -cd "$TESTS_FULL_PATH" || exit 1 +cd "$TESTS_PATH" || exit 1 # Do it only now since before the cd command, we might not have been inside the repository GIT_REPO_ROOT=$(git rev-parse --show-toplevel) -TESTS_REL_PATH=$(realpath --relative-to="$GIT_REPO_ROOT" "$TESTS_FULL_PATH") # create elfs directory if it doesn't exist mkdir -p elfs @@ -39,7 +38,7 @@ do echo "** Building app $appname..." make clean BOLOS_SDK="$nano_sdk" make -j DEBUG=1 NFT_TESTING_KEY=1 BOLOS_SDK="$nano_sdk" CHAIN="$appname" - cp bin/app.elf "$TESTS_REL_PATH/elfs/${appname}_${elf_suffix}.elf" + cp bin/app.elf "$TESTS_PATH/elfs/${appname}_${elf_suffix}.elf" done done From aabf44e0d92234a88a34f3cfdc85954ca3733a13 Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 26 Oct 2022 18:18:54 +0200 Subject: [PATCH 183/184] Switch to using the lite container --- .github/workflows/build-workflow.yml | 2 +- .github/workflows/ci-workflow.yml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-workflow.yml b/.github/workflows/build-workflow.yml index 54bd896..46f714b 100644 --- a/.github/workflows/build-workflow.yml +++ b/.github/workflows/build-workflow.yml @@ -18,7 +18,7 @@ jobs: sdk: ["$NANOS_SDK", "$NANOX_SDK", "$NANOSP_SDK"] runs-on: ubuntu-latest container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest steps: - name: Clone diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 8a54522..428de38 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -15,7 +15,7 @@ jobs: name: Clang Static Analyzer runs-on: ubuntu-latest container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest steps: - uses: actions/checkout@v2 @@ -39,7 +39,7 @@ jobs: name: Building binaries for E2E Zemu tests runs-on: ubuntu-latest container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest steps: - uses: actions/checkout@v2 @@ -106,7 +106,7 @@ jobs: name: Building binaries for E2E Speculos tests runs-on: ubuntu-latest container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest steps: - uses: actions/checkout@v2 @@ -169,7 +169,7 @@ jobs: name: Building binaries for Ragger tests runs-on: ubuntu-latest container: - image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder:latest + image: ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest steps: - name: Clone From 1e800478babe1e7964631c29092e9d732a4f7b6b Mon Sep 17 00:00:00 2001 From: Alexandre Paillier Date: Wed, 26 Oct 2022 18:31:58 +0200 Subject: [PATCH 184/184] Changelog update + removed -dev version suffix --- CHANGELOG.md | 4 ++-- Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fda0f81..c678402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [1.10.0](https://github.com/ledgerhq/app-ethereum/compare/1.9.20...1.10.0) - 2022-XX-XX +## [1.10.0](https://github.com/ledgerhq/app-ethereum/compare/1.9.20...1.10.0) - 2022-10-26 ### Changed -- EIP-712 signatures are now computed on-device and display their content (clear-signing) +- EIP-712 signatures are now computed on-device and display their content (clear-signing) (LNX & LNS+) ## [1.9.20](https://github.com/ledgerhq/app-ethereum/compare/1.9.19...1.9.20) - 2022-10-10 diff --git a/Makefile b/Makefile index f41e67a..40b9020 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ APP_LOAD_PARAMS += --path "1517992542'/1101353413'" APPVERSION_M=1 APPVERSION_N=10 APPVERSION_P=0 -APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)-dev +APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P) APP_LOAD_FLAGS= --appFlags 0xa40 --dep Ethereum:$(APPVERSION) ###########################