URI: 
       tledger new ui and mobile 2fa validation - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit da7e48f3a7e00689e71628f9a39242e10c7b45d4
   DIR parent 59ed5932a859fd807232960404764b8686355830
  HTML Author: neocogent <info@neocogent.com>
       Date:   Wed, 21 Dec 2016 12:52:54 +0700
       
       ledger new ui and mobile 2fa validation
       
       Diffstat:
         A plugins/ledger/auth2fa.py           |     347 +++++++++++++++++++++++++++++++
         M plugins/ledger/ledger.py            |      86 ++++++++++---------------------
         M plugins/ledger/qt.py                |      44 ++++++++++++++++++++++++++++++-
       
       3 files changed, 417 insertions(+), 60 deletions(-)
       ---
   DIR diff --git a/plugins/ledger/auth2fa.py b/plugins/ledger/auth2fa.py
       t@@ -0,0 +1,347 @@
       +import threading
       +
       +from PyQt4.Qt import (QDialog, QInputDialog, QLineEdit, QTextEdit, QVBoxLayout, QLabel, SIGNAL)
       +import PyQt4.QtCore as QtCore
       +
       +from electrum.i18n import _
       +from electrum_gui.qt.util import *
       +from electrum.util import print_msg
       +
       +import os, hashlib, websocket, threading, logging, json, copy
       +from electrum_gui.qt.qrcodewidget import QRCodeWidget, QRDialog
       +from btchip.btchip import *
       +
       +DEBUG = False
       +
       +helpTxt = [_("Your Ledger Wallet wants tell you a one-time PIN code.<br><br>" \
       +            "For best security you should unplug your device, open a text editor on another computer, " \
       +            "put your cursor into it, and plug your device into that computer. " \
       +            "It will output a summary of the transaction being signed and a one-time PIN.<br><br>" \
       +            "Verify the transaction summary and type the PIN code here.<br><br>" \
       +            "Before pressing enter, plug the device back into this computer.<br>" ),
       +        _("Verify the address below.<br>Type the character from your security card corresponding to the <u><b>BOLD</b></u> character."),
       +        _("Waiting for authentication on your mobile phone"),
       +        _("Transaction accepted by mobile phone. Waiting for confirmation."),
       +        _("Click Pair button to begin pairing a mobile phone."),
       +        _("Scan this QR code with your LedgerWallet phone app to pair it with this Ledger device.<br>" 
       +            "To complete pairing you will need your security card to answer a challenge." )
       +        ]
       +
       +class LedgerAuthDialog(QDialog):
       +    def __init__(self, handler, data):
       +        '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods.
       +        Use last method from settings, but support new pairing and downgrade.
       +        '''
       +        QDialog.__init__(self, handler.top_level_window())
       +        self.handler = handler
       +        self.txdata = data
       +        self.idxs = self.txdata['keycardData'] if self.txdata['confirmationType'] > 1 else ''
       +        self.setMinimumWidth(600)
       +        self.setWindowTitle(_("Ledger Wallet Authentication"))
       +        self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg)
       +        self.dongle = self.handler.win.wallet.get_keystore().get_client().dongle
       +        self.ws = None
       +        self.pin = ''
       +        
       +        self.devmode = self.getDevice2FAMode()
       +        if self.devmode == 0x11 or self.txdata['confirmationType'] == 1:
       +            self.cfg['mode'] = 0
       +        
       +        vbox = QVBoxLayout()
       +        self.setLayout(vbox)
       +        
       +        def on_change_mode(idx):
       +            if idx < 2 and self.ws:
       +                self.ws.stop()
       +                self.ws = None
       +            self.cfg['mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1
       +            if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws:
       +                self.req_validation()
       +            if self.cfg['mode'] > 0:
       +                self.handler.win.wallet.get_keystore().cfg = self.cfg
       +                self.handler.win.wallet.save_keystore()
       +            self.update_dlg()
       +        def add_pairing():
       +            self.do_pairing()
       +        def return_pin():
       +            self.pin = self.pintxt.text() if self.txdata['confirmationType'] == 1 else self.cardtxt.text() 
       +            if self.cfg['mode'] == 1:
       +                self.pin = ''.join(chr(int(str(i),16)) for i in self.pin)
       +            self.accept()
       +        
       +        self.modebox = QWidget()
       +        modelayout = QHBoxLayout()
       +        self.modebox.setLayout(modelayout)
       +        modelayout.addWidget(QLabel(_("Method:")))
       +        self.modes = QComboBox()
       +        modelayout.addWidget(self.modes, 2)
       +        self.addPair = QPushButton(_("Pair"))
       +        self.addPair.setMaximumWidth(60)
       +        modelayout.addWidget(self.addPair)
       +        modelayout.addStretch(1)
       +        self.modebox.setMaximumHeight(50)
       +        vbox.addWidget(self.modebox)
       +        
       +        self.populate_modes()
       +        self.modes.currentIndexChanged.connect(on_change_mode)
       +        self.addPair.clicked.connect(add_pairing)
       +        
       +        self.helpmsg = QTextEdit()
       +        self.helpmsg.setStyleSheet("QTextEdit { background-color: lightgray; }")
       +        self.helpmsg.setReadOnly(True)
       +        vbox.addWidget(self.helpmsg)
       +        
       +        self.pinbox = QWidget()
       +        pinlayout = QHBoxLayout()
       +        self.pinbox.setLayout(pinlayout)
       +        self.pintxt = QLineEdit()
       +        self.pintxt.setEchoMode(2)
       +        self.pintxt.setMaxLength(4)
       +        self.pintxt.returnPressed.connect(return_pin)
       +        pinlayout.addWidget(QLabel(_("Enter PIN:")))
       +        pinlayout.addWidget(self.pintxt)
       +        pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
       +        pinlayout.addStretch(1)
       +        self.pinbox.setVisible(self.cfg['mode'] == 0)
       +        vbox.addWidget(self.pinbox)
       +                    
       +        self.cardbox = QWidget()
       +        card = QVBoxLayout()
       +        self.cardbox.setLayout(card)
       +        self.addrtext = QTextEdit()
       +        self.addrtext.setStyleSheet("QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }")
       +        self.addrtext.setReadOnly(True)
       +        self.addrtext.setMaximumHeight(120)
       +        card.addWidget(self.addrtext)
       +        
       +        def pin_changed(s):
       +            if len(s) < len(self.idxs):
       +                i = self.idxs[len(s)]
       +                addr = self.txdata['address']
       +                addr = addr[:i] + '<u><b>' + addr[i:i+1] + '</u></b>' + addr[i+1:]
       +                self.addrtext.setHtml(str(addr))
       +            else:
       +                self.addrtext.setHtml(_("Press Enter"))
       +                
       +        pin_changed('')    
       +        cardpin = QHBoxLayout()
       +        cardpin.addWidget(QLabel(_("Enter PIN:")))
       +        self.cardtxt = QLineEdit()
       +        self.cardtxt.setEchoMode(2)
       +        self.cardtxt.setMaxLength(len(self.idxs))
       +        self.cardtxt.textChanged.connect(pin_changed)
       +        self.cardtxt.returnPressed.connect(return_pin)
       +        cardpin.addWidget(self.cardtxt)
       +        cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above")))
       +        cardpin.addStretch(1)
       +        card.addLayout(cardpin)
       +        self.cardbox.setVisible(self.cfg['mode'] == 1)
       +        vbox.addWidget(self.cardbox)
       +        
       +        self.pairbox = QWidget()
       +        pairlayout = QVBoxLayout()
       +        self.pairbox.setLayout(pairlayout)
       +        pairhelp = QTextEdit(helpTxt[5])
       +        pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }")
       +        pairhelp.setReadOnly(True)
       +        pairlayout.addWidget(pairhelp, 1)
       +        self.pairqr = QRCodeWidget()
       +        pairlayout.addWidget(self.pairqr, 4)
       +        self.pairbox.setVisible(False)
       +        vbox.addWidget(self.pairbox)
       +        self.update_dlg()
       +        
       +        if self.cfg['mode'] > 1 and not self.ws:
       +            self.req_validation()
       +        
       +    def populate_modes(self):
       +        self.modes.blockSignals(True)
       +        self.modes.clear()
       +        self.modes.addItem(_("Summary Text PIN (requires dongle replugging)") if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled"))
       +        if self.txdata['confirmationType'] > 1:
       +            self.modes.addItem(_("Security Card Challenge"))
       +            if not self.cfg['pair']:
       +                self.modes.addItem(_("Mobile - Not paired")) 
       +            else:
       +                self.modes.addItem(_("Mobile - %s") % self.cfg['pair'][1]) 
       +        self.modes.blockSignals(False)
       +        
       +    def update_dlg(self):
       +        self.modes.setCurrentIndex(self.cfg['mode'])
       +        self.modebox.setVisible(True)
       +        self.addPair.setText(_("Pair") if not self.cfg['pair'] else _("Re-Pair"))
       +        self.addPair.setVisible(self.txdata['confirmationType'] > 2)
       +        self.helpmsg.setText(helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self.cfg['pair'] else 4])
       +        self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100)
       +        self.pairbox.setVisible(False)
       +        self.helpmsg.setVisible(True)
       +        self.pinbox.setVisible(self.cfg['mode'] == 0)
       +        self.cardbox.setVisible(self.cfg['mode'] == 1)
       +        self.pintxt.setFocus(True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True)
       +        self.setMaximumHeight(200)
       +        
       +    def do_pairing(self):
       +        rng = os.urandom(16)
       +        pairID = rng.encode('hex') + hashlib.sha256(rng).digest()[0].encode('hex')
       +        self.pairqr.setData(pairID)
       +        self.modebox.setVisible(False)
       +        self.helpmsg.setVisible(False)
       +        self.pinbox.setVisible(False)
       +        self.cardbox.setVisible(False)
       +        self.pairbox.setVisible(True)
       +        self.pairqr.setMinimumSize(300,300)
       +        if self.ws:
       +            self.ws.stop()
       +        self.ws = LedgerWebSocket(self, pairID)
       +        self.ws.pairing_done.connect(self.pairing_done)
       +        self.ws.start() 
       +               
       +    def pairing_done(self, data):
       +        if data is not None:
       +            self.cfg['pair'] = [ data['pairid'], data['name'], data['platform'] ]
       +            self.cfg['mode'] = 2
       +            self.handler.win.wallet.get_keystore().cfg = self.cfg
       +            self.handler.win.wallet.save_keystore()
       +        self.pin = 'paired'
       +        self.accept()
       +    
       +    def req_validation(self):
       +        if self.cfg['pair'] and 'secureScreenData' in self.txdata:
       +            if self.ws:
       +                self.ws.stop()
       +            self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata)
       +            self.ws.req_updated.connect(self.req_updated)
       +            self.ws.start()
       +              
       +    def req_updated(self, pin):
       +        if pin == 'accepted':
       +            self.helpmsg.setText(helpTxt[3])
       +        else:
       +            self.pin = str(pin)
       +            self.accept()
       +        
       +    def getDevice2FAMode(self):
       +        apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode
       +        try:
       +            mode = self.dongle.exchange( bytearray(apdu) )
       +            return mode
       +        except BTChipException, e:
       +            debug_msg('Device getMode Failed')
       +        return 0x11
       +    
       +    def closeEvent(self, evnt):
       +        debug_msg("CLOSE - Stop WS")
       +        if self.ws:
       +            self.ws.stop()
       +        if self.pairbox.isVisible():
       +            evnt.ignore()
       +            self.update_dlg()
       +
       +class LedgerWebSocket(QThread):
       +    pairing_done = pyqtSignal(object)
       +    req_updated = pyqtSignal(str)
       +    
       +    def __init__(self, dlg, pairID, txdata=None):
       +        QThread.__init__(self)
       +        self.stopping = False
       +        self.pairID = pairID
       +        self.txreq = '{"type":"request","second_factor_data":"' + str(txdata['secureScreenData']).encode('hex')  + '"}' if txdata else None
       +        self.dlg = dlg
       +        self.dongle = self.dlg.dongle
       +        self.data = None
       +             
       +        #websocket.enableTrace(True)
       +        logging.basicConfig(level=logging.INFO)
       +        self.ws = websocket.WebSocketApp('wss://ws.ledgerwallet.com/2fa/channels', 
       +                                on_message = self.on_message, on_error = self.on_error,
       +                                on_close = self.on_close, on_open = self.on_open)
       +                                
       +    def run(self):
       +        while not self.stopping:
       +            self.ws.run_forever()
       +    def stop(self):
       +        debug_msg("WS: Stopping")
       +        self.stopping = True
       +        self.ws.close()
       +
       +    def on_message(self, ws, msg):
       +        data = json.loads(msg)
       +        if data['type'] == 'identify':
       +            debug_msg('Identify')
       +            apdu = [0xe0, 0x12, 0x01, 0x00, 0x41] # init pairing
       +            apdu.extend(data['public_key'].decode('hex'))
       +            try:
       +                challenge = self.dongle.exchange( bytearray(apdu) )
       +                ws.send( '{"type":"challenge","data":"%s" }' % str(challenge).encode('hex') )
       +                self.data = data
       +            except BTChipException, e:
       +                debug_msg('Identify Failed')
       +                
       +        if data['type'] == 'challenge':
       +            debug_msg('Challenge')
       +            apdu = [0xe0, 0x12, 0x02, 0x00, 0x10] # confirm pairing
       +            apdu.extend(data['data'].decode('hex'))
       +            try:
       +                self.dongle.exchange( bytearray(apdu) )
       +                debug_msg('Pairing Successful')
       +                ws.send( '{"type":"pairing","is_successful":"true"}' )
       +                self.data['pairid'] = self.pairID
       +                self.pairing_done.emit(self.data)
       +            except BTChipException, e:
       +                debug_msg('Pairing Failed')
       +                ws.send( '{"type":"pairing","is_successful":"false"}' ) 
       +                self.pairing_done.emit(None)
       +            ws.send( '{"type":"disconnect"}' )
       +            self.stopping = True
       +            ws.close()
       +        
       +        if data['type'] == 'accept':
       +            debug_msg('Accepted')
       +            self.req_updated.emit('accepted')
       +        if data['type'] == 'response':
       +            debug_msg('Responded', data)
       +            self.req_updated.emit(str(data['pin']) if data['is_accepted'] else '')
       +            self.txreq = None
       +            self.stopping = True
       +            ws.close()
       +            
       +        if data['type'] == 'repeat':
       +            debug_msg('Repeat')
       +            if self.txreq:
       +                ws.send( self.txreq )
       +                debug_msg("Req Sent", self.txreq)
       +        if data['type'] == 'connect':
       +            debug_msg('Connected')
       +            if self.txreq:
       +                ws.send( self.txreq )
       +                debug_msg("Req Sent", self.txreq)
       +        if data['type'] == 'disconnect':
       +            debug_msg('Disconnected')
       +            ws.close()
       +            
       +    def on_error(self, ws, error):
       +        message = getattr(error, 'strerror', '')
       +        if not message:
       +            message = getattr(error, 'message', '')
       +        debug_msg("WS: %s" % message)
       +    
       +    def on_close(self, ws):
       +        debug_msg("WS: ### socket closed ###")
       +    
       +    def on_open(self, ws):
       +        debug_msg("WS: ### socket open ###")
       +        debug_msg("Joining with pairing ID", self.pairID)
       +        ws.send( '{"type":"join","room":"%s"}' % self.pairID )
       +        ws.send( '{"type":"repeat"}' )
       +        if self.txreq:
       +            ws.send( self.txreq )
       +            debug_msg("Req Sent", self.txreq)
       +
       +def debug_msg(*args):
       +    if DEBUG:
       +        print_msg(*args)        
       +
       +    
       +        
       +        
       +        
   DIR diff --git a/plugins/ledger/ledger.py b/plugins/ledger/ledger.py
       t@@ -13,14 +13,12 @@ from electrum.keystore import Hardware_KeyStore, parse_xpubkey
        from ..hw_wallet import HW_PluginBase
        from electrum.util import format_satoshis_plain, print_error
        
       -
        try:
            import hid
            from btchip.btchipComm import HIDDongleHIDAPI, DongleWait
            from btchip.btchip import btchip
            from btchip.btchipUtils import compress_public_key,format_transaction, get_regular_input_script, get_p2sh_input_script
            from btchip.bitcoinTransaction import bitcoinTransaction
       -    from btchip.btchipPersoWizard import StartBTChipPersoDialog
            from btchip.btchipFirmwareWizard import checkFirmware, updateFirmware
            from btchip.btchipException import BTChipException
            BTCHIP = True
       t@@ -121,8 +119,7 @@ class Ledger_Client():
                    except BTChipException, e:
                        if (e.sw == 0x6985):
                            self.dongleObject.dongle.close()
       -                    dialog = StartBTChipPersoDialog()
       -                    dialog.exec_()
       +                    self.handler.get_setup( )
                            # Acquire the new client on the next run
                        else:
                            raise e
       t@@ -172,6 +169,12 @@ class Ledger_KeyStore(Hardware_KeyStore):
                # device reconnects
                self.force_watching_only = False
                self.signing = False
       +        self.cfg = d.get('cfg', {'mode':0,'pair':''})
       +        
       +    def dump(self):
       +        obj = Hardware_KeyStore.dump(self)
       +        obj['cfg'] = self.cfg
       +        return obj
        
            def get_derivation(self):
                return self.derivation        
       t@@ -209,18 +212,19 @@ class Ledger_KeyStore(Hardware_KeyStore):
                    info = self.get_client().signMessagePrepare(address_path, message)
                    pin = ""
                    if info['confirmationNeeded']:
       -                # TODO : handle different confirmation types. For the time being only supports keyboard 2FA
       -                confirmed, p, pin = self.password_dialog()
       -                if not confirmed:
       -                    raise Exception('Aborted by user')
       -                pin = pin.encode()
       -                #self.plugin.get_client(self, True, True)
       +                pin = self.handler.get_auth( info ) # does the authenticate dialog and returns pin
       +                if not pin:
       +                    raise UserWarning(_('Cancelled by user'))
       +                pin = str(pin).encode()
                    signature = self.get_client().signMessageSign(pin)
                except BTChipException, e:
                    if e.sw == 0x6a80:
                        self.give_error("Unfortunately, this message cannot be signed by the Ledger wallet. Only alphanumerical messages shorter than 140 characters are supported. Please remove any extra characters (tab, carriage return) and retry.")
                    else:
                        self.give_error(e, True)
       +        except UserWarning:
       +            self.handler.show_error(_('Cancelled by user'))
       +            return ''
                except Exception, e:
                    self.give_error(e, True)
                finally:
       t@@ -334,6 +338,7 @@ class Ledger_KeyStore(Hardware_KeyStore):
                    firstTransaction = True
                    inputIndex = 0
                    rawTx = tx.serialize()
       +            self.get_client().enableAlternate2fa(False)
                    while inputIndex < len(inputs):
                        self.get_client().startUntrustedTransaction(firstTransaction, inputIndex,
                                                                    chipInputs, redeemScripts[inputIndex])
       t@@ -348,44 +353,24 @@ class Ledger_KeyStore(Hardware_KeyStore):
                        if firstTransaction:
                            transactionOutput = outputData['outputData']
                        if outputData['confirmationNeeded']:
       -                    # TODO : handle different confirmation types. For the time being only supports keyboard 2FA
       +                    outputData['address'] = output
                            self.handler.clear_dialog()
       -                    if 'keycardData' in outputData:
       -                        pin2 = ""
       -                        for keycardIndex in range(len(outputData['keycardData'])):
       -                            msg = "Do not enter your device PIN here !\r\n\r\n" + \
       -                                "Your Ledger Wallet wants to talk to you and tell you a unique second factor code.\r\n" + \
       -                                "For this to work, please match the character between stars of the output address using your security card\r\n\r\n" + \
       -                                "Output address : "
       -                            for index in range(len(output)):
       -                                if index == outputData['keycardData'][keycardIndex]:
       -                                    msg = msg + "*" + output[index] + "*"
       -                                else:
       -                                    msg = msg + output[index]
       -                            msg = msg + "\r\n"
       -                            confirmed, p, pin = self.password_dialog(msg)
       -                            if not confirmed:
       -                                raise Exception('Aborted by user')
       -                            try:
       -                                pin2 = pin2 + chr(int(pin[0], 16))
       -                            except:
       -                                raise Exception('Invalid PIN character')
       -                        pin = pin2
       -                    else:
       -                        confirmed, p, pin = self.password_dialog()
       -                        if not confirmed:
       -                            raise Exception('Aborted by user')
       -                        pin = pin.encode()
       -                        #self.plugin.get_client(self, True, True)
       -                    self.handler.show_message("Signing ...")
       +                    pin = self.handler.get_auth( outputData ) # does the authenticate dialog and returns pin
       +                    if not pin:
       +                        raise UserWarning()
       +                    if pin != 'paired':
       +                        self.handler.show_message(_("Confirmed. Signing Transaction..."))
                        else:
                            # Sign input with the provided PIN
       -                    inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex],
       -                    pin)
       +                    inputSignature = self.get_client().untrustedHashSign(inputsPaths[inputIndex], pin)
                            inputSignature[0] = 0x30 # force for 1.4.9+
                            signatures.append(inputSignature)
                            inputIndex = inputIndex + 1
       -                firstTransaction = False
       +                if pin != 'paired':
       +                    firstTransaction = False
       +        except UserWarning:
       +            self.handler.show_error(_('Cancelled by user'))
       +            return
                except BaseException as e:
                    traceback.print_exc(file=sys.stdout)
                    self.give_error(e, True)
       t@@ -412,23 +397,6 @@ class Ledger_KeyStore(Hardware_KeyStore):
                   tx.update_signatures(updatedTransaction)
                self.signing = False
        
       -    def password_dialog(self, msg=None):
       -        if not msg:
       -            msg = _("Do not enter your device PIN here !\r\n\r\n" \
       -                    "Your Ledger Wallet wants to talk to you and tell you a unique second factor code.\r\n" \
       -                    "For this to work, please open a text editor " \
       -                    "(on a different computer / device if you believe this computer is compromised) " \
       -                    "and put your cursor into it, unplug your Ledger Wallet and plug it back in.\r\n" \
       -                    "It should show itself to your computer as a keyboard " \
       -                    "and output the second factor along with a summary of " \
       -                    "the transaction being signed into the text-editor.\r\n\r\n" \
       -                    "Check that summary and then enter the second factor code here.\r\n" \
       -                    "Before clicking OK, re-plug the device once more (unplug it and plug it again if you read the second factor code on the same computer)")
       -        response = self.handler.get_word(msg)
       -        if response is None:
       -            return False, None, None
       -        return True, response, response
       -
        
        class LedgerPlugin(HW_PluginBase):
            libraries_available = BTCHIP
   DIR diff --git a/plugins/ledger/qt.py b/plugins/ledger/qt.py
       t@@ -9,6 +9,9 @@ from .ledger import LedgerPlugin
        from ..hw_wallet.qt import QtHandlerBase, QtPluginBase
        from electrum_gui.qt.util import *
        
       +from btchip.btchipPersoWizard import StartBTChipPersoDialog
       +
       +from .auth2fa import LedgerAuthDialog
        
        class Plugin(LedgerPlugin, QtPluginBase):
            icon_unpaired = ":icons/ledger_unpaired.png"
       t@@ -17,11 +20,14 @@ class Plugin(LedgerPlugin, QtPluginBase):
            def create_handler(self, window):
                return Ledger_Handler(window)
        
       -
        class Ledger_Handler(QtHandlerBase):
       +    setup_signal = pyqtSignal()
       +    auth_signal = pyqtSignal(object)
        
            def __init__(self, win):
                super(Ledger_Handler, self).__init__(win, 'Ledger')
       +        self.setup_signal.connect(self.setup_dialog)
       +        self.auth_signal.connect(self.auth_dialog)
        
            def word_dialog(self, msg):
                response = QInputDialog.getText(self.top_level_window(), "Ledger Wallet Authentication", msg, QLineEdit.Password)
       t@@ -30,3 +36,39 @@ class Ledger_Handler(QtHandlerBase):
                else:
                    self.word = str(response[0])
                self.done.set()
       +    
       +    def message_dialog(self, msg):
       +        self.clear_dialog()
       +        self.dialog = dialog = WindowModalDialog(self.top_level_window(), _("Ledger Status"))
       +        l = QLabel(msg)
       +        vbox = QVBoxLayout(dialog)
       +        vbox.addWidget(l)
       +        dialog.show()
       +
       +    def auth_dialog(self, data):
       +        dialog = LedgerAuthDialog(self, data)
       +        dialog.exec_()
       +        self.word = dialog.pin
       +        self.done.set()
       +                    
       +    def get_auth(self, data):
       +        self.done.clear()
       +        self.auth_signal.emit(data)
       +        self.done.wait()
       +        return self.word
       +        
       +    def get_setup(self):
       +        self.done.clear()
       +        self.setup_signal.emit()
       +        self.done.wait()
       +        return 
       +        
       +    def setup_dialog(self):
       +        dialog = StartBTChipPersoDialog()
       +        dialog.exec_()
       +
       +
       +        
       +        
       +        
       +