2012-12-03 15:36:03 +00:00
#!/usr/bin/python
2013-10-07 21:28:48 +00:00
import os
2012-12-03 15:36:03 +00:00
import binascii
import argparse
import json
2013-10-07 21:28:48 +00:00
import threading
2012-12-03 15:36:03 +00:00
2013-10-07 21:28:48 +00:00
from trezorlib . client import TrezorClient , pin_func
2013-09-13 03:33:20 +00:00
from trezorlib . debuglink import DebugLink
from trezorlib . protobuf_json import pb2json
2013-10-07 21:28:48 +00:00
from trezorlib . pinmatrix import PinMatrixWidget
2013-09-12 22:17:06 +00:00
2012-12-03 15:36:03 +00:00
def parse_args ( commands ) :
2013-09-13 03:33:20 +00:00
parser = argparse . ArgumentParser ( description = ' Commandline tool for Trezor devices. ' )
2013-08-30 21:32:02 +00:00
parser . add_argument ( ' -t ' , ' --transport ' , dest = ' transport ' , choices = [ ' usb ' , ' serial ' , ' pipe ' , ' socket ' ] , default = ' usb ' , help = " Transport used for talking with the device " )
parser . add_argument ( ' -p ' , ' --path ' , dest = ' path ' , default = ' ' , help = " Path used by the transport (usually serial port) " )
2013-11-15 00:43:05 +00:00
parser . add_argument ( ' -dt ' , ' --debuglink-transport ' , dest = ' debuglink_transport ' , choices = [ ' usb ' , ' serial ' , ' pipe ' , ' socket ' ] , default = ' usb ' , help = " Debuglink transport " )
parser . add_argument ( ' -dp ' , ' --debuglink-path ' , dest = ' debuglink_path ' , default = ' ' , help = " Path used by the transport (usually serial port) " )
2012-12-03 15:36:03 +00:00
parser . add_argument ( ' -j ' , ' --json ' , dest = ' json ' , action = ' store_true ' , help = " Prints result as json object " )
2012-12-05 19:31:26 +00:00
parser . add_argument ( ' -d ' , ' --debug ' , dest = ' debug ' , action = ' store_true ' , help = ' Enable low-level debugging ' )
2012-12-03 15:36:03 +00:00
cmdparser = parser . add_subparsers ( title = ' Available commands ' )
for cmd in commands . _list_commands ( ) :
func = object . __getattribute__ ( commands , cmd )
try :
help = func . help
except AttributeError :
help = ' '
try :
arguments = func . arguments
except AttributeError :
arguments = ( ( ( ' params ' , ) , { ' nargs ' : ' * ' } ) , )
item = cmdparser . add_parser ( cmd , help = func . help )
for arg in arguments :
item . add_argument ( * arg [ 0 ] , * * arg [ 1 ] )
item . set_defaults ( func = func )
2013-04-01 14:59:42 +00:00
item . set_defaults ( cmd = cmd )
2012-12-03 15:36:03 +00:00
return parser . parse_args ( )
2013-11-15 00:43:05 +00:00
def get_transport ( transport_string , path , * * kwargs ) :
2012-12-03 15:36:03 +00:00
if transport_string == ' usb ' :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_hid import HidTransport
2013-08-30 21:32:02 +00:00
if path == ' ' :
2013-08-31 21:45:53 +00:00
try :
path = list_usb ( ) [ 0 ]
except IndexError :
raise Exception ( " No Trezor found on USB " )
2013-08-30 21:32:02 +00:00
2013-11-15 00:43:05 +00:00
return HidTransport ( path , * * kwargs )
2013-04-01 14:59:42 +00:00
2012-12-03 15:36:03 +00:00
if transport_string == ' serial ' :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_serial import SerialTransport
2013-11-15 00:43:05 +00:00
return SerialTransport ( path , * * kwargs )
2012-12-03 15:36:03 +00:00
if transport_string == ' pipe ' :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_pipe import PipeTransport
2013-11-15 00:43:05 +00:00
return PipeTransport ( path , is_device = False , * * kwargs )
2012-12-03 15:36:03 +00:00
if transport_string == ' socket ' :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_socket import SocketTransportClient
2013-11-15 00:43:05 +00:00
return SocketTransportClient ( path , * * kwargs )
2012-12-03 15:36:03 +00:00
if transport_string == ' fake ' :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_fake import FakeTransport
2013-11-15 00:43:05 +00:00
return FakeTransport ( path , * * kwargs )
2012-12-03 15:36:03 +00:00
raise NotImplemented ( " Unknown transport " )
class Commands ( object ) :
def __init__ ( self , client ) :
self . client = client
@classmethod
def _list_commands ( cls ) :
return [ x for x in dir ( cls ) if not x . startswith ( ' _ ' ) ]
2013-04-01 14:59:42 +00:00
def list ( self , args ) :
# Fake method for advertising 'list' command
pass
2013-01-05 14:43:21 +00:00
def get_address ( self , args ) :
2014-01-09 16:34:29 +00:00
address_n = self . client . expand_path ( args . n )
return self . client . get_address ( args . coin , address_n )
2013-09-09 13:38:15 +00:00
2012-12-03 15:36:03 +00:00
def get_entropy ( self , args ) :
return binascii . hexlify ( self . client . get_entropy ( args . size ) )
2013-01-05 14:43:21 +00:00
2013-09-12 22:17:06 +00:00
def get_features ( self , args ) :
2014-01-09 16:34:29 +00:00
return self . client . features
def list_coins ( self , args ) :
return [ coin . coin_name for coin in self . client . features . coins ]
2013-09-12 22:17:06 +00:00
def ping ( self , args ) :
return self . client . ping ( args . msg )
2013-11-15 00:43:05 +00:00
def get_public_node ( self , args ) :
2014-01-09 16:34:29 +00:00
address_n = self . client . expand_path ( args . n )
return self . client . get_public_node ( address_n )
2012-12-03 15:36:03 +00:00
2013-09-12 22:17:06 +00:00
def set_label ( self , args ) :
return self . client . apply_settings ( label = args . label )
2014-01-31 18:48:19 +00:00
def change_pin ( self , args ) :
return self . client . change_pin ( args . remove )
2012-12-05 19:31:26 +00:00
def load_device ( self , args ) :
2013-12-30 22:35:20 +00:00
if not args . mnemonic and not args . xprv :
raise Exception ( " Please provide mnemonic or xprv " )
if args . mnemonic :
mnemonic = ' ' . join ( args . mnemonic )
2014-01-13 03:44:57 +00:00
return self . client . load_device_by_mnemonic ( mnemonic , args . pin ,
args . passphrase_protection , args . label , ' english ' )
2012-12-05 19:31:26 +00:00
2014-01-06 00:54:53 +00:00
else :
2014-01-13 03:44:57 +00:00
return self . client . load_device_by_xprv ( args . xprv , args . pin ,
args . passphrase_protection , args . label , ' english ' )
2014-01-06 00:54:53 +00:00
def reset_device ( self , args ) :
return self . client . reset_device ( True , args . strength , args . passphrase , args . pin , args . label )
2013-10-12 15:40:48 +00:00
2013-11-15 00:43:05 +00:00
def sign_message ( self , args ) :
2014-01-09 16:34:29 +00:00
return pb2json ( self . client . sign_message ( args . n , args . message ) , { ' message ' : args . message } )
2013-11-15 00:43:05 +00:00
2013-11-26 16:29:50 +00:00
def verify_message ( self , args ) :
return self . client . verify_message ( args . address , args . signature , args . message )
2013-10-12 15:40:48 +00:00
def firmware_update ( self , args ) :
2013-10-19 12:19:09 +00:00
if not args . file :
raise Exception ( " Must provide firmware filename " )
2013-10-12 15:40:48 +00:00
fp = open ( args . file , ' r ' )
if fp . read ( 4 ) != ' TRZR ' :
raise Exception ( " Trezor firmware header expected " )
fp . seek ( 0 )
2013-10-21 16:30:43 +00:00
return self . client . firmware_update ( fp = open ( args . file , ' r ' ) )
2013-10-12 15:40:48 +00:00
2013-04-01 14:59:42 +00:00
list . help = ' List connected Trezor USB devices '
2013-09-12 22:17:06 +00:00
ping . help = ' Send ping message '
2013-01-05 14:43:21 +00:00
get_address . help = ' Get bitcoin address in base58 encoding '
2012-12-03 15:36:03 +00:00
get_entropy . help = ' Get example entropy '
2013-09-12 22:17:06 +00:00
get_features . help = ' Retrieve device features and settings '
2013-11-15 00:43:05 +00:00
get_public_node . help = ' Get public node of given path '
2013-09-12 22:17:06 +00:00
set_label . help = ' Set new wallet label '
2014-01-31 18:48:19 +00:00
change_pin . help = ' Change new PIN or remove existing '
2014-01-09 16:34:29 +00:00
list_coins . help = ' List all supported coin types by the device '
2012-12-05 19:31:26 +00:00
load_device . help = ' Load custom configuration to the device '
2014-01-06 00:54:53 +00:00
reset_device . help = ' Perform factory reset of the device and generate new seed '
2013-11-15 00:43:05 +00:00
sign_message . help = ' Sign message using address of given path '
2013-11-26 16:29:50 +00:00
verify_message . help = ' Verify message '
2013-10-12 15:40:48 +00:00
firmware_update . help = ' Upload new firmware to device (must be in bootloader mode) '
2013-01-05 14:43:21 +00:00
get_address . arguments = (
2014-01-09 16:34:29 +00:00
( ( ' -c ' , ' --coin ' ) , { ' type ' : str , ' default ' : ' Bitcoin ' } ) ,
# (('n',), {'metavar': 'N', 'type': int, 'nargs': '+'}),
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
2013-01-05 14:43:21 +00:00
)
2012-12-03 15:36:03 +00:00
get_entropy . arguments = (
( ( ' size ' , ) , { ' type ' : int } ) ,
)
2013-09-12 22:17:06 +00:00
get_features . arguments = ( )
2014-01-09 16:34:29 +00:00
list_coins . arguments = ( )
2013-09-12 22:17:06 +00:00
ping . arguments = (
( ( ' msg ' , ) , { ' type ' : str } ) ,
)
2012-12-03 15:36:03 +00:00
2013-09-12 22:17:06 +00:00
set_label . arguments = (
2014-01-09 23:11:03 +00:00
( ( ' -l ' , ' --label ' , ) , { ' type ' : str , ' default ' : ' ' } ) ,
# (('-c', '--clear'), {'action': 'store_true', 'default': False})
2013-09-12 22:17:06 +00:00
)
2014-01-31 18:48:19 +00:00
change_pin . arguments = (
( ( ' -r ' , ' --remove ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
)
2012-12-05 19:31:26 +00:00
load_device . arguments = (
2013-12-30 22:35:20 +00:00
( ( ' -m ' , ' --mnemonic ' ) , { ' type ' : str , ' nargs ' : ' + ' } ) ,
( ( ' -x ' , ' --xprv ' ) , { ' type ' : str } ) ,
( ( ' -p ' , ' --pin ' ) , { ' type ' : str , ' default ' : ' ' } ) ,
( ( ' -r ' , ' --passphrase-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
2014-01-06 00:54:53 +00:00
( ( ' -l ' , ' --label ' ) , { ' type ' : str , ' default ' : ' ' } ) ,
)
reset_device . arguments = (
( ( ' -t ' , ' --strength ' ) , { ' type ' : int , ' choices ' : [ 128 , 192 , 256 ] , ' default ' : 128 } ) ,
( ( ' -p ' , ' --pin ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -r ' , ' --passphrase ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -l ' , ' --label ' ) , { ' type ' : str , ' default ' : ' ' } ) ,
2012-12-05 19:31:26 +00:00
)
2013-08-30 21:32:02 +00:00
2013-11-15 00:43:05 +00:00
sign_message . arguments = (
( ( ' n ' , ) , { ' metavar ' : ' N ' , ' type ' : int , ' nargs ' : ' + ' } ) ,
( ( ' message ' , ) , { ' type ' : str } ) ,
)
2013-11-26 16:29:50 +00:00
verify_message . arguments = (
( ( ' address ' , ) , { ' type ' : str } ) ,
( ( ' signature ' , ) , { ' type ' : str } ) ,
( ( ' message ' , ) , { ' type ' : str } ) ,
)
2013-11-15 00:43:05 +00:00
get_public_node . arguments = (
2014-01-09 16:34:29 +00:00
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
2013-11-15 00:43:05 +00:00
)
2013-10-12 15:40:48 +00:00
firmware_update . arguments = (
( ( ' -f ' , ' --file ' ) , { ' type ' : str } ) ,
)
2013-08-30 21:32:02 +00:00
def list_usb ( ) :
2013-09-13 03:33:20 +00:00
from trezorlib . transport_hid import HidTransport
2013-08-30 21:32:02 +00:00
devices = HidTransport . enumerate ( )
return devices
2013-10-07 21:28:48 +00:00
class PinMatrixThread ( threading . Thread ) :
'''
Hacked PinMatrixWidget into command line tool : - ) .
'''
def __init__ ( self , input_text , message ) :
super ( PinMatrixThread , self ) . __init__ ( )
self . input_text = input_text
self . message = message
self . pin_value = ' '
def run ( self ) :
import sys
from PyQt4 . Qt import QApplication , QWidget , QVBoxLayout
from PyQt4 . QtGui import QPushButton , QLabel
from PyQt4 . QtCore import QObject , SIGNAL
a = QApplication ( sys . argv )
2014-01-06 00:54:53 +00:00
2013-10-07 21:28:48 +00:00
matrix = PinMatrixWidget ( )
def clicked ( ) :
self . pin_value = str ( matrix . get_value ( ) )
a . closeAllWindows ( )
ok = QPushButton ( ' OK ' )
QObject . connect ( ok , SIGNAL ( ' clicked() ' ) , clicked )
vbox = QVBoxLayout ( )
vbox . addWidget ( QLabel ( self . input_text + self . message ) )
vbox . addWidget ( matrix )
vbox . addWidget ( ok )
w = QWidget ( )
w . setLayout ( vbox )
w . move ( 100 , 100 )
w . show ( )
a . exec_ ( )
def qt_pin_func ( input_text , message = None ) :
'''
This is a hack to display Qt window in non - qt application .
Qt window just asks for PIN and closes itself , which trigger join ( ) .
'''
2014-01-06 00:54:53 +00:00
if False : # os.getenv('DISPLAY'):
2013-10-07 21:28:48 +00:00
# Let's hope that system is configured properly and this won't crash
t = PinMatrixThread ( input_text , message )
t . start ( )
t . join ( )
return t . pin_value
else :
# Most likely no X is running,
# let's fallback to default pin_func implementation
return pin_func ( input_text , message )
2012-12-03 15:36:03 +00:00
def main ( ) :
args = parse_args ( Commands )
2013-04-01 14:59:42 +00:00
if args . cmd == ' list ' :
2013-08-30 21:32:02 +00:00
devices = list_usb ( )
2013-04-01 14:59:42 +00:00
if args . json :
print json . dumps ( devices )
else :
for dev in devices :
print dev
return
2012-12-03 15:36:03 +00:00
transport = get_transport ( args . transport , args . path )
2012-12-05 19:31:26 +00:00
if args . debug :
2013-11-15 00:43:05 +00:00
if args . debuglink_transport == ' usb ' and args . debuglink_path == ' ' :
debuglink_transport = get_transport ( ' usb ' , args . path , debug_link = True )
else :
debuglink_transport = get_transport ( args . debuglink_transport , args . debuglink_path )
2013-01-05 14:43:21 +00:00
debuglink = DebugLink ( debuglink_transport )
2012-12-05 19:31:26 +00:00
else :
2013-01-05 14:43:21 +00:00
debuglink = None
2013-04-05 15:12:18 +00:00
2013-10-07 21:28:48 +00:00
client = TrezorClient ( transport , pin_func = qt_pin_func , debuglink = debuglink )
2013-09-09 13:38:15 +00:00
client . setup_debuglink ( button = True , pin_correct = True )
2012-12-03 15:36:03 +00:00
cmds = Commands ( client )
res = args . func ( cmds , args )
if args . json :
2013-09-12 22:17:06 +00:00
print json . dumps ( res , sort_keys = True , indent = 4 )
2012-12-03 15:36:03 +00:00
else :
print res
if __name__ == ' __main__ ' :
2013-04-01 14:59:42 +00:00
main ( )