From 30a55829e5b31f0f437ce03782b74a692178ecdf Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Sun, 21 Dec 2014 19:39:20 +0100 Subject: [PATCH] rework hashing of transactions --- firmware/crypto.c | 18 +++++++ firmware/crypto.h | 3 ++ firmware/signing.c | 16 +++--- firmware/transaction.c | 113 +++++++++++++++++++++++++++++++---------- firmware/transaction.h | 8 +-- 5 files changed, 118 insertions(+), 40 deletions(-) diff --git a/firmware/crypto.c b/firmware/crypto.c index ea094fbdd..82d4ed7b6 100644 --- a/firmware/crypto.c +++ b/firmware/crypto.c @@ -47,6 +47,24 @@ uint32_t ser_length(uint32_t len, uint8_t *out) return 5; } +uint32_t ser_length_hash(SHA256_CTX *ctx, uint32_t len) +{ + if (len < 253) { + sha256_Update(ctx, (const uint8_t *)&len, 1); + return 1; + } + if (len < 0x10000) { + uint8_t d = 253; + sha256_Update(ctx, &d, 1); + sha256_Update(ctx, (const uint8_t *)&len, 2); + return 3; + } + uint8_t d = 254; + sha256_Update(ctx, &d, 1); + sha256_Update(ctx, (const uint8_t *)&len, 4); + return 5; +} + uint32_t deser_length(const uint8_t *in, uint32_t *out) { if (in[0] < 253) { diff --git a/firmware/crypto.h b/firmware/crypto.h index 83af40c0d..63b011c67 100644 --- a/firmware/crypto.h +++ b/firmware/crypto.h @@ -24,11 +24,14 @@ #include #include #include +#include #include #include "types.pb.h" uint32_t ser_length(uint32_t len, uint8_t *out); +uint32_t ser_length_hash(SHA256_CTX *ctx, uint32_t len); + int cryptoMessageSign(const uint8_t *message, pb_size_t message_len, const uint8_t *privkey, uint8_t *signature); int cryptoMessageVerify(const uint8_t *message, pb_size_t message_len, const uint8_t *address_raw, const uint8_t *signature); diff --git a/firmware/signing.c b/firmware/signing.c index a9f33672a..f3f8bf6db 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -248,7 +248,7 @@ void signing_txack(TransactionType *tx) send_req_2_prev_input(); return; case STAGE_REQUEST_2_PREV_INPUT: - if (!tx_hash_input(&tp, tx->inputs)) { + if (!tx_serialize_input_hash(&tp, tx->inputs)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input"); signing_abort(); return; @@ -261,7 +261,7 @@ void signing_txack(TransactionType *tx) } return; case STAGE_REQUEST_2_PREV_OUTPUT: - if (!tx_hash_output(&tp, tx->bin_outputs)) { + if (!tx_serialize_output_hash(&tp, tx->bin_outputs)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output"); signing_abort(); return; @@ -288,7 +288,7 @@ void signing_txack(TransactionType *tx) return; case STAGE_REQUEST_3_INPUT: progress++; - if (!tx_hash_input(&tc, tx->inputs)) { + if (!tx_serialize_input_hash(&tc, tx->inputs)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input"); signing_abort(); return; @@ -347,7 +347,7 @@ void signing_txack(TransactionType *tx) } else { tx->inputs[0].script_sig.size = 0; } - if (!tx_hash_input(&ti, tx->inputs)) { + if (!tx_serialize_input_hash(&ti, tx->inputs)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize input"); signing_abort(); return; @@ -406,12 +406,12 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } - if (!tx_hash_output(&tc, &bin_output)) { + if (!tx_serialize_output_hash(&tc, &bin_output)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output"); signing_abort(); return; } - if (!tx_hash_output(&ti, &bin_output)) { + if (!tx_serialize_output_hash(&ti, &bin_output)) { fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize output"); signing_abort(); return; @@ -462,7 +462,7 @@ void signing_txack(TransactionType *tx) } 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.prev_hash.bytes, input.prev_index, input.script_sig.bytes, input.script_sig.size, input.sequence, resp.serialized.serialized_tx.bytes); + resp.serialized.serialized_tx.size = tx_serialize_input(&to, &input, resp.serialized.serialized_tx.bytes); if (idx1i < inputs_count - 1) { idx1i++; send_req_1_input(); @@ -502,7 +502,7 @@ void signing_txack(TransactionType *tx) } resp.has_serialized = true; resp.serialized.has_serialized_tx = true; - resp.serialized.serialized_tx.size = tx_serialize_output(&to, bin_output.amount, bin_output.script_pubkey.bytes, bin_output.script_pubkey.size, resp.serialized.serialized_tx.bytes); + resp.serialized.serialized_tx.size = tx_serialize_output(&to, &bin_output, resp.serialized.serialized_tx.bytes); if (idx4o < outputs_count - 1) { idx4o++; send_req_4_output(); diff --git a/firmware/transaction.c b/firmware/transaction.c index e851399c4..c224f4191 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -29,6 +29,7 @@ #include "ripemd160.h" #include "base58.h" #include "messages.pb.h" +#include "types.pb.h" uint32_t op_push(uint32_t i, uint8_t *out) { if (i < 0x4C) { @@ -261,7 +262,13 @@ uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) return 4 + ser_length(tx->inputs_len, out + 4); } -uint32_t tx_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_index, uint8_t *script_sig, uint32_t script_sig_len, uint32_t sequence, uint8_t *out) +uint32_t tx_serialize_header_hash(TxStruct *tx) +{ + sha256_Update(&(tx->ctx), (const uint8_t *)&(tx->version), 4); + return 4 + ser_length_hash(&(tx->ctx), tx->inputs_len); +} + +uint32_t tx_serialize_input(TxStruct *tx, const TxInputType *input, uint8_t *out) { int i; if (tx->have_inputs >= tx->inputs_len) { @@ -273,13 +280,39 @@ uint32_t tx_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_inde r += tx_serialize_header(tx, out + r); } for (i = 0; i < 32; i++) { - *(out + r + i) = prev_hash[31 - i]; + *(out + r + i) = input->prev_hash.bytes[31 - i]; + } + 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; + memcpy(out + r, &input->sequence, 4); r += 4; + + tx->have_inputs++; + tx->size += r; + + return r; +} + +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; + } + uint32_t r = 0; + 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; - memcpy(out + r, &prev_index, 4); r += 4; - r += ser_length(script_sig_len, out + r); - memcpy(out + r, script_sig, script_sig_len); r+= script_sig_len; - memcpy(out + r, &sequence, 4); r += 4; + sha256_Update(&(tx->ctx), (const uint8_t *)&input->prev_index, 4); r += 4; + r += ser_length_hash(&(tx->ctx), input->script_sig.size); + sha256_Update(&(tx->ctx), input->script_sig.bytes, input->script_sig.size); r += input->script_sig.size; + sha256_Update(&(tx->ctx), (const uint8_t *)&input->sequence, 4); r += 4; tx->have_inputs++; tx->size += r; @@ -292,6 +325,11 @@ uint32_t tx_serialize_middle(TxStruct *tx, uint8_t *out) return ser_length(tx->outputs_len, out); } +uint32_t tx_serialize_middle_hash(TxStruct *tx) +{ + return ser_length_hash(&(tx->ctx), tx->outputs_len); +} + uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) { memcpy(out, &(tx->lock_time), 4); @@ -304,7 +342,19 @@ uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) } } -uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubkey, uint32_t script_pubkey_len, uint8_t *out) +uint32_t tx_serialize_footer_hash(TxStruct *tx) +{ + sha256_Update(&(tx->ctx), (const uint8_t *)&(tx->lock_time), 4); + if (tx->add_hash_type) { + uint32_t ht = 1; + sha256_Update(&(tx->ctx), (const uint8_t *)&ht, 4); + return 8; + } else { + return 4; + } +} + +uint32_t tx_serialize_output(TxStruct *tx, const TxOutputBinType *output, uint8_t *out) { if (tx->have_inputs < tx->inputs_len) { // not all inputs provided @@ -318,9 +368,9 @@ uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubk if (tx->have_outputs == 0) { r += tx_serialize_middle(tx, out + r); } - memcpy(out + r, &amount, 8); r += 8; - r += ser_length(script_pubkey_len, out + r); - memcpy(out + r, script_pubkey, script_pubkey_len); r+= script_pubkey_len; + 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; tx->have_outputs++; if (tx->have_outputs == tx->outputs_len) { r += tx_serialize_footer(tx, out + r); @@ -329,6 +379,31 @@ uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubk return r; } +uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) +{ + if (tx->have_inputs < tx->inputs_len) { + // not all inputs provided + return 0; + } + if (tx->have_outputs >= tx->outputs_len) { + // already got all outputs + return 0; + } + uint32_t r = 0; + 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; + tx->have_outputs++; + if (tx->have_outputs == tx->outputs_len) { + r += tx_serialize_footer_hash(tx); + } + tx->size += r; + return r; +} + void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, bool add_hash_type) { tx->inputs_len = inputs_len; @@ -342,24 +417,6 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v sha256_Init(&(tx->ctx)); } -bool tx_hash_input(TxStruct *t, TxInputType *input) -{ - uint8_t buf[1024]; - uint32_t r = tx_serialize_input(t, input->prev_hash.bytes, input->prev_index, input->script_sig.bytes, input->script_sig.size, input->sequence, buf); - if (!r) return false; - sha256_Update(&(t->ctx), buf, r); - return true; -} - -bool tx_hash_output(TxStruct *t, TxOutputBinType *output) -{ - uint8_t buf[1024]; - uint32_t r = tx_serialize_output(t, output->amount, output->script_pubkey.bytes, output->script_pubkey.size, buf); - if (!r) return false; - sha256_Update(&(t->ctx), buf, r); - return true; -} - void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse) { sha256_Final(hash, &(t->ctx)); diff --git a/firmware/transaction.h b/firmware/transaction.h index 7fb819aa3..e6980a041 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -47,12 +47,12 @@ 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_serialize_input(TxStruct *tx, uint8_t *prev_hash, uint32_t prev_index, uint8_t *script_sig, uint32_t script_sig_len, uint32_t sequence, uint8_t *out); -uint32_t tx_serialize_output(TxStruct *tx, uint64_t amount, uint8_t *script_pubkey, uint32_t script_pubkey_len, 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); void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, bool add_hash_type); -bool tx_hash_input(TxStruct *t, TxInputType *input); -bool tx_hash_output(TxStruct *t, TxOutputBinType *output); +uint32_t tx_serialize_input_hash(TxStruct *tx, const TxInputType *input); +uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output); void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs);