diff --git a/core/src/apps/bitcoin/sign_tx/bitcoin.py b/core/src/apps/bitcoin/sign_tx/bitcoin.py index b94d64979..64a05e7af 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoin.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoin.py @@ -239,21 +239,22 @@ class Bitcoin: self, i: int, txi: TxInput, + tx: Union[SignTx, PrevTx], + hash143: Hash143, + h_approved: HashWriter, public_keys: List[bytes], threshold: int, script_pubkey: bytes, + tx_hash: Optional[bytes] = None, ) -> bytes: if txi.witness: - return self.hash143.preimage_hash( - txi, - public_keys, - threshold, - self.tx, - self.coin, - self.get_sighash_type(txi), + return hash143.preimage_hash( + txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi), ) else: - digest, _, _ = await self.get_legacy_tx_digest(i, script_pubkey) + digest, _, _ = await self.get_legacy_tx_digest( + i, tx, h_approved, script_pubkey, tx_hash + ) return digest async def verify_external_input( @@ -276,7 +277,14 @@ class Bitcoin: verifier.ensure_hash_type(self.get_hash_type(txi)) tx_digest = await self.get_tx_digest( - i, txi, verifier.public_keys, verifier.threshold, script_pubkey + i, + txi, + self.tx, + self.hash143, + self.h_approved, + verifier.public_keys, + verifier.threshold, + script_pubkey, ) verifier.verify(tx_digest) @@ -347,27 +355,34 @@ class Bitcoin: ) async def get_legacy_tx_digest( - self, index: int, script_pubkey: Optional[bytes] = None + self, + index: int, + tx: Union[SignTx, PrevTx], + h_approved: HashWriter, + script_pubkey: Optional[bytes] = None, + tx_hash: Optional[bytes] = None, ) -> Tuple[bytes, TxInput, Optional[bip32.HDNode]]: + # the transaction digest which gets signed for this input h_sign = self.create_hash_writer() # should come out the same as h_approved, checked before signing the digest h_check = self.create_hash_writer() - self.write_tx_header(h_sign, self.tx, witness_marker=False) - write_bitcoin_varint(h_sign, self.tx.inputs_count) + self.write_tx_header(h_sign, tx, witness_marker=False) + write_bitcoin_varint(h_sign, tx.inputs_count) - for i in range(self.tx.inputs_count): + for i in range(tx.inputs_count): # STAGE_REQUEST_4_INPUT in legacy - txi = await helpers.request_tx_input(self.tx_req, i, self.coin) + txi = await helpers.request_tx_input(self.tx_req, i, self.coin, tx_hash) writers.write_tx_input_check(h_check, txi) # Only the previous UTXO's scriptPubKey is included in h_sign. if i == index: txi_sign = txi node = None if not script_pubkey: - self.wallet_path.check_input(txi) - self.multisig_fingerprint.check_input(txi) + if isinstance(tx, SignTx): + self.wallet_path.check_input(txi) + self.multisig_fingerprint.check_input(txi) node = self.keychain.derive(txi.address_n) key_sign_pub = node.public_key() if txi.multisig: @@ -390,27 +405,29 @@ class Bitcoin: else: self.write_tx_input(h_sign, txi, bytes()) - write_bitcoin_varint(h_sign, self.tx.outputs_count) + write_bitcoin_varint(h_sign, tx.outputs_count) - for i in range(self.tx.outputs_count): + for i in range(tx.outputs_count): # STAGE_REQUEST_4_OUTPUT in legacy - txo = await helpers.request_tx_output(self.tx_req, i, self.coin) + txo = await helpers.request_tx_output(self.tx_req, i, self.coin, tx_hash) script_pubkey = self.output_derive_script(txo) self.write_tx_output(h_check, txo, script_pubkey) self.write_tx_output(h_sign, txo, script_pubkey) - writers.write_uint32(h_sign, self.tx.lock_time) + writers.write_uint32(h_sign, tx.lock_time) writers.write_uint32(h_sign, self.get_sighash_type(txi_sign)) # check that the inputs were the same as those streamed for approval - if self.h_approved.get_digest() != h_check.get_digest(): + if h_approved.get_digest() != h_check.get_digest(): raise wire.ProcessError("Transaction has changed during signing") tx_digest = writers.get_tx_hash(h_sign, double=self.coin.sign_hash_double) return tx_digest, txi_sign, node async def sign_nonsegwit_input(self, i: int) -> None: - tx_digest, txi, node = await self.get_legacy_tx_digest(i) + tx_digest, txi, node = await self.get_legacy_tx_digest( + i, self.tx, self.h_approved + ) assert node is not None # compute the signature from the tx digest diff --git a/core/src/apps/bitcoin/sign_tx/bitcoinlike.py b/core/src/apps/bitcoin/sign_tx/bitcoinlike.py index 2cc0bd2b9..2f08b7c10 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoinlike.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoinlike.py @@ -9,10 +9,11 @@ from apps.common.writers import write_bitcoin_varint from .. import multisig, writers from . import helpers -from .bitcoin import Bitcoin, input_is_nonsegwit +from .bitcoin import Bitcoin, Hash143, input_is_nonsegwit if False: - from typing import List, Union + from typing import List, Optional, Union + from trezor.utils import HashWriter _SIGHASH_FORKID = const(0x40) @@ -44,22 +45,21 @@ class Bitcoinlike(Bitcoin): self, i: int, txi: TxInput, + tx: Union[SignTx, PrevTx], + hash143: Hash143, + h_approved: HashWriter, public_keys: List[bytes], threshold: int, script_pubkey: bytes, + tx_hash: Optional[bytes] = None, ) -> bytes: if self.coin.force_bip143: - return self.hash143.preimage_hash( - txi, - public_keys, - threshold, - self.tx, - self.coin, - self.get_sighash_type(txi), + return hash143.preimage_hash( + txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi), ) else: return await super().get_tx_digest( - i, txi, public_keys, threshold, script_pubkey + i, txi, tx, hash143, h_approved, public_keys, threshold, script_pubkey ) def get_sighash_type(self, txi: TxInput) -> int: diff --git a/core/src/apps/bitcoin/sign_tx/helpers.py b/core/src/apps/bitcoin/sign_tx/helpers.py index d2031a1da..949446354 100644 --- a/core/src/apps/bitcoin/sign_tx/helpers.py +++ b/core/src/apps/bitcoin/sign_tx/helpers.py @@ -8,6 +8,8 @@ from trezor.messages.RequestType import ( TXFINISHED, TXINPUT, TXMETA, + TXORIGINPUT, + TXORIGOUTPUT, TXOUTPUT, ) from trezor.messages.SignTx import SignTx @@ -29,7 +31,7 @@ from ..writers import TX_HASH_SIZE from . import layout if False: - from typing import Any, Awaitable + from typing import Any, Awaitable, Optional # Machine instructions @@ -170,9 +172,13 @@ def request_tx_extra_data( # type: ignore return ack.tx.extra_data_chunk -def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo) -> Awaitable[TxInput]: # type: ignore +def request_tx_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: Optional[bytes] = None) -> Awaitable[TxInput]: # type: ignore assert tx_req.details is not None - tx_req.request_type = TXINPUT + if tx_hash: + tx_req.request_type = TXORIGINPUT + tx_req.details.tx_hash = tx_hash + else: + tx_req.request_type = TXINPUT tx_req.details.request_index = i ack = yield TxAckInput, tx_req _clear_tx_request(tx_req) @@ -189,9 +195,13 @@ def request_tx_prev_input(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: by return sanitize_tx_prev_input(ack.tx.input, coin) -def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo) -> Awaitable[TxOutput]: # type: ignore +def request_tx_output(tx_req: TxRequest, i: int, coin: CoinInfo, tx_hash: Optional[bytes] = None) -> Awaitable[TxOutput]: # type: ignore assert tx_req.details is not None - tx_req.request_type = TXOUTPUT + if tx_hash: + tx_req.request_type = TXORIGOUTPUT + tx_req.details.tx_hash = tx_hash + else: + tx_req.request_type = TXOUTPUT tx_req.details.request_index = i ack = yield TxAckOutput, tx_req _clear_tx_request(tx_req) diff --git a/core/src/apps/bitcoin/sign_tx/zcash.py b/core/src/apps/bitcoin/sign_tx/zcash.py index 8af194a3c..93a030306 100644 --- a/core/src/apps/bitcoin/sign_tx/zcash.py +++ b/core/src/apps/bitcoin/sign_tx/zcash.py @@ -30,7 +30,7 @@ from .hash143 import Hash143 if False: from apps.common import coininfo - from typing import List, Union + from typing import List, Optional, Union from ..writers import Writer OVERWINTERED = const(0x80000000) @@ -134,12 +134,16 @@ class Zcashlike(Bitcoinlike): self, i: int, txi: TxInput, + tx: Union[SignTx, PrevTx], + hash143: Hash143, + h_approved: HashWriter, public_keys: List[bytes], threshold: int, script_pubkey: bytes, + tx_hash: Optional[bytes] = None, ) -> bytes: - return self.hash143.preimage_hash( - txi, public_keys, threshold, self.tx, self.coin, self.get_sighash_type(txi) + return hash143.preimage_hash( + txi, public_keys, threshold, tx, self.coin, self.get_sighash_type(txi) ) def write_tx_header(