From 05c7ffdabf4a33e27d17319d86299410bfe4815a Mon Sep 17 00:00:00 2001 From: ftheirs Date: Wed, 10 Jul 2024 16:12:38 -0300 Subject: [PATCH] add Swap --- app/Makefile | 21 +-- app/src/apdu_handler.c | 6 +- app/src/common/main.c | 23 ++- app/src/common/tx.c | 8 +- app/src/crypto.c | 11 +- app/src/swap/handle_check_address.c | 58 ++++++++ app/src/swap/handle_get_printable_amount.c | 46 ++++++ app/src/swap/handle_sign_transaction.c | 155 +++++++++++++++++++++ app/src/swap/swap.h | 42 ++++++ app/src/swap/swap_utils.c | 70 ++++++++++ app/src/swap/swap_utils.h | 25 ++++ 11 files changed, 442 insertions(+), 23 deletions(-) create mode 100644 app/src/swap/handle_check_address.c create mode 100644 app/src/swap/handle_get_printable_amount.c create mode 100644 app/src/swap/handle_sign_transaction.c create mode 100644 app/src/swap/swap.h create mode 100644 app/src/swap/swap_utils.c create mode 100644 app/src/swap/swap_utils.h diff --git a/app/Makefile b/app/Makefile index 3845ae9c..080ef398 100755 --- a/app/Makefile +++ b/app/Makefile @@ -24,8 +24,18 @@ MY_DIR := $(dir $(lastword $(MAKEFILE_LIST))) include $(BOLOS_SDK)/Makefile.defines +# Coin definition +ifndef COIN +COIN=DOT +endif + +include $(CURDIR)/Makefile.version +$(info COIN = [$(COIN)]) + # # Set the default value for HAVE_SWAP to undefined if not already defined -# HAVE_SWAP ?= +ifeq ($(COIN),DOT) +HAVE_SWAP ?= 1 +endif # Set the default value for PRODUCTION_BUILD to 0 if not already defined PRODUCTION_BUILD ?= 1 @@ -41,7 +51,7 @@ else endif # Display whether swap functionality is enabled or not -ifdef HAVE_SWAP +ifeq ($(HAVE_SWAP), 1) $(info ************ HAVE_SWAP = [ENABLED]) DEFINES += HAVE_SWAP=$(HAVE_SWAP) else @@ -52,13 +62,6 @@ endif DEFINES += PRODUCTION_BUILD=$(PRODUCTION_BUILD) include $(CURDIR)/../deps/ledger-zxlib/makefiles/Makefile.app_testing -ifndef COIN -COIN=DOT -endif - -include $(CURDIR)/Makefile.version -$(info COIN = [$(COIN)]) - APPPATH = "44'/354'" ifeq ($(COIN),DOT) diff --git a/app/src/apdu_handler.c b/app/src/apdu_handler.c index 1684b72c..a3804a2c 100644 --- a/app/src/apdu_handler.c +++ b/app/src/apdu_handler.c @@ -30,6 +30,10 @@ #include "view.h" #include "zxmacros.h" +#ifdef HAVE_SWAP +#include "swap.h" +#endif + static bool tx_initialized = false; uint16_t blobLen = 0; @@ -260,7 +264,6 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { *tx += 2; } FINALLY { -#if 0 #ifdef HAVE_SWAP if (G_swap_state.called_from_swap && G_swap_state.should_exit) { // Swap checking failed, send reply now and exit, don't wait next cycle @@ -270,7 +273,6 @@ void handleApdu(volatile uint32_t *flags, volatile uint32_t *tx, uint32_t rx) { // Go back to exchange and report our status finalize_exchange_sign_transaction(sw == 0); } -#endif #endif } } diff --git a/app/src/common/main.c b/app/src/common/main.c index ddf2b6eb..c1c56193 100644 --- a/app/src/common/main.c +++ b/app/src/common/main.c @@ -24,12 +24,20 @@ #include "nbgl_use_case.h" #endif -#if 0 +#ifdef HAVE_SWAP +#include "lib_standard_app/swap_lib_calls.h" +#include "swap.h" +#endif + +#ifdef HAVE_SWAP // Helper to quit the application in a limited THROW context static void app_exit(void) { BEGIN_TRY_L(exit) { - TRY_L(exit) { os_sched_exit(-1); } - FINALLY_L(exit) {} + TRY_L(exit) { + os_sched_exit(-1); + } + FINALLY_L(exit) { + } } END_TRY_L(exit); } @@ -69,8 +77,11 @@ static void library_main(libargs_t *args) { break; } } - CATCH_OTHER(e) {} - FINALLY { os_lib_end(); } + CATCH_OTHER(e) { + } + FINALLY { + os_lib_end(); + } } END_TRY; } @@ -83,7 +94,7 @@ __attribute__((section(".boot"))) int main(int arg0) { os_boot(); if (arg0 != 0) { -#if 0 +#if HAVE_SWAP // The app has been started in library mode libargs_t *args = (libargs_t *)arg0; if (args->id != 0x100) { diff --git a/app/src/common/tx.c b/app/src/common/tx.c index 17ab35f2..13545047 100644 --- a/app/src/common/tx.c +++ b/app/src/common/tx.c @@ -26,6 +26,10 @@ #include "zxformat.h" #include "zxmacros.h" +#ifdef HAVE_SWAP +#include "swap.h" +#endif + extern uint16_t blobLen; #if defined(TARGET_NANOX) || defined(TARGET_NANOS2) || defined(TARGET_STAX) @@ -111,7 +115,7 @@ const char *tx_parse() { return parser_getErrorDescription(err); } -#if 0 +#ifdef HAVE_SWAP // If in swap mode, compare swap tx parameters with stored info. if (G_swap_state.called_from_swap) { if (G_swap_state.should_exit == 1) { @@ -123,7 +127,7 @@ const char *tx_parse() { // We will quit the app after this transaction, whether it succeeds or fails G_swap_state.should_exit = 1; } - err = check_swap_conditions(&ctx_parsed_tx); + err = check_swap_conditions(&tx_obj); CHECK_APP_CANARY() if (err != parser_ok) { return parser_getErrorDescription(err); diff --git a/app/src/crypto.c b/app/src/crypto.c index ad7b20eb..2f94d945 100644 --- a/app/src/crypto.c +++ b/app/src/crypto.c @@ -20,6 +20,7 @@ #include "coin.h" #include "crypto_helper.h" #include "cx.h" +#include "swap_utils.h" #include "zxmacros.h" #define PREFIX_SIGNATURE_TYPE_ED25519 0 @@ -129,10 +130,13 @@ zxerr_t crypto_fillAddress(uint8_t *buffer, uint16_t bufferLen, uint16_t *addrRe return crypto_fillAddress_helper(buffer, bufferLen, addrResponseLen, ss58prefix, hdPath); } -#if 0 // fill a crypto address using a locally computed hdpath -zxerr_t crypto_fillAddress_standalone(uint8_t *params, uint8_t paramsSize, uint8_t *buffer, uint16_t bufferLen, - uint16_t *addrResponseLen, const uint16_t ss58prefix) { +zxerr_t crypto_fillAddress_standalone(uint8_t *params, + uint8_t paramsSize, + uint8_t *buffer, + uint16_t bufferLen, + uint16_t *addrResponseLen, + const uint16_t ss58prefix) { uint32_t local_hdPath[HDPATH_LEN_DEFAULT]; if (paramsSize != (sizeof(uint32_t) * HDPATH_LEN_DEFAULT)) { @@ -145,4 +149,3 @@ zxerr_t crypto_fillAddress_standalone(uint8_t *params, uint8_t paramsSize, uint8 return crypto_fillAddress_helper(buffer, bufferLen, addrResponseLen, ss58prefix, local_hdPath); } -#endif diff --git a/app/src/swap/handle_check_address.c b/app/src/swap/handle_check_address.c new file mode 100644 index 00000000..a2bd84fe --- /dev/null +++ b/app/src/swap/handle_check_address.c @@ -0,0 +1,58 @@ +/******************************************************************************* + * (c) 2016 Ledger + * (c) 2018 - 2023 Zondax AG + * + * 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. + ********************************************************************************/ +#include "crypto.h" +#include "lib_standard_app/swap_lib_calls.h" +#include "swap.h" +#include "zxformat.h" + +#define DOT_SS58_PREFIX 0 +#define ED25519_ADD_KIND 0 + +void handle_check_address(check_address_parameters_t *params) { + if (params == NULL || params->address_to_check == NULL || params->address_parameters_length < 2) { + return; + } + params->result = 0; + uint8_t buffer[100] = {0}; + uint16_t replyLen = 0; + + // address parameters have the following structure + // address kind (1 byte) | path length (1 byte) | bip44 path (4 * pathLength bytes) + // address kind won't be used anymore since Generic Polkadot app only works for ED25519 + const uint8_t add_kind = *params->address_parameters; + if (add_kind != ED25519_ADD_KIND) { + return; + } + + zxerr_t err = crypto_fillAddress_standalone((uint8_t *)params->address_parameters + 2, + (uint8_t)params->address_parameters_length - 2, buffer, sizeof(buffer), + &replyLen, DOT_SS58_PREFIX); + if (err != zxerr_ok || replyLen <= PK_LEN_25519) { + MEMZERO(buffer, sizeof(buffer)); + return; + } + + const uint8_t *address = buffer + PK_LEN_25519; + const uint8_t addressLen = replyLen - PK_LEN_25519; + + // Exchange guarantees that the input string is '\0' terminated + uint8_t address_to_check_len = strlen(params->address_to_check); + + if (addressLen == address_to_check_len && memcmp(address, params->address_to_check, addressLen) == 0) { + params->result = 1; + } +} diff --git a/app/src/swap/handle_get_printable_amount.c b/app/src/swap/handle_get_printable_amount.c new file mode 100644 index 00000000..ad19720c --- /dev/null +++ b/app/src/swap/handle_get_printable_amount.c @@ -0,0 +1,46 @@ +/******************************************************************************* + * (c) 2016 Ledger + * (c) 2018 - 2023 Zondax AG + * + * 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. + ********************************************************************************/ +#include "bignum.h" +#include "crypto.h" +#include "lib_standard_app/swap_lib_calls.h" +#include "swap.h" +#include "swap_utils.h" +#include "zxformat.h" + +void handle_get_printable_amount(get_printable_amount_parameters_t *params) { + if (params == NULL) { + return; + } + uint8_t amount[16]; + + MEMZERO(amount, sizeof(amount)); + MEMZERO(params->printable_amount, sizeof(params->printable_amount)); + + if (params->amount_length > 16) { + return; + } + + memcpy(amount + 16 - params->amount_length, params->amount, params->amount_length); + + char tmp_amount[100] = {0}; + const zxerr_t zxerr = bytesAmountToStringBalance(amount, sizeof(amount), tmp_amount, sizeof(tmp_amount)); + + if (zxerr != zxerr_ok || strnlen(tmp_amount, sizeof(tmp_amount)) > sizeof(params->printable_amount)) { + return; + } + strncpy(params->printable_amount, tmp_amount, sizeof(params->printable_amount) - 1); +} diff --git a/app/src/swap/handle_sign_transaction.c b/app/src/swap/handle_sign_transaction.c new file mode 100644 index 00000000..ad64b773 --- /dev/null +++ b/app/src/swap/handle_sign_transaction.c @@ -0,0 +1,155 @@ +/******************************************************************************* + * (c) 2016 Ledger + * (c) 2018 - 2023 Zondax AG + * + * 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. + ********************************************************************************/ +#include "bignum.h" +#include "crypto.h" +#include "lib_standard_app/swap_lib_calls.h" +#include "parser.h" +#include "swap.h" +#include "swap_utils.h" +#include "zxformat.h" + +#define SWAP_EXPECTED_ITEMS 3 + +swap_globals_t G_swap_state; + +// Save the BSS address where we will write the return value when finished +static uint8_t *G_swap_sign_return_value_address; + +//////////////////////////////////////////////////////////////// + +bool copy_transaction_parameters(create_transaction_parameters_t *sign_transaction_params) { + if (sign_transaction_params == NULL) { + return false; + } + + // First copy parameters to stack, and then to global data. + // We need this "trick" as the input data position can overlap with globals + char destination_address[65] = {0}; + uint8_t amount[16] = {0}; + uint8_t amount_length = {0}; + uint8_t fees[8] = {0}; + + strncpy(destination_address, sign_transaction_params->destination_address, sizeof(destination_address) - 1); + + if ((destination_address[sizeof(destination_address) - 1] != '\0') || (sign_transaction_params->amount_length > 16) || + (sign_transaction_params->fee_amount_length > 8)) { + return false; + } + + // store amount as big endian in 16 bytes, so the passed data should be alligned to right + // input {0xEE, 0x00, 0xFF} should be stored like {0x00, 0x00, 0x00, 0x00, 0x00, 0xEE, 0x00, 0xFF} + memcpy(amount + 16 - sign_transaction_params->amount_length, sign_transaction_params->amount, + sign_transaction_params->amount_length); + + memcpy(fees + 8 - sign_transaction_params->fee_amount_length, sign_transaction_params->fee_amount, + sign_transaction_params->fee_amount_length); + + amount_length = sign_transaction_params->amount_length; + + // Full reset the global variables + os_explicit_zero_BSS_segment(); + + // Keep the address at which we'll reply the signing status + G_swap_sign_return_value_address = &sign_transaction_params->result; + + // Commit the values read from exchange to the clean global space + G_swap_state.amount_length = amount_length; + memcpy(G_swap_state.amount, amount, sizeof(amount)); + memcpy(G_swap_state.destination_address, destination_address, sizeof(G_swap_state.destination_address)); + readU64BE(fees, &G_swap_state.fees); + + return true; +} + +parser_error_t check_swap_conditions(parser_tx_t *txObj) { + parser_error_t err = parser_unexpected_error; + if (txObj == NULL) { + return err; + } + // Check transaction method arguments number. Balance transfer Should be 3 (for tx v26). + // [dest(address type) | dest(address) | value(amount)] + if (txObj->blob.totalMethodItems != SWAP_EXPECTED_ITEMS) { + zemu_log("Wrong swap tx method arguments count.\n"); + return parser_swap_tx_wrong_method_args_num; + } + + // Check method. + // const char *valid_tx_method = "Balances transfer_allow_death"; + const char *valid_tx_pallet = "Balances"; + const char *valid_tx_call = "transfer_allow_death"; + char tmpKey[20] = {0}; + char tmpValue[65] = {0}; + + uint8_t pageCount = 0; + ui_field_t uiFields = {.displayIdx = 1, + .outKey = tmpKey, + .outKeyLen = sizeof(tmpKey), + .outVal = tmpValue, + .outValLen = sizeof(tmpValue), + .pageIdx = 0, + .pageCount = &pageCount}; + + CHECK_ERROR(parser_getItem(txObj, &uiFields)) + if (strncmp(valid_tx_pallet, tmpKey, strlen(valid_tx_pallet)) != 0 || + strncmp(valid_tx_call, tmpValue, strlen(valid_tx_call)) != 0) { + ZEMU_LOGF(200, "Wrong swap tx method (%s %s, should be : %s %s).\n", tmpKey, tmpValue, valid_tx_pallet, + valid_tx_call); + return parser_swap_tx_wrong_method; + } + + // // Check destination address. + uiFields.displayIdx = 3; + MEMZERO(tmpKey, sizeof(tmpKey)); + MEMZERO(tmpValue, sizeof(tmpValue)); + if (parser_getItem(txObj, &uiFields) != parser_ok) { + ZEMU_LOGF(100, "Could not parse swap tx destination address.\n"); + return parser_swap_tx_wrong_dest_addr; + } + + if (strncmp(tmpValue, G_swap_state.destination_address, sizeof(G_swap_state.destination_address)) != 0) { + ZEMU_LOGF(200, "Wrong swap tx destination address ('%s', should be : '%s').\n", tmpValue, + G_swap_state.destination_address); + return parser_swap_tx_wrong_dest_addr; + } + + // Check amount. + uiFields.displayIdx = 4; + MEMZERO(tmpKey, sizeof(tmpKey)); + MEMZERO(tmpValue, sizeof(tmpValue)); + if (parser_getItem(txObj, &uiFields) != parser_ok) { + ZEMU_LOGF(100, "Could not parse swap tx amount.\n"); + return parser_swap_tx_wrong_dest_addr; + } + char tmpAmount[100] = {0}; + const zxerr_t zxerr = + bytesAmountToStringBalance(G_swap_state.amount, sizeof(G_swap_state.amount), tmpAmount, sizeof(tmpAmount)); + + const size_t strLen = strlen(tmpValue); + const size_t amountLen = strlen(tmpAmount); + if (zxerr != zxerr_ok || strLen != amountLen || strncmp(tmpValue, tmpAmount, strLen) != 0) { + ZEMU_LOGF(200, "Wrong swap tx amount (%s, should be : %s).\n", tmp_str, tmpAmount); + return parser_swap_tx_wrong_amount; + } + + ZEMU_LOGF(50, "Swap parameters verified by current tx\n"); + return err; +} + +void __attribute__((noreturn)) finalize_exchange_sign_transaction(bool is_success) { + *G_swap_sign_return_value_address = is_success; + os_lib_end(); +} diff --git a/app/src/swap/swap.h b/app/src/swap/swap.h new file mode 100644 index 00000000..b7f30b97 --- /dev/null +++ b/app/src/swap/swap.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * (c) 2016 Ledger + * (c) 2018 - 2023 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#include "lib_standard_app/swap_lib_calls.h" +#include "parser_common.h" +#include "stdbool.h" +#include "stdint.h" +#include "zxerror.h" + +typedef struct { + uint8_t amount[16]; + uint8_t amount_length; + uint64_t fees; + char destination_address[65]; + /* Is swap mode */ + uint8_t called_from_swap; + uint8_t should_exit; +} swap_globals_t; + +extern swap_globals_t G_swap_state; + +// Handler for swap features +parser_error_t check_swap_conditions(parser_tx_t *txObj); +void handle_check_address(check_address_parameters_t *params); +void handle_get_printable_amount(get_printable_amount_parameters_t *params); +bool copy_transaction_parameters(create_transaction_parameters_t *sign_transaction_params); +void __attribute__((noreturn)) finalize_exchange_sign_transaction(bool is_success); diff --git a/app/src/swap/swap_utils.c b/app/src/swap/swap_utils.c new file mode 100644 index 00000000..386cbea1 --- /dev/null +++ b/app/src/swap/swap_utils.c @@ -0,0 +1,70 @@ +/******************************************************************************* + * (c) 2016 Ledger + * (c) 2018 - 2023 Zondax AG + * + * 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. + ********************************************************************************/ +#include "bignum.h" +#include "crypto.h" +#include "lib_standard_app/swap_lib_calls.h" +#include "swap.h" +#include "zxformat.h" + +#define DOT_COIN_AMOUNT_DECIMAL_PLACES 10 +#define DOT_COIN_TICKER "DOT " +//////////////////////////////////////////////////////////////// + +zxerr_t bytesAmountToStringBalance(uint8_t *amount, uint8_t amount_len, char *out, uint8_t out_len) { + uint8_t tmpBuf[50] = {0}; + + // Convert byte array (up to 128bits/16bytes) to decimal string + bignumBigEndian_to_bcd(tmpBuf, sizeof(tmpBuf), amount, amount_len); + bignumBigEndian_bcdprint(out, out_len, tmpBuf, sizeof(tmpBuf)); + + // Format number. + if (!intstr_to_fpstr_inplace(out, out_len, DOT_COIN_AMOUNT_DECIMAL_PLACES)) { + return zxerr_encoding_failed; + } + + // Add ticker prefix. + CHECK_ZXERR(z_str3join(out, out_len, DOT_COIN_TICKER, "")) + + // Trim trailing zeros + number_inplace_trimming(out, 1); + + return zxerr_ok; +} + +zxerr_t readU32BE(uint8_t *input, uint32_t *output) { + if (input == NULL || output == NULL) { + return zxerr_no_data; + } + + *output = 0; + for (uint8_t i = 0; i < 4; i++) { + *output += (uint32_t) * (input + i) << (32 - (8 * (i + 1))); + } + return zxerr_ok; +} + +zxerr_t readU64BE(uint8_t *input, uint64_t *output) { + if (input == NULL || output == NULL) { + return zxerr_no_data; + } + + *output = 0; + for (uint8_t i = 0; i < 8; i++) { + *output += (uint64_t) * (input + i) << (64 - (8 * (i + 1))); + } + return zxerr_ok; +} diff --git a/app/src/swap/swap_utils.h b/app/src/swap/swap_utils.h new file mode 100644 index 00000000..760d1ee7 --- /dev/null +++ b/app/src/swap/swap_utils.h @@ -0,0 +1,25 @@ +/******************************************************************************* + * (c) 2018 - 2024 Zondax AG + * + * 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. + ********************************************************************************/ +#pragma once + +#include "stdbool.h" +#include "stdint.h" +#include "zxerror.h" + +// Helper functions for swap handlers +zxerr_t bytesAmountToStringBalance(uint8_t *amount, uint8_t amount_len, char *out, uint8_t out_len); +zxerr_t readU64BE(uint8_t *input, uint64_t *output); +zxerr_t readU32BE(uint8_t *input, uint32_t *output);