diff --git a/core/src/apps/bitcoin/sign_tx/bitcoin.py b/core/src/apps/bitcoin/sign_tx/bitcoin.py index 0defbf841..b94d64979 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoin.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoin.py @@ -15,6 +15,7 @@ from ..common import BIP32_WALLET_DEPTH, SIGHASH_ALL, ecdsa_sign from ..ownership import verify_nonownership from ..verification import SignatureVerifier from . import approvers, helpers, progress +from .hash143 import Hash143 from .matchcheck import MultisigFingerprintChecker, WalletPathChecker if False: @@ -114,19 +115,22 @@ class Bitcoin: self.h_inputs = None # type: Optional[bytes] # BIP-0143 transaction hashing - self.init_hash143() + self.hash143 = self.create_hash143() progress.init(self.tx.inputs_count, self.tx.outputs_count) def create_hash_writer(self) -> HashWriter: return HashWriter(sha256()) + def create_hash143(self) -> Hash143: + return Hash143() + async def step1_process_inputs(self) -> None: for i in range(self.tx.inputs_count): # STAGE_REQUEST_1_INPUT in legacy txi = await helpers.request_tx_input(self.tx_req, i, self.coin) - self.hash143_add_input(txi) # all inputs are included (non-segwit as well) + self.hash143.add_input(txi) # all inputs are included (non-segwit as well) writers.write_tx_input_check(self.h_approved, txi) if input_is_segwit(txi): @@ -229,7 +233,7 @@ class Bitcoin: await self.approver.add_external_output(txo, script_pubkey) self.write_tx_output(self.h_approved, txo, script_pubkey) - self.hash143_add_output(txo, script_pubkey) + self.hash143.add_output(txo, script_pubkey) async def get_tx_digest( self, @@ -240,7 +244,14 @@ class Bitcoin: script_pubkey: bytes, ) -> bytes: if txi.witness: - return self.hash143_preimage_hash(txi, public_keys, threshold) + return self.hash143.preimage_hash( + txi, + public_keys, + threshold, + self.tx, + self.coin, + self.get_sighash_type(txi), + ) else: digest, _, _ = await self.get_legacy_tx_digest(i, script_pubkey) return digest @@ -304,7 +315,9 @@ class Bitcoin: else: public_keys = [public_key] threshold = 1 - hash143_hash = self.hash143_preimage_hash(txi, public_keys, threshold) + hash143_hash = self.hash143.preimage_hash( + txi, public_keys, threshold, self.tx, self.coin, self.get_sighash_type(txi) + ) signature = ecdsa_sign(node, hash143_hash) @@ -478,16 +491,16 @@ class Bitcoin: # the fork ID value. return self.get_sighash_type(txi) & 0xFF + @staticmethod def write_tx_input( - self, w: writers.Writer, txi: Union[TxInput, PrevInput], script: bytes, ) -> None: writers.write_tx_input(w, txi, script) + @staticmethod def write_tx_output( - self, w: writers.Writer, txo: Union[TxOutput, PrevOutput], script_pubkey: bytes, @@ -574,74 +587,6 @@ class Bitcoin: signature, ) - # BIP-0143 - # === - - def init_hash143(self) -> None: - self.h_prevouts = HashWriter(sha256()) - self.h_sequence = HashWriter(sha256()) - self.h_outputs = HashWriter(sha256()) - - def hash143_add_input(self, txi: TxInput) -> None: - writers.write_bytes_reversed( - self.h_prevouts, txi.prev_hash, writers.TX_HASH_SIZE - ) - writers.write_uint32(self.h_prevouts, txi.prev_index) - writers.write_uint32(self.h_sequence, txi.sequence) - - def hash143_add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: - writers.write_tx_output(self.h_outputs, txo, script_pubkey) - - def hash143_preimage_hash( - self, txi: TxInput, public_keys: List[bytes], threshold: int - ) -> bytes: - h_preimage = HashWriter(sha256()) - - # nVersion - writers.write_uint32(h_preimage, self.tx.version) - - # hashPrevouts - prevouts_hash = writers.get_tx_hash( - self.h_prevouts, double=self.coin.sign_hash_double - ) - writers.write_bytes_fixed(h_preimage, prevouts_hash, writers.TX_HASH_SIZE) - - # hashSequence - sequence_hash = writers.get_tx_hash( - self.h_sequence, double=self.coin.sign_hash_double - ) - writers.write_bytes_fixed(h_preimage, sequence_hash, writers.TX_HASH_SIZE) - - # outpoint - writers.write_bytes_reversed(h_preimage, txi.prev_hash, writers.TX_HASH_SIZE) - writers.write_uint32(h_preimage, txi.prev_index) - - # scriptCode - script_code = scripts.bip143_derive_script_code( - txi, public_keys, threshold, self.coin - ) - writers.write_bytes_prefixed(h_preimage, script_code) - - # amount - writers.write_uint64(h_preimage, txi.amount) - - # nSequence - writers.write_uint32(h_preimage, txi.sequence) - - # hashOutputs - outputs_hash = writers.get_tx_hash( - self.h_outputs, double=self.coin.sign_hash_double - ) - writers.write_bytes_fixed(h_preimage, outputs_hash, writers.TX_HASH_SIZE) - - # nLockTime - writers.write_uint32(h_preimage, self.tx.lock_time) - - # nHashType - writers.write_uint32(h_preimage, self.get_sighash_type(txi)) - - return writers.get_tx_hash(h_preimage, double=self.coin.sign_hash_double) - def input_is_segwit(txi: TxInput) -> bool: return txi.script_type in common.SEGWIT_INPUT_SCRIPT_TYPES or ( diff --git a/core/src/apps/bitcoin/sign_tx/bitcoinlike.py b/core/src/apps/bitcoin/sign_tx/bitcoinlike.py index 472d4b5b3..2cc0bd2b9 100644 --- a/core/src/apps/bitcoin/sign_tx/bitcoinlike.py +++ b/core/src/apps/bitcoin/sign_tx/bitcoinlike.py @@ -49,7 +49,14 @@ class Bitcoinlike(Bitcoin): script_pubkey: bytes, ) -> bytes: if self.coin.force_bip143: - return self.hash143_preimage_hash(txi, public_keys, threshold) + return self.hash143.preimage_hash( + txi, + public_keys, + threshold, + self.tx, + self.coin, + self.get_sighash_type(txi), + ) else: return await super().get_tx_digest( i, txi, public_keys, threshold, script_pubkey diff --git a/core/src/apps/bitcoin/sign_tx/decred.py b/core/src/apps/bitcoin/sign_tx/decred.py index aed98829e..d2481e1b2 100644 --- a/core/src/apps/bitcoin/sign_tx/decred.py +++ b/core/src/apps/bitcoin/sign_tx/decred.py @@ -12,6 +12,7 @@ from .. import multisig, scripts, writers from ..common import ecdsa_hash_pubkey, ecdsa_sign from . import approvers, helpers, progress from .bitcoin import Bitcoin +from .hash143 import Hash143 DECRED_SERIALIZE_FULL = const(0 << 16) DECRED_SERIALIZE_NO_WITNESS = const(1 << 16) @@ -33,6 +34,17 @@ if False: from apps.common.keychain import Keychain +class DecredHash(Hash143): + def __init__(self, h_prefix: HashWriter) -> None: + self.h_prefix = h_prefix + + def add_input(self, txi: TxInput) -> None: + Decred.write_tx_input(self.h_prefix, txi, bytes()) + + def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: + Decred.write_tx_output(self.h_prefix, txo, script_pubkey) + + class Decred(Bitcoin): def __init__( self, @@ -42,21 +54,22 @@ class Decred(Bitcoin): approver: approvers.Approver, ) -> None: ensure(coin.decred) + + self.h_prefix = HashWriter(blake256()) + writers.write_uint32(self.h_prefix, tx.version | DECRED_SERIALIZE_NO_WITNESS) + write_bitcoin_varint(self.h_prefix, tx.inputs_count) + super().__init__(tx, keychain, coin, approver) self.write_tx_header(self.serialized_tx, self.tx, witness_marker=True) write_bitcoin_varint(self.serialized_tx, self.tx.inputs_count) - def init_hash143(self) -> None: - self.h_prefix = self.create_hash_writer() - writers.write_uint32( - self.h_prefix, self.tx.version | DECRED_SERIALIZE_NO_WITNESS - ) - write_bitcoin_varint(self.h_prefix, self.tx.inputs_count) - def create_hash_writer(self) -> HashWriter: return HashWriter(blake256()) + def create_hash143(self) -> Hash143: + return DecredHash(self.h_prefix) + async def step2_approve_outputs(self) -> None: write_bitcoin_varint(self.serialized_tx, self.tx.outputs_count) write_bitcoin_varint(self.h_prefix, self.tx.outputs_count) @@ -148,14 +161,8 @@ class Decred(Bitcoin): if txo_bin.decred_script_version != 0: raise wire.ProcessError("Cannot use utxo that has script_version != 0") - def hash143_add_input(self, txi: TxInput) -> None: - self.write_tx_input(self.h_prefix, txi, bytes()) - - def hash143_add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: - self.write_tx_output(self.h_prefix, txo, script_pubkey) - + @staticmethod def write_tx_input( - self, w: writers.Writer, txi: Union[TxInput, PrevInput], script: bytes, @@ -165,8 +172,8 @@ class Decred(Bitcoin): writers.write_uint8(w, txi.decred_tree or 0) writers.write_uint32(w, txi.sequence) + @staticmethod def write_tx_output( - self, w: writers.Writer, txo: Union[TxOutput, PrevOutput], script_pubkey: bytes, diff --git a/core/src/apps/bitcoin/sign_tx/hash143.py b/core/src/apps/bitcoin/sign_tx/hash143.py new file mode 100644 index 000000000..5822c05fd --- /dev/null +++ b/core/src/apps/bitcoin/sign_tx/hash143.py @@ -0,0 +1,85 @@ +from trezor.crypto.hashlib import sha256 +from trezor.messages.PrevTx import PrevTx +from trezor.messages.SignTx import SignTx +from trezor.messages.TxInput import TxInput +from trezor.messages.TxOutput import TxOutput +from trezor.utils import HashWriter + +from apps.common import coininfo + +from .. import scripts, writers + +if False: + from typing import List, Union + + +# BIP-0143 hash +class Hash143: + def __init__(self) -> None: + self.h_prevouts = HashWriter(sha256()) + self.h_sequence = HashWriter(sha256()) + self.h_outputs = HashWriter(sha256()) + + def add_input(self, txi: TxInput) -> None: + writers.write_bytes_reversed( + self.h_prevouts, txi.prev_hash, writers.TX_HASH_SIZE + ) + writers.write_uint32(self.h_prevouts, txi.prev_index) + writers.write_uint32(self.h_sequence, txi.sequence) + + def add_output(self, txo: TxOutput, script_pubkey: bytes) -> None: + writers.write_tx_output(self.h_outputs, txo, script_pubkey) + + def preimage_hash( + self, + txi: TxInput, + public_keys: List[bytes], + threshold: int, + tx: Union[SignTx, PrevTx], + coin: coininfo.CoinInfo, + sighash_type: int, + ) -> bytes: + h_preimage = HashWriter(sha256()) + + # nVersion + writers.write_uint32(h_preimage, tx.version) + + # hashPrevouts + prevouts_hash = writers.get_tx_hash( + self.h_prevouts, double=coin.sign_hash_double + ) + writers.write_bytes_fixed(h_preimage, prevouts_hash, writers.TX_HASH_SIZE) + + # hashSequence + sequence_hash = writers.get_tx_hash( + self.h_sequence, double=coin.sign_hash_double + ) + writers.write_bytes_fixed(h_preimage, sequence_hash, writers.TX_HASH_SIZE) + + # outpoint + writers.write_bytes_reversed(h_preimage, txi.prev_hash, writers.TX_HASH_SIZE) + writers.write_uint32(h_preimage, txi.prev_index) + + # scriptCode + script_code = scripts.bip143_derive_script_code( + txi, public_keys, threshold, coin + ) + writers.write_bytes_prefixed(h_preimage, script_code) + + # amount + writers.write_uint64(h_preimage, txi.amount) + + # nSequence + writers.write_uint32(h_preimage, txi.sequence) + + # hashOutputs + outputs_hash = writers.get_tx_hash(self.h_outputs, double=coin.sign_hash_double) + writers.write_bytes_fixed(h_preimage, outputs_hash, writers.TX_HASH_SIZE) + + # nLockTime + writers.write_uint32(h_preimage, tx.lock_time) + + # nHashType + writers.write_uint32(h_preimage, sighash_type) + + return writers.get_tx_hash(h_preimage, double=coin.sign_hash_double) diff --git a/core/src/apps/bitcoin/sign_tx/zcash.py b/core/src/apps/bitcoin/sign_tx/zcash.py index 45fdd0e08..8af194a3c 100644 --- a/core/src/apps/bitcoin/sign_tx/zcash.py +++ b/core/src/apps/bitcoin/sign_tx/zcash.py @@ -26,14 +26,80 @@ from ..writers import ( ) from . import approvers, helpers from .bitcoinlike import Bitcoinlike +from .hash143 import Hash143 if False: + from apps.common import coininfo from typing import List, Union from ..writers import Writer OVERWINTERED = const(0x80000000) +class Zip243Hash(Hash143): + def __init__(self) -> None: + self.h_prevouts = HashWriter(blake2b(outlen=32, personal=b"ZcashPrevoutHash")) + self.h_sequence = HashWriter(blake2b(outlen=32, personal=b"ZcashSequencHash")) + self.h_outputs = HashWriter(blake2b(outlen=32, personal=b"ZcashOutputsHash")) + + def preimage_hash( + self, + txi: TxInput, + public_keys: List[bytes], + threshold: int, + tx: Union[SignTx, PrevTx], + coin: coininfo.CoinInfo, + sighash_type: int, + ) -> bytes: + h_preimage = HashWriter( + blake2b( + outlen=32, + personal=b"ZcashSigHash" + struct.pack(" Hash143: + return Zip243Hash() + async def step7_finish(self) -> None: self.write_tx_footer(self.serialized_tx, self.tx) @@ -69,7 +138,9 @@ class Zcashlike(Bitcoinlike): threshold: int, script_pubkey: bytes, ) -> bytes: - return self.hash143_preimage_hash(txi, public_keys, threshold) + return self.hash143.preimage_hash( + txi, public_keys, threshold, self.tx, self.coin, self.get_sighash_type(txi) + ) def write_tx_header( self, w: Writer, tx: Union[SignTx, PrevTx], witness_marker: bool @@ -90,70 +161,9 @@ class Zcashlike(Bitcoinlike): if tx.version >= 3: write_uint32(w, tx.expiry) # expiryHeight - # ZIP-0143 / ZIP-0243 - # === - - def init_hash143(self) -> None: - self.h_prevouts = HashWriter(blake2b(outlen=32, personal=b"ZcashPrevoutHash")) - self.h_sequence = HashWriter(blake2b(outlen=32, personal=b"ZcashSequencHash")) - self.h_outputs = HashWriter(blake2b(outlen=32, personal=b"ZcashOutputsHash")) - - def hash143_preimage_hash( - self, txi: TxInput, public_keys: List[bytes], threshold: int - ) -> bytes: - h_preimage = HashWriter( - blake2b( - outlen=32, - personal=b"ZcashSigHash" + struct.pack("