diff --git a/firmware/fsm.c b/firmware/fsm.c index 41133ed312..70cfaf08f2 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -31,6 +31,8 @@ #include "protect.h" #include "pinmatrix.h" #include "layout2.h" +#include "address.h" +#include "base58.h" #include "ecdsa.h" #include "reset.h" #include "recovery.h" @@ -579,24 +581,9 @@ void fsm_msgGetAddress(GetAddress *msg) if (!node) return; hdnode_fill_public_key(node); - if (msg->has_multisig) { - layoutProgressSwipe("Preparing", 0); - if (cryptoMultisigPubkeyIndex(&(msg->multisig), node->public_key) < 0) { - fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); - layoutHome(); - return; - } - uint8_t buf[32]; - if (compile_script_multisig_hash(&(msg->multisig), buf) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig script"); - layoutHome(); - return; - } - ripemd160(buf, 32, buf + 1); - buf[0] = coin->address_type_p2sh; // multisig cointype - base58_encode_check(buf, 21, resp->address, sizeof(resp->address)); - } else { - ecdsa_get_address(node->public_key, coin->address_type, resp->address, sizeof(resp->address)); + layoutProgress("Computing address", 0); + if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, resp->address)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); } if (msg->has_show_display && msg->show_display) { diff --git a/firmware/layout2.c b/firmware/layout2.c index 971b7f9f9b..0a8e665231 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -249,6 +249,7 @@ void layoutAddress(const char *address, const char *desc) static unsigned char bitdata[QR_MAX_BITDATA]; int a, i, j; int side = qr_encode(QR_LEVEL_M, 0, address, 0, bitdata); + int startx; if (side > 0 && side <= 29) { oledInvert(0, 0, (side + 2) * 2, (side + 2) * 2); @@ -263,6 +264,20 @@ void layoutAddress(const char *address, const char *desc) } } } + startx = 68; + } else if (side > 0 && side <= 60) { + oledInvert(0, 0, (side + 3), (side + 3)); + for (i = 0; i < side; i++) { + for (j = 0; j< side; j++) { + a = j * side + i; + if (bitdata[a / 8] & (1 << (7 - a % 8))) { + oledClearPixel(2 + i, 2 + j); + } + } + } + startx = side + 6; + } else { + startx = 0; } uint32_t addrlen = strlen(address); @@ -273,12 +288,11 @@ void layoutAddress(const char *address, const char *desc) const char **str = split_message((const uint8_t *)address, addrlen, rowlen); if (desc) { - oledDrawString(68, 0 * 9, desc); + oledDrawString(startx, 0 * 9, desc); + } + for (i = 0; i < 4; i++) { + oledDrawString(startx, (i+1) * 9 + 4, str[i]); } - oledDrawString(68, 1 * 9 + 4, str[0]); - oledDrawString(68, 2 * 9 + 4, str[1]); - oledDrawString(68, 3 * 9 + 4, str[2]); - oledDrawString(68, 4 * 9 + 4, str[3]); static const char *btnYes = "Continue"; oledDrawString(OLED_WIDTH - fontCharWidth('\x06') - 1, OLED_HEIGHT - 8, "\x06"); diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index 7138c65978..5fbfc481bf 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -33,7 +33,7 @@ PublicKey.xpub max_size:113 GetAddress.address_n max_count:8 GetAddress.coin_name max_size:17 -Address.address max_size:41 +Address.address max_size:60 EthereumGetAddress.address_n max_count:8 EthereumAddress.address max_size:20 diff --git a/firmware/protob/messages.pb.h b/firmware/protob/messages.pb.h index 0dad8391fc..9da12a785e 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -127,7 +127,7 @@ typedef struct _WipeDevice { } WipeDevice; typedef struct _Address { - char address[41]; + char address[60]; } Address; typedef struct { @@ -1215,7 +1215,7 @@ extern const pb_field_t DebugLinkFlashErase_fields[2]; #define PublicKey_size (121 + HDNodeType_size) #define GetAddress_size (81 + MultisigRedeemScriptType_size) #define EthereumGetAddress_size 50 -#define Address_size 43 +#define Address_size 62 #define EthereumAddress_size 22 #define WipeDevice_size 0 #define LoadDevice_size (326 + HDNodeType_size) diff --git a/firmware/protob/types.options b/firmware/protob/types.options index 536e91ab48..bd56519202 100644 --- a/firmware/protob/types.options +++ b/firmware/protob/types.options @@ -12,7 +12,7 @@ TxInputType.address_n max_count:8 TxInputType.prev_hash max_size:32 TxInputType.script_sig max_size:1650 -TxOutputType.address max_size:41 +TxOutputType.address max_size:54 TxOutputType.address_n max_count:8 TxOutputType.op_return_data max_size:80 diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index 8dd5cc5dfc..30c52123c5 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -246,7 +246,7 @@ typedef struct { typedef struct _TxOutputType { bool has_address; - char address[41]; + char address[54]; size_t address_n_count; uint32_t address_n[8]; uint64_t amount; @@ -409,9 +409,9 @@ extern const pb_field_t IdentityType_fields[7]; #define CoinType_size 113 #define MultisigRedeemScriptType_size 3741 #define TxInputType_size 5508 -#define TxOutputType_size 3934 +#define TxOutputType_size 3947 #define TxOutputBinType_size 534 -#define TransactionType_size 11042 +#define TransactionType_size 11055 #define TxRequestDetailsType_size 52 #define TxRequestSerializedType_size 2132 #define IdentityType_size 416 diff --git a/firmware/signing.c b/firmware/signing.c index 0a00cf3532..8871b01e4b 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -42,23 +42,32 @@ enum { STAGE_REQUEST_3_OUTPUT, STAGE_REQUEST_4_INPUT, STAGE_REQUEST_4_OUTPUT, - STAGE_REQUEST_5_OUTPUT + STAGE_REQUEST_SEGWIT_INPUT, + STAGE_REQUEST_5_OUTPUT, + STAGE_REQUEST_SEGWIT_WITNESS } signing_stage; static uint32_t idx1, idx2; +static uint32_t signatures; static TxRequest resp; static TxInputType input; static TxOutputBinType bin_output; static TxStruct to, tp, ti; -static SHA256_CTX tc; -static uint8_t hash[32], hash_check[32], privkey[32], pubkey[33], sig[64]; -static uint64_t to_spend, spending, change_spend; +static SHA256_CTX hashers[3]; +static uint8_t privkey[32], pubkey[33], sig[64]; +static uint8_t hash_prevouts[32], hash_sequence[32],hash_outputs[32]; +static uint8_t hash_check[32]; +static uint64_t to_spend, segwit_to_spend, spending, change_spend; static uint32_t version = 1; static uint32_t lock_time = 0; +static uint32_t next_nonsegwit_input; static uint32_t progress, progress_step, progress_meta_step; static bool multisig_fp_set, multisig_fp_mismatch; static uint8_t multisig_fp[32]; +static uint32_t in_address_n[8]; +static size_t in_address_n_count; -/* progress_step/meta_step are fixed point numbers, giving the + +/* progress_step/meta_step are fixed point numbers, giving the * progress per input in permille with these many additional bits. */ #define PROGRESS_PRECISION 16 @@ -78,8 +87,9 @@ Phase1 - check inputs, previous transactions, and outputs foreach I (idx1): Request I STAGE_REQUEST_1_INPUT - Add I to TransactionChecksum - Calculate amount of I: + Add I to segwit hash_prevouts, hash_sequence + Add I to TransactionChecksum (prevout and type) + If not segwit, Calculate amount of I: Request prevhash I, META STAGE_REQUEST_2_PREV_META foreach prevhash I (idx2): Request prevhash I STAGE_REQUEST_2_PREV_INPUT @@ -101,27 +111,43 @@ Phase2: sign inputs, check that nothing changed =============================================== foreach I (idx1): // input to sign - foreach I (idx2): - Request I STAGE_REQUEST_4_INPUT - If idx1 == idx2 - Remember key for signing - Fill scriptsig - Add I to StreamTransactionSign - Add I to TransactionChecksum - foreach O (idx2): - Request O STAGE_REQUEST_4_OUTPUT - Add O to StreamTransactionSign - Add O to TransactionChecksum + if (idx1 is segwit) + Request I STAGE_REQUEST_SEGWIT_INPUT + Return serialized input chunk + + else + foreach I (idx2): + Request I STAGE_REQUEST_4_INPUT + If idx1 == idx2 + Remember key for signing + Fill scriptsig + Add I to StreamTransactionSign + Add I to TransactionChecksum + foreach O (idx2): + Request O STAGE_REQUEST_4_OUTPUT + Add O to StreamTransactionSign + Add O to TransactionChecksum + + Compare TransactionChecksum with checksum computed in Phase 1 + If different: + Failure + Sign StreamTransactionSign + Return signed chunk - Compare TransactionChecksum with checksum computed in Phase 1 - If different: - Failure - Sign StreamTransactionSign - Return signed chunk foreach O (idx1): Request O STAGE_REQUEST_5_OUTPUT Rewrite change address Return O + + +Phase3: sign segwit inputs, check that nothing changed +=============================================== + +foreach I (idx1): // input to sign + Request I STAGE_REQUEST_SEGWIT_WITNESS + Check amount + Sign segwit prevhash, sequence, amount, outputs + Return witness */ void send_req_1_input(void) @@ -224,6 +250,28 @@ void send_req_4_output(void) msg_write(MessageType_MessageType_TxRequest, &resp); } +void send_req_segwit_input(void) +{ + signing_stage = STAGE_REQUEST_SEGWIT_INPUT; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); +} + +void send_req_segwit_witness(void) +{ + signing_stage = STAGE_REQUEST_SEGWIT_WITNESS; + resp.has_request_type = true; + resp.request_type = RequestType_TXINPUT; + resp.has_details = true; + resp.details.has_request_index = true; + resp.details.request_index = idx1; + msg_write(MessageType_MessageType_TxRequest, &resp); +} + void send_req_5_output(void) { signing_stage = STAGE_REQUEST_5_OUTPUT; @@ -242,6 +290,122 @@ void send_req_finished(void) msg_write(MessageType_MessageType_TxRequest, &resp); } +void phase1_request_next_input(void) +{ + if (idx1 < inputs_count - 1) { + idx1++; + send_req_1_input(); + } else { + // compute segwit hashPrevouts & hashSequence + sha256_Final(&hashers[0], hash_prevouts); + sha256_Raw(hash_prevouts, 32, hash_prevouts); + sha256_Final(&hashers[1], hash_sequence); + sha256_Raw(hash_sequence, 32, hash_sequence); + sha256_Final(&hashers[2], hash_check); + // init hashOutputs + sha256_Init(&hashers[0]); + idx1 = 0; + send_req_3_output(); + } +} + +void phase2_request_next_input(void) +{ + if (idx1 == next_nonsegwit_input) { + idx2 = 0; + send_req_4_input(); + } else { + send_req_segwit_input(); + } +} + +void extract_input_bip32_path(const TxInputType *tinput) +{ + if (in_address_n_count == (size_t) -1) { + return; + } + size_t count = tinput->address_n_count; + if (count < 1) { + // no change address allowed + in_address_n_count = (size_t) -1; + return; + } + if (in_address_n_count == 0) { + // initialize in_address_n on first input seen + in_address_n_count = count; + if (count > 2) { // if longer than 2 elements, store first N - 2 + memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)); + } + return; + } + // check whether they are same length + if (in_address_n_count != count) { + in_address_n_count = (size_t) -1; + return; + } + if (count > 2 && memcmp(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)) != 0) { + // mismatch -> no change address allowed + in_address_n_count = (size_t) -1; + return; + } +} + +#define MAX_BIP32_LAST_ELEMENT 1000000 + +bool check_change_bip32_path(const TxOutputType *toutput) +{ + size_t count = toutput->address_n_count; + + if (count < 1 || in_address_n_count < 1) { + return 0; + } + + if (count != in_address_n_count) { + return 0; + } + + if (toutput->address_n[count - 1] > MAX_BIP32_LAST_ELEMENT) { + return 0; + } + + if (count >= 2) { + if (0 != memcmp(in_address_n, toutput->address_n, (count - 2) * sizeof(uint32_t)) || + toutput->address_n[count - 2] != 1) { + return 0; + } + } + + return 1; +} + +bool compile_input_script_sig(TxInputType *tinput) +{ + if (!multisig_fp_mismatch) { + // check that this is still multisig + uint8_t h[32]; + if (tinput->script_type != InputScriptType_SPENDMULTISIG + || cryptoMultisigFingerprint(&(tinput->multisig), h) == 0 + || memcmp(multisig_fp, h, 32) != 0) { + // Transaction has changed during signing + return false; + } + } + memcpy(&node, root, sizeof(HDNode)); + if (hdnode_private_ckd_cached(&node, tinput->address_n, tinput->address_n_count, NULL) == 0) { + // Failed to derive private key + return false; + } + hdnode_fill_public_key(&node); + if (tinput->has_multisig) { + tinput->script_sig.size = compile_script_multisig(&(tinput->multisig), tinput->script_sig.bytes); + } else { // SPENDADDRESS + uint8_t hash[20]; + ecdsa_get_pubkeyhash(node.public_key, hash); + tinput->script_sig.size = compile_script_sig(coin->address_type, hash, tinput->script_sig.bytes); + } + return tinput->script_sig.size > 0; +} + void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinType *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time) { inputs_count = _inputs_count; @@ -251,10 +415,12 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp version = _version; lock_time = _lock_time; + signatures = 0; idx1 = 0; to_spend = 0; spending = 0; change_spend = 0; + segwit_to_spend = 0; memset(&input, 0, sizeof(TxInputType)); memset(&resp, 0, sizeof(TxRequest)); @@ -264,15 +430,16 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp // this means 50 % per phase. progress_step = (500 << PROGRESS_PRECISION) / inputs_count; + in_address_n_count = 0; multisig_fp_set = false; multisig_fp_mismatch = false; + next_nonsegwit_input = 0xffffffff; tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, false); - sha256_Init(&tc); - sha256_Update(&tc, (const uint8_t *)&inputs_count, sizeof(inputs_count)); - sha256_Update(&tc, (const uint8_t *)&outputs_count, sizeof(outputs_count)); - sha256_Update(&tc, (const uint8_t *)&version, sizeof(version)); - sha256_Update(&tc, (const uint8_t *)&lock_time, sizeof(lock_time)); + // segwit hashes for hashPrevouts and hashSequence + sha256_Init(&hashers[0]); + sha256_Init(&hashers[1]); + sha256_Init(&hashers[2]); layoutProgressSwipe("Signing transaction", 0); @@ -281,6 +448,308 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp #define MIN(a,b) (((a)<(b))?(a):(b)) +static bool signing_check_input(TxInputType *txinput) { + /* compute multisig fingerprint */ + /* (if all input share the same fingerprint, outputs having the same fingerprint will be considered as change outputs) */ + if (txinput->has_multisig && !multisig_fp_mismatch + && txinput->script_type == InputScriptType_SPENDMULTISIG) { + uint8_t h[32]; + if (cryptoMultisigFingerprint(&txinput->multisig, h) == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingerprint"); + signing_abort(); + return false; + } + if (multisig_fp_set) { + if (memcmp(multisig_fp, h, 32) != 0) { + multisig_fp_mismatch = true; + } + } else { + memcpy(multisig_fp, h, 32); + multisig_fp_set = true; + } + } else { // single signature + multisig_fp_mismatch = true; + } + // remember the input bip32 path + // change addresses must use the same bip32 path as all inputs + extract_input_bip32_path(txinput); + // compute segwit hashPrevouts & hashSequence + tx_prevout_hash(&hashers[0], txinput); + tx_sequence_hash(&hashers[1], txinput); + // hash prevout and script type to check it later (relevant for fee computation) + tx_prevout_hash(&hashers[2], txinput); + sha256_Update(&hashers[2], &txinput->script_type, sizeof(&txinput->script_type)); + return true; +} + +// check if the hash of the prevtx matches +static bool signing_check_prevtx_hash(void) { + uint8_t hash[32]; + tx_hash_final(&tp, hash, true); + if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash"); + signing_abort(); + return false; + } + phase1_request_next_input(); + return true; +} + +static bool signing_check_output(TxOutputType *txoutput) { + // Phase1: Check outputs + // add it to hash_outputs + // ask user for permission + + // check for change address + bool is_change = false; + if (txoutput->address_n_count > 0) { + if (txoutput->has_address) { + fsm_sendFailure(FailureType_Failure_Other, "Address in change output"); + signing_abort(); + return false; + } + if (txoutput->script_type == OutputScriptType_PAYTOMULTISIG) { + uint8_t h[32]; + if (multisig_fp_set && !multisig_fp_mismatch + && cryptoMultisigFingerprint(&(txoutput->multisig), h) + && memcmp(multisig_fp, h, 32) == 0) { + is_change = check_change_bip32_path(txoutput); + } + } else if (txoutput->script_type == OutputScriptType_PAYTOADDRESS + || ((txoutput->script_type == OutputScriptType_PAYTOWITNESS + || txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) + && txoutput->amount < segwit_to_spend)) { + is_change = check_change_bip32_path(txoutput); + } + } + + if (is_change) { + if (change_spend == 0) { // not set + change_spend = txoutput->amount; + } else { + fsm_sendFailure(FailureType_Failure_Other, "Only one change output allowed"); + signing_abort(); + return false; + } + } + + if (spending + txoutput->amount < spending) { + fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); + signing_abort(); + return false; + } + spending += txoutput->amount; + int co = compile_output(coin, root, txoutput, &bin_output, !is_change); + if (!is_change) { + layoutProgress("Signing transaction", progress); + } + if (co < 0) { + fsm_sendFailure(FailureType_Failure_Other, "Signing cancelled by user"); + signing_abort(); + return false; + } else if (co == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output"); + signing_abort(); + return false; + } + // compute segwit hashOuts + tx_output_hash(&hashers[0], &bin_output); + return true; +} + +static bool signing_check_fee(void) { + // check fees + if (spending > to_spend) { + fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds"); + signing_abort(); + return false; + } + uint64_t fee = to_spend - spending; + uint32_t tx_est_size = transactionEstimateSizeKb(inputs_count, outputs_count); + if (fee > (uint64_t)tx_est_size * coin->maxfee_kb) { + layoutFeeOverThreshold(coin, fee, tx_est_size); + if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Fee over threshold. Signing cancelled."); + signing_abort(); + return false; + } + layoutProgress("Signing transaction", progress); + } + // last confirmation + layoutConfirmTx(coin, to_spend - change_spend, fee); + if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); + signing_abort(); + return false; + } + return true; +} + +static void phase1_request_next_output(void) { + if (idx1 < outputs_count - 1) { + idx1++; + send_req_3_output(); + } else { + sha256_Final(&hashers[0], hash_outputs); + sha256_Raw(hash_outputs, 32, hash_outputs); + if (!signing_check_fee()) { + return; + } + // Everything was checked, now phase 2 begins and the transaction is signed. + progress_meta_step = progress_step / (inputs_count + outputs_count); + layoutProgress("Signing transaction", progress); + idx1 = 0; + phase2_request_next_input(); + } +} + +static bool signing_sign_input(void) { + uint8_t hash[32]; + sha256_Final(&hashers[0], hash); + sha256_Raw(hash, 32, hash); + if (memcmp(hash, hash_outputs, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); + signing_abort(); + return false; + } + tx_hash_final(&ti, hash, false); + resp.has_serialized = true; + resp.serialized.has_signature_index = true; + resp.serialized.signature_index = idx1; + resp.serialized.has_signature = true; + resp.serialized.has_serialized_tx = true; + if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, NULL, NULL) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); + signing_abort(); + return false; + } + resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); + + if (input.has_multisig) { + // fill in the signature + int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), pubkey); + if (pubkey_idx < 0) { + fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); + signing_abort(); + return false; + } + memcpy(input.multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); + input.multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; + input.script_sig.size = serialize_script_multisig(&(input.multisig), input.script_sig.bytes); + if (input.script_sig.size == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize multisig script"); + signing_abort(); + return false; + } + } else { // SPENDADDRESS + input.script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, pubkey, 33, input.script_sig.bytes); + } + resp.serialized.serialized_tx.size = tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); + return true; +} + +static bool signing_sign_segwit_input(TxInputType *txinput) { + // idx1: index to sign + uint8_t hash[32]; + uint32_t sighash = 1; + + if (txinput->script_type == InputScriptType_SPENDWITNESS + || txinput->script_type == InputScriptType_SPENDP2SHWITNESS) { + if (!compile_input_script_sig(txinput)) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); + signing_abort(); + return false; + } + if (txinput->amount > segwit_to_spend) { + fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); + signing_abort(); + return false; + } + segwit_to_spend -= txinput->amount; + + sha256_Init(&hashers[0]); + sha256_Update(&hashers[0], (const uint8_t *)&version, 4); + sha256_Update(&hashers[0], hash_prevouts, 32); + sha256_Update(&hashers[0], hash_sequence, 32); + tx_prevout_hash(&hashers[0], txinput); + tx_script_hash(&hashers[0], txinput->script_sig.size, txinput->script_sig.bytes); + sha256_Update(&hashers[0], (const uint8_t*) &txinput->amount, 8); + tx_sequence_hash(&hashers[0], txinput); + sha256_Update(&hashers[0], hash_outputs, 32); + sha256_Update(&hashers[0], (const uint8_t*) &lock_time, 4); + sha256_Update(&hashers[0], (const uint8_t*) &sighash, 4); + sha256_Final(&hashers[0], hash); + sha256_Raw(hash, 32, hash); + + resp.has_serialized = true; + resp.serialized.has_signature_index = true; + resp.serialized.signature_index = idx1; + resp.serialized.has_signature = true; + resp.serialized.has_serialized_tx = true; + if (ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, NULL, NULL) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); + signing_abort(); + return false; + } + + resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); + if (txinput->has_multisig) { + uint32_t r, i, script_len; + int nwitnesses; + // fill in the signature + int pubkey_idx = cryptoMultisigPubkeyIndex(&(txinput->multisig), node.public_key); + if (pubkey_idx < 0) { + fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); + signing_abort(); + return false; + } + memcpy(txinput->multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); + txinput->multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; + + r = 1; // skip number of items (filled in later) + resp.serialized.serialized_tx.bytes[r] = 0; r++; + nwitnesses = 2; + for (i = 0; i < txinput->multisig.signatures_count; i++) { + if (txinput->multisig.signatures[i].size == 0) { + continue; + } + nwitnesses++; + txinput->multisig.signatures[i].bytes[txinput->multisig.signatures[i].size] = 1; + r += tx_serialize_script(txinput->multisig.signatures[i].size + 1, txinput->multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); + } + script_len = compile_script_multisig(&txinput->multisig, 0); + r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); + r += compile_script_multisig(&txinput->multisig, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.bytes[0] = nwitnesses; + resp.serialized.serialized_tx.size = r; + } else { // single signature + uint32_t r = 0; + r += ser_length(2, resp.serialized.serialized_tx.bytes + r); + resp.serialized.signature.bytes[resp.serialized.signature.size] = 1; + r += tx_serialize_script(resp.serialized.signature.size + 1, resp.serialized.signature.bytes, resp.serialized.serialized_tx.bytes + r); + r += tx_serialize_script(33, node.public_key, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } + } else { + // empty witness + resp.has_serialized = true; + resp.serialized.has_signature_index = false; + resp.serialized.has_signature = false; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.bytes[0] = 0; + resp.serialized.serialized_tx.size = 1; + } + // if last witness add tx footer + if (idx1 == inputs_count - 1) { + uint32_t r = resp.serialized.serialized_tx.size; + r += tx_serialize_footer(&to, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } + return true; +} + +#define ENABLE_SEGWIT_NONSEGWIT_MIXING 0 + void signing_txack(TransactionType *tx) { if (!signing) { @@ -295,40 +764,64 @@ void signing_txack(TransactionType *tx) update_ctr = 0; } - int co; memset(&resp, 0, sizeof(TxRequest)); switch (signing_stage) { case STAGE_REQUEST_1_INPUT: - /* compute multisig fingerprint */ - /* (if all input share the same fingerprint, outputs having the same fingerprint will be considered as change outputs) */ - if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG) { - if (tx->inputs[0].has_multisig && !multisig_fp_mismatch) { - if (multisig_fp_set) { - uint8_t h[32]; - if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), h) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingeprint"); - signing_abort(); - return; - } - if (memcmp(multisig_fp, h, 32) != 0) { - multisig_fp_mismatch = true; - } - } else { - if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), multisig_fp) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingeprint"); - signing_abort(); - return; - } - multisig_fp_set = true; - } + signing_check_input(&tx->inputs[0]); + if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { + // remember the first non-segwit input -- this is the first input + // we need to sign during phase2 + if (next_nonsegwit_input == 0xffffffff) + next_nonsegwit_input = idx1; + memcpy(&input, tx->inputs, sizeof(TxInputType)); +#if !ENABLE_SEGWIT_NONSEGWIT_MIXING + // don't mix segwit and non-segwit inputs + if (idx1 > 0 && to.is_segwit == true) { + fsm_sendFailure(FailureType_Failure_Other, "Mixing segwit and non-segwit inputs is not allowed"); + signing_abort(); + return; } - } else { // InputScriptType_SPENDADDRESS - multisig_fp_mismatch = true; +#endif + send_req_2_prev_meta(); + } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { + if (!coin->has_segwit || !coin->segwit) { + fsm_sendFailure(FailureType_Failure_Other, "Segwit not enabled on this coin"); + signing_abort(); + return; + } + if (!tx->inputs[0].has_amount) { + fsm_sendFailure(FailureType_Failure_Other, "Segwit input without amount"); + signing_abort(); + return; + } + if (to_spend + tx->inputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); + signing_abort(); + return; + } +#if !ENABLE_SEGWIT_NONSEGWIT_MIXING + // don't mix segwit and non-segwit inputs + if (idx1 == 0) { + to.is_segwit = true; + } else if (to.is_segwit == false) { + fsm_sendFailure(FailureType_Failure_Other, "Mixing segwit and non-segwit inputs is not allowed"); + signing_abort(); + return; + } +#else + to.is_segwit = true; +#endif + to_spend += tx->inputs[0].amount; + segwit_to_spend += tx->inputs[0].amount; + phase1_request_next_input(); + } else { + fsm_sendFailure(FailureType_Failure_Other, "Wrong input script type"); + signing_abort(); + return; } - sha256_Update(&tc, (const uint8_t *)tx->inputs, sizeof(TxInputType)); - memcpy(&input, tx->inputs, sizeof(TxInputType)); - send_req_2_prev_meta(); return; case STAGE_REQUEST_2_PREV_META: tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->extra_data_len, false); @@ -364,30 +857,23 @@ void signing_txack(TransactionType *tx) return; } if (idx2 == input.prev_index) { + if (to_spend + tx->bin_outputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); + signing_abort(); + return; + } to_spend += tx->bin_outputs[0].amount; } if (idx2 < tp.outputs_len - 1) { /* Check prevtx of next input */ idx2++; send_req_2_prev_output(); - } else { // last output - if (tp.extra_data_len > 0) { // has extra data - send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); - return; - } - tx_hash_final(&tp, hash, true); - if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash"); - signing_abort(); - return; - } - if (idx1 < inputs_count - 1) { - idx1++; - send_req_1_input(); - } else { - idx1 = 0; - send_req_3_output(); - } + } else if (tp.extra_data_len > 0) { // has extra data + send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); + return; + } else { + /* prevtx is done */ + signing_check_prevtx_hash(); } return; case STAGE_REQUEST_2_PREV_EXTRADATA: @@ -399,150 +885,39 @@ void signing_txack(TransactionType *tx) if (tp.extra_data_received < tp.extra_data_len) { // still some data remanining send_req_2_prev_extradata(tp.extra_data_received, MIN(1024, tp.extra_data_len - tp.extra_data_received)); } else { - tx_hash_final(&tp, hash, true); - if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash"); - signing_abort(); - return; - } - if (idx1 < inputs_count - 1) { - idx1++; - send_req_1_input(); - } else { - idx1 = 0; - send_req_3_output(); - } + signing_check_prevtx_hash(); } return; case STAGE_REQUEST_3_OUTPUT: - { - /* Downloaded output idx1 the first time. - * Add it to transaction check - * Ask for permission. - */ - bool is_change = false; - if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG && - tx->outputs[0].has_multisig && - multisig_fp_set && !multisig_fp_mismatch) { - uint8_t h[32]; - if (cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingeprint"); - signing_abort(); - return; - } - if (memcmp(multisig_fp, h, 32) == 0) { - is_change = true; - } - } else - if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS && - tx->outputs[0].address_n_count > 0) { - is_change = true; - } - - if (is_change) { - if (change_spend == 0) { // not set - change_spend = tx->outputs[0].amount; - } else { - fsm_sendFailure(FailureType_Failure_Other, "Only one change output allowed"); - signing_abort(); - return; - } - } - - spending += tx->outputs[0].amount; - co = compile_output(coin, root, tx->outputs, &bin_output, !is_change); - if (!is_change) { - layoutProgress("Signing transaction", progress); - } - if (co < 0) { - fsm_sendFailure(FailureType_Failure_Other, "Signing cancelled by user"); - signing_abort(); - return; - } else if (co == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output"); - signing_abort(); + if (!signing_check_output(&tx->outputs[0])) { return; } - sha256_Update(&tc, (const uint8_t *)&bin_output, sizeof(TxOutputBinType)); - if (idx1 < outputs_count - 1) { - idx1++; - send_req_3_output(); - } else { - sha256_Final(&tc, hash_check); - // check fees - if (spending > to_spend) { - fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds"); - layoutHome(); - return; - } - uint64_t fee = to_spend - spending; - uint32_t tx_est_size = transactionEstimateSizeKb(inputs_count, outputs_count); - if (fee > (uint64_t)tx_est_size * coin->maxfee_kb) { - layoutFeeOverThreshold(coin, fee, tx_est_size); - if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Fee over threshold. Signing cancelled."); - layoutHome(); - return; - } - layoutProgress("Signing transaction", progress); - } - // last confirmation - layoutConfirmTx(coin, to_spend - change_spend, fee); - if (!protectButton(ButtonRequestType_ButtonRequest_SignTx, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Signing cancelled by user"); - signing_abort(); - return; - } - // Everything was checked, now phase 2 begins and the transaction is signed. - progress_meta_step = progress_step / (inputs_count + outputs_count); - layoutProgress("Signing transaction", progress); - idx1 = 0; - idx2 = 0; - send_req_4_input(); - } + phase1_request_next_output(); return; - } case STAGE_REQUEST_4_INPUT: - progress = 500 + ((idx1 * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); + progress = 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); if (idx2 == 0) { tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, true); - sha256_Init(&tc); - sha256_Update(&tc, (const uint8_t *)&inputs_count, sizeof(inputs_count)); - sha256_Update(&tc, (const uint8_t *)&outputs_count, sizeof(outputs_count)); - sha256_Update(&tc, (const uint8_t *)&version, sizeof(version)); - sha256_Update(&tc, (const uint8_t *)&lock_time, sizeof(lock_time)); - memset(privkey, 0, 32); - memset(pubkey, 0, 33); + sha256_Init(&hashers[0]); } - sha256_Update(&tc, (const uint8_t *)tx->inputs, sizeof(TxInputType)); + // check prevouts and script type + tx_prevout_hash(&hashers[0], tx->inputs); + sha256_Update(&hashers[0], &tx->inputs[0].script_type, sizeof(&tx->inputs[0].script_type)); if (idx2 == idx1) { - memcpy(&input, tx->inputs, sizeof(TxInputType)); - memcpy(&node, root, sizeof(HDNode)); - if (hdnode_private_ckd_cached(&node, tx->inputs[0].address_n, tx->inputs[0].address_n_count, NULL) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Failed to derive private key"); - signing_abort(); - return; - } - hdnode_fill_public_key(&node); - if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG) { - if (!tx->inputs[0].has_multisig) { - fsm_sendFailure(FailureType_Failure_Other, "Multisig info not provided"); - signing_abort(); - return; - } - tx->inputs[0].script_sig.size = compile_script_multisig(&(tx->inputs[0].multisig), tx->inputs[0].script_sig.bytes); - } else { // SPENDADDRESS - ecdsa_get_pubkeyhash(node.public_key, hash); - tx->inputs[0].script_sig.size = compile_script_sig(coin->address_type, hash, tx->inputs[0].script_sig.bytes); - } - if (tx->inputs[0].script_sig.size == 0) { + if (!compile_input_script_sig(&tx->inputs[0])) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); signing_abort(); return; } + memcpy(&input, &tx->inputs[0], sizeof(input)); memcpy(privkey, node.private_key, 32); memcpy(pubkey, node.public_key, 33); } else { + if (next_nonsegwit_input == idx1 && idx2 > idx1 + && (tx->inputs[0].script_type == InputScriptType_SPENDADDRESS + || tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG)) { + next_nonsegwit_input = idx2; + } tx->inputs[0].script_sig.size = 0; } if (!tx_serialize_input_hash(&ti, tx->inputs)) { @@ -554,23 +929,27 @@ void signing_txack(TransactionType *tx) idx2++; send_req_4_input(); } else { + uint8_t hash[32]; + sha256_Final(&hashers[0], hash); + if (memcmp(hash, hash_check, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); + signing_abort(); + return; + } + sha256_Init(&hashers[0]); idx2 = 0; send_req_4_output(); } return; case STAGE_REQUEST_4_OUTPUT: - progress = 500 + ((idx1 * progress_step + (inputs_count + idx2) * progress_meta_step) >> PROGRESS_PRECISION); - co = compile_output(coin, root, tx->outputs, &bin_output, false); - if (co < 0) { - fsm_sendFailure(FailureType_Failure_Other, "Signing cancelled by user"); - signing_abort(); - return; - } else if (co == 0) { + progress = 500 + ((signatures * progress_step + (inputs_count + idx2) * progress_meta_step) >> PROGRESS_PRECISION); + if (compile_output(coin, root, tx->outputs, &bin_output, false) <= 0) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output"); signing_abort(); return; } - sha256_Update(&tc, (const uint8_t *)&bin_output, sizeof(TxOutputBinType)); + // check hashOutputs + tx_output_hash(&hashers[0], &bin_output); if (!tx_serialize_output_hash(&ti, &bin_output)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output"); signing_abort(); @@ -580,62 +959,70 @@ void signing_txack(TransactionType *tx) idx2++; send_req_4_output(); } else { - sha256_Final(&tc, hash); - if (memcmp(hash, hash_check, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); - signing_abort(); + if (!signing_sign_input()) { return; } - tx_hash_final(&ti, hash, false); - resp.has_serialized = true; - resp.serialized.has_signature_index = true; - resp.serialized.signature_index = idx1; - resp.serialized.has_signature = true; - resp.serialized.has_serialized_tx = true; - if (ecdsa_sign_digest(&secp256k1, privkey, hash, sig, NULL, NULL) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Signing failed"); - signing_abort(); - return; - } - resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - if (input.script_type == InputScriptType_SPENDMULTISIG) { - if (!input.has_multisig) { - fsm_sendFailure(FailureType_Failure_Other, "Multisig info not provided"); - signing_abort(); - return; - } - // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), pubkey); - if (pubkey_idx < 0) { - fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); - signing_abort(); - return; - } - memcpy(input.multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); - input.multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; - input.script_sig.size = serialize_script_multisig(&(input.multisig), input.script_sig.bytes); - if (input.script_sig.size == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize multisig script"); - signing_abort(); - return; - } - } else { // SPENDADDRESS - input.script_sig.size = serialize_script_sig(resp.serialized.signature.bytes, resp.serialized.signature.size, pubkey, 33, input.script_sig.bytes); - } - resp.serialized.serialized_tx.size = tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); // since this took a longer time, update progress + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); layoutProgress("Signing transaction", progress); update_ctr = 0; if (idx1 < inputs_count - 1) { idx1++; - idx2 = 0; - send_req_4_input(); + phase2_request_next_input(); } else { idx1 = 0; send_req_5_output(); } } return; + + case STAGE_REQUEST_SEGWIT_INPUT: + resp.has_serialized = true; + resp.serialized.has_signature_index = false; + resp.serialized.has_signature = false; + resp.serialized.has_serialized_tx = true; + if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS + && !tx->inputs[0].has_multisig) { + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); + signing_abort(); + return; + } + // fixup normal p2pkh script into witness 0 p2wpkh script for p2sh + // we convert 76 A9 14 88 AC to 16 00 14 + // P2SH input pushes witness 0 script + tx->inputs[0].script_sig.size = 0x17; // drops last 2 bytes. + tx->inputs[0].script_sig.bytes[0] = 0x16; // push 22 bytes; replaces OP_DUP + tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script ; replaces OP_HASH160 + // digest is already in right place. + } else if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { + // Prepare P2SH witness script. + tx->inputs[0].script_sig.size = 0x23; // 35 bytes long: + tx->inputs[0].script_sig.bytes[0] = 0x22; // push 34 bytes (full witness script) + tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script + tx->inputs[0].script_sig.bytes[2] = 0x20; // push 32 bytes (digest) + // compute digest of multisig script + if (!compile_script_multisig_hash(&tx->inputs[0].multisig, tx->inputs[0].script_sig.bytes + 3)) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); + signing_abort(); + return; + } + } else { + // direct witness scripts require zero scriptSig + tx->inputs[0].script_sig.size = 0; + } + resp.serialized.serialized_tx.size = tx_serialize_input(&to, &tx->inputs[0], resp.serialized.serialized_tx.bytes); + update_ctr = 0; + if (idx1 < inputs_count - 1) { + idx1++; + phase2_request_next_input(); + } else { + idx1 = 0; + send_req_5_output(); + } + return; + case STAGE_REQUEST_5_OUTPUT: if (compile_output(coin, root, tx->outputs, &bin_output,false) <= 0) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile output"); @@ -648,13 +1035,33 @@ void signing_txack(TransactionType *tx) if (idx1 < outputs_count - 1) { idx1++; send_req_5_output(); + } else if (to.is_segwit) { + idx1 = 0; + send_req_segwit_witness(); + } else { + send_req_finished(); + signing_abort(); + } + return; + + case STAGE_REQUEST_SEGWIT_WITNESS: + if (!signing_sign_segwit_input(&tx->inputs[0])) { + return; + } + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); + layoutProgress("Signing transaction", progress); + update_ctr = 0; + if (idx1 < inputs_count - 1) { + idx1++; + send_req_segwit_witness(); } else { send_req_finished(); signing_abort(); } return; } - + fsm_sendFailure(FailureType_Failure_Other, "Signing error"); signing_abort(); } diff --git a/firmware/transaction.c b/firmware/transaction.c index 36a8bdc90b..5956f12c07 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -32,6 +32,8 @@ #include "messages.pb.h" #include "types.pb.h" +static const uint8_t segwit_header[2] = {0,1}; + uint32_t op_push(uint32_t i, uint8_t *out) { if (i < 0x4C) { out[0] = i & 0xFF; @@ -56,95 +58,118 @@ uint32_t op_push(uint32_t i, uint8_t *out) { return 5; } +bool compute_address(const CoinType *coin, + InputScriptType script_type, + const HDNode *node, + bool has_multisig, const MultisigRedeemScriptType *multisig, + char address[MAX_ADDR_SIZE]) { + + uint8_t raw[32]; + uint8_t digest[MAX_ADDR_RAW_SIZE]; + size_t prelen; + + if (has_multisig) { + if (cryptoMultisigPubkeyIndex(multisig, node->public_key) < 0) { + return 0; + } + if (compile_script_multisig_hash(multisig, digest) == 0) { + return 0; + } + if (script_type == InputScriptType_SPENDWITNESS) { + // segwit p2wsh: script hash is single sha256 + if (!coin->has_segwit || !coin->segwit) { + return 0; + } + if (!coin->has_address_type_p2wsh) { + return 0; + } + prelen = address_prefix_bytes_len(coin->address_type_p2wsh); + address_write_prefix_bytes(coin->address_type_p2wsh, raw); + raw[prelen] = 0; // version byte + raw[prelen + 1] = 0; // always 0, see bip-142 + memcpy(raw+prelen+2, digest, 32); + if (!base58_encode_check(raw, prelen + 34, address, MAX_ADDR_SIZE)) { + return 0; + } + } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { + // segwit p2wsh encapsuled in p2sh address + if (!coin->has_segwit || !coin->segwit) { + return 0; + } + if (!coin->has_address_type_p2sh) { + return 0; + } + raw[0] = 0; // push version + raw[1] = 32; // push 32 bytes + memcpy(raw+2, digest, 32); // push hash + sha256_Raw(raw, 34, digest); + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) { + return 0; + } + } else { + // non-segwit p2sh multisig + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) { + return 0; + } + } + } else if (script_type == InputScriptType_SPENDWITNESS) { + // segwit p2wpkh: pubkey hash is ripemd160 of sha256 + if (!coin->has_segwit || !coin->segwit) { + return 0; + } + if (!coin->has_address_type_p2wpkh) { + return 0; + } + prelen = address_prefix_bytes_len(coin->address_type_p2wpkh); + address_write_prefix_bytes(coin->address_type_p2wpkh, raw); + raw[prelen] = 0; // version byte + raw[prelen + 1] = 0; // always 0, see bip-142 + ecdsa_get_pubkeyhash(node->public_key, raw + prelen + 2); + if (!base58_encode_check(raw, prelen + 22, address, MAX_ADDR_SIZE)) { + return 0; + } + } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { + // segwit p2wpkh embedded in p2sh + if (!coin->has_segwit || !coin->segwit) { + return 0; + } + if (!coin->has_address_type_p2sh) { + return 0; + } + prelen = address_prefix_bytes_len(coin->address_type_p2sh); + raw[0] = 0; // version byte + raw[1] = 20; // push 20 bytes + ecdsa_get_pubkeyhash(node->public_key, raw + 2); + sha256_Raw(raw, 22, digest); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + if (!base58_encode_check(raw, prelen + 20, address, MAX_ADDR_SIZE)) { + return 0; + } + } else { + ecdsa_get_address(node->public_key, coin->address_type, address, MAX_ADDR_SIZE); + } + return 1; +} + int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm) { memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - - if (in->script_type == OutputScriptType_PAYTOADDRESS) { - - // address_n provided-> change address -> calculate from address_n - if (in->address_n_count > 0) { - HDNode node; - memcpy(&node, root, sizeof(HDNode)); - if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, NULL) == 0) { - return 0; - } - layoutProgressUpdate(true); - hdnode_get_address_raw(&node, coin->address_type, addr_raw); - } else - if (in->has_address) { // address provided -> regular output - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } - } - if (!ecdsa_address_decode(in->address, coin->address_type, addr_raw)) { - return 0; - } - - } else { // does not have address_n neither address -> error - return 0; - } - - out->script_pubkey.bytes[0] = 0x76; // OP_DUP - out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 3, addr_raw + address_prefix_bytes_len(coin->address_type), 20); - out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY - out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG - out->script_pubkey.size = 25; - return 25; - } - - if (in->script_type == OutputScriptType_PAYTOSCRIPTHASH) { - if (!in->has_address || !ecdsa_address_decode(in->address, coin->address_type_p2sh, addr_raw)) { - return 0; - } - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } - } - out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + address_prefix_bytes_len(coin->address_type_p2sh), 20); - out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL - out->script_pubkey.size = 23; - return 23; - } - - if (in->script_type == OutputScriptType_PAYTOMULTISIG) { - uint8_t buf[32]; - size_t prefix_bytes = address_prefix_bytes_len(coin->address_type_p2sh); - if (!in->has_multisig) { - return 0; - } - if (compile_script_multisig_hash(&(in->multisig), buf) == 0) { - return 0; - } - address_write_prefix_bytes(coin->address_type_p2sh, addr_raw); - ripemd160(buf, 32, addr_raw + prefix_bytes); - if (needs_confirm) { - base58_encode_check(addr_raw, prefix_bytes + 20, in->address, sizeof(in->address)); - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } - } - out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 - out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes - memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_bytes, 20); - out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL - out->script_pubkey.size = 23; - return 23; - } + size_t addr_raw_len; if (in->script_type == OutputScriptType_PAYTOOPRETURN) { - if (in->amount != 0) return 0; // only 0 satoshi allowed for OP_RETURN + // only 0 satoshi allowed for OP_RETURN + if (in->amount != 0) { + return 0; // failed to compile output + } uint32_t r = 0; out->script_pubkey.bytes[0] = 0x6A; r++; // OP_RETURN r += op_push(in->op_return_data.size, out->script_pubkey.bytes + r); @@ -153,7 +178,85 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T return r; } - return 0; + if (in->address_n_count > 0) { + HDNode node; + InputScriptType input_script_type; + + switch (in->script_type) { + case OutputScriptType_PAYTOADDRESS: + input_script_type = InputScriptType_SPENDADDRESS; + break; + case OutputScriptType_PAYTOMULTISIG: + input_script_type = InputScriptType_SPENDMULTISIG; + break; + case OutputScriptType_PAYTOWITNESS: + input_script_type = InputScriptType_SPENDWITNESS; + break; + case OutputScriptType_PAYTOP2SHWITNESS: + input_script_type = InputScriptType_SPENDP2SHWITNESS; + break; + default: + return 0; // failed to compile output + } + memcpy(&node, root, sizeof(HDNode)); + if (hdnode_private_ckd_cached(&node, in->address_n, in->address_n_count, NULL) == 0) { + return 0; // failed to compile output + } + hdnode_fill_public_key(&node); + if (!compute_address(coin, input_script_type, &node, + in->has_multisig, &in->multisig, + in->address)) { + return 0; // failed to compile output + } + } else if (!in->has_address) { + return 0; // failed to compile output + } + + addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE); + size_t prefix_len; + if (address_check_prefix(addr_raw, coin->address_type) // p2pkh + && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type))) { + + out->script_pubkey.bytes[0] = 0x76; // OP_DUP + out->script_pubkey.bytes[1] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[2] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 3, addr_raw + prefix_len, 20); + out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY + out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG + out->script_pubkey.size = 25; + } else if (address_check_prefix(addr_raw, coin->address_type_p2sh) // p2sh + && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2sh))) { + out->script_pubkey.bytes[0] = 0xA9; // OP_HASH_160 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20); + out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL + out->script_pubkey.size = 23; + } else if (address_check_prefix(addr_raw, coin->address_type_p2wpkh) + && addr_raw_len == 22 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wpkh)) + && addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wpkh v0 + out->script_pubkey.bytes[0] = 0x00; // version 0 + out->script_pubkey.bytes[1] = 0x14; // pushing 20 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 20); + out->script_pubkey.size = 22; + } else if (address_check_prefix(addr_raw, coin->address_type_p2wsh) + && addr_raw_len == 34 + (prefix_len = address_prefix_bytes_len(coin->address_type_p2wsh)) + && addr_raw[prefix_len] == 0 && addr_raw[prefix_len + 1] == 0) { // p2wsh v0 + out->script_pubkey.bytes[0] = 0x00; // version 0 + out->script_pubkey.bytes[1] = 0x20; // pushing 32 bytes + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 32); + out->script_pubkey.size = 34; + } else { + return 0; + } + + if (needs_confirm) { + layoutConfirmOutput(coin, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return -1; // user aborted + } + } + + return out->script_pubkey.size; } uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out) @@ -259,16 +362,64 @@ uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uin // tx methods +uint32_t tx_prevout_hash(SHA256_CTX *ctx, const TxInputType *input) +{ + int i; + for (i = 0; i < 32; i++) { + sha256_Update(ctx, &(input->prev_hash.bytes[31 - i]), 1); + } + sha256_Update(ctx, (const uint8_t *)&input->prev_index, 4); + return 36; +} + +uint32_t tx_script_hash(SHA256_CTX *ctx, uint32_t size, const uint8_t *data) +{ + int r = ser_length_hash(ctx, size); + sha256_Update(ctx, data, size); + return r + size; +} + +uint32_t tx_sequence_hash(SHA256_CTX *ctx, const TxInputType *input) +{ + sha256_Update(ctx, (const uint8_t *)&input->sequence, 4); + return 4; +} + +uint32_t tx_output_hash(SHA256_CTX *ctx, const TxOutputBinType *output) +{ + uint32_t r = 0; + sha256_Update(ctx, (const uint8_t *)&output->amount, 8); r += 8; + r += tx_script_hash(ctx, output->script_pubkey.size, output->script_pubkey.bytes); + return r; +} + +uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) +{ + int r = ser_length(size, out); + memcpy(out + r, data, size); + return r + size; +} + uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { + int r = 4; memcpy(out, &(tx->version), 4); - return 4 + ser_length(tx->inputs_len, out + 4); + if (tx->is_segwit) { + memcpy(out + r, segwit_header, 2); + r += 2; + } + return r + ser_length(tx->inputs_len, out + r); } uint32_t tx_serialize_header_hash(TxStruct *tx) { + int r = 4; sha256_Update(&(tx->ctx), (const uint8_t *)&(tx->version), 4); - return 4 + ser_length_hash(&(tx->ctx), tx->inputs_len); + if (tx->is_segwit) { + sha256_Update(&(tx->ctx), segwit_header, 2); + r += 2; + } + return r + ser_length_hash(&(tx->ctx), tx->inputs_len); } uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out) @@ -287,8 +438,7 @@ uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out } r += 32; memcpy(out + r, &input->prev_index, 4); r += 4; - r += ser_length(input->script_sig.size, out + r); - memcpy(out + r, input->script_sig.bytes, input->script_sig.size); r += input->script_sig.size; + r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); memcpy(out + r, &input->sequence, 4); r += 4; tx->have_inputs++; @@ -299,7 +449,6 @@ uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) { - int i; if (tx->have_inputs >= tx->inputs_len) { // already got all inputs return 0; @@ -308,14 +457,9 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) if (tx->have_inputs == 0) { r += tx_serialize_header_hash(tx); } - for (i = 0; i < 32; i++) { - sha256_Update(&(tx->ctx), &(input->prev_hash.bytes[31 - i]), 1); - } - r += 32; - sha256_Update(&(tx->ctx), (const uint8_t *)&input->prev_index, 4); r += 4; - r += ser_length_hash(&(tx->ctx), input->script_sig.size); - sha256_Update(&(tx->ctx), input->script_sig.bytes, input->script_sig.size); r += input->script_sig.size; - sha256_Update(&(tx->ctx), (const uint8_t *)&input->sequence, 4); r += 4; + r += tx_prevout_hash(&(tx->ctx), input); + r += tx_script_hash(&(tx->ctx), input->script_sig.size, input->script_sig.bytes); + r += tx_sequence_hash(&(tx->ctx), input); tx->have_inputs++; tx->size += r; @@ -372,10 +516,10 @@ uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_ r += tx_serialize_middle(tx, out + r); } memcpy(out + r, &output->amount, 8); r += 8; - r += ser_length(output->script_pubkey.size, out + r); - memcpy(out + r, output->script_pubkey.bytes, output->script_pubkey.size); r+= output->script_pubkey.size; + r += tx_serialize_script(output->script_pubkey.size, output->script_pubkey.bytes, out + r); tx->have_outputs++; - if (tx->have_outputs == tx->outputs_len) { + if (tx->have_outputs == tx->outputs_len + && !tx->is_segwit) { r += tx_serialize_footer(tx, out + r); } tx->size += r; @@ -396,11 +540,10 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) if (tx->have_outputs == 0) { r += tx_serialize_middle_hash(tx); } - sha256_Update(&(tx->ctx), (const uint8_t *)&output->amount, 8); r += 8; - r += ser_length_hash(&(tx->ctx), output->script_pubkey.size); - sha256_Update(&(tx->ctx), output->script_pubkey.bytes, output->script_pubkey.size); r+= output->script_pubkey.size; + r += tx_output_hash(&(tx->ctx), output); tx->have_outputs++; - if (tx->have_outputs == tx->outputs_len) { + if (tx->have_outputs == tx->outputs_len + && !tx->is_segwit) { r += tx_serialize_footer_hash(tx); } tx->size += r; @@ -439,6 +582,7 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v tx->extra_data_len = extra_data_len; tx->extra_data_received = 0; tx->size = 0; + tx->is_segwit = false; sha256_Init(&(tx->ctx)); } diff --git a/firmware/transaction.h b/firmware/transaction.h index 71ec1c1137..06d07f765e 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -32,7 +32,7 @@ typedef struct { uint32_t version; uint32_t lock_time; - bool add_hash_type; + bool add_hash_type, is_segwit; uint32_t have_inputs; uint32_t have_outputs; @@ -45,12 +45,21 @@ typedef struct { SHA256_CTX ctx; } TxStruct; +bool compute_address(const CoinType *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, char address[MAX_ADDR_SIZE]); uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out); uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out); uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, uint8_t *hash); uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t *out); uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out); int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm); + +uint32_t tx_prevout_hash(SHA256_CTX *ctx, const TxInputType *input); +uint32_t tx_script_hash(SHA256_CTX *ctx, uint32_t size, const uint8_t *data); +uint32_t tx_sequence_hash(SHA256_CTX *ctx, const TxInputType *input); +uint32_t tx_output_hash(SHA256_CTX *ctx, const TxOutputBinType *output); +uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out); + +uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out); uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out); uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out);