Implement signmessage for the Coldcard with a simple test

This commit is contained in:
Andrew Chow 2019-01-02 13:10:41 -05:00
parent cd0dd7d529
commit 14a1345ad1
4 changed files with 35 additions and 3 deletions

View File

@ -110,6 +110,8 @@ def signmessage(args, client):
return client.sign_message(args.message, args.path)
except NotImplementedError as e:
return {'error': str(e), 'code': NOT_IMPLEMENTED}
except ValueError as e:
return {'error': str(e), 'code': BAD_ARGUMENT}
def getkeypool(args, client):
# args[0]; start index (e.g. 0)

View File

@ -2,7 +2,7 @@
from ..hwwclient import HardwareWalletClient, UnavailableActionError
from ckcc.client import ColdcardDevice, COINKITE_VID, CKCC_PID
from ckcc.protocol import CCProtocolPacker
from ckcc.protocol import CCProtocolPacker, CCProtoError
from ckcc.constants import MAX_BLK_LEN, AF_P2WPKH, AF_CLASSIC, AF_P2WPKH_P2SH
from ..base58 import xpub_main_2_test, get_xpub_fingerprint_hex
from hashlib import sha256
@ -95,7 +95,31 @@ class ColdcardClient(HardwareWalletClient):
# Must return a base64 encoded string with the signed message
# The message can be any string. keypath is the bip 32 derivation path for the key to sign with
def sign_message(self, message, keypath):
raise NotImplementedError('The Coldcard does not currently implement signmessage')
self.device.check_mitm()
keypath = keypath.replace('h', '\'')
keypath = keypath.replace('H', '\'')
try:
ok = self.device.send_recv(CCProtocolPacker.sign_message(message.encode(), keypath, AF_CLASSIC), timeout=None)
assert ok == None
except CCProtoError as e:
raise ValueError(str(e))
while 1:
time.sleep(0.250)
done = self.device.send_recv(CCProtocolPacker.get_signed_msg(), timeout=None)
if done == None:
continue
break
if len(done) != 2:
raise ValueError('Failed: %r' % done)
addr, raw = done
sig = str(base64.b64encode(raw), 'ascii').replace('\n', '')
return {"signature": sig}
# Display address of specified type on the device. Only supports single-key based addresses.
def display_address(self, keypath, p2sh_p2wpkh, bech32):

View File

@ -10,7 +10,7 @@ import unittest
from hwilib.commands import process_commands
from ckcc.protocol import CCProtocolPacker
from ckcc.client import ColdcardDevice
from test_device import DeviceTestCase, start_bitcoind, TestDeviceConnect, TestDisplayAddress, TestGetKeypool, TestSignTx
from test_device import DeviceTestCase, start_bitcoind, TestDeviceConnect, TestDisplayAddress, TestGetKeypool, TestSignMessage, TestSignTx
def coldcard_test_suite(simulator, rpc, userpass):
# Start the Coldcard simulator
@ -49,6 +49,7 @@ def coldcard_test_suite(simulator, rpc, userpass):
suite.addTest(DeviceTestCase.parameterize(TestDeviceConnect, rpc, userpass, 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd'))
suite.addTest(DeviceTestCase.parameterize(TestGetKeypool, rpc, userpass, 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd'))
suite.addTest(DeviceTestCase.parameterize(TestDisplayAddress, rpc, userpass, 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd'))
suite.addTest(DeviceTestCase.parameterize(TestSignMessage, rpc, userpass, 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd'))
# HACK: Skip this in headless simulator because it requires user input
if not simulator.endswith('headless.py'):
suite.addTest(DeviceTestCase.parameterize(TestSignTx, rpc, userpass, 'coldcard', '/tmp/ckcc-simulator.sock', '0f056943', 'tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd'))

View File

@ -237,3 +237,8 @@ class TestDisplayAddress(DeviceTestCase):
process_commands(self.dev_args + ['displayaddress', 'm/44h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--sh_wpkh', 'm/49h/1h/0h/0/0'])
process_commands(self.dev_args + ['displayaddress', '--wpkh', 'm/84h/1h/0h/0/0'])
class TestSignMessage(DeviceTestCase):
def test_sign_msg(self):
process_commands(self.dev_args + ['signmessage', 'Message signing test', 'm/44h/1h/0h/0/0'])