From 1bd4b99f95f15771d24ee2f1f2552a335bede380 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 27 Apr 2016 23:38:59 +0200 Subject: [PATCH 01/25] Allow SegWit addresses New output scripts for segwit addresses in accordance to BIP-142 and BIP-141. This allows Trezor to pay to segwit users, but it doesn't enable segwit for Trezor itself. --- firmware/protob/types.options | 2 +- firmware/protob/types.pb.h | 2 +- firmware/transaction.c | 91 +++++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 34 deletions(-) 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 a7dd51e8a2..39ddfe3d70 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -229,7 +229,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; diff --git a/firmware/transaction.c b/firmware/transaction.c index c4667b963b..fd032067a2 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -61,11 +61,68 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; + int addr_raw_len; + + if (in->has_address) { // address provided -> regular output + addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE); + if (in->script_type != OutputScriptType_PAYTOADDRESS) { + // allow for p2sh (backward compatibility only) + if (in->script_type != OutputScriptType_PAYTOSCRIPTHASH + || addr_raw_len != 21 + || addr_raw[0] != coin->address_type_p2sh) { + return 0; + } + } + + if (addr_raw_len == 21 && + addr_raw[0] == coin->address_type) { // p2pkh + + 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 + 1, 20); + out->script_pubkey.bytes[23] = 0x88; // OP_EQUALVERIFY + out->script_pubkey.bytes[24] = 0xAC; // OP_CHECKSIG + out->script_pubkey.size = 25; + } else if (addr_raw_len == 21 + && addr_raw[0] == coin->address_type_p2sh) { // 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 + 1, 20); + out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL + out->script_pubkey.size = 23; + } else if (addr_raw_len == 23 + && addr_raw[0] == coin->address_type_p2wpkh + && addr_raw[1] == 0 && addr_raw[2] == 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 + 3, 20); + out->script_pubkey.size = 22; + } else if (addr_raw_len == 35 + && addr_raw[0] == coin->address_type_p2wsh + && addr_raw[1] == 0 && addr_raw[2] == 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 + 3, 32); + out->script_pubkey.size = 34; + } else { + return 0; + } + + if (needs_confirm) { + layoutConfirmOutput(coin, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return -1; + } + } + + return out->script_pubkey.size; + } if (in->script_type == OutputScriptType_PAYTOADDRESS) { - // address_n provided-> change address -> calculate from address_n - if (in->address_n_count > 0) { + if (in->script_type == OutputScriptType_PAYTOADDRESS && + 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) == 0) { @@ -73,18 +130,6 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } 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; } @@ -99,24 +144,6 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T 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); From 5c60be9854d8955ffe4731390d912423d1b215fb Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 29 Apr 2016 21:17:06 +0200 Subject: [PATCH 02/25] hashes for segwit signature --- firmware/signing.c | 25 ++++++++++++++++++++++++- firmware/transaction.c | 38 ++++++++++++++++++++++++++++---------- firmware/transaction.h | 5 +++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 3743092309..cc3228e79e 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -50,7 +50,9 @@ 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 SHA256_CTX hashers[2]; +static uint8_t hash_check[32], privkey[32], pubkey[33], sig[64]; +static uint8_t hash_prevouts[32], hash_sequence[32],hash_outputs[32]; static uint64_t to_spend, spending, change_spend; static uint32_t version = 1; static uint32_t lock_time = 0; @@ -273,6 +275,9 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp 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]); layoutProgressSwipe("Signing transaction", 0); @@ -326,6 +331,9 @@ void signing_txack(TransactionType *tx) } else { // InputScriptType_SPENDADDRESS multisig_fp_mismatch = true; } + // compute segwit hashPrevouts & hashSequence + tx_prevout_hash(&hashers[0], tx->inputs); + tx_sequence_hash(&hashers[1], tx->inputs); sha256_Update(&tc, (const uint8_t *)tx->inputs, sizeof(TxInputType)); memcpy(&input, tx->inputs, sizeof(TxInputType)); send_req_2_prev_meta(); @@ -399,6 +407,8 @@ 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 { + /* Check next output */ + 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"); @@ -409,6 +419,13 @@ void signing_txack(TransactionType *tx) 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); + // init hashOutputs + sha256_Init(&hashers[0]); idx1 = 0; send_req_3_output(); } @@ -463,11 +480,15 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } + // compute segwit hashOuts + tx_output_hash(&hashers[0], &bin_output); sha256_Update(&tc, (const uint8_t *)&bin_output, sizeof(TxOutputBinType)); if (idx1 < outputs_count - 1) { idx1++; send_req_3_output(); } else { + sha256_Final(&hashers[0], hash_outputs); + sha256_Raw(hash_sequence, 32, hash_outputs); sha256_Final(&tc, hash_check); // check fees if (spending > to_spend) { @@ -532,6 +553,7 @@ void signing_txack(TransactionType *tx) } tx->inputs[0].script_sig.size = compile_script_multisig(&(tx->inputs[0].multisig), tx->inputs[0].script_sig.bytes); } else { // SPENDADDRESS + uint8_t hash[20]; 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); } @@ -580,6 +602,7 @@ void signing_txack(TransactionType *tx) idx2++; send_req_4_output(); } else { + uint8_t hash[32]; sha256_Final(&tc, hash); if (memcmp(hash, hash_check, 32) != 0) { fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); diff --git a/firmware/transaction.c b/firmware/transaction.c index fd032067a2..45e33cd005 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -286,6 +286,31 @@ 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_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 += ser_length_hash(ctx, output->script_pubkey.size); + sha256_Update(ctx, output->script_pubkey.bytes, output->script_pubkey.size); r+= output->script_pubkey.size; + return r; +} + uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { memcpy(out, &(tx->version), 4); @@ -326,7 +351,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; @@ -335,14 +359,10 @@ 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 += tx_prevout_hash(&(tx->ctx), input); 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_sequence_hash(&(tx->ctx), input); tx->have_inputs++; tx->size += r; @@ -423,9 +443,7 @@ 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) { r += tx_serialize_footer_hash(tx); diff --git a/firmware/transaction.h b/firmware/transaction.h index 71ec1c1137..c03b9fdd84 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -51,6 +51,11 @@ uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, 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_sequence_hash(SHA256_CTX *ctx, const TxInputType *input); +uint32_t tx_output_hash(SHA256_CTX *ctx, const TxOutputBinType *output); + 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); From e5000fb196875b29bd6026eafe074c4968920a76 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 29 Apr 2016 22:15:55 +0200 Subject: [PATCH 03/25] segwit sign (completely untested) --- firmware/protob/types.pb.h | 6 +- firmware/signing.c | 397 +++++++++++++++++++++++++++---------- firmware/transaction.c | 42 +++- firmware/transaction.h | 4 +- 4 files changed, 333 insertions(+), 116 deletions(-) diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index 39ddfe3d70..16d1a6e3a7 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -387,10 +387,10 @@ extern const pb_field_t IdentityType_fields[7]; #define CoinType_size 99 #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 TxRequestDetailsType_size 52 +#define TransactionType_size 10022 +#define TxRequestDetailsType_size 40 #define TxRequestSerializedType_size 2132 #define IdentityType_size 416 diff --git a/firmware/signing.c b/firmware/signing.c index cc3228e79e..23300e511c 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -42,20 +42,23 @@ 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 TxRequest resp; static TxInputType input; static TxOutputBinType bin_output; static TxStruct to, tp, ti; -static SHA256_CTX tc; -static SHA256_CTX hashers[2]; -static uint8_t hash_check[32], privkey[32], pubkey[33], sig[64]; +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 uint64_t to_spend, spending, change_spend; +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]; @@ -226,6 +229,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; @@ -244,6 +269,63 @@ 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(); + } +} + +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) == 0) { + // Failed to derive private key + return false; + } + if (tinput->script_type == InputScriptType_SPENDMULTISIG + || tinput->script_type == InputScriptType_SPENDWMULTISIG) { + 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; @@ -257,6 +339,7 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp to_spend = 0; spending = 0; change_spend = 0; + segwit_to_spend = 0; memset(&input, 0, sizeof(TxInputType)); memset(&resp, 0, sizeof(TxRequest)); @@ -268,16 +351,13 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp 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); @@ -307,36 +387,53 @@ void signing_txack(TransactionType *tx) 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; - } + if ((tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG) + && !multisig_fp_mismatch) { + uint8_t h[32]; + if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), h) == 0) { + fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingerprint"); + signing_abort(); + return; } - } else { // InputScriptType_SPENDADDRESS + 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 { // InputScriptType_SPENDADDRESS or SPENDWADDRESS multisig_fp_mismatch = true; } - // compute segwit hashPrevouts & hashSequence - tx_prevout_hash(&hashers[0], tx->inputs); - tx_sequence_hash(&hashers[1], tx->inputs); - sha256_Update(&tc, (const uint8_t *)tx->inputs, sizeof(TxInputType)); - memcpy(&input, tx->inputs, sizeof(TxInputType)); - send_req_2_prev_meta(); + // compute segwit hashPrevouts & hashSequence + tx_prevout_hash(&hashers[0], &tx->inputs[0]); + tx_sequence_hash(&hashers[1], &tx->inputs[0]); + // hash prevout and script type to check it later (relevant for fee computation) + tx_prevout_hash(&hashers[2], &tx->inputs[0]); + sha256_Update(&hashers[2], &tx->inputs[0].script_type, sizeof(&tx->inputs[0].script_type)); + if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { + if (next_nonsegwit_input == 0xffffffff) + next_nonsegwit_input = idx1; + memcpy(&input, tx->inputs, sizeof(TxInputType)); + send_req_2_prev_meta(); + } else if (tx->inputs[0].has_amount + && (tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS)) { + if (to_spend + tx->inputs[0].amount < to_spend) { + fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); + signing_abort(); + } + to_spend += tx->inputs[0].amount; + segwit_to_spend += tx->inputs[0].amount; + to.is_segwit = true; + phase1_request_next_input(); + } else { + fsm_sendFailure(FailureType_Failure_Other, "Wrong input script type"); + signing_abort(); + } 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); @@ -372,6 +469,10 @@ 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(); + } to_spend += tx->bin_outputs[0].amount; } if (idx2 < tp.outputs_len - 1) { @@ -415,20 +516,7 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } - 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); - // init hashOutputs - sha256_Init(&hashers[0]); - idx1 = 0; - send_req_3_output(); - } + phase1_request_next_input(); } return; case STAGE_REQUEST_3_OUTPUT: @@ -438,24 +526,21 @@ void signing_txack(TransactionType *tx) * 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) { + if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) { uint8_t h[32]; - if (cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingeprint"); + if (!multisig_fp_set || multisig_fp_mismatch + || cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0 + || memcmp(multisig_fp, h, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); 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; + } 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; @@ -466,6 +551,10 @@ void signing_txack(TransactionType *tx) } } + if (spending + tx->inputs[0].amount < spending) { + fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); + signing_abort(); + } spending += tx->outputs[0].amount; co = compile_output(coin, root, tx->outputs, &bin_output, !is_change); if (!is_change) { @@ -482,14 +571,12 @@ void signing_txack(TransactionType *tx) } // compute segwit hashOuts tx_output_hash(&hashers[0], &bin_output); - sha256_Update(&tc, (const uint8_t *)&bin_output, sizeof(TxOutputBinType)); if (idx1 < outputs_count - 1) { idx1++; send_req_3_output(); } else { sha256_Final(&hashers[0], hash_outputs); - sha256_Raw(hash_sequence, 32, hash_outputs); - sha256_Final(&tc, hash_check); + sha256_Raw(hash_outputs, 32, hash_outputs); // check fees if (spending > to_spend) { fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds"); @@ -518,8 +605,7 @@ void signing_txack(TransactionType *tx) progress_meta_step = progress_step / (inputs_count + outputs_count); layoutProgress("Signing transaction", progress); idx1 = 0; - idx2 = 0; - send_req_4_input(); + phase2_request_next_input(); } return; } @@ -527,44 +613,26 @@ void signing_txack(TransactionType *tx) progress = 500 + ((idx1 * 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) == 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 - uint8_t hash[20]; - 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)) { @@ -576,6 +644,14 @@ 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(); } @@ -592,7 +668,8 @@ void signing_txack(TransactionType *tx) 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(); @@ -603,8 +680,9 @@ void signing_txack(TransactionType *tx) send_req_4_output(); } else { uint8_t hash[32]; - sha256_Final(&tc, hash); - if (memcmp(hash, hash_check, 32) != 0) { + 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; @@ -617,6 +695,7 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; ecdsa_sign_digest(&secp256k1, privkey, hash, sig, NULL, NULL); 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"); @@ -647,14 +726,33 @@ void signing_txack(TransactionType *tx) 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: + progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); + + resp.has_serialized = true; + resp.serialized.has_signature_index = false; + resp.serialized.has_signature = false; + resp.serialized.has_serialized_tx = true; + 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"); @@ -667,11 +765,106 @@ 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: + { + uint8_t hash[32]; + uint32_t sighash = 1; + progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); + + if (tx->inputs[0].script_type != InputScriptType_SPENDWADDRESS + && tx->inputs[0].script_type != InputScriptType_SPENDWMULTISIG) { + // empty witness + resp.serialized.serialized_tx.bytes[0] = 0; + resp.serialized.serialized_tx.size = 1; + } else { + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); + signing_abort(); + return; + } + if (tx->inputs[0].amount > segwit_to_spend) { + fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); + signing_abort(); + return; + } + segwit_to_spend -= tx->inputs[0].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], &tx->inputs[0]); + tx_script_hash(&hashers[0], tx->inputs[0].script_sig.size, tx->inputs[0].script_sig.bytes); + sha256_Update(&hashers[0], (const uint8_t*) &tx->inputs[0].amount, 8); + 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); + + 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; + ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, 0); + resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); + if (input.script_type == InputScriptType_SPENDWMULTISIG) { + uint32_t r, i, script_len; + 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; + + r = 0; + r += ser_length(input.multisig.signatures_count + 2, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.bytes[r] = 0; r++; + for (i = 0; i < input.multisig.signatures_count; i++) { + r += tx_serialize_script(input.multisig.signatures[i].size, input.multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); + } + script_len = compile_script_multisig(&input.multisig, 0); + r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); + r += compile_script_multisig(&input.multisig, resp.serialized.serialized_tx.bytes + r); + r += tx_serialize_script(resp.serialized.signature.size, resp.serialized.signature.bytes, resp.serialized.serialized_tx.bytes + r); + resp.serialized.serialized_tx.size = r; + } else { // SPENDWADDRESS + uint32_t r = 0; + r += ser_length(2, resp.serialized.serialized_tx.bytes + r); + r += tx_serialize_script(resp.serialized.signature.size, 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; + } + } + // since this took a longer time, update progress + 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"); diff --git a/firmware/transaction.c b/firmware/transaction.c index 45e33cd005..62be8638a8 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; @@ -296,6 +298,13 @@ uint32_t tx_prevout_hash(SHA256_CTX *ctx, const TxInputType *input) 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); @@ -306,21 +315,37 @@ 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 += ser_length_hash(ctx, output->script_pubkey.size); - sha256_Update(ctx, output->script_pubkey.bytes, output->script_pubkey.size); r+= output->script_pubkey.size; + 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) @@ -339,8 +364,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++; @@ -360,8 +384,7 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) r += tx_serialize_header_hash(tx); } r += tx_prevout_hash(&(tx->ctx), input); - 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; + r += tx_script_hash(&(tx->ctx), input->script_sig.size, input->script_sig.bytes); r += tx_sequence_hash(&(tx->ctx), input); tx->have_inputs++; @@ -419,8 +442,7 @@ 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) { r += tx_serialize_footer(tx, out + r); diff --git a/firmware/transaction.h b/firmware/transaction.h index c03b9fdd84..ef59a052e1 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; @@ -53,8 +53,10 @@ uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uin 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_input(TxStruct *tx, const TxInputType *input, uint8_t *out); uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out); From b7b9891cb49e8a674a9bd6cd024650d17dfb01a0 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sun, 1 May 2016 02:47:15 +0200 Subject: [PATCH 04/25] Signing for Segnet Transaction works see segnet4 txid: aa434a6ef4fcf350e319bacbd725fa7446f797cb3ed0cd0582826a49d3351ffa --- firmware/signing.c | 19 ++++++++++++++++--- firmware/transaction.c | 7 +++++-- firmware/transaction.h | 1 + 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 23300e511c..7262138c87 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -783,6 +783,10 @@ void signing_txack(TransactionType *tx) if (tx->inputs[0].script_type != InputScriptType_SPENDWADDRESS && tx->inputs[0].script_type != InputScriptType_SPENDWMULTISIG) { // 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; } else { @@ -805,10 +809,12 @@ void signing_txack(TransactionType *tx) tx_prevout_hash(&hashers[0], &tx->inputs[0]); tx_script_hash(&hashers[0], tx->inputs[0].script_sig.size, tx->inputs[0].script_sig.bytes); sha256_Update(&hashers[0], (const uint8_t*) &tx->inputs[0].amount, 8); + tx_sequence_hash(&hashers[0], &tx->inputs[0]); 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; @@ -825,7 +831,7 @@ void signing_txack(TransactionType *tx) return; } // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), pubkey); + int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), node.public_key); if (pubkey_idx < 0) { fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); signing_abort(); @@ -838,7 +844,8 @@ void signing_txack(TransactionType *tx) r += ser_length(input.multisig.signatures_count + 2, resp.serialized.serialized_tx.bytes + r); resp.serialized.serialized_tx.bytes[r] = 0; r++; for (i = 0; i < input.multisig.signatures_count; i++) { - r += tx_serialize_script(input.multisig.signatures[i].size, input.multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); + input.multisig.signatures[i].bytes[input.multisig.signatures[i].size] = 1; + r += tx_serialize_script(input.multisig.signatures[i].size + 1, input.multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); } script_len = compile_script_multisig(&input.multisig, 0); r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); @@ -848,11 +855,17 @@ void signing_txack(TransactionType *tx) } else { // SPENDWADDRESS uint32_t r = 0; r += ser_length(2, resp.serialized.serialized_tx.bytes + r); - r += tx_serialize_script(resp.serialized.signature.size, resp.serialized.signature.bytes, 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; } } + 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; + } // since this took a longer time, update progress layoutProgress("Signing transaction", progress); update_ctr = 0; diff --git a/firmware/transaction.c b/firmware/transaction.c index 62be8638a8..9e63ce82d2 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -444,7 +444,8 @@ uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_ memcpy(out + r, &output->amount, 8); r += 8; 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; @@ -467,7 +468,8 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) } 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; @@ -506,6 +508,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 ef59a052e1..e100aeaa35 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -58,6 +58,7 @@ 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); From 388750f2d1e4965fd9404c5ca5a26338d91a132d Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sun, 3 Jul 2016 16:11:56 +0200 Subject: [PATCH 05/25] Support for P2SH compatible segwit --- firmware/signing.c | 59 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 7262138c87..f62340670a 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -421,7 +421,9 @@ void signing_txack(TransactionType *tx) send_req_2_prev_meta(); } else if (tx->inputs[0].has_amount && (tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS)) { + || tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS)) { if (to_spend + tx->inputs[0].amount < to_spend) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); @@ -741,7 +743,35 @@ void signing_txack(TransactionType *tx) resp.serialized.has_signature_index = false; resp.serialized.has_signature = false; resp.serialized.has_serialized_tx = true; - tx->inputs[0].script_sig.size = 0; + if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS) { + 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_SPENDP2SHWMULTISIG) { + // 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 disgest 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) { @@ -780,16 +810,10 @@ void signing_txack(TransactionType *tx) uint32_t sighash = 1; progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); - if (tx->inputs[0].script_type != InputScriptType_SPENDWADDRESS - && tx->inputs[0].script_type != InputScriptType_SPENDWMULTISIG) { - // 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; - } else { + if (tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS + || tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWMULTISIG) { if (!compile_input_script_sig(&tx->inputs[0])) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); signing_abort(); @@ -823,7 +847,8 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, 0); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - if (input.script_type == InputScriptType_SPENDWMULTISIG) { + if (input.script_type == InputScriptType_SPENDWMULTISIG + || input.script_type == InputScriptType_SPENDP2SHWMULTISIG) { uint32_t r, i, script_len; if (!input.has_multisig) { fsm_sendFailure(FailureType_Failure_Other, "Multisig info not provided"); @@ -860,6 +885,14 @@ void signing_txack(TransactionType *tx) 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 (idx1 == inputs_count - 1) { uint32_t r = resp.serialized.serialized_tx.size; From 895da908e06ec74fc098310784706d989a776735 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Mon, 4 Jul 2016 23:04:40 +0200 Subject: [PATCH 06/25] Simplified InputScriptType Distinguish between single signature and multisig via has_multisig. --- firmware/signing.c | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index f62340670a..01488dc4cc 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -315,8 +315,7 @@ bool compile_input_script_sig(TxInputType *tinput) // Failed to derive private key return false; } - if (tinput->script_type == InputScriptType_SPENDMULTISIG - || tinput->script_type == InputScriptType_SPENDWMULTISIG) { + if (tinput->has_multisig) { tinput->script_sig.size = compile_script_multisig(&(tinput->multisig), tinput->script_sig.bytes); } else { // SPENDADDRESS uint8_t hash[20]; @@ -387,9 +386,7 @@ void signing_txack(TransactionType *tx) 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 - || tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG) - && !multisig_fp_mismatch) { + if (tx->inputs[0].has_multisig && !multisig_fp_mismatch) { uint8_t h[32]; if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), h) == 0) { fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingerprint"); @@ -404,7 +401,7 @@ void signing_txack(TransactionType *tx) memcpy(multisig_fp, h, 32); multisig_fp_set = true; } - } else { // InputScriptType_SPENDADDRESS or SPENDWADDRESS + } else { // single signature multisig_fp_mismatch = true; } // compute segwit hashPrevouts & hashSequence @@ -420,10 +417,8 @@ void signing_txack(TransactionType *tx) memcpy(&input, tx->inputs, sizeof(TxInputType)); send_req_2_prev_meta(); } else if (tx->inputs[0].has_amount - && (tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS)) { + && (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS)) { if (to_spend + tx->inputs[0].amount < to_spend) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); @@ -698,12 +693,7 @@ void signing_txack(TransactionType *tx) ecdsa_sign_digest(&secp256k1, privkey, hash, sig, NULL, NULL); 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; - } + if (input.has_multisig) { // fill in the signature int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), pubkey); if (pubkey_idx < 0) { @@ -743,7 +733,8 @@ void signing_txack(TransactionType *tx) resp.serialized.has_signature_index = false; resp.serialized.has_signature = false; resp.serialized.has_serialized_tx = true; - if (tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS) { + 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(); @@ -756,13 +747,13 @@ void signing_txack(TransactionType *tx) 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_SPENDP2SHWMULTISIG) { + } 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 disgest of multisig script + // 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(); @@ -810,10 +801,8 @@ void signing_txack(TransactionType *tx) uint32_t sighash = 1; progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); - if (tx->inputs[0].script_type == InputScriptType_SPENDWADDRESS - || tx->inputs[0].script_type == InputScriptType_SPENDWMULTISIG - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWADDRESS - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWMULTISIG) { + if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { if (!compile_input_script_sig(&tx->inputs[0])) { fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); signing_abort(); @@ -847,14 +836,8 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, 0); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - if (input.script_type == InputScriptType_SPENDWMULTISIG - || input.script_type == InputScriptType_SPENDP2SHWMULTISIG) { + if (input.has_multisig) { uint32_t r, i, script_len; - 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), node.public_key); if (pubkey_idx < 0) { @@ -877,7 +860,7 @@ void signing_txack(TransactionType *tx) r += compile_script_multisig(&input.multisig, resp.serialized.serialized_tx.bytes + r); r += tx_serialize_script(resp.serialized.signature.size, resp.serialized.signature.bytes, resp.serialized.serialized_tx.bytes + r); resp.serialized.serialized_tx.size = r; - } else { // SPENDWADDRESS + } 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; From 2950588271cbb88343ca2bc4fbd570ea46066101 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 8 Jul 2016 00:41:59 +0200 Subject: [PATCH 07/25] Fix segwit multisig. Tested, see f41cbedd8becee05a830f418d13aa665125464547db5c7a6cd28f21639fe1228 and c9348040bbc2024e12dcb4a0b4806b0398646b91acf314da028c3f03dd0179fc on testnet --- firmware/signing.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 01488dc4cc..641a2d01f8 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -386,7 +386,8 @@ void signing_txack(TransactionType *tx) 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].has_multisig && !multisig_fp_mismatch) { + if (tx->inputs[0].has_multisig && !multisig_fp_mismatch + && tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG) { uint8_t h[32]; if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), h) == 0) { fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingerprint"); @@ -836,29 +837,35 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, 0); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - if (input.has_multisig) { + if (tx->inputs[0].has_multisig) { uint32_t r, i, script_len; + int nwitnesses; // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(&(input.multisig), node.public_key); + int pubkey_idx = cryptoMultisigPubkeyIndex(&(tx->inputs[0].multisig), node.public_key); 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; + memcpy(tx->inputs[0].multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); + tx->inputs[0].multisig.signatures[pubkey_idx].size = resp.serialized.signature.size; - r = 0; - r += ser_length(input.multisig.signatures_count + 2, resp.serialized.serialized_tx.bytes + r); + + r = 1; // skip number of items (filled in later) resp.serialized.serialized_tx.bytes[r] = 0; r++; - for (i = 0; i < input.multisig.signatures_count; i++) { - input.multisig.signatures[i].bytes[input.multisig.signatures[i].size] = 1; - r += tx_serialize_script(input.multisig.signatures[i].size + 1, input.multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); + nwitnesses = 2; + for (i = 0; i < tx->inputs[0].multisig.signatures_count; i++) { + if (tx->inputs[0].multisig.signatures[i].size == 0) { + continue; + } + nwitnesses++; + tx->inputs[0].multisig.signatures[i].bytes[tx->inputs[0].multisig.signatures[i].size] = 1; + r += tx_serialize_script(tx->inputs[0].multisig.signatures[i].size + 1, tx->inputs[0].multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); } - script_len = compile_script_multisig(&input.multisig, 0); + script_len = compile_script_multisig(&tx->inputs[0].multisig, 0); r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); - r += compile_script_multisig(&input.multisig, resp.serialized.serialized_tx.bytes + r); - r += tx_serialize_script(resp.serialized.signature.size, resp.serialized.signature.bytes, resp.serialized.serialized_tx.bytes + r); + r += compile_script_multisig(&tx->inputs[0].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; From 52da2fc5e798144ae08cd3aa40d954e5d31f46e7 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Thu, 10 Nov 2016 18:06:00 +0100 Subject: [PATCH 08/25] Segwit: Fix problems introduced by rebase --- firmware/signing.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 641a2d01f8..6fe29cdd22 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -315,6 +315,7 @@ bool compile_input_script_sig(TxInputType *tinput) // 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 @@ -478,6 +479,7 @@ void signing_txack(TransactionType *tx) idx2++; send_req_2_prev_output(); } else { // last output + uint8_t hash[32]; if (tp.extra_data_len > 0) { // has extra data send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); return; @@ -488,13 +490,7 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } - if (idx1 < inputs_count - 1) { - idx1++; - send_req_1_input(); - } else { - idx1 = 0; - send_req_3_output(); - } + phase1_request_next_input(); } return; case STAGE_REQUEST_2_PREV_EXTRADATA: @@ -821,7 +817,7 @@ void signing_txack(TransactionType *tx) sha256_Update(&hashers[0], hash_prevouts, 32); sha256_Update(&hashers[0], hash_sequence, 32); tx_prevout_hash(&hashers[0], &tx->inputs[0]); - tx_script_hash(&hashers[0], tx->inputs[0].script_sig.size, tx->inputs[0].script_sig.bytes); + tx_script_hash(&hashers[0], tx->inputs[0].script_sig.size, tx->inputs[0].script_sig.bytes); sha256_Update(&hashers[0], (const uint8_t*) &tx->inputs[0].amount, 8); tx_sequence_hash(&hashers[0], &tx->inputs[0]); sha256_Update(&hashers[0], hash_outputs, 32); @@ -835,7 +831,7 @@ void signing_txack(TransactionType *tx) resp.serialized.signature_index = idx1; resp.serialized.has_signature = true; resp.serialized.has_serialized_tx = true; - ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, 0); + ecdsa_sign_digest(&secp256k1, node.private_key, hash, sig, NULL, NULL); resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); if (tx->inputs[0].has_multisig) { uint32_t r, i, script_len; From e67f13ef4bcb0c9bede8f94cede70654098c5051 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 28 Dec 2016 19:16:50 +0100 Subject: [PATCH 09/25] Multi-byte address prefixes for segwit --- firmware/transaction.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/firmware/transaction.c b/firmware/transaction.c index 9e63ce82d2..3cbdd1c3a5 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -63,7 +63,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - int addr_raw_len; + size_t addr_raw_len; if (in->has_address) { // address provided -> regular output addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE); @@ -76,36 +76,37 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T } } - if (addr_raw_len == 21 && - addr_raw[0] == coin->address_type) { // p2pkh + 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 + 1, 20); + 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 (addr_raw_len == 21 - && addr_raw[0] == coin->address_type_p2sh) { // p2sh + } 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 + 1, 20); + 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 (addr_raw_len == 23 - && addr_raw[0] == coin->address_type_p2wpkh - && addr_raw[1] == 0 && addr_raw[2] == 0) { // p2wpkh v0 + } 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 + 3, 20); + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 20); out->script_pubkey.size = 22; - } else if (addr_raw_len == 35 - && addr_raw[0] == coin->address_type_p2wsh - && addr_raw[1] == 0 && addr_raw[2] == 0) { // p2wsh v0 + } 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 + 3, 32); + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len + 2, 32); out->script_pubkey.size = 34; } else { return 0; From f9a203431ec3b4d3e16b246769da889ac8a38fa8 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Mon, 2 Jan 2017 21:22:59 +0100 Subject: [PATCH 10/25] Display SegWit address on Trezor --- firmware/fsm.c | 44 ++++++++++++++++++++++++++++++++ firmware/layout2.c | 15 +++++++++++ firmware/layout2.h | 1 + firmware/protob/messages.options | 2 +- 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index ca3c997cdc..4740c93d92 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" @@ -608,6 +610,7 @@ void fsm_msgGetAddress(GetAddress *msg) HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; hdnode_fill_public_key(node); + int is_segwit = 0; if (msg->has_multisig) { layoutProgressSwipe("Preparing", 0); @@ -625,11 +628,52 @@ void fsm_msgGetAddress(GetAddress *msg) ripemd160(buf, 32, buf + 1); buf[0] = coin->address_type_p2sh; // multisig cointype base58_encode_check(buf, 21, resp->address, sizeof(resp->address)); + } else if (msg->has_script_type + && msg->script_type == InputScriptType_SPENDWITNESS + && coin->has_address_type_p2wpkh) { + uint8_t raw[22+4]; + int 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, resp->address, sizeof(resp->address))) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); + layoutHome(); + return; + } + is_segwit = 1; + } else if (msg->has_script_type + && msg->script_type == InputScriptType_SPENDP2SHWITNESS + && coin->has_address_type_p2sh) { + uint8_t script[22]; + uint8_t digest[32]; + int prelen = address_prefix_bytes_len(coin->address_type_p2sh); + script[0] = 0; // version byte + script[1] = 20; // push 20 bytes + ecdsa_get_pubkeyhash(node->public_key, script + 2); + sha256_Raw(script, 22, digest); + ripemd160(digest, 32, digest + prelen); + address_write_prefix_bytes(coin->address_type_p2sh, digest); + if (!base58_encode_check(digest, 21, resp->address, sizeof(resp->address))) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); + layoutHome(); + return; + } + is_segwit = 1; } else { ecdsa_get_address(node->public_key, coin->address_type, resp->address, sizeof(resp->address)); } if (msg->has_show_display && msg->show_display) { + if (is_segwit) { + layoutSegwitWarning(); + if (!protectButton(ButtonRequestType_ButtonRequest_Address, true)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show address cancelled"); + layoutHome(); + return; + } + } char desc[16]; if (msg->has_multisig) { strlcpy(desc, "Msig __ of __:", sizeof(desc)); diff --git a/firmware/layout2.c b/firmware/layout2.c index e7d49262ab..6a7612062b 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -391,3 +391,18 @@ void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appico } layoutDialog(appicon, NULL, verb, NULL, verb, "U2F security key?", NULL, appname, NULL, NULL); } + +void layoutSegwitWarning() +{ + layoutDialogSwipe(&bmp_icon_info, + "Cancel", + "Understood", + NULL, + "The following address", + "is for SegWit soft fork.", + NULL, + "It is unsafe to use", + "until segwit activates.", + NULL + ); +} diff --git a/firmware/layout2.h b/firmware/layout2.h index 458fa55fda..95633fa722 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -45,5 +45,6 @@ void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); void layoutDecryptIdentity(const IdentityType *identity); void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon); +void layoutSegwitWarning(void); #endif diff --git a/firmware/protob/messages.options b/firmware/protob/messages.options index 9164ce6dba..66c8456bd4 100644 --- a/firmware/protob/messages.options +++ b/firmware/protob/messages.options @@ -32,7 +32,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 From e9eaad2fcfddd2dac4821caee1d453b5116811d5 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Fri, 6 Jan 2017 16:18:28 +0100 Subject: [PATCH 11/25] Segwit: Show multisig segwit address changed layout for very large addresses. --- firmware/fsm.c | 60 +++++++++++++++++++++++++---------- firmware/layout2.c | 24 +++++++++++--- firmware/protob/messages.pb.h | 4 +-- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 4740c93d92..4a97c39b33 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -611,6 +611,9 @@ void fsm_msgGetAddress(GetAddress *msg) if (!node) return; hdnode_fill_public_key(node); int is_segwit = 0; + uint8_t digest[32]; + uint8_t raw[32+2+4]; + size_t prelen; if (msg->has_multisig) { layoutProgressSwipe("Preparing", 0); @@ -619,20 +622,45 @@ void fsm_msgGetAddress(GetAddress *msg) layoutHome(); return; } - uint8_t buf[32]; - if (compile_script_multisig_hash(&(msg->multisig), buf) == 0) { + if (compile_script_multisig_hash(&(msg->multisig), digest) == 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)); + if (msg->has_script_type + && (msg->script_type == InputScriptType_SPENDWITNESS + || msg->script_type == InputScriptType_SPENDP2SHWITNESS)) { + is_segwit = 1; + // segwit p2wsh: script hash is single sha256 + if (msg->script_type == InputScriptType_SPENDWITNESS) { + 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); + base58_encode_check(raw, prelen + 34, resp->address, sizeof(resp->address)); + } else { + // segwit p2wsh encapsuled in p2sh address + 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_p2wsh); + address_write_prefix_bytes(coin->address_type_p2sh, raw); + ripemd160(digest, 32, raw + prelen); + base58_encode_check(raw, prelen + 20, resp->address, sizeof(resp->address)); + } + } 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); + base58_encode_check(raw, prelen + 20, resp->address, sizeof(resp->address)); + } } else if (msg->has_script_type && msg->script_type == InputScriptType_SPENDWITNESS && coin->has_address_type_p2wpkh) { - uint8_t raw[22+4]; - int prelen = address_prefix_bytes_len(coin->address_type_p2wpkh); + 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 @@ -646,16 +674,14 @@ void fsm_msgGetAddress(GetAddress *msg) } else if (msg->has_script_type && msg->script_type == InputScriptType_SPENDP2SHWITNESS && coin->has_address_type_p2sh) { - uint8_t script[22]; - uint8_t digest[32]; - int prelen = address_prefix_bytes_len(coin->address_type_p2sh); - script[0] = 0; // version byte - script[1] = 20; // push 20 bytes - ecdsa_get_pubkeyhash(node->public_key, script + 2); - sha256_Raw(script, 22, digest); - ripemd160(digest, 32, digest + prelen); - address_write_prefix_bytes(coin->address_type_p2sh, digest); - if (!base58_encode_check(digest, 21, resp->address, sizeof(resp->address))) { + 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, 21, resp->address, sizeof(resp->address))) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); layoutHome(); return; diff --git a/firmware/layout2.c b/firmware/layout2.c index 6a7612062b..5862a95ade 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -245,6 +245,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); @@ -259,6 +260,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); @@ -269,12 +284,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.pb.h b/firmware/protob/messages.pb.h index 7123f4495b..2b19e7edaf 100644 --- a/firmware/protob/messages.pb.h +++ b/firmware/protob/messages.pb.h @@ -130,7 +130,7 @@ typedef struct _WordRequest { } WordRequest; typedef struct _Address { - char address[41]; + char address[60]; } Address; typedef struct { @@ -1242,7 +1242,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) From 7b1381766f3c1e2f9b5d9d1935f032219f9d3431 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 29 Mar 2017 16:43:48 +0200 Subject: [PATCH 12/25] Change address support for segwit. Rewrote change address support for segwit. Also checks the bip32 path of change address. --- firmware/signing.c | 70 +++++++++-- firmware/transaction.c | 267 ++++++++++++++++++++++++++--------------- firmware/transaction.h | 1 + 3 files changed, 228 insertions(+), 110 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index e8eebad2b5..f398aaa52a 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -62,6 +62,9 @@ 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 per input in permille with these many additional bits. @@ -298,6 +301,40 @@ void phase2_request_next_input(void) } } +void set_input_bip32_path(const TxInputType *tinput) +{ + size_t count = tinput->address_n_count; + if (count < 2) { + // no change address allowed + in_address_n_count = (size_t) -1; + } else if (in_address_n_count == 0) { + // initialize in_address_n on first input seen + in_address_n_count = count; + memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)); + } else if (in_address_n_count != count + || 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; + } +} + +bool check_change_bip32_path(const TxOutputType *toutput) +{ + size_t count = toutput->address_n_count; + // check that the last two components specify a sane address on the change chain + if (count < 2 + || toutput->address_n[count-2] != 1 + || toutput->address_n[count-1] > 1000000) + return 0; + + // check that the other components exactly match input. + if (in_address_n_count != count + || memcmp(in_address_n, toutput->address_n, (count-2) * sizeof(uint32_t)) != 0) + return 0; + + return 1; +} + bool compile_input_script_sig(TxInputType *tinput) { if (!multisig_fp_mismatch) { @@ -349,6 +386,7 @@ 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; @@ -406,6 +444,7 @@ void signing_txack(TransactionType *tx) } else { // single signature multisig_fp_mismatch = true; } + set_input_bip32_path(&tx->inputs[0]); // compute segwit hashPrevouts & hashSequence tx_prevout_hash(&hashers[0], &tx->inputs[0]); tx_sequence_hash(&hashers[1], &tx->inputs[0]); @@ -520,19 +559,28 @@ void signing_txack(TransactionType *tx) * Ask for permission. */ bool is_change = false; - if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) { - uint8_t h[32]; - if (!multisig_fp_set || multisig_fp_mismatch - || cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0 - || memcmp(multisig_fp, h, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); + if (tx->outputs[0].address_n_count > 0) { + if (tx->outputs[0].has_address) { + fsm_sendFailure(FailureType_Failure_Other, "Address in change output"); signing_abort(); return; } - is_change = true; - } else if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS && - tx->outputs[0].address_n_count > 0) { - is_change = true; + if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) { + uint8_t h[32]; + if (!multisig_fp_set || multisig_fp_mismatch + || cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0 + || memcmp(multisig_fp, h, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); + signing_abort(); + return; + } + is_change = check_change_bip32_path(&tx->outputs[0]); + } else if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS + || ((tx->outputs[0].script_type == OutputScriptType_PAYTOWITNESS + || tx->outputs[0].script_type == OutputScriptType_PAYTOP2SHWITNESS) + && tx->outputs[0].amount < segwit_to_spend)) { + is_change = check_change_bip32_path(&tx->outputs[0]); + } } if (is_change) { @@ -545,7 +593,7 @@ void signing_txack(TransactionType *tx) } } - if (spending + tx->inputs[0].amount < spending) { + if (spending + tx->outputs[0].amount < spending) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); } diff --git a/firmware/transaction.c b/firmware/transaction.c index d19899f63e..a844c16505 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -58,132 +58,201 @@ 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], + bool *is_segwit) { + + 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 + *is_segwit = 1; + 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 + *is_segwit = 1; + 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 + *is_segwit = 0; + 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 + *is_segwit = 1; + 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 + *is_segwit = 1; + 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 { + *is_segwit = 0; + 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]; size_t addr_raw_len; + bool is_segwit; - if (in->has_address) { // address provided -> regular output - addr_raw_len = base58_decode_check(in->address, addr_raw, MAX_ADDR_RAW_SIZE); - if (in->script_type != OutputScriptType_PAYTOADDRESS) { - // allow for p2sh (backward compatibility only) - if (in->script_type != OutputScriptType_PAYTOSCRIPTHASH - || addr_raw_len != 21 - || addr_raw[0] != coin->address_type_p2sh) { - return 0; - } + if (in->address_n_count > 0) { + HDNode node; + InputScriptType input_script_type; + + switch (in->script_type) { + + case OutputScriptType_PAYTOOPRETURN: + // 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); + memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size; + out->script_pubkey.size = r; + return r; + + 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 } - - 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; + 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 } - - if (needs_confirm) { - layoutConfirmOutput(coin, in); - if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { - return -1; - } + hdnode_fill_public_key(&node); + if (!compute_address(coin, input_script_type, &node, + in->has_multisig, &in->multisig, + in->address, &is_segwit)) { + return 0; // failed to compile output } - - return out->script_pubkey.size; + } else if (!in->has_address) { + return 0; // failed to compile output } - if (in->script_type == OutputScriptType_PAYTOADDRESS) { - // address_n provided-> change address -> calculate from address_n - if (in->script_type == OutputScriptType_PAYTOADDRESS && - 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 { // does not have address_n neither address -> error - return 0; - } + 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 + address_prefix_bytes_len(coin->address_type), 20); + 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; - return 25; - } - - 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; - } - } + } 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_bytes, 20); + memcpy(out->script_pubkey.bytes + 2, addr_raw + prefix_len, 20); out->script_pubkey.bytes[22] = 0x87; // OP_EQUAL out->script_pubkey.size = 23; - return 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 (in->script_type == OutputScriptType_PAYTOOPRETURN) { - if (in->amount != 0) return 0; // only 0 satoshi allowed for OP_RETURN - 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); - memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size; - out->script_pubkey.size = r; - return r; + if (needs_confirm) { + layoutConfirmOutput(coin, in); + if (!protectButton(ButtonRequestType_ButtonRequest_ConfirmOutput, false)) { + return -1; // user aborted + } } - return 0; + return out->script_pubkey.size; } uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, uint8_t *out) diff --git a/firmware/transaction.h b/firmware/transaction.h index e100aeaa35..808ccbca2e 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -45,6 +45,7 @@ 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], bool *is_segwit); 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); From 99fc6d31d138a59e042c9a1d099e04dc14954afa Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Wed, 29 Mar 2017 16:53:45 +0200 Subject: [PATCH 13/25] [refactor] Use compute_address in msgGetAddress. --- firmware/fsm.c | 81 ++------------------------------------------------ 1 file changed, 3 insertions(+), 78 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index f04afc6e94..4df6911236 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -584,85 +584,10 @@ void fsm_msgGetAddress(GetAddress *msg) HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; hdnode_fill_public_key(node); - int is_segwit = 0; - uint8_t digest[32]; - uint8_t raw[32+2+4]; - size_t prelen; + bool is_segwit = 0; - 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; - } - if (compile_script_multisig_hash(&(msg->multisig), digest) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig script"); - layoutHome(); - return; - } - if (msg->has_script_type - && (msg->script_type == InputScriptType_SPENDWITNESS - || msg->script_type == InputScriptType_SPENDP2SHWITNESS)) { - is_segwit = 1; - // segwit p2wsh: script hash is single sha256 - if (msg->script_type == InputScriptType_SPENDWITNESS) { - 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); - base58_encode_check(raw, prelen + 34, resp->address, sizeof(resp->address)); - } else { - // segwit p2wsh encapsuled in p2sh address - 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_p2wsh); - address_write_prefix_bytes(coin->address_type_p2sh, raw); - ripemd160(digest, 32, raw + prelen); - base58_encode_check(raw, prelen + 20, resp->address, sizeof(resp->address)); - } - } 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); - base58_encode_check(raw, prelen + 20, resp->address, sizeof(resp->address)); - } - } else if (msg->has_script_type - && msg->script_type == InputScriptType_SPENDWITNESS - && coin->has_address_type_p2wpkh) { - 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, resp->address, sizeof(resp->address))) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); - layoutHome(); - return; - } - is_segwit = 1; - } else if (msg->has_script_type - && msg->script_type == InputScriptType_SPENDP2SHWITNESS - && coin->has_address_type_p2sh) { - 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, 21, resp->address, sizeof(resp->address))) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); - layoutHome(); - return; - } - is_segwit = 1; - } else { - ecdsa_get_address(node->public_key, coin->address_type, resp->address, sizeof(resp->address)); + if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, resp->address, &is_segwit)) { + fsm_sendFailure(FailureType_Failure_ActionCancelled, "Can't encode address"); } if (msg->has_show_display && msg->show_display) { From e5c9b361d3e7d77c2135ff931d8e58809421cfe3 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sat, 8 Apr 2017 20:29:58 +0200 Subject: [PATCH 14/25] Better error message for segwit without amount --- firmware/signing.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index f398aaa52a..4a5dd9f3ac 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -444,6 +444,8 @@ void signing_txack(TransactionType *tx) } else { // single signature multisig_fp_mismatch = true; } + // remember the input bip32 path + // change addresses must use the same bip32 path as all inputs set_input_bip32_path(&tx->inputs[0]); // compute segwit hashPrevouts & hashSequence tx_prevout_hash(&hashers[0], &tx->inputs[0]); @@ -457,9 +459,12 @@ void signing_txack(TransactionType *tx) next_nonsegwit_input = idx1; memcpy(&input, tx->inputs, sizeof(TxInputType)); send_req_2_prev_meta(); - } else if (tx->inputs[0].has_amount - && (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS)) { + } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS + || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { + if (!tx->inputs[0].has_amount) { + fsm_sendFailure(FailureType_Failure_Other, "Segwit input without amount"); + signing_abort(); + } if (to_spend + tx->inputs[0].amount < to_spend) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); From 9d9377438c58e90b1951c62aff83df44cef72de8 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sat, 8 Apr 2017 20:32:14 +0200 Subject: [PATCH 15/25] Fix missing returns after signing_abort --- firmware/signing.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/signing.c b/firmware/signing.c index 4a5dd9f3ac..3359a42a0e 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -464,10 +464,12 @@ void signing_txack(TransactionType *tx) 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; } to_spend += tx->inputs[0].amount; segwit_to_spend += tx->inputs[0].amount; @@ -476,6 +478,7 @@ void signing_txack(TransactionType *tx) } else { fsm_sendFailure(FailureType_Failure_Other, "Wrong input script type"); signing_abort(); + return; } return; case STAGE_REQUEST_2_PREV_META: @@ -515,6 +518,7 @@ void signing_txack(TransactionType *tx) 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; } @@ -601,6 +605,7 @@ void signing_txack(TransactionType *tx) if (spending + tx->outputs[0].amount < spending) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); + return; } spending += tx->outputs[0].amount; co = compile_output(coin, root, tx->outputs, &bin_output, !is_change); From 420471889d173ffa0b67643e33195fb9286b1207 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sat, 8 Apr 2017 21:46:43 +0200 Subject: [PATCH 16/25] Refactored signing method. Put larger pieces of codes into functions of their own. No changes to this code. --- firmware/signing.c | 661 ++++++++++++++++++++++++--------------------- 1 file changed, 356 insertions(+), 305 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 3359a42a0e..91c4bfd548 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -66,7 +66,7 @@ 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 @@ -86,8 +86,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 @@ -109,27 +110,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) @@ -404,6 +421,309 @@ 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 + set_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) == 0 + || memcmp(multisig_fp, h, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); + signing_abort(); + return false; + } + 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"); + layoutHome(); + 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."); + layoutHome(); + 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; +} + void signing_txack(TransactionType *tx) { if (!signing) { @@ -423,38 +743,11 @@ void signing_txack(TransactionType *tx) 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].has_multisig && !multisig_fp_mismatch - && tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG) { - uint8_t h[32]; - if (cryptoMultisigFingerprint(&(tx->inputs[0].multisig), h) == 0) { - fsm_sendFailure(FailureType_Failure_Other, "Error computing multisig fingerprint"); - signing_abort(); - return; - } - 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 - set_input_bip32_path(&tx->inputs[0]); - // compute segwit hashPrevouts & hashSequence - tx_prevout_hash(&hashers[0], &tx->inputs[0]); - tx_sequence_hash(&hashers[1], &tx->inputs[0]); - // hash prevout and script type to check it later (relevant for fee computation) - tx_prevout_hash(&hashers[2], &tx->inputs[0]); - sha256_Update(&hashers[2], &tx->inputs[0].script_type, sizeof(&tx->inputs[0].script_type)); + 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)); @@ -526,19 +819,12 @@ void signing_txack(TransactionType *tx) /* Check prevtx of next input */ idx2++; send_req_2_prev_output(); - } else { // last output - uint8_t hash[32]; - 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; - } - phase1_request_next_input(); + } 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: @@ -550,117 +836,15 @@ 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 { - /* Check next output */ - 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; - } - phase1_request_next_input(); + 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].address_n_count > 0) { - if (tx->outputs[0].has_address) { - fsm_sendFailure(FailureType_Failure_Other, "Address in change output"); - signing_abort(); - return; - } - if (tx->outputs[0].script_type == OutputScriptType_PAYTOMULTISIG) { - uint8_t h[32]; - if (!multisig_fp_set || multisig_fp_mismatch - || cryptoMultisigFingerprint(&(tx->outputs[0].multisig), h) == 0 - || memcmp(multisig_fp, h, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); - signing_abort(); - return; - } - is_change = check_change_bip32_path(&tx->outputs[0]); - } else if (tx->outputs[0].script_type == OutputScriptType_PAYTOADDRESS - || ((tx->outputs[0].script_type == OutputScriptType_PAYTOWITNESS - || tx->outputs[0].script_type == OutputScriptType_PAYTOP2SHWITNESS) - && tx->outputs[0].amount < segwit_to_spend)) { - is_change = check_change_bip32_path(&tx->outputs[0]); - } - } - - 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; - } - } - - if (spending + tx->outputs[0].amount < spending) { - fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); - signing_abort(); + if (!signing_check_output(&tx->outputs[0])) { 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(); - return; - } - // compute segwit hashOuts - tx_output_hash(&hashers[0], &bin_output); - if (idx1 < outputs_count - 1) { - idx1++; - send_req_3_output(); - } else { - sha256_Final(&hashers[0], hash_outputs); - sha256_Raw(hash_outputs, 32, hash_outputs); - // 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; - phase2_request_next_input(); - } + phase1_request_next_output(); return; - } case STAGE_REQUEST_4_INPUT: progress = 500 + ((idx1 * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); if (idx2 == 0) { @@ -731,47 +915,9 @@ void signing_txack(TransactionType *tx) idx2++; send_req_4_output(); } else { - 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(); + 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.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; - } - 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 layoutProgress("Signing transaction", progress); update_ctr = 0; @@ -855,104 +1001,10 @@ void signing_txack(TransactionType *tx) return; case STAGE_REQUEST_SEGWIT_WITNESS: - { - uint8_t hash[32]; - uint32_t sighash = 1; + if (!signing_sign_segwit_input(&tx->inputs[0])) { + return; + } progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); - - if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS - || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { - if (!compile_input_script_sig(&tx->inputs[0])) { - fsm_sendFailure(FailureType_Failure_Other, "Failed to compile input"); - signing_abort(); - return; - } - if (tx->inputs[0].amount > segwit_to_spend) { - fsm_sendFailure(FailureType_Failure_Other, "Transaction has changed during signing"); - signing_abort(); - return; - } - segwit_to_spend -= tx->inputs[0].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], &tx->inputs[0]); - tx_script_hash(&hashers[0], tx->inputs[0].script_sig.size, tx->inputs[0].script_sig.bytes); - sha256_Update(&hashers[0], (const uint8_t*) &tx->inputs[0].amount, 8); - tx_sequence_hash(&hashers[0], &tx->inputs[0]); - 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; - } - - resp.serialized.signature.size = ecdsa_sig_to_der(sig, resp.serialized.signature.bytes); - if (tx->inputs[0].has_multisig) { - uint32_t r, i, script_len; - int nwitnesses; - // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(&(tx->inputs[0].multisig), node.public_key); - if (pubkey_idx < 0) { - fsm_sendFailure(FailureType_Failure_Other, "Pubkey not found in multisig script"); - signing_abort(); - return; - } - memcpy(tx->inputs[0].multisig.signatures[pubkey_idx].bytes, resp.serialized.signature.bytes, resp.serialized.signature.size); - tx->inputs[0].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 < tx->inputs[0].multisig.signatures_count; i++) { - if (tx->inputs[0].multisig.signatures[i].size == 0) { - continue; - } - nwitnesses++; - tx->inputs[0].multisig.signatures[i].bytes[tx->inputs[0].multisig.signatures[i].size] = 1; - r += tx_serialize_script(tx->inputs[0].multisig.signatures[i].size + 1, tx->inputs[0].multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); - } - script_len = compile_script_multisig(&tx->inputs[0].multisig, 0); - r += ser_length(script_len, resp.serialized.serialized_tx.bytes + r); - r += compile_script_multisig(&tx->inputs[0].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 (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; - } - // since this took a longer time, update progress layoutProgress("Signing transaction", progress); update_ctr = 0; if (idx1 < inputs_count - 1) { @@ -963,9 +1015,8 @@ void signing_txack(TransactionType *tx) signing_abort(); } return; - } } - + fsm_sendFailure(FailureType_Failure_Other, "Signing error"); signing_abort(); } From d70ac623a46e0177d8f2ce7e124c4b4d12e01e58 Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sat, 8 Apr 2017 22:36:32 +0200 Subject: [PATCH 17/25] Small bugfixes in signing Segwit progress bar fixed. Call `signing_abort` instead of `layoutHome` on all errors The second `compile_output` does not work for user button and cannot return -1. --- firmware/signing.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 91c4bfd548..d4921e03e3 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -47,6 +47,7 @@ enum { 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; @@ -389,6 +390,7 @@ 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; @@ -512,7 +514,7 @@ static bool signing_check_output(TxOutputType *txoutput) { if (spending + txoutput->amount < spending) { fsm_sendFailure(FailureType_Failure_Other, "Value overflow"); signing_abort(); - return false; + return false; } spending += txoutput->amount; int co = compile_output(coin, root, txoutput, &bin_output, !is_change); @@ -537,7 +539,7 @@ static bool signing_check_fee(void) { // check fees if (spending > to_spend) { fsm_sendFailure(FailureType_Failure_NotEnoughFunds, "Not enough funds"); - layoutHome(); + signing_abort(); return false; } uint64_t fee = to_spend - spending; @@ -546,7 +548,7 @@ static bool signing_check_fee(void) { layoutFeeOverThreshold(coin, fee, tx_est_size); if (!protectButton(ButtonRequestType_ButtonRequest_FeeOverThreshold, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Fee over threshold. Signing cancelled."); - layoutHome(); + signing_abort(); return false; } layoutProgress("Signing transaction", progress); @@ -738,7 +740,6 @@ void signing_txack(TransactionType *tx) update_ctr = 0; } - int co; memset(&resp, 0, sizeof(TxRequest)); switch (signing_stage) { @@ -846,7 +847,7 @@ void signing_txack(TransactionType *tx) 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(&hashers[0]); @@ -893,13 +894,8 @@ void signing_txack(TransactionType *tx) } 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; @@ -919,6 +915,8 @@ void signing_txack(TransactionType *tx) return; } // 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) { @@ -932,8 +930,6 @@ void signing_txack(TransactionType *tx) return; case STAGE_REQUEST_SEGWIT_INPUT: - progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); - resp.has_serialized = true; resp.serialized.has_signature_index = false; resp.serialized.has_signature = false; @@ -1004,7 +1000,8 @@ void signing_txack(TransactionType *tx) if (!signing_sign_segwit_input(&tx->inputs[0])) { return; } - progress = 500 + ((idx1 * progress_step) >> PROGRESS_PRECISION); + signatures++; + progress = 500 + ((signatures * progress_step) >> PROGRESS_PRECISION); layoutProgress("Signing transaction", progress); update_ctr = 0; if (idx1 < inputs_count - 1) { From dcceec806dc0fe2a2259984dc57508fc14a0f4ca Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 11 Apr 2017 14:05:19 +0200 Subject: [PATCH 18/25] fix call to protectButton while showing segwit warning --- firmware/fsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 4df6911236..57f35db8e8 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -593,7 +593,7 @@ void fsm_msgGetAddress(GetAddress *msg) if (msg->has_show_display && msg->show_display) { if (is_segwit) { layoutSegwitWarning(); - if (!protectButton(ButtonRequestType_ButtonRequest_Address, true)) { + if (!protectButton(ButtonRequestType_ButtonRequest_Address, false)) { fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show address cancelled"); layoutHome(); return; From 38970cbd68aee688307c8922c1097a5a35324a8d Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 3 May 2017 18:57:47 +0200 Subject: [PATCH 19/25] transaction: fix compile_output for OP_RETURN --- firmware/transaction.c | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/firmware/transaction.c b/firmware/transaction.c index a844c16505..691f1ca056 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -161,38 +161,38 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T size_t addr_raw_len; bool is_segwit; + if (in->script_type == OutputScriptType_PAYTOOPRETURN) { + // 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); + memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size; + out->script_pubkey.size = r; + return r; + } + if (in->address_n_count > 0) { HDNode node; InputScriptType input_script_type; switch (in->script_type) { - - case OutputScriptType_PAYTOOPRETURN: - // only 0 satoshi allowed for OP_RETURN - if (in->amount != 0) + 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 - 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); - memcpy(out->script_pubkey.bytes + r, in->op_return_data.bytes, in->op_return_data.size); r += in->op_return_data.size; - out->script_pubkey.size = r; - return r; - - 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) { From 4183b6cbbc73cbf70e600df5563c3dc02510c6a9 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 4 May 2017 18:15:46 +0200 Subject: [PATCH 20/25] update logic for change addresses (address_n length 1 is allowed now) --- firmware/signing.c | 48 ++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index d4921e03e3..500595da91 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -322,35 +322,51 @@ void phase2_request_next_input(void) void set_input_bip32_path(const TxInputType *tinput) { size_t count = tinput->address_n_count; - if (count < 2) { + if (count < 1) { // no change address allowed in_address_n_count = (size_t) -1; - } else if (in_address_n_count == 0) { + return; + } + if (in_address_n_count == 0) { // initialize in_address_n on first input seen in_address_n_count = count; - memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)); - } else if (in_address_n_count != count - || 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; + if (count > 2) { // if longer than 2 elements, store first N - 2 + memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)); + } + } else { + // 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; + } } } bool check_change_bip32_path(const TxOutputType *toutput) { size_t count = toutput->address_n_count; - // check that the last two components specify a sane address on the change chain - if (count < 2 - || toutput->address_n[count-2] != 1 - || toutput->address_n[count-1] > 1000000) - return 0; - // check that the other components exactly match input. - if (in_address_n_count != count - || memcmp(in_address_n, toutput->address_n, (count-2) * sizeof(uint32_t)) != 0) + if (count < 1) { return 0; + } - return 1; + if (count == 1) { + return in_address_n_count == 1 && toutput->address_n[0] < 1000000; + } + + if (count >= 2) { + return in_address_n_count == count && + 0 == memcmp(in_address_n, toutput->address_n, (count - 2) * sizeof(uint32_t)) && + toutput->address_n[count - 2] == 1 && + toutput->address_n[count - 1] < 1000000; + } + + return 0; } bool compile_input_script_sig(TxInputType *tinput) From b1995bb8d1aad983770aae8a13106ed5cadbcf71 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Thu, 4 May 2017 18:54:54 +0200 Subject: [PATCH 21/25] remove segwit warning, don't enable segwit on coins that don't have segwit=true in definition --- firmware/fsm.c | 11 +---------- firmware/layout2.c | 15 --------------- firmware/layout2.h | 1 - firmware/signing.c | 9 +++++++-- firmware/transaction.c | 24 ++++++++++++++---------- firmware/transaction.h | 2 +- 6 files changed, 23 insertions(+), 39 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 527f7756c4..1c4b86df83 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -580,21 +580,12 @@ void fsm_msgGetAddress(GetAddress *msg) HDNode *node = fsm_getDerivedNode(SECP256K1_NAME, msg->address_n, msg->address_n_count); if (!node) return; hdnode_fill_public_key(node); - bool is_segwit = 0; - if (!compute_address(coin, msg->script_type, node, msg->has_multisig, &msg->multisig, resp->address, &is_segwit)) { + 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) { - if (is_segwit) { - layoutSegwitWarning(); - if (!protectButton(ButtonRequestType_ButtonRequest_Address, false)) { - fsm_sendFailure(FailureType_Failure_ActionCancelled, "Show address cancelled"); - layoutHome(); - return; - } - } char desc[16]; if (msg->has_multisig) { strlcpy(desc, "Msig __ of __:", sizeof(desc)); diff --git a/firmware/layout2.c b/firmware/layout2.c index bb6516a9ec..0a8e665231 100644 --- a/firmware/layout2.c +++ b/firmware/layout2.c @@ -426,18 +426,3 @@ void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appico } layoutDialog(appicon, NULL, verb, NULL, verb, "U2F security key?", NULL, appname, NULL, NULL); } - -void layoutSegwitWarning() -{ - layoutDialogSwipe(&bmp_icon_info, - "Cancel", - "Understood", - NULL, - "The following address", - "is for SegWit soft fork.", - NULL, - "It is unsafe to use", - "until segwit activates.", - NULL - ); -} diff --git a/firmware/layout2.h b/firmware/layout2.h index 95633fa722..458fa55fda 100644 --- a/firmware/layout2.h +++ b/firmware/layout2.h @@ -45,6 +45,5 @@ void layoutPublicKey(const uint8_t *pubkey); void layoutSignIdentity(const IdentityType *identity, const char *challenge); void layoutDecryptIdentity(const IdentityType *identity); void layoutU2FDialog(const char *verb, const char *appname, const BITMAP *appicon); -void layoutSegwitWarning(void); #endif diff --git a/firmware/signing.c b/firmware/signing.c index 500595da91..4e5c197574 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -113,9 +113,9 @@ Phase2: sign inputs, check that nothing changed foreach I (idx1): // input to sign if (idx1 is segwit) Request I STAGE_REQUEST_SEGWIT_INPUT - Return serialized input chunk + Return serialized input chunk - else + else foreach I (idx2): Request I STAGE_REQUEST_4_INPUT If idx1 == idx2 @@ -771,6 +771,11 @@ void signing_txack(TransactionType *tx) 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(); diff --git a/firmware/transaction.c b/firmware/transaction.c index 691f1ca056..5956f12c07 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -62,8 +62,7 @@ bool compute_address(const CoinType *coin, InputScriptType script_type, const HDNode *node, bool has_multisig, const MultisigRedeemScriptType *multisig, - char address[MAX_ADDR_SIZE], - bool *is_segwit) { + char address[MAX_ADDR_SIZE]) { uint8_t raw[32]; uint8_t digest[MAX_ADDR_RAW_SIZE]; @@ -78,7 +77,9 @@ bool compute_address(const CoinType *coin, } if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wsh: script hash is single sha256 - *is_segwit = 1; + if (!coin->has_segwit || !coin->segwit) { + return 0; + } if (!coin->has_address_type_p2wsh) { return 0; } @@ -92,7 +93,9 @@ bool compute_address(const CoinType *coin, } } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wsh encapsuled in p2sh address - *is_segwit = 1; + if (!coin->has_segwit || !coin->segwit) { + return 0; + } if (!coin->has_address_type_p2sh) { return 0; } @@ -108,7 +111,6 @@ bool compute_address(const CoinType *coin, } } else { // non-segwit p2sh multisig - *is_segwit = 0; prelen = address_prefix_bytes_len(coin->address_type_p2sh); address_write_prefix_bytes(coin->address_type_p2sh, raw); ripemd160(digest, 32, raw + prelen); @@ -118,7 +120,9 @@ bool compute_address(const CoinType *coin, } } else if (script_type == InputScriptType_SPENDWITNESS) { // segwit p2wpkh: pubkey hash is ripemd160 of sha256 - *is_segwit = 1; + if (!coin->has_segwit || !coin->segwit) { + return 0; + } if (!coin->has_address_type_p2wpkh) { return 0; } @@ -132,7 +136,9 @@ bool compute_address(const CoinType *coin, } } else if (script_type == InputScriptType_SPENDP2SHWITNESS) { // segwit p2wpkh embedded in p2sh - *is_segwit = 1; + if (!coin->has_segwit || !coin->segwit) { + return 0; + } if (!coin->has_address_type_p2sh) { return 0; } @@ -147,7 +153,6 @@ bool compute_address(const CoinType *coin, return 0; } } else { - *is_segwit = 0; ecdsa_get_address(node->public_key, coin->address_type, address, MAX_ADDR_SIZE); } return 1; @@ -159,7 +164,6 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T out->amount = in->amount; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; size_t addr_raw_len; - bool is_segwit; if (in->script_type == OutputScriptType_PAYTOOPRETURN) { // only 0 satoshi allowed for OP_RETURN @@ -201,7 +205,7 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T hdnode_fill_public_key(&node); if (!compute_address(coin, input_script_type, &node, in->has_multisig, &in->multisig, - in->address, &is_segwit)) { + in->address)) { return 0; // failed to compile output } } else if (!in->has_address) { diff --git a/firmware/transaction.h b/firmware/transaction.h index 808ccbca2e..06d07f765e 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -45,7 +45,7 @@ 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], bool *is_segwit); +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); From e31e55e5050155c426bab0b6f2d11412908fbbae Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 5 May 2017 14:37:40 +0200 Subject: [PATCH 22/25] simplify bip32 change logic --- firmware/signing.c | 51 +++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 4e5c197574..dedbec4068 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -319,8 +319,11 @@ void phase2_request_next_input(void) } } -void set_input_bip32_path(const TxInputType *tinput) +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 @@ -333,40 +336,46 @@ void set_input_bip32_path(const TxInputType *tinput) if (count > 2) { // if longer than 2 elements, store first N - 2 memcpy(in_address_n, tinput->address_n, (count - 2) * sizeof(uint32_t)); } - } else { - // 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; - } + 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) { + if (count < 1 || in_address_n_count < 1) { return 0; } - if (count == 1) { - return in_address_n_count == 1 && toutput->address_n[0] < 1000000; + if (count != in_address_n_count) { + return 0; + } + + if (toutput->address_n[count - 1] > MAX_BIP32_LAST_ELEMENT) { + return 0; } if (count >= 2) { - return in_address_n_count == count && - 0 == memcmp(in_address_n, toutput->address_n, (count - 2) * sizeof(uint32_t)) && - toutput->address_n[count - 2] == 1 && - toutput->address_n[count - 1] < 1000000; + if (0 != memcmp(in_address_n, toutput->address_n, (count - 2) * sizeof(uint32_t)) || + toutput->address_n[count - 2] != 1) { + return 0; + } } - return 0; + return 1; } bool compile_input_script_sig(TxInputType *tinput) @@ -463,7 +472,7 @@ static bool signing_check_input(TxInputType *txinput) { } // remember the input bip32 path // change addresses must use the same bip32 path as all inputs - set_input_bip32_path(txinput); + extract_input_bip32_path(txinput); // compute segwit hashPrevouts & hashSequence tx_prevout_hash(&hashers[0], txinput); tx_sequence_hash(&hashers[1], txinput); From 4343d0eaeb483127c07ca4ee62c3d6febd1f12bd Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Fri, 5 May 2017 15:45:58 +0200 Subject: [PATCH 23/25] show progressbar layout in GetAddress --- firmware/fsm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/fsm.c b/firmware/fsm.c index 1c4b86df83..70cfaf08f2 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -581,6 +581,7 @@ void fsm_msgGetAddress(GetAddress *msg) if (!node) return; hdnode_fill_public_key(node); + 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"); } From 261b8d5e411f95189bbc36da0f8bef2b99678a76 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sat, 6 May 2017 19:52:49 +0200 Subject: [PATCH 24/25] multisig: allow mismatched change addresses, show them as non-change --- firmware/signing.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index dedbec4068..780a587cb5 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -510,14 +510,11 @@ static bool signing_check_output(TxOutputType *txoutput) { } if (txoutput->script_type == OutputScriptType_PAYTOMULTISIG) { uint8_t h[32]; - if (!multisig_fp_set || multisig_fp_mismatch - || cryptoMultisigFingerprint(&(txoutput->multisig), h) == 0 - || memcmp(multisig_fp, h, 32) != 0) { - fsm_sendFailure(FailureType_Failure_Other, "Invalid multisig change address"); - signing_abort(); - return false; + if (multisig_fp_set && !multisig_fp_mismatch + && cryptoMultisigFingerprint(&(txoutput->multisig), h) + && memcmp(multisig_fp, h, 32) == 0) { + is_change = check_change_bip32_path(txoutput); } - is_change = check_change_bip32_path(txoutput); } else if (txoutput->script_type == OutputScriptType_PAYTOADDRESS || ((txoutput->script_type == OutputScriptType_PAYTOWITNESS || txoutput->script_type == OutputScriptType_PAYTOP2SHWITNESS) From 1ecbca83381118637481ab0293bb9bcb1d2ede95 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 9 May 2017 23:26:18 +0200 Subject: [PATCH 25/25] signing: add option to enable/disable mixing of segwit/non-segwit inputs --- firmware/signing.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/firmware/signing.c b/firmware/signing.c index 780a587cb5..8871b01e4b 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -748,6 +748,8 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { return true; } +#define ENABLE_SEGWIT_NONSEGWIT_MIXING 0 + void signing_txack(TransactionType *tx) { if (!signing) { @@ -774,6 +776,14 @@ void signing_txack(TransactionType *tx) 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; + } +#endif send_req_2_prev_meta(); } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { @@ -792,9 +802,20 @@ void signing_txack(TransactionType *tx) 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; - to.is_segwit = true; phase1_request_next_input(); } else { fsm_sendFailure(FailureType_Failure_Other, "Wrong input script type");