From b73e18e5732e3493edbd0860577f39053c86826a Mon Sep 17 00:00:00 2001 From: Pavol Rusnak Date: Tue, 12 Jun 2018 15:03:17 +0200 Subject: [PATCH] firmware: implement zip143 overwinter --- firmware/Makefile | 3 ++ firmware/signing.c | 88 ++++++++++++++++++++++++++++++------------ firmware/transaction.c | 45 ++++++++++++++++----- firmware/transaction.h | 5 ++- vendor/trezor-crypto | 2 +- 5 files changed, 107 insertions(+), 36 deletions(-) diff --git a/firmware/Makefile b/firmware/Makefile index a7c36296b..ba3f7516d 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -65,6 +65,8 @@ OBJS += ../vendor/trezor-crypto/ripemd160.o OBJS += ../vendor/trezor-crypto/sha2.o OBJS += ../vendor/trezor-crypto/sha3.o OBJS += ../vendor/trezor-crypto/blake256.o +OBJS += ../vendor/trezor-crypto/blake2b.o +OBJS += ../vendor/trezor-crypto/groestl.o OBJS += ../vendor/trezor-crypto/hasher.o OBJS += ../vendor/trezor-crypto/aes/aescrypt.o @@ -103,6 +105,7 @@ CFLAGS += -DDEBUG_LOG=$(DEBUG_LOG) CFLAGS += -DSCM_REVISION='"$(shell git rev-parse HEAD | sed 's:\(..\):\\x\1:g')"' CFLAGS += -DUSE_ETHEREUM=1 CFLAGS += -DUSE_NEM=1 +CFLAGS += -DUSE_MONERO=0 define GENERATE_CODE # $(1) - Basename for script and output header file diff --git a/firmware/signing.c b/firmware/signing.c index 9618a647c..83503a918 100644 --- a/firmware/signing.c +++ b/firmware/signing.c @@ -64,6 +64,7 @@ static uint64_t to_spend, authorized_amount, spending, change_spend; static uint32_t version = 1; static uint32_t lock_time = 0; static uint32_t expiry = 0; +static bool overwintered = false; static uint32_t next_nonsegwit_input; static uint32_t progress, progress_step, progress_meta_step; static bool multisig_fp_set, multisig_fp_mismatch; @@ -479,6 +480,7 @@ void signing_init(const SignTx *msg, const CoinInfo *_coin, const HDNode *_root) version = msg->version; lock_time = msg->lock_time; expiry = msg->expiry; + overwintered = msg->has_overwintered && msg->overwintered; uint32_t size = TXSIZE_HEADER + TXSIZE_FOOTER + ser_length_size(inputs_count) + ser_length_size(outputs_count); if (coin->decred) { @@ -508,22 +510,31 @@ 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, expiry, 0, coin->curve->hasher_sign); + tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered); if (coin->decred) { to.version |= (DECRED_SERIALIZE_FULL << 16); to.is_decred = true; - tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered); ti.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); ti.is_decred = true; } - hasher_Init(&hasher_prevouts, coin->curve->hasher_sign); - hasher_Init(&hasher_sequence, coin->curve->hasher_sign); - hasher_Init(&hasher_outputs, coin->curve->hasher_sign); - hasher_Init(&hasher_check, coin->curve->hasher_sign); - hasher_Init(&hasher_preimage, coin->curve->hasher_sign); + // segwit hashes for hashPrevouts and hashSequence + if (overwintered) { + hasher_Init(&hasher_prevouts, HASHER_OVERWINTER_PREVOUTS); + hasher_Init(&hasher_sequence, HASHER_OVERWINTER_SEQUENCE); + hasher_Init(&hasher_outputs, HASHER_OVERWINTER_OUTPUTS); + hasher_Init(&hasher_preimage, HASHER_OVERWINTER_PREIMAGE); + hasher_Init(&hasher_check, coin->curve->hasher_sign); + } else { + hasher_Init(&hasher_prevouts, coin->curve->hasher_sign); + hasher_Init(&hasher_sequence, coin->curve->hasher_sign); + hasher_Init(&hasher_outputs, coin->curve->hasher_sign); + hasher_Init(&hasher_preimage, coin->curve->hasher_sign); + hasher_Init(&hasher_check, coin->curve->hasher_sign); + } layoutProgressSwipe(_("Signing transaction"), 0); @@ -739,16 +750,39 @@ static void phase1_request_next_output(void) { static void signing_hash_bip143(const TxInputType *txinput, uint8_t *hash) { uint32_t hash_type = signing_hash_type(); hasher_Reset(&hasher_preimage); - hasher_Update(&hasher_preimage, (const uint8_t *)&version, 4); - hasher_Update(&hasher_preimage, hash_prevouts, 32); - hasher_Update(&hasher_preimage, hash_sequence, 32); - tx_prevout_hash(&hasher_preimage, txinput); - tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); - hasher_Update(&hasher_preimage, (const uint8_t*) &txinput->amount, 8); - tx_sequence_hash(&hasher_preimage, txinput); - hasher_Update(&hasher_preimage, hash_outputs, 32); - hasher_Update(&hasher_preimage, (const uint8_t*) &lock_time, 4); - hasher_Update(&hasher_preimage, (const uint8_t*) &hash_type, 4); + hasher_Update(&hasher_preimage, (const uint8_t *)&version, 4); // nVersion + hasher_Update(&hasher_preimage, hash_prevouts, 32); // hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // hashSequence + tx_prevout_hash(&hasher_preimage, txinput); // outpoint + tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // scriptCode + hasher_Update(&hasher_preimage, (const uint8_t*) &txinput->amount, 8); // amount + tx_sequence_hash(&hasher_preimage, txinput); // nSequence + hasher_Update(&hasher_preimage, hash_outputs, 32); // hashOutputs + hasher_Update(&hasher_preimage, (const uint8_t*)&lock_time, 4); // nLockTime + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); // nHashType + hasher_Final(&hasher_preimage, hash); +} + +static void signing_hash_zip143(const TxInputType *txinput, uint8_t *hash) { + uint32_t hash_type = signing_hash_type(); + hasher_Reset(&hasher_preimage); + uint32_t ver = version | TX_OVERWINTERED; // 1. nVersion | fOverwintered + hasher_Update(&hasher_preimage, (const uint8_t *)&ver, 4); + hasher_Update(&hasher_preimage, (const uint8_t *)&coin->version_group_id, 4); // 2. nVersionGroupId + hasher_Update(&hasher_preimage, hash_prevouts, 32); // 3. hashPrevouts + hasher_Update(&hasher_preimage, hash_sequence, 32); // 4. hashSequence + hasher_Update(&hasher_preimage, hash_outputs, 32); // 5. hashOutputs + // 6. hashJoinSplits + hasher_Update(&hasher_preimage, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32); + hasher_Update(&hasher_preimage, (const uint8_t*)&lock_time, 4); // 7. nLockTime + hasher_Update(&hasher_preimage, (const uint8_t*)&expiry, 4); // 8. expiryHeight + hasher_Update(&hasher_preimage, (const uint8_t*)&hash_type, 4); // 9. nHashType + + tx_prevout_hash(&hasher_preimage, txinput); // 10a. outpoint + tx_script_hash(&hasher_preimage, txinput->script_sig.size, txinput->script_sig.bytes); // 10b. scriptCode + hasher_Update(&hasher_preimage, (const uint8_t*)&txinput->amount, 8); // 10c. value + tx_sequence_hash(&hasher_preimage, txinput); // 10d. nSequence + hasher_Final(&hasher_preimage, hash); } @@ -933,9 +967,9 @@ void signing_txack(TransactionType *tx) } #endif - if (coin->force_bip143) { + if (coin->force_bip143 || overwintered) { if (!tx->inputs[0].has_amount) { - fsm_sendFailure(FailureType_Failure_DataError, _("BIP 143 input without amount")); + fsm_sendFailure(FailureType_Failure_DataError, _("BIP/ZIP 143 input without amount")); signing_abort(); return; } @@ -1011,7 +1045,7 @@ void signing_txack(TransactionType *tx) signing_abort(); return; } - tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->expiry, tx->extra_data_len, coin->curve->hasher_sign); + tx_init(&tp, tx->inputs_cnt, tx->outputs_cnt, tx->version, tx->lock_time, tx->expiry, tx->extra_data_len, coin->curve->hasher_sign, overwintered); if (coin->decred) { tp.version |= (DECRED_SERIALIZE_NO_WITNESS << 16); tp.is_decred = true; @@ -1094,7 +1128,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, expiry, 0, coin->curve->hasher_sign); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered); hasher_Reset(&hasher_check); } // check prevouts and script type @@ -1181,7 +1215,7 @@ void signing_txack(TransactionType *tx) resp.serialized.has_serialized_tx = true; if (tx->inputs[0].script_type == InputScriptType_SPENDMULTISIG || tx->inputs[0].script_type == InputScriptType_SPENDADDRESS) { - if (!coin->force_bip143) { + if (!(coin->force_bip143 || overwintered)) { fsm_sendFailure(FailureType_Failure_DataError, _("Transaction has changed during signing")); signing_abort(); return; @@ -1199,7 +1233,11 @@ void signing_txack(TransactionType *tx) authorized_amount -= tx->inputs[0].amount; uint8_t hash[32]; - signing_hash_bip143(&tx->inputs[0], hash); + if (overwintered) { + signing_hash_zip143(&tx->inputs[0], hash); + } else { + signing_hash_bip143(&tx->inputs[0], hash); + } if (!signing_sign_hash(&tx->inputs[0], node.private_key, node.public_key, hash)) return; // since this took a longer time, update progress @@ -1289,12 +1327,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, expiry, 0, coin->curve->hasher_sign); + tx_init(&to, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered); to.is_decred = true; } // witness hash - tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign); + tx_init(&ti, inputs_count, outputs_count, version, lock_time, expiry, 0, coin->curve->hasher_sign, overwintered); 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 106dba5a0..7d12749b3 100644 --- a/firmware/transaction.c +++ b/firmware/transaction.c @@ -465,10 +465,18 @@ uint32_t tx_serialize_script(uint32_t size, const uint8_t *data, uint8_t *out) uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) { int r = 4; - memcpy(out, &(tx->version), 4); - if (tx->is_segwit) { - memcpy(out + r, segwit_header, 2); - r += 2; + if (tx->overwintered) { + uint32_t ver = tx->version | TX_OVERWINTERED; + memcpy(out, &ver, 4); + uint32_t version_group_id = 0x03c48270; + memcpy(out + 4, &version_group_id, 4); + r += 4; + } else { + memcpy(out, &(tx->version), 4); + if (tx->is_segwit) { + memcpy(out + r, segwit_header, 2); + r += 2; + } } return r + ser_length(tx->inputs_len, out + r); } @@ -476,10 +484,18 @@ uint32_t tx_serialize_header(TxStruct *tx, uint8_t *out) uint32_t tx_serialize_header_hash(TxStruct *tx) { int r = 4; - hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); - if (tx->is_segwit) { - hasher_Update(&(tx->hasher), segwit_header, 2); - r += 2; + if (tx->overwintered) { + uint32_t ver = tx->version | TX_OVERWINTERED; + hasher_Update(&(tx->hasher), (const uint8_t *)&ver, 4); + uint32_t version_group_id = 0x03c48270; + hasher_Update(&(tx->hasher), (const uint8_t *)&version_group_id, 4); + r += 4; + } else { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->version), 4); + if (tx->is_segwit) { + hasher_Update(&(tx->hasher), segwit_header, 2); + r += 2; + } } return r + ser_length_hash(&(tx->hasher), tx->inputs_len); } @@ -598,6 +614,11 @@ 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->overwintered) { + memcpy(out + 4, &(tx->expiry), 4); + out[8] = 0x00; // nJoinSplit + return 9; + } if (tx->is_decred) { memcpy(out + 4, &(tx->expiry), 4); return 8; @@ -608,6 +629,11 @@ uint32_t tx_serialize_footer(TxStruct *tx, uint8_t *out) uint32_t tx_serialize_footer_hash(TxStruct *tx) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->lock_time), 4); + if (tx->overwintered) { + hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); + hasher_Update(&(tx->hasher), (const uint8_t *)"\x00", 1); // nJoinSplit + return 9; + } if (tx->is_decred) { hasher_Update(&(tx->hasher), (const uint8_t *)&(tx->expiry), 4); return 8; @@ -688,7 +714,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 expiry, uint32_t extra_data_len, HasherType hasher_sign) +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered) { tx->inputs_len = inputs_len; tx->outputs_len = outputs_len; @@ -702,6 +728,7 @@ void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t v tx->size = 0; tx->is_segwit = false; tx->is_decred = false; + tx->overwintered = overwintered; hasher_Init(&(tx->hasher), hasher_sign); } diff --git a/firmware/transaction.h b/firmware/transaction.h index c269c4518..22c252299 100644 --- a/firmware/transaction.h +++ b/firmware/transaction.h @@ -28,6 +28,8 @@ #include "hasher.h" #include "types.pb.h" +#define TX_OVERWINTERED 0x80000000 + typedef struct { uint32_t inputs_len; uint32_t outputs_len; @@ -41,6 +43,7 @@ typedef struct { uint32_t have_inputs; uint32_t have_outputs; + bool overwintered; uint32_t extra_data_len; uint32_t extra_data_received; @@ -68,7 +71,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 expiry, uint32_t extra_data_len, HasherType hasher_sign); +void tx_init(TxStruct *tx, uint32_t inputs_len, uint32_t outputs_len, uint32_t version, uint32_t lock_time, uint32_t expiry, uint32_t extra_data_len, HasherType hasher_sign, bool overwintered); 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 e81fb38ab..dba236172 160000 --- a/vendor/trezor-crypto +++ b/vendor/trezor-crypto @@ -1 +1 @@ -Subproject commit e81fb38ab452f2170769e9c454b10caaf8869974 +Subproject commit dba23617280ea75b434bbca4699abfd1524fdbe2