From 603b201ad07eccd24d7126666aa61a0d68f546d1 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 18 Apr 2018 13:47:20 +0200 Subject: [PATCH 01/29] tests: create tests.support module for things that should not be part of trezorlib but tests use them --- trezorlib/tests/support/__init__.py | 0 trezorlib/{ => tests/support}/ckd_public.py | 10 +++++----- trezorlib/{ => tests/support}/ed25519cosi.py | 2 +- trezorlib/{ => tests/support}/ed25519raw.py | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) create mode 100644 trezorlib/tests/support/__init__.py rename trezorlib/{ => tests/support}/ckd_public.py (96%) rename trezorlib/{ => tests/support}/ed25519cosi.py (96%) rename trezorlib/{ => tests/support}/ed25519raw.py (99%) diff --git a/trezorlib/tests/support/__init__.py b/trezorlib/tests/support/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/trezorlib/ckd_public.py b/trezorlib/tests/support/ckd_public.py similarity index 96% rename from trezorlib/ckd_public.py rename to trezorlib/tests/support/ckd_public.py index 9b187880c3..e1ee5d8fa4 100644 --- a/trezorlib/ckd_public.py +++ b/trezorlib/tests/support/ckd_public.py @@ -27,8 +27,8 @@ from ecdsa.util import string_to_number, number_to_string from ecdsa.curves import SECP256k1 from ecdsa.ellipticcurve import Point, INFINITY -from . import tools -from . import messages as proto +from trezorlib import tools +from trezorlib import messages PRIME_DERIVATION_FLAG = 0x80000000 @@ -76,7 +76,7 @@ def public_ckd(public_node, n): if not isinstance(n, list): raise ValueError('Parameter must be a list') - node = proto.HDNodeType() + node = messages.HDNodeType() node.CopyFrom(public_node) for i in n: @@ -98,7 +98,7 @@ def get_subnode(node, i): I64 = hmac.HMAC(key=node.chain_code, msg=data, digestmod=hashlib.sha512).digest() I_left_as_exponent = string_to_number(I64[:32]) - node_out = proto.HDNodeType() + node_out = messages.HDNodeType() node_out.depth = node.depth + 1 node_out.child_num = i node_out.chain_code = I64[32:] @@ -138,7 +138,7 @@ def deserialize(xpub): if tools.Hash(data[:-4])[:4] != data[-4:]: raise ValueError("Checksum failed") - node = proto.HDNodeType() + node = messages.HDNodeType() node.depth = struct.unpack('>B', data[4:5])[0] node.fingerprint = struct.unpack('>I', data[5:9])[0] node.child_num = struct.unpack('>I', data[9:13])[0] diff --git a/trezorlib/ed25519cosi.py b/trezorlib/tests/support/ed25519cosi.py similarity index 96% rename from trezorlib/ed25519cosi.py rename to trezorlib/tests/support/ed25519cosi.py index 530718a109..645dc29b5d 100644 --- a/trezorlib/ed25519cosi.py +++ b/trezorlib/tests/support/ed25519cosi.py @@ -70,7 +70,7 @@ def self_test(digest): R = commits[i] h = ed25519raw.H(seckey) b = ed25519raw.b - a = 2**(b - 2) + sum(2**i * ed25519raw.bit(h, i) for i in range(3, b - 2)) + a = 2**(b - 2) + sum(2 ** i * ed25519raw.bit(h, i) for i in range(3, b - 2)) S = (r + ed25519raw.Hint(global_R + global_pk + digest) * a) % ed25519raw.l print('Local sig %d: %s' % (i + 1, to_hex(ed25519raw.encodeint(S)))) sigs.append(ed25519raw.encodeint(S)) diff --git a/trezorlib/ed25519raw.py b/trezorlib/tests/support/ed25519raw.py similarity index 99% rename from trezorlib/ed25519raw.py rename to trezorlib/tests/support/ed25519raw.py index 81476396e9..5bfa47bffa 100644 --- a/trezorlib/ed25519raw.py +++ b/trezorlib/tests/support/ed25519raw.py @@ -1,7 +1,6 @@ # orignal version downloaded from https://ed25519.cr.yp.to/python/ed25519.py # modified for Python 3 by Jochen Hoenicke -import sys import hashlib b = 256 From 4f66b37f25c4a61037563780fc38108ecc343984 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 18 Apr 2018 14:00:11 +0200 Subject: [PATCH 02/29] tests: update imports after tests.support move --- trezorlib/tests/device_tests/test_cosi.py | 7 ++++--- trezorlib/tests/device_tests/test_msg_getaddress.py | 6 ++++-- .../tests/device_tests/test_msg_getaddress_segwit.py | 4 ++-- .../device_tests/test_msg_getaddress_segwit_native.py | 4 ++-- trezorlib/tests/device_tests/test_msg_getaddress_show.py | 4 ++-- trezorlib/tests/device_tests/test_msg_getpublickey.py | 4 ++-- .../tests/device_tests/test_msg_getpublickey_curve.py | 3 +-- trezorlib/tests/device_tests/test_msg_signtx_bcash.py | 8 +++++--- .../tests/device_tests/test_msg_signtx_bitcoin_gold.py | 8 +++++--- trezorlib/tests/device_tests/test_msg_signtx_segwit.py | 6 +++--- .../tests/device_tests/test_msg_signtx_segwit_native.py | 6 ++++-- trezorlib/tests/device_tests/test_multisig.py | 7 +++---- trezorlib/tests/device_tests/test_multisig_change.py | 6 ++++-- trezorlib/tests/unit_tests/test_ckd_public.py | 2 +- 14 files changed, 42 insertions(+), 33 deletions(-) diff --git a/trezorlib/tests/device_tests/test_cosi.py b/trezorlib/tests/device_tests/test_cosi.py index d5d9c1790d..08c60bdc93 100644 --- a/trezorlib/tests/device_tests/test_cosi.py +++ b/trezorlib/tests/device_tests/test_cosi.py @@ -16,10 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * - +import pytest from hashlib import sha256 -from trezorlib import ed25519raw, ed25519cosi + +from .common import TrezorTest +from ..support import ed25519cosi, ed25519raw @pytest.mark.skip_t2 diff --git a/trezorlib/tests/device_tests/test_msg_getaddress.py b/trezorlib/tests/device_tests/test_msg_getaddress.py index e850e0393b..a8e8936de8 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress.py @@ -16,9 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +import pytest + +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto -import trezorlib.ckd_public as bip32 class TestMsgGetaddress(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py b/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py index 8d34304a0f..b4fcb67610 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.ckd_public as bip32 +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py b/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py index d0c5cf01fd..4bdb6d3a13 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py @@ -15,8 +15,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.ckd_public as bip32 +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_getaddress_show.py b/trezorlib/tests/device_tests/test_msg_getaddress_show.py index 5ea4f45a36..ba9411ceb8 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress_show.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress_show.py @@ -16,8 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.ckd_public as bip32 +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_getpublickey.py b/trezorlib/tests/device_tests/test_msg_getpublickey.py index 77e2547920..5047cd74ca 100644 --- a/trezorlib/tests/device_tests/test_msg_getpublickey.py +++ b/trezorlib/tests/device_tests/test_msg_getpublickey.py @@ -16,8 +16,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.ckd_public as bip32 +from .common import TrezorTest +from ..support import ckd_public as bip32 class TestMsgGetpublickey(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py b/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py index 5acf7e9568..f2130eed07 100644 --- a/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py +++ b/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py @@ -16,8 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * -import trezorlib.ckd_public as bip32 +from .common import TrezorTest from trezorlib.client import CallException diff --git a/trezorlib/tests/device_tests/test_msg_signtx_bcash.py b/trezorlib/tests/device_tests/test_msg_signtx_bcash.py index f397d5b6ad..9a6d329749 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_bcash.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_bcash.py @@ -15,10 +15,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +import pytest +from binascii import hexlify, unhexlify + +from .common import TrezorTest +from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto -from trezorlib.ckd_public import deserialize from trezorlib.client import CallException TxApiBcash = coins.tx_api['Bcash'] @@ -256,7 +259,6 @@ class TestMsgSigntxBch(TrezorTest): attack_ctr = 0 def attack_processor(req, msg): - import sys global attack_ctr if req.details.tx_hash is not None: diff --git a/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py b/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py index 99b5082c00..846f029ded 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py @@ -16,10 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +import pytest +from binascii import hexlify, unhexlify + +from .common import TrezorTest +from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto -from trezorlib.ckd_public import deserialize from trezorlib.client import CallException TxApiBitcoinGold = coins.tx_api["Bitcoin Gold"] @@ -129,7 +132,6 @@ class TestMsgSigntxBitcoinGold(TrezorTest): attack_ctr = 0 def attack_processor(req, msg): - import sys global attack_ctr if req.details.tx_hash is not None: diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py index 65136c3a3f..9064a6d2eb 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py @@ -15,11 +15,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify +from .common import TrezorTest +from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto -from trezorlib.ckd_public import deserialize from trezorlib.client import CallException TxApiTestnet = coins.tx_api["Testnet"] @@ -186,7 +187,6 @@ class TestMsgSigntxSegwit(TrezorTest): run_attack = True def attack_processor(req, msg): - import sys global run_attack if req.details.tx_hash is not None: diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py index f82f93ff82..18bf144474 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py @@ -15,11 +15,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify +from .common import TrezorTest +from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto -from trezorlib.ckd_public import deserialize +from trezorlib.client import CallException TxApiTestnet = coins.tx_api['Testnet'] diff --git a/trezorlib/tests/device_tests/test_multisig.py b/trezorlib/tests/device_tests/test_multisig.py index 4316a9a71d..24446835ec 100644 --- a/trezorlib/tests/device_tests/test_multisig.py +++ b/trezorlib/tests/device_tests/test_multisig.py @@ -16,14 +16,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function +from binascii import hexlify, unhexlify -from .common import * +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto -import trezorlib.ckd_public as bip32 from trezorlib.client import CallException - TXHASH_c6091a = unhexlify('c6091adf4c0c23982a35899a6e58ae11e703eacd7954f588ed4b9cdefc4dba52') diff --git a/trezorlib/tests/device_tests/test_multisig_change.py b/trezorlib/tests/device_tests/test_multisig_change.py index 2d2d1c4dd2..0086d353cc 100644 --- a/trezorlib/tests/device_tests/test_multisig_change.py +++ b/trezorlib/tests/device_tests/test_multisig_change.py @@ -16,9 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify + +from .common import TrezorTest +from ..support import ckd_public as bip32 from trezorlib import messages as proto -import trezorlib.ckd_public as bip32 from trezorlib.coins import tx_api diff --git a/trezorlib/tests/unit_tests/test_ckd_public.py b/trezorlib/tests/unit_tests/test_ckd_public.py index d7f36fb3d0..b6488a7738 100644 --- a/trezorlib/tests/unit_tests/test_ckd_public.py +++ b/trezorlib/tests/unit_tests/test_ckd_public.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from trezorlib import ckd_public +from ..support import ckd_public def test_ckd_public(): From d10686906167a65ee7a300f7bcf8f716a908bf7d Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 18 Apr 2018 15:00:59 +0200 Subject: [PATCH 03/29] trezorlib: deprecate client.expand_path and move the staticmethod to an ordinary function tools.parse_path Also remove PRIME_DERIVATION_FLAG and move it to tools.HARDENED_FLAG --- trezorctl | 47 ++++++++++++----------- trezorlib/client.py | 41 +++----------------- trezorlib/tests/support/ckd_public.py | 4 +- trezorlib/tools.py | 54 ++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 65 deletions(-) diff --git a/trezorctl b/trezorctl index edb15fb23d..1fc7394cc4 100755 --- a/trezorctl +++ b/trezorctl @@ -23,18 +23,17 @@ import base64 import binascii import click -import functools import json import os import sys from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException, format_protobuf -from trezorlib.transport import get_transport, enumerate_devices, TransportException +from trezorlib.transport import get_transport, enumerate_devices from trezorlib import coins from trezorlib import messages as proto from trezorlib import protobuf -from trezorlib.ckd_public import PRIME_DERIVATION_FLAG from trezorlib import stellar +from trezorlib import tools class ChoiceType(click.Choice): @@ -449,7 +448,7 @@ def self_test(connect): @click.pass_obj def get_address(connect, coin, address, script_type, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.get_address(coin, address_n, show_display, script_type=script_type) @@ -461,7 +460,7 @@ def get_address(connect, coin, address, script_type, show_display): @click.pass_obj def get_public_node(connect, coin, address, curve, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) result = client.get_public_node(address_n, ecdsa_curve_name=curve, show_display=show_display, coin_name=coin) return { 'node': { @@ -502,7 +501,7 @@ def sign_tx(connect, coin): if address_n is None: pass - elif address_n[0] == (49 | PRIME_DERIVATION_FLAG): + elif address_n[0] == tools.H_(49): script_type = 'p2shsegwit' return script_type @@ -518,7 +517,7 @@ def sign_tx(connect, coin): if not prev: break prev_hash, prev_index = prev - address_n = click.prompt('BIP-32 path to derive the key', type=client.expand_path) + address_n = click.prompt('BIP-32 path to derive the key', type=tools.parse_path) amount = click.prompt('Input amount (satoshis)', type=int, default=0) sequence = click.prompt('Sequence Number to use (RBF opt-in enabled by default)', type=int, default=0xfffffffd) script_type = click.prompt('Input type', type=CHOICE_INPUT_SCRIPT_TYPE, default=default_script_type(address_n)) @@ -540,7 +539,7 @@ def sign_tx(connect, coin): address_n = None else: address = None - address_n = click.prompt('BIP-32 path (for change output)', type=client.expand_path, default='') + address_n = click.prompt('BIP-32 path (for change output)', type=tools.parse_path, default='') if not address_n: break amount = click.prompt('Amount to spend (satoshis)', type=int) @@ -581,7 +580,7 @@ def sign_tx(connect, coin): @click.pass_obj def sign_message(connect, coin, address, message, script_type): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) typemap = { 'address': proto.InputScriptType.SPENDADDRESS, 'segwit': proto.InputScriptType.SPENDWITNESS, @@ -613,7 +612,7 @@ def verify_message(connect, coin, address, signature, message): @click.pass_obj def ethereum_sign_message(connect, address, message): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) ret = client.ethereum_sign_message(address_n, message) output = { 'message': message, @@ -648,7 +647,7 @@ def ethereum_verify_message(connect, address, signature, message): @click.pass_obj def encrypt_keyvalue(connect, address, key, value): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) res = client.encrypt_keyvalue(address_n, key, value.encode()) return binascii.hexlify(res) @@ -660,7 +659,7 @@ def encrypt_keyvalue(connect, address, key, value): @click.pass_obj def decrypt_keyvalue(connect, address, key, value): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.decrypt_keyvalue(address_n, key, binascii.unhexlify(value)) @@ -674,7 +673,7 @@ def decrypt_keyvalue(connect, address, key, value): def encrypt_message(connect, coin, display_only, address, pubkey, message): client = connect() pubkey = binascii.unhexlify(pubkey) - address_n = client.expand_path(address) + address_n = tools.parse_path(address) res = client.encrypt_message(pubkey, message, display_only, coin, address_n) return { 'nonce': binascii.hexlify(res.nonce), @@ -690,7 +689,7 @@ def encrypt_message(connect, coin, display_only, address, pubkey, message): @click.pass_obj def decrypt_message(connect, address, payload): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) payload = base64.b64decode(payload) nonce, message, msg_hmac = payload[:33], payload[33:-8], payload[-8:] return client.decrypt_message(address_n, nonce, message, msg_hmac) @@ -707,7 +706,7 @@ def decrypt_message(connect, address, payload): @click.pass_obj def ethereum_get_address(connect, address, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) address = client.ethereum_get_address(address_n, show_display) return '0x%s' % binascii.hexlify(address).decode() @@ -774,7 +773,7 @@ def ethereum_sign_tx(connect, host, chain_id, address, value, gas_limit, gas_pri to_address = ethereum_decode_hex(to) client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) address = '0x%s' % (binascii.hexlify(client.ethereum_get_address(address_n)).decode()) if gas_price is None or gas_limit is None or nonce is None or publish: @@ -836,7 +835,7 @@ def ethereum_sign_tx(connect, host, chain_id, address, value, gas_limit, gas_pri @click.pass_obj def nem_get_address(connect, address, network, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.nem_get_address(address_n, network, show_display) @@ -847,7 +846,7 @@ def nem_get_address(connect, address, network, show_display): @click.pass_obj def nem_sign_tx(connect, address, file, broadcast): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) transaction = client.nem_sign_tx(address_n, json.load(file)) payload = { @@ -873,7 +872,7 @@ def nem_sign_tx(connect, address, file, broadcast): @click.pass_obj def lisk_get_address(connect, address, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.lisk_get_address(address_n, show_display) @@ -883,7 +882,7 @@ def lisk_get_address(connect, address, show_display): @click.pass_obj def lisk_get_public_key(connect, address, show_display): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) res = client.lisk_get_public_key(address_n, show_display) output = { "public_key": binascii.hexlify(res.public_key).decode() @@ -897,7 +896,7 @@ def lisk_get_public_key(connect, address, show_display): @click.pass_obj def lisk_sign_message(connect, address, message): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) res = client.lisk_sign_message(address_n, message) output = { 'message': message, @@ -925,7 +924,7 @@ def lisk_verify_message(connect, pubkey, signature, message): @click.pass_obj def lisk_sign_tx(connect, address, file): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) transaction = client.lisk_sign_tx(address_n, json.load(file)) payload = { @@ -946,7 +945,7 @@ def lisk_sign_tx(connect, address, file): @click.pass_obj def cosi_commit(connect, address, data): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.cosi_commit(address_n, binascii.unhexlify(data)) @@ -958,7 +957,7 @@ def cosi_commit(connect, address, data): @click.pass_obj def cosi_sign(connect, address, data, global_commitment, global_pubkey): client = connect() - address_n = client.expand_path(address) + address_n = tools.parse_path(address) return client.cosi_sign(address_n, binascii.unhexlify(data), binascii.unhexlify(global_commitment), binascii.unhexlify(global_pubkey)) diff --git a/trezorlib/client.py b/trezorlib/client.py index 0479cf10bb..eedc6225c4 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -34,7 +34,6 @@ from . import messages as proto from . import tools from . import mapping from . import nem -from .coins import slip44 from . import stellar from .debuglink import DebugLink from .protobuf import MessageType @@ -490,7 +489,6 @@ class DebugLinkMixin(object): class ProtocolMixin(object): - PRIME_DERIVATION_FLAG = 0x80000000 VENDORS = ('bitcointrezor.com', 'trezor.io') def __init__(self, state=None, *args, **kwargs): @@ -513,44 +511,15 @@ class ProtocolMixin(object): def _get_local_entropy(self): return os.urandom(32) - def _convert_prime(self, n): + @staticmethod + def _convert_prime(n: tools.Address) -> tools.Address: # Convert minus signs to uint32 with flag - return [int(abs(x) | self.PRIME_DERIVATION_FLAG) if x < 0 else x for x in n] + return [tools.H_(int(abs(x))) if x < 0 else x for x in n] @staticmethod def expand_path(n): - # Convert string of bip32 path to list of uint32 integers with prime flags - # 0/-1/1' -> [0, 0x80000001, 0x80000001] - if not n: - return [] - - n = n.split('/') - - # m/a/b/c => a/b/c - if n[0] == 'm': - n = n[1:] - - # coin_name/a/b/c => 44'/SLIP44_constant'/a/b/c - if n[0] in slip44: - n = ["44'", "%d'" % slip44[n[0]]] + n[1:] - - path = [] - for x in n: - prime = False - if x.endswith("'"): - x = x.replace('\'', '') - prime = True - if x.startswith('-'): - prime = True - - x = abs(int(x)) - - if prime: - x |= ProtocolMixin.PRIME_DERIVATION_FLAG - - path.append(x) - - return path + warnings.warn('expand_path is deprecated, use tools.parse_path', DeprecationWarning) + return tools.parse_path(n) @expect(proto.PublicKey) def get_public_node(self, n, ecdsa_curve_name=None, show_display=False, coin_name=None): diff --git a/trezorlib/tests/support/ckd_public.py b/trezorlib/tests/support/ckd_public.py index e1ee5d8fa4..e377cf05a9 100644 --- a/trezorlib/tests/support/ckd_public.py +++ b/trezorlib/tests/support/ckd_public.py @@ -30,8 +30,6 @@ from ecdsa.ellipticcurve import Point, INFINITY from trezorlib import tools from trezorlib import messages -PRIME_DERIVATION_FLAG = 0x80000000 - def point_to_pubkey(point): order = SECP256k1.order @@ -61,7 +59,7 @@ def sec_to_public_pair(pubkey): def is_prime(n): - return (bool)(n & PRIME_DERIVATION_FLAG) + return bool(n & tools.HARDENED_FLAG) def fingerprint(pubkey): diff --git a/trezorlib/tools.py b/trezorlib/tools.py index 56e409dceb..8029cb1cbf 100644 --- a/trezorlib/tools.py +++ b/trezorlib/tools.py @@ -18,9 +18,21 @@ # along with this library. If not, see . import hashlib -import binascii import struct -import sys +from typing import NewType, List + +from .coins import slip44 + +HARDENED_FLAG = 1 << 31 + +Address = NewType('Address', List[int]) + + +def H_(x: int) -> int: + """ + Shortcut function that "hardens" a number in a BIP44 path. + """ + return x | HARDENED_FLAG def Hash(data): @@ -109,3 +121,41 @@ def b58decode(v, length): return None return result + + +def parse_path(nstr: str) -> Address: + """ + Convert BIP32 path string to list of uint32 integers with hardened flags. + Several conventions are supported to set the hardened flag: -1, 1', 1h + + e.g.: "0/1h/1" -> [0, 0x80000001, 1] + + :param nstr: path string + :return: list of integers + """ + if not nstr: + return [] + + n = nstr.split('/') + + # m/a/b/c => a/b/c + if n[0] == 'm': + n = n[1:] + + # coin_name/a/b/c => 44'/SLIP44_constant'/a/b/c + if n[0] in slip44: + coin_id = slip44[n[0]] + n[0:1] = ['44h', '{}h'.format(coin_id)] + + def str_to_harden(x: str) -> int: + if x.startswith('-'): + return H_(abs(int(x))) + elif x.endswith(('h', "'")): + return H_(int(x[:-1])) + else: + return int(x) + + try: + return list(str_to_harden(x) for x in n) + except Exception: + raise ValueError('Invalid BIP32 path', nstr) From 579adb187113f232e296374a4b7e75833d041fa2 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 18 Apr 2018 15:53:40 +0200 Subject: [PATCH 04/29] tests: update to use parse_path --- trezorlib/tests/device_tests/test_cosi.py | 26 +++++++------ .../tests/device_tests/test_msg_getaddress.py | 14 ++++--- .../test_msg_getaddress_segwit.py | 13 ++++--- .../test_msg_getaddress_segwit_native.py | 15 ++++---- .../test_msg_getpublickey_curve.py | 3 ++ .../device_tests/test_msg_lisk_signtx.py | 13 ++++--- .../device_tests/test_msg_nem_getaddress.py | 9 +++-- .../test_msg_nem_signtx_mosaics.py | 14 ++++--- .../test_msg_nem_signtx_mosaics_t2.py | 11 ++++-- .../test_msg_nem_signtx_multisig.py | 16 +++++--- .../test_msg_nem_signtx_others.py | 10 +++-- .../test_msg_nem_signtx_transfers.py | 14 ++++--- .../tests/device_tests/test_msg_signtx.py | 27 +++++++------ .../device_tests/test_msg_signtx_bcash.py | 36 +++++++++--------- .../test_msg_signtx_bitcoin_gold.py | 31 +++++++-------- .../device_tests/test_msg_signtx_decred.py | 20 ++++++---- .../device_tests/test_msg_signtx_segwit.py | 15 ++++---- .../test_msg_signtx_segwit_native.py | 38 +++++++++---------- .../device_tests/test_multisig_change.py | 5 ++- 19 files changed, 187 insertions(+), 143 deletions(-) diff --git a/trezorlib/tests/device_tests/test_cosi.py b/trezorlib/tests/device_tests/test_cosi.py index 08c60bdc93..33e7ff9afa 100644 --- a/trezorlib/tests/device_tests/test_cosi.py +++ b/trezorlib/tests/device_tests/test_cosi.py @@ -22,6 +22,8 @@ from hashlib import sha256 from .common import TrezorTest from ..support import ed25519cosi, ed25519raw +from trezorlib.tools import parse_path + @pytest.mark.skip_t2 class TestCosi(TrezorTest): @@ -31,9 +33,9 @@ class TestCosi(TrezorTest): digest = sha256(b'this is a message').digest() - c0 = self.client.cosi_commit(self.client.expand_path("10018'/0'"), digest) - c1 = self.client.cosi_commit(self.client.expand_path("10018'/1'"), digest) - c2 = self.client.cosi_commit(self.client.expand_path("10018'/2'"), digest) + c0 = self.client.cosi_commit(parse_path("10018'/0'"), digest) + c1 = self.client.cosi_commit(parse_path("10018'/1'"), digest) + c2 = self.client.cosi_commit(parse_path("10018'/2'"), digest) assert c0.pubkey != c1.pubkey assert c0.pubkey != c2.pubkey @@ -45,9 +47,9 @@ class TestCosi(TrezorTest): digestb = sha256(b'this is a different message').digest() - c0b = self.client.cosi_commit(self.client.expand_path("10018'/0'"), digestb) - c1b = self.client.cosi_commit(self.client.expand_path("10018'/1'"), digestb) - c2b = self.client.cosi_commit(self.client.expand_path("10018'/2'"), digestb) + c0b = self.client.cosi_commit(parse_path("10018'/0'"), digestb) + c1b = self.client.cosi_commit(parse_path("10018'/1'"), digestb) + c2b = self.client.cosi_commit(parse_path("10018'/2'"), digestb) assert c0.pubkey == c0b.pubkey assert c1.pubkey == c1b.pubkey @@ -62,16 +64,16 @@ class TestCosi(TrezorTest): digest = sha256(b'this is a message').digest() - c0 = self.client.cosi_commit(self.client.expand_path("10018'/0'"), digest) - c1 = self.client.cosi_commit(self.client.expand_path("10018'/1'"), digest) - c2 = self.client.cosi_commit(self.client.expand_path("10018'/2'"), digest) + c0 = self.client.cosi_commit(parse_path("10018'/0'"), digest) + c1 = self.client.cosi_commit(parse_path("10018'/1'"), digest) + c2 = self.client.cosi_commit(parse_path("10018'/2'"), digest) global_pk = ed25519cosi.combine_keys([c0.pubkey, c1.pubkey, c2.pubkey]) global_R = ed25519cosi.combine_keys([c0.commitment, c1.commitment, c2.commitment]) - sig0 = self.client.cosi_sign(self.client.expand_path("10018'/0'"), digest, global_R, global_pk) - sig1 = self.client.cosi_sign(self.client.expand_path("10018'/1'"), digest, global_R, global_pk) - sig2 = self.client.cosi_sign(self.client.expand_path("10018'/2'"), digest, global_R, global_pk) + sig0 = self.client.cosi_sign(parse_path("10018'/0'"), digest, global_R, global_pk) + sig1 = self.client.cosi_sign(parse_path("10018'/1'"), digest, global_R, global_pk) + sig2 = self.client.cosi_sign(parse_path("10018'/2'"), digest, global_R, global_pk) sig = ed25519cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature]) diff --git a/trezorlib/tests/device_tests/test_msg_getaddress.py b/trezorlib/tests/device_tests/test_msg_getaddress.py index a8e8936de8..a332dcb0c8 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress.py @@ -22,6 +22,8 @@ from .common import TrezorTest from ..support import ckd_public as bip32 from trezorlib import messages as proto +from trezorlib.tools import parse_path + class TestMsgGetaddress(TrezorTest): @@ -48,15 +50,15 @@ class TestMsgGetaddress(TrezorTest): @pytest.mark.skip_t2 def test_bch(self): self.setup_mnemonic_allallall() - assert self.client.get_address('Bcash', self.client.expand_path("44'/145'/0'/0/0")) == 'bitcoincash:qr08q88p9etk89wgv05nwlrkm4l0urz4cyl36hh9sv' - assert self.client.get_address('Bcash', self.client.expand_path("44'/145'/0'/0/1")) == 'bitcoincash:qr23ajjfd9wd73l87j642puf8cad20lfmqdgwvpat4' - assert self.client.get_address('Bcash', self.client.expand_path("44'/145'/0'/1/0")) == 'bitcoincash:qzc5q87w069lzg7g3gzx0c8dz83mn7l02scej5aluw' + assert self.client.get_address('Bcash', parse_path("44'/145'/0'/0/0")) == 'bitcoincash:qr08q88p9etk89wgv05nwlrkm4l0urz4cyl36hh9sv' + assert self.client.get_address('Bcash', parse_path("44'/145'/0'/0/1")) == 'bitcoincash:qr23ajjfd9wd73l87j642puf8cad20lfmqdgwvpat4' + assert self.client.get_address('Bcash', parse_path("44'/145'/0'/1/0")) == 'bitcoincash:qzc5q87w069lzg7g3gzx0c8dz83mn7l02scej5aluw' @pytest.mark.skip_t2 def test_bch_multisig(self): self.setup_mnemonic_allallall() xpubs = [] - for n in map(lambda index: self.client.get_public_node(self.client.expand_path("44'/145'/" + str(index) + "'")), range(1, 4)): + for n in map(lambda index: self.client.get_public_node(parse_path("44'/145'/" + str(index) + "'")), range(1, 4)): xpubs.append(n.xpub) def getmultisig(chain, nr, signatures=[b'', b'', b''], xpubs=xpubs): @@ -66,8 +68,8 @@ class TestMsgGetaddress(TrezorTest): m=2, ) for nr in range(1, 4): - assert self.client.get_address('Bcash', self.client.expand_path("44'/145'/" + str(nr) + "'/0/0"), show_display=(nr == 1), multisig=getmultisig(0, 0)) == 'bitcoincash:pqguz4nqq64jhr5v3kvpq4dsjrkda75hwy86gq0qzw' - assert self.client.get_address('Bcash', self.client.expand_path("44'/145'/" + str(nr) + "'/1/0"), show_display=(nr == 1), multisig=getmultisig(1, 0)) == 'bitcoincash:pp6kcpkhua7789g2vyj0qfkcux3yvje7euhyhltn0a' + assert self.client.get_address('Bcash', parse_path("44'/145'/" + str(nr) + "'/0/0"), show_display=(nr == 1), multisig=getmultisig(0, 0)) == 'bitcoincash:pqguz4nqq64jhr5v3kvpq4dsjrkda75hwy86gq0qzw' + assert self.client.get_address('Bcash', parse_path("44'/145'/" + str(nr) + "'/1/0"), show_display=(nr == 1), multisig=getmultisig(1, 0)) == 'bitcoincash:pp6kcpkhua7789g2vyj0qfkcux3yvje7euhyhltn0a' def test_public_ckd(self): self.setup_mnemonic_nopin_nopassphrase() diff --git a/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py b/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py index b4fcb67610..f0fd4c17b0 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress_segwit.py @@ -18,20 +18,21 @@ from .common import TrezorTest from ..support import ckd_public as bip32 from trezorlib import messages as proto +from trezorlib.tools import parse_path class TestMsgGetaddressSegwit(TrezorTest): def test_show_segwit(self): self.setup_mnemonic_allallall() - assert self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/1/0"), True, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX' - assert self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp' - assert self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc' - assert self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDADDRESS) == 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q' + assert self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), True, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX' + assert self.client.get_address("Testnet", parse_path("49'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N4Q5FhU2497BryFfUgbqkAJE87aKHUhXMp' + assert self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc' + assert self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDADDRESS) == 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q' def test_show_multisig_3(self): self.setup_mnemonic_allallall() - nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4)) + nodes = map(lambda index: self.client.get_public_node(parse_path("999'/1'/%d'" % index)), range(1, 4)) multisig1 = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -43,4 +44,4 @@ class TestMsgGetaddressSegwit(TrezorTest): # m=2, # ) for i in [1, 2, 3]: - assert self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/0" % i), False, multisig1, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N2MxyAfifVhb3AMagisxaj3uij8bfXqf4Y' + assert self.client.get_address("Testnet", parse_path("999'/1'/%d'/2/0" % i), False, multisig1, script_type=proto.InputScriptType.SPENDP2SHWITNESS) == '2N2MxyAfifVhb3AMagisxaj3uij8bfXqf4Y' diff --git a/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py b/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py index 4bdb6d3a13..3d54b51607 100644 --- a/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_getaddress_segwit_native.py @@ -18,20 +18,21 @@ from .common import TrezorTest from ..support import ckd_public as bip32 from trezorlib import messages as proto +from trezorlib.tools import parse_path class TestMsgGetaddressSegwitNative(TrezorTest): def test_show_segwit(self): self.setup_mnemonic_allallall() - assert self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/0/0"), True, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s' - assert self.client.get_address("Testnet", self.client.expand_path("49'/1'/0'/1/0"), False, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu' - assert self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1q54un3q39sf7e7tlfq99d6ezys7qgc62a6rxllc' - assert self.client.get_address("Testnet", self.client.expand_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDADDRESS) == 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q' + assert self.client.get_address("Testnet", parse_path("49'/1'/0'/0/0"), True, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s' + assert self.client.get_address("Testnet", parse_path("49'/1'/0'/1/0"), False, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu' + assert self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1q54un3q39sf7e7tlfq99d6ezys7qgc62a6rxllc' + assert self.client.get_address("Testnet", parse_path("44'/1'/0'/0/0"), False, None, script_type=proto.InputScriptType.SPENDADDRESS) == 'mvbu1Gdy8SUjTenqerxUaZyYjmveZvt33q' def test_show_multisig_3(self): self.setup_mnemonic_allallall() - nodes = [self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)) for index in range(1, 4)] + nodes = [self.client.get_public_node(parse_path("999'/1'/%d'" % index)) for index in range(1, 4)] multisig1 = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=bip32.deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -43,5 +44,5 @@ class TestMsgGetaddressSegwitNative(TrezorTest): m=2, ) for i in [1, 2, 3]: - assert self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/1" % i), False, multisig2, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qch62pf820spe9mlq49ns5uexfnl6jzcezp7d328fw58lj0rhlhasge9hzy' - assert self.client.get_address("Testnet", self.client.expand_path("999'/1'/%d'/2/0" % i), False, multisig1, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qr6xa5v60zyt3ry9nmfew2fk5g9y3gerkjeu6xxdz7qga5kknz2ssld9z2z' + assert self.client.get_address("Testnet", parse_path("999'/1'/%d'/2/1" % i), False, multisig2, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qch62pf820spe9mlq49ns5uexfnl6jzcezp7d328fw58lj0rhlhasge9hzy' + assert self.client.get_address("Testnet", parse_path("999'/1'/%d'/2/0" % i), False, multisig1, script_type=proto.InputScriptType.SPENDWITNESS) == 'tb1qr6xa5v60zyt3ry9nmfew2fk5g9y3gerkjeu6xxdz7qga5kknz2ssld9z2z' diff --git a/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py b/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py index f2130eed07..d2418336cd 100644 --- a/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py +++ b/trezorlib/tests/device_tests/test_msg_getpublickey_curve.py @@ -16,6 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify +import pytest + from .common import TrezorTest from trezorlib.client import CallException diff --git a/trezorlib/tests/device_tests/test_msg_lisk_signtx.py b/trezorlib/tests/device_tests/test_msg_lisk_signtx.py index a1d36385c1..35f811acf9 100644 --- a/trezorlib/tests/device_tests/test_msg_lisk_signtx.py +++ b/trezorlib/tests/device_tests/test_msg_lisk_signtx.py @@ -21,6 +21,7 @@ import pytest from .common import TrezorTest from trezorlib import messages as proto +from trezorlib.tools import parse_path PUBLIC_KEY = unhexlify('eb56d7bbb5e8ea9269405f7a8527fe126023d1db2c973cfac6f760b60ae27294') @@ -41,7 +42,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "10000000", "recipientId": "9971262264659915921L", "timestamp": 57525937, @@ -62,7 +63,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "10000000", "recipientId": "9971262264659915921L", "timestamp": 57525937, @@ -85,7 +86,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "0", "timestamp": 57525937, "type": 1, @@ -109,7 +110,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "0", "timestamp": 57525937, "type": 2, @@ -133,7 +134,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "0", "timestamp": 57525937, "type": 3, @@ -158,7 +159,7 @@ class TestMsgLiskSignTx(TrezorTest): ) ]) - self.client.lisk_sign_tx(self.client.expand_path("m/44'/134'/0'/0'"), { + self.client.lisk_sign_tx(parse_path("m/44'/134'/0'/0'"), { "amount": "0", "timestamp": 57525937, "type": 4, diff --git a/trezorlib/tests/device_tests/test_msg_nem_getaddress.py b/trezorlib/tests/device_tests/test_msg_nem_getaddress.py index 305be56a05..1e199a7637 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_getaddress.py +++ b/trezorlib/tests/device_tests/test_msg_nem_getaddress.py @@ -15,7 +15,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +import pytest + +from .common import TrezorTest +from trezorlib.tools import parse_path @pytest.mark.xfail # to be removed when nem is merged @@ -23,5 +26,5 @@ class TestMsgNEMGetaddress(TrezorTest): def test_nem_getaddress(self): self.setup_mnemonic_nopin_nopassphrase() - assert self.client.nem_get_address(self.client.expand_path("m/44'/1'/0'/0'/0'"), 0x68) == "NB3JCHVARQNGDS3UVGAJPTFE22UQFGMCQGHUBWQN" - assert self.client.nem_get_address(self.client.expand_path("m/44'/1'/0'/0'/0'"), 0x98) == "TB3JCHVARQNGDS3UVGAJPTFE22UQFGMCQHSBNBMF" + assert self.client.nem_get_address(parse_path("m/44'/1'/0'/0'/0'"), 0x68) == "NB3JCHVARQNGDS3UVGAJPTFE22UQFGMCQGHUBWQN" + assert self.client.nem_get_address(parse_path("m/44'/1'/0'/0'/0'"), 0x98) == "TB3JCHVARQNGDS3UVGAJPTFE22UQFGMCQHSBNBMF" diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics.py index e9f8836b95..dda87efb5e 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics.py @@ -15,8 +15,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +import pytest +from binascii import hexlify + +from .common import TrezorTest from trezorlib import nem +from trezorlib.tools import parse_path # assertion data from T1 @@ -26,7 +30,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): def test_nem_signtx_mosaic_supply_change(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_MOSAIC_SUPPLY_CHANGE, @@ -50,7 +54,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): def test_nem_signtx_mosaic_creation(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_MOSAIC_CREATION, @@ -77,7 +81,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): def test_nem_signtx_mosaic_creation_properties(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_MOSAIC_CREATION, @@ -121,7 +125,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): def test_nem_signtx_mosaic_creation_levy(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_MOSAIC_CREATION, diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics_t2.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics_t2.py index 9619533985..96112b07ab 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics_t2.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_mosaics_t2.py @@ -15,11 +15,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify +import pytest +import time +from .common import TrezorTest from trezorlib import messages as proto from trezorlib import nem -import time +from trezorlib.tools import parse_path # assertion data from T1 @@ -31,7 +34,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() with self.client: - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_MOSAIC_SUPPLY_CHANGE, @@ -181,7 +184,7 @@ class TestMsgNEMSignTxMosaics(TrezorTest): assert hexlify(tx.signature) == b'b87aac1ddf146d35e6a7f3451f57e2fe504ac559031e010a51261257c37bd50fcfa7b2939dd7a3203b54c4807d458475182f5d3dc135ec0d1d4a9cd42159fd0a' def _nem_sign(self, num_of_swipes, test_suite): - n = self.client.expand_path("m/44'/1'/0'/0'/0'") + n = parse_path("m/44'/1'/0'/0'/0'") n = self.client._convert_prime(n) msg = nem.create_sign_tx(test_suite) assert msg.transaction is not None diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_multisig.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_multisig.py index b41f34ad2b..bbb429dee4 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_multisig.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_multisig.py @@ -15,7 +15,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify +import pytest + +from .common import TrezorTest +from trezorlib.tools import parse_path from trezorlib import nem @@ -27,7 +31,7 @@ class TestMsgNEMSignTxMultisig(TrezorTest): def test_nem_signtx_aggregate_modification(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_AGGREGATE_MODIFICATION, @@ -51,7 +55,7 @@ class TestMsgNEMSignTxMultisig(TrezorTest): def test_nem_signtx_multisig(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 1, "fee": 10000, "type": nem.TYPE_MULTISIG, @@ -76,7 +80,7 @@ class TestMsgNEMSignTxMultisig(TrezorTest): assert hexlify(tx.data) == b'04100000010000980100000020000000edfd32f6e760648c032f9acb4b30d514265f6a5b5f8a7154f2618922b40620841027000000000000ff5f74049900000001010000010000980200000020000000c5f54ba980fcbb657dbaaa42700539b207873e134d2375efeab5f1ab52f87844983a000000000000320901002800000054414c49434532474d4133344358484437584c4a513533364e4d35554e4b5148544f524e4e54324a80841e000000000025000000010000001d000000746573745f6e656d5f7472616e73616374696f6e5f7472616e73666572' assert hexlify(tx.signature) == b'0cab2fddf2f02b5d7201675b9a71869292fe25ed33a366c7d2cbea7676fed491faaa03310079b7e17884b6ba2e3ea21c4f728d1cca8f190b8288207f6514820a' - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 150, "type": nem.TYPE_MULTISIG, @@ -104,7 +108,7 @@ class TestMsgNEMSignTxMultisig(TrezorTest): def test_nem_signtx_multisig_signer(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 333, "fee": 200, "type": nem.TYPE_MULTISIG_SIGNATURE, @@ -129,7 +133,7 @@ class TestMsgNEMSignTxMultisig(TrezorTest): assert hexlify(tx.data) == b'02100000010000984d01000020000000edfd32f6e760648c032f9acb4b30d514265f6a5b5f8a7154f2618922b4062084c800000000000000bc010000240000002000000087923cd4805f3babe6b5af9cbb2b08be4458e39531618aed73c911f160c8e38528000000544444324354364c514c49595135364b49584933454e544d36454b3344343450354b5a50464d4b32' assert hexlify(tx.signature) == b'286358a16ae545bff798feab93a713440c7c2f236d52ac0e995669d17a1915b0903667c97fa04418eccb42333cba95b19bccc8ac1faa8224dcfaeb41890ae807' - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 900000, "fee": 200000, "type": nem.TYPE_MULTISIG_SIGNATURE, diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_others.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_others.py index 50812c1fb0..e24c3f740b 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_others.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_others.py @@ -15,9 +15,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify +import pytest + +from .common import TrezorTest from trezorlib import nem +from trezorlib.tools import parse_path # assertion data from T1 @@ -28,7 +32,7 @@ class TestMsgNEMSignTxOther(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() with self.client: - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 12349215, "fee": 9900, "type": nem.TYPE_IMPORTANCE_TRANSFER, @@ -49,7 +53,7 @@ class TestMsgNEMSignTxOther(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "fee": 2000000, "type": nem.TYPE_PROVISION_NAMESPACE, diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py index a6e10baae4..83a4b61cc7 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py @@ -15,10 +15,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify +import pytest + +from .common import TrezorTest from trezorlib import messages as proto from trezorlib import nem +from trezorlib.tools import parse_path # assertion data from T1 @@ -43,7 +47,7 @@ class TestMsgNEMSignTx(TrezorTest): proto.NEMSignedTx(), ]) - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "amount": 2000000, "fee": 2000000, @@ -74,7 +78,7 @@ class TestMsgNEMSignTx(TrezorTest): proto.NEMSignedTx(), ]) - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 74649215, "amount": 2000000, "fee": 2000000, @@ -100,7 +104,7 @@ class TestMsgNEMSignTx(TrezorTest): def test_nem_signtx_xem_as_mosaic(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 76809215, "amount": 5000000, "fee": 1000000, @@ -128,7 +132,7 @@ class TestMsgNEMSignTx(TrezorTest): def test_nem_signtx_unknown_mosaic(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 76809215, "amount": 2000000, "fee": 1000000, diff --git a/trezorlib/tests/device_tests/test_msg_signtx.py b/trezorlib/tests/device_tests/test_msg_signtx.py index 7b92325aa6..52aa8a3d89 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx.py +++ b/trezorlib/tests/device_tests/test_msg_signtx.py @@ -16,11 +16,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify + +from .common import TrezorTest from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException +from trezorlib.tools import parse_path TxApiTestnet = coins.tx_api['Testnet'] @@ -88,7 +91,7 @@ class TestMsgSigntx(TrezorTest): # tx: e5040e1bc1ae7667ffb9e5248e90b2fb93cd9150234151ce90e14ab2f5933bcd # input 0: 0.31 BTC inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/1'/0'/0/0"), + address_n=parse_path("44'/1'/0'/0/0"), # amount=31000000, prev_hash=TXHASH_e5040e, prev_index=0, @@ -101,7 +104,7 @@ class TestMsgSigntx(TrezorTest): ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("44'/1'/0'/1/0"), + address_n=parse_path("44'/1'/0'/1/0"), amount=900000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -185,14 +188,14 @@ class TestMsgSigntx(TrezorTest): # tx: c275c333fd1b36bef4af316226c66a8b3693fbfcc081a5e16a2ae5fcb09e92bf inp1 = proto.TxInputType( - address_n=self.client.expand_path("m/44'/0'/0'/0/5"), # 1GA9u9TfCG7SWmKCveBumdA1TZpfom6ZdJ + address_n=parse_path("m/44'/0'/0'/0/5"), # 1GA9u9TfCG7SWmKCveBumdA1TZpfom6ZdJ # amount=50000, prev_hash=TXHASH_50f6f1, prev_index=1, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("m/44'/0'/0'/1/3"), # 1EcL6AyfQTyWKGvXwNSfsWoYnD3whzVFdu + address_n=parse_path("m/44'/0'/0'/1/3"), # 1EcL6AyfQTyWKGvXwNSfsWoYnD3whzVFdu amount=30000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -639,7 +642,7 @@ class TestMsgSigntx(TrezorTest): self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/1'/4'/0/0"), + address_n=parse_path("44'/1'/4'/0/0"), # moUJnmge8SRXuediK7bW6t4YfrPqbE6hD7 prev_hash=TXHASH_d2dcda, prev_index=1, @@ -653,7 +656,7 @@ class TestMsgSigntx(TrezorTest): ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("44'/1'/12345'/1/0"), + address_n=parse_path("44'/1'/12345'/1/0"), amount=123400000 - 5000 - 100000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -756,7 +759,7 @@ class TestMsgSigntx(TrezorTest): # tx: e5040e1bc1ae7667ffb9e5248e90b2fb93cd9150234151ce90e14ab2f5933bcd # input 0: 0.31 BTC inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/1'/0'/0/0"), + address_n=parse_path("44'/1'/0'/0/0"), # amount=31000000, prev_hash=TXHASH_e5040e, prev_index=0, @@ -769,13 +772,13 @@ class TestMsgSigntx(TrezorTest): ) out_change1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/1'/0'/1/0"), + address_n=parse_path("44'/1'/0'/1/0"), amount=900000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) out_change2 = proto.TxOutputType( - address_n=self.client.expand_path("44'/1'/0'/1/1"), + address_n=parse_path("44'/1'/0'/1/1"), amount=10000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -815,7 +818,7 @@ class TestMsgSigntx(TrezorTest): # tx: e5040e1bc1ae7667ffb9e5248e90b2fb93cd9150234151ce90e14ab2f5933bcd # input 0: 0.31 BTC inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/1'/0'/0/0"), + address_n=parse_path("44'/1'/0'/0/0"), # amount=31000000, prev_hash=TXHASH_e5040e, prev_index=0, @@ -829,7 +832,7 @@ class TestMsgSigntx(TrezorTest): # change on main chain is allowed => treated as a change out_change = proto.TxOutputType( - address_n=self.client.expand_path("44'/1'/0'/0/0"), + address_n=parse_path("44'/1'/0'/0/0"), amount=900000, script_type=proto.OutputScriptType.PAYTOADDRESS, ) diff --git a/trezorlib/tests/device_tests/test_msg_signtx_bcash.py b/trezorlib/tests/device_tests/test_msg_signtx_bcash.py index 9a6d329749..508364923f 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_bcash.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_bcash.py @@ -23,6 +23,8 @@ from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException +from trezorlib.tools import parse_path + TxApiBcash = coins.tx_api['Bcash'] @@ -34,7 +36,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/0/0"), + address_n=parse_path("44'/145'/0'/0/0"), # bitcoincash:qr08q88p9etk89wgv05nwlrkm4l0urz4cyl36hh9sv amount=1995344, prev_hash=unhexlify('bc37c28dfb467d2ecb50261387bf752a3977d7e5337915071bb4151e6b711a78'), @@ -42,7 +44,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/145'/0'/1/0"), + address_n=parse_path("44'/145'/0'/1/0"), amount=1896050, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -71,7 +73,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/1/0"), + address_n=parse_path("44'/145'/0'/1/0"), # bitcoincash:qzc5q87w069lzg7g3gzx0c8dz83mn7l02scej5aluw amount=1896050, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -79,7 +81,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) inp2 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/0/1"), + address_n=parse_path("44'/145'/0'/0/1"), # bitcoincash:qr23ajjfd9wd73l87j642puf8cad20lfmqdgwvpat4 amount=73452, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -111,7 +113,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/1/0"), + address_n=parse_path("44'/145'/0'/1/0"), # bitcoincash:qzc5q87w069lzg7g3gzx0c8dz83mn7l02scej5aluw amount=1896050, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -119,7 +121,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) inp2 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/0/1"), + address_n=parse_path("44'/145'/0'/0/1"), # bitcoincash:qr23ajjfd9wd73l87j642puf8cad20lfmqdgwvpat4 amount=73452, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -151,7 +153,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/1/0"), + address_n=parse_path("44'/145'/0'/1/0"), # bitcoincash:qzc5q87w069lzg7g3gzx0c8dz83mn7l02scej5aluw amount=300, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -159,7 +161,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) inp2 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/0'/0/1"), + address_n=parse_path("44'/145'/0'/0/1"), # bitcoincash:qr23ajjfd9wd73l87j642puf8cad20lfmqdgwvpat4 amount=70, prev_hash=unhexlify('502e8577b237b0152843a416f8f1ab0c63321b1be7a8cad7bf5c5c216fcf062c'), @@ -237,7 +239,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/1000'/0/0"), + address_n=parse_path("44'/145'/1000'/0/0"), # bitcoincash:qr08q88p9etk89wgv05nwlrkm4l0urz4cyl36hh9sv amount=1995344, prev_hash=unhexlify('bc37c28dfb467d2ecb50261387bf752a3977d7e5337915071bb4151e6b711a78'), @@ -245,7 +247,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/145'/1000'/1/0"), + address_n=parse_path("44'/145'/1000'/1/0"), amount=1896050, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -291,7 +293,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) xpubs = [] - for n in map(lambda index: self.client.get_public_node(self.client.expand_path("44'/145'/" + str(index) + "'")), range(1, 4)): + for n in map(lambda index: self.client.get_public_node(parse_path("44'/145'/" + str(index) + "'")), range(1, 4)): xpubs.append(n.xpub) def getmultisig(chain, nr, signatures=[b'', b'', b''], xpubs=xpubs): @@ -306,7 +308,7 @@ class TestMsgSigntxBch(TrezorTest): public_key=unhexlify('0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71')) sig = unhexlify(b'304402207274b5a4d15e75f3df7319a375557b0efba9b27bc63f9f183a17da95a6125c94022000efac57629f1522e2d3958430e2ef073b0706cfac06cce492651b79858f09ae') inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/1'/1/0"), + address_n=parse_path("44'/145'/1'/1/0"), multisig=getmultisig(1, 0, [b'', sig, b'']), # bitcoincash:pp6kcpkhua7789g2vyj0qfkcux3yvje7euhyhltn0a amount=24000, @@ -315,7 +317,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.InputScriptType.SPENDMULTISIG, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/145'/1'/1/1"), + address_n=parse_path("44'/145'/1'/1/1"), multisig=proto.MultisigRedeemScriptType( pubkeys=[proto.HDNodePathType(node=deserialize(xpubs[0]), address_n=[1, 1]), proto.HDNodePathType(node=correcthorse, address_n=[]), @@ -344,7 +346,7 @@ class TestMsgSigntxBch(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBcash) xpubs = [] - for n in map(lambda index: self.client.get_public_node(self.client.expand_path("44'/145'/" + str(index) + "'")), range(1, 4)): + for n in map(lambda index: self.client.get_public_node(parse_path("44'/145'/" + str(index) + "'")), range(1, 4)): xpubs.append(n.xpub) def getmultisig(chain, nr, signatures=[b'', b'', b''], xpubs=xpubs): @@ -354,7 +356,7 @@ class TestMsgSigntxBch(TrezorTest): m=2, ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/3'/0/0"), + address_n=parse_path("44'/145'/3'/0/0"), multisig=getmultisig(0, 0), # bitcoincash:pqguz4nqq64jhr5v3kvpq4dsjrkda75hwy86gq0qzw amount=48490, @@ -368,7 +370,7 @@ class TestMsgSigntxBch(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("44'/145'/3'/1/0"), + address_n=parse_path("44'/145'/3'/1/0"), multisig=getmultisig(1, 0), script_type=proto.OutputScriptType.PAYTOMULTISIG, amount=24000 @@ -390,7 +392,7 @@ class TestMsgSigntxBch(TrezorTest): assert hexlify(signatures1[0]) == b'3045022100bcb1a7134a13025a06052546ee1c6ac3640a0abd2d130190ed13ed7fcb43e9cd02207c381478e2ee123c850425bfbf6d3c691230eb37e333832cb32a1ed3f2cd9e85' inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/145'/1'/0/0"), + address_n=parse_path("44'/145'/1'/0/0"), multisig=getmultisig(0, 0, [b'', b'', signatures1[0]]), # bitcoincash:pqguz4nqq64jhr5v3kvpq4dsjrkda75hwy86gq0qzw amount=48490, diff --git a/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py b/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py index 846f029ded..c08514166a 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_bitcoin_gold.py @@ -24,6 +24,7 @@ from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException +from trezorlib.tools import parse_path TxApiBitcoinGold = coins.tx_api["Bitcoin Gold"] @@ -35,14 +36,14 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/0'/0/0"), + address_n=parse_path("44'/156'/0'/0/0"), amount=1995344, prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), prev_index=0, script_type=proto.InputScriptType.SPENDADDRESS, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/156'/0'/1/0"), + address_n=parse_path("44'/156'/0'/1/0"), amount=1896050, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -71,14 +72,14 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/0'/1/0"), + address_n=parse_path("44'/156'/0'/1/0"), amount=1896050, prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), prev_index=0, script_type=proto.InputScriptType.SPENDADDRESS, ) inp2 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/0'/0/1"), + address_n=parse_path("44'/156'/0'/0/1"), # 1LRspCZNFJcbuNKQkXgHMDucctFRQya5a3 amount=73452, prev_hash=unhexlify('db77c2461b840e6edbe7f9280043184a98e020d9795c1b65cb7cef2551a8fb18'), @@ -110,7 +111,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/1000'/0/0"), + address_n=parse_path("44'/156'/1000'/0/0"), # 1MH9KKcvdCTY44xVDC2k3fjBbX5Cz29N1q amount=1995344, prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), @@ -118,7 +119,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): script_type=proto.InputScriptType.SPENDADDRESS, ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("44'/156'/1000'/1/0"), + address_n=parse_path("44'/156'/1000'/1/0"), amount=1896050, script_type=proto.OutputScriptType.PAYTOADDRESS, ) @@ -164,7 +165,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) xpubs = [] - for n in map(lambda index: self.client.get_public_node(self.client.expand_path("44'/156'/" + str(index) + "'")), range(1, 4)): + for n in map(lambda index: self.client.get_public_node(parse_path("44'/156'/" + str(index) + "'")), range(1, 4)): xpubs.append(n.xpub) def getmultisig(chain, nr, signatures=[b'', b'', b''], xpubs=xpubs): @@ -174,7 +175,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): m=2, ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/3'/0/0"), + address_n=parse_path("44'/156'/3'/0/0"), multisig=getmultisig(0, 0), # 33Ju286QvonBz5N1V754ZekQv4GLJqcc5R amount=48490, @@ -188,7 +189,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("44'/156'/3'/1/0"), + address_n=parse_path("44'/156'/3'/1/0"), multisig=getmultisig(1, 0), script_type=proto.OutputScriptType.PAYTOMULTISIG, amount=24000 @@ -210,7 +211,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): assert hexlify(signatures1[0]) == b'3045022100b1594f3b186d0dedbf61e53a1c407b1e0747098b7375941df85af045040f578e022013ba1893eb9e2fd854dd07073a83b261cf4beba76f66b07742e462b4088a7e4a' inp1 = proto.TxInputType( - address_n=self.client.expand_path("44'/156'/1'/0/0"), + address_n=parse_path("44'/156'/1'/0/0"), multisig=getmultisig(0, 0, [b'', b'', signatures1[0]]), # 33Ju286QvonBz5N1V754ZekQv4GLJqcc5R amount=48490, @@ -241,7 +242,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/156'/0'/1/0"), + address_n=parse_path("49'/156'/0'/1/0"), amount=123456789, prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), prev_index=0, @@ -279,7 +280,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/156'/0'/1/0"), + address_n=parse_path("49'/156'/0'/1/0"), amount=123456789, prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), prev_index=0, @@ -291,7 +292,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("49'/156'/0'/1/0"), + address_n=parse_path("49'/156'/0'/1/0"), script_type=proto.OutputScriptType.PAYTOP2SHWITNESS, amount=123456789 - 11000 - 12300000, ) @@ -317,7 +318,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): def test_send_multisig_1(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiBitcoinGold) - nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4)) + nodes = map(lambda index: self.client.get_public_node(parse_path("999'/1'/%d'" % index)), range(1, 4)) multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -325,7 +326,7 @@ class TestMsgSigntxBitcoinGold(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/1'/2/0"), + address_n=parse_path("999'/1'/1'/2/0"), prev_hash=unhexlify('25526bf06c76ad3082bba930cf627cdd5f1b3cd0b9907dd7ff1a07e14addc985'), prev_index=1, script_type=proto.InputScriptType.SPENDP2SHWITNESS, diff --git a/trezorlib/tests/device_tests/test_msg_signtx_decred.py b/trezorlib/tests/device_tests/test_msg_signtx_decred.py index 77b0040a5b..445404f776 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_decred.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_decred.py @@ -15,10 +15,14 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from binascii import hexlify, unhexlify +import pytest + +from .common import TrezorTest from trezorlib import coins from trezorlib import messages as proto +from trezorlib.tools import parse_path TxApiDecredTestnet = coins.tx_api['Decred Testnet'] @@ -41,7 +45,7 @@ class TestMsgSigntxDecred(TrezorTest): inp1 = proto.TxInputType( # TscqTv1he8MZrV321SfRghw7LFBCJDKB3oz - address_n=self.client.expand_path("m/44'/1'/0'/0/0"), + address_n=parse_path("m/44'/1'/0'/0/0"), prev_hash=TXHASH_e16248, prev_index=1, script_type=proto.InputScriptType.SPENDADDRESS, @@ -80,7 +84,7 @@ class TestMsgSigntxDecred(TrezorTest): inp1 = proto.TxInputType( # TscqTv1he8MZrV321SfRghw7LFBCJDKB3oz - address_n=self.client.expand_path("m/44'/1'/0'/0/0"), + address_n=parse_path("m/44'/1'/0'/0/0"), prev_hash=TXHASH_5e6e35, prev_index=0, script_type=proto.InputScriptType.SPENDADDRESS, @@ -89,7 +93,7 @@ class TestMsgSigntxDecred(TrezorTest): inp2 = proto.TxInputType( # TscqTv1he8MZrV321SfRghw7LFBCJDKB3oz - address_n=self.client.expand_path("m/44'/1'/0'/0/0"), + address_n=parse_path("m/44'/1'/0'/0/0"), prev_hash=TXHASH_ccf95b, prev_index=1, script_type=proto.InputScriptType.SPENDADDRESS, @@ -98,7 +102,7 @@ class TestMsgSigntxDecred(TrezorTest): inp3 = proto.TxInputType( # Tskt39YEvzoJ5KBDH4f1auNzG3jViVjZ2RV - address_n=self.client.expand_path("m/44'/1'/0'/0/1"), + address_n=parse_path("m/44'/1'/0'/0/1"), prev_hash=TXHASH_f395ef, prev_index=0, script_type=proto.InputScriptType.SPENDADDRESS, @@ -114,7 +118,7 @@ class TestMsgSigntxDecred(TrezorTest): out2 = proto.TxOutputType( # TsaSFRwfN9muW5F6ZX36iSksc9hruiC5F97 - address_n=self.client.expand_path("m/44'/1'/0'/1/0"), + address_n=parse_path("m/44'/1'/0'/1/0"), amount=100000000, script_type=proto.OutputScriptType.PAYTOADDRESS, decred_script_version=0, @@ -154,7 +158,7 @@ class TestMsgSigntxDecred(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiDecredTestnet) - paths = [self.client.expand_path("m/48'/1'/%d'" % index) for index in range(3)] + paths = [parse_path("m/48'/1'/%d'" % index) for index in range(3)] nodes = [self.client.get_public_node(address_n, coin_name="Decred Testnet").node for address_n in paths] signatures = [ @@ -163,7 +167,7 @@ class TestMsgSigntxDecred(TrezorTest): ] def create_multisig(index, address, signatures=None): - address_n = self.client.expand_path(address) + address_n = parse_path(address) multisig = proto.MultisigRedeemScriptType( pubkeys=[proto.HDNodePathType(node=node, address_n=address_n) for node in nodes], signatures=signatures, diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py index 9064a6d2eb..a3a1b6022e 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit.py @@ -22,6 +22,7 @@ from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto from trezorlib.client import CallException +from trezorlib.tools import parse_path TxApiTestnet = coins.tx_api["Testnet"] @@ -32,7 +33,7 @@ class TestMsgSigntxSegwit(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=123456789, prev_hash=unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'), @@ -71,7 +72,7 @@ class TestMsgSigntxSegwit(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=123456789, prev_hash=unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'), @@ -84,7 +85,7 @@ class TestMsgSigntxSegwit(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), script_type=proto.OutputScriptType.PAYTOP2SHWITNESS, amount=123456789 - 11000 - 12300000, ) @@ -108,7 +109,7 @@ class TestMsgSigntxSegwit(TrezorTest): def test_send_multisig_1(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) - nodes = map(lambda index: self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)), range(1, 4)) + nodes = map(lambda index: self.client.get_public_node(parse_path("999'/1'/%d'" % index)), range(1, 4)) multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -116,7 +117,7 @@ class TestMsgSigntxSegwit(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/1'/2/0"), + address_n=parse_path("999'/1'/1'/2/0"), prev_hash=unhexlify('9c31922be756c06d02167656465c8dc83bb553bf386a3f478ae65b5c021002be'), prev_index=1, script_type=proto.InputScriptType.SPENDP2SHWITNESS, @@ -165,7 +166,7 @@ class TestMsgSigntxSegwit(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=123456789, prev_hash=unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'), @@ -178,7 +179,7 @@ class TestMsgSigntxSegwit(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("49'/1'/12345'/1/0"), + address_n=parse_path("49'/1'/12345'/1/0"), script_type=proto.OutputScriptType.PAYTOP2SHWITNESS, amount=123456789 - 11000 - 12300000, ) diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py index 18bf144474..304c7f0022 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py @@ -21,7 +21,7 @@ from .common import TrezorTest from ..support.ckd_public import deserialize from trezorlib import coins from trezorlib import messages as proto -from trezorlib.client import CallException +from trezorlib.tools import parse_path TxApiTestnet = coins.tx_api['Testnet'] @@ -32,7 +32,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=123456789, prev_hash=unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'), @@ -71,7 +71,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=123456789, prev_hash=unhexlify('20912f98ea3ed849042efed0fdac8cb4fc301961c5988cba56902d8ffb61c337'), @@ -84,7 +84,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), script_type=proto.OutputScriptType.PAYTOP2SHWITNESS, amount=123456789 - 11000 - 12300000, ) @@ -109,7 +109,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/0/0"), + address_n=parse_path("49'/1'/0'/0/0"), # tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s amount=12300000, prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'), @@ -148,7 +148,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/0/0"), + address_n=parse_path("49'/1'/0'/0/0"), # tb1qqzv60m9ajw8drqulta4ld4gfx0rdh82un5s65s amount=12300000, prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'), @@ -161,7 +161,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), script_type=proto.OutputScriptType.PAYTOWITNESS, amount=12300000 - 11000 - 5000000, ) @@ -186,7 +186,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) inp1 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # 2N1LGaGg836mqSQqiuUBLfcyGBhyZbremDX amount=111145789, prev_hash=unhexlify('09144602765ce3dd8f4329445b20e3684e948709c5cdcaf12da3bb079c99448a'), @@ -194,7 +194,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): script_type=proto.InputScriptType.SPENDP2SHWITNESS, ) inp2 = proto.TxInputType( - address_n=self.client.expand_path("49'/1'/0'/1/0"), + address_n=parse_path("49'/1'/0'/1/0"), # tb1q694ccp5qcc0udmfwgp692u2s2hjpq5h407urtu amount=7289000, prev_hash=unhexlify('65b811d3eca0fe6915d9f2d77c86c5a7f19bf66b1b1253c2c51cb4ae5f0c017b'), @@ -247,7 +247,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): def test_send_multisig_1(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) - nodes = [self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)) for index in range(1, 4)] + nodes = [self.client.get_public_node(parse_path("999'/1'/%d'" % index)) for index in range(1, 4)] multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -255,7 +255,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/1'/2/0"), + address_n=parse_path("999'/1'/1'/2/0"), prev_hash=unhexlify('9c31922be756c06d02167656465c8dc83bb553bf386a3f478ae65b5c021002be'), prev_index=1, script_type=proto.InputScriptType.SPENDP2SHWITNESS, @@ -303,7 +303,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): def test_send_multisig_2(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) - nodes = [self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)) for index in range(1, 4)] + nodes = [self.client.get_public_node(parse_path("999'/1'/%d'" % index)) for index in range(1, 4)] multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 1]), nodes)), signatures=[b'', b'', b''], @@ -311,7 +311,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/2'/2/1"), + address_n=parse_path("999'/1'/2'/2/1"), prev_hash=unhexlify('f41cbedd8becee05a830f418d13aa665125464547db5c7a6cd28f21639fe1228'), prev_index=0, script_type=proto.InputScriptType.SPENDWITNESS, @@ -359,7 +359,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): def test_send_multisig_3_change(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) - nodes = [self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)) for index in range(1, 4)] + nodes = [self.client.get_public_node(parse_path("999'/1'/%d'" % index)) for index in range(1, 4)] multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[2, 0]), nodes)), signatures=[b'', b'', b''], @@ -372,7 +372,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/1'/2/0"), + address_n=parse_path("999'/1'/1'/2/0"), prev_hash=unhexlify('c9348040bbc2024e12dcb4a0b4806b0398646b91acf314da028c3f03dd0179fc'), prev_index=0, script_type=proto.InputScriptType.SPENDWITNESS, @@ -381,7 +381,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("999'/1'/1'/1/1"), + address_n=parse_path("999'/1'/1'/1/1"), amount=1603000, multisig=multisig2, script_type=proto.OutputScriptType.PAYTOP2SHWITNESS @@ -420,7 +420,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): def test_send_multisig_4_change(self): self.setup_mnemonic_allallall() self.client.set_tx_api(TxApiTestnet) - nodes = [self.client.get_public_node(self.client.expand_path("999'/1'/%d'" % index)) for index in range(1, 4)] + nodes = [self.client.get_public_node(parse_path("999'/1'/%d'" % index)) for index in range(1, 4)] multisig = proto.MultisigRedeemScriptType( pubkeys=list(map(lambda n: proto.HDNodePathType(node=deserialize(n.xpub), address_n=[1, 1]), nodes)), signatures=[b'', b'', b''], @@ -433,7 +433,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) inp1 = proto.TxInputType( - address_n=self.client.expand_path("999'/1'/1'/1/1"), + address_n=parse_path("999'/1'/1'/1/1"), prev_hash=unhexlify('31bc1c88ce6ae337a6b3057a16d5bad0b561ad1dfc047d0a7fbb8814668f91e5'), prev_index=0, script_type=proto.InputScriptType.SPENDP2SHWITNESS, @@ -442,7 +442,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): ) out1 = proto.TxOutputType( - address_n=self.client.expand_path("999'/1'/1'/1/2"), + address_n=parse_path("999'/1'/1'/1/2"), amount=1602000, multisig=multisig2, script_type=proto.OutputScriptType.PAYTOWITNESS diff --git a/trezorlib/tests/device_tests/test_multisig_change.py b/trezorlib/tests/device_tests/test_multisig_change.py index 0086d353cc..e09dcbd03d 100644 --- a/trezorlib/tests/device_tests/test_multisig_change.py +++ b/trezorlib/tests/device_tests/test_multisig_change.py @@ -22,6 +22,7 @@ from .common import TrezorTest from ..support import ckd_public as bip32 from trezorlib import messages as proto from trezorlib.coins import tx_api +from trezorlib.tools import parse_path class TestMultisigChange(TrezorTest): @@ -195,7 +196,7 @@ class TestMultisigChange(TrezorTest): ) out2 = proto.TxOutputType( - address_n=self.client.expand_path("45'/0/1/1"), + address_n=parse_path("45'/0/1/1"), amount=44000000, script_type=proto.OutputScriptType.PAYTOADDRESS ) @@ -211,7 +212,7 @@ class TestMultisigChange(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() out1 = proto.TxOutputType( - address_n=self.client.expand_path("45'/0/1/0"), + address_n=parse_path("45'/0/1/0"), amount=40000000, script_type=proto.OutputScriptType.PAYTOADDRESS ) From 325312d11c23372743fb4dd541bd67672000d923 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 20 Apr 2018 15:52:47 +0200 Subject: [PATCH 05/29] protobuf: move formatter function to protobuf where it belongs better --- trezorctl | 4 ++-- trezorlib/client.py | 37 ++----------------------------------- trezorlib/protobuf.py | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/trezorctl b/trezorctl index 1fc7394cc4..b12499efb8 100755 --- a/trezorctl +++ b/trezorctl @@ -27,7 +27,7 @@ import json import os import sys -from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException, format_protobuf +from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException from trezorlib.transport import get_transport, enumerate_devices from trezorlib import coins from trezorlib import messages as proto @@ -111,7 +111,7 @@ def print_result(res, path, verbose, is_json): else: click.echo('%s: %s' % (k, v)) elif isinstance(res, protobuf.MessageType): - click.echo(format_protobuf(res)) + click.echo(protobuf.format_message(res)) else: click.echo(res) diff --git a/trezorlib/client.py b/trezorlib/client.py index eedc6225c4..f5bdd973d1 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -34,10 +34,9 @@ from . import messages as proto from . import tools from . import mapping from . import nem +from . import protobuf from . import stellar from .debuglink import DebugLink -from .protobuf import MessageType - if sys.version_info.major < 3: raise Exception("Trezorlib does not support Python 2 anymore.") @@ -83,38 +82,6 @@ def get_buttonrequest_value(code): return [k for k in dir(proto.ButtonRequestType) if getattr(proto.ButtonRequestType, k) == code][0] -def format_protobuf(pb, indent=0, sep=' ' * 4): - def pformat_value(value, indent): - level = sep * indent - leadin = sep * (indent + 1) - if isinstance(value, MessageType): - return format_protobuf(value, indent, sep) - if isinstance(value, list): - lines = [] - lines.append('[') - lines += [leadin + pformat_value(x, indent + 1) + ',' for x in value] - lines.append(level + ']') - return '\n'.join(lines) - if isinstance(value, dict): - lines = [] - lines.append('{') - for key, val in sorted(value.items()): - if val is None or val == []: - continue - if key == 'address_n' and isinstance(val, list): - lines.append(leadin + key + ': ' + repr(val) + ',') - else: - lines.append(leadin + key + ': ' + pformat_value(val, indent + 1) + ',') - lines.append(level + '}') - return '\n'.join(lines) - if isinstance(value, bytearray): - return 'bytearray(0x{})'.format(binascii.hexlify(value).decode('ascii')) - - return repr(value) - - return pb.__class__.__name__ + ' ' + pformat_value(pb.__dict__, indent) - - def pprint(msg): msg_class = msg.__class__.__name__ msg_size = msg.ByteSize() @@ -122,7 +89,7 @@ def pprint(msg): or isinstance(msg, proto.Features): return "<%s> (%d bytes)" % (msg_class, msg_size) else: - return "<%s> (%d bytes):\n%s" % (msg_class, msg_size, format_protobuf(msg)) + return "<%s> (%d bytes):\n%s" % (msg_class, msg_size, protobuf.format_message(msg)) def log(msg): diff --git a/trezorlib/protobuf.py b/trezorlib/protobuf.py index 287d7dabf6..b42347c3d8 100644 --- a/trezorlib/protobuf.py +++ b/trezorlib/protobuf.py @@ -39,7 +39,9 @@ required: >>> """ ''' +import binascii from io import BytesIO +from typing import Any _UVARINT_BUFFER = bytearray(1) @@ -344,3 +346,35 @@ def dump_message(writer, msg): else: raise TypeError + + +def format_message(pb: MessageType, indent: int=0, sep: str= ' ' * 4) -> str: + def pformat_value(value: Any, indent: int) -> str: + level = sep * indent + leadin = sep * (indent + 1) + if isinstance(value, MessageType): + return format_message(value, indent, sep) + if isinstance(value, list): + lines = [] + lines.append('[') + lines += [leadin + pformat_value(x, indent + 1) + ',' for x in value] + lines.append(level + ']') + return '\n'.join(lines) + if isinstance(value, dict): + lines = [] + lines.append('{') + for key, val in sorted(value.items()): + if val is None or val == []: + continue + if key == 'address_n' and isinstance(val, list): + lines.append(leadin + key + ': ' + repr(val) + ',') + else: + lines.append(leadin + key + ': ' + pformat_value(val, indent + 1) + ',') + lines.append(level + '}') + return '\n'.join(lines) + if isinstance(value, bytearray): + return 'bytearray(0x{})'.format(binascii.hexlify(value).decode('ascii')) + + return repr(value) + + return pb.__class__.__name__ + ' ' + pformat_value(pb.__dict__, indent) From b7c7190573cd1f163226f156b6075d31f6fb51d1 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 20 Apr 2018 17:23:43 +0200 Subject: [PATCH 06/29] trezorlib: remove Hash to btc_hash, prepare for moving it away from things --- tools/signtest.py | 2 +- trezorlib/client.py | 4 ++-- trezorlib/tests/support/ckd_public.py | 4 ++-- trezorlib/tools.py | 7 +++++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/tools/signtest.py b/tools/signtest.py index 547c3c6953..1fbdd6e2e3 100755 --- a/tools/signtest.py +++ b/tools/signtest.py @@ -110,7 +110,7 @@ class MyTxApiBitcoin(object): o.script_pubkey = b'\x76\xa9\x14' + pubkey + b'\x88\xac' txser = self.serialize_tx(t) - txhash = tools.Hash(txser)[::-1] + txhash = tools.btc_hash(txser)[::-1] self.inputs.append( proto_types.TxInputType( address_n=self.client.expand_path("44'/0'/0'/0/%d" % idx), diff --git a/trezorlib/client.py b/trezorlib/client.py index f5bdd973d1..8a0bb46a9e 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -1029,7 +1029,7 @@ class ProtocolMixin(object): if xprv[0:4] not in ('xprv', 'tprv'): raise ValueError("Unknown type of xprv") - if len(xprv) < 100 and len(xprv) > 112: + if not 100 < len(xprv) < 112: # yes this is correct in Python raise ValueError("Invalid length of xprv") node = proto.HDNodeType() @@ -1038,7 +1038,7 @@ class ProtocolMixin(object): if data[90:92] != b'00': raise ValueError("Contain invalid private key") - checksum = binascii.hexlify(hashlib.sha256(hashlib.sha256(binascii.unhexlify(data[:156])).digest()).digest()[:4]) + checksum = binascii.hexlify(tools.btc_hash(binascii.unhexlify(data[:156]))[:4]) if checksum != data[156:]: raise ValueError("Checksum doesn't match") diff --git a/trezorlib/tests/support/ckd_public.py b/trezorlib/tests/support/ckd_public.py index e377cf05a9..cb4f2fa5ba 100644 --- a/trezorlib/tests/support/ckd_public.py +++ b/trezorlib/tests/support/ckd_public.py @@ -126,14 +126,14 @@ def serialize(node, version=0x0488B21E): s += b'\x00' + node.private_key else: s += node.public_key - s += tools.Hash(s)[:4] + s += tools.btc_hash(s)[:4] return tools.b58encode(s) def deserialize(xpub): data = tools.b58decode(xpub, None) - if tools.Hash(data[:-4])[:4] != data[-4:]: + if tools.btc_hash(data[:-4])[:4] != data[-4:]: raise ValueError("Checksum failed") node = messages.HDNodeType() diff --git a/trezorlib/tools.py b/trezorlib/tools.py index 8029cb1cbf..bb24e9dd63 100644 --- a/trezorlib/tools.py +++ b/trezorlib/tools.py @@ -35,7 +35,10 @@ def H_(x: int) -> int: return x | HARDENED_FLAG -def Hash(data): +def btc_hash(data): + """ + Double-SHA256 hash as used in BTC + """ return hashlib.sha256(hashlib.sha256(data).digest()).digest() @@ -47,7 +50,7 @@ def hash_160(public_key): def hash_160_to_bc_address(h160, address_type): vh160 = struct.pack(' Date: Mon, 23 Apr 2018 12:58:30 +0200 Subject: [PATCH 07/29] protocol: python logging to supersede VerboseWire --- trezorctl | 40 ++++++++++++++++++++++++---------------- trezorlib/log.py | 19 +++++++++++++++++++ trezorlib/protobuf.py | 6 +++++- trezorlib/protocol_v1.py | 30 ++++++++++++++++++++---------- trezorlib/protocol_v2.py | 39 +++++++++++++++++++++++++-------------- 5 files changed, 93 insertions(+), 41 deletions(-) create mode 100644 trezorlib/log.py diff --git a/trezorctl b/trezorctl index b12499efb8..1f7a10d78a 100755 --- a/trezorctl +++ b/trezorctl @@ -24,12 +24,14 @@ import base64 import binascii import click import json +import logging import os import sys from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException from trezorlib.transport import get_transport, enumerate_devices from trezorlib import coins +from trezorlib import log from trezorlib import messages as proto from trezorlib import protobuf from trezorlib import stellar @@ -64,32 +66,38 @@ CHOICE_OUTPUT_SCRIPT_TYPE = ChoiceType({ }) +def enable_logging(): + handler = logging.StreamHandler() + handler.setFormatter(log.PrettyProtobufFormatter()) + logger = logging.getLogger('trezorlib') + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) + log.OMITTED_MESSAGES.add(proto.Features) + + @click.group(context_settings={'max_content_width': 400}) @click.option('-p', '--path', help='Select device by specific path.', default=os.environ.get('TREZOR_PATH')) @click.option('-v', '--verbose', is_flag=True, help='Show communication messages.') @click.option('-j', '--json', 'is_json', is_flag=True, help='Print result as JSON object') @click.pass_context def cli(ctx, path, verbose, is_json): - if ctx.invoked_subcommand != 'list': - if verbose: - cls = TrezorClientVerbose - else: - cls = TrezorClient + if verbose: + enable_logging() - def get_device(): + def get_device(): + try: + device = get_transport(path, prefix_search=False) + except: try: - device = get_transport(path, prefix_search=False) + device = get_transport(path, prefix_search=True) except: - try: - device = get_transport(path, prefix_search=True) - except: - click.echo("Failed to find a TREZOR device.") - if path is not None: - click.echo("Using path: {}".format(path)) - sys.exit(1) - return cls(transport=device) + click.echo("Failed to find a TREZOR device.") + if path is not None: + click.echo("Using path: {}".format(path)) + sys.exit(1) + return TrezorClient(transport=device) - ctx.obj = get_device + ctx.obj = get_device @cli.resultcallback() diff --git a/trezorlib/log.py b/trezorlib/log.py new file mode 100644 index 0000000000..f23e39ed3a --- /dev/null +++ b/trezorlib/log.py @@ -0,0 +1,19 @@ +import logging +from typing import Set, Type + +from . import protobuf + +OMITTED_MESSAGES = set() # type: Set[Type[protobuf.MessageType]] + + +class PrettyProtobufFormatter(logging.Formatter): + + def format(self, record): + time = self.formatTime(record) + message = '[{time}] {level}: {msg}'.format(time=time, level=record.levelname, msg=super().format(record)) + if hasattr(record, 'protobuf'): + if type(record.protobuf) in OMITTED_MESSAGES: + message += " ({} bytes)".format(record.protobuf.ByteSize()) + else: + message += "\n" + protobuf.format_message(record.protobuf) + return message diff --git a/trezorlib/protobuf.py b/trezorlib/protobuf.py index b42347c3d8..17e7fe3c77 100644 --- a/trezorlib/protobuf.py +++ b/trezorlib/protobuf.py @@ -377,4 +377,8 @@ def format_message(pb: MessageType, indent: int=0, sep: str= ' ' * 4) -> str: return repr(value) - return pb.__class__.__name__ + ' ' + pformat_value(pb.__dict__, indent) + return '{name} ({size} bytes) {content}'.format( + name=pb.__class__.__name__, + size=pb.ByteSize(), + content=pformat_value(pb.__dict__, indent) + ) diff --git a/trezorlib/protocol_v1.py b/trezorlib/protocol_v1.py index 6c04af3251..cd263e7d7e 100644 --- a/trezorlib/protocol_v1.py +++ b/trezorlib/protocol_v1.py @@ -19,22 +19,30 @@ from __future__ import absolute_import from io import BytesIO +import logging import struct +from typing import Tuple, Type + from . import mapping from . import protobuf +from .transport import Transport REPLEN = 64 +LOG = logging.getLogger(__name__) -class ProtocolV1(object): - def session_begin(self, transport): +class ProtocolV1: + + def session_begin(self, transport: Transport) -> None: pass - def session_end(self, transport): + def session_end(self, transport: Transport) -> None: pass - def write(self, transport, msg): + def write(self, transport: Transport, msg: protobuf.MessageType) -> None: + LOG.debug("sending message: {}".format(msg.__class__.__name__), + extra={'protobuf': msg}) data = BytesIO() protobuf.dump_message(data, msg) ser = data.getvalue() @@ -48,10 +56,10 @@ class ProtocolV1(object): transport.write_chunk(chunk) data = data[63:] - def read(self, transport): + def read(self, transport: Transport) -> protobuf.MessageType: # Read header with first part of message data chunk = transport.read_chunk() - (msg_type, datalen, data) = self.parse_first(chunk) + msg_type, datalen, data = self.parse_first(chunk) # Read the rest of the message while len(data) < datalen: @@ -63,21 +71,23 @@ class ProtocolV1(object): # Parse to protobuf msg = protobuf.load_message(data, mapping.get_class(msg_type)) + LOG.debug("received message: {}".format(msg.__class__.__name__), + extra={'protobuf': msg}) return msg - def parse_first(self, chunk): + def parse_first(self, chunk: bytes) -> Tuple[int, int, bytes]: if chunk[:3] != b'?##': raise RuntimeError('Unexpected magic characters') try: headerlen = struct.calcsize('>HL') - (msg_type, datalen) = struct.unpack('>HL', chunk[3:3 + headerlen]) + msg_type, datalen = struct.unpack('>HL', chunk[3:3 + headerlen]) except: raise RuntimeError('Cannot parse header') data = chunk[3 + headerlen:] - return (msg_type, datalen, data) + return msg_type, datalen, data - def parse_next(self, chunk): + def parse_next(self, chunk: bytes) -> bytes: if chunk[:1] != b'?': raise RuntimeError('Unexpected magic characters') return chunk[1:] diff --git a/trezorlib/protocol_v2.py b/trezorlib/protocol_v2.py index fd33c96a7c..ba0c4f02d4 100644 --- a/trezorlib/protocol_v2.py +++ b/trezorlib/protocol_v2.py @@ -18,28 +18,34 @@ from __future__ import absolute_import -import struct from io import BytesIO -from . import messages as proto +import logging +import struct +from typing import Tuple + from . import mapping from . import protobuf +from .transport import Transport REPLEN = 64 +LOG = logging.getLogger(__name__) -class ProtocolV2(object): - def __init__(self): +class ProtocolV2: + + def __init__(self) -> None: self.session = None - def session_begin(self, transport): + def session_begin(self, transport: Transport) -> None: chunk = struct.pack('>B', 0x03) chunk = chunk.ljust(REPLEN, b'\x00') transport.write_chunk(chunk) resp = transport.read_chunk() self.session = self.parse_session_open(resp) + LOG.debug("[session {}] session started".format(self.session)) - def session_end(self, transport): + def session_end(self, transport: Transport) -> None: if not self.session: return chunk = struct.pack('>BL', 0x04, self.session) @@ -49,12 +55,15 @@ class ProtocolV2(object): (magic, ) = struct.unpack('>B', resp[:1]) if magic != 0x04: raise RuntimeError('Expected session close') + LOG.debug("[session {}] session ended".format(self.session)) self.session = None - def write(self, transport, msg): + def write(self, transport: Transport, msg: protobuf.MessageType) -> None: if not self.session: raise RuntimeError('Missing session for v2 protocol') + LOG.debug("[session {}] sending message: {}".format(self.session, msg.__class__.__name__), + extra={'protobuf': msg}) # Serialize whole message data = BytesIO() protobuf.dump_message(data, msg) @@ -76,7 +85,7 @@ class ProtocolV2(object): data = data[datalen:] seq += 1 - def read(self, transport): + def read(self, transport: Transport) -> protobuf.MessageType: if not self.session: raise RuntimeError('Missing session for v2 protocol') @@ -95,12 +104,14 @@ class ProtocolV2(object): # Parse to protobuf msg = protobuf.load_message(data, mapping.get_class(msg_type)) + LOG.debug("[session {}] received message: {}".format(self.session, msg.__class__.__name__), + extra={'protobuf': msg}) return msg - def parse_first(self, chunk): + def parse_first(self, chunk: bytes) -> Tuple[int, int, bytes]: try: headerlen = struct.calcsize('>BLLL') - (magic, session, msg_type, datalen) = struct.unpack('>BLLL', chunk[:headerlen]) + magic, session, msg_type, datalen = struct.unpack('>BLLL', chunk[:headerlen]) except: raise RuntimeError('Cannot parse header') if magic != 0x01: @@ -109,10 +120,10 @@ class ProtocolV2(object): raise RuntimeError('Session id mismatch') return msg_type, datalen, chunk[headerlen:] - def parse_next(self, chunk): + def parse_next(self, chunk: bytes) -> bytes: try: headerlen = struct.calcsize('>BLL') - (magic, session, sequence) = struct.unpack('>BLL', chunk[:headerlen]) + magic, session, sequence = struct.unpack('>BLL', chunk[:headerlen]) except: raise RuntimeError('Cannot parse header') if magic != 0x02: @@ -121,10 +132,10 @@ class ProtocolV2(object): raise RuntimeError('Session id mismatch') return chunk[headerlen:] - def parse_session_open(self, chunk): + def parse_session_open(self, chunk: bytes) -> int: try: headerlen = struct.calcsize('>BL') - (magic, session) = struct.unpack('>BL', chunk[:headerlen]) + magic, session = struct.unpack('>BL', chunk[:headerlen]) except: raise RuntimeError('Cannot parse header') if magic != 0x03: From 1e4c1c093e146572888edbf3cfdb706c6a5ed4fc Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 9 May 2018 18:01:51 +0200 Subject: [PATCH 08/29] protobuf: improve prettyprint formatting --- trezorlib/protobuf.py | 56 +++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/trezorlib/protobuf.py b/trezorlib/protobuf.py index 17e7fe3c77..f64cb3c83f 100644 --- a/trezorlib/protobuf.py +++ b/trezorlib/protobuf.py @@ -41,7 +41,7 @@ required: import binascii from io import BytesIO -from typing import Any +from typing import Any, Optional _UVARINT_BUFFER = bytearray(1) @@ -348,37 +348,63 @@ def dump_message(writer, msg): raise TypeError -def format_message(pb: MessageType, indent: int=0, sep: str= ' ' * 4) -> str: +def format_message(pb: MessageType, + indent: int = 0, + sep: str = ' ' * 4, + truncate_after: Optional[int] = 256, + truncate_to: Optional[int] = 64, + collapse_cointypes: bool = False) -> str: + + def mostly_printable(bytes): + if not bytes: + return True + printable = sum(1 for byte in bytes if 0x20 <= byte <= 0x7e) + return printable / len(bytes) > 0.8 + def pformat_value(value: Any, indent: int) -> str: level = sep * indent leadin = sep * (indent + 1) if isinstance(value, MessageType): return format_message(value, indent, sep) if isinstance(value, list): - lines = [] - lines.append('[') - lines += [leadin + pformat_value(x, indent + 1) + ',' for x in value] - lines.append(level + ']') + # short list of simple values + if not value or not isinstance(value[0], MessageType): + return repr(value) + + # long list, one line per entry + lines = ['[', level + ']'] + lines[1:1] = [leadin + pformat_value(x, indent + 1) + ',' for x in value] return '\n'.join(lines) if isinstance(value, dict): - lines = [] - lines.append('{') + lines = ['{'] for key, val in sorted(value.items()): if val is None or val == []: continue - if key == 'address_n' and isinstance(val, list): - lines.append(leadin + key + ': ' + repr(val) + ',') - else: - lines.append(leadin + key + ': ' + pformat_value(val, indent + 1) + ',') + lines.append(leadin + key + ': ' + pformat_value(val, indent + 1) + ',') lines.append(level + '}') return '\n'.join(lines) - if isinstance(value, bytearray): - return 'bytearray(0x{})'.format(binascii.hexlify(value).decode('ascii')) + if isinstance(value, (bytes, bytearray)): + length = len(value) + suffix = '' + if truncate_after and length > truncate_after: + suffix = '...' + value = value[:truncate_to or 0] + if mostly_printable(value): + output = repr(value) + else: + output = '0x' + binascii.hexlify(value).decode('ascii') + return '{} bytes {}{}'.format(length, output, suffix) return repr(value) + from .messages import Features + pb_dict = pb.__dict__.copy() + if collapse_cointypes and isinstance(pb, Features): + del pb_dict['coins'] + pb_dict['coins (shortened)'] = ' '.join(coin.coin_shortcut for coin in pb.coins) + return '{name} ({size} bytes) {content}'.format( name=pb.__class__.__name__, size=pb.ByteSize(), - content=pformat_value(pb.__dict__, indent) + content=pformat_value(pb_dict, indent) ) From ca8ebacab9a707182f5160453202c35b42aa32be Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 9 May 2018 18:09:31 +0200 Subject: [PATCH 09/29] logging: convenience function to set up protobuf logging --- trezorctl | 8 ++------ trezorlib/log.py | 23 +++++++++++++++++++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/trezorctl b/trezorctl index 1f7a10d78a..52d82050c2 100755 --- a/trezorctl +++ b/trezorctl @@ -28,7 +28,7 @@ import logging import os import sys -from trezorlib.client import TrezorClient, TrezorClientVerbose, CallException +from trezorlib.client import TrezorClient, CallException from trezorlib.transport import get_transport, enumerate_devices from trezorlib import coins from trezorlib import log @@ -67,11 +67,7 @@ CHOICE_OUTPUT_SCRIPT_TYPE = ChoiceType({ def enable_logging(): - handler = logging.StreamHandler() - handler.setFormatter(log.PrettyProtobufFormatter()) - logger = logging.getLogger('trezorlib') - logger.setLevel(logging.DEBUG) - logger.addHandler(handler) + log.enable_debug_output() log.OMITTED_MESSAGES.add(proto.Features) diff --git a/trezorlib/log.py b/trezorlib/log.py index f23e39ed3a..0e07232fa3 100644 --- a/trezorlib/log.py +++ b/trezorlib/log.py @@ -1,5 +1,5 @@ import logging -from typing import Set, Type +from typing import Set, Type, Optional from . import protobuf @@ -8,12 +8,27 @@ OMITTED_MESSAGES = set() # type: Set[Type[protobuf.MessageType]] class PrettyProtobufFormatter(logging.Formatter): - def format(self, record): + def format(self, record: logging.LogRecord) -> str: time = self.formatTime(record) - message = '[{time}] {level}: {msg}'.format(time=time, level=record.levelname, msg=super().format(record)) + message = '[{time}] {source} {level}: {msg}'.format( + time=time, level=record.levelname.upper(), + source=record.name, + msg=super().format(record)) if hasattr(record, 'protobuf'): if type(record.protobuf) in OMITTED_MESSAGES: message += " ({} bytes)".format(record.protobuf.ByteSize()) else: - message += "\n" + protobuf.format_message(record.protobuf) + message += "\n" + protobuf.format_message(record.protobuf, collapse_cointypes=True) return message + + +def enable_debug_output(handler: Optional[logging.Handler] = None): + if handler is None: + handler = logging.StreamHandler() + + formatter = PrettyProtobufFormatter() + handler.setFormatter(formatter) + + logger = logging.getLogger('trezorlib') + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) From 0b6ec2706412b9691254040c234be766a6818ebe Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 9 May 2018 18:11:38 +0200 Subject: [PATCH 10/29] logging: get rid of pprint and VerboseWireMixin (and also TrezorClientVerbose) --- trezorlib/client.py | 23 +++++------------------ trezorlib/debuglink.py | 8 +------- 2 files changed, 6 insertions(+), 25 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index 8a0bb46a9e..e4da85ff0b 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -210,14 +210,6 @@ class BaseClient(object): mapping.register_message(msg) -class VerboseWireMixin(object): - def call_raw(self, msg): - log("SENDING " + pprint(msg)) - resp = super(VerboseWireMixin, self).call_raw(msg) - log("RECEIVED " + pprint(resp)) - return resp - - class TextUIMixin(object): # This class demonstrates easy test-based UI # integration between the device and wallet. @@ -362,7 +354,7 @@ class DebugLinkMixin(object): # Evaluate missed responses in 'with' statement if self.expected_responses is not None and len(self.expected_responses): raise RuntimeError("Some of expected responses didn't come from device: %s" % - [pprint(x) for x in self.expected_responses]) + [repr(x) for x in self.expected_responses]) # Cleanup self.expected_responses = None @@ -408,18 +400,18 @@ class DebugLinkMixin(object): expected = self.expected_responses.pop(0) except IndexError: raise AssertionException(proto.FailureType.UnexpectedMessage, - "Got %s, but no message has been expected" % pprint(msg)) + "Got %s, but no message has been expected" % repr(msg)) if msg.__class__ != expected.__class__: raise AssertionException(proto.FailureType.UnexpectedMessage, - "Expected %s, got %s" % (pprint(expected), pprint(msg))) + "Expected %s, got %s" % (repr(expected), repr(msg))) for field, value in expected.__dict__.items(): if value is None or value == []: continue if getattr(msg, field) != value: raise AssertionException(proto.FailureType.UnexpectedMessage, - "Expected %s, got %s" % (pprint(expected), pprint(msg))) + "Expected %s, got %s" % (repr(expected), repr(msg))) def callback_ButtonRequest(self, msg): log("ButtonRequest code: " + get_buttonrequest_value(msg.code)) @@ -1165,11 +1157,6 @@ class TrezorClient(ProtocolMixin, TextUIMixin, BaseClient): super().__init__(transport=transport, *args, **kwargs) -class TrezorClientVerbose(ProtocolMixin, TextUIMixin, VerboseWireMixin, BaseClient): - def __init__(self, transport, *args, **kwargs): - super().__init__(transport=transport, *args, **kwargs) - - -class TrezorClientDebugLink(ProtocolMixin, DebugLinkMixin, VerboseWireMixin, BaseClient): +class TrezorClientDebugLink(ProtocolMixin, DebugLinkMixin, BaseClient): def __init__(self, transport, *args, **kwargs): super().__init__(transport=transport, *args, **kwargs) diff --git a/trezorlib/debuglink.py b/trezorlib/debuglink.py index af130f8690..bfe44c1c3d 100644 --- a/trezorlib/debuglink.py +++ b/trezorlib/debuglink.py @@ -29,10 +29,6 @@ def button_press(yes_no): print("User pressed", '"y"' if yes_no else '"n"') -def pprint(msg): - return "<%s> (%d bytes):\n%s" % (msg.__class__.__name__, msg.ByteSize(), msg) - - class DebugLink(object): def __init__(self, transport, pin_func=pin_info, button_func=button_press): self.transport = transport @@ -45,12 +41,10 @@ class DebugLink(object): self.transport.session_end() def _call(self, msg, nowait=False): - print("DEBUGLINK SEND", pprint(msg)) self.transport.write(msg) if nowait: - return + return None ret = self.transport.read() - print("DEBUGLINK RECV", pprint(ret)) return ret def read_pin(self): From 266e34eec46dff08686d4af43e537268d4999658 Mon Sep 17 00:00:00 2001 From: matejcik Date: Wed, 9 May 2018 18:12:31 +0200 Subject: [PATCH 11/29] tests/device_tests: disable protobuf outputs unless run with -v --- trezorlib/tests/device_tests/common.py | 4 +--- trezorlib/tests/device_tests/conftest.py | 6 ++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/trezorlib/tests/device_tests/common.py b/trezorlib/tests/device_tests/common.py index 8681d947f6..5e925b7afa 100644 --- a/trezorlib/tests/device_tests/common.py +++ b/trezorlib/tests/device_tests/common.py @@ -18,13 +18,11 @@ from __future__ import print_function -from binascii import hexlify, unhexlify -import pytest import os from trezorlib import coins from trezorlib import tx_api -from trezorlib.client import TrezorClient, TrezorClientDebugLink +from trezorlib.client import TrezorClientDebugLink from trezorlib.transport import get_transport tests_dir = os.path.dirname(os.path.abspath(__file__)) diff --git a/trezorlib/tests/device_tests/conftest.py b/trezorlib/tests/device_tests/conftest.py index 6cb9d410a5..2a0187e696 100644 --- a/trezorlib/tests/device_tests/conftest.py +++ b/trezorlib/tests/device_tests/conftest.py @@ -2,6 +2,7 @@ import pytest from . import common from trezorlib.client import TrezorClient +from trezorlib import log def device_version(): @@ -22,6 +23,11 @@ except: TREZOR_VERSION = None +def pytest_configure(config): + if config.getoption('verbose'): + log.enable_debug_output() + + def pytest_runtest_setup(item): ''' Called for each test item (class, individual tests). From 123aec5d7dfdadd783f9f480f10a00728b1399ee Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 11 May 2018 14:53:51 +0200 Subject: [PATCH 12/29] tests/device_tests: remove all star imports --- trezorlib/tests/device_tests/test_basic.py | 2 +- trezorlib/tests/device_tests/test_bip32_speed.py | 7 +++---- trezorlib/tests/device_tests/test_debuglink.py | 4 ++-- .../tests/device_tests/test_msg_applysettings.py | 3 ++- trezorlib/tests/device_tests/test_msg_changepin.py | 3 ++- .../tests/device_tests/test_msg_cipherkeyvalue.py | 4 +++- trezorlib/tests/device_tests/test_msg_clearsession.py | 3 ++- .../device_tests/test_msg_ethereum_getaddress.py | 3 ++- .../device_tests/test_msg_ethereum_signmessage.py | 4 +++- .../tests/device_tests/test_msg_ethereum_signtx.py | 5 ++++- .../device_tests/test_msg_ethereum_verifymessage.py | 5 ++++- .../tests/device_tests/test_msg_getecdhsessionkey.py | 3 ++- trezorlib/tests/device_tests/test_msg_getentropy.py | 3 +-- trezorlib/tests/device_tests/test_msg_loaddevice.py | 3 ++- .../tests/device_tests/test_msg_loaddevice_xprv.py | 3 ++- trezorlib/tests/device_tests/test_msg_ping.py | 3 ++- .../tests/device_tests/test_msg_recoverydevice.py | 4 ++-- .../device_tests/test_msg_recoverydevice_dryrun.py | 4 ++-- .../tests/device_tests/test_msg_recoverydevice_t2.py | 5 +++-- trezorlib/tests/device_tests/test_msg_resetdevice.py | 3 ++- .../device_tests/test_msg_resetdevice_skipbackup.py | 3 ++- .../tests/device_tests/test_msg_resetdevice_t2.py | 3 ++- trezorlib/tests/device_tests/test_msg_signidentity.py | 5 +++-- trezorlib/tests/device_tests/test_msg_signmessage.py | 3 ++- .../tests/device_tests/test_msg_signmessage_segwit.py | 3 ++- .../test_msg_signmessage_segwit_native.py | 3 ++- .../tests/device_tests/test_msg_signtx_decred.py | 2 +- trezorlib/tests/device_tests/test_msg_signtx_zcash.py | 11 ++++++----- .../tests/device_tests/test_msg_verifymessage.py | 4 ++-- .../device_tests/test_msg_verifymessage_segwit.py | 4 ++-- .../test_msg_verifymessage_segwit_native.py | 4 ++-- trezorlib/tests/device_tests/test_msg_wipedevice.py | 2 +- trezorlib/tests/device_tests/test_op_return.py | 3 ++- trezorlib/tests/device_tests/test_protect_call.py | 6 +++--- .../tests/device_tests/test_protection_levels.py | 4 +++- trezorlib/tests/device_tests/test_zerosig.py | 7 ++----- 36 files changed, 83 insertions(+), 58 deletions(-) diff --git a/trezorlib/tests/device_tests/test_basic.py b/trezorlib/tests/device_tests/test_basic.py index 902b385351..fbba93212e 100644 --- a/trezorlib/tests/device_tests/test_basic.py +++ b/trezorlib/tests/device_tests/test_basic.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from .common import TrezorTest from trezorlib import messages diff --git a/trezorlib/tests/device_tests/test_bip32_speed.py b/trezorlib/tests/device_tests/test_bip32_speed.py index 11c7ecbf88..04ffa79001 100644 --- a/trezorlib/tests/device_tests/test_bip32_speed.py +++ b/trezorlib/tests/device_tests/test_bip32_speed.py @@ -15,11 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . - -from __future__ import print_function - -from .common import * import time +import pytest + +from .common import TrezorTest class TestBip32Speed(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_debuglink.py b/trezorlib/tests/device_tests/test_debuglink.py index 795d2f9668..ab9dc91776 100644 --- a/trezorlib/tests/device_tests/test_debuglink.py +++ b/trezorlib/tests/device_tests/test_debuglink.py @@ -15,9 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * - +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_applysettings.py b/trezorlib/tests/device_tests/test_msg_applysettings.py index 07bf805d47..c5511658c9 100644 --- a/trezorlib/tests/device_tests/test_msg_applysettings.py +++ b/trezorlib/tests/device_tests/test_msg_applysettings.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest import time from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_changepin.py b/trezorlib/tests/device_tests/test_msg_changepin.py index bab268044c..3d40bbafbb 100644 --- a/trezorlib/tests/device_tests/test_msg_changepin.py +++ b/trezorlib/tests/device_tests/test_msg_changepin.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_cipherkeyvalue.py b/trezorlib/tests/device_tests/test_msg_cipherkeyvalue.py index de01f1e141..174145a3e2 100644 --- a/trezorlib/tests/device_tests/test_msg_cipherkeyvalue.py +++ b/trezorlib/tests/device_tests/test_msg_cipherkeyvalue.py @@ -15,8 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify, unhexlify +import pytest -from .common import * +from .common import TrezorTest class TestMsgCipherkeyvalue(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_clearsession.py b/trezorlib/tests/device_tests/test_msg_clearsession.py index 4452fd12bc..64c90db976 100644 --- a/trezorlib/tests/device_tests/test_msg_clearsession.py +++ b/trezorlib/tests/device_tests/test_msg_clearsession.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_ethereum_getaddress.py b/trezorlib/tests/device_tests/test_msg_ethereum_getaddress.py index b1e04f28f0..19cce76c69 100644 --- a/trezorlib/tests/device_tests/test_msg_ethereum_getaddress.py +++ b/trezorlib/tests/device_tests/test_msg_ethereum_getaddress.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify -from .common import * +from .common import TrezorTest class TestMsgEthereumGetaddress(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_ethereum_signmessage.py b/trezorlib/tests/device_tests/test_msg_ethereum_signmessage.py index 24531b21c4..6e55deae29 100644 --- a/trezorlib/tests/device_tests/test_msg_ethereum_signmessage.py +++ b/trezorlib/tests/device_tests/test_msg_ethereum_signmessage.py @@ -14,8 +14,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify +import pytest -from .common import * +from .common import TrezorTest @pytest.mark.skip_t2 diff --git a/trezorlib/tests/device_tests/test_msg_ethereum_signtx.py b/trezorlib/tests/device_tests/test_msg_ethereum_signtx.py index 825848ecca..d6188bc1dc 100644 --- a/trezorlib/tests/device_tests/test_msg_ethereum_signtx.py +++ b/trezorlib/tests/device_tests/test_msg_ethereum_signtx.py @@ -15,8 +15,11 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify, hexlify -from .common import * +import pytest + +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_ethereum_verifymessage.py b/trezorlib/tests/device_tests/test_msg_ethereum_verifymessage.py index 7b27d24a69..94bf62a08e 100644 --- a/trezorlib/tests/device_tests/test_msg_ethereum_verifymessage.py +++ b/trezorlib/tests/device_tests/test_msg_ethereum_verifymessage.py @@ -14,8 +14,11 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from .common import * +import pytest + +from .common import TrezorTest @pytest.mark.skip_t2 diff --git a/trezorlib/tests/device_tests/test_msg_getecdhsessionkey.py b/trezorlib/tests/device_tests/test_msg_getecdhsessionkey.py index b411abc11f..c7049c5ffe 100644 --- a/trezorlib/tests/device_tests/test_msg_getecdhsessionkey.py +++ b/trezorlib/tests/device_tests/test_msg_getecdhsessionkey.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_getentropy.py b/trezorlib/tests/device_tests/test_msg_getentropy.py index 14a0df3bb6..bee7f6a182 100644 --- a/trezorlib/tests/device_tests/test_msg_getentropy.py +++ b/trezorlib/tests/device_tests/test_msg_getentropy.py @@ -16,10 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function import math -from .common import * +from .common import TrezorTest import trezorlib.messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_loaddevice.py b/trezorlib/tests/device_tests/test_msg_loaddevice.py index e52840883f..317132aee8 100644 --- a/trezorlib/tests/device_tests/test_msg_loaddevice.py +++ b/trezorlib/tests/device_tests/test_msg_loaddevice.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest @pytest.mark.skip_t2 diff --git a/trezorlib/tests/device_tests/test_msg_loaddevice_xprv.py b/trezorlib/tests/device_tests/test_msg_loaddevice_xprv.py index e28a633a6d..b7017f1ca8 100644 --- a/trezorlib/tests/device_tests/test_msg_loaddevice_xprv.py +++ b/trezorlib/tests/device_tests/test_msg_loaddevice_xprv.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest @pytest.mark.skip_t2 diff --git a/trezorlib/tests/device_tests/test_msg_ping.py b/trezorlib/tests/device_tests/test_msg_ping.py index e797e1dab5..c5c129a770 100644 --- a/trezorlib/tests/device_tests/test_msg_ping.py +++ b/trezorlib/tests/device_tests/test_msg_ping.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_recoverydevice.py b/trezorlib/tests/device_tests/test_msg_recoverydevice.py index 79eddecd0a..b8bd50b8cd 100644 --- a/trezorlib/tests/device_tests/test_msg_recoverydevice.py +++ b/trezorlib/tests/device_tests/test_msg_recoverydevice.py @@ -16,9 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_recoverydevice_dryrun.py b/trezorlib/tests/device_tests/test_msg_recoverydevice_dryrun.py index fed37ac871..0f575d3a10 100644 --- a/trezorlib/tests/device_tests/test_msg_recoverydevice_dryrun.py +++ b/trezorlib/tests/device_tests/test_msg_recoverydevice_dryrun.py @@ -16,9 +16,9 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_recoverydevice_t2.py b/trezorlib/tests/device_tests/test_msg_recoverydevice_t2.py index 88240219a3..ee2e2d994b 100644 --- a/trezorlib/tests/device_tests/test_msg_recoverydevice_t2.py +++ b/trezorlib/tests/device_tests/test_msg_recoverydevice_t2.py @@ -16,11 +16,12 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function import time -from .common import * +import pytest + +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_resetdevice.py b/trezorlib/tests/device_tests/test_msg_resetdevice.py index b8a63a745c..2286b523c1 100644 --- a/trezorlib/tests/device_tests/test_msg_resetdevice.py +++ b/trezorlib/tests/device_tests/test_msg_resetdevice.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest, generate_entropy from trezorlib import messages as proto from mnemonic import Mnemonic diff --git a/trezorlib/tests/device_tests/test_msg_resetdevice_skipbackup.py b/trezorlib/tests/device_tests/test_msg_resetdevice_skipbackup.py index 8893710967..127bcb48ed 100644 --- a/trezorlib/tests/device_tests/test_msg_resetdevice_skipbackup.py +++ b/trezorlib/tests/device_tests/test_msg_resetdevice_skipbackup.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest -from .common import * +from .common import TrezorTest, generate_entropy from trezorlib import messages as proto from mnemonic import Mnemonic diff --git a/trezorlib/tests/device_tests/test_msg_resetdevice_t2.py b/trezorlib/tests/device_tests/test_msg_resetdevice_t2.py index 5fee646e15..823c69d5fd 100644 --- a/trezorlib/tests/device_tests/test_msg_resetdevice_t2.py +++ b/trezorlib/tests/device_tests/test_msg_resetdevice_t2.py @@ -17,8 +17,9 @@ # along with this library. If not, see . import time +import pytest -from .common import * +from .common import TrezorTest, generate_entropy from trezorlib import messages as proto from mnemonic import Mnemonic diff --git a/trezorlib/tests/device_tests/test_msg_signidentity.py b/trezorlib/tests/device_tests/test_msg_signidentity.py index 4d80d6477c..2985063f10 100644 --- a/trezorlib/tests/device_tests/test_msg_signidentity.py +++ b/trezorlib/tests/device_tests/test_msg_signidentity.py @@ -16,10 +16,11 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function import struct -from .common import * +from binascii import hexlify, unhexlify + +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_signmessage.py b/trezorlib/tests/device_tests/test_msg_signmessage.py index aac02275af..592f0c15c3 100644 --- a/trezorlib/tests/device_tests/test_msg_signmessage.py +++ b/trezorlib/tests/device_tests/test_msg_signmessage.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify -from .common import * +from .common import TrezorTest class TestMsgSignmessage(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_signmessage_segwit.py b/trezorlib/tests/device_tests/test_msg_signmessage_segwit.py index d46a9edf03..086d216750 100644 --- a/trezorlib/tests/device_tests/test_msg_signmessage_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_signmessage_segwit.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_signmessage_segwit_native.py b/trezorlib/tests/device_tests/test_msg_signmessage_segwit_native.py index b895c8f2e2..08e8629b7c 100644 --- a/trezorlib/tests/device_tests/test_msg_signmessage_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_signmessage_segwit_native.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import hexlify -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_msg_signtx_decred.py b/trezorlib/tests/device_tests/test_msg_signtx_decred.py index 445404f776..bc3c0c9957 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_decred.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_decred.py @@ -15,7 +15,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from binascii import hexlify, unhexlify +from binascii import unhexlify import pytest from .common import TrezorTest diff --git a/trezorlib/tests/device_tests/test_msg_signtx_zcash.py b/trezorlib/tests/device_tests/test_msg_signtx_zcash.py index e4dac926e2..aec7a76ad2 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_zcash.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_zcash.py @@ -15,12 +15,14 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify, hexlify +import pytest - -from .common import * +from .common import TrezorTest from trezorlib import coins from trezorlib import messages as proto +from trezorlib.tools import parse_path TxApiZcash = coins.tx_api["Zcash"] @@ -28,8 +30,7 @@ TxApiZcash = coins.tx_api["Zcash"] TXHASH_93373e = unhexlify('93373e63cc626c4a7d049ad775d6511bb5eba985f142db660c9b9f955c722f5c') -@pytest.mark.skip_t1 -@pytest.mark.skip_t2 +@pytest.mark.skip(reason="Broken until we have support for Zcash v3 transactions.") class TestMsgSigntxZcash(TrezorTest): def test_one_one_fee(self): @@ -39,7 +40,7 @@ class TestMsgSigntxZcash(TrezorTest): # input 0: 1.234567 TAZ inp1 = proto.TxInputType( - address_n=[2147483692, 2147483649, 2147483648, 0, 0], # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu + address_n=parse_path("m/Zcash Testnet/0h/0/0"), # tmQoJ3PTXgQLaRRZZYT6xk8XtjRbr2kCqwu # amount=123456700, prev_hash=TXHASH_93373e, prev_index=0, diff --git a/trezorlib/tests/device_tests/test_msg_verifymessage.py b/trezorlib/tests/device_tests/test_msg_verifymessage.py index ba51f4a58b..7c64670b0d 100644 --- a/trezorlib/tests/device_tests/test_msg_verifymessage.py +++ b/trezorlib/tests/device_tests/test_msg_verifymessage.py @@ -15,9 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from .common import * -import base64 +from .common import TrezorTest class TestMsgVerifymessage(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_verifymessage_segwit.py b/trezorlib/tests/device_tests/test_msg_verifymessage_segwit.py index ba8cf46c2b..15c21b03b0 100644 --- a/trezorlib/tests/device_tests/test_msg_verifymessage_segwit.py +++ b/trezorlib/tests/device_tests/test_msg_verifymessage_segwit.py @@ -15,9 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from .common import * -import base64 +from .common import TrezorTest class TestMsgVerifymessageSegwit(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_verifymessage_segwit_native.py b/trezorlib/tests/device_tests/test_msg_verifymessage_segwit_native.py index fa2beb084a..dbaa4bd107 100644 --- a/trezorlib/tests/device_tests/test_msg_verifymessage_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_verifymessage_segwit_native.py @@ -15,9 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from .common import * -import base64 +from .common import TrezorTest class TestMsgVerifymessageSegwitNative(TrezorTest): diff --git a/trezorlib/tests/device_tests/test_msg_wipedevice.py b/trezorlib/tests/device_tests/test_msg_wipedevice.py index a089de6825..2a5f259eb7 100644 --- a/trezorlib/tests/device_tests/test_msg_wipedevice.py +++ b/trezorlib/tests/device_tests/test_msg_wipedevice.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_op_return.py b/trezorlib/tests/device_tests/test_op_return.py index e4a38bdc98..bfcc973e38 100644 --- a/trezorlib/tests/device_tests/test_op_return.py +++ b/trezorlib/tests/device_tests/test_op_return.py @@ -15,8 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify, hexlify -from .common import * +from .common import TrezorTest from trezorlib import messages as proto from trezorlib.client import CallException diff --git a/trezorlib/tests/device_tests/test_protect_call.py b/trezorlib/tests/device_tests/test_protect_call.py index 3b7980e9b8..10fe06ee12 100644 --- a/trezorlib/tests/device_tests/test_protect_call.py +++ b/trezorlib/tests/device_tests/test_protect_call.py @@ -16,10 +16,10 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function - import time -from .common import * +import pytest + +from .common import TrezorTest from trezorlib import messages as proto from trezorlib.client import PinException, CallException diff --git a/trezorlib/tests/device_tests/test_protection_levels.py b/trezorlib/tests/device_tests/test_protection_levels.py index c3fa7418b6..ba944be418 100644 --- a/trezorlib/tests/device_tests/test_protection_levels.py +++ b/trezorlib/tests/device_tests/test_protection_levels.py @@ -15,8 +15,10 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify +import pytest -from .common import * +from .common import TrezorTest from trezorlib import messages as proto diff --git a/trezorlib/tests/device_tests/test_zerosig.py b/trezorlib/tests/device_tests/test_zerosig.py index 507ef7d719..5b65a1f181 100644 --- a/trezorlib/tests/device_tests/test_zerosig.py +++ b/trezorlib/tests/device_tests/test_zerosig.py @@ -15,12 +15,9 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +from binascii import unhexlify -from __future__ import print_function - -import sys -from .common import * - +from .common import TrezorTest from trezorlib import messages as proto From a2b70562bced99b8b44cf430962b4f7b3823f927 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 11 May 2018 14:56:20 +0200 Subject: [PATCH 13/29] flake8: be stricter --- .flake8 | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.flake8 b/.flake8 index a89b939558..5fd5447939 100644 --- a/.flake8 +++ b/.flake8 @@ -10,18 +10,12 @@ exclude = ignore = # F401: module imported but unused F401, - # F403: used import * - F403, - # F405 'foo' may be undefined, or defined from star imports - F405, # E241: multiple spaces after ':' E241, # E402: module level import not at top of file E402, # E501: line too long E501, - # E721: do not compare types, use 'isinstance()' - E721, # E722: do not use bare except E722, # E741: ambiguous variable name From a478dac5f7a76882d0091a0f7a1b7e6b762e66a6 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 11 May 2018 15:24:24 +0200 Subject: [PATCH 14/29] client: clean up logging and printing --- trezorlib/client.py | 69 +++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index e4da85ff0b..03053949a9 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . -from __future__ import print_function, absolute_import - +import functools +import logging import os import sys import time @@ -43,7 +43,7 @@ if sys.version_info.major < 3: SCREENSHOT = False - +LOG = logging.getLogger(__name__) # make a getch function try: @@ -82,22 +82,6 @@ def get_buttonrequest_value(code): return [k for k in dir(proto.ButtonRequestType) if getattr(proto.ButtonRequestType, k) == code][0] -def pprint(msg): - msg_class = msg.__class__.__name__ - msg_size = msg.ByteSize() - if isinstance(msg, proto.FirmwareUpload) or isinstance(msg, proto.SelfTest) \ - or isinstance(msg, proto.Features): - return "<%s> (%d bytes)" % (msg_class, msg_size) - else: - return "<%s> (%d bytes):\n%s" % (msg_class, msg_size, protobuf.format_message(msg)) - - -def log(msg): - sys.stderr.write(msg) - sys.stderr.write('\n') - sys.stderr.flush() - - class CallException(Exception): def __init__(self, code, message): super(CallException, self).__init__() @@ -220,6 +204,10 @@ class TextUIMixin(object): def __init__(self, *args, **kwargs): super(TextUIMixin, self).__init__(*args, **kwargs) + @staticmethod + def print(text): + print(text, file=sys.stderr) + def callback_ButtonRequest(self, msg): # log("Sending ButtonAck for %s " % get_buttonrequest_value(msg.code)) return proto.ButtonAck() @@ -227,11 +215,11 @@ class TextUIMixin(object): def callback_RecoveryMatrix(self, msg): if self.recovery_matrix_first_pass: self.recovery_matrix_first_pass = False - log("Use the numeric keypad to describe positions. For the word list use only left and right keys.") - log("Use backspace to correct an entry. The keypad layout is:") - log(" 7 8 9 7 | 9") - log(" 4 5 6 4 | 6") - log(" 1 2 3 1 | 3") + self.print("Use the numeric keypad to describe positions. For the word list use only left and right keys.") + self.print("Use backspace to correct an entry. The keypad layout is:") + self.print(" 7 8 9 7 | 9") + self.print(" 4 5 6 4 | 6") + self.print(" 1 2 3 1 | 3") while True: character = getch() if character in ('\x03', '\x04'): @@ -257,11 +245,11 @@ class TextUIMixin(object): else: desc = 'PIN' - log("Use the numeric keypad to describe number positions. The layout is:") - log(" 7 8 9") - log(" 4 5 6") - log(" 1 2 3") - log("Please enter %s: " % desc) + self.print("Use the numeric keypad to describe number positions. The layout is:") + self.print(" 7 8 9") + self.print(" 4 5 6") + self.print(" 1 2 3") + self.print("Please enter %s: " % desc) pin = getpass.getpass('') if not pin.isdigit(): raise ValueError('Non-numerical PIN provided') @@ -272,18 +260,18 @@ class TextUIMixin(object): return proto.PassphraseAck() if os.getenv("PASSPHRASE") is not None: - log("Passphrase required. Using PASSPHRASE environment variable.") + self.print("Passphrase required. Using PASSPHRASE environment variable.") passphrase = Mnemonic.normalize_string(os.getenv("PASSPHRASE")) return proto.PassphraseAck(passphrase=passphrase) - log("Passphrase required: ") + self.print("Passphrase required: ") passphrase = getpass.getpass('') - log("Confirm your Passphrase: ") + self.print("Confirm your Passphrase: ") if passphrase == getpass.getpass(''): passphrase = Mnemonic.normalize_string(passphrase) return proto.PassphraseAck(passphrase=passphrase) else: - log("Passphrase did not match! ") + self.print("Passphrase did not match! ") exit() def callback_PassphraseStateRequest(self, msg): @@ -293,7 +281,7 @@ class TextUIMixin(object): if msg.type in (proto.WordRequestType.Matrix9, proto.WordRequestType.Matrix6): return self.callback_RecoveryMatrix(msg) - log("Enter one word of mnemonic: ") + self.print("Enter one word of mnemonic: ") word = input() if self.expand: word = self.mnemonic_wordlist.expand_word(word) @@ -310,6 +298,7 @@ class DebugLinkMixin(object): # of unit testing, because it will fail to work # without special DebugLink interface provided # by the device. + DEBUG = LOG.getChild('debug_link').debug def __init__(self, *args, **kwargs): super(DebugLinkMixin, self).__init__(*args, **kwargs) @@ -414,11 +403,11 @@ class DebugLinkMixin(object): "Expected %s, got %s" % (repr(expected), repr(msg))) def callback_ButtonRequest(self, msg): - log("ButtonRequest code: " + get_buttonrequest_value(msg.code)) + self.DEBUG("ButtonRequest code: " + get_buttonrequest_value(msg.code)) - log("Pressing button " + str(self.button)) + self.DEBUG("Pressing button " + str(self.button)) if self.button_wait: - log("Waiting %d seconds " % self.button_wait) + self.DEBUG("Waiting %d seconds " % self.button_wait) time.sleep(self.button_wait) self.debug.press_button(self.button) return proto.ButtonAck() @@ -431,7 +420,7 @@ class DebugLinkMixin(object): return proto.PinMatrixAck(pin=pin) def callback_PassphraseRequest(self, msg): - log("Provided passphrase: '%s'" % self.passphrase) + self.DEBUG("Provided passphrase: '%s'" % self.passphrase) return proto.PassphraseAck(passphrase=self.passphrase) def callback_PassphraseStateRequest(self, msg): @@ -973,7 +962,7 @@ class ProtocolMixin(object): raise RuntimeError("Invalid response, expected EntropyRequest") external_entropy = self._get_local_entropy() - log("Computer generated entropy: " + binascii.hexlify(external_entropy).decode()) + LOG.debug("Computer generated entropy: " + binascii.hexlify(external_entropy).decode()) ret = self.call(proto.EntropyAck(entropy=external_entropy)) self.init_device() return ret @@ -1070,7 +1059,7 @@ class ProtocolMixin(object): # TREZORv1 method if isinstance(resp, proto.Success): fingerprint = hashlib.sha256(data[256:]).hexdigest() - log("Firmware fingerprint: " + fingerprint) + LOG.debug("Firmware fingerprint: " + fingerprint) resp = self.call(proto.FirmwareUpload(payload=data)) if isinstance(resp, proto.Success): return True From cc7c8ccb59d4692244a320c6a0d1b16512741474 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 11 May 2018 15:27:39 +0200 Subject: [PATCH 15/29] client: clean up exception and decorator code --- trezorlib/client.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index 03053949a9..c27fb8393e 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -83,21 +83,14 @@ def get_buttonrequest_value(code): class CallException(Exception): - def __init__(self, code, message): - super(CallException, self).__init__() - self.args = [code, message] - - -class AssertionException(Exception): - def __init__(self, code, message): - self.args = [code, message] + pass class PinException(CallException): pass -class field(object): +class field: # Decorator extracts single value from # protobuf object. If the field is not # present, raises an exception. @@ -105,13 +98,14 @@ class field(object): self.field = field def __call__(self, f): + @functools.wraps(f) def wrapped_f(*args, **kwargs): ret = f(*args, **kwargs) return getattr(ret, self.field) return wrapped_f -class expect(object): +class expect: # Decorator checks if the method # returned one of expected protobuf messages # or raises an exception @@ -119,6 +113,7 @@ class expect(object): self.expected = expected def __call__(self, f): + @functools.wraps(f) def wrapped_f(*args, **kwargs): ret = f(*args, **kwargs) if not isinstance(ret, self.expected): @@ -130,6 +125,7 @@ class expect(object): def session(f): # Decorator wraps a BaseClient method # with session activation / deactivation + @functools.wraps(f) def wrapped_f(*args, **kwargs): client = args[0] client.transport.session_begin() @@ -388,19 +384,19 @@ class DebugLinkMixin(object): try: expected = self.expected_responses.pop(0) except IndexError: - raise AssertionException(proto.FailureType.UnexpectedMessage, - "Got %s, but no message has been expected" % repr(msg)) + raise AssertionError(proto.FailureType.UnexpectedMessage, + "Got %s, but no message has been expected" % repr(msg)) if msg.__class__ != expected.__class__: - raise AssertionException(proto.FailureType.UnexpectedMessage, - "Expected %s, got %s" % (repr(expected), repr(msg))) + raise AssertionError(proto.FailureType.UnexpectedMessage, + "Expected %s, got %s" % (repr(expected), repr(msg))) for field, value in expected.__dict__.items(): if value is None or value == []: continue if getattr(msg, field) != value: - raise AssertionException(proto.FailureType.UnexpectedMessage, - "Expected %s, got %s" % (repr(expected), repr(msg))) + raise AssertionError(proto.FailureType.UnexpectedMessage, + "Expected %s, got %s" % (repr(expected), repr(msg))) def callback_ButtonRequest(self, msg): self.DEBUG("ButtonRequest code: " + get_buttonrequest_value(msg.code)) From f9c20f8d2fd73977474ec4e44dadfbbb5e48d58c Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 11 May 2018 16:01:11 +0200 Subject: [PATCH 16/29] travis: do not trigger trezor-core if not master --- trigger-travis.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/trigger-travis.sh b/trigger-travis.sh index 238f34b96c..d9cd2388db 100755 --- a/trigger-travis.sh +++ b/trigger-travis.sh @@ -12,6 +12,11 @@ if [ "$TRAVIS_REPO_SLUG" != "$SOURCE" ]; then exit 0; fi +if [ "$TRAVIS_BRANCH" != "master" ]; then + echo "not triggering from non-master branch" + exit 0; +fi + if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "not triggering from pull requests" exit 0; From db92b13f9788f7224782a9635fc76ee3593e577c Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 17 May 2018 12:53:01 +0200 Subject: [PATCH 17/29] cosi: move things around ed25519raw is moved back to trezorlib ed25519cosi is renamed to cosi, and has a couple more functions, with the expectation that TrezorClient.cosi_* methods will move there. Also most code shouldn't need ed25519raw for anything, so it might get renamed to "_ed25519" to indicate that it's a private implementation. For now, I added a "verify" method to cosi, so that you don't need to call into ed25519raw.checkvalid. But trezor-core's keyctl is also using ed25519raw.publickey. I'm not sure if that's worth replicating in cosi, or whether to just leave it be, so I'm leaving it be for now. Importantly, new function "sign_with_privkey" does that math thing that was part of the selftest and is also explicitly listed in keyctl. (it's called sign_with_privkey because I expect to have a "sign" method here that calls into Trezor) --- .../{tests/support/ed25519cosi.py => cosi.py} | 37 ++++++++++--------- trezorlib/{tests/support => }/ed25519raw.py | 0 trezorlib/tests/device_tests/test_cosi.py | 10 ++--- 3 files changed, 25 insertions(+), 22 deletions(-) rename trezorlib/{tests/support/ed25519cosi.py => cosi.py} (71%) rename trezorlib/{tests/support => }/ed25519raw.py (100%) diff --git a/trezorlib/tests/support/ed25519cosi.py b/trezorlib/cosi.py similarity index 71% rename from trezorlib/tests/support/ed25519cosi.py rename to trezorlib/cosi.py index 645dc29b5d..0c7a22c87d 100644 --- a/trezorlib/tests/support/ed25519cosi.py +++ b/trezorlib/cosi.py @@ -2,7 +2,7 @@ import sys from functools import reduce import binascii -from . import ed25519raw +from trezorlib import ed25519raw def combine_keys(pks): @@ -26,13 +26,24 @@ def get_nonce(sk, data, ctr): return r, ed25519raw.encodepoint(R) +def verify(signature, digest, pub_key): + ed25519raw.checkvalid(signature, digest, pub_key) + + +def sign_with_privkey(digest, privkey, global_pubkey, nonce, global_commit): + h = ed25519raw.H(privkey) + b = ed25519raw.b + a = 2 ** (b - 2) + sum(2 ** i * ed25519raw.bit(h, i) for i in range(3, b - 2)) + S = (nonce + ed25519raw.Hint(global_commit + global_pubkey + digest) * a) % ed25519raw.l + return ed25519raw.encodeint(S) + + def self_test(digest): def to_hex(by): return binascii.hexlify(by).decode() N = 3 - keyset = [0, 2] digest = binascii.unhexlify(digest) print('Digest: %s' % to_hex(digest)) @@ -56,29 +67,21 @@ def self_test(digest): nonces.append(r) commits.append(R) - global_pk = combine_keys([pks[i] for i in keyset]) - global_R = combine_keys([commits[i] for i in keyset]) + global_pk = combine_keys(pks) + global_R = combine_keys(commits) print('-----------------') print('Global pubkey: %s' % to_hex(global_pk)) print('Global commit: %s' % to_hex(global_R)) print('-----------------') - for i in range(0, N): - seckey = sks[i] - pubkey = pks[i] - r = nonces[i] - R = commits[i] - h = ed25519raw.H(seckey) - b = ed25519raw.b - a = 2**(b - 2) + sum(2 ** i * ed25519raw.bit(h, i) for i in range(3, b - 2)) - S = (r + ed25519raw.Hint(global_R + global_pk + digest) * a) % ed25519raw.l - print('Local sig %d: %s' % (i + 1, to_hex(ed25519raw.encodeint(S)))) - sigs.append(ed25519raw.encodeint(S)) + sigs = [sign_with_privkey(digest, sks[i], global_pk, nonces[i], global_R) for i in range(N)] + for sig in sigs: + print('Local signature: %s' % to_hex(sig)) print('-----------------') - sig = combine_sig(global_R, [sigs[i] for i in keyset]) + sig = combine_sig(global_R, sigs) print('Global sig: %s' % to_hex(sig)) - ed25519raw.checkvalid(sig, digest, global_pk) + verify(sig, digest, global_pk) print('Valid Signature!') diff --git a/trezorlib/tests/support/ed25519raw.py b/trezorlib/ed25519raw.py similarity index 100% rename from trezorlib/tests/support/ed25519raw.py rename to trezorlib/ed25519raw.py diff --git a/trezorlib/tests/device_tests/test_cosi.py b/trezorlib/tests/device_tests/test_cosi.py index 33e7ff9afa..45807915aa 100644 --- a/trezorlib/tests/device_tests/test_cosi.py +++ b/trezorlib/tests/device_tests/test_cosi.py @@ -20,7 +20,7 @@ import pytest from hashlib import sha256 from .common import TrezorTest -from ..support import ed25519cosi, ed25519raw +from trezorlib import cosi from trezorlib.tools import parse_path @@ -68,13 +68,13 @@ class TestCosi(TrezorTest): c1 = self.client.cosi_commit(parse_path("10018'/1'"), digest) c2 = self.client.cosi_commit(parse_path("10018'/2'"), digest) - global_pk = ed25519cosi.combine_keys([c0.pubkey, c1.pubkey, c2.pubkey]) - global_R = ed25519cosi.combine_keys([c0.commitment, c1.commitment, c2.commitment]) + global_pk = cosi.combine_keys([c0.pubkey, c1.pubkey, c2.pubkey]) + global_R = cosi.combine_keys([c0.commitment, c1.commitment, c2.commitment]) sig0 = self.client.cosi_sign(parse_path("10018'/0'"), digest, global_R, global_pk) sig1 = self.client.cosi_sign(parse_path("10018'/1'"), digest, global_R, global_pk) sig2 = self.client.cosi_sign(parse_path("10018'/2'"), digest, global_R, global_pk) - sig = ed25519cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature]) + sig = cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature]) - ed25519raw.checkvalid(sig, digest, global_pk) + cosi.verify(sig, digest, global_pk) From 97fa4670ac1565b5b172112dcbb9c1249cbab048 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 24 May 2018 17:55:41 +0200 Subject: [PATCH 18/29] transport: add message logging to bridge --- trezorlib/transport/bridge.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/trezorlib/transport/bridge.py b/trezorlib/transport/bridge.py index cb38ec64ff..898e6a62c4 100644 --- a/trezorlib/transport/bridge.py +++ b/trezorlib/transport/bridge.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import logging import requests import binascii from io import BytesIO @@ -27,6 +28,8 @@ from .. import messages from .. import protobuf from . import Transport, TransportException +LOG = logging.getLogger(__name__) + TREZORD_HOST = 'http://127.0.0.1:21325' @@ -78,6 +81,8 @@ class BridgeTransport(Transport): self.session = None def write(self, msg): + LOG.debug("sending message: {}".format(msg.__class__.__name__), + extra={'protobuf': msg}) data = BytesIO() protobuf.dump_message(data, msg) ser = data.getvalue() @@ -97,5 +102,7 @@ class BridgeTransport(Transport): (msg_type, datalen) = struct.unpack('>HL', data[:headerlen]) data = BytesIO(data[headerlen:headerlen + datalen]) msg = protobuf.load_message(data, mapping.get_class(msg_type)) + LOG.debug("received message: {}".format(msg.__class__.__name__), + extra={'protobuf': msg}) self.response = None return msg From e779a251fb95c6a23daa26f3827e549d24090638 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 24 May 2018 19:14:05 +0200 Subject: [PATCH 19/29] transport: better ways to handle errors when enumerating devices --- trezorlib/client.py | 1 + trezorlib/transport/__init__.py | 69 ++++++++++++++++++--------------- trezorlib/transport/bridge.py | 3 ++ trezorlib/transport/hid.py | 3 ++ trezorlib/transport/udp.py | 3 ++ trezorlib/transport/webusb.py | 3 ++ 6 files changed, 50 insertions(+), 32 deletions(-) diff --git a/trezorlib/client.py b/trezorlib/client.py index c27fb8393e..b0b9780696 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -150,6 +150,7 @@ class BaseClient(object): # Implements very basic layer of sending raw protobuf # messages to device and getting its response back. def __init__(self, transport, **kwargs): + LOG.info("creating client instance for device: {}".format(transport.get_path())) self.transport = transport super(BaseClient, self).__init__() # *args, **kwargs) diff --git a/trezorlib/transport/__init__.py b/trezorlib/transport/__init__.py index e6a4bca13e..0aa22a92e9 100644 --- a/trezorlib/transport/__init__.py +++ b/trezorlib/transport/__init__.py @@ -17,6 +17,13 @@ # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import importlib +import logging + +from typing import Iterable, Type, List, Set + +LOG = logging.getLogger(__name__) + class TransportException(Exception): pass @@ -63,54 +70,52 @@ class Transport(object): raise TransportException('{} device not found: {}'.format(cls.PATH_PREFIX, path)) -def all_transports(): - transports = [] - try: - from .bridge import BridgeTransport - transports.append(BridgeTransport) - except: - pass - - try: - from .hid import HidTransport - transports.append(HidTransport) - except: - pass - - try: - from .udp import UdpTransport - transports.append(UdpTransport) - except: - pass - - try: - from .webusb import WebUsbTransport - transports.append(WebUsbTransport) - except: - pass +def all_transports() -> Iterable[Type[Transport]]: + transports = set() # type: Set[Type[Transport]] + for modname in ("bridge", "hid", "udp", "webusb"): + try: + # Import the module and find the Transport class. + # To avoid iterating over every item, the module should assign its Transport class + # to a constant named TRANSPORT. + module = importlib.import_module("." + modname, __name__) + try: + transports.add(getattr(module, "TRANSPORT")) + except AttributeError: + LOG.warning("Skipping broken module {}".format(modname)) + except ImportError as e: + LOG.info("Failed to import module {}: {}".format(modname, e)) return transports -def enumerate_devices(): - return [device - for transport in all_transports() - for device in transport.enumerate()] +def enumerate_devices() -> Iterable[Transport]: + devices = [] # type: List[Transport] + for transport in all_transports(): + try: + found = transport.enumerate() + LOG.info("Enumerating {}: found {} devices".format(transport.__name__, len(found))) + devices.extend(found) + except NotImplementedError: + LOG.error("{} does not implement device enumeration".format(transport.__name__)) + except Exception as e: + LOG.error("Failed to enumerate {}. {}: {}".format(transport.__name__, e.__class__.__name__, e)) + return devices -def get_transport(path=None, prefix_search=False): +def get_transport(path: str = None, prefix_search: bool = False) -> Transport: if path is None: try: - return enumerate_devices()[0] + return next(iter(enumerate_devices())) except IndexError: raise Exception("No TREZOR device found") from None # Find whether B is prefix of A (transport name is part of the path) # or A is prefix of B (path is a prefix, or a name, of transport). # This naively expects that no two transports have a common prefix. - def match_prefix(a, b): + def match_prefix(a: str, b: str) -> bool: return a.startswith(b) or b.startswith(a) + LOG.info("looking for device by {}: {}".format("prefix" if prefix_search else "full path", path)) transports = [t for t in all_transports() if match_prefix(path, t.PATH_PREFIX)] if transports: return transports[0].find_by_path(path, prefix_search=prefix_search) diff --git a/trezorlib/transport/bridge.py b/trezorlib/transport/bridge.py index 898e6a62c4..4b20780abe 100644 --- a/trezorlib/transport/bridge.py +++ b/trezorlib/transport/bridge.py @@ -106,3 +106,6 @@ class BridgeTransport(Transport): extra={'protobuf': msg}) self.response = None return msg + + +TRANSPORT = BridgeTransport diff --git a/trezorlib/transport/hid.py b/trezorlib/transport/hid.py index 9a47354ab8..991998a826 100644 --- a/trezorlib/transport/hid.py +++ b/trezorlib/transport/hid.py @@ -180,3 +180,6 @@ def is_wirelink(dev): def is_debuglink(dev): return (dev['usage_page'] == 0xFF01 or dev['interface_number'] == 1) + + +TRANSPORT = HidTransport diff --git a/trezorlib/transport/udp.py b/trezorlib/transport/udp.py index ffcfb250f6..631cd0a18e 100644 --- a/trezorlib/transport/udp.py +++ b/trezorlib/transport/udp.py @@ -124,3 +124,6 @@ class UdpTransport(Transport): if len(chunk) != 64: raise TransportException('Unexpected chunk size: %d' % len(chunk)) return bytearray(chunk) + + +TRANSPORT = UdpTransport diff --git a/trezorlib/transport/webusb.py b/trezorlib/transport/webusb.py index b9782f748d..81ea75a713 100644 --- a/trezorlib/transport/webusb.py +++ b/trezorlib/transport/webusb.py @@ -188,3 +188,6 @@ def is_vendor_class(dev): def dev_to_str(dev): return ':'.join(str(x) for x in ['%03i' % (dev.getBusNumber(), )] + dev.getPortNumberList()) + + +TRANSPORT = WebUsbTransport From 886e269e2f1993f12c0729c7f3eeb5c194f30973 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 25 May 2018 13:07:02 +0200 Subject: [PATCH 20/29] trezorlib: mark ed25519 impl as private --- trezorlib/{ed25519raw.py => _ed25519.py} | 0 trezorlib/cosi.py | 40 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) rename trezorlib/{ed25519raw.py => _ed25519.py} (100%) diff --git a/trezorlib/ed25519raw.py b/trezorlib/_ed25519.py similarity index 100% rename from trezorlib/ed25519raw.py rename to trezorlib/_ed25519.py diff --git a/trezorlib/cosi.py b/trezorlib/cosi.py index 0c7a22c87d..03f7521483 100644 --- a/trezorlib/cosi.py +++ b/trezorlib/cosi.py @@ -2,40 +2,40 @@ import sys from functools import reduce import binascii -from trezorlib import ed25519raw +from trezorlib import _ed25519 def combine_keys(pks): - P = [ed25519raw.decodepoint(pk) for pk in pks] - combine = reduce(ed25519raw.edwards, P) - return ed25519raw.encodepoint(combine) + P = [_ed25519.decodepoint(pk) for pk in pks] + combine = reduce(_ed25519.edwards, P) + return _ed25519.encodepoint(combine) def combine_sig(R, sigs): - S = [ed25519raw.decodeint(si) for si in sigs] - s = sum(S) % ed25519raw.l - sig = R + ed25519raw.encodeint(s) + S = [_ed25519.decodeint(si) for si in sigs] + s = sum(S) % _ed25519.l + sig = R + _ed25519.encodeint(s) return sig def get_nonce(sk, data, ctr): - h = ed25519raw.H(sk) - b = ed25519raw.b - r = ed25519raw.Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + data + binascii.unhexlify('%08x' % ctr)) - R = ed25519raw.scalarmult(ed25519raw.B, r) - return r, ed25519raw.encodepoint(R) + h = _ed25519.H(sk) + b = _ed25519.b + r = _ed25519.Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + data + binascii.unhexlify('%08x' % ctr)) + R = _ed25519.scalarmult(_ed25519.B, r) + return r, _ed25519.encodepoint(R) def verify(signature, digest, pub_key): - ed25519raw.checkvalid(signature, digest, pub_key) + _ed25519.checkvalid(signature, digest, pub_key) def sign_with_privkey(digest, privkey, global_pubkey, nonce, global_commit): - h = ed25519raw.H(privkey) - b = ed25519raw.b - a = 2 ** (b - 2) + sum(2 ** i * ed25519raw.bit(h, i) for i in range(3, b - 2)) - S = (nonce + ed25519raw.Hint(global_commit + global_pubkey + digest) * a) % ed25519raw.l - return ed25519raw.encodeint(S) + h = _ed25519.H(privkey) + b = _ed25519.b + a = 2 ** (b - 2) + sum(2 ** i * _ed25519.bit(h, i) for i in range(3, b - 2)) + S = (nonce + _ed25519.Hint(global_commit + global_pubkey + digest) * a) % _ed25519.l + return _ed25519.encodeint(S) def self_test(digest): @@ -55,14 +55,14 @@ def self_test(digest): for i in range(0, N): print('----- Key %d ------' % (i + 1)) seckey = (chr(0x41 + i) * 32).encode() - pubkey = ed25519raw.publickey(seckey) + pubkey = _ed25519.publickey(seckey) print('Secret Key: %s' % to_hex(seckey)) print('Public Key: %s' % to_hex(pubkey)) sks.append(seckey) pks.append(pubkey) ctr = 0 r, R = get_nonce(seckey, digest, ctr) - print('Local nonce: %s' % to_hex(ed25519raw.encodeint(r))) + print('Local nonce: %s' % to_hex(_ed25519.encodeint(r))) print('Local commit: %s' % to_hex(R)) nonces.append(r) commits.append(R) From 2fdb5cd5387b9c8aa91c54c450956c11086622b9 Mon Sep 17 00:00:00 2001 From: matejcik Date: Fri, 25 May 2018 13:12:42 +0200 Subject: [PATCH 21/29] cosi: publish "pubkey from privkey" operation from ed25519 --- trezorlib/cosi.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/trezorlib/cosi.py b/trezorlib/cosi.py index 03f7521483..b1a5cfcf85 100644 --- a/trezorlib/cosi.py +++ b/trezorlib/cosi.py @@ -30,6 +30,10 @@ def verify(signature, digest, pub_key): _ed25519.checkvalid(signature, digest, pub_key) +def pubkey_from_privkey(privkey): + return _ed25519.publickey(privkey) + + def sign_with_privkey(digest, privkey, global_pubkey, nonce, global_commit): h = _ed25519.H(privkey) b = _ed25519.b From 0e8fe9e743703a0ae47d0099cc8e08b3c28b4929 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 14:17:11 +0200 Subject: [PATCH 22/29] ed25519: type hints --- trezorlib/_ed25519.py | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/trezorlib/_ed25519.py b/trezorlib/_ed25519.py index 5bfa47bffa..7470dea1a4 100644 --- a/trezorlib/_ed25519.py +++ b/trezorlib/_ed25519.py @@ -2,17 +2,20 @@ # modified for Python 3 by Jochen Hoenicke import hashlib +from typing import Tuple, NewType + +Point = NewType("Point", Tuple[int, int]) b = 256 q = 2 ** 255 - 19 l = 2 ** 252 + 27742317777372353535851937790883648493 -def H(m): +def H(m: bytes) -> bytes: return hashlib.sha512(m).digest() -def expmod(b, e, m): +def expmod(b: int, e: int, m: int) -> int: if e < 0: raise ValueError('negative exponent') if e == 0: @@ -23,7 +26,7 @@ def expmod(b, e, m): return t -def inv(x): +def inv(x: int) -> int: return expmod(x, q - 2, q) @@ -31,7 +34,7 @@ d = -121665 * inv(121666) I = expmod(2, (q - 1) >> 2, q) -def xrecover(y): +def xrecover(y: int) -> int: xx = (y * y - 1) * inv(d * y * y + 1) x = expmod(xx, (q + 3) >> 3, q) if (x * x - xx) % q != 0: @@ -43,22 +46,22 @@ def xrecover(y): By = 4 * inv(5) Bx = xrecover(By) -B = [Bx % q, By % q] +B = Point((Bx % q, By % q)) -def edwards(P, Q): +def edwards(P: Point, Q: Point) -> Point: x1 = P[0] y1 = P[1] x2 = Q[0] y2 = Q[1] x3 = (x1 * y2 + x2 * y1) * inv(1 + d * x1 * x2 * y1 * y2) y3 = (y1 * y2 + x1 * x2) * inv(1 - d * x1 * x2 * y1 * y2) - return [x3 % q, y3 % q] + return Point((x3 % q, y3 % q)) -def scalarmult(P, e): +def scalarmult(P: Point, e: int) -> Point: if e == 0: - return [0, 1] + return Point((0, 1)) Q = scalarmult(P, e >> 1) Q = edwards(Q, Q) if e & 1: @@ -66,35 +69,35 @@ def scalarmult(P, e): return Q -def encodeint(y): +def encodeint(y: int) -> bytes: bits = [(y >> i) & 1 for i in range(b)] return bytes([sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b >> 3)]) -def encodepoint(P): +def encodepoint(P: Point) -> bytes: x = P[0] y = P[1] bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] return bytes([sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b >> 3)]) -def bit(h, i): +def bit(h: bytes, i: int) -> int: return (h[i >> 3] >> (i & 7)) & 1 -def publickey(sk): +def publickey(sk: bytes) -> bytes: h = H(sk) a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) A = scalarmult(B, a) return encodepoint(A) -def Hint(m): +def Hint(m: bytes) -> int: h = H(m) return sum(2 ** i * bit(h, i) for i in range(2 * b)) -def signature(m, sk, pk): +def signature(m: bytes, sk: bytes, pk: bytes) -> bytes: h = H(sk) a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) r = Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + m) @@ -103,28 +106,28 @@ def signature(m, sk, pk): return encodepoint(R) + encodeint(S) -def isoncurve(P): +def isoncurve(P: Point) -> bool: x = P[0] y = P[1] return (-x * x + y * y - 1 - d * x * x * y * y) % q == 0 -def decodeint(s): +def decodeint(s: bytes) -> int: return sum(2 ** i * bit(s, i) for i in range(0, b)) -def decodepoint(s): +def decodepoint(s: bytes) -> Point: y = sum(2 ** i * bit(s, i) for i in range(0, b - 1)) x = xrecover(y) if x & 1 != bit(s, b - 1): x = q - x - P = [x, y] + P = Point((x, y)) if not isoncurve(P): raise ValueError('decoding point that is not on curve') return P -def checkvalid(s, m, pk): +def checkvalid(s: bytes, m: bytes, pk: bytes) -> None: if len(s) != b >> 2: raise ValueError('signature length is wrong') if len(pk) != b >> 3: From a0f73b726d78e685c94beb978264b699a21fe850 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 14:20:26 +0200 Subject: [PATCH 23/29] cosi: typehints, documentation, removed selftest --- trezorlib/cosi.py | 108 ++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 66 deletions(-) diff --git a/trezorlib/cosi.py b/trezorlib/cosi.py index b1a5cfcf85..71e2899de1 100644 --- a/trezorlib/cosi.py +++ b/trezorlib/cosi.py @@ -1,96 +1,72 @@ import sys from functools import reduce import binascii +from typing import Iterable, Tuple from trezorlib import _ed25519 +# XXX, these could be NewType's, but that would infect users of the cosi module with these types as well. +# Unsure if we want that. +Ed25519PrivateKey = bytes +Ed25519PublicPoint = bytes +Ed25519Signature = bytes -def combine_keys(pks): + +def combine_keys(pks: Iterable[Ed25519PublicPoint]) -> Ed25519PublicPoint: + """Combine a list of Ed25519 points into a "global" CoSi key.""" P = [_ed25519.decodepoint(pk) for pk in pks] combine = reduce(_ed25519.edwards, P) - return _ed25519.encodepoint(combine) + return Ed25519PublicPoint(_ed25519.encodepoint(combine)) -def combine_sig(R, sigs): +def combine_sig(global_R: Ed25519PublicPoint, sigs: Iterable[Ed25519Signature]) -> Ed25519Signature: + """Combine a list of signatures into a single CoSi signature.""" S = [_ed25519.decodeint(si) for si in sigs] s = sum(S) % _ed25519.l - sig = R + _ed25519.encodeint(s) - return sig + sig = global_R + _ed25519.encodeint(s) + return Ed25519Signature(sig) -def get_nonce(sk, data, ctr): +def get_nonce(sk: Ed25519PrivateKey, data: bytes, ctr: int = 0) -> Tuple[int, Ed25519PublicPoint]: + """Calculate CoSi nonces for given data. + These differ from Ed25519 deterministic nonces in that there is a counter appended at end. + + Returns both the private point `r` and the partial signature `R`. + `r` is returned for performance reasons: :func:`sign_with_privkey` + takes it as its `nonce` argument so that it doesn't repeat the `get_nonce` call. + + `R` should be combined with other partial signatures through :func:`combine_keys` + to obtain a "global commitment". + """ h = _ed25519.H(sk) b = _ed25519.b r = _ed25519.Hint(bytes([h[i] for i in range(b >> 3, b >> 2)]) + data + binascii.unhexlify('%08x' % ctr)) R = _ed25519.scalarmult(_ed25519.B, r) - return r, _ed25519.encodepoint(R) + return r, Ed25519PublicPoint(_ed25519.encodepoint(R)) -def verify(signature, digest, pub_key): +def verify(signature: Ed25519Signature, digest: bytes, pub_key: Ed25519PublicPoint) -> None: + """Verify Ed25519 signature. Raise exception if the signature is invalid.""" + # XXX this *might* change to bool function _ed25519.checkvalid(signature, digest, pub_key) -def pubkey_from_privkey(privkey): - return _ed25519.publickey(privkey) +def pubkey_from_privkey(privkey: Ed25519PrivateKey) -> Ed25519PublicPoint: + """Interpret 32 bytes of data as an Ed25519 private key. + Calculate and return the corresponding public key. + """ + return Ed25519PublicPoint(_ed25519.publickey(privkey)) -def sign_with_privkey(digest, privkey, global_pubkey, nonce, global_commit): +def sign_with_privkey(digest: bytes, privkey: Ed25519PrivateKey, + global_pubkey: Ed25519PublicPoint, + nonce: int, + global_commit: Ed25519PublicPoint) -> Ed25519Signature: + """Create a CoSi signature of `digest` with the supplied private key. + This function needs to know the global public key and global commitment. + """ h = _ed25519.H(privkey) b = _ed25519.b a = 2 ** (b - 2) + sum(2 ** i * _ed25519.bit(h, i) for i in range(3, b - 2)) S = (nonce + _ed25519.Hint(global_commit + global_pubkey + digest) * a) % _ed25519.l - return _ed25519.encodeint(S) - - -def self_test(digest): - - def to_hex(by): - return binascii.hexlify(by).decode() - - N = 3 - - digest = binascii.unhexlify(digest) - print('Digest: %s' % to_hex(digest)) - sks = [] - pks = [] - nonces = [] - commits = [] - sigs = [] - for i in range(0, N): - print('----- Key %d ------' % (i + 1)) - seckey = (chr(0x41 + i) * 32).encode() - pubkey = _ed25519.publickey(seckey) - print('Secret Key: %s' % to_hex(seckey)) - print('Public Key: %s' % to_hex(pubkey)) - sks.append(seckey) - pks.append(pubkey) - ctr = 0 - r, R = get_nonce(seckey, digest, ctr) - print('Local nonce: %s' % to_hex(_ed25519.encodeint(r))) - print('Local commit: %s' % to_hex(R)) - nonces.append(r) - commits.append(R) - - global_pk = combine_keys(pks) - global_R = combine_keys(commits) - print('-----------------') - print('Global pubkey: %s' % to_hex(global_pk)) - print('Global commit: %s' % to_hex(global_R)) - print('-----------------') - - sigs = [sign_with_privkey(digest, sks[i], global_pk, nonces[i], global_R) for i in range(N)] - for sig in sigs: - print('Local signature: %s' % to_hex(sig)) - - print('-----------------') - sig = combine_sig(global_R, sigs) - print('Global sig: %s' % to_hex(sig)) - verify(sig, digest, global_pk) - print('Valid Signature!') - - -if __name__ == '__main__': - if len(sys.argv) > 1: - self_test(digest=sys.argv[1]) - else: - self_test(digest='4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b') + return Ed25519Signature(_ed25519.encodeint(S)) From 2bf34dc4b4ee3a199ea2693e40cf85e0179b5cdb Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 14:20:45 +0200 Subject: [PATCH 24/29] unit_tests: added test suite for Ed25519 and CoSi It's rather slow so it's disabled by default. Use `pytest -m slow_cosi` to run. --- trezorlib/tests/unit_tests/test_cosi.py | 124 ++++++++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 trezorlib/tests/unit_tests/test_cosi.py diff --git a/trezorlib/tests/unit_tests/test_cosi.py b/trezorlib/tests/unit_tests/test_cosi.py new file mode 100644 index 0000000000..63d03d4c9c --- /dev/null +++ b/trezorlib/tests/unit_tests/test_cosi.py @@ -0,0 +1,124 @@ +import binascii +import hashlib +import pytest + +from trezorlib import cosi + +# These tests calculate Ed25519 signatures in pure Python. +# In addition to being Python, this is also DJB's proof-of-concept, unoptimized code. +# As a result, it is actually very noticeably slow. On a gen8 Core i5, this takes around 40 seconds. +# To skip the test, run `pytest -m "not slow_cosi"`. + +# Therefore, the tests are skipped by default. +# Run `pytest -m slow_cosi` to explicitly enable. + +pytestmark = pytest.mark.slow_cosi +if "slow_cosi" not in pytest.config.getoption("-m"): + pytestmark = pytest.mark.skip("Skipping slow CoSi tests. 'pytest -m slow_cosi' to run.") + + +RFC8032_VECTORS = ( + ( # test 1 + # privkey + binascii.unhexlify("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"), + # pubkey + binascii.unhexlify("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"), + # message + binascii.unhexlify(""), + # signature + binascii.unhexlify("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e06522490155" + "5fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"), + ), + ( # test 2 + # privkey + binascii.unhexlify("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"), + # pubkey + binascii.unhexlify("3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c"), + # message + binascii.unhexlify("72"), + # signature + binascii.unhexlify("92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da" + "085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00"), + ), + ( # test 3 + # privkey + binascii.unhexlify("c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7"), + # pubkey + binascii.unhexlify("fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"), + # message + binascii.unhexlify("af82"), + # signature + binascii.unhexlify("6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac" + "18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a"), + ), + ( # test SHA(abc) + # privkey + binascii.unhexlify("833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42"), + # pubkey + binascii.unhexlify("ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf"), + # message + hashlib.sha512(b"abc").digest(), + # signature + binascii.unhexlify("dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b589" + "09351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704"), + ), +) + +COMBINED_KEY = binascii.unhexlify("283967b1c19ff93d2924cdcba95e586547cafef509ea402963ceefe96ccb44f2") +GLOBAL_COMMIT = binascii.unhexlify("75bd5806c6366e0374a1c6e020c53feb0791d6cc07560d27d8c158f886ecf389") + + +@pytest.mark.parametrize("privkey, pubkey, message, signature", + RFC8032_VECTORS) +def test_single_eddsa_vector(privkey, pubkey, message, signature): + my_pubkey = cosi.pubkey_from_privkey(privkey) + assert my_pubkey == pubkey + try: + cosi.verify(signature, message, pubkey) + except ValueError: + pytest.fail("Signature does not verify.") + + fake_signature = b'\xf1' + signature[1:] + with pytest.raises(ValueError): + cosi.verify(fake_signature, message, pubkey) + + +def test_combine_keys(): + pubkeys = [pubkey for _, pubkey, _, _ in RFC8032_VECTORS] + assert cosi.combine_keys(pubkeys) == COMBINED_KEY + + Rs = [cosi.get_nonce(privkey, message)[1] for privkey, _, message, _ in RFC8032_VECTORS] + assert cosi.combine_keys(Rs) == GLOBAL_COMMIT + + +@pytest.mark.parametrize("keyset", [ + (0,), + (0, 1), + (0, 1, 2), + (0, 1, 2, 3), + (1, 3), +]) +def test_cosi_combination(keyset): + message = hashlib.sha512(b"You all have to sign this.").digest() + selection = [RFC8032_VECTORS[i] for i in keyset] + + # zip(*iterable) turns a list of tuples to a tuple of lists + privkeys, pubkeys, _, _ = zip(*selection) + nonce_pairs = [cosi.get_nonce(pk, message) for pk in privkeys] + nonces, commits = zip(*nonce_pairs) + + # calculate global pubkey and commitment + global_pk = cosi.combine_keys(pubkeys) + global_commit = cosi.combine_keys(commits) + + # generate individual signatures + signatures = [cosi.sign_with_privkey(message, privkey, global_pk, nonce, global_commit) + for privkey, nonce in zip(privkeys, nonces)] + + # combine signatures + global_sig = cosi.combine_sig(global_commit, signatures) + + try: + cosi.verify(global_sig, message, global_pk) + except ValueError: + pytest.fail("Failed to validate global signature") From 99bc267301118c4a1c166e4497527c7151e3524d Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 14:45:54 +0200 Subject: [PATCH 25/29] client: hide uninteresting functions from test tracebacks --- trezorlib/client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/trezorlib/client.py b/trezorlib/client.py index e73f2134ec..a654c25a15 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -127,6 +127,7 @@ def session(f): # with session activation / deactivation @functools.wraps(f) def wrapped_f(*args, **kwargs): + __tracebackhide__ = True # pytest traceback hiding - this function won't appear in tracebacks client = args[0] client.transport.session_begin() try: @@ -162,6 +163,7 @@ class BaseClient(object): @session def call_raw(self, msg): + __tracebackhide__ = True # pytest traceback hiding - this function won't appear in tracebacks self.transport.write(msg) return self.transport.read() @@ -362,6 +364,7 @@ class DebugLinkMixin(object): self.mnemonic = Mnemonic.normalize_string(mnemonic).split(' ') def call_raw(self, msg): + __tracebackhide__ = True # pytest traceback hiding - this function won't appear in tracebacks if SCREENSHOT and self.debug: from PIL import Image @@ -381,6 +384,8 @@ class DebugLinkMixin(object): return resp def _check_request(self, msg): + __tracebackhide__ = True # pytest traceback hiding - this function won't appear in tracebacks + if self.expected_responses is not None: try: expected = self.expected_responses.pop(0) From aace6577c8b8de06cec9604e5573e1834bea5a05 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 15:01:17 +0200 Subject: [PATCH 26/29] log: remove special handling of Features/CoinType in prettyprinter now that Trezor won't send CoinTypes anymore --- trezorlib/log.py | 2 +- trezorlib/protobuf.py | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/trezorlib/log.py b/trezorlib/log.py index 0e07232fa3..89abd17cfd 100644 --- a/trezorlib/log.py +++ b/trezorlib/log.py @@ -18,7 +18,7 @@ class PrettyProtobufFormatter(logging.Formatter): if type(record.protobuf) in OMITTED_MESSAGES: message += " ({} bytes)".format(record.protobuf.ByteSize()) else: - message += "\n" + protobuf.format_message(record.protobuf, collapse_cointypes=True) + message += "\n" + protobuf.format_message(record.protobuf) return message diff --git a/trezorlib/protobuf.py b/trezorlib/protobuf.py index f64cb3c83f..866f5e9b2c 100644 --- a/trezorlib/protobuf.py +++ b/trezorlib/protobuf.py @@ -352,8 +352,7 @@ def format_message(pb: MessageType, indent: int = 0, sep: str = ' ' * 4, truncate_after: Optional[int] = 256, - truncate_to: Optional[int] = 64, - collapse_cointypes: bool = False) -> str: + truncate_to: Optional[int] = 64) -> str: def mostly_printable(bytes): if not bytes: @@ -397,14 +396,8 @@ def format_message(pb: MessageType, return repr(value) - from .messages import Features - pb_dict = pb.__dict__.copy() - if collapse_cointypes and isinstance(pb, Features): - del pb_dict['coins'] - pb_dict['coins (shortened)'] = ' '.join(coin.coin_shortcut for coin in pb.coins) - return '{name} ({size} bytes) {content}'.format( name=pb.__class__.__name__, size=pb.ByteSize(), - content=pformat_value(pb_dict, indent) + content=pformat_value(pb.__dict__, indent) ) From c10c3f7fd9b24efa69d51777e3c10de9795f607c Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 15:44:43 +0200 Subject: [PATCH 27/29] stellar: better way to handle default path (maybe do this for other currencies too?) --- trezorctl | 9 ++++----- trezorlib/stellar.py | 9 +-------- .../test_msg_stellar_get_public_key.py | 5 +++-- .../test_msg_stellar_sign_transaction.py | 14 +++++++------- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/trezorctl b/trezorctl index 1f9a502d85..8b09e3d8fb 100755 --- a/trezorctl +++ b/trezorctl @@ -942,25 +942,24 @@ def cosi_sign(connect, address, data, global_commitment, global_pubkey): # Stellar functions # @cli.command(help='Get Stellar public address') -@click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") +@click.option('-n', '--address', required=False, help="BIP32 path. Always use hardened paths and the m/44'/148'/ prefix", default=stellar.DEFAULT_BIP32_PATH) @click.pass_obj def stellar_get_address(connect, address): client = connect() - address_n = stellar.expand_path_or_default(client, address) + address_n = tools.parse_path(address) # StellarPublicKey response response = client.stellar_get_public_key(address_n) return stellar.address_from_public_key(response.public_key) @cli.command(help='Sign a base64-encoded transaction envelope') -@click.option('-n', '--address', required=False, help="BIP32 path. Default primary account is m/44'/148'/0'. Always use hardened paths and the m/44'/148'/ prefix") +@click.option('-n', '--address', required=False, help="BIP32 path. Always use hardened paths and the m/44'/148'/ prefix", default=stellar.DEFAULT_BIP32_PATH) @click.option('-n', '--network-passphrase', default='Public Global Stellar Network ; September 2015', required=False, help="Network passphrase (blank for public network). Testnet is: 'Test SDF Network ; September 2015'") @click.argument('b64envelope') @click.pass_obj def stellar_sign_transaction(connect, b64envelope, address, network_passphrase): client = connect() - - address_n = stellar.expand_path_or_default(client, address) + address_n = tools.parse_path(address) tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64envelope)) resp = client.stellar_sign_transaction(tx, operations, address_n, network_passphrase) diff --git a/trezorlib/stellar.py b/trezorlib/stellar.py index 292dae7c77..3c7c9e2cb9 100644 --- a/trezorlib/stellar.py +++ b/trezorlib/stellar.py @@ -30,14 +30,7 @@ OP_MANAGE_DATA = 10 OP_BUMP_SEQUENCE = 11 -def expand_path_or_default(client, address): - """Uses client to parse address and returns an array of integers - If no address is specified, the default of m/44'/148'/0' is used - """ - if address: - return client.expand_path(address) - else: - return client.expand_path("m/44'/148'/0'") +DEFAULT_BIP32_PATH = "m/44h/148h/0h" def address_from_public_key(pk_bytes): diff --git a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py index cd7cec2791..0a45fa6314 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_get_public_key.py @@ -12,11 +12,12 @@ # # You should have received a copy of the GNU Lesser General Public License # along with this library. If not, see . +import pytest from .common import TrezorTest from .conftest import TREZOR_VERSION from trezorlib import stellar -import pytest +from trezorlib.tools import parse_path @pytest.mark.stellar @@ -27,5 +28,5 @@ class TestMsgStellarGetPublicKey(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() # GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW - response = self.client.stellar_get_public_key(self.client.expand_path("m/44'/148'/0'")) + response = self.client.stellar_get_public_key(parse_path(stellar.DEFAULT_BIP32_PATH)) assert stellar.address_from_public_key(response.public_key) == b'GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW' diff --git a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py index dfef8509e2..f4c8d560e0 100644 --- a/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py +++ b/trezorlib/tests/device_tests/test_msg_stellar_sign_transaction.py @@ -20,8 +20,10 @@ from base64 import b64encode from .common import TrezorTest from .conftest import TREZOR_VERSION -from binascii import hexlify, unhexlify +from binascii import unhexlify from trezorlib import messages as proto +from trezorlib import stellar +from trezorlib.tools import parse_path import pytest @@ -29,14 +31,12 @@ import pytest @pytest.mark.xfail(TREZOR_VERSION == 2, reason="T2 support is not yet finished") class TestMsgStellarSignTransaction(TrezorTest): + ADDRESS_N = parse_path(stellar.DEFAULT_BIP32_PATH) + def get_network_passphrase(self): """Use the same passphrase as the network that generated the test XDR/signatures""" return "Integration Test Network ; zulucrypto" - def get_address_n(self): - """BIP32 path of the default account""" - return self.client.expand_path("m/44'/148'/0'") - def test_sign_tx_bump_sequence_op(self): self.setup_mnemonic_nopin_nopassphrase() @@ -44,7 +44,7 @@ class TestMsgStellarSignTransaction(TrezorTest): op.bump_to = 0x7fffffffffffffff tx = self._create_msg() - response = self.client.stellar_sign_transaction(tx, [op], self.get_address_n(), self.get_network_passphrase()) + response = self.client.stellar_sign_transaction(tx, [op], self.ADDRESS_N, self.get_network_passphrase()) assert b64encode(response.signature) == b'UAOL4ZPYIOzEgM66kBrhyNjLR66dNXtuNrmvd3m0/pc8qCSoLmYY4TybS0lHiMtb+LFZESTaxrpErMHz1sZ6DQ==' def test_sign_tx_account_merge_op(self): @@ -55,7 +55,7 @@ class TestMsgStellarSignTransaction(TrezorTest): tx = self._create_msg() - response = self.client.stellar_sign_transaction(tx, [op], self.get_address_n(), self.get_network_passphrase()) + response = self.client.stellar_sign_transaction(tx, [op], self.ADDRESS_N, self.get_network_passphrase()) assert b64encode(response.signature) == b'gjoPRj4sW5o7NAXzYOqPK0uxfPbeKb4Qw48LJiCH/XUZ6YVCiZogePC0Z5ISUlozMh6YO6HoYtuLPbm7jq+eCA==' def _create_msg(self) -> proto.StellarSignTx: From 84101518101f3411fe6d7a9add3d1310d13a5cc8 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 15:47:57 +0200 Subject: [PATCH 28/29] remove remaining usages of expand_path --- tools/helloworld.py | 3 ++- tools/pwd_reader.py | 8 ++++---- tools/signtest.py | 7 +++++++ .../tests/device_tests/test_msg_nem_signtx_transfers.py | 6 +++--- .../tests/device_tests/test_msg_signtx_segwit_native.py | 2 +- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tools/helloworld.py b/tools/helloworld.py index c91e5d392a..c851baca36 100755 --- a/tools/helloworld.py +++ b/tools/helloworld.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 from trezorlib.client import TrezorClient from trezorlib.transport import get_transport +from trezorlib.tools import parse_path def main(): @@ -15,7 +16,7 @@ def main(): # Get the first address of first BIP44 account # (should be the same address as shown in wallet.trezor.io) - bip32_path = client.expand_path("44'/0'/0'/0/0") + bip32_path = parse_path("44'/0'/0'/0/0") address = client.get_address('Bitcoin', bip32_path) print('Bitcoin address:', address) diff --git a/tools/pwd_reader.py b/tools/pwd_reader.py index a979bd5279..58c9b5fe45 100755 --- a/tools/pwd_reader.py +++ b/tools/pwd_reader.py @@ -10,16 +10,16 @@ from urllib.parse import urlparse from trezorlib.client import TrezorClient from trezorlib.transport import get_transport +from trezorlib.tools import parse_path # Return path by BIP-32 -def getPath(client): - return client.expand_path("10016'/0") +BIP32_PATH = parse_path("10016h/0") # Deriving master key def getMasterKey(client): - bip32_path = getPath(client) + bip32_path = BIP32_PATH ENC_KEY = 'Activate TREZOR Password Manager?' ENC_VALUE = unhexlify('2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee2d650551248d792eabf628f451200d7f51cb63e46aadcbb1038aacb05e8c8aee') key = hexlify(client.encrypt_keyvalue( @@ -99,7 +99,7 @@ def getDecryptedNonce(client, entry): ENC_KEY = 'Unlock %s for user %s?' % (item, entry['username']) ENC_VALUE = entry['nonce'] decrypted_nonce = hexlify(client.decrypt_keyvalue( - getPath(client), + BIP32_PATH, ENC_KEY, unhexlify(ENC_VALUE), False, diff --git a/tools/signtest.py b/tools/signtest.py index 1fbdd6e2e3..90feb7a4aa 100755 --- a/tools/signtest.py +++ b/tools/signtest.py @@ -14,6 +14,13 @@ from trezorlib.tx_api import TxApiBitcoin from trezorlib.transport import get_transport +# This script has survived unmodified through several significant changes +# of the trezorlib library. While we want to have something like this, +# we're waiting on a couple more changes in order to implement this a little more cleanly. +# Wait for trezorlib v1.0. +raise Exception("This code is too old to run. Sorry.") + + def hash160(x): h = hashlib.new("ripemd160") h.update(hashlib.sha256(x).digest()) diff --git a/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py b/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py index d1ac704b85..94ea1182d4 100644 --- a/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py +++ b/trezorlib/tests/device_tests/test_msg_nem_signtx_transfers.py @@ -162,7 +162,7 @@ class TestMsgNEMSignTx(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 76809215, "amount": 3000000, "fee": 1000000, @@ -191,7 +191,7 @@ class TestMsgNEMSignTx(TrezorTest): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 76809215, "amount": 2000000, "fee": 1000000, @@ -219,7 +219,7 @@ class TestMsgNEMSignTx(TrezorTest): def test_nem_signtx_multiple_mosaics(self): self.setup_mnemonic_nopin_nopassphrase() - tx = self.client.nem_sign_tx(self.client.expand_path("m/44'/1'/0'/0'/0'"), { + tx = self.client.nem_sign_tx(parse_path("m/44'/1'/0'/0'/0'"), { "timeStamp": 76809215, "amount": 2000000, "fee": 1000000, diff --git a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py index 304c7f0022..a5081c6b5d 100644 --- a/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py +++ b/trezorlib/tests/device_tests/test_msg_signtx_segwit_native.py @@ -207,7 +207,7 @@ class TestMsgSigntxSegwitNative(TrezorTest): script_type=proto.OutputScriptType.PAYTOADDRESS, ) out2 = proto.TxOutputType( - # address_n=self.client.expand_path("44'/1'/0'/0/0"), + # address_n=parse_path("44'/1'/0'/0/0"), # script_type=proto.OutputScriptType.PAYTOP2SHWITNESS, address='2N6UeBoqYEEnybg4cReFYDammpsyDw8R2Mc', script_type=proto.OutputScriptType.PAYTOADDRESS, From 5191fd09bec04af8da43df93ac97f985e4bf7d49 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 28 May 2018 17:41:52 +0200 Subject: [PATCH 29/29] device_tests: add a compat test for cosi, checking interoperability between Trezor and local implementation --- trezorlib/tests/device_tests/test_cosi.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/trezorlib/tests/device_tests/test_cosi.py b/trezorlib/tests/device_tests/test_cosi.py index 45807915aa..46ec9eb4d6 100644 --- a/trezorlib/tests/device_tests/test_cosi.py +++ b/trezorlib/tests/device_tests/test_cosi.py @@ -78,3 +78,22 @@ class TestCosi(TrezorTest): sig = cosi.combine_sig(global_R, [sig0.signature, sig1.signature, sig2.signature]) cosi.verify(sig, digest, global_pk) + + def test_cosi_compat(self): + self.setup_mnemonic_pin_passphrase() + + digest = sha256(b'this is not a pipe').digest() + remote_commit = self.client.cosi_commit(parse_path("10018'/0'"), digest) + + local_privkey = sha256(b'private key').digest()[:32] + local_pubkey = cosi.pubkey_from_privkey(local_privkey) + local_nonce, local_commitment = cosi.get_nonce(local_privkey, digest, 42) + + global_pk = cosi.combine_keys([remote_commit.pubkey, local_pubkey]) + global_R = cosi.combine_keys([remote_commit.commitment, local_commitment]) + + remote_sig = self.client.cosi_sign(parse_path("10018'/0'"), digest, global_R, global_pk) + local_sig = cosi.sign_with_privkey(digest, local_privkey, global_pk, local_nonce, global_R) + sig = cosi.combine_sig(global_R, [remote_sig.signature, local_sig]) + + cosi.verify(sig, digest, global_pk)