Display public key, support stremed outputs, support P2SH parsing (Nano-S only for the time being), add Peercoin build

This commit is contained in:
BTChip github 2017-03-03 12:07:51 +01:00
parent ba19526c06
commit ececc16bee
No known key found for this signature in database
GPG Key ID: 48BCF826EBFA4D17
13 changed files with 1032 additions and 844 deletions

View File

@ -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

BIN
glyphs/icon_dashboard.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
glyphs/icon_peercoin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 B

BIN
icon_peercoin.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 B

View File

@ -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;

View File

@ -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];

View File

@ -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
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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[] = {