Display public key, support stremed outputs, support P2SH parsing (Nano-S only for the time being), add Peercoin build
This commit is contained in:
parent
ba19526c06
commit
ececc16bee
17
Makefile
17
Makefile
|
@ -21,7 +21,7 @@ APP_LOAD_PARAMS=--appFlags 0x50 --path "" --curve secp256k1
|
|||
|
||||
APPVERSION_M=1
|
||||
APPVERSION_N=1
|
||||
APPVERSION_P=3
|
||||
APPVERSION_P=5
|
||||
APPVERSION=$(APPVERSION_M).$(APPVERSION_N).$(APPVERSION_P)
|
||||
|
||||
#prepare hsm generation
|
||||
|
@ -37,7 +37,7 @@ endif
|
|||
|
||||
all: default
|
||||
|
||||
# consider every intermediate target aTARGET_ID=s final to avoid deleting intermediate files
|
||||
# consider every intermediate target as final to avoid deleting intermediate files
|
||||
.SECONDARY:
|
||||
|
||||
# disable builtin rules that overload the build process (and the debug log !!)
|
||||
|
@ -60,8 +60,9 @@ GLYPH_DESTC := src/glyphs.c
|
|||
GLYPH_DESTH := src/glyphs.h
|
||||
$(GLYPH_DESTC) $(GLYPH_DESTH): $(GLYPH_FILES) $(BOLOS_SDK)/icon.py
|
||||
-rm $@
|
||||
if [ ! -z "$(GLYPH_FILES)" ] ; then for gif in $(GLYPH_FILES) ; do python $(BOLOS_SDK)/icon.py $$gif glyphcheader ; done > $(GLYPH_DESTH) ; fi
|
||||
if [ ! -z "$(GLYPH_FILES)" ] ; then for gif in $(GLYPH_FILES) ; do python $(BOLOS_SDK)/icon.py $$gif glyphcfile ; done > $(GLYPH_DESTC) ; fi
|
||||
for gif in $(GLYPH_FILES) ; do python $(BOLOS_SDK)/icon.py $$gif glyphcheader ; done > $(GLYPH_DESTH)
|
||||
for gif in $(GLYPH_FILES) ; do python $(BOLOS_SDK)/icon.py $$gif glyphcfile ; done > $(GLYPH_DESTC)
|
||||
|
||||
|
||||
SOURCE_PATH := src $(BOLOS_SDK)/src $(dir $(shell find $(BOLOS_SDK)/lib_stusb* | grep "\.c$$"))
|
||||
SOURCE_FILES := $(foreach path, $(SOURCE_PATH),$(shell find $(path) | grep -E "\.c$$|\.s") ) $(GLYPH_DESTC)
|
||||
|
@ -76,7 +77,7 @@ DEFINES += HAVE_BAGL HAVE_SPRINTF
|
|||
#DEFINES += HAVE_PRINTF PRINTF=screen_printf
|
||||
DEFINES += PRINTF\(...\)=
|
||||
DEFINES += HAVE_IO_USB HAVE_L4_USBLIB IO_USB_MAX_ENDPOINTS=6 IO_HID_EP_LENGTH=64 HAVE_USB_APDU
|
||||
DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) TCS_LOADER_PATCH_VERSION=0
|
||||
DEFINES += LEDGER_MAJOR_VERSION=$(APPVERSION_M) LEDGER_MINOR_VERSION=$(APPVERSION_N) LEDGER_PATCH_VERSION=$(APPVERSION_P) TCS_LOADER_PATCH_VERSION=0 APPVERSION=\"$(APPVERSION)\"
|
||||
|
||||
# ifndef COIN
|
||||
# COIN =bitcoin
|
||||
|
@ -110,9 +111,13 @@ else ifeq ($(COIN),stratis)
|
|||
# Stratis
|
||||
DEFINES += BTCHIP_P2PKH_VERSION=63 BTCHIP_P2SH_VERSION=125 BTCHIP_COIN_FAMILY=2 BTCHIP_COINID=\"Stratis\" COINID_UPCASE=\"STRAT\" COLOR_HDR=0x3790CA COLOR_DB=0x9BC8E5 COINID_NAME=\"Strat\" COINID=$(COIN) BTCHIP_COINID_SHORT=\"STRAT\" COIN_STRATIS HAVE_PEERCOIN_SUPPORT
|
||||
APPNAME ="Stratis"
|
||||
else ifeq ($(COIN),peercoin)
|
||||
# Peercoin
|
||||
DEFINES += BTCHIP_P2PKH_VERSION=55 BTCHIP_P2SH_VERSION=117 BTCHIP_COIN_FAMILY=2 BTCHIP_COINID=\"Peercoin\" COINID_UPCASE=\"PPC\" COLOR_HDR=0x3790CA COLOR_DB=0x9BC8E5 COINID_NAME=\"Peercoin\" COINID=$(COIN) BTCHIP_COINID_SHORT=\"PPC\" COIN_PEERCOIN HAVE_PEERCOIN_SUPPORT
|
||||
APPNAME ="Peercoin"
|
||||
else
|
||||
ifeq ($(filter clean,$(MAKECMDGOALS)),)
|
||||
$(error Unsupported COIN - use bitcoin_testnet, bitcoin, litecoin, dogecoin, dash, zcash, stratis)
|
||||
$(error Unsupported COIN - use bitcoin_testnet, bitcoin, litecoin, dogecoin, dash, zcash, stratis, peercoin)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 75 B |
Binary file not shown.
After Width: | Height: | Size: 67 B |
|
@ -80,6 +80,15 @@ enum btchip_transaction_state_e {
|
|||
};
|
||||
typedef enum btchip_transaction_state_e btchip_transaction_state_t;
|
||||
|
||||
enum btchip_output_parsing_state_e {
|
||||
BTCHIP_OUTPUT_PARSING_NONE = 0x00,
|
||||
BTCHIP_OUTPUT_PARSING_NUMBER_OUTPUTS = 0x01,
|
||||
BTCHIP_OUTPUT_PARSING_OUTPUT = 0x02,
|
||||
BTCHIP_OUTPUT_FINALIZE_TX = 0x03,
|
||||
BTCHIP_OUTPUT_HANDLE_LEGACY = 0xFF
|
||||
};
|
||||
typedef enum btchip_output_parsing_state_e btchip_output_parsing_state_t;
|
||||
|
||||
struct segwit_hash_s {
|
||||
cx_sha256_t hashPrevouts;
|
||||
cx_sha256_t hashSequence;
|
||||
|
@ -130,8 +139,6 @@ struct btchip_tmp_output_s {
|
|||
unsigned char changeAccepted;
|
||||
/** Flag set if the outputs have been fragmented */
|
||||
unsigned char multipleOutput;
|
||||
/** Crc of the cleartext output */
|
||||
unsigned short outputCrc;
|
||||
};
|
||||
typedef struct btchip_tmp_output_s btchip_tmp_output_t;
|
||||
|
||||
|
@ -215,6 +222,12 @@ struct btchip_context_s {
|
|||
|
||||
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;
|
||||
};
|
||||
typedef struct btchip_context_s btchip_context_t;
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ struct btchip_transaction_summary_data_s {
|
|||
unsigned char isP2sh;
|
||||
unsigned char arbitraryChange;
|
||||
unsigned char relaxed;
|
||||
unsigned char outputAmount[8];
|
||||
unsigned char fees[8]; // only in wallet mode
|
||||
unsigned char changeAmount[8]; // only in wallet mode
|
||||
unsigned char outputAddress[21]; // only in wallet mode
|
||||
|
@ -44,10 +43,6 @@ typedef struct btchip_transaction_summary_data_s
|
|||
|
||||
struct btchip_transaction_summary_s {
|
||||
unsigned char active;
|
||||
#define ACTION_NONE 0
|
||||
#define ACTION_REQUIRED 1
|
||||
#define ACTION_DONE 2
|
||||
unsigned char action;
|
||||
unsigned char payToAddressVersion;
|
||||
unsigned char payToScriptHashVersion;
|
||||
unsigned char authorizationHash[32];
|
||||
|
|
|
@ -18,14 +18,32 @@
|
|||
#include "btchip_internal.h"
|
||||
#include "btchip_apdu_constants.h"
|
||||
|
||||
#include "btchip_bagl_extensions.h"
|
||||
|
||||
#ifdef HAVE_U2F
|
||||
|
||||
#include "u2f_service.h"
|
||||
#include "u2f_transport.h"
|
||||
|
||||
extern bool fidoActivated;
|
||||
extern volatile u2f_service_t u2fService;
|
||||
void u2f_proxy_response(u2f_service_t *service, unsigned int tx);
|
||||
|
||||
#endif
|
||||
|
||||
#define P1_NO_DISPLAY 0x00
|
||||
#define P1_DISPLAY 0x01
|
||||
|
||||
unsigned short btchip_apdu_get_wallet_public_key() {
|
||||
unsigned char keyLength;
|
||||
unsigned char uncompressedPublicKeys =
|
||||
((N_btchip.bkp.config.options & BTCHIP_OPTION_UNCOMPRESSED_KEYS) != 0);
|
||||
unsigned char keyPath[MAX_BIP32_PATH_LENGTH];
|
||||
unsigned char chainCode[32];
|
||||
bool display = (G_io_apdu_buffer[ISO_OFFSET_P1] == P1_DISPLAY);
|
||||
|
||||
if ((G_io_apdu_buffer[ISO_OFFSET_P1] != 0x00) ||
|
||||
if (((G_io_apdu_buffer[ISO_OFFSET_P1] != P1_NO_DISPLAY) &&
|
||||
(G_io_apdu_buffer[ISO_OFFSET_P1] != P1_DISPLAY)) ||
|
||||
(G_io_apdu_buffer[ISO_OFFSET_P2] != 0x00)) {
|
||||
return BTCHIP_SW_INCORRECT_P1_P2;
|
||||
}
|
||||
|
@ -82,5 +100,47 @@ unsigned short btchip_apdu_get_wallet_public_key() {
|
|||
sizeof(chainCode));
|
||||
btchip_context_D.outLength = 1 + 65 + 1 + keyLength + sizeof(chainCode);
|
||||
|
||||
if (display) {
|
||||
if (keyLength > 50) {
|
||||
return BTCHIP_SW_INCORRECT_DATA;
|
||||
}
|
||||
// Hax, avoid wasting space
|
||||
os_memmove(G_io_apdu_buffer + 200, G_io_apdu_buffer + 67, keyLength);
|
||||
G_io_apdu_buffer[200 + keyLength] = '\0';
|
||||
btchip_context_D.io_flags |= IO_ASYNCH_REPLY;
|
||||
if (!btchip_bagl_display_public_key()) {
|
||||
btchip_context_D.io_flags &= ~IO_ASYNCH_REPLY;
|
||||
btchip_context_D.outLength = 0;
|
||||
return BTCHIP_SW_INCORRECT_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
return BTCHIP_SW_OK;
|
||||
}
|
||||
|
||||
void btchip_bagl_user_action_display(unsigned char confirming) {
|
||||
unsigned short sw = BTCHIP_SW_OK;
|
||||
// confirm and finish the apdu exchange //spaghetti
|
||||
if (confirming) {
|
||||
btchip_context_D.outLength -=
|
||||
2; // status was already set by the last call
|
||||
|
||||
} else {
|
||||
sw = BTCHIP_SW_CONDITIONS_OF_USE_NOT_SATISFIED;
|
||||
btchip_context_D.outLength = 0;
|
||||
}
|
||||
G_io_apdu_buffer[btchip_context_D.outLength++] = sw >> 8;
|
||||
G_io_apdu_buffer[btchip_context_D.outLength++] = sw;
|
||||
|
||||
#ifdef HAVE_U2F
|
||||
if (fidoActivated) {
|
||||
u2f_proxy_response((u2f_service_t *)&u2fService,
|
||||
btchip_context_D.outLength);
|
||||
} else {
|
||||
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX,
|
||||
btchip_context_D.outLength);
|
||||
}
|
||||
#else
|
||||
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, btchip_context_D.outLength);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -40,11 +40,156 @@ void u2f_proxy_response(u2f_service_t *service, unsigned int tx);
|
|||
#define FLAG_SIGNATURE 0x01
|
||||
#define FLAG_CHANGE_VALIDATED 0x80
|
||||
|
||||
extern uint8_t prepare_full_output(uint8_t checkOnly);
|
||||
|
||||
static void btchip_apdu_hash_input_finalize_full_reset(void) {
|
||||
btchip_context_D.currentOutputOffset = 0;
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_PARSING_NUMBER_OUTPUTS;
|
||||
os_memset(btchip_context_D.totalOutputAmount, 0,
|
||||
sizeof(btchip_context_D.totalOutputAmount));
|
||||
btchip_context_D.changeOutputFound = 0;
|
||||
btchip_set_check_internal_structure_integrity(1);
|
||||
}
|
||||
|
||||
static bool check_output_displayable() {
|
||||
bool displayable = true;
|
||||
unsigned char amount[8], isOpReturn, isP2sh, j, nullAmount = 1;
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (btchip_context_D.currentOutput[j] != 0) {
|
||||
nullAmount = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!nullAmount) {
|
||||
btchip_swap_bytes(amount, btchip_context_D.currentOutput, 8);
|
||||
transaction_amount_add_be(btchip_context_D.totalOutputAmount,
|
||||
btchip_context_D.totalOutputAmount, amount);
|
||||
}
|
||||
isOpReturn =
|
||||
btchip_output_script_is_op_return(btchip_context_D.currentOutput + 8);
|
||||
isP2sh = btchip_output_script_is_p2sh(btchip_context_D.currentOutput + 8);
|
||||
if (!btchip_output_script_is_regular(btchip_context_D.currentOutput + 8) &&
|
||||
!isP2sh && !(nullAmount && isOpReturn)) {
|
||||
PRINTF("Error : Unrecognized input script");
|
||||
THROW(EXCEPTION);
|
||||
}
|
||||
if (btchip_context_D.tmpCtx.output.changeInitialized && !isOpReturn) {
|
||||
unsigned char addressOffset =
|
||||
(isP2sh ? OUTPUT_SCRIPT_P2SH_PRE_LENGTH
|
||||
: OUTPUT_SCRIPT_REGULAR_PRE_LENGTH);
|
||||
if (os_memcmp(btchip_context_D.currentOutput + 8 + addressOffset,
|
||||
btchip_context_D.tmpCtx.output.changeAddress + 1,
|
||||
20) == 0) {
|
||||
if (btchip_context_D.changeOutputFound) {
|
||||
PRINTF("Error : Multiple change output found");
|
||||
THROW(EXCEPTION);
|
||||
}
|
||||
btchip_context_D.changeOutputFound = true;
|
||||
displayable = false;
|
||||
}
|
||||
}
|
||||
return displayable;
|
||||
}
|
||||
|
||||
static bool handle_output_state() {
|
||||
uint32_t discardSize = 0;
|
||||
btchip_context_D.discardSize = 0;
|
||||
bool processed = false;
|
||||
switch (btchip_context_D.outputParsingState) {
|
||||
case BTCHIP_OUTPUT_PARSING_NUMBER_OUTPUTS: {
|
||||
btchip_context_D.totalOutputs = 0;
|
||||
if (btchip_context_D.currentOutputOffset < 1) {
|
||||
break;
|
||||
}
|
||||
if (btchip_context_D.currentOutput[0] < 0xFD) {
|
||||
btchip_context_D.totalOutputs = btchip_context_D.remainingOutputs =
|
||||
btchip_context_D.currentOutput[0];
|
||||
discardSize = 1;
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_PARSING_OUTPUT;
|
||||
processed = true;
|
||||
break;
|
||||
}
|
||||
if (btchip_context_D.currentOutput[0] == 0xFD) {
|
||||
if (btchip_context_D.currentOutputOffset < 3) {
|
||||
break;
|
||||
}
|
||||
btchip_context_D.totalOutputs = btchip_context_D.remainingOutputs =
|
||||
(btchip_context_D.currentOutput[2] << 8) |
|
||||
btchip_context_D.currentOutput[1];
|
||||
discardSize = 3;
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_PARSING_OUTPUT;
|
||||
processed = true;
|
||||
break;
|
||||
} else if (btchip_context_D.currentOutput[0] == 0xFE) {
|
||||
if (btchip_context_D.currentOutputOffset < 5) {
|
||||
break;
|
||||
}
|
||||
btchip_context_D.totalOutputs = btchip_context_D.remainingOutputs =
|
||||
btchip_read_u32(btchip_context_D.currentOutput + 1, 0, 0);
|
||||
discardSize = 5;
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_PARSING_OUTPUT;
|
||||
processed = true;
|
||||
break;
|
||||
} else {
|
||||
THROW(EXCEPTION);
|
||||
}
|
||||
} break;
|
||||
|
||||
case BTCHIP_OUTPUT_PARSING_OUTPUT: {
|
||||
unsigned int scriptSize;
|
||||
if (btchip_context_D.currentOutputOffset < 9) {
|
||||
break;
|
||||
}
|
||||
if (btchip_context_D.currentOutput[8] < 0xFD) {
|
||||
scriptSize = btchip_context_D.currentOutput[8];
|
||||
discardSize = 1;
|
||||
} else if (btchip_context_D.currentOutput[8] == 0xFD) {
|
||||
if (btchip_context_D.currentOutputOffset < 9 + 2) {
|
||||
break;
|
||||
}
|
||||
scriptSize =
|
||||
btchip_read_u32(btchip_context_D.currentOutput + 9, 0, 0);
|
||||
discardSize = 3;
|
||||
} else {
|
||||
// Unrealistically large script
|
||||
THROW(EXCEPTION);
|
||||
}
|
||||
if (btchip_context_D.currentOutputOffset <
|
||||
8 + discardSize + scriptSize) {
|
||||
discardSize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
processed = true;
|
||||
|
||||
discardSize += 8 + scriptSize;
|
||||
|
||||
if (check_output_displayable()) {
|
||||
btchip_context_D.io_flags |= IO_ASYNCH_REPLY;
|
||||
|
||||
// The output can be processed by the UI
|
||||
|
||||
btchip_context_D.discardSize = discardSize;
|
||||
discardSize = 0;
|
||||
} else {
|
||||
btchip_context_D.remainingOutputs--;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
THROW(EXCEPTION);
|
||||
}
|
||||
|
||||
if (discardSize != 0) {
|
||||
os_memmove(btchip_context_D.currentOutput,
|
||||
btchip_context_D.currentOutput + discardSize,
|
||||
btchip_context_D.currentOutputOffset - discardSize);
|
||||
btchip_context_D.currentOutputOffset -= discardSize;
|
||||
}
|
||||
|
||||
return processed;
|
||||
}
|
||||
|
||||
unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
||||
btchip_transaction_summary_t *transactionSummary) {
|
||||
unsigned char authorizationHash[32];
|
||||
|
@ -59,26 +204,6 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
unsigned char hashOffset = 0;
|
||||
unsigned char numOutputs = 0;
|
||||
|
||||
SB_CHECK(N_btchip.bkp.config.operationMode);
|
||||
switch (SB_GET(N_btchip.bkp.config.operationMode)) {
|
||||
case BTCHIP_MODE_WALLET:
|
||||
|
||||
break;
|
||||
case BTCHIP_MODE_RELAXED_WALLET:
|
||||
persistentCommit = 1;
|
||||
break;
|
||||
case BTCHIP_MODE_SERVER:
|
||||
break;
|
||||
default:
|
||||
return BTCHIP_SW_CONDITIONS_OF_USE_NOT_SATISFIED;
|
||||
}
|
||||
|
||||
/*
|
||||
if (G_io_apdu_buffer[ISO_OFFSET_P2] != 0) {
|
||||
return BTCHIP_SW_INCORRECT_P1_P2;
|
||||
}
|
||||
*/
|
||||
|
||||
apduLength = G_io_apdu_buffer[ISO_OFFSET_LC];
|
||||
|
||||
if ((p1 != FINALIZE_P1_MORE) && (p1 != FINALIZE_P1_LAST) &&
|
||||
|
@ -113,6 +238,7 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
unsigned char keyLength;
|
||||
if (!btchip_context_D.transactionContext.firstSigned) {
|
||||
// Already validated, should be prevented on the client side
|
||||
CLOSE_TRY;
|
||||
return BTCHIP_SW_OK;
|
||||
}
|
||||
if (!btchip_context_D.tmpCtx.output.changeAccepted) {
|
||||
|
@ -124,6 +250,7 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
if (G_io_apdu_buffer[ISO_OFFSET_CDATA] == 0x00) {
|
||||
// Called with no change path, abort, should be prevented on
|
||||
// the client side
|
||||
CLOSE_TRY;
|
||||
return BTCHIP_SW_OK;
|
||||
}
|
||||
os_memmove(transactionSummary->summarydata.keyPath,
|
||||
|
@ -143,13 +270,13 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
keyLength, // INLEN
|
||||
transactionSummary->summarydata.changeAddress + 1 // OUT
|
||||
);
|
||||
// Commit to persistent memory if necessary
|
||||
os_memmove(
|
||||
btchip_context_D.tmpCtx.output.changeAddress,
|
||||
transactionSummary->summarydata.changeAddress,
|
||||
sizeof(transactionSummary->summarydata.changeAddress));
|
||||
btchip_context_D.tmpCtx.output.changeInitialized = 1;
|
||||
btchip_context_D.tmpCtx.output.changeAccepted = 0;
|
||||
CLOSE_TRY;
|
||||
return BTCHIP_SW_OK;
|
||||
}
|
||||
|
||||
|
@ -161,22 +288,42 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
G_io_apdu_buffer + ISO_OFFSET_CDATA + hashOffset,
|
||||
apduLength - hashOffset, NULL);
|
||||
}
|
||||
if (screenPaired) {
|
||||
btchip_context_D.tmpCtx.output.outputCrc = cx_crc16_update(
|
||||
btchip_context_D.tmpCtx.output.outputCrc,
|
||||
G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength);
|
||||
}
|
||||
|
||||
if ((btchip_context_D.currentOutputOffset + apduLength) >
|
||||
sizeof(btchip_context_D.currentOutput)) {
|
||||
L_DEBUG_APP(("Output is too long to be checked\n"));
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
goto discardTransaction;
|
||||
if (btchip_context_D.transactionContext.firstSigned) {
|
||||
if ((btchip_context_D.currentOutputOffset + apduLength) >
|
||||
sizeof(btchip_context_D.currentOutput)) {
|
||||
L_DEBUG_APP(("Output is too long to be checked\n"));
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
goto discardTransaction;
|
||||
}
|
||||
os_memmove(btchip_context_D.currentOutput +
|
||||
btchip_context_D.currentOutputOffset,
|
||||
G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength);
|
||||
btchip_context_D.currentOutputOffset += apduLength;
|
||||
|
||||
// Check if the legacy UI can be applied
|
||||
if ((G_io_apdu_buffer[ISO_OFFSET_P1] == FINALIZE_P1_LAST) &&
|
||||
!btchip_context_D.tmpCtx.output.multipleOutput &&
|
||||
prepare_full_output(1)) {
|
||||
btchip_context_D.io_flags |= IO_ASYNCH_REPLY;
|
||||
btchip_context_D.outputParsingState =
|
||||
BTCHIP_OUTPUT_HANDLE_LEGACY;
|
||||
btchip_context_D.remainingOutputs = 0;
|
||||
} else {
|
||||
while (handle_output_state() &&
|
||||
(!(btchip_context_D.io_flags & IO_ASYNCH_REPLY)))
|
||||
;
|
||||
|
||||
// Finalize the TX if necessary
|
||||
|
||||
if ((btchip_context_D.remainingOutputs == 0) &&
|
||||
(!(btchip_context_D.io_flags & IO_ASYNCH_REPLY))) {
|
||||
btchip_context_D.io_flags |= IO_ASYNCH_REPLY;
|
||||
btchip_context_D.outputParsingState =
|
||||
BTCHIP_OUTPUT_FINALIZE_TX;
|
||||
}
|
||||
}
|
||||
}
|
||||
os_memmove(btchip_context_D.currentOutput +
|
||||
btchip_context_D.currentOutputOffset,
|
||||
G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength);
|
||||
btchip_context_D.currentOutputOffset += apduLength;
|
||||
|
||||
if (G_io_apdu_buffer[ISO_OFFSET_P1] == FINALIZE_P1_MORE) {
|
||||
if (!btchip_context_D.usingSegwit) {
|
||||
|
@ -185,12 +332,10 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
0, G_io_apdu_buffer + ISO_OFFSET_CDATA, apduLength,
|
||||
NULL);
|
||||
}
|
||||
if (screenPaired) {
|
||||
} else {
|
||||
G_io_apdu_buffer[0] = 0x00;
|
||||
btchip_context_D.outLength = 1;
|
||||
}
|
||||
G_io_apdu_buffer[0] = 0x00;
|
||||
btchip_context_D.outLength = 1;
|
||||
btchip_context_D.tmpCtx.output.multipleOutput = 1;
|
||||
CLOSE_TRY;
|
||||
return BTCHIP_SW_OK;
|
||||
}
|
||||
|
||||
|
@ -228,31 +373,8 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
}
|
||||
}
|
||||
|
||||
// On last output, generate the persistent transaction context if
|
||||
// necessary
|
||||
/*
|
||||
if (btchip_context_D.transactionContext.firstSigned) {
|
||||
os_memset(&transactionSummary, 0, sizeof(transactionSummary));
|
||||
transactionSummary.summarydata.relaxed =
|
||||
btchip_context_D.transactionContext.relaxed;
|
||||
}
|
||||
*/
|
||||
|
||||
/* seem a dup ?!
|
||||
if (G_io_apdu_buffer[ISO_OFFSET_P1] == FINALIZE_P1_LAST) {
|
||||
// On last output, generate the persistent transaction context if
|
||||
necessary
|
||||
if (btchip_context_D.transactionContext.firstSigned) {
|
||||
os_memset(&transactionSummary, 0, sizeof(transactionSummary));
|
||||
transactionSummary.summarydata.relaxed =
|
||||
btchip_context_D.transactionContext.relaxed;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (btchip_context_D.transactionContext.firstSigned) {
|
||||
if (btchip_context_D.tmpCtx.output.changeInitialized) {
|
||||
} else {
|
||||
if (!btchip_context_D.tmpCtx.output.changeInitialized) {
|
||||
os_memset(transactionSummary, 0,
|
||||
sizeof(btchip_transaction_summary_t));
|
||||
}
|
||||
|
@ -262,192 +384,13 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
transactionSummary->payToScriptHashVersion =
|
||||
btchip_context_D.payToScriptHashVersion;
|
||||
|
||||
if (!deepControl &&
|
||||
!(screenPaired &&
|
||||
!btchip_context_D.tmpCtx.output.multipleOutput)) {
|
||||
transactionSummary->summarydata.relaxed = 1;
|
||||
} else {
|
||||
unsigned char offset = ISO_OFFSET_CDATA;
|
||||
unsigned char i;
|
||||
unsigned char changeFilled = 0;
|
||||
unsigned char regularFilled = 0;
|
||||
numOutputs = G_io_apdu_buffer[offset++];
|
||||
// Too many outputs, deny
|
||||
if (numOutputs > 3) {
|
||||
L_DEBUG_APP(("Too many outputs\n"));
|
||||
goto failControl;
|
||||
}
|
||||
for (i = 0; i < numOutputs; i++) {
|
||||
unsigned char tmpVersion;
|
||||
unsigned char address[20];
|
||||
unsigned char *destinationAddress = NULL;
|
||||
unsigned char *destinationAmount = NULL;
|
||||
offset += 8;
|
||||
if (btchip_output_script_is_regular(G_io_apdu_buffer +
|
||||
offset)) {
|
||||
tmpVersion =
|
||||
transactionSummary->payToAddressVersion;
|
||||
os_memmove(address,
|
||||
G_io_apdu_buffer + offset +
|
||||
OUTPUT_SCRIPT_REGULAR_PRE_LENGTH,
|
||||
20);
|
||||
L_DEBUG_BUF(
|
||||
("Regular script, address\n", address, 20));
|
||||
} else if (btchip_output_script_is_p2sh(
|
||||
G_io_apdu_buffer + offset)) {
|
||||
tmpVersion =
|
||||
transactionSummary->payToScriptHashVersion;
|
||||
os_memmove(address,
|
||||
G_io_apdu_buffer + offset +
|
||||
OUTPUT_SCRIPT_P2SH_PRE_LENGTH,
|
||||
20);
|
||||
L_DEBUG_BUF(
|
||||
("P2SH script, address\n", address, 20));
|
||||
} else if (btchip_output_script_is_op_return(
|
||||
G_io_apdu_buffer + offset)) {
|
||||
unsigned char j;
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (G_io_apdu_buffer[offset - 8 + j]) {
|
||||
L_DEBUG_APP(
|
||||
("Output amount is not null\n"));
|
||||
goto failControl;
|
||||
}
|
||||
}
|
||||
goto nextOutput;
|
||||
} else {
|
||||
L_DEBUG_APP(("Unrecognized output script\n"));
|
||||
goto failControl;
|
||||
}
|
||||
// If the address matches the registred change, use it
|
||||
if (btchip_context_D.tmpCtx.output.changeInitialized) {
|
||||
if (os_memcmp(address,
|
||||
btchip_context_D.tmpCtx.output
|
||||
.changeAddress +
|
||||
1,
|
||||
20) == 0) {
|
||||
if (changeFilled) {
|
||||
L_DEBUG_APP(("Change already filled\n"));
|
||||
goto failControl;
|
||||
}
|
||||
destinationAddress =
|
||||
transactionSummary->summarydata
|
||||
.changeAddress;
|
||||
destinationAmount =
|
||||
transactionSummary->summarydata
|
||||
.changeAmount;
|
||||
btchip_context_D.tmpCtx.output.changeChecked =
|
||||
1;
|
||||
changeFilled = 1;
|
||||
}
|
||||
}
|
||||
if (destinationAddress == NULL) {
|
||||
if (changeFilled && regularFilled) {
|
||||
L_DEBUG_APP(
|
||||
("Both output addresses already filled\n"));
|
||||
goto failControl;
|
||||
}
|
||||
if (!regularFilled) {
|
||||
destinationAddress =
|
||||
transactionSummary->summarydata
|
||||
.outputAddress;
|
||||
destinationAmount =
|
||||
transactionSummary->summarydata
|
||||
.outputAmount;
|
||||
regularFilled = 1;
|
||||
} else {
|
||||
destinationAddress =
|
||||
transactionSummary->summarydata
|
||||
.changeAddress;
|
||||
destinationAmount =
|
||||
transactionSummary->summarydata
|
||||
.changeAmount;
|
||||
changeFilled = 1;
|
||||
}
|
||||
}
|
||||
os_memmove(destinationAddress + 1, address, 20);
|
||||
destinationAddress[0] = tmpVersion;
|
||||
btchip_swap_bytes(destinationAmount,
|
||||
G_io_apdu_buffer + offset - 8, 8);
|
||||
// Otherwise fit the next input
|
||||
// Then move to the next input
|
||||
nextOutput:
|
||||
offset += G_io_apdu_buffer[offset] + 1;
|
||||
}
|
||||
goto endControl;
|
||||
failControl:
|
||||
if (deepControl) {
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
goto discardTransaction;
|
||||
} else {
|
||||
// Soft fail, clear validation since it is meaningless
|
||||
btchip_context_D.tmpCtx.output.changeChecked = 0;
|
||||
}
|
||||
endControl:
|
||||
// Internal change is mandatory but wasn't used, deny if it
|
||||
// cannot be validated
|
||||
// (keycard mode without screen cannot verify it)
|
||||
if (deepControl && changeFilled &&
|
||||
!btchip_context_D.tmpCtx.output.changeChecked) {
|
||||
if (((N_btchip.bkp.config.options &
|
||||
BTCHIP_OPTION_ALLOW_ARBITRARY_CHANGE) == 0) ||
|
||||
(keycardActivated && !screenPaired)) {
|
||||
L_DEBUG_APP(
|
||||
("Mandatory change control not done\n"));
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
goto discardTransaction;
|
||||
} else {
|
||||
transactionSummary->summarydata.arbitraryChange = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deepControl) {
|
||||
unsigned char workAmount[8];
|
||||
L_DEBUG_BUF(("Output Amount\n",
|
||||
transactionSummary->summarydata.outputAmount,
|
||||
8));
|
||||
L_DEBUG_BUF(("Change Amount\n",
|
||||
transactionSummary->summarydata.changeAmount,
|
||||
8));
|
||||
L_DEBUG_BUF(
|
||||
("Transaction Amount\n",
|
||||
btchip_context_D.transactionContext.transactionAmount,
|
||||
8));
|
||||
if (transaction_amount_add_be(
|
||||
workAmount,
|
||||
transactionSummary->summarydata.outputAmount,
|
||||
transactionSummary->summarydata.changeAmount) ||
|
||||
transaction_amount_sub_be(
|
||||
transactionSummary->summarydata.fees,
|
||||
btchip_context_D.transactionContext
|
||||
.transactionAmount,
|
||||
workAmount)) {
|
||||
L_DEBUG_APP(("Failed computing fees\n"));
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
goto discardTransaction;
|
||||
}
|
||||
} else {
|
||||
// Only used for relaxed mode
|
||||
os_memmove(
|
||||
transactionSummary->summarydata.outputAmount,
|
||||
btchip_context_D.transactionContext.transactionAmount,
|
||||
8);
|
||||
}
|
||||
|
||||
// Generate new nonce
|
||||
|
||||
cx_rng(transactionSummary->summarydata.transactionNonce, 8);
|
||||
transactionSummary->active =
|
||||
!btchip_context_D.transactionContext.consumeP2SH &&
|
||||
!keycardActivated;
|
||||
} else {
|
||||
}
|
||||
|
||||
if (screenPaired) {
|
||||
} else {
|
||||
G_io_apdu_buffer[0] = 0x00;
|
||||
target++;
|
||||
}
|
||||
G_io_apdu_buffer[0] = 0x00;
|
||||
target++;
|
||||
|
||||
*target = 0x00;
|
||||
target++;
|
||||
|
@ -462,7 +405,6 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
os_memmove(transactionSummary->authorizationHash,
|
||||
authorizationHash,
|
||||
sizeof(transactionSummary->authorizationHash));
|
||||
btchip_context_D.io_flags |= IO_ASYNCH_REPLY;
|
||||
CLOSE_TRY;
|
||||
return BTCHIP_SW_OK;
|
||||
|
||||
|
@ -497,6 +439,10 @@ unsigned short btchip_apdu_hash_input_finalize_full_internal(
|
|||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
btchip_context_D.outLength = 0;
|
||||
|
||||
os_memmove(G_io_apdu_buffer, btchip_context_D.currentOutput,
|
||||
btchip_context_D.currentOutputOffset);
|
||||
btchip_context_D.outLength = btchip_context_D.currentOutputOffset;
|
||||
}
|
||||
FINALLY {
|
||||
btchip_apdu_hash_input_finalize_full_reset();
|
||||
|
@ -512,7 +458,16 @@ unsigned short btchip_apdu_hash_input_finalize_full() {
|
|||
if (btchip_context_D.io_flags & IO_ASYNCH_REPLY) {
|
||||
// if the UI reject the processing of the request, then reply
|
||||
// immediately
|
||||
if (!btchip_bagl_confirm_full_output()) {
|
||||
bool status;
|
||||
if (btchip_context_D.outputParsingState == BTCHIP_OUTPUT_FINALIZE_TX) {
|
||||
status = btchip_bagl_finalize_tx();
|
||||
} else if (btchip_context_D.outputParsingState ==
|
||||
BTCHIP_OUTPUT_HANDLE_LEGACY) {
|
||||
status = btchip_bagl_confirm_full_output();
|
||||
} else {
|
||||
status = btchip_bagl_confirm_single_output();
|
||||
}
|
||||
if (!status) {
|
||||
btchip_context_D.io_flags &= ~IO_ASYNCH_REPLY;
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
|
@ -523,26 +478,81 @@ unsigned short btchip_apdu_hash_input_finalize_full() {
|
|||
return sw;
|
||||
}
|
||||
|
||||
void btchip_bagl_user_action(unsigned char confirming) {
|
||||
unsigned char btchip_bagl_user_action(unsigned char confirming) {
|
||||
unsigned short sw = BTCHIP_SW_OK;
|
||||
// confirm and finish the apdu exchange //spaghetti
|
||||
if (confirming) {
|
||||
btchip_context_D.transactionContext.firstSigned = 0;
|
||||
// Check if all inputs have been confirmed
|
||||
|
||||
if (btchip_context_D.usingSegwit &&
|
||||
!btchip_context_D.segwitParsedOnce) {
|
||||
// This input cannot be signed when using segwit - just restart.
|
||||
btchip_context_D.segwitParsedOnce = 1;
|
||||
L_DEBUG_APP(("Segwit parsed once\n"));
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
} else {
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_SIGN_READY;
|
||||
if (btchip_context_D.outputParsingState ==
|
||||
BTCHIP_OUTPUT_PARSING_OUTPUT) {
|
||||
btchip_context_D.remainingOutputs--;
|
||||
}
|
||||
|
||||
while (btchip_context_D.remainingOutputs != 0) {
|
||||
os_memmove(btchip_context_D.currentOutput,
|
||||
btchip_context_D.currentOutput +
|
||||
btchip_context_D.discardSize,
|
||||
btchip_context_D.currentOutputOffset -
|
||||
btchip_context_D.discardSize);
|
||||
btchip_context_D.currentOutputOffset -=
|
||||
btchip_context_D.discardSize;
|
||||
btchip_context_D.io_flags &= ~IO_ASYNCH_REPLY;
|
||||
while (handle_output_state() &&
|
||||
(!(btchip_context_D.io_flags & IO_ASYNCH_REPLY)))
|
||||
;
|
||||
if (btchip_context_D.io_flags & IO_ASYNCH_REPLY) {
|
||||
if (!btchip_bagl_confirm_single_output()) {
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
break;
|
||||
} else {
|
||||
// Let the UI play
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Out of data to process, wait for the next call
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((btchip_context_D.outputParsingState ==
|
||||
BTCHIP_OUTPUT_PARSING_OUTPUT) &&
|
||||
(btchip_context_D.remainingOutputs == 0)) {
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_FINALIZE_TX;
|
||||
if (!btchip_bagl_finalize_tx()) {
|
||||
btchip_context_D.outputParsingState =
|
||||
BTCHIP_OUTPUT_PARSING_NONE;
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
sw = BTCHIP_SW_INCORRECT_DATA;
|
||||
} else {
|
||||
// Let the UI play
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((btchip_context_D.outputParsingState ==
|
||||
BTCHIP_OUTPUT_FINALIZE_TX) ||
|
||||
(btchip_context_D.outputParsingState ==
|
||||
BTCHIP_OUTPUT_HANDLE_LEGACY)) {
|
||||
btchip_context_D.transactionContext.firstSigned = 0;
|
||||
|
||||
if (btchip_context_D.usingSegwit &&
|
||||
!btchip_context_D.segwitParsedOnce) {
|
||||
// This input cannot be signed when using segwit - just restart.
|
||||
btchip_context_D.segwitParsedOnce = 1;
|
||||
L_DEBUG_APP(("Segwit parsed once\n"));
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_NONE;
|
||||
} else {
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
BTCHIP_TRANSACTION_SIGN_READY;
|
||||
}
|
||||
}
|
||||
btchip_context_D.outLength -=
|
||||
2; // status was already set by the last call
|
||||
|
||||
} else {
|
||||
// Discard transaction
|
||||
btchip_context_D.transactionContext.transactionState =
|
||||
|
@ -553,8 +563,12 @@ void btchip_bagl_user_action(unsigned char confirming) {
|
|||
G_io_apdu_buffer[btchip_context_D.outLength++] = sw >> 8;
|
||||
G_io_apdu_buffer[btchip_context_D.outLength++] = sw;
|
||||
|
||||
// we've finished the processing of the input
|
||||
btchip_apdu_hash_input_finalize_full_reset();
|
||||
if ((btchip_context_D.outputParsingState == BTCHIP_OUTPUT_FINALIZE_TX) ||
|
||||
(btchip_context_D.outputParsingState == BTCHIP_OUTPUT_HANDLE_LEGACY) ||
|
||||
(sw != BTCHIP_SW_OK)) {
|
||||
// we've finished the processing of the input
|
||||
btchip_apdu_hash_input_finalize_full_reset();
|
||||
}
|
||||
|
||||
#ifdef HAVE_U2F
|
||||
if (fidoActivated) {
|
||||
|
@ -567,4 +581,6 @@ void btchip_bagl_user_action(unsigned char confirming) {
|
|||
#else
|
||||
io_exchange(CHANNEL_APDU | IO_RETURN_AFTER_TX, btchip_context_D.outLength);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ unsigned short btchip_apdu_hash_input_start() {
|
|||
os_memset(&btchip_context_D.tmpCtx.output, 0,
|
||||
sizeof(btchip_context_D.tmpCtx.output));
|
||||
btchip_context_D.tmpCtx.output.changeAccepted = 1;
|
||||
btchip_context_D.tmpCtx.output.outputCrc = CX_CRC16_INIT;
|
||||
}
|
||||
} else if (G_io_apdu_buffer[ISO_OFFSET_P2] != P2_CONTINUE) {
|
||||
return BTCHIP_SW_INCORRECT_P1_P2;
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
|
||||
#define __BTCHIP_BAGL_H__
|
||||
|
||||
// btchip asking the UI
|
||||
unsigned int btchip_bagl_confirm_full_output();
|
||||
// btchip asking the legacy grouped UI
|
||||
unsigned int btchip_bagl_confirm_full_output(void);
|
||||
|
||||
// btchip asking the per-output UI
|
||||
unsigned int btchip_bagl_confirm_single_output(void);
|
||||
|
||||
// btchip finalizing the transaction
|
||||
unsigned int btchip_bagl_finalize_tx(void);
|
||||
|
||||
// UI response to btchip to finish the exchange
|
||||
void btchip_bagl_user_action(unsigned char confirming);
|
||||
unsigned char btchip_bagl_user_action(unsigned char confirming);
|
||||
|
||||
// request the UI to redisplay the idle screen
|
||||
void btchip_bagl_idle(void);
|
||||
|
@ -17,4 +23,8 @@ void btchip_bagl_confirm_message_signature(void);
|
|||
// UI response to message signature
|
||||
void btchip_bagl_user_action_message_signing(unsigned char confirming);
|
||||
|
||||
// Public key display
|
||||
unsigned int btchip_bagl_display_public_key(void);
|
||||
void btchip_bagl_user_action_display(unsigned char confirming);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,6 +28,10 @@ void btchip_context_init() {
|
|||
os_memset(&btchip_context_D, 0, sizeof(btchip_context_D));
|
||||
SB_SET(btchip_context_D.halted, 0);
|
||||
btchip_context_D.currentOutputOffset = 0;
|
||||
btchip_context_D.outputParsingState = BTCHIP_OUTPUT_PARSING_NUMBER_OUTPUTS;
|
||||
os_memset(btchip_context_D.totalOutputAmount, 0,
|
||||
sizeof(btchip_context_D.totalOutputAmount));
|
||||
btchip_context_D.changeOutputFound = 0;
|
||||
|
||||
if (N_btchip.config_valid != 0x01) {
|
||||
btchip_autosetup();
|
||||
|
|
1212
src/main.c
1212
src/main.c
File diff suppressed because it is too large
Load Diff
|
@ -158,7 +158,7 @@ const uint8_t const USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] = {
|
|||
};
|
||||
|
||||
const uint8_t const USB_SERIAL_STRING[] = {
|
||||
3 * 2 + 2, USB_DESC_TYPE_STRING, '0', 0, '.', 0, '1', 0,
|
||||
4 * 2 + 2, USB_DESC_TYPE_STRING, '0', 0, '0', 0, '0', 0, '1', 0,
|
||||
};
|
||||
|
||||
const uint8_t const USBD_MANUFACTURER_STRING[] = {
|
||||
|
|
Loading…
Reference in New Issue