diff --git a/Makefile b/Makefile index 4d0fa531..0b4a93c0 100644 --- a/Makefile +++ b/Makefile @@ -138,9 +138,18 @@ else ifeq ($(COIN),qtum) # Qtum # Qtum can run significantly different code paths, thus is locked by the OS # using APP_LOAD_PARAMS instead of BIP44_COIN_TYPE -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=58 COIN_P2SH_VERSION=50 COIN_FAMILY=3 COIN_COINID=\"Qtum\" COIN_COINID_HEADER=\"QTUM\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"QTUM\" COIN_COINID_SHORT=\"QTUM\" COIN_NATIVE_SEGWIT_PREFIX=\"qc\" COIN_KIND=COIN_KIND_QTUM COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=58 COIN_P2SH_VERSION=50 COIN_FAMILY=3 COIN_COINID=\"Qtum\" COIN_COINID_HEADER=\"QTUM\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"QTUM\" COIN_COINID_SHORT=\"QTUM\" COIN_NATIVE_SEGWIT_PREFIX=\"qc\" COIN_KIND=COIN_KIND_QTUM COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT MAX_OUTPUT_TO_CHECK=500 HAVE_QTUM_SUPPORT USE_NO_OVERWINTER APPNAME ="Qtum" -APP_LOAD_PARAMS += --path "44'/88'" --path "49'/88'" --path "84'/88'" --path "0'/45342'" --path "20698'/3053'/12648430'" +DEFINES_LIB=# we're not using the lib :) +APP_LOAD_PARAMS += --path $(APP_PATH) --path "44'/88'" --path "45'/88'" --path "48'/88'" --path "49'/88'" --path "84'/88'" --path "0'/45342'" --path "20698'/3053'/12648430'" +APP_LOAD_FLAGS=--appFlags 0xa50 +else ifeq ($(COIN),qtum_testnet) +# Qtum Testnet +DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=120 COIN_P2SH_VERSION=110 COIN_FAMILY=3 COIN_COINID=\"Qtum\" COIN_COINID_HEADER=\"QTUM\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"QTUM\" COIN_COINID_SHORT=\"QTUM\" COIN_NATIVE_SEGWIT_PREFIX=\"tq\" COIN_KIND=COIN_KIND_QTUM COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT MAX_OUTPUT_TO_CHECK=500 HAVE_QTUM_SUPPORT USE_NO_OVERWINTER +APPNAME ="Qtum Test" +DEFINES_LIB=# we're not using the lib :) +APP_LOAD_PARAMS += --path $(APP_PATH) --path "44'/1'" --path "45'/1'" --path "48'/1'" --path "49'/1'" --path "84'/1'" --path "0'/45342'" --path "20698'/3053'/12648430'" +APP_LOAD_FLAGS=--appFlags 0xa50 else ifeq ($(COIN),firo) DEFINES += BIP44_COIN_TYPE=136 BIP44_COIN_TYPE_2=136 COIN_P2PKH_VERSION=82 COIN_P2SH_VERSION=7 COIN_FAMILY=1 COIN_COINID=\"Zcoin\" COIN_COINID_HEADER=\"FIRO\" COIN_COLOR_HDR=0x3EAD54 COIN_COLOR_DB=0xA3DCAE COIN_COINID_NAME=\"Firo\" COIN_COINID_SHORT=\"FIRO\" COIN_KIND=COIN_KIND_FIRO APPNAME ="Firo" @@ -189,12 +198,12 @@ APPNAME ="Ravencoin" APP_LOAD_PARAMS += --path $(APP_PATH) else ifeq ($(COIN),hydra_testnet) # Hydra testnet -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=66 COIN_P2SH_VERSION=128 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=66 COIN_P2SH_VERSION=128 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT HAVE_QTUM_SUPPORT APPNAME ="Hydra" APP_LOAD_PARAMS += --path "44'/609'" else ifeq ($(COIN),hydra) # Hydra mainnet -DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=40 COIN_P2SH_VERSION=63 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT +DEFINES += BIP44_COIN_TYPE=0 BIP44_COIN_TYPE_2=0 COIN_P2PKH_VERSION=40 COIN_P2SH_VERSION=63 COIN_FAMILY=3 COIN_COINID=\"Hydra\" COIN_COINID_HEADER=\"HYDRA\" COIN_COLOR_HDR=0x2E9AD0 COIN_COLOR_DB=0x97CDE8 COIN_COINID_NAME=\"HYDRA\" COIN_COINID_SHORT=\"HYDRA\" COIN_NATIVE_SEGWIT_PREFIX=\"hc\" COIN_KIND=COIN_KIND_HYDRA COIN_FLAGS=FLAG_SEGWIT_CHANGE_SUPPORT HAVE_QTUM_SUPPORT APPNAME ="Hydra" APP_LOAD_PARAMS += --path "44'/609'" else diff --git a/icons/blue_app_qtum.gif b/icons/blue_app_qtum.gif index 4c0194c3..c4f61bb6 100644 Binary files a/icons/blue_app_qtum.gif and b/icons/blue_app_qtum.gif differ diff --git a/icons/nanos_app_qtum.gif b/icons/nanos_app_qtum.gif index 7e2c908c..62782dcb 100644 Binary files a/icons/nanos_app_qtum.gif and b/icons/nanos_app_qtum.gif differ diff --git a/icons/nanox_app_qtum.gif b/icons/nanox_app_qtum.gif index 225ef6f2..65eeeb74 100644 Binary files a/icons/nanox_app_qtum.gif and b/icons/nanox_app_qtum.gif differ diff --git a/icons/qtum.png b/icons/qtum.png index 0f154f9b..242128c3 100644 Binary files a/icons/qtum.png and b/icons/qtum.png differ diff --git a/include/btchip_context.h b/include/btchip_context.h index 2667b9d6..8b84bca6 100644 --- a/include/btchip_context.h +++ b/include/btchip_context.h @@ -24,7 +24,9 @@ #include "btchip_secure_value.h" #include "btchip_filesystem_tx.h" +#ifndef MAX_OUTPUT_TO_CHECK #define MAX_OUTPUT_TO_CHECK 100 +#endif #define MAX_COIN_ID 13 #define MAX_SHORT_COIN_ID 5 @@ -96,7 +98,9 @@ typedef enum btchip_output_parsing_state_e btchip_output_parsing_state_t; typedef union multi_hash { cx_sha256_t sha256; +#ifndef USE_NO_OVERWINTER cx_blake2b_t blake2b; +#endif } multi_hash; struct segwit_hash_s { @@ -171,6 +175,9 @@ struct btchip_context_s { cx_sha256_t transactionHashAuthorization; /** Current hash to perform (TRANSACTION_HASH_) */ unsigned char transactionHashOption; + #ifdef HAVE_QTUM_SUPPORT + cx_sha256_t transactionOutputHash; + #endif /* Segregated Witness changes */ @@ -185,6 +192,9 @@ struct btchip_context_s { unsigned char segwitParsedOnce; /** Prevents display of segwit input warning at each InputHashStart APDU */ unsigned char segwitWarningSeen; + #ifdef HAVE_QTUM_SUPPORT + unsigned char signOpSender; + #endif /* /Segregated Witness changes */ diff --git a/include/btchip_helpers.h b/include/btchip_helpers.h index a99504b7..82892374 100644 --- a/include/btchip_helpers.h +++ b/include/btchip_helpers.h @@ -35,14 +35,26 @@ unsigned char btchip_output_script_is_p2sh(unsigned char *buffer); unsigned char btchip_output_script_is_op_return(unsigned char *buffer); unsigned char btchip_output_script_is_native_witness(unsigned char *buffer); +#ifdef HAVE_QTUM_SUPPORT unsigned char btchip_output_script_is_op_create(unsigned char *buffer, size_t size); unsigned char btchip_output_script_is_op_call(unsigned char *buffer, size_t size); +unsigned char btchip_output_script_is_op_sender(unsigned char *buffer, + size_t size); +unsigned char btchip_get_script_size(unsigned char *buffer, size_t maxSize, + unsigned int *scriptSize, unsigned int *discardSize); +unsigned char btchip_get_script_sender_address(unsigned char *buffer, + size_t size, unsigned char *script); +unsigned char btchip_get_sender_sig(unsigned char *buffer, + size_t size, unsigned char **sig, unsigned int *sigSize); +#endif void btchip_sleep16(unsigned short delay); void btchip_sleep32(unsigned long int delayEach, unsigned long int delayRepeat); +unsigned long int btchip_read_u16(unsigned char *buffer, unsigned char be, + unsigned char skipSign); unsigned long int btchip_read_u32(unsigned char *buffer, unsigned char be, unsigned char skipSign); diff --git a/src/btchip_apdu_get_trusted_input.c b/src/btchip_apdu_get_trusted_input.c index cc46f4be..ae16abea 100644 --- a/src/btchip_apdu_get_trusted_input.c +++ b/src/btchip_apdu_get_trusted_input.c @@ -49,6 +49,9 @@ unsigned short btchip_apdu_get_trusted_input() { btchip_context_D.transactionHashOption = TRANSACTION_HASH_FULL; btchip_context_D.usingSegwit = 0; btchip_context_D.usingOverwinter = 0; + #ifdef HAVE_QTUM_SUPPORT + btchip_context_D.signOpSender = 0; + #endif } else if (G_io_apdu_buffer[ISO_OFFSET_P1] != GET_TRUSTED_INPUT_P1_NEXT) { return BTCHIP_SW_INCORRECT_P1_P2; } diff --git a/src/btchip_apdu_hash_input_finalize_full.c b/src/btchip_apdu_hash_input_finalize_full.c index dfb1cd9c..5327dca3 100644 --- a/src/btchip_apdu_hash_input_finalize_full.c +++ b/src/btchip_apdu_hash_input_finalize_full.c @@ -44,7 +44,7 @@ static bool check_output_displayable() { bool displayable = true; unsigned char amount[8], isOpReturn, isP2sh, isNativeSegwit, j, nullAmount = 1; - unsigned char isOpCreate, isOpCall; + unsigned char isOpCreate = 0, isOpCall = 0, isOpSender = 0; for (j = 0; j < 8; j++) { if (btchip_context_D.currentOutput[j] != 0) { @@ -62,12 +62,19 @@ static bool check_output_displayable() { isP2sh = btchip_output_script_is_p2sh(btchip_context_D.currentOutput + 8); isNativeSegwit = btchip_output_script_is_native_witness( btchip_context_D.currentOutput + 8); - isOpCreate = - btchip_output_script_is_op_create(btchip_context_D.currentOutput + 8, - sizeof(btchip_context_D.currentOutput) - 8); - isOpCall = - btchip_output_script_is_op_call(btchip_context_D.currentOutput + 8, - sizeof(btchip_context_D.currentOutput) - 8); + #ifdef HAVE_QTUM_SUPPORT + if(G_coin_config->kind == COIN_KIND_QTUM) { + isOpCreate = + btchip_output_script_is_op_create(btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8); + isOpCall = + btchip_output_script_is_op_call(btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8); + isOpSender = + btchip_output_script_is_op_sender(btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8); + } + #endif if (((G_coin_config->kind == COIN_KIND_QTUM || G_coin_config->kind == COIN_KIND_HYDRA) && !btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) && !isP2sh && !(nullAmount && isOpReturn) && !isOpCreate && !isOpCall) || @@ -77,13 +84,32 @@ static bool check_output_displayable() { PRINTF("Error : Unrecognized output script"); THROW(EXCEPTION); } + #ifdef HAVE_QTUM_SUPPORT + if((G_coin_config->kind == COIN_KIND_QTUM) && isOpSender && (isOpCreate || isOpCall)) + { + unsigned char *sig = 0; + unsigned int sigSize = 0; + btchip_get_sender_sig(btchip_context_D.currentOutput + 8, + sizeof(btchip_context_D.currentOutput) - 8, &sig, &sigSize); + if(!btchip_context_D.signOpSender && sigSize == 0) + { + PRINTF("Error : No op_sender signature"); + THROW(EXCEPTION); + } + if(btchip_context_D.signOpSender && sigSize > 0) + { + PRINTF("Error : op_sender is already signed"); + THROW(EXCEPTION); + } + } + #endif if (btchip_context_D.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 && + if (!isP2sh && !isOpSender && os_memcmp(btchip_context_D.currentOutput + 8 + addressOffset, btchip_context_D.tmpCtx.output.changeAddress, 20) == 0) { @@ -176,7 +202,7 @@ bool handle_output_state() { break; } scriptSize = - btchip_read_u32(btchip_context_D.currentOutput + 9, 0, 0); + btchip_read_u16(btchip_context_D.currentOutput + 9, 0, 0); discardSize = 3; } else { // Unrealistically large script @@ -332,10 +358,13 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal( sw = BTCHIP_SW_INCORRECT_DATA; goto discardTransaction; } + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset, apduLength - hashOffset, NULL, 0); } - else { + else + #endif + { PRINTF("--- ADD TO HASH FULL:\n%.*H\n", apduLength - hashOffset, G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset); cx_hash(&btchip_context_D.transactionHashFull.sha256.header, 0, G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset, @@ -392,10 +421,13 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal( if (btchip_context_D.usingSegwit) { if (!btchip_context_D.segwitParsedOnce) { + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, CX_LAST, btchip_context_D.segwit.cache.hashedOutputs, 0, btchip_context_D.segwit.cache.hashedOutputs, 32); } - else { + else + #endif + { cx_hash(&btchip_context_D.transactionHashFull.sha256.header, CX_LAST, btchip_context_D.segwit.cache.hashedOutputs, 0, @@ -470,7 +502,11 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal( } if (btchip_context_D.usingSegwit && - !btchip_context_D.segwitParsedOnce) { + !btchip_context_D.segwitParsedOnce + #ifdef HAVE_QTUM_SUPPORT + && !btchip_context_D.signOpSender + #endif + ) { // This input cannot be signed when using segwit - just restart. btchip_context_D.segwitParsedOnce = 1; PRINTF("Segwit parsed once\n"); @@ -592,7 +628,11 @@ unsigned char btchip_bagl_user_action(unsigned char confirming) { btchip_context_D.transactionContext.firstSigned = 0; if (btchip_context_D.usingSegwit && - !btchip_context_D.segwitParsedOnce) { + !btchip_context_D.segwitParsedOnce + #ifdef HAVE_QTUM_SUPPORT + && !btchip_context_D.signOpSender + #endif + ) { // This input cannot be signed when using segwit - just restart. btchip_context_D.segwitParsedOnce = 1; PRINTF("Segwit parsed once\n"); diff --git a/src/btchip_apdu_hash_input_start.c b/src/btchip_apdu_hash_input_start.c index a2e7bfb9..16431b98 100644 --- a/src/btchip_apdu_hash_input_start.c +++ b/src/btchip_apdu_hash_input_start.c @@ -27,6 +27,7 @@ #define P2_NEW_SEGWIT_OVERWINTER 0x04 #define P2_NEW_SEGWIT_SAPLING 0x05 #define P2_CONTINUE 0x80 +#define P2_NEW_SENDER 0x81 #define IS_INPUT() \ (G_io_apdu_buffer[ISO_OFFSET_LC] - 1 > 8 \ @@ -64,6 +65,9 @@ unsigned short btchip_apdu_hash_input_start() { } if ((G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW) || + #ifdef HAVE_QTUM_SUPPORT + (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SENDER) || + #endif (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT) || (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR) || (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_OVERWINTER) || @@ -78,6 +82,10 @@ unsigned short btchip_apdu_hash_input_start() { (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_SAPLING); unsigned char usingCashAddr = (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SEGWIT_CASHADDR); + #ifdef HAVE_QTUM_SUPPORT + unsigned char signOpSender = + (G_io_apdu_buffer[ISO_OFFSET_P2] == P2_NEW_SENDER); + #endif // Request PIN validation // Only request PIN validation (user presence) to start a new // transaction signing flow. @@ -92,7 +100,14 @@ unsigned short btchip_apdu_hash_input_start() { btchip_context_D.transactionContext.firstSigned = 1; btchip_context_D.transactionContext.consumeP2SH = 0; btchip_context_D.transactionContext.relaxed = 0; + #ifdef HAVE_QTUM_SUPPORT + if(signOpSender) + usingSegwit = 1; + #endif btchip_context_D.usingSegwit = usingSegwit; + #ifdef HAVE_QTUM_SUPPORT + btchip_context_D.signOpSender = signOpSender; + #endif btchip_context_D.usingCashAddr = (G_coin_config->kind == COIN_KIND_BITCOIN_CASH ? usingCashAddr : 0); diff --git a/src/btchip_apdu_hash_sign.c b/src/btchip_apdu_hash_sign.c index a9c86d03..bd12d5ac 100644 --- a/src/btchip_apdu_hash_sign.c +++ b/src/btchip_apdu_hash_sign.c @@ -56,6 +56,7 @@ unsigned short btchip_apdu_hash_sign() { // Zcash special - store parameters for later + #ifndef USE_NO_OVERWINTER if ((btchip_context_D.usingOverwinter) && (!btchip_context_D.overwinterSignReady) && (btchip_context_D.segwitParsedOnce) && @@ -75,6 +76,7 @@ unsigned short btchip_apdu_hash_sign() { CLOSE_TRY; return BTCHIP_SW_OK; } + #endif if (btchip_context_D.transactionContext.transactionState != BTCHIP_TRANSACTION_SIGN_READY) { @@ -83,11 +85,13 @@ unsigned short btchip_apdu_hash_sign() { goto discardTransaction; } + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter && !btchip_context_D.overwinterSignReady) { PRINTF("Overwinter not ready to sign\n"); sw = BTCHIP_SW_CONDITIONS_OF_USE_NOT_SATISFIED; goto discardTransaction; } + #endif // Read parameters if (G_io_apdu_buffer[ISO_OFFSET_CDATA] > MAX_BIP32_PATH) { @@ -130,9 +134,18 @@ unsigned short btchip_apdu_hash_sign() { if (!btchip_context_D.usingOverwinter) { btchip_write_u32_le(dataBuffer, lockTime); btchip_write_u32_le(dataBuffer + 4, sighashType); - PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); - cx_hash(&btchip_context_D.transactionHashFull.sha256.header, 0, - dataBuffer, sizeof(dataBuffer), NULL, 0); + #ifdef HAVE_QTUM_SUPPORT + if(btchip_context_D.signOpSender) + { + btchip_hash_sender_finalize(dataBuffer, sizeof(dataBuffer)); + } + else + #endif + { + PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(dataBuffer), dataBuffer); + cx_hash(&btchip_context_D.transactionHashFull.sha256.header, 0, + dataBuffer, sizeof(dataBuffer), NULL, 0); + } } // Check if the path needs to be enforced @@ -179,13 +192,26 @@ void btchip_bagl_user_action_signtx(unsigned char confirming, unsigned char dire unsigned char hash[32]; // Fetch the private key btchip_private_derive_keypair(btchip_context_D.transactionSummary.keyPath, 0, NULL, &private_key, NULL); + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, CX_LAST, hash, 0, hash, 32); } - else { + else + #endif + { cx_sha256_t localHash; - cx_hash(&btchip_context_D.transactionHashFull.sha256.header, CX_LAST, - hash, 0, hash, 32); + #ifdef HAVE_QTUM_SUPPORT + if(btchip_context_D.signOpSender) + { + cx_hash(&btchip_context_D.transactionOutputHash.header, CX_LAST, + hash, 0, hash, 32); + } + else + #endif + { + cx_hash(&btchip_context_D.transactionHashFull.sha256.header, CX_LAST, + hash, 0, hash, 32); + } PRINTF("Hash1\n%.*H\n", sizeof(hash), hash); // Rehash diff --git a/src/btchip_hash_sender_sign.c b/src/btchip_hash_sender_sign.c new file mode 100644 index 00000000..d12347a8 --- /dev/null +++ b/src/btchip_hash_sender_sign.c @@ -0,0 +1,99 @@ +/******************************************************************************* +* 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. +********************************************************************************/ +#ifdef HAVE_QTUM_SUPPORT +#include "btchip_internal.h" +#include "btchip_hash_sender_sign.h" + +unsigned char btchip_hash_sender_start(unsigned char* senderOutput) +{ + cx_sha256_init(&btchip_context_D.transactionOutputHash); + + // Use cache data generated from Segwit + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", sizeof(btchip_context_D.transactionVersion), btchip_context_D.transactionVersion); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + btchip_context_D.transactionVersion, + sizeof(btchip_context_D.transactionVersion), + NULL, 0); + + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", sizeof(btchip_context_D.segwit.cache.hashedPrevouts), btchip_context_D.segwit.cache.hashedPrevouts); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + btchip_context_D.segwit.cache.hashedPrevouts, + sizeof(btchip_context_D.segwit.cache + .hashedPrevouts), + NULL, 0); + + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", sizeof(btchip_context_D.segwit.cache.hashedSequence), btchip_context_D.segwit.cache.hashedSequence); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + btchip_context_D.segwit.cache.hashedSequence, + sizeof(btchip_context_D.segwit.cache + .hashedSequence), + NULL, 0); + + // Op sender specific data + unsigned int scriptSize = 0; + unsigned int discardSize = 0; + if(btchip_get_script_size(senderOutput + 8, sizeof(btchip_context_D.currentOutput), &scriptSize, &discardSize)) + { + unsigned outputSize = 8 + scriptSize + discardSize; + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", outputSize, senderOutput); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + senderOutput, + outputSize, + NULL, 0); + + unsigned char scriptCode[26]; + if(!btchip_get_script_sender_address(senderOutput + 8, sizeof(btchip_context_D.currentOutput), scriptCode)) + return 0; + + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", sizeof(scriptCode), scriptCode); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + scriptCode, + sizeof(scriptCode), + NULL, 0); + + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", 8, senderOutput); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + senderOutput, + 8, + NULL, 0); + } + else + return 0; + + return 1; +} + +void btchip_hash_sender_finalize(unsigned char* dataBuffer, unsigned int bufferSize) +{ + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", sizeof(btchip_context_D.segwit.cache.hashedOutputs), btchip_context_D.segwit.cache.hashedOutputs); + cx_hash( + &btchip_context_D.transactionOutputHash.header, 0, + btchip_context_D.segwit.cache.hashedOutputs, + sizeof(btchip_context_D.segwit.cache + .hashedOutputs), + NULL, 0); + PRINTF("--- ADD TO HASH SENDER:\n%.*H\n", bufferSize, dataBuffer); + cx_hash(&btchip_context_D.transactionOutputHash.header, 0, + dataBuffer, bufferSize, NULL, 0); +} +#endif diff --git a/src/btchip_hash_sender_sign.h b/src/btchip_hash_sender_sign.h new file mode 100644 index 00000000..694808b4 --- /dev/null +++ b/src/btchip_hash_sender_sign.h @@ -0,0 +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. +********************************************************************************/ + +#ifndef BTCHIP_HASH_SENDER_SIGN_H +#define BTCHIP_HASH_SENDER_SIGN_H +#ifdef HAVE_QTUM_SUPPORT +#include "os.h" +#include "cx.h" + +unsigned char btchip_hash_sender_start(unsigned char* senderOutput); +void btchip_hash_sender_finalize(unsigned char* dataBuffer, unsigned int bufferSize); +#endif +#endif diff --git a/src/btchip_helpers.c b/src/btchip_helpers.c index a7131b0c..567a5df1 100644 --- a/src/btchip_helpers.c +++ b/src/btchip_helpers.c @@ -18,6 +18,35 @@ #include "btchip_internal.h" #include "btchip_apdu_constants.h" +#ifdef HAVE_QTUM_SUPPORT +/** Script opcodes */ +enum opcodetype +{ + // push value + OP_PUSHDATA1 = 0x4c, + OP_PUSHDATA2 = 0x4d, + OP_PUSHDATA4 = 0x4e, + + // stack ops + OP_DUP = 0x76, + + // bit logic + OP_EQUALVERIFY = 0x88, + + // crypto + OP_HASH160 = 0xa9, + OP_CHECKSIG = 0xac, + + // Execute EXT byte code. + OP_CREATE = 0xc1, + OP_CALL = 0xc2, + OP_SPEND = 0xc3, + OP_SENDER = 0xc4, + + OP_INVALIDOPCODE = 0xff, +}; +#endif + const unsigned char TRANSACTION_OUTPUT_SCRIPT_PRE[] = { 0x19, 0x76, 0xA9, 0x14}; // script length, OP_DUP, OP_HASH160, address length @@ -120,6 +149,130 @@ unsigned char btchip_output_script_is_native_witness(unsigned char *buffer) { return 0; } +#ifdef HAVE_QTUM_SUPPORT +unsigned char btchip_get_script_op(unsigned char ** pc, const unsigned char * end, unsigned char* opcodeRet, unsigned char **pvchRet, unsigned int *pvchSize) +{ + *opcodeRet = OP_INVALIDOPCODE; + if (*pc >= end) + return 0; + + if(pvchRet) + *pvchRet = 0; + if(pvchSize) + *pvchSize = 0; + + // Read instruction + if (end - *pc < 1) + return 0; + unsigned char opcode = *(*pc)++; + + // Immediate operand + if (opcode <= OP_PUSHDATA4) + { + unsigned int nSize = 0; + if (opcode < OP_PUSHDATA1) + { + nSize = opcode; + } + else if (opcode == OP_PUSHDATA1) + { + if (end - *pc < 1) + return 0; + nSize = *(*pc)++; + } + else if (opcode == OP_PUSHDATA2) + { + if (end - *pc < 2) + return 0; + + nSize = btchip_read_u16(*pc, 0, 0); + *pc += 2; + } + else if (opcode == OP_PUSHDATA4) + { + if (end - *pc < 4) + return 0; + nSize = btchip_read_u32(*pc, 0, 0); + *pc += 4; + } + if (end - *pc < 0 || (unsigned int)(end - *pc) < nSize) + return 0; + if(pvchRet) + *pvchRet = *pc; + if(pvchSize) + *pvchSize = nSize; + *pc += nSize; + } + + *opcodeRet = opcode; + return 1; +} + +unsigned char btchip_get_script_size(unsigned char *buffer, size_t maxSize, unsigned int *scriptSize, unsigned int *discardSize) +{ + *scriptSize = 0; + *discardSize = 0; + if (maxSize > 0 && buffer[0] < 0xFD) { + *scriptSize = buffer[0]; + *discardSize = 1; + } else if (maxSize > 2 && buffer[0] == 0xFD) { + *scriptSize = btchip_read_u16(buffer + 1, 0, 0); + *discardSize = 3; + } else { + return 0; + } + + size_t bifferSize = *scriptSize + *discardSize; + if(bifferSize <= maxSize) { + return 1; + } + + return 0; +} + +int btchip_find_script_op(unsigned char *buffer, size_t size, unsigned char op, unsigned char haveSize) +{ + int nFound = 0; + unsigned int scriptSize = size; + unsigned int discardSize = 0; + if(haveSize) + btchip_get_script_size(buffer, size, &scriptSize, &discardSize); + unsigned char opcode = OP_INVALIDOPCODE; + const unsigned char* end = buffer + scriptSize + discardSize; + unsigned char *begin = buffer + discardSize; + for (unsigned char * pc = begin; pc != end && btchip_get_script_op(&pc, end, &opcode, 0, 0);) + if (opcode == op) + ++nFound; + return nFound; +} + +unsigned char btchip_find_script_data(unsigned char *buffer, size_t size, int index, unsigned char haveSize, unsigned char **pvchRet, unsigned int *pvchSize) +{ + unsigned int scriptSize = size; + unsigned int discardSize = 0; + if(haveSize) + btchip_get_script_size(buffer, size, &scriptSize, &discardSize); + unsigned char opcode = OP_INVALIDOPCODE; + const unsigned char* end = buffer + scriptSize + discardSize; + unsigned char *begin = buffer + discardSize; + int i = 0; + for (unsigned char * pc = begin; i < index && pc != end && btchip_get_script_op(&pc, end, &opcode, pvchRet, pvchSize); i++); + return i == index; +} + +void btchip_get_script_p2pkh(const unsigned char *pkh, unsigned char *script, unsigned char haveSize) +{ + unsigned char offset = haveSize ? 1 : 0; + if(haveSize) script[0] = 0x19; + script[0 + offset] = OP_DUP; + script[1 + offset] = OP_HASH160; + script[2 + offset] = 0x14; + os_memcpy(script + 3 + offset, pkh, 20); + script[23 + offset] = OP_EQUALVERIFY; + script[24 + offset] = OP_CHECKSIG; +} +#endif + unsigned char btchip_output_script_is_op_return(unsigned char *buffer) { if (G_coin_config->kind == COIN_KIND_BITCOIN_CASH) { return ((buffer[1] == 0x6A) || ((buffer[1] == 0x00) && (buffer[2] == 0x6A))); @@ -129,26 +282,48 @@ unsigned char btchip_output_script_is_op_return(unsigned char *buffer) { } } -static unsigned char output_script_is_op_create_or_call(unsigned char *buffer, +#ifdef HAVE_QTUM_SUPPORT +static unsigned char output_script_is_op_contract(unsigned char *buffer, size_t size, unsigned char value) { return (!btchip_output_script_is_regular(buffer) && !btchip_output_script_is_p2sh(buffer) && - !btchip_output_script_is_op_return(buffer) && (buffer[0] <= 0xEA) && - (buffer[0] < size) && - (buffer[buffer[0]] == value)); + !btchip_output_script_is_op_return(buffer) && + btchip_find_script_op(buffer, size, value, 1) == 1); } unsigned char btchip_output_script_is_op_create(unsigned char *buffer, size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC1); + return output_script_is_op_contract(buffer, size, OP_CREATE); } unsigned char btchip_output_script_is_op_call(unsigned char *buffer, size_t size) { - return output_script_is_op_create_or_call(buffer, size, 0xC2); + return output_script_is_op_contract(buffer, size, OP_CALL); } +unsigned char btchip_output_script_is_op_sender(unsigned char *buffer, + size_t size) { + return output_script_is_op_contract(buffer, size, OP_SENDER); +} + +unsigned char btchip_get_script_sender_address(unsigned char *buffer, + size_t size, unsigned char *script) { + unsigned char *pkh = 0; + unsigned int pkhSize = 0; + unsigned char ret = btchip_find_script_data(buffer, size, 2, 1, &pkh, &pkhSize) == 1 && pkh != 0 && pkhSize == 20; + if(ret) btchip_get_script_p2pkh(pkh, script, 1); + return ret; +} + +unsigned char btchip_get_sender_sig(unsigned char *buffer, + size_t size, unsigned char **sig, unsigned int *sigSize) { + if(sig == 0 || sigSize == 0) + return 0; + return btchip_find_script_data(buffer, size, 3, 1, sig, sigSize) && *sig != 0 && *sigSize > 0; +} +#endif + unsigned char btchip_rng_u8_modulo(unsigned char modulo) { unsigned int rng_max = 256 % modulo; unsigned int rng_limit = 256 - rng_max; @@ -171,6 +346,26 @@ unsigned char btchip_secure_memcmp(const void *buf1, const void *buf2, return error; } +unsigned long int btchip_read_u16(unsigned char *buffer, unsigned char be, + unsigned char skipSign) { + unsigned char i; + unsigned long int result = 0; + unsigned char shiftValue = (be ? 8 : 0); + for (i = 0; i < 2; i++) { + unsigned char x = (unsigned char)buffer[i]; + if ((i == 0) && skipSign) { + x &= 0x7f; + } + result += ((unsigned long int)x) << shiftValue; + if (be) { + shiftValue -= 8; + } else { + shiftValue += 8; + } + } + return result; +} + unsigned long int btchip_read_u32(unsigned char *buffer, unsigned char be, unsigned char skipSign) { unsigned char i; diff --git a/src/btchip_internal.h b/src/btchip_internal.h index d6b455dd..4222ca3d 100644 --- a/src/btchip_internal.h +++ b/src/btchip_internal.h @@ -27,5 +27,8 @@ #include "btchip_ecc.h" #include "btchip_helpers.h" #include "btchip_transaction.h" +#ifdef HAVE_QTUM_SUPPORT +#include "btchip_hash_sender_sign.h" +#endif #endif diff --git a/src/btchip_transaction.c b/src/btchip_transaction.c index 4f218534..457c04e3 100644 --- a/src/btchip_transaction.c +++ b/src/btchip_transaction.c @@ -86,10 +86,13 @@ unsigned char transaction_amount_sub_be(unsigned char *target, void transaction_offset(unsigned char value) { if ((btchip_context_D.transactionHashOption & TRANSACTION_HASH_FULL) != 0) { PRINTF("--- ADD TO HASH FULL:\n%.*H\n", value, btchip_context_D.transactionBufferPointer); + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionBufferPointer, value, NULL, 0); } - else { + else + #endif + { cx_hash(&btchip_context_D.transactionHashFull.sha256.header, 0, btchip_context_D.transactionBufferPointer, value, NULL, 0); } @@ -163,6 +166,7 @@ void transaction_parse(unsigned char parseMode) { .transactionAmount)); // TODO : transactionControlFid // Reset hashes + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { if (btchip_context_D.segwitParsedOnce) { uint8_t parameters[16]; @@ -178,7 +182,9 @@ void transaction_parse(unsigned char parseMode) { cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, parameters, 16); } } - else { + else + #endif + { cx_sha256_init(&btchip_context_D.transactionHashFull.sha256); } cx_sha256_init( @@ -186,11 +192,14 @@ void transaction_parse(unsigned char parseMode) { if (btchip_context_D.usingSegwit) { btchip_context_D.transactionHashOption = 0; if (!btchip_context_D.segwitParsedOnce) { + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_blake2b_init2(&btchip_context_D.segwit.hash.hashPrevouts.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_PREVOUTS, 16); cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_SEQUENCE, 16); } - else { + else + #endif + { cx_sha256_init( &btchip_context_D.segwit.hash.hashPrevouts.sha256); } @@ -199,6 +208,7 @@ void transaction_parse(unsigned char parseMode) { PRINTF("SEGWIT Version\n%.*H\n",sizeof(btchip_context_D.transactionVersion),btchip_context_D.transactionVersion); PRINTF("SEGWIT HashedPrevouts\n%.*H\n",sizeof(btchip_context_D.segwit.cache.hashedPrevouts),btchip_context_D.segwit.cache.hashedPrevouts); PRINTF("SEGWIT HashedSequence\n%.*H\n",sizeof(btchip_context_D.segwit.cache.hashedSequence),btchip_context_D.segwit.cache.hashedSequence); + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionVersion, sizeof(btchip_context_D.transactionVersion), NULL, 0); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.nVersionGroupId, sizeof(btchip_context_D.nVersionGroupId), NULL, 0); @@ -219,7 +229,9 @@ void transaction_parse(unsigned char parseMode) { } cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.sigHashType, sizeof(btchip_context_D.sigHashType), NULL, 0); } - else { + else + #endif + { PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(btchip_context_D.transactionVersion), btchip_context_D.transactionVersion); cx_hash( &btchip_context_D.transactionHashFull.sha256.header, 0, @@ -412,10 +424,13 @@ void transaction_parse(unsigned char parseMode) { check_transaction_available( 36); // prevout : 32 hash + 4 index if (!btchip_context_D.segwitParsedOnce) { + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts.blake2b.header, 0, btchip_context_D.transactionBufferPointer, 36, NULL, 0); } - else { + else + #endif + { cx_hash( &btchip_context_D.segwit.hash.hashPrevouts .sha256.header, @@ -607,10 +622,13 @@ void transaction_parse(unsigned char parseMode) { if (btchip_context_D.segwitParsedOnce) { // Append the saved value PRINTF("SEGWIT Add value\n%.*H\n",8,btchip_context_D.inputValue); + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.inputValue, 8, NULL, 0); } - else { + else + #endif + { PRINTF("--- ADD TO HASH FULL:\n%.*H\n", sizeof(btchip_context_D.inputValue), btchip_context_D.inputValue); cx_hash(&btchip_context_D .transactionHashFull.sha256.header, @@ -624,10 +642,13 @@ void transaction_parse(unsigned char parseMode) { check_transaction_available(4); if (btchip_context_D.usingSegwit && !btchip_context_D.segwitParsedOnce) { + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, 0, btchip_context_D.transactionBufferPointer, 4, NULL, 0); } - else { + else + #endif + { PRINTF("--- ADD TO HASH FULL:\n%.*H\n", 4, btchip_context_D.transactionBufferPointer); cx_hash(&btchip_context_D.transactionHashFull .sha256.header, @@ -673,11 +694,14 @@ void transaction_parse(unsigned char parseMode) { unsigned char hashedPrevouts[32]; unsigned char hashedSequence[32]; // Flush the cache + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts.blake2b.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32); cx_hash(&btchip_context_D.transactionHashFull.blake2b.header, CX_LAST, hashedSequence, 0, hashedSequence, 32); } - else { + else + #endif + { cx_hash(&btchip_context_D.segwit.hash.hashPrevouts .sha256.header, CX_LAST, hashedPrevouts, 0, hashedPrevouts, 32); @@ -726,10 +750,12 @@ void transaction_parse(unsigned char parseMode) { btchip_context_D.transactionContext .transactionState = BTCHIP_TRANSACTION_PRESIGN_READY; + #ifndef USE_NO_OVERWINTER if (btchip_context_D.usingOverwinter) { cx_blake2b_init2(&btchip_context_D.transactionHashFull.blake2b, 256, NULL, 0, (uint8_t *)OVERWINTER_PARAM_OUTPUTS, 16); } else + #endif if (btchip_context_D.usingSegwit) { cx_sha256_init(&btchip_context_D.transactionHashFull.sha256); } diff --git a/src/main.c b/src/main.c index b4069954..3a283047 100644 --- a/src/main.c +++ b/src/main.c @@ -327,6 +327,17 @@ UX_STEP_CB( &C_icon_crossmark, "Reject", }); +#ifdef HAVE_QTUM_SUPPORT +UX_STEP_CB( + ux_confirm_full_flow_7_step, + pbb, + io_seproxyhal_touch_verify_ok(NULL), + { + &C_icon_validate_14, + "Sign", + "OP_SENDER", + }); +#endif // confirm_full: confirm transaction / Amount: fullAmount / Address: fullAddress / Fees: feesAmount UX_FLOW(ux_confirm_full_flow, &ux_confirm_full_flow_1_step, @@ -337,6 +348,18 @@ UX_FLOW(ux_confirm_full_flow, &ux_confirm_full_flow_6_step ); +#ifdef HAVE_QTUM_SUPPORT +// confirm_full: sign output sender transaction / Amount: fullAmount / Address: fullAddress / Fees: feesAmount +UX_FLOW(ux_confirm_sender_flow, + &ux_confirm_full_flow_1_step, + &ux_confirm_full_flow_2_step, + &ux_confirm_full_flow_3_step, + &ux_confirm_full_flow_4_step, + &ux_confirm_full_flow_7_step, + &ux_confirm_full_flow_6_step +); +#endif + ////////////////////////////////////////////////////////////////////// UX_STEP_NOCB( @@ -420,6 +443,17 @@ UX_STEP_CB( &C_icon_crossmark, "Reject", }); +#ifdef HAVE_QTUM_SUPPORT +UX_STEP_CB( + ux_finalize_flow_7_step, + pbb, + io_seproxyhal_touch_verify_ok(NULL), + { + &C_icon_validate_14, + "Sign", + "OP_SENDER", + }); +#endif // finalize: confirm transaction / Fees: feesAmount UX_FLOW(ux_finalize_flow, &ux_finalize_flow_1_step, @@ -428,6 +462,16 @@ UX_FLOW(ux_finalize_flow, &ux_finalize_flow_6_step ); +#ifdef HAVE_QTUM_SUPPORT +// finalize: sign output sender transaction / Fees: feesAmount +UX_FLOW(ux_finalize_sender_flow, + &ux_finalize_flow_1_step, + &ux_finalize_flow_4_step, + &ux_finalize_flow_7_step, + &ux_finalize_flow_6_step +); +#endif + ////////////////////////////////////////////////////////////////////// UX_STEP_NOCB( ux_display_public_flow_1_step, @@ -841,21 +885,124 @@ uint8_t prepare_fees() { #define MAIDSAFE_ASSETID 3 #define USDT_ASSETID 31 +#ifdef HAVE_QTUM_SUPPORT +#define DELEGATIONS_ADDRESS "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x86" +#define ADD_DELEGATION_HASH "\x4c\x0e\x96\x8c" +#define REMOVE_DELEGATION_HASH "\x3d\x66\x6e\x8b" +#endif + void get_address_from_output_script(unsigned char* script, int script_size, char* out, int out_size) { if (btchip_output_script_is_op_return(script)) { strcpy(out, "OP_RETURN"); return; } + #ifdef HAVE_QTUM_SUPPORT + unsigned char isOpSender = 0; + if(G_coin_config->kind == COIN_KIND_QTUM) { + isOpSender = btchip_output_script_is_op_sender(script, script_size); + } if ((G_coin_config->kind == COIN_KIND_QTUM || G_coin_config->kind == COIN_KIND_HYDRA) && btchip_output_script_is_op_create(script, script_size)) { - strcpy(out, "OP_CREATE"); + if(btchip_context_D.signOpSender && isOpSender) + { + unsigned char* senderOutput = script - 8; + btchip_hash_sender_start(senderOutput); + } + strcpy(out, isOpSender ? "OP_SENDER_CREATE" : "OP_CREATE"); return; } if ((G_coin_config->kind == COIN_KIND_QTUM || G_coin_config->kind == COIN_KIND_HYDRA) && btchip_output_script_is_op_call(script, script_size)) { - strcpy(out, "OP_CALL"); + if(btchip_context_D.signOpSender && isOpSender) + { + unsigned char* senderOutput = script - 8; + btchip_hash_sender_start(senderOutput); + } + unsigned char contractaddress[20]; + int i; + int pos = 1; + int outputsize; + if (script[0] == 0xfd) { + outputsize = script[2] * 0x100 + + script[1] + 3; + } else { + outputsize = script[0] + 1; + } + for (i = 0; i < sizeof(contractaddress); i++) { + contractaddress[i] = script[outputsize - 21 + i]; + } + if (strncmp(contractaddress, DELEGATIONS_ADDRESS, + sizeof(contractaddress)) == 0) { + unsigned char functionhash[4]; + if (script[0] == 0xfd) + pos += 2; // varint>255? + if (!isOpSender) { + pos += script[pos]; // version + pos += script[pos] + 1; // gas limit + pos += script[pos] + 1; // gas price + } else { + pos += script[pos]; // address version + pos += script[pos]; // address + pos += script[pos] + 1; // gas price + if (script[pos] == 0x4c) { // check for OP_PUSHDATA1 + pos += script[pos + 1] + 2; + } else if (script[pos] == 0x00) { + pos += 1; + } + pos += 1; // OP_SENDER + pos += script[pos] + 1; // // version + pos += script[pos] + 1; // gas limit + pos += script[pos] + 1; // gas price + } + if (script[pos] == 0x4c) + pos++; // check for OP_PUSHDATA1 + + for (i = 0; i < sizeof(functionhash); i++) { + functionhash[i] = script[pos + 1 + i]; + } + if (strncmp(functionhash, ADD_DELEGATION_HASH, sizeof(functionhash)) + == 0) { + unsigned char stakeraddress[21]; + char stakerbase58[80]; + unsigned short stakerbase58size; + unsigned char delegationfee; + stakeraddress[0] = G_coin_config->p2pkh_version; + + for (i = 0; i < sizeof(stakeraddress); i++) { + stakeraddress[i + 1] = script[pos + + 17 + i]; + } + stakerbase58size = btchip_public_key_to_encoded_base58( + stakeraddress, sizeof(stakeraddress), + (unsigned char*) stakerbase58, sizeof(stakerbase58), + G_coin_config->p2pkh_version, 1); + stakerbase58[stakerbase58size] = '\0'; + + delegationfee = script[pos + 17 + 20 + + 31]; + snprintf(out, sizeof(vars.tmp.fullAddress), + "Delegate to %s (fee %d %%)", stakerbase58, + delegationfee); + } else if (strncmp(functionhash, REMOVE_DELEGATION_HASH, + sizeof(functionhash)) == 0) { + strcpy(out, "Undelegate"); + } + } else { + unsigned char contractaddressstring[41]; + const char *hex = "0123456789ABCDEF"; + for (i = 0; i < sizeof(contractaddressstring); i = i + 2) { + contractaddressstring[i] = hex[(contractaddress[i / 2] >> 4) + & 0xF]; + contractaddressstring[i + 1] = + hex[contractaddress[i / 2] & 0xF]; + } + contractaddressstring[40] = '\0'; + snprintf(out, sizeof(vars.tmp.fullAddress), + "Call contract %s", contractaddressstring); + } return; } + #endif if (btchip_output_script_is_native_witness(script)) { if (G_coin_config->native_segwit_prefix) { segwit_addr_encode( @@ -1082,7 +1229,17 @@ unsigned int btchip_bagl_finalize_tx() { return 0; } + #ifdef HAVE_QTUM_SUPPORT + if(btchip_context_D.signOpSender) { + ux_flow_init(0, ux_finalize_sender_flow, NULL); + } + else { + ux_flow_init(0, ux_finalize_flow, NULL); + } + #else ux_flow_init(0, ux_finalize_flow, NULL); + #endif + return 1; }