From a6d8e3a6c34c08e91ffba9aede64636853064e4c Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Sun, 10 Dec 2017 13:27:00 +0000 Subject: [PATCH 01/10] coins: Add Decred support --- firmware/coin_info.py | 1 + firmware/coins.h | 1 + 2 files changed, 2 insertions(+) diff --git a/firmware/coin_info.py b/firmware/coin_info.py index c8e8a5cf4..509d06c15 100755 --- a/firmware/coin_info.py +++ b/firmware/coin_info.py @@ -84,6 +84,7 @@ def coin_to_struct(coin): ("has_segwit", format_bool(coin["segwit"])), ("has_forkid", format_bool(coin["forkid"] is not None)), ("force_bip143", format_bool(coin["force_bip143"])), + ("decred", format_bool(coin["decred"])), ("address_type", format_number(coin["address_type"])), ("address_type_p2sh", format_number(coin["address_type_p2sh"])), ("xpub_magic", format_hex(coin["xpub_magic"])), diff --git a/firmware/coins.h b/firmware/coins.h index c6a1c0b7a..2cdd65724 100644 --- a/firmware/coins.h +++ b/firmware/coins.h @@ -36,6 +36,7 @@ typedef struct _CoinInfo { bool has_segwit; bool has_forkid; bool force_bip143; + bool decred; // address types > 0xFF represent a two-byte prefix in big-endian order uint32_t address_type; uint32_t address_type_p2sh; From e4dbba33e829837a2ebe55785fe1712e2aad36c4 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Sun, 10 Dec 2017 19:22:31 +0000 Subject: [PATCH 02/10] signing: Use SignTx in signing_init --- firmware/fsm.c | 2 +- firmware/signing.c | 10 +++++----- firmware/signing.h | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/firmware/fsm.c b/firmware/fsm.c index 4ad289215..6616b9e3e 100644 --- a/firmware/fsm.c +++ b/firmware/fsm.c @@ -541,7 +541,7 @@ void fsm_msgSignTx(SignTx *msg) const HDNode *node = fsm_getDerivedNode(coin->curve_name, NULL, 0, NULL); if (!node) return; - signing_init(msg->inputs_count, msg->outputs_count, coin, node, msg->version, msg->lock_time); + signing_init(msg, coin, node); } void fsm_msgTxAck(TxAck *msg) diff --git a/firmware/signing.c b/firmware/signing.c index 3c7003408..50e94c892 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -430,14 +430,14 @@ bool compile_input_script_sig(TxInputType *tinput) return tinput->script_sig.size > 0; } -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInfo *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time) +void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) { - inputs_count = _inputs_count; - outputs_count = _outputs_count; + inputs_count = msg->inputs_count; + outputs_count = msg->outputs_count; coin = _coin; root = _root; - version = _version; - lock_time = _lock_time; + version = msg->version; + lock_time = msg->lock_time; tx_weight = 4 * (TXSIZE_HEADER + TXSIZE_FOOTER + ser_length_size(inputs_count) diff --git a/firmware/signing.h b/firmware/signing.h index e4ee8c55d..68c3ccf53 100644 --- a/firmware/signing.h +++ b/firmware/signing.h @@ -25,9 +25,10 @@ #include "bip32.h" #include "coins.h" #include "hasher.h" +#include "messages.pb.h" #include "types.pb.h" -void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinInfo *_coin, const HDNode *_root, uint32_t _version, uint32_t _lock_time); +void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root); void signing_abort(void); void signing_txack(TransactionType *tx); From 110b82359804d47ab013592ed07f1b25f28e0c85 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Sun, 10 Dec 2017 19:22:31 +0000 Subject: [PATCH 03/10] signing: Add Decred support --- firmware/signing.c | 146 +++++++++++++++++++++++++++++++++++++++-- firmware/transaction.c | 90 +++++++++++++++++++++++-- firmware/transaction.h | 6 +- 3 files changed, 232 insertions(+), 10 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 50e94c892..f9f63b39b 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -45,7 +45,8 @@ enum { STAGE_REQUEST_4_OUTPUT, STAGE_REQUEST_SEGWIT_INPUT, STAGE_REQUEST_5_OUTPUT, - STAGE_REQUEST_SEGWIT_WITNESS + STAGE_REQUEST_SEGWIT_WITNESS, + STAGE_REQUEST_DECRED_WITNESS } signing_stage; static uint32_t idx1, idx2; static uint32_t signatures; @@ -57,6 +58,7 @@ static Hasher hashers[3]; static uint8_t CONFIDENTIAL privkey[32]; static uint8_t pubkey[33], sig[64]; static uint8_t hash_prevouts[32], hash_sequence[32],hash_outputs[32]; +static uint8_t hash_prefix[32]; static uint8_t hash_check[32]; static uint64_t to_spend, authorized_amount, spending, change_spend; static uint32_t version = 1; @@ -92,6 +94,12 @@ enum { SIGHASH_FORKID = 0x40, }; +enum { + DECRED_SERIALIZE_FULL = 0, + DECRED_SERIALIZE_NO_WITNESS = 1, + DECRED_SERIALIZE_WITNESS_SIGNING = 3, +}; + /* progress_step/meta_step are fixed point numbers, giving the * progress per input in permille with these many additional bits. @@ -298,6 +306,17 @@ void send_req_segwit_witness(void) msg_write(MessageType_MessageType_TxRequest, &resp); } +void send_req_decred_witness(void) +{ + signing_stage = STAGE_REQUEST_DECRED_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; @@ -464,6 +483,18 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) next_nonsegwit_input = 0xffffffff; tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + + if (coin->decred) { + to.version |= (DECRED_SERIALIZE_FULL << 16); + to.is_decred = true; + to.decred_expiry = msg->decred_expiry; + + tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); + ti.is_decred = true; + ti.decred_expiry = msg->decred_expiry; + } + // segwit hashes for hashPrevouts and hashSequence hasher_Init(&hashers[0], coin->curve->hasher_type); hasher_Init(&hashers[1], coin->curve->hasher_type); @@ -503,6 +534,15 @@ static bool signing_check_input(TxInputType *txinput) { // compute segwit hashPrevouts & hashSequence tx_prevout_hash(&hashers[0], txinput); tx_sequence_hash(&hashers[1], txinput); + if (coin->decred) { + // serialize Decred prefix in Phase 1 + resp.has_serialized = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.size = tx_serialize_input(&to, txinput, resp.serialized.serialized_tx.bytes); + + // compute Decred hashPrefix + tx_serialize_input_hash(&ti, txinput); + } // hash prevout and script type to check it later (relevant for fee computation) tx_prevout_hash(&hashers[2], txinput); hasher_Update(&hashers[2], (const uint8_t *) &txinput->script_type, sizeof(&txinput->script_type)); @@ -589,8 +629,17 @@ static bool signing_check_output(TxOutputType *txoutput) { signing_abort(); return false; } + if (coin->decred) { + // serialize Decred prefix in Phase 1 + resp.has_serialized = true; + resp.serialized.has_serialized_tx = true; + resp.serialized.serialized_tx.size = tx_serialize_output(&to, &bin_output, resp.serialized.serialized_tx.bytes); + + // compute Decred hashPrefix + tx_serialize_output_hash(&ti, &bin_output); + } // compute segwit hashOuts - tx_output_hash(&hashers[0], &bin_output); + tx_output_hash(&hashers[0], &bin_output, coin->decred); return true; } @@ -635,6 +684,10 @@ static void phase1_request_next_output(void) { idx1++; send_req_3_output(); } else { + if (coin->decred) { + // compute Decred hashPrefix + tx_hash_final(&ti, hash_prefix, false); + } hasher_Double(&hashers[0], hash_outputs); if (!signing_check_fee()) { return; @@ -643,7 +696,12 @@ static void phase1_request_next_output(void) { progress_meta_step = progress_step / (inputs_count + outputs_count); layoutProgress(_("Signing transaction"), progress); idx1 = 0; - phase2_request_next_input(); + if (coin->decred) { + // Decred prefix serialized in Phase 1, skip Phase 2 + send_req_decred_witness(); + } else { + phase2_request_next_input(); + } } } @@ -663,6 +721,15 @@ static void signing_hash_bip143(const TxInputType *txinput, uint8_t *hash) { hasher_Double(&hashers[0], hash); } +static void signing_hash_decred(const uint8_t *hash_witness, uint8_t *hash) { + uint32_t hash_type = signing_hash_type(); + hasher_Reset(&hashers[0]); + hasher_Update(&hashers[0], (const uint8_t*) &hash_type, 4); + hasher_Update(&hashers[0], hash_prefix, 32); + hasher_Update(&hashers[0], hash_witness, 32); + hasher_Final(&hashers[0], hash); +} + static bool signing_sign_hash(TxInputType *txinput, const uint8_t* private_key, const uint8_t *public_key, const uint8_t *hash) { resp.serialized.has_signature_index = true; resp.serialized.signature_index = idx1; @@ -785,6 +852,17 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { return true; } +static bool signing_sign_decred_input(TxInputType *txinput) { + uint8_t hash[32], hash_witness[32]; + tx_hash_final(&ti, hash_witness, false); + signing_hash_decred(hash_witness, hash); + resp.has_serialized = true; + if (!signing_sign_hash(txinput, node.private_key, node.public_key, hash)) + return false; + resp.serialized.serialized_tx.size = tx_serialize_decred_witness(&to, txinput, resp.serialized.serialized_tx.bytes); + return true; +} + #define ENABLE_SEGWIT_NONSEGWIT_MIXING 1 void signing_txack(TransactionType *tx) @@ -842,6 +920,11 @@ void signing_txack(TransactionType *tx) } } else if (tx->inputs[0].script_type == InputScriptType_SPENDWITNESS || tx->inputs[0].script_type == InputScriptType_SPENDP2SHWITNESS) { + if (coin->decred) { + fsm_sendFailure(FailureType_Failure_DataError, _("Decred does not support Segwit")); + signing_abort(); + return; + } if (!coin->has_segwit) { fsm_sendFailure(FailureType_Failure_DataError, _("Segwit not enabled on this coin")); signing_abort(); @@ -883,6 +966,11 @@ void signing_txack(TransactionType *tx) 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, coin->curve->hasher_type); + if (coin->decred) { + tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); + tp.is_decred = true; + tp.decred_expiry = tx->decred_expiry; + } progress_meta_step = progress_step / (tp.inputs_len + tp.outputs_len); idx2 = 0; if (tp.inputs_len > 0) { @@ -1008,7 +1096,7 @@ void signing_txack(TransactionType *tx) return; } // check hashOutputs - tx_output_hash(&hashers[0], &bin_output); + tx_output_hash(&hashers[0], &bin_output, coin->decred); if (!tx_serialize_output_hash(&ti, &bin_output)) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize output")); signing_abort(); @@ -1146,6 +1234,56 @@ void signing_txack(TransactionType *tx) signing_abort(); } return; + + case STAGE_REQUEST_DECRED_WITNESS: + progress = 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); + if (idx1 == 0) { + // witness + tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + to.is_decred = true; + } + + // witness hash + tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + ti.version |= (DECRED_SERIALIZE_WITNESS_SIGNING << 16); + ti.is_decred = true; + if (!compile_input_script_sig(&tx->inputs[0])) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); + signing_abort(); + return; + } + + for (idx2 = 0; idx2 < inputs_count; idx2++) { + uint32_t r; + if (idx2 == idx1) { + r = tx_serialize_decred_witness_hash(&ti, &tx->inputs[0]); + } else { + r = tx_serialize_decred_witness_hash(&ti, NULL); + } + + if (!r) { + fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize input")); + signing_abort(); + return; + } + } + + if (!signing_sign_decred_input(&tx->inputs[0])) { + 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) { + idx1++; + send_req_decred_witness(); + } else { + send_req_finished(); + signing_abort(); + } + return; } fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing error")); diff --git a/firmware/transaction.c b/firmware/transaction.c index 8cbe21c53..7f46e61ea 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -173,6 +173,7 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T { memset(out, 0, sizeof(TxOutputBinType)); out->amount = in->amount; + out->decred_script_version = in->decred_script_version; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; size_t addr_raw_len; @@ -399,10 +400,14 @@ uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input) return 4; } -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output) +uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, bool decred) { uint32_t r = 0; hasher_Update(hasher, (const uint8_t *)&output->amount, 8); r += 8; + if (decred) { + uint16_t script_version = output->decred_script_version & 0xFFFF; + hasher_Update(hasher, (const uint8_t *)&script_version, 2); r += 2; + } r += tx_script_hash(hasher, output->script_pubkey.size, output->script_pubkey.bytes); return r; } @@ -451,7 +456,12 @@ 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 += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); + if (tx->is_decred) { + uint8_t tree = input->decred_tree & 0xFF; + out[r++] = tree; + } else { + 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++; @@ -471,7 +481,12 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) r += tx_serialize_header_hash(tx); } r += tx_prevout_hash(&(tx->hasher), input); - r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); + if (tx->is_decred) { + uint8_t tree = input->decred_tree & 0xFF; + hasher_Update(&(tx->hasher), (const uint8_t *)&(tree), 1); r++; + } else { + r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); + } r += tx_sequence_hash(&(tx->hasher), input); tx->have_inputs++; @@ -480,6 +495,53 @@ uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input) return r; } +uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out) +{ + static const uint64_t amount = 0; + static const uint32_t block_height = 0x00000000; + static const uint32_t block_index = 0xFFFFFFFF; + + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += ser_length(tx->inputs_len, out + r); + } + memcpy(out + r, &amount, 8); r += 8; + memcpy(out + r, &block_height, 4); r += 4; + memcpy(out + r, &block_index, 4); r += 4; + r += tx_serialize_script(input->script_sig.size, input->script_sig.bytes, out + r); + + tx->have_inputs++; + tx->size += r; + + return r; +} + +uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input) +{ + if (tx->have_inputs >= tx->inputs_len) { + // already got all inputs + return 0; + } + uint32_t r = 0; + if (tx->have_inputs == 0) { + r += tx_serialize_header_hash(tx); + } + if (input == NULL) { + r += ser_length_hash(&(tx->hasher), 0); + } else { + r += tx_script_hash(&(tx->hasher), input->script_sig.size, input->script_sig.bytes); + } + + tx->have_inputs++; + tx->size += r; + + return r; +} + uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) { return ser_length(tx->outputs_len, out); @@ -493,12 +555,20 @@ uint32_t tx_serialize_middle_hash(TxStruct *tx) uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { memcpy(out, &(tx->lock_time), 4); + if (tx->is_decred) { + memcpy(out + 4, &(tx->decred_expiry), 4); + return 8; + } return 4; } uint32_t tx_serialize_footer_hash(TxStruct *tx) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); + if (tx->is_decred) { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->decred_expiry), 4); + return 8; + } return 4; } @@ -517,6 +587,10 @@ uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_ r += tx_serialize_middle(tx, out + r); } memcpy(out + r, &output->amount, 8); r += 8; + if (tx->is_decred) { + uint16_t script_version = output->decred_script_version & 0xFFFF; + memcpy(out + r, &script_version, 2); r += 2; + } r += tx_serialize_script(output->script_pubkey.size, output->script_pubkey.bytes, out + r); tx->have_outputs++; if (tx->have_outputs == tx->outputs_len @@ -541,7 +615,7 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) if (tx->have_outputs == 0) { r += tx_serialize_middle_hash(tx); } - r += tx_output_hash(&(tx->hasher), output); + r += tx_output_hash(&(tx->hasher), output, tx->is_decred); tx->have_outputs++; if (tx->have_outputs == tx->outputs_len && !tx->is_segwit) { @@ -583,12 +657,18 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v tx->extra_data_received = 0; tx->size = 0; tx->is_segwit = false; + tx->is_decred = false; + tx->decred_expiry = 0; hasher_Init(&(tx->hasher), hasher_type); } void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { - hasher_Double(&(t->hasher), hash); + if (t->is_decred) { + hasher_Final(&(t->hasher), hash); + } else { + hasher_Double(&(t->hasher), hash); + } if (!reverse) return; for (uint8_t i = 0; i < 16; i++) { uint8_t k = hash[31 - i]; diff --git a/firmware/transaction.h b/firmware/transaction.h index a65f58970..3695cfc4c 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -34,7 +34,9 @@ typedef struct { uint32_t version; uint32_t lock_time; + uint32_t decred_expiry; bool is_segwit; + bool is_decred; uint32_t have_inputs; uint32_t have_outputs; @@ -58,18 +60,20 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input); uint32_t tx_script_hash(Hasher *hasher, uint32_t size, const uint8_t *data); uint32_t tx_sequence_hash(Hasher *hasher, const TxInputType *input); -uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output); +uint32_t tx_output_hash(Hasher *hasher, const TxOutputBinType *output, bool decred); 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); +uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out); void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t extra_data_len, HasherType hasher_type); uint32_t tx_serialize_header_hash(TxStruct *tx); uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output); uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen); +uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input); void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); uint32_t tx_input_weight(const TxInputType *txinput); From ba4957317937aa13ae3d8f93f8265a1c41b15ff9 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Sun, 17 Dec 2017 21:42:33 +0000 Subject: [PATCH 04/10] signing: Document Decred signing --- firmware/signing.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/firmware/signing.c b/firmware/signing.c index f9f63b39b..05cb9054b 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -122,7 +122,10 @@ Phase1 - check inputs, previous transactions, and outputs foreach I (idx1): Request I STAGE_REQUEST_1_INPUT Add I to segwit hash_prevouts, hash_sequence + Add I to Decred hash_prefix Add I to TransactionChecksum (prevout and type) + if (Decred) + Return I If not segwit, Calculate amount of I: Request prevhash I, META STAGE_REQUEST_2_PREV_META foreach prevhash I (idx2): @@ -134,7 +137,10 @@ foreach I (idx1): Calculate hash of streamed tx, compare to prevhash I foreach O (idx1): Request O STAGE_REQUEST_3_OUTPUT + Add O to Decred hash_prefix Add O to TransactionChecksum + if (Decred) + Return O Display output Ask for confirmation @@ -144,6 +150,9 @@ Ask for confirmation Phase2: sign inputs, check that nothing changed =============================================== +if (Decred) + Skip to STAGE_REQUEST_DECRED_WITNESS + foreach I (idx1): // input to sign if (idx1 is segwit) Request I STAGE_REQUEST_SEGWIT_INPUT @@ -153,8 +162,8 @@ foreach I (idx1): // input to sign foreach I (idx2): Request I STAGE_REQUEST_4_INPUT If idx1 == idx2 - Remember key for signing Fill scriptsig + Remember key for signing Add I to StreamTransactionSign Add I to TransactionChecksum foreach O (idx2): @@ -182,6 +191,17 @@ foreach I (idx1): // input to sign Check amount Sign segwit prevhash, sequence, amount, outputs Return witness + +Phase3: sign Decred inputs +========================== + +foreach I (idx1): // input to sign STAGE_REQUEST_DECRED_WITNESS + Request I + Fill scriptSig + Compute hash_witness + + Sign (hash_type || hash_prefix || hash_witness) + Return witness */ void send_req_1_input(void) From 800ea672fe78afb36660bcd8883ac06c0dc94857 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Mon, 18 Dec 2017 16:49:17 +0000 Subject: [PATCH 05/10] signing: Compute tx_weight for Decred --- firmware/signing.c | 17 +++++++++++++---- firmware/transaction.c | 29 +++++++++++++++++++++++++++-- firmware/transaction.h | 3 ++- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/firmware/signing.c b/firmware/signing.c index 05cb9054b..2aff6bd21 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -478,9 +478,13 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) version = msg->version; lock_time = msg->lock_time; - tx_weight = 4 * (TXSIZE_HEADER + TXSIZE_FOOTER - + ser_length_size(inputs_count) - + ser_length_size(outputs_count)); + uint32_t size = TXSIZE_HEADER + TXSIZE_FOOTER + ser_length_size(inputs_count) + ser_length_size(outputs_count); + if (coin->decred) { + size += 4; // Decred expiry + size += ser_length_size(inputs_count); // Witness inputs count + } + + tx_weight = 4 * size; signatures = 0; idx1 = 0; @@ -904,7 +908,12 @@ void signing_txack(TransactionType *tx) switch (signing_stage) { case STAGE_REQUEST_1_INPUT: signing_check_input(&tx->inputs[0]); - tx_weight += tx_input_weight(&tx->inputs[0]); + + tx_weight += tx_input_weight(coin, &tx->inputs[0]); + if (coin->decred) { + tx_weight += tx_decred_witness_weight(&tx->inputs[0]); + } + if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { memcpy(&input, tx->inputs, sizeof(TxInputType)); diff --git a/firmware/transaction.c b/firmware/transaction.c index 7f46e61ea..e99dc7578 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -53,6 +53,8 @@ #define TXSIZE_P2PKHASH 25 /* size of a p2sh script (hash, push, 20 scripthash, equal) */ #define TXSIZE_P2SCRIPT 23 +/* size of a Decred witness (without script): 8 amount, 4 block height, 4 block index */ +#define TXSIZE_DECRED_WITNESS 16 static const uint8_t segwit_header[2] = {0,1}; @@ -677,7 +679,7 @@ void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) } } -uint32_t tx_input_weight(const TxInputType *txinput) { +static uint32_t tx_input_script_size(const TxInputType *txinput) { uint32_t input_script_size; if (txinput->has_multisig) { uint32_t multisig_script_size = TXSIZE_MULTISIGSCRIPT @@ -688,6 +690,16 @@ uint32_t tx_input_weight(const TxInputType *txinput) { } else { input_script_size = (1 + TXSIZE_SIGNATURE + 1 + TXSIZE_PUBKEY); } + + return input_script_size; +} + +uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput) { + if (coin->decred) { + return 4 * (TXSIZE_INPUT + 1); // Decred tree + } + + uint32_t input_script_size = tx_input_script_size(txinput); uint32_t weight = 4 * TXSIZE_INPUT; if (txinput->script_type == InputScriptType_SPENDADDRESS || txinput->script_type == InputScriptType_SPENDMULTISIG) { @@ -740,5 +752,18 @@ uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) { } } output_script_size += ser_length_size(output_script_size); - return 4 * (TXSIZE_OUTPUT + output_script_size); + + uint32_t size = TXSIZE_OUTPUT; + if (coin->decred) { + size += 2; // Decred script version + } + + return 4 * (size + output_script_size); +} + +uint32_t tx_decred_witness_weight(const TxInputType *txinput) { + uint32_t input_script_size = tx_input_script_size(txinput); + uint32_t size = TXSIZE_DECRED_WITNESS + ser_length_size(input_script_size) + input_script_size; + + return 4 * size; } diff --git a/firmware/transaction.h b/firmware/transaction.h index 3695cfc4c..cd344093f 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -76,7 +76,8 @@ uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_ uint32_t tx_serialize_decred_witness_hash(TxStruct *tx, const TxInputType *input); void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); -uint32_t tx_input_weight(const TxInputType *txinput); +uint32_t tx_input_weight(const CoinInfo *coin, const TxInputType *txinput); uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput); +uint32_t tx_decred_witness_weight(const TxInputType *txinput); #endif From d974774fe6d90a0dab44494a270281c3bf8b1cff Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Wed, 20 Dec 2017 16:25:01 +0000 Subject: [PATCH 06/10] crypto: Remove hardcoded instances of secp256k1 --- firmware/crypto.c | 10 +++++----- firmware/crypto.h | 4 ++-- firmware/signing.c | 14 +++++++------- firmware/transaction.c | 20 ++++++++++---------- firmware/transaction.h | 6 +++--- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/firmware/crypto.c b/firmware/crypto.c index 349116bef..4ff322c25 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -163,7 +163,7 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t mes // check if signature verifies the digest and recover the public key uint8_t pubkey[65]; - if (ecdsa_verify_digest_recover(&secp256k1, pubkey, signature + 1, hash, recid) != 0) { + if (ecdsa_verify_digest_recover(coin->curve->params, pubkey, signature + 1, hash, recid) != 0) { return 3; } // convert public key to compressed pubkey if necessary @@ -327,11 +327,11 @@ int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_le } */ -uint8_t *cryptoHDNodePathToPubkey(const HDNodePathType *hdnodepath) +uint8_t *cryptoHDNodePathToPubkey(const CoinInfo *coin, const HDNodePathType *hdnodepath) { if (!hdnodepath->node.has_public_key || hdnodepath->node.public_key.size != 33) return 0; static HDNode node; - if (hdnode_from_xpub(hdnodepath->node.depth, hdnodepath->node.child_num, hdnodepath->node.chain_code.bytes, hdnodepath->node.public_key.bytes, SECP256K1_NAME, &node) == 0) { + if (hdnode_from_xpub(hdnodepath->node.depth, hdnodepath->node.child_num, hdnodepath->node.chain_code.bytes, hdnodepath->node.public_key.bytes, coin->curve_name, &node) == 0) { return 0; } layoutProgressUpdate(true); @@ -344,10 +344,10 @@ uint8_t *cryptoHDNodePathToPubkey(const HDNodePathType *hdnodepath) return node.public_key; } -int cryptoMultisigPubkeyIndex(const MultisigRedeemScriptType *multisig, const uint8_t *pubkey) +int cryptoMultisigPubkeyIndex(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, const uint8_t *pubkey) { for (size_t i = 0; i < multisig->pubkeys_count; i++) { - const uint8_t *node_pubkey = cryptoHDNodePathToPubkey(&(multisig->pubkeys[i])); + const uint8_t *node_pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (node_pubkey && memcmp(node_pubkey, pubkey, 33) == 0) { return i; } diff --git a/firmware/crypto.h b/firmware/crypto.h index 7744c2909..2f72a48bc 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -51,9 +51,9 @@ int cryptoMessageEncrypt(curve_point *pubkey, const uint8_t *msg, size_t msg_siz int cryptoMessageDecrypt(curve_point *nonce, uint8_t *payload, size_t payload_len, const uint8_t *hmac, size_t hmac_len, const uint8_t *privkey, uint8_t *msg, size_t *msg_len, bool *display_only, bool *signing, uint8_t *address_raw); */ -uint8_t *cryptoHDNodePathToPubkey(const HDNodePathType *hdnodepath); +uint8_t *cryptoHDNodePathToPubkey(const CoinInfo *coin, const HDNodePathType *hdnodepath); -int cryptoMultisigPubkeyIndex(const MultisigRedeemScriptType *multisig, const uint8_t *pubkey); +int cryptoMultisigPubkeyIndex(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, const uint8_t *pubkey); int cryptoMultisigFingerprint(const MultisigRedeemScriptType *multisig, uint8_t *hash); diff --git a/firmware/signing.c b/firmware/signing.c index 2aff6bd21..5cccaff4a 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -460,7 +460,7 @@ bool compile_input_script_sig(TxInputType *tinput) } hdnode_fill_public_key(&node); if (tinput->has_multisig) { - tinput->script_sig.size = compile_script_multisig(&(tinput->multisig), tinput->script_sig.bytes); + tinput->script_sig.size = compile_script_multisig(coin, &(tinput->multisig), tinput->script_sig.bytes); } else { // SPENDADDRESS uint8_t hash[20]; ecdsa_get_pubkeyhash(node.public_key, coin->curve->hasher_type, hash); @@ -759,7 +759,7 @@ static bool signing_sign_hash(TxInputType *txinput, const uint8_t* private_key, resp.serialized.signature_index = idx1; resp.serialized.has_signature = true; resp.serialized.has_serialized_tx = true; - if (ecdsa_sign_digest(&secp256k1, private_key, hash, sig, NULL, NULL) != 0) { + if (ecdsa_sign_digest(coin->curve->params, private_key, hash, sig, NULL, NULL) != 0) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Signing failed")); signing_abort(); return false; @@ -769,7 +769,7 @@ static bool signing_sign_hash(TxInputType *txinput, const uint8_t* private_key, uint8_t sighash = signing_hash_type() & 0xff; if (txinput->has_multisig) { // fill in the signature - int pubkey_idx = cryptoMultisigPubkeyIndex(&(txinput->multisig), public_key); + int pubkey_idx = cryptoMultisigPubkeyIndex(coin, &(txinput->multisig), public_key); if (pubkey_idx < 0) { fsm_sendFailure(FailureType_Failure_DataError, _("Pubkey not found in multisig script")); signing_abort(); @@ -777,7 +777,7 @@ static bool signing_sign_hash(TxInputType *txinput, const uint8_t* private_key, } 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; - txinput->script_sig.size = serialize_script_multisig(&(txinput->multisig), sighash, txinput->script_sig.bytes); + txinput->script_sig.size = serialize_script_multisig(coin, &(txinput->multisig), sighash, txinput->script_sig.bytes); if (txinput->script_sig.size == 0) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to serialize multisig script")); signing_abort(); @@ -845,9 +845,9 @@ static bool signing_sign_segwit_input(TxInputType *txinput) { txinput->multisig.signatures[i].bytes[txinput->multisig.signatures[i].size] = sighash; r += tx_serialize_script(txinput->multisig.signatures[i].size + 1, txinput->multisig.signatures[i].bytes, resp.serialized.serialized_tx.bytes + r); } - uint32_t script_len = compile_script_multisig(&txinput->multisig, 0); + uint32_t script_len = compile_script_multisig(coin, &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); + r += compile_script_multisig(coin, &txinput->multisig, resp.serialized.serialized_tx.bytes + r); resp.serialized.serialized_tx.bytes[0] = nwitnesses; resp.serialized.serialized_tx.size = r; } else { // single signature @@ -1207,7 +1207,7 @@ void signing_txack(TransactionType *tx) tx->inputs[0].script_sig.bytes[1] = 0x00; // witness 0 script tx->inputs[0].script_sig.bytes[2] = 0x20; // push 32 bytes (digest) // compute digest of multisig script - if (!compile_script_multisig_hash(&tx->inputs[0].multisig, coin->curve->hasher_type, tx->inputs[0].script_sig.bytes + 3)) { + if (!compile_script_multisig_hash(coin, &tx->inputs[0].multisig, tx->inputs[0].script_sig.bytes + 3)) { fsm_sendFailure(FailureType_Failure_ProcessError, _("Failed to compile input")); signing_abort(); return; diff --git a/firmware/transaction.c b/firmware/transaction.c index e99dc7578..cee79124d 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -106,10 +106,10 @@ bool compute_address(const CoinInfo *coin, size_t prelen; if (has_multisig) { - if (cryptoMultisigPubkeyIndex(multisig, node->public_key) < 0) { + if (cryptoMultisigPubkeyIndex(coin, multisig, node->public_key) < 0) { return 0; } - if (compile_script_multisig_hash(multisig, coin->curve->hasher_type, digest) == 0) { + if (compile_script_multisig_hash(coin, multisig, digest) == 0) { return 0; } if (script_type == InputScriptType_SPENDWITNESS) { @@ -294,7 +294,7 @@ uint32_t compile_script_sig(uint32_t address_type, const uint8_t *pubkeyhash, ui } // if out == NULL just compute the length -uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t *out) +uint32_t compile_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *out) { if (!multisig->has_m) return 0; const uint32_t m = multisig->m; @@ -306,7 +306,7 @@ uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8 out[r] = 0x50 + m; r++; for (uint32_t i = 0; i < n; i++) { out[r] = 33; r++; // OP_PUSH 33 - const uint8_t *pubkey = cryptoHDNodePathToPubkey(&(multisig->pubkeys[i])); + const uint8_t *pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (!pubkey) return 0; memcpy(out + r, pubkey, 33); r += 33; } @@ -318,7 +318,7 @@ uint32_t compile_script_multisig(const MultisigRedeemScriptType *multisig, uint8 return r; } -uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, HasherType hasher_type, uint8_t *hash) +uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *hash) { if (!multisig->has_m) return 0; const uint32_t m = multisig->m; @@ -327,13 +327,13 @@ uint32_t compile_script_multisig_hash(const MultisigRedeemScriptType *multisig, if (n < 1 || n > 15) return 0; Hasher hasher; - hasher_Init(&hasher, hasher_type); + hasher_Init(&hasher, coin->curve->hasher_type); uint8_t d[2]; d[0] = 0x50 + m; hasher_Update(&hasher, d, 1); for (uint32_t i = 0; i < n; i++) { d[0] = 33; hasher_Update(&hasher, d, 1); // OP_PUSH 33 - const uint8_t *pubkey = cryptoHDNodePathToPubkey(&(multisig->pubkeys[i])); + const uint8_t *pubkey = cryptoHDNodePathToPubkey(coin, &(multisig->pubkeys[i])); if (!pubkey) return 0; hasher_Update(&hasher, pubkey, 33); } @@ -357,7 +357,7 @@ uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, return r; } -uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out) +uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out) { uint32_t r = 0; out[r] = 0x00; r++; @@ -369,12 +369,12 @@ uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uin memcpy(out + r, multisig->signatures[i].bytes, multisig->signatures[i].size); r += multisig->signatures[i].size; out[r] = sighash; r++; } - uint32_t script_len = compile_script_multisig(multisig, 0); + uint32_t script_len = compile_script_multisig(coin, multisig, 0); if (script_len == 0) { return 0; } r += op_push(script_len, out + r); - r += compile_script_multisig(multisig, out + r); + r += compile_script_multisig(coin, multisig, out + r); return r; } diff --git a/firmware/transaction.h b/firmware/transaction.h index cd344093f..8177a9655 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -51,10 +51,10 @@ typedef struct { bool compute_address(const CoinInfo *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, HasherType hasher_type, uint8_t *hash); +uint32_t compile_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *out); +uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t *hash); uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, const uint8_t *pubkey, uint32_t pubkey_len, uint8_t sighash, uint8_t *out); -uint32_t serialize_script_multisig(const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out); +uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out); int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, TxOutputBinType *out, bool needs_confirm); uint32_t tx_prevout_hash(Hasher *hasher, const TxInputType *input); From ec855c8638be6abb05318fce40bedaa1de031460 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Wed, 20 Dec 2017 20:38:36 +0000 Subject: [PATCH 07/10] transaction: Fix Decred multisig --- firmware/transaction.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/transaction.c b/firmware/transaction.c index cee79124d..1237da7d0 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -360,7 +360,10 @@ uint32_t serialize_script_sig(const uint8_t *signature, uint32_t signature_len, uint32_t serialize_script_multisig(const CoinInfo *coin, const MultisigRedeemScriptType *multisig, uint8_t sighash, uint8_t *out) { uint32_t r = 0; - out[r] = 0x00; r++; + if (!coin->decred) { + // Decred fixed the off-by-one bug + out[r] = 0x00; r++; + } for (uint32_t i = 0; i < multisig->signatures_count; i++) { if (multisig->signatures[i].size == 0) { continue; From 2bbd4dbdf5dbc54d920eafa006ec11c1948bfcb8 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Wed, 20 Dec 2017 20:51:45 +0000 Subject: [PATCH 08/10] signing: Check decred_script_version for txinput --- firmware/signing.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/firmware/signing.c b/firmware/signing.c index 5cccaff4a..bf5d87e0c 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -559,6 +559,12 @@ static bool signing_check_input(TxInputType *txinput) { tx_prevout_hash(&hashers[0], txinput); tx_sequence_hash(&hashers[1], txinput); if (coin->decred) { + if (txinput->decred_script_version > 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Decred v1+ scripts are not supported")); + signing_abort(); + return false; + } + // serialize Decred prefix in Phase 1 resp.has_serialized = true; resp.serialized.has_serialized_tx = true; @@ -1037,6 +1043,11 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } + if (coin->decred && tx->bin_outputs[0].decred_script_version > 0) { + fsm_sendFailure(FailureType_Failure_DataError, _("Decred script version does not match previous output")); + signing_abort(); + return; + } to_spend += tx->bin_outputs[0].amount; } if (idx2 < tp.outputs_len - 1) { From c5bce33b9b58b76ced05e065cc487c6d1a6f3a83 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Tue, 3 Apr 2018 15:25:06 +0100 Subject: [PATCH 09/10] vendor: Update trezor-crypto --- firmware/coins.c | 2 +- firmware/crypto.c | 18 +++++++++--------- firmware/signing.c | 30 +++++++++++++++--------------- firmware/transaction.c | 28 ++++++++++++---------------- firmware/transaction.h | 2 +- vendor/trezor-crypto | 2 +- 6 files changed, 39 insertions(+), 43 deletions(-) diff --git a/firmware/coins.c b/firmware/coins.c index 1600f9acb..1acd8f522 100644 --- a/firmware/coins.c +++ b/firmware/coins.c @@ -58,7 +58,7 @@ bool coinExtractAddressType(const CoinInfo *coin, const char *addr, uint32_t *ad { if (!addr) return false; uint8_t addr_raw[MAX_ADDR_RAW_SIZE]; - int len = base58_decode_check(addr, coin->curve->hasher_type, addr_raw, MAX_ADDR_RAW_SIZE); + int len = base58_decode_check(addr, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); if (len >= 21) { return coinExtractAddressTypeRaw(coin, addr_raw, address_type); } diff --git a/firmware/crypto.c b/firmware/crypto.c index 4ff322c25..2830f7b01 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -92,7 +92,7 @@ uint32_t deser_length(const uint8_t *in, uint32_t *out) int sshMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) { signature[0] = 0; // prefix: pad with zero, so all signatures are 65 bytes - return hdnode_sign(node, message, message_len, signature + 1, NULL, NULL); + return hdnode_sign(node, message, message_len, HASHER_SHA2, signature + 1, NULL, NULL); } int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uint8_t *signature) @@ -101,7 +101,7 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin const curve_info *ed25519_curve_info = get_curve_by_name(ED25519_NAME); if (ed25519_curve_info && node->curve == ed25519_curve_info) { // GPG supports variable size digest for Ed25519 signatures - return hdnode_sign(node, message, message_len, signature + 1, NULL, NULL); + return hdnode_sign(node, message, message_len, 0, signature + 1, NULL, NULL); } else { // Ensure 256-bit digest before proceeding if (message_len != 32) { @@ -113,13 +113,13 @@ int gpgMessageSign(HDNode *node, const uint8_t *message, size_t message_len, uin static void cryptoMessageHash(const CoinInfo *coin, const uint8_t *message, size_t message_len, uint8_t hash[HASHER_DIGEST_LENGTH]) { Hasher hasher; - hasher_Init(&hasher, coin->curve->hasher_type); + hasher_Init(&hasher, coin->curve->hasher_sign); hasher_Update(&hasher, (const uint8_t *)coin->signed_message_header, strlen(coin->signed_message_header)); uint8_t varint[5]; uint32_t l = ser_length(message_len, varint); hasher_Update(&hasher, varint, l); hasher_Update(&hasher, message, message_len); - hasher_Double(&hasher, hash); + hasher_Final(&hasher, hash); } int cryptoMessageSign(const CoinInfo *coin, HDNode *node, InputScriptType script_type, const uint8_t *message, size_t message_len, uint8_t *signature) @@ -177,8 +177,8 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t mes // p2pkh if (signature[0] >= 27 && signature[0] <= 34) { - size_t len = base58_decode_check(address, coin->curve->hasher_type, addr_raw, MAX_ADDR_RAW_SIZE); - ecdsa_get_address_raw(pubkey, coin->address_type, coin->curve->hasher_type, recovered_raw); + size_t len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); + ecdsa_get_address_raw(pubkey, coin->address_type, coin->curve->hasher_pubkey, recovered_raw); if (memcmp(recovered_raw, addr_raw, len) != 0 || len != address_prefix_bytes_len(coin->address_type) + 20) { return 2; @@ -186,8 +186,8 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t mes } else // segwit-in-p2sh if (signature[0] >= 35 && signature[0] <= 38) { - size_t len = base58_decode_check(address, coin->curve->hasher_type, addr_raw, MAX_ADDR_RAW_SIZE); - ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, coin->curve->hasher_type, recovered_raw); + size_t len = base58_decode_check(address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); + ecdsa_get_address_segwit_p2sh_raw(pubkey, coin->address_type_p2sh, coin->curve->hasher_pubkey, recovered_raw); if (memcmp(recovered_raw, addr_raw, len) != 0 || len != address_prefix_bytes_len(coin->address_type_p2sh) + 20) { return 2; @@ -201,7 +201,7 @@ int cryptoMessageVerify(const CoinInfo *coin, const uint8_t *message, size_t mes || !segwit_addr_decode(&witver, recovered_raw, &len, coin->bech32_prefix, address)) { return 4; } - ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_type, addr_raw); + ecdsa_get_pubkeyhash(pubkey, coin->curve->hasher_pubkey, addr_raw); if (memcmp(recovered_raw, addr_raw, len) != 0 || witver != 0 || len != 20) { return 2; diff --git a/firmware/signing.c b/firmware/signing.c index bf5d87e0c..8b34e10b8 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -362,8 +362,8 @@ void phase1_request_next_input(void) send_req_1_input(); } else { // compute segwit hashPrevouts & hashSequence - hasher_Double(&hashers[0], hash_prevouts); - hasher_Double(&hashers[1], hash_sequence); + hasher_Final(&hashers[0], hash_prevouts); + hasher_Final(&hashers[1], hash_sequence); hasher_Final(&hashers[2], hash_check); // init hashOutputs hasher_Reset(&hashers[0]); @@ -463,7 +463,7 @@ bool compile_input_script_sig(TxInputType *tinput) tinput->script_sig.size = compile_script_multisig(coin, &(tinput->multisig), tinput->script_sig.bytes); } else { // SPENDADDRESS uint8_t hash[20]; - ecdsa_get_pubkeyhash(node.public_key, coin->curve->hasher_type, hash); + ecdsa_get_pubkeyhash(node.public_key, coin->curve->hasher_pubkey, hash); tinput->script_sig.size = compile_script_sig(coin->address_type, hash, tinput->script_sig.bytes); } return tinput->script_sig.size > 0; @@ -506,23 +506,23 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) multisig_fp_mismatch = false; next_nonsegwit_input = 0xffffffff; - tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_sign); if (coin->decred) { to.version |= (DECRED_SERIALIZE_FULL << 16); to.is_decred = true; to.decred_expiry = msg->decred_expiry; - tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_sign); ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); ti.is_decred = true; ti.decred_expiry = msg->decred_expiry; } // segwit hashes for hashPrevouts and hashSequence - hasher_Init(&hashers[0], coin->curve->hasher_type); - hasher_Init(&hashers[1], coin->curve->hasher_type); - hasher_Init(&hashers[2], coin->curve->hasher_type); + hasher_Init(&hashers[0], coin->curve->hasher_sign); + hasher_Init(&hashers[1], coin->curve->hasher_sign); + hasher_Init(&hashers[2], coin->curve->hasher_sign); layoutProgressSwipe(_("Signing transaction"), 0); @@ -718,7 +718,7 @@ static void phase1_request_next_output(void) { // compute Decred hashPrefix tx_hash_final(&ti, hash_prefix, false); } - hasher_Double(&hashers[0], hash_outputs); + hasher_Final(&hashers[0], hash_outputs); if (!signing_check_fee()) { return; } @@ -748,7 +748,7 @@ static void signing_hash_bip143(const TxInputType *txinput, uint8_t *hash) { hasher_Update(&hashers[0], hash_outputs, 32); hasher_Update(&hashers[0], (const uint8_t*) &lock_time, 4); hasher_Update(&hashers[0], (const uint8_t*) &hash_type, 4); - hasher_Double(&hashers[0], hash); + hasher_Final(&hashers[0], hash); } static void signing_hash_decred(const uint8_t *hash_witness, uint8_t *hash) { @@ -797,7 +797,7 @@ static bool signing_sign_hash(TxInputType *txinput, const uint8_t* private_key, static bool signing_sign_input(void) { uint8_t hash[32]; - hasher_Double(&hashers[0], hash); + hasher_Final(&hashers[0], hash); if (memcmp(hash, hash_outputs, 32) != 0) { fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); signing_abort(); @@ -1000,7 +1000,7 @@ void signing_txack(TransactionType *tx) } 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, coin->curve->hasher_type); + tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->extra_data_len, coin->curve->hasher_sign); if (coin->decred) { tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); tp.is_decred = true; @@ -1084,7 +1084,7 @@ void signing_txack(TransactionType *tx) case STAGE_REQUEST_4_INPUT: 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, coin->curve->hasher_type); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_sign); hasher_Reset(&hashers[0]); } // check prevouts and script type @@ -1279,12 +1279,12 @@ void signing_txack(TransactionType *tx) progress = 500 + ((signatures * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); if (idx1 == 0) { // witness - tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + tx_init(&to, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_sign); to.is_decred = true; } // witness hash - tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_type); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, 0, coin->curve->hasher_sign); ti.version |= (DECRED_SERIALIZE_WITNESS_SIGNING << 16); ti.is_decred = true; if (!compile_input_script_sig(&tx->inputs[0])) { diff --git a/firmware/transaction.c b/firmware/transaction.c index 1237da7d0..5a544a927 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -131,11 +131,11 @@ bool compute_address(const CoinInfo *coin, raw[0] = 0; // push version raw[1] = 32; // push 32 bytes memcpy(raw+2, digest, 32); // push hash - hasher_Raw(coin->curve->hasher_type, raw, 34, digest); + hasher_Raw(coin->curve->hasher_pubkey, 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, coin->curve->hasher_type, address, MAX_ADDR_SIZE)) { + if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { return 0; } } else { @@ -143,7 +143,7 @@ bool compute_address(const CoinInfo *coin, 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, coin->curve->hasher_type, address, MAX_ADDR_SIZE)) { + if (!base58_encode_check(raw, prelen + 20, coin->curve->hasher_base58, address, MAX_ADDR_SIZE)) { return 0; } } @@ -152,7 +152,7 @@ bool compute_address(const CoinInfo *coin, if (!coin->has_segwit || !coin->bech32_prefix) { return 0; } - ecdsa_get_pubkeyhash(node->public_key, coin->curve->hasher_type, digest); + ecdsa_get_pubkeyhash(node->public_key, coin->curve->hasher_pubkey, digest); if (!segwit_addr_encode(address, coin->bech32_prefix, SEGWIT_VERSION_0, digest, 20)) { return 0; } @@ -164,9 +164,9 @@ bool compute_address(const CoinInfo *coin, if (!coin->has_address_type_p2sh) { return 0; } - ecdsa_get_address_segwit_p2sh(node->public_key, coin->address_type_p2sh, coin->curve->hasher_type, address, MAX_ADDR_SIZE); + ecdsa_get_address_segwit_p2sh(node->public_key, coin->address_type_p2sh, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); } else { - ecdsa_get_address(node->public_key, coin->address_type, coin->curve->hasher_type, address, MAX_ADDR_SIZE); + ecdsa_get_address(node->public_key, coin->address_type, coin->curve->hasher_pubkey, coin->curve->hasher_base58, address, MAX_ADDR_SIZE); } return 1; } @@ -232,7 +232,7 @@ int compile_output(const CoinInfo *coin, const HDNode *root, TxOutputType *in, T return 0; // failed to compile output } - addr_raw_len = base58_decode_check(in->address, coin->curve->hasher_type, addr_raw, MAX_ADDR_RAW_SIZE); + addr_raw_len = base58_decode_check(in->address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); size_t prefix_len; if (coin->has_address_type // p2pkh && addr_raw_len == 20 + (prefix_len = address_prefix_bytes_len(coin->address_type)) @@ -327,7 +327,7 @@ uint32_t compile_script_multisig_hash(const CoinInfo *coin, const MultisigRedeem if (n < 1 || n > 15) return 0; Hasher hasher; - hasher_Init(&hasher, coin->curve->hasher_type); + hasher_Init(&hasher, coin->curve->hasher_pubkey); uint8_t d[2]; d[0] = 0x50 + m; hasher_Update(&hasher, d, 1); @@ -650,7 +650,7 @@ uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_ return datalen; } -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t extra_data_len, HasherType hasher_type) +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t extra_data_len, HasherType hasher_sign) { tx->inputs_len = inputs_len; tx->outputs_len = outputs_len; @@ -664,16 +664,12 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v tx->is_segwit = false; tx->is_decred = false; tx->decred_expiry = 0; - hasher_Init(&(tx->hasher), hasher_type); + hasher_Init(&(tx->hasher), hasher_sign); } void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { - if (t->is_decred) { - hasher_Final(&(t->hasher), hash); - } else { - hasher_Double(&(t->hasher), hash); - } + hasher_Final(&(t->hasher), hash); if (!reverse) return; for (uint8_t i = 0; i < 16; i++) { uint8_t k = hash[31 - i]; @@ -744,7 +740,7 @@ uint32_t tx_output_weight(const CoinInfo *coin, const TxOutputType *txoutput) { && segwit_addr_decode(&witver, addr_raw, &addr_raw_len, coin->bech32_prefix, txoutput->address)) { output_script_size = 2 + addr_raw_len; } else { - addr_raw_len = base58_decode_check(txoutput->address, coin->curve->hasher_type, addr_raw, MAX_ADDR_RAW_SIZE); + addr_raw_len = base58_decode_check(txoutput->address, coin->curve->hasher_base58, addr_raw, MAX_ADDR_RAW_SIZE); if (coin->has_address_type && address_check_prefix(addr_raw, coin->address_type)) { output_script_size = TXSIZE_P2PKHASH; diff --git a/firmware/transaction.h b/firmware/transaction.h index 8177a9655..4363a3b2c 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -68,7 +68,7 @@ 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); uint32_t tx_serialize_decred_witness(TxStruct *tx, const TxInputType *input, uint8_t *out); -void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t extra_data_len, HasherType hasher_type); +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t extra_data_len, HasherType hasher_sign); uint32_t tx_serialize_header_hash(TxStruct *tx); uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output); diff --git a/vendor/trezor-crypto b/vendor/trezor-crypto index bb4c3d052..b9043659c 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit bb4c3d052561bd31856a03d975ca226571f6a893 +Subproject commit b9043659c5c91180c4abfb3ebb603f7b0385f201 From 834116daa80bc4c4fe735321d6b39b71b37e20f5 Mon Sep 17 00:00:00 2001 From: Saleem Rashid Date: Tue, 3 Apr 2018 15:27:53 +0100 Subject: [PATCH 10/10] coins-gen: Remove obsolete script --- firmware/coins-gen.py | 75 ------------------------------------------- 1 file changed, 75 deletions(-) delete mode 100755 firmware/coins-gen.py diff --git a/firmware/coins-gen.py b/firmware/coins-gen.py deleted file mode 100755 index a184ccd96..000000000 --- a/firmware/coins-gen.py +++ /dev/null @@ -1,75 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function -import json, sys - -coins_json = json.load(open('coins.json', 'r')) - -coins_stable, coins_debug = [], [] - -if len(sys.argv) != 2 or sys.argv[1] not in ("count", "array"): - print("usage: coins-gen.py [count|array]\n", file=sys.stderr) - sys.exit(1) - - -def get_fields(coin): - return [ - '"%s"' % coin['coin_name'] if coin['coin_name'] is not None else 'NULL', - '" %s"' % coin['coin_shortcut'] if coin['coin_shortcut'] is not None else 'NULL', - '%d' % coin['maxfee_kb'] if coin['maxfee_kb'] is not None else '0', - '"\\x%02x" "%s"' % (len(coin['signed_message_header']), coin['signed_message_header'].replace('\n', '\\n')) if coin['signed_message_header'] is not None else 'NULL', - 'true' if coin['address_type'] is not None else 'false', - 'true' if coin['address_type_p2sh'] is not None else 'false', - 'true' if coin['segwit'] else 'false', - 'true' if coin['forkid'] is not None else 'false', - 'true' if coin['force_bip143'] else 'false', - '%d' % coin['address_type'] if coin['address_type'] is not None else '0', - '%d' % coin['address_type_p2sh'] if coin['address_type_p2sh'] is not None else '0', - '0x%s' % coin['xpub_magic'] if coin['xpub_magic'] is not None else '0x00000000', - '0x%s' % coin['xprv_magic'] if coin['xprv_magic'] is not None else '0x00000000', - '%d' % coin['forkid'] if coin['forkid'] else '0', - '"%s"' % coin['bech32_prefix'] if coin.get('bech32_prefix') is not None else 'NULL', - '0x%08x' % (0x80000000 + coin['bip44']), - '%s_NAME' % 'secp256k1'.upper(), - '&%s_info' % 'secp256k1', - ] - - -def justify_width(coins): - for j in range(len(coins[0])): - l = max([len(x[j]) for x in coins]) + 1 - for i in range(len(coins)): - if coins[i][j][0] in '0123456789': - coins[i][j] = (coins[i][j] + ',').rjust(l) - else: - coins[i][j] = (coins[i][j] + ',').ljust(l) - - -for coin in coins_json: - if coin['firmware'] == 'stable': - coins_stable.append(get_fields(coin)) - if coin['firmware'] == 'debug': - coins_debug.append(get_fields(coin)) - -justify_width(coins_stable) -justify_width(coins_debug) - -print("// THIS IS A GENERATED FILE - DO NOT HAND EDIT\n\n") - -if sys.argv[1] == "array": - for row in coins_stable: - print('\t{' + ' '.join(row) + ' },') - - print('#if DEBUG_LINK') - - for row in coins_debug: - print('\t{' + ' '.join(row) + ' },') - - print('#endif') - - -if sys.argv[1] == "count": - print('#if DEBUG_LINK') - print('#define COINS_COUNT %d' % (len(coins_stable) + len(coins_debug))) - print('#else') - print('#define COINS_COUNT %d' % (len(coins_stable))) - print('#endif')