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
2014-02-13 17:51:11 +00:00
import base64
2012-12-03 15:36:03 +00:00
2014-06-06 12:50:01 +00:00
from trezorlib . client import TrezorClient , TrezorClientDebug
2014-03-28 20:34:15 +00:00
from trezorlib . tx_api import TXAPIBitcoin
2013-09-13 03:33:20 +00:00
from trezorlib . protobuf_json import pb2json
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. ' )
2014-06-06 12:50:01 +00:00
parser . add_argument ( ' -v ' , ' --verbose ' , dest = ' verbose ' , action = ' store_true ' , help = ' Prints communication to device ' )
2014-07-26 14:27:28 +00:00
parser . add_argument ( ' -t ' , ' --transport ' , dest = ' transport ' , choices = [ ' usb ' , ' serial ' , ' pipe ' , ' socket ' , ' bridge ' ] , default = ' usb ' , help = " Transport used for talking with the device " )
2013-08-30 21:32:02 +00:00
parser . add_argument ( ' -p ' , ' --path ' , dest = ' path ' , default = ' ' , help = " Path used by the transport (usually serial port) " )
2014-02-13 15:47:03 +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 " )
2014-02-13 15:47:03 +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 :
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 :
2014-02-03 20:49:07 +00:00
path = list_usb ( ) [ 0 ] [ 0 ]
2013-08-31 21:45:53 +00:00
except IndexError :
raise Exception ( " No Trezor found on USB " )
2013-08-30 21:32:02 +00:00
2014-02-03 20:49:07 +00:00
for d in HidTransport . enumerate ( ) :
# Two-tuple of (normal_interface, debug_interface)
if path in d :
return HidTransport ( d , * * kwargs )
raise Exception ( " Device not found " )
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 )
2014-07-26 14:27:28 +00:00
if transport_string == ' bridge ' :
from trezorlib . transport_bridge import BridgeTransport
return BridgeTransport ( 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 )
2014-08-08 16:01:27 +00:00
return self . client . get_address ( args . coin , address_n , args . show_display )
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 ) :
2014-02-22 16:39:23 +00:00
return self . client . ping ( args . msg , button_protection = args . button_protection , pin_protection = args . pin_protection , passphrase_protection = args . passphrase_protection )
2013-09-12 22:17:06 +00:00
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 )
2014-03-03 14:56:32 +00:00
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-06-17 13:31:10 +00:00
def clear_session ( self , args ) :
return self . client . clear_session ( )
2014-01-31 18:48:19 +00:00
def change_pin ( self , args ) :
return self . client . change_pin ( args . remove )
2014-02-01 12:39:21 +00:00
def wipe_device ( self , args ) :
return self . client . wipe_device ( )
2014-02-02 17:27:44 +00:00
def recovery_device ( self , args ) :
return self . client . recovery_device ( args . words , args . passphrase_protection ,
args . pin_protection , args . label , ' english ' )
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 ) :
2014-02-02 17:27:44 +00:00
return self . client . reset_device ( True , args . strength , args . passphrase_protection ,
args . pin_protection , args . label , ' english ' )
2013-10-12 15:40:48 +00:00
2013-11-15 00:43:05 +00:00
def sign_message ( self , args ) :
2014-05-28 13:52:40 +00:00
address_n = self . client . expand_path ( args . n )
ret = self . client . sign_message ( args . coin , address_n , args . message )
2014-02-13 17:51:11 +00:00
output = {
' message ' : args . message ,
' address ' : ret . address ,
' signature ' : base64 . b64encode ( ret . signature )
}
return output
2013-11-15 00:43:05 +00:00
2013-11-26 16:29:50 +00:00
def verify_message ( self , args ) :
2014-02-13 17:51:11 +00:00
signature = base64 . b64decode ( args . signature )
return self . client . verify_message ( args . address , signature , args . message )
2013-11-26 16:29:50 +00:00
2014-06-12 15:02:46 +00:00
def encrypt_message ( self , args ) :
pubkey = binascii . unhexlify ( args . pubkey )
2014-06-13 14:37:06 +00:00
ret = self . client . encrypt_message ( pubkey , args . message , args . display_only )
2014-06-12 15:02:46 +00:00
return binascii . hexlify ( ret )
def decrypt_message ( self , args ) :
address_n = self . client . expand_path ( args . n )
message = binascii . unhexlify ( args . message )
2014-06-13 14:37:06 +00:00
ret = self . client . decrypt_message ( address_n , message )
2014-06-12 15:02:46 +00:00
return ret
2014-06-12 14:08:20 +00:00
def encrypt_keyvalue ( self , args ) :
2014-06-06 12:40:07 +00:00
address_n = self . client . expand_path ( args . n )
ret = self . client . encrypt_keyvalue ( address_n , args . key , args . value )
return binascii . hexlify ( ret )
2014-06-12 14:08:20 +00:00
def decrypt_keyvalue ( self , args ) :
2014-06-06 12:40:07 +00:00
address_n = self . client . expand_path ( args . n )
2014-06-20 22:31:16 +00:00
ret = self . client . decrypt_keyvalue ( address_n , args . key , args . value )
2014-06-06 12:40:07 +00:00
return ret
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-06-17 13:31:10 +00:00
clear_session . help = ' Clear session (remove cached PIN, passphrase, etc.) '
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 '
2014-02-01 12:39:21 +00:00
wipe_device . help = ' Reset device to factory defaults and remove all private data. '
2014-02-02 17:27:44 +00:00
recovery_device . help = ' Start safe recovery workflow '
2012-12-05 19:31:26 +00:00
load_device . help = ' Load custom configuration to the device '
2014-02-01 12:39:21 +00:00
reset_device . help = ' Perform device setup 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 '
2014-06-12 15:02:46 +00:00
encrypt_message . help = ' Encrypt message '
decrypt_message . help = ' Decrypt message '
2014-06-12 14:08:20 +00:00
encrypt_keyvalue . help = ' Encrypt value by given key and path '
decrypt_keyvalue . help = ' Decrypt value by given key and path '
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 ' , ' -address ' ) , { ' type ' : str } ) ,
2014-08-08 16:01:27 +00:00
( ( ' -d ' , ' --show-display ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
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 } ) ,
2014-02-22 16:39:23 +00:00
( ( ' -b ' , ' --button-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -p ' , ' --pin-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -r ' , ' --passphrase-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
2013-09-12 22:17:06 +00:00
)
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 } ) ,
)
2014-02-01 12:39:21 +00:00
wipe_device . arguments = ( )
2014-02-02 17:27:44 +00:00
recovery_device . arguments = (
( ( ' -w ' , ' --words ' ) , { ' type ' : int } ) ,
( ( ' -p ' , ' --pin-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -r ' , ' --passphrase-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -l ' , ' --label ' ) , { ' type ' : str , ' default ' : ' ' } ) ,
)
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 = (
2014-07-30 15:55:20 +00:00
( ( ' -t ' , ' --strength ' ) , { ' type ' : int , ' choices ' : [ 128 , 192 , 256 ] , ' default ' : 256 } ) ,
2014-02-02 17:27:44 +00:00
( ( ' -p ' , ' --pin-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
( ( ' -r ' , ' --passphrase-protection ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
2014-01-06 00:54:53 +00:00
( ( ' -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 = (
2014-02-21 01:32:37 +00:00
( ( ' -c ' , ' --coin ' ) , { ' type ' : str , ' default ' : ' Bitcoin ' } ) ,
2014-05-28 13:52:40 +00:00
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
2013-11-15 00:43:05 +00:00
( ( ' message ' , ) , { ' type ' : str } ) ,
)
2014-06-12 15:02:46 +00:00
encrypt_message . arguments = (
( ( ' pubkey ' , ) , { ' type ' : str } ) ,
( ( ' message ' , ) , { ' type ' : str } ) ,
2014-06-13 14:37:06 +00:00
( ( ' -d ' , ' --display-only ' ) , { ' action ' : ' store_true ' , ' default ' : False } ) ,
2014-06-12 15:02:46 +00:00
)
decrypt_message . arguments = (
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
( ( ' message ' , ) , { ' type ' : str } ) ,
)
2013-11-26 16:29:50 +00:00
verify_message . arguments = (
( ( ' address ' , ) , { ' type ' : str } ) ,
( ( ' signature ' , ) , { ' type ' : str } ) ,
( ( ' message ' , ) , { ' type ' : str } ) ,
)
2014-06-12 14:08:20 +00:00
encrypt_keyvalue . arguments = (
2014-06-06 12:40:07 +00:00
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
( ( ' key ' , ) , { ' type ' : str } ) ,
( ( ' value ' , ) , { ' type ' : str } ) ,
)
2014-06-12 14:08:20 +00:00
decrypt_keyvalue . arguments = (
2014-06-06 12:40:07 +00:00
( ( ' -n ' , ' -address ' ) , { ' type ' : str } ) ,
( ( ' key ' , ) , { ' type ' : str } ) ,
( ( ' value ' , ) , { ' 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
2014-02-03 20:49:07 +00:00
return HidTransport . enumerate ( )
2013-10-07 21:28:48 +00:00
2014-02-13 15:47:03 +00:00
'''
2013-10-07 21:28:48 +00:00
class PinMatrixThread ( threading . Thread ) :
2014-02-13 15:47:03 +00:00
# Hacked PinMatrixWidget into command line tool :-).
2013-10-07 21:28:48 +00:00
def __init__ ( self , input_text , message ) :
super ( PinMatrixThread , self ) . __init__ ( )
self . input_text = input_text
self . message = message
self . pin_value = ' '
def run ( self ) :
2014-02-03 18:30:40 +00:00
from trezorlib . pinmatrix import PinMatrixWidget
2013-10-07 21:28:48 +00:00
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-06-06 12:40:07 +00:00
pass
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 ) :
2014-02-13 15:47:03 +00:00
# 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 )
2014-02-13 15:47:03 +00:00
'''
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 :
2014-02-03 20:49:07 +00:00
if dev [ 1 ] != None :
print " %s - debuglink enabled " % dev [ 0 ]
else :
print dev [ 0 ]
2013-04-01 14:59:42 +00:00
return
2014-02-03 20:49:07 +00:00
transport = get_transport ( args . transport , args . path )
2014-06-06 12:50:01 +00:00
if args . verbose :
client = TrezorClientDebug ( transport )
else :
client = TrezorClient ( transport )
2014-03-28 20:34:15 +00:00
client . set_tx_api ( TXAPIBitcoin ( ) )
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 ( )