diff --git a/core/src/apps/bitcoin/scripts.py b/core/src/apps/bitcoin/scripts.py index 02799a9ec..1570e3664 100644 --- a/core/src/apps/bitcoin/scripts.py +++ b/core/src/apps/bitcoin/scripts.py @@ -7,6 +7,7 @@ from trezor.messages.TxInputType import TxInputType from apps.common import address_type from apps.common.coininfo import CoinInfo +from apps.common.readers import BytearrayReader, read_bitcoin_varint from apps.common.writers import empty_bytearray, write_bitcoin_varint from . import common @@ -15,6 +16,7 @@ from .multisig import ( multisig_get_pubkeys, multisig_pubkey_index, ) +from .readers import read_bytes_prefixed, read_op_push from .writers import ( write_bytes_fixed, write_bytes_prefixed, @@ -23,7 +25,7 @@ from .writers import ( ) if False: - from typing import List, Optional + from typing import List, Optional, Tuple from trezor.messages.TxInputType import EnumTypeInputScriptType from .writers import Writer @@ -143,6 +145,23 @@ def input_script_p2pkh_or_p2sh( return w +def parse_input_script_p2pkh(script_sig: bytes,) -> Tuple[bytes, bytes, int]: + try: + r = BytearrayReader(script_sig) + n = read_op_push(r) + signature = r.read(n - 1) + hash_type = r.get() + + n = read_op_push(r) + pubkey = r.read() + if len(pubkey) != n: + raise ValueError + except (ValueError, IndexError): + wire.DataError("Invalid scriptSig.") + + return pubkey, signature, hash_type + + def output_script_p2pkh(pubkeyhash: bytes) -> bytearray: utils.ensure(len(pubkeyhash) == 20) s = bytearray(25) @@ -253,6 +272,27 @@ def witness_p2wpkh(signature: bytes, pubkey: bytes, sighash: int) -> bytearray: return w +def parse_witness_p2wpkh(witness: bytes) -> Tuple[bytes, bytes, int]: + try: + r = BytearrayReader(witness) + + if r.get() != 2: + # num of stack items, in P2WPKH it's always 2 + raise ValueError + + n = read_bitcoin_varint(r) + signature = r.read(n - 1) + hash_type = r.get() + + pubkey = read_bytes_prefixed(r) + if r.remaining_count(): + raise ValueError + except (ValueError, IndexError): + raise wire.DataError("Invalid witness.") + + return pubkey, signature, hash_type + + def witness_p2wsh( multisig: MultisigRedeemScriptType, signature: bytes, @@ -302,6 +342,33 @@ def witness_p2wsh( return w +def parse_witness_p2wsh(witness: bytes) -> Tuple[bytes, List[Tuple[bytes, int]]]: + try: + r = BytearrayReader(witness) + + # Get number of witness stack items. + item_count = read_bitcoin_varint(r) + + # Skip over OP_FALSE, which is due to the old OP_CHECKMULTISIG bug. + if r.get() != 0: + raise ValueError + + signatures = [] + for i in range(item_count - 2): + n = read_bitcoin_varint(r) + signature = r.read(n - 1) + hash_type = r.get() + signatures.append((signature, hash_type)) + + script = read_bytes_prefixed(r) + if r.remaining_count(): + raise ValueError + except (ValueError, IndexError): + raise wire.DataError("Invalid witness.") + + return script, signatures + + # Multisig # === # @@ -351,6 +418,33 @@ def input_script_multisig( return w +def parse_input_script_multisig( + script_sig: bytes, +) -> Tuple[bytes, List[Tuple[bytes, int]]]: + try: + r = BytearrayReader(script_sig) + + # Skip over OP_FALSE, which is due to the old OP_CHECKMULTISIG bug. + if r.get() != 0: + raise ValueError + + signatures = [] + n = read_op_push(r) + while r.remaining_count() > n: + signature = r.read(n - 1) + hash_type = r.get() + signatures.append((signature, hash_type)) + n = read_op_push(r) + + script = r.read() + if len(script) != n: + raise ValueError + except (ValueError, IndexError): + raise wire.DataError("Invalid scriptSig.") + + return script, signatures + + def output_script_multisig(pubkeys: List[bytes], m: int) -> bytearray: w = empty_bytearray(output_script_multisig_length(pubkeys, m)) write_output_script_multisig(w, pubkeys, m) @@ -376,6 +470,39 @@ def output_script_multisig_length(pubkeys: List[bytes], m: int) -> int: return 1 + len(pubkeys) * (1 + 33) + 1 + 1 # see output_script_multisig +def parse_output_script_multisig(script: bytes) -> Tuple[List[bytes], int]: + try: + r = BytearrayReader(script) + + threshold = r.get() - 0x50 + pubkey_count = script[-2] - 0x50 + + if ( + not 1 <= threshold <= 15 + or not 1 <= pubkey_count <= 15 + or threshold > pubkey_count + ): + raise ValueError + + public_keys = [] + for i in range(pubkey_count): + n = read_op_push(r) + if n != 33: + raise ValueError + public_keys.append(r.read(n)) + + r.get() # ignore pubkey_count + if r.get() != 0xAE: # OP_CHECKMULTISIG + raise ValueError + if r.remaining_count(): + raise ValueError + + except (ValueError, IndexError): + raise wire.DataError("Invalid multisig script") + + return public_keys, threshold + + # OP_RETURN # ===