From de37b89f8e88e21939a59822c4b57aac1d1c98ed Mon Sep 17 00:00:00 2001 From: Sarah GLINER Date: Thu, 4 Apr 2024 12:49:22 +0200 Subject: [PATCH] Lint: apply clang-format --- .github/workflows/coding_style_checks.yml | 25 + lib-app-bitcoin/apdu/apdu_constants.h | 49 +- lib-app-bitcoin/apdu/dispatcher.c | 172 +- lib-app-bitcoin/app_main.c | 100 +- lib-app-bitcoin/context.c | 48 +- lib-app-bitcoin/context.h | 385 ++-- lib-app-bitcoin/customizable_helpers.c | 174 +- lib-app-bitcoin/customizable_helpers.h | 39 +- lib-app-bitcoin/customizable_ui.c | 197 +- lib-app-bitcoin/customizable_ui.h | 37 +- lib-app-bitcoin/display_utils.c | 216 ++- lib-app-bitcoin/display_utils.h | 26 +- lib-app-bitcoin/filesystem.h | 44 +- lib-app-bitcoin/filesystem_tx.h | 47 +- lib-app-bitcoin/handler/get_coin_version.c | 66 +- .../handler/get_firmware_version.c | 52 +- lib-app-bitcoin/handler/get_trusted_input.c | 132 +- .../handler/get_wallet_public_key.c | 309 +-- .../handler/hash_input_finalize_full.c | 1098 +++++------ lib-app-bitcoin/handler/hash_input_start.c | 195 +- lib-app-bitcoin/handler/hash_sign.c | 303 ++- lib-app-bitcoin/handler/sign_message.c | 450 +++-- lib-app-bitcoin/helpers.c | 361 ++-- lib-app-bitcoin/helpers.h | 60 +- lib-app-bitcoin/swap/handle_check_address.c | 226 ++- lib-app-bitcoin/swap/handle_check_address.h | 3 +- .../swap/handle_get_printable_amount.c | 31 +- .../swap/handle_get_printable_amount.h | 5 +- .../swap/handle_swap_sign_transaction.c | 100 +- .../swap/handle_swap_sign_transaction.h | 6 +- lib-app-bitcoin/transaction.c | 1694 ++++++++--------- lib-app-bitcoin/transaction.h | 34 +- lib-app-bitcoin/ui/display_variables.h | 51 +- lib-app-bitcoin/ui/extensions.h | 40 +- lib-app-bitcoin/ui/main_ui.c | 460 +++-- lib-app-bitcoin/ui/ui_bagl.c | 13 +- lib-app-bitcoin/ui/ui_menu_nbgl.c | 7 +- lib-app-bitcoin/ui/ui_nbgl.c | 229 ++- lib-app-bitcoin/utils/be_operations.c | 62 +- lib-app-bitcoin/utils/be_operations.h | 36 +- lib-app-bitcoin/utils/cashaddr.c | 189 +- lib-app-bitcoin/utils/segwit_addr.c | 274 +-- 42 files changed, 3976 insertions(+), 4069 deletions(-) create mode 100644 .github/workflows/coding_style_checks.yml diff --git a/.github/workflows/coding_style_checks.yml b/.github/workflows/coding_style_checks.yml new file mode 100644 index 00000000..6be27868 --- /dev/null +++ b/.github/workflows/coding_style_checks.yml @@ -0,0 +1,25 @@ +name: Run coding style check through reusable workflow + +# This workflow will run linting checks to ensure a level of uniformization among all Ledger applications. +# +# The presence of this workflow is mandatory as a minimal level of linting is required. +# You are however free to modify the content of the .clang-format file and thus the coding style of your application. +# We simply ask you to not diverge too much from the linting of the Boilerplate application. + +on: + workflow_dispatch: + push: + branches: + - master + - main + - develop + pull_request: + +jobs: + check_linting: + name: Check linting using the reusable workflow + uses: LedgerHQ/ledger-app-workflows/.github/workflows/reusable_lint.yml@v1 + with: + source: './src' + extensions: 'h,c' + version: 11 diff --git a/lib-app-bitcoin/apdu/apdu_constants.h b/lib-app-bitcoin/apdu/apdu_constants.h index f5265425..ce412089 100644 --- a/lib-app-bitcoin/apdu/apdu_constants.h +++ b/lib-app-bitcoin/apdu/apdu_constants.h @@ -1,24 +1,24 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once -#include "os.h" #include "buffer.h" #include "macros.h" +#include "os.h" #define CLA 0xE0 @@ -49,12 +49,15 @@ #define ZCASH_USING_OVERWINTER 0x01 #define ZCASH_USING_OVERWINTER_SAPLING 0x02 -unsigned short handler_sign_message(buffer_t* buffer, uint8_t p1, uint8_t p2); -unsigned short handler_hash_sign(buffer_t* buffer, uint8_t p1, uint8_t p2); -unsigned short handler_hash_input_start(buffer_t* buffer, uint8_t p1, uint8_t p2); -unsigned short handler_hash_input_finalize_full(buffer_t* buffer, uint8_t p1, uint8_t p2); -unsigned short handler_get_wallet_public_key(buffer_t* buffer, uint8_t p1, uint8_t p2); -unsigned short handler_get_trusted_input(buffer_t* buffer, uint8_t p1, uint8_t p2); +unsigned short handler_sign_message(buffer_t *buffer, uint8_t p1, uint8_t p2); +unsigned short handler_hash_sign(buffer_t *buffer, uint8_t p1, uint8_t p2); +unsigned short handler_hash_input_start(buffer_t *buffer, uint8_t p1, + uint8_t p2); +unsigned short handler_hash_input_finalize_full(buffer_t *buffer, uint8_t p1, + uint8_t p2); +unsigned short handler_get_wallet_public_key(buffer_t *buffer, uint8_t p1, + uint8_t p2); +unsigned short handler_get_trusted_input(buffer_t *buffer, uint8_t p1, + uint8_t p2); unsigned short handler_get_firmware_version(void); unsigned short handler_get_coin_version(void); - diff --git a/lib-app-bitcoin/apdu/dispatcher.c b/lib-app-bitcoin/apdu/dispatcher.c index 7e2c6c0e..1c44fae9 100644 --- a/lib-app-bitcoin/apdu/dispatcher.c +++ b/lib-app-bitcoin/apdu/dispatcher.c @@ -15,103 +15,103 @@ * limitations under the License. *****************************************************************************/ -#include #include +#include #include "buffer.h" #include "io.h" #include "ledger_assert.h" -#include "dispatcher.h" #include "apdu_constants.h" +#include "dispatcher.h" int apdu_dispatcher(const command_t *cmd) { - LEDGER_ASSERT(cmd != NULL, "NULL cmd"); + LEDGER_ASSERT(cmd != NULL, "NULL cmd"); + + if (cmd->cla != CLA) { + return io_send_sw(SW_CLA_NOT_SUPPORTED); + } + + buffer_t buf = {0}; - if (cmd->cla != CLA) { - return io_send_sw(SW_CLA_NOT_SUPPORTED); + switch (cmd->ins) { + case INS_GET_WALLET_PUBLIC_KEY: + PRINTF("Get wallet public key\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); } + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_get_wallet_public_key(&buf, cmd->p1, cmd->p2); + + case INS_GET_TRUSTED_INPUT: + PRINTF("Get trusted input\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_get_trusted_input(&buf, cmd->p1, cmd->p2); + + case INS_HASH_INPUT_START: + PRINTF("Hash input start\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_input_start(&buf, cmd->p1, cmd->p2); - buffer_t buf = {0}; - - switch (cmd->ins) { - case INS_GET_WALLET_PUBLIC_KEY: - PRINTF("Get wallet public key\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_get_wallet_public_key(&buf, cmd->p1, cmd->p2); - - case INS_GET_TRUSTED_INPUT: - PRINTF("Get trusted input\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_get_trusted_input(&buf, cmd->p1, cmd->p2); - - case INS_HASH_INPUT_START: - PRINTF("Hash input start\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_hash_input_start(&buf, cmd->p1, cmd->p2); - - case INS_HASH_SIGN: - PRINTF("Hash sign\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_hash_sign(&buf, cmd->p1, cmd->p2); - - case INS_HASH_INPUT_FINALIZE_FULL: - PRINTF("Hash input finalize full\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_hash_input_finalize_full(&buf, cmd->p1, cmd->p2); - - case INS_SIGN_MESSAGE: - PRINTF("Sign message\n"); - if (!cmd->data) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - buf.ptr = cmd->data; - buf.size = cmd->lc; - buf.offset = 0; - return handler_sign_message(&buf, cmd->p1, cmd->p2); - - case INS_GET_FIRMWARE_VERSION: - PRINTF("Get firmware version\n"); - - return handler_get_firmware_version(); - - case INS_GET_COIN_VER: - PRINTF("Get coin version\n"); - - return handler_get_coin_version(); - - default: - PRINTF("Instruction not supported\n"); - return io_send_sw(SW_INS_NOT_SUPPORTED); + case INS_HASH_SIGN: + PRINTF("Hash sign\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_sign(&buf, cmd->p1, cmd->p2); + + case INS_HASH_INPUT_FINALIZE_FULL: + PRINTF("Hash input finalize full\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_hash_input_finalize_full(&buf, cmd->p1, cmd->p2); + + case INS_SIGN_MESSAGE: + PRINTF("Sign message\n"); + if (!cmd->data) { + return io_send_sw(SW_INCORRECT_LENGTH); + } + + buf.ptr = cmd->data; + buf.size = cmd->lc; + buf.offset = 0; + return handler_sign_message(&buf, cmd->p1, cmd->p2); + + case INS_GET_FIRMWARE_VERSION: + PRINTF("Get firmware version\n"); + + return handler_get_firmware_version(); + + case INS_GET_COIN_VER: + PRINTF("Get coin version\n"); + + return handler_get_coin_version(); + + default: + PRINTF("Instruction not supported\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + } } diff --git a/lib-app-bitcoin/app_main.c b/lib-app-bitcoin/app_main.c index c2f9a62b..9c32e633 100644 --- a/lib-app-bitcoin/app_main.c +++ b/lib-app-bitcoin/app_main.c @@ -1,72 +1,66 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ -#include "swap.h" + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "io.h" +#include "swap.h" -#include "context.h" #include "apdu_constants.h" +#include "context.h" #include "dispatcher.h" #include "ui.h" - void app_main(void) { - // Structured APDU command - command_t cmd; + // Structured APDU command + command_t cmd; - io_init(); + io_init(); - if (!G_called_from_swap) { - ui_idle_flow(); - } + if (!G_called_from_swap) { + ui_idle_flow(); + } - context_init(); + context_init(); - for (;;) { - // Length of APDU command received in G_io_apdu_buffer - int input_len = 0; + for (;;) { + // Length of APDU command received in G_io_apdu_buffer + int input_len = 0; - // Receive command bytes in G_io_apdu_buffer - if ((input_len = io_recv_command()) < 0) { - PRINTF("=> io_recv_command failure\n"); - return; - } + // Receive command bytes in G_io_apdu_buffer + if ((input_len = io_recv_command()) < 0) { + PRINTF("=> io_recv_command failure\n"); + return; + } - // Parse APDU command from G_io_apdu_buffer - if (!apdu_parser(&cmd, G_io_apdu_buffer, input_len)) { - PRINTF("=> /!\\ BAD LENGTH: %.*H\n", input_len, G_io_apdu_buffer); - io_send_sw(SW_INCORRECT_LENGTH); - continue; - } + // Parse APDU command from G_io_apdu_buffer + if (!apdu_parser(&cmd, G_io_apdu_buffer, input_len)) { + PRINTF("=> /!\\ BAD LENGTH: %.*H\n", input_len, G_io_apdu_buffer); + io_send_sw(SW_INCORRECT_LENGTH); + continue; + } - PRINTF("=> CLA=%02X | INS=%02X | P1=%02X | P2=%02X | Lc=%02X | CData=%.*H\n", - cmd.cla, - cmd.ins, - cmd.p1, - cmd.p2, - cmd.lc, - cmd.lc, - cmd.data); + PRINTF( + "=> CLA=%02X | INS=%02X | P1=%02X | P2=%02X | Lc=%02X | CData=%.*H\n", + cmd.cla, cmd.ins, cmd.p1, cmd.p2, cmd.lc, cmd.lc, cmd.data); - context.outLength = 0; + context.outLength = 0; - // Dispatch structured APDU command to handler - if (apdu_dispatcher(&cmd) < 0) { - PRINTF("=> apdu_dispatcher failure\n"); - return; - } + // Dispatch structured APDU command to handler + if (apdu_dispatcher(&cmd) < 0) { + PRINTF("=> apdu_dispatcher failure\n"); + return; } + } } diff --git a/lib-app-bitcoin/context.c b/lib-app-bitcoin/context.c index 2cf6da40..1f19b25b 100644 --- a/lib-app-bitcoin/context.c +++ b/lib-app-bitcoin/context.c @@ -1,19 +1,19 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "context.h" #include "filesystem.h" @@ -25,14 +25,12 @@ storage_t const N_real; * Initialize the application context on boot */ void context_init() { - PRINTF("Context init\n"); - PRINTF("Backup size %d\n", sizeof(g_nvram_data.bkp)); - memset(&context, 0, sizeof(context)); - context.currentOutputOffset = 0; - context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - memset(context.totalOutputAmount, 0, - sizeof(context.totalOutputAmount)); - context.changeOutputFound = 0; - context.segwitWarningSeen = 0; + PRINTF("Context init\n"); + PRINTF("Backup size %d\n", sizeof(g_nvram_data.bkp)); + memset(&context, 0, sizeof(context)); + context.currentOutputOffset = 0; + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + memset(context.totalOutputAmount, 0, sizeof(context.totalOutputAmount)); + context.changeOutputFound = 0; + context.segwitWarningSeen = 0; } - diff --git a/lib-app-bitcoin/context.h b/lib-app-bitcoin/context.h index 48a4aa1a..08978a7a 100644 --- a/lib-app-bitcoin/context.h +++ b/lib-app-bitcoin/context.h @@ -1,30 +1,29 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once -#include "os.h" #include "cx.h" #include "filesystem_tx.h" +#include "os.h" #ifdef HAVE_NBGL #include "nbgl_types.h" #endif // HAVE_NBGL - #define MAX_OUTPUT_TO_CHECK 100 #define MAX_COIN_ID 13 #define MAX_SHORT_COIN_ID 5 @@ -35,222 +34,218 @@ * Current state of an untrusted transaction hashing */ enum transaction_state_e { - /** No transaction in progress */ - TRANSACTION_NONE = 0x00, - /** Transaction defined, waiting for an input to be hashed */ - TRANSACTION_DEFINED_WAIT_INPUT = 0x01, - /** Transaction defined, input hashing in progress, pending input script - data */ - TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT = 0x02, - /** Transaction defined, input hashing done, pending output hashing for this - input */ - TRANSACTION_INPUT_HASHING_DONE = 0x03, - /** Transaction defined, waiting for an output to be hashed */ - TRANSACTION_DEFINED_WAIT_OUTPUT = 0x04, - /** Transaction defined, output hashing in progress for a complex script, - pending output script data */ - TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT = 0x05, - /** Transaction defined, output hashing done, pending finalization */ - TRANSACTION_OUTPUT_HASHING_DONE = 0x06, - /** Extra data present */ - TRANSACTION_PROCESS_EXTRA = 0x07, - /** Transaction parsed */ - TRANSACTION_PARSED = 0x08, - /** Transaction parsed, ready to prepare for signature after validating the - user outputs */ - TRANSACTION_PRESIGN_READY = 0x09, - /** Transaction fully parsed, ready to be signed */ - TRANSACTION_SIGN_READY = 0x0a, + /** No transaction in progress */ + TRANSACTION_NONE = 0x00, + /** Transaction defined, waiting for an input to be hashed */ + TRANSACTION_DEFINED_WAIT_INPUT = 0x01, + /** Transaction defined, input hashing in progress, pending input script + data */ + TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT = 0x02, + /** Transaction defined, input hashing done, pending output hashing for this + input */ + TRANSACTION_INPUT_HASHING_DONE = 0x03, + /** Transaction defined, waiting for an output to be hashed */ + TRANSACTION_DEFINED_WAIT_OUTPUT = 0x04, + /** Transaction defined, output hashing in progress for a complex script, + pending output script data */ + TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT = 0x05, + /** Transaction defined, output hashing done, pending finalization */ + TRANSACTION_OUTPUT_HASHING_DONE = 0x06, + /** Extra data present */ + TRANSACTION_PROCESS_EXTRA = 0x07, + /** Transaction parsed */ + TRANSACTION_PARSED = 0x08, + /** Transaction parsed, ready to prepare for signature after validating the + user outputs */ + TRANSACTION_PRESIGN_READY = 0x09, + /** Transaction fully parsed, ready to be signed */ + TRANSACTION_SIGN_READY = 0x0a, }; typedef enum transaction_state_e transaction_state_t; enum output_parsing_state_e { - OUTPUT_PARSING_NONE = 0x00, - OUTPUT_PARSING_NUMBER_OUTPUTS = 0x01, - OUTPUT_PARSING_OUTPUT = 0x02, - OUTPUT_FINALIZE_TX = 0x03, - BIP44_CHANGE_PATH_VALIDATION = 0x04 + OUTPUT_PARSING_NONE = 0x00, + OUTPUT_PARSING_NUMBER_OUTPUTS = 0x01, + OUTPUT_PARSING_OUTPUT = 0x02, + OUTPUT_FINALIZE_TX = 0x03, + BIP44_CHANGE_PATH_VALIDATION = 0x04 }; typedef enum output_parsing_state_e output_parsing_state_t; - typedef union multi_hash { - cx_sha256_t sha256; - cx_blake2b_t blake2b; + cx_sha256_t sha256; + cx_blake2b_t blake2b; } multi_hash; struct segwit_hash_s { - union multi_hash hashPrevouts; + union multi_hash hashPrevouts; }; struct segwit_cache_s { - unsigned char hashedPrevouts[32]; - unsigned char hashedSequence[32]; - unsigned char hashedOutputs[32]; + unsigned char hashedPrevouts[32]; + unsigned char hashedSequence[32]; + unsigned char hashedOutputs[32]; }; /** * Structure defining an operation on a transaction */ struct transaction_context_s { - /** Transient over signing components */ - - /** Remaining number of inputs/outputs to process for this transaction */ - unsigned long int transactionRemainingInputsOutputs; - /** Index of the currently processed input/output for this transaction */ - unsigned long int transactionCurrentInputOutput; - /** Remaining script bytes to process for the current input or output */ - unsigned long int scriptRemaining; - - /** Persistent over signing components */ - - /** State of the transaction, type transaction_state_t */ - unsigned char transactionState; - /** Computed sum of transaction inputs or value of the output to convert to - * a trusted input */ - unsigned char transactionAmount[8]; - /** Flag indicating if this transaction has been processed before */ - unsigned char firstSigned; - /** If the transaction is relaxed */ - unsigned char relaxed; - /** If the transaction consumes a P2SH input */ - unsigned char consumeP2SH; + /** Transient over signing components */ + + /** Remaining number of inputs/outputs to process for this transaction */ + unsigned long int transactionRemainingInputsOutputs; + /** Index of the currently processed input/output for this transaction */ + unsigned long int transactionCurrentInputOutput; + /** Remaining script bytes to process for the current input or output */ + unsigned long int scriptRemaining; + + /** Persistent over signing components */ + + /** State of the transaction, type transaction_state_t */ + unsigned char transactionState; + /** Computed sum of transaction inputs or value of the output to convert to + * a trusted input */ + unsigned char transactionAmount[8]; + /** Flag indicating if this transaction has been processed before */ + unsigned char firstSigned; + /** If the transaction is relaxed */ + unsigned char relaxed; + /** If the transaction consumes a P2SH input */ + unsigned char consumeP2SH; }; typedef struct transaction_context_s transaction_context_t; struct tmp_output_s { - /** Change address if initialized */ - unsigned char changeAddress[20]; - /** Flag set if the change address was initialized */ - unsigned char changeInitialized; - /** Flag set if the change address was checked */ - unsigned char changeChecked; - /** Flag set if the change address can be submitted */ - unsigned char changeAccepted; - /** Flag set if the outputs have been fragmented */ - unsigned char multipleOutput; + /** Change address if initialized */ + unsigned char changeAddress[20]; + /** Flag set if the change address was initialized */ + unsigned char changeInitialized; + /** Flag set if the change address was checked */ + unsigned char changeChecked; + /** Flag set if the change address can be submitted */ + unsigned char changeAccepted; + /** Flag set if the outputs have been fragmented */ + unsigned char multipleOutput; }; typedef struct tmp_output_s tmp_output_t; struct context_s { - /** Index of the output to convert into a trusted input in a transaction */ - unsigned long int trustedInputIndex; - /** (Integrity protected) transaction context */ - transaction_context_t transactionContext; - - /** Full transaction hash context */ - union multi_hash transactionHashFull; - /** Authorization transaction hash context */ - cx_sha256_t transactionHashAuthorization; - /** Current hash to perform (TRANSACTION_HASH_) */ - unsigned char transactionHashOption; - - /* Segregated Witness changes */ - - union { - struct segwit_hash_s hash; - struct segwit_cache_s cache; - } segwit; - unsigned char transactionVersion[4]; - unsigned char inputValue[8]; - unsigned char usingSegwit; - unsigned char usingCashAddr; - unsigned char segwitParsedOnce; - /** Prevents display of segwit input warning at each InputHashStart APDU */ - unsigned char segwitWarningSeen; - - /* /Segregated Witness changes */ - - /** Size currently available to the transaction parser */ - unsigned char transactionDataRemaining; - /** Current pointer to the transaction buffer for the transaction parser */ - unsigned char *transactionBufferPointer; - /** Trusted Input index processed */ - unsigned char trustedInputProcessed; - /** Transaction input to catch for a Trusted Input lookup */ - unsigned long int transactionTargetInput; - - /** Length of the incoming command */ - unsigned short inLength; - /** Length of the outgoing command */ - unsigned short outLength; - - /** Status Word of the response */ - unsigned short sw; - - /** Current scratch buffer */ - unsigned char *tmp; - - // was previously in NVRAM - transaction_summary_t transactionSummary; - - - unsigned short hashedMessageLength; - - union { - tmp_output_t output; - } tmpCtx; - - unsigned char currentOutput[MAX_OUTPUT_TO_CHECK]; - unsigned short currentOutputOffset; - unsigned int remainingOutputs; - unsigned int totalOutputs; - unsigned int discardSize; - unsigned char outputParsingState; - unsigned char totalOutputAmount[8]; - unsigned char changeOutputFound; - - /* Overwinter */ - unsigned char usingOverwinter; - unsigned char overwinterSignReady; - unsigned char nVersionGroupId[4]; - unsigned char nExpiryHeight[4]; - unsigned char nLockTime[4]; - unsigned char sigHashType[4]; + /** Index of the output to convert into a trusted input in a transaction */ + unsigned long int trustedInputIndex; + /** (Integrity protected) transaction context */ + transaction_context_t transactionContext; + + /** Full transaction hash context */ + union multi_hash transactionHashFull; + /** Authorization transaction hash context */ + cx_sha256_t transactionHashAuthorization; + /** Current hash to perform (TRANSACTION_HASH_) */ + unsigned char transactionHashOption; + + /* Segregated Witness changes */ + + union { + struct segwit_hash_s hash; + struct segwit_cache_s cache; + } segwit; + unsigned char transactionVersion[4]; + unsigned char inputValue[8]; + unsigned char usingSegwit; + unsigned char usingCashAddr; + unsigned char segwitParsedOnce; + /** Prevents display of segwit input warning at each InputHashStart APDU */ + unsigned char segwitWarningSeen; + + /* /Segregated Witness changes */ + + /** Size currently available to the transaction parser */ + unsigned char transactionDataRemaining; + /** Current pointer to the transaction buffer for the transaction parser */ + unsigned char *transactionBufferPointer; + /** Trusted Input index processed */ + unsigned char trustedInputProcessed; + /** Transaction input to catch for a Trusted Input lookup */ + unsigned long int transactionTargetInput; + + /** Length of the incoming command */ + unsigned short inLength; + /** Length of the outgoing command */ + unsigned short outLength; + + /** Status Word of the response */ + unsigned short sw; + + /** Current scratch buffer */ + unsigned char *tmp; + + // was previously in NVRAM + transaction_summary_t transactionSummary; + + unsigned short hashedMessageLength; + + union { + tmp_output_t output; + } tmpCtx; + + unsigned char currentOutput[MAX_OUTPUT_TO_CHECK]; + unsigned short currentOutputOffset; + unsigned int remainingOutputs; + unsigned int totalOutputs; + unsigned int discardSize; + unsigned char outputParsingState; + unsigned char totalOutputAmount[8]; + unsigned char changeOutputFound; + + /* Overwinter */ + unsigned char usingOverwinter; + unsigned char overwinterSignReady; + unsigned char nVersionGroupId[4]; + unsigned char nExpiryHeight[4]; + unsigned char nLockTime[4]; + unsigned char sigHashType[4]; }; typedef struct context_s context_t; - /** * Structure to configure the bitcoin application for a given altcoin * */ typedef enum coin_flags_e { - FLAG_PEERCOIN_UNITS=1, - FLAG_PEERCOIN_SUPPORT=2, - FLAG_SEGWIT_CHANGE_SUPPORT=4 + FLAG_PEERCOIN_UNITS = 1, + FLAG_PEERCOIN_SUPPORT = 2, + FLAG_SEGWIT_CHANGE_SUPPORT = 4 } coin_flags_t; - typedef enum coin_kind_e { - COIN_KIND_BITCOIN_TESTNET, - COIN_KIND_BITCOIN, - COIN_KIND_BITCOIN_CASH, - COIN_KIND_BITCOIN_GOLD, - COIN_KIND_LITECOIN, - COIN_KIND_DOGE, - COIN_KIND_DASH, - COIN_KIND_ZCASH, - COIN_KIND_KOMODO, - COIN_KIND_RFU, - COIN_KIND_STRATIS, - COIN_KIND_PEERCOIN, - COIN_KIND_PIVX, - COIN_KIND_STEALTH, - COIN_KIND_VIACOIN, - COIN_KIND_VERTCOIN, - COIN_KIND_DIGIBYTE, - COIN_KIND_BITCOIN_PRIVATE, - COIN_KIND_XRHODIUM, - COIN_KIND_HORIZEN, - COIN_KIND_GAMECREDITS, - COIN_KIND_FIRO, - COIN_KIND_ZCLASSIC, - COIN_KIND_XSN, - COIN_KIND_NIX, - COIN_KIND_LBRY, - COIN_KIND_RESISTANCE, - COIN_KIND_RAVENCOIN, - COIN_KIND_HYDRA + COIN_KIND_BITCOIN_TESTNET, + COIN_KIND_BITCOIN, + COIN_KIND_BITCOIN_CASH, + COIN_KIND_BITCOIN_GOLD, + COIN_KIND_LITECOIN, + COIN_KIND_DOGE, + COIN_KIND_DASH, + COIN_KIND_ZCASH, + COIN_KIND_KOMODO, + COIN_KIND_RFU, + COIN_KIND_STRATIS, + COIN_KIND_PEERCOIN, + COIN_KIND_PIVX, + COIN_KIND_STEALTH, + COIN_KIND_VIACOIN, + COIN_KIND_VERTCOIN, + COIN_KIND_DIGIBYTE, + COIN_KIND_BITCOIN_PRIVATE, + COIN_KIND_XRHODIUM, + COIN_KIND_HORIZEN, + COIN_KIND_GAMECREDITS, + COIN_KIND_FIRO, + COIN_KIND_ZCLASSIC, + COIN_KIND_XSN, + COIN_KIND_NIX, + COIN_KIND_LBRY, + COIN_KIND_RESISTANCE, + COIN_KIND_RAVENCOIN, + COIN_KIND_HYDRA } coin_kind_t; void context_init(void); diff --git a/lib-app-bitcoin/customizable_helpers.c b/lib-app-bitcoin/customizable_helpers.c index 78e70a39..098e4670 100644 --- a/lib-app-bitcoin/customizable_helpers.c +++ b/lib-app-bitcoin/customizable_helpers.c @@ -1,19 +1,19 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "macros.h" #include "context.h" @@ -30,18 +30,16 @@ const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = {0x87}; // OP_EQUAL const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE[] = { - 0x3D, 0xA9, - 0x14}; // script length, OP_HASH160, address length + 0x3D, 0xA9, 0x14}; // script length, OP_HASH160, address length const unsigned char ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST[] = { - 0x87, // OP_EQUAL - 0x20, 0x9E, 0xC9, 0x84, 0x5A, 0xCB, 0x02, 0xFA, 0XB2, 0X4E, - 0x1C, 0x03, 0x68, 0xB3, 0xB5, 0x17, 0xC1, 0xA4, 0x48, 0x8F, - 0xBA, 0x97, 0xF0, 0xE3, 0x45, 0x9A, 0xC0, 0x53, 0xEA, 0x01, - 0x00, 0x00, 0x00, // ParamHash - 0x03, // Push 3 bytes to stack to make ParamHeight line up properly - 0xC0, 0x1F, 0x02, // ParamHeight (139200) -> hex -> endianness swapped - 0xB4}; // OP_CHECKBLOCKATHEIGHT + 0x87, // OP_EQUAL + 0x20, 0x9E, 0xC9, 0x84, 0x5A, 0xCB, 0x02, 0xFA, 0XB2, 0X4E, 0x1C, 0x03, + 0x68, 0xB3, 0xB5, 0x17, 0xC1, 0xA4, 0x48, 0x8F, 0xBA, 0x97, 0xF0, 0xE3, + 0x45, 0x9A, 0xC0, 0x53, 0xEA, 0x01, 0x00, 0x00, 0x00, // ParamHash + 0x03, // Push 3 bytes to stack to make ParamHeight line up properly + 0xC0, 0x1F, 0x02, // ParamHeight (139200) -> hex -> endianness swapped + 0xB4}; // OP_CHECKBLOCKATHEIGHT const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE[] = {0x16, 0x00, 0x14}; const unsigned char TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE[] = {0x22, 0x00, 0x20}; @@ -60,92 +58,88 @@ const unsigned char ZEN_OUTPUT_SCRIPT_POST[] = { }; // BIP0115 Replay Protection WEAK unsigned char output_script_is_regular(unsigned char *buffer) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || - (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { - return 1; - } - } - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) && - (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, - TRANSACTION_OUTPUT_SCRIPT_POST, - sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) { - return 1; + if (COIN_NATIVE_SEGWIT_PREFIX) { + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || + (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { + return 1; } - if (COIN_KIND == COIN_KIND_HORIZEN) { - if ((memcmp(buffer, ZEN_OUTPUT_SCRIPT_PRE, - sizeof(ZEN_OUTPUT_SCRIPT_PRE)) == 0) && - (memcmp(buffer + sizeof(ZEN_OUTPUT_SCRIPT_PRE) + 20, - ZEN_OUTPUT_SCRIPT_POST, - sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) { - return 1; - } + } + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE)) == 0) && + (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_POST)) == 0)) { + return 1; + } + if (COIN_KIND == COIN_KIND_HORIZEN) { + if ((memcmp(buffer, ZEN_OUTPUT_SCRIPT_PRE, sizeof(ZEN_OUTPUT_SCRIPT_PRE)) == + 0) && + (memcmp(buffer + sizeof(ZEN_OUTPUT_SCRIPT_PRE) + 20, + ZEN_OUTPUT_SCRIPT_POST, sizeof(ZEN_OUTPUT_SCRIPT_POST)) == 0)) { + return 1; } + } - return 0; + return 0; } WEAK unsigned char output_script_is_p2sh(unsigned char *buffer) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && - (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, - TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { - return 1; + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && + (memcmp(buffer + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, + TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { + return 1; + } + if (COIN_KIND == COIN_KIND_HORIZEN) { + if ((memcmp(buffer, ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && + (memcmp(buffer + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, + ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { + return 1; } - if (COIN_KIND == COIN_KIND_HORIZEN) { - if ((memcmp(buffer, ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE, - sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE)) == 0) && - (memcmp(buffer + sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_PRE) + 20, - ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST, - sizeof(ZEN_TRANSACTION_OUTPUT_SCRIPT_P2SH_POST)) == 0)) { - return 1; - } - } - return 0; + } + return 0; } WEAK unsigned char output_script_is_native_witness(unsigned char *buffer) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || - (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, - sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { - return 1; - } + if (COIN_NATIVE_SEGWIT_PREFIX) { + if ((memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WPKH_PRE)) == 0) || + (memcmp(buffer, TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE, + sizeof(TRANSACTION_OUTPUT_SCRIPT_P2WSH_PRE)) == 0)) { + return 1; } - return 0; + } + return 0; } WEAK unsigned char output_script_is_op_return(unsigned char *buffer) { - if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { - return ((buffer[1] == 0x6A) || ((buffer[1] == 0x00) && (buffer[2] == 0x6A))); - } - else { - return (buffer[1] == 0x6A); - } + if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { + return ((buffer[1] == 0x6A) || + ((buffer[1] == 0x00) && (buffer[2] == 0x6A))); + } else { + return (buffer[1] == 0x6A); + } } WEAK unsigned char output_script_is_op_create_or_call(unsigned char *buffer, - size_t size, - unsigned char value) { - return (!output_script_is_regular(buffer) && - !output_script_is_p2sh(buffer) && - !output_script_is_op_return(buffer) && (buffer[0] <= 0xEA) && - (buffer[0] < size) && - (buffer[buffer[0]] == value)); + size_t size, + unsigned char value) { + return (!output_script_is_regular(buffer) && !output_script_is_p2sh(buffer) && + !output_script_is_op_return(buffer) && (buffer[0] <= 0xEA) && + (buffer[0] < size) && (buffer[buffer[0]] == value)); } WEAK unsigned char output_script_is_op_create(unsigned char *buffer, - size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC1); + size_t size) { + return output_script_is_op_create_or_call(buffer, size, 0xC1); } WEAK unsigned char output_script_is_op_call(unsigned char *buffer, - size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC2); + size_t size) { + return output_script_is_op_create_or_call(buffer, size, 0xC2); } - diff --git a/lib-app-bitcoin/customizable_helpers.h b/lib-app-bitcoin/customizable_helpers.h index ab7ac1c9..4f6e911c 100644 --- a/lib-app-bitcoin/customizable_helpers.h +++ b/lib-app-bitcoin/customizable_helpers.h @@ -1,31 +1,28 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once -#include "os.h" #include "cx.h" +#include "os.h" unsigned char output_script_is_regular(unsigned char *buffer); unsigned char output_script_is_p2sh(unsigned char *buffer); unsigned char output_script_is_op_return(unsigned char *buffer); unsigned char output_script_is_native_witness(unsigned char *buffer); -unsigned char output_script_is_op_create(unsigned char *buffer, - size_t size); -unsigned char output_script_is_op_call(unsigned char *buffer, - size_t size); - +unsigned char output_script_is_op_create(unsigned char *buffer, size_t size); +unsigned char output_script_is_op_call(unsigned char *buffer, size_t size); diff --git a/lib-app-bitcoin/customizable_ui.c b/lib-app-bitcoin/customizable_ui.c index 8a10da47..4e82ade0 100644 --- a/lib-app-bitcoin/customizable_ui.c +++ b/lib-app-bitcoin/customizable_ui.c @@ -1,117 +1,114 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "customizable_ui.h" -#include "customizable_helpers.h" -#include "helpers.h" -#include "context.h" -#include "display_variables.h" -#include "segwit_addr.h" -#include "cashaddr.h" #include "be_operations.h" +#include "cashaddr.h" +#include "context.h" +#include "customizable_helpers.h" #include "display_utils.h" +#include "display_variables.h" +#include "helpers.h" #include "read.h" +#include "segwit_addr.h" - -WEAK void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size) { - if (output_script_is_op_return(script)) { - strncpy(out, "OP_RETURN", out_size); - return; - } - if ((COIN_KIND == COIN_KIND_HYDRA) && - output_script_is_op_create(script, script_size)) { - strncpy(out, "OP_CREATE", out_size); - return; - } - if ((COIN_KIND == COIN_KIND_HYDRA) && - output_script_is_op_call(script, script_size)) { - strncpy(out, "OP_CALL", out_size); - return; +WEAK void get_address_from_output_script(unsigned char *script, int script_size, + char *out, int out_size) { + if (output_script_is_op_return(script)) { + strncpy(out, "OP_RETURN", out_size); + return; + } + if ((COIN_KIND == COIN_KIND_HYDRA) && + output_script_is_op_create(script, script_size)) { + strncpy(out, "OP_CREATE", out_size); + return; + } + if ((COIN_KIND == COIN_KIND_HYDRA) && + output_script_is_op_call(script, script_size)) { + strncpy(out, "OP_CALL", out_size); + return; + } + if (output_script_is_native_witness(script)) { + if (COIN_NATIVE_SEGWIT_PREFIX) { + segwit_addr_encode( + out, (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), 0, + script + OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET, + script[OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - 1]); } - if (output_script_is_native_witness(script)) { - if (COIN_NATIVE_SEGWIT_PREFIX) { - segwit_addr_encode( - out, (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), 0, - script + OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET, - script[OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - 1]); - } - return; - } - unsigned char versionSize; - unsigned char address[22]; - unsigned short textSize; - int addressOffset = 3; - unsigned short version = COIN_P2SH_VERSION; + return; + } + unsigned char versionSize; + unsigned char address[22]; + unsigned short textSize; + int addressOffset = 3; + unsigned short version = COIN_P2SH_VERSION; - if (output_script_is_regular(script)) { - addressOffset = 4; - version = COIN_P2PKH_VERSION; - } + if (output_script_is_regular(script)) { + addressOffset = 4; + version = COIN_P2PKH_VERSION; + } - if (version > 255) { - versionSize = 2; - address[0] = (version >> 8); - address[1] = version; - } else { - versionSize = 1; - address[0] = version; - } - memmove(address + versionSize, script + addressOffset, 20); + if (version > 255) { + versionSize = 2; + address[0] = (version >> 8); + address[1] = version; + } else { + versionSize = 1; + address[0] = version; + } + memmove(address + versionSize, script + addressOffset, 20); - // Prepare address - if (context.usingCashAddr) { - cashaddr_encode( - address + versionSize, 20, (uint8_t *)out, out_size, - (version == COIN_P2SH_VERSION - ? CASHADDR_P2SH - : CASHADDR_P2PKH)); - } else { - textSize = public_key_to_encoded_base58( - address, 20 + versionSize, (unsigned char *)out, - out_size, version, 1); - out[textSize] = '\0'; - } + // Prepare address + if (context.usingCashAddr) { + cashaddr_encode( + address + versionSize, 20, (uint8_t *)out, out_size, + (version == COIN_P2SH_VERSION ? CASHADDR_P2SH : CASHADDR_P2PKH)); + } else { + textSize = public_key_to_encoded_base58( + address, 20 + versionSize, (unsigned char *)out, out_size, version, 1); + out[textSize] = '\0'; + } } WEAK uint8_t prepare_fees(void) { - if (context.transactionContext.relaxed) { - memmove(vars.tmp.feesAmount, "UNKNOWN", 7); - vars.tmp.feesAmount[7] = '\0'; - } else { - unsigned char fees[8]; - unsigned char borrow; + if (context.transactionContext.relaxed) { + memmove(vars.tmp.feesAmount, "UNKNOWN", 7); + vars.tmp.feesAmount[7] = '\0'; + } else { + unsigned char fees[8]; + unsigned char borrow; - borrow = transaction_amount_sub_be( - fees, context.transactionContext.transactionAmount, - context.totalOutputAmount); - if (borrow && COIN_KIND == COIN_KIND_KOMODO) { - memmove(vars.tmp.feesAmount, "REWARD", 6); - vars.tmp.feesAmount[6] = '\0'; - } - else { - if (borrow) { - PRINTF("Error : Fees not consistent"); - goto error; - } - format_sats_amount(COIN_COINID_SHORT, - (uint64_t)read_u64_be(fees, 0), // Cast prevents weird compilo bug - vars.tmp.feesAmount); - } + borrow = transaction_amount_sub_be( + fees, context.transactionContext.transactionAmount, + context.totalOutputAmount); + if (borrow && COIN_KIND == COIN_KIND_KOMODO) { + memmove(vars.tmp.feesAmount, "REWARD", 6); + vars.tmp.feesAmount[6] = '\0'; + } else { + if (borrow) { + PRINTF("Error : Fees not consistent"); + goto error; + } + format_sats_amount( + COIN_COINID_SHORT, + (uint64_t)read_u64_be(fees, 0), // Cast prevents weird compilo bug + vars.tmp.feesAmount); } - return 1; + } + return 1; error: - return 0; + return 0; } diff --git a/lib-app-bitcoin/customizable_ui.h b/lib-app-bitcoin/customizable_ui.h index 75649c2b..767b2211 100644 --- a/lib-app-bitcoin/customizable_ui.h +++ b/lib-app-bitcoin/customizable_ui.h @@ -1,23 +1,24 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ -#pragma once -#include "os.h" + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ +#pragma once #include "macros.h" +#include "os.h" -void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size); +void get_address_from_output_script(unsigned char *script, int script_size, + char *out, int out_size); uint8_t prepare_fees(void); diff --git a/lib-app-bitcoin/display_utils.c b/lib-app-bitcoin/display_utils.c index 4bfae3f5..a871b8f2 100644 --- a/lib-app-bitcoin/display_utils.c +++ b/lib-app-bitcoin/display_utils.c @@ -1,132 +1,138 @@ +#include "filesystem_tx.h" +#include "ledger_assert.h" +#include "read.h" #include #include #include -#include "ledger_assert.h" -#include "read.h" -#include "filesystem_tx.h" #include "./display_utils.h" -// Division and modulus operators over uint64_t causes the inclusion of the __udivmoddi4 and other -// library functions that occupy more than 400 bytes. Since performance is not critical and division -// by 10 is sufficient, we avoid it with a binary search instead. +// Division and modulus operators over uint64_t causes the inclusion of the +// __udivmoddi4 and other library functions that occupy more than 400 bytes. +// Since performance is not critical and division by 10 is sufficient, we avoid +// it with a binary search instead. static uint64_t div10(uint64_t n) { - if (n < 10) return 0; // special case needed to make sure that n - 10 is safe - - // Since low, mid and high are always <= UINT64_MAX / 10, there is no risk of overflow - uint64_t low = 0; - uint64_t high = UINT64_MAX / 10; - - while (true) { - uint64_t mid = (low + high) / 2; - - // the result equals mid if and only if mid * 10 <= n < mid * 10 + 10 - // care is taken to make sure overflows and underflows are impossible - if (mid * 10 > n - 10 && n >= mid * 10) { - return mid; - } else if (n < mid * 10) { - high = mid - 1; - } else /* n >= 10 * mid + 10 */ { - low = mid + 1; - } + if (n < 10) + return 0; // special case needed to make sure that n - 10 is safe + + // Since low, mid and high are always <= UINT64_MAX / 10, there is no risk of + // overflow + uint64_t low = 0; + uint64_t high = UINT64_MAX / 10; + + while (true) { + uint64_t mid = (low + high) / 2; + + // the result equals mid if and only if mid * 10 <= n < mid * 10 + 10 + // care is taken to make sure overflows and underflows are impossible + if (mid * 10 > n - 10 && n >= mid * 10) { + return mid; + } else if (n < mid * 10) { + high = mid - 1; + } else /* n >= 10 * mid + 10 */ { + low = mid + 1; } + } } static uint64_t div100000000(uint64_t n) { - uint64_t res = n; - for (int i = 0; i < 8; i++) res = div10(res); - return res; + uint64_t res = n; + for (int i = 0; i < 8; i++) + res = div10(res); + return res; } static size_t n_digits(uint64_t number) { - size_t count = 0; - do { - count++; + size_t count = 0; + do { + count++; - // HACK: avoid __udivmoddi4 - // number /= 10; + // HACK: avoid __udivmoddi4 + // number /= 10; - number = div10(number); - } while (number != 0); - return count; + number = div10(number); + } while (number != 0); + return count; } -void format_sats_amount(const char *coin_name, - uint64_t amount, +void format_sats_amount(const char *coin_name, uint64_t amount, char out[static MAX_AMOUNT_LENGTH + 1]) { - size_t coin_name_len = strlen(coin_name); - strncpy(out, coin_name, MAX_AMOUNT_LENGTH + 1); - out[coin_name_len] = ' '; + size_t coin_name_len = strlen(coin_name); + strncpy(out, coin_name, MAX_AMOUNT_LENGTH + 1); + out[coin_name_len] = ' '; - char *amount_str = out + coin_name_len + 1; + char *amount_str = out + coin_name_len + 1; - // HACK: avoid __udivmoddi4 - // uint64_t integral_part = amount / 100000000; - // uint32_t fractional_part = (uint32_t) (amount % 100000000); - uint64_t integral_part = div100000000(amount); - uint32_t fractional_part = (uint32_t) (amount - integral_part * 100000000); - - // format the integral part, starting from the least significant digit - size_t integral_part_digit_count = n_digits(integral_part); - for (unsigned int i = 0; i < integral_part_digit_count; i++) { - // HACK: avoid __udivmoddi4 - // amount_str[integral_part_digit_count - 1 - i] = '0' + (integral_part % 10); - // integral_part /= 10; - - uint64_t tmp_quotient = div10(integral_part); - char tmp_remainder = (char) (integral_part - 10 * tmp_quotient); - amount_str[integral_part_digit_count - 1 - i] = '0' + tmp_remainder; - integral_part = tmp_quotient; - } + // HACK: avoid __udivmoddi4 + // uint64_t integral_part = amount / 100000000; + // uint32_t fractional_part = (uint32_t) (amount % 100000000); + uint64_t integral_part = div100000000(amount); + uint32_t fractional_part = (uint32_t)(amount - integral_part * 100000000); - if (fractional_part == 0) { - amount_str[integral_part_digit_count] = '\0'; - } else { - // format the fractional part (exactly 8 digits, possibly with trailing zeros) - amount_str[integral_part_digit_count] = '.'; - char *fract_part_str = amount_str + integral_part_digit_count + 1; - snprintf(fract_part_str, 8 + 1, "%08u", fractional_part); - - // drop trailing zeros - for (int i = 7; i > 0 && fract_part_str[i] == '0'; i--) { - fract_part_str[i] = '\0'; - } + // format the integral part, starting from the least significant digit + size_t integral_part_digit_count = n_digits(integral_part); + for (unsigned int i = 0; i < integral_part_digit_count; i++) { + // HACK: avoid __udivmoddi4 + // amount_str[integral_part_digit_count - 1 - i] = '0' + (integral_part % + // 10); integral_part /= 10; + + uint64_t tmp_quotient = div10(integral_part); + char tmp_remainder = (char)(integral_part - 10 * tmp_quotient); + amount_str[integral_part_digit_count - 1 - i] = '0' + tmp_remainder; + integral_part = tmp_quotient; + } + + if (fractional_part == 0) { + amount_str[integral_part_digit_count] = '\0'; + } else { + // format the fractional part (exactly 8 digits, possibly with trailing + // zeros) + amount_str[integral_part_digit_count] = '.'; + char *fract_part_str = amount_str + integral_part_digit_count + 1; + snprintf(fract_part_str, 8 + 1, "%08u", fractional_part); + + // drop trailing zeros + for (int i = 7; i > 0 && fract_part_str[i] == '0'; i--) { + fract_part_str[i] = '\0'; } + } } -unsigned char format_path(const unsigned char *bip32Path, char* out, unsigned char max_out_len) { - - unsigned char bip32PathLength; - unsigned char i, offset; - unsigned int current_level; - bool hardened; - - bip32PathLength = bip32Path[0]; - - LEDGER_ASSERT(bip32PathLength <= MAX_BIP32_PATH, "Wrong path len: %d", bip32PathLength); - - bip32Path++; - out[0] = ' '; - offset=1; - for (i = 0; i < bip32PathLength; i++) { - current_level = read_u32_be(bip32Path, 0); - hardened = (bool)(current_level & 0x80000000); - if(hardened) { - //remove hardening flag - current_level ^= 0x80000000; - } - bip32Path += 4; - snprintf(out+offset, max_out_len-offset, "%u", current_level); - offset = strnlen(out, max_out_len); - LEDGER_ASSERT(offset < max_out_len - 2, "OVERFLOW"); - if(hardened) out[offset++] = '\''; - - out[offset++] = '/'; - out[offset] = '\0'; - } - // remove last '/' - out[offset-1] = '\0'; +unsigned char format_path(const unsigned char *bip32Path, char *out, + unsigned char max_out_len) { - return offset -1; -} + unsigned char bip32PathLength; + unsigned char i, offset; + unsigned int current_level; + bool hardened; + bip32PathLength = bip32Path[0]; + + LEDGER_ASSERT(bip32PathLength <= MAX_BIP32_PATH, "Wrong path len: %d", + bip32PathLength); + + bip32Path++; + out[0] = ' '; + offset = 1; + for (i = 0; i < bip32PathLength; i++) { + current_level = read_u32_be(bip32Path, 0); + hardened = (bool)(current_level & 0x80000000); + if (hardened) { + // remove hardening flag + current_level ^= 0x80000000; + } + bip32Path += 4; + snprintf(out + offset, max_out_len - offset, "%u", current_level); + offset = strnlen(out, max_out_len); + LEDGER_ASSERT(offset < max_out_len - 2, "OVERFLOW"); + if (hardened) + out[offset++] = '\''; + + out[offset++] = '/'; + out[offset] = '\0'; + } + // remove last '/' + out[offset - 1] = '\0'; + + return offset - 1; +} diff --git a/lib-app-bitcoin/display_utils.h b/lib-app-bitcoin/display_utils.h index f5f28ba7..09cb9405 100644 --- a/lib-app-bitcoin/display_utils.h +++ b/lib-app-bitcoin/display_utils.h @@ -2,23 +2,25 @@ #include -// up to 5 chars for ticker, 1 space, up to 20 digits (20 = digits of 2^64), + 1 decimal separator +// up to 5 chars for ticker, 1 space, up to 20 digits (20 = digits of 2^64), + 1 +// decimal separator #define MAX_AMOUNT_LENGTH (5 + 1 + 20 + 1) /** - * Converts a 64-bits unsigned integer into a decimal rapresentation, where the `amount` is a - * multiple of 1/100_000_000th. Trailing decimal zeros are not appended (and no decimal point is - * present if the `amount` is a multiple of 100_000_000). The resulting string is prefixed with a - * ticker name (up to 5 characters long), followed by a space. + * Converts a 64-bits unsigned integer into a decimal rapresentation, where the + * `amount` is a multiple of 1/100_000_000th. Trailing decimal zeros are not + * appended (and no decimal point is present if the `amount` is a multiple of + * 100_000_000). The resulting string is prefixed with a ticker name (up to 5 + * characters long), followed by a space. * - * @param coin_name a zero-terminated ticker name, at most 5 characterso long (not including the - * terminating 0) + * @param coin_name a zero-terminated ticker name, at most 5 characterso long + * (not including the terminating 0) * @param amount the amount to format - * @param out the output array which must be at least MAX_AMOUNT_LENGTH + 1 bytes long + * @param out the output array which must be at least MAX_AMOUNT_LENGTH + 1 + * bytes long */ -void format_sats_amount(const char *coin_name, - uint64_t amount, +void format_sats_amount(const char *coin_name, uint64_t amount, char out[static MAX_AMOUNT_LENGTH + 1]); -unsigned char format_path(const unsigned char *bip32Path, char* out, unsigned char max_out_len); - +unsigned char format_path(const unsigned char *bip32Path, char *out, + unsigned char max_out_len); diff --git a/lib-app-bitcoin/filesystem.h b/lib-app-bitcoin/filesystem.h index fd615b04..208f644f 100644 --- a/lib-app-bitcoin/filesystem.h +++ b/lib-app-bitcoin/filesystem.h @@ -1,38 +1,38 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ -#pragma once +#pragma once -#include "os.h" #include "context.h" #include "filesystem_tx.h" +#include "os.h" enum family_e { - FAMILY_BITCOIN = 0x01, - FAMILY_PEERCOIN = 0x02, - FAMILY_STEALTH = 0x04 + FAMILY_BITCOIN = 0x01, + FAMILY_PEERCOIN = 0x02, + FAMILY_STEALTH = 0x04 }; typedef struct backup_area_s { - uint8_t trustedinput_key[32]; + uint8_t trustedinput_key[32]; } backup_area_t; typedef struct storage_s { - backup_area_t bkp; + backup_area_t bkp; } storage_t; // the global nvram memory variable diff --git a/lib-app-bitcoin/filesystem_tx.h b/lib-app-bitcoin/filesystem_tx.h index 8a413c08..f045e380 100644 --- a/lib-app-bitcoin/filesystem_tx.h +++ b/lib-app-bitcoin/filesystem_tx.h @@ -1,19 +1,19 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once #include "os.h" @@ -30,13 +30,14 @@ #define MAX_BIP44_ADDRESS_INDEX_RECOMMENDED 50000 struct transaction_summary_s { - unsigned char active; - unsigned char payToAddressVersion; - unsigned char payToScriptHashVersion; - unsigned char authorizationHash[32]; - unsigned char keyPath[MAX_BIP32_PATH_LENGTH]; - unsigned char transactionNonce[8]; // used to bind to the current set of inputs - unsigned short messageLength; - unsigned char sighashType; + unsigned char active; + unsigned char payToAddressVersion; + unsigned char payToScriptHashVersion; + unsigned char authorizationHash[32]; + unsigned char keyPath[MAX_BIP32_PATH_LENGTH]; + unsigned char + transactionNonce[8]; // used to bind to the current set of inputs + unsigned short messageLength; + unsigned char sighashType; }; typedef struct transaction_summary_s transaction_summary_t; diff --git a/lib-app-bitcoin/handler/get_coin_version.c b/lib-app-bitcoin/handler/get_coin_version.c index 1366783d..f996d951 100644 --- a/lib-app-bitcoin/handler/get_coin_version.c +++ b/lib-app-bitcoin/handler/get_coin_version.c @@ -1,49 +1,49 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "io.h" #include "write.h" -#include "context.h" #include "apdu_constants.h" +#include "context.h" WEAK unsigned short handler_get_coin_version(void) { - uint8_t offset = 0; - size_t string_size; + uint8_t offset = 0; + size_t string_size; - write_u16_be(G_io_apdu_buffer, offset, COIN_P2PKH_VERSION); - offset += 2; + write_u16_be(G_io_apdu_buffer, offset, COIN_P2PKH_VERSION); + offset += 2; - write_u16_be(G_io_apdu_buffer, offset, COIN_P2SH_VERSION); - offset += 2; + write_u16_be(G_io_apdu_buffer, offset, COIN_P2SH_VERSION); + offset += 2; - G_io_apdu_buffer[offset++] = COIN_FAMILY; + G_io_apdu_buffer[offset++] = COIN_FAMILY; - string_size = strlen(COIN_COINID); - G_io_apdu_buffer[offset++] = string_size; - memmove(G_io_apdu_buffer + offset, COIN_COINID, string_size); - offset += string_size; + string_size = strlen(COIN_COINID); + G_io_apdu_buffer[offset++] = string_size; + memmove(G_io_apdu_buffer + offset, COIN_COINID, string_size); + offset += string_size; - string_size = strlen(COIN_COINID_SHORT); - G_io_apdu_buffer[offset++] = string_size - memmove(G_io_apdu_buffer + offset, COIN_COINID_SHORT, string_size); - offset += string_size; + string_size = strlen(COIN_COINID_SHORT); + G_io_apdu_buffer[offset++] = string_size memmove( + G_io_apdu_buffer + offset, COIN_COINID_SHORT, string_size); + offset += string_size; - context.outLength = offset; + context.outLength = offset; - return io_send_response_pointer(G_io_apdu_buffer, offset, SW_OK); + return io_send_response_pointer(G_io_apdu_buffer, offset, SW_OK); } diff --git a/lib-app-bitcoin/handler/get_firmware_version.c b/lib-app-bitcoin/handler/get_firmware_version.c index 6f11725a..7c9dad9a 100644 --- a/lib-app-bitcoin/handler/get_firmware_version.c +++ b/lib-app-bitcoin/handler/get_firmware_version.c @@ -1,23 +1,23 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "io.h" -#include "context.h" #include "apdu_constants.h" +#include "context.h" #define FEATURES_SELF_SCREEN_BUTTONS 0x02 #define FEATURES_NFC 0x08 @@ -35,16 +35,16 @@ WEAK unsigned short handler_get_firmware_version() { - G_io_apdu_buffer[0] = FEATURES; - G_io_apdu_buffer[1] = ARCH_ID; - G_io_apdu_buffer[2] = MAJOR_VERSION; - G_io_apdu_buffer[3] = MINOR_VERSION; - G_io_apdu_buffer[4] = PATCH_VERSION; - G_io_apdu_buffer[5] = 1; - G_io_apdu_buffer[6] = TCS_LOADER_PATCH_VERSION; - G_io_apdu_buffer[7] = MODE; + G_io_apdu_buffer[0] = FEATURES; + G_io_apdu_buffer[1] = ARCH_ID; + G_io_apdu_buffer[2] = MAJOR_VERSION; + G_io_apdu_buffer[3] = MINOR_VERSION; + G_io_apdu_buffer[4] = PATCH_VERSION; + G_io_apdu_buffer[5] = 1; + G_io_apdu_buffer[6] = TCS_LOADER_PATCH_VERSION; + G_io_apdu_buffer[7] = MODE; - context.outLength = 0x08; + context.outLength = 0x08; - return io_send_response_pointer(G_io_apdu_buffer, 0x08, SW_OK); + return io_send_response_pointer(G_io_apdu_buffer, 0x08, SW_OK); } diff --git a/lib-app-bitcoin/handler/get_trusted_input.c b/lib-app-bitcoin/handler/get_trusted_input.c index 6925b36b..dd634eae 100644 --- a/lib-app-bitcoin/handler/get_trusted_input.c +++ b/lib-app-bitcoin/handler/get_trusted_input.c @@ -1,89 +1,87 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ +#include "io.h" #include "lib_standard_app/read.h" #include "lib_standard_app/write.h" -#include "io.h" +#include "apdu_constants.h" #include "context.h" #include "filesystem.h" #include "transaction.h" -#include "apdu_constants.h" #define GET_TRUSTED_INPUT_P1_FIRST 0x00 #define GET_TRUSTED_INPUT_P1_NEXT 0x80 -WEAK unsigned short handler_get_trusted_input(buffer_t* buffer, uint8_t p1, uint8_t p2) { - unsigned char dataOffset = 0; +WEAK unsigned short handler_get_trusted_input(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + unsigned char dataOffset = 0; - if (p1 == GET_TRUSTED_INPUT_P1_FIRST) { - // Initialize - context.transactionTargetInput = - read_u32_be(buffer->ptr, 0); - context.transactionContext.transactionState = - TRANSACTION_NONE; - context.trustedInputProcessed = 0; - context.transactionContext.consumeP2SH = 0; - dataOffset = 4; - context.transactionHashOption = TRANSACTION_HASH_FULL; - context.usingSegwit = 0; - context.usingOverwinter = 0; - } else if (p1 != GET_TRUSTED_INPUT_P1_NEXT) { - return io_send_sw(SW_INCORRECT_P1_P2); - } + if (p1 == GET_TRUSTED_INPUT_P1_FIRST) { + // Initialize + context.transactionTargetInput = read_u32_be(buffer->ptr, 0); + context.transactionContext.transactionState = TRANSACTION_NONE; + context.trustedInputProcessed = 0; + context.transactionContext.consumeP2SH = 0; + dataOffset = 4; + context.transactionHashOption = TRANSACTION_HASH_FULL; + context.usingSegwit = 0; + context.usingOverwinter = 0; + } else if (p1 != GET_TRUSTED_INPUT_P1_NEXT) { + return io_send_sw(SW_INCORRECT_P1_P2); + } - if (p2 != 0x00) { - return io_send_sw(SW_INCORRECT_P1_P2); - } + if (p2 != 0x00) { + return io_send_sw(SW_INCORRECT_P1_P2); + } - context.transactionBufferPointer = (uint8_t* ) buffer->ptr + dataOffset; - context.transactionDataRemaining = buffer->size - dataOffset; + context.transactionBufferPointer = (uint8_t *)buffer->ptr + dataOffset; + context.transactionDataRemaining = buffer->size - dataOffset; - transaction_parse(PARSE_MODE_TRUSTED_INPUT); + transaction_parse(PARSE_MODE_TRUSTED_INPUT); - if (context.transactionContext.transactionState == - TRANSACTION_PARSED) { + if (context.transactionContext.transactionState == TRANSACTION_PARSED) { - context.transactionContext.transactionState = - TRANSACTION_NONE; - if (!context.trustedInputProcessed) { - // Output was not found - return io_send_sw(SW_INCORRECT_DATA); - } + context.transactionContext.transactionState = TRANSACTION_NONE; + if (!context.trustedInputProcessed) { + // Output was not found + return io_send_sw(SW_INCORRECT_DATA); + } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, - NULL, 0, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32)) { - return io_send_sw(SW_TECHNICAL_PROBLEM); - } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, + NULL, 0, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32)) { + return io_send_sw(SW_TECHNICAL_PROBLEM); + } - // Otherwise prepare - cx_rng(G_io_apdu_buffer, 8); - G_io_apdu_buffer[0] = MAGIC_TRUSTED_INPUT; - G_io_apdu_buffer[1] = 0x00; - cx_hash_sha256(G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32, G_io_apdu_buffer + 4, 32); + // Otherwise prepare + cx_rng(G_io_apdu_buffer, 8); + G_io_apdu_buffer[0] = MAGIC_TRUSTED_INPUT; + G_io_apdu_buffer[1] = 0x00; + cx_hash_sha256(G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32, + G_io_apdu_buffer + 4, 32); - write_u32_le(G_io_apdu_buffer, 4 + 32, - context.transactionTargetInput); - memmove(G_io_apdu_buffer + 4 + 32 + 4, - context.transactionContext.transactionAmount, 8); + write_u32_le(G_io_apdu_buffer, 4 + 32, context.transactionTargetInput); + memmove(G_io_apdu_buffer + 4 + 32 + 4, + context.transactionContext.transactionAmount, 8); - cx_hmac_sha256((uint8_t *)g_nvram_data.bkp.trustedinput_key, - sizeof(g_nvram_data.bkp.trustedinput_key), G_io_apdu_buffer, - TRUSTED_INPUT_SIZE, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, 32); - context.outLength = TRUSTED_INPUT_TOTAL_SIZE; - } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + cx_hmac_sha256((uint8_t *)g_nvram_data.bkp.trustedinput_key, + sizeof(g_nvram_data.bkp.trustedinput_key), G_io_apdu_buffer, + TRUSTED_INPUT_SIZE, G_io_apdu_buffer + TRUSTED_INPUT_SIZE, + 32); + context.outLength = TRUSTED_INPUT_TOTAL_SIZE; + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } diff --git a/lib-app-bitcoin/handler/get_wallet_public_key.c b/lib-app-bitcoin/handler/get_wallet_public_key.c index d376bda6..d7edfb36 100644 --- a/lib-app-bitcoin/handler/get_wallet_public_key.c +++ b/lib-app-bitcoin/handler/get_wallet_public_key.c @@ -1,31 +1,31 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ +#include "apdu_constants.h" #include "context.h" #include "helpers.h" -#include "apdu_constants.h" #include "extensions.h" -#include "segwit_addr.h" #include "cashaddr.h" +#include "io.h" #include "lib_standard_app/read.h" +#include "segwit_addr.h" #include "swap.h" -#include "io.h" #define P1_NO_DISPLAY 0x00 #define P1_DISPLAY 0x01 @@ -36,151 +36,154 @@ #define P2_NATIVE_SEGWIT 0x02 #define P2_CASHADDR 0x03 -static int get_public_key_chain_code(const unsigned char* keyPath, size_t keyPath_len, unsigned char* publicKey, unsigned char* chainCode) { - uint8_t public_key[65]; - int keyLength = 0; - - if (get_public_key(keyPath, keyPath_len, public_key, chainCode)) { - return keyLength; - } - // Then encode it - compress_public_key_value(public_key); - keyLength = 33; +static int get_public_key_chain_code(const unsigned char *keyPath, + size_t keyPath_len, + unsigned char *publicKey, + unsigned char *chainCode) { + uint8_t public_key[65]; + int keyLength = 0; - memmove(publicKey, public_key, - sizeof(public_key)); + if (get_public_key(keyPath, keyPath_len, public_key, chainCode)) { return keyLength; -} - -WEAK unsigned short handler_get_wallet_public_key(buffer_t* buffer, uint8_t p1, uint8_t p2) { - unsigned char keyLength; - unsigned char chainCode[32]; - uint8_t is_derivation_path_unusual = 0; - - bool display = (p1 == P1_DISPLAY); - bool segwit = (p2 == P2_SEGWIT); - bool nativeSegwit = (p2 == P2_NATIVE_SEGWIT); - bool cashAddr = (p2 == P2_CASHADDR); - - if (display && G_called_from_swap) { - PRINTF("Refused INS when in SWAP mode\n"); - return io_send_sw(SW_INS_NOT_SUPPORTED); - } - - if (p1 != P1_NO_DISPLAY && p1 != P1_DISPLAY) { - PRINTF("Wrong P1 value\n"); - return io_send_sw(SW_INCORRECT_P1_P2); - } - - if (p2 == P2_NATIVE_SEGWIT && !COIN_NATIVE_SEGWIT_PREFIX) { - PRINTF("Wrong P2 value\n"); - return io_send_sw(SW_INCORRECT_P1_P2); - } - - if (p2 == P2_CASHADDR && COIN_KIND != COIN_KIND_BITCOIN_CASH) { - PRINTF("Wrong P2 value\n"); - return io_send_sw(SW_INCORRECT_P1_P2); - } - - if (p2 != P2_NATIVE_SEGWIT && p2 != P2_LEGACY && p2 != P2_SEGWIT && p2 != P2_CASHADDR) { - PRINTF("Wrong P2 value\n"); - return io_send_sw(SW_INCORRECT_P1_P2); - } - - if (buffer->size < 0x01) { - PRINTF("Wrong size\n"); - return io_send_sw(SW_INCORRECT_LENGTH); - } - - if (display) { - is_derivation_path_unusual = set_key_path_to_display(buffer->ptr); - } - - unsigned char bip44_enforced = enforce_bip44_coin_type(buffer->ptr, true); - - G_io_apdu_buffer[0] = 65; - keyLength = get_public_key_chain_code(buffer->ptr, MAX_BIP32_PATH_LENGTH, G_io_apdu_buffer + 1, chainCode); + } + // Then encode it + compress_public_key_value(public_key); + keyLength = 33; - if (keyLength == 0) { - return io_send_sw(SW_TECHNICAL_PROBLEM); - } + memmove(publicKey, public_key, sizeof(public_key)); + return keyLength; +} - if (cashAddr) { - uint8_t tmp[20]; - public_key_hash160(G_io_apdu_buffer + 1, // IN - keyLength, // INLEN - tmp); - keyLength = - cashaddr_encode(tmp, 20, G_io_apdu_buffer + 67, 50, CASHADDR_P2PKH); - } else if (!(segwit || nativeSegwit)) { - keyLength = public_key_to_encoded_base58( - G_io_apdu_buffer + 1, // IN - keyLength, // INLEN - G_io_apdu_buffer + 67, // OUT - 150, // MAXOUTLEN - COIN_P2PKH_VERSION, 0); +WEAK unsigned short handler_get_wallet_public_key(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + unsigned char keyLength; + unsigned char chainCode[32]; + uint8_t is_derivation_path_unusual = 0; + + bool display = (p1 == P1_DISPLAY); + bool segwit = (p2 == P2_SEGWIT); + bool nativeSegwit = (p2 == P2_NATIVE_SEGWIT); + bool cashAddr = (p2 == P2_CASHADDR); + + if (display && G_called_from_swap) { + PRINTF("Refused INS when in SWAP mode\n"); + return io_send_sw(SW_INS_NOT_SUPPORTED); + } + + if (p1 != P1_NO_DISPLAY && p1 != P1_DISPLAY) { + PRINTF("Wrong P1 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if (p2 == P2_NATIVE_SEGWIT && !COIN_NATIVE_SEGWIT_PREFIX) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if (p2 == P2_CASHADDR && COIN_KIND != COIN_KIND_BITCOIN_CASH) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if (p2 != P2_NATIVE_SEGWIT && p2 != P2_LEGACY && p2 != P2_SEGWIT && + p2 != P2_CASHADDR) { + PRINTF("Wrong P2 value\n"); + return io_send_sw(SW_INCORRECT_P1_P2); + } + + if (buffer->size < 0x01) { + PRINTF("Wrong size\n"); + return io_send_sw(SW_INCORRECT_LENGTH); + } + + if (display) { + is_derivation_path_unusual = set_key_path_to_display(buffer->ptr); + } + + unsigned char bip44_enforced = enforce_bip44_coin_type(buffer->ptr, true); + + G_io_apdu_buffer[0] = 65; + keyLength = get_public_key_chain_code(buffer->ptr, MAX_BIP32_PATH_LENGTH, + G_io_apdu_buffer + 1, chainCode); + + if (keyLength == 0) { + return io_send_sw(SW_TECHNICAL_PROBLEM); + } + + if (cashAddr) { + uint8_t tmp[20]; + public_key_hash160(G_io_apdu_buffer + 1, // IN + keyLength, // INLEN + tmp); + keyLength = + cashaddr_encode(tmp, 20, G_io_apdu_buffer + 67, 50, CASHADDR_P2PKH); + } else if (!(segwit || nativeSegwit)) { + keyLength = public_key_to_encoded_base58(G_io_apdu_buffer + 1, // IN + keyLength, // INLEN + G_io_apdu_buffer + 67, // OUT + 150, // MAXOUTLEN + COIN_P2PKH_VERSION, 0); + } else { + uint8_t tmp[22]; + tmp[0] = 0x00; + tmp[1] = 0x14; + public_key_hash160(G_io_apdu_buffer + 1, // IN + keyLength, // INLEN + tmp + 2 // OUT + ); + if (!nativeSegwit) { + keyLength = public_key_to_encoded_base58(tmp, // IN + 22, // INLEN + G_io_apdu_buffer + 67, // OUT + 150, // MAXOUTLEN + COIN_P2SH_VERSION, 0); } else { - uint8_t tmp[22]; - tmp[0] = 0x00; - tmp[1] = 0x14; - public_key_hash160(G_io_apdu_buffer + 1, // IN - keyLength, // INLEN - tmp + 2 // OUT - ); - if (!nativeSegwit) { - keyLength = public_key_to_encoded_base58( - tmp, // IN - 22, // INLEN - G_io_apdu_buffer + 67, // OUT - 150, // MAXOUTLEN - COIN_P2SH_VERSION, 0); - } else { - if (COIN_NATIVE_SEGWIT_PREFIX) { - keyLength = segwit_addr_encode( - (char *)(G_io_apdu_buffer + 67), - (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), 0, tmp + 2, 20); - if (keyLength == 1) { - keyLength = strlen((char *)(G_io_apdu_buffer + 67)); - } - } + if (COIN_NATIVE_SEGWIT_PREFIX) { + keyLength = segwit_addr_encode((char *)(G_io_apdu_buffer + 67), + (char *)PIC(COIN_NATIVE_SEGWIT_PREFIX), + 0, tmp + 2, 20); + if (keyLength == 1) { + keyLength = strlen((char *)(G_io_apdu_buffer + 67)); } + } } - G_io_apdu_buffer[66] = keyLength; - PRINTF("Length %d\n", keyLength); - // Restore for the full key component - G_io_apdu_buffer[1] = 0x04; - - // output chain code - memmove(G_io_apdu_buffer + 1 + 65 + 1 + keyLength, chainCode, - sizeof(chainCode)); - context.outLength = 1 + 65 + 1 + keyLength + sizeof(chainCode); - - // privacy : force display the address if the path isn't standard - // and could reveal another fork holdings according to BIP 44 rules - if (!display && !bip44_enforced) { - display = true; - } - - if (display) { - if (keyLength > 50) { - return io_send_sw(SW_INCORRECT_DATA); - } - // Hax, avoid wasting space - memmove(G_io_apdu_buffer + 200, G_io_apdu_buffer + 67, keyLength); - G_io_apdu_buffer[200 + keyLength] = '\0'; - display_public_key(is_derivation_path_unusual); - return 0; + } + G_io_apdu_buffer[66] = keyLength; + PRINTF("Length %d\n", keyLength); + // Restore for the full key component + G_io_apdu_buffer[1] = 0x04; + + // output chain code + memmove(G_io_apdu_buffer + 1 + 65 + 1 + keyLength, chainCode, + sizeof(chainCode)); + context.outLength = 1 + 65 + 1 + keyLength + sizeof(chainCode); + + // privacy : force display the address if the path isn't standard + // and could reveal another fork holdings according to BIP 44 rules + if (!display && !bip44_enforced) { + display = true; + } + + if (display) { + if (keyLength > 50) { + return io_send_sw(SW_INCORRECT_DATA); } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + // Hax, avoid wasting space + memmove(G_io_apdu_buffer + 200, G_io_apdu_buffer + 67, keyLength); + G_io_apdu_buffer[200 + keyLength] = '\0'; + display_public_key(is_derivation_path_unusual); + return 0; + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } int user_action_display(unsigned char confirming) { - // confirm and finish the apdu exchange //spaghetti - if (confirming) { - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + // confirm and finish the apdu exchange //spaghetti + if (confirming) { + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); - } else { - context.outLength = 0; - return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); - } + } else { + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } } diff --git a/lib-app-bitcoin/handler/hash_input_finalize_full.c b/lib-app-bitcoin/handler/hash_input_finalize_full.c index 8fc61a9d..4c34b104 100644 --- a/lib-app-bitcoin/handler/hash_input_finalize_full.c +++ b/lib-app-bitcoin/handler/hash_input_finalize_full.c @@ -1,35 +1,35 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ // TODO Trustlet, BAGL : process each output separately. // review nvm_write policy #include "crypto_helpers.h" +#include "io.h" #include "read.h" #include "swap.h" -#include "io.h" +#include "apdu_constants.h" +#include "be_operations.h" #include "context.h" -#include "helpers.h" #include "customizable_helpers.h" -#include "apdu_constants.h" #include "extensions.h" +#include "helpers.h" #include "ui.h" -#include "be_operations.h" #define FINALIZE_P1_MORE 0x00 #define FINALIZE_P1_LAST 0x80 @@ -41,621 +41,579 @@ #define FLAG_CHANGE_VALIDATED 0x80 void hash_input_finalize_full_reset(void) { - context.currentOutputOffset = 0; - context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - memset(context.totalOutputAmount, 0, - sizeof(context.totalOutputAmount)); - context.changeOutputFound = 0; + context.currentOutputOffset = 0; + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + memset(context.totalOutputAmount, 0, sizeof(context.totalOutputAmount)); + context.changeOutputFound = 0; } -static int check_output_displayable(bool* displayable) { - *displayable = true; - unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, - nullAmount = 1; +static int check_output_displayable(bool *displayable) { + *displayable = true; + unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, + nullAmount = 1; - for (j = 0; j < 8; j++) { - if (context.currentOutput[j] != 0) { - nullAmount = 0; - break; - } - } - if (!nullAmount) { - swap_bytes(amount, context.currentOutput, 8); - transaction_amount_add_be(context.totalOutputAmount, - context.totalOutputAmount, amount); + for (j = 0; j < 8; j++) { + if (context.currentOutput[j] != 0) { + nullAmount = 0; + break; } - isOpReturn = - output_script_is_op_return(context.currentOutput + 8); - isP2sh = output_script_is_p2sh(context.currentOutput + 8); - isNativeSegwit = output_script_is_native_witness( - context.currentOutput + 8); + } + if (!nullAmount) { + swap_bytes(amount, context.currentOutput, 8); + transaction_amount_add_be(context.totalOutputAmount, + context.totalOutputAmount, amount); + } + isOpReturn = output_script_is_op_return(context.currentOutput + 8); + isP2sh = output_script_is_p2sh(context.currentOutput + 8); + isNativeSegwit = output_script_is_native_witness(context.currentOutput + 8); #ifndef __clang_analyzer__ - unsigned char isOpCreate = - output_script_is_op_create(context.currentOutput + 8, - sizeof(context.currentOutput) - 8); - unsigned char isOpCall = - output_script_is_op_call(context.currentOutput + 8, - sizeof(context.currentOutput) - 8); - if (((COIN_KIND == COIN_KIND_HYDRA) && - !output_script_is_regular(context.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || - (!(COIN_KIND == COIN_KIND_HYDRA) && - !output_script_is_regular(context.currentOutput + 8) && - !isP2sh && !(nullAmount && isOpReturn))) { - PRINTF("Error : Unrecognized output script"); - return -1; - } + unsigned char isOpCreate = output_script_is_op_create( + context.currentOutput + 8, sizeof(context.currentOutput) - 8); + unsigned char isOpCall = output_script_is_op_call( + context.currentOutput + 8, sizeof(context.currentOutput) - 8); + if (((COIN_KIND == COIN_KIND_HYDRA) && + !output_script_is_regular(context.currentOutput + 8) && !isP2sh && + !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || + (!(COIN_KIND == COIN_KIND_HYDRA) && + !output_script_is_regular(context.currentOutput + 8) && !isP2sh && + !(nullAmount && isOpReturn))) { + PRINTF("Error : Unrecognized output script"); + return -1; + } #endif - if (context.tmpCtx.output.changeInitialized && !isOpReturn) { - bool changeFound = false; - unsigned char addressOffset = - (isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET - : isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH - : OUTPUT_SCRIPT_REGULAR_PRE_LENGTH); - if (!isP2sh && - memcmp(context.currentOutput + 8 + addressOffset, - context.tmpCtx.output.changeAddress, - 20) == 0) { - changeFound = true; - } else if (isP2sh && context.usingSegwit) { - unsigned char changeSegwit[22]; - changeSegwit[0] = 0x00; - changeSegwit[1] = 0x14; - memmove(changeSegwit + 2, - context.tmpCtx.output.changeAddress, 20); - public_key_hash160(changeSegwit, 22, changeSegwit); - if (memcmp(context.currentOutput + 8 + addressOffset, - changeSegwit, 20) == 0) { - if (COIN_FLAGS & FLAG_SEGWIT_CHANGE_SUPPORT) { - changeFound = true; - } else { - // Attempt to avoid fatal failures on Bitcoin Cash - PRINTF("Error : Non spendable Segwit change"); - return -1; - } - } - } - if (changeFound) { - if (context.changeOutputFound) { - PRINTF("Error : Multiple change output found"); - return -1; - } - context.changeOutputFound = true; - *displayable = false; + if (context.tmpCtx.output.changeInitialized && !isOpReturn) { + bool changeFound = false; + unsigned char addressOffset = + (isNativeSegwit ? OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET + : isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH + : OUTPUT_SCRIPT_REGULAR_PRE_LENGTH); + if (!isP2sh && memcmp(context.currentOutput + 8 + addressOffset, + context.tmpCtx.output.changeAddress, 20) == 0) { + changeFound = true; + } else if (isP2sh && context.usingSegwit) { + unsigned char changeSegwit[22]; + changeSegwit[0] = 0x00; + changeSegwit[1] = 0x14; + memmove(changeSegwit + 2, context.tmpCtx.output.changeAddress, 20); + public_key_hash160(changeSegwit, 22, changeSegwit); + if (memcmp(context.currentOutput + 8 + addressOffset, changeSegwit, 20) == + 0) { + if (COIN_FLAGS & FLAG_SEGWIT_CHANGE_SUPPORT) { + changeFound = true; + } else { + // Attempt to avoid fatal failures on Bitcoin Cash + PRINTF("Error : Non spendable Segwit change"); + return -1; } + } } - return 0; + if (changeFound) { + if (context.changeOutputFound) { + PRINTF("Error : Multiple change output found"); + return -1; + } + context.changeOutputFound = true; + *displayable = false; + } + } + return 0; } -int handle_output_state(unsigned int* processed) { - uint32_t discardSize = 0; - context.discardSize = 0; - *processed = 0; - switch (context.outputParsingState) { - case OUTPUT_PARSING_NUMBER_OUTPUTS: { - context.totalOutputs = 0; - if (context.currentOutputOffset < 1) { - break; - } - if (context.currentOutput[0] < 0xFD) { - context.totalOutputs = context.remainingOutputs = - context.currentOutput[0]; - discardSize = 1; - context.outputParsingState = OUTPUT_PARSING_OUTPUT; - *processed = 1; - break; - } - if (context.currentOutput[0] == 0xFD) { - if (context.currentOutputOffset < 3) { - break; - } - context.totalOutputs = context.remainingOutputs = - (context.currentOutput[2] << 8) | - context.currentOutput[1]; - discardSize = 3; - context.outputParsingState = OUTPUT_PARSING_OUTPUT; - *processed = 1; - break; - } else if (context.currentOutput[0] == 0xFE) { - if (context.currentOutputOffset < 5) { - break; - } - context.totalOutputs = context.remainingOutputs = - read_u32_le(context.currentOutput, 1); - discardSize = 5; - context.outputParsingState = OUTPUT_PARSING_OUTPUT; - *processed = 1; - break; - } else { - return -1; - } - } break; - - case OUTPUT_PARSING_OUTPUT: { - unsigned int scriptSize = 0; - if (context.currentOutputOffset < 9) { - break; - } - if (context.currentOutput[8] < 0xFD) { - scriptSize = context.currentOutput[8]; - discardSize = 1; - } else if (context.currentOutput[8] == 0xFD) { - if (context.currentOutputOffset < 9 + 2) { - break; - } - scriptSize = read_u32_le(context.currentOutput, 9); - discardSize = 3; - } else { - // Unrealistically large script - return -1; - } - if (context.currentOutputOffset < - 8 + discardSize + scriptSize) { - discardSize = 0; - break; - } +int handle_output_state(unsigned int *processed) { + uint32_t discardSize = 0; + context.discardSize = 0; + *processed = 0; + switch (context.outputParsingState) { + case OUTPUT_PARSING_NUMBER_OUTPUTS: { + context.totalOutputs = 0; + if (context.currentOutputOffset < 1) { + break; + } + if (context.currentOutput[0] < 0xFD) { + context.totalOutputs = context.remainingOutputs = + context.currentOutput[0]; + discardSize = 1; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } + if (context.currentOutput[0] == 0xFD) { + if (context.currentOutputOffset < 3) { + break; + } + context.totalOutputs = context.remainingOutputs = + (context.currentOutput[2] << 8) | context.currentOutput[1]; + discardSize = 3; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } else if (context.currentOutput[0] == 0xFE) { + if (context.currentOutputOffset < 5) { + break; + } + context.totalOutputs = context.remainingOutputs = + read_u32_le(context.currentOutput, 1); + discardSize = 5; + context.outputParsingState = OUTPUT_PARSING_OUTPUT; + *processed = 1; + break; + } else { + return -1; + } + } break; - *processed = 1; + case OUTPUT_PARSING_OUTPUT: { + unsigned int scriptSize = 0; + if (context.currentOutputOffset < 9) { + break; + } + if (context.currentOutput[8] < 0xFD) { + scriptSize = context.currentOutput[8]; + discardSize = 1; + } else if (context.currentOutput[8] == 0xFD) { + if (context.currentOutputOffset < 9 + 2) { + break; + } + scriptSize = read_u32_le(context.currentOutput, 9); + discardSize = 3; + } else { + // Unrealistically large script + return -1; + } + if (context.currentOutputOffset < 8 + discardSize + scriptSize) { + discardSize = 0; + break; + } - discardSize += 8 + scriptSize; + *processed = 1; - bool displayable; - if (check_output_displayable(&displayable)) { - return -1; - } + discardSize += 8 + scriptSize; - if (displayable) { - // The output can be processed by the UI + bool displayable; + if (check_output_displayable(&displayable)) { + return -1; + } - context.discardSize = discardSize; - discardSize = 0; - *processed = 2; - } else { - context.remainingOutputs--; - } - } break; + if (displayable) { + // The output can be processed by the UI - default: - return -1; + context.discardSize = discardSize; + discardSize = 0; + *processed = 2; + } else { + context.remainingOutputs--; } + } break; - if (discardSize != 0) { - memmove(context.currentOutput, - context.currentOutput + discardSize, - context.currentOutputOffset - discardSize); - context.currentOutputOffset -= discardSize; - } + default: + return -1; + } - return 0; + if (discardSize != 0) { + memmove(context.currentOutput, context.currentOutput + discardSize, + context.currentOutputOffset - discardSize); + context.currentOutputOffset -= discardSize; + } + + return 0; } // out should be 32 bytes, even only 20 bytes is significant for output -int get_pubkey_hash160(unsigned char* keyPath, size_t keyPath_len, unsigned char* out) { - cx_ecfp_public_key_t public_key; - if (get_public_key(keyPath, keyPath_len, public_key.W, NULL)) { - return -1; - } - compress_public_key_value(public_key.W); - - public_key_hash160( - public_key.W, // IN - 33, // INLEN - out // OUT - ); - return 0; +int get_pubkey_hash160(unsigned char *keyPath, size_t keyPath_len, + unsigned char *out) { + cx_ecfp_public_key_t public_key; + if (get_public_key(keyPath, keyPath_len, public_key.W, NULL)) { + return -1; + } + compress_public_key_value(public_key.W); + + public_key_hash160(public_key.W, // IN + 33, // INLEN + out // OUT + ); + return 0; } -static unsigned short hash_input_finalize_full_internal(transaction_summary_t *transactionSummary, buffer_t* buffer, uint8_t p1, uint8_t p2, bool *async) { - unsigned char authorizationHash[32]; - unsigned short sw = SW_OK; - unsigned char *target = G_io_apdu_buffer; - unsigned char hashOffset = 0; - - (void) p2; - - if ((p1 != FINALIZE_P1_MORE) && (p1 != FINALIZE_P1_LAST) && - (p1 != FINALIZE_P1_CHANGEINFO)) { - return SW_INCORRECT_P1_P2; - } - - // See if there is a hashing offset - if (context.usingSegwit && - (context.tmpCtx.output.multipleOutput == 0)) { - unsigned char firstByte = buffer->ptr[0]; - if (firstByte < 0xfd) { - hashOffset = 1; - } else if (firstByte == 0xfd) { - hashOffset = 3; - } else if (firstByte == 0xfe) { - hashOffset = 5; - } +static unsigned short +hash_input_finalize_full_internal(transaction_summary_t *transactionSummary, + buffer_t *buffer, uint8_t p1, uint8_t p2, + bool *async) { + unsigned char authorizationHash[32]; + unsigned short sw = SW_OK; + unsigned char *target = G_io_apdu_buffer; + unsigned char hashOffset = 0; + + (void)p2; + + if ((p1 != FINALIZE_P1_MORE) && (p1 != FINALIZE_P1_LAST) && + (p1 != FINALIZE_P1_CHANGEINFO)) { + return SW_INCORRECT_P1_P2; + } + + // See if there is a hashing offset + if (context.usingSegwit && (context.tmpCtx.output.multipleOutput == 0)) { + unsigned char firstByte = buffer->ptr[0]; + if (firstByte < 0xfd) { + hashOffset = 1; + } else if (firstByte == 0xfd) { + hashOffset = 3; + } else if (firstByte == 0xfe) { + hashOffset = 5; + } + } + + // Check state + if (context.transactionContext.transactionState != + TRANSACTION_PRESIGN_READY) { + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; + } + + if (p1 == FINALIZE_P1_CHANGEINFO) { + if (!context.transactionContext.firstSigned) { + // Already validated, should be prevented on the client side + return SW_OK; + } + if (!context.tmpCtx.output.changeAccepted) { + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; } + memset(transactionSummary, 0, sizeof(transaction_summary_t)); + if (buffer->ptr[0] == 0x00) { + // Called with no change path, abort, should be prevented on + // the client side + return SW_OK; + } + memmove(transactionSummary->keyPath, buffer->ptr, MAX_BIP32_PATH_LENGTH); + + if (get_pubkey_hash160(transactionSummary->keyPath, + sizeof(transactionSummary->keyPath), + context.tmpCtx.output.changeAddress)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Change address = %.*H\n", 20, context.tmpCtx.output.changeAddress); + + context.tmpCtx.output.changeInitialized = 1; + context.tmpCtx.output.changeAccepted = 0; - // Check state - if (context.transactionContext.transactionState != - TRANSACTION_PRESIGN_READY) { + // if the bip44 change path provided is not canonical or its index are + // unsual, ask for user approval + if (bip44_derivation_guard(transactionSummary->keyPath, true)) { + if (G_called_from_swap) { + PRINTF("In swap mode only standart path is allowed\n"); sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; goto discardTransaction; + } + *async = true; + context.outputParsingState = BIP44_CHANGE_PATH_VALIDATION; + request_change_path_approval(transactionSummary->keyPath); } - if (p1 == FINALIZE_P1_CHANGEINFO) { - if (!context.transactionContext.firstSigned) { - // Already validated, should be prevented on the client side - return SW_OK; - } - if (!context.tmpCtx.output.changeAccepted) { - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - memset(transactionSummary, 0, - sizeof(transaction_summary_t)); - if (buffer->ptr[0] == 0x00) { - // Called with no change path, abort, should be prevented on - // the client side - return SW_OK; - } - memmove(transactionSummary->keyPath, - buffer->ptr, - MAX_BIP32_PATH_LENGTH); + return SW_OK; + } - if (get_pubkey_hash160(transactionSummary->keyPath, sizeof(transactionSummary->keyPath), context.tmpCtx.output.changeAddress)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - PRINTF("Change address = %.*H\n", 20, context.tmpCtx.output.changeAddress); - - context.tmpCtx.output.changeInitialized = 1; - context.tmpCtx.output.changeAccepted = 0; - - // if the bip44 change path provided is not canonical or its index are unsual, ask for user approval - if(bip44_derivation_guard(transactionSummary->keyPath, true)) { - if (G_called_from_swap) { - PRINTF("In swap mode only standart path is allowed\n"); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - *async = true; - context.outputParsingState = BIP44_CHANGE_PATH_VALIDATION; - request_change_path_approval(transactionSummary->keyPath); - } - - return SW_OK; + // Always update the transaction & authorization hashes with the + // given data + // For SegWit, this has been reset to hold hashOutputs + if (!context.segwitParsedOnce) { + if ((int)(buffer->size - hashOffset) < 0) { + sw = SW_INCORRECT_DATA; + goto discardTransaction; } - - // Always update the transaction & authorization hashes with the - // given data - // For SegWit, this has been reset to hold hashOutputs - if (!context.segwitParsedOnce) { - if ((int)(buffer->size - hashOffset) < 0) { - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, buffer->ptr + hashOffset, buffer->size - hashOffset, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } - else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", buffer->size - hashOffset, buffer->ptr + hashOffset); - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - buffer->ptr + hashOffset, - buffer->size - hashOffset, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + buffer->ptr + hashOffset, buffer->size - hashOffset, + NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } else { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", buffer->size - hashOffset, + buffer->ptr + hashOffset); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + hashOffset, buffer->size - hashOffset, + NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + } + + if (context.transactionContext.firstSigned) { + if ((context.currentOutputOffset + buffer->size) > + sizeof(context.currentOutput)) { + PRINTF("Output is too long to be checked\n"); + sw = SW_INCORRECT_DATA; + goto discardTransaction; + } + memmove(context.currentOutput + context.currentOutputOffset, buffer->ptr, + buffer->size); + context.currentOutputOffset += buffer->size; + + unsigned int processed = 1; + while (processed == 1) { + if (handle_output_state(&processed)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } } - if (context.transactionContext.firstSigned) { - if ((context.currentOutputOffset + buffer->size) > - sizeof(context.currentOutput)) { - PRINTF("Output is too long to be checked\n"); - sw = SW_INCORRECT_DATA; - goto discardTransaction; - } - memmove(context.currentOutput + - context.currentOutputOffset, - buffer->ptr, buffer->size); - context.currentOutputOffset += buffer->size; - - unsigned int processed = 1; - while (processed == 1) { - if (handle_output_state(&processed)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } - - if (processed == 2) { - *async = true; - } - // Finalize the TX if necessary - - if ((context.remainingOutputs == 0) && - (!*async)) { - *async = true; - context.outputParsingState = - OUTPUT_FINALIZE_TX; - } + if (processed == 2) { + *async = true; } + // Finalize the TX if necessary - if (p1 == FINALIZE_P1_MORE) { - if (!context.usingSegwit) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); - if (cx_hash_no_throw( - &context.transactionHashAuthorization.header, - 0, buffer->ptr, buffer->size, - NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } - G_io_apdu_buffer[0] = 0x00; - context.outLength = 1; - context.tmpCtx.output.multipleOutput = 1; - return SW_OK; + if ((context.remainingOutputs == 0) && (!*async)) { + *async = true; + context.outputParsingState = OUTPUT_FINALIZE_TX; } + } + if (p1 == FINALIZE_P1_MORE) { if (!context.usingSegwit) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); - if (cx_hash_no_throw(&context.transactionHashAuthorization.header, - CX_LAST, buffer->ptr, - buffer->size, authorizationHash, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, 0, + buffer->ptr, buffer->size, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } } - - if (context.usingSegwit) { - if (!context.segwitParsedOnce) { - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, context.segwit.cache.hashedOutputs, 0, context.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } - else { - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, - CX_LAST, - context.segwit.cache.hashedOutputs, 0, - context.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, - CX_LAST, - context.segwit.cache.hashedOutputs, - sizeof(context.segwit.cache.hashedOutputs), - context.segwit.cache.hashedOutputs, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - } - PRINTF("hashOutputs\n%.*H\n",32,context.segwit.cache.hashedOutputs); - if (cx_hash_no_throw( - &context.transactionHashAuthorization.header, - CX_LAST, G_io_apdu_buffer, 0, authorizationHash, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); - } else { - if (cx_hash_no_throw( - &context.transactionHashAuthorization.header, - CX_LAST, - (unsigned char *)&context.segwit.cache, - sizeof(context.segwit.cache), - authorizationHash, 32)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discardTransaction; - } - PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); - } + G_io_apdu_buffer[0] = 0x00; + context.outLength = 1; + context.tmpCtx.output.multipleOutput = 1; + return SW_OK; + } + + if (!context.usingSegwit) { + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", buffer->size, buffer->ptr); + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, CX_LAST, + buffer->ptr, buffer->size, authorizationHash, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; } + } - if (context.transactionContext.firstSigned) { - if (!context.tmpCtx.output.changeInitialized) { - memset(transactionSummary, 0, - sizeof(transaction_summary_t)); - } - - transactionSummary->payToAddressVersion = COIN_P2PKH_VERSION; - transactionSummary->payToScriptHashVersion = COIN_P2SH_VERSION; + if (context.usingSegwit) { + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, + CX_LAST, context.segwit.cache.hashedOutputs, 0, + context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } else { + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, context.segwit.cache.hashedOutputs, 0, + context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, context.segwit.cache.hashedOutputs, + sizeof(context.segwit.cache.hashedOutputs), + context.segwit.cache.hashedOutputs, 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + } + PRINTF("hashOutputs\n%.*H\n", 32, context.segwit.cache.hashedOutputs); + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, + CX_LAST, G_io_apdu_buffer, 0, authorizationHash, + 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); + } else { + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, + CX_LAST, (unsigned char *)&context.segwit.cache, + sizeof(context.segwit.cache), authorizationHash, + 32)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discardTransaction; + } + PRINTF("Auth Hash:\n%.*H\n", 32, authorizationHash); + } + } - // Generate new nonce + if (context.transactionContext.firstSigned) { + if (!context.tmpCtx.output.changeInitialized) { + memset(transactionSummary, 0, sizeof(transaction_summary_t)); + } - cx_rng(transactionSummary->transactionNonce, 8); + transactionSummary->payToAddressVersion = COIN_P2PKH_VERSION; + transactionSummary->payToScriptHashVersion = COIN_P2SH_VERSION; + + // Generate new nonce + + cx_rng(transactionSummary->transactionNonce, 8); + } + + G_io_apdu_buffer[0] = 0x00; + target++; + + *target = 0x00; + target++; + + context.outLength = (target - G_io_apdu_buffer); + + // Check that the input being signed is part of the same + // transaction, otherwise abort + // (this is done to keep the transaction counter limit per session + // synchronized) + if (context.transactionContext.firstSigned) { + memmove(transactionSummary->authorizationHash, authorizationHash, + sizeof(transactionSummary->authorizationHash)); + return SW_OK; + } else { + if (os_secure_memcmp(authorizationHash, + transactionSummary->authorizationHash, + sizeof(transactionSummary->authorizationHash))) { + PRINTF("Authorization hash not matching, aborting\n"); + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + goto discardTransaction; } - G_io_apdu_buffer[0] = 0x00; - target++; - - *target = 0x00; - target++; - - context.outLength = (target - G_io_apdu_buffer); - - // Check that the input being signed is part of the same - // transaction, otherwise abort - // (this is done to keep the transaction counter limit per session - // synchronized) - if (context.transactionContext.firstSigned) { - memmove(transactionSummary->authorizationHash, - authorizationHash, - sizeof(transactionSummary->authorizationHash)); - return SW_OK; + if (context.usingSegwit && !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; } else { - if (os_secure_memcmp( - authorizationHash, - transactionSummary->authorizationHash, - sizeof(transactionSummary->authorizationHash))) { - PRINTF("Authorization hash not matching, aborting\n"); - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - goto discardTransaction; - } - - if (context.usingSegwit && - !context.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - hash_input_finalize_full_reset(); - return SW_OK; + context.transactionContext.transactionState = TRANSACTION_SIGN_READY; } - -discardTransaction: hash_input_finalize_full_reset(); - ui_transaction_error(); - context.transactionContext.transactionState = - TRANSACTION_NONE; - context.outLength = 0; + return SW_OK; + } - memmove(G_io_apdu_buffer, context.currentOutput, - context.currentOutputOffset); - context.outLength = context.currentOutputOffset; - return sw; +discardTransaction: + hash_input_finalize_full_reset(); + ui_transaction_error(); + context.transactionContext.transactionState = TRANSACTION_NONE; + context.outLength = 0; + + memmove(G_io_apdu_buffer, context.currentOutput, context.currentOutputOffset); + context.outLength = context.currentOutputOffset; + return sw; } -unsigned short handler_hash_input_finalize_full(buffer_t* buffer, uint8_t p1, uint8_t p2) { - bool is_async = false; - PRINTF("state=%d\n", context.outputParsingState); - unsigned short sw = hash_input_finalize_full_internal( - &context.transactionSummary, buffer, p1, p2, &is_async); - - if (is_async) { - // if the UI reject the processing of the request, then reply - // immediately - int status; - if(context.outputParsingState == BIP44_CHANGE_PATH_VALIDATION) { - context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; - return 0; - } - else if (context.outputParsingState == OUTPUT_FINALIZE_TX) { - status = finalize_tx(); - } - else { - status = confirm_single_output(); - } - if (status == 0) { - ui_transaction_error(); - context.transactionContext.transactionState = - TRANSACTION_NONE; - context.outLength = 0; - sw = SW_INCORRECT_DATA; - return io_send_sw(sw); - } - else if (status == 2) { - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); - } - return 0; +unsigned short handler_hash_input_finalize_full(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + bool is_async = false; + PRINTF("state=%d\n", context.outputParsingState); + unsigned short sw = hash_input_finalize_full_internal( + &context.transactionSummary, buffer, p1, p2, &is_async); + + if (is_async) { + // if the UI reject the processing of the request, then reply + // immediately + int status; + if (context.outputParsingState == BIP44_CHANGE_PATH_VALIDATION) { + context.outputParsingState = OUTPUT_PARSING_NUMBER_OUTPUTS; + return 0; + } else if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + status = finalize_tx(); + } else { + status = confirm_single_output(); } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + if (status == 0) { + ui_transaction_error(); + context.transactionContext.transactionState = TRANSACTION_NONE; + context.outLength = 0; + sw = SW_INCORRECT_DATA; + return io_send_sw(sw); + } else if (status == 2) { + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, + SW_OK); + } + return 0; + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); } unsigned char user_action(unsigned char confirming) { - unsigned short sw = SW_OK; - - // confirm and finish the apdu exchange //spaghetti - - if (confirming) { - // Check if all inputs have been confirmed + unsigned short sw = SW_OK; - if (context.outputParsingState == - OUTPUT_PARSING_OUTPUT) { - context.remainingOutputs--; - } + // confirm and finish the apdu exchange //spaghetti - while (context.remainingOutputs != 0) { - memmove(context.currentOutput, - context.currentOutput + - context.discardSize, - context.currentOutputOffset - - context.discardSize); - context.currentOutputOffset -= - context.discardSize; - unsigned int processed = 1; - while (processed == 1) { - if (handle_output_state(&processed)) { - context.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_INCORRECT_DATA; - break; - } - } - - if (processed == 2) { - if (!confirm_single_output()) { - context.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_INCORRECT_DATA; - break; - } else { - // Let the UI play - return 1; - } - } else { - // Out of data to process, wait for the next call - break; - } - } + if (confirming) { + // Check if all inputs have been confirmed - if ((context.outputParsingState == - OUTPUT_PARSING_OUTPUT) && - (context.remainingOutputs == 0)) { - context.outputParsingState = OUTPUT_FINALIZE_TX; - if (!finalize_tx()) { - context.outputParsingState = - OUTPUT_PARSING_NONE; - context.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_INCORRECT_DATA; - } else { - // Let the UI play - return 1; - } - } + if (context.outputParsingState == OUTPUT_PARSING_OUTPUT) { + context.remainingOutputs--; + } - if (context.outputParsingState == - OUTPUT_FINALIZE_TX) { - context.transactionContext.firstSigned = 0; - - if (context.usingSegwit && - !context.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } + while (context.remainingOutputs != 0) { + memmove(context.currentOutput, + context.currentOutput + context.discardSize, + context.currentOutputOffset - context.discardSize); + context.currentOutputOffset -= context.discardSize; + unsigned int processed = 1; + while (processed == 1) { + if (handle_output_state(&processed)) { + context.transactionContext.transactionState = TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + break; + } + } + + if (processed == 2) { + if (!confirm_single_output()) { + context.transactionContext.transactionState = TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + break; + } else { + // Let the UI play + return 1; } - } else { - // Discard transaction - context.transactionContext.transactionState = - TRANSACTION_NONE; - sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; - context.outLength = 0; + } else { + // Out of data to process, wait for the next call + break; + } } - if ((context.outputParsingState == OUTPUT_FINALIZE_TX) || - (sw != SW_OK)) { + if ((context.outputParsingState == OUTPUT_PARSING_OUTPUT) && + (context.remainingOutputs == 0)) { + context.outputParsingState = OUTPUT_FINALIZE_TX; + if (!finalize_tx()) { + context.outputParsingState = OUTPUT_PARSING_NONE; + context.transactionContext.transactionState = TRANSACTION_NONE; + sw = SW_INCORRECT_DATA; + } else { + // Let the UI play + return 1; + } + } - // we've finished the processing of the input - hash_input_finalize_full_reset(); + if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = TRANSACTION_SIGN_READY; + } } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + } else { + // Discard transaction + context.transactionContext.transactionState = TRANSACTION_NONE; + sw = SW_CONDITIONS_OF_USE_NOT_SATISFIED; + context.outLength = 0; + } + + if ((context.outputParsingState == OUTPUT_FINALIZE_TX) || (sw != SW_OK)) { + // we've finished the processing of the input + hash_input_finalize_full_reset(); + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); } diff --git a/lib-app-bitcoin/handler/hash_input_start.c b/lib-app-bitcoin/handler/hash_input_start.c index 274418a9..787f0bbd 100644 --- a/lib-app-bitcoin/handler/hash_input_start.c +++ b/lib-app-bitcoin/handler/hash_input_start.c @@ -1,27 +1,27 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "io.h" -#include "context.h" -#include "transaction.h" #include "apdu_constants.h" +#include "context.h" #include "extensions.h" #include "swap.h" +#include "transaction.h" #define P1_FIRST 0x00 #define P1_NEXT 0x80 @@ -32,105 +32,92 @@ #define P2_NEW_SEGWIT_SAPLING 0x05 #define P2_CONTINUE 0x80 -#define IS_INPUT() \ - (buffer->size - 1 > 8 \ - && buffer->size - 1 <= TRUSTED_INPUT_TOTAL_SIZE + 2 \ - && buffer->ptr[0] <= 0x02) \ +#define IS_INPUT() \ + (buffer->size - 1 > 8 && buffer->size - 1 <= TRUSTED_INPUT_TOTAL_SIZE + 2 && \ + buffer->ptr[0] <= 0x02) -#define IS_INPUT_TRUSTED() \ - (buffer->ptr[0] == 0x01 \ - && buffer->ptr[1] == TRUSTED_INPUT_TOTAL_SIZE \ - && buffer->ptr[2] == MAGIC_TRUSTED_INPUT \ - && buffer->ptr[3] == 0x00) +#define IS_INPUT_TRUSTED() \ + (buffer->ptr[0] == 0x01 && buffer->ptr[1] == TRUSTED_INPUT_TOTAL_SIZE && \ + buffer->ptr[2] == MAGIC_TRUSTED_INPUT && buffer->ptr[3] == 0x00) -WEAK unsigned short handler_hash_input_start(buffer_t* buffer, uint8_t p1, uint8_t p2) { - if (p1 == P1_FIRST) { - // Initialize - context.transactionContext.transactionState = - TRANSACTION_NONE; - context.transactionHashOption = TRANSACTION_HASH_BOTH; - } else if (p1 != P1_NEXT) { - return io_send_sw(SW_INCORRECT_P1_P2); - } +WEAK unsigned short handler_hash_input_start(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + if (p1 == P1_FIRST) { + // Initialize + context.transactionContext.transactionState = TRANSACTION_NONE; + context.transactionHashOption = TRANSACTION_HASH_BOTH; + } else if (p1 != P1_NEXT) { + return io_send_sw(SW_INCORRECT_P1_P2); + } - if ((p2 == P2_NEW) || - (p2 == P2_NEW_SEGWIT) || - (p2 == P2_NEW_SEGWIT_CASHADDR) || - (p2 == P2_NEW_SEGWIT_OVERWINTER) || - (p2 == P2_NEW_SEGWIT_SAPLING)) { - if (p1 == P1_FIRST) { - unsigned char usingSegwit = - (p2 == P2_NEW_SEGWIT) || - (p2 == P2_NEW_SEGWIT_CASHADDR) || - (p2 == P2_NEW_SEGWIT_OVERWINTER) || - (p2 == P2_NEW_SEGWIT_SAPLING); - // Master transaction reset - context.transactionContext.firstSigned = 1; - context.transactionContext.consumeP2SH = 0; - context.transactionContext.relaxed = 0; - context.usingSegwit = usingSegwit; + if ((p2 == P2_NEW) || (p2 == P2_NEW_SEGWIT) || + (p2 == P2_NEW_SEGWIT_CASHADDR) || (p2 == P2_NEW_SEGWIT_OVERWINTER) || + (p2 == P2_NEW_SEGWIT_SAPLING)) { + if (p1 == P1_FIRST) { + unsigned char usingSegwit = + (p2 == P2_NEW_SEGWIT) || (p2 == P2_NEW_SEGWIT_CASHADDR) || + (p2 == P2_NEW_SEGWIT_OVERWINTER) || (p2 == P2_NEW_SEGWIT_SAPLING); + // Master transaction reset + context.transactionContext.firstSigned = 1; + context.transactionContext.consumeP2SH = 0; + context.transactionContext.relaxed = 0; + context.usingSegwit = usingSegwit; - if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { - unsigned char usingCashAddr = (p2 == P2_NEW_SEGWIT_CASHADDR); - context.usingCashAddr = usingCashAddr; - } - else { - context.usingCashAddr = 0; - } + if (COIN_KIND == COIN_KIND_BITCOIN_CASH) { + unsigned char usingCashAddr = (p2 == P2_NEW_SEGWIT_CASHADDR); + context.usingCashAddr = usingCashAddr; + } else { + context.usingCashAddr = 0; + } - context.usingOverwinter = 0; - if ((COIN_KIND == COIN_KIND_ZCASH) || (COIN_KIND == COIN_KIND_KOMODO) || (COIN_KIND == COIN_KIND_ZCLASSIC) || (COIN_KIND == COIN_KIND_RESISTANCE)) { - if (p2 == P2_NEW_SEGWIT_OVERWINTER) { - context.usingOverwinter = ZCASH_USING_OVERWINTER; - } - else - if (p2 == P2_NEW_SEGWIT_SAPLING) { - context.usingOverwinter = ZCASH_USING_OVERWINTER_SAPLING; - } - } - context.overwinterSignReady = 0; - context.segwitParsedOnce = 0; - // Initialize for screen pairing - memset(&context.tmpCtx.output, 0, - sizeof(context.tmpCtx.output)); - context.tmpCtx.output.changeAccepted = 1; - // Reset segwitWarningSeen flag to prevent displaying the warning for each - // segwit input when coontinuing from a previous session (P2=0x80) - context.segwitWarningSeen = 0; + context.usingOverwinter = 0; + if ((COIN_KIND == COIN_KIND_ZCASH) || (COIN_KIND == COIN_KIND_KOMODO) || + (COIN_KIND == COIN_KIND_ZCLASSIC) || + (COIN_KIND == COIN_KIND_RESISTANCE)) { + if (p2 == P2_NEW_SEGWIT_OVERWINTER) { + context.usingOverwinter = ZCASH_USING_OVERWINTER; + } else if (p2 == P2_NEW_SEGWIT_SAPLING) { + context.usingOverwinter = ZCASH_USING_OVERWINTER_SAPLING; } - } else if (p2 != P2_CONTINUE) { - return io_send_sw(SW_INCORRECT_P1_P2); + } + context.overwinterSignReady = 0; + context.segwitParsedOnce = 0; + // Initialize for screen pairing + memset(&context.tmpCtx.output, 0, sizeof(context.tmpCtx.output)); + context.tmpCtx.output.changeAccepted = 1; + // Reset segwitWarningSeen flag to prevent displaying the warning for each + // segwit input when coontinuing from a previous session (P2=0x80) + context.segwitWarningSeen = 0; } + } else if (p2 != P2_CONTINUE) { + return io_send_sw(SW_INCORRECT_P1_P2); + } - // In segwit mode, warn user one time only to update its client wallet... - if (context.usingSegwit - && !context.segwitWarningSeen - && (p1 == P1_NEXT) - && (p2 != P2_CONTINUE) - // ...if input is not passed as a TrustedInput - && IS_INPUT() - && !IS_INPUT_TRUSTED()) - { - if(G_called_from_swap){ - /* There is no point in displaying a warning when the app is signing - in silent mode, as its UI is hidden behind the exchange app*/ - return io_send_sw(SW_SWAP_WITHOUT_TRUSTED_INPUTS); - } - context.segwitWarningSeen = 1; - request_segwit_input_approval(); - // Start parsing of the 1st chunk - context.transactionBufferPointer = (uint8_t*) buffer->ptr; - context.transactionDataRemaining = buffer->size; - - transaction_parse(PARSE_MODE_SIGNATURE); - return 0; + // In segwit mode, warn user one time only to update its client wallet... + if (context.usingSegwit && !context.segwitWarningSeen && (p1 == P1_NEXT) && + (p2 != P2_CONTINUE) + // ...if input is not passed as a TrustedInput + && IS_INPUT() && !IS_INPUT_TRUSTED()) { + if (G_called_from_swap) { + /* There is no point in displaying a warning when the app is signing + in silent mode, as its UI is hidden behind the exchange app*/ + return io_send_sw(SW_SWAP_WITHOUT_TRUSTED_INPUTS); } - + context.segwitWarningSeen = 1; + request_segwit_input_approval(); // Start parsing of the 1st chunk - context.transactionBufferPointer = (uint8_t*) buffer->ptr; + context.transactionBufferPointer = (uint8_t *)buffer->ptr; context.transactionDataRemaining = buffer->size; transaction_parse(PARSE_MODE_SIGNATURE); + return 0; + } + + // Start parsing of the 1st chunk + context.transactionBufferPointer = (uint8_t *)buffer->ptr; + context.transactionDataRemaining = buffer->size; + + transaction_parse(PARSE_MODE_SIGNATURE); - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); } diff --git a/lib-app-bitcoin/handler/hash_sign.c b/lib-app-bitcoin/handler/hash_sign.c index c4496660..50b74e33 100644 --- a/lib-app-bitcoin/handler/hash_sign.c +++ b/lib-app-bitcoin/handler/hash_sign.c @@ -1,192 +1,191 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ -#include "macros.h" #include "io.h" #include "ledger_assert.h" +#include "macros.h" #include "read.h" -#include "write.h" #include "swap.h" +#include "write.h" -#include "context.h" -#include "helpers.h" #include "apdu_constants.h" -#include "extensions.h" +#include "context.h" #include "display_variables.h" +#include "extensions.h" +#include "helpers.h" #include "ui.h" #define SIGHASH_ALL 0x01 #ifndef COIN_FORKID #define COIN_FORKID 0 -#endif +#endif -WEAK unsigned short handler_hash_sign(buffer_t* buffer, uint8_t p1, uint8_t p2) { - unsigned long int lockTime; - uint32_t sighashType; - unsigned char dataBuffer[8]; - unsigned char authorizationLength; - unsigned char *parameters = (uint8_t*)buffer->ptr; +WEAK unsigned short handler_hash_sign(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + unsigned long int lockTime; + uint32_t sighashType; + unsigned char dataBuffer[8]; + unsigned char authorizationLength; + unsigned char *parameters = (uint8_t *)buffer->ptr; - if ((p1 != 0) || (p2 != 0)) { - return io_send_sw(SW_INCORRECT_P1_P2); - } + if ((p1 != 0) || (p2 != 0)) { + return io_send_sw(SW_INCORRECT_P1_P2); + } #define HASH_LENGTH 1 + 1 + 4 + 1 - if (buffer->size < HASH_LENGTH) { - return io_send_sw(SW_INCORRECT_LENGTH); - } - - // Zcash special - store parameters for later - - if ((context.usingOverwinter) && - (!context.overwinterSignReady) && - (context.segwitParsedOnce) && - (context.transactionContext.transactionState == TRANSACTION_NONE)) { - unsigned long int expiryHeight; - parameters += (4 * buffer->ptr[0]) + 1; - authorizationLength = *(parameters++); - parameters += authorizationLength; - lockTime = read_u32_be(parameters, 0); - parameters += 4; - sighashType = *(parameters++); - expiryHeight = read_u32_be(parameters, 0); - write_u32_le(context.nLockTime, 0, lockTime); - write_u32_le(context.sigHashType, 0, sighashType); - write_u32_le(context.nExpiryHeight, 0, expiryHeight); - context.overwinterSignReady = 1; - return io_send_sw(SW_OK); - } - - if (context.transactionContext.transactionState != - TRANSACTION_SIGN_READY) { - PRINTF("Invalid transaction state %d\n", context.transactionContext.transactionState); - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); - } + if (buffer->size < HASH_LENGTH) { + return io_send_sw(SW_INCORRECT_LENGTH); + } - if (context.usingOverwinter && !context.overwinterSignReady) { - PRINTF("Overwinter not ready to sign\n"); - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); - } + // Zcash special - store parameters for later - // Read parameters - if (buffer->ptr[0] > MAX_BIP32_PATH) { - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_INCORRECT_DATA); - } - memmove(context.transactionSummary.keyPath, - buffer->ptr, - MAX_BIP32_PATH_LENGTH); + if ((context.usingOverwinter) && (!context.overwinterSignReady) && + (context.segwitParsedOnce) && + (context.transactionContext.transactionState == TRANSACTION_NONE)) { + unsigned long int expiryHeight; parameters += (4 * buffer->ptr[0]) + 1; authorizationLength = *(parameters++); parameters += authorizationLength; lockTime = read_u32_be(parameters, 0); parameters += 4; sighashType = *(parameters++); - context.transactionSummary.sighashType = sighashType; - - // if bitcoin cash OR forkid is set, then use the fork id - if ((COIN_KIND == COIN_KIND_BITCOIN_CASH) || - (COIN_FORKID)) { + expiryHeight = read_u32_be(parameters, 0); + write_u32_le(context.nLockTime, 0, lockTime); + write_u32_le(context.sigHashType, 0, sighashType); + write_u32_le(context.nExpiryHeight, 0, expiryHeight); + context.overwinterSignReady = 1; + return io_send_sw(SW_OK); + } + + if (context.transactionContext.transactionState != TRANSACTION_SIGN_READY) { + PRINTF("Invalid transaction state %d\n", + context.transactionContext.transactionState); + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + if (context.usingOverwinter && !context.overwinterSignReady) { + PRINTF("Overwinter not ready to sign\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + // Read parameters + if (buffer->ptr[0] > MAX_BIP32_PATH) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); + } + memmove(context.transactionSummary.keyPath, buffer->ptr, + MAX_BIP32_PATH_LENGTH); + parameters += (4 * buffer->ptr[0]) + 1; + authorizationLength = *(parameters++); + parameters += authorizationLength; + lockTime = read_u32_be(parameters, 0); + parameters += 4; + sighashType = *(parameters++); + context.transactionSummary.sighashType = sighashType; + + // if bitcoin cash OR forkid is set, then use the fork id + if ((COIN_KIND == COIN_KIND_BITCOIN_CASH) || (COIN_FORKID)) { #define SIGHASH_FORKID 0x40 - if (sighashType != (SIGHASH_ALL | SIGHASH_FORKID)) { - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_INCORRECT_DATA); - } - sighashType |= (COIN_FORKID << 8); - - } else { - if (sighashType != SIGHASH_ALL) { - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_INCORRECT_DATA); - } - } - - // Finalize the hash - if (!context.usingOverwinter) { - write_u32_le(dataBuffer, 0, lockTime); - write_u32_le(dataBuffer, 4, sighashType); - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - dataBuffer, sizeof(dataBuffer), NULL, 0)) { - context.transactionContext.transactionState = TRANSACTION_NONE; - return io_send_sw(SW_INCORRECT_DATA); - } + if (sighashType != (SIGHASH_ALL | SIGHASH_FORKID)) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); } + sighashType |= (COIN_FORKID << 8); - // Check if the path needs to be enforced - if (!enforce_bip44_coin_type(context.transactionSummary.keyPath, false)) { - request_sign_path_approval(context.transactionSummary.keyPath); + } else { + if (sighashType != SIGHASH_ALL) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); } - else { - // Sign immediately - user_action_signtx(1, 0); + } + + // Finalize the hash + if (!context.usingOverwinter) { + write_u32_le(dataBuffer, 0, lockTime); + write_u32_le(dataBuffer, 4, sighashType); + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + dataBuffer, sizeof(dataBuffer), NULL, 0)) { + context.transactionContext.transactionState = TRANSACTION_NONE; + return io_send_sw(SW_INCORRECT_DATA); } - if (G_called_from_swap) { - // if we signed all outputs we should exit, - // but only after sending response, so lets raise the - // vars.swap_data.should_exit flag and check it on timer later - vars.swap_data.alreadySignedInputs++; - if (vars.swap_data.alreadySignedInputs >= vars.swap_data.totalNumberOfInputs) { - vars.swap_data.should_exit = 1; - } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + + // Check if the path needs to be enforced + if (!enforce_bip44_coin_type(context.transactionSummary.keyPath, false)) { + request_sign_path_approval(context.transactionSummary.keyPath); + } else { + // Sign immediately + user_action_signtx(1, 0); + } + if (G_called_from_swap) { + // if we signed all outputs we should exit, + // but only after sending response, so lets raise the + // vars.swap_data.should_exit flag and check it on timer later + vars.swap_data.alreadySignedInputs++; + if (vars.swap_data.alreadySignedInputs >= + vars.swap_data.totalNumberOfInputs) { + vars.swap_data.should_exit = 1; } - return 0; + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + return 0; } int user_action_signtx(unsigned char confirming, unsigned char direct) { - // confirm and finish the apdu exchange //spaghetti - if (confirming) { - unsigned char hash[32]; - if (context.usingOverwinter) { - LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, hash, 0, hash, 32) == CX_OK, "Hash Failed"); - } - else { - LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, - hash, 0, hash, 32) == CX_OK, "Hash Failed"); - PRINTF("Hash1\n%.*H\n", sizeof(hash), hash); - - // Rehash - cx_hash_sha256(hash, sizeof(hash), hash, 32); - } - PRINTF("Hash2\n%.*H\n", sizeof(hash), hash); - // Sign - size_t out_len = sizeof(G_io_apdu_buffer); - sign_finalhash( - context.transactionSummary.keyPath, - sizeof(context.transactionSummary.keyPath), - hash, sizeof(hash), - G_io_apdu_buffer, &out_len); - - context.outLength = G_io_apdu_buffer[1] + 2; - G_io_apdu_buffer[context.outLength++] = context.transactionSummary.sighashType; - ui_transaction_finish(); - + // confirm and finish the apdu exchange //spaghetti + if (confirming) { + unsigned char hash[32]; + if (context.usingOverwinter) { + LEDGER_ASSERT( + cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, + hash, 0, hash, 32) == CX_OK, + "Hash Failed"); } else { - context.outLength = 0; - return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); - } + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, hash, 0, hash, 32) == CX_OK, + "Hash Failed"); + PRINTF("Hash1\n%.*H\n", sizeof(hash), hash); - if (!direct) { - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + // Rehash + cx_hash_sha256(hash, sizeof(hash), hash, 32); } - return 0; + PRINTF("Hash2\n%.*H\n", sizeof(hash), hash); + // Sign + size_t out_len = sizeof(G_io_apdu_buffer); + sign_finalhash(context.transactionSummary.keyPath, + sizeof(context.transactionSummary.keyPath), hash, + sizeof(hash), G_io_apdu_buffer, &out_len); + + context.outLength = G_io_apdu_buffer[1] + 2; + G_io_apdu_buffer[context.outLength++] = + context.transactionSummary.sighashType; + ui_transaction_finish(); + + } else { + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } + + if (!direct) { + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, SW_OK); + } + return 0; } - diff --git a/lib-app-bitcoin/handler/sign_message.c b/lib-app-bitcoin/handler/sign_message.c index 01a6a332..487cf44c 100644 --- a/lib-app-bitcoin/handler/sign_message.c +++ b/lib-app-bitcoin/handler/sign_message.c @@ -1,27 +1,27 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ -#include "context.h" -#include "helpers.h" #include "apdu_constants.h" +#include "context.h" #include "extensions.h" +#include "helpers.h" +#include "io.h" #include "lib_standard_app/read.h" #include "swap.h" -#include "io.h" #define P1_PREPARE 0x00 #define P1_SIGN 0x80 @@ -38,242 +38,230 @@ unsigned char const SIGNMAGIC[] = {' ', 'S', 'i', 'g', 'n', 'e', 'd', ' ', 'M', // TODO : support longer messages unsigned char message_check_bit_id(unsigned char *bip32Path) { - unsigned char i; - unsigned char bip32PathLength = bip32Path[0]; - bip32Path++; - for (i = 0; i < bip32PathLength; i++) { - unsigned short account = read_u32_be(bip32Path, 0); - bip32Path += 4; + unsigned char i; + unsigned char bip32PathLength = bip32Path[0]; + bip32Path++; + for (i = 0; i < bip32PathLength; i++) { + unsigned short account = read_u32_be(bip32Path, 0); + bip32Path += 4; - if (account == BITID_DERIVE) { - return BITID_POWERCYCLE; - } - if (account == BITID_DERIVE_MULTIPLE) { - return BITID_MULTIPLE; - } + if (account == BITID_DERIVE) { + return BITID_POWERCYCLE; + } + if (account == BITID_DERIVE_MULTIPLE) { + return BITID_MULTIPLE; } - return BITID_NONE; + } + return BITID_NONE; } unsigned short message_compute_hash(void) { - unsigned char hash[32]; - unsigned short sw = SW_OK; + unsigned char hash[32]; + unsigned short sw = SW_OK; - context.outLength = 0; - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, hash, - 0, hash, 32)) { - goto discard; - } - - if (cx_hash_sha256(hash, sizeof(hash), hash, 32) == 0) { - goto discard; - } + context.outLength = 0; + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, CX_LAST, + hash, 0, hash, 32)) { + goto discard; + } - size_t out_len = 100; - sign_finalhash( - context.transactionSummary.keyPath, - sizeof(context.transactionSummary.keyPath), - hash, sizeof(hash), // IN - G_io_apdu_buffer, &out_len); // OUT - context.outLength = G_io_apdu_buffer[1] + 2; - memset(&context.transactionSummary, 0, - sizeof(transaction_summary_t)); - return sw; + if (cx_hash_sha256(hash, sizeof(hash), hash, 32) == 0) { + goto discard; + } - discard: - sw = SW_TECHNICAL_PROBLEM_2; - return sw; -} + size_t out_len = 100; + sign_finalhash(context.transactionSummary.keyPath, + sizeof(context.transactionSummary.keyPath), hash, + sizeof(hash), // IN + G_io_apdu_buffer, &out_len); // OUT + context.outLength = G_io_apdu_buffer[1] + 2; + memset(&context.transactionSummary, 0, sizeof(transaction_summary_t)); + return sw; +discard: + sw = SW_TECHNICAL_PROBLEM_2; + return sw; +} -static unsigned short sign_message_internal(buffer_t* buffer, uint8_t p1, uint8_t p2) { - unsigned short sw = SW_OK; - unsigned char apduLength = buffer->size; - unsigned short offset = 0; +static unsigned short sign_message_internal(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + unsigned short sw = SW_OK; + unsigned char apduLength = buffer->size; + unsigned short offset = 0; - if ((p1 != P1_PREPARE) && (p1 != P1_SIGN)) { - return io_send_sw(SW_INCORRECT_P1_P2); - } - if (p1 == P1_PREPARE) { - if ((p2 != P2_FIRST) && (p2 != P2_OTHER) && (p2 != P2_LEGACY)) { - return io_send_sw(SW_INCORRECT_P1_P2); - } + if ((p1 != P1_PREPARE) && (p1 != P1_SIGN)) { + return io_send_sw(SW_INCORRECT_P1_P2); + } + if (p1 == P1_PREPARE) { + if ((p2 != P2_FIRST) && (p2 != P2_OTHER) && (p2 != P2_LEGACY)) { + return io_send_sw(SW_INCORRECT_P1_P2); } + } - if (p1 == P1_PREPARE) { - if ((p2 == P2_FIRST) || (p2 == P2_LEGACY)) { - unsigned char chunkLength; - unsigned char messageLength[3]; - unsigned char messageLengthSize; - memset(&context.transactionSummary, 0, - sizeof(transaction_summary_t)); - if (buffer->ptr[0] > MAX_BIP32_PATH) { - PRINTF("Invalid path\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - context.transactionSummary.payToAddressVersion = COIN_P2PKH_VERSION; - context.transactionSummary.payToScriptHashVersion = COIN_P2SH_VERSION; - memmove( - context.transactionSummary.keyPath, - buffer->ptr, MAX_BIP32_PATH_LENGTH); - offset += (4 * buffer->ptr[0]) + 1; - if (p2 == P2_LEGACY) { - context.transactionSummary.messageLength = - buffer->ptr[offset]; - offset++; - } else { - context.transactionSummary.messageLength = - (buffer->ptr[offset] << 8) | - (buffer->ptr[offset + 1]); - offset += 2; - } - if (context.transactionSummary.messageLength == - 0) { - PRINTF("Null message length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - context.hashedMessageLength = 0; + if (p1 == P1_PREPARE) { + if ((p2 == P2_FIRST) || (p2 == P2_LEGACY)) { + unsigned char chunkLength; + unsigned char messageLength[3]; + unsigned char messageLengthSize; + memset(&context.transactionSummary, 0, sizeof(transaction_summary_t)); + if (buffer->ptr[0] > MAX_BIP32_PATH) { + PRINTF("Invalid path\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + context.transactionSummary.payToAddressVersion = COIN_P2PKH_VERSION; + context.transactionSummary.payToScriptHashVersion = COIN_P2SH_VERSION; + memmove(context.transactionSummary.keyPath, buffer->ptr, + MAX_BIP32_PATH_LENGTH); + offset += (4 * buffer->ptr[0]) + 1; + if (p2 == P2_LEGACY) { + context.transactionSummary.messageLength = buffer->ptr[offset]; + offset++; + } else { + context.transactionSummary.messageLength = + (buffer->ptr[offset] << 8) | (buffer->ptr[offset + 1]); + offset += 2; + } + if (context.transactionSummary.messageLength == 0) { + PRINTF("Null message length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + context.hashedMessageLength = 0; - // Horizen signed message magic header is "Zcash" - // See https://github.com/HorizenOfficial/zen/blob/v5.0.0/src/main.cpp#L122 - const char* magicHeader = (COIN_KIND != COIN_KIND_HORIZEN) ? COIN_COINID : "Zcash"; + // Horizen signed message magic header is "Zcash" + // See + // https://github.com/HorizenOfficial/zen/blob/v5.0.0/src/main.cpp#L122 + const char *magicHeader = + (COIN_KIND != COIN_KIND_HORIZEN) ? COIN_COINID : "Zcash"; - cx_sha256_init_no_throw(&context.transactionHashFull.sha256); - cx_sha256_init_no_throw(&context.transactionHashAuthorization); + cx_sha256_init_no_throw(&context.transactionHashFull.sha256); + cx_sha256_init_no_throw(&context.transactionHashAuthorization); - chunkLength = - strlen(magicHeader) + sizeof(SIGNMAGIC); - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - &chunkLength, 1, NULL, 0)) { - goto discard; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - (uint8_t *)magicHeader, - strlen(magicHeader), NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - (unsigned char *)SIGNMAGIC, sizeof(SIGNMAGIC), NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - if (context.transactionSummary.messageLength < - 0xfd) { - messageLength[0] = - context.transactionSummary.messageLength; - messageLengthSize = 1; - } else { - messageLength[0] = 0xfd; - messageLength[1] = - (context.transactionSummary.messageLength & - 0xff); - messageLength[2] = ((context.transactionSummary - .messageLength >> - 8) & - 0xff); - messageLengthSize = 3; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - messageLength, messageLengthSize, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - chunkLength = apduLength - offset; - if ((context.hashedMessageLength + chunkLength) > - context.transactionSummary.messageLength) { - PRINTF("Invalid data length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - buffer->ptr + offset, chunkLength, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - if (cx_hash_no_throw( - &context.transactionHashAuthorization.header, - 0, buffer->ptr + offset, chunkLength, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - context.hashedMessageLength += chunkLength; - G_io_apdu_buffer[0] = 0x00; - if (context.hashedMessageLength == - context.transactionSummary.messageLength) { - G_io_apdu_buffer[1] = 0x00; - context.outLength = 2; - } else { - context.outLength = 1; - } - } else { - if ((context.hashedMessageLength + apduLength) > - context.transactionSummary.messageLength) { - PRINTF("Invalid data length\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - buffer->ptr + offset, apduLength, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - if (cx_hash_no_throw( - &context.transactionHashAuthorization.header, - 0, buffer->ptr + offset, apduLength, NULL, 0)) { - sw = SW_TECHNICAL_PROBLEM_2; - goto discard; - } - context.hashedMessageLength += apduLength; - G_io_apdu_buffer[0] = 0x00; - if (context.hashedMessageLength == - context.transactionSummary.messageLength) { - G_io_apdu_buffer[1] = 0x00; - context.outLength = 2; - } else { - context.outLength = 1; - } - } + chunkLength = strlen(magicHeader) + sizeof(SIGNMAGIC); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + &chunkLength, 1, NULL, 0)) { + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + (uint8_t *)magicHeader, strlen(magicHeader), NULL, + 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + (unsigned char *)SIGNMAGIC, sizeof(SIGNMAGIC), NULL, + 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (context.transactionSummary.messageLength < 0xfd) { + messageLength[0] = context.transactionSummary.messageLength; + messageLengthSize = 1; + } else { + messageLength[0] = 0xfd; + messageLength[1] = (context.transactionSummary.messageLength & 0xff); + messageLength[2] = + ((context.transactionSummary.messageLength >> 8) & 0xff); + messageLengthSize = 3; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + messageLength, messageLengthSize, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + chunkLength = apduLength - offset; + if ((context.hashedMessageLength + chunkLength) > + context.transactionSummary.messageLength) { + PRINTF("Invalid data length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + offset, chunkLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, 0, + buffer->ptr + offset, chunkLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + context.hashedMessageLength += chunkLength; + G_io_apdu_buffer[0] = 0x00; + if (context.hashedMessageLength == + context.transactionSummary.messageLength) { + G_io_apdu_buffer[1] = 0x00; + context.outLength = 2; + } else { + context.outLength = 1; + } + } else { + if ((context.hashedMessageLength + apduLength) > + context.transactionSummary.messageLength) { + PRINTF("Invalid data length\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + buffer->ptr + offset, apduLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, 0, + buffer->ptr + offset, apduLength, NULL, 0)) { + sw = SW_TECHNICAL_PROBLEM_2; + goto discard; + } + context.hashedMessageLength += apduLength; + G_io_apdu_buffer[0] = 0x00; + if (context.hashedMessageLength == + context.transactionSummary.messageLength) { + G_io_apdu_buffer[1] = 0x00; + context.outLength = 2; + } else { + context.outLength = 1; + } + } + } else { + if ((context.transactionSummary.messageLength == 0) || + (context.hashedMessageLength != + context.transactionSummary.messageLength)) { + PRINTF("Invalid length to sign\n"); + sw = SW_INCORRECT_DATA; + goto discard; + } + if (message_check_bit_id(context.transactionSummary.keyPath) != + BITID_NONE) { + sw = message_compute_hash(); } else { - if ((context.transactionSummary.messageLength == 0) || - (context.hashedMessageLength != - context.transactionSummary.messageLength)) { - PRINTF("Invalid length to sign\n"); - sw = SW_INCORRECT_DATA; - goto discard; - } - if (message_check_bit_id(context.transactionSummary.keyPath) != BITID_NONE) { - sw = message_compute_hash(); - } else { - confirm_message_signature(); - return 0; - } + confirm_message_signature(); + return 0; } - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + } + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); - discard : - memset(&context.transactionSummary, 0, - sizeof(transaction_summary_t)); - return io_send_sw(sw); +discard: + memset(&context.transactionSummary, 0, sizeof(transaction_summary_t)); + return io_send_sw(sw); } -WEAK unsigned short handler_sign_message(buffer_t* buffer, uint8_t p1, uint8_t p2) { - if (G_called_from_swap) { - return io_send_sw(SW_SECURITY_STATUS_NOT_SATISFIED); - } +WEAK unsigned short handler_sign_message(buffer_t *buffer, uint8_t p1, + uint8_t p2) { + if (G_called_from_swap) { + return io_send_sw(SW_SECURITY_STATUS_NOT_SATISFIED); + } - return sign_message_internal(buffer, p1, p2); + return sign_message_internal(buffer, p1, p2); } int user_action_message_signing(unsigned char confirming) { - unsigned short sw; - if (confirming) { - sw = message_compute_hash(); - return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); - } else { - context.outLength = 0; - return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); - } + unsigned short sw; + if (confirming) { + sw = message_compute_hash(); + return io_send_response_pointer(G_io_apdu_buffer, context.outLength, sw); + } else { + context.outLength = 0; + return io_send_sw(SW_CONDITIONS_OF_USE_NOT_SATISFIED); + } } diff --git a/lib-app-bitcoin/helpers.c b/lib-app-bitcoin/helpers.c index 50a50f8b..9c3ebf56 100644 --- a/lib-app-bitcoin/helpers.c +++ b/lib-app-bitcoin/helpers.c @@ -1,231 +1,234 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ -#include "lib_standard_app/crypto_helpers.h" -#include "lib_standard_app/bip32.h" -#include "ledger_assert.h" -#include "io.h" #include "base58.h" +#include "io.h" +#include "ledger_assert.h" +#include "lib_standard_app/bip32.h" +#include "lib_standard_app/crypto_helpers.h" #include "read.h" #include "context.h" #include "helpers.h" void public_key_hash160(unsigned char *in, unsigned short inlen, - unsigned char out[static CX_RIPEMD160_SIZE]) { - unsigned char buffer[CX_SHA256_SIZE]; - cx_hash_sha256(in, inlen, buffer, sizeof(buffer)); - cx_ripemd160_hash(buffer, sizeof(buffer), out); + unsigned char out[static CX_RIPEMD160_SIZE]) { + unsigned char buffer[CX_SHA256_SIZE]; + cx_hash_sha256(in, inlen, buffer, sizeof(buffer)); + cx_ripemd160_hash(buffer, sizeof(buffer), out); } -void compute_checksum(unsigned char* in, unsigned short inlen, unsigned char * output) { - unsigned char checksumBuffer[32]; - cx_hash_sha256(in, inlen, checksumBuffer, 32); - cx_hash_sha256(checksumBuffer, 32, checksumBuffer, 32); +void compute_checksum(unsigned char *in, unsigned short inlen, + unsigned char *output) { + unsigned char checksumBuffer[32]; + cx_hash_sha256(in, inlen, checksumBuffer, 32); + cx_hash_sha256(checksumBuffer, 32, checksumBuffer, 32); - PRINTF("Checksum\n%.*H\n",4,checksumBuffer); - memmove(output, checksumBuffer, 4); + PRINTF("Checksum\n%.*H\n", 4, checksumBuffer); + memmove(output, checksumBuffer, 4); } -unsigned short public_key_to_encoded_base58( - unsigned char *in, unsigned short inlen, unsigned char *out, - unsigned short outlen, unsigned short version, - unsigned char alreadyHashed) { - unsigned char tmpBuffer[34]; - - unsigned char versionSize = (version > 255 ? 2 : 1); - short outputLen; - - if (!alreadyHashed) { - PRINTF("To hash\n%.*H\n",inlen,in); - public_key_hash160(in, inlen, tmpBuffer + versionSize); - PRINTF("Hash160\n%.*H\n",20,(tmpBuffer + versionSize)); - if (version > 255) { - tmpBuffer[0] = (version >> 8); - tmpBuffer[1] = version; - } else { - tmpBuffer[0] = version; - } +unsigned short public_key_to_encoded_base58(unsigned char *in, + unsigned short inlen, + unsigned char *out, + unsigned short outlen, + unsigned short version, + unsigned char alreadyHashed) { + unsigned char tmpBuffer[34]; + + unsigned char versionSize = (version > 255 ? 2 : 1); + short outputLen; + + if (!alreadyHashed) { + PRINTF("To hash\n%.*H\n", inlen, in); + public_key_hash160(in, inlen, tmpBuffer + versionSize); + PRINTF("Hash160\n%.*H\n", 20, (tmpBuffer + versionSize)); + if (version > 255) { + tmpBuffer[0] = (version >> 8); + tmpBuffer[1] = version; } else { - memmove(tmpBuffer, in, 20 + versionSize); + tmpBuffer[0] = version; } + } else { + memmove(tmpBuffer, in, 20 + versionSize); + } - compute_checksum(tmpBuffer, 20 + versionSize, tmpBuffer + 20 + versionSize); + compute_checksum(tmpBuffer, 20 + versionSize, tmpBuffer + 20 + versionSize); - outputLen = base58_encode(tmpBuffer, 24 + versionSize, (char *)out, outlen); - LEDGER_ASSERT(outputLen >= 0, "Error encoding public key"); + outputLen = base58_encode(tmpBuffer, 24 + versionSize, (char *)out, outlen); + LEDGER_ASSERT(outputLen >= 0, "Error encoding public key"); - return outputLen; + return outputLen; } void swap_bytes(unsigned char *target, unsigned char *source, - unsigned char size) { - unsigned char i; - for (i = 0; i < size; i++) { - target[i] = source[size - 1 - i]; - } + unsigned char size) { + unsigned char i; + for (i = 0; i < size; i++) { + target[i] = source[size - 1 - i]; + } } /* -Checks if the values of a derivation path are within "normal" (arbitrary) ranges: -Account < 100, change == 1 or 0, address index < 50000 +Checks if the values of a derivation path are within "normal" (arbitrary) +ranges: Account < 100, change == 1 or 0, address index < 50000 Returns 1 if the path is unusual, or not compliant with BIP44*/ -unsigned char bip44_derivation_guard(const unsigned char *bip32Path, bool is_change_path) { - unsigned char path_len; - bip32_path_t bip32PathInt; - - path_len = bip32Path[0]; - if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { - return 1; - } - - // If the path length is not compliant with BIP44 or if the purpose don't match regular usage, return a warning - if(path_len != BIP44_PATH_LEN || - ((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 44 && - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 49 && - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) != 84)) { - return 1; - } - - // If the coin type doesn't match, return a warning - if ((BIP44_COIN_TYPE != 0) && - (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE) && - ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) != BIP44_COIN_TYPE_2))) { - return 1; - } - - // If the account or address index is very high or if the change isn't 1, return a warning - if((bip32PathInt.path[BIP44_ACCOUNT_OFFSET]^0x80000000) > MAX_BIP44_ACCOUNT_RECOMMENDED || - bip32PathInt.path[BIP44_CHANGE_OFFSET] != is_change_path?1:0 || - bip32PathInt.path[BIP44_ADDRESS_INDEX_OFFSET] > MAX_BIP44_ADDRESS_INDEX_RECOMMENDED) { - return 1; - } - - return 0; +unsigned char bip44_derivation_guard(const unsigned char *bip32Path, + bool is_change_path) { + unsigned char path_len; + bip32_path_t bip32PathInt; + + path_len = bip32Path[0]; + if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { + return 1; + } + + // If the path length is not compliant with BIP44 or if the purpose don't + // match regular usage, return a warning + if (path_len != BIP44_PATH_LEN || + ((bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) != 44 && + (bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) != 49 && + (bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) != 84)) { + return 1; + } + + // If the coin type doesn't match, return a warning + if ((BIP44_COIN_TYPE != 0) && (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET] ^ + 0x80000000) != BIP44_COIN_TYPE) && + ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET] ^ + 0x80000000) != BIP44_COIN_TYPE_2))) { + return 1; + } + + // If the account or address index is very high or if the change isn't 1, + // return a warning + if ((bip32PathInt.path[BIP44_ACCOUNT_OFFSET] ^ 0x80000000) > + MAX_BIP44_ACCOUNT_RECOMMENDED || + bip32PathInt.path[BIP44_CHANGE_OFFSET] != is_change_path + ? 1 + : 0 || bip32PathInt.path[BIP44_ADDRESS_INDEX_OFFSET] > + MAX_BIP44_ADDRESS_INDEX_RECOMMENDED) { + return 1; + } + + return 0; } /* Only enforce the structure or coin type for consumed UTXOs or a public address Returns 0 if the path is non compliant, or 1 if compliant */ -unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, bool for_pubkey) { - bip32_path_t bip32PathInt; - // No enforcement required - if (BIP44_COIN_TYPE == 0) { - return 1; - } - // Path is too short - always require a user validation if signing - if (bip32Path[0] < 2) { - return for_pubkey; - } - - if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { - return 1; - } - - // Path is not compliant with BIP 44 or derivatives - valid if not signing - if (!(((bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 44 || - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 49 || - (bip32PathInt.path[BIP44_PURPOSE_OFFSET]^0x80000000) == 84))) { - return for_pubkey; - } - - if (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE) || - ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET]^0x80000000) == BIP44_COIN_TYPE_2)) { - // Valid BIP 44 path - return 1; - } - // Everything else needs a user validation - return 0; +unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, + bool for_pubkey) { + bip32_path_t bip32PathInt; + // No enforcement required + if (BIP44_COIN_TYPE == 0) { + return 1; + } + // Path is too short - always require a user validation if signing + if (bip32Path[0] < 2) { + return for_pubkey; + } + + if (!parse_serialized_path(&bip32PathInt, bip32Path, MAX_BIP32_PATH_LENGTH)) { + return 1; + } + + // Path is not compliant with BIP 44 or derivatives - valid if not signing + if (!(((bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) == 44 || + (bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) == 49 || + (bip32PathInt.path[BIP44_PURPOSE_OFFSET] ^ 0x80000000) == 84))) { + return for_pubkey; + } + + if (((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET] ^ 0x80000000) == + BIP44_COIN_TYPE) || + ((bip32PathInt.path[BIP44_COIN_TYPE_OFFSET] ^ 0x80000000) == + BIP44_COIN_TYPE_2)) { + // Valid BIP 44 path + return 1; + } + // Everything else needs a user validation + return 0; } -int sign_finalhash(unsigned char* path, size_t path_len, unsigned char *in, unsigned short inlen, - unsigned char *out, size_t* outlen) { +int sign_finalhash(unsigned char *path, size_t path_len, unsigned char *in, + unsigned short inlen, unsigned char *out, size_t *outlen) { - unsigned int info = 0; + unsigned int info = 0; - io_seproxyhal_io_heartbeat(); - - bip32_path_t bip32Path; - bip32Path.length = path[0]; + io_seproxyhal_io_heartbeat(); - if (!parse_serialized_path(&bip32Path, path, path_len)) { - return -1; - } + bip32_path_t bip32Path; + bip32Path.length = path[0]; - if (bip32_derive_ecdsa_sign_hash_256( - CX_CURVE_SECP256K1, - bip32Path.path, - bip32Path.length, - CX_LAST | CX_RND_RFC6979, - CX_SHA256, - in, - inlen, - out, - outlen, - &info) != CX_OK) { - return -1; - } + if (!parse_serialized_path(&bip32Path, path, path_len)) { + return -1; + } - // Store information about the parity of the 'y' coordinate - if (info & CX_ECCINFO_PARITY_ODD) { - out[0] |= 0x01; - } + if (bip32_derive_ecdsa_sign_hash_256(CX_CURVE_SECP256K1, bip32Path.path, + bip32Path.length, + CX_LAST | CX_RND_RFC6979, CX_SHA256, in, + inlen, out, outlen, &info) != CX_OK) { + return -1; + } - io_seproxyhal_io_heartbeat(); - return 0; + // Store information about the parity of the 'y' coordinate + if (info & CX_ECCINFO_PARITY_ODD) { + out[0] |= 0x01; + } + + io_seproxyhal_io_heartbeat(); + return 0; } -int get_public_key(const unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode) { +int get_public_key(const unsigned char *keyPath, size_t keyPath_len, + uint8_t raw_pubkey[static 65], unsigned char *chainCode) { - bip32_path_t bip32Path; + bip32_path_t bip32Path; - if (!parse_serialized_path(&bip32Path, keyPath, keyPath_len)) { - return -1; - } + if (!parse_serialized_path(&bip32Path, keyPath, keyPath_len)) { + return -1; + } - if (bip32_derive_get_pubkey_256( - CX_CURVE_SECP256K1, - bip32Path.path, - bip32Path.length, - raw_pubkey, - chainCode, - CX_SHA512) != CX_OK) - { - return -1; - } + if (bip32_derive_get_pubkey_256(CX_CURVE_SECP256K1, bip32Path.path, + bip32Path.length, raw_pubkey, chainCode, + CX_SHA512) != CX_OK) { + return -1; + } - return 0; + return 0; } void compress_public_key_value(unsigned char *value) { - bool odd = (value[64] & 1); - value[0] = odd ? 0x03 : 0x02; + bool odd = (value[64] & 1); + value[0] = odd ? 0x03 : 0x02; } -bool parse_serialized_path(bip32_path_t* path, const unsigned char* serialized_path, unsigned char serialized_path_length) { - if (serialized_path_length < 1 || - serialized_path[0] > MAX_BIP32_PATH || - serialized_path[0] * 4 + 1 > serialized_path_length) - return false; - path->length = serialized_path[0]; - serialized_path++; - for (int i = 0; i < path->length; i += 1, serialized_path += 4) { - path->path[i] = read_u32_be(serialized_path, 0); - } - return true; +bool parse_serialized_path(bip32_path_t *path, + const unsigned char *serialized_path, + unsigned char serialized_path_length) { + if (serialized_path_length < 1 || serialized_path[0] > MAX_BIP32_PATH || + serialized_path[0] * 4 + 1 > serialized_path_length) + return false; + path->length = serialized_path[0]; + serialized_path++; + for (int i = 0; i < path->length; i += 1, serialized_path += 4) { + path->path[i] = read_u32_be(serialized_path, 0); + } + return true; } diff --git a/lib-app-bitcoin/helpers.h b/lib-app-bitcoin/helpers.h index 88314f74..9c100a83 100644 --- a/lib-app-bitcoin/helpers.h +++ b/lib-app-bitcoin/helpers.h @@ -1,26 +1,26 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once -#include "os.h" #include "cx.h" -#include "stdbool.h" #include "filesystem_tx.h" +#include "os.h" +#include "stdbool.h" #define OUTPUT_SCRIPT_REGULAR_PRE_LENGTH 4 #define OUTPUT_SCRIPT_REGULAR_POST_LENGTH 2 @@ -30,28 +30,32 @@ #define OUTPUT_SCRIPT_NATIVE_WITNESS_PROGRAM_OFFSET 3 typedef struct bip32_path { - unsigned char length; - unsigned int path[MAX_BIP32_PATH]; + unsigned char length; + unsigned int path[MAX_BIP32_PATH]; } bip32_path_t; void public_key_hash160(unsigned char *in, unsigned short inlen, - unsigned char out [static CX_RIPEMD160_SIZE]); + unsigned char out[static CX_RIPEMD160_SIZE]); unsigned short public_key_to_encoded_base58( unsigned char *in, unsigned short inlen, unsigned char *out, unsigned short outlen, unsigned short version, unsigned char alreadyHashed); -unsigned char bip44_derivation_guard(const unsigned char *bip32Path, bool is_change_path); -unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, bool for_pubkey); +unsigned char bip44_derivation_guard(const unsigned char *bip32Path, + bool is_change_path); +unsigned char enforce_bip44_coin_type(const unsigned char *bip32Path, + bool for_pubkey); void swap_bytes(unsigned char *target, unsigned char *source, - unsigned char size); + unsigned char size); -int sign_finalhash(unsigned char *path, size_t path_len, - unsigned char *in, unsigned short inlen, - unsigned char *out, size_t* outlen); +int sign_finalhash(unsigned char *path, size_t path_len, unsigned char *in, + unsigned short inlen, unsigned char *out, size_t *outlen); -int get_public_key(const unsigned char* keyPath, size_t keyPath_len, uint8_t raw_pubkey[static 65], unsigned char* chainCode); +int get_public_key(const unsigned char *keyPath, size_t keyPath_len, + uint8_t raw_pubkey[static 65], unsigned char *chainCode); void compress_public_key_value(unsigned char *value); -bool parse_serialized_path(bip32_path_t* path, const unsigned char* serialized_path, unsigned char serialized_path_length); +bool parse_serialized_path(bip32_path_t *path, + const unsigned char *serialized_path, + unsigned char serialized_path_length); diff --git a/lib-app-bitcoin/swap/handle_check_address.c b/lib-app-bitcoin/swap/handle_check_address.c index 95e6d166..7b8e7f0b 100644 --- a/lib-app-bitcoin/swap/handle_check_address.c +++ b/lib-app-bitcoin/swap/handle_check_address.c @@ -1,7 +1,7 @@ #include "handle_check_address.h" -#include "helpers.h" -#include "context.h" #include "cashaddr.h" +#include "context.h" +#include "helpers.h" #include "segwit_addr.h" #define P1_NO_DISPLAY 0x00 @@ -13,137 +13,127 @@ #define P2_NATIVE_SEGWIT 0x02 #define P2_CASHADDR 0x03 -static bool derive_compressed_public_key( - unsigned char* serialized_path, unsigned char serialized_path_length, - unsigned char* public_key, unsigned char public_key_length) { - UNUSED(public_key_length); - uint8_t pubKey[65]; +static bool derive_compressed_public_key(unsigned char *serialized_path, + unsigned char serialized_path_length, + unsigned char *public_key, + unsigned char public_key_length) { + UNUSED(public_key_length); + uint8_t pubKey[65]; - if (get_public_key(serialized_path, serialized_path_length, pubKey, NULL)){ - return false; - } + if (get_public_key(serialized_path, serialized_path_length, pubKey, NULL)) { + return false; + } - compress_public_key_value(pubKey); - memcpy(public_key, pubKey, 33); - return true; + compress_public_key_value(pubKey); + memcpy(public_key, pubKey, 33); + return true; } static bool get_address_from_compressed_public_key( - unsigned char format, - unsigned char* compressed_pub_key, - unsigned short payToAddressVersion, - unsigned short payToScriptHashVersion, - const char* native_segwit_prefix, - char * address, - unsigned char max_address_length -) { - bool segwit = (format == P2_SEGWIT); - bool nativeSegwit = (format == P2_NATIVE_SEGWIT); - bool cashAddr = (format == P2_CASHADDR); - int address_length; - if (cashAddr) { - uint8_t tmp[20]; - public_key_hash160(compressed_pub_key, // IN - 33, // INLEN - tmp); - if (!cashaddr_encode(tmp, 20, (uint8_t *)address, max_address_length, CASHADDR_P2PKH)) - return false; - } else if (!(segwit || nativeSegwit)) { - // public_key_to_encoded_base58 doesn't add terminating 0, - // so we will do this ourself - address_length = public_key_to_encoded_base58( - compressed_pub_key, // IN - 33, // INLEN - (uint8_t *)address, // OUT - max_address_length - 1, // MAXOUTLEN - payToAddressVersion, 0); - address[address_length] = 0; + unsigned char format, unsigned char *compressed_pub_key, + unsigned short payToAddressVersion, unsigned short payToScriptHashVersion, + const char *native_segwit_prefix, char *address, + unsigned char max_address_length) { + bool segwit = (format == P2_SEGWIT); + bool nativeSegwit = (format == P2_NATIVE_SEGWIT); + bool cashAddr = (format == P2_CASHADDR); + int address_length; + if (cashAddr) { + uint8_t tmp[20]; + public_key_hash160(compressed_pub_key, // IN + 33, // INLEN + tmp); + if (!cashaddr_encode(tmp, 20, (uint8_t *)address, max_address_length, + CASHADDR_P2PKH)) + return false; + } else if (!(segwit || nativeSegwit)) { + // public_key_to_encoded_base58 doesn't add terminating 0, + // so we will do this ourself + address_length = + public_key_to_encoded_base58(compressed_pub_key, // IN + 33, // INLEN + (uint8_t *)address, // OUT + max_address_length - 1, // MAXOUTLEN + payToAddressVersion, 0); + address[address_length] = 0; + } else { + uint8_t tmp[22]; + tmp[0] = 0x00; + tmp[1] = 0x14; + public_key_hash160(compressed_pub_key, // IN + 33, // INLEN + tmp + 2 // OUT + ); + if (!nativeSegwit) { + address_length = public_key_to_encoded_base58(tmp, // IN + 22, // INLEN + (uint8_t *)address, // OUT + 150, // MAXOUTLEN + payToScriptHashVersion, 0); + address[address_length] = 0; } else { - uint8_t tmp[22]; - tmp[0] = 0x00; - tmp[1] = 0x14; - public_key_hash160(compressed_pub_key, // IN - 33, // INLEN - tmp + 2 // OUT - ); - if (!nativeSegwit) { - address_length = public_key_to_encoded_base58( - tmp, // IN - 22, // INLEN - (uint8_t *)address, // OUT - 150, // MAXOUTLEN - payToScriptHashVersion, 0); - address[address_length] = 0; - } else { - if (!native_segwit_prefix) - return false; - if (!segwit_addr_encode( - address, - native_segwit_prefix, 0, tmp + 2, 20)) { - return false; - } - } + if (!native_segwit_prefix) + return false; + if (!segwit_addr_encode(address, native_segwit_prefix, 0, tmp + 2, 20)) { + return false; + } } - return true; + } + return true; } -void swap_handle_check_address(check_address_parameters_t* params) { - PRINTF("Inside swap_handle_check_address\n"); - params->result = 0; +void swap_handle_check_address(check_address_parameters_t *params) { + PRINTF("Inside swap_handle_check_address\n"); + params->result = 0; - if (params->address_parameters == NULL) { - PRINTF("derivation path expected\n"); - return; - } + if (params->address_parameters == NULL) { + PRINTF("derivation path expected\n"); + return; + } - if (params->address_to_check == NULL) { - PRINTF("Address to check expected\n"); - return; - } - PRINTF("Address to check %s\n", params->address_to_check); + if (params->address_to_check == NULL) { + PRINTF("Address to check expected\n"); + return; + } + PRINTF("Address to check %s\n", params->address_to_check); - if (params->extra_id_to_check == NULL) { - PRINTF("extra_id_to_check expected\n"); - return; - } else if (params->extra_id_to_check[0] != '\0') { - PRINTF("extra_id_to_check expected empty, not '%s'\n", params->extra_id_to_check); - return; - } + if (params->extra_id_to_check == NULL) { + PRINTF("extra_id_to_check expected\n"); + return; + } else if (params->extra_id_to_check[0] != '\0') { + PRINTF("extra_id_to_check expected empty, not '%s'\n", + params->extra_id_to_check); + return; + } - if (params->address_to_check == 0) { - PRINTF("Address to check == 0\n"); - return; - } + if (params->address_to_check == 0) { + PRINTF("Address to check == 0\n"); + return; + } - unsigned char compressed_public_key[33]; - if (!derive_compressed_public_key( - params->address_parameters + 1, - params->address_parameters_length - 1, - compressed_public_key, - sizeof(compressed_public_key))) { - PRINTF("Failed to derive public key\n"); - return; - } + unsigned char compressed_public_key[33]; + if (!derive_compressed_public_key( + params->address_parameters + 1, params->address_parameters_length - 1, + compressed_public_key, sizeof(compressed_public_key))) { + PRINTF("Failed to derive public key\n"); + return; + } - char address[51]; - if (!get_address_from_compressed_public_key( - params->address_parameters[0], - compressed_public_key, - COIN_P2PKH_VERSION, - COIN_P2SH_VERSION, - COIN_NATIVE_SEGWIT_PREFIX, - address, - sizeof(address))) { - PRINTF("Can't create address from given public key\n"); - return; - } - if (strcmp(params->address_to_check, address) != 0) { - PRINTF("Address %s != %s\n", params->address_to_check, address); - return; - } + char address[51]; + if (!get_address_from_compressed_public_key( + params->address_parameters[0], compressed_public_key, + COIN_P2PKH_VERSION, COIN_P2SH_VERSION, COIN_NATIVE_SEGWIT_PREFIX, + address, sizeof(address))) { + PRINTF("Can't create address from given public key\n"); + return; + } + if (strcmp(params->address_to_check, address) != 0) { + PRINTF("Address %s != %s\n", params->address_to_check, address); + return; + } - PRINTF("Addresses match\n"); + PRINTF("Addresses match\n"); - params->result = 1; - return; + params->result = 1; + return; } \ No newline at end of file diff --git a/lib-app-bitcoin/swap/handle_check_address.h b/lib-app-bitcoin/swap/handle_check_address.h index 2e5eaa54..95361a26 100644 --- a/lib-app-bitcoin/swap/handle_check_address.h +++ b/lib-app-bitcoin/swap/handle_check_address.h @@ -3,6 +3,7 @@ #include "swap_lib_calls.h" -void swap_handle_check_address(check_address_parameters_t* check_address_params); +void swap_handle_check_address( + check_address_parameters_t *check_address_params); #endif // _HANDLE_CHECK_ADDRESS_H_ \ No newline at end of file diff --git a/lib-app-bitcoin/swap/handle_get_printable_amount.c b/lib-app-bitcoin/swap/handle_get_printable_amount.c index d801bfab..c6834598 100644 --- a/lib-app-bitcoin/swap/handle_get_printable_amount.c +++ b/lib-app-bitcoin/swap/handle_get_printable_amount.c @@ -1,20 +1,23 @@ #include "read.h" -#include "handle_get_printable_amount.h" #include "display_utils.h" +#include "handle_get_printable_amount.h" -void swap_handle_get_printable_amount(get_printable_amount_parameters_t* params) { - params->printable_amount[0] = 0; - if (params->amount_length > 8) { - PRINTF("Amount is too big"); - return; - } - unsigned char amount[8]; - memset(amount, 0, 8); - memcpy(amount + (8 - params->amount_length), params->amount, params->amount_length); - - format_sats_amount(COIN_COINID_SHORT, - (uint64_t)(read_u64_be(amount, 0)), // Cast prevents weird compilo bug - params->printable_amount); +void swap_handle_get_printable_amount( + get_printable_amount_parameters_t *params) { + params->printable_amount[0] = 0; + if (params->amount_length > 8) { + PRINTF("Amount is too big"); return; + } + unsigned char amount[8]; + memset(amount, 0, 8); + memcpy(amount + (8 - params->amount_length), params->amount, + params->amount_length); + + format_sats_amount( + COIN_COINID_SHORT, + (uint64_t)(read_u64_be(amount, 0)), // Cast prevents weird compilo bug + params->printable_amount); + return; } \ No newline at end of file diff --git a/lib-app-bitcoin/swap/handle_get_printable_amount.h b/lib-app-bitcoin/swap/handle_get_printable_amount.h index f47d46e0..2718126c 100644 --- a/lib-app-bitcoin/swap/handle_get_printable_amount.h +++ b/lib-app-bitcoin/swap/handle_get_printable_amount.h @@ -1,9 +1,10 @@ #ifndef _HANDLE_GET_PRINTABLE_AMOUNT_H_ #define _HANDLE_GET_PRINTABLE_AMOUNT_H_ -#include "swap_lib_calls.h" #include "context.h" +#include "swap_lib_calls.h" -void swap_handle_get_printable_amount(get_printable_amount_parameters_t* get_printable_amount_params); +void swap_handle_get_printable_amount( + get_printable_amount_parameters_t *get_printable_amount_params); #endif // _HANDLE_GET_PRINTABLE_AMOUNT_H_ \ No newline at end of file diff --git a/lib-app-bitcoin/swap/handle_swap_sign_transaction.c b/lib-app-bitcoin/swap/handle_swap_sign_transaction.c index 9452ef94..e30abb58 100644 --- a/lib-app-bitcoin/swap/handle_swap_sign_transaction.c +++ b/lib-app-bitcoin/swap/handle_swap_sign_transaction.c @@ -1,7 +1,7 @@ #include "handle_swap_sign_transaction.h" -#include "os_io_seproxyhal.h" -#include "display_variables.h" #include "context.h" +#include "display_variables.h" +#include "os_io_seproxyhal.h" #include "usbd_core.h" #include "ux.h" @@ -14,60 +14,64 @@ // Save the BSS address where we will write the return value when finished static uint8_t *G_swap_sign_return_value_address; -bool swap_copy_transaction_parameters(create_transaction_parameters_t* params) { - PRINTF("Inside swap_copy_transaction_parameters\n"); - - // Ensure no extraid - if (params->destination_address_extra_id == NULL) { - PRINTF("destination_address_extra_id expected\n"); - return false; - } else if (params->destination_address_extra_id[0] != '\0') { - PRINTF("destination_address_extra_id expected empty, not '%s'\n", - params->destination_address_extra_id); - return false; - } +bool swap_copy_transaction_parameters(create_transaction_parameters_t *params) { + PRINTF("Inside swap_copy_transaction_parameters\n"); - // We need this "trick" as the input data position can overlap with app globals - // and also because we want to memset the whole bss segment as it is not done - // when an app is called as a lib. - // This is necessary as many part of the code expect bss variables to - // initialized at 0. - swap_data_t swap_validated; - memset(&swap_validated, 0, sizeof(swap_validated)); + // Ensure no extraid + if (params->destination_address_extra_id == NULL) { + PRINTF("destination_address_extra_id expected\n"); + return false; + } else if (params->destination_address_extra_id[0] != '\0') { + PRINTF("destination_address_extra_id expected empty, not '%s'\n", + params->destination_address_extra_id); + return false; + } - // Save recipient - strlcpy(swap_validated.destination_address, - params->destination_address, - sizeof(swap_validated.destination_address)); - if (swap_validated.destination_address[sizeof(swap_validated.destination_address) - 1] != '\0') { - return false; - } + // We need this "trick" as the input data position can overlap with app + // globals and also because we want to memset the whole bss segment as it is + // not done when an app is called as a lib. This is necessary as many part of + // the code expect bss variables to initialized at 0. + swap_data_t swap_validated; + memset(&swap_validated, 0, sizeof(swap_validated)); - // store amount as big endian in 8 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(swap_validated.amount + 8 - params->amount_length, params->amount, params->amount_length); - memcpy(swap_validated.fees + 8 - params->fee_amount_length, params->fee_amount, params->fee_amount_length); + // Save recipient + strlcpy(swap_validated.destination_address, params->destination_address, + sizeof(swap_validated.destination_address)); + if (swap_validated + .destination_address[sizeof(swap_validated.destination_address) - + 1] != '\0') { + return false; + } - // Save amount and fees -// swap_str_to_u64(params->amount, params->amount_length, &swap_validated.amount); -// swap_str_to_u64(params->fee_amount, params->fee_amount_length, &swap_validated.fees); -// - swap_validated.initialized = true; + // store amount as big endian in 8 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(swap_validated.amount + 8 - params->amount_length, params->amount, + params->amount_length); + memcpy(swap_validated.fees + 8 - params->fee_amount_length, + params->fee_amount, params->fee_amount_length); - // Full reset the global variables - os_explicit_zero_BSS_segment(); + // Save amount and fees + // swap_str_to_u64(params->amount, params->amount_length, + // &swap_validated.amount); swap_str_to_u64(params->fee_amount, + // params->fee_amount_length, &swap_validated.fees); + // + swap_validated.initialized = true; - // Keep the address at which we'll reply the signing status - G_swap_sign_return_value_address = ¶ms->result; + // 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 = ¶ms->result; - // Copy from stack back to global data segment - memcpy(&vars.swap_data, &swap_validated, sizeof(swap_validated)); - swap_validated.initialized = true; - return true; + // Copy from stack back to global data segment + memcpy(&vars.swap_data, &swap_validated, sizeof(swap_validated)); + swap_validated.initialized = true; + return true; } -void __attribute__((noreturn)) swap_finalize_exchange_sign_transaction(bool is_success) { - *G_swap_sign_return_value_address = is_success; - os_lib_end(); +void __attribute__((noreturn)) +swap_finalize_exchange_sign_transaction(bool is_success) { + *G_swap_sign_return_value_address = is_success; + os_lib_end(); } diff --git a/lib-app-bitcoin/swap/handle_swap_sign_transaction.h b/lib-app-bitcoin/swap/handle_swap_sign_transaction.h index a9e2914c..7b3d12e3 100644 --- a/lib-app-bitcoin/swap/handle_swap_sign_transaction.h +++ b/lib-app-bitcoin/swap/handle_swap_sign_transaction.h @@ -3,8 +3,10 @@ #include "swap_lib_calls.h" -bool swap_copy_transaction_parameters(create_transaction_parameters_t* sign_transaction_params); +bool swap_copy_transaction_parameters( + create_transaction_parameters_t *sign_transaction_params); -void __attribute__((noreturn)) swap_finalize_exchange_sign_transaction(bool is_success); +void __attribute__((noreturn)) +swap_finalize_exchange_sign_transaction(bool is_success); #endif // _HANDLE_SWAP_SIGN_TRANSACTION_H_ diff --git a/lib-app-bitcoin/transaction.c b/lib-app-bitcoin/transaction.c index ab59b215..2d6b0e1e 100644 --- a/lib-app-bitcoin/transaction.c +++ b/lib-app-bitcoin/transaction.c @@ -1,19 +1,19 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #include "ledger_assert.h" #include "lib_standard_app/read.h" @@ -21,37 +21,47 @@ #include "swap.h" #include "apdu_constants.h" -#include "display_variables.h" #include "be_operations.h" -#include "transaction.h" #include "context.h" +#include "display_variables.h" #include "filesystem.h" #include "helpers.h" +#include "transaction.h" #ifndef COIN_CONSENSUS_BRANCH_ID #define COIN_CONSENSUS_BRANCH_ID 0 -#endif +#endif #define CONSENSUS_BRANCH_ID_OVERWINTER 0x5ba81b19 #define CONSENSUS_BRANCH_ID_SAPLING 0x76b809bb #define CONSENSUS_BRANCH_ID_ZCLASSIC 0x930b540d -unsigned char const OVERWINTER_PARAM_PREVOUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'P', 'r', 'e', 'v', 'o', 'u', 't', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_SEQUENCE[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'e', 'q', 'u', 'e', 'n', 'c', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_OUTPUTS[16] = { 'Z', 'c', 'a', 's', 'h', 'O', 'u', 't', 'p', 'u', 't', 's', 'H', 'a', 's', 'h' }; -unsigned char const OVERWINTER_PARAM_SIGHASH[16] = { 'Z', 'c', 'a', 's', 'h', 'S', 'i', 'g', 'H', 'a', 's', 'h', 0, 0, 0, 0 }; -unsigned char const OVERWINTER_NO_JOINSPLITS[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +unsigned char const OVERWINTER_PARAM_PREVOUTS[16] = { + 'Z', 'c', 'a', 's', 'h', 'P', 'r', 'e', + 'v', 'o', 'u', 't', 'H', 'a', 's', 'h'}; +unsigned char const OVERWINTER_PARAM_SEQUENCE[16] = { + 'Z', 'c', 'a', 's', 'h', 'S', 'e', 'q', + 'u', 'e', 'n', 'c', 'H', 'a', 's', 'h'}; +unsigned char const OVERWINTER_PARAM_OUTPUTS[16] = { + 'Z', 'c', 'a', 's', 'h', 'O', 'u', 't', + 'p', 'u', 't', 's', 'H', 'a', 's', 'h'}; +unsigned char const OVERWINTER_PARAM_SIGHASH[16] = { + 'Z', 'c', 'a', 's', 'h', 'S', 'i', 'g', 'H', 'a', 's', 'h', 0, 0, 0, 0}; +unsigned char const OVERWINTER_NO_JOINSPLITS[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // Check if fOverwintered flag is set and if nVersion is >= 0x03 -#define TRUSTED_INPUT_OVERWINTER ( (COIN_KIND == COIN_KIND_ZCASH || \ - COIN_KIND == COIN_KIND_ZCLASSIC || \ - COIN_KIND == COIN_KIND_KOMODO) && \ - (read_u32_le(context.transactionVersion, 0) & (1<<31)) && \ - (read_u32_le(context.transactionVersion, 0) ^ (1<<31)) >= 0x03 \ - ) +#define TRUSTED_INPUT_OVERWINTER \ + ((COIN_KIND == COIN_KIND_ZCASH || COIN_KIND == COIN_KIND_ZCLASSIC || \ + COIN_KIND == COIN_KIND_KOMODO) && \ + (read_u32_le(context.transactionVersion, 0) & (1 << 31)) && \ + (read_u32_le(context.transactionVersion, 0) ^ (1 << 31)) >= 0x03) static void check_transaction_available(unsigned char x) { - LEDGER_ASSERT(context.transactionDataRemaining >= x, "Check transaction available failed %d < %d\n", context.transactionDataRemaining, x); + LEDGER_ASSERT(context.transactionDataRemaining >= x, + "Check transaction available failed %d < %d\n", + context.transactionDataRemaining, x); } #define OP_HASH160 0xA9 @@ -59,848 +69,822 @@ static void check_transaction_available(unsigned char x) { #define OP_CHECKMULTISIG 0xAE static void transaction_offset(unsigned char value) { - if ((context.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", value, context.transactionBufferPointer); - if (context.usingOverwinter) { - LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); - } - else { - LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, - context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); - } - } - if ((context.transactionHashOption & - TRANSACTION_HASH_AUTHORIZATION) != 0) { - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", value, context.transactionBufferPointer); - LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashAuthorization.header, 0, - context.transactionBufferPointer, value, NULL, 0) == CX_OK, "Hash Failed"); + if ((context.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", value, + context.transactionBufferPointer); + if (context.usingOverwinter) { + LEDGER_ASSERT( + cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.transactionBufferPointer, value, NULL, + 0) == CX_OK, + "Hash Failed"); + } else { + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashFull.sha256.header, + 0, context.transactionBufferPointer, value, + NULL, 0) == CX_OK, + "Hash Failed"); } + } + if ((context.transactionHashOption & TRANSACTION_HASH_AUTHORIZATION) != 0) { + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", value, + context.transactionBufferPointer); + LEDGER_ASSERT(cx_hash_no_throw(&context.transactionHashAuthorization.header, + 0, context.transactionBufferPointer, value, + NULL, 0) == CX_OK, + "Hash Failed"); + } } static void transaction_offset_increase(unsigned char value) { - transaction_offset(value); - context.transactionBufferPointer += value; - context.transactionDataRemaining -= value; + transaction_offset(value); + context.transactionBufferPointer += value; + context.transactionDataRemaining -= value; } static unsigned long int transaction_get_varint(void) { - unsigned char firstByte; - check_transaction_available(1); - firstByte = *context.transactionBufferPointer; - if (firstByte < 0xFD) { - transaction_offset_increase(1); - return firstByte; - } else if (firstByte == 0xFD) { - unsigned long int result; - transaction_offset_increase(1); - check_transaction_available(2); - result = - (unsigned long int)(*context.transactionBufferPointer) | - ((unsigned long int)(*(context.transactionBufferPointer + - 1)) - << 8); - transaction_offset_increase(2); - return result; - } else if (firstByte == 0xFE) { - unsigned long int result; - transaction_offset_increase(1); + unsigned char firstByte; + check_transaction_available(1); + firstByte = *context.transactionBufferPointer; + if (firstByte < 0xFD) { + transaction_offset_increase(1); + return firstByte; + } else if (firstByte == 0xFD) { + unsigned long int result; + transaction_offset_increase(1); + check_transaction_available(2); + result = + (unsigned long int)(*context.transactionBufferPointer) | + ((unsigned long int)(*(context.transactionBufferPointer + 1)) << 8); + transaction_offset_increase(2); + return result; + } else if (firstByte == 0xFE) { + unsigned long int result; + transaction_offset_increase(1); + check_transaction_available(4); + result = read_u32_le(context.transactionBufferPointer, 0); + transaction_offset_increase(4); + return result; + } else { + LEDGER_ASSERT(false, "Varint parsing failed"); + __builtin_unreachable(); + } +} + +void transaction_parse(unsigned char parseMode) { + for (;;) { + switch (context.transactionContext.transactionState) { + case TRANSACTION_NONE: { + PRINTF("Init transaction parser\n"); + // Reset transaction state + context.transactionContext.transactionRemainingInputsOutputs = 0; + context.transactionContext.transactionCurrentInputOutput = 0; + context.transactionContext.scriptRemaining = 0; + memset(context.transactionContext.transactionAmount, 0, + sizeof(context.transactionContext.transactionAmount)); + // TODO : transactionControlFid + // Reset hashes + if (context.usingOverwinter) { + if (context.segwitParsedOnce) { + uint8_t parameters[16]; + memmove(parameters, OVERWINTER_PARAM_SIGHASH, 16); + if (COIN_KIND == COIN_KIND_ZCLASSIC) { + write_u32_le(parameters, 12, CONSENSUS_BRANCH_ID_ZCLASSIC); + } else { + write_u32_le(parameters, 12, + context.usingOverwinter == + ZCASH_USING_OVERWINTER_SAPLING + ? (COIN_CONSENSUS_BRANCH_ID != 0 + ? COIN_CONSENSUS_BRANCH_ID + : CONSENSUS_BRANCH_ID_SAPLING) + : CONSENSUS_BRANCH_ID_OVERWINTER); + } + if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, + 256, NULL, 0, parameters, 16)) { + goto fail; + } + } + } else { + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { + goto fail; + } + } + if (cx_sha256_init_no_throw(&context.transactionHashAuthorization)) { + goto fail; + } + if (context.usingSegwit) { + context.transactionHashOption = 0; + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_blake2b_init2_no_throw( + &context.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, + (uint8_t *)OVERWINTER_PARAM_PREVOUTS, 16)) { + goto fail; + } + if (cx_blake2b_init2_no_throw( + &context.transactionHashFull.blake2b, 256, NULL, 0, + (uint8_t *)OVERWINTER_PARAM_SEQUENCE, 16)) { + goto fail; + } + } else { + if (cx_sha256_init_no_throw( + &context.segwit.hash.hashPrevouts.sha256)) { + goto fail; + } + } + } else { + PRINTF("Resume SegWit hash\n"); + PRINTF("SEGWIT Version\n%.*H\n", sizeof(context.transactionVersion), + context.transactionVersion); + PRINTF("SEGWIT HashedPrevouts\n%.*H\n", + sizeof(context.segwit.cache.hashedPrevouts), + context.segwit.cache.hashedPrevouts); + PRINTF("SEGWIT HashedSequence\n%.*H\n", + sizeof(context.segwit.cache.hashedSequence), + context.segwit.cache.hashedSequence); + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.transactionVersion, + sizeof(context.transactionVersion), NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.nVersionGroupId, + sizeof(context.nVersionGroupId), NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.segwit.cache.hashedPrevouts, + sizeof(context.segwit.cache.hashedPrevouts), + NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.segwit.cache.hashedSequence, + sizeof(context.segwit.cache.hashedSequence), + NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.segwit.cache.hashedOutputs, + sizeof(context.segwit.cache.hashedOutputs), + NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { + goto fail; + } + if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, + 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, + 0)) { // sapling hashShieldedSpend) + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, + 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, + 0)) { // sapling hashShieldedOutputs + goto fail; + } + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.nLockTime, sizeof(context.nLockTime), + NULL, 0)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.nExpiryHeight, + sizeof(context.nExpiryHeight), NULL, 0)) { + goto fail; + } + if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { + unsigned char valueBalance[8]; + memset(valueBalance, 0, sizeof(valueBalance)); + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, + 0, valueBalance, sizeof(valueBalance), NULL, + 0)) { // sapling valueBalance + goto fail; + } + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.sigHashType, + sizeof(context.sigHashType), NULL, 0)) { + goto fail; + } + } else { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", + sizeof(context.transactionVersion), + context.transactionVersion); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.transactionVersion, + sizeof(context.transactionVersion), NULL, 0)) { + goto fail; + } + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", + sizeof(context.segwit.cache.hashedPrevouts), + context.segwit.cache.hashedPrevouts); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedPrevouts, + sizeof(context.segwit.cache.hashedPrevouts), + NULL, 0)) { + goto fail; + } + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", + sizeof(context.segwit.cache.hashedSequence), + context.segwit.cache.hashedSequence); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedSequence, + sizeof(context.segwit.cache.hashedSequence), + NULL, 0)) { + goto fail; + } + PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", + sizeof(context.segwit.cache), + (unsigned char *)&context.segwit.cache); + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, + 0, (unsigned char *)&context.segwit.cache, + sizeof(context.segwit.cache), NULL, 0)) { + goto fail; + } + } + } + } + // Parse the beginning of the transaction + // Version + check_transaction_available(4); + memmove(context.transactionVersion, context.transactionBufferPointer, 4); + transaction_offset_increase(4); + + if (context.usingOverwinter || TRUSTED_INPUT_OVERWINTER) { + // nVersionGroupId check_transaction_available(4); - result = read_u32_le(context.transactionBufferPointer, 0); + memmove(context.nVersionGroupId, context.transactionBufferPointer, 4); transaction_offset_increase(4); - return result; - } else { - LEDGER_ASSERT(false, "Varint parsing failed"); - __builtin_unreachable(); + } + + if (COIN_FLAGS & FLAG_PEERCOIN_SUPPORT) { + if (((COIN_FAMILY == FAMILY_PEERCOIN && + (context.transactionVersion[0] < 3))) || + ((COIN_FAMILY == FAMILY_STEALTH) && + (context.transactionVersion[0] < 2))) { + // Timestamp + check_transaction_available(4); + transaction_offset_increase(4); + } + } + + // Number of inputs + context.transactionContext.transactionRemainingInputsOutputs = + transaction_get_varint(); + PRINTF("Number of inputs : %d\n", + context.transactionContext.transactionRemainingInputsOutputs); + if (G_called_from_swap && parseMode == PARSE_MODE_SIGNATURE) { + // remember number of inputs to know when to exit from library + // we will count number of already signed inputs and compare with this + // value As there are a lot of different states in which we can have + // different number of input (when for ex. we sign segregated witness) + if (vars.swap_data.totalNumberOfInputs == 0) { + vars.swap_data.totalNumberOfInputs = + context.transactionContext.transactionRemainingInputsOutputs; + } + // Reseting the flag, because we should check address ones for each + // input + vars.swap_data.was_address_checked = 0; + } + // Ready to proceed + context.transactionContext.transactionState = + TRANSACTION_DEFINED_WAIT_INPUT; + + __attribute__((fallthrough)); } -} -void transaction_parse(unsigned char parseMode) { - for (;;) { - switch (context.transactionContext.transactionState) { - case TRANSACTION_NONE: { - PRINTF("Init transaction parser\n"); - // Reset transaction state - context.transactionContext - .transactionRemainingInputsOutputs = 0; - context.transactionContext - .transactionCurrentInputOutput = 0; - context.transactionContext.scriptRemaining = 0; - memset( - context.transactionContext.transactionAmount, - 0, sizeof(context.transactionContext - .transactionAmount)); - // TODO : transactionControlFid - // Reset hashes - if (context.usingOverwinter) { - if (context.segwitParsedOnce) { - uint8_t parameters[16]; - memmove(parameters, OVERWINTER_PARAM_SIGHASH, 16); - if (COIN_KIND == COIN_KIND_ZCLASSIC) { - write_u32_le(parameters, 12, CONSENSUS_BRANCH_ID_ZCLASSIC); - } - else { - write_u32_le(parameters, 12, - context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING ? - (COIN_CONSENSUS_BRANCH_ID != 0 ? COIN_CONSENSUS_BRANCH_ID : CONSENSUS_BRANCH_ID_SAPLING) : CONSENSUS_BRANCH_ID_OVERWINTER); - } - if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, parameters, 16)) { - goto fail; - } - } - } - else { - if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { - goto fail; - } - } - if (cx_sha256_init_no_throw( - &context.transactionHashAuthorization)) { - goto fail; - } - if (context.usingSegwit) { - context.transactionHashOption = 0; - if (!context.segwitParsedOnce) { - if (context.usingOverwinter) { - if (cx_blake2b_init2_no_throw(&context.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_PREVOUTS, 16)) { - goto fail; - } - if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_SEQUENCE, 16)) { - goto fail; - } - } - else { - if (cx_sha256_init_no_throw( - &context.segwit.hash.hashPrevouts.sha256)) { - goto fail; - } - } - } else { - PRINTF("Resume SegWit hash\n"); - PRINTF("SEGWIT Version\n%.*H\n",sizeof(context.transactionVersion),context.transactionVersion); - PRINTF("SEGWIT HashedPrevouts\n%.*H\n",sizeof(context.segwit.cache.hashedPrevouts),context.segwit.cache.hashedPrevouts); - PRINTF("SEGWIT HashedSequence\n%.*H\n",sizeof(context.segwit.cache.hashedSequence),context.segwit.cache.hashedSequence); - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionVersion, sizeof(context.transactionVersion), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nVersionGroupId, sizeof(context.nVersionGroupId), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedPrevouts, sizeof(context.segwit.cache.hashedPrevouts), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedSequence, sizeof(context.segwit.cache.hashedSequence), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.segwit.cache.hashedOutputs, sizeof(context.segwit.cache.hashedOutputs), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { - goto fail; - } - if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedSpend) - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, OVERWINTER_NO_JOINSPLITS, 32, NULL, 0)) { // sapling hashShieldedOutputs - goto fail; - } - - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nLockTime, sizeof(context.nLockTime), NULL, 0)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.nExpiryHeight, sizeof(context.nExpiryHeight), NULL, 0)) { - goto fail; - } - if (context.usingOverwinter == ZCASH_USING_OVERWINTER_SAPLING) { - unsigned char valueBalance[8]; - memset(valueBalance, 0, sizeof(valueBalance)); - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, valueBalance, sizeof(valueBalance), NULL, 0)) { // sapling valueBalance - goto fail; - } - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.sigHashType, sizeof(context.sigHashType), NULL, 0)) { - goto fail; - } - } - else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.transactionVersion), context.transactionVersion); - if (cx_hash_no_throw( - &context.transactionHashFull.sha256.header, 0, - context.transactionVersion, - sizeof(context.transactionVersion), - NULL, 0)) { - goto fail; - } - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.segwit.cache.hashedPrevouts), context.segwit.cache.hashedPrevouts); - if (cx_hash_no_throw( - &context.transactionHashFull.sha256.header, 0, - context.segwit.cache.hashedPrevouts, - sizeof(context.segwit.cache - .hashedPrevouts), - NULL, 0)) { - goto fail; - } - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.segwit.cache.hashedSequence), context.segwit.cache.hashedSequence); - if (cx_hash_no_throw( - &context.transactionHashFull.sha256.header, 0, - context.segwit.cache.hashedSequence, - sizeof(context.segwit.cache - .hashedSequence), - NULL, 0)) { - goto fail; - } - PRINTF("--- ADD TO HASH AUTH:\n%.*H\n", sizeof(context.segwit.cache), (unsigned char *)&context.segwit.cache); - if (cx_hash_no_throw(&context - .transactionHashAuthorization.header, - 0, - (unsigned char *)&context - .segwit.cache, - sizeof(context.segwit.cache), - NULL, 0)) { - goto fail; - } - } - } - } - // Parse the beginning of the transaction - // Version - check_transaction_available(4); - memmove(context.transactionVersion, - context.transactionBufferPointer, 4); - transaction_offset_increase(4); - - if (context.usingOverwinter || - TRUSTED_INPUT_OVERWINTER) { - // nVersionGroupId - check_transaction_available(4); - memmove(context.nVersionGroupId, - context.transactionBufferPointer, 4); - transaction_offset_increase(4); - } - - if (COIN_FLAGS & FLAG_PEERCOIN_SUPPORT) { - if (((COIN_FAMILY == - FAMILY_PEERCOIN && - (context.transactionVersion[0] < 3))) || - ((COIN_FAMILY == FAMILY_STEALTH) && - (context.transactionVersion[0] < 2))) { - // Timestamp - check_transaction_available(4); - transaction_offset_increase(4); - } - } - - // Number of inputs - context.transactionContext - .transactionRemainingInputsOutputs = - transaction_get_varint(); - PRINTF("Number of inputs : %d\n", context.transactionContext.transactionRemainingInputsOutputs); - if (G_called_from_swap && parseMode == PARSE_MODE_SIGNATURE) { - // remember number of inputs to know when to exit from library - // we will count number of already signed inputs and compare with this value - // As there are a lot of different states in which we can have different number of input - // (when for ex. we sign segregated witness) - if (vars.swap_data.totalNumberOfInputs == 0) { - vars.swap_data.totalNumberOfInputs = - context.transactionContext.transactionRemainingInputsOutputs; - } - // Reseting the flag, because we should check address ones for each input - vars.swap_data.was_address_checked = 0; - } - // Ready to proceed - context.transactionContext.transactionState = - TRANSACTION_DEFINED_WAIT_INPUT; - - __attribute__((fallthrough)); - } + case TRANSACTION_DEFINED_WAIT_INPUT: { + unsigned char trustedInputFlag = 1; + PRINTF("Process input\n"); + if (context.transactionContext.transactionRemainingInputsOutputs == 0) { + // No more inputs to hash, move forward + context.transactionContext.transactionState = + TRANSACTION_INPUT_HASHING_DONE; + continue; + } + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + // Proceed with the next input + if (parseMode == PARSE_MODE_TRUSTED_INPUT) { + check_transaction_available(36); // prevout : 32 hash + 4 index + transaction_offset_increase(36); + } + if (parseMode == PARSE_MODE_SIGNATURE) { + unsigned char trustedInputLength; + unsigned char trustedInput[TRUSTED_INPUT_TOTAL_SIZE]; + unsigned char amount[8]; + unsigned char *savePointer; + + // Expect the trusted input flag and trusted input length + check_transaction_available(2); + switch (*context.transactionBufferPointer) { + case 0: + if (context.usingSegwit) { + PRINTF("Non trusted input used in segwit mode\n"); + goto fail; + } + trustedInputFlag = 0; + break; + case 1: + if (context.usingSegwit) { + // Segwit inputs can be passed as TrustedInput also + PRINTF("Trusted input used in segwit mode\n"); + } + trustedInputFlag = 1; + break; + case 2: + if (!context.usingSegwit) { + PRINTF("Segwit input not used in segwit mode\n"); + goto fail; + } + trustedInputFlag = 0; + break; + default: + PRINTF("Invalid trusted input flag\n"); + goto fail; + } + // Check TrustedInput (TI) integrity, be it a non-segwit TI or a segwit + // TI + if (trustedInputFlag) { + trustedInputLength = *(context.transactionBufferPointer + 1); + if ((trustedInputLength > sizeof(trustedInput)) || + (trustedInputLength < 8)) { + PRINTF("Invalid trusted input size\n"); + goto fail; + } + + check_transaction_available(2 + trustedInputLength); + // Check TrustedInput Hmac + cx_hmac_sha256((uint8_t *)g_nvram_data.bkp.trustedinput_key, + sizeof(g_nvram_data.bkp.trustedinput_key), + context.transactionBufferPointer + 2, + trustedInputLength - 8, trustedInput, + trustedInputLength); + PRINTF("====> Input HMAC: %.*H\n", 8, + context.transactionBufferPointer + 2 + trustedInputLength - 8); + PRINTF("====> Computed HMAC: %.*H\n", 8, trustedInput); + + if (os_secure_memcmp(trustedInput, // Contains computed Hmac for now + context.transactionBufferPointer + 2 + + trustedInputLength - 8, + 8) != 0) { + PRINTF("Invalid signature\n"); + goto fail; + } + // Hmac is valid. If TrustedInput contains a segwit input, update data + // pointer & length to fake the parser into believing a normal segwit + // input was received. Do not use transaction_offset_increase() here + // as it could update the hash being computed. + if (context.usingSegwit) { + // Overwrite the no longer needed HMAC's 1st byte w/ the input + // script length byte. + *(context.transactionBufferPointer + 1 + TRUSTED_INPUT_SIZE + 1) = + *(context.transactionBufferPointer + 1 + + TRUSTED_INPUT_TOTAL_SIZE + 1); + // Set tx data pointer on TI header's (i.e. 0x38||0x32||0x00||Nonce + // (2B)) last byte before prevout tx hash. Also remove HMAC size + // from remaining data length. + context.transactionBufferPointer += 5; + context.transactionDataRemaining -= (5 + 8); + } + } + // Handle pure segwit inputs, whether trusted or not (i.e. + // InputHashStart 1st APDU's P2==02 & data[0]=={0x01, 0x02}) + if (context.usingSegwit) { + transaction_offset_increase(1); // Set tx pointer on 1st byte of hash + check_transaction_available(36); // prevout : 32 hash + 4 index + if (!context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw( + &context.segwit.hash.hashPrevouts.blake2b.header, 0, + context.transactionBufferPointer, 36, NULL, 0)) { + goto fail; + } + } else { + if (cx_hash_no_throw( + &context.segwit.hash.hashPrevouts.sha256.header, 0, + context.transactionBufferPointer, 36, NULL, 0)) { + goto fail; + } + } + transaction_offset_increase(36); + check_transaction_available(8); // update amount + swap_bytes(amount, context.transactionBufferPointer, 8); + if (transaction_amount_add_be( + context.transactionContext.transactionAmount, + context.transactionContext.transactionAmount, amount)) { + PRINTF("Overflow\n"); + goto fail; + } + PRINTF("Adding amount\n%.*H\n", 8, + context.transactionBufferPointer); + PRINTF("New amount\n%.*H\n", 8, + context.transactionContext.transactionAmount); + transaction_offset_increase(8); + } else { + context.transactionHashOption = TRANSACTION_HASH_FULL; + transaction_offset_increase(36); + context.transactionHashOption = 0; + check_transaction_available(8); // save amount + memmove(context.inputValue, context.transactionBufferPointer, 8); + transaction_offset_increase(8); + context.transactionHashOption = TRANSACTION_HASH_FULL; + } + } + // Handle non-segwit inputs (i.e. InputHashStart 1st APDU's P2==00 && + // data[0]==0x00) + else if (!trustedInputFlag) { + PRINTF("Untrusted input not authorized\n"); + goto fail; + } + // Handle non-segwit TrustedInput (i.e. InputHashStart 1st APDU's P2==00 + // & data[0]==0x01) + else if (trustedInputFlag && !context.usingSegwit) { + memmove(trustedInput, context.transactionBufferPointer + 2, + trustedInputLength - 8); + if (trustedInput[0] != MAGIC_TRUSTED_INPUT) { + PRINTF("Failed to verify trusted input signature\n"); + goto fail; + } + // Update the hash with prevout data + savePointer = context.transactionBufferPointer; + context.transactionBufferPointer = trustedInput + 4; + PRINTF("Trusted input hash\n%.*H\n", 36, + context.transactionBufferPointer); + transaction_offset(36); + + context.transactionBufferPointer = + savePointer + (2 + trustedInputLength); + context.transactionDataRemaining -= (2 + trustedInputLength); + + // Update the amount + + swap_bytes(amount, trustedInput + 40, 8); + if (transaction_amount_add_be( + context.transactionContext.transactionAmount, + context.transactionContext.transactionAmount, amount)) { + PRINTF("Overflow\n"); + goto fail; + } + + PRINTF("Adding amount\n%.*H\n", 8, (trustedInput + 40)); + PRINTF("New amount\n%.*H\n", 8, + context.transactionContext.transactionAmount); + } - case TRANSACTION_DEFINED_WAIT_INPUT: { - unsigned char trustedInputFlag = 1; - PRINTF("Process input\n"); - if (context.transactionContext - .transactionRemainingInputsOutputs == 0) { - // No more inputs to hash, move forward - context.transactionContext.transactionState = - TRANSACTION_INPUT_HASHING_DONE; - continue; - } - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - // Proceed with the next input - if (parseMode == PARSE_MODE_TRUSTED_INPUT) { - check_transaction_available( - 36); // prevout : 32 hash + 4 index - transaction_offset_increase(36); - } - if (parseMode == PARSE_MODE_SIGNATURE) { - unsigned char trustedInputLength; - unsigned char trustedInput[TRUSTED_INPUT_TOTAL_SIZE]; - unsigned char amount[8]; - unsigned char *savePointer; - - // Expect the trusted input flag and trusted input length - check_transaction_available(2); - switch (*context.transactionBufferPointer) { - case 0: - if (context.usingSegwit) { - PRINTF("Non trusted input used in segwit mode\n"); - goto fail; - } - trustedInputFlag = 0; - break; - case 1: - if (context.usingSegwit) { - // Segwit inputs can be passed as TrustedInput also - PRINTF("Trusted input used in segwit mode\n"); - } - trustedInputFlag = 1; - break; - case 2: - if (!context.usingSegwit) { - PRINTF("Segwit input not used in segwit mode\n"); - goto fail; - } - trustedInputFlag = 0; - break; - default: - PRINTF("Invalid trusted input flag\n"); - goto fail; - } - // Check TrustedInput (TI) integrity, be it a non-segwit TI or a segwit TI - if (trustedInputFlag) { - trustedInputLength = *( - context.transactionBufferPointer + 1); - if ((trustedInputLength > sizeof(trustedInput)) || - (trustedInputLength < 8)) { - PRINTF("Invalid trusted input size\n"); - goto fail; - } - - check_transaction_available(2 + trustedInputLength); - // Check TrustedInput Hmac - cx_hmac_sha256( - (uint8_t *)g_nvram_data.bkp.trustedinput_key, - sizeof(g_nvram_data.bkp.trustedinput_key), - context.transactionBufferPointer + 2, - trustedInputLength - 8, trustedInput, trustedInputLength); - PRINTF("====> Input HMAC: %.*H\n", 8, context.transactionBufferPointer + 2 + trustedInputLength - 8); - PRINTF("====> Computed HMAC: %.*H\n", 8, trustedInput); - - if (os_secure_memcmp( - trustedInput, // Contains computed Hmac for now - context.transactionBufferPointer + - 2 + trustedInputLength - 8, - 8) != 0) { - PRINTF("Invalid signature\n"); - goto fail; - } - // Hmac is valid. If TrustedInput contains a segwit input, update data pointer & length - // to fake the parser into believing a normal segwit input was received. Do not use - // transaction_offset_increase() here as it could update the hash being computed. - if (context.usingSegwit) { - // Overwrite the no longer needed HMAC's 1st byte w/ the input script length byte. - *(context.transactionBufferPointer + 1 + TRUSTED_INPUT_SIZE + 1) = - *(context.transactionBufferPointer + 1 + TRUSTED_INPUT_TOTAL_SIZE + 1); - // Set tx data pointer on TI header's (i.e. 0x38||0x32||0x00||Nonce (2B)) last byte - // before prevout tx hash. Also remove HMAC size from remaining data length. - context.transactionBufferPointer += 5; - context.transactionDataRemaining -= (5+8); - } - } - // Handle pure segwit inputs, whether trusted or not (i.e. InputHashStart 1st APDU's P2==02 - // & data[0]=={0x01, 0x02}) - if (context.usingSegwit) { - transaction_offset_increase(1); // Set tx pointer on 1st byte of hash - check_transaction_available( - 36); // prevout : 32 hash + 4 index - if (!context.segwitParsedOnce) { - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts.blake2b.header, 0, context.transactionBufferPointer, 36, NULL, 0)) { - goto fail; - } - } - else { - if (cx_hash_no_throw( - &context.segwit.hash.hashPrevouts - .sha256.header, - 0, - context.transactionBufferPointer, - 36, NULL, 0)) { - goto fail; - } - } - transaction_offset_increase(36); - check_transaction_available(8); // update amount - swap_bytes( - amount, - context.transactionBufferPointer, - 8); - if (transaction_amount_add_be( - context.transactionContext - .transactionAmount, - context.transactionContext - .transactionAmount, - amount)) { - PRINTF("Overflow\n"); - goto fail; - } - PRINTF("Adding amount\n%.*H\n",8,context.transactionBufferPointer); - PRINTF("New amount\n%.*H\n",8,context.transactionContext.transactionAmount); - transaction_offset_increase(8); - } else { - context.transactionHashOption = - TRANSACTION_HASH_FULL; - transaction_offset_increase(36); - context.transactionHashOption = 0; - check_transaction_available(8); // save amount - memmove( - context.inputValue, - context.transactionBufferPointer, - 8); - transaction_offset_increase(8); - context.transactionHashOption = - TRANSACTION_HASH_FULL; - } - } - // Handle non-segwit inputs (i.e. InputHashStart 1st APDU's P2==00 && data[0]==0x00) - else if (!trustedInputFlag) { - PRINTF("Untrusted input not authorized\n"); - goto fail; - } - // Handle non-segwit TrustedInput (i.e. InputHashStart 1st APDU's P2==00 & data[0]==0x01) - else if (trustedInputFlag && !context.usingSegwit) { - memmove( - trustedInput, - context.transactionBufferPointer + 2, - trustedInputLength - 8); - if (trustedInput[0] != MAGIC_TRUSTED_INPUT) { - PRINTF("Failed to verify trusted input signature\n"); - goto fail; - } - // Update the hash with prevout data - savePointer = - context.transactionBufferPointer; - context.transactionBufferPointer = - trustedInput + 4; - PRINTF("Trusted input hash\n%.*H\n",36,context.transactionBufferPointer); - transaction_offset(36); - - context.transactionBufferPointer = - savePointer + (2 + trustedInputLength); - context.transactionDataRemaining -= - (2 + trustedInputLength); - - // Update the amount - - swap_bytes(amount, trustedInput + 40, 8); - if (transaction_amount_add_be( - context.transactionContext - .transactionAmount, - context.transactionContext - .transactionAmount, - amount)) { - PRINTF("Overflow\n"); - goto fail; - } - - PRINTF("Adding amount\n%.*H\n",8,(trustedInput + 40)); - PRINTF("New amount\n%.*H\n",8,context.transactionContext.transactionAmount); - } - - if (!context.usingSegwit) { - // Do not include the input script length + value in - // the authentication hash - context.transactionHashOption = - TRANSACTION_HASH_FULL; - } - } - // Read the script length - context.transactionContext.scriptRemaining = - transaction_get_varint(); - PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); - - if ((parseMode == PARSE_MODE_SIGNATURE) && - !trustedInputFlag && !context.usingSegwit) { - // Only proceeds if this is not to be signed - so length - // should be null - if (context.transactionContext - .scriptRemaining != 0) { - PRINTF("Request to sign relaxed input\n"); - goto fail; - } - } - // Move on - context.transactionContext.transactionState = - TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT; - - __attribute__((fallthrough)); - } - case TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT: { - unsigned char dataAvailable; - PRINTF("Process input script, remaining %d\n", context.transactionContext.scriptRemaining); - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - // Scan for P2SH consumption - huge shortcut, but fine - // enough - // Also usable in SegWit mode - if (context.transactionContext.scriptRemaining == - 1) { - if (*context.transactionBufferPointer == - OP_CHECKMULTISIG) { - PRINTF("Marking P2SH consumption\n"); - context.transactionContext.consumeP2SH = 1; - } else { - // When using the P2SH shortcut, all inputs must use - // P2SH - PRINTF("Disabling P2SH consumption\n"); - context.transactionContext.consumeP2SH = 0; - } - transaction_offset_increase(1); - context.transactionContext.scriptRemaining--; - } - - if (context.transactionContext.scriptRemaining == - 0) { - if (parseMode == PARSE_MODE_SIGNATURE) { - if (!context.usingSegwit) { - // Restore dual hash for signature + - // authentication - context.transactionHashOption = - TRANSACTION_HASH_BOTH; - } else { - if (context.segwitParsedOnce) { - // Append the saved value - PRINTF("SEGWIT Add value\n%.*H\n",8,context.inputValue); - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.inputValue, 8, NULL, 0)) { - goto fail; - } - } - else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(context.inputValue), context.inputValue); - if (cx_hash_no_throw(&context - .transactionHashFull.sha256.header, - 0, context.inputValue, 8, - NULL, 0)) { - goto fail; - } - } - } - } - } - // Sequence - check_transaction_available(4); - if (context.usingSegwit && - !context.segwitParsedOnce) { - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, context.transactionBufferPointer, 4, NULL, 0)) { - goto fail; - } - } - else { - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", 4, context.transactionBufferPointer); - if (cx_hash_no_throw(&context.transactionHashFull - .sha256.header, - 0, - context.transactionBufferPointer, - 4, NULL, 0)) { - goto fail; - } - } - } - transaction_offset_increase(4); - // Move to next input - context.transactionContext - .transactionRemainingInputsOutputs--; - context.transactionContext - .transactionCurrentInputOutput++; - context.transactionContext.transactionState = - TRANSACTION_DEFINED_WAIT_INPUT; - continue; - } - // Save the last script byte for the P2SH check - dataAvailable = - (context.transactionDataRemaining > - context.transactionContext - .scriptRemaining - - 1 - ? context.transactionContext - .scriptRemaining - - 1 - : context.transactionDataRemaining); - if (dataAvailable == 0) { - goto ok; - } - transaction_offset_increase(dataAvailable); - context.transactionContext.scriptRemaining -= - dataAvailable; - break; - } - case TRANSACTION_INPUT_HASHING_DONE: { - PRINTF("Input hashing done\n"); - if (parseMode == PARSE_MODE_SIGNATURE) { - // inputs have been prepared, stop the parsing here - if (context.usingSegwit && - !context.segwitParsedOnce) { - unsigned char hashedPrevouts[32]; - unsigned char hashedSequence[32]; - // Flush the cache - if (context.usingOverwinter) { - if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, CX_LAST, hashedSequence, 0, hashedSequence, 32)) { - goto fail; - } - } - else { - if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts - .sha256.header, - CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32)) { - goto fail; - } - if (cx_sha256_init_no_throw( - &context.segwit.hash.hashPrevouts.sha256)) { - goto fail; - } - if (cx_hash_no_throw(&context.segwit.hash.hashPrevouts - .sha256.header, - CX_LAST, hashedPrevouts, - sizeof(hashedPrevouts), hashedPrevouts, 32)) { - goto fail; - } - if (cx_hash_no_throw(&context.transactionHashFull - .sha256.header, - CX_LAST, hashedSequence, 0, hashedSequence, 32)) { - goto fail; - } - if (cx_sha256_init_no_throw( - &context.transactionHashFull.sha256)) { - goto fail; - } - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(hashedSequence), hashedSequence); - if (cx_hash_no_throw(&context.transactionHashFull - .sha256.header, - CX_LAST, hashedSequence, - sizeof(hashedSequence), hashedSequence, 32)) { - goto fail; - } - - } - memmove( - context.segwit.cache.hashedPrevouts, - hashedPrevouts, sizeof(hashedPrevouts)); - memmove( - context.segwit.cache.hashedSequence, - hashedSequence, sizeof(hashedSequence)); - PRINTF("hashPrevout\n%.*H\n",32,context.segwit.cache.hashedPrevouts); - PRINTF("hashSequence\n%.*H\n",32,context.segwit.cache.hashedSequence); - } - if (context.usingSegwit && - context.segwitParsedOnce) { - if (!context.usingOverwinter) { - PRINTF("SEGWIT hashedOutputs\n%.*H\n",sizeof(context.segwit.cache.hashedOutputs),context.segwit.cache.hashedOutputs); - if (cx_hash_no_throw( - &context.transactionHashFull.sha256.header, 0, - context.segwit.cache.hashedOutputs, - sizeof(context.segwit.cache - .hashedOutputs), - NULL, 0)) { - goto fail; - } - } - context.transactionContext - .transactionState = - TRANSACTION_SIGN_READY; - } else { - context.transactionContext - .transactionState = - TRANSACTION_PRESIGN_READY; - if (context.usingOverwinter) { - if (cx_blake2b_init2_no_throw(&context.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_OUTPUTS, 16)) { - goto fail; - } - } - else - if (context.usingSegwit) { - if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { - goto fail; - } - } - } - continue; - } - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - // Number of outputs - context.transactionContext - .transactionRemainingInputsOutputs = - transaction_get_varint(); - context.transactionContext - .transactionCurrentInputOutput = 0; - PRINTF("Number of outputs : %d\n", - context.transactionContext.transactionRemainingInputsOutputs); - // Ready to proceed - context.transactionContext.transactionState = - TRANSACTION_DEFINED_WAIT_OUTPUT; - - __attribute__((fallthrough)); - } - case TRANSACTION_DEFINED_WAIT_OUTPUT: { - if (context.transactionContext - .transactionRemainingInputsOutputs == 0) { - // No more outputs to hash, move forward - context.transactionContext.transactionState = - TRANSACTION_OUTPUT_HASHING_DONE; - continue; - } - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - // Amount - check_transaction_available(8); - if ((parseMode == PARSE_MODE_TRUSTED_INPUT) && - (context.transactionContext - .transactionCurrentInputOutput == - context.transactionTargetInput)) { - // Save the amount - memmove(context.transactionContext - .transactionAmount, - context.transactionBufferPointer, - 8); - context.trustedInputProcessed = 1; - } - transaction_offset_increase(8); - // Read the script length - context.transactionContext.scriptRemaining = - transaction_get_varint(); - - PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); - // Move on - context.transactionContext.transactionState = - TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT; - - __attribute__((fallthrough)); - } - case TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT: { - unsigned char dataAvailable; - PRINTF("Process output script, remaining %d\n", context.transactionContext.scriptRemaining); - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - if (context.transactionContext.scriptRemaining == - 0) { - // Move to next output - context.transactionContext - .transactionRemainingInputsOutputs--; - context.transactionContext - .transactionCurrentInputOutput++; - context.transactionContext.transactionState = - TRANSACTION_DEFINED_WAIT_OUTPUT; - continue; - } - dataAvailable = - (context.transactionDataRemaining > - context.transactionContext - .scriptRemaining - ? context.transactionContext - .scriptRemaining - : context.transactionDataRemaining); - if (dataAvailable == 0) { - goto ok; - } - transaction_offset_increase(dataAvailable); - context.transactionContext.scriptRemaining -= - dataAvailable; - break; - } - case TRANSACTION_OUTPUT_HASHING_DONE: { - PRINTF("Output hashing done\n"); - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - // Locktime - check_transaction_available(4); - transaction_offset_increase(4); - - if (context.transactionDataRemaining == 0) { - context.transactionContext.transactionState = - TRANSACTION_PARSED; - continue; - } else { - context.transactionHashOption = 0; - context.transactionContext.scriptRemaining = - transaction_get_varint(); - context.transactionHashOption = - TRANSACTION_HASH_FULL; - context.transactionContext.transactionState = - TRANSACTION_PROCESS_EXTRA; - continue; - } - } + if (!context.usingSegwit) { + // Do not include the input script length + value in + // the authentication hash + context.transactionHashOption = TRANSACTION_HASH_FULL; + } + } + // Read the script length + context.transactionContext.scriptRemaining = transaction_get_varint(); + PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); + + if ((parseMode == PARSE_MODE_SIGNATURE) && !trustedInputFlag && + !context.usingSegwit) { + // Only proceeds if this is not to be signed - so length + // should be null + if (context.transactionContext.scriptRemaining != 0) { + PRINTF("Request to sign relaxed input\n"); + goto fail; + } + } + // Move on + context.transactionContext.transactionState = + TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT; - case TRANSACTION_PROCESS_EXTRA: { - unsigned char dataAvailable; - - if (context.transactionContext.scriptRemaining == - 0) { - context.transactionContext.transactionState = - TRANSACTION_PARSED; - continue; - } - - if (context.transactionDataRemaining < 1) { - // No more data to read, ok - goto ok; - } - - dataAvailable = - (context.transactionDataRemaining > - context.transactionContext - .scriptRemaining - ? context.transactionContext - .scriptRemaining - : context.transactionDataRemaining); - if (dataAvailable == 0) { - goto ok; - } - transaction_offset_increase(dataAvailable); - context.transactionContext.scriptRemaining -= - dataAvailable; - break; + __attribute__((fallthrough)); + } + case TRANSACTION_INPUT_HASHING_IN_PROGRESS_INPUT_SCRIPT: { + unsigned char dataAvailable; + PRINTF("Process input script, remaining %d\n", + context.transactionContext.scriptRemaining); + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + // Scan for P2SH consumption - huge shortcut, but fine + // enough + // Also usable in SegWit mode + if (context.transactionContext.scriptRemaining == 1) { + if (*context.transactionBufferPointer == OP_CHECKMULTISIG) { + PRINTF("Marking P2SH consumption\n"); + context.transactionContext.consumeP2SH = 1; + } else { + // When using the P2SH shortcut, all inputs must use + // P2SH + PRINTF("Disabling P2SH consumption\n"); + context.transactionContext.consumeP2SH = 0; + } + transaction_offset_increase(1); + context.transactionContext.scriptRemaining--; + } + + if (context.transactionContext.scriptRemaining == 0) { + if (parseMode == PARSE_MODE_SIGNATURE) { + if (!context.usingSegwit) { + // Restore dual hash for signature + + // authentication + context.transactionHashOption = TRANSACTION_HASH_BOTH; + } else { + if (context.segwitParsedOnce) { + // Append the saved value + PRINTF("SEGWIT Add value\n%.*H\n", 8, context.inputValue); + if (context.usingOverwinter) { + if (cx_hash_no_throw( + &context.transactionHashFull.blake2b.header, 0, + context.inputValue, 8, NULL, 0)) { + goto fail; } - - case TRANSACTION_PARSED: { - PRINTF("Transaction parsed\n"); - goto ok; + } else { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", + sizeof(context.inputValue), context.inputValue); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + 0, context.inputValue, 8, NULL, 0)) { + goto fail; } + } + } + } + } + // Sequence + check_transaction_available(4); + if (context.usingSegwit && !context.segwitParsedOnce) { + if (context.usingOverwinter) { + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, 0, + context.transactionBufferPointer, 4, NULL, + 0)) { + goto fail; + } + } else { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", 4, + context.transactionBufferPointer); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.transactionBufferPointer, 4, NULL, + 0)) { + goto fail; + } + } + } + transaction_offset_increase(4); + // Move to next input + context.transactionContext.transactionRemainingInputsOutputs--; + context.transactionContext.transactionCurrentInputOutput++; + context.transactionContext.transactionState = + TRANSACTION_DEFINED_WAIT_INPUT; + continue; + } + // Save the last script byte for the P2SH check + dataAvailable = (context.transactionDataRemaining > + context.transactionContext.scriptRemaining - 1 + ? context.transactionContext.scriptRemaining - 1 + : context.transactionDataRemaining); + if (dataAvailable == 0) { + goto ok; + } + transaction_offset_increase(dataAvailable); + context.transactionContext.scriptRemaining -= dataAvailable; + break; + } + case TRANSACTION_INPUT_HASHING_DONE: { + PRINTF("Input hashing done\n"); + if (parseMode == PARSE_MODE_SIGNATURE) { + // inputs have been prepared, stop the parsing here + if (context.usingSegwit && !context.segwitParsedOnce) { + unsigned char hashedPrevouts[32]; + unsigned char hashedSequence[32]; + // Flush the cache + if (context.usingOverwinter) { + if (cx_hash_no_throw( + &context.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, + hashedPrevouts, 0, hashedPrevouts, 32)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.blake2b.header, + CX_LAST, hashedSequence, 0, hashedSequence, + 32)) { + goto fail; + } + } else { + if (cx_hash_no_throw( + &context.segwit.hash.hashPrevouts.sha256.header, CX_LAST, + hashedPrevouts, 0, hashedPrevouts, 32)) { + goto fail; + } + if (cx_sha256_init_no_throw( + &context.segwit.hash.hashPrevouts.sha256)) { + goto fail; + } + if (cx_hash_no_throw( + &context.segwit.hash.hashPrevouts.sha256.header, CX_LAST, + hashedPrevouts, sizeof(hashedPrevouts), hashedPrevouts, + 32)) { + goto fail; + } + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, hashedSequence, 0, hashedSequence, + 32)) { + goto fail; + } + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { + goto fail; + } + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(hashedSequence), + hashedSequence); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, + CX_LAST, hashedSequence, + sizeof(hashedSequence), hashedSequence, 32)) { + goto fail; + } + } + memmove(context.segwit.cache.hashedPrevouts, hashedPrevouts, + sizeof(hashedPrevouts)); + memmove(context.segwit.cache.hashedSequence, hashedSequence, + sizeof(hashedSequence)); + PRINTF("hashPrevout\n%.*H\n", 32, + context.segwit.cache.hashedPrevouts); + PRINTF("hashSequence\n%.*H\n", 32, + context.segwit.cache.hashedSequence); + } + if (context.usingSegwit && context.segwitParsedOnce) { + if (!context.usingOverwinter) { + PRINTF("SEGWIT hashedOutputs\n%.*H\n", + sizeof(context.segwit.cache.hashedOutputs), + context.segwit.cache.hashedOutputs); + if (cx_hash_no_throw(&context.transactionHashFull.sha256.header, 0, + context.segwit.cache.hashedOutputs, + sizeof(context.segwit.cache.hashedOutputs), + NULL, 0)) { + goto fail; + } + } + context.transactionContext.transactionState = TRANSACTION_SIGN_READY; + } else { + context.transactionContext.transactionState = + TRANSACTION_PRESIGN_READY; + if (context.usingOverwinter) { + if (cx_blake2b_init2_no_throw( + &context.transactionHashFull.blake2b, 256, NULL, 0, + (uint8_t *)OVERWINTER_PARAM_OUTPUTS, 16)) { + goto fail; + } + } else if (context.usingSegwit) { + if (cx_sha256_init_no_throw(&context.transactionHashFull.sha256)) { + goto fail; + } + } + } + continue; + } + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + // Number of outputs + context.transactionContext.transactionRemainingInputsOutputs = + transaction_get_varint(); + context.transactionContext.transactionCurrentInputOutput = 0; + PRINTF("Number of outputs : %d\n", + context.transactionContext.transactionRemainingInputsOutputs); + // Ready to proceed + context.transactionContext.transactionState = + TRANSACTION_DEFINED_WAIT_OUTPUT; + + __attribute__((fallthrough)); + } + case TRANSACTION_DEFINED_WAIT_OUTPUT: { + if (context.transactionContext.transactionRemainingInputsOutputs == 0) { + // No more outputs to hash, move forward + context.transactionContext.transactionState = + TRANSACTION_OUTPUT_HASHING_DONE; + continue; + } + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + // Amount + check_transaction_available(8); + if ((parseMode == PARSE_MODE_TRUSTED_INPUT) && + (context.transactionContext.transactionCurrentInputOutput == + context.transactionTargetInput)) { + // Save the amount + memmove(context.transactionContext.transactionAmount, + context.transactionBufferPointer, 8); + context.trustedInputProcessed = 1; + } + transaction_offset_increase(8); + // Read the script length + context.transactionContext.scriptRemaining = transaction_get_varint(); + + PRINTF("Script to read %d\n", context.transactionContext.scriptRemaining); + // Move on + context.transactionContext.transactionState = + TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT; + + __attribute__((fallthrough)); + } + case TRANSACTION_OUTPUT_HASHING_IN_PROGRESS_OUTPUT_SCRIPT: { + unsigned char dataAvailable; + PRINTF("Process output script, remaining %d\n", + context.transactionContext.scriptRemaining); + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + if (context.transactionContext.scriptRemaining == 0) { + // Move to next output + context.transactionContext.transactionRemainingInputsOutputs--; + context.transactionContext.transactionCurrentInputOutput++; + context.transactionContext.transactionState = + TRANSACTION_DEFINED_WAIT_OUTPUT; + continue; + } + dataAvailable = (context.transactionDataRemaining > + context.transactionContext.scriptRemaining + ? context.transactionContext.scriptRemaining + : context.transactionDataRemaining); + if (dataAvailable == 0) { + goto ok; + } + transaction_offset_increase(dataAvailable); + context.transactionContext.scriptRemaining -= dataAvailable; + break; + } + case TRANSACTION_OUTPUT_HASHING_DONE: { + PRINTF("Output hashing done\n"); + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + // Locktime + check_transaction_available(4); + transaction_offset_increase(4); + + if (context.transactionDataRemaining == 0) { + context.transactionContext.transactionState = TRANSACTION_PARSED; + continue; + } else { + context.transactionHashOption = 0; + context.transactionContext.scriptRemaining = transaction_get_varint(); + context.transactionHashOption = TRANSACTION_HASH_FULL; + context.transactionContext.transactionState = TRANSACTION_PROCESS_EXTRA; + continue; + } + } - case TRANSACTION_PRESIGN_READY: { - PRINTF("Presign ready\n"); - goto ok; - } + case TRANSACTION_PROCESS_EXTRA: { + unsigned char dataAvailable; + + if (context.transactionContext.scriptRemaining == 0) { + context.transactionContext.transactionState = TRANSACTION_PARSED; + continue; + } + + if (context.transactionDataRemaining < 1) { + // No more data to read, ok + goto ok; + } + + dataAvailable = (context.transactionDataRemaining > + context.transactionContext.scriptRemaining + ? context.transactionContext.scriptRemaining + : context.transactionDataRemaining); + if (dataAvailable == 0) { + goto ok; + } + transaction_offset_increase(dataAvailable); + context.transactionContext.scriptRemaining -= dataAvailable; + break; + } - case TRANSACTION_SIGN_READY: { - PRINTF("Sign ready\n"); - goto ok; - } - } - } + case TRANSACTION_PARSED: { + PRINTF("Transaction parsed\n"); + goto ok; + } + + case TRANSACTION_PRESIGN_READY: { + PRINTF("Presign ready\n"); + goto ok; + } - fail: - LEDGER_ASSERT(false, "Transaction parse - fail\n"); - ok: - return; + case TRANSACTION_SIGN_READY: { + PRINTF("Sign ready\n"); + goto ok; + } + } + } +fail: + LEDGER_ASSERT(false, "Transaction parse - fail\n"); +ok: + return; } diff --git a/lib-app-bitcoin/transaction.h b/lib-app-bitcoin/transaction.h index fa88a83b..249a5527 100644 --- a/lib-app-bitcoin/transaction.h +++ b/lib-app-bitcoin/transaction.h @@ -1,23 +1,22 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once - #define TRANSACTION_HASH_NONE 0x00 #define TRANSACTION_HASH_FULL 0x01 #define TRANSACTION_HASH_AUTHORIZATION 0x02 @@ -26,8 +25,7 @@ #define PARSE_MODE_TRUSTED_INPUT 0x01 #define PARSE_MODE_SIGNATURE 0x02 -#define TRUSTED_INPUT_SIZE 48 +#define TRUSTED_INPUT_SIZE 48 #define TRUSTED_INPUT_TOTAL_SIZE (TRUSTED_INPUT_SIZE + 8) void transaction_parse(unsigned char parseMode); - diff --git a/lib-app-bitcoin/ui/display_variables.h b/lib-app-bitcoin/ui/display_variables.h index 723009ab..ad292b2a 100644 --- a/lib-app-bitcoin/ui/display_variables.h +++ b/lib-app-bitcoin/ui/display_variables.h @@ -1,40 +1,41 @@ -#pragma once +#pragma once #include "os.h" -// A path contains 10 elements max, which max length in ascii is 1 whitespace + 10 char + optional quote "'" + "/" + \0" -#define MAX_DERIV_PATH_ASCII_LENGTH 1 + 10*(10+2) + 1 +// A path contains 10 elements max, which max length in ascii is 1 whitespace + +// 10 char + optional quote "'" + "/" + \0" +#define MAX_DERIV_PATH_ASCII_LENGTH 1 + 10 * (10 + 2) + 1 #define MAX_CHAR_PER_LINE 25 typedef struct swap_data_s { - int was_address_checked; - // total number of inputs to be signed - int totalNumberOfInputs; - // number of already signed input in the transaction, to compare with - // totalNumberOfInputs and exit properly - int alreadySignedInputs; - int initialized; - unsigned char amount[8]; - unsigned char fees[8]; - char destination_address[65]; - unsigned char should_exit; + int was_address_checked; + // total number of inputs to be signed + int totalNumberOfInputs; + // number of already signed input in the transaction, to compare with + // totalNumberOfInputs and exit properly + int alreadySignedInputs; + int initialized; + unsigned char amount[8]; + unsigned char fees[8]; + char destination_address[65]; + unsigned char should_exit; } swap_data_t; union display_variables { - struct { - // char addressSummary[40]; // beginning of the output address ... end - // of + struct { + // char addressSummary[40]; // beginning of the output address ... end + // of - char fullAddress[65]; // the address - char fullAmount[28]; // full amount - char feesAmount[28]; // fees - } tmp; + char fullAddress[65]; // the address + char fullAmount[28]; // full amount + char feesAmount[28]; // fees + } tmp; - struct { - char derivation_path [MAX_DERIV_PATH_ASCII_LENGTH]; - } tmp_warning; + struct { + char derivation_path[MAX_DERIV_PATH_ASCII_LENGTH]; + } tmp_warning; - swap_data_t swap_data; + swap_data_t swap_data; }; extern union display_variables vars; diff --git a/lib-app-bitcoin/ui/extensions.h b/lib-app-bitcoin/ui/extensions.h index 7aed64c9..c28d928a 100644 --- a/lib-app-bitcoin/ui/extensions.h +++ b/lib-app-bitcoin/ui/extensions.h @@ -1,21 +1,21 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ - -#pragma once + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ + +#pragma once // btchip asking the per-output UI unsigned int confirm_single_output(void); @@ -39,16 +39,16 @@ void confirm_message_signature(void); int user_action_message_signing(unsigned char confirming); // Public key display -uint8_t set_key_path_to_display(const unsigned char* keyPath); +uint8_t set_key_path_to_display(const unsigned char *keyPath); void display_public_key(uint8_t is_derivation_path_unusual); int user_action_display(unsigned char confirming); void request_pubkey_approval(void); -void request_change_path_approval(unsigned char* change_path); +void request_change_path_approval(unsigned char *change_path); // UI to confirm processing of tx with segwit inputs void request_segwit_input_approval(void); // UI to confirm signing path -void request_sign_path_approval(unsigned char* change_path); +void request_sign_path_approval(unsigned char *change_path); int user_action_signtx(unsigned char confirming, unsigned char direct); diff --git a/lib-app-bitcoin/ui/main_ui.c b/lib-app-bitcoin/ui/main_ui.c index 5d2579bb..2fd4e0fd 100644 --- a/lib-app-bitcoin/ui/main_ui.c +++ b/lib-app-bitcoin/ui/main_ui.c @@ -1,75 +1,72 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ -#include "os.h" + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ +#include "bip32.h" #include "cx.h" #include "format.h" +#include "os.h" #include "read.h" -#include "write.h" -#include "bip32.h" -#include "swap.h" #include "string.h" +#include "swap.h" +#include "write.h" - +#include "be_operations.h" #include "context.h" -#include "helpers.h" #include "customizable_helpers.h" #include "customizable_ui.h" -#include "extensions.h" #include "display_utils.h" -#include "ux.h" #include "display_variables.h" -#include "swap_lib_calls.h" -#include "handle_swap_sign_transaction.h" -#include "handle_get_printable_amount.h" +#include "extensions.h" #include "handle_check_address.h" +#include "handle_get_printable_amount.h" +#include "handle_swap_sign_transaction.h" +#include "helpers.h" +#include "swap_lib_calls.h" #include "ui.h" -#include "be_operations.h" +#include "ux.h" #define OMNI_ASSETID 1 #define MAIDSAFE_ASSETID 3 #define USDT_ASSETID 31 static uint8_t check_fee_swap() { - unsigned char fees[8]; - unsigned char borrow; - - borrow = transaction_amount_sub_be( - fees, context.transactionContext.transactionAmount, - context.totalOutputAmount); - if ((borrow != 0) || (memcmp(fees, vars.swap_data.fees, 8) != 0)) - return 0; - context.transactionContext.firstSigned = 0; - - if (context.usingSegwit && !context.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } - context.sw = 0x9000; - context.outLength = 0; - G_io_apdu_buffer[context.outLength++] = 0x90; - G_io_apdu_buffer[context.outLength++] = 0x00; - - return 1; + unsigned char fees[8]; + unsigned char borrow; + + borrow = transaction_amount_sub_be( + fees, context.transactionContext.transactionAmount, + context.totalOutputAmount); + if ((borrow != 0) || (memcmp(fees, vars.swap_data.fees, 8) != 0)) + return 0; + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = TRANSACTION_SIGN_READY; + } + context.sw = 0x9000; + context.outLength = 0; + G_io_apdu_buffer[context.outLength++] = 0x90; + G_io_apdu_buffer[context.outLength++] = 0x00; + + return 1; } #define OMNI_ASSETID 1 @@ -77,235 +74,228 @@ static uint8_t check_fee_swap() { #define USDT_ASSETID 31 static void prepare_single_output(void) { - // TODO : special display for OP_RETURN - unsigned char amount[8]; - unsigned int offset = 0; - char tmp[80] = {0}; - - swap_bytes(amount, context.currentOutput + offset, 8); - offset += 8; - - get_address_from_output_script(context.currentOutput + offset, sizeof(context.currentOutput) - offset, tmp, sizeof(tmp)); - strncpy(vars.tmp.fullAddress, tmp, sizeof(vars.tmp.fullAddress) - 1); - - // Prepare amount - - // Handle Omni simple send - if ((context.currentOutput[offset + 2] == 0x14) && - (memcmp(context.currentOutput + offset + 3, "omni", 4) == 0) && - (memcmp(context.currentOutput + offset + 3 + 4, "\0\0\0\0", 4) == 0)) { - uint32_t omniAssetId = read_u32_be(context.currentOutput, offset + 3 + 4 + 4); - switch(omniAssetId) { - case OMNI_ASSETID: - strcpy(vars.tmp.fullAmount, "OMNI "); - break; - case USDT_ASSETID: - strcpy(vars.tmp.fullAmount, "USDT "); - break; - case MAIDSAFE_ASSETID: - strcpy(vars.tmp.fullAmount, "MAID "); - break; - default: - snprintf(vars.tmp.fullAmount, sizeof(vars.tmp.fullAmount), "OMNI asset %d ", omniAssetId); - break; - } - format_sats_amount(vars.tmp.fullAmount, - (uint64_t) read_u64_be(context.currentOutput, offset + 3 + 4 + 4 + 4), // Cast prevents weird compilo bug - vars.tmp.fullAmount); - } - else { - format_sats_amount(COIN_COINID_SHORT, - (uint64_t)read_u64_be(amount, 0), // Cast prevents weird compilo bug - vars.tmp.fullAmount); + // TODO : special display for OP_RETURN + unsigned char amount[8]; + unsigned int offset = 0; + char tmp[80] = {0}; + + swap_bytes(amount, context.currentOutput + offset, 8); + offset += 8; + + get_address_from_output_script(context.currentOutput + offset, + sizeof(context.currentOutput) - offset, tmp, + sizeof(tmp)); + strncpy(vars.tmp.fullAddress, tmp, sizeof(vars.tmp.fullAddress) - 1); + + // Prepare amount + + // Handle Omni simple send + if ((context.currentOutput[offset + 2] == 0x14) && + (memcmp(context.currentOutput + offset + 3, "omni", 4) == 0) && + (memcmp(context.currentOutput + offset + 3 + 4, "\0\0\0\0", 4) == 0)) { + uint32_t omniAssetId = + read_u32_be(context.currentOutput, offset + 3 + 4 + 4); + switch (omniAssetId) { + case OMNI_ASSETID: + strcpy(vars.tmp.fullAmount, "OMNI "); + break; + case USDT_ASSETID: + strcpy(vars.tmp.fullAmount, "USDT "); + break; + case MAIDSAFE_ASSETID: + strcpy(vars.tmp.fullAmount, "MAID "); + break; + default: + snprintf(vars.tmp.fullAmount, sizeof(vars.tmp.fullAmount), + "OMNI asset %d ", omniAssetId); + break; } + format_sats_amount( + vars.tmp.fullAmount, + (uint64_t)read_u64_be(context.currentOutput, + offset + 3 + 4 + 4 + + 4), // Cast prevents weird compilo bug + vars.tmp.fullAmount); + } else { + format_sats_amount( + COIN_COINID_SHORT, + (uint64_t)read_u64_be(amount, 0), // Cast prevents weird compilo bug + vars.tmp.fullAmount); + } } static uint8_t prepare_message_signature(void) { - uint8_t buffer[32]; + uint8_t buffer[32]; - if (cx_hash_no_throw(&context.transactionHashAuthorization.header, CX_LAST, - (uint8_t*)vars.tmp.fullAmount, 0, buffer, 32)) { - return 0; - } + if (cx_hash_no_throw(&context.transactionHashAuthorization.header, CX_LAST, + (uint8_t *)vars.tmp.fullAmount, 0, buffer, 32)) { + return 0; + } - format_hex((const uint8_t*) buffer, sizeof(buffer), vars.tmp.fullAddress, sizeof(vars.tmp.fullAddress)); + format_hex((const uint8_t *)buffer, sizeof(buffer), vars.tmp.fullAddress, + sizeof(vars.tmp.fullAddress)); - return 1; + return 1; } - -extern int handle_output_state(unsigned int* processed); +extern int handle_output_state(unsigned int *processed); extern void hash_input_finalize_full_reset(void); // Analog of confirm_single_output to work // in silent mode, when called from SWAP app unsigned int silent_confirm_single_output() { - char tmp[80] = {0}; - unsigned char amount[8]; - while (true) { - // in swap operation we can only have 1 "external" output - if (vars.swap_data.was_address_checked) { - PRINTF("Address was already checked\n"); - return 0; - } - vars.swap_data.was_address_checked = 1; - // check amount - swap_bytes(amount, context.currentOutput, 8); - if (memcmp(amount, vars.swap_data.amount, 8) != 0) { - PRINTF("Amount not matched\n"); - return 0; - } - get_address_from_output_script(context.currentOutput + 8, sizeof(context.currentOutput) - 8, tmp, sizeof(tmp)); - if (strcmp(tmp, vars.swap_data.destination_address) != 0) { - PRINTF("Address not matched\n"); - return 0; - } - - // Check if all inputs have been confirmed - - if (context.outputParsingState == - OUTPUT_PARSING_OUTPUT) { - context.remainingOutputs--; - if (context.remainingOutputs == 0) - break; - } - - memmove(context.currentOutput, - context.currentOutput + - context.discardSize, - context.currentOutputOffset - - context.discardSize); - context.currentOutputOffset -= context.discardSize; - unsigned int processed = true; - while (processed == 1) { - if (handle_output_state(&processed)) { - PRINTF("Error in handle output state \n"); - return 0; - } - } - - if (processed != 2) { - // Out of data to process, wait for the next call - break; - } + char tmp[80] = {0}; + unsigned char amount[8]; + while (true) { + // in swap operation we can only have 1 "external" output + if (vars.swap_data.was_address_checked) { + PRINTF("Address was already checked\n"); + return 0; + } + vars.swap_data.was_address_checked = 1; + // check amount + swap_bytes(amount, context.currentOutput, 8); + if (memcmp(amount, vars.swap_data.amount, 8) != 0) { + PRINTF("Amount not matched\n"); + return 0; } + get_address_from_output_script(context.currentOutput + 8, + sizeof(context.currentOutput) - 8, tmp, + sizeof(tmp)); + if (strcmp(tmp, vars.swap_data.destination_address) != 0) { + PRINTF("Address not matched\n"); + return 0; + } + + // Check if all inputs have been confirmed - if ((context.outputParsingState == OUTPUT_PARSING_OUTPUT) && - (context.remainingOutputs == 0)) { - context.outputParsingState = OUTPUT_FINALIZE_TX; - // check fees - unsigned char fees[8]; - - if ((transaction_amount_sub_be(fees, - context.transactionContext.transactionAmount, - context.totalOutputAmount) != 0) || - (memcmp(fees, vars.swap_data.fees, 8) != 0)) { - PRINTF("Fees is not matched\n"); - return 0; - } + if (context.outputParsingState == OUTPUT_PARSING_OUTPUT) { + context.remainingOutputs--; + if (context.remainingOutputs == 0) + break; } - if (context.outputParsingState == OUTPUT_FINALIZE_TX) { - context.transactionContext.firstSigned = 0; - - if (context.usingSegwit && - !context.segwitParsedOnce) { - // This input cannot be signed when using segwit - just restart. - context.segwitParsedOnce = 1; - PRINTF("Segwit parsed once\n"); - context.transactionContext.transactionState = - TRANSACTION_NONE; - } else { - context.transactionContext.transactionState = - TRANSACTION_SIGN_READY; - } + memmove(context.currentOutput, context.currentOutput + context.discardSize, + context.currentOutputOffset - context.discardSize); + context.currentOutputOffset -= context.discardSize; + unsigned int processed = true; + while (processed == 1) { + if (handle_output_state(&processed)) { + PRINTF("Error in handle output state \n"); + return 0; + } + } + + if (processed != 2) { + // Out of data to process, wait for the next call + break; + } + } + + if ((context.outputParsingState == OUTPUT_PARSING_OUTPUT) && + (context.remainingOutputs == 0)) { + context.outputParsingState = OUTPUT_FINALIZE_TX; + // check fees + unsigned char fees[8]; + + if ((transaction_amount_sub_be(fees, + context.transactionContext.transactionAmount, + context.totalOutputAmount) != 0) || + (memcmp(fees, vars.swap_data.fees, 8) != 0)) { + PRINTF("Fees is not matched\n"); + return 0; } - if (context.outputParsingState == OUTPUT_FINALIZE_TX) { - // we've finished the processing of the input - hash_input_finalize_full_reset(); + } + + if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + context.transactionContext.firstSigned = 0; + + if (context.usingSegwit && !context.segwitParsedOnce) { + // This input cannot be signed when using segwit - just restart. + context.segwitParsedOnce = 1; + PRINTF("Segwit parsed once\n"); + context.transactionContext.transactionState = TRANSACTION_NONE; + } else { + context.transactionContext.transactionState = TRANSACTION_SIGN_READY; } + } + if (context.outputParsingState == OUTPUT_FINALIZE_TX) { + // we've finished the processing of the input + hash_input_finalize_full_reset(); + } - return 1; + return 1; } unsigned int confirm_single_output(void) { - if (G_called_from_swap) { - if (silent_confirm_single_output()) { - return 2; - } - return 0; + if (G_called_from_swap) { + if (silent_confirm_single_output()) { + return 2; } - prepare_single_output(); + return 0; + } + prepare_single_output(); - ui_confirm_single_flow(); - return 1; + ui_confirm_single_flow(); + return 1; } unsigned int finalize_tx(void) { - if (G_called_from_swap) { - if (check_fee_swap()) { - return 2; - } + if (G_called_from_swap) { + if (check_fee_swap()) { + return 2; } + } - if (!prepare_fees()) { - return 0; - } + if (!prepare_fees()) { + return 0; + } - ui_finalize_flow(); - return 1; + ui_finalize_flow(); + return 1; } void confirm_message_signature(void) { - if (!prepare_message_signature()) { - return; - } + if (!prepare_message_signature()) { + return; + } - ui_sign_message_flow(); + ui_sign_message_flow(); } -uint8_t set_key_path_to_display(const unsigned char* keyPath) { - format_path(keyPath, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); - return bip44_derivation_guard(keyPath, false); +uint8_t set_key_path_to_display(const unsigned char *keyPath) { + format_path(keyPath, vars.tmp_warning.derivation_path, + sizeof(vars.tmp_warning.derivation_path)); + return bip44_derivation_guard(keyPath, false); } void display_public_key(uint8_t is_derivation_path_unusual) { - // append a white space at the end of the address to avoid glitch on nano S - strlcat((char *)G_io_apdu_buffer + 200, " ", sizeof(G_io_apdu_buffer) - 200); - - if (is_derivation_path_unusual) { - ui_display_public_with_warning_flow(); - } - else { - ui_display_public_flow(); - } + // append a white space at the end of the address to avoid glitch on nano S + strlcat((char *)G_io_apdu_buffer + 200, " ", sizeof(G_io_apdu_buffer) - 200); + + if (is_derivation_path_unusual) { + ui_display_public_with_warning_flow(); + } else { + ui_display_public_flow(); + } } -void display_token(void) -{ - ui_display_token_flow(); -} +void display_token(void) { ui_display_token_flow(); } -void request_pubkey_approval(void) -{ - ui_request_pubkey_approval_flow(); -} +void request_pubkey_approval(void) { ui_request_pubkey_approval_flow(); } -void request_change_path_approval(unsigned char* change_path) -{ - format_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); - ui_request_change_path_approval_flow(); +void request_change_path_approval(unsigned char *change_path) { + format_path(change_path, vars.tmp_warning.derivation_path, + sizeof(vars.tmp_warning.derivation_path)); + ui_request_change_path_approval_flow(); } -void request_sign_path_approval(unsigned char* change_path) -{ - format_path(change_path, vars.tmp_warning.derivation_path, sizeof(vars.tmp_warning.derivation_path)); - ui_request_sign_path_approval_flow(); +void request_sign_path_approval(unsigned char *change_path) { + format_path(change_path, vars.tmp_warning.derivation_path, + sizeof(vars.tmp_warning.derivation_path)); + ui_request_sign_path_approval_flow(); } -void request_segwit_input_approval(void) -{ - ui_request_segwit_input_approval_flow(); +void request_segwit_input_approval(void) { + ui_request_segwit_input_approval_flow(); } - - diff --git a/lib-app-bitcoin/ui/ui_bagl.c b/lib-app-bitcoin/ui/ui_bagl.c index 9a60828b..e4eaaf69 100644 --- a/lib-app-bitcoin/ui/ui_bagl.c +++ b/lib-app-bitcoin/ui/ui_bagl.c @@ -17,9 +17,9 @@ #ifdef HAVE_BAGL ////////////////////////////////////////////////////////////////////// +#include "context.h" #include "display_variables.h" #include "transaction.h" -#include "context.h" #include "ui.h" #include "extensions.h" @@ -389,8 +389,7 @@ void ui_sign_message_flow(void) { ux_flow_init(0, ux_sign_flow, NULL); } void ui_confirm_single_flow(void) { snprintf(vars.tmp.feesAmount, sizeof(vars.tmp.feesAmount), "output #%d", - context.totalOutputs - context.remainingOutputs + - 1); + context.totalOutputs - context.remainingOutputs + 1); ux_flow_init(0, ux_confirm_single_flow, NULL); } @@ -429,12 +428,10 @@ void ui_idle_flow(void) { ux_stack_push(); } ux_flow_init(0, ux_idle_flow, NULL); -} - -void ui_transaction_error(void) { } -void ui_transaction_finish(void) { -} +void ui_transaction_error(void) {} + +void ui_transaction_finish(void) {} #endif // HAVE_BAGL diff --git a/lib-app-bitcoin/ui/ui_menu_nbgl.c b/lib-app-bitcoin/ui/ui_menu_nbgl.c index de876fc7..e767e60c 100644 --- a/lib-app-bitcoin/ui/ui_menu_nbgl.c +++ b/lib-app-bitcoin/ui/ui_menu_nbgl.c @@ -34,9 +34,7 @@ static char text[20]; static const char *const infoTypes[] = {"Version", "Developer"}; static const char *const infoContents[] = {APPVERSION, "Ledger"}; -static void quit_cb(void) { - os_sched_exit(-1); -} +static void quit_cb(void) { os_sched_exit(-1); } static bool settings_navigation_cb(uint8_t page, nbgl_pageContent_t *content) { if (page == 0) { @@ -56,8 +54,7 @@ static void display_settings_menu(void) { snprintf(text, sizeof(text), "%s settings", COIN_COINID_NAME); nbgl_useCaseSettings(text, PAGE_START, NB_PAGE_SETTING, IS_TOUCHABLE, - ui_idle_flow, settings_navigation_cb, - NULL); + ui_idle_flow, settings_navigation_cb, NULL); } void ui_idle_flow(void) { diff --git a/lib-app-bitcoin/ui/ui_nbgl.c b/lib-app-bitcoin/ui/ui_nbgl.c index 8876b6bd..23828d56 100644 --- a/lib-app-bitcoin/ui/ui_nbgl.c +++ b/lib-app-bitcoin/ui/ui_nbgl.c @@ -16,31 +16,29 @@ ********************************************************************************/ #ifdef HAVE_NBGL -#include "ui.h" -#include "nbgl_use_case.h" -#include "display_variables.h" #include "context.h" +#include "display_variables.h" +#include "nbgl_use_case.h" +#include "ui.h" #include "extensions.h" -typedef enum { - MESSAGE_TYPE, - TRANSACTION_TYPE -} flow_type_t; +typedef enum { MESSAGE_TYPE, TRANSACTION_TYPE } flow_type_t; enum { - CANCEL_TOKEN, - CONFIRM_TOKEN, - BACK_TOKEN, + CANCEL_TOKEN, + CONFIRM_TOKEN, + BACK_TOKEN, }; - typedef struct { bool transaction_prompt_done; const char *prompt_cancel_message; - const char *prompt; // text displayed in last transaction page - const char *approved_status; // text displayed in confirmation page (after long press) - const char *abandon_status; // text displayed in rejection page (after reject confirmed) + const char *prompt; // text displayed in last transaction page + const char * + approved_status; // text displayed in confirmation page (after long press) + const char *abandon_status; // text displayed in rejection page (after reject + // confirmed) void (*approved_cb)(void); void (*abandon_cb)(void); nbgl_layoutTagValueList_t tagValueList; @@ -138,25 +136,21 @@ static void status_callback(bool confirm) { // Prompt Cancel static void prompt_cancel(flow_type_t type) { switch (type) { - case MESSAGE_TYPE: - nbgl_useCaseConfirm("Reject message", NULL, "Yes, Reject", - "Go back to message", abandon_status); - break; - - case TRANSACTION_TYPE: - nbgl_useCaseConfirm("Reject transaction", NULL, "Yes, Reject", - "Go back to transaction", abandon_status); - break; + case MESSAGE_TYPE: + nbgl_useCaseConfirm("Reject message", NULL, "Yes, Reject", + "Go back to message", abandon_status); + break; + + case TRANSACTION_TYPE: + nbgl_useCaseConfirm("Reject transaction", NULL, "Yes, Reject", + "Go back to transaction", abandon_status); + break; } } -static void prompt_cancel_message(void) { - prompt_cancel(MESSAGE_TYPE); -} +static void prompt_cancel_message(void) { prompt_cancel(MESSAGE_TYPE); } -static void prompt_cancel_transaction(void) { - prompt_cancel(TRANSACTION_TYPE); -} +static void prompt_cancel_transaction(void) { prompt_cancel(TRANSACTION_TYPE); } static void transaction_review_callback(bool token) { if (token) { @@ -168,45 +162,48 @@ static void transaction_review_callback(bool token) { } static void transaction_finish_callback(int token, uint8_t index) { - (void) index; + (void)index; switch (token) { - case CANCEL_TOKEN: - prompt_cancel_transaction(); - break; - case CONFIRM_TOKEN: - releaseContext(); - uiContext.approved_cb(); - break; - case BACK_TOKEN: - ui_finalize_flow(); - break; + case CANCEL_TOKEN: + prompt_cancel_transaction(); + break; + case CONFIRM_TOKEN: + releaseContext(); + uiContext.approved_cb(); + break; + case BACK_TOKEN: + ui_finalize_flow(); + break; } } static void transaction_fee_callback(int token, uint8_t index) { - (void) index; + (void)index; if (token) { - releaseContext(); - snprintf(text, sizeof(text), "Sign transaction\nto send %s?", COIN_COINID_NAME); - nbgl_pageNavigationInfo_t info = {.activePage = 0, - .nbPages = 0, - .navType = NAV_WITH_TAP, - .progressIndicator = true, - .navWithTap.backButton = true, - .navWithTap.backToken = BACK_TOKEN, - .navWithTap.nextPageText = NULL, - .navWithTap.quitText = "Reject transaction", - .quitToken = CANCEL_TOKEN, - .tuneId = TUNE_TAP_CASUAL}; - - nbgl_pageContent_t content = {.type = INFO_LONG_PRESS, - .infoLongPress.icon = &COIN_ICON, - .infoLongPress.text = text, - .infoLongPress.longPressText = "Hold to sign", - .infoLongPress.longPressToken = CONFIRM_TOKEN, - .infoLongPress.tuneId = TUNE_TAP_NEXT}; - - pageContext = nbgl_pageDrawGenericContent(&transaction_finish_callback, &info, &content); + releaseContext(); + snprintf(text, sizeof(text), "Sign transaction\nto send %s?", + COIN_COINID_NAME); + nbgl_pageNavigationInfo_t info = {.activePage = 0, + .nbPages = 0, + .navType = NAV_WITH_TAP, + .progressIndicator = true, + .navWithTap.backButton = true, + .navWithTap.backToken = BACK_TOKEN, + .navWithTap.nextPageText = NULL, + .navWithTap.quitText = + "Reject transaction", + .quitToken = CANCEL_TOKEN, + .tuneId = TUNE_TAP_CASUAL}; + + nbgl_pageContent_t content = {.type = INFO_LONG_PRESS, + .infoLongPress.icon = &COIN_ICON, + .infoLongPress.text = text, + .infoLongPress.longPressText = "Hold to sign", + .infoLongPress.longPressToken = CONFIRM_TOKEN, + .infoLongPress.tuneId = TUNE_TAP_NEXT}; + + pageContext = nbgl_pageDrawGenericContent(&transaction_finish_callback, + &info, &content); } else { prompt_cancel_transaction(); } @@ -232,36 +229,34 @@ static void continue_review(flow_type_t type) { uiContext.infoLongPress.text = uiContext.prompt; switch (type) { - case MESSAGE_TYPE: - nbgl_useCaseStaticReview(&uiContext.tagValueList, &uiContext.infoLongPress, - "Cancel", message_review_callback); - break; - - case TRANSACTION_TYPE: - nbgl_useCaseStaticReview(&uiContext.tagValueList, &uiContext.infoLongPress, - "Cancel", transaction_review_callback); - break; + case MESSAGE_TYPE: + nbgl_useCaseStaticReview(&uiContext.tagValueList, &uiContext.infoLongPress, + "Cancel", message_review_callback); + break; + + case TRANSACTION_TYPE: + nbgl_useCaseStaticReview(&uiContext.tagValueList, &uiContext.infoLongPress, + "Cancel", transaction_review_callback); + break; } } -static void continue_message_review(void) { - continue_review(MESSAGE_TYPE); -} +static void continue_message_review(void) { continue_review(MESSAGE_TYPE); } // UI Start static void ui_start(void (*cb)(void), flow_type_t type) { switch (type) { - case MESSAGE_TYPE: - nbgl_useCaseReviewStart(&COIN_ICON, "Review\nmessage", NULL, - "Cancel", continue_message_review, - prompt_cancel_message); - break; - - case TRANSACTION_TYPE: - snprintf(text, sizeof(text), "Review transaction\nto send %s", COIN_COINID_NAME); - nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, - "Cancel", cb, prompt_cancel_transaction); - break; + case MESSAGE_TYPE: + nbgl_useCaseReviewStart(&COIN_ICON, "Review\nmessage", NULL, "Cancel", + continue_message_review, prompt_cancel_message); + break; + + case TRANSACTION_TYPE: + snprintf(text, sizeof(text), "Review transaction\nto send %s", + COIN_COINID_NAME); + nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, "Cancel", cb, + prompt_cancel_transaction); + break; } } @@ -284,13 +279,14 @@ static void ui_message_start(void) { // Other callbacks static void display_pubkey_callback(void) { if (uiContext.nbPairs == 1) { - nbgl_useCaseAddressConfirmation(uiContext.tagValues[0].value, status_callback); - } - else { - uiContext.tagValueList.pairs = &uiContext.tagValues[1]; - uiContext.tagValueList.nbPairs = 1; + nbgl_useCaseAddressConfirmation(uiContext.tagValues[0].value, + status_callback); + } else { + uiContext.tagValueList.pairs = &uiContext.tagValues[1]; + uiContext.tagValueList.nbPairs = 1; - nbgl_useCaseAddressConfirmationExt(uiContext.tagValues[0].value, status_callback, &uiContext.tagValueList); + nbgl_useCaseAddressConfirmationExt( + uiContext.tagValues[0].value, status_callback, &uiContext.tagValueList); } } @@ -310,8 +306,7 @@ void ui_confirm_single_flow(void) { ui_transaction_start(ui_confirm_single_flow); } else { snprintf(vars.tmp.feesAmount, sizeof(vars.tmp.feesAmount), "#%d", - context.totalOutputs - context.remainingOutputs + - 1); + context.totalOutputs - context.remainingOutputs + 1); uiContext.tagValues[0].item = "Output"; uiContext.tagValues[0].value = vars.tmp.feesAmount; @@ -324,17 +319,17 @@ void ui_confirm_single_flow(void) { uiContext.nbPairs = 3; - nbgl_pageNavigationInfo_t info = { - .activePage = 0, - .nbPages = 0, - .navType = NAV_WITH_TAP, - .progressIndicator = true, - .navWithTap.backButton = false, - .navWithTap.nextPageText = "Tap to continue", - .navWithTap.nextPageToken = 1, - .navWithTap.quitText = "Cancel", - .quitToken = 0, - .tuneId = TUNE_TAP_CASUAL}; + nbgl_pageNavigationInfo_t info = {.activePage = 0, + .nbPages = 0, + .navType = NAV_WITH_TAP, + .progressIndicator = true, + .navWithTap.backButton = false, + .navWithTap.nextPageText = + "Tap to continue", + .navWithTap.nextPageToken = 1, + .navWithTap.quitText = "Cancel", + .quitToken = 0, + .tuneId = TUNE_TAP_CASUAL}; nbgl_pageContent_t content = { .type = TAG_VALUE_LIST, @@ -361,7 +356,8 @@ void ui_finalize_flow(void) { .navType = NAV_WITH_TAP, .progressIndicator = true, .navWithTap.backButton = false, - .navWithTap.nextPageText = "Tap to continue", + .navWithTap.nextPageText = + "Tap to continue", .navWithTap.nextPageToken = 1, .navWithTap.quitText = "Reject transaction", .quitToken = 0, @@ -370,10 +366,10 @@ void ui_finalize_flow(void) { nbgl_pageContent_t content = { .type = TAG_VALUE_LIST, .tagValueList.nbPairs = uiContext.nbPairs, - .tagValueList.pairs = (nbgl_layoutTagValue_t *)uiContext.tagValues - }; + .tagValueList.pairs = (nbgl_layoutTagValue_t *)uiContext.tagValues}; - pageContext = nbgl_pageDrawGenericContent(&transaction_fee_callback, &info, &content); + pageContext = + nbgl_pageDrawGenericContent(&transaction_fee_callback, &info, &content); nbgl_refresh(); } @@ -417,8 +413,8 @@ void ui_request_pubkey_approval_flow(void) { ui_transaction_start(ui_request_pubkey_approval_flow); } else { - nbgl_useCaseChoice(&COIN_ICON, "Export public key", NULL, - "Approve", "Reject", transaction_review_callback); + nbgl_useCaseChoice(&COIN_ICON, "Export public key", NULL, "Approve", + "Reject", transaction_review_callback); } } @@ -470,12 +466,11 @@ static void prompt_public_key(bool warning) { snprintf(text, sizeof(text), "Verify %s\naddress", COIN_COINID_NAME); if (warning) { - nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, - "Cancel", warn_unusual_derivation_path, - abandon_status); + nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, "Cancel", + warn_unusual_derivation_path, abandon_status); } else { - nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, - "Cancel", display_pubkey_callback, abandon_status); + nbgl_useCaseReviewStart(&COIN_ICON, text, NULL, "Cancel", + display_pubkey_callback, abandon_status); } } @@ -510,9 +505,7 @@ void ui_display_public_with_warning_flow(void) { display_show_public_key(true); } -void ui_display_public_flow(void) { - display_show_public_key(false); -} +void ui_display_public_flow(void) { display_show_public_key(false); } void ui_transaction_finish(void) { if (uiContext.transaction_prompt_done) { diff --git a/lib-app-bitcoin/utils/be_operations.c b/lib-app-bitcoin/utils/be_operations.c index 43c916e3..c99d05be 100644 --- a/lib-app-bitcoin/utils/be_operations.c +++ b/lib-app-bitcoin/utils/be_operations.c @@ -1,42 +1,38 @@ #include "be_operations.h" -unsigned char transaction_amount_add_be(unsigned char *target, - unsigned char *a, +unsigned char transaction_amount_add_be(unsigned char *target, unsigned char *a, unsigned char *b) { - unsigned char carry = 0; - unsigned char i; - for (i = 0; i < 8; i++) { - unsigned short val = a[8 - 1 - i] + b[8 - 1 - i] + (carry ? 1 : 0); - carry = (val > 255); - target[8 - 1 - i] = (val & 255); - } - return carry; + unsigned char carry = 0; + unsigned char i; + for (i = 0; i < 8; i++) { + unsigned short val = a[8 - 1 - i] + b[8 - 1 - i] + (carry ? 1 : 0); + carry = (val > 255); + target[8 - 1 - i] = (val & 255); + } + return carry; } -unsigned char transaction_amount_sub_be(unsigned char *target, - unsigned char *a, +unsigned char transaction_amount_sub_be(unsigned char *target, unsigned char *a, unsigned char *b) { - unsigned char borrow = 0; - unsigned char i; - for (i = 0; i < 8; i++) { - unsigned short tmpA = a[8 - 1 - i]; - unsigned short tmpB = b[8 - 1 - i]; - if (borrow) { - if (tmpA <= tmpB) { - tmpA += (255 + 1) - 1; - } else { - borrow = 0; - tmpA--; - } - } - if (tmpA < tmpB) { - borrow = 1; - tmpA += 255 + 1; - } - target[8 - 1 - i] = (unsigned char)(tmpA - tmpB); + unsigned char borrow = 0; + unsigned char i; + for (i = 0; i < 8; i++) { + unsigned short tmpA = a[8 - 1 - i]; + unsigned short tmpB = b[8 - 1 - i]; + if (borrow) { + if (tmpA <= tmpB) { + tmpA += (255 + 1) - 1; + } else { + borrow = 0; + tmpA--; + } + } + if (tmpA < tmpB) { + borrow = 1; + tmpA += 255 + 1; } + target[8 - 1 - i] = (unsigned char)(tmpA - tmpB); + } - return borrow; + return borrow; } - - diff --git a/lib-app-bitcoin/utils/be_operations.h b/lib-app-bitcoin/utils/be_operations.h index 8dc7a9a2..81533086 100644 --- a/lib-app-bitcoin/utils/be_operations.h +++ b/lib-app-bitcoin/utils/be_operations.h @@ -1,28 +1,26 @@ /******************************************************************************* -* Ledger App - Bitcoin Wallet -* (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. -********************************************************************************/ + * Ledger App - Bitcoin Wallet + * (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. + ********************************************************************************/ #pragma once // target = a + b -unsigned char transaction_amount_add_be(unsigned char *target, - unsigned char *a, +unsigned char transaction_amount_add_be(unsigned char *target, unsigned char *a, unsigned char *b); // target = a - b -unsigned char transaction_amount_sub_be(unsigned char *target, - unsigned char *a, +unsigned char transaction_amount_sub_be(unsigned char *target, unsigned char *a, unsigned char *b); diff --git a/lib-app-bitcoin/utils/cashaddr.c b/lib-app-bitcoin/utils/cashaddr.c index fb14ad30..3214af13 100644 --- a/lib-app-bitcoin/utils/cashaddr.c +++ b/lib-app-bitcoin/utils/cashaddr.c @@ -20,134 +20,133 @@ * THE SOFTWARE. */ -#include "os.h" #include "cx.h" -#include +#include "os.h" +#include #include +#include #include -#include #include "cashaddr.h" static const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; uint64_t cashaddr_polymod_step(uint64_t pre) { - uint8_t b = pre >> 35; - return ((pre & 0x07ffffffff) << 5) ^ (-((b >> 0) & 1) & 0x98f2bc8e61UL) ^ - (-((b >> 1) & 1) & 0x79b76d99e2UL) ^ - (-((b >> 2) & 1) & 0xf33e5fb3c4UL) ^ - (-((b >> 3) & 1) & 0xae2eabe2a8UL) ^ - (-((b >> 4) & 1) & 0x1e4f43e470UL); + uint8_t b = pre >> 35; + return ((pre & 0x07ffffffff) << 5) ^ (-((b >> 0) & 1) & 0x98f2bc8e61UL) ^ + (-((b >> 1) & 1) & 0x79b76d99e2UL) ^ + (-((b >> 2) & 1) & 0xf33e5fb3c4UL) ^ + (-((b >> 3) & 1) & 0xae2eabe2a8UL) ^ + (-((b >> 4) & 1) & 0x1e4f43e470UL); } uint64_t PolyMod(uint8_t *prefix, uint8_t *payload, size_t payload_length) { - size_t i; - uint64_t c = 1; - while (*prefix != 0) { - c = cashaddr_polymod_step(c) ^ (*prefix++ & 0x1f); // Prefix - } - c = cashaddr_polymod_step(c); // The zero valued separator - for (i = 0; i < payload_length; ++i) { - c = cashaddr_polymod_step(c) ^ (*payload++); // Hash - } - for (i = 0; i < 8; ++i) { - c = cashaddr_polymod_step(c); // 8 zeros for empty checksum - } - return c ^ 1; + size_t i; + uint64_t c = 1; + while (*prefix != 0) { + c = cashaddr_polymod_step(c) ^ (*prefix++ & 0x1f); // Prefix + } + c = cashaddr_polymod_step(c); // The zero valued separator + for (i = 0; i < payload_length; ++i) { + c = cashaddr_polymod_step(c) ^ (*payload++); // Hash + } + for (i = 0; i < 8; ++i) { + c = cashaddr_polymod_step(c); // 8 zeros for empty checksum + } + return c ^ 1; } static int convert_bits(uint8_t *out, size_t *outlen, int outbits, const uint8_t *in, size_t inlen, int inbits, int pad) { - uint32_t val = 0; - int bits = 0; - uint32_t maxv = (((uint32_t)1) << outbits) - 1; - while (inlen--) { - val = (val << inbits) | *(in++); - bits += inbits; - while (bits >= outbits) { - bits -= outbits; - out[(*outlen)++] = (val >> bits) & maxv; - } + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while (inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while (bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; } - if (pad) { - if (bits) { - out[(*outlen)++] = (val << (outbits - bits)) & maxv; - } - } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { - return 0; + } + if (pad) { + if (bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; } - return 1; + } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; } void create_checksum(uint8_t *payload, size_t payload_length, uint8_t *checksum) { - uint8_t *prefix = (uint8_t *)"bitcoincash"; - uint64_t mod = PolyMod(prefix, payload, payload_length); + uint8_t *prefix = (uint8_t *)"bitcoincash"; + uint64_t mod = PolyMod(prefix, payload, payload_length); - for (size_t i = 0; i < 8; ++i) { - // Convert the 5-bit groups in mod to checksum values. - *checksum++ = (mod >> (5 * (7 - i))) & 0x1f; - } + for (size_t i = 0; i < 8; ++i) { + // Convert the 5-bit groups in mod to checksum values. + *checksum++ = (mod >> (5 * (7 - i))) & 0x1f; + } } int cashaddr_encode(uint8_t *hash, const size_t hash_length, uint8_t *addr, const size_t max_addr_len, const unsigned short version) { - uint8_t version_byte; - uint8_t checksum[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // 5-bit bytes. - uint8_t - tmp[40]; // 8-bit bytes. Should be enough for 1 version byte + 160 bit - uint8_t payload[40]; // 5-bit bytes. Should be enough for 1 version byte + - // 160 bit hash - uint8_t *addr_start; - size_t payload_length = 0; - size_t addr_length = 0; - size_t i; + uint8_t version_byte; + uint8_t checksum[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // 5-bit bytes. + uint8_t tmp[40]; // 8-bit bytes. Should be enough for 1 version byte + 160 bit + uint8_t payload[40]; // 5-bit bytes. Should be enough for 1 version byte + + // 160 bit hash + uint8_t *addr_start; + size_t payload_length = 0; + size_t addr_length = 0; + size_t i; - addr_start = addr; - *addr_start = 0; + addr_start = addr; + *addr_start = 0; - if (hash_length != 20) // Only support 160 bit hash - return 0; - if (version == CASHADDR_P2PKH) { // Support P2PKH = 0, P2SH = 1 - version_byte = 0; - } else if (version == CASHADDR_P2SH) { - version_byte = 8; - } else { - return 0; - } + if (hash_length != 20) // Only support 160 bit hash + return 0; + if (version == CASHADDR_P2PKH) { // Support P2PKH = 0, P2SH = 1 + version_byte = 0; + } else if (version == CASHADDR_P2SH) { + version_byte = 8; + } else { + return 0; + } - tmp[0] = version_byte; - memmove(tmp + 1, hash, hash_length); - convert_bits(payload, &payload_length, 5, tmp, hash_length + 1, 8, 1); + tmp[0] = version_byte; + memmove(tmp + 1, hash, hash_length); + convert_bits(payload, &payload_length, 5, tmp, hash_length + 1, 8, 1); - create_checksum(payload, payload_length, - checksum); // Assume prefix is 'bitcoincash' + create_checksum(payload, payload_length, + checksum); // Assume prefix is 'bitcoincash' - for (i = 0; i < payload_length; ++i) { - if (*payload >> 5) { - *addr_start = 0; - return 0; - } - addr_length++; - if (max_addr_len < addr_length) { - *addr_start = 0; - return 0; - } - *(addr++) = charset[payload[i]]; + for (i = 0; i < payload_length; ++i) { + if (*payload >> 5) { + *addr_start = 0; + return 0; + } + addr_length++; + if (max_addr_len < addr_length) { + *addr_start = 0; + return 0; + } + *(addr++) = charset[payload[i]]; + } + for (i = 0; i < 8; ++i) { + if (*checksum >> 5) { + *addr_start = 0; + return 0; } - for (i = 0; i < 8; ++i) { - if (*checksum >> 5) { - *addr_start = 0; - return 0; - } - addr_length++; - if (max_addr_len < addr_length) { - *addr_start = 0; - return 0; - } - *(addr++) = charset[checksum[i]]; + addr_length++; + if (max_addr_len < addr_length) { + *addr_start = 0; + return 0; } - *addr = 0; + *(addr++) = charset[checksum[i]]; + } + *addr = 0; - return addr_length; + return addr_length; } diff --git a/lib-app-bitcoin/utils/segwit_addr.c b/lib-app-bitcoin/utils/segwit_addr.c index 95c37e8e..bec567ec 100644 --- a/lib-app-bitcoin/utils/segwit_addr.c +++ b/lib-app-bitcoin/utils/segwit_addr.c @@ -19,17 +19,17 @@ * THE SOFTWARE. */ -#include #include +#include #include #include "segwit_addr.h" uint32_t bech32_polymod_step(uint32_t pre) { - uint8_t b = pre >> 25; - return ((pre & 0x1FFFFFF) << 5) ^ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ - (-((b >> 1) & 1) & 0x26508e6dUL) ^ (-((b >> 2) & 1) & 0x1ea119faUL) ^ - (-((b >> 3) & 1) & 0x3d4233ddUL) ^ (-((b >> 4) & 1) & 0x2a1462b3UL); + uint8_t b = pre >> 25; + return ((pre & 0x1FFFFFF) << 5) ^ (-((b >> 0) & 1) & 0x3b6a57b2UL) ^ + (-((b >> 1) & 1) & 0x26508e6dUL) ^ (-((b >> 2) & 1) & 0x1ea119faUL) ^ + (-((b >> 3) & 1) & 0x3d4233ddUL) ^ (-((b >> 4) & 1) & 0x2a1462b3UL); } static const char *charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; @@ -45,160 +45,160 @@ static const int8_t charset_rev[128] = { int bech32_encode(char *output, const char *hrp, const uint8_t *data, size_t data_len) { - uint32_t chk = 1; - size_t i = 0; - while (hrp[i] != 0) { - if (hrp[i] >= 'A' && hrp[i] <= 'Z') - return 0; - if (!(hrp[i] >> 5)) - return 0; - chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5); - ++i; - } - if (i + 7 + data_len > 90) - return 0; + uint32_t chk = 1; + size_t i = 0; + while (hrp[i] != 0) { + if (hrp[i] >= 'A' && hrp[i] <= 'Z') + return 0; + if (!(hrp[i] >> 5)) + return 0; + chk = bech32_polymod_step(chk) ^ (hrp[i] >> 5); + ++i; + } + if (i + 7 + data_len > 90) + return 0; + chk = bech32_polymod_step(chk); + while (*hrp != 0) { + chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); + *(output++) = *(hrp++); + } + *(output++) = '1'; + for (i = 0; i < data_len; ++i) { + if (*data >> 5) + return 0; + chk = bech32_polymod_step(chk) ^ (*data); + *(output++) = charset[*(data++)]; + } + for (i = 0; i < 6; ++i) { chk = bech32_polymod_step(chk); - while (*hrp != 0) { - chk = bech32_polymod_step(chk) ^ (*hrp & 0x1f); - *(output++) = *(hrp++); - } - *(output++) = '1'; - for (i = 0; i < data_len; ++i) { - if (*data >> 5) - return 0; - chk = bech32_polymod_step(chk) ^ (*data); - *(output++) = charset[*(data++)]; - } - for (i = 0; i < 6; ++i) { - chk = bech32_polymod_step(chk); - } - chk ^= 1; - for (i = 0; i < 6; ++i) { - *(output++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; - } - *output = 0; - return 1; + } + chk ^= 1; + for (i = 0; i < 6; ++i) { + *(output++) = charset[(chk >> ((5 - i) * 5)) & 0x1f]; + } + *output = 0; + return 1; } int bech32_decode(char *hrp, uint8_t *data, size_t *data_len, const char *input) { - uint32_t chk = 1; - size_t i; - size_t input_len = strlen(input); - size_t hrp_len; - int have_lower = 0, have_upper = 0; - if (input_len < 8 || input_len > 90) { - return 0; - } - *data_len = 0; - while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { - ++(*data_len); + uint32_t chk = 1; + size_t i; + size_t input_len = strlen(input); + size_t hrp_len; + int have_lower = 0, have_upper = 0; + if (input_len < 8 || input_len > 90) { + return 0; + } + *data_len = 0; + while (*data_len < input_len && input[(input_len - 1) - *data_len] != '1') { + ++(*data_len); + } + hrp_len = input_len - (1 + *data_len); + if (hrp_len < 1 || *data_len < 6) { + return 0; + } + *(data_len) -= 6; + for (i = 0; i < hrp_len; ++i) { + int ch = input[i]; + if (ch < 33 || ch > 126) { + return 0; } - hrp_len = input_len - (1 + *data_len); - if (hrp_len < 1 || *data_len < 6) { - return 0; + if (ch >= 'a' && ch <= 'z') { + have_lower = 1; + } else if (ch >= 'A' && ch <= 'Z') { + have_upper = 1; + ch = (ch - 'A') + 'a'; } - *(data_len) -= 6; - for (i = 0; i < hrp_len; ++i) { - int ch = input[i]; - if (ch < 33 || ch > 126) { - return 0; - } - if (ch >= 'a' && ch <= 'z') { - have_lower = 1; - } else if (ch >= 'A' && ch <= 'Z') { - have_upper = 1; - ch = (ch - 'A') + 'a'; - } - hrp[i] = ch; - chk = bech32_polymod_step(chk) ^ (ch >> 5); + hrp[i] = ch; + chk = bech32_polymod_step(chk) ^ (ch >> 5); + } + hrp[i] = 0; + chk = bech32_polymod_step(chk); + for (i = 0; i < hrp_len; ++i) { + chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f); + } + ++i; + while (i < input_len) { + int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; + if (input[i] >= 'a' && input[i] <= 'z') + have_lower = 1; + if (input[i] >= 'A' && input[i] <= 'Z') + have_upper = 1; + if (v == -1) { + return 0; } - hrp[i] = 0; - chk = bech32_polymod_step(chk); - for (i = 0; i < hrp_len; ++i) { - chk = bech32_polymod_step(chk) ^ (input[i] & 0x1f); + chk = bech32_polymod_step(chk) ^ v; + if (i + 6 < input_len) { + data[i - (1 + hrp_len)] = v; } ++i; - while (i < input_len) { - int v = (input[i] & 0x80) ? -1 : charset_rev[(int)input[i]]; - if (input[i] >= 'a' && input[i] <= 'z') - have_lower = 1; - if (input[i] >= 'A' && input[i] <= 'Z') - have_upper = 1; - if (v == -1) { - return 0; - } - chk = bech32_polymod_step(chk) ^ v; - if (i + 6 < input_len) { - data[i - (1 + hrp_len)] = v; - } - ++i; - } - if (have_lower && have_upper) { - return 0; - } - return chk == 1; + } + if (have_lower && have_upper) { + return 0; + } + return chk == 1; } static int convert_bits(uint8_t *out, size_t *outlen, int outbits, const uint8_t *in, size_t inlen, int inbits, int pad) { - uint32_t val = 0; - int bits = 0; - uint32_t maxv = (((uint32_t)1) << outbits) - 1; - while (inlen--) { - val = (val << inbits) | *(in++); - bits += inbits; - while (bits >= outbits) { - bits -= outbits; - out[(*outlen)++] = (val >> bits) & maxv; - } + uint32_t val = 0; + int bits = 0; + uint32_t maxv = (((uint32_t)1) << outbits) - 1; + while (inlen--) { + val = (val << inbits) | *(in++); + bits += inbits; + while (bits >= outbits) { + bits -= outbits; + out[(*outlen)++] = (val >> bits) & maxv; } - if (pad) { - if (bits) { - out[(*outlen)++] = (val << (outbits - bits)) & maxv; - } - } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { - return 0; + } + if (pad) { + if (bits) { + out[(*outlen)++] = (val << (outbits - bits)) & maxv; } - return 1; + } else if (((val << (outbits - bits)) & maxv) || bits >= inbits) { + return 0; + } + return 1; } int segwit_addr_encode(char *output, const char *hrp, int witver, const uint8_t *witprog, size_t witprog_len) { - uint8_t data[65]; - size_t datalen = 0; - if (witver > 16) - return 0; - if (witver == 0 && witprog_len != 20 && witprog_len != 32) - return 0; - if (witprog_len < 2 || witprog_len > 40) - return 0; - data[0] = witver; - convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); - ++datalen; - return bech32_encode(output, hrp, data, datalen); + uint8_t data[65]; + size_t datalen = 0; + if (witver > 16) + return 0; + if (witver == 0 && witprog_len != 20 && witprog_len != 32) + return 0; + if (witprog_len < 2 || witprog_len > 40) + return 0; + data[0] = witver; + convert_bits(data + 1, &datalen, 5, witprog, witprog_len, 8, 1); + ++datalen; + return bech32_encode(output, hrp, data, datalen); } int segwit_addr_decode(int *witver, uint8_t *witdata, size_t *witdata_len, const char *hrp, const char *addr) { - uint8_t data[84]; - char hrp_actual[84]; - size_t data_len; - if (!bech32_decode(hrp_actual, data, &data_len, addr)) - return 0; - if (data_len == 0 || data_len > 65) - return 0; - if (strncmp(hrp, hrp_actual, 84) != 0) - return 0; - if (data[0] > 16) - return 0; - *witdata_len = 0; - if (!convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) - return 0; - if (*witdata_len < 2 || *witdata_len > 40) - return 0; - if (data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) - return 0; - *witver = data[0]; - return 1; + uint8_t data[84]; + char hrp_actual[84]; + size_t data_len; + if (!bech32_decode(hrp_actual, data, &data_len, addr)) + return 0; + if (data_len == 0 || data_len > 65) + return 0; + if (strncmp(hrp, hrp_actual, 84) != 0) + return 0; + if (data[0] > 16) + return 0; + *witdata_len = 0; + if (!convert_bits(witdata, witdata_len, 8, data + 1, data_len - 1, 5, 0)) + return 0; + if (*witdata_len < 2 || *witdata_len > 40) + return 0; + if (data[0] == 0 && *witdata_len != 20 && *witdata_len != 32) + return 0; + *witver = data[0]; + return 1; }