Tests and fixes for remainig Keepkey commands.
Tests for setup, wipe, restore, backup, promptpin, sendpin
This commit is contained in:
parent
cf152b709a
commit
d627634ce6
@ -1,7 +1,7 @@
|
||||
# KeepKey interaction script
|
||||
|
||||
from ..hwwclient import HardwareWalletClient
|
||||
from ..errors import BadArgumentError, DeviceAlreadyUnlockedError, DeviceConnectionError, UnavailableActionError, DeviceNotReadyError
|
||||
from ..errors import BadArgumentError, DeviceAlreadyInitError, DeviceAlreadyUnlockedError, DeviceConnectionError, UnavailableActionError, DeviceNotReadyError
|
||||
from keepkeylib.transport_hid import HidTransport
|
||||
from keepkeylib.transport_udp import UDPTransport
|
||||
from keepkeylib.client import BaseClient, DebugWireMixin, DebugLinkMixin, ProtocolMixin, TextUIMixin
|
||||
@ -111,8 +111,13 @@ def parse_multisig(script):
|
||||
# Doesn't init device
|
||||
class NoInitMixin(ProtocolMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ProtocolMixin, self).__init__(*args, **kwargs)
|
||||
self.tx_api = None
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def init_device(self):
|
||||
pass
|
||||
|
||||
def actual_init_device(self):
|
||||
return super().init_device()
|
||||
|
||||
class KeepKey(NoInitMixin, TextUIMixin, BaseClient):
|
||||
pass
|
||||
@ -160,7 +165,7 @@ class KeepkeyClient(HardwareWalletClient):
|
||||
os.environ['PASSPHRASE'] = password
|
||||
|
||||
def _check_unlocked(self):
|
||||
self.client.init_device()
|
||||
self.client.actual_init_device()
|
||||
if self.client.features.pin_protection and not self.client.features.pin_cached:
|
||||
raise DeviceNotReadyError('Keepkey is locked. Unlock by using \'promptpin\' and then \'sendpin\'.')
|
||||
|
||||
@ -360,6 +365,7 @@ class KeepkeyClient(HardwareWalletClient):
|
||||
# Setup a new device
|
||||
@keepkey_exception
|
||||
def setup_device(self, label='', passphrase=''):
|
||||
self.client.actual_init_device()
|
||||
if self.client.features.initialized:
|
||||
raise DeviceAlreadyInitError('Device is already initialized. Use wipe first and try again')
|
||||
self.client.reset_device(False, 256, bool(self.password), True, label, 'english')
|
||||
@ -390,7 +396,7 @@ class KeepkeyClient(HardwareWalletClient):
|
||||
# Prompt for a pin on device
|
||||
@keepkey_exception
|
||||
def prompt_pin(self):
|
||||
self.client.init_device()
|
||||
self.client.actual_init_device()
|
||||
if not self.client.features.pin_protection:
|
||||
raise DeviceAlreadyUnlockedError('This device does not need a PIN')
|
||||
if self.client.features.pin_cached:
|
||||
@ -442,7 +448,7 @@ def enumerate(password=''):
|
||||
client = None
|
||||
try:
|
||||
client = KeepkeyClient(path, password)
|
||||
client.client.init_device()
|
||||
client.client.actual_init_device()
|
||||
if client.client.features.initialized:
|
||||
master_xpub = client.get_pubkey_at_path('m/0h')['xpub']
|
||||
d_data['fingerprint'] = get_xpub_fingerprint_hex(master_xpub)
|
||||
|
||||
@ -38,7 +38,7 @@ suite = unittest.TestSuite()
|
||||
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestSegwitAddress))
|
||||
suite.addTests(unittest.defaultTestLoader.loadTestsFromTestCase(TestPSBT))
|
||||
|
||||
if not args.no_trezor or not args.no_coldcard or args.ledger or not args.no_bitbox:
|
||||
if not args.no_trezor or not args.no_coldcard or args.ledger or not args.no_bitbox or not args.no_keepkey:
|
||||
# Start bitcoind
|
||||
rpc, userpass = start_bitcoind(args.bitcoind)
|
||||
|
||||
|
||||
@ -13,9 +13,20 @@ import unittest
|
||||
|
||||
from keepkeylib.transport_udp import UDPTransport
|
||||
from keepkeylib.client import KeepKeyDebugClient
|
||||
from test_device import DeviceEmulator, DeviceTestCase, start_bitcoind, TestDeviceConnect, TestDisplayAddress, TestGetKeypool, TestSignTx
|
||||
from keepkeylib import messages_pb2 as messages
|
||||
from test_device import DeviceEmulator, DeviceTestCase, start_bitcoind, TestDeviceConnect, TestDisplayAddress, TestGetKeypool, TestSignMessage, TestSignTx
|
||||
|
||||
from hwilib.cli import process_commands
|
||||
from hwilib.devices.keepkey import KeepkeyClient
|
||||
|
||||
from types import MethodType
|
||||
|
||||
def pin_matrix(self, code=None):
|
||||
if self.pin:
|
||||
pin = self.debug.encode_pin(self.pin)
|
||||
else:
|
||||
pin = self.debug.read_pin_encoded()
|
||||
return messages.PinMatrixAck(pin=pin)
|
||||
|
||||
class KeepkeyEmulator(DeviceEmulator):
|
||||
def __init__(self, emulator_path):
|
||||
@ -105,6 +116,91 @@ class TestKeepkeyGetxpub(KeepkeyTestCase):
|
||||
gxp_res = process_commands(['-t', 'keepkey', '-d', 'udp:127.0.0.1:21324', 'getxpub', path_vec['path']])
|
||||
self.assertEqual(gxp_res['xpub'], path_vec['xpub'])
|
||||
|
||||
# Trezor specific management (setup, wipe, restore, backup, promptpin, sendpin) command tests
|
||||
class TestKeepkeyManCommands(KeepkeyTestCase):
|
||||
def setUp(self):
|
||||
self.client = self.emulator.start()
|
||||
self.dev_args = ['-t', 'keepkey', '-d', 'udp:127.0.0.1:21324']
|
||||
|
||||
def tearDown(self):
|
||||
self.emulator.stop()
|
||||
|
||||
def test_setup_wipe(self):
|
||||
# Device is init, setup should fail
|
||||
result = process_commands(self.dev_args + ['setup'])
|
||||
self.assertEquals(result['code'], -10)
|
||||
self.assertEquals(result['error'], 'Device is already initialized. Use wipe first and try again')
|
||||
|
||||
# Wipe
|
||||
result = process_commands(self.dev_args + ['wipe'])
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
# Setup
|
||||
k_client = KeepkeyClient('udp:127.0.0.1:21324', 'test')
|
||||
k_client.client.callback_PinMatrixRequest = MethodType(pin_matrix, k_client.client)
|
||||
k_client.client.pin = '1234'
|
||||
result = k_client.setup_device()
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
# Make sure device is init, setup should fail
|
||||
result = process_commands(self.dev_args + ['setup'])
|
||||
self.assertEquals(result['code'], -10)
|
||||
self.assertEquals(result['error'], 'Device is already initialized. Use wipe first and try again')
|
||||
|
||||
def test_backup(self):
|
||||
result = process_commands(self.dev_args + ['backup'])
|
||||
self.assertIn('error', result)
|
||||
self.assertIn('code', result)
|
||||
self.assertEqual(result['error'], 'The Keepkey does not support creating a backup via software')
|
||||
self.assertEqual(result['code'], -9)
|
||||
|
||||
def test_pins(self):
|
||||
# There's no PIN
|
||||
result = process_commands(self.dev_args + ['--debug', 'promptpin'])
|
||||
self.assertEqual(result['error'], 'This device does not need a PIN')
|
||||
self.assertEqual(result['code'], -11)
|
||||
result = process_commands(self.dev_args + ['sendpin', '1234'])
|
||||
self.assertEqual(result['error'], 'This device does not need a PIN')
|
||||
self.assertEqual(result['code'], -11)
|
||||
|
||||
# Set a PIN
|
||||
self.client.wipe_device()
|
||||
self.client.load_device_by_mnemonic(mnemonic='alcohol woman abuse must during monitor noble actual mixed trade anger aisle', pin='1234', passphrase_protection=False, label='test', language='english')
|
||||
self.client.call(messages.ClearSession())
|
||||
result = process_commands(self.dev_args + ['promptpin'])
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
# Invalid pin
|
||||
result = process_commands(self.dev_args + ['sendpin', 'notnum'])
|
||||
self.assertEqual(result['error'], 'Non-numeric PIN provided')
|
||||
self.assertEqual(result['code'], -7)
|
||||
|
||||
result = process_commands(self.dev_args + ['sendpin', '00000'])
|
||||
self.assertFalse(result['success'])
|
||||
|
||||
# Make sure we get a needs pin message
|
||||
result = process_commands(self.dev_args + ['getxpub', 'm/0h'])
|
||||
self.assertEqual(result['code'], -12)
|
||||
self.assertEqual(result['error'], 'Keepkey is locked. Unlock by using \'promptpin\' and then \'sendpin\'.')
|
||||
|
||||
# Prompt pin
|
||||
self.client.call(messages.ClearSession())
|
||||
result = process_commands(self.dev_args + ['promptpin'])
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
# Send the PIN
|
||||
pin = self.client.debug.encode_pin('1234')
|
||||
result = process_commands(self.dev_args + ['sendpin', pin])
|
||||
self.assertTrue(result['success'])
|
||||
|
||||
# Sending PIN after unlock
|
||||
result = process_commands(self.dev_args + ['promptpin'])
|
||||
self.assertEqual(result['error'], 'The PIN has already been sent to this device')
|
||||
self.assertEqual(result['code'], -11)
|
||||
result = process_commands(self.dev_args + ['sendpin', '1234'])
|
||||
self.assertEqual(result['error'], 'The PIN has already been sent to this device')
|
||||
self.assertEqual(result['code'], -11)
|
||||
|
||||
def keepkey_test_suite(emulator, rpc, userpass):
|
||||
# Redirect stderr to /dev/null as it's super spammy
|
||||
sys.stderr = open(os.devnull, 'w')
|
||||
@ -122,7 +218,9 @@ def keepkey_test_suite(emulator, rpc, userpass):
|
||||
suite.addTest(DeviceTestCase.parameterize(TestGetKeypool, rpc, userpass, type, path, fingerprint, master_xpub, emulator=dev_emulator))
|
||||
suite.addTest(DeviceTestCase.parameterize(TestSignTx, rpc, userpass, type, path, fingerprint, master_xpub, emulator=dev_emulator))
|
||||
suite.addTest(DeviceTestCase.parameterize(TestDisplayAddress, rpc, userpass, type, path, fingerprint, master_xpub, emulator=dev_emulator))
|
||||
suite.addTest(DeviceTestCase.parameterize(TestSignMessage, rpc, userpass, type, path, fingerprint, master_xpub, emulator=dev_emulator))
|
||||
suite.addTest(KeepkeyTestCase.parameterize(TestKeepkeyGetxpub, emulator=dev_emulator))
|
||||
suite.addTest(KeepkeyTestCase.parameterize(TestKeepkeyManCommands, emulator=dev_emulator))
|
||||
return suite
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Loading…
Reference in New Issue
Block a user