diff --git a/src/apps/wallet/sign_tx/__init__.py b/src/apps/wallet/sign_tx/__init__.py index 02cc913a4..1f13deece 100644 --- a/src/apps/wallet/sign_tx/__init__.py +++ b/src/apps/wallet/sign_tx/__init__.py @@ -20,6 +20,8 @@ async def sign_tx(ctx, msg): req = signer.send(res) except signing.SigningError as e: raise wire.FailureError(*e.args) + except signing.AddressError as e: + raise wire.FailureError(*e.args) if req.__qualname__ == 'TxRequest': if req.request_type == TXFINISHED: break diff --git a/src/apps/wallet/sign_tx/addresses.py b/src/apps/wallet/sign_tx/addresses.py new file mode 100644 index 000000000..81051c0d2 --- /dev/null +++ b/src/apps/wallet/sign_tx/addresses.py @@ -0,0 +1,84 @@ +from micropython import const + +from trezor.crypto.hashlib import sha256, ripemd160 +from trezor.crypto import base58, bech32 +from trezor.utils import ensure + +from trezor.messages.CoinType import CoinType +from trezor.messages import FailureType +from trezor.messages import InputScriptType + +# supported witness version for bech32 addresses +_BECH32_WITVER = const(0x00) + + +class AddressError(Exception): + pass + + +def get_address(script_type: InputScriptType, coin: CoinType, node) -> bytes: + + if script_type == InputScriptType.SPENDADDRESS: # p2pkh + return node.address(coin.address_type) + + elif script_type == InputScriptType.SPENDWITNESS: # native p2wpkh + if not coin.segwit or not coin.bech32_prefix: + raise AddressError(FailureType.ProcessError, + 'Segwit not enabled on this coin') + return address_p2wpkh(node.public_key(), coin.bech32_prefix) + + elif script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh using p2sh + if not coin.segwit or not coin.address_type_p2sh: + raise AddressError(FailureType.ProcessError, + 'Segwit not enabled on this coin') + return address_p2wpkh_in_p2sh(node.public_key(), coin.address_type_p2sh) + + else: + raise AddressError(FailureType.ProcessError, + 'Invalid script type') + + +def address_p2wpkh_in_p2sh(pubkey: bytes, addrtype: int) -> str: + s = bytearray(21) + s[0] = addrtype + s[1:21] = address_p2wpkh_in_p2sh_raw(pubkey) + return base58.encode_check(bytes(s)) + + +def address_p2wpkh_in_p2sh_raw(pubkey: bytes) -> bytes: + s = bytearray(22) + s[0] = 0x00 # OP_0 + s[1] = 0x14 # pushing 20 bytes + s[2:22] = ecdsa_hash_pubkey(pubkey) + h = sha256(s).digest() + h = ripemd160(h).digest() + return h + + +def address_p2wpkh(pubkey: bytes, hrp: str) -> str: + pubkeyhash = ecdsa_hash_pubkey(pubkey) + address = bech32.encode(hrp, _BECH32_WITVER, pubkeyhash) + if address is None: + raise AddressError(FailureType.ProcessError, + 'Invalid address') + return address + + +def decode_bech32_address(prefix: str, address: str) -> bytes: + witver, raw = bech32.decode(prefix, address) + if witver != _BECH32_WITVER: + raise AddressError(FailureType.ProcessError, + 'Invalid address witness program') + return bytes(raw) + + +def ecdsa_hash_pubkey(pubkey: bytes) -> bytes: + if pubkey[0] == 0x04: + ensure(len(pubkey) == 65) # uncompressed format + elif pubkey[0] == 0x00: + ensure(len(pubkey) == 1) # point at infinity + else: + ensure(len(pubkey) == 33) # compresssed format + h = sha256(pubkey).digest() + h = ripemd160(h).digest() + return h diff --git a/src/apps/wallet/sign_tx/helpers.py b/src/apps/wallet/sign_tx/helpers.py index 91b003448..3574e0973 100644 --- a/src/apps/wallet/sign_tx/helpers.py +++ b/src/apps/wallet/sign_tx/helpers.py @@ -1,4 +1,3 @@ - from trezor.messages.CoinType import CoinType from trezor.messages.TxOutputType import TxOutputType from trezor.messages.TxOutputBinType import TxOutputBinType diff --git a/src/apps/wallet/sign_tx/signing.py b/src/apps/wallet/sign_tx/signing.py index 2106bf00b..1f7c5863f 100644 --- a/src/apps/wallet/sign_tx/signing.py +++ b/src/apps/wallet/sign_tx/signing.py @@ -1,9 +1,8 @@ from micropython import const -from trezor.crypto.hashlib import sha256, ripemd160 +from trezor.crypto.hashlib import sha256 from trezor.crypto.curve import secp256k1 -from trezor.crypto import base58, der, bech32 -from trezor.utils import ensure +from trezor.crypto import base58, der from trezor.messages.TxRequestSerializedType import TxRequestSerializedType from trezor.messages.TxRequestDetailsType import TxRequestDetailsType @@ -11,8 +10,9 @@ from trezor.messages import OutputScriptType from apps.common import address_type from apps.common import coins -from apps.wallet.sign_tx.segwit_bip143 import * +from apps.wallet.sign_tx.addresses import * from apps.wallet.sign_tx.helpers import * +from apps.wallet.sign_tx.segwit_bip143 import * from apps.wallet.sign_tx.scripts import * from apps.wallet.sign_tx.writers import * @@ -345,64 +345,6 @@ def get_p2wpkh_witness(signature: bytes, pubkey: bytes): return w -def get_address(script_type: InputScriptType, coin: CoinType, node) -> bytes: - - if script_type == InputScriptType.SPENDADDRESS: # p2pkh - return node.address(coin.address_type) - - elif script_type == InputScriptType.SPENDWITNESS: # native p2wpkh - if not coin.segwit or not coin.bech32_prefix: - raise SigningError(FailureType.ProcessError, - 'Segwit not enabled on this coin') - return address_p2wpkh(node.public_key(), coin.bech32_prefix) - - elif script_type == InputScriptType.SPENDP2SHWITNESS: # p2wpkh using p2sh - if not coin.segwit or not coin.address_type_p2sh: - raise SigningError(FailureType.ProcessError, - 'Segwit not enabled on this coin') - return address_p2wpkh_in_p2sh(node.public_key(), coin.address_type_p2sh) - - else: - raise SigningError(FailureType.ProcessError, 'Invalid script type') - - -def address_p2wpkh_in_p2sh(pubkey: bytes, addrtype: int) -> str: - s = bytearray(21) - s[0] = addrtype - s[1:21] = address_p2wpkh_in_p2sh_raw(pubkey) - return base58.encode_check(bytes(s)) - - -def address_p2wpkh_in_p2sh_raw(pubkey: bytes) -> bytes: - s = bytearray(22) - s[0] = 0x00 # OP_0 - s[1] = 0x14 # pushing 20 bytes - s[2:22] = ecdsa_hash_pubkey(pubkey) - h = sha256(s).digest() - h = ripemd160(h).digest() - return h - - -_BECH32_WITVER = const(0x00) - - -def address_p2wpkh(pubkey: bytes, hrp: str) -> str: - pubkeyhash = ecdsa_hash_pubkey(pubkey) - address = bech32.encode(hrp, _BECH32_WITVER, pubkeyhash) - if address is None: - raise SigningError(FailureType.ProcessError, - 'Invalid address') - return address - - -def decode_bech32_address(prefix: str, address: str) -> bytes: - witver, raw = bech32.decode(prefix, address) - if witver != _BECH32_WITVER: - raise SigningError(FailureType.DataError, - 'Invalid address witness program') - return bytes(raw) - - # TX Outputs # === @@ -505,18 +447,6 @@ def node_derive(root, address_n: list): return node -def ecdsa_hash_pubkey(pubkey: bytes) -> bytes: - if pubkey[0] == 0x04: - ensure(len(pubkey) == 65) # uncompressed format - elif pubkey[0] == 0x00: - ensure(len(pubkey) == 1) # point at infinity - else: - ensure(len(pubkey) == 33) # compresssed format - h = sha256(pubkey).digest() - h = ripemd160(h).digest() - return h - - def ecdsa_sign(node, digest: bytes) -> bytes: sig = secp256k1.sign(node.private_key(), digest) sigder = der.encode_seq((sig[1:33], sig[33:65]))