From c288a0e328428572de99e4848184c1a2b0661e79 Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Wed, 19 Oct 2016 09:37:46 +0200 Subject: [PATCH] implement TXEXTRADATA transaction RequestType --- firmware/protob/types.options | 1 + firmware/protob/types.pb.c | 8 +++-- firmware/protob/types.pb.h | 36 +++++++++++++++++------ firmware/signing.c | 55 ++++++++++++++++++++++++++++++++--- firmware/transaction.c | 24 ++++++++++++++- firmware/transaction.h | 7 ++++- vendor/trezor-common | 2 +- 7 files changed, 115 insertions(+), 18 deletions(-) diff --git a/firmware/protob/types.options b/firmware/protob/types.options index 40f553d3fb..536e91ab48 100644 --- a/firmware/protob/types.options +++ b/firmware/protob/types.options @@ -21,6 +21,7 @@ TxOutputBinType.script_pubkey max_size:520 TransactionType.inputs max_count:1 TransactionType.bin_outputs max_count:1 TransactionType.outputs max_count:1 +TransactionType.extra_data max_size:1024 TxRequestDetailsType.tx_hash max_size:32 diff --git a/firmware/protob/types.pb.c b/firmware/protob/types.pb.c index 04ef17f7bb..67148cacd7 100644 --- a/firmware/protob/types.pb.c +++ b/firmware/protob/types.pb.c @@ -75,7 +75,7 @@ const pb_field_t TxOutputBinType_fields[3] = { PB_LAST_FIELD }; -const pb_field_t TransactionType_fields[8] = { +const pb_field_t TransactionType_fields[10] = { PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TransactionType, version, version, 0), PB_FIELD2( 2, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, inputs, version, &TxInputType_fields), PB_FIELD2( 3, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, bin_outputs, inputs, &TxOutputBinType_fields), @@ -83,12 +83,16 @@ const pb_field_t TransactionType_fields[8] = { PB_FIELD2( 5, MESSAGE , REPEATED, STATIC , OTHER, TransactionType, outputs, lock_time, &TxOutputType_fields), PB_FIELD2( 6, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, inputs_cnt, outputs, 0), PB_FIELD2( 7, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, outputs_cnt, inputs_cnt, 0), + PB_FIELD2( 8, BYTES , OPTIONAL, STATIC , OTHER, TransactionType, extra_data, outputs_cnt, 0), + PB_FIELD2( 9, UINT32 , OPTIONAL, STATIC , OTHER, TransactionType, extra_data_len, extra_data, 0), PB_LAST_FIELD }; -const pb_field_t TxRequestDetailsType_fields[3] = { +const pb_field_t TxRequestDetailsType_fields[5] = { PB_FIELD2( 1, UINT32 , OPTIONAL, STATIC , FIRST, TxRequestDetailsType, request_index, request_index, 0), PB_FIELD2( 2, BYTES , OPTIONAL, STATIC , OTHER, TxRequestDetailsType, tx_hash, request_index, 0), + PB_FIELD2( 3, UINT32 , OPTIONAL, STATIC , OTHER, TxRequestDetailsType, extra_data_len, tx_hash, 0), + PB_FIELD2( 4, UINT32 , OPTIONAL, STATIC , OTHER, TxRequestDetailsType, extra_data_offset, extra_data_len, 0), PB_LAST_FIELD }; diff --git a/firmware/protob/types.pb.h b/firmware/protob/types.pb.h index de87ac94f7..a7dd51e8a2 100644 --- a/firmware/protob/types.pb.h +++ b/firmware/protob/types.pb.h @@ -45,7 +45,8 @@ typedef enum _RequestType { RequestType_TXINPUT = 0, RequestType_TXOUTPUT = 1, RequestType_TXMETA = 2, - RequestType_TXFINISHED = 3 + RequestType_TXFINISHED = 3, + RequestType_TXEXTRADATA = 4 } RequestType; typedef enum _ButtonRequestType { @@ -149,6 +150,10 @@ typedef struct _TxRequestDetailsType { uint32_t request_index; bool has_tx_hash; TxRequestDetailsType_tx_hash_t tx_hash; + bool has_extra_data_len; + uint32_t extra_data_len; + bool has_extra_data_offset; + uint32_t extra_data_offset; } TxRequestDetailsType; typedef struct { @@ -235,6 +240,11 @@ typedef struct _TxOutputType { TxOutputType_op_return_data_t op_return_data; } TxOutputType; +typedef struct { + size_t size; + uint8_t bytes[1024]; +} TransactionType_extra_data_t; + typedef struct _TransactionType { bool has_version; uint32_t version; @@ -250,6 +260,10 @@ typedef struct _TransactionType { uint32_t inputs_cnt; bool has_outputs_cnt; uint32_t outputs_cnt; + bool has_extra_data; + TransactionType_extra_data_t extra_data; + bool has_extra_data_len; + uint32_t extra_data_len; } TransactionType; /* Extensions */ @@ -275,8 +289,8 @@ extern const uint32_t IdentityType_index_default; #define TxInputType_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}, {0, {0}}, 0, false, {0, {0}}, false, 4294967295u, false, InputScriptType_SPENDADDRESS, false, MultisigRedeemScriptType_init_default, false, 0} #define TxOutputType_init_default {false, "", 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, (OutputScriptType)0, false, MultisigRedeemScriptType_init_default, false, {0, {0}}} #define TxOutputBinType_init_default {0, {0, {0}}} -#define TransactionType_init_default {false, 0, 0, {TxInputType_init_default}, 0, {TxOutputBinType_init_default}, false, 0, 0, {TxOutputType_init_default}, false, 0, false, 0} -#define TxRequestDetailsType_init_default {false, 0, false, {0, {0}}} +#define TransactionType_init_default {false, 0, 0, {TxInputType_init_default}, 0, {TxOutputBinType_init_default}, false, 0, 0, {TxOutputType_init_default}, false, 0, false, 0, false, {0, {0}}, false, 0} +#define TxRequestDetailsType_init_default {false, 0, false, {0, {0}}, false, 0, false, 0} #define TxRequestSerializedType_init_default {false, 0, false, {0, {0}}, false, {0, {0}}} #define IdentityType_init_default {false, "", false, "", false, "", false, "", false, "", false, 0u} #define HDNodeType_init_zero {0, 0, 0, {0, {0}}, false, {0, {0}}, false, {0, {0}}} @@ -286,8 +300,8 @@ extern const uint32_t IdentityType_index_default; #define TxInputType_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}, {0, {0}}, 0, false, {0, {0}}, false, 0, false, (InputScriptType)0, false, MultisigRedeemScriptType_init_zero, false, 0} #define TxOutputType_init_zero {false, "", 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, (OutputScriptType)0, false, MultisigRedeemScriptType_init_zero, false, {0, {0}}} #define TxOutputBinType_init_zero {0, {0, {0}}} -#define TransactionType_init_zero {false, 0, 0, {TxInputType_init_zero}, 0, {TxOutputBinType_init_zero}, false, 0, 0, {TxOutputType_init_zero}, false, 0, false, 0} -#define TxRequestDetailsType_init_zero {false, 0, false, {0, {0}}} +#define TransactionType_init_zero {false, 0, 0, {TxInputType_init_zero}, 0, {TxOutputBinType_init_zero}, false, 0, 0, {TxOutputType_init_zero}, false, 0, false, 0, false, {0, {0}}, false, 0} +#define TxRequestDetailsType_init_zero {false, 0, false, {0, {0}}, false, 0, false, 0} #define TxRequestSerializedType_init_zero {false, 0, false, {0, {0}}, false, {0, {0}}} #define IdentityType_init_zero {false, "", false, "", false, "", false, "", false, "", false, 0} @@ -316,6 +330,8 @@ extern const uint32_t IdentityType_index_default; #define TxOutputBinType_script_pubkey_tag 2 #define TxRequestDetailsType_request_index_tag 1 #define TxRequestDetailsType_tx_hash_tag 2 +#define TxRequestDetailsType_extra_data_len_tag 3 +#define TxRequestDetailsType_extra_data_offset_tag 4 #define TxRequestSerializedType_signature_index_tag 1 #define TxRequestSerializedType_signature_tag 2 #define TxRequestSerializedType_serialized_tx_tag 3 @@ -345,6 +361,8 @@ extern const uint32_t IdentityType_index_default; #define TransactionType_lock_time_tag 4 #define TransactionType_inputs_cnt_tag 6 #define TransactionType_outputs_cnt_tag 7 +#define TransactionType_extra_data_tag 8 +#define TransactionType_extra_data_len_tag 9 #define wire_in_tag 50002 #define wire_out_tag 50003 #define wire_debug_in_tag 50004 @@ -358,8 +376,8 @@ extern const pb_field_t MultisigRedeemScriptType_fields[4]; extern const pb_field_t TxInputType_fields[9]; extern const pb_field_t TxOutputType_fields[7]; extern const pb_field_t TxOutputBinType_fields[3]; -extern const pb_field_t TransactionType_fields[8]; -extern const pb_field_t TxRequestDetailsType_fields[3]; +extern const pb_field_t TransactionType_fields[10]; +extern const pb_field_t TxRequestDetailsType_fields[5]; extern const pb_field_t TxRequestSerializedType_fields[4]; extern const pb_field_t IdentityType_fields[7]; @@ -371,8 +389,8 @@ extern const pb_field_t IdentityType_fields[7]; #define TxInputType_size 5508 #define TxOutputType_size 3934 #define TxOutputBinType_size 534 -#define TransactionType_size 10009 -#define TxRequestDetailsType_size 40 +#define TransactionType_size 11042 +#define TxRequestDetailsType_size 52 #define TxRequestSerializedType_size 2132 #define IdentityType_size 416 diff --git a/firmware/signing.c b/firmware/signing.c index 4648536619..805d36b137 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -38,6 +38,7 @@ enum { STAGE_REQUEST_2_PREV_META, STAGE_REQUEST_2_PREV_INPUT, STAGE_REQUEST_2_PREV_OUTPUT, + STAGE_REQUEST_2_PREV_EXTRADATA, STAGE_REQUEST_3_OUTPUT, STAGE_REQUEST_4_INPUT, STAGE_REQUEST_4_OUTPUT, @@ -85,6 +86,7 @@ foreach I (idx1): foreach prevhash O (idx2): Request prevhash O STAGE_REQUEST_2_PREV_OUTPUT Add amount of prevhash O (which is amount of I) + Request prevhash extra data (if applicable) STAGE_REQUEST_2_PREV_EXTRADATA Calculate hash of streamed tx, compare to prevhash I foreach O (idx1): Request O STAGE_REQUEST_3_OUTPUT @@ -173,6 +175,22 @@ void send_req_2_prev_output(void) msg_write(MessageType_MessageType_TxRequest, &resp); } +void send_req_2_prev_extradata(uint32_t chunk_offset, uint32_t chunk_len) +{ + signing_stage = STAGE_REQUEST_2_PREV_EXTRADATA; + resp.has_request_type = true; + resp.request_type = RequestType_TXEXTRADATA; + resp.has_details = true; + resp.details.has_extra_data_offset = true; + resp.details.extra_data_offset = chunk_offset; + resp.details.has_extra_data_len = true; + resp.details.extra_data_len = chunk_len; + resp.details.has_tx_hash = true; + resp.details.tx_hash.size = input.prev_hash.size; + memcpy(resp.details.tx_hash.bytes, input.prev_hash.bytes, resp.details.tx_hash.size); + msg_write(MessageType_MessageType_TxRequest, &resp); +} + void send_req_3_output(void) { signing_stage = STAGE_REQUEST_3_OUTPUT; @@ -249,7 +267,7 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp multisig_fp_set = false; multisig_fp_mismatch = false; - tx_init(&to, inputs_count, outputs_count, version, lock_time, false); + 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)); @@ -261,6 +279,8 @@ void signing_init(uint32_t _inputs_count, uint32_t _outputs_count, const CoinTyp send_req_1_input(); } +#define MIN(a,b) (((a)<(b))?(a):(b)) + void signing_txack(TransactionType *tx) { if (!signing) { @@ -311,7 +331,7 @@ void signing_txack(TransactionType *tx) send_req_2_prev_meta(); return; case STAGE_REQUEST_2_PREV_META: - tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, false); + tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->extra_data_len, false); progress_meta_step = progress_step / (tp.inputs_len + tp.outputs_len); idx2 = 0; send_req_2_prev_input(); @@ -345,8 +365,35 @@ void signing_txack(TransactionType *tx) /* Check prevtx of next input */ idx2++; send_req_2_prev_output(); + } else { // last output + if (tp.extra_data_len > 0) { // has extra data + send_req_2_prev_extradata(0, MIN(1024, tp.extra_data_len)); + return; + } + tx_hash_final(&tp, hash, true); + if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { + fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash"); + signing_abort(); + return; + } + if (idx1 < inputs_count - 1) { + idx1++; + send_req_1_input(); + } else { + idx1 = 0; + send_req_3_output(); + } + } + return; + case STAGE_REQUEST_2_PREV_EXTRADATA: + if (!tx_serialize_extra_data_hash(&tp, tx->extra_data.bytes, tx->extra_data.size)) { + fsm_sendFailure(FailureType_Failure_Other, "Failed to serialize extra data"); + signing_abort(); + return; + } + 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 */ tx_hash_final(&tp, hash, true); if (memcmp(hash, input.prev_hash.bytes, 32) != 0) { fsm_sendFailure(FailureType_Failure_Other, "Encountered invalid prevhash"); @@ -453,7 +500,7 @@ void signing_txack(TransactionType *tx) case STAGE_REQUEST_4_INPUT: progress = 500 + ((idx1 * progress_step + idx2 * progress_meta_step) >> PROGRESS_PRECISION); if (idx2 == 0) { - tx_init(&ti, inputs_count, outputs_count, version, lock_time, true); + 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)); diff --git a/firmware/transaction.c b/firmware/transaction.c index 2513e58698..c4667b963b 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -407,7 +407,27 @@ uint32_t tx_serialize_output_hash(TxStruct *tx, const TxOutputBinType *output) 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) +uint32_t tx_serialize_extra_data_hash(TxStruct *tx, const uint8_t *data, uint32_t datalen) +{ + if (tx->have_inputs < tx->inputs_len) { + // not all inputs provided + return 0; + } + if (tx->have_outputs < tx->outputs_len) { + // not all inputs provided + return 0; + } + if (tx->extra_data_received + datalen > tx->extra_data_len) { + // we are receiving too much data + return 0; + } + sha256_Update(&(tx->ctx), data, datalen); + tx->extra_data_received += datalen; + tx->size += datalen; + 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, bool add_hash_type) { tx->inputs_len = inputs_len; tx->outputs_len = outputs_len; @@ -416,6 +436,8 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v tx->add_hash_type = add_hash_type; tx->have_inputs = 0; tx->have_outputs = 0; + tx->extra_data_len = extra_data_len; + tx->extra_data_received = 0; tx->size = 0; sha256_Init(&(tx->ctx)); } diff --git a/firmware/transaction.h b/firmware/transaction.h index bd64baa4f2..f9d76f8200 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -36,6 +36,10 @@ typedef struct { uint32_t have_inputs; uint32_t have_outputs; + + uint32_t extra_data_len; + uint32_t extra_data_received; + uint32_t size; SHA256_CTX ctx; @@ -50,9 +54,10 @@ int compile_output(const CoinType *coin, const HDNode *root, TxOutputType *in, T 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); +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, bool add_hash_type); 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); void tx_hash_final(TxStruct *t, uint8_t *hash, bool reverse); uint32_t transactionEstimateSize(uint32_t inputs, uint32_t outputs); diff --git a/vendor/trezor-common b/vendor/trezor-common index 1e74952359..20c1d05f9d 160000 --- a/vendor/trezor-common +++ b/vendor/trezor-common @@ -1 +1 @@ -Subproject commit 1e749523593506f2f268c038225f2c847ad2d0ef +Subproject commit 20c1d05f9de778e28726690c4969e6ce92296ce4