diff --git a/src/utils.c b/src/utils.c index 2c553b4..3926bf2 100644 --- a/src/utils.c +++ b/src/utils.c @@ -116,7 +116,7 @@ bool uint256_to_decimal(const uint8_t *value, size_t value_len, char *out, size_ return true; } -void amountToString(const uint8_t *amount, +bool amountToString(const uint8_t *amount, uint8_t amount_size, uint8_t decimals, const char *ticker, @@ -125,7 +125,7 @@ void amountToString(const uint8_t *amount, char tmp_buffer[100] = {0}; if (uint256_to_decimal(amount, amount_size, tmp_buffer, sizeof(tmp_buffer)) == false) { - THROW(EXCEPTION_OVERFLOW); + return false; } uint8_t amount_len = strnlen(tmp_buffer, sizeof(tmp_buffer)); @@ -141,10 +141,11 @@ void amountToString(const uint8_t *amount, out_buffer + ticker_len, out_buffer_size - ticker_len - 1, decimals) == false) { - THROW(EXCEPTION_OVERFLOW); + return false; } out_buffer[out_buffer_size - 1] = '\0'; + return true; } bool parse_swap_config(const uint8_t *config, uint8_t config_len, char *ticker, uint8_t *decimals) { diff --git a/src/utils.h b/src/utils.h index 504c1b1..c276b6d 100644 --- a/src/utils.h +++ b/src/utils.h @@ -34,7 +34,7 @@ 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); -void amountToString(const uint8_t* amount, +bool amountToString(const uint8_t* amount, uint8_t amount_len, uint8_t decimals, const char* ticker, diff --git a/src_common/ethUtils.c b/src_common/ethUtils.c index e66e0d2..0eb8ca3 100644 --- a/src_common/ethUtils.c +++ b/src_common/ethUtils.c @@ -117,30 +117,59 @@ bool rlpDecodeLength(uint8_t *buffer, uint32_t *fieldLength, uint32_t *offset, b return true; } -void getEthAddressFromKey(cx_ecfp_public_key_t *publicKey, uint8_t *out, cx_sha3_t *sha3Context) { +bool getEthAddressFromKey(cx_ecfp_public_key_t *publicKey, uint8_t *out, cx_sha3_t *sha3Context) { uint8_t hashAddress[INT256_LENGTH]; - cx_keccak_init(sha3Context, 256); - cx_hash((cx_hash_t *) sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32); + + if (cx_keccak_init_no_throw(sha3Context, 256) != CX_OK) { + return false; + } + + if (cx_hash_no_throw((cx_hash_t *) sha3Context, + CX_LAST, + publicKey->W + 1, + 64, + hashAddress, + 32) != CX_OK) { + return false; + } + memmove(out, hashAddress + 12, 20); + return true; } -void getEthAddressStringFromKey(cx_ecfp_public_key_t *publicKey, +bool getEthAddressStringFromKey(cx_ecfp_public_key_t *publicKey, char *out, cx_sha3_t *sha3Context, uint64_t chainId) { uint8_t hashAddress[INT256_LENGTH]; - cx_keccak_init(sha3Context, 256); - cx_hash((cx_hash_t *) sha3Context, CX_LAST, publicKey->W + 1, 64, hashAddress, 32); - getEthAddressStringFromBinary(hashAddress + 12, out, sha3Context, chainId); + + if (cx_keccak_init_no_throw(sha3Context, 256) != CX_OK) { + return false; + } + + if (cx_hash_no_throw((cx_hash_t *) sha3Context, + CX_LAST, + publicKey->W + 1, + 64, + hashAddress, + 32) != CX_OK) { + return false; + } + + if (!getEthAddressStringFromBinary(hashAddress + 12, out, sha3Context, chainId)) { + return false; + } + + return true; } -void u64_to_string(uint64_t src, char *dst, uint8_t dst_size) { +bool u64_to_string(uint64_t src, char *dst, uint8_t dst_size) { // Copy the numbers in ASCII format. uint8_t i = 0; do { // Checking `i + 1` to make sure we have enough space for '\0'. if (i + 1 >= dst_size) { - THROW(0x6502); + return false; } dst[i] = src % 10 + '0'; src /= 10; @@ -160,9 +189,10 @@ void u64_to_string(uint64_t src, char *dst, uint8_t dst_size) { i--; j++; } + return true; } -void getEthAddressStringFromBinary(uint8_t *address, +bool getEthAddressStringFromBinary(uint8_t *address, char *out, cx_sha3_t *sha3Context, uint64_t chainId) { @@ -182,7 +212,9 @@ void getEthAddressStringFromBinary(uint8_t *address, break; } if (eip1191) { - u64_to_string(chainId, (char *) locals_union.tmp, sizeof(locals_union.tmp)); + if (!u64_to_string(chainId, (char *) locals_union.tmp, sizeof(locals_union.tmp))) { + return false; + } offset = strnlen((char *) locals_union.tmp, sizeof(locals_union.tmp)); strlcat((char *) locals_union.tmp + offset, "0x", sizeof(locals_union.tmp) - offset); offset = strnlen((char *) locals_union.tmp, sizeof(locals_union.tmp)); @@ -192,13 +224,18 @@ void getEthAddressStringFromBinary(uint8_t *address, locals_union.tmp[offset + 2 * i] = HEXDIGITS[(digit >> 4) & 0x0f]; locals_union.tmp[offset + 2 * i + 1] = HEXDIGITS[digit & 0x0f]; } - cx_keccak_init(sha3Context, 256); - cx_hash((cx_hash_t *) sha3Context, - CX_LAST, - locals_union.tmp, - offset + 40, - locals_union.hashChecksum, - 32); + if (cx_keccak_init_no_throw(sha3Context, 256) != CX_OK) { + return false; + } + + if (cx_hash_no_throw((cx_hash_t *) sha3Context, + CX_LAST, + locals_union.tmp, + offset + 40, + locals_union.hashChecksum, + 32) != CX_OK) { + return false; + } for (i = 0; i < 40; i++) { uint8_t digit = address[i / 2]; if ((i % 2) == 0) { @@ -218,24 +255,31 @@ void getEthAddressStringFromBinary(uint8_t *address, } } out[40] = '\0'; + + return true; } /* Fills the `out` buffer with the lowercase string representation of the pubkey passed in as binary format by `in`. (eg: uint8_t*:0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB -> char*:"0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB\0" ) `sha3` context doesn't have have to be initialized prior to call.*/ -void getEthDisplayableAddress(uint8_t *in, +bool getEthDisplayableAddress(uint8_t *in, char *out, size_t out_len, cx_sha3_t *sha3, uint64_t chainId) { if (out_len < 43) { strlcpy(out, "ERROR", out_len); - return; + return false; } out[0] = '0'; out[1] = 'x'; - getEthAddressStringFromBinary(in, out + 2, sha3, chainId); + if (!getEthAddressStringFromBinary(in, out + 2, sha3, chainId)) { + strlcpy(out, "ERROR", out_len); + return false; + } + + return true; } bool adjustDecimals(const char *src, diff --git a/src_common/ethUtils.h b/src_common/ethUtils.h index 8702533..2942243 100644 --- a/src_common/ethUtils.h +++ b/src_common/ethUtils.h @@ -40,21 +40,21 @@ bool rlpDecodeLength(uint8_t *buffer, uint32_t *fieldLength, uint32_t *offset, b bool rlpCanDecode(uint8_t *buffer, uint32_t bufferLength, bool *valid); -void getEthAddressFromKey(cx_ecfp_public_key_t *publicKey, uint8_t *out, cx_sha3_t *sha3Context); +bool getEthAddressFromKey(cx_ecfp_public_key_t *publicKey, uint8_t *out, cx_sha3_t *sha3Context); -void getEthAddressStringFromKey(cx_ecfp_public_key_t *publicKey, +bool getEthAddressStringFromKey(cx_ecfp_public_key_t *publicKey, char *out, cx_sha3_t *sha3Context, uint64_t chainId); -void u64_to_string(uint64_t src, char *dst, uint8_t dst_size); +bool u64_to_string(uint64_t src, char *dst, uint8_t dst_size); -void getEthAddressStringFromBinary(uint8_t *address, +bool getEthAddressStringFromBinary(uint8_t *address, char *out, cx_sha3_t *sha3Context, uint64_t chainId); -void getEthDisplayableAddress(uint8_t *in, +bool getEthDisplayableAddress(uint8_t *in, char *out, size_t out_len, cx_sha3_t *sha3, diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index c8143a0..c84e9d1 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -463,12 +463,16 @@ void finalizeParsing(bool direct) { // Format the amount in a temporary buffer, if in swap case compare it with validated // amount, else commit it - amountToString(tmpContent.txContent.value.value, - tmpContent.txContent.value.length, - decimals, - ticker, - displayBuffer, - sizeof(displayBuffer)); + if (!amountToString(tmpContent.txContent.value.value, + tmpContent.txContent.value.length, + decimals, + ticker, + displayBuffer, + sizeof(displayBuffer))) { + PRINTF("OVERFLOW, amount to string failed\n"); + THROW(EXCEPTION_OVERFLOW); + } + if (G_called_from_swap) { // Ensure the values are the same that the ones that have been previously validated if (strcmp(strings.common.fullAmount, displayBuffer) != 0) { diff --git a/src_plugins_sdk/plugin_main.c b/src_plugins_sdk/plugin_main.c new file mode 100644 index 0000000..06bd3a4 --- /dev/null +++ b/src_plugins_sdk/plugin_main.c @@ -0,0 +1,68 @@ +/***************************************************************************** + * Ledger Plugins SDK. + * (c) 2023 Ledger SAS. + * + * 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. + *****************************************************************************/ + +// Calls the ethereum app. +void call_app_ethereum() { + unsigned int libcall_params[5]; + libcall_params[0] = (unsigned int) "Ethereum"; + libcall_params[1] = 0x100; + libcall_params[2] = RUN_APPLICATION; + libcall_params[3] = (unsigned int) NULL; +#ifdef HAVE_NBGL + caller_app_t capp; + const char name[] = APPNAME; + nbgl_icon_details_t icon_details; + uint8_t bitmap[sizeof(ICONBITMAP)]; + + memcpy(&icon_details, &ICONGLYPH, sizeof(ICONGLYPH)); + memcpy(&bitmap, &ICONBITMAP, sizeof(bitmap)); + icon_details.bitmap = (const uint8_t *) bitmap; + capp.name = (const char *) name; + capp.icon = &icon_details; + libcall_params[4] = (unsigned int) &capp; +#else + libcall_params[4] = (unsigned int) NULL; +#endif + os_lib_call((unsigned int *) &libcall_params); +} + +// Low-level main for plugins. +__attribute__((section(".boot"))) int main(int arg0) { + // Exit critical section + __asm volatile("cpsie i"); + + os_boot(); + + check_api_level(CX_COMPAT_APILEVEL); + + // Check if plugin is called from the dashboard. + if (!arg0) { + // Called from dashboard, launch Ethereum app + call_app_ethereum(); + return 0; + } else { + // Not called from dashboard: called from the ethereum app! + // launch plugin main + plugin_main(arg0); + } + + // Call `os_lib_end`, go back to the ethereum app. + os_lib_end(); + + // Will not get reached. + return 0; +} \ No newline at end of file diff --git a/src_plugins_sdk/plugin_main.h b/src_plugins_sdk/plugin_main.h new file mode 100644 index 0000000..5801aa9 --- /dev/null +++ b/src_plugins_sdk/plugin_main.h @@ -0,0 +1,19 @@ +/***************************************************************************** + * Ledger Plugins SDK. + * (c) 2023 Ledger SAS. + * + * 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. + *****************************************************************************/ + +// applicative main for plugins +void plugin_main(int arg0); \ No newline at end of file diff --git a/tools/build_sdk.py b/tools/build_sdk.py index 552bb25..6cc1e83 100755 --- a/tools/build_sdk.py +++ b/tools/build_sdk.py @@ -5,7 +5,7 @@ This script extract a few specific definitions from app-ethereum that are required to exchange information with ethereum external plugins. It should always be launched from app-ethereum: -python3 ethereum-plugin-sdk/build_sdk.py +python3 tools/build_sdk.py ''' @@ -88,6 +88,7 @@ def merge_headers(sources, nodes_to_extract): '#include "cx.h"', '#ifdef HAVE_NBGL', '#include "nbgl_types.h"', + '#include "glyphs.h"', '#endif' ] @@ -159,7 +160,8 @@ if __name__ == "__main__": "src/shared_context.h", "src/eth_plugin_internal.h", "src/nft.h", - "src/swap_lib_calls.h" + "src/swap_lib_calls.h", + "src_plugins_sdk/plugin_main.h" ] nodes_to_extract = { "#define": ["MAX_TICKER_LEN", "ADDRESS_LENGTH", "INT256_LENGTH", "WEI_TO_ETHER", "SELECTOR_SIZE", "PARAMETER_LENGTH", "RUN_APPLICATION", "COLLECTION_NAME_MAX_LEN"], @@ -168,7 +170,20 @@ if __name__ == "__main__": "typedef union": ["extraInfo_t"], "__attribute__((no_instrument_function)) inline": ["int allzeroes"], "const": ["HEXDIGITS"], - "fn": ["void getEthAddressStringFromBinary", "void getEthAddressFromKey", "void getEthDisplayableAddress", "bool adjustDecimals", "bool uint256_to_decimal", "void amountToString", "void u64_to_string", "void copy_address", "void copy_parameter", "bool U2BE_from_parameter", "bool U4BE_from_parameter"] + "fn": ["bool getEthAddressStringFromBinary", + "bool getEthAddressFromKey", + "bool getEthDisplayableAddress", + "bool adjustDecimals", + "bool uint256_to_decimal", + "bool amountToString", + "bool u64_to_string", + "void copy_address", + "void copy_parameter", + "bool U2BE_from_parameter", + "bool U4BE_from_parameter", + "void plugin_main", + "void call_app_ethereum", + "int main"] } merge_headers(headers_to_merge, nodes_to_extract) @@ -179,6 +194,7 @@ if __name__ == "__main__": c_files_to_merge = [ "src/utils.c", "src_common/ethUtils.c", - "src/eth_plugin_internal.c" + "src/eth_plugin_internal.c", + "src_plugins_sdk/plugin_main.c" ] merge_c_files(c_files_to_merge, nodes_to_extract["fn"])